test(runtime): pin the empty agentArgs override at the wire boundary

The PR's launch tests mock web-runtime-session wholesale, so nothing
caught a future `args.agentArgs || args.launchConfig?.agentArgs`
refactor silently dropping Manual's empty string. Assert the exact
terminal.createAgentSession params instead. Also trims the new WHY
comments to repo length and notes why the queued local startup keeps
using the raw param.
This commit is contained in:
Neil
2026-08-21 22:59:59 -07:00
parent ed510f01af
commit 4932ed84a7
3 changed files with 63 additions and 7 deletions
@@ -93,8 +93,7 @@ describe('launchAgentInNewTab paired web runtime', () => {
activate: true,
agentSessionKind: 'fresh',
agent: 'claude',
// Why: an unconfigured client still resolves the default explicitly, so
// the host applies this client's launch defaults rather than its own.
// Why: the client resolves its default explicitly so the host cannot apply its own.
agentArgs: '--dangerously-skip-permissions',
viewMode: 'terminal'
})
@@ -157,11 +157,8 @@ export function launchAgentInNewTab(args: LaunchAgentInNewTabArgs): LaunchAgentI
promptDelivery,
pastePromptAfterReady: pasteDraftAfterLaunch,
submitPastedPrompt,
// Why: the raw param is undefined on ordinary UI launches, and omitting it
// lets a headless host apply its own defaults — so a client configured for
// Manual gets the host's YOLO default (#15373). Send the client-resolved
// value, empty string included: the host treats present-but-empty as an
// explicit no-args override.
// Why: the raw param is undefined on ordinary UI launches, so omission let a headless host apply its
// own YOLO default (#15373). Send the resolved value — present-but-empty is an explicit no-args override.
agentArgs: effectiveAgentArgs,
// Why: omission means terminal locally, but would let a paired host apply
// its own default; send the client's resolved terminal choice explicitly.
@@ -195,6 +192,7 @@ export function launchAgentInNewTab(args: LaunchAgentInNewTabArgs): LaunchAgentI
...(startupPlan.env ? { env: startupPlan.env } : {}),
launchConfig: startupPlan.launchConfig,
launchAgent: agent,
// Why: the local launch command already bakes in the resolved args, so this override only records explicit caller intent.
...(agentArgs !== undefined ? { agentArgsOverride: agentArgs } : {}),
...(startupPlan.sessionOptions ? { sessionOptions: startupPlan.sessionOptions } : {}),
...(startupPlan.startupCommandDelivery
@@ -325,6 +325,65 @@ describe('createWebRuntimeSessionTerminal', () => {
)
})
it('keeps an explicit empty agentArgs in the structured create payload', async () => {
// Why (#15373): Manual is stored as an empty string; dropping it here would let the host re-resolve YOLO.
const runtimeCall = vi.fn(async (request: { method: string }) => {
if (request.method === 'status.get') {
return {
id: 'status',
ok: true,
result: {
runtimeId: 'runtime-1',
graphStatus: 'ready',
runtimeProtocolVersion: 3,
minCompatibleRuntimeClientVersion: 2,
capabilities: ['agent-session.host-authority.v1']
}
}
}
if (request.method === 'terminal.createAgentSession') {
return {
id: 'agent-session',
ok: true,
result: {
terminal: {
handle: 'term-manual',
worktreeId: WORKTREE_ID,
tabId: 'host-tab-manual',
paneKey: `host-tab-manual:${FOCUS_LEAF_ID}`
},
disposition: 'created'
}
}
}
return { id: 'list', ok: true, result: makeSnapshot() }
})
vi.stubGlobal('window', {
api: { runtimeEnvironments: { call: runtimeCall } }
})
await expect(
createWebRuntimeSessionTerminal({
worktreeId: WORKTREE_ID,
agent: 'claude',
agentSessionKind: 'fresh',
agentArgs: '',
activate: true
})
).resolves.toEqual({ status: 'created' })
const createRequest = runtimeCall.mock.calls.find(
([request]) => request.method === 'terminal.createAgentSession'
)?.[0] as { params: Record<string, unknown> } | undefined
expect(createRequest?.params).toEqual({
clientOperationId: expect.stringMatching(/^\d{13}-[0-9a-f]{32}$/),
worktree: `id:${WORKTREE_ID}`,
agent: 'claude',
agentArgs: '',
presentation: 'background'
})
})
it.each(['session.tabs.move', 'session.tabs.list'] as const)(
'treats %s failure after host creation as accepted so callers do not duplicate the agent',
async (failedMethod) => {