Class: Terret::Agent

Inherits:
Object
  • Object
show all
Defined in:
lib/terret/loop.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id:, session_id:, ctx:) ⇒ Agent

Returns a new instance of Agent.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/terret/loop.rb', line 49

def initialize(id:, session_id:, ctx:)
  @id = id
  @session_id = session_id
  @ctx = ctx           # a forked, agent-scoped context
  @inbox = []          # injected context waits here until a waking message
  @unattended = false
  # :idle | :running | :waiting_approval (parked in the tools pipeline) |
  # :stopping (cancelled, still finishing) | :done (disposed, terminal).
  # docs/subagents.md §8: :failed is a TURN status, not an agent one, and
  # :waiting_input stays vocabulary until something parks a turn on it.
  @status = :idle
  @cancelled = false
  @cancel_reason = nil
end

Instance Attribute Details

#cancel_reasonObject (readonly)

Returns the value of attribute cancel_reason.



76
77
78
# File 'lib/terret/loop.rb', line 76

def cancel_reason
  @cancel_reason
end

#ctxObject (readonly)

Returns the value of attribute ctx.



38
39
40
# File 'lib/terret/loop.rb', line 38

def ctx
  @ctx
end

#idObject (readonly)

Returns the value of attribute id.



38
39
40
# File 'lib/terret/loop.rb', line 38

def id
  @id
end

#session_idObject (readonly)

Returns the value of attribute session_id.



38
39
40
# File 'lib/terret/loop.rb', line 38

def session_id
  @session_id
end

#statusObject

Returns the value of attribute status.



39
40
41
# File 'lib/terret/loop.rb', line 39

def status
  @status
end

#unattendedObject

True when no human can be asked about this agent's tool calls. Nothing routes an approval request for a subagent's session to an operator — the parent's log does not even name it (docs/subagents.md §2) — so the approvals gate denies rather than parking on a verdict that can never arrive. Set by the subagent provider on the children it spawns; a top-level agent is attended and parks exactly as it always did.



47
48
49
# File 'lib/terret/loop.rb', line 47

def unattended
  @unattended
end

Instance Method Details

#cancel(reason = nil) ⇒ Object

Cooperative stop: the loop honors it at step boundaries. Mid-stream abort arrives with the async task-tree work (plan §8); until then this is the honest synchronous form.

A cancel raised DURING a turn is per-turn and best-effort: whether that turn rejects, fails, or completes before a boundary honors it, turning's ensure clears the flag, so it never haunts the next one. A cancel on an IDLE agent has no turn to clear it and so it persists — the next turn honors it at once and closes cancelled having spent no step, which is what a stop pressed just before a message lands should do.

The status moves only from :running: :stopping is a sub-state of a turn that is still working and is no longer going to finish, so there is nothing for it to mean on an idle agent — and an idle agent left non-idle by a cancel could never start the turn that would clear it. A parked agent keeps saying :waiting_approval, which is still true; the approvals gate's restore is what reads the standing cancel and returns it to :stopping rather than to :running.



96
97
98
99
100
# File 'lib/terret/loop.rb', line 96

def cancel(reason = nil)
  @cancel_reason = reason
  @cancelled = true
  @status = :stopping if @status == :running
end

#cancelled?Boolean

Returns:

  • (Boolean)


102
# File 'lib/terret/loop.rb', line 102

def cancelled? = !!@cancelled

#clear_cancel!Object



104
105
106
107
# File 'lib/terret/loop.rb', line 104

def clear_cancel!
  @cancelled = false
  @cancel_reason = nil
end

#drain_inboxObject



68
# File 'lib/terret/loop.rb', line 68

def drain_inbox = @inbox.slice!(0..)

#inbox_empty?Boolean

Returns:

  • (Boolean)


70
# File 'lib/terret/loop.rb', line 70

def inbox_empty? = @inbox.empty?

#inject(text) ⇒ Object



64
65
66
# File 'lib/terret/loop.rb', line 64

def inject(text)
  @inbox << text
end

#requeue(items) ⇒ Object

Steers drained for a step that never happened go back to the front of the queue, so a rejected claim cannot silently eat an inject.



74
# File 'lib/terret/loop.rb', line 74

def requeue(items) = @inbox.unshift(*items)