@bastani/atomic 0.9.4-alpha.6 → 0.9.4-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/builtin/cursor/package.json +3 -3
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +2 -2
- package/dist/builtin/subagents/CHANGELOG.md +14 -0
- package/dist/builtin/subagents/package.json +1 -1
- 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/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +6 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts +34 -4
- package/dist/core/agent-session-extension-bindings.d.ts +2 -0
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +10 -2
- 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/copilot-model-catalog.d.ts +25 -74
- package/dist/core/copilot-model-catalog.d.ts.map +1 -1
- package/dist/core/copilot-model-catalog.js +118 -68
- package/dist/core/copilot-model-catalog.js.map +1 -1
- 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/model-registry-builtins.d.ts.map +1 -1
- package/dist/core/model-registry-builtins.js +21 -3
- package/dist/core/model-registry-builtins.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/modes/interactive/interactive-model-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.js +1 -0
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/docs/models.md +3 -3
- package/docs/providers.md +1 -1
- package/docs/rpc.md +2 -2
- package/docs/sdk.md +1 -1
- package/docs/settings.md +2 -1
- package/docs/subagents.md +6 -0
- package/docs/workflows.md +2 -0
- package/npm-shrinkwrap.json +27 -27
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.4-alpha.7] - 2026-07-02
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added dynamic GitHub Copilot model population from the live CAPI `/models` catalog: picker-enabled, non-disabled plain chat ids are synthesized from catalog metadata (endpoints, capabilities, limits, and display names) while built-in `pi-ai` definitions still win, namespaced enterprise deployments such as `org/deployment/model` are skipped, and cached catalog metadata enables the same models on cold start.
|
|
10
|
+
- Added catalog-driven thinking-level gating for GitHub Copilot models so dynamically synthesized entries and bundled `pi-ai` Copilot models only offer the reasoning levels advertised by CAPI's `capabilities.supports.reasoning_effort` arrays, while models without an effort array keep their existing thinking behavior.
|
|
11
|
+
- Added a subagent watchdog escape hatch: setting `ATOMIC_SUBAGENT_ATTEMPT_IDLE_TIMEOUT_MS` or `ATOMIC_SUBAGENT_ATTEMPT_TIMEOUT_MS` to `0` (or a negative value) now disables the corresponding per-attempt timeout entirely; non-numeric values are ignored and the defaults apply. The `ATOMIC_SUBAGENT_ATTEMPT_KILL_GRACE_MS` SIGTERM→SIGKILL grace period intentionally cannot be disabled — `0`, negative, or non-numeric values fall back to its default so escalation always stays bounded ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Reverted the `@types/node` devDependency from `26.0.1` back to `24.12.4` to match the deliberate root `overrides` pin that aligns with upstream Pi's resolved lockfile (see [#1489](https://github.com/bastani-inc/atomic/pull/1489)); the dependabot bump in [#1548](https://github.com/bastani-inc/atomic/pull/1548) was a semantic no-op because the override forced resolution to `24.12.4`. Dependabot now ignores `@types/node` so it stops proposing inert bumps.
|
|
16
|
+
- Fixed the `read` tool to parse colon-delimited `file:START:END` (and grep-style `file:LINE:COL`) path selectors as a line range instead of leaving the leading number glued to the path (`file:395`), which produced a bogus `ENOENT` and pushed models (e.g. Opus) to fall back to `sed` ([#1585](https://github.com/bastani-inc/atomic/issues/1585)).
|
|
17
|
+
- Fixed GitHub Copilot models to use the live `max_output_tokens` value from the Copilot model catalog, preventing `github-copilot/claude-opus-4.8` from being capped by Atomic's stale built-in output-token limit after compaction ([#1582](https://github.com/bastani-inc/atomic/issues/1582)).
|
|
18
|
+
- Fixed active GitHub Copilot sessions to adopt live catalog model metadata as soon as the catalog loads, so fallback models refresh their supported reasoning levels without requiring a restart.
|
|
19
|
+
- Fixed workflow and subagent model fallback chains so request/context incompatibility failures (HTTP 400/413/422 bad/unprocessable/payload-too-large request, unsupported tool/parameter, context-length/context-window overflow, `invalid_request`/`bad_request`/`too_large` errors) advance to the next candidate instead of stopping. This ensures that when none of the configured fallback candidates can serve the current request, Atomic falls back to the currently selected user model rather than failing outright. Refusals, content-filter/safety blocks, cancellations, and task failures still stop the chain and are never retried on another model ([#1580](https://github.com/bastani-inc/atomic/issues/1580)).
|
|
20
|
+
- Fixed foreground and background subagent model attempts that produced no child activity from hanging indefinitely. Atomic now bounds each candidate with an idle watchdog and wall-clock cap, records retryable timeout attempts so fallback continues, and skips known unauthenticated providers before spawning while preserving unknown/custom providers and the current-model last resort ([#1580](https://github.com/bastani-inc/atomic/issues/1580)).
|
|
21
|
+
- Fixed the subagent per-attempt idle watchdog so an in-flight tool execution counts as activity: a slow, quiet tool call (long build or test run with no interim output) is no longer killed as a stalled attempt; the wall-clock cap still bounds such attempts ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
22
|
+
- Aligned the subagents and workflows model-failure classifiers' direct-message precedence and added a cross-package conformance test suite so the two classifier copies cannot silently drift ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
23
|
+
- Fixed the background subagent runner to spawn one default-model attempt when no model candidates were ever configured (no primary model, no fallbacks, and no current model), mirroring the foreground path instead of silently exiting 1 with no error and no spawn; an explicitly empty candidate list produced by pre-spawn auth filtering is still respected and surfaced as an error ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
24
|
+
|
|
5
25
|
## [0.9.4-alpha.6] - 2026-07-01
|
|
6
26
|
|
|
7
27
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/cursor",
|
|
3
|
-
"version": "0.9.4-alpha.
|
|
3
|
+
"version": "0.9.4-alpha.7",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Experimental first-party Atomic extension for Cursor OAuth, model discovery, and streaming provider registration.",
|
|
6
6
|
"contributors": [
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@bastani/atomic-natives": "0.9.4-alpha.
|
|
44
|
-
"@bufbuild/protobuf": "^2.
|
|
43
|
+
"@bastani/atomic-natives": "0.9.4-alpha.7",
|
|
44
|
+
"@bufbuild/protobuf": "^2.12.1",
|
|
45
45
|
"@earendil-works/pi-ai": "^0.80.3"
|
|
46
46
|
}
|
|
47
47
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/intercom",
|
|
3
|
-
"version": "0.9.4-alpha.
|
|
3
|
+
"version": "0.9.4-alpha.7",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
|
|
6
6
|
"contributors": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/mcp",
|
|
3
|
-
"version": "0.9.4-alpha.
|
|
3
|
+
"version": "0.9.4-alpha.7",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension that adapts MCP (Model Context Protocol) servers into the coding agent. Fork of: https://github.com/nicobailon/pi-mcp-adapter",
|
|
6
6
|
"contributors": [
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@modelcontextprotocol/ext-apps": "^1.7.
|
|
51
|
+
"@modelcontextprotocol/ext-apps": "^1.7.4",
|
|
52
52
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
53
53
|
"open": "^11.0.0",
|
|
54
54
|
"typebox": "^1.1.24",
|
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.4-alpha.7] - 2026-07-02
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed subagent model fallback so request/context incompatibility failures (HTTP 400/413/422 bad/unprocessable/payload-too-large request, unsupported tool/parameter, context-length/context-window overflow, `invalid_request`/`bad_request`/`too_large` errors) advance the chain to the next candidate instead of stopping. When none of the configured candidates can serve the request, the subagent now falls back to the current user-selected model. Refusals, content-filter/safety blocks, cancellations, and task failures still stop the chain ([#1580](https://github.com/bastani-inc/atomic/issues/1580)).
|
|
10
|
+
- Fixed foreground and background subagent model attempts that produced no child activity from hanging indefinitely. Each candidate attempt now has a conservative idle watchdog and absolute wall-clock cap, records a retryable timeout failure, and advances to the next fallback candidate; known providers without configured auth are skipped before spawning while unknown/custom providers and the current-model last resort are still attempted ([#1580](https://github.com/bastani-inc/atomic/issues/1580)).
|
|
11
|
+
- Fixed the per-attempt idle watchdog so an in-flight tool execution counts as activity: a slow, quiet tool call (long build or test run with no interim output) is no longer killed as a stalled attempt; the wall-clock cap still bounds such attempts ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
12
|
+
- Aligned the subagents model-failure classifier's direct-message precedence with the workflows classifier (direct-message classification now runs after nested cause/diagnostic traversal and before outer status/code classification), and added a cross-package conformance test suite so the two classifier copies cannot silently drift ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
13
|
+
- Fixed the background subagent runner to spawn one default-model attempt when no model candidates were ever configured (no primary model, no fallbacks, and no current model), mirroring the foreground path instead of silently exiting 1 with no error and no spawn. An explicitly empty candidate list produced by pre-spawn auth filtering is still respected and surfaced as an error ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Added a watchdog escape hatch: setting `ATOMIC_SUBAGENT_ATTEMPT_IDLE_TIMEOUT_MS` or `ATOMIC_SUBAGENT_ATTEMPT_TIMEOUT_MS` to `0` (or a negative value) now disables the corresponding per-attempt timeout entirely; non-numeric values are ignored and the defaults apply. The `ATOMIC_SUBAGENT_ATTEMPT_KILL_GRACE_MS` SIGTERM→SIGKILL grace period intentionally cannot be disabled — `0`, negative, or non-numeric values fall back to its default so escalation always stays bounded ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
18
|
+
|
|
5
19
|
## [0.9.4-alpha.6] - 2026-07-01
|
|
6
20
|
|
|
7
21
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/subagents",
|
|
3
|
-
"version": "0.9.4-alpha.
|
|
3
|
+
"version": "0.9.4-alpha.7",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification. Fork of: https://github.com/nicobailon/pi-subagents",
|
|
6
6
|
"contributors": [
|
|
@@ -12,6 +12,7 @@ import type { RunnerStep } from "../shared/parallel-utils.ts";
|
|
|
12
12
|
import { injectSingleOutputInstruction, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
13
13
|
import { ChainOutputValidationError, validateChainOutputBindings } from "../shared/chain-outputs.ts";
|
|
14
14
|
import { buildModelCandidates, resolveModelCandidate } from "../shared/model-fallback.ts";
|
|
15
|
+
import { filterSpawnableModelCandidates } from "../shared/model-candidate-filter.ts";
|
|
15
16
|
import { NESTED_RUNS_DIR, nestedResultsPath, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.ts";
|
|
16
17
|
import { createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
17
18
|
import { resolveExpectedWorktreeAgentCwd } from "../shared/worktree.ts";
|
|
@@ -57,6 +58,7 @@ export function executeAsyncChain(
|
|
|
57
58
|
const resultMode = params.resultMode ?? "chain";
|
|
58
59
|
const chainSkills = params.chainSkills ?? [];
|
|
59
60
|
const availableModels = params.availableModels;
|
|
61
|
+
const knownModelProviders = params.knownModelProviders;
|
|
60
62
|
const fastModeScope = resolveSubagentCodexFastModeScope(workflowStageSubagentGuard);
|
|
61
63
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
62
64
|
const firstStep = chain[0];
|
|
@@ -149,9 +151,16 @@ export function executeAsyncChain(
|
|
|
149
151
|
|
|
150
152
|
const primaryModel = resolveModelCandidate(behavior.model ?? a.model, availableModels, ctx.currentModelProvider);
|
|
151
153
|
const model = applyThinkingSuffix(primaryModel, a.thinking);
|
|
152
|
-
const
|
|
154
|
+
const rawModelCandidates = buildModelCandidates(behavior.model ?? a.model, a.fallbackModels, availableModels, ctx.currentModelProvider, ctx.currentModel, a.fallbackThinkingLevels)
|
|
153
155
|
.map((candidate) => applyThinkingSuffix(candidate, a.thinking))
|
|
154
156
|
.filter((candidate): candidate is string => typeof candidate === "string");
|
|
157
|
+
const filteredCandidates = filterSpawnableModelCandidates({
|
|
158
|
+
candidates: rawModelCandidates,
|
|
159
|
+
availableModels,
|
|
160
|
+
knownModelProviders,
|
|
161
|
+
currentModel: applyThinkingSuffix(ctx.currentModel, a.thinking),
|
|
162
|
+
});
|
|
163
|
+
const modelCandidates = filteredCandidates.candidates;
|
|
155
164
|
const fastModeSettings = getSubagentCodexFastModeSettings(stepCwd);
|
|
156
165
|
return {
|
|
157
166
|
agent: s.agent,
|
|
@@ -165,6 +174,7 @@ export function executeAsyncChain(
|
|
|
165
174
|
thinking: resolveEffectiveThinking(model, a.thinking),
|
|
166
175
|
...resolveSubagentModelFastModeMetadata({ model, modelCandidates, cwd: stepCwd, settings: fastModeSettings, scope: fastModeScope }),
|
|
167
176
|
modelCandidates,
|
|
177
|
+
modelAttempts: filteredCandidates.skippedAttempts,
|
|
168
178
|
codexFastModeSettings: fastModeSettings,
|
|
169
179
|
codexFastModeScope: fastModeScope,
|
|
170
180
|
tools: a.tools,
|
|
@@ -9,6 +9,7 @@ import { resolveChildCwd } from "../../shared/utils.ts";
|
|
|
9
9
|
import { applyThinkingSuffix, SUBAGENT_INTERCOM_SESSION_NAME_ENV } from "../shared/pi-args.ts";
|
|
10
10
|
import { injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
11
11
|
import { buildModelCandidates, resolveModelCandidate } from "../shared/model-fallback.ts";
|
|
12
|
+
import { filterSpawnableModelCandidates } from "../shared/model-candidate-filter.ts";
|
|
12
13
|
import { NESTED_RUNS_DIR, nestedResultsPath, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.ts";
|
|
13
14
|
import {
|
|
14
15
|
UNAVAILABLE_SUBAGENT_SKILL_ERROR,
|
|
@@ -50,6 +51,7 @@ export function executeAsyncSingle(
|
|
|
50
51
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
51
52
|
const skillNames = params.skills ?? agentConfig.skills ?? [];
|
|
52
53
|
const availableModels = params.availableModels;
|
|
54
|
+
const knownModelProviders = params.knownModelProviders;
|
|
53
55
|
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(skillNames, runnerCwd, ctx.cwd);
|
|
54
56
|
if (missingSkills.includes("subagent")) return formatAsyncStartError("single", UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
55
57
|
let systemPrompt = agentConfig.systemPrompt?.trim() ?? "";
|
|
@@ -84,9 +86,16 @@ export function executeAsyncSingle(
|
|
|
84
86
|
resolveModelCandidate(params.modelOverride ?? agentConfig.model, availableModels, ctx.currentModelProvider),
|
|
85
87
|
agentConfig.thinking,
|
|
86
88
|
);
|
|
87
|
-
const
|
|
89
|
+
const rawModelCandidates = buildModelCandidates(params.modelOverride ?? agentConfig.model, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider, ctx.currentModel)
|
|
88
90
|
.map((candidate) => applyThinkingSuffix(candidate, agentConfig.thinking))
|
|
89
91
|
.filter((candidate): candidate is string => typeof candidate === "string");
|
|
92
|
+
const filteredCandidates = filterSpawnableModelCandidates({
|
|
93
|
+
candidates: rawModelCandidates,
|
|
94
|
+
availableModels,
|
|
95
|
+
knownModelProviders,
|
|
96
|
+
currentModel: applyThinkingSuffix(ctx.currentModel, agentConfig.thinking),
|
|
97
|
+
});
|
|
98
|
+
const modelCandidates = filteredCandidates.candidates;
|
|
90
99
|
const fastModeSettings = getSubagentCodexFastModeSettings(runnerCwd);
|
|
91
100
|
const fastModeScope = resolveSubagentCodexFastModeScope(workflowStageSubagentGuard);
|
|
92
101
|
let spawnResult: AsyncSpawnResult = {};
|
|
@@ -103,6 +112,7 @@ export function executeAsyncSingle(
|
|
|
103
112
|
thinking: resolveEffectiveThinking(model, agentConfig.thinking),
|
|
104
113
|
...resolveSubagentModelFastModeMetadata({ model, modelCandidates, cwd: runnerCwd, settings: fastModeSettings, scope: fastModeScope }),
|
|
105
114
|
modelCandidates,
|
|
115
|
+
modelAttempts: filteredCandidates.skippedAttempts,
|
|
106
116
|
codexFastModeSettings: fastModeSettings,
|
|
107
117
|
codexFastModeScope: fastModeScope,
|
|
108
118
|
tools: agentConfig.tools,
|
|
@@ -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,
|