Skip to content

Floating-workspace chats of every native-chat agent keep links in the folder they started in - #26981

Merged
brennanb2025 merged 2 commits into
mainfrom
brennanb2025/native-chat-agent-checks-use-registry
Oct 9, 2026
Merged

brennanb2025 merged 2 commits into
mainfrom
brennanb2025/native-chat-agent-checks-use-registry

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​14 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​14
Prod 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​5 $\color{#cf222e}{\Huge{\mathbf{−}}}$​15 $\color{#cf222e}{\Huge{\mathbf{−}}}$​10

ELI5

An OpenCode, Grok, OMP or Pi chat in the floating workspace now stays in the folder it started in. Its file links point there, and after an app restart the agent runs there again, even if you have since changed the floating workspace folder. Claude and Codex chats already worked this way.

What Changed

The problem. A chat in the floating workspace starts in whatever folder the floating workspace points at. The user can change that folder setting while chats exist. For Claude and Codex, Orca records the chat's starting folder (its "pin") and uses it for two things: launching the agent again (after a restart, for example), and resolving the chat's file links, image paths and skill lookups. For OpenCode, Grok, OMP and Pi floating chats, both used the current setting. So after a folder change:

  • a link like [the readme](./README.md) in an earlier reply pointed into the new folder, where the file may not exist or may be a different file;
  • after an app restart, the agent itself silently moved to the new folder. Pi even copied its conversation into the new folder.

Why it happened. Two places used a Claude/Codex-only check (isAgentSessionHandleProvider) or Claude/Codex-only code to mean "this is a native chat":

  • the renderer's floating-chat folder lookup (native-chat-tab-directory.ts);
  • the launch code: only the Claude and Codex launchers call resolveAgentSessionLaunchDirectory, the host's one rule for "which folder does this chat's agent start in". The OpenCode/Grok/OMP and Pi launchers read the current setting instead.

When the other agents got native chat (#25225 onward), the status bridge that publishes the pin was switched to "any registered native-chat agent", but these two places weren't. #26776 fixed the same kind of leftover for orchestration workers.

User-facing change. In the floating workspace, OpenCode, Grok, OMP and Pi chats:

  • resolve file links, images and skills against the folder they started in;
  • run in that folder again after a restart;
  • if that folder has been deleted, show the message Claude and Codex chats already show, "The folder this chat ran in no longer exists. Restore it to continue this chat.", instead of quietly starting in a different folder. Closing the chat and starting a new one still works.

Nothing changes for chats in a repo worktree or a folder workspace.

Mechanism.

  • acp-structured-launch-resolution.ts (OpenCode, Grok, OMP) and pi/rpc-launch-resolution.ts (Pi) now call resolveAgentSessionLaunchDirectory, the function Claude and Codex already use. For floating chats it returns the pin, refuses if the pinned folder is gone, and records a pin for an old floating chat that has none.
  • native-chat-tab-directory.ts now calls the status bridge's own isStructuredTab check instead of restating it, so the reader and the publisher of the pin can't drift apart again.
  • The unused showStructuredAgentSessionChoice helper is removed.
  • The doc comment on isAgentSessionHandleProvider now says what it means: one of the two built-in agents, for features only they have, not "has native chat". I kept the name, because a rename touches about 25 files and would conflict with other open PRs.

Leftovers checked and kept. Every other Claude/Codex-only check outside tests guards something only Claude or Codex has (importing an existing conversation, AI vault history, the built-in model lists), or keeps compatibility with older Orca hosts that don't publish their agent list. One more case of the same mistake exists in Session History (an OpenCode native chat's history shows as a plain OpenCode CLI session). It needs per-client capability checks, so it is left for a follow-up rather than added here.

Why

This reuses the launch-folder rule and the native-chat check that already exist, rather than adding a per-agent list. Each chat now has one folder that both the agent and Orca's link handling use, which is the common pattern.

Linked Issue

None. This is a follow-up to #26776, found while looking for other leftovers of the same Claude/Codex-only check.

Visual Proof

From a hidden dev Orca on this branch (21db2b4) on a second Mac, with the real OpenCode CLI against a local test model. The floating folder started as folder-a; it was then changed to folder-b. Both folders contain a README.md.

After an app restart, with the setting on folder-b, the chat still runs in folder-a:
b2-pwd-after-restart.png

Its link still targets folder-a/README.md:
b3-link-dialog.png

With folder-a deleted, sending shows the existing "folder no longer exists" message (no other notice):
c1-refusal.png

A new floating chat then runs in folder-b, and its link targets folder-b:
c3-new-chat-reply.png

The tab title "pwd printed: ..." comes from the local test model, which answered Orca's chat-naming request with its usual reply. A real model writes a normal title.

The earlier screenshots from the laptop run (10382b2) showed the link half only:
fl-2-after-move.png
fl-3-opened.png

There is no "before" screenshot from main. The new tests show the before state instead: with the fix reverted, they fail.

Testing

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

Manual, macOS (second Mac, hidden dev build, isolated profile, real OpenCode 2.0.21): link keeps the start folder after the setting changes; after a restart the agent runs in the start folder; deleted start folder shows the refusal message and a new chat works; a repo-worktree chat is unchanged. All passed.

Not run live: Grok, OMP and Pi floating chats (same code path as OpenCode for Grok and OMP; Pi is covered by unit tests). Windows and Linux.

Automated:

  • native-chat-tab-directory.test.ts: one case per agent (OpenCode, Grok, OMP, Pi). All four fail without the fix.
  • acp-structured-launch-resolution.test.ts and rpc-launch-resolution.test.ts: a floating chat launches in its pin after the setting moved (Pi also resumes without copying its conversation); a missing pin folder refuses; an old unpinned floating chat gets pinned; worktree and folder chats unchanged. The floating cases fail without the fix.
  • Adapter tests for both: the refusal reaches the user as the same "folder missing" reason Claude and Codex use, and nothing is started.
  • pnpm tc:node, pnpm tc:web and orca-ci-checks pass.

Review

Reviewed in two rounds plus a release-readiness pass; see the review comment.

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: none.
  • Platforms: no platform-specific code. The folder check is the existing one Claude and Codex use on every platform.
  • SSH and remote: floating chats only run on the local host. The OpenCode/Grok/OMP and Pi launchers still refuse chats that belong to another host or WSL before the folder check.
  • Mobile: unaffected.
  • Older peers: nothing on the wire changes.
  • Performance: one folder-exists check per launch of a pinned floating chat.
  • CI: cross-version wire compatibility is red because of main (feat(kiro): report Kiro agent status and list Kiro sessions in history #26695 added Kiro; session-search-agent-set-downgrade and qoder-history-search-downgrade expect Kiro in an older host's agent list). Not caused by this PR; this PR doesn't touch session search.

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)

…ts pinned folder

Drop the unused showStructuredAgentSessionChoice and say what the built-in agent check means.
… their pinned folder

The renderer now resolves every floating chat's links, images and skills
against the folder the chat was pinned to. The ACP (OpenCode/Grok/OMP) and
Pi launchers still started the agent in the current floating folder
setting, so after the setting moved and the agent relaunched, the agent ran
in one folder while links resolved against another (and Pi forked its
conversation into the new folder).

Both launchers now use resolveAgentSessionLaunchDirectory, the helper Claude
and Codex already use: launch in the pin, refuse with the existing
"folder this chat ran in no longer exists" message when it is gone, and pin
an unpinned floating record on first launch. Worktree and folder chats are
unchanged.

The tab-directory reader now calls the status bridge's own isStructuredTab
instead of restating its predicate inline.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review summary (two review rounds + release-readiness pass, head 21db2b4)

The problem. OpenCode, Grok, OMP and Pi chats in the floating workspace weren't held to the folder they started in, unlike Claude and Codex chats. After the floating folder setting changed, their file links pointed into the new folder. The original version of this PR fixed only that link half.

What review found and fixed.

  • P1, found in round 1 and fixed in 21db2b4. The links-only fix made things inconsistent after a restart. The OpenCode/Grok/OMP and Pi launchers still started a relaunched chat in the current floating folder, while the links now pointed at the original one. So after a setting change plus an app restart, the agent ran in folder B and its links opened files in folder A. Pi also copied its conversation into folder B. Both launchers now use the same launch-folder rule as Claude and Codex.
  • P3, fixed in 21db2b4. The renderer's floating-folder lookup restated the status bridge's "is this a native chat" check under a comment claiming they were the same. That drift is how this bug started. It now calls the bridge's own check.

User-facing change. Floating OpenCode/Grok/OMP/Pi chats resolve links, images and skills against their starting folder and run there again after a restart. If that folder has been deleted, sending shows the message Claude and Codex chats already show: "The folder this chat ran in no longer exists. Restore it to continue this chat." Before, they quietly started in another folder. Repo-worktree and folder-workspace chats are unchanged.

Deferred (not in this PR).

  • P2, already existed before this PR, separate follow-up. Session History treats only Claude/Codex rows as owned by a native chat (ai-vault/structured-session-ownership.ts:95-97, :131-133). An OpenCode native chat's history therefore shows as a plain OpenCode CLI session, and "resume" opens it in a terminal. Fixing it needs a per-row check that the client can render that agent, plus checks of the Grok/OMP/Pi session ids. That is a Session History ownership change, not part of this fix.
  • P3, from this loop's fix. OpenCode's crash-recovery read now stops when the pinned folder has been deleted. A send left unconfirmed by a crash stays unconfirmed in that chat. The chat can't continue anyway, and the user can still resend, discard or close.
  • Residual risk. Floating OpenCode/Grok/OMP/Pi chats that main already relaunched in a moved folder go back to their original folder. Pi copies its conversation over with history; whether OpenCode and Grok handle that is unverified. Native chat has no real users yet.
  • Copy. The deleted-folder message doesn't name the folder or mention starting a new chat. It is the existing shared line, unchanged here.
  • Nits are in the review ledger: the dropped "unknown values must not impersonate Codex" doc phrase, test-assert gaps, and a mobile comment.

Verified.

  • Unit tests for each agent; the new launch-folder and refusal tests fail with the fix reverted. tc:node, tc:web and orca-ci-checks pass.
  • CI is green except cross-version wire compatibility and its verify gate. Those fail in session-search-agent-set-downgrade and qoder-history-search-downgrade, which come from main (feat(kiro): report Kiro agent status and list Kiro sessions in history #26695, Kiro), not this PR.
  • Live on a second Mac (hidden dev build of 21db2b4, real OpenCode 2.0.21, local test model), all four checks passed:
    • the link keeps the starting folder after the setting changes;
    • after a restart the agent runs in the starting folder (checked by the agent's own pwd and its process folder);
    • with the starting folder deleted, the refusal message is the only notice and a new floating chat works in the new folder;
    • a repo-worktree chat is unchanged.
  • The release-readiness pass found no P0, P1 or P2 issues.

Not verified.

  • Grok, OMP and Pi floating chats live. Grok and OMP share OpenCode's launcher; Pi is covered by unit tests.
  • Windows, Linux, and at human typing speed: the test driver's first Enter, pressed milliseconds after opening a fresh floating chat, was dropped. That code is untouched here and was not checked at human speed.
  • Whether OpenCode and Grok can reload a session from a different folder than the one it was created in.

@brennanb2025
brennanb2025 marked this pull request as ready for review October 9, 2026 23:55
@brennanb2025
brennanb2025 merged commit 094f589 into main Oct 9, 2026
35 of 37 checks passed
brennanb2025 added a commit that referenced this pull request Oct 15, 2026
… folder they started in (#26981)

* fix(native-chat): resolve every registered agent's floating chat to its pinned folder

Drop the unused showStructuredAgentSessionChoice and say what the built-in agent check means.

* fix(native-chat): launch floating OpenCode, Grok, OMP and Pi chats in their pinned folder

The renderer now resolves every floating chat's links, images and skills
against the folder the chat was pinned to. The ACP (OpenCode/Grok/OMP) and
Pi launchers still started the agent in the current floating folder
setting, so after the setting moved and the agent relaunched, the agent ran
in one folder while links resolved against another (and Pi forked its
conversation into the new folder).

Both launchers now use resolveAgentSessionLaunchDirectory, the helper Claude
and Codex already use: launch in the pin, refuse with the existing
"folder this chat ran in no longer exists" message when it is gone, and pin
an unpinned floating record on first launch. Worktree and folder chats are
unchanged.

The tab-directory reader now calls the status bridge's own isStructuredTab
instead of restating its predicate inline.

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