Repository navigation
feat(agents2): show agents working: 👀💬 reactions, local typing, Codex settles - #860
Conversation
261db4c to
afa8713
Compare
71f044f to
737b32d
Compare
afa8713 to
8fd8bb0
Compare
737b32d to
84e88c1
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 84e88c1200
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
| // STOPGAP(agent-local-typing): an agent's progress posts would hide its | ||
| // typing between pulses; the local end says when the turn is over. | ||
| if (local) record.local = typing; |
There was a problem hiding this comment.
Track overlapping local typing runs independently
When two mentions for the same agent and thread overlap—as supported by Codex steering—both pulse the same activity record, but this single boolean cannot represent both producers. When the first request settles, its local kind-9 end sets record.local to false and clears the indicator even though the second request is still working; the second interval restores it only after its next pulse and post-message quiet period, causing a several-second false idle transition. Keep a per-scope active-run count/token, or otherwise end the record only after the final local producer stops.
Useful? React with 👍 / 👎.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
84e88c1 to
ba9b31b
Compare
8fd8bb0 to
0ab40dd
Compare
wesbillman
left a comment
There was a problem hiding this comment.
Changes needed:
- Settle rejected Codex follow-ups independently of the preceding turn (new P2 inline).
- The existing overlapping local-typing finding still applies at this head; retain per-scope producer ownership until the last run ends.
Star Lord’s automated source review via Wes’s account. Head ba9b31b90f55dba431787043f94a8463928953de; base 0ab40dd5db1b040db1f221ec3fee4de6ed263bd6. Source-only: no tests, builds, app/browser execution or CI verification. Reviewed the public description and commit messages; the description contains no image attachments.
| // The steered turn, or one started in its place, answers this mention. | ||
| const steered = lane; | ||
| delivery.workingUntil( | ||
| Promise.race([replied, accepted.then(() => steered.tail)]), |
There was a problem hiding this comment.
[P2] Release a rejected follow-up’s working hold
When turn/steer (or the preceding history read) fails, the catch above posts “Steering was not accepted” but resolves accepted without adding this request to active.requests. Its replied promise can therefore never resolve, while this fallback waits for the previous turn’s tail. A rejected mention keeps its 👀💬 reactions until that unrelated work ends; with channel-scoped sessions, a follow-up in another thread also keeps that thread falsely typing after the rejection. Settle this request’s hold on the handled rejection, retaining the turn-tail fallback only for accepted steering or a replacement turn. Add coverage with the original turn held open while steering fails, asserting the rejected hold settles without ending the original work.
0ab40dd to
78c3214
Compare
Until Agents2 agents show as typing, the host reacts to a mention as the agent with 👀 and 💬 when its run starts, and deletes both once the run and the work it handed off have settled (at most the longest run allowed). Plugins return from `run` early and finish their turns in the background, so `Delivery.workingUntil(done)` lets them say when that work settles. Claude Code and Codex now call it. The reactions live in working-reactions.ts and its one call in the service, so they can be deleted once typing indicators ship; `workingUntil` stays for those. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Bradley Axen <baxen@squareup.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
STOPGAP until agents' typing pulses reach the relay: the host shows a mention's agent typing in the mention's thread on this device only, for as long as the run counts as working. Every 3 s it feeds a pulse into the session's existing typing store through a new `typing.local`, so expiry and the agent's reply ending it work as they do for live pulses. Every piece is tagged STOPGAP(agent-local-typing); grep for it and delete what it marks to retire this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Bradley Axen <baxen@squareup.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Codex makes one more model call after a final send before its turn ends, so the request's working handoff, and with it the agent's working reactions and typing, outlasted its reply by about 3s. The handoff now settles at the final send, for every request the turn has taken in by then. The turn itself still ends on its own: interrupting it would leave a <turn_aborted> note in Codex's history after every reply. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Bradley Axen <baxen@squareup.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
ba9b31b to
04c3944
Compare
What
A stopgap until Agents2 agents show as typing. When an agent's run starts for a mention, the host reacts to the mention as the agent with 👀 and 💬. It deletes them, one kind 5 per reaction because the relay takes one target per deletion, once the run, and any work the run handed off, has settled. The cap is the longest run allowed (30 min).
src/features/agents2/working-reactions.tsplus one call inAgents2Service.working(). Plugins never publish them.Delivery.workingUntil(done). Claude Code and Codex return fromrunearly and finish their turns in the background, so only they know when a turn ends. This hook lets them say so: one call in each. It has the same shape as feat(agents2): show agents as typing while they work on a mention #792's typing hook, so it is meant to stay when the reactions go.STOPGAP(agent-working-reactions). Rungrep -rn "STOPGAP(agent-working-reactions)" srcand delete what it marks.Behavior notes
respondTo: ownergetting a non-owner mention, briefly shows 👀💬 and then removes them. That follows from reacting at invocation. Reacting on the firstworkingUntilcall instead would avoid the flicker.hande, nop.Validation
vitest run src/features/agents2 src/bundled/codex src/bundled/claude-code: 17 files / 190 tests pass.tsc --noEmitis clean.workingUntilholds are ignored.ptag, synchronous publish errors can no longer wedgedrain, and a real barrier before a negative assertion.🤖 Generated with Claude Code
From #861
What
STOPGAP, to be deleted once agents' typing pulses reach the relay (#792 plus block/buzz#8207). While an Agents2 agent works on a mention (the same lifetime as #860's reactions,
Agents2Service.working()), this device shows the agent typing in the mention's thread. Nothing is published, so other devices don't see it.agents2/local-typing.tsbuilds a kind 20002 pulse every 3 s. It uses the tags feat(agents2): show agents as typing while they work on a mention #792 would publish:h, the thread root, and the mention asreply.typing.local(event)inrelay/typing.tsfeeds that pulse into the session's existing typing store as if it came live. The store's expiry (8 s), the end-on-reply rule (the agent's kind 9 hides the indicator, then a 2 s quiet period) and the existing indicators all work unchanged.communities.open→acquire). Agents2 agents are already inagentChoicesthroughlocalAgents.bind(agents2.local), so the floating indicator and the thread rows show them.relay/session.tsalready exposestyping.capability.Removal
Every piece is tagged
STOPGAP(agent-local-typing). Rungrep -rn "STOPGAP(agent-local-typing)" srcand delete what it marks. #860's reactions are taggedSTOPGAP(agent-working-reactions)the same way.Notes
finalsend, so its indicator can stay about 3 s past its reply.typing.localsits on the session's typing capability, so code that can see the session could show fake typing for any pubkey. It only affects this device.Validation
vitest runacross agents2, relay typing and activity-typing, Codex, Claude Code and TypingIndicator: 22 files / 244 tests pass.tsc --noEmitis clean.From #863
What
Codex doesn't end its turn when it sends a reply with
final: true. It makes one more model call that returns nothing, then ends the turn about 3s later. That call is visible in the #buzzin-in-secret rollouts. Until now, the request'sworkingUntilhandoff settled only when the turn ended, so the agent's working reactions and typing (#860, #861) lingered past its reply.The handoff now settles as soon as a final send succeeds. That covers every request the turn has taken in by then: the first one and each steered follow-up. A follow-up steered in after the send keeps counting as working until its own answer.
Why not end the turn
Claude Code ends its turn on a final send through a hook. The Codex app-server has no matching option, so the only way to end the turn is
turn/interrupt. Codex then writes a<turn_aborted>note into the session after every reply: "The user interrupted the previous turn on purpose…". The model would read that note on every later turn, andlive.mjsalready asserts that ordinary final sends leave no such note. So the turn still ends by itself, and only the "working" signal moves. That extra model call still happens; it is cheap, with cached context and no output.Validation
vitest run src/bundled/codex src/bundled/claude-code src/features/agents2 src/features/relay/typing: 218 tests pass.tsc --noEmitis clean.answered()call removed, the first two tests fail.