fix(apiproxy): derive queued steering on the client, drop it from the wire
The session/queued frame no longer carries steering — AgentMessage no longer has the field. The client derives it from the same ordered turn boundaries the host saw (a frame arriving while a turn is open joined the steering FIFO).
This commit is contained in:
@@ -238,14 +238,11 @@ describe('host/commands-changed frame', () => {
|
||||
})
|
||||
|
||||
/** Build one frozen inbox message for the live `agent/inbox/*` events. */
|
||||
function inboxMessage(id: string, text: string, steering: boolean, rpcId?: string): AgentMessage {
|
||||
function inboxMessage(id: string, text: string, rpcId?: string): AgentMessage {
|
||||
return Object.freeze({
|
||||
id: AgentMessageId(id),
|
||||
content: [{ type: 'text' as const, text }],
|
||||
source: rpcId === undefined ? { kind: 'user' as const } : { kind: 'user' as const, rpcId: RpcId(rpcId) },
|
||||
contexts: [],
|
||||
steering,
|
||||
wakeup: true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -259,15 +256,15 @@ describe('session/queued frames', () => {
|
||||
// subscribed baseline + 2 queued frames
|
||||
const liveCollected = collect<MuxFrame>(liveStream, 3, live)
|
||||
|
||||
const queued = inboxMessage('m-1', 'queued prompt', false)
|
||||
const steering = inboxMessage('m-2', 'queued prompt', true)
|
||||
const queued = inboxMessage('m-1', 'queued prompt')
|
||||
const steering = inboxMessage('m-2', 'queued prompt')
|
||||
ctx.emit('agent/inbox/enqueue', agent, queued)
|
||||
ctx.emit('agent/inbox/enqueue', agent, steering)
|
||||
|
||||
const liveFrames = (await liveCollected).filter(f => f.type === 'session/queued')
|
||||
expect(liveFrames).toEqual([
|
||||
{ type: 'session/queued', sessionId: agent.id, content: queued.content, source: { kind: 'user' }, steering: false },
|
||||
{ type: 'session/queued', sessionId: agent.id, content: steering.content, source: { kind: 'user' }, steering: true },
|
||||
{ type: 'session/queued', sessionId: agent.id, content: queued.content, source: { kind: 'user' } },
|
||||
{ type: 'session/queued', sessionId: agent.id, content: steering.content, source: { kind: 'user' } },
|
||||
])
|
||||
|
||||
// A fresh mux connection replays the still-pending entries as its baseline.
|
||||
@@ -281,8 +278,8 @@ describe('session/queued frames', () => {
|
||||
const ctx = await harness()
|
||||
const api = createApiProxy(ctx, DEFAULTS)
|
||||
const agent = stubAgent(ctx)
|
||||
const queued = inboxMessage('m-3', 'x', false)
|
||||
const steering = inboxMessage('m-4', 'x', true, 'r-1')
|
||||
const queued = inboxMessage('m-3', 'x')
|
||||
const steering = inboxMessage('m-4', 'x', 'r-1')
|
||||
ctx.emit('agent/inbox/enqueue', agent, queued)
|
||||
ctx.emit('agent/inbox/enqueue', agent, steering)
|
||||
ctx.emit('agent/inbox/dequeue', agent, queued)
|
||||
@@ -298,8 +295,8 @@ describe('session/queued frames', () => {
|
||||
const ctx = await harness()
|
||||
const api = createApiProxy(ctx, DEFAULTS)
|
||||
const agent = stubAgent(ctx)
|
||||
const doomed = inboxMessage('m-5', 'doomed', false)
|
||||
const survivor = inboxMessage('m-6', 'survivor', false)
|
||||
const doomed = inboxMessage('m-5', 'doomed')
|
||||
const survivor = inboxMessage('m-6', 'survivor')
|
||||
ctx.emit('agent/inbox/enqueue', agent, doomed)
|
||||
ctx.emit('agent/inbox/enqueue', agent, survivor)
|
||||
ctx.emit('agent/inbox/discard', agent, [doomed])
|
||||
|
||||
@@ -240,8 +240,8 @@ describe('events frame schemas', () => {
|
||||
{ type: 'approval/resolved', sessionId: 's', approvalId: 'a', outcome: 'allowed-once' },
|
||||
{ type: 'question/requested', sessionId: 's', questions: [{ id: 'q', question: 'Q?', options: [{ label: 'L' }], multiSelect: true }] },
|
||||
{ type: 'question/resolved', sessionId: 's', questionRpcId: 'r', outcome: 'answered' },
|
||||
{ type: 'session/queued', sessionId: 's', content: [{ type: 'text', text: 'queued prompt' }], source: { kind: 'user', rpcId: 'r9' }, steering: false },
|
||||
{ type: 'session/queued', sessionId: 's', content: [{ type: 'text', text: 'steer' }], source: { kind: 'user' }, steering: true },
|
||||
{ type: 'session/queued', sessionId: 's', content: [{ type: 'text', text: 'queued prompt' }], source: { kind: 'user', rpcId: 'r9' } },
|
||||
{ type: 'session/queued', sessionId: 's', content: [{ type: 'text', text: 'steer' }], source: { kind: 'user' } },
|
||||
{ type: 'stream/error', error: { code: 'internal', message: 'm', details: {} } },
|
||||
]
|
||||
for (const frame of frames) expect(muxFrameSchema.parse(frame)).toMatchObject({ type: frame.type })
|
||||
@@ -261,9 +261,8 @@ describe('events frame schemas', () => {
|
||||
})
|
||||
|
||||
it('rejects a queued frame missing its members', () => {
|
||||
expect(() => muxFrameSchema.parse({ type: 'session/queued', sessionId: 's', content: [{ type: 'text' }], source: { kind: 'user' } })).toThrow()
|
||||
expect(() => muxFrameSchema.parse({ type: 'session/queued', sessionId: 's', content: 'x', source: { kind: 'user' }, steering: false })).toThrow()
|
||||
expect(() => muxFrameSchema.parse({ type: 'session/queued', sessionId: 's', content: [], source: {}, steering: false })).toThrow()
|
||||
expect(() => muxFrameSchema.parse({ type: 'session/queued', sessionId: 's', content: 'x', source: { kind: 'user' } })).toThrow()
|
||||
expect(() => muxFrameSchema.parse({ type: 'session/queued', sessionId: 's', content: [], source: {} })).toThrow()
|
||||
})
|
||||
|
||||
it('accepts every host frame branch', () => {
|
||||
|
||||
Reference in New Issue
Block a user