@selesai/code 0.13.16 → 0.13.19
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 +41 -0
- package/README.md +2 -14
- package/dist/bun/cli.d.ts +3 -1
- package/dist/bun/cli.js +3 -9
- package/dist/bun/{register-bedrock.js → runtime-setup.js} +5 -0
- package/dist/bun/sandbox-env-setup.d.ts +1 -0
- package/dist/bun/sandbox-env-setup.js +3 -0
- package/dist/cli/auth-command.d.ts +23 -0
- package/dist/cli/auth-command.js +102 -0
- package/dist/cli/config-selector.js +2 -1
- package/dist/cli/setup.d.ts +1 -0
- package/dist/cli/setup.js +11 -0
- package/dist/cli.js +2 -14
- package/dist/config.d.ts +3 -2
- package/dist/config.js +9 -4
- package/dist/core/agent-session-runtime.js +15 -7
- package/dist/core/agent-session.d.ts +2 -6
- package/dist/core/agent-session.js +15 -4
- package/dist/core/compaction/branch-summarization.d.ts +1 -1
- package/dist/core/compaction/branch-summarization.js +2 -1
- package/dist/core/extensions/loader.js +3 -0
- package/dist/core/extensions/types.d.ts +16 -2
- package/dist/core/http-dispatcher.js +2 -0
- package/dist/core/keybindings.d.ts +5 -0
- package/dist/core/keybindings.js +4 -0
- package/dist/core/messages.d.ts +1 -1
- package/dist/core/model-config.d.ts +15 -0
- package/dist/core/model-config.js +3 -0
- package/dist/core/model-runtime.d.ts +1 -0
- package/dist/core/model-runtime.js +5 -2
- package/dist/core/provider-composer.js +8 -2
- package/dist/core/session-export.d.ts +3 -0
- package/dist/core/session-export.js +31 -0
- package/dist/core/session-manager.d.ts +3 -2
- package/dist/core/session-manager.js +40 -13
- package/dist/core/skills.d.ts +1 -1
- package/dist/core/skills.js +4 -2
- package/dist/core/system-prompt.js +10 -10
- package/dist/core/tools/bash.js +3 -117
- package/dist/core/tools/edit.d.ts +1 -12
- package/dist/core/tools/edit.js +5 -163
- package/dist/core/tools/find.js +4 -55
- package/dist/core/tools/grep.js +4 -60
- package/dist/core/tools/ls.js +4 -49
- package/dist/core/tools/read.d.ts +4 -0
- package/dist/core/tools/read.js +10 -122
- package/dist/core/tools/renderers/bash.d.ts +11 -0
- package/dist/core/tools/renderers/bash.js +129 -0
- package/dist/core/tools/renderers/edit.d.ts +22 -0
- package/dist/core/tools/renderers/edit.js +166 -0
- package/dist/core/tools/renderers/find.d.ts +9 -0
- package/dist/core/tools/renderers/find.js +63 -0
- package/dist/core/tools/renderers/grep.d.ts +9 -0
- package/dist/core/tools/renderers/grep.js +68 -0
- package/dist/core/tools/renderers/index.d.ts +27 -0
- package/dist/core/tools/renderers/index.js +46 -0
- package/dist/core/tools/renderers/ls.d.ts +9 -0
- package/dist/core/tools/renderers/ls.js +57 -0
- package/dist/core/tools/renderers/read.d.ts +10 -0
- package/dist/core/tools/renderers/read.js +128 -0
- package/dist/core/tools/renderers/write.d.ts +9 -0
- package/dist/core/tools/renderers/write.js +152 -0
- package/dist/core/tools/write.js +5 -146
- package/dist/extensions/agent-browser.ts +1 -2
- package/dist/extensions/package.json +0 -1
- package/dist/extensions/pi-hermes-memory/CHANGELOG.md +18 -0
- package/dist/extensions/pi-hermes-memory/README.md +16 -12
- package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +1 -1
- package/dist/extensions/pi-hermes-memory/package-lock.json +587 -62
- package/dist/extensions/pi-hermes-memory/package.json +4 -4
- package/dist/extensions/pi-hermes-memory/src/config.ts +32 -0
- package/dist/extensions/pi-hermes-memory/src/constants.ts +12 -2
- package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +109 -51
- package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +9 -3
- package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +257 -98
- package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +9 -2
- package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +5 -0
- package/dist/extensions/pi-hermes-memory/src/index.ts +22 -2
- package/dist/extensions/pi-hermes-memory/src/paths.ts +1 -1
- package/dist/extensions/pi-hermes-memory/src/store/db.ts +6 -0
- package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +58 -1
- package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +60 -8
- package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +169 -7
- package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +7 -21
- package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +134 -15
- package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +4 -2
- package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +24 -9
- package/dist/extensions/pi-hermes-memory/src/types.ts +11 -0
- package/dist/extensions/pi-hermes-memory/tests/config.test.ts +42 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +159 -3
- package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +452 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +39 -0
- package/dist/extensions/pi-hermes-memory/tests/integration/corruption-recovery-integration.test.ts +91 -0
- package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +29 -0
- package/dist/extensions/pi-hermes-memory/tests/store/fts-query.test.ts +116 -0
- package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +163 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +233 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +12 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +175 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +2 -2
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +22 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +37 -1
- package/dist/extensions/pi-intercom/CHANGELOG.md +13 -0
- package/dist/extensions/pi-intercom/README.md +16 -2
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +19 -2
- package/dist/extensions/pi-intercom/broker/spawn.ts +4 -4
- package/dist/extensions/pi-intercom/index.ts +1 -1
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +121 -2
- package/dist/extensions/pi-intercom/package.json +1 -1
- package/dist/extensions/pi-intercom/project-agent.test.ts +1 -1
- package/dist/extensions/pi-intercom/project-agent.ts +1 -6
- package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +7 -6
- package/dist/extensions/pi-rewind-hook/index.test.ts +7 -0
- package/dist/extensions/pi-subagents/.github/workflows/test.yml +36 -4
- package/dist/extensions/pi-subagents/CHANGELOG.md +120 -0
- package/dist/extensions/pi-subagents/README.md +3 -3
- package/dist/extensions/pi-subagents/agents/researcher.md +23 -13
- package/dist/extensions/pi-subagents/agents/reviewer.md +1 -1
- package/dist/extensions/pi-subagents/agents/scout.md +1 -1
- package/dist/extensions/pi-subagents/docs/agents.md +32 -16
- package/dist/extensions/pi-subagents/docs/configuration.md +41 -15
- package/dist/extensions/pi-subagents/docs/extension-api.md +109 -6
- package/dist/extensions/pi-subagents/docs/missions.md +2 -0
- package/dist/extensions/pi-subagents/docs/models.md +58 -1
- package/dist/extensions/pi-subagents/docs/observability.md +50 -12
- package/dist/extensions/pi-subagents/docs/tool-reference.md +17 -10
- package/dist/extensions/pi-subagents/docs/watchdog.md +1 -1
- package/dist/extensions/pi-subagents/docs/workflows.md +26 -9
- package/dist/extensions/pi-subagents/package-lock.json +747 -177
- package/dist/extensions/pi-subagents/package.json +6 -4
- package/dist/extensions/pi-subagents/runner-server-preload.mjs +13 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +2 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +21 -4
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
- package/dist/extensions/pi-subagents/src/agents/advertised-agent-prompt.ts +63 -0
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +61 -16
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +8 -0
- package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +0 -1
- package/dist/extensions/pi-subagents/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
- package/dist/extensions/pi-subagents/src/api/preflight.ts +7 -4
- package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -2
- package/dist/extensions/pi-subagents/src/api/workflow-resources.ts +6 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +4 -2
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +2 -10
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +9 -11
- package/dist/extensions/pi-subagents/src/extension/index.ts +99 -10
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +13 -0
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +7 -23
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +11 -8
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +25 -7
- package/dist/extensions/pi-subagents/src/integrations/pi-web-session-liveness.ts +73 -0
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +228 -136
- package/dist/extensions/pi-subagents/src/intercom/supervisor-ui.ts +244 -0
- package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +37 -16
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +18 -18
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +51 -25
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +46 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +9 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +10 -12
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -15
- package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +40 -29
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +8 -0
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +79 -247
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +88 -12
- package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +6 -6
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +24 -24
- package/dist/extensions/pi-subagents/src/runs/background/retained-nested-route-tracker.ts +96 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-child-session.ts +642 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +75 -6
- package/dist/extensions/pi-subagents/src/runs/background/runner-aliases.ts +163 -0
- package/dist/extensions/pi-subagents/src/runs/background/runner-child-launch.ts +86 -0
- package/dist/extensions/pi-subagents/src/runs/background/runner-child-sessions.ts +31 -0
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +18 -4
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +597 -1092
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +3 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +20 -17
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +296 -394
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +9 -5
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +630 -283
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +8 -5
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +78 -98
- package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +16 -3
- package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +47 -47
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -2
- package/dist/extensions/pi-subagents/src/runs/shared/child-hooks.ts +174 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +14 -3
- package/dist/extensions/pi-subagents/src/runs/shared/child-launch.ts +319 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-lifecycle.ts +25 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-runtime-config.ts +126 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-session.ts +373 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-tool-plan.ts +530 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +11 -11
- package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +30 -20
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
- package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +84 -15
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +88 -12
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +37 -53
- package/dist/extensions/pi-subagents/src/runs/shared/nested-path.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +18 -7
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +57 -12
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +3 -4
- package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +0 -11
- package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/shared/readonly-drain-observation.ts +42 -0
- package/dist/extensions/pi-subagents/src/runs/shared/readonly-model-continuation.ts +69 -0
- package/dist/extensions/pi-subagents/src/runs/shared/readonly-session-evidence.ts +307 -0
- package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +8 -21
- package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +3 -30
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +17 -4
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +99 -386
- package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +18 -62
- package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +0 -14
- package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +31 -9
- package/dist/extensions/pi-subagents/src/runs/shared/worktree-setup-command.ts +190 -0
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +506 -226
- package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +1 -1
- package/dist/extensions/pi-subagents/src/shared/jsonl-writer.ts +11 -0
- package/dist/extensions/pi-subagents/src/shared/model-response-aliases.ts +13 -0
- package/dist/extensions/pi-subagents/src/shared/thinking-ceiling.ts +0 -6
- package/dist/extensions/pi-subagents/src/shared/types.ts +145 -89
- package/dist/extensions/pi-subagents/src/shared/utils.ts +13 -6
- package/dist/extensions/pi-subagents/src/shared/watch-strategy.ts +2 -0
- package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +18 -13
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +0 -6
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +1 -1
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +11 -6
- package/dist/extensions/pi-subagents/src/tui/render.ts +277 -46
- package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +0 -1
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +12 -12
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -3
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +116 -15
- package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +15 -18
- package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +57 -8
- package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +19 -19
- package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +3 -3
- package/dist/extensions/pi-subagents/src/workflows/workflow-resources.ts +97 -22
- package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +3 -0
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +336 -21
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-1.test.ts +1181 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-2.test.ts +2240 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-3.test.ts +2268 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-4.test.ts +1512 -0
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +231 -16
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +4 -18
- package/dist/extensions/pi-subagents/test/integration/foreign-workflow-steering.test.ts +95 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +9 -15
- package/dist/extensions/pi-subagents/test/integration/in-process-child.test.ts +374 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +20 -10
- package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +18 -6
- package/dist/extensions/pi-subagents/test/integration/recovered-assistant-error.test.ts +151 -0
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +42 -1
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +150 -25
- package/dist/extensions/pi-subagents/test/integration/result-publication.test.ts +354 -0
- package/dist/extensions/pi-subagents/test/integration/shared-cwd-terminal-evidence.test.ts +117 -0
- package/dist/extensions/pi-subagents/test/integration/single-execution.part-1.test.ts +3678 -0
- package/dist/extensions/pi-subagents/test/integration/{single-execution.test.ts → single-execution.part-2.test.ts} +500 -4207
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -2
- package/dist/extensions/pi-subagents/test/integration/workflow-result-publication.test.ts +127 -0
- package/dist/extensions/pi-subagents/test/integration/workflow-steer-inbox.test.ts +365 -0
- package/dist/extensions/pi-subagents/test/smoke/pi085-child.ts +31 -0
- package/dist/extensions/pi-subagents/test/smoke/pi085-clean-install.mjs +74 -0
- package/dist/extensions/pi-subagents/test/smoke/pi085-extension.ts +16 -0
- package/dist/extensions/pi-subagents/test/support/async-execution-fixture.ts +699 -0
- package/dist/extensions/pi-subagents/test/support/fake-child-session.ts +418 -0
- package/dist/extensions/pi-subagents/test/support/foreign-workflow-steer-host.mjs +13 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +30 -76
- package/dist/extensions/pi-subagents/test/support/notify-diagnostics-fixture.ts +84 -0
- package/dist/extensions/pi-subagents/test/support/result-publication-capacity-preload.mjs +70 -0
- package/dist/extensions/pi-subagents/test/support/runner-child-session-factory.ts +17 -0
- package/dist/extensions/pi-subagents/test/support/single-execution-fixture.ts +390 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance-compaction.test.ts +120 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +24 -3
- package/dist/extensions/pi-subagents/test/unit/advertised-agent-prompt.test.ts +87 -0
- package/dist/extensions/pi-subagents/test/unit/advertised-agent-refresh.test.ts +163 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +70 -31
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +78 -3
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +2 -8
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +73 -66
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +51 -1
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +49 -1
- package/dist/extensions/pi-subagents/test/unit/async-spawn-preload.test.ts +103 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +9 -8
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +10 -7
- package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +48 -6
- package/dist/extensions/pi-subagents/test/unit/child-runtime-config.test.ts +90 -0
- package/dist/extensions/pi-subagents/test/unit/{pi-args-permission-system.test.ts → child-tool-plan-permission-system.test.ts} +48 -86
- package/dist/extensions/pi-subagents/test/unit/child-tool-plan.test.ts +33 -0
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +18 -18
- package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +8 -7
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +225 -65
- package/dist/extensions/pi-subagents/test/unit/extension-bindings.test.ts +17 -22
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +11 -1
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +47 -3
- package/dist/extensions/pi-subagents/test/unit/foreign-workflow-steering.test.ts +74 -0
- package/dist/extensions/pi-subagents/test/unit/fork-cache-key.test.ts +23 -29
- package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +96 -4
- package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +4 -4
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +116 -55
- package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +58 -1
- package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +104 -3
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +10 -1
- package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +31 -1
- package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +102 -51
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +196 -6
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +181 -38
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +62 -70
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +58 -53
- package/dist/extensions/pi-subagents/test/unit/notify-diagnostics.test.ts +42 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +84 -0
- package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +13 -5
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +21 -5
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +5 -4
- package/dist/extensions/pi-subagents/test/unit/permissions.test.ts +2 -6
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +28 -2
- package/dist/extensions/pi-subagents/test/unit/pi-web-session-liveness.test.ts +209 -0
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +38 -1
- package/dist/extensions/pi-subagents/test/unit/prompt-audit-optional-peers.test.ts +43 -0
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +24 -2
- package/dist/extensions/pi-subagents/test/unit/readonly-drain-observation.test.ts +174 -0
- package/dist/extensions/pi-subagents/test/unit/readonly-model-continuation.test.ts +94 -0
- package/dist/extensions/pi-subagents/test/unit/readonly-session-evidence.test.ts +1329 -0
- package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +29 -80
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +11 -8
- package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/retained-nested-route-tracker.test.ts +175 -0
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +38 -12
- package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +2 -5
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +89 -1
- package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +2 -16
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +27 -3
- package/dist/extensions/pi-subagents/test/unit/scheduled-supervisor-demand.test.ts +215 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +15 -11
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +237 -1
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -5
- package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +14 -1
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +130 -739
- package/dist/extensions/pi-subagents/test/unit/supervisor-ask-registration.test.ts +682 -0
- package/dist/extensions/pi-subagents/test/unit/supervisor-ui.test.ts +137 -0
- package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +1 -11
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +23 -3
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +8 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +19 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +72 -4
- package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +17 -3
- package/dist/extensions/pi-subagents/test/unit/workflow-child-summary.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +12 -2
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +46 -6
- package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/workflow-resources.test.ts +133 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +53 -2
- package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +222 -14
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +526 -85
- package/dist/extensions/pi-web-agent/README.md +6 -5
- package/dist/extensions/pi-web-agent/src/backends/config.ts +12 -6
- package/dist/extensions/pi-web-agent/src/backends/factory.ts +2 -2
- package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +4 -5
- package/dist/extensions/pi-web-agent/src/commands/web-agent-config.ts +8 -8
- package/dist/extensions/pi-web-agent/src/extension.ts +3 -3
- package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +2 -2
- package/dist/extensions/pi-web-agent/src/search/tokenin.ts +14 -3
- package/dist/extensions/pi-zentui/README.md +17 -0
- package/dist/extensions/pi-zentui/assets/screenshots/footer-status-hyperlink.png +0 -0
- package/dist/extensions/pi-zentui/docs/configuration.md +71 -2
- package/dist/extensions/pi-zentui/extensions/zentui/config.ts +11 -0
- package/dist/extensions/pi-zentui/extensions/zentui/editor-transfer.ts +14 -0
- package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +29 -13
- package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +5 -4
- package/dist/extensions/pi-zentui/extensions/zentui/footer-text.ts +11 -0
- package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +11 -10
- package/dist/extensions/pi-zentui/extensions/zentui/index.ts +77 -3
- package/dist/extensions/pi-zentui/extensions/zentui/presets.ts +77 -0
- package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +103 -2
- package/dist/extensions/pi-zentui/extensions/zentui/working-line-extension-segments.ts +115 -0
- package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +87 -5
- package/dist/extensions/pi-zentui/package-lock.json +277 -517
- package/dist/extensions/pi-zentui/package.json +7 -4
- package/dist/extensions/pi-zentui/test/config-presets.test.ts +238 -0
- package/dist/extensions/pi-zentui/test/editor-transfer.test.ts +24 -1
- package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +570 -2
- package/dist/extensions/pi-zentui/test/extension-status.test.ts +90 -0
- package/dist/extensions/pi-zentui/test/footer-hyperlink.test.ts +243 -0
- package/dist/extensions/pi-zentui/test/package-contents.mjs +1 -0
- package/dist/extensions/pi-zentui/test/presets.test.ts +121 -0
- package/dist/extensions/pi-zentui/test/settings-command.test.ts +205 -1
- package/dist/extensions/pi-zentui/test/working-line-extension-segments.test.ts +139 -0
- package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +237 -3
- package/dist/extensions/pi-zentui/test/working-line.test.ts +94 -2
- package/dist/index.d.ts +1 -1
- package/dist/main.js +4 -1
- package/dist/modes/interactive/chat-viewport.d.ts +19 -0
- package/dist/modes/interactive/chat-viewport.js +27 -0
- package/dist/modes/interactive/components/assistant-message.d.ts +6 -2
- package/dist/modes/interactive/components/assistant-message.js +30 -13
- package/dist/modes/interactive/components/custom-editor.d.ts +10 -1
- package/dist/modes/interactive/components/custom-editor.js +39 -1
- package/dist/modes/interactive/components/first-time-setup.js +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -1
- package/dist/modes/interactive/components/markdown-transform.d.ts +2 -0
- package/dist/modes/interactive/components/markdown-transform.js +18 -0
- package/dist/modes/interactive/components/model-selector.js +19 -17
- package/dist/modes/interactive/components/scoped-models-selector.js +21 -23
- package/dist/modes/interactive/components/settings-selector.d.ts +7 -1
- package/dist/modes/interactive/components/settings-selector.js +128 -20
- package/dist/modes/interactive/components/settings-submenu.d.ts +71 -0
- package/dist/modes/interactive/components/settings-submenu.js +164 -0
- package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
- package/dist/modes/interactive/components/status-indicator.js +10 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
- package/dist/modes/interactive/components/thinking-selector.js +75 -18
- package/dist/modes/interactive/components/tool-execution.d.ts +21 -4
- package/dist/modes/interactive/components/tool-execution.js +35 -33
- package/dist/modes/interactive/components/trust-selector.js +2 -2
- package/dist/modes/interactive/interactive-mode.d.ts +13 -15
- package/dist/modes/interactive/interactive-mode.js +144 -80
- package/dist/modes/interactive/session-share.d.ts +15 -0
- package/dist/modes/interactive/session-share.js +183 -0
- package/dist/modes/interactive/theme/dark.json +2 -1
- package/dist/modes/interactive/theme/light.json +2 -1
- package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
- package/dist/modes/interactive/theme/theme-controller.js +5 -0
- package/dist/modes/interactive/theme/theme-json.d.ts +83 -0
- package/dist/modes/interactive/theme/theme-json.js +129 -0
- package/dist/modes/interactive/theme/theme-schema.json +9 -1
- package/dist/modes/interactive/theme/theme.d.ts +14 -5
- package/dist/modes/interactive/theme/theme.js +18 -119
- package/dist/modes/interactive/tui-renderer.d.ts +20 -0
- package/dist/modes/interactive/tui-renderer.js +65 -0
- package/dist/utils/exif-orientation.js +2 -3
- package/dist/utils/management-http.d.ts +24 -0
- package/dist/utils/management-http.js +53 -0
- package/dist/utils/text.d.ts +7 -0
- package/dist/utils/text.js +8 -0
- package/dist/utils/tools-manager.d.ts +1 -0
- package/dist/utils/tools-manager.js +43 -21
- package/docs/docs.json +0 -4
- package/package.json +6 -6
- package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +0 -415
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +0 -1059
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +0 -116
- package/dist/extensions/pi-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +0 -224
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +0 -6986
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +0 -447
- package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +0 -185
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +0 -311
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +0 -84
- package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +0 -202
- package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +0 -101
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +0 -2347
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +0 -104
- package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +0 -49
- package/dist/extensions/web-agent-onboarding.test.ts +0 -415
- package/dist/extensions/web-agent-onboarding.ts +0 -222
- package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -1465
- package/dist/skills/imagegen-frontend-web/SKILL.md +0 -987
- package/dist/skills/implanger/SKILL.md +0 -70
- package/dist/skills/output/SKILL.md +0 -49
- package/dist/skills/planger/SKILL.md +0 -167
- package/dist/skills/workflow/SKILL.md +0 -95
- package/docs/workflows.md +0 -18
- /package/dist/bun/{register-bedrock.d.ts → runtime-setup.d.ts} +0 -0
|
@@ -50,9 +50,10 @@ import { updateActiveRunIndex } from "../background/active-run-index.ts";
|
|
|
50
50
|
import { steeringReceipt } from "../background/steering.ts";
|
|
51
51
|
import { acquireActiveAsyncCapacity, ActiveAsyncCapacityError, getActiveAsyncCapacitySnapshot, resolveAbandonedSlotReleaseAfterMs, resolveMaxActiveAsyncRunsPerSession, transferActiveAsyncCapacity, type ActiveAsyncCapacityHandle } from "../background/active-async-capacity.ts";
|
|
52
52
|
import { isScheduledRunAction } from "../background/scheduled-runs.ts";
|
|
53
|
+
import { encodeIndexSegment } from "../background/index-segment.ts";
|
|
53
54
|
import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
|
|
54
55
|
import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
|
|
55
|
-
import { normalizeGateAcceptance, resolveAcceptanceReportMode, validateExecutionAcceptance } from "../shared/acceptance.ts";
|
|
56
|
+
import { normalizeGateAcceptance, resolveAcceptanceReportMode, validateAcceptanceInput, validateExecutionAcceptance } from "../shared/acceptance.ts";
|
|
56
57
|
import { canPreferFork, createForkContextResolver, forkedChildRequiresThinkingOff, resolveSubagentLaunchContext } from "../../shared/fork-context.ts";
|
|
57
58
|
import { createPrunedForkSessionWriter } from "../../shared/pruned-fork.ts";
|
|
58
59
|
import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
|
|
@@ -60,17 +61,18 @@ import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
|
|
|
60
61
|
import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBridge, resolveIntercomSessionTarget, resolveSubagentIntercomTarget, type IntercomBridgeState } from "../../intercom/intercom-bridge.ts";
|
|
61
62
|
import { formatControlIntercomMessage, formatControlNoticeMessage, resolveControlConfig, shouldNotifyControlEvent } from "../shared/subagent-control.ts";
|
|
62
63
|
import { formatSpawnBudget, getSpawnBudgetSnapshot, grantSpawnBudget, preflightSpawnBudget, preflightSpawnBudgetGrant, reserveSpawnBudget } from "../shared/spawn-budget.ts";
|
|
63
|
-
import { claimRunFanoutBatch, claimRunFanoutBatchWithCommit, createRunFanoutBudget,
|
|
64
|
+
import { claimRunFanoutBatch, claimRunFanoutBatchWithCommit, createRunFanoutBudget, formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetDescriptor, RunFanoutLimitError, writeRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.ts";
|
|
65
|
+
import { retainLiveForegroundNestedRoute } from "../../integrations/pi-web-session-liveness.ts";
|
|
64
66
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
65
67
|
import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig } from "../shared/usage-budget.ts";
|
|
66
68
|
import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
67
|
-
import {
|
|
69
|
+
import { isAgentContract } from "../shared/agent-contract.ts";
|
|
68
70
|
import { normalizeExtensionBindings, type ExtensionBindings } from "../shared/extension-bindings.ts";
|
|
69
71
|
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, outputPathMappingFromTask, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
70
72
|
import { assertJsonSchemaObject, cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
71
73
|
import { compactForegroundDetails, getSingleResultOutput, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage, toAgentToolUsage } from "../../shared/utils.ts";
|
|
72
74
|
import { createTaskMutationArbiter } from "../shared/llm-intent-arbiter.ts";
|
|
73
|
-
import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, formatStoredParallelHandoffCleanup, parallelHandoffPath, readParallelHandoffManifest, recordParallelHandoffMerge, recordParallelHandoffSupersession, writeParallelHandoffGroup,
|
|
75
|
+
import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, formatStoredParallelHandoffCleanup, parallelHandoffPath, readParallelHandoffManifest, recordParallelHandoffMerge, recordParallelHandoffSupersession, writeParallelHandoffGroup, writeWorktreeSetupHandoff } from "../shared/parallel-handoff.ts";
|
|
74
76
|
import { summarizeContextModes, type ContextMode, type ContextSummary } from "../shared/context-mode.ts";
|
|
75
77
|
import {
|
|
76
78
|
attachNestedChildrenToResultChildren,
|
|
@@ -81,15 +83,13 @@ import {
|
|
|
81
83
|
stripDetailsOutputsForIntercomReceipt,
|
|
82
84
|
} from "../../intercom/result-intercom.ts";
|
|
83
85
|
import { applySteeringRecoveryAgentConfig, asyncReviveRequiresRecoveryDescriptor, buildRevivedAsyncTask, readAsyncRecoveryDescriptor, resolveAsyncResumeTarget, resolveAsyncRunLocation } from "../background/async-resume.ts";
|
|
84
|
-
import { deliverInterruptRequest, readRevivalBriefs, requestAsyncSteer, type SteerDeliveryMode } from "../background/control-channel.ts";
|
|
85
|
-
import { updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
|
|
86
|
+
import { closeSteerInbox, consumeSteerRequests, deliverInterruptRequest, readRevivalBriefs, requestAsyncSteer, watchAsyncControlInbox, type SteerDeliveryMode, type SteerRequest } from "../background/control-channel.ts";
|
|
87
|
+
import { createSteeringStatus, recordSteeringRequest, steeringStatus, updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
|
|
86
88
|
import { canQueueRetainedAsyncFollowUp, steerAsyncRun } from "./async-steering-action.ts";
|
|
87
89
|
import {
|
|
88
|
-
removeWorkflowForegroundSteeringRoute,
|
|
89
90
|
resolveWorkflowForegroundSteeringTarget,
|
|
90
91
|
steerWorkflowForegroundTarget,
|
|
91
|
-
|
|
92
|
-
workflowForegroundSteeringLaunchOptions,
|
|
92
|
+
steerWorkflowRun,
|
|
93
93
|
} from "./workflow-foreground-steering.ts";
|
|
94
94
|
import { stopAsyncRun } from "./async-stop-action.ts";
|
|
95
95
|
import { dismissRecoveredWorkflow } from "./async-dismiss-action.ts";
|
|
@@ -97,10 +97,11 @@ import { promotePausedWorkflowIfSettled, reconcileDetachedWorkflowChildCompletio
|
|
|
97
97
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
98
98
|
import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.ts";
|
|
99
99
|
import { resultFilePath, writeAsyncResultFile } from "../background/result-files.ts";
|
|
100
|
-
import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult,
|
|
100
|
+
import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, inheritedNestedParentAddressOf, inheritedNestedRouteOf, resolveNestedAsyncDir, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedParentAddress, type NestedRoute, type NestedRunResolutionScope } from "../shared/nested-events.ts";
|
|
101
|
+
import type { ChildRuntimeConfig } from "../shared/child-runtime-config.ts";
|
|
101
102
|
import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
|
|
102
103
|
import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
|
|
103
|
-
import { isStoppableAsyncStatusStep, resolveAsyncStatusChild } from "../shared/child-identity.ts";
|
|
104
|
+
import { isStoppableAsyncStatusStep, resolveAsyncStatusChild, stopStoppableAsyncStatusChildren } from "../shared/child-identity.ts";
|
|
104
105
|
import { inspectSubagentStatus } from "../background/run-status.ts";
|
|
105
106
|
import { getExternalJobProvider } from "../../api/external-job-provider.ts";
|
|
106
107
|
import { externalJobFollowUpRequestDigest, externalJobFollowUpRequestId, externalJobFollowUpRunId, externalJobPromptDigest, externalJobStableJson } from "../shared/external-job-runner.ts";
|
|
@@ -116,10 +117,10 @@ import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspe
|
|
|
116
117
|
import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
|
|
117
118
|
import { previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError, type WorkflowLanePlan, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowScriptTraceEntry, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
|
|
118
119
|
import { executeWorkflowHostCommand, resolveWorkflowHostOutputClaimPath, type WorkflowHostCommandParams, type WorkflowHostCommandResult } from "../../workflows/host-command.ts";
|
|
119
|
-
import { buildWorkflowReceipt, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt, type WorkflowReceipt, type WorkflowReceiptState } from "../../workflows/workflow-receipt.ts";
|
|
120
|
+
import { buildWorkflowReceipt, readWorkflowReceipt, workflowReceiptPath, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt, type WorkflowReceipt, type WorkflowReceiptState } from "../../workflows/workflow-receipt.ts";
|
|
120
121
|
import { upsertHostStep, validHostStepNodes } from "../shared/host-step-status.ts";
|
|
121
122
|
import { assertWorkflowLaneKey, normalizeWorkflowLaneMetadata } from "../shared/lane-metadata.ts";
|
|
122
|
-
import { parseWorkflowChildSummary, workflowChildSummary } from "../../workflows/workflow-child-summary.ts";
|
|
123
|
+
import { parseWorkflowChildSummary, workflowChildProgress, workflowChildSummary } from "../../workflows/workflow-child-summary.ts";
|
|
123
124
|
import { resolveWorkflowChatProgress, type WorkflowChatProgressProjection } from "../../workflows/chat-progress.ts";
|
|
124
125
|
import { annotateWorkflowPreflightTrace, formatWorkflowPreflight, formatWorkflowPreflightWarnings, normalizeWorkflowPreflight, workflowPreflightWarnings } from "../../workflows/workflow-preflight.ts";
|
|
125
126
|
import {
|
|
@@ -136,6 +137,8 @@ import { resolveWorkflowResource } from "../../workflows/workflow-resources.ts";
|
|
|
136
137
|
import {
|
|
137
138
|
cleanupWorktrees,
|
|
138
139
|
createWorktrees,
|
|
140
|
+
withWorktreeTransaction,
|
|
141
|
+
type WorktreeSetupProgress,
|
|
139
142
|
diffWorktrees,
|
|
140
143
|
formatWorktreeDiffSummary,
|
|
141
144
|
type WorktreeSetup,
|
|
@@ -154,8 +157,11 @@ import {
|
|
|
154
157
|
type Details,
|
|
155
158
|
type ExtensionConfig,
|
|
156
159
|
type ForegroundResumeChild,
|
|
160
|
+
type ForegroundChildSessionControls,
|
|
161
|
+
type ForegroundSteerInput,
|
|
162
|
+
type ForegroundSteerOutcome,
|
|
157
163
|
type ForegroundRunControl,
|
|
158
|
-
type
|
|
164
|
+
type HostStepNode,
|
|
159
165
|
type IntercomBridgeConfig,
|
|
160
166
|
type IntercomEventBus,
|
|
161
167
|
type JsonSchemaObject,
|
|
@@ -172,7 +178,7 @@ import {
|
|
|
172
178
|
type ToolBudgetConfig,
|
|
173
179
|
type Usage,
|
|
174
180
|
type UsageBudgetConfig,
|
|
175
|
-
type
|
|
181
|
+
type WorkflowResourceProvenance,
|
|
176
182
|
type WorkflowGraphNode,
|
|
177
183
|
type WorkflowGraphSnapshot,
|
|
178
184
|
type WorkflowNodeStatus,
|
|
@@ -192,6 +198,7 @@ import {
|
|
|
192
198
|
resolveMaxSubagentSpawnsPerRun,
|
|
193
199
|
wrapForkTask,
|
|
194
200
|
type ScheduleOrigin,
|
|
201
|
+
type SteeringTargetState,
|
|
195
202
|
} from "../../shared/types.ts";
|
|
196
203
|
import { deriveChildSessionName } from "../../shared/child-session-name.ts";
|
|
197
204
|
|
|
@@ -324,7 +331,7 @@ export interface SubagentParamsLike {
|
|
|
324
331
|
workflowScriptPath?: string;
|
|
325
332
|
globalConcurrencyLimit?: number;
|
|
326
333
|
maxSubagentSpawnsPerRun?: number;
|
|
327
|
-
preflight?: import("../../shared/types.ts").
|
|
334
|
+
preflight?: import("../../shared/types.ts").WorkflowPreflight;
|
|
328
335
|
chatProgress?: "auto" | "off" | "live-card";
|
|
329
336
|
isolation?: "none" | "worktree";
|
|
330
337
|
step?: ChainStep;
|
|
@@ -352,6 +359,8 @@ export interface SubagentParamsLike {
|
|
|
352
359
|
tasks?: TaskParam[];
|
|
353
360
|
concurrency?: number;
|
|
354
361
|
worktree?: boolean;
|
|
362
|
+
/** Git ref used as the managed worktree base. */
|
|
363
|
+
baseRef?: string;
|
|
355
364
|
context?: "fresh" | "fork" | "profile";
|
|
356
365
|
/** Per-run intercom bridge config. It replaces the global config for this launch only. */
|
|
357
366
|
intercomBridge?: IntercomBridgeConfig;
|
|
@@ -433,10 +442,27 @@ interface ExecutorDeps {
|
|
|
433
442
|
getSubagentSessionRoot: (parentSessionFile: string | null) => string;
|
|
434
443
|
expandTilde: (p: string) => string;
|
|
435
444
|
discoverAgents: (cwd: string, scope: AgentScope, preferredModelProvider?: string) => { agents: AgentConfig[]; agentDiagnostics?: AgentDiscoveryDiagnostic[]; modelScope?: ModelScopeConfig; maxThinking?: AgentConfig["maxThinking"]; cwd?: string; scope?: AgentScope; directories?: UnknownAgentDiagnosticContext["directories"] };
|
|
445
|
+
onAgentsChanged?: () => void;
|
|
436
446
|
allowMutatingManagementActions?: boolean;
|
|
437
447
|
activateSupervisorTransport?: () => void;
|
|
448
|
+
findPendingAsks?: Parameters<typeof steerAsyncRun>[0]["findPendingAsks"];
|
|
438
449
|
refreshResultDelivery?: () => void;
|
|
450
|
+
trackRetainedNestedRoute?: (rootRunId: string) => void;
|
|
439
451
|
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
|
|
452
|
+
/** Set when this executor runs inside a child session; carries the runtime settings the host passes instead of environment variables. */
|
|
453
|
+
childRuntime?: ChildRuntimeConfig;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function inheritedNestedRoute(deps: Pick<ExecutorDeps, "childRuntime">): NestedRoute | undefined {
|
|
457
|
+
return inheritedNestedRouteOf(deps.childRuntime);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function inheritedNestedParentAddress(deps: Pick<ExecutorDeps, "childRuntime">): NestedParentAddress | undefined {
|
|
461
|
+
return inheritedNestedParentAddressOf(deps.childRuntime);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function inheritedRunFanoutBudget(deps: Pick<ExecutorDeps, "childRuntime">): RunFanoutBudgetDescriptor | undefined {
|
|
465
|
+
return deps.childRuntime?.runFanoutBudget;
|
|
440
466
|
}
|
|
441
467
|
|
|
442
468
|
type ForkSessionFileForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => string | undefined;
|
|
@@ -478,6 +504,8 @@ interface ExecutionContextData {
|
|
|
478
504
|
configToolTimeoutMs?: number;
|
|
479
505
|
toolBudget?: ResolvedToolBudget;
|
|
480
506
|
usageBudget?: UsageBudgetConfig;
|
|
507
|
+
/** Parent workflow owns this allowance; presence vetoes unverifiable child continuation. */
|
|
508
|
+
inheritedUsageBudget?: UsageBudgetConfig;
|
|
481
509
|
allowZeroToolBudget?: boolean;
|
|
482
510
|
configToolBudget?: ResolvedToolBudget;
|
|
483
511
|
contextPolicy: AgentDefaultContextPolicy;
|
|
@@ -510,10 +538,16 @@ function loadWorkflowScriptPath(params: SubagentParamsLike, runtimeCwd: string):
|
|
|
510
538
|
return { params: { ...rest, workflowScript } };
|
|
511
539
|
}
|
|
512
540
|
|
|
513
|
-
function removeForegroundControlIfIdle(state: SubagentState, runId: string): boolean {
|
|
541
|
+
export function removeForegroundControlIfIdle(state: SubagentState, runId: string, trackRetainedNestedRoute?: (rootRunId: string) => void): boolean {
|
|
514
542
|
const control = state.foregroundControls.get(runId);
|
|
515
543
|
if (control && (!foregroundSchedulingSettled(control) || (control.activeChildren?.size ?? 0) > 0)) return false;
|
|
516
|
-
if (control)
|
|
544
|
+
if (control?.nestedRoute && trackRetainedNestedRoute) {
|
|
545
|
+
try {
|
|
546
|
+
if (retainLiveForegroundNestedRoute(state, control.nestedRoute)) trackRetainedNestedRoute(runId);
|
|
547
|
+
} catch (error) {
|
|
548
|
+
console.error(`Failed to retain live nested descendants for foreground run '${runId}':`, error);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
517
551
|
state.foregroundControls.delete(runId);
|
|
518
552
|
if (state.lastForegroundControlId === runId) state.lastForegroundControlId = null;
|
|
519
553
|
return true;
|
|
@@ -566,8 +600,8 @@ function formatForegroundActivity(control: SubagentState["foregroundControls"] e
|
|
|
566
600
|
|
|
567
601
|
function nestedResolutionScopeForExecutor(deps: ExecutorDeps): NestedRunResolutionScope | undefined {
|
|
568
602
|
if (deps.allowMutatingManagementActions !== false) return undefined;
|
|
569
|
-
const route =
|
|
570
|
-
const address = route ?
|
|
603
|
+
const route = inheritedNestedRoute(deps);
|
|
604
|
+
const address = route ? inheritedNestedParentAddress(deps) : undefined;
|
|
571
605
|
return {
|
|
572
606
|
routes: route ? [route] : [],
|
|
573
607
|
...(address ? { descendantOf: { parentRunId: address.parentRunId, ...(address.parentStepIndex !== undefined ? { parentStepIndex: address.parentStepIndex } : {}) } } : {}),
|
|
@@ -708,7 +742,7 @@ function foregroundChildActivityFromProgress(progress: SingleResult["progress"]
|
|
|
708
742
|
};
|
|
709
743
|
}
|
|
710
744
|
|
|
711
|
-
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 {
|
|
745
|
+
function rememberForegroundRun(state: SubagentState, input: { modelResponseAliases?: Record<string, string[]>; runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[]; params: SubagentParamsLike; effectiveOutput?: string | boolean; effectiveOutputMode: OutputMode; extensionBindings?: ExtensionBindings }): void {
|
|
712
746
|
state.foregroundRuns ??= new Map();
|
|
713
747
|
const previous = state.foregroundRuns.get(input.runId);
|
|
714
748
|
const updatedAt = Date.now();
|
|
@@ -720,6 +754,7 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
720
754
|
updatedAt,
|
|
721
755
|
children: input.results.map((result, index) => {
|
|
722
756
|
const resumeContract = omitUndefinedProperties({
|
|
757
|
+
modelResponseAliases: input.modelResponseAliases,
|
|
723
758
|
outputSchema: input.params.outputSchema,
|
|
724
759
|
agentContract: input.params.agentContract,
|
|
725
760
|
acceptance: input.params.acceptance,
|
|
@@ -1298,8 +1333,10 @@ function appendStepToAsyncChain(input: {
|
|
|
1298
1333
|
currentModelProvider: parentModel?.provider,
|
|
1299
1334
|
currentModel: parentModel,
|
|
1300
1335
|
modelScope: discoveredForAppend.modelScope,
|
|
1336
|
+
modelResponseAliases: input.deps.config.modelResponseAliases,
|
|
1301
1337
|
interactive: input.ctx.hasUI,
|
|
1302
1338
|
permissions: input.deps.config.permissions,
|
|
1339
|
+
childRuntime: input.deps.childRuntime,
|
|
1303
1340
|
});
|
|
1304
1341
|
const built = buildAsyncRunnerSteps(resolved.id, compactOptional<Parameters<typeof buildAsyncRunnerSteps>[1]>({
|
|
1305
1342
|
chain: wrapChainTasksForFork(chain, contextPolicy),
|
|
@@ -1312,7 +1349,7 @@ function appendStepToAsyncChain(input: {
|
|
|
1312
1349
|
cwd: status.cwd ?? input.requestCwd,
|
|
1313
1350
|
chainSkills,
|
|
1314
1351
|
dynamicFanoutMaxItems: input.deps.config.chain?.dynamicFanout?.maxItems,
|
|
1315
|
-
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1352
|
+
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
|
|
1316
1353
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
1317
1354
|
waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
|
|
1318
1355
|
contextForAgent: contextPolicy.contextForAgent,
|
|
@@ -1638,11 +1675,11 @@ async function resumeExternalJobFollowUp(input: {
|
|
|
1638
1675
|
return externalJobFollowUpStarted({ sourceRunId: input.target.runId, runId, asyncDir, duplicate: true, interactive: input.ctx.hasUI });
|
|
1639
1676
|
}
|
|
1640
1677
|
|
|
1641
|
-
const depthState = checkSubagentDepth(input.deps.config.maxSubagentDepth);
|
|
1678
|
+
const depthState = checkSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime);
|
|
1642
1679
|
if (depthState.blocked) {
|
|
1643
1680
|
return { content: [{ type: "text", text: `Nested subagent resume blocked (depth=${depthState.depth}, max=${depthState.maxDepth}). Complete the follow-up directly instead.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1644
1681
|
}
|
|
1645
|
-
const topLevelResume = depthState.depth === 0 && !
|
|
1682
|
+
const topLevelResume = depthState.depth === 0 && !inheritedNestedRoute(input.deps) && !input.deps.state.workflowControllers?.has(input.target.runId);
|
|
1646
1683
|
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
1647
1684
|
try {
|
|
1648
1685
|
activeAsyncCapacity = topLevelResume ? acquireActiveAsyncCapacity({
|
|
@@ -1678,15 +1715,17 @@ async function resumeExternalJobFollowUp(input: {
|
|
|
1678
1715
|
currentModelProvider: parentModel?.provider,
|
|
1679
1716
|
currentModel: parentModel,
|
|
1680
1717
|
modelScope: input.modelScope,
|
|
1718
|
+
modelResponseAliases: input.deps.config.modelResponseAliases,
|
|
1681
1719
|
interactive: input.ctx.hasUI,
|
|
1682
1720
|
permissions: input.deps.config.permissions,
|
|
1721
|
+
childRuntime: input.deps.childRuntime,
|
|
1683
1722
|
}),
|
|
1684
1723
|
cwd: input.effectiveCwd,
|
|
1685
1724
|
artifactsDir,
|
|
1686
1725
|
artifactConfig,
|
|
1687
1726
|
shareEnabled: false,
|
|
1688
1727
|
...(input.parentSessionFile ? { sessionRoot: input.deps.getSubagentSessionRoot(input.parentSessionFile) } : {}),
|
|
1689
|
-
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1728
|
+
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
|
|
1690
1729
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
1691
1730
|
waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
|
|
1692
1731
|
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
@@ -1712,6 +1751,28 @@ async function resumeExternalJobFollowUp(input: {
|
|
|
1712
1751
|
return externalJobFollowUpStarted({ sourceRunId: input.target.runId, runId: result.details.asyncId ?? runId, asyncDir: result.details.asyncDir ?? asyncDir, interactive: input.ctx.hasUI });
|
|
1713
1752
|
}
|
|
1714
1753
|
|
|
1754
|
+
function resolveRequestedResumeTarget(params: SubagentParamsLike, deps: ExecutorDeps, parentSessionFile: string | null): ResumeSourceTarget | { kind: "live-nested"; target: ResolvedSubagentRunId & { kind: "nested" } } {
|
|
1755
|
+
const requestedId = params.id ?? params.runId;
|
|
1756
|
+
let resolved: ResolvedSubagentRunId | undefined;
|
|
1757
|
+
try {
|
|
1758
|
+
resolved = requestedId ? resolveSubagentRunId(requestedId, omitUndefinedProperties({ state: deps.state, nested: nestedResolutionScopeForExecutor(deps) })) : undefined;
|
|
1759
|
+
} catch (error) {
|
|
1760
|
+
const message = error instanceof Error ? error.message : "";
|
|
1761
|
+
const asyncMatches = message.match(/async:/g)?.length ?? 0;
|
|
1762
|
+
if (!isResumeAmbiguity(error) || !message.includes("foreground:") || asyncMatches !== 1) throw error;
|
|
1763
|
+
}
|
|
1764
|
+
if (resolved?.kind === "nested") {
|
|
1765
|
+
if (params.chain?.length) throw new Error("Attaching a running subagent as a chain root is currently available for top-level async runs only.");
|
|
1766
|
+
if (resolved.match.run.state === "running" || resolved.match.run.state === "queued") return { kind: "live-nested", target: resolved };
|
|
1767
|
+
const trustedSessionRoots = [
|
|
1768
|
+
...(deps.config.defaultSessionDir ? [path.resolve(deps.expandTilde(deps.config.defaultSessionDir))] : []),
|
|
1769
|
+
...(parentSessionFile ? [deps.getSubagentSessionRoot(parentSessionFile)] : []),
|
|
1770
|
+
];
|
|
1771
|
+
return resolveNestedResumeTarget(resolved, trustedSessionRoots);
|
|
1772
|
+
}
|
|
1773
|
+
return resolveResumeTarget(params, deps.state, { asyncRequireSessionFile: false });
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1715
1776
|
async function resumeAsyncRun(input: {
|
|
1716
1777
|
params: SubagentParamsLike;
|
|
1717
1778
|
requestCwd: string;
|
|
@@ -1750,34 +1811,9 @@ async function resumeAsyncRun(input: {
|
|
|
1750
1811
|
let target: ResumeSourceTarget;
|
|
1751
1812
|
const parentSessionFile = input.ctx.sessionManager.getSessionFile() ?? null;
|
|
1752
1813
|
try {
|
|
1753
|
-
const
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
resolved = requestedId ? resolveSubagentRunId(requestedId, omitUndefinedProperties({ state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) })) : undefined;
|
|
1757
|
-
} catch (error) {
|
|
1758
|
-
const message = error instanceof Error ? error.message : "";
|
|
1759
|
-
const asyncMatches = message.match(/async:/g)?.length ?? 0;
|
|
1760
|
-
if (!isResumeAmbiguity(error) || !message.includes("foreground:") || asyncMatches !== 1) throw error;
|
|
1761
|
-
}
|
|
1762
|
-
if (resolved?.kind === "nested") {
|
|
1763
|
-
if (attachChain) {
|
|
1764
|
-
return {
|
|
1765
|
-
content: [{ type: "text", text: "Attaching a running subagent as a chain root is currently available for top-level async runs only." }],
|
|
1766
|
-
isError: true,
|
|
1767
|
-
details: { mode: "management", results: [] },
|
|
1768
|
-
};
|
|
1769
|
-
}
|
|
1770
|
-
if (resolved.match.run.state === "running" || resolved.match.run.state === "queued") {
|
|
1771
|
-
return resumeLiveNestedRun({ target: resolved, message: followUp });
|
|
1772
|
-
}
|
|
1773
|
-
const trustedSessionRoots = [
|
|
1774
|
-
...(input.deps.config.defaultSessionDir ? [path.resolve(input.deps.expandTilde(input.deps.config.defaultSessionDir))] : []),
|
|
1775
|
-
...(parentSessionFile ? [input.deps.getSubagentSessionRoot(parentSessionFile)] : []),
|
|
1776
|
-
];
|
|
1777
|
-
target = resolveNestedResumeTarget(resolved, trustedSessionRoots);
|
|
1778
|
-
} else {
|
|
1779
|
-
target = resolveResumeTarget(input.params, input.deps.state, { asyncRequireSessionFile: false });
|
|
1780
|
-
}
|
|
1814
|
+
const resolved = resolveRequestedResumeTarget(input.params, input.deps, parentSessionFile);
|
|
1815
|
+
if (resolved.kind === "live-nested") return resumeLiveNestedRun({ target: resolved.target, message: followUp });
|
|
1816
|
+
target = resolved;
|
|
1781
1817
|
} catch (error) {
|
|
1782
1818
|
const message = error instanceof Error ? error.message : String(error);
|
|
1783
1819
|
return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
|
|
@@ -1797,7 +1833,7 @@ async function resumeAsyncRun(input: {
|
|
|
1797
1833
|
};
|
|
1798
1834
|
}
|
|
1799
1835
|
|
|
1800
|
-
const { blocked, depth, maxDepth } = checkSubagentDepth(input.deps.config.maxSubagentDepth);
|
|
1836
|
+
const { blocked, depth, maxDepth } = checkSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime);
|
|
1801
1837
|
if (blocked) {
|
|
1802
1838
|
return {
|
|
1803
1839
|
content: [{ type: "text", text: `Nested subagent resume blocked (depth=${depth}, max=${maxDepth}). Complete the follow-up directly instead.` }],
|
|
@@ -1886,7 +1922,7 @@ async function resumeAsyncRun(input: {
|
|
|
1886
1922
|
};
|
|
1887
1923
|
}
|
|
1888
1924
|
const runId = randomUUID();
|
|
1889
|
-
const topLevelResume = depth === 0 && !
|
|
1925
|
+
const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
|
|
1890
1926
|
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
1891
1927
|
try {
|
|
1892
1928
|
activeAsyncCapacity = topLevelResume ? acquireActiveAsyncCapacity({
|
|
@@ -1930,8 +1966,10 @@ async function resumeAsyncRun(input: {
|
|
|
1930
1966
|
currentModelProvider: parentModel?.provider,
|
|
1931
1967
|
currentModel: parentModel,
|
|
1932
1968
|
modelScope,
|
|
1969
|
+
modelResponseAliases: input.deps.config.modelResponseAliases,
|
|
1933
1970
|
interactive: input.ctx.hasUI,
|
|
1934
1971
|
permissions: input.deps.config.permissions,
|
|
1972
|
+
childRuntime: input.deps.childRuntime,
|
|
1935
1973
|
}),
|
|
1936
1974
|
availableModels,
|
|
1937
1975
|
cwd: effectiveCwd,
|
|
@@ -1944,12 +1982,13 @@ async function resumeAsyncRun(input: {
|
|
|
1944
1982
|
agentContract: input.params.agentContract,
|
|
1945
1983
|
fast: input.params.fast,
|
|
1946
1984
|
dynamicFanoutMaxItems: input.deps.config.chain?.dynamicFanout?.maxItems,
|
|
1947
|
-
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1985
|
+
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
|
|
1948
1986
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
1949
1987
|
waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
|
|
1950
1988
|
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
1951
1989
|
worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
|
|
1952
1990
|
worktreeBaseDir: input.deps.config.worktreeBaseDir,
|
|
1991
|
+
baseRef: input.params.baseRef ?? target.recoveryDescriptor?.baseRef,
|
|
1953
1992
|
worktreeProvider: input.deps.config.worktreeProvider,
|
|
1954
1993
|
worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
|
|
1955
1994
|
controlConfig: resolveControlConfig(input.deps.config.control, input.params.control),
|
|
@@ -1986,8 +2025,11 @@ async function resumeAsyncRun(input: {
|
|
|
1986
2025
|
if (target.source === "async" && asyncReviveRequiresRecoveryDescriptor(target)) {
|
|
1987
2026
|
return { content: [{ type: "text", text: `Async child '${target.runId}' is missing its required run fan-out recovery identity. Start a new run instead.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1988
2027
|
}
|
|
2028
|
+
if (input.params.baseRef !== undefined && "managedWorktree" in target && target.managedWorktree === true) {
|
|
2029
|
+
return { content: [{ type: "text", text: "Cannot resume with baseRef: retained managed-worktree children continue in their existing worktree. Start a new worktree run from that base ref instead." }], isError: true, details: { mode: "management", results: [] } };
|
|
2030
|
+
}
|
|
1989
2031
|
const runId = randomUUID();
|
|
1990
|
-
const topLevelResume = depth === 0 && !
|
|
2032
|
+
const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
|
|
1991
2033
|
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
1992
2034
|
try {
|
|
1993
2035
|
activeAsyncCapacity = !topLevelResume ? undefined : target.source === "async"
|
|
@@ -2033,8 +2075,11 @@ async function resumeAsyncRun(input: {
|
|
|
2033
2075
|
currentModelProvider: parentModel?.provider,
|
|
2034
2076
|
currentModel: parentModel,
|
|
2035
2077
|
modelScope,
|
|
2078
|
+
// Absence in the retained contract is meaningful; never acquire current aliases.
|
|
2079
|
+
modelResponseAliases: recoveryDescriptor ? recoveryDescriptor.modelResponseAliases : foregroundContract?.modelResponseAliases,
|
|
2036
2080
|
interactive: input.ctx.hasUI,
|
|
2037
2081
|
permissions: input.deps.config.permissions,
|
|
2082
|
+
childRuntime: input.deps.childRuntime,
|
|
2038
2083
|
}),
|
|
2039
2084
|
cwd: effectiveCwd,
|
|
2040
2085
|
maxOutput: input.params.maxOutput ?? recoveryDescriptor?.maxOutput,
|
|
@@ -2059,12 +2104,13 @@ async function resumeAsyncRun(input: {
|
|
|
2059
2104
|
thinkingCeiling: recoveryDescriptor?.thinkingCeiling ?? ("thinkingCeiling" in target ? target.thinkingCeiling : undefined),
|
|
2060
2105
|
extensionBindings: recoveryDescriptor?.extensionBindings ?? ("extensionBindings" in target ? target.extensionBindings : undefined),
|
|
2061
2106
|
outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
|
|
2062
|
-
maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
2107
|
+
maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
|
|
2063
2108
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
2064
2109
|
waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
|
|
2065
2110
|
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
2066
2111
|
worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
|
|
2067
2112
|
worktreeBaseDir: input.deps.config.worktreeBaseDir,
|
|
2113
|
+
baseRef: input.params.baseRef ?? recoveryDescriptor?.baseRef,
|
|
2068
2114
|
worktreeProvider: input.deps.config.worktreeProvider,
|
|
2069
2115
|
worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
|
|
2070
2116
|
// A retained async child already owns the recorded worktree. Resume it in
|
|
@@ -2078,6 +2124,7 @@ async function resumeAsyncRun(input: {
|
|
|
2078
2124
|
availableModels,
|
|
2079
2125
|
output: input.params.output !== undefined ? input.params.output : foregroundContract?.output ?? recoveryDescriptor?.outputPath,
|
|
2080
2126
|
outputMode: input.params.outputMode ?? foregroundContract?.outputMode ?? recoveryDescriptor?.outputMode,
|
|
2127
|
+
outputClaimPath: input.params.workflowOutputClaimPath,
|
|
2081
2128
|
...(agentContract ? { agentContract } : {}),
|
|
2082
2129
|
...(outputSchema ? { structuredOutputSchema: outputSchema } : {}),
|
|
2083
2130
|
...(recoveryDescriptor?.skills ? { skills: [...recoveryDescriptor.skills] } : {}),
|
|
@@ -2154,6 +2201,7 @@ async function resumeAsyncRun(input: {
|
|
|
2154
2201
|
...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
|
|
2155
2202
|
...(completed.acceptance ? { acceptance: completed.acceptance } : {}),
|
|
2156
2203
|
...(completed.artifactPaths ? { artifactPaths: completed.artifactPaths } : {}),
|
|
2204
|
+
...(completed.savedOutputPath ? { savedOutputPath: completed.savedOutputPath } : {}),
|
|
2157
2205
|
...(completed.outputSaveError ? { outputSaveError: completed.outputSaveError } : {}),
|
|
2158
2206
|
...(completed.transcriptPath ? { transcriptPath: completed.transcriptPath } : {}),
|
|
2159
2207
|
...(completed.transcriptError ? { transcriptError: completed.transcriptError } : {}),
|
|
@@ -3129,6 +3177,7 @@ async function waitForWorkflowAsyncSingleResult(
|
|
|
3129
3177
|
...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
|
|
3130
3178
|
...(completed.acceptance ? { acceptance: completed.acceptance } : {}),
|
|
3131
3179
|
...(completed.artifactPaths ? { artifactPaths: completed.artifactPaths } : {}),
|
|
3180
|
+
...(completed.savedOutputPath ? { savedOutputPath: completed.savedOutputPath } : {}),
|
|
3132
3181
|
...(completed.outputSaveError ? { outputSaveError: completed.outputSaveError } : {}),
|
|
3133
3182
|
...(completed.transcriptPath ? { transcriptPath: completed.transcriptPath } : {}),
|
|
3134
3183
|
...(completed.transcriptError ? { transcriptError: completed.transcriptError } : {}),
|
|
@@ -3186,11 +3235,13 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3186
3235
|
currentModelProvider: parentModel?.provider,
|
|
3187
3236
|
currentModel: parentModel,
|
|
3188
3237
|
modelScope: data.modelScope,
|
|
3238
|
+
modelResponseAliases: deps.config.modelResponseAliases,
|
|
3189
3239
|
interactive: ctx.hasUI,
|
|
3190
3240
|
permissions: deps.config.permissions,
|
|
3241
|
+
childRuntime: deps.childRuntime,
|
|
3191
3242
|
});
|
|
3192
3243
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
3193
|
-
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
3244
|
+
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
|
|
3194
3245
|
const currentProvider = parentModel?.provider;
|
|
3195
3246
|
const controlIntercomTarget = resolveRunLevelIntercomTarget(intercomBridge, contextPolicy);
|
|
3196
3247
|
const childIntercomTarget = resolveChildIntercomTargetFactory(intercomBridge, contextPolicy, id);
|
|
@@ -3270,6 +3321,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3270
3321
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
3271
3322
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
3272
3323
|
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
3324
|
+
baseRef: params.baseRef,
|
|
3273
3325
|
worktreeProvider: deps.config.worktreeProvider,
|
|
3274
3326
|
worktreeBranchPrefix: deps.config.worktreeBranchPrefix,
|
|
3275
3327
|
controlConfig,
|
|
@@ -3300,34 +3352,38 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3300
3352
|
return null;
|
|
3301
3353
|
}
|
|
3302
3354
|
|
|
3303
|
-
function createSingleWorktreeSetup(
|
|
3304
|
-
enabled: boolean | undefined,
|
|
3355
|
+
async function createSingleWorktreeSetup(
|
|
3305
3356
|
cwd: string,
|
|
3306
3357
|
runId: string,
|
|
3307
3358
|
agent: string,
|
|
3308
3359
|
setupHook: ExtensionConfig["worktreeSetupHook"],
|
|
3309
3360
|
setupHookTimeoutMs: ExtensionConfig["worktreeSetupHookTimeoutMs"],
|
|
3310
3361
|
baseDir: ExtensionConfig["worktreeBaseDir"],
|
|
3362
|
+
baseRef: string | undefined,
|
|
3311
3363
|
provider: ExtensionConfig["worktreeProvider"],
|
|
3312
3364
|
branchPrefix: ExtensionConfig["worktreeBranchPrefix"],
|
|
3313
3365
|
label?: string,
|
|
3314
3366
|
task?: string,
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3367
|
+
onProgress?: (snapshot: WorktreeSetupProgress) => void,
|
|
3368
|
+
signal?: AbortSignal,
|
|
3369
|
+
deadlineAt?: number,
|
|
3370
|
+
): Promise<{ setup?: WorktreeSetup; errorResult?: AgentToolResult<Details> }> {
|
|
3318
3371
|
try {
|
|
3319
3372
|
return {
|
|
3320
|
-
setup: createWorktrees(cwd, runId, 1, omitUndefinedProperties({
|
|
3373
|
+
setup: await createWorktrees(cwd, runId, 1, omitUndefinedProperties({
|
|
3321
3374
|
agents: [agent],
|
|
3322
3375
|
setupHook: setupHook
|
|
3323
3376
|
? { hookPath: setupHook, ...(setupHookTimeoutMs === undefined ? {} : { timeoutMs: setupHookTimeoutMs }) }
|
|
3324
3377
|
: undefined,
|
|
3325
3378
|
baseDir,
|
|
3379
|
+
baseRef,
|
|
3326
3380
|
provider,
|
|
3327
3381
|
branchPrefix,
|
|
3328
3382
|
labels: [label],
|
|
3329
3383
|
tasks: [task],
|
|
3330
|
-
|
|
3384
|
+
onProgress,
|
|
3385
|
+
signal,
|
|
3386
|
+
deadlineAt,
|
|
3331
3387
|
})),
|
|
3332
3388
|
};
|
|
3333
3389
|
} catch (error) {
|
|
@@ -3353,15 +3409,6 @@ function resolveSingleRunOutputBaseDir(deps: ExecutorDeps, artifactsDir: string,
|
|
|
3353
3409
|
return resolveConfiguredSingleRunOutputBaseDir(deps) ?? path.join(artifactsDir, "outputs", sanitizeRunPathSegment(runId));
|
|
3354
3410
|
}
|
|
3355
3411
|
|
|
3356
|
-
function resolveWorkflowAggregateOutputPath(
|
|
3357
|
-
output: string | boolean | undefined,
|
|
3358
|
-
ctxCwd: string,
|
|
3359
|
-
workflowCwd: string,
|
|
3360
|
-
outputBaseDir: string,
|
|
3361
|
-
): string | undefined {
|
|
3362
|
-
return resolveSingleOutputPath(output, ctxCwd, workflowCwd, outputBaseDir);
|
|
3363
|
-
}
|
|
3364
|
-
|
|
3365
3412
|
function workflowChildDefaultOutput(aggregateOutputPath: string | undefined, artifactsDir: string, workflowRunId: string, workflowKey: string): string {
|
|
3366
3413
|
if (aggregateOutputPath) {
|
|
3367
3414
|
const parsed = path.parse(aggregateOutputPath);
|
|
@@ -3423,7 +3470,9 @@ function resolveWorkflowChildOutputPath(input: {
|
|
|
3423
3470
|
key: string;
|
|
3424
3471
|
params: Record<string, unknown>;
|
|
3425
3472
|
}): { path?: string; inherited: boolean } {
|
|
3426
|
-
|
|
3473
|
+
const rawOutput = input.params.output;
|
|
3474
|
+
const hasExplicitOutput = typeof rawOutput === "string" || typeof rawOutput === "boolean";
|
|
3475
|
+
if (typeof input.params.resume === "string" && (!hasExplicitOutput || rawOutput === true || rawOutput === "true")) {
|
|
3427
3476
|
if (!input.state) return { path: undefined, inherited: false };
|
|
3428
3477
|
const index = input.params.index;
|
|
3429
3478
|
const target = resolveResumeTarget({
|
|
@@ -3432,15 +3481,16 @@ function resolveWorkflowChildOutputPath(input: {
|
|
|
3432
3481
|
}, input.state);
|
|
3433
3482
|
return { path: "recoveryDescriptor" in target ? target.recoveryDescriptor?.outputPath : undefined, inherited: false };
|
|
3434
3483
|
}
|
|
3435
|
-
const rawOutput = input.params.output;
|
|
3436
|
-
const hasExplicitOutput = typeof rawOutput === "string" || typeof rawOutput === "boolean";
|
|
3437
3484
|
const childCwd = typeof input.params.cwd === "string" ? resolveChildCwd(input.workflowCwd, input.params.cwd) : input.workflowCwd;
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3485
|
+
let agentOutput: string | undefined;
|
|
3486
|
+
if (rawOutput === true || rawOutput === "true" || (!hasExplicitOutput && !input.aggregateOutputPath)) {
|
|
3487
|
+
const agentScope = resolveExecutionAgentScope(input.params.agentScope ?? input.workflowAgentScope);
|
|
3488
|
+
const discoveredAgents = input.discoverAgents(childCwd, agentScope).agents;
|
|
3489
|
+
const agent = typeof input.params.agent === "string"
|
|
3490
|
+
? resolveAgentName(input.params.agent, discoveredAgents).agent ?? resolveAgentName(input.params.agent, input.agents).agent
|
|
3491
|
+
: undefined;
|
|
3492
|
+
agentOutput = typeof agent?.output === "string" ? agent.output : undefined;
|
|
3493
|
+
}
|
|
3444
3494
|
const output = rawOutput === true || rawOutput === "true"
|
|
3445
3495
|
? agentOutput
|
|
3446
3496
|
: hasExplicitOutput
|
|
@@ -3529,7 +3579,7 @@ function prepareWorkflowChildLaunchParams(input: {
|
|
|
3529
3579
|
childParams = { ...input.childParams, output: input.outputOverride };
|
|
3530
3580
|
} else if (usesDefaultOutput && input.aggregateOutputPath !== undefined) {
|
|
3531
3581
|
childParams = { ...input.childParams, output: workflowChildDefaultOutput(input.aggregateOutputPath, input.artifactsDir, input.parentWorkflowRunId, input.workflowKey) };
|
|
3532
|
-
} else if (input.childParams.resume === undefined) {
|
|
3582
|
+
} else if (input.childParams.resume === undefined || input.childParams.output !== undefined) {
|
|
3533
3583
|
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 });
|
|
3534
3584
|
if (resolvedOutput.path) childParams = { ...input.childParams, output: resolvedOutput.path };
|
|
3535
3585
|
}
|
|
@@ -3543,7 +3593,7 @@ function prepareWorkflowChildLaunchParams(input: {
|
|
|
3543
3593
|
return prepareWorkflowLaunchParams(input.workflowDefaults, childParams, input.parentWorkflowRunId, input.workflowKey, { ...input.options, externalAsyncRequired, outputClaimPath: input.outputClaimPath });
|
|
3544
3594
|
}
|
|
3545
3595
|
|
|
3546
|
-
function finalizeSingleWorktreeHandoff(input: {
|
|
3596
|
+
async function finalizeSingleWorktreeHandoff(input: {
|
|
3547
3597
|
worktreeSetup: WorktreeSetup;
|
|
3548
3598
|
artifactsDir: string;
|
|
3549
3599
|
runId: string;
|
|
@@ -3552,55 +3602,78 @@ function finalizeSingleWorktreeHandoff(input: {
|
|
|
3552
3602
|
result: SingleResult;
|
|
3553
3603
|
workflowKey?: string;
|
|
3554
3604
|
lane?: import("../../shared/types.ts").WorkflowLaneMetadata;
|
|
3555
|
-
}): { suffix: string; reference?: NonNullable<Details["parallelHandoff"]> } {
|
|
3556
|
-
|
|
3557
|
-
const diffs = diffWorktrees(input.worktreeSetup, [input.agent], diffsDir);
|
|
3558
|
-
const diffSummary = formatWorktreeDiffSummary(diffs);
|
|
3559
|
-
const manifestPath = parallelHandoffPath(input.artifactsDir, input.runId);
|
|
3560
|
-
const handoff = {
|
|
3561
|
-
manifestPath,
|
|
3562
|
-
runId: input.runId,
|
|
3563
|
-
mode: "single" as const,
|
|
3564
|
-
source: "foreground" as const,
|
|
3565
|
-
cwd: input.cwd,
|
|
3566
|
-
stepIndex: 0,
|
|
3567
|
-
flatStartIndex: 0,
|
|
3568
|
-
setup: input.worktreeSetup,
|
|
3569
|
-
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,
|
|
3570
|
-
diffs,
|
|
3571
|
-
results: [{
|
|
3572
|
-
agent: input.result.agent,
|
|
3573
|
-
...(input.workflowKey ? { workflowKey: input.workflowKey } : {}),
|
|
3574
|
-
...(input.workflowKey ? { runId: input.runId } : {}),
|
|
3575
|
-
...(input.lane ? { lane: input.lane } : {}),
|
|
3576
|
-
status: resolveSubagentResultStatus(omitUndefinedProperties({
|
|
3577
|
-
exitCode: input.result.exitCode,
|
|
3578
|
-
interrupted: input.result.interrupted,
|
|
3579
|
-
detached: input.result.detached,
|
|
3580
|
-
state: input.result.stopped ? "stopped" : undefined,
|
|
3581
|
-
processSignal: input.result.processSignal,
|
|
3582
|
-
timedOut: input.result.timedOut,
|
|
3583
|
-
stopped: input.result.stopped,
|
|
3584
|
-
turnBudgetExceeded: input.result.turnBudgetExceeded,
|
|
3585
|
-
})),
|
|
3586
|
-
summary: resultSummaryForIntercom(input.result),
|
|
3587
|
-
...(input.result.artifactPaths?.outputPath ? { outputPath: input.result.artifactPaths.outputPath } : {}),
|
|
3588
|
-
...(input.result.structuredOutput !== undefined ? { structuredOutput: input.result.structuredOutput } : {}),
|
|
3589
|
-
...(input.result.structuredOutputPath ? { structuredOutputPath: input.result.structuredOutputPath } : {}),
|
|
3590
|
-
...(input.result.sessionFile ? { sessionPath: input.result.sessionFile } : {}),
|
|
3591
|
-
}],
|
|
3592
|
-
};
|
|
3605
|
+
}): Promise<{ suffix: string; reference?: NonNullable<Details["parallelHandoff"]> }> {
|
|
3606
|
+
let admitted = false;
|
|
3593
3607
|
try {
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3608
|
+
return await withWorktreeTransaction(() => {
|
|
3609
|
+
admitted = true;
|
|
3610
|
+
const diffsDir = path.join(input.artifactsDir, "worktree-diffs", input.runId);
|
|
3611
|
+
const diffs = diffWorktrees(input.worktreeSetup, [input.agent], diffsDir);
|
|
3612
|
+
const diffSummary = formatWorktreeDiffSummary(diffs);
|
|
3613
|
+
const manifestPath = parallelHandoffPath(input.artifactsDir, input.runId);
|
|
3614
|
+
const handoff = {
|
|
3615
|
+
manifestPath,
|
|
3616
|
+
runId: input.runId,
|
|
3617
|
+
mode: "single" as const,
|
|
3618
|
+
source: "foreground" as const,
|
|
3619
|
+
cwd: input.cwd,
|
|
3620
|
+
stepIndex: 0,
|
|
3621
|
+
flatStartIndex: 0,
|
|
3622
|
+
setup: input.worktreeSetup,
|
|
3623
|
+
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,
|
|
3624
|
+
diffs,
|
|
3625
|
+
results: [{
|
|
3626
|
+
agent: input.result.agent,
|
|
3627
|
+
...(input.workflowKey ? { workflowKey: input.workflowKey } : {}),
|
|
3628
|
+
...(input.workflowKey ? { runId: input.runId } : {}),
|
|
3629
|
+
...(input.lane ? { lane: input.lane } : {}),
|
|
3630
|
+
status: resolveSubagentResultStatus(omitUndefinedProperties({
|
|
3631
|
+
exitCode: input.result.exitCode,
|
|
3632
|
+
interrupted: input.result.interrupted,
|
|
3633
|
+
detached: input.result.detached,
|
|
3634
|
+
state: input.result.stopped ? "stopped" : undefined,
|
|
3635
|
+
processSignal: input.result.processSignal,
|
|
3636
|
+
timedOut: input.result.timedOut,
|
|
3637
|
+
stopped: input.result.stopped,
|
|
3638
|
+
turnBudgetExceeded: input.result.turnBudgetExceeded,
|
|
3639
|
+
})),
|
|
3640
|
+
summary: resultSummaryForIntercom(input.result),
|
|
3641
|
+
...(input.result.artifactPaths?.outputPath ? { outputPath: input.result.artifactPaths.outputPath } : {}),
|
|
3642
|
+
...(input.result.structuredOutput !== undefined ? { structuredOutput: input.result.structuredOutput } : {}),
|
|
3643
|
+
...(input.result.structuredOutputPath ? { structuredOutputPath: input.result.structuredOutputPath } : {}),
|
|
3644
|
+
...(input.result.sessionFile ? { sessionPath: input.result.sessionFile } : {}),
|
|
3645
|
+
}],
|
|
3646
|
+
};
|
|
3647
|
+
try {
|
|
3648
|
+
writeParallelHandoffGroup(handoff);
|
|
3649
|
+
const cleanup = cleanupWorktrees(input.worktreeSetup, { kind: "preserve", capturedDiffs: diffs, handoffManifestPath: manifestPath });
|
|
3650
|
+
const reference = writeParallelHandoffGroup({ ...handoff, cleanup });
|
|
3651
|
+
return {
|
|
3652
|
+
suffix: [diffSummary, formatParallelHandoffReference(reference)].filter(Boolean).join("\n\n"),
|
|
3653
|
+
reference,
|
|
3654
|
+
};
|
|
3655
|
+
} catch (error) {
|
|
3656
|
+
return { suffix: [diffSummary, formatParallelHandoffError(error)].filter(Boolean).join("\n\n") };
|
|
3657
|
+
}
|
|
3658
|
+
});
|
|
3601
3659
|
} catch (error) {
|
|
3602
|
-
|
|
3603
|
-
|
|
3660
|
+
if (admitted) throw error;
|
|
3661
|
+
const reference = retainSingleWorktreeHandoff(input, error);
|
|
3662
|
+
return { suffix: `${formatParallelHandoffError(error)}\n\n${formatParallelHandoffReference(reference)}`, reference };
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
|
|
3666
|
+
function retainSingleWorktreeHandoff(input: { worktreeSetup: WorktreeSetup; artifactsDir: string; runId: string; cwd: string }, error: unknown): NonNullable<Details["parallelHandoff"]> {
|
|
3667
|
+
const reason = `Worktree finalization retained; manual reconciliation required: ${error instanceof Error ? error.message : String(error)}`;
|
|
3668
|
+
return writeParallelHandoffGroup({
|
|
3669
|
+
manifestPath: parallelHandoffPath(input.artifactsDir, input.runId), runId: input.runId,
|
|
3670
|
+
mode: "single", source: "foreground", cwd: input.cwd, stepIndex: 0, flatStartIndex: 0,
|
|
3671
|
+
setup: input.worktreeSetup, diffs: [], results: [],
|
|
3672
|
+
cleanup: { state: "partial", pruned: false, errors: [reason], tasks: input.worktreeSetup.worktrees.map((worktree) => ({
|
|
3673
|
+
index: worktree.index, path: worktree.path, branch: worktree.branch, provider: worktree.provider, naming: worktree.naming,
|
|
3674
|
+
worktreeRemoved: false, branchRemoved: false, preserved: true, reason,
|
|
3675
|
+
})) },
|
|
3676
|
+
});
|
|
3604
3677
|
}
|
|
3605
3678
|
|
|
3606
3679
|
async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Promise<AgentToolResult<Details>> {
|
|
@@ -3675,26 +3748,26 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3675
3748
|
const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
|
|
3676
3749
|
let effectiveOutput = normalizeSingleOutputOverride(rawOutput, agentConfig.output);
|
|
3677
3750
|
const effectiveOutputMode = params.outputMode ?? agentConfig.outputMode ?? "inline";
|
|
3678
|
-
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
3751
|
+
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
|
|
3679
3752
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
|
|
3680
3753
|
|
|
3681
3754
|
|
|
3682
3755
|
const sourceCwd = effectiveCwd;
|
|
3683
3756
|
let pendingHandoff: Details["parallelHandoff"];
|
|
3684
|
-
const { setup: worktreeSetup, errorResult: worktreeSetupError } = createSingleWorktreeSetup(
|
|
3685
|
-
params.worktree,
|
|
3757
|
+
const { setup: worktreeSetup, errorResult: worktreeSetupError } = params.worktree ? await createSingleWorktreeSetup(
|
|
3686
3758
|
sourceCwd,
|
|
3687
3759
|
runId,
|
|
3688
3760
|
params.agent!,
|
|
3689
3761
|
deps.config.worktreeSetupHook,
|
|
3690
3762
|
deps.config.worktreeSetupHookTimeoutMs,
|
|
3691
3763
|
deps.config.worktreeBaseDir,
|
|
3764
|
+
params.baseRef,
|
|
3692
3765
|
deps.config.worktreeProvider,
|
|
3693
3766
|
deps.config.worktreeBranchPrefix,
|
|
3694
3767
|
params.lane?.key ?? params.workflowKey,
|
|
3695
3768
|
task,
|
|
3696
|
-
(
|
|
3697
|
-
pendingHandoff =
|
|
3769
|
+
(progress) => {
|
|
3770
|
+
pendingHandoff = writeWorktreeSetupHandoff({
|
|
3698
3771
|
manifestPath: parallelHandoffPath(artifactsDir, runId),
|
|
3699
3772
|
runId,
|
|
3700
3773
|
mode: "single",
|
|
@@ -3702,13 +3775,42 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3702
3775
|
cwd: sourceCwd,
|
|
3703
3776
|
stepIndex: 0,
|
|
3704
3777
|
flatStartIndex: 0,
|
|
3705
|
-
|
|
3778
|
+
progress,
|
|
3706
3779
|
laneBindings: params.workflowKey || lane ? [{ index: 0, taskIndex: 0, ...(params.workflowKey ? { workflowKey: params.workflowKey, runId } : {}), ...(lane ? { lane } : {}) }] : undefined,
|
|
3707
3780
|
});
|
|
3708
3781
|
},
|
|
3709
|
-
|
|
3710
|
-
|
|
3782
|
+
signal,
|
|
3783
|
+
data.deadlineAt,
|
|
3784
|
+
) : {};
|
|
3785
|
+
if (worktreeSetupError) {
|
|
3786
|
+
if (pendingHandoff) {
|
|
3787
|
+
worktreeSetupError.details!.parallelHandoff = pendingHandoff;
|
|
3788
|
+
worktreeSetupError.content.push({ type: "text", text: formatParallelHandoffReference(pendingHandoff) });
|
|
3789
|
+
}
|
|
3790
|
+
return worktreeSetupError;
|
|
3791
|
+
}
|
|
3711
3792
|
const singleCwd = worktreeSetup?.worktrees[0]?.agentCwd ?? sourceCwd;
|
|
3793
|
+
const cleanupSingleWorktree = async () => {
|
|
3794
|
+
if (!worktreeSetup) return;
|
|
3795
|
+
let admitted = false;
|
|
3796
|
+
try { await withWorktreeTransaction(() => {
|
|
3797
|
+
if (data.deadlineAt !== undefined && Date.now() >= data.deadlineAt) throw new Error("Run deadline expired before unlaunched worktree cleanup");
|
|
3798
|
+
admitted = true;
|
|
3799
|
+
const cleanup = cleanupWorktrees(worktreeSetup);
|
|
3800
|
+
pendingHandoff = writeParallelHandoffGroup({
|
|
3801
|
+
manifestPath: parallelHandoffPath(artifactsDir, runId), runId, mode: "single", source: "foreground",
|
|
3802
|
+
cwd: sourceCwd, stepIndex: 0, flatStartIndex: 0, setup: worktreeSetup, diffs: [], results: [], cleanup,
|
|
3803
|
+
});
|
|
3804
|
+
}); }
|
|
3805
|
+
catch (error) {
|
|
3806
|
+
if (admitted) throw error;
|
|
3807
|
+
pendingHandoff = retainSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd }, error);
|
|
3808
|
+
}
|
|
3809
|
+
};
|
|
3810
|
+
if (worktreeSetup && (signal?.aborted || (data.deadlineAt !== undefined && Date.now() >= data.deadlineAt))) {
|
|
3811
|
+
await cleanupSingleWorktree();
|
|
3812
|
+
return { content: [{ type: "text", text: "Subagent setup canceled before child launch." }], isError: true, details: { mode: "single", results: [], parallelHandoff: pendingHandoff } };
|
|
3813
|
+
}
|
|
3712
3814
|
|
|
3713
3815
|
const authoredTask = task;
|
|
3714
3816
|
if (shouldForkAgent(contextPolicy, params.agent!)) {
|
|
@@ -3718,8 +3820,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3718
3820
|
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, singleCwd, resolveSingleRunOutputBaseDir(deps, artifactsDir, runId));
|
|
3719
3821
|
const validationError = validateFileOnlyOutputMode(effectiveOutputMode, outputPath, `Single run (${params.agent})`);
|
|
3720
3822
|
if (validationError) {
|
|
3721
|
-
|
|
3722
|
-
return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [] } };
|
|
3823
|
+
await cleanupSingleWorktree();
|
|
3824
|
+
return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [], parallelHandoff: pendingHandoff } };
|
|
3723
3825
|
}
|
|
3724
3826
|
const structuredRuntime = params.outputSchema
|
|
3725
3827
|
? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ? path.join(artifactsDir, "structured-output", runId) : undefined, { acceptanceReport: resolveAcceptanceReportMode(params.acceptance) })
|
|
@@ -3742,6 +3844,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3742
3844
|
}
|
|
3743
3845
|
const interruptController = new AbortController();
|
|
3744
3846
|
let detachForeground: ((reason?: string) => boolean) | undefined;
|
|
3847
|
+
let childSessionControls: ForegroundChildSessionControls | undefined;
|
|
3745
3848
|
const foregroundControl = deps.state.foregroundControls.get(runId);
|
|
3746
3849
|
if (foregroundControl) {
|
|
3747
3850
|
const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin));
|
|
@@ -3762,9 +3865,23 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3762
3865
|
return true;
|
|
3763
3866
|
},
|
|
3764
3867
|
detach: () => detachForeground?.("user request") === true,
|
|
3868
|
+
steer: async (input: ForegroundSteerInput): Promise<ForegroundSteerOutcome> => {
|
|
3869
|
+
if (!childSessionControls) return { state: "failed", reason: CHILD_SESSION_NOT_RUNNING_YET };
|
|
3870
|
+
try {
|
|
3871
|
+
if (input.mode === "follow_up") {
|
|
3872
|
+
await childSessionControls.followUp(input.message);
|
|
3873
|
+
return { state: "queued" };
|
|
3874
|
+
}
|
|
3875
|
+
await childSessionControls.steer(input.message);
|
|
3876
|
+
return { state: "delivered" };
|
|
3877
|
+
} catch (error) {
|
|
3878
|
+
return { state: "failed", reason: error instanceof Error ? error.message : String(error) };
|
|
3879
|
+
}
|
|
3880
|
+
},
|
|
3765
3881
|
}));
|
|
3766
3882
|
}
|
|
3767
3883
|
|
|
3884
|
+
const modelResponseAliases = deps.config.modelResponseAliases === undefined ? undefined : structuredClone(deps.config.modelResponseAliases);
|
|
3768
3885
|
const forwardSingleUpdate = onUpdate
|
|
3769
3886
|
? (update: AgentToolResult<Details>) => {
|
|
3770
3887
|
if (foregroundControl) updateForegroundChild(foregroundControl, 0, update.details?.progress?.[0]);
|
|
@@ -3784,7 +3901,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3784
3901
|
runtimeSnapshotHost: deps.pi,
|
|
3785
3902
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
3786
3903
|
llmIntentArbiter: createTaskMutationArbiter(ctx),
|
|
3787
|
-
|
|
3904
|
+
childRuntime: deps.childRuntime,
|
|
3905
|
+
onChildSession: (controls) => { childSessionControls = controls; },
|
|
3788
3906
|
context: data.contextPolicy.contextForAgent(params.agent!),
|
|
3789
3907
|
unknownAgentDiagnosticContext: data.unknownAgentDiagnosticContext,
|
|
3790
3908
|
runFanoutBudget: params.runFanoutAdmitted ? data.runFanoutBudget : { ...data.runFanoutBudget, parentPath: `${data.runFanoutBudget.parentPath ? `${data.runFanoutBudget.parentPath}/` : ""}single` },
|
|
@@ -3823,6 +3941,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3823
3941
|
thinkingCeiling: agentConfig.maxThinking,
|
|
3824
3942
|
extensionBindings: params.extensionBindings,
|
|
3825
3943
|
availableModels,
|
|
3944
|
+
modelResponseAliases,
|
|
3826
3945
|
preferredModelProvider: currentProvider,
|
|
3827
3946
|
modelScope: modelScopes,
|
|
3828
3947
|
skills: effectiveSkills,
|
|
@@ -3835,14 +3954,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3835
3954
|
onDetachReady: (detach) => {
|
|
3836
3955
|
detachForeground = detach;
|
|
3837
3956
|
},
|
|
3838
|
-
onDetachedExit: (result) => {
|
|
3957
|
+
onDetachedExit: async (result) => {
|
|
3839
3958
|
if (resolveDetachedWorkflowChild) {
|
|
3840
3959
|
resolveDetachedWorkflowChild(result);
|
|
3841
3960
|
return;
|
|
3842
3961
|
}
|
|
3843
3962
|
try {
|
|
3844
3963
|
if (worktreeSetup) {
|
|
3845
|
-
finalizeSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd, agent: params.agent!, result, workflowKey: params.workflowKey, lane });
|
|
3964
|
+
await finalizeSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd, agent: params.agent!, result, workflowKey: params.workflowKey, lane });
|
|
3846
3965
|
}
|
|
3847
3966
|
try {
|
|
3848
3967
|
updateRememberedForegroundChild(deps.state, { runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, index: 0, result, events: deps.pi.events, notify: true });
|
|
@@ -3867,7 +3986,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3867
3986
|
try {
|
|
3868
3987
|
if (foregroundControl) finishForegroundChild(foregroundControl, 0);
|
|
3869
3988
|
} finally {
|
|
3870
|
-
removeForegroundControlIfIdle(deps.state, runId);
|
|
3989
|
+
removeForegroundControlIfIdle(deps.state, runId, deps.trackRetainedNestedRoute);
|
|
3871
3990
|
}
|
|
3872
3991
|
}
|
|
3873
3992
|
}
|
|
@@ -3878,12 +3997,13 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3878
3997
|
toolTimeoutMs: params.toolTimeoutMs,
|
|
3879
3998
|
configToolTimeoutMs: data.configToolTimeoutMs,
|
|
3880
3999
|
toolBudget: effectiveToolBudget.toolBudget,
|
|
4000
|
+
usageBudget: data.usageBudget ?? data.inheritedUsageBudget,
|
|
3881
4001
|
capabilityCeiling: data.capabilityCeiling,
|
|
3882
4002
|
allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
|
|
3883
4003
|
}));
|
|
3884
4004
|
r = launched.detached && detachedWorkflowChild ? await detachedWorkflowChild : launched;
|
|
3885
4005
|
} catch (error) {
|
|
3886
|
-
|
|
4006
|
+
await cleanupSingleWorktree();
|
|
3887
4007
|
throw error;
|
|
3888
4008
|
} finally {
|
|
3889
4009
|
// An attached runSync rejection still owns its child and structured runtime.
|
|
@@ -3898,11 +4018,11 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3898
4018
|
recordRun(params.agent!, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0, r);
|
|
3899
4019
|
}
|
|
3900
4020
|
|
|
3901
|
-
let worktreeHandoff: ReturnType<typeof finalizeSingleWorktreeHandoff
|
|
4021
|
+
let worktreeHandoff: Awaited<ReturnType<typeof finalizeSingleWorktreeHandoff>> | undefined;
|
|
3902
4022
|
if (worktreeSetup) {
|
|
3903
4023
|
worktreeHandoff = r.detached
|
|
3904
4024
|
? { suffix: pendingHandoff ? formatParallelHandoffReference(pendingHandoff) : "", reference: pendingHandoff }
|
|
3905
|
-
: finalizeSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd, agent: params.agent!, result: r, workflowKey: params.workflowKey, lane });
|
|
4025
|
+
: await finalizeSingleWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: sourceCwd, agent: params.agent!, result: r, workflowKey: params.workflowKey, lane });
|
|
3906
4026
|
}
|
|
3907
4027
|
|
|
3908
4028
|
if (r.progress) allProgress.push(r.progress);
|
|
@@ -3940,7 +4060,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3940
4060
|
usageBudget: usageBudgetState(data.usageBudget, totalCost),
|
|
3941
4061
|
...(worktreeHandoff?.reference ? { parallelHandoff: worktreeHandoff.reference } : {}),
|
|
3942
4062
|
}));
|
|
3943
|
-
rememberForegroundRun(deps.state, { runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: details.results, params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings });
|
|
4063
|
+
rememberForegroundRun(deps.state, { modelResponseAliases, runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: details.results, params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings });
|
|
3944
4064
|
|
|
3945
4065
|
const suppressRoutineResultIntercom = shouldSuppressRoutineResultIntercom({ suppressRoutineResultIntercom: params.suppressRoutineResultIntercom, results: [r] });
|
|
3946
4066
|
if (!r.detached && !r.interrupted && !suppressRoutineResultIntercom) {
|
|
@@ -4016,6 +4136,7 @@ function duplicateSubagentCallResult(params: SubagentParamsLike): AgentToolResul
|
|
|
4016
4136
|
}
|
|
4017
4137
|
|
|
4018
4138
|
const workflowLaunchObservers = new WeakMap<object, (launch: { agent: string; sessionName?: string; sessionFile?: string; async: boolean; runId?: string }) => void>();
|
|
4139
|
+
const workflowOwnedUsageBudgets = new WeakMap<object, UsageBudgetConfig>();
|
|
4019
4140
|
|
|
4020
4141
|
/**
|
|
4021
4142
|
* Terminal-mission retention can remove a mission while its children still
|
|
@@ -4096,9 +4217,13 @@ function workflowChildResult(
|
|
|
4096
4217
|
? result.details.results[0].finalOutput
|
|
4097
4218
|
: receiptOutput;
|
|
4098
4219
|
const childError = result.details.results.map((child) => child.error).find((error): error is string => Boolean(error));
|
|
4099
|
-
const
|
|
4100
|
-
? `${childError}\n\n${receiptOutput}`
|
|
4220
|
+
const failureErrorBase = childError && receiptOutput
|
|
4221
|
+
? receiptOutput.includes(childError) ? receiptOutput : `${childError}\n\n${receiptOutput}`
|
|
4101
4222
|
: childError || receiptOutput || output || "Child run failed.";
|
|
4223
|
+
const savedOutputEvidence = [...new Set(result.details.results.map((child) => child.savedOutputPath).filter((value): value is string => Boolean(value)))]
|
|
4224
|
+
.filter((savedOutputPath) => !failureErrorBase.includes(savedOutputPath))
|
|
4225
|
+
.map((savedOutputPath) => `Saved output: ${savedOutputPath}`);
|
|
4226
|
+
const failureError = [failureErrorBase, ...savedOutputEvidence].join("\n");
|
|
4102
4227
|
const detached = result.details.results.some((child) => child.detached);
|
|
4103
4228
|
const interrupted = result.details.results.some((child) => child.interrupted);
|
|
4104
4229
|
const stopped = result.details.results.some((child) => child.stopped);
|
|
@@ -4137,8 +4262,6 @@ function workflowChildResult(
|
|
|
4137
4262
|
}
|
|
4138
4263
|
const requestedContext = childParams.context === "fresh" || childParams.context === "fork" ? childParams.context : undefined;
|
|
4139
4264
|
const resolvedContext = result.details.context ?? (resolvedContexts.length === 1 ? resolvedContexts[0] : resolvedContexts.length > 1 ? "mixed" : undefined);
|
|
4140
|
-
const resumeSourceRunId = typeof childParams.resume === "string" && childParams.resume.trim() ? childParams.resume.trim() : undefined;
|
|
4141
|
-
const continuationRunIds = [...new Set([resumeSourceRunId, runId].filter((value): value is string => Boolean(value)))];
|
|
4142
4265
|
const outputReference = result.details.results.find((child) => child.savedOutputPath)?.savedOutputPath
|
|
4143
4266
|
?? result.details.results.find((child) => child.outputReference?.path)?.outputReference?.path;
|
|
4144
4267
|
const outputPathMapping = typeof childParams.task === "string" ? outputPathMappingFromTask(childParams.task, outputReference) : undefined;
|
|
@@ -4171,7 +4294,7 @@ function workflowChildResult(
|
|
|
4171
4294
|
...(outputPathMapping ? { outputPathMapping } : {}),
|
|
4172
4295
|
...(externalAdapter ? { externalAdapter } : {}),
|
|
4173
4296
|
resumability,
|
|
4174
|
-
continuation: { runIds:
|
|
4297
|
+
continuation: { runIds: runId ? [runId] : [] },
|
|
4175
4298
|
artifactPaths: [...artifactPaths],
|
|
4176
4299
|
results: result.details.results,
|
|
4177
4300
|
};
|
|
@@ -4216,6 +4339,7 @@ function workflowSteerReceipt(key: string, result: AgentToolResult<Details>): Wo
|
|
|
4216
4339
|
};
|
|
4217
4340
|
}
|
|
4218
4341
|
|
|
4342
|
+
const CHILD_SESSION_NOT_RUNNING_YET = "Child session is not running yet.";
|
|
4219
4343
|
const MAX_WORKFLOW_RESUME_HINT_BYTES = 1024;
|
|
4220
4344
|
const MAX_WORKFLOW_CHILD_RUN_ID_BYTES = 256;
|
|
4221
4345
|
const WORKFLOW_RESUME_HINT_PARENT_STATES = new Set(["complete", "failed", "partial"]);
|
|
@@ -4283,10 +4407,33 @@ function missingWorkflowReceiptResumeHint(reference: WorkflowReceiptResumeRefere
|
|
|
4283
4407
|
}
|
|
4284
4408
|
}
|
|
4285
4409
|
|
|
4286
|
-
function
|
|
4287
|
-
reference: WorkflowReceiptResumeReference,
|
|
4288
|
-
|
|
4289
|
-
|
|
4410
|
+
function resolveWorkflowResume(
|
|
4411
|
+
reference: WorkflowReceiptResumeReference | string,
|
|
4412
|
+
deps: ExecutorDeps,
|
|
4413
|
+
parentSessionFile: string | null,
|
|
4414
|
+
index?: number,
|
|
4415
|
+
): string | { runId: string; runIds: string[] } {
|
|
4416
|
+
const state = deps.state;
|
|
4417
|
+
if (typeof reference === "string") {
|
|
4418
|
+
const target = resolveRequestedResumeTarget({ id: reference.trim(), ...(index !== undefined ? { index } : {}) }, deps, parentSessionFile);
|
|
4419
|
+
// Live routing stays with action=resume and does not claim retained lineage.
|
|
4420
|
+
if (target.kind !== "revive") return reference.trim();
|
|
4421
|
+
const runId = target.runId;
|
|
4422
|
+
const status = target.source === "async" && target.asyncDir ? readStatus(target.asyncDir) : undefined;
|
|
4423
|
+
if (status?.runId === runId && status.parentWorkflowRunId && status.workflowKey) {
|
|
4424
|
+
// Only follow the validated child's recorded owner; never search other workflows.
|
|
4425
|
+
const receiptPath = workflowReceiptPath(DIRS.async, status.parentWorkflowRunId);
|
|
4426
|
+
try {
|
|
4427
|
+
fs.statSync(receiptPath);
|
|
4428
|
+
} catch (error) {
|
|
4429
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return { runId, runIds: [runId] };
|
|
4430
|
+
throw error;
|
|
4431
|
+
}
|
|
4432
|
+
const entry = readWorkflowReceipt(DIRS.async, status.parentWorkflowRunId).entries[status.workflowKey];
|
|
4433
|
+
if (entry?.latestRunId === runId) return { runId, runIds: entry.continuation.runIds };
|
|
4434
|
+
}
|
|
4435
|
+
return { runId, runIds: [runId] };
|
|
4436
|
+
}
|
|
4290
4437
|
try {
|
|
4291
4438
|
const entry = resolveWorkflowReceiptResumeEntry({
|
|
4292
4439
|
reference,
|
|
@@ -4296,9 +4443,7 @@ function resolveKeyedWorkflowResume(
|
|
|
4296
4443
|
if (target.kind !== "revive") throw new Error(`Workflow receipt child '${reference.key}' latest run '${runId}' is still running.`);
|
|
4297
4444
|
},
|
|
4298
4445
|
});
|
|
4299
|
-
|
|
4300
|
-
if (!runId) throw new Error(`Workflow receipt child '${reference.key}' has no retained run id.`);
|
|
4301
|
-
return { runId, runIds: entry.continuation.runIds };
|
|
4446
|
+
return { runId: entry.latestRunId, runIds: entry.continuation.runIds };
|
|
4302
4447
|
} catch (error) {
|
|
4303
4448
|
const workflowRunId = reference.workflowRunId.trim();
|
|
4304
4449
|
if (isMissingWorkflowReceiptDiagnostic(error, workflowRunId)) {
|
|
@@ -4351,7 +4496,6 @@ export async function steerWorkflowChildByKey(input: {
|
|
|
4351
4496
|
while (true) {
|
|
4352
4497
|
const control = [...input.state.foregroundControls.values()].find((candidate) => candidate.parentWorkflowRunId === input.workflowRunId
|
|
4353
4498
|
&& candidate.workflowKey === input.key
|
|
4354
|
-
&& Boolean(candidate.workflowSteeringDir)
|
|
4355
4499
|
&& (candidate.activeChildren?.size ?? 0) > 0);
|
|
4356
4500
|
if (control) {
|
|
4357
4501
|
const result = await steerWorkflowForegroundTarget({
|
|
@@ -4359,10 +4503,9 @@ export async function steerWorkflowChildByKey(input: {
|
|
|
4359
4503
|
message: input.message,
|
|
4360
4504
|
mode: input.options.mode,
|
|
4361
4505
|
index: input.options.index,
|
|
4362
|
-
ackTimeoutMs: Math.max(1, deadline - Date.now()),
|
|
4363
|
-
signal: input.signal,
|
|
4364
4506
|
});
|
|
4365
|
-
|
|
4507
|
+
// The control registers before its child session exists; keep polling until the steer can route.
|
|
4508
|
+
if (!result.details.steering?.targets.some((target) => target.reason === CHILD_SESSION_NOT_RUNNING_YET) || Date.now() >= deadline) return workflowSteerReceipt(input.key, result);
|
|
4366
4509
|
}
|
|
4367
4510
|
|
|
4368
4511
|
const workflowStatus = readStatus(path.join(asyncDirRoot, input.workflowRunId));
|
|
@@ -4435,10 +4578,11 @@ export function prepareWorkflowLaunchParams(
|
|
|
4435
4578
|
const toolBudget = childParams.toolBudget ?? workflowDefaults.toolBudget;
|
|
4436
4579
|
const intercomBridge = childParams.intercomBridge ?? workflowDefaults.intercomBridge;
|
|
4437
4580
|
const worktree = childParams.worktree ?? workflowDefaults.worktree;
|
|
4581
|
+
const baseRef = Object.hasOwn(childParams, "baseRef") ? childParams.baseRef : undefined;
|
|
4438
4582
|
const outputSchema = Object.hasOwn(childParams, "outputSchema") ? childParams.outputSchema : workflowDefaults.outputSchema;
|
|
4439
4583
|
if (outputSchema !== undefined) assertJsonSchemaObject(outputSchema, "outputSchema");
|
|
4440
4584
|
const agentContract = Object.hasOwn(childParams, "agentContract") ? childParams.agentContract : workflowDefaults.agentContract;
|
|
4441
|
-
if (agentContract !== undefined && !
|
|
4585
|
+
if (agentContract !== undefined && !isAgentContract(agentContract as AgentContract)) throw new Error("agentContract must be { version: 1 }.");
|
|
4442
4586
|
const acceptance = Object.hasOwn(childParams, "acceptance") ? childParams.acceptance : workflowDefaults.acceptance;
|
|
4443
4587
|
const output = Object.hasOwn(childParams, "output") ? childParams.output : workflowDefaults.output;
|
|
4444
4588
|
if (output !== undefined && typeof output !== "string" && typeof output !== "boolean") throw new Error("output must be a path string or boolean.");
|
|
@@ -4452,8 +4596,10 @@ export function prepareWorkflowLaunchParams(
|
|
|
4452
4596
|
message: typeof childParams.task === "string" ? childParams.task.trim() : "",
|
|
4453
4597
|
workflowParentRunId: parentWorkflowRunId,
|
|
4454
4598
|
workflowKey,
|
|
4599
|
+
...(options.outputClaimPath ? { workflowOutputClaimPath: options.outputClaimPath } : {}),
|
|
4455
4600
|
...(lane ? { lane } : {}),
|
|
4456
4601
|
...(worktree !== undefined ? { worktree: worktree as boolean } : {}),
|
|
4602
|
+
...(baseRef !== undefined ? { baseRef: baseRef as string } : {}),
|
|
4457
4603
|
...(outputSchema !== undefined ? { outputSchema: outputSchema as JsonSchemaObject } : {}),
|
|
4458
4604
|
...(agentContract !== undefined ? { agentContract: agentContract as AgentContract } : {}),
|
|
4459
4605
|
...(acceptance !== undefined ? { acceptance: acceptance as AcceptanceInput } : {}),
|
|
@@ -4469,12 +4615,13 @@ export function prepareWorkflowLaunchParams(
|
|
|
4469
4615
|
};
|
|
4470
4616
|
}
|
|
4471
4617
|
const control = mergeWorkflowControlOverrides(workflowDefaults.control, childParams.control as ControlConfig | undefined);
|
|
4618
|
+
const asyncOmitted = childParams.async === undefined && workflowDefaults.async === undefined;
|
|
4472
4619
|
const launchParams = {
|
|
4473
4620
|
...workflowDefaults,
|
|
4474
|
-
|
|
4621
|
+
...(options.externalAsyncRequired === true && asyncOmitted ? { async: true } : {}),
|
|
4475
4622
|
...childParams,
|
|
4476
4623
|
...(control !== undefined ? { control } : {}),
|
|
4477
|
-
...(
|
|
4624
|
+
...(asyncOmitted ? { workflowAwaitAsync: true } : {}),
|
|
4478
4625
|
...(options.missionDetached ? { mission: false } : {}),
|
|
4479
4626
|
workflowParentRunId: parentWorkflowRunId,
|
|
4480
4627
|
workflowKey,
|
|
@@ -4612,14 +4759,14 @@ function applyWorkflowLaneTrace(graph: WorkflowGraphSnapshot, trace: WorkflowScr
|
|
|
4612
4759
|
else delete graph.currentNodeId;
|
|
4613
4760
|
}
|
|
4614
4761
|
|
|
4615
|
-
function
|
|
4762
|
+
function workflowProgressUpdate(
|
|
4616
4763
|
runId: string,
|
|
4617
4764
|
chatProgress: WorkflowChatProgressProjection,
|
|
4618
4765
|
workflow: NonNullable<Details["workflow"]>,
|
|
4619
4766
|
workflowChildren?: Details["workflowChildren"],
|
|
4620
|
-
preflight?: import("../../shared/types.ts").
|
|
4621
|
-
): AgentToolResult<Details>
|
|
4622
|
-
|
|
4767
|
+
preflight?: import("../../shared/types.ts").WorkflowPreflight,
|
|
4768
|
+
): AgentToolResult<Details> {
|
|
4769
|
+
// chatProgress controls the TUI projection, not transport-level progress.
|
|
4623
4770
|
return {
|
|
4624
4771
|
content: [{ type: "text", text: "Workflow running." }],
|
|
4625
4772
|
details: { mode: "workflow", runId, results: [], workflow, ...(preflight ? { preflight } : {}), ...(workflowChildren ? { workflowChildren } : {}), chatProgress },
|
|
@@ -4695,6 +4842,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4695
4842
|
signal: AbortSignal,
|
|
4696
4843
|
ctx: ExtensionContext,
|
|
4697
4844
|
) => Promise<AgentToolResult<Details>>;
|
|
4845
|
+
/** Scheduled state visible to the current runtime supervisor owner only. */
|
|
4846
|
+
getCurrentSupervisorOwnerStates: () => Iterable<SubagentState>;
|
|
4698
4847
|
} {
|
|
4699
4848
|
const delegatedThinkingOverrides = new WeakMap<object, AgentConfig["thinking"]>();
|
|
4700
4849
|
const delegatedZeroToolBudgets = new WeakSet<object>();
|
|
@@ -4703,7 +4852,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4703
4852
|
const workflowResourcePermits = new WeakMap<object, WorkflowResourcePermit>();
|
|
4704
4853
|
const workflowPermitContexts = new WeakMap<object, { root: WorkflowChildPermit } | { child: WorkflowChildPermitContext }>();
|
|
4705
4854
|
const warnedArtifactPackageDirs = new Set<string>();
|
|
4706
|
-
const scheduledOwnerExecutors = new Map<string, ReturnType<typeof createSubagentExecutor>>();
|
|
4855
|
+
const scheduledOwnerExecutors = new Map<string | null, Map<string, { state: SubagentState; executor: ReturnType<typeof createSubagentExecutor> }>>();
|
|
4707
4856
|
const execute = async (
|
|
4708
4857
|
_id: string,
|
|
4709
4858
|
params: SubagentParamsLike,
|
|
@@ -4714,6 +4863,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4714
4863
|
parentModelOverride?: ParentModel | null,
|
|
4715
4864
|
): Promise<AgentToolResult<Details>> => {
|
|
4716
4865
|
const workflowLaunchObserver = workflowLaunchObservers.get(params);
|
|
4866
|
+
const inheritedUsageBudget = workflowOwnedUsageBudgets.get(params);
|
|
4717
4867
|
const delegatedThinkingOverride = delegatedThinkingOverrides.get(params);
|
|
4718
4868
|
const allowZeroToolBudget = delegatedZeroToolBudgets.has(params);
|
|
4719
4869
|
const delegatedExecution = delegatedExecutions.has(params);
|
|
@@ -4731,7 +4881,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4731
4881
|
let requestParams = normalizedGate.params;
|
|
4732
4882
|
const capacityOverrideError = validateWorkflowCapacityOverrides(requestParams);
|
|
4733
4883
|
if (capacityOverrideError) return buildRequestedModeError(requestParams, capacityOverrideError);
|
|
4734
|
-
let workflowPreflight: import("../../shared/types.ts").
|
|
4884
|
+
let workflowPreflight: import("../../shared/types.ts").WorkflowPreflight | undefined;
|
|
4735
4885
|
try {
|
|
4736
4886
|
if (requestParams.preflight !== undefined && requestParams.workflowScript === undefined && requestParams.workflowScriptPath === undefined) {
|
|
4737
4887
|
throw new Error("preflight requires workflowScript or workflowScriptPath.");
|
|
@@ -4762,7 +4912,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4762
4912
|
}
|
|
4763
4913
|
const normalizedAction = typeof requestParams.action === "string" ? requestParams.action.trim() : requestParams.action;
|
|
4764
4914
|
if (normalizedAction === "resume" && requestParams.extensionBindings !== undefined) return buildRequestedModeError(requestParams, "extensionBindings is not supported with action='resume'; resume uses the original retained child binding.");
|
|
4765
|
-
let workflowResource: { permit: WorkflowResourcePermit; provenance:
|
|
4915
|
+
let workflowResource: { permit: WorkflowResourcePermit; provenance: WorkflowResourceProvenance; authority: WorkflowResourceAuthority } | undefined;
|
|
4766
4916
|
if (workflowResourcePermit) {
|
|
4767
4917
|
if (typeof requestParams.workflowScript !== "string") return buildRequestedModeError(requestParams, "Resolved workflow resource is missing its workflow script.");
|
|
4768
4918
|
const consumed = consumeWorkflowResourcePermit(workflowResourcePermit, requestParams.workflowScript);
|
|
@@ -4770,8 +4920,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4770
4920
|
workflowResource = { permit: workflowResourcePermit, ...consumed };
|
|
4771
4921
|
}
|
|
4772
4922
|
if (requestParams.workflowScript !== undefined && normalizedAction === undefined) {
|
|
4923
|
+
const acceptanceErrors = validateAcceptanceInput(requestParams.acceptance);
|
|
4924
|
+
if (acceptanceErrors.length > 0) return buildRequestedModeError(requestParams, acceptanceErrors.join(" "));
|
|
4925
|
+
const foregroundWorkflowRunId = encodeIndexSegment(_id);
|
|
4773
4926
|
if (delegatedWorkflowPermit) {
|
|
4774
|
-
const permitError = validateWorkflowChildPermitRoot(delegatedWorkflowPermit,
|
|
4927
|
+
const permitError = validateWorkflowChildPermitRoot(delegatedWorkflowPermit, foregroundWorkflowRunId);
|
|
4775
4928
|
if (permitError) return buildRequestedModeError(requestParams, permitError);
|
|
4776
4929
|
if (requestParams.async !== false) return buildRequestedModeError(requestParams, "Workflow child permit supports foreground workflow roots only; set async:false.");
|
|
4777
4930
|
}
|
|
@@ -4818,11 +4971,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4818
4971
|
const scriptFirstLine = requestParams.workflowScript.split(/\r?\n/).map((line) => line.trim()).find(Boolean) || "Workflow";
|
|
4819
4972
|
const boundedScriptPreview = scriptFirstLine.length > 100 ? `${scriptFirstLine.slice(0, 97)}...` : scriptFirstLine;
|
|
4820
4973
|
const derivedObjective = previewAgent ? `Workflow: ${previewAgent}` : boundedScriptPreview;
|
|
4821
|
-
const workflowDepth = checkSubagentDepth(deps.config.maxSubagentDepth).depth;
|
|
4974
|
+
const workflowDepth = checkSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime).depth;
|
|
4822
4975
|
const asyncWorkflow = requestParams.async !== false;
|
|
4823
4976
|
const topLevelAsyncWorkflow = asyncWorkflow
|
|
4824
4977
|
&& workflowDepth === 0
|
|
4825
|
-
&& !
|
|
4978
|
+
&& !inheritedNestedRoute(deps)
|
|
4826
4979
|
&& !requestParams.workflowParentRunId;
|
|
4827
4980
|
const workflowRunId = asyncWorkflow ? randomUUID() : undefined;
|
|
4828
4981
|
let workflowCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
@@ -4847,7 +5000,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4847
5000
|
let workflowFanoutBudget: RunFanoutBudgetDescriptor;
|
|
4848
5001
|
try {
|
|
4849
5002
|
workflowFanoutBudget = requestParams.runFanoutBudget
|
|
4850
|
-
??
|
|
5003
|
+
?? inheritedRunFanoutBudget(deps)
|
|
4851
5004
|
?? createRunFanoutBudget(_id, requestParams.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
|
|
4852
5005
|
} catch (error) {
|
|
4853
5006
|
workflowCapacity?.rollback();
|
|
@@ -4946,18 +5099,31 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4946
5099
|
onSuccess: (_filePath, payload) => { indexedState = (payload as { state: AsyncStatus["state"] }).state; },
|
|
4947
5100
|
onError: (error, filePath) => console.error(`Failed to update async workflow index '${filePath}':`, error),
|
|
4948
5101
|
});
|
|
4949
|
-
const queueActiveRunIndex = (): void => {
|
|
4950
|
-
const state =
|
|
5102
|
+
const queueActiveRunIndex = (snapshot: AsyncStatus = status): void => {
|
|
5103
|
+
const state = snapshot.state;
|
|
4951
5104
|
if (indexedState === state && indexPersistence.pendingCount() === 0) return;
|
|
4952
|
-
indexPersistence.write(asyncDir, { state, toolCallId:
|
|
5105
|
+
indexPersistence.write(asyncDir, { state, toolCallId: snapshot.toolCallId }, (_filePath, payload) => {
|
|
4953
5106
|
const indexPayload = payload as { state: AsyncStatus["state"]; toolCallId?: string };
|
|
4954
5107
|
updateActiveRunIndex(asyncDir, indexPayload.state, indexPayload.toolCallId, { retryCapacityErrors: true });
|
|
4955
5108
|
});
|
|
4956
5109
|
};
|
|
5110
|
+
let pendingResultPublication: Promise<boolean> | undefined;
|
|
5111
|
+
let settleResultPublication: ((published: boolean) => void) | undefined;
|
|
5112
|
+
const reportResultWriteFailure = (error: unknown): void => {
|
|
5113
|
+
const message = `Failed to write async workflow result ${resultPath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
5114
|
+
console.error(message, error);
|
|
5115
|
+
appendWorkflowEvent({ type: "subagent.workflow.result_write_failed", error: message });
|
|
5116
|
+
};
|
|
4957
5117
|
const runPersistence = createCapacityResilientJsonWriter({
|
|
4958
5118
|
keepAlive: true,
|
|
4959
|
-
onSuccess: (filePath) => {
|
|
4960
|
-
|
|
5119
|
+
onSuccess: (filePath, payload) => {
|
|
5120
|
+
if (filePath === statusPath) queueActiveRunIndex(payload as AsyncStatus);
|
|
5121
|
+
if (filePath === resultPath) settleResultPublication?.(true);
|
|
5122
|
+
},
|
|
5123
|
+
onError: (error, filePath) => {
|
|
5124
|
+
if (filePath === resultPath) { reportResultWriteFailure(error); settleResultPublication?.(false); }
|
|
5125
|
+
else console.error(`Failed to persist async workflow state '${filePath}':`, error);
|
|
5126
|
+
},
|
|
4961
5127
|
write: (filePath, payload) => filePath === resultPath
|
|
4962
5128
|
? writeAsyncResultFile(filePath, payload as Record<string, unknown>)
|
|
4963
5129
|
: writeAtomicJson(filePath, payload),
|
|
@@ -4985,7 +5151,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4985
5151
|
queueActiveRunIndex();
|
|
4986
5152
|
} else if (options.tolerateStatusWriteFailure) {
|
|
4987
5153
|
try {
|
|
4988
|
-
runPersistence.write(statusPath, status);
|
|
5154
|
+
runPersistence.write(statusPath, { ...status });
|
|
4989
5155
|
statusPersistenceDegraded = false;
|
|
4990
5156
|
} catch (error) {
|
|
4991
5157
|
const message = `Failed to persist async workflow state ${statusPath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -5000,7 +5166,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5000
5166
|
}
|
|
5001
5167
|
}
|
|
5002
5168
|
} else {
|
|
5003
|
-
runPersistence.write(statusPath, status);
|
|
5169
|
+
runPersistence.write(statusPath, { ...status });
|
|
5004
5170
|
}
|
|
5005
5171
|
if (liveJob) {
|
|
5006
5172
|
liveJob.status = status.state;
|
|
@@ -5028,12 +5194,15 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5028
5194
|
};
|
|
5029
5195
|
const writeWorkflowResult = (payload: Record<string, unknown>): boolean => {
|
|
5030
5196
|
try {
|
|
5197
|
+
let resultPublished = false;
|
|
5198
|
+
settleResultPublication = (published) => { resultPublished = published; };
|
|
5199
|
+
pendingResultPublication = undefined;
|
|
5031
5200
|
runPersistence.write(resultPath, payload);
|
|
5201
|
+
// Only capacity deferral yields; ordinary publication remains synchronous.
|
|
5202
|
+
if (!resultPublished) pendingResultPublication = new Promise<boolean>((resolve) => { settleResultPublication = resolve; });
|
|
5032
5203
|
return true;
|
|
5033
5204
|
} catch (error) {
|
|
5034
|
-
|
|
5035
|
-
console.error(message, error);
|
|
5036
|
-
appendWorkflowEvent({ type: "subagent.workflow.result_write_failed", error: message });
|
|
5205
|
+
reportResultWriteFailure(error);
|
|
5037
5206
|
return false;
|
|
5038
5207
|
}
|
|
5039
5208
|
};
|
|
@@ -5073,7 +5242,148 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5073
5242
|
return { content: [{ type: "text", text: `Failed to create async workflow storage: ${error instanceof Error ? error.message : String(error)}` }], isError: true, details: { mode: "workflow", results: [] } };
|
|
5074
5243
|
}
|
|
5075
5244
|
appendWorkflowEvent({ type: "subagent.workflow.started" });
|
|
5245
|
+
// The runner does not execute workflow scripts: this active owner must consume its own
|
|
5246
|
+
// durable steer inbox, deliver through in-process child controls, and persist receipts.
|
|
5247
|
+
const emitWorkflowSteerEvent = (type: string, requestId: string, index?: number, extra: Record<string, unknown> = {}): void => {
|
|
5248
|
+
appendWorkflowEvent({ type, requestId, ...(index !== undefined ? { index } : {}), ...extra });
|
|
5249
|
+
};
|
|
5250
|
+
const recordWorkflowSteerTargets = (request: SteerRequest, targets: Array<{ index: number; state: SteeringTargetState; reason?: string }>): void => {
|
|
5251
|
+
status.steering ??= createSteeringStatus();
|
|
5252
|
+
recordSteeringRequest(steeringStatus(status), {
|
|
5253
|
+
id: request.id,
|
|
5254
|
+
requestedAt: request.ts,
|
|
5255
|
+
...(request.source ? { source: request.source } : {}),
|
|
5256
|
+
message: request.message,
|
|
5257
|
+
targets,
|
|
5258
|
+
});
|
|
5259
|
+
};
|
|
5260
|
+
const explicitWorkflowSteerIndexes = (request: SteerRequest): number[] | undefined => request.targetIndex !== undefined ? [request.targetIndex] : request.targetIndexes;
|
|
5261
|
+
const failWorkflowSteer = (request: SteerRequest, indexes: number[], reason: string): void => {
|
|
5262
|
+
const targets = indexes.map((index) => ({ index, state: "failed" as const, reason }));
|
|
5263
|
+
recordWorkflowSteerTargets(request, targets);
|
|
5264
|
+
emitWorkflowSteerEvent("subagent.steer.requested", request.id, undefined, { targets });
|
|
5265
|
+
for (const index of indexes) emitWorkflowSteerEvent("subagent.steer.failed", request.id, index, { reason });
|
|
5266
|
+
persist({ tolerateStatusWriteFailure: true });
|
|
5267
|
+
};
|
|
5268
|
+
const pendingWorkflowSteers = new Set<(state: "delivered" | "queued" | "failed", reason?: string) => void>();
|
|
5269
|
+
const deliverWorkflowSteerRequest = (request: SteerRequest): void => {
|
|
5270
|
+
let indexes = explicitWorkflowSteerIndexes(request);
|
|
5271
|
+
if (status.state !== "running") {
|
|
5272
|
+
failWorkflowSteer(request, indexes ?? [0], `run became ${status.state} before steering request was consumed`);
|
|
5273
|
+
return;
|
|
5274
|
+
}
|
|
5275
|
+
const liveControls = [...deps.state.foregroundControls.values()].filter((candidate) => candidate.parentWorkflowRunId === workflowRunId && (candidate.activeChildren?.size ?? 0) > 0);
|
|
5276
|
+
if (indexes === undefined) {
|
|
5277
|
+
if (liveControls.length !== 1) {
|
|
5278
|
+
failWorkflowSteer(request, [0], liveControls.length > 1
|
|
5279
|
+
? `workflow has ${liveControls.length} live children; steer a child run id instead`
|
|
5280
|
+
: "workflow child is not live in this process");
|
|
5281
|
+
return;
|
|
5282
|
+
}
|
|
5283
|
+
const index = status.steps?.findIndex((step) => !!step.workflowKey && step.workflowKey === liveControls[0]!.workflowKey) ?? -1;
|
|
5284
|
+
if (index < 0) {
|
|
5285
|
+
failWorkflowSteer(request, [0], "live workflow child has no projected step");
|
|
5286
|
+
return;
|
|
5287
|
+
}
|
|
5288
|
+
indexes = [index];
|
|
5289
|
+
}
|
|
5290
|
+
// Normalize and record the whole set once: recordSteeringRequest is idempotent by id.
|
|
5291
|
+
// Explicit indexes resolve only by their projected key, never by a live sibling.
|
|
5292
|
+
const resolved = indexes.map((index) => {
|
|
5293
|
+
const step = status.steps?.[index];
|
|
5294
|
+
const control = step?.workflowKey ? liveControls.find((candidate) => candidate.workflowKey === step.workflowKey) : undefined;
|
|
5295
|
+
return { index, control, reason: !step ? "child index out of range" : !control ? "workflow child is not live in this process" : undefined };
|
|
5296
|
+
});
|
|
5297
|
+
const targets = resolved.map(({ index, reason }) => ({ index, state: reason ? "failed" as const : "routed" as const, ...(reason ? { reason } : {}) }));
|
|
5298
|
+
recordWorkflowSteerTargets(request, targets);
|
|
5299
|
+
emitWorkflowSteerEvent("subagent.steer.requested", request.id, undefined, { targets });
|
|
5300
|
+
persist({ tolerateStatusWriteFailure: true });
|
|
5301
|
+
for (const { index, control, reason } of resolved) {
|
|
5302
|
+
if (!control) {
|
|
5303
|
+
emitWorkflowSteerEvent("subagent.steer.failed", request.id, index, { reason });
|
|
5304
|
+
continue;
|
|
5305
|
+
}
|
|
5306
|
+
emitWorkflowSteerEvent("subagent.steer.routed", request.id, index);
|
|
5307
|
+
const applyWorkflowSteerDelivery = (state: "delivered" | "queued" | "failed", reason?: string): void => {
|
|
5308
|
+
// Shutdown settles unresolved receipts synchronously; late SDK callbacks cannot rewrite them.
|
|
5309
|
+
if (!pendingWorkflowSteers.has(applyWorkflowSteerDelivery)) return;
|
|
5310
|
+
// Queuing is not confirmation of delivery: retain ownership until terminal settlement.
|
|
5311
|
+
if (state !== "queued") pendingWorkflowSteers.delete(applyWorkflowSteerDelivery);
|
|
5312
|
+
const now = Date.now();
|
|
5313
|
+
const steering = steeringStatus(status);
|
|
5314
|
+
// This callback owns a pending target, even after bounded display history
|
|
5315
|
+
// evicts its request. Account independently; queued still represents one pending target.
|
|
5316
|
+
if (state !== "queued") steering.pending = Math.max(0, steering.pending - 1);
|
|
5317
|
+
if (state === "delivered") {
|
|
5318
|
+
steering.delivered++;
|
|
5319
|
+
steering.lastDeliveredAt = now;
|
|
5320
|
+
} else if (state === "failed") steering.failed++;
|
|
5321
|
+
const receipt = steering.recent.find((candidate) => candidate.id === request.id)?.targets.find((target) => target.index === index);
|
|
5322
|
+
if (receipt) {
|
|
5323
|
+
receipt.state = state;
|
|
5324
|
+
if (state === "delivered") receipt.deliveredAt = now;
|
|
5325
|
+
if (state === "failed") receipt.failedAt = now;
|
|
5326
|
+
if (reason) receipt.reason = reason;
|
|
5327
|
+
}
|
|
5328
|
+
if (state === "failed") {
|
|
5329
|
+
const failedStep = status.steps?.[index];
|
|
5330
|
+
if (failedStep) failedStep.activityState = "needs_attention";
|
|
5331
|
+
status.activityState = "needs_attention";
|
|
5332
|
+
}
|
|
5333
|
+
emitWorkflowSteerEvent(`subagent.steer.${state}`, request.id, index, state === "failed" ? { reason } : { deliveryStatus: state, message: request.message });
|
|
5334
|
+
persist({ tolerateStatusWriteFailure: true });
|
|
5335
|
+
};
|
|
5336
|
+
pendingWorkflowSteers.add(applyWorkflowSteerDelivery);
|
|
5337
|
+
void steerWorkflowForegroundTarget({
|
|
5338
|
+
target: { control, workflowRunId, sourceRunId: workflowRunId },
|
|
5339
|
+
message: request.message,
|
|
5340
|
+
...(request.mode ? { mode: request.mode } : {}),
|
|
5341
|
+
}).then(
|
|
5342
|
+
(result) => {
|
|
5343
|
+
const target = result.details.steering?.targets[0];
|
|
5344
|
+
applyWorkflowSteerDelivery(target?.state === "delivered" ? "delivered" : target?.state === "queued" ? "queued" : "failed", target?.reason ?? (target ? undefined : "steering produced no target receipt"));
|
|
5345
|
+
},
|
|
5346
|
+
(error) => applyWorkflowSteerDelivery("failed", error instanceof Error ? error.message : String(error)),
|
|
5347
|
+
);
|
|
5348
|
+
}
|
|
5349
|
+
};
|
|
5350
|
+
let disposeWorkflowSteerInbox: (() => void) | undefined;
|
|
5351
|
+
try {
|
|
5352
|
+
disposeWorkflowSteerInbox = watchAsyncControlInbox(asyncDir, {
|
|
5353
|
+
onSteer: deliverWorkflowSteerRequest,
|
|
5354
|
+
onError: (error, phase, request) => {
|
|
5355
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
5356
|
+
appendWorkflowEvent({ type: "subagent.workflow.steer_inbox_unavailable", phase, error: reason });
|
|
5357
|
+
if (request) failWorkflowSteer(request, explicitWorkflowSteerIndexes(request) ?? [0], `steer consumer failed: ${reason}`);
|
|
5358
|
+
},
|
|
5359
|
+
});
|
|
5360
|
+
} catch (error) {
|
|
5361
|
+
// Steering is an optional control surface, so a watcher that cannot install must not fail the run.
|
|
5362
|
+
appendWorkflowEvent({ type: "subagent.workflow.steer_inbox_unavailable", error: error instanceof Error ? error.message : String(error) });
|
|
5363
|
+
}
|
|
5364
|
+
deps.activateSupervisorTransport?.();
|
|
5076
5365
|
const { workflowScript, async: _workflowAsync, chatProgress: _chatProgress, ...workflowRequest } = requestParams;
|
|
5366
|
+
let workflowSteerInboxClosed = false;
|
|
5367
|
+
const settleWorkflowSteerInbox = (outcome = status.state): void => {
|
|
5368
|
+
if (workflowSteerInboxClosed) return;
|
|
5369
|
+
workflowSteerInboxClosed = true;
|
|
5370
|
+
disposeWorkflowSteerInbox?.();
|
|
5371
|
+
// Consumed deliveries are not files anymore. Fail unresolved SDK operations without awaiting them.
|
|
5372
|
+
for (const settle of pendingWorkflowSteers) settle("failed", `run became ${outcome} before steering delivery settled; delivery unconfirmed`);
|
|
5373
|
+
try {
|
|
5374
|
+
closeSteerInbox(asyncDir, outcome);
|
|
5375
|
+
// Separately drain requests that never reached the consumer.
|
|
5376
|
+
const undelivered = consumeSteerRequests(asyncDir, undefined, (error) => {
|
|
5377
|
+
appendWorkflowEvent({ type: "subagent.workflow.steer_inbox_unavailable", phase: "drain", error: error instanceof Error ? error.message : String(error) });
|
|
5378
|
+
});
|
|
5379
|
+
for (const request of undelivered) failWorkflowSteer(request, explicitWorkflowSteerIndexes(request) ?? [0], `run became ${outcome} before steering request was consumed`);
|
|
5380
|
+
} catch (error) {
|
|
5381
|
+
appendWorkflowEvent({ type: "subagent.workflow.steer_inbox_unavailable", phase: "close", error: error instanceof Error ? error.message : String(error) });
|
|
5382
|
+
}
|
|
5383
|
+
// Commit receipts while run status is still nonterminal. Result/index publication
|
|
5384
|
+
// can fail next; teardown must not publish the subsequently computed terminal status.
|
|
5385
|
+
persist();
|
|
5386
|
+
};
|
|
5077
5387
|
void Promise.resolve().then(async () => {
|
|
5078
5388
|
const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
|
|
5079
5389
|
const workflowResults: SingleResult[] = [];
|
|
@@ -5081,7 +5391,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5081
5391
|
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, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = workflowRequest;
|
|
5082
5392
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
|
|
5083
5393
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
|
|
5084
|
-
const workflowAggregateOutputPath =
|
|
5394
|
+
const workflowAggregateOutputPath = resolveSingleOutputPath(workflowOutput, parentCwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, workflowRunId));
|
|
5085
5395
|
const claimedOutputPaths = new Map<string, string>();
|
|
5086
5396
|
const childOutputOverrides = new Map<string, string>();
|
|
5087
5397
|
const childOutputClaimPaths = new Map<string, string>();
|
|
@@ -5206,6 +5516,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5206
5516
|
globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
|
|
5207
5517
|
timeoutMs: timeout,
|
|
5208
5518
|
signal: controller.signal,
|
|
5519
|
+
continueAfterAbortWhenChildrenSettled: (abortError) => {
|
|
5520
|
+
if (abortError.message !== "Workflow stopped because the extension session was replaced or reloaded.") return false;
|
|
5521
|
+
const activeAsyncChild = [...(deps.state.asyncJobs?.values() ?? [])].some((job) => job.parentWorkflowRunId === workflowRunId && (job.status === "queued" || job.status === "running"));
|
|
5522
|
+
const activeForegroundChild = [...deps.state.foregroundControls.values()].some((control) => control.parentWorkflowRunId === workflowRunId && (control.activeChildren?.size ?? 0) > 0);
|
|
5523
|
+
return !activeAsyncChild && !activeForegroundChild;
|
|
5524
|
+
},
|
|
5209
5525
|
registerStopChild: (stop) => {
|
|
5210
5526
|
if (stop) deps.state.workflowChildStops?.set(workflowRunId, stop);
|
|
5211
5527
|
else deps.state.workflowChildStops?.delete(workflowRunId);
|
|
@@ -5260,6 +5576,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5260
5576
|
deps.asyncByDefault,
|
|
5261
5577
|
);
|
|
5262
5578
|
preparedChildParams = childRequest;
|
|
5579
|
+
if (workflowUsageBudget.budget) workflowOwnedUsageBudgets.set(childRequest, workflowUsageBudget.budget);
|
|
5263
5580
|
workflowLaunchObservers.set(childRequest, (launch) => {
|
|
5264
5581
|
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
5265
5582
|
if (step) {
|
|
@@ -5308,7 +5625,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5308
5625
|
if (child.runId) workflowChildRunIds.set(key, child.runId);
|
|
5309
5626
|
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
5310
5627
|
if (step) {
|
|
5311
|
-
step.async = Boolean(result.details.asyncId || result.details.asyncDir);
|
|
5628
|
+
step.async = step.async === true || Boolean(result.details.asyncId || result.details.asyncDir);
|
|
5312
5629
|
if (child.runId) step.runId = child.runId;
|
|
5313
5630
|
if (child.lane) step.lane = child.lane;
|
|
5314
5631
|
}
|
|
@@ -5330,7 +5647,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5330
5647
|
return child;
|
|
5331
5648
|
},
|
|
5332
5649
|
status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession, workflowParentModel)),
|
|
5333
|
-
resolveResume: (reference) =>
|
|
5650
|
+
resolveResume: (reference, _signal, index) => resolveWorkflowResume(reference, deps, ctx.sessionManager.getSessionFile() ?? null, index),
|
|
5334
5651
|
steer: (key, message, options, workflowSignal) => steerWorkflowChildByKey({ state: deps.state, workflowRunId, key, message, options, signal: workflowSignal, resolveRunId: () => workflowChildRunIds.get(key) }),
|
|
5335
5652
|
});
|
|
5336
5653
|
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, workflow.trace, { settled: true });
|
|
@@ -5341,16 +5658,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5341
5658
|
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, summary, producedChildOutputPaths);
|
|
5342
5659
|
const resultSummary = appendWorkflowOutputWarning(summary, outputWarning);
|
|
5343
5660
|
const workflowUsage = sumResultsUsage(workflowResults);
|
|
5661
|
+
settleWorkflowSteerInbox("complete");
|
|
5344
5662
|
const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children, steps: status.steps });
|
|
5345
5663
|
status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
|
|
5346
5664
|
const receipt = terminalWorkflowReceipt(workflowRunId, "complete", workflow.children, workflowChildren, undefined, validHostStepNodes(status.workflowGraph), workflowResource?.provenance);
|
|
5665
|
+
delete status.workflowReceiptPath;
|
|
5347
5666
|
let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
|
|
5348
5667
|
try {
|
|
5349
5668
|
workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
|
|
5669
|
+
status.workflowReceiptPath = workflowReceipt.path;
|
|
5350
5670
|
} catch (receiptError) {
|
|
5351
5671
|
appendWorkflowEvent({ type: "subagent.workflow.receipt_write_failed", error: `Failed to persist async workflow receipt: ${receiptError instanceof Error ? receiptError.message : String(receiptError)}` });
|
|
5352
5672
|
}
|
|
5353
5673
|
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;
|
|
5674
|
+
if (pendingResultPublication && !await pendingResultPublication) return;
|
|
5354
5675
|
persist();
|
|
5355
5676
|
deps.refreshResultDelivery?.();
|
|
5356
5677
|
persistClosed = true;
|
|
@@ -5374,10 +5695,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5374
5695
|
}
|
|
5375
5696
|
}
|
|
5376
5697
|
const workflowState = state === "paused" ? "paused" : state === "stopped" ? "stopped" : "failed";
|
|
5698
|
+
settleWorkflowSteerInbox(state);
|
|
5377
5699
|
const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState, inventoryComplete: true, trace: partial.trace, children: partial.children, steps: status.steps });
|
|
5378
5700
|
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, partial.trace, { settled: true });
|
|
5379
5701
|
const finalPreflightTrace = annotateWorkflowPreflightTrace(partial.trace, workflowPreflight);
|
|
5380
|
-
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, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren });
|
|
5702
|
+
status = compactOptional<AsyncStatus>({ ...status, state, stopped: stopped || undefined, activityState: pauseForDetached || status.activityState === "needs_attention" ? "needs_attention" : undefined, error: workflowFailureMessage(error, workflowRunId, partial.children), endedAt: Date.now(), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren });
|
|
5381
5703
|
if (pauseForDetached) {
|
|
5382
5704
|
const promoted = promotePausedWorkflowIfSettled(status);
|
|
5383
5705
|
if (promoted) status = promoted;
|
|
@@ -5390,18 +5712,27 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5390
5712
|
const receiptState: WorkflowReceiptState = status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
|
|
5391
5713
|
const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
|
|
5392
5714
|
const receipt = terminalWorkflowReceipt(workflowRunId, receiptState, partial.children, workflowChildren, terminalOutcome, validHostStepNodes(status.workflowGraph), workflowResource?.provenance);
|
|
5715
|
+
delete status.workflowReceiptPath;
|
|
5393
5716
|
let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
|
|
5394
5717
|
try {
|
|
5395
5718
|
workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
|
|
5719
|
+
status.workflowReceiptPath = workflowReceipt.path;
|
|
5396
5720
|
} catch (receiptError) {
|
|
5397
5721
|
appendWorkflowEvent({ type: "subagent.workflow.receipt_write_failed", error: `Failed to persist async workflow receipt: ${receiptError instanceof Error ? receiptError.message : String(receiptError)}` });
|
|
5398
5722
|
}
|
|
5399
5723
|
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;
|
|
5724
|
+
if (pendingResultPublication && !await pendingResultPublication) return;
|
|
5400
5725
|
persist();
|
|
5401
5726
|
deps.refreshResultDelivery?.();
|
|
5402
5727
|
persistClosed = true;
|
|
5403
5728
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(terminalOutcome ? { terminalOutcome } : {}), ...(status.error ? { error: status.error } : {}), ...(status.activityState ? { activityState: status.activityState } : {}) });
|
|
5404
5729
|
} finally {
|
|
5730
|
+
// Idempotent cleanup only: a failed result/index write must not authorize terminal status.
|
|
5731
|
+
try {
|
|
5732
|
+
settleWorkflowSteerInbox();
|
|
5733
|
+
} catch (error) {
|
|
5734
|
+
console.error(`Failed to close async workflow steer inbox '${asyncDir}':`, error);
|
|
5735
|
+
}
|
|
5405
5736
|
persistClosed = true;
|
|
5406
5737
|
deps.state.workflowControllers?.delete(workflowRunId);
|
|
5407
5738
|
deps.state.workflowChildStops?.delete(workflowRunId);
|
|
@@ -5417,7 +5748,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5417
5748
|
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, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = requestParams;
|
|
5418
5749
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
|
|
5419
5750
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
|
|
5420
|
-
const workflowAggregateOutputPath =
|
|
5751
|
+
const workflowAggregateOutputPath = resolveSingleOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, foregroundWorkflowRunId));
|
|
5421
5752
|
const claimedOutputPaths = new Map<string, string>();
|
|
5422
5753
|
const childOutputOverrides = new Map<string, string>();
|
|
5423
5754
|
const childOutputClaimPaths = new Map<string, string>();
|
|
@@ -5427,7 +5758,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5427
5758
|
const runHostCommand = workflowHostCommandRunner({
|
|
5428
5759
|
workflowCwd,
|
|
5429
5760
|
artifactsDir: workflowArtifactsDir,
|
|
5430
|
-
workflowRunId:
|
|
5761
|
+
workflowRunId: foregroundWorkflowRunId,
|
|
5431
5762
|
claimedOutputPaths,
|
|
5432
5763
|
producedOutputPaths: producedChildOutputPaths,
|
|
5433
5764
|
...(workflowResource ? { authorize: (key, params) => authorizeWorkflowResourceHost(workflowResource!.permit, key, params.command) } : {}),
|
|
@@ -5437,19 +5768,26 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5437
5768
|
: workflowResource
|
|
5438
5769
|
? workflowResource.authority.host ? runHostCommand : undefined
|
|
5439
5770
|
: publicExecution ? undefined : runHostCommand;
|
|
5440
|
-
const workflowHostSteps = new Map<string,
|
|
5771
|
+
const workflowHostSteps = new Map<string, HostStepNode>();
|
|
5441
5772
|
let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [], ...(workflowResource ? { resource: workflowResource.provenance } : {}) };
|
|
5442
|
-
|
|
5773
|
+
const childProgress = new Map<string, ReturnType<typeof workflowChildProgress>>();
|
|
5774
|
+
let activityTimer: ReturnType<typeof setTimeout> | undefined;
|
|
5775
|
+
let lastProgressAt = 0;
|
|
5776
|
+
let workflowProgressClosed = false;
|
|
5443
5777
|
const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
|
|
5444
5778
|
const workflowCapabilityCeiling = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(resolveCurrentSessionId(ctx.sessionManager)));
|
|
5445
5779
|
const sendWorkflowProgress = () => {
|
|
5446
|
-
|
|
5447
|
-
if (
|
|
5780
|
+
if (workflowProgressClosed) return;
|
|
5781
|
+
if (activityTimer) clearTimeout(activityTimer);
|
|
5782
|
+
activityTimer = undefined;
|
|
5783
|
+
lastProgressAt = Date.now();
|
|
5784
|
+
const liveWorkflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "running", inventoryComplete: false, trace: liveWorkflow.trace, progress: childProgress });
|
|
5785
|
+
onUpdate?.(workflowProgressUpdate(foregroundWorkflowRunId, chatProgress, liveWorkflow, liveWorkflowChildren, workflowPreflight));
|
|
5448
5786
|
};
|
|
5449
5787
|
try {
|
|
5450
5788
|
const workflow = await runWorkflowScript({
|
|
5451
5789
|
script: requestParams.workflowScript,
|
|
5452
|
-
...(delegatedWorkflowPermit ? { oneUsePermit: { claim: (key: string) => claimWorkflowChildPermit(delegatedWorkflowPermit,
|
|
5790
|
+
...(delegatedWorkflowPermit ? { oneUsePermit: { claim: (key: string) => claimWorkflowChildPermit(delegatedWorkflowPermit, foregroundWorkflowRunId, key) } } : {}),
|
|
5453
5791
|
globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
|
|
5454
5792
|
timeoutMs: timeout,
|
|
5455
5793
|
signal,
|
|
@@ -5462,11 +5800,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5462
5800
|
const projectedTrace = annotateWorkflowPreflightTrace(trace, workflowPreflight);
|
|
5463
5801
|
const preflightWarnings = workflowPreflightWarnings(workflowPreflight, trace);
|
|
5464
5802
|
liveWorkflow = { ...liveWorkflow, trace: projectedTrace, ...(preflightWarnings.length ? { preflightWarnings } : {}) };
|
|
5465
|
-
|
|
5803
|
+
for (const entry of trace) {
|
|
5804
|
+
if (entry.operation === "run" && ["completed", "failed", "stopped", "detached"].includes(entry.state)) childProgress.delete(entry.key);
|
|
5805
|
+
}
|
|
5466
5806
|
sendWorkflowProgress();
|
|
5467
5807
|
},
|
|
5468
5808
|
admit: (calls) => {
|
|
5469
|
-
const outputClaims = workflowChildOutputClaims({ ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId:
|
|
5809
|
+
const outputClaims = workflowChildOutputClaims({ ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId: foregroundWorkflowRunId, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, state: deps.state, claimedOutputPaths, entries: calls });
|
|
5470
5810
|
if (outputClaims.error) throw new Error(outputClaims.error);
|
|
5471
5811
|
claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
|
|
5472
5812
|
if (outputClaims.claims) applyWorkflowChildOutputClaims(claimedOutputPaths, outputClaims.claims);
|
|
@@ -5486,7 +5826,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5486
5826
|
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)), childParams, deps.state, { state: "partial", reason: "budget_exhausted" });
|
|
5487
5827
|
const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
|
|
5488
5828
|
const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
|
|
5489
|
-
recordMissionWorkflowChild(missionBinding,
|
|
5829
|
+
recordMissionWorkflowChild(missionBinding, foregroundWorkflowRunId, key, {
|
|
5490
5830
|
status: "running",
|
|
5491
5831
|
...(typeof childParams.agent === "string" && childParams.agent.trim() ? { agent: childParams.agent.trim() } : {}),
|
|
5492
5832
|
...(childLabel ? { label: childLabel } : {}),
|
|
@@ -5494,13 +5834,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5494
5834
|
heartbeat: { status: "running", ...(childPhase ? { phase: childPhase } : {}) },
|
|
5495
5835
|
});
|
|
5496
5836
|
let preparedChildParams: SubagentParamsLike | undefined;
|
|
5497
|
-
const result = await runMissionWorkflowChild(missionBinding,
|
|
5837
|
+
const result = await runMissionWorkflowChild(missionBinding, foregroundWorkflowRunId, key, childPhase, () => {
|
|
5498
5838
|
const childRequest = bindMissionWorkflowChildAsyncLaunch(
|
|
5499
|
-
{ ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams, parentWorkflowRunId:
|
|
5839
|
+
{ ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams: delegatedWorkflowPermit ? { ...childParams, async: false } : childParams, parentWorkflowRunId: foregroundWorkflowRunId, 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 },
|
|
5500
5840
|
missionBinding,
|
|
5501
5841
|
deps.asyncByDefault,
|
|
5502
5842
|
);
|
|
5503
5843
|
preparedChildParams = childRequest;
|
|
5844
|
+
if (workflowUsageBudget.budget) workflowOwnedUsageBudgets.set(childRequest, workflowUsageBudget.budget);
|
|
5504
5845
|
if (delegatedWorkflowPermit) {
|
|
5505
5846
|
if (childRequest.async !== false) throw new Error("Workflow child permit supports one foreground child only.");
|
|
5506
5847
|
const childCwd = resolveRequestedCwd(workflowCwd, childRequest.cwd);
|
|
@@ -5508,9 +5849,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5508
5849
|
? resolveAgentName(childRequest.agent, discoverWorkflowAgents(childCwd, resolveExecutionAgentScope(childRequest.agentScope)).agents).agent
|
|
5509
5850
|
: undefined;
|
|
5510
5851
|
if (childAgent?.runner?.type === "external-cli" || childAgent?.runner?.type === "external-job") throw new Error("Workflow child permit supports native Pi children only.");
|
|
5511
|
-
workflowPermitContexts.set(childRequest, { child: { permit: delegatedWorkflowPermit, workflowRunId:
|
|
5852
|
+
workflowPermitContexts.set(childRequest, { child: { permit: delegatedWorkflowPermit, workflowRunId: foregroundWorkflowRunId, childKey: key } });
|
|
5512
5853
|
}
|
|
5513
|
-
workflowLaunchObservers.set(childRequest, (launch) => recordMissionWorkflowChild(missionBinding,
|
|
5854
|
+
workflowLaunchObservers.set(childRequest, (launch) => recordMissionWorkflowChild(missionBinding, foregroundWorkflowRunId, key, {
|
|
5514
5855
|
status: "running",
|
|
5515
5856
|
agent: launch.agent,
|
|
5516
5857
|
...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}),
|
|
@@ -5518,8 +5859,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5518
5859
|
return execute(randomUUID(), childRequest, workflowSignal, (update) => {
|
|
5519
5860
|
const progress = update.details.progress?.[0];
|
|
5520
5861
|
if (!progress) return;
|
|
5862
|
+
if (onUpdate && !workflowProgressClosed) {
|
|
5863
|
+
if (progress.status === "running") childProgress.set(key, workflowChildProgress(progress));
|
|
5864
|
+
else childProgress.delete(key);
|
|
5865
|
+
if (!activityTimer) {
|
|
5866
|
+
const delay = 100 - (Date.now() - lastProgressAt);
|
|
5867
|
+
if (delay <= 0) sendWorkflowProgress();
|
|
5868
|
+
else activityTimer = setTimeout(sendWorkflowProgress, delay);
|
|
5869
|
+
}
|
|
5870
|
+
}
|
|
5521
5871
|
const progressStatus = progress.status === "completed" ? "completed" : progress.status === "failed" ? "failed" : "running";
|
|
5522
|
-
recordMissionWorkflowChild(missionBinding,
|
|
5872
|
+
recordMissionWorkflowChild(missionBinding, foregroundWorkflowRunId, key, {
|
|
5523
5873
|
status: progressStatus,
|
|
5524
5874
|
heartbeat: { status: progressStatus, ...(childPhase ? { phase: childPhase } : {}) },
|
|
5525
5875
|
});
|
|
@@ -5533,7 +5883,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5533
5883
|
const child = workflowChildResult(key, result, preparedChildParams ?? childParams, deps.state);
|
|
5534
5884
|
if (child.runId) workflowChildRunIds.set(key, child.runId);
|
|
5535
5885
|
const childStatus = missionWorkflowChildStatus(result);
|
|
5536
|
-
recordMissionWorkflowChild(missionBinding,
|
|
5886
|
+
recordMissionWorkflowChild(missionBinding, foregroundWorkflowRunId, key, {
|
|
5537
5887
|
status: childStatus,
|
|
5538
5888
|
...(child.runId ? { runId: child.runId } : {}),
|
|
5539
5889
|
...(result.details.results[0]?.agent ? { agent: result.details.results[0].agent } : {}),
|
|
@@ -5545,13 +5895,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5545
5895
|
return child;
|
|
5546
5896
|
},
|
|
5547
5897
|
status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession, workflowParentModel)),
|
|
5548
|
-
resolveResume: (reference) =>
|
|
5549
|
-
steer: (key, message, options, workflowSignal) => steerWorkflowChildByKey({ state: deps.state, workflowRunId:
|
|
5898
|
+
resolveResume: (reference, _signal, index) => resolveWorkflowResume(reference, deps, ctx.sessionManager.getSessionFile() ?? null, index),
|
|
5899
|
+
steer: (key, message, options, workflowSignal) => steerWorkflowChildByKey({ state: deps.state, workflowRunId: foregroundWorkflowRunId, key, message, options, signal: workflowSignal, resolveRunId: () => workflowChildRunIds.get(key) }),
|
|
5550
5900
|
});
|
|
5551
5901
|
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, workflow.trace, { settled: true });
|
|
5552
5902
|
const finalPreflightTrace = annotateWorkflowPreflightTrace(workflow.trace, workflowPreflight);
|
|
5553
|
-
const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId:
|
|
5554
|
-
const receipt = terminalWorkflowReceipt(
|
|
5903
|
+
const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children });
|
|
5904
|
+
const receipt = terminalWorkflowReceipt(foregroundWorkflowRunId, "complete", workflow.children, workflowChildren, undefined, [...workflowHostSteps.values()], workflowResource?.provenance);
|
|
5555
5905
|
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}` : ""}`);
|
|
5556
5906
|
const sections = [
|
|
5557
5907
|
...(workflowPreflight ? [formatWorkflowPreflight(workflowPreflight)] : []),
|
|
@@ -5569,11 +5919,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5569
5919
|
const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
|
|
5570
5920
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
5571
5921
|
content: [{ type: "text", text: displayText }],
|
|
5572
|
-
details: compactOptional<Details>({ mode: "workflow", runId:
|
|
5922
|
+
details: compactOptional<Details>({ mode: "workflow", runId: foregroundWorkflowRunId, results: workflowDetailsResults(workflow.children), ...(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, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
|
|
5573
5923
|
}, workflowFanoutBudget));
|
|
5574
5924
|
} catch (error) {
|
|
5575
5925
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
5576
|
-
const text = workflowFailureMessage(error,
|
|
5926
|
+
const text = workflowFailureMessage(error, foregroundWorkflowRunId, partial.children);
|
|
5577
5927
|
const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, partial.trace, { settled: true });
|
|
5578
5928
|
const finalPreflightTrace = annotateWorkflowPreflightTrace(partial.trace, workflowPreflight);
|
|
5579
5929
|
const traceLines = finalPreflightTrace.map((entry) => `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.warning ? ` — ${entry.warning}` : ""}${entry.error ? ` — ${entry.error}` : ""}`);
|
|
@@ -5590,14 +5940,18 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5590
5940
|
const workflowText = sections.join("\n\n");
|
|
5591
5941
|
const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, workflowText, producedChildOutputPaths);
|
|
5592
5942
|
const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
|
|
5593
|
-
const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId:
|
|
5943
|
+
const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "failed", inventoryComplete: true, trace: partial.trace, children: partial.children });
|
|
5594
5944
|
const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
|
|
5595
|
-
const receipt = terminalWorkflowReceipt(
|
|
5945
|
+
const receipt = terminalWorkflowReceipt(foregroundWorkflowRunId, "failed", partial.children, workflowChildren, terminalOutcome, [...workflowHostSteps.values()], workflowResource?.provenance);
|
|
5596
5946
|
return attachWorkflowMission(withRunFanoutBudget({
|
|
5597
5947
|
content: [{ type: "text", text: displayText }],
|
|
5598
5948
|
isError: true,
|
|
5599
|
-
details: compactOptional<Details>({ mode: "workflow", runId:
|
|
5949
|
+
details: compactOptional<Details>({ mode: "workflow", runId: foregroundWorkflowRunId, results: workflowDetailsResults(partial.children), ...(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, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
|
|
5600
5950
|
}, workflowFanoutBudget));
|
|
5951
|
+
} finally {
|
|
5952
|
+
workflowProgressClosed = true;
|
|
5953
|
+
if (activityTimer) clearTimeout(activityTimer);
|
|
5954
|
+
childProgress.clear();
|
|
5601
5955
|
}
|
|
5602
5956
|
}
|
|
5603
5957
|
const directParams = requestParams;
|
|
@@ -5668,10 +6022,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5668
6022
|
}
|
|
5669
6023
|
if (!confirmed) return { content: [{ type: "text", text: "Worktree discard canceled; preserved worktrees were not changed." }], details: { mode: "management", results: [] } };
|
|
5670
6024
|
try {
|
|
5671
|
-
const
|
|
5672
|
-
|
|
6025
|
+
const manifestPath = path.isAbsolute(paramsWithResolvedCwd.handoffPath) ? paramsWithResolvedCwd.handoffPath : path.resolve(requestCwd, paramsWithResolvedCwd.handoffPath);
|
|
6026
|
+
const discarded = await withWorktreeTransaction(() => discardPreservedWorktrees(
|
|
6027
|
+
manifestPath,
|
|
5673
6028
|
{ kind: decision === "confirm" ? "confirmed" : "policy", ...(deps.config.authorityPolicy ? { policy: deps.config.authorityPolicy } : {}) },
|
|
5674
|
-
);
|
|
6029
|
+
));
|
|
5675
6030
|
return { content: [{ type: "text", text: discarded.text }], details: { mode: "management", results: [] } };
|
|
5676
6031
|
} catch (error) {
|
|
5677
6032
|
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
|
|
@@ -5940,7 +6295,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5940
6295
|
}
|
|
5941
6296
|
return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots, abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })));
|
|
5942
6297
|
}
|
|
5943
|
-
if (paramsWithResolvedCwd.view === "fleet") {
|
|
6298
|
+
if (paramsWithResolvedCwd.view === "fleet" || paramsWithResolvedCwd.view === "transcript") {
|
|
5944
6299
|
return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots, abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })));
|
|
5945
6300
|
}
|
|
5946
6301
|
if (targetRunId) {
|
|
@@ -5949,12 +6304,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5949
6304
|
if (resolved?.kind === "foreground") {
|
|
5950
6305
|
const foreground = getForegroundControl(deps.state, resolved.id);
|
|
5951
6306
|
if (foreground) {
|
|
5952
|
-
if (paramsWithResolvedCwd.view === "transcript") {
|
|
5953
|
-
return withBudget({
|
|
5954
|
-
content: [{ type: "text", text: "Live foreground transcript is already visible in the expanded running subagent result. Persisted session transcript becomes inspectable after the foreground run completes when sessions are enabled." }],
|
|
5955
|
-
details: { mode: "management", results: [] },
|
|
5956
|
-
});
|
|
5957
|
-
}
|
|
5958
6307
|
return withBudget(foregroundStatusResult(foreground));
|
|
5959
6308
|
}
|
|
5960
6309
|
}
|
|
@@ -5964,13 +6313,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5964
6313
|
}
|
|
5965
6314
|
} else if (!hasDirectoryTarget) {
|
|
5966
6315
|
const foreground = getForegroundControl(deps.state, undefined);
|
|
5967
|
-
if (foreground
|
|
5968
|
-
if (foreground && paramsWithResolvedCwd.view === "transcript") {
|
|
5969
|
-
return withBudget({
|
|
5970
|
-
content: [{ type: "text", text: "Live foreground transcript is already visible in the expanded running subagent result. Pass an async run id to inspect a background transcript." }],
|
|
5971
|
-
details: { mode: "management", results: [] },
|
|
5972
|
-
});
|
|
5973
|
-
}
|
|
6316
|
+
if (foreground) return withBudget(foregroundStatusResult(foreground));
|
|
5974
6317
|
}
|
|
5975
6318
|
return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots, abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })));
|
|
5976
6319
|
}
|
|
@@ -5991,9 +6334,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5991
6334
|
const runId = location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir);
|
|
5992
6335
|
const directoryStatus = location.asyncDir ? readStatus(location.asyncDir) : null;
|
|
5993
6336
|
if (directoryStatus?.mode === "workflow") {
|
|
5994
|
-
|
|
5995
|
-
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
5996
|
-
return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
|
|
6337
|
+
return steerWorkflowRun({ state: deps.state, runId, asyncDir: location.asyncDir!, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
|
|
5997
6338
|
}
|
|
5998
6339
|
if (location.asyncDir) {
|
|
5999
6340
|
const unsupported = externalRunnerControlError(location.asyncDir, "steer");
|
|
@@ -6001,6 +6342,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6001
6342
|
}
|
|
6002
6343
|
return steerAsyncRun(compactOptional<Parameters<typeof steerAsyncRun>[0]>({
|
|
6003
6344
|
state: deps.state,
|
|
6345
|
+
findPendingAsks: deps.findPendingAsks,
|
|
6004
6346
|
runId,
|
|
6005
6347
|
message,
|
|
6006
6348
|
mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode),
|
|
@@ -6033,14 +6375,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6033
6375
|
if (resolved?.kind === "foreground") {
|
|
6034
6376
|
const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, childRunId: resolved.id, asyncDirRoot: DIRS.async });
|
|
6035
6377
|
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
6036
|
-
return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index
|
|
6378
|
+
return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index });
|
|
6037
6379
|
}
|
|
6038
6380
|
if (resolved?.kind !== "async") return { content: [{ type: "text", text: `No async run found for '${targetRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6039
6381
|
const resolvedStatus = resolved.location.asyncDir ? readStatus(resolved.location.asyncDir) : null;
|
|
6040
6382
|
if (resolvedStatus?.mode === "workflow") {
|
|
6041
|
-
|
|
6042
|
-
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
6043
|
-
return steerWorkflowForegroundTarget({ target: route.target, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
|
|
6383
|
+
return steerWorkflowRun({ state: deps.state, runId: resolved.id, asyncDir: resolved.location.asyncDir!, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
|
|
6044
6384
|
}
|
|
6045
6385
|
if (resolved.location.asyncDir) {
|
|
6046
6386
|
const unsupported = externalRunnerControlError(resolved.location.asyncDir, "steer");
|
|
@@ -6048,6 +6388,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6048
6388
|
}
|
|
6049
6389
|
return steerAsyncRun(compactOptional<Parameters<typeof steerAsyncRun>[0]>({
|
|
6050
6390
|
state: deps.state,
|
|
6391
|
+
findPendingAsks: deps.findPendingAsks,
|
|
6051
6392
|
runId: resolved.id,
|
|
6052
6393
|
message,
|
|
6053
6394
|
mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode),
|
|
@@ -6119,9 +6460,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6119
6460
|
if (action === "stop") {
|
|
6120
6461
|
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
6121
6462
|
const workflowController = targetRunId ? deps.state.workflowControllers?.get(targetRunId) : undefined;
|
|
6122
|
-
if (workflowController) {
|
|
6463
|
+
if (workflowController && targetRunId) {
|
|
6464
|
+
const stopChild = deps.state.workflowChildStops?.get(targetRunId);
|
|
6123
6465
|
if (paramsWithResolvedCwd.childId !== undefined) {
|
|
6124
|
-
const workflowRunId = targetRunId
|
|
6466
|
+
const workflowRunId = targetRunId;
|
|
6125
6467
|
const asyncJob = deps.state.asyncJobs.get(workflowRunId);
|
|
6126
6468
|
if (!asyncJob?.asyncDir) return { content: [{ type: "text", text: `Status file not found for async workflow '${workflowRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6127
6469
|
const status = readStatus(asyncJob.asyncDir);
|
|
@@ -6129,7 +6471,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6129
6471
|
const resolution = resolveAsyncStatusChild(status, paramsWithResolvedCwd.childId);
|
|
6130
6472
|
if (!resolution.ok) return { content: [{ type: "text", text: resolution.message }], isError: true, details: { mode: "management", results: [] } };
|
|
6131
6473
|
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: [] } };
|
|
6132
|
-
const stopChild = deps.state.workflowChildStops?.get(targetRunId!);
|
|
6133
6474
|
if (!stopChild) return { content: [{ type: "text", text: `Workflow ${targetRunId} child stop is unavailable in this extension runtime.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6134
6475
|
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: [] } };
|
|
6135
6476
|
try {
|
|
@@ -6154,6 +6495,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6154
6495
|
}
|
|
6155
6496
|
return { content: [{ type: "text", text: `Stop requested for child ${resolution.child.id} in async workflow ${workflowRunId}.` }], details: { mode: "management", results: [] } };
|
|
6156
6497
|
}
|
|
6498
|
+
const asyncJob = deps.state.asyncJobs.get(targetRunId);
|
|
6499
|
+
const status = asyncJob?.asyncDir ? readStatus(asyncJob.asyncDir) : undefined;
|
|
6500
|
+
if (status) stopStoppableAsyncStatusChildren(status, stopChild, "Workflow stopped.");
|
|
6157
6501
|
workflowController.abort(new Error("Workflow stopped."));
|
|
6158
6502
|
return { content: [{ type: "text", text: `Stop requested for async workflow ${targetRunId}.` }], details: { mode: "management", results: [] } };
|
|
6159
6503
|
}
|
|
@@ -6246,10 +6590,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6246
6590
|
config: deps.config,
|
|
6247
6591
|
currentSessionId: deps.state.currentSessionId ?? ctx.sessionManager.getSessionId() ?? undefined,
|
|
6248
6592
|
runtimeAgentOwner: deps.pi,
|
|
6593
|
+
onAgentsChanged: deps.onAgentsChanged,
|
|
6249
6594
|
});
|
|
6250
6595
|
}
|
|
6251
6596
|
|
|
6252
|
-
const { blocked, depth, maxDepth } = checkSubagentDepth(deps.config.maxSubagentDepth);
|
|
6597
|
+
const { blocked, depth, maxDepth } = checkSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
|
|
6253
6598
|
if (blocked) {
|
|
6254
6599
|
return {
|
|
6255
6600
|
content: [
|
|
@@ -6326,8 +6671,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6326
6671
|
});
|
|
6327
6672
|
const agents = applyScopedIntercomBridgeToAgents(discoveredAgents, intercomBridge, contextPolicy);
|
|
6328
6673
|
const runId = randomUUID();
|
|
6329
|
-
const
|
|
6330
|
-
const nestedParentAddress =
|
|
6674
|
+
const inheritedNestedRouteValue = inheritedNestedRoute(deps);
|
|
6675
|
+
const nestedParentAddress = inheritedNestedRouteValue ? inheritedNestedParentAddress(deps) : undefined;
|
|
6331
6676
|
const shareEnabled = effectiveParams.share === true;
|
|
6332
6677
|
const hasChain = (effectiveParams.chain?.length ?? 0) > 0;
|
|
6333
6678
|
const hasTasks = (effectiveParams.tasks?.length ?? 0) > 0;
|
|
@@ -6448,7 +6793,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6448
6793
|
const asyncRunId = requestedWorkflowChildAsyncId && path.basename(requestedWorkflowChildAsyncId) === requestedWorkflowChildAsyncId
|
|
6449
6794
|
? requestedWorkflowChildAsyncId
|
|
6450
6795
|
: randomUUID();
|
|
6451
|
-
const topLevelAsyncCapacityEligible = depth === 0 && !
|
|
6796
|
+
const topLevelAsyncCapacityEligible = depth === 0 && !inheritedNestedRouteValue && !effectiveParams.workflowParentRunId;
|
|
6452
6797
|
const topLevelAsync = effectiveAsync && topLevelAsyncCapacityEligible;
|
|
6453
6798
|
let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
|
|
6454
6799
|
if (topLevelAsync) {
|
|
@@ -6473,9 +6818,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6473
6818
|
|
|
6474
6819
|
let runFanoutBudget: RunFanoutBudgetDescriptor;
|
|
6475
6820
|
try {
|
|
6476
|
-
const
|
|
6821
|
+
const inheritedRunFanoutBudgetValue = effectiveParams.runFanoutBudget ? undefined : inheritedRunFanoutBudget(deps);
|
|
6477
6822
|
runFanoutBudget = effectiveParams.runFanoutBudget
|
|
6478
|
-
?? (
|
|
6823
|
+
?? (inheritedRunFanoutBudgetValue ? { ...inheritedRunFanoutBudgetValue, parentPath: `${inheritedRunFanoutBudgetValue.parentPath ? `${inheritedRunFanoutBudgetValue.parentPath}/` : ""}${runId}` } : undefined)
|
|
6479
6824
|
?? createRunFanoutBudget(runId, resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun));
|
|
6480
6825
|
if (!effectiveParams.runFanoutAdmitted) claimRunFanoutBatch(runFanoutBudget, staticRunFanoutPaths(effectiveParams));
|
|
6481
6826
|
} catch (error) {
|
|
@@ -6483,7 +6828,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6483
6828
|
if (error instanceof RunFanoutLimitError) return runFanoutErrorResult(error, foregroundMode);
|
|
6484
6829
|
return buildRequestedModeError(effectiveParams, error instanceof Error ? error.message : String(error));
|
|
6485
6830
|
}
|
|
6486
|
-
const nestedRoute =
|
|
6831
|
+
const nestedRoute = inheritedNestedRouteValue ?? createNestedRoute(runId);
|
|
6487
6832
|
|
|
6488
6833
|
const artifactConfig: ArtifactConfig = omitUndefinedProperties({
|
|
6489
6834
|
...DEFAULT_ARTIFACT_CONFIG,
|
|
@@ -6499,7 +6844,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6499
6844
|
|
|
6500
6845
|
let sessionRoot: string;
|
|
6501
6846
|
if (effectiveParams.sessionDir) {
|
|
6502
|
-
|
|
6847
|
+
// An explicit sessionDir is a root keyed by this launch's run id so
|
|
6848
|
+
// concurrent children resolve distinct per-child session files.
|
|
6849
|
+
sessionRoot = path.join(path.resolve(deps.expandTilde(effectiveParams.sessionDir)), runId);
|
|
6503
6850
|
} else {
|
|
6504
6851
|
const baseSessionRoot = deps.config.defaultSessionDir
|
|
6505
6852
|
? path.resolve(deps.expandTilde(deps.config.defaultSessionDir))
|
|
@@ -6637,6 +6984,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6637
6984
|
timeoutMs: foregroundTimeout.timeoutMs,
|
|
6638
6985
|
toolBudget: runToolBudget.toolBudget,
|
|
6639
6986
|
usageBudget: usageBudget.budget,
|
|
6987
|
+
inheritedUsageBudget,
|
|
6640
6988
|
allowZeroToolBudget,
|
|
6641
6989
|
configToolBudget: configToolBudget.toolBudget,
|
|
6642
6990
|
configToolTimeoutMs: deps.config.toolTimeoutMs,
|
|
@@ -6655,17 +7003,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6655
7003
|
const foregroundDescription = selectedAgentNames.length === 1
|
|
6656
7004
|
? `${selectedAgentNames[0]} child`
|
|
6657
7005
|
: `${selectedAgentNames.length} live children`;
|
|
6658
|
-
const parentWorkflowStatus = effectiveParams.workflowParentRunId
|
|
6659
|
-
? readStatus(path.join(DIRS.async, effectiveParams.workflowParentRunId))
|
|
6660
|
-
: null;
|
|
6661
|
-
const workflowSteeringDir = effectiveParams.workflowParentRunId
|
|
6662
|
-
&& requestSessionId
|
|
6663
|
-
&& deps.state.workflowControllers?.has(effectiveParams.workflowParentRunId)
|
|
6664
|
-
&& parentWorkflowStatus?.mode === "workflow"
|
|
6665
|
-
&& (parentWorkflowStatus.state === "running" || parentWorkflowStatus.state === "queued")
|
|
6666
|
-
&& parentWorkflowStatus.sessionId === requestSessionId
|
|
6667
|
-
? workflowForegroundSteeringDir(DIRS.async, effectiveParams.workflowParentRunId, runId)
|
|
6668
|
-
: undefined;
|
|
6669
7006
|
const foregroundControl: ForegroundRunControl | undefined = effectiveAsync
|
|
6670
7007
|
? undefined
|
|
6671
7008
|
: compactOptional<ForegroundRunControl>({
|
|
@@ -6674,7 +7011,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6674
7011
|
mode: foregroundMode,
|
|
6675
7012
|
...(effectiveParams.workflowParentRunId ? { parentWorkflowRunId: effectiveParams.workflowParentRunId } : {}),
|
|
6676
7013
|
...(effectiveParams.workflowKey ? { workflowKey: effectiveParams.workflowKey } : {}),
|
|
6677
|
-
workflowSteeringDir,
|
|
6678
7014
|
startedAt: Date.now(),
|
|
6679
7015
|
updatedAt: Date.now(),
|
|
6680
7016
|
cwd: effectiveCwd,
|
|
@@ -6727,7 +7063,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6727
7063
|
}
|
|
6728
7064
|
|
|
6729
7065
|
const writeNestedForegroundEvent = (type: "subagent.nested.started" | "subagent.nested.completed", result?: AgentToolResult<Details>): void => {
|
|
6730
|
-
if (!
|
|
7066
|
+
if (!inheritedNestedRouteValue || !nestedParentAddress) return;
|
|
6731
7067
|
const now = Date.now();
|
|
6732
7068
|
const details = result?.details;
|
|
6733
7069
|
const state = type === "subagent.nested.started"
|
|
@@ -6761,7 +7097,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6761
7097
|
? resolveSubagentIntercomTarget(runId, agentsForSummary[0], 0)
|
|
6762
7098
|
: undefined;
|
|
6763
7099
|
try {
|
|
6764
|
-
writeNestedEvent(
|
|
7100
|
+
writeNestedEvent(inheritedNestedRouteValue, compactOptional<Parameters<typeof writeNestedEvent>[1]>({
|
|
6765
7101
|
type,
|
|
6766
7102
|
ts: now,
|
|
6767
7103
|
parentRunId: nestedParentAddress.parentRunId,
|
|
@@ -6772,7 +7108,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6772
7108
|
parentStepIndex: nestedParentAddress.parentStepIndex,
|
|
6773
7109
|
depth: nestedParentAddress.depth,
|
|
6774
7110
|
path: nestedParentAddress.path,
|
|
6775
|
-
ownerIntercomTarget:
|
|
7111
|
+
ownerIntercomTarget: deps.childRuntime?.intercomSessionName,
|
|
6776
7112
|
leafIntercomTarget,
|
|
6777
7113
|
intercomTarget: leafIntercomTarget,
|
|
6778
7114
|
ownerState: state === "running" ? "live" : "gone",
|
|
@@ -6862,7 +7198,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6862
7198
|
if (activeAsyncCapacity && !activeAsyncCapacity.owner.runnerStartedAt) activeAsyncCapacity.rollback();
|
|
6863
7199
|
if (foregroundControl) {
|
|
6864
7200
|
settleForegroundSchedulingOwner(foregroundControl);
|
|
6865
|
-
removeForegroundControlIfIdle(deps.state, runId);
|
|
7201
|
+
removeForegroundControlIfIdle(deps.state, runId, deps.trackRetainedNestedRoute);
|
|
6866
7202
|
}
|
|
6867
7203
|
}
|
|
6868
7204
|
|
|
@@ -6883,7 +7219,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6883
7219
|
const normalizedAction = typeof params.action === "string" ? params.action.trim() : params.action;
|
|
6884
7220
|
const requestParams = normalizedAction ? { ...params, action: normalizedAction } : params;
|
|
6885
7221
|
if (normalizedAction) return execute(id, requestParams, signal, onUpdate, ctx).then(withAggregatedToolUsage);
|
|
6886
|
-
const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth);
|
|
7222
|
+
const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
|
|
6887
7223
|
const dispatchParams = applyForceTopLevelAsyncOverride(requestParams, depth, deps.config.forceTopLevelAsync === true);
|
|
6888
7224
|
const runsForeground = dispatchParams.clarify === true || (dispatchParams.async ?? deps.asyncByDefault) !== true;
|
|
6889
7225
|
if (!runsForeground) return execute(id, requestParams, signal, onUpdate, ctx).then(withAggregatedToolUsage);
|
|
@@ -6909,7 +7245,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6909
7245
|
}
|
|
6910
7246
|
let publicParams = normalized.params as SubagentParamsLike;
|
|
6911
7247
|
if (publicParams.workflow !== undefined) {
|
|
6912
|
-
const resolved = resolveWorkflowResource(publicParams.workflow, publicParams.args);
|
|
7248
|
+
const resolved = resolveWorkflowResource(publicParams.workflow, publicParams.args, ctx.sessionManager.getSessionId() ?? undefined);
|
|
6913
7249
|
if (!resolved.ok) return Promise.resolve({ content: [{ type: "text", text: resolved.error }], isError: true, details: { mode: "workflow", results: [] } });
|
|
6914
7250
|
const { workflow: _workflow, args: _args, ...withoutResourceInput } = publicParams;
|
|
6915
7251
|
publicParams = { ...withoutResourceInput, workflowScript: resolved.resource.script };
|
|
@@ -6956,16 +7292,27 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6956
7292
|
ctx: ExtensionContext,
|
|
6957
7293
|
) => {
|
|
6958
7294
|
const ownerSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
});
|
|
6965
|
-
scheduledOwnerExecutors.set(ownerSessionId, ownerExecutor);
|
|
7295
|
+
const runtimeOwnerId = ctx.sessionManager.getSessionId() || null;
|
|
7296
|
+
let ownerExecutors = scheduledOwnerExecutors.get(runtimeOwnerId);
|
|
7297
|
+
if (!ownerExecutors) {
|
|
7298
|
+
ownerExecutors = new Map();
|
|
7299
|
+
scheduledOwnerExecutors.set(runtimeOwnerId, ownerExecutors);
|
|
6966
7300
|
}
|
|
6967
|
-
|
|
7301
|
+
let owner = ownerExecutors.get(ownerSessionId);
|
|
7302
|
+
if (!owner) {
|
|
7303
|
+
const state = createScheduledOwnerState(deps.state, ownerSessionId, ctx);
|
|
7304
|
+
owner = { state, executor: createSubagentExecutor({ ...deps, state }) };
|
|
7305
|
+
ownerExecutors.set(ownerSessionId, owner);
|
|
7306
|
+
}
|
|
7307
|
+
return owner.executor.executePublic(id, params, signal, undefined, ctx);
|
|
6968
7308
|
};
|
|
6969
7309
|
|
|
6970
|
-
|
|
6971
|
-
|
|
7310
|
+
function* getCurrentSupervisorOwnerStates(): Iterable<SubagentState> {
|
|
7311
|
+
const ownerId = deps.state.supervisorOwnerSessionId;
|
|
7312
|
+
if (!ownerId) return;
|
|
7313
|
+
// File transitions remain separate entries; other runtime owners are never scanned.
|
|
7314
|
+
for (const owner of scheduledOwnerExecutors.get(ownerId)?.values() ?? []) yield owner.state;
|
|
7315
|
+
}
|
|
7316
|
+
|
|
7317
|
+
return { execute: executeWithSingleDispatchGuard, executePublic, executeDelegated, executeScheduled, getCurrentSupervisorOwnerStates };
|
|
7318
|
+
}
|