@selesai/code 0.13.0 → 0.13.2
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 +22 -0
- package/dist/core/agent-session.d.ts +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/sdk.d.ts +2 -2
- package/dist/core/sdk.js +1 -1
- package/dist/core/system-prompt.d.ts +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/extensions/package.json +1 -2
- package/dist/extensions/pi-hermes-memory/CHANGELOG.md +371 -0
- package/dist/extensions/pi-hermes-memory/LICENSE +21 -0
- package/dist/extensions/pi-hermes-memory/README.md +643 -0
- package/dist/extensions/pi-hermes-memory/docs/0.1/TASKS.md +197 -0
- package/dist/extensions/pi-hermes-memory/docs/0.2/PLAN.md +290 -0
- package/dist/extensions/pi-hermes-memory/docs/0.2/TASKS.md +134 -0
- package/dist/extensions/pi-hermes-memory/docs/0.2/TEST-PLAN.md +216 -0
- package/dist/extensions/pi-hermes-memory/docs/0.3/PLAN.md +330 -0
- package/dist/extensions/pi-hermes-memory/docs/0.3/TASKS.md +125 -0
- package/dist/extensions/pi-hermes-memory/docs/0.4/PLAN.md +160 -0
- package/dist/extensions/pi-hermes-memory/docs/0.4/TASKS.md +113 -0
- package/dist/extensions/pi-hermes-memory/docs/0.6/CHANGELOG.md +57 -0
- package/dist/extensions/pi-hermes-memory/docs/0.6/PLAN.md +202 -0
- package/dist/extensions/pi-hermes-memory/docs/0.6/TASKS.md +144 -0
- package/dist/extensions/pi-hermes-memory/docs/0.7/PLAN.md +349 -0
- package/dist/extensions/pi-hermes-memory/docs/0.7/TASKS.md +110 -0
- package/dist/extensions/pi-hermes-memory/docs/PUBLISHING.md +149 -0
- package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +468 -0
- package/dist/extensions/pi-hermes-memory/docs/images/memory-architecture.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/images/pi-logo.svg +22 -0
- package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.png +0 -0
- package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.svg +19 -0
- package/dist/extensions/pi-hermes-memory/docs/images/security-flow.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/images/session-lifecycle.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/images/source-architecture.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/memory-architecture.mmd +49 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/security-flow.mmd +40 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/session-lifecycle.mmd +56 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/source-architecture.mmd +54 -0
- package/dist/extensions/pi-hermes-memory/package-lock.json +3799 -0
- package/dist/extensions/pi-hermes-memory/package.json +66 -0
- package/dist/extensions/pi-hermes-memory/scripts/check-min-sdk.mjs +99 -0
- package/dist/extensions/pi-hermes-memory/scripts/ensure-dev.mjs +57 -0
- package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +6 -0
- package/dist/extensions/pi-hermes-memory/src/config.ts +178 -0
- package/dist/extensions/pi-hermes-memory/src/constants.ts +424 -0
- package/dist/extensions/pi-hermes-memory/src/extension-root-migration.ts +639 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +401 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +297 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/child-process-watchdog.mjs +90 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +300 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +86 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +78 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/interview.ts +37 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/learn-memory.ts +186 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/message-parts.ts +27 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/pi-child-process.ts +485 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/preview-context.ts +105 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +506 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +145 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +151 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/session-live-index.ts +94 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +1334 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/standing-pin.ts +111 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/switch-project.ts +75 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +266 -0
- package/dist/extensions/pi-hermes-memory/src/index.ts +378 -0
- package/dist/extensions/pi-hermes-memory/src/lifecycle-timing.ts +54 -0
- package/dist/extensions/pi-hermes-memory/src/paths.ts +61 -0
- package/dist/extensions/pi-hermes-memory/src/project-context.ts +13 -0
- package/dist/extensions/pi-hermes-memory/src/project-memory-migration.ts +93 -0
- package/dist/extensions/pi-hermes-memory/src/project.ts +157 -0
- package/dist/extensions/pi-hermes-memory/src/prompt-context.ts +55 -0
- package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +375 -0
- package/dist/extensions/pi-hermes-memory/src/store/canonical-storage-path.ts +54 -0
- package/dist/extensions/pi-hermes-memory/src/store/content-scanner.ts +101 -0
- package/dist/extensions/pi-hermes-memory/src/store/db.ts +1180 -0
- package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +101 -0
- package/dist/extensions/pi-hermes-memory/src/store/markdown-mutation-lock.ts +38 -0
- package/dist/extensions/pi-hermes-memory/src/store/memory-lookup.ts +24 -0
- package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +1191 -0
- package/dist/extensions/pi-hermes-memory/src/store/recovery-maintenance.ts +66 -0
- package/dist/extensions/pi-hermes-memory/src/store/schema.ts +118 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-anchor-search.ts +472 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +511 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-parser.ts +213 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +234 -0
- package/dist/extensions/pi-hermes-memory/src/store/skill-store.ts +950 -0
- package/dist/extensions/pi-hermes-memory/src/store/skill-utils.ts +133 -0
- package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +1002 -0
- package/dist/extensions/pi-hermes-memory/src/store/sqlite-native.ts +241 -0
- package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +247 -0
- package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +95 -0
- package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +469 -0
- package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +246 -0
- package/dist/extensions/pi-hermes-memory/src/tools/shared-output-view.ts +170 -0
- package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +333 -0
- package/dist/extensions/pi-hermes-memory/src/tools/tool-result-views.ts +105 -0
- package/dist/extensions/pi-hermes-memory/src/types.ts +214 -0
- package/dist/extensions/pi-hermes-memory/tests/config.test.ts +434 -0
- package/dist/extensions/pi-hermes-memory/tests/extension-root-migration.test.ts +368 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +984 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +950 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/correction-detector.test.ts +671 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +157 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/interview.test.ts +127 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/pi-child-process.test.ts +905 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/preview-context.test.ts +152 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/prompt-context.test.ts +166 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/resources-discover.test.ts +71 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +614 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +192 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +677 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-live-index.test.ts +225 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +827 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/standing-pin.test.ts +134 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +491 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +199 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/thinking-max.test.ts +64 -0
- package/dist/extensions/pi-hermes-memory/tests/index.test.ts +30 -0
- package/dist/extensions/pi-hermes-memory/tests/integration/flow.test.ts +126 -0
- package/dist/extensions/pi-hermes-memory/tests/lifecycle-timing.test.ts +76 -0
- package/dist/extensions/pi-hermes-memory/tests/paths.test.ts +23 -0
- package/dist/extensions/pi-hermes-memory/tests/project-memory-migration.test.ts +80 -0
- package/dist/extensions/pi-hermes-memory/tests/project-rebinding.test.ts +115 -0
- package/dist/extensions/pi-hermes-memory/tests/project.test.ts +120 -0
- package/dist/extensions/pi-hermes-memory/tests/run-all-timeout.test.ts +107 -0
- package/dist/extensions/pi-hermes-memory/tests/run-all.sh +45 -0
- package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-coordinator.test.ts +381 -0
- package/dist/extensions/pi-hermes-memory/tests/store/content-scanner.test.ts +388 -0
- package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +1046 -0
- package/dist/extensions/pi-hermes-memory/tests/store/markdown-mutation-lock.test.ts +34 -0
- package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +2150 -0
- package/dist/extensions/pi-hermes-memory/tests/store/recovery-maintenance.test.ts +170 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-anchor-search.test.ts +210 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +643 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-parser.test.ts +282 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +287 -0
- package/dist/extensions/pi-hermes-memory/tests/store/skill-store.test.ts +678 -0
- package/dist/extensions/pi-hermes-memory/tests/store/skill-utils.test.ts +21 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-lazy-load.test.ts +78 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +543 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +164 -0
- package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +171 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +84 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +709 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/session-search-tool.test.ts +345 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/shared-output-view.test.ts +362 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +478 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/tool-result-renderer-wiring.test.ts +40 -0
- package/dist/extensions/pi-hermes-memory/tsconfig.json +19 -0
- package/dist/extensions/pi-rewind-hook/README.md +4 -1
- package/dist/extensions/pi-rewind-hook/index.ts +151 -21
- package/dist/extensions/pi-rewind-hook/package.json +5 -3
- package/dist/extensions/pi-subagents/.oxlintrc.json +43 -0
- package/dist/extensions/pi-subagents/AGENTS.md +5 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +157 -0
- package/dist/extensions/pi-subagents/README.md +12 -6
- package/dist/extensions/pi-subagents/VISION.md +81 -0
- package/dist/extensions/pi-subagents/agents/claude-code-writer.md +15 -0
- package/dist/extensions/pi-subagents/agents/claude-code.md +15 -0
- package/dist/extensions/pi-subagents/agents/codex-exec-writer.md +15 -0
- package/dist/extensions/pi-subagents/agents/codex-exec.md +15 -0
- package/dist/extensions/pi-subagents/agents/cursor-agent-writer.md +14 -0
- package/dist/extensions/pi-subagents/agents/cursor-agent.md +14 -0
- package/dist/extensions/pi-subagents/agents/reviewer.md +1 -3
- package/dist/extensions/pi-subagents/docs/agents.md +488 -0
- package/dist/extensions/pi-subagents/docs/configuration.md +46 -9
- package/dist/extensions/pi-subagents/docs/extension-api.md +42 -3
- package/dist/extensions/pi-subagents/docs/models.md +27 -4
- package/dist/extensions/pi-subagents/docs/observability.md +10 -9
- package/dist/extensions/pi-subagents/docs/tool-reference.md +102 -6
- package/dist/extensions/pi-subagents/docs/workflows.md +135 -0
- package/dist/extensions/pi-subagents/package-lock.json +389 -2
- package/dist/extensions/pi-subagents/package.json +3 -1
- package/dist/extensions/pi-subagents/prompts/review-loop.md +2 -2
- package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +3 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +7 -3
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +26 -5
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -22
- package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +4 -4
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +9 -2
- package/dist/extensions/pi-subagents/src/agents/agents.ts +322 -75
- package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +6 -0
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-events.ts +70 -0
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +27 -20
- package/dist/extensions/pi-subagents/src/api/agents.ts +10 -5
- package/dist/extensions/pi-subagents/src/api/background-work.ts +5 -1
- package/dist/extensions/pi-subagents/src/api/delegation.ts +0 -7
- package/dist/extensions/pi-subagents/src/api/preflight.ts +34 -12
- package/dist/extensions/pi-subagents/src/extension/config.ts +70 -0
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -3
- package/dist/extensions/pi-subagents/src/extension/index.ts +83 -30
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +44 -15
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +55 -19
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +41 -15
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -31
- package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -1
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -10
- package/dist/extensions/pi-subagents/src/inspectors/herdr/session-roots-codec.ts +42 -0
- package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +51 -7
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +2 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +5 -6
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +77 -10
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +176 -53
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +19 -12
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +29 -27
- package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +20 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +23 -261
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +73 -7
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +8 -3
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +74 -8
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +37 -21
- package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +8 -8
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +184 -9
- package/dist/extensions/pi-subagents/src/runs/background/result-delivery-ownership.ts +45 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +28 -14
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +38 -15
- package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +50 -13
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +93 -8
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +10 -4
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -14
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +549 -377
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +58 -10
- package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +26 -1
- package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +23 -9
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +9 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +204 -129
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +9 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +23 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +773 -311
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +140 -115
- package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +119 -0
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +463 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +19 -4
- package/dist/extensions/pi-subagents/src/runs/shared/child-launch-plan.ts +151 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +21 -7
- package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +129 -0
- package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +129 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +89 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +5 -4
- package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +114 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +167 -0
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +122 -0
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +348 -55
- package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +5 -5
- package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +230 -0
- package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +105 -0
- package/dist/extensions/pi-subagents/src/runs/shared/launch-cwd.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-config-sources.ts +422 -0
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
- package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +69 -7
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +47 -6
- package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +7 -2
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +9 -3
- package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +9 -5
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +419 -7
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +15 -1
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +81 -8
- package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +13 -0
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +21 -1
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +44 -4
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +98 -13
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +847 -0
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +18 -0
- package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +46 -0
- package/dist/extensions/pi-subagents/src/shared/extension-context.ts +24 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +21 -0
- package/dist/extensions/pi-subagents/src/shared/formatters.ts +18 -3
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +5 -1
- package/dist/extensions/pi-subagents/src/shared/pruned-fork.ts +450 -0
- package/dist/extensions/pi-subagents/src/shared/session-file-trust.ts +19 -0
- package/dist/extensions/pi-subagents/src/shared/session-tokens.ts +14 -3
- package/dist/extensions/pi-subagents/src/shared/settings.ts +19 -105
- package/dist/extensions/pi-subagents/src/shared/shortcuts.ts +17 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +359 -40
- package/dist/extensions/pi-subagents/src/shared/utils.ts +41 -84
- package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +116 -0
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -9
- package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +0 -4
- package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -2
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +378 -88
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +22 -11
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +3 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +147 -79
- package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +11 -5
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +39 -18
- package/dist/extensions/pi-subagents/src/tui/render.ts +369 -46
- package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +1 -1
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +6 -3
- package/dist/extensions/pi-subagents/src/workflows/host-command.ts +230 -0
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +740 -58
- package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +121 -0
- package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +270 -0
- package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +195 -6
- package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +246 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1114 -260
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +150 -3
- package/dist/extensions/pi-subagents/test/integration/claude-code-smoke.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/integration/claude-code-writer-smoke.test.ts +55 -0
- package/dist/extensions/pi-subagents/test/integration/codex-exec-smoke.test.ts +53 -0
- package/dist/extensions/pi-subagents/test/integration/codex-exec-writer-smoke.test.ts +57 -0
- package/dist/extensions/pi-subagents/test/integration/cursor-agent-smoke.test.ts +59 -0
- package/dist/extensions/pi-subagents/test/integration/cursor-agent-writer-smoke.test.ts +62 -0
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +57 -1
- package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +4 -3
- package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +54 -4
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +482 -4
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +157 -9
- package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +5 -5
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1508 -291
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +423 -1
- package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/support/cursor-smoke-inputs.ts +51 -0
- package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +5 -5
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +6 -6
- package/dist/extensions/pi-subagents/test/unit/abort-recovery.test.ts +152 -0
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +48 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +184 -6
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +66 -6
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +77 -0
- package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/anti-slop-oxlint.test.ts +86 -0
- package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +101 -1
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +121 -0
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +14 -7
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +53 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +190 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +42 -0
- package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +8 -4
- package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +78 -1
- package/dist/extensions/pi-subagents/test/unit/child-launch-plan.test.ts +94 -0
- package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/child-session-name.test.ts +50 -0
- package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +245 -0
- package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +192 -0
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +150 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +259 -0
- package/dist/extensions/pi-subagents/test/unit/cursor-smoke-inputs.test.ts +77 -0
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +1 -4
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/extension-context.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +223 -0
- package/dist/extensions/pi-subagents/test/unit/fast-mode-extension.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +208 -2
- package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +29 -1
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +110 -1
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
- package/dist/extensions/pi-subagents/test/unit/formatters.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +18 -9
- package/dist/extensions/pi-subagents/test/unit/herdr-session-roots-codec.test.ts +63 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +136 -6
- package/dist/extensions/pi-subagents/test/unit/host-command.test.ts +156 -0
- package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +98 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +178 -26
- package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +2 -1
- package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +138 -0
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +91 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +72 -7
- package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +20 -2
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +129 -1
- package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +4 -0
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +358 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +574 -21
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +109 -1
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +71 -1
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/pruned-fork.test.ts +214 -0
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +29 -8
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +53 -0
- package/dist/extensions/pi-subagents/test/unit/result-delivery-ownership.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +101 -3
- package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +10 -8
- package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +71 -1
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +41 -7
- package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +136 -2
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +137 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +33 -12
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +504 -1
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +12 -1
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +3 -5
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +5 -6
- package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +0 -8
- package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +5 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +157 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +63 -4
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +27 -23
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +44 -0
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +7 -5
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +106 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +484 -9
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +38 -4
- package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +155 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +318 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +365 -0
- package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +349 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +2 -7
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/index.ts +13 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/rules/no-service-constructor-imports.ts +52 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/index.ts +41 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-chained-type-assertions.ts +77 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-conditional-empty-object-spread.ts +49 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-known-value-widening.ts +247 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-module-mocking.ts +91 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-object-parameters.ts +126 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-apply.ts +28 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-get.ts +28 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-runtime-typeof.ts +67 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-shape-in-symbol-names.ts +39 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-parameters.ts +83 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-returns.ts +115 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-type-aliases.ts +72 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unsafe-dictionary-type.ts +134 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-widen-then-assert.ts +366 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/require-safety-comment-for-type-assertion.ts +62 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/dictionary-types.ts +502 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/lexical-type-parameters.ts +60 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/reflect-method.ts +35 -0
- package/dist/extensions/pi-tool-display/tests/capabilities.test.ts +1 -1
- package/dist/extensions/pi-tool-display/tests/settings-inspector.test.ts +2 -2
- package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +1 -1
- package/dist/modes/interactive/interactive-mode.js +7 -7
- package/dist/skills/workflow/SKILL.md +65 -50
- package/package.json +112 -110
- package/dist/extensions/enable-readonly-tools.test.ts +0 -63
- package/dist/extensions/enable-readonly-tools.ts +0 -24
- package/dist/extensions/model-prompt-injector/config.json +0 -16
- package/dist/extensions/model-prompt-injector/index.test.ts +0 -102
- package/dist/extensions/model-prompt-injector/index.ts +0 -148
- package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +0 -98
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +0 -250
|
@@ -3,7 +3,7 @@ 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 { findBlockingAgentDiagnostic, resolveAgentName, type AgentConfig, type AgentDiscoveryDiagnostic, type AgentScope } from "../../agents/agents.ts";
|
|
6
|
+
import { discoverAgents, findBlockingAgentDiagnostic, formatUnknownAgentError, resolveAgentName, unknownAgentDiagnosticContext, type AgentConfig, type AgentDiscoveryDiagnostic, type AgentScope, type UnknownAgentDiagnosticContext } 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
9
|
import { createCapacityResilientJsonWriter } from "../../shared/capacity-resilient-json.ts";
|
|
@@ -13,13 +13,13 @@ import {
|
|
|
13
13
|
beginForegroundChild,
|
|
14
14
|
finishForegroundChild,
|
|
15
15
|
foregroundSchedulingSettled,
|
|
16
|
-
retainForegroundSchedulingOwner,
|
|
17
16
|
settleForegroundSchedulingOwner,
|
|
18
17
|
updateForegroundChild,
|
|
19
18
|
} from "./foreground-control.ts";
|
|
20
19
|
import { getLivePromptAudit, rewritePromptWithGuidance, updateLiveEffectivePrompt } from "./prompt-audit.ts";
|
|
21
20
|
import { persistForegroundRunHistory, MAX_REMEMBERED_FOREGROUND_RUNS } from "./foreground-history.ts";
|
|
22
21
|
import { resolveExecutionAgentScope } from "../../agents/agent-scope.ts";
|
|
22
|
+
import { preflightLaunchCwd } from "../shared/launch-cwd.ts";
|
|
23
23
|
import { handleManagementAction } from "../../agents/agent-management.ts";
|
|
24
24
|
import { handleRefinementAction } from "../../agents/agent-refinements.ts";
|
|
25
25
|
import { buildDoctorReport } from "../../extension/doctor.ts";
|
|
@@ -28,45 +28,36 @@ import { normalizePublicSubagentExecution } from "../../extension/public-executi
|
|
|
28
28
|
import { runSync } from "./execution.ts";
|
|
29
29
|
import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
|
|
30
30
|
import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
|
|
31
|
-
import { buildModelCandidates, inheritsParentModel, normalizeParentModel, resolveEffectiveSubagentModel,
|
|
31
|
+
import { buildModelCandidates, inheritsParentModel, normalizeParentModel, resolveEffectiveSubagentModel, type ParentModel } from "../shared/model-fallback.ts";
|
|
32
32
|
import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.ts";
|
|
33
33
|
import { resolveModelScopesForAgent, type ModelScopeConfig } from "../shared/model-scope.ts";
|
|
34
|
-
import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
|
|
35
34
|
import { recordRun } from "../shared/run-history.ts";
|
|
36
35
|
import {
|
|
37
|
-
buildChainInstructions,
|
|
38
|
-
writeInitialProgressFile,
|
|
39
36
|
getStepAgents,
|
|
40
37
|
isParallelStep,
|
|
41
38
|
isDynamicParallelStep,
|
|
42
|
-
resolveChainPath,
|
|
43
39
|
resolveExistingReadPaths,
|
|
44
|
-
resolveStepBehavior,
|
|
45
|
-
suppressProgressForReadOnlyTask,
|
|
46
|
-
taskDisallowsFileUpdates,
|
|
47
40
|
type ChainStep,
|
|
48
41
|
type DynamicParallelStep,
|
|
49
42
|
type ParallelStep,
|
|
50
43
|
type ParallelTaskItem,
|
|
51
|
-
type ResolvedStepBehavior,
|
|
52
44
|
type SequentialStep,
|
|
53
|
-
type StepOverrides,
|
|
54
45
|
} from "../../shared/settings.ts";
|
|
55
|
-
import {
|
|
46
|
+
import { normalizeSkillInput } from "../../agents/skills.ts";
|
|
56
47
|
import { buildAsyncRunnerSteps, DEFAULT_ASYNC_TIMEOUT_MS, executeAsyncChain, executeAsyncSingle, formatAsyncStartedMessage, isAsyncAvailable, workflowAwaitedAsyncResultPath } from "../background/async-execution.ts";
|
|
57
48
|
import { updateActiveRunIndex } from "../background/active-run-index.ts";
|
|
58
49
|
import { steeringReceipt } from "../background/steering.ts";
|
|
59
|
-
import { acquireActiveAsyncCapacity, ActiveAsyncCapacityError, getActiveAsyncCapacitySnapshot, resolveMaxActiveAsyncRunsPerSession, transferActiveAsyncCapacity, type ActiveAsyncCapacityHandle } from "../background/active-async-capacity.ts";
|
|
60
|
-
import { isScheduledRunAction
|
|
50
|
+
import { acquireActiveAsyncCapacity, ActiveAsyncCapacityError, getActiveAsyncCapacitySnapshot, resolveAbandonedSlotReleaseAfterMs, resolveMaxActiveAsyncRunsPerSession, transferActiveAsyncCapacity, type ActiveAsyncCapacityHandle } from "../background/active-async-capacity.ts";
|
|
51
|
+
import { isScheduledRunAction } from "../background/scheduled-runs.ts";
|
|
61
52
|
import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
|
|
62
53
|
import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
|
|
63
54
|
import { normalizeGateAcceptance, validateExecutionAcceptance } from "../shared/acceptance.ts";
|
|
64
55
|
import { canPreferFork, createForkContextResolver, forkedChildRequiresThinkingOff, resolveSubagentLaunchContext } from "../../shared/fork-context.ts";
|
|
56
|
+
import { createPrunedForkSessionWriter } from "../../shared/pruned-fork.ts";
|
|
65
57
|
import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
|
|
66
58
|
import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
|
|
67
59
|
import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBridge, resolveIntercomSessionTarget, resolveSubagentIntercomTarget, type IntercomBridgeState } from "../../intercom/intercom-bridge.ts";
|
|
68
60
|
import { formatControlIntercomMessage, formatControlNoticeMessage, resolveControlConfig, shouldNotifyControlEvent } from "../shared/subagent-control.ts";
|
|
69
|
-
import { resolveTurnBudgetConfig } from "../shared/turn-budget.ts";
|
|
70
61
|
import { formatSpawnBudget, getSpawnBudgetSnapshot, grantSpawnBudget, preflightSpawnBudget, preflightSpawnBudgetGrant, reserveSpawnBudget } from "../shared/spawn-budget.ts";
|
|
71
62
|
import { claimRunFanoutBatch, claimRunFanoutBatchWithCommit, createRunFanoutBudget, decodeRunFanoutBudgetDescriptor, formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetDescriptor, RunFanoutLimitError, RUN_FANOUT_BUDGET_ENV, writeRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.ts";
|
|
72
63
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
@@ -74,12 +65,11 @@ import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig
|
|
|
74
65
|
import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
75
66
|
import { isAgentContractV1 } from "../shared/agent-contract.ts";
|
|
76
67
|
import { normalizeExtensionBindings, type ExtensionBindings } from "../shared/extension-bindings.ts";
|
|
77
|
-
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
78
|
-
import { cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
79
|
-
import { compactForegroundDetails, getSingleResultOutput,
|
|
68
|
+
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, outputPathMappingFromTask, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
69
|
+
import { assertJsonSchemaObject, cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
70
|
+
import { compactForegroundDetails, getSingleResultOutput, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage, toAgentToolUsage } from "../../shared/utils.ts";
|
|
80
71
|
import { createTaskMutationArbiter } from "../shared/llm-intent-arbiter.ts";
|
|
81
|
-
import {
|
|
82
|
-
import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup, writePendingParallelHandoff } from "../shared/parallel-handoff.ts";
|
|
72
|
+
import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, formatStoredParallelHandoffCleanup, parallelHandoffPath, readParallelHandoffManifest, recordParallelHandoffMerge, recordParallelHandoffSupersession, writeParallelHandoffGroup, writePendingParallelHandoff } from "../shared/parallel-handoff.ts";
|
|
83
73
|
import { summarizeContextModes, type ContextMode, type ContextSummary } from "../shared/context-mode.ts";
|
|
84
74
|
import {
|
|
85
75
|
attachNestedChildrenToResultChildren,
|
|
@@ -113,6 +103,7 @@ import { isStoppableAsyncStatusStep, resolveAsyncStatusChild } from "../shared/c
|
|
|
113
103
|
import { inspectSubagentStatus } from "../background/run-status.ts";
|
|
114
104
|
import { getExternalJobProvider } from "../../api/external-job-provider.ts";
|
|
115
105
|
import { externalJobFollowUpRequestDigest, externalJobFollowUpRequestId, externalJobFollowUpRunId, externalJobPromptDigest, externalJobStableJson } from "../shared/external-job-runner.ts";
|
|
106
|
+
import { externalCliReceiptMetadata, normalizeExternalCliRunnerStatus } from "../shared/external-cli-contract.ts";
|
|
116
107
|
import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.ts";
|
|
117
108
|
import { handleMissionAction, MISSION_ACTIONS } from "../../missions/actions.ts";
|
|
118
109
|
import { attachMissionToLaunchResult, prepareMissionLaunch, writeMissionAsyncBinding, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
|
|
@@ -122,18 +113,23 @@ import { createMissionWorkflowState } from "../../missions/workflow-state.ts";
|
|
|
122
113
|
import { resolveAuthorityDecision } from "../../policy/authority.ts";
|
|
123
114
|
import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspectors/herdr/actions.ts";
|
|
124
115
|
import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
|
|
125
|
-
import { previewSimpleWorkflowRun, runWorkflowScript, WorkflowScriptError, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
|
|
116
|
+
import { previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
|
|
117
|
+
import { executeWorkflowHostCommand, resolveWorkflowHostOutputClaimPath, type WorkflowHostCommandParams, type WorkflowHostCommandResult } from "../../workflows/host-command.ts";
|
|
126
118
|
import { buildWorkflowReceipt, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt, type WorkflowReceipt, type WorkflowReceiptState } from "../../workflows/workflow-receipt.ts";
|
|
119
|
+
import { upsertHostStep, validHostStepNodes } from "../shared/host-step-status.ts";
|
|
120
|
+
import { assertWorkflowLaneKey, normalizeWorkflowLaneMetadata } from "../shared/lane-metadata.ts";
|
|
121
|
+
import { parseWorkflowChildSummary, workflowChildSummary } from "../../workflows/workflow-child-summary.ts";
|
|
127
122
|
import { resolveWorkflowChatProgress, type WorkflowChatProgressProjection } from "../../workflows/chat-progress.ts";
|
|
123
|
+
import { annotateWorkflowPreflightTrace, formatWorkflowPreflight, formatWorkflowPreflightWarnings, normalizeWorkflowPreflight, workflowPreflightWarnings } from "../../workflows/workflow-preflight.ts";
|
|
124
|
+
import { claimWorkflowChildPermit, validateWorkflowChildPermitRoot, type WorkflowChildPermit, type WorkflowChildPermitContext } from "../../shared/workflow-child-permit.ts";
|
|
128
125
|
import {
|
|
129
126
|
cleanupWorktrees,
|
|
130
127
|
createWorktrees,
|
|
131
128
|
diffWorktrees,
|
|
132
|
-
findWorktreeTaskCwdConflict,
|
|
133
129
|
formatWorktreeDiffSummary,
|
|
134
|
-
formatWorktreeTaskCwdConflict,
|
|
135
130
|
type WorktreeSetup,
|
|
136
131
|
} from "../shared/worktree.ts";
|
|
132
|
+
import { createWorktreeCleanupPlan, formatWorktreeCleanupPlan } from "../shared/worktree-cleanup-plan.ts";
|
|
137
133
|
import {
|
|
138
134
|
type AgentProgress,
|
|
139
135
|
type AsyncJobState,
|
|
@@ -146,23 +142,26 @@ import {
|
|
|
146
142
|
type ControlEvent,
|
|
147
143
|
type Details,
|
|
148
144
|
type ExtensionConfig,
|
|
145
|
+
type ForegroundResumeChild,
|
|
149
146
|
type ForegroundRunControl,
|
|
147
|
+
type HostStepNodeV1,
|
|
150
148
|
type IntercomBridgeConfig,
|
|
151
149
|
type IntercomEventBus,
|
|
152
150
|
type JsonSchemaObject,
|
|
153
151
|
type MaxOutputConfig,
|
|
154
152
|
type NestedRouteInfo,
|
|
155
153
|
type NestedRunSummary,
|
|
154
|
+
type OutputMode,
|
|
156
155
|
type ResolvedControlConfig,
|
|
157
|
-
type ResolvedTurnBudget,
|
|
158
156
|
type ResolvedToolBudget,
|
|
159
157
|
type RunFanoutBudgetDescriptor,
|
|
160
158
|
type SteeringRecoveryDescriptor,
|
|
161
159
|
type SingleResult,
|
|
162
160
|
type SubagentChildStatusEvent,
|
|
163
161
|
type ToolBudgetConfig,
|
|
164
|
-
type
|
|
162
|
+
type Usage,
|
|
165
163
|
type UsageBudgetConfig,
|
|
164
|
+
type WorkflowTerminalOutcome,
|
|
166
165
|
type SubagentRunMode,
|
|
167
166
|
type SubagentState,
|
|
168
167
|
DIRS,
|
|
@@ -173,18 +172,21 @@ import {
|
|
|
173
172
|
SUBAGENT_CONTROL_INTERCOM_EVENT,
|
|
174
173
|
SUBAGENT_FOREGROUND_COMPLETE_EVENT,
|
|
175
174
|
checkSubagentDepth,
|
|
176
|
-
resolveTopLevelParallelConcurrency,
|
|
177
|
-
resolveTopLevelParallelMaxTasks,
|
|
178
175
|
resolveChildMaxSubagentDepth,
|
|
179
176
|
resolveCurrentMaxSubagentDepth,
|
|
180
177
|
resolveMaxSubagentSpawnsPerRun,
|
|
181
178
|
wrapForkTask,
|
|
182
179
|
type ScheduleOrigin,
|
|
183
180
|
} from "../../shared/types.ts";
|
|
181
|
+
import { deriveChildSessionName } from "../../shared/child-session-name.ts";
|
|
184
182
|
|
|
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"]);
|
|
183
|
+
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", "worktree.cleanup", "lane.recordMerge", "lane.recordSupersession", "refine", "refine.rollback", "dismiss", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
|
|
186
184
|
const DESTRUCTIVE_MANAGEMENT_ACTIONS = new Set(["delete", "eject", "disable", "reset", "mission.close", "worktree.discard", "refine.rollback", "inspector.close", "project.close", "stop", "interrupt", "schedule.delete"]);
|
|
187
185
|
|
|
186
|
+
function resolveSteerDeliveryMode(mode: SubagentParamsLike["mode"]): SteerDeliveryMode | undefined {
|
|
187
|
+
return mode === "steer" || mode === "follow_up" || mode === "auto" ? mode : undefined;
|
|
188
|
+
}
|
|
189
|
+
|
|
188
190
|
function editDistance(left: string, right: string): number {
|
|
189
191
|
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
190
192
|
for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
|
|
@@ -280,6 +282,9 @@ export interface SubagentParamsLike {
|
|
|
280
282
|
runId?: string;
|
|
281
283
|
dir?: string;
|
|
282
284
|
handoffPath?: string;
|
|
285
|
+
laneId?: string;
|
|
286
|
+
merge?: unknown;
|
|
287
|
+
supersession?: unknown;
|
|
283
288
|
index?: number;
|
|
284
289
|
childId?: string;
|
|
285
290
|
view?: "fleet" | "transcript";
|
|
@@ -296,19 +301,27 @@ export interface SubagentParamsLike {
|
|
|
296
301
|
resume?: string;
|
|
297
302
|
message?: string;
|
|
298
303
|
steeringRecovery?: boolean;
|
|
299
|
-
mode?: SteerDeliveryMode;
|
|
304
|
+
mode?: SteerDeliveryMode | "plan" | "apply";
|
|
305
|
+
repo?: string;
|
|
306
|
+
planId?: string;
|
|
300
307
|
workflowScript?: string;
|
|
308
|
+
workflowScriptPath?: string;
|
|
309
|
+
preflight?: import("../../shared/types.ts").WorkflowPreflightV1;
|
|
301
310
|
chatProgress?: "auto" | "off" | "live-card";
|
|
302
311
|
isolation?: "none" | "worktree";
|
|
303
312
|
step?: ChainStep;
|
|
304
313
|
/** Internal workflow ownership metadata; not part of the public schema. */
|
|
305
314
|
workflowParentRunId?: string;
|
|
306
315
|
workflowKey?: string;
|
|
316
|
+
lane?: import("../../shared/types.ts").WorkflowLaneMetadata;
|
|
307
317
|
/** Set by the scheduler so this run's completion can name the schedule that produced it. */
|
|
308
318
|
scheduleOrigin?: ScheduleOrigin;
|
|
309
319
|
workflowChildAsyncId?: string;
|
|
310
320
|
workflowAwaitAsync?: boolean;
|
|
321
|
+
/** Internal async-workflow bridge: keep the live VM await pending across supervisor detachment. */
|
|
322
|
+
workflowAwaitDetached?: boolean;
|
|
311
323
|
workflowParentDeadlineAt?: number;
|
|
324
|
+
workflowOutputClaimPath?: string;
|
|
312
325
|
suppressRoutineResultIntercom?: boolean;
|
|
313
326
|
/** Internal inherited cumulative run-tree budget. */
|
|
314
327
|
runFanoutBudget?: RunFanoutBudgetDescriptor;
|
|
@@ -330,9 +343,6 @@ export interface SubagentParamsLike {
|
|
|
330
343
|
maxRuntimeMs?: number;
|
|
331
344
|
/** Optional hard per-tool-call timeout (ms). Known-fast tools also have a default. */
|
|
332
345
|
toolTimeoutMs?: number;
|
|
333
|
-
turnBudget?: TurnBudgetConfig;
|
|
334
|
-
/** Internal-only strict turn-boundary enforcement for versioned foreground delegation. */
|
|
335
|
-
enforceHardTurnLimit?: boolean;
|
|
336
346
|
toolBudget?: ToolBudgetConfig;
|
|
337
347
|
usageBudget?: UsageBudgetConfig;
|
|
338
348
|
clarify?: boolean;
|
|
@@ -392,12 +402,13 @@ interface ExecutorDeps {
|
|
|
392
402
|
config: ExtensionConfig;
|
|
393
403
|
asyncByDefault: boolean;
|
|
394
404
|
waitToolEnabled?: boolean;
|
|
405
|
+
waitToolDefaultTimeoutMs?: number;
|
|
395
406
|
handleScheduledRunAction?: (params: SubagentParamsLike, ctx: ExtensionContext) => Promise<AgentToolResult<Details>>;
|
|
396
407
|
watchdog?: MainWatchdogRuntime;
|
|
397
408
|
tempArtifactsDir: string;
|
|
398
409
|
getSubagentSessionRoot: (parentSessionFile: string | null) => string;
|
|
399
410
|
expandTilde: (p: string) => string;
|
|
400
|
-
discoverAgents: (cwd: string, scope: AgentScope) => { agents: AgentConfig[]; agentDiagnostics?: AgentDiscoveryDiagnostic[]; modelScope?: ModelScopeConfig; maxThinking?: AgentConfig["maxThinking"] };
|
|
411
|
+
discoverAgents: (cwd: string, scope: AgentScope, preferredModelProvider?: string) => { agents: AgentConfig[]; agentDiagnostics?: AgentDiscoveryDiagnostic[]; modelScope?: ModelScopeConfig; maxThinking?: AgentConfig["maxThinking"]; cwd?: string; scope?: AgentScope; directories?: UnknownAgentDiagnosticContext["directories"] };
|
|
401
412
|
allowMutatingManagementActions?: boolean;
|
|
402
413
|
activateSupervisorTransport?: () => void;
|
|
403
414
|
refreshResultDelivery?: () => void;
|
|
@@ -405,15 +416,19 @@ interface ExecutorDeps {
|
|
|
405
416
|
}
|
|
406
417
|
|
|
407
418
|
type ForkSessionFileForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean) => string | undefined;
|
|
419
|
+
type PrepareForkSessionForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean) => Promise<void>;
|
|
408
420
|
type ForkThinkingOverrideForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean) => AgentConfig["thinking"] | undefined;
|
|
409
421
|
|
|
410
422
|
interface ExecutionContextData {
|
|
411
423
|
params: SubagentParamsLike;
|
|
412
424
|
effectiveCwd: string;
|
|
425
|
+
requestedCwd?: string;
|
|
413
426
|
ctx: ExtensionContext;
|
|
414
427
|
signal: AbortSignal;
|
|
415
428
|
onUpdate?: (r: AgentToolResult<Details>) => void;
|
|
416
429
|
agents: AgentConfig[];
|
|
430
|
+
/** Exact filesystem discovery provenance retained through defensive execution backstops. */
|
|
431
|
+
unknownAgentDiagnosticContext: UnknownAgentDiagnosticContext;
|
|
417
432
|
/** Discovered agent definitions before per-run bridge injection. */
|
|
418
433
|
recoveryAgents: AgentConfig[];
|
|
419
434
|
runId: string;
|
|
@@ -435,7 +450,6 @@ interface ExecutionContextData {
|
|
|
435
450
|
deadlineAt?: number;
|
|
436
451
|
/** Raw global config.toolTimeoutMs, for per-step resolution in async runners. */
|
|
437
452
|
configToolTimeoutMs?: number;
|
|
438
|
-
turnBudget?: ResolvedTurnBudget;
|
|
439
453
|
toolBudget?: ResolvedToolBudget;
|
|
440
454
|
usageBudget?: UsageBudgetConfig;
|
|
441
455
|
allowZeroToolBudget?: boolean;
|
|
@@ -449,12 +463,27 @@ interface ExecutionContextData {
|
|
|
449
463
|
runFanoutBudget: RunFanoutBudgetDescriptor;
|
|
450
464
|
topLevelAsyncCapacityEligible: boolean;
|
|
451
465
|
activeAsyncCapacity?: ActiveAsyncCapacityHandle;
|
|
466
|
+
workflowChildPermitLaunch?: WorkflowChildPermitContext;
|
|
452
467
|
}
|
|
453
468
|
|
|
454
469
|
function resolveRequestedCwd(runtimeCwd: string, requestedCwd: string | undefined): string {
|
|
455
470
|
return requestedCwd ? path.resolve(runtimeCwd, requestedCwd) : runtimeCwd;
|
|
456
471
|
}
|
|
457
472
|
|
|
473
|
+
function loadWorkflowScriptPath(params: SubagentParamsLike, runtimeCwd: string): { params?: SubagentParamsLike; error?: string } {
|
|
474
|
+
if (params.workflowScriptPath === undefined) return { params };
|
|
475
|
+
const scriptPath = path.resolve(resolveRequestedCwd(runtimeCwd, params.cwd), params.workflowScriptPath);
|
|
476
|
+
let workflowScript: string;
|
|
477
|
+
try {
|
|
478
|
+
workflowScript = fs.readFileSync(scriptPath, "utf8");
|
|
479
|
+
} catch (error) {
|
|
480
|
+
return { error: `Failed to read workflowScriptPath '${scriptPath}': ${error instanceof Error ? error.message : String(error)}` };
|
|
481
|
+
}
|
|
482
|
+
if (!workflowScript.trim()) return { error: `workflowScriptPath file '${scriptPath}' is empty.` };
|
|
483
|
+
const { workflowScriptPath: _workflowScriptPath, ...rest } = params;
|
|
484
|
+
return { params: { ...rest, workflowScript } };
|
|
485
|
+
}
|
|
486
|
+
|
|
458
487
|
function removeForegroundControlIfIdle(state: SubagentState, runId: string): boolean {
|
|
459
488
|
const control = state.foregroundControls.get(runId);
|
|
460
489
|
if (control && (!foregroundSchedulingSettled(control) || (control.activeChildren?.size ?? 0) > 0)) return false;
|
|
@@ -477,12 +506,13 @@ function getForegroundControl(state: SubagentState, runId: string | undefined) {
|
|
|
477
506
|
return newest;
|
|
478
507
|
}
|
|
479
508
|
|
|
480
|
-
function promptAuditRedoParams(value: unknown, rewrittenTask: string): SubagentParamsLike {
|
|
509
|
+
export function promptAuditRedoParams(value: unknown, rewrittenTask: string): SubagentParamsLike {
|
|
481
510
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Prompt redo is missing a safe live launch contract.");
|
|
482
511
|
const params = { ...(value as SubagentParamsLike), task: rewrittenTask, async: false };
|
|
483
512
|
delete params.workflowParentRunId;
|
|
484
513
|
delete params.workflowKey;
|
|
485
514
|
delete params.workflowChildAsyncId;
|
|
515
|
+
delete params.workflowAwaitDetached;
|
|
486
516
|
delete params.workflowParentDeadlineAt;
|
|
487
517
|
delete params.suppressRoutineResultIntercom;
|
|
488
518
|
if (params.worktree === true && Array.isArray(params.tasks)) delete params.cwd;
|
|
@@ -543,6 +573,7 @@ function withSpawnBudgetStatus(
|
|
|
543
573
|
const activeAsyncCapacity = sessionId
|
|
544
574
|
? getActiveAsyncCapacitySnapshot(sessionId, resolveMaxActiveAsyncRunsPerSession(config.maxActiveAsyncRunsPerSession), {
|
|
545
575
|
liveWorkflowRunIds: new Set(state.workflowControllers?.keys() ?? []),
|
|
576
|
+
abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(config.capacity?.abandonedSlotReleaseAfterMs),
|
|
546
577
|
})
|
|
547
578
|
: { used: 0, limit: resolveMaxActiveAsyncRunsPerSession(config.maxActiveAsyncRunsPerSession) ?? 0 };
|
|
548
579
|
state.activeAsyncCapacity = activeAsyncCapacity;
|
|
@@ -645,11 +676,13 @@ function foregroundChildActivityFromProgress(progress: SingleResult["progress"]
|
|
|
645
676
|
...(progress?.currentPath ? { currentPath: progress.currentPath } : {}),
|
|
646
677
|
...(progress?.turnCount !== undefined ? { turnCount: progress.turnCount } : {}),
|
|
647
678
|
...(progress?.tokens !== undefined ? { tokens: progress.tokens } : {}),
|
|
679
|
+
...(progress?.window !== undefined ? { window: progress.window } : {}),
|
|
680
|
+
...(progress?.windowPeak !== undefined ? { windowPeak: progress.windowPeak } : {}),
|
|
648
681
|
...(progress?.toolCount !== undefined ? { toolCount: progress.toolCount } : {}),
|
|
649
682
|
};
|
|
650
683
|
}
|
|
651
684
|
|
|
652
|
-
function rememberForegroundRun(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[]; extensionBindings?: ExtensionBindings }): void {
|
|
685
|
+
function rememberForegroundRun(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[]; params: SubagentParamsLike; effectiveOutput?: string | boolean; effectiveOutputMode: OutputMode; extensionBindings?: ExtensionBindings }): void {
|
|
653
686
|
state.foregroundRuns ??= new Map();
|
|
654
687
|
const previous = state.foregroundRuns.get(input.runId);
|
|
655
688
|
const updatedAt = Date.now();
|
|
@@ -660,9 +693,17 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
660
693
|
...(input.sessionId ? { sessionId: input.sessionId } : {}),
|
|
661
694
|
updatedAt,
|
|
662
695
|
children: input.results.map((result, index) => {
|
|
696
|
+
const resumeContract = omitUndefinedProperties({
|
|
697
|
+
outputSchema: input.params.outputSchema,
|
|
698
|
+
agentContract: input.params.agentContract,
|
|
699
|
+
acceptance: input.params.acceptance,
|
|
700
|
+
output: input.effectiveOutput,
|
|
701
|
+
outputMode: input.effectiveOutputMode,
|
|
702
|
+
});
|
|
663
703
|
const child = {
|
|
664
704
|
agent: result.agent,
|
|
665
705
|
index,
|
|
706
|
+
...(result.sessionName ? { sessionName: result.sessionName } : {}),
|
|
666
707
|
...(result.context ? { context: result.context } : {}),
|
|
667
708
|
status: resolveSubagentResultStatus(omitUndefinedProperties({
|
|
668
709
|
exitCode: result.exitCode,
|
|
@@ -690,6 +731,7 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
690
731
|
...(result.transcriptError ? { transcriptError: result.transcriptError } : {}),
|
|
691
732
|
...(result.detachedReason ? { detachedReason: result.detachedReason } : {}),
|
|
692
733
|
...(result.acceptance ? { acceptance: result.acceptance } : {}),
|
|
734
|
+
...(Object.keys(resumeContract).length ? { resumeContract } : {}),
|
|
693
735
|
...(result.launchContractDigest ? { launchContractDigest: result.launchContractDigest } : {}),
|
|
694
736
|
...(input.extensionBindings ? { extensionBindings: input.extensionBindings } : {}),
|
|
695
737
|
...(result.launchResolvedExtensions ? { launchResolvedExtensions: result.launchResolvedExtensions } : {}),
|
|
@@ -810,7 +852,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
810
852
|
});
|
|
811
853
|
}
|
|
812
854
|
|
|
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 {
|
|
855
|
+
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; resumeContract?: ForegroundResumeChild["resumeContract"]; extensionBindings?: ExtensionBindings; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } | undefined {
|
|
814
856
|
const requested = (params.id ?? params.runId)?.trim();
|
|
815
857
|
if (!requested || !state.foregroundRuns?.size || !state.currentSessionId) return undefined;
|
|
816
858
|
const direct = state.foregroundRuns.get(requested);
|
|
@@ -841,6 +883,7 @@ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: Subage
|
|
|
841
883
|
...(child.model ? { model: child.model } : {}),
|
|
842
884
|
...(child.thinking ? { thinking: child.thinking } : {}),
|
|
843
885
|
...(child.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {}),
|
|
886
|
+
...(child.resumeContract ? { resumeContract: child.resumeContract } : {}),
|
|
844
887
|
...(child.extensionBindings ? { extensionBindings: normalizeExtensionBindings(child.extensionBindings)!.value } : {}),
|
|
845
888
|
...(child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {}),
|
|
846
889
|
};
|
|
@@ -1215,7 +1258,7 @@ function appendStepToAsyncChain(input: {
|
|
|
1215
1258
|
}
|
|
1216
1259
|
|
|
1217
1260
|
const scope: AgentScope = resolveExecutionAgentScope(input.params.agentScope);
|
|
1218
|
-
const discoveredForAppend = input.deps.discoverAgents(input.requestCwd, scope);
|
|
1261
|
+
const discoveredForAppend = input.deps.discoverAgents(input.requestCwd, scope, input.parentModel?.provider);
|
|
1219
1262
|
const agents = discoveredForAppend.agents;
|
|
1220
1263
|
const contextPolicy = resolveExplicitContextPolicy(input.params);
|
|
1221
1264
|
const chainSkillInput = normalizeSkillInput(input.params.skill);
|
|
@@ -1239,11 +1282,13 @@ function appendStepToAsyncChain(input: {
|
|
|
1239
1282
|
agents,
|
|
1240
1283
|
ctx: asyncCtx,
|
|
1241
1284
|
availableModels: input.ctx.modelRegistry.getAvailable().map(toModelInfo),
|
|
1285
|
+
unknownAgentDiagnosticContext: diagnosticContextFromDiscovery(discoveredForAppend, input.requestCwd, scope),
|
|
1242
1286
|
cwd: status.cwd ?? input.requestCwd,
|
|
1243
1287
|
chainSkills,
|
|
1244
1288
|
dynamicFanoutMaxItems: input.deps.config.chain?.dynamicFanout?.maxItems,
|
|
1245
1289
|
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1246
1290
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
1291
|
+
waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
|
|
1247
1292
|
contextForAgent: contextPolicy.contextForAgent,
|
|
1248
1293
|
asyncDir: resolved.location.asyncDir,
|
|
1249
1294
|
validateOutputBindings: false,
|
|
@@ -1514,9 +1559,14 @@ function externalJobFollowUpStarted(input: { sourceRunId: string; runId: string;
|
|
|
1514
1559
|
function externalRunnerControlError(asyncDir: string, action: "steer" | "resume"): AgentToolResult<Details> | undefined {
|
|
1515
1560
|
const status = readStatus(asyncDir);
|
|
1516
1561
|
if (!status?.steps?.length || !status.steps.every((step) => step.runner?.type === "external-cli" || step.runner?.type === "external-job")) return undefined;
|
|
1517
|
-
const
|
|
1518
|
-
|
|
1519
|
-
|
|
1562
|
+
const externalCli = normalizeExternalCliRunnerStatus(status.steps.find((step) => step.runner?.type === "external-cli")?.runner);
|
|
1563
|
+
const message = externalCli
|
|
1564
|
+
? action === "steer"
|
|
1565
|
+
? `External adapter '${externalCli.adapter.id}' does not support runs.steer: ${externalCli.unsupportedReasons.steer}`
|
|
1566
|
+
: `External adapter '${externalCli.adapter.id}' cannot resume: ${externalCli.nonResumableReason}`
|
|
1567
|
+
: action === "steer"
|
|
1568
|
+
? "External runners do not accept live steer messages."
|
|
1569
|
+
: "External runners do not persist Pi sessions and cannot be resumed.";
|
|
1520
1570
|
return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
|
|
1521
1571
|
}
|
|
1522
1572
|
|
|
@@ -1572,7 +1622,7 @@ async function resumeExternalJobFollowUp(input: {
|
|
|
1572
1622
|
runId,
|
|
1573
1623
|
kind: "runner",
|
|
1574
1624
|
asyncDir,
|
|
1575
|
-
}, { liveWorkflowRunIds: new Set(input.deps.state.workflowControllers?.keys() ?? []) }) : undefined;
|
|
1625
|
+
}, { liveWorkflowRunIds: new Set(input.deps.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(input.deps.config.capacity?.abandonedSlotReleaseAfterMs) }) : undefined;
|
|
1576
1626
|
} catch (error) {
|
|
1577
1627
|
if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "single", results: [], activeAsyncCapacity: error.snapshot } };
|
|
1578
1628
|
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "single", results: [] } };
|
|
@@ -1609,6 +1659,7 @@ async function resumeExternalJobFollowUp(input: {
|
|
|
1609
1659
|
...(input.parentSessionFile ? { sessionRoot: input.deps.getSubagentSessionRoot(input.parentSessionFile) } : {}),
|
|
1610
1660
|
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1611
1661
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
1662
|
+
waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
|
|
1612
1663
|
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
1613
1664
|
worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
|
|
1614
1665
|
worktreeBaseDir: input.deps.config.worktreeBaseDir,
|
|
@@ -1727,8 +1778,9 @@ async function resumeAsyncRun(input: {
|
|
|
1727
1778
|
input.deps.state.currentSessionId = resolveCurrentSessionId(input.ctx.sessionManager);
|
|
1728
1779
|
const effectiveCwd = target.cwd ?? input.requestCwd;
|
|
1729
1780
|
const scope: AgentScope = resolveExecutionAgentScope(input.params.agentScope);
|
|
1730
|
-
const discovered = input.deps.discoverAgents(effectiveCwd, scope);
|
|
1781
|
+
const discovered = input.deps.discoverAgents(effectiveCwd, scope, input.parentModel?.provider);
|
|
1731
1782
|
const discoveredAgents = discovered.agents;
|
|
1783
|
+
const unknownAgentDiagnosticContext = diagnosticContextFromDiscovery(discovered, effectiveCwd, scope);
|
|
1732
1784
|
const modelScope = discovered.modelScope;
|
|
1733
1785
|
const sessionName = resolveIntercomSessionTarget(input.deps.pi.getSessionName(), input.ctx.sessionManager.getSessionId());
|
|
1734
1786
|
const recoveryDescriptor = "recoveryDescriptor" in target ? target.recoveryDescriptor : undefined;
|
|
@@ -1749,6 +1801,7 @@ async function resumeAsyncRun(input: {
|
|
|
1749
1801
|
systemPrompt: "",
|
|
1750
1802
|
systemPromptMode: recoveryDescriptor.systemPromptMode,
|
|
1751
1803
|
inheritProjectContext: recoveryDescriptor.inheritProjectContext,
|
|
1804
|
+
inheritGlobalContext: recoveryDescriptor.inheritGlobalContext,
|
|
1752
1805
|
inheritSkills: recoveryDescriptor.inheritSkills,
|
|
1753
1806
|
source: "project",
|
|
1754
1807
|
filePath: recoveryDescriptor.agentFilePath ?? path.join(getProjectSubagentsDir(recoveryDescriptor.cwd), "recovery-agent"),
|
|
@@ -1756,13 +1809,17 @@ async function resumeAsyncRun(input: {
|
|
|
1756
1809
|
} : undefined);
|
|
1757
1810
|
if (!baseAgentConfig) {
|
|
1758
1811
|
return {
|
|
1759
|
-
content: [{ type: "text", text:
|
|
1812
|
+
content: [{ type: "text", text: formatUnknownAgentError(target.agent, unknownAgentDiagnosticContext, "Unknown agent for resume") }],
|
|
1760
1813
|
isError: true,
|
|
1761
1814
|
details: { mode: "management", results: [] },
|
|
1762
1815
|
};
|
|
1763
1816
|
}
|
|
1764
1817
|
if (target.source === "async" && target.runner?.type === "external-cli") {
|
|
1765
|
-
|
|
1818
|
+
const runner = normalizeExternalCliRunnerStatus(target.runner);
|
|
1819
|
+
const message = runner
|
|
1820
|
+
? `External adapter '${runner.adapter.id}' cannot resume: ${runner.nonResumableReason}`
|
|
1821
|
+
: "External runners do not persist Pi sessions and cannot be resumed.";
|
|
1822
|
+
return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
|
|
1766
1823
|
}
|
|
1767
1824
|
if (target.source === "async" && target.runner?.type === "external-job") {
|
|
1768
1825
|
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: [] } };
|
|
@@ -1807,7 +1864,7 @@ async function resumeAsyncRun(input: {
|
|
|
1807
1864
|
runId,
|
|
1808
1865
|
kind: "runner",
|
|
1809
1866
|
asyncDir: path.join(DIRS.async, runId),
|
|
1810
|
-
}, { liveWorkflowRunIds: new Set(input.deps.state.workflowControllers?.keys() ?? []) }) : undefined;
|
|
1867
|
+
}, { liveWorkflowRunIds: new Set(input.deps.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(input.deps.config.capacity?.abandonedSlotReleaseAfterMs) }) : undefined;
|
|
1811
1868
|
} catch (error) {
|
|
1812
1869
|
if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "chain", results: [], activeAsyncCapacity: error.snapshot } };
|
|
1813
1870
|
throw error;
|
|
@@ -1833,6 +1890,7 @@ async function resumeAsyncRun(input: {
|
|
|
1833
1890
|
label: `Attached ${target.runId}`,
|
|
1834
1891
|
},
|
|
1835
1892
|
agents,
|
|
1893
|
+
unknownAgentDiagnosticContext,
|
|
1836
1894
|
ctx: compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
|
|
1837
1895
|
pi: input.deps.pi,
|
|
1838
1896
|
cwd: input.requestCwd,
|
|
@@ -1857,6 +1915,7 @@ async function resumeAsyncRun(input: {
|
|
|
1857
1915
|
dynamicFanoutMaxItems: input.deps.config.chain?.dynamicFanout?.maxItems,
|
|
1858
1916
|
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1859
1917
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
1918
|
+
waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
|
|
1860
1919
|
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
1861
1920
|
worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
|
|
1862
1921
|
worktreeBaseDir: input.deps.config.worktreeBaseDir,
|
|
@@ -1905,20 +1964,23 @@ async function resumeAsyncRun(input: {
|
|
|
1905
1964
|
sourceRunId: target.runId,
|
|
1906
1965
|
runId,
|
|
1907
1966
|
asyncDir: path.join(DIRS.async, runId),
|
|
1908
|
-
})
|
|
1967
|
+
}, { abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(input.deps.config.capacity?.abandonedSlotReleaseAfterMs) })
|
|
1909
1968
|
: acquireActiveAsyncCapacity({
|
|
1910
1969
|
sessionId: input.deps.state.currentSessionId!,
|
|
1911
1970
|
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
1912
1971
|
runId,
|
|
1913
1972
|
kind: "runner",
|
|
1914
1973
|
asyncDir: path.join(DIRS.async, runId),
|
|
1915
|
-
});
|
|
1974
|
+
}, { abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(input.deps.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
1916
1975
|
} catch (error) {
|
|
1917
1976
|
if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "single", results: [], activeAsyncCapacity: error.snapshot } };
|
|
1918
1977
|
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "single", results: [] } };
|
|
1919
1978
|
}
|
|
1920
1979
|
const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(baseAgentConfig, recoveryDescriptor) : baseAgentConfig;
|
|
1921
1980
|
const agentConfig = intercomBridge.active ? applyIntercomBridgeToAgent(recoveryAgentConfig, intercomBridge) : recoveryAgentConfig;
|
|
1981
|
+
const foregroundContract = target.source === "foreground" ? target.resumeContract : undefined;
|
|
1982
|
+
const outputSchema = input.params.outputSchema ?? foregroundContract?.outputSchema ?? recoveryDescriptor?.structuredOutputSchema;
|
|
1983
|
+
const agentContract = input.params.agentContract ?? foregroundContract?.agentContract ?? recoveryDescriptor?.agentContract;
|
|
1922
1984
|
const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? omitUndefinedProperties({ ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir });
|
|
1923
1985
|
const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
1924
1986
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
@@ -1965,24 +2027,25 @@ async function resumeAsyncRun(input: {
|
|
|
1965
2027
|
outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
|
|
1966
2028
|
maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1967
2029
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
2030
|
+
waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
|
|
1968
2031
|
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
1969
2032
|
worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
|
|
1970
2033
|
worktreeBaseDir: input.deps.config.worktreeBaseDir,
|
|
1971
2034
|
worktree: input.params.worktree === true,
|
|
2035
|
+
lane: input.params.lane ?? recoveryDescriptor?.lane,
|
|
1972
2036
|
controlConfig: recoveryDescriptor?.controlConfig ?? resolveControlConfig(input.deps.config.control, input.params.control),
|
|
1973
2037
|
intercomBridge: input.params.intercomBridge ?? recoveryDescriptor?.intercomBridge,
|
|
1974
2038
|
controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
|
|
1975
2039
|
childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
|
|
1976
2040
|
availableModels,
|
|
1977
|
-
output:
|
|
1978
|
-
outputMode: input.params.outputMode ?? recoveryDescriptor?.outputMode,
|
|
1979
|
-
...(
|
|
1980
|
-
...(
|
|
2041
|
+
output: input.params.output !== undefined ? input.params.output : foregroundContract?.output ?? recoveryDescriptor?.outputPath,
|
|
2042
|
+
outputMode: input.params.outputMode ?? foregroundContract?.outputMode ?? recoveryDescriptor?.outputMode,
|
|
2043
|
+
...(agentContract ? { agentContract } : {}),
|
|
2044
|
+
...(outputSchema ? { structuredOutputSchema: outputSchema } : {}),
|
|
1981
2045
|
...(recoveryDescriptor?.skills ? { skills: [...recoveryDescriptor.skills] } : {}),
|
|
1982
|
-
...(
|
|
2046
|
+
...(input.params.acceptance !== undefined ? { acceptance: input.params.acceptance } : foregroundContract?.acceptance !== undefined ? { acceptance: foregroundContract.acceptance } : recoveryDescriptor?.acceptance !== undefined ? { acceptance: recoveryDescriptor.acceptance } : {}),
|
|
1983
2047
|
...(input.params.timeoutMs !== undefined ? { timeoutMs: input.params.timeoutMs } : {}),
|
|
1984
2048
|
...(input.absoluteDeadlineAt !== undefined ? { absoluteDeadlineAt: input.absoluteDeadlineAt } : {}),
|
|
1985
|
-
...(input.params.turnBudget !== undefined ? { turnBudget: input.params.turnBudget } : {}),
|
|
1986
2049
|
...(input.params.toolBudget !== undefined ? { toolBudget: input.params.toolBudget } : {}),
|
|
1987
2050
|
capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, recoveryDescriptor?.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
|
|
1988
2051
|
runFanoutBudget: input.params.runFanoutBudget ?? recoveryDescriptor?.runFanoutBudget ?? createRunFanoutBudget(runId, resolveMaxSubagentSpawnsPerRun(input.deps.config.maxSubagentSpawnsPerRun)),
|
|
@@ -2030,20 +2093,14 @@ async function resumeAsyncRun(input: {
|
|
|
2030
2093
|
input.signal?.removeEventListener("abort", stopOnAbort);
|
|
2031
2094
|
}
|
|
2032
2095
|
fs.rmSync(resultPath, { force: true });
|
|
2033
|
-
const
|
|
2096
|
+
const usage = importedAsyncRootUsage(completed);
|
|
2034
2097
|
const childResult: SingleResult = {
|
|
2035
2098
|
index: 0,
|
|
2036
2099
|
agent: completed.agent,
|
|
2100
|
+
...(completed.sessionName ? { sessionName: completed.sessionName } : {}),
|
|
2037
2101
|
task: effectiveFollowUp,
|
|
2038
2102
|
exitCode: completed.exitCode,
|
|
2039
|
-
usage
|
|
2040
|
-
input: totalCost?.inputTokens ?? 0,
|
|
2041
|
-
output: totalCost?.outputTokens ?? 0,
|
|
2042
|
-
cacheRead: 0,
|
|
2043
|
-
cacheWrite: 0,
|
|
2044
|
-
cost: totalCost?.costUsd ?? 0,
|
|
2045
|
-
turns: 0,
|
|
2046
|
-
},
|
|
2103
|
+
usage,
|
|
2047
2104
|
finalOutput: completed.output,
|
|
2048
2105
|
outputState: completed.output.trim() ? "present" : "absent",
|
|
2049
2106
|
...(completed.error ? { error: completed.error } : {}),
|
|
@@ -2058,9 +2115,13 @@ async function resumeAsyncRun(input: {
|
|
|
2058
2115
|
...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
|
|
2059
2116
|
...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
|
|
2060
2117
|
...(completed.acceptance ? { acceptance: completed.acceptance } : {}),
|
|
2118
|
+
...(completed.artifactPaths ? { artifactPaths: completed.artifactPaths } : {}),
|
|
2119
|
+
...(completed.outputSaveError ? { outputSaveError: completed.outputSaveError } : {}),
|
|
2120
|
+
...(completed.transcriptPath ? { transcriptPath: completed.transcriptPath } : {}),
|
|
2121
|
+
...(completed.transcriptError ? { transcriptError: completed.transcriptError } : {}),
|
|
2061
2122
|
};
|
|
2062
2123
|
return {
|
|
2063
|
-
content: [{ type: "text", text: completed.output || completed.error || `Revived ${target.source} subagent ${revivedId} completed without output.` }],
|
|
2124
|
+
content: [{ type: "text", text: completed.success ? completed.output || completed.error || `Revived ${target.source} subagent ${revivedId} completed without output.` : completed.error || completed.output || `Revived ${target.source} subagent ${revivedId} completed without output.` }],
|
|
2064
2125
|
...(completed.success ? {} : { isError: true }),
|
|
2065
2126
|
details: {
|
|
2066
2127
|
...result.details,
|
|
@@ -2176,6 +2237,7 @@ async function emitForegroundResultIntercom(input: {
|
|
|
2176
2237
|
if (!input.intercomBridge.active || !input.intercomBridge.resultDelivery || !input.intercomBridge.orchestratorTarget) return null;
|
|
2177
2238
|
const children = input.results.flatMap((result, index) => result.detached ? [] : [omitUndefinedProperties({
|
|
2178
2239
|
agent: result.agent,
|
|
2240
|
+
...(result.sessionName ? { sessionName: result.sessionName } : {}),
|
|
2179
2241
|
status: foregroundResultIntercomStatus(result),
|
|
2180
2242
|
outputState: result.outputState ?? "unknown",
|
|
2181
2243
|
summary: resultSummaryForIntercom(result),
|
|
@@ -2225,19 +2287,28 @@ async function maybeBuildForegroundIntercomReceipt(input: {
|
|
|
2225
2287
|
};
|
|
2226
2288
|
}
|
|
2227
2289
|
|
|
2228
|
-
function
|
|
2290
|
+
function diagnosticContextFromDiscovery(
|
|
2291
|
+
discovered: { agents: AgentConfig[]; cwd?: string; scope?: AgentScope; directories?: UnknownAgentDiagnosticContext["directories"] },
|
|
2292
|
+
cwd: string,
|
|
2293
|
+
scope: AgentScope,
|
|
2294
|
+
): UnknownAgentDiagnosticContext {
|
|
2295
|
+
if (discovered.cwd && discovered.scope && discovered.directories) return unknownAgentDiagnosticContext({ ...discovered, cwd: discovered.cwd, scope: discovered.scope, directories: discovered.directories });
|
|
2296
|
+
return unknownAgentDiagnosticContext(discoverAgents(path.resolve(cwd), scope));
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
function canonicalizeAgentName(name: string, agents: AgentConfig[], diagnostics: AgentDiscoveryDiagnostic[] | undefined, context: UnknownAgentDiagnosticContext): { name?: string; error?: string } {
|
|
2229
2300
|
const resolved = resolveAgentName(name, agents);
|
|
2230
2301
|
const candidates = resolved.error ? agents.filter((agent) => resolveAgentName(name, [agent]).agent) : resolved.agent;
|
|
2231
2302
|
const diagnostic = findBlockingAgentDiagnostic(name, candidates, diagnostics);
|
|
2232
2303
|
if (diagnostic) return { error: `Agent '${name}' has invalid configuration: ${diagnostic.error}` };
|
|
2233
2304
|
if (resolved.error) return { error: resolved.error };
|
|
2234
|
-
if (!resolved.agent) return { error:
|
|
2305
|
+
if (!resolved.agent) return { error: formatUnknownAgentError(name, context) };
|
|
2235
2306
|
return { name: resolved.agent.name };
|
|
2236
2307
|
}
|
|
2237
2308
|
|
|
2238
|
-
function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentConfig[], diagnostics
|
|
2309
|
+
function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentConfig[], diagnostics: AgentDiscoveryDiagnostic[] | undefined, context: UnknownAgentDiagnosticContext): { params?: SubagentParamsLike; error?: string } {
|
|
2239
2310
|
const resolve = (name: string, location?: string): { name?: string; error?: string } => {
|
|
2240
|
-
const result = canonicalizeAgentName(name, agents, diagnostics);
|
|
2311
|
+
const result = canonicalizeAgentName(name, agents, diagnostics, context);
|
|
2241
2312
|
return result.error && location ? { error: `${result.error} (${location})` } : result;
|
|
2242
2313
|
};
|
|
2243
2314
|
if (params.agent) {
|
|
@@ -2305,6 +2376,7 @@ function validateExecutionInput(
|
|
|
2305
2376
|
hasTasks: boolean,
|
|
2306
2377
|
hasSingle: boolean,
|
|
2307
2378
|
allowClarifyTaskPrompt: boolean,
|
|
2379
|
+
context: UnknownAgentDiagnosticContext,
|
|
2308
2380
|
): AgentToolResult<Details> | null {
|
|
2309
2381
|
if (Number(hasChain) + Number(hasTasks) + Number(hasSingle) !== 1) {
|
|
2310
2382
|
return {
|
|
@@ -2330,7 +2402,7 @@ function validateExecutionInput(
|
|
|
2330
2402
|
|
|
2331
2403
|
if (hasSingle && params.agent && !agents.find((agent) => agent.name === params.agent)) {
|
|
2332
2404
|
return {
|
|
2333
|
-
content: [{ type: "text", text:
|
|
2405
|
+
content: [{ type: "text", text: formatUnknownAgentError(params.agent, context) }],
|
|
2334
2406
|
isError: true,
|
|
2335
2407
|
details: { mode: "single" as const, results: [] },
|
|
2336
2408
|
};
|
|
@@ -2341,7 +2413,7 @@ function validateExecutionInput(
|
|
|
2341
2413
|
const task = params.tasks[i]!;
|
|
2342
2414
|
if (!agents.find((agent) => agent.name === task.agent)) {
|
|
2343
2415
|
return {
|
|
2344
|
-
content: [{ type: "text", text:
|
|
2416
|
+
content: [{ type: "text", text: `${formatUnknownAgentError(task.agent, context)} (task ${i + 1})` }],
|
|
2345
2417
|
isError: true,
|
|
2346
2418
|
details: { mode: "parallel" as const, results: [] },
|
|
2347
2419
|
};
|
|
@@ -2386,7 +2458,7 @@ function validateExecutionInput(
|
|
|
2386
2458
|
for (const agentName of stepAgents) {
|
|
2387
2459
|
if (!agents.find((a) => a.name === agentName)) {
|
|
2388
2460
|
return {
|
|
2389
|
-
content: [{ type: "text", text:
|
|
2461
|
+
content: [{ type: "text", text: `${formatUnknownAgentError(agentName, context)} (step ${i + 1})` }],
|
|
2390
2462
|
isError: true,
|
|
2391
2463
|
details: { mode: "chain" as const, results: [] },
|
|
2392
2464
|
};
|
|
@@ -2578,9 +2650,6 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
|
|
|
2578
2650
|
? { timeoutMs: agent.defaultTimeoutMs }
|
|
2579
2651
|
: {}),
|
|
2580
2652
|
...(parentTimeoutMs !== undefined ? { timeoutMs: parentTimeoutMs } : {}),
|
|
2581
|
-
...(params.turnBudget === undefined && agent.defaultTurnBudget !== undefined
|
|
2582
|
-
? { turnBudget: agent.defaultTurnBudget }
|
|
2583
|
-
: {}),
|
|
2584
2653
|
...(params.acceptance === undefined && agent.defaultAcceptance !== undefined
|
|
2585
2654
|
? { acceptance: agent.defaultAcceptance }
|
|
2586
2655
|
: {}),
|
|
@@ -2749,6 +2818,14 @@ function withResolvedContext(
|
|
|
2749
2818
|
};
|
|
2750
2819
|
}
|
|
2751
2820
|
|
|
2821
|
+
function withAggregatedToolUsage(result: AgentToolResult<Details>): AgentToolResult<Details> {
|
|
2822
|
+
if (result.details.results.length === 0) return result;
|
|
2823
|
+
const usage = sumResultsUsage(result.details.results);
|
|
2824
|
+
return usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0
|
|
2825
|
+
? { ...result, usage: toAgentToolUsage(usage) }
|
|
2826
|
+
: result;
|
|
2827
|
+
}
|
|
2828
|
+
|
|
2752
2829
|
function withForkThinkingNotes(
|
|
2753
2830
|
result: AgentToolResult<Details>,
|
|
2754
2831
|
downgrades: Map<number, string>,
|
|
@@ -2774,66 +2851,6 @@ function toExecutionErrorResult(params: SubagentParamsLike, error: unknown, cont
|
|
|
2774
2851
|
);
|
|
2775
2852
|
}
|
|
2776
2853
|
|
|
2777
|
-
function collectChainSessionFiles(
|
|
2778
|
-
chain: ChainStep[],
|
|
2779
|
-
sessionFileForTask: ForkSessionFileForTask,
|
|
2780
|
-
dynamicFanoutMaxItems?: number,
|
|
2781
|
-
): (string | undefined)[] {
|
|
2782
|
-
const sessionFiles: (string | undefined)[] = [];
|
|
2783
|
-
let flatIndex = 0;
|
|
2784
|
-
for (const step of chain) {
|
|
2785
|
-
if (isParallelStep(step)) {
|
|
2786
|
-
for (const task of step.parallel) {
|
|
2787
|
-
sessionFiles.push(sessionFileForTask(task.agent, flatIndex, task.model));
|
|
2788
|
-
flatIndex++;
|
|
2789
|
-
}
|
|
2790
|
-
continue;
|
|
2791
|
-
}
|
|
2792
|
-
if (isDynamicParallelStep(step)) {
|
|
2793
|
-
const maxItems = step.expand.maxItems ?? dynamicFanoutMaxItems ?? 0;
|
|
2794
|
-
for (let itemIndex = 0; itemIndex < maxItems; itemIndex++) {
|
|
2795
|
-
sessionFiles.push(sessionFileForTask(step.parallel.agent, flatIndex, step.parallel.model));
|
|
2796
|
-
flatIndex++;
|
|
2797
|
-
}
|
|
2798
|
-
continue;
|
|
2799
|
-
}
|
|
2800
|
-
const sequential = step as SequentialStep;
|
|
2801
|
-
sessionFiles.push(sessionFileForTask(sequential.agent, flatIndex, sequential.model));
|
|
2802
|
-
flatIndex++;
|
|
2803
|
-
}
|
|
2804
|
-
return sessionFiles;
|
|
2805
|
-
}
|
|
2806
|
-
|
|
2807
|
-
function collectChainThinkingOverrides(
|
|
2808
|
-
chain: ChainStep[],
|
|
2809
|
-
thinkingOverrideForTask: ForkThinkingOverrideForTask,
|
|
2810
|
-
dynamicFanoutMaxItems?: number,
|
|
2811
|
-
): (AgentConfig["thinking"] | undefined)[] {
|
|
2812
|
-
const thinkingOverrides: (AgentConfig["thinking"] | undefined)[] = [];
|
|
2813
|
-
let flatIndex = 0;
|
|
2814
|
-
for (const step of chain) {
|
|
2815
|
-
if (isParallelStep(step)) {
|
|
2816
|
-
for (const task of step.parallel) {
|
|
2817
|
-
thinkingOverrides.push(thinkingOverrideForTask(task.agent, flatIndex, task.model));
|
|
2818
|
-
flatIndex++;
|
|
2819
|
-
}
|
|
2820
|
-
continue;
|
|
2821
|
-
}
|
|
2822
|
-
if (isDynamicParallelStep(step)) {
|
|
2823
|
-
const maxItems = step.expand.maxItems ?? dynamicFanoutMaxItems ?? 0;
|
|
2824
|
-
for (let itemIndex = 0; itemIndex < maxItems; itemIndex++) {
|
|
2825
|
-
thinkingOverrides.push(thinkingOverrideForTask(step.parallel.agent, flatIndex, step.parallel.model));
|
|
2826
|
-
flatIndex++;
|
|
2827
|
-
}
|
|
2828
|
-
continue;
|
|
2829
|
-
}
|
|
2830
|
-
const sequential = step as SequentialStep;
|
|
2831
|
-
thinkingOverrides.push(thinkingOverrideForTask(sequential.agent, flatIndex, sequential.model));
|
|
2832
|
-
flatIndex++;
|
|
2833
|
-
}
|
|
2834
|
-
return thinkingOverrides;
|
|
2835
|
-
}
|
|
2836
|
-
|
|
2837
2854
|
type StaticLaunchSummary = { agent: string; model?: string; thinking?: string };
|
|
2838
2855
|
|
|
2839
2856
|
function resolveStaticLaunchSummary(input: {
|
|
@@ -2919,14 +2936,6 @@ function collectStaticLaunchSummaries(input: {
|
|
|
2919
2936
|
return input.params.agent ? [summary(input.params.agent, 0, input.params.model as string | undefined)] : [];
|
|
2920
2937
|
}
|
|
2921
2938
|
|
|
2922
|
-
function firstChainAgent(chain: ChainStep[]): string | undefined {
|
|
2923
|
-
const first = chain[0];
|
|
2924
|
-
if (!first) return undefined;
|
|
2925
|
-
if (isParallelStep(first)) return first.parallel[0]?.agent;
|
|
2926
|
-
if (isDynamicParallelStep(first)) return first.parallel.agent;
|
|
2927
|
-
return (first as SequentialStep).agent;
|
|
2928
|
-
}
|
|
2929
|
-
|
|
2930
2939
|
function firstRawChainTask(chain: ChainStep[]): string | undefined {
|
|
2931
2940
|
const first = chain[0];
|
|
2932
2941
|
if (!first) return undefined;
|
|
@@ -2977,21 +2986,21 @@ function wrapChainTasksForFork(chain: ChainStep[], contextPolicy: AgentDefaultCo
|
|
|
2977
2986
|
});
|
|
2978
2987
|
}
|
|
2979
2988
|
|
|
2980
|
-
function preflightForkSessionsForStaticTasks(
|
|
2989
|
+
async function preflightForkSessionsForStaticTasks(
|
|
2981
2990
|
params: SubagentParamsLike,
|
|
2982
2991
|
contextPolicy: AgentDefaultContextPolicy,
|
|
2983
|
-
|
|
2992
|
+
prepareSessionForTask: PrepareForkSessionForTask,
|
|
2984
2993
|
dynamicFanoutMaxItems?: number,
|
|
2985
|
-
): void {
|
|
2994
|
+
): Promise<void> {
|
|
2986
2995
|
if (!contextPolicy.usesFork) return;
|
|
2987
2996
|
if (params.agent) {
|
|
2988
|
-
if (shouldForkAgent(contextPolicy, params.agent))
|
|
2997
|
+
if (shouldForkAgent(contextPolicy, params.agent)) await prepareSessionForTask(params.agent, 0, params.model);
|
|
2989
2998
|
return;
|
|
2990
2999
|
}
|
|
2991
3000
|
if (params.tasks) {
|
|
2992
|
-
params.tasks.
|
|
2993
|
-
if (shouldForkAgent(contextPolicy, task.agent))
|
|
2994
|
-
}
|
|
3001
|
+
for (const [index, task] of params.tasks.entries()) {
|
|
3002
|
+
if (shouldForkAgent(contextPolicy, task.agent)) await prepareSessionForTask(task.agent, index, task.model);
|
|
3003
|
+
}
|
|
2995
3004
|
return;
|
|
2996
3005
|
}
|
|
2997
3006
|
if (!params.chain?.length) return;
|
|
@@ -2999,7 +3008,7 @@ function preflightForkSessionsForStaticTasks(
|
|
|
2999
3008
|
for (const step of params.chain) {
|
|
3000
3009
|
if (isParallelStep(step)) {
|
|
3001
3010
|
for (const task of step.parallel) {
|
|
3002
|
-
if (shouldForkAgent(contextPolicy, task.agent))
|
|
3011
|
+
if (shouldForkAgent(contextPolicy, task.agent)) await prepareSessionForTask(task.agent, flatIndex, task.model);
|
|
3003
3012
|
flatIndex++;
|
|
3004
3013
|
}
|
|
3005
3014
|
continue;
|
|
@@ -3007,17 +3016,29 @@ function preflightForkSessionsForStaticTasks(
|
|
|
3007
3016
|
if (isDynamicParallelStep(step)) {
|
|
3008
3017
|
const maxItems = step.expand.maxItems ?? dynamicFanoutMaxItems ?? 0;
|
|
3009
3018
|
if (shouldForkAgent(contextPolicy, step.parallel.agent)) {
|
|
3010
|
-
for (let itemIndex = 0; itemIndex < maxItems; itemIndex++)
|
|
3019
|
+
for (let itemIndex = 0; itemIndex < maxItems; itemIndex++) await prepareSessionForTask(step.parallel.agent, flatIndex + itemIndex, step.parallel.model);
|
|
3011
3020
|
}
|
|
3012
3021
|
flatIndex += maxItems;
|
|
3013
3022
|
continue;
|
|
3014
3023
|
}
|
|
3015
3024
|
const sequential = step as SequentialStep;
|
|
3016
|
-
if (shouldForkAgent(contextPolicy, sequential.agent))
|
|
3025
|
+
if (shouldForkAgent(contextPolicy, sequential.agent)) await prepareSessionForTask(sequential.agent, flatIndex, sequential.model);
|
|
3017
3026
|
flatIndex++;
|
|
3018
3027
|
}
|
|
3019
3028
|
}
|
|
3020
3029
|
|
|
3030
|
+
function importedAsyncRootUsage(completed: Awaited<ReturnType<typeof waitForImportedAsyncRoot>>): Usage {
|
|
3031
|
+
const totalCost = completed.totalCost;
|
|
3032
|
+
return completed.usage ?? {
|
|
3033
|
+
input: totalCost?.inputTokens ?? 0,
|
|
3034
|
+
output: totalCost?.outputTokens ?? 0,
|
|
3035
|
+
cacheRead: 0,
|
|
3036
|
+
cacheWrite: 0,
|
|
3037
|
+
cost: totalCost?.costUsd ?? 0,
|
|
3038
|
+
turns: 0,
|
|
3039
|
+
};
|
|
3040
|
+
}
|
|
3041
|
+
|
|
3021
3042
|
async function waitForWorkflowAsyncSingleResult(
|
|
3022
3043
|
params: SubagentParamsLike,
|
|
3023
3044
|
launchResult: AgentToolResult<Details>,
|
|
@@ -3039,20 +3060,14 @@ async function waitForWorkflowAsyncSingleResult(
|
|
|
3039
3060
|
options.signal?.removeEventListener("abort", stopOnAbort);
|
|
3040
3061
|
}
|
|
3041
3062
|
fs.rmSync(resultPath, { force: true });
|
|
3042
|
-
const
|
|
3063
|
+
const usage = importedAsyncRootUsage(completed);
|
|
3043
3064
|
const childResult: SingleResult = omitUndefinedProperties({
|
|
3044
3065
|
index: 0,
|
|
3045
3066
|
agent: completed.agent,
|
|
3067
|
+
...(completed.sessionName ? { sessionName: completed.sessionName } : {}),
|
|
3046
3068
|
task: options.task,
|
|
3047
3069
|
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
|
-
},
|
|
3070
|
+
usage,
|
|
3056
3071
|
finalOutput: completed.output,
|
|
3057
3072
|
outputState: completed.output.trim() ? "present" as const : "absent" as const,
|
|
3058
3073
|
...(completed.error ? { error: completed.error } : {}),
|
|
@@ -3067,9 +3082,13 @@ async function waitForWorkflowAsyncSingleResult(
|
|
|
3067
3082
|
...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
|
|
3068
3083
|
...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
|
|
3069
3084
|
...(completed.acceptance ? { acceptance: completed.acceptance } : {}),
|
|
3085
|
+
...(completed.artifactPaths ? { artifactPaths: completed.artifactPaths } : {}),
|
|
3086
|
+
...(completed.outputSaveError ? { outputSaveError: completed.outputSaveError } : {}),
|
|
3087
|
+
...(completed.transcriptPath ? { transcriptPath: completed.transcriptPath } : {}),
|
|
3088
|
+
...(completed.transcriptError ? { transcriptError: completed.transcriptError } : {}),
|
|
3070
3089
|
});
|
|
3071
3090
|
return {
|
|
3072
|
-
content: [{ type: "text", text: completed.output || completed.error || `Async workflow child ${options.runId} completed without output.` }],
|
|
3091
|
+
content: [{ type: "text", text: completed.success ? completed.output || completed.error || `Async workflow child ${options.runId} completed without output.` : completed.error || completed.output || `Async workflow child ${options.runId} completed without output.` }],
|
|
3073
3092
|
...(completed.success ? {} : { isError: true }),
|
|
3074
3093
|
details: {
|
|
3075
3094
|
...launchResult.details,
|
|
@@ -3087,7 +3106,6 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3087
3106
|
ctx,
|
|
3088
3107
|
shareEnabled,
|
|
3089
3108
|
sessionRoot,
|
|
3090
|
-
sessionFileForIndex,
|
|
3091
3109
|
sessionFileForTask,
|
|
3092
3110
|
thinkingOverrideForTask,
|
|
3093
3111
|
artifactConfig,
|
|
@@ -3097,6 +3115,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3097
3115
|
intercomBridge,
|
|
3098
3116
|
nestedRoute,
|
|
3099
3117
|
contextPolicy,
|
|
3118
|
+
unknownAgentDiagnosticContext,
|
|
3100
3119
|
} = data;
|
|
3101
3120
|
const hasChain = (params.chain?.length ?? 0) > 0;
|
|
3102
3121
|
const hasTasks = (params.tasks?.length ?? 0) > 0;
|
|
@@ -3135,7 +3154,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3135
3154
|
const a = agents.find((x) => x.name === params.agent);
|
|
3136
3155
|
if (!a) {
|
|
3137
3156
|
return {
|
|
3138
|
-
content: [{ type: "text", text:
|
|
3157
|
+
content: [{ type: "text", text: formatUnknownAgentError(params.agent!, unknownAgentDiagnosticContext) }],
|
|
3139
3158
|
isError: true,
|
|
3140
3159
|
details: { mode: "single" as const, results: [] },
|
|
3141
3160
|
};
|
|
@@ -3166,6 +3185,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3166
3185
|
ctx: asyncCtx,
|
|
3167
3186
|
availableModels,
|
|
3168
3187
|
cwd: effectiveCwd,
|
|
3188
|
+
requestedCwd: data.requestedCwd,
|
|
3169
3189
|
maxOutput: params.maxOutput,
|
|
3170
3190
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
3171
3191
|
artifactConfig,
|
|
@@ -3177,6 +3197,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3177
3197
|
skills,
|
|
3178
3198
|
output: effectiveOutput,
|
|
3179
3199
|
outputMode: effectiveOutputMode,
|
|
3200
|
+
outputClaimPath: params.workflowOutputClaimPath,
|
|
3180
3201
|
...(params.reads !== undefined ? { reads: params.reads } : {}),
|
|
3181
3202
|
outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
|
|
3182
3203
|
modelOverride,
|
|
@@ -3186,6 +3207,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3186
3207
|
thinkingCeiling: a.maxThinking,
|
|
3187
3208
|
maxSubagentDepth,
|
|
3188
3209
|
waitToolEnabled: deps.waitToolEnabled,
|
|
3210
|
+
waitToolDefaultTimeoutMs: deps.waitToolDefaultTimeoutMs,
|
|
3189
3211
|
...(params.worktree === true ? { worktree: true } : {}),
|
|
3190
3212
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
3191
3213
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
@@ -3200,7 +3222,6 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3200
3222
|
extensionBindings: params.extensionBindings,
|
|
3201
3223
|
acceptance: params.acceptance,
|
|
3202
3224
|
timeoutMs: data.timeoutMs,
|
|
3203
|
-
turnBudget: data.turnBudget,
|
|
3204
3225
|
toolBudget: data.toolBudget,
|
|
3205
3226
|
usageBudget: data.usageBudget,
|
|
3206
3227
|
configToolBudget: data.configToolBudget,
|
|
@@ -3210,6 +3231,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3210
3231
|
runFanoutBudget: data.runFanoutBudget,
|
|
3211
3232
|
parentWorkflowRunId: params.workflowParentRunId,
|
|
3212
3233
|
workflowKey: params.workflowKey,
|
|
3234
|
+
lane: params.lane,
|
|
3213
3235
|
workflowAwaitAsync: params.workflowAwaitAsync,
|
|
3214
3236
|
}));
|
|
3215
3237
|
return waitForWorkflowAsyncSingleResult(params, asyncResult, { runId: id, task: params.task ?? "", signal: data.signal, state: deps.state, kill: deps.kill });
|
|
@@ -3226,6 +3248,7 @@ function createSingleWorktreeSetup(
|
|
|
3226
3248
|
setupHook: ExtensionConfig["worktreeSetupHook"],
|
|
3227
3249
|
setupHookTimeoutMs: ExtensionConfig["worktreeSetupHookTimeoutMs"],
|
|
3228
3250
|
baseDir: ExtensionConfig["worktreeBaseDir"],
|
|
3251
|
+
beforeCreate?: (setup: WorktreeSetup) => void,
|
|
3229
3252
|
): { setup?: WorktreeSetup; errorResult?: AgentToolResult<Details> } {
|
|
3230
3253
|
if (!enabled) return {};
|
|
3231
3254
|
try {
|
|
@@ -3236,6 +3259,7 @@ function createSingleWorktreeSetup(
|
|
|
3236
3259
|
? { hookPath: setupHook, ...(setupHookTimeoutMs === undefined ? {} : { timeoutMs: setupHookTimeoutMs }) }
|
|
3237
3260
|
: undefined,
|
|
3238
3261
|
baseDir,
|
|
3262
|
+
beforeCreate,
|
|
3239
3263
|
})),
|
|
3240
3264
|
};
|
|
3241
3265
|
} catch (error) {
|
|
@@ -3278,10 +3302,30 @@ function workflowChildDefaultOutput(aggregateOutputPath: string | undefined, art
|
|
|
3278
3302
|
return path.join(artifactsDir, "outputs", sanitizeRunPathSegment(workflowRunId), `${workflowKey}.md`);
|
|
3279
3303
|
}
|
|
3280
3304
|
|
|
3305
|
+
function workflowHostCommandRunner(input: {
|
|
3306
|
+
workflowCwd: string;
|
|
3307
|
+
artifactsDir: string;
|
|
3308
|
+
workflowRunId: string;
|
|
3309
|
+
claimedOutputPaths: Map<string, string>;
|
|
3310
|
+
producedOutputPaths: Set<string>;
|
|
3311
|
+
}) {
|
|
3312
|
+
return async (key: string, params: WorkflowHostCommandParams, signal: AbortSignal): Promise<WorkflowHostCommandResult> => {
|
|
3313
|
+
const defaultOutputPath = path.join(input.artifactsDir, "outputs", sanitizeRunPathSegment(input.workflowRunId), "host", `${sanitizeRunPathSegment(key)}.log`);
|
|
3314
|
+
const outputPath = params.output ? path.resolve(input.workflowCwd, params.output) : defaultOutputPath;
|
|
3315
|
+
const claimPath = resolveWorkflowHostOutputClaimPath(outputPath);
|
|
3316
|
+
const previous = input.claimedOutputPaths.get(claimPath);
|
|
3317
|
+
if (previous) throw new Error(`runs.host('${key}') output path is already claimed by '${previous}': ${outputPath}.`);
|
|
3318
|
+
input.claimedOutputPaths.set(claimPath, `host:${key}`);
|
|
3319
|
+
const result = await executeWorkflowHostCommand({ key, params, cwd: input.workflowCwd, defaultOutputPath, claimedOutputPath: claimPath, signal });
|
|
3320
|
+
input.producedOutputPaths.add(resolveWorkflowHostOutputClaimPath(result.outputPath));
|
|
3321
|
+
return result;
|
|
3322
|
+
};
|
|
3323
|
+
}
|
|
3324
|
+
|
|
3281
3325
|
function writeWorkflowAggregateOutput(outputPath: string | undefined, text: string, producedChildOutputPaths: ReadonlySet<string>): string | undefined {
|
|
3282
3326
|
if (!outputPath) return undefined;
|
|
3283
3327
|
try {
|
|
3284
|
-
if (producedChildOutputPaths.has(outputPath)) return undefined;
|
|
3328
|
+
if (producedChildOutputPaths.has(resolveWorkflowHostOutputClaimPath(outputPath))) return undefined;
|
|
3285
3329
|
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
3286
3330
|
fs.writeFileSync(outputPath, text, "utf-8");
|
|
3287
3331
|
return undefined;
|
|
@@ -3352,7 +3396,7 @@ function workflowChildOutputClaims(input: {
|
|
|
3352
3396
|
state: SubagentState;
|
|
3353
3397
|
claimedOutputPaths: Map<string, string>;
|
|
3354
3398
|
entries: Array<{ key: string; params: Record<string, unknown> }>;
|
|
3355
|
-
}): { error?: string; claims?: Map<string, string>; overrides?: Map<string, string> } {
|
|
3399
|
+
}): { error?: string; claims?: Map<string, string>; childClaims?: Map<string, string>; overrides?: Map<string, string> } {
|
|
3356
3400
|
const resolvedEntries = input.entries.map(({ key, params }) => ({
|
|
3357
3401
|
key,
|
|
3358
3402
|
...resolveWorkflowChildOutputPath({ ...input, key, params }),
|
|
@@ -3360,24 +3404,30 @@ function workflowChildOutputClaims(input: {
|
|
|
3360
3404
|
const paths = new Map<string, number>();
|
|
3361
3405
|
for (const claimedPath of input.claimedOutputPaths.keys()) paths.set(claimedPath, 1);
|
|
3362
3406
|
for (const { path: resolved } of resolvedEntries) {
|
|
3363
|
-
if (resolved)
|
|
3407
|
+
if (resolved) {
|
|
3408
|
+
const claimPath = resolveWorkflowHostOutputClaimPath(resolved);
|
|
3409
|
+
paths.set(claimPath, (paths.get(claimPath) ?? 0) + 1);
|
|
3410
|
+
}
|
|
3364
3411
|
}
|
|
3365
3412
|
const overrides = new Map<string, string>();
|
|
3366
3413
|
for (const entry of resolvedEntries) {
|
|
3367
|
-
if (entry.inherited && entry.path && (paths.get(entry.path) ?? 0) > 1) {
|
|
3414
|
+
if (entry.inherited && entry.path && (paths.get(resolveWorkflowHostOutputClaimPath(entry.path)) ?? 0) > 1) {
|
|
3368
3415
|
const output = workflowChildDefaultOutput(input.aggregateOutputPath, input.artifactsDir, input.workflowRunId, entry.key);
|
|
3369
3416
|
overrides.set(entry.key, output);
|
|
3370
3417
|
entry.path = output;
|
|
3371
3418
|
}
|
|
3372
3419
|
}
|
|
3373
3420
|
const claims = new Map(input.claimedOutputPaths);
|
|
3421
|
+
const childClaims = new Map<string, string>();
|
|
3374
3422
|
for (const { key, path: resolved } of resolvedEntries) {
|
|
3375
3423
|
if (!resolved) continue;
|
|
3376
|
-
const
|
|
3424
|
+
const claimPath = resolveWorkflowHostOutputClaimPath(resolved);
|
|
3425
|
+
const previous = claims.get(claimPath);
|
|
3377
3426
|
if (previous) return { error: `Workflow children '${previous}' and '${key}' resolve output to the same path: ${resolved}. Use distinct child output paths.` };
|
|
3378
|
-
claims.set(
|
|
3427
|
+
claims.set(claimPath, key);
|
|
3428
|
+
childClaims.set(key, claimPath);
|
|
3379
3429
|
}
|
|
3380
|
-
return { claims, overrides };
|
|
3430
|
+
return { claims, childClaims, overrides };
|
|
3381
3431
|
}
|
|
3382
3432
|
|
|
3383
3433
|
function applyWorkflowChildOutputClaims(target: Map<string, string>, claims: Map<string, string>): void {
|
|
@@ -3399,12 +3449,14 @@ function prepareWorkflowChildLaunchParams(input: {
|
|
|
3399
3449
|
agents: AgentConfig[];
|
|
3400
3450
|
workflowAgentScope?: unknown;
|
|
3401
3451
|
outputOverride?: string;
|
|
3402
|
-
|
|
3452
|
+
outputClaimPath?: string;
|
|
3453
|
+
options?: { missionDetached?: boolean; suppressRoutineResultIntercom?: boolean; awaitDetachedChild?: boolean; runFanoutBudget?: RunFanoutBudgetDescriptor; parentDeadlineAt?: number; capabilityCeiling?: ResolvedSubagentCapabilityCeiling };
|
|
3403
3454
|
}): SubagentParamsLike {
|
|
3404
3455
|
let childParams = input.childParams;
|
|
3405
|
-
|
|
3456
|
+
const usesDefaultOutput = input.childParams.output === undefined && input.childParams.resume === undefined;
|
|
3457
|
+
if (usesDefaultOutput && input.outputOverride !== undefined) {
|
|
3406
3458
|
childParams = { ...input.childParams, output: input.outputOverride };
|
|
3407
|
-
} else if (
|
|
3459
|
+
} else if (usesDefaultOutput && input.aggregateOutputPath !== undefined) {
|
|
3408
3460
|
childParams = { ...input.childParams, output: workflowChildDefaultOutput(input.aggregateOutputPath, input.artifactsDir, input.parentWorkflowRunId, input.workflowKey) };
|
|
3409
3461
|
} else if (input.childParams.resume === undefined) {
|
|
3410
3462
|
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 });
|
|
@@ -3417,7 +3469,7 @@ function prepareWorkflowChildLaunchParams(input: {
|
|
|
3417
3469
|
? resolveAgentName(childParams.agent, discoveredAgents).agent ?? resolveAgentName(childParams.agent, input.agents).agent
|
|
3418
3470
|
: undefined;
|
|
3419
3471
|
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 });
|
|
3472
|
+
return prepareWorkflowLaunchParams(input.workflowDefaults, childParams, input.parentWorkflowRunId, input.workflowKey, { ...input.options, externalAsyncRequired, outputClaimPath: input.outputClaimPath });
|
|
3421
3473
|
}
|
|
3422
3474
|
|
|
3423
3475
|
function finalizeSingleWorktreeHandoff(input: {
|
|
@@ -3427,6 +3479,8 @@ function finalizeSingleWorktreeHandoff(input: {
|
|
|
3427
3479
|
cwd: string;
|
|
3428
3480
|
agent: string;
|
|
3429
3481
|
result: SingleResult;
|
|
3482
|
+
workflowKey?: string;
|
|
3483
|
+
lane?: import("../../shared/types.ts").WorkflowLaneMetadata;
|
|
3430
3484
|
}): { suffix: string; reference?: NonNullable<Details["parallelHandoff"]> } {
|
|
3431
3485
|
const diffsDir = path.join(input.artifactsDir, "worktree-diffs", input.runId);
|
|
3432
3486
|
const diffs = diffWorktrees(input.worktreeSetup, [input.agent], diffsDir);
|
|
@@ -3441,9 +3495,13 @@ function finalizeSingleWorktreeHandoff(input: {
|
|
|
3441
3495
|
stepIndex: 0,
|
|
3442
3496
|
flatStartIndex: 0,
|
|
3443
3497
|
setup: input.worktreeSetup,
|
|
3498
|
+
laneBindings: input.workflowKey || input.lane ? [{ index: 0, taskIndex: 0, ...(input.workflowKey ? { workflowKey: input.workflowKey } : {}), runId: input.workflowKey ? input.runId : undefined, ...(input.lane ? { lane: input.lane } : {}) }] : undefined,
|
|
3444
3499
|
diffs,
|
|
3445
3500
|
results: [{
|
|
3446
3501
|
agent: input.result.agent,
|
|
3502
|
+
...(input.workflowKey ? { workflowKey: input.workflowKey } : {}),
|
|
3503
|
+
...(input.workflowKey ? { runId: input.runId } : {}),
|
|
3504
|
+
...(input.lane ? { lane: input.lane } : {}),
|
|
3447
3505
|
status: resolveSubagentResultStatus(omitUndefinedProperties({
|
|
3448
3506
|
exitCode: input.result.exitCode,
|
|
3449
3507
|
interrupted: input.result.interrupted,
|
|
@@ -3489,10 +3547,16 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3489
3547
|
artifactConfig,
|
|
3490
3548
|
artifactsDir,
|
|
3491
3549
|
onUpdate,
|
|
3492
|
-
sessionRoot,
|
|
3493
3550
|
controlConfig,
|
|
3494
3551
|
contextPolicy,
|
|
3495
3552
|
} = data;
|
|
3553
|
+
let lane: import("../../shared/types.ts").WorkflowLaneMetadata | undefined;
|
|
3554
|
+
try {
|
|
3555
|
+
lane = normalizeWorkflowLaneMetadata(params.lane, "lane");
|
|
3556
|
+
assertWorkflowLaneKey(lane, params.workflowKey, "lane");
|
|
3557
|
+
} catch (error) {
|
|
3558
|
+
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "single", results: [] } };
|
|
3559
|
+
}
|
|
3496
3560
|
const onControlEvent = createForegroundControlNotifier(data, deps);
|
|
3497
3561
|
const childBridgeActive = intercomBridgeAppliesToAgent(data.intercomBridge, contextPolicy, params.agent!);
|
|
3498
3562
|
const childIntercomTarget = childBridgeActive ? resolveSubagentIntercomTarget(runId, params.agent!, 0) : undefined;
|
|
@@ -3501,7 +3565,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3501
3565
|
const agentConfig = agents.find((a) => a.name === params.agent);
|
|
3502
3566
|
if (!agentConfig) {
|
|
3503
3567
|
return {
|
|
3504
|
-
content: [{ type: "text", text:
|
|
3568
|
+
content: [{ type: "text", text: formatUnknownAgentError(params.agent!, data.unknownAgentDiagnosticContext) }],
|
|
3505
3569
|
isError: true,
|
|
3506
3570
|
details: { mode: "single", results: [] },
|
|
3507
3571
|
};
|
|
@@ -3513,7 +3577,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3513
3577
|
const currentProvider = parentModel?.provider;
|
|
3514
3578
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
3515
3579
|
const modelScopes = resolveModelScopesForAgent(data.modelScope, agentConfig.name, parentModel);
|
|
3516
|
-
let task = params.task
|
|
3580
|
+
let task = typeof params.task === "string" ? params.task : "";
|
|
3517
3581
|
let modelOverride: string | undefined = resolveEffectiveSubagentModel(
|
|
3518
3582
|
params.model as string | undefined,
|
|
3519
3583
|
agentConfig.model,
|
|
@@ -3533,6 +3597,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3533
3597
|
|
|
3534
3598
|
|
|
3535
3599
|
const sourceCwd = effectiveCwd;
|
|
3600
|
+
let pendingHandoff: Details["parallelHandoff"];
|
|
3536
3601
|
const { setup: worktreeSetup, errorResult: worktreeSetupError } = createSingleWorktreeSetup(
|
|
3537
3602
|
params.worktree,
|
|
3538
3603
|
sourceCwd,
|
|
@@ -3541,12 +3606,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3541
3606
|
deps.config.worktreeSetupHook,
|
|
3542
3607
|
deps.config.worktreeSetupHookTimeoutMs,
|
|
3543
3608
|
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 {
|
|
3609
|
+
(plannedSetup) => {
|
|
3550
3610
|
pendingHandoff = writePendingParallelHandoff({
|
|
3551
3611
|
manifestPath: parallelHandoffPath(artifactsDir, runId),
|
|
3552
3612
|
runId,
|
|
@@ -3555,13 +3615,13 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3555
3615
|
cwd: sourceCwd,
|
|
3556
3616
|
stepIndex: 0,
|
|
3557
3617
|
flatStartIndex: 0,
|
|
3558
|
-
setup:
|
|
3618
|
+
setup: plannedSetup,
|
|
3619
|
+
laneBindings: params.workflowKey || lane ? [{ index: 0, taskIndex: 0, ...(params.workflowKey ? { workflowKey: params.workflowKey, runId } : {}), ...(lane ? { lane } : {}) }] : undefined,
|
|
3559
3620
|
});
|
|
3560
|
-
}
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
}
|
|
3621
|
+
},
|
|
3622
|
+
);
|
|
3623
|
+
if (worktreeSetupError) return worktreeSetupError;
|
|
3624
|
+
const singleCwd = worktreeSetup?.worktrees[0]?.agentCwd ?? sourceCwd;
|
|
3565
3625
|
|
|
3566
3626
|
const authoredTask = task;
|
|
3567
3627
|
if (shouldForkAgent(contextPolicy, params.agent!)) {
|
|
@@ -3627,15 +3687,22 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3627
3687
|
|
|
3628
3688
|
const deadlineAt = data.deadlineAt ?? (data.timeoutMs !== undefined ? Date.now() + data.timeoutMs : undefined);
|
|
3629
3689
|
let r: Awaited<ReturnType<typeof runSync>> | undefined;
|
|
3690
|
+
let resolveDetachedWorkflowChild: ((result: Awaited<ReturnType<typeof runSync>>) => void) | undefined;
|
|
3691
|
+
const detachedWorkflowChild = params.workflowAwaitDetached === true
|
|
3692
|
+
? new Promise<Awaited<ReturnType<typeof runSync>>>((resolve) => { resolveDetachedWorkflowChild = resolve; })
|
|
3693
|
+
: undefined;
|
|
3630
3694
|
try {
|
|
3631
|
-
|
|
3695
|
+
const launched = await runSync(ctx.cwd, agents, params.agent!, task, compactOptional<Parameters<typeof runSync>[4]>({
|
|
3632
3696
|
permissions: deps.config.permissions,
|
|
3697
|
+
runtimeSnapshotHost: deps.pi,
|
|
3633
3698
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
3634
3699
|
llmIntentArbiter: createTaskMutationArbiter(ctx),
|
|
3635
3700
|
...workflowForegroundSteeringLaunchOptions(foregroundControl, 0),
|
|
3636
3701
|
context: data.contextPolicy.contextForAgent(params.agent!),
|
|
3702
|
+
unknownAgentDiagnosticContext: data.unknownAgentDiagnosticContext,
|
|
3637
3703
|
runFanoutBudget: params.runFanoutAdmitted ? data.runFanoutBudget : { ...data.runFanoutBudget, parentPath: `${data.runFanoutBudget.parentPath ? `${data.runFanoutBudget.parentPath}/` : ""}single` },
|
|
3638
3704
|
cwd: singleCwd,
|
|
3705
|
+
requestedCwd: data.requestedCwd,
|
|
3639
3706
|
signal,
|
|
3640
3707
|
interruptSignal: interruptController.signal,
|
|
3641
3708
|
allowIntercomDetach: agentConfig.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
|
|
@@ -3648,9 +3715,11 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3648
3715
|
artifactConfig,
|
|
3649
3716
|
maxOutput: params.maxOutput,
|
|
3650
3717
|
outputPath,
|
|
3718
|
+
outputClaimPath: params.workflowOutputClaimPath,
|
|
3651
3719
|
outputMode: effectiveOutputMode,
|
|
3652
3720
|
maxSubagentDepth,
|
|
3653
3721
|
waitToolEnabled: deps.waitToolEnabled,
|
|
3722
|
+
waitToolDefaultTimeoutMs: deps.waitToolDefaultTimeoutMs,
|
|
3654
3723
|
onUpdate: forwardSingleUpdate,
|
|
3655
3724
|
controlConfig,
|
|
3656
3725
|
onControlEvent,
|
|
@@ -3672,12 +3741,20 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3672
3741
|
agentContract: params.agentContract,
|
|
3673
3742
|
acceptance: params.acceptance,
|
|
3674
3743
|
acceptanceContext: { mode: "single" },
|
|
3744
|
+
workflowChildPermitLaunch: data.workflowChildPermitLaunch,
|
|
3675
3745
|
onEffectivePrompt: foregroundControl ? (prompt) => updateLiveEffectivePrompt(foregroundControl, 0, prompt) : undefined,
|
|
3676
3746
|
onDetachReady: (detach) => {
|
|
3677
3747
|
detachForeground = detach;
|
|
3678
3748
|
},
|
|
3679
3749
|
onDetachedExit: (result) => {
|
|
3750
|
+
if (resolveDetachedWorkflowChild) {
|
|
3751
|
+
resolveDetachedWorkflowChild(result);
|
|
3752
|
+
return;
|
|
3753
|
+
}
|
|
3680
3754
|
try {
|
|
3755
|
+
if (worktreeSetup) {
|
|
3756
|
+
finalizeSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd, agent: params.agent!, result, workflowKey: params.workflowKey, lane });
|
|
3757
|
+
}
|
|
3681
3758
|
try {
|
|
3682
3759
|
updateRememberedForegroundChild(deps.state, { runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, index: 0, result, events: deps.pi.events, notify: true });
|
|
3683
3760
|
} catch {
|
|
@@ -3705,18 +3782,17 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3705
3782
|
}
|
|
3706
3783
|
}
|
|
3707
3784
|
}
|
|
3708
|
-
recordRun(params.agent!, cleanTask, result.exitCode, result.progressSummary?.durationMs ?? 0);
|
|
3785
|
+
recordRun(params.agent!, cleanTask, result.exitCode, result.progressSummary?.durationMs ?? 0, result);
|
|
3709
3786
|
},
|
|
3710
3787
|
timeoutMs: data.timeoutMs,
|
|
3711
3788
|
deadlineAt,
|
|
3712
3789
|
toolTimeoutMs: params.toolTimeoutMs,
|
|
3713
3790
|
configToolTimeoutMs: data.configToolTimeoutMs,
|
|
3714
|
-
turnBudget: data.turnBudget,
|
|
3715
|
-
enforceHardTurnLimit: params.enforceHardTurnLimit,
|
|
3716
3791
|
toolBudget: effectiveToolBudget.toolBudget,
|
|
3717
3792
|
capabilityCeiling: data.capabilityCeiling,
|
|
3718
3793
|
allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
|
|
3719
3794
|
}));
|
|
3795
|
+
r = launched.detached && detachedWorkflowChild ? await detachedWorkflowChild : launched;
|
|
3720
3796
|
} catch (error) {
|
|
3721
3797
|
if (worktreeSetup) cleanupWorktrees(worktreeSetup);
|
|
3722
3798
|
throw error;
|
|
@@ -3730,14 +3806,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3730
3806
|
}
|
|
3731
3807
|
}
|
|
3732
3808
|
if (!r.detached) {
|
|
3733
|
-
recordRun(params.agent!, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0);
|
|
3809
|
+
recordRun(params.agent!, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0, r);
|
|
3734
3810
|
}
|
|
3735
3811
|
|
|
3736
3812
|
let worktreeHandoff: ReturnType<typeof finalizeSingleWorktreeHandoff> | undefined;
|
|
3737
3813
|
if (worktreeSetup) {
|
|
3738
3814
|
worktreeHandoff = r.detached
|
|
3739
3815
|
? { suffix: pendingHandoff ? formatParallelHandoffReference(pendingHandoff) : "", reference: pendingHandoff }
|
|
3740
|
-
: finalizeSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd, agent: params.agent!, result: r });
|
|
3816
|
+
: finalizeSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd, agent: params.agent!, result: r, workflowKey: params.workflowKey, lane });
|
|
3741
3817
|
}
|
|
3742
3818
|
|
|
3743
3819
|
if (r.progress) allProgress.push(r.progress);
|
|
@@ -3766,7 +3842,6 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3766
3842
|
runId,
|
|
3767
3843
|
timeoutMs: data.timeoutMs,
|
|
3768
3844
|
results: [r],
|
|
3769
|
-
...(data.turnBudget ? { turnBudget: data.turnBudget } : {}),
|
|
3770
3845
|
...(effectiveToolBudget.toolBudget ? { toolBudget: effectiveToolBudget.toolBudget } : {}),
|
|
3771
3846
|
progress: params.includeProgress ? allProgress : undefined,
|
|
3772
3847
|
artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
|
|
@@ -3776,7 +3851,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3776
3851
|
usageBudget: usageBudgetState(data.usageBudget, totalCost),
|
|
3777
3852
|
...(worktreeHandoff?.reference ? { parallelHandoff: worktreeHandoff.reference } : {}),
|
|
3778
3853
|
}));
|
|
3779
|
-
rememberForegroundRun(deps.state, { runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: details.results, extensionBindings: params.extensionBindings });
|
|
3854
|
+
rememberForegroundRun(deps.state, { runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: details.results, params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings });
|
|
3780
3855
|
|
|
3781
3856
|
const suppressRoutineResultIntercom = shouldSuppressRoutineResultIntercom({ suppressRoutineResultIntercom: params.suppressRoutineResultIntercom, results: [r] });
|
|
3782
3857
|
if (!r.detached && !r.interrupted && !suppressRoutineResultIntercom) {
|
|
@@ -3851,7 +3926,7 @@ function duplicateSubagentCallResult(params: SubagentParamsLike): AgentToolResul
|
|
|
3851
3926
|
};
|
|
3852
3927
|
}
|
|
3853
3928
|
|
|
3854
|
-
const workflowLaunchObservers = new WeakMap<object, (launch: { agent: string; sessionFile?: string; async: boolean; runId?: string }) => void>();
|
|
3929
|
+
const workflowLaunchObservers = new WeakMap<object, (launch: { agent: string; sessionName?: string; sessionFile?: string; async: boolean; runId?: string }) => void>();
|
|
3855
3930
|
|
|
3856
3931
|
/**
|
|
3857
3932
|
* Terminal-mission retention can remove a mission while its children still
|
|
@@ -3923,16 +3998,27 @@ export function bindMissionWorkflowChildAsyncLaunch(
|
|
|
3923
3998
|
function workflowChildResult(
|
|
3924
3999
|
key: string,
|
|
3925
4000
|
result: AgentToolResult<Details>,
|
|
3926
|
-
childParams: Record<string, unknown> = {},
|
|
4001
|
+
childParams: SubagentParamsLike | Record<string, unknown> = {},
|
|
3927
4002
|
resumeState?: SubagentState,
|
|
4003
|
+
forcedTerminalOutcome?: WorkflowTerminalOutcome,
|
|
3928
4004
|
): WorkflowScriptChildResult {
|
|
3929
4005
|
const receiptOutput = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n");
|
|
3930
4006
|
const output = result.details.results.length === 1 && result.details.results[0]?.finalOutput !== undefined
|
|
3931
4007
|
? result.details.results[0].finalOutput
|
|
3932
4008
|
: receiptOutput;
|
|
4009
|
+
const childError = result.details.results.map((child) => child.error).find((error): error is string => Boolean(error));
|
|
4010
|
+
const failureError = childError && receiptOutput && receiptOutput !== childError
|
|
4011
|
+
? `${childError}\n\n${receiptOutput}`
|
|
4012
|
+
: childError || receiptOutput || output || "Child run failed.";
|
|
3933
4013
|
const detached = result.details.results.some((child) => child.detached);
|
|
3934
4014
|
const interrupted = result.details.results.some((child) => child.interrupted);
|
|
3935
4015
|
const stopped = result.details.results.some((child) => child.stopped);
|
|
4016
|
+
const terminalOutcome = forcedTerminalOutcome
|
|
4017
|
+
?? (result.details.results.some((child) => child.timedOut)
|
|
4018
|
+
? { state: "partial" as const, reason: "timeout" as const }
|
|
4019
|
+
: result.details.usageBudget?.exhausted || result.details.results.some((child) => child.turnBudgetExceeded || child.toolBudgetBlocked)
|
|
4020
|
+
? { state: "partial" as const, reason: "budget_exhausted" as const }
|
|
4021
|
+
: undefined);
|
|
3936
4022
|
const ok = result.isError !== true && !detached && !interrupted && !stopped;
|
|
3937
4023
|
const artifactPaths = new Set<string>();
|
|
3938
4024
|
if (result.details.asyncDir) artifactPaths.add(result.details.asyncDir);
|
|
@@ -3965,13 +4051,25 @@ function workflowChildResult(
|
|
|
3965
4051
|
const continuationRunIds = [...new Set([resumeSourceRunId, runId].filter((value): value is string => Boolean(value)))];
|
|
3966
4052
|
const outputReference = result.details.results.find((child) => child.savedOutputPath)?.savedOutputPath
|
|
3967
4053
|
?? result.details.results.find((child) => child.outputReference?.path)?.outputReference?.path;
|
|
4054
|
+
const outputPathMapping = typeof childParams.task === "string" ? outputPathMappingFromTask(childParams.task, outputReference) : undefined;
|
|
4055
|
+
const externalResult = result.details.results.length === 1 && result.details.results[0]?.runner?.type === "external-cli" ? result.details.results[0] : undefined;
|
|
4056
|
+
const externalStatus = result.details.asyncDir ? readStatus(result.details.asyncDir) : undefined;
|
|
4057
|
+
const externalStatusStep = externalStatus?.steps?.length === 1 && externalStatus.steps[0]?.runner?.type === "external-cli" ? externalStatus.steps[0] : undefined;
|
|
4058
|
+
const externalRunner = normalizeExternalCliRunnerStatus(externalResult?.runner ?? externalStatusStep?.runner);
|
|
4059
|
+
const externalProcess = externalResult?.externalProcess ?? externalStatusStep?.externalProcess;
|
|
4060
|
+
const externalAdapter = externalRunner ? externalCliReceiptMetadata({ runner: externalRunner, externalProcess, outputReference }) : undefined;
|
|
4061
|
+
if (externalAdapter) resumability = { state: "not-resumable", reason: externalAdapter.nonResumableReason };
|
|
4062
|
+
const lane = normalizeWorkflowLaneMetadata(childParams.lane, `workflow child '${key}'.lane`);
|
|
4063
|
+
assertWorkflowLaneKey(lane, key, `workflow child '${key}'.lane`);
|
|
3968
4064
|
return {
|
|
3969
4065
|
key,
|
|
3970
4066
|
ok,
|
|
4067
|
+
...(lane ? { lane } : {}),
|
|
4068
|
+
...(terminalOutcome ? { terminalOutcome } : {}),
|
|
3971
4069
|
...(resolvedAgents.length === 1 ? { agent: resolvedAgents[0] } : {}),
|
|
3972
4070
|
...(runId ? { runId } : {}),
|
|
3973
4071
|
output,
|
|
3974
|
-
...(!ok ? { error:
|
|
4072
|
+
...(!ok ? { error: failureError } : {}),
|
|
3975
4073
|
...(detached ? { detached: true } : {}),
|
|
3976
4074
|
...(interrupted ? { interrupted: true } : {}),
|
|
3977
4075
|
...(stopped ? { stopped: true } : {}),
|
|
@@ -3979,6 +4077,8 @@ function workflowChildResult(
|
|
|
3979
4077
|
...(requestedContext ? { requestedContext } : {}),
|
|
3980
4078
|
...(resolvedContext ? { resolvedContext } : {}),
|
|
3981
4079
|
...(outputReference ? { outputReference } : {}),
|
|
4080
|
+
...(outputPathMapping ? { outputPathMapping } : {}),
|
|
4081
|
+
...(externalAdapter ? { externalAdapter } : {}),
|
|
3982
4082
|
resumability,
|
|
3983
4083
|
continuation: { runIds: continuationRunIds },
|
|
3984
4084
|
artifactPaths: [...artifactPaths],
|
|
@@ -3986,6 +4086,23 @@ function workflowChildResult(
|
|
|
3986
4086
|
};
|
|
3987
4087
|
}
|
|
3988
4088
|
|
|
4089
|
+
function workflowChildAccountingFields(child: WorkflowScriptChildResult): { usage?: Usage; sessionFile?: string } {
|
|
4090
|
+
if (!child.results?.length) return {};
|
|
4091
|
+
const usage = sumResultsUsage(child.results);
|
|
4092
|
+
const sessionFile = child.results.find((result) => result.sessionFile)?.sessionFile;
|
|
4093
|
+
return {
|
|
4094
|
+
...(usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0 ? { usage } : {}),
|
|
4095
|
+
...(sessionFile ? { sessionFile } : {}),
|
|
4096
|
+
};
|
|
4097
|
+
}
|
|
4098
|
+
|
|
4099
|
+
function workflowOutputPathMappingSummary(children: WorkflowScriptChildResult[]): string {
|
|
4100
|
+
const mappings = children.flatMap((child) => child.outputPathMapping
|
|
4101
|
+
? [`'${child.key}': requested ${child.outputPathMapping.requestedPath} -> saved ${child.outputPathMapping.savedPath}`]
|
|
4102
|
+
: []);
|
|
4103
|
+
return mappings.length > 0 ? ` Output path mappings: ${mappings.join("; ")}.` : "";
|
|
4104
|
+
}
|
|
4105
|
+
|
|
3989
4106
|
function workflowSteerReceipt(key: string, result: AgentToolResult<Details>): WorkflowSteerResult {
|
|
3990
4107
|
const steering = result.details.steering;
|
|
3991
4108
|
const error = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n") || undefined;
|
|
@@ -4003,29 +4120,122 @@ function workflowSteerReceipt(key: string, result: AgentToolResult<Details>): Wo
|
|
|
4003
4120
|
};
|
|
4004
4121
|
}
|
|
4005
4122
|
|
|
4123
|
+
const MAX_WORKFLOW_RESUME_HINT_BYTES = 1024;
|
|
4124
|
+
const MAX_WORKFLOW_CHILD_RUN_ID_BYTES = 256;
|
|
4125
|
+
const WORKFLOW_RESUME_HINT_PARENT_STATES = new Set(["complete", "failed", "partial"]);
|
|
4126
|
+
const WORKFLOW_STEP_STATES = new Set(["pending", "running", "complete", "completed", "failed", "partial", "paused", "stopped", "rejected"]);
|
|
4127
|
+
|
|
4128
|
+
function isSafeWorkflowChildRunId(value: unknown): value is string {
|
|
4129
|
+
if (typeof value !== "string") return false;
|
|
4130
|
+
const runId = value.trim();
|
|
4131
|
+
return value === runId
|
|
4132
|
+
&& Boolean(runId)
|
|
4133
|
+
&& Buffer.byteLength(runId, "utf8") <= MAX_WORKFLOW_CHILD_RUN_ID_BYTES
|
|
4134
|
+
&& path.basename(runId) === runId
|
|
4135
|
+
&& path.win32.basename(runId) === runId
|
|
4136
|
+
&& !/[\\/]/.test(runId)
|
|
4137
|
+
&& !runId.includes("..")
|
|
4138
|
+
&& !/[\u0000-\u001f\u007f]/.test(runId);
|
|
4139
|
+
}
|
|
4140
|
+
|
|
4141
|
+
function isMissingWorkflowReceiptDiagnostic(error: unknown, workflowRunId: string): error is Error {
|
|
4142
|
+
return error instanceof Error
|
|
4143
|
+
&& error.message.startsWith(`Workflow receipt '${workflowRunId}' is not available because the workflow may still be active or terminal receipt writing failed.`);
|
|
4144
|
+
}
|
|
4145
|
+
|
|
4146
|
+
function missingWorkflowReceiptResumeHint(reference: WorkflowReceiptResumeReference, state: SubagentState): string | undefined {
|
|
4147
|
+
try {
|
|
4148
|
+
if (!state.currentSessionId) return undefined;
|
|
4149
|
+
const workflowRunId = reference.workflowRunId.trim();
|
|
4150
|
+
const workflowStatus = readStatus(path.join(DIRS.async, workflowRunId));
|
|
4151
|
+
if (!workflowStatus
|
|
4152
|
+
|| workflowStatus.runId !== workflowRunId
|
|
4153
|
+
|| workflowStatus.mode !== "workflow"
|
|
4154
|
+
|| workflowStatus.sessionId !== state.currentSessionId
|
|
4155
|
+
|| typeof workflowStatus.startedAt !== "number"
|
|
4156
|
+
|| !Number.isFinite(workflowStatus.startedAt)
|
|
4157
|
+
|| !WORKFLOW_RESUME_HINT_PARENT_STATES.has(workflowStatus.state)
|
|
4158
|
+
|| !Array.isArray(workflowStatus.steps)
|
|
4159
|
+
|| workflowStatus.steps.some((step) => {
|
|
4160
|
+
if (!step || typeof step !== "object" || Array.isArray(step)) return true;
|
|
4161
|
+
const record = step as Record<string, unknown>;
|
|
4162
|
+
return typeof record.agent !== "string"
|
|
4163
|
+
|| typeof record.status !== "string"
|
|
4164
|
+
|| !WORKFLOW_STEP_STATES.has(record.status)
|
|
4165
|
+
|| (record.workflowKey !== undefined && typeof record.workflowKey !== "string")
|
|
4166
|
+
|| (record.runId !== undefined && typeof record.runId !== "string");
|
|
4167
|
+
})) return undefined;
|
|
4168
|
+
const matchingSteps = workflowStatus.steps.filter((step) => step.workflowKey === reference.key);
|
|
4169
|
+
if (matchingSteps.length !== 1) return undefined;
|
|
4170
|
+
const childRunId = matchingSteps[0]?.runId;
|
|
4171
|
+
if (!isSafeWorkflowChildRunId(childRunId)) return undefined;
|
|
4172
|
+
|
|
4173
|
+
const workflowChildren = parseWorkflowChildSummary(workflowStatus.workflowChildren);
|
|
4174
|
+
if (workflowChildren) {
|
|
4175
|
+
if (workflowChildren.workflowRunId !== workflowRunId) return undefined;
|
|
4176
|
+
const matchingChildren = workflowChildren.children.filter((child) => child.childId === reference.key);
|
|
4177
|
+
if (matchingChildren.length > 1 || (workflowChildren.inventoryComplete && matchingChildren.length !== 1)) return undefined;
|
|
4178
|
+
if (matchingChildren.length === 1 && matchingChildren[0]?.runId !== childRunId) return undefined;
|
|
4179
|
+
}
|
|
4180
|
+
|
|
4181
|
+
const target = resolveResumeTarget({ id: childRunId }, state, { asyncRequireSessionFile: true, exactOnly: true });
|
|
4182
|
+
if (target.kind !== "revive") return undefined;
|
|
4183
|
+
const hint = `Direct resumable child for workflow key '${reference.key}': subagent({ action: "resume", id: ${JSON.stringify(childRunId)}, message: "..." })`;
|
|
4184
|
+
return Buffer.byteLength(hint, "utf8") <= MAX_WORKFLOW_RESUME_HINT_BYTES ? hint : undefined;
|
|
4185
|
+
} catch {
|
|
4186
|
+
return undefined;
|
|
4187
|
+
}
|
|
4188
|
+
}
|
|
4189
|
+
|
|
4006
4190
|
function resolveKeyedWorkflowResume(
|
|
4007
4191
|
reference: WorkflowReceiptResumeReference,
|
|
4008
4192
|
state: SubagentState,
|
|
4009
4193
|
): { runId: string; runIds: string[] } {
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4194
|
+
try {
|
|
4195
|
+
const entry = resolveWorkflowReceiptResumeEntry({
|
|
4196
|
+
reference,
|
|
4197
|
+
asyncDirRoot: DIRS.async,
|
|
4198
|
+
assertResumable(runId) {
|
|
4199
|
+
const target = resolveResumeTarget({ id: runId }, state, { asyncRequireSessionFile: true, exactOnly: true });
|
|
4200
|
+
if (target.kind !== "revive") throw new Error(`Workflow receipt child '${reference.key}' latest run '${runId}' is still running.`);
|
|
4201
|
+
},
|
|
4202
|
+
});
|
|
4203
|
+
const runId = entry.latestRunId;
|
|
4204
|
+
if (!runId) throw new Error(`Workflow receipt child '${reference.key}' has no retained run id.`);
|
|
4205
|
+
return { runId, runIds: entry.continuation.runIds };
|
|
4206
|
+
} catch (error) {
|
|
4207
|
+
const workflowRunId = reference.workflowRunId.trim();
|
|
4208
|
+
if (isMissingWorkflowReceiptDiagnostic(error, workflowRunId)) {
|
|
4209
|
+
const hint = missingWorkflowReceiptResumeHint(reference, state);
|
|
4210
|
+
if (hint) throw new Error(`${error.message} ${hint}`, { cause: error });
|
|
4211
|
+
}
|
|
4212
|
+
throw error;
|
|
4213
|
+
}
|
|
4021
4214
|
}
|
|
4022
4215
|
|
|
4023
4216
|
function terminalWorkflowReceipt(
|
|
4024
4217
|
workflowRunId: string,
|
|
4025
4218
|
state: WorkflowReceiptState,
|
|
4026
4219
|
children: WorkflowScriptChildResult[],
|
|
4220
|
+
workflowChildren?: WorkflowReceipt["workflowChildren"],
|
|
4221
|
+
terminalOutcome?: WorkflowTerminalOutcome,
|
|
4222
|
+
hostSteps?: WorkflowReceipt["hostSteps"],
|
|
4027
4223
|
): WorkflowReceipt {
|
|
4028
|
-
return buildWorkflowReceipt({ workflowRunId, state, children });
|
|
4224
|
+
return buildWorkflowReceipt({ workflowRunId, state, children, workflowChildren, terminalOutcome, hostSteps });
|
|
4225
|
+
}
|
|
4226
|
+
|
|
4227
|
+
function workflowFailureTerminalOutcome(error: unknown, _children: WorkflowScriptChildResult[], usageBudget: ReturnType<typeof usageBudgetState>): WorkflowTerminalOutcome | undefined {
|
|
4228
|
+
if (usageBudget?.exhausted) return { state: "partial", reason: "budget_exhausted" };
|
|
4229
|
+
return error instanceof WorkflowScriptError && error.errorKind === "timeout" ? { state: "partial", reason: "timeout" } : undefined;
|
|
4230
|
+
}
|
|
4231
|
+
|
|
4232
|
+
function workflowFailureMessage(error: unknown, workflowRunId: string, children: WorkflowScriptChildResult[]): string {
|
|
4233
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
4234
|
+
const validationPrefix = "workflowScript validation failed before child launch; no children launched.";
|
|
4235
|
+
if (children.length === 0 && text.includes(validationPrefix)) {
|
|
4236
|
+
return `Workflow '${workflowRunId}' validation failed before child launch; no children launched.${text.slice(text.indexOf(validationPrefix) + validationPrefix.length)}`;
|
|
4237
|
+
}
|
|
4238
|
+
return text;
|
|
4029
4239
|
}
|
|
4030
4240
|
|
|
4031
4241
|
export async function steerWorkflowChildByKey(input: {
|
|
@@ -4068,6 +4278,8 @@ export async function steerWorkflowChildByKey(input: {
|
|
|
4068
4278
|
return { key: input.key, state: "missed", error: `Workflow child '${input.key}' is ${childStatus.state}.` };
|
|
4069
4279
|
}
|
|
4070
4280
|
if (childStatus) {
|
|
4281
|
+
const unsupported = externalRunnerControlError(asyncDir, "steer");
|
|
4282
|
+
if (unsupported) return workflowSteerReceipt(input.key, unsupported);
|
|
4071
4283
|
const result = await steerAsyncRun({
|
|
4072
4284
|
state: input.state,
|
|
4073
4285
|
runId: childRunId,
|
|
@@ -4099,9 +4311,11 @@ export function prepareWorkflowLaunchParams(
|
|
|
4099
4311
|
childParams: Record<string, unknown>,
|
|
4100
4312
|
parentWorkflowRunId: string,
|
|
4101
4313
|
workflowKey: string,
|
|
4102
|
-
options: { missionDetached?: boolean; suppressRoutineResultIntercom?: boolean; runFanoutBudget?: RunFanoutBudgetDescriptor; parentDeadlineAt?: number; externalAsyncRequired?: boolean; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } = {},
|
|
4314
|
+
options: { missionDetached?: boolean; suppressRoutineResultIntercom?: boolean; awaitDetachedChild?: boolean; runFanoutBudget?: RunFanoutBudgetDescriptor; parentDeadlineAt?: number; externalAsyncRequired?: boolean; capabilityCeiling?: ResolvedSubagentCapabilityCeiling; outputClaimPath?: string } = {},
|
|
4103
4315
|
): SubagentParamsLike {
|
|
4104
4316
|
const capabilityCeiling = intersectSubagentCapabilityCeilings(workflowDefaults.capabilityCeiling, options.capabilityCeiling);
|
|
4317
|
+
const lane = normalizeWorkflowLaneMetadata(Object.hasOwn(childParams, "lane") ? childParams.lane : workflowDefaults.lane, `workflow child '${workflowKey}'.lane`);
|
|
4318
|
+
assertWorkflowLaneKey(lane, workflowKey, `workflow child '${workflowKey}'.lane`);
|
|
4105
4319
|
const parentTimeoutMs = options.parentDeadlineAt === undefined
|
|
4106
4320
|
|| childParams.timeoutMs !== undefined
|
|
4107
4321
|
|| childParams.maxRuntimeMs !== undefined
|
|
@@ -4117,10 +4331,18 @@ export function prepareWorkflowLaunchParams(
|
|
|
4117
4331
|
throw new Error("gate is not supported with retained resume; resume uses the retained child contract.");
|
|
4118
4332
|
}
|
|
4119
4333
|
const timeoutMs = childParams.timeoutMs ?? childParams.maxRuntimeMs ?? workflowDefaults.timeoutMs ?? workflowDefaults.maxRuntimeMs;
|
|
4120
|
-
const turnBudget = childParams.turnBudget ?? workflowDefaults.turnBudget;
|
|
4121
4334
|
const toolBudget = childParams.toolBudget ?? workflowDefaults.toolBudget;
|
|
4122
4335
|
const intercomBridge = childParams.intercomBridge ?? workflowDefaults.intercomBridge;
|
|
4123
4336
|
const worktree = childParams.worktree ?? workflowDefaults.worktree;
|
|
4337
|
+
const outputSchema = Object.hasOwn(childParams, "outputSchema") ? childParams.outputSchema : workflowDefaults.outputSchema;
|
|
4338
|
+
if (outputSchema !== undefined) assertJsonSchemaObject(outputSchema, "outputSchema");
|
|
4339
|
+
const agentContract = Object.hasOwn(childParams, "agentContract") ? childParams.agentContract : workflowDefaults.agentContract;
|
|
4340
|
+
if (agentContract !== undefined && !isAgentContractV1(agentContract as AgentContract)) throw new Error("agentContract must be { version: 1 }.");
|
|
4341
|
+
const acceptance = Object.hasOwn(childParams, "acceptance") ? childParams.acceptance : workflowDefaults.acceptance;
|
|
4342
|
+
const output = Object.hasOwn(childParams, "output") ? childParams.output : workflowDefaults.output;
|
|
4343
|
+
if (output !== undefined && typeof output !== "string" && typeof output !== "boolean") throw new Error("output must be a path string or boolean.");
|
|
4344
|
+
const outputMode = Object.hasOwn(childParams, "outputMode") ? childParams.outputMode : workflowDefaults.outputMode;
|
|
4345
|
+
if (outputMode !== undefined && outputMode !== "inline" && outputMode !== "file-only") throw new Error("outputMode must be 'inline' or 'file-only'.");
|
|
4124
4346
|
return {
|
|
4125
4347
|
action: "resume",
|
|
4126
4348
|
id: childParams.resume.trim(),
|
|
@@ -4128,11 +4350,16 @@ export function prepareWorkflowLaunchParams(
|
|
|
4128
4350
|
message: typeof childParams.task === "string" ? childParams.task.trim() : "",
|
|
4129
4351
|
workflowParentRunId: parentWorkflowRunId,
|
|
4130
4352
|
workflowKey,
|
|
4353
|
+
...(lane ? { lane } : {}),
|
|
4131
4354
|
...(worktree !== undefined ? { worktree: worktree as boolean } : {}),
|
|
4355
|
+
...(outputSchema !== undefined ? { outputSchema: outputSchema as JsonSchemaObject } : {}),
|
|
4356
|
+
...(agentContract !== undefined ? { agentContract: agentContract as AgentContract } : {}),
|
|
4357
|
+
...(acceptance !== undefined ? { acceptance: acceptance as AcceptanceInput } : {}),
|
|
4358
|
+
...(output !== undefined ? { output: output as string | boolean } : {}),
|
|
4359
|
+
...(outputMode !== undefined ? { outputMode: outputMode as OutputMode } : {}),
|
|
4132
4360
|
...(options.runFanoutBudget ? { runFanoutBudget: { ...options.runFanoutBudget, parentPath: `${options.runFanoutBudget.parentPath ? `${options.runFanoutBudget.parentPath}/` : ""}workflow[${workflowKey}]` } } : {}),
|
|
4133
4361
|
...(options.missionDetached ? { mission: false } : {}),
|
|
4134
4362
|
...(timeoutMs !== undefined ? { timeoutMs: timeoutMs as number } : {}),
|
|
4135
|
-
...(turnBudget !== undefined ? { turnBudget: turnBudget as TurnBudgetConfig } : {}),
|
|
4136
4363
|
...(toolBudget !== undefined ? { toolBudget: toolBudget as ToolBudgetConfig } : {}),
|
|
4137
4364
|
...(intercomBridge !== undefined ? { intercomBridge: intercomBridge as IntercomBridgeConfig } : {}),
|
|
4138
4365
|
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
@@ -4146,6 +4373,9 @@ export function prepareWorkflowLaunchParams(
|
|
|
4146
4373
|
...(options.missionDetached ? { mission: false } : {}),
|
|
4147
4374
|
workflowParentRunId: parentWorkflowRunId,
|
|
4148
4375
|
workflowKey,
|
|
4376
|
+
...(options.outputClaimPath ? { workflowOutputClaimPath: options.outputClaimPath } : {}),
|
|
4377
|
+
...(options.awaitDetachedChild ? { workflowAwaitDetached: true } : {}),
|
|
4378
|
+
...(lane ? { lane } : {}),
|
|
4149
4379
|
...(parentTimeoutMs !== undefined ? { workflowParentDeadlineAt: options.parentDeadlineAt } : {}),
|
|
4150
4380
|
...(options.runFanoutBudget ? { runFanoutBudget: { ...options.runFanoutBudget, parentPath: `${options.runFanoutBudget.parentPath ? `${options.runFanoutBudget.parentPath}/` : ""}workflow[${workflowKey}]` } } : {}),
|
|
4151
4381
|
...(options.suppressRoutineResultIntercom ? { suppressRoutineResultIntercom: true } : {}),
|
|
@@ -4186,11 +4416,13 @@ function workflowChatProgressUpdate(
|
|
|
4186
4416
|
runId: string,
|
|
4187
4417
|
chatProgress: WorkflowChatProgressProjection,
|
|
4188
4418
|
workflow: NonNullable<Details["workflow"]>,
|
|
4419
|
+
workflowChildren?: Details["workflowChildren"],
|
|
4420
|
+
preflight?: import("../../shared/types.ts").WorkflowPreflightV1,
|
|
4189
4421
|
): AgentToolResult<Details> | undefined {
|
|
4190
4422
|
if (chatProgress.mode !== "live-card") return undefined;
|
|
4191
4423
|
return {
|
|
4192
4424
|
content: [{ type: "text", text: "Workflow running." }],
|
|
4193
|
-
details: { mode: "workflow", runId, results: [], workflow, chatProgress },
|
|
4425
|
+
details: { mode: "workflow", runId, results: [], workflow, ...(preflight ? { preflight } : {}), ...(workflowChildren ? { workflowChildren } : {}), chatProgress },
|
|
4194
4426
|
};
|
|
4195
4427
|
}
|
|
4196
4428
|
|
|
@@ -4201,10 +4433,12 @@ function createScheduledOwnerState(source: SubagentState, ownerSessionId: string
|
|
|
4201
4433
|
grantHistory: [...(source.subagentSpawns.grantHistory ?? [])],
|
|
4202
4434
|
}
|
|
4203
4435
|
: undefined;
|
|
4436
|
+
const ownerParentModel = source.currentSessionId === ownerSessionId ? source.lastParentModel : undefined;
|
|
4204
4437
|
return {
|
|
4205
4438
|
...source,
|
|
4206
4439
|
baseCwd: ctx.cwd,
|
|
4207
4440
|
currentSessionId: ownerSessionId,
|
|
4441
|
+
lastParentModel: ownerParentModel,
|
|
4208
4442
|
parentSessionFile: ctx.sessionManager.getSessionFile() ?? null,
|
|
4209
4443
|
subagentInProgress: false,
|
|
4210
4444
|
...(ownerSpawns ? { subagentSpawns: ownerSpawns } : { subagentSpawns: undefined }),
|
|
@@ -4265,6 +4499,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4265
4499
|
const delegatedThinkingOverrides = new WeakMap<object, AgentConfig["thinking"]>();
|
|
4266
4500
|
const delegatedZeroToolBudgets = new WeakSet<object>();
|
|
4267
4501
|
const delegatedExecutions = new WeakSet<object>();
|
|
4502
|
+
const workflowPermitContexts = new WeakMap<object, { root: WorkflowChildPermit } | { child: WorkflowChildPermitContext }>();
|
|
4268
4503
|
const warnedArtifactPackageDirs = new Set<string>();
|
|
4269
4504
|
const scheduledOwnerExecutors = new Map<string, ReturnType<typeof createSubagentExecutor>>();
|
|
4270
4505
|
const execute = async (
|
|
@@ -4274,21 +4509,67 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4274
4509
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
4275
4510
|
ctx: ExtensionContext,
|
|
4276
4511
|
preserveActiveSession = false,
|
|
4512
|
+
parentModelOverride?: ParentModel | null,
|
|
4277
4513
|
): Promise<AgentToolResult<Details>> => {
|
|
4278
4514
|
const workflowLaunchObserver = workflowLaunchObservers.get(params);
|
|
4279
4515
|
const delegatedThinkingOverride = delegatedThinkingOverrides.get(params);
|
|
4280
4516
|
const allowZeroToolBudget = delegatedZeroToolBudgets.has(params);
|
|
4281
4517
|
const delegatedExecution = delegatedExecutions.has(params);
|
|
4518
|
+
const workflowPermitContext = workflowPermitContexts.get(params);
|
|
4519
|
+
const delegatedWorkflowPermit = workflowPermitContext && "root" in workflowPermitContext ? workflowPermitContext.root : undefined;
|
|
4520
|
+
const workflowChildPermitLaunch = workflowPermitContext && "child" in workflowPermitContext ? workflowPermitContext.child : undefined;
|
|
4282
4521
|
if (!preserveActiveSession) deps.state.baseCwd = ctx.cwd;
|
|
4283
4522
|
deps.state.foregroundRuns ??= new Map();
|
|
4284
4523
|
deps.state.foregroundControls ??= new Map();
|
|
4285
4524
|
deps.state.lastForegroundControlId ??= null;
|
|
4286
4525
|
const normalizedGate = normalizeGateParams(params);
|
|
4287
4526
|
if (!normalizedGate.ok) return buildRequestedModeError(params, normalizedGate.error);
|
|
4288
|
-
|
|
4527
|
+
let requestParams = normalizedGate.params;
|
|
4528
|
+
let workflowPreflight: import("../../shared/types.ts").WorkflowPreflightV1 | undefined;
|
|
4529
|
+
try {
|
|
4530
|
+
if (requestParams.preflight !== undefined && requestParams.workflowScript === undefined && requestParams.workflowScriptPath === undefined) {
|
|
4531
|
+
throw new Error("preflight requires workflowScript or workflowScriptPath.");
|
|
4532
|
+
}
|
|
4533
|
+
workflowPreflight = normalizeWorkflowPreflight(requestParams.preflight);
|
|
4534
|
+
if (workflowPreflight) requestParams = { ...requestParams, preflight: workflowPreflight };
|
|
4535
|
+
} catch (error) {
|
|
4536
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4537
|
+
if (requestParams.action?.trim() === "validate") {
|
|
4538
|
+
const validation = validateWorkflowScript(requestParams.workflowScript ?? "");
|
|
4539
|
+
const invalidValidation = { ...validation, ok: false, errors: [...validation.errors, { message }] };
|
|
4540
|
+
return {
|
|
4541
|
+
content: [{ type: "text", text: JSON.stringify(invalidValidation) }],
|
|
4542
|
+
isError: true,
|
|
4543
|
+
details: { mode: "management", results: [] },
|
|
4544
|
+
};
|
|
4545
|
+
}
|
|
4546
|
+
return buildRequestedModeError(requestParams, message);
|
|
4547
|
+
}
|
|
4548
|
+
if (requestParams.action?.trim() === "validate") {
|
|
4549
|
+
const validation = validateWorkflowScript(requestParams.workflowScript ?? "");
|
|
4550
|
+
const payload = workflowPreflight ? { ...validation, preflight: workflowPreflight } : validation;
|
|
4551
|
+
return {
|
|
4552
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
4553
|
+
...(validation.ok ? {} : { isError: true }),
|
|
4554
|
+
details: { mode: "management", results: [] },
|
|
4555
|
+
};
|
|
4556
|
+
}
|
|
4289
4557
|
const normalizedAction = typeof requestParams.action === "string" ? requestParams.action.trim() : requestParams.action;
|
|
4290
4558
|
if (normalizedAction === "resume" && requestParams.extensionBindings !== undefined) return buildRequestedModeError(requestParams, "extensionBindings is not supported with action='resume'; resume uses the original retained child binding.");
|
|
4291
4559
|
if (requestParams.workflowScript !== undefined && normalizedAction === undefined) {
|
|
4560
|
+
if (delegatedWorkflowPermit) {
|
|
4561
|
+
const permitError = validateWorkflowChildPermitRoot(delegatedWorkflowPermit, _id);
|
|
4562
|
+
if (permitError) return buildRequestedModeError(requestParams, permitError);
|
|
4563
|
+
if (requestParams.async !== false) return buildRequestedModeError(requestParams, "Workflow child permit supports foreground workflow roots only; set async:false.");
|
|
4564
|
+
}
|
|
4565
|
+
const workflowParentModel = parentModelOverride !== undefined
|
|
4566
|
+
? parentModelOverride
|
|
4567
|
+
: (() => {
|
|
4568
|
+
const currentParentModel = normalizeParentModel(ctx.model);
|
|
4569
|
+
return (preserveActiveSession
|
|
4570
|
+
? currentParentModel
|
|
4571
|
+
: rememberParentModel(deps.state, resolveCurrentSessionId(ctx.sessionManager), currentParentModel)) ?? null;
|
|
4572
|
+
})();
|
|
4292
4573
|
if (requestParams.extensionBindings !== undefined) {
|
|
4293
4574
|
try {
|
|
4294
4575
|
requestParams.extensionBindings = normalizeExtensionBindings(requestParams.extensionBindings)!.value;
|
|
@@ -4301,7 +4582,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4301
4582
|
const workflowUsageBudget = validateUsageBudgetConfig(requestParams.usageBudget ?? deps.config.usageBudget, requestParams.usageBudget ? "usageBudget" : "config.usageBudget");
|
|
4302
4583
|
if (workflowUsageBudget.error) return buildRequestedModeError(requestParams, workflowUsageBudget.error);
|
|
4303
4584
|
const workflowCwd = resolveRequestedCwd(parentCwd, requestParams.cwd);
|
|
4304
|
-
const
|
|
4585
|
+
const discoverWorkflowAgents = (cwd: string, scope: AgentScope) => deps.discoverAgents(cwd, scope, workflowParentModel?.provider);
|
|
4586
|
+
const workflowAgents = discoverWorkflowAgents(workflowCwd, resolveExecutionAgentScope(requestParams.agentScope)).agents;
|
|
4305
4587
|
const workflowArtifactConfig: ArtifactConfig = omitUndefinedProperties({
|
|
4306
4588
|
...DEFAULT_ARTIFACT_CONFIG,
|
|
4307
4589
|
enabled: requestParams.artifacts !== false,
|
|
@@ -4340,7 +4622,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4340
4622
|
runId: workflowRunId,
|
|
4341
4623
|
kind: "workflow",
|
|
4342
4624
|
asyncDir: path.join(DIRS.async, workflowRunId),
|
|
4343
|
-
}, { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []) });
|
|
4625
|
+
}, { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
4344
4626
|
} catch (error) {
|
|
4345
4627
|
if (error instanceof ActiveAsyncCapacityError) {
|
|
4346
4628
|
deps.state.activeAsyncCapacity = error.snapshot;
|
|
@@ -4414,7 +4696,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4414
4696
|
deps.state.workflowChildStops ??= new Map();
|
|
4415
4697
|
deps.state.workflowControllers.set(workflowRunId, controller);
|
|
4416
4698
|
workflowCapacity?.markWorkflowStarted();
|
|
4417
|
-
if (workflowCapacity) deps.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(currentSessionId, resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession), { liveWorkflowRunIds: new Set(deps.state.workflowControllers.keys()) });
|
|
4699
|
+
if (workflowCapacity) deps.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(currentSessionId, resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession), { liveWorkflowRunIds: new Set(deps.state.workflowControllers.keys()), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
4418
4700
|
let status: AsyncStatus = {
|
|
4419
4701
|
runId: workflowRunId,
|
|
4420
4702
|
toolCallId,
|
|
@@ -4430,7 +4712,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4430
4712
|
...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}),
|
|
4431
4713
|
pid: process.pid,
|
|
4432
4714
|
steps: [],
|
|
4715
|
+
...(workflowPreflight ? { preflight: workflowPreflight } : {}),
|
|
4433
4716
|
workflow: { trace: [], emits: [], console: [] },
|
|
4717
|
+
workflowChildren: workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "running", inventoryComplete: false }),
|
|
4434
4718
|
runFanoutBudget: getRunFanoutBudgetSnapshot(workflowFanoutBudget),
|
|
4435
4719
|
};
|
|
4436
4720
|
const appendWorkflowEvent = (event: Record<string, unknown>) => {
|
|
@@ -4479,6 +4763,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4479
4763
|
if (persistClosed) return;
|
|
4480
4764
|
const liveJob = deps.state.asyncJobs.get(workflowRunId);
|
|
4481
4765
|
if (liveJob && (liveJob.status === "complete" || liveJob.status === "failed") && status.state !== "complete" && status.state !== "failed") return;
|
|
4766
|
+
const workflowState = status.state === "complete" ? "completed" : status.state === "failed" || status.state === "rejected" ? "failed" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "running";
|
|
4767
|
+
status.workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState, inventoryComplete: workflowState !== "running", trace: status.workflow?.trace, steps: status.steps });
|
|
4482
4768
|
status.lastUpdate = Date.now();
|
|
4483
4769
|
if (!initialPersistenceComplete) {
|
|
4484
4770
|
writeAtomicJson(statusPath, status);
|
|
@@ -4514,6 +4800,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4514
4800
|
liveJob.turnCount = status.turnCount;
|
|
4515
4801
|
liveJob.toolCount = status.toolCount;
|
|
4516
4802
|
liveJob.currentStep = status.currentStep;
|
|
4803
|
+
liveJob.preflight = status.preflight;
|
|
4517
4804
|
if (status.steps) {
|
|
4518
4805
|
liveJob.steps = status.steps.map((step, index) => ({ ...step, index }));
|
|
4519
4806
|
liveJob.agents = status.steps.map((step) => step.agent);
|
|
@@ -4522,6 +4809,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4522
4809
|
delete liveJob.agents;
|
|
4523
4810
|
}
|
|
4524
4811
|
liveJob.workflow = status.workflow;
|
|
4812
|
+
liveJob.workflowChildren = status.workflowChildren;
|
|
4525
4813
|
}
|
|
4526
4814
|
};
|
|
4527
4815
|
const writeWorkflowResult = (payload: Record<string, unknown>): boolean => {
|
|
@@ -4556,7 +4844,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4556
4844
|
status.toolCount = toolCounts.length > 0 ? toolCounts.reduce((total, count) => total + count, 0) : undefined;
|
|
4557
4845
|
status.currentStep = runningSteps.length === 1 ? steps.indexOf(runningSteps[0]!) : undefined;
|
|
4558
4846
|
};
|
|
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 };
|
|
4847
|
+
const workflowJob: AsyncJobState = { asyncId: workflowRunId, asyncDir, toolCallId, cwd: workflowCwd, ...(workflowSessionRoot ? { sessionRoot: workflowSessionRoot } : {}), status: "running", sessionId: currentSessionId ?? undefined, mode: "workflow", agents: [], steps: [], ...(workflowPreflight ? { preflight: workflowPreflight } : {}), startedAt, updatedAt: startedAt, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), ...(timeout !== undefined ? { timeoutMs: timeout, deadlineAt: startedAt + timeout } : {}), workflow: status.workflow, workflowChildren: status.workflowChildren };
|
|
4560
4848
|
deps.state.asyncJobs.set(workflowRunId, workflowJob);
|
|
4561
4849
|
deps.state.fleetJobs ??= new Map();
|
|
4562
4850
|
deps.state.fleetJobs.set(workflowRunId, workflowJob);
|
|
@@ -4576,18 +4864,26 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4576
4864
|
const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
|
|
4577
4865
|
const workflowResults: SingleResult[] = [];
|
|
4578
4866
|
const workflowChildRunIds = new Map<string, string>();
|
|
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;
|
|
4867
|
+
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, preflight: _preflight, ...workflowChildDefaults } = workflowRequest;
|
|
4580
4868
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
|
|
4581
4869
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
|
|
4582
|
-
const workflowAggregateOutputPath = resolveWorkflowAggregateOutputPath(workflowOutput,
|
|
4870
|
+
const workflowAggregateOutputPath = resolveWorkflowAggregateOutputPath(workflowOutput, parentCwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, workflowRunId));
|
|
4583
4871
|
const claimedOutputPaths = new Map<string, string>();
|
|
4584
4872
|
const childOutputOverrides = new Map<string, string>();
|
|
4873
|
+
const childOutputClaimPaths = new Map<string, string>();
|
|
4585
4874
|
const producedChildOutputPaths = new Set<string>();
|
|
4586
4875
|
const workflowSteps = new Map<string, NonNullable<AsyncStatus["steps"]>[number]>();
|
|
4876
|
+
const runHostCommand = workflowHostCommandRunner({ workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId, claimedOutputPaths, producedOutputPaths: producedChildOutputPaths });
|
|
4587
4877
|
let projectedTraceLength = 0;
|
|
4588
4878
|
let projectedTraceTail: NonNullable<Details["workflow"]>["trace"][number] | undefined;
|
|
4589
4879
|
const updateTrace = (trace: NonNullable<Details["workflow"]>["trace"]) => {
|
|
4590
|
-
|
|
4880
|
+
const projectedTrace = annotateWorkflowPreflightTrace(trace, workflowPreflight);
|
|
4881
|
+
const preflightWarnings = workflowPreflightWarnings(workflowPreflight, trace);
|
|
4882
|
+
status.workflow = {
|
|
4883
|
+
...(status.workflow ?? { emits: [], console: [] }),
|
|
4884
|
+
trace: projectedTrace,
|
|
4885
|
+
...(preflightWarnings.length ? { preflightWarnings } : {}),
|
|
4886
|
+
};
|
|
4591
4887
|
const rebuild = trace.length < projectedTraceLength
|
|
4592
4888
|
|| (projectedTraceLength > 0 && trace[projectedTraceLength - 1] !== projectedTraceTail);
|
|
4593
4889
|
if (rebuild) {
|
|
@@ -4600,8 +4896,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4600
4896
|
for (let index = projectedTraceLength; index < trace.length; index += 1) {
|
|
4601
4897
|
const entry = trace[index]!;
|
|
4602
4898
|
if (entry.operation !== "run") continue;
|
|
4899
|
+
const entryLabel = entry.label?.trim() || undefined;
|
|
4900
|
+
const entryPhase = entry.phase?.trim() || undefined;
|
|
4901
|
+
const shouldProjectPhase = entryPhase !== undefined && (entryPhase !== "auto-resume" || workflowSteps.get(entry.key)?.phase === undefined);
|
|
4603
4902
|
const existing = workflowSteps.get(entry.key);
|
|
4604
|
-
if (
|
|
4903
|
+
if (existing) {
|
|
4904
|
+
if (entryLabel) existing.label = entryLabel;
|
|
4905
|
+
if (shouldProjectPhase) existing.phase = entryPhase;
|
|
4906
|
+
}
|
|
4907
|
+
if (entry.state === "reused" && existing) {
|
|
4908
|
+
continue;
|
|
4909
|
+
}
|
|
4605
4910
|
const mapped = entry.state === "started" || entry.state === "reused"
|
|
4606
4911
|
? "running"
|
|
4607
4912
|
: entry.state === "completed"
|
|
@@ -4625,13 +4930,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4625
4930
|
if (entry.durationMs === undefined) delete existing.durationMs;
|
|
4626
4931
|
else existing.durationMs = entry.durationMs;
|
|
4627
4932
|
} else {
|
|
4933
|
+
// Naming uses the explicit label only — never the workflow key — so the
|
|
4934
|
+
// placeholder cannot diverge from the task-derived name the child
|
|
4935
|
+
// session actually gets (launch/progress updates overwrite this).
|
|
4936
|
+
const stepSessionName = deriveChildSessionName({ agent: entry.agent ?? entry.key, label: entryLabel });
|
|
4628
4937
|
const step: NonNullable<AsyncStatus["steps"]>[number] = {
|
|
4629
4938
|
agent: entry.agent ?? entry.key,
|
|
4630
|
-
|
|
4939
|
+
...(stepSessionName ? { sessionName: stepSessionName } : {}),
|
|
4940
|
+
label: entryLabel ?? entry.key,
|
|
4631
4941
|
workflowKey: entry.key,
|
|
4632
4942
|
parentWorkflowRunId: workflowRunId,
|
|
4633
4943
|
status: mapped,
|
|
4634
4944
|
startedAt: Date.now(),
|
|
4945
|
+
...(entryPhase ? { phase: entryPhase } : {}),
|
|
4635
4946
|
...(entry.runId ? { runId: entry.runId } : {}),
|
|
4636
4947
|
...(entry.state === "failed" && !entry.runId ? { async: false } : {}),
|
|
4637
4948
|
...(entry.state === "detached" ? { activityState: "needs_attention" as const } : {}),
|
|
@@ -4647,7 +4958,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4647
4958
|
version: 1,
|
|
4648
4959
|
childId: entry.key,
|
|
4649
4960
|
status: "stopped",
|
|
4650
|
-
reason: "
|
|
4961
|
+
reason: "subagent-action",
|
|
4651
4962
|
source: "async",
|
|
4652
4963
|
stepIndex: status.steps?.indexOf(projectedStep),
|
|
4653
4964
|
agent: projectedStep.agent,
|
|
@@ -4667,6 +4978,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4667
4978
|
try {
|
|
4668
4979
|
const workflow = await runWorkflowScript({
|
|
4669
4980
|
script: workflowScript,
|
|
4981
|
+
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
4670
4982
|
timeoutMs: timeout,
|
|
4671
4983
|
signal: controller.signal,
|
|
4672
4984
|
registerStopChild: (stop) => {
|
|
@@ -4675,11 +4987,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4675
4987
|
},
|
|
4676
4988
|
...(workflowState ? { state: workflowState } : {}),
|
|
4677
4989
|
onTrace: updateTrace,
|
|
4990
|
+
host: runHostCommand,
|
|
4991
|
+
onHostStep: (hostStep) => {
|
|
4992
|
+
status = upsertHostStep({ status, hostStep, persist: (nextStatus) => {
|
|
4993
|
+
status = nextStatus;
|
|
4994
|
+
persist({ tolerateStatusWriteFailure: true });
|
|
4995
|
+
} });
|
|
4996
|
+
},
|
|
4678
4997
|
admit: (calls) => {
|
|
4679
|
-
const outputClaims = workflowChildOutputClaims({ ctxCwd:
|
|
4998
|
+
const outputClaims = workflowChildOutputClaims({ ctxCwd: parentCwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, state: deps.state, claimedOutputPaths, entries: calls });
|
|
4680
4999
|
if (outputClaims.error) throw new Error(outputClaims.error);
|
|
4681
5000
|
status.runFanoutBudget = claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
|
|
4682
5001
|
if (outputClaims.claims) applyWorkflowChildOutputClaims(claimedOutputPaths, outputClaims.claims);
|
|
5002
|
+
if (outputClaims.childClaims) for (const [key, claimPath] of outputClaims.childClaims) childOutputClaimPaths.set(key, claimPath);
|
|
4683
5003
|
if (outputClaims.overrides) for (const [key, output] of outputClaims.overrides) childOutputOverrides.set(key, output);
|
|
4684
5004
|
persist({ tolerateStatusWriteFailure: true });
|
|
4685
5005
|
},
|
|
@@ -4692,7 +5012,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4692
5012
|
launch: async (key, childParams, workflowSignal, admission) => {
|
|
4693
5013
|
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);
|
|
4694
5014
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
4695
|
-
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)), childParams, deps.state);
|
|
5015
|
+
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)), childParams, deps.state, { state: "partial", reason: "budget_exhausted" });
|
|
4696
5016
|
const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
|
|
4697
5017
|
const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
|
|
4698
5018
|
recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
|
|
@@ -4702,19 +5022,23 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4702
5022
|
...(childPhase ? { phase: childPhase } : {}),
|
|
4703
5023
|
heartbeat: { status: "running", ...(childPhase ? { phase: childPhase } : {}) },
|
|
4704
5024
|
});
|
|
5025
|
+
let preparedChildParams: SubagentParamsLike | undefined;
|
|
4705
5026
|
const result = await runMissionWorkflowChild(missionBinding, workflowRunId, key, childPhase, () => {
|
|
4706
5027
|
const childRequest = bindMissionWorkflowChildAsyncLaunch(
|
|
4707
|
-
{ ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams, parentWorkflowRunId: workflowRunId, workflowKey: key, ctxCwd:
|
|
5028
|
+
{ ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams, parentWorkflowRunId: workflowRunId, workflowKey: key, ctxCwd: parentCwd, workflowCwd, artifactsDir: workflowArtifactsDir, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, outputOverride: childOutputOverrides.get(key), outputClaimPath: childOutputClaimPaths.get(key), options: { missionDetached: detachWorkflowChildMissions, awaitDetachedChild: true, runFanoutBudget: workflowFanoutBudget, parentDeadlineAt: workflowDeadlineAt, capabilityCeiling: workflowCapabilityCeiling } }), runFanoutAdmitted: admission.admitted },
|
|
4708
5029
|
missionBinding,
|
|
4709
5030
|
deps.asyncByDefault,
|
|
4710
5031
|
);
|
|
5032
|
+
preparedChildParams = childRequest;
|
|
4711
5033
|
workflowLaunchObservers.set(childRequest, (launch) => {
|
|
4712
5034
|
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
4713
5035
|
if (step) {
|
|
4714
5036
|
step.agent = launch.agent;
|
|
5037
|
+
if (launch.sessionName) step.sessionName = launch.sessionName;
|
|
4715
5038
|
step.sessionFile = launch.sessionFile;
|
|
4716
5039
|
step.async = launch.async;
|
|
4717
5040
|
if (launch.runId) step.runId = launch.runId;
|
|
5041
|
+
if (childRequest.lane) step.lane = childRequest.lane;
|
|
4718
5042
|
persist({ tolerateStatusWriteFailure: true });
|
|
4719
5043
|
}
|
|
4720
5044
|
recordMissionWorkflowChild(missionBinding, workflowRunId, key, { status: "running", agent: launch.agent, ...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}) });
|
|
@@ -4736,6 +5060,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4736
5060
|
step.toolCount = progress.toolCount;
|
|
4737
5061
|
step.model = progress.model;
|
|
4738
5062
|
step.thinking = progress.thinking;
|
|
5063
|
+
if (progress.sessionName) step.sessionName = progress.sessionName;
|
|
4739
5064
|
step.error = progress.error;
|
|
4740
5065
|
projectWorkflowActivity();
|
|
4741
5066
|
persist({ tolerateStatusWriteFailure: true });
|
|
@@ -4743,18 +5068,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4743
5068
|
status: step.status,
|
|
4744
5069
|
heartbeat: { status: step.status, ...(childPhase ? { phase: childPhase } : {}) },
|
|
4745
5070
|
});
|
|
4746
|
-
}, ctx, preserveActiveSession);
|
|
5071
|
+
}, ctx, preserveActiveSession, workflowParentModel);
|
|
4747
5072
|
});
|
|
4748
5073
|
workflowResults.push(...result.details.results);
|
|
4749
5074
|
for (const childResult of result.details.results) {
|
|
4750
|
-
if (childResult.savedOutputPath) producedChildOutputPaths.add(childResult.savedOutputPath);
|
|
5075
|
+
if (childResult.savedOutputPath) producedChildOutputPaths.add(resolveWorkflowHostOutputClaimPath(childResult.savedOutputPath));
|
|
4751
5076
|
}
|
|
4752
|
-
const child = workflowChildResult(key, result, childParams, deps.state);
|
|
5077
|
+
const child = workflowChildResult(key, result, preparedChildParams ?? childParams, deps.state);
|
|
4753
5078
|
if (child.runId) workflowChildRunIds.set(key, child.runId);
|
|
4754
5079
|
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
4755
5080
|
if (step) {
|
|
4756
5081
|
step.async = Boolean(result.details.asyncId || result.details.asyncDir);
|
|
4757
5082
|
if (child.runId) step.runId = child.runId;
|
|
5083
|
+
if (child.lane) step.lane = child.lane;
|
|
4758
5084
|
}
|
|
4759
5085
|
if (result.details.asyncDir && missionBinding) writeMissionAsyncBinding(result.details.asyncDir, missionBinding);
|
|
4760
5086
|
const childStatus = missionWorkflowChildStatus(result);
|
|
@@ -4773,25 +5099,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4773
5099
|
}
|
|
4774
5100
|
return child;
|
|
4775
5101
|
},
|
|
4776
|
-
status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession)),
|
|
5102
|
+
status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession, workflowParentModel)),
|
|
4777
5103
|
resolveResume: (reference) => resolveKeyedWorkflowResume(reference, deps.state),
|
|
4778
5104
|
steer: (key, message, options, workflowSignal) => steerWorkflowChildByKey({ state: deps.state, workflowRunId, key, message, options, signal: workflowSignal, resolveRunId: () => workflowChildRunIds.get(key) }),
|
|
4779
5105
|
});
|
|
5106
|
+
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, workflow.trace, { settled: true });
|
|
5107
|
+
const finalPreflightTrace = annotateWorkflowPreflightTrace(workflow.trace, workflowPreflight);
|
|
4780
5108
|
const returnPreview = formatWorkflowValue(workflow.value).slice(0, 1_000);
|
|
4781
5109
|
const emitPreview = workflow.emits.length > 0 ? ` Emitted: ${workflow.emits.map(formatWorkflowValue).join(", ").slice(0, 1_000)}` : "";
|
|
4782
|
-
const summary = `Workflow completed with ${workflow.children.length} child run(s). Return: ${returnPreview}${emitPreview} Trace: ${workflow.trace.length} event(s)
|
|
5110
|
+
const summary = `Workflow completed with ${workflow.children.length} child run(s). Return: ${returnPreview}${emitPreview} Trace: ${workflow.trace.length} event(s).${workflowOutputPathMappingSummary(workflow.children)}${finalPreflightWarnings.length ? ` ${finalPreflightWarnings.join(" ")}` : ""}`;
|
|
4783
5111
|
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, summary, producedChildOutputPaths);
|
|
4784
5112
|
const resultSummary = appendWorkflowOutputWarning(summary, outputWarning);
|
|
4785
5113
|
const workflowUsage = sumResultsUsage(workflowResults);
|
|
4786
|
-
|
|
4787
|
-
|
|
5114
|
+
const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children, steps: status.steps });
|
|
5115
|
+
status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
|
|
5116
|
+
const receipt = terminalWorkflowReceipt(workflowRunId, "complete", workflow.children, workflowChildren, undefined, validHostStepNodes(status.workflowGraph));
|
|
4788
5117
|
let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
|
|
4789
5118
|
try {
|
|
4790
5119
|
workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
|
|
4791
5120
|
} catch (receiptError) {
|
|
4792
5121
|
appendWorkflowEvent({ type: "subagent.workflow.receipt_write_failed", error: `Failed to persist async workflow receipt: ${receiptError instanceof Error ? receiptError.message : String(receiptError)}` });
|
|
4793
5122
|
}
|
|
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;
|
|
5123
|
+
if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary: resultSummary, output: resultSummary, workflowChildren, results: workflow.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(status.steps?.find((step) => step.workflowKey === child.key)?.sessionName ? { sessionName: status.steps?.find((step) => step.workflowKey === child.key)?.sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(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
5124
|
persist();
|
|
4796
5125
|
persistClosed = true;
|
|
4797
5126
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(status.error ? { error: status.error } : {}) });
|
|
@@ -4813,28 +5142,33 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4813
5142
|
delete step.activityState;
|
|
4814
5143
|
}
|
|
4815
5144
|
}
|
|
4816
|
-
|
|
5145
|
+
const workflowState = state === "paused" ? "paused" : state === "stopped" ? "stopped" : "failed";
|
|
5146
|
+
const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState, inventoryComplete: true, trace: partial.trace, children: partial.children, steps: status.steps });
|
|
5147
|
+
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, partial.trace, { settled: true });
|
|
5148
|
+
const finalPreflightTrace = annotateWorkflowPreflightTrace(partial.trace, workflowPreflight);
|
|
5149
|
+
status = compactOptional<AsyncStatus>({ ...status, state, stopped: stopped || undefined, activityState: pauseForDetached ? "needs_attention" : undefined, error: workflowFailureMessage(error, workflowRunId, partial.children), endedAt: Date.now(), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren });
|
|
4817
5150
|
if (pauseForDetached) {
|
|
4818
5151
|
const promoted = promotePausedWorkflowIfSettled(status);
|
|
4819
5152
|
if (promoted) status = promoted;
|
|
4820
5153
|
}
|
|
4821
|
-
const terminalSummary = status.state === "complete"
|
|
5154
|
+
const terminalSummary = `${status.state === "complete"
|
|
4822
5155
|
? "Workflow completed after detached child finished."
|
|
4823
|
-
: status.error ?? (pauseForDetached ? "Workflow paused." : "Workflow failed.")
|
|
5156
|
+
: status.error ?? (pauseForDetached ? "Workflow paused." : "Workflow failed.")}${workflowOutputPathMappingSummary(partial.children)}${finalPreflightWarnings.length ? ` ${finalPreflightWarnings.join(" ")}` : ""}`;
|
|
4824
5157
|
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, terminalSummary, producedChildOutputPaths);
|
|
4825
5158
|
const resultSummary = appendWorkflowOutputWarning(terminalSummary, outputWarning);
|
|
4826
5159
|
const receiptState: WorkflowReceiptState = status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
|
|
4827
|
-
const
|
|
5160
|
+
const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
|
|
5161
|
+
const receipt = terminalWorkflowReceipt(workflowRunId, receiptState, partial.children, workflowChildren, terminalOutcome, validHostStepNodes(status.workflowGraph));
|
|
4828
5162
|
let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
|
|
4829
5163
|
try {
|
|
4830
5164
|
workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
|
|
4831
5165
|
} catch (receiptError) {
|
|
4832
5166
|
appendWorkflowEvent({ type: "subagent.workflow.receipt_write_failed", error: `Failed to persist async workflow receipt: ${receiptError instanceof Error ? receiptError.message : String(receiptError)}` });
|
|
4833
5167
|
}
|
|
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;
|
|
5168
|
+
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, workflowChildren, ...(terminalOutcome ? { terminalOutcome } : {}), results: partial.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(status.steps?.find((step) => step.workflowKey === child.key)?.sessionName ? { sessionName: status.steps?.find((step) => step.workflowKey === child.key)?.sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(child.terminalOutcome ? { terminalOutcome: child.terminalOutcome } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(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;
|
|
4835
5169
|
persist();
|
|
4836
5170
|
persistClosed = true;
|
|
4837
|
-
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(status.error ? { error: status.error } : {}), ...(status.activityState ? { activityState: status.activityState } : {}) });
|
|
5171
|
+
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(terminalOutcome ? { terminalOutcome } : {}), ...(status.error ? { error: status.error } : {}), ...(status.activityState ? { activityState: status.activityState } : {}) });
|
|
4838
5172
|
} finally {
|
|
4839
5173
|
persistClosed = true;
|
|
4840
5174
|
deps.state.workflowControllers?.delete(workflowRunId);
|
|
@@ -4844,41 +5178,55 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4844
5178
|
}
|
|
4845
5179
|
});
|
|
4846
5180
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
4847
|
-
content: [{ type: "text", text: formatAsyncStartedMessage(`Async workflow [${workflowRunId}]`, ctx.hasUI === true) }],
|
|
4848
|
-
details: { mode: "workflow", runId: workflowRunId, toolCallId, asyncId: workflowRunId, asyncDir, results: [], chatProgress, ...(deps.state.activeAsyncCapacity ? { activeAsyncCapacity: deps.state.activeAsyncCapacity } : {}) },
|
|
5181
|
+
content: [{ type: "text", text: `${workflowPreflight ? `${formatWorkflowPreflight(workflowPreflight)}\n\n` : ""}${formatAsyncStartedMessage(`Async workflow [${workflowRunId}]`, ctx.hasUI === true)}` }],
|
|
5182
|
+
details: { mode: "workflow", runId: workflowRunId, toolCallId, asyncId: workflowRunId, asyncDir, results: [], ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren: status.workflowChildren, chatProgress, ...(deps.state.activeAsyncCapacity ? { activeAsyncCapacity: deps.state.activeAsyncCapacity } : {}) },
|
|
4849
5183
|
}, workflowFanoutBudget));
|
|
4850
5184
|
}
|
|
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;
|
|
5185
|
+
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, preflight: _preflight, ...workflowChildDefaults } = requestParams;
|
|
4852
5186
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
|
|
4853
5187
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
|
|
4854
5188
|
const workflowAggregateOutputPath = resolveWorkflowAggregateOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, _id));
|
|
4855
5189
|
const claimedOutputPaths = new Map<string, string>();
|
|
4856
5190
|
const childOutputOverrides = new Map<string, string>();
|
|
5191
|
+
const childOutputClaimPaths = new Map<string, string>();
|
|
4857
5192
|
const producedChildOutputPaths = new Set<string>();
|
|
4858
5193
|
const workflowResults: SingleResult[] = [];
|
|
4859
5194
|
const workflowChildRunIds = new Map<string, string>();
|
|
5195
|
+
const runHostCommand = workflowHostCommandRunner({ workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId: _id, claimedOutputPaths, producedOutputPaths: producedChildOutputPaths });
|
|
5196
|
+
const workflowHostSteps = new Map<string, HostStepNodeV1>();
|
|
4860
5197
|
let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [] };
|
|
5198
|
+
let liveWorkflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "running", inventoryComplete: false });
|
|
4861
5199
|
const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
|
|
4862
5200
|
const workflowCapabilityCeiling = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(resolveCurrentSessionId(ctx.sessionManager)));
|
|
4863
5201
|
const sendWorkflowProgress = () => {
|
|
4864
|
-
const update = workflowChatProgressUpdate(_id, chatProgress, liveWorkflow);
|
|
5202
|
+
const update = workflowChatProgressUpdate(_id, chatProgress, liveWorkflow, liveWorkflowChildren, workflowPreflight);
|
|
4865
5203
|
if (update) onUpdate?.(update);
|
|
4866
5204
|
};
|
|
4867
5205
|
try {
|
|
4868
5206
|
const workflow = await runWorkflowScript({
|
|
4869
5207
|
script: requestParams.workflowScript,
|
|
5208
|
+
...(delegatedWorkflowPermit ? { oneUsePermit: { claim: (key: string) => claimWorkflowChildPermit(delegatedWorkflowPermit, _id, key) } } : {}),
|
|
5209
|
+
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
4870
5210
|
timeoutMs: timeout,
|
|
4871
5211
|
signal,
|
|
4872
5212
|
...(workflowState ? { state: workflowState } : {}),
|
|
5213
|
+
...(!delegatedWorkflowPermit ? { host: runHostCommand } : {}),
|
|
5214
|
+
onHostStep: (hostStep) => {
|
|
5215
|
+
workflowHostSteps.set(hostStep.id, { ...hostStep, ...(hostStep.freshness ? { freshness: { ...hostStep.freshness } } : {}) });
|
|
5216
|
+
},
|
|
4873
5217
|
onTrace: (trace) => {
|
|
4874
|
-
|
|
5218
|
+
const projectedTrace = annotateWorkflowPreflightTrace(trace, workflowPreflight);
|
|
5219
|
+
const preflightWarnings = workflowPreflightWarnings(workflowPreflight, trace);
|
|
5220
|
+
liveWorkflow = { ...liveWorkflow, trace: projectedTrace, ...(preflightWarnings.length ? { preflightWarnings } : {}) };
|
|
5221
|
+
liveWorkflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "running", inventoryComplete: false, trace });
|
|
4875
5222
|
sendWorkflowProgress();
|
|
4876
5223
|
},
|
|
4877
5224
|
admit: (calls) => {
|
|
4878
|
-
const outputClaims = workflowChildOutputClaims({ ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId: _id, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents:
|
|
5225
|
+
const outputClaims = workflowChildOutputClaims({ ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId: _id, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, state: deps.state, claimedOutputPaths, entries: calls });
|
|
4879
5226
|
if (outputClaims.error) throw new Error(outputClaims.error);
|
|
4880
5227
|
claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
|
|
4881
5228
|
if (outputClaims.claims) applyWorkflowChildOutputClaims(claimedOutputPaths, outputClaims.claims);
|
|
5229
|
+
if (outputClaims.childClaims) for (const [key, claimPath] of outputClaims.childClaims) childOutputClaimPaths.set(key, claimPath);
|
|
4882
5230
|
if (outputClaims.overrides) for (const [key, output] of outputClaims.overrides) childOutputOverrides.set(key, output);
|
|
4883
5231
|
},
|
|
4884
5232
|
onEmit: (emits) => {
|
|
@@ -4886,9 +5234,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4886
5234
|
sendWorkflowProgress();
|
|
4887
5235
|
},
|
|
4888
5236
|
launch: async (key, childParams, workflowSignal, admission) => {
|
|
5237
|
+
if (delegatedWorkflowPermit && admission.batch) throw new Error("Workflow child permit does not support runs.all.");
|
|
5238
|
+
if (delegatedWorkflowPermit && childParams.resume !== undefined) throw new Error("Workflow child permit does not support retained resume.");
|
|
5239
|
+
if (delegatedWorkflowPermit && childParams.async === true) throw new Error("Workflow child permit supports one foreground child only.");
|
|
4889
5240
|
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);
|
|
4890
5241
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
4891
|
-
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)), childParams, deps.state);
|
|
5242
|
+
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)), childParams, deps.state, { state: "partial", reason: "budget_exhausted" });
|
|
4892
5243
|
const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
|
|
4893
5244
|
const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
|
|
4894
5245
|
recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
@@ -4898,12 +5249,23 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4898
5249
|
...(childPhase ? { phase: childPhase } : {}),
|
|
4899
5250
|
heartbeat: { status: "running", ...(childPhase ? { phase: childPhase } : {}) },
|
|
4900
5251
|
});
|
|
5252
|
+
let preparedChildParams: SubagentParamsLike | undefined;
|
|
4901
5253
|
const result = await runMissionWorkflowChild(missionBinding, _id, key, childPhase, () => {
|
|
4902
5254
|
const childRequest = bindMissionWorkflowChildAsyncLaunch(
|
|
4903
|
-
{ ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams, parentWorkflowRunId: _id, workflowKey: key, ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents:
|
|
5255
|
+
{ ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams, parentWorkflowRunId: _id, workflowKey: key, ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, outputOverride: childOutputOverrides.get(key), outputClaimPath: childOutputClaimPaths.get(key), options: { missionDetached: detachWorkflowChildMissions, suppressRoutineResultIntercom: chatProgress.mode === "live-card", runFanoutBudget: workflowFanoutBudget, parentDeadlineAt: workflowDeadlineAt, capabilityCeiling: workflowCapabilityCeiling } }), runFanoutAdmitted: admission.admitted },
|
|
4904
5256
|
missionBinding,
|
|
4905
5257
|
deps.asyncByDefault,
|
|
4906
5258
|
);
|
|
5259
|
+
preparedChildParams = childRequest;
|
|
5260
|
+
if (delegatedWorkflowPermit) {
|
|
5261
|
+
if (childRequest.async !== false) throw new Error("Workflow child permit supports one foreground child only.");
|
|
5262
|
+
const childCwd = resolveRequestedCwd(workflowCwd, childRequest.cwd);
|
|
5263
|
+
const childAgent = typeof childRequest.agent === "string"
|
|
5264
|
+
? resolveAgentName(childRequest.agent, discoverWorkflowAgents(childCwd, resolveExecutionAgentScope(childRequest.agentScope)).agents).agent
|
|
5265
|
+
: undefined;
|
|
5266
|
+
if (childAgent?.runner?.type === "external-cli" || childAgent?.runner?.type === "external-job") throw new Error("Workflow child permit supports native Pi children only.");
|
|
5267
|
+
workflowPermitContexts.set(childRequest, { child: { permit: delegatedWorkflowPermit, workflowRunId: _id, childKey: key } });
|
|
5268
|
+
}
|
|
4907
5269
|
workflowLaunchObservers.set(childRequest, (launch) => recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
4908
5270
|
status: "running",
|
|
4909
5271
|
agent: launch.agent,
|
|
@@ -4917,14 +5279,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4917
5279
|
status: progressStatus,
|
|
4918
5280
|
heartbeat: { status: progressStatus, ...(childPhase ? { phase: childPhase } : {}) },
|
|
4919
5281
|
});
|
|
4920
|
-
}, ctx, preserveActiveSession);
|
|
5282
|
+
}, ctx, preserveActiveSession, workflowParentModel);
|
|
4921
5283
|
});
|
|
4922
5284
|
workflowResults.push(...result.details.results);
|
|
4923
5285
|
for (const childResult of result.details.results) {
|
|
4924
|
-
if (childResult.savedOutputPath) producedChildOutputPaths.add(childResult.savedOutputPath);
|
|
5286
|
+
if (childResult.savedOutputPath) producedChildOutputPaths.add(resolveWorkflowHostOutputClaimPath(childResult.savedOutputPath));
|
|
4925
5287
|
}
|
|
4926
5288
|
if (result.details.asyncDir && missionBinding) writeMissionAsyncBinding(result.details.asyncDir, missionBinding);
|
|
4927
|
-
const child = workflowChildResult(key, result, childParams, deps.state);
|
|
5289
|
+
const child = workflowChildResult(key, result, preparedChildParams ?? childParams, deps.state);
|
|
4928
5290
|
if (child.runId) workflowChildRunIds.set(key, child.runId);
|
|
4929
5291
|
const childStatus = missionWorkflowChildStatus(result);
|
|
4930
5292
|
recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
@@ -4938,43 +5300,64 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4938
5300
|
});
|
|
4939
5301
|
return child;
|
|
4940
5302
|
},
|
|
4941
|
-
status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession)),
|
|
5303
|
+
status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession, workflowParentModel)),
|
|
4942
5304
|
resolveResume: (reference) => resolveKeyedWorkflowResume(reference, deps.state),
|
|
4943
5305
|
steer: (key, message, options, workflowSignal) => steerWorkflowChildByKey({ state: deps.state, workflowRunId: _id, key, message, options, signal: workflowSignal, resolveRunId: () => workflowChildRunIds.get(key) }),
|
|
4944
5306
|
});
|
|
4945
|
-
const
|
|
4946
|
-
const
|
|
4947
|
-
const
|
|
5307
|
+
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, workflow.trace, { settled: true });
|
|
5308
|
+
const finalPreflightTrace = annotateWorkflowPreflightTrace(workflow.trace, workflowPreflight);
|
|
5309
|
+
const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children });
|
|
5310
|
+
const receipt = terminalWorkflowReceipt(_id, "complete", workflow.children, workflowChildren, undefined, [...workflowHostSteps.values()]);
|
|
5311
|
+
const traceLines = finalPreflightTrace.map((entry) => `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.durationMs !== undefined ? ` in ${entry.durationMs}ms` : ""}${entry.warning ? ` — ${entry.warning}` : ""}${entry.error ? ` — ${entry.error}` : ""}`);
|
|
5312
|
+
const sections = [
|
|
5313
|
+
...(workflowPreflight ? [formatWorkflowPreflight(workflowPreflight)] : []),
|
|
5314
|
+
"Workflow completed.",
|
|
5315
|
+
`Return:\n${formatWorkflowValue(workflow.value)}`,
|
|
5316
|
+
];
|
|
4948
5317
|
if (workflow.emits.length > 0) sections.push(`Emitted:\n${workflow.emits.map(formatWorkflowValue).join("\n")}`);
|
|
4949
5318
|
if (workflow.console.length > 0) sections.push(`Console:\n${workflow.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
|
|
4950
5319
|
if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
|
|
5320
|
+
if (finalPreflightWarnings.length > 0) sections.push(formatWorkflowPreflightWarnings(finalPreflightWarnings));
|
|
5321
|
+
const outputMappings = workflowOutputPathMappingSummary(workflow.children).trim();
|
|
5322
|
+
if (outputMappings) sections.push(outputMappings);
|
|
4951
5323
|
const workflowText = sections.join("\n\n");
|
|
4952
5324
|
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, workflowText, producedChildOutputPaths);
|
|
4953
5325
|
const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
|
|
4954
5326
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
4955
5327
|
content: [{ type: "text", text: displayText }],
|
|
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:
|
|
5328
|
+
details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflow.children.flatMap((child) => (child.results ?? []) as SingleResult[]), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
|
|
4957
5329
|
}, workflowFanoutBudget));
|
|
4958
5330
|
} catch (error) {
|
|
4959
5331
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
4960
|
-
const text = error
|
|
4961
|
-
const
|
|
4962
|
-
const
|
|
5332
|
+
const text = workflowFailureMessage(error, _id, partial.children);
|
|
5333
|
+
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, partial.trace, { settled: true });
|
|
5334
|
+
const finalPreflightTrace = annotateWorkflowPreflightTrace(partial.trace, workflowPreflight);
|
|
5335
|
+
const traceLines = finalPreflightTrace.map((entry) => `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.warning ? ` — ${entry.warning}` : ""}${entry.error ? ` — ${entry.error}` : ""}`);
|
|
5336
|
+
const sections = [
|
|
5337
|
+
...(workflowPreflight ? [formatWorkflowPreflight(workflowPreflight)] : []),
|
|
5338
|
+
`Workflow failed: ${text}`,
|
|
5339
|
+
];
|
|
4963
5340
|
if (partial.emits.length > 0) sections.push(`Emitted:\n${partial.emits.map(formatWorkflowValue).join("\n")}`);
|
|
4964
5341
|
if (partial.console.length > 0) sections.push(`Console:\n${partial.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
|
|
4965
5342
|
if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
|
|
5343
|
+
if (finalPreflightWarnings.length > 0) sections.push(formatWorkflowPreflightWarnings(finalPreflightWarnings));
|
|
5344
|
+
const outputMappings = workflowOutputPathMappingSummary(partial.children).trim();
|
|
5345
|
+
if (outputMappings) sections.push(outputMappings);
|
|
4966
5346
|
const workflowText = sections.join("\n\n");
|
|
4967
5347
|
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, workflowText, producedChildOutputPaths);
|
|
4968
5348
|
const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
|
|
4969
|
-
const
|
|
5349
|
+
const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: _id, workflowState: "failed", inventoryComplete: true, trace: partial.trace, children: partial.children });
|
|
5350
|
+
const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
|
|
5351
|
+
const receipt = terminalWorkflowReceipt(_id, "failed", partial.children, workflowChildren, terminalOutcome, [...workflowHostSteps.values()]);
|
|
4970
5352
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
4971
5353
|
content: [{ type: "text", text: displayText }],
|
|
4972
5354
|
isError: true,
|
|
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:
|
|
5355
|
+
details: compactOptional<Details>({ mode: "workflow", runId: _id, results: partial.children.flatMap((child) => (child.results ?? []) as SingleResult[]), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
|
|
4974
5356
|
}, workflowFanoutBudget));
|
|
4975
5357
|
}
|
|
4976
5358
|
}
|
|
4977
5359
|
const directParams = requestParams;
|
|
5360
|
+
const requestedCwd = params.cwd;
|
|
4978
5361
|
const requestCwd = resolveRequestedCwd(ctx.cwd, directParams.cwd);
|
|
4979
5362
|
const paramsWithResolvedCwd = directParams.cwd === undefined ? directParams : { ...directParams, cwd: requestCwd };
|
|
4980
5363
|
const action = paramsWithResolvedCwd.action;
|
|
@@ -4984,14 +5367,45 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4984
5367
|
try {
|
|
4985
5368
|
requestSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
4986
5369
|
requestPiSessionId = ctx.sessionManager.getSessionId() ?? undefined;
|
|
4987
|
-
requestParentModel =
|
|
4988
|
-
?
|
|
4989
|
-
:
|
|
5370
|
+
requestParentModel = parentModelOverride !== undefined
|
|
5371
|
+
? parentModelOverride ?? undefined
|
|
5372
|
+
: preserveActiveSession
|
|
5373
|
+
? normalizeParentModel(ctx.model)
|
|
5374
|
+
: rememberParentModel(deps.state, requestSessionId, ctx.model);
|
|
4990
5375
|
} catch (error) {
|
|
4991
5376
|
if (action?.toLowerCase() !== "doctor" && action?.toLowerCase() !== "guide") throw error;
|
|
4992
5377
|
requestParentModel = normalizeParentModel(ctx.model);
|
|
4993
5378
|
}
|
|
4994
5379
|
if (action) {
|
|
5380
|
+
if (action === "worktree.cleanup") {
|
|
5381
|
+
if (deps.allowMutatingManagementActions === false) {
|
|
5382
|
+
return { content: [{ type: "text", text: "Action 'worktree.cleanup' is not available from child-safe subagent fanout mode." }], isError: true, details: { mode: "management", results: [] } };
|
|
5383
|
+
}
|
|
5384
|
+
if (paramsWithResolvedCwd.mode !== "plan") {
|
|
5385
|
+
return { content: [{ type: "text", text: "worktree.cleanup currently supports mode='plan' only; apply/removal is not available yet." }], isError: true, details: { mode: "management", results: [] } };
|
|
5386
|
+
}
|
|
5387
|
+
if (paramsWithResolvedCwd.planId !== undefined) {
|
|
5388
|
+
return { content: [{ type: "text", text: "worktree.cleanup plan mode does not accept planId; apply is not available yet." }], isError: true, details: { mode: "management", results: [] } };
|
|
5389
|
+
}
|
|
5390
|
+
try {
|
|
5391
|
+
const created = createWorktreeCleanupPlan({
|
|
5392
|
+
repo: paramsWithResolvedCwd.repo?.trim()
|
|
5393
|
+
? path.isAbsolute(paramsWithResolvedCwd.repo) ? paramsWithResolvedCwd.repo : path.resolve(requestCwd, paramsWithResolvedCwd.repo)
|
|
5394
|
+
: requestCwd,
|
|
5395
|
+
...(paramsWithResolvedCwd.handoffPath ? { handoffPath: path.isAbsolute(paramsWithResolvedCwd.handoffPath) ? paramsWithResolvedCwd.handoffPath : path.resolve(requestCwd, paramsWithResolvedCwd.handoffPath) } : {}),
|
|
5396
|
+
...(deps.config.worktreeBaseDir ? { worktreeBaseDir: deps.config.worktreeBaseDir } : {}),
|
|
5397
|
+
foregroundRunOwnership: (runId: string) => {
|
|
5398
|
+
if (deps.state.foregroundControls.has(runId)) return "active" as const;
|
|
5399
|
+
const remembered = deps.state.foregroundRuns?.get(runId);
|
|
5400
|
+
if (!remembered || remembered.children.length === 0 || remembered.children.some((child) => child.status === "detached")) return "unknown" as const;
|
|
5401
|
+
return "terminal" as const;
|
|
5402
|
+
},
|
|
5403
|
+
});
|
|
5404
|
+
return { content: [{ type: "text", text: formatWorktreeCleanupPlan(created) }], details: { mode: "management", results: [] } };
|
|
5405
|
+
} catch (error) {
|
|
5406
|
+
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
|
|
5407
|
+
}
|
|
5408
|
+
}
|
|
4995
5409
|
if (action === "worktree.discard") {
|
|
4996
5410
|
if (deps.allowMutatingManagementActions === false) {
|
|
4997
5411
|
return { content: [{ type: "text", text: "Action 'worktree.discard' is not available from child-safe subagent fanout mode." }], isError: true, details: { mode: "management", results: [] } };
|
|
@@ -5019,6 +5433,30 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5019
5433
|
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
|
|
5020
5434
|
}
|
|
5021
5435
|
}
|
|
5436
|
+
if (action === "lane.status" || action === "lane.recordMerge" || action === "lane.recordSupersession") {
|
|
5437
|
+
if (action !== "lane.status" && deps.allowMutatingManagementActions === false) {
|
|
5438
|
+
return { content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5439
|
+
}
|
|
5440
|
+
const laneId = paramsWithResolvedCwd.laneId?.trim();
|
|
5441
|
+
if (!laneId) return { content: [{ type: "text", text: `${action} requires laneId.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5442
|
+
const handoffPath = paramsWithResolvedCwd.handoffPath?.trim();
|
|
5443
|
+
if (!handoffPath) return { content: [{ type: "text", text: `${action} requires handoffPath for the existing parallel handoff manifest.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5444
|
+
const manifestPath = path.isAbsolute(handoffPath) ? handoffPath : path.resolve(requestCwd, handoffPath);
|
|
5445
|
+
try {
|
|
5446
|
+
if (action === "lane.status") {
|
|
5447
|
+
let manifest;
|
|
5448
|
+
try { manifest = readParallelHandoffManifest(manifestPath); } catch { manifest = undefined; }
|
|
5449
|
+
if (manifest && manifest.runId !== laneId) throw new Error(`Lane '${laneId}' does not match manifest run '${manifest.runId}'.`);
|
|
5450
|
+
return { content: [{ type: "text", text: formatStoredParallelHandoffCleanup(manifestPath, manifest) }], details: { mode: "management", results: [] } };
|
|
5451
|
+
}
|
|
5452
|
+
const recorded = action === "lane.recordMerge"
|
|
5453
|
+
? recordParallelHandoffMerge({ manifestPath, laneId, merge: paramsWithResolvedCwd.merge })
|
|
5454
|
+
: recordParallelHandoffSupersession({ manifestPath, laneId, supersession: paramsWithResolvedCwd.supersession });
|
|
5455
|
+
return { content: [{ type: "text", text: recorded.text }], details: { mode: "management", results: [], parallelHandoff: recorded.reference } };
|
|
5456
|
+
} catch (error) {
|
|
5457
|
+
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
|
|
5458
|
+
}
|
|
5459
|
+
}
|
|
5022
5460
|
if ((HERDR_PROJECT_PANE_ACTIONS as readonly string[]).includes(action)) {
|
|
5023
5461
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
5024
5462
|
return { content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }], isError: true, details: { mode: "management", results: [] } };
|
|
@@ -5211,7 +5649,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5211
5649
|
}
|
|
5212
5650
|
const spawnBudget = getSpawnBudgetSnapshot(deps.state, deps.config, currentSessionId);
|
|
5213
5651
|
const activeAsyncCapacity = currentSessionId
|
|
5214
|
-
? getActiveAsyncCapacitySnapshot(currentSessionId, resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession), { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []) })
|
|
5652
|
+
? getActiveAsyncCapacitySnapshot(currentSessionId, resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession), { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })
|
|
5215
5653
|
: { used: 0, limit: resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession) ?? 0 };
|
|
5216
5654
|
deps.state.activeAsyncCapacity = activeAsyncCapacity;
|
|
5217
5655
|
return {
|
|
@@ -5256,10 +5694,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5256
5694
|
if (paramsWithResolvedCwd.view) {
|
|
5257
5695
|
return withBudget({ content: [{ type: "text", text: "action='debug.run' does not support status views." }], isError: true, details: { mode: "management", results: [] } });
|
|
5258
5696
|
}
|
|
5259
|
-
return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots })));
|
|
5697
|
+
return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots, abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })));
|
|
5260
5698
|
}
|
|
5261
5699
|
if (paramsWithResolvedCwd.view === "fleet") {
|
|
5262
|
-
return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots })));
|
|
5700
|
+
return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots, abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })));
|
|
5263
5701
|
}
|
|
5264
5702
|
if (targetRunId) {
|
|
5265
5703
|
try {
|
|
@@ -5290,12 +5728,15 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5290
5728
|
});
|
|
5291
5729
|
}
|
|
5292
5730
|
}
|
|
5293
|
-
return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots })));
|
|
5731
|
+
return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots, abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })));
|
|
5294
5732
|
}
|
|
5295
5733
|
if (action === "resume") {
|
|
5296
5734
|
return resumeAsyncRun(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel, signal }));
|
|
5297
5735
|
}
|
|
5298
5736
|
if (action === "steer") {
|
|
5737
|
+
if (paramsWithResolvedCwd.mode !== undefined && resolveSteerDeliveryMode(paramsWithResolvedCwd.mode) === undefined) {
|
|
5738
|
+
return { content: [{ type: "text", text: "action='steer' mode must be 'steer', 'follow_up', or 'auto'." }], isError: true, details: { mode: "management", results: [] } };
|
|
5739
|
+
}
|
|
5299
5740
|
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
5300
5741
|
const message = (paramsWithResolvedCwd.message ?? paramsWithResolvedCwd.task ?? "").trim();
|
|
5301
5742
|
if (!message) return { content: [{ type: "text", text: "action='steer' requires message." }], isError: true, details: { mode: "management", results: [] } };
|
|
@@ -5308,7 +5749,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5308
5749
|
if (directoryStatus?.mode === "workflow") {
|
|
5309
5750
|
const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: directoryStatus.runId || runId, asyncDirRoot: DIRS.async });
|
|
5310
5751
|
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
5311
|
-
return steerWorkflowForegroundTarget({ target: route.target, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal });
|
|
5752
|
+
return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
|
|
5312
5753
|
}
|
|
5313
5754
|
if (location.asyncDir) {
|
|
5314
5755
|
const unsupported = externalRunnerControlError(location.asyncDir, "steer");
|
|
@@ -5318,7 +5759,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5318
5759
|
state: deps.state,
|
|
5319
5760
|
runId,
|
|
5320
5761
|
message,
|
|
5321
|
-
mode: paramsWithResolvedCwd.mode,
|
|
5762
|
+
mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode),
|
|
5322
5763
|
index: paramsWithResolvedCwd.index,
|
|
5323
5764
|
kill: deps.kill,
|
|
5324
5765
|
location,
|
|
@@ -5344,18 +5785,18 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5344
5785
|
const text = error instanceof Error ? error.message : String(error);
|
|
5345
5786
|
return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
|
|
5346
5787
|
}
|
|
5347
|
-
if (resolved?.kind === "nested") return steerNestedRun(omitUndefinedProperties({ target: resolved, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal }));
|
|
5788
|
+
if (resolved?.kind === "nested") return steerNestedRun(omitUndefinedProperties({ target: resolved, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal }));
|
|
5348
5789
|
if (resolved?.kind === "foreground") {
|
|
5349
5790
|
const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, childRunId: resolved.id, asyncDirRoot: DIRS.async });
|
|
5350
5791
|
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
5351
|
-
return steerWorkflowForegroundTarget({ target: route.target, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal });
|
|
5792
|
+
return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
|
|
5352
5793
|
}
|
|
5353
5794
|
if (resolved?.kind !== "async") return { content: [{ type: "text", text: `No async run found for '${targetRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5354
5795
|
const resolvedStatus = resolved.location.asyncDir ? readStatus(resolved.location.asyncDir) : null;
|
|
5355
5796
|
if (resolvedStatus?.mode === "workflow") {
|
|
5356
5797
|
const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: resolvedStatus.runId || resolved.id, asyncDirRoot: DIRS.async });
|
|
5357
5798
|
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
5358
|
-
return steerWorkflowForegroundTarget({ target: route.target, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal });
|
|
5799
|
+
return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
|
|
5359
5800
|
}
|
|
5360
5801
|
if (resolved.location.asyncDir) {
|
|
5361
5802
|
const unsupported = externalRunnerControlError(resolved.location.asyncDir, "steer");
|
|
@@ -5365,7 +5806,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5365
5806
|
state: deps.state,
|
|
5366
5807
|
runId: resolved.id,
|
|
5367
5808
|
message,
|
|
5368
|
-
mode: paramsWithResolvedCwd.mode,
|
|
5809
|
+
mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode),
|
|
5369
5810
|
index: paramsWithResolvedCwd.index,
|
|
5370
5811
|
kill: deps.kill,
|
|
5371
5812
|
location: resolved.location,
|
|
@@ -5446,7 +5887,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5446
5887
|
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
5888
|
const stopChild = deps.state.workflowChildStops?.get(targetRunId!);
|
|
5448
5889
|
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: [] } };
|
|
5890
|
+
if (!stopChild(resolution.child.id, `Workflow child '${resolution.child.id}' stopped.`)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in workflow ${workflowRunId} is not available to stop.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5450
5891
|
try {
|
|
5451
5892
|
fs.appendFileSync(path.join(asyncJob.asyncDir, "events.jsonl"), `${JSON.stringify({
|
|
5452
5893
|
type: "subagent.child-status",
|
|
@@ -5455,7 +5896,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5455
5896
|
childId: resolution.child.id,
|
|
5456
5897
|
status: "stopping",
|
|
5457
5898
|
ts: Date.now(),
|
|
5458
|
-
reason: "
|
|
5899
|
+
reason: "subagent-action",
|
|
5459
5900
|
source: "async",
|
|
5460
5901
|
stepIndex: resolution.child.index,
|
|
5461
5902
|
agent: resolution.child.step.agent,
|
|
@@ -5469,17 +5910,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5469
5910
|
}
|
|
5470
5911
|
return { content: [{ type: "text", text: `Stop requested for child ${resolution.child.id} in async workflow ${workflowRunId}.` }], details: { mode: "management", results: [] } };
|
|
5471
5912
|
}
|
|
5472
|
-
workflowController.abort(new Error("Workflow stopped
|
|
5913
|
+
workflowController.abort(new Error("Workflow stopped."));
|
|
5473
5914
|
return { content: [{ type: "text", text: `Stop requested for async workflow ${targetRunId}.` }], details: { mode: "management", results: [] } };
|
|
5474
5915
|
}
|
|
5475
5916
|
let resolved: ResolvedSubagentRunId | undefined;
|
|
5476
5917
|
if (paramsWithResolvedCwd.dir) {
|
|
5477
5918
|
try {
|
|
5478
5919
|
const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
|
|
5479
|
-
const existingStatus = readStatus(location.asyncDir ?? "");
|
|
5480
|
-
if (existingStatus?.mode === "workflow" && existingStatus.state === "running") {
|
|
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: [] } };
|
|
5482
|
-
}
|
|
5483
5920
|
const stopResult = stopAsyncRun(deps.state, location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir), deps.kill, location, paramsWithResolvedCwd.childId);
|
|
5484
5921
|
return stopResult ?? { content: [{ type: "text", text: `No running or queued async run was found for '${targetRunId ?? paramsWithResolvedCwd.dir}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5485
5922
|
} catch (error) {
|
|
@@ -5496,12 +5933,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5496
5933
|
}
|
|
5497
5934
|
if (resolved?.kind === "nested") return { content: [{ type: "text", text: "action='stop' supports current-session top-level async runs only." }], isError: true, details: { mode: "management", results: [] } };
|
|
5498
5935
|
if (resolved?.kind === "foreground") return { content: [{ type: "text", text: "action='stop' supports async runs only. Use action='interrupt' for foreground runs." }], isError: true, details: { mode: "management", results: [] } };
|
|
5499
|
-
if (resolved?.kind === "async") {
|
|
5500
|
-
const existingStatus = readStatus(resolved.location.asyncDir ?? "");
|
|
5501
|
-
if (existingStatus?.mode === "workflow" && existingStatus.state === "running") {
|
|
5502
|
-
return { content: [{ type: "text", text: `Workflow ${resolved.id} is not controlled by this extension runtime; reload recovery cannot stop it safely.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5503
|
-
}
|
|
5504
|
-
}
|
|
5505
5936
|
const stopResult = stopAsyncRun(
|
|
5506
5937
|
deps.state,
|
|
5507
5938
|
resolved?.kind === "async" ? resolved.id : targetRunId,
|
|
@@ -5613,16 +6044,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5613
6044
|
|
|
5614
6045
|
const scope: AgentScope = resolveExecutionAgentScope(effectiveParams.agentScope);
|
|
5615
6046
|
const effectiveCwd = effectiveParams.cwd ?? ctx.cwd;
|
|
6047
|
+
const cwdError = preflightLaunchCwd(requestedCwd ?? effectiveCwd, effectiveCwd);
|
|
6048
|
+
if (cwdError) return buildRequestedModeError(effectiveParams, cwdError);
|
|
5616
6049
|
const parentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
|
|
5617
|
-
const discovered = deps.discoverAgents(effectiveCwd, scope);
|
|
6050
|
+
const discovered = deps.discoverAgents(effectiveCwd, scope, requestParentModel?.provider);
|
|
5618
6051
|
const discoveredAgents = discovered.agents;
|
|
5619
|
-
const
|
|
6052
|
+
const unknownAgentDiagnosticContext = diagnosticContextFromDiscovery(discovered, effectiveCwd, scope);
|
|
6053
|
+
const canonicalParams = canonicalizeExecutionParams(effectiveParams, discoveredAgents, discovered.agentDiagnostics, unknownAgentDiagnosticContext);
|
|
5620
6054
|
if (canonicalParams.error) return buildRequestedModeError(effectiveParams, canonicalParams.error);
|
|
5621
6055
|
effectiveParams = canonicalParams.params!;
|
|
5622
6056
|
const modelScope = discovered.modelScope;
|
|
5623
6057
|
effectiveParams = applySingleAgentLaunchDefaults(effectiveParams, discoveredAgents);
|
|
5624
|
-
const turnBudget = resolveTurnBudgetConfig(effectiveParams.turnBudget ?? deps.config.turnBudget);
|
|
5625
|
-
if (turnBudget.error) return buildRequestedModeError(effectiveParams, turnBudget.error);
|
|
5626
6058
|
// An agent-level defaultContext is a preference, unlike an explicit request.
|
|
5627
6059
|
// Prefer fork only when the parent session is persisted and has a current leaf;
|
|
5628
6060
|
// otherwise use fresh immediately instead of launching a guaranteed-to-fail fork.
|
|
@@ -5665,6 +6097,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5665
6097
|
hasTasks,
|
|
5666
6098
|
hasSingle,
|
|
5667
6099
|
allowClarifyTaskPrompt,
|
|
6100
|
+
unknownAgentDiagnosticContext,
|
|
5668
6101
|
);
|
|
5669
6102
|
if (validationError) return validationError;
|
|
5670
6103
|
|
|
@@ -5679,6 +6112,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5679
6112
|
if (spawnPreflight.error) return spawnBudgetErrorResult(spawnPreflight.error, foregroundMode);
|
|
5680
6113
|
|
|
5681
6114
|
let forkSessionFileForIndex: (idx?: number) => string | undefined = () => undefined;
|
|
6115
|
+
let prepareForkSessionForIndex: (idx?: number) => Promise<void> = async () => {};
|
|
5682
6116
|
let forkThinkingOverrideForIndex: (idx?: number) => AgentConfig["thinking"] | undefined = () => undefined;
|
|
5683
6117
|
let prepareForkThinking = (_agentName: string, _index: number, _modelOverride?: string, _modelOverrideFromParent?: boolean): void => {};
|
|
5684
6118
|
const forkThinkingRequirements = new Map<number, boolean>();
|
|
@@ -5700,6 +6134,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5700
6134
|
parentModel,
|
|
5701
6135
|
forkAvailableModels,
|
|
5702
6136
|
agentConfig?.modelProvider ?? parentModel?.provider,
|
|
6137
|
+
{ source: "inherited" },
|
|
5703
6138
|
);
|
|
5704
6139
|
const candidates = buildModelCandidates(
|
|
5705
6140
|
primaryModel,
|
|
@@ -5714,9 +6149,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5714
6149
|
|| candidates.some((candidate) => forkedChildRequiresThinkingOff(candidate, forkAvailableModels, parentModel?.provider)),
|
|
5715
6150
|
);
|
|
5716
6151
|
};
|
|
6152
|
+
const pruneSession = contextPolicy.usesFork && deps.config.forkContext?.mode === "pruned"
|
|
6153
|
+
? await createPrunedForkSessionWriter(ctx, deps.config.forkContext, signal)
|
|
6154
|
+
: undefined;
|
|
5717
6155
|
const forkContextResolver = createForkContextResolver(ctx.sessionManager, contextPolicy.usesFork ? "fork" : undefined, {
|
|
5718
6156
|
forceThinkingOffForIndex: (index) => forkThinkingRequirements.get(index) ?? true,
|
|
6157
|
+
...(pruneSession ? { pruneSession } : {}),
|
|
5719
6158
|
});
|
|
6159
|
+
prepareForkSessionForIndex = forkContextResolver.prepareSessionForIndex;
|
|
5720
6160
|
forkSessionFileForIndex = forkContextResolver.sessionFileForIndex;
|
|
5721
6161
|
forkThinkingOverrideForIndex = forkContextResolver.thinkingOverrideForIndex;
|
|
5722
6162
|
} catch (error) {
|
|
@@ -5760,8 +6200,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5760
6200
|
runId: asyncRunId,
|
|
5761
6201
|
kind: "runner",
|
|
5762
6202
|
asyncDir: path.join(DIRS.async, asyncRunId),
|
|
5763
|
-
}, { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []) });
|
|
5764
|
-
deps.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(requestSessionId, activeLimit, { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []) });
|
|
6203
|
+
}, { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
6204
|
+
deps.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(requestSessionId, activeLimit, { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
5765
6205
|
} catch (error) {
|
|
5766
6206
|
if (error instanceof ActiveAsyncCapacityError) {
|
|
5767
6207
|
deps.state.activeAsyncCapacity = error.snapshot;
|
|
@@ -5824,6 +6264,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5824
6264
|
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent);
|
|
5825
6265
|
return forkSessionFileForIndex(idx);
|
|
5826
6266
|
};
|
|
6267
|
+
const prepareForkSessionForTask: PrepareForkSessionForTask = async (agentName, idx = 0, modelOverride, modelOverrideFromParent) => {
|
|
6268
|
+
if (!shouldForkAgent(contextPolicy, agentName)) return;
|
|
6269
|
+
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent);
|
|
6270
|
+
await prepareForkSessionForIndex(idx);
|
|
6271
|
+
};
|
|
5827
6272
|
const forkThinkingOverrideForTask: ForkThinkingOverrideForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent) => {
|
|
5828
6273
|
if (!shouldForkAgent(contextPolicy, agentName)) return delegatedThinkingOverride;
|
|
5829
6274
|
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent);
|
|
@@ -5836,8 +6281,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5836
6281
|
const childSessionFileForIndex = (idx?: number) =>
|
|
5837
6282
|
path.join(sessionDirForIndex(idx), "session.jsonl");
|
|
5838
6283
|
try {
|
|
5839
|
-
if (!(effectiveParams.clarify === true && ctx.hasUI)) {
|
|
5840
|
-
preflightForkSessionsForStaticTasks(effectiveParams, contextPolicy,
|
|
6284
|
+
if (!(effectiveParams.clarify === true && ctx.hasUI) || deps.config.forkContext?.mode === "pruned") {
|
|
6285
|
+
await preflightForkSessionsForStaticTasks(effectiveParams, contextPolicy, prepareForkSessionForTask, deps.config.chain?.dynamicFanout?.maxItems);
|
|
5841
6286
|
}
|
|
5842
6287
|
} catch (error) {
|
|
5843
6288
|
activeAsyncCapacity?.rollback();
|
|
@@ -5906,10 +6351,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5906
6351
|
const execData: ExecutionContextData = omitUndefinedProperties({
|
|
5907
6352
|
params: effectiveParams,
|
|
5908
6353
|
effectiveCwd,
|
|
6354
|
+
requestedCwd,
|
|
5909
6355
|
ctx,
|
|
5910
6356
|
signal,
|
|
5911
6357
|
onUpdate: onUpdateWithContext,
|
|
5912
6358
|
agents,
|
|
6359
|
+
unknownAgentDiagnosticContext,
|
|
5913
6360
|
recoveryAgents: discoveredAgents,
|
|
5914
6361
|
runId,
|
|
5915
6362
|
shareEnabled,
|
|
@@ -5927,7 +6374,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5927
6374
|
intercomBridge,
|
|
5928
6375
|
nestedRoute,
|
|
5929
6376
|
timeoutMs: foregroundTimeout.timeoutMs,
|
|
5930
|
-
turnBudget: turnBudget.turnBudget,
|
|
5931
6377
|
toolBudget: runToolBudget.toolBudget,
|
|
5932
6378
|
usageBudget: usageBudget.budget,
|
|
5933
6379
|
allowZeroToolBudget,
|
|
@@ -5942,6 +6388,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5942
6388
|
runFanoutBudget,
|
|
5943
6389
|
topLevelAsyncCapacityEligible,
|
|
5944
6390
|
activeAsyncCapacity,
|
|
6391
|
+
workflowChildPermitLaunch,
|
|
5945
6392
|
});
|
|
5946
6393
|
|
|
5947
6394
|
const foregroundDescription = selectedAgentNames.length === 1
|
|
@@ -6071,6 +6518,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6071
6518
|
mode: foregroundMode,
|
|
6072
6519
|
state,
|
|
6073
6520
|
agent: agentsForSummary[0],
|
|
6521
|
+
...(details?.results.length === 1 && details.results[0]?.sessionName ? { sessionName: details.results[0].sessionName } : {}),
|
|
6074
6522
|
agents: agentsForSummary,
|
|
6075
6523
|
...(agentsForSummary.length === 1 && (type === "subagent.nested.started" ? startedLaunches[0]?.model : details?.results[0]?.model) ? { model: type === "subagent.nested.started" ? startedLaunches[0]?.model : details?.results[0]?.model } : {}),
|
|
6076
6524
|
...(agentsForSummary.length === 1 && (type === "subagent.nested.started" ? startedLaunches[0]?.thinking : details?.results[0]?.thinking) ? { thinking: type === "subagent.nested.started" ? startedLaunches[0]?.thinking : details?.results[0]?.thinking } : {}),
|
|
@@ -6089,6 +6537,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6089
6537
|
: details?.results.length
|
|
6090
6538
|
? { steps: details.results.map((child) => ({
|
|
6091
6539
|
agent: child.agent,
|
|
6540
|
+
...(child.sessionName ? { sessionName: child.sessionName } : {}),
|
|
6092
6541
|
status: child.interrupted || child.detached ? "paused" as const : child.exitCode === 0 ? "complete" as const : "failed" as const,
|
|
6093
6542
|
...(child.model ? { model: child.model } : {}),
|
|
6094
6543
|
...(child.thinking ? { thinking: child.thinking } : {}),
|
|
@@ -6112,10 +6561,15 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6112
6561
|
}
|
|
6113
6562
|
if (workflowLaunchObserver) {
|
|
6114
6563
|
const singleTask = hasTasks && effectiveParams.tasks?.length === 1 ? effectiveParams.tasks[0] : undefined;
|
|
6564
|
+
const singleSessionName = hasSingle
|
|
6565
|
+
? deriveChildSessionName({ agent: effectiveParams.agent!, task: effectiveParams.task })
|
|
6566
|
+
: singleTask
|
|
6567
|
+
? deriveChildSessionName({ agent: singleTask.agent, task: singleTask.task })
|
|
6568
|
+
: undefined;
|
|
6115
6569
|
const launch = hasSingle
|
|
6116
|
-
? { agent: effectiveParams.agent!, sessionFile: childSessionFileForTask(effectiveParams.agent!, 0, effectiveParams.model), async: effectiveAsync,
|
|
6570
|
+
? { agent: effectiveParams.agent!, ...(singleSessionName ? { sessionName: singleSessionName } : {}), sessionFile: childSessionFileForTask(effectiveParams.agent!, 0, effectiveParams.model), async: effectiveAsync, runId: effectiveAsync ? asyncRunId : runId }
|
|
6117
6571
|
: singleTask
|
|
6118
|
-
? { agent: singleTask.agent, sessionFile: childSessionFileForTask(singleTask.agent, 0, singleTask.model), async: effectiveAsync,
|
|
6572
|
+
? { agent: singleTask.agent, ...(singleSessionName ? { sessionName: singleSessionName } : {}), sessionFile: childSessionFileForTask(singleTask.agent, 0, singleTask.model), async: effectiveAsync, runId: effectiveAsync ? asyncRunId : runId }
|
|
6119
6573
|
: undefined;
|
|
6120
6574
|
if (launch) {
|
|
6121
6575
|
workflowLaunchObservers.delete(params);
|
|
@@ -6167,15 +6621,15 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6167
6621
|
): Promise<AgentToolResult<Details>> => {
|
|
6168
6622
|
const normalizedAction = typeof params.action === "string" ? params.action.trim() : params.action;
|
|
6169
6623
|
const requestParams = normalizedAction ? { ...params, action: normalizedAction } : params;
|
|
6170
|
-
if (normalizedAction) return execute(id, requestParams, signal, onUpdate, ctx);
|
|
6624
|
+
if (normalizedAction) return execute(id, requestParams, signal, onUpdate, ctx).then(withAggregatedToolUsage);
|
|
6171
6625
|
const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth);
|
|
6172
6626
|
const dispatchParams = applyForceTopLevelAsyncOverride(requestParams, depth, deps.config.forceTopLevelAsync === true);
|
|
6173
6627
|
const runsForeground = dispatchParams.clarify === true || (dispatchParams.async ?? deps.asyncByDefault) !== true;
|
|
6174
|
-
if (!runsForeground) return execute(id, requestParams, signal, onUpdate, ctx);
|
|
6628
|
+
if (!runsForeground) return execute(id, requestParams, signal, onUpdate, ctx).then(withAggregatedToolUsage);
|
|
6175
6629
|
if (deps.state.subagentInProgress === true) return duplicateSubagentCallResult(requestParams);
|
|
6176
6630
|
deps.state.subagentInProgress = true;
|
|
6177
6631
|
try {
|
|
6178
|
-
return await execute(id, requestParams, signal, onUpdate, ctx);
|
|
6632
|
+
return withAggregatedToolUsage(await execute(id, requestParams, signal, onUpdate, ctx));
|
|
6179
6633
|
} finally {
|
|
6180
6634
|
deps.state.subagentInProgress = false;
|
|
6181
6635
|
}
|
|
@@ -6188,11 +6642,15 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6188
6642
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
6189
6643
|
ctx: ExtensionContext,
|
|
6190
6644
|
): Promise<AgentToolResult<Details>> => {
|
|
6191
|
-
const normalized = normalizePublicSubagentExecution(params
|
|
6645
|
+
const normalized = normalizePublicSubagentExecution(params);
|
|
6192
6646
|
if (!normalized.ok) {
|
|
6193
6647
|
return Promise.resolve({ content: [{ type: "text", text: normalized.error }], isError: true, details: { mode: normalized.mode, results: [] } });
|
|
6194
6648
|
}
|
|
6195
|
-
|
|
6649
|
+
const loaded = loadWorkflowScriptPath(normalized.params, ctx.cwd);
|
|
6650
|
+
if (loaded.error) {
|
|
6651
|
+
return Promise.resolve({ content: [{ type: "text", text: loaded.error }], isError: true, details: { mode: normalized.params.action ? "management" : "workflow", results: [] } });
|
|
6652
|
+
}
|
|
6653
|
+
return executeWithSingleDispatchGuard(id, loaded.params!, signal, onUpdate, ctx);
|
|
6196
6654
|
};
|
|
6197
6655
|
|
|
6198
6656
|
const executeDelegated = async (
|
|
@@ -6206,15 +6664,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6206
6664
|
const privateParams = delegatedParams as SubagentParamsLike & {
|
|
6207
6665
|
delegatedThinkingOverride?: AgentConfig["thinking"];
|
|
6208
6666
|
delegatedAllowZeroToolBudget?: true;
|
|
6667
|
+
delegatedWorkflowPermit?: WorkflowChildPermit;
|
|
6209
6668
|
};
|
|
6210
6669
|
const thinkingOverride = privateParams.delegatedThinkingOverride;
|
|
6211
6670
|
const allowZeroToolBudget = privateParams.delegatedAllowZeroToolBudget === true;
|
|
6671
|
+
const workflowPermit = privateParams.delegatedWorkflowPermit;
|
|
6212
6672
|
delete privateParams.delegatedThinkingOverride;
|
|
6213
6673
|
delete privateParams.delegatedAllowZeroToolBudget;
|
|
6674
|
+
delete privateParams.delegatedWorkflowPermit;
|
|
6214
6675
|
if (thinkingOverride !== undefined) delegatedThinkingOverrides.set(delegatedParams, thinkingOverride);
|
|
6215
6676
|
if (allowZeroToolBudget) delegatedZeroToolBudgets.add(delegatedParams);
|
|
6677
|
+
if (workflowPermit) workflowPermitContexts.set(delegatedParams, { root: workflowPermit });
|
|
6216
6678
|
delegatedExecutions.add(delegatedParams);
|
|
6217
|
-
return execute(id, delegatedParams, signal, onUpdate, ctx);
|
|
6679
|
+
return withAggregatedToolUsage(await execute(id, delegatedParams, signal, onUpdate, ctx));
|
|
6218
6680
|
};
|
|
6219
6681
|
|
|
6220
6682
|
const executeScheduled = (
|