@bastani/atomic 0.8.26-alpha.5 → 0.8.26-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +7 -4
- package/dist/builtin/intercom/CHANGELOG.md +12 -0
- package/dist/builtin/intercom/package.json +2 -2
- package/dist/builtin/mcp/CHANGELOG.md +12 -0
- package/dist/builtin/mcp/package.json +3 -3
- package/dist/builtin/subagents/CHANGELOG.md +12 -0
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +9 -9
- package/dist/builtin/subagents/agents/debugger.md +6 -6
- package/dist/builtin/subagents/package.json +4 -4
- package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +1 -1
- package/dist/builtin/subagents/skills/browser/EXAMPLES.md +151 -0
- package/dist/builtin/subagents/skills/browser/LICENSE.txt +21 -0
- package/dist/builtin/subagents/skills/browser/REFERENCE.md +451 -0
- package/dist/builtin/subagents/skills/browser/SKILL.md +170 -0
- package/dist/builtin/subagents/skills/subagent/SKILL.md +4 -4
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +48 -10
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +30 -9
- package/dist/builtin/subagents/src/runs/shared/final-drain.ts +34 -0
- package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +416 -7
- package/dist/builtin/web-access/CHANGELOG.md +12 -0
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/workflows/CHANGELOG.md +17 -0
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +4 -1
- package/dist/builtin/workflows/builtin/goal.ts +127 -99
- package/dist/builtin/workflows/builtin/open-claude-design.ts +224 -147
- package/dist/builtin/workflows/builtin/ralph.ts +160 -197
- package/dist/builtin/workflows/package.json +2 -2
- package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
- package/dist/builtin/workflows/src/extension/index.ts +10 -2
- package/dist/builtin/workflows/src/extension/runtime.ts +35 -3
- package/dist/builtin/workflows/src/runs/background/status.ts +52 -6
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +441 -15
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +69 -8
- package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +402 -8
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +182 -6
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +76 -6
- package/dist/builtin/workflows/src/shared/stage-prompt.ts +33 -2
- package/dist/builtin/workflows/src/shared/store-types.ts +31 -0
- package/dist/builtin/workflows/src/shared/store.ts +99 -11
- package/dist/builtin/workflows/src/shared/workflow-failures.ts +758 -132
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +9 -0
- package/dist/core/agent-session.d.ts +28 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +110 -28
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +6 -3
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +23 -10
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/context-compaction.d.ts +61 -0
- package/dist/core/compaction/context-compaction.d.ts.map +1 -0
- package/dist/core/compaction/context-compaction.js +602 -0
- package/dist/core/compaction/context-compaction.js.map +1 -0
- package/dist/core/compaction/index.d.ts +1 -0
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/index.js +1 -0
- package/dist/core/compaction/index.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/session-manager.d.ts +41 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +146 -7
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/ask-user-question/tool/format-answer.d.ts +5 -5
- package/dist/core/tools/ask-user-question/tool/format-answer.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/tool/format-answer.js +5 -5
- package/dist/core/tools/ask-user-question/tool/format-answer.js.map +1 -1
- package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts +16 -3
- package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/tool/response-envelope.js +21 -3
- package/dist/core/tools/ask-user-question/tool/response-envelope.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/modes/index.d.ts +1 -1
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.js +17 -0
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +74 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +12 -7
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +8 -1
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +4 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +13 -2
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/compaction.md +42 -23
- package/docs/custom-provider.md +11 -9
- package/docs/extensions.md +35 -35
- package/docs/index.md +1 -8
- package/docs/json.md +14 -11
- package/docs/packages.md +2 -0
- package/docs/providers.md +4 -1
- package/docs/quickstart.md +5 -12
- package/docs/rpc.md +44 -8
- package/docs/sdk.md +1 -8
- package/docs/session-format.md +25 -12
- package/docs/sessions.md +2 -1
- package/docs/skills.md +1 -15
- package/docs/termux.md +9 -10
- package/docs/themes.md +2 -2
- package/docs/tmux.md +3 -3
- package/docs/tui.md +19 -32
- package/docs/usage.md +2 -0
- package/docs/workflows.md +44 -2
- package/package.json +4 -12
- package/dist/builtin/subagents/skills/browser-use/SKILL.md +0 -234
- package/dist/builtin/subagents/skills/browser-use/references/cdp-python.md +0 -76
- package/dist/builtin/subagents/skills/browser-use/references/multi-session.md +0 -92
- package/node_modules/@earendil-works/pi-tui/README.md +0 -779
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts +0 -54
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js +0 -632
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/box.d.ts +0 -22
- package/node_modules/@earendil-works/pi-tui/dist/components/box.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/box.js +0 -104
- package/node_modules/@earendil-works/pi-tui/dist/components/box.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.d.ts +0 -22
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.js +0 -35
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +0 -249
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +0 -1857
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/image.d.ts +0 -28
- package/node_modules/@earendil-works/pi-tui/dist/components/image.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/image.js +0 -89
- package/node_modules/@earendil-works/pi-tui/dist/components/image.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/input.d.ts +0 -37
- package/node_modules/@earendil-works/pi-tui/dist/components/input.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/input.js +0 -378
- package/node_modules/@earendil-works/pi-tui/dist/components/input.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.d.ts +0 -31
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.js +0 -69
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts +0 -96
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +0 -644
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.d.ts +0 -50
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.js +0 -159
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.d.ts +0 -50
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.js +0 -185
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.d.ts +0 -12
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.js +0 -23
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/text.d.ts +0 -19
- package/node_modules/@earendil-works/pi-tui/dist/components/text.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/text.js +0 -89
- package/node_modules/@earendil-works/pi-tui/dist/components/text.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.d.ts +0 -13
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.js +0 -51
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts +0 -39
- package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/editor-component.js +0 -2
- package/node_modules/@earendil-works/pi-tui/dist/editor-component.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts +0 -16
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js +0 -110
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +0 -23
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.js +0 -32
- package/node_modules/@earendil-works/pi-tui/dist/index.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.d.ts +0 -193
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.js +0 -174
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/keys.d.ts +0 -184
- package/node_modules/@earendil-works/pi-tui/dist/keys.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/keys.js +0 -1173
- package/node_modules/@earendil-works/pi-tui/dist/keys.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.d.ts +0 -28
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.js +0 -44
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.d.ts +0 -3
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.js +0 -53
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts +0 -50
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js +0 -361
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts +0 -90
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js +0 -366
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +0 -113
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js +0 -472
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts +0 -227
- package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.js +0 -1106
- package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.d.ts +0 -17
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.js +0 -25
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts +0 -84
- package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/utils.js +0 -1029
- package/node_modules/@earendil-works/pi-tui/dist/utils.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.d.ts +0 -25
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js +0 -96
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
- package/node_modules/@earendil-works/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
- package/node_modules/@earendil-works/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/node_modules/@earendil-works/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/node_modules/@earendil-works/pi-tui/package.json +0 -47
- package/node_modules/get-east-asian-width/index.d.ts +0 -60
- package/node_modules/get-east-asian-width/index.js +0 -30
- package/node_modules/get-east-asian-width/license +0 -9
- package/node_modules/get-east-asian-width/lookup-data.js +0 -21
- package/node_modules/get-east-asian-width/lookup.js +0 -138
- package/node_modules/get-east-asian-width/package.json +0 -71
- package/node_modules/get-east-asian-width/readme.md +0 -65
- package/node_modules/get-east-asian-width/utilities.js +0 -24
- package/node_modules/marked/LICENSE.md +0 -44
- package/node_modules/marked/README.md +0 -106
- package/node_modules/marked/bin/main.js +0 -282
- package/node_modules/marked/bin/marked.js +0 -15
- package/node_modules/marked/lib/marked.cjs +0 -2211
- package/node_modules/marked/lib/marked.cjs.map +0 -7
- package/node_modules/marked/lib/marked.d.cts +0 -728
- package/node_modules/marked/lib/marked.d.ts +0 -728
- package/node_modules/marked/lib/marked.esm.js +0 -2189
- package/node_modules/marked/lib/marked.esm.js.map +0 -7
- package/node_modules/marked/lib/marked.umd.js +0 -2213
- package/node_modules/marked/lib/marked.umd.js.map +0 -7
- package/node_modules/marked/man/marked.1 +0 -111
- package/node_modules/marked/man/marked.1.md +0 -92
- package/node_modules/marked/marked.min.js +0 -69
- package/node_modules/marked/package.json +0 -111
|
@@ -241,6 +241,59 @@ function lastAssistantTextFromMessages(messages: AgentSession["messages"]): stri
|
|
|
241
241
|
return undefined;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
function messageStopReason(message: AgentSession["messages"][number]): string | undefined {
|
|
245
|
+
const record = message as { readonly stopReason?: unknown };
|
|
246
|
+
return typeof record.stopReason === "string" ? record.stopReason : undefined;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function normalizedStopReason(stopReason: string | undefined): string | undefined {
|
|
250
|
+
return stopReason?.toLowerCase().replace(/[_-]+/g, "");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function isTerminalAssistantFailureStopReason(stopReason: string | undefined): boolean {
|
|
254
|
+
const normalized = normalizedStopReason(stopReason);
|
|
255
|
+
return normalized === "error" || normalized === "aborted";
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function isCleanAssistantStopReason(stopReason: string | undefined): boolean {
|
|
259
|
+
const normalized = normalizedStopReason(stopReason);
|
|
260
|
+
return normalized === "stop" || normalized === "tooluse" || normalized === "length";
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function assistantErrorMessage(message: AgentSession["messages"][number]): string | undefined {
|
|
264
|
+
const record = message as { readonly errorMessage?: unknown };
|
|
265
|
+
return typeof record.errorMessage === "string" && record.errorMessage.trim().length > 0
|
|
266
|
+
? record.errorMessage
|
|
267
|
+
: undefined;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function latestTerminalAssistantFailureSince(
|
|
271
|
+
messages: AgentSession["messages"],
|
|
272
|
+
startIndex: number,
|
|
273
|
+
): AgentSession["messages"][number] | undefined {
|
|
274
|
+
for (let index = messages.length - 1; index >= startIndex; index -= 1) {
|
|
275
|
+
const message = messages[index];
|
|
276
|
+
if (!message || message.role !== "assistant") continue;
|
|
277
|
+
const stopReason = messageStopReason(message);
|
|
278
|
+
if (isTerminalAssistantFailureStopReason(stopReason)) return message;
|
|
279
|
+
if (isCleanAssistantStopReason(stopReason)) return undefined;
|
|
280
|
+
if (assistantErrorMessage(message) === undefined && extractMessageText(message).trim().length > 0) {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
class WorkflowPromptModelFailure extends Error {
|
|
288
|
+
override readonly cause: unknown;
|
|
289
|
+
|
|
290
|
+
constructor(cause: unknown) {
|
|
291
|
+
super(errorMessage(cause));
|
|
292
|
+
this.name = "WorkflowPromptModelFailure";
|
|
293
|
+
this.cause = cause;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
244
297
|
/**
|
|
245
298
|
* When an agent turn ends on a tool that returned `terminate: true`, control
|
|
246
299
|
* returns with the tool result as the final conversational message and no
|
|
@@ -734,7 +787,7 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
|
|
|
734
787
|
activeSession: StageSessionRuntime,
|
|
735
788
|
initialText: string,
|
|
736
789
|
sdkOptions: PromptOptions | undefined,
|
|
737
|
-
): Promise<
|
|
790
|
+
): Promise<{ readonly terminalScanStartIndex: number }> {
|
|
738
791
|
// Pause/resume loop: when a controlled pause aborts the SDK call,
|
|
739
792
|
// swallow the resulting abort, suspend on `pauseRequest.deferred`,
|
|
740
793
|
// and either re-issue with the user's resume message or return the
|
|
@@ -745,28 +798,32 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
|
|
|
745
798
|
if (pendingPauseBeforePrompt) {
|
|
746
799
|
const { message } = await pendingPauseBeforePrompt.deferred.promise;
|
|
747
800
|
nextText = message;
|
|
748
|
-
if (nextText === undefined) return;
|
|
801
|
+
if (nextText === undefined) return { terminalScanStartIndex: activeSession.messages.length };
|
|
749
802
|
continue;
|
|
750
803
|
}
|
|
804
|
+
const promptStartIndex = activeSession.messages.length;
|
|
751
805
|
try {
|
|
752
806
|
await activeSession.prompt(nextText, sdkOptions);
|
|
753
807
|
const pendingPauseAfterPrompt = pauseRequest;
|
|
754
808
|
if (pendingPauseAfterPrompt) {
|
|
755
809
|
const { message } = await pendingPauseAfterPrompt.deferred.promise;
|
|
756
810
|
nextText = message;
|
|
757
|
-
if (nextText === undefined) return;
|
|
811
|
+
if (nextText === undefined) return { terminalScanStartIndex: activeSession.messages.length };
|
|
758
812
|
continue;
|
|
759
813
|
}
|
|
760
|
-
|
|
814
|
+
return { terminalScanStartIndex: promptStartIndex };
|
|
761
815
|
} catch (err) {
|
|
762
|
-
|
|
763
|
-
|
|
816
|
+
const pendingPauseAfterThrow = pauseRequest;
|
|
817
|
+
if (pendingPauseAfterThrow) {
|
|
818
|
+
const { message } = await pendingPauseAfterThrow.deferred.promise;
|
|
764
819
|
nextText = message;
|
|
820
|
+
if (nextText === undefined) return { terminalScanStartIndex: activeSession.messages.length };
|
|
765
821
|
continue;
|
|
766
822
|
}
|
|
767
823
|
throw err;
|
|
768
824
|
}
|
|
769
825
|
}
|
|
826
|
+
return { terminalScanStartIndex: activeSession.messages.length };
|
|
770
827
|
}
|
|
771
828
|
|
|
772
829
|
async function promptWithFallback(
|
|
@@ -795,14 +852,18 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
|
|
|
795
852
|
selectedModel = candidate.id;
|
|
796
853
|
notifyModelFallbackMetaChange();
|
|
797
854
|
try {
|
|
798
|
-
await promptWithPauseResume(activeSession, text, sdkOptions);
|
|
855
|
+
const { terminalScanStartIndex } = await promptWithPauseResume(activeSession, text, sdkOptions);
|
|
856
|
+
const terminalFailure = latestTerminalAssistantFailureSince(activeSession.messages, terminalScanStartIndex);
|
|
857
|
+
if (terminalFailure !== undefined) {
|
|
858
|
+
throw new WorkflowPromptModelFailure(terminalFailure);
|
|
859
|
+
}
|
|
799
860
|
modelAttempts.push({ model: candidate.id, success: true, ...modelAttemptReasoning(candidate) });
|
|
800
861
|
pendingFallbackWarnings.length = 0;
|
|
801
862
|
return;
|
|
802
863
|
} catch (err) {
|
|
803
864
|
const message = errorMessage(err);
|
|
804
865
|
modelAttempts.push({ model: candidate.id, success: false, ...modelAttemptReasoning(candidate), error: message });
|
|
805
|
-
if (signal?.aborted || !isRetryableModelFailure(
|
|
866
|
+
if (signal?.aborted || !isRetryableModelFailure(err) || index === candidates.length - 1) {
|
|
806
867
|
modelWarnings.push(...pendingFallbackWarnings);
|
|
807
868
|
pendingFallbackWarnings.length = 0;
|
|
808
869
|
notifyModelFallbackMetaChange();
|
|
@@ -313,9 +313,12 @@ const RETRYABLE_MODEL_FAILURE_PATTERNS: readonly RegExp[] = [
|
|
|
313
313
|
/billing/i,
|
|
314
314
|
/credit/i,
|
|
315
315
|
/auth(?:entication|orization)?/i,
|
|
316
|
+
/unauthori[sz]ed/i,
|
|
317
|
+
/\b40[13]\b/,
|
|
316
318
|
/api\s*key/i,
|
|
317
319
|
/token\s*expired/i,
|
|
318
320
|
/forbidden/i,
|
|
321
|
+
/invalid\s*key/i,
|
|
319
322
|
/model.*(?:unavailable|disabled|not\s*found|unknown)/i,
|
|
320
323
|
/(?:unavailable|disabled|not\s*found|unknown).*model/i,
|
|
321
324
|
/overloaded/i,
|
|
@@ -324,10 +327,11 @@ const RETRYABLE_MODEL_FAILURE_PATTERNS: readonly RegExp[] = [
|
|
|
324
327
|
/network/i,
|
|
325
328
|
/fetch/i,
|
|
326
329
|
/socket/i,
|
|
330
|
+
/connection\s*refused/i,
|
|
327
331
|
/upstream/i,
|
|
328
332
|
/timeout/i,
|
|
329
333
|
/timed\s*out/i,
|
|
330
|
-
/\b50[
|
|
334
|
+
/\b50[0-4]\b/,
|
|
331
335
|
];
|
|
332
336
|
|
|
333
337
|
const NON_RETRYABLE_FAILURE_PATTERNS: readonly RegExp[] = [
|
|
@@ -342,15 +346,405 @@ const NON_RETRYABLE_FAILURE_PATTERNS: readonly RegExp[] = [
|
|
|
342
346
|
/interrupted/i,
|
|
343
347
|
];
|
|
344
348
|
|
|
349
|
+
const CANCELLED_FAILURE_PATTERNS: readonly RegExp[] = [
|
|
350
|
+
/cancel/i,
|
|
351
|
+
/abort/i,
|
|
352
|
+
/interrupted/i,
|
|
353
|
+
];
|
|
354
|
+
|
|
355
|
+
export type ModelFallbackFailureKind =
|
|
356
|
+
| "auth_on_candidate_provider"
|
|
357
|
+
| "rate_limit"
|
|
358
|
+
| "provider_unavailable"
|
|
359
|
+
| "network_timeout"
|
|
360
|
+
| "model_unavailable"
|
|
361
|
+
| "cancelled"
|
|
362
|
+
| "task_failure"
|
|
363
|
+
| "unknown";
|
|
364
|
+
|
|
365
|
+
export type ModelFallbackFailureSource =
|
|
366
|
+
| "assistant_message"
|
|
367
|
+
| "diagnostic"
|
|
368
|
+
| "throw"
|
|
369
|
+
| "structured"
|
|
370
|
+
| "string_fallback";
|
|
371
|
+
|
|
372
|
+
export interface ModelFallbackFailureSignal {
|
|
373
|
+
readonly kind: ModelFallbackFailureKind;
|
|
374
|
+
readonly message: string;
|
|
375
|
+
readonly source: ModelFallbackFailureSource;
|
|
376
|
+
readonly stopReason?: string;
|
|
377
|
+
readonly status?: number;
|
|
378
|
+
readonly code?: string | number;
|
|
379
|
+
readonly name?: string;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const FALLBACKABLE_FAILURE_KINDS: ReadonlySet<ModelFallbackFailureKind> = new Set([
|
|
383
|
+
"auth_on_candidate_provider",
|
|
384
|
+
"rate_limit",
|
|
385
|
+
"provider_unavailable",
|
|
386
|
+
"network_timeout",
|
|
387
|
+
"model_unavailable",
|
|
388
|
+
]);
|
|
389
|
+
|
|
390
|
+
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
391
|
+
return value !== null && typeof value === "object" ? value as Record<string, unknown> : undefined;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function field(value: unknown, key: string): unknown {
|
|
395
|
+
return asRecord(value)?.[key];
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function stringField(value: unknown, key: string): string | undefined {
|
|
399
|
+
const raw = field(value, key);
|
|
400
|
+
return typeof raw === "string" && raw.trim().length > 0 ? raw : undefined;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function errorName(value: unknown): string | undefined {
|
|
404
|
+
return value instanceof Error ? value.name : stringField(value, "name");
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function directMessageFrom(value: unknown): string | undefined {
|
|
408
|
+
return stringField(value, "errorMessage")
|
|
409
|
+
?? stringField(value, "message")
|
|
410
|
+
?? stringField(value, "statusText");
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function integerFrom(value: unknown): number | undefined {
|
|
414
|
+
if (typeof value === "number" && Number.isInteger(value)) return value;
|
|
415
|
+
if (typeof value !== "string" || value.trim().length === 0) return undefined;
|
|
416
|
+
const parsed = Number(value.trim());
|
|
417
|
+
return Number.isInteger(parsed) ? parsed : undefined;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function statusFrom(value: unknown): number | undefined {
|
|
421
|
+
return integerFrom(field(value, "status"))
|
|
422
|
+
?? integerFrom(field(value, "statusCode"))
|
|
423
|
+
?? integerFrom(field(value, "httpStatus"));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function codeFrom(value: unknown): string | number | undefined {
|
|
427
|
+
const rawCode = field(value, "code");
|
|
428
|
+
return typeof rawCode === "string" || typeof rawCode === "number" ? rawCode : undefined;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function stopReasonFrom(value: unknown): string | undefined {
|
|
432
|
+
return stringField(value, "stopReason");
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function finishReasonFrom(value: unknown): string | undefined {
|
|
436
|
+
return stringField(value, "finish_reason") ?? stringField(value, "finishReason");
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function causeOf(value: unknown): unknown {
|
|
440
|
+
return value instanceof Error ? value.cause : field(value, "cause");
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function diagnosticErrors(value: unknown): readonly unknown[] {
|
|
444
|
+
const diagnostics = field(value, "diagnostics");
|
|
445
|
+
if (!Array.isArray(diagnostics)) return [];
|
|
446
|
+
const errors: unknown[] = [];
|
|
447
|
+
for (const diagnostic of diagnostics) {
|
|
448
|
+
const diagnosticError = field(diagnostic, "error");
|
|
449
|
+
errors.push(diagnosticError ?? diagnostic);
|
|
450
|
+
}
|
|
451
|
+
return errors;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function normalizeCode(value: string | number | undefined): string | undefined {
|
|
455
|
+
if (value === undefined) return undefined;
|
|
456
|
+
const normalized = String(value)
|
|
457
|
+
.trim()
|
|
458
|
+
.toLowerCase()
|
|
459
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
460
|
+
.replace(/^_+|_+$/g, "");
|
|
461
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function kindFromStatus(status: number | undefined): ModelFallbackFailureKind | undefined {
|
|
465
|
+
switch (status) {
|
|
466
|
+
case 401:
|
|
467
|
+
case 403:
|
|
468
|
+
return "auth_on_candidate_provider";
|
|
469
|
+
case 408:
|
|
470
|
+
return "network_timeout";
|
|
471
|
+
case 404:
|
|
472
|
+
return "model_unavailable";
|
|
473
|
+
case 429:
|
|
474
|
+
return "rate_limit";
|
|
475
|
+
default:
|
|
476
|
+
if (status !== undefined && status >= 500 && status <= 599) return "provider_unavailable";
|
|
477
|
+
return undefined;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function refusalKindFromCode(code: string | number | undefined): ModelFallbackFailureKind | undefined {
|
|
482
|
+
const normalizedCode = normalizeCode(code);
|
|
483
|
+
if (normalizedCode === undefined) return undefined;
|
|
484
|
+
if (normalizedCode.includes("content_filter") || normalizedCode.includes("contentfilter")) return "task_failure";
|
|
485
|
+
if (normalizedCode.includes("safety") || normalizedCode.includes("policy")) return "task_failure";
|
|
486
|
+
switch (normalizedCode) {
|
|
487
|
+
case "blocked":
|
|
488
|
+
case "blocked_by_provider":
|
|
489
|
+
case "blocked_by_safety":
|
|
490
|
+
case "blocked_by_policy":
|
|
491
|
+
case "provider_refusal":
|
|
492
|
+
case "refusal":
|
|
493
|
+
case "tool_refusal":
|
|
494
|
+
case "tool_call_refusal":
|
|
495
|
+
case "tool_use_refusal":
|
|
496
|
+
return "task_failure";
|
|
497
|
+
default:
|
|
498
|
+
return undefined;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function kindFromCode(code: string | number | undefined): ModelFallbackFailureKind | undefined {
|
|
503
|
+
const normalizedCode = normalizeCode(code);
|
|
504
|
+
if (normalizedCode === undefined) return undefined;
|
|
505
|
+
const refusalKind = refusalKindFromCode(code);
|
|
506
|
+
if (refusalKind !== undefined) return refusalKind;
|
|
507
|
+
const httpStatusKind = kindFromStatus(integerFrom(code));
|
|
508
|
+
if (httpStatusKind !== undefined) return httpStatusKind;
|
|
509
|
+
|
|
510
|
+
switch (normalizedCode) {
|
|
511
|
+
case "auth":
|
|
512
|
+
case "auth_required":
|
|
513
|
+
case "authentication_required":
|
|
514
|
+
case "unauthorized":
|
|
515
|
+
case "forbidden":
|
|
516
|
+
case "invalid_api_key":
|
|
517
|
+
case "missing_api_key":
|
|
518
|
+
case "invalid_key":
|
|
519
|
+
return "auth_on_candidate_provider";
|
|
520
|
+
case "etimedout":
|
|
521
|
+
case "econnreset":
|
|
522
|
+
case "econnrefused":
|
|
523
|
+
case "enotfound":
|
|
524
|
+
case "eai_again":
|
|
525
|
+
case "fetch_failed":
|
|
526
|
+
case "network_error":
|
|
527
|
+
case "timeout":
|
|
528
|
+
case "timeout_error":
|
|
529
|
+
case "und_err_connect_timeout":
|
|
530
|
+
return "network_timeout";
|
|
531
|
+
case "rate_limit":
|
|
532
|
+
case "rate_limit_exceeded":
|
|
533
|
+
case "too_many_requests":
|
|
534
|
+
case "quota_exceeded":
|
|
535
|
+
return "rate_limit";
|
|
536
|
+
case "aborterror":
|
|
537
|
+
case "aborted":
|
|
538
|
+
case "cancelled":
|
|
539
|
+
case "canceled":
|
|
540
|
+
return "cancelled";
|
|
541
|
+
case "model_not_found":
|
|
542
|
+
case "model_unavailable":
|
|
543
|
+
case "model_disabled":
|
|
544
|
+
case "unknown_model":
|
|
545
|
+
return "model_unavailable";
|
|
546
|
+
case "provider_error":
|
|
547
|
+
case "api_error":
|
|
548
|
+
case "service_unavailable":
|
|
549
|
+
case "temporarily_unavailable":
|
|
550
|
+
case "overloaded":
|
|
551
|
+
return "provider_unavailable";
|
|
552
|
+
default:
|
|
553
|
+
return undefined;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const PROVIDER_REFUSAL_FAILURE_PATTERNS: readonly RegExp[] = [
|
|
558
|
+
/\bfinish[_\s-]?reason\b[^\n]*\bcontent[_\s-]?filter\b/i,
|
|
559
|
+
/\bcontent[_\s-]?filter(?:ed|ing)?\b/i,
|
|
560
|
+
/\b(?:safety|policy)\b[^\n]*\b(?:refus(?:e|al|ed|es|ing)?|block(?:ed|ing)?|filter(?:ed|ing)?|violat(?:e|ion|ed|ing)?|disallow(?:ed|ing)?|reject(?:ed|ion|ing)?)\b/i,
|
|
561
|
+
/\b(?:refus(?:e|al|ed|es|ing)?|block(?:ed|ing)?|filter(?:ed|ing)?|violat(?:e|ion|ed|ing)?|disallow(?:ed|ing)?|reject(?:ed|ion|ing)?)\b[^\n]*\b(?:safety|policy)\b/i,
|
|
562
|
+
/\btool[_\s-]?(?:call|use)?[_\s-]?refus(?:e|al|ed|es|ing)?\b/i,
|
|
563
|
+
/\btool(?:\s+call|\s+use)?\b[^\n]*\brefus(?:e|al|ed|es|ing)?\b/i,
|
|
564
|
+
/\brefus(?:e|al|ed|es|ing)?\b[^\n]*\btool(?:\s+call|\s+use)?\b/i,
|
|
565
|
+
/\bprovider[_\s-]?refus(?:e|al|ed|es|ing)?\b/i,
|
|
566
|
+
/\bprovider\b[^\n]*\brefus(?:e|al|ed|es|ing)?\b[^\n]*\b(?:prompt|request|content|policy|safety)\b/i,
|
|
567
|
+
];
|
|
568
|
+
|
|
569
|
+
function refusalKindFromMessage(message: string): ModelFallbackFailureKind | undefined {
|
|
570
|
+
if (CANCELLED_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return "cancelled";
|
|
571
|
+
if (NON_RETRYABLE_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return "task_failure";
|
|
572
|
+
if (PROVIDER_REFUSAL_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return "task_failure";
|
|
573
|
+
return undefined;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function fallbackKindFromMessage(message: string, name: string | undefined): ModelFallbackFailureKind | undefined {
|
|
577
|
+
const refusalKind = refusalKindFromMessage(message);
|
|
578
|
+
if (refusalKind !== undefined) return refusalKind;
|
|
579
|
+
const nameKind = kindFromCode(name);
|
|
580
|
+
if (nameKind !== undefined) return nameKind;
|
|
581
|
+
if (!RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return undefined;
|
|
582
|
+
if (/rate\s*limit|too\s*many\s*requests|\b429\b|quota|billing|credit/i.test(message)) return "rate_limit";
|
|
583
|
+
if (/auth|unauthori[sz]ed|\b40[13]\b|api\s*key|token\s*expired|forbidden|invalid\s*key/i.test(message)) return "auth_on_candidate_provider";
|
|
584
|
+
if (/model.*(?:unavailable|disabled|not\s*found|unknown)|(?:unavailable|disabled|not\s*found|unknown).*model/i.test(message)) return "model_unavailable";
|
|
585
|
+
if (/network|fetch|socket|connection\s*refused|timeout|timed\s*out/i.test(message)) return "network_timeout";
|
|
586
|
+
return "provider_unavailable";
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function signalSource(value: unknown, fallback: ModelFallbackFailureSource | undefined): ModelFallbackFailureSource {
|
|
590
|
+
if (fallback !== undefined) return fallback;
|
|
591
|
+
if (stopReasonFrom(value) !== undefined || diagnosticErrors(value).length > 0) return "assistant_message";
|
|
592
|
+
if (value instanceof Error) return "throw";
|
|
593
|
+
return "structured";
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function makeSignal(
|
|
597
|
+
kind: ModelFallbackFailureKind,
|
|
598
|
+
value: unknown,
|
|
599
|
+
source: ModelFallbackFailureSource | undefined,
|
|
600
|
+
): ModelFallbackFailureSignal {
|
|
601
|
+
const status = statusFrom(value);
|
|
602
|
+
const code = codeFrom(value);
|
|
603
|
+
const name = errorName(value);
|
|
604
|
+
const stopReason = stopReasonFrom(value);
|
|
605
|
+
return {
|
|
606
|
+
kind,
|
|
607
|
+
message: errorMessage(value),
|
|
608
|
+
source: signalSource(value, source),
|
|
609
|
+
...(stopReason !== undefined ? { stopReason } : {}),
|
|
610
|
+
...(status !== undefined ? { status } : {}),
|
|
611
|
+
...(code !== undefined ? { code } : {}),
|
|
612
|
+
...(name !== undefined ? { name } : {}),
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function fallbackSignalFromMessage(
|
|
617
|
+
value: unknown,
|
|
618
|
+
source: ModelFallbackFailureSource | undefined,
|
|
619
|
+
): ModelFallbackFailureSignal | undefined {
|
|
620
|
+
const message = errorMessage(value);
|
|
621
|
+
if (!message.trim()) return undefined;
|
|
622
|
+
const kind = fallbackKindFromMessage(message, errorName(value));
|
|
623
|
+
return kind === undefined ? undefined : makeSignal(kind, value, source);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function classifyAssistantRefusalSignal(
|
|
627
|
+
value: unknown,
|
|
628
|
+
source: ModelFallbackFailureSource | undefined,
|
|
629
|
+
): ModelFallbackFailureSignal | undefined {
|
|
630
|
+
const codeRefusalKind = refusalKindFromCode(codeFrom(value))
|
|
631
|
+
?? refusalKindFromCode(errorName(value))
|
|
632
|
+
?? refusalKindFromCode(finishReasonFrom(value));
|
|
633
|
+
if (codeRefusalKind !== undefined) return makeSignal(codeRefusalKind, value, source);
|
|
634
|
+
|
|
635
|
+
const messageRefusalKind = refusalKindFromMessage(directMessageFrom(value) ?? "");
|
|
636
|
+
return messageRefusalKind === undefined ? undefined : makeSignal(messageRefusalKind, value, source);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function isRefusalSignal(signal: ModelFallbackFailureSignal): boolean {
|
|
640
|
+
return signal.kind === "cancelled" || signal.kind === "task_failure";
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function structuredSignal(
|
|
644
|
+
value: unknown,
|
|
645
|
+
seen: Set<unknown>,
|
|
646
|
+
source?: ModelFallbackFailureSource,
|
|
647
|
+
): ModelFallbackFailureSignal | undefined {
|
|
648
|
+
if (value === undefined || value === null || seen.has(value)) return undefined;
|
|
649
|
+
if (typeof value === "object") seen.add(value);
|
|
650
|
+
|
|
651
|
+
const stopReason = stopReasonFrom(value)?.toLowerCase();
|
|
652
|
+
if (stopReason === "aborted") return makeSignal("cancelled", value, source);
|
|
653
|
+
|
|
654
|
+
const directRefusalSignal = classifyAssistantRefusalSignal(value, source);
|
|
655
|
+
if (directRefusalSignal !== undefined) return directRefusalSignal;
|
|
656
|
+
|
|
657
|
+
const codeKind = kindFromCode(codeFrom(value));
|
|
658
|
+
const nameKind = kindFromCode(errorName(value));
|
|
659
|
+
if (codeKind === "cancelled" || nameKind === "cancelled") return makeSignal("cancelled", value, source);
|
|
660
|
+
|
|
661
|
+
let firstNestedFallbackSignal: ModelFallbackFailureSignal | undefined;
|
|
662
|
+
const nestedSeen = new Set(seen);
|
|
663
|
+
for (const diagnosticError of diagnosticErrors(value)) {
|
|
664
|
+
const diagnosticSignal = structuredSignal(diagnosticError, nestedSeen, "diagnostic")
|
|
665
|
+
?? fallbackSignalFromMessage(diagnosticError, "diagnostic");
|
|
666
|
+
if (diagnosticSignal === undefined) continue;
|
|
667
|
+
if (isRefusalSignal(diagnosticSignal)) return diagnosticSignal;
|
|
668
|
+
firstNestedFallbackSignal ??= diagnosticSignal;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const cause = causeOf(value);
|
|
672
|
+
const causeSignal = structuredSignal(cause, nestedSeen, source)
|
|
673
|
+
?? fallbackSignalFromMessage(cause, source);
|
|
674
|
+
if (causeSignal !== undefined) {
|
|
675
|
+
if (isRefusalSignal(causeSignal)) return causeSignal;
|
|
676
|
+
firstNestedFallbackSignal ??= causeSignal;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
const statusKind = kindFromStatus(statusFrom(value));
|
|
680
|
+
if (statusKind !== undefined) return makeSignal(statusKind, value, source);
|
|
681
|
+
if (codeKind !== undefined) return makeSignal(codeKind, value, source);
|
|
682
|
+
if (nameKind !== undefined) return makeSignal(nameKind, value, source);
|
|
683
|
+
|
|
684
|
+
if (firstNestedFallbackSignal !== undefined) return firstNestedFallbackSignal;
|
|
685
|
+
|
|
686
|
+
if (stopReason === "error") return makeSignal("provider_unavailable", value, source);
|
|
687
|
+
|
|
688
|
+
return undefined;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function messageFromUnknown(value: unknown, seen: Set<unknown>): string | undefined {
|
|
692
|
+
if (value === undefined || value === null || seen.has(value)) return undefined;
|
|
693
|
+
if (typeof value === "string") return value.trim().length > 0 ? value : undefined;
|
|
694
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
|
|
695
|
+
if (typeof value === "symbol" || typeof value === "function") return undefined;
|
|
696
|
+
seen.add(value);
|
|
697
|
+
|
|
698
|
+
if (value instanceof Error && value.message.trim().length > 0) return value.message;
|
|
699
|
+
const directMessage = directMessageFrom(value);
|
|
700
|
+
if (directMessage !== undefined) return directMessage;
|
|
701
|
+
|
|
702
|
+
for (const diagnosticError of diagnosticErrors(value)) {
|
|
703
|
+
const diagnosticMessage = messageFromUnknown(diagnosticError, seen);
|
|
704
|
+
if (diagnosticMessage !== undefined) return diagnosticMessage;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
const causeMessage = messageFromUnknown(causeOf(value), seen);
|
|
708
|
+
if (causeMessage !== undefined) return causeMessage;
|
|
709
|
+
|
|
710
|
+
const stopReason = stopReasonFrom(value);
|
|
711
|
+
if (stopReason !== undefined) return `Assistant message ended with stopReason:${stopReason}`;
|
|
712
|
+
const finishReason = finishReasonFrom(value);
|
|
713
|
+
if (finishReason !== undefined) return `Model request finished with finish_reason:${finishReason}`;
|
|
714
|
+
const status = statusFrom(value);
|
|
715
|
+
if (status !== undefined) return `Model request failed with status ${status}`;
|
|
716
|
+
const code = codeFrom(value);
|
|
717
|
+
if (code !== undefined) return `Model request failed with code ${String(code)}`;
|
|
718
|
+
|
|
719
|
+
return undefined;
|
|
720
|
+
}
|
|
721
|
+
|
|
345
722
|
export function errorMessage(error: unknown): string {
|
|
346
|
-
|
|
347
|
-
|
|
723
|
+
const structuredMessage = messageFromUnknown(error, new Set());
|
|
724
|
+
if (structuredMessage !== undefined) return structuredMessage;
|
|
725
|
+
const rendered = String(error);
|
|
726
|
+
return rendered === "[object Object]" ? "Model request failed" : rendered;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
export function normalizeModelFailureSignal(error: unknown): ModelFallbackFailureSignal {
|
|
730
|
+
const structured = structuredSignal(error, new Set());
|
|
731
|
+
if (structured !== undefined) return structured;
|
|
732
|
+
|
|
733
|
+
const message = errorMessage(error);
|
|
734
|
+
const name = errorName(error);
|
|
735
|
+
const fallbackKind = message.trim().length > 0
|
|
736
|
+
? fallbackKindFromMessage(message, name)
|
|
737
|
+
: undefined;
|
|
738
|
+
return {
|
|
739
|
+
kind: fallbackKind ?? "unknown",
|
|
740
|
+
message,
|
|
741
|
+
source: "string_fallback",
|
|
742
|
+
...(name !== undefined ? { name } : {}),
|
|
743
|
+
};
|
|
348
744
|
}
|
|
349
745
|
|
|
350
|
-
export function isRetryableModelFailure(error:
|
|
746
|
+
export function isRetryableModelFailure(error: unknown): boolean {
|
|
351
747
|
if (error === undefined) return false;
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
if (NON_RETRYABLE_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return false;
|
|
355
|
-
return RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(message));
|
|
748
|
+
const signal = normalizeModelFailureSignal(error);
|
|
749
|
+
return FALLBACKABLE_FAILURE_KINDS.has(signal.kind);
|
|
356
750
|
}
|