refactor(agent): simplify inbox-driven turn admission

This commit is contained in:
_Kerman
2026-08-02 00:27:37 +08:00
parent d38c8bfaf3
commit dbdf270af0
104 files changed
+550 -511

No files matched your search

@@ -122,15 +122,15 @@ export interface ContextMessageNode {
source: unknown
}
/** Durable notice that a closed failed step is waiting for a model-request retry. */
/** Durable notice that a failed model request is waiting for another attempt. */
export type ModelRetryNode = LlmRetryEventData & {
kind: 'model-retry'
seq: number
/** Unix epoch ms from the llm/retry session event. */
time: number
/**
* Client-derived lifecycle: scheduled until a retry turn starts, started
* once it does, or cancelled when the failed turn aborts first.
* Client-derived lifecycle: scheduled until another attempt emits retry or
* chunk evidence, started once it does, or cancelled if the turn aborts first.
*/
retryState: 'scheduled' | 'started' | 'cancelled'
}
@@ -1,10 +1,13 @@
/**
* Convert a durable failure into copy that is safe to expose in the GUI.
* @param failure - Structured failure preserved by the session event.
* @param failure - Failure value preserved by the session event.
* @returns Display-safe copy for client projections.
*/
export function displayFailureMessage(failure: { code?: string; message: string }): string {
export function displayFailureMessage(failure: unknown): string {
if (failure === null || typeof failure !== 'object') return String(failure)
const record = failure as { code?: unknown; message?: unknown }
// Provider AUTH messages may echo a masked or partially preserved credential.
// Keep the raw diagnostic in the session log, but never project it into UI state.
return failure.code === 'AUTH' ? 'API key is invalid' : failure.message
if (record.code === 'AUTH') return 'API key is invalid'
return typeof record.message === 'string' ? record.message : JSON.stringify(failure)
}
@@ -360,9 +360,9 @@ function deriveRequests(events: readonly SessionEvent[]): readonly RequestView[]
}
if (sourceEvent.type === 'turn/end' && sourceEvent.data.reason.kind === 'error') {
const reason = sourceEvent.data.reason
updateAssistant(ordinaryByStep.get(requestKey(sourceEvent.data.turn, reason.step)), {
updateAssistant(ordinaryByStep.get(requestKey(sourceEvent.data.turn, sourceEvent.data.step)), {
status: 'error',
error: displayFailureMessage('failure' in reason ? reason.failure : reason),
error: displayFailureMessage(reason.error),
})
continue
}
@@ -646,6 +646,7 @@ export class Session implements SessionFace {
if (this.partial !== null && this.partial.turn === data.turn && this.partial.step === data.step) {
this.partial = null
}
this.settleScheduledRetry('started', data.turn)
this.derivedNodes.push({
kind: 'model-retry',
seq: event.seq,
@@ -716,11 +717,10 @@ export class Session implements SessionFace {
return
}
switch (event.type) {
case 'turn/start': {
if (event.data.trigger.kind === 'retry') this.settleScheduledRetry('started')
case 'turn/start':
return
}
case 'assistant/chunk': {
this.settleScheduledRetry('started', event.data.turn)
const { turn, step, chunk } = event.data
if (this.partial === null || this.partial.turn !== turn || this.partial.step !== step) {
this.partial = new PartialAccumulator(turn, step)
@@ -748,22 +748,29 @@ export class Session implements SessionFace {
return
}
case 'turn/end': {
if (event.data.reason.kind === 'aborted' || event.data.reason.kind === 'disposed') {
if (event.data.reason.kind === 'error') {
this.settleScheduledRetry('started', event.data.turn)
} else if (event.data.reason.kind === 'aborted' || event.data.reason.kind === 'interrupted') {
this.settleScheduledRetry('cancelled', event.data.turn)
}
if (
event.data.reason.kind === 'error'
&& !this.derivedNodes.some(node => node.kind === 'model-retry' && node.turn === event.data.turn)
) {
const failure = 'failure' in event.data.reason ? event.data.reason.failure : event.data.reason
const failure = event.data.reason.error
const failedTurn = event.data.turn
const code = failure !== null && typeof failure === 'object'
&& typeof (failure as { code?: unknown }).code === 'string'
? (failure as { code: string }).code
: undefined
this.derivedNodes.push({
kind: 'turn-error',
seq: event.seq,
time: event.time,
turn: event.data.turn,
step: event.data.reason.step,
turn: failedTurn,
step: event.data.step,
message: displayFailureMessage(failure),
...(failure.code === undefined ? {} : { code: failure.code }),
...code === undefined ? {} : { code },
})
this.derivedRev++
}