Skip to content

feat(native-chat): answer /context in chat view for OpenClaude and OMP - #22294

Merged
brennanb2025 merged 17 commits into
mainfrom
brennanb2025/chat-context-command
Oct 5, 2026
Merged

brennanb2025 merged 17 commits into
mainfrom
brennanb2025/chat-context-command

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 15 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​873 $\color{#cf222e}{\Huge{\mathbf{−}}}$​2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​871
Prod 31 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​554 $\color{#cf222e}{\Huge{\mathbf{−}}}$​31 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​523

ELI5

In the terminal-backed Chat view, /context now shows an answer for OpenClaude and OMP sessions. Before, it showed nothing for either. Claude, Codex and Grok are not changed by this PR.

What Changed

OpenClaude.

  • Before: /context was not in the slash menu. Typed by hand, it ran in the hidden terminal. OpenClaude writes its report to the transcript, but the chat treated that row as internal noise and hid it, so nothing appeared.
  • After: /context is in OpenClaude's slash menu. The chat shows OpenClaude's own report, the same grid its terminal draws (model, used/window tokens and percentage, usage by category), in monospace with the terminal colors removed.

OMP.

  • Before: /context was in the menu, but it only opens a panel inside OMP's terminal and writes nothing to the session file. The chat showed only Ran /context.
  • After: the chat answers it in place of the Ran line, drawn the same muted way, for example Context: 21.7k / 272k tokens (8%), estimated from the last response. If OMP's model list does not give that model's window, the answer is Context: 21.7k tokens used, estimated from the last response. Before the first reply, or after a compaction until the next reply, it says usage is not known yet and becomes available after the agent next responds. When the remote host is older than this change, it says usage is not available for this session. Compactions now also show as a "Context compacted" divider in the OMP chat.

Claude, Codex, Grok. In the terminal-backed Chat view, Claude /context behaves as on main: it goes to the terminal and nothing appears in chat. Claude and Codex answer /context themselves in the structured chat session, which is where they are moving. Grok is a follow-up.

Mechanism.

  • OpenClaude: the live message preparation that already turns skill-command rows back into user turns now also handles a <local-command-stdout> row. Claude-format transcripts link every row to the row before it (parentUuid), and OpenClaude writes the reply row linked to its <command-name>/context</command-name> row. The transcript decoder now keeps that link on each message as an optional parentId (the id of the row this one follows). A reply row is shown only when the row it links to is a command whose declared reply is transcript (only /context for OpenClaude). The matched row becomes command output (plain text, ANSI removed). Replies to other local commands (/model, /compact, and so on) and Claude's rows stay hidden as before. A reply with no link (an older host) or whose command row is outside the loaded part of the transcript also stays hidden, as on main. The notice row now renders command output in monospace so the grid keeps its columns.
  • OMP: the OMP transcript decoder now keeps, on each reply, the provider and model that served it and the request's token usage (input, cache read, cache write, output), using the shared per-message usage type. Aborted and errored replies keep their model but not their usage, because OMP's own context gauge skips them. A compaction row becomes the existing compaction divider. omp models --json already reports each model's contextWindow, and the model list parser now keeps it as an optional contextWindowTokens on the model listing the chat already holds. The answer takes the newest reply with usage, sums input plus both cache counts, and looks up the window for that reply's provider/model. A compaction after that reply means nothing is known until the next one.
  • One declaration per command: how /context is answered is written once, as reply on the agent's slash-command catalog row (composer for OMP: the chat answers it and never sends it; transcript for OpenClaude: the chat shows the row the agent writes). The slash menu, the composer's answer, the transcript surfacing and the mobile menu all read that field, and the typed and picked send paths share one intercept. No other command declares a reply.
  • Removed from the earlier version of this PR: the Claude host answer, the [1m] window rule, and the resolved-model field threaded through Claude's model listing, the commit-message model types and the terminal session options, plus the Claude decoder's usage capture.

Compatibility. The new message fields (provider, usage) and the new listing field (contextWindowTokens) are optional. An older remote host sends neither, and the chat then says usage is not available or leaves out the window. An older client ignores them. The new parentId message field is optional too: an older host does not send it, and the chat then keeps OpenClaude's reply hidden, as on main; an older client ignores it. One change in what the host sends: OMP compaction rows now arrive as a system message. Older desktop clients already render that as the compaction divider, and mobile shows it as a line of text.

Why

  • OpenClaude: its window depends on per-session overrides and provider profiles that Orca cannot see, so the only correct number is OpenClaude's own. It already writes that number to the transcript, so the chat only has to stop hiding it. Having the chat compute its own answer would mean guessing the window.
  • OMP: nothing is written to replay, so the chat has to answer. The two inputs are data Orca already reads. The token count uses OMP's own formula: OMP computes the contextSnapshot.promptTokens it stores on replies as input + cache read + cache write, and in a real session they match (680 + 20,992 = 21,672). So no separate prompt-size field is stored. The window comes from OMP's own model list, looked up for the model that actually answered, not the one selected in the picker.
  • Claude and Codex are left out because their structured sessions already give the CLI's full report, and a terminal-lane estimate would be a second, less accurate answer. The earlier Claude version could only name a window for [1m] models. Grok is deferred to keep this PR focused.
  • Pairing a reply by the transcript's own row link, not by timestamps: OpenClaude writes the command and its reply in one batch with the same timestamp (in the recorded session a command row was even stamped 1 ms before the row it follows), and the chat breaks timestamp ties by id, so a timestamp rule needed tie-break rules and an ordering constraint against the skill-command rewrite. The link says which command a reply answers, so none of that is needed.
  • The declaration lives on the catalog row, not in the per-agent profile: the catalog row is already what makes /context appear in the menu, so the row and its reply cannot drift apart, and OMP has no profile (adding one would also change its skill-prefix handling).
  • Alternatives considered: sending OMP's /context to the terminal and pointing the user there (no answer in the chat), or running a second headless CLI per question (an extra process, and it would not see the live session's state).

Linked Issue

Related to #22292. That issue is about Claude. This PR no longer changes Claude's terminal-backed chat, because Claude's /context is answered in the structured chat session instead.

Visual Proof

Captured on macOS in an isolated dev instance of this branch (head 10996cd347), driven over the Chrome DevTools Protocol, with the terminal-backed Chat view for every agent.

OpenClaude: /context is listed in the slash menu.

01-openclaude-slash-menu.png

OpenClaude: typed /context shows OpenClaude's own report in monospace, laid out exactly as OpenClaude writes it, with no escape codes.

02-openclaude-context.png

OpenClaude: a second /context, picked from the menu. Each report stays with its own command.

03-openclaude-second-context.png

OpenClaude: /model afterwards. Its reply row stays hidden, as on main.

04-openclaude-model-hidden.png

OMP: the chat answers /context itself, and nothing is typed into OMP's terminal (next image). This machine has no working OMP provider login, so OMP never replied and the answer is the "not known yet" case. The case with a reply is covered by a unit test that runs a real OMP reply and a real omp models --json row: 21.7k / 272k (8%).

05-omp-context-chat.png

06-omp-terminal-untouched.png

Claude (unchanged): the menu does not list /context, which matches main, and a normal prompt still gets its reply.

07-claude-slash-menu.png

08-claude-prompt-roundtrip.png

Codex (unchanged): the slash menu opens and a normal prompt gets its reply.

09-codex-slash-menu.png

10-codex-prompt.png

Testing

  • OpenClaude 0.31.0 was run in an isolated home directory with a provider URL that goes nowhere, running /context, /cost, /context. Each /context wrote a caveat row, a <command-name>/context</command-name> row and a <local-command-stdout> row with the colored grid, and each reply row's parentUuid is its command row's uuid. (/cost replies in a system row the decoder does not read.) Timestamps are not reliably in write order: one command row was stamped 1 ms before the row it follows, which is why replies are matched by row link. The whole session file, with the session id and folder scrubbed, is the test fixture.

  • OMP 17.0.5: checked in the shipped CLI how /context is computed (last non-aborted, non-errored reply; promptTokens = input + cache read + cache write; compaction rows end the search). Also checked the compaction row fields and the contextWindow field in omp models --json. Checked against a real local session reply (identifiers removed).

  • Unit tests cover: the OMP decoder (provider, model and usage kept; aborted and errored replies skipped; compaction rows), the listing window through parsing and discovery, the terminal session options lookup, the answer text (window, no window, compaction, older host), real OpenClaude and OMP transcript lines driven through the transcript decoders into the reply surfacing and the usage answer (including the OMP reply's provider/model matching its listing row), the typed and picked composer paths, reply matching for OpenClaude by row link (a /context and a /model batch interleaved at one timestamp, a reply stamped before its command, no link, a command row outside the loaded window), the declared replies and the per-surface menus, the noise filter, monospace rendering, and mobile leaving OpenClaude and OMP /context out of its menu. The mobile test was not run through the mobile setup (it needs Expo, which is not installed here); it was run against a copy of the module with the shared code linked in. Each regression test was checked by removing the fix and confirming it fails.

  • Electron (macOS, isolated dev instance, CDP): OpenClaude typed and picked /context render its own report; Claude's menu omits /context; OMP answers in the chat without writing to its terminal. The OMP case with a reply was not captured live because this machine has no working OMP provider login.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

Mobile: mobile does not run the desktop message preparation and its markdown rows cannot keep the report's columns, so mobile leaves OpenClaude's /context out of its menu and send classification, exactly as on main. Mobile change: mobile also no longer lists OMP's /context. On main it was listed but did nothing there (OMP draws the answer inside its terminal and writes nothing the chat reads), and mobile cannot compute the answer the desktop composer gives. Typed by hand it still goes to the terminal. Known limitation: showing these answers on mobile would need the desktop's transcript handling plus a mobile layout that keeps the report's columns, left for a follow-up. OMP compaction rows show on mobile as a plain "Context compacted" line, the same way mobile already shows other agents' compaction rows. After OMP /new (also /clear), OMP starts a new session file. The answer resets only if the chat follows that switch, or for /clear sent from the chat. Linux and Windows were not exercised, and nothing here is platform-specific.

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Ensure no issues in: Security, Cross-platoform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

Over a terminal-backed chat, Claude's /context paints a grid only the hidden
terminal sees and records nothing in the transcript, so the chat showed
nothing. The chat host now answers it itself: the transcript decoder keeps
each assistant response's API usage and model, and the composer replies with
the same estimate the CLI statusline shows (input + cache tokens against the
model's window) instead of typing the command into the PTY. The command is
also listed in the curated Claude catalog so the slash menu offers it.
@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 6b216426-673a-4d05-a0b9-6a6934a53e17

📥 Commits

Reviewing files that changed from the base of the PR and between 0c36584 and 640b55d.

📒 Files selected for processing (10)
  • mobile/src/session/MobileNativeChatComposer.tsx
  • mobile/src/session/mobile-native-chat-send-classification.test.ts
  • mobile/src/session/mobile-native-chat-send-classification.ts
  • src/renderer/src/components/native-chat/NativeChatNoticeRow.test.tsx
  • src/renderer/src/components/native-chat/native-chat-live-message-preparation.test.ts
  • src/renderer/src/components/native-chat/native-chat-live-message-preparation.ts
  • src/shared/native-chat-command-output.test.ts
  • src/shared/native-chat-command-output.ts
  • src/shared/native-chat-context-usage.test.ts
  • src/shared/native-chat-context-usage.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 4 remain after this review.


📝 Walkthrough

Walkthrough

Native chat now records OMP assistant model, provider, token usage, and compaction metadata. Context usage uses model-specific context-window token counts. Supported local commands can return formatted answers without PTY dispatch. OpenClaude command output is surfaced and rendered in chat. Composer interruption logic now uses a dedicated hook.

Priority: ➖ Normal

Merge Risk: 🟡 Moderate · up to 640b5

Some locally answered commands can unintentionally carry an earlier image into the next message, while OMP /context can omit the model window and percentage. Both issues should be fixed before merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.17% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 53 functions across 46 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main change: adding /context answers in the chat view for OpenClaude and OMP.
Description check ✅ Passed The description covers the required explanation, user-facing changes, rationale, linked issue, visual proof, testing, compatibility, and checklist. The Review section is blank, but this does not make …
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: e3dfc9a3-ed50-4d85-b4d9-f90b654ca319

📥 Commits

Reviewing files that changed from the base of the PR and between a8786c0 and 52a3ed1.

📒 Files selected for processing (21)
  • src/main/native-chat/transcript-line-decoders-claude-usage.test.ts
  • src/main/native-chat/transcript-line-decoders-claude.ts
  • src/renderer/src/components/native-chat/NativeChatComposer.tsx
  • src/renderer/src/components/native-chat/NativeChatResolvedView.tsx
  • src/renderer/src/components/native-chat/native-chat-command-marker.ts
  • src/renderer/src/components/native-chat/native-chat-composer-types.ts
  • src/renderer/src/components/native-chat/native-chat-context-usage-answer.ts
  • src/renderer/src/components/native-chat/native-chat-pending.test.ts
  • src/renderer/src/components/native-chat/use-native-chat-composer-interrupt.ts
  • src/renderer/src/components/native-chat/use-native-chat-local-command-answer.ts
  • src/renderer/src/components/native-chat/use-native-chat-picker-command-dispatch.ts
  • src/renderer/src/components/native-chat/use-native-chat-pty-composer-send.ts
  • src/renderer/src/i18n/locales/en.json
  • src/shared/claude-context-window.ts
  • src/shared/native-chat-context-usage.test.ts
  • src/shared/native-chat-context-usage.ts
  • src/shared/native-chat-local-commands.test.ts
  • src/shared/native-chat-local-commands.ts
  • src/shared/native-chat-slash-commands.test.ts
  • src/shared/native-chat-slash-commands.ts
  • src/shared/native-chat-types.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.

Comment on lines +63 to +76
const localAnswer = answerCommandLocally?.(text) ?? null
if (localAnswer !== null) {
// Why: the host answers in place of the PTY, so the composer resets as
// after a send but nothing is written to the terminal.
emitNativeChatPickerItemAccepted({ agent, itemKind: 'command' })
emitNativeChatSendClassified({ agent, outcome: 'command' })
onSlashCommand?.(text, localAnswer)
setHistory((previous) => pushHistory(previous, text))
setDraft('')
setCaret(0)
setActiveSuggestion(0)
clearSkillOrigin()
setNotice(null)
return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

sed -n '1,140p' src/renderer/src/components/native-chat/use-native-chat-picker-command-dispatch.ts
sed -n '40,100p' src/renderer/src/components/native-chat/use-native-chat-pty-composer-send.ts
rg -n 'clearImageAttachments|imageAttachments|useNativeChatPickerCommandDispatch' src/renderer/src/components/native-chat

Repository: stablyai/orca

Length of output: 17356


🏁 Script executed:

sed -n '145,340p' src/renderer/src/components/native-chat/NativeChatComposer.tsx
sed -n '1,220p' src/renderer/src/components/native-chat/use-native-chat-picker-command-dispatch.test.tsx
rg -n -C 5 'answerCommandLocally|/context|context' src/renderer/src/components/native-chat src/renderer/src | head -160

Repository: stablyai/orca

Length of output: 26981


🏁 Script executed:

printf '%s\n' '--- NativeChatComposer ---'
sed -n '145,340p' src/renderer/src/components/native-chat/NativeChatComposer.tsx
printf '%s\n' '--- picker dispatch tests ---'
sed -n '1,220p' src/renderer/src/components/native-chat/use-native-chat-picker-command-dispatch.test.tsx
printf '%s\n' '--- local answer references ---'
rg -n -C 5 'answerCommandLocally|/context|context' src/renderer/src/components/native-chat src/renderer/src | head -160

Repository: stablyai/orca

Length of output: 27586


🏁 Script executed:

printf '%s\n' '--- answerCommandLocally bindings ---'
rg -n -C 4 'answerCommandLocally' src/renderer/src --glob '*.ts' --glob '*.tsx'
printf '%s\n' '--- context answer bindings ---'
rg -n -C 5 'formatNativeChatContextUsageAnswer|contextUsage|context usage|/context' src/renderer/src/components/native-chat src/renderer/src --glob '*.ts' --glob '*.tsx' | head -240

Repository: stablyai/orca

Length of output: 37331


Clear image attachments after a locally answered picker command.

In a PTY session, selecting /context with an image attached enters the local-answer branch. That branch resets the composer and returns without clearing the attachment. The next message can include that stale image.

Suggested fix
         setCaret(0)
         setActiveSuggestion(0)
         clearSkillOrigin()
+        clearImageAttachments()
         setNotice(null)
         return
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const localAnswer = answerCommandLocally?.(text) ?? null
if (localAnswer !== null) {
// Why: the host answers in place of the PTY, so the composer resets as
// after a send but nothing is written to the terminal.
emitNativeChatPickerItemAccepted({ agent, itemKind: 'command' })
emitNativeChatSendClassified({ agent, outcome: 'command' })
onSlashCommand?.(text, localAnswer)
setHistory((previous) => pushHistory(previous, text))
setDraft('')
setCaret(0)
setActiveSuggestion(0)
clearSkillOrigin()
setNotice(null)
return
const localAnswer = answerCommandLocally?.(text) ?? null
if (localAnswer !== null) {
// Why: the host answers in place of the PTY, so the composer resets as
// after a send but nothing is written to the terminal.
emitNativeChatPickerItemAccepted({ agent, itemKind: 'command' })
emitNativeChatSendClassified({ agent, outcome: 'command' })
onSlashCommand?.(text, localAnswer)
setHistory((previous) => pushHistory(previous, text))
setDraft('')
setCaret(0)
setActiveSuggestion(0)
clearSkillOrigin()
clearImageAttachments()
setNotice(null)
return

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

claudeContextWindowTokens keys the 1M window off a [1m] suffix that Claude Code does not write into the transcript's message.model, so every 1M-context session reports a percentage roughly 5× too high.

Reviewed changes

  • Decoder usage capture — Claude assistant rows now carry model and the API token usage; all-zero rows the CLI synthesizes are dropped.
  • Context derivation — newest usage-bearing response (input + cache read + cache write) against the model window, with compact token formatting.
  • Host-answered /context — the typed and picker paths record the answer on the command marker instead of writing to the PTY; the marker renders the answer in place of Ran /x.
  • Catalog + i18n — context added to the Claude slash catalog; two new English strings.
  • Composer interrupt refactor — ESC/stop logic extracted to use-native-chat-composer-interrupt.ts with no behavior change.

The structured lane really is untouched: dispatchPickerCommand and send both route to sendStructured when structuredTransport is set, so answerCommandLocally never intercepts it. The text.trim() in the PTY send hook is also safe because classifyNativeChatSend gates on the untrimmed draft.

ℹ️ Mobile now offers /context but cannot answer it

getAgentSlashCommands is shared with the mobile app, so adding context to CLAUDE_COMMANDS surfaces it in the mobile picker too. Mobile forwards it to the terminal as before, where Claude paints a grid the chat never sees — so on mobile the new menu row looks inert. Worth a deliberate decision: keep the row desktop-only, or wire the same host answer into the mobile composer.

Technical details
# Mobile picker offers a command only the desktop host answers

## Affected sites
- `src/shared/native-chat-slash-commands.ts` — `context` added to `CLAUDE_COMMANDS`, which is the
  shared catalog (`getAgentSlashCommands`) consumed by mobile.
- Mobile composer (`mobile/src/session/…`) does not pass any `answerCommandLocally` equivalent.

## Open questions for the human
- Is a discoverable-but-inert `/context` on mobile acceptable for this PR, or should the catalog
  entry be scoped to the desktop host until mobile answers it?

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread src/shared/claude-context-window.ts Resolved
The /context answer sized the window from the transcript's model id, which
never carries the [1m] suffix, so a 1M session read as 200k (e.g. 450k / 200k,
225%). The window now comes from the session's tracked model resolved through
the host CLI's own model listing (the resolvedModel field the list_models probe
already returned), and only a [1m] id that matches the model the transcript
last answered with is sized; anything else reports the used figure alone.

Also:
- Offer /context only in the desktop terminal-backed composer; the shared
  catalog is mobile's too, and mobile cannot answer it.
- Answer a typed /context even with images attached, as the picker does, and
  keep the attachments armed instead of sending them to the terminal.
- Report nothing until a response lands after a /compact or /clear sent from
  the chat, instead of the pre-compaction size.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Since the prior pullfrog review (52a3ed1), two commits landed and both prior threads were addressed:

  • Reworked the 1M window source — claudeContextWindowTokens now takes the host's resolved session model (resolvedSessionModel(), the tracked catalog entry's resolvedModel) and only states 1M when its base matches the transcript's last model, instead of keying off a [1m] suffix the transcript never carries.
  • Degraded the answer honestly — windowTokens/percentage are now nullable; when no window can be established the host prints the used figure alone rather than guessing 200k.
  • Added a post-reset guard — /context answers "unavailable" when the newest usage-bearing response predates a /compact or /clear this pane sent, backed by the generalized latestCommandSentAt marker helper.
  • Kept attachments on a host-answered command — the typed and picker local-answer paths now both leave image attachments armed (a real send still clears them), with tests pinning each path.
  • Scoped /context to the terminal lane — removed from the shared per-agent catalog so mobile no longer offers an inert row; ptyLaneCommands merges the local commands only for the desktop PTY menu.
  • Carried resolvedModel end to end — Claude's list_models probe now parses and spreads it through CommitMessageModel/CatalogModel into the discovery cache, covered by tests at each hop; hosts that predate the field degrade to an unknown window.

I traced the discovery pipeline (probe → discoverNativeChatCatalogModels → enrichment cache → replaceModels → resolvedSessionModel()) and confirmed /context reaches the typed path as command via the catalog-backed classifyNativeChatSend. The structured lane remains untouched.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

…orts usage

A host that predates usage decoding, or a scraped view, decodes Claude's
replies without a model or usage, so the chat promised an answer after the
next response that would never come. Once the agent has answered and no
answer names its model, say usage is not available for this session.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 4d602afb-7791-4a07-a3db-99986a77f06d

📥 Commits

Reviewing files that changed from the base of the PR and between 06dc157 and 20f03c6.

📒 Files selected for processing (4)
  • src/renderer/src/components/native-chat/native-chat-context-command.test.tsx
  • src/renderer/src/components/native-chat/native-chat-context-usage-answer.ts
  • src/renderer/src/components/native-chat/use-native-chat-local-command-answer.ts
  • src/renderer/src/i18n/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/src/i18n/locales/en.json

Included review availability: Your plan provides up to 10 included reviews per hour; 2 remain after this review.

Comment on lines +30 to +31
if (!sessionReportsUsage(args.messages)) {
return formatNativeChatContextUsageUnreported()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 8 'model|usage|NativeChatMessage' \
  src/main/native-chat/transcript-line-decoders-claude.ts \
  src/shared/native-chat-context-usage.ts \
  src/shared/native-chat-types.ts

Repository: stablyai/orca

Length of output: 19870


🏁 Script executed:

#!/bin/bash
# Read the reviewed file at the critical lines
sed -n '1,50p' src/renderer/src/components/native-chat/use-native-chat-local-command-answer.ts

# Search for sessionReportsUsage definition and related functions
rg -n 'sessionReportsUsage|formatNativeChatContextUsageUnreported|formatNativeChatContextUsageAnswer' src/ -A 5 -B 1

Repository: stablyai/orca

Length of output: 7720


🏁 Script executed:

sed -n '49,70p' src/renderer/src/components/native-chat/use-native-chat-local-command-answer.ts

Repository: stablyai/orca

Length of output: 838


Verify that assistant messages carry model whenever they carry usage.

sessionReportsUsage() returns false when an assistant (non-hook) message lacks a model field, triggering an early return that reports the session as permanently unavailable. However, deriveNativeChatContextUsage() can derive used tokens from an assistant message with usage but no model, by setting model to null and omitting the window size.

The Claude decoder emits model and usage as independent optional properties (lines 133–134 of transcript-line-decoders-claude.ts). If the decoder can produce a message with usage but no model, the early return at lines 30–31 would suppress derivable usage.

Confirm that the decoder guarantees model is present whenever usage is emitted for an assistant message. If this guarantee does not hold, either:

  • Remove the early return and let deriveNativeChatContextUsage() attempt derivation (it handles null model correctly), or
  • Check for usage presence in sessionReportsUsage() in addition to or instead of model presence.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Since the prior pullfrog review (06dc157), one commit landed:

  • Added an "unreported" answer — answerNativeChatLocalCommand now calls a new sessionReportsUsage guard before deriving: when the agent has answered but no decoded response names a model (a host that predates usage decoding, or a scraped view), /context says "Context usage is not available for this session." instead of wrongly promising a later answer.
  • Kept the pending answer for live previews — hook-sourced rows are ignored by that guard, so a preview with no transcript reply still reads "not known yet".
  • Added the English string and a test — components.native-chat.context.unreported, with the test pinning the unreported, empty-session, and hook-preview branches.

I traced the new guard against the decoder (model/usage are attached together, synthetic rows carry <synthetic> with no usage, scrape rows carry neither) and the routing: it sits before the compact/clear staleness check, so a post-reset /context still reports "not known yet" rather than "unavailable". The full PR diff and both prior threads (already resolved) were re-read.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Claude and Codex answer /context themselves in the structured chat lane, so
the terminal-backed Claude answer, its [1m] window rule, and the resolved-model
plumbing through the model listing go away.

OMP's /context only paints a panel inside its TUI. The OMP decoder now keeps
the provider, model and usage of each reply (none for aborted or errored
replies, which OMP's own gauge skips) and turns compaction rows into the
existing compaction boundary. The OMP model listing keeps each model's
context window, and the chat answers /context from the last reply's prompt
size against the window of the provider/model that served it.
OpenClaude writes its /context grid to the transcript as a
<local-command-stdout> row, which the chat hid as harness noise. The live
message preparation now turns the stdout row that answers an opted-in command
(/context for OpenClaude) into command output with the ANSI colors stripped,
and the notice row renders command output in monospace so the grid keeps its
columns. Replies to other local commands stay hidden, and Claude is not opted
in. OpenClaude's slash menu now lists /context; Claude's does not.
@brennanb2025 brennanb2025 changed the title feat(native-chat): answer /context in the Chat view for Claude feat(native-chat): answer /context in chat view for OpenClaude and OMP Sep 22, 2026
…st replaced

The previous commit rewrote NativeChatNoticeRow.test.tsx wholesale, deleting
main's compaction, tone, plan, provider-notice and old-reader coverage. Restore
it and pin monospace command output through the message row instead.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟠 Major · Propagate contextWindowTokens into the capability payload. · commit-message-agent-spec.ts:203-210

src/shared/commit-message-agent-spec.ts:203-210
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Propagate contextWindowTokens into the capability payload.

toCommitMessageAgentCapability() drops the new field. A remote capability response therefore loses the discovered window before the renderer builds CatalogModel.contextWindowTokens.

Copy the field when it is defined.

Proposed fix
     models: spec.models.map((model) => ({
       id: model.id,
       label: model.label,
       ...(model.description ? { description: model.description } : {}),
       ...(model.thinkingLevels ? { thinkingLevels: [...model.thinkingLevels] } : {}),
       ...(model.defaultThinkingLevel ? { defaultThinkingLevel: model.defaultThinkingLevel } : {}),
-      ...(model.supportsFastMode ? { supportsFastMode: true } : {})
+      ...(model.supportsFastMode ? { supportsFastMode: true } : {}),
+      ...(model.contextWindowTokens !== undefined
+        ? { contextWindowTokens: model.contextWindowTokens }
+        : {})
     }))

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 55d97f90-cd38-4299-bc0a-e26209c34e58

📥 Commits

Reviewing files that changed from the base of the PR and between 20f03c6 and 0c36584.

📒 Files selected for processing (28)
  • src/main/native-chat/transcript-line-decoders-omp.ts
  • src/main/native-chat/transcript-line-decoders.omp.test.ts
  • src/renderer/src/components/native-chat/NativeChatNoticeRow.test.tsx
  • src/renderer/src/components/native-chat/NativeChatNoticeRow.tsx
  • src/renderer/src/components/native-chat/NativeChatResolvedView.tsx
  • src/renderer/src/components/native-chat/native-chat-command-marker.ts
  • src/renderer/src/components/native-chat/native-chat-context-command.test.tsx
  • src/renderer/src/components/native-chat/native-chat-live-message-preparation.test.ts
  • src/renderer/src/components/native-chat/native-chat-live-message-preparation.ts
  • src/renderer/src/components/native-chat/native-chat-pty-session-options.ts
  • src/renderer/src/components/native-chat/native-chat-session-option-discovery.ts
  • src/renderer/src/components/native-chat/native-chat-session-option-enrichment.test.ts
  • src/renderer/src/components/native-chat/use-native-chat-local-command-answer.ts
  • src/renderer/src/components/native-chat/use-native-chat-picker-command-dispatch.ts
  • src/renderer/src/components/native-chat/use-native-chat-pty-composer-send.ts
  • src/shared/agent-session-option-catalog-omp.test.ts
  • src/shared/agent-session-option-catalog-types.ts
  • src/shared/commit-message-agent-spec.ts
  • src/shared/native-chat-command-output.test.ts
  • src/shared/native-chat-command-output.ts
  • src/shared/native-chat-context-usage.test.ts
  • src/shared/native-chat-context-usage.ts
  • src/shared/native-chat-local-commands.test.ts
  • src/shared/native-chat-local-commands.ts
  • src/shared/native-chat-slash-commands.test.ts
  • src/shared/native-chat-slash-commands.ts
  • src/shared/native-chat-types.ts
  • src/shared/omp-model-list-probe.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/shared/native-chat-types.ts
  • src/renderer/src/components/native-chat/NativeChatResolvedView.tsx

Included review availability: Your plan provides up to 10 included reviews per hour; 3 remain after this review.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

The OMP and OpenClaude /context paths are sound and covered by targeted tests, but this revision drops the pre-existing NativeChatNoticeRow test coverage.

Reviewed changes

Since the prior pullfrog review (20f03c6), this PR pivoted from a Claude host answer to OMP + OpenClaude:

  • Answered /context for OMP from the transcript. The decoder now keeps each reply's provider/model/usage and turns compaction rows into the compaction divider; the answer sums input + cacheRead + cacheWrite and resolves the window from omp models --json for the model that actually served the reply.
  • Surfaced OpenClaude's own /context report. A <local-command-stdout> row is paired with the newest command envelope written at or before it and rendered as plain command output, gated to OpenClaude.
  • Removed the Claude path (claude-context-window, the resolved-model threading) and scoped the host answer to OMP.
  • Added a command-output presentation rendered in monospace via NativeChatNoticeRow.
  • Rewrote NativeChatNoticeRow.test.tsx, dropping its prior coverage (inline).

I verified the new tests pass, ran the affected integration suites (native-chat-context-command, live-message-preparation, pending, session-option-enrichment, composer-catalog, agent-profiles), and confirmed the changed-code quality gate is clean. The OpenClaude surfacing correctly depends on context being in getVerifiedNativeChatCommands('openclaude') so surfaceSkillInvocationUserTurns leaves the envelope for surfaceNativeChatCommandOutputs to pair.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

…ts report

The shared slash catalog now lists /context for OpenClaude because the desktop
chat surfaces its transcript reply. Mobile's transcript fold does not, so the
command did nothing there. Mobile now offers the catalog minus commands answered
only by a surfaced reply, which returns its OpenClaude menu and send
classification to main's behavior.
Left from the earlier design; the answer text states the estimate itself.
…text

Skill surfacing ran first and rewrote non-catalog envelopes such as /model into
plain user text, so command-output pairing no longer saw them. A later /model
reply then paired with the earlier /context envelope and appeared in the chat.
Pair outputs against the raw envelopes before skill surfacing.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Since the prior pullfrog review (0c36584), four commits landed, all addressing prior feedback:

  • Kept OpenClaude /context off mobile — getMobileNativeChatCommands filters out commands answered only by a surfaced reply, so mobile's menu and send classification no longer offer an inert row.
  • Fixed reply pairing — prepareNativeChatLiveMessages now surfaces command outputs before skill-envelope rewriting, so /model's envelope is still visible to the pairing pass and its reply no longer mispairs with an earlier /context.
  • Dropped the dead estimated flag — NativeChatContextUsage.estimated was always true and never read; contextTokensFromUsage is now module-private.
  • Restored the notice-row suite — the 13 pre-existing NativeChatNoticeRow tests return alongside the new monospace command-output test.

I re-read the full PR diff, re-ran the affected suites (native-chat-command-output, native-chat-context-usage, native-chat-live-message-preparation, NativeChatNoticeRow, mobile mobile-native-chat-send-classification) and pnpm tc:web; all pass. The prior open thread on the dropped test coverage is resolved.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

…n and show it as an aside

After a compaction the agent has already answered, so the unknown-usage
line now promises the next response instead. The host answer is one
sentence, so it draws as the muted line it replaces rather than a
monospace grid. The provider/model selector now comes from one helper
shared by the OMP listing parser and the answer, and new tests drive
real OpenClaude and OMP transcript lines through the decoders.

Co-Authored-By: Claude <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Since the prior pullfrog review (640b55d), one commit landed:

  • Reworded the OMP pending answer — "available once the agent has answered in this session" becomes "available after the agent next responds", which also reads correctly for the post-compaction gap where a response predates the boundary.
  • Drew the OMP answer as the aside it replaces — commandMarkersAsMessages now renders marker.output as plain system text instead of presentation: 'command-output'; the monospace grid presentation stays reserved for OpenClaude's transcript-sourced report, which is the only column-laid-out output.
  • Shared the OMP model selector — ompModelSelector is extracted and reused by both parseOmpModelList and the renderer's window lookup, removing the duplicated provider/model rule.
  • Added decoder-level coverage — a new Claude-decoder test drives the OpenClaude /context rows end to end (pairing, noise filtering, colors stripped), and the OMP decoder test pins prompt-to-window derivation plus the compaction boundary.

I re-ran the affected suites (transcript-line-decoders-claude-command-output, transcript-line-decoders.omp, native-chat-pending, native-chat-context-command, agent-session-option-catalog-omp) and pnpm tc:web / pnpm tc:node; all pass. The prior open thread is resolved.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

…context replies on the catalog

A `<local-command-stdout>` row now answers the command row its `parentUuid`
names, carried on decoded messages as the optional `parentId`, instead of the
newest command row at or before its timestamp. A reply with no loaded linked
row (older host, command outside the tail window) stays hidden.

How each agent's `/context` is answered is declared once, as `reply` on its
catalog row (`composer` for OMP, `transcript` for OpenClaude). The slash menu,
the composer's answer, the transcript surfacing and the mobile catalog all
derive from it, and both send paths share one intercept. Mobile no longer lists
OMP `/context`, which did nothing there.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Since the prior pullfrog review (9679aef), one refactor commit landed:

  • Paired command replies by transcript row link — the Claude decoder now carries parentUuid as an optional parentId, and surfaceNativeChatCommandOutputs surfaces a <local-command-stdout> row only when the row it links to parses as a command envelope declaring a transcript reply. This replaces the timestamp/order heuristic, so a reply stamped before its command still pairs, and a reply with no loaded linked row (older host, or a command envelope outside the loaded tail window) stays hidden — the documented degradation.
  • Moved the reply declaration onto the catalog row — SlashCommandSuggestion.reply is now the single place /context is declared (composer for OMP, transcript for OpenClaude); the slash menu, getNativeChatCommandReply, getAgentAnsweredNativeChatCommands, the composer answer, and transcript surfacing all read it.
  • Routed both send paths through one intercept — answerNativeChatCommandInComposer now handles the typed and picker paths (trim, history, draft/caret, skill origin, notice), with attachments staying armed, removing the duplicated local-answer blocks.
  • Kept /context off mobile for both agents — mobile's menu and send classification now derive from getAgentAnsweredNativeChatCommands, so OMP's inert /context no longer appears.
  • Bounded parentId on the worker wire — boundWorkerTranscriptMessages maps parentId through the same boundIdentifier digest as the parent's id, keeping the link joinable.
  • Removed native-chat-local-commands.ts and its obsolete test, superseded by the catalog declaration; no remaining importers.

I traced the link end to end: the decoder sets id from record.uuid and parentId from record.parentUuid (confirmed against the new real-session fixture), the assembler's mergeOne keeps transcript objects verbatim (same-source never replaces; cross-source replacement inserts the transcript copy as-is), and RPC sanitizeMessage spreads the message, so parentId reaches the renderer intact. The prepareNativeChatLiveMessages order swap is neutral for the tested cases because surfaceSkillInvocationUserTurns leaves catalog envelopes untouched. Ran the seven affected suites (66 tests), pnpm typecheck:node, and pnpm typecheck:web — all green.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status: ready to merge

Scope. /context now answers in the terminal-backed Chat view for OpenClaude and OMP.

  • Claude and Codex were deliberately taken out: they are moving to the structured chat session, where the CLI already answers /context.
  • Grok is left for a follow-up. Its context data is only in internal files Orca does not read yet.
  • Terminal-backed Claude behaves exactly as on main.

How it works

  • OpenClaude writes its own /context report into its transcript, and the chat used to hide it. The chat now shows that report. It finds the report through the transcript's own link from each reply row to its command row, not through timestamps. A captured OpenClaude session showed that timestamps are not reliably in write order.
  • OMP only draws its answer inside its terminal. So the chat answers from data Orca already reads:
    • tokens used from OMP's last successful reply;
    • the window from the model list omp models reports, for the model that actually answered;
    • OMP's compaction rows count as a reset.
  • One declaration per command (reply: 'composer' | 'transcript' on the slash command entry) drives the menu, the composer's answer, the transcript surfacing and the mobile menu, so they cannot disagree.

Review. Five review rounds plus a separate architecture review. Fixed along the way:

  • the first design's context window was wrong for most 1M sessions, and Claude was removed as a result;
  • a /model reply could pair with an earlier /context;
  • main's notice-row tests had been deleted and are restored;
  • mobile listed commands it cannot answer;
  • typed and menu-picked /context treated attachments differently.

The architecture review led to matching replies by row link and to the single declaration above. The final round found nothing to change.

Validation

  • Typecheck, lint, the code-quality gate and the localization checks pass.
  • Targeted tests pass: 193 in the changed files, and more than 3,700 across the chat folders.
  • Each regression test was proven by removing its fix and watching it fail.
  • CI is green. The one red "mobile web app bundle" test hit an unrelated redirect screen and passed on re-run.
  • Electron on macOS: see the Visual Proof section of the PR description.

Known gaps

  • The OMP case with a reply was not captured live because the test machine had no working OMP provider login. A unit test covers it with a real OMP reply and model-list row.
  • Mobile shows neither answer. It keeps main's behavior for OpenClaude and no longer lists OMP /context, which did nothing there.
  • Linux and Windows were not exercised; nothing here is platform-specific.
  • After an OMP model switch, the window shown is the model that answered last until the next reply.

…are no transcript reply

Every Claude-format row now carries its parent link, so once a session holds any
local-command reply (/model, /compact) the surfacing pass built a map of every
message on each live update and changed nothing for agents like Claude. Agents
whose catalog declares no transcript reply now return before the scan.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Since the prior pullfrog review (ef691da), one commit landed:

  • Skipped command-output surfacing when no reply is declared — surfaceNativeChatCommandOutputs now builds the set of catalog command names whose row declares reply: 'transcript' once per call and returns the message list unchanged when that set is empty, so agents like Claude, Codex, and OMP skip the per-update map build entirely. The per-row getNativeChatCommandReply lookup is replaced by a Set.has, which resolves against the same getVerifiedNativeChatCommands(agent) catalog and is equivalent for the unique command names it holds.

I confirmed the guard preserves behavior: the catalog is static per agent with no duplicate names, so the set membership matches the old lookup exactly, and the existing "leaves agents that declare no transcript reply untouched" case already pins the early return. native-chat-command-output.test.ts passes (5 tests).

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status: ready to merge (head 10996cd347)

Result: no open defects. All CI checks pass, and the Electron check passes for every desktop case. Mobile QA on an emulator is blocked (details below).

Review loop

  • Round 1 fixed one performance issue, in 10996cd347. Every Claude-format row now carries its parent link, so once a Claude session had any local-command reply row (from /model or /compact), each transcript update rebuilt a lookup of the whole conversation. For Claude that work never changed anything. The scan now returns immediately for any agent whose catalog declares no transcript reply. That covers every agent except OpenClaude, so Claude, Codex and the others no longer pay for it.
  • Round 2 was clean and made no changes. It traced every reader of the new optional message fields (parentId, model, provider, usage) and of the new command-output presentation. It also checked the typed and picked /context paths end to end, and removed each fix in turn to confirm the regression tests fail without it: 19 of 20 do. The 20th is round 1's early return, which doesn't change behavior.

Design

  • Why /context showed nothing before:
    • OpenClaude writes its report as a transcript row, and the noise filter hid it.
    • OMP draws the report only in its terminal and writes nothing to its session file.
  • How the PR removes each cause:
    • OpenClaude: the reply is shown when its row links to a /context command row. The link is the transcript's own, and the timestamps are measured out of write order.
    • OMP: the chat answers from data Orca already reads, the per-reply usage and the context window from omp models --json.
  • Checked against OMP 17.0.5 itself: the token formula (input + cache read + cache write), skipping aborted and errored replies, the provider/model key used to look up the window, and resetting after compaction.
  • Alternative considered and not taken: push OMP's own context figure through Orca's OMP status extension. That would add a second source for a fact the transcript already records, so the answer is derived from the per-reply usage on the rows instead.

Readiness checklist

Run at the start (ef691da276) and at the end (10996cd347). Both PASS, with no P0 or P1 findings. Remaining minor notes:

  • Two plain functions live in a file named as a React hook (use-native-chat-local-command-answer.ts).
  • OMP compaction rows now show as a "Context compacted" line, which is also what older clients see.

CI

All checks are green. One test in shard 8, the worktree scan-cache test (lineage-and-scan-cache-part-03.spec.ts:353), failed once; that code isn't touched by this PR. It passed locally at the same head and passed when the failed jobs were re-run.

Electron QA (macOS, isolated dev instance, head 10996cd347)

  • OpenClaude — PASS:
    • /context is in the slash menu.
    • Typed and picked /context each show OpenClaude's own report in monospace, with each report under its own command.
    • /model's reply stays hidden.
  • OMP — PASS: the chat answers /context without writing to OMP's terminal. The reply case couldn't be captured live because this machine has no working OMP provider login, so OMP never replied; a unit test covers it.
  • Claude and Codex — PASS, unchanged: Claude's menu doesn't list /context, as on main. Normal prompts get their replies on both.

The screenshots are in the PR body under Visual Proof.

Not covered

  • Mobile emulator QA is blocked. The shared emulator helper was already driving another lane's simulator, and attaching this lane would have ended that one.
    • The mobile change is small: OpenClaude's and OMP's /context are dropped from the mobile slash menu over terminal sessions. Its logic was run directly and gives the expected output: /context is classified as not a command, and /compact still is.
    • The mobile test suite needs Expo, which isn't installed here, so it wasn't run.
  • Linux and Windows weren't exercised. Nothing in the change is platform-specific.

Heads-up on merge order with #22301

That open PR defines its own token-usage type, context-token sum and 18.6k count formatter. It also adds the same composer stop-action hook file, use-native-chat-composer-interrupt.ts. Whichever PR merges second should switch to the other's versions and resolve that file.

Resolve conflicts with main and adopt the context usage helpers that
landed there: the terminal-lane usage type is now AgentSessionTokenUsage,
the token sum is contextTokensFromUsage, and the answer uses the shared
formatContextTokenCount. Add the /context answer strings to the
non-English catalogs.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

This run re-reviewed the only commit since the prior pullfrog review (10996cd): c1b1c9b, a merge of origin/main (98171a89) into the branch. git show c1b1c9b --cc names 31 conflict-resolved files; I read all 22 combined-hunk files and confirmed neither side's work was dropped.

  • Adopted main's shared context-usage helpers — the branch's NativeChatTokenUsage, private contextTokensFromUsage, and local formatContextTokenCount are replaced by main's AgentSessionTokenUsage / contextTokensFromUsage (agent-session-context-usage.ts) and the renderer's formatContextTokenCount (native-chat-context-usage-summary.ts). Expectations updated for main's capital 1M.
  • Combined the Claude decoder changes — main's unwrapClaudePastedContentBlock on user blocks sits alongside the branch's ...parent (parentId), which the OpenClaude reply pairing reads.
  • Combined the composer optimistic-send refactor with the local-answer intercept — main's optimisticSendOutcome / record / delivery and the branch's answerCommandLocally and onSlashCommand(command, output?) both survive; answerNativeChatCommandInComposer still early-returns the typed and picked paths.
  • Merged the NativeChatMessage shape — main's AgentJournalProducerLinkage fields (journalPosition, sentAs, queued, …) plus the branch's model / provider / usage / parentId; the worker payload strips journalPosition while still bounding parentId.
  • Kept the command-output presentation ahead of main's host-status rows — the OMP aside and OpenClaude monospace grid branches are unchanged.
  • Added the /context answer strings to the non-English catalogs and reconciled main's new native-chat keys in en and translations.

Verified: 11 affected suites (111 tests) green, pnpm tc:node, pnpm tc:web, pnpm run verify:localization-catalogs, and pnpm run verify:rpc-params-catalog all pass. All prior pullfrog threads are resolved.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@brennanb2025
brennanb2025 merged commit ca2cdc0 into main Oct 5, 2026
34 checks passed
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.

1 participant