@selesai/code 0.2.1 → 0.3.1
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/dist/config.d.ts +4 -15
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -45
- package/dist/config.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 +8 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/diagnostics.d.ts +1 -0
- package/dist/core/diagnostics.d.ts.map +1 -1
- package/dist/core/diagnostics.js.map +1 -1
- package/dist/core/model-registry.d.ts +4 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +101 -51
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/package-manager.d.ts +10 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +93 -8
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/package-manager.test.d.ts +2 -0
- package/dist/core/package-manager.test.d.ts.map +1 -0
- package/dist/core/package-manager.test.js +20 -0
- package/dist/core/package-manager.test.js.map +1 -0
- package/dist/core/resource-loader.d.ts +3 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +10 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/defaults/models.json +64 -113
- package/dist/extensions/caveman/caveman-instructions.cjs +39 -0
- package/dist/extensions/caveman/index.js +116 -0
- package/dist/extensions/caveman/package.json +8 -0
- package/dist/extensions/caveman/test/extension.test.js +185 -0
- package/dist/extensions/caveman/test/helpers.test.js +67 -0
- package/dist/extensions/copy-turn.ts +2 -2
- package/dist/extensions/pi-intercom/broker/broker.ts +2 -3
- package/dist/extensions/pi-intercom/broker/paths.ts +10 -5
- package/dist/extensions/pi-intercom/broker/spawn.ts +2 -3
- package/dist/extensions/pi-intercom/config.ts +2 -2
- package/dist/extensions/pi-intercom/index.ts +2 -2
- package/dist/extensions/pi-intercom/package.json +2 -2
- package/dist/extensions/pi-intercom/ui/compose.ts +3 -3
- package/dist/extensions/pi-intercom/ui/inline-message.ts +3 -3
- package/dist/extensions/pi-intercom/ui/session-list.ts +3 -3
- package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +2 -2
- package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +2 -1
- package/dist/extensions/pi-powerline-footer/index.ts +40 -47
- package/dist/extensions/pi-powerline-footer/package.json +2 -2
- package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +10 -10
- package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +1 -1
- package/dist/extensions/pi-powerline-footer/tests/working-vibes.test.ts +2 -2
- package/dist/extensions/pi-powerline-footer/theme.ts +6 -6
- package/dist/extensions/pi-powerline-footer/tps.ts +1 -1
- package/dist/extensions/pi-powerline-footer/types.ts +7 -7
- package/dist/extensions/pi-powerline-footer/welcome.ts +23 -16
- package/dist/extensions/pi-powerline-footer/working-vibes.ts +52 -59
- package/dist/extensions/pi-rewind-hook/index.ts +1 -1
- package/dist/extensions/pi-rewind-hook/package.json +1 -1
- package/dist/extensions/pi-subagents/README.md +224 -52
- package/dist/extensions/pi-subagents/agents/architect.md +188 -0
- package/dist/extensions/pi-subagents/agents/builder.md +120 -0
- package/dist/extensions/pi-subagents/agents/commentator.md +131 -0
- package/dist/extensions/pi-subagents/agents/explorer.md +51 -0
- package/dist/extensions/pi-subagents/agents/recapper.md +21 -0
- package/dist/extensions/pi-subagents/install.mjs +93 -0
- package/dist/extensions/pi-subagents/package.json +4 -5
- package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +1 -1
- package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +76 -13
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +179 -2
- package/dist/extensions/pi-subagents/src/agents/agent-memory.ts +254 -0
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +11 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +193 -19
- package/dist/extensions/pi-subagents/src/agents/chain-serializer.ts +27 -2
- package/dist/extensions/pi-subagents/src/extension/config.ts +27 -4
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +1 -7
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -2
- package/dist/extensions/pi-subagents/src/extension/index.ts +73 -71
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +369 -0
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +54 -10
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +200 -0
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +21 -253
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +519 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +195 -105
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +88 -2
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +67 -10
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +34 -4
- package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +166 -0
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +156 -1
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +515 -0
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +161 -44
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +1 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +3 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +167 -6
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +514 -0
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +28 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +873 -204
- package/dist/extensions/pi-subagents/src/runs/background/wait.ts +353 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +123 -27
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +174 -70
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +569 -81
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +45 -22
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +172 -21
- package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +128 -0
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +89 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +50 -1
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +35 -4
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +63 -45
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +125 -5
- package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +74 -0
- package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +52 -0
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +28 -5
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +16 -1
- package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +15 -2
- package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +212 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +133 -22
- package/dist/extensions/pi-subagents/src/shared/settings.ts +3 -1
- package/dist/extensions/pi-subagents/src/shared/types.ts +197 -4
- package/dist/extensions/pi-subagents/src/shared/utils.ts +108 -17
- package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +330 -0
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +135 -4
- package/dist/extensions/pi-subagents/src/tui/render.ts +32 -12
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +94 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2772 -0
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +1107 -0
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +463 -0
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +274 -0
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1535 -0
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +242 -0
- package/dist/extensions/pi-subagents/test/integration/doctor-executor.test.ts +128 -0
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +252 -0
- package/dist/extensions/pi-subagents/test/integration/foreground-result-size.test.ts +188 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +1306 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +1033 -0
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +397 -0
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +583 -0
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +691 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +892 -0
- package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +62 -0
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1824 -0
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1284 -0
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +127 -0
- package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +299 -0
- package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +37 -0
- package/dist/extensions/pi-subagents/test/support/helpers.ts +189 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +335 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +129 -0
- package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +184 -0
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +285 -0
- package/dist/extensions/pi-subagents/test/support/register-loader.mjs +15 -0
- package/dist/extensions/pi-subagents/test/support/ts-loader.mjs +108 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +421 -0
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +213 -0
- package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +343 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +1199 -0
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +556 -0
- package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +420 -0
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +579 -0
- package/dist/extensions/pi-subagents/test/unit/agent-scope.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +88 -0
- package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +24 -0
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +86 -0
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +221 -0
- package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +98 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +285 -0
- package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +112 -0
- package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +206 -0
- package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +159 -0
- package/dist/extensions/pi-subagents/test/unit/chain-serializer.test.ts +229 -0
- package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +225 -0
- package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +101 -0
- package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +228 -0
- package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +36 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +152 -0
- package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +72 -0
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +327 -0
- package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +161 -0
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +159 -0
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +217 -0
- package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +88 -0
- package/dist/extensions/pi-subagents/test/unit/file-coalescer.test.ts +68 -0
- package/dist/extensions/pi-subagents/test/unit/foreground-tool-call-compaction.test.ts +78 -0
- package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +415 -0
- package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +126 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +271 -0
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +173 -0
- package/dist/extensions/pi-subagents/test/unit/jsonl-writer.test.ts +119 -0
- package/dist/extensions/pi-subagents/test/unit/live-async-resume.test.ts +170 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +346 -0
- package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +62 -0
- package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +87 -0
- package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +145 -0
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +162 -0
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +470 -0
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +329 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +319 -0
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +70 -0
- package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +247 -0
- package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +113 -0
- package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +94 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +803 -0
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +209 -0
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +238 -0
- package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +118 -0
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +299 -0
- package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +408 -0
- package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +134 -0
- package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +257 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +209 -0
- package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +226 -0
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +294 -0
- package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +147 -0
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +997 -0
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +478 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +562 -0
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +213 -0
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +438 -0
- package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +54 -0
- package/dist/extensions/pi-subagents/test/unit/slash-chain-groups.test.ts +464 -0
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +255 -0
- package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +19 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +245 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +517 -0
- package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +72 -0
- package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +237 -0
- package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +60 -0
- package/dist/extensions/pi-subagents/test/unit/ts-loader.test.ts +33 -0
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +202 -0
- package/dist/extensions/pi-subagents/test/unit/types-fork-preamble.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/wait.test.ts +398 -0
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +88 -0
- package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +134 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +479 -0
- package/dist/extensions/pi-web-agent/package.json +2 -2
- package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +2 -2
- package/dist/extensions/pi-web-agent/src/changelog-notice.ts +5 -2
- package/dist/extensions/pi-web-agent/src/commands/web-agent-config.ts +1 -1
- package/dist/extensions/pi-web-agent/src/extension.ts +1 -1
- package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +6 -4
- package/dist/extensions/question/helpers.ts +3 -3
- package/dist/extensions/question/index.ts +2 -2
- package/dist/extensions/question/navigation.ts +2 -2
- package/dist/extensions/question/package.json +2 -2
- package/dist/extensions/question/question-list.ts +2 -2
- package/dist/extensions/question/tests/question-list.test.ts +2 -2
- package/dist/extensions/question/tui-adapter.ts +2 -2
- package/dist/extensions/question/types.ts +2 -2
- package/dist/extensions/question/ui-protocol.ts +2 -2
- package/dist/extensions/rtk.ts +2 -2
- package/dist/extensions/tokenin-onboarding.ts +2 -2
- package/dist/extensions/undo.ts +1 -1
- package/dist/extensions/web-agent-onboarding.ts +3 -3
- package/dist/extensions/workflow/adapter.ts +16 -8
- package/dist/extensions/workflow/extension.ts +1 -1
- package/dist/extensions/workflow/package.json +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +4 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +2 -0
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +19 -5
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/skills/caveman/SKILL.md +49 -0
- package/dist/skills/handoff-text/SKILL.md +13 -0
- package/dist/utils/thinking-tags.d.ts +7 -0
- package/dist/utils/thinking-tags.d.ts.map +1 -0
- package/dist/utils/thinking-tags.js +63 -0
- package/dist/utils/thinking-tags.js.map +1 -0
- package/docs/docs.json +4 -0
- package/docs/extensions.md +2 -0
- package/docs/settings.md +15 -0
- package/docs/shared-host-extensions.md +109 -0
- package/package.json +2 -2
|
@@ -4,7 +4,7 @@ import * as path from "node:path";
|
|
|
4
4
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
5
5
|
import type { ExtensionAPI, ExtensionContext } from "@selesai/code";
|
|
6
6
|
import { type AgentConfig, type AgentScope } from "../../agents/agents.ts";
|
|
7
|
-
import { getArtifactsDir } from "../../shared/artifacts.ts";
|
|
7
|
+
import { getArtifactsDir, getProjectChainRunsDir } from "../../shared/artifacts.ts";
|
|
8
8
|
import { ChainClarifyComponent, type ChainClarifyResult } from "./chain-clarify.ts";
|
|
9
9
|
import { toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
10
10
|
import { executeChain } from "./chain-execution.ts";
|
|
@@ -14,6 +14,7 @@ import { buildDoctorReport } from "../../extension/doctor.ts";
|
|
|
14
14
|
import { clearPendingForegroundControlNotices } from "../../extension/control-notices.ts";
|
|
15
15
|
import { runSync } from "./execution.ts";
|
|
16
16
|
import { resolveModelCandidate, resolveSubagentModelOverride } from "../shared/model-fallback.ts";
|
|
17
|
+
import type { ModelScopeConfig } from "../shared/model-scope.ts";
|
|
17
18
|
import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
|
|
18
19
|
import { recordRun } from "../shared/run-history.ts";
|
|
19
20
|
import {
|
|
@@ -32,6 +33,7 @@ import {
|
|
|
32
33
|
} from "../../shared/settings.ts";
|
|
33
34
|
import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.ts";
|
|
34
35
|
import { buildAsyncRunnerSteps, executeAsyncChain, executeAsyncSingle, formatAsyncStartedMessage, isAsyncAvailable } from "../background/async-execution.ts";
|
|
36
|
+
import type { ScheduledRunAction } from "../background/scheduled-runs.ts";
|
|
35
37
|
import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
|
|
36
38
|
import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
|
|
37
39
|
import { validateAcceptanceInput } from "../shared/acceptance.ts";
|
|
@@ -39,8 +41,11 @@ import { createForkContextResolver } from "../../shared/fork-context.ts";
|
|
|
39
41
|
import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
|
|
40
42
|
import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBridge, resolveIntercomSessionTarget, resolveSubagentIntercomTarget, type IntercomBridgeState } from "../../intercom/intercom-bridge.ts";
|
|
41
43
|
import { formatControlIntercomMessage, formatControlNoticeMessage, resolveControlConfig, shouldNotifyControlEvent } from "../shared/subagent-control.ts";
|
|
44
|
+
import { DEFAULT_TURN_BUDGET_GRACE_TURNS } from "../shared/turn-budget.ts";
|
|
45
|
+
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
42
46
|
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
43
|
-
import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, readStatus, resolveChildCwd } from "../../shared/utils.ts";
|
|
47
|
+
import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage } from "../../shared/utils.ts";
|
|
48
|
+
import { DEFAULT_GLOBAL_CONCURRENCY_LIMIT, Semaphore } from "../shared/parallel-utils.ts";
|
|
44
49
|
import {
|
|
45
50
|
attachNestedChildrenToResultChildren,
|
|
46
51
|
buildSubagentResultIntercomPayload,
|
|
@@ -50,11 +55,11 @@ import {
|
|
|
50
55
|
resolveSubagentResultStatus,
|
|
51
56
|
stripDetailsOutputsForIntercomReceipt,
|
|
52
57
|
} from "../../intercom/result-intercom.ts";
|
|
53
|
-
import { buildRevivedAsyncTask, interruptLiveAsyncResumeTarget, resolveAsyncResumeTarget } from "../background/async-resume.ts";
|
|
54
|
-
import { deliverInterruptRequest } from "../background/control-channel.ts";
|
|
58
|
+
import { buildRevivedAsyncTask, interruptLiveAsyncResumeTarget, resolveAsyncResumeTarget, resolveAsyncRunLocation } from "../background/async-resume.ts";
|
|
59
|
+
import { deliverInterruptRequest, requestAsyncSteer } from "../background/control-channel.ts";
|
|
55
60
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
56
61
|
import { resolveAsyncRootResultPath } from "../background/chain-root-attachment.ts";
|
|
57
|
-
import { createNestedRoute, readNestedControlResults, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedRunResolutionScope } from "../shared/nested-events.ts";
|
|
62
|
+
import { attachRootChildrenToSteps, createNestedRoute, readNestedControlResults, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedRunResolutionScope } from "../shared/nested-events.ts";
|
|
58
63
|
import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
|
|
59
64
|
import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
|
|
60
65
|
import { inspectSubagentStatus } from "../background/run-status.ts";
|
|
@@ -82,7 +87,11 @@ import {
|
|
|
82
87
|
type NestedRouteInfo,
|
|
83
88
|
type NestedRunSummary,
|
|
84
89
|
type ResolvedControlConfig,
|
|
90
|
+
type ResolvedTurnBudget,
|
|
91
|
+
type ResolvedToolBudget,
|
|
85
92
|
type SingleResult,
|
|
93
|
+
type ToolBudgetConfig,
|
|
94
|
+
type TurnBudgetConfig,
|
|
86
95
|
type SubagentRunMode,
|
|
87
96
|
type SubagentState,
|
|
88
97
|
ASYNC_DIR,
|
|
@@ -92,6 +101,7 @@ import {
|
|
|
92
101
|
SUBAGENT_CONTROL_EVENT,
|
|
93
102
|
SUBAGENT_CONTROL_INTERCOM_EVENT,
|
|
94
103
|
checkSubagentDepth,
|
|
104
|
+
resolveMaxSubagentSpawnsPerSession,
|
|
95
105
|
resolveTopLevelParallelConcurrency,
|
|
96
106
|
resolveTopLevelParallelMaxTasks,
|
|
97
107
|
resolveChildMaxSubagentDepth,
|
|
@@ -99,7 +109,7 @@ import {
|
|
|
99
109
|
wrapForkTask,
|
|
100
110
|
} from "../../shared/types.ts";
|
|
101
111
|
|
|
102
|
-
const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete"]);
|
|
112
|
+
const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete", "eject", "disable", "enable", "reset"]);
|
|
103
113
|
|
|
104
114
|
interface TaskParam {
|
|
105
115
|
agent: string;
|
|
@@ -113,6 +123,7 @@ interface TaskParam {
|
|
|
113
123
|
model?: string;
|
|
114
124
|
skill?: string | string[] | boolean;
|
|
115
125
|
acceptance?: AcceptanceInput;
|
|
126
|
+
toolBudget?: ToolBudgetConfig;
|
|
116
127
|
}
|
|
117
128
|
|
|
118
129
|
export interface SubagentParamsLike {
|
|
@@ -121,6 +132,8 @@ export interface SubagentParamsLike {
|
|
|
121
132
|
runId?: string;
|
|
122
133
|
dir?: string;
|
|
123
134
|
index?: number;
|
|
135
|
+
view?: "fleet" | "transcript";
|
|
136
|
+
lines?: number;
|
|
124
137
|
agent?: string;
|
|
125
138
|
task?: string;
|
|
126
139
|
message?: string;
|
|
@@ -132,6 +145,8 @@ export interface SubagentParamsLike {
|
|
|
132
145
|
async?: boolean;
|
|
133
146
|
timeoutMs?: number;
|
|
134
147
|
maxRuntimeMs?: number;
|
|
148
|
+
turnBudget?: TurnBudgetConfig;
|
|
149
|
+
toolBudget?: ToolBudgetConfig;
|
|
135
150
|
clarify?: boolean;
|
|
136
151
|
share?: boolean;
|
|
137
152
|
control?: ControlConfig;
|
|
@@ -147,6 +162,8 @@ export interface SubagentParamsLike {
|
|
|
147
162
|
agentScope?: unknown;
|
|
148
163
|
chainDir?: string;
|
|
149
164
|
acceptance?: AcceptanceInput;
|
|
165
|
+
schedule?: string;
|
|
166
|
+
scheduleName?: string;
|
|
150
167
|
}
|
|
151
168
|
|
|
152
169
|
interface ExecutorDeps {
|
|
@@ -154,10 +171,11 @@ interface ExecutorDeps {
|
|
|
154
171
|
state: SubagentState;
|
|
155
172
|
config: ExtensionConfig;
|
|
156
173
|
asyncByDefault: boolean;
|
|
174
|
+
handleScheduledRunAction?: (params: SubagentParamsLike, ctx: ExtensionContext) => Promise<AgentToolResult<Details>>;
|
|
157
175
|
tempArtifactsDir: string;
|
|
158
176
|
getSubagentSessionRoot: (parentSessionFile: string | null) => string;
|
|
159
177
|
expandTilde: (p: string) => string;
|
|
160
|
-
discoverAgents: (cwd: string, scope: AgentScope) => { agents: AgentConfig[] };
|
|
178
|
+
discoverAgents: (cwd: string, scope: AgentScope) => { agents: AgentConfig[]; modelScope?: ModelScopeConfig };
|
|
161
179
|
allowMutatingManagementActions?: boolean;
|
|
162
180
|
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
|
|
163
181
|
}
|
|
@@ -175,6 +193,7 @@ interface ExecutionContextData {
|
|
|
175
193
|
sessionDirForIndex: (idx?: number) => string;
|
|
176
194
|
sessionFileForIndex: (idx?: number) => string | undefined;
|
|
177
195
|
sessionFileForTask: (agentName: string, idx?: number) => string | undefined;
|
|
196
|
+
thinkingOverrideForTask: (agentName: string, idx?: number) => AgentConfig["thinking"] | undefined;
|
|
178
197
|
artifactConfig: ArtifactConfig;
|
|
179
198
|
artifactsDir: string;
|
|
180
199
|
backgroundRequestedWhileClarifying: boolean;
|
|
@@ -184,7 +203,11 @@ interface ExecutionContextData {
|
|
|
184
203
|
nestedRoute?: NestedRouteInfo;
|
|
185
204
|
timeoutMs?: number;
|
|
186
205
|
deadlineAt?: number;
|
|
206
|
+
turnBudget?: ResolvedTurnBudget;
|
|
207
|
+
toolBudget?: ResolvedToolBudget;
|
|
208
|
+
configToolBudget?: ResolvedToolBudget;
|
|
187
209
|
contextPolicy: AgentDefaultContextPolicy;
|
|
210
|
+
modelScope?: ModelScopeConfig;
|
|
188
211
|
}
|
|
189
212
|
|
|
190
213
|
function resolveRequestedCwd(runtimeCwd: string, requestedCwd: string | undefined): string {
|
|
@@ -233,6 +256,42 @@ function nestedResolutionScopeForExecutor(deps: ExecutorDeps): NestedRunResoluti
|
|
|
233
256
|
};
|
|
234
257
|
}
|
|
235
258
|
|
|
259
|
+
function trustedSessionRootsForStatus(ctx: ExtensionContext, deps: ExecutorDeps): string[] {
|
|
260
|
+
const roots = deps.config.defaultSessionDir ? [path.resolve(deps.expandTilde(deps.config.defaultSessionDir))] : [];
|
|
261
|
+
const parentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
|
|
262
|
+
if (parentSessionFile) roots.push(deps.getSubagentSessionRoot(parentSessionFile));
|
|
263
|
+
return [...new Set(roots)];
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function reserveSubagentSpawns(input: { state: SubagentState; config: ExtensionConfig; sessionId: string | null; requested: number; mode: "single" | "parallel" | "chain" }): AgentToolResult<Details> | undefined {
|
|
267
|
+
if (input.requested <= 0) return undefined;
|
|
268
|
+
if (input.state.subagentSpawns?.sessionId !== input.sessionId) {
|
|
269
|
+
input.state.subagentSpawns = { sessionId: input.sessionId, count: 0 };
|
|
270
|
+
}
|
|
271
|
+
const maxSpawns = resolveMaxSubagentSpawnsPerSession(input.config.maxSubagentSpawnsPerSession);
|
|
272
|
+
const used = input.state.subagentSpawns.count;
|
|
273
|
+
if (used + input.requested > maxSpawns) {
|
|
274
|
+
return {
|
|
275
|
+
content: [{ type: "text", text: `Subagent spawn limit reached for this session (${used}/${maxSpawns} used, ${input.requested} requested). Complete the work directly or start a new session.` }],
|
|
276
|
+
isError: true,
|
|
277
|
+
details: { mode: input.mode, results: [] },
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
input.state.subagentSpawns.count = used + input.requested;
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function countRequestedSubagentSpawns(params: SubagentParamsLike, config: ExtensionConfig): number {
|
|
285
|
+
if (params.tasks) return params.tasks.length;
|
|
286
|
+
if (params.chain) {
|
|
287
|
+
return params.chain.reduce((total, step) => {
|
|
288
|
+
if (isDynamicParallelStep(step)) return total + (step.expand.maxItems ?? config.chain?.dynamicFanout?.maxItems ?? 0);
|
|
289
|
+
return total + getStepAgents(step).length;
|
|
290
|
+
}, 0);
|
|
291
|
+
}
|
|
292
|
+
return params.agent ? 1 : 0;
|
|
293
|
+
}
|
|
294
|
+
|
|
236
295
|
function foregroundStatusResult(control: SubagentState["foregroundControls"] extends Map<string, infer T> ? T : never): AgentToolResult<Details> {
|
|
237
296
|
let nestedWarning: string | undefined;
|
|
238
297
|
try {
|
|
@@ -253,25 +312,70 @@ function foregroundStatusResult(control: SubagentState["foregroundControls"] ext
|
|
|
253
312
|
return { content: [{ type: "text", text: lines.join("\n") }], details: { mode: "management", results: [] } };
|
|
254
313
|
}
|
|
255
314
|
|
|
315
|
+
function trimRememberedForegroundRuns(state: SubagentState): void {
|
|
316
|
+
if (!state.foregroundRuns) return;
|
|
317
|
+
while (state.foregroundRuns.size > 50) {
|
|
318
|
+
const oldest = [...state.foregroundRuns.values()].sort((left, right) => left.updatedAt - right.updatedAt)[0];
|
|
319
|
+
if (!oldest) break;
|
|
320
|
+
state.foregroundRuns.delete(oldest.runId);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
256
324
|
function rememberForegroundRun(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; results: SingleResult[] }): void {
|
|
257
325
|
state.foregroundRuns ??= new Map();
|
|
326
|
+
const previous = state.foregroundRuns.get(input.runId);
|
|
327
|
+
const updatedAt = Date.now();
|
|
258
328
|
state.foregroundRuns.set(input.runId, {
|
|
259
329
|
runId: input.runId,
|
|
260
330
|
mode: input.mode,
|
|
261
331
|
cwd: input.cwd,
|
|
262
|
-
updatedAt
|
|
263
|
-
children: input.results.map((result, index) =>
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
332
|
+
updatedAt,
|
|
333
|
+
children: input.results.map((result, index) => {
|
|
334
|
+
const child = {
|
|
335
|
+
agent: result.agent,
|
|
336
|
+
index,
|
|
337
|
+
status: resolveSubagentResultStatus({ exitCode: result.exitCode, interrupted: result.interrupted, detached: result.detached }),
|
|
338
|
+
updatedAt,
|
|
339
|
+
...(result.exitCode !== undefined ? { exitCode: result.exitCode } : {}),
|
|
340
|
+
...(result.finalOutput ? { finalOutput: result.finalOutput } : {}),
|
|
341
|
+
...(result.sessionFile ? { sessionFile: result.sessionFile } : {}),
|
|
342
|
+
...(result.artifactPaths ? { artifactPaths: result.artifactPaths } : {}),
|
|
343
|
+
...(result.transcriptPath ? { transcriptPath: result.transcriptPath } : {}),
|
|
344
|
+
...(result.transcriptError ? { transcriptError: result.transcriptError } : {}),
|
|
345
|
+
...(result.detachedReason ? { detachedReason: result.detachedReason } : {}),
|
|
346
|
+
};
|
|
347
|
+
const recovered = previous?.children[index];
|
|
348
|
+
return child.status === "detached" && recovered && recovered.status !== "detached" ? recovered : child;
|
|
349
|
+
}),
|
|
269
350
|
});
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
351
|
+
trimRememberedForegroundRuns(state);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function updateRememberedForegroundChild(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; index: number; result: SingleResult }): void {
|
|
355
|
+
state.foregroundRuns ??= new Map();
|
|
356
|
+
const updatedAt = Date.now();
|
|
357
|
+
let run = state.foregroundRuns.get(input.runId);
|
|
358
|
+
if (!run) {
|
|
359
|
+
run = { runId: input.runId, mode: input.mode, cwd: input.cwd, updatedAt, children: [] };
|
|
360
|
+
state.foregroundRuns.set(input.runId, run);
|
|
361
|
+
}
|
|
362
|
+
run.updatedAt = updatedAt;
|
|
363
|
+
const child = run.children[input.index] ?? { agent: input.result.agent, index: input.index, status: "detached" as const };
|
|
364
|
+
run.children[input.index] = {
|
|
365
|
+
...child,
|
|
366
|
+
agent: input.result.agent,
|
|
367
|
+
index: input.index,
|
|
368
|
+
status: resolveSubagentResultStatus({ exitCode: input.result.exitCode, interrupted: input.result.interrupted, detached: false }),
|
|
369
|
+
updatedAt,
|
|
370
|
+
...(input.result.exitCode !== undefined ? { exitCode: input.result.exitCode } : {}),
|
|
371
|
+
...(input.result.finalOutput ? { finalOutput: input.result.finalOutput } : {}),
|
|
372
|
+
...(input.result.sessionFile ? { sessionFile: input.result.sessionFile } : {}),
|
|
373
|
+
...(input.result.artifactPaths ? { artifactPaths: input.result.artifactPaths } : {}),
|
|
374
|
+
...(input.result.transcriptPath ? { transcriptPath: input.result.transcriptPath } : {}),
|
|
375
|
+
...(input.result.transcriptError ? { transcriptError: input.result.transcriptError } : {}),
|
|
376
|
+
...(input.result.detachedReason ? { detachedReason: input.result.detachedReason } : {}),
|
|
377
|
+
};
|
|
378
|
+
trimRememberedForegroundRuns(state);
|
|
275
379
|
}
|
|
276
380
|
|
|
277
381
|
function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: "single" | "parallel" | "chain"; state: "complete"; agent: string; index: number; intercomTarget: string; cwd: string; sessionFile: string } | undefined {
|
|
@@ -463,6 +567,66 @@ function interruptAsyncRun(
|
|
|
463
567
|
}
|
|
464
568
|
}
|
|
465
569
|
|
|
570
|
+
function steerAsyncRun(input: {
|
|
571
|
+
state: SubagentState;
|
|
572
|
+
runId: string;
|
|
573
|
+
message: string;
|
|
574
|
+
index?: number;
|
|
575
|
+
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
|
|
576
|
+
location: { asyncDir: string | null; resolvedId?: string };
|
|
577
|
+
}): AgentToolResult<Details> {
|
|
578
|
+
if (!input.location.asyncDir) {
|
|
579
|
+
return {
|
|
580
|
+
content: [{ type: "text", text: `Async run '${input.runId}' has no live run directory to steer.` }],
|
|
581
|
+
isError: true,
|
|
582
|
+
details: { mode: "management", results: [] },
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
const status = reconcileAsyncRun(input.location.asyncDir, { kill: input.kill }).status;
|
|
586
|
+
if (!status || (status.state !== "running" && status.state !== "queued")) {
|
|
587
|
+
return {
|
|
588
|
+
content: [{ type: "text", text: `Async run '${input.runId}' is not running or queued and cannot be steered.` }],
|
|
589
|
+
isError: true,
|
|
590
|
+
details: { mode: "management", results: [] },
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
const steps = status.steps ?? [];
|
|
594
|
+
if (input.index !== undefined) {
|
|
595
|
+
if (input.index < 0 || input.index >= steps.length) {
|
|
596
|
+
return {
|
|
597
|
+
content: [{ type: "text", text: `Async run '${status.runId}' has ${steps.length} children. Index ${input.index} is out of range.` }],
|
|
598
|
+
isError: true,
|
|
599
|
+
details: { mode: "management", results: [] },
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
const targetStep = steps[input.index];
|
|
603
|
+
if (targetStep && targetStep.status !== "running" && targetStep.status !== "pending") {
|
|
604
|
+
return {
|
|
605
|
+
content: [{ type: "text", text: `Async run '${status.runId}' child ${input.index} is ${targetStep.status} and cannot be steered.` }],
|
|
606
|
+
isError: true,
|
|
607
|
+
details: { mode: "management", results: [] },
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
} else {
|
|
611
|
+
const running = steps.filter((step) => step.status === "running");
|
|
612
|
+
if (running.length === 0 && steps.length > 1) {
|
|
613
|
+
return {
|
|
614
|
+
content: [{ type: "text", text: `Async run '${status.runId}' has no running child yet. Provide index to steer a queued child.` }],
|
|
615
|
+
isError: true,
|
|
616
|
+
details: { mode: "management", results: [] },
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
requestAsyncSteer(input.location.asyncDir, { message: input.message, targetIndex: input.index, source: "steer-action" });
|
|
621
|
+
const tracked = input.state.asyncJobs.get(status.runId);
|
|
622
|
+
if (tracked) tracked.updatedAt = Date.now();
|
|
623
|
+
const childText = input.index !== undefined ? ` child ${input.index}` : " running child";
|
|
624
|
+
return {
|
|
625
|
+
content: [{ type: "text", text: `Steering queued for async run ${status.runId}${childText}. Delivery requires a live Pi child session that supports mid-run steering.` }],
|
|
626
|
+
details: { mode: "management", results: [] },
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
466
630
|
function duplicateNames(names: string[]): string[] {
|
|
467
631
|
const seen = new Set<string>();
|
|
468
632
|
const duplicates = new Set<string>();
|
|
@@ -577,7 +741,8 @@ function appendStepToAsyncChain(input: {
|
|
|
577
741
|
}
|
|
578
742
|
|
|
579
743
|
const scope: AgentScope = resolveExecutionAgentScope(input.params.agentScope);
|
|
580
|
-
const
|
|
744
|
+
const discoveredForAppend = input.deps.discoverAgents(input.requestCwd, scope);
|
|
745
|
+
const agents = discoveredForAppend.agents;
|
|
581
746
|
const contextPolicy = resolveExplicitContextPolicy(input.params);
|
|
582
747
|
const chainSkillInput = normalizeSkillInput(input.params.skill);
|
|
583
748
|
const chainSkills = chainSkillInput === false ? [] : (chainSkillInput ?? []);
|
|
@@ -588,6 +753,7 @@ function appendStepToAsyncChain(input: {
|
|
|
588
753
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
589
754
|
currentModelProvider: input.ctx.model?.provider,
|
|
590
755
|
currentModel: input.ctx.model,
|
|
756
|
+
modelScope: discoveredForAppend.modelScope,
|
|
591
757
|
};
|
|
592
758
|
const built = buildAsyncRunnerSteps(resolved.id, {
|
|
593
759
|
chain: wrapChainTasksForFork(input.params.chain, contextPolicy),
|
|
@@ -747,6 +913,22 @@ function directNestedAsyncInterrupt(target: ResolvedSubagentRunId & { kind: "nes
|
|
|
747
913
|
}
|
|
748
914
|
}
|
|
749
915
|
|
|
916
|
+
function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; index?: number }): AgentToolResult<Details> | undefined {
|
|
917
|
+
const run = input.target.match.run;
|
|
918
|
+
const asyncDir = resolveNestedAsyncDir(input.target.match.rootRunId, run);
|
|
919
|
+
if (!asyncDir) return undefined;
|
|
920
|
+
const status = reconcileAsyncRun(asyncDir, { resultsDir: path.join(RESULTS_DIR, "nested", input.target.match.rootRunId) }).status;
|
|
921
|
+
if (!status || (status.state !== "running" && status.state !== "queued")) return undefined;
|
|
922
|
+
const steps = status.steps ?? [];
|
|
923
|
+
if (input.index !== undefined) {
|
|
924
|
+
if (input.index < 0 || input.index >= steps.length) return { content: [{ type: "text", text: `Nested async run ${run.id} has ${steps.length} children. Index ${input.index} is out of range.` }], isError: true, details: { mode: "management", results: [] } };
|
|
925
|
+
const step = steps[input.index];
|
|
926
|
+
if (step && step.status !== "running" && step.status !== "pending") return { content: [{ type: "text", text: `Nested async run ${run.id} child ${input.index} is ${step.status} and cannot be steered.` }], isError: true, details: { mode: "management", results: [] } };
|
|
927
|
+
}
|
|
928
|
+
requestAsyncSteer(asyncDir, { message: input.message, targetIndex: input.index, source: "nested-steer" });
|
|
929
|
+
return { content: [{ type: "text", text: `Steering queued for nested async run ${run.id}. Delivery requires a live Pi child session that supports mid-run steering.` }], details: { mode: "management", results: [] } };
|
|
930
|
+
}
|
|
931
|
+
|
|
750
932
|
async function interruptNestedRun(target: ResolvedSubagentRunId & { kind: "nested" }): Promise<AgentToolResult<Details>> {
|
|
751
933
|
const run = target.match.run;
|
|
752
934
|
if (run.state === "complete") return { content: [{ type: "text", text: `Nested run ${run.id} is already complete and cannot be interrupted.` }], isError: true, details: { mode: "management", results: [] } };
|
|
@@ -766,6 +948,14 @@ async function resumeLiveNestedRun(input: { target: ResolvedSubagentRunId & { ki
|
|
|
766
948
|
return { content: [{ type: "text", text: `Nested run ${run.id} appears live but its owner route is not reachable. Wait for completion, then retry action='resume'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
767
949
|
}
|
|
768
950
|
|
|
951
|
+
function steerNestedRun(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; index?: number }): AgentToolResult<Details> {
|
|
952
|
+
const run = input.target.match.run;
|
|
953
|
+
if (run.state !== "running" && run.state !== "queued") return { content: [{ type: "text", text: `Nested run ${run.id} is ${run.state} and cannot be steered.` }], isError: true, details: { mode: "management", results: [] } };
|
|
954
|
+
const direct = directNestedAsyncSteer(input);
|
|
955
|
+
if (direct) return direct;
|
|
956
|
+
return { content: [{ type: "text", text: `Nested run ${run.id} is not a live async Pi child session with a steering inbox. action='steer' cannot target foreground nested runs.` }], isError: true, details: { mode: "management", results: [] } };
|
|
957
|
+
}
|
|
958
|
+
|
|
769
959
|
async function resumeAsyncRun(input: {
|
|
770
960
|
params: SubagentParamsLike;
|
|
771
961
|
requestCwd: string;
|
|
@@ -786,7 +976,14 @@ async function resumeAsyncRun(input: {
|
|
|
786
976
|
const parentSessionFile = input.ctx.sessionManager.getSessionFile() ?? null;
|
|
787
977
|
try {
|
|
788
978
|
const requestedId = input.params.id ?? input.params.runId;
|
|
789
|
-
|
|
979
|
+
let resolved: ResolvedSubagentRunId | undefined;
|
|
980
|
+
try {
|
|
981
|
+
resolved = requestedId ? resolveSubagentRunId(requestedId, { state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) }) : undefined;
|
|
982
|
+
} catch (error) {
|
|
983
|
+
const message = error instanceof Error ? error.message : "";
|
|
984
|
+
const asyncMatches = message.match(/async:/g)?.length ?? 0;
|
|
985
|
+
if (!isResumeAmbiguity(error) || !message.includes("foreground:") || asyncMatches !== 1) throw error;
|
|
986
|
+
}
|
|
790
987
|
if (resolved?.kind === "nested") {
|
|
791
988
|
if (attachChain) {
|
|
792
989
|
return {
|
|
@@ -857,13 +1054,14 @@ async function resumeAsyncRun(input: {
|
|
|
857
1054
|
input.deps.state.currentSessionId = resolveCurrentSessionId(input.ctx.sessionManager);
|
|
858
1055
|
const effectiveCwd = target.cwd ?? input.requestCwd;
|
|
859
1056
|
const scope: AgentScope = resolveExecutionAgentScope(input.params.agentScope);
|
|
860
|
-
const
|
|
1057
|
+
const discovered = input.deps.discoverAgents(effectiveCwd, scope);
|
|
1058
|
+
const discoveredAgents = discovered.agents;
|
|
1059
|
+
const modelScope = discovered.modelScope;
|
|
861
1060
|
const sessionName = resolveIntercomSessionTarget(input.deps.pi.getSessionName(), input.ctx.sessionManager.getSessionId());
|
|
862
1061
|
const intercomBridge = resolveIntercomBridge({
|
|
863
1062
|
config: input.deps.config.intercomBridge,
|
|
864
1063
|
context: input.params.context,
|
|
865
1064
|
orchestratorTarget: sessionName,
|
|
866
|
-
cwd: effectiveCwd,
|
|
867
1065
|
});
|
|
868
1066
|
const agents = intercomBridge.active
|
|
869
1067
|
? discoveredAgents.map((agent) => applyIntercomBridgeToAgent(agent, intercomBridge))
|
|
@@ -917,11 +1115,12 @@ async function resumeAsyncRun(input: {
|
|
|
917
1115
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
918
1116
|
currentModelProvider: input.ctx.model?.provider,
|
|
919
1117
|
currentModel: input.ctx.model,
|
|
1118
|
+
modelScope,
|
|
920
1119
|
},
|
|
921
1120
|
availableModels,
|
|
922
1121
|
cwd: effectiveCwd,
|
|
923
1122
|
maxOutput: input.params.maxOutput,
|
|
924
|
-
artifactsDir:
|
|
1123
|
+
artifactsDir: getArtifactsDir(parentSessionFile, effectiveCwd),
|
|
925
1124
|
artifactConfig,
|
|
926
1125
|
shareEnabled: input.params.share === true,
|
|
927
1126
|
sessionRoot: input.deps.getSubagentSessionRoot(parentSessionFile),
|
|
@@ -930,9 +1129,11 @@ async function resumeAsyncRun(input: {
|
|
|
930
1129
|
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
931
1130
|
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
932
1131
|
worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
|
|
1132
|
+
worktreeBaseDir: input.deps.config.worktreeBaseDir,
|
|
933
1133
|
controlConfig: resolveControlConfig(input.deps.config.control, input.params.control),
|
|
934
1134
|
controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
|
|
935
1135
|
childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
|
|
1136
|
+
globalConcurrencyLimit: input.deps.config.globalConcurrencyLimit,
|
|
936
1137
|
});
|
|
937
1138
|
if (result.isError) return result;
|
|
938
1139
|
const attachedId = result.details.asyncId ?? runId;
|
|
@@ -948,6 +1149,7 @@ async function resumeAsyncRun(input: {
|
|
|
948
1149
|
|
|
949
1150
|
const runId = randomUUID().slice(0, 8);
|
|
950
1151
|
const artifactConfig: ArtifactConfig = { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false };
|
|
1152
|
+
const artifactsDir = getArtifactsDir(parentSessionFile, effectiveCwd);
|
|
951
1153
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
952
1154
|
const result = executeAsyncSingle(runId, {
|
|
953
1155
|
agent: target.agent,
|
|
@@ -960,17 +1162,20 @@ async function resumeAsyncRun(input: {
|
|
|
960
1162
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
961
1163
|
currentModelProvider: input.ctx.model?.provider,
|
|
962
1164
|
currentModel: input.ctx.model,
|
|
1165
|
+
modelScope,
|
|
963
1166
|
},
|
|
964
1167
|
cwd: effectiveCwd,
|
|
965
1168
|
maxOutput: input.params.maxOutput,
|
|
966
|
-
artifactsDir
|
|
1169
|
+
artifactsDir,
|
|
967
1170
|
artifactConfig,
|
|
968
1171
|
shareEnabled: input.params.share === true,
|
|
969
1172
|
sessionRoot: input.deps.getSubagentSessionRoot(parentSessionFile),
|
|
970
1173
|
sessionFile: target.sessionFile,
|
|
1174
|
+
outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
|
|
971
1175
|
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
972
1176
|
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
973
1177
|
worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
|
|
1178
|
+
worktreeBaseDir: input.deps.config.worktreeBaseDir,
|
|
974
1179
|
controlConfig: resolveControlConfig(input.deps.config.control, input.params.control),
|
|
975
1180
|
controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
|
|
976
1181
|
childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
|
|
@@ -1303,6 +1508,32 @@ function resolveForegroundTimeout(params: SubagentParamsLike): { timeoutMs?: num
|
|
|
1303
1508
|
return { timeoutMs: rawTimeout ?? rawMaxRuntime };
|
|
1304
1509
|
}
|
|
1305
1510
|
|
|
1511
|
+
function resolveTurnBudget(params: SubagentParamsLike, config: ExtensionConfig): { turnBudget?: ResolvedTurnBudget; error?: string } {
|
|
1512
|
+
const raw = params.turnBudget ?? config.turnBudget;
|
|
1513
|
+
if (raw === undefined) return {};
|
|
1514
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return { error: "turnBudget must be an object with maxTurns and optional graceTurns." };
|
|
1515
|
+
if (typeof raw.maxTurns !== "number" || !Number.isInteger(raw.maxTurns) || raw.maxTurns < 1) {
|
|
1516
|
+
return { error: "turnBudget.maxTurns must be an integer >= 1." };
|
|
1517
|
+
}
|
|
1518
|
+
const graceTurns = raw.graceTurns ?? DEFAULT_TURN_BUDGET_GRACE_TURNS;
|
|
1519
|
+
if (typeof graceTurns !== "number" || !Number.isInteger(graceTurns) || graceTurns < 0) {
|
|
1520
|
+
return { error: "turnBudget.graceTurns must be an integer >= 0." };
|
|
1521
|
+
}
|
|
1522
|
+
return { turnBudget: { maxTurns: raw.maxTurns, graceTurns } };
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
function resolveToolBudget(raw: unknown, label = "toolBudget"): { toolBudget?: ResolvedToolBudget; error?: string } {
|
|
1526
|
+
const resolved = validateToolBudgetConfig(raw, label);
|
|
1527
|
+
return { toolBudget: resolved.budget, error: resolved.error };
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
function resolveEffectiveToolBudget(input: { stepBudget?: ToolBudgetConfig; runBudget?: ResolvedToolBudget; agentBudget?: ToolBudgetConfig; configBudget?: ToolBudgetConfig }): { toolBudget?: ResolvedToolBudget; error?: string } {
|
|
1531
|
+
if (input.stepBudget !== undefined) return resolveToolBudget(input.stepBudget, "toolBudget");
|
|
1532
|
+
if (input.runBudget !== undefined) return { toolBudget: input.runBudget };
|
|
1533
|
+
if (input.agentBudget !== undefined) return resolveToolBudget(input.agentBudget, "agent.toolBudget");
|
|
1534
|
+
return resolveToolBudget(input.configBudget, "config.toolBudget");
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1306
1537
|
function expandTopLevelTaskCounts(tasks: TaskParam[]): { tasks?: TaskParam[]; error?: string } {
|
|
1307
1538
|
const expanded: TaskParam[] = [];
|
|
1308
1539
|
for (let taskIndex = 0; taskIndex < tasks.length; taskIndex++) {
|
|
@@ -1391,6 +1622,7 @@ function toExecutionErrorResult(params: SubagentParamsLike, error: unknown): Age
|
|
|
1391
1622
|
function collectChainSessionFiles(
|
|
1392
1623
|
chain: ChainStep[],
|
|
1393
1624
|
sessionFileForTask: (agentName: string, idx?: number) => string | undefined,
|
|
1625
|
+
dynamicFanoutMaxItems?: number,
|
|
1394
1626
|
): (string | undefined)[] {
|
|
1395
1627
|
const sessionFiles: (string | undefined)[] = [];
|
|
1396
1628
|
let flatIndex = 0;
|
|
@@ -1403,7 +1635,11 @@ function collectChainSessionFiles(
|
|
|
1403
1635
|
continue;
|
|
1404
1636
|
}
|
|
1405
1637
|
if (isDynamicParallelStep(step)) {
|
|
1406
|
-
|
|
1638
|
+
const maxItems = step.expand.maxItems ?? dynamicFanoutMaxItems ?? 0;
|
|
1639
|
+
for (let itemIndex = 0; itemIndex < maxItems; itemIndex++) {
|
|
1640
|
+
sessionFiles.push(sessionFileForTask(step.parallel.agent, flatIndex));
|
|
1641
|
+
flatIndex++;
|
|
1642
|
+
}
|
|
1407
1643
|
continue;
|
|
1408
1644
|
}
|
|
1409
1645
|
sessionFiles.push(sessionFileForTask((step as SequentialStep).agent, flatIndex));
|
|
@@ -1412,6 +1648,35 @@ function collectChainSessionFiles(
|
|
|
1412
1648
|
return sessionFiles;
|
|
1413
1649
|
}
|
|
1414
1650
|
|
|
1651
|
+
function collectChainThinkingOverrides(
|
|
1652
|
+
chain: ChainStep[],
|
|
1653
|
+
thinkingOverrideForTask: (agentName: string, idx?: number) => AgentConfig["thinking"] | undefined,
|
|
1654
|
+
dynamicFanoutMaxItems?: number,
|
|
1655
|
+
): (AgentConfig["thinking"] | undefined)[] {
|
|
1656
|
+
const thinkingOverrides: (AgentConfig["thinking"] | undefined)[] = [];
|
|
1657
|
+
let flatIndex = 0;
|
|
1658
|
+
for (const step of chain) {
|
|
1659
|
+
if (isParallelStep(step)) {
|
|
1660
|
+
for (const task of step.parallel) {
|
|
1661
|
+
thinkingOverrides.push(thinkingOverrideForTask(task.agent, flatIndex));
|
|
1662
|
+
flatIndex++;
|
|
1663
|
+
}
|
|
1664
|
+
continue;
|
|
1665
|
+
}
|
|
1666
|
+
if (isDynamicParallelStep(step)) {
|
|
1667
|
+
const maxItems = step.expand.maxItems ?? dynamicFanoutMaxItems ?? 0;
|
|
1668
|
+
for (let itemIndex = 0; itemIndex < maxItems; itemIndex++) {
|
|
1669
|
+
thinkingOverrides.push(thinkingOverrideForTask(step.parallel.agent, flatIndex));
|
|
1670
|
+
flatIndex++;
|
|
1671
|
+
}
|
|
1672
|
+
continue;
|
|
1673
|
+
}
|
|
1674
|
+
thinkingOverrides.push(thinkingOverrideForTask((step as SequentialStep).agent, flatIndex));
|
|
1675
|
+
flatIndex++;
|
|
1676
|
+
}
|
|
1677
|
+
return thinkingOverrides;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1415
1680
|
function wrapChainTasksForFork(chain: ChainStep[], contextPolicy: AgentDefaultContextPolicy): ChainStep[] {
|
|
1416
1681
|
return chain.map((step, stepIndex) => {
|
|
1417
1682
|
if (isParallelStep(step)) {
|
|
@@ -1450,6 +1715,7 @@ function preflightForkSessionsForStaticTasks(
|
|
|
1450
1715
|
params: SubagentParamsLike,
|
|
1451
1716
|
contextPolicy: AgentDefaultContextPolicy,
|
|
1452
1717
|
sessionFileForTask: (agentName: string, idx?: number) => string | undefined,
|
|
1718
|
+
dynamicFanoutMaxItems?: number,
|
|
1453
1719
|
): void {
|
|
1454
1720
|
if (!contextPolicy.usesFork) return;
|
|
1455
1721
|
if (params.agent) {
|
|
@@ -1473,8 +1739,11 @@ function preflightForkSessionsForStaticTasks(
|
|
|
1473
1739
|
continue;
|
|
1474
1740
|
}
|
|
1475
1741
|
if (isDynamicParallelStep(step)) {
|
|
1476
|
-
|
|
1477
|
-
|
|
1742
|
+
const maxItems = step.expand.maxItems ?? dynamicFanoutMaxItems ?? 0;
|
|
1743
|
+
if (shouldForkAgent(contextPolicy, step.parallel.agent)) {
|
|
1744
|
+
for (let itemIndex = 0; itemIndex < maxItems; itemIndex++) sessionFileForTask(step.parallel.agent, flatIndex + itemIndex);
|
|
1745
|
+
}
|
|
1746
|
+
flatIndex += maxItems;
|
|
1478
1747
|
continue;
|
|
1479
1748
|
}
|
|
1480
1749
|
const sequential = step as SequentialStep;
|
|
@@ -1493,6 +1762,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1493
1762
|
sessionRoot,
|
|
1494
1763
|
sessionFileForIndex,
|
|
1495
1764
|
sessionFileForTask,
|
|
1765
|
+
thinkingOverrideForTask,
|
|
1496
1766
|
artifactConfig,
|
|
1497
1767
|
artifactsDir,
|
|
1498
1768
|
effectiveAsync,
|
|
@@ -1543,6 +1813,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1543
1813
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
1544
1814
|
currentModelProvider: ctx.model?.provider,
|
|
1545
1815
|
currentModel: ctx.model,
|
|
1816
|
+
modelScope: data.modelScope,
|
|
1546
1817
|
};
|
|
1547
1818
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
1548
1819
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
@@ -1553,7 +1824,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1553
1824
|
if (hasTasks && params.tasks) {
|
|
1554
1825
|
const agentConfigs = params.tasks.map((task) => agents.find((agent) => agent.name === task.agent));
|
|
1555
1826
|
const modelOverrides = params.tasks.map((task, index) =>
|
|
1556
|
-
resolveSubagentModelOverride(task.model ?? agentConfigs[index]?.model, ctx.model, availableModels, currentProvider),
|
|
1827
|
+
resolveSubagentModelOverride(task.model ?? agentConfigs[index]?.model, ctx.model, availableModels, currentProvider, { scope: data.modelScope, source: task.model ? "explicit" : "inherited" }),
|
|
1557
1828
|
);
|
|
1558
1829
|
const skillOverrides = params.tasks.map((task) => normalizeSkillInput(task.skill));
|
|
1559
1830
|
const parallelTasks = params.tasks.map((task, index) => ({
|
|
@@ -1566,6 +1837,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1566
1837
|
...(task.outputMode !== undefined ? { outputMode: task.outputMode } : {}),
|
|
1567
1838
|
...(task.reads !== undefined && task.reads !== true ? { reads: task.reads } : {}),
|
|
1568
1839
|
...(task.progress !== undefined ? { progress: task.progress } : {}),
|
|
1840
|
+
...(task.toolBudget !== undefined ? { toolBudget: task.toolBudget } : {}),
|
|
1569
1841
|
...(task.acceptance !== undefined ? { acceptance: task.acceptance } : {}),
|
|
1570
1842
|
}));
|
|
1571
1843
|
return executeAsyncChain(id, {
|
|
@@ -1586,13 +1858,20 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1586
1858
|
sessionRoot,
|
|
1587
1859
|
chainSkills: [],
|
|
1588
1860
|
sessionFilesByFlatIndex: params.tasks.map((task, index) => sessionFileForTask(task.agent, index)),
|
|
1861
|
+
thinkingOverridesByFlatIndex: params.tasks.map((task, index) => thinkingOverrideForTask(task.agent, index)),
|
|
1589
1862
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
1590
1863
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
1591
1864
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
1865
|
+
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
1592
1866
|
controlConfig,
|
|
1593
1867
|
controlIntercomTarget,
|
|
1594
1868
|
childIntercomTarget,
|
|
1595
1869
|
nestedRoute,
|
|
1870
|
+
timeoutMs: data.timeoutMs,
|
|
1871
|
+
turnBudget: data.turnBudget,
|
|
1872
|
+
toolBudget: data.toolBudget,
|
|
1873
|
+
configToolBudget: data.configToolBudget,
|
|
1874
|
+
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
1596
1875
|
});
|
|
1597
1876
|
}
|
|
1598
1877
|
|
|
@@ -1613,15 +1892,22 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1613
1892
|
shareEnabled,
|
|
1614
1893
|
sessionRoot,
|
|
1615
1894
|
chainSkills,
|
|
1616
|
-
sessionFilesByFlatIndex: collectChainSessionFiles(chain, sessionFileForTask),
|
|
1895
|
+
sessionFilesByFlatIndex: collectChainSessionFiles(chain, sessionFileForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
1896
|
+
thinkingOverridesByFlatIndex: collectChainThinkingOverrides(chain, thinkingOverrideForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
1617
1897
|
dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
|
|
1618
1898
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
1619
1899
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
1620
1900
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
1901
|
+
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
1621
1902
|
controlConfig,
|
|
1622
1903
|
controlIntercomTarget,
|
|
1623
1904
|
childIntercomTarget,
|
|
1624
1905
|
nestedRoute,
|
|
1906
|
+
timeoutMs: data.timeoutMs,
|
|
1907
|
+
turnBudget: data.turnBudget,
|
|
1908
|
+
toolBudget: data.toolBudget,
|
|
1909
|
+
configToolBudget: data.configToolBudget,
|
|
1910
|
+
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
1625
1911
|
});
|
|
1626
1912
|
}
|
|
1627
1913
|
|
|
@@ -1640,7 +1926,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1640
1926
|
const normalizedSkills = normalizeSkillInput(params.skill);
|
|
1641
1927
|
const skills = normalizedSkills === false ? [] : normalizedSkills;
|
|
1642
1928
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, a.maxSubagentDepth);
|
|
1643
|
-
const modelOverride = resolveSubagentModelOverride((params.model as string | undefined) ?? a.model, ctx.model, availableModels, currentProvider);
|
|
1929
|
+
const modelOverride = resolveSubagentModelOverride((params.model as string | undefined) ?? a.model, ctx.model, availableModels, currentProvider, { scope: data.modelScope, source: (params.model as string | undefined) ? "explicit" : "inherited" });
|
|
1644
1930
|
return executeAsyncSingle(id, {
|
|
1645
1931
|
agent: params.agent!,
|
|
1646
1932
|
task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
|
|
@@ -1657,15 +1943,22 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1657
1943
|
skills,
|
|
1658
1944
|
output: effectiveOutput,
|
|
1659
1945
|
outputMode: effectiveOutputMode,
|
|
1946
|
+
outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
|
|
1660
1947
|
modelOverride,
|
|
1948
|
+
thinkingOverride: thinkingOverrideForTask(params.agent!, 0),
|
|
1661
1949
|
maxSubagentDepth,
|
|
1662
1950
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
1663
1951
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
1952
|
+
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
1664
1953
|
controlConfig,
|
|
1665
1954
|
controlIntercomTarget,
|
|
1666
1955
|
childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(agent, index) : undefined,
|
|
1667
1956
|
nestedRoute,
|
|
1668
1957
|
acceptance: params.acceptance,
|
|
1958
|
+
timeoutMs: data.timeoutMs,
|
|
1959
|
+
turnBudget: data.turnBudget,
|
|
1960
|
+
toolBudget: data.toolBudget,
|
|
1961
|
+
configToolBudget: data.configToolBudget,
|
|
1669
1962
|
});
|
|
1670
1963
|
}
|
|
1671
1964
|
|
|
@@ -1684,6 +1977,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1684
1977
|
sessionDirForIndex,
|
|
1685
1978
|
sessionFileForIndex,
|
|
1686
1979
|
sessionFileForTask,
|
|
1980
|
+
thinkingOverrideForTask,
|
|
1687
1981
|
artifactsDir,
|
|
1688
1982
|
artifactConfig,
|
|
1689
1983
|
onUpdate,
|
|
@@ -1703,6 +1997,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1703
1997
|
task: params.task,
|
|
1704
1998
|
agents,
|
|
1705
1999
|
ctx,
|
|
2000
|
+
modelScope: data.modelScope,
|
|
1706
2001
|
intercomEvents: deps.pi.events,
|
|
1707
2002
|
signal,
|
|
1708
2003
|
runId,
|
|
@@ -1711,6 +2006,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1711
2006
|
sessionDirForIndex,
|
|
1712
2007
|
sessionFileForIndex,
|
|
1713
2008
|
sessionFileForTask,
|
|
2009
|
+
thinkingOverrideForTask,
|
|
1714
2010
|
artifactsDir,
|
|
1715
2011
|
artifactConfig,
|
|
1716
2012
|
includeProgress: params.includeProgress,
|
|
@@ -1723,19 +2019,22 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1723
2019
|
foregroundControl,
|
|
1724
2020
|
nestedRoute: foregroundControl?.nestedRoute,
|
|
1725
2021
|
chainSkills,
|
|
1726
|
-
chainDir: params.chainDir,
|
|
2022
|
+
chainDir: params.chainDir ?? getProjectChainRunsDir(effectiveCwd),
|
|
1727
2023
|
dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
|
|
1728
2024
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
1729
2025
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
1730
2026
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
2027
|
+
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
1731
2028
|
timeoutMs: data.timeoutMs,
|
|
1732
2029
|
deadlineAt: data.deadlineAt,
|
|
2030
|
+
turnBudget: data.turnBudget,
|
|
2031
|
+
onDetachedExit: (index, result) => updateRememberedForegroundChild(deps.state, { runId, mode: "chain", cwd: effectiveCwd, index, result }),
|
|
2032
|
+
toolBudget: data.toolBudget,
|
|
2033
|
+
configToolBudget: data.configToolBudget,
|
|
2034
|
+
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
1733
2035
|
});
|
|
1734
2036
|
|
|
1735
2037
|
if (chainResult.requestedAsync) {
|
|
1736
|
-
if (data.timeoutMs !== undefined) {
|
|
1737
|
-
return buildRequestedModeError(params, "timeoutMs/maxRuntimeMs are only supported for foreground runs; background launch from clarify cannot preserve the timeout.");
|
|
1738
|
-
}
|
|
1739
2038
|
if (!isAsyncAvailable()) {
|
|
1740
2039
|
return {
|
|
1741
2040
|
content: [{ type: "text", text: "Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the pi-subagents package dependencies are installed." }],
|
|
@@ -1751,6 +2050,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1751
2050
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
1752
2051
|
currentModelProvider: ctx.model?.provider,
|
|
1753
2052
|
currentModel: ctx.model,
|
|
2053
|
+
modelScope: data.modelScope,
|
|
1754
2054
|
};
|
|
1755
2055
|
const asyncChain = wrapChainTasksForFork(chainResult.requestedAsync.chain, contextPolicy);
|
|
1756
2056
|
return executeAsyncChain(id, {
|
|
@@ -1766,20 +2066,32 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1766
2066
|
shareEnabled,
|
|
1767
2067
|
sessionRoot,
|
|
1768
2068
|
chainSkills: chainResult.requestedAsync.chainSkills,
|
|
1769
|
-
sessionFilesByFlatIndex: collectChainSessionFiles(asyncChain, sessionFileForTask),
|
|
2069
|
+
sessionFilesByFlatIndex: collectChainSessionFiles(asyncChain, sessionFileForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
2070
|
+
thinkingOverridesByFlatIndex: collectChainThinkingOverrides(asyncChain, thinkingOverrideForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
1770
2071
|
dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
|
|
1771
2072
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
1772
2073
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
1773
2074
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
2075
|
+
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
1774
2076
|
controlConfig,
|
|
1775
2077
|
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
1776
2078
|
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
1777
2079
|
nestedRoute: data.nestedRoute,
|
|
2080
|
+
timeoutMs: data.timeoutMs,
|
|
2081
|
+
turnBudget: data.turnBudget,
|
|
2082
|
+
toolBudget: data.toolBudget,
|
|
2083
|
+
configToolBudget: data.configToolBudget,
|
|
2084
|
+
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
1778
2085
|
});
|
|
1779
2086
|
}
|
|
1780
2087
|
|
|
1781
|
-
const
|
|
1782
|
-
if (foregroundControl)
|
|
2088
|
+
const rawChainDetails = chainResult.details ? { ...chainResult.details, runId } : undefined;
|
|
2089
|
+
if (foregroundControl && rawChainDetails) {
|
|
2090
|
+
updateForegroundNestedProjection(foregroundControl);
|
|
2091
|
+
attachRootChildrenToSteps(runId, rawChainDetails.results, foregroundControl.nestedChildren);
|
|
2092
|
+
rawChainDetails.totalCost = sumResultsCost(rawChainDetails.results);
|
|
2093
|
+
}
|
|
2094
|
+
const chainDetails = rawChainDetails ? compactForegroundDetails(rawChainDetails) : undefined;
|
|
1783
2095
|
if (chainDetails) rememberForegroundRun(deps.state, { runId, mode: "chain", cwd: effectiveCwd, results: chainDetails.results });
|
|
1784
2096
|
const intercomReceipt = chainDetails && !chainDetails.results.some((result) => result.interrupted || result.detached)
|
|
1785
2097
|
? await maybeBuildForegroundIntercomReceipt({
|
|
@@ -1807,20 +2119,24 @@ interface ForegroundParallelRunInput {
|
|
|
1807
2119
|
taskTexts: string[];
|
|
1808
2120
|
agents: AgentConfig[];
|
|
1809
2121
|
ctx: ExtensionContext;
|
|
2122
|
+
state: SubagentState;
|
|
1810
2123
|
intercomEvents: IntercomEventBus;
|
|
1811
2124
|
signal: AbortSignal;
|
|
1812
2125
|
runId: string;
|
|
1813
2126
|
sessionDirForIndex: (idx?: number) => string | undefined;
|
|
1814
2127
|
sessionFileForIndex: (idx?: number) => string | undefined;
|
|
1815
2128
|
sessionFileForTask: (agentName: string, idx?: number) => string | undefined;
|
|
2129
|
+
thinkingOverrideForTask: (agentName: string, idx?: number) => AgentConfig["thinking"] | undefined;
|
|
1816
2130
|
shareEnabled: boolean;
|
|
1817
2131
|
artifactConfig: ArtifactConfig;
|
|
1818
2132
|
artifactsDir: string;
|
|
2133
|
+
outputBaseDir: string;
|
|
1819
2134
|
maxOutput?: MaxOutputConfig;
|
|
1820
2135
|
paramsCwd: string;
|
|
1821
2136
|
progressDir: string;
|
|
1822
2137
|
maxSubagentDepths: number[];
|
|
1823
2138
|
availableModels: ModelInfo[];
|
|
2139
|
+
modelScope?: ModelScopeConfig;
|
|
1824
2140
|
modelOverrides: (string | undefined)[];
|
|
1825
2141
|
behaviors: Array<ReturnType<typeof resolveStepBehavior>>;
|
|
1826
2142
|
firstProgressIndex: number;
|
|
@@ -1830,12 +2146,15 @@ interface ForegroundParallelRunInput {
|
|
|
1830
2146
|
orchestratorIntercomTarget?: string;
|
|
1831
2147
|
foregroundControl?: SubagentState["foregroundControls"] extends Map<string, infer T> ? T : never;
|
|
1832
2148
|
concurrencyLimit: number;
|
|
2149
|
+
globalSemaphore?: Semaphore;
|
|
1833
2150
|
liveResults: (SingleResult | undefined)[];
|
|
1834
2151
|
liveProgress: (AgentProgress | undefined)[];
|
|
1835
2152
|
onUpdate?: (r: AgentToolResult<Details>) => void;
|
|
1836
2153
|
worktreeSetup?: WorktreeSetup;
|
|
1837
2154
|
timeoutMs?: number;
|
|
1838
2155
|
deadlineAt?: number;
|
|
2156
|
+
turnBudget?: ResolvedTurnBudget;
|
|
2157
|
+
toolBudgets: (ResolvedToolBudget | undefined)[];
|
|
1839
2158
|
}
|
|
1840
2159
|
|
|
1841
2160
|
function buildParallelModeError(message: string): AgentToolResult<Details> {
|
|
@@ -1853,6 +2172,7 @@ function createParallelWorktreeSetup(
|
|
|
1853
2172
|
tasks: TaskParam[],
|
|
1854
2173
|
setupHook: ExtensionConfig["worktreeSetupHook"],
|
|
1855
2174
|
setupHookTimeoutMs: ExtensionConfig["worktreeSetupHookTimeoutMs"],
|
|
2175
|
+
baseDir: ExtensionConfig["worktreeBaseDir"],
|
|
1856
2176
|
): { setup?: WorktreeSetup; errorResult?: AgentToolResult<Details> } {
|
|
1857
2177
|
if (!enabled) return {};
|
|
1858
2178
|
try {
|
|
@@ -1862,6 +2182,7 @@ function createParallelWorktreeSetup(
|
|
|
1862
2182
|
setupHook: setupHook
|
|
1863
2183
|
? { hookPath: setupHook, timeoutMs: setupHookTimeoutMs }
|
|
1864
2184
|
: undefined,
|
|
2185
|
+
baseDir,
|
|
1865
2186
|
}),
|
|
1866
2187
|
};
|
|
1867
2188
|
} catch (error) {
|
|
@@ -1879,6 +2200,12 @@ function buildParallelWorktreeTaskCwdError(
|
|
|
1879
2200
|
return formatWorktreeTaskCwdConflict(conflict, sharedCwd);
|
|
1880
2201
|
}
|
|
1881
2202
|
|
|
2203
|
+
function resolveSingleRunOutputBaseDir(deps: ExecutorDeps, artifactsDir: string, runId: string): string {
|
|
2204
|
+
return deps.config.singleRunOutputBaseDir
|
|
2205
|
+
? path.resolve(deps.expandTilde(deps.config.singleRunOutputBaseDir))
|
|
2206
|
+
: path.join(artifactsDir, "outputs", runId);
|
|
2207
|
+
}
|
|
2208
|
+
|
|
1882
2209
|
function buildChainWorktreeTaskCwdError(chain: ChainStep[], sharedCwd: string): string | undefined {
|
|
1883
2210
|
for (let stepIndex = 0; stepIndex < chain.length; stepIndex++) {
|
|
1884
2211
|
const step = chain[stepIndex]!;
|
|
@@ -1918,6 +2245,7 @@ function findDuplicateParallelOutputPath(input: {
|
|
|
1918
2245
|
behaviors: ResolvedStepBehavior[];
|
|
1919
2246
|
paramsCwd: string;
|
|
1920
2247
|
ctxCwd: string;
|
|
2248
|
+
outputBaseDir: string;
|
|
1921
2249
|
worktreeSetup?: WorktreeSetup;
|
|
1922
2250
|
}): string | undefined {
|
|
1923
2251
|
const seen = new Map<string, { index: number; agent: string }>();
|
|
@@ -1926,7 +2254,7 @@ function findDuplicateParallelOutputPath(input: {
|
|
|
1926
2254
|
if (!behavior?.output) continue;
|
|
1927
2255
|
const task = input.tasks[index]!;
|
|
1928
2256
|
const taskCwd = resolveParallelTaskCwd(task, input.paramsCwd, input.worktreeSetup, index);
|
|
1929
|
-
const outputPath = resolveSingleOutputPath(behavior.output, input.ctxCwd, taskCwd);
|
|
2257
|
+
const outputPath = resolveSingleOutputPath(behavior.output, input.ctxCwd, taskCwd, input.outputBaseDir);
|
|
1930
2258
|
if (!outputPath) continue;
|
|
1931
2259
|
const previous = seen.get(outputPath);
|
|
1932
2260
|
if (previous) {
|
|
@@ -1938,6 +2266,12 @@ function findDuplicateParallelOutputPath(input: {
|
|
|
1938
2266
|
}
|
|
1939
2267
|
|
|
1940
2268
|
async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Promise<SingleResult[]> {
|
|
2269
|
+
// Pre-warm fork session files sequentially before concurrent dispatch to avoid
|
|
2270
|
+
// races where multiple workers simultaneously try to branch the same parent session.
|
|
2271
|
+
// sessionFileForIndex caches results, so these calls return instantly inside mapConcurrent.
|
|
2272
|
+
for (let i = 0; i < input.tasks.length; i++) {
|
|
2273
|
+
input.sessionFileForIndex(i);
|
|
2274
|
+
}
|
|
1941
2275
|
return mapConcurrent(input.tasks, input.concurrencyLimit, async (task, index) => {
|
|
1942
2276
|
const behavior = input.behaviors[index];
|
|
1943
2277
|
const effectiveSkills = behavior?.skills;
|
|
@@ -1948,7 +2282,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
1948
2282
|
const progressInstructions = behavior
|
|
1949
2283
|
? buildChainInstructions({ ...behavior, output: false, reads: false }, input.progressDir, index === input.firstProgressIndex)
|
|
1950
2284
|
: { prefix: "", suffix: "" };
|
|
1951
|
-
const outputPath = resolveSingleOutputPath(behavior?.output, input.ctx.cwd, taskCwd);
|
|
2285
|
+
const outputPath = resolveSingleOutputPath(behavior?.output, input.ctx.cwd, taskCwd, input.outputBaseDir);
|
|
1952
2286
|
const taskText = injectSingleOutputInstruction(
|
|
1953
2287
|
`${readInstructions.prefix}${input.taskTexts[index]!}${progressInstructions.suffix}`,
|
|
1954
2288
|
outputPath,
|
|
@@ -1988,17 +2322,22 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
1988
2322
|
maxSubagentDepth: input.maxSubagentDepths[index],
|
|
1989
2323
|
controlConfig: input.controlConfig,
|
|
1990
2324
|
onControlEvent: input.onControlEvent,
|
|
2325
|
+
onDetachedExit: (result) => updateRememberedForegroundChild(input.state, { runId: input.runId, mode: "parallel", cwd: taskCwd, index, result }),
|
|
1991
2326
|
intercomSessionName: input.childIntercomTarget?.(task.agent, index),
|
|
1992
2327
|
orchestratorIntercomTarget: input.orchestratorIntercomTarget,
|
|
1993
2328
|
nestedRoute: input.foregroundControl?.nestedRoute,
|
|
1994
2329
|
modelOverride: input.modelOverrides[index],
|
|
2330
|
+
thinkingOverride: input.thinkingOverrideForTask(task.agent, index),
|
|
1995
2331
|
availableModels: input.availableModels,
|
|
1996
2332
|
preferredModelProvider: input.ctx.model?.provider,
|
|
2333
|
+
modelScope: input.modelScope,
|
|
1997
2334
|
skills: effectiveSkills === false ? [] : effectiveSkills,
|
|
1998
2335
|
acceptance: task.acceptance,
|
|
1999
2336
|
acceptanceContext: { mode: "parallel" },
|
|
2000
2337
|
timeoutMs: input.timeoutMs,
|
|
2001
2338
|
deadlineAt: input.deadlineAt,
|
|
2339
|
+
turnBudget: input.turnBudget,
|
|
2340
|
+
toolBudget: input.toolBudgets[index],
|
|
2002
2341
|
onUpdate: input.onUpdate
|
|
2003
2342
|
? (progressUpdate) => {
|
|
2004
2343
|
const stepResults = progressUpdate.details?.results || [];
|
|
@@ -2039,7 +2378,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2039
2378
|
input.foregroundControl.updatedAt = Date.now();
|
|
2040
2379
|
}
|
|
2041
2380
|
});
|
|
2042
|
-
});
|
|
2381
|
+
}, input.globalSemaphore);
|
|
2043
2382
|
}
|
|
2044
2383
|
|
|
2045
2384
|
async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps): Promise<AgentToolResult<Details>> {
|
|
@@ -2053,6 +2392,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2053
2392
|
sessionDirForIndex,
|
|
2054
2393
|
sessionFileForIndex,
|
|
2055
2394
|
sessionFileForTask,
|
|
2395
|
+
thinkingOverrideForTask,
|
|
2056
2396
|
shareEnabled,
|
|
2057
2397
|
artifactConfig,
|
|
2058
2398
|
artifactsDir,
|
|
@@ -2094,6 +2434,12 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2094
2434
|
const maxSubagentDepths = agentConfigs.map((config) =>
|
|
2095
2435
|
resolveChildMaxSubagentDepth(currentMaxSubagentDepth, config.maxSubagentDepth),
|
|
2096
2436
|
);
|
|
2437
|
+
const toolBudgets: (ResolvedToolBudget | undefined)[] = [];
|
|
2438
|
+
for (let index = 0; index < tasks.length; index++) {
|
|
2439
|
+
const resolved = resolveEffectiveToolBudget({ stepBudget: tasks[index]?.toolBudget, runBudget: data.toolBudget, agentBudget: agentConfigs[index]?.toolBudget, configBudget: data.configToolBudget });
|
|
2440
|
+
if (resolved.error) return buildParallelModeError(resolved.error);
|
|
2441
|
+
toolBudgets.push(resolved.toolBudget);
|
|
2442
|
+
}
|
|
2097
2443
|
|
|
2098
2444
|
if (params.worktree) {
|
|
2099
2445
|
const worktreeTaskCwdError = buildParallelWorktreeTaskCwdError(tasks, effectiveCwd);
|
|
@@ -2115,7 +2461,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2115
2461
|
...(task.model ? { model: task.model } : {}),
|
|
2116
2462
|
}));
|
|
2117
2463
|
const modelOverrides: (string | undefined)[] = tasks.map((_, i) =>
|
|
2118
|
-
resolveSubagentModelOverride(behaviorOverrides[i]?.model ?? agentConfigs[i]?.model, ctx.model, availableModels, currentProvider),
|
|
2464
|
+
resolveSubagentModelOverride(behaviorOverrides[i]?.model ?? agentConfigs[i]?.model, ctx.model, availableModels, currentProvider, { scope: data.modelScope, source: behaviorOverrides[i]?.model ? "explicit" : "inherited" }),
|
|
2119
2465
|
);
|
|
2120
2466
|
|
|
2121
2467
|
if (params.clarify === true && ctx.hasUI) {
|
|
@@ -2150,7 +2496,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2150
2496
|
for (let i = 0; i < result.behaviorOverrides.length; i++) {
|
|
2151
2497
|
const override = result.behaviorOverrides[i];
|
|
2152
2498
|
if (override?.model) {
|
|
2153
|
-
modelOverrides[i] = override.model;
|
|
2499
|
+
modelOverrides[i] = resolveSubagentModelOverride(override.model, ctx.model, availableModels, currentProvider, { scope: data.modelScope, source: "explicit" });
|
|
2154
2500
|
behaviorOverrides[i]!.model = override.model;
|
|
2155
2501
|
}
|
|
2156
2502
|
if (override?.output !== undefined) behaviorOverrides[i]!.output = override.output;
|
|
@@ -2163,9 +2509,6 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2163
2509
|
}
|
|
2164
2510
|
|
|
2165
2511
|
if (result.runInBackground) {
|
|
2166
|
-
if (data.timeoutMs !== undefined) {
|
|
2167
|
-
return buildRequestedModeError(params, "timeoutMs/maxRuntimeMs are only supported for foreground runs; background launch from clarify cannot preserve the timeout.");
|
|
2168
|
-
}
|
|
2169
2512
|
if (!isAsyncAvailable()) {
|
|
2170
2513
|
return {
|
|
2171
2514
|
content: [{ type: "text", text: "Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the pi-subagents package dependencies are installed." }],
|
|
@@ -2181,6 +2524,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2181
2524
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
2182
2525
|
currentModelProvider: ctx.model?.provider,
|
|
2183
2526
|
currentModel: ctx.model,
|
|
2527
|
+
modelScope: data.modelScope,
|
|
2184
2528
|
};
|
|
2185
2529
|
const parallelTasks = tasks.map((t, i) => {
|
|
2186
2530
|
const taskText = shouldForkAgent(contextPolicy, t.agent) ? wrapForkTask(taskTexts[i]!) : taskTexts[i]!;
|
|
@@ -2195,6 +2539,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2195
2539
|
...(behaviorOverrides[i]?.outputMode !== undefined ? { outputMode: behaviorOverrides[i]!.outputMode } : {}),
|
|
2196
2540
|
...(behaviorOverrides[i]?.reads !== undefined ? { reads: behaviorOverrides[i]!.reads } : {}),
|
|
2197
2541
|
...(progress !== undefined ? { progress } : {}),
|
|
2542
|
+
...(t.toolBudget !== undefined ? { toolBudget: t.toolBudget } : {}),
|
|
2198
2543
|
...(t.acceptance !== undefined ? { acceptance: t.acceptance } : {}),
|
|
2199
2544
|
};
|
|
2200
2545
|
});
|
|
@@ -2212,12 +2557,17 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2212
2557
|
sessionRoot,
|
|
2213
2558
|
chainSkills: [],
|
|
2214
2559
|
sessionFilesByFlatIndex: tasks.map((task, index) => sessionFileForTask(task.agent, index)),
|
|
2560
|
+
thinkingOverridesByFlatIndex: tasks.map((task, index) => thinkingOverrideForTask(task.agent, index)),
|
|
2215
2561
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
2216
2562
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
2217
2563
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
2564
|
+
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
2218
2565
|
controlConfig,
|
|
2219
2566
|
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
2220
2567
|
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
2568
|
+
timeoutMs: data.timeoutMs,
|
|
2569
|
+
turnBudget: data.turnBudget,
|
|
2570
|
+
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2221
2571
|
});
|
|
2222
2572
|
}
|
|
2223
2573
|
}
|
|
@@ -2234,21 +2584,24 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2234
2584
|
tasks,
|
|
2235
2585
|
deps.config.worktreeSetupHook,
|
|
2236
2586
|
deps.config.worktreeSetupHookTimeoutMs,
|
|
2587
|
+
deps.config.worktreeBaseDir,
|
|
2237
2588
|
);
|
|
2238
2589
|
if (errorResult) return errorResult;
|
|
2239
2590
|
|
|
2240
2591
|
try {
|
|
2592
|
+
const outputBaseDir = path.join(artifactsDir, "outputs", runId);
|
|
2241
2593
|
const duplicateOutputError = findDuplicateParallelOutputPath({
|
|
2242
2594
|
tasks,
|
|
2243
2595
|
behaviors,
|
|
2244
2596
|
paramsCwd: effectiveCwd,
|
|
2245
2597
|
ctxCwd: ctx.cwd,
|
|
2598
|
+
outputBaseDir,
|
|
2246
2599
|
worktreeSetup,
|
|
2247
2600
|
});
|
|
2248
2601
|
if (duplicateOutputError) return buildParallelModeError(duplicateOutputError);
|
|
2249
2602
|
for (let index = 0; index < tasks.length; index++) {
|
|
2250
2603
|
const taskCwd = resolveParallelTaskCwd(tasks[index]!, effectiveCwd, worktreeSetup, index);
|
|
2251
|
-
const outputPath = resolveSingleOutputPath(behaviors[index]?.output, ctx.cwd, taskCwd);
|
|
2604
|
+
const outputPath = resolveSingleOutputPath(behaviors[index]?.output, ctx.cwd, taskCwd, outputBaseDir);
|
|
2252
2605
|
const validationError = validateFileOnlyOutputMode(behaviors[index]?.outputMode, outputPath, `Parallel task ${index + 1} (${tasks[index]!.agent})`);
|
|
2253
2606
|
if (validationError) return buildParallelModeError(validationError);
|
|
2254
2607
|
}
|
|
@@ -2267,19 +2620,23 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2267
2620
|
taskTexts,
|
|
2268
2621
|
agents,
|
|
2269
2622
|
ctx,
|
|
2623
|
+
state: deps.state,
|
|
2270
2624
|
intercomEvents: deps.pi.events,
|
|
2271
2625
|
signal,
|
|
2272
2626
|
runId,
|
|
2273
2627
|
sessionDirForIndex,
|
|
2274
2628
|
sessionFileForIndex,
|
|
2275
2629
|
sessionFileForTask,
|
|
2630
|
+
thinkingOverrideForTask,
|
|
2276
2631
|
shareEnabled,
|
|
2277
2632
|
artifactConfig,
|
|
2278
2633
|
artifactsDir,
|
|
2634
|
+
outputBaseDir,
|
|
2279
2635
|
maxOutput: params.maxOutput,
|
|
2280
2636
|
paramsCwd: effectiveCwd,
|
|
2281
2637
|
progressDir: parallelProgressDir,
|
|
2282
2638
|
availableModels,
|
|
2639
|
+
modelScope: data.modelScope,
|
|
2283
2640
|
modelOverrides,
|
|
2284
2641
|
behaviors,
|
|
2285
2642
|
firstProgressIndex: parallelProgressPrecreated ? -1 : firstProgressIndex,
|
|
@@ -2289,6 +2646,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2289
2646
|
orchestratorIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
2290
2647
|
foregroundControl,
|
|
2291
2648
|
concurrencyLimit: parallelConcurrency,
|
|
2649
|
+
globalSemaphore: new Semaphore(deps.config.globalConcurrencyLimit ?? DEFAULT_GLOBAL_CONCURRENCY_LIMIT),
|
|
2292
2650
|
maxSubagentDepths,
|
|
2293
2651
|
liveResults,
|
|
2294
2652
|
liveProgress,
|
|
@@ -2296,6 +2654,8 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2296
2654
|
worktreeSetup,
|
|
2297
2655
|
timeoutMs: data.timeoutMs,
|
|
2298
2656
|
deadlineAt,
|
|
2657
|
+
turnBudget: data.turnBudget,
|
|
2658
|
+
toolBudgets,
|
|
2299
2659
|
});
|
|
2300
2660
|
for (let i = 0; i < results.length; i++) {
|
|
2301
2661
|
const run = results[i]!;
|
|
@@ -2307,6 +2667,10 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2307
2667
|
if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
|
|
2308
2668
|
}
|
|
2309
2669
|
|
|
2670
|
+
if (foregroundControl) {
|
|
2671
|
+
updateForegroundNestedProjection(foregroundControl);
|
|
2672
|
+
attachRootChildrenToSteps(runId, results, foregroundControl.nestedChildren);
|
|
2673
|
+
}
|
|
2310
2674
|
const interrupted = results.find((result) => result.interrupted);
|
|
2311
2675
|
const details = compactForegroundDetails({
|
|
2312
2676
|
mode: "parallel",
|
|
@@ -2314,6 +2678,8 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2314
2678
|
results,
|
|
2315
2679
|
progress: params.includeProgress ? allProgress : undefined,
|
|
2316
2680
|
artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
|
|
2681
|
+
totalChildUsage: sumResultsUsage(results),
|
|
2682
|
+
totalCost: sumResultsCost(results),
|
|
2317
2683
|
});
|
|
2318
2684
|
rememberForegroundRun(deps.state, { runId, mode: "parallel", cwd: effectiveCwd, results: details.results });
|
|
2319
2685
|
if (interrupted) {
|
|
@@ -2385,6 +2751,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2385
2751
|
runId,
|
|
2386
2752
|
sessionDirForIndex,
|
|
2387
2753
|
sessionFileForTask,
|
|
2754
|
+
thinkingOverrideForTask,
|
|
2388
2755
|
shareEnabled,
|
|
2389
2756
|
artifactConfig,
|
|
2390
2757
|
artifactsDir,
|
|
@@ -2405,6 +2772,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2405
2772
|
details: { mode: "single", results: [] },
|
|
2406
2773
|
};
|
|
2407
2774
|
}
|
|
2775
|
+
const effectiveToolBudget = resolveEffectiveToolBudget({ runBudget: data.toolBudget, agentBudget: agentConfig.toolBudget, configBudget: data.configToolBudget });
|
|
2776
|
+
if (effectiveToolBudget.error) return toExecutionErrorResult(params, new Error(effectiveToolBudget.error));
|
|
2408
2777
|
|
|
2409
2778
|
const currentProvider = ctx.model?.provider;
|
|
2410
2779
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
@@ -2414,6 +2783,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2414
2783
|
ctx.model,
|
|
2415
2784
|
availableModels,
|
|
2416
2785
|
currentProvider,
|
|
2786
|
+
{ scope: data.modelScope, source: (params.model as string | undefined) ? "explicit" : "inherited" },
|
|
2417
2787
|
);
|
|
2418
2788
|
let skillOverride: string[] | false | undefined = normalizeSkillInput(params.skill);
|
|
2419
2789
|
const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
|
|
@@ -2450,14 +2820,11 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2450
2820
|
|
|
2451
2821
|
task = result.templates[0]!;
|
|
2452
2822
|
const override = result.behaviorOverrides[0];
|
|
2453
|
-
if (override?.model) modelOverride = override.model;
|
|
2823
|
+
if (override?.model) modelOverride = resolveSubagentModelOverride(override.model, ctx.model, availableModels, currentProvider, { scope: data.modelScope, source: "explicit" });
|
|
2454
2824
|
if (override?.output !== undefined) effectiveOutput = normalizeSingleOutputOverride(override.output, agentConfig.output);
|
|
2455
2825
|
if (override?.skills !== undefined) skillOverride = override.skills;
|
|
2456
2826
|
|
|
2457
2827
|
if (result.runInBackground) {
|
|
2458
|
-
if (data.timeoutMs !== undefined) {
|
|
2459
|
-
return buildRequestedModeError(params, "timeoutMs/maxRuntimeMs are only supported for foreground runs; background launch from clarify cannot preserve the timeout.");
|
|
2460
|
-
}
|
|
2461
2828
|
if (!isAsyncAvailable()) {
|
|
2462
2829
|
return {
|
|
2463
2830
|
content: [{ type: "text", text: "Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the pi-subagents package dependencies are installed." }],
|
|
@@ -2473,6 +2840,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2473
2840
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
2474
2841
|
currentModelProvider: ctx.model?.provider,
|
|
2475
2842
|
currentModel: ctx.model,
|
|
2843
|
+
modelScope: data.modelScope,
|
|
2476
2844
|
};
|
|
2477
2845
|
return executeAsyncSingle(id, {
|
|
2478
2846
|
agent: params.agent!,
|
|
@@ -2490,13 +2858,19 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2490
2858
|
skills: skillOverride === false ? [] : skillOverride,
|
|
2491
2859
|
output: effectiveOutput,
|
|
2492
2860
|
outputMode: effectiveOutputMode,
|
|
2861
|
+
outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
|
|
2493
2862
|
modelOverride,
|
|
2863
|
+
thinkingOverride: thinkingOverrideForTask(params.agent!, 0),
|
|
2494
2864
|
maxSubagentDepth,
|
|
2495
2865
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
2496
2866
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
2867
|
+
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
2497
2868
|
controlConfig,
|
|
2498
2869
|
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
2499
2870
|
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
2871
|
+
timeoutMs: data.timeoutMs,
|
|
2872
|
+
turnBudget: data.turnBudget,
|
|
2873
|
+
toolBudget: effectiveToolBudget.toolBudget,
|
|
2500
2874
|
});
|
|
2501
2875
|
}
|
|
2502
2876
|
}
|
|
@@ -2505,7 +2879,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2505
2879
|
task = wrapForkTask(task);
|
|
2506
2880
|
}
|
|
2507
2881
|
const cleanTask = task;
|
|
2508
|
-
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, effectiveCwd);
|
|
2882
|
+
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, effectiveCwd, resolveSingleRunOutputBaseDir(deps, artifactsDir, runId));
|
|
2509
2883
|
const validationError = validateFileOnlyOutputMode(effectiveOutputMode, outputPath, `Single run (${params.agent})`);
|
|
2510
2884
|
if (validationError) {
|
|
2511
2885
|
return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [] } };
|
|
@@ -2580,13 +2954,18 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2580
2954
|
nestedRoute: foregroundControl?.nestedRoute,
|
|
2581
2955
|
index: 0,
|
|
2582
2956
|
modelOverride,
|
|
2957
|
+
thinkingOverride: thinkingOverrideForTask(params.agent!, 0),
|
|
2583
2958
|
availableModels,
|
|
2584
2959
|
preferredModelProvider: currentProvider,
|
|
2960
|
+
modelScope: data.modelScope,
|
|
2585
2961
|
skills: effectiveSkills,
|
|
2586
2962
|
acceptance: params.acceptance,
|
|
2587
2963
|
acceptanceContext: { mode: "single" },
|
|
2964
|
+
onDetachedExit: (result) => updateRememberedForegroundChild(deps.state, { runId, mode: "single", cwd: effectiveCwd, index: 0, result }),
|
|
2588
2965
|
timeoutMs: data.timeoutMs,
|
|
2589
2966
|
deadlineAt,
|
|
2967
|
+
turnBudget: data.turnBudget,
|
|
2968
|
+
toolBudget: effectiveToolBudget.toolBudget,
|
|
2590
2969
|
});
|
|
2591
2970
|
if (foregroundControl?.currentIndex === 0) {
|
|
2592
2971
|
foregroundControl.interrupt = undefined;
|
|
@@ -2616,13 +2995,21 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2616
2995
|
outputReference: r.outputReference,
|
|
2617
2996
|
saveError: r.outputSaveError,
|
|
2618
2997
|
});
|
|
2998
|
+
if (foregroundControl) {
|
|
2999
|
+
updateForegroundNestedProjection(foregroundControl);
|
|
3000
|
+
attachRootChildrenToSteps(runId, [r], foregroundControl.nestedChildren);
|
|
3001
|
+
}
|
|
2619
3002
|
const details = compactForegroundDetails({
|
|
2620
3003
|
mode: "single",
|
|
2621
3004
|
runId,
|
|
2622
3005
|
results: [r],
|
|
3006
|
+
...(data.turnBudget ? { turnBudget: data.turnBudget } : {}),
|
|
3007
|
+
...(effectiveToolBudget.toolBudget ? { toolBudget: effectiveToolBudget.toolBudget } : {}),
|
|
2623
3008
|
progress: params.includeProgress ? allProgress : undefined,
|
|
2624
3009
|
artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
|
|
2625
3010
|
truncation: r.truncation,
|
|
3011
|
+
totalChildUsage: sumResultsUsage([r]),
|
|
3012
|
+
totalCost: sumResultsCost([r]),
|
|
2626
3013
|
});
|
|
2627
3014
|
rememberForegroundRun(deps.state, { runId, mode: "single", cwd: effectiveCwd, results: details.results });
|
|
2628
3015
|
|
|
@@ -2688,6 +3075,21 @@ function duplicateSubagentCallResult(params: SubagentParamsLike): AgentToolResul
|
|
|
2688
3075
|
};
|
|
2689
3076
|
}
|
|
2690
3077
|
|
|
3078
|
+
function omitExecutionModeActionAlias(params: SubagentParamsLike): SubagentParamsLike {
|
|
3079
|
+
const action = params.action?.toLowerCase();
|
|
3080
|
+
if (action === "single" && (params.agent !== undefined || params.task !== undefined)) {
|
|
3081
|
+
const rest = { ...params };
|
|
3082
|
+
delete rest.action;
|
|
3083
|
+
return rest;
|
|
3084
|
+
}
|
|
3085
|
+
if ((action === "parallel" || action === "tasks") && (params.tasks?.length ?? 0) > 0) {
|
|
3086
|
+
const rest = { ...params };
|
|
3087
|
+
delete rest.action;
|
|
3088
|
+
return rest;
|
|
3089
|
+
}
|
|
3090
|
+
return params;
|
|
3091
|
+
}
|
|
3092
|
+
|
|
2691
3093
|
export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
2692
3094
|
execute: (
|
|
2693
3095
|
id: string,
|
|
@@ -2708,10 +3110,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2708
3110
|
deps.state.foregroundRuns ??= new Map();
|
|
2709
3111
|
deps.state.foregroundControls ??= new Map();
|
|
2710
3112
|
deps.state.lastForegroundControlId ??= null;
|
|
2711
|
-
const
|
|
2712
|
-
const
|
|
2713
|
-
|
|
2714
|
-
|
|
3113
|
+
const requestParams = omitExecutionModeActionAlias(params);
|
|
3114
|
+
const requestCwd = resolveRequestedCwd(ctx.cwd, requestParams.cwd);
|
|
3115
|
+
const paramsWithResolvedCwd = requestParams.cwd === undefined ? requestParams : { ...requestParams, cwd: requestCwd };
|
|
3116
|
+
const action = paramsWithResolvedCwd.action;
|
|
3117
|
+
if (action) {
|
|
3118
|
+
if (action === "doctor") {
|
|
2715
3119
|
let currentSessionFile: string | null = null;
|
|
2716
3120
|
let currentSessionId = deps.state.currentSessionId;
|
|
2717
3121
|
let sessionError: string | undefined;
|
|
@@ -2746,15 +3150,27 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2746
3150
|
details: { mode: "management", results: [] },
|
|
2747
3151
|
};
|
|
2748
3152
|
}
|
|
2749
|
-
if (
|
|
3153
|
+
if (action === "status") {
|
|
2750
3154
|
const targetRunId = paramsWithResolvedCwd.id ?? paramsWithResolvedCwd.runId;
|
|
3155
|
+
const nestedScope = nestedResolutionScopeForExecutor(deps);
|
|
3156
|
+
const sessionRoots = trustedSessionRootsForStatus(ctx, deps);
|
|
3157
|
+
if (paramsWithResolvedCwd.view === "fleet") {
|
|
3158
|
+
return inspectSubagentStatus(paramsWithResolvedCwd, { state: deps.state, nested: nestedScope, sessionRoots });
|
|
3159
|
+
}
|
|
2751
3160
|
if (targetRunId) {
|
|
2752
3161
|
try {
|
|
2753
|
-
const nestedScope = nestedResolutionScopeForExecutor(deps);
|
|
2754
3162
|
const resolved = resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedScope });
|
|
2755
3163
|
if (resolved?.kind === "foreground") {
|
|
2756
3164
|
const foreground = getForegroundControl(deps.state, resolved.id);
|
|
2757
|
-
if (foreground)
|
|
3165
|
+
if (foreground) {
|
|
3166
|
+
if (paramsWithResolvedCwd.view === "transcript") {
|
|
3167
|
+
return {
|
|
3168
|
+
content: [{ type: "text", text: "Live foreground transcript is already visible in the expanded running subagent result. Persisted session transcript becomes inspectable after the foreground run completes when sessions are enabled." }],
|
|
3169
|
+
details: { mode: "management", results: [] },
|
|
3170
|
+
};
|
|
3171
|
+
}
|
|
3172
|
+
return foregroundStatusResult(foreground);
|
|
3173
|
+
}
|
|
2758
3174
|
}
|
|
2759
3175
|
} catch (error) {
|
|
2760
3176
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -2762,17 +3178,60 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2762
3178
|
}
|
|
2763
3179
|
} else {
|
|
2764
3180
|
const foreground = getForegroundControl(deps.state, undefined);
|
|
2765
|
-
if (foreground) return foregroundStatusResult(foreground);
|
|
3181
|
+
if (foreground && paramsWithResolvedCwd.view !== "transcript") return foregroundStatusResult(foreground);
|
|
3182
|
+
if (foreground && paramsWithResolvedCwd.view === "transcript") {
|
|
3183
|
+
return {
|
|
3184
|
+
content: [{ type: "text", text: "Live foreground transcript is already visible in the expanded running subagent result. Pass an async run id to inspect a background transcript." }],
|
|
3185
|
+
details: { mode: "management", results: [] },
|
|
3186
|
+
};
|
|
3187
|
+
}
|
|
2766
3188
|
}
|
|
2767
|
-
return inspectSubagentStatus(paramsWithResolvedCwd, { state: deps.state, nested:
|
|
3189
|
+
return inspectSubagentStatus(paramsWithResolvedCwd, { state: deps.state, nested: nestedScope, sessionRoots });
|
|
2768
3190
|
}
|
|
2769
|
-
if (
|
|
3191
|
+
if (action === "resume") {
|
|
2770
3192
|
return resumeAsyncRun({ params: paramsWithResolvedCwd, requestCwd, ctx, deps });
|
|
2771
3193
|
}
|
|
2772
|
-
if (
|
|
3194
|
+
if (action === "steer") {
|
|
3195
|
+
const message = (paramsWithResolvedCwd.message ?? paramsWithResolvedCwd.task ?? "").trim();
|
|
3196
|
+
if (!message) return { content: [{ type: "text", text: "action='steer' requires message." }], isError: true, details: { mode: "management", results: [] } };
|
|
3197
|
+
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
3198
|
+
if (paramsWithResolvedCwd.dir) {
|
|
3199
|
+
try {
|
|
3200
|
+
const location = resolveAsyncRunLocation(paramsWithResolvedCwd, ASYNC_DIR, RESULTS_DIR);
|
|
3201
|
+
const runId = location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir);
|
|
3202
|
+
return steerAsyncRun({ state: deps.state, runId, message, index: paramsWithResolvedCwd.index, kill: deps.kill, location });
|
|
3203
|
+
} catch (error) {
|
|
3204
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
3205
|
+
return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
if (!targetRunId) return { content: [{ type: "text", text: "action='steer' requires id or dir." }], isError: true, details: { mode: "management", results: [] } };
|
|
3209
|
+
let resolved: ResolvedSubagentRunId | undefined;
|
|
3210
|
+
try {
|
|
3211
|
+
resolved = resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedResolutionScopeForExecutor(deps) });
|
|
3212
|
+
} catch (error) {
|
|
3213
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
3214
|
+
return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
|
|
3215
|
+
}
|
|
3216
|
+
if (resolved?.kind === "nested") return steerNestedRun({ target: resolved, message, index: paramsWithResolvedCwd.index });
|
|
3217
|
+
if (resolved?.kind === "foreground") return { content: [{ type: "text", text: "action='steer' currently supports live async Pi child sessions only; use action='interrupt' or action='resume' for foreground runs." }], isError: true, details: { mode: "management", results: [] } };
|
|
3218
|
+
if (resolved?.kind !== "async") return { content: [{ type: "text", text: `No async run found for '${targetRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
3219
|
+
return steerAsyncRun({ state: deps.state, runId: resolved.id, message, index: paramsWithResolvedCwd.index, kill: deps.kill, location: resolved.location });
|
|
3220
|
+
}
|
|
3221
|
+
if (action === "append-step") {
|
|
2773
3222
|
return appendStepToAsyncChain({ params: paramsWithResolvedCwd, requestCwd, ctx, deps });
|
|
2774
3223
|
}
|
|
2775
|
-
if (
|
|
3224
|
+
if (action === "schedule" || action === "schedule-list" || action === "schedule-status" || action === "schedule-cancel") {
|
|
3225
|
+
if (!deps.handleScheduledRunAction) {
|
|
3226
|
+
return {
|
|
3227
|
+
content: [{ type: "text", text: `Action '${action}' is not available in this subagent context.` }],
|
|
3228
|
+
isError: true,
|
|
3229
|
+
details: { mode: "management", results: [] },
|
|
3230
|
+
};
|
|
3231
|
+
}
|
|
3232
|
+
return deps.handleScheduledRunAction(paramsWithResolvedCwd, ctx);
|
|
3233
|
+
}
|
|
3234
|
+
if (action === "interrupt") {
|
|
2776
3235
|
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
2777
3236
|
let resolved: ResolvedSubagentRunId | undefined;
|
|
2778
3237
|
if (targetRunId) {
|
|
@@ -2814,21 +3273,25 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2814
3273
|
details: { mode: "management", results: [] },
|
|
2815
3274
|
};
|
|
2816
3275
|
}
|
|
2817
|
-
if (!(SUBAGENT_ACTIONS as readonly string[]).includes(
|
|
3276
|
+
if (!(SUBAGENT_ACTIONS as readonly string[]).includes(action)) {
|
|
2818
3277
|
return {
|
|
2819
|
-
content: [{ type: "text", text: `Unknown action: ${
|
|
3278
|
+
content: [{ type: "text", text: `Unknown action: ${action}. Valid: ${SUBAGENT_ACTIONS.join(", ")}` }],
|
|
2820
3279
|
isError: true,
|
|
2821
3280
|
details: { mode: "management" as const, results: [] },
|
|
2822
3281
|
};
|
|
2823
3282
|
}
|
|
2824
|
-
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(
|
|
3283
|
+
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
2825
3284
|
return {
|
|
2826
|
-
content: [{ type: "text", text: `Action '${
|
|
3285
|
+
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
2827
3286
|
isError: true,
|
|
2828
3287
|
details: { mode: "management" as const, results: [] },
|
|
2829
3288
|
};
|
|
2830
3289
|
}
|
|
2831
|
-
return handleManagementAction(
|
|
3290
|
+
return handleManagementAction(action, paramsWithResolvedCwd, {
|
|
3291
|
+
...ctx,
|
|
3292
|
+
cwd: requestCwd,
|
|
3293
|
+
config: deps.config,
|
|
3294
|
+
});
|
|
2832
3295
|
}
|
|
2833
3296
|
|
|
2834
3297
|
const { blocked, depth, maxDepth } = checkSubagentDepth(deps.config.maxSubagentDepth);
|
|
@@ -2859,12 +3322,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2859
3322
|
);
|
|
2860
3323
|
const foregroundTimeout = resolveForegroundTimeout(effectiveParams);
|
|
2861
3324
|
if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
|
|
3325
|
+
const turnBudget = resolveTurnBudget(effectiveParams, deps.config);
|
|
3326
|
+
if (turnBudget.error) return buildRequestedModeError(effectiveParams, turnBudget.error);
|
|
3327
|
+
const runToolBudget = resolveToolBudget(effectiveParams.toolBudget, "toolBudget");
|
|
3328
|
+
if (runToolBudget.error) return buildRequestedModeError(effectiveParams, runToolBudget.error);
|
|
3329
|
+
const configToolBudget = resolveToolBudget(deps.config.toolBudget, "config.toolBudget");
|
|
3330
|
+
if (configToolBudget.error) return buildRequestedModeError(effectiveParams, configToolBudget.error);
|
|
2862
3331
|
|
|
2863
3332
|
const scope: AgentScope = resolveExecutionAgentScope(effectiveParams.agentScope);
|
|
2864
3333
|
const effectiveCwd = effectiveParams.cwd ?? ctx.cwd;
|
|
2865
3334
|
const parentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
|
|
2866
3335
|
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
2867
|
-
const
|
|
3336
|
+
const discovered = deps.discoverAgents(effectiveCwd, scope);
|
|
3337
|
+
const discoveredAgents = discovered.agents;
|
|
3338
|
+
const modelScope = discovered.modelScope;
|
|
2868
3339
|
const contextPolicy = resolveAgentDefaultContextPolicy(effectiveParams, discoveredAgents);
|
|
2869
3340
|
effectiveParams = contextPolicy.params;
|
|
2870
3341
|
const sessionName = resolveIntercomSessionTarget(deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
|
|
@@ -2872,7 +3343,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2872
3343
|
config: deps.config.intercomBridge,
|
|
2873
3344
|
context: effectiveParams.context,
|
|
2874
3345
|
orchestratorTarget: sessionName,
|
|
2875
|
-
cwd: effectiveCwd,
|
|
2876
3346
|
});
|
|
2877
3347
|
const agents = intercomBridge.active
|
|
2878
3348
|
? discoveredAgents.map((agent) => applyIntercomBridgeToAgent(agent, intercomBridge))
|
|
@@ -2901,24 +3371,24 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2901
3371
|
if (validationError) return validationError;
|
|
2902
3372
|
|
|
2903
3373
|
let forkSessionFileForIndex: (idx?: number) => string | undefined = () => undefined;
|
|
3374
|
+
let forkThinkingOverrideForIndex: (idx?: number) => AgentConfig["thinking"] | undefined = () => undefined;
|
|
2904
3375
|
try {
|
|
2905
|
-
|
|
3376
|
+
const forkContextResolver = createForkContextResolver(ctx.sessionManager, contextPolicy.usesFork ? "fork" : undefined);
|
|
3377
|
+
forkSessionFileForIndex = forkContextResolver.sessionFileForIndex;
|
|
3378
|
+
forkThinkingOverrideForIndex = forkContextResolver.thinkingOverrideForIndex;
|
|
2906
3379
|
} catch (error) {
|
|
2907
3380
|
return toExecutionErrorResult(effectiveParams, error);
|
|
2908
3381
|
}
|
|
2909
3382
|
const requestedAsync = effectiveParams.async ?? deps.asyncByDefault;
|
|
2910
3383
|
const backgroundRequestedWhileClarifying = (hasChain || hasTasks) && requestedAsync && effectiveParams.clarify === true;
|
|
2911
3384
|
const effectiveAsync = requestedAsync && effectiveParams.clarify !== true;
|
|
2912
|
-
if (foregroundTimeout.timeoutMs !== undefined && effectiveAsync) {
|
|
2913
|
-
return buildRequestedModeError(effectiveParams, "timeoutMs/maxRuntimeMs are only supported for foreground runs; set async: false or omit the timeout for background runs.");
|
|
2914
|
-
}
|
|
2915
3385
|
const controlConfig = resolveControlConfig(deps.config.control, effectiveParams.control);
|
|
2916
3386
|
|
|
2917
3387
|
const artifactConfig: ArtifactConfig = {
|
|
2918
3388
|
...DEFAULT_ARTIFACT_CONFIG,
|
|
2919
3389
|
enabled: effectiveParams.artifacts !== false,
|
|
2920
3390
|
};
|
|
2921
|
-
const artifactsDir =
|
|
3391
|
+
const artifactsDir = getArtifactsDir(parentSessionFile, effectiveCwd);
|
|
2922
3392
|
|
|
2923
3393
|
let sessionRoot: string;
|
|
2924
3394
|
if (effectiveParams.sessionDir) {
|
|
@@ -2942,12 +3412,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2942
3412
|
path.join(sessionRoot, `run-${idx ?? 0}`);
|
|
2943
3413
|
const forkSessionFileForTask = (agentName: string, idx?: number) =>
|
|
2944
3414
|
shouldForkAgent(contextPolicy, agentName) ? forkSessionFileForIndex(idx) : undefined;
|
|
3415
|
+
const forkThinkingOverrideForTask = (agentName: string, idx?: number) =>
|
|
3416
|
+
shouldForkAgent(contextPolicy, agentName) ? forkThinkingOverrideForIndex(idx) : undefined;
|
|
2945
3417
|
const childSessionFileForTask = (agentName: string, idx?: number) =>
|
|
2946
3418
|
forkSessionFileForTask(agentName, idx) ?? path.join(sessionDirForIndex(idx), "session.jsonl");
|
|
2947
3419
|
const childSessionFileForIndex = (idx?: number) =>
|
|
2948
3420
|
path.join(sessionDirForIndex(idx), "session.jsonl");
|
|
2949
3421
|
try {
|
|
2950
|
-
preflightForkSessionsForStaticTasks(effectiveParams, contextPolicy, forkSessionFileForTask);
|
|
3422
|
+
preflightForkSessionsForStaticTasks(effectiveParams, contextPolicy, forkSessionFileForTask, deps.config.chain?.dynamicFanout?.maxItems);
|
|
2951
3423
|
} catch (error) {
|
|
2952
3424
|
return toExecutionErrorResult(effectiveParams, error);
|
|
2953
3425
|
}
|
|
@@ -2958,6 +3430,16 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2958
3430
|
? (r: AgentToolResult<Details>) => onUpdate(withForkContext(r, effectiveParams.context))
|
|
2959
3431
|
: undefined;
|
|
2960
3432
|
|
|
3433
|
+
const foregroundMode: "single" | "parallel" | "chain" = hasChain ? "chain" : hasTasks ? "parallel" : "single";
|
|
3434
|
+
const spawnLimitError = reserveSubagentSpawns({
|
|
3435
|
+
state: deps.state,
|
|
3436
|
+
config: deps.config,
|
|
3437
|
+
sessionId: deps.state.currentSessionId,
|
|
3438
|
+
requested: countRequestedSubagentSpawns(effectiveParams, deps.config),
|
|
3439
|
+
mode: foregroundMode,
|
|
3440
|
+
});
|
|
3441
|
+
if (spawnLimitError) return spawnLimitError;
|
|
3442
|
+
|
|
2961
3443
|
const execData: ExecutionContextData = {
|
|
2962
3444
|
params: effectiveParams,
|
|
2963
3445
|
effectiveCwd,
|
|
@@ -2971,6 +3453,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2971
3453
|
sessionDirForIndex,
|
|
2972
3454
|
sessionFileForIndex: childSessionFileForIndex,
|
|
2973
3455
|
sessionFileForTask: childSessionFileForTask,
|
|
3456
|
+
thinkingOverrideForTask: forkThinkingOverrideForTask,
|
|
2974
3457
|
artifactConfig,
|
|
2975
3458
|
artifactsDir,
|
|
2976
3459
|
backgroundRequestedWhileClarifying,
|
|
@@ -2979,10 +3462,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2979
3462
|
intercomBridge,
|
|
2980
3463
|
nestedRoute,
|
|
2981
3464
|
timeoutMs: foregroundTimeout.timeoutMs,
|
|
3465
|
+
turnBudget: turnBudget.turnBudget,
|
|
3466
|
+
toolBudget: runToolBudget.toolBudget,
|
|
3467
|
+
configToolBudget: configToolBudget.toolBudget,
|
|
2982
3468
|
contextPolicy,
|
|
3469
|
+
modelScope,
|
|
2983
3470
|
};
|
|
2984
3471
|
|
|
2985
|
-
const foregroundMode: "single" | "parallel" | "chain" = hasChain ? "chain" : hasTasks ? "parallel" : "single";
|
|
2986
3472
|
const foregroundControl = effectiveAsync
|
|
2987
3473
|
? undefined
|
|
2988
3474
|
: {
|
|
@@ -3046,6 +3532,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3046
3532
|
startedAt: foregroundControl?.startedAt ?? now,
|
|
3047
3533
|
...(state !== "running" ? { endedAt: now } : {}),
|
|
3048
3534
|
lastUpdate: now,
|
|
3535
|
+
...(details?.totalCost ? { totalCost: details.totalCost } : {}),
|
|
3049
3536
|
...(errorText ? { error: errorText } : {}),
|
|
3050
3537
|
...(details?.results.length ? { steps: details.results.map((child) => ({
|
|
3051
3538
|
agent: child.agent,
|
|
@@ -3111,11 +3598,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3111
3598
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
3112
3599
|
ctx: ExtensionContext,
|
|
3113
3600
|
): Promise<AgentToolResult<Details>> => {
|
|
3114
|
-
|
|
3115
|
-
if (
|
|
3601
|
+
const requestParams = omitExecutionModeActionAlias(params);
|
|
3602
|
+
if (requestParams.action) return execute(id, requestParams, signal, onUpdate, ctx);
|
|
3603
|
+
if (deps.state.subagentInProgress === true) return duplicateSubagentCallResult(requestParams);
|
|
3116
3604
|
deps.state.subagentInProgress = true;
|
|
3117
3605
|
try {
|
|
3118
|
-
return await execute(id,
|
|
3606
|
+
return await execute(id, requestParams, signal, onUpdate, ctx);
|
|
3119
3607
|
} finally {
|
|
3120
3608
|
deps.state.subagentInProgress = false;
|
|
3121
3609
|
}
|