Skip to content

Native chat: new and resumed ZCode sessions as live structured chats - #27230

Open
star-nebula wants to merge 22 commits into
stablyai:mainfrom
star-nebula:feat/zcode-structured-chat
Open

star-nebula wants to merge 22 commits into
stablyai:mainfrom
star-nebula:feat/zcode-structured-chat

Conversation

@star-nebula

@star-nebula star-nebula commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

ELI5

Before, opening a past ZCode conversation in Orca showed a frozen, read-only transcript. Starting a new ZCode session looked like a terminal, not a chat. This PR makes ZCode conversations work like the other agents' chats: a new session opens as a real chat you can type into, and an old session can be reopened as a chat and continued from where it left off.

What Changed

Before: ZCode history chats were read-only transcripts (from #27131), and a new ZCode session fell back to the terminal-style view. The native chat's structured-session system knew about pi, Codex, and Claude — but not ZCode. "Resume in New Chat" on an old ZCode row always opened the read-only transcript, because the row had never been adopted as a structured session.

After: When the Experimental Chat UI setting is on, a ZCode session opens as a live chat. You type, the assistant answers with streamed text and tool calls, you can stop a turn, and you can approve or deny tool permissions in the chat. Opening a past ZCode conversation resumes it in the same live chat, with the full history shown. "Resume in New Chat" on an old ZCode row now opens a live chat too — except when the conversation's database lives inside WSL, which a local app-server child cannot open; those keep the read-only transcript.

Mechanism: a new src/main/zcode/ adapter family drives one zcode app-server --stdio child per session over ZCode's NDJSON protocol. Acquiring a session spawns the child; opening it then creates or resumes the conversation, subscribes to its event stream, seeds the journal from the snapshot the provider returns, and mints the provider handle. That acquire/open split is upstream's current provider contract, so the adapter publishes a spawned child with no session id and refuses dispatch until the open lands. session/send dispatches a message, session/stop cancels a turn, and the child's reverse-RPC permission requests surface as chat approvals. ZCode registers in the agent-agnostic structured-session registry and joins the provider-handle providers, so the existing provisional-tab, resume, and routing machinery adopts it with no renderer changes. The read-only history layer now distinguishes an adopted structured tab (by its structured-agent-session- tab-id prefix) from a vault row's read-only tab, so the two layers never paint the same tab. For routing, the resume-in-chat eligibility gate now emits a route (structured or history) alongside its verdict: an unadopted local ZCode row routes to structured adoption, a WSL-stored or remote row routes to the read-only tab, and an already-adopted row reopens its own chat. The route travels through the surface-switch targets and the tab right-click menu into the launch action, so the row menu and the tab menu always offer the same thing. The main Resume action is unchanged and keeps the claude/codex semantics: an unadopted row opens the CLI terminal, an adopted row opens its chat.

Why

ZCode's app-server protocol gives a whole-conversation snapshot on create and resume, so the journal can be seeded from the provider itself — no transcript-file import path was needed, unlike Codex/Claude adoption. Building a regular adapter on the existing registry was much smaller than any parallel ZCode-specific chat path, and it inherits the journal-first durability, provisional tabs, and recovery behavior the other agents already have. For the resume routing, reusing the eligibility gate as the single decision point (rather than adding a second zcode-specific check in the launch action) means the row menu and the tab menu cannot drift apart, and the WSL-storage refusal lives in one place next to the other storage refusals.

Linked Issue

Follow-up to PR #27131 (read-only ZCode history chats); no separate issue.

Visual Proof

N/A for automated proof here; the behavior change is visible as: with Experimental Chat UI on, a new ZCode session renders as a chat with streamed replies, a history row opens as a resumable live chat instead of a frozen transcript, and "Resume in New Chat" on an old row opens that live chat rather than a read-only one. (Real-machine verification steps under Testing.)

Testing

  • Synced onto upstream 6cd2550e61 (2026-10-22) and adapted to the split provider contract (acquire spawns the child; open creates or resumes the conversation and answers the handle). Two merge overlaps resolved by taking upstream's abstraction and keeping the ZCode additions.
  • New tests: connection framing (zcode-app-server-connection.test.ts, 6 tests over a real node -e NDJSON child), adapter behavior (zcode-structured-session-adapter.test.ts, 17 tests: spawn-then-open, create/resume, refusal, timeout, cancel, close, exit, sink binding, permission routing), plus existing suites updated.
  • Resume routing (ai-vault-session-resume-in-chat.test.ts, 24 tests): an unadopted local ZCode row routes to structured, a WSL-stored one to the read-only tab (with and without a structured route available), an adopted one reports already-structured, and a WSL-stored non-ZCode row keeps the remote refusal. Parity tests pin that the tab right-click menu passes the same route as the row menu.
  • True-machine run against a real ZCode CLI build: spawn → create (answering the provider's session/requestRuntimePreferences reverse request) → subscribe → send → 14 streamed events → resume returning the full conversation snapshot.
  • Typecheck (tsc node + web + cli) clean; changed-files quality gate passes with 0 findings.
  • Post-sync re-test on Windows: src/main/zcode 150 passed / 7 skipped. The wider local set (agent-session-wire, acp, claude, runtime/structured-agent-*, zcode) still reports host-specific failures on this machine — POSIX PATH expectations, C:\ vs C:/ temp paths, and Antigravity sign-in capture — none in the files this PR touches. The full unit suite is not a meaningful local gate on Windows, so it is left to CI's Linux runners rather than reported as green here.
  • Platforms: developed and verified on Windows. The adapter only supports local execution (supportsLocation = supervised local child), same as Codex, so SSH/WSL hosts keep their existing behavior; remote rows never enter the structured route (covered by the remote refusal tests).

AI Disclosure

Developed with assistance from ZCode (GLM); all changes reviewed and tested by the author.

Review

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Security: the child runs with the workspace as cwd; permission requests from the child are gated through the existing chat approval flow, and the answers are limited to the decision words the protocol defines. Cross-platform: process spawning goes through the shared managed-provider-process layer (windowsHide, no shell); no paths or shortcuts are hardcoded; the ZCode command resolves through the shared CLI resolver with the ZCODE_HOME account home honored. WSL-stored conversations are detected by their UNC transcript path and routed to the read-only tab because the app-server child runs on the host OS. Remote SSH: out of scope by design (local child only), and mixed-version peers are unaffected because nothing changes on the wire between client and host.

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)

…e SQLite reader

ZCode's CLI persists an OpenCode-v1-shaped history in its own SQLite store
(~/.zcode/cli/db/db.sqlite). The Agent Session History scan already lists
those sessions, but opening one in the native chat found nothing: the chat
transcript path had no ZCode awareness.

Resolve a zcode transcript against the ZCode database (native home, then WSL
homes) before any OpenCode install, tag every read (signal, page, subscribe)
with the agent, and apply the same hidden-transcript filter the scan uses so
ZCode's hidden bookkeeping rows stay out of chat renders.
ZCode has no structured-session host adapter, so an Agent Session History
row could not reach a chat at all: the resume paths only knew how to adopt
a conversation into a host-registered structured session.

Rows whose agent reads its own store (zcode today) now open as a read-only
chat tab. The tab reuses the agent-session content type with the session id
as its entity, and AiVaultSessionHistoryChatLayer paints it with the same
live-session hook and message list the chat overlay uses — polling the
ZCode SQLite store through nativeChat:subscribe, no PTY involved. There is
no composer: nothing sends into a conversation the CLI may still be holding.

The eligibility gates answer from the history-chat branch alone — no host
adoption, capability, or workspace matching — while keeping the remote and
empty-content refusals, and reopening focuses the existing tab instead of
duplicating it.
…d pass agent to full-read discovery

Address two CodeRabbit majors on stablyai#27131:
- zcode discovery awaited getAiVaultWslHomeDirs() before probing the
  usable native database, so slow WSL discovery could eat the whole
  5s deadline and fail an available native transcript; probe native
  first as the OpenCode branch does.
- readOpenCodeNativeChatTranscriptFull passed the agent selector to
  page reads but not resolveDbPath, so zcode full reads discovered
  against OpenCode stores: zcode-only sessions reported
  'Transcript unavailable' and a colliding session id could select
  the wrong conversation.
…ver protocol

A ZCode session can now run as a live structured chat, the same way Codex
and Claude do. A new `src/main/zcode/` adapter family spawns one
`zcode app-server --stdio` child per session over the ZCode NDJSON
protocol: create a conversation (or resume the one the session record
proved), subscribe to its event stream, translate `session/event`
notifications into journal rows, and answer the child's reverse-RPC
permission prompts. Sending a message is `session/send`; cancelling a
turn is `session/stop`, the one call that bypasses the request queue.

Registration adds ZCode to the agent-agnostic structured-session
registry and to the provider-handle providers, so the existing
provisional-tab, resume, and routing machinery adopts it without
renderer changes.

Verified against a real ZCode CLI build: create -> subscribe -> send ->
streamed events -> resume with a full conversation snapshot.
… chats

With the ZCode adapter in place, the entry points that decide what a
ZCode session opens as now route to the live structured chat. Session
creation treats a ZCode conversation as adoptable (its transcript lives
in the provider's SQLite store, and the resume handshake re-seeds the
journal from the provider's snapshot, so no file import is needed), and
the ownership, history-adoption, and worker-authority lookups accept
ZCode through the shared provider check instead of hardcoded agent
names.

The renderer's read-only history layer learns the split: a tab the host
adopted as a structured chat carries a `structured-agent-session-` id
and belongs to the structured overlay, while the vault row's own tab id
stays the read-only path for hosts that have not adopted the session.
@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

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: dfc0a174-7a27-448b-9e4f-4e7c10f405d8
📥 Commits

Reviewing files that changed from the base of the PR and between fcc6cd7 and 27a6f1a.

📒 Files selected for processing (16)
  • src/main/zcode/zcode-structured-session-adapter.test.ts
  • src/renderer/src/components/right-sidebar/AiVaultSessionVirtualList.tsx
  • src/renderer/src/components/right-sidebar/AiVaultVirtualRow.tsx
  • src/renderer/src/components/right-sidebar/ai-vault-session-history-chat-open.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-history-chat.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-launch-actions.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-launch-target-notify.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-resume-in-chat-workspace.test.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-resume-in-chat.test.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-resume-in-chat.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-surface-switch.test.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-surface-switch.ts
  • src/renderer/src/components/tab-bar/TabSessionSurfaceSwitchMenuItems.test.tsx
  • src/renderer/src/components/tab-bar/TabSessionSurfaceSwitchMenuItems.tsx
  • src/renderer/src/components/tab-bar/tab-session-history-switch.parity.test.ts
  • src/renderer/src/components/tab-bar/tab-session-history-switch.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/renderer/src/components/right-sidebar/ai-vault-session-history-chat.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-history-chat-open.ts

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

📝 Walkthrough

The pull request adds ZCode as a structured-session provider, including app-server communication, session acquisition, journal translation, and runtime registration. It routes ZCode transcript reads through OpenCode-format SQLite queries and filters hidden messages. It also adds read-only AI Vault history-chat tabs for eligible ZCode sessions.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to 27a6f

History routing is mergeable, but a completion write refused under backpressure may leave a chat row showing as running. Track that remaining issue.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 42.34% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 111 functions across 64 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed 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 identifies the main change: adding live structured native chat support for new and resumed ZCode sessions.
Description check Passed The description is detailed and covers the user impact, implementation, rationale, testing, platform scope, security, and checklist. The Linked Issue section references follow-up PR #27131 instead of …
Full details: Description check

Explanation

The description is detailed and covers the user impact, implementation, rationale, testing, platform scope, security, and checklist. The Linked Issue section references follow-up PR #27131 instead of an issue, but the rest of the required content is substantially complete.

  • 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.

@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: 6

🧹 Nitpick comments (1)
src/renderer/src/components/right-sidebar/ai-vault-session-history-chat-open.ts (1)

14-19: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Update the stale comment about the ZCode structured-session adapter.

The comment says "ZCode has no structured-session host adapter". This PR adds that adapter. canOpenAiVaultSessionHistoryChat now uses the read-only tab only for rows with no structuredSession. The comment misstates the routing rule. Reword the comment to say that the read-only tab handles rows that no host has adopted.


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 9f4ee907-0ab9-46b2-8550-ff36c9028d11
📥 Commits

Reviewing files that changed from the base of the PR and between 9d43038 and 27a417b.

📒 Files selected for processing (50)
  • src/main/ai-vault/session-scanner-opencode-sqlite-dispatch.ts
  • src/main/ai-vault/session-scanner-opencode-sqlite-worker-protocol.ts
  • src/main/ai-vault/session-scanner-opencode-sqlite-worker-spawn.ts
  • src/main/ai-vault/structured-session-ownership.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-adopted-import.ts
  • src/main/native-chat/structured-agent-session-history-adoption.ts
  • src/main/native-chat/transcript-opencode-database.test.ts
  • src/main/native-chat/transcript-opencode-database.ts
  • src/main/native-chat/transcript-opencode-sqlite-query.ts
  • src/main/native-chat/transcript-opencode-subscribe.ts
  • src/main/native-chat/transcript-opencode.test.ts
  • src/main/native-chat/transcript-opencode.ts
  • src/main/native-chat/transcript-read-cache.ts
  • src/main/native-chat/transcript-reader.ts
  • src/main/native-chat/transcript-tail-reader.ts
  • src/main/native-chat/transcript-watch.ts
  • src/main/runtime/structured-agent-runtime-registrations-acp.test.ts
  • src/main/runtime/structured-agent-runtime-registrations.ts
  • src/main/runtime/structured-agent-session-create-adoption.ts
  • src/main/runtime/structured-agent-session-runtime.ts
  • src/main/runtime/structured-worker-authority.ts
  • src/main/zcode/zcode-app-server-connection-types.ts
  • src/main/zcode/zcode-app-server-connection.test.ts
  • src/main/zcode/zcode-app-server-connection.ts
  • src/main/zcode/zcode-app-server-record-dispatch.ts
  • src/main/zcode/zcode-app-server-request-error.ts
  • src/main/zcode/zcode-protocol-events.ts
  • src/main/zcode/zcode-runtime-registration.ts
  • src/main/zcode/zcode-structured-acquire.ts
  • src/main/zcode/zcode-structured-adapter-fixture.ts
  • src/main/zcode/zcode-structured-agent-definition.ts
  • src/main/zcode/zcode-structured-child-environment.ts
  • src/main/zcode/zcode-structured-journal-payload-readers.ts
  • src/main/zcode/zcode-structured-journal-translation.ts
  • src/main/zcode/zcode-structured-launch-resolution.ts
  • src/main/zcode/zcode-structured-session-adapter.test.ts
  • src/main/zcode/zcode-structured-session-adapter.ts
  • src/main/zcode/zcode-structured-session-state.ts
  • src/renderer/src/components/WorkspacePaneOverlayLayers.tsx
  • src/renderer/src/components/native-chat/AiVaultSessionHistoryChatLayer.tsx
  • src/renderer/src/components/native-chat/AiVaultSessionHistoryChatView.tsx
  • src/renderer/src/components/right-sidebar/ai-vault-session-history-chat-open.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-history-chat.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-launch-actions.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-resume-in-chat-workspace.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-resume-in-chat.ts
  • src/shared/agent-session-provider-handle-encoding.ts
  • src/shared/agent-session-provider-handle.ts
  • src/shared/native-chat-agent-support.test.ts
  • src/shared/native-chat-agent-support.ts

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

Comment thread src/main/zcode/zcode-structured-acquire.ts
Comment thread src/main/zcode/zcode-structured-acquire.ts
Comment thread src/main/zcode/zcode-structured-acquire.ts
Comment thread src/main/zcode/zcode-structured-journal-translation.ts Outdated
Comment thread src/main/zcode/zcode-structured-session-adapter.ts
Comment thread src/renderer/src/components/right-sidebar/ai-vault-session-launch-actions.ts Outdated
…, and bind backpressure

Address the CodeRabbit majors and minors on stablyai#27230:

- Prompts the child asks after acquire no longer strand: the live
  prompt map is shared with the session, and each permission request
  also lands a durable pending-approval journal row so a second
  client can answer it. Previously answerPrompt always threw
  AgentSessionPromptUnavailableError and the child waited forever.
- Snapshot and upsert messages file their id inside info on some
  builds; read it there before dropping the message, so a resumed
  chat keeps its history.
- onSpawned now reports the identity the acquisition carries: the
  start time is read once and shared, so the owner check cannot see
  a null start time against a proved one and throw
  agent_session_ownership_unknown.
- The sink's reading control binds to this child's stream, as the
  Codex acquire does, so hard-watermark backpressure pauses the
  reader instead of dropping rows; every close path unbinds.
- session/send carries the session's model option.
- The read-only history chat opens in the row's target worktree
  rather than whichever workspace is active.

@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

🧹 Nitpick comments (1)
src/main/zcode/zcode-structured-session-adapter.test.ts (1)

165-165: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the restored snapshot message.

The test calls adapter.acquire(acquireInput()) without an event sink, so the translator is not created. It then checks only the session/resume request. Bind a sink and assert that the journal receives message:m1 with text earlier.

Suggested test assertion
     const adapter = adapterFor(zcode, [], {
       resolveLaunch: async () => ({
         command: 'zcode',
         args: ['app-server', '--stdio'],
@@
         workspacePath: '/work/repo'
       })
     })
+    const rows: { identity: unknown; body: unknown }[] = []
+    const sink = {
+      appendItem: (_identity: unknown, body: unknown) => {
+        rows.push({ identity: _identity, body })
+      },
+      publish: () => {}
+    }

-    await adapter.acquire(acquireInput())
+    await adapter.acquire(
+      acquireInput({ events: sink as unknown as StructuredAgentSessionEventSink })
+    )

-    // The resumed conversation seeded its history: the session answer over the
-    // journal names the snapshot rows, whatever shape carried the id.
-    expect(zcode.connections[0]!.requests[0]!.method).toBe('session/resume')
+    expect(rows).toContainEqual({
+      identity: expect.objectContaining({ recordId: 'message:m1' }),
+      body: expect.objectContaining({
+        blocks: [{ type: 'text', text: 'earlier' }]
+      })
+    })

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b4a1a00a-2346-463d-9c7b-2064d08cc813
📥 Commits

Reviewing files that changed from the base of the PR and between 27a417b and e0b9be1.

📒 Files selected for processing (8)
  • src/main/zcode/zcode-structured-acquire.ts
  • src/main/zcode/zcode-structured-adapter-fixture.ts
  • src/main/zcode/zcode-structured-journal-translation.ts
  • src/main/zcode/zcode-structured-session-adapter.test.ts
  • src/main/zcode/zcode-structured-session-adapter.ts
  • src/main/zcode/zcode-structured-session-state.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-history-chat-open.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-launch-actions.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/renderer/src/components/right-sidebar/ai-vault-session-launch-actions.ts
  • src/main/zcode/zcode-structured-acquire.ts
  • src/main/zcode/zcode-structured-session-state.ts
  • src/main/zcode/zcode-structured-session-adapter.ts
  • src/main/zcode/zcode-structured-journal-translation.ts

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

…ry chat

Tab state is per-worktree: createUnifiedTab and activateTab never move
activeWorktreeId, so a row whose conversation belongs to another
worktree built the tab out of view. Activate the row's workspace first
through the resume workspace helper; a failed activation keeps the tab
unopened instead of landing it in the wrong workspace.
…g deltas

This ZCode build never upserts whole assistant messages: a reply reaches
Orca only as model.streaming deltas (text_delta / reasoning_delta, keyed
by assistantMessageId, with the delta fields nested one payload level
down), so the journal translator's message.upserted path never fired and
finished turns showed the user's message and nothing else.

Accumulate the deltas in a stream translator (satellite module, shared
delta coalescer), rewrite the running assistant/reasoning row as the
stream grows, close every open stream completed at turn end, and let a
whole-message upsert or snapshot restore override the stale stream copy.
Identity and append plumbing move to zcode-structured-journal-sink to
keep the translator under the file-size cap.

@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: 7f2eeaf0-dc53-48ff-a44f-f71828b6dff3
📥 Commits

Reviewing files that changed from the base of the PR and between ca54e64 and fcc6cd7.

📒 Files selected for processing (5)
  • src/main/zcode/zcode-protocol-events.ts
  • src/main/zcode/zcode-structured-journal-sink.ts
  • src/main/zcode/zcode-structured-journal-translation.test.ts
  • src/main/zcode/zcode-structured-journal-translation.ts
  • src/main/zcode/zcode-structured-stream-translation.ts

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

Comment thread src/main/zcode/zcode-structured-stream-translation.ts
…Chat

The Resume in New Chat action short-circuited every unadopted ZCode row
into the read-only history tab, so old conversations never reached the
structured adoption path the host already implements. The eligibility
gate now emits a route: an unadopted local row whose database the
app-server child can open adopts into a live chat, while a row stored
inside WSL keeps the read-only tab. The main Resume action keeps the
claude/codex semantics unchanged. The launch-target notify helpers move
to a satellite module to hold the file under the line cap.
@star-nebula

Copy link
Copy Markdown
Contributor Author

Pushed a follow-up commit (27a6f1a) that closes the main gap left by the base PR: old, never-adopted ZCode rows used to open as the read-only transcript from "Resume in New Chat", never reaching the structured adoption path this PR implements.

What changed:

  • resolveAiVaultSessionResumeInChatEligibility now emits a route (structured | history) with its verdict. An unadopted local ZCode row (SQLite store the app-server child can open) routes to structured adoption, exactly like Claude/Codex rows. A row whose database lives inside WSL (\wsl.localhost\... transcript path — the child runs on the host OS, so session/resume would find nothing) and remote rows keep the read-only history tab. An already-adopted row still reports already-structured.
  • The route is carried through the surface-switch targets and the tab right-click menu (TabSessionSurfaceSwitchMenuItems) into handleResumeInNewChat, so the row menu and tab menu stay in lockstep (pinned by the parity test).
  • The main Resume button is deliberately unchanged: unadopted row → CLI terminal, adopted row → its chat, matching claude/codex semantics.
  • Refusal timing stays deterministic: the WSL/remote decisions are made in the gate, before any launch, so no path ends in a failed-adoption provisional tab.

Housekeeping: two launch-target notify helpers moved into a satellite module (ai-vault-session-launch-target-notify.ts) to hold the file under the max-lines cap; four as unknown as test casts in the adapter test got the repo-standard SAFETY suppressions so the changed-lines quality gate passes (it does now, 0 findings across all six checks, verified locally with ORCA_CODE_QUALITY_BASE=upstream/main).

Verification: adapter suite 16 tests, routing suite 24 tests, parity + menu suites green; tsc node/web/cli clean; oxlint + oxfmt clean.

A completed rewrite the sink refuses used to be lost: the coalesced
stream was no longer dirty, so the coalescer timer never retried it and
the journal kept a running row for a finished turn. Pending completions
are now retained and retried on the coalescing window until the sink
admits them.
Adopts upstream's structured-agent abstractions rather than replaying our
older shapes: ZCode's launch now composes through composeStructuredLaunch
with a ZCode launch part, the sealed child env supplies identity and spawn
token, and transcript adoption plus Session History ownership are
registration data (ZCODE_CLI_CONVERSATIONS). ZCode declares its model
catalog unavailable because the app-server lists models only inside a
session.

Conflicts resolved toward upstream in the runtime registrations, session
create adoption, structured-session ownership, and the OpenCode transcript
reader; the ZCode journal admission type moved to the sink to break an
import cycle.
Upstream's native-chat design-token lint rejects arbitrary font sizes on
changed lines. The history chat header used a fixed 11px, which also meant
it ignored the user's chat font setting; text-2xs is the same size at root
and scales with it.
…ly sends

Upstream added the folder-workspace parity case before this branch began
attaching the resume route to the request, so its expectation stayed short
one field the sibling-worktree case already pins.
… row-action extraction

Conflicts resolved toward upstream's newer abstractions rather than our own:

- resume-in-chat now asks `adoptsCliConversations(agent)`, the shared list upstream
  added for agents that can take over a CLI conversation. 'zcode' joins that list:
  the ZCode adapter registers a transcript import, and upstream's registration test
  asserts the two sets are equal.
- the ZCode two-lane decision (structured adoption vs. the read-only history tab)
  survives as the `route` the eligibility gate returns, carried through the row
  action and the tab menu.
- `AiVaultVirtualRow` takes upstream's `resolveAiVaultSessionRowChatActions`, with
  `resumeInNewChatRoute` added to its result.

Two oversized files split instead of given a max-lines disable, per AGENTS.md:
`orcad-entry.ts` (upstream grew it to 301 counted lines over budget, so a merge
commit touching it cannot pass the pre-commit lint) sheds its Node host adapters
into `orcad-host-adapters.ts`, and the structured-session runtime sheds its
attention delivery wiring.
…code adoption

Adopt upstream's orcad-host-adapters extraction (drop our duplicate). Keep ZCode
in the CLI-conversation adoption map under upstream's new row-agent shape. Port
upstream's agent-aware preparation-failure notification into the extracted
ai-vault-session-launch-target-notify module the merged call sites use.
…versation import

Upstream reworked StructuredAgentTranscriptImport to pass AgentSessionAccountHome
instead of raw path strings. ZCode's single-directory import now goes through the
same directoryTranscriptImport adapter Claude/Codex/Grok/OMP use. Behavior unchanged:
one candidate (the chat's own home), transcript resolved only if its SQLite store exists.
…ting

Upstream already routes zcode through the OpenCode reader branch
(opencode || opencode2 || zcode), so the branch this PR added below it could
never run. Keep its rationale as a comment on the branch that does. Behavior
unchanged: zcode still resolves to the OpenCode-v1 SQLite transcript reader.
…ed-chat

# Conflicts:
#	src/renderer/src/components/right-sidebar/ai-vault-session-launch-actions.ts
@star-nebula

Copy link
Copy Markdown
Contributor Author

Merged current upstream/main (613c598) into the branch; the PR is out of conflict and mergeable: MERGEABLE.

Resolution notes for reviewers:

  • Registry: upstream moved the structured-agent registry onto STRUCTURED_AGENT_RUNTIME_CATALOG. ZCode is now a kind: 'zcode' catalog entry: ZCODE_RUNTIME_METADATA (definition, modelCatalog = unavailable — the app-server lists models only inside a session, ...ZCODE_CLI_CONVERSATIONS, supportsLocation, resolveAccountHome) plus the createZcodeAdapter factory in structured-agent-runtime-registrations.ts.
  • Provider transports: the scripted-child overrides now live in StructuredAgentSessionTransportOverrides; ZCode's openZcodeConnection / resolveZcodeCommand moved there.
  • Transcript import: ZCode's directoryTranscriptImport adopts upstream's current shape (conversationStore + findTranscript({ store })).
  • Already upstream: the SQLite transcript reader and its native-before-WSL probing are in upstream/main (via fix(native-chat): read ZCode conversations in their recorded order #27142 and follow-ups), so the branch's copies of those files were resolved to upstream's version — they are no longer part of this PR's diff.
  • Tab menu: upstream rewrote the menu into CliChatResumeMenuItems / NativeChatCliForkMenuItems; that path is taken as upstream's, and the branch's older chatRoute threading was dropped rather than re-added.

Local verification on the merge commit: all four typecheck projects clean (16.7k + 14.9k + 2.9k + 31 files), the 10 test files this branch touches pass (115 tests), oxlint clean on changed files.

The upstream PR workflow has not run for this commit yet (fork PRs wait for a maintainer to approve the run); clean passed.

…ed-chat

# Conflicts:
#	src/main/runtime/structured-agent-runtime-registrations-acp.test.ts
#	src/renderer/src/components/right-sidebar/ai-vault-session-surface-switch.ts
…hild

Upstream split the provider contract into acquire (spawn only) and open
(create/resume plus the handle), so the zcode adapter now publishes the
child with no provider session id and mints the link at open time.

This branch has not been deployed

No deployments
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