Files
orca/src/shared/orca-session-address.ts
T
Brennan Benson e42c0b3630 fix(orchestration): call the Orca session ID orca_session_id everywhere agents see it (#24230)
* refactor(orchestration): spell the Orca session address orca_session_id:<id>

The database already names this identity orca_session_id, but agents saw and typed
session:<id>, which collides with the Claude/Codex session ID, Session History and
terminal sessions. The prefix is now orca_session_id:. No alias, migration or version
handshake for the old session: spelling: restructured native chat is experimental.

The coordinator-address triggers compile the prefix into their body, and migrate-v42
stamped them once, so an existing database would keep remembering session:<id> for
new Runs. They are now recreated on every open, like the mail routing trigger.

Provider-id refusals now name the "Orca session ID" instead of "Orca address"/"Orca id".

* fix(orchestration): name the Orca session ID the same way in both provider-id refusals

The send refusal handed back orca_session_id:<id> as the Orca session ID while
the caller refusal called the bare id by that name. Both now say "This
session's Orca session ID is orca_session_id:<id>"; the caller refusal also
names the bare value ORCA_AGENT_SESSION_ID accepts, since that input takes
only the bare id.

Tests pin both refusal texts, that session:<id> no longer parses as a session,
and that the older-build trigger rewrite actually changes the trigger SQL.

* fix(orchestration): keep chats reachable at their session:<id> address after the rename

Existing native chats were addressed as session:<id>, so agents and stored
mail still use that spelling. Input now accepts session:<id> and treats it as
orca_session_id:<id> (the codec parses both; nothing writes the old one), and
schema v43 rewrites every stored session:<id> address once: message senders
and recipients, remembered Run coordinator addresses, structured pointer
operation keys, and coordinator loop handles. Subjects, bodies and payloads
are left as written.

* fix(orchestration): refuse session:<id> input again; stored addresses still migrate once

The old prefix is not accepted as input: no respelling helper, no special
case. A session:<id> recipient is refused like any unknown terminal handle
(terminal_not_found), including after v43 rewrote a Run's remembered
session:<id> coordinator address, so the old spelling no longer routes.
Schema v43 still rewrites stored session:<id> addresses once.
2026-10-01 10:33:49 -07:00

56 lines
2.8 KiB
TypeScript

import { isAgentSessionId } from './agent-session-record'
import { STRUCTURED_WORKER_HANDLE_PREFIX } from './structured-worker-handle'
import { ORCA_SESSION_ADDRESS_PREFIX } from './orca-session-address-prefix'
/**
* The Orca session id is the id Orca minted for a structured session (its session record id, the
* value of `ORCA_AGENT_SESSION_ID`), never the provider's own session id. Orchestration stores,
* bare, the one the agent is addressed by: for a `/clear`ed chat, its lineage root's, not the live
* session's. Mail addresses the session as `orca_session_id:<id>`, beside `run:<id>` and `dispatch:<id>`,
* and derives that spelling here rather than storing it.
*
* Where the session runs is not part of the id; it is read from the session record when needed. PTY
* agents have none today, and never a pane-keyed one: a pane outlives the agent in it, so such an id
* would be inherited by the pane's next occupant.
*/
export { ORCA_SESSION_ADDRESS_PREFIX }
declare const orcaSessionIdBrand: unique symbol
declare const orcaSessionAddressBrand: unique symbol
/** A bare Orca session id; only `isOrcaSessionId` and `parseOrcaSessionAddress` produce one. */
export type OrcaSessionId = string & { readonly [orcaSessionIdBrand]: true }
/** An `orca_session_id:<id>` mail address; only `formatOrcaSessionAddress` produces one. */
export type OrcaSessionAddress = string & { readonly [orcaSessionAddressBrand]: true }
// Terminal handles (`term_` from the PTY runtime, `structworker_` from structured-worker-identity)
// share the session-id charset. A handle is never a session, so one handed over by mistake must not
// become a durable Orca session id.
const TERMINAL_HANDLE_PREFIXES = ['term_', STRUCTURED_WORKER_HANDLE_PREFIX] as const
export function isOrcaSessionId(id: string): id is OrcaSessionId {
return isAgentSessionId(id) && !TERMINAL_HANDLE_PREFIXES.some((prefix) => id.startsWith(prefix))
}
export function formatOrcaSessionAddress(orcaSessionId: OrcaSessionId): OrcaSessionAddress {
const address = `${ORCA_SESSION_ADDRESS_PREFIX}${orcaSessionId}`
// Always true for a checked id; the check brands the address without a type assertion.
if (!isOrcaSessionAddress(address)) {
throw new Error(`Not an Orca session id: ${orcaSessionId}`)
}
return address
}
/** The bare Orca session id of an `orca_session_id:<id>` address; anything else reads as null. */
export function parseOrcaSessionAddress(address: string | null | undefined): OrcaSessionId | null {
if (!address?.startsWith(ORCA_SESSION_ADDRESS_PREFIX)) {
return null
}
const id = address.slice(ORCA_SESSION_ADDRESS_PREFIX.length)
return isOrcaSessionId(id) ? id : null
}
function isOrcaSessionAddress(address: string): address is OrcaSessionAddress {
return parseOrcaSessionAddress(address) !== null
}