@selesai/code 0.9.10 → 0.9.12
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 +271 -255
- package/dist/cli/args.js +13 -2
- package/dist/cli/args.test.js +8 -0
- package/dist/cli/credential-print.js +4 -4
- package/dist/cli.js +0 -0
- package/dist/config.d.ts +4 -6
- package/dist/config.js +10 -14
- package/dist/core/agent-session-auto-handoff.test.js +24 -1
- package/dist/core/agent-session.d.ts +10 -4
- package/dist/core/agent-session.js +61 -46
- package/dist/core/compaction/branch-summarization.js +30 -30
- package/dist/core/compaction/compaction.js +81 -81
- package/dist/core/compaction/utils.js +2 -2
- package/dist/core/defaults.d.ts +1 -0
- package/dist/core/defaults.js +9 -0
- package/dist/core/export-html/template.css +1066 -1066
- package/dist/core/export-html/template.html +55 -55
- package/dist/core/export-html/template.js +1864 -1864
- package/dist/core/export-html/vendor/highlight.min.js +1212 -1212
- package/dist/core/export-html/vendor/marked.min.js +78 -78
- package/dist/core/extensions/loader.js +108 -35
- package/dist/core/extensions/loader.test.js +21 -0
- package/dist/core/extensions/types.d.ts +12 -4
- package/dist/core/keybindings.d.ts +2 -2
- package/dist/core/messages.js +7 -7
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.js +10 -4
- package/dist/core/package-manager.js +4 -4
- package/dist/core/sdk.d.ts +5 -5
- package/dist/core/sdk.js +16 -4
- package/dist/core/settings-manager.d.ts +6 -2
- package/dist/core/settings-manager.js +22 -3
- package/dist/core/tools/bash.d.ts +18 -1
- package/dist/core/tools/bash.js +41 -23
- package/dist/core/tools/index.d.ts +4 -2
- package/dist/core/tools/index.js +9 -2
- package/dist/core/tools/powershell.d.ts +15 -0
- package/dist/core/tools/powershell.js +38 -0
- package/dist/core/tools/powershell.test.d.ts +1 -0
- package/dist/core/tools/powershell.test.js +20 -0
- package/dist/defaults/settings.json +0 -1
- package/dist/extensions/context-compaction-reminder.test.ts +82 -82
- package/dist/extensions/context-compaction-reminder.ts +28 -28
- package/dist/extensions/model-prompt-injector/index.test.ts +0 -10
- package/dist/extensions/model-prompt-injector/index.ts +0 -3
- package/dist/extensions/pi-intercom/CHANGELOG.md +53 -16
- package/dist/extensions/pi-intercom/LICENSE +21 -21
- package/dist/extensions/pi-intercom/README.md +41 -8
- package/dist/extensions/pi-intercom/banner.png +0 -0
- package/dist/extensions/pi-intercom/broker/broker.ts +498 -215
- package/dist/extensions/pi-intercom/broker/client.test.ts +83 -83
- package/dist/extensions/pi-intercom/broker/client.ts +39 -12
- package/dist/extensions/pi-intercom/broker/extension.test.ts +486 -387
- package/dist/extensions/pi-intercom/broker/framing.test.ts +114 -114
- package/dist/extensions/pi-intercom/broker/paths.test.ts +153 -153
- package/dist/extensions/pi-intercom/broker/paths.ts +134 -134
- package/dist/extensions/pi-intercom/broker/protocol.ts +26 -0
- package/dist/extensions/pi-intercom/broker/runtime-claim.test.ts +34 -34
- package/dist/extensions/pi-intercom/broker/runtime-claim.ts +21 -21
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +5 -20
- package/dist/extensions/pi-intercom/config.test.ts +41 -0
- package/dist/extensions/pi-intercom/config.ts +21 -0
- package/dist/extensions/pi-intercom/cwd.test.ts +40 -40
- package/dist/extensions/pi-intercom/cwd.ts +31 -31
- package/dist/extensions/pi-intercom/extension-api.ts +84 -44
- package/dist/extensions/pi-intercom/format-context.test.ts +31 -31
- package/dist/extensions/pi-intercom/format-context.ts +32 -32
- package/dist/extensions/pi-intercom/index.ts +362 -26
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +819 -13
- package/dist/extensions/pi-intercom/package.json +1 -1
- package/dist/extensions/pi-intercom/project-agent.ts +2 -2
- package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +8 -110
- package/dist/extensions/pi-intercom/test/overlay-width.test.ts +66 -66
- package/dist/extensions/pi-intercom/types.ts +32 -5
- package/dist/extensions/pi-intercom/ui/compose.ts +143 -143
- package/dist/extensions/pi-intercom/ui/inline-message.ts +6 -0
- package/dist/extensions/pi-intercom/ui/session-list.ts +166 -166
- package/dist/extensions/pi-powerline-footer/bash-mode/shell-session.ts +286 -286
- package/dist/extensions/pi-powerline-footer/bash-mode/transcript.ts +108 -108
- package/dist/extensions/pi-powerline-footer/separators.ts +57 -57
- package/dist/extensions/pi-powerline-footer/tests/session-usage.test.ts +47 -47
- package/dist/extensions/pi-powerline-footer/tests/tps.test.ts +39 -39
- package/dist/extensions/pi-powerline-footer/theme.example.json +24 -24
- package/dist/extensions/pi-powerline-footer/theme.json +12 -12
- package/dist/extensions/pi-powerline-footer/tps.ts +345 -345
- package/dist/extensions/pi-rewind-hook/README.md +245 -245
- package/dist/extensions/pi-rewind-hook/index.ts +1445 -1445
- package/dist/extensions/pi-rewind-hook/package.json +29 -29
- package/dist/extensions/pi-subagents/.github/assets/subagent-fleet-inspector.png +0 -0
- package/dist/extensions/pi-subagents/.github/workflows/release.yml +28 -0
- package/dist/extensions/pi-subagents/.github/workflows/test.yml +51 -0
- package/dist/extensions/pi-subagents/.greptile/config.json +33 -0
- package/dist/extensions/pi-subagents/.greptile/files.json +8 -0
- package/dist/extensions/pi-subagents/.greptile/rules.md +16 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +309 -87
- package/dist/extensions/pi-subagents/README.md +15 -17
- package/dist/extensions/pi-subagents/agents/oracle.md +3 -1
- package/dist/extensions/pi-subagents/agents/researcher.md +32 -13
- package/dist/extensions/pi-subagents/agents/reviewer.md +13 -2
- package/dist/extensions/pi-subagents/agents/scout.md +2 -2
- package/dist/extensions/pi-subagents/agents/worker.md +2 -1
- package/dist/extensions/pi-subagents/async-retention-discovery-worker.mjs +180 -0
- package/dist/extensions/pi-subagents/docs/configuration.md +106 -27
- package/dist/extensions/pi-subagents/docs/extension-api.md +85 -21
- package/dist/extensions/pi-subagents/docs/missions.md +41 -4
- package/dist/extensions/pi-subagents/docs/models.md +52 -9
- package/dist/extensions/pi-subagents/docs/observability.md +103 -16
- package/dist/extensions/pi-subagents/docs/tool-reference.md +60 -45
- package/dist/extensions/pi-subagents/docs/watchdog.md +60 -3
- package/dist/extensions/pi-subagents/docs/workflows.md +181 -7
- package/dist/extensions/pi-subagents/index.ts +10 -1
- package/dist/extensions/pi-subagents/install.mjs +4 -4
- package/dist/extensions/pi-subagents/package-lock.json +1483 -1470
- package/dist/extensions/pi-subagents/package.json +6 -3
- package/dist/extensions/pi-subagents/prompts/council.md +60 -0
- package/dist/extensions/pi-subagents/prompts/parallel-review.md +5 -1
- package/dist/extensions/pi-subagents/prompts/review-loop.md +9 -5
- package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +254 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +18 -17
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +31 -9
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +107 -140
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +9 -12
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +39 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +44 -92
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +261 -368
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +6 -2
- package/dist/extensions/pi-subagents/src/agents/agents.ts +388 -85
- package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +14 -0
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +418 -0
- package/dist/extensions/pi-subagents/src/agents/skills.ts +1 -1
- package/dist/extensions/pi-subagents/src/api/agents.ts +7 -0
- package/dist/extensions/pi-subagents/src/api/external-job-provider.ts +195 -0
- package/dist/extensions/pi-subagents/src/api/preflight.ts +88 -27
- package/dist/extensions/pi-subagents/src/api/project-panes.ts +2 -0
- package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +20 -11
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +14 -6
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -4
- package/dist/extensions/pi-subagents/src/extension/index.ts +270 -187
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +65 -28
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +120 -11
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +46 -64
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +51 -37
- package/dist/extensions/pi-subagents/src/extension/tool-result.ts +19 -0
- package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -2
- package/dist/extensions/pi-subagents/src/inspectors/herdr/client.ts +3 -3
- package/dist/extensions/pi-subagents/src/inspectors/herdr/focus.ts +55 -0
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -1
- package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +230 -46
- package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +26 -4
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +32 -10
- package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +6 -1
- package/dist/extensions/pi-subagents/src/missions/store.ts +4 -9
- package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +2 -2
- package/dist/extensions/pi-subagents/src/policy/authority.ts +2 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +31 -8
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +210 -52
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +63 -33
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +71 -17
- package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +886 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +14 -5
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +49 -56
- package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +1 -0
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +3 -33
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +5 -0
- package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +5 -1
- package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +22 -12
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +105 -71
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +68 -19
- package/dist/extensions/pi-subagents/src/runs/background/index-segment.ts +59 -0
- package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +443 -0
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +94 -10
- package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +158 -90
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +141 -53
- package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +8 -5
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +14 -4
- package/dist/extensions/pi-subagents/src/runs/background/run-id-query.ts +7 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +13 -14
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +46 -25
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +823 -0
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +9 -3
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +11 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +594 -216
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +17 -13
- package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +138 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +23 -4
- package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +82 -4
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +5 -3
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +21 -14
- package/dist/extensions/pi-subagents/src/runs/foreground/async-stop-action.ts +23 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +153 -58
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +7 -9
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1283 -1657
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +274 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +6 -5
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +59 -10
- package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/chain-outputs.ts +1 -3
- package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +36 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +52 -2
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/extension-bindings.ts +78 -0
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/external-job-bridge.ts +460 -0
- package/dist/extensions/pi-subagents/src/runs/shared/external-job-runner.ts +389 -0
- package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +10 -0
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +14 -0
- package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +243 -0
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +201 -85
- package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +106 -39
- package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +145 -0
- package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +150 -27
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +46 -4
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +19 -15
- package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +5 -1
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +86 -2
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +9 -9
- package/dist/extensions/pi-subagents/src/runs/shared/session-lease.ts +0 -6
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +18 -4
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +51 -7
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +62 -21
- package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +1 -3
- package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +1 -3
- package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +1 -23
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +14 -2
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +2 -2
- package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +22 -2
- package/dist/extensions/pi-subagents/src/shared/capacity-resilient-json.ts +102 -0
- package/dist/extensions/pi-subagents/src/shared/completion-owner.ts +14 -0
- package/dist/extensions/pi-subagents/src/shared/file-system-retry.ts +49 -1
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +59 -1
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +12 -5
- package/dist/extensions/pi-subagents/src/shared/model-info.ts +20 -0
- package/dist/extensions/pi-subagents/src/shared/prompt-resources.ts +0 -40
- package/dist/extensions/pi-subagents/src/shared/settings.ts +11 -30
- package/dist/extensions/pi-subagents/src/shared/thinking-ceiling.ts +52 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +228 -49
- package/dist/extensions/pi-subagents/src/shared/utils.ts +11 -3
- package/dist/extensions/pi-subagents/src/shared/watch-strategy.ts +10 -0
- package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +0 -162
- package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +2 -5
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +105 -13
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +6 -36
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +829 -0
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +1415 -0
- package/dist/extensions/pi-subagents/src/tui/render.ts +78 -7
- package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +1 -1
- package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +1 -0
- package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +59 -51
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +8 -2
- package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +2 -1
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +4 -4
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +471 -86
- package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +140 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
- 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 +1 -1
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +0 -54
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1053 -414
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +194 -4
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +59 -4
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +0 -77
- package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +107 -6
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +211 -897
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +265 -375
- package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +77 -7
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +10 -10
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +547 -30
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +2112 -310
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +274 -34
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +20 -3
- package/dist/extensions/pi-subagents/test/support/isolated-temp-root.mjs +9 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +30 -5
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +2 -0
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +4 -2
- package/dist/extensions/pi-subagents/test/support/register-loader.mjs +1 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +197 -0
- package/dist/extensions/pi-subagents/test/unit/agent-contract.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +204 -27
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +444 -145
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +256 -4
- package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +9 -7
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +152 -22
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +29 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +205 -23
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +579 -0
- package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +24 -0
- package/dist/extensions/pi-subagents/test/unit/authority-policy.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/capacity-resilient-json.test.ts +155 -0
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +7 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +123 -0
- package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +81 -3
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +109 -7
- package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +5 -2
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +3 -2
- package/dist/extensions/pi-subagents/test/unit/extension-bindings.test.ts +79 -0
- package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/external-job-runner.test.ts +961 -0
- package/dist/extensions/pi-subagents/test/unit/file-system-retry.test.ts +64 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +1110 -0
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +1664 -0
- package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +81 -1
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +138 -11
- package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +41 -2
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +210 -79
- package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +95 -0
- package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +596 -0
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +10 -1
- package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +3 -5
- package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +167 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +319 -18
- package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +78 -0
- package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +19 -5
- package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +44 -0
- package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +129 -0
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +231 -2
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +28 -1
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +106 -2
- package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +204 -7
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +59 -4
- package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +2 -6
- package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +34 -2
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +116 -6
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +12 -3
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +56 -61
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +416 -1
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +9 -2
- package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +42 -0
- package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +21 -30
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +86 -11
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +49 -1
- package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +233 -5
- package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +28 -5
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +323 -20
- package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +66 -10
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +68 -5
- package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +174 -0
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +679 -0
- package/dist/extensions/pi-subagents/test/unit/scheduled-store-root.test.ts +67 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +55 -44
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +399 -84
- package/dist/extensions/pi-subagents/test/unit/session-lease.test.ts +13 -0
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +15 -10
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +11 -7
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -0
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +40 -1
- package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +10 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +38 -3
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +110 -12
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +204 -19
- package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +25 -0
- package/dist/extensions/pi-subagents/test/unit/thinking-ceiling.test.ts +73 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +90 -50
- package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +1 -2
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +234 -7
- package/dist/extensions/pi-subagents/test/unit/watch-strategy.test.ts +24 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-permission-arbiter.test.ts +36 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +37 -9
- package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +302 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +167 -7
- package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +113 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +6 -6
- package/dist/extensions/pi-web-agent/package.json +31 -31
- package/dist/extensions/pi-web-agent/src/backends/config.ts +205 -205
- package/dist/extensions/pi-web-agent/src/backends/doctor.ts +136 -136
- package/dist/extensions/pi-web-agent/src/backends/factory.ts +152 -152
- package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +25 -25
- package/dist/extensions/pi-web-agent/src/cache/ttl-cache.ts +28 -28
- package/dist/extensions/pi-web-agent/src/changelog-notice.ts +136 -136
- package/dist/extensions/pi-web-agent/src/commands/web-agent-config.ts +946 -946
- package/dist/extensions/pi-web-agent/src/extension.ts +126 -126
- package/dist/extensions/pi-web-agent/src/extract/readability.ts +118 -118
- package/dist/extensions/pi-web-agent/src/fetch/browser-resolution.ts +199 -199
- package/dist/extensions/pi-web-agent/src/fetch/firecrawl-fetch.ts +100 -100
- package/dist/extensions/pi-web-agent/src/fetch/headless-fetch.ts +117 -117
- package/dist/extensions/pi-web-agent/src/fetch/http-fetch.ts +67 -67
- package/dist/extensions/pi-web-agent/src/orchestration/answer-synthesizer.ts +60 -60
- package/dist/extensions/pi-web-agent/src/orchestration/candidate-selector.ts +50 -50
- package/dist/extensions/pi-web-agent/src/orchestration/direct-url.ts +52 -52
- package/dist/extensions/pi-web-agent/src/orchestration/evidence-quality.ts +105 -105
- package/dist/extensions/pi-web-agent/src/orchestration/evidence-ranker.ts +45 -45
- package/dist/extensions/pi-web-agent/src/orchestration/index.ts +28 -28
- package/dist/extensions/pi-web-agent/src/orchestration/query-planner.ts +47 -47
- package/dist/extensions/pi-web-agent/src/orchestration/research-orchestrator.ts +376 -376
- package/dist/extensions/pi-web-agent/src/orchestration/research-types.ts +64 -64
- package/dist/extensions/pi-web-agent/src/orchestration/research-worker.ts +181 -181
- package/dist/extensions/pi-web-agent/src/orchestration/source-profile.ts +101 -101
- package/dist/extensions/pi-web-agent/src/orchestration/stop-decider.ts +81 -81
- package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +210 -210
- package/dist/extensions/pi-web-agent/src/presentation/config.ts +75 -75
- package/dist/extensions/pi-web-agent/src/presentation/explore-presentation.ts +61 -61
- package/dist/extensions/pi-web-agent/src/presentation/fetch-presentation.ts +54 -54
- package/dist/extensions/pi-web-agent/src/presentation/search-presentation.ts +41 -41
- package/dist/extensions/pi-web-agent/src/presentation/select-view.ts +20 -20
- package/dist/extensions/pi-web-agent/src/presentation/types.ts +63 -63
- package/dist/extensions/pi-web-agent/src/search/brave.ts +114 -114
- package/dist/extensions/pi-web-agent/src/search/duckduckgo.ts +72 -72
- package/dist/extensions/pi-web-agent/src/search/searxng.ts +96 -96
- package/dist/extensions/pi-web-agent/src/tools/web-explore.ts +71 -71
- package/dist/extensions/pi-web-agent/src/tools/web-fetch-headless.ts +31 -31
- package/dist/extensions/pi-web-agent/src/tools/web-fetch.ts +31 -31
- package/dist/extensions/pi-web-agent/src/tools/web-search.ts +157 -157
- package/dist/extensions/pi-web-agent/src/types.ts +88 -88
- package/dist/extensions/ponytail/index.js +31 -25
- package/dist/extensions/ponytail/package.json +8 -8
- package/dist/extensions/ponytail/ponytail-config.cjs +34 -7
- package/dist/extensions/ponytail/ponytail-instructions.cjs +7 -9
- package/dist/extensions/ponytail/test/extension.test.js +7 -8
- package/dist/extensions/ponytail/test/helpers.test.js +1 -1
- package/dist/extensions/question/batch.ts +103 -103
- package/dist/extensions/question/constants.ts +30 -30
- package/dist/extensions/question/helpers.ts +58 -58
- package/dist/extensions/question/navigation.ts +14 -14
- package/dist/extensions/question/package.json +19 -19
- package/dist/extensions/question/schemas.ts +43 -43
- package/dist/extensions/question/selection-mode.ts +46 -46
- package/dist/extensions/question/shortcuts.ts +44 -44
- package/dist/extensions/question/types.ts +132 -132
- package/dist/extensions/test-resolve-hook-impl.mjs +6 -6
- package/dist/extensions/test-resolve-hook.mjs +6 -6
- package/dist/extensions/web-agent-onboarding.ts +222 -222
- package/dist/extensions/workflow/package.json +17 -17
- package/dist/package-manager-cli.js +71 -71
- package/dist/rpc-entry.js +0 -0
- package/dist/skills/agent-browser/SKILL.md +52 -52
- package/dist/skills/batch-grill-me/SKILL.md +19 -19
- package/dist/skills/brandkit/SKILL.md +799 -0
- package/dist/skills/design-taste-frontend/SKILL.md +1207 -0
- package/dist/skills/design-taste-frontend-v1/SKILL.md +227 -0
- package/dist/skills/full-output-enforcement/SKILL.md +50 -0
- package/dist/skills/gpt-taste/SKILL.md +75 -0
- package/dist/skills/grill-me/SKILL.md +10 -10
- package/dist/skills/handoff/SKILL.md +16 -16
- package/dist/skills/handoff-text/SKILL.md +14 -14
- package/dist/skills/high-end-visual-design/SKILL.md +99 -0
- package/dist/skills/image-to-code/SKILL.md +1229 -0
- package/dist/skills/imagegen-frontend-mobile/SKILL.md +1466 -0
- package/dist/skills/imagegen-frontend-web/SKILL.md +988 -0
- package/dist/skills/implanger/SKILL.md +69 -69
- package/dist/skills/improve-codebase/REFERENCE.md +78 -78
- package/dist/skills/improve-codebase/SKILL.md +178 -178
- package/dist/skills/industrial-brutalist-ui/SKILL.md +93 -0
- package/dist/skills/minimalist-ui/SKILL.md +86 -0
- package/dist/skills/pi-subagents/SKILL.md +18 -17
- package/dist/skills/pi-subagents/references/constraints-and-recipes.md +31 -9
- package/dist/skills/pi-subagents/references/execution-controls.md +95 -143
- package/dist/skills/pi-subagents/references/management-authoring-rpc.md +9 -12
- package/dist/skills/pi-subagents/references/multi-lane-orchestration.md +39 -0
- package/dist/skills/pi-subagents/references/prompting-and-roles.md +45 -94
- package/dist/skills/planger/SKILL.md +166 -166
- package/dist/skills/ponytail/SKILL.md +120 -116
- package/dist/skills/ponytail-audit/SKILL.md +41 -42
- package/dist/skills/ponytail-debt/SKILL.md +44 -45
- package/dist/skills/ponytail-gain/SKILL.md +50 -51
- package/dist/skills/ponytail-help/SKILL.md +71 -70
- package/dist/skills/ponytail-review/SKILL.md +57 -58
- package/dist/skills/redesign-existing-projects/SKILL.md +179 -0
- package/dist/skills/selesai-handoff/SKILL.md +20 -20
- package/dist/skills/stitch-design-taste/SKILL.md +185 -0
- package/dist/skills/unlazy/CHANGELOG.md +100 -0
- package/dist/skills/unlazy/CONTRIBUTING.md +66 -0
- package/dist/skills/unlazy/LICENSE +21 -0
- package/dist/skills/unlazy/README.md +242 -0
- package/dist/skills/unlazy/SECURITY.md +70 -0
- package/dist/skills/unlazy/SKILL.md +100 -0
- package/dist/skills/unlazy/package.json +13 -0
- package/dist/skills/unlazy/references/dispatch.md +82 -0
- package/dist/skills/unlazy/references/gates.md +132 -0
- package/dist/skills/unlazy/references/method.md +49 -0
- package/dist/skills/unlazy/references/orchestration.md +99 -0
- package/dist/skills/unlazy/references/parallel.md +123 -0
- package/dist/skills/unlazy/references/token-economy.md +39 -0
- package/dist/skills/unlazy/research/validation-protocol.md +81 -0
- package/dist/skills/unlazy/scripts/dispatch-check.mjs +108 -0
- package/dist/skills/unlazy/scripts/gate-check.mjs +894 -0
- package/dist/skills/unlazy/scripts/gate-lint.mjs +178 -0
- package/dist/skills/unlazy/scripts/install-hooks.mjs +206 -0
- package/dist/skills/unlazy/scripts/lib/check-supervisor.mjs +46 -0
- package/dist/skills/unlazy/scripts/lib/dispatch.mjs +283 -0
- package/dist/skills/unlazy/scripts/lib/gates.mjs +629 -0
- package/dist/skills/unlazy/scripts/lib/process-tree.mjs +161 -0
- package/dist/skills/unlazy/scripts/lib/regex-worker.mjs +9 -0
- package/dist/skills/unlazy/scripts/stop-hook.mjs +174 -0
- package/dist/skills/unlazy/templates/PLAN.md +65 -0
- package/dist/skills/unlazy/templates/gates-leaf.md +51 -0
- package/dist/skills/unlazy/templates/gates-node.md +50 -0
- package/dist/skills/unlazy/tests/contract-tests.mjs +127 -0
- package/dist/skills/unlazy/tests/dispatch-tests.mjs +481 -0
- package/dist/skills/unlazy/tests/hardening-tests.mjs +903 -0
- package/dist/skills/unlazy/tests/lint-tests.mjs +242 -0
- package/dist/skills/unlazy/tests/run-tests.mjs +554 -0
- package/dist/skills/unlazy/tests/self-check.mjs +165 -0
- package/dist/skills/unlazy/tests/stress-tests.mjs +356 -0
- package/dist/skills/web-design-guidelines/SKILL.md +40 -0
- package/dist/skills/workflow-creation/SKILL.md +73 -73
- package/dist/themes/powerline-footer/theme.json +33 -33
- package/dist/utils/shell.d.ts +3 -0
- package/dist/utils/shell.js +17 -5
- package/docs/compaction.md +396 -396
- package/docs/containerization.md +111 -111
- package/docs/development.md +71 -71
- package/docs/docs.json +164 -164
- package/docs/environment-variables.md +86 -86
- package/docs/index.md +83 -83
- package/docs/json.md +82 -82
- package/docs/models.md +502 -502
- package/docs/packages.md +227 -227
- package/docs/plans/subagent-delegation/phase-0-correctness.md +264 -264
- package/docs/plans/subagent-delegation/phase-1-behavioral-contract.md +485 -485
- package/docs/plans/subagent-delegation/phase-2-context-controls.md +281 -281
- package/docs/plans/subagent-delegation/phase-3-advisory-routing.md +361 -361
- package/docs/plans/subagent-delegation/phase-4-optional-enforcement.md +380 -380
- package/docs/prompt-templates.md +95 -95
- package/docs/providers.md +293 -293
- package/docs/sdk.md +1144 -1143
- package/docs/security.md +59 -59
- package/docs/session-format.md +414 -414
- package/docs/sessions.md +145 -145
- package/docs/settings.md +0 -5
- package/docs/shared-host-extensions.md +109 -109
- package/docs/shell-aliases.md +13 -13
- package/docs/skills.md +231 -231
- package/docs/terminal-setup.md +142 -142
- package/docs/termux.md +127 -127
- package/docs/themes.md +295 -295
- package/docs/tmux.md +63 -63
- package/docs/tui.md +927 -927
- package/docs/windows.md +17 -17
- package/examples/README.md +25 -25
- package/examples/extensions/README.md +211 -211
- package/examples/extensions/auto-commit-on-exit.ts +49 -49
- package/examples/extensions/bash-spawn-hook.ts +30 -30
- package/examples/extensions/bookmark.ts +50 -50
- package/examples/extensions/border-status-editor.ts +150 -150
- package/examples/extensions/built-in-tool-renderer.ts +249 -249
- package/examples/extensions/claude-rules.ts +86 -86
- package/examples/extensions/commands.ts +72 -72
- package/examples/extensions/confirm-destructive.ts +59 -59
- package/examples/extensions/custom-compaction.ts +130 -130
- package/examples/extensions/custom-footer.ts +64 -64
- package/examples/extensions/custom-header.ts +73 -73
- package/examples/extensions/custom-provider-anthropic/index.ts +610 -610
- package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -24
- package/examples/extensions/custom-provider-anthropic/package.json +19 -19
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +404 -404
- package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -16
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -82
- package/examples/extensions/dirty-repo-guard.ts +56 -56
- package/examples/extensions/doom-overlay/README.md +46 -46
- package/examples/extensions/doom-overlay/doom/build/doom.js +21 -21
- package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
- package/examples/extensions/doom-overlay/doom/build.sh +152 -152
- package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -72
- package/examples/extensions/doom-overlay/doom-component.ts +132 -132
- package/examples/extensions/doom-overlay/doom-engine.ts +173 -173
- package/examples/extensions/doom-overlay/doom-keys.ts +104 -104
- package/examples/extensions/doom-overlay/index.ts +74 -74
- package/examples/extensions/doom-overlay/wad-finder.ts +51 -51
- package/examples/extensions/dynamic-resources/SKILL.md +8 -8
- package/examples/extensions/dynamic-resources/dynamic.json +79 -79
- package/examples/extensions/dynamic-resources/dynamic.md +5 -5
- package/examples/extensions/dynamic-resources/index.ts +15 -15
- package/examples/extensions/dynamic-tools.ts +74 -74
- package/examples/extensions/event-bus.ts +43 -43
- package/examples/extensions/file-trigger.ts +41 -41
- package/examples/extensions/git-checkpoint.ts +53 -53
- package/examples/extensions/git-merge-and-resolve.ts +115 -115
- package/examples/extensions/github-issue-autocomplete.ts +185 -185
- package/examples/extensions/gondolin/index.ts +531 -531
- package/examples/extensions/gondolin/package-lock.json +185 -185
- package/examples/extensions/gondolin/package.json +19 -19
- package/examples/extensions/handoff.ts +199 -199
- package/examples/extensions/hello.ts +26 -26
- package/examples/extensions/hidden-thinking-label.ts +53 -53
- package/examples/extensions/inline-bash.ts +94 -94
- package/examples/extensions/input-transform-streaming.ts +39 -39
- package/examples/extensions/input-transform.ts +43 -43
- package/examples/extensions/interactive-shell.ts +196 -196
- package/examples/extensions/mac-system-theme.ts +47 -47
- package/examples/extensions/message-renderer.ts +59 -59
- package/examples/extensions/minimal-mode.ts +426 -426
- package/examples/extensions/modal-editor.ts +85 -85
- package/examples/extensions/model-status.ts +31 -31
- package/examples/extensions/notify.ts +55 -55
- package/examples/extensions/overlay-qa-tests.ts +1450 -1450
- package/examples/extensions/overlay-test.ts +153 -153
- package/examples/extensions/permission-gate.ts +34 -34
- package/examples/extensions/pirate.ts +47 -47
- package/examples/extensions/plan-mode/README.md +66 -66
- package/examples/extensions/plan-mode/index.ts +390 -390
- package/examples/extensions/plan-mode/utils.ts +168 -168
- package/examples/extensions/preset.ts +436 -436
- package/examples/extensions/project-trust.ts +64 -64
- package/examples/extensions/prompt-customizer.ts +97 -97
- package/examples/extensions/protected-paths.ts +30 -30
- package/examples/extensions/provider-payload.ts +18 -18
- package/examples/extensions/qna.ts +122 -122
- package/examples/extensions/question.ts +285 -285
- package/examples/extensions/questionnaire.ts +448 -448
- package/examples/extensions/rainbow-editor.ts +88 -88
- package/examples/extensions/reload-runtime.ts +37 -37
- package/examples/extensions/rpc-demo.ts +118 -118
- package/examples/extensions/sandbox/index.ts +321 -321
- package/examples/extensions/sandbox/package-lock.json +92 -92
- package/examples/extensions/sandbox/package.json +19 -19
- package/examples/extensions/send-user-message.ts +97 -97
- package/examples/extensions/session-name.ts +27 -27
- package/examples/extensions/shutdown-command.ts +63 -63
- package/examples/extensions/snake.ts +343 -343
- package/examples/extensions/space-invaders.ts +560 -560
- package/examples/extensions/ssh.ts +220 -220
- package/examples/extensions/status-line.ts +32 -32
- package/examples/extensions/structured-output.ts +65 -65
- package/examples/extensions/subagent/README.md +175 -175
- package/examples/extensions/subagent/agents/planner.md +37 -37
- package/examples/extensions/subagent/agents/reviewer.md +35 -35
- package/examples/extensions/subagent/agents/scout.md +50 -50
- package/examples/extensions/subagent/agents/worker.md +24 -24
- package/examples/extensions/subagent/agents.ts +126 -126
- package/examples/extensions/subagent/index.ts +1015 -1015
- package/examples/extensions/subagent/prompts/implement-and-review.md +10 -10
- package/examples/extensions/subagent/prompts/implement.md +10 -10
- package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -9
- package/examples/extensions/summarize.ts +209 -209
- package/examples/extensions/system-prompt-header.ts +17 -17
- package/examples/extensions/tic-tac-toe.ts +1008 -1008
- package/examples/extensions/timed-confirm.ts +70 -70
- package/examples/extensions/titlebar-spinner.ts +58 -58
- package/examples/extensions/todo.ts +297 -297
- package/examples/extensions/tool-override.ts +144 -144
- package/examples/extensions/tools.ts +146 -146
- package/examples/extensions/trigger-compact.ts +50 -50
- package/examples/extensions/truncated-tool.ts +195 -195
- package/examples/extensions/widget-placement.ts +9 -9
- package/examples/extensions/with-deps/index.ts +32 -32
- package/examples/extensions/with-deps/package-lock.json +31 -31
- package/examples/extensions/with-deps/package.json +22 -22
- package/examples/extensions/working-indicator.ts +123 -123
- package/examples/extensions/working-message-test.ts +25 -25
- package/examples/rpc-extension-ui.ts +632 -632
- package/examples/sdk/01-minimal.ts +26 -26
- package/examples/sdk/02-custom-model.ts +53 -53
- package/examples/sdk/03-custom-prompt.ts +75 -75
- package/examples/sdk/04-skills.ts +55 -55
- package/examples/sdk/05-tools.ts +48 -48
- package/examples/sdk/06-extensions.ts +99 -99
- package/examples/sdk/07-context-files.ts +47 -47
- package/examples/sdk/08-prompt-templates.ts +51 -51
- package/examples/sdk/09-api-keys-and-oauth.ts +52 -52
- package/examples/sdk/10-settings.ts +53 -53
- package/examples/sdk/11-sessions.ts +52 -52
- package/examples/sdk/12-full-control.ts +79 -79
- package/examples/sdk/13-session-runtime.ts +67 -67
- package/examples/sdk/README.md +144 -144
- package/package.json +4 -4
- package/dist/core/tools/schema-prune.d.ts +0 -16
- package/dist/core/tools/schema-prune.js +0 -39
- package/dist/core/tools/schema-prune.test.js +0 -111
- package/dist/extensions/pi-subagents/UPSTREAM-V0.50-MAPPING.md +0 -172
- package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +0 -55
- package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +0 -61
- package/dist/extensions/pi-subagents/src/agents/task-aware-routing.ts +0 -132
- package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +0 -181
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +0 -1354
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +0 -1581
- package/dist/extensions/pi-subagents/src/shared/env.ts +0 -16
- package/dist/extensions/pi-subagents/src/shared/session-lineage.ts +0 -71
- package/dist/extensions/pi-subagents/src/slash/inline-subagents.ts +0 -136
- package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +0 -41
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +0 -466
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +0 -1859
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +0 -733
- package/dist/extensions/pi-subagents/test/unit/builtin-agent-documentation.test.ts +0 -63
- package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +0 -326
- package/dist/extensions/pi-subagents/test/unit/handoff-adoption.test.ts +0 -103
- package/dist/extensions/pi-subagents/test/unit/inline-subagents.test.ts +0 -127
- package/dist/extensions/pi-subagents/test/unit/session-lineage.test.ts +0 -73
- package/dist/extensions/pi-subagents/test/unit/task-aware-routing.test.ts +0 -213
- package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +0 -65
- /package/dist/core/{tools/schema-prune.test.d.ts → extensions/loader.test.d.ts} +0 -0
|
@@ -3,12 +3,12 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
5
5
|
import type { ExtensionAPI, ExtensionContext } from "@selesai/code";
|
|
6
|
-
import { resolveAgentName, type AgentConfig, type AgentScope } from "../../agents/agents.ts";
|
|
6
|
+
import { findBlockingAgentDiagnostic, resolveAgentName, type AgentConfig, type AgentDiscoveryDiagnostic, type AgentScope } from "../../agents/agents.ts";
|
|
7
7
|
import { getArtifactsDir, getChainRunsDir, getProjectArtifactPackagingWarning, getProjectSubagentsDir } from "../../shared/artifacts.ts";
|
|
8
8
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
9
|
-
import {
|
|
9
|
+
import { createCapacityResilientJsonWriter } from "../../shared/capacity-resilient-json.ts";
|
|
10
|
+
import { isStorageCapacityError } from "../../shared/file-system-retry.ts";
|
|
10
11
|
import { resolveEffectiveThinking, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
11
|
-
import { executeChain } from "./chain-execution.ts";
|
|
12
12
|
import {
|
|
13
13
|
beginForegroundChild,
|
|
14
14
|
finishForegroundChild,
|
|
@@ -26,9 +26,11 @@ import { buildDoctorReport } from "../../extension/doctor.ts";
|
|
|
26
26
|
import { readSubagentGuide } from "../../extension/subagent-guide.ts";
|
|
27
27
|
import { normalizePublicSubagentExecution } from "../../extension/public-execution.ts";
|
|
28
28
|
import { runSync } from "./execution.ts";
|
|
29
|
-
import {
|
|
29
|
+
import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
|
|
30
|
+
import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
|
|
31
|
+
import { buildModelCandidates, inheritsParentModel, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelCandidate, type ParentModel } from "../shared/model-fallback.ts";
|
|
30
32
|
import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.ts";
|
|
31
|
-
import type
|
|
33
|
+
import { resolveModelScopesForAgent, type ModelScopeConfig } from "../shared/model-scope.ts";
|
|
32
34
|
import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
|
|
33
35
|
import { recordRun } from "../shared/run-history.ts";
|
|
34
36
|
import {
|
|
@@ -53,12 +55,15 @@ import {
|
|
|
53
55
|
import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.ts";
|
|
54
56
|
import { buildAsyncRunnerSteps, DEFAULT_ASYNC_TIMEOUT_MS, executeAsyncChain, executeAsyncSingle, formatAsyncStartedMessage, isAsyncAvailable, workflowAwaitedAsyncResultPath } from "../background/async-execution.ts";
|
|
55
57
|
import { updateActiveRunIndex } from "../background/active-run-index.ts";
|
|
58
|
+
import { steeringReceipt } from "../background/steering.ts";
|
|
56
59
|
import { acquireActiveAsyncCapacity, ActiveAsyncCapacityError, getActiveAsyncCapacitySnapshot, resolveMaxActiveAsyncRunsPerSession, transferActiveAsyncCapacity, type ActiveAsyncCapacityHandle } from "../background/active-async-capacity.ts";
|
|
60
|
+
import { isScheduledRunAction, type ScheduledRunAction } from "../background/scheduled-runs.ts";
|
|
57
61
|
import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
|
|
58
62
|
import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
|
|
59
63
|
import { normalizeGateAcceptance, validateExecutionAcceptance } from "../shared/acceptance.ts";
|
|
60
|
-
import { createForkContextResolver, forkedChildRequiresThinkingOff } from "../../shared/fork-context.ts";
|
|
64
|
+
import { canPreferFork, createForkContextResolver, forkedChildRequiresThinkingOff, resolveSubagentLaunchContext } from "../../shared/fork-context.ts";
|
|
61
65
|
import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
|
|
66
|
+
import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
|
|
62
67
|
import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBridge, resolveIntercomSessionTarget, resolveSubagentIntercomTarget, type IntercomBridgeState } from "../../intercom/intercom-bridge.ts";
|
|
63
68
|
import { formatControlIntercomMessage, formatControlNoticeMessage, resolveControlConfig, shouldNotifyControlEvent } from "../shared/subagent-control.ts";
|
|
64
69
|
import { resolveTurnBudgetConfig } from "../shared/turn-budget.ts";
|
|
@@ -68,6 +73,7 @@ import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
|
68
73
|
import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig } from "../shared/usage-budget.ts";
|
|
69
74
|
import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
70
75
|
import { isAgentContractV1 } from "../shared/agent-contract.ts";
|
|
76
|
+
import { normalizeExtensionBindings, type ExtensionBindings } from "../shared/extension-bindings.ts";
|
|
71
77
|
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
72
78
|
import { cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
73
79
|
import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage } from "../../shared/utils.ts";
|
|
@@ -83,10 +89,10 @@ import {
|
|
|
83
89
|
resolveSubagentResultStatus,
|
|
84
90
|
stripDetailsOutputsForIntercomReceipt,
|
|
85
91
|
} from "../../intercom/result-intercom.ts";
|
|
86
|
-
import { applySteeringRecoveryAgentConfig, buildRevivedAsyncTask, resolveAsyncResumeTarget, resolveAsyncRunLocation } from "../background/async-resume.ts";
|
|
87
|
-
import {
|
|
92
|
+
import { applySteeringRecoveryAgentConfig, asyncReviveRequiresRecoveryDescriptor, buildRevivedAsyncTask, readAsyncRecoveryDescriptor, resolveAsyncResumeTarget, resolveAsyncRunLocation } from "../background/async-resume.ts";
|
|
93
|
+
import { deliverInterruptRequest, readRevivalBriefs, requestAsyncSteer, type SteerDeliveryMode } from "../background/control-channel.ts";
|
|
88
94
|
import { updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
|
|
89
|
-
import { steerAsyncRun } from "./async-steering-action.ts";
|
|
95
|
+
import { canQueueRetainedAsyncFollowUp, steerAsyncRun } from "./async-steering-action.ts";
|
|
90
96
|
import {
|
|
91
97
|
removeWorkflowForegroundSteeringRoute,
|
|
92
98
|
resolveWorkflowForegroundSteeringTarget,
|
|
@@ -96,13 +102,17 @@ import {
|
|
|
96
102
|
} from "./workflow-foreground-steering.ts";
|
|
97
103
|
import { stopAsyncRun } from "./async-stop-action.ts";
|
|
98
104
|
import { dismissRecoveredWorkflow } from "./async-dismiss-action.ts";
|
|
105
|
+
import { promotePausedWorkflowIfSettled, reconcileDetachedWorkflowChildCompletion } from "./workflow-detach-reconcile.ts";
|
|
99
106
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
100
107
|
import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.ts";
|
|
101
108
|
import { resultFilePath, writeAsyncResultFile } from "../background/result-files.ts";
|
|
102
109
|
import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedRunResolutionScope } from "../shared/nested-events.ts";
|
|
103
110
|
import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
|
|
104
111
|
import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
|
|
112
|
+
import { isStoppableAsyncStatusStep, resolveAsyncStatusChild } from "../shared/child-identity.ts";
|
|
105
113
|
import { inspectSubagentStatus } from "../background/run-status.ts";
|
|
114
|
+
import { getExternalJobProvider } from "../../api/external-job-provider.ts";
|
|
115
|
+
import { externalJobFollowUpRequestDigest, externalJobFollowUpRequestId, externalJobFollowUpRunId, externalJobPromptDigest, externalJobStableJson } from "../shared/external-job-runner.ts";
|
|
106
116
|
import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.ts";
|
|
107
117
|
import { handleMissionAction, MISSION_ACTIONS } from "../../missions/actions.ts";
|
|
108
118
|
import { attachMissionToLaunchResult, prepareMissionLaunch, writeMissionAsyncBinding, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
|
|
@@ -112,9 +122,8 @@ import { createMissionWorkflowState } from "../../missions/workflow-state.ts";
|
|
|
112
122
|
import { resolveAuthorityDecision } from "../../policy/authority.ts";
|
|
113
123
|
import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspectors/herdr/actions.ts";
|
|
114
124
|
import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
|
|
115
|
-
import { previewSimpleWorkflowRun, runWorkflowScript, WorkflowScriptError, type WorkflowScriptChildResult } from "../../workflows/scripted-workflow.ts";
|
|
116
|
-
import {
|
|
117
|
-
import { renderWorkflowPrompt } from "../../shared/prompt-resources.ts";
|
|
125
|
+
import { previewSimpleWorkflowRun, runWorkflowScript, WorkflowScriptError, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
|
|
126
|
+
import { buildWorkflowReceipt, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt, type WorkflowReceipt, type WorkflowReceiptState } from "../../workflows/workflow-receipt.ts";
|
|
118
127
|
import { resolveWorkflowChatProgress, type WorkflowChatProgressProjection } from "../../workflows/chat-progress.ts";
|
|
119
128
|
import {
|
|
120
129
|
cleanupWorktrees,
|
|
@@ -148,7 +157,9 @@ import {
|
|
|
148
157
|
type ResolvedTurnBudget,
|
|
149
158
|
type ResolvedToolBudget,
|
|
150
159
|
type RunFanoutBudgetDescriptor,
|
|
160
|
+
type SteeringRecoveryDescriptor,
|
|
151
161
|
type SingleResult,
|
|
162
|
+
type SubagentChildStatusEvent,
|
|
152
163
|
type ToolBudgetConfig,
|
|
153
164
|
type TurnBudgetConfig,
|
|
154
165
|
type UsageBudgetConfig,
|
|
@@ -168,10 +179,11 @@ import {
|
|
|
168
179
|
resolveCurrentMaxSubagentDepth,
|
|
169
180
|
resolveMaxSubagentSpawnsPerRun,
|
|
170
181
|
wrapForkTask,
|
|
182
|
+
type ScheduleOrigin,
|
|
171
183
|
} from "../../shared/types.ts";
|
|
172
184
|
|
|
173
|
-
const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "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"]);
|
|
174
|
-
const DESTRUCTIVE_MANAGEMENT_ACTIONS = new Set(["delete", "eject", "disable", "reset", "mission.close", "worktree.discard", "refine.rollback", "inspector.close", "project.close", "stop", "interrupt", "
|
|
185
|
+
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"]);
|
|
186
|
+
const DESTRUCTIVE_MANAGEMENT_ACTIONS = new Set(["delete", "eject", "disable", "reset", "mission.close", "worktree.discard", "refine.rollback", "inspector.close", "project.close", "stop", "interrupt", "schedule.delete"]);
|
|
175
187
|
|
|
176
188
|
function editDistance(left: string, right: string): number {
|
|
177
189
|
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
@@ -254,6 +266,7 @@ interface TaskParam {
|
|
|
254
266
|
reads?: string[] | boolean;
|
|
255
267
|
progress?: boolean;
|
|
256
268
|
model?: string;
|
|
269
|
+
fast?: boolean;
|
|
257
270
|
skill?: string | string[] | boolean;
|
|
258
271
|
outputSchema?: JsonSchemaObject;
|
|
259
272
|
acceptance?: AcceptanceInput;
|
|
@@ -268,6 +281,7 @@ export interface SubagentParamsLike {
|
|
|
268
281
|
dir?: string;
|
|
269
282
|
handoffPath?: string;
|
|
270
283
|
index?: number;
|
|
284
|
+
childId?: string;
|
|
271
285
|
view?: "fleet" | "transcript";
|
|
272
286
|
lines?: number;
|
|
273
287
|
topic?: string;
|
|
@@ -277,29 +291,37 @@ export interface SubagentParamsLike {
|
|
|
277
291
|
type?: string;
|
|
278
292
|
agent?: string;
|
|
279
293
|
task?: string;
|
|
294
|
+
extensionBindings?: ExtensionBindings;
|
|
280
295
|
/** Retained async child run id. Valid only on workflow runs.run items. */
|
|
281
296
|
resume?: string;
|
|
282
297
|
message?: string;
|
|
283
298
|
steeringRecovery?: boolean;
|
|
284
299
|
mode?: SteerDeliveryMode;
|
|
285
300
|
workflowScript?: string;
|
|
286
|
-
chatProgress?: "auto" | "off" | "
|
|
301
|
+
chatProgress?: "auto" | "off" | "live-card";
|
|
302
|
+
isolation?: "none" | "worktree";
|
|
287
303
|
step?: ChainStep;
|
|
288
304
|
/** Internal workflow ownership metadata; not part of the public schema. */
|
|
289
305
|
workflowParentRunId?: string;
|
|
290
306
|
workflowKey?: string;
|
|
307
|
+
/** Set by the scheduler so this run's completion can name the schedule that produced it. */
|
|
308
|
+
scheduleOrigin?: ScheduleOrigin;
|
|
291
309
|
workflowChildAsyncId?: string;
|
|
310
|
+
workflowAwaitAsync?: boolean;
|
|
311
|
+
workflowParentDeadlineAt?: number;
|
|
292
312
|
suppressRoutineResultIntercom?: boolean;
|
|
293
313
|
/** Internal inherited cumulative run-tree budget. */
|
|
294
314
|
runFanoutBudget?: RunFanoutBudgetDescriptor;
|
|
295
315
|
/** Internal workflow host admission proof. */
|
|
296
316
|
runFanoutAdmitted?: boolean;
|
|
317
|
+
/** Internal inherited tool/agent ceiling for delegated child launches. */
|
|
318
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
297
319
|
/** Internal durable-run compatibility fields. Public callers must use workflowScript. */
|
|
298
320
|
chain?: ChainStep[];
|
|
299
321
|
tasks?: TaskParam[];
|
|
300
322
|
concurrency?: number;
|
|
301
323
|
worktree?: boolean;
|
|
302
|
-
context?: "fresh" | "fork";
|
|
324
|
+
context?: "fresh" | "fork" | "profile";
|
|
303
325
|
/** Per-run intercom bridge config. It replaces the global config for this launch only. */
|
|
304
326
|
intercomBridge?: IntercomBridgeConfig;
|
|
305
327
|
async?: boolean;
|
|
@@ -322,6 +344,7 @@ export interface SubagentParamsLike {
|
|
|
322
344
|
artifacts?: boolean;
|
|
323
345
|
includeProgress?: boolean;
|
|
324
346
|
model?: string;
|
|
347
|
+
fast?: boolean;
|
|
325
348
|
thinking?: string | false;
|
|
326
349
|
scope?: string;
|
|
327
350
|
target?: string;
|
|
@@ -337,8 +360,6 @@ export interface SubagentParamsLike {
|
|
|
337
360
|
acceptance?: AcceptanceInput;
|
|
338
361
|
gate?: string;
|
|
339
362
|
agentContract?: AgentContract;
|
|
340
|
-
schedule?: string;
|
|
341
|
-
scheduleName?: string;
|
|
342
363
|
at?: string;
|
|
343
364
|
every?: string;
|
|
344
365
|
on?: string | number;
|
|
@@ -371,16 +392,20 @@ interface ExecutorDeps {
|
|
|
371
392
|
config: ExtensionConfig;
|
|
372
393
|
asyncByDefault: boolean;
|
|
373
394
|
waitToolEnabled?: boolean;
|
|
395
|
+
handleScheduledRunAction?: (params: SubagentParamsLike, ctx: ExtensionContext) => Promise<AgentToolResult<Details>>;
|
|
396
|
+
watchdog?: MainWatchdogRuntime;
|
|
374
397
|
tempArtifactsDir: string;
|
|
375
398
|
getSubagentSessionRoot: (parentSessionFile: string | null) => string;
|
|
376
399
|
expandTilde: (p: string) => string;
|
|
377
|
-
discoverAgents: (cwd: string, scope: AgentScope) => { agents: AgentConfig[]; modelScope?: ModelScopeConfig };
|
|
400
|
+
discoverAgents: (cwd: string, scope: AgentScope) => { agents: AgentConfig[]; agentDiagnostics?: AgentDiscoveryDiagnostic[]; modelScope?: ModelScopeConfig; maxThinking?: AgentConfig["maxThinking"] };
|
|
378
401
|
allowMutatingManagementActions?: boolean;
|
|
402
|
+
activateSupervisorTransport?: () => void;
|
|
403
|
+
refreshResultDelivery?: () => void;
|
|
379
404
|
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
|
|
380
405
|
}
|
|
381
406
|
|
|
382
|
-
type ForkSessionFileForTask = (agentName: string, idx?: number, modelOverride?: string) => string | undefined;
|
|
383
|
-
type ForkThinkingOverrideForTask = (agentName: string, idx?: number, modelOverride?: string) => AgentConfig["thinking"] | undefined;
|
|
407
|
+
type ForkSessionFileForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean) => string | undefined;
|
|
408
|
+
type ForkThinkingOverrideForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean) => AgentConfig["thinking"] | undefined;
|
|
384
409
|
|
|
385
410
|
interface ExecutionContextData {
|
|
386
411
|
params: SubagentParamsLike;
|
|
@@ -458,6 +483,7 @@ function promptAuditRedoParams(value: unknown, rewrittenTask: string): SubagentP
|
|
|
458
483
|
delete params.workflowParentRunId;
|
|
459
484
|
delete params.workflowKey;
|
|
460
485
|
delete params.workflowChildAsyncId;
|
|
486
|
+
delete params.workflowParentDeadlineAt;
|
|
461
487
|
delete params.suppressRoutineResultIntercom;
|
|
462
488
|
if (params.worktree === true && Array.isArray(params.tasks)) delete params.cwd;
|
|
463
489
|
return params;
|
|
@@ -549,7 +575,7 @@ function countRequestedSubagentSpawns(params: SubagentParamsLike, config: Extens
|
|
|
549
575
|
function staticRunFanoutPaths(params: SubagentParamsLike): string[] {
|
|
550
576
|
if (params.tasks) return params.tasks.map((_, index) => `tasks[${index}]`);
|
|
551
577
|
if (params.chain) return params.chain.flatMap((step, stepIndex) => {
|
|
552
|
-
if (isDynamicParallelStep(step)
|
|
578
|
+
if (isDynamicParallelStep(step)) return [];
|
|
553
579
|
if (isParallelStep(step)) return step.parallel.map((_, itemIndex) => `chain[${stepIndex}].parallel[${itemIndex}]`);
|
|
554
580
|
return [`chain[${stepIndex}]`];
|
|
555
581
|
});
|
|
@@ -623,7 +649,7 @@ function foregroundChildActivityFromProgress(progress: SingleResult["progress"]
|
|
|
623
649
|
};
|
|
624
650
|
}
|
|
625
651
|
|
|
626
|
-
function rememberForegroundRun(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[];
|
|
652
|
+
function rememberForegroundRun(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[]; extensionBindings?: ExtensionBindings }): void {
|
|
627
653
|
state.foregroundRuns ??= new Map();
|
|
628
654
|
const previous = state.foregroundRuns.get(input.runId);
|
|
629
655
|
const updatedAt = Date.now();
|
|
@@ -633,7 +659,6 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
633
659
|
cwd: input.cwd,
|
|
634
660
|
...(input.sessionId ? { sessionId: input.sessionId } : {}),
|
|
635
661
|
updatedAt,
|
|
636
|
-
...(input.checkpoint ? { checkpoint: input.checkpoint } : {}),
|
|
637
662
|
children: input.results.map((result, index) => {
|
|
638
663
|
const child = {
|
|
639
664
|
agent: result.agent,
|
|
@@ -666,6 +691,7 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
666
691
|
...(result.detachedReason ? { detachedReason: result.detachedReason } : {}),
|
|
667
692
|
...(result.acceptance ? { acceptance: result.acceptance } : {}),
|
|
668
693
|
...(result.launchContractDigest ? { launchContractDigest: result.launchContractDigest } : {}),
|
|
694
|
+
...(input.extensionBindings ? { extensionBindings: input.extensionBindings } : {}),
|
|
669
695
|
...(result.launchResolvedExtensions ? { launchResolvedExtensions: result.launchResolvedExtensions } : {}),
|
|
670
696
|
...(result.runtimeAcknowledgedExtensions ? { runtimeAcknowledgedExtensions: result.runtimeAcknowledgedExtensions } : {}),
|
|
671
697
|
...(result.capabilityCeiling ? { capabilityCeiling: result.capabilityCeiling } : {}),
|
|
@@ -784,13 +810,13 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
784
810
|
});
|
|
785
811
|
}
|
|
786
812
|
|
|
787
|
-
function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: SubagentRunMode; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string; model?: string; thinking?: string; launchContractDigest?: string; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } | undefined {
|
|
813
|
+
function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState, options: { exactOnly?: boolean } = {}): { runId: string; mode: SubagentRunMode; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string; model?: string; thinking?: string; launchContractDigest?: string; extensionBindings?: ExtensionBindings; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } | undefined {
|
|
788
814
|
const requested = (params.id ?? params.runId)?.trim();
|
|
789
815
|
if (!requested || !state.foregroundRuns?.size || !state.currentSessionId) return undefined;
|
|
790
|
-
const
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
|
|
816
|
+
const direct = state.foregroundRuns.get(requested);
|
|
817
|
+
const matches = direct?.sessionId === state.currentSessionId
|
|
818
|
+
? [direct]
|
|
819
|
+
: options.exactOnly ? [] : [...state.foregroundRuns.values()].filter((run) => run.sessionId === state.currentSessionId && run.runId.startsWith(requested));
|
|
794
820
|
if (matches.length === 0) return undefined;
|
|
795
821
|
if (matches.length > 1) throw new Error(`Ambiguous foreground run id prefix '${requested}' matched: ${matches.map((run) => run.runId).join(", ")}. Provide a longer id.`);
|
|
796
822
|
const run = matches[0]!;
|
|
@@ -815,6 +841,7 @@ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: Subage
|
|
|
815
841
|
...(child.model ? { model: child.model } : {}),
|
|
816
842
|
...(child.thinking ? { thinking: child.thinking } : {}),
|
|
817
843
|
...(child.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {}),
|
|
844
|
+
...(child.extensionBindings ? { extensionBindings: normalizeExtensionBindings(child.extensionBindings)!.value } : {}),
|
|
818
845
|
...(child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {}),
|
|
819
846
|
};
|
|
820
847
|
}
|
|
@@ -834,6 +861,7 @@ type NestedResumeSourceTarget = {
|
|
|
834
861
|
thinking?: AgentConfig["thinking"];
|
|
835
862
|
launchContractDigest?: string;
|
|
836
863
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
864
|
+
recoveryDescriptor?: SteeringRecoveryDescriptor;
|
|
837
865
|
};
|
|
838
866
|
type ResumeSourceTarget = AsyncResumeSourceTarget | ForegroundResumeSourceTarget | NestedResumeSourceTarget;
|
|
839
867
|
|
|
@@ -841,6 +869,10 @@ function isAsyncRunNotFound(error: unknown): boolean {
|
|
|
841
869
|
return error instanceof Error && error.message.startsWith("Async run not found.");
|
|
842
870
|
}
|
|
843
871
|
|
|
872
|
+
function isMissingExactAsyncStatusFile(error: unknown, requested: string): boolean {
|
|
873
|
+
return error instanceof Error && error.message === `Status file not found for async run '${requested}'.`;
|
|
874
|
+
}
|
|
875
|
+
|
|
844
876
|
function isResumeAmbiguity(error: unknown): boolean {
|
|
845
877
|
return error instanceof Error && /Ambiguous .*run id prefix/.test(error.message);
|
|
846
878
|
}
|
|
@@ -858,7 +890,7 @@ function isExactResumeError(error: unknown, source: "async" | "foreground", requ
|
|
|
858
890
|
return new RegExp(`\\b${source} run '${escapeRegExp(requested)}'`, "i").test(error.message);
|
|
859
891
|
}
|
|
860
892
|
|
|
861
|
-
function resolveResumeTarget(params: SubagentParamsLike, state: SubagentState, options: { asyncRequireSessionFile?: boolean } = {}): ResumeSourceTarget {
|
|
893
|
+
function resolveResumeTarget(params: SubagentParamsLike, state: SubagentState, options: { asyncRequireSessionFile?: boolean; exactOnly?: boolean } = {}): ResumeSourceTarget {
|
|
862
894
|
const requested = (params.id ?? params.runId)?.trim() ?? "";
|
|
863
895
|
let foregroundTarget: ForegroundResumeSourceTarget | undefined;
|
|
864
896
|
let foregroundError: unknown;
|
|
@@ -866,15 +898,18 @@ function resolveResumeTarget(params: SubagentParamsLike, state: SubagentState, o
|
|
|
866
898
|
let asyncError: unknown;
|
|
867
899
|
|
|
868
900
|
try {
|
|
869
|
-
const target = resolveForegroundResumeTarget(params, state);
|
|
901
|
+
const target = resolveForegroundResumeTarget(params, state, options);
|
|
870
902
|
if (target) foregroundTarget = { kind: "revive", source: "foreground", ...target };
|
|
871
903
|
} catch (error) {
|
|
872
904
|
foregroundError = error;
|
|
873
905
|
}
|
|
874
906
|
try {
|
|
907
|
+
const asyncParams = options.exactOnly && requested && !params.dir
|
|
908
|
+
? { ...params, dir: path.join(DIRS.async, requested) }
|
|
909
|
+
: params;
|
|
875
910
|
asyncTarget = {
|
|
876
911
|
source: "async",
|
|
877
|
-
...resolveAsyncResumeTarget(
|
|
912
|
+
...resolveAsyncResumeTarget(asyncParams, {}, compactOptional<NonNullable<Parameters<typeof resolveAsyncResumeTarget>[2]>>({
|
|
878
913
|
requireSessionFile: options.asyncRequireSessionFile,
|
|
879
914
|
sessionId: state.currentSessionId ?? undefined,
|
|
880
915
|
})),
|
|
@@ -891,7 +926,7 @@ function resolveResumeTarget(params: SubagentParamsLike, state: SubagentState, o
|
|
|
891
926
|
throw new Error(`Resume id '${requested}' is ambiguous between foreground run '${foregroundTarget.runId}' and async run '${asyncTarget.runId}'. Provide a full run id.`);
|
|
892
927
|
}
|
|
893
928
|
if (foregroundTarget) {
|
|
894
|
-
if (isExactResumeError(asyncError, "async", requested)) throw asyncError;
|
|
929
|
+
if (isExactResumeError(asyncError, "async", requested) && !isMissingExactAsyncStatusFile(asyncError, requested)) throw asyncError;
|
|
895
930
|
if (isResumeAmbiguity(asyncError) && !resumeTargetExact(foregroundTarget, requested)) throw asyncError;
|
|
896
931
|
return foregroundTarget;
|
|
897
932
|
}
|
|
@@ -947,11 +982,48 @@ function emitAdvisoryControlEvent(pi: ExtensionAPI, channel: string, payload: un
|
|
|
947
982
|
}
|
|
948
983
|
}
|
|
949
984
|
|
|
985
|
+
function persistAsyncWorkflowControlEvent(input: {
|
|
986
|
+
job: AsyncJobState;
|
|
987
|
+
event: ControlEvent;
|
|
988
|
+
controlConfig: ResolvedControlConfig;
|
|
989
|
+
intercomBridge: IntercomBridgeState;
|
|
990
|
+
childIntercomTarget?: string;
|
|
991
|
+
}): void {
|
|
992
|
+
const channels = input.event.type === "active_long_running"
|
|
993
|
+
? input.controlConfig.notifyChannels.filter((channel) => channel !== "intercom")
|
|
994
|
+
: input.controlConfig.notifyChannels;
|
|
995
|
+
if (channels.length === 0) return;
|
|
996
|
+
const record = {
|
|
997
|
+
ts: Date.now(),
|
|
998
|
+
runId: input.job.asyncId,
|
|
999
|
+
type: "subagent.control",
|
|
1000
|
+
event: input.event,
|
|
1001
|
+
channels,
|
|
1002
|
+
childIntercomTarget: input.childIntercomTarget,
|
|
1003
|
+
noticeText: formatControlNoticeMessage(input.event, input.childIntercomTarget),
|
|
1004
|
+
...(input.intercomBridge.active && input.intercomBridge.orchestratorTarget && channels.includes("intercom")
|
|
1005
|
+
? {
|
|
1006
|
+
intercom: {
|
|
1007
|
+
to: input.intercomBridge.orchestratorTarget,
|
|
1008
|
+
message: formatControlIntercomMessage(input.event, input.childIntercomTarget),
|
|
1009
|
+
},
|
|
1010
|
+
}
|
|
1011
|
+
: {}),
|
|
1012
|
+
};
|
|
1013
|
+
try {
|
|
1014
|
+
fs.appendFileSync(path.join(input.job.asyncDir, "events.jsonl"), `${JSON.stringify(record)}\n`, "utf-8");
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
if (!isStorageCapacityError(error)) throw error;
|
|
1017
|
+
console.error("Failed to append async workflow control event while storage is full:", error);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
950
1021
|
function emitControlNotification(input: {
|
|
951
1022
|
pi: ExtensionAPI;
|
|
952
1023
|
controlConfig: ResolvedControlConfig;
|
|
953
1024
|
intercomBridge: IntercomBridgeState;
|
|
954
1025
|
event: ControlEvent;
|
|
1026
|
+
source?: "foreground" | "async";
|
|
955
1027
|
}): void {
|
|
956
1028
|
if (!shouldNotifyControlEvent(input.controlConfig, input.event)) return;
|
|
957
1029
|
const childIntercomTarget = input.intercomBridge.active
|
|
@@ -959,7 +1031,7 @@ function emitControlNotification(input: {
|
|
|
959
1031
|
: undefined;
|
|
960
1032
|
const payload = {
|
|
961
1033
|
event: input.event,
|
|
962
|
-
source: "foreground"
|
|
1034
|
+
source: input.source ?? "foreground",
|
|
963
1035
|
childIntercomTarget,
|
|
964
1036
|
noticeText: formatControlNoticeMessage(input.event, childIntercomTarget),
|
|
965
1037
|
};
|
|
@@ -992,9 +1064,9 @@ function interruptAsyncRun(
|
|
|
992
1064
|
};
|
|
993
1065
|
}
|
|
994
1066
|
const activeSteps = status.steps?.filter((step) => step.status === "running") ?? [];
|
|
995
|
-
if (activeSteps.length > 0 && activeSteps.every((step) => step.runner?.type === "external-cli")) {
|
|
1067
|
+
if (activeSteps.length > 0 && activeSteps.every((step) => step.runner?.type === "external-cli" || step.runner?.type === "external-job")) {
|
|
996
1068
|
return {
|
|
997
|
-
content: [{ type: "text", text: `Interrupt is unsupported for
|
|
1069
|
+
content: [{ type: "text", text: `Interrupt is unsupported for external async run ${target.asyncId}; use stop instead.` }],
|
|
998
1070
|
isError: true,
|
|
999
1071
|
details: { mode: "management", results: [] },
|
|
1000
1072
|
};
|
|
@@ -1211,7 +1283,7 @@ function appendStepToAsyncChain(input: {
|
|
|
1211
1283
|
const startIndex = (status.chainStepCount ?? status.steps?.length ?? 0) + pendingAppendRequests.reduce((total, request) => total + request.steps.length, 0);
|
|
1212
1284
|
const appendPaths = chain.flatMap((step, localIndex) => {
|
|
1213
1285
|
const absoluteIndex = startIndex + localIndex;
|
|
1214
|
-
if (isDynamicParallelStep(step)
|
|
1286
|
+
if (isDynamicParallelStep(step)) return [];
|
|
1215
1287
|
if (isParallelStep(step)) return step.parallel.map((_, itemIndex) => `chain[${absoluteIndex}].parallel[${itemIndex}]`);
|
|
1216
1288
|
return [`chain[${absoluteIndex}]`];
|
|
1217
1289
|
});
|
|
@@ -1275,6 +1347,14 @@ function validateNestedSessionFile(run: NestedRunSummary, trustedSessionRoots: s
|
|
|
1275
1347
|
return realSessionFile;
|
|
1276
1348
|
}
|
|
1277
1349
|
|
|
1350
|
+
export function readNestedRecoveryDescriptor(asyncDir: string | undefined, runId: string, agent: string): SteeringRecoveryDescriptor | undefined {
|
|
1351
|
+
const recoveryDescriptor = readAsyncRecoveryDescriptor(asyncDir);
|
|
1352
|
+
if (!recoveryDescriptor) return undefined;
|
|
1353
|
+
if (recoveryDescriptor.sourceRunId !== runId) throw new Error(`Nested run '${runId}' has a recovery descriptor for a different source run.`);
|
|
1354
|
+
if (recoveryDescriptor.agent !== agent) throw new Error(`Nested run '${runId}' has a recovery descriptor for a different agent.`);
|
|
1355
|
+
return recoveryDescriptor;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1278
1358
|
function resolveNestedResumeTarget(match: ResolvedSubagentRunId & { kind: "nested" }, trustedSessionRoots: string[]): NestedResumeSourceTarget {
|
|
1279
1359
|
const run = match.match.run;
|
|
1280
1360
|
if (run.state === "running" || run.state === "queued") throw new Error(`Nested run '${run.id}' is live; route the follow-up to the owner process instead.`);
|
|
@@ -1283,6 +1363,7 @@ function resolveNestedResumeTarget(match: ResolvedSubagentRunId & { kind: "neste
|
|
|
1283
1363
|
if (!agent) throw new Error(`Could not determine child agent for nested run '${run.id}'.`);
|
|
1284
1364
|
const state = run.state === "complete" || run.state === "failed" || run.state === "paused" ? run.state : "failed";
|
|
1285
1365
|
const asyncDir = resolveNestedAsyncDir(match.match.rootRunId, run);
|
|
1366
|
+
const recoveryDescriptor = readNestedRecoveryDescriptor(asyncDir, run.id, agent);
|
|
1286
1367
|
return compactOptional<NestedResumeSourceTarget>({
|
|
1287
1368
|
kind: "revive",
|
|
1288
1369
|
source: "nested",
|
|
@@ -1293,6 +1374,7 @@ function resolveNestedResumeTarget(match: ResolvedSubagentRunId & { kind: "neste
|
|
|
1293
1374
|
cwd: asyncDir ? path.dirname(asyncDir) : undefined,
|
|
1294
1375
|
sessionFile: validateNestedSessionFile(run, trustedSessionRoots),
|
|
1295
1376
|
...(run.capabilityCeiling ? { capabilityCeiling: run.capabilityCeiling } : {}),
|
|
1377
|
+
...(recoveryDescriptor ? { recoveryDescriptor } : {}),
|
|
1296
1378
|
});
|
|
1297
1379
|
}
|
|
1298
1380
|
|
|
@@ -1369,12 +1451,12 @@ async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & {
|
|
|
1369
1451
|
const targets = targetIndexes.map((index) => ({ index, state: steps[index]?.status === "pending" ? "scheduled" as const : "pending" as const }));
|
|
1370
1452
|
if (targets.every((target) => target.state === "scheduled")) {
|
|
1371
1453
|
const scheduled = { requestId, state: "scheduled" as const, deliveryStatus: "queued" as const, sourceRunId: run.id, targets };
|
|
1372
|
-
return { content: [{ type: "text", text: `Steering scheduled for nested async run ${run.id} (request ${requestId}).` }], details: { mode: "management", results: [], steering: scheduled } };
|
|
1454
|
+
return { content: [{ type: "text", text: steeringReceipt(input.message, `Steering scheduled for nested async run ${run.id} (request ${requestId}).`) }], details: { mode: "management", results: [], steering: scheduled } };
|
|
1373
1455
|
}
|
|
1374
1456
|
const waited = await waitForSteeringAction(omitUndefinedProperties({ asyncDir, sourceRunId: run.id, requestId, timeoutMs: 3_000, signal: input.signal }));
|
|
1375
1457
|
const result = waited ?? { requestId, state: "pending" as const, deliveryStatus: "queued" as const, sourceRunId: run.id, targets };
|
|
1376
1458
|
const stateText = result.state === "failed" ? "failed" : result.state === "partial" ? "partial" : result.deliveryStatus === "queued" ? "queued" : result.state === "delivered" ? "delivered" : "pending";
|
|
1377
|
-
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 } };
|
|
1459
|
+
return { content: [{ type: "text", text: steeringReceipt(input.message, `Steering ${stateText} for nested async run ${run.id} (request ${requestId}).`) }], ...(result.state === "failed" || result.state === "partial" ? { isError: true } : {}), details: { mode: "management", results: [], steering: result } };
|
|
1378
1460
|
}
|
|
1379
1461
|
|
|
1380
1462
|
async function interruptNestedRun(target: ResolvedSubagentRunId & { kind: "nested" }): Promise<AgentToolResult<Details>> {
|
|
@@ -1404,15 +1486,150 @@ async function steerNestedRun(input: { target: ResolvedSubagentRunId & { kind: "
|
|
|
1404
1486
|
return { content: [{ type: "text", text: `Nested run ${run.id} is not a live async Pi child session with a steering inbox. action='steer' cannot target foreground nested runs.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1405
1487
|
}
|
|
1406
1488
|
|
|
1489
|
+
function externalJobOptionsEqual(left: Record<string, unknown>, right: Record<string, unknown>): boolean {
|
|
1490
|
+
return externalJobStableJson(left) === externalJobStableJson(right);
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
function providerFollowUpSupport(providerName: string): { ok: true } | { ok: false; message: string } {
|
|
1494
|
+
try {
|
|
1495
|
+
const provider = getExternalJobProvider(providerName);
|
|
1496
|
+
if (!provider) return { ok: false, message: `External-job provider '${providerName}' is not registered. Load the provider package, then retry action='resume'.` };
|
|
1497
|
+
if (typeof provider.followUp !== "function") return { ok: false, message: `External-job provider '${providerName}' does not support follow-up. Update or reload the provider package, then retry action='resume'.` };
|
|
1498
|
+
return { ok: true };
|
|
1499
|
+
} catch (error) {
|
|
1500
|
+
return { ok: false, message: `External-job provider registry unavailable: ${error instanceof Error ? error.message : String(error)}` };
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
function externalJobFollowUpStarted(input: { sourceRunId: string; runId: string; asyncDir: string; duplicate?: boolean; interactive: boolean }): AgentToolResult<Details> {
|
|
1505
|
+
const lines = [
|
|
1506
|
+
input.duplicate ? `External-job follow-up already exists for ${input.sourceRunId}.` : `Started external-job follow-up for ${input.sourceRunId}.`,
|
|
1507
|
+
`Follow-up run: ${input.runId}`,
|
|
1508
|
+
`Async dir: ${input.asyncDir}`,
|
|
1509
|
+
`Status if needed: subagent({ action: "status", id: "${input.runId}" })`,
|
|
1510
|
+
];
|
|
1511
|
+
return { content: [{ type: "text", text: formatAsyncStartedMessage(lines.join("\n"), input.interactive) }], details: { mode: "single", results: [], asyncId: input.runId, asyncDir: input.asyncDir } };
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1407
1514
|
function externalRunnerControlError(asyncDir: string, action: "steer" | "resume"): AgentToolResult<Details> | undefined {
|
|
1408
1515
|
const status = readStatus(asyncDir);
|
|
1409
|
-
if (!status?.steps?.length || !status.steps.every((step) => step.runner?.type === "external-cli")) return undefined;
|
|
1516
|
+
if (!status?.steps?.length || !status.steps.every((step) => step.runner?.type === "external-cli" || step.runner?.type === "external-job")) return undefined;
|
|
1410
1517
|
const message = action === "steer"
|
|
1411
|
-
? "
|
|
1412
|
-
: "
|
|
1518
|
+
? "External runners do not accept live steer messages."
|
|
1519
|
+
: "External runners do not persist Pi sessions and cannot be resumed.";
|
|
1413
1520
|
return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
|
|
1414
1521
|
}
|
|
1415
1522
|
|
|
1523
|
+
async function resumeExternalJobFollowUp(input: {
|
|
1524
|
+
target: AsyncResumeSourceTarget;
|
|
1525
|
+
followUp: string;
|
|
1526
|
+
baseAgentConfig: AgentConfig;
|
|
1527
|
+
effectiveCwd: string;
|
|
1528
|
+
requestCwd: string;
|
|
1529
|
+
ctx: ExtensionContext;
|
|
1530
|
+
deps: ExecutorDeps;
|
|
1531
|
+
parentModel?: ParentModel;
|
|
1532
|
+
modelScope?: ModelScopeConfig;
|
|
1533
|
+
intercomBridge: IntercomBridgeState;
|
|
1534
|
+
parentSessionFile: string | null;
|
|
1535
|
+
absoluteDeadlineAt?: number;
|
|
1536
|
+
}): Promise<AgentToolResult<Details>> {
|
|
1537
|
+
if (input.target.kind === "live" || input.target.state === "running" || input.target.state === "queued") {
|
|
1538
|
+
return { content: [{ type: "text", text: `External-job run '${input.target.runId}' is still running. Wait for completion, then use subagent({ action: "resume", id: "${input.target.runId}", message: "..." }).` }], isError: true, details: { mode: "management", results: [] } };
|
|
1539
|
+
}
|
|
1540
|
+
const runner = input.target.runner;
|
|
1541
|
+
const externalJob = input.target.externalJob;
|
|
1542
|
+
if (runner?.type !== "external-job") return { content: [{ type: "text", text: "Internal error: external-job follow-up was requested for a non-external-job runner." }], isError: true, details: { mode: "management", results: [] } };
|
|
1543
|
+
if (!externalJob) return { content: [{ type: "text", text: `External-job run '${input.target.runId}' has no persisted provider metadata. Cannot follow up without the parent provider job id.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1544
|
+
if (externalJob.provider !== runner.provider) return { content: [{ type: "text", text: `External-job run '${input.target.runId}' has mismatched provider metadata. Refusing to follow up.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1545
|
+
if (!externalJobOptionsEqual(externalJob.options, runner.options)) return { content: [{ type: "text", text: `External-job run '${input.target.runId}' has mismatched provider options. Refusing to follow up.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1546
|
+
if (!externalJob.providerJobId) return { content: [{ type: "text", text: `External-job run '${input.target.runId}' has no parent provider job id. Cannot follow up without reopening or redispatching, so this fails closed.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1547
|
+
if (externalJob.state !== "completed") return { content: [{ type: "text", text: `External-job run '${input.target.runId}' provider state is ${externalJob.state}. Wait for completion, then use action='resume'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1548
|
+
const support = providerFollowUpSupport(runner.provider);
|
|
1549
|
+
if (!support.ok) return { content: [{ type: "text", text: support.message }], isError: true, details: { mode: "management", results: [] } };
|
|
1550
|
+
|
|
1551
|
+
const promptDigest = externalJobPromptDigest(input.followUp);
|
|
1552
|
+
const requestDigest = externalJobFollowUpRequestDigest({ provider: runner.provider, parentProviderJobId: externalJob.providerJobId, promptDigest, options: runner.options });
|
|
1553
|
+
const requestId = externalJobFollowUpRequestId(requestDigest);
|
|
1554
|
+
const runId = externalJobFollowUpRunId(requestDigest);
|
|
1555
|
+
const asyncDir = path.join(DIRS.async, runId);
|
|
1556
|
+
const currentSessionId = input.deps.state.currentSessionId;
|
|
1557
|
+
if (!currentSessionId) return { content: [{ type: "text", text: "External-job follow-up requires an active parent session." }], isError: true, details: { mode: "management", results: [] } };
|
|
1558
|
+
if (fs.existsSync(asyncDir) || fs.existsSync(resultFilePath(DIRS.results, runId))) {
|
|
1559
|
+
return externalJobFollowUpStarted({ sourceRunId: input.target.runId, runId, asyncDir, duplicate: true, interactive: input.ctx.hasUI });
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
const depthState = checkSubagentDepth(input.deps.config.maxSubagentDepth);
|
|
1563
|
+
if (depthState.blocked) {
|
|
1564
|
+
return { content: [{ type: "text", text: `Nested subagent resume blocked (depth=${depthState.depth}, max=${depthState.maxDepth}). Complete the follow-up directly instead.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1565
|
+
}
|
|
1566
|
+
const topLevelResume = depthState.depth === 0 && !resolveInheritedNestedRouteFromEnv() && !input.deps.state.workflowControllers?.has(input.target.runId);
|
|
1567
|
+
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
1568
|
+
try {
|
|
1569
|
+
activeAsyncCapacity = topLevelResume ? acquireActiveAsyncCapacity({
|
|
1570
|
+
sessionId: currentSessionId,
|
|
1571
|
+
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
1572
|
+
runId,
|
|
1573
|
+
kind: "runner",
|
|
1574
|
+
asyncDir,
|
|
1575
|
+
}, { liveWorkflowRunIds: new Set(input.deps.state.workflowControllers?.keys() ?? []) }) : undefined;
|
|
1576
|
+
} catch (error) {
|
|
1577
|
+
if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "single", results: [], activeAsyncCapacity: error.snapshot } };
|
|
1578
|
+
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "single", results: [] } };
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
const artifactConfig: ArtifactConfig = omitUndefinedProperties({ ...DEFAULT_ARTIFACT_CONFIG, enabled: true, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir });
|
|
1582
|
+
const artifactsDir = getArtifactsDir(input.parentSessionFile, input.effectiveCwd, artifactConfig.dir);
|
|
1583
|
+
const parentModel = input.parentModel;
|
|
1584
|
+
const agentConfig: AgentConfig = {
|
|
1585
|
+
...input.baseAgentConfig,
|
|
1586
|
+
runner: { type: "external-job", provider: runner.provider, options: runner.options },
|
|
1587
|
+
};
|
|
1588
|
+
const result = executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
1589
|
+
agent: input.target.agent,
|
|
1590
|
+
task: input.followUp,
|
|
1591
|
+
goal: input.followUp,
|
|
1592
|
+
agentConfig,
|
|
1593
|
+
recoveryAgentConfig: agentConfig,
|
|
1594
|
+
ctx: compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
|
|
1595
|
+
pi: input.deps.pi,
|
|
1596
|
+
cwd: input.requestCwd,
|
|
1597
|
+
currentSessionId,
|
|
1598
|
+
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1599
|
+
currentModelProvider: parentModel?.provider,
|
|
1600
|
+
currentModel: parentModel,
|
|
1601
|
+
modelScope: input.modelScope,
|
|
1602
|
+
interactive: input.ctx.hasUI,
|
|
1603
|
+
permissions: input.deps.config.permissions,
|
|
1604
|
+
}),
|
|
1605
|
+
cwd: input.effectiveCwd,
|
|
1606
|
+
artifactsDir,
|
|
1607
|
+
artifactConfig,
|
|
1608
|
+
shareEnabled: false,
|
|
1609
|
+
...(input.parentSessionFile ? { sessionRoot: input.deps.getSubagentSessionRoot(input.parentSessionFile) } : {}),
|
|
1610
|
+
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1611
|
+
waitToolEnabled: input.deps.waitToolEnabled,
|
|
1612
|
+
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
1613
|
+
worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
|
|
1614
|
+
worktreeBaseDir: input.deps.config.worktreeBaseDir,
|
|
1615
|
+
controlConfig: resolveControlConfig(input.deps.config.control, undefined),
|
|
1616
|
+
controlIntercomTarget: input.intercomBridge.active ? input.intercomBridge.orchestratorTarget : undefined,
|
|
1617
|
+
childIntercomTarget: input.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
|
|
1618
|
+
availableModels: input.ctx.modelRegistry.getAvailable().map(toModelInfo),
|
|
1619
|
+
outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
|
|
1620
|
+
...(input.absoluteDeadlineAt !== undefined ? { absoluteDeadlineAt: input.absoluteDeadlineAt } : {}),
|
|
1621
|
+
capabilityCeiling: resolveCurrentSubagentCapabilityCeiling(currentSessionId),
|
|
1622
|
+
runFanoutBudget: createRunFanoutBudget(runId, resolveMaxSubagentSpawnsPerRun(input.deps.config.maxSubagentSpawnsPerRun)),
|
|
1623
|
+
activeAsyncCapacity,
|
|
1624
|
+
externalJobFollowUp: { sourceRunId: input.target.runId, sourceStepIndex: input.target.index, parentProviderJobId: externalJob.providerJobId, requestId, requestDigest },
|
|
1625
|
+
}));
|
|
1626
|
+
if (result.isError) {
|
|
1627
|
+
activeAsyncCapacity?.rollback();
|
|
1628
|
+
return result;
|
|
1629
|
+
}
|
|
1630
|
+
return externalJobFollowUpStarted({ sourceRunId: input.target.runId, runId: result.details.asyncId ?? runId, asyncDir: result.details.asyncDir ?? asyncDir, interactive: input.ctx.hasUI });
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1416
1633
|
async function resumeAsyncRun(input: {
|
|
1417
1634
|
params: SubagentParamsLike;
|
|
1418
1635
|
requestCwd: string;
|
|
@@ -1477,11 +1694,7 @@ async function resumeAsyncRun(input: {
|
|
|
1477
1694
|
];
|
|
1478
1695
|
target = resolveNestedResumeTarget(resolved, trustedSessionRoots);
|
|
1479
1696
|
} else {
|
|
1480
|
-
|
|
1481
|
-
const unsupported = externalRunnerControlError(resolved.location.asyncDir, "resume");
|
|
1482
|
-
if (unsupported) return unsupported;
|
|
1483
|
-
}
|
|
1484
|
-
target = resolveResumeTarget(input.params, input.deps.state, { asyncRequireSessionFile: !attachChain });
|
|
1697
|
+
target = resolveResumeTarget(input.params, input.deps.state, { asyncRequireSessionFile: false });
|
|
1485
1698
|
}
|
|
1486
1699
|
} catch (error) {
|
|
1487
1700
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1519,10 +1732,11 @@ async function resumeAsyncRun(input: {
|
|
|
1519
1732
|
const modelScope = discovered.modelScope;
|
|
1520
1733
|
const sessionName = resolveIntercomSessionTarget(input.deps.pi.getSessionName(), input.ctx.sessionManager.getSessionId());
|
|
1521
1734
|
const recoveryDescriptor = "recoveryDescriptor" in target ? target.recoveryDescriptor : undefined;
|
|
1735
|
+
const recoveryContext = recoveryDescriptor?.context ?? (input.params.context === "profile" ? undefined : input.params.context);
|
|
1522
1736
|
const intercomBridge = resolveIntercomBridge({
|
|
1523
1737
|
config: input.deps.config.intercomBridge,
|
|
1524
1738
|
override: input.params.intercomBridge ?? recoveryDescriptor?.intercomBridge,
|
|
1525
|
-
context:
|
|
1739
|
+
context: recoveryContext,
|
|
1526
1740
|
orchestratorTarget: sessionName,
|
|
1527
1741
|
});
|
|
1528
1742
|
const agents = intercomBridge.active
|
|
@@ -1538,6 +1752,7 @@ async function resumeAsyncRun(input: {
|
|
|
1538
1752
|
inheritSkills: recoveryDescriptor.inheritSkills,
|
|
1539
1753
|
source: "project",
|
|
1540
1754
|
filePath: recoveryDescriptor.agentFilePath ?? path.join(getProjectSubagentsDir(recoveryDescriptor.cwd), "recovery-agent"),
|
|
1755
|
+
...(discovered.maxThinking ? { maxThinking: discovered.maxThinking } : {}),
|
|
1541
1756
|
} : undefined);
|
|
1542
1757
|
if (!baseAgentConfig) {
|
|
1543
1758
|
return {
|
|
@@ -1546,6 +1761,26 @@ async function resumeAsyncRun(input: {
|
|
|
1546
1761
|
details: { mode: "management", results: [] },
|
|
1547
1762
|
};
|
|
1548
1763
|
}
|
|
1764
|
+
if (target.source === "async" && target.runner?.type === "external-cli") {
|
|
1765
|
+
return { content: [{ type: "text", text: "External runners do not persist Pi sessions and cannot be resumed." }], isError: true, details: { mode: "management", results: [] } };
|
|
1766
|
+
}
|
|
1767
|
+
if (target.source === "async" && target.runner?.type === "external-job") {
|
|
1768
|
+
if (attachChain) return { content: [{ type: "text", text: "External-job follow-up does not support chain attachment. Use action='resume' with message instead." }], isError: true, details: { mode: "management", results: [] } };
|
|
1769
|
+
return resumeExternalJobFollowUp({
|
|
1770
|
+
target,
|
|
1771
|
+
followUp,
|
|
1772
|
+
baseAgentConfig,
|
|
1773
|
+
effectiveCwd,
|
|
1774
|
+
requestCwd: input.requestCwd,
|
|
1775
|
+
ctx: input.ctx,
|
|
1776
|
+
deps: input.deps,
|
|
1777
|
+
parentModel: input.parentModel,
|
|
1778
|
+
modelScope,
|
|
1779
|
+
intercomBridge,
|
|
1780
|
+
parentSessionFile,
|
|
1781
|
+
absoluteDeadlineAt: input.absoluteDeadlineAt,
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1549
1784
|
|
|
1550
1785
|
if (attachChain) {
|
|
1551
1786
|
if (target.source !== "async") {
|
|
@@ -1562,7 +1797,7 @@ async function resumeAsyncRun(input: {
|
|
|
1562
1797
|
details: { mode: "chain", results: [] },
|
|
1563
1798
|
};
|
|
1564
1799
|
}
|
|
1565
|
-
const runId = randomUUID()
|
|
1800
|
+
const runId = randomUUID();
|
|
1566
1801
|
const topLevelResume = depth === 0 && !resolveInheritedNestedRouteFromEnv() && !input.params.workflowParentRunId;
|
|
1567
1802
|
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
1568
1803
|
try {
|
|
@@ -1618,6 +1853,7 @@ async function resumeAsyncRun(input: {
|
|
|
1618
1853
|
sessionRoot: input.deps.getSubagentSessionRoot(parentSessionFile),
|
|
1619
1854
|
chainSkills: normalized === false ? [] : (normalized ?? []),
|
|
1620
1855
|
agentContract: input.params.agentContract,
|
|
1856
|
+
fast: input.params.fast,
|
|
1621
1857
|
dynamicFanoutMaxItems: input.deps.config.chain?.dynamicFanout?.maxItems,
|
|
1622
1858
|
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1623
1859
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
@@ -1630,6 +1866,7 @@ async function resumeAsyncRun(input: {
|
|
|
1630
1866
|
globalConcurrencyLimit: input.deps.config.globalConcurrencyLimit,
|
|
1631
1867
|
runFanoutBudget: createRunFanoutBudget(runId, resolveMaxSubagentSpawnsPerRun(input.deps.config.maxSubagentSpawnsPerRun)),
|
|
1632
1868
|
capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
|
|
1869
|
+
thinkingCeiling: target.thinkingCeiling,
|
|
1633
1870
|
activeAsyncCapacity,
|
|
1634
1871
|
}));
|
|
1635
1872
|
if (result.isError) {
|
|
@@ -1652,12 +1889,12 @@ async function resumeAsyncRun(input: {
|
|
|
1652
1889
|
const effectiveFollowUp = [...queuedBriefs.map(({ request }) => request.message), followUp].filter(Boolean).join("\n\n");
|
|
1653
1890
|
const revivalSessionFile = target.sessionFile;
|
|
1654
1891
|
if (!revivalSessionFile) {
|
|
1655
|
-
return { content: [{ type: "text", text: `Async
|
|
1892
|
+
return { content: [{ type: "text", text: `Async run '${target.runId}' child ${target.index} does not have a persisted session file to resume from.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1656
1893
|
}
|
|
1657
|
-
if (target.source === "async" &&
|
|
1894
|
+
if (target.source === "async" && asyncReviveRequiresRecoveryDescriptor(target)) {
|
|
1658
1895
|
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: [] } };
|
|
1659
1896
|
}
|
|
1660
|
-
const runId = randomUUID()
|
|
1897
|
+
const runId = randomUUID();
|
|
1661
1898
|
const topLevelResume = depth === 0 && !resolveInheritedNestedRouteFromEnv() && !input.params.workflowParentRunId;
|
|
1662
1899
|
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
1663
1900
|
try {
|
|
@@ -1718,14 +1955,20 @@ async function resumeAsyncRun(input: {
|
|
|
1718
1955
|
sourceRunId: target.runId,
|
|
1719
1956
|
...(input.deps.state.currentSessionId ? { parentSessionId: input.deps.state.currentSessionId } : {}),
|
|
1720
1957
|
},
|
|
1958
|
+
context: recoveryContext,
|
|
1721
1959
|
modelOverride: recoveryDescriptor?.model ?? target.model,
|
|
1960
|
+
fast: recoveryDescriptor?.fast,
|
|
1961
|
+
modelOverrideFromParent: recoveryDescriptor?.modelOverrideFromParent,
|
|
1722
1962
|
thinkingOverride: recoveryDescriptor?.thinking ?? target.thinking,
|
|
1963
|
+
thinkingCeiling: recoveryDescriptor?.thinkingCeiling ?? ("thinkingCeiling" in target ? target.thinkingCeiling : undefined),
|
|
1964
|
+
extensionBindings: recoveryDescriptor?.extensionBindings ?? ("extensionBindings" in target ? target.extensionBindings : undefined),
|
|
1723
1965
|
outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
|
|
1724
1966
|
maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1725
1967
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
1726
1968
|
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
1727
1969
|
worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
|
|
1728
1970
|
worktreeBaseDir: input.deps.config.worktreeBaseDir,
|
|
1971
|
+
worktree: input.params.worktree === true,
|
|
1729
1972
|
controlConfig: recoveryDescriptor?.controlConfig ?? resolveControlConfig(input.deps.config.control, input.params.control),
|
|
1730
1973
|
intercomBridge: input.params.intercomBridge ?? recoveryDescriptor?.intercomBridge,
|
|
1731
1974
|
controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
|
|
@@ -1769,7 +2012,7 @@ async function resumeAsyncRun(input: {
|
|
|
1769
2012
|
const sourceStatus = readStatus(sourceAsyncDir);
|
|
1770
2013
|
if (sourceStatus?.steering) {
|
|
1771
2014
|
for (const brief of queuedBriefs) updateSteeringTarget(sourceStatus.steering, brief.request.id, target.index, "delivered", Date.now());
|
|
1772
|
-
|
|
2015
|
+
createCapacityResilientJsonWriter({ keepAlive: true }).write(path.join(sourceAsyncDir, "status.json"), sourceStatus);
|
|
1773
2016
|
}
|
|
1774
2017
|
}
|
|
1775
2018
|
|
|
@@ -1810,6 +2053,7 @@ async function resumeAsyncRun(input: {
|
|
|
1810
2053
|
...(completed.model ? { model: completed.model } : {}),
|
|
1811
2054
|
...(completed.attemptedModels ? { attemptedModels: completed.attemptedModels } : {}),
|
|
1812
2055
|
...(completed.modelAttempts ? { modelAttempts: completed.modelAttempts } : {}),
|
|
2056
|
+
...(completed.contextOverflow ? { contextOverflow: true } : {}),
|
|
1813
2057
|
...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
|
|
1814
2058
|
...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
|
|
1815
2059
|
...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
|
|
@@ -1867,14 +2111,35 @@ function formatFailedSingleRunOutput(result: SingleResult, displayOutput: string
|
|
|
1867
2111
|
return lines.join("\n");
|
|
1868
2112
|
}
|
|
1869
2113
|
|
|
1870
|
-
function createForegroundControlNotifier(data: Pick<ExecutionContextData, "controlConfig" | "intercomBridge">, deps: Pick<ExecutorDeps, "pi" | "state">): (event: ControlEvent) => void {
|
|
2114
|
+
function createForegroundControlNotifier(data: Pick<ExecutionContextData, "controlConfig" | "contextPolicy" | "intercomBridge" | "params">, deps: Pick<ExecutorDeps, "pi" | "state">): (event: ControlEvent) => void {
|
|
1871
2115
|
return (event) => {
|
|
1872
2116
|
applyControlEventToRememberedForegroundRun(deps.state, event);
|
|
2117
|
+
const eventBridge = intercomBridgeAppliesToAgent(data.intercomBridge, data.contextPolicy, event.agent)
|
|
2118
|
+
? data.intercomBridge
|
|
2119
|
+
: { ...data.intercomBridge, active: false };
|
|
2120
|
+
const parentWorkflowRunId = data.params.workflowParentRunId;
|
|
2121
|
+
const asyncWorkflow = typeof parentWorkflowRunId === "string" ? deps.state.asyncJobs.get(parentWorkflowRunId) : undefined;
|
|
2122
|
+
const workflowKey = typeof data.params.workflowKey === "string" && data.params.workflowKey.trim()
|
|
2123
|
+
? data.params.workflowKey.trim()
|
|
2124
|
+
: undefined;
|
|
2125
|
+
const enriched = workflowKey && !event.workflowKey ? { ...event, workflowKey } : event;
|
|
2126
|
+
if (asyncWorkflow) {
|
|
2127
|
+
persistAsyncWorkflowControlEvent({
|
|
2128
|
+
job: asyncWorkflow,
|
|
2129
|
+
event: enriched,
|
|
2130
|
+
controlConfig: data.controlConfig,
|
|
2131
|
+
intercomBridge: eventBridge,
|
|
2132
|
+
childIntercomTarget: eventBridge.active
|
|
2133
|
+
? resolveSubagentIntercomTarget(enriched.runId, enriched.agent, enriched.index)
|
|
2134
|
+
: undefined,
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
1873
2137
|
emitControlNotification({
|
|
1874
2138
|
pi: deps.pi,
|
|
1875
2139
|
controlConfig: data.controlConfig,
|
|
1876
|
-
intercomBridge:
|
|
1877
|
-
event,
|
|
2140
|
+
intercomBridge: eventBridge,
|
|
2141
|
+
event: enriched,
|
|
2142
|
+
source: asyncWorkflow ? "async" : "foreground",
|
|
1878
2143
|
});
|
|
1879
2144
|
};
|
|
1880
2145
|
}
|
|
@@ -1960,22 +2225,34 @@ async function maybeBuildForegroundIntercomReceipt(input: {
|
|
|
1960
2225
|
};
|
|
1961
2226
|
}
|
|
1962
2227
|
|
|
1963
|
-
function canonicalizeAgentName(name: string, agents: AgentConfig[]): { name?: string; error?: string } {
|
|
2228
|
+
function canonicalizeAgentName(name: string, agents: AgentConfig[], diagnostics?: AgentDiscoveryDiagnostic[]): { name?: string; error?: string } {
|
|
1964
2229
|
const resolved = resolveAgentName(name, agents);
|
|
2230
|
+
const candidates = resolved.error ? agents.filter((agent) => resolveAgentName(name, [agent]).agent) : resolved.agent;
|
|
2231
|
+
const diagnostic = findBlockingAgentDiagnostic(name, candidates, diagnostics);
|
|
2232
|
+
if (diagnostic) return { error: `Agent '${name}' has invalid configuration: ${diagnostic.error}` };
|
|
1965
2233
|
if (resolved.error) return { error: resolved.error };
|
|
1966
2234
|
if (!resolved.agent) return { error: `Unknown agent: ${name}` };
|
|
1967
2235
|
return { name: resolved.agent.name };
|
|
1968
2236
|
}
|
|
1969
2237
|
|
|
1970
|
-
function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentConfig[]): { params?: SubagentParamsLike; error?: string } {
|
|
2238
|
+
function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentConfig[], diagnostics?: AgentDiscoveryDiagnostic[]): { params?: SubagentParamsLike; error?: string } {
|
|
1971
2239
|
const resolve = (name: string, location?: string): { name?: string; error?: string } => {
|
|
1972
|
-
const result = canonicalizeAgentName(name, agents);
|
|
2240
|
+
const result = canonicalizeAgentName(name, agents, diagnostics);
|
|
1973
2241
|
return result.error && location ? { error: `${result.error} (${location})` } : result;
|
|
1974
2242
|
};
|
|
1975
2243
|
if (params.agent) {
|
|
1976
2244
|
const result = resolve(params.agent);
|
|
1977
2245
|
if (result.error) return { error: result.error };
|
|
1978
2246
|
params = omitUndefinedProperties({ ...params, agent: result.name });
|
|
2247
|
+
const agent = agents.find((candidate) => candidate.name === result.name);
|
|
2248
|
+
if (params.extensionBindings !== undefined && (agent?.runner?.type === "external-cli" || agent?.runner?.type === "external-job")) return { error: `extensionBindings is not supported for runner.type='${agent.runner.type}'.` };
|
|
2249
|
+
}
|
|
2250
|
+
if (params.extensionBindings !== undefined) {
|
|
2251
|
+
try {
|
|
2252
|
+
params = { ...params, extensionBindings: normalizeExtensionBindings(params.extensionBindings)!.value };
|
|
2253
|
+
} catch (error) {
|
|
2254
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
2255
|
+
}
|
|
1979
2256
|
}
|
|
1980
2257
|
if (params.tasks) {
|
|
1981
2258
|
const tasks: TaskParam[] = [];
|
|
@@ -2173,13 +2450,44 @@ interface AgentDefaultContextPolicy {
|
|
|
2173
2450
|
usesFork: boolean;
|
|
2174
2451
|
}
|
|
2175
2452
|
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2453
|
+
type AgentDefaultContextPolicyResult = AgentDefaultContextPolicy | { error: string };
|
|
2454
|
+
|
|
2455
|
+
function resolveAgentDefaultContextPolicy(
|
|
2456
|
+
params: SubagentParamsLike,
|
|
2457
|
+
agents: AgentConfig[],
|
|
2458
|
+
defaultSubagentContext: ExtensionConfig["defaultSubagentContext"],
|
|
2459
|
+
canUseDefaultFork = false,
|
|
2460
|
+
): AgentDefaultContextPolicyResult {
|
|
2461
|
+
if (params.context === "profile") {
|
|
2462
|
+
const byName = new Map(agents.map((agent) => [agent.name, agent]));
|
|
2463
|
+
for (const agentName of collectRequestedAgentNames(params)) {
|
|
2464
|
+
const agent = byName.get(agentName);
|
|
2465
|
+
if (agent && agent.defaultContext === undefined) {
|
|
2466
|
+
return { error: `context: "profile" requires agent '${agentName}' to declare defaultContext.` };
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
const contextForAgent = (agentName: string): ContextMode => {
|
|
2470
|
+
const context = byName.get(agentName)?.defaultContext;
|
|
2471
|
+
if (context === undefined) throw new Error(`context: "profile" requires agent '${agentName}' to declare defaultContext.`);
|
|
2472
|
+
return context;
|
|
2473
|
+
};
|
|
2474
|
+
const contextSummary = summarizeContextModes(collectRequestedAgentNames(params).map(contextForAgent));
|
|
2475
|
+
return {
|
|
2476
|
+
params,
|
|
2477
|
+
contextForAgent,
|
|
2478
|
+
contextSummary,
|
|
2479
|
+
usesFork: contextSummary === "fork" || contextSummary === "mixed",
|
|
2480
|
+
};
|
|
2179
2481
|
}
|
|
2482
|
+
if (params.context === "fresh" || params.context === "fork") return resolveExplicitContextPolicy(params);
|
|
2180
2483
|
const byName = new Map(agents.map((agent) => [agent.name, agent]));
|
|
2181
2484
|
const contextForAgent = (agentName: string): ContextMode =>
|
|
2182
|
-
|
|
2485
|
+
resolveSubagentLaunchContext({
|
|
2486
|
+
explicitContext: undefined,
|
|
2487
|
+
agentDefaultContext: byName.get(agentName)?.defaultContext,
|
|
2488
|
+
defaultSubagentContext,
|
|
2489
|
+
canUseImplicitFork: canUseDefaultFork,
|
|
2490
|
+
});
|
|
2183
2491
|
const requestedAgentNames = collectRequestedAgentNames(params);
|
|
2184
2492
|
const contextSummary = summarizeContextModes(requestedAgentNames.map((name) => contextForAgent(name)));
|
|
2185
2493
|
const usesFork = contextSummary === "fork" || contextSummary === "mixed";
|
|
@@ -2192,7 +2500,10 @@ function resolveAgentDefaultContextPolicy(params: SubagentParamsLike, agents: Ag
|
|
|
2192
2500
|
}
|
|
2193
2501
|
|
|
2194
2502
|
function resolveExplicitContextPolicy(params: SubagentParamsLike): AgentDefaultContextPolicy {
|
|
2195
|
-
const context =
|
|
2503
|
+
const context = resolveSubagentLaunchContext({
|
|
2504
|
+
explicitContext: params.context === "profile" ? undefined : params.context,
|
|
2505
|
+
canUseImplicitFork: false,
|
|
2506
|
+
});
|
|
2196
2507
|
return {
|
|
2197
2508
|
params,
|
|
2198
2509
|
contextForAgent: () => context,
|
|
@@ -2213,6 +2524,31 @@ function shouldForkAgent(contextPolicy: AgentDefaultContextPolicy, agentName: st
|
|
|
2213
2524
|
return contextPolicy.contextForAgent(agentName) === "fork";
|
|
2214
2525
|
}
|
|
2215
2526
|
|
|
2527
|
+
function intercomBridgeAppliesToAgent(bridge: IntercomBridgeState, contextPolicy: AgentDefaultContextPolicy, agentName: string): boolean {
|
|
2528
|
+
if (!bridge.active) return false;
|
|
2529
|
+
return bridge.mode !== "fork-only" || shouldForkAgent(contextPolicy, agentName);
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
function applyScopedIntercomBridgeToAgents(agents: AgentConfig[], bridge: IntercomBridgeState, contextPolicy: AgentDefaultContextPolicy): AgentConfig[] {
|
|
2533
|
+
if (!bridge.active) return agents;
|
|
2534
|
+
return agents.map((agent) => intercomBridgeAppliesToAgent(bridge, contextPolicy, agent.name)
|
|
2535
|
+
? applyIntercomBridgeToAgent(agent, bridge)
|
|
2536
|
+
: agent);
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
function resolveChildIntercomTargetFactory(bridge: IntercomBridgeState, contextPolicy: AgentDefaultContextPolicy, runId: string): ((agent: string, index: number) => string | undefined) | undefined {
|
|
2540
|
+
if (!bridge.active) return undefined;
|
|
2541
|
+
return (agent, index) => intercomBridgeAppliesToAgent(bridge, contextPolicy, agent)
|
|
2542
|
+
? resolveSubagentIntercomTarget(runId, agent, index)
|
|
2543
|
+
: undefined;
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
function resolveRunLevelIntercomTarget(bridge: IntercomBridgeState, contextPolicy: AgentDefaultContextPolicy): string | undefined {
|
|
2547
|
+
if (!bridge.active) return undefined;
|
|
2548
|
+
if (bridge.mode === "fork-only" && contextPolicy.contextSummary === "mixed") return undefined;
|
|
2549
|
+
return bridge.orchestratorTarget;
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2216
2552
|
function summarizeResultContext(details: Details, fallback: ContextSummary | undefined): ContextSummary | undefined {
|
|
2217
2553
|
return summarizeContextModes(details.results.map((result) => result.context)) ?? fallback;
|
|
2218
2554
|
}
|
|
@@ -2224,7 +2560,7 @@ function buildRequestedModeError(params: SubagentParamsLike, message: string): A
|
|
|
2224
2560
|
isError: true,
|
|
2225
2561
|
details: { mode: getRequestedModeLabel(params), results: [] },
|
|
2226
2562
|
},
|
|
2227
|
-
params.context,
|
|
2563
|
+
params.context === "profile" ? undefined : params.context,
|
|
2228
2564
|
);
|
|
2229
2565
|
}
|
|
2230
2566
|
|
|
@@ -2232,12 +2568,16 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
|
|
|
2232
2568
|
if ((params.chain?.length ?? 0) > 0 || (params.tasks?.length ?? 0) > 0 || !params.agent) return params;
|
|
2233
2569
|
const agent = agents.find((candidate) => candidate.name === params.agent);
|
|
2234
2570
|
if (!agent) return params;
|
|
2571
|
+
const parentTimeoutMs = params.timeoutMs === undefined && params.maxRuntimeMs === undefined && agent.defaultTimeoutMs === undefined && params.workflowParentDeadlineAt !== undefined
|
|
2572
|
+
? Math.max(1, params.workflowParentDeadlineAt - Date.now())
|
|
2573
|
+
: undefined;
|
|
2235
2574
|
return {
|
|
2236
2575
|
...params,
|
|
2237
2576
|
...(params.async === undefined && agent.defaultAsync !== undefined ? { async: agent.defaultAsync } : {}),
|
|
2238
2577
|
...(params.timeoutMs === undefined && params.maxRuntimeMs === undefined && agent.defaultTimeoutMs !== undefined
|
|
2239
2578
|
? { timeoutMs: agent.defaultTimeoutMs }
|
|
2240
2579
|
: {}),
|
|
2580
|
+
...(parentTimeoutMs !== undefined ? { timeoutMs: parentTimeoutMs } : {}),
|
|
2241
2581
|
...(params.turnBudget === undefined && agent.defaultTurnBudget !== undefined
|
|
2242
2582
|
? { turnBudget: agent.defaultTurnBudget }
|
|
2243
2583
|
: {}),
|
|
@@ -2508,7 +2848,8 @@ function resolveStaticLaunchSummary(input: {
|
|
|
2508
2848
|
thinkingOverrideForTask: ForkThinkingOverrideForTask;
|
|
2509
2849
|
}): StaticLaunchSummary {
|
|
2510
2850
|
const agentConfig = input.agents.find((agent) => agent.name === input.agent);
|
|
2511
|
-
const externalRunner = agentConfig?.runner?.type === "external-cli";
|
|
2851
|
+
const externalRunner = agentConfig?.runner?.type === "external-cli" || agentConfig?.runner?.type === "external-job";
|
|
2852
|
+
const modelScopes = resolveModelScopesForAgent(input.modelScope, input.agent, input.parentModel);
|
|
2512
2853
|
const model = externalRunner
|
|
2513
2854
|
? undefined
|
|
2514
2855
|
: resolveEffectiveSubagentModel(
|
|
@@ -2516,8 +2857,8 @@ function resolveStaticLaunchSummary(input: {
|
|
|
2516
2857
|
agentConfig?.model,
|
|
2517
2858
|
input.parentModel,
|
|
2518
2859
|
input.availableModels,
|
|
2519
|
-
input.currentProvider,
|
|
2520
|
-
|
|
2860
|
+
agentConfig?.modelProvider ?? input.currentProvider,
|
|
2861
|
+
modelScopes.length === 0 ? {} : { scope: modelScopes },
|
|
2521
2862
|
);
|
|
2522
2863
|
const thinkingOverride = externalRunner ? undefined : input.thinkingOverrideForTask(input.agent, input.index, model);
|
|
2523
2864
|
const thinking = externalRunner ? undefined : resolveEffectiveThinking(model, thinkingOverride ?? agentConfig?.thinking);
|
|
@@ -2677,7 +3018,68 @@ function preflightForkSessionsForStaticTasks(
|
|
|
2677
3018
|
}
|
|
2678
3019
|
}
|
|
2679
3020
|
|
|
2680
|
-
function
|
|
3021
|
+
async function waitForWorkflowAsyncSingleResult(
|
|
3022
|
+
params: SubagentParamsLike,
|
|
3023
|
+
launchResult: AgentToolResult<Details>,
|
|
3024
|
+
options: { runId: string; task: string; signal?: AbortSignal; state: SubagentState; kill?: ExecutorDeps["kill"] },
|
|
3025
|
+
): Promise<AgentToolResult<Details>> {
|
|
3026
|
+
if (params.workflowAwaitAsync !== true || !launchResult.details.asyncDir) return launchResult;
|
|
3027
|
+
const asyncDir = launchResult.details.asyncDir;
|
|
3028
|
+
const resultPath = workflowAwaitedAsyncResultPath(asyncDir);
|
|
3029
|
+
const stopOnAbort = () => { stopAsyncRun(options.state, options.runId, options.kill, { asyncDir, resolvedId: options.runId }); };
|
|
3030
|
+
if (options.signal?.aborted) stopOnAbort();
|
|
3031
|
+
else options.signal?.addEventListener("abort", stopOnAbort, { once: true });
|
|
3032
|
+
let completed: Awaited<ReturnType<typeof waitForImportedAsyncRoot>>;
|
|
3033
|
+
try {
|
|
3034
|
+
completed = await waitForImportedAsyncRoot({ runId: options.runId, asyncDir, resultPath, index: 0 }, omitUndefinedProperties({
|
|
3035
|
+
shouldAbort: () => options.signal?.aborted === true,
|
|
3036
|
+
timeoutMessage: "Workflow stopped before async child completed.",
|
|
3037
|
+
}));
|
|
3038
|
+
} finally {
|
|
3039
|
+
options.signal?.removeEventListener("abort", stopOnAbort);
|
|
3040
|
+
}
|
|
3041
|
+
fs.rmSync(resultPath, { force: true });
|
|
3042
|
+
const totalCost = completed.totalCost;
|
|
3043
|
+
const childResult: SingleResult = omitUndefinedProperties({
|
|
3044
|
+
index: 0,
|
|
3045
|
+
agent: completed.agent,
|
|
3046
|
+
task: options.task,
|
|
3047
|
+
exitCode: completed.exitCode,
|
|
3048
|
+
usage: {
|
|
3049
|
+
input: totalCost?.inputTokens ?? 0,
|
|
3050
|
+
output: totalCost?.outputTokens ?? 0,
|
|
3051
|
+
cacheRead: 0,
|
|
3052
|
+
cacheWrite: 0,
|
|
3053
|
+
cost: totalCost?.costUsd ?? 0,
|
|
3054
|
+
turns: 0,
|
|
3055
|
+
},
|
|
3056
|
+
finalOutput: completed.output,
|
|
3057
|
+
outputState: completed.output.trim() ? "present" as const : "absent" as const,
|
|
3058
|
+
...(completed.error ? { error: completed.error } : {}),
|
|
3059
|
+
...(completed.timedOut ? { timedOut: true } : {}),
|
|
3060
|
+
...(completed.stopped ? { stopped: true } : {}),
|
|
3061
|
+
...(completed.sessionFile ? { sessionFile: completed.sessionFile } : {}),
|
|
3062
|
+
...(completed.model ? { model: completed.model } : {}),
|
|
3063
|
+
...(completed.attemptedModels ? { attemptedModels: completed.attemptedModels } : {}),
|
|
3064
|
+
...(completed.modelAttempts ? { modelAttempts: completed.modelAttempts } : {}),
|
|
3065
|
+
...(completed.contextOverflow ? { contextOverflow: true } : {}),
|
|
3066
|
+
...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
|
|
3067
|
+
...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
|
|
3068
|
+
...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
|
|
3069
|
+
...(completed.acceptance ? { acceptance: completed.acceptance } : {}),
|
|
3070
|
+
});
|
|
3071
|
+
return {
|
|
3072
|
+
content: [{ type: "text", text: completed.output || completed.error || `Async workflow child ${options.runId} completed without output.` }],
|
|
3073
|
+
...(completed.success ? {} : { isError: true }),
|
|
3074
|
+
details: {
|
|
3075
|
+
...launchResult.details,
|
|
3076
|
+
runId: options.runId,
|
|
3077
|
+
results: [childResult],
|
|
3078
|
+
},
|
|
3079
|
+
};
|
|
3080
|
+
}
|
|
3081
|
+
|
|
3082
|
+
async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Promise<AgentToolResult<Details> | null> {
|
|
2681
3083
|
const {
|
|
2682
3084
|
params,
|
|
2683
3085
|
effectiveCwd,
|
|
@@ -2701,27 +3103,6 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2701
3103
|
const hasSingle = !hasChain && !hasTasks && Boolean(params.agent);
|
|
2702
3104
|
if (!effectiveAsync) return null;
|
|
2703
3105
|
|
|
2704
|
-
if (hasChain && params.chain) {
|
|
2705
|
-
const chainWorktreeTaskCwdError = buildChainWorktreeTaskCwdError(params.chain as ChainStep[], effectiveCwd);
|
|
2706
|
-
if (chainWorktreeTaskCwdError) {
|
|
2707
|
-
return {
|
|
2708
|
-
content: [{ type: "text", text: chainWorktreeTaskCwdError }],
|
|
2709
|
-
isError: true,
|
|
2710
|
-
details: { mode: "chain" as const, results: [] },
|
|
2711
|
-
};
|
|
2712
|
-
}
|
|
2713
|
-
}
|
|
2714
|
-
|
|
2715
|
-
if (hasTasks && params.tasks) {
|
|
2716
|
-
const maxParallelTasks = resolveTopLevelParallelMaxTasks(deps.config.parallel?.maxTasks);
|
|
2717
|
-
if (params.tasks.length > maxParallelTasks) {
|
|
2718
|
-
return buildParallelModeError(`Max ${maxParallelTasks} tasks`);
|
|
2719
|
-
}
|
|
2720
|
-
if (params.worktree) {
|
|
2721
|
-
const worktreeTaskCwdError = buildParallelWorktreeTaskCwdError(params.tasks, effectiveCwd);
|
|
2722
|
-
if (worktreeTaskCwdError) return buildParallelModeError(worktreeTaskCwdError);
|
|
2723
|
-
}
|
|
2724
|
-
}
|
|
2725
3106
|
|
|
2726
3107
|
if (!isAsyncAvailable()) {
|
|
2727
3108
|
return {
|
|
@@ -2746,121 +3127,9 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2746
3127
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
2747
3128
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
2748
3129
|
const currentProvider = parentModel?.provider;
|
|
2749
|
-
const controlIntercomTarget = intercomBridge
|
|
2750
|
-
const childIntercomTarget = intercomBridge
|
|
2751
|
-
|
|
2752
|
-
if (hasTasks && params.tasks) {
|
|
2753
|
-
const skillOverrides = params.tasks.map((task) => normalizeSkillInput(task.skill));
|
|
2754
|
-
const parallelTasks = params.tasks.map((task, index) => compactOptional<ParallelTaskItem>({
|
|
2755
|
-
agent: task.agent,
|
|
2756
|
-
task: shouldForkAgent(contextPolicy, task.agent) ? wrapForkTask(task.task) : task.task,
|
|
2757
|
-
cwd: task.cwd,
|
|
2758
|
-
...(task.model !== undefined ? { model: task.model } : {}),
|
|
2759
|
-
...(skillOverrides[index] !== undefined ? { skill: skillOverrides[index] } : {}),
|
|
2760
|
-
...(task.output !== undefined && task.output !== true ? { output: task.output } : {}),
|
|
2761
|
-
...(task.outputMode !== undefined ? { outputMode: task.outputMode } : {}),
|
|
2762
|
-
...(task.reads !== undefined && task.reads !== true ? { reads: task.reads } : {}),
|
|
2763
|
-
...(task.progress !== undefined ? { progress: task.progress } : {}),
|
|
2764
|
-
...(task.toolBudget !== undefined ? { toolBudget: task.toolBudget } : {}),
|
|
2765
|
-
...(task.outputSchema !== undefined ? { outputSchema: task.outputSchema } : {}),
|
|
2766
|
-
...(task.agentContract !== undefined ? { agentContract: task.agentContract } : {}),
|
|
2767
|
-
...(task.acceptance !== undefined ? { acceptance: task.acceptance } : {}),
|
|
2768
|
-
}));
|
|
2769
|
-
return executeAsyncChain(id, compactOptional<Parameters<typeof executeAsyncChain>[1]>({
|
|
2770
|
-
chain: [compactOptional<ParallelStep>({
|
|
2771
|
-
parallel: parallelTasks,
|
|
2772
|
-
concurrency: resolveTopLevelParallelConcurrency(params.concurrency, deps.config.parallel?.concurrency),
|
|
2773
|
-
worktree: params.worktree,
|
|
2774
|
-
})],
|
|
2775
|
-
resultMode: "parallel",
|
|
2776
|
-
goal: params.tasks[0]?.task ?? "",
|
|
2777
|
-
agents,
|
|
2778
|
-
ctx: asyncCtx,
|
|
2779
|
-
availableModels,
|
|
2780
|
-
cwd: effectiveCwd,
|
|
2781
|
-
maxOutput: params.maxOutput,
|
|
2782
|
-
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
2783
|
-
artifactConfig,
|
|
2784
|
-
shareEnabled,
|
|
2785
|
-
sessionRoot,
|
|
2786
|
-
chainSkills: [],
|
|
2787
|
-
activeAsyncCapacity: data.activeAsyncCapacity,
|
|
2788
|
-
sessionFilesByFlatIndex: params.tasks.map((task, index) => sessionFileForTask(task.agent, index, task.model)),
|
|
2789
|
-
thinkingOverridesByFlatIndex: params.tasks.map((task, index) => thinkingOverrideForTask(task.agent, index, task.model)),
|
|
2790
|
-
contextForAgent: contextPolicy.contextForAgent,
|
|
2791
|
-
maxSubagentDepth: currentMaxSubagentDepth,
|
|
2792
|
-
waitToolEnabled: deps.waitToolEnabled,
|
|
2793
|
-
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
2794
|
-
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
2795
|
-
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
2796
|
-
controlConfig,
|
|
2797
|
-
agentContract: params.agentContract,
|
|
2798
|
-
controlIntercomTarget,
|
|
2799
|
-
childIntercomTarget,
|
|
2800
|
-
nestedRoute,
|
|
2801
|
-
timeoutMs: data.timeoutMs,
|
|
2802
|
-
turnBudget: data.turnBudget,
|
|
2803
|
-
toolBudget: data.toolBudget,
|
|
2804
|
-
usageBudget: data.usageBudget,
|
|
2805
|
-
configToolBudget: data.configToolBudget,
|
|
2806
|
-
callToolTimeoutMs: data.params?.toolTimeoutMs,
|
|
2807
|
-
configToolTimeoutMs: data.configToolTimeoutMs,
|
|
2808
|
-
capabilityCeiling: data.capabilityCeiling,
|
|
2809
|
-
runFanoutBudget: data.runFanoutBudget,
|
|
2810
|
-
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2811
|
-
parentWorkflowRunId: params.workflowParentRunId,
|
|
2812
|
-
workflowKey: params.workflowKey,
|
|
2813
|
-
}));
|
|
2814
|
-
}
|
|
3130
|
+
const controlIntercomTarget = resolveRunLevelIntercomTarget(intercomBridge, contextPolicy);
|
|
3131
|
+
const childIntercomTarget = resolveChildIntercomTargetFactory(intercomBridge, contextPolicy, id);
|
|
2815
3132
|
|
|
2816
|
-
if (hasChain && params.chain) {
|
|
2817
|
-
const normalized = normalizeSkillInput(params.skill);
|
|
2818
|
-
const chainSkills = normalized === false ? [] : (normalized ?? []);
|
|
2819
|
-
const rawChain = params.chain as ChainStep[];
|
|
2820
|
-
const chain = wrapChainTasksForFork(rawChain, contextPolicy);
|
|
2821
|
-
return executeAsyncChain(id, compactOptional<Parameters<typeof executeAsyncChain>[1]>({
|
|
2822
|
-
chain,
|
|
2823
|
-
task: params.task,
|
|
2824
|
-
goal: resolveAsyncEventGoal(params.task, rawChain),
|
|
2825
|
-
agents,
|
|
2826
|
-
ctx: asyncCtx,
|
|
2827
|
-
availableModels,
|
|
2828
|
-
cwd: effectiveCwd,
|
|
2829
|
-
maxOutput: params.maxOutput,
|
|
2830
|
-
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
2831
|
-
artifactConfig,
|
|
2832
|
-
shareEnabled,
|
|
2833
|
-
sessionRoot,
|
|
2834
|
-
chainSkills,
|
|
2835
|
-
activeAsyncCapacity: data.activeAsyncCapacity,
|
|
2836
|
-
sessionFilesByFlatIndex: collectChainSessionFiles(chain, sessionFileForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
2837
|
-
thinkingOverridesByFlatIndex: collectChainThinkingOverrides(chain, thinkingOverrideForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
2838
|
-
contextForAgent: contextPolicy.contextForAgent,
|
|
2839
|
-
dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
|
|
2840
|
-
maxSubagentDepth: currentMaxSubagentDepth,
|
|
2841
|
-
waitToolEnabled: deps.waitToolEnabled,
|
|
2842
|
-
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
2843
|
-
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
2844
|
-
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
2845
|
-
controlConfig,
|
|
2846
|
-
agentContract: params.agentContract,
|
|
2847
|
-
controlIntercomTarget,
|
|
2848
|
-
childIntercomTarget,
|
|
2849
|
-
nestedRoute,
|
|
2850
|
-
timeoutMs: data.timeoutMs,
|
|
2851
|
-
turnBudget: data.turnBudget,
|
|
2852
|
-
toolBudget: data.toolBudget,
|
|
2853
|
-
usageBudget: data.usageBudget,
|
|
2854
|
-
configToolBudget: data.configToolBudget,
|
|
2855
|
-
callToolTimeoutMs: data.params?.toolTimeoutMs,
|
|
2856
|
-
configToolTimeoutMs: data.configToolTimeoutMs,
|
|
2857
|
-
capabilityCeiling: data.capabilityCeiling,
|
|
2858
|
-
runFanoutBudget: data.runFanoutBudget,
|
|
2859
|
-
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2860
|
-
parentWorkflowRunId: params.workflowParentRunId,
|
|
2861
|
-
workflowKey: params.workflowKey,
|
|
2862
|
-
}));
|
|
2863
|
-
}
|
|
2864
3133
|
|
|
2865
3134
|
if (hasSingle) {
|
|
2866
3135
|
const a = agents.find((x) => x.name === params.agent);
|
|
@@ -2873,17 +3142,22 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2873
3142
|
}
|
|
2874
3143
|
const rawOutput = params.output !== undefined ? params.output : a.output;
|
|
2875
3144
|
const effectiveOutput = normalizeSingleOutputOverride(rawOutput, a.output);
|
|
2876
|
-
const effectiveOutputMode = params.outputMode ?? "inline";
|
|
3145
|
+
const effectiveOutputMode = params.outputMode ?? a.outputMode ?? "inline";
|
|
2877
3146
|
const normalizedSkills = normalizeSkillInput(params.skill);
|
|
2878
3147
|
const skills = normalizedSkills === false ? [] : normalizedSkills;
|
|
2879
3148
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, a.maxSubagentDepth);
|
|
2880
|
-
const externalRunnerWithoutExplicitModel = a.runner?.type === "external-cli"
|
|
3149
|
+
const externalRunnerWithoutExplicitModel = (a.runner?.type === "external-cli" || a.runner?.type === "external-job")
|
|
2881
3150
|
&& params.model === undefined
|
|
2882
3151
|
&& (a.model === undefined || (a.modelSource?.type === "subagents.defaultModel" && a.model === a.modelSource.model));
|
|
2883
|
-
|
|
3152
|
+
if ((a.runner?.type === "external-cli" || a.runner?.type === "external-job") && (params.fast ?? a.fast) === true) {
|
|
3153
|
+
return buildRequestedModeError(params, `Agent '${a.name}' uses runner.type='${a.runner.type}' and does not support fast mode.`);
|
|
3154
|
+
}
|
|
3155
|
+
const modelScopes = resolveModelScopesForAgent(data.modelScope, a.name, parentModel);
|
|
3156
|
+
const modelOverride = a.runner?.type === "external-cli" || a.runner?.type === "external-job"
|
|
2884
3157
|
? params.model ?? (externalRunnerWithoutExplicitModel ? undefined : a.model)
|
|
2885
|
-
: resolveEffectiveSubagentModel(params.model as string | undefined, a.model, parentModel, availableModels, currentProvider,
|
|
2886
|
-
|
|
3158
|
+
: resolveEffectiveSubagentModel(params.model as string | undefined, a.model, parentModel, availableModels, a.modelProvider ?? currentProvider, modelScopes.length === 0 ? {} : { scope: modelScopes });
|
|
3159
|
+
const modelOverrideFromParent = inheritsParentModel(params.model as string | undefined, a.model, parentModel);
|
|
3160
|
+
const asyncResult = executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
2887
3161
|
agent: params.agent!,
|
|
2888
3162
|
task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
|
|
2889
3163
|
goal: params.task ?? "",
|
|
@@ -2898,7 +3172,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2898
3172
|
shareEnabled,
|
|
2899
3173
|
activeAsyncCapacity: data.activeAsyncCapacity,
|
|
2900
3174
|
sessionRoot,
|
|
2901
|
-
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride),
|
|
3175
|
+
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride, modelOverrideFromParent),
|
|
2902
3176
|
context: contextPolicy.contextForAgent(params.agent!),
|
|
2903
3177
|
skills,
|
|
2904
3178
|
output: effectiveOutput,
|
|
@@ -2906,9 +3180,13 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2906
3180
|
...(params.reads !== undefined ? { reads: params.reads } : {}),
|
|
2907
3181
|
outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
|
|
2908
3182
|
modelOverride,
|
|
2909
|
-
|
|
3183
|
+
fast: params.fast,
|
|
3184
|
+
modelOverrideFromParent,
|
|
3185
|
+
thinkingOverride: externalRunnerWithoutExplicitModel ? undefined : thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent),
|
|
3186
|
+
thinkingCeiling: a.maxThinking,
|
|
2910
3187
|
maxSubagentDepth,
|
|
2911
3188
|
waitToolEnabled: deps.waitToolEnabled,
|
|
3189
|
+
...(params.worktree === true ? { worktree: true } : {}),
|
|
2912
3190
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
2913
3191
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
2914
3192
|
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
@@ -2919,6 +3197,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2919
3197
|
nestedRoute,
|
|
2920
3198
|
agentContract: params.agentContract,
|
|
2921
3199
|
structuredOutputSchema: params.outputSchema,
|
|
3200
|
+
extensionBindings: params.extensionBindings,
|
|
2922
3201
|
acceptance: params.acceptance,
|
|
2923
3202
|
timeoutMs: data.timeoutMs,
|
|
2924
3203
|
turnBudget: data.turnBudget,
|
|
@@ -2931,314 +3210,19 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2931
3210
|
runFanoutBudget: data.runFanoutBudget,
|
|
2932
3211
|
parentWorkflowRunId: params.workflowParentRunId,
|
|
2933
3212
|
workflowKey: params.workflowKey,
|
|
3213
|
+
workflowAwaitAsync: params.workflowAwaitAsync,
|
|
2934
3214
|
}));
|
|
3215
|
+
return waitForWorkflowAsyncSingleResult(params, asyncResult, { runId: id, task: params.task ?? "", signal: data.signal, state: deps.state, kill: deps.kill });
|
|
2935
3216
|
}
|
|
2936
3217
|
|
|
2937
3218
|
return null;
|
|
2938
3219
|
}
|
|
2939
3220
|
|
|
2940
|
-
|
|
2941
|
-
const {
|
|
2942
|
-
params,
|
|
2943
|
-
effectiveCwd,
|
|
2944
|
-
agents,
|
|
2945
|
-
ctx,
|
|
2946
|
-
signal,
|
|
2947
|
-
runId,
|
|
2948
|
-
shareEnabled,
|
|
2949
|
-
sessionDirForIndex,
|
|
2950
|
-
sessionFileForIndex,
|
|
2951
|
-
sessionFileForTask,
|
|
2952
|
-
thinkingOverrideForTask,
|
|
2953
|
-
artifactsDir,
|
|
2954
|
-
artifactConfig,
|
|
2955
|
-
onUpdate,
|
|
2956
|
-
sessionRoot,
|
|
2957
|
-
controlConfig,
|
|
2958
|
-
contextPolicy,
|
|
2959
|
-
} = data;
|
|
2960
|
-
const onControlEvent = createForegroundControlNotifier(data, deps);
|
|
2961
|
-
const childIntercomTarget = data.intercomBridge.active ? resolveSubagentIntercomTarget : undefined;
|
|
2962
|
-
const foregroundControl = deps.state.foregroundControls.get(runId);
|
|
2963
|
-
const normalized = normalizeSkillInput(params.skill);
|
|
2964
|
-
const chainSkills = normalized === false ? [] : (normalized ?? []);
|
|
2965
|
-
const chain = wrapChainTasksForFork(params.chain as ChainStep[], contextPolicy);
|
|
2966
|
-
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
2967
|
-
const chainCtx = normalizeParentModel(ctx.model) || !data.parentModel ? ctx : ({ ...ctx, model: data.parentModel } as ExtensionContext);
|
|
2968
|
-
const chainResult = await executeChain(compactOptional<Parameters<typeof executeChain>[0]>({
|
|
2969
|
-
chain,
|
|
2970
|
-
task: params.task,
|
|
2971
|
-
agents,
|
|
2972
|
-
ctx: chainCtx,
|
|
2973
|
-
modelScope: data.modelScope,
|
|
2974
|
-
intercomEvents: deps.pi.events,
|
|
2975
|
-
signal,
|
|
2976
|
-
runId,
|
|
2977
|
-
cwd: effectiveCwd,
|
|
2978
|
-
shareEnabled,
|
|
2979
|
-
sessionDirForIndex,
|
|
2980
|
-
sessionFileForIndex,
|
|
2981
|
-
sessionFileForTask,
|
|
2982
|
-
thinkingOverrideForTask,
|
|
2983
|
-
contextForAgent: contextPolicy.contextForAgent,
|
|
2984
|
-
artifactsDir,
|
|
2985
|
-
artifactConfig,
|
|
2986
|
-
includeProgress: params.includeProgress,
|
|
2987
|
-
clarify: params.clarify,
|
|
2988
|
-
onUpdate,
|
|
2989
|
-
onControlEvent,
|
|
2990
|
-
controlConfig,
|
|
2991
|
-
agentContract: params.agentContract,
|
|
2992
|
-
childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(runId, agent, index) : undefined,
|
|
2993
|
-
orchestratorIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
2994
|
-
foregroundControl,
|
|
2995
|
-
nestedRoute: foregroundControl?.nestedRoute,
|
|
2996
|
-
chainSkills,
|
|
2997
|
-
chainDir: params.chainDir ?? getChainRunsDir(effectiveCwd, artifactConfig.dir),
|
|
2998
|
-
dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
|
|
2999
|
-
maxSubagentDepth: currentMaxSubagentDepth,
|
|
3000
|
-
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
3001
|
-
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
3002
|
-
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
3003
|
-
timeoutMs: data.timeoutMs,
|
|
3004
|
-
deadlineAt: data.deadlineAt,
|
|
3005
|
-
turnBudget: data.turnBudget,
|
|
3006
|
-
onDetachedExit: (index, result) => {
|
|
3007
|
-
try {
|
|
3008
|
-
updateRememberedForegroundChild(deps.state, { runId, mode: "chain", cwd: effectiveCwd, sessionId: data.parentSessionId, index, result, events: deps.pi.events });
|
|
3009
|
-
} finally {
|
|
3010
|
-
removeForegroundControlIfIdle(deps.state, runId);
|
|
3011
|
-
}
|
|
3012
|
-
},
|
|
3013
|
-
onForegroundChildSettled: () => {
|
|
3014
|
-
removeForegroundControlIfIdle(deps.state, runId);
|
|
3015
|
-
},
|
|
3016
|
-
toolBudget: data.toolBudget,
|
|
3017
|
-
usageBudget: data.usageBudget,
|
|
3018
|
-
configToolBudget: data.configToolBudget,
|
|
3019
|
-
callToolTimeoutMs: data.params?.toolTimeoutMs,
|
|
3020
|
-
configToolTimeoutMs: data.configToolTimeoutMs,
|
|
3021
|
-
permissions: deps.config.permissions,
|
|
3022
|
-
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
3023
|
-
capabilityCeiling: data.capabilityCeiling,
|
|
3024
|
-
runFanoutBudget: data.runFanoutBudget,
|
|
3025
|
-
}));
|
|
3026
|
-
|
|
3027
|
-
if (chainResult.requestedAsync) {
|
|
3028
|
-
if (!isAsyncAvailable()) {
|
|
3029
|
-
return {
|
|
3030
|
-
content: [{ type: "text", text: "Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the pi-subagents package dependencies are installed." }],
|
|
3031
|
-
isError: true,
|
|
3032
|
-
details: { mode: "chain" as const, results: [] },
|
|
3033
|
-
};
|
|
3034
|
-
}
|
|
3035
|
-
const id = data.asyncRunId;
|
|
3036
|
-
const capacity = acquireClarifiedBackgroundCapacity(data, deps, "chain", id);
|
|
3037
|
-
if (capacity.errorResult) return capacity.errorResult;
|
|
3038
|
-
const parentModel = data.parentModel;
|
|
3039
|
-
const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
|
|
3040
|
-
pi: deps.pi,
|
|
3041
|
-
cwd: ctx.cwd,
|
|
3042
|
-
currentSessionId: data.parentSessionId!,
|
|
3043
|
-
parentSessionId: data.parentPiSessionId,
|
|
3044
|
-
currentModelProvider: parentModel?.provider,
|
|
3045
|
-
currentModel: parentModel,
|
|
3046
|
-
modelScope: data.modelScope,
|
|
3047
|
-
interactive: ctx.hasUI,
|
|
3048
|
-
permissions: deps.config.permissions,
|
|
3049
|
-
});
|
|
3050
|
-
const rawAsyncChain = chainResult.requestedAsync.chain;
|
|
3051
|
-
const asyncChain = wrapChainTasksForFork(rawAsyncChain, contextPolicy);
|
|
3052
|
-
const firstAgent = firstChainAgent(rawAsyncChain);
|
|
3053
|
-
try {
|
|
3054
|
-
return executeAsyncChain(id, compactOptional<Parameters<typeof executeAsyncChain>[1]>({
|
|
3055
|
-
chain: asyncChain,
|
|
3056
|
-
task: params.task,
|
|
3057
|
-
goal: resolveAsyncEventGoal(params.task, rawAsyncChain, firstAgent ? shouldForkAgent(contextPolicy, firstAgent) : false),
|
|
3058
|
-
agents,
|
|
3059
|
-
ctx: asyncCtx,
|
|
3060
|
-
availableModels: ctx.modelRegistry.getAvailable().map(toModelInfo),
|
|
3061
|
-
cwd: effectiveCwd,
|
|
3062
|
-
maxOutput: params.maxOutput,
|
|
3063
|
-
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
3064
|
-
artifactConfig,
|
|
3065
|
-
shareEnabled,
|
|
3066
|
-
sessionRoot,
|
|
3067
|
-
chainSkills: chainResult.requestedAsync.chainSkills,
|
|
3068
|
-
activeAsyncCapacity: capacity.activeAsyncCapacity,
|
|
3069
|
-
sessionFilesByFlatIndex: collectChainSessionFiles(asyncChain, sessionFileForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
3070
|
-
thinkingOverridesByFlatIndex: collectChainThinkingOverrides(asyncChain, thinkingOverrideForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
3071
|
-
contextForAgent: contextPolicy.contextForAgent,
|
|
3072
|
-
dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
|
|
3073
|
-
maxSubagentDepth: currentMaxSubagentDepth,
|
|
3074
|
-
waitToolEnabled: deps.waitToolEnabled,
|
|
3075
|
-
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
3076
|
-
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
3077
|
-
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
3078
|
-
controlConfig,
|
|
3079
|
-
agentContract: params.agentContract,
|
|
3080
|
-
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
3081
|
-
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
3082
|
-
nestedRoute: data.nestedRoute,
|
|
3083
|
-
timeoutMs: data.timeoutMs,
|
|
3084
|
-
turnBudget: data.turnBudget,
|
|
3085
|
-
toolBudget: data.toolBudget,
|
|
3086
|
-
usageBudget: data.usageBudget,
|
|
3087
|
-
configToolBudget: data.configToolBudget,
|
|
3088
|
-
callToolTimeoutMs: data.params?.toolTimeoutMs,
|
|
3089
|
-
configToolTimeoutMs: data.configToolTimeoutMs,
|
|
3090
|
-
capabilityCeiling: data.capabilityCeiling,
|
|
3091
|
-
runFanoutBudget: data.runFanoutBudget,
|
|
3092
|
-
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
3093
|
-
}));
|
|
3094
|
-
} finally {
|
|
3095
|
-
finalizeClarifiedBackgroundCapacity(data, deps, capacity.activeAsyncCapacity);
|
|
3096
|
-
}
|
|
3097
|
-
}
|
|
3098
|
-
|
|
3099
|
-
const rawChainDetails = chainResult.details ? compactOptional<Details>({ ...chainResult.details, runId, timeoutMs: data.timeoutMs }) : undefined;
|
|
3100
|
-
if (foregroundControl && rawChainDetails) {
|
|
3101
|
-
updateForegroundNestedProjection(foregroundControl);
|
|
3102
|
-
attachRootChildrenToSteps(runId, rawChainDetails.results, foregroundControl.nestedChildren);
|
|
3103
|
-
rawChainDetails.totalCost = sumResultsCost(rawChainDetails.results);
|
|
3104
|
-
const usageBudget = usageBudgetState(data.usageBudget, rawChainDetails.totalCost);
|
|
3105
|
-
if (usageBudget === undefined) delete rawChainDetails.usageBudget;
|
|
3106
|
-
else rawChainDetails.usageBudget = usageBudget;
|
|
3107
|
-
}
|
|
3108
|
-
const chainDetails = rawChainDetails ? compactForegroundDetails(rawChainDetails) : undefined;
|
|
3109
|
-
if (chainDetails) rememberForegroundRun(deps.state, { runId, mode: "chain", cwd: effectiveCwd, sessionId: data.parentSessionId, results: chainDetails.results, checkpoint: chainDetails.checkpoint });
|
|
3110
|
-
const intercomReceipt = chainDetails && !chainDetails.results.some((result) => result.interrupted || result.detached)
|
|
3111
|
-
? await maybeBuildForegroundIntercomReceipt({
|
|
3112
|
-
pi: deps.pi,
|
|
3113
|
-
intercomBridge: data.intercomBridge,
|
|
3114
|
-
runId,
|
|
3115
|
-
mode: "chain",
|
|
3116
|
-
details: chainDetails,
|
|
3117
|
-
...(foregroundControl?.nestedChildren?.length ? { nestedChildren: foregroundControl.nestedChildren } : {}),
|
|
3118
|
-
})
|
|
3119
|
-
: null;
|
|
3120
|
-
if (intercomReceipt) {
|
|
3121
|
-
return {
|
|
3122
|
-
...chainResult,
|
|
3123
|
-
content: [{ type: "text", text: intercomReceipt.text }],
|
|
3124
|
-
details: intercomReceipt.details,
|
|
3125
|
-
};
|
|
3126
|
-
}
|
|
3127
|
-
|
|
3128
|
-
return chainDetails ? { ...chainResult, details: chainDetails } : chainResult;
|
|
3129
|
-
}
|
|
3130
|
-
|
|
3131
|
-
interface ForegroundParallelRunInput {
|
|
3132
|
-
tasks: TaskParam[];
|
|
3133
|
-
taskTexts: string[];
|
|
3134
|
-
taskDescriptions: string[];
|
|
3135
|
-
agents: AgentConfig[];
|
|
3136
|
-
ctx: ExtensionContext;
|
|
3137
|
-
state: SubagentState;
|
|
3138
|
-
intercomEvents: IntercomEventBus;
|
|
3139
|
-
parentSessionId: string | null;
|
|
3140
|
-
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
3141
|
-
runFanoutBudget: RunFanoutBudgetDescriptor;
|
|
3142
|
-
signal: AbortSignal;
|
|
3143
|
-
runId: string;
|
|
3144
|
-
sessionDirForIndex: (idx?: number) => string | undefined;
|
|
3145
|
-
sessionFileForIndex: (idx?: number) => string | undefined;
|
|
3146
|
-
sessionFileForTask: ForkSessionFileForTask;
|
|
3147
|
-
thinkingOverrideForTask: ForkThinkingOverrideForTask;
|
|
3148
|
-
shareEnabled: boolean;
|
|
3149
|
-
artifactConfig: ArtifactConfig;
|
|
3150
|
-
artifactsDir: string;
|
|
3151
|
-
outputBaseDir: string;
|
|
3152
|
-
maxOutput?: MaxOutputConfig;
|
|
3153
|
-
paramsCwd: string;
|
|
3154
|
-
progressDir: string;
|
|
3155
|
-
maxSubagentDepths: number[];
|
|
3156
|
-
waitToolEnabled?: boolean;
|
|
3157
|
-
availableModels: ModelInfo[];
|
|
3158
|
-
modelScope?: ModelScopeConfig;
|
|
3159
|
-
parentModel?: ParentModel;
|
|
3160
|
-
modelOverrides: (string | undefined)[];
|
|
3161
|
-
behaviors: Array<ReturnType<typeof resolveStepBehavior>>;
|
|
3162
|
-
firstProgressIndex: number;
|
|
3163
|
-
controlConfig: ResolvedControlConfig;
|
|
3164
|
-
contextPolicy: AgentDefaultContextPolicy;
|
|
3165
|
-
onControlEvent?: (event: ControlEvent) => void;
|
|
3166
|
-
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
3167
|
-
orchestratorIntercomTarget?: string;
|
|
3168
|
-
foregroundControl?: SubagentState["foregroundControls"] extends Map<string, infer T> ? T : never;
|
|
3169
|
-
concurrencyLimit: number;
|
|
3170
|
-
globalSemaphore?: Semaphore;
|
|
3171
|
-
liveResults: (SingleResult | undefined)[];
|
|
3172
|
-
liveProgress: (AgentProgress | undefined)[];
|
|
3173
|
-
onUpdate?: (r: AgentToolResult<Details>) => void;
|
|
3174
|
-
worktreeSetup?: WorktreeSetup;
|
|
3175
|
-
timeoutMs?: number;
|
|
3176
|
-
deadlineAt?: number;
|
|
3177
|
-
/** Per-call per-tool timeout, resolved separately for each task's agent. */
|
|
3178
|
-
toolTimeoutMs?: number;
|
|
3179
|
-
/** Raw global config.toolTimeoutMs, used by each task's resolver. */
|
|
3180
|
-
configToolTimeoutMs?: number;
|
|
3181
|
-
turnBudget?: ResolvedTurnBudget;
|
|
3182
|
-
usageBudget?: UsageBudgetConfig;
|
|
3183
|
-
toolBudgets: (ResolvedToolBudget | undefined)[];
|
|
3184
|
-
agentContract?: AgentContract;
|
|
3185
|
-
permissions?: ExtensionConfig["permissions"];
|
|
3186
|
-
}
|
|
3187
|
-
function buildParallelModeError(message: string): AgentToolResult<Details> {
|
|
3188
|
-
return {
|
|
3189
|
-
content: [{ type: "text", text: message }],
|
|
3190
|
-
isError: true,
|
|
3191
|
-
details: { mode: "parallel" as const, results: [] },
|
|
3192
|
-
};
|
|
3193
|
-
}
|
|
3194
|
-
|
|
3195
|
-
function acquireClarifiedBackgroundCapacity(
|
|
3196
|
-
data: ExecutionContextData,
|
|
3197
|
-
deps: ExecutorDeps,
|
|
3198
|
-
mode: "single" | "parallel" | "chain",
|
|
3199
|
-
asyncRunId: string,
|
|
3200
|
-
): { activeAsyncCapacity?: ActiveAsyncCapacityHandle; errorResult?: AgentToolResult<Details> } {
|
|
3201
|
-
if (!data.topLevelAsyncCapacityEligible || !data.parentSessionId) return {};
|
|
3202
|
-
const liveWorkflowRunIds = new Set(deps.state.workflowControllers?.keys() ?? []);
|
|
3203
|
-
const activeLimit = resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession);
|
|
3204
|
-
try {
|
|
3205
|
-
const activeAsyncCapacity = acquireActiveAsyncCapacity({
|
|
3206
|
-
sessionId: data.parentSessionId,
|
|
3207
|
-
limit: activeLimit,
|
|
3208
|
-
runId: asyncRunId,
|
|
3209
|
-
kind: "runner",
|
|
3210
|
-
asyncDir: path.join(DIRS.async, asyncRunId),
|
|
3211
|
-
}, { liveWorkflowRunIds });
|
|
3212
|
-
deps.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(data.parentSessionId, activeLimit, { liveWorkflowRunIds });
|
|
3213
|
-
return { activeAsyncCapacity };
|
|
3214
|
-
} catch (error) {
|
|
3215
|
-
if (error instanceof ActiveAsyncCapacityError) {
|
|
3216
|
-
deps.state.activeAsyncCapacity = error.snapshot;
|
|
3217
|
-
return { errorResult: { content: [{ type: "text", text: error.message }], isError: true, details: { mode, results: [], activeAsyncCapacity: error.snapshot } } };
|
|
3218
|
-
}
|
|
3219
|
-
throw error;
|
|
3220
|
-
}
|
|
3221
|
-
}
|
|
3222
|
-
|
|
3223
|
-
function finalizeClarifiedBackgroundCapacity(
|
|
3224
|
-
data: ExecutionContextData,
|
|
3225
|
-
deps: ExecutorDeps,
|
|
3226
|
-
activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined,
|
|
3227
|
-
): void {
|
|
3228
|
-
if (!activeAsyncCapacity || !data.parentSessionId) return;
|
|
3229
|
-
if (!activeAsyncCapacity.owner.runnerStartedAt) activeAsyncCapacity.rollback();
|
|
3230
|
-
deps.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(
|
|
3231
|
-
data.parentSessionId,
|
|
3232
|
-
resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession),
|
|
3233
|
-
{ liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []) },
|
|
3234
|
-
);
|
|
3235
|
-
}
|
|
3236
|
-
|
|
3237
|
-
function createParallelWorktreeSetup(
|
|
3221
|
+
function createSingleWorktreeSetup(
|
|
3238
3222
|
enabled: boolean | undefined,
|
|
3239
3223
|
cwd: string,
|
|
3240
3224
|
runId: string,
|
|
3241
|
-
|
|
3225
|
+
agent: string,
|
|
3242
3226
|
setupHook: ExtensionConfig["worktreeSetupHook"],
|
|
3243
3227
|
setupHookTimeoutMs: ExtensionConfig["worktreeSetupHookTimeoutMs"],
|
|
3244
3228
|
baseDir: ExtensionConfig["worktreeBaseDir"],
|
|
@@ -3246,8 +3230,8 @@ function createParallelWorktreeSetup(
|
|
|
3246
3230
|
if (!enabled) return {};
|
|
3247
3231
|
try {
|
|
3248
3232
|
return {
|
|
3249
|
-
setup: createWorktrees(cwd, runId,
|
|
3250
|
-
agents:
|
|
3233
|
+
setup: createWorktrees(cwd, runId, 1, omitUndefinedProperties({
|
|
3234
|
+
agents: [agent],
|
|
3251
3235
|
setupHook: setupHook
|
|
3252
3236
|
? { hookPath: setupHook, ...(setupHookTimeoutMs === undefined ? {} : { timeoutMs: setupHookTimeoutMs }) }
|
|
3253
3237
|
: undefined,
|
|
@@ -3256,27 +3240,25 @@ function createParallelWorktreeSetup(
|
|
|
3256
3240
|
};
|
|
3257
3241
|
} catch (error) {
|
|
3258
3242
|
const message = error instanceof Error ? error.message : String(error);
|
|
3259
|
-
return { errorResult:
|
|
3243
|
+
return { errorResult: { content: [{ type: "text", text: message }], isError: true, details: { mode: "single", results: [] } } };
|
|
3260
3244
|
}
|
|
3261
3245
|
}
|
|
3262
3246
|
|
|
3263
|
-
function buildParallelWorktreeTaskCwdError(
|
|
3264
|
-
tasks: ReadonlyArray<{ agent: string; cwd?: string }>,
|
|
3265
|
-
sharedCwd: string,
|
|
3266
|
-
): string | undefined {
|
|
3267
|
-
const conflict = findWorktreeTaskCwdConflict(tasks, sharedCwd);
|
|
3268
|
-
if (!conflict) return undefined;
|
|
3269
|
-
return formatWorktreeTaskCwdConflict(conflict, sharedCwd);
|
|
3270
|
-
}
|
|
3271
|
-
|
|
3272
3247
|
function resolveConfiguredSingleRunOutputBaseDir(deps: ExecutorDeps): string | undefined {
|
|
3273
3248
|
return deps.config.singleRunOutputBaseDir
|
|
3274
3249
|
? path.resolve(deps.expandTilde(deps.config.singleRunOutputBaseDir))
|
|
3275
3250
|
: undefined;
|
|
3276
3251
|
}
|
|
3277
3252
|
|
|
3253
|
+
export function sanitizeRunPathSegment(value: string, maxBytes = 120): string {
|
|
3254
|
+
const sanitized = value.trim().replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^_+|_+$/g, "");
|
|
3255
|
+
if (!sanitized) return "unknown";
|
|
3256
|
+
if (Buffer.byteLength(sanitized, "utf-8") <= maxBytes) return sanitized;
|
|
3257
|
+
return sanitized.slice(0, maxBytes).replace(/_+$/, "") || "unknown";
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3278
3260
|
function resolveSingleRunOutputBaseDir(deps: ExecutorDeps, artifactsDir: string, runId: string): string {
|
|
3279
|
-
return resolveConfiguredSingleRunOutputBaseDir(deps) ?? path.join(artifactsDir, "outputs", runId);
|
|
3261
|
+
return resolveConfiguredSingleRunOutputBaseDir(deps) ?? path.join(artifactsDir, "outputs", sanitizeRunPathSegment(runId));
|
|
3280
3262
|
}
|
|
3281
3263
|
|
|
3282
3264
|
function resolveWorkflowAggregateOutputPath(
|
|
@@ -3284,9 +3266,7 @@ function resolveWorkflowAggregateOutputPath(
|
|
|
3284
3266
|
ctxCwd: string,
|
|
3285
3267
|
workflowCwd: string,
|
|
3286
3268
|
outputBaseDir: string,
|
|
3287
|
-
configuredOutputBaseDir: string | undefined,
|
|
3288
3269
|
): string | undefined {
|
|
3289
|
-
if (typeof output === "string" && output && !path.isAbsolute(output) && !configuredOutputBaseDir) return path.resolve(workflowCwd, output);
|
|
3290
3270
|
return resolveSingleOutputPath(output, ctxCwd, workflowCwd, outputBaseDir);
|
|
3291
3271
|
}
|
|
3292
3272
|
|
|
@@ -3295,12 +3275,13 @@ function workflowChildDefaultOutput(aggregateOutputPath: string | undefined, art
|
|
|
3295
3275
|
const parsed = path.parse(aggregateOutputPath);
|
|
3296
3276
|
return path.join(parsed.dir, `${parsed.name}.${workflowKey}${parsed.ext || ".md"}`);
|
|
3297
3277
|
}
|
|
3298
|
-
return path.join(artifactsDir, "outputs", workflowRunId, `${workflowKey}.md`);
|
|
3278
|
+
return path.join(artifactsDir, "outputs", sanitizeRunPathSegment(workflowRunId), `${workflowKey}.md`);
|
|
3299
3279
|
}
|
|
3300
3280
|
|
|
3301
|
-
function writeWorkflowAggregateOutput(outputPath: string | undefined, text: string): string | undefined {
|
|
3281
|
+
function writeWorkflowAggregateOutput(outputPath: string | undefined, text: string, producedChildOutputPaths: ReadonlySet<string>): string | undefined {
|
|
3302
3282
|
if (!outputPath) return undefined;
|
|
3303
3283
|
try {
|
|
3284
|
+
if (producedChildOutputPaths.has(outputPath)) return undefined;
|
|
3304
3285
|
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
3305
3286
|
fs.writeFileSync(outputPath, text, "utf-8");
|
|
3306
3287
|
return undefined;
|
|
@@ -3321,23 +3302,41 @@ function resolveWorkflowChildOutputPath(input: {
|
|
|
3321
3302
|
aggregateOutputPath?: string;
|
|
3322
3303
|
configuredOutputBaseDir?: string;
|
|
3323
3304
|
discoverAgents: (cwd: string, scope: AgentScope) => { agents: AgentConfig[] };
|
|
3305
|
+
agents: AgentConfig[];
|
|
3324
3306
|
workflowAgentScope?: unknown;
|
|
3307
|
+
state?: SubagentState;
|
|
3325
3308
|
key: string;
|
|
3326
3309
|
params: Record<string, unknown>;
|
|
3327
|
-
}): string
|
|
3310
|
+
}): { path?: string; inherited: boolean } {
|
|
3311
|
+
if (typeof input.params.resume === "string") {
|
|
3312
|
+
if (!input.state) return { path: undefined, inherited: false };
|
|
3313
|
+
const index = input.params.index;
|
|
3314
|
+
const target = resolveResumeTarget({
|
|
3315
|
+
id: input.params.resume.trim(),
|
|
3316
|
+
...(typeof index === "number" && Number.isInteger(index) ? { index } : {}),
|
|
3317
|
+
}, input.state);
|
|
3318
|
+
return { path: "recoveryDescriptor" in target ? target.recoveryDescriptor?.outputPath : undefined, inherited: false };
|
|
3319
|
+
}
|
|
3328
3320
|
const rawOutput = input.params.output;
|
|
3329
3321
|
const hasExplicitOutput = typeof rawOutput === "string" || typeof rawOutput === "boolean";
|
|
3330
3322
|
const childCwd = typeof input.params.cwd === "string" ? resolveChildCwd(input.workflowCwd, input.params.cwd) : input.workflowCwd;
|
|
3331
3323
|
const agentScope = resolveExecutionAgentScope(input.params.agentScope ?? input.workflowAgentScope);
|
|
3332
|
-
const
|
|
3333
|
-
const agent = typeof input.params.agent === "string"
|
|
3324
|
+
const discoveredAgents = input.discoverAgents(childCwd, agentScope).agents;
|
|
3325
|
+
const agent = typeof input.params.agent === "string"
|
|
3326
|
+
? resolveAgentName(input.params.agent, discoveredAgents).agent ?? resolveAgentName(input.params.agent, input.agents).agent
|
|
3327
|
+
: undefined;
|
|
3334
3328
|
const agentOutput = typeof agent?.output === "string" ? agent.output : undefined;
|
|
3335
3329
|
const output = rawOutput === true || rawOutput === "true"
|
|
3336
3330
|
? agentOutput
|
|
3337
3331
|
: hasExplicitOutput
|
|
3338
3332
|
? rawOutput
|
|
3339
|
-
:
|
|
3340
|
-
|
|
3333
|
+
: input.aggregateOutputPath
|
|
3334
|
+
? workflowChildDefaultOutput(input.aggregateOutputPath, input.artifactsDir, input.workflowRunId, input.key)
|
|
3335
|
+
: agentOutput;
|
|
3336
|
+
return {
|
|
3337
|
+
path: resolveSingleOutputPath(output, input.ctxCwd, childCwd, input.configuredOutputBaseDir ?? path.join(input.artifactsDir, "outputs", sanitizeRunPathSegment(input.workflowRunId))),
|
|
3338
|
+
inherited: !hasExplicitOutput && !input.aggregateOutputPath && agentOutput !== undefined,
|
|
3339
|
+
};
|
|
3341
3340
|
}
|
|
3342
3341
|
|
|
3343
3342
|
function workflowChildOutputClaims(input: {
|
|
@@ -3348,20 +3347,37 @@ function workflowChildOutputClaims(input: {
|
|
|
3348
3347
|
aggregateOutputPath?: string;
|
|
3349
3348
|
configuredOutputBaseDir?: string;
|
|
3350
3349
|
discoverAgents: (cwd: string, scope: AgentScope) => { agents: AgentConfig[] };
|
|
3350
|
+
agents: AgentConfig[];
|
|
3351
3351
|
workflowAgentScope?: unknown;
|
|
3352
|
+
state: SubagentState;
|
|
3352
3353
|
claimedOutputPaths: Map<string, string>;
|
|
3353
3354
|
entries: Array<{ key: string; params: Record<string, unknown> }>;
|
|
3354
|
-
}): { error?: string; claims?: Map<string, string> } {
|
|
3355
|
+
}): { error?: string; claims?: Map<string, string>; overrides?: Map<string, string> } {
|
|
3356
|
+
const resolvedEntries = input.entries.map(({ key, params }) => ({
|
|
3357
|
+
key,
|
|
3358
|
+
...resolveWorkflowChildOutputPath({ ...input, key, params }),
|
|
3359
|
+
}));
|
|
3360
|
+
const paths = new Map<string, number>();
|
|
3361
|
+
for (const claimedPath of input.claimedOutputPaths.keys()) paths.set(claimedPath, 1);
|
|
3362
|
+
for (const { path: resolved } of resolvedEntries) {
|
|
3363
|
+
if (resolved) paths.set(resolved, (paths.get(resolved) ?? 0) + 1);
|
|
3364
|
+
}
|
|
3365
|
+
const overrides = new Map<string, string>();
|
|
3366
|
+
for (const entry of resolvedEntries) {
|
|
3367
|
+
if (entry.inherited && entry.path && (paths.get(entry.path) ?? 0) > 1) {
|
|
3368
|
+
const output = workflowChildDefaultOutput(input.aggregateOutputPath, input.artifactsDir, input.workflowRunId, entry.key);
|
|
3369
|
+
overrides.set(entry.key, output);
|
|
3370
|
+
entry.path = output;
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3355
3373
|
const claims = new Map(input.claimedOutputPaths);
|
|
3356
|
-
for (const { key,
|
|
3357
|
-
const resolved = resolveWorkflowChildOutputPath({ ...input, key, params });
|
|
3374
|
+
for (const { key, path: resolved } of resolvedEntries) {
|
|
3358
3375
|
if (!resolved) continue;
|
|
3359
|
-
if (input.aggregateOutputPath && resolved === input.aggregateOutputPath) return { error: `Workflow child '${key}' output resolves to the workflow aggregate output path: ${resolved}. Use a distinct child output path.` };
|
|
3360
3376
|
const previous = claims.get(resolved);
|
|
3361
3377
|
if (previous) return { error: `Workflow children '${previous}' and '${key}' resolve output to the same path: ${resolved}. Use distinct child output paths.` };
|
|
3362
3378
|
claims.set(resolved, key);
|
|
3363
3379
|
}
|
|
3364
|
-
return { claims };
|
|
3380
|
+
return { claims, overrides };
|
|
3365
3381
|
}
|
|
3366
3382
|
|
|
3367
3383
|
function applyWorkflowChildOutputClaims(target: Map<string, string>, claims: Map<string, string>): void {
|
|
@@ -3375,746 +3391,86 @@ function prepareWorkflowChildLaunchParams(input: {
|
|
|
3375
3391
|
parentWorkflowRunId: string;
|
|
3376
3392
|
workflowKey: string;
|
|
3377
3393
|
ctxCwd: string;
|
|
3378
|
-
workflowCwd: string;
|
|
3379
|
-
artifactsDir: string;
|
|
3380
|
-
aggregateOutputPath?: string;
|
|
3381
|
-
configuredOutputBaseDir?: string;
|
|
3382
|
-
discoverAgents: (cwd: string, scope: AgentScope) => { agents: AgentConfig[] };
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
}
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
return undefined;
|
|
3407
|
-
}
|
|
3408
|
-
|
|
3409
|
-
function resolveParallelTaskCwd(
|
|
3410
|
-
task: TaskParam,
|
|
3411
|
-
paramsCwd: string,
|
|
3412
|
-
worktreeSetup: WorktreeSetup | undefined,
|
|
3413
|
-
index: number,
|
|
3414
|
-
): string {
|
|
3415
|
-
if (worktreeSetup) return worktreeSetup.worktrees[index]!.agentCwd;
|
|
3416
|
-
return resolveChildCwd(paramsCwd, task.cwd);
|
|
3417
|
-
}
|
|
3418
|
-
|
|
3419
|
-
function finalizeParallelWorktreeHandoff(input: {
|
|
3420
|
-
worktreeSetup: WorktreeSetup;
|
|
3421
|
-
artifactsDir: string;
|
|
3422
|
-
runId: string;
|
|
3423
|
-
cwd: string;
|
|
3424
|
-
tasks: TaskParam[];
|
|
3425
|
-
results: SingleResult[];
|
|
3426
|
-
}): { suffix: string; reference?: NonNullable<Details["parallelHandoff"]> } {
|
|
3427
|
-
const diffsDir = path.join(input.artifactsDir, "worktree-diffs", input.runId);
|
|
3428
|
-
const diffs = diffWorktrees(input.worktreeSetup, input.tasks.map((task) => task.agent), diffsDir);
|
|
3429
|
-
const diffSummary = formatWorktreeDiffSummary(diffs);
|
|
3430
|
-
const manifestPath = parallelHandoffPath(input.artifactsDir, input.runId);
|
|
3431
|
-
const handoff = {
|
|
3432
|
-
manifestPath,
|
|
3433
|
-
runId: input.runId,
|
|
3434
|
-
mode: "parallel" as const,
|
|
3435
|
-
source: "foreground" as const,
|
|
3436
|
-
cwd: input.cwd,
|
|
3437
|
-
stepIndex: 0,
|
|
3438
|
-
flatStartIndex: 0,
|
|
3439
|
-
setup: input.worktreeSetup,
|
|
3440
|
-
diffs,
|
|
3441
|
-
results: input.results.map((result) => ({
|
|
3442
|
-
agent: result.agent,
|
|
3443
|
-
status: resolveSubagentResultStatus(omitUndefinedProperties({
|
|
3444
|
-
exitCode: result.exitCode,
|
|
3445
|
-
interrupted: result.interrupted,
|
|
3446
|
-
detached: result.detached,
|
|
3447
|
-
state: result.stopped ? "stopped" : undefined,
|
|
3448
|
-
processSignal: result.processSignal,
|
|
3449
|
-
timedOut: result.timedOut,
|
|
3450
|
-
stopped: result.stopped,
|
|
3451
|
-
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
3452
|
-
})),
|
|
3453
|
-
summary: resultSummaryForIntercom(result),
|
|
3454
|
-
...(result.artifactPaths?.outputPath ? { outputPath: result.artifactPaths.outputPath } : {}),
|
|
3455
|
-
...(result.structuredOutput !== undefined ? { structuredOutput: result.structuredOutput } : {}),
|
|
3456
|
-
...(result.structuredOutputPath ? { structuredOutputPath: result.structuredOutputPath } : {}),
|
|
3457
|
-
...(result.sessionFile ? { sessionPath: result.sessionFile } : {}),
|
|
3458
|
-
})),
|
|
3459
|
-
};
|
|
3460
|
-
try {
|
|
3461
|
-
writeParallelHandoffGroup(handoff);
|
|
3462
|
-
const cleanup = cleanupWorktrees(input.worktreeSetup, { kind: "preserve", capturedDiffs: diffs, handoffManifestPath: manifestPath });
|
|
3463
|
-
const reference = writeParallelHandoffGroup({ ...handoff, cleanup });
|
|
3464
|
-
return {
|
|
3465
|
-
suffix: [diffSummary, formatParallelHandoffReference(reference)].filter(Boolean).join("\n\n"),
|
|
3466
|
-
reference,
|
|
3467
|
-
};
|
|
3468
|
-
} catch (error) {
|
|
3469
|
-
return { suffix: [diffSummary, formatParallelHandoffError(error)].filter(Boolean).join("\n\n") };
|
|
3470
|
-
}
|
|
3471
|
-
}
|
|
3472
|
-
|
|
3473
|
-
function findDuplicateParallelOutputPath(input: {
|
|
3474
|
-
tasks: TaskParam[];
|
|
3475
|
-
behaviors: ResolvedStepBehavior[];
|
|
3476
|
-
paramsCwd: string;
|
|
3477
|
-
ctxCwd: string;
|
|
3478
|
-
outputBaseDir: string;
|
|
3479
|
-
worktreeSetup?: WorktreeSetup;
|
|
3480
|
-
}): string | undefined {
|
|
3481
|
-
const seen = new Map<string, { index: number; agent: string }>();
|
|
3482
|
-
for (let index = 0; index < input.tasks.length; index++) {
|
|
3483
|
-
const behavior = input.behaviors[index];
|
|
3484
|
-
if (!behavior?.output) continue;
|
|
3485
|
-
const task = input.tasks[index]!;
|
|
3486
|
-
const taskCwd = resolveParallelTaskCwd(task, input.paramsCwd, input.worktreeSetup, index);
|
|
3487
|
-
const outputPath = resolveSingleOutputPath(behavior.output, input.ctxCwd, taskCwd, input.outputBaseDir);
|
|
3488
|
-
if (!outputPath) continue;
|
|
3489
|
-
const previous = seen.get(outputPath);
|
|
3490
|
-
if (previous) {
|
|
3491
|
-
return `Parallel tasks ${previous.index + 1} (${previous.agent}) and ${index + 1} (${task.agent}) resolve output to the same path: ${outputPath}. Use distinct output paths.`;
|
|
3492
|
-
}
|
|
3493
|
-
seen.set(outputPath, { index, agent: task.agent });
|
|
3494
|
-
}
|
|
3495
|
-
return undefined;
|
|
3496
|
-
}
|
|
3497
|
-
|
|
3498
|
-
async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Promise<SingleResult[]> {
|
|
3499
|
-
// Pre-warm fork session files sequentially before concurrent dispatch to avoid
|
|
3500
|
-
// races where multiple workers simultaneously try to branch the same parent session.
|
|
3501
|
-
for (let i = 0; i < input.tasks.length; i++) {
|
|
3502
|
-
input.sessionFileForTask(input.tasks[i]!.agent, i, input.modelOverrides[i]);
|
|
3503
|
-
}
|
|
3504
|
-
const completedResults: SingleResult[] = [];
|
|
3505
|
-
if (input.foregroundControl) retainForegroundSchedulingOwner(input.foregroundControl);
|
|
3506
|
-
return mapConcurrent(input.tasks, input.concurrencyLimit, async (task, index) => {
|
|
3507
|
-
const budgetState = usageBudgetState(input.usageBudget, sumResultsCost(completedResults));
|
|
3508
|
-
if (budgetState?.exhausted) {
|
|
3509
|
-
return {
|
|
3510
|
-
index,
|
|
3511
|
-
agent: task.agent,
|
|
3512
|
-
task: input.taskTexts[index] ?? "(skipped)",
|
|
3513
|
-
exitCode: 1,
|
|
3514
|
-
messages: [],
|
|
3515
|
-
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 },
|
|
3516
|
-
error: usageBudgetExceededMessage(budgetState),
|
|
3517
|
-
skipped: true,
|
|
3518
|
-
} as SingleResult;
|
|
3519
|
-
}
|
|
3520
|
-
const behavior = input.behaviors[index];
|
|
3521
|
-
const effectiveSkills = behavior?.skills;
|
|
3522
|
-
const taskCwd = resolveParallelTaskCwd(task, input.paramsCwd, input.worktreeSetup, index);
|
|
3523
|
-
const readInstructions = behavior
|
|
3524
|
-
? buildChainInstructions({ ...behavior, output: false, progress: false }, taskCwd, false)
|
|
3525
|
-
: { prefix: "", suffix: "" };
|
|
3526
|
-
const progressInstructions = behavior
|
|
3527
|
-
? buildChainInstructions({ ...behavior, output: false, reads: false }, input.progressDir, index === input.firstProgressIndex)
|
|
3528
|
-
: { prefix: "", suffix: "" };
|
|
3529
|
-
const outputPath = resolveSingleOutputPath(behavior?.output, input.ctx.cwd, taskCwd, input.outputBaseDir);
|
|
3530
|
-
const agentConfig = input.agents.find((agent) => agent.name === task.agent);
|
|
3531
|
-
const taskText = injectSingleOutputInstruction(
|
|
3532
|
-
`${readInstructions.prefix}${input.taskTexts[index]!}${progressInstructions.suffix}`,
|
|
3533
|
-
outputPath,
|
|
3534
|
-
agentConfig,
|
|
3535
|
-
);
|
|
3536
|
-
const interruptController = new AbortController();
|
|
3537
|
-
if (input.foregroundControl) {
|
|
3538
|
-
const model = input.modelOverrides[index];
|
|
3539
|
-
const thinking = resolveEffectiveThinking(model, input.thinkingOverrideForTask(task.agent, index, model));
|
|
3540
|
-
beginForegroundChild(input.foregroundControl, {
|
|
3541
|
-
index,
|
|
3542
|
-
agent: task.agent,
|
|
3543
|
-
authoredTask: input.taskDescriptions[index] ?? "",
|
|
3544
|
-
effectivePrompt: taskText,
|
|
3545
|
-
cwd: taskCwd,
|
|
3546
|
-
...(outputPath ? { outputPath } : {}),
|
|
3547
|
-
rerun: { params: compactOptional<Record<string, unknown>>({
|
|
3548
|
-
agent: task.agent,
|
|
3549
|
-
task: input.taskDescriptions[index] ?? "",
|
|
3550
|
-
async: false,
|
|
3551
|
-
cwd: input.worktreeSetup ? undefined : taskCwd,
|
|
3552
|
-
worktree: input.worktreeSetup ? true : false,
|
|
3553
|
-
context: input.contextPolicy.contextForAgent(task.agent),
|
|
3554
|
-
share: input.shareEnabled || undefined,
|
|
3555
|
-
maxOutput: input.maxOutput,
|
|
3556
|
-
timeoutMs: input.timeoutMs,
|
|
3557
|
-
toolTimeoutMs: input.toolTimeoutMs,
|
|
3558
|
-
configToolTimeoutMs: input.configToolTimeoutMs,
|
|
3559
|
-
turnBudget: input.turnBudget,
|
|
3560
|
-
...(model ? { model } : {}),
|
|
3561
|
-
...(effectiveSkills !== undefined ? { skill: effectiveSkills } : {}),
|
|
3562
|
-
...(outputPath ? { output: outputPath } : {}),
|
|
3563
|
-
...(behavior?.outputMode !== undefined ? { outputMode: behavior.outputMode } : {}),
|
|
3564
|
-
...(behavior?.reads !== undefined ? { reads: behavior.reads } : {}),
|
|
3565
|
-
...(behavior?.progress !== undefined ? { progress: behavior.progress } : {}),
|
|
3566
|
-
...(task.toolBudget !== undefined ? { toolBudget: task.toolBudget } : {}),
|
|
3567
|
-
...(task.outputSchema !== undefined ? { outputSchema: task.outputSchema } : {}),
|
|
3568
|
-
...(task.acceptance !== undefined ? { acceptance: task.acceptance } : {}),
|
|
3569
|
-
...(task.agentContract !== undefined || input.agentContract !== undefined ? { agentContract: task.agentContract ?? input.agentContract } : {}),
|
|
3570
|
-
}) },
|
|
3571
|
-
description: `${task.agent} child`,
|
|
3572
|
-
...(model ? { model } : {}),
|
|
3573
|
-
...(thinking ? { thinking } : {}),
|
|
3574
|
-
interrupt: () => {
|
|
3575
|
-
if (interruptController.signal.aborted) return false;
|
|
3576
|
-
interruptController.abort();
|
|
3577
|
-
return true;
|
|
3578
|
-
},
|
|
3579
|
-
});
|
|
3580
|
-
}
|
|
3581
|
-
const structuredRuntime = task.outputSchema
|
|
3582
|
-
? createStructuredOutputRuntime(task.outputSchema, path.join(input.artifactsDir, "structured-output", input.runId))
|
|
3583
|
-
: undefined;
|
|
3584
|
-
let detachedReceipt = false;
|
|
3585
|
-
const result = await runSync(input.ctx.cwd, input.agents, task.agent, taskText, compactOptional<Parameters<typeof runSync>[4]>({
|
|
3586
|
-
permissions: input.permissions,
|
|
3587
|
-
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
3588
|
-
llmIntentArbiter: createTaskMutationArbiter(input.ctx),
|
|
3589
|
-
...workflowForegroundSteeringLaunchOptions(input.foregroundControl, index),
|
|
3590
|
-
context: input.contextPolicy.contextForAgent(task.agent),
|
|
3591
|
-
runFanoutBudget: { ...input.runFanoutBudget, parentPath: `${input.runFanoutBudget.parentPath ? `${input.runFanoutBudget.parentPath}/` : ""}tasks[${index}]` },
|
|
3592
|
-
cwd: taskCwd,
|
|
3593
|
-
signal: input.signal,
|
|
3594
|
-
interruptSignal: interruptController.signal,
|
|
3595
|
-
allowIntercomDetach: agentConfig?.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
|
|
3596
|
-
intercomEvents: input.intercomEvents,
|
|
3597
|
-
runId: input.runId,
|
|
3598
|
-
index,
|
|
3599
|
-
sessionDir: input.sessionDirForIndex(index),
|
|
3600
|
-
sessionFile: input.sessionFileForTask(task.agent, index, input.modelOverrides[index]),
|
|
3601
|
-
share: input.shareEnabled,
|
|
3602
|
-
artifactsDir: input.artifactConfig.enabled ? input.artifactsDir : undefined,
|
|
3603
|
-
artifactConfig: input.artifactConfig,
|
|
3604
|
-
maxOutput: input.maxOutput,
|
|
3605
|
-
outputPath,
|
|
3606
|
-
outputMode: behavior?.outputMode,
|
|
3607
|
-
maxSubagentDepth: input.maxSubagentDepths[index],
|
|
3608
|
-
waitToolEnabled: input.waitToolEnabled,
|
|
3609
|
-
capabilityCeiling: input.capabilityCeiling,
|
|
3610
|
-
controlConfig: input.controlConfig,
|
|
3611
|
-
onControlEvent: input.onControlEvent,
|
|
3612
|
-
onDetachedExit: (result) => {
|
|
3613
|
-
try {
|
|
3614
|
-
updateRememberedForegroundChild(input.state, { runId: input.runId, mode: "parallel", cwd: taskCwd, sessionId: input.parentSessionId, index, result, events: input.intercomEvents });
|
|
3615
|
-
} finally {
|
|
3616
|
-
try {
|
|
3617
|
-
if (input.foregroundControl) finishForegroundChild(input.foregroundControl, index);
|
|
3618
|
-
} finally {
|
|
3619
|
-
removeForegroundControlIfIdle(input.state, input.runId);
|
|
3620
|
-
}
|
|
3621
|
-
}
|
|
3622
|
-
},
|
|
3623
|
-
intercomSessionName: input.childIntercomTarget?.(task.agent, index),
|
|
3624
|
-
orchestratorIntercomTarget: input.orchestratorIntercomTarget,
|
|
3625
|
-
nestedRoute: input.foregroundControl?.nestedRoute,
|
|
3626
|
-
modelOverride: input.modelOverrides[index],
|
|
3627
|
-
thinkingOverride: input.thinkingOverrideForTask(task.agent, index, input.modelOverrides[index]),
|
|
3628
|
-
availableModels: input.availableModels,
|
|
3629
|
-
preferredModelProvider: input.parentModel?.provider,
|
|
3630
|
-
modelScope: input.modelScope,
|
|
3631
|
-
skills: effectiveSkills === false ? [] : effectiveSkills,
|
|
3632
|
-
structuredOutput: structuredRuntime,
|
|
3633
|
-
agentContract: task.agentContract ?? input.agentContract,
|
|
3634
|
-
acceptance: task.acceptance,
|
|
3635
|
-
acceptanceContext: { mode: "parallel" },
|
|
3636
|
-
onEffectivePrompt: input.foregroundControl ? (prompt) => updateLiveEffectivePrompt(input.foregroundControl!, index, prompt) : undefined,
|
|
3637
|
-
timeoutMs: input.timeoutMs,
|
|
3638
|
-
toolTimeoutMs: input.toolTimeoutMs,
|
|
3639
|
-
configToolTimeoutMs: input.configToolTimeoutMs,
|
|
3640
|
-
deadlineAt: input.deadlineAt,
|
|
3641
|
-
turnBudget: input.turnBudget,
|
|
3642
|
-
toolBudget: input.toolBudgets[index],
|
|
3643
|
-
onUpdate: input.onUpdate
|
|
3644
|
-
? (progressUpdate) => {
|
|
3645
|
-
const stepResults = progressUpdate.details?.results || [];
|
|
3646
|
-
const stepProgress = progressUpdate.details?.progress || [];
|
|
3647
|
-
if (input.foregroundControl && stepProgress.length > 0) {
|
|
3648
|
-
updateForegroundChild(input.foregroundControl, index, stepProgress[0]);
|
|
3649
|
-
}
|
|
3650
|
-
if (stepResults.length > 0) input.liveResults[index] = stepResults[0];
|
|
3651
|
-
if (stepProgress.length > 0) input.liveProgress[index] = stepProgress[0];
|
|
3652
|
-
const mergedResults = input.liveResults.filter((result): result is SingleResult => result !== undefined);
|
|
3653
|
-
const mergedProgress = input.liveProgress.filter((progress): progress is AgentProgress => progress !== undefined);
|
|
3654
|
-
input.onUpdate?.({
|
|
3655
|
-
content: progressUpdate.content,
|
|
3656
|
-
details: compactOptional<Details>({
|
|
3657
|
-
mode: "parallel",
|
|
3658
|
-
results: mergedResults,
|
|
3659
|
-
progress: mergedProgress,
|
|
3660
|
-
controlEvents: progressUpdate.details?.controlEvents,
|
|
3661
|
-
totalSteps: input.tasks.length,
|
|
3662
|
-
}),
|
|
3663
|
-
});
|
|
3664
|
-
}
|
|
3665
|
-
: undefined,
|
|
3666
|
-
})).then((result) => {
|
|
3667
|
-
detachedReceipt = result.detached === true;
|
|
3668
|
-
return result;
|
|
3669
|
-
}).finally(() => {
|
|
3670
|
-
// mapConcurrent rejects before siblings settle, so every attached child
|
|
3671
|
-
// attempts idle removal after releasing its own control. Detached receipts
|
|
3672
|
-
// transfer both responsibilities to the authoritative exit callback.
|
|
3673
|
-
if (!detachedReceipt) {
|
|
3674
|
-
if (input.foregroundControl) finishForegroundChild(input.foregroundControl, index);
|
|
3675
|
-
removeForegroundControlIfIdle(input.state, input.runId);
|
|
3676
|
-
}
|
|
3677
|
-
});
|
|
3678
|
-
completedResults.push(result);
|
|
3679
|
-
return result;
|
|
3680
|
-
}, input.globalSemaphore, () => {
|
|
3681
|
-
if (input.foregroundControl) settleForegroundSchedulingOwner(input.foregroundControl);
|
|
3682
|
-
removeForegroundControlIfIdle(input.state, input.runId);
|
|
3683
|
-
});
|
|
3684
|
-
}
|
|
3685
|
-
|
|
3686
|
-
async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps): Promise<AgentToolResult<Details>> {
|
|
3687
|
-
const {
|
|
3688
|
-
params,
|
|
3689
|
-
effectiveCwd,
|
|
3690
|
-
agents,
|
|
3691
|
-
ctx,
|
|
3692
|
-
signal,
|
|
3693
|
-
runId,
|
|
3694
|
-
sessionDirForIndex,
|
|
3695
|
-
sessionFileForIndex,
|
|
3696
|
-
sessionFileForTask,
|
|
3697
|
-
thinkingOverrideForTask,
|
|
3698
|
-
shareEnabled,
|
|
3699
|
-
artifactConfig,
|
|
3700
|
-
artifactsDir,
|
|
3701
|
-
backgroundRequestedWhileClarifying,
|
|
3702
|
-
onUpdate,
|
|
3703
|
-
sessionRoot,
|
|
3704
|
-
controlConfig,
|
|
3705
|
-
contextPolicy,
|
|
3706
|
-
} = data;
|
|
3707
|
-
const onControlEvent = createForegroundControlNotifier(data, deps);
|
|
3708
|
-
const childIntercomTarget = data.intercomBridge.active ? resolveSubagentIntercomTarget : undefined;
|
|
3709
|
-
const allProgress: AgentProgress[] = [];
|
|
3710
|
-
const allArtifactPaths: ArtifactPaths[] = [];
|
|
3711
|
-
const tasks = params.tasks!;
|
|
3712
|
-
const maxParallelTasks = resolveTopLevelParallelMaxTasks(deps.config.parallel?.maxTasks);
|
|
3713
|
-
const parallelConcurrency = resolveTopLevelParallelConcurrency(params.concurrency, deps.config.parallel?.concurrency);
|
|
3714
|
-
|
|
3715
|
-
if (tasks.length > maxParallelTasks)
|
|
3716
|
-
return {
|
|
3717
|
-
content: [{ type: "text", text: `Max ${maxParallelTasks} tasks` }],
|
|
3718
|
-
isError: true,
|
|
3719
|
-
details: { mode: "parallel" as const, results: [] },
|
|
3720
|
-
};
|
|
3721
|
-
|
|
3722
|
-
const agentConfigs: AgentConfig[] = [];
|
|
3723
|
-
for (const t of tasks) {
|
|
3724
|
-
const config = agents.find((a) => a.name === t.agent);
|
|
3725
|
-
if (!config) {
|
|
3726
|
-
return {
|
|
3727
|
-
content: [{ type: "text", text: `Unknown agent: ${t.agent}` }],
|
|
3728
|
-
isError: true,
|
|
3729
|
-
details: { mode: "parallel" as const, results: [] },
|
|
3730
|
-
};
|
|
3731
|
-
}
|
|
3732
|
-
agentConfigs.push(config);
|
|
3733
|
-
}
|
|
3734
|
-
|
|
3735
|
-
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
3736
|
-
const maxSubagentDepths = agentConfigs.map((config) =>
|
|
3737
|
-
resolveChildMaxSubagentDepth(currentMaxSubagentDepth, config.maxSubagentDepth),
|
|
3738
|
-
);
|
|
3739
|
-
const toolBudgets: (ResolvedToolBudget | undefined)[] = [];
|
|
3740
|
-
for (let index = 0; index < tasks.length; index++) {
|
|
3741
|
-
const resolved = resolveEffectiveToolBudget(omitUndefinedProperties({ stepBudget: tasks[index]?.toolBudget, runBudget: data.toolBudget, agentBudget: agentConfigs[index]?.toolBudget, configBudget: data.configToolBudget }));
|
|
3742
|
-
if (resolved.error) return buildParallelModeError(resolved.error);
|
|
3743
|
-
toolBudgets.push(resolved.toolBudget);
|
|
3744
|
-
}
|
|
3745
|
-
|
|
3746
|
-
if (params.worktree) {
|
|
3747
|
-
const worktreeTaskCwdError = buildParallelWorktreeTaskCwdError(tasks, effectiveCwd);
|
|
3748
|
-
if (worktreeTaskCwdError) return buildParallelModeError(worktreeTaskCwdError);
|
|
3749
|
-
}
|
|
3750
|
-
|
|
3751
|
-
const parentModel = data.parentModel;
|
|
3752
|
-
const currentProvider = parentModel?.provider;
|
|
3753
|
-
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
3754
|
-
let taskTexts = tasks.map((t) => t.task);
|
|
3755
|
-
const skillOverrides: (string[] | false | undefined)[] = tasks.map((t) =>
|
|
3756
|
-
normalizeSkillInput(t.skill),
|
|
3757
|
-
);
|
|
3758
|
-
const behaviorOverrides: StepOverrides[] = tasks.map((task, index) => ({
|
|
3759
|
-
...(task.output !== undefined && task.output !== true ? { output: task.output } : {}),
|
|
3760
|
-
...(task.outputMode !== undefined ? { outputMode: task.outputMode } : {}),
|
|
3761
|
-
...(task.reads !== undefined && task.reads !== true ? { reads: task.reads } : {}),
|
|
3762
|
-
...(task.progress !== undefined ? { progress: task.progress } : {}),
|
|
3763
|
-
...(skillOverrides[index] !== undefined ? { skills: skillOverrides[index] } : {}),
|
|
3764
|
-
...(task.model !== undefined ? { model: task.model } : {}),
|
|
3765
|
-
}));
|
|
3766
|
-
const modelOverrides: (string | undefined)[] = tasks.map((_, i) =>
|
|
3767
|
-
agentConfigs[i]?.runner?.type === "external-cli"
|
|
3768
|
-
? undefined
|
|
3769
|
-
: resolveEffectiveSubagentModel(behaviorOverrides[i]?.model, agentConfigs[i]?.model, parentModel, availableModels, currentProvider, data.modelScope === undefined ? {} : { scope: data.modelScope }),
|
|
3770
|
-
);
|
|
3771
|
-
|
|
3772
|
-
if (params.clarify === true && ctx.hasUI) {
|
|
3773
|
-
const behaviors = agentConfigs.map((c, i) =>
|
|
3774
|
-
resolveStepBehavior(c, behaviorOverrides[i]!),
|
|
3775
|
-
);
|
|
3776
|
-
const availableSkills = discoverAvailableSkills(effectiveCwd);
|
|
3777
|
-
|
|
3778
|
-
const result = await ctx.ui.custom<ChainClarifyResult>(
|
|
3779
|
-
(tui, theme, _kb, done) =>
|
|
3780
|
-
new ChainClarifyComponent(
|
|
3781
|
-
tui, theme,
|
|
3782
|
-
agentConfigs,
|
|
3783
|
-
taskTexts,
|
|
3784
|
-
"",
|
|
3785
|
-
undefined,
|
|
3786
|
-
behaviors,
|
|
3787
|
-
availableModels,
|
|
3788
|
-
currentProvider,
|
|
3789
|
-
availableSkills,
|
|
3790
|
-
done,
|
|
3791
|
-
"parallel",
|
|
3792
|
-
),
|
|
3793
|
-
{ overlay: true, overlayOptions: { anchor: "center", width: 84, maxHeight: "80%" } },
|
|
3794
|
-
);
|
|
3795
|
-
|
|
3796
|
-
if (!result || !result.confirmed) {
|
|
3797
|
-
return { content: [{ type: "text", text: "Cancelled" }], details: { mode: "parallel", results: [] } };
|
|
3798
|
-
}
|
|
3799
|
-
|
|
3800
|
-
taskTexts = result.templates;
|
|
3801
|
-
for (let i = 0; i < result.behaviorOverrides.length; i++) {
|
|
3802
|
-
const override = result.behaviorOverrides[i];
|
|
3803
|
-
if (override?.model !== undefined) {
|
|
3804
|
-
modelOverrides[i] = agentConfigs[i]?.runner?.type === "external-cli"
|
|
3805
|
-
? undefined
|
|
3806
|
-
: resolveEffectiveSubagentModel(override.model, agentConfigs[i]?.model, parentModel, availableModels, currentProvider, data.modelScope === undefined ? {} : { scope: data.modelScope });
|
|
3807
|
-
behaviorOverrides[i]!.model = override.model;
|
|
3808
|
-
}
|
|
3809
|
-
if (override?.output !== undefined) behaviorOverrides[i]!.output = override.output;
|
|
3810
|
-
if (override?.reads !== undefined) behaviorOverrides[i]!.reads = override.reads;
|
|
3811
|
-
if (override?.progress !== undefined) behaviorOverrides[i]!.progress = override.progress;
|
|
3812
|
-
if (override?.skills !== undefined) {
|
|
3813
|
-
skillOverrides[i] = override.skills;
|
|
3814
|
-
behaviorOverrides[i]!.skills = override.skills;
|
|
3815
|
-
}
|
|
3816
|
-
}
|
|
3817
|
-
|
|
3818
|
-
if (result.runInBackground) {
|
|
3819
|
-
if (!isAsyncAvailable()) {
|
|
3820
|
-
return {
|
|
3821
|
-
content: [{ type: "text", text: "Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the pi-subagents package dependencies are installed." }],
|
|
3822
|
-
isError: true,
|
|
3823
|
-
details: { mode: "parallel" as const, results: [] },
|
|
3824
|
-
};
|
|
3825
|
-
}
|
|
3826
|
-
const id = data.asyncRunId;
|
|
3827
|
-
const capacity = acquireClarifiedBackgroundCapacity(data, deps, "parallel", id);
|
|
3828
|
-
if (capacity.errorResult) return capacity.errorResult;
|
|
3829
|
-
const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
|
|
3830
|
-
pi: deps.pi,
|
|
3831
|
-
cwd: ctx.cwd,
|
|
3832
|
-
currentSessionId: data.parentSessionId!,
|
|
3833
|
-
parentSessionId: data.parentPiSessionId,
|
|
3834
|
-
currentModelProvider: parentModel?.provider,
|
|
3835
|
-
currentModel: parentModel,
|
|
3836
|
-
modelScope: data.modelScope,
|
|
3837
|
-
interactive: ctx.hasUI,
|
|
3838
|
-
permissions: deps.config.permissions,
|
|
3839
|
-
});
|
|
3840
|
-
const parallelTasks = tasks.map((t, i) => {
|
|
3841
|
-
const taskText = shouldForkAgent(contextPolicy, t.agent) ? wrapForkTask(taskTexts[i]!) : taskTexts[i]!;
|
|
3842
|
-
const progress = taskDisallowsFileUpdates(taskText) ? false : behaviorOverrides[i]?.progress;
|
|
3843
|
-
return compactOptional<ParallelTaskItem>({
|
|
3844
|
-
agent: t.agent,
|
|
3845
|
-
task: taskText,
|
|
3846
|
-
cwd: t.cwd,
|
|
3847
|
-
...(behaviorOverrides[i]?.model !== undefined ? { model: behaviorOverrides[i]!.model } : {}),
|
|
3848
|
-
...(skillOverrides[i] !== undefined ? { skill: skillOverrides[i] } : {}),
|
|
3849
|
-
...(behaviorOverrides[i]?.output !== undefined ? { output: behaviorOverrides[i]!.output } : {}),
|
|
3850
|
-
...(behaviorOverrides[i]?.outputMode !== undefined ? { outputMode: behaviorOverrides[i]!.outputMode } : {}),
|
|
3851
|
-
...(behaviorOverrides[i]?.reads !== undefined ? { reads: behaviorOverrides[i]!.reads } : {}),
|
|
3852
|
-
...(progress !== undefined ? { progress } : {}),
|
|
3853
|
-
...(t.toolBudget !== undefined ? { toolBudget: t.toolBudget } : {}),
|
|
3854
|
-
...(t.outputSchema !== undefined ? { outputSchema: t.outputSchema } : {}),
|
|
3855
|
-
...(t.acceptance !== undefined ? { acceptance: t.acceptance } : {}),
|
|
3856
|
-
...(t.agentContract !== undefined ? { agentContract: t.agentContract } : {}),
|
|
3857
|
-
});
|
|
3858
|
-
});
|
|
3859
|
-
try {
|
|
3860
|
-
return executeAsyncChain(id, compactOptional<Parameters<typeof executeAsyncChain>[1]>({
|
|
3861
|
-
chain: [compactOptional<ParallelStep>({ parallel: parallelTasks, concurrency: parallelConcurrency, worktree: params.worktree })],
|
|
3862
|
-
resultMode: "parallel",
|
|
3863
|
-
goal: taskTexts[0] ?? "",
|
|
3864
|
-
agents,
|
|
3865
|
-
ctx: asyncCtx,
|
|
3866
|
-
availableModels,
|
|
3867
|
-
cwd: effectiveCwd,
|
|
3868
|
-
maxOutput: params.maxOutput,
|
|
3869
|
-
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
3870
|
-
artifactConfig,
|
|
3871
|
-
shareEnabled,
|
|
3872
|
-
sessionRoot,
|
|
3873
|
-
chainSkills: [],
|
|
3874
|
-
activeAsyncCapacity: capacity.activeAsyncCapacity,
|
|
3875
|
-
sessionFilesByFlatIndex: tasks.map((task, index) => sessionFileForTask(task.agent, index, modelOverrides[index])),
|
|
3876
|
-
thinkingOverridesByFlatIndex: tasks.map((task, index) => thinkingOverrideForTask(task.agent, index, modelOverrides[index])),
|
|
3877
|
-
contextForAgent: contextPolicy.contextForAgent,
|
|
3878
|
-
maxSubagentDepth: currentMaxSubagentDepth,
|
|
3879
|
-
waitToolEnabled: deps.waitToolEnabled,
|
|
3880
|
-
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
3881
|
-
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
3882
|
-
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
3883
|
-
controlConfig,
|
|
3884
|
-
agentContract: params.agentContract,
|
|
3885
|
-
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
3886
|
-
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
3887
|
-
nestedRoute: data.nestedRoute,
|
|
3888
|
-
timeoutMs: data.timeoutMs,
|
|
3889
|
-
turnBudget: data.turnBudget,
|
|
3890
|
-
usageBudget: data.usageBudget,
|
|
3891
|
-
toolBudget: data.toolBudget,
|
|
3892
|
-
configToolBudget: data.configToolBudget,
|
|
3893
|
-
callToolTimeoutMs: data.params?.toolTimeoutMs,
|
|
3894
|
-
configToolTimeoutMs: data.configToolTimeoutMs,
|
|
3895
|
-
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
3896
|
-
runFanoutBudget: data.runFanoutBudget,
|
|
3897
|
-
}));
|
|
3898
|
-
} finally {
|
|
3899
|
-
finalizeClarifiedBackgroundCapacity(data, deps, capacity.activeAsyncCapacity);
|
|
3900
|
-
}
|
|
3901
|
-
}
|
|
3902
|
-
}
|
|
3903
|
-
|
|
3904
|
-
const behaviors = tasks.map((task, index) => {
|
|
3905
|
-
let behavior = suppressProgressForReadOnlyTask(resolveStepBehavior(agentConfigs[index]!, behaviorOverrides[index]!), taskTexts[index]);
|
|
3906
|
-
if (behaviorOverrides[index]?.output === undefined && typeof behavior.output === "string" && !path.isAbsolute(behavior.output)) {
|
|
3907
|
-
behavior = { ...behavior, output: path.join("parallel-0", `${index}-${task.agent}`, behavior.output) };
|
|
3908
|
-
}
|
|
3909
|
-
return behavior;
|
|
3910
|
-
});
|
|
3911
|
-
const firstProgressIndex = behaviors.findIndex((behavior) => behavior.progress);
|
|
3912
|
-
const liveResults: (SingleResult | undefined)[] = new Array(tasks.length).fill(undefined);
|
|
3913
|
-
const liveProgress: (AgentProgress | undefined)[] = new Array(tasks.length).fill(undefined);
|
|
3914
|
-
const foregroundControl = deps.state.foregroundControls.get(runId);
|
|
3915
|
-
const { setup: worktreeSetup, errorResult } = createParallelWorktreeSetup(
|
|
3916
|
-
params.worktree,
|
|
3917
|
-
effectiveCwd,
|
|
3918
|
-
runId,
|
|
3919
|
-
tasks,
|
|
3920
|
-
deps.config.worktreeSetupHook,
|
|
3921
|
-
deps.config.worktreeSetupHookTimeoutMs,
|
|
3922
|
-
deps.config.worktreeBaseDir,
|
|
3923
|
-
);
|
|
3924
|
-
if (errorResult) return errorResult;
|
|
3925
|
-
|
|
3926
|
-
let worktreeCleanupHandled = false;
|
|
3927
|
-
let pendingHandoff: Details["parallelHandoff"];
|
|
3928
|
-
try {
|
|
3929
|
-
if (worktreeSetup) {
|
|
3930
|
-
pendingHandoff = writePendingParallelHandoff({
|
|
3931
|
-
manifestPath: parallelHandoffPath(artifactsDir, runId),
|
|
3932
|
-
runId,
|
|
3933
|
-
mode: "parallel",
|
|
3934
|
-
source: "foreground",
|
|
3935
|
-
cwd: effectiveCwd,
|
|
3936
|
-
stepIndex: 0,
|
|
3937
|
-
flatStartIndex: 0,
|
|
3938
|
-
setup: worktreeSetup,
|
|
3939
|
-
});
|
|
3940
|
-
}
|
|
3941
|
-
const outputBaseDir = path.join(artifactsDir, "outputs", runId);
|
|
3942
|
-
const duplicateOutputError = findDuplicateParallelOutputPath(omitUndefinedProperties({
|
|
3943
|
-
tasks,
|
|
3944
|
-
behaviors,
|
|
3945
|
-
paramsCwd: effectiveCwd,
|
|
3946
|
-
ctxCwd: ctx.cwd,
|
|
3947
|
-
outputBaseDir,
|
|
3948
|
-
worktreeSetup,
|
|
3949
|
-
}));
|
|
3950
|
-
if (duplicateOutputError) return buildParallelModeError(duplicateOutputError);
|
|
3951
|
-
for (let index = 0; index < tasks.length; index++) {
|
|
3952
|
-
const taskCwd = resolveParallelTaskCwd(tasks[index]!, effectiveCwd, worktreeSetup, index);
|
|
3953
|
-
const outputPath = resolveSingleOutputPath(behaviors[index]?.output, ctx.cwd, taskCwd, outputBaseDir);
|
|
3954
|
-
const validationError = validateFileOnlyOutputMode(behaviors[index]?.outputMode, outputPath, `Parallel task ${index + 1} (${tasks[index]!.agent})`);
|
|
3955
|
-
if (validationError) return buildParallelModeError(validationError);
|
|
3956
|
-
}
|
|
3957
|
-
|
|
3958
|
-
const parallelProgressPrecreated = firstProgressIndex !== -1;
|
|
3959
|
-
const parallelProgressDir = path.join(artifactsDir, "progress", runId);
|
|
3960
|
-
if (parallelProgressPrecreated) writeInitialProgressFile(parallelProgressDir);
|
|
3961
|
-
|
|
3962
|
-
const taskDescriptions = taskTexts.map((taskText) => taskText.trim());
|
|
3963
|
-
for (let i = 0; i < taskTexts.length; i++) {
|
|
3964
|
-
if (shouldForkAgent(contextPolicy, tasks[i]!.agent)) taskTexts[i] = wrapForkTask(taskTexts[i]!);
|
|
3965
|
-
}
|
|
3966
|
-
|
|
3967
|
-
const deadlineAt = data.deadlineAt ?? (data.timeoutMs !== undefined ? Date.now() + data.timeoutMs : undefined);
|
|
3968
|
-
const results = await runForegroundParallelTasks(compactOptional<ForegroundParallelRunInput>({
|
|
3969
|
-
tasks,
|
|
3970
|
-
permissions: deps.config.permissions,
|
|
3971
|
-
taskTexts,
|
|
3972
|
-
taskDescriptions,
|
|
3973
|
-
agents,
|
|
3974
|
-
ctx,
|
|
3975
|
-
state: deps.state,
|
|
3976
|
-
intercomEvents: deps.pi.events,
|
|
3977
|
-
parentSessionId: data.parentSessionId,
|
|
3978
|
-
capabilityCeiling: data.capabilityCeiling,
|
|
3979
|
-
runFanoutBudget: data.runFanoutBudget,
|
|
3980
|
-
signal,
|
|
3981
|
-
runId,
|
|
3982
|
-
sessionDirForIndex,
|
|
3983
|
-
sessionFileForIndex,
|
|
3984
|
-
sessionFileForTask,
|
|
3985
|
-
thinkingOverrideForTask,
|
|
3986
|
-
shareEnabled,
|
|
3987
|
-
artifactConfig,
|
|
3988
|
-
artifactsDir,
|
|
3989
|
-
outputBaseDir,
|
|
3990
|
-
maxOutput: params.maxOutput,
|
|
3991
|
-
paramsCwd: effectiveCwd,
|
|
3992
|
-
progressDir: parallelProgressDir,
|
|
3993
|
-
availableModels,
|
|
3994
|
-
modelScope: data.modelScope,
|
|
3995
|
-
parentModel,
|
|
3996
|
-
modelOverrides,
|
|
3997
|
-
behaviors,
|
|
3998
|
-
firstProgressIndex: parallelProgressPrecreated ? -1 : firstProgressIndex,
|
|
3999
|
-
controlConfig,
|
|
4000
|
-
contextPolicy,
|
|
4001
|
-
onControlEvent,
|
|
4002
|
-
childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(runId, agent, index) : undefined,
|
|
4003
|
-
orchestratorIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
4004
|
-
foregroundControl,
|
|
4005
|
-
concurrencyLimit: parallelConcurrency,
|
|
4006
|
-
globalSemaphore: new Semaphore(deps.config.globalConcurrencyLimit ?? DEFAULT_GLOBAL_CONCURRENCY_LIMIT),
|
|
4007
|
-
maxSubagentDepths,
|
|
4008
|
-
waitToolEnabled: deps.waitToolEnabled,
|
|
4009
|
-
liveResults,
|
|
4010
|
-
liveProgress,
|
|
4011
|
-
onUpdate,
|
|
4012
|
-
worktreeSetup,
|
|
4013
|
-
timeoutMs: data.timeoutMs,
|
|
4014
|
-
deadlineAt,
|
|
4015
|
-
toolTimeoutMs: data.params?.toolTimeoutMs,
|
|
4016
|
-
configToolTimeoutMs: data.configToolTimeoutMs,
|
|
4017
|
-
turnBudget: data.turnBudget,
|
|
4018
|
-
usageBudget: data.usageBudget,
|
|
4019
|
-
toolBudgets,
|
|
4020
|
-
agentContract: params.agentContract,
|
|
4021
|
-
}));
|
|
4022
|
-
for (let i = 0; i < results.length; i++) {
|
|
4023
|
-
const run = results[i]!;
|
|
4024
|
-
recordRun(run.agent, taskTexts[i]!, run.exitCode, run.progressSummary?.durationMs ?? 0);
|
|
4025
|
-
}
|
|
4026
|
-
|
|
4027
|
-
for (const result of results) {
|
|
4028
|
-
if (result.progress) allProgress.push(result.progress);
|
|
4029
|
-
if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
|
|
4030
|
-
}
|
|
4031
|
-
|
|
4032
|
-
if (foregroundControl) {
|
|
4033
|
-
updateForegroundNestedProjection(foregroundControl);
|
|
4034
|
-
attachRootChildrenToSteps(runId, results, foregroundControl.nestedChildren);
|
|
4035
|
-
}
|
|
4036
|
-
const detached = results.find((result) => result.detached);
|
|
4037
|
-
let handoff: ReturnType<typeof finalizeParallelWorktreeHandoff> | undefined;
|
|
4038
|
-
if (worktreeSetup) {
|
|
4039
|
-
worktreeCleanupHandled = true;
|
|
4040
|
-
handoff = detached
|
|
4041
|
-
? { suffix: pendingHandoff ? formatParallelHandoffReference(pendingHandoff) : "", reference: pendingHandoff }
|
|
4042
|
-
: finalizeParallelWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: effectiveCwd, tasks, results });
|
|
4043
|
-
}
|
|
4044
|
-
const interrupted = results.find((result) => result.interrupted);
|
|
4045
|
-
const totalCost = sumResultsCost(results);
|
|
4046
|
-
const details = compactForegroundDetails(compactOptional<Details>({
|
|
4047
|
-
mode: "parallel",
|
|
4048
|
-
runId,
|
|
4049
|
-
timeoutMs: data.timeoutMs,
|
|
4050
|
-
results,
|
|
4051
|
-
progress: params.includeProgress ? allProgress : undefined,
|
|
4052
|
-
artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
|
|
4053
|
-
totalChildUsage: sumResultsUsage(results),
|
|
4054
|
-
totalCost,
|
|
4055
|
-
usageBudget: usageBudgetState(data.usageBudget, totalCost),
|
|
4056
|
-
...(handoff?.reference ? { parallelHandoff: handoff.reference } : {}),
|
|
4057
|
-
}));
|
|
4058
|
-
rememberForegroundRun(deps.state, { runId, mode: "parallel", cwd: effectiveCwd, sessionId: data.parentSessionId, results: details.results });
|
|
4059
|
-
if (interrupted) {
|
|
4060
|
-
return {
|
|
4061
|
-
content: [{ type: "text", text: `Parallel run paused after interrupt (${interrupted.agent}). Waiting for explicit next action.` }],
|
|
4062
|
-
details,
|
|
4063
|
-
};
|
|
4064
|
-
}
|
|
4065
|
-
if (detached) {
|
|
4066
|
-
const handoffSuffix = handoff?.suffix ? `\n\n${handoff.suffix}` : "";
|
|
4067
|
-
return {
|
|
4068
|
-
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}` }],
|
|
4069
|
-
details,
|
|
4070
|
-
};
|
|
4071
|
-
}
|
|
4072
|
-
|
|
4073
|
-
const suppressRoutineResultIntercom = shouldSuppressRoutineResultIntercom({ suppressRoutineResultIntercom: params.suppressRoutineResultIntercom, results });
|
|
4074
|
-
if (foregroundControl) updateForegroundNestedProjection(foregroundControl);
|
|
4075
|
-
if (!suppressRoutineResultIntercom) {
|
|
4076
|
-
const intercomReceipt = await maybeBuildForegroundIntercomReceipt({
|
|
4077
|
-
pi: deps.pi,
|
|
4078
|
-
intercomBridge: data.intercomBridge,
|
|
4079
|
-
runId,
|
|
4080
|
-
mode: "parallel",
|
|
4081
|
-
details,
|
|
4082
|
-
...(params.workflowParentRunId !== undefined ? { preserveDetailsOutputs: true } : {}),
|
|
4083
|
-
...(foregroundControl?.nestedChildren?.length ? { nestedChildren: foregroundControl.nestedChildren } : {}),
|
|
4084
|
-
});
|
|
4085
|
-
if (intercomReceipt) {
|
|
4086
|
-
return {
|
|
4087
|
-
content: [{ type: "text", text: intercomReceipt.text }],
|
|
4088
|
-
details: intercomReceipt.details,
|
|
4089
|
-
};
|
|
4090
|
-
}
|
|
4091
|
-
}
|
|
3394
|
+
workflowCwd: string;
|
|
3395
|
+
artifactsDir: string;
|
|
3396
|
+
aggregateOutputPath?: string;
|
|
3397
|
+
configuredOutputBaseDir?: string;
|
|
3398
|
+
discoverAgents: (cwd: string, scope: AgentScope) => { agents: AgentConfig[] };
|
|
3399
|
+
agents: AgentConfig[];
|
|
3400
|
+
workflowAgentScope?: unknown;
|
|
3401
|
+
outputOverride?: string;
|
|
3402
|
+
options?: { missionDetached?: boolean; suppressRoutineResultIntercom?: boolean; runFanoutBudget?: RunFanoutBudgetDescriptor; parentDeadlineAt?: number; capabilityCeiling?: ResolvedSubagentCapabilityCeiling };
|
|
3403
|
+
}): SubagentParamsLike {
|
|
3404
|
+
let childParams = input.childParams;
|
|
3405
|
+
if (input.childParams.output === undefined && input.childParams.resume === undefined && input.outputOverride !== undefined) {
|
|
3406
|
+
childParams = { ...input.childParams, output: input.outputOverride };
|
|
3407
|
+
} else if (input.childParams.output === undefined && input.childParams.resume === undefined && input.aggregateOutputPath !== undefined) {
|
|
3408
|
+
childParams = { ...input.childParams, output: workflowChildDefaultOutput(input.aggregateOutputPath, input.artifactsDir, input.parentWorkflowRunId, input.workflowKey) };
|
|
3409
|
+
} else if (input.childParams.resume === undefined) {
|
|
3410
|
+
const resolvedOutput = resolveWorkflowChildOutputPath({ ctxCwd: input.ctxCwd, workflowCwd: input.workflowCwd, artifactsDir: input.artifactsDir, workflowRunId: input.parentWorkflowRunId, aggregateOutputPath: input.aggregateOutputPath, configuredOutputBaseDir: input.configuredOutputBaseDir, discoverAgents: input.discoverAgents, agents: input.agents, workflowAgentScope: input.workflowAgentScope, key: input.workflowKey, params: input.childParams });
|
|
3411
|
+
if (resolvedOutput.path) childParams = { ...input.childParams, output: resolvedOutput.path };
|
|
3412
|
+
}
|
|
3413
|
+
const childCwd = typeof childParams.cwd === "string" ? resolveChildCwd(input.workflowCwd, childParams.cwd) : input.workflowCwd;
|
|
3414
|
+
const agentScope = resolveExecutionAgentScope(childParams.agentScope ?? input.workflowAgentScope);
|
|
3415
|
+
const discoveredAgents = input.discoverAgents(childCwd, agentScope).agents;
|
|
3416
|
+
const agent = typeof childParams.agent === "string"
|
|
3417
|
+
? resolveAgentName(childParams.agent, discoveredAgents).agent ?? resolveAgentName(childParams.agent, input.agents).agent
|
|
3418
|
+
: undefined;
|
|
3419
|
+
const externalAsyncRequired = agent?.runner?.type === "external-cli" || agent?.runner?.type === "external-job";
|
|
3420
|
+
return prepareWorkflowLaunchParams(input.workflowDefaults, childParams, input.parentWorkflowRunId, input.workflowKey, { ...input.options, externalAsyncRequired });
|
|
3421
|
+
}
|
|
4092
3422
|
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
3423
|
+
function finalizeSingleWorktreeHandoff(input: {
|
|
3424
|
+
worktreeSetup: WorktreeSetup;
|
|
3425
|
+
artifactsDir: string;
|
|
3426
|
+
runId: string;
|
|
3427
|
+
cwd: string;
|
|
3428
|
+
agent: string;
|
|
3429
|
+
result: SingleResult;
|
|
3430
|
+
}): { suffix: string; reference?: NonNullable<Details["parallelHandoff"]> } {
|
|
3431
|
+
const diffsDir = path.join(input.artifactsDir, "worktree-diffs", input.runId);
|
|
3432
|
+
const diffs = diffWorktrees(input.worktreeSetup, [input.agent], diffsDir);
|
|
3433
|
+
const diffSummary = formatWorktreeDiffSummary(diffs);
|
|
3434
|
+
const manifestPath = parallelHandoffPath(input.artifactsDir, input.runId);
|
|
3435
|
+
const handoff = {
|
|
3436
|
+
manifestPath,
|
|
3437
|
+
runId: input.runId,
|
|
3438
|
+
mode: "single" as const,
|
|
3439
|
+
source: "foreground" as const,
|
|
3440
|
+
cwd: input.cwd,
|
|
3441
|
+
stepIndex: 0,
|
|
3442
|
+
flatStartIndex: 0,
|
|
3443
|
+
setup: input.worktreeSetup,
|
|
3444
|
+
diffs,
|
|
3445
|
+
results: [{
|
|
3446
|
+
agent: input.result.agent,
|
|
3447
|
+
status: resolveSubagentResultStatus(omitUndefinedProperties({
|
|
3448
|
+
exitCode: input.result.exitCode,
|
|
3449
|
+
interrupted: input.result.interrupted,
|
|
3450
|
+
detached: input.result.detached,
|
|
3451
|
+
state: input.result.stopped ? "stopped" : undefined,
|
|
3452
|
+
processSignal: input.result.processSignal,
|
|
3453
|
+
timedOut: input.result.timedOut,
|
|
3454
|
+
stopped: input.result.stopped,
|
|
3455
|
+
turnBudgetExceeded: input.result.turnBudgetExceeded,
|
|
4103
3456
|
})),
|
|
4104
|
-
(
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
3457
|
+
summary: resultSummaryForIntercom(input.result),
|
|
3458
|
+
...(input.result.artifactPaths?.outputPath ? { outputPath: input.result.artifactPaths.outputPath } : {}),
|
|
3459
|
+
...(input.result.structuredOutput !== undefined ? { structuredOutput: input.result.structuredOutput } : {}),
|
|
3460
|
+
...(input.result.structuredOutputPath ? { structuredOutputPath: input.result.structuredOutputPath } : {}),
|
|
3461
|
+
...(input.result.sessionFile ? { sessionPath: input.result.sessionFile } : {}),
|
|
3462
|
+
}],
|
|
3463
|
+
};
|
|
3464
|
+
try {
|
|
3465
|
+
writeParallelHandoffGroup(handoff);
|
|
3466
|
+
const cleanup = cleanupWorktrees(input.worktreeSetup, { kind: "preserve", capturedDiffs: diffs, handoffManifestPath: manifestPath });
|
|
3467
|
+
const reference = writeParallelHandoffGroup({ ...handoff, cleanup });
|
|
4112
3468
|
return {
|
|
4113
|
-
|
|
4114
|
-
|
|
3469
|
+
suffix: [diffSummary, formatParallelHandoffReference(reference)].filter(Boolean).join("\n\n"),
|
|
3470
|
+
reference,
|
|
4115
3471
|
};
|
|
4116
|
-
}
|
|
4117
|
-
|
|
3472
|
+
} catch (error) {
|
|
3473
|
+
return { suffix: [diffSummary, formatParallelHandoffError(error)].filter(Boolean).join("\n\n") };
|
|
4118
3474
|
}
|
|
4119
3475
|
}
|
|
4120
3476
|
|
|
@@ -4138,7 +3494,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
4138
3494
|
contextPolicy,
|
|
4139
3495
|
} = data;
|
|
4140
3496
|
const onControlEvent = createForegroundControlNotifier(data, deps);
|
|
4141
|
-
const
|
|
3497
|
+
const childBridgeActive = intercomBridgeAppliesToAgent(data.intercomBridge, contextPolicy, params.agent!);
|
|
3498
|
+
const childIntercomTarget = childBridgeActive ? resolveSubagentIntercomTarget(runId, params.agent!, 0) : undefined;
|
|
4142
3499
|
const allProgress: AgentProgress[] = [];
|
|
4143
3500
|
const allArtifactPaths: ArtifactPaths[] = [];
|
|
4144
3501
|
const agentConfig = agents.find((a) => a.name === params.agent);
|
|
@@ -4155,126 +3512,54 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
4155
3512
|
const parentModel = data.parentModel;
|
|
4156
3513
|
const currentProvider = parentModel?.provider;
|
|
4157
3514
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
3515
|
+
const modelScopes = resolveModelScopesForAgent(data.modelScope, agentConfig.name, parentModel);
|
|
4158
3516
|
let task = params.task ?? "";
|
|
4159
3517
|
let modelOverride: string | undefined = resolveEffectiveSubagentModel(
|
|
4160
3518
|
params.model as string | undefined,
|
|
4161
3519
|
agentConfig.model,
|
|
4162
3520
|
parentModel,
|
|
4163
3521
|
availableModels,
|
|
4164
|
-
currentProvider,
|
|
4165
|
-
|
|
3522
|
+
agentConfig.modelProvider ?? currentProvider,
|
|
3523
|
+
modelScopes.length === 0 ? {} : { scope: modelScopes },
|
|
4166
3524
|
);
|
|
3525
|
+
const modelOverrideFromParent = inheritsParentModel(params.model as string | undefined, agentConfig.model, parentModel);
|
|
4167
3526
|
let skillOverride: string[] | false | undefined = normalizeSkillInput(params.skill);
|
|
4168
3527
|
let readsOverride: string[] | false | undefined = params.reads;
|
|
4169
3528
|
const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
|
|
4170
3529
|
let effectiveOutput = normalizeSingleOutputOverride(rawOutput, agentConfig.output);
|
|
4171
|
-
const effectiveOutputMode = params.outputMode ?? "inline";
|
|
3530
|
+
const effectiveOutputMode = params.outputMode ?? agentConfig.outputMode ?? "inline";
|
|
4172
3531
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
4173
3532
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
|
|
4174
3533
|
|
|
4175
|
-
if (params.clarify === true && ctx.hasUI) {
|
|
4176
|
-
const behavior = resolveStepBehavior(agentConfig, omitUndefinedProperties({ output: effectiveOutput, skills: skillOverride }));
|
|
4177
|
-
const availableSkills = discoverAvailableSkills(effectiveCwd);
|
|
4178
|
-
|
|
4179
|
-
const result = await ctx.ui.custom<ChainClarifyResult>(
|
|
4180
|
-
(tui, theme, _kb, done) =>
|
|
4181
|
-
new ChainClarifyComponent(
|
|
4182
|
-
tui, theme,
|
|
4183
|
-
[agentConfig],
|
|
4184
|
-
[task],
|
|
4185
|
-
task,
|
|
4186
|
-
undefined,
|
|
4187
|
-
[behavior],
|
|
4188
|
-
availableModels,
|
|
4189
|
-
currentProvider,
|
|
4190
|
-
availableSkills,
|
|
4191
|
-
done,
|
|
4192
|
-
"single",
|
|
4193
|
-
),
|
|
4194
|
-
{ overlay: true, overlayOptions: { anchor: "center", width: 84, maxHeight: "80%" } },
|
|
4195
|
-
);
|
|
4196
|
-
|
|
4197
|
-
if (!result || !result.confirmed) {
|
|
4198
|
-
return { content: [{ type: "text", text: "Cancelled" }], details: { mode: "single", results: [] } };
|
|
4199
|
-
}
|
|
4200
|
-
|
|
4201
|
-
task = result.templates[0]!;
|
|
4202
|
-
const override = result.behaviorOverrides[0];
|
|
4203
|
-
if (override?.model !== undefined) modelOverride = resolveEffectiveSubagentModel(override.model, agentConfig.model, parentModel, availableModels, currentProvider, data.modelScope === undefined ? {} : { scope: data.modelScope });
|
|
4204
|
-
if (override?.output !== undefined) effectiveOutput = normalizeSingleOutputOverride(override.output, agentConfig.output);
|
|
4205
|
-
if (override?.skills !== undefined) skillOverride = override.skills;
|
|
4206
|
-
if (override?.reads !== undefined) readsOverride = override.reads;
|
|
4207
3534
|
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
3535
|
+
const sourceCwd = effectiveCwd;
|
|
3536
|
+
const { setup: worktreeSetup, errorResult: worktreeSetupError } = createSingleWorktreeSetup(
|
|
3537
|
+
params.worktree,
|
|
3538
|
+
sourceCwd,
|
|
3539
|
+
runId,
|
|
3540
|
+
params.agent!,
|
|
3541
|
+
deps.config.worktreeSetupHook,
|
|
3542
|
+
deps.config.worktreeSetupHookTimeoutMs,
|
|
3543
|
+
deps.config.worktreeBaseDir,
|
|
3544
|
+
);
|
|
3545
|
+
if (worktreeSetupError) return worktreeSetupError;
|
|
3546
|
+
const singleCwd = worktreeSetup?.worktrees[0]?.agentCwd ?? sourceCwd;
|
|
3547
|
+
let pendingHandoff: Details["parallelHandoff"];
|
|
3548
|
+
if (worktreeSetup) {
|
|
3549
|
+
try {
|
|
3550
|
+
pendingHandoff = writePendingParallelHandoff({
|
|
3551
|
+
manifestPath: parallelHandoffPath(artifactsDir, runId),
|
|
3552
|
+
runId,
|
|
3553
|
+
mode: "single",
|
|
3554
|
+
source: "foreground",
|
|
3555
|
+
cwd: sourceCwd,
|
|
3556
|
+
stepIndex: 0,
|
|
3557
|
+
flatStartIndex: 0,
|
|
3558
|
+
setup: worktreeSetup,
|
|
4229
3559
|
});
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(task) : task,
|
|
4234
|
-
goal: task,
|
|
4235
|
-
agentConfig,
|
|
4236
|
-
recoveryAgentConfig: data.recoveryAgents.find((agent) => agent.name === params.agent),
|
|
4237
|
-
ctx: asyncCtx,
|
|
4238
|
-
availableModels,
|
|
4239
|
-
cwd: effectiveCwd,
|
|
4240
|
-
maxOutput: params.maxOutput,
|
|
4241
|
-
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
4242
|
-
artifactConfig,
|
|
4243
|
-
shareEnabled,
|
|
4244
|
-
activeAsyncCapacity: capacity.activeAsyncCapacity,
|
|
4245
|
-
sessionRoot,
|
|
4246
|
-
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride),
|
|
4247
|
-
context: contextPolicy.contextForAgent(params.agent!),
|
|
4248
|
-
skills: skillOverride === false ? [] : skillOverride,
|
|
4249
|
-
output: effectiveOutput,
|
|
4250
|
-
outputMode: effectiveOutputMode,
|
|
4251
|
-
...(readsOverride !== undefined ? { reads: readsOverride } : {}),
|
|
4252
|
-
outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
|
|
4253
|
-
modelOverride,
|
|
4254
|
-
thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
|
|
4255
|
-
maxSubagentDepth,
|
|
4256
|
-
waitToolEnabled: deps.waitToolEnabled,
|
|
4257
|
-
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
4258
|
-
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
4259
|
-
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
4260
|
-
controlConfig,
|
|
4261
|
-
intercomBridge: params.intercomBridge,
|
|
4262
|
-
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
4263
|
-
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
4264
|
-
nestedRoute: data.nestedRoute,
|
|
4265
|
-
agentContract: params.agentContract,
|
|
4266
|
-
structuredOutputSchema: params.outputSchema,
|
|
4267
|
-
acceptance: params.acceptance,
|
|
4268
|
-
timeoutMs: data.timeoutMs,
|
|
4269
|
-
turnBudget: data.turnBudget,
|
|
4270
|
-
toolBudget: effectiveToolBudget.toolBudget,
|
|
4271
|
-
usageBudget: data.usageBudget,
|
|
4272
|
-
allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
|
|
4273
|
-
runFanoutBudget: data.runFanoutBudget,
|
|
4274
|
-
}));
|
|
4275
|
-
} finally {
|
|
4276
|
-
finalizeClarifiedBackgroundCapacity(data, deps, capacity.activeAsyncCapacity);
|
|
4277
|
-
}
|
|
3560
|
+
} catch (error) {
|
|
3561
|
+
cleanupWorktrees(worktreeSetup);
|
|
3562
|
+
return { content: [{ type: "text", text: formatParallelHandoffError(error) }], isError: true, details: { mode: "single", results: [] } };
|
|
4278
3563
|
}
|
|
4279
3564
|
}
|
|
4280
3565
|
|
|
@@ -4283,18 +3568,19 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
4283
3568
|
task = wrapForkTask(task);
|
|
4284
3569
|
}
|
|
4285
3570
|
const cleanTask = task;
|
|
4286
|
-
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd,
|
|
3571
|
+
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, singleCwd, resolveSingleRunOutputBaseDir(deps, artifactsDir, runId));
|
|
4287
3572
|
const validationError = validateFileOnlyOutputMode(effectiveOutputMode, outputPath, `Single run (${params.agent})`);
|
|
4288
3573
|
if (validationError) {
|
|
3574
|
+
if (worktreeSetup) cleanupWorktrees(worktreeSetup);
|
|
4289
3575
|
return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [] } };
|
|
4290
3576
|
}
|
|
4291
3577
|
const structuredRuntime = params.outputSchema
|
|
4292
|
-
? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ? path.join(artifactsDir, "structured-output", runId) : undefined)
|
|
3578
|
+
? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ? path.join(artifactsDir, "structured-output", runId) : undefined, { captureAcceptanceReport: params.acceptance !== false })
|
|
4293
3579
|
: undefined;
|
|
4294
3580
|
// Reads: caller override > agent defaultReads > none. `~`/`~/` expand to home;
|
|
4295
3581
|
// absolute paths pass through; relative paths resolve against the child cwd.
|
|
4296
3582
|
const reads = readsOverride !== undefined ? readsOverride : agentConfig.defaultReads ?? false;
|
|
4297
|
-
const readPaths = Array.isArray(reads) ? resolveExistingReadPaths(reads,
|
|
3583
|
+
const readPaths = Array.isArray(reads) ? resolveExistingReadPaths(reads, singleCwd) : [];
|
|
4298
3584
|
const readsInstruction = readPaths.length > 0
|
|
4299
3585
|
? `[Read from: ${readPaths.join(", ")}]\n\n`
|
|
4300
3586
|
: "";
|
|
@@ -4311,13 +3597,13 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
4311
3597
|
let detachForeground: ((reason?: string) => boolean) | undefined;
|
|
4312
3598
|
const foregroundControl = deps.state.foregroundControls.get(runId);
|
|
4313
3599
|
if (foregroundControl) {
|
|
4314
|
-
const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride));
|
|
3600
|
+
const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent));
|
|
4315
3601
|
beginForegroundChild(foregroundControl, omitUndefinedProperties({
|
|
4316
3602
|
index: 0,
|
|
4317
3603
|
agent: params.agent!,
|
|
4318
3604
|
authoredTask,
|
|
4319
3605
|
effectivePrompt: task,
|
|
4320
|
-
cwd:
|
|
3606
|
+
cwd: singleCwd,
|
|
4321
3607
|
outputPath,
|
|
4322
3608
|
rerun: { params: { ...params, task: authoredTask, async: params.async ?? false } },
|
|
4323
3609
|
description: foregroundControl.description,
|
|
@@ -4349,14 +3635,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
4349
3635
|
...workflowForegroundSteeringLaunchOptions(foregroundControl, 0),
|
|
4350
3636
|
context: data.contextPolicy.contextForAgent(params.agent!),
|
|
4351
3637
|
runFanoutBudget: params.runFanoutAdmitted ? data.runFanoutBudget : { ...data.runFanoutBudget, parentPath: `${data.runFanoutBudget.parentPath ? `${data.runFanoutBudget.parentPath}/` : ""}single` },
|
|
4352
|
-
cwd:
|
|
3638
|
+
cwd: singleCwd,
|
|
4353
3639
|
signal,
|
|
4354
3640
|
interruptSignal: interruptController.signal,
|
|
4355
3641
|
allowIntercomDetach: agentConfig.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
|
|
4356
3642
|
intercomEvents: deps.pi.events,
|
|
4357
3643
|
runId,
|
|
4358
3644
|
sessionDir: sessionDirForIndex(0),
|
|
4359
|
-
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride),
|
|
3645
|
+
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride, modelOverrideFromParent),
|
|
4360
3646
|
share: shareEnabled,
|
|
4361
3647
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
4362
3648
|
artifactConfig,
|
|
@@ -4369,14 +3655,18 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
4369
3655
|
controlConfig,
|
|
4370
3656
|
onControlEvent,
|
|
4371
3657
|
intercomSessionName: childIntercomTarget,
|
|
4372
|
-
orchestratorIntercomTarget:
|
|
3658
|
+
orchestratorIntercomTarget: childBridgeActive ? data.intercomBridge.orchestratorTarget : undefined,
|
|
4373
3659
|
nestedRoute: foregroundControl?.nestedRoute,
|
|
4374
3660
|
index: 0,
|
|
4375
3661
|
modelOverride,
|
|
4376
|
-
|
|
3662
|
+
fast: params.fast,
|
|
3663
|
+
modelOverrideFromParent,
|
|
3664
|
+
thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent),
|
|
3665
|
+
thinkingCeiling: agentConfig.maxThinking,
|
|
3666
|
+
extensionBindings: params.extensionBindings,
|
|
4377
3667
|
availableModels,
|
|
4378
3668
|
preferredModelProvider: currentProvider,
|
|
4379
|
-
modelScope:
|
|
3669
|
+
modelScope: modelScopes,
|
|
4380
3670
|
skills: effectiveSkills,
|
|
4381
3671
|
structuredOutput: structuredRuntime,
|
|
4382
3672
|
agentContract: params.agentContract,
|
|
@@ -4389,10 +3679,21 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
4389
3679
|
onDetachedExit: (result) => {
|
|
4390
3680
|
try {
|
|
4391
3681
|
try {
|
|
4392
|
-
updateRememberedForegroundChild(deps.state, { runId, mode: "single", cwd:
|
|
3682
|
+
updateRememberedForegroundChild(deps.state, { runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, index: 0, result, events: deps.pi.events, notify: true });
|
|
4393
3683
|
} catch {
|
|
4394
3684
|
// Remembered foreground state is best-effort; run history and cleanup must still complete.
|
|
4395
3685
|
}
|
|
3686
|
+
const workflowParentRunId = params.workflowParentRunId ?? foregroundControl?.parentWorkflowRunId;
|
|
3687
|
+
if (workflowParentRunId) {
|
|
3688
|
+
reconcileDetachedWorkflowChildCompletion({
|
|
3689
|
+
state: deps.state,
|
|
3690
|
+
workflowRunId: workflowParentRunId,
|
|
3691
|
+
childRunId: runId,
|
|
3692
|
+
result,
|
|
3693
|
+
events: deps.pi.events,
|
|
3694
|
+
workflowKey: params.workflowKey ?? foregroundControl?.workflowKey,
|
|
3695
|
+
});
|
|
3696
|
+
}
|
|
4396
3697
|
} finally {
|
|
4397
3698
|
try {
|
|
4398
3699
|
if (!artifactConfig.enabled) cleanupStructuredOutputRuntime(structuredRuntime);
|
|
@@ -4416,6 +3717,9 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
4416
3717
|
capabilityCeiling: data.capabilityCeiling,
|
|
4417
3718
|
allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
|
|
4418
3719
|
}));
|
|
3720
|
+
} catch (error) {
|
|
3721
|
+
if (worktreeSetup) cleanupWorktrees(worktreeSetup);
|
|
3722
|
+
throw error;
|
|
4419
3723
|
} finally {
|
|
4420
3724
|
// An attached runSync rejection still owns its child and structured runtime.
|
|
4421
3725
|
// A successful detached receipt transfers both to onDetachedExit while the
|
|
@@ -4429,6 +3733,13 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
4429
3733
|
recordRun(params.agent!, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0);
|
|
4430
3734
|
}
|
|
4431
3735
|
|
|
3736
|
+
let worktreeHandoff: ReturnType<typeof finalizeSingleWorktreeHandoff> | undefined;
|
|
3737
|
+
if (worktreeSetup) {
|
|
3738
|
+
worktreeHandoff = r.detached
|
|
3739
|
+
? { suffix: pendingHandoff ? formatParallelHandoffReference(pendingHandoff) : "", reference: pendingHandoff }
|
|
3740
|
+
: finalizeSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd, agent: params.agent!, result: r });
|
|
3741
|
+
}
|
|
3742
|
+
|
|
4432
3743
|
if (r.progress) allProgress.push(r.progress);
|
|
4433
3744
|
if (r.artifactPaths) allArtifactPaths.push(r.artifactPaths);
|
|
4434
3745
|
|
|
@@ -4463,8 +3774,9 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
4463
3774
|
totalChildUsage: sumResultsUsage([r]),
|
|
4464
3775
|
totalCost,
|
|
4465
3776
|
usageBudget: usageBudgetState(data.usageBudget, totalCost),
|
|
3777
|
+
...(worktreeHandoff?.reference ? { parallelHandoff: worktreeHandoff.reference } : {}),
|
|
4466
3778
|
}));
|
|
4467
|
-
rememberForegroundRun(deps.state, { runId, mode: "single", cwd:
|
|
3779
|
+
rememberForegroundRun(deps.state, { runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: details.results, extensionBindings: params.extensionBindings });
|
|
4468
3780
|
|
|
4469
3781
|
const suppressRoutineResultIntercom = shouldSuppressRoutineResultIntercom({ suppressRoutineResultIntercom: params.suppressRoutineResultIntercom, results: [r] });
|
|
4470
3782
|
if (!r.detached && !r.interrupted && !suppressRoutineResultIntercom) {
|
|
@@ -4487,6 +3799,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
4487
3799
|
}
|
|
4488
3800
|
}
|
|
4489
3801
|
|
|
3802
|
+
const worktreeSuffix = worktreeHandoff?.suffix ? `\n\n${worktreeHandoff.suffix}` : "";
|
|
4490
3803
|
if (r.detached) {
|
|
4491
3804
|
const statusRecovery = `subagent({ action: "status", id: "${runId}" }) to recover the result; do not resume or launch a replacement while it remains detached.`;
|
|
4492
3805
|
const blockingRecovery = `subagent_wait({ id: "${runId}" }). Use ${statusRecovery}`;
|
|
@@ -4496,26 +3809,26 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
4496
3809
|
? `Detached at user request: ${params.agent}. The child continues independently. Register a completion wake-up with subagent_wait({ id: "${runId}", nonBlocking: true }), or use ${statusRecovery}`
|
|
4497
3810
|
: `Detached before task completion: ${params.agent}. Wait with ${blockingRecovery}`;
|
|
4498
3811
|
return {
|
|
4499
|
-
content: [{ type: "text", text: message }],
|
|
3812
|
+
content: [{ type: "text", text: `${message}${worktreeSuffix}` }],
|
|
4500
3813
|
details,
|
|
4501
3814
|
};
|
|
4502
3815
|
}
|
|
4503
3816
|
|
|
4504
3817
|
if (r.interrupted) {
|
|
4505
3818
|
return {
|
|
4506
|
-
content: [{ type: "text", text: `Run paused after interrupt (${params.agent}). Waiting for explicit next action
|
|
3819
|
+
content: [{ type: "text", text: `Run paused after interrupt (${params.agent}). Waiting for explicit next action.${worktreeSuffix}` }],
|
|
4507
3820
|
details,
|
|
4508
3821
|
};
|
|
4509
3822
|
}
|
|
4510
3823
|
|
|
4511
3824
|
if (r.exitCode !== 0)
|
|
4512
3825
|
return {
|
|
4513
|
-
content: [{ type: "text", text: formatFailedSingleRunOutput(r, finalizedOutput.displayOutput) }],
|
|
3826
|
+
content: [{ type: "text", text: `${formatFailedSingleRunOutput(r, finalizedOutput.displayOutput)}${worktreeSuffix}` }],
|
|
4514
3827
|
details,
|
|
4515
3828
|
isError: true,
|
|
4516
3829
|
};
|
|
4517
3830
|
return {
|
|
4518
|
-
content: [{ type: "text", text: finalizedOutput.displayOutput || "(no output)" }],
|
|
3831
|
+
content: [{ type: "text", text: `${finalizedOutput.displayOutput || "(no output)"}${worktreeSuffix}` }],
|
|
4519
3832
|
details,
|
|
4520
3833
|
};
|
|
4521
3834
|
}
|
|
@@ -4607,13 +3920,20 @@ export function bindMissionWorkflowChildAsyncLaunch(
|
|
|
4607
3920
|
return { ...params, workflowChildAsyncId: id };
|
|
4608
3921
|
}
|
|
4609
3922
|
|
|
4610
|
-
function workflowChildResult(
|
|
3923
|
+
function workflowChildResult(
|
|
3924
|
+
key: string,
|
|
3925
|
+
result: AgentToolResult<Details>,
|
|
3926
|
+
childParams: Record<string, unknown> = {},
|
|
3927
|
+
resumeState?: SubagentState,
|
|
3928
|
+
): WorkflowScriptChildResult {
|
|
4611
3929
|
const receiptOutput = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n");
|
|
4612
3930
|
const output = result.details.results.length === 1 && result.details.results[0]?.finalOutput !== undefined
|
|
4613
3931
|
? result.details.results[0].finalOutput
|
|
4614
3932
|
: receiptOutput;
|
|
4615
3933
|
const detached = result.details.results.some((child) => child.detached);
|
|
4616
|
-
const
|
|
3934
|
+
const interrupted = result.details.results.some((child) => child.interrupted);
|
|
3935
|
+
const stopped = result.details.results.some((child) => child.stopped);
|
|
3936
|
+
const ok = result.isError !== true && !detached && !interrupted && !stopped;
|
|
4617
3937
|
const artifactPaths = new Set<string>();
|
|
4618
3938
|
if (result.details.asyncDir) artifactPaths.add(result.details.asyncDir);
|
|
4619
3939
|
if (result.details.parallelHandoff?.path) artifactPaths.add(result.details.parallelHandoff.path);
|
|
@@ -4624,28 +3944,175 @@ function workflowChildResult(key: string, result: AgentToolResult<Details>): Wor
|
|
|
4624
3944
|
}
|
|
4625
3945
|
const structured = result.details.results.map((child) => child.structuredOutput).filter((value) => value !== undefined);
|
|
4626
3946
|
const resolvedAgents = [...new Set(result.details.results.map((child) => child.agent).filter((agent): agent is string => Boolean(agent)))];
|
|
3947
|
+
const resolvedContexts = [...new Set(result.details.results.map((child) => child.context).filter((context): context is "fresh" | "fork" => context === "fresh" || context === "fork"))];
|
|
3948
|
+
const runId = result.details.runId ?? result.details.asyncId;
|
|
3949
|
+
let resumability: WorkflowScriptChildResult["resumability"];
|
|
3950
|
+
if (!runId || !resumeState) {
|
|
3951
|
+
resumability = { state: "not-resumable", reason: runId ? "resumability was not inspected" : "child produced no run id" };
|
|
3952
|
+
} else {
|
|
3953
|
+
try {
|
|
3954
|
+
const target = resolveResumeTarget({ id: runId }, resumeState, { asyncRequireSessionFile: true, exactOnly: true });
|
|
3955
|
+
resumability = target.kind === "revive"
|
|
3956
|
+
? { state: "resumable" }
|
|
3957
|
+
: { state: "not-resumable", reason: "child is still running" };
|
|
3958
|
+
} catch (error) {
|
|
3959
|
+
resumability = { state: "not-resumable", reason: error instanceof Error ? error.message : String(error) };
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
const requestedContext = childParams.context === "fresh" || childParams.context === "fork" ? childParams.context : undefined;
|
|
3963
|
+
const resolvedContext = result.details.context ?? (resolvedContexts.length === 1 ? resolvedContexts[0] : resolvedContexts.length > 1 ? "mixed" : undefined);
|
|
3964
|
+
const resumeSourceRunId = typeof childParams.resume === "string" && childParams.resume.trim() ? childParams.resume.trim() : undefined;
|
|
3965
|
+
const continuationRunIds = [...new Set([resumeSourceRunId, runId].filter((value): value is string => Boolean(value)))];
|
|
3966
|
+
const outputReference = result.details.results.find((child) => child.savedOutputPath)?.savedOutputPath
|
|
3967
|
+
?? result.details.results.find((child) => child.outputReference?.path)?.outputReference?.path;
|
|
4627
3968
|
return {
|
|
4628
3969
|
key,
|
|
4629
3970
|
ok,
|
|
4630
3971
|
...(resolvedAgents.length === 1 ? { agent: resolvedAgents[0] } : {}),
|
|
4631
|
-
...(
|
|
3972
|
+
...(runId ? { runId } : {}),
|
|
4632
3973
|
output,
|
|
4633
3974
|
...(!ok ? { error: receiptOutput || output || "Child run failed." } : {}),
|
|
4634
3975
|
...(detached ? { detached: true } : {}),
|
|
3976
|
+
...(interrupted ? { interrupted: true } : {}),
|
|
3977
|
+
...(stopped ? { stopped: true } : {}),
|
|
4635
3978
|
...(structured.length === 1 ? { structuredOutput: structured[0] } : structured.length > 1 ? { structuredOutput: structured } : {}),
|
|
3979
|
+
...(requestedContext ? { requestedContext } : {}),
|
|
3980
|
+
...(resolvedContext ? { resolvedContext } : {}),
|
|
3981
|
+
...(outputReference ? { outputReference } : {}),
|
|
3982
|
+
resumability,
|
|
3983
|
+
continuation: { runIds: continuationRunIds },
|
|
4636
3984
|
artifactPaths: [...artifactPaths],
|
|
4637
3985
|
results: result.details.results,
|
|
4638
3986
|
};
|
|
4639
3987
|
}
|
|
4640
3988
|
|
|
3989
|
+
function workflowSteerReceipt(key: string, result: AgentToolResult<Details>): WorkflowSteerResult {
|
|
3990
|
+
const steering = result.details.steering;
|
|
3991
|
+
const error = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n") || undefined;
|
|
3992
|
+
if (!steering) return { key, state: "failed", ...(error ? { error } : {}) };
|
|
3993
|
+
const state = result.isError === true || steering.state === "failed" || steering.state === "partial"
|
|
3994
|
+
? "failed"
|
|
3995
|
+
: steering.deliveryStatus === "delivered" ? "delivered" : "queued";
|
|
3996
|
+
return {
|
|
3997
|
+
key,
|
|
3998
|
+
state,
|
|
3999
|
+
requestId: steering.requestId,
|
|
4000
|
+
deliveryStatus: steering.deliveryStatus,
|
|
4001
|
+
targets: steering.targets.map((target) => ({ index: target.index, state: target.state, ...(target.reason ? { reason: target.reason } : {}) })),
|
|
4002
|
+
...(state === "failed" && error ? { error } : {}),
|
|
4003
|
+
};
|
|
4004
|
+
}
|
|
4005
|
+
|
|
4006
|
+
function resolveKeyedWorkflowResume(
|
|
4007
|
+
reference: WorkflowReceiptResumeReference,
|
|
4008
|
+
state: SubagentState,
|
|
4009
|
+
): { runId: string; runIds: string[] } {
|
|
4010
|
+
const entry = resolveWorkflowReceiptResumeEntry({
|
|
4011
|
+
reference,
|
|
4012
|
+
asyncDirRoot: DIRS.async,
|
|
4013
|
+
assertResumable(runId) {
|
|
4014
|
+
const target = resolveResumeTarget({ id: runId }, state, { asyncRequireSessionFile: true, exactOnly: true });
|
|
4015
|
+
if (target.kind !== "revive") throw new Error(`Workflow receipt child '${reference.key}' latest run '${runId}' is still running.`);
|
|
4016
|
+
},
|
|
4017
|
+
});
|
|
4018
|
+
const runId = entry.latestRunId;
|
|
4019
|
+
if (!runId) throw new Error(`Workflow receipt child '${reference.key}' has no retained run id.`);
|
|
4020
|
+
return { runId, runIds: entry.continuation.runIds };
|
|
4021
|
+
}
|
|
4022
|
+
|
|
4023
|
+
function terminalWorkflowReceipt(
|
|
4024
|
+
workflowRunId: string,
|
|
4025
|
+
state: WorkflowReceiptState,
|
|
4026
|
+
children: WorkflowScriptChildResult[],
|
|
4027
|
+
): WorkflowReceipt {
|
|
4028
|
+
return buildWorkflowReceipt({ workflowRunId, state, children });
|
|
4029
|
+
}
|
|
4030
|
+
|
|
4031
|
+
export async function steerWorkflowChildByKey(input: {
|
|
4032
|
+
state: SubagentState;
|
|
4033
|
+
workflowRunId: string;
|
|
4034
|
+
key: string;
|
|
4035
|
+
message: string;
|
|
4036
|
+
options: WorkflowSteerOptions;
|
|
4037
|
+
signal?: AbortSignal;
|
|
4038
|
+
asyncDirRoot?: string;
|
|
4039
|
+
resolveRunId?: () => string | undefined;
|
|
4040
|
+
}): Promise<WorkflowSteerResult> {
|
|
4041
|
+
const asyncDirRoot = input.asyncDirRoot ?? DIRS.async;
|
|
4042
|
+
const ackTimeoutMs = input.options.ackTimeoutMs ?? 3_000;
|
|
4043
|
+
const deadline = Date.now() + ackTimeoutMs;
|
|
4044
|
+
while (true) {
|
|
4045
|
+
const control = [...input.state.foregroundControls.values()].find((candidate) => candidate.parentWorkflowRunId === input.workflowRunId
|
|
4046
|
+
&& candidate.workflowKey === input.key
|
|
4047
|
+
&& Boolean(candidate.workflowSteeringDir)
|
|
4048
|
+
&& (candidate.activeChildren?.size ?? 0) > 0);
|
|
4049
|
+
if (control) {
|
|
4050
|
+
const result = await steerWorkflowForegroundTarget({
|
|
4051
|
+
target: { control, workflowRunId: input.workflowRunId, sourceRunId: control.runId },
|
|
4052
|
+
message: input.message,
|
|
4053
|
+
mode: input.options.mode,
|
|
4054
|
+
index: input.options.index,
|
|
4055
|
+
ackTimeoutMs: Math.max(1, deadline - Date.now()),
|
|
4056
|
+
signal: input.signal,
|
|
4057
|
+
});
|
|
4058
|
+
return workflowSteerReceipt(input.key, result);
|
|
4059
|
+
}
|
|
4060
|
+
|
|
4061
|
+
const workflowStatus = readStatus(path.join(asyncDirRoot, input.workflowRunId));
|
|
4062
|
+
const step = workflowStatus?.steps?.find((candidate) => candidate.workflowKey === input.key);
|
|
4063
|
+
const childRunId = step?.runId ?? input.resolveRunId?.();
|
|
4064
|
+
if (childRunId) {
|
|
4065
|
+
const asyncDir = path.join(asyncDirRoot, childRunId);
|
|
4066
|
+
const childStatus = reconcileAsyncRun(asyncDir).status;
|
|
4067
|
+
if (childStatus && childStatus.state !== "running" && childStatus.state !== "queued" && (input.options.mode !== "follow_up" || !canQueueRetainedAsyncFollowUp(childStatus, input.options.index))) {
|
|
4068
|
+
return { key: input.key, state: "missed", error: `Workflow child '${input.key}' is ${childStatus.state}.` };
|
|
4069
|
+
}
|
|
4070
|
+
if (childStatus) {
|
|
4071
|
+
const result = await steerAsyncRun({
|
|
4072
|
+
state: input.state,
|
|
4073
|
+
runId: childRunId,
|
|
4074
|
+
message: input.message,
|
|
4075
|
+
mode: input.options.mode,
|
|
4076
|
+
index: input.options.index,
|
|
4077
|
+
ackTimeoutMs: Math.max(1, deadline - Date.now()),
|
|
4078
|
+
location: { asyncDir },
|
|
4079
|
+
signal: input.signal,
|
|
4080
|
+
});
|
|
4081
|
+
return workflowSteerReceipt(input.key, result);
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
if (step && step.status !== "running" && step.status !== "pending") {
|
|
4085
|
+
return { key: input.key, state: "missed", error: `Workflow child '${input.key}' is ${step.status}.` };
|
|
4086
|
+
}
|
|
4087
|
+
if (workflowStatus && workflowStatus.state !== "running" && workflowStatus.state !== "queued") {
|
|
4088
|
+
return { key: input.key, state: "missed", error: `Workflow '${input.workflowRunId}' is ${workflowStatus.state}.` };
|
|
4089
|
+
}
|
|
4090
|
+
if (input.signal?.aborted || Date.now() >= deadline) {
|
|
4091
|
+
return { key: input.key, state: "missed", error: `Workflow child '${input.key}' had no live steering route.` };
|
|
4092
|
+
}
|
|
4093
|
+
await new Promise<void>((resolve) => setTimeout(resolve, Math.min(10, Math.max(1, deadline - Date.now()))));
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
|
|
4641
4097
|
export function prepareWorkflowLaunchParams(
|
|
4642
4098
|
workflowDefaults: SubagentParamsLike,
|
|
4643
4099
|
childParams: Record<string, unknown>,
|
|
4644
4100
|
parentWorkflowRunId: string,
|
|
4645
4101
|
workflowKey: string,
|
|
4646
|
-
options: { missionDetached?: boolean; suppressRoutineResultIntercom?: boolean; runFanoutBudget?: RunFanoutBudgetDescriptor } = {},
|
|
4102
|
+
options: { missionDetached?: boolean; suppressRoutineResultIntercom?: boolean; runFanoutBudget?: RunFanoutBudgetDescriptor; parentDeadlineAt?: number; externalAsyncRequired?: boolean; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } = {},
|
|
4647
4103
|
): SubagentParamsLike {
|
|
4104
|
+
const capabilityCeiling = intersectSubagentCapabilityCeilings(workflowDefaults.capabilityCeiling, options.capabilityCeiling);
|
|
4105
|
+
const parentTimeoutMs = options.parentDeadlineAt === undefined
|
|
4106
|
+
|| childParams.timeoutMs !== undefined
|
|
4107
|
+
|| childParams.maxRuntimeMs !== undefined
|
|
4108
|
+
|| workflowDefaults.timeoutMs !== undefined
|
|
4109
|
+
|| workflowDefaults.maxRuntimeMs !== undefined
|
|
4110
|
+
? undefined
|
|
4111
|
+
: Math.max(1, options.parentDeadlineAt - Date.now());
|
|
4648
4112
|
if (typeof childParams.resume === "string") {
|
|
4113
|
+
if (childParams.extensionBindings !== undefined || workflowDefaults.extensionBindings !== undefined) {
|
|
4114
|
+
throw new Error("extensionBindings is not supported with retained resume; resume uses the original retained child binding.");
|
|
4115
|
+
}
|
|
4649
4116
|
if (childParams.gate !== undefined || workflowDefaults.gate !== undefined) {
|
|
4650
4117
|
throw new Error("gate is not supported with retained resume; resume uses the retained child contract.");
|
|
4651
4118
|
}
|
|
@@ -4653,33 +4120,41 @@ export function prepareWorkflowLaunchParams(
|
|
|
4653
4120
|
const turnBudget = childParams.turnBudget ?? workflowDefaults.turnBudget;
|
|
4654
4121
|
const toolBudget = childParams.toolBudget ?? workflowDefaults.toolBudget;
|
|
4655
4122
|
const intercomBridge = childParams.intercomBridge ?? workflowDefaults.intercomBridge;
|
|
4123
|
+
const worktree = childParams.worktree ?? workflowDefaults.worktree;
|
|
4656
4124
|
return {
|
|
4657
4125
|
action: "resume",
|
|
4658
4126
|
id: childParams.resume.trim(),
|
|
4127
|
+
...(typeof childParams.index === "number" && Number.isInteger(childParams.index) ? { index: childParams.index } : {}),
|
|
4659
4128
|
message: typeof childParams.task === "string" ? childParams.task.trim() : "",
|
|
4660
4129
|
workflowParentRunId: parentWorkflowRunId,
|
|
4661
4130
|
workflowKey,
|
|
4131
|
+
...(worktree !== undefined ? { worktree: worktree as boolean } : {}),
|
|
4662
4132
|
...(options.runFanoutBudget ? { runFanoutBudget: { ...options.runFanoutBudget, parentPath: `${options.runFanoutBudget.parentPath ? `${options.runFanoutBudget.parentPath}/` : ""}workflow[${workflowKey}]` } } : {}),
|
|
4663
4133
|
...(options.missionDetached ? { mission: false } : {}),
|
|
4664
4134
|
...(timeoutMs !== undefined ? { timeoutMs: timeoutMs as number } : {}),
|
|
4665
4135
|
...(turnBudget !== undefined ? { turnBudget: turnBudget as TurnBudgetConfig } : {}),
|
|
4666
4136
|
...(toolBudget !== undefined ? { toolBudget: toolBudget as ToolBudgetConfig } : {}),
|
|
4667
4137
|
...(intercomBridge !== undefined ? { intercomBridge: intercomBridge as IntercomBridgeConfig } : {}),
|
|
4138
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
4668
4139
|
};
|
|
4669
4140
|
}
|
|
4670
4141
|
const launchParams = {
|
|
4671
4142
|
...workflowDefaults,
|
|
4672
|
-
async: false,
|
|
4143
|
+
async: options.externalAsyncRequired === true && childParams.async === undefined && workflowDefaults.async === undefined ? true : false,
|
|
4673
4144
|
...childParams,
|
|
4145
|
+
...(options.externalAsyncRequired === true && childParams.async === undefined && workflowDefaults.async === undefined ? { workflowAwaitAsync: true } : {}),
|
|
4674
4146
|
...(options.missionDetached ? { mission: false } : {}),
|
|
4675
4147
|
workflowParentRunId: parentWorkflowRunId,
|
|
4676
4148
|
workflowKey,
|
|
4149
|
+
...(parentTimeoutMs !== undefined ? { workflowParentDeadlineAt: options.parentDeadlineAt } : {}),
|
|
4677
4150
|
...(options.runFanoutBudget ? { runFanoutBudget: { ...options.runFanoutBudget, parentPath: `${options.runFanoutBudget.parentPath ? `${options.runFanoutBudget.parentPath}/` : ""}workflow[${workflowKey}]` } } : {}),
|
|
4678
4151
|
...(options.suppressRoutineResultIntercom ? { suppressRoutineResultIntercom: true } : {}),
|
|
4152
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
4679
4153
|
} as SubagentParamsLike;
|
|
4154
|
+
if (launchParams.extensionBindings !== undefined) launchParams.extensionBindings = normalizeExtensionBindings(launchParams.extensionBindings)!.value;
|
|
4680
4155
|
const normalizedGate = normalizeGateParams(launchParams);
|
|
4681
4156
|
if (!normalizedGate.ok) throw new Error(normalizedGate.error);
|
|
4682
|
-
return
|
|
4157
|
+
return normalizedGate.params;
|
|
4683
4158
|
}
|
|
4684
4159
|
|
|
4685
4160
|
type GateParamsNormalizationResult =
|
|
@@ -4697,41 +4172,6 @@ function normalizeGateParams(params: SubagentParamsLike): GateParamsNormalizatio
|
|
|
4697
4172
|
return { ok: true, params: { ...rest, ...(normalized.acceptance !== undefined ? { acceptance: normalized.acceptance } : {}) } };
|
|
4698
4173
|
}
|
|
4699
4174
|
|
|
4700
|
-
function prepareWorkflowChildParams(params: SubagentParamsLike): SubagentParamsLike {
|
|
4701
|
-
if (params.worktree !== true || !params.agent) return params;
|
|
4702
|
-
const {
|
|
4703
|
-
agent,
|
|
4704
|
-
task = "",
|
|
4705
|
-
model,
|
|
4706
|
-
skill,
|
|
4707
|
-
output,
|
|
4708
|
-
outputMode,
|
|
4709
|
-
outputSchema,
|
|
4710
|
-
acceptance,
|
|
4711
|
-
agentContract,
|
|
4712
|
-
toolBudget,
|
|
4713
|
-
reads,
|
|
4714
|
-
...runParams
|
|
4715
|
-
} = params;
|
|
4716
|
-
return {
|
|
4717
|
-
...runParams,
|
|
4718
|
-
worktree: true,
|
|
4719
|
-
tasks: [{
|
|
4720
|
-
agent,
|
|
4721
|
-
task,
|
|
4722
|
-
...(model !== undefined ? { model } : {}),
|
|
4723
|
-
...(skill !== undefined ? { skill } : {}),
|
|
4724
|
-
...(output !== undefined ? { output } : {}),
|
|
4725
|
-
...(reads !== undefined ? { reads } : {}),
|
|
4726
|
-
...(outputMode !== undefined ? { outputMode } : {}),
|
|
4727
|
-
...(outputSchema !== undefined ? { outputSchema } : {}),
|
|
4728
|
-
...(acceptance !== undefined ? { acceptance } : {}),
|
|
4729
|
-
...(agentContract !== undefined ? { agentContract } : {}),
|
|
4730
|
-
...(toolBudget !== undefined ? { toolBudget } : {}),
|
|
4731
|
-
}],
|
|
4732
|
-
};
|
|
4733
|
-
}
|
|
4734
|
-
|
|
4735
4175
|
function formatWorkflowValue(value: unknown): string {
|
|
4736
4176
|
if (value === undefined) return "(undefined)";
|
|
4737
4177
|
if (typeof value === "string") return value;
|
|
@@ -4754,6 +4194,38 @@ function workflowChatProgressUpdate(
|
|
|
4754
4194
|
};
|
|
4755
4195
|
}
|
|
4756
4196
|
|
|
4197
|
+
function createScheduledOwnerState(source: SubagentState, ownerSessionId: string, ctx: ExtensionContext): SubagentState {
|
|
4198
|
+
const ownerSpawns = source.subagentSpawns?.sessionId === ownerSessionId
|
|
4199
|
+
? {
|
|
4200
|
+
...source.subagentSpawns,
|
|
4201
|
+
grantHistory: [...(source.subagentSpawns.grantHistory ?? [])],
|
|
4202
|
+
}
|
|
4203
|
+
: undefined;
|
|
4204
|
+
return {
|
|
4205
|
+
...source,
|
|
4206
|
+
baseCwd: ctx.cwd,
|
|
4207
|
+
currentSessionId: ownerSessionId,
|
|
4208
|
+
parentSessionFile: ctx.sessionManager.getSessionFile() ?? null,
|
|
4209
|
+
subagentInProgress: false,
|
|
4210
|
+
...(ownerSpawns ? { subagentSpawns: ownerSpawns } : { subagentSpawns: undefined }),
|
|
4211
|
+
herdrProjectPanes: new Map(),
|
|
4212
|
+
asyncJobs: new Map(),
|
|
4213
|
+
fleetJobs: new Map(),
|
|
4214
|
+
foregroundRuns: new Map(),
|
|
4215
|
+
foregroundControls: new Map(),
|
|
4216
|
+
lastForegroundControlId: null,
|
|
4217
|
+
cleanupTimers: new Map(),
|
|
4218
|
+
lastUiContext: null,
|
|
4219
|
+
poller: null,
|
|
4220
|
+
completionSeen: new Map(),
|
|
4221
|
+
watcher: null,
|
|
4222
|
+
watcherRestartTimer: null,
|
|
4223
|
+
waitSubscriptions: new Map(),
|
|
4224
|
+
workflowControllers: new Map(),
|
|
4225
|
+
workflowChildStops: new Map(),
|
|
4226
|
+
};
|
|
4227
|
+
}
|
|
4228
|
+
|
|
4757
4229
|
export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
4758
4230
|
execute: (
|
|
4759
4231
|
id: string,
|
|
@@ -4782,11 +4254,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4782
4254
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
4783
4255
|
ctx: ExtensionContext,
|
|
4784
4256
|
) => Promise<AgentToolResult<Details>>;
|
|
4257
|
+
/** Scheduled launches retain their owning context without replacing the live active session. */
|
|
4258
|
+
executeScheduled: (
|
|
4259
|
+
id: string,
|
|
4260
|
+
params: SubagentParamsLike,
|
|
4261
|
+
signal: AbortSignal,
|
|
4262
|
+
ctx: ExtensionContext,
|
|
4263
|
+
) => Promise<AgentToolResult<Details>>;
|
|
4785
4264
|
} {
|
|
4786
4265
|
const delegatedThinkingOverrides = new WeakMap<object, AgentConfig["thinking"]>();
|
|
4787
4266
|
const delegatedZeroToolBudgets = new WeakSet<object>();
|
|
4788
4267
|
const delegatedExecutions = new WeakSet<object>();
|
|
4789
4268
|
const warnedArtifactPackageDirs = new Set<string>();
|
|
4269
|
+
const scheduledOwnerExecutors = new Map<string, ReturnType<typeof createSubagentExecutor>>();
|
|
4790
4270
|
const execute = async (
|
|
4791
4271
|
_id: string,
|
|
4792
4272
|
params: SubagentParamsLike,
|
|
@@ -4807,12 +4287,21 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4807
4287
|
if (!normalizedGate.ok) return buildRequestedModeError(params, normalizedGate.error);
|
|
4808
4288
|
const requestParams = normalizedGate.params;
|
|
4809
4289
|
const normalizedAction = typeof requestParams.action === "string" ? requestParams.action.trim() : requestParams.action;
|
|
4290
|
+
if (normalizedAction === "resume" && requestParams.extensionBindings !== undefined) return buildRequestedModeError(requestParams, "extensionBindings is not supported with action='resume'; resume uses the original retained child binding.");
|
|
4810
4291
|
if (requestParams.workflowScript !== undefined && normalizedAction === undefined) {
|
|
4292
|
+
if (requestParams.extensionBindings !== undefined) {
|
|
4293
|
+
try {
|
|
4294
|
+
requestParams.extensionBindings = normalizeExtensionBindings(requestParams.extensionBindings)!.value;
|
|
4295
|
+
} catch (error) {
|
|
4296
|
+
return buildRequestedModeError(requestParams, error instanceof Error ? error.message : String(error));
|
|
4297
|
+
}
|
|
4298
|
+
}
|
|
4811
4299
|
const parentCwd = ctx.cwd;
|
|
4812
4300
|
const timeout = requestParams.timeoutMs ?? requestParams.maxRuntimeMs ?? (requestParams.async === false ? resolveConfigDefaultTimeoutMs(deps.config.timeoutMs) ?? DEFAULT_FOREGROUND_TIMEOUT_MS : undefined);
|
|
4813
4301
|
const workflowUsageBudget = validateUsageBudgetConfig(requestParams.usageBudget ?? deps.config.usageBudget, requestParams.usageBudget ? "usageBudget" : "config.usageBudget");
|
|
4814
4302
|
if (workflowUsageBudget.error) return buildRequestedModeError(requestParams, workflowUsageBudget.error);
|
|
4815
4303
|
const workflowCwd = resolveRequestedCwd(parentCwd, requestParams.cwd);
|
|
4304
|
+
const workflowAgents = deps.discoverAgents(workflowCwd, resolveExecutionAgentScope(requestParams.agentScope)).agents;
|
|
4816
4305
|
const workflowArtifactConfig: ArtifactConfig = omitUndefinedProperties({
|
|
4817
4306
|
...DEFAULT_ARTIFACT_CONFIG,
|
|
4818
4307
|
enabled: requestParams.artifacts !== false,
|
|
@@ -4824,7 +4313,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4824
4313
|
const warning = getProjectArtifactPackagingWarning(workflowCwd);
|
|
4825
4314
|
if (warning) console.warn(`[pi-subagents] ${warning}`);
|
|
4826
4315
|
}
|
|
4827
|
-
const workflowPrompts = { render: (ref: string, vars?: unknown) => renderWorkflowPrompt(ref, vars, workflowCwd) };
|
|
4828
4316
|
const chatProgressResult = resolveWorkflowChatProgress({ requested: requestParams.chatProgress, parentCwd, workflowCwd, background: requestParams.async !== false });
|
|
4829
4317
|
if (chatProgressResult.error) return { content: [{ type: "text", text: chatProgressResult.error }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4830
4318
|
const chatProgress = chatProgressResult.projection!;
|
|
@@ -4901,12 +4389,18 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4901
4389
|
};
|
|
4902
4390
|
if (workflowRunId) {
|
|
4903
4391
|
const toolCallId = _id;
|
|
4392
|
+
const workflowSessionRoot = requestParams.sessionDir
|
|
4393
|
+
? path.resolve(deps.expandTilde(requestParams.sessionDir))
|
|
4394
|
+
: trustedSessionRootsForStatus(ctx, deps)[0];
|
|
4904
4395
|
const asyncDir = path.join(DIRS.async, workflowRunId);
|
|
4905
4396
|
const resultPath = resultFilePath(DIRS.results, workflowRunId);
|
|
4906
4397
|
const statusPath = path.join(asyncDir, "status.json");
|
|
4907
4398
|
const eventsPath = path.join(asyncDir, "events.jsonl");
|
|
4908
4399
|
const startedAt = Date.now();
|
|
4909
4400
|
const currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
4401
|
+
const workflowCapabilityCeiling = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(currentSessionId));
|
|
4402
|
+
const completionOwnerId = deps.state.completionOwnerId ?? currentCompletionOwnerId();
|
|
4403
|
+
deps.state.completionOwnerId = completionOwnerId;
|
|
4910
4404
|
try {
|
|
4911
4405
|
fs.mkdirSync(asyncDir, { recursive: true });
|
|
4912
4406
|
writeRunFanoutBudgetDescriptor(asyncDir, workflowFanoutBudget);
|
|
@@ -4917,6 +4411,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4917
4411
|
}
|
|
4918
4412
|
const controller = new AbortController();
|
|
4919
4413
|
deps.state.workflowControllers ??= new Map();
|
|
4414
|
+
deps.state.workflowChildStops ??= new Map();
|
|
4920
4415
|
deps.state.workflowControllers.set(workflowRunId, controller);
|
|
4921
4416
|
workflowCapacity?.markWorkflowStarted();
|
|
4922
4417
|
if (workflowCapacity) deps.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(currentSessionId, resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession), { liveWorkflowRunIds: new Set(deps.state.workflowControllers.keys()) });
|
|
@@ -4924,51 +4419,114 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4924
4419
|
runId: workflowRunId,
|
|
4925
4420
|
toolCallId,
|
|
4926
4421
|
sessionId: currentSessionId ?? undefined,
|
|
4422
|
+
completionOwnerId,
|
|
4927
4423
|
mode: "workflow",
|
|
4928
4424
|
state: "running",
|
|
4929
4425
|
startedAt,
|
|
4930
4426
|
lastUpdate: startedAt,
|
|
4931
4427
|
...(timeout !== undefined ? { deadlineAt: startedAt + timeout, timeoutMs: timeout } : {}),
|
|
4932
4428
|
cwd: workflowCwd,
|
|
4429
|
+
...(workflowSessionRoot ? { sessionRoot: workflowSessionRoot } : {}),
|
|
4430
|
+
...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}),
|
|
4933
4431
|
pid: process.pid,
|
|
4934
4432
|
steps: [],
|
|
4935
4433
|
workflow: { trace: [], emits: [], console: [] },
|
|
4936
4434
|
runFanoutBudget: getRunFanoutBudgetSnapshot(workflowFanoutBudget),
|
|
4937
4435
|
};
|
|
4938
|
-
const appendWorkflowEvent = (event: Record<string, unknown>) =>
|
|
4436
|
+
const appendWorkflowEvent = (event: Record<string, unknown>) => {
|
|
4437
|
+
try {
|
|
4438
|
+
fs.appendFileSync(eventsPath, `${JSON.stringify({ ts: Date.now(), runId: workflowRunId, ...event })}\n`, "utf-8");
|
|
4439
|
+
} catch (error) {
|
|
4440
|
+
// The event log is a journal, not workflow truth. Callers append from
|
|
4441
|
+
// inside run-result handling, so losing an entry to a full disk or to a
|
|
4442
|
+
// transient Windows lock must not fail the run being recorded.
|
|
4443
|
+
console.error(`Failed to append async workflow event '${eventsPath}':`, error);
|
|
4444
|
+
}
|
|
4445
|
+
};
|
|
4939
4446
|
let indexedState: AsyncStatus["state"] | undefined;
|
|
4940
|
-
const
|
|
4447
|
+
const indexPersistence = createCapacityResilientJsonWriter({
|
|
4448
|
+
keepAlive: true,
|
|
4449
|
+
onSuccess: (_filePath, payload) => { indexedState = (payload as { state: AsyncStatus["state"] }).state; },
|
|
4450
|
+
onError: (error, filePath) => console.error(`Failed to update async workflow index '${filePath}':`, error),
|
|
4451
|
+
});
|
|
4452
|
+
const queueActiveRunIndex = (): void => {
|
|
4453
|
+
const state = status.state;
|
|
4454
|
+
if (indexedState === state && indexPersistence.pendingCount() === 0) return;
|
|
4455
|
+
indexPersistence.write(asyncDir, { state, toolCallId: status.toolCallId }, (_filePath, payload) => {
|
|
4456
|
+
const indexPayload = payload as { state: AsyncStatus["state"]; toolCallId?: string };
|
|
4457
|
+
updateActiveRunIndex(asyncDir, indexPayload.state, indexPayload.toolCallId, { retryCapacityErrors: true });
|
|
4458
|
+
});
|
|
4459
|
+
};
|
|
4460
|
+
const runPersistence = createCapacityResilientJsonWriter({
|
|
4461
|
+
keepAlive: true,
|
|
4462
|
+
onSuccess: (filePath) => { if (filePath === statusPath) queueActiveRunIndex(); },
|
|
4463
|
+
onError: (error, filePath) => console.error(`Failed to persist async workflow state '${filePath}':`, error),
|
|
4464
|
+
write: (filePath, payload) => filePath === resultPath
|
|
4465
|
+
? writeAsyncResultFile(filePath, payload as Record<string, unknown>)
|
|
4466
|
+
: writeAtomicJson(filePath, payload),
|
|
4467
|
+
});
|
|
4468
|
+
let initialPersistenceComplete = false;
|
|
4469
|
+
let persistClosed = false;
|
|
4470
|
+
let statusPersistenceDegraded = false;
|
|
4471
|
+
// Progress journalling runs from admit, launch, the launch and progress observers,
|
|
4472
|
+
// onTrace and onEmit -- all inside the promises a workflowScript awaits. Those
|
|
4473
|
+
// callers pass tolerateStatusWriteFailure so a transient lock on status.json cannot
|
|
4474
|
+
// mark a finished child failed or abort its still-running siblings. Initial and
|
|
4475
|
+
// terminal writes stay fail-fast on purpose: no child work is at risk by then, and
|
|
4476
|
+
// silently dropping a terminal write would leave status.json and the active-run
|
|
4477
|
+
// index pinned at "running" after the result already says complete.
|
|
4478
|
+
const persist = (options: { tolerateStatusWriteFailure?: boolean } = {}) => {
|
|
4479
|
+
if (persistClosed) return;
|
|
4480
|
+
const liveJob = deps.state.asyncJobs.get(workflowRunId);
|
|
4481
|
+
if (liveJob && (liveJob.status === "complete" || liveJob.status === "failed") && status.state !== "complete" && status.state !== "failed") return;
|
|
4941
4482
|
status.lastUpdate = Date.now();
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4483
|
+
if (!initialPersistenceComplete) {
|
|
4484
|
+
writeAtomicJson(statusPath, status);
|
|
4485
|
+
initialPersistenceComplete = true;
|
|
4486
|
+
queueActiveRunIndex();
|
|
4487
|
+
} else if (options.tolerateStatusWriteFailure) {
|
|
4488
|
+
try {
|
|
4489
|
+
runPersistence.write(statusPath, status);
|
|
4490
|
+
statusPersistenceDegraded = false;
|
|
4491
|
+
} catch (error) {
|
|
4492
|
+
const message = `Failed to persist async workflow state ${statusPath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
4493
|
+
console.error(message, error);
|
|
4494
|
+
if (!statusPersistenceDegraded) {
|
|
4495
|
+
statusPersistenceDegraded = true;
|
|
4496
|
+
try {
|
|
4497
|
+
appendWorkflowEvent({ type: "subagent.workflow.status_write_failed", error: message });
|
|
4498
|
+
} catch (eventError) {
|
|
4499
|
+
console.error(`Failed to record degraded status persistence for '${statusPath}':`, eventError);
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4502
|
+
}
|
|
4503
|
+
} else {
|
|
4504
|
+
runPersistence.write(statusPath, status);
|
|
4946
4505
|
}
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
job.currentStep = status.currentStep;
|
|
4506
|
+
if (liveJob) {
|
|
4507
|
+
liveJob.status = status.state;
|
|
4508
|
+
liveJob.updatedAt = status.lastUpdate;
|
|
4509
|
+
liveJob.activityState = status.activityState;
|
|
4510
|
+
liveJob.lastActivityAt = status.lastActivityAt;
|
|
4511
|
+
liveJob.currentTool = status.currentTool;
|
|
4512
|
+
liveJob.currentToolStartedAt = status.currentToolStartedAt;
|
|
4513
|
+
liveJob.currentPath = status.currentPath;
|
|
4514
|
+
liveJob.turnCount = status.turnCount;
|
|
4515
|
+
liveJob.toolCount = status.toolCount;
|
|
4516
|
+
liveJob.currentStep = status.currentStep;
|
|
4959
4517
|
if (status.steps) {
|
|
4960
|
-
|
|
4961
|
-
|
|
4518
|
+
liveJob.steps = status.steps.map((step, index) => ({ ...step, index }));
|
|
4519
|
+
liveJob.agents = status.steps.map((step) => step.agent);
|
|
4962
4520
|
} else {
|
|
4963
|
-
delete
|
|
4964
|
-
delete
|
|
4521
|
+
delete liveJob.steps;
|
|
4522
|
+
delete liveJob.agents;
|
|
4965
4523
|
}
|
|
4966
|
-
|
|
4524
|
+
liveJob.workflow = status.workflow;
|
|
4967
4525
|
}
|
|
4968
4526
|
};
|
|
4969
4527
|
const writeWorkflowResult = (payload: Record<string, unknown>): boolean => {
|
|
4970
4528
|
try {
|
|
4971
|
-
|
|
4529
|
+
runPersistence.write(resultPath, payload);
|
|
4972
4530
|
return true;
|
|
4973
4531
|
} catch (error) {
|
|
4974
4532
|
const message = `Failed to write async workflow result ${resultPath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -4998,20 +4556,33 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4998
4556
|
status.toolCount = toolCounts.length > 0 ? toolCounts.reduce((total, count) => total + count, 0) : undefined;
|
|
4999
4557
|
status.currentStep = runningSteps.length === 1 ? steps.indexOf(runningSteps[0]!) : undefined;
|
|
5000
4558
|
};
|
|
5001
|
-
const workflowJob: AsyncJobState = { asyncId: workflowRunId, asyncDir, toolCallId, cwd: workflowCwd, status: "running", sessionId: currentSessionId ?? undefined, mode: "workflow", agents: [], steps: [], startedAt, updatedAt: startedAt, ...(timeout !== undefined ? { timeoutMs: timeout, deadlineAt: startedAt + timeout } : {}), workflow: status.workflow };
|
|
4559
|
+
const workflowJob: AsyncJobState = { asyncId: workflowRunId, asyncDir, toolCallId, cwd: workflowCwd, ...(workflowSessionRoot ? { sessionRoot: workflowSessionRoot } : {}), status: "running", sessionId: currentSessionId ?? undefined, mode: "workflow", agents: [], steps: [], startedAt, updatedAt: startedAt, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), ...(timeout !== undefined ? { timeoutMs: timeout, deadlineAt: startedAt + timeout } : {}), workflow: status.workflow };
|
|
5002
4560
|
deps.state.asyncJobs.set(workflowRunId, workflowJob);
|
|
5003
4561
|
deps.state.fleetJobs ??= new Map();
|
|
5004
4562
|
deps.state.fleetJobs.set(workflowRunId, workflowJob);
|
|
5005
|
-
|
|
4563
|
+
try {
|
|
4564
|
+
persist();
|
|
4565
|
+
} catch (error) {
|
|
4566
|
+
deps.state.workflowControllers?.delete(workflowRunId);
|
|
4567
|
+
deps.state.asyncJobs.delete(workflowRunId);
|
|
4568
|
+
deps.state.fleetJobs?.delete(workflowRunId);
|
|
4569
|
+
workflowCapacity?.rollback();
|
|
4570
|
+
indexPersistence.dispose();
|
|
4571
|
+
return { content: [{ type: "text", text: `Failed to create async workflow storage: ${error instanceof Error ? error.message : String(error)}` }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4572
|
+
}
|
|
5006
4573
|
appendWorkflowEvent({ type: "subagent.workflow.started" });
|
|
5007
4574
|
const { workflowScript, async: _workflowAsync, chatProgress: _chatProgress, ...workflowRequest } = requestParams;
|
|
5008
4575
|
void Promise.resolve().then(async () => {
|
|
4576
|
+
const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
|
|
5009
4577
|
const workflowResults: SingleResult[] = [];
|
|
4578
|
+
const workflowChildRunIds = new Map<string, string>();
|
|
5010
4579
|
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;
|
|
5011
4580
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
|
|
5012
4581
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
|
|
5013
|
-
const workflowAggregateOutputPath = resolveWorkflowAggregateOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, workflowRunId)
|
|
4582
|
+
const workflowAggregateOutputPath = resolveWorkflowAggregateOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, workflowRunId));
|
|
5014
4583
|
const claimedOutputPaths = new Map<string, string>();
|
|
4584
|
+
const childOutputOverrides = new Map<string, string>();
|
|
4585
|
+
const producedChildOutputPaths = new Set<string>();
|
|
5015
4586
|
const workflowSteps = new Map<string, NonNullable<AsyncStatus["steps"]>[number]>();
|
|
5016
4587
|
let projectedTraceLength = 0;
|
|
5017
4588
|
let projectedTraceTail: NonNullable<Details["workflow"]>["trace"][number] | undefined;
|
|
@@ -5069,62 +4640,59 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5069
4640
|
status.steps?.push(step);
|
|
5070
4641
|
workflowSteps.set(entry.key, step);
|
|
5071
4642
|
}
|
|
4643
|
+
const projectedStep = workflowSteps.get(entry.key);
|
|
4644
|
+
if (entry.state === "stopped" && projectedStep) {
|
|
4645
|
+
appendWorkflowEvent({
|
|
4646
|
+
type: "subagent.child-status",
|
|
4647
|
+
version: 1,
|
|
4648
|
+
childId: entry.key,
|
|
4649
|
+
status: "stopped",
|
|
4650
|
+
reason: "user",
|
|
4651
|
+
source: "async",
|
|
4652
|
+
stepIndex: status.steps?.indexOf(projectedStep),
|
|
4653
|
+
agent: projectedStep.agent,
|
|
4654
|
+
...(projectedStep.runId ? { childRunId: projectedStep.runId } : {}),
|
|
4655
|
+
workflowKey: entry.key,
|
|
4656
|
+
...(projectedStep.phase ? { phase: projectedStep.phase } : {}),
|
|
4657
|
+
...(projectedStep.label ? { label: projectedStep.label } : {}),
|
|
4658
|
+
});
|
|
4659
|
+
}
|
|
5072
4660
|
}
|
|
5073
4661
|
projectedTraceLength = trace.length;
|
|
5074
4662
|
projectedTraceTail = trace.at(-1);
|
|
5075
4663
|
projectWorkflowActivity();
|
|
5076
|
-
persist();
|
|
4664
|
+
persist({ tolerateStatusWriteFailure: true });
|
|
5077
4665
|
appendWorkflowEvent({ type: "subagent.workflow.trace", trace });
|
|
5078
4666
|
};
|
|
5079
4667
|
try {
|
|
5080
|
-
const
|
|
5081
|
-
|
|
5082
|
-
let capReached = false;
|
|
5083
|
-
let stallReason: "fanout-limited" | "usage-budget" | "no-progress" | undefined;
|
|
5084
|
-
let workflow: Awaited<ReturnType<typeof runWorkflowScript>> | undefined;
|
|
5085
|
-
let roundProgress = { launched: 0, fanoutRejected: 0, usageBudgetBlocked: 0, stateWrites: 0 };
|
|
5086
|
-
while (true) {
|
|
5087
|
-
if (relaunched > 0) {
|
|
5088
|
-
workflowFanoutBudget = createRunFanoutBudget(`${workflowRunId}#${relaunched + 1}`, workflowFanoutBudget.limit);
|
|
5089
|
-
workflowSteps.clear();
|
|
5090
|
-
projectedTraceLength = 0;
|
|
5091
|
-
projectedTraceTail = undefined;
|
|
5092
|
-
status = { ...status, steps: [], currentStep: undefined, workflow: { trace: [], emits: [], console: [] } };
|
|
5093
|
-
}
|
|
5094
|
-
roundProgress = { launched: 0, fanoutRejected: 0, usageBudgetBlocked: 0, stateWrites: 0 };
|
|
5095
|
-
workflow = await runWorkflowScript({
|
|
5096
|
-
script: workflowScript,
|
|
4668
|
+
const workflow = await runWorkflowScript({
|
|
4669
|
+
script: workflowScript,
|
|
5097
4670
|
timeoutMs: timeout,
|
|
5098
4671
|
signal: controller.signal,
|
|
5099
|
-
|
|
5100
|
-
|
|
4672
|
+
registerStopChild: (stop) => {
|
|
4673
|
+
if (stop) deps.state.workflowChildStops?.set(workflowRunId, stop);
|
|
4674
|
+
else deps.state.workflowChildStops?.delete(workflowRunId);
|
|
4675
|
+
},
|
|
4676
|
+
...(workflowState ? { state: workflowState } : {}),
|
|
5101
4677
|
onTrace: updateTrace,
|
|
5102
4678
|
admit: (calls) => {
|
|
5103
|
-
const outputClaims = workflowChildOutputClaims({ ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: deps.discoverAgents, workflowAgentScope: workflowChildDefaults.agentScope, claimedOutputPaths, entries: calls });
|
|
4679
|
+
const outputClaims = workflowChildOutputClaims({ ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: deps.discoverAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, state: deps.state, claimedOutputPaths, entries: calls });
|
|
5104
4680
|
if (outputClaims.error) throw new Error(outputClaims.error);
|
|
5105
|
-
|
|
5106
|
-
status.runFanoutBudget = claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
|
|
5107
|
-
} catch (error) {
|
|
5108
|
-
if (error instanceof RunFanoutLimitError) roundProgress.fanoutRejected += calls.length;
|
|
5109
|
-
throw error;
|
|
5110
|
-
}
|
|
4681
|
+
status.runFanoutBudget = claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
|
|
5111
4682
|
if (outputClaims.claims) applyWorkflowChildOutputClaims(claimedOutputPaths, outputClaims.claims);
|
|
5112
|
-
|
|
4683
|
+
if (outputClaims.overrides) for (const [key, output] of outputClaims.overrides) childOutputOverrides.set(key, output);
|
|
4684
|
+
persist({ tolerateStatusWriteFailure: true });
|
|
5113
4685
|
},
|
|
5114
4686
|
onEmit: (emits) => {
|
|
5115
4687
|
// Each emit is validated at the host boundary in runWorkflowScript before onEmit fires.
|
|
5116
4688
|
status.workflow = { ...(status.workflow ?? { trace: [], console: [] }), emits };
|
|
5117
|
-
persist();
|
|
4689
|
+
persist({ tolerateStatusWriteFailure: true });
|
|
5118
4690
|
appendWorkflowEvent({ type: "subagent.workflow.emit", value: emits.at(-1) });
|
|
5119
4691
|
},
|
|
5120
4692
|
launch: async (key, childParams, workflowSignal, admission) => {
|
|
5121
|
-
if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
|
|
4693
|
+
if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."), childParams, deps.state);
|
|
5122
4694
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
5123
|
-
if (budgetState?.exhausted)
|
|
5124
|
-
roundProgress.usageBudgetBlocked += 1;
|
|
5125
|
-
return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
|
|
5126
|
-
}
|
|
5127
|
-
roundProgress.launched += 1;
|
|
4695
|
+
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)), childParams, deps.state);
|
|
5128
4696
|
const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
|
|
5129
4697
|
const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
|
|
5130
4698
|
recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
|
|
@@ -5136,7 +4704,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5136
4704
|
});
|
|
5137
4705
|
const result = await runMissionWorkflowChild(missionBinding, workflowRunId, key, childPhase, () => {
|
|
5138
4706
|
const childRequest = bindMissionWorkflowChildAsyncLaunch(
|
|
5139
|
-
{ ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams, parentWorkflowRunId: workflowRunId, workflowKey: key, ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: deps.discoverAgents, workflowAgentScope: workflowChildDefaults.agentScope, options: { missionDetached: detachWorkflowChildMissions, runFanoutBudget: workflowFanoutBudget } }), runFanoutAdmitted: admission.admitted },
|
|
4707
|
+
{ ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams, parentWorkflowRunId: workflowRunId, workflowKey: key, ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: deps.discoverAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, outputOverride: childOutputOverrides.get(key), options: { missionDetached: detachWorkflowChildMissions, runFanoutBudget: workflowFanoutBudget, parentDeadlineAt: workflowDeadlineAt, capabilityCeiling: workflowCapabilityCeiling } }), runFanoutAdmitted: admission.admitted },
|
|
5140
4708
|
missionBinding,
|
|
5141
4709
|
deps.asyncByDefault,
|
|
5142
4710
|
);
|
|
@@ -5147,7 +4715,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5147
4715
|
step.sessionFile = launch.sessionFile;
|
|
5148
4716
|
step.async = launch.async;
|
|
5149
4717
|
if (launch.runId) step.runId = launch.runId;
|
|
5150
|
-
persist();
|
|
4718
|
+
persist({ tolerateStatusWriteFailure: true });
|
|
5151
4719
|
}
|
|
5152
4720
|
recordMissionWorkflowChild(missionBinding, workflowRunId, key, { status: "running", agent: launch.agent, ...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}) });
|
|
5153
4721
|
});
|
|
@@ -5170,7 +4738,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5170
4738
|
step.thinking = progress.thinking;
|
|
5171
4739
|
step.error = progress.error;
|
|
5172
4740
|
projectWorkflowActivity();
|
|
5173
|
-
persist();
|
|
4741
|
+
persist({ tolerateStatusWriteFailure: true });
|
|
5174
4742
|
recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
|
|
5175
4743
|
status: step.status,
|
|
5176
4744
|
heartbeat: { status: step.status, ...(childPhase ? { phase: childPhase } : {}) },
|
|
@@ -5178,7 +4746,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5178
4746
|
}, ctx, preserveActiveSession);
|
|
5179
4747
|
});
|
|
5180
4748
|
workflowResults.push(...result.details.results);
|
|
5181
|
-
const
|
|
4749
|
+
for (const childResult of result.details.results) {
|
|
4750
|
+
if (childResult.savedOutputPath) producedChildOutputPaths.add(childResult.savedOutputPath);
|
|
4751
|
+
}
|
|
4752
|
+
const child = workflowChildResult(key, result, childParams, deps.state);
|
|
4753
|
+
if (child.runId) workflowChildRunIds.set(key, child.runId);
|
|
5182
4754
|
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
5183
4755
|
if (step) {
|
|
5184
4756
|
step.async = Boolean(result.details.asyncId || result.details.asyncDir);
|
|
@@ -5202,46 +4774,27 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5202
4774
|
return child;
|
|
5203
4775
|
},
|
|
5204
4776
|
status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession)),
|
|
4777
|
+
resolveResume: (reference) => resolveKeyedWorkflowResume(reference, deps.state),
|
|
4778
|
+
steer: (key, message, options, workflowSignal) => steerWorkflowChildByKey({ state: deps.state, workflowRunId, key, message, options, signal: workflowSignal, resolveRunId: () => workflowChildRunIds.get(key) }),
|
|
5205
4779
|
});
|
|
5206
|
-
const decision = resolveAutoRelaunchDecision(workflow?.value, relaunched, autoRelaunchCap, roundProgress);
|
|
5207
|
-
if (!decision.relaunch) {
|
|
5208
|
-
capReached = decision.capped;
|
|
5209
|
-
stallReason = decision.stallReason;
|
|
5210
|
-
break;
|
|
5211
|
-
}
|
|
5212
|
-
relaunched += 1;
|
|
5213
|
-
status = { ...status, workflowRelaunchCount: relaunched };
|
|
5214
|
-
persist();
|
|
5215
|
-
appendWorkflowEvent({ type: "subagent.workflow.relaunched", relaunched });
|
|
5216
|
-
}
|
|
5217
|
-
if (!workflow) throw new Error("Workflow script produced no result.");
|
|
5218
4780
|
const returnPreview = formatWorkflowValue(workflow.value).slice(0, 1_000);
|
|
5219
4781
|
const emitPreview = workflow.emits.length > 0 ? ` Emitted: ${workflow.emits.map(formatWorkflowValue).join(", ").slice(0, 1_000)}` : "";
|
|
5220
|
-
const
|
|
5221
|
-
const
|
|
4782
|
+
const summary = `Workflow completed with ${workflow.children.length} child run(s). Return: ${returnPreview}${emitPreview} Trace: ${workflow.trace.length} event(s).`;
|
|
4783
|
+
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, summary, producedChildOutputPaths);
|
|
4784
|
+
const resultSummary = appendWorkflowOutputWarning(summary, outputWarning);
|
|
5222
4785
|
const workflowUsage = sumResultsUsage(workflowResults);
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
: `Workflow auto-relaunch cap reached after ${relaunched} relaunch(es); the goal is not clean yet. The progress file is current; raise config.maxWorkflowAutoRelaunches or relaunch manually.`;
|
|
5231
|
-
status = compactOptional<AsyncStatus>({ ...status, state: "failed", error: relaunchCapError, endedAt: Date.now(), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console } });
|
|
5232
|
-
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, relaunchCapError);
|
|
5233
|
-
const resultSummary = appendWorkflowOutputWarning(relaunchCapError, outputWarning);
|
|
5234
|
-
if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: false, state: "failed", summary: resultSummary, error: relaunchCapError, 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 })) return;
|
|
5235
|
-
persist();
|
|
5236
|
-
appendWorkflowEvent({ type: "subagent.workflow.completed", state: "failed", error: status.error });
|
|
5237
|
-
} else {
|
|
5238
|
-
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, summary);
|
|
5239
|
-
const resultSummary = appendWorkflowOutputWarning(summary, outputWarning);
|
|
5240
|
-
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) };
|
|
5241
|
-
if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary: resultSummary, output: resultSummary, 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 })) return;
|
|
5242
|
-
persist();
|
|
5243
|
-
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(status.error ? { error: status.error } : {}) });
|
|
4786
|
+
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) };
|
|
4787
|
+
const receipt = terminalWorkflowReceipt(workflowRunId, "complete", workflow.children);
|
|
4788
|
+
let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
|
|
4789
|
+
try {
|
|
4790
|
+
workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
|
|
4791
|
+
} catch (receiptError) {
|
|
4792
|
+
appendWorkflowEvent({ type: "subagent.workflow.receipt_write_failed", error: `Failed to persist async workflow receipt: ${receiptError instanceof Error ? receiptError.message : String(receiptError)}` });
|
|
5244
4793
|
}
|
|
4794
|
+
if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary: resultSummary, output: resultSummary, 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.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, ...(workflowReceipt ? { workflowReceipt } : {}), asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
|
|
4795
|
+
persist();
|
|
4796
|
+
persistClosed = true;
|
|
4797
|
+
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(status.error ? { error: status.error } : {}) });
|
|
5245
4798
|
} catch (error) {
|
|
5246
4799
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
5247
4800
|
const stopped = controller.signal.aborted;
|
|
@@ -5251,18 +4804,41 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5251
4804
|
const state = stopped ? "stopped" : pauseForDetached ? "paused" : "failed";
|
|
5252
4805
|
for (const step of status.steps ?? []) {
|
|
5253
4806
|
if (step.workflowKey && detachedChildKeys.has(step.workflowKey)) {
|
|
4807
|
+
if (step.status === "completed" || step.status === "complete" || step.status === "failed") continue;
|
|
5254
4808
|
step.status = "paused";
|
|
5255
4809
|
step.activityState = "needs_attention";
|
|
4810
|
+
} else if (pauseForDetached && step.status === "running") {
|
|
4811
|
+
step.status = "stopped";
|
|
4812
|
+
step.stopped = true;
|
|
4813
|
+
delete step.activityState;
|
|
5256
4814
|
}
|
|
5257
4815
|
}
|
|
5258
4816
|
status = compactOptional<AsyncStatus>({ ...status, state, stopped: stopped || undefined, activityState: pauseForDetached ? "needs_attention" : undefined, error: error instanceof Error ? error.message : String(error), endedAt: Date.now(), workflow: { trace: partial.trace, emits: partial.emits, console: partial.console } });
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
4817
|
+
if (pauseForDetached) {
|
|
4818
|
+
const promoted = promotePausedWorkflowIfSettled(status);
|
|
4819
|
+
if (promoted) status = promoted;
|
|
4820
|
+
}
|
|
4821
|
+
const terminalSummary = status.state === "complete"
|
|
4822
|
+
? "Workflow completed after detached child finished."
|
|
4823
|
+
: status.error ?? (pauseForDetached ? "Workflow paused." : "Workflow failed.");
|
|
4824
|
+
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, terminalSummary, producedChildOutputPaths);
|
|
4825
|
+
const resultSummary = appendWorkflowOutputWarning(terminalSummary, outputWarning);
|
|
4826
|
+
const receiptState: WorkflowReceiptState = status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
|
|
4827
|
+
const receipt = terminalWorkflowReceipt(workflowRunId, receiptState, partial.children);
|
|
4828
|
+
let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
|
|
4829
|
+
try {
|
|
4830
|
+
workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
|
|
4831
|
+
} catch (receiptError) {
|
|
4832
|
+
appendWorkflowEvent({ type: "subagent.workflow.receipt_write_failed", error: `Failed to persist async workflow receipt: ${receiptError instanceof Error ? receiptError.message : String(receiptError)}` });
|
|
4833
|
+
}
|
|
4834
|
+
if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: status.state === "complete", state: status.state, summary: resultSummary, error: status.state === "complete" ? undefined : status.error, stopped: status.stopped, activityState: status.activityState, 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.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(child.detached && status.state !== "complete" ? { detached: true } : {}), ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, ...(workflowReceipt ? { workflowReceipt } : {}), asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
|
|
5262
4835
|
persist();
|
|
5263
|
-
|
|
4836
|
+
persistClosed = true;
|
|
4837
|
+
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(status.error ? { error: status.error } : {}), ...(status.activityState ? { activityState: status.activityState } : {}) });
|
|
5264
4838
|
} finally {
|
|
4839
|
+
persistClosed = true;
|
|
5265
4840
|
deps.state.workflowControllers?.delete(workflowRunId);
|
|
4841
|
+
deps.state.workflowChildStops?.delete(workflowRunId);
|
|
5266
4842
|
deps.state.activeAsyncCapacity = workflowCapacity?.reconcile(new Set(deps.state.workflowControllers?.keys() ?? []))
|
|
5267
4843
|
?? deps.state.activeAsyncCapacity;
|
|
5268
4844
|
}
|
|
@@ -5275,10 +4851,15 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5275
4851
|
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;
|
|
5276
4852
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
|
|
5277
4853
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
|
|
5278
|
-
const workflowAggregateOutputPath = resolveWorkflowAggregateOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, _id)
|
|
4854
|
+
const workflowAggregateOutputPath = resolveWorkflowAggregateOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, _id));
|
|
5279
4855
|
const claimedOutputPaths = new Map<string, string>();
|
|
4856
|
+
const childOutputOverrides = new Map<string, string>();
|
|
4857
|
+
const producedChildOutputPaths = new Set<string>();
|
|
5280
4858
|
const workflowResults: SingleResult[] = [];
|
|
4859
|
+
const workflowChildRunIds = new Map<string, string>();
|
|
5281
4860
|
let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [] };
|
|
4861
|
+
const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
|
|
4862
|
+
const workflowCapabilityCeiling = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(resolveCurrentSessionId(ctx.sessionManager)));
|
|
5282
4863
|
const sendWorkflowProgress = () => {
|
|
5283
4864
|
const update = workflowChatProgressUpdate(_id, chatProgress, liveWorkflow);
|
|
5284
4865
|
if (update) onUpdate?.(update);
|
|
@@ -5288,26 +4869,26 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5288
4869
|
script: requestParams.workflowScript,
|
|
5289
4870
|
timeoutMs: timeout,
|
|
5290
4871
|
signal,
|
|
5291
|
-
prompts: workflowPrompts,
|
|
5292
4872
|
...(workflowState ? { state: workflowState } : {}),
|
|
5293
4873
|
onTrace: (trace) => {
|
|
5294
4874
|
liveWorkflow = { ...liveWorkflow, trace };
|
|
5295
4875
|
sendWorkflowProgress();
|
|
5296
4876
|
},
|
|
5297
4877
|
admit: (calls) => {
|
|
5298
|
-
const outputClaims = workflowChildOutputClaims({ ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId: _id, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: deps.discoverAgents, workflowAgentScope: workflowChildDefaults.agentScope, claimedOutputPaths, entries: calls });
|
|
4878
|
+
const outputClaims = workflowChildOutputClaims({ ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId: _id, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: deps.discoverAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, state: deps.state, claimedOutputPaths, entries: calls });
|
|
5299
4879
|
if (outputClaims.error) throw new Error(outputClaims.error);
|
|
5300
4880
|
claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
|
|
5301
4881
|
if (outputClaims.claims) applyWorkflowChildOutputClaims(claimedOutputPaths, outputClaims.claims);
|
|
4882
|
+
if (outputClaims.overrides) for (const [key, output] of outputClaims.overrides) childOutputOverrides.set(key, output);
|
|
5302
4883
|
},
|
|
5303
4884
|
onEmit: (emits) => {
|
|
5304
4885
|
liveWorkflow = { ...liveWorkflow, emits };
|
|
5305
4886
|
sendWorkflowProgress();
|
|
5306
4887
|
},
|
|
5307
4888
|
launch: async (key, childParams, workflowSignal, admission) => {
|
|
5308
|
-
if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
|
|
4889
|
+
if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."), childParams, deps.state);
|
|
5309
4890
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
5310
|
-
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
|
|
4891
|
+
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)), childParams, deps.state);
|
|
5311
4892
|
const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
|
|
5312
4893
|
const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
|
|
5313
4894
|
recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
@@ -5319,7 +4900,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5319
4900
|
});
|
|
5320
4901
|
const result = await runMissionWorkflowChild(missionBinding, _id, key, childPhase, () => {
|
|
5321
4902
|
const childRequest = bindMissionWorkflowChildAsyncLaunch(
|
|
5322
|
-
{ ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams, parentWorkflowRunId: _id, workflowKey: key, ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: deps.discoverAgents, workflowAgentScope: workflowChildDefaults.agentScope, options: { missionDetached: detachWorkflowChildMissions, suppressRoutineResultIntercom: chatProgress.mode === "live-card", runFanoutBudget: workflowFanoutBudget } }), runFanoutAdmitted: admission.admitted },
|
|
4903
|
+
{ ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams, parentWorkflowRunId: _id, workflowKey: key, ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: deps.discoverAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, outputOverride: childOutputOverrides.get(key), options: { missionDetached: detachWorkflowChildMissions, suppressRoutineResultIntercom: chatProgress.mode === "live-card", runFanoutBudget: workflowFanoutBudget, parentDeadlineAt: workflowDeadlineAt, capabilityCeiling: workflowCapabilityCeiling } }), runFanoutAdmitted: admission.admitted },
|
|
5323
4904
|
missionBinding,
|
|
5324
4905
|
deps.asyncByDefault,
|
|
5325
4906
|
);
|
|
@@ -5339,8 +4920,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5339
4920
|
}, ctx, preserveActiveSession);
|
|
5340
4921
|
});
|
|
5341
4922
|
workflowResults.push(...result.details.results);
|
|
4923
|
+
for (const childResult of result.details.results) {
|
|
4924
|
+
if (childResult.savedOutputPath) producedChildOutputPaths.add(childResult.savedOutputPath);
|
|
4925
|
+
}
|
|
5342
4926
|
if (result.details.asyncDir && missionBinding) writeMissionAsyncBinding(result.details.asyncDir, missionBinding);
|
|
5343
|
-
const child = workflowChildResult(key, result);
|
|
4927
|
+
const child = workflowChildResult(key, result, childParams, deps.state);
|
|
4928
|
+
if (child.runId) workflowChildRunIds.set(key, child.runId);
|
|
5344
4929
|
const childStatus = missionWorkflowChildStatus(result);
|
|
5345
4930
|
recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
5346
4931
|
status: childStatus,
|
|
@@ -5354,18 +4939,21 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5354
4939
|
return child;
|
|
5355
4940
|
},
|
|
5356
4941
|
status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession)),
|
|
4942
|
+
resolveResume: (reference) => resolveKeyedWorkflowResume(reference, deps.state),
|
|
4943
|
+
steer: (key, message, options, workflowSignal) => steerWorkflowChildByKey({ state: deps.state, workflowRunId: _id, key, message, options, signal: workflowSignal, resolveRunId: () => workflowChildRunIds.get(key) }),
|
|
5357
4944
|
});
|
|
4945
|
+
const receipt = terminalWorkflowReceipt(_id, "complete", workflow.children);
|
|
5358
4946
|
const traceLines = workflow.trace.map((entry) => `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.durationMs !== undefined ? ` in ${entry.durationMs}ms` : ""}${entry.error ? ` — ${entry.error}` : ""}`);
|
|
5359
4947
|
const sections = ["Workflow completed.", `Return:\n${formatWorkflowValue(workflow.value)}`];
|
|
5360
4948
|
if (workflow.emits.length > 0) sections.push(`Emitted:\n${workflow.emits.map(formatWorkflowValue).join("\n")}`);
|
|
5361
4949
|
if (workflow.console.length > 0) sections.push(`Console:\n${workflow.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
|
|
5362
4950
|
if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
|
|
5363
4951
|
const workflowText = sections.join("\n\n");
|
|
5364
|
-
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, workflowText);
|
|
4952
|
+
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, workflowText, producedChildOutputPaths);
|
|
5365
4953
|
const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
|
|
5366
4954
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
5367
4955
|
content: [{ type: "text", text: displayText }],
|
|
5368
|
-
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 }),
|
|
4956
|
+
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, receipt }, chatProgress }),
|
|
5369
4957
|
}, workflowFanoutBudget));
|
|
5370
4958
|
} catch (error) {
|
|
5371
4959
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
@@ -5376,16 +4964,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5376
4964
|
if (partial.console.length > 0) sections.push(`Console:\n${partial.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
|
|
5377
4965
|
if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
|
|
5378
4966
|
const workflowText = sections.join("\n\n");
|
|
5379
|
-
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, workflowText);
|
|
4967
|
+
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, workflowText, producedChildOutputPaths);
|
|
5380
4968
|
const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
|
|
4969
|
+
const receipt = terminalWorkflowReceipt(_id, "failed", partial.children);
|
|
5381
4970
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
5382
4971
|
content: [{ type: "text", text: displayText }],
|
|
5383
4972
|
isError: true,
|
|
5384
|
-
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 }),
|
|
4973
|
+
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, receipt }, chatProgress }),
|
|
5385
4974
|
}, workflowFanoutBudget));
|
|
5386
4975
|
}
|
|
5387
4976
|
}
|
|
5388
|
-
const directParams =
|
|
4977
|
+
const directParams = requestParams;
|
|
5389
4978
|
const requestCwd = resolveRequestedCwd(ctx.cwd, directParams.cwd);
|
|
5390
4979
|
const paramsWithResolvedCwd = directParams.cwd === undefined ? directParams : { ...directParams, cwd: requestCwd };
|
|
5391
4980
|
const action = paramsWithResolvedCwd.action;
|
|
@@ -5434,7 +5023,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5434
5023
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
5435
5024
|
return { content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5436
5025
|
}
|
|
5437
|
-
|
|
5026
|
+
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
5027
|
+
return handleHerdrProjectPaneAction(action as (typeof HERDR_PROJECT_PANE_ACTIONS)[number], paramsWithResolvedCwd, { cwd: requestCwd, state: deps.state, signal });
|
|
5438
5028
|
}
|
|
5439
5029
|
if ((HERDR_INSPECTOR_ACTIONS as readonly string[]).includes(action)) {
|
|
5440
5030
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
@@ -5464,7 +5054,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5464
5054
|
...(currentSessionId ? { currentSessionId } : {}),
|
|
5465
5055
|
});
|
|
5466
5056
|
}
|
|
5467
|
-
const policyAction = action === "stop" ? "stopRun" : action === "steer" ? "steerRun" : undefined;
|
|
5057
|
+
const policyAction = action === "stop" ? "stopRun" : action === "steer" ? "steerRun" : action === "schedule.create" ? "scheduleCreate" : undefined;
|
|
5468
5058
|
if (policyAction) {
|
|
5469
5059
|
const decision = resolveAuthorityDecision({ action: policyAction, ...(deps.config.authorityPolicy === undefined ? {} : { policy: deps.config.authorityPolicy }) });
|
|
5470
5060
|
if (decision === "forbid") {
|
|
@@ -5476,6 +5066,16 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5476
5066
|
if (!confirmed) return { content: [{ type: "text", text: `Action '${action}' canceled; authority was not granted.` }], details: { mode: "management", results: [] } };
|
|
5477
5067
|
}
|
|
5478
5068
|
}
|
|
5069
|
+
if ((WATCHDOG_TOOL_ACTIONS as readonly string[]).includes(action)) {
|
|
5070
|
+
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
5071
|
+
return {
|
|
5072
|
+
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
5073
|
+
isError: true,
|
|
5074
|
+
details: { mode: "management" as const, results: [] },
|
|
5075
|
+
};
|
|
5076
|
+
}
|
|
5077
|
+
return handleWatchdogToolAction(action, paramsWithResolvedCwd, ctx, deps.watchdog);
|
|
5078
|
+
}
|
|
5479
5079
|
if (action === "refine" || action === "refine.show" || action === "refine.rollback") {
|
|
5480
5080
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
5481
5081
|
return {
|
|
@@ -5587,7 +5187,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5587
5187
|
}
|
|
5588
5188
|
if (action === "children.list") {
|
|
5589
5189
|
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
5590
|
-
const children = listRetainedChildren(DIRS.async, deps.state.
|
|
5190
|
+
const children = listRetainedChildren(DIRS.async, deps.state.currentSessionId);
|
|
5591
5191
|
return {
|
|
5592
5192
|
content: [{ type: "text", text: formatRetainedChildren(children) }],
|
|
5593
5193
|
details: { mode: "management", results: [] },
|
|
@@ -5621,7 +5221,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5621
5221
|
cwd: requestCwd,
|
|
5622
5222
|
config: deps.config,
|
|
5623
5223
|
state: deps.state,
|
|
5624
|
-
context: paramsWithResolvedCwd.context,
|
|
5224
|
+
context: paramsWithResolvedCwd.context === "profile" ? undefined : paramsWithResolvedCwd.context,
|
|
5625
5225
|
requestedSessionDir: paramsWithResolvedCwd.sessionDir,
|
|
5626
5226
|
currentSessionFile,
|
|
5627
5227
|
currentSessionId,
|
|
@@ -5692,61 +5292,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5692
5292
|
}
|
|
5693
5293
|
return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots })));
|
|
5694
5294
|
}
|
|
5695
|
-
|
|
5696
|
-
if (action === "approve-checkpoint" || action === "reject-checkpoint") {
|
|
5697
|
-
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
5698
|
-
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
5699
|
-
if (!targetRunId && !paramsWithResolvedCwd.dir) {
|
|
5700
|
-
return { content: [{ type: "text", text: `action='${action}' requires id or dir.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5701
|
-
}
|
|
5702
|
-
try {
|
|
5703
|
-
const resolved = targetRunId ? resolveSubagentRunId(targetRunId, omitUndefinedProperties({ state: deps.state, nested: nestedResolutionScopeForExecutor(deps) })) : undefined;
|
|
5704
|
-
const decision = action === "approve-checkpoint" ? "approved" : "rejected";
|
|
5705
|
-
if (resolved?.kind === "foreground") {
|
|
5706
|
-
const run = deps.state.foregroundRuns?.get(resolved.id);
|
|
5707
|
-
if (!run?.checkpoint || run.checkpoint.status !== "pending") {
|
|
5708
|
-
return { content: [{ type: "text", text: `Run '${resolved.id}' is not paused at an approval checkpoint.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5709
|
-
}
|
|
5710
|
-
if (deps.state.currentSessionId && run.sessionId !== deps.state.currentSessionId) {
|
|
5711
|
-
return { content: [{ type: "text", text: `Run '${resolved.id}' was not found in the active session.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5712
|
-
}
|
|
5713
|
-
const decidedAt = Date.now();
|
|
5714
|
-
const checkpoint: NonNullable<Details["checkpoint"]> = decision === "approved"
|
|
5715
|
-
? { ...run.checkpoint, status: "approved", approvedAt: decidedAt }
|
|
5716
|
-
: { ...run.checkpoint, status: "rejected", rejectedAt: decidedAt };
|
|
5717
|
-
run.checkpoint = checkpoint;
|
|
5718
|
-
run.updatedAt = decidedAt;
|
|
5719
|
-
return {
|
|
5720
|
-
content: [{ type: "text", text: `Checkpoint '${checkpoint.name}' ${decision} for foreground run ${resolved.id}.` }],
|
|
5721
|
-
details: { mode: "management", results: [], checkpoint },
|
|
5722
|
-
};
|
|
5723
|
-
}
|
|
5724
|
-
const location = paramsWithResolvedCwd.dir
|
|
5725
|
-
? resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results)
|
|
5726
|
-
: resolved?.kind === "async"
|
|
5727
|
-
? resolved.location
|
|
5728
|
-
: undefined;
|
|
5729
|
-
if (!location?.asyncDir) {
|
|
5730
|
-
return { content: [{ type: "text", text: `action='${action}' targets paused foreground or async checkpoints. No matching run found.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5731
|
-
}
|
|
5732
|
-
const status = readStatus(location.asyncDir);
|
|
5733
|
-
const runId = status?.runId ?? location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir);
|
|
5734
|
-
if (!status?.checkpoint || status.state !== "paused") {
|
|
5735
|
-
return { content: [{ type: "text", text: `Run '${runId}' is not paused at an approval checkpoint.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5736
|
-
}
|
|
5737
|
-
if (deps.state.currentSessionId && status.sessionId !== deps.state.currentSessionId) {
|
|
5738
|
-
return { content: [{ type: "text", text: `Run '${runId}' was not found in the active session.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5739
|
-
}
|
|
5740
|
-
deliverCheckpointDecisionRequest({ asyncDir: location.asyncDir, decision, source: "subagent-action", ...(paramsWithResolvedCwd.message ? { reason: paramsWithResolvedCwd.message } : {}) });
|
|
5741
|
-
return {
|
|
5742
|
-
content: [{ type: "text", text: `Checkpoint '${status.checkpoint.name}' ${decision} for run ${runId}.` }],
|
|
5743
|
-
details: { mode: "management", results: [], checkpoint: { ...status.checkpoint, status: decision } },
|
|
5744
|
-
};
|
|
5745
|
-
} catch (error) {
|
|
5746
|
-
const text = error instanceof Error ? error.message : String(error);
|
|
5747
|
-
return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
|
|
5748
|
-
}
|
|
5749
|
-
}
|
|
5750
5295
|
if (action === "resume") {
|
|
5751
5296
|
return resumeAsyncRun(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel, signal }));
|
|
5752
5297
|
}
|
|
@@ -5844,6 +5389,26 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5844
5389
|
if (action === "append-step") {
|
|
5845
5390
|
return appendStepToAsyncChain(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel }));
|
|
5846
5391
|
}
|
|
5392
|
+
if (action.startsWith("schedule.")) {
|
|
5393
|
+
if (!isScheduledRunAction(action)) {
|
|
5394
|
+
return { content: [{ type: "text", text: unknownSubagentActionMessage(action) }], isError: true, details: { mode: "management", results: [] } };
|
|
5395
|
+
}
|
|
5396
|
+
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
5397
|
+
return {
|
|
5398
|
+
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
5399
|
+
isError: true,
|
|
5400
|
+
details: { mode: "management", results: [] },
|
|
5401
|
+
};
|
|
5402
|
+
}
|
|
5403
|
+
if (!deps.handleScheduledRunAction) {
|
|
5404
|
+
return {
|
|
5405
|
+
content: [{ type: "text", text: `Action '${action}' is not available in this subagent context.` }],
|
|
5406
|
+
isError: true,
|
|
5407
|
+
details: { mode: "management", results: [] },
|
|
5408
|
+
};
|
|
5409
|
+
}
|
|
5410
|
+
return deps.handleScheduledRunAction(paramsWithResolvedCwd, ctx);
|
|
5411
|
+
}
|
|
5847
5412
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
5848
5413
|
return {
|
|
5849
5414
|
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
@@ -5870,6 +5435,40 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5870
5435
|
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
5871
5436
|
const workflowController = targetRunId ? deps.state.workflowControllers?.get(targetRunId) : undefined;
|
|
5872
5437
|
if (workflowController) {
|
|
5438
|
+
if (paramsWithResolvedCwd.childId !== undefined) {
|
|
5439
|
+
const workflowRunId = targetRunId!;
|
|
5440
|
+
const asyncJob = deps.state.asyncJobs.get(workflowRunId);
|
|
5441
|
+
if (!asyncJob?.asyncDir) return { content: [{ type: "text", text: `Status file not found for async workflow '${workflowRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5442
|
+
const status = readStatus(asyncJob.asyncDir);
|
|
5443
|
+
if (!status) return { content: [{ type: "text", text: `Status file not found for async workflow '${workflowRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5444
|
+
const resolution = resolveAsyncStatusChild(status, paramsWithResolvedCwd.childId);
|
|
5445
|
+
if (!resolution.ok) return { content: [{ type: "text", text: resolution.message }], isError: true, details: { mode: "management", results: [] } };
|
|
5446
|
+
if (!isStoppableAsyncStatusStep(resolution.child.step)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in async run '${targetRunId}' is ${resolution.child.step.status}; stop only supports pending or running children.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5447
|
+
const stopChild = deps.state.workflowChildStops?.get(targetRunId!);
|
|
5448
|
+
if (!stopChild) return { content: [{ type: "text", text: `Workflow ${targetRunId} child stop is unavailable in this extension runtime.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5449
|
+
if (!stopChild(resolution.child.id)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in workflow ${workflowRunId} is not available to stop.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5450
|
+
try {
|
|
5451
|
+
fs.appendFileSync(path.join(asyncJob.asyncDir, "events.jsonl"), `${JSON.stringify({
|
|
5452
|
+
type: "subagent.child-status",
|
|
5453
|
+
version: 1,
|
|
5454
|
+
runId: workflowRunId,
|
|
5455
|
+
childId: resolution.child.id,
|
|
5456
|
+
status: "stopping",
|
|
5457
|
+
ts: Date.now(),
|
|
5458
|
+
reason: "user",
|
|
5459
|
+
source: "async",
|
|
5460
|
+
stepIndex: resolution.child.index,
|
|
5461
|
+
agent: resolution.child.step.agent,
|
|
5462
|
+
...(resolution.child.step.runId ? { childRunId: resolution.child.step.runId } : {}),
|
|
5463
|
+
...(resolution.child.step.workflowKey ? { workflowKey: resolution.child.step.workflowKey } : {}),
|
|
5464
|
+
...(resolution.child.step.phase ? { phase: resolution.child.step.phase } : {}),
|
|
5465
|
+
...(resolution.child.step.label ? { label: resolution.child.step.label } : {}),
|
|
5466
|
+
} satisfies SubagentChildStatusEvent)}\n`, "utf-8");
|
|
5467
|
+
} catch (error) {
|
|
5468
|
+
console.error(`Failed to append child status event for workflow ${workflowRunId}:`, error);
|
|
5469
|
+
}
|
|
5470
|
+
return { content: [{ type: "text", text: `Stop requested for child ${resolution.child.id} in async workflow ${workflowRunId}.` }], details: { mode: "management", results: [] } };
|
|
5471
|
+
}
|
|
5873
5472
|
workflowController.abort(new Error("Workflow stopped by user."));
|
|
5874
5473
|
return { content: [{ type: "text", text: `Stop requested for async workflow ${targetRunId}.` }], details: { mode: "management", results: [] } };
|
|
5875
5474
|
}
|
|
@@ -5881,7 +5480,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5881
5480
|
if (existingStatus?.mode === "workflow" && existingStatus.state === "running") {
|
|
5882
5481
|
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: [] } };
|
|
5883
5482
|
}
|
|
5884
|
-
const stopResult = stopAsyncRun(deps.state, location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir), deps.kill, location);
|
|
5483
|
+
const stopResult = stopAsyncRun(deps.state, location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir), deps.kill, location, paramsWithResolvedCwd.childId);
|
|
5885
5484
|
return stopResult ?? { content: [{ type: "text", text: `No running or queued async run was found for '${targetRunId ?? paramsWithResolvedCwd.dir}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5886
5485
|
} catch (error) {
|
|
5887
5486
|
const text = error instanceof Error ? error.message : String(error);
|
|
@@ -5908,6 +5507,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5908
5507
|
resolved?.kind === "async" ? resolved.id : targetRunId,
|
|
5909
5508
|
deps.kill,
|
|
5910
5509
|
resolved?.kind === "async" ? resolved.location : undefined,
|
|
5510
|
+
paramsWithResolvedCwd.childId,
|
|
5911
5511
|
);
|
|
5912
5512
|
if (stopResult) return stopResult;
|
|
5913
5513
|
return {
|
|
@@ -5970,6 +5570,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5970
5570
|
cwd: requestCwd,
|
|
5971
5571
|
config: deps.config,
|
|
5972
5572
|
currentSessionId: deps.state.currentSessionId ?? ctx.sessionManager.getSessionId() ?? undefined,
|
|
5573
|
+
runtimeAgentOwner: deps.pi,
|
|
5973
5574
|
});
|
|
5974
5575
|
}
|
|
5975
5576
|
|
|
@@ -6015,26 +5616,37 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6015
5616
|
const parentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
|
|
6016
5617
|
const discovered = deps.discoverAgents(effectiveCwd, scope);
|
|
6017
5618
|
const discoveredAgents = discovered.agents;
|
|
6018
|
-
const canonicalParams = canonicalizeExecutionParams(effectiveParams, discoveredAgents);
|
|
5619
|
+
const canonicalParams = canonicalizeExecutionParams(effectiveParams, discoveredAgents, discovered.agentDiagnostics);
|
|
6019
5620
|
if (canonicalParams.error) return buildRequestedModeError(effectiveParams, canonicalParams.error);
|
|
6020
5621
|
effectiveParams = canonicalParams.params!;
|
|
6021
5622
|
const modelScope = discovered.modelScope;
|
|
6022
5623
|
effectiveParams = applySingleAgentLaunchDefaults(effectiveParams, discoveredAgents);
|
|
6023
5624
|
const turnBudget = resolveTurnBudgetConfig(effectiveParams.turnBudget ?? deps.config.turnBudget);
|
|
6024
5625
|
if (turnBudget.error) return buildRequestedModeError(effectiveParams, turnBudget.error);
|
|
6025
|
-
|
|
5626
|
+
// An agent-level defaultContext is a preference, unlike an explicit request.
|
|
5627
|
+
// Prefer fork only when the parent session is persisted and has a current leaf;
|
|
5628
|
+
// otherwise use fresh immediately instead of launching a guaranteed-to-fail fork.
|
|
5629
|
+
// Explicit context:"fork" remains strict.
|
|
5630
|
+
const contextPolicyResult = resolveAgentDefaultContextPolicy(
|
|
5631
|
+
effectiveParams,
|
|
5632
|
+
discoveredAgents,
|
|
5633
|
+
deps.config.defaultSubagentContext,
|
|
5634
|
+
canPreferFork(ctx.sessionManager),
|
|
5635
|
+
);
|
|
5636
|
+
if ("error" in contextPolicyResult) return buildRequestedModeError(effectiveParams, contextPolicyResult.error);
|
|
5637
|
+
const contextPolicy = contextPolicyResult;
|
|
6026
5638
|
effectiveParams = contextPolicy.params;
|
|
6027
5639
|
const sessionName = resolveIntercomSessionTarget(deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
|
|
6028
5640
|
const intercomBridge = resolveIntercomBridge({
|
|
6029
5641
|
config: deps.config.intercomBridge,
|
|
6030
5642
|
override: effectiveParams.intercomBridge,
|
|
6031
|
-
context: effectiveParams.context
|
|
5643
|
+
context: effectiveParams.context === "fresh" || effectiveParams.context === "fork"
|
|
5644
|
+
? effectiveParams.context
|
|
5645
|
+
: contextPolicy.usesFork ? "fork" : undefined,
|
|
6032
5646
|
orchestratorTarget: sessionName,
|
|
6033
5647
|
});
|
|
6034
|
-
const agents = intercomBridge
|
|
6035
|
-
|
|
6036
|
-
: discoveredAgents;
|
|
6037
|
-
const runId = randomUUID().slice(0, 8);
|
|
5648
|
+
const agents = applyScopedIntercomBridgeToAgents(discoveredAgents, intercomBridge, contextPolicy);
|
|
5649
|
+
const runId = randomUUID();
|
|
6038
5650
|
const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
|
|
6039
5651
|
const nestedParentAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
|
|
6040
5652
|
const shareEnabled = effectiveParams.share === true;
|
|
@@ -6068,30 +5680,33 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6068
5680
|
|
|
6069
5681
|
let forkSessionFileForIndex: (idx?: number) => string | undefined = () => undefined;
|
|
6070
5682
|
let forkThinkingOverrideForIndex: (idx?: number) => AgentConfig["thinking"] | undefined = () => undefined;
|
|
6071
|
-
let prepareForkThinking = (_agentName: string, _index: number, _modelOverride?: string): void => {};
|
|
5683
|
+
let prepareForkThinking = (_agentName: string, _index: number, _modelOverride?: string, _modelOverrideFromParent?: boolean): void => {};
|
|
6072
5684
|
const forkThinkingRequirements = new Map<number, boolean>();
|
|
6073
5685
|
const forkThinkingDowngrades = new Map<number, string>();
|
|
6074
5686
|
try {
|
|
6075
5687
|
const forkAvailableModels = contextPolicy.usesFork ? ctx.modelRegistry.getAvailable().map(toModelInfo) : [];
|
|
6076
5688
|
const parentModel = requestParentModel;
|
|
6077
|
-
prepareForkThinking = (agentName, index, modelOverride) => {
|
|
5689
|
+
prepareForkThinking = (agentName, index, modelOverride, modelOverrideFromParent) => {
|
|
6078
5690
|
const agentConfig = agents.find((agent) => agent.name === agentName);
|
|
6079
|
-
if (agentConfig?.runner?.type === "external-cli") {
|
|
5691
|
+
if (agentConfig?.runner?.type === "external-cli" || agentConfig?.runner?.type === "external-job") {
|
|
6080
5692
|
forkThinkingRequirements.set(index, true);
|
|
6081
5693
|
return;
|
|
6082
5694
|
}
|
|
6083
|
-
const primaryModel =
|
|
6084
|
-
modelOverride
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
5695
|
+
const primaryModel = modelOverrideFromParent
|
|
5696
|
+
? modelOverride
|
|
5697
|
+
: resolveEffectiveSubagentModel(
|
|
5698
|
+
modelOverride,
|
|
5699
|
+
agentConfig?.model,
|
|
5700
|
+
parentModel,
|
|
5701
|
+
forkAvailableModels,
|
|
5702
|
+
agentConfig?.modelProvider ?? parentModel?.provider,
|
|
5703
|
+
);
|
|
6090
5704
|
const candidates = buildModelCandidates(
|
|
6091
5705
|
primaryModel,
|
|
6092
5706
|
agentConfig?.fallbackModels,
|
|
6093
5707
|
forkAvailableModels,
|
|
6094
|
-
parentModel?.provider,
|
|
5708
|
+
agentConfig?.modelProvider ?? parentModel?.provider,
|
|
5709
|
+
{ primaryModelFromParent: modelOverrideFromParent ?? inheritsParentModel(modelOverride, agentConfig?.model, parentModel) },
|
|
6095
5710
|
);
|
|
6096
5711
|
forkThinkingRequirements.set(
|
|
6097
5712
|
index,
|
|
@@ -6107,9 +5722,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6107
5722
|
} catch (error) {
|
|
6108
5723
|
return toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary);
|
|
6109
5724
|
}
|
|
6110
|
-
const requestedAsync = effectiveParams.async ?? deps.asyncByDefault;
|
|
6111
|
-
const backgroundRequestedWhileClarifying = (hasChain || hasTasks) && requestedAsync && effectiveParams.clarify === true;
|
|
6112
|
-
const effectiveAsync = requestedAsync && effectiveParams.clarify !== true;
|
|
6113
5725
|
const selectedAgentNames = hasSingle
|
|
6114
5726
|
? [effectiveParams.agent!]
|
|
6115
5727
|
: hasTasks
|
|
@@ -6117,9 +5729,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6117
5729
|
: (effectiveParams.chain ?? []).flatMap((step) => getStepAgents(step as ChainStep));
|
|
6118
5730
|
const externalAgent = selectedAgentNames
|
|
6119
5731
|
.map((name) => agents.find((agent) => agent.name === name))
|
|
6120
|
-
.find((agent) => agent?.runner?.type === "external-cli");
|
|
5732
|
+
.find((agent) => agent?.runner?.type === "external-cli" || agent?.runner?.type === "external-job");
|
|
5733
|
+
const externalAsyncRequired = Boolean(externalAgent) && effectiveParams.async === undefined && effectiveParams.clarify !== true && effectiveParams.foregroundOnly !== true;
|
|
5734
|
+
const requestedAsync = externalAsyncRequired ? true : effectiveParams.async ?? deps.asyncByDefault;
|
|
5735
|
+
const backgroundRequestedWhileClarifying = (hasChain || hasTasks) && requestedAsync && effectiveParams.clarify === true;
|
|
5736
|
+
const effectiveAsync = requestedAsync && effectiveParams.clarify !== true;
|
|
6121
5737
|
if (externalAgent && (!effectiveAsync || effectiveParams.foregroundOnly === true)) {
|
|
6122
|
-
return buildRequestedModeError(effectiveParams, `Agent '${externalAgent.name}' uses runner.type='
|
|
5738
|
+
return buildRequestedModeError(effectiveParams, `Agent '${externalAgent.name}' uses runner.type='${externalAgent.runner?.type}', which currently supports async/background execution only. Omit async or pass async:true; clarify and foregroundOnly are unsupported.`);
|
|
6123
5739
|
}
|
|
6124
5740
|
const foregroundTimeout = resolveSingleAgentLaunchTimeout(
|
|
6125
5741
|
effectiveParams,
|
|
@@ -6203,20 +5819,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6203
5819
|
}
|
|
6204
5820
|
const sessionDirForIndex = (idx?: number) =>
|
|
6205
5821
|
path.join(sessionRoot, `run-${idx ?? 0}`);
|
|
6206
|
-
const forkSessionFileForTask: ForkSessionFileForTask = (agentName, idx = 0, modelOverride) => {
|
|
5822
|
+
const forkSessionFileForTask: ForkSessionFileForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent) => {
|
|
6207
5823
|
if (!shouldForkAgent(contextPolicy, agentName)) return undefined;
|
|
6208
|
-
prepareForkThinking(agentName, idx, modelOverride);
|
|
5824
|
+
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent);
|
|
6209
5825
|
return forkSessionFileForIndex(idx);
|
|
6210
5826
|
};
|
|
6211
|
-
const forkThinkingOverrideForTask: ForkThinkingOverrideForTask = (agentName, idx = 0, modelOverride) => {
|
|
5827
|
+
const forkThinkingOverrideForTask: ForkThinkingOverrideForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent) => {
|
|
6212
5828
|
if (!shouldForkAgent(contextPolicy, agentName)) return delegatedThinkingOverride;
|
|
6213
|
-
prepareForkThinking(agentName, idx, modelOverride);
|
|
5829
|
+
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent);
|
|
6214
5830
|
const override = forkThinkingOverrideForIndex(idx);
|
|
6215
5831
|
if (override === "off") forkThinkingDowngrades.set(idx, agentName);
|
|
6216
5832
|
return override ?? delegatedThinkingOverride;
|
|
6217
5833
|
};
|
|
6218
|
-
const childSessionFileForTask: ForkSessionFileForTask = (agentName, idx, modelOverride) =>
|
|
6219
|
-
forkSessionFileForTask(agentName, idx, modelOverride) ?? path.join(sessionDirForIndex(idx), "session.jsonl");
|
|
5834
|
+
const childSessionFileForTask: ForkSessionFileForTask = (agentName, idx, modelOverride, modelOverrideFromParent) =>
|
|
5835
|
+
forkSessionFileForTask(agentName, idx, modelOverride, modelOverrideFromParent) ?? path.join(sessionDirForIndex(idx), "session.jsonl");
|
|
6220
5836
|
const childSessionFileForIndex = (idx?: number) =>
|
|
6221
5837
|
path.join(sessionDirForIndex(idx), "session.jsonl");
|
|
6222
5838
|
try {
|
|
@@ -6322,7 +5938,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6322
5938
|
parentModel: requestParentModel,
|
|
6323
5939
|
parentSessionId: requestSessionId,
|
|
6324
5940
|
parentPiSessionId: requestPiSessionId,
|
|
6325
|
-
capabilityCeiling: resolveCurrentSubagentCapabilityCeiling(requestSessionId),
|
|
5941
|
+
capabilityCeiling: intersectSubagentCapabilityCeilings(effectiveParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(requestSessionId)),
|
|
6326
5942
|
runFanoutBudget,
|
|
6327
5943
|
topLevelAsyncCapacityEligible,
|
|
6328
5944
|
activeAsyncCapacity,
|
|
@@ -6398,6 +6014,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6398
6014
|
};
|
|
6399
6015
|
deps.state.foregroundControls.set(runId, foregroundControl);
|
|
6400
6016
|
deps.state.lastForegroundControlId = runId;
|
|
6017
|
+
deps.activateSupervisorTransport?.();
|
|
6018
|
+
deps.refreshResultDelivery?.();
|
|
6401
6019
|
}
|
|
6402
6020
|
|
|
6403
6021
|
const writeNestedForegroundEvent = (type: "subagent.nested.started" | "subagent.nested.completed", result?: AgentToolResult<Details>): void => {
|
|
@@ -6431,7 +6049,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6431
6049
|
startedLaunches = selectedAgentNames.map((agent) => ({ agent }));
|
|
6432
6050
|
}
|
|
6433
6051
|
const agentsForSummary = startedLaunches.map((launch) => launch.agent);
|
|
6434
|
-
const leafIntercomTarget =
|
|
6052
|
+
const leafIntercomTarget = agentsForSummary[0] && intercomBridgeAppliesToAgent(intercomBridge, contextPolicy, agentsForSummary[0])
|
|
6435
6053
|
? resolveSubagentIntercomTarget(runId, agentsForSummary[0], 0)
|
|
6436
6054
|
: undefined;
|
|
6437
6055
|
try {
|
|
@@ -6504,7 +6122,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6504
6122
|
workflowLaunchObserver(launch);
|
|
6505
6123
|
}
|
|
6506
6124
|
}
|
|
6507
|
-
const asyncResult = runAsyncPath(execData, deps);
|
|
6125
|
+
const asyncResult = await runAsyncPath(execData, deps);
|
|
6508
6126
|
if (asyncResult) {
|
|
6509
6127
|
asyncLaunchFailed = asyncResult.isError === true;
|
|
6510
6128
|
return attachMission(withRunFanoutBudget(withResolvedContext(withForkThinkingNotes(asyncResult, forkThinkingDowngrades), contextPolicy.contextSummary), runFanoutBudget));
|
|
@@ -6514,16 +6132,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6514
6132
|
nestedForegroundStarted = true;
|
|
6515
6133
|
}
|
|
6516
6134
|
const runFanoutAnnotateContent = !delegatedExecution;
|
|
6517
|
-
if (hasChain && effectiveParams.chain) {
|
|
6518
|
-
const result = await runChainPath(execData, deps);
|
|
6519
|
-
writeNestedForegroundEvent("subagent.nested.completed", result);
|
|
6520
|
-
return attachMission(withRunFanoutBudget(withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary), runFanoutBudget, { annotateContent: runFanoutAnnotateContent }));
|
|
6521
|
-
}
|
|
6522
|
-
if (hasTasks && effectiveParams.tasks) {
|
|
6523
|
-
const result = await runParallelPath(execData, deps);
|
|
6524
|
-
writeNestedForegroundEvent("subagent.nested.completed", result);
|
|
6525
|
-
return attachMission(withRunFanoutBudget(withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary), runFanoutBudget, { annotateContent: runFanoutAnnotateContent }));
|
|
6526
|
-
}
|
|
6527
6135
|
if (hasSingle) {
|
|
6528
6136
|
const result = await runSinglePath(execData, deps);
|
|
6529
6137
|
writeNestedForegroundEvent("subagent.nested.completed", result);
|
|
@@ -6580,7 +6188,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6580
6188
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
6581
6189
|
ctx: ExtensionContext,
|
|
6582
6190
|
): Promise<AgentToolResult<Details>> => {
|
|
6583
|
-
const normalized = normalizePublicSubagentExecution(params);
|
|
6191
|
+
const normalized = normalizePublicSubagentExecution(params, { asyncByDefault: deps.asyncByDefault });
|
|
6584
6192
|
if (!normalized.ok) {
|
|
6585
6193
|
return Promise.resolve({ content: [{ type: "text", text: normalized.error }], isError: true, details: { mode: normalized.mode, results: [] } });
|
|
6586
6194
|
}
|
|
@@ -6609,5 +6217,23 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6609
6217
|
return execute(id, delegatedParams, signal, onUpdate, ctx);
|
|
6610
6218
|
};
|
|
6611
6219
|
|
|
6612
|
-
|
|
6220
|
+
const executeScheduled = (
|
|
6221
|
+
id: string,
|
|
6222
|
+
params: SubagentParamsLike,
|
|
6223
|
+
signal: AbortSignal,
|
|
6224
|
+
ctx: ExtensionContext,
|
|
6225
|
+
) => {
|
|
6226
|
+
const ownerSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
6227
|
+
let ownerExecutor = scheduledOwnerExecutors.get(ownerSessionId);
|
|
6228
|
+
if (!ownerExecutor) {
|
|
6229
|
+
ownerExecutor = createSubagentExecutor({
|
|
6230
|
+
...deps,
|
|
6231
|
+
state: createScheduledOwnerState(deps.state, ownerSessionId, ctx),
|
|
6232
|
+
});
|
|
6233
|
+
scheduledOwnerExecutors.set(ownerSessionId, ownerExecutor);
|
|
6234
|
+
}
|
|
6235
|
+
return ownerExecutor.execute(id, params, signal, undefined, ctx);
|
|
6236
|
+
};
|
|
6237
|
+
|
|
6238
|
+
return { execute: executeWithSingleDispatchGuard, executePublic, executeDelegated, executeScheduled };
|
|
6613
6239
|
}
|