refactor(orchestration): drop the session id's terminal-view spawn now that the handoff is gone

The terminal handoff was removed, so no terminal is ever a structured session:
- delete the terminal-view identity env and its WSL passthrough, and their tests;
- strip the session caller keys from every terminal's env unconditionally;
- the CLI's own-address spelling moves beside the injected id in src/shared, with
  a test pinning it to the address the host's party resolver gives that session.
This commit is contained in:
Brennan Benson
2026-09-25 18:41:27 -07:00
parent ae683e11d5
commit 177ff7d9ac
15 changed files with 114 additions and 158 deletions
@@ -6,7 +6,7 @@ import { orchestrationMigrationData } from '../../../shared/orchestration-rpc-co
import { callOrchestrationMutation } from './mutation-request'
import { isDevCliInvocation } from './runtime-compatibility'
import { resolveCoordinatorTerminalHandle } from './terminal-identity'
import { injectedSessionAddress } from '../../session-caller-flags'
import { injectedSessionAddress } from '../../../shared/agent-session-caller-env'
export const ORCHESTRATION_DISPATCH_HANDLER: Record<string, CommandHandler> = {
'orchestration dispatch': async ({ flags, client, cwd, json }) => {
@@ -41,7 +41,7 @@ export const ORCHESTRATION_CHECK_HANDLER: Record<string, CommandHandler> = {
const timeoutMs = getOptionalPositiveIntegerValueFlag(flags, 'timeout-ms')
const explicitTerminal = getOptionalStringFlag(flags, 'terminal')
const terminal = await resolveOrchestrationTerminalHandle(flags, cwd, client, 'terminal')
// Why: a session names itself by its id alone; a terminal view's pane is not its identity.
// Why: a session names itself by its id alone; a pane key it inherited is not its identity.
const paneKey =
explicitTerminal || terminal === undefined ? undefined : process.env.ORCA_PANE_KEY
const callerLabel = orchestrationCallerLabel(terminal)
@@ -3,8 +3,10 @@ import { getOptionalStringFlag } from '../../flags'
import { RuntimeClientError } from '../../runtime-client'
import { getTerminalHandle } from '../../selectors'
import { hasStructuredSessionMarker } from '../../../shared/structured-session-marker'
import { readInjectedAgentSessionId } from '../../../shared/agent-session-caller-env'
import { injectedSessionAddress } from '../../session-caller-flags'
import {
injectedSessionAddress,
readInjectedAgentSessionId
} from '../../../shared/agent-session-caller-env'
/**
* The caller's terminal handle, or `undefined` when an injected agent session id names the caller:
@@ -6,8 +6,9 @@
* naming anyone else is refused before any request — never silently dropped, never allowed to win.
* The #21097 accident was a chat that named a sibling's terminal and consumed that sibling's mail.
*
* The session env here is the hardest case, a chat in terminal view: it also carries its pane's
* `ORCA_TERMINAL_HANDLE` and `ORCA_PANE_KEY`, and the implicit-terminal guess has a sibling to find.
* The session env here is the hardest case, a chat that inherited a pane's `ORCA_TERMINAL_HANDLE`
* and `ORCA_PANE_KEY` (an Orca launched from an Orca terminal), and the implicit-terminal guess has
* a sibling to find.
*/
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
@@ -238,12 +239,12 @@ function setEnv(env: Partial<Record<(typeof IDENTITY_ENV)[number], string>>): vo
}
}
/** A chat in terminal view: its id, plus the pane identity that view inherits. */
function asSessionInTerminalView(): void {
/** A chat with its id, plus a pane identity it inherited from the Orca that launched it. */
function asSessionWithInheritedPane(): void {
setEnv({
ORCA_AGENT_SESSION_ID: SESSION,
ORCA_TERMINAL_HANDLE: 'term_view_pane',
ORCA_PANE_KEY: 'tab_view:11111111-1111-4111-8111-111111111111'
ORCA_TERMINAL_HANDLE: 'term_inherited_pane',
ORCA_PANE_KEY: 'tab_inherited:11111111-1111-4111-8111-111111111111'
})
}
@@ -261,7 +262,7 @@ afterEach(() => {
})
describe.each(CALLER_VERBS)('orchestration $command run as an agent session', (verb) => {
beforeEach(asSessionInTerminalView)
beforeEach(asSessionWithInheritedPane)
it('acts as the session: no terminal is resolved, guessed or sent', async () => {
await invoke(verb.command, flagMap(verb.flags))
@@ -269,7 +270,7 @@ describe.each(CALLER_VERBS)('orchestration $command run as an agent session', (v
const [params] = callsTo(verb.method)
expect(params, 'the verb reached its method').toBeDefined()
expect(params?.[verb.callerParam]).toBeUndefined()
// A terminal view's pane is not the session's identity.
// An inherited pane is not the session's identity.
expect(params?.terminalPaneKey).toBeUndefined()
expect(params?.senderPaneKey).toBeUndefined()
expect(getTerminalHandleMock).not.toHaveBeenCalled()
@@ -293,9 +294,9 @@ describe.each(CALLER_VERBS)('orchestration $command run as an agent session', (v
)
it.runIf(verb.callerFlag !== undefined)(
"refuses its own terminal view's pane handle too: the session, not the pane, is the caller",
'refuses an inherited pane handle too: the session, not the pane, is the caller',
async () => {
const flags = flagMap({ ...verb.flags, [verb.callerFlag ?? 'from']: 'term_view_pane' })
const flags = flagMap({ ...verb.flags, [verb.callerFlag ?? 'from']: 'term_inherited_pane' })
await expect(invoke(verb.command, flags)).rejects.toMatchObject({ code: 'consumer_fenced' })
expect(callMock).not.toHaveBeenCalled()
@@ -318,7 +319,7 @@ describe.each([
{ command: 'gate-list', method: 'gateList', callerParam: 'from' },
{ command: 'task-list', method: 'taskList', callerParam: 'callerTerminalHandle' }
])('orchestration $command --run run as an agent session', ({ command, method, callerParam }) => {
beforeEach(asSessionInTerminalView)
beforeEach(asSessionWithInheritedPane)
it('needs no caller, but refuses a --from naming another caller, before any request', async () => {
await invoke(command, flagMap({ run: 'run_1' }))
@@ -406,7 +407,7 @@ describe('the identity a session presents', () => {
await invoke('dispatch-show', flagMap({ task: 'task_1', preamble: true, ...flags }))
return callsTo('dispatchShow')[0]?.from
}
asSessionInTerminalView()
asSessionWithInheritedPane()
expect(await preview({})).toBe(`session:${SESSION}`)
// Not a caller flag: it names the text to preview, so it is never fenced.
expect(await preview({ from: 'term_sibling' })).toBe('term_sibling')
@@ -416,7 +417,7 @@ describe('the identity a session presents', () => {
})
it('resumes a timed-out ask as the session, without naming a terminal', async () => {
asSessionInTerminalView()
asSessionWithInheritedPane()
callMock.mockResolvedValue({ result: { ...RESULT.result, answer: null, timedOut: true } })
const errors = vi.mocked(console.error)
@@ -443,7 +444,7 @@ describe('a host refusal of the session', () => {
it.each(['check', 'run-current', 'worker-list'])(
'surfaces from %s verbatim, never widened, retried or turned into a terminal guess',
async (command) => {
asSessionInTerminalView()
asSessionWithInheritedPane()
callMock.mockRejectedValue(WORKER_GONE)
await expect(invoke(command, flagMap({}))).rejects.toBe(WORKER_GONE)
@@ -481,11 +482,11 @@ describe('the orchestration envelope', () => {
it('carries the injected id beside whatever terminal evidence the process also has', () => {
const envelope = createOrchestrationCompatibilityEnvelope({
ORCA_AGENT_SESSION_ID: ` ${SESSION} `,
ORCA_TERMINAL_HANDLE: 'term_view_pane'
ORCA_TERMINAL_HANDLE: 'term_inherited_pane'
})
expect(envelope.orchestrationCompatibilityEvidence).toEqual({
terminalHandle: 'term_view_pane',
terminalHandle: 'term_inherited_pane',
agentSessionId: SESSION
})
})
@@ -539,7 +540,7 @@ describe('which flag names the caller, declared on every spec', () => {
it.each(callerFlagVerbs)(
'$command refuses --$flag naming another caller, before any request',
async ({ command, flag }) => {
asSessionInTerminalView()
asSessionWithInheritedPane()
await expect(
invoke(command, flagMap({ ...EVERY_REQUIRED_FLAG, [flag]: 'term_sibling' }))
).rejects.toMatchObject({ code: 'consumer_fenced' })
@@ -555,7 +556,7 @@ describe('which flag names the caller, declared on every spec', () => {
.map((flag) => ({ command: spec.path[1] ?? '', flag }))
)
)('$command passes a --$flag target through unfenced', async ({ command, flag }) => {
asSessionInTerminalView()
asSessionWithInheritedPane()
await invoke(command, flagMap({ ...EVERY_REQUIRED_FLAG, [flag]: 'term_sibling' })).catch(
(error: unknown) => {
expect(error).not.toMatchObject({ code: 'consumer_fenced' })
@@ -608,7 +609,7 @@ describe('every orchestration verb, enumerated', () => {
'worker-start'
])
asSessionInTerminalView()
asSessionWithInheritedPane()
expect(await verbsThatGuess()).toEqual([])
})
})
+4 -17
View File
@@ -10,8 +10,10 @@
import type { CommandSpec, IdentityFlag } from './command-spec'
import { RuntimeClientError } from './runtime/types'
import { readInjectedAgentSessionId } from '../shared/agent-session-caller-env'
import { isStructuredWorkerHandle } from '../shared/structured-worker-handle'
import {
injectedSessionAddress,
readInjectedAgentSessionId
} from '../shared/agent-session-caller-env'
import { ORCA_SESSION_ADDRESS_PREFIX } from '../shared/orca-session-address-prefix'
export function refuseConflictingSessionCallerFlags(
@@ -53,18 +55,3 @@ function namesInjectedSession(value: string, sessionId: string, env: NodeJS.Proc
value === injectedSessionAddress(env)
)
}
/**
* The address the host gives this session: a structured worker keeps the handle it was minted, any
* other session is `session:<id>`. Only for text that must match what the host writes.
*/
export function injectedSessionAddress(env: NodeJS.ProcessEnv = process.env): string | undefined {
const sessionId = readInjectedAgentSessionId(env)
if (!sessionId) {
return undefined
}
const ownHandle = env.ORCA_TERMINAL_HANDLE
return isStructuredWorkerHandle(ownHandle)
? ownHandle
: `${ORCA_SESSION_ADDRESS_PREFIX}${sessionId}`
}
@@ -413,7 +413,7 @@ describe('registerPtyHandlers', () => {
])
)
})
it("strips an inherited agent session id and keeps a terminal view's own", async () => {
it('strips an inherited agent session id', async () => {
// Why: a daemon forked by an Orca launched inside a structured session inherits its id,
// and every daemon pane would present that session as its orchestration caller.
const inherited = await daemonSpawnAndGetOptions(undefined, undefined, undefined, {
@@ -423,14 +423,6 @@ describe('registerPtyHandlers', () => {
expect(inherited.envToDelete).toEqual(
expect.arrayContaining(['ORCA_AGENT_SESSION_ID', 'ORCA_STRUCTURED_SESSION'])
)
const own = await daemonSpawnAndGetOptions(
{ ORCA_AGENT_SESSION_ID: 'f7a1c0de-1111-4222-8333-444455556666' },
undefined,
undefined,
{ ORCA_AGENT_SESSION_ID: 'a0b1c2d3-0000-4000-8000-00000000abcd' }
)
expect(own.envToDelete ?? []).not.toContain('ORCA_AGENT_SESSION_ID')
expect(own.env.ORCA_AGENT_SESSION_ID).toBe('f7a1c0de-1111-4222-8333-444455556666')
})
it('preserves an explicitly requested Claude child-session stamp', async () => {
// Why: only inherited values are poison; a caller deliberately spawning a
@@ -462,7 +454,7 @@ describe('registerPtyHandlers', () => {
// Why: bare `orca` must resolve to the Orca CLI before /usr/bin/orca (the GNOME screen reader) in Orca terminals (#7904).
expect(entries.indexOf(shimDir)).toBeGreaterThanOrEqual(0)
expect(entries.indexOf(shimDir)).toBeLessThan(entries.indexOf('/usr/bin'))
// The same absolute spelling a structured session gets, so a terminal view keeps it too.
// The same absolute spelling a structured session gets.
expect(env.ORCA_CLI_COMMAND).toBe(join(shimDir, 'orca'))
} finally {
Object.defineProperty(process, 'platform', {
@@ -361,13 +361,6 @@ describe('registerPtyHandlers', () => {
expect(env.ORCA_AGENT_SESSION_ID).toBeUndefined()
expect(env.ORCA_STRUCTURED_SESSION).toBeUndefined()
})
it('keeps the session id a terminal view is spawned with', async () => {
const env = await spawnAndGetEnv(
{ ORCA_AGENT_SESSION_ID: 'f7a1c0de-1111-4222-8333-444455556666' },
{ ORCA_AGENT_SESSION_ID: 'a0b1c2d3-0000-4000-8000-00000000abcd' }
)
expect(env.ORCA_AGENT_SESSION_ID).toBe('f7a1c0de-1111-4222-8333-444455556666')
})
it('keeps an explicitly requested Claude child-session stamp on a local spawn', async () => {
const env = await spawnAndGetEnv(
{ CLAUDE_CODE_CHILD_SESSION: '1' },
+6 -5
View File
@@ -141,11 +141,12 @@ export function getInheritedAgentSessionStampEnvKeysToDelete(
spawnEnv: Record<string, string> | undefined
): string[] {
const env = spawnEnv ?? {}
// Why: strip only values inherited from the pty host; a caller that explicitly
// provides a stamp (a nested Claude child, a structured session's terminal view) keeps it.
return [...CLAUDE_CHILD_SESSION_STAMP_ENV_KEYS, ...ORCA_AGENT_SESSION_CALLER_ENV_KEYS].filter(
(key) => env[key] === undefined
)
// Why: a caller that explicitly provides a Claude stamp (a nested Claude child) keeps it; no
// terminal is a structured session, so the session caller keys always go.
return [
...CLAUDE_CHILD_SESSION_STAMP_ENV_KEYS.filter((key) => env[key] === undefined),
...ORCA_AGENT_SESSION_CALLER_ENV_KEYS
]
}
// Why: a nested terminal can inherit prior OpenCode/Pi/OMP overlay env; restore the user's recorded source dir, else strip only Orca-owned values.
-20
View File
@@ -16,26 +16,6 @@ describe('addOrcaWslInteropEnv', () => {
expect(env.WSLENV).toBe('ORCA_TERMINAL_HANDLE/u:ORCA_SHELL_READY_ROOT/p')
})
it("carries a terminal view's session id into the guest untranslated, beside the WSL stamp", () => {
// Crossing is what makes the claim refusable: without it the pane handle would silently become
// the caller inside WSL, and the host could not tell the session was asking at all.
const env: Record<string, string> = {
ORCA_TERMINAL_HANDLE: 'term_wsl',
ORCA_AGENT_SESSION_ID: 'f7a1c0de-1111-4222-8333-444455556666'
}
stampWslOrchestrationCompatibilityHost(env, 'local', 'Ubuntu')
addOrcaWslInteropEnv(env)
expect(env.WSLENV?.split(':')).toEqual(
expect.arrayContaining([
'ORCA_AGENT_SESSION_ID/u',
'ORCA_ORCHESTRATION_COMPATIBILITY_HOST_KIND/u',
'ORCA_ORCHESTRATION_COMPATIBILITY_HOST_INCARNATION/u'
])
)
})
// Why this is published at all: the wrapper tree is content-addressed, so the
// in-guest login script cannot rebuild its path from ORCA_USER_DATA_PATH -- it
// cannot derive the hash segment. Without this the guest finds no wrapper and
-4
View File
@@ -9,7 +9,6 @@ import {
SETUP_AGENT_SEQUENCE_STARTUP_SCRIPT_ENV
} from '../../shared/setup-agent-sequencing'
import { getShellReadyWrapperRoot } from '../providers/local-pty-shell-ready-wrapper-root'
import { ORCA_AGENT_SESSION_ID_ENV } from '../../shared/agent-session-caller-env'
import { ORCA_IMAGE_PROTOCOL_ENV } from '../../shared/terminal-image-protocol'
const WSLENV_ENTRY_SEPARATOR = ':'
@@ -75,9 +74,6 @@ export function addOrcaWslInteropEnv(env: Record<string, string>): void {
// Why: wsl.exe only imports selected Windows env vars, so WSL needs the wrapper root, pane identity, and hook/OMP coordinates at start.
const passthroughEntries = [
'ORCA_TERMINAL_HANDLE/u',
// Why: a structured session's terminal view in a WSL shell must still present its id, so the
// host refuses the cross-host claim instead of the pane handle silently becoming its caller.
`${ORCA_AGENT_SESSION_ID_ENV}/u`,
'ORCA_USER_DATA_PATH/p',
// Why /p: the guest reads the content-addressed wrapper tree through /mnt/c,
// and it cannot derive the hash segment from ORCA_USER_DATA_PATH alone.
@@ -266,40 +266,6 @@ describe('agent-session create operation ledger', () => {
)
})
it("spawns a structured session's terminal view with its session id, never persisting it", async () => {
// Switching a chat to terminal view must not change who it is as an orchestration caller. The
// id rides the spawn env only: the launch config persists, and a relaunch from it would replay
// the id without the handoff that binds the terminal to the session.
const sessionId = 'f7a1c0de-1111-4222-8333-444455556666'
const runtime = createRuntime()
const createTerminal = vi.spyOn(runtime, 'createTerminal').mockResolvedValue(terminal())
const resume = {
kind: 'explicit' as const,
worktree: 'id:worktree-1',
agent: 'claude' as const,
providerSession: { key: 'session_id' as const, id: 'provider-session-1' }
}
await runtime.ensureAgentSession(
resume,
{},
{
spawnToken: 'spawn-9',
providerRoot: '/accounts/claude',
sessionId
}
)
await runtime.ensureAgentSession(resume)
const [handoff, plain] = createTerminal.mock.calls.map(([, opts]) => opts)
expect(handoff).toMatchObject({
structuredAgentSessionId: sessionId,
env: expect.objectContaining({ ORCA_AGENT_SESSION_ID: sessionId })
})
expect(handoff?.launchConfig?.agentEnv).not.toHaveProperty('ORCA_AGENT_SESSION_ID')
expect(plain?.env ?? {}).not.toHaveProperty('ORCA_AGENT_SESSION_ID')
})
it('selects nested SSH legacy fallback before reading a Pi transcript path locally', async () => {
const runtime = createRuntime()
const internal = runtime as unknown as {
@@ -0,0 +1,50 @@
import { afterEach, describe, expect, it } from 'vitest'
import { injectedSessionAddress } from '../../../shared/agent-session-caller-env'
import { testOrcaSessionId } from '../../../shared/orca-session-address-test-fixture'
import {
mintStructuredWorkerHandle,
mintStructuredWorkerPaneKey,
structuredWorkerIdentities,
structuredWorkerProcessIncarnation
} from '../structured-worker-identity'
import { resolveOrcaSessionParty } from './orchestration-party'
// The CLI spells its own address without the host resolver (it runs before the codec's module
// graph), so it must land on the one mailbox address the resolver gives the same session.
const CHAT = testOrcaSessionId('f7a1c0de-1111-4222-8333-444455556666')
const WORKER = testOrcaSessionId('a0b1c2d3-0000-4000-8000-00000000abcd')
afterEach(() => {
structuredWorkerIdentities.clear()
})
describe("the CLI's own address", () => {
it("is a chat's session address, even beside a pane handle it inherited", () => {
const hostAddress = resolveOrcaSessionParty(CHAT, null).address
expect(injectedSessionAddress({ ORCA_AGENT_SESSION_ID: CHAT })).toBe(hostAddress)
expect(
injectedSessionAddress({
ORCA_AGENT_SESSION_ID: CHAT,
ORCA_TERMINAL_HANDLE: 'term_inherited'
})
).toBe(hostAddress)
})
it("is a structured worker's minted handle, as the host resolves it", () => {
const handle = mintStructuredWorkerHandle()
structuredWorkerIdentities.register({
handle,
sessionId: WORKER,
agent: 'claude',
paneKey: mintStructuredWorkerPaneKey(WORKER),
processIncarnation: structuredWorkerProcessIncarnation(WORKER),
worktreeId: 'wt_1',
hostScope: { kind: 'local', hostId: 'local' }
})
const hostAddress = resolveOrcaSessionParty(WORKER, null).address
expect(hostAddress).toBe(handle)
expect(
injectedSessionAddress({ ORCA_AGENT_SESSION_ID: WORKER, ORCA_TERMINAL_HANDLE: handle })
).toBe(hostAddress)
})
})
@@ -5,10 +5,7 @@ import { installFakeAppEnvironment } from '../../../config/scripts/vitest-host-p
const shim = vi.hoisted(() => ({ ensureLinuxTerminalOrcaCliShimDir: vi.fn() }))
vi.mock('../cli/linux-terminal-orca-cli-shim', () => shim)
import {
structuredSessionChildIdentityEnv,
withStructuredSessionTerminalViewEnv
} from './structured-session-child-identity-env'
import { structuredSessionChildIdentityEnv } from './structured-session-child-identity-env'
import {
mintStructuredWorkerHandle,
mintStructuredWorkerPaneKey,
@@ -163,19 +160,6 @@ describe('structuredSessionChildIdentityEnv', () => {
expect(console.warn).toHaveBeenCalledOnce()
})
it("gives the terminal view the same id, and leaves any other terminal's env as given", () => {
expect(withStructuredSessionTerminalViewEnv({ CLAUDE_CONFIG_DIR: '/c' }, SESSION_ID)).toEqual({
CLAUDE_CONFIG_DIR: '/c',
ORCA_AGENT_SESSION_ID: SESSION_ID
})
expect(withStructuredSessionTerminalViewEnv(undefined, SESSION_ID)).toEqual({
ORCA_AGENT_SESSION_ID: SESSION_ID
})
const plain = { CLAUDE_CONFIG_DIR: '/c' }
expect(withStructuredSessionTerminalViewEnv(plain, undefined)).toBe(plain)
expect(withStructuredSessionTerminalViewEnv(undefined, undefined)).toBeUndefined()
})
it('never puts a pane key in the child environment', () => {
// A pane key here flows into hook-emitted agent statuses and the attestation, agent-row and
// mobile-projection pipelines, all of which assume it names a live PTY leaf.
@@ -37,9 +37,7 @@
* `ORCA_STRUCTURED_SESSION` stays beside the id for a CLI that predates it — one reached through a
* global install when a shell rc resets PATH — which would otherwise guess a sibling's terminal;
* such a CLI refuses on the marker. A current CLI checks the id first, so the marker never makes a
* session with an id identity-less. The terminal view deliberately gets the id WITHOUT the marker:
* there an older CLI has the view's own pane handle and legitimately acts as that pane, and the
* marker would make it refuse its own pane.
* session with an id identity-less.
*
* The handle is read from the registry at spawn time, so an in-host recovery respawn re-bakes the
* SAME handle rather than a stale or fresh one.
@@ -66,20 +64,6 @@ export function structuredSessionChildIdentityEnv(
return env
}
/**
* The same session id for its terminal view, so switching views never changes who the session is.
* Same-host only, as above: the host refuses the claim from a terminal that runs in WSL or over SSH.
* A terminal that is not a session's view keeps its env exactly as given. No marker (see above), and
* no CLI command: the PTY lane names this app's launcher for every local terminal, and this env also
* crosses to SSH hosts, where a local path means nothing.
*/
export function withStructuredSessionTerminalViewEnv(
env: Record<string, string> | undefined,
sessionId: string | undefined
): Record<string, string> | undefined {
return sessionId ? { ...env, [ORCA_AGENT_SESSION_ID_ENV]: sessionId } : env
}
/**
* A host with no app environment installed — a plain-Node fork, or a unit test — has no userData
* root to resolve, and inventing one would write a shim into the wrong directory.
+24 -4
View File
@@ -1,13 +1,15 @@
/**
* The Orca-minted agent session id, injected into a structured session's own child processes, in
* native chat and in terminal view alike. When it is present it IS the orchestration caller: the
* CLI sends it in the orchestration envelope and the host resolves the session it names, so no
* terminal is resolved or guessed on its behalf.
* The Orca-minted agent session id, injected into a structured session's own child processes. When
* it is present it IS the orchestration caller: the CLI sends it in the orchestration envelope and
* the host resolves the session it names, so no terminal is resolved or guessed on its behalf.
*
* Identity by session id assumes one machine and one user. A host boundary (SSH, a paired peer,
* WSL) re-opens that decision: the host refuses a claim that arrives across one, and the SSH
* passthrough never carries the id.
*/
import { ORCA_SESSION_ADDRESS_PREFIX } from './orca-session-address-prefix'
import { isStructuredWorkerHandle } from './structured-worker-handle'
export const ORCA_AGENT_SESSION_ID_ENV = 'ORCA_AGENT_SESSION_ID'
export function readInjectedAgentSessionId(
@@ -16,3 +18,21 @@ export function readInjectedAgentSessionId(
const value = env[ORCA_AGENT_SESSION_ID_ENV]?.trim()
return value ? value : undefined
}
/**
* The address the host gives this session (`mailboxAddressOf` on its resolved party): a structured
* worker keeps the handle it was minted, any other session is `session:<id>`. Only for text that
* must match what the host writes; the CLI spells it without the host resolver.
*/
export function injectedSessionAddress(
env: Readonly<Record<string, string | undefined>> = process.env
): string | undefined {
const sessionId = readInjectedAgentSessionId(env)
if (!sessionId) {
return undefined
}
const ownHandle = env.ORCA_TERMINAL_HANDLE
return isStructuredWorkerHandle(ownHandle)
? ownHandle
: `${ORCA_SESSION_ADDRESS_PREFIX}${sessionId}`
}