@bastani/atomic 0.9.40 → 0.9.41-alpha.2
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 +75 -0
- package/README.md +1 -1
- package/dist/builtin/intercom/CHANGELOG.md +12 -0
- package/dist/builtin/intercom/README.md +1 -1
- package/dist/builtin/intercom/broker/broker.ts +24 -21
- package/dist/builtin/intercom/broker/client.ts +97 -6
- package/dist/builtin/intercom/broker/list-reply.ts +53 -0
- package/dist/builtin/intercom/index.bundle.mjs +278 -46
- package/dist/builtin/intercom/package.json +2 -2
- package/dist/builtin/intercom/recoverable-disconnect.ts +27 -1
- package/dist/builtin/intercom/types.ts +8 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +14 -0
- package/dist/builtin/subagents/README.md +14 -0
- package/dist/builtin/subagents/package.json +3 -3
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +99 -19
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/workflows/CHANGELOG.md +33 -0
- package/dist/builtin/workflows/README.md +2 -0
- package/dist/builtin/workflows/builtin/adversarial-verification.js +2 -2
- package/dist/builtin/workflows/builtin/{chunk-qjqn389g.js → chunk-2jfst7e4.js} +72 -10
- package/dist/builtin/workflows/builtin/{chunk-4bwtzyjq.js → chunk-49kkbdpr.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-qdd897wm.js → chunk-apagcs4r.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-454hz1qt.js → chunk-dcxscfnb.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-bjva9j5k.js → chunk-hx8v4p97.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-85mx6wcf.js → chunk-k5q3t7je.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-8kkv04hz.js → chunk-n2gkzkty.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-y45tkftg.js → chunk-txbc93t6.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-x0vvjyzg.js → chunk-w4xp1w1f.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-5eh0brtd.js → chunk-z3pajrfs.js} +1 -1
- package/dist/builtin/workflows/builtin/classify-and-act.js +2 -2
- package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +2 -2
- package/dist/builtin/workflows/builtin/generate-and-filter.js +2 -2
- package/dist/builtin/workflows/builtin/goal.js +2 -2
- package/dist/builtin/workflows/builtin/index.js +10 -10
- package/dist/builtin/workflows/builtin/loop-until-done.js +2 -2
- package/dist/builtin/workflows/builtin/open-claude-design.js +2 -2
- package/dist/builtin/workflows/builtin/ralph.js +2 -2
- package/dist/builtin/workflows/builtin/tournament.js +2 -2
- package/dist/builtin/workflows/package.json +2 -2
- package/dist/builtin/workflows/src/authoring.d.ts +1 -1
- package/dist/builtin/workflows/src/durable/journal/internal.d.ts +16 -0
- package/dist/builtin/workflows/src/durable/journal/legacy-journal-tables.d.ts +9 -0
- package/dist/builtin/workflows/src/durable/journal/sql-run-journal.d.ts +6 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +950 -116
- package/dist/builtin/workflows/src/index.js +806 -131
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +17 -1
- package/dist/cli/credential-print.d.ts.map +1 -1
- package/dist/cli/credential-print.js +1 -1
- package/dist/cli/credential-print.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-extension-bindings.d.ts +2 -1
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +33 -18
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +2 -1
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +114 -21
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +6 -0
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-state.d.ts +19 -1
- package/dist/core/agent-session-state.d.ts.map +1 -1
- package/dist/core/agent-session-state.js +41 -6
- package/dist/core/agent-session-state.js.map +1 -1
- package/dist/core/auth-storage-backends.d.ts +3 -1
- package/dist/core/auth-storage-backends.d.ts.map +1 -1
- package/dist/core/auth-storage-backends.js +79 -12
- package/dist/core/auth-storage-backends.js.map +1 -1
- package/dist/core/client-identity.d.ts +8 -0
- package/dist/core/client-identity.d.ts.map +1 -0
- package/dist/core/client-identity.js +16 -0
- package/dist/core/client-identity.js.map +1 -0
- package/dist/core/compaction/planner-outcome.d.ts.map +1 -1
- package/dist/core/compaction/planner-outcome.js +2 -1
- package/dist/core/compaction/planner-outcome.js.map +1 -1
- package/dist/core/execution-model-router.d.ts +18 -0
- package/dist/core/execution-model-router.d.ts.map +1 -1
- package/dist/core/execution-model-router.js +14 -8
- package/dist/core/execution-model-router.js.map +1 -1
- package/dist/core/extension-oauth-refresh-rejection.d.ts +12 -0
- package/dist/core/extension-oauth-refresh-rejection.d.ts.map +1 -0
- package/dist/core/extension-oauth-refresh-rejection.js +70 -0
- package/dist/core/extension-oauth-refresh-rejection.js.map +1 -0
- package/dist/core/extension-startup-failure.d.ts +30 -0
- package/dist/core/extension-startup-failure.d.ts.map +1 -0
- package/dist/core/extension-startup-failure.js +62 -0
- package/dist/core/extension-startup-failure.js.map +1 -0
- package/dist/core/extensions/context-types.d.ts +2 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/provider-types.d.ts +2 -0
- package/dist/core/extensions/provider-types.d.ts.map +1 -1
- package/dist/core/extensions/provider-types.js.map +1 -1
- package/dist/core/extensions/runner-context.d.ts +1 -0
- package/dist/core/extensions/runner-context.d.ts.map +1 -1
- package/dist/core/extensions/runner-context.js +8 -0
- package/dist/core/extensions/runner-context.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +3 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/runtime-types.d.ts +1 -0
- package/dist/core/extensions/runtime-types.d.ts.map +1 -1
- package/dist/core/extensions/runtime-types.js.map +1 -1
- package/dist/core/extensions/workflow-run-control-hub.d.ts +1 -0
- package/dist/core/extensions/workflow-run-control-hub.d.ts.map +1 -1
- package/dist/core/extensions/workflow-run-control-hub.js +3 -0
- package/dist/core/extensions/workflow-run-control-hub.js.map +1 -1
- package/dist/core/extensions/workflow-run-control.d.ts +8 -0
- package/dist/core/extensions/workflow-run-control.d.ts.map +1 -1
- package/dist/core/extensions/workflow-run-control.js.map +1 -1
- package/dist/core/model-config.d.ts +28 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-fallback-failures.d.ts.map +1 -1
- package/dist/core/model-fallback-failures.js +12 -0
- package/dist/core/model-fallback-failures.js.map +1 -1
- package/dist/core/model-resolver-defaults.js +1 -1
- package/dist/core/model-resolver-defaults.js.map +1 -1
- package/dist/core/model-runtime-streaming.d.ts.map +1 -1
- package/dist/core/model-runtime-streaming.js +9 -3
- package/dist/core/model-runtime-streaming.js.map +1 -1
- package/dist/core/model-runtime.d.ts +5 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +6 -0
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/provider-composer-internal.d.ts +8 -6
- package/dist/core/provider-composer-internal.d.ts.map +1 -1
- package/dist/core/provider-composer-internal.js +21 -8
- package/dist/core/provider-composer-internal.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +12 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/storage/session/sql-session-store.d.ts +35 -0
- package/dist/core/storage/session/sql-session-store.d.ts.map +1 -1
- package/dist/core/storage/session/sql-session-store.js +158 -42
- package/dist/core/storage/session/sql-session-store.js.map +1 -1
- package/dist/core/structured-output/deadline.d.ts +40 -0
- package/dist/core/structured-output/deadline.d.ts.map +1 -0
- package/dist/core/structured-output/deadline.js +110 -0
- package/dist/core/structured-output/deadline.js.map +1 -0
- package/dist/core/structured-output/index.d.ts.map +1 -1
- package/dist/core/structured-output/index.js +103 -47
- package/dist/core/structured-output/index.js.map +1 -1
- package/dist/core/structured-output/rejection-detail.d.ts +9 -0
- package/dist/core/structured-output/rejection-detail.d.ts.map +1 -0
- package/dist/core/structured-output/rejection-detail.js +129 -0
- package/dist/core/structured-output/rejection-detail.js.map +1 -0
- package/dist/core/structured-output/types.d.ts +8 -1
- package/dist/core/structured-output/types.d.ts.map +1 -1
- package/dist/core/structured-output/types.js.map +1 -1
- package/dist/core/tasks/completion.d.ts +7 -1
- package/dist/core/tasks/completion.d.ts.map +1 -1
- package/dist/core/tasks/completion.js +28 -6
- package/dist/core/tasks/completion.js.map +1 -1
- package/dist/core/tasks/contracts.d.ts +6 -1
- package/dist/core/tasks/contracts.d.ts.map +1 -1
- package/dist/core/tasks/contracts.js.map +1 -1
- package/dist/core/tasks/pause.d.ts +2 -1
- package/dist/core/tasks/pause.d.ts.map +1 -1
- package/dist/core/tasks/pause.js +3 -2
- package/dist/core/tasks/pause.js.map +1 -1
- package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
- package/dist/core/tools/bash-pty-native.js +10 -5
- package/dist/core/tools/bash-pty-native.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +16 -32
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/grep.js +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/kill.d.ts.map +1 -1
- package/dist/core/tools/kill.js +17 -3
- package/dist/core/tools/kill.js.map +1 -1
- package/dist/core/tools/shell-task-render.d.ts +55 -0
- package/dist/core/tools/shell-task-render.d.ts.map +1 -0
- package/dist/core/tools/shell-task-render.js +123 -0
- package/dist/core/tools/shell-task-render.js.map +1 -0
- package/dist/core/tools/shell-task-status.d.ts +52 -0
- package/dist/core/tools/shell-task-status.d.ts.map +1 -0
- package/dist/core/tools/shell-task-status.js +184 -0
- package/dist/core/tools/shell-task-status.js.map +1 -0
- package/dist/core/tools/structured-output.d.ts +5 -0
- package/dist/core/tools/structured-output.d.ts.map +1 -1
- package/dist/core/tools/structured-output.js +3 -0
- package/dist/core/tools/structured-output.js.map +1 -1
- package/dist/extensions/codemode/execute.d.ts.map +1 -1
- package/dist/extensions/codemode/execute.js +134 -57
- package/dist/extensions/codemode/execute.js.map +1 -1
- package/dist/extensions/mcp/config.d.ts.map +1 -1
- package/dist/extensions/mcp/config.js +3 -2
- package/dist/extensions/mcp/config.js.map +1 -1
- package/dist/index-extensions.d.ts +1 -0
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js +3 -0
- package/dist/index-extensions.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +19 -6
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/task-row.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-row.js +11 -1
- package/dist/modes/interactive/components/task-row.js.map +1 -1
- package/dist/modes/interactive/interactive-auth-routing.js +12 -10
- package/dist/modes/interactive/interactive-auth-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-custom-ui.js +20 -30
- package/dist/modes/interactive/interactive-extension-custom-ui.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-dialogs.js +23 -96
- package/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-runtime.js +7 -8
- package/dist/modes/interactive/interactive-extension-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +27 -4
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +20 -10
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +40 -5
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +2 -4
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-types.d.ts +4 -1
- package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
- package/dist/modes/interactive/interactive-prompt-restore.d.ts +3 -0
- package/dist/modes/interactive/interactive-prompt-restore.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-prompt-restore.js +9 -1
- package/dist/modes/interactive/interactive-prompt-restore.js.map +1 -1
- package/dist/modes/interactive/interactive-prompt-turn.js +30 -2
- package/dist/modes/interactive/interactive-prompt-turn.js.map +1 -1
- package/dist/modes/interactive/interactive-queueing.js +43 -8
- package/dist/modes/interactive/interactive-queueing.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +12 -3
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive/theme/color-utils.d.ts +6 -1
- package/dist/modes/interactive/theme/color-utils.d.ts.map +1 -1
- package/dist/modes/interactive/theme/color-utils.js +39 -17
- package/dist/modes/interactive/theme/color-utils.js.map +1 -1
- package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-class.js +2 -2
- package/dist/modes/interactive/theme/theme-class.js.map +1 -1
- package/dist/modes/interactive/theme/theme-tokens.d.ts +6 -0
- package/dist/modes/interactive/theme/theme-tokens.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-tokens.js +7 -3
- package/dist/modes/interactive/theme/theme-tokens.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-auth.d.ts +3 -0
- package/dist/modes/interactive-engine/isolated-auth.d.ts.map +1 -1
- package/dist/modes/interactive-engine/isolated-auth.js +13 -1
- package/dist/modes/interactive-engine/isolated-auth.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.js +11 -2
- package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
- package/dist/modes/interactive-engine/remote-model-catalog.d.ts +8 -0
- package/dist/modes/interactive-engine/remote-model-catalog.d.ts.map +1 -1
- package/dist/modes/interactive-engine/remote-model-catalog.js +54 -0
- package/dist/modes/interactive-engine/remote-model-catalog.js.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.js +4 -13
- package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
- package/dist/modes/rpc/rpc-model-catalog.d.ts +9 -0
- package/dist/modes/rpc/rpc-model-catalog.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-model-catalog.js +19 -0
- package/dist/modes/rpc/rpc-model-catalog.js.map +1 -0
- package/dist/modes/rpc/rpc-provider-auth.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-provider-auth.js +2 -6
- package/dist/modes/rpc/rpc-provider-auth.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +6 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/changelog.d.ts +6 -0
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js +11 -0
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +4 -0
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +3 -2
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/background-tasks.md +5 -3
- package/docs/codemode.md +2 -2
- package/docs/custom-provider/oauth.md +42 -1
- package/docs/custom-provider/registration.md +2 -0
- package/docs/environment-variables.md +1 -0
- package/docs/extensions/api-reference.md +3 -4
- package/docs/extensions/authoring.md +1 -1
- package/docs/extensions/events.md +1 -1
- package/docs/models-configuration.md +9 -0
- package/docs/models.md +3 -1
- package/docs/providers.md +4 -4
- package/docs/rpc/protocol.md +4 -2
- package/docs/sdk/reference.md +2 -0
- package/docs/sdk/structured-decisions.md +7 -5
- package/docs/sdk.md +5 -2
- package/docs/settings.md +4 -2
- package/docs/subagents/reference.md +1 -1
- package/docs/subagents.md +15 -1
- package/docs/themes/reference.md +3 -1
- package/docs/usage.md +3 -1
- package/docs/workflows/api-reference.md +28 -0
- package/docs/workflows/authoring.md +1 -1
- package/docs/workflows/operations.md +2 -2
- package/npm-shrinkwrap.json +67 -54
- package/package.json +6 -6
- package/schemas/models.schema.json +21 -0
- package/dist/core/provider-attribution.d.ts +0 -4
- package/dist/core/provider-attribution.d.ts.map +0 -1
- package/dist/core/provider-attribution.js +0 -87
- package/dist/core/provider-attribution.js.map +0 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { SETTINGS_DEFAULTS } from "../settings-defaults.js";
|
|
2
|
+
/** The largest delay `setTimeout` honors; a larger one fires immediately. */
|
|
3
|
+
const MAX_DEADLINE_MS = 2_147_483_647;
|
|
4
|
+
const CANCELLED_MESSAGE = "Structured output cancelled; no decision was accepted.";
|
|
5
|
+
export class StructuredOutputDeadlineError extends Error {
|
|
6
|
+
constructor(timeoutMs, attempts) {
|
|
7
|
+
super(`Structured output decision exceeded its ${timeoutMs}ms deadline; no decision was accepted.${attempts ? ` ${attempts}` : ""}`);
|
|
8
|
+
this.name = "StructuredOutputDeadlineError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** The failure for an aborted decision: the deadline error when the deadline fired, otherwise a plain cancellation. */
|
|
12
|
+
export function abortFailure(signal) {
|
|
13
|
+
return signal.reason instanceof StructuredOutputDeadlineError ? signal.reason : new Error(CANCELLED_MESSAGE);
|
|
14
|
+
}
|
|
15
|
+
export function validateTimeoutOverride(value) {
|
|
16
|
+
if (value === undefined)
|
|
17
|
+
return;
|
|
18
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
19
|
+
throw new Error("Structured output timeoutMs must be a positive integer.");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The wall-clock limit for one decision: an explicit `timeoutMs`, else the session's `httpIdleTimeoutMs`, else that
|
|
24
|
+
* setting's default. Like the provider clients, an idle timeout of `0` (disabled) becomes the largest timer delay.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveDecisionDeadlineMs(timeoutMs, httpIdleTimeoutMs) {
|
|
27
|
+
validateTimeoutOverride(timeoutMs);
|
|
28
|
+
const requested = timeoutMs ?? httpIdleTimeoutMs ?? SETTINGS_DEFAULTS.httpIdleTimeoutMs;
|
|
29
|
+
return requested === 0 ? MAX_DEADLINE_MS : Math.min(requested, MAX_DEADLINE_MS);
|
|
30
|
+
}
|
|
31
|
+
function formatDuration(milliseconds) {
|
|
32
|
+
const ms = Math.round(milliseconds);
|
|
33
|
+
if (ms < 1_000)
|
|
34
|
+
return `${ms}ms`;
|
|
35
|
+
if (ms < 60_000)
|
|
36
|
+
return `${(ms / 1_000).toFixed(1)}s`;
|
|
37
|
+
const totalSeconds = Math.round(ms / 1_000);
|
|
38
|
+
const hours = Math.floor(totalSeconds / 3_600);
|
|
39
|
+
const minutes = Math.floor((totalSeconds % 3_600) / 60);
|
|
40
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
41
|
+
const clock = hours > 0 ? `${hours}h${pad(minutes)}m` : `${minutes}m`;
|
|
42
|
+
return `${clock}${pad(totalSeconds % 60)}s`;
|
|
43
|
+
}
|
|
44
|
+
/** One model request, from send to response; transport retries and their waits are not part of it. Records only the stop reason and timing, never content. */
|
|
45
|
+
export class DecisionAttempt {
|
|
46
|
+
startedAt = performance.now();
|
|
47
|
+
endedAt;
|
|
48
|
+
stopReason;
|
|
49
|
+
settle(stopReason) {
|
|
50
|
+
this.endedAt ??= performance.now();
|
|
51
|
+
if (stopReason !== undefined)
|
|
52
|
+
this.stopReason = stopReason;
|
|
53
|
+
}
|
|
54
|
+
describe(now) {
|
|
55
|
+
const outcome = this.endedAt === undefined
|
|
56
|
+
? "still generating"
|
|
57
|
+
: this.stopReason === undefined
|
|
58
|
+
? "no response"
|
|
59
|
+
: `stopReason=${this.stopReason}`;
|
|
60
|
+
return `${outcome} ${formatDuration((this.endedAt ?? now) - this.startedAt)}`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** How every generation of one decision ended and how long it took, across repairs, retries and candidates. */
|
|
64
|
+
export class DecisionTrace {
|
|
65
|
+
attempts = [];
|
|
66
|
+
get size() {
|
|
67
|
+
return this.attempts.length;
|
|
68
|
+
}
|
|
69
|
+
begin() {
|
|
70
|
+
const attempt = new DecisionAttempt();
|
|
71
|
+
this.attempts.push(attempt);
|
|
72
|
+
return attempt;
|
|
73
|
+
}
|
|
74
|
+
/** Mark every generation still in flight as ended without a response. */
|
|
75
|
+
settleOpen() {
|
|
76
|
+
for (const attempt of this.attempts)
|
|
77
|
+
attempt.settle();
|
|
78
|
+
}
|
|
79
|
+
/** The attempts from `first` onward, numbered from 1, or an empty string when there are none. */
|
|
80
|
+
describe(first = 0) {
|
|
81
|
+
const now = performance.now();
|
|
82
|
+
const shown = this.attempts.slice(first);
|
|
83
|
+
if (shown.length === 0)
|
|
84
|
+
return "";
|
|
85
|
+
return `Attempts: ${shown.map((attempt, index) => `${index + 1}) ${attempt.describe(now)}`).join("; ")}.`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Run a whole decision under one wall-clock limit. `run` receives a signal that aborts with the caller's reason when
|
|
90
|
+
* the caller aborts and with a {@link StructuredOutputDeadlineError} when the limit passes, so the existing abort
|
|
91
|
+
* plumbing cancels the in-flight generation. The timer is always released.
|
|
92
|
+
*/
|
|
93
|
+
export async function withinDecisionDeadline(callerSignal, timeoutMs, run) {
|
|
94
|
+
const controller = new AbortController();
|
|
95
|
+
const trace = new DecisionTrace();
|
|
96
|
+
const forwardCallerAbort = () => controller.abort(callerSignal?.reason);
|
|
97
|
+
if (callerSignal?.aborted)
|
|
98
|
+
forwardCallerAbort();
|
|
99
|
+
else
|
|
100
|
+
callerSignal?.addEventListener("abort", forwardCallerAbort, { once: true });
|
|
101
|
+
const timer = setTimeout(() => controller.abort(new StructuredOutputDeadlineError(timeoutMs, trace.describe())), timeoutMs);
|
|
102
|
+
try {
|
|
103
|
+
return await run({ signal: controller.signal, trace });
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
clearTimeout(timer);
|
|
107
|
+
callerSignal?.removeEventListener("abort", forwardCallerAbort);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=deadline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deadline.js","sourceRoot":"","sources":["../../../src/core/structured-output/deadline.ts"],"names":[],"mappings":"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","sourcesContent":["import { SETTINGS_DEFAULTS } from \"../settings-defaults.ts\";\n\n/** The largest delay `setTimeout` honors; a larger one fires immediately. */\nconst MAX_DEADLINE_MS = 2_147_483_647;\n\nconst CANCELLED_MESSAGE = \"Structured output cancelled; no decision was accepted.\";\n\nexport class StructuredOutputDeadlineError extends Error {\n\tconstructor(timeoutMs: number, attempts: string) {\n\t\tsuper(\n\t\t\t`Structured output decision exceeded its ${timeoutMs}ms deadline; no decision was accepted.${attempts ? ` ${attempts}` : \"\"}`,\n\t\t);\n\t\tthis.name = \"StructuredOutputDeadlineError\";\n\t}\n}\n\n/** The failure for an aborted decision: the deadline error when the deadline fired, otherwise a plain cancellation. */\nexport function abortFailure(signal: AbortSignal): Error {\n\treturn signal.reason instanceof StructuredOutputDeadlineError ? signal.reason : new Error(CANCELLED_MESSAGE);\n}\n\nexport function validateTimeoutOverride(value: number | undefined): void {\n\tif (value === undefined) return;\n\tif (!Number.isSafeInteger(value) || value <= 0) {\n\t\tthrow new Error(\"Structured output timeoutMs must be a positive integer.\");\n\t}\n}\n\n/**\n * The wall-clock limit for one decision: an explicit `timeoutMs`, else the session's `httpIdleTimeoutMs`, else that\n * setting's default. Like the provider clients, an idle timeout of `0` (disabled) becomes the largest timer delay.\n */\nexport function resolveDecisionDeadlineMs(timeoutMs: number | undefined, httpIdleTimeoutMs?: number): number {\n\tvalidateTimeoutOverride(timeoutMs);\n\tconst requested = timeoutMs ?? httpIdleTimeoutMs ?? SETTINGS_DEFAULTS.httpIdleTimeoutMs;\n\treturn requested === 0 ? MAX_DEADLINE_MS : Math.min(requested, MAX_DEADLINE_MS);\n}\n\nfunction formatDuration(milliseconds: number): string {\n\tconst ms = Math.round(milliseconds);\n\tif (ms < 1_000) return `${ms}ms`;\n\tif (ms < 60_000) return `${(ms / 1_000).toFixed(1)}s`;\n\tconst totalSeconds = Math.round(ms / 1_000);\n\tconst hours = Math.floor(totalSeconds / 3_600);\n\tconst minutes = Math.floor((totalSeconds % 3_600) / 60);\n\tconst pad = (value: number) => String(value).padStart(2, \"0\");\n\tconst clock = hours > 0 ? `${hours}h${pad(minutes)}m` : `${minutes}m`;\n\treturn `${clock}${pad(totalSeconds % 60)}s`;\n}\n\n/** One model request, from send to response; transport retries and their waits are not part of it. Records only the stop reason and timing, never content. */\nexport class DecisionAttempt {\n\treadonly startedAt = performance.now();\n\tendedAt: number | undefined;\n\tstopReason: string | undefined;\n\n\tsettle(stopReason?: string): void {\n\t\tthis.endedAt ??= performance.now();\n\t\tif (stopReason !== undefined) this.stopReason = stopReason;\n\t}\n\n\tdescribe(now: number): string {\n\t\tconst outcome =\n\t\t\tthis.endedAt === undefined\n\t\t\t\t? \"still generating\"\n\t\t\t\t: this.stopReason === undefined\n\t\t\t\t\t? \"no response\"\n\t\t\t\t\t: `stopReason=${this.stopReason}`;\n\t\treturn `${outcome} ${formatDuration((this.endedAt ?? now) - this.startedAt)}`;\n\t}\n}\n\n/** How every generation of one decision ended and how long it took, across repairs, retries and candidates. */\nexport class DecisionTrace {\n\tprivate readonly attempts: DecisionAttempt[] = [];\n\n\tget size(): number {\n\t\treturn this.attempts.length;\n\t}\n\n\tbegin(): DecisionAttempt {\n\t\tconst attempt = new DecisionAttempt();\n\t\tthis.attempts.push(attempt);\n\t\treturn attempt;\n\t}\n\n\t/** Mark every generation still in flight as ended without a response. */\n\tsettleOpen(): void {\n\t\tfor (const attempt of this.attempts) attempt.settle();\n\t}\n\n\t/** The attempts from `first` onward, numbered from 1, or an empty string when there are none. */\n\tdescribe(first = 0): string {\n\t\tconst now = performance.now();\n\t\tconst shown = this.attempts.slice(first);\n\t\tif (shown.length === 0) return \"\";\n\t\treturn `Attempts: ${shown.map((attempt, index) => `${index + 1}) ${attempt.describe(now)}`).join(\"; \")}.`;\n\t}\n}\n\nexport interface DecisionRun {\n\treadonly signal: AbortSignal;\n\treadonly trace: DecisionTrace;\n}\n\n/**\n * Run a whole decision under one wall-clock limit. `run` receives a signal that aborts with the caller's reason when\n * the caller aborts and with a {@link StructuredOutputDeadlineError} when the limit passes, so the existing abort\n * plumbing cancels the in-flight generation. The timer is always released.\n */\nexport async function withinDecisionDeadline<T>(\n\tcallerSignal: AbortSignal | undefined,\n\ttimeoutMs: number,\n\trun: (decision: DecisionRun) => Promise<T>,\n): Promise<T> {\n\tconst controller = new AbortController();\n\tconst trace = new DecisionTrace();\n\tconst forwardCallerAbort = () => controller.abort(callerSignal?.reason);\n\tif (callerSignal?.aborted) forwardCallerAbort();\n\telse callerSignal?.addEventListener(\"abort\", forwardCallerAbort, { once: true });\n\tconst timer = setTimeout(\n\t\t() => controller.abort(new StructuredOutputDeadlineError(timeoutMs, trace.describe())),\n\t\ttimeoutMs,\n\t);\n\ttry {\n\t\treturn await run({ signal: controller.signal, trace });\n\t} finally {\n\t\tclearTimeout(timer);\n\t\tcallerSignal?.removeEventListener(\"abort\", forwardCallerAbort);\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/structured-output/index.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/structured-output/index.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAU/C,OAAO,KAAK,EAGX,qBAAqB,EAErB,uBAAuB,EACvB,sBAAsB,EACtB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACX,qBAAqB,EACrB,2BAA2B,EAC3B,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,GACtB,MAAM,YAAY,CAAC;AAkRpB,wBAAsB,wBAAwB,CAAC,CAAC,SAAS,OAAO,EAC/D,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC,GACjC,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAM5C;AAkQD,6GAA6G;AAC7G,wBAAsB,UAAU,CAAC,CAAC,SAAS,OAAO,EACjD,OAAO,EAAE,qBAAqB,CAAC,CAAC,CAAC;AACjC,iGAAiG;AACjG,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,OAAO,GAC9C,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAa5C"}
|
|
@@ -4,7 +4,9 @@ import { raceWithAbortSignal } from "../../utils/abort.js";
|
|
|
4
4
|
import { reportModelRoutingDebug } from "../model-routing-debug.js";
|
|
5
5
|
import { STRUCTURED_OUTPUT_TOOL_NAME } from "../tools/structured-output.js";
|
|
6
6
|
import { compileChoiceSchema } from "./choice-schema.js";
|
|
7
|
+
import { abortFailure, resolveDecisionDeadlineMs, withinDecisionDeadline } from "./deadline.js";
|
|
7
8
|
import { InvalidDecisionOutputError } from "./invalid-output.js";
|
|
9
|
+
import { describeSchemaErrors, schemaMismatchMessage, unparseableArgumentsMessage } from "./rejection-detail.js";
|
|
8
10
|
import { resolveRouterModel } from "./resolver.js";
|
|
9
11
|
export { resolveRouterModel } from "./resolver.js";
|
|
10
12
|
const DEFAULT_DECISION_RETRY = Object.freeze({ enabled: true, maxRetries: 3, baseDelayMs: 2000 });
|
|
@@ -45,7 +47,7 @@ function validateState(state) {
|
|
|
45
47
|
throw new Error("Structured output requires a nonempty named state object containing the task and relevant context text.");
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
|
-
async function inferChat(request, model, signal, assertActive) {
|
|
50
|
+
async function inferChat(request, model, signal, assertActive, trace) {
|
|
49
51
|
// Anthropic catalog configuration may opt into server-side fallback. A decision must not.
|
|
50
52
|
const decisionModel = model.api === "anthropic-messages"
|
|
51
53
|
? { ...model, compat: { ...model.compat, allowedFallbackModels: [] } }
|
|
@@ -58,34 +60,44 @@ async function inferChat(request, model, signal, assertActive) {
|
|
|
58
60
|
let response;
|
|
59
61
|
assertActive();
|
|
60
62
|
try {
|
|
61
|
-
response = await retryAssistantCall(() =>
|
|
62
|
-
.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
{
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
63
|
+
response = await retryAssistantCall(() => {
|
|
64
|
+
const attempt = trace.begin();
|
|
65
|
+
return request.modelRegistry
|
|
66
|
+
.streamSimple(decisionModel, {
|
|
67
|
+
systemPrompt: `${STRUCTURED_DECISION_POLICY}\n\n${request.instructions}\n\nCall ${STRUCTURED_OUTPUT_TOOL_NAME} exactly once with the decision. Do not use prose or other tools.`,
|
|
68
|
+
messages: [
|
|
69
|
+
{
|
|
70
|
+
role: "user",
|
|
71
|
+
content: JSON.stringify(Object.keys(request.classifier.questions).length
|
|
72
|
+
? { state: request.state, questions: request.classifier.questions }
|
|
73
|
+
: { state: request.state }),
|
|
74
|
+
timestamp: Date.now(),
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
tools: [
|
|
78
|
+
{
|
|
79
|
+
name: tool.name,
|
|
80
|
+
description: tool.description,
|
|
81
|
+
parameters: tool.parameters,
|
|
82
|
+
constrainedSampling: { type: "json_schema", strict: "prefer" },
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
}, {
|
|
86
|
+
signal,
|
|
87
|
+
maxRetries: 0,
|
|
88
|
+
transport: "sse",
|
|
89
|
+
toolChoice: "auto",
|
|
90
|
+
...(request.maxTokens === undefined ? {} : { maxTokens: request.maxTokens }),
|
|
91
|
+
})
|
|
92
|
+
.result()
|
|
93
|
+
.then((message) => {
|
|
94
|
+
attempt.settle(message.stopReason);
|
|
95
|
+
return message;
|
|
96
|
+
}, (error) => {
|
|
97
|
+
attempt.settle();
|
|
98
|
+
throw error;
|
|
99
|
+
});
|
|
100
|
+
}, request.retry ?? DEFAULT_DECISION_RETRY, signal);
|
|
89
101
|
}
|
|
90
102
|
catch (error) {
|
|
91
103
|
signal.throwIfAborted();
|
|
@@ -116,6 +128,11 @@ async function inferChat(request, model, signal, assertActive) {
|
|
|
116
128
|
error.usage = { inputTokens: response.usage.input, outputTokens: response.usage.output };
|
|
117
129
|
throw error;
|
|
118
130
|
}
|
|
131
|
+
if (calls[0].argumentsError) {
|
|
132
|
+
const error = new InvalidDecisionOutputError(unparseableArgumentsMessage(calls[0].argumentsError));
|
|
133
|
+
error.usage = { inputTokens: response.usage.input, outputTokens: response.usage.output };
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
119
136
|
// These are result arguments, never executable tool calls. Strict validation happens below.
|
|
120
137
|
return {
|
|
121
138
|
value: calls[0].arguments,
|
|
@@ -126,7 +143,7 @@ async function inferChat(request, model, signal, assertActive) {
|
|
|
126
143
|
}
|
|
127
144
|
function assertNotCancelled(signal) {
|
|
128
145
|
if (signal?.aborted)
|
|
129
|
-
throw
|
|
146
|
+
throw abortFailure(signal);
|
|
130
147
|
}
|
|
131
148
|
function resolveCandidate(id, request) {
|
|
132
149
|
if (typeof id !== "string" || !id.trim() || id.trim() !== id || id === "auto")
|
|
@@ -145,6 +162,14 @@ function resolveCandidate(id, request) {
|
|
|
145
162
|
return { kind: "classifier", fullId: id, model: classifier };
|
|
146
163
|
throw new Error(`Structured output model is unavailable or not a chat or classifier model: ${id}`);
|
|
147
164
|
}
|
|
165
|
+
function resolveCandidateOutcome(id, request) {
|
|
166
|
+
try {
|
|
167
|
+
return { candidate: resolveCandidate(id, request) };
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
return { id, failure: error instanceof Error ? error : new Error(String(error)) };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
148
173
|
class ClassifierDecisionError extends Error {
|
|
149
174
|
/** Status and error type only; provider bodies can echo private state. */
|
|
150
175
|
detail;
|
|
@@ -156,7 +181,7 @@ class ClassifierDecisionError extends Error {
|
|
|
156
181
|
}
|
|
157
182
|
}
|
|
158
183
|
const CLASSIFIER_ABORTED = "Structured output classifier request was aborted; no fallback was attempted.";
|
|
159
|
-
async function inferClassifier(request, selected, signal) {
|
|
184
|
+
async function inferClassifier(request, selected, signal, trace) {
|
|
160
185
|
const classify = request.modelRegistry.classify;
|
|
161
186
|
if (!classify)
|
|
162
187
|
throw new ClassifierDecisionError();
|
|
@@ -169,6 +194,7 @@ async function inferClassifier(request, selected, signal) {
|
|
|
169
194
|
},
|
|
170
195
|
]));
|
|
171
196
|
let result;
|
|
197
|
+
const attempt = trace.begin();
|
|
172
198
|
try {
|
|
173
199
|
result = await classify.call(request.modelRegistry, selected.model, { state: request.state, questions }, {
|
|
174
200
|
signal,
|
|
@@ -184,6 +210,7 @@ async function inferClassifier(request, selected, signal) {
|
|
|
184
210
|
signal.throwIfAborted();
|
|
185
211
|
if (!result || typeof result !== "object")
|
|
186
212
|
throw new ClassifierDecisionError();
|
|
213
|
+
attempt.settle(result.stopReason);
|
|
187
214
|
if (result.stopReason === "aborted")
|
|
188
215
|
throw new TerminalDecisionError(CLASSIFIER_ABORTED);
|
|
189
216
|
if (result.stopReason !== "stop")
|
|
@@ -209,6 +236,11 @@ async function inferClassifier(request, selected, signal) {
|
|
|
209
236
|
return { value, model: selected.fullId, responseModel: result.model, usage: { inputTokens: 0, outputTokens: 0 } };
|
|
210
237
|
}
|
|
211
238
|
export async function generateStructuredOutput(request) {
|
|
239
|
+
assertNotCancelled(request.signal);
|
|
240
|
+
const timeoutMs = resolveDecisionDeadlineMs(request.timeoutMs);
|
|
241
|
+
return withinDecisionDeadline(request.signal, timeoutMs, ({ signal, trace }) => generateWithinDeadline({ ...request, signal }, trace));
|
|
242
|
+
}
|
|
243
|
+
async function generateWithinDeadline(request, trace) {
|
|
212
244
|
assertNotCancelled(request.signal);
|
|
213
245
|
const current = request.currentModel;
|
|
214
246
|
if (current && (current.id === "auto" || !isModelType(current, "chat")))
|
|
@@ -219,6 +251,7 @@ export async function generateStructuredOutput(request) {
|
|
|
219
251
|
throw new Error("Structured output requires complete judgment instructions.");
|
|
220
252
|
const snapshot = {
|
|
221
253
|
...request,
|
|
254
|
+
trace,
|
|
222
255
|
state: jsonSnapshot(request.state),
|
|
223
256
|
schema: jsonSnapshot(request.schema),
|
|
224
257
|
instructions: request.instructions,
|
|
@@ -230,12 +263,20 @@ export async function generateStructuredOutput(request) {
|
|
|
230
263
|
];
|
|
231
264
|
if (!ids[0])
|
|
232
265
|
throw new Error("Structured output requires model or currentModel.");
|
|
233
|
-
const
|
|
266
|
+
const resolutions = [...new Set(ids)].map((id) => resolveCandidateOutcome(id, request));
|
|
267
|
+
const resolutionFailures = resolutions.filter((resolution) => "failure" in resolution);
|
|
268
|
+
if (resolutionFailures.length === resolutions.length)
|
|
269
|
+
throw resolutionFailures[0].failure;
|
|
234
270
|
const choices = compileChoiceSchema(snapshot.schema);
|
|
235
271
|
const modelAttempts = [];
|
|
236
272
|
let lastDiagnostic;
|
|
237
|
-
for (const
|
|
273
|
+
for (const resolution of resolutions) {
|
|
238
274
|
assertNotCancelled(request.signal);
|
|
275
|
+
if ("failure" in resolution) {
|
|
276
|
+
modelAttempts.push({ model: resolution.id, error: resolution.failure.message });
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const candidate = resolution.candidate;
|
|
239
280
|
if (candidate.kind === "classifier" && !choices) {
|
|
240
281
|
modelAttempts.push({
|
|
241
282
|
model: candidate.fullId,
|
|
@@ -274,7 +315,10 @@ export async function generateStructuredOutput(request) {
|
|
|
274
315
|
: "Structured output inference failed; no fallback was attempted.");
|
|
275
316
|
}
|
|
276
317
|
lastDiagnostic = error instanceof OutputRepairExhaustedError ? error : undefined;
|
|
277
|
-
modelAttempts.push({
|
|
318
|
+
modelAttempts.push({
|
|
319
|
+
model: candidate.fullId,
|
|
320
|
+
error: error instanceof OutputRepairExhaustedError ? error.message : "Structured output inference failed.",
|
|
321
|
+
});
|
|
278
322
|
}
|
|
279
323
|
}
|
|
280
324
|
if (lastDiagnostic)
|
|
@@ -301,7 +345,7 @@ class TerminalDecisionError extends Error {
|
|
|
301
345
|
class CandidateProviderError extends Error {
|
|
302
346
|
}
|
|
303
347
|
async function inferDecision(request, repairs, validateDecision, fallbackModel) {
|
|
304
|
-
request.signal
|
|
348
|
+
request.signal.throwIfAborted();
|
|
305
349
|
validateMaxTokensOverride(request.maxTokens);
|
|
306
350
|
validateState(request.state);
|
|
307
351
|
if (!request.instructions?.trim())
|
|
@@ -340,13 +384,15 @@ async function inferDecision(request, repairs, validateDecision, fallbackModel)
|
|
|
340
384
|
}
|
|
341
385
|
let fallback;
|
|
342
386
|
const controller = new AbortController();
|
|
343
|
-
const abort = () => controller.abort(
|
|
344
|
-
request.signal
|
|
387
|
+
const abort = () => controller.abort(abortFailure(request.signal));
|
|
388
|
+
request.signal.addEventListener("abort", abort, { once: true });
|
|
345
389
|
const assertActive = () => controller.signal.throwIfAborted();
|
|
346
390
|
try {
|
|
347
|
-
if (request.signal
|
|
391
|
+
if (request.signal.aborted)
|
|
348
392
|
abort();
|
|
349
393
|
const usage = { inputTokens: 0, outputTokens: 0 };
|
|
394
|
+
let rejection;
|
|
395
|
+
let firstAttempt = request.trace.size;
|
|
350
396
|
for (let attempt = 0;;) {
|
|
351
397
|
assertActive();
|
|
352
398
|
const current = {
|
|
@@ -354,12 +400,12 @@ async function inferDecision(request, repairs, validateDecision, fallbackModel)
|
|
|
354
400
|
model: selected,
|
|
355
401
|
instructions: attempt === 0
|
|
356
402
|
? snapshot.instructions
|
|
357
|
-
: `${snapshot.instructions}\n\nThe previous response failed output validation. Return a complete valid decision satisfying the original schema, candidates and constraints. Do not change the task or invent values.`,
|
|
403
|
+
: `${snapshot.instructions}\n\nThe previous response failed output validation${rejection ? `: ${rejection}` : "."} Return a complete valid decision satisfying the original schema, candidates and constraints. Do not change the task or invent values.`,
|
|
358
404
|
};
|
|
359
405
|
try {
|
|
360
406
|
const result = await raceWithAbortSignal(selected.kind === "classifier"
|
|
361
|
-
? inferClassifier(current, selected, controller.signal)
|
|
362
|
-
: inferChat(current, selected.model, controller.signal, assertActive), controller.signal);
|
|
407
|
+
? inferClassifier(current, selected, controller.signal, request.trace)
|
|
408
|
+
: inferChat(current, selected.model, controller.signal, assertActive, request.trace), controller.signal);
|
|
363
409
|
assertActive();
|
|
364
410
|
usage.inputTokens += result.usage.inputTokens;
|
|
365
411
|
usage.outputTokens += result.usage.outputTokens;
|
|
@@ -372,12 +418,14 @@ async function inferDecision(request, repairs, validateDecision, fallbackModel)
|
|
|
372
418
|
}
|
|
373
419
|
if (request.candidateFallback && selected.kind === "chat")
|
|
374
420
|
omitOptionalNulls(value, snapshot.schema);
|
|
375
|
-
|
|
376
|
-
|
|
421
|
+
const matchesSchema = Check(snapshot.schema, value);
|
|
422
|
+
if (!matchesSchema || (validateDecision && !validateDecision(value)))
|
|
423
|
+
throw new InvalidDecisionOutputError(schemaMismatchMessage(matchesSchema ? [] : describeSchemaErrors(snapshot.schema, value)));
|
|
377
424
|
assertActive();
|
|
378
425
|
return { ...result, value, usage, ...(fallback ? { fallback } : {}) };
|
|
379
426
|
}
|
|
380
427
|
catch (error) {
|
|
428
|
+
request.trace.settleOpen();
|
|
381
429
|
assertActive();
|
|
382
430
|
if (selected.kind === "classifier" &&
|
|
383
431
|
fallbackChat &&
|
|
@@ -394,7 +442,9 @@ async function inferDecision(request, repairs, validateDecision, fallbackModel)
|
|
|
394
442
|
};
|
|
395
443
|
reportModelRoutingDebug(`Classifier routing failed${error instanceof ClassifierDecisionError && error.detail ? ` (${error.detail})` : ""}; falling back to current chat model ${fallback.to} for this routing decision.`);
|
|
396
444
|
selected = { kind: "chat", fullId: fallback.to, model: fallbackChat };
|
|
445
|
+
rejection = undefined;
|
|
397
446
|
attempt = 0;
|
|
447
|
+
firstAttempt = request.trace.size;
|
|
398
448
|
continue;
|
|
399
449
|
}
|
|
400
450
|
if (!(error instanceof InvalidDecisionOutputError))
|
|
@@ -404,17 +454,22 @@ async function inferDecision(request, repairs, validateDecision, fallbackModel)
|
|
|
404
454
|
usage.outputTokens += error.usage.outputTokens;
|
|
405
455
|
}
|
|
406
456
|
if (attempt < repairs) {
|
|
457
|
+
rejection = error.message;
|
|
407
458
|
attempt++;
|
|
408
459
|
continue;
|
|
409
460
|
}
|
|
410
461
|
const scope = request.candidateFallback ? "Structured" : fallback ? "Chat fallback" : "Routing";
|
|
411
|
-
const
|
|
462
|
+
const exhaustion = repairs
|
|
463
|
+
? `${scope} output repair exhausted after ${repairs + 1} attempts.`
|
|
464
|
+
: "No repair request was made.";
|
|
465
|
+
const attempts = request.trace.describe(firstAttempt);
|
|
466
|
+
const message = `${error.message} ${exhaustion}${attempts ? ` ${attempts}` : ""}`;
|
|
412
467
|
throw request.candidateFallback ? new OutputRepairExhaustedError(message) : new Error(message);
|
|
413
468
|
}
|
|
414
469
|
}
|
|
415
470
|
}
|
|
416
471
|
finally {
|
|
417
|
-
request.signal
|
|
472
|
+
request.signal.removeEventListener("abort", abort);
|
|
418
473
|
}
|
|
419
474
|
}
|
|
420
475
|
/** Resolve only prerequisite routing inference. Does not execute the selected action or alter chat/tools. */
|
|
@@ -422,12 +477,13 @@ export async function routeModel(request,
|
|
|
422
477
|
/** Pure correlated-field validation against original candidates, never live admission checks. */
|
|
423
478
|
validateDecision) {
|
|
424
479
|
request.signal?.throwIfAborted();
|
|
425
|
-
const { settings, currentModel, ...inference } = request;
|
|
480
|
+
const { settings, currentModel, timeoutMs, ...inference } = request;
|
|
426
481
|
const model = resolveRouterModel({ settings, currentModel, modelRegistry: request.modelRegistry });
|
|
427
482
|
const fallback = model.kind !== "chat" && currentModel && currentModel.id !== "auto" && isModelType(currentModel, "chat")
|
|
428
483
|
? currentModel
|
|
429
484
|
: undefined;
|
|
430
485
|
const retry = inference.retry ?? settings.getRetrySettings?.() ?? DEFAULT_DECISION_RETRY;
|
|
431
|
-
|
|
486
|
+
const deadlineMs = resolveDecisionDeadlineMs(timeoutMs, settings.getHttpIdleTimeoutMs?.());
|
|
487
|
+
return withinDecisionDeadline(request.signal, deadlineMs, ({ signal, trace }) => inferDecision({ ...inference, model, retry, signal, trace }, 3, validateDecision, fallback));
|
|
432
488
|
}
|
|
433
489
|
//# sourceMappingURL=index.js.map
|