Replies: 2 comments
-
|
Fair to start where you did: this does cut against the stated scope. The README calls out "no event subscription" as a deliberate non-goal, and you flagged that yourself, so I'm treating it as reopening a line I drew on purpose, not a small add. That said, the pitch has my attention, because it covers things a
Those are real, and they line up with where the agent-status side is already heading, so I don't want to wave it off. What's keeping the jury out isn't the value, it's code growth and API surface against the benefit. Some kinds are already poll-covered, and part of the edge story is reachable today from the agent-status hook (a script you own, it can journal or curl a push directly). My first instinct was to ask for a trimmed kind set, just the OSC/GUI-native edges agterm alone sees. But that only pays off if the smaller set genuinely shrinks the implementation. If most of the ~1K LOC is the connection/broker/lifecycle scaffolding and per-kind emission is cheap on top, trimming kinds saves little and leaves the event API lopsided, which I'd like less than either doing it in full or leaving it out. Keeping the control API internally consistent matters more to me than shaving a couple of kinds. So the real question, and worth you chewing on too: where does the ~1K LOC actually go? If it's mostly fixed scaffolding, this is close to all-or-nothing and it comes down to whether the full event surface is worth that fixed cost. If a good chunk is per-kind and a minimal v1 is genuinely small, a tight subset gets attractive. That breakdown is what would move the decision. |
Beta Was this translation helpful? Give feedback.
-
|
Good question to force — I measured instead of guessing. Current branch: ~850 source lines (~800 net new; ~45 are a relocation) plus ~355 test lines. Once again, obvious disclaimer - all numbers based on a draft implementation. Where the lines go:
So this lands in your all-or-nothing branch: dropping two kinds saves ~50 lines out of 850 and leaves the API lopsided. The honest decision is the full v1 surface vs. nothing — with the wire frozen small (optional fields, One correction on the hook-script alternative. "A script you own can journal or push directly" only covers the opening edges — the hook sees what it writes. The closing edges are app-native and fire no hook at all: the Esc/Ctrl-C keystroke-clear (which exists precisely because Claude Code has no event for a declined prompt — notifications.md documents it as "the only signal"), the GUI Clear Status, and the auto-reset visit-clear. So blocked-duration can't be rebuilt hook-side any more than notify content can be polled — same class of argument you already accepted, and it's why Use cases that are impossible or painful without edges, beyond the two you granted:
Happy to open the branch as a draft PR if you want to judge the code directly rather than my accounting of it. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
TLDR: the README lists "no event subscription" as out of scope, this pitch tries to fight it :)
Problem
The control API can write but can't watch. Anything that wants to react to agterm polls
tree --json: reaction latency equals the poll interval, N watchers means N poll loops, and a fast status transition (blocked → cleared by a keystroke) can vanish between polls.The agent-status hooks make this concrete. They mark sessions
active/blocked/completed, but the only consumer is the sidebar glyph. "Tell me the moment an agent blocks" has no answer today.Proposal
agtermctl events— subscribe on the existing socket. The server acks the request (normalControlResponse, carrying the current eventseq), then streams NDJSON events on the same connection until the client hangs up:Human mode prints one-liners (
15:07:46 status api-fix blocked);--kindfilters per subscriber.v1 kinds:
status,notify,session.created,session.closed,tree.changed(coalesced structural event, ~100 ms debounce — rename/move/reorder collapse into it, scripts re-readtreefor detail).seqis monotonic per app run: gap detection now,--sincecatch-up later with no wire break.What it enables
Jump to whichever agent just got stuck:
Same shape gets you: notify → phone push relay (ntfy/Telegram),
completed→ feed the next task into the freed session (session.type), a menubar widget showing blocked/working counts without a poll loop, andevents --json >> journal.ndjsonfor "how long was I the bottleneck today" analytics.Design notes
events.subscribethe server acks, sets the fd non-blocking, and gives it to a broker on its own serial queue. The accept loop stays strictly serial and one-shot — it never learns streaming exists.session.statusall emit the samestatusevent. Change-only: the hooks re-assertactive --blinkper tool call, and same-value re-sets don't spam the stream.Prototype
I have a working prototype, reasonably shaped and sized(~1K LOC)
Beta Was this translation helpful? Give feedback.
All reactions