Class: Terret::Subagents
- Inherits:
-
Hames::Service
- Object
- Hames::Service
- Terret::Subagents
- Defined in:
- lib/terret/subagents.rb
Overview
ctx — the delegation seam, sole-provider like ctx and ctx. This is the fork provider; the seam, the other two providers plan §6.4 names, and the lifecycle below are docs/subagents.md §§1-3.
ctx[:subagents].run(prompt:, ctx:) # => Result(text:, session_id:, usage:, status:)
ctx: is the CALLING AGENT's context and it is an explicit argument
rather than a service ivar for one reason: it is what makes the
no-escalation guarantee structural. No path through this provider can
build a child from the root, so a child inherits the caller's roster and
its install-time policy FLOOR — an agent whose FLOOR is Read and Grep
cannot ask a child to run Bash. The qualifier matters (docs/subagents.md
§3): a policy hot-narrowed mid-session does not carry to children spawned
after it, because the child's session is fresh and holds no policy/updated,
so it runs at the floor rather than at the parent's live, narrowed set. A
narrowing that must reach children belongs in the floor (a config row).
Defined Under Namespace
Classes: Result
Instance Method Summary collapse
-
#reconfigure(_config) ⇒ Object
Nothing is captured from config, so a swapped row governs the very next delegation with nothing to re-derive.
-
#run(prompt:, ctx:) ⇒ Object
Spawn a child inside a fork of the caller's context, run one turn to completion on a fresh durable session, and hand back what it said, where it said it, and what it cost.
- #start(ctx) ⇒ Object
Instance Method Details
#reconfigure(_config) ⇒ Object
Nothing is captured from config, so a swapped row governs the very next delegation with nothing to re-derive.
38 |
# File 'lib/terret/subagents.rb', line 38 def reconfigure(_config); end |
#run(prompt:, ctx:) ⇒ Object
Spawn a child inside a fork of the caller's context, run one turn to completion on a fresh durable session, and hand back what it said, where it said it, and what it cost.
The child's session is FRESH, not Sessions#forked: a subagent inherits
its parent's capabilities, not its parent's transcript.
AgentCapExceeded raises straight through rather than being wrapped: a refused spawn is the caller's answer, not something that happened inside a child.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/terret/subagents.rb', line 50 def run(prompt:, ctx:) sessions = @ctx[:sessions] loop_service = @ctx[:loop] session = sessions.create agent = loop_service.spawn_agent(session_id: session.id, id: "subagent-#{session.id}", parent: ctx) # Marked before the turn can start: nothing routes an approval request # for this session to a human, so the gate must deny rather than park on # a verdict that can never arrive. A parked child would hold the parent's # fiber forever and there is no one to unstick it. agent.unattended = true begin # The ordinary Loop: same steps, same MAX_STEPS ceiling, same pipeline, # same approvals gate, same allow list. run_turn's own input path is # what appends the prompt as a durable user/message. status = loop_service.run_turn(agent, prompt) Result.new(text: final_text(sessions, session.id), session_id: session.id, usage: sessions.usage(session.id), status: status) rescue StandardError => e # A stack trace in a tool result is context the parent's model cannot # act on and pays for on every subsequent request. The session id is # the pointer to where the whole story actually is. raise Tools::Failure, "the subagent turn failed (#{e.class}); its session #{session.id} has the story" ensure dispose(loop_service, agent) end end |
#start(ctx) ⇒ Object
32 33 34 |
# File 'lib/terret/subagents.rb', line 32 def start(ctx) @ctx = ctx end |