fix(session): type turn/end error as one structured failure
TurnEndReasonMap.error now carries a single `error: LlmFailure` field: an LlmError keeps its structured facts, any other error flattens to errorChain text under the UNKNOWN code. Consumers read message/code directly instead of defending against an unknown union — this also fixes errorChain() rendering structured failures as '[object Object]' in the TUI and ACP error paths. Document the turn-stopping contract: a concludesTurn result never short-circuits already-submitted next-step work (same-step additionalContexts or racing steering still runs), data decides.
This commit is contained in:
@@ -831,7 +831,7 @@ export function createTuiChat(
|
||||
liveErrors.delete(key)
|
||||
alreadyReported = true
|
||||
}
|
||||
const message = errorChain(reason.error)
|
||||
const message = reason.error.message
|
||||
if (!alreadyReported) appendNotice(message, 'error')
|
||||
break
|
||||
}
|
||||
|
||||
@@ -641,7 +641,7 @@ describe('TUI terminal-state snapshots', () => {
|
||||
todos: [{ content: `Unsafe todo ${CONTROL_PROBE}`, status: 'in_progress' }],
|
||||
})
|
||||
session.append('step/end', { turn: 1, step: 1 })
|
||||
session.append('turn/end', { turn: 1, step: 1, reason: { kind: 'error', error: `Unsafe turn error ${CONTROL_PROBE}` },
|
||||
session.append('turn/end', { turn: 1, step: 1, reason: { kind: 'error', error: { message: `Unsafe turn error ${CONTROL_PROBE}`, code: 'UNKNOWN' } },
|
||||
})
|
||||
},
|
||||
}, { columns: 100, rows: 34 })
|
||||
@@ -801,7 +801,7 @@ describe('TUI terminal-state snapshots', () => {
|
||||
harness.terminal.send('\r')
|
||||
agentEvents(harness.ctx, harness.agent).emit('agent/error', 1, 1, new Error('provider stream failed after partial output'))
|
||||
harness.session.append('step/end', { turn: 1, step: 1 })
|
||||
harness.session.append('turn/end', { turn: 1, step: 1, reason: { kind: 'error', error: 'provider stream failed after partial output' },
|
||||
harness.session.append('turn/end', { turn: 1, step: 1, reason: { kind: 'error', error: { message: 'provider stream failed after partial output', code: 'UNKNOWN' } },
|
||||
})
|
||||
harness.session.append('turn/start', { turn: 2 })
|
||||
harness.session.append('turn/end', { turn: 2, step: 0, reason: { kind: 'interrupted' },
|
||||
|
||||
@@ -466,7 +466,7 @@ describe('goodbye message and /resume', () => {
|
||||
|
||||
it.each([
|
||||
[{ kind: 'aborted', reason: { kind: 'user' } }, 'cancelled'],
|
||||
[{ kind: 'error', step: 1, error: 'failed' }, 'error'],
|
||||
[{ kind: 'error', error: { message: 'failed', code: 'UNKNOWN' } }, 'error'],
|
||||
[{ kind: 'aborted', reason: { kind: 'disposed' } }, 'disposed'],
|
||||
[{ kind: 'max-tokens' }, 'max tokens'],
|
||||
[{ kind: 'interrupted' }, 'interrupted'],
|
||||
@@ -3981,9 +3981,9 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
agentEvents(events.ctx, unrelatedAgent).emit('agent/disposed')
|
||||
agentEvents(events.ctx, events.agent).emit('agent/error', 1, 1, new Error('live failure'))
|
||||
events.session.append('step/end', { turn: 1, step: 1 })
|
||||
events.session.append('turn/end', { turn: 1, step: 1, reason: { kind: 'error', error: 'live failure' } })
|
||||
events.session.append('turn/end', { turn: 1, step: 1, reason: { kind: 'error', error: { message: 'live failure', code: 'UNKNOWN' } } })
|
||||
events.session.append('turn/start', { turn: 2 })
|
||||
events.session.append('turn/end', { turn: 2, step: 0, reason: { kind: 'error', error: 'durable failure' } })
|
||||
events.session.append('turn/end', { turn: 2, step: 0, reason: { kind: 'error', error: { message: 'durable failure', code: 'UNKNOWN' } } })
|
||||
events.session.append('turn/start', { turn: 3 })
|
||||
events.session.append('turn/end', { turn: 3, step: 0, reason: { kind: 'aborted', reason: { kind: 'user' } },
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user