refactor(agent-loop): drop the steering/message session event
Steer, inject, and followup now land as durable user/message events on the session surface; the steering/message event type and its ConversationNode kind are removed from the client projection. Update tests, docs, generated catalogs, and agent notes to match, and align the steering e2e fixture and prompt inventory assertions with the durable user/message landing.
This commit is contained in:
@@ -339,7 +339,7 @@ function fixtureUsage(turn: number, step: number): TokenUsage {
|
||||
}
|
||||
|
||||
/** fx-alpha history script: 60 turns (~130+ messages -> 3 pages at PAGE_MESSAGES=50),
|
||||
* mixing reasoning blocks / tool call+result / steering / context. */
|
||||
* mixing reasoning blocks / tool call+result / context. */
|
||||
function buildAlphaLog(): SessionEvent[] {
|
||||
const events: Record<string, unknown>[] = []
|
||||
let time = Date.now() - 3_600_000
|
||||
@@ -393,9 +393,6 @@ function buildAlphaLog(): SessionEvent[] {
|
||||
push({ type: 'assistant/message', surfaceOp: 'append', data: { turn, step: 0, message: assistantMessage(blocks) } })
|
||||
push({ type: 'step/end', data: { turn, step: 0 } })
|
||||
}
|
||||
if (turn % 13 === 6) {
|
||||
push({ type: 'steering/message', surfaceOp: 'append', data: { turn, message: userMessage(text(`插话 ${turn}:fixture steering 消息。`)) } })
|
||||
}
|
||||
push({ type: 'turn/end', data: { turn, reason: { kind: 'completed' } } })
|
||||
}
|
||||
// Three view-sample turns (60-62) cover the built-in card types. The real filesystem names in
|
||||
@@ -957,7 +954,7 @@ function pageOf(
|
||||
const event = log[i]
|
||||
/* v8 ignore next -- dense-array guard: log seqs are array indexes, i stays within [0, end). */
|
||||
if (event === undefined) break
|
||||
if (event.type === 'user/message' || event.type === 'assistant/message' || event.type === 'steering/message') messages++
|
||||
if (event.type === 'user/message' || event.type === 'assistant/message') messages++
|
||||
if (event.type === 'turn/start' && messages >= maxMessages) {
|
||||
start = i
|
||||
break
|
||||
@@ -986,11 +983,11 @@ function searchBlockText(block: ContentBlock): string[] {
|
||||
}
|
||||
}
|
||||
|
||||
/** One current-surface user/assistant/steering document, if searchable. */
|
||||
/** One current-surface user/assistant document, if searchable. */
|
||||
function searchEventText(event: SessionEvent): string {
|
||||
const content = event.type === 'user/message'
|
||||
? event.data.content
|
||||
: event.type === 'assistant/message' || event.type === 'steering/message'
|
||||
: event.type === 'assistant/message'
|
||||
? event.data.message.content
|
||||
: undefined
|
||||
if (content === undefined) return ''
|
||||
@@ -1833,10 +1830,8 @@ export function createFixtureApi(options: FixtureOptions = {}): ApiProxy {
|
||||
summary.blank = false
|
||||
const userText = content.map(b => (b.type === 'text' ? b.text : '')).join('')
|
||||
if (mode === 'steer' && replays.has(id)) {
|
||||
// Steering: insert a steering message into the current turn; the replay continues.
|
||||
/* v8 ignore next -- the ?? arm needs a missing counter, but a live replay implies a prior prompt already set it. */
|
||||
const turn = (nextTurn.get(id) ?? 1) - 1
|
||||
append(id, { type: 'steering/message', surfaceOp: 'append', data: { turn, message: userMessage(content) } })
|
||||
// Steering: the durable user/message lands inside the current turn; the replay continues.
|
||||
append(id, { type: 'user/message', surfaceOp: 'append', data: userMessage(content) })
|
||||
return ok(request, { accepted: true as const })
|
||||
}
|
||||
const turn = nextTurn.get(id) ?? 0
|
||||
|
||||
@@ -301,7 +301,7 @@ describe('createFixtureApi', () => {
|
||||
expect(idleCancel.result).toMatchObject({ ok: true })
|
||||
})
|
||||
|
||||
it('steer during a replay inserts a steering message and the replay continues to completion', async () => {
|
||||
it('steer during a replay lands a user/message inside the current turn and the replay continues', async () => {
|
||||
const api = createFixtureApi()
|
||||
const created = await api.sessions.create(req({}))
|
||||
if (!created.result.ok) throw new Error('create failed')
|
||||
@@ -314,7 +314,7 @@ describe('createFixtureApi', () => {
|
||||
await api.sessions.prompt(req({ sessionId: id, mode: 'steer' as const, content: [{ type: 'text' as const, text: '插话' }] }))
|
||||
const frames = await framesPromise
|
||||
const types = frames.filter((f): f is Extract<MuxFrame, { type: 'session/event' }> => f.type === 'session/event').map(f => f.event.type)
|
||||
expect(types).toContain('steering/message')
|
||||
expect(JSON.stringify(frames)).toContain('插话')
|
||||
expect(types.at(-1)).toBe('turn/end') // steer did not restart the turn
|
||||
})
|
||||
|
||||
@@ -362,7 +362,7 @@ describe('createFixtureApi', () => {
|
||||
}))
|
||||
const frames = await framesPromise
|
||||
const types = frames.filter((f): f is Extract<MuxFrame, { type: 'session/event' }> => f.type === 'session/event').map(f => f.event.type)
|
||||
expect(types[0]).toBe('turn/start') // idle steer degraded to a queued turn, not a steering insert
|
||||
expect(types[0]).toBe('turn/start') // idle steer degraded to a queued turn, not an in-turn insert
|
||||
})
|
||||
|
||||
it('gamma interval flip emits host/session-status and a running log-less session subscribes at lastSeq -1', async () => {
|
||||
|
||||
Reference in New Issue
Block a user