@selesai/code 0.8.3 → 0.8.5
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 +15 -0
- package/README.md +7 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/defaults/models.json +41 -55
- package/dist/defaults/settings.json +7 -8
- package/dist/extensions/handoff-new.ts +1 -1
- package/dist/extensions/inline-skills.test.ts +1 -0
- package/dist/extensions/inline-skills.ts +1 -1
- package/dist/extensions/pi-intercom/CHANGELOG.md +25 -3
- package/dist/extensions/pi-intercom/README.md +34 -15
- package/dist/extensions/pi-intercom/broker/broker.ts +38 -141
- package/dist/extensions/pi-intercom/broker/client-liveness.test.ts +173 -0
- package/dist/extensions/pi-intercom/broker/client.ts +78 -150
- package/dist/extensions/pi-intercom/broker/extension-state.ts +18 -7
- package/dist/extensions/pi-intercom/broker/framing.ts +1 -1
- package/dist/extensions/pi-intercom/broker/protocol.ts +182 -0
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +106 -7
- package/dist/extensions/pi-intercom/broker/spawn.ts +37 -11
- package/dist/extensions/pi-intercom/config.test.ts +8 -10
- package/dist/extensions/pi-intercom/config.ts +3 -3
- package/dist/extensions/pi-intercom/index.ts +218 -147
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +891 -103
- package/dist/extensions/pi-intercom/package.json +3 -2
- package/dist/extensions/pi-intercom/project-agent.test.ts +145 -0
- package/dist/extensions/pi-intercom/project-agent.ts +324 -0
- package/dist/extensions/pi-intercom/reply-tracker.test.ts +86 -1
- package/dist/extensions/pi-intercom/reply-tracker.ts +41 -9
- package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +49 -9
- package/dist/extensions/pi-intercom/test/inline-message.test.ts +9 -9
- package/dist/extensions/pi-intercom/types.ts +3 -1
- package/dist/extensions/pi-intercom/ui/inline-message.ts +7 -7
- package/dist/extensions/pi-powerline-footer/.github/workflows/test.yml +2 -2
- package/dist/extensions/pi-powerline-footer/CHANGELOG.md +14 -1
- package/dist/extensions/pi-powerline-footer/README.md +20 -37
- package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +40 -25
- package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +149 -18
- package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +1 -0
- package/dist/extensions/pi-powerline-footer/editor-performance.ts +99 -0
- package/dist/extensions/pi-powerline-footer/git-status.ts +15 -0
- package/dist/extensions/pi-powerline-footer/index.ts +394 -317
- package/dist/extensions/pi-powerline-footer/package.json +1 -1
- package/dist/extensions/pi-powerline-footer/powerline-config.ts +0 -12
- package/dist/extensions/pi-powerline-footer/queue/store.ts +8 -56
- package/dist/extensions/pi-powerline-footer/queue/types.ts +1 -2
- package/dist/extensions/pi-powerline-footer/segments.ts +0 -3
- package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +231 -1
- package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +5 -19
- package/dist/extensions/pi-powerline-footer/tests/editor-performance.test.ts +43 -0
- package/dist/extensions/pi-powerline-footer/tests/git-optional-locks.test.ts +64 -0
- package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +116 -0
- package/dist/extensions/pi-powerline-footer/tests/queue-store.test.ts +19 -149
- package/dist/extensions/pi-powerline-footer/tests/remaining-regressions.test.ts +6 -0
- package/dist/extensions/pi-powerline-footer/tests/usage-display.test.ts +5 -5
- package/dist/extensions/pi-powerline-footer/types.ts +1 -2
- package/dist/extensions/pi-subagents/CHANGELOG.md +225 -14
- package/dist/extensions/pi-subagents/README.md +66 -1613
- package/dist/extensions/pi-subagents/agents/builder.md +0 -1
- package/dist/extensions/pi-subagents/agents/delegate.md +14 -0
- package/dist/extensions/pi-subagents/agents/oracle.md +74 -0
- package/dist/extensions/pi-subagents/agents/reviewer.md +78 -0
- package/dist/extensions/pi-subagents/agents/scout.md +50 -0
- package/dist/extensions/pi-subagents/agents/worker.md +58 -0
- package/dist/extensions/pi-subagents/docs/configuration.md +372 -0
- package/dist/extensions/pi-subagents/docs/extension-api.md +308 -0
- package/dist/extensions/pi-subagents/docs/missions.md +119 -0
- package/dist/extensions/pi-subagents/docs/models.md +192 -0
- package/dist/extensions/pi-subagents/docs/observability.md +174 -0
- package/dist/extensions/pi-subagents/docs/tool-reference.md +343 -0
- package/dist/extensions/pi-subagents/docs/watchdog.md +176 -0
- package/dist/extensions/pi-subagents/docs/workflows.md +163 -0
- package/dist/extensions/pi-subagents/inspector-runner.mjs +11 -0
- package/dist/extensions/pi-subagents/package-lock.json +152 -194
- package/dist/extensions/pi-subagents/package.json +9 -10
- package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
- package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
- package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
- package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
- package/dist/extensions/pi-subagents/prompts/review-loop.md +12 -12
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +22 -17
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +77 -72
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +177 -69
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +28 -8
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +88 -66
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +3 -3
- package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +624 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +38 -19
- package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +7 -3
- package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
- package/dist/extensions/pi-subagents/src/agents/skills.ts +2 -9
- package/dist/extensions/pi-subagents/src/api/delegation.ts +1 -2
- package/dist/extensions/pi-subagents/src/api/project-panes.ts +30 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +62 -8
- package/dist/extensions/pi-subagents/src/extension/control-notices.ts +2 -2
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +40 -0
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +51 -26
- package/dist/extensions/pi-subagents/src/extension/index.ts +140 -50
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +77 -0
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +18 -24
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +58 -21
- package/dist/extensions/pi-subagents/src/extension/subagent-guide.ts +39 -0
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +47 -25
- package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +3 -3
- package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +460 -64
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -1
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +102 -4
- package/dist/extensions/pi-subagents/src/missions/actions.ts +77 -16
- package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +162 -0
- package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +65 -17
- package/dist/extensions/pi-subagents/src/missions/store.ts +151 -9
- package/dist/extensions/pi-subagents/src/missions/types.ts +58 -3
- package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +260 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -8
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +374 -0
- package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +46 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +158 -36
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +294 -134
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +26 -4
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +45 -6
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +33 -15
- package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +267 -0
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +145 -60
- package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +104 -0
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +17 -3
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +114 -6
- package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +33 -0
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +68 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +71 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +26 -3
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +42 -14
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +12 -24
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +7 -5
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +233 -101
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +17 -4
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +146 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +21 -5
- package/dist/extensions/pi-subagents/src/runs/foreground/async-dismiss-action.ts +85 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +43 -18
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +20 -19
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +40 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +188 -46
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +12 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +137 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +171 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1365 -252
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +187 -0
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +178 -26
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +286 -0
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +32 -5
- package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +12 -2
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +3 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +509 -117
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +10 -10
- package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +280 -0
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +4 -2
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +189 -32
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +21 -5
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +20 -8
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +128 -3
- package/dist/extensions/pi-subagents/src/shared/display-text.ts +100 -0
- package/dist/extensions/pi-subagents/src/shared/file-coalescer.ts +9 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +13 -0
- package/dist/extensions/pi-subagents/src/shared/formatters.ts +4 -6
- package/dist/extensions/pi-subagents/src/shared/prompt-resources.ts +51 -0
- package/dist/extensions/pi-subagents/src/shared/settings.ts +41 -11
- package/dist/extensions/pi-subagents/src/shared/types.ts +192 -3
- package/dist/extensions/pi-subagents/src/shared/utf8.ts +11 -0
- package/dist/extensions/pi-subagents/src/shared/utils.ts +46 -34
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -14
- package/dist/extensions/pi-subagents/src/slash/inline-subagents.ts +1 -1
- package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +14 -12
- package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +176 -21
- package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +12 -6
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +52 -18
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +36 -5
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +37 -21
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +375 -42
- package/dist/extensions/pi-subagents/src/tui/render.ts +201 -68
- package/dist/extensions/pi-subagents/src/types/pi-runtime-compat.d.ts +0 -6
- package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +5 -4
- package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +0 -1
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +332 -27
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +15 -15
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/cli.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/register-bedrock.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/restore-sandbox-env.d.ts +17 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/args.d.ts +57 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/config-selector.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/file-processor.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/initial-message.d.ts +18 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/list-models.d.ts +9 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/project-trust.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/session-picker.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/startup-ui.d.ts +20 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/config.d.ts +96 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-runtime.d.ts +119 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-services.d.ts +85 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session.d.ts +620 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-guidance.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-storage.d.ts +56 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/bash-executor.d.ts +32 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/cache-stats.d.ts +49 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/branch-summarization.d.ts +93 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/compaction.d.ts +128 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/index.d.ts +7 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/utils.d.ts +38 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/defaults.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/diagnostics.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/event-bus.d.ts +9 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/exec.d.ts +29 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/experimental.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/ansi-to-html.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/index.d.ts +37 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/tool-renderer.d.ts +34 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/index.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/loader.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/runner.d.ts +171 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +1261 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/wrapper.d.ts +20 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/footer-data-provider.d.ts +54 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/http-dispatcher.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/index.d.ts +13 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/keybindings.d.ts +358 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/messages.d.ts +77 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-config.d.ts +512 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-registry.d.ts +43 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-resolver.d.ts +121 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-runtime.d.ts +82 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/models-store.d.ts +17 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/output-guard.d.ts +7 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/package-manager.d.ts +210 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/project-trust.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/prompt-templates.d.ts +54 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-attribution.d.ts +4 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-composer.d.ts +55 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/radius.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/remote-catalog-provider.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resolve-config-value.d.ts +30 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resource-loader.d.ts +206 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/runtime-credentials.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/sdk.d.ts +106 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-cwd.d.ts +19 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-manager.d.ts +356 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/settings-manager.d.ts +297 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/skills.d.ts +60 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/slash-commands.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/source-info.d.ts +18 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/system-prompt.d.ts +28 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/telemetry.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/timings.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/bash.d.ts +68 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit-diff.d.ts +106 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit.d.ts +51 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/file-mutation-queue.d.ts +6 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/find.d.ts +35 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/grep.d.ts +37 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/index.d.ts +40 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/ls.d.ts +37 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/output-accumulator.d.ts +52 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/path-utils.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/read.d.ts +35 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/render-utils.d.ts +24 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/truncate.d.ts +70 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/write.d.ts +26 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/trust-manager.d.ts +36 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/usage-totals.d.ts +19 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/index.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/client.d.ts +61 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/huggingface.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/index.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/provider.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/ui.d.ts +42 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/index.d.ts +35 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/main.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/migrations.d.ts +33 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/index.d.ts +9 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/armin.d.ts +34 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/assistant-message.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bash-execution.d.ts +34 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/config-selector.d.ts +102 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-editor.d.ts +21 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-entry.d.ts +19 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-message.d.ts +20 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/daxnuts.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/diff.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-editor.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-input.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-selector.d.ts +26 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/footer.d.ts +32 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/index.d.ts +34 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/keybinding-hints.d.ts +13 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/login-dialog.d.ts +52 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/model-selector.d.ts +54 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/oauth-selector.d.ts +33 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/scoped-models-selector.d.ts +42 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector.d.ts +95 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/settings-selector.d.ts +77 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/status-indicator.d.ts +28 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/theme-selector.d.ts +11 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tool-execution.d.ts +63 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tree-selector.d.ts +94 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/trust-selector.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message.d.ts +14 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/interactive-mode.d.ts +394 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/model-search.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme-controller.d.ts +29 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme.d.ts +120 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/print-mode.d.ts +28 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/jsonl.d.ts +17 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-client.d.ts +246 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-mode.d.ts +20 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-types.d.ts +457 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/package-manager-cli.d.ts +8 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/rpc-entry.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/ansi.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/changelog.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/child-process.d.ts +18 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-image.d.ts +11 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-native.d.ts +11 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard.d.ts +4 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/deprecation.d.ts +4 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/exif-orientation.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/frontmatter.d.ts +8 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/fs-watch.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/git.d.ts +26 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/html.d.ts +7 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-convert.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-process.d.ts +18 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-core.d.ts +30 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-worker.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/json.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/mime.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/open-browser.d.ts +9 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/paths.d.ts +31 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/photon.d.ts +21 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/pi-user-agent.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/shell.d.ts +31 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/sleep.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/syntax-highlight.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/tools-manager.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/version-check.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/windows-self-update.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/index.mjs +84 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/package.json +14 -0
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +102 -15
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +507 -306
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +309 -123
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +124 -58
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +62 -16
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +167 -113
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +139 -58
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +324 -59
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +98 -22
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +106 -106
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +416 -106
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +287 -17
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1122 -96
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +107 -20
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +26 -6
- package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +1 -1
- package/dist/extensions/pi-subagents/test/support/ts-loader.mjs +0 -8
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +194 -59
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +317 -0
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
- package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +334 -248
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +52 -52
- package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +26 -26
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +118 -99
- package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +160 -0
- package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +43 -1
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +36 -10
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +387 -30
- package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +18 -3
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +99 -40
- package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +5 -3
- package/dist/extensions/pi-subagents/test/unit/builtin-agent-documentation.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +19 -19
- package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +94 -23
- package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/chain-serializer.test.ts +20 -20
- package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +35 -5
- package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +70 -52
- package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +187 -0
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +125 -71
- package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +18 -3
- package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +12 -12
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +17 -23
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +30 -2
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +25 -25
- package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/file-coalescer.test.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +91 -47
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +468 -38
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +14 -4
- package/dist/extensions/pi-subagents/test/unit/foreground-tool-call-compaction.test.ts +37 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +69 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +79 -2
- package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +102 -49
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +98 -32
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +7 -6
- package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +171 -0
- package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +116 -0
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +81 -7
- package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +289 -13
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +42 -1
- package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +11 -2
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +84 -10
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +122 -8
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +8 -8
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +24 -24
- package/dist/extensions/pi-subagents/test/unit/owned-process-tree.test.ts +69 -0
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +16 -2
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +10 -10
- package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +26 -17
- package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +405 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +440 -92
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +24 -8
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +6 -6
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +43 -43
- package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +13 -13
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +59 -6
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +19 -19
- package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +243 -0
- package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +68 -168
- package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +58 -15
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +51 -0
- package/dist/extensions/pi-subagents/test/unit/reads-resolution.test.ts +66 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +47 -28
- package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +12 -12
- package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +57 -0
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +111 -31
- package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +121 -0
- package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +65 -50
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +69 -28
- package/dist/extensions/pi-subagents/test/unit/scheduled-store-root.test.ts +67 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +60 -44
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +424 -49
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +23 -1
- package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +32 -5
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +13 -13
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +130 -92
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +85 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +35 -35
- package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +24 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +312 -22
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +150 -15
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +65 -26
- package/dist/extensions/pi-subagents/test/unit/timeout-defaults.test.ts +138 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +76 -25
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +5 -5
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +55 -6
- package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +13 -13
- package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +8 -8
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +6 -6
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +138 -11
- package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +8 -8
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +113 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +68 -7
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +6 -6
- package/dist/extensions/workflow/extension.ts +21 -19
- package/dist/extensions/workflow/modes.ts +65 -0
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.js +48 -1
- package/dist/skills/pi-subagents/SKILL.md +20 -15
- package/dist/skills/pi-subagents/references/constraints-and-recipes.md +77 -83
- package/dist/skills/pi-subagents/references/execution-controls.md +215 -106
- package/dist/skills/pi-subagents/references/management-authoring-rpc.md +29 -9
- package/dist/skills/pi-subagents/references/prompting-and-roles.md +58 -43
- package/docs/plans/workflow-autoloop-reference.md +192 -0
- package/docs/plans/workflow-subagent-consolidation-planb.md +374 -0
- package/docs/plans/workflow-subagent-scripted-merge.md +406 -0
- package/docs/quickstart.md +27 -43
- package/docs/settings.md +4 -0
- package/docs/usage.md +1 -0
- package/package.json +2 -1
- package/dist/extensions/workflow/adapter.ts +0 -949
- package/dist/extensions/workflow/modes/loop.ts +0 -74
- package/dist/extensions/workflow/modes/prototype.ts +0 -170
- package/dist/extensions/workflow/modes/quicktype.ts +0 -150
- package/dist/extensions/workflow/modes/task.ts +0 -105
- package/dist/extensions/workflow/run-state.ts +0 -124
- package/dist/extensions/workflow/state-machine.ts +0 -484
- package/dist/extensions/workflow/validators.ts +0 -42
|
@@ -4,7 +4,7 @@ import * as path from "node:path";
|
|
|
4
4
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
5
5
|
import type { ExtensionAPI, ExtensionContext } from "@selesai/code";
|
|
6
6
|
import { resolveAgentName, type AgentConfig, type AgentScope } from "../../agents/agents.ts";
|
|
7
|
-
import { getArtifactsDir, getChainRunsDir } from "../../shared/artifacts.ts";
|
|
7
|
+
import { getArtifactsDir, getChainRunsDir, getProjectArtifactPackagingWarning, getProjectSubagentsDir } from "../../shared/artifacts.ts";
|
|
8
8
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
9
9
|
import { ChainClarifyComponent, type ChainClarifyResult } from "./chain-clarify.ts";
|
|
10
10
|
import { resolveEffectiveThinking, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
@@ -17,13 +17,19 @@ import {
|
|
|
17
17
|
settleForegroundSchedulingOwner,
|
|
18
18
|
updateForegroundChild,
|
|
19
19
|
} from "./foreground-control.ts";
|
|
20
|
+
import { getLivePromptAudit, rewritePromptWithGuidance, updateLiveEffectivePrompt } from "./prompt-audit.ts";
|
|
21
|
+
import { persistForegroundRunHistory, MAX_REMEMBERED_FOREGROUND_RUNS } from "./foreground-history.ts";
|
|
20
22
|
import { resolveExecutionAgentScope } from "../../agents/agent-scope.ts";
|
|
21
23
|
import { handleManagementAction } from "../../agents/agent-management.ts";
|
|
24
|
+
import { handleRefinementAction } from "../../agents/agent-refinements.ts";
|
|
22
25
|
import { buildDoctorReport } from "../../extension/doctor.ts";
|
|
26
|
+
import { readSubagentGuide } from "../../extension/subagent-guide.ts";
|
|
27
|
+
import { normalizePublicSubagentExecution } from "../../extension/public-execution.ts";
|
|
23
28
|
import { runSync } from "./execution.ts";
|
|
24
29
|
import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
|
|
25
30
|
import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
|
|
26
31
|
import { buildModelCandidates, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelCandidate, type ParentModel } from "../shared/model-fallback.ts";
|
|
32
|
+
import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.ts";
|
|
27
33
|
import type { ModelScopeConfig } from "../shared/model-scope.ts";
|
|
28
34
|
import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
|
|
29
35
|
import { recordRun } from "../shared/run-history.ts";
|
|
@@ -33,6 +39,8 @@ import {
|
|
|
33
39
|
getStepAgents,
|
|
34
40
|
isParallelStep,
|
|
35
41
|
isDynamicParallelStep,
|
|
42
|
+
resolveChainPath,
|
|
43
|
+
resolveExistingReadPaths,
|
|
36
44
|
resolveStepBehavior,
|
|
37
45
|
suppressProgressForReadOnlyTask,
|
|
38
46
|
taskDisallowsFileUpdates,
|
|
@@ -45,17 +53,20 @@ import {
|
|
|
45
53
|
type StepOverrides,
|
|
46
54
|
} from "../../shared/settings.ts";
|
|
47
55
|
import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.ts";
|
|
48
|
-
import { buildAsyncRunnerSteps, executeAsyncChain, executeAsyncSingle, formatAsyncStartedMessage, isAsyncAvailable } from "../background/async-execution.ts";
|
|
49
|
-
import
|
|
56
|
+
import { buildAsyncRunnerSteps, DEFAULT_ASYNC_TIMEOUT_MS, executeAsyncChain, executeAsyncSingle, formatAsyncStartedMessage, isAsyncAvailable, workflowAwaitedAsyncResultPath } from "../background/async-execution.ts";
|
|
57
|
+
import { updateActiveRunIndex } from "../background/active-run-index.ts";
|
|
58
|
+
import { acquireActiveAsyncCapacity, ActiveAsyncCapacityError, getActiveAsyncCapacitySnapshot, resolveMaxActiveAsyncRunsPerSession, transferActiveAsyncCapacity, type ActiveAsyncCapacityHandle } from "../background/active-async-capacity.ts";
|
|
59
|
+
import { isScheduledRunAction, type ScheduledRunAction } from "../background/scheduled-runs.ts";
|
|
50
60
|
import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
|
|
51
61
|
import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
|
|
52
|
-
import { validateExecutionAcceptance } from "../shared/acceptance.ts";
|
|
62
|
+
import { normalizeGateAcceptance, validateExecutionAcceptance } from "../shared/acceptance.ts";
|
|
53
63
|
import { createForkContextResolver, forkedChildRequiresThinkingOff } from "../../shared/fork-context.ts";
|
|
54
64
|
import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
|
|
55
65
|
import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBridge, resolveIntercomSessionTarget, resolveSubagentIntercomTarget, type IntercomBridgeState } from "../../intercom/intercom-bridge.ts";
|
|
56
66
|
import { formatControlIntercomMessage, formatControlNoticeMessage, resolveControlConfig, shouldNotifyControlEvent } from "../shared/subagent-control.ts";
|
|
57
67
|
import { resolveTurnBudgetConfig } from "../shared/turn-budget.ts";
|
|
58
68
|
import { formatSpawnBudget, getSpawnBudgetSnapshot, grantSpawnBudget, preflightSpawnBudget, preflightSpawnBudgetGrant, reserveSpawnBudget } from "../shared/spawn-budget.ts";
|
|
69
|
+
import { claimRunFanoutBatch, claimRunFanoutBatchWithCommit, createRunFanoutBudget, decodeRunFanoutBudgetDescriptor, formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetDescriptor, RunFanoutLimitError, RUN_FANOUT_BUDGET_ENV, writeRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.ts";
|
|
59
70
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
60
71
|
import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig } from "../shared/usage-budget.ts";
|
|
61
72
|
import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
@@ -63,6 +74,7 @@ import { isAgentContractV1 } from "../shared/agent-contract.ts";
|
|
|
63
74
|
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
64
75
|
import { cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
65
76
|
import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage } from "../../shared/utils.ts";
|
|
77
|
+
import { createTaskMutationArbiter } from "../shared/llm-intent-arbiter.ts";
|
|
66
78
|
import { DEFAULT_GLOBAL_CONCURRENCY_LIMIT, Semaphore } from "../shared/parallel-utils.ts";
|
|
67
79
|
import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup, writePendingParallelHandoff } from "../shared/parallel-handoff.ts";
|
|
68
80
|
import { summarizeContextModes, type ContextMode, type ContextSummary } from "../shared/context-mode.ts";
|
|
@@ -75,23 +87,35 @@ import {
|
|
|
75
87
|
stripDetailsOutputsForIntercomReceipt,
|
|
76
88
|
} from "../../intercom/result-intercom.ts";
|
|
77
89
|
import { applySteeringRecoveryAgentConfig, buildRevivedAsyncTask, resolveAsyncResumeTarget, resolveAsyncRunLocation } from "../background/async-resume.ts";
|
|
78
|
-
import { deliverCheckpointDecisionRequest, deliverInterruptRequest, requestAsyncSteer } from "../background/control-channel.ts";
|
|
79
|
-
import { waitForSteeringAction } from "../background/steering.ts";
|
|
90
|
+
import { deliverCheckpointDecisionRequest, deliverInterruptRequest, readRevivalBriefs, requestAsyncSteer, type SteerDeliveryMode } from "../background/control-channel.ts";
|
|
91
|
+
import { updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
|
|
80
92
|
import { steerAsyncRun } from "./async-steering-action.ts";
|
|
93
|
+
import {
|
|
94
|
+
removeWorkflowForegroundSteeringRoute,
|
|
95
|
+
resolveWorkflowForegroundSteeringTarget,
|
|
96
|
+
steerWorkflowForegroundTarget,
|
|
97
|
+
workflowForegroundSteeringDir,
|
|
98
|
+
workflowForegroundSteeringLaunchOptions,
|
|
99
|
+
} from "./workflow-foreground-steering.ts";
|
|
81
100
|
import { stopAsyncRun } from "./async-stop-action.ts";
|
|
101
|
+
import { dismissRecoveredWorkflow } from "./async-dismiss-action.ts";
|
|
82
102
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
83
|
-
import { resolveAsyncRootResultPath } from "../background/chain-root-attachment.ts";
|
|
103
|
+
import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.ts";
|
|
84
104
|
import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedRunResolutionScope } from "../shared/nested-events.ts";
|
|
85
105
|
import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
|
|
86
106
|
import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
|
|
87
107
|
import { inspectSubagentStatus } from "../background/run-status.ts";
|
|
88
108
|
import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.ts";
|
|
89
109
|
import { handleMissionAction, MISSION_ACTIONS } from "../../missions/actions.ts";
|
|
90
|
-
import { attachMissionToLaunchResult, prepareMissionLaunch, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
|
|
110
|
+
import { attachMissionToLaunchResult, prepareMissionLaunch, writeMissionAsyncBinding, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
|
|
111
|
+
import { updateMission } from "../../missions/store.ts";
|
|
112
|
+
import type { MissionWorkflowChildUpdate } from "../../missions/types.ts";
|
|
113
|
+
import { createMissionWorkflowState } from "../../missions/workflow-state.ts";
|
|
91
114
|
import { resolveAuthorityDecision } from "../../policy/authority.ts";
|
|
92
115
|
import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspectors/herdr/actions.ts";
|
|
93
116
|
import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
|
|
94
|
-
import { runWorkflowScript, WorkflowScriptError, type WorkflowScriptChildResult } from "../../workflows/scripted-workflow.ts";
|
|
117
|
+
import { previewSimpleWorkflowRun, runWorkflowScript, WorkflowScriptError, type WorkflowScriptChildResult } from "../../workflows/scripted-workflow.ts";
|
|
118
|
+
import { renderWorkflowPrompt } from "../../shared/prompt-resources.ts";
|
|
95
119
|
import { resolveWorkflowChatProgress, type WorkflowChatProgressProjection } from "../../workflows/chat-progress.ts";
|
|
96
120
|
import {
|
|
97
121
|
cleanupWorktrees,
|
|
@@ -123,6 +147,7 @@ import {
|
|
|
123
147
|
type ResolvedControlConfig,
|
|
124
148
|
type ResolvedTurnBudget,
|
|
125
149
|
type ResolvedToolBudget,
|
|
150
|
+
type RunFanoutBudgetDescriptor,
|
|
126
151
|
type SingleResult,
|
|
127
152
|
type ToolBudgetConfig,
|
|
128
153
|
type TurnBudgetConfig,
|
|
@@ -141,10 +166,52 @@ import {
|
|
|
141
166
|
resolveTopLevelParallelMaxTasks,
|
|
142
167
|
resolveChildMaxSubagentDepth,
|
|
143
168
|
resolveCurrentMaxSubagentDepth,
|
|
169
|
+
resolveMaxSubagentSpawnsPerRun,
|
|
144
170
|
wrapForkTask,
|
|
145
171
|
} from "../../shared/types.ts";
|
|
146
172
|
|
|
147
|
-
const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure", "mission.create", "mission.update", "mission.attach-run", "mission.close", "inspector.open", "inspector.close", "project.open", "project.close", "worktree.discard", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
|
|
173
|
+
const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure", "mission.create", "mission.update", "mission.resolve-decision", "mission.attach-run", "mission.close", "inspector.open", "inspector.close", "project.open", "project.close", "worktree.discard", "refine", "refine.rollback", "dismiss", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
|
|
174
|
+
const DESTRUCTIVE_MANAGEMENT_ACTIONS = new Set(["delete", "eject", "disable", "reset", "mission.close", "worktree.discard", "refine.rollback", "inspector.close", "project.close", "stop", "interrupt", "reject-checkpoint", "schedule.delete"]);
|
|
175
|
+
|
|
176
|
+
function editDistance(left: string, right: string): number {
|
|
177
|
+
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
178
|
+
for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
|
|
179
|
+
let diagonal = previous[0]!;
|
|
180
|
+
previous[0] = leftIndex;
|
|
181
|
+
for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) {
|
|
182
|
+
const above = previous[rightIndex]!;
|
|
183
|
+
previous[rightIndex] = left[leftIndex - 1] === right[rightIndex - 1]
|
|
184
|
+
? diagonal
|
|
185
|
+
: Math.min(diagonal, above, previous[rightIndex - 1]!) + 1;
|
|
186
|
+
diagonal = above;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return previous[right.length]!;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function hasSingleAdjacentTransposition(left: string, right: string): boolean {
|
|
193
|
+
if (left.length !== right.length) return false;
|
|
194
|
+
const mismatch = [...left].findIndex((character, index) => character !== right[index]);
|
|
195
|
+
return mismatch >= 0
|
|
196
|
+
&& left[mismatch] === right[mismatch + 1]
|
|
197
|
+
&& left[mismatch + 1] === right[mismatch]
|
|
198
|
+
&& left.slice(mismatch + 2) === right.slice(mismatch + 2);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function unknownSubagentActionMessage(action: string): string {
|
|
202
|
+
const requested = action.toLowerCase();
|
|
203
|
+
const suggestion = SUBAGENT_ACTIONS.find((candidate) => {
|
|
204
|
+
const distance = editDistance(requested, candidate);
|
|
205
|
+
const closeMatch = distance <= Math.max(1, Math.floor(candidate.length / 4)) || hasSingleAdjacentTransposition(requested, candidate);
|
|
206
|
+
if (DESTRUCTIVE_MANAGEMENT_ACTIONS.has(candidate)) return distance === 1 && requested.length >= candidate.length - 1;
|
|
207
|
+
return closeMatch;
|
|
208
|
+
});
|
|
209
|
+
const nextStep = 'Use subagent({ action: "status" }) to inspect runs or subagent({ action: "list" }) to inspect agents.';
|
|
210
|
+
const validActions = `Valid: ${SUBAGENT_ACTIONS.join(", ")}.`;
|
|
211
|
+
return suggestion
|
|
212
|
+
? `Unknown action: ${action}. Did you mean ${suggestion}? ${nextStep} ${validActions}`
|
|
213
|
+
: `Unknown action: ${action}. ${nextStep} ${validActions}`;
|
|
214
|
+
}
|
|
148
215
|
|
|
149
216
|
type UndefinedOmitted<T extends object> = {
|
|
150
217
|
[K in keyof T as undefined extends T[K] ? never : K]: T[K];
|
|
@@ -203,21 +270,30 @@ export interface SubagentParamsLike {
|
|
|
203
270
|
index?: number;
|
|
204
271
|
view?: "fleet" | "transcript";
|
|
205
272
|
lines?: number;
|
|
273
|
+
topic?: string;
|
|
206
274
|
chainName?: string;
|
|
207
275
|
config?: unknown;
|
|
208
276
|
name?: string;
|
|
209
277
|
type?: string;
|
|
210
278
|
agent?: string;
|
|
211
279
|
task?: string;
|
|
280
|
+
/** Retained async child run id. Valid only on workflow runs.run items. */
|
|
281
|
+
resume?: string;
|
|
212
282
|
message?: string;
|
|
213
283
|
steeringRecovery?: boolean;
|
|
284
|
+
mode?: SteerDeliveryMode;
|
|
214
285
|
workflowScript?: string;
|
|
215
286
|
chatProgress?: "auto" | "off" | "terminal" | "milestones" | "live-card";
|
|
216
287
|
step?: ChainStep;
|
|
217
288
|
/** Internal workflow ownership metadata; not part of the public schema. */
|
|
218
289
|
workflowParentRunId?: string;
|
|
219
290
|
workflowKey?: string;
|
|
291
|
+
workflowChildAsyncId?: string;
|
|
220
292
|
suppressRoutineResultIntercom?: boolean;
|
|
293
|
+
/** Internal inherited cumulative run-tree budget. */
|
|
294
|
+
runFanoutBudget?: RunFanoutBudgetDescriptor;
|
|
295
|
+
/** Internal workflow host admission proof. */
|
|
296
|
+
runFanoutAdmitted?: boolean;
|
|
221
297
|
/** Internal durable-run compatibility fields. Public callers must use workflowScript. */
|
|
222
298
|
chain?: ChainStep[];
|
|
223
299
|
tasks?: TaskParam[];
|
|
@@ -248,11 +324,14 @@ export interface SubagentParamsLike {
|
|
|
248
324
|
focus?: boolean;
|
|
249
325
|
skill?: string | string[] | boolean;
|
|
250
326
|
output?: string | boolean;
|
|
327
|
+
/** Internal-only; not part of the public tool schema. Wired for single-run reads (chain steps use their own field). */
|
|
328
|
+
reads?: string[] | false;
|
|
251
329
|
outputMode?: "inline" | "file-only";
|
|
252
330
|
outputSchema?: JsonSchemaObject;
|
|
253
331
|
agentScope?: unknown;
|
|
254
332
|
chainDir?: string;
|
|
255
333
|
acceptance?: AcceptanceInput;
|
|
334
|
+
gate?: string;
|
|
256
335
|
agentContract?: AgentContract;
|
|
257
336
|
schedule?: string;
|
|
258
337
|
scheduleName?: string;
|
|
@@ -319,6 +398,7 @@ interface ExecutionContextData {
|
|
|
319
398
|
artifactsDir: string;
|
|
320
399
|
backgroundRequestedWhileClarifying: boolean;
|
|
321
400
|
effectiveAsync: boolean;
|
|
401
|
+
asyncRunId: string;
|
|
322
402
|
controlConfig: ResolvedControlConfig;
|
|
323
403
|
intercomBridge: IntercomBridgeState;
|
|
324
404
|
nestedRoute?: NestedRouteInfo;
|
|
@@ -335,6 +415,9 @@ interface ExecutionContextData {
|
|
|
335
415
|
parentSessionId: string | null;
|
|
336
416
|
parentPiSessionId?: string;
|
|
337
417
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
418
|
+
runFanoutBudget: RunFanoutBudgetDescriptor;
|
|
419
|
+
topLevelAsyncCapacityEligible: boolean;
|
|
420
|
+
activeAsyncCapacity?: ActiveAsyncCapacityHandle;
|
|
338
421
|
}
|
|
339
422
|
|
|
340
423
|
function resolveRequestedCwd(runtimeCwd: string, requestedCwd: string | undefined): string {
|
|
@@ -344,6 +427,7 @@ function resolveRequestedCwd(runtimeCwd: string, requestedCwd: string | undefine
|
|
|
344
427
|
function removeForegroundControlIfIdle(state: SubagentState, runId: string): boolean {
|
|
345
428
|
const control = state.foregroundControls.get(runId);
|
|
346
429
|
if (control && (!foregroundSchedulingSettled(control) || (control.activeChildren?.size ?? 0) > 0)) return false;
|
|
430
|
+
if (control) removeWorkflowForegroundSteeringRoute(control);
|
|
347
431
|
state.foregroundControls.delete(runId);
|
|
348
432
|
if (state.lastForegroundControlId === runId) state.lastForegroundControlId = null;
|
|
349
433
|
return true;
|
|
@@ -362,6 +446,17 @@ function getForegroundControl(state: SubagentState, runId: string | undefined) {
|
|
|
362
446
|
return newest;
|
|
363
447
|
}
|
|
364
448
|
|
|
449
|
+
function promptAuditRedoParams(value: unknown, rewrittenTask: string): SubagentParamsLike {
|
|
450
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Prompt redo is missing a safe live launch contract.");
|
|
451
|
+
const params = { ...(value as SubagentParamsLike), task: rewrittenTask, async: false };
|
|
452
|
+
delete params.workflowParentRunId;
|
|
453
|
+
delete params.workflowKey;
|
|
454
|
+
delete params.workflowChildAsyncId;
|
|
455
|
+
delete params.suppressRoutineResultIntercom;
|
|
456
|
+
if (params.worktree === true && Array.isArray(params.tasks)) delete params.cwd;
|
|
457
|
+
return params;
|
|
458
|
+
}
|
|
459
|
+
|
|
365
460
|
function formatForegroundActivity(control: SubagentState["foregroundControls"] extends Map<string, infer T> ? T : never): string | undefined {
|
|
366
461
|
const facts: string[] = [];
|
|
367
462
|
if (control.currentTool && control.currentToolStartedAt) facts.push(`tool ${control.currentTool} for ${Math.floor(Math.max(0, Date.now() - control.currentToolStartedAt) / 1000)}s`);
|
|
@@ -413,12 +508,18 @@ function withSpawnBudgetStatus(
|
|
|
413
508
|
sessionId: string | null,
|
|
414
509
|
): AgentToolResult<Details> {
|
|
415
510
|
const spawnBudget = getSpawnBudgetSnapshot(state, config, sessionId);
|
|
511
|
+
const activeAsyncCapacity = sessionId
|
|
512
|
+
? getActiveAsyncCapacitySnapshot(sessionId, resolveMaxActiveAsyncRunsPerSession(config.maxActiveAsyncRunsPerSession), {
|
|
513
|
+
liveWorkflowRunIds: new Set(state.workflowControllers?.keys() ?? []),
|
|
514
|
+
})
|
|
515
|
+
: { used: 0, limit: resolveMaxActiveAsyncRunsPerSession(config.maxActiveAsyncRunsPerSession) ?? 0 };
|
|
516
|
+
state.activeAsyncCapacity = activeAsyncCapacity;
|
|
416
517
|
return {
|
|
417
518
|
...result,
|
|
418
519
|
content: result.content.map((item, index) => index === 0 && item.type === "text"
|
|
419
|
-
? { ...item, text: `${formatSpawnBudget(spawnBudget)}\n${item.text}` }
|
|
520
|
+
? { ...item, text: `${formatSpawnBudget(spawnBudget)}\nActive async capacity: ${activeAsyncCapacity.used}/${activeAsyncCapacity.limit || "unlimited"} used\n${item.text}` }
|
|
420
521
|
: item),
|
|
421
|
-
details: { ...result.details, spawnBudget },
|
|
522
|
+
details: { ...result.details, spawnBudget, activeAsyncCapacity },
|
|
422
523
|
};
|
|
423
524
|
}
|
|
424
525
|
|
|
@@ -439,6 +540,31 @@ function countRequestedSubagentSpawns(params: SubagentParamsLike, config: Extens
|
|
|
439
540
|
return params.agent ? 1 : 0;
|
|
440
541
|
}
|
|
441
542
|
|
|
543
|
+
function staticRunFanoutPaths(params: SubagentParamsLike): string[] {
|
|
544
|
+
if (params.tasks) return params.tasks.map((_, index) => `tasks[${index}]`);
|
|
545
|
+
if (params.chain) return params.chain.flatMap((step, stepIndex) => {
|
|
546
|
+
if (isDynamicParallelStep(step) || "checkpoint" in step) return [];
|
|
547
|
+
if (isParallelStep(step)) return step.parallel.map((_, itemIndex) => `chain[${stepIndex}].parallel[${itemIndex}]`);
|
|
548
|
+
return [`chain[${stepIndex}]`];
|
|
549
|
+
});
|
|
550
|
+
return params.agent ? ["single"] : [];
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function runFanoutErrorResult(error: RunFanoutLimitError, mode: "single" | "parallel" | "chain"): AgentToolResult<Details> {
|
|
554
|
+
return { content: [{ type: "text", text: error.message }], isError: true, details: { mode, results: [], runFanoutBudget: error.snapshot, runFanoutRejection: error.rejection } };
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function withRunFanoutBudget(result: AgentToolResult<Details>, descriptor: RunFanoutBudgetDescriptor, options: { annotateContent?: boolean } = {}): AgentToolResult<Details> {
|
|
558
|
+
const runFanoutBudget = getRunFanoutBudgetSnapshot(descriptor);
|
|
559
|
+
return {
|
|
560
|
+
...result,
|
|
561
|
+
content: options.annotateContent === false
|
|
562
|
+
? result.content
|
|
563
|
+
: result.content.map((item, index) => index === 0 && item.type === "text" ? { ...item, text: `${formatRunFanoutBudget(runFanoutBudget)}\n${item.text}` } : item),
|
|
564
|
+
details: { ...result.details, runFanoutBudget },
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
|
|
442
568
|
function foregroundStatusResult(control: SubagentState["foregroundControls"] extends Map<string, infer T> ? T : never): AgentToolResult<Details> {
|
|
443
569
|
let nestedWarning: string | undefined;
|
|
444
570
|
try {
|
|
@@ -461,7 +587,7 @@ function foregroundStatusResult(control: SubagentState["foregroundControls"] ext
|
|
|
461
587
|
|
|
462
588
|
function trimRememberedForegroundRuns(state: SubagentState): void {
|
|
463
589
|
if (!state.foregroundRuns) return;
|
|
464
|
-
while (state.foregroundRuns.size >
|
|
590
|
+
while (state.foregroundRuns.size > MAX_REMEMBERED_FOREGROUND_RUNS) {
|
|
465
591
|
const oldestTerminal = [...state.foregroundRuns.values()]
|
|
466
592
|
.filter((run) => !run.children.some((child) => child.status === "detached"))
|
|
467
593
|
.sort((left, right) => left.updatedAt - right.updatedAt)[0];
|
|
@@ -470,6 +596,14 @@ function trimRememberedForegroundRuns(state: SubagentState): void {
|
|
|
470
596
|
}
|
|
471
597
|
}
|
|
472
598
|
|
|
599
|
+
function persistRememberedForegroundRuns(state: SubagentState): void {
|
|
600
|
+
try {
|
|
601
|
+
persistForegroundRunHistory(state);
|
|
602
|
+
} catch (error) {
|
|
603
|
+
console.error("Failed to persist foreground run history:", error);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
473
607
|
function foregroundChildActivityFromProgress(progress: SingleResult["progress"] | undefined) {
|
|
474
608
|
return {
|
|
475
609
|
...(progress?.activityState ? { activityState: progress.activityState } : {}),
|
|
@@ -536,6 +670,7 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
536
670
|
}),
|
|
537
671
|
});
|
|
538
672
|
trimRememberedForegroundRuns(state);
|
|
673
|
+
persistRememberedForegroundRuns(state);
|
|
539
674
|
}
|
|
540
675
|
|
|
541
676
|
function applyControlEventToRememberedForegroundRun(state: SubagentState, event: ControlEvent): void {
|
|
@@ -611,6 +746,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
611
746
|
...(input.result.capabilityAudit ? { capabilityAudit: input.result.capabilityAudit } : {}),
|
|
612
747
|
});
|
|
613
748
|
trimRememberedForegroundRuns(state);
|
|
749
|
+
persistRememberedForegroundRuns(state);
|
|
614
750
|
const output = getSingleResultOutput(input.result).trim();
|
|
615
751
|
const success = terminalStatus === "completed";
|
|
616
752
|
const summary = !success && input.result.error
|
|
@@ -790,6 +926,20 @@ function getAsyncInterruptTarget(
|
|
|
790
926
|
return newest ? { asyncId: newest.asyncId, asyncDir: newest.asyncDir } : undefined;
|
|
791
927
|
}
|
|
792
928
|
|
|
929
|
+
function isStaleExtensionContextError(error: unknown): boolean {
|
|
930
|
+
if (!(error instanceof Error)) return false;
|
|
931
|
+
return /extension ctx is stale|stale after session replacement or reload/i.test(error.message);
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
function emitAdvisoryControlEvent(pi: ExtensionAPI, channel: string, payload: unknown): void {
|
|
935
|
+
try {
|
|
936
|
+
pi.events.emit(channel, payload);
|
|
937
|
+
} catch (error) {
|
|
938
|
+
if (isStaleExtensionContextError(error)) return;
|
|
939
|
+
throw error;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
793
943
|
function emitControlNotification(input: {
|
|
794
944
|
pi: ExtensionAPI;
|
|
795
945
|
controlConfig: ResolvedControlConfig;
|
|
@@ -807,10 +957,10 @@ function emitControlNotification(input: {
|
|
|
807
957
|
noticeText: formatControlNoticeMessage(input.event, childIntercomTarget),
|
|
808
958
|
};
|
|
809
959
|
if (input.controlConfig.notifyChannels.includes("event")) {
|
|
810
|
-
input.pi
|
|
960
|
+
emitAdvisoryControlEvent(input.pi, SUBAGENT_CONTROL_EVENT, payload);
|
|
811
961
|
}
|
|
812
962
|
if (input.event.type !== "active_long_running" && input.controlConfig.notifyChannels.includes("intercom") && input.intercomBridge.active && input.intercomBridge.orchestratorTarget) {
|
|
813
|
-
input.pi
|
|
963
|
+
emitAdvisoryControlEvent(input.pi, SUBAGENT_CONTROL_INTERCOM_EVENT, {
|
|
814
964
|
...payload,
|
|
815
965
|
to: input.intercomBridge.orchestratorTarget,
|
|
816
966
|
message: formatControlIntercomMessage(input.event, childIntercomTarget),
|
|
@@ -842,8 +992,15 @@ function interruptAsyncRun(
|
|
|
842
992
|
details: { mode: "management", results: [] },
|
|
843
993
|
};
|
|
844
994
|
}
|
|
995
|
+
if (status.mode === "workflow") {
|
|
996
|
+
return {
|
|
997
|
+
content: [{ type: "text", text: `Interrupt is unsupported for async workflow ${target.asyncId}; use stop instead.` }],
|
|
998
|
+
isError: true,
|
|
999
|
+
details: { mode: "management", results: [] },
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
845
1002
|
try {
|
|
846
|
-
deliverInterruptRequest(
|
|
1003
|
+
deliverInterruptRequest({ asyncDir: target.asyncDir, source: "interrupt-action" });
|
|
847
1004
|
const tracked = state.asyncJobs.get(target.asyncId);
|
|
848
1005
|
if (tracked) {
|
|
849
1006
|
delete tracked.activityState;
|
|
@@ -1040,18 +1197,30 @@ function appendStepToAsyncChain(input: {
|
|
|
1040
1197
|
}
|
|
1041
1198
|
|
|
1042
1199
|
try {
|
|
1200
|
+
const asyncDir = resolved.location.asyncDir;
|
|
1201
|
+
if (!asyncDir) throw new Error(`Run '${resolved.id}' is missing its async directory.`);
|
|
1202
|
+
const runFanoutBudget = readRunFanoutBudgetDescriptor(asyncDir);
|
|
1203
|
+
if (!runFanoutBudget) throw new Error(`Run '${resolved.id}' is missing its run fan-out budget identity.`);
|
|
1204
|
+
const startIndex = (status.chainStepCount ?? status.steps?.length ?? 0) + pendingAppendRequests.reduce((total, request) => total + request.steps.length, 0);
|
|
1205
|
+
const appendPaths = chain.flatMap((step, localIndex) => {
|
|
1206
|
+
const absoluteIndex = startIndex + localIndex;
|
|
1207
|
+
if (isDynamicParallelStep(step) || "checkpoint" in step) return [];
|
|
1208
|
+
if (isParallelStep(step)) return step.parallel.map((_, itemIndex) => `chain[${absoluteIndex}].parallel[${itemIndex}]`);
|
|
1209
|
+
return [`chain[${absoluteIndex}]`];
|
|
1210
|
+
});
|
|
1043
1211
|
const result = enqueueChainAppendRequest({
|
|
1044
|
-
asyncDir
|
|
1212
|
+
asyncDir,
|
|
1045
1213
|
runId: resolved.id,
|
|
1046
1214
|
steps: built.steps,
|
|
1215
|
+
admit: (persist) => claimRunFanoutBatchWithCommit(runFanoutBudget, appendPaths, persist),
|
|
1047
1216
|
});
|
|
1048
1217
|
const stepText = built.steps.length === 1 ? "step" : "steps";
|
|
1049
1218
|
return {
|
|
1050
1219
|
content: [{
|
|
1051
1220
|
type: "text",
|
|
1052
|
-
text: `Append queued for chain run ${resolved.id}: ${built.steps.length} ${stepText}. It becomes eligible after the chain's already-queued steps finish. Pending appends: ${result.pendingCount}
|
|
1221
|
+
text: `Append queued for chain run ${resolved.id}: ${built.steps.length} ${stepText}. It becomes eligible after the chain's already-queued steps finish. Pending appends: ${result.pendingCount}.${result.bookkeepingError ? ` Bookkeeping warning: ${result.bookkeepingError}` : ""}`,
|
|
1053
1222
|
}],
|
|
1054
|
-
details: { mode: "management", results: [], asyncId: resolved.id, asyncDir
|
|
1223
|
+
details: { mode: "management", results: [], asyncId: resolved.id, asyncDir },
|
|
1055
1224
|
};
|
|
1056
1225
|
} catch (error) {
|
|
1057
1226
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1152,7 +1321,7 @@ function directNestedAsyncInterrupt(target: ResolvedSubagentRunId & { kind: "nes
|
|
|
1152
1321
|
const pid = typeof status?.pid === "number" && status.pid > 0 ? status.pid : run.pid;
|
|
1153
1322
|
if (!status || status.state !== "running" || typeof pid !== "number" || pid <= 0) return undefined;
|
|
1154
1323
|
try {
|
|
1155
|
-
deliverInterruptRequest({ asyncDir,
|
|
1324
|
+
deliverInterruptRequest({ asyncDir, source: "nested-interrupt" });
|
|
1156
1325
|
return { content: [{ type: "text", text: `Interrupt requested for nested async run ${run.id}.` }], details: { mode: "management", results: [] } };
|
|
1157
1326
|
} catch (error) {
|
|
1158
1327
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1160,7 +1329,7 @@ function directNestedAsyncInterrupt(target: ResolvedSubagentRunId & { kind: "nes
|
|
|
1160
1329
|
}
|
|
1161
1330
|
}
|
|
1162
1331
|
|
|
1163
|
-
async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details> | undefined> {
|
|
1332
|
+
async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; mode?: SteerDeliveryMode; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details> | undefined> {
|
|
1164
1333
|
const run = input.target.match.run;
|
|
1165
1334
|
const asyncDir = resolveNestedAsyncDir(input.target.match.rootRunId, run);
|
|
1166
1335
|
if (!asyncDir) return undefined;
|
|
@@ -1182,6 +1351,7 @@ async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & {
|
|
|
1182
1351
|
try {
|
|
1183
1352
|
requestAsyncSteer(asyncDir, {
|
|
1184
1353
|
message: input.message,
|
|
1354
|
+
mode: input.mode,
|
|
1185
1355
|
...(effectiveTargetIndex !== undefined ? { targetIndex: effectiveTargetIndex } : { targetIndexes }),
|
|
1186
1356
|
source: "nested-steer",
|
|
1187
1357
|
id: requestId,
|
|
@@ -1191,12 +1361,12 @@ async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & {
|
|
|
1191
1361
|
}
|
|
1192
1362
|
const targets = targetIndexes.map((index) => ({ index, state: steps[index]?.status === "pending" ? "scheduled" as const : "pending" as const }));
|
|
1193
1363
|
if (targets.every((target) => target.state === "scheduled")) {
|
|
1194
|
-
const scheduled = { requestId, state: "scheduled" as const, sourceRunId: run.id, targets };
|
|
1364
|
+
const scheduled = { requestId, state: "scheduled" as const, deliveryStatus: "queued" as const, sourceRunId: run.id, targets };
|
|
1195
1365
|
return { content: [{ type: "text", text: `Steering scheduled for nested async run ${run.id} (request ${requestId}).` }], details: { mode: "management", results: [], steering: scheduled } };
|
|
1196
1366
|
}
|
|
1197
1367
|
const waited = await waitForSteeringAction(omitUndefinedProperties({ asyncDir, sourceRunId: run.id, requestId, timeoutMs: 3_000, signal: input.signal }));
|
|
1198
|
-
const result = waited ?? { requestId, state: "pending" as const, sourceRunId: run.id, targets };
|
|
1199
|
-
const stateText = result.state === "
|
|
1368
|
+
const result = waited ?? { requestId, state: "pending" as const, deliveryStatus: "queued" as const, sourceRunId: run.id, targets };
|
|
1369
|
+
const stateText = result.state === "failed" ? "failed" : result.state === "partial" ? "partial" : result.deliveryStatus === "queued" ? "queued" : result.state === "delivered" ? "delivered" : "pending";
|
|
1200
1370
|
return { content: [{ type: "text", text: `Steering ${stateText} for nested async run ${run.id} (request ${requestId}).` }], ...(result.state === "failed" || result.state === "partial" ? { isError: true } : {}), details: { mode: "management", results: [], steering: result } };
|
|
1201
1371
|
}
|
|
1202
1372
|
|
|
@@ -1219,7 +1389,7 @@ async function resumeLiveNestedRun(input: { target: ResolvedSubagentRunId & { ki
|
|
|
1219
1389
|
return { content: [{ type: "text", text: `Nested run ${run.id} appears live but its owner route is not reachable. Wait for completion, then retry action='resume'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1220
1390
|
}
|
|
1221
1391
|
|
|
1222
|
-
async function steerNestedRun(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details>> {
|
|
1392
|
+
async function steerNestedRun(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; mode?: SteerDeliveryMode; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details>> {
|
|
1223
1393
|
const run = input.target.match.run;
|
|
1224
1394
|
if (run.state !== "running" && run.state !== "queued") return { content: [{ type: "text", text: `Nested run ${run.id} is ${run.state} and cannot be steered.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1225
1395
|
const direct = await directNestedAsyncSteer(input);
|
|
@@ -1243,6 +1413,7 @@ async function resumeAsyncRun(input: {
|
|
|
1243
1413
|
deps: ExecutorDeps;
|
|
1244
1414
|
parentModel?: ParentModel;
|
|
1245
1415
|
absoluteDeadlineAt?: number;
|
|
1416
|
+
signal?: AbortSignal;
|
|
1246
1417
|
}): Promise<AgentToolResult<Details>> {
|
|
1247
1418
|
const followUp = (input.params.message ?? input.params.task ?? "").trim();
|
|
1248
1419
|
const attachChain = (input.params.chain?.length ?? 0) > 0 ? input.params.chain as ChainStep[] : undefined;
|
|
@@ -1358,7 +1529,7 @@ async function resumeAsyncRun(input: {
|
|
|
1358
1529
|
inheritProjectContext: recoveryDescriptor.inheritProjectContext,
|
|
1359
1530
|
inheritSkills: recoveryDescriptor.inheritSkills,
|
|
1360
1531
|
source: "project",
|
|
1361
|
-
filePath: recoveryDescriptor.agentFilePath ?? path.join(recoveryDescriptor.cwd, "
|
|
1532
|
+
filePath: recoveryDescriptor.agentFilePath ?? path.join(getProjectSubagentsDir(recoveryDescriptor.cwd), "recovery-agent"),
|
|
1362
1533
|
} : undefined);
|
|
1363
1534
|
if (!agentConfig) {
|
|
1364
1535
|
return {
|
|
@@ -1384,6 +1555,20 @@ async function resumeAsyncRun(input: {
|
|
|
1384
1555
|
};
|
|
1385
1556
|
}
|
|
1386
1557
|
const runId = randomUUID().slice(0, 8);
|
|
1558
|
+
const topLevelResume = depth === 0 && !resolveInheritedNestedRouteFromEnv() && !input.params.workflowParentRunId;
|
|
1559
|
+
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
1560
|
+
try {
|
|
1561
|
+
activeAsyncCapacity = topLevelResume ? acquireActiveAsyncCapacity({
|
|
1562
|
+
sessionId: input.deps.state.currentSessionId!,
|
|
1563
|
+
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
1564
|
+
runId,
|
|
1565
|
+
kind: "runner",
|
|
1566
|
+
asyncDir: path.join(DIRS.async, runId),
|
|
1567
|
+
}, { liveWorkflowRunIds: new Set(input.deps.state.workflowControllers?.keys() ?? []) }) : undefined;
|
|
1568
|
+
} catch (error) {
|
|
1569
|
+
if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "chain", results: [], activeAsyncCapacity: error.snapshot } };
|
|
1570
|
+
throw error;
|
|
1571
|
+
}
|
|
1387
1572
|
const artifactConfig: ArtifactConfig = omitUndefinedProperties({ ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir });
|
|
1388
1573
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
1389
1574
|
const contextPolicy = resolveExplicitContextPolicy(input.params);
|
|
@@ -1435,9 +1620,14 @@ async function resumeAsyncRun(input: {
|
|
|
1435
1620
|
controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
|
|
1436
1621
|
childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
|
|
1437
1622
|
globalConcurrencyLimit: input.deps.config.globalConcurrencyLimit,
|
|
1623
|
+
runFanoutBudget: createRunFanoutBudget(runId, resolveMaxSubagentSpawnsPerRun(input.deps.config.maxSubagentSpawnsPerRun)),
|
|
1438
1624
|
capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
|
|
1625
|
+
activeAsyncCapacity,
|
|
1439
1626
|
}));
|
|
1440
|
-
if (result.isError)
|
|
1627
|
+
if (result.isError) {
|
|
1628
|
+
activeAsyncCapacity?.rollback();
|
|
1629
|
+
return result;
|
|
1630
|
+
}
|
|
1441
1631
|
const attachedId = result.details.asyncId ?? runId;
|
|
1442
1632
|
const lines = [
|
|
1443
1633
|
`Attached async subagent ${target.runId} as the first step of a new chain.`,
|
|
@@ -1449,20 +1639,49 @@ async function resumeAsyncRun(input: {
|
|
|
1449
1639
|
return { content: [{ type: "text", text: formatAsyncStartedMessage(lines.join("\n"), input.ctx.hasUI) }], details: result.details };
|
|
1450
1640
|
}
|
|
1451
1641
|
|
|
1642
|
+
const sourceAsyncDir = target.source === "async" ? target.asyncDir : undefined;
|
|
1643
|
+
const queuedBriefs = sourceAsyncDir ? readRevivalBriefs(sourceAsyncDir) : [];
|
|
1644
|
+
const effectiveFollowUp = [...queuedBriefs.map(({ request }) => request.message), followUp].filter(Boolean).join("\n\n");
|
|
1452
1645
|
const revivalSessionFile = target.sessionFile;
|
|
1453
1646
|
if (!revivalSessionFile) {
|
|
1454
1647
|
return { content: [{ type: "text", text: `Async child '${target.runId}' has no persisted session file to resume.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1455
1648
|
}
|
|
1649
|
+
if (target.source === "async" && !recoveryDescriptor) {
|
|
1650
|
+
return { content: [{ type: "text", text: `Async child '${target.runId}' is missing its required run fan-out recovery identity. Start a new run instead.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1651
|
+
}
|
|
1456
1652
|
const runId = randomUUID().slice(0, 8);
|
|
1653
|
+
const topLevelResume = depth === 0 && !resolveInheritedNestedRouteFromEnv() && !input.params.workflowParentRunId;
|
|
1654
|
+
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
1655
|
+
try {
|
|
1656
|
+
activeAsyncCapacity = !topLevelResume ? undefined : target.source === "async"
|
|
1657
|
+
? transferActiveAsyncCapacity({
|
|
1658
|
+
sessionId: input.deps.state.currentSessionId!,
|
|
1659
|
+
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
1660
|
+
sourceRunId: target.runId,
|
|
1661
|
+
runId,
|
|
1662
|
+
asyncDir: path.join(DIRS.async, runId),
|
|
1663
|
+
})
|
|
1664
|
+
: acquireActiveAsyncCapacity({
|
|
1665
|
+
sessionId: input.deps.state.currentSessionId!,
|
|
1666
|
+
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
1667
|
+
runId,
|
|
1668
|
+
kind: "runner",
|
|
1669
|
+
asyncDir: path.join(DIRS.async, runId),
|
|
1670
|
+
});
|
|
1671
|
+
} catch (error) {
|
|
1672
|
+
if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "single", results: [], activeAsyncCapacity: error.snapshot } };
|
|
1673
|
+
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "single", results: [] } };
|
|
1674
|
+
}
|
|
1457
1675
|
const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(agentConfig, recoveryDescriptor) : agentConfig;
|
|
1458
1676
|
const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? omitUndefinedProperties({ ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir });
|
|
1459
1677
|
const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
1460
1678
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
1461
1679
|
const parentModel = input.parentModel;
|
|
1680
|
+
const revivalAsyncDir = path.join(DIRS.async, runId);
|
|
1462
1681
|
const result = executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
1463
1682
|
agent: target.agent,
|
|
1464
|
-
task: buildRevivedAsyncTask(target as Parameters<typeof buildRevivedAsyncTask>[0],
|
|
1465
|
-
goal:
|
|
1683
|
+
task: buildRevivedAsyncTask(target as Parameters<typeof buildRevivedAsyncTask>[0], effectiveFollowUp),
|
|
1684
|
+
goal: effectiveFollowUp,
|
|
1466
1685
|
agentConfig: recoveryAgentConfig,
|
|
1467
1686
|
ctx: compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
|
|
1468
1687
|
pi: input.deps.pi,
|
|
@@ -1512,10 +1731,90 @@ async function resumeAsyncRun(input: {
|
|
|
1512
1731
|
...(input.params.turnBudget !== undefined ? { turnBudget: input.params.turnBudget } : {}),
|
|
1513
1732
|
...(input.params.toolBudget !== undefined ? { toolBudget: input.params.toolBudget } : {}),
|
|
1514
1733
|
capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, recoveryDescriptor?.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
|
|
1734
|
+
runFanoutBudget: recoveryDescriptor?.runFanoutBudget ?? createRunFanoutBudget(runId, resolveMaxSubagentSpawnsPerRun(input.deps.config.maxSubagentSpawnsPerRun)),
|
|
1735
|
+
parentWorkflowRunId: input.params.workflowParentRunId,
|
|
1736
|
+
workflowKey: input.params.workflowKey,
|
|
1737
|
+
activeAsyncCapacity,
|
|
1515
1738
|
}));
|
|
1516
|
-
if (result.isError)
|
|
1739
|
+
if (result.isError) {
|
|
1740
|
+
const startedStatus = readStatus(revivalAsyncDir);
|
|
1741
|
+
if (input.params.workflowParentRunId !== undefined && startedStatus?.runId === runId && startedStatus.processTerminal?.runnerProcessInstanceId) {
|
|
1742
|
+
return {
|
|
1743
|
+
...result,
|
|
1744
|
+
details: {
|
|
1745
|
+
...result.details,
|
|
1746
|
+
runId,
|
|
1747
|
+
asyncId: runId,
|
|
1748
|
+
asyncDir: revivalAsyncDir,
|
|
1749
|
+
...(target.launchContractDigest ? { sourceLaunchContractDigest: target.launchContractDigest } : {}),
|
|
1750
|
+
},
|
|
1751
|
+
};
|
|
1752
|
+
}
|
|
1753
|
+
activeAsyncCapacity?.rollback();
|
|
1754
|
+
return result;
|
|
1755
|
+
}
|
|
1756
|
+
for (const brief of queuedBriefs) fs.rmSync(brief.path, { force: true });
|
|
1757
|
+
if (queuedBriefs.length > 0 && sourceAsyncDir) {
|
|
1758
|
+
const sourceStatus = readStatus(sourceAsyncDir);
|
|
1759
|
+
if (sourceStatus?.steering) {
|
|
1760
|
+
for (const brief of queuedBriefs) updateSteeringTarget(sourceStatus.steering, brief.request.id, target.index, "delivered", Date.now());
|
|
1761
|
+
writeAtomicJson(path.join(sourceAsyncDir, "status.json"), sourceStatus);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1517
1764
|
|
|
1518
1765
|
const revivedId = result.details.asyncId ?? runId;
|
|
1766
|
+
if (input.params.workflowParentRunId !== undefined && result.details.asyncDir) {
|
|
1767
|
+
const asyncDir = result.details.asyncDir;
|
|
1768
|
+
const resultPath = workflowAwaitedAsyncResultPath(asyncDir);
|
|
1769
|
+
const stopOnAbort = () => { stopAsyncRun(input.deps.state, revivedId, input.deps.kill, { asyncDir, resolvedId: revivedId }); };
|
|
1770
|
+
if (input.signal?.aborted) stopOnAbort();
|
|
1771
|
+
else input.signal?.addEventListener("abort", stopOnAbort, { once: true });
|
|
1772
|
+
let completed: Awaited<ReturnType<typeof waitForImportedAsyncRoot>>;
|
|
1773
|
+
try {
|
|
1774
|
+
completed = await waitForImportedAsyncRoot({ runId: revivedId, asyncDir, resultPath, index: 0 });
|
|
1775
|
+
} finally {
|
|
1776
|
+
input.signal?.removeEventListener("abort", stopOnAbort);
|
|
1777
|
+
}
|
|
1778
|
+
fs.rmSync(resultPath, { force: true });
|
|
1779
|
+
const totalCost = completed.totalCost;
|
|
1780
|
+
const childResult: SingleResult = {
|
|
1781
|
+
index: 0,
|
|
1782
|
+
agent: completed.agent,
|
|
1783
|
+
task: effectiveFollowUp,
|
|
1784
|
+
exitCode: completed.exitCode,
|
|
1785
|
+
usage: {
|
|
1786
|
+
input: totalCost?.inputTokens ?? 0,
|
|
1787
|
+
output: totalCost?.outputTokens ?? 0,
|
|
1788
|
+
cacheRead: 0,
|
|
1789
|
+
cacheWrite: 0,
|
|
1790
|
+
cost: totalCost?.costUsd ?? 0,
|
|
1791
|
+
turns: 0,
|
|
1792
|
+
},
|
|
1793
|
+
finalOutput: completed.output,
|
|
1794
|
+
outputState: completed.output.trim() ? "present" : "absent",
|
|
1795
|
+
...(completed.error ? { error: completed.error } : {}),
|
|
1796
|
+
...(completed.timedOut ? { timedOut: true } : {}),
|
|
1797
|
+
...(completed.stopped ? { stopped: true } : {}),
|
|
1798
|
+
...(completed.sessionFile ? { sessionFile: completed.sessionFile } : {}),
|
|
1799
|
+
...(completed.model ? { model: completed.model } : {}),
|
|
1800
|
+
...(completed.attemptedModels ? { attemptedModels: completed.attemptedModels } : {}),
|
|
1801
|
+
...(completed.modelAttempts ? { modelAttempts: completed.modelAttempts } : {}),
|
|
1802
|
+
...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
|
|
1803
|
+
...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
|
|
1804
|
+
...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
|
|
1805
|
+
...(completed.acceptance ? { acceptance: completed.acceptance } : {}),
|
|
1806
|
+
};
|
|
1807
|
+
return {
|
|
1808
|
+
content: [{ type: "text", text: completed.output || completed.error || `Revived ${target.source} subagent ${revivedId} completed without output.` }],
|
|
1809
|
+
...(completed.success ? {} : { isError: true }),
|
|
1810
|
+
details: {
|
|
1811
|
+
...result.details,
|
|
1812
|
+
runId: revivedId,
|
|
1813
|
+
results: [childResult],
|
|
1814
|
+
...(target.launchContractDigest ? { sourceLaunchContractDigest: target.launchContractDigest } : {}),
|
|
1815
|
+
},
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1519
1818
|
const revivedTarget = intercomBridge.active ? resolveSubagentIntercomTarget(revivedId, target.agent, 0) : undefined;
|
|
1520
1819
|
const sourceLabel = target.source;
|
|
1521
1820
|
const lines = [
|
|
@@ -1631,6 +1930,7 @@ async function maybeBuildForegroundIntercomReceipt(input: {
|
|
|
1631
1930
|
mode: SubagentRunMode;
|
|
1632
1931
|
details: Details;
|
|
1633
1932
|
nestedChildren?: NestedRunSummary[];
|
|
1933
|
+
preserveDetailsOutputs?: boolean;
|
|
1634
1934
|
}): Promise<{ text: string; details: Details } | null> {
|
|
1635
1935
|
const payload = await emitForegroundResultIntercom({
|
|
1636
1936
|
pi: input.pi,
|
|
@@ -1645,7 +1945,7 @@ async function maybeBuildForegroundIntercomReceipt(input: {
|
|
|
1645
1945
|
if (!payload) return null;
|
|
1646
1946
|
return {
|
|
1647
1947
|
text: formatSubagentResultReceipt({ mode: input.mode, runId: input.runId, payload }),
|
|
1648
|
-
details: stripDetailsOutputsForIntercomReceipt(input.details),
|
|
1948
|
+
details: input.preserveDetailsOutputs ? input.details : stripDetailsOutputsForIntercomReceipt(input.details),
|
|
1649
1949
|
};
|
|
1650
1950
|
}
|
|
1651
1951
|
|
|
@@ -1842,6 +2142,19 @@ function getRequestedModeLabel(params: SubagentParamsLike): Details["mode"] {
|
|
|
1842
2142
|
return "single";
|
|
1843
2143
|
}
|
|
1844
2144
|
|
|
2145
|
+
function formatStatusTargetLabel(params: Pick<SubagentParamsLike, "dir" | "index" | "view">, targetRunId: string | undefined): string {
|
|
2146
|
+
let target: string;
|
|
2147
|
+
if (targetRunId) {
|
|
2148
|
+
target = `run ${targetRunId}`;
|
|
2149
|
+
} else if (params.dir) {
|
|
2150
|
+
target = `dir ${params.dir}`;
|
|
2151
|
+
} else {
|
|
2152
|
+
target = params.view === "transcript" ? "active run" : "active runs";
|
|
2153
|
+
}
|
|
2154
|
+
if (params.view !== "transcript") return `Status target: ${target}`;
|
|
2155
|
+
return `Transcript target: ${target}${params.index !== undefined ? ` · child ${params.index}` : ""}`;
|
|
2156
|
+
}
|
|
2157
|
+
|
|
1845
2158
|
interface AgentDefaultContextPolicy {
|
|
1846
2159
|
params: SubagentParamsLike;
|
|
1847
2160
|
contextForAgent(agentName: string): ContextMode;
|
|
@@ -1925,7 +2238,38 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
|
|
|
1925
2238
|
|
|
1926
2239
|
export const DEFAULT_FOREGROUND_TIMEOUT_MS = 30 * 60 * 1000;
|
|
1927
2240
|
|
|
1928
|
-
|
|
2241
|
+
// Async single-agent runs also need a wall-clock backstop: a child whose bash
|
|
2242
|
+
// tool blocks forever (e.g. a background process inheriting the terminal with
|
|
2243
|
+
// no bash `timeout` arg) would otherwise hang the parent indefinitely with
|
|
2244
|
+
// zero signal. Same generous default as foreground; explicit timeoutMs/
|
|
2245
|
+
// maxRuntimeMs and agent-level defaultTimeoutMs remain authoritative.
|
|
2246
|
+
//
|
|
2247
|
+
// Deliberately NOT applied at the workflow level: async scripted workflows
|
|
2248
|
+
// stay unbounded as a whole, while each runner child has its own deadline.
|
|
2249
|
+
export { DEFAULT_ASYNC_TIMEOUT_MS };
|
|
2250
|
+
|
|
2251
|
+
/**
|
|
2252
|
+
* Maximum delay a Node.js timer accepts. Values above the 32-bit signed integer
|
|
2253
|
+
* ceiling overflow `setTimeout`, which silently clamps the delay to ~1ms and
|
|
2254
|
+
* fires almost immediately — so a run configured with a larger deadline would
|
|
2255
|
+
* terminate right away while reporting the long duration. Any timeout destined
|
|
2256
|
+
* for a timer must stay within this bound.
|
|
2257
|
+
*/
|
|
2258
|
+
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
2259
|
+
|
|
2260
|
+
/**
|
|
2261
|
+
* Resolve the optional global default runtime deadline from extension config
|
|
2262
|
+
* (`config.timeoutMs`). Returns undefined for unset or invalid values so callers
|
|
2263
|
+
* fall back to the built-in defaults. "Invalid" covers non-positive-integer
|
|
2264
|
+
* values and values above `MAX_TIMER_DELAY_MS`; the latter would overflow the
|
|
2265
|
+
* Node.js timer and expire the run almost immediately instead of running long.
|
|
2266
|
+
*/
|
|
2267
|
+
export function resolveConfigDefaultTimeoutMs(raw: unknown): number | undefined {
|
|
2268
|
+
if (typeof raw !== "number" || !Number.isInteger(raw) || raw <= 0 || raw > MAX_TIMER_DELAY_MS) return undefined;
|
|
2269
|
+
return raw;
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
export function resolveForegroundTimeout(params: SubagentParamsLike, defaultTimeoutMs?: number): { timeoutMs?: number; error?: string } {
|
|
1929
2273
|
const rawTimeout = params.timeoutMs;
|
|
1930
2274
|
const rawMaxRuntime = params.maxRuntimeMs;
|
|
1931
2275
|
if (rawTimeout === undefined && rawMaxRuntime === undefined) {
|
|
@@ -1944,6 +2288,25 @@ function resolveForegroundTimeout(params: SubagentParamsLike, defaultTimeoutMs?:
|
|
|
1944
2288
|
return timeoutMs === undefined ? {} : { timeoutMs };
|
|
1945
2289
|
}
|
|
1946
2290
|
|
|
2291
|
+
/**
|
|
2292
|
+
* Resolve the effective launch timeout for a single-agent run, applying the
|
|
2293
|
+
* async/foreground default when neither the caller nor the agent set one.
|
|
2294
|
+
*
|
|
2295
|
+
* A global config default (`config.timeoutMs`, passed as `configDefaultTimeoutMs`)
|
|
2296
|
+
* replaces the built-in 30-minute backstop wherever a concrete default is applied.
|
|
2297
|
+
* The async default is deliberately applied only to plain single-agent launches.
|
|
2298
|
+
* Composite launches keep their top-level execution unbounded when no timeout is
|
|
2299
|
+
* set — even with a config default — while their runner children resolve separate
|
|
2300
|
+
* deadlines. Exported so the executor wiring is directly testable.
|
|
2301
|
+
*/
|
|
2302
|
+
export function resolveSingleAgentLaunchTimeout(params: SubagentParamsLike, async: boolean, configDefaultTimeoutMs?: number): { timeoutMs?: number; error?: string } {
|
|
2303
|
+
const isComposite = (params.chain?.length ?? 0) > 0 || (params.tasks?.length ?? 0) > 0 || params.workflowScript !== undefined;
|
|
2304
|
+
const foregroundDefault = configDefaultTimeoutMs ?? DEFAULT_FOREGROUND_TIMEOUT_MS;
|
|
2305
|
+
const asyncSingleDefault = configDefaultTimeoutMs ?? DEFAULT_ASYNC_TIMEOUT_MS;
|
|
2306
|
+
const defaultTimeoutMs = !async ? foregroundDefault : isComposite ? undefined : asyncSingleDefault;
|
|
2307
|
+
return resolveForegroundTimeout(params, defaultTimeoutMs);
|
|
2308
|
+
}
|
|
2309
|
+
|
|
1947
2310
|
function resolveToolBudget(
|
|
1948
2311
|
raw: unknown,
|
|
1949
2312
|
label = "toolBudget",
|
|
@@ -2353,7 +2716,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2353
2716
|
details: { mode: "single" as const, results: [] },
|
|
2354
2717
|
};
|
|
2355
2718
|
}
|
|
2356
|
-
const id =
|
|
2719
|
+
const id = data.asyncRunId;
|
|
2357
2720
|
const parentModel = data.parentModel;
|
|
2358
2721
|
const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
|
|
2359
2722
|
pi: deps.pi,
|
|
@@ -2407,6 +2770,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2407
2770
|
shareEnabled,
|
|
2408
2771
|
sessionRoot,
|
|
2409
2772
|
chainSkills: [],
|
|
2773
|
+
activeAsyncCapacity: data.activeAsyncCapacity,
|
|
2410
2774
|
sessionFilesByFlatIndex: params.tasks.map((task, index) => sessionFileForTask(task.agent, index, task.model)),
|
|
2411
2775
|
thinkingOverridesByFlatIndex: params.tasks.map((task, index) => thinkingOverrideForTask(task.agent, index, task.model)),
|
|
2412
2776
|
contextForAgent: contextPolicy.contextForAgent,
|
|
@@ -2426,6 +2790,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2426
2790
|
usageBudget: data.usageBudget,
|
|
2427
2791
|
configToolBudget: data.configToolBudget,
|
|
2428
2792
|
capabilityCeiling: data.capabilityCeiling,
|
|
2793
|
+
runFanoutBudget: data.runFanoutBudget,
|
|
2429
2794
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2430
2795
|
parentWorkflowRunId: params.workflowParentRunId,
|
|
2431
2796
|
workflowKey: params.workflowKey,
|
|
@@ -2451,6 +2816,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2451
2816
|
shareEnabled,
|
|
2452
2817
|
sessionRoot,
|
|
2453
2818
|
chainSkills,
|
|
2819
|
+
activeAsyncCapacity: data.activeAsyncCapacity,
|
|
2454
2820
|
sessionFilesByFlatIndex: collectChainSessionFiles(chain, sessionFileForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
2455
2821
|
thinkingOverridesByFlatIndex: collectChainThinkingOverrides(chain, thinkingOverrideForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
2456
2822
|
contextForAgent: contextPolicy.contextForAgent,
|
|
@@ -2471,6 +2837,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2471
2837
|
usageBudget: data.usageBudget,
|
|
2472
2838
|
configToolBudget: data.configToolBudget,
|
|
2473
2839
|
capabilityCeiling: data.capabilityCeiling,
|
|
2840
|
+
runFanoutBudget: data.runFanoutBudget,
|
|
2474
2841
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2475
2842
|
parentWorkflowRunId: params.workflowParentRunId,
|
|
2476
2843
|
workflowKey: params.workflowKey,
|
|
@@ -2492,7 +2859,12 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2492
2859
|
const normalizedSkills = normalizeSkillInput(params.skill);
|
|
2493
2860
|
const skills = normalizedSkills === false ? [] : normalizedSkills;
|
|
2494
2861
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, a.maxSubagentDepth);
|
|
2495
|
-
const
|
|
2862
|
+
const externalRunnerWithoutExplicitModel = a.runner?.type === "external-cli"
|
|
2863
|
+
&& params.model === undefined
|
|
2864
|
+
&& (a.model === undefined || (a.modelSource?.type === "subagents.defaultModel" && a.model === a.modelSource.model));
|
|
2865
|
+
const modelOverride = externalRunnerWithoutExplicitModel
|
|
2866
|
+
? undefined
|
|
2867
|
+
: resolveEffectiveSubagentModel(params.model as string | undefined, a.model, parentModel, availableModels, currentProvider, data.modelScope === undefined ? {} : { scope: data.modelScope });
|
|
2496
2868
|
return executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
2497
2869
|
agent: params.agent!,
|
|
2498
2870
|
task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
|
|
@@ -2505,15 +2877,17 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2505
2877
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
2506
2878
|
artifactConfig,
|
|
2507
2879
|
shareEnabled,
|
|
2880
|
+
activeAsyncCapacity: data.activeAsyncCapacity,
|
|
2508
2881
|
sessionRoot,
|
|
2509
2882
|
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride),
|
|
2510
2883
|
context: contextPolicy.contextForAgent(params.agent!),
|
|
2511
2884
|
skills,
|
|
2512
2885
|
output: effectiveOutput,
|
|
2513
2886
|
outputMode: effectiveOutputMode,
|
|
2887
|
+
...(params.reads !== undefined ? { reads: params.reads } : {}),
|
|
2514
2888
|
outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
|
|
2515
2889
|
modelOverride,
|
|
2516
|
-
thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
|
|
2890
|
+
thinkingOverride: externalRunnerWithoutExplicitModel ? undefined : thinkingOverrideForTask(params.agent!, 0, modelOverride),
|
|
2517
2891
|
maxSubagentDepth,
|
|
2518
2892
|
waitToolEnabled: deps.waitToolEnabled,
|
|
2519
2893
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
@@ -2532,6 +2906,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2532
2906
|
usageBudget: data.usageBudget,
|
|
2533
2907
|
configToolBudget: data.configToolBudget,
|
|
2534
2908
|
capabilityCeiling: data.capabilityCeiling,
|
|
2909
|
+
runFanoutBudget: data.runFanoutBudget,
|
|
2535
2910
|
parentWorkflowRunId: params.workflowParentRunId,
|
|
2536
2911
|
workflowKey: params.workflowKey,
|
|
2537
2912
|
}));
|
|
@@ -2622,6 +2997,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2622
2997
|
permissions: deps.config.permissions,
|
|
2623
2998
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2624
2999
|
capabilityCeiling: data.capabilityCeiling,
|
|
3000
|
+
runFanoutBudget: data.runFanoutBudget,
|
|
2625
3001
|
}));
|
|
2626
3002
|
|
|
2627
3003
|
if (chainResult.requestedAsync) {
|
|
@@ -2632,7 +3008,9 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2632
3008
|
details: { mode: "chain" as const, results: [] },
|
|
2633
3009
|
};
|
|
2634
3010
|
}
|
|
2635
|
-
const id =
|
|
3011
|
+
const id = data.asyncRunId;
|
|
3012
|
+
const capacity = acquireClarifiedBackgroundCapacity(data, deps, "chain", id);
|
|
3013
|
+
if (capacity.errorResult) return capacity.errorResult;
|
|
2636
3014
|
const parentModel = data.parentModel;
|
|
2637
3015
|
const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
|
|
2638
3016
|
pi: deps.pi,
|
|
@@ -2648,42 +3026,48 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2648
3026
|
const rawAsyncChain = chainResult.requestedAsync.chain;
|
|
2649
3027
|
const asyncChain = wrapChainTasksForFork(rawAsyncChain, contextPolicy);
|
|
2650
3028
|
const firstAgent = firstChainAgent(rawAsyncChain);
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
3029
|
+
try {
|
|
3030
|
+
return executeAsyncChain(id, compactOptional<Parameters<typeof executeAsyncChain>[1]>({
|
|
3031
|
+
chain: asyncChain,
|
|
3032
|
+
task: params.task,
|
|
3033
|
+
goal: resolveAsyncEventGoal(params.task, rawAsyncChain, firstAgent ? shouldForkAgent(contextPolicy, firstAgent) : false),
|
|
3034
|
+
agents,
|
|
3035
|
+
ctx: asyncCtx,
|
|
3036
|
+
availableModels: ctx.modelRegistry.getAvailable().map(toModelInfo),
|
|
3037
|
+
cwd: effectiveCwd,
|
|
3038
|
+
maxOutput: params.maxOutput,
|
|
3039
|
+
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
3040
|
+
artifactConfig,
|
|
3041
|
+
shareEnabled,
|
|
3042
|
+
sessionRoot,
|
|
3043
|
+
chainSkills: chainResult.requestedAsync.chainSkills,
|
|
3044
|
+
activeAsyncCapacity: capacity.activeAsyncCapacity,
|
|
3045
|
+
sessionFilesByFlatIndex: collectChainSessionFiles(asyncChain, sessionFileForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
3046
|
+
thinkingOverridesByFlatIndex: collectChainThinkingOverrides(asyncChain, thinkingOverrideForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
3047
|
+
contextForAgent: contextPolicy.contextForAgent,
|
|
3048
|
+
dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
|
|
3049
|
+
maxSubagentDepth: currentMaxSubagentDepth,
|
|
3050
|
+
waitToolEnabled: deps.waitToolEnabled,
|
|
3051
|
+
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
3052
|
+
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
3053
|
+
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
3054
|
+
controlConfig,
|
|
3055
|
+
agentContract: params.agentContract,
|
|
3056
|
+
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
3057
|
+
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
3058
|
+
nestedRoute: data.nestedRoute,
|
|
3059
|
+
timeoutMs: data.timeoutMs,
|
|
3060
|
+
turnBudget: data.turnBudget,
|
|
3061
|
+
toolBudget: data.toolBudget,
|
|
3062
|
+
usageBudget: data.usageBudget,
|
|
3063
|
+
configToolBudget: data.configToolBudget,
|
|
3064
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
3065
|
+
runFanoutBudget: data.runFanoutBudget,
|
|
3066
|
+
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
3067
|
+
}));
|
|
3068
|
+
} finally {
|
|
3069
|
+
finalizeClarifiedBackgroundCapacity(data, deps, capacity.activeAsyncCapacity);
|
|
3070
|
+
}
|
|
2687
3071
|
}
|
|
2688
3072
|
|
|
2689
3073
|
const rawChainDetails = chainResult.details ? compactOptional<Details>({ ...chainResult.details, runId, timeoutMs: data.timeoutMs }) : undefined;
|
|
@@ -2728,6 +3112,7 @@ interface ForegroundParallelRunInput {
|
|
|
2728
3112
|
intercomEvents: IntercomEventBus;
|
|
2729
3113
|
parentSessionId: string | null;
|
|
2730
3114
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
3115
|
+
runFanoutBudget: RunFanoutBudgetDescriptor;
|
|
2731
3116
|
signal: AbortSignal;
|
|
2732
3117
|
runId: string;
|
|
2733
3118
|
sessionDirForIndex: (idx?: number) => string | undefined;
|
|
@@ -2777,6 +3162,48 @@ function buildParallelModeError(message: string): AgentToolResult<Details> {
|
|
|
2777
3162
|
};
|
|
2778
3163
|
}
|
|
2779
3164
|
|
|
3165
|
+
function acquireClarifiedBackgroundCapacity(
|
|
3166
|
+
data: ExecutionContextData,
|
|
3167
|
+
deps: ExecutorDeps,
|
|
3168
|
+
mode: "single" | "parallel" | "chain",
|
|
3169
|
+
asyncRunId: string,
|
|
3170
|
+
): { activeAsyncCapacity?: ActiveAsyncCapacityHandle; errorResult?: AgentToolResult<Details> } {
|
|
3171
|
+
if (!data.topLevelAsyncCapacityEligible || !data.parentSessionId) return {};
|
|
3172
|
+
const liveWorkflowRunIds = new Set(deps.state.workflowControllers?.keys() ?? []);
|
|
3173
|
+
const activeLimit = resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession);
|
|
3174
|
+
try {
|
|
3175
|
+
const activeAsyncCapacity = acquireActiveAsyncCapacity({
|
|
3176
|
+
sessionId: data.parentSessionId,
|
|
3177
|
+
limit: activeLimit,
|
|
3178
|
+
runId: asyncRunId,
|
|
3179
|
+
kind: "runner",
|
|
3180
|
+
asyncDir: path.join(DIRS.async, asyncRunId),
|
|
3181
|
+
}, { liveWorkflowRunIds });
|
|
3182
|
+
deps.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(data.parentSessionId, activeLimit, { liveWorkflowRunIds });
|
|
3183
|
+
return { activeAsyncCapacity };
|
|
3184
|
+
} catch (error) {
|
|
3185
|
+
if (error instanceof ActiveAsyncCapacityError) {
|
|
3186
|
+
deps.state.activeAsyncCapacity = error.snapshot;
|
|
3187
|
+
return { errorResult: { content: [{ type: "text", text: error.message }], isError: true, details: { mode, results: [], activeAsyncCapacity: error.snapshot } } };
|
|
3188
|
+
}
|
|
3189
|
+
throw error;
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
function finalizeClarifiedBackgroundCapacity(
|
|
3194
|
+
data: ExecutionContextData,
|
|
3195
|
+
deps: ExecutorDeps,
|
|
3196
|
+
activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined,
|
|
3197
|
+
): void {
|
|
3198
|
+
if (!activeAsyncCapacity || !data.parentSessionId) return;
|
|
3199
|
+
if (!activeAsyncCapacity.owner.runnerStartedAt) activeAsyncCapacity.rollback();
|
|
3200
|
+
deps.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(
|
|
3201
|
+
data.parentSessionId,
|
|
3202
|
+
resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession),
|
|
3203
|
+
{ liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []) },
|
|
3204
|
+
);
|
|
3205
|
+
}
|
|
3206
|
+
|
|
2780
3207
|
function createParallelWorktreeSetup(
|
|
2781
3208
|
enabled: boolean | undefined,
|
|
2782
3209
|
cwd: string,
|
|
@@ -2965,7 +3392,33 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2965
3392
|
beginForegroundChild(input.foregroundControl, {
|
|
2966
3393
|
index,
|
|
2967
3394
|
agent: task.agent,
|
|
2968
|
-
|
|
3395
|
+
authoredTask: input.taskDescriptions[index] ?? "",
|
|
3396
|
+
effectivePrompt: taskText,
|
|
3397
|
+
cwd: taskCwd,
|
|
3398
|
+
...(outputPath ? { outputPath } : {}),
|
|
3399
|
+
rerun: { params: compactOptional<Record<string, unknown>>({
|
|
3400
|
+
agent: task.agent,
|
|
3401
|
+
task: input.taskDescriptions[index] ?? "",
|
|
3402
|
+
async: false,
|
|
3403
|
+
cwd: input.worktreeSetup ? undefined : taskCwd,
|
|
3404
|
+
worktree: input.worktreeSetup ? true : false,
|
|
3405
|
+
context: input.contextPolicy.contextForAgent(task.agent),
|
|
3406
|
+
share: input.shareEnabled || undefined,
|
|
3407
|
+
maxOutput: input.maxOutput,
|
|
3408
|
+
timeoutMs: input.timeoutMs,
|
|
3409
|
+
turnBudget: input.turnBudget,
|
|
3410
|
+
...(model ? { model } : {}),
|
|
3411
|
+
...(effectiveSkills !== undefined ? { skill: effectiveSkills } : {}),
|
|
3412
|
+
...(outputPath ? { output: outputPath } : {}),
|
|
3413
|
+
...(behavior?.outputMode !== undefined ? { outputMode: behavior.outputMode } : {}),
|
|
3414
|
+
...(behavior?.reads !== undefined ? { reads: behavior.reads } : {}),
|
|
3415
|
+
...(behavior?.progress !== undefined ? { progress: behavior.progress } : {}),
|
|
3416
|
+
...(task.toolBudget !== undefined ? { toolBudget: task.toolBudget } : {}),
|
|
3417
|
+
...(task.outputSchema !== undefined ? { outputSchema: task.outputSchema } : {}),
|
|
3418
|
+
...(task.acceptance !== undefined ? { acceptance: task.acceptance } : {}),
|
|
3419
|
+
...(task.agentContract !== undefined || input.agentContract !== undefined ? { agentContract: task.agentContract ?? input.agentContract } : {}),
|
|
3420
|
+
}) },
|
|
3421
|
+
description: `${task.agent} child`,
|
|
2969
3422
|
...(model ? { model } : {}),
|
|
2970
3423
|
...(thinking ? { thinking } : {}),
|
|
2971
3424
|
interrupt: () => {
|
|
@@ -2982,7 +3435,10 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2982
3435
|
const result = await runSync(input.ctx.cwd, input.agents, task.agent, taskText, compactOptional<Parameters<typeof runSync>[4]>({
|
|
2983
3436
|
permissions: input.permissions,
|
|
2984
3437
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
3438
|
+
llmIntentArbiter: createTaskMutationArbiter(input.ctx),
|
|
3439
|
+
...workflowForegroundSteeringLaunchOptions(input.foregroundControl, index),
|
|
2985
3440
|
context: input.contextPolicy.contextForAgent(task.agent),
|
|
3441
|
+
runFanoutBudget: { ...input.runFanoutBudget, parentPath: `${input.runFanoutBudget.parentPath ? `${input.runFanoutBudget.parentPath}/` : ""}tasks[${index}]` },
|
|
2986
3442
|
cwd: taskCwd,
|
|
2987
3443
|
signal: input.signal,
|
|
2988
3444
|
interruptSignal: interruptController.signal,
|
|
@@ -3027,6 +3483,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
3027
3483
|
agentContract: task.agentContract ?? input.agentContract,
|
|
3028
3484
|
acceptance: task.acceptance,
|
|
3029
3485
|
acceptanceContext: { mode: "parallel" },
|
|
3486
|
+
onEffectivePrompt: input.foregroundControl ? (prompt) => updateLiveEffectivePrompt(input.foregroundControl!, index, prompt) : undefined,
|
|
3030
3487
|
timeoutMs: input.timeoutMs,
|
|
3031
3488
|
deadlineAt: input.deadlineAt,
|
|
3032
3489
|
turnBudget: input.turnBudget,
|
|
@@ -3210,7 +3667,9 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3210
3667
|
details: { mode: "parallel" as const, results: [] },
|
|
3211
3668
|
};
|
|
3212
3669
|
}
|
|
3213
|
-
const id =
|
|
3670
|
+
const id = data.asyncRunId;
|
|
3671
|
+
const capacity = acquireClarifiedBackgroundCapacity(data, deps, "parallel", id);
|
|
3672
|
+
if (capacity.errorResult) return capacity.errorResult;
|
|
3214
3673
|
const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
|
|
3215
3674
|
pi: deps.pi,
|
|
3216
3675
|
cwd: ctx.cwd,
|
|
@@ -3241,40 +3700,46 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3241
3700
|
...(t.agentContract !== undefined ? { agentContract: t.agentContract } : {}),
|
|
3242
3701
|
});
|
|
3243
3702
|
});
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3703
|
+
try {
|
|
3704
|
+
return executeAsyncChain(id, compactOptional<Parameters<typeof executeAsyncChain>[1]>({
|
|
3705
|
+
chain: [compactOptional<ParallelStep>({ parallel: parallelTasks, concurrency: parallelConcurrency, worktree: params.worktree })],
|
|
3706
|
+
resultMode: "parallel",
|
|
3707
|
+
goal: taskTexts[0] ?? "",
|
|
3708
|
+
agents,
|
|
3709
|
+
ctx: asyncCtx,
|
|
3710
|
+
availableModels,
|
|
3711
|
+
cwd: effectiveCwd,
|
|
3712
|
+
maxOutput: params.maxOutput,
|
|
3713
|
+
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
3714
|
+
artifactConfig,
|
|
3715
|
+
shareEnabled,
|
|
3716
|
+
sessionRoot,
|
|
3717
|
+
chainSkills: [],
|
|
3718
|
+
activeAsyncCapacity: capacity.activeAsyncCapacity,
|
|
3719
|
+
sessionFilesByFlatIndex: tasks.map((task, index) => sessionFileForTask(task.agent, index, modelOverrides[index])),
|
|
3720
|
+
thinkingOverridesByFlatIndex: tasks.map((task, index) => thinkingOverrideForTask(task.agent, index, modelOverrides[index])),
|
|
3721
|
+
contextForAgent: contextPolicy.contextForAgent,
|
|
3722
|
+
maxSubagentDepth: currentMaxSubagentDepth,
|
|
3723
|
+
waitToolEnabled: deps.waitToolEnabled,
|
|
3724
|
+
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
3725
|
+
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
3726
|
+
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
3727
|
+
controlConfig,
|
|
3728
|
+
agentContract: params.agentContract,
|
|
3729
|
+
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
3730
|
+
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
3731
|
+
nestedRoute: data.nestedRoute,
|
|
3732
|
+
timeoutMs: data.timeoutMs,
|
|
3733
|
+
turnBudget: data.turnBudget,
|
|
3734
|
+
usageBudget: data.usageBudget,
|
|
3735
|
+
toolBudget: data.toolBudget,
|
|
3736
|
+
configToolBudget: data.configToolBudget,
|
|
3737
|
+
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
3738
|
+
runFanoutBudget: data.runFanoutBudget,
|
|
3739
|
+
}));
|
|
3740
|
+
} finally {
|
|
3741
|
+
finalizeClarifiedBackgroundCapacity(data, deps, capacity.activeAsyncCapacity);
|
|
3742
|
+
}
|
|
3278
3743
|
}
|
|
3279
3744
|
}
|
|
3280
3745
|
|
|
@@ -3300,10 +3765,11 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3300
3765
|
);
|
|
3301
3766
|
if (errorResult) return errorResult;
|
|
3302
3767
|
|
|
3303
|
-
let
|
|
3768
|
+
let worktreeCleanupHandled = false;
|
|
3769
|
+
let pendingHandoff: Details["parallelHandoff"];
|
|
3304
3770
|
try {
|
|
3305
3771
|
if (worktreeSetup) {
|
|
3306
|
-
writePendingParallelHandoff({
|
|
3772
|
+
pendingHandoff = writePendingParallelHandoff({
|
|
3307
3773
|
manifestPath: parallelHandoffPath(artifactsDir, runId),
|
|
3308
3774
|
runId,
|
|
3309
3775
|
mode: "parallel",
|
|
@@ -3352,6 +3818,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3352
3818
|
intercomEvents: deps.pi.events,
|
|
3353
3819
|
parentSessionId: data.parentSessionId,
|
|
3354
3820
|
capabilityCeiling: data.capabilityCeiling,
|
|
3821
|
+
runFanoutBudget: data.runFanoutBudget,
|
|
3355
3822
|
signal,
|
|
3356
3823
|
runId,
|
|
3357
3824
|
sessionDirForIndex,
|
|
@@ -3406,10 +3873,13 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3406
3873
|
updateForegroundNestedProjection(foregroundControl);
|
|
3407
3874
|
attachRootChildrenToSteps(runId, results, foregroundControl.nestedChildren);
|
|
3408
3875
|
}
|
|
3876
|
+
const detached = results.find((result) => result.detached);
|
|
3409
3877
|
let handoff: ReturnType<typeof finalizeParallelWorktreeHandoff> | undefined;
|
|
3410
3878
|
if (worktreeSetup) {
|
|
3411
|
-
|
|
3412
|
-
handoff =
|
|
3879
|
+
worktreeCleanupHandled = true;
|
|
3880
|
+
handoff = detached
|
|
3881
|
+
? { suffix: pendingHandoff ? formatParallelHandoffReference(pendingHandoff) : "", reference: pendingHandoff }
|
|
3882
|
+
: finalizeParallelWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: effectiveCwd, tasks, results });
|
|
3413
3883
|
}
|
|
3414
3884
|
const interrupted = results.find((result) => result.interrupted);
|
|
3415
3885
|
const totalCost = sumResultsCost(results);
|
|
@@ -3432,11 +3902,10 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3432
3902
|
details,
|
|
3433
3903
|
};
|
|
3434
3904
|
}
|
|
3435
|
-
const detachedIndex = results.findIndex((result) => result.detached);
|
|
3436
|
-
const detached = detachedIndex >= 0 ? results[detachedIndex] : undefined;
|
|
3437
3905
|
if (detached) {
|
|
3906
|
+
const handoffSuffix = handoff?.suffix ? `\n\n${handoff.suffix}` : "";
|
|
3438
3907
|
return {
|
|
3439
|
-
content: [{ type: "text", text: `Parallel run detached for intercom coordination (${detached.agent}). Reply to the supervisor request first, then wait with subagent_wait({ id: "${runId}" }). Use subagent({ action: "status", id: "${runId}" }) to recover the result; do not resume or launch a replacement while it remains detached
|
|
3908
|
+
content: [{ type: "text", text: `Parallel run detached for intercom coordination (${detached.agent}). Reply to the supervisor request first, then wait with subagent_wait({ id: "${runId}" }). Use subagent({ action: "status", id: "${runId}" }) to recover the result; do not resume or launch a replacement while it remains detached.${handoffSuffix}` }],
|
|
3440
3909
|
details,
|
|
3441
3910
|
};
|
|
3442
3911
|
}
|
|
@@ -3450,6 +3919,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3450
3919
|
runId,
|
|
3451
3920
|
mode: "parallel",
|
|
3452
3921
|
details,
|
|
3922
|
+
...(params.workflowParentRunId !== undefined ? { preserveDetailsOutputs: true } : {}),
|
|
3453
3923
|
...(foregroundControl?.nestedChildren?.length ? { nestedChildren: foregroundControl.nestedChildren } : {}),
|
|
3454
3924
|
});
|
|
3455
3925
|
if (intercomReceipt) {
|
|
@@ -3484,7 +3954,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3484
3954
|
details,
|
|
3485
3955
|
};
|
|
3486
3956
|
} finally {
|
|
3487
|
-
if (worktreeSetup && !
|
|
3957
|
+
if (worktreeSetup && !worktreeCleanupHandled) cleanupWorktrees(worktreeSetup);
|
|
3488
3958
|
}
|
|
3489
3959
|
}
|
|
3490
3960
|
|
|
@@ -3535,6 +4005,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3535
4005
|
data.modelScope === undefined ? {} : { scope: data.modelScope },
|
|
3536
4006
|
);
|
|
3537
4007
|
let skillOverride: string[] | false | undefined = normalizeSkillInput(params.skill);
|
|
4008
|
+
let readsOverride: string[] | false | undefined = params.reads;
|
|
3538
4009
|
const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
|
|
3539
4010
|
let effectiveOutput = normalizeSingleOutputOverride(rawOutput, agentConfig.output);
|
|
3540
4011
|
const effectiveOutputMode = params.outputMode ?? "inline";
|
|
@@ -3572,6 +4043,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3572
4043
|
if (override?.model !== undefined) modelOverride = resolveEffectiveSubagentModel(override.model, agentConfig.model, parentModel, availableModels, currentProvider, data.modelScope === undefined ? {} : { scope: data.modelScope });
|
|
3573
4044
|
if (override?.output !== undefined) effectiveOutput = normalizeSingleOutputOverride(override.output, agentConfig.output);
|
|
3574
4045
|
if (override?.skills !== undefined) skillOverride = override.skills;
|
|
4046
|
+
if (override?.reads !== undefined) readsOverride = override.reads;
|
|
3575
4047
|
|
|
3576
4048
|
if (result.runInBackground) {
|
|
3577
4049
|
if (!isAsyncAvailable()) {
|
|
@@ -3581,7 +4053,9 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3581
4053
|
details: { mode: "single" as const, results: [] },
|
|
3582
4054
|
};
|
|
3583
4055
|
}
|
|
3584
|
-
const id =
|
|
4056
|
+
const id = data.asyncRunId;
|
|
4057
|
+
const capacity = acquireClarifiedBackgroundCapacity(data, deps, "single", id);
|
|
4058
|
+
if (capacity.errorResult) return capacity.errorResult;
|
|
3585
4059
|
const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
|
|
3586
4060
|
pi: deps.pi,
|
|
3587
4061
|
cwd: ctx.cwd,
|
|
@@ -3593,48 +4067,56 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3593
4067
|
interactive: ctx.hasUI,
|
|
3594
4068
|
permissions: deps.config.permissions,
|
|
3595
4069
|
});
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
4070
|
+
try {
|
|
4071
|
+
return executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
4072
|
+
agent: params.agent!,
|
|
4073
|
+
task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(task) : task,
|
|
4074
|
+
goal: task,
|
|
4075
|
+
agentConfig,
|
|
4076
|
+
ctx: asyncCtx,
|
|
4077
|
+
availableModels,
|
|
4078
|
+
cwd: effectiveCwd,
|
|
4079
|
+
maxOutput: params.maxOutput,
|
|
4080
|
+
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
4081
|
+
artifactConfig,
|
|
4082
|
+
shareEnabled,
|
|
4083
|
+
activeAsyncCapacity: capacity.activeAsyncCapacity,
|
|
4084
|
+
sessionRoot,
|
|
4085
|
+
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride),
|
|
4086
|
+
context: contextPolicy.contextForAgent(params.agent!),
|
|
4087
|
+
skills: skillOverride === false ? [] : skillOverride,
|
|
4088
|
+
output: effectiveOutput,
|
|
4089
|
+
outputMode: effectiveOutputMode,
|
|
4090
|
+
...(readsOverride !== undefined ? { reads: readsOverride } : {}),
|
|
4091
|
+
outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
|
|
4092
|
+
modelOverride,
|
|
4093
|
+
thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
|
|
4094
|
+
maxSubagentDepth,
|
|
4095
|
+
waitToolEnabled: deps.waitToolEnabled,
|
|
4096
|
+
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
4097
|
+
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
4098
|
+
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
4099
|
+
controlConfig,
|
|
4100
|
+
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
4101
|
+
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
4102
|
+
nestedRoute: data.nestedRoute,
|
|
4103
|
+
agentContract: params.agentContract,
|
|
4104
|
+
structuredOutputSchema: params.outputSchema,
|
|
4105
|
+
acceptance: params.acceptance,
|
|
4106
|
+
timeoutMs: data.timeoutMs,
|
|
4107
|
+
turnBudget: data.turnBudget,
|
|
4108
|
+
toolBudget: effectiveToolBudget.toolBudget,
|
|
4109
|
+
usageBudget: data.usageBudget,
|
|
4110
|
+
allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
|
|
4111
|
+
runFanoutBudget: data.runFanoutBudget,
|
|
4112
|
+
}));
|
|
4113
|
+
} finally {
|
|
4114
|
+
finalizeClarifiedBackgroundCapacity(data, deps, capacity.activeAsyncCapacity);
|
|
4115
|
+
}
|
|
3635
4116
|
}
|
|
3636
4117
|
}
|
|
3637
4118
|
|
|
4119
|
+
const authoredTask = task;
|
|
3638
4120
|
if (shouldForkAgent(contextPolicy, params.agent!)) {
|
|
3639
4121
|
task = wrapForkTask(task);
|
|
3640
4122
|
}
|
|
@@ -3647,6 +4129,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3647
4129
|
const structuredRuntime = params.outputSchema
|
|
3648
4130
|
? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ? path.join(artifactsDir, "structured-output", runId) : undefined)
|
|
3649
4131
|
: undefined;
|
|
4132
|
+
// Reads: caller override > agent defaultReads > none. `~`/`~/` expand to home;
|
|
4133
|
+
// absolute paths pass through; relative paths resolve against the child cwd.
|
|
4134
|
+
const reads = readsOverride !== undefined ? readsOverride : agentConfig.defaultReads ?? false;
|
|
4135
|
+
const readPaths = Array.isArray(reads) ? resolveExistingReadPaths(reads, effectiveCwd) : [];
|
|
4136
|
+
const readsInstruction = readPaths.length > 0
|
|
4137
|
+
? `[Read from: ${readPaths.join(", ")}]\n\n`
|
|
4138
|
+
: "";
|
|
4139
|
+
task = readsInstruction + task;
|
|
3650
4140
|
task = injectSingleOutputInstruction(task, outputPath, agentConfig);
|
|
3651
4141
|
|
|
3652
4142
|
let effectiveSkills: string[] | undefined;
|
|
@@ -3663,6 +4153,11 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3663
4153
|
beginForegroundChild(foregroundControl, omitUndefinedProperties({
|
|
3664
4154
|
index: 0,
|
|
3665
4155
|
agent: params.agent!,
|
|
4156
|
+
authoredTask,
|
|
4157
|
+
effectivePrompt: task,
|
|
4158
|
+
cwd: effectiveCwd,
|
|
4159
|
+
outputPath,
|
|
4160
|
+
rerun: { params: { ...params, task: authoredTask, async: params.async ?? false } },
|
|
3666
4161
|
description: foregroundControl.description,
|
|
3667
4162
|
...(modelOverride ? { model: modelOverride } : {}),
|
|
3668
4163
|
...(thinking ? { thinking } : {}),
|
|
@@ -3688,7 +4183,10 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3688
4183
|
r = await runSync(ctx.cwd, agents, params.agent!, task, compactOptional<Parameters<typeof runSync>[4]>({
|
|
3689
4184
|
permissions: deps.config.permissions,
|
|
3690
4185
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
4186
|
+
llmIntentArbiter: createTaskMutationArbiter(ctx),
|
|
4187
|
+
...workflowForegroundSteeringLaunchOptions(foregroundControl, 0),
|
|
3691
4188
|
context: data.contextPolicy.contextForAgent(params.agent!),
|
|
4189
|
+
runFanoutBudget: params.runFanoutAdmitted ? data.runFanoutBudget : { ...data.runFanoutBudget, parentPath: `${data.runFanoutBudget.parentPath ? `${data.runFanoutBudget.parentPath}/` : ""}single` },
|
|
3692
4190
|
cwd: effectiveCwd,
|
|
3693
4191
|
signal,
|
|
3694
4192
|
interruptSignal: interruptController.signal,
|
|
@@ -3722,6 +4220,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3722
4220
|
agentContract: params.agentContract,
|
|
3723
4221
|
acceptance: params.acceptance,
|
|
3724
4222
|
acceptanceContext: { mode: "single" },
|
|
4223
|
+
onEffectivePrompt: foregroundControl ? (prompt) => updateLiveEffectivePrompt(foregroundControl, 0, prompt) : undefined,
|
|
3725
4224
|
onDetachReady: (detach) => {
|
|
3726
4225
|
detachForeground = detach;
|
|
3727
4226
|
},
|
|
@@ -3770,12 +4269,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3770
4269
|
if (r.artifactPaths) allArtifactPaths.push(r.artifactPaths);
|
|
3771
4270
|
|
|
3772
4271
|
const fullOutput = getSingleResultOutput(r);
|
|
4272
|
+
const preserveRejectedSavedOutput = r.acceptance?.explicit && r.acceptance.status === "rejected" && r.savedOutputPath !== undefined;
|
|
3773
4273
|
const finalizedOutput = finalizeSingleOutput(omitUndefinedProperties({
|
|
3774
4274
|
fullOutput,
|
|
3775
4275
|
truncatedOutput: r.truncation?.text,
|
|
3776
4276
|
outputPath,
|
|
3777
4277
|
outputMode: r.outputMode,
|
|
3778
4278
|
exitCode: r.exitCode,
|
|
4279
|
+
preserveSavedOutput: preserveRejectedSavedOutput,
|
|
3779
4280
|
savedPath: r.savedOutputPath,
|
|
3780
4281
|
outputReference: r.outputReference,
|
|
3781
4282
|
saveError: r.outputSaveError,
|
|
@@ -3810,6 +4311,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3810
4311
|
runId,
|
|
3811
4312
|
mode: "single",
|
|
3812
4313
|
details,
|
|
4314
|
+
...(params.workflowParentRunId !== undefined ? { preserveDetailsOutputs: true } : {}),
|
|
3813
4315
|
...(foregroundControl?.nestedChildren?.length ? { nestedChildren: foregroundControl.nestedChildren } : {}),
|
|
3814
4316
|
});
|
|
3815
4317
|
if (intercomReceipt) {
|
|
@@ -3865,8 +4367,71 @@ function duplicateSubagentCallResult(params: SubagentParamsLike): AgentToolResul
|
|
|
3865
4367
|
};
|
|
3866
4368
|
}
|
|
3867
4369
|
|
|
4370
|
+
const workflowLaunchObservers = new WeakMap<object, (launch: { agent: string; sessionFile?: string; async: boolean; runId?: string }) => void>();
|
|
4371
|
+
|
|
4372
|
+
function recordMissionWorkflowChild(
|
|
4373
|
+
binding: MissionLaunchBinding | undefined,
|
|
4374
|
+
workflowRunId: string,
|
|
4375
|
+
key: string,
|
|
4376
|
+
update: Omit<MissionWorkflowChildUpdate, "workflowRunId" | "key">,
|
|
4377
|
+
): void {
|
|
4378
|
+
if (!binding) return;
|
|
4379
|
+
const { task: _task, ...durableUpdate } = update;
|
|
4380
|
+
try {
|
|
4381
|
+
updateMission(binding.location, binding.missionId, { upsertWorkflowChildren: [{ workflowRunId, key, ...durableUpdate }] });
|
|
4382
|
+
} catch (error) {
|
|
4383
|
+
console.warn(`[pi-subagents] Failed to record mission workflow child '${key}': ${error instanceof Error ? error.message : String(error)}`);
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
|
|
4387
|
+
export function missionWorkflowChildStatus(result: AgentToolResult<Details>): string {
|
|
4388
|
+
const childResults = result.details.results;
|
|
4389
|
+
if (childResults.some((child) => child.detached || child.interrupted)) return "paused";
|
|
4390
|
+
if (result.isError === true || childResults.some((child) => child.exitCode !== 0)) return "failed";
|
|
4391
|
+
if (childResults.length === 0 && (result.details.asyncId || result.details.asyncDir)) return "running";
|
|
4392
|
+
return "completed";
|
|
4393
|
+
}
|
|
4394
|
+
|
|
4395
|
+
export async function runMissionWorkflowChild(
|
|
4396
|
+
binding: MissionLaunchBinding | undefined,
|
|
4397
|
+
workflowRunId: string,
|
|
4398
|
+
key: string,
|
|
4399
|
+
phase: string | undefined,
|
|
4400
|
+
run: () => Promise<AgentToolResult<Details>>,
|
|
4401
|
+
): Promise<AgentToolResult<Details>> {
|
|
4402
|
+
try {
|
|
4403
|
+
return await run();
|
|
4404
|
+
} catch (error) {
|
|
4405
|
+
recordMissionWorkflowChild(binding, workflowRunId, key, {
|
|
4406
|
+
status: "failed",
|
|
4407
|
+
completedAt: new Date().toISOString(),
|
|
4408
|
+
heartbeat: { status: "failed", ...(phase ? { phase } : {}), message: error instanceof Error ? error.message : String(error) },
|
|
4409
|
+
});
|
|
4410
|
+
throw error;
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4413
|
+
|
|
4414
|
+
export function bindMissionWorkflowChildAsyncLaunch(
|
|
4415
|
+
params: SubagentParamsLike,
|
|
4416
|
+
binding: MissionLaunchBinding | undefined,
|
|
4417
|
+
asyncByDefault: boolean,
|
|
4418
|
+
asyncId: string = randomUUID(),
|
|
4419
|
+
): SubagentParamsLike {
|
|
4420
|
+
const requestedAsync = params.async ?? asyncByDefault;
|
|
4421
|
+
if (!binding || !requestedAsync || params.clarify === true) return params;
|
|
4422
|
+
const id = asyncId.trim();
|
|
4423
|
+
if (!id || path.basename(id) !== id) throw new Error("workflow child async id must be a single path segment");
|
|
4424
|
+
writeMissionAsyncBinding(path.join(DIRS.async, id), binding);
|
|
4425
|
+
return { ...params, workflowChildAsyncId: id };
|
|
4426
|
+
}
|
|
4427
|
+
|
|
3868
4428
|
function workflowChildResult(key: string, result: AgentToolResult<Details>): WorkflowScriptChildResult {
|
|
3869
|
-
const
|
|
4429
|
+
const receiptOutput = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n");
|
|
4430
|
+
const output = result.details.results.length === 1 && result.details.results[0]?.finalOutput !== undefined
|
|
4431
|
+
? result.details.results[0].finalOutput
|
|
4432
|
+
: receiptOutput;
|
|
4433
|
+
const detached = result.details.results.some((child) => child.detached);
|
|
4434
|
+
const ok = result.isError !== true && !detached;
|
|
3870
4435
|
const artifactPaths = new Set<string>();
|
|
3871
4436
|
if (result.details.asyncDir) artifactPaths.add(result.details.asyncDir);
|
|
3872
4437
|
if (result.details.parallelHandoff?.path) artifactPaths.add(result.details.parallelHandoff.path);
|
|
@@ -3876,18 +4441,76 @@ function workflowChildResult(key: string, result: AgentToolResult<Details>): Wor
|
|
|
3876
4441
|
if (child.sessionFile) artifactPaths.add(child.sessionFile);
|
|
3877
4442
|
}
|
|
3878
4443
|
const structured = result.details.results.map((child) => child.structuredOutput).filter((value) => value !== undefined);
|
|
4444
|
+
const resolvedAgents = [...new Set(result.details.results.map((child) => child.agent).filter((agent): agent is string => Boolean(agent)))];
|
|
3879
4445
|
return {
|
|
3880
4446
|
key,
|
|
3881
|
-
ok
|
|
4447
|
+
ok,
|
|
4448
|
+
...(resolvedAgents.length === 1 ? { agent: resolvedAgents[0] } : {}),
|
|
3882
4449
|
...(result.details.runId || result.details.asyncId ? { runId: result.details.runId ?? result.details.asyncId } : {}),
|
|
3883
4450
|
output,
|
|
3884
|
-
...(
|
|
4451
|
+
...(!ok ? { error: receiptOutput || output || "Child run failed." } : {}),
|
|
3885
4452
|
...(structured.length === 1 ? { structuredOutput: structured[0] } : structured.length > 1 ? { structuredOutput: structured } : {}),
|
|
3886
4453
|
artifactPaths: [...artifactPaths],
|
|
3887
4454
|
results: result.details.results,
|
|
3888
4455
|
};
|
|
3889
4456
|
}
|
|
3890
4457
|
|
|
4458
|
+
export function prepareWorkflowLaunchParams(
|
|
4459
|
+
workflowDefaults: SubagentParamsLike,
|
|
4460
|
+
childParams: Record<string, unknown>,
|
|
4461
|
+
parentWorkflowRunId: string,
|
|
4462
|
+
workflowKey: string,
|
|
4463
|
+
options: { missionDetached?: boolean; suppressRoutineResultIntercom?: boolean; runFanoutBudget?: RunFanoutBudgetDescriptor } = {},
|
|
4464
|
+
): SubagentParamsLike {
|
|
4465
|
+
if (typeof childParams.resume === "string") {
|
|
4466
|
+
if (childParams.gate !== undefined || workflowDefaults.gate !== undefined) {
|
|
4467
|
+
throw new Error("gate is not supported with retained resume; resume uses the retained child contract.");
|
|
4468
|
+
}
|
|
4469
|
+
const timeoutMs = childParams.timeoutMs ?? childParams.maxRuntimeMs ?? workflowDefaults.timeoutMs ?? workflowDefaults.maxRuntimeMs;
|
|
4470
|
+
const turnBudget = childParams.turnBudget ?? workflowDefaults.turnBudget;
|
|
4471
|
+
const toolBudget = childParams.toolBudget ?? workflowDefaults.toolBudget;
|
|
4472
|
+
return {
|
|
4473
|
+
action: "resume",
|
|
4474
|
+
id: childParams.resume.trim(),
|
|
4475
|
+
message: typeof childParams.task === "string" ? childParams.task.trim() : "",
|
|
4476
|
+
workflowParentRunId: parentWorkflowRunId,
|
|
4477
|
+
workflowKey,
|
|
4478
|
+
...(options.missionDetached ? { mission: false } : {}),
|
|
4479
|
+
...(timeoutMs !== undefined ? { timeoutMs: timeoutMs as number } : {}),
|
|
4480
|
+
...(turnBudget !== undefined ? { turnBudget: turnBudget as TurnBudgetConfig } : {}),
|
|
4481
|
+
...(toolBudget !== undefined ? { toolBudget: toolBudget as ToolBudgetConfig } : {}),
|
|
4482
|
+
};
|
|
4483
|
+
}
|
|
4484
|
+
const launchParams = {
|
|
4485
|
+
...workflowDefaults,
|
|
4486
|
+
async: false,
|
|
4487
|
+
...childParams,
|
|
4488
|
+
...(options.missionDetached ? { mission: false } : {}),
|
|
4489
|
+
workflowParentRunId: parentWorkflowRunId,
|
|
4490
|
+
workflowKey,
|
|
4491
|
+
...(options.runFanoutBudget ? { runFanoutBudget: { ...options.runFanoutBudget, parentPath: `${options.runFanoutBudget.parentPath ? `${options.runFanoutBudget.parentPath}/` : ""}workflow[${workflowKey}]` } } : {}),
|
|
4492
|
+
...(options.suppressRoutineResultIntercom ? { suppressRoutineResultIntercom: true } : {}),
|
|
4493
|
+
} as SubagentParamsLike;
|
|
4494
|
+
const normalizedGate = normalizeGateParams(launchParams);
|
|
4495
|
+
if (!normalizedGate.ok) throw new Error(normalizedGate.error);
|
|
4496
|
+
return prepareWorkflowChildParams(normalizedGate.params);
|
|
4497
|
+
}
|
|
4498
|
+
|
|
4499
|
+
type GateParamsNormalizationResult =
|
|
4500
|
+
| { ok: true; params: SubagentParamsLike }
|
|
4501
|
+
| { ok: false; error: string };
|
|
4502
|
+
|
|
4503
|
+
function normalizeGateParams(params: SubagentParamsLike): GateParamsNormalizationResult {
|
|
4504
|
+
if (params.gate !== undefined && params.action === "resume") {
|
|
4505
|
+
return { ok: false, error: "gate is not supported with action='resume'; resume uses the retained child contract." };
|
|
4506
|
+
}
|
|
4507
|
+
const normalized = normalizeGateAcceptance(params.gate, params.acceptance);
|
|
4508
|
+
if (!normalized.ok) return { ok: false, error: normalized.error };
|
|
4509
|
+
if (params.gate === undefined) return { ok: true, params };
|
|
4510
|
+
const { gate: _gate, ...rest } = params;
|
|
4511
|
+
return { ok: true, params: { ...rest, ...(normalized.acceptance !== undefined ? { acceptance: normalized.acceptance } : {}) } };
|
|
4512
|
+
}
|
|
4513
|
+
|
|
3891
4514
|
function prepareWorkflowChildParams(params: SubagentParamsLike): SubagentParamsLike {
|
|
3892
4515
|
if (params.worktree !== true || !params.agent) return params;
|
|
3893
4516
|
const {
|
|
@@ -3901,6 +4524,7 @@ function prepareWorkflowChildParams(params: SubagentParamsLike): SubagentParamsL
|
|
|
3901
4524
|
acceptance,
|
|
3902
4525
|
agentContract,
|
|
3903
4526
|
toolBudget,
|
|
4527
|
+
reads,
|
|
3904
4528
|
...runParams
|
|
3905
4529
|
} = params;
|
|
3906
4530
|
return {
|
|
@@ -3912,6 +4536,7 @@ function prepareWorkflowChildParams(params: SubagentParamsLike): SubagentParamsL
|
|
|
3912
4536
|
...(model !== undefined ? { model } : {}),
|
|
3913
4537
|
...(skill !== undefined ? { skill } : {}),
|
|
3914
4538
|
...(output !== undefined ? { output } : {}),
|
|
4539
|
+
...(reads !== undefined ? { reads } : {}),
|
|
3915
4540
|
...(outputMode !== undefined ? { outputMode } : {}),
|
|
3916
4541
|
...(outputSchema !== undefined ? { outputSchema } : {}),
|
|
3917
4542
|
...(acceptance !== undefined ? { acceptance } : {}),
|
|
@@ -3943,21 +4568,6 @@ function workflowChatProgressUpdate(
|
|
|
3943
4568
|
};
|
|
3944
4569
|
}
|
|
3945
4570
|
|
|
3946
|
-
function omitExecutionModeActionAlias(params: SubagentParamsLike): SubagentParamsLike {
|
|
3947
|
-
const action = params.action?.toLowerCase();
|
|
3948
|
-
if (action === "single" && (params.agent !== undefined || params.task !== undefined)) {
|
|
3949
|
-
const rest = { ...params };
|
|
3950
|
-
delete rest.action;
|
|
3951
|
-
return rest;
|
|
3952
|
-
}
|
|
3953
|
-
if ((action === "parallel" || action === "tasks") && (params.tasks?.length ?? 0) > 0) {
|
|
3954
|
-
const rest = { ...params };
|
|
3955
|
-
delete rest.action;
|
|
3956
|
-
return rest;
|
|
3957
|
-
}
|
|
3958
|
-
return params;
|
|
3959
|
-
}
|
|
3960
|
-
|
|
3961
4571
|
function createScheduledOwnerState(source: SubagentState, ownerSessionId: string, ctx: ExtensionContext): SubagentState {
|
|
3962
4572
|
const ownerSpawns = source.subagentSpawns?.sessionId === ownerSessionId
|
|
3963
4573
|
? {
|
|
@@ -3996,6 +4606,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3996
4606
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
3997
4607
|
ctx: ExtensionContext,
|
|
3998
4608
|
) => Promise<AgentToolResult<Details>>;
|
|
4609
|
+
/** Public/model-facing execution boundary. Internal direct launch primitives use execute or executeDelegated. */
|
|
4610
|
+
executePublic: (
|
|
4611
|
+
id: string,
|
|
4612
|
+
params: SubagentParamsLike,
|
|
4613
|
+
signal: AbortSignal,
|
|
4614
|
+
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
4615
|
+
ctx: ExtensionContext,
|
|
4616
|
+
) => Promise<AgentToolResult<Details>>;
|
|
3999
4617
|
/**
|
|
4000
4618
|
* Correlated extension-to-extension delegation owns its request IDs and
|
|
4001
4619
|
* cancellation controllers, so independent requests may execute concurrently.
|
|
@@ -4018,6 +4636,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4018
4636
|
} {
|
|
4019
4637
|
const delegatedThinkingOverrides = new WeakMap<object, AgentConfig["thinking"]>();
|
|
4020
4638
|
const delegatedZeroToolBudgets = new WeakSet<object>();
|
|
4639
|
+
const delegatedExecutions = new WeakSet<object>();
|
|
4640
|
+
const warnedArtifactPackageDirs = new Set<string>();
|
|
4021
4641
|
const scheduledOwnerExecutors = new Map<string, ReturnType<typeof createSubagentExecutor>>();
|
|
4022
4642
|
const execute = async (
|
|
4023
4643
|
_id: string,
|
|
@@ -4027,63 +4647,156 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4027
4647
|
ctx: ExtensionContext,
|
|
4028
4648
|
preserveActiveSession = false,
|
|
4029
4649
|
): Promise<AgentToolResult<Details>> => {
|
|
4650
|
+
const workflowLaunchObserver = workflowLaunchObservers.get(params);
|
|
4030
4651
|
const delegatedThinkingOverride = delegatedThinkingOverrides.get(params);
|
|
4031
4652
|
const allowZeroToolBudget = delegatedZeroToolBudgets.has(params);
|
|
4653
|
+
const delegatedExecution = delegatedExecutions.has(params);
|
|
4032
4654
|
if (!preserveActiveSession) deps.state.baseCwd = ctx.cwd;
|
|
4033
4655
|
deps.state.foregroundRuns ??= new Map();
|
|
4034
4656
|
deps.state.foregroundControls ??= new Map();
|
|
4035
4657
|
deps.state.lastForegroundControlId ??= null;
|
|
4036
|
-
const
|
|
4037
|
-
if (
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
return { content: [{ type: "text", text: "workflowScript does not support clarify UI." }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4044
|
-
}
|
|
4045
|
-
const timeout = requestParams.timeoutMs ?? requestParams.maxRuntimeMs ?? DEFAULT_FOREGROUND_TIMEOUT_MS;
|
|
4658
|
+
const normalizedGate = normalizeGateParams(params);
|
|
4659
|
+
if (!normalizedGate.ok) return buildRequestedModeError(params, normalizedGate.error);
|
|
4660
|
+
const requestParams = normalizedGate.params;
|
|
4661
|
+
const normalizedAction = typeof requestParams.action === "string" ? requestParams.action.trim() : requestParams.action;
|
|
4662
|
+
if (requestParams.workflowScript !== undefined && normalizedAction === undefined) {
|
|
4663
|
+
const parentCwd = ctx.cwd;
|
|
4664
|
+
const timeout = requestParams.timeoutMs ?? requestParams.maxRuntimeMs ?? (requestParams.async === false ? resolveConfigDefaultTimeoutMs(deps.config.timeoutMs) ?? DEFAULT_FOREGROUND_TIMEOUT_MS : undefined);
|
|
4046
4665
|
const workflowUsageBudget = validateUsageBudgetConfig(requestParams.usageBudget ?? deps.config.usageBudget, requestParams.usageBudget ? "usageBudget" : "config.usageBudget");
|
|
4047
4666
|
if (workflowUsageBudget.error) return buildRequestedModeError(requestParams, workflowUsageBudget.error);
|
|
4048
|
-
const workflowCwd = resolveRequestedCwd(
|
|
4049
|
-
const
|
|
4667
|
+
const workflowCwd = resolveRequestedCwd(parentCwd, requestParams.cwd);
|
|
4668
|
+
const workflowPrompts = { render: (ref: string, vars?: unknown) => renderWorkflowPrompt(ref, vars, workflowCwd) };
|
|
4669
|
+
const chatProgressResult = resolveWorkflowChatProgress({ requested: requestParams.chatProgress, parentCwd, workflowCwd, background: requestParams.async !== false });
|
|
4050
4670
|
if (chatProgressResult.error) return { content: [{ type: "text", text: chatProgressResult.error }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4051
4671
|
const chatProgress = chatProgressResult.projection!;
|
|
4052
|
-
|
|
4053
|
-
|
|
4672
|
+
const explicitMission = requestParams.missionId !== undefined || requestParams.mission !== undefined;
|
|
4673
|
+
const autoMission = !explicitMission;
|
|
4674
|
+
const workflowPreview = autoMission ? previewSimpleWorkflowRun(requestParams.workflowScript) : undefined;
|
|
4675
|
+
const previewAgent = workflowPreview?.agent?.trim() || undefined;
|
|
4676
|
+
const scriptFirstLine = requestParams.workflowScript.split(/\r?\n/).map((line) => line.trim()).find(Boolean) || "Workflow";
|
|
4677
|
+
const boundedScriptPreview = scriptFirstLine.length > 100 ? `${scriptFirstLine.slice(0, 97)}...` : scriptFirstLine;
|
|
4678
|
+
const derivedObjective = previewAgent ? `Workflow: ${previewAgent}` : boundedScriptPreview;
|
|
4679
|
+
const workflowDepth = checkSubagentDepth(deps.config.maxSubagentDepth).depth;
|
|
4680
|
+
const asyncWorkflow = requestParams.async !== false;
|
|
4681
|
+
const topLevelAsyncWorkflow = asyncWorkflow
|
|
4682
|
+
&& workflowDepth === 0
|
|
4683
|
+
&& !resolveInheritedNestedRouteFromEnv()
|
|
4684
|
+
&& !requestParams.workflowParentRunId;
|
|
4685
|
+
const workflowRunId = asyncWorkflow ? randomUUID() : undefined;
|
|
4686
|
+
let workflowCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
4687
|
+
if (workflowRunId && topLevelAsyncWorkflow) {
|
|
4688
|
+
const currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
4689
|
+
try {
|
|
4690
|
+
workflowCapacity = acquireActiveAsyncCapacity({
|
|
4691
|
+
sessionId: currentSessionId,
|
|
4692
|
+
limit: resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession),
|
|
4693
|
+
runId: workflowRunId,
|
|
4694
|
+
kind: "workflow",
|
|
4695
|
+
asyncDir: path.join(DIRS.async, workflowRunId),
|
|
4696
|
+
}, { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []) });
|
|
4697
|
+
} catch (error) {
|
|
4698
|
+
if (error instanceof ActiveAsyncCapacityError) {
|
|
4699
|
+
deps.state.activeAsyncCapacity = error.snapshot;
|
|
4700
|
+
return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "workflow", results: [], activeAsyncCapacity: error.snapshot } };
|
|
4701
|
+
}
|
|
4702
|
+
throw error;
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
let workflowFanoutBudget: RunFanoutBudgetDescriptor;
|
|
4706
|
+
try {
|
|
4707
|
+
workflowFanoutBudget = requestParams.runFanoutBudget
|
|
4708
|
+
?? decodeRunFanoutBudgetDescriptor(process.env[RUN_FANOUT_BUDGET_ENV])
|
|
4709
|
+
?? createRunFanoutBudget(_id, resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
|
|
4710
|
+
} catch (error) {
|
|
4711
|
+
workflowCapacity?.rollback();
|
|
4712
|
+
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4713
|
+
}
|
|
4714
|
+
let missionBinding: MissionLaunchBinding | undefined;
|
|
4715
|
+
let missionWarning: string | undefined;
|
|
4716
|
+
try {
|
|
4717
|
+
missionBinding = prepareMissionLaunch({
|
|
4718
|
+
params: autoMission ? { ...requestParams, task: derivedObjective } : requestParams,
|
|
4719
|
+
projectRoot: workflowCwd,
|
|
4720
|
+
...(deps.config.missions ? { config: deps.config.missions } : {}),
|
|
4721
|
+
ownerSessionId: resolveCurrentSessionId(ctx.sessionManager),
|
|
4722
|
+
});
|
|
4723
|
+
} catch (error) {
|
|
4724
|
+
if (explicitMission) {
|
|
4725
|
+
workflowCapacity?.rollback();
|
|
4726
|
+
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4727
|
+
}
|
|
4728
|
+
missionWarning = `Mission tracking unavailable: ${error instanceof Error ? error.message : String(error)}`;
|
|
4729
|
+
}
|
|
4730
|
+
const detachWorkflowChildMissions = autoMission || missionBinding !== undefined || requestParams.mission === false;
|
|
4731
|
+
const workflowState = missionBinding ? createMissionWorkflowState(missionBinding.location, missionBinding.missionId) : undefined;
|
|
4732
|
+
const attachWorkflowMission = (result: AgentToolResult<Details>): AgentToolResult<Details> => {
|
|
4733
|
+
if (!missionBinding) return missionWarning ? { ...result, details: { ...result.details, missionWarning } } : result;
|
|
4734
|
+
try {
|
|
4735
|
+
return attachMissionToLaunchResult({ binding: missionBinding, result });
|
|
4736
|
+
} catch (error) {
|
|
4737
|
+
const warning = `Mission tracking unavailable after launch: ${error instanceof Error ? error.message : String(error)}`;
|
|
4738
|
+
return explicitMission
|
|
4739
|
+
? { ...result, isError: true, content: [...result.content, { type: "text", text: warning }], details: { ...result.details, missionWarning: warning } }
|
|
4740
|
+
: { ...result, details: { ...result.details, missionWarning: warning } };
|
|
4741
|
+
}
|
|
4742
|
+
};
|
|
4743
|
+
if (workflowRunId) {
|
|
4744
|
+
const toolCallId = _id;
|
|
4054
4745
|
const asyncDir = path.join(DIRS.async, workflowRunId);
|
|
4055
4746
|
const resultPath = path.join(DIRS.results, `${workflowRunId}.json`);
|
|
4056
4747
|
const statusPath = path.join(asyncDir, "status.json");
|
|
4057
4748
|
const eventsPath = path.join(asyncDir, "events.jsonl");
|
|
4058
4749
|
const startedAt = Date.now();
|
|
4059
4750
|
const currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
4060
|
-
|
|
4061
|
-
|
|
4751
|
+
try {
|
|
4752
|
+
fs.mkdirSync(asyncDir, { recursive: true });
|
|
4753
|
+
writeRunFanoutBudgetDescriptor(asyncDir, workflowFanoutBudget);
|
|
4754
|
+
fs.mkdirSync(DIRS.results, { recursive: true });
|
|
4755
|
+
} catch (error) {
|
|
4756
|
+
workflowCapacity?.rollback();
|
|
4757
|
+
return { content: [{ type: "text", text: `Failed to create async workflow storage: ${error instanceof Error ? error.message : String(error)}` }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4758
|
+
}
|
|
4062
4759
|
const controller = new AbortController();
|
|
4063
4760
|
deps.state.workflowControllers ??= new Map();
|
|
4064
4761
|
deps.state.workflowControllers.set(workflowRunId, controller);
|
|
4762
|
+
workflowCapacity?.markWorkflowStarted();
|
|
4763
|
+
if (workflowCapacity) deps.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(currentSessionId, resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession), { liveWorkflowRunIds: new Set(deps.state.workflowControllers.keys()) });
|
|
4065
4764
|
let status: AsyncStatus = {
|
|
4066
4765
|
runId: workflowRunId,
|
|
4766
|
+
toolCallId,
|
|
4067
4767
|
sessionId: currentSessionId ?? undefined,
|
|
4068
4768
|
mode: "workflow",
|
|
4069
4769
|
state: "running",
|
|
4070
4770
|
startedAt,
|
|
4071
4771
|
lastUpdate: startedAt,
|
|
4072
|
-
deadlineAt: startedAt + timeout,
|
|
4073
|
-
|
|
4074
|
-
cwd: ctx.cwd,
|
|
4772
|
+
...(timeout !== undefined ? { deadlineAt: startedAt + timeout, timeoutMs: timeout } : {}),
|
|
4773
|
+
cwd: workflowCwd,
|
|
4075
4774
|
pid: process.pid,
|
|
4076
4775
|
steps: [],
|
|
4077
4776
|
workflow: { trace: [], emits: [], console: [] },
|
|
4777
|
+
runFanoutBudget: getRunFanoutBudgetSnapshot(workflowFanoutBudget),
|
|
4078
4778
|
};
|
|
4079
4779
|
const appendWorkflowEvent = (event: Record<string, unknown>) => fs.appendFileSync(eventsPath, `${JSON.stringify({ ts: Date.now(), runId: workflowRunId, ...event })}\n`, "utf-8");
|
|
4780
|
+
let indexedState: AsyncStatus["state"] | undefined;
|
|
4080
4781
|
const persist = () => {
|
|
4081
4782
|
status.lastUpdate = Date.now();
|
|
4082
4783
|
writeAtomicJson(statusPath, status);
|
|
4784
|
+
if (indexedState !== status.state) {
|
|
4785
|
+
updateActiveRunIndex(asyncDir, status.state);
|
|
4786
|
+
indexedState = status.state;
|
|
4787
|
+
}
|
|
4083
4788
|
const job = deps.state.asyncJobs.get(workflowRunId);
|
|
4084
4789
|
if (job) {
|
|
4085
4790
|
job.status = status.state;
|
|
4086
4791
|
job.updatedAt = status.lastUpdate;
|
|
4792
|
+
job.activityState = status.activityState;
|
|
4793
|
+
job.lastActivityAt = status.lastActivityAt;
|
|
4794
|
+
job.currentTool = status.currentTool;
|
|
4795
|
+
job.currentToolStartedAt = status.currentToolStartedAt;
|
|
4796
|
+
job.currentPath = status.currentPath;
|
|
4797
|
+
job.turnCount = status.turnCount;
|
|
4798
|
+
job.toolCount = status.toolCount;
|
|
4799
|
+
job.currentStep = status.currentStep;
|
|
4087
4800
|
if (status.steps) {
|
|
4088
4801
|
job.steps = status.steps.map((step, index) => ({ ...step, index }));
|
|
4089
4802
|
job.agents = status.steps.map((step) => step.agent);
|
|
@@ -4094,7 +4807,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4094
4807
|
job.workflow = status.workflow;
|
|
4095
4808
|
}
|
|
4096
4809
|
};
|
|
4097
|
-
const
|
|
4810
|
+
const projectWorkflowActivity = () => {
|
|
4811
|
+
const steps = status.steps ?? [];
|
|
4812
|
+
const runningSteps = steps.filter((step) => step.status === "running");
|
|
4813
|
+
const lastActivityAt = runningSteps.reduce<number | undefined>((latest, step) => step.lastActivityAt === undefined ? latest : Math.max(latest ?? step.lastActivityAt, step.lastActivityAt), undefined);
|
|
4814
|
+
const activeToolStep = runningSteps
|
|
4815
|
+
.filter((step) => step.currentTool)
|
|
4816
|
+
.sort((left, right) => (left.lastActivityAt ?? 0) - (right.lastActivityAt ?? 0))
|
|
4817
|
+
.at(-1);
|
|
4818
|
+
status.activityState = runningSteps.some((step) => step.activityState === "needs_attention")
|
|
4819
|
+
? "needs_attention"
|
|
4820
|
+
: runningSteps.some((step) => step.activityState === "active_long_running") ? "active_long_running" : undefined;
|
|
4821
|
+
status.lastActivityAt = lastActivityAt;
|
|
4822
|
+
status.currentTool = activeToolStep?.currentTool;
|
|
4823
|
+
status.currentToolStartedAt = activeToolStep?.currentToolStartedAt;
|
|
4824
|
+
status.currentPath = activeToolStep?.currentPath;
|
|
4825
|
+
const turnCounts = steps.flatMap((step) => step.turnCount === undefined ? [] : [step.turnCount]);
|
|
4826
|
+
const toolCounts = steps.flatMap((step) => step.toolCount === undefined ? [] : [step.toolCount]);
|
|
4827
|
+
status.turnCount = turnCounts.length > 0 ? turnCounts.reduce((total, count) => total + count, 0) : undefined;
|
|
4828
|
+
status.toolCount = toolCounts.length > 0 ? toolCounts.reduce((total, count) => total + count, 0) : undefined;
|
|
4829
|
+
status.currentStep = runningSteps.length === 1 ? steps.indexOf(runningSteps[0]!) : undefined;
|
|
4830
|
+
};
|
|
4831
|
+
const workflowJob: AsyncJobState = { asyncId: workflowRunId, asyncDir, cwd: workflowCwd, status: "running", sessionId: currentSessionId ?? undefined, mode: "workflow", agents: [], steps: [], startedAt, updatedAt: startedAt, ...(timeout !== undefined ? { timeoutMs: timeout, deadlineAt: startedAt + timeout } : {}), workflow: status.workflow };
|
|
4098
4832
|
deps.state.asyncJobs.set(workflowRunId, workflowJob);
|
|
4099
4833
|
deps.state.fleetJobs ??= new Map();
|
|
4100
4834
|
deps.state.fleetJobs.set(workflowRunId, workflowJob);
|
|
@@ -4103,23 +4837,54 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4103
4837
|
const { workflowScript, async: _workflowAsync, chatProgress: _chatProgress, ...workflowRequest } = requestParams;
|
|
4104
4838
|
void Promise.resolve().then(async () => {
|
|
4105
4839
|
const workflowResults: SingleResult[] = [];
|
|
4106
|
-
const { action: _action, agent: _agent, task: _task, tasks: _tasks, chain: _chain, concurrency: _concurrency, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, ...workflowChildDefaults } = workflowRequest;
|
|
4840
|
+
const { action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, missionId: _missionId, mission: _mission, ...workflowChildDefaults } = workflowRequest;
|
|
4841
|
+
const workflowSteps = new Map<string, NonNullable<AsyncStatus["steps"]>[number]>();
|
|
4842
|
+
let projectedTraceLength = 0;
|
|
4843
|
+
let projectedTraceTail: NonNullable<Details["workflow"]>["trace"][number] | undefined;
|
|
4107
4844
|
const updateTrace = (trace: NonNullable<Details["workflow"]>["trace"]) => {
|
|
4108
4845
|
status.workflow = { ...(status.workflow ?? { emits: [], console: [] }), trace };
|
|
4109
|
-
|
|
4110
|
-
|
|
4846
|
+
const rebuild = trace.length < projectedTraceLength
|
|
4847
|
+
|| (projectedTraceLength > 0 && trace[projectedTraceLength - 1] !== projectedTraceTail);
|
|
4848
|
+
if (rebuild) {
|
|
4849
|
+
workflowSteps.clear();
|
|
4850
|
+
for (const step of status.steps ?? []) {
|
|
4851
|
+
if (step.workflowKey) workflowSteps.set(step.workflowKey, step);
|
|
4852
|
+
}
|
|
4853
|
+
projectedTraceLength = 0;
|
|
4854
|
+
}
|
|
4855
|
+
for (let index = projectedTraceLength; index < trace.length; index += 1) {
|
|
4856
|
+
const entry = trace[index]!;
|
|
4857
|
+
if (entry.operation !== "run") continue;
|
|
4858
|
+
const existing = workflowSteps.get(entry.key);
|
|
4111
4859
|
if (entry.state === "reused" && existing) continue;
|
|
4112
4860
|
const mapped = entry.state === "started" || entry.state === "reused" ? "running" : entry.state === "completed" ? "completed" : "failed";
|
|
4113
4861
|
if (existing) {
|
|
4114
4862
|
existing.status = mapped;
|
|
4863
|
+
if (entry.agent) existing.agent = entry.agent;
|
|
4864
|
+
if (entry.runId) existing.runId = entry.runId;
|
|
4865
|
+
if (entry.state === "failed" && !entry.runId && existing.async === undefined) existing.async = false;
|
|
4115
4866
|
if (entry.error === undefined) delete existing.error;
|
|
4116
4867
|
else existing.error = entry.error;
|
|
4117
4868
|
if (entry.durationMs === undefined) delete existing.durationMs;
|
|
4118
4869
|
else existing.durationMs = entry.durationMs;
|
|
4119
4870
|
} else {
|
|
4120
|
-
|
|
4871
|
+
const step: NonNullable<AsyncStatus["steps"]>[number] = {
|
|
4872
|
+
agent: entry.agent ?? entry.key,
|
|
4873
|
+
label: entry.key,
|
|
4874
|
+
workflowKey: entry.key,
|
|
4875
|
+
parentWorkflowRunId: workflowRunId,
|
|
4876
|
+
status: mapped,
|
|
4877
|
+
startedAt: Date.now(),
|
|
4878
|
+
...(entry.runId ? { runId: entry.runId } : {}),
|
|
4879
|
+
...(entry.state === "failed" && !entry.runId ? { async: false } : {}),
|
|
4880
|
+
};
|
|
4881
|
+
status.steps?.push(step);
|
|
4882
|
+
workflowSteps.set(entry.key, step);
|
|
4121
4883
|
}
|
|
4122
4884
|
}
|
|
4885
|
+
projectedTraceLength = trace.length;
|
|
4886
|
+
projectedTraceTail = trace.at(-1);
|
|
4887
|
+
projectWorkflowActivity();
|
|
4123
4888
|
persist();
|
|
4124
4889
|
appendWorkflowEvent({ type: "subagent.workflow.trace", trace });
|
|
4125
4890
|
};
|
|
@@ -4128,21 +4893,93 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4128
4893
|
script: workflowScript,
|
|
4129
4894
|
timeoutMs: timeout,
|
|
4130
4895
|
signal: controller.signal,
|
|
4896
|
+
prompts: workflowPrompts,
|
|
4897
|
+
...(workflowState ? { state: workflowState } : {}),
|
|
4131
4898
|
onTrace: updateTrace,
|
|
4899
|
+
admit: (calls) => {
|
|
4900
|
+
status.runFanoutBudget = claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
|
|
4901
|
+
persist();
|
|
4902
|
+
},
|
|
4132
4903
|
onEmit: (emits) => {
|
|
4133
4904
|
// Each emit is validated at the host boundary in runWorkflowScript before onEmit fires.
|
|
4134
4905
|
status.workflow = { ...(status.workflow ?? { trace: [], console: [] }), emits };
|
|
4135
4906
|
persist();
|
|
4136
4907
|
appendWorkflowEvent({ type: "subagent.workflow.emit", value: emits.at(-1) });
|
|
4137
4908
|
},
|
|
4138
|
-
launch: async (key, childParams, workflowSignal) => {
|
|
4909
|
+
launch: async (key, childParams, workflowSignal, admission) => {
|
|
4139
4910
|
if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
|
|
4140
4911
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
4141
4912
|
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
|
|
4142
|
-
const
|
|
4143
|
-
const
|
|
4913
|
+
const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
|
|
4914
|
+
const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
|
|
4915
|
+
recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
|
|
4916
|
+
status: "running",
|
|
4917
|
+
...(typeof childParams.agent === "string" && childParams.agent.trim() ? { agent: childParams.agent.trim() } : {}),
|
|
4918
|
+
...(childLabel ? { label: childLabel } : {}),
|
|
4919
|
+
...(childPhase ? { phase: childPhase } : {}),
|
|
4920
|
+
heartbeat: { status: "running", ...(childPhase ? { phase: childPhase } : {}) },
|
|
4921
|
+
});
|
|
4922
|
+
const result = await runMissionWorkflowChild(missionBinding, workflowRunId, key, childPhase, () => {
|
|
4923
|
+
const childRequest = bindMissionWorkflowChildAsyncLaunch(
|
|
4924
|
+
{ ...prepareWorkflowLaunchParams(workflowChildDefaults, childParams, workflowRunId, key, { missionDetached: detachWorkflowChildMissions, runFanoutBudget: workflowFanoutBudget }), runFanoutAdmitted: admission.admitted },
|
|
4925
|
+
missionBinding,
|
|
4926
|
+
deps.asyncByDefault,
|
|
4927
|
+
);
|
|
4928
|
+
workflowLaunchObservers.set(childRequest, (launch) => {
|
|
4929
|
+
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
4930
|
+
if (step) {
|
|
4931
|
+
step.agent = launch.agent;
|
|
4932
|
+
step.sessionFile = launch.sessionFile;
|
|
4933
|
+
step.async = launch.async;
|
|
4934
|
+
if (launch.runId) step.runId = launch.runId;
|
|
4935
|
+
persist();
|
|
4936
|
+
}
|
|
4937
|
+
recordMissionWorkflowChild(missionBinding, workflowRunId, key, { status: "running", agent: launch.agent, ...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}) });
|
|
4938
|
+
});
|
|
4939
|
+
return execute(randomUUID(), childRequest, workflowSignal, (update) => {
|
|
4940
|
+
const progress = update.details.progress?.[0];
|
|
4941
|
+
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
4942
|
+
if (!progress || !step) return;
|
|
4943
|
+
step.status = progress.status === "completed" ? "completed" : progress.status === "failed" ? "failed" : "running";
|
|
4944
|
+
step.activityState = progress.activityState;
|
|
4945
|
+
step.lastActivityAt = progress.lastActivityAt;
|
|
4946
|
+
step.currentTool = progress.currentTool;
|
|
4947
|
+
step.currentToolArgs = progress.currentToolArgs;
|
|
4948
|
+
step.currentToolStartedAt = progress.currentToolStartedAt;
|
|
4949
|
+
step.currentPath = progress.currentPath;
|
|
4950
|
+
step.recentTools = progress.recentTools.map((tool) => ({ ...tool }));
|
|
4951
|
+
step.recentOutput = [...progress.recentOutput];
|
|
4952
|
+
step.turnCount = progress.turnCount;
|
|
4953
|
+
step.toolCount = progress.toolCount;
|
|
4954
|
+
step.model = progress.model;
|
|
4955
|
+
step.thinking = progress.thinking;
|
|
4956
|
+
step.error = progress.error;
|
|
4957
|
+
projectWorkflowActivity();
|
|
4958
|
+
persist();
|
|
4959
|
+
recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
|
|
4960
|
+
status: step.status,
|
|
4961
|
+
heartbeat: { status: step.status, ...(childPhase ? { phase: childPhase } : {}) },
|
|
4962
|
+
});
|
|
4963
|
+
}, ctx, preserveActiveSession);
|
|
4964
|
+
});
|
|
4144
4965
|
workflowResults.push(...result.details.results);
|
|
4145
4966
|
const child = workflowChildResult(key, result);
|
|
4967
|
+
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
4968
|
+
if (step) {
|
|
4969
|
+
step.async = Boolean(result.details.asyncId || result.details.asyncDir);
|
|
4970
|
+
if (child.runId) step.runId = child.runId;
|
|
4971
|
+
}
|
|
4972
|
+
if (result.details.asyncDir && missionBinding) writeMissionAsyncBinding(result.details.asyncDir, missionBinding);
|
|
4973
|
+
const childStatus = missionWorkflowChildStatus(result);
|
|
4974
|
+
recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
|
|
4975
|
+
status: childStatus,
|
|
4976
|
+
...(child.runId ? { runId: child.runId } : {}),
|
|
4977
|
+
...(result.details.results[0]?.agent ? { agent: result.details.results[0].agent } : {}),
|
|
4978
|
+
...(result.details.results[0]?.sessionFile ? { sessionPath: result.details.results[0].sessionFile } : {}),
|
|
4979
|
+
artifactPaths: child.artifactPaths,
|
|
4980
|
+
...(["completed", "failed"].includes(childStatus) ? { completedAt: new Date().toISOString() } : {}),
|
|
4981
|
+
heartbeat: { status: childStatus, ...(childPhase ? { phase: childPhase } : {}) },
|
|
4982
|
+
});
|
|
4146
4983
|
if (result.details.asyncId) {
|
|
4147
4984
|
const childJob = deps.state.asyncJobs.get(result.details.asyncId);
|
|
4148
4985
|
if (childJob) { childJob.parentWorkflowRunId = workflowRunId; childJob.workflowKey = key; }
|
|
@@ -4156,26 +4993,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4156
4993
|
const summary = `Workflow completed with ${workflow.children.length} child run(s). Return: ${returnPreview}${emitPreview} Trace: ${workflow.trace.length} event(s).`;
|
|
4157
4994
|
const workflowUsage = sumResultsUsage(workflowResults);
|
|
4158
4995
|
status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console }, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
|
|
4996
|
+
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary, output: summary, results: workflow.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: workflowCwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
|
|
4159
4997
|
persist();
|
|
4160
4998
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: "complete" });
|
|
4161
|
-
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary, output: summary, results: workflow.children.map((child) => ({ agent: child.key, output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: ctx.cwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
|
|
4162
4999
|
} catch (error) {
|
|
4163
5000
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
4164
5001
|
const stopped = controller.signal.aborted;
|
|
4165
5002
|
status = compactOptional<AsyncStatus>({ ...status, state: stopped ? "stopped" : "failed", stopped: stopped || undefined, error: error instanceof Error ? error.message : String(error), endedAt: Date.now(), workflow: { trace: partial.trace, emits: partial.emits, console: partial.console } });
|
|
5003
|
+
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: false, state: status.state, summary: status.error, error: status.error, stopped: status.stopped, results: partial.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: workflowCwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
|
|
4166
5004
|
persist();
|
|
4167
5005
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, error: status.error });
|
|
4168
|
-
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, agent: "workflow", mode: "workflow", success: false, state: status.state, summary: status.error, error: status.error, stopped: status.stopped, results: partial.children.map((child) => ({ agent: child.key, output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: ctx.cwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
|
|
4169
5006
|
} finally {
|
|
4170
5007
|
deps.state.workflowControllers?.delete(workflowRunId);
|
|
5008
|
+
deps.state.activeAsyncCapacity = workflowCapacity?.reconcile(new Set(deps.state.workflowControllers?.keys() ?? []))
|
|
5009
|
+
?? deps.state.activeAsyncCapacity;
|
|
4171
5010
|
}
|
|
4172
5011
|
});
|
|
4173
|
-
return {
|
|
5012
|
+
return attachWorkflowMission(withRunFanoutBudget({
|
|
4174
5013
|
content: [{ type: "text", text: formatAsyncStartedMessage(`Async workflow [${workflowRunId}]`, ctx.hasUI === true) }],
|
|
4175
|
-
details: { mode: "workflow", runId: workflowRunId, asyncId: workflowRunId, asyncDir, results: [], chatProgress },
|
|
4176
|
-
};
|
|
5014
|
+
details: { mode: "workflow", runId: workflowRunId, toolCallId, asyncId: workflowRunId, asyncDir, results: [], chatProgress, ...(deps.state.activeAsyncCapacity ? { activeAsyncCapacity: deps.state.activeAsyncCapacity } : {}) },
|
|
5015
|
+
}, workflowFanoutBudget));
|
|
4177
5016
|
}
|
|
4178
|
-
const { workflowScript: _workflowScript, action: _action, agent: _agent, task: _task, tasks: _tasks, chain: _chain, concurrency: _concurrency, async: _async, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, chatProgress: _chatProgress, ...workflowChildDefaults } = requestParams;
|
|
5017
|
+
const { workflowScript: _workflowScript, action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, async: _async, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, chatProgress: _chatProgress, missionId: _missionId, mission: _mission, ...workflowChildDefaults } = requestParams;
|
|
4179
5018
|
const workflowResults: SingleResult[] = [];
|
|
4180
5019
|
let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [] };
|
|
4181
5020
|
const sendWorkflowProgress = () => {
|
|
@@ -4187,22 +5026,67 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4187
5026
|
script: requestParams.workflowScript,
|
|
4188
5027
|
timeoutMs: timeout,
|
|
4189
5028
|
signal,
|
|
5029
|
+
prompts: workflowPrompts,
|
|
5030
|
+
...(workflowState ? { state: workflowState } : {}),
|
|
4190
5031
|
onTrace: (trace) => {
|
|
4191
5032
|
liveWorkflow = { ...liveWorkflow, trace };
|
|
4192
5033
|
sendWorkflowProgress();
|
|
4193
5034
|
},
|
|
5035
|
+
admit: (calls) => {
|
|
5036
|
+
claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
|
|
5037
|
+
},
|
|
4194
5038
|
onEmit: (emits) => {
|
|
4195
5039
|
liveWorkflow = { ...liveWorkflow, emits };
|
|
4196
5040
|
sendWorkflowProgress();
|
|
4197
5041
|
},
|
|
4198
|
-
launch: async (key, childParams, workflowSignal) => {
|
|
5042
|
+
launch: async (key, childParams, workflowSignal, admission) => {
|
|
4199
5043
|
if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
|
|
4200
5044
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
4201
5045
|
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
|
|
4202
|
-
const
|
|
4203
|
-
const
|
|
5046
|
+
const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
|
|
5047
|
+
const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
|
|
5048
|
+
recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
5049
|
+
status: "running",
|
|
5050
|
+
...(typeof childParams.agent === "string" && childParams.agent.trim() ? { agent: childParams.agent.trim() } : {}),
|
|
5051
|
+
...(childLabel ? { label: childLabel } : {}),
|
|
5052
|
+
...(childPhase ? { phase: childPhase } : {}),
|
|
5053
|
+
heartbeat: { status: "running", ...(childPhase ? { phase: childPhase } : {}) },
|
|
5054
|
+
});
|
|
5055
|
+
const result = await runMissionWorkflowChild(missionBinding, _id, key, childPhase, () => {
|
|
5056
|
+
const childRequest = bindMissionWorkflowChildAsyncLaunch(
|
|
5057
|
+
{ ...prepareWorkflowLaunchParams(workflowChildDefaults, childParams, _id, key, { missionDetached: detachWorkflowChildMissions, suppressRoutineResultIntercom: chatProgress.mode === "live-card", runFanoutBudget: workflowFanoutBudget }), runFanoutAdmitted: admission.admitted },
|
|
5058
|
+
missionBinding,
|
|
5059
|
+
deps.asyncByDefault,
|
|
5060
|
+
);
|
|
5061
|
+
workflowLaunchObservers.set(childRequest, (launch) => recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
5062
|
+
status: "running",
|
|
5063
|
+
agent: launch.agent,
|
|
5064
|
+
...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}),
|
|
5065
|
+
}));
|
|
5066
|
+
return execute(randomUUID(), childRequest, workflowSignal, (update) => {
|
|
5067
|
+
const progress = update.details.progress?.[0];
|
|
5068
|
+
if (!progress) return;
|
|
5069
|
+
const progressStatus = progress.status === "completed" ? "completed" : progress.status === "failed" ? "failed" : "running";
|
|
5070
|
+
recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
5071
|
+
status: progressStatus,
|
|
5072
|
+
heartbeat: { status: progressStatus, ...(childPhase ? { phase: childPhase } : {}) },
|
|
5073
|
+
});
|
|
5074
|
+
}, ctx, preserveActiveSession);
|
|
5075
|
+
});
|
|
4204
5076
|
workflowResults.push(...result.details.results);
|
|
4205
|
-
|
|
5077
|
+
if (result.details.asyncDir && missionBinding) writeMissionAsyncBinding(result.details.asyncDir, missionBinding);
|
|
5078
|
+
const child = workflowChildResult(key, result);
|
|
5079
|
+
const childStatus = missionWorkflowChildStatus(result);
|
|
5080
|
+
recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
5081
|
+
status: childStatus,
|
|
5082
|
+
...(child.runId ? { runId: child.runId } : {}),
|
|
5083
|
+
...(result.details.results[0]?.agent ? { agent: result.details.results[0].agent } : {}),
|
|
5084
|
+
...(result.details.results[0]?.sessionFile ? { sessionPath: result.details.results[0].sessionFile } : {}),
|
|
5085
|
+
artifactPaths: child.artifactPaths,
|
|
5086
|
+
...(["completed", "failed"].includes(childStatus) ? { completedAt: new Date().toISOString() } : {}),
|
|
5087
|
+
heartbeat: { status: childStatus, ...(childPhase ? { phase: childPhase } : {}) },
|
|
5088
|
+
});
|
|
5089
|
+
return child;
|
|
4206
5090
|
},
|
|
4207
5091
|
status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession)),
|
|
4208
5092
|
});
|
|
@@ -4211,10 +5095,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4211
5095
|
if (workflow.emits.length > 0) sections.push(`Emitted:\n${workflow.emits.map(formatWorkflowValue).join("\n")}`);
|
|
4212
5096
|
if (workflow.console.length > 0) sections.push(`Console:\n${workflow.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
|
|
4213
5097
|
if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
|
|
4214
|
-
return {
|
|
5098
|
+
return attachWorkflowMission(withRunFanoutBudget({
|
|
4215
5099
|
content: [{ type: "text", text: sections.join("\n\n") }],
|
|
4216
5100
|
details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflow.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console }, chatProgress }),
|
|
4217
|
-
};
|
|
5101
|
+
}, workflowFanoutBudget));
|
|
4218
5102
|
} catch (error) {
|
|
4219
5103
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
4220
5104
|
const text = error instanceof Error ? error.message : String(error);
|
|
@@ -4223,11 +5107,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4223
5107
|
if (partial.emits.length > 0) sections.push(`Emitted:\n${partial.emits.map(formatWorkflowValue).join("\n")}`);
|
|
4224
5108
|
if (partial.console.length > 0) sections.push(`Console:\n${partial.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
|
|
4225
5109
|
if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
|
|
4226
|
-
return {
|
|
5110
|
+
return attachWorkflowMission(withRunFanoutBudget({
|
|
4227
5111
|
content: [{ type: "text", text: sections.join("\n\n") }],
|
|
4228
5112
|
isError: true,
|
|
4229
5113
|
details: compactOptional<Details>({ mode: "workflow", runId: _id, results: partial.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: partial.trace, emits: partial.emits, console: partial.console }, chatProgress }),
|
|
4230
|
-
};
|
|
5114
|
+
}, workflowFanoutBudget));
|
|
4231
5115
|
}
|
|
4232
5116
|
}
|
|
4233
5117
|
const directParams = prepareWorkflowChildParams(requestParams);
|
|
@@ -4244,7 +5128,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4244
5128
|
? normalizeParentModel(ctx.model)
|
|
4245
5129
|
: rememberParentModel(deps.state, requestSessionId, ctx.model);
|
|
4246
5130
|
} catch (error) {
|
|
4247
|
-
if (action?.toLowerCase() !== "doctor") throw error;
|
|
5131
|
+
if (action?.toLowerCase() !== "doctor" && action?.toLowerCase() !== "guide") throw error;
|
|
4248
5132
|
requestParentModel = normalizeParentModel(ctx.model);
|
|
4249
5133
|
}
|
|
4250
5134
|
if (action) {
|
|
@@ -4331,6 +5215,29 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4331
5215
|
}
|
|
4332
5216
|
return handleWatchdogToolAction(action, paramsWithResolvedCwd, ctx, deps.watchdog);
|
|
4333
5217
|
}
|
|
5218
|
+
if (action === "refine" || action === "refine.show" || action === "refine.rollback") {
|
|
5219
|
+
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
5220
|
+
return {
|
|
5221
|
+
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
5222
|
+
isError: true,
|
|
5223
|
+
details: { mode: "management", results: [] },
|
|
5224
|
+
};
|
|
5225
|
+
}
|
|
5226
|
+
return handleRefinementAction(action, paramsWithResolvedCwd, {
|
|
5227
|
+
cwd: requestCwd,
|
|
5228
|
+
state: deps.state,
|
|
5229
|
+
signal,
|
|
5230
|
+
launchProposalChild: (task, outputSchema, proposalSignal) => execute(randomUUID(), {
|
|
5231
|
+
agent: "reviewer",
|
|
5232
|
+
task,
|
|
5233
|
+
context: "fresh",
|
|
5234
|
+
async: false,
|
|
5235
|
+
artifacts: false,
|
|
5236
|
+
outputSchema,
|
|
5237
|
+
toolBudget: { hard: 1, block: ["write", "edit", "bash"] },
|
|
5238
|
+
}, proposalSignal, undefined, ctx, true),
|
|
5239
|
+
});
|
|
5240
|
+
}
|
|
4334
5241
|
if (action === "grant-spawn-budget") {
|
|
4335
5242
|
if (deps.allowMutatingManagementActions === false || !ctx.hasUI) {
|
|
4336
5243
|
return {
|
|
@@ -4403,6 +5310,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4403
5310
|
details: { mode: "management", results: [], spawnBudget: granted.snapshot },
|
|
4404
5311
|
};
|
|
4405
5312
|
}
|
|
5313
|
+
if (action === "guide") {
|
|
5314
|
+
try {
|
|
5315
|
+
return {
|
|
5316
|
+
content: [{ type: "text", text: readSubagentGuide(paramsWithResolvedCwd.topic) }],
|
|
5317
|
+
details: { mode: "management", results: [] },
|
|
5318
|
+
};
|
|
5319
|
+
} catch (error) {
|
|
5320
|
+
return {
|
|
5321
|
+
content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
|
|
5322
|
+
isError: true,
|
|
5323
|
+
details: { mode: "management", results: [] },
|
|
5324
|
+
};
|
|
5325
|
+
}
|
|
5326
|
+
}
|
|
5327
|
+
if (action === "children.list") {
|
|
5328
|
+
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
5329
|
+
const children = listRetainedChildren(DIRS.async, deps.state.currentSessionId);
|
|
5330
|
+
return {
|
|
5331
|
+
content: [{ type: "text", text: formatRetainedChildren(children) }],
|
|
5332
|
+
details: { mode: "management", results: [] },
|
|
5333
|
+
};
|
|
5334
|
+
}
|
|
4406
5335
|
if (action === "doctor") {
|
|
4407
5336
|
let currentSessionFile: string | null = null;
|
|
4408
5337
|
let currentSessionId = deps.state.currentSessionId;
|
|
@@ -4420,6 +5349,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4420
5349
|
if (!sessionError) sessionError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
4421
5350
|
}
|
|
4422
5351
|
const spawnBudget = getSpawnBudgetSnapshot(deps.state, deps.config, currentSessionId);
|
|
5352
|
+
const activeAsyncCapacity = currentSessionId
|
|
5353
|
+
? getActiveAsyncCapacitySnapshot(currentSessionId, resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession), { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []) })
|
|
5354
|
+
: { used: 0, limit: resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession) ?? 0 };
|
|
5355
|
+
deps.state.activeAsyncCapacity = activeAsyncCapacity;
|
|
4423
5356
|
return {
|
|
4424
5357
|
content: [{
|
|
4425
5358
|
type: "text",
|
|
@@ -4436,18 +5369,23 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4436
5369
|
expandTilde: deps.expandTilde,
|
|
4437
5370
|
})),
|
|
4438
5371
|
}],
|
|
4439
|
-
details: { mode: "management", results: [], spawnBudget },
|
|
5372
|
+
details: { mode: "management", results: [], spawnBudget, activeAsyncCapacity },
|
|
4440
5373
|
};
|
|
4441
5374
|
}
|
|
4442
5375
|
if (action === "status") {
|
|
4443
5376
|
if (!preserveActiveSession) deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
4444
|
-
const withBudget = (result: AgentToolResult<Details>) => withSpawnBudgetStatus(
|
|
4445
|
-
result,
|
|
4446
|
-
deps.state,
|
|
4447
|
-
deps.config,
|
|
4448
|
-
deps.state.currentSessionId,
|
|
4449
|
-
);
|
|
4450
5377
|
const targetRunId = paramsWithResolvedCwd.id ?? paramsWithResolvedCwd.runId;
|
|
5378
|
+
const hasDirectoryTarget = Boolean(paramsWithResolvedCwd.dir);
|
|
5379
|
+
const targetLabel = formatStatusTargetLabel(paramsWithResolvedCwd, targetRunId);
|
|
5380
|
+
const withBudget = (result: AgentToolResult<Details>) => {
|
|
5381
|
+
const budgeted = withSpawnBudgetStatus(result, deps.state, deps.config, deps.state.currentSessionId);
|
|
5382
|
+
return {
|
|
5383
|
+
...budgeted,
|
|
5384
|
+
content: budgeted.content.map((item, index) => index === 0 && item.type === "text"
|
|
5385
|
+
? { ...item, text: `${targetLabel}\n${item.text}` }
|
|
5386
|
+
: item),
|
|
5387
|
+
};
|
|
5388
|
+
};
|
|
4451
5389
|
const nestedScope = nestedResolutionScopeForExecutor(deps);
|
|
4452
5390
|
const sessionRoots = trustedSessionRootsForStatus(ctx, deps);
|
|
4453
5391
|
if (paramsWithResolvedCwd.view === "fleet") {
|
|
@@ -4472,7 +5410,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4472
5410
|
const message = error instanceof Error ? error.message : String(error);
|
|
4473
5411
|
return withBudget({ content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } });
|
|
4474
5412
|
}
|
|
4475
|
-
} else {
|
|
5413
|
+
} else if (!hasDirectoryTarget) {
|
|
4476
5414
|
const foreground = getForegroundControl(deps.state, undefined);
|
|
4477
5415
|
if (foreground && paramsWithResolvedCwd.view !== "transcript") return withBudget(foregroundStatusResult(foreground));
|
|
4478
5416
|
if (foreground && paramsWithResolvedCwd.view === "transcript") {
|
|
@@ -4540,7 +5478,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4540
5478
|
}
|
|
4541
5479
|
}
|
|
4542
5480
|
if (action === "resume") {
|
|
4543
|
-
return resumeAsyncRun(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel }));
|
|
5481
|
+
return resumeAsyncRun(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel, signal }));
|
|
4544
5482
|
}
|
|
4545
5483
|
if (action === "steer") {
|
|
4546
5484
|
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
@@ -4551,6 +5489,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4551
5489
|
try {
|
|
4552
5490
|
const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
|
|
4553
5491
|
const runId = location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir);
|
|
5492
|
+
const directoryStatus = location.asyncDir ? readStatus(location.asyncDir) : null;
|
|
5493
|
+
if (directoryStatus?.mode === "workflow") {
|
|
5494
|
+
const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: directoryStatus.runId || runId, asyncDirRoot: DIRS.async });
|
|
5495
|
+
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
5496
|
+
return steerWorkflowForegroundTarget({ target: route.target, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal });
|
|
5497
|
+
}
|
|
4554
5498
|
if (location.asyncDir) {
|
|
4555
5499
|
const unsupported = externalRunnerControlError(location.asyncDir, "steer");
|
|
4556
5500
|
if (unsupported) return unsupported;
|
|
@@ -4559,6 +5503,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4559
5503
|
state: deps.state,
|
|
4560
5504
|
runId,
|
|
4561
5505
|
message,
|
|
5506
|
+
mode: paramsWithResolvedCwd.mode,
|
|
4562
5507
|
index: paramsWithResolvedCwd.index,
|
|
4563
5508
|
kill: deps.kill,
|
|
4564
5509
|
location,
|
|
@@ -4584,9 +5529,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4584
5529
|
const text = error instanceof Error ? error.message : String(error);
|
|
4585
5530
|
return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
|
|
4586
5531
|
}
|
|
4587
|
-
if (resolved?.kind === "nested") return steerNestedRun(omitUndefinedProperties({ target: resolved, message, index: paramsWithResolvedCwd.index, signal }));
|
|
4588
|
-
if (resolved?.kind === "foreground")
|
|
5532
|
+
if (resolved?.kind === "nested") return steerNestedRun(omitUndefinedProperties({ target: resolved, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal }));
|
|
5533
|
+
if (resolved?.kind === "foreground") {
|
|
5534
|
+
const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, childRunId: resolved.id, asyncDirRoot: DIRS.async });
|
|
5535
|
+
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
5536
|
+
return steerWorkflowForegroundTarget({ target: route.target, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal });
|
|
5537
|
+
}
|
|
4589
5538
|
if (resolved?.kind !== "async") return { content: [{ type: "text", text: `No async run found for '${targetRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5539
|
+
const resolvedStatus = resolved.location.asyncDir ? readStatus(resolved.location.asyncDir) : null;
|
|
5540
|
+
if (resolvedStatus?.mode === "workflow") {
|
|
5541
|
+
const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: resolvedStatus.runId || resolved.id, asyncDirRoot: DIRS.async });
|
|
5542
|
+
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
5543
|
+
return steerWorkflowForegroundTarget({ target: route.target, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal });
|
|
5544
|
+
}
|
|
4590
5545
|
if (resolved.location.asyncDir) {
|
|
4591
5546
|
const unsupported = externalRunnerControlError(resolved.location.asyncDir, "steer");
|
|
4592
5547
|
if (unsupported) return unsupported;
|
|
@@ -4595,6 +5550,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4595
5550
|
state: deps.state,
|
|
4596
5551
|
runId: resolved.id,
|
|
4597
5552
|
message,
|
|
5553
|
+
mode: paramsWithResolvedCwd.mode,
|
|
4598
5554
|
index: paramsWithResolvedCwd.index,
|
|
4599
5555
|
kill: deps.kill,
|
|
4600
5556
|
location: resolved.location,
|
|
@@ -4619,6 +5575,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4619
5575
|
return appendStepToAsyncChain(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel }));
|
|
4620
5576
|
}
|
|
4621
5577
|
if (action.startsWith("schedule.")) {
|
|
5578
|
+
if (!isScheduledRunAction(action)) {
|
|
5579
|
+
return { content: [{ type: "text", text: unknownSubagentActionMessage(action) }], isError: true, details: { mode: "management", results: [] } };
|
|
5580
|
+
}
|
|
4622
5581
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
4623
5582
|
return {
|
|
4624
5583
|
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
@@ -4635,6 +5594,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4635
5594
|
}
|
|
4636
5595
|
return deps.handleScheduledRunAction(paramsWithResolvedCwd, ctx);
|
|
4637
5596
|
}
|
|
5597
|
+
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
5598
|
+
return {
|
|
5599
|
+
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
5600
|
+
isError: true,
|
|
5601
|
+
details: { mode: "management" as const, results: [] },
|
|
5602
|
+
};
|
|
5603
|
+
}
|
|
5604
|
+
if (action === "dismiss") {
|
|
5605
|
+
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
5606
|
+
if (!targetRunId) return { content: [{ type: "text", text: "action='dismiss' requires id." }], isError: true, details: { mode: "management", results: [] } };
|
|
5607
|
+
let resolved: ResolvedSubagentRunId | undefined;
|
|
5608
|
+
try {
|
|
5609
|
+
resolved = resolveSubagentRunId(targetRunId, omitUndefinedProperties({ state: deps.state, nested: nestedResolutionScopeForExecutor(deps) }));
|
|
5610
|
+
} catch (error) {
|
|
5611
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5612
|
+
return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
|
|
5613
|
+
}
|
|
5614
|
+
if (resolved?.kind !== "async") {
|
|
5615
|
+
return { content: [{ type: "text", text: `Run '${targetRunId}' is not a recovered workflow.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5616
|
+
}
|
|
5617
|
+
return dismissRecoveredWorkflow(deps.state, resolved.location);
|
|
5618
|
+
}
|
|
4638
5619
|
if (action === "stop") {
|
|
4639
5620
|
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
4640
5621
|
const workflowController = targetRunId ? deps.state.workflowControllers?.get(targetRunId) : undefined;
|
|
@@ -4646,6 +5627,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4646
5627
|
if (paramsWithResolvedCwd.dir) {
|
|
4647
5628
|
try {
|
|
4648
5629
|
const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
|
|
5630
|
+
const existingStatus = readStatus(location.asyncDir ?? "");
|
|
5631
|
+
if (existingStatus?.mode === "workflow" && existingStatus.state === "running") {
|
|
5632
|
+
return { content: [{ type: "text", text: `Workflow ${existingStatus.runId} is not controlled by this extension runtime; reload recovery cannot stop it safely.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5633
|
+
}
|
|
4649
5634
|
const stopResult = stopAsyncRun(deps.state, location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir), deps.kill, location);
|
|
4650
5635
|
return stopResult ?? { content: [{ type: "text", text: `No running or queued async run was found for '${targetRunId ?? paramsWithResolvedCwd.dir}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
4651
5636
|
} catch (error) {
|
|
@@ -4725,14 +5710,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4725
5710
|
}
|
|
4726
5711
|
if (!(SUBAGENT_ACTIONS as readonly string[]).includes(action)) {
|
|
4727
5712
|
return {
|
|
4728
|
-
content: [{ type: "text", text:
|
|
4729
|
-
isError: true,
|
|
4730
|
-
details: { mode: "management" as const, results: [] },
|
|
4731
|
-
};
|
|
4732
|
-
}
|
|
4733
|
-
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
4734
|
-
return {
|
|
4735
|
-
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
5713
|
+
content: [{ type: "text", text: unknownSubagentActionMessage(action) }],
|
|
4736
5714
|
isError: true,
|
|
4737
5715
|
details: { mode: "management" as const, results: [] },
|
|
4738
5716
|
};
|
|
@@ -4808,7 +5786,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4808
5786
|
const runId = randomUUID().slice(0, 8);
|
|
4809
5787
|
const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
|
|
4810
5788
|
const nestedParentAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
|
|
4811
|
-
const nestedRoute = inheritedNestedRoute ?? createNestedRoute(runId);
|
|
4812
5789
|
const shareEnabled = effectiveParams.share === true;
|
|
4813
5790
|
const hasChain = (effectiveParams.chain?.length ?? 0) > 0;
|
|
4814
5791
|
const hasTasks = (effectiveParams.tasks?.length ?? 0) > 0;
|
|
@@ -4889,12 +5866,53 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4889
5866
|
if (externalAgent && (!effectiveAsync || effectiveParams.foregroundOnly === true)) {
|
|
4890
5867
|
return buildRequestedModeError(effectiveParams, `Agent '${externalAgent.name}' uses runner.type='external-cli', which currently supports async/background execution only. Omit async or pass async:true; clarify and foregroundOnly are unsupported.`);
|
|
4891
5868
|
}
|
|
4892
|
-
const foregroundTimeout =
|
|
5869
|
+
const foregroundTimeout = resolveSingleAgentLaunchTimeout(
|
|
4893
5870
|
effectiveParams,
|
|
4894
|
-
effectiveAsync
|
|
5871
|
+
effectiveAsync,
|
|
5872
|
+
resolveConfigDefaultTimeoutMs(deps.config.timeoutMs),
|
|
4895
5873
|
);
|
|
4896
5874
|
if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
|
|
4897
5875
|
const controlConfig = resolveControlConfig(deps.config.control, effectiveParams.control);
|
|
5876
|
+
const requestedWorkflowChildAsyncId = typeof effectiveParams.workflowChildAsyncId === "string" ? effectiveParams.workflowChildAsyncId.trim() : "";
|
|
5877
|
+
const asyncRunId = requestedWorkflowChildAsyncId && path.basename(requestedWorkflowChildAsyncId) === requestedWorkflowChildAsyncId
|
|
5878
|
+
? requestedWorkflowChildAsyncId
|
|
5879
|
+
: randomUUID();
|
|
5880
|
+
const topLevelAsyncCapacityEligible = depth === 0 && !inheritedNestedRoute && !effectiveParams.workflowParentRunId;
|
|
5881
|
+
const topLevelAsync = effectiveAsync && topLevelAsyncCapacityEligible;
|
|
5882
|
+
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
5883
|
+
if (topLevelAsync) {
|
|
5884
|
+
try {
|
|
5885
|
+
const activeLimit = resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession);
|
|
5886
|
+
activeAsyncCapacity = acquireActiveAsyncCapacity({
|
|
5887
|
+
sessionId: requestSessionId,
|
|
5888
|
+
limit: activeLimit,
|
|
5889
|
+
runId: asyncRunId,
|
|
5890
|
+
kind: "runner",
|
|
5891
|
+
asyncDir: path.join(DIRS.async, asyncRunId),
|
|
5892
|
+
}, { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []) });
|
|
5893
|
+
deps.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(requestSessionId, activeLimit, { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []) });
|
|
5894
|
+
} catch (error) {
|
|
5895
|
+
if (error instanceof ActiveAsyncCapacityError) {
|
|
5896
|
+
deps.state.activeAsyncCapacity = error.snapshot;
|
|
5897
|
+
return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: foregroundMode, results: [], activeAsyncCapacity: error.snapshot } };
|
|
5898
|
+
}
|
|
5899
|
+
throw error;
|
|
5900
|
+
}
|
|
5901
|
+
}
|
|
5902
|
+
|
|
5903
|
+
let runFanoutBudget: RunFanoutBudgetDescriptor;
|
|
5904
|
+
try {
|
|
5905
|
+
const inheritedRunFanoutBudget = effectiveParams.runFanoutBudget ? undefined : decodeRunFanoutBudgetDescriptor(process.env[RUN_FANOUT_BUDGET_ENV]);
|
|
5906
|
+
runFanoutBudget = effectiveParams.runFanoutBudget
|
|
5907
|
+
?? (inheritedRunFanoutBudget ? { ...inheritedRunFanoutBudget, parentPath: `${inheritedRunFanoutBudget.parentPath ? `${inheritedRunFanoutBudget.parentPath}/` : ""}${runId}` } : undefined)
|
|
5908
|
+
?? createRunFanoutBudget(runId, resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
|
|
5909
|
+
if (!effectiveParams.runFanoutAdmitted) claimRunFanoutBatch(runFanoutBudget, staticRunFanoutPaths(effectiveParams));
|
|
5910
|
+
} catch (error) {
|
|
5911
|
+
activeAsyncCapacity?.rollback();
|
|
5912
|
+
if (error instanceof RunFanoutLimitError) return runFanoutErrorResult(error, foregroundMode);
|
|
5913
|
+
return buildRequestedModeError(effectiveParams, error instanceof Error ? error.message : String(error));
|
|
5914
|
+
}
|
|
5915
|
+
const nestedRoute = inheritedNestedRoute ?? createNestedRoute(runId);
|
|
4898
5916
|
|
|
4899
5917
|
const artifactConfig: ArtifactConfig = omitUndefinedProperties({
|
|
4900
5918
|
...DEFAULT_ARTIFACT_CONFIG,
|
|
@@ -4902,6 +5920,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4902
5920
|
dir: deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir,
|
|
4903
5921
|
});
|
|
4904
5922
|
const artifactsDir = getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
5923
|
+
if (artifactConfig.dir === "project" && !warnedArtifactPackageDirs.has(effectiveCwd)) {
|
|
5924
|
+
warnedArtifactPackageDirs.add(effectiveCwd);
|
|
5925
|
+
const warning = getProjectArtifactPackagingWarning(effectiveCwd);
|
|
5926
|
+
if (warning) console.warn(`[pi-subagents] ${warning}`);
|
|
5927
|
+
}
|
|
4905
5928
|
|
|
4906
5929
|
let sessionRoot: string;
|
|
4907
5930
|
if (effectiveParams.sessionDir) {
|
|
@@ -4915,6 +5938,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4915
5938
|
try {
|
|
4916
5939
|
fs.mkdirSync(sessionRoot, { recursive: true });
|
|
4917
5940
|
} catch (error) {
|
|
5941
|
+
activeAsyncCapacity?.rollback();
|
|
4918
5942
|
const message = error instanceof Error ? error.message : String(error);
|
|
4919
5943
|
return toExecutionErrorResult(
|
|
4920
5944
|
effectiveParams,
|
|
@@ -4945,10 +5969,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4945
5969
|
preflightForkSessionsForStaticTasks(effectiveParams, contextPolicy, forkSessionFileForTask, deps.config.chain?.dynamicFanout?.maxItems);
|
|
4946
5970
|
}
|
|
4947
5971
|
} catch (error) {
|
|
5972
|
+
activeAsyncCapacity?.rollback();
|
|
4948
5973
|
return toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary);
|
|
4949
5974
|
}
|
|
4950
5975
|
const chainBindingsError = validateExecutionChainBindings(effectiveParams, deps.config.chain?.dynamicFanout?.maxItems);
|
|
4951
|
-
if (chainBindingsError)
|
|
5976
|
+
if (chainBindingsError) {
|
|
5977
|
+
activeAsyncCapacity?.rollback();
|
|
5978
|
+
return withResolvedContext(chainBindingsError, contextPolicy.contextSummary);
|
|
5979
|
+
}
|
|
4952
5980
|
|
|
4953
5981
|
const onUpdateWithContext = onUpdate
|
|
4954
5982
|
? (r: AgentToolResult<Details>) => onUpdate(withResolvedContext({
|
|
@@ -4968,14 +5996,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4968
5996
|
ownerSessionId: requestSessionId,
|
|
4969
5997
|
});
|
|
4970
5998
|
} catch (error) {
|
|
4971
|
-
if (explicitMission)
|
|
5999
|
+
if (explicitMission) {
|
|
6000
|
+
activeAsyncCapacity?.rollback();
|
|
6001
|
+
return toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary);
|
|
6002
|
+
}
|
|
4972
6003
|
missionWarning = `Mission tracking unavailable: ${error instanceof Error ? error.message : String(error)}`;
|
|
4973
6004
|
}
|
|
4974
6005
|
|
|
4975
6006
|
const attachMission = (result: AgentToolResult<Details>): AgentToolResult<Details> => {
|
|
4976
6007
|
if (!missionBinding) return missionWarning ? { ...result, details: { ...result.details, missionWarning } } : result;
|
|
4977
6008
|
try {
|
|
4978
|
-
return attachMissionToLaunchResult({ binding: missionBinding, result });
|
|
6009
|
+
return attachMissionToLaunchResult({ binding: delegatedExecution ? { ...missionBinding, announceInContent: false } : missionBinding, result });
|
|
4979
6010
|
} catch (error) {
|
|
4980
6011
|
const warning = `Mission tracking unavailable after launch: ${error instanceof Error ? error.message : String(error)}`;
|
|
4981
6012
|
if (explicitMission) {
|
|
@@ -4996,7 +6027,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4996
6027
|
requestSessionId,
|
|
4997
6028
|
requestedSpawns,
|
|
4998
6029
|
);
|
|
4999
|
-
if (reservation.error)
|
|
6030
|
+
if (reservation.error) {
|
|
6031
|
+
activeAsyncCapacity?.rollback();
|
|
6032
|
+
return attachMission(spawnBudgetErrorResult(reservation.error, foregroundMode));
|
|
6033
|
+
}
|
|
5000
6034
|
|
|
5001
6035
|
const execData: ExecutionContextData = omitUndefinedProperties({
|
|
5002
6036
|
params: effectiveParams,
|
|
@@ -5016,6 +6050,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5016
6050
|
artifactsDir,
|
|
5017
6051
|
backgroundRequestedWhileClarifying,
|
|
5018
6052
|
effectiveAsync,
|
|
6053
|
+
asyncRunId,
|
|
5019
6054
|
controlConfig,
|
|
5020
6055
|
intercomBridge,
|
|
5021
6056
|
nestedRoute,
|
|
@@ -5031,17 +6066,34 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5031
6066
|
parentSessionId: requestSessionId,
|
|
5032
6067
|
parentPiSessionId: requestPiSessionId,
|
|
5033
6068
|
capabilityCeiling: resolveCurrentSubagentCapabilityCeiling(requestSessionId),
|
|
6069
|
+
runFanoutBudget,
|
|
6070
|
+
topLevelAsyncCapacityEligible,
|
|
6071
|
+
activeAsyncCapacity,
|
|
5034
6072
|
});
|
|
5035
6073
|
|
|
5036
|
-
const foregroundDescription =
|
|
5037
|
-
|
|
5038
|
-
|
|
6074
|
+
const foregroundDescription = selectedAgentNames.length === 1
|
|
6075
|
+
? `${selectedAgentNames[0]} child`
|
|
6076
|
+
: `${selectedAgentNames.length} live children`;
|
|
6077
|
+
const parentWorkflowStatus = effectiveParams.workflowParentRunId
|
|
6078
|
+
? readStatus(path.join(DIRS.async, effectiveParams.workflowParentRunId))
|
|
6079
|
+
: null;
|
|
6080
|
+
const workflowSteeringDir = effectiveParams.workflowParentRunId
|
|
6081
|
+
&& requestSessionId
|
|
6082
|
+
&& deps.state.workflowControllers?.has(effectiveParams.workflowParentRunId)
|
|
6083
|
+
&& parentWorkflowStatus?.mode === "workflow"
|
|
6084
|
+
&& (parentWorkflowStatus.state === "running" || parentWorkflowStatus.state === "queued")
|
|
6085
|
+
&& parentWorkflowStatus.sessionId === requestSessionId
|
|
6086
|
+
? workflowForegroundSteeringDir(DIRS.async, effectiveParams.workflowParentRunId, runId)
|
|
6087
|
+
: undefined;
|
|
5039
6088
|
const foregroundControl: ForegroundRunControl | undefined = effectiveAsync
|
|
5040
6089
|
? undefined
|
|
5041
6090
|
: compactOptional<ForegroundRunControl>({
|
|
5042
6091
|
runId,
|
|
5043
6092
|
sessionId: requestSessionId,
|
|
5044
6093
|
mode: foregroundMode,
|
|
6094
|
+
...(effectiveParams.workflowParentRunId ? { parentWorkflowRunId: effectiveParams.workflowParentRunId } : {}),
|
|
6095
|
+
...(effectiveParams.workflowKey ? { workflowKey: effectiveParams.workflowKey } : {}),
|
|
6096
|
+
workflowSteeringDir,
|
|
5045
6097
|
startedAt: Date.now(),
|
|
5046
6098
|
updatedAt: Date.now(),
|
|
5047
6099
|
cwd: effectiveCwd,
|
|
@@ -5056,6 +6108,37 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5056
6108
|
interrupt: undefined,
|
|
5057
6109
|
});
|
|
5058
6110
|
if (foregroundControl) {
|
|
6111
|
+
foregroundControl.promptAuditRedo = async (index, guidance) => {
|
|
6112
|
+
const audit = getLivePromptAudit(foregroundControl, index);
|
|
6113
|
+
if (!audit?.rerun) return { text: "Redo is not safe for this prompt in this slice.", isError: true };
|
|
6114
|
+
const rewrittenTask = await rewritePromptWithGuidance({
|
|
6115
|
+
ctx,
|
|
6116
|
+
authoredTask: audit.authoredTask,
|
|
6117
|
+
runtimeAdditions: audit.runtimeAdditions,
|
|
6118
|
+
finalEffectivePrompt: audit.finalEffectivePrompt,
|
|
6119
|
+
guidance,
|
|
6120
|
+
signal,
|
|
6121
|
+
});
|
|
6122
|
+
const redoParams = promptAuditRedoParams(audit.rerun.params, rewrittenTask);
|
|
6123
|
+
const previousForegroundId = deps.state.lastForegroundControlId;
|
|
6124
|
+
const launch = execute(randomUUID(), redoParams, signal, undefined, ctx, true);
|
|
6125
|
+
const newRunId = deps.state.lastForegroundControlId && deps.state.lastForegroundControlId !== previousForegroundId
|
|
6126
|
+
? deps.state.lastForegroundControlId
|
|
6127
|
+
: undefined;
|
|
6128
|
+
if (!newRunId) {
|
|
6129
|
+
const result = await launch;
|
|
6130
|
+
return { text: result.content.find((item) => item.type === "text")?.text ?? "Prompt redo could not start.", isError: true };
|
|
6131
|
+
}
|
|
6132
|
+
foregroundControl.supersededByRunId = newRunId;
|
|
6133
|
+
const replacement = deps.state.foregroundControls.get(newRunId);
|
|
6134
|
+
if (replacement) replacement.sourceRunId = foregroundControl.runId;
|
|
6135
|
+
void launch.then((result) => {
|
|
6136
|
+
if (result.isError) console.warn(`[pi-subagents] Prompt redo ${newRunId} failed: ${result.content.find((item) => item.type === "text")?.text ?? "unknown error"}`);
|
|
6137
|
+
}).catch((error) => {
|
|
6138
|
+
console.warn(`[pi-subagents] Prompt redo ${newRunId} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
6139
|
+
});
|
|
6140
|
+
return { text: `Prompt redo started ${newRunId}.` };
|
|
6141
|
+
};
|
|
5059
6142
|
deps.state.foregroundControls.set(runId, foregroundControl);
|
|
5060
6143
|
deps.state.lastForegroundControlId = runId;
|
|
5061
6144
|
}
|
|
@@ -5147,32 +6230,46 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5147
6230
|
|
|
5148
6231
|
let nestedForegroundStarted = false;
|
|
5149
6232
|
try {
|
|
6233
|
+
if (workflowLaunchObserver) {
|
|
6234
|
+
const singleTask = hasTasks && effectiveParams.tasks?.length === 1 ? effectiveParams.tasks[0] : undefined;
|
|
6235
|
+
const launch = hasSingle
|
|
6236
|
+
? { agent: effectiveParams.agent!, sessionFile: childSessionFileForTask(effectiveParams.agent!, 0, effectiveParams.model), async: effectiveAsync, ...(effectiveAsync ? { runId: asyncRunId } : {}) }
|
|
6237
|
+
: singleTask
|
|
6238
|
+
? { agent: singleTask.agent, sessionFile: childSessionFileForTask(singleTask.agent, 0, singleTask.model), async: effectiveAsync, ...(effectiveAsync ? { runId: asyncRunId } : {}) }
|
|
6239
|
+
: undefined;
|
|
6240
|
+
if (launch) {
|
|
6241
|
+
workflowLaunchObservers.delete(params);
|
|
6242
|
+
workflowLaunchObserver(launch);
|
|
6243
|
+
}
|
|
6244
|
+
}
|
|
5150
6245
|
const asyncResult = runAsyncPath(execData, deps);
|
|
5151
|
-
if (asyncResult) return attachMission(withResolvedContext(withForkThinkingNotes(asyncResult, forkThinkingDowngrades), contextPolicy.contextSummary));
|
|
6246
|
+
if (asyncResult) return attachMission(withRunFanoutBudget(withResolvedContext(withForkThinkingNotes(asyncResult, forkThinkingDowngrades), contextPolicy.contextSummary), runFanoutBudget));
|
|
5152
6247
|
if (foregroundControl) {
|
|
5153
6248
|
writeNestedForegroundEvent("subagent.nested.started");
|
|
5154
6249
|
nestedForegroundStarted = true;
|
|
5155
6250
|
}
|
|
6251
|
+
const runFanoutAnnotateContent = !delegatedExecution;
|
|
5156
6252
|
if (hasChain && effectiveParams.chain) {
|
|
5157
6253
|
const result = await runChainPath(execData, deps);
|
|
5158
6254
|
writeNestedForegroundEvent("subagent.nested.completed", result);
|
|
5159
|
-
return attachMission(withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary));
|
|
6255
|
+
return attachMission(withRunFanoutBudget(withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary), runFanoutBudget, { annotateContent: runFanoutAnnotateContent }));
|
|
5160
6256
|
}
|
|
5161
6257
|
if (hasTasks && effectiveParams.tasks) {
|
|
5162
6258
|
const result = await runParallelPath(execData, deps);
|
|
5163
6259
|
writeNestedForegroundEvent("subagent.nested.completed", result);
|
|
5164
|
-
return attachMission(withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary));
|
|
6260
|
+
return attachMission(withRunFanoutBudget(withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary), runFanoutBudget, { annotateContent: runFanoutAnnotateContent }));
|
|
5165
6261
|
}
|
|
5166
6262
|
if (hasSingle) {
|
|
5167
6263
|
const result = await runSinglePath(execData, deps);
|
|
5168
6264
|
writeNestedForegroundEvent("subagent.nested.completed", result);
|
|
5169
|
-
return attachMission(withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary));
|
|
6265
|
+
return attachMission(withRunFanoutBudget(withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary), runFanoutBudget, { annotateContent: runFanoutAnnotateContent }));
|
|
5170
6266
|
}
|
|
5171
6267
|
} catch (error) {
|
|
5172
6268
|
const errorResult = withForkThinkingNotes(toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary), forkThinkingDowngrades);
|
|
5173
6269
|
if (nestedForegroundStarted) writeNestedForegroundEvent("subagent.nested.completed", errorResult);
|
|
5174
6270
|
return attachMission(errorResult);
|
|
5175
6271
|
} finally {
|
|
6272
|
+
if (activeAsyncCapacity && !activeAsyncCapacity.owner.runnerStartedAt) activeAsyncCapacity.rollback();
|
|
5176
6273
|
if (foregroundControl) {
|
|
5177
6274
|
settleForegroundSchedulingOwner(foregroundControl);
|
|
5178
6275
|
removeForegroundControlIfIdle(deps.state, runId);
|
|
@@ -5193,8 +6290,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5193
6290
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
5194
6291
|
ctx: ExtensionContext,
|
|
5195
6292
|
): Promise<AgentToolResult<Details>> => {
|
|
5196
|
-
const
|
|
5197
|
-
|
|
6293
|
+
const normalizedAction = typeof params.action === "string" ? params.action.trim() : params.action;
|
|
6294
|
+
const requestParams = normalizedAction ? { ...params, action: normalizedAction } : params;
|
|
6295
|
+
if (normalizedAction) return execute(id, requestParams, signal, onUpdate, ctx);
|
|
5198
6296
|
const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth);
|
|
5199
6297
|
const dispatchParams = applyForceTopLevelAsyncOverride(requestParams, depth, deps.config.forceTopLevelAsync === true);
|
|
5200
6298
|
const runsForeground = dispatchParams.clarify === true || (dispatchParams.async ?? deps.asyncByDefault) !== true;
|
|
@@ -5208,6 +6306,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5208
6306
|
}
|
|
5209
6307
|
};
|
|
5210
6308
|
|
|
6309
|
+
const executePublic = (
|
|
6310
|
+
id: string,
|
|
6311
|
+
params: SubagentParamsLike,
|
|
6312
|
+
signal: AbortSignal,
|
|
6313
|
+
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
6314
|
+
ctx: ExtensionContext,
|
|
6315
|
+
): Promise<AgentToolResult<Details>> => {
|
|
6316
|
+
const normalized = normalizePublicSubagentExecution(params);
|
|
6317
|
+
if (!normalized.ok) {
|
|
6318
|
+
return Promise.resolve({ content: [{ type: "text", text: normalized.error }], isError: true, details: { mode: normalized.mode, results: [] } });
|
|
6319
|
+
}
|
|
6320
|
+
return executeWithSingleDispatchGuard(id, normalized.params, signal, onUpdate, ctx);
|
|
6321
|
+
};
|
|
6322
|
+
|
|
5211
6323
|
const executeDelegated = async (
|
|
5212
6324
|
id: string,
|
|
5213
6325
|
params: SubagentParamsLike,
|
|
@@ -5226,6 +6338,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5226
6338
|
delete privateParams.delegatedAllowZeroToolBudget;
|
|
5227
6339
|
if (thinkingOverride !== undefined) delegatedThinkingOverrides.set(delegatedParams, thinkingOverride);
|
|
5228
6340
|
if (allowZeroToolBudget) delegatedZeroToolBudgets.add(delegatedParams);
|
|
6341
|
+
delegatedExecutions.add(delegatedParams);
|
|
5229
6342
|
return execute(id, delegatedParams, signal, onUpdate, ctx);
|
|
5230
6343
|
};
|
|
5231
6344
|
|
|
@@ -5247,5 +6360,5 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5247
6360
|
return ownerExecutor.execute(id, params, signal, undefined, ctx);
|
|
5248
6361
|
};
|
|
5249
6362
|
|
|
5250
|
-
return { execute: executeWithSingleDispatchGuard, executeDelegated, executeScheduled };
|
|
6363
|
+
return { execute: executeWithSingleDispatchGuard, executePublic, executeDelegated, executeScheduled };
|
|
5251
6364
|
}
|