fix(agent): preserve thrown error values

This commit is contained in:
_Kerman
2026-07-24 17:11:58 +08:00
parent 90e69a3123
commit 76d0e450ce
14 files changed
+55 -61

No files matched your search

+14 -23
View File
@@ -22,10 +22,10 @@ import type {
SendOptions,
} from '@deepseek-ai/dsh-agent'
import {
BlockAssembler, HarnessError, LlmError, deepFreeze, errorChain, llmFailureOf, markAgentLoopRequest,
BlockAssembler, LlmError, deepFreeze, errorChain, isHarnessError, llmFailureOf, markAgentLoopRequest,
} from '@deepseek-ai/dsh-llm'
import type {
ContentBlock, GenerateOptions, LlmCallConfig, LlmFailure, Message, MessageSource,
ContentBlock, GenerateOptions, LlmCallConfig, Message, MessageSource,
} from '@deepseek-ai/dsh-llm'
import { canonicalHeader, headerEquals } from '@deepseek-ai/dsh-session'
import type { PromptMessageData, Session, SessionId, TurnEndReason, TurnTrigger } from '@deepseek-ai/dsh-session'
@@ -47,16 +47,6 @@ interface OutboxItem extends PromptMessageData {
steering?: PendingMessage
}
/** Normalize thrown values while preserving an existing error code. */
function toError(error: unknown): Error & { code?: string } {
return error instanceof Error ? error : new HarnessError(String(error), 'UNKNOWN', { cause: error })
}
/** Rebuild the live {@link LlmError} for serializable provider facts; `cause` keeps the foreign original. */
function llmError(facts: LlmFailure, cause?: Error): LlmError {
return new LlmError(facts.message, facts.code, { ...facts, cause })
}
function withoutToolCalls(message: Message): Message {
return { ...message, content: message.content.filter(block => block.type !== 'tool-call') }
}
@@ -216,8 +206,7 @@ export class ReactLoopAgent extends Agent {
}
} catch (error: unknown) {
if (agentInterruptReasonOf(signal) === undefined) {
const failure = toError(error)
this.loopCtx.logger.warn(`agent "${this.id}": prompt admission failed: ${errorChain(failure)}`)
this.loopCtx.logger.warn(`agent "${this.id}": prompt admission failed: ${errorChain(error)}`)
}
}
@@ -276,9 +265,8 @@ export class ReactLoopAgent extends Agent {
this.session.append('turn/end', { turn, reason })
}
} catch (error: unknown) {
const err = toError(error)
this.loopCtx.logger.warn(`agent "${this.id}": closing turn ${turn} failed: ${errorChain(err)}`)
emitAgentEvent(this.loopCtx, this, 'agent/error', turn, step, err)
this.loopCtx.logger.warn(`agent "${this.id}": closing turn ${turn} failed: ${errorChain(error)}`)
emitAgentEvent(this.loopCtx, this, 'agent/error', turn, step, error)
}
if (this.abort === controller) this.abort = undefined
emitAgentEvent(this.loopCtx, this, 'agent/idle', turn, idle)
@@ -335,14 +323,18 @@ export class ReactLoopAgent extends Agent {
// Normalize a final-adapter failure into the one model-error type; the
// foreign original stays on `cause` for the rendered chain.
const facts = llmFailureOf(stream, error)
if (facts !== undefined && error instanceof Error) throw llmError(facts, error)
if (facts !== undefined && error instanceof Error) {
throw new LlmError(facts.message, facts.code, { ...facts, cause: error })
}
throw error
}
signal.throwIfAborted()
// Failure finish chunks take the same path as thrown stream errors.
const finish = assembler.finish
if (finish.kind === 'error' || finish.kind === 'aborted') throw llmError(finish.failure)
if (finish.kind === 'error' || finish.kind === 'aborted') {
throw new LlmError(finish.failure.message, finish.failure.code, finish.failure)
}
// Truncated (max-tokens) output cannot owe tool calls.
const assembled = assembler.finish.kind === 'max-tokens'
@@ -478,11 +470,10 @@ export class ReactLoopAgent extends Agent {
idle: { kind: 'error', error, failure: error.failure },
}
}
const err = toError(error)
emitAgentEvent(this.loopCtx, this, 'agent/error', turn, step, err)
emitAgentEvent(this.loopCtx, this, 'agent/error', turn, step, error)
return {
reason: { kind: 'error', step, message: errorChain(err), ...typeof err.code === 'string' ? { code: err.code } : {} },
idle: { kind: 'error', error: err },
reason: { kind: 'error', step, message: errorChain(error), ...isHarnessError(error) ? { code: error.code } : {} },
idle: { kind: 'error', error },
}
}