Files
orca/src/shared/agent-session-create-capabilities.ts
T
Jinwoo Hong 24f5c57030 A chat started in a server workspace drawn from the server's layout stays where you opened it (switch-on builds only) (#32003)
* fix(L273-core): tabs this desktop created in a paired server's frame-drawn workspace survive the server's frames (switch-on)

A tab the server's layout never lists (a client-hosted browser tab today; any kind recorded the
same way) used to vanish on the server's next frame rebuild. This desktop now records such tabs by
server and workspace, in its localStorage only (never a session field nor sent), and the server's
frame builds keep them as drawn, inside the server's own groups:
- A held tab drawn in a group the server lacks moves to the nearest server group; it shows there
  only if the user was on it, and the user's group follows it. The group tree stays the server's.
- The record forgets an id once the server's frame lists it, on close, and on worktree removal,
  and prunes ids the restored session no longer has before the first frame after a reload.
- A held tab's drag moves it here only (no session.tabs.move); it offers no new split, and a
  server tab sharing its group counts only the server's tabs for split no-ops.
- Client-hosted staging records its tab (both ids) in layout-owner builds; restage takes the
  worktree from its caller instead of scanning the store.

* refactor(L273-core): the desktop-created record is inert with the switch off; clearer names

- With the switch off the record is never read, kept or written (a single gate in the record).
- The frame-drawn check is isDesktopCreatedTabInFrameDrawnServerWorkspace in
  frame-drawn-desktop-created-tabs.ts: "held" stays the frame hold's word.
- The held-tab placement drops its unread activeGroupId; the group follow derives it.

* wip(L270): a host advertising agentSession.create.tab-placement.v1 opens a created chat's tab where the creating client asked, in its own group and tab ids

* test(L270): a switch-off windowless host saves a chat opened with no client place at the end of the remembered active group, as before (test only)

* wip(L270): a desktop's chat in a frame-drawn server workspace stays put: the frames hold its provisional tab until the server lists it, and the create names its group and the server tab before it (layout-owner build only)

* test(L270): a switch-on serve saves a client's chat after the tab it named; the server-layout read moves to one e2e helper (test only)

* refactor(L270): the create's tab place keeps its own name, and the layout rule's comment names the client's place first

* refactor(L270): the create's tab fields live in their own schema file and the windowless save passes its chat through, so no file passes its line limit; the placement test types its mocks without assertions

* refactor(L270): the chat's place names server tabs through H5c's route, which also skips tabs the server's frames do not hold

* fix(L270): a chat opened while a reconnected server stream awaits its snapshot still names its group; the layout-owner e2e list runs the placement spec

* fix(L270): only a create in a frame-drawn server workspace awaits its tab place, so every other create keeps its timing (the eager History joiner after an early terminal close opens again)

* fix(native-chat): read a frame-drawn chat tab's place without a tick, so an eager History joiner's create is still sent

* test(native-chat): type the create's params so an async placement cannot compile; name the probe that fills the cache

* test: type the placement mock's original through a type import
2026-10-22 16:16:43 -04:00

42 lines
2.4 KiB
TypeScript

// The capabilities that gate what an agent-session create (structured or terminal) or an
// `agentSession.send` may carry.
// Why: `agentSession.create` is a strict object, so an older host refuses a payload carrying the
// reserved `tabId` rather than ignoring it. A client sends the field only to a host advertising this.
export const AGENT_SESSION_CREATE_TAB_ID_RUNTIME_CAPABILITY =
'agentSession.create.tab-id.v1' as const
// Why: same strict object; a host advertising this opens the created chat's tab at `tabPlacement`
// (its own group and tab ids), so a paired client's chosen place survives the host's save (L270).
export const AGENT_SESSION_CREATE_TAB_PLACEMENT_RUNTIME_CAPABILITY =
'agentSession.create.tab-placement.v1' as const
// Why: same strict object; a host advertising this records the conversation's first message with
// the chat it creates, so the message is the host's from the start.
export const AGENT_SESSION_CREATE_MESSAGE_RUNTIME_CAPABILITY =
'agentSession.create.message.v1' as const
// Why: `agentSession.send` is a strict object, so an older host refuses `options`. A host advertising
// this applies a send's model and effort to the turn it starts and records them on the chat.
export const AGENT_SESSION_SEND_OPTIONS_RUNTIME_CAPABILITY =
'agent-session.send-options.v1' as const
// Why: terminal.createAgentSession and terminal.ensureAgentSession are strict objects too, so an
// older host refuses `accountEnv`. A client sends it only to a host advertising this.
export const AGENT_SESSION_ACCOUNT_ENV_RUNTIME_CAPABILITY = 'agent-session.account-env.v1' as const
// Why: the send body is a strict object, so an older host refuses `skills`. A host advertising this
// hands each skill the user picked to the agent by its path; elsewhere the `$name` text stands alone.
export const AGENT_SESSION_SEND_SKILLS_RUNTIME_CAPABILITY = 'agent-session.send-skills.v1' as const
export const AGENT_SESSION_SEND_USAGE_LIMIT_CONTINUATION_RUNTIME_CAPABILITY =
'agent-session.send-usage-limit-continuation.v1' as const
export const AGENT_SESSION_CREATE_RUNTIME_CAPABILITIES = [
AGENT_SESSION_SEND_USAGE_LIMIT_CONTINUATION_RUNTIME_CAPABILITY,
AGENT_SESSION_CREATE_TAB_ID_RUNTIME_CAPABILITY,
AGENT_SESSION_CREATE_MESSAGE_RUNTIME_CAPABILITY,
AGENT_SESSION_CREATE_TAB_PLACEMENT_RUNTIME_CAPABILITY,
AGENT_SESSION_SEND_OPTIONS_RUNTIME_CAPABILITY,
AGENT_SESSION_ACCOUNT_ENV_RUNTIME_CAPABILITY,
AGENT_SESSION_SEND_SKILLS_RUNTIME_CAPABILITY
] as const