mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 08:37:49 +00:00
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:
@@ -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([])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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' },
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user