Declare agentSession.requestHandoff on the cross-version wire surface

The manifest is a ratchet for cross-version reachability, so the method is
declared with real HandoffParams rather than counted. requestHandoff is
capability-gated through requireStructuredHost and has no client caller, so
declaring it is the whole of the change.

Also model two host capabilities the harness omitted: the stub host's
supportsCreate, and the fake adapter's, without which adapterSupportsCreate
falls through to a supportsLocation the fake also lacks. Every ensure was
refused for the harness's silence rather than for its location.
This commit is contained in:
Merge Sim
2026-09-03 20:55:31 -07:00
parent ab00a5a9f3
commit 1ffaed8d56
@@ -77,6 +77,11 @@ const STRUCTURED_CALLS: {
hostMethod: 'setOption',
result: { ok: true, replayed: false }
},
{
method: 'agentSession.requestHandoff',
hostMethod: 'requestHandoff',
result: { status: { owner: 'native' } }
},
{
method: 'agentSession.handoffStatus',
hostMethod: 'handoffStatus',
@@ -197,6 +202,14 @@ function paramsFor(method: string): unknown {
const fields = { itemId: 'item-1', expectedRevision: 1, optionId: 'allow' }
return { envelope: envelope({ method, fields, fence }), ...fields }
}
case 'agentSession.requestHandoff': {
const fields = {
direction: 'to-tui' as const,
mode: 'now' as const,
action: 'start' as const
}
return { envelope: envelope({ method, fields, fence }), ...fields }
}
case 'agentSession.setOption': {
const fields = { key: 'model', value: 'gpt-5' }
return { envelope: envelope({ method, fields, fence }), ...fields }
@@ -286,6 +299,10 @@ async function callBuild(
function structuredHostStub(): Record<string, ReturnType<typeof vi.fn>> {
return {
attach: vi.fn(async () => ({ ok: true, replayed: false, value: { sessionId: SESSION } })),
// Attach-shaped entries take a client-supplied location, so the host is asked whether it
// supports creating there. A real host always answers; leaving it unstubbed made every
// `ensure` refuse for the harness's own reason rather than the location's.
supportsCreate: vi.fn(() => true),
send: vi.fn(async () => ({ ok: true, replayed: false })),
cancel: vi.fn(async () => ({ ok: true, replayed: false })),
close: vi.fn(async () => undefined),
@@ -723,6 +740,10 @@ describe('cross-version structured agent sessions', () => {
/** Phase 2 owns provider processes; the adapter is the only stub here. */
function adapter(): StructuredAgentSessionAdapter {
return {
// Every real adapter answers this; without it adapterSupportsCreate falls through to
// `supportsLocation`, which this fake also lacks, so the client-supplied-location gate
// refused for the fake's silence rather than for the location.
supportsCreate: () => true,
acquire: async ({ fence }) => ({
process: {
hostId: 'local',