Module: Silas::Instructions

Defined in:
lib/silas/instructions.rb

Overview

Renders instructions.md ONCE per turn into an immutable snapshot — the determinism anchor. (Skill-index injection and loaded-skill inlining land with the skills work; the snapshot-once mechanics are load-bearing now.)

Class Method Summary collapse

Class Method Details

.base_instructions(turn) ⇒ Object



24
25
26
27
28
29
30
31
32
# File 'lib/silas/instructions.rb', line 24

def base_instructions(turn)
  # config.instructions_dir points at the active agent's directory (root or a
  # subagent, swapped during a nested run).
  dir = Silas.instructions_dir || Rails.root.join("app/agent")
  path = Pathname(dir).join("instructions.md")
  return default_instructions unless path.exist?

  ERB.new(path.read).result_with_hash(session: turn.session, agent_name: turn.session.agent_name)
end

.default_instructionsObject



63
64
65
# File 'lib/silas/instructions.rb', line 63

def default_instructions
  "You are a helpful agent."
end

.loaded_skills_block(session) ⇒ Object

Skills loaded in prior turns are inlined "for the rest of the session".



56
57
58
59
60
61
# File 'lib/silas/instructions.rb', line 56

def loaded_skills_block(session)
  loaded = Silas.skills.select { |s| session.loaded_skills.include?(s.name) }
  return nil if loaded.empty?

  loaded.map { |s| "## Skill: #{s.name}\n\n#{s.body.strip}" }.join("\n\n")
end

.memory_block(session) ⇒ Object

Recent memories surface into the snapshot (bounded; recall digs deeper).



35
36
37
38
39
40
41
42
43
44
# File 'lib/silas/instructions.rb', line 35

def memory_block(session)
  return nil unless Silas.memory_enabled?

  memories = Silas::Memory.recall(agent_name: session.agent_name,
                                  limit: Silas.config.memory_injection_limit)
  return nil if memories.empty?

  "## Memory (most recent — use the recall tool for more)\n\n" +
    memories.map { |m| "- #{m.to_line}" }.join("\n")
end

.render(turn) ⇒ Object



19
20
21
22
# File 'lib/silas/instructions.rb', line 19

def render(turn)
  [ base_instructions(turn), memory_block(turn.session), skill_index_block(turn.session), loaded_skills_block(turn.session) ]
    .compact.join("\n\n")
end

.skill_index_block(session) ⇒ Object

Advertise skill descriptions (eve's routing hint); bodies load on demand.



47
48
49
50
51
52
53
# File 'lib/silas/instructions.rb', line 47

def skill_index_block(session)
  advertised = Silas.skills.reject { |s| session.loaded_skills.include?(s.name) }
  return nil if advertised.empty?

  lines = advertised.map { |s| "- #{s.name}: #{s.description}" }
  "## Available skills\n\nLoad with the load_skill tool when relevant:\n#{lines.join("\n")}"
end

.snapshot!(turn) ⇒ Object



10
11
12
13
14
15
16
17
# File 'lib/silas/instructions.rb', line 10

def snapshot!(turn)
  return if turn.instructions_snapshot.present? # idempotent: replay-safe

  turn.update!(
    instructions_snapshot: render(turn),
    definitions_digest: Silas.definitions_digest.to_s
  )
end