feat(acp-adapter): Support displaying context length usage in Zed ACP like Codex - #2412
feat(acp-adapter): Support displaying context length usage in Zed ACP like Codex#2412iluv7 wants to merge 6 commits into
Conversation
…ification Map agent.status.updated events to the ACP protocol UsageUpdate type (size/used fields), dispatched as fire-and-forget session notifications so ACP clients (Zed, JetBrains) can render a real-time context consumption indicator. - events-map.ts: add contextUsageToUsageUpdate() builder - session.ts: subscribe to agent.status.updated in runTurnBody() - index.ts: export new function - plan-and-commands.test.ts: 7 tests (4 unit + 3 e2e)
🦋 Changeset detectedLatest commit: 894912a The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 44a2049415
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (event.type === 'agent.status.updated') { | ||
| if (!isFromMainAgent(event)) return; | ||
| const update = contextUsageToUsageUpdate( | ||
| sessionId, | ||
| event.contextTokens, | ||
| event.maxContextTokens, | ||
| ); |
There was a problem hiding this comment.
Forward usage updates after ACP compaction
When the user runs ACP /compact, prompt() routes to runBuiltInCommand/runCompactCommand instead of runTurnBody, so this newly added agent.status.updated handler is not installed. The core updates the context token count during compaction via emitStatusUpdated(), but runCompactCommand has its own event listener that ignores that event, leaving clients such as Zed showing the pre-compaction usage until a later normal prompt happens. Please share this forwarding with the command/compaction event path or emit a usage_update after compaction completes.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed — runCompactCommand now pushes a one-shot usage_update via getStatus() after compaction completes. Best-effort with its own try-catch so a status refresh failure never turns a successful compaction into an error.
After compaction, the core updates context token counts via emitStatusUpdated(), but the agent.status.updated handler only runs inside runTurnBody(). Push a one-shot usage_update after compaction completes so the client reflects the compacted context size immediately instead of waiting for the next normal prompt. Co-Authored-By: Claude <noreply@anthropic.com>
6c903df to
48d2674
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 48d26746e7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const status = await this.session.getStatus(); | ||
| const update = contextUsageToUsageUpdate( | ||
| this.id, | ||
| status.contextTokens, | ||
| status.maxContextTokens, | ||
| ); |
There was a problem hiding this comment.
Keep /compact success independent of status refresh
When /compact succeeds but Session.getStatus() rejects or stalls after the compaction completion event, this new await throws out to runBuiltInCommand's catch after the success message has already been emitted, so ACP clients can see a spurious /compact failed for a completed compaction and the prompt response now depends on an optional usage refresh. Since the usage notification is best-effort like the following sessionUpdate call, wrap this status-read/update path in its own catch and leave the compaction result intact.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
fixed — wrapped the status-read/update block in its own try-catch so a failed getStatus() never turns a successful compaction into a failure.
48d2674 to
db9db41
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: db9db41936
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| contextTokens: number | undefined, | ||
| maxContextTokens: number | undefined, | ||
| ): SessionNotification | null { | ||
| if (contextTokens === undefined || maxContextTokens === undefined) return null; |
There was a problem hiding this comment.
Avoid sending zero-sized usage updates
When a model/provider does not advertise a context-window capability, the SDK's v1 getStatus() normalizes that to maxContextTokens: 0, but this guard treats 0 as a valid ACP size. ACP clients compute the display percentage from used / size and the usage RFD says agents should skip usage_update when there is no meaningful context window size; after /compact or any status event carrying 0, this can push size: 0 to Zed/JetBrains and produce a bogus/undefined context indicator. Please skip maxContextTokens <= 0 as well.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Already fixed — maxContextTokens <= 0 now returns null in that same commit. Also addressed the other review feedback:
- Added pushContextUsage() so session load/resume/new pushes context usage immediately instead of waiting for the first prompt.
- Wrapped the post-compaction status refresh in its own try-catch so a getStatus() failure doesn't turn a successful compaction into a failure.
…sion setup
- Guard maxContextTokens <= 0 in contextUsageToUsageUpdate to prevent
degenerate { size: 0 } updates, matching existing guards in
formatStatusReport and emitStatusUpdated.
- Add AcpSession.pushContextUsage() (best-effort, typeof-guarded) and
call it after newSession, loadSession replay, and resumeSession so
clients see context usage immediately instead of waiting for the
next active turn.
- Update session-new test to expect two getStatus calls (thinking
effort resolution + pushContextUsage).
|
@codex review |
|
Codex Review: Didn't find any major issues. 👍 Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
Description
Closed #2346
Pushes live context window usage from the agent core through the ACP
usage_updatesession notification so ACP clients (Zed, JetBrains) can render a real-time token consumption indicator. Everyagent.status.updatedevent from the engine is mapped to the ACP protocol'sUsageUpdatetype (@experimental, SDK v0.23+) and dispatched as a fire-and-forget notification on the stdio ndjson stream. Verified on Zed 1.13.1 — the circular progress indicator at the bottom of the Agent panel renders the data natively.What Changed
contextUsageToUsageUpdate()(packages/acp-adapter/src/events-map.ts)New builder that constructs an ACP
SessionNotificationwith discriminatorsessionUpdate: 'usage_update'. Mirrors the existing pattern ofassistantDeltaToSessionUpdate,thinkingDeltaToSessionUpdate, etc.Both input fields are
number | undefined(matchingAgentStatusUpdatedEventfrompackages/protocol/src/events.ts:483-495). Returnsnullwhen either is missing — callers silently skip degenerate events rather than pushing a zero-filled update.agent.status.updatedhandler (packages/acp-adapter/src/session.ts)A new branch in
runTurnBody()'sonEventchain catchesagent.status.updatedevents:isFromMainAgent()guard.event.contextTokens/event.maxContextTokens.conn.sessionUpdate()as fire-and-forget — same pattern asagent_message_chunkandagent_thought_chunk.log.warn; never crash the turn loop.Exports (
packages/acp-adapter/src/index.ts)contextUsageToUsageUpdateadded to theevents-mapre-export block, alongside the existingassistantDeltaToSessionUpdate,toolCallStartToSessionUpdate, etc.Testing
plan-and-commands.test.ts: 7 new tests added (4 unit + 3 e2e){ sessionUpdate: 'usage_update', size, used }shape; returnsnullfor missingcontextTokens, missingmaxContextTokens, and both missingagent.status.updatedforwarded asusage_update; sub-agent events filtered out; events with missing fields skippedpackages/acp-adapter(36 files, 333 tests)