@bastani/atomic 0.9.3 → 0.9.4-alpha.10
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 +115 -0
- package/dist/builtin/cursor/CHANGELOG.md +30 -0
- package/dist/builtin/cursor/package.json +4 -4
- package/dist/builtin/intercom/CHANGELOG.md +30 -0
- package/dist/builtin/intercom/package.json +2 -2
- package/dist/builtin/mcp/CHANGELOG.md +30 -0
- package/dist/builtin/mcp/package.json +4 -4
- package/dist/builtin/subagents/CHANGELOG.md +51 -0
- package/dist/builtin/subagents/agents/code-simplifier.md +2 -2
- package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -2
- package/dist/builtin/subagents/agents/codebase-locator.md +2 -2
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +2 -2
- package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -2
- package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -2
- package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -2
- package/dist/builtin/subagents/agents/debugger.md +1 -1
- package/dist/builtin/subagents/agents/worker.md +2 -2
- package/dist/builtin/subagents/package.json +4 -4
- package/dist/builtin/subagents/src/runs/background/async-execution-chain.ts +11 -1
- package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +11 -1
- package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +2 -0
- package/dist/builtin/subagents/src/runs/background/subagent-runner-step.ts +27 -4
- package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +25 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +1 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +1 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +1 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +1 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-types.ts +2 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +3 -1
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +22 -0
- package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +15 -5
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +5 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +2 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +4 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +4 -1
- package/dist/builtin/subagents/src/runs/shared/attempt-watchdog.ts +126 -0
- package/dist/builtin/subagents/src/runs/shared/model-candidate-filter.ts +53 -0
- package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +52 -53
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +2 -0
- package/dist/builtin/subagents/src/shared/model-info.ts +16 -0
- package/dist/builtin/subagents/src/shared/types-config.ts +2 -0
- package/dist/builtin/web-access/CHANGELOG.md +30 -0
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/workflows/CHANGELOG.md +54 -0
- package/dist/builtin/workflows/README.md +4 -4
- package/dist/builtin/workflows/builtin/deep-research-codebase-utils.ts +24 -22
- package/dist/builtin/workflows/builtin/goal-runner.ts +12 -23
- package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +4 -4
- package/dist/builtin/workflows/builtin/ralph-models.ts +53 -75
- package/dist/builtin/workflows/package.json +2 -2
- package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +4 -2
- package/dist/builtin/workflows/src/engine/run-returned-status.ts +41 -0
- package/dist/builtin/workflows/src/engine/run.ts +6 -4
- package/dist/builtin/workflows/src/extension/config-file-loader.ts +1 -1
- package/dist/builtin/workflows/src/extension/config-loader.ts +1 -1
- package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +37 -12
- package/dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts +59 -1
- package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +1 -1
- package/dist/builtin/workflows/src/shared/store-public-types.ts +3 -2
- package/dist/builtin/workflows/src/shared/store-run-methods.ts +6 -1
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js +3 -2
- package/dist/bun/cli.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +13 -25
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +15 -5
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +10 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +18 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-auto-compaction.js +8 -1
- package/dist/core/agent-session-auto-compaction.js.map +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +14 -8
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-export.d.ts.map +1 -1
- package/dist/core/agent-session-export.js +4 -2
- package/dist/core/agent-session-export.js.map +1 -1
- package/dist/core/agent-session-extension-bindings.d.ts +5 -1
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +18 -7
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +9 -2
- 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 +2 -0
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +15 -2
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-retry.d.ts +2 -0
- package/dist/core/agent-session-retry.d.ts.map +1 -1
- package/dist/core/agent-session-retry.js +66 -7
- package/dist/core/agent-session-retry.js.map +1 -1
- package/dist/core/agent-session-state.d.ts.map +1 -1
- package/dist/core/agent-session-state.js +2 -2
- package/dist/core/agent-session-state.js.map +1 -1
- package/dist/core/agent-session-tool-hooks.d.ts +7 -1
- package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
- package/dist/core/agent-session-tool-hooks.js +26 -4
- package/dist/core/agent-session-tool-hooks.js.map +1 -1
- package/dist/core/agent-session-tree.d.ts.map +1 -1
- package/dist/core/agent-session-tree.js +3 -1
- package/dist/core/agent-session-tree.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +4 -0
- package/dist/core/agent-session-types.d.ts.map +1 -1
- package/dist/core/agent-session-types.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/copilot-model-catalog.d.ts +38 -74
- package/dist/core/copilot-model-catalog.d.ts.map +1 -1
- package/dist/core/copilot-model-catalog.js +177 -68
- package/dist/core/copilot-model-catalog.js.map +1 -1
- package/dist/core/copilot-model-static-fallbacks.d.ts +43 -0
- package/dist/core/copilot-model-static-fallbacks.d.ts.map +1 -0
- package/dist/core/copilot-model-static-fallbacks.js +50 -0
- package/dist/core/copilot-model-static-fallbacks.js.map +1 -0
- package/dist/core/copilot-model-synthesis.d.ts +10 -0
- package/dist/core/copilot-model-synthesis.d.ts.map +1 -0
- package/dist/core/copilot-model-synthesis.js +89 -0
- package/dist/core/copilot-model-synthesis.js.map +1 -0
- package/dist/core/extensions/api-types.d.ts +2 -1
- package/dist/core/extensions/api-types.d.ts.map +1 -1
- package/dist/core/extensions/api-types.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader-core.d.ts.map +1 -1
- package/dist/core/extensions/loader-core.js +3 -3
- package/dist/core/extensions/loader-core.js.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.d.ts +11 -0
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +79 -11
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/dist/core/extensions/session-events.d.ts +7 -1
- package/dist/core/extensions/session-events.d.ts.map +1 -1
- package/dist/core/extensions/session-events.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts +5 -3
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +56 -10
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/model-registry-builtins.d.ts.map +1 -1
- package/dist/core/model-registry-builtins.js +40 -6
- package/dist/core/model-registry-builtins.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +4 -3
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver-defaults.d.ts.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/resource-loader-reload.d.ts.map +1 -1
- package/dist/core/resource-loader-reload.js +6 -3
- package/dist/core/resource-loader-reload.js.map +1 -1
- package/dist/core/resource-loader-types.d.ts +6 -0
- package/dist/core/resource-loader-types.d.ts.map +1 -1
- package/dist/core/resource-loader-types.js.map +1 -1
- package/dist/core/session-manager-core.d.ts +2 -12
- package/dist/core/session-manager-core.d.ts.map +1 -1
- package/dist/core/session-manager-core.js +8 -16
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js +1 -1
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-manager-resource-accessors.d.ts +1 -0
- package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-resource-accessors.js +11 -0
- package/dist/core/settings-manager-resource-accessors.js.map +1 -1
- package/dist/core/settings-manager-ui-accessors.d.ts +2 -0
- package/dist/core/settings-manager-ui-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-ui-accessors.js +9 -0
- package/dist/core/settings-manager-ui-accessors.js.map +1 -1
- package/dist/core/settings-types.d.ts +2 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/timings.d.ts +7 -5
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +42 -24
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
- package/dist/core/tools/bash-pty-native.js +2 -2
- package/dist/core/tools/bash-pty-native.js.map +1 -1
- package/dist/core/tools/read-selectors.d.ts.map +1 -1
- package/dist/core/tools/read-selectors.js +19 -1
- package/dist/core/tools/read-selectors.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +12 -23
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/search-native.d.ts.map +1 -1
- package/dist/core/tools/search-native.js +2 -2
- package/dist/core/tools/search-native.js.map +1 -1
- package/dist/index-extensions.d.ts +1 -1
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main-session.d.ts.map +1 -1
- package/dist/main-session.js +14 -5
- package/dist/main-session.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +56 -32
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +22 -15
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -0
- package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.js +5 -5
- package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.js +8 -0
- package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-rendering.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-rendering.js +1 -0
- package/dist/modes/interactive/components/chat-session-host-rendering.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +6 -2
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +16 -5
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.js +6 -1
- package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
- package/dist/modes/interactive/interactive-deferred-startup.d.ts +2 -0
- package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-deferred-startup.js +59 -0
- package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -0
- package/dist/modes/interactive/interactive-editor-actions.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-editor-actions.js +1 -0
- package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-context.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-extension-context.js +1 -0
- package/dist/modes/interactive/interactive-extension-context.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +3 -0
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +6 -0
- 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 -0
- 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 +2 -0
- 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-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +1 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.js +2 -4
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.js +4 -3
- package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
- package/dist/modes/interactive/interactive-session-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-session-runtime.js +5 -2
- package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +11 -1
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +15 -13
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +28 -51
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +27 -51
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.js +19 -0
- package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +26 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/rpc-entry.d.ts +3 -0
- package/dist/rpc-entry.d.ts.map +1 -0
- package/dist/rpc-entry.js +12 -0
- package/dist/rpc-entry.js.map +1 -0
- package/dist/utils/image-convert.d.ts +1 -0
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js +21 -15
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-process.d.ts +18 -0
- package/dist/utils/image-process.d.ts.map +1 -0
- package/dist/utils/image-process.js +83 -0
- package/dist/utils/image-process.js.map +1 -0
- package/dist/utils/mime.d.ts.map +1 -1
- package/dist/utils/mime.js +41 -0
- package/dist/utils/mime.js.map +1 -1
- package/dist/utils/module-require.d.ts +11 -0
- package/dist/utils/module-require.d.ts.map +1 -0
- package/dist/utils/module-require.js +16 -0
- package/dist/utils/module-require.js.map +1 -0
- package/docs/compaction.md +2 -0
- package/docs/development.md +10 -0
- package/docs/extensions.md +11 -0
- package/docs/models.md +3 -3
- package/docs/providers.md +2 -1
- package/docs/rpc.md +60 -2
- package/docs/sdk.md +1 -1
- package/docs/settings.md +8 -5
- package/docs/subagents.md +6 -0
- package/docs/workflows.md +9 -5
- package/npm-shrinkwrap.json +4266 -0
- package/package.json +23 -13
|
@@ -26,6 +26,7 @@ export interface AsyncChainParams {
|
|
|
26
26
|
agents: AgentConfig[];
|
|
27
27
|
ctx: AsyncExecutionContext;
|
|
28
28
|
availableModels?: AvailableModelInfo[];
|
|
29
|
+
knownModelProviders?: string[];
|
|
29
30
|
cwd?: string;
|
|
30
31
|
maxOutput?: MaxOutputConfig;
|
|
31
32
|
artifactsDir?: string;
|
|
@@ -62,6 +63,7 @@ export interface AsyncSingleParams {
|
|
|
62
63
|
outputMode?: "inline" | "file-only";
|
|
63
64
|
modelOverride?: string;
|
|
64
65
|
availableModels?: AvailableModelInfo[];
|
|
66
|
+
knownModelProviders?: string[];
|
|
65
67
|
maxSubagentDepth: number;
|
|
66
68
|
workflowStageSubagentGuard?: boolean;
|
|
67
69
|
worktreeSetupHook?: string;
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import { formatModelAttemptNote, isRetryableModelFailure } from "../shared/model-fallback.ts";
|
|
18
18
|
import type { ArtifactPaths, ModelAttempt } from "../../shared/types.ts";
|
|
19
19
|
import type { RunPiStreamingResult, SingleStepContext, SubagentStep } from "./subagent-runner-types.ts";
|
|
20
|
-
import { fastModeForStepAttempt } from "./subagent-runner-utils.ts";
|
|
20
|
+
import { emptyUsage, fastModeForStepAttempt } from "./subagent-runner-utils.ts";
|
|
21
21
|
import { runPiStreaming } from "./subagent-runner-streaming.ts";
|
|
22
22
|
|
|
23
23
|
export { outputEntryFromAsyncResult };
|
|
@@ -61,14 +61,26 @@ export async function runSingleStep(
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
// `!== undefined` is intentional: an explicitly empty array means every candidate
|
|
65
|
+
// was removed by pre-spawn filtering (see filterSpawnableModelCandidates) and must
|
|
66
|
+
// be respected — do not "simplify" this back to `.length > 0`, which would spawn a
|
|
67
|
+
// doomed default attempt. Pre-spawn filtering always records each removal as a
|
|
68
|
+
// skipped attempt in step.modelAttempts, so an empty array WITHOUT skipped attempts
|
|
69
|
+
// means no candidates were configured at all (no primary model, no fallbacks, no
|
|
70
|
+
// current model); mirror the foreground path (execution-run-sync.ts `modelsToTry`)
|
|
71
|
+
// and run one default-model attempt instead of silently exiting with no attempt.
|
|
72
|
+
// The filtered-to-empty case is surfaced as an error below.
|
|
73
|
+
const preSkippedAttempts = step.modelAttempts ?? [];
|
|
74
|
+
const candidates = step.modelCandidates !== undefined && (step.modelCandidates.length > 0 || preSkippedAttempts.length > 0)
|
|
65
75
|
? step.modelCandidates
|
|
66
76
|
: step.model
|
|
67
77
|
? [step.model]
|
|
68
78
|
: [undefined];
|
|
69
79
|
const attemptedModels: string[] = [];
|
|
70
|
-
const modelAttempts: ModelAttempt[] = [];
|
|
71
|
-
const attemptNotes: string[] =
|
|
80
|
+
const modelAttempts: ModelAttempt[] = [...preSkippedAttempts];
|
|
81
|
+
const attemptNotes: string[] = modelAttempts
|
|
82
|
+
.filter((attempt) => !attempt.success && attempt.exitCode === null && attempt.error)
|
|
83
|
+
.map((attempt) => `[fallback] ${attempt.error}`);
|
|
72
84
|
const pendingAttemptNotes: string[] = [];
|
|
73
85
|
const eventsPath = path.join(path.dirname(ctx.outputFile), "events.jsonl");
|
|
74
86
|
let finalResult: RunPiStreamingResult | undefined;
|
|
@@ -205,6 +217,17 @@ export async function runSingleStep(
|
|
|
205
217
|
if (!tryNextModel) break;
|
|
206
218
|
}
|
|
207
219
|
|
|
220
|
+
if (!finalResult && candidates.length === 0 && modelAttempts.length > 0) {
|
|
221
|
+
finalResult = {
|
|
222
|
+
stderr: "",
|
|
223
|
+
exitCode: 1,
|
|
224
|
+
messages: [],
|
|
225
|
+
usage: emptyUsage(),
|
|
226
|
+
error: "No spawnable subagent model candidates after pre-spawn filtering.",
|
|
227
|
+
finalOutput: "",
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
208
231
|
const rawOutput = finalResult?.finalOutput ?? "";
|
|
209
232
|
const outputForPersistence = rawOutput;
|
|
210
233
|
const resolvedOutput = step.outputPath && finalResult?.exitCode === 0
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
shouldStartSubagentFinalDrain,
|
|
13
13
|
} from "../shared/final-drain.ts";
|
|
14
14
|
import { modelFailureMessage } from "../shared/model-fallback.ts";
|
|
15
|
+
import { createAttemptWatchdog } from "../shared/attempt-watchdog.ts";
|
|
15
16
|
import type { ChildEvent, ChildEventContext, RunPiStreamingResult } from "./subagent-runner-types.ts";
|
|
16
17
|
import { emptyUsage } from "./subagent-runner-utils.ts";
|
|
17
18
|
|
|
@@ -55,6 +56,7 @@ export function runPiStreaming(
|
|
|
55
56
|
let assistantError: string | undefined;
|
|
56
57
|
let assistantFailureSignal: unknown;
|
|
57
58
|
let interrupted = false;
|
|
59
|
+
let activeToolExecutions = 0;
|
|
58
60
|
const rawStdoutLines: string[] = [];
|
|
59
61
|
|
|
60
62
|
const writeOutputLine = (line: string) => {
|
|
@@ -99,6 +101,11 @@ export function runPiStreaming(
|
|
|
99
101
|
appendChildEvent(event);
|
|
100
102
|
onChildEvent?.(event);
|
|
101
103
|
|
|
104
|
+
// Track in-flight tool executions so the idle watchdog does not mistake a
|
|
105
|
+
// slow, quiet tool call for a stalled attempt.
|
|
106
|
+
if (event.type === "tool_execution_start") activeToolExecutions += 1;
|
|
107
|
+
else if (event.type === "tool_execution_end") activeToolExecutions = Math.max(0, activeToolExecutions - 1);
|
|
108
|
+
|
|
102
109
|
if (event.type === "tool_execution_start" && event.toolName) {
|
|
103
110
|
const toolArgs = extractToolArgsPreview(event.args ?? {});
|
|
104
111
|
writeOutputLine(toolArgs ? `${event.toolName}: ${toolArgs}` : event.toolName);
|
|
@@ -163,7 +170,22 @@ export function runPiStreaming(
|
|
|
163
170
|
let finalHardKillTimer: NodeJS.Timeout | undefined;
|
|
164
171
|
let settled = false;
|
|
165
172
|
const clearStdioGuard = attachPostExitStdioGuard(child, { idleMs: 2000, hardMs: 8000 });
|
|
173
|
+
const attemptWatchdog = createAttemptWatchdog({
|
|
174
|
+
child,
|
|
175
|
+
isSettled: () => settled,
|
|
176
|
+
// An in-flight tool execution counts as watchdog activity so a slow, quiet
|
|
177
|
+
// tool call is not mistaken for a stalled attempt. Caveat: the counter only
|
|
178
|
+
// decrements on tool_execution_end, so if a tool ends abnormally without its
|
|
179
|
+
// end event the idle watchdog is deferred indefinitely and the wall-clock cap
|
|
180
|
+
// becomes the sole backstop — do not lower the wall cap assuming idle fires.
|
|
181
|
+
isToolActive: () => activeToolExecutions > 0,
|
|
182
|
+
onTimeout(message) {
|
|
183
|
+
forcedTerminationSignal = true;
|
|
184
|
+
error ??= message;
|
|
185
|
+
},
|
|
186
|
+
});
|
|
166
187
|
child.stdout.on("data", (chunk: Buffer) => {
|
|
188
|
+
attemptWatchdog.activity();
|
|
167
189
|
const text = chunk.toString();
|
|
168
190
|
stdoutBuf += text;
|
|
169
191
|
const lines = stdoutBuf.split("\n");
|
|
@@ -172,6 +194,7 @@ export function runPiStreaming(
|
|
|
172
194
|
});
|
|
173
195
|
|
|
174
196
|
child.stderr.on("data", (chunk: Buffer) => {
|
|
197
|
+
attemptWatchdog.activity();
|
|
175
198
|
processStderrText(chunk.toString());
|
|
176
199
|
});
|
|
177
200
|
registerInterrupt?.(() => {
|
|
@@ -220,6 +243,7 @@ export function runPiStreaming(
|
|
|
220
243
|
registerInterrupt?.(undefined);
|
|
221
244
|
clearDrainTimers();
|
|
222
245
|
clearStdioGuard();
|
|
246
|
+
attemptWatchdog.clear();
|
|
223
247
|
if (stdoutBuf.trim()) processStdoutLine(stdoutBuf);
|
|
224
248
|
if (stderrBuf.trim()) appendChildLine("subagent.child.stderr", stderrBuf);
|
|
225
249
|
outputStream.end();
|
|
@@ -246,6 +270,7 @@ export function runPiStreaming(
|
|
|
246
270
|
registerInterrupt?.(undefined);
|
|
247
271
|
clearDrainTimers();
|
|
248
272
|
clearStdioGuard();
|
|
273
|
+
attemptWatchdog.clear();
|
|
249
274
|
outputStream.end();
|
|
250
275
|
const finalOutput = getFinalOutput(messages) || rawStdoutLines.join("\n").trim();
|
|
251
276
|
const spawnErrorMessage = spawnError instanceof Error ? spawnError.message : String(spawnError);
|
|
@@ -96,6 +96,7 @@ export async function runDynamicParallelChainStep(input: {
|
|
|
96
96
|
agents: context.agents,
|
|
97
97
|
stepIndex,
|
|
98
98
|
availableModels: context.availableModels,
|
|
99
|
+
knownModelProviders: context.knownModelProviders,
|
|
99
100
|
chainDir: context.chainDir,
|
|
100
101
|
prev: state.prev,
|
|
101
102
|
originalTask: context.originalTask,
|
|
@@ -125,6 +125,7 @@ export async function runParallelChainTasks(input: ParallelChainRunInput): Promi
|
|
|
125
125
|
nestedRoute: input.nestedRoute,
|
|
126
126
|
modelOverride: effectiveModel,
|
|
127
127
|
availableModels: input.availableModels,
|
|
128
|
+
knownModelProviders: input.knownModelProviders,
|
|
128
129
|
currentModel: currentModelFullId(input.ctx.model),
|
|
129
130
|
preferredModelProvider: input.ctx.model?.provider,
|
|
130
131
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
@@ -78,6 +78,7 @@ export async function runStaticParallelChainStep(input: {
|
|
|
78
78
|
agents: context.agents,
|
|
79
79
|
stepIndex,
|
|
80
80
|
availableModels: context.availableModels,
|
|
81
|
+
knownModelProviders: context.knownModelProviders,
|
|
81
82
|
chainDir: context.chainDir,
|
|
82
83
|
prev: state.prev,
|
|
83
84
|
originalTask: context.originalTask,
|
|
@@ -126,6 +126,7 @@ export async function runSequentialChainStep(input: {
|
|
|
126
126
|
nestedRoute: context.params.nestedRoute,
|
|
127
127
|
modelOverride: effectiveModel,
|
|
128
128
|
availableModels: context.availableModels,
|
|
129
|
+
knownModelProviders: context.knownModelProviders,
|
|
129
130
|
currentModel: currentModelFullId(context.ctx.model),
|
|
130
131
|
preferredModelProvider: context.ctx.model?.provider,
|
|
131
132
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
@@ -109,6 +109,7 @@ export interface ParallelChainRunInput {
|
|
|
109
109
|
agents: AgentConfig[];
|
|
110
110
|
stepIndex: number;
|
|
111
111
|
availableModels: ModelInfo[];
|
|
112
|
+
knownModelProviders: string[];
|
|
112
113
|
chainDir: string;
|
|
113
114
|
prev: string;
|
|
114
115
|
originalTask: string;
|
|
@@ -179,6 +180,7 @@ export interface ChainRuntimeContext {
|
|
|
179
180
|
chainSkills: string[];
|
|
180
181
|
chainDir: string;
|
|
181
182
|
availableModels: ModelInfo[];
|
|
183
|
+
knownModelProviders: string[];
|
|
182
184
|
originalTask: string;
|
|
183
185
|
chainAgents: string[];
|
|
184
186
|
chainSteps: ChainStep[];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Public foreground chain execution API.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
5
|
+
import { collectKnownModelProviders, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
6
6
|
import {
|
|
7
7
|
createChainDir,
|
|
8
8
|
isDynamicParallelStep,
|
|
@@ -117,6 +117,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
117
117
|
let templates: ResolvedTemplates = resolveChainTemplates(chainSteps);
|
|
118
118
|
let tuiBehaviorOverrides: Awaited<ReturnType<typeof resolveChainClarification>>["tuiBehaviorOverrides"];
|
|
119
119
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
120
|
+
const knownModelProviders = collectKnownModelProviders(ctx.modelRegistry);
|
|
120
121
|
const context: ChainRuntimeContext = {
|
|
121
122
|
params,
|
|
122
123
|
agents,
|
|
@@ -140,6 +141,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
140
141
|
chainSkills,
|
|
141
142
|
chainDir,
|
|
142
143
|
availableModels,
|
|
144
|
+
knownModelProviders,
|
|
143
145
|
originalTask,
|
|
144
146
|
chainAgents,
|
|
145
147
|
chainSteps,
|
|
@@ -17,6 +17,7 @@ import { applyThinkingSuffix, buildPiArgs, cleanupTempDir } from "../shared/pi-a
|
|
|
17
17
|
import { getPiSpawnCommand } from "../shared/pi-spawn.ts";
|
|
18
18
|
import { assistantStopReason, isAssistantFailureStopReason, shouldStartSubagentFinalDrain } from "../shared/final-drain.ts";
|
|
19
19
|
import { modelFailureMessage } from "../shared/model-fallback.ts";
|
|
20
|
+
import { createAttemptWatchdog } from "../shared/attempt-watchdog.ts";
|
|
20
21
|
import {
|
|
21
22
|
createMutatingFailureState,
|
|
22
23
|
didMutatingToolFail,
|
|
@@ -204,6 +205,7 @@ export async function runSingleAttempt(
|
|
|
204
205
|
settled = true;
|
|
205
206
|
clearFinalDrainTimers();
|
|
206
207
|
clearStdioGuard();
|
|
208
|
+
attemptWatchdog.clear();
|
|
207
209
|
if (activityTimer) {
|
|
208
210
|
clearInterval(activityTimer);
|
|
209
211
|
activityTimer = undefined;
|
|
@@ -358,13 +360,31 @@ export async function runSingleAttempt(
|
|
|
358
360
|
|
|
359
361
|
let stderrBuf = "";
|
|
360
362
|
const clearStdioGuard = attachPostExitStdioGuard(proc, { idleMs: 2000, hardMs: 8000 });
|
|
363
|
+
const attemptWatchdog = createAttemptWatchdog({
|
|
364
|
+
child: proc,
|
|
365
|
+
isSettled: () => settled || processClosed || detached,
|
|
366
|
+
// A slow, quiet tool call (long build/test run) must not be mistaken for a
|
|
367
|
+
// stalled attempt: an in-flight tool execution counts as watchdog activity.
|
|
368
|
+
// Caveat: `progress.currentTool` is cleared on tool_execution_end, so if a
|
|
369
|
+
// tool ends abnormally without emitting its end event the idle watchdog is
|
|
370
|
+
// deferred indefinitely and the wall-clock cap becomes the sole backstop for
|
|
371
|
+
// this attempt — do not lower the wall cap assuming idle always fires.
|
|
372
|
+
isToolActive: () => progress.currentTool !== undefined,
|
|
373
|
+
onTimeout(message) {
|
|
374
|
+
forcedTerminationSignal = true;
|
|
375
|
+
result.error ??= message;
|
|
376
|
+
progress.error = message;
|
|
377
|
+
},
|
|
378
|
+
});
|
|
361
379
|
proc.stdout.on("data", (d) => {
|
|
380
|
+
attemptWatchdog.activity();
|
|
362
381
|
buf += d.toString();
|
|
363
382
|
const lines = buf.split("\n");
|
|
364
383
|
buf = lines.pop() || "";
|
|
365
384
|
lines.forEach(processLine);
|
|
366
385
|
});
|
|
367
386
|
proc.stderr.on("data", (d) => {
|
|
387
|
+
attemptWatchdog.activity();
|
|
368
388
|
stderrBuf += d.toString();
|
|
369
389
|
});
|
|
370
390
|
proc.on("exit", () => {
|
|
@@ -374,6 +394,7 @@ export async function runSingleAttempt(
|
|
|
374
394
|
proc.on("close", (code, signal) => {
|
|
375
395
|
clearFinalDrainTimers();
|
|
376
396
|
clearStdioGuard();
|
|
397
|
+
attemptWatchdog.clear();
|
|
377
398
|
void jsonlWriter.close().catch(() => undefined);
|
|
378
399
|
cleanupTempDir(tempDir);
|
|
379
400
|
if (detached) {
|
|
@@ -394,6 +415,7 @@ export async function runSingleAttempt(
|
|
|
394
415
|
proc.on("error", (error) => {
|
|
395
416
|
clearFinalDrainTimers();
|
|
396
417
|
clearStdioGuard();
|
|
418
|
+
attemptWatchdog.clear();
|
|
397
419
|
void jsonlWriter.close().catch(() => undefined);
|
|
398
420
|
cleanupTempDir(tempDir);
|
|
399
421
|
if (!result.error) result.error = error instanceof Error ? error.message : String(error);
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
formatModelAttemptNote,
|
|
21
21
|
isRetryableModelFailure,
|
|
22
22
|
} from "../shared/model-fallback.ts";
|
|
23
|
+
import { filterSpawnableModelCandidates } from "../shared/model-candidate-filter.ts";
|
|
23
24
|
import { artifactOutputByResult, emptyUsage, modelFailureSignalByResult, sumUsage } from "./execution-utils.ts";
|
|
24
25
|
import { runSingleAttemptWithStructuredOutputRetries } from "./execution-structured-retries.ts";
|
|
25
26
|
import { shouldSuppressIntermediateRetryableFailureUpdate } from "./execution-updates.ts";
|
|
@@ -65,7 +66,7 @@ export async function runSync(
|
|
|
65
66
|
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${skillInjection}` : skillInjection;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
const
|
|
69
|
+
const rawCandidates = buildModelCandidates(
|
|
69
70
|
options.modelOverride ?? agent.model,
|
|
70
71
|
agent.fallbackModels,
|
|
71
72
|
options.availableModels,
|
|
@@ -73,13 +74,20 @@ export async function runSync(
|
|
|
73
74
|
options.currentModel,
|
|
74
75
|
agent.fallbackThinkingLevels,
|
|
75
76
|
);
|
|
77
|
+
const filteredCandidates = filterSpawnableModelCandidates({
|
|
78
|
+
candidates: rawCandidates,
|
|
79
|
+
availableModels: options.availableModels,
|
|
80
|
+
knownModelProviders: options.knownModelProviders,
|
|
81
|
+
currentModel: options.currentModel,
|
|
82
|
+
});
|
|
83
|
+
const candidates = filteredCandidates.candidates;
|
|
76
84
|
const fastModeCwd = options.cwd ?? runtimeCwd;
|
|
77
85
|
const fastModeSettings = getSubagentCodexFastModeSettings(fastModeCwd);
|
|
78
86
|
const fastModeScope = resolveSubagentCodexFastModeScope(options.workflowStageSubagentGuard);
|
|
79
87
|
const attemptedModels: string[] = [];
|
|
80
|
-
const modelAttempts: ModelAttempt[] = [];
|
|
88
|
+
const modelAttempts: ModelAttempt[] = [...filteredCandidates.skippedAttempts];
|
|
81
89
|
const aggregateUsage = emptyUsage();
|
|
82
|
-
const attemptNotes: string[] = [];
|
|
90
|
+
const attemptNotes: string[] = filteredCandidates.skippedAttempts.map((attempt) => `[fallback] ${attempt.error}`);
|
|
83
91
|
const pendingAttemptNotes: string[] = [];
|
|
84
92
|
let totalToolCount = 0;
|
|
85
93
|
let totalDurationMs = 0;
|
|
@@ -96,7 +104,7 @@ export async function runSync(
|
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
let lastResult: SingleResult | undefined;
|
|
99
|
-
const modelsToTry = candidates.length > 0 ? candidates : [undefined];
|
|
107
|
+
const modelsToTry = candidates.length > 0 ? candidates : (rawCandidates.length === 0 ? [undefined] : []);
|
|
100
108
|
for (let i = 0; i < modelsToTry.length; i++) {
|
|
101
109
|
const candidate = modelsToTry[i];
|
|
102
110
|
if (candidate) attemptedModels.push(candidate);
|
|
@@ -156,7 +164,9 @@ export async function runSync(
|
|
|
156
164
|
exitCode: 1,
|
|
157
165
|
messages: [],
|
|
158
166
|
usage: emptyUsage(),
|
|
159
|
-
error:
|
|
167
|
+
error: modelAttempts.length > 0
|
|
168
|
+
? "No spawnable subagent model candidates after pre-spawn filtering."
|
|
169
|
+
: "Subagent did not produce a result.",
|
|
160
170
|
} satisfies SingleResult;
|
|
161
171
|
|
|
162
172
|
result.usage = aggregateUsage;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { APP_NAME } from "@bastani/atomic";
|
|
3
3
|
import { currentModelFullId, resolveModelCandidate } from "../shared/model-fallback.ts";
|
|
4
|
-
import { toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
4
|
+
import { collectKnownModelProviders, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
5
5
|
import { normalizeSkillInput } from "../../agents/skills.ts";
|
|
6
6
|
import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
|
|
7
7
|
import {
|
|
@@ -76,6 +76,7 @@ export function runAsyncPath(data: ExecutionContextData, deps: ResolvedExecutorD
|
|
|
76
76
|
currentModel: currentModelFullId(ctx.model),
|
|
77
77
|
};
|
|
78
78
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
79
|
+
const knownModelProviders = collectKnownModelProviders(ctx.modelRegistry);
|
|
79
80
|
const depthPolicy = resolveSubagentDepthPolicy(ctx, deps.config.maxSubagentDepth);
|
|
80
81
|
const currentMaxSubagentDepth = depthPolicy.maxSubagentDepth;
|
|
81
82
|
const workflowStageSubagentGuard = depthPolicy.workflowStageSubagentGuard;
|
|
@@ -110,6 +111,7 @@ export function runAsyncPath(data: ExecutionContextData, deps: ResolvedExecutorD
|
|
|
110
111
|
agents,
|
|
111
112
|
ctx: asyncCtx,
|
|
112
113
|
availableModels,
|
|
114
|
+
knownModelProviders,
|
|
113
115
|
cwd: effectiveCwd,
|
|
114
116
|
maxOutput: params.maxOutput,
|
|
115
117
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
@@ -139,6 +141,7 @@ export function runAsyncPath(data: ExecutionContextData, deps: ResolvedExecutorD
|
|
|
139
141
|
agents,
|
|
140
142
|
ctx: asyncCtx,
|
|
141
143
|
availableModels,
|
|
144
|
+
knownModelProviders,
|
|
142
145
|
cwd: effectiveCwd,
|
|
143
146
|
maxOutput: params.maxOutput,
|
|
144
147
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
@@ -181,6 +184,7 @@ export function runAsyncPath(data: ExecutionContextData, deps: ResolvedExecutorD
|
|
|
181
184
|
agentConfig: a,
|
|
182
185
|
ctx: asyncCtx,
|
|
183
186
|
availableModels,
|
|
187
|
+
knownModelProviders,
|
|
184
188
|
cwd: effectiveCwd,
|
|
185
189
|
maxOutput: params.maxOutput,
|
|
186
190
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
@@ -38,6 +38,7 @@ interface ForegroundParallelRunInput {
|
|
|
38
38
|
maxSubagentDepths: number[];
|
|
39
39
|
workflowStageSubagentGuard?: boolean;
|
|
40
40
|
availableModels: ModelInfo[];
|
|
41
|
+
knownModelProviders: string[];
|
|
41
42
|
modelOverrides: (string | undefined)[];
|
|
42
43
|
behaviors: ResolvedStepBehavior[];
|
|
43
44
|
firstProgressIndex: number;
|
|
@@ -110,6 +111,7 @@ export async function runForegroundParallelTasks(input: ForegroundParallelRunInp
|
|
|
110
111
|
nestedRoute: input.foregroundControl?.nestedRoute,
|
|
111
112
|
modelOverride: input.modelOverrides[index],
|
|
112
113
|
availableModels: input.availableModels,
|
|
114
|
+
knownModelProviders: input.knownModelProviders,
|
|
113
115
|
preferredModelProvider: input.ctx.model?.provider,
|
|
114
116
|
currentModel: currentModelFullId(input.ctx.model),
|
|
115
117
|
skills: effectiveSkills === false ? [] : effectiveSkills,
|
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { APP_NAME } from "@bastani/atomic";
|
|
3
3
|
import { ChainClarifyComponent, type ChainClarifyResult } from "./chain-clarify.ts";
|
|
4
4
|
import { currentModelFullId, resolveModelCandidate } from "../shared/model-fallback.ts";
|
|
5
|
-
import { toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
5
|
+
import { collectKnownModelProviders, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
6
6
|
import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.ts";
|
|
7
7
|
import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
|
|
8
8
|
import { recordRun } from "../shared/run-history.ts";
|
|
@@ -108,6 +108,7 @@ export async function runParallelPath(data: ExecutionContextData, deps: Resolved
|
|
|
108
108
|
|
|
109
109
|
const currentProvider = ctx.model?.provider;
|
|
110
110
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
111
|
+
const knownModelProviders = collectKnownModelProviders(ctx.modelRegistry);
|
|
111
112
|
let taskTexts = tasks.map((t) => t.task);
|
|
112
113
|
const skillOverrides: (string[] | false | undefined)[] = tasks.map((t) =>
|
|
113
114
|
normalizeSkillInput(t.skill),
|
|
@@ -205,6 +206,7 @@ export async function runParallelPath(data: ExecutionContextData, deps: Resolved
|
|
|
205
206
|
agents,
|
|
206
207
|
ctx: asyncCtx,
|
|
207
208
|
availableModels,
|
|
209
|
+
knownModelProviders,
|
|
208
210
|
cwd: effectiveCwd,
|
|
209
211
|
maxOutput: params.maxOutput,
|
|
210
212
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
@@ -281,6 +283,7 @@ export async function runParallelPath(data: ExecutionContextData, deps: Resolved
|
|
|
281
283
|
paramsCwd: effectiveCwd,
|
|
282
284
|
workflowStageSubagentGuard,
|
|
283
285
|
availableModels,
|
|
286
|
+
knownModelProviders,
|
|
284
287
|
modelOverrides,
|
|
285
288
|
behaviors,
|
|
286
289
|
firstProgressIndex: parallelProgressPrecreated ? -1 : firstProgressIndex,
|
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { APP_NAME } from "@bastani/atomic";
|
|
3
3
|
import { ChainClarifyComponent, type ChainClarifyResult } from "./chain-clarify.ts";
|
|
4
4
|
import { currentModelFullId, resolveModelCandidate } from "../shared/model-fallback.ts";
|
|
5
|
-
import { toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
5
|
+
import { collectKnownModelProviders, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
6
6
|
import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.ts";
|
|
7
7
|
import { recordRun } from "../shared/run-history.ts";
|
|
8
8
|
import { getSingleResultOutput, compactForegroundDetails } from "../../shared/utils.ts";
|
|
@@ -76,6 +76,7 @@ export async function runSinglePath(data: ExecutionContextData, deps: ResolvedEx
|
|
|
76
76
|
|
|
77
77
|
const currentProvider = ctx.model?.provider;
|
|
78
78
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
79
|
+
const knownModelProviders = collectKnownModelProviders(ctx.modelRegistry);
|
|
79
80
|
let task = params.task ?? "";
|
|
80
81
|
let modelOverride: string | undefined = resolveModelCandidate(
|
|
81
82
|
(params.model as string | undefined) ?? agentConfig.model,
|
|
@@ -145,6 +146,7 @@ export async function runSinglePath(data: ExecutionContextData, deps: ResolvedEx
|
|
|
145
146
|
agentConfig,
|
|
146
147
|
ctx: asyncCtx,
|
|
147
148
|
availableModels,
|
|
149
|
+
knownModelProviders,
|
|
148
150
|
cwd: effectiveCwd,
|
|
149
151
|
maxOutput: params.maxOutput,
|
|
150
152
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
@@ -246,6 +248,7 @@ export async function runSinglePath(data: ExecutionContextData, deps: ResolvedEx
|
|
|
246
248
|
index: 0,
|
|
247
249
|
modelOverride,
|
|
248
250
|
availableModels,
|
|
251
|
+
knownModelProviders,
|
|
249
252
|
preferredModelProvider: currentProvider,
|
|
250
253
|
currentModel: currentModelFullId(ctx.model),
|
|
251
254
|
skills: effectiveSkills,
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { ChildProcess } from "node:child_process";
|
|
2
|
+
import { trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_IDLE_MS = 5 * 60_000;
|
|
5
|
+
const DEFAULT_WALL_MS = 60 * 60_000;
|
|
6
|
+
const DEFAULT_KILL_GRACE_MS = 3_000;
|
|
7
|
+
|
|
8
|
+
export interface AttemptTimeoutConfig {
|
|
9
|
+
idleMs: number;
|
|
10
|
+
wallMs: number;
|
|
11
|
+
killGraceMs: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AttemptWatchdog {
|
|
15
|
+
activity(): void;
|
|
16
|
+
clear(): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Parse a millisecond override from the environment. Non-numeric values are
|
|
20
|
+
* silently ignored (the default applies). Zero or negative values are clamped
|
|
21
|
+
* to `0`, which callers treat as "timeout disabled". */
|
|
22
|
+
function envMs(name: string): number | undefined {
|
|
23
|
+
const raw = process.env[name];
|
|
24
|
+
if (!raw) return undefined;
|
|
25
|
+
const value = Number(raw);
|
|
26
|
+
if (!Number.isFinite(value)) return undefined;
|
|
27
|
+
return value > 0 ? Math.floor(value) : 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function positiveEnvMs(name: string): number | undefined {
|
|
31
|
+
const value = envMs(name);
|
|
32
|
+
return value !== undefined && value > 0 ? value : undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function resolveAttemptTimeoutConfig(): AttemptTimeoutConfig {
|
|
36
|
+
return {
|
|
37
|
+
// `0` (or a negative value) disables the corresponding timeout entirely.
|
|
38
|
+
idleMs: envMs("ATOMIC_SUBAGENT_ATTEMPT_IDLE_TIMEOUT_MS") ?? DEFAULT_IDLE_MS,
|
|
39
|
+
wallMs: envMs("ATOMIC_SUBAGENT_ATTEMPT_TIMEOUT_MS") ?? DEFAULT_WALL_MS,
|
|
40
|
+
// The SIGTERM→SIGKILL grace period intentionally cannot be disabled: once a
|
|
41
|
+
// watchdog trips, escalation must always be bounded. `0`, negative, or
|
|
42
|
+
// non-numeric values fall back to the default.
|
|
43
|
+
killGraceMs: positiveEnvMs("ATOMIC_SUBAGENT_ATTEMPT_KILL_GRACE_MS") ?? DEFAULT_KILL_GRACE_MS,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function idleTimeoutMessage(idleMs: number): string {
|
|
48
|
+
return `Subagent model attempt timed out after ${idleMs}ms without child activity.`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function wallTimeoutMessage(wallMs: number): string {
|
|
52
|
+
return `Subagent model attempt timed out after ${wallMs}ms.`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createAttemptWatchdog(params: {
|
|
56
|
+
child: ChildProcess;
|
|
57
|
+
config?: Partial<AttemptTimeoutConfig>;
|
|
58
|
+
onTimeout: (message: string) => void;
|
|
59
|
+
isSettled: () => boolean;
|
|
60
|
+
/** Reports whether a tool call is currently executing in the child. A slow tool
|
|
61
|
+
* (long build, large test suite) can legitimately stay silent past the idle
|
|
62
|
+
* window, so an in-flight tool execution counts as activity and defers the idle
|
|
63
|
+
* trip. The wall-clock cap still bounds the whole attempt. */
|
|
64
|
+
isToolActive?: () => boolean;
|
|
65
|
+
}): AttemptWatchdog {
|
|
66
|
+
const config = { ...resolveAttemptTimeoutConfig(), ...(params.config ?? {}) };
|
|
67
|
+
let idleTimer: NodeJS.Timeout | undefined;
|
|
68
|
+
let wallTimer: NodeJS.Timeout | undefined;
|
|
69
|
+
let killTimer: NodeJS.Timeout | undefined;
|
|
70
|
+
let tripped = false;
|
|
71
|
+
|
|
72
|
+
const clearIdle = () => {
|
|
73
|
+
if (!idleTimer) return;
|
|
74
|
+
clearTimeout(idleTimer);
|
|
75
|
+
idleTimer = undefined;
|
|
76
|
+
};
|
|
77
|
+
const clearAll = () => {
|
|
78
|
+
clearIdle();
|
|
79
|
+
if (wallTimer) {
|
|
80
|
+
clearTimeout(wallTimer);
|
|
81
|
+
wallTimer = undefined;
|
|
82
|
+
}
|
|
83
|
+
if (killTimer) {
|
|
84
|
+
clearTimeout(killTimer);
|
|
85
|
+
killTimer = undefined;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const trip = (message: string) => {
|
|
89
|
+
if (tripped || params.isSettled()) return;
|
|
90
|
+
tripped = true;
|
|
91
|
+
clearIdle();
|
|
92
|
+
params.onTimeout(message);
|
|
93
|
+
trySignalChild(params.child, "SIGTERM");
|
|
94
|
+
killTimer = setTimeout(() => {
|
|
95
|
+
if (!params.isSettled()) trySignalChild(params.child, "SIGKILL");
|
|
96
|
+
}, config.killGraceMs);
|
|
97
|
+
killTimer.unref?.();
|
|
98
|
+
};
|
|
99
|
+
const scheduleIdle = () => {
|
|
100
|
+
clearIdle();
|
|
101
|
+
if (config.idleMs <= 0) return; // idle watchdog disabled
|
|
102
|
+
idleTimer = setTimeout(() => {
|
|
103
|
+
if (params.isToolActive?.()) {
|
|
104
|
+
// Do not kill a healthy attempt that is busy inside a slow tool call;
|
|
105
|
+
// re-arm the idle window and let the wall-clock cap bound the attempt.
|
|
106
|
+
scheduleIdle();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
trip(idleTimeoutMessage(config.idleMs));
|
|
110
|
+
}, config.idleMs);
|
|
111
|
+
idleTimer.unref?.();
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
scheduleIdle();
|
|
115
|
+
if (config.wallMs > 0) {
|
|
116
|
+
wallTimer = setTimeout(() => trip(wallTimeoutMessage(config.wallMs)), config.wallMs);
|
|
117
|
+
wallTimer.unref?.();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
activity() {
|
|
122
|
+
if (!tripped && !params.isSettled()) scheduleIdle();
|
|
123
|
+
},
|
|
124
|
+
clear: clearAll,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { splitKnownThinkingSuffix } from "../../shared/model-info.ts";
|
|
2
|
+
import type { ModelAttempt } from "../../shared/types.ts";
|
|
3
|
+
import type { AvailableModelInfo } from "./model-fallback.ts";
|
|
4
|
+
|
|
5
|
+
export interface FilteredModelCandidates {
|
|
6
|
+
candidates: string[];
|
|
7
|
+
skippedAttempts: ModelAttempt[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function providerFromModel(model: string | undefined): string | undefined {
|
|
11
|
+
if (!model) return undefined;
|
|
12
|
+
const { baseModel } = splitKnownThinkingSuffix(model);
|
|
13
|
+
const slash = baseModel.indexOf("/");
|
|
14
|
+
return slash > 0 ? baseModel.slice(0, slash) : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function skippedModelAttempt(model: string, reason: string): ModelAttempt {
|
|
18
|
+
return {
|
|
19
|
+
model,
|
|
20
|
+
success: false,
|
|
21
|
+
exitCode: null,
|
|
22
|
+
error: reason,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function filterSpawnableModelCandidates(params: {
|
|
27
|
+
candidates: string[];
|
|
28
|
+
availableModels?: AvailableModelInfo[];
|
|
29
|
+
knownModelProviders?: string[];
|
|
30
|
+
currentModel?: string;
|
|
31
|
+
}): FilteredModelCandidates {
|
|
32
|
+
const providersWithAvailableAuth = new Set((params.availableModels ?? []).map((model) => model.provider));
|
|
33
|
+
const knownProviders = new Set(params.knownModelProviders ?? []);
|
|
34
|
+
const currentModel = params.currentModel;
|
|
35
|
+
const filtered: string[] = [];
|
|
36
|
+
const skippedAttempts: ModelAttempt[] = [];
|
|
37
|
+
for (const candidate of params.candidates) {
|
|
38
|
+
if (currentModel && candidate === currentModel) {
|
|
39
|
+
filtered.push(candidate);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const provider = providerFromModel(candidate);
|
|
43
|
+
const shouldSkip = provider !== undefined
|
|
44
|
+
&& knownProviders.has(provider)
|
|
45
|
+
&& !providersWithAvailableAuth.has(provider);
|
|
46
|
+
if (!shouldSkip) {
|
|
47
|
+
filtered.push(candidate);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
skippedAttempts.push(skippedModelAttempt(candidate, `Skipped ${candidate}: provider '${provider}' has no configured API key/auth in the current session.`));
|
|
51
|
+
}
|
|
52
|
+
return { candidates: filtered, skippedAttempts };
|
|
53
|
+
}
|