Skip to content

feat(acp): compact Grok, OpenCode and OMP chats - #26502

Merged
brennanb2025 merged 10 commits into
mainfrom
brennanb2025/acp-compaction
Oct 9, 2026
Merged

brennanb2025 merged 10 commits into
mainfrom
brennanb2025/acp-compaction

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

ELI5

Grok, OpenCode and OMP chats could not run their native /compact command through Orca. During review, an OMP conversation that was too short to compact also appeared to finish with an empty “Worked for 0s” turn: the warning existed, but the transcript folded it away.

You can now compact these chats and keep chatting. Successful compaction shows “Context compacted.” OMP’s “Nothing to compact” and “Already compacted” outcomes show their actual warning reason, including after reopening or paging through history.

What Changed

Orca sends /compact as an ordinary prompt through the existing Agent Client Protocol connection, inside the command turn the chat already owns. Its response settles that turn through the existing queue, Stop and process-exit handling. Compaction reply text becomes one outcome row; tool and context updates still flow through.

Skipped outcomes carry their own presentation hint, so visibility does not depend on loading the original command on the same history page. Previously saved OMP outcomes derive that hint from their own reserved identity. Ordinary warnings keep their existing folding behavior. A skipped command does not reset context usage or claim successful compaction.

Why

Invoking native compaction through an ordinary prompt follows the common pattern. Reusing the command’s existing owner avoids another scheduler or a persistent flag. Giving the outcome its own meaning removes the page-boundary failure instead of exempting warnings whenever a nearby command is found.

Differences from the common pattern:

  • Intended: Orca renders one existing outcome row rather than the agent’s compaction reply text. A skipped outcome keeps the existing warning text rather than adding a separate operation title. The already-supported optional presentation string lets older readers retain that text; older desktop versions retain their previous folding behavior.
  • Temporary: OpenCode’s slash menu still lists only advertised commands, so /compact must be typed. Follow-up: include the host-supported command in that menu; no separate ticket exists.

Linked Issue

Follow-up to #25845 and #26401; no separate issue.

Visual Proof

Earlier laptop evidence on 537a7b45003: Grok and OpenCode each show “Context compacted.” These screenshots predate the OMP visibility correction; their follow-ups were not captured.

grok-compact.png

opencode-compact.png

Before the OMP correction, the installed 17.0.5 agent’s skipped command appeared as an empty collapsed “Worked for 0s” turn:

omp-compact.png

On final head cb055619931, OMP 17.0.5 now shows its skipped reason with the work turn still collapsed:

omp-fresh-skip.png

OMP success and follow-up, rendered on a local integration build (403242994a66): this PR's head cb055619931 merged with draft #26658's head 3f52e6fe00a in a disposable QA checkout, never pushed. #26658 was needed because ordinary OMP replies could vanish when reasoning and answer share an id; its branch also carries its own main ancestry. So this is not standalone evidence for this PR, and this PR does not fix ordinary reply delivery. In one OMP 17.0.5 chat opened from New tab, six long chapter prompts were sent, then /compact once, then one follow-up:

omp-integration-compact-outcome.png

omp-integration-followup.png

The saved journal records one /compact submission, one “Context compacted” outcome and the command turn completed as success; the follow-up answered “blue cedar”. Limits: OMP 17.0.5 answered /compact in about 100 ms and the next turn's context estimate was slightly higher (48.3k to 52.2k), the same shape as the native 17.0.5 success recording (instant, token increase, real saved entry); OMP's own session file was not inspected. The model also repeated the marker inside chapter 6, so the follow-up shows the chat stays usable after compaction rather than proving chapter-1 memory survived it.

Final head 9b579925d7b. After QA, main was merged once (754b21c0cfd, three conflicts resolved by keeping main's code and re-adding only this PR's compaction lines). A fresh review of that merge found that main's new sign-in handling did not reach /compact: a signed-out /compact showed "Compaction failed: Authentication required", and main's restart of a signed-out agent never fired, so /compact could keep failing after signing in. 059ccbde009 records the same "not signed in" result an ordinary message records, so the sign-in guidance shows and the restart happens. Then main was merged again (bfa8c848626, after #26658 and #26666 landed): the one conflict kept main's new shared prompt-error rule and re-added only this PR's compaction line, and 052f3656f78 moved this PR's compaction handling out of the timeline translator into acp-compaction-turn.ts because main's per-channel reply identity pushed that file over the line limit (same behaviour, reviewed). A third main merge (f87dd3b6570, after #26508 Grok subagents) kept main's subagent handling and moved this PR's compaction bookkeeping into acp-prompt-turns.ts to stay under the translator's line limit (same behaviour, reviewed); 996ea5018a8 merges current main once more with no conflicts. A fourth main merge (9b579925d7b, after #26587 and #26407) kept main's model-discovery settings, re-added only compaction: true, and made compact return its receipt from the turn runner to keep the session adapter under its line limit (same behaviour, reviewed). The live screenshots above were taken before these commits; the later changes are covered by tests, not live QA.

Testing

Real native recordings cover OMP 17.0.5 and 18.2.7: two short prompts and a fresh chat both return the skipped reason, while sufficient default conversation history produces an actual saved compaction entry. Version 17.0.5 reported a 548-token increase; these results establish completion, not guaranteed token savings. Version 18.2.7 used a checksum-verified isolated official binary without replacing the installed version.

On the prepared final code, 211 targeted tests passed. A fresh reviewer independently passed 177 tests, including the actual 200-item history reader with the command omitted, older-page loading, saved-journal reload, turn ownership, ordinary-warning controls and usage preservation. Queued Node, renderer and CLI typechecks passed. Immediate prepush static checks passed 22/22. Earlier draft CI on cb055619931: 32 checks, 15 passed, 17 skipped, 0 failed. CI on final head 9b579925d7b (runs 37905775276, 37905775284, 37905775301, 37905775712): all 32 checks finished, 17 passed, 15 skipped by workflow rules, 0 failed. Earlier ready-for-review CI on 059ccbde009 (run): 35 checks, 17 passed, 18 skipped, 0 failed after one rerun of a flaky remote-server file-permission test this PR does not touch. Skipped jobs provide no coverage: Git compatibility, Codex index-heal contract, xterm patch sync, shell contracts, orcad browser provider, managed hooks on Node 18, e2e, real IME, mobile web app bundle, real WSL terminal, unit_selection_evidence, test, four headless Linux server jobs, headless persistence and mobile tests on main.

Earlier lifecycle review passed 76 tests and four temporary full-host scenarios: queued-message handover, acknowledged Stop, bounded recovery from hanging cancellation, and process exit followed by another message. Temporary tests were archived outside the PR and removed.

Windows, Linux, SSH, mobile and older peers were checked in source review, not exercised live. No new journal kind, field, protocol operation or migration was introduced; the existing open presentation string carries the hint. Opaque legacy identities are conservatively left unchanged. Two synthetic late-event sequences remain unproven with real providers. Grok and OpenCode compaction screenshots precede the OMP visibility correction; their post-compaction follow-ups were not verified.

Review

Fresh architecture and lifecycle reviews covered the original command implementation. Review fixed a stale Grok capability assertion and the OMP warning visibility failure. A fresh final review and the readiness checklist without counsel accepted the outcome-owned design and exact source hashes. No main merge or PR merge was performed.

Agent skill upstream boundary

  • This change copies no upstream skill-installer material.

Checklist

  • Focused on native compaction for the three supported agents.
  • Problem, user-facing change, mechanism and choice explained.
  • Uploaded before/after skipped-command evidence and integration-build success/follow-up evidence embedded.
  • Source reviewed for correctness, security, resources and compatibility.
  • OMP rendered integration QA completed and ready-event CI classified.
Files Added Deleted Net
Test 8 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​710 $\color{#cf222e}{\Huge{\mathbf{−}}}$​1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​709
Prod 25 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​390 $\color{#cf222e}{\Huge{\mathbf{−}}}$​49 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​341

@brennanb2025

brennanb2025 commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor Author

Problem. In Grok, OpenCode and OMP chats, typing /compact did not run the agent's own compaction through Orca. Review also found a second problem: when OMP had nothing to compact, the chat showed an empty, collapsed "Worked for 0s" turn. OMP's "Nothing to compact" message was saved, but the transcript folded it away, so the command looked like it did nothing.

What changes for users. /compact now works in all three chats, and you can keep chatting afterwards. A successful compaction shows a "Context compacted" divider. When OMP skips (nothing to compact, or already compacted), its actual reason is shown, including after reopening the chat or scrolling back through older history. A skipped command does not reset the context-usage meter.

How. Orca sends /compact as an ordinary prompt inside the command turn the chat already owns, so the existing queue, Stop and process-exit handling settle it. There is no new scheduler, saved flag, journal field, protocol message or migration. The outcome row carries its own meaning, so it stays visible even when the page of history that held the original command isn't loaded. Older saved OMP outcomes are recognised from their existing identity.

Fixed during review. A stale Grok capability test (537a7b45003), and the OMP skipped-outcome visibility and paging failure (cb055619931).

Deferred. OpenCode's slash menu only lists commands the agent advertises, so /compact must be typed there. Adding it to the menu is a follow-up; no separate ticket exists. Separately, and unchanged by this PR (main behaves the same): OMP sessions can appear labelled "Pi" in the session history panel, which may confuse users.

Verified live.

  • Grok and OpenCode each showed "Context compacted" on 537a7b45003. Their follow-up messages were not captured.
  • OMP 17.0.5 skipped compaction on this head (cb055619931): the reason is visible while the work turn stays collapsed. Before and after screenshots are in the PR body.
  • OMP success plus a follow-up, on a local integration build 403242994a66. That build is this PR's cb055619931 merged with draft fix(acp): keep OMP replies that follow its reasoning #26658's 3f52e6fe00a in a disposable QA checkout, never pushed.
    • Why fix(acp): keep OMP replies that follow its reasoning #26658 was included: ordinary OMP replies could vanish when reasoning and answer share an id, which blocked earlier runs before any compaction. fix(acp): keep OMP replies that follow its reasoning #26658 also brings its own main ancestry.
    • So this is integration evidence, not standalone evidence, and this PR does not fix ordinary reply delivery.
    • What happened: in one chat, six long prompts, then /compact once, then "Context compacted" appeared. The follow-up answered "blue cedar". The saved journal shows one /compact, one compaction outcome, and the command turn completed as success. Screenshots are in the PR body.
  • Native OMP recordings (17.0.5 installed; 18.2.7 from a checksum-verified official binary, run in isolation): short or fresh chats skip with the reason, and enough default history produces a real saved compaction entry.

Final head 9b579925d7b. After QA: one main merge (754b21c0cfd, main kept whole, only this PR's compaction lines re-added) and one fix found by the merge review. Main's new sign-in handling did not reach /compact, so a signed-out /compact showed a generic failure and main never restarted the signed-out agent; 059ccbde009 records the same "not signed in" result an ordinary message does. Then main was merged again (bfa8c848626, after #26658 and #26666 landed): the one conflict kept main's new shared prompt-error rule and re-added only this PR's compaction line, and 052f3656f78 moved this PR's compaction handling out of the timeline translator into acp-compaction-turn.ts because main's per-channel reply identity pushed that file over the line limit (same behaviour, reviewed). A third main merge (f87dd3b6570, after #26508 Grok subagents) kept main's subagent handling and moved this PR's compaction bookkeeping into acp-prompt-turns.ts to stay under the translator's line limit (same behaviour, reviewed); 996ea5018a8 merges current main once more with no conflicts. A fourth main merge (9b579925d7b, after #26587 and #26407) kept main's model-discovery settings, re-added only compaction: true, and made compact return its receipt from the turn runner to keep the session adapter under its line limit (same behaviour, reviewed). The live screenshots predate these commits; the later changes are test-covered only.

Limits.

  • OMP 17.0.5 answered /compact in about 100 ms, and the next turn's context estimate was slightly higher (48.3k to 52.2k). The native 17.0.5 recording has the same shape: instant, a reported 548-token increase, and a real saved entry. So compaction completing does not guarantee token savings.
  • OMP's own session file for the live run was not opened.
  • The model also repeated the marker inside chapter 6, so the follow-up proves the chat stays usable after compaction. It does not prove chapter-1 memory survived it.
  • Windows, Linux, SSH, mobile and older-peer behaviour were reviewed in source only. Two synthetic late-event orderings remain unproven against real agents. Older desktop versions keep their previous folding of skipped outcomes.

Tests and CI.

  • 211 targeted tests passed, plus 177 run independently by a fresh reviewer. These cover command-less 200-item history pages, older-page loading, journal reload, turn ownership, ordinary warnings and usage preservation.
  • Node, renderer and CLI typechecks and 22/22 pre-push checks passed.
  • Draft CI on cb055619931: 32 checks, 15 passed, 17 skipped by workflow rules, 0 failures. Skips are not coverage. CI on final head 9b579925d7b (runs 37905775276, 37905775284, 37905775301, 37905775712): all 32 checks finished, 17 passed, 15 skipped by workflow rules, 0 failed. Earlier ready-for-review CI on 059ccbde009: 0 failed after one rerun of a flaky remote-server file-permission test this PR does not touch. Skipped jobs provide no coverage: Git compatibility, Codex index-heal contract, xterm patch sync, shell contracts, orcad browser provider, managed hooks on Node 18, e2e, real IME, mobile web app bundle, real WSL terminal, unit_selection_evidence, test, four headless Linux server jobs, headless persistence and mobile tests on main.

QA incidents, kept for the record.

  • The first M4Air launcher was stopped before any UI exercise because its isolation was incomplete. Brennan classified the six Claude file hashes that changed then as unrelated activity.
  • macOS Electron ignores the HOME variable for its native home, so the launcher was rebuilt with a disposable native home. Real .orca/keybindings.json hashes never changed.
  • The remote QA checkout /Users/m4air/orca/workspaces/orca/qa-26502-compaction was created with --no-parent and later removed.
  • Several laptop runs stopped on harness problems before reaching compaction:
    • the driver mistook labels for identity;
    • a stdout redirect overwrote a receipt;
    • a first-run tip dialog swallowed a send;
    • a driver-written readiness check was wrong.
  • The final runs pre-seeded first-run tips and the telemetry choice (opted out), and used a pre-written readiness helper.
  • In one of those runs, the real ~/.codex/config.toml changed while the live Orca app was running its own Codex server. It was not attributed and not opened. A watcher during the passing run saw no change.
  • Real-agent runs changed OMP runtime databases and session files, and earlier runs also changed Grok runtime and auth-file hashes, including concurrent writes from another lane. Contents and credential state were never inspected. No credentials were copied, printed, refreshed or restored, and no unchanged-credential claim is made.
  • All recorded rig processes were confirmed exited and their ports freed after every run.

No main merge or PR merge was performed.

…action

# Conflicts:
#	src/main/acp/acp-dialects/omp-dialect.ts
#	src/main/acp/acp-launch-specs.ts
#	src/shared/structured-agent-session-status-block.ts
A /compact the agent refused for sign-in now records the same notSignedIn
fact as an ordinary send, so the chat shows sign-in guidance and the next
send starts a new agent that reads the new login.
@brennanb2025
brennanb2025 marked this pull request as ready for review October 9, 2026 04:53
@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

📝 Walkthrough

Walkthrough

The change adds /compact command handling to the ACP adapter for Grok, OpenCode, and OMP. The ACP timeline translator processes compaction updates and records success, skipped, failure, and cancellation outcomes. Shared projection and transcript-slot logic identifies skipped-compaction warnings and keeps them visible when paging omits the command opener. New tests and fixtures cover command outcomes and transcript visibility.


Priority: ➖ Normal

Merge Risk: 🟡 Moderate · up to 9b579

A refused /compact command can leave its chat turn unfinished. Settle that failure before merging.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 44.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 25 functions across 25 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 identifies the primary change: adding native compaction support for Grok, OpenCode, and OMP chats.
Description check Passed The description is comprehensive and follows the template. It explains the user impact, implementation, rationale, linked issues, visual evidence, testing, review status, limitations, and compatibilit…


  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR


  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

…action

# Conflicts:
#	src/main/acp/acp-dialects/omp-dialect.ts
…r main's growth

Main's OMP per-channel message identity grew acp-timeline-translator.ts past
max-lines with this PR's compaction handling. The running compaction's frame
folding moves beside its ending in acp-compaction-turn.ts, and the
compacting-prompt lookup into AcpPromptTurns; behaviour is unchanged.
Conflict in acp-structured-lane.ts: keeps main's child-work lane and
AcpTimelineEvent type; re-adds only this PR's beginCommand/forgetCommand.

Main's Grok subagent rows (#26508) left acp-timeline-translator.ts at its
300-line limit, so this PR's compaction hooks move out of it: the running
compaction now opens, absorbs its frames and ends inside AcpPromptTurns
(which takes the dialect and agent name instead of an injected flag), the
translator's openPrompt takes the command turn instead of a separate
openCompaction, and the substantive-update list moves to acp-session-update.ts.
Behaviour is unchanged.

@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: 8ad6b024-2a44-4a7f-a54b-1189d73b8573
📥 Commits

Reviewing files that changed from the base of the PR and between 052f365 and 996ea50.

📒 Files selected for processing (9)
  • src/main/acp/acp-compaction-turn.ts
  • src/main/acp/acp-dialects/acp-dialect.ts
  • src/main/acp/acp-prompt-turns.ts
  • src/main/acp/acp-session-update.ts
  • src/main/acp/acp-structured-agent-definitions.ts
  • src/main/acp/acp-structured-lane.ts
  • src/main/acp/acp-structured-session-adapter.ts
  • src/main/acp/acp-structured-turns.ts
  • src/main/acp/acp-timeline-translator.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.

Comment on lines +91 to +120
return [
{
type: 'item.close',
item: `compaction:${turn}`,
body: {
kind: 'status',
tone: 'warning',
text,
presentation: AGENT_SESSION_COMPACTION_SKIPPED_PRESENTATION
},
join
},
end('success')
]
}
const words = reply?.detail ?? input.failureDetail ?? compaction.failureDetail
const detail = words === undefined ? undefined : providerDiagnostic(words, 'person')
const verdict = structuredCompactionOutcome({
// An agent that ends `/compact` normally compacted, unless its reply says otherwise.
compacted: stopReason === 'end_turn' && reply === undefined,
interruptRequested: stopReason === 'cancelled',
failed: detail ? { detail } : {}
})
if (verdict.outcome === 'cancellation') {
return [end('cancellation')]
}
// The same fact a signed-out send records, so its guidance shows and the child is replaced.
const failure =
verdict.failure && input.notSignedIn
? agentSessionFailureFact('notSignedIn', detail ? { detail } : {})

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:

rg -n 'structuredCompactionOutcome|failureDetail|absorbCompaction' src/main/acp src/shared/agent-session-compaction.ts
sed -n '1,180p' src/shared/agent-session-compaction.ts
sed -n '85,125p' src/main/acp/acp-compaction-turn.ts

Repository: stablyai/orca

Length of output: 5519


🏁 Script executed:

set -o pipefail
printf '%s\n' '--- outcome helper references ---'
rg -n -F --glob '*.ts' -- 'structuredCompactionOutcome' src test tests 2>/dev/null || true
printf '%s\n' '--- outcome helper source ---'
helper=$(rg -l -F --glob '*.ts' -- 'export function structuredCompactionOutcome' src)
printf '%s\n' "$helper"
sed -n '1,240p' "$helper"
printf '%s\n' '--- compaction turn source ---'
sed -n '1,190p' src/main/acp/acp-compaction-turn.ts
printf '%s\n' '--- ACP translator caller ---'
sed -n '90,145p' src/main/acp/acp-timeline-translator.ts
sed -n '215,320p' src/main/acp/acp-timeline-translator.ts
printf '%s\n' '--- prompt owner ---'
sed -n '1,130p' src/main/acp/acp-prompt-turns.ts
printf '%s\n' '--- focused tests ---'
rg -n -i --glob '*.{ts,tsx}' -- 'compaction|failureDetail|Context compacted|Nothing to compact|structuredCompactionOutcome' test tests src 2>/dev/null | head -240

Repository: stablyai/orca

Length of output: 41958


🏁 Script executed:

printf '%s\n' '--- ACP translator frame path ---'
nl -ba src/main/acp/acp-timeline-translator.ts | sed -n '185,275p'
printf '%s\n' '--- ACP compaction settlement tests ---'
nl -ba src/main/acp/acp-structured-compaction.test.ts | sed -n '110,335p'
printf '%s\n' '--- dialect reply contracts and implementations ---'
nl -ba src/main/acp/acp-dialects/acp-dialect.ts | sed -n '45,105p'
rg -n -F --glob '*.ts' -- 'compactionReply' src/main/acp/acp-dialects

Repository: stablyai/orca

Length of output: 17865


Make extension failures override the default end_turn success.

When an ACP extension supplies failureDetail and the prompt ends with end_turn without a parsed reply, acpCompactionEnd passes both compacted: true and failed: { detail }. structuredCompactionOutcome checks compacted first, so the turn reports successful compaction instead of the failure.

Suggested fix
-    compacted: stopReason === 'end_turn' && reply === undefined,
+    compacted: stopReason === 'end_turn' && reply === undefined && detail === undefined,
📝 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
return [
{
type: 'item.close',
item: `compaction:${turn}`,
body: {
kind: 'status',
tone: 'warning',
text,
presentation: AGENT_SESSION_COMPACTION_SKIPPED_PRESENTATION
},
join
},
end('success')
]
}
const words = reply?.detail ?? input.failureDetail ?? compaction.failureDetail
const detail = words === undefined ? undefined : providerDiagnostic(words, 'person')
const verdict = structuredCompactionOutcome({
// An agent that ends `/compact` normally compacted, unless its reply says otherwise.
compacted: stopReason === 'end_turn' && reply === undefined,
interruptRequested: stopReason === 'cancelled',
failed: detail ? { detail } : {}
})
if (verdict.outcome === 'cancellation') {
return [end('cancellation')]
}
// The same fact a signed-out send records, so its guidance shows and the child is replaced.
const failure =
verdict.failure && input.notSignedIn
? agentSessionFailureFact('notSignedIn', detail ? { detail } : {})
return [
{
type: 'item.close',
item: `compaction:${turn}`,
body: {
kind: 'status',
tone: 'warning',
text,
presentation: AGENT_SESSION_COMPACTION_SKIPPED_PRESENTATION
},
join
},
end('success')
]
}
const words = reply?.detail ?? input.failureDetail ?? compaction.failureDetail
const detail = words === undefined ? undefined : providerDiagnostic(words, 'person')
const verdict = structuredCompactionOutcome({
// An agent that ends `/compact` normally compacted, unless its reply says otherwise.
compacted: stopReason === 'end_turn' && reply === undefined && detail === undefined,
interruptRequested: stopReason === 'cancelled',
failed: detail ? { detail } : {}
})
if (verdict.outcome === 'cancellation') {
return [end('cancellation')]
}
// The same fact a signed-out send records, so its guidance shows and the child is replaced.
const failure =
verdict.failure && input.notSignedIn
? agentSessionFailureFact('notSignedIn', detail ? { detail } : {})

Conflict in acp-launch-specs.ts: keeps main's modelDiscovery on the Grok,
OpenCode and OMP specs and re-adds only this PR's `compaction: true`.

Main's model catalog work (#26407) left acp-structured-session-adapter.ts at
its 300-line limit, so the adapter's `compact` now delegates in one
expression and AcpStructuredTurns.compact returns the admission receipt
itself; the adapter's two imports from acp-structured-connection are joined
and releaseAcquisition drops a return annotation close() already gives.
Behaviour is unchanged.

@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: e462d281-7e94-4531-8546-167e7e8fed4d
📥 Commits

Reviewing files that changed from the base of the PR and between 996ea50 and 9b57992.

📒 Files selected for processing (4)
  • src/main/acp/acp-dialects/acp-dialect.ts
  • src/main/acp/acp-launch-specs.ts
  • src/main/acp/acp-structured-session-adapter.ts
  • src/main/acp/acp-structured-turns.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.

lane.forgetCommand(command.turnId)
throw error
}
return { state: 'accepted', providerIdentity: null }

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Settle a refused /compact command turn.

If the agent refuses the prompt before starting it, run calls reject. The command’s message ID is not in unsettled, so reject does nothing. The host has already received accepted admission, and this path emits no command result or turn end. Handle that refusal as a command failure so the turn can finish.

@brennanb2025
brennanb2025 merged commit f16212a into main Oct 9, 2026
32 checks passed
brennanb2025 added a commit that referenced this pull request Oct 9, 2026
…utput rig

Main's #26502 made Grok declare compaction; the rest-test rig adapter has no compact, so the
registry refused it before any recovery case ran.
brennanb2025 added a commit that referenced this pull request Oct 10, 2026
* fix: preserve streamed turn duration after host restart

* test: run restart duration regression in Node

* fix: preserve provider receipt time in journal updates

* test: verify provider receipt in Claude ownership assertion

* fix(native-chat): time interrupted turns from real provider output, not flushes

Shared-timeline checkpoints stamped the coalescer flush or retry time as the
provider's latest proof of life, so a turn whose provider died before a delayed
flush settled with an inflated duration after a host restart. Carry each delta's
receipt time through the coalescer and timeline lane.

* fix(native-chat): derive interrupted-turn duration from saved output

A reply cut off by a server restart could show "Worked for 0s" because recovery
ended the turn at the last heartbeat or the reply's first timestamp. End it at
the latest time this owner's output was saved to chat history instead, derived
in the existing journal fold from the row save time. Drops the per-frame
receipt plumbing through every provider and buffer.

* fix(chat): exclude host recovery revisions from provider activity

Mark journal-open and connection-cleanup revisions with the existing recovery
annotation so an interrupted turn does not include host downtime. Preserve
buffered provider output and advance restart fixtures to the restart clock.

* fix(native-chat): interrupt turns a replaced server left running

When the managed server died mid-reply and a new one started on the same
host, the cut-off turn settled `unverifiable` with no end whenever the old
provider's identity could not be proven gone, and nothing ever revised it:
the chat showed no turn status and the sidebar said "unconfirmed".

The new server opens the chat records holding the host's instance lock, so
every runtime that wrote them before is gone, and every provider it held
lost its pipes with it. Record that at load (the fences those runtimes
granted, the loaded owner's last renewal, how its runtime ended) and let
the stale settle end those runtimes' running or unverifiable turns as
interrupted at the owner's last saved output or renewal, with the existing
Orca-stop row. Lease adjudication and the live / unverifiable / exited
owner verdicts are unchanged.

* fix(native-chat): require the record store for any journal open that settles

Only an acquisition's open may skip it; a reader open without the store would leave a replaced server's turns unverifiable.

* fix(native-chat): say a remote host's Orca restarted, not that it was closed

A reply cut off when a remote host's Orca server stopped read "Orca on <host> was closed while
this response was in progress." Nobody closed anything there: the server stopped and Orca started
it again before the row could show. For a chat on a remote environment, cause quit now reads
"Orca on <host> restarted while this response was in progress." The desktop's own quit keeps
"was closed". Renderer-only; no wire change.

* fix(native-chat): say a remote host's Orca stopped, true for a shared desktop too

* test(native-chat): register Grok without compaction in the recovery-output rig

Main's #26502 made Grok declare compaction; the rest-test rig adapter has no compact, so the
registry refused it before any recovery case ran.

* chore(native-chat): say a replaced runtime also revises an unverifiable turn
@brennanb2025
brennanb2025 deleted the brennanb2025/acp-compaction branch October 13, 2026 23:29
brennanb2025 added a commit that referenced this pull request Oct 15, 2026
* fix: preserve streamed turn duration after host restart

* test: run restart duration regression in Node

* fix: preserve provider receipt time in journal updates

* test: verify provider receipt in Claude ownership assertion

* fix(native-chat): time interrupted turns from real provider output, not flushes

Shared-timeline checkpoints stamped the coalescer flush or retry time as the
provider's latest proof of life, so a turn whose provider died before a delayed
flush settled with an inflated duration after a host restart. Carry each delta's
receipt time through the coalescer and timeline lane.

* fix(native-chat): derive interrupted-turn duration from saved output

A reply cut off by a server restart could show "Worked for 0s" because recovery
ended the turn at the last heartbeat or the reply's first timestamp. End it at
the latest time this owner's output was saved to chat history instead, derived
in the existing journal fold from the row save time. Drops the per-frame
receipt plumbing through every provider and buffer.

* fix(chat): exclude host recovery revisions from provider activity

Mark journal-open and connection-cleanup revisions with the existing recovery
annotation so an interrupted turn does not include host downtime. Preserve
buffered provider output and advance restart fixtures to the restart clock.

* fix(native-chat): interrupt turns a replaced server left running

When the managed server died mid-reply and a new one started on the same
host, the cut-off turn settled `unverifiable` with no end whenever the old
provider's identity could not be proven gone, and nothing ever revised it:
the chat showed no turn status and the sidebar said "unconfirmed".

The new server opens the chat records holding the host's instance lock, so
every runtime that wrote them before is gone, and every provider it held
lost its pipes with it. Record that at load (the fences those runtimes
granted, the loaded owner's last renewal, how its runtime ended) and let
the stale settle end those runtimes' running or unverifiable turns as
interrupted at the owner's last saved output or renewal, with the existing
Orca-stop row. Lease adjudication and the live / unverifiable / exited
owner verdicts are unchanged.

* fix(native-chat): require the record store for any journal open that settles

Only an acquisition's open may skip it; a reader open without the store would leave a replaced server's turns unverifiable.

* fix(native-chat): say a remote host's Orca restarted, not that it was closed

A reply cut off when a remote host's Orca server stopped read "Orca on <host> was closed while
this response was in progress." Nobody closed anything there: the server stopped and Orca started
it again before the row could show. For a chat on a remote environment, cause quit now reads
"Orca on <host> restarted while this response was in progress." The desktop's own quit keeps
"was closed". Renderer-only; no wire change.

* fix(native-chat): say a remote host's Orca stopped, true for a shared desktop too

* test(native-chat): register Grok without compaction in the recovery-output rig

Main's #26502 made Grok declare compaction; the rest-test rig adapter has no compact, so the
registry refused it before any recovery case ran.

* chore(native-chat): say a replaced runtime also revises an unverifiable turn

(cherry picked from commit b44a579)
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