Skip to content

fix(native-chat): a local chat this computer can't run opens the agent in a terminal - #25947

Merged
brennanb2025 merged 8 commits into
mainfrom
brennanb2025/acp-local-declined-terminal
Oct 7, 2026
Merged

brennanb2025 merged 8 commits into
mainfrom
brennanb2025/acp-local-declined-terminal

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 14 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​757 $\color{#cf222e}{\Huge{\mathbf{−}}}$​237 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​520
Prod 13 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​231 $\color{#cf222e}{\Huge{\mathbf{−}}}$​202 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​29

ELI5

With Settings > Chat UI > "Use updated structured native chat" on, opening an agent in a new tab on this computer showed the chat tab immediately, before Orca had checked whether this computer could actually run that chat. When the answer was "no", you were left with a failed chat. Its Retry button failed the same way, and no terminal opened for the agent. Now Orca checks first. If the answer is "no", the agent opens in a terminal tab instead, with the prompt you typed. Launches on a paired Orca server already worked this way.

What Changed

The problem. When you start a structured chat (the chat view Orca draws itself, as opposed to the agent's own terminal screen), the computer that runs it has the final say on whether it can create that chat. One real "no" today: Claude, when the active Claude account is tied to WSL (the Windows Subsystem for Linux). Upcoming OpenCode support adds another: an OpenCode release the chat view can't run (2.x, or older than 1.18.31). On a paired Orca server, the desktop already asked the server first, and a "no" opened the agent in a terminal. On this computer, the chat tab opened at once and the question was asked while the chat was being created. So a "no" surfaced as a failed chat.

What you see now.

  • Before: you pick Claude in a new tab and a Claude chat tab opens. It then shows that the start failed, Retry fails the same way, and you have to open a terminal yourself and retype your prompt.
  • After: a Claude terminal tab opens instead, and the prompt you typed is sent to it once.
  • When this computer says yes: you still get the chat. It opens once this computer has answered, instead of before. That answer is usually quick, but I have not measured it. When a workspace is being revealed, it stays empty until then.
  • The wait is capped at 3 seconds. If this computer hasn't answered by then, or a just-created workspace isn't resolvable yet, the chat opens anyway and reports any problem itself, exactly as before. Only a real "no" opens a terminal.
  • Empty workspace with "open agent tabs as chat" on: if this computer says no to the default agent's chat, you get the plain shell you would otherwise have had. It no longer starts an agent you didn't ask for.
  • New-workspace flows: if you moved to another workspace while a new one was being created and this computer declines its chat, the agent's terminal opens in the new workspace in the background and you stay where you are, the same way a background terminal create behaves.
  • No new notice. A paired server's "no" still shows its existing toast. A "no" from this computer shows nothing extra: the terminal opening is the answer.

The mechanism. All structured launches now go through one path, for every host. Before anything of the chat exists here (no tab, launch record, queued prompt or focus record), Orca sends the owning host its existing "can you create this chat?" request (agentSession.createSupport). That host can be this computer or a paired server.

  • Yes: the chat opens through the same local launch code as before.
  • No: the caller's own terminal launch runs, with its arguments and your prompt. A caller can name a different fallback; the empty-workspace default chat uses this to open its plain shell.
  • Can't answer, or the workspace isn't resolvable yet: on this computer, the chat opens and its own create reports, as before. A paired server that can't resolve the workspace still opens the terminal.
  • One small paired-server change: before, after the retries for a workspace that isn't resolvable yet, any other error on the last try counted as a "no" and opened the terminal. It is now classified like any other error, so a connection error there reports "Could not reach…" and opens nothing.
  • A resume (continuing a past conversation in chat) still fails on a "no", because a resume has no terminal equivalent.

What keeps a stray "Terminal 1" out. While a launch waits for its host's answer, it claims the workspace's first surface. The claim lives in the same in-memory registry that already holds a workspace's first surface while the default agent list loads. Both things that seed a shell into an empty workspace read that registry: the background first-terminal watcher, and the reseed that runs when you click the workspace. So neither adds a shell beside the coming chat. The claim ends as soon as the answer's tab or terminal has opened.

Code changes:

  • The paired-only admission module is renamed (structured-agent-session-paired-admission.ts becomes structured-agent-session-launch-admission.ts, and beginPairedStructuredLaunch becomes beginHostAdmittedStructuredLaunch). It now serves this computer too.
  • structuredLaunchPairedOwner becomes structuredLaunchOwner.
  • The new-tab launcher's separate "open the local chat at once" branch (launch-agent-in-new-tab-structured.ts) and the local-agent-session launch-surface kind are removed, since nothing produces them anymore.

Why

There were two ways to fix this:

  1. Ask before the tab opens (chosen). This is the path paired launches already use. Nothing has to be undone after a "no", and it removes the bug class instead of adding a guard beside it. The pull request that added the paired path (feat(native-chat): open structured chats on the paired Orca server that owns the workspace #24205) named this as its follow-up: make local launches ask first too, so both work the same way.
  2. Open the chat, then swap it for a terminal on a "no" (rejected). By the time the answer arrives, the chat tab, launch record, prompt queued in its outbox, composer draft, focus record and saved launch state all exist, and other launches may have joined it. Every one of those would need undoing, the prompt would have to be pulled back out of the chat and sent to the terminal exactly once, and the chat tab would flash and be replaced.

How this compares with the common pattern. It matches in one respect: no server-side chat record exists until the host has answered. It differs in three:

  • No surface appears immediately. Intended for now. In the common pattern, you see a draft or compose surface the moment you click, and the server is asked when you submit. Here, nothing new shows until this computer answers. The wait is capped at 3 seconds, after which the chat opens anyway, and while waiting the workspace doesn't fill with a stray shell. A draft surface would be a larger UI change.
  • Asked on each launch, rather than known ahead of time. Temporary. In the common pattern, the server pushes which agents are usable, so a launch never has to ask. A host-published "which agents can open as chat here" state would remove this per-launch check, the wait above and the double ask below. That needs new published state and wire-compatibility work, so it's a follow-up. There is no ticket yet.
  • A launch the host admits is asked twice (once here, then again inside the create). Temporary, inherited from the paired path. The same follow-up removes it.

Other differences from the common pattern:

  • A "no" opens a terminal rather than leaving the request with you. Intended. Orca has a terminal surface for every agent, and the paired path already does this; the common pattern has no terminal to fall back to.
  • This computer's "no" shows no notice, while a paired server's does. Intended. The terminal tab opening is the visible answer, and the existing toast's wording is about a server.

Linked Issue

None. This is internal follow-up work on structured native chat.

Visual Proof

Live QA in a hidden Orca build on macOS. A Mac has no Claude-on-WSL refusal, so the test build was this branch merged locally with #25845 (OpenCode over ACP), whose host refuses an OpenCode chat it cannot run. The QA machine's real ~/.codex, ~/.claude, ~/.orca, ~/.grok and OpenCode login files were hashed before and after every check: no change came from the test app.

Check Result Screenshot
Host says no (OpenCode pointed at a stand-in reporting an unsupported 2.0.0): the host logs opencode unsupported: installed-agent check refused; New tab > OpenCode opens exactly one terminal-backed OpenCode tab running that program, with no chat tab, no "Chat could not be started" card, no toast and no stray extra terminal Pass declined
Host says yes (OpenCode 1.18.31): New tab > OpenCode opens the structured OpenCode chat and it answers Pass admitted
Setting off: New tab > OpenCode opens the terminal-backed chat as before Pass off

In the "no" screenshot the terminal tab shows Orca's existing chat view of a terminal agent, because that profile opens agent tabs in chat view by default; the process behind it is the terminal program.

Not verified live: a typed prompt being pasted once into the terminal after a "no" (no launch surface in the test build takes a prompt before the tab opens; it is covered by the launcher test), the paired-server path, Windows, Linux, SSH and mobile. Also found: inline OpenCode credentials were refused only at create time, so they still produced a failed chat card. #25845 no longer refuses that setup at all (the chat opens with your OpenCode variables as they are), so no OpenCode refusal is left that only create detects.

Testing

  • I manually tested these changes locally (hidden Orca build driven over CDP; see Visual Proof)
  • Automated tests added/updated, or explained why not below

New tests:

  • New-tab launch of Claude on this computer, which declines: no chat, intent or tab is committed; one terminal tab opens with the caller's arguments; the typed prompt is pasted once; no toast appears. Tested at the launcher level with the real launch machinery.
  • Admission on this computer:
    • A "no" opens only the terminal, without a notice.
    • A caller's own fallback replaces the agent's terminal.
    • Abandoning the launch before the answer opens nothing.
    • "Yes" opens the chat only after the answer.
    • "Can't answer", a workspace that isn't resolvable yet, and passing the 3-second cap each open the chat.
    • A paired server that can't resolve the workspace still opens its terminal, as before.
  • Stray-shell regressions:
    • initial-terminal-admission-wait.test.tsx, with the real first-terminal watcher and the real launch: a watched create, and an empty workspace's default chat, no longer get "Terminal 1" beside the chat; the claim is released once the answer's surface opens.
    • worktree-activation-launch-awaiting-host.test.ts, with the real store, activation and reseed: clicking the still-empty workspace during the wait seeds no shell and starts no second launch.
  • Empty workspace: a "no" to the default chat seeds the plain shell, with no agent.
  • Background new-workspace create that this computer declines: the agent's terminal opens in place, with no chat, and you stay where you are.

I checked that the tests detect the bugs by turning each fix off:

  • Paired-only owner check restored: 33 tests fail.
  • Background terminal branch removed: its test fails.
  • Launch claim ignored: all 4 stray-shell tests fail.
  • "Not resolvable yet" treated as a "no": its test fails.
  • 3-second cap removed: its test fails.

Then I ran 122 test files covering every structured launch caller, the empty-workspace seeding, activation and the first-terminal watcher: all 1,324 tests pass. oxlint, oxfmt, the anti-slop lint, the changed-code quality gate and the max-lines ratchet pass on the changed files. I ran no typecheck locally; CI's typecheck passes (see CI notes).

Review

What I verified / didn't

  • Verified, in unit and integration tests only: each local structured entry point now asks first. They are the new-tab launcher and every surface that goes through it, workspace creation (watched and background), folder-workspace creation, onboarding folder add, direct work items, the full-create dialog, vault resume, and the empty-workspace default chat. A "no" opens a terminal (or the empty workspace's shell) with the prompt sent once, and leaves no chat, launch record, queued prompt or focus record behind. Neither the first-terminal watcher nor the click-to-activate reseed adds a shell beside a chat while the host decides. Those are the two seeders that read the first-surface registry; other direct calls that seed explicit startup work are not gated.
  • Not verified:
    • A real Orca run (not allowed on this machine).
    • The real Claude/WSL "no".
    • How long this computer actually takes to answer. It is not measured, and the 3-second cap is a chosen bound, not a measurement.
    • Windows and Linux.
  • Inherited from the paired path, not tested: some agents get their first prompt typed in once the terminal is ready, rather than passed on the command line. For those, in the background new-workspace case, the prompt follows whatever the creation's terminal startup plan carries; this wasn't tested with such an agent.

CI notes

  • On head c1bb90a, everything that ran passes: "static analysis and typecheck" (lint and typecheck), all five unit test shards, relay integration, packaging (macOS/Linux and Windows) and verify. An earlier head failed typecheck on one test-fixture type error, which I fixed.
  • The cross-version qoder-history-search-downgrade test also fails on unrelated PRs; if it is red here, that is not caused by this change.

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

  • Security: no change.
  • Cross-platform: no platform branches added. WSL is the main real case for the "no".
  • SSH: SSH workspaces never take the structured route, so they are unchanged.
  • Mobile: the phone launches through its own path and is unchanged.
  • Backwards compatibility: the "can you create this chat?" request already exists on every host that supports structured chat. No wire or persisted-state change.
  • Performance: one round trip to this computer's own Orca process before a structured chat tab appears, capped at 3 seconds.

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)

… terminal

A local structured launch opened its chat tab before this machine said whether it could create
the chat, so a "no" (a Claude account bound to WSL, for example) left a failed chat whose Retry
failed the same way and no terminal. Local launches now ask first through the same admission a
paired server's launch already uses: nothing of the chat exists until the host answers, and a
"no" runs the caller's own launch as a terminal.
…; bound that wait

Round 1 review of the ask-first local launch:
- A launch waiting on its host now counts as a pending chat create, so the first-terminal
  watcher no longer seeds "Terminal 1" beside a local chat (watched creates and the
  empty-workspace default chat).
- This machine's answer is waited on for at most 3 s; past it, or while a new workspace is
  not resolvable yet, the chat opens and its own create reports, as before. Only a real
  "no" opens a terminal.
- The empty-workspace default chat seeds its plain shell on a "no" instead of starting the
  agent in a terminal nobody asked for, through a narrow launcher option.
… host

The wait was held in a separate record only the passive watcher read, so re-activating the
still-empty workspace during the wait seeded "Terminal 1" through the activation's reseed. The
wait now lives in the empty-workspace default-surface claims both seeders already read, and the
separate record is gone.
@brennanb2025

brennanb2025 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Review summary

The problem. With "Use updated structured native chat" on, a chat launched on this machine opened its tab before Orca asked whether it could create that chat here. When the answer was no (today: a Claude account tied to WSL; next: OpenCode accounts Orca cannot pin), you got a failed chat whose Retry failed the same way, instead of the agent in a terminal. Launches on a paired server already asked first and fell back to the terminal.

The change for you. A launch on this machine now waits for that answer first: yes opens the chat as before; no opens the agent in a terminal with your prompt, once; no answer within 3 seconds opens the chat, which reports its own problem as before.

Fixed during review (two rounds of fresh reviewers):

  • An extra "Terminal 1" could appear beside a new chat, because the empty-workspace shell seeding ran during the wait (also via a click on the workspace). The wait now claims the workspace's first surface in the one record both seeders read.
  • The wait was unbounded; it is now 3 seconds on this machine, then the chat opens.
  • A workspace that is not yet visible right after creation no longer turns into a silent terminal; only a definite no does.
  • An empty workspace's default chat keeps its promise of a plain shell on a no, rather than starting an agent you didn't ask for.
  • The body's description of how this compares with the common pattern was corrected, with differences labelled.

Deferred: the declined-shell callback reads a store snapshot up to 3 seconds old; server-pushed agent availability (no wait, no double ask) is the better end state and a follow-up.

Verified: 1,324 tests in the 122 affected files; CI green on 6704f98 after merging main with #25225 landed (typecheck, five test shards, relay, packaging incl. Windows); the merge took main's widening of structured launches to any registered agent into the renamed host-admitted launch. Not verified: a running Orca, real WSL timing (if the local answer routinely takes over 3 s there, the WSL-account case still shows a failed chat), Windows/Linux by hand.

…lined-local-launch terminal fallback

Main widened structured launches to any registered agent; the renamed host-admitted launch takes that widening.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Ready for review.

@brennanb2025
brennanb2025 marked this pull request as ready for review October 7, 2026 12:02
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

📝 Walkthrough

Walkthrough

Structured chat launches now request admission from the local machine or a paired environment before opening a chat. Admission distinguishes unresolved workspaces and applies a three-second wait to local requests. When a host declines, callers can open a terminal. Empty-workspace launch waits hold the default surface until admission completes, and related tests cover routing, terminal fallback, and activation behavior.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to 6704f

A newly created background worktree may open without a terminal when its chat is declined. Users can open one manually, but the fallback should be fixed before merge if practical.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.61% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 31 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 describes the primary behavior change: local structured chats that cannot run now open the agent in a terminal.
Description check ✅ Passed The description is detailed and covers the user impact, implementation, rationale, testing, limitations, platform considerations, and CI results. The linked issue field is left as “None” despite the t…
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · 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: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 04d33b6c-2365-46aa-8d3d-c9290f8bb899
📥 Commits

Reviewing files that changed from the base of the PR and between 988f87e and 6704f98.

📒 Files selected for processing (28)
  • src/renderer/src/components/tab-bar/QuickLaunchButton.launch-status.test.tsx
  • src/renderer/src/components/tab-bar/QuickLaunchButton.tsx
  • src/renderer/src/components/terminal-pane/terminal-agent-session-fork.test.ts
  • src/renderer/src/components/terminal/initial-terminal-admission-wait.test.tsx
  • src/renderer/src/lib/agent-launch-caller-routing.test.ts
  • src/renderer/src/lib/empty-workspace-default-agent-chat.test.ts
  • src/renderer/src/lib/empty-workspace-default-agent-chat.ts
  • src/renderer/src/lib/empty-workspace-default-surface-claims.ts
  • src/renderer/src/lib/launch-agent-in-new-tab-structured-route.ts
  • src/renderer/src/lib/launch-agent-in-new-tab-structured.test.ts
  • src/renderer/src/lib/launch-agent-in-new-tab-structured.ts
  • src/renderer/src/lib/launch-agent-in-new-tab.ts
  • src/renderer/src/lib/launch-agent-structured-chat-guard.test.ts
  • src/renderer/src/lib/launch-structured-agent-session.ts
  • src/renderer/src/lib/launch-work-item-direct-agent-routing.ts
  • src/renderer/src/lib/run-quick-command-in-new-tab.test.ts
  • src/renderer/src/lib/run-quick-command-in-new-tab.ts
  • src/renderer/src/lib/structured-agent-session-empty-chat-reuse.test.ts
  • src/renderer/src/lib/structured-agent-session-host-admission.ts
  • src/renderer/src/lib/structured-agent-session-launch-admission.test.ts
  • src/renderer/src/lib/structured-agent-session-launch-admission.ts
  • src/renderer/src/lib/structured-agent-session-provisional-tab.test.ts
  • src/renderer/src/lib/structured-agent-session-provisional-tab.ts
  • src/renderer/src/lib/worktree-activation-default-agent-chat.test.ts
  • src/renderer/src/lib/worktree-activation-launch-awaiting-host.test.ts
  • src/renderer/src/lib/worktree-creation-structured-session-host-decline.test.ts
  • src/renderer/src/lib/worktree-creation-structured-session.ts
  • src/renderer/src/lib/worktree-initial-terminal-seeding.ts
💤 Files with no reviewable changes (3)
  • src/renderer/src/lib/launch-agent-in-new-tab-structured.test.ts
  • src/renderer/src/lib/launch-agent-in-new-tab-structured.ts
  • src/renderer/src/components/tab-bar/QuickLaunchButton.tsx

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 on lines +33 to +64
* The host's "no" to a create the user moved away from opens the agent terminal the way a
* background terminal create does: in place, without selecting the workspace (#23974). A create the
* user is still watching keeps the default, which opens it as a new agent tab there.
*/
function openBackgroundDeclinedTerminal(
args: LaunchStructuredWorktreeSessionArgs
args: LaunchStructuredWorktreeSessionArgs,
host: RuntimeClientTarget
): StructuredLaunchTerminal {
ensureWebRuntimeWorktreeTerminalAfterWake(args.worktreeId, {
startup: buildWorktreeCreationStartupOpt(args.request, false),
agent: args.request.agent,
activate: false
})
return { opened: true }
const startup = buildWorktreeCreationStartupOpt(args.request, false)
if (host.kind === 'environment') {
ensureWebRuntimeWorktreeTerminalAfterWake(args.worktreeId, {
startup,
agent: args.request.agent,
activate: false
})
return { opened: true }
}
if (!startup) {
return { opened: false }
}
// This machine seeds the agent terminal itself; a background workspace mounts it without showing it.
const tabId = ensureWorktreeHasInitialTerminal(
useAppStore.getState(),
args.worktreeId,
startup,
undefined,
undefined,
undefined,
{ activateCreatedTabs: false, createNewTerminalForStartup: true }
)
mountCreatedWorktreeStartupTabsInBackground(args.worktreeId)
return { opened: tabId !== 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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

sed -n '1,165p' src/renderer/src/lib/worktree-creation-structured-session.ts
rg -n 'buildStartupOptions|openBackgroundDeclinedTerminal|ensureWorktreeHasInitialTerminal' src/renderer/src/lib/worktree-creation-structured-session.ts src/renderer/src/lib/worktree-initial-terminal-seeding.ts

Repository: stablyai/orca

Length of output: 7213


🏁 Script executed:

printf '%s\n' '--- startup builder ---'
sed -n '1,240p' src/renderer/src/lib/worktree-creation-flow-startup.ts
printf '%s\n' '--- initial terminal seeding ---'
sed -n '1,290p' src/renderer/src/lib/worktree-initial-terminal-seeding.ts
printf '%s\n' '--- builder and seeder references ---'
rg -n -F -- 'buildWorktreeCreationStartupOpt(' src/renderer/src
rg -n -F -- 'ensureWorktreeHasInitialTerminal(' src/renderer/src
printf '%s\n' '--- creation request and caller references ---'
rg -n 'WorktreeCreationRequest|launchStructuredWorktreeSession|launchDraftPrompt|quickPrompt|startupCommand|command' src/renderer/src/lib/worktree-creation* src/renderer/src | head -240

Repository: stablyai/orca

Length of output: 41310


🏁 Script executed:

printf '%s\n' '--- execute creation flow ---'
sed -n '120,285p' src/renderer/src/lib/worktree-creation-flow-execute.ts
printf '%s\n' '--- creation request type and startupPlan producers ---'
rg -n 'startupPlan[?:]|startupPlan:' src/renderer/src/lib src/renderer/src/components | head -100
rg -n 'type WorktreeCreationRequest|interface WorktreeCreationRequest' src/renderer/src/lib/pending-worktree-creation.ts
sed -n '1,190p' src/renderer/src/lib/pending-worktree-creation.ts
printf '%s\n' '--- declined-launch implementation and tests ---'
rg -n 'onHostDeclined|host declined|declined|startupPlan' src/renderer/src/lib/structured-agent-session-provisional-tab.ts src/renderer/src/lib/worktree-creation-structured-session.test.ts
sed -n '1,220p' src/renderer/src/lib/structured-agent-session-provisional-tab.ts
sed -n '1,235p' src/renderer/src/lib/worktree-creation-structured-session.test.ts
printf '%s\n' '--- terminal auto-create contract ---'
rg -n 'shouldAutoCreateInitialTerminal|function shouldAutoCreateInitialTerminal' src/renderer/src/components/terminal/initial-terminal.tsx src/renderer/src/components/terminal/initial-terminal.ts
sed -n '1,120p' src/renderer/src/components/terminal/initial-terminal.tsx 2>/dev/null || sed -n '1,120p' src/renderer/src/components/terminal/initial-terminal.ts

Repository: stablyai/orca

Length of output: 38360


🏁 Script executed:

printf '%s\n' '--- route and startup-plan producers ---'
rg -n 'agentLaunchRoute|startupPlan' src/renderer/src/components/worktree-creation src/renderer/src/lib/worktree-creation-flow* src/renderer/src/lib/launch-agent-startup-prompt-plan.ts src/renderer/src/lib/launch-work-item-direct-route-preparation.ts
printf '%s\n' '--- host-decline behavior test ---'
sed -n '1,260p' src/renderer/src/lib/worktree-creation-structured-session-host-decline.test.ts
printf '%s\n' '--- remaining creation dispatch ---'
sed -n '270,360p' src/renderer/src/lib/worktree-creation-flow-execute.ts
printf '%s\n' '--- PR diff for reviewed helper ---'
git diff --unified=8 988f87e743ec52aa24ae1f25a308e66d6d8d2e59 6704f98a10343056e5aa0c6441abe70e125174aa -- src/renderer/src/lib/worktree-creation-structured-session.ts

Repository: stablyai/orca

Length of output: 16795


🏁 Script executed:

printf '%s\n' '--- non-test startupPlan references ---'
rg -n 'startupPlan|WorktreeCreationRequest|agentLaunchRoute' src/renderer/src --glob '!**/*.test.*' --glob '!**/*.spec.*'
printf '%s\n' '--- worktree creation source files ---'
rg --files src/renderer/src | rg 'worktree.creation|worktree-creation|composer-submit|agent-startup-prompt-plan'
printf '%s\n' '--- request producers around shared submission APIs ---'
rg -n 'pendingFirstAgentMessageRename:|quickTelemetry:|launchDraftPrompt:|promptDelivery:' src/renderer/src --glob '!**/*.test.*' --glob '!**/*.spec.*'

Repository: stablyai/orca

Length of output: 43495


🏁 Script executed:

printf '%s\n' '--- quick startup plan ---'
sed -n '1,155p' src/renderer/src/hooks/composer-state/quick-startup-plan.ts
printf '%s\n' '--- quick creation request assembly ---'
sed -n '105,260p' src/renderer/src/hooks/composer-state/quick-creation-execution.ts
printf '%s\n' '--- full submit startup-plan construction ---'
sed -n '155,255p' src/renderer/src/hooks/composer-state/full-submit-preparation.ts
printf '%s\n' '--- quick request contract ---'
sed -n '1,130p' src/renderer/src/hooks/composer-state/quick-creation-request.ts

Repository: stablyai/orca

Length of output: 20358


🏁 Script executed:

printf '%s\n' '--- structured route resolver ---'
sed -n '1,220p' src/renderer/src/lib/agent-launch-routing.ts
printf '%s\n' '--- startup builder declaration and implementation ---'
rg -n 'export function buildAgentStartupPlan|function buildAgentStartupPlan' src/renderer/src/lib/tui-agent-startup*
sed -n '1,260p' src/renderer/src/lib/tui-agent-startup.ts
printf '%s\n' '--- local terminal host authority ---'
sed -n '1,220p' src/renderer/src/lib/workspace-terminal-host-authority.ts
printf '%s\n' '--- default terminal tab seeding ---'
sed -n '1,240p' src/renderer/src/lib/worktree-default-terminal-tabs.ts
printf '%s\n' '--- seeder decision and initial terminal tests ---'
sed -n '125,255p' src/renderer/src/lib/worktree-initial-terminal-seeding.ts
rg -n 'ensureWorktreeHasInitialTerminal\\(.*|shouldAutoCreate|creates.*terminal|initial terminal' src/renderer/src/lib/worktree-activation-agent-startup.test.ts src/renderer/src/lib/worktree-activation-default-agent-chat.test.ts src/renderer/src/lib/workspace-terminal-host-authority.test.ts

Repository: stablyai/orca

Length of output: 23303


🏁 Script executed:

printf '%s\n' '--- structured route resolver ---'
sed -n '1,220p' src/renderer/src/lib/agent-launch-routing.ts
printf '%s\n' '--- startup builder declaration and implementation ---'
rg -n 'export function buildAgentStartupPlan|function buildAgentStartupPlan' src/renderer/src/lib/tui-agent-startup*
sed -n '1,260p' src/renderer/src/lib/tui-agent-startup.ts
printf '%s\n' '--- local terminal host authority ---'
sed -n '1,220p' src/renderer/src/lib/workspace-terminal-host-authority.ts
printf '%s\n' '--- default terminal tab seeding ---'
sed -n '1,240p' src/renderer/src/lib/worktree-default-terminal-tabs.ts
printf '%s\n' '--- seeder decision and initial terminal tests ---'
sed -n '125,255p' src/renderer/src/lib/worktree-initial-terminal-seeding.ts
rg -n 'ensureWorktreeHasInitialTerminal\(|shouldAutoCreate|creates.*terminal|initial terminal' src/renderer/src/lib/worktree-activation-agent-startup.test.ts src/renderer/src/lib/worktree-activation-default-agent-chat.test.ts src/renderer/src/lib/workspace-terminal-host-authority.test.ts

Repository: stablyai/orca

Length of output: 26557


🏁 Script executed:

printf '%s\n' '--- shared startup-plan builder ---'
rg -n 'function buildAgentStartupPlan|export function buildAgentStartupPlan|const buildAgentStartupPlan' src/shared/tui-agent-startup.ts
nl -ba src/shared/tui-agent-startup.ts | sed -n '1,240p'
printf '%s\n' '--- exact quick-create route/build locations ---'
nl -ba src/renderer/src/hooks/composer-state/quick-startup-plan.ts | sed -n '20,130p'
nl -ba src/renderer/src/hooks/composer-state/quick-creation-execution.ts | sed -n '120,250p'
printf '%s\n' '--- exact local fallback seeding locations ---'
nl -ba src/renderer/src/lib/worktree-initial-terminal-seeding.ts | sed -n '77,110p;132,250p'
printf '%s\n' '--- exact changed helper ---'
nl -ba src/renderer/src/lib/worktree-creation-structured-session.ts | sed -n '33,68p'
printf '%s\n' '--- local decline test input and assertions ---'
nl -ba src/renderer/src/lib/worktree-creation-structured-session-host-decline.test.ts | sed -n '75,180p'

Repository: stablyai/orca

Length of output: 33936


🏁 Script executed:

printf '%s\n' '--- declined-terminal admission contract and consumer ---'
nl -ba src/renderer/src/lib/structured-agent-session-launch-admission.ts | sed -n '1,230p'
printf '%s\n' '--- launch-command resolver null conditions ---'
rg -n 'export function resolveAgentLaunchCommand|function resolveAgentLaunchCommand' src/shared/tui-agent-launch-command.ts
nl -ba src/shared/tui-agent-launch-command.ts | sed -n '1,230p'
printf '%s\n' '--- creation completion for declined structured launch ---'
nl -ba src/renderer/src/lib/worktree-creation-completion.ts | sed -n '1,100p'
printf '%s\n' '--- remaining seeder tab creation ---'
nl -ba src/renderer/src/lib/worktree-initial-terminal-seeding.ts | sed -n '245,315p'

Repository: stablyai/orca

Length of output: 21281


Seed the default terminal when no startup plan exists.

If a background structured-chat create has no startup plan and its local host declines, this guard returns before ensureWorktreeHasInitialTerminal runs. The creation flow skipped initial seeding because it expected the chat to provide the surface, so the worktree can be left without a terminal. Let the seeder run with startup undefined; it creates the default terminal for a fresh, empty local worktree.

Suggested fix
-  if (!startup) {
-    return { opened: false }
-  }
   // This machine seeds the agent terminal itself; a background workspace mounts it without showing it.
📝 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
* The host's "no" to a create the user moved away from opens the agent terminal the way a
* background terminal create does: in place, without selecting the workspace (#23974). A create the
* user is still watching keeps the default, which opens it as a new agent tab there.
*/
function openBackgroundDeclinedTerminal(
args: LaunchStructuredWorktreeSessionArgs
args: LaunchStructuredWorktreeSessionArgs,
host: RuntimeClientTarget
): StructuredLaunchTerminal {
ensureWebRuntimeWorktreeTerminalAfterWake(args.worktreeId, {
startup: buildWorktreeCreationStartupOpt(args.request, false),
agent: args.request.agent,
activate: false
})
return { opened: true }
const startup = buildWorktreeCreationStartupOpt(args.request, false)
if (host.kind === 'environment') {
ensureWebRuntimeWorktreeTerminalAfterWake(args.worktreeId, {
startup,
agent: args.request.agent,
activate: false
})
return { opened: true }
}
if (!startup) {
return { opened: false }
}
// This machine seeds the agent terminal itself; a background workspace mounts it without showing it.
const tabId = ensureWorktreeHasInitialTerminal(
useAppStore.getState(),
args.worktreeId,
startup,
undefined,
undefined,
undefined,
{ activateCreatedTabs: false, createNewTerminalForStartup: true }
)
mountCreatedWorktreeStartupTabsInBackground(args.worktreeId)
return { opened: tabId !== null }
* The host's "no" to a create the user moved away from opens the agent terminal the way a
* background terminal create does: in place, without selecting the workspace (#23974). A create the
* user is still watching keeps the default, which opens it as a new agent tab there.
*/
function openBackgroundDeclinedTerminal(
args: LaunchStructuredWorktreeSessionArgs,
host: RuntimeClientTarget
): StructuredLaunchTerminal {
const startup = buildWorktreeCreationStartupOpt(args.request, false)
if (host.kind === 'environment') {
ensureWebRuntimeWorktreeTerminalAfterWake(args.worktreeId, {
startup,
agent: args.request.agent,
activate: false
})
return { opened: true }
}
// This machine seeds the agent terminal itself; a background workspace mounts it without showing it.
const tabId = ensureWorktreeHasInitialTerminal(
useAppStore.getState(),
args.worktreeId,
startup,
undefined,
undefined,
undefined,
{ activateCreatedTabs: false, createNewTerminalForStartup: true }
)
mountCreatedWorktreeStartupTabsInBackground(args.worktreeId)
return { opened: tabId !== null }

…l-declined-terminal

# Conflicts:
#	src/renderer/src/components/tab-bar/QuickLaunchButton.tsx
@brennanb2025
brennanb2025 merged commit 661cba9 into main Oct 7, 2026
3 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