Module: Agentilda::Control
- Defined in:
- lib/agentilda/control.rb
Overview
The side channel between a keypress and a running agent.
claude -p takes no input once it starts, so the only way to reach an
agent mid-flight is a file it has been told to poll: each invocation gets
a control file of its own, named in its prompt, and a keypress writes a
word into every file currently registered. Like everything else about the
prompt this is a request — an agent deep in a tool call reads the file at
its next step, not instantly — which is why #quit! also arms a deadline
the Executor checks, so "quit" is eventually a guarantee too.
Constant Summary collapse
- WRAP_UP =
What a keypress writes into a control file. One word, one line, so an agent can act on
File.read(path).stripand nothing subtler. "WRAP_UP"- STOP =
"STOP"- GRACE =
Seconds between #quit! and the harness terminating whatever is still running. Long enough to write files and a handoff note; short enough that q means quit rather than "quit eventually".
60
Class Method Summary collapse
-
.overdue? ⇒ Boolean
Whether the grace period after
qhas run out. - .quit! ⇒ void
-
.quit? ⇒ Boolean
Whether
qhas been pressed. -
.register(dir, name) ⇒ String
Register a fresh control file for one invocation and hand back its path, to be named in the agent's prompt.
-
.release(path) ⇒ void
Forget a finished invocation's file.
-
.reset! ⇒ void
Back to rest, for the next
Runner#call— and for the suite, where one example's q must not quit every example after it. -
.stop! ⇒ void
n: every running agent is asked to write out what it has and end its turn. -
.wrap_up! ⇒ void
w: every running agent is asked to finish the essential remainder as fast as it can.
Class Method Details
.overdue? ⇒ Boolean
Returns whether the grace period after q has run out.
85 86 87 |
# File 'lib/agentilda/control.rb', line 85 def overdue? @mutex.synchronize { !@deadline.nil? && UI.monotonic > @deadline } end |
.quit! ⇒ void
73 74 75 76 77 78 79 |
# File 'lib/agentilda/control.rb', line 73 def quit! stop! @mutex.synchronize { @quit = true @deadline ||= UI.monotonic + GRACE } end |
.quit? ⇒ Boolean
Returns whether q has been pressed.
82 |
# File 'lib/agentilda/control.rb', line 82 def quit? = @mutex.synchronize { @quit } |
.register(dir, name) ⇒ String
Register a fresh control file for one invocation and hand back its path, to be named in the agent's prompt.
37 38 39 40 41 42 43 |
# File 'lib/agentilda/control.rb', line 37 def register(dir, name) FileUtils.mkdir_p(dir) path = File.join(dir, "control-#{name}-#{Process.pid}-#{format("%04x", rand(0x10000))}") File.write(path, "") @mutex.synchronize { @files << path } path end |
.release(path) ⇒ void
This method returns an undefined value.
Forget a finished invocation's file. The file itself is removed so a crashed run does not leave stale STOPs for the next one to find.
50 51 52 53 |
# File 'lib/agentilda/control.rb', line 50 def release(path) @mutex.synchronize { @files.delete(path) } FileUtils.rm_f(path) end |
.reset! ⇒ void
This method returns an undefined value.
Back to rest, for the next Runner#call — and for the suite, where
one example's q must not quit every example after it.
93 94 95 96 97 98 99 100 |
# File 'lib/agentilda/control.rb', line 93 def reset! @mutex.synchronize { @files.each { |f| FileUtils.rm_f(f) } @files.clear @quit = false @deadline = nil } end |
.stop! ⇒ void
This method returns an undefined value.
n: every running agent is asked to write out what it has and end
its turn. The loop keeps going — under chaining that is exactly what
hands the plan to the next agent.
66 |
# File 'lib/agentilda/control.rb', line 66 def stop! = broadcast(STOP) |
.wrap_up! ⇒ void
This method returns an undefined value.
w: every running agent is asked to finish the essential remainder
as fast as it can. The loop itself keeps going.
59 |
# File 'lib/agentilda/control.rb', line 59 def wrap_up! = broadcast(WRAP_UP) |