feat(web): surface and configure composer steering
This commit is contained in:
@@ -379,7 +379,7 @@ describe('session/queue frames', () => {
|
||||
|
||||
const liveFrames = (await collected).filter(frame => frame.type === 'session/queue')
|
||||
expect(liveFrames.map(frame => frame.items)).toEqual([
|
||||
[{ id: edited.id, message: edited.message }],
|
||||
[{ id: edited.id, placement: edited.placement, message: edited.message }],
|
||||
])
|
||||
const replay = new AbortController()
|
||||
const replayFrames = await collect<MuxFrame>(
|
||||
@@ -393,8 +393,8 @@ describe('session/queue frames', () => {
|
||||
const agent = stubAgent(ctx)
|
||||
const live = new AbortController()
|
||||
const liveStream = api.events.mux({ rpcId: RpcId('t-mux-live'), payload: {} }, live.signal)
|
||||
// subscribed baseline + one queued snapshot; pending steering stays off this wire.
|
||||
const liveCollected = collect<MuxFrame>(liveStream, 2, live)
|
||||
// subscribed baseline + one snapshot per accepted inbox occurrence.
|
||||
const liveCollected = collect<MuxFrame>(liveStream, 3, live)
|
||||
|
||||
const queued = inboxItem('i-1', inboxMessage('m-1', 'queued prompt'), 'queued')
|
||||
const steering = inboxItem('i-2', inboxMessage('m-2', 'steering prompt'), 'steering')
|
||||
@@ -406,7 +406,15 @@ describe('session/queue frames', () => {
|
||||
{
|
||||
type: 'session/queue',
|
||||
sessionId: agent.id,
|
||||
items: [{ id: queued.id, message: queued.message }],
|
||||
items: [{ id: queued.id, placement: 'queued', message: queued.message }],
|
||||
},
|
||||
{
|
||||
type: 'session/queue',
|
||||
sessionId: agent.id,
|
||||
items: [
|
||||
{ id: queued.id, placement: 'queued', message: queued.message },
|
||||
{ id: steering.id, placement: 'steering', message: steering.message },
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
@@ -414,7 +422,88 @@ describe('session/queue frames', () => {
|
||||
const replay = new AbortController()
|
||||
const replayFrames = await collect<MuxFrame>(
|
||||
api.events.mux({ rpcId: RpcId('t-mux-replay'), payload: {} }, replay.signal), 2, replay)
|
||||
expect(replayFrames.filter(f => f.type === 'session/queue')).toEqual([liveFrames[0]])
|
||||
expect(replayFrames.filter(f => f.type === 'session/queue')).toEqual([liveFrames[1]])
|
||||
})
|
||||
|
||||
it('publishes the durable steering event before retiring its transient row', async () => {
|
||||
const ctx = await harness()
|
||||
const api = createApiProxy(ctx, DEFAULTS)
|
||||
const agent = stubAgent(ctx)
|
||||
const abort = new AbortController()
|
||||
const collected = collect<MuxFrame>(
|
||||
api.events.mux({ rpcId: RpcId('t-steering-order'), payload: {} }, abort.signal), 5, abort)
|
||||
const steering = inboxItem('i-steering', inboxMessage('m-steering', 'interrupt now'), 'steering')
|
||||
|
||||
agent.session.append('turn/start', {
|
||||
turn: 1,
|
||||
trigger: { kind: 'message', source: { kind: 'user' } },
|
||||
})
|
||||
ctx.emit('agent/inbox/enqueue', agent, steering)
|
||||
ctx.emit('agent/inbox/dequeue', agent, steering)
|
||||
agent.session.append('steering/message', {
|
||||
turn: 1,
|
||||
message: steering.message,
|
||||
}, { surfaceOp: 'append' })
|
||||
|
||||
const frames = await collected
|
||||
expect(frames.map(frame => frame.type)).toEqual([
|
||||
'session/subscribed',
|
||||
'session/event',
|
||||
'session/queue',
|
||||
'session/event',
|
||||
'session/queue',
|
||||
])
|
||||
expect(frames[2]).toMatchObject({
|
||||
type: 'session/queue',
|
||||
items: [{ id: steering.id, placement: 'steering' }],
|
||||
})
|
||||
expect(frames[3]).toMatchObject({
|
||||
type: 'session/event',
|
||||
event: { type: 'steering/message', data: { message: { id: steering.message.id } } },
|
||||
})
|
||||
expect(frames[4]).toMatchObject({ type: 'session/queue', items: [] })
|
||||
})
|
||||
|
||||
it('retains claimed steering in re-entrant snapshots until its durable event', async () => {
|
||||
const ctx = await harness()
|
||||
const api = createApiProxy(ctx, DEFAULTS)
|
||||
const agent = stubAgent(ctx)
|
||||
const steering = inboxItem('i-steering', inboxMessage('m-steering', 'interrupt now'), 'steering')
|
||||
const queued = inboxItem('i-reentrant', inboxMessage('m-reentrant', 'later'), 'queued')
|
||||
ctx.on('agent/inbox/dequeue', (subject, item) => {
|
||||
if (subject === agent && item.id === steering.id) ctx.emit('agent/inbox/enqueue', agent, queued)
|
||||
})
|
||||
const abort = new AbortController()
|
||||
const collected = collect<MuxFrame>(
|
||||
api.events.mux({ rpcId: RpcId('t-steering-reentrant-order'), payload: {} }, abort.signal), 6, abort)
|
||||
|
||||
agent.session.append('turn/start', {
|
||||
turn: 1,
|
||||
trigger: { kind: 'message', source: { kind: 'user' } },
|
||||
})
|
||||
ctx.emit('agent/inbox/enqueue', agent, steering)
|
||||
ctx.emit('agent/inbox/dequeue', agent, steering)
|
||||
agent.session.append('steering/message', {
|
||||
turn: 1,
|
||||
message: steering.message,
|
||||
}, { surfaceOp: 'append' })
|
||||
|
||||
const frames = await collected
|
||||
expect(frames[3]).toMatchObject({
|
||||
type: 'session/queue',
|
||||
items: [
|
||||
{ id: steering.id, placement: 'steering' },
|
||||
{ id: queued.id, placement: 'queued' },
|
||||
],
|
||||
})
|
||||
expect(frames[4]).toMatchObject({
|
||||
type: 'session/event',
|
||||
event: { type: 'steering/message', data: { message: { id: steering.message.id } } },
|
||||
})
|
||||
expect(frames[5]).toMatchObject({
|
||||
type: 'session/queue',
|
||||
items: [{ id: queued.id, placement: 'queued' }],
|
||||
})
|
||||
})
|
||||
|
||||
it('publishes edits in place in the authoritative order', async () => {
|
||||
@@ -434,10 +523,16 @@ describe('session/queue frames', () => {
|
||||
|
||||
const frames = (await collected).filter(frame => frame.type === 'session/queue')
|
||||
expect(frames.map(frame => frame.items)).toEqual([
|
||||
[{ id: first.id, message: first.message }],
|
||||
[{ id: first.id, message: first.message }, { id: second.id, message: second.message }],
|
||||
[{ id: first.id, message: first.message }, { id: edited.id, message: edited.message }],
|
||||
[{ id: first.id, message: first.message }],
|
||||
[{ id: first.id, placement: first.placement, message: first.message }],
|
||||
[
|
||||
{ id: first.id, placement: first.placement, message: first.message },
|
||||
{ id: second.id, placement: second.placement, message: second.message },
|
||||
],
|
||||
[
|
||||
{ id: first.id, placement: first.placement, message: first.message },
|
||||
{ id: edited.id, placement: edited.placement, message: edited.message },
|
||||
],
|
||||
[{ id: first.id, placement: first.placement, message: first.message }],
|
||||
])
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user