refactor(agent-launch): drop the unshipped viewMode field and trusted local caller id

Both were inert in step 1 and existed only for step 2. agent.launch will become a
public plugin API, so every wire field is permanent once shipped; a top-level
viewMode reads as "choose terminal vs chat", which the host decides. Step 2
introduces placement and view intent under a placement object instead.
This commit is contained in:
Brennan Benson
2026-10-01 19:07:08 -07:00
parent 8dc5516836
commit aa5db2d9dc
15 changed files with 2 additions and 108 deletions
@@ -337,8 +337,7 @@ async function createTerminalSurface(
worktreeId,
agent: intent.agent,
...(startupPrompt ? { startupPrompt } : {}),
...terminalLaunchInputs(intent),
...(intent.viewMode ? { viewMode: intent.viewMode } : {})
...terminalLaunchInputs(intent)
})
return {
outcome: {
@@ -35,8 +35,6 @@ export type AgentLaunchSurfaceFactory = {
launchSource?: string
/** The caller-minted pane to create; refused with `AgentLaunchPaneAlreadyLiveError` if live. */
paneKey?: string
/** How the tab first shows, as the caller would have opened it. */
viewMode?: 'terminal' | 'chat'
}): Promise<{
handle: string
/** The pane this create minted; a factory whose runtime reports none omits it, never invents. */
@@ -12,8 +12,6 @@ import { AGENT_LAUNCH_RUNTIME_CAPABILITY } from '../../shared/agent-launch-runti
type AdvertisedClient = {
clientKind?: 'mobile' | 'runtime'
clientCapabilities?: readonly RuntimeCapability[]
trustedLocalCallerId?: string
pairedDeviceId?: string
}
const { handlers, advertised } = vi.hoisted(
@@ -59,7 +57,6 @@ vi.mock('../runtime/rpc/dispatcher', () => ({
const { registerRuntimeHandlers } = await import('./runtime')
const { supportsAgentLaunch } = await import('../runtime/rpc/methods/agent-launch')
const { agentLaunchOperationCallerKey } = await import('../runtime/rpc/methods/agent-launch-replay')
function rendererEvent() {
const mainFrame = {}
@@ -129,20 +126,4 @@ describe('desktop renderer reaching agent.launch on its own main process', () =>
)
expect(supportsAgentLaunch(streaming)).toBe(true)
})
// Why: the desktop has no paired device, so without its own host-set identity every
// replay-protected launch it sent was refused with agent_session_identity_required.
it('gives a replay-protected launch a caller identity of its own', () => {
invoke('runtime:call', {
method: 'agent.launchReplay',
params: { agent: 'claude', target: { kind: 'existing', worktree: 'id:wt-7' } }
})
const client = onlyAdvertisedClient(advertised.unary)
expect(agentLaunchOperationCallerKey(client)).toBe('trusted-local:desktop-renderer')
// Negative control: the same client kind with no host-set identity is still refused.
expect(() =>
agentLaunchOperationCallerKey({ clientKind: client.clientKind, pairedDeviceId: undefined })
).toThrow('agent_session_identity_required')
})
})
-2
View File
@@ -78,7 +78,6 @@ export function registerRuntimeHandlers(runtime: OrcaRuntimeService): void {
},
{
clientId: 'desktop-renderer',
trustedLocalCallerId: 'desktop-renderer',
clientKind: 'runtime',
connectionId: desktopSenders.connectionIdFor(event.sender),
clientCapabilities: DESKTOP_RENDERER_RUNTIME_CLIENT_CAPABILITIES
@@ -121,7 +120,6 @@ export function registerRuntimeHandlers(runtime: OrcaRuntimeService): void {
{
signal: controller.signal,
clientId: 'desktop-renderer',
trustedLocalCallerId: 'desktop-renderer',
clientKind: 'runtime',
connectionId,
clientCapabilities: DESKTOP_RENDERER_RUNTIME_CLIENT_CAPABILITIES
-3
View File
@@ -75,9 +75,6 @@ export type RpcContext = {
clientId?: string
// Why: navigation is keyed by revocable device identity, never by the bearer credential or transient socket id.
pairedDeviceId?: string
// Why: an in-process caller (the desktop renderer over IPC) has no paired device, yet its identity
// is fixed by the host, so replay-protected operations can key on it.
trustedLocalCallerId?: string
// Why: lets handlers gate mobile payload truncation to phones only; undefined for in-process callers → treat as full-class (no clip).
clientKind?: 'mobile' | 'runtime'
// Why: negotiation is bound to the authenticated socket, never asserted by a destructive request.
@@ -8,8 +8,6 @@ export type RpcDispatchStreamingOptions = {
signal?: AbortSignal
clientId?: string
pairedDeviceId?: string
/** Set only by an in-process transport (desktop IPC); never derived from anything a peer sends. */
trustedLocalCallerId?: string
clientKind?: 'mobile' | 'runtime'
clientCapabilities?: readonly RuntimeCapability[]
updateClientCapabilities?: (capabilities: readonly RuntimeCapability[]) => void
@@ -1,47 +0,0 @@
import { describe, expect, it } from 'vitest'
import { z } from 'zod'
import { RpcDispatcher } from './dispatcher'
import { defineMethod, type RpcRequest } from './core'
import type { OrcaRuntimeService } from '../orca-runtime'
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the dispatcher reads only getRuntimeId on this path; any other runtime member it touched would throw rather than read a wrong value.
const RUNTIME = { getRuntimeId: () => 'test-runtime' } as OrcaRuntimeService
const INSPECT = defineMethod({
name: 'test.inspectCaller',
params: z.object({}),
handler: (_params, { trustedLocalCallerId }) => ({
trustedLocalCallerId: trustedLocalCallerId ?? null
})
})
function request(): RpcRequest {
return { id: 'req-1', authToken: 'tok', method: 'test.inspectCaller', params: {} }
}
describe('the in-process caller identity a transport vouches for', () => {
it('reaches a unary handler from the transport that set it', async () => {
const dispatcher = new RpcDispatcher({ runtime: RUNTIME, methods: [INSPECT] })
const response = await dispatcher.dispatch(request(), {
clientKind: 'runtime',
trustedLocalCallerId: 'desktop-renderer'
})
expect(response).toMatchObject({
ok: true,
result: { trustedLocalCallerId: 'desktop-renderer' }
})
})
it('is absent for a transport that set none, whatever the request carries', async () => {
const dispatcher = new RpcDispatcher({ runtime: RUNTIME, methods: [INSPECT] })
const response = await dispatcher.dispatch(
{ ...request(), params: { trustedLocalCallerId: 'desktop-renderer' } },
{ clientKind: 'runtime' }
)
expect(response).toMatchObject({ ok: true, result: { trustedLocalCallerId: null } })
})
})
-1
View File
@@ -116,7 +116,6 @@ export class RpcDispatcher {
: undefined,
requestId: request.id,
clientId: options?.clientId,
trustedLocalCallerId: options?.trustedLocalCallerId,
clientKind: options?.clientKind,
clientCapabilities: options?.clientCapabilities,
updateClientCapabilities: options?.updateClientCapabilities,
@@ -29,16 +29,11 @@ import type { AgentLaunchParams } from './agent-launch-schemas'
/** Remote replay needs the paired-device subject because its bearer credential can rotate. */
export function agentLaunchOperationCallerKey(
context: Pick<RpcContext, 'pairedDeviceId' | 'clientKind' | 'trustedLocalCallerId'>
context: Pick<RpcContext, 'pairedDeviceId' | 'clientKind'>
): string {
if (context.clientKind === undefined) {
return 'trusted-local:runtime'
}
// The desktop renderer is in-process like the CLI, but advertises capabilities as a client.
const trustedLocalCallerId = context.trustedLocalCallerId?.trim()
if (trustedLocalCallerId) {
return `trusted-local:${trustedLocalCallerId}`
}
const pairedDeviceId = context.pairedDeviceId?.trim()
if (!pairedDeviceId) {
throw new Error('agent_session_identity_required')
@@ -118,7 +118,6 @@ export function agentLaunchSurfaceFactory(
cwd,
launchSource,
paneKey,
viewMode,
options
}) => {
const launchPreferences = toAgentLaunchPreferences(options)
@@ -144,8 +143,6 @@ export function agentLaunchSurfaceFactory(
// A live reserved pane would be attached, not launched into, so the runtime refuses it.
...(paneKey ? { ...paneIdentity(paneKey), requireFreshPane: true } : {}),
...(launchSource ? { launchSource } : {}),
// Not `presentation`: a focused create takes the renderer path, which drops the pane identity.
...(viewMode ? { viewMode } : {}),
onPtySpawnDispatched: terminalSpawn.onPtySpawnDispatched
})
const terminal = await created.catch(terminalSpawn.rethrow)
@@ -80,16 +80,6 @@ describe('a terminal launch into an existing workspace', () => {
expect(terminalOptions(runtime)).toMatchObject({ launchPreferences: PICKS })
})
it('opens the tab in the view mode the caller asked for, without asking for a focused create', async () => {
const runtime = runtimeStub({ settings: TERMINAL_ONLY })
await launch({ ...EXISTING_LAUNCH, viewMode: 'chat' }, runtime)
// A focused create takes the renderer path, which drops the caller's pane identity.
expect(terminalOptions(runtime)).toMatchObject({ viewMode: 'chat' })
expect(terminalOptions(runtime)).not.toHaveProperty('presentation')
})
it('drops options that are not launch preferences', async () => {
const runtime = runtimeStub({ settings: TERMINAL_ONLY })
@@ -105,7 +105,6 @@ async function agentLaunchIntent(
...(params.cwd ? { cwd: params.cwd } : {}),
...(params.launchSource ? { launchSource: params.launchSource } : {}),
...(params.paneKey ? { paneKey: params.paneKey } : {}),
...(params.viewMode ? { viewMode: params.viewMode } : {}),
...(params.sessionId ? { sessionId: params.sessionId } : {})
}
}
@@ -138,7 +138,6 @@ export class RpcStreamingDispatcher {
subscriptionRegistrationVersion,
clientId: options?.clientId,
pairedDeviceId: options?.pairedDeviceId,
trustedLocalCallerId: options?.trustedLocalCallerId,
clientKind: options?.clientKind,
clientCapabilities: options?.clientCapabilities,
updateClientCapabilities: options?.updateClientCapabilities,
@@ -193,7 +192,6 @@ export class RpcStreamingDispatcher {
connectionId: options?.connectionId,
clientId: options?.clientId,
pairedDeviceId: options?.pairedDeviceId,
trustedLocalCallerId: options?.trustedLocalCallerId,
clientKind: options?.clientKind,
clientCapabilities: options?.clientCapabilities,
updateClientCapabilities: options?.updateClientCapabilities,
-2
View File
@@ -93,8 +93,6 @@ export type AgentLaunchIntent = {
/** The `tabId:leafId` a terminal launch creates its pane under, for a caller that places its own
* tabs. Not a route input; refused when that pane is already live. */
paneKey?: string
/** How a terminal launched into an existing workspace first shows its tab. Not a route input. */
viewMode?: 'terminal' | 'chat'
/** The caller-minted id of the chat session a structured launch creates. Not a route input;
* refused when that session already exists. */
sessionId?: string
@@ -102,12 +102,6 @@ export const AgentLaunchFields = z.object({
)
}, 'Malformed launch pane key')
.optional(),
/**
* How a terminal launch in an existing workspace first shows its tab, so the tab opens as the
* caller would have opened it. Presentation only; a chat and a new workspace's startup terminal
* ignore it, and an older host strips it.
*/
viewMode: z.enum(['terminal', 'chat']).optional(),
/**
* The id of the chat session a structured launch creates, minted by the caller so it knows which
* conversation it started before the reply arrives. Refused when a session with this id already