@selesai/code 0.5.28 → 0.6.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/CHANGELOG.md +24 -0
- package/README.md +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +9 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/credential-print.d.ts +23 -0
- package/dist/cli/credential-print.d.ts.map +1 -0
- package/dist/cli/credential-print.js +117 -0
- package/dist/cli/credential-print.js.map +1 -0
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +1 -1
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +3 -0
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts +12 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +20 -14
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +11 -3
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +11 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +14 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +10 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/llama/provider.d.ts.map +1 -1
- package/dist/core/llama/provider.js +8 -3
- package/dist/core/llama/provider.js.map +1 -1
- package/dist/core/model-config.d.ts +30 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +6 -0
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +2 -2
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +20 -3
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +2 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +5 -4
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +13 -6
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +24 -11
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts +15 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +66 -9
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +19 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/system-prompt.test.d.ts +2 -0
- package/dist/core/system-prompt.test.d.ts.map +1 -0
- package/dist/core/system-prompt.test.js +89 -0
- package/dist/core/system-prompt.test.js.map +1 -0
- package/dist/core/tools/bash.d.ts +2 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +34 -5
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js +3 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/defaults/models.json +13 -45
- package/dist/defaults/settings.json +1 -2
- package/dist/extensions/copy-turn.test.ts +131 -0
- package/dist/extensions/copy-turn.ts +6 -1
- package/dist/extensions/package.json +0 -1
- package/dist/extensions/pi-intercom/CHANGELOG.md +249 -0
- package/dist/extensions/pi-intercom/README.md +102 -39
- package/dist/extensions/pi-intercom/broker/broker.ts +1233 -36
- package/dist/extensions/pi-intercom/broker/client.test.ts +83 -0
- package/dist/extensions/pi-intercom/broker/client.ts +315 -12
- package/dist/extensions/pi-intercom/broker/extension-state.ts +186 -0
- package/dist/extensions/pi-intercom/broker/extension.test.ts +387 -0
- package/dist/extensions/pi-intercom/broker/framing.test.ts +114 -0
- package/dist/extensions/pi-intercom/broker/framing.ts +82 -24
- package/dist/extensions/pi-intercom/broker/paths.test.ts +153 -0
- package/dist/extensions/pi-intercom/broker/paths.ts +117 -8
- package/dist/extensions/pi-intercom/broker/runtime-claim.test.ts +34 -0
- package/dist/extensions/pi-intercom/broker/runtime-claim.ts +21 -0
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +160 -23
- package/dist/extensions/pi-intercom/broker/spawn.ts +113 -27
- package/dist/extensions/pi-intercom/config.test.ts +93 -0
- package/dist/extensions/pi-intercom/config.ts +55 -6
- package/dist/extensions/pi-intercom/cwd.test.ts +40 -0
- package/dist/extensions/pi-intercom/cwd.ts +31 -0
- package/dist/extensions/pi-intercom/extension-api.ts +44 -0
- package/dist/extensions/pi-intercom/format-context.test.ts +31 -0
- package/dist/extensions/pi-intercom/format-context.ts +32 -0
- package/dist/extensions/pi-intercom/index.ts +742 -145
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +2646 -0
- package/dist/extensions/pi-intercom/package.json +15 -5
- package/dist/extensions/pi-intercom/reply-tracker.test.ts +134 -0
- package/dist/extensions/pi-intercom/reply-tracker.ts +31 -13
- package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +13 -11
- package/dist/extensions/pi-intercom/test/inline-message.test.ts +184 -0
- package/dist/extensions/pi-intercom/test/overlay-width.test.ts +66 -0
- package/dist/extensions/pi-intercom/types.ts +94 -4
- package/dist/extensions/pi-intercom/ui/compose.ts +8 -4
- package/dist/extensions/pi-intercom/ui/inline-message.ts +61 -25
- package/dist/extensions/pi-intercom/ui/session-list.ts +7 -3
- package/dist/extensions/pi-subagents/CHANGELOG.md +88 -0
- package/dist/extensions/pi-subagents/LICENSE +21 -0
- package/dist/extensions/pi-subagents/README.md +158 -69
- package/dist/extensions/pi-subagents/agents/architect.md +5 -4
- package/dist/extensions/pi-subagents/agents/builder.md +6 -4
- package/dist/extensions/pi-subagents/agents/commentator.md +3 -2
- package/dist/extensions/pi-subagents/agents/explorer.md +3 -2
- package/dist/extensions/pi-subagents/agents/recapper.md +3 -2
- package/dist/extensions/pi-subagents/agents/researcher.md +4 -3
- package/dist/extensions/pi-subagents/package-lock.json +2 -2
- package/dist/extensions/pi-subagents/package.json +3 -3
- package/dist/extensions/pi-subagents/prompts/review-loop.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +22 -988
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +257 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +431 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +144 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +281 -0
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +125 -37
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +4 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +118 -9
- package/dist/extensions/pi-subagents/src/agents/skills.ts +14 -12
- package/dist/extensions/pi-subagents/src/agents/task-aware-routing.ts +125 -0
- package/dist/extensions/pi-subagents/src/api/delegation.ts +3 -0
- package/dist/extensions/pi-subagents/src/api/preflight.ts +17 -13
- package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +17 -1
- package/dist/extensions/pi-subagents/src/extension/index.ts +22 -8
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +248 -6
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +33 -8
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +32 -14
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +9 -4
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +33 -4
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +86 -21
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +24 -13
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +7 -5
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +48 -5
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +68 -1
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +56 -5
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +92 -11
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +29 -3
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +388 -29
- package/dist/extensions/pi-subagents/src/runs/foreground/async-stop-action.ts +65 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +214 -60
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +546 -252
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +42 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +553 -154
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +19 -13
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +51 -19
- package/dist/extensions/pi-subagents/src/runs/shared/chain-outputs.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +44 -11
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +8 -0
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +97 -20
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +43 -12
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +52 -4
- package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +19 -0
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +45 -9
- package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +71 -0
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +63 -9
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +31 -1
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +101 -0
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +22 -1
- package/dist/extensions/pi-subagents/src/runs/shared/usage-budget.ts +65 -0
- package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +26 -1
- package/dist/extensions/pi-subagents/src/shared/settings.ts +17 -1
- package/dist/extensions/pi-subagents/src/shared/types.ts +196 -12
- package/dist/extensions/pi-subagents/src/shared/utils.ts +102 -5
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +10 -1
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +46 -4
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +5 -3
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +87 -24
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +182 -9
- package/dist/extensions/pi-subagents/src/tui/render.ts +55 -34
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +14 -7
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +5 -4
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +170 -17
- package/dist/extensions/pi-subagents/src/watchdog/scope.ts +62 -0
- package/dist/extensions/pi-subagents/src/watchdog/settings.ts +41 -1
- package/dist/extensions/pi-subagents/src/watchdog/types.ts +10 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +113 -8
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +419 -47
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +84 -0
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +51 -2
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +62 -22
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +48 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +8 -6
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +231 -19
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +48 -7
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +44 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +199 -17
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +898 -16
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +112 -23
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -5
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +2 -0
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +18 -2
- package/dist/extensions/pi-subagents/test/support/register-loader.mjs +3 -3
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +3 -1
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +198 -6
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +161 -1
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +47 -2
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +60 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/builtin-agent-documentation.test.ts +63 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +136 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +6 -0
- package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +48 -48
- package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +28 -2
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +176 -8
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +164 -5
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +41 -1
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +17 -12
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +18 -2
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +20 -1
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +30 -2
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +39 -2
- package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +103 -0
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +48 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +13 -1
- package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +29 -4
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +223 -4
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +30 -0
- package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +46 -2
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +91 -1
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +78 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +41 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +77 -0
- package/dist/extensions/pi-subagents/test/unit/task-aware-routing.test.ts +213 -0
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +26 -1
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +61 -10
- package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +210 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +33 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +32 -0
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +43 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts +3 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +10 -2
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +16 -46
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +4 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +22 -13
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts +12 -0
- package/dist/modes/interactive/external-editor.d.ts.map +1 -0
- package/dist/modes/interactive/external-editor.js +37 -0
- package/dist/modes/interactive/external-editor.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +72 -64
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +14 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/skills/ponytail/SKILL.md +1 -3
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +19 -8
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +1 -1
- package/dist/utils/version-check.js.map +1 -1
- package/docs/compaction.md +1 -1
- package/docs/custom-provider.md +14 -5
- package/docs/environment-variables.md +86 -0
- package/docs/extensions.md +15 -5
- package/docs/index.md +1 -0
- package/docs/models.md +9 -2
- package/docs/plans/subagent-delegation/phase-0-correctness.md +265 -0
- package/docs/plans/subagent-delegation/phase-1-behavioral-contract.md +486 -0
- package/docs/plans/subagent-delegation/phase-2-context-controls.md +282 -0
- package/docs/plans/subagent-delegation/phase-3-advisory-routing.md +362 -0
- package/docs/plans/subagent-delegation/phase-4-optional-enforcement.md +381 -0
- package/docs/providers.md +21 -2
- package/docs/rpc.md +23 -6
- package/docs/session-format.md +2 -0
- package/docs/settings.md +1 -1
- package/docs/usage.md +0 -13
- package/examples/extensions/custom-compaction.ts +5 -2
- package/examples/extensions/custom-provider-anthropic/index.ts +9 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/handoff.ts +11 -3
- package/examples/extensions/message-renderer.ts +3 -3
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/summarize.ts +5 -2
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/12-full-control.ts +3 -1
- package/package.json +16 -7
- package/dist/extensions/caveman/caveman-instructions.cjs +0 -11
- package/dist/extensions/caveman/index.js +0 -118
- package/dist/extensions/caveman/package.json +0 -8
- package/dist/extensions/caveman/test/extension.test.js +0 -203
- package/dist/extensions/caveman/test/helpers.test.js +0 -58
- package/dist/skills/caveman/SKILL.md +0 -50
|
@@ -3,12 +3,19 @@ import * as fs from "node:fs";
|
|
|
3
3
|
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
|
-
import type
|
|
6
|
+
import { resolveAgentName, type AgentConfig, type AgentScope } from "../../agents/agents.ts";
|
|
7
7
|
import { getArtifactsDir, getProjectChainRunsDir } from "../../shared/artifacts.ts";
|
|
8
8
|
import { ChainClarifyComponent, type ChainClarifyResult } from "./chain-clarify.ts";
|
|
9
|
-
import { toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
9
|
+
import { resolveEffectiveThinking, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
10
10
|
import { executeChain } from "./chain-execution.ts";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
beginForegroundChild,
|
|
13
|
+
finishForegroundChild,
|
|
14
|
+
foregroundSchedulingSettled,
|
|
15
|
+
retainForegroundSchedulingOwner,
|
|
16
|
+
settleForegroundSchedulingOwner,
|
|
17
|
+
updateForegroundChild,
|
|
18
|
+
} from "./foreground-control.ts";
|
|
12
19
|
import { resolveExecutionAgentScope } from "../../agents/agent-scope.ts";
|
|
13
20
|
import { handleManagementAction } from "../../agents/agent-management.ts";
|
|
14
21
|
import { buildDoctorReport } from "../../extension/doctor.ts";
|
|
@@ -16,9 +23,8 @@ import { clearPendingForegroundControlNotices } from "../../extension/control-no
|
|
|
16
23
|
import { runSync } from "./execution.ts";
|
|
17
24
|
import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
|
|
18
25
|
import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
|
|
19
|
-
import { buildModelCandidates, resolveEffectiveSubagentModel, resolveModelCandidate } from "../shared/model-fallback.ts";
|
|
26
|
+
import { buildModelCandidates, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelCandidate, type ParentModel } from "../shared/model-fallback.ts";
|
|
20
27
|
import type { ModelScopeConfig } from "../shared/model-scope.ts";
|
|
21
|
-
import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
|
|
22
28
|
import { recordRun } from "../shared/run-history.ts";
|
|
23
29
|
import {
|
|
24
30
|
buildChainInstructions,
|
|
@@ -47,9 +53,10 @@ import { formatControlIntercomMessage, formatControlNoticeMessage, resolveContro
|
|
|
47
53
|
import { resolveTurnBudgetConfig } from "../shared/turn-budget.ts";
|
|
48
54
|
import { formatSpawnBudget, getSpawnBudgetSnapshot, grantSpawnBudget, preflightSpawnBudget, preflightSpawnBudgetGrant, reserveSpawnBudget } from "../shared/spawn-budget.ts";
|
|
49
55
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
56
|
+
import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig } from "../shared/usage-budget.ts";
|
|
50
57
|
import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
51
58
|
import { isAgentContractV1 } from "../shared/agent-contract.ts";
|
|
52
|
-
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
59
|
+
import { finalizeSingleOutput, formatBoundedPersistenceFallback, formatSavedOutputReference, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
53
60
|
import { cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
54
61
|
import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage } from "../../shared/utils.ts";
|
|
55
62
|
import { DEFAULT_GLOBAL_CONCURRENCY_LIMIT, Semaphore } from "../shared/parallel-utils.ts";
|
|
@@ -64,12 +71,13 @@ import {
|
|
|
64
71
|
stripDetailsOutputsForIntercomReceipt,
|
|
65
72
|
} from "../../intercom/result-intercom.ts";
|
|
66
73
|
import { applySteeringRecoveryAgentConfig, buildRevivedAsyncTask, resolveAsyncResumeTarget, resolveAsyncRunLocation } from "../background/async-resume.ts";
|
|
67
|
-
import {
|
|
74
|
+
import { deliverCheckpointDecisionRequest, deliverInterruptRequest, requestAsyncSteer } from "../background/control-channel.ts";
|
|
68
75
|
import { waitForSteeringAction } from "../background/steering.ts";
|
|
69
76
|
import { steerAsyncRun } from "./async-steering-action.ts";
|
|
77
|
+
import { stopAsyncRun } from "./async-stop-action.ts";
|
|
70
78
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
71
79
|
import { resolveAsyncRootResultPath } from "../background/chain-root-attachment.ts";
|
|
72
|
-
import { attachRootChildrenToSteps, createNestedRoute,
|
|
80
|
+
import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedRunResolutionScope } from "../shared/nested-events.ts";
|
|
73
81
|
import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
|
|
74
82
|
import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
|
|
75
83
|
import { inspectSubagentStatus } from "../background/run-status.ts";
|
|
@@ -106,6 +114,7 @@ import {
|
|
|
106
114
|
type SingleResult,
|
|
107
115
|
type ToolBudgetConfig,
|
|
108
116
|
type TurnBudgetConfig,
|
|
117
|
+
type UsageBudgetConfig,
|
|
109
118
|
type SubagentRunMode,
|
|
110
119
|
type SubagentState,
|
|
111
120
|
ASYNC_DIR,
|
|
@@ -167,6 +176,7 @@ export interface SubagentParamsLike {
|
|
|
167
176
|
/** Internal-only strict turn-boundary enforcement for versioned foreground delegation. */
|
|
168
177
|
enforceHardTurnLimit?: boolean;
|
|
169
178
|
toolBudget?: ToolBudgetConfig;
|
|
179
|
+
usageBudget?: UsageBudgetConfig;
|
|
170
180
|
clarify?: boolean;
|
|
171
181
|
share?: boolean;
|
|
172
182
|
control?: ControlConfig;
|
|
@@ -192,6 +202,15 @@ export interface SubagentParamsLike {
|
|
|
192
202
|
additional?: number;
|
|
193
203
|
}
|
|
194
204
|
|
|
205
|
+
function rememberParentModel(state: { currentSessionId?: string | null; lastParentModel?: ParentModel }, sessionId: string | null, model: unknown): ParentModel | undefined {
|
|
206
|
+
if (state.currentSessionId !== sessionId) state.lastParentModel = undefined;
|
|
207
|
+
state.currentSessionId = sessionId;
|
|
208
|
+
const parentModel = normalizeParentModel(model);
|
|
209
|
+
if (!sessionId) return parentModel;
|
|
210
|
+
if (parentModel) state.lastParentModel = parentModel;
|
|
211
|
+
return parentModel ?? state.lastParentModel;
|
|
212
|
+
}
|
|
213
|
+
|
|
195
214
|
interface ExecutorDeps {
|
|
196
215
|
pi: ExtensionAPI;
|
|
197
216
|
state: SubagentState;
|
|
@@ -236,10 +255,12 @@ interface ExecutionContextData {
|
|
|
236
255
|
deadlineAt?: number;
|
|
237
256
|
turnBudget?: ResolvedTurnBudget;
|
|
238
257
|
toolBudget?: ResolvedToolBudget;
|
|
258
|
+
usageBudget?: UsageBudgetConfig;
|
|
239
259
|
allowZeroToolBudget?: boolean;
|
|
240
260
|
configToolBudget?: ResolvedToolBudget;
|
|
241
261
|
contextPolicy: AgentDefaultContextPolicy;
|
|
242
262
|
modelScope?: ModelScopeConfig;
|
|
263
|
+
parentModel?: ParentModel;
|
|
243
264
|
parentSessionId: string | null;
|
|
244
265
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
245
266
|
}
|
|
@@ -248,6 +269,15 @@ function resolveRequestedCwd(runtimeCwd: string, requestedCwd: string | undefine
|
|
|
248
269
|
return requestedCwd ? path.resolve(runtimeCwd, requestedCwd) : runtimeCwd;
|
|
249
270
|
}
|
|
250
271
|
|
|
272
|
+
function removeForegroundControlIfIdle(state: SubagentState, runId: string): boolean {
|
|
273
|
+
const control = state.foregroundControls.get(runId);
|
|
274
|
+
if (control && (!foregroundSchedulingSettled(control) || (control.activeChildren?.size ?? 0) > 0)) return false;
|
|
275
|
+
clearPendingForegroundControlNotices(state, runId);
|
|
276
|
+
state.foregroundControls.delete(runId);
|
|
277
|
+
if (state.lastForegroundControlId === runId) state.lastForegroundControlId = null;
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
|
|
251
281
|
function getForegroundControl(state: SubagentState, runId: string | undefined) {
|
|
252
282
|
if (runId) return state.foregroundControls.get(runId);
|
|
253
283
|
if (state.lastForegroundControlId) {
|
|
@@ -382,7 +412,7 @@ function foregroundChildActivityFromProgress(progress: SingleResult["progress"]
|
|
|
382
412
|
};
|
|
383
413
|
}
|
|
384
414
|
|
|
385
|
-
function rememberForegroundRun(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[] }): void {
|
|
415
|
+
function rememberForegroundRun(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[]; checkpoint?: Details["checkpoint"] }): void {
|
|
386
416
|
state.foregroundRuns ??= new Map();
|
|
387
417
|
const previous = state.foregroundRuns.get(input.runId);
|
|
388
418
|
const updatedAt = Date.now();
|
|
@@ -392,26 +422,41 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
392
422
|
cwd: input.cwd,
|
|
393
423
|
...(input.sessionId ? { sessionId: input.sessionId } : {}),
|
|
394
424
|
updatedAt,
|
|
425
|
+
...(input.checkpoint ? { checkpoint: input.checkpoint } : {}),
|
|
395
426
|
children: input.results.map((result, index) => {
|
|
396
427
|
const child = {
|
|
397
428
|
agent: result.agent,
|
|
398
429
|
index,
|
|
399
430
|
...(result.context ? { context: result.context } : {}),
|
|
400
|
-
status: resolveSubagentResultStatus({
|
|
431
|
+
status: resolveSubagentResultStatus({
|
|
432
|
+
exitCode: result.exitCode,
|
|
433
|
+
interrupted: result.interrupted,
|
|
434
|
+
detached: result.detached,
|
|
435
|
+
processSignal: result.processSignal,
|
|
436
|
+
timedOut: result.timedOut,
|
|
437
|
+
stopped: result.stopped,
|
|
438
|
+
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
439
|
+
}),
|
|
401
440
|
...foregroundChildActivityFromProgress(result.progress),
|
|
402
441
|
updatedAt,
|
|
403
442
|
...(result.exitCode !== undefined ? { exitCode: result.exitCode } : {}),
|
|
404
443
|
...(result.error ? { error: result.error } : {}),
|
|
405
444
|
...(result.finalOutput ? { finalOutput: result.finalOutput } : {}),
|
|
445
|
+
...(result.outputState ? { outputState: result.outputState } : {}),
|
|
406
446
|
...(result.outputMode ? { outputMode: result.outputMode } : {}),
|
|
407
447
|
...(result.savedOutputPath ? { savedOutputPath: result.savedOutputPath } : {}),
|
|
408
448
|
...(result.outputSaveError ? { outputSaveError: result.outputSaveError } : {}),
|
|
409
449
|
...(result.sessionFile ? { sessionFile: result.sessionFile } : {}),
|
|
450
|
+
...(result.model ? { model: result.model } : {}),
|
|
451
|
+
...(result.thinking ? { thinking: result.thinking } : {}),
|
|
410
452
|
...(result.artifactPaths ? { artifactPaths: result.artifactPaths } : {}),
|
|
411
453
|
...(result.transcriptPath ? { transcriptPath: result.transcriptPath } : {}),
|
|
412
454
|
...(result.transcriptError ? { transcriptError: result.transcriptError } : {}),
|
|
413
455
|
...(result.detachedReason ? { detachedReason: result.detachedReason } : {}),
|
|
414
456
|
...(result.acceptance ? { acceptance: result.acceptance } : {}),
|
|
457
|
+
...(result.launchContractDigest ? { launchContractDigest: result.launchContractDigest } : {}),
|
|
458
|
+
...(result.launchResolvedExtensions ? { launchResolvedExtensions: result.launchResolvedExtensions } : {}),
|
|
459
|
+
...(result.runtimeAcknowledgedExtensions ? { runtimeAcknowledgedExtensions: result.runtimeAcknowledgedExtensions } : {}),
|
|
415
460
|
...(result.capabilityCeiling ? { capabilityCeiling: result.capabilityCeiling } : {}),
|
|
416
461
|
...(result.capabilityAudit ? { capabilityAudit: result.capabilityAudit } : {}),
|
|
417
462
|
};
|
|
@@ -454,37 +499,57 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
454
499
|
state.foregroundRuns.set(input.runId, run);
|
|
455
500
|
}
|
|
456
501
|
run.updatedAt = updatedAt;
|
|
502
|
+
const terminalStatus = resolveSubagentResultStatus({
|
|
503
|
+
exitCode: input.result.exitCode,
|
|
504
|
+
...(input.result.acceptance?.status === "rejected" ? { success: false } : {}),
|
|
505
|
+
interrupted: input.result.interrupted,
|
|
506
|
+
detached: false,
|
|
507
|
+
processSignal: input.result.processSignal,
|
|
508
|
+
timedOut: input.result.timedOut,
|
|
509
|
+
stopped: input.result.stopped,
|
|
510
|
+
turnBudgetExceeded: input.result.turnBudgetExceeded,
|
|
511
|
+
});
|
|
457
512
|
const child = run.children[input.index] ?? { agent: input.result.agent, index: input.index, status: "detached" as const };
|
|
458
513
|
run.children[input.index] = {
|
|
459
514
|
...child,
|
|
460
515
|
agent: input.result.agent,
|
|
461
516
|
index: input.index,
|
|
462
517
|
...(input.result.context ? { context: input.result.context } : {}),
|
|
463
|
-
status:
|
|
464
|
-
? "failed"
|
|
465
|
-
: resolveSubagentResultStatus({ exitCode: input.result.exitCode, interrupted: input.result.interrupted, detached: false }),
|
|
518
|
+
status: terminalStatus,
|
|
466
519
|
...foregroundChildActivityFromProgress(input.result.progress),
|
|
467
520
|
updatedAt,
|
|
468
521
|
...(input.result.exitCode !== undefined ? { exitCode: input.result.exitCode } : {}),
|
|
469
522
|
...(input.result.error ? { error: input.result.error } : {}),
|
|
470
|
-
|
|
523
|
+
// Reference-first remembered state: when an authoritative saved output path
|
|
524
|
+
// exists, do not retain the full final output in memory; status/transcript
|
|
525
|
+
// views recover it from `savedOutputPath` via `rememberedForegroundChildOutput`
|
|
526
|
+
// (runs/background/run-status.ts) and the TUI via `resultOutputForUi`
|
|
527
|
+
// (tui/render.ts).
|
|
528
|
+
...(input.result.finalOutput && !input.result.savedOutputPath ? { finalOutput: input.result.finalOutput } : {}),
|
|
529
|
+
outputState: input.result.outputState,
|
|
471
530
|
outputMode: input.result.outputMode,
|
|
472
531
|
savedOutputPath: input.result.savedOutputPath,
|
|
473
532
|
outputSaveError: input.result.outputSaveError,
|
|
474
533
|
...(input.result.sessionFile ? { sessionFile: input.result.sessionFile } : {}),
|
|
534
|
+
...(input.result.model ? { model: input.result.model } : {}),
|
|
535
|
+
...(input.result.thinking ? { thinking: input.result.thinking } : {}),
|
|
475
536
|
...(input.result.artifactPaths ? { artifactPaths: input.result.artifactPaths } : {}),
|
|
476
537
|
...(input.result.transcriptPath ? { transcriptPath: input.result.transcriptPath } : {}),
|
|
477
538
|
...(input.result.transcriptError ? { transcriptError: input.result.transcriptError } : {}),
|
|
478
539
|
...(input.result.detachedReason ? { detachedReason: input.result.detachedReason } : {}),
|
|
479
540
|
...(input.result.acceptance ? { acceptance: input.result.acceptance } : {}),
|
|
541
|
+
...(input.result.launchContractDigest ? { launchContractDigest: input.result.launchContractDigest } : {}),
|
|
542
|
+
...(input.result.launchResolvedExtensions ? { launchResolvedExtensions: input.result.launchResolvedExtensions } : {}),
|
|
543
|
+
...(input.result.runtimeAcknowledgedExtensions ? { runtimeAcknowledgedExtensions: input.result.runtimeAcknowledgedExtensions } : {}),
|
|
480
544
|
...(input.result.capabilityCeiling ? { capabilityCeiling: input.result.capabilityCeiling } : {}),
|
|
481
545
|
...(input.result.capabilityAudit ? { capabilityAudit: input.result.capabilityAudit } : {}),
|
|
482
546
|
};
|
|
483
547
|
trimRememberedForegroundRuns(state);
|
|
484
|
-
const
|
|
485
|
-
const
|
|
548
|
+
const reference = input.result.outputReference?.message;
|
|
549
|
+
const output = reference ?? getSingleResultOutput(input.result).trim();
|
|
550
|
+
const success = terminalStatus === "completed";
|
|
486
551
|
const summary = !success && input.result.error
|
|
487
|
-
? `${input.result.error}${output ? `\n\
|
|
552
|
+
? `${input.result.error}${output ? `\n\n${output}` : ""}`
|
|
488
553
|
: output || input.result.error || "Detached child exited without final output.";
|
|
489
554
|
// A detached callback may outlive its extension runtime. Stale sessions are
|
|
490
555
|
// intentionally dropped rather than routed through a replacement runtime.
|
|
@@ -498,7 +563,12 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
498
563
|
success,
|
|
499
564
|
summary,
|
|
500
565
|
exitCode: input.result.exitCode,
|
|
501
|
-
state:
|
|
566
|
+
state: terminalStatus === "completed" ? "complete" : terminalStatus,
|
|
567
|
+
...(input.result.interrupted !== undefined ? { interrupted: input.result.interrupted } : {}),
|
|
568
|
+
...(input.result.stopped !== undefined ? { stopped: input.result.stopped } : {}),
|
|
569
|
+
...(input.result.processSignal !== undefined ? { processSignal: input.result.processSignal } : {}),
|
|
570
|
+
...(input.result.timedOut !== undefined ? { timedOut: input.result.timedOut } : {}),
|
|
571
|
+
...(input.result.turnBudgetExceeded !== undefined ? { turnBudgetExceeded: input.result.turnBudgetExceeded } : {}),
|
|
502
572
|
timestamp: updatedAt,
|
|
503
573
|
cwd: input.cwd,
|
|
504
574
|
sessionFile: input.result.sessionFile,
|
|
@@ -507,7 +577,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
507
577
|
});
|
|
508
578
|
}
|
|
509
579
|
|
|
510
|
-
function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: "single" | "parallel" | "chain"; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } | undefined {
|
|
580
|
+
function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: "single" | "parallel" | "chain"; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string; model?: string; thinking?: string; launchContractDigest?: string; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } | undefined {
|
|
511
581
|
const requested = (params.id ?? params.runId)?.trim();
|
|
512
582
|
if (!requested || !state.foregroundRuns?.size || !state.currentSessionId) return undefined;
|
|
513
583
|
const sessionRuns = [...state.foregroundRuns.values()].filter((run) => run.sessionId === state.currentSessionId);
|
|
@@ -534,6 +604,9 @@ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: Subage
|
|
|
534
604
|
index,
|
|
535
605
|
cwd: run.cwd,
|
|
536
606
|
sessionFile,
|
|
607
|
+
...(child.model ? { model: child.model } : {}),
|
|
608
|
+
...(child.thinking ? { thinking: child.thinking } : {}),
|
|
609
|
+
...(child.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {}),
|
|
537
610
|
...(child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {}),
|
|
538
611
|
};
|
|
539
612
|
}
|
|
@@ -714,50 +787,6 @@ function interruptAsyncRun(
|
|
|
714
787
|
}
|
|
715
788
|
}
|
|
716
789
|
|
|
717
|
-
function stopAsyncRun(
|
|
718
|
-
state: SubagentState,
|
|
719
|
-
runId: string | undefined,
|
|
720
|
-
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean,
|
|
721
|
-
location?: { asyncDir: string | null; resolvedId?: string },
|
|
722
|
-
): AgentToolResult<Details> | null {
|
|
723
|
-
const target = getAsyncInterruptTarget(state, runId, location, { fallbackToNewest: false });
|
|
724
|
-
if (!target) return null;
|
|
725
|
-
const status = reconcileAsyncRun(target.asyncDir, { kill }).status;
|
|
726
|
-
if (state.currentSessionId && status?.sessionId !== state.currentSessionId) {
|
|
727
|
-
return {
|
|
728
|
-
content: [{ type: "text", text: `Async run '${target.asyncId}' was not found in the active session.` }],
|
|
729
|
-
isError: true,
|
|
730
|
-
details: { mode: "management", results: [] },
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
if (!status || (status.state !== "running" && status.state !== "queued")) {
|
|
734
|
-
return {
|
|
735
|
-
content: [{ type: "text", text: `No running or queued async run was found for '${runId ?? "current"}'.` }],
|
|
736
|
-
isError: true,
|
|
737
|
-
details: { mode: "management", results: [] },
|
|
738
|
-
};
|
|
739
|
-
}
|
|
740
|
-
try {
|
|
741
|
-
deliverStopRequest({ asyncDir: target.asyncDir, pid: typeof status.pid === "number" ? status.pid : undefined, kill, source: "stop-action" });
|
|
742
|
-
const tracked = state.asyncJobs.get(target.asyncId);
|
|
743
|
-
if (tracked) {
|
|
744
|
-
tracked.activityState = undefined;
|
|
745
|
-
tracked.updatedAt = Date.now();
|
|
746
|
-
}
|
|
747
|
-
return {
|
|
748
|
-
content: [{ type: "text", text: `Stop requested for async run ${target.asyncId}.` }],
|
|
749
|
-
details: { mode: "management", results: [] },
|
|
750
|
-
};
|
|
751
|
-
} catch (error) {
|
|
752
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
753
|
-
return {
|
|
754
|
-
content: [{ type: "text", text: `Failed to stop async run ${target.asyncId}: ${message}` }],
|
|
755
|
-
isError: true,
|
|
756
|
-
details: { mode: "management", results: [] },
|
|
757
|
-
};
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
|
|
761
790
|
function duplicateNames(names: string[]): string[] {
|
|
762
791
|
const seen = new Set<string>();
|
|
763
792
|
const duplicates = new Set<string>();
|
|
@@ -773,6 +802,7 @@ function appendStepToAsyncChain(input: {
|
|
|
773
802
|
requestCwd: string;
|
|
774
803
|
ctx: ExtensionContext;
|
|
775
804
|
deps: ExecutorDeps;
|
|
805
|
+
parentModel?: ParentModel;
|
|
776
806
|
}): AgentToolResult<Details> {
|
|
777
807
|
const targetRunId = input.params.id ?? input.params.runId;
|
|
778
808
|
if (!targetRunId) {
|
|
@@ -877,13 +907,14 @@ function appendStepToAsyncChain(input: {
|
|
|
877
907
|
const contextPolicy = resolveExplicitContextPolicy(input.params);
|
|
878
908
|
const chainSkillInput = normalizeSkillInput(input.params.skill);
|
|
879
909
|
const chainSkills = chainSkillInput === false ? [] : (chainSkillInput ?? []);
|
|
910
|
+
const parentModel = input.parentModel;
|
|
880
911
|
const asyncCtx = {
|
|
881
912
|
pi: input.deps.pi,
|
|
882
913
|
cwd: input.ctx.cwd,
|
|
883
914
|
currentSessionId: resolveCurrentSessionId(input.ctx.sessionManager),
|
|
884
915
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
885
|
-
currentModelProvider:
|
|
886
|
-
currentModel:
|
|
916
|
+
currentModelProvider: parentModel?.provider,
|
|
917
|
+
currentModel: parentModel,
|
|
887
918
|
modelScope: discoveredForAppend.modelScope,
|
|
888
919
|
interactive: input.ctx.hasUI,
|
|
889
920
|
};
|
|
@@ -1011,10 +1042,10 @@ function resolveNestedResumeTarget(match: ResolvedSubagentRunId & { kind: "neste
|
|
|
1011
1042
|
};
|
|
1012
1043
|
}
|
|
1013
1044
|
|
|
1014
|
-
async function waitForNestedControlResult(target: ResolvedSubagentRunId & { kind: "nested" }, requestId: string, timeoutMs = 1_000) {
|
|
1045
|
+
async function waitForNestedControlResult(target: ResolvedSubagentRunId & { kind: "nested" }, requestId: string, ignoredFiles: ReadonlySet<string>, timeoutMs = 1_000) {
|
|
1015
1046
|
const deadline = Date.now() + timeoutMs;
|
|
1016
1047
|
while (Date.now() < deadline) {
|
|
1017
|
-
const result =
|
|
1048
|
+
const result = findNestedControlResult(target.match.route, requestId, target.match.run.id, ignoredFiles);
|
|
1018
1049
|
if (result) return result;
|
|
1019
1050
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1020
1051
|
}
|
|
@@ -1023,14 +1054,16 @@ async function waitForNestedControlResult(target: ResolvedSubagentRunId & { kind
|
|
|
1023
1054
|
|
|
1024
1055
|
async function sendNestedControlRequest(target: ResolvedSubagentRunId & { kind: "nested" }, action: "interrupt" | "resume", message?: string) {
|
|
1025
1056
|
const requestId = randomUUID();
|
|
1057
|
+
const ignoredFiles = snapshotNestedEventFiles(target.match.route);
|
|
1058
|
+
const requestedAt = Date.now();
|
|
1026
1059
|
writeNestedControlRequest(target.match.route, {
|
|
1027
|
-
ts:
|
|
1060
|
+
ts: requestedAt,
|
|
1028
1061
|
requestId,
|
|
1029
1062
|
targetRunId: target.match.run.id,
|
|
1030
1063
|
action,
|
|
1031
1064
|
...(message ? { message } : {}),
|
|
1032
1065
|
});
|
|
1033
|
-
return waitForNestedControlResult(target, requestId);
|
|
1066
|
+
return waitForNestedControlResult(target, requestId, ignoredFiles);
|
|
1034
1067
|
}
|
|
1035
1068
|
|
|
1036
1069
|
function directNestedAsyncInterrupt(target: ResolvedSubagentRunId & { kind: "nested" }): AgentToolResult<Details> | undefined {
|
|
@@ -1121,6 +1154,7 @@ async function resumeAsyncRun(input: {
|
|
|
1121
1154
|
requestCwd: string;
|
|
1122
1155
|
ctx: ExtensionContext;
|
|
1123
1156
|
deps: ExecutorDeps;
|
|
1157
|
+
parentModel?: ParentModel;
|
|
1124
1158
|
absoluteDeadlineAt?: number;
|
|
1125
1159
|
}): Promise<AgentToolResult<Details>> {
|
|
1126
1160
|
const followUp = (input.params.message ?? input.params.task ?? "").trim();
|
|
@@ -1132,6 +1166,13 @@ async function resumeAsyncRun(input: {
|
|
|
1132
1166
|
details: { mode: "management", results: [] },
|
|
1133
1167
|
};
|
|
1134
1168
|
}
|
|
1169
|
+
if (input.params.model !== undefined) {
|
|
1170
|
+
return {
|
|
1171
|
+
content: [{ type: "text", text: "action='resume' reuses the persisted child model and does not accept a model override." }],
|
|
1172
|
+
isError: true,
|
|
1173
|
+
details: { mode: "management", results: [] },
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1135
1176
|
const acceptanceErrors = validateExecutionAcceptance(input.params);
|
|
1136
1177
|
if (acceptanceErrors.length > 0) {
|
|
1137
1178
|
return {
|
|
@@ -1252,13 +1293,14 @@ async function resumeAsyncRun(input: {
|
|
|
1252
1293
|
};
|
|
1253
1294
|
}
|
|
1254
1295
|
const runId = randomUUID().slice(0, 8);
|
|
1255
|
-
const artifactConfig: ArtifactConfig = { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts
|
|
1296
|
+
const artifactConfig: ArtifactConfig = { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts === true, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir };
|
|
1256
1297
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
1257
1298
|
const contextPolicy = resolveExplicitContextPolicy(input.params);
|
|
1258
1299
|
const workflowTask = (input.params.task ?? followUp) || undefined;
|
|
1259
1300
|
const goal = resolveAsyncEventGoal(workflowTask, attachChain);
|
|
1260
1301
|
const chain = wrapChainTasksForFork(attachChain, contextPolicy);
|
|
1261
1302
|
const normalized = normalizeSkillInput(input.params.skill);
|
|
1303
|
+
const parentModel = input.parentModel;
|
|
1262
1304
|
const result = executeAsyncChain(runId, {
|
|
1263
1305
|
chain,
|
|
1264
1306
|
task: workflowTask,
|
|
@@ -1277,8 +1319,8 @@ async function resumeAsyncRun(input: {
|
|
|
1277
1319
|
cwd: input.requestCwd,
|
|
1278
1320
|
currentSessionId: input.deps.state.currentSessionId,
|
|
1279
1321
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1280
|
-
currentModelProvider:
|
|
1281
|
-
currentModel:
|
|
1322
|
+
currentModelProvider: parentModel?.provider,
|
|
1323
|
+
currentModel: parentModel,
|
|
1282
1324
|
modelScope,
|
|
1283
1325
|
interactive: input.ctx.hasUI,
|
|
1284
1326
|
},
|
|
@@ -1317,9 +1359,10 @@ async function resumeAsyncRun(input: {
|
|
|
1317
1359
|
|
|
1318
1360
|
const runId = randomUUID().slice(0, 8);
|
|
1319
1361
|
const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(agentConfig, recoveryDescriptor) : agentConfig;
|
|
1320
|
-
const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts
|
|
1362
|
+
const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts === true, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir };
|
|
1321
1363
|
const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
1322
1364
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
1365
|
+
const parentModel = input.parentModel;
|
|
1323
1366
|
const result = executeAsyncSingle(runId, {
|
|
1324
1367
|
agent: target.agent,
|
|
1325
1368
|
task: buildRevivedAsyncTask(target, followUp),
|
|
@@ -1330,8 +1373,8 @@ async function resumeAsyncRun(input: {
|
|
|
1330
1373
|
cwd: input.requestCwd,
|
|
1331
1374
|
currentSessionId: input.deps.state.currentSessionId,
|
|
1332
1375
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1333
|
-
currentModelProvider:
|
|
1334
|
-
currentModel:
|
|
1376
|
+
currentModelProvider: parentModel?.provider,
|
|
1377
|
+
currentModel: parentModel,
|
|
1335
1378
|
modelScope,
|
|
1336
1379
|
interactive: input.ctx.hasUI,
|
|
1337
1380
|
},
|
|
@@ -1349,8 +1392,8 @@ async function resumeAsyncRun(input: {
|
|
|
1349
1392
|
sourceRunId: target.runId,
|
|
1350
1393
|
...(input.deps.state.currentSessionId ? { parentSessionId: input.deps.state.currentSessionId } : {}),
|
|
1351
1394
|
},
|
|
1352
|
-
modelOverride:
|
|
1353
|
-
thinkingOverride:
|
|
1395
|
+
modelOverride: recoveryDescriptor?.model ?? target.model,
|
|
1396
|
+
thinkingOverride: recoveryDescriptor?.thinking ?? target.thinking,
|
|
1354
1397
|
outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
|
|
1355
1398
|
maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1356
1399
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
@@ -1361,8 +1404,8 @@ async function resumeAsyncRun(input: {
|
|
|
1361
1404
|
controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
|
|
1362
1405
|
childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
|
|
1363
1406
|
availableModels,
|
|
1364
|
-
output: recoveryDescriptor?.outputPath,
|
|
1365
|
-
outputMode: recoveryDescriptor?.outputMode,
|
|
1407
|
+
output: typeof input.params.output === "string" ? input.params.output : recoveryDescriptor?.outputPath,
|
|
1408
|
+
outputMode: input.params.outputMode ?? recoveryDescriptor?.outputMode,
|
|
1366
1409
|
...(recoveryDescriptor?.agentContract ? { agentContract: recoveryDescriptor.agentContract } : {}),
|
|
1367
1410
|
...(recoveryDescriptor?.structuredOutputSchema ? { structuredOutputSchema: recoveryDescriptor.structuredOutputSchema } : {}),
|
|
1368
1411
|
...(recoveryDescriptor?.skills ? { skills: [...recoveryDescriptor.skills] } : {}),
|
|
@@ -1387,18 +1430,92 @@ async function resumeAsyncRun(input: {
|
|
|
1387
1430
|
revivedTarget ? `Intercom target: ${revivedTarget} (if registered)` : undefined,
|
|
1388
1431
|
`Status if needed: subagent({ action: "status", id: "${revivedId}" })`,
|
|
1389
1432
|
].filter((line): line is string => Boolean(line));
|
|
1390
|
-
return {
|
|
1433
|
+
return {
|
|
1434
|
+
content: [{ type: "text", text: formatAsyncStartedMessage(lines.join("\n"), input.ctx.hasUI) }],
|
|
1435
|
+
details: {
|
|
1436
|
+
...result.details,
|
|
1437
|
+
...(target.launchContractDigest ? { sourceLaunchContractDigest: target.launchContractDigest } : {}),
|
|
1438
|
+
},
|
|
1439
|
+
};
|
|
1391
1440
|
}
|
|
1392
1441
|
|
|
1393
1442
|
function resultSummaryForIntercom(result: SingleResult): string {
|
|
1394
|
-
const
|
|
1395
|
-
if (
|
|
1396
|
-
return
|
|
1443
|
+
const reference = result.outputReference?.message;
|
|
1444
|
+
if (reference) {
|
|
1445
|
+
return result.exitCode !== 0 && result.error ? `${result.error}\n\n${reference}` : reference;
|
|
1446
|
+
}
|
|
1447
|
+
if (result.savedOutputPath) {
|
|
1448
|
+
try {
|
|
1449
|
+
const ref = formatSavedOutputReference(result.savedOutputPath, fs.readFileSync(result.savedOutputPath, "utf-8"));
|
|
1450
|
+
return result.exitCode !== 0 && result.error ? `${result.error}\n\n${ref.message}` : ref.message;
|
|
1451
|
+
} catch (error) {
|
|
1452
|
+
const fallback = formatBoundedPersistenceFallback({
|
|
1453
|
+
error: `Failed to read saved output: ${error instanceof Error ? error.message : String(error)}`,
|
|
1454
|
+
outputPath: result.savedOutputPath,
|
|
1455
|
+
fullOutput: getSingleResultOutput(result),
|
|
1456
|
+
exitCode: result.exitCode,
|
|
1457
|
+
processError: result.error,
|
|
1458
|
+
});
|
|
1459
|
+
return result.exitCode !== 0 && result.error ? `${result.error}\n\n${fallback}` : fallback;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
if (result.outputSaveError) {
|
|
1463
|
+
return formatBoundedPersistenceFallback({
|
|
1464
|
+
error: result.outputSaveError,
|
|
1465
|
+
outputPath: result.savedOutputPath,
|
|
1466
|
+
fullOutput: getSingleResultOutput(result),
|
|
1467
|
+
exitCode: result.exitCode,
|
|
1468
|
+
processError: result.error,
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
// Legacy / `output: false` child without a durable saved path: bounded fallback
|
|
1472
|
+
// only, never the raw full output.
|
|
1473
|
+
return formatBoundedPersistenceFallback({
|
|
1474
|
+
error: "No saved output path is available for this child.",
|
|
1475
|
+
fullOutput: getSingleResultOutput(result),
|
|
1476
|
+
exitCode: result.exitCode,
|
|
1477
|
+
processError: result.error,
|
|
1478
|
+
});
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
/**
|
|
1482
|
+
* Compact terminal per-child line for bridge-off foreground parallel content:
|
|
1483
|
+
* status plus either the saved-output reference (default/file-only), the legacy
|
|
1484
|
+
* inline full output when explicitly requested, or the bounded fallback.
|
|
1485
|
+
*/
|
|
1486
|
+
function formatParallelChildResult(result: SingleResult, index: number): string {
|
|
1487
|
+
const status = resolveSubagentResultStatus({
|
|
1488
|
+
exitCode: result.exitCode,
|
|
1489
|
+
interrupted: result.interrupted,
|
|
1490
|
+
detached: result.detached,
|
|
1491
|
+
processSignal: result.processSignal,
|
|
1492
|
+
timedOut: result.timedOut,
|
|
1493
|
+
stopped: result.stopped,
|
|
1494
|
+
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
1495
|
+
});
|
|
1496
|
+
const header = `=== Task ${index + 1}: ${result.agent} [${status}] ===`;
|
|
1497
|
+
if (result.outputMode === "inline") {
|
|
1498
|
+
const output = getSingleResultOutput(result).trim();
|
|
1499
|
+
return `${header}\n${output || "(no output)"}`;
|
|
1500
|
+
}
|
|
1501
|
+
const reference = result.outputReference?.message;
|
|
1502
|
+
if (reference) {
|
|
1503
|
+
const body = result.exitCode !== 0 && result.error ? `${result.error}\n${reference}` : reference;
|
|
1504
|
+
return `${header}\n${body}`;
|
|
1397
1505
|
}
|
|
1398
|
-
return
|
|
1506
|
+
return `${header}\n${formatBoundedPersistenceFallback({
|
|
1507
|
+
error: result.outputSaveError ?? "No saved output path is available for this child.",
|
|
1508
|
+
outputPath: result.savedOutputPath,
|
|
1509
|
+
fullOutput: getSingleResultOutput(result),
|
|
1510
|
+
exitCode: result.exitCode,
|
|
1511
|
+
processError: result.error,
|
|
1512
|
+
})}`;
|
|
1399
1513
|
}
|
|
1400
1514
|
|
|
1401
1515
|
function formatFailedSingleRunOutput(result: SingleResult, displayOutput: string): string {
|
|
1516
|
+
// Reference-first and persistence-failure fallbacks already carry error/status
|
|
1517
|
+
// and must not be re-wrapped as raw "Output:" text.
|
|
1518
|
+
if (result.savedOutputPath || result.outputSaveError) return displayOutput;
|
|
1402
1519
|
const error = result.error || "Failed";
|
|
1403
1520
|
const output = displayOutput.trim();
|
|
1404
1521
|
const lines = [error];
|
|
@@ -1433,14 +1550,19 @@ async function emitForegroundResultIntercom(input: {
|
|
|
1433
1550
|
nestedChildren?: NestedRunSummary[];
|
|
1434
1551
|
parallelHandoff?: Details["parallelHandoff"];
|
|
1435
1552
|
}): Promise<ReturnType<typeof buildSubagentResultIntercomPayload> | null> {
|
|
1436
|
-
if (!input.intercomBridge.active || !input.intercomBridge.orchestratorTarget) return null;
|
|
1553
|
+
if (!input.intercomBridge.active || !input.intercomBridge.resultDelivery || !input.intercomBridge.orchestratorTarget) return null;
|
|
1437
1554
|
const children = input.results.flatMap((result, index) => result.detached ? [] : [{
|
|
1438
1555
|
agent: result.agent,
|
|
1439
1556
|
status: resolveSubagentResultStatus({
|
|
1440
1557
|
exitCode: result.exitCode,
|
|
1441
1558
|
interrupted: result.interrupted,
|
|
1442
1559
|
detached: result.detached,
|
|
1560
|
+
processSignal: result.processSignal,
|
|
1561
|
+
timedOut: result.timedOut,
|
|
1562
|
+
stopped: result.stopped,
|
|
1563
|
+
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
1443
1564
|
}),
|
|
1565
|
+
outputState: result.outputState ?? "unknown",
|
|
1444
1566
|
summary: resultSummaryForIntercom(result),
|
|
1445
1567
|
index,
|
|
1446
1568
|
artifactPath: result.artifactPaths?.outputPath,
|
|
@@ -1487,6 +1609,67 @@ async function maybeBuildForegroundIntercomReceipt(input: {
|
|
|
1487
1609
|
};
|
|
1488
1610
|
}
|
|
1489
1611
|
|
|
1612
|
+
function canonicalizeAgentName(name: string, agents: AgentConfig[]): { name?: string; error?: string } {
|
|
1613
|
+
const resolved = resolveAgentName(name, agents);
|
|
1614
|
+
if (resolved.error) return { error: resolved.error };
|
|
1615
|
+
if (!resolved.agent) return { error: `Unknown agent: ${name}` };
|
|
1616
|
+
return { name: resolved.agent.name };
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentConfig[]): { params?: SubagentParamsLike; error?: string } {
|
|
1620
|
+
const resolve = (name: string, location?: string): { name?: string; error?: string } => {
|
|
1621
|
+
const result = canonicalizeAgentName(name, agents);
|
|
1622
|
+
return result.error && location ? { error: `${result.error} (${location})` } : result;
|
|
1623
|
+
};
|
|
1624
|
+
if (params.agent) {
|
|
1625
|
+
const result = resolve(params.agent);
|
|
1626
|
+
if (result.error) return { error: result.error };
|
|
1627
|
+
params = { ...params, agent: result.name };
|
|
1628
|
+
}
|
|
1629
|
+
if (params.tasks) {
|
|
1630
|
+
const tasks: TaskParam[] = [];
|
|
1631
|
+
for (let index = 0; index < params.tasks.length; index++) {
|
|
1632
|
+
const task = params.tasks[index]!;
|
|
1633
|
+
const result = resolve(task.agent, `task ${index + 1}`);
|
|
1634
|
+
if (result.error) return { error: result.error };
|
|
1635
|
+
tasks.push({ ...task, agent: result.name! });
|
|
1636
|
+
}
|
|
1637
|
+
params = { ...params, tasks };
|
|
1638
|
+
}
|
|
1639
|
+
if (params.chain) {
|
|
1640
|
+
const chain: ChainStep[] = [];
|
|
1641
|
+
for (let index = 0; index < params.chain.length; index++) {
|
|
1642
|
+
const step = params.chain[index]!;
|
|
1643
|
+
if (isParallelStep(step)) {
|
|
1644
|
+
const parallel: typeof step.parallel = [];
|
|
1645
|
+
for (let taskIndex = 0; taskIndex < step.parallel.length; taskIndex++) {
|
|
1646
|
+
const task = step.parallel[taskIndex]!;
|
|
1647
|
+
const result = resolve(task.agent, `step ${index + 1}, task ${taskIndex + 1}`);
|
|
1648
|
+
if (result.error) return { error: result.error };
|
|
1649
|
+
parallel.push({ ...task, agent: result.name! });
|
|
1650
|
+
}
|
|
1651
|
+
chain.push({ ...step, parallel });
|
|
1652
|
+
continue;
|
|
1653
|
+
}
|
|
1654
|
+
if (isDynamicParallelStep(step)) {
|
|
1655
|
+
const result = resolve(step.parallel.agent, `step ${index + 1}`);
|
|
1656
|
+
if (result.error) return { error: result.error };
|
|
1657
|
+
chain.push({ ...step, parallel: { ...step.parallel, agent: result.name! } });
|
|
1658
|
+
continue;
|
|
1659
|
+
}
|
|
1660
|
+
if ("agent" in step && typeof step.agent === "string") {
|
|
1661
|
+
const result = resolve(step.agent, `step ${index + 1}`);
|
|
1662
|
+
if (result.error) return { error: result.error };
|
|
1663
|
+
chain.push({ ...step, agent: result.name! });
|
|
1664
|
+
continue;
|
|
1665
|
+
}
|
|
1666
|
+
chain.push(step);
|
|
1667
|
+
}
|
|
1668
|
+
params = { ...params, chain };
|
|
1669
|
+
}
|
|
1670
|
+
return { params };
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1490
1673
|
function validateExecutionInput(
|
|
1491
1674
|
params: SubagentParamsLike,
|
|
1492
1675
|
agents: AgentConfig[],
|
|
@@ -1699,10 +1882,14 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
|
|
|
1699
1882
|
};
|
|
1700
1883
|
}
|
|
1701
1884
|
|
|
1702
|
-
|
|
1885
|
+
export const DEFAULT_FOREGROUND_TIMEOUT_MS = 30 * 60 * 1000;
|
|
1886
|
+
|
|
1887
|
+
function resolveForegroundTimeout(params: SubagentParamsLike, defaultTimeoutMs?: number): { timeoutMs?: number; error?: string } {
|
|
1703
1888
|
const rawTimeout = params.timeoutMs;
|
|
1704
1889
|
const rawMaxRuntime = params.maxRuntimeMs;
|
|
1705
|
-
if (rawTimeout === undefined && rawMaxRuntime === undefined)
|
|
1890
|
+
if (rawTimeout === undefined && rawMaxRuntime === undefined) {
|
|
1891
|
+
return defaultTimeoutMs === undefined ? {} : { timeoutMs: defaultTimeoutMs };
|
|
1892
|
+
}
|
|
1706
1893
|
for (const [name, value] of [["timeoutMs", rawTimeout], ["maxRuntimeMs", rawMaxRuntime]] as const) {
|
|
1707
1894
|
if (value === undefined) continue;
|
|
1708
1895
|
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
@@ -2044,19 +2231,20 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2044
2231
|
};
|
|
2045
2232
|
}
|
|
2046
2233
|
const id = randomUUID();
|
|
2234
|
+
const parentModel = data.parentModel;
|
|
2047
2235
|
const asyncCtx = {
|
|
2048
2236
|
pi: deps.pi,
|
|
2049
2237
|
cwd: ctx.cwd,
|
|
2050
2238
|
currentSessionId: deps.state.currentSessionId!,
|
|
2051
2239
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
2052
|
-
currentModelProvider:
|
|
2053
|
-
currentModel:
|
|
2240
|
+
currentModelProvider: parentModel?.provider,
|
|
2241
|
+
currentModel: parentModel,
|
|
2054
2242
|
modelScope: data.modelScope,
|
|
2055
2243
|
interactive: ctx.hasUI,
|
|
2056
2244
|
};
|
|
2057
2245
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
2058
2246
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
2059
|
-
const currentProvider =
|
|
2247
|
+
const currentProvider = parentModel?.provider;
|
|
2060
2248
|
const controlIntercomTarget = intercomBridge.active ? intercomBridge.orchestratorTarget : undefined;
|
|
2061
2249
|
const childIntercomTarget = intercomBridge.active ? (agent: string, index: number) => resolveSubagentIntercomTarget(id, agent, index) : undefined;
|
|
2062
2250
|
|
|
@@ -2111,6 +2299,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2111
2299
|
timeoutMs: data.timeoutMs,
|
|
2112
2300
|
turnBudget: data.turnBudget,
|
|
2113
2301
|
toolBudget: data.toolBudget,
|
|
2302
|
+
usageBudget: data.usageBudget,
|
|
2114
2303
|
configToolBudget: data.configToolBudget,
|
|
2115
2304
|
capabilityCeiling: data.capabilityCeiling,
|
|
2116
2305
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
@@ -2153,6 +2342,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2153
2342
|
timeoutMs: data.timeoutMs,
|
|
2154
2343
|
turnBudget: data.turnBudget,
|
|
2155
2344
|
toolBudget: data.toolBudget,
|
|
2345
|
+
usageBudget: data.usageBudget,
|
|
2156
2346
|
configToolBudget: data.configToolBudget,
|
|
2157
2347
|
capabilityCeiling: data.capabilityCeiling,
|
|
2158
2348
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
@@ -2170,11 +2360,10 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2170
2360
|
}
|
|
2171
2361
|
const rawOutput = params.output !== undefined ? params.output : a.output;
|
|
2172
2362
|
const effectiveOutput = normalizeSingleOutputOverride(rawOutput, a.output);
|
|
2173
|
-
const effectiveOutputMode = params.outputMode ?? "inline";
|
|
2174
2363
|
const normalizedSkills = normalizeSkillInput(params.skill);
|
|
2175
2364
|
const skills = normalizedSkills === false ? [] : normalizedSkills;
|
|
2176
2365
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, a.maxSubagentDepth);
|
|
2177
|
-
const modelOverride = resolveEffectiveSubagentModel(params.model as string | undefined, a.model,
|
|
2366
|
+
const modelOverride = resolveEffectiveSubagentModel(params.model as string | undefined, a.model, parentModel, availableModels, currentProvider, { scope: data.modelScope });
|
|
2178
2367
|
return executeAsyncSingle(id, {
|
|
2179
2368
|
agent: params.agent!,
|
|
2180
2369
|
task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
|
|
@@ -2192,7 +2381,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2192
2381
|
context: contextPolicy.contextForAgent(params.agent!),
|
|
2193
2382
|
skills,
|
|
2194
2383
|
output: effectiveOutput,
|
|
2195
|
-
outputMode:
|
|
2384
|
+
outputMode: params.outputMode,
|
|
2196
2385
|
outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
|
|
2197
2386
|
modelOverride,
|
|
2198
2387
|
thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
|
|
@@ -2211,6 +2400,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2211
2400
|
timeoutMs: data.timeoutMs,
|
|
2212
2401
|
turnBudget: data.turnBudget,
|
|
2213
2402
|
toolBudget: data.toolBudget,
|
|
2403
|
+
usageBudget: data.usageBudget,
|
|
2214
2404
|
configToolBudget: data.configToolBudget,
|
|
2215
2405
|
capabilityCeiling: data.capabilityCeiling,
|
|
2216
2406
|
});
|
|
@@ -2246,11 +2436,12 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2246
2436
|
const chainSkills = normalized === false ? [] : (normalized ?? []);
|
|
2247
2437
|
const chain = wrapChainTasksForFork(params.chain as ChainStep[], contextPolicy);
|
|
2248
2438
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
2439
|
+
const chainCtx = normalizeParentModel(ctx.model) || !data.parentModel ? ctx : { ...ctx, model: data.parentModel };
|
|
2249
2440
|
const chainResult = await executeChain({
|
|
2250
2441
|
chain,
|
|
2251
2442
|
task: params.task,
|
|
2252
2443
|
agents,
|
|
2253
|
-
ctx,
|
|
2444
|
+
ctx: chainCtx,
|
|
2254
2445
|
modelScope: data.modelScope,
|
|
2255
2446
|
intercomEvents: deps.pi.events,
|
|
2256
2447
|
signal,
|
|
@@ -2284,8 +2475,18 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2284
2475
|
timeoutMs: data.timeoutMs,
|
|
2285
2476
|
deadlineAt: data.deadlineAt,
|
|
2286
2477
|
turnBudget: data.turnBudget,
|
|
2287
|
-
onDetachedExit: (index, result) =>
|
|
2478
|
+
onDetachedExit: (index, result) => {
|
|
2479
|
+
try {
|
|
2480
|
+
updateRememberedForegroundChild(deps.state, { runId, mode: "chain", cwd: effectiveCwd, sessionId: data.parentSessionId, index, result, events: deps.pi.events });
|
|
2481
|
+
} finally {
|
|
2482
|
+
removeForegroundControlIfIdle(deps.state, runId);
|
|
2483
|
+
}
|
|
2484
|
+
},
|
|
2485
|
+
onForegroundChildSettled: () => {
|
|
2486
|
+
removeForegroundControlIfIdle(deps.state, runId);
|
|
2487
|
+
},
|
|
2288
2488
|
toolBudget: data.toolBudget,
|
|
2489
|
+
usageBudget: data.usageBudget,
|
|
2289
2490
|
configToolBudget: data.configToolBudget,
|
|
2290
2491
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2291
2492
|
capabilityCeiling: data.capabilityCeiling,
|
|
@@ -2300,13 +2501,14 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2300
2501
|
};
|
|
2301
2502
|
}
|
|
2302
2503
|
const id = randomUUID();
|
|
2504
|
+
const parentModel = data.parentModel;
|
|
2303
2505
|
const asyncCtx = {
|
|
2304
2506
|
pi: deps.pi,
|
|
2305
2507
|
cwd: ctx.cwd,
|
|
2306
2508
|
currentSessionId: deps.state.currentSessionId!,
|
|
2307
2509
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
2308
|
-
currentModelProvider:
|
|
2309
|
-
currentModel:
|
|
2510
|
+
currentModelProvider: parentModel?.provider,
|
|
2511
|
+
currentModel: parentModel,
|
|
2310
2512
|
modelScope: data.modelScope,
|
|
2311
2513
|
interactive: ctx.hasUI,
|
|
2312
2514
|
};
|
|
@@ -2344,20 +2546,22 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2344
2546
|
timeoutMs: data.timeoutMs,
|
|
2345
2547
|
turnBudget: data.turnBudget,
|
|
2346
2548
|
toolBudget: data.toolBudget,
|
|
2549
|
+
usageBudget: data.usageBudget,
|
|
2347
2550
|
configToolBudget: data.configToolBudget,
|
|
2348
2551
|
capabilityCeiling: data.capabilityCeiling,
|
|
2349
2552
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2350
2553
|
});
|
|
2351
2554
|
}
|
|
2352
2555
|
|
|
2353
|
-
const rawChainDetails = chainResult.details ? { ...chainResult.details, runId } : undefined;
|
|
2556
|
+
const rawChainDetails = chainResult.details ? { ...chainResult.details, runId, timeoutMs: data.timeoutMs } : undefined;
|
|
2354
2557
|
if (foregroundControl && rawChainDetails) {
|
|
2355
2558
|
updateForegroundNestedProjection(foregroundControl);
|
|
2356
2559
|
attachRootChildrenToSteps(runId, rawChainDetails.results, foregroundControl.nestedChildren);
|
|
2357
2560
|
rawChainDetails.totalCost = sumResultsCost(rawChainDetails.results);
|
|
2561
|
+
rawChainDetails.usageBudget = usageBudgetState(data.usageBudget, rawChainDetails.totalCost);
|
|
2358
2562
|
}
|
|
2359
2563
|
const chainDetails = rawChainDetails ? compactForegroundDetails(rawChainDetails) : undefined;
|
|
2360
|
-
if (chainDetails) rememberForegroundRun(deps.state, { runId, mode: "chain", cwd: effectiveCwd, sessionId: data.parentSessionId, results: chainDetails.results });
|
|
2564
|
+
if (chainDetails) rememberForegroundRun(deps.state, { runId, mode: "chain", cwd: effectiveCwd, sessionId: data.parentSessionId, results: chainDetails.results, checkpoint: chainDetails.checkpoint });
|
|
2361
2565
|
const intercomReceipt = chainDetails && !chainDetails.results.some((result) => result.interrupted || result.detached)
|
|
2362
2566
|
? await maybeBuildForegroundIntercomReceipt({
|
|
2363
2567
|
pi: deps.pi,
|
|
@@ -2406,6 +2610,7 @@ interface ForegroundParallelRunInput {
|
|
|
2406
2610
|
waitToolEnabled?: boolean;
|
|
2407
2611
|
availableModels: ModelInfo[];
|
|
2408
2612
|
modelScope?: ModelScopeConfig;
|
|
2613
|
+
parentModel?: ParentModel;
|
|
2409
2614
|
modelOverrides: (string | undefined)[];
|
|
2410
2615
|
behaviors: Array<ReturnType<typeof resolveStepBehavior>>;
|
|
2411
2616
|
firstProgressIndex: number;
|
|
@@ -2424,6 +2629,7 @@ interface ForegroundParallelRunInput {
|
|
|
2424
2629
|
timeoutMs?: number;
|
|
2425
2630
|
deadlineAt?: number;
|
|
2426
2631
|
turnBudget?: ResolvedTurnBudget;
|
|
2632
|
+
usageBudget?: UsageBudgetConfig;
|
|
2427
2633
|
toolBudgets: (ResolvedToolBudget | undefined)[];
|
|
2428
2634
|
agentContract?: AgentContract;
|
|
2429
2635
|
}
|
|
@@ -2476,6 +2682,19 @@ function resolveSingleRunOutputBaseDir(deps: ExecutorDeps, artifactsDir: string,
|
|
|
2476
2682
|
: path.join(artifactsDir, "outputs", runId);
|
|
2477
2683
|
}
|
|
2478
2684
|
|
|
2685
|
+
/**
|
|
2686
|
+
* Generated per-run durable output path for foreground single runs when the
|
|
2687
|
+
* caller omits `output` (and does not set `output: false`):
|
|
2688
|
+
* `<singleRunOutputBaseDir>/<runId>/result.md`. The base is the configured
|
|
2689
|
+
* `singleRunOutputBaseDir` when present, otherwise `{artifactsDir}/outputs`.
|
|
2690
|
+
*/
|
|
2691
|
+
function resolveGeneratedSingleOutputPath(deps: ExecutorDeps, artifactsDir: string, runId: string): string {
|
|
2692
|
+
const base = deps.config.singleRunOutputBaseDir
|
|
2693
|
+
? path.resolve(deps.expandTilde(deps.config.singleRunOutputBaseDir))
|
|
2694
|
+
: path.join(artifactsDir, "outputs");
|
|
2695
|
+
return path.join(base, runId, "result.md");
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2479
2698
|
function buildChainWorktreeTaskCwdError(chain: ChainStep[], sharedCwd: string): string | undefined {
|
|
2480
2699
|
for (let stepIndex = 0; stepIndex < chain.length; stepIndex++) {
|
|
2481
2700
|
const step = chain[stepIndex]!;
|
|
@@ -2530,6 +2749,10 @@ function finalizeParallelWorktreeHandoff(input: {
|
|
|
2530
2749
|
interrupted: result.interrupted,
|
|
2531
2750
|
detached: result.detached,
|
|
2532
2751
|
state: result.stopped ? "stopped" : undefined,
|
|
2752
|
+
processSignal: result.processSignal,
|
|
2753
|
+
timedOut: result.timedOut,
|
|
2754
|
+
stopped: result.stopped,
|
|
2755
|
+
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
2533
2756
|
}),
|
|
2534
2757
|
summary: resultSummaryForIntercom(result),
|
|
2535
2758
|
...(result.artifactPaths?.outputPath ? { outputPath: result.artifactPaths.outputPath } : {}),
|
|
@@ -2578,7 +2801,22 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2578
2801
|
for (let i = 0; i < input.tasks.length; i++) {
|
|
2579
2802
|
input.sessionFileForTask(input.tasks[i]!.agent, i, input.modelOverrides[i]);
|
|
2580
2803
|
}
|
|
2804
|
+
const completedResults: SingleResult[] = [];
|
|
2805
|
+
if (input.foregroundControl) retainForegroundSchedulingOwner(input.foregroundControl);
|
|
2581
2806
|
return mapConcurrent(input.tasks, input.concurrencyLimit, async (task, index) => {
|
|
2807
|
+
const budgetState = usageBudgetState(input.usageBudget, sumResultsCost(completedResults));
|
|
2808
|
+
if (budgetState?.exhausted) {
|
|
2809
|
+
return {
|
|
2810
|
+
index,
|
|
2811
|
+
agent: task.agent,
|
|
2812
|
+
task: input.taskTexts[index] ?? "(skipped)",
|
|
2813
|
+
exitCode: 1,
|
|
2814
|
+
messages: [],
|
|
2815
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 },
|
|
2816
|
+
error: usageBudgetExceededMessage(budgetState),
|
|
2817
|
+
skipped: true,
|
|
2818
|
+
} as SingleResult;
|
|
2819
|
+
}
|
|
2582
2820
|
const behavior = input.behaviors[index];
|
|
2583
2821
|
const effectiveSkills = behavior?.skills;
|
|
2584
2822
|
const taskCwd = resolveParallelTaskCwd(task, input.paramsCwd, input.worktreeSetup, index);
|
|
@@ -2597,10 +2835,14 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2597
2835
|
);
|
|
2598
2836
|
const interruptController = new AbortController();
|
|
2599
2837
|
if (input.foregroundControl) {
|
|
2838
|
+
const model = input.modelOverrides[index];
|
|
2839
|
+
const thinking = resolveEffectiveThinking(model, input.thinkingOverrideForTask(task.agent, index, model));
|
|
2600
2840
|
beginForegroundChild(input.foregroundControl, {
|
|
2601
2841
|
index,
|
|
2602
2842
|
agent: task.agent,
|
|
2603
2843
|
description: input.taskDescriptions[index],
|
|
2844
|
+
...(model ? { model } : {}),
|
|
2845
|
+
...(thinking ? { thinking } : {}),
|
|
2604
2846
|
interrupt: () => {
|
|
2605
2847
|
if (interruptController.signal.aborted) return false;
|
|
2606
2848
|
interruptController.abort();
|
|
@@ -2611,7 +2853,8 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2611
2853
|
const structuredRuntime = task.outputSchema
|
|
2612
2854
|
? createStructuredOutputRuntime(task.outputSchema, path.join(input.artifactsDir, "structured-output", input.runId))
|
|
2613
2855
|
: undefined;
|
|
2614
|
-
|
|
2856
|
+
let detachedReceipt = false;
|
|
2857
|
+
const result = await runSync(input.ctx.cwd, input.agents, task.agent, taskText, {
|
|
2615
2858
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
2616
2859
|
context: input.contextPolicy.contextForAgent(task.agent),
|
|
2617
2860
|
cwd: taskCwd,
|
|
@@ -2634,14 +2877,24 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2634
2877
|
capabilityCeiling: input.capabilityCeiling,
|
|
2635
2878
|
controlConfig: input.controlConfig,
|
|
2636
2879
|
onControlEvent: input.onControlEvent,
|
|
2637
|
-
onDetachedExit: (result) =>
|
|
2880
|
+
onDetachedExit: (result) => {
|
|
2881
|
+
try {
|
|
2882
|
+
updateRememberedForegroundChild(input.state, { runId: input.runId, mode: "parallel", cwd: taskCwd, sessionId: input.parentSessionId, index, result, events: input.intercomEvents });
|
|
2883
|
+
} finally {
|
|
2884
|
+
try {
|
|
2885
|
+
if (input.foregroundControl) finishForegroundChild(input.foregroundControl, index);
|
|
2886
|
+
} finally {
|
|
2887
|
+
removeForegroundControlIfIdle(input.state, input.runId);
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
},
|
|
2638
2891
|
intercomSessionName: input.childIntercomTarget?.(task.agent, index),
|
|
2639
2892
|
orchestratorIntercomTarget: input.orchestratorIntercomTarget,
|
|
2640
2893
|
nestedRoute: input.foregroundControl?.nestedRoute,
|
|
2641
2894
|
modelOverride: input.modelOverrides[index],
|
|
2642
2895
|
thinkingOverride: input.thinkingOverrideForTask(task.agent, index, input.modelOverrides[index]),
|
|
2643
2896
|
availableModels: input.availableModels,
|
|
2644
|
-
preferredModelProvider: input.
|
|
2897
|
+
preferredModelProvider: input.parentModel?.provider,
|
|
2645
2898
|
modelScope: input.modelScope,
|
|
2646
2899
|
skills: effectiveSkills === false ? [] : effectiveSkills,
|
|
2647
2900
|
structuredOutput: structuredRuntime,
|
|
@@ -2675,10 +2928,24 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2675
2928
|
});
|
|
2676
2929
|
}
|
|
2677
2930
|
: undefined,
|
|
2931
|
+
}).then((result) => {
|
|
2932
|
+
detachedReceipt = result.detached === true;
|
|
2933
|
+
return result;
|
|
2678
2934
|
}).finally(() => {
|
|
2679
|
-
|
|
2935
|
+
// mapConcurrent rejects before siblings settle, so every attached child
|
|
2936
|
+
// attempts idle removal after releasing its own control. Detached receipts
|
|
2937
|
+
// transfer both responsibilities to the authoritative exit callback.
|
|
2938
|
+
if (!detachedReceipt) {
|
|
2939
|
+
if (input.foregroundControl) finishForegroundChild(input.foregroundControl, index);
|
|
2940
|
+
removeForegroundControlIfIdle(input.state, input.runId);
|
|
2941
|
+
}
|
|
2680
2942
|
});
|
|
2681
|
-
|
|
2943
|
+
completedResults.push(result);
|
|
2944
|
+
return result;
|
|
2945
|
+
}, input.globalSemaphore, () => {
|
|
2946
|
+
if (input.foregroundControl) settleForegroundSchedulingOwner(input.foregroundControl);
|
|
2947
|
+
removeForegroundControlIfIdle(input.state, input.runId);
|
|
2948
|
+
});
|
|
2682
2949
|
}
|
|
2683
2950
|
|
|
2684
2951
|
async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps): Promise<AgentToolResult<Details>> {
|
|
@@ -2746,7 +3013,8 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2746
3013
|
if (worktreeTaskCwdError) return buildParallelModeError(worktreeTaskCwdError);
|
|
2747
3014
|
}
|
|
2748
3015
|
|
|
2749
|
-
const
|
|
3016
|
+
const parentModel = data.parentModel;
|
|
3017
|
+
const currentProvider = parentModel?.provider;
|
|
2750
3018
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
2751
3019
|
let taskTexts = tasks.map((t) => t.task);
|
|
2752
3020
|
const skillOverrides: (string[] | false | undefined)[] = tasks.map((t) =>
|
|
@@ -2761,7 +3029,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2761
3029
|
...(task.model !== undefined ? { model: task.model } : {}),
|
|
2762
3030
|
}));
|
|
2763
3031
|
const modelOverrides: (string | undefined)[] = tasks.map((_, i) =>
|
|
2764
|
-
resolveEffectiveSubagentModel(behaviorOverrides[i]?.model, agentConfigs[i]?.model,
|
|
3032
|
+
resolveEffectiveSubagentModel(behaviorOverrides[i]?.model, agentConfigs[i]?.model, parentModel, availableModels, currentProvider, { scope: data.modelScope }),
|
|
2765
3033
|
);
|
|
2766
3034
|
|
|
2767
3035
|
if (params.clarify === true && ctx.hasUI) {
|
|
@@ -2796,7 +3064,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2796
3064
|
for (let i = 0; i < result.behaviorOverrides.length; i++) {
|
|
2797
3065
|
const override = result.behaviorOverrides[i];
|
|
2798
3066
|
if (override?.model !== undefined) {
|
|
2799
|
-
modelOverrides[i] = resolveEffectiveSubagentModel(override.model, agentConfigs[i]?.model,
|
|
3067
|
+
modelOverrides[i] = resolveEffectiveSubagentModel(override.model, agentConfigs[i]?.model, parentModel, availableModels, currentProvider, { scope: data.modelScope });
|
|
2800
3068
|
behaviorOverrides[i]!.model = override.model;
|
|
2801
3069
|
}
|
|
2802
3070
|
if (override?.output !== undefined) behaviorOverrides[i]!.output = override.output;
|
|
@@ -2822,8 +3090,8 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2822
3090
|
cwd: ctx.cwd,
|
|
2823
3091
|
currentSessionId: deps.state.currentSessionId!,
|
|
2824
3092
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
2825
|
-
currentModelProvider:
|
|
2826
|
-
currentModel:
|
|
3093
|
+
currentModelProvider: parentModel?.provider,
|
|
3094
|
+
currentModel: parentModel,
|
|
2827
3095
|
modelScope: data.modelScope,
|
|
2828
3096
|
interactive: ctx.hasUI,
|
|
2829
3097
|
};
|
|
@@ -2875,6 +3143,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2875
3143
|
nestedRoute: data.nestedRoute,
|
|
2876
3144
|
timeoutMs: data.timeoutMs,
|
|
2877
3145
|
turnBudget: data.turnBudget,
|
|
3146
|
+
usageBudget: data.usageBudget,
|
|
2878
3147
|
toolBudget: data.toolBudget,
|
|
2879
3148
|
configToolBudget: data.configToolBudget,
|
|
2880
3149
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
@@ -2884,8 +3153,19 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2884
3153
|
|
|
2885
3154
|
const behaviors = tasks.map((task, index) => {
|
|
2886
3155
|
let behavior = suppressProgressForReadOnlyTask(resolveStepBehavior(agentConfigs[index]!, behaviorOverrides[index]!), taskTexts[index]);
|
|
2887
|
-
if (behaviorOverrides[index]?.output === undefined
|
|
2888
|
-
|
|
3156
|
+
if (behaviorOverrides[index]?.output === undefined) {
|
|
3157
|
+
if (typeof behavior.output === "string" && !path.isAbsolute(behavior.output)) {
|
|
3158
|
+
behavior = { ...behavior, output: path.join("parallel-0", `${index}-${task.agent}`, behavior.output) };
|
|
3159
|
+
} else if (behavior.output === false && behaviorOverrides[index]?.output !== false && behaviorOverrides[index]?.output !== "false") {
|
|
3160
|
+
// Omitted output: generate a collision-safe per-task durable path in the
|
|
3161
|
+
// existing per-task namespace (parallel-0/<index>-<agent>/result.md).
|
|
3162
|
+
behavior = { ...behavior, output: path.join("parallel-0", `${index}-${task.agent}`, "result.md") };
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
// Omitted outputMode resolves to file-only whenever an output path is active;
|
|
3166
|
+
// explicit `outputMode: "inline"` is preserved exactly.
|
|
3167
|
+
if (behaviorOverrides[index]?.outputMode === undefined && typeof behavior.output === "string") {
|
|
3168
|
+
behavior = { ...behavior, outputMode: "file-only" };
|
|
2889
3169
|
}
|
|
2890
3170
|
return behavior;
|
|
2891
3171
|
});
|
|
@@ -2958,6 +3238,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2958
3238
|
progressDir: parallelProgressDir,
|
|
2959
3239
|
availableModels,
|
|
2960
3240
|
modelScope: data.modelScope,
|
|
3241
|
+
parentModel,
|
|
2961
3242
|
modelOverrides,
|
|
2962
3243
|
behaviors,
|
|
2963
3244
|
firstProgressIndex: parallelProgressPrecreated ? -1 : firstProgressIndex,
|
|
@@ -2978,6 +3259,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2978
3259
|
timeoutMs: data.timeoutMs,
|
|
2979
3260
|
deadlineAt,
|
|
2980
3261
|
turnBudget: data.turnBudget,
|
|
3262
|
+
usageBudget: data.usageBudget,
|
|
2981
3263
|
toolBudgets,
|
|
2982
3264
|
agentContract: params.agentContract,
|
|
2983
3265
|
});
|
|
@@ -3001,14 +3283,17 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3001
3283
|
handoff = finalizeParallelWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: effectiveCwd, tasks, results });
|
|
3002
3284
|
}
|
|
3003
3285
|
const interrupted = results.find((result) => result.interrupted);
|
|
3286
|
+
const totalCost = sumResultsCost(results);
|
|
3004
3287
|
const details = compactForegroundDetails({
|
|
3005
3288
|
mode: "parallel",
|
|
3006
3289
|
runId,
|
|
3290
|
+
timeoutMs: data.timeoutMs,
|
|
3007
3291
|
results,
|
|
3008
3292
|
progress: params.includeProgress ? allProgress : undefined,
|
|
3009
3293
|
artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
|
|
3010
3294
|
totalChildUsage: sumResultsUsage(results),
|
|
3011
|
-
totalCost
|
|
3295
|
+
totalCost,
|
|
3296
|
+
usageBudget: usageBudgetState(data.usageBudget, totalCost),
|
|
3012
3297
|
...(handoff?.reference ? { parallelHandoff: handoff.reference } : {}),
|
|
3013
3298
|
});
|
|
3014
3299
|
rememberForegroundRun(deps.state, { runId, mode: "parallel", cwd: effectiveCwd, sessionId: data.parentSessionId, results: details.results });
|
|
@@ -3046,21 +3331,12 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3046
3331
|
const worktreeSuffix = handoff?.suffix ?? "";
|
|
3047
3332
|
const ok = results.filter((result) => result.exitCode === 0).length;
|
|
3048
3333
|
const downgradeNote = backgroundRequestedWhileClarifying ? " (background requested, but clarify kept this run foreground)" : "";
|
|
3049
|
-
const
|
|
3050
|
-
results.map((result) => ({
|
|
3051
|
-
agent: result.agent,
|
|
3052
|
-
output: result.truncation?.text || getSingleResultOutput(result),
|
|
3053
|
-
exitCode: result.exitCode,
|
|
3054
|
-
error: result.error,
|
|
3055
|
-
timedOut: result.timedOut,
|
|
3056
|
-
})),
|
|
3057
|
-
(i, agent) => `=== Task ${i + 1}: ${agent} ===`,
|
|
3058
|
-
);
|
|
3334
|
+
const perChildResults = results.map((result, i) => formatParallelChildResult(result, i)).join("\n\n");
|
|
3059
3335
|
|
|
3060
3336
|
const summary = `${ok}/${results.length} succeeded${downgradeNote}`;
|
|
3061
3337
|
const fullContent = worktreeSuffix
|
|
3062
|
-
? `${summary}\n\n${
|
|
3063
|
-
: `${summary}\n\n${
|
|
3338
|
+
? `${summary}\n\n${perChildResults}\n\n${worktreeSuffix}`
|
|
3339
|
+
: `${summary}\n\n${perChildResults}`;
|
|
3064
3340
|
|
|
3065
3341
|
return {
|
|
3066
3342
|
content: [{ type: "text", text: fullContent }],
|
|
@@ -3105,13 +3381,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3105
3381
|
const effectiveToolBudget = resolveEffectiveToolBudget({ runBudget: data.toolBudget, agentBudget: agentConfig.toolBudget, configBudget: data.configToolBudget });
|
|
3106
3382
|
if (effectiveToolBudget.error) return toExecutionErrorResult(params, new Error(effectiveToolBudget.error), data.contextPolicy.contextSummary);
|
|
3107
3383
|
|
|
3108
|
-
const
|
|
3384
|
+
const parentModel = data.parentModel;
|
|
3385
|
+
const currentProvider = parentModel?.provider;
|
|
3109
3386
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
3110
3387
|
let task = params.task ?? "";
|
|
3111
3388
|
let modelOverride: string | undefined = resolveEffectiveSubagentModel(
|
|
3112
3389
|
params.model as string | undefined,
|
|
3113
3390
|
agentConfig.model,
|
|
3114
|
-
|
|
3391
|
+
parentModel,
|
|
3115
3392
|
availableModels,
|
|
3116
3393
|
currentProvider,
|
|
3117
3394
|
{ scope: data.modelScope },
|
|
@@ -3119,7 +3396,6 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3119
3396
|
let skillOverride: string[] | false | undefined = normalizeSkillInput(params.skill);
|
|
3120
3397
|
const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
|
|
3121
3398
|
let effectiveOutput = normalizeSingleOutputOverride(rawOutput, agentConfig.output);
|
|
3122
|
-
const effectiveOutputMode = params.outputMode ?? "inline";
|
|
3123
3399
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
3124
3400
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
|
|
3125
3401
|
|
|
@@ -3151,7 +3427,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3151
3427
|
|
|
3152
3428
|
task = result.templates[0]!;
|
|
3153
3429
|
const override = result.behaviorOverrides[0];
|
|
3154
|
-
if (override?.model !== undefined) modelOverride = resolveEffectiveSubagentModel(override.model, agentConfig.model,
|
|
3430
|
+
if (override?.model !== undefined) modelOverride = resolveEffectiveSubagentModel(override.model, agentConfig.model, parentModel, availableModels, currentProvider, { scope: data.modelScope });
|
|
3155
3431
|
if (override?.output !== undefined) effectiveOutput = normalizeSingleOutputOverride(override.output, agentConfig.output);
|
|
3156
3432
|
if (override?.skills !== undefined) skillOverride = override.skills;
|
|
3157
3433
|
|
|
@@ -3169,8 +3445,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3169
3445
|
cwd: ctx.cwd,
|
|
3170
3446
|
currentSessionId: deps.state.currentSessionId!,
|
|
3171
3447
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
3172
|
-
currentModelProvider:
|
|
3173
|
-
currentModel:
|
|
3448
|
+
currentModelProvider: parentModel?.provider,
|
|
3449
|
+
currentModel: parentModel,
|
|
3174
3450
|
modelScope: data.modelScope,
|
|
3175
3451
|
interactive: ctx.hasUI,
|
|
3176
3452
|
};
|
|
@@ -3191,7 +3467,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3191
3467
|
context: contextPolicy.contextForAgent(params.agent!),
|
|
3192
3468
|
skills: skillOverride === false ? [] : skillOverride,
|
|
3193
3469
|
output: effectiveOutput,
|
|
3194
|
-
outputMode:
|
|
3470
|
+
outputMode: params.outputMode,
|
|
3195
3471
|
outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
|
|
3196
3472
|
modelOverride,
|
|
3197
3473
|
thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
|
|
@@ -3210,6 +3486,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3210
3486
|
timeoutMs: data.timeoutMs,
|
|
3211
3487
|
turnBudget: data.turnBudget,
|
|
3212
3488
|
toolBudget: effectiveToolBudget.toolBudget,
|
|
3489
|
+
usageBudget: data.usageBudget,
|
|
3213
3490
|
allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
|
|
3214
3491
|
});
|
|
3215
3492
|
}
|
|
@@ -3219,7 +3496,16 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3219
3496
|
task = wrapForkTask(task);
|
|
3220
3497
|
}
|
|
3221
3498
|
const cleanTask = task;
|
|
3222
|
-
|
|
3499
|
+
// Omitted output resolves to a generated per-run durable path
|
|
3500
|
+
// (`<singleRunOutputBaseDir>/<runId>/result.md`); omitted outputMode resolves to
|
|
3501
|
+
// file-only whenever an output path is active. Explicit paths, `output: false`,
|
|
3502
|
+
// and explicit `outputMode: "inline"` are preserved exactly.
|
|
3503
|
+
const outputPath = typeof effectiveOutput === "string"
|
|
3504
|
+
? resolveSingleOutputPath(effectiveOutput, ctx.cwd, effectiveCwd, resolveSingleRunOutputBaseDir(deps, artifactsDir, runId))
|
|
3505
|
+
: effectiveOutput === false
|
|
3506
|
+
? undefined
|
|
3507
|
+
: resolveGeneratedSingleOutputPath(deps, artifactsDir, runId);
|
|
3508
|
+
const effectiveOutputMode = params.outputMode ?? (outputPath ? "file-only" : "inline");
|
|
3223
3509
|
const validationError = validateFileOnlyOutputMode(effectiveOutputMode, outputPath, `Single run (${params.agent})`);
|
|
3224
3510
|
if (validationError) {
|
|
3225
3511
|
return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [] } };
|
|
@@ -3236,17 +3522,22 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3236
3522
|
effectiveSkills = skillOverride;
|
|
3237
3523
|
}
|
|
3238
3524
|
const interruptController = new AbortController();
|
|
3525
|
+
let detachForeground: ((reason?: string) => boolean) | undefined;
|
|
3239
3526
|
const foregroundControl = deps.state.foregroundControls.get(runId);
|
|
3240
3527
|
if (foregroundControl) {
|
|
3528
|
+
const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride));
|
|
3241
3529
|
beginForegroundChild(foregroundControl, {
|
|
3242
3530
|
index: 0,
|
|
3243
3531
|
agent: params.agent!,
|
|
3244
3532
|
description: foregroundControl.description,
|
|
3533
|
+
...(modelOverride ? { model: modelOverride } : {}),
|
|
3534
|
+
...(thinking ? { thinking } : {}),
|
|
3245
3535
|
interrupt: () => {
|
|
3246
3536
|
if (interruptController.signal.aborted) return false;
|
|
3247
3537
|
interruptController.abort();
|
|
3248
3538
|
return true;
|
|
3249
3539
|
},
|
|
3540
|
+
detach: () => detachForeground?.("user request") === true,
|
|
3250
3541
|
});
|
|
3251
3542
|
}
|
|
3252
3543
|
|
|
@@ -3296,7 +3587,29 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3296
3587
|
agentContract: params.agentContract,
|
|
3297
3588
|
acceptance: params.acceptance,
|
|
3298
3589
|
acceptanceContext: { mode: "single" },
|
|
3299
|
-
|
|
3590
|
+
onDetachReady: (detach) => {
|
|
3591
|
+
detachForeground = detach;
|
|
3592
|
+
},
|
|
3593
|
+
onDetachedExit: (result) => {
|
|
3594
|
+
try {
|
|
3595
|
+
try {
|
|
3596
|
+
updateRememberedForegroundChild(deps.state, { runId, mode: "single", cwd: effectiveCwd, sessionId: data.parentSessionId, index: 0, result, events: deps.pi.events });
|
|
3597
|
+
} catch {
|
|
3598
|
+
// Remembered foreground state is best-effort; run history and cleanup must still complete.
|
|
3599
|
+
}
|
|
3600
|
+
} finally {
|
|
3601
|
+
try {
|
|
3602
|
+
if (!artifactConfig.enabled) cleanupStructuredOutputRuntime(structuredRuntime);
|
|
3603
|
+
} finally {
|
|
3604
|
+
try {
|
|
3605
|
+
if (foregroundControl) finishForegroundChild(foregroundControl, 0);
|
|
3606
|
+
} finally {
|
|
3607
|
+
removeForegroundControlIfIdle(deps.state, runId);
|
|
3608
|
+
}
|
|
3609
|
+
}
|
|
3610
|
+
}
|
|
3611
|
+
recordRun(params.agent!, cleanTask, result.exitCode, result.progressSummary?.durationMs ?? 0);
|
|
3612
|
+
},
|
|
3300
3613
|
timeoutMs: data.timeoutMs,
|
|
3301
3614
|
deadlineAt,
|
|
3302
3615
|
turnBudget: data.turnBudget,
|
|
@@ -3306,10 +3619,17 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3306
3619
|
allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
|
|
3307
3620
|
});
|
|
3308
3621
|
} finally {
|
|
3309
|
-
|
|
3622
|
+
// An attached runSync rejection still owns its child and structured runtime.
|
|
3623
|
+
// A successful detached receipt transfers both to onDetachedExit while the
|
|
3624
|
+
// authoritative completion remains live.
|
|
3625
|
+
if (!r?.detached) {
|
|
3626
|
+
if (!artifactConfig.enabled) cleanupStructuredOutputRuntime(structuredRuntime);
|
|
3627
|
+
if (foregroundControl) finishForegroundChild(foregroundControl, 0);
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3630
|
+
if (!r.detached) {
|
|
3631
|
+
recordRun(params.agent!, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0);
|
|
3310
3632
|
}
|
|
3311
|
-
if (foregroundControl) finishForegroundChild(foregroundControl, 0);
|
|
3312
|
-
recordRun(params.agent!, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0);
|
|
3313
3633
|
|
|
3314
3634
|
if (r.progress) allProgress.push(r.progress);
|
|
3315
3635
|
if (r.artifactPaths) allArtifactPaths.push(r.artifactPaths);
|
|
@@ -3324,14 +3644,17 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3324
3644
|
savedPath: r.savedOutputPath,
|
|
3325
3645
|
outputReference: r.outputReference,
|
|
3326
3646
|
saveError: r.outputSaveError,
|
|
3647
|
+
error: r.error,
|
|
3327
3648
|
});
|
|
3328
3649
|
if (foregroundControl) {
|
|
3329
3650
|
updateForegroundNestedProjection(foregroundControl);
|
|
3330
3651
|
attachRootChildrenToSteps(runId, [r], foregroundControl.nestedChildren);
|
|
3331
3652
|
}
|
|
3653
|
+
const totalCost = sumResultsCost([r]);
|
|
3332
3654
|
const details = compactForegroundDetails({
|
|
3333
3655
|
mode: "single",
|
|
3334
3656
|
runId,
|
|
3657
|
+
timeoutMs: data.timeoutMs,
|
|
3335
3658
|
results: [r],
|
|
3336
3659
|
...(data.turnBudget ? { turnBudget: data.turnBudget } : {}),
|
|
3337
3660
|
...(effectiveToolBudget.toolBudget ? { toolBudget: effectiveToolBudget.toolBudget } : {}),
|
|
@@ -3339,7 +3662,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3339
3662
|
artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
|
|
3340
3663
|
truncation: r.truncation,
|
|
3341
3664
|
totalChildUsage: sumResultsUsage([r]),
|
|
3342
|
-
totalCost
|
|
3665
|
+
totalCost,
|
|
3666
|
+
usageBudget: usageBudgetState(data.usageBudget, totalCost),
|
|
3343
3667
|
});
|
|
3344
3668
|
rememberForegroundRun(deps.state, { runId, mode: "single", cwd: effectiveCwd, sessionId: data.parentSessionId, results: details.results });
|
|
3345
3669
|
|
|
@@ -3460,6 +3784,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3460
3784
|
const requestCwd = resolveRequestedCwd(ctx.cwd, requestParams.cwd);
|
|
3461
3785
|
const paramsWithResolvedCwd = requestParams.cwd === undefined ? requestParams : { ...requestParams, cwd: requestCwd };
|
|
3462
3786
|
const action = paramsWithResolvedCwd.action;
|
|
3787
|
+
let requestParentModel: ParentModel | undefined;
|
|
3788
|
+
try {
|
|
3789
|
+
requestParentModel = rememberParentModel(deps.state, resolveCurrentSessionId(ctx.sessionManager), ctx.model);
|
|
3790
|
+
} catch (error) {
|
|
3791
|
+
if (action?.toLowerCase() !== "doctor") throw error;
|
|
3792
|
+
requestParentModel = normalizeParentModel(ctx.model);
|
|
3793
|
+
}
|
|
3463
3794
|
if (action) {
|
|
3464
3795
|
if ((WATCHDOG_TOOL_ACTIONS as readonly string[]).includes(action)) {
|
|
3465
3796
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
@@ -3616,8 +3947,61 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3616
3947
|
}
|
|
3617
3948
|
return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, { state: deps.state, nested: nestedScope, sessionRoots }));
|
|
3618
3949
|
}
|
|
3950
|
+
|
|
3951
|
+
if (action === "approve-checkpoint" || action === "reject-checkpoint") {
|
|
3952
|
+
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
3953
|
+
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
3954
|
+
if (!targetRunId && !paramsWithResolvedCwd.dir) {
|
|
3955
|
+
return { content: [{ type: "text", text: `action='${action}' requires id or dir.` }], isError: true, details: { mode: "management", results: [] } };
|
|
3956
|
+
}
|
|
3957
|
+
try {
|
|
3958
|
+
const resolved = targetRunId ? resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedResolutionScopeForExecutor(deps) }) : undefined;
|
|
3959
|
+
const decision = action === "approve-checkpoint" ? "approved" : "rejected";
|
|
3960
|
+
if (resolved?.kind === "foreground") {
|
|
3961
|
+
const run = deps.state.foregroundRuns?.get(resolved.id);
|
|
3962
|
+
if (!run?.checkpoint || run.checkpoint.status !== "pending") {
|
|
3963
|
+
return { content: [{ type: "text", text: `Run '${resolved.id}' is not paused at an approval checkpoint.` }], isError: true, details: { mode: "management", results: [] } };
|
|
3964
|
+
}
|
|
3965
|
+
if (deps.state.currentSessionId && run.sessionId !== deps.state.currentSessionId) {
|
|
3966
|
+
return { content: [{ type: "text", text: `Run '${resolved.id}' was not found in the active session.` }], isError: true, details: { mode: "management", results: [] } };
|
|
3967
|
+
}
|
|
3968
|
+
const decidedAt = Date.now();
|
|
3969
|
+
const checkpoint = { ...run.checkpoint, status: decision, ...(decision === "approved" ? { approvedAt: decidedAt } : { rejectedAt: decidedAt }) };
|
|
3970
|
+
run.checkpoint = checkpoint;
|
|
3971
|
+
run.updatedAt = decidedAt;
|
|
3972
|
+
return {
|
|
3973
|
+
content: [{ type: "text", text: `Checkpoint '${checkpoint.name}' ${decision} for foreground run ${resolved.id}.` }],
|
|
3974
|
+
details: { mode: "management", results: [], checkpoint },
|
|
3975
|
+
};
|
|
3976
|
+
}
|
|
3977
|
+
const location = paramsWithResolvedCwd.dir
|
|
3978
|
+
? resolveAsyncRunLocation(paramsWithResolvedCwd, ASYNC_DIR, RESULTS_DIR)
|
|
3979
|
+
: resolved?.kind === "async"
|
|
3980
|
+
? resolved.location
|
|
3981
|
+
: undefined;
|
|
3982
|
+
if (!location?.asyncDir) {
|
|
3983
|
+
return { content: [{ type: "text", text: `action='${action}' targets paused foreground or async checkpoints. No matching run found.` }], isError: true, details: { mode: "management", results: [] } };
|
|
3984
|
+
}
|
|
3985
|
+
const status = readStatus(location.asyncDir);
|
|
3986
|
+
const runId = status?.runId ?? location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir);
|
|
3987
|
+
if (!status?.checkpoint || status.state !== "paused") {
|
|
3988
|
+
return { content: [{ type: "text", text: `Run '${runId}' is not paused at an approval checkpoint.` }], isError: true, details: { mode: "management", results: [] } };
|
|
3989
|
+
}
|
|
3990
|
+
if (deps.state.currentSessionId && status.sessionId !== deps.state.currentSessionId) {
|
|
3991
|
+
return { content: [{ type: "text", text: `Run '${runId}' was not found in the active session.` }], isError: true, details: { mode: "management", results: [] } };
|
|
3992
|
+
}
|
|
3993
|
+
deliverCheckpointDecisionRequest({ asyncDir: location.asyncDir, decision, source: "subagent-action", ...(paramsWithResolvedCwd.message ? { reason: paramsWithResolvedCwd.message } : {}) });
|
|
3994
|
+
return {
|
|
3995
|
+
content: [{ type: "text", text: `Checkpoint '${status.checkpoint.name}' ${decision} for run ${runId}.` }],
|
|
3996
|
+
details: { mode: "management", results: [], checkpoint: { ...status.checkpoint, status: decision } },
|
|
3997
|
+
};
|
|
3998
|
+
} catch (error) {
|
|
3999
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
4000
|
+
return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
3619
4003
|
if (action === "resume") {
|
|
3620
|
-
return resumeAsyncRun({ params: paramsWithResolvedCwd, requestCwd, ctx, deps });
|
|
4004
|
+
return resumeAsyncRun({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel });
|
|
3621
4005
|
}
|
|
3622
4006
|
if (action === "steer") {
|
|
3623
4007
|
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
@@ -3640,7 +4024,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3640
4024
|
? {}
|
|
3641
4025
|
: {
|
|
3642
4026
|
recover: ({ absoluteDeadlineAt, ...limits }) =>
|
|
3643
|
-
resumeAsyncRun({ params: { ...limits, action: "resume", id: runId, message }, requestCwd, ctx, deps, absoluteDeadlineAt }),
|
|
4027
|
+
resumeAsyncRun({ params: { ...limits, action: "resume", id: runId, message }, requestCwd, ctx, deps, parentModel: requestParentModel, absoluteDeadlineAt }),
|
|
3644
4028
|
}
|
|
3645
4029
|
),
|
|
3646
4030
|
});
|
|
@@ -3677,6 +4061,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3677
4061
|
requestCwd,
|
|
3678
4062
|
ctx,
|
|
3679
4063
|
deps,
|
|
4064
|
+
parentModel: requestParentModel,
|
|
3680
4065
|
absoluteDeadlineAt,
|
|
3681
4066
|
}),
|
|
3682
4067
|
}
|
|
@@ -3684,7 +4069,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3684
4069
|
});
|
|
3685
4070
|
}
|
|
3686
4071
|
if (action === "append-step") {
|
|
3687
|
-
return appendStepToAsyncChain({ params: paramsWithResolvedCwd, requestCwd, ctx, deps });
|
|
4072
|
+
return appendStepToAsyncChain({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel });
|
|
3688
4073
|
}
|
|
3689
4074
|
if (action === "schedule" || action === "schedule-list" || action === "schedule-status" || action === "schedule-cancel") {
|
|
3690
4075
|
if (!deps.handleScheduledRunAction) {
|
|
@@ -3790,6 +4175,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3790
4175
|
...ctx,
|
|
3791
4176
|
cwd: requestCwd,
|
|
3792
4177
|
config: deps.config,
|
|
4178
|
+
currentSessionId: deps.state.currentSessionId ?? ctx.sessionManager.getSessionId() ?? undefined,
|
|
3793
4179
|
});
|
|
3794
4180
|
}
|
|
3795
4181
|
|
|
@@ -3827,17 +4213,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3827
4213
|
if (runToolBudget.error) return buildRequestedModeError(effectiveParams, runToolBudget.error);
|
|
3828
4214
|
const configToolBudget = resolveToolBudget(deps.config.toolBudget, "config.toolBudget");
|
|
3829
4215
|
if (configToolBudget.error) return buildRequestedModeError(effectiveParams, configToolBudget.error);
|
|
4216
|
+
const usageBudget = validateUsageBudgetConfig(effectiveParams.usageBudget ?? deps.config.usageBudget, effectiveParams.usageBudget ? "usageBudget" : "config.usageBudget");
|
|
4217
|
+
if (usageBudget.error) return buildRequestedModeError(effectiveParams, usageBudget.error);
|
|
3830
4218
|
|
|
3831
4219
|
const scope: AgentScope = resolveExecutionAgentScope(effectiveParams.agentScope);
|
|
3832
4220
|
const effectiveCwd = effectiveParams.cwd ?? ctx.cwd;
|
|
3833
4221
|
const parentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
|
|
3834
|
-
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
3835
4222
|
const discovered = deps.discoverAgents(effectiveCwd, scope);
|
|
3836
4223
|
const discoveredAgents = discovered.agents;
|
|
4224
|
+
const canonicalParams = canonicalizeExecutionParams(effectiveParams, discoveredAgents);
|
|
4225
|
+
if (canonicalParams.error) return buildRequestedModeError(effectiveParams, canonicalParams.error);
|
|
4226
|
+
effectiveParams = canonicalParams.params!;
|
|
3837
4227
|
const modelScope = discovered.modelScope;
|
|
3838
4228
|
effectiveParams = applySingleAgentLaunchDefaults(effectiveParams, discoveredAgents);
|
|
3839
|
-
const foregroundTimeout = resolveForegroundTimeout(effectiveParams);
|
|
3840
|
-
if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
|
|
3841
4229
|
const turnBudget = resolveTurnBudgetConfig(effectiveParams.turnBudget ?? deps.config.turnBudget);
|
|
3842
4230
|
if (turnBudget.error) return buildRequestedModeError(effectiveParams, turnBudget.error);
|
|
3843
4231
|
const contextPolicy = resolveAgentDefaultContextPolicy(effectiveParams, discoveredAgents);
|
|
@@ -3891,25 +4279,26 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3891
4279
|
const forkThinkingDowngrades = new Map<number, string>();
|
|
3892
4280
|
try {
|
|
3893
4281
|
const forkAvailableModels = contextPolicy.usesFork ? ctx.modelRegistry.getAvailable().map(toModelInfo) : [];
|
|
4282
|
+
const parentModel = requestParentModel;
|
|
3894
4283
|
prepareForkThinking = (agentName, index, modelOverride) => {
|
|
3895
4284
|
const agentConfig = agents.find((agent) => agent.name === agentName);
|
|
3896
4285
|
const primaryModel = resolveEffectiveSubagentModel(
|
|
3897
4286
|
modelOverride,
|
|
3898
4287
|
agentConfig?.model,
|
|
3899
|
-
|
|
4288
|
+
parentModel,
|
|
3900
4289
|
forkAvailableModels,
|
|
3901
|
-
|
|
4290
|
+
parentModel?.provider,
|
|
3902
4291
|
);
|
|
3903
4292
|
const candidates = buildModelCandidates(
|
|
3904
4293
|
primaryModel,
|
|
3905
4294
|
agentConfig?.fallbackModels,
|
|
3906
4295
|
forkAvailableModels,
|
|
3907
|
-
|
|
4296
|
+
parentModel?.provider,
|
|
3908
4297
|
);
|
|
3909
4298
|
forkThinkingRequirements.set(
|
|
3910
4299
|
index,
|
|
3911
4300
|
candidates.length === 0
|
|
3912
|
-
|| candidates.some((candidate) => forkedChildRequiresThinkingOff(candidate, forkAvailableModels,
|
|
4301
|
+
|| candidates.some((candidate) => forkedChildRequiresThinkingOff(candidate, forkAvailableModels, parentModel?.provider)),
|
|
3913
4302
|
);
|
|
3914
4303
|
};
|
|
3915
4304
|
const forkContextResolver = createForkContextResolver(ctx.sessionManager, contextPolicy.usesFork ? "fork" : undefined, {
|
|
@@ -3923,11 +4312,16 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3923
4312
|
const requestedAsync = effectiveParams.async ?? deps.asyncByDefault;
|
|
3924
4313
|
const backgroundRequestedWhileClarifying = (hasChain || hasTasks) && requestedAsync && effectiveParams.clarify === true;
|
|
3925
4314
|
const effectiveAsync = requestedAsync && effectiveParams.clarify !== true;
|
|
4315
|
+
const foregroundTimeout = resolveForegroundTimeout(
|
|
4316
|
+
effectiveParams,
|
|
4317
|
+
effectiveAsync ? undefined : DEFAULT_FOREGROUND_TIMEOUT_MS,
|
|
4318
|
+
);
|
|
4319
|
+
if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
|
|
3926
4320
|
const controlConfig = resolveControlConfig(deps.config.control, effectiveParams.control);
|
|
3927
4321
|
|
|
3928
4322
|
const artifactConfig: ArtifactConfig = {
|
|
3929
4323
|
...DEFAULT_ARTIFACT_CONFIG,
|
|
3930
|
-
enabled: effectiveParams.artifacts
|
|
4324
|
+
enabled: effectiveParams.artifacts === true,
|
|
3931
4325
|
dir: deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir,
|
|
3932
4326
|
};
|
|
3933
4327
|
const artifactsDir = getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
@@ -3980,7 +4374,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3980
4374
|
if (chainBindingsError) return withResolvedContext(chainBindingsError, contextPolicy.contextSummary);
|
|
3981
4375
|
|
|
3982
4376
|
const onUpdateWithContext = onUpdate
|
|
3983
|
-
? (r: AgentToolResult<Details>) => onUpdate(withResolvedContext(
|
|
4377
|
+
? (r: AgentToolResult<Details>) => onUpdate(withResolvedContext({
|
|
4378
|
+
...r,
|
|
4379
|
+
details: { ...r.details, runId },
|
|
4380
|
+
}, contextPolicy.contextSummary))
|
|
3984
4381
|
: undefined;
|
|
3985
4382
|
|
|
3986
4383
|
const reservation = reserveSpawnBudget(
|
|
@@ -4015,10 +4412,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4015
4412
|
timeoutMs: foregroundTimeout.timeoutMs,
|
|
4016
4413
|
turnBudget: turnBudget.turnBudget,
|
|
4017
4414
|
toolBudget: runToolBudget.toolBudget,
|
|
4415
|
+
usageBudget: usageBudget.budget,
|
|
4018
4416
|
allowZeroToolBudget,
|
|
4019
4417
|
configToolBudget: configToolBudget.toolBudget,
|
|
4020
4418
|
contextPolicy,
|
|
4021
4419
|
modelScope,
|
|
4420
|
+
parentModel: requestParentModel,
|
|
4022
4421
|
parentSessionId: deps.state.currentSessionId,
|
|
4023
4422
|
capabilityCeiling: resolveCurrentSubagentCapabilityCeiling(deps.state.currentSessionId ?? undefined),
|
|
4024
4423
|
};
|
|
@@ -4030,6 +4429,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4030
4429
|
? undefined
|
|
4031
4430
|
: {
|
|
4032
4431
|
runId,
|
|
4432
|
+
...(deps.state.currentSessionId ? { sessionId: deps.state.currentSessionId } : {}),
|
|
4033
4433
|
mode: foregroundMode,
|
|
4034
4434
|
startedAt: Date.now(),
|
|
4035
4435
|
updatedAt: Date.now(),
|
|
@@ -4039,6 +4439,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4039
4439
|
description: foregroundDescription,
|
|
4040
4440
|
currentActivityState: undefined,
|
|
4041
4441
|
activeChildren: new Map(),
|
|
4442
|
+
// The outer executor owns scheduling until its finally block settles.
|
|
4443
|
+
schedulingOwners: 1,
|
|
4042
4444
|
nestedRoute,
|
|
4043
4445
|
interrupt: undefined,
|
|
4044
4446
|
};
|
|
@@ -4136,11 +4538,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4136
4538
|
return errorResult;
|
|
4137
4539
|
} finally {
|
|
4138
4540
|
if (foregroundControl) {
|
|
4139
|
-
|
|
4140
|
-
deps.state
|
|
4141
|
-
if (deps.state.lastForegroundControlId === runId) {
|
|
4142
|
-
deps.state.lastForegroundControlId = null;
|
|
4143
|
-
}
|
|
4541
|
+
settleForegroundSchedulingOwner(foregroundControl);
|
|
4542
|
+
removeForegroundControlIfIdle(deps.state, runId);
|
|
4144
4543
|
}
|
|
4145
4544
|
}
|
|
4146
4545
|
|