Skip to content

feat: goose2 context window usage in chat input#8613

Merged
tellaho merged 21 commits intomainfrom
codex/context-window-tracking
Apr 20, 2026
Merged

feat: goose2 context window usage in chat input#8613
tellaho merged 21 commits intomainfrom
codex/context-window-tracking

Conversation

@tellaho
Copy link
Copy Markdown
Collaborator

@tellaho tellaho commented Apr 17, 2026

Category: improvement
User Impact: Goose2 users can now see context window usage in the chat input and compact a conversation directly from that popover.
Problem: goose2 already had the backend data needed for context tracking, but the UI still surfaced it unreliably for draft chats because early ACP usage updates could arrive before the local session mapping existed. Even when the indicator showed up, compacting context still depended on typing a hidden slash command, and just dev could quietly boot a different goose binary than the one in this worktree.
Solution: Emit and return usage from ACP session lifecycle events, buffer early usage updates until the chat session is registered locally, and pass the selected provider into draft session creation so the context limit resolves immediately. On top of that, turn the ring into a compact popover with a slim progress bar, tighter metrics, and a first-class compact action, refresh replayed history after compaction so /compact stays invisible, and force goose2 dev to launch against this branch's local goose binary.

File changes

crates/goose-acp/src/server.rs
Emits usage_update notifications when sessions are created, loaded, and prompted, and includes per-turn prompt usage in the ACP response. This gives goose2 a stable source of context-window state instead of waiting for later replay events.

ui/goose2/justfile
Builds the local goose CLI during setup and makes just dev prefer the worktree's debug or release binary. That keeps the frontend pointed at the matching backend code while testing the context indicator locally.

ui/goose2/src/features/chat/hooks/tests/useChat.test.ts
Adds hook coverage for compacting a prepared conversation, reloading replayed history, hiding manual /compact echoes, and surfacing the right error when a session has not been prepared yet.

ui/goose2/src/features/chat/hooks/useChat.ts
Adds compactConversation() to send /compact, reload the ACP session, replace the visible transcript with replayed history, and strip the internal compact command back out of the UI. It also introduces the dedicated compacting chat state and the user-facing error path for unprepared sessions.

ui/goose2/src/features/chat/ui/ChatInput.tsx
Threads the new compact-context props through the shared chat input so both the home and chat compositions can render the updated toolbar contract.

ui/goose2/src/features/chat/ui/ChatInputToolbar.tsx
Turns the context ring into a popover that now uses a slim progress bar, compact token totals, and a concise percentage readout alongside the shared Compact action. The control still matches the neighboring toolbar popovers, but the latest layout reads faster at a glance.

ui/goose2/src/features/chat/ui/ChatView.tsx
Wires the compact action from useChat() into the toolbar, only enables it when the session is actually compactable, and passes the compacting state down so the popover can reflect in-flight work.

ui/goose2/src/features/chat/ui/ContextRing.tsx
Simplifies the ring styling so the neutral track stays visible and the foreground arc maps directly to real usage. The result is more legible on light and dark surfaces and easier to scan at a glance.

ui/goose2/src/features/chat/ui/LoadingGoose.tsx
Maps the new compacting chat state to dedicated loading copy instead of reusing the generic responding label.

ui/goose2/src/features/chat/ui/tests/ChatInput.test.tsx
Covers the refined popover metrics layout, the shortened Compact action label, and the hidden state when no context limit is available.

ui/goose2/src/features/chat/ui/tests/LoadingGoose.test.tsx
Adds regression coverage for the new compacting loading message so that state keeps its own visible copy.

ui/goose2/src/shared/api/acp.ts
Registers the local-to-goose session mapping before loading ACP history and rolls that mapping back if the load fails. That keeps replay refreshes from leaving bad session state behind.

ui/goose2/src/shared/api/acpApi.ts
Passes the chosen provider into newSession() metadata so draft sessions can resolve the right model context limit as soon as they are created.

ui/goose2/src/shared/api/acpNotificationHandler.test.ts
Exercises the race where a usage_update arrives before the local draft session is registered, and locks in the rule that only usage updates are buffered.

ui/goose2/src/shared/api/acpNotificationHandler.ts
Buffers early usage_update notifications until goose2 knows which local chat session they belong to, then replays them through the correct live or replay path. That removes the draft-session race that made the ring appear inconsistent.

ui/goose2/src/shared/api/acpSessionTracker.ts
Adds session-registration listeners, notifies them whenever a goose session is bound locally, and exposes unregister cleanup. This gives the notification layer a deterministic point to flush buffered usage updates.

ui/goose2/src/shared/i18n/locales/en/chat.json
Adds English copy for the context popover and the compacting state, then refines the CTA copy from Compact now to Compact for the tighter layout.

ui/goose2/src/shared/i18n/locales/es/chat.json
Adds the matching Spanish strings for the same popover and compacting copy, including the shorter Compactar CTA.

ui/goose2/src/shared/ui/button.tsx
Removes the default shadow from the shared secondary button so the compact action sits flush with the rest of the popover surface.

ui/goose2/src/test/mocks/goose-sdk.ts
Drops an unused mock constructor to clear the lint noise introduced while touching the chat tests.

Reproduction Steps

  1. In ui/goose2, run just setup and then just dev.
  2. Start a new Goose chat with a Goose provider and send any message.
  3. Wait for the first response to finish and confirm the context ring appears in the chat input toolbar.
  4. Click the ring and verify the popover now shows a slim progress bar, compact token totals, a percentage readout, and the Compact action with sizing that still matches the project and attachment popovers.
  5. Click Compact and verify the loading state switches to Compacting conversation..., then the transcript refreshes without showing a visible /compact user message.
  6. Check the dev terminal and confirm just dev reports Using local goose binary: from this worktree.

Screenshots

Context usage popover with the refined metrics layout:

Context usage popover with refined metrics layout

Attachment popover spacing reference:

Attachment popover spacing reference

Project popover spacing reference:

Project popover spacing reference

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2daa5f8de9

ℹ️ 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".

Comment thread crates/goose-acp/src/server.rs Outdated
Comment thread ui/goose2/src/shared/api/acpNotificationHandler.ts
@tellaho tellaho changed the title surface goose2 context window usage in chat input feat: goose2 context window usage in chat input Apr 17, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e8c5a5daf7

ℹ️ 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".

Comment thread ui/goose2/src/features/chat/ui/ContextRing.tsx Outdated
@tellaho tellaho force-pushed the codex/context-window-tracking branch from e8c5a5d to 76f16f2 Compare April 17, 2026 16:20
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 76f16f2a27

ℹ️ 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".

Comment thread ui/goose2/justfile
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 98ef122fc1

ℹ️ 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".

Comment thread ui/goose2/src/shared/api/acp.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: af15de93bd

ℹ️ 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".

Comment thread ui/goose2/src/features/chat/hooks/useChat.ts
Comment thread ui/goose2/justfile
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5c2366caf2

ℹ️ 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".

Comment thread ui/goose2/src/features/chat/hooks/useChat.ts Outdated
Comment on lines 520 to 521
contextTokens={tokenState.accumulatedTotal}
contextLimit={tokenState.contextLimit}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh context limit when the model changes

This new UI binds context usage directly to tokenState.contextLimit, but that value is only updated from usage_update notifications; changing models via acpSetModel does not immediately refresh token limits. As a result, after switching to a model with a very different context window, the ring/popover can keep showing the old denominator until the next prompt/load, making the displayed percentage and compacting signal inaccurate. Trigger a limit refresh on model change (or emit/consume an update that carries the new context limit).

Useful? React with 👍 / 👎.

@tellaho tellaho force-pushed the codex/context-window-tracking branch from 81c05c8 to 01eede6 Compare April 17, 2026 19:33
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2f10a30043

ℹ️ 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".

Comment on lines +1835 to +1837
let mut response = PromptResponse::new(stop_reason);
if let Some(usage) = build_prompt_usage(&session) {
response = response.usage(usage);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Omit usage from cancelled prompt responses

When was_cancelled is true, this path still attaches build_prompt_usage(&session) to the PromptResponse. If cancellation happens before the canceled turn writes fresh metrics, session.total_tokens/input_tokens/output_tokens can still reflect the previous completed turn, so ACP clients that sum per-turn response.usage will overcount costs/tokens on each cancel. Skip setting response.usage for cancelled turns (or derive usage from the canceled turn itself) to avoid reporting stale usage.

Useful? React with 👍 / 👎.

tellaho added 20 commits April 17, 2026 10:26
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
(cherry picked from commit 35d7813)
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Render the base track with the neutral border color so it stays visible behind the foreground arc on dark surfaces. Remove the old inner fill and exaggerated scaling so the ring maps directly to actual usage.

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Wire the context usage popover to a shared Compact now button and
show a dedicated compacting state while the command runs.

Refresh the transcript from ACP session replay after /compact so the
UI reflects the compacted history, and cover the new behavior with
chat hook and popover tests.

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Filter manual compact command messages out of replayed history so
/compact never shows up in the visible transcript after a compaction.

Cover the doubled /compact replay case with a hook regression test to
keep the compact popover action internal to the chat UI.

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Use the shared secondary button treatment for the compact action
and remove the secondary variant's default shadow so the popover
button sits flat with the rest of the control surface.

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Use the shared progress indicator in the context popover and compress
the usage metrics into a cleaner, left-aligned layout.

Trim extra spacing around the compact action and shorten the button
label in the localized chat copy.

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Document that the temporary /compact trigger should be removed once goose2 adds first-class slash command support.

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
@tellaho tellaho force-pushed the codex/context-window-tracking branch from 2f10a30 to 142865f Compare April 17, 2026 20:30
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 17888f63a8

ℹ️ 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".

Comment on lines +167 to +170
return () => {
prepared.delete(sessionId);
if (previousEntry) {
prepared.set(sessionId, previousEntry);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Guard rollback so it doesn't undo newer registrations

Make the rollback returned by registerSession conditional on the current mapping still matching the registration being rolled back. Right now it always runs prepared.delete(sessionId) and restores the previous entry, so if two acpLoadSession calls overlap for the same local session, a later failure from the older call can clobber the mapping established by the newer successful call; subsequent sends then fail with “Session not prepared” because getGooseSessionId points to stale/cleared state.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

@baxen baxen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! tested this and works on historical and new sessions

@tellaho tellaho added this pull request to the merge queue Apr 20, 2026
Merged via the queue into main with commit a7d78ee Apr 20, 2026
24 checks passed
@tellaho tellaho deleted the codex/context-window-tracking branch April 20, 2026 05:00
spikewang pushed a commit to spikewang/goose that referenced this pull request Apr 22, 2026
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants