export interface TerminalToolBatch { declaredToolCalls: number; startedToolCalls: number; completedToolCalls: number; allTerminate: boolean; anyError: boolean; } export type RunOutcome = | { kind: "completed"; completionKind: "assistant-stop" | "terminating-tool" } | { kind: "failed"; detail: string } | { kind: "interrupted"; detail?: string }; export interface RunOutcomeInput { stopReason?: string; errorMessage?: string; providerErrors: number; lastToolError?: string; abortObserved: boolean; terminalToolBatch?: TerminalToolBatch; } /** * Classify the final settled state of a Pi run. * * An observed AbortSignal wins over tool/provider errors because Escape during * tool execution can leave the last assistant message at `toolUse`. A terminal * `toolUse` is successful only when the complete tool batch finished without * errors and every original result requested `terminate: true`. */ export function classifyRunOutcome(input: RunOutcomeInput): RunOutcome { const { stopReason, errorMessage, providerErrors, lastToolError, abortObserved, terminalToolBatch, } = input; if (abortObserved || stopReason === "aborted") { return { kind: "interrupted", detail: errorMessage }; } if (errorMessage) return { kind: "failed", detail: errorMessage }; if (stopReason === "length") { return { kind: "failed", detail: "模型输出达到长度限制,结果可能不完整。" }; } if (stopReason === "error") { return { kind: "failed", detail: lastToolError || "模型请求以错误状态结束。" }; } if (stopReason === "toolUse") { const completeTerminatingBatch = Boolean( terminalToolBatch && terminalToolBatch.declaredToolCalls > 0 && terminalToolBatch.startedToolCalls === terminalToolBatch.declaredToolCalls && terminalToolBatch.completedToolCalls === terminalToolBatch.declaredToolCalls && terminalToolBatch.allTerminate && !terminalToolBatch.anyError, ); return completeTerminatingBatch ? { kind: "completed", completionKind: "terminating-tool" } : { kind: "failed", detail: "Agent 在工具调用阶段停止,但没有形成完整、成功的终止工具批次。" }; } if (stopReason && stopReason !== "stop") { return { kind: "failed", detail: `Agent 以非正常状态结束:${stopReason}。` }; } if (!stopReason && providerErrors > 0) { return { kind: "failed", detail: `模型请求失败 ${providerErrors} 次。` }; } return { kind: "completed", completionKind: "assistant-stop" }; }