@selesai/code 0.13.15 → 0.13.18
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 +37 -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/capability-gateway/index.ts +12 -1
- package/dist/extensions/capability-gateway/integration.test.ts +23 -0
- 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/src/backends/config.ts +1 -0
- package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +5 -3
- package/dist/extensions/pi-web-agent/src/search/tokenin.ts +7 -3
- package/dist/extensions/pi-web-agent/src/types.ts +1 -1
- 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 +4 -4
- 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/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
|
@@ -1,9 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { EventEmitter } from "node:events";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
4
5
|
import * as path from "node:path";
|
|
5
6
|
import { pathToFileURL } from "node:url";
|
|
6
7
|
import type { Message } from "@earendil-works/pi-ai";
|
|
8
|
+
import { arbitrateCompletionGuardRescue, createTaskMutationArbiter } from "../shared/llm-intent-arbiter.ts";
|
|
9
|
+
|
|
10
|
+
// Detached runners skip Pi's CLI proxy setup. Keep fetch on the same Undici dispatcher.
|
|
11
|
+
function ensureProxyAwareHttpDispatcher(): void {
|
|
12
|
+
try {
|
|
13
|
+
// SAFETY: require loads the pinned direct dependency described by these types.
|
|
14
|
+
const undici = createRequire(import.meta.url)("undici") as typeof import("undici");
|
|
15
|
+
const dispatcher = new undici.EnvHttpProxyAgent({ allowH2: false });
|
|
16
|
+
// Fetch rejects stream errors; the listener prevents an unhandled EventEmitter error.
|
|
17
|
+
EventEmitter.prototype.on.call(dispatcher, "error", () => {});
|
|
18
|
+
undici.setGlobalDispatcher(dispatcher);
|
|
19
|
+
undici.install();
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error(`[pi-subagents] proxy-aware HTTP dispatcher not installed: ${error instanceof Error ? error.message : String(error)}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const isRunnerEntrypoint = Boolean(process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href);
|
|
25
|
+
if (isRunnerEntrypoint) ensureProxyAwareHttpDispatcher();
|
|
7
26
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
8
27
|
import { writeAsyncResultFile, writePendingAsyncResultFile } from "./result-files.ts";
|
|
9
28
|
import { createFileCoalescer } from "../../shared/file-coalescer.ts";
|
|
@@ -11,9 +30,9 @@ import { createCapacityResilientJsonWriter } from "../../shared/capacity-resilie
|
|
|
11
30
|
import { isStorageCapacityError } from "../../shared/file-system-retry.ts";
|
|
12
31
|
import { updateActiveRunIndex } from "./active-run-index.ts";
|
|
13
32
|
import { createChildTranscriptWriter, type ChildTranscriptWriter } from "../../shared/child-transcript.ts";
|
|
14
|
-
import { closeSteerInbox, consumeInterruptRequest, consumeSteerRequests, deliverInterruptRequest, deliverStopRequest, deliverTimeoutRequest,
|
|
33
|
+
import { closeSteerInbox, consumeInterruptRequest, consumeSteerRequests, deliverInterruptRequest, deliverStopRequest, deliverTimeoutRequest, watchAsyncControlInbox, type SteerRequest, type StopRequest } from "./control-channel.ts";
|
|
15
34
|
import { appendJsonl as appendRawJsonl, formatOutputArtifactContent, getArtifactPaths, writeArtifact, writeMetadata } from "../../shared/artifacts.ts";
|
|
16
|
-
import { SELESAI_CODING_AGENT_PACKAGE,
|
|
35
|
+
import { SELESAI_CODING_AGENT_PACKAGE, resolveInstalledPiPackageRoot } from "../shared/pi-spawn.ts";
|
|
17
36
|
import { preflightLaunchCwd } from "../shared/launch-cwd.ts";
|
|
18
37
|
import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, injectOutputPathSystemPrompt, injectSingleOutputInstruction, resolveSingleOutput, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
19
38
|
import {
|
|
@@ -28,11 +47,10 @@ import {
|
|
|
28
47
|
type AsyncStatus,
|
|
29
48
|
type ChainOutputMap,
|
|
30
49
|
type CostSummary,
|
|
31
|
-
type
|
|
32
|
-
type
|
|
50
|
+
type LaunchResolvedChildExtensions,
|
|
51
|
+
type RuntimeAcknowledgedChildExtensions,
|
|
33
52
|
type ModelAttempt,
|
|
34
|
-
type
|
|
35
|
-
type ProcessTreeTerminalV1,
|
|
53
|
+
type PiWriterProcessInstanceExit,
|
|
36
54
|
type NestedRouteInfo,
|
|
37
55
|
type NestedRunSummary,
|
|
38
56
|
type ResolvedControlConfig,
|
|
@@ -52,7 +70,6 @@ import {
|
|
|
52
70
|
type MaxOutputConfig,
|
|
53
71
|
SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
54
72
|
truncateOutput,
|
|
55
|
-
getSubagentDepthEnv,
|
|
56
73
|
} from "../../shared/types.ts";
|
|
57
74
|
import {
|
|
58
75
|
DEFAULT_CONTROL_CONFIG,
|
|
@@ -75,31 +92,29 @@ import {
|
|
|
75
92
|
DEFAULT_GLOBAL_CONCURRENCY_LIMIT,
|
|
76
93
|
Semaphore,
|
|
77
94
|
} from "../shared/parallel-utils.ts";
|
|
78
|
-
import { applyThinkingSuffix,
|
|
95
|
+
import { applyThinkingSuffix, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan } from "../shared/child-tool-plan.ts";
|
|
96
|
+
import type { InheritedChildRuntime } from "../shared/child-launch.ts";
|
|
97
|
+
import { buildRunnerChildLaunch } from "./runner-child-launch.ts";
|
|
98
|
+
import { normalizeExtensionBindings } from "../shared/extension-bindings.ts";
|
|
99
|
+
import type { ChildSessionFactory } from "../shared/child-session.ts";
|
|
100
|
+
import { getSettledReadonlyChild, runChildSession, type ChildEvent, type RunChildSessionInput, type RunChildSessionResult, type StepSteerHandler } from "./run-child-session.ts";
|
|
101
|
+
import { planReadonlyModelContinuation, READONLY_CONTINUATION_PROMPT, type LogicalRecoveryState } from "../shared/readonly-model-continuation.ts";
|
|
102
|
+
import { getReadonlySessionEvidence } from "../shared/readonly-session-evidence.ts";
|
|
103
|
+
import { loadRunnerChildSessionFactory } from "./runner-child-sessions.ts";
|
|
104
|
+
import { SUBAGENT_CHILD_ENV } from "../shared/child-runtime-config.ts";
|
|
79
105
|
import { deriveChildSessionName } from "../../shared/child-session-name.ts";
|
|
80
106
|
import { alignForkedSessionCwd } from "../../shared/fork-session-cwd.ts";
|
|
81
|
-
import { readRuntimeAcknowledgedExtensions } from "../shared/runtime-acknowledged-extensions.ts";
|
|
82
107
|
import { outputEntryFromAsyncResult, resolveOutputReferences } from "../shared/chain-outputs.ts";
|
|
83
|
-
import { clearStructuredOutputCaptures, createStructuredOutputRuntime, MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput, readStructuredOutputAcceptanceReport } from "../shared/structured-output.ts";
|
|
84
|
-
import { formatMidToolExitError,
|
|
85
|
-
import {
|
|
108
|
+
import { clearStructuredOutputCaptures, createStructuredOutputFileCapture, createStructuredOutputRuntime, MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput, readStructuredOutputAcceptanceReport } from "../shared/structured-output.ts";
|
|
109
|
+
import { formatMidToolExitError, isOrdinaryToolForMidToolExit, isUnexplainedProcessSignal } from "../shared/process-signal.ts";
|
|
110
|
+
import { formatChildToolDiagnostic } from "../shared/tool-availability.ts";
|
|
86
111
|
import { buildTimeoutRecoverySummary, collectTrackedMutationEvidence, snapshotTrackedMutations } from "../shared/mutation-evidence.ts";
|
|
87
112
|
import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelStep, validateDynamicCollection } from "../shared/dynamic-fanout.ts";
|
|
88
113
|
import { claimRunFanoutBatch, getRunFanoutBudgetSnapshot } from "../shared/run-fanout-budget.ts";
|
|
89
114
|
import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDir, writeNestedEvent } from "../shared/nested-events.ts";
|
|
90
115
|
import { formatModelAttemptNote, formatSubagentModelVerificationError, isContextOverflow, isRetryableModelFailureAttempt, recordRetryableModelFailure } from "../shared/model-fallback.ts";
|
|
91
|
-
import {
|
|
92
|
-
SUBAGENT_STARTUP_RETRY_DELAYS_MS,
|
|
93
|
-
formatSubagentExtensionConflictError,
|
|
94
|
-
formatSubagentStartupRetryExhaustedError,
|
|
95
|
-
formatSubagentStartupRetryNote,
|
|
96
|
-
isRetryableSubagentStartupFailure,
|
|
97
|
-
waitForSubagentStartupRetry,
|
|
98
|
-
} from "../shared/subagent-startup-retry.ts";
|
|
99
|
-
import { markProcessTerminalCandidateLeaseRelease, writeProcessTerminalCandidate, type ProcessTerminalCandidate } from "./process-terminal.ts";
|
|
100
|
-
import { createOwnedProcessTreeController, type OwnedProcessTreeController } from "./owned-process-tree.ts";
|
|
116
|
+
import { markProcessTerminalCandidateLeaseRelease, processTerminalPath, writeProcessTerminalCandidate, type ProcessTerminalCandidate } from "./process-terminal.ts";
|
|
101
117
|
import { createSteeringStatus, recordSteeringRequest, steeringStatus, terminalSteeringNoticeState, updateSteeringTarget } from "./steering.ts";
|
|
102
|
-
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
103
118
|
import { PROMPT_REDACTED, detectSubagentError, extractTextFromContent, extractToolArgsPreview, formatEmptyTerminalAssistantResponseError, getFinalOutput, hasEmptyTerminalAssistantResponse, readStatus } from "../../shared/utils.ts";
|
|
104
119
|
import { evaluateCompletionMutationGuard, expectsImplementationMutation, hasMutationToolCapability, validateImplementationToolContract } from "../shared/completion-guard.ts";
|
|
105
120
|
import { planCompletionEvidence, projectSettlementDiagnostic } from "../shared/completion-evidence.ts";
|
|
@@ -120,6 +135,9 @@ import type { TokenUsage } from "../../shared/types.ts";
|
|
|
120
135
|
import {
|
|
121
136
|
cleanupWorktrees,
|
|
122
137
|
createWorktrees,
|
|
138
|
+
withWorktreeTransaction,
|
|
139
|
+
WorktreeSetupError,
|
|
140
|
+
type WorktreeSetupProgress,
|
|
123
141
|
diffWorktrees,
|
|
124
142
|
findWorktreeTaskCwdConflict,
|
|
125
143
|
formatWorktreeDiffSummary,
|
|
@@ -127,42 +145,42 @@ import {
|
|
|
127
145
|
WORKTREE_AGENT_CWD_PLACEHOLDER,
|
|
128
146
|
type WorktreeSetup,
|
|
129
147
|
} from "../shared/worktree.ts";
|
|
130
|
-
import { findModelInfo, resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
131
|
-
import { assertThinkingWithinCeiling
|
|
148
|
+
import { findModelInfo, resolveEffectiveThinking, splitKnownThinkingSuffix } from "../../shared/model-info.ts";
|
|
149
|
+
import { assertThinkingWithinCeiling } from "../../shared/thinking-ceiling.ts";
|
|
132
150
|
import { launchBindingDigest } from "../../shared/launch-contract.ts";
|
|
133
151
|
import { writeInitialProgressFile } from "../../shared/settings.ts";
|
|
134
152
|
import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
|
|
135
153
|
import { acceptanceFailureMessage, aggregateAcceptanceReport, buildSkippedAcceptanceLedger, evaluateAcceptance, formatAcceptancePrompt, resolveAcceptanceReportMode, resolveEffectiveAcceptance, stripAcceptanceReport } from "../shared/acceptance.ts";
|
|
136
|
-
import { attachContractProjections,
|
|
154
|
+
import { attachContractProjections, isAgentContract } from "../shared/agent-contract.ts";
|
|
137
155
|
import { waitForImportedAsyncRoot } from "./chain-root-attachment.ts";
|
|
138
|
-
import { normalizeExtensionBindings } from "../shared/extension-bindings.ts";
|
|
139
156
|
import { appendRunnerStepsToStatus, consumeChainAppendRequests, countPendingChainAppendRequests, statusStepDescription } from "./chain-append.ts";
|
|
140
157
|
import { asyncStatusChildIdentity } from "../shared/child-identity.ts";
|
|
141
158
|
import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.ts";
|
|
142
159
|
import { effectiveToolTimeoutMs, formatToolTimeoutMessage, toolTimeoutCallKey } from "../shared/tool-timeout.ts";
|
|
143
160
|
import { usageBudgetExceededMessage, usageBudgetState } from "../shared/usage-budget.ts";
|
|
144
|
-
import { formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup,
|
|
161
|
+
import { formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup, writeWorktreeSetupHandoff } from "../shared/parallel-handoff.ts";
|
|
145
162
|
import { resolveWatchdogConfig } from "../../watchdog/settings.ts";
|
|
146
|
-
import { createBoundedByteTail, createBoundedLineReader, formatProtocolOutputLimit, MAX_CHILD_STDERR_BYTES, PI_AGGREGATE_EVENT_PROJECTOR, projectChildLifecycle, type ChildLifecycleAction, type ChildLifecycleState, type ProtocolOutputLimit } from "../shared/child-protocol.ts";
|
|
147
163
|
import { acquireSessionLease, type SessionLeaseRequest } from "../shared/session-lease.ts";
|
|
148
164
|
import { buildExternalCliPrompt, runExternalCli } from "../shared/external-cli-runner.ts";
|
|
149
165
|
import { resolveExternalCliRunnerStatus } from "../shared/external-cli-contract.ts";
|
|
150
166
|
import { runExternalJob } from "../shared/external-job-runner.ts";
|
|
151
167
|
import { createOrcaProgressTab, type OrcaProgressTab } from "../shared/orca-progress-tabs.ts";
|
|
152
|
-
import {
|
|
168
|
+
import type { ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
153
169
|
import {
|
|
154
|
-
CHILD_WATCHDOG_CONFIG_ENV,
|
|
155
170
|
acceptChildWatchdogEvent,
|
|
156
171
|
applyChildWatchdogMessage,
|
|
157
|
-
childWatchdogIsActive,
|
|
158
|
-
decodeChildWatchdogConfig,
|
|
159
172
|
isChildWatchdogStatusEvent,
|
|
160
173
|
resolveChildWatchdogConfig,
|
|
161
|
-
type
|
|
174
|
+
type ChildWatchdogStatusEvent,
|
|
162
175
|
} from "../../watchdog/child-status.ts";
|
|
163
176
|
|
|
164
177
|
const INTERCOM_DETACH_RECEIPT = "Detached for intercom coordination before task completion.";
|
|
165
178
|
|
|
179
|
+
// This process hosts child sessions. An ambient copy of pi-subagents loaded
|
|
180
|
+
// into one of them must register nothing; the variable marks the process as a
|
|
181
|
+
// child host.
|
|
182
|
+
process.env[SUBAGENT_CHILD_ENV] = "1";
|
|
183
|
+
|
|
166
184
|
interface SubagentRunConfig {
|
|
167
185
|
id: string;
|
|
168
186
|
steps: RunnerStep[];
|
|
@@ -180,10 +198,14 @@ interface SubagentRunConfig {
|
|
|
180
198
|
sessionId?: string | null;
|
|
181
199
|
completionOwnerId?: string;
|
|
182
200
|
piPackageRoot?: string;
|
|
183
|
-
|
|
201
|
+
/** Test seam: module the runner imports its `ChildSessionFactory` from. */
|
|
202
|
+
childSessionFactoryModule?: string;
|
|
203
|
+
/** The launching executor's own child runtime when it was itself an in-process child. */
|
|
204
|
+
inheritedChildRuntime?: InheritedChildRuntime;
|
|
184
205
|
worktreeSetupHook?: string;
|
|
185
206
|
worktreeSetupHookTimeoutMs?: number;
|
|
186
207
|
worktreeBaseDir?: string;
|
|
208
|
+
baseRef?: string;
|
|
187
209
|
worktreeProvider?: import("../../shared/types.ts").WorktreeProvider;
|
|
188
210
|
worktreeBranchPrefix?: string;
|
|
189
211
|
controlConfig?: ResolvedControlConfig;
|
|
@@ -208,8 +230,8 @@ interface SubagentRunConfig {
|
|
|
208
230
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
209
231
|
runFanoutBudget?: RunFanoutBudgetDescriptor;
|
|
210
232
|
launchContractDigest?: string;
|
|
211
|
-
launchResolvedExtensions?:
|
|
212
|
-
runtimeAcknowledgedExtensions?:
|
|
233
|
+
launchResolvedExtensions?: LaunchResolvedChildExtensions;
|
|
234
|
+
runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensions;
|
|
213
235
|
runnerProcessInstanceId?: string;
|
|
214
236
|
launchBarrierToken?: string;
|
|
215
237
|
parentWorkflowRunId?: string;
|
|
@@ -224,12 +246,11 @@ interface StepResult {
|
|
|
224
246
|
context?: "fresh" | "fork";
|
|
225
247
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
226
248
|
capabilityAudit?: import("../shared/capability-ceiling.ts").SubagentCapabilityAudit;
|
|
227
|
-
launchResolvedExtensions?:
|
|
228
|
-
runtimeAcknowledgedExtensions?:
|
|
249
|
+
launchResolvedExtensions?: LaunchResolvedChildExtensions;
|
|
250
|
+
runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensions;
|
|
229
251
|
output: string;
|
|
230
252
|
outputState?: SubagentOutputState;
|
|
231
253
|
error?: string;
|
|
232
|
-
protocolError?: ProtocolOutputLimit;
|
|
233
254
|
success?: boolean;
|
|
234
255
|
exitCode: number | null;
|
|
235
256
|
usage?: Usage;
|
|
@@ -268,8 +289,6 @@ interface StepResult {
|
|
|
268
289
|
structuredOutputSchemaPath?: string;
|
|
269
290
|
acceptance?: import("../../shared/types.ts").AcceptanceLedger;
|
|
270
291
|
watchdog?: import("../../shared/types.ts").ChildWatchdogProgress;
|
|
271
|
-
writerProcesses?: PiWriterProcessInstanceExitV1[];
|
|
272
|
-
writerAttemptCount?: number;
|
|
273
292
|
runner?: ExternalCliRunnerStatus | ExternalJobRunnerStatus;
|
|
274
293
|
externalProcess?: ExternalProcessStatus;
|
|
275
294
|
externalJob?: ExternalJobStatus;
|
|
@@ -370,10 +389,6 @@ function appendDiagnosticJsonl(filePath: string, line: string, droppedEventType?
|
|
|
370
389
|
state.diagnosticsTruncated = true;
|
|
371
390
|
}
|
|
372
391
|
|
|
373
|
-
function shouldPersistChildEvent(event: Record<string, unknown>): boolean {
|
|
374
|
-
return event.type !== "message_update";
|
|
375
|
-
}
|
|
376
|
-
|
|
377
392
|
function isBlockingSupervisorTool(toolName: string | undefined, args: unknown): boolean {
|
|
378
393
|
if (!args || typeof args !== "object" || Array.isArray(args)) return false;
|
|
379
394
|
if (toolName === "contact_supervisor") {
|
|
@@ -456,15 +471,6 @@ function appendRecentStepOutput(step: RunnerStatusStep, lines: string[]): void {
|
|
|
456
471
|
}
|
|
457
472
|
}
|
|
458
473
|
|
|
459
|
-
function assistantStartsToolCall(message: Message): boolean {
|
|
460
|
-
return Array.isArray(message.content)
|
|
461
|
-
&& message.content.some((part) => (part as { type?: string }).type === "toolCall");
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
function isTerminalAssistantStop(message: Message): boolean {
|
|
465
|
-
return (message as { stopReason?: string }).stopReason === "stop" && !assistantStartsToolCall(message);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
474
|
type UndefinedOmitted<T extends object> = {
|
|
469
475
|
[K in keyof T]: Exclude<T[K], undefined>;
|
|
470
476
|
};
|
|
@@ -507,73 +513,6 @@ function resetStepLiveDetail(step: RunnerStatusStep): void {
|
|
|
507
513
|
step.recentOutput = [];
|
|
508
514
|
}
|
|
509
515
|
|
|
510
|
-
interface ChildEventContext {
|
|
511
|
-
eventsPath: string;
|
|
512
|
-
runId: string;
|
|
513
|
-
stepIndex: number;
|
|
514
|
-
agent: string;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
interface ChildUsage {
|
|
518
|
-
input?: number;
|
|
519
|
-
inputTokens?: number;
|
|
520
|
-
output?: number;
|
|
521
|
-
outputTokens?: number;
|
|
522
|
-
cacheRead?: number;
|
|
523
|
-
cacheReadTokens?: number;
|
|
524
|
-
cacheWrite?: number;
|
|
525
|
-
cost?: { total?: number };
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
type ChildMessage = Message & {
|
|
529
|
-
model?: string;
|
|
530
|
-
errorMessage?: string;
|
|
531
|
-
usage?: ChildUsage;
|
|
532
|
-
};
|
|
533
|
-
|
|
534
|
-
interface ChildEvent {
|
|
535
|
-
type?: string;
|
|
536
|
-
message?: ChildMessage;
|
|
537
|
-
toolName?: string;
|
|
538
|
-
args?: Record<string, unknown>;
|
|
539
|
-
willRetry?: unknown;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
interface RunPiStreamingResult {
|
|
543
|
-
stderr: string;
|
|
544
|
-
exitCode: number | null;
|
|
545
|
-
messages: Message[];
|
|
546
|
-
usage: Usage;
|
|
547
|
-
toolCount: number;
|
|
548
|
-
durationMs: number;
|
|
549
|
-
model?: string;
|
|
550
|
-
error?: string;
|
|
551
|
-
protocolError?: ProtocolOutputLimit;
|
|
552
|
-
finalOutput: string;
|
|
553
|
-
outputState: SubagentOutputState;
|
|
554
|
-
interrupted?: boolean;
|
|
555
|
-
timedOut?: boolean;
|
|
556
|
-
stopped?: boolean;
|
|
557
|
-
toolBudget?: ToolBudgetState;
|
|
558
|
-
toolBudgetBlocked?: boolean;
|
|
559
|
-
observedMutationAttempt?: boolean;
|
|
560
|
-
structuredOutputToolInvoked?: boolean;
|
|
561
|
-
structuredOutputMessageStartIndex?: number;
|
|
562
|
-
structuredOutput?: unknown;
|
|
563
|
-
watchdog?: ChildWatchdogStateSnapshot;
|
|
564
|
-
runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensionsV1;
|
|
565
|
-
processInstanceId: string;
|
|
566
|
-
processCloseObservedAt?: number;
|
|
567
|
-
processSignal?: string | null;
|
|
568
|
-
processTree: ProcessTreeTerminalV1;
|
|
569
|
-
currentTool?: string;
|
|
570
|
-
currentToolArgs?: string;
|
|
571
|
-
currentPath?: string;
|
|
572
|
-
afterCompactionSettlement?: boolean;
|
|
573
|
-
abortRecoveryDiagnostic?: string;
|
|
574
|
-
effects?: import("../../shared/types.ts").EffectsProjection;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
516
|
const MAX_CHILD_FAILURE_DIAGNOSTIC_CHARS = 8_192;
|
|
578
517
|
|
|
579
518
|
function formatRequiredOutputError(requiredOutput: {
|
|
@@ -608,568 +547,6 @@ function formatChildFailureDiagnostic(input: {
|
|
|
608
547
|
return `${baseError.slice(0, Math.max(0, errorLimit))}${context ? `\n${context}` : ""}`;
|
|
609
548
|
}
|
|
610
549
|
|
|
611
|
-
function runPiStreaming(
|
|
612
|
-
args: string[],
|
|
613
|
-
cwd: string,
|
|
614
|
-
outputFile: string,
|
|
615
|
-
env?: Record<string, string | undefined>,
|
|
616
|
-
piPackageRoot?: string,
|
|
617
|
-
piArgv1?: string,
|
|
618
|
-
maxSubagentDepth?: number,
|
|
619
|
-
childEventContext?: ChildEventContext,
|
|
620
|
-
registerInterrupt?: (interrupt: (() => void) | undefined) => void,
|
|
621
|
-
onChildEvent?: (event: ChildEvent) => void,
|
|
622
|
-
transcriptWriter?: ChildTranscriptWriter,
|
|
623
|
-
registerTimeout?: (interrupt: (() => void) | undefined) => void,
|
|
624
|
-
timeoutMessage?: string,
|
|
625
|
-
registerStop?: (stop: (() => void) | undefined) => void,
|
|
626
|
-
stopMessage?: string,
|
|
627
|
-
onWriterProcess?: (writer: { state: "none" | "spawning" } | { state: "running"; pid: number }) => void,
|
|
628
|
-
toolTimeoutMs?: number,
|
|
629
|
-
runDeadlineAt?: number,
|
|
630
|
-
orcaProgressTab?: OrcaProgressTab,
|
|
631
|
-
expectedModelForVerification?: string,
|
|
632
|
-
modelVerificationRegistry?: Array<{ provider: string; id: string; fullId: string }>,
|
|
633
|
-
mutationTools?: readonly string[],
|
|
634
|
-
): Promise<RunPiStreamingResult> {
|
|
635
|
-
return new Promise((resolve) => {
|
|
636
|
-
const startedAt = Date.now();
|
|
637
|
-
const processInstanceId = randomUUID();
|
|
638
|
-
onWriterProcess?.({ state: "spawning" });
|
|
639
|
-
const outputStream = fs.createWriteStream(outputFile, { flags: "w" });
|
|
640
|
-
const spawnEnv = { ...process.env, ...(env ?? {}), ...getSubagentDepthEnv(maxSubagentDepth) };
|
|
641
|
-
const spawnSpec = getPiSpawnCommand(args, {
|
|
642
|
-
...(piPackageRoot ? { piPackageRoot } : {}),
|
|
643
|
-
...(piArgv1 ? { argv1: piArgv1 } : {}),
|
|
644
|
-
});
|
|
645
|
-
const child = spawn(spawnSpec.command, spawnSpec.args, {
|
|
646
|
-
cwd,
|
|
647
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
648
|
-
env: spawnEnv,
|
|
649
|
-
windowsHide: true,
|
|
650
|
-
detached: process.platform !== "win32",
|
|
651
|
-
});
|
|
652
|
-
let processTreeController: OwnedProcessTreeController | undefined;
|
|
653
|
-
const stderrTail = createBoundedByteTail();
|
|
654
|
-
const rawStdoutTail = createBoundedByteTail();
|
|
655
|
-
const messages: Message[] = [];
|
|
656
|
-
const usage = emptyUsage();
|
|
657
|
-
let model: string | undefined;
|
|
658
|
-
let writerRegistrationError: string | undefined;
|
|
659
|
-
if (typeof child.pid === "number") {
|
|
660
|
-
processTreeController = createOwnedProcessTreeController(child.pid);
|
|
661
|
-
try {
|
|
662
|
-
onWriterProcess?.({ state: "running", pid: child.pid });
|
|
663
|
-
} catch (writerError) {
|
|
664
|
-
writerRegistrationError = `Failed to record revived Pi writer ownership: ${writerError instanceof Error ? writerError.message : String(writerError)}`;
|
|
665
|
-
trySignalChild(child, "SIGKILL");
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
let error: string | undefined = writerRegistrationError;
|
|
669
|
-
let assistantError: string | undefined;
|
|
670
|
-
let interrupted = false;
|
|
671
|
-
let timedOut = false;
|
|
672
|
-
let stopped = false;
|
|
673
|
-
let observedMutationAttempt = false;
|
|
674
|
-
let structuredOutputToolInvoked = false;
|
|
675
|
-
let structuredOutputMessageStartIndex: number | undefined;
|
|
676
|
-
let currentTool: string | undefined;
|
|
677
|
-
let currentToolArgs: string | undefined;
|
|
678
|
-
let currentPath: string | undefined;
|
|
679
|
-
let toolCount = 0;
|
|
680
|
-
type ActiveToolCall = { key: string; tool: string; args?: string; path?: string };
|
|
681
|
-
let activeToolSequence = 0;
|
|
682
|
-
const activeToolCalls = new Map<string, ActiveToolCall>();
|
|
683
|
-
const activeToolKeysByName = new Map<string, string[]>();
|
|
684
|
-
const refreshCurrentTool = (): void => {
|
|
685
|
-
const active = [...activeToolCalls.values()].at(-1);
|
|
686
|
-
currentTool = active?.tool;
|
|
687
|
-
currentToolArgs = active?.args;
|
|
688
|
-
currentPath = active?.path;
|
|
689
|
-
};
|
|
690
|
-
const recordActiveToolCall = (event: { toolCallId?: unknown; toolName: string; args?: Record<string, unknown> }): void => {
|
|
691
|
-
const key = toolTimeoutCallKey(event, ++activeToolSequence);
|
|
692
|
-
const active = omitUndefinedProperties({
|
|
693
|
-
key,
|
|
694
|
-
tool: event.toolName,
|
|
695
|
-
args: extractToolArgsPreview(event.args ?? {}),
|
|
696
|
-
path: resolveCurrentPath(event.toolName, event.args),
|
|
697
|
-
});
|
|
698
|
-
activeToolCalls.set(key, active);
|
|
699
|
-
const keys = activeToolKeysByName.get(active.tool) ?? [];
|
|
700
|
-
keys.push(key);
|
|
701
|
-
activeToolKeysByName.set(active.tool, keys);
|
|
702
|
-
refreshCurrentTool();
|
|
703
|
-
};
|
|
704
|
-
const removeActiveToolCall = (event: { toolCallId?: unknown; toolName?: unknown }): void => {
|
|
705
|
-
const key = typeof event.toolCallId === "string" && event.toolCallId.length > 0
|
|
706
|
-
? `id:${event.toolCallId}`
|
|
707
|
-
: typeof event.toolName === "string"
|
|
708
|
-
? activeToolKeysByName.get(event.toolName)?.[0]
|
|
709
|
-
: activeToolCalls.size === 1
|
|
710
|
-
? [...activeToolCalls.keys()][0]
|
|
711
|
-
: undefined;
|
|
712
|
-
if (!key) return;
|
|
713
|
-
const active = activeToolCalls.get(key);
|
|
714
|
-
if (!active) return;
|
|
715
|
-
activeToolCalls.delete(key);
|
|
716
|
-
const keys = activeToolKeysByName.get(active.tool)?.filter((candidate) => candidate !== key) ?? [];
|
|
717
|
-
if (keys.length > 0) activeToolKeysByName.set(active.tool, keys);
|
|
718
|
-
else activeToolKeysByName.delete(active.tool);
|
|
719
|
-
refreshCurrentTool();
|
|
720
|
-
};
|
|
721
|
-
const childWatchdogConfig = decodeChildWatchdogConfig(env?.[CHILD_WATCHDOG_CONFIG_ENV]);
|
|
722
|
-
let childWatchdogState: ChildWatchdogStateSnapshot | undefined;
|
|
723
|
-
const childLifecycleState: ChildLifecycleState = { compactionRetryActive: false };
|
|
724
|
-
let applyChildLifecycle = (_action: ChildLifecycleAction): void => {};
|
|
725
|
-
const updateChildWatchdogState = (snapshot: ChildWatchdogStateSnapshot): void => {
|
|
726
|
-
childWatchdogState = snapshot;
|
|
727
|
-
};
|
|
728
|
-
|
|
729
|
-
const writeOutputLine = (line: string) => {
|
|
730
|
-
if (!line.trim()) return;
|
|
731
|
-
outputStream.write(`${line}\n`);
|
|
732
|
-
orcaProgressTab?.append(`${line}\n`);
|
|
733
|
-
};
|
|
734
|
-
|
|
735
|
-
const writeOutputText = (text: string) => {
|
|
736
|
-
for (const line of text.split("\n")) {
|
|
737
|
-
writeOutputLine(line);
|
|
738
|
-
}
|
|
739
|
-
};
|
|
740
|
-
|
|
741
|
-
const appendChildEvent = (event: Record<string, unknown>) => {
|
|
742
|
-
if (!childEventContext) return;
|
|
743
|
-
if (!shouldPersistChildEvent(event)) return;
|
|
744
|
-
appendDiagnosticJsonl(childEventContext.eventsPath, JSON.stringify({
|
|
745
|
-
...event,
|
|
746
|
-
subagentSource: "child",
|
|
747
|
-
subagentRunId: childEventContext.runId,
|
|
748
|
-
subagentStepIndex: childEventContext.stepIndex,
|
|
749
|
-
subagentAgent: childEventContext.agent,
|
|
750
|
-
observedAt: Date.now(),
|
|
751
|
-
}), typeof event.type === "string" ? event.type : undefined);
|
|
752
|
-
};
|
|
753
|
-
|
|
754
|
-
const appendChildLine = (type: "subagent.child.stdout" | "subagent.child.stderr", line: string) => {
|
|
755
|
-
appendChildEvent({ type, line });
|
|
756
|
-
if (type === "subagent.child.stdout") transcriptWriter?.writeStdoutLine(line);
|
|
757
|
-
else transcriptWriter?.writeStderrLine(line);
|
|
758
|
-
};
|
|
759
|
-
|
|
760
|
-
const processStdoutLine = (line: string) => {
|
|
761
|
-
if (!line.trim()) return;
|
|
762
|
-
let event: ChildEvent;
|
|
763
|
-
try {
|
|
764
|
-
event = JSON.parse(line) as ChildEvent;
|
|
765
|
-
} catch {
|
|
766
|
-
rawStdoutTail.push(`${line}\n`);
|
|
767
|
-
writeOutputLine(line);
|
|
768
|
-
appendChildLine("subagent.child.stdout", line);
|
|
769
|
-
return;
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
appendChildEvent(event as unknown as Record<string, unknown>);
|
|
773
|
-
transcriptWriter?.writeChildEvent(event);
|
|
774
|
-
if (event.type === "compaction_start") compactionStartedReceived = true;
|
|
775
|
-
if (event.type === "compaction_end" && event.willRetry === true) {
|
|
776
|
-
compactionStartedReceived = false;
|
|
777
|
-
afterCompactionSettlement = false;
|
|
778
|
-
}
|
|
779
|
-
if (event.type === "agent_start" || event.type === "auto_retry_start") {
|
|
780
|
-
compactionStartedReceived = false;
|
|
781
|
-
afterCompactionSettlement = false;
|
|
782
|
-
}
|
|
783
|
-
const lifecycleAction = projectChildLifecycle(event, false, childLifecycleState);
|
|
784
|
-
if (event.type === "agent_settled" && lifecycleAction === "start-drain") {
|
|
785
|
-
agentSettledReceived = true;
|
|
786
|
-
afterCompactionSettlement = compactionStartedReceived;
|
|
787
|
-
}
|
|
788
|
-
applyChildLifecycle(lifecycleAction);
|
|
789
|
-
|
|
790
|
-
if (isChildWatchdogStatusEvent(event)) {
|
|
791
|
-
if (!childWatchdogConfig) return;
|
|
792
|
-
const next = acceptChildWatchdogEvent({
|
|
793
|
-
current: childWatchdogState,
|
|
794
|
-
event,
|
|
795
|
-
...(childEventContext ? {
|
|
796
|
-
runId: childEventContext.runId,
|
|
797
|
-
agent: childEventContext.agent,
|
|
798
|
-
childIndex: childEventContext.stepIndex,
|
|
799
|
-
} : {}),
|
|
800
|
-
});
|
|
801
|
-
if (!next) return;
|
|
802
|
-
updateChildWatchdogState(next);
|
|
803
|
-
onChildEvent?.(event);
|
|
804
|
-
if (childWatchdogIsActive(next)) {
|
|
805
|
-
if (finalDrainTimer) {
|
|
806
|
-
clearTimeout(finalDrainTimer);
|
|
807
|
-
finalDrainTimer = undefined;
|
|
808
|
-
}
|
|
809
|
-
if (finalHardKillTimer) {
|
|
810
|
-
clearTimeout(finalHardKillTimer);
|
|
811
|
-
finalHardKillTimer = undefined;
|
|
812
|
-
}
|
|
813
|
-
armWatchdogTail();
|
|
814
|
-
} else {
|
|
815
|
-
clearWatchdogTailTimer();
|
|
816
|
-
if (cleanTerminalAssistantStopReceived || agentSettledReceived) startFinalDrain();
|
|
817
|
-
}
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
onChildEvent?.(event);
|
|
822
|
-
|
|
823
|
-
if (event.type === "tool_execution_end") {
|
|
824
|
-
clearActiveToolTimeout(event);
|
|
825
|
-
removeActiveToolCall(event);
|
|
826
|
-
return;
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
if (event.type === "tool_execution_start" && event.toolName) {
|
|
830
|
-
toolCount += 1;
|
|
831
|
-
armToolTimeout({ toolCallId: (event as { toolCallId?: unknown }).toolCallId, toolName: event.toolName });
|
|
832
|
-
recordActiveToolCall({ toolCallId: (event as { toolCallId?: unknown }).toolCallId, toolName: event.toolName, args: event.args });
|
|
833
|
-
if (event.toolName === "structured_output") {
|
|
834
|
-
structuredOutputToolInvoked = true;
|
|
835
|
-
structuredOutputMessageStartIndex = messages.length;
|
|
836
|
-
}
|
|
837
|
-
observedMutationAttempt = observedMutationAttempt || isMutatingTool(event.toolName, event.args, mutationTools);
|
|
838
|
-
const toolArgs = extractToolArgsPreview(event.args ?? {});
|
|
839
|
-
writeOutputLine(toolArgs ? `${event.toolName}: ${toolArgs}` : event.toolName);
|
|
840
|
-
return;
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
if ((event.type === "message_end" || event.type === "tool_result_end") && event.message) {
|
|
844
|
-
if (event.type === "tool_result_end") {
|
|
845
|
-
clearActiveToolTimeout(event);
|
|
846
|
-
removeActiveToolCall({
|
|
847
|
-
toolCallId: (event.message as { toolCallId?: unknown }).toolCallId ?? (event as { toolCallId?: unknown }).toolCallId,
|
|
848
|
-
toolName: (event.message as { toolName?: unknown }).toolName ?? event.toolName,
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
messages.push(event.message);
|
|
852
|
-
const text = extractTextFromContent(event.message.content);
|
|
853
|
-
if (text) writeOutputText(text);
|
|
854
|
-
|
|
855
|
-
if (childWatchdogConfig && event.type === "message_end") {
|
|
856
|
-
const next = applyChildWatchdogMessage(childWatchdogState, event.message);
|
|
857
|
-
if (next) updateChildWatchdogState(next);
|
|
858
|
-
}
|
|
859
|
-
if (event.type !== "message_end" || event.message.role !== "assistant") return;
|
|
860
|
-
const hasToolCall = assistantStartsToolCall(event.message);
|
|
861
|
-
if (event.message.model) {
|
|
862
|
-
model = event.message.model;
|
|
863
|
-
if (expectedModelForVerification && !hasToolCall) {
|
|
864
|
-
const modelVerificationError = formatSubagentModelVerificationError(expectedModelForVerification, event.message.model, modelVerificationRegistry);
|
|
865
|
-
if (modelVerificationError && !error) error = modelVerificationError;
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
if (event.message.errorMessage) assistantError = event.message.errorMessage;
|
|
869
|
-
const eventUsage = event.message.usage;
|
|
870
|
-
if (eventUsage) {
|
|
871
|
-
usage.turns++;
|
|
872
|
-
usage.input += eventUsage.input ?? eventUsage.inputTokens ?? 0;
|
|
873
|
-
usage.output += eventUsage.output ?? eventUsage.outputTokens ?? 0;
|
|
874
|
-
usage.cacheRead += eventUsage.cacheRead ?? 0;
|
|
875
|
-
usage.cacheWrite += eventUsage.cacheWrite ?? 0;
|
|
876
|
-
usage.cost += eventUsage.cost?.total ?? 0;
|
|
877
|
-
}
|
|
878
|
-
if (isTerminalAssistantStop(event.message)) {
|
|
879
|
-
if (!event.message.errorMessage && extractTextFromContent(event.message.content).trim()) assistantError = undefined;
|
|
880
|
-
cleanTerminalAssistantStopReceived ||= !event.message.errorMessage;
|
|
881
|
-
clearAllToolTimeouts();
|
|
882
|
-
activeToolCalls.clear();
|
|
883
|
-
activeToolKeysByName.clear();
|
|
884
|
-
refreshCurrentTool();
|
|
885
|
-
applyChildLifecycle(projectChildLifecycle(event, true, childLifecycleState));
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
};
|
|
889
|
-
|
|
890
|
-
// Guard both cases that can leave the parent waiting on `close` forever:
|
|
891
|
-
// a lingering stdio holder after `exit`, or a child that never exits.
|
|
892
|
-
const FINAL_STOP_GRACE_MS = 1000;
|
|
893
|
-
const HARD_KILL_MS = 3000;
|
|
894
|
-
let childExited = false;
|
|
895
|
-
let forcedTerminationSignal = false;
|
|
896
|
-
let cleanTerminalAssistantStopReceived = false;
|
|
897
|
-
let agentSettledReceived = false;
|
|
898
|
-
let compactionStartedReceived = false;
|
|
899
|
-
let afterCompactionSettlement = false;
|
|
900
|
-
let finalDrainTimer: NodeJS.Timeout | undefined;
|
|
901
|
-
let finalHardKillTimer: NodeJS.Timeout | undefined;
|
|
902
|
-
let watchdogTailTimer: NodeJS.Timeout | undefined;
|
|
903
|
-
let protocolHardKillTimer: NodeJS.Timeout | undefined;
|
|
904
|
-
let protocolError: ProtocolOutputLimit | undefined;
|
|
905
|
-
let settled = false;
|
|
906
|
-
applyChildLifecycle = (action: ChildLifecycleAction): void => {
|
|
907
|
-
if (action === "cancel-drain") {
|
|
908
|
-
if (finalDrainTimer) {
|
|
909
|
-
clearTimeout(finalDrainTimer);
|
|
910
|
-
finalDrainTimer = undefined;
|
|
911
|
-
}
|
|
912
|
-
if (finalHardKillTimer) {
|
|
913
|
-
clearTimeout(finalHardKillTimer);
|
|
914
|
-
finalHardKillTimer = undefined;
|
|
915
|
-
}
|
|
916
|
-
clearWatchdogTailTimer();
|
|
917
|
-
return;
|
|
918
|
-
}
|
|
919
|
-
if (action === "start-drain") startFinalDrain();
|
|
920
|
-
};
|
|
921
|
-
const failProtocol = (limit: ProtocolOutputLimit): void => {
|
|
922
|
-
if (protocolError) return;
|
|
923
|
-
protocolError = limit;
|
|
924
|
-
error = formatProtocolOutputLimit(limit);
|
|
925
|
-
if (!childExited) {
|
|
926
|
-
trySignalChild(child, "SIGTERM");
|
|
927
|
-
protocolHardKillTimer = setTimeout(() => {
|
|
928
|
-
if (!settled) trySignalChild(child, "SIGKILL");
|
|
929
|
-
}, 3000);
|
|
930
|
-
protocolHardKillTimer.unref?.();
|
|
931
|
-
}
|
|
932
|
-
};
|
|
933
|
-
const stdoutReader = createBoundedLineReader({
|
|
934
|
-
oversizedLineProjector: PI_AGGREGATE_EVENT_PROJECTOR,
|
|
935
|
-
onLine: processStdoutLine,
|
|
936
|
-
onLimit: failProtocol,
|
|
937
|
-
});
|
|
938
|
-
const stderrReader = createBoundedLineReader({
|
|
939
|
-
stream: "stderr",
|
|
940
|
-
maxPendingLineBytes: MAX_CHILD_STDERR_BYTES,
|
|
941
|
-
onLine: (line) => appendChildLine("subagent.child.stderr", line),
|
|
942
|
-
onLimit: (limit) => appendChildLine("subagent.child.stderr", formatProtocolOutputLimit(limit)),
|
|
943
|
-
});
|
|
944
|
-
const clearStdioGuard = attachPostExitStdioGuard(child, { idleMs: 2000, hardMs: 8000 });
|
|
945
|
-
child.stdout.on("data", (chunk: Buffer) => stdoutReader.push(chunk));
|
|
946
|
-
child.stderr.on("data", (chunk: Buffer) => {
|
|
947
|
-
stderrTail.push(chunk);
|
|
948
|
-
stderrReader.push(chunk);
|
|
949
|
-
outputStream.write(chunk);
|
|
950
|
-
orcaProgressTab?.append(chunk.toString("utf-8"));
|
|
951
|
-
});
|
|
952
|
-
registerInterrupt?.(() => {
|
|
953
|
-
if (settled || timedOut || stopped) return;
|
|
954
|
-
interrupted = true;
|
|
955
|
-
if (!error) error = "Interrupted. Waiting for explicit next action.";
|
|
956
|
-
trySignalChild(child, "SIGINT");
|
|
957
|
-
setTimeout(() => {
|
|
958
|
-
if (!settled && !timedOut && !stopped) trySignalChild(child, "SIGTERM");
|
|
959
|
-
}, 1000).unref?.();
|
|
960
|
-
});
|
|
961
|
-
const terminateForTimeout = (message: string): void => {
|
|
962
|
-
if (settled || timedOut || stopped) return;
|
|
963
|
-
timedOut = true;
|
|
964
|
-
// runPiStreaming's terminal result derives the timeout error from this
|
|
965
|
-
// message, so retain the tool-specific reason through finalization.
|
|
966
|
-
timeoutMessage = message;
|
|
967
|
-
interrupted = false;
|
|
968
|
-
error = message;
|
|
969
|
-
if (processTreeController) void processTreeController.terminate();
|
|
970
|
-
else trySignalChild(child, "SIGTERM");
|
|
971
|
-
};
|
|
972
|
-
let toolTimeoutSequence = 0;
|
|
973
|
-
const activeToolTimeouts = new Map<string, { toolName: string; timer: ReturnType<typeof setTimeout> }>();
|
|
974
|
-
const activeToolTimeoutKeysByName = new Map<string, string[]>();
|
|
975
|
-
const removeToolTimeoutKey = (key: string): void => {
|
|
976
|
-
const active = activeToolTimeouts.get(key);
|
|
977
|
-
if (!active) return;
|
|
978
|
-
clearTimeout(active.timer);
|
|
979
|
-
activeToolTimeouts.delete(key);
|
|
980
|
-
const keys = activeToolTimeoutKeysByName.get(active.toolName)?.filter((candidate) => candidate !== key) ?? [];
|
|
981
|
-
if (keys.length > 0) activeToolTimeoutKeysByName.set(active.toolName, keys);
|
|
982
|
-
else activeToolTimeoutKeysByName.delete(active.toolName);
|
|
983
|
-
};
|
|
984
|
-
const clearActiveToolTimeout = (event: { toolCallId?: unknown; toolName?: unknown }): void => {
|
|
985
|
-
const key = typeof event.toolCallId === "string" && event.toolCallId.length > 0
|
|
986
|
-
? `id:${event.toolCallId}`
|
|
987
|
-
: typeof event.toolName === "string"
|
|
988
|
-
? activeToolTimeoutKeysByName.get(event.toolName)?.[0]
|
|
989
|
-
: activeToolTimeouts.size === 1
|
|
990
|
-
? [...activeToolTimeouts.keys()][0]
|
|
991
|
-
: undefined;
|
|
992
|
-
if (key) removeToolTimeoutKey(key);
|
|
993
|
-
};
|
|
994
|
-
const clearAllToolTimeouts = (): void => {
|
|
995
|
-
for (const key of [...activeToolTimeouts.keys()]) removeToolTimeoutKey(key);
|
|
996
|
-
};
|
|
997
|
-
const armToolTimeout = (event: { toolCallId?: unknown; toolName: string }): void => {
|
|
998
|
-
const timeoutForTool = effectiveToolTimeoutMs(event.toolName, toolTimeoutMs);
|
|
999
|
-
if (timeoutForTool === undefined) return;
|
|
1000
|
-
const runRemaining = runDeadlineAt === undefined ? undefined : Math.max(0, runDeadlineAt - Date.now());
|
|
1001
|
-
if (runRemaining !== undefined && timeoutForTool >= runRemaining) return;
|
|
1002
|
-
const key = toolTimeoutCallKey(event, ++toolTimeoutSequence);
|
|
1003
|
-
const toolName = event.toolName;
|
|
1004
|
-
const timer = setTimeout(() => {
|
|
1005
|
-
removeToolTimeoutKey(key);
|
|
1006
|
-
terminateForTimeout(formatToolTimeoutMessage(toolName, timeoutForTool));
|
|
1007
|
-
}, timeoutForTool);
|
|
1008
|
-
timer.unref?.();
|
|
1009
|
-
activeToolTimeouts.set(key, { toolName, timer });
|
|
1010
|
-
const keys = activeToolTimeoutKeysByName.get(toolName) ?? [];
|
|
1011
|
-
keys.push(key);
|
|
1012
|
-
activeToolTimeoutKeysByName.set(toolName, keys);
|
|
1013
|
-
};
|
|
1014
|
-
registerTimeout?.(() => terminateForTimeout(timeoutMessage ?? "Subagent timed out."));
|
|
1015
|
-
registerStop?.(() => {
|
|
1016
|
-
if (settled || timedOut || stopped) return;
|
|
1017
|
-
stopped = true;
|
|
1018
|
-
interrupted = false;
|
|
1019
|
-
error = stopMessage ?? "Subagent stopped by user.";
|
|
1020
|
-
if (processTreeController) void processTreeController.terminate();
|
|
1021
|
-
else trySignalChild(child, "SIGTERM");
|
|
1022
|
-
});
|
|
1023
|
-
const clearDrainTimers = () => {
|
|
1024
|
-
clearAllToolTimeouts();
|
|
1025
|
-
if (finalDrainTimer) {
|
|
1026
|
-
clearTimeout(finalDrainTimer);
|
|
1027
|
-
finalDrainTimer = undefined;
|
|
1028
|
-
}
|
|
1029
|
-
if (finalHardKillTimer) {
|
|
1030
|
-
clearTimeout(finalHardKillTimer);
|
|
1031
|
-
finalHardKillTimer = undefined;
|
|
1032
|
-
}
|
|
1033
|
-
clearWatchdogTailTimer();
|
|
1034
|
-
if (protocolHardKillTimer) {
|
|
1035
|
-
clearTimeout(protocolHardKillTimer);
|
|
1036
|
-
protocolHardKillTimer = undefined;
|
|
1037
|
-
}
|
|
1038
|
-
};
|
|
1039
|
-
function startFinalDrain(): void {
|
|
1040
|
-
if (childWatchdogIsActive(childWatchdogState)) {
|
|
1041
|
-
armWatchdogTail();
|
|
1042
|
-
return;
|
|
1043
|
-
}
|
|
1044
|
-
if (childExited || finalDrainTimer || settled) return;
|
|
1045
|
-
finalDrainTimer = setTimeout(() => {
|
|
1046
|
-
if (settled) return;
|
|
1047
|
-
const termSent = trySignalChild(child, "SIGTERM");
|
|
1048
|
-
if (!termSent) return;
|
|
1049
|
-
forcedTerminationSignal = true;
|
|
1050
|
-
if (!cleanTerminalAssistantStopReceived && !agentSettledReceived && !error && !assistantError) {
|
|
1051
|
-
error = `Subagent process did not exit within ${FINAL_STOP_GRACE_MS}ms after its terminal event. Forcing termination.`;
|
|
1052
|
-
}
|
|
1053
|
-
finalHardKillTimer = setTimeout(() => {
|
|
1054
|
-
if (settled) return;
|
|
1055
|
-
forcedTerminationSignal = trySignalChild(child, "SIGKILL") || forcedTerminationSignal;
|
|
1056
|
-
}, HARD_KILL_MS);
|
|
1057
|
-
finalHardKillTimer.unref?.();
|
|
1058
|
-
}, FINAL_STOP_GRACE_MS);
|
|
1059
|
-
finalDrainTimer.unref?.();
|
|
1060
|
-
}
|
|
1061
|
-
function clearWatchdogTailTimer(): void {
|
|
1062
|
-
if (watchdogTailTimer) {
|
|
1063
|
-
clearTimeout(watchdogTailTimer);
|
|
1064
|
-
watchdogTailTimer = undefined;
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
function armWatchdogTail(): void {
|
|
1068
|
-
if ((!cleanTerminalAssistantStopReceived && !agentSettledReceived) || watchdogTailTimer || settled) return;
|
|
1069
|
-
watchdogTailTimer = setTimeout(() => {
|
|
1070
|
-
watchdogTailTimer = undefined;
|
|
1071
|
-
updateChildWatchdogState({
|
|
1072
|
-
phase: "stale",
|
|
1073
|
-
seq: (childWatchdogState?.seq ?? 0) + 1,
|
|
1074
|
-
lastUpdate: Date.now(),
|
|
1075
|
-
reason: "child watchdog tail timeout",
|
|
1076
|
-
timedOut: true,
|
|
1077
|
-
});
|
|
1078
|
-
startFinalDrain();
|
|
1079
|
-
}, childWatchdogConfig?.watchdogTailTimeoutMs ?? 120_000);
|
|
1080
|
-
watchdogTailTimer.unref?.();
|
|
1081
|
-
}
|
|
1082
|
-
child.on("exit", () => {
|
|
1083
|
-
childExited = true;
|
|
1084
|
-
clearDrainTimers();
|
|
1085
|
-
});
|
|
1086
|
-
child.on("close", async (exitCode, signal) => {
|
|
1087
|
-
settled = true;
|
|
1088
|
-
const processCloseObservedAt = Date.now();
|
|
1089
|
-
const processTree = processTreeController
|
|
1090
|
-
? await processTreeController.finishAfterWriterClose()
|
|
1091
|
-
: { state: "unknown" as const, reason: "verification-failed" as const, diagnostic: "Writer PID was unavailable." };
|
|
1092
|
-
try {
|
|
1093
|
-
onWriterProcess?.({ state: "none" });
|
|
1094
|
-
} catch {
|
|
1095
|
-
// The runner still owns and releases the lease during finalization.
|
|
1096
|
-
}
|
|
1097
|
-
registerInterrupt?.(undefined);
|
|
1098
|
-
registerTimeout?.(undefined);
|
|
1099
|
-
registerStop?.(undefined);
|
|
1100
|
-
clearDrainTimers();
|
|
1101
|
-
clearStdioGuard();
|
|
1102
|
-
stdoutReader.end();
|
|
1103
|
-
stderrReader.end();
|
|
1104
|
-
outputStream.end();
|
|
1105
|
-
const stderr = stderrTail.text();
|
|
1106
|
-
const finalOutput = getFinalOutput(messages) || rawStdoutTail.text().trim();
|
|
1107
|
-
const finalError = error ?? assistantError;
|
|
1108
|
-
const forcedDrainAfterFinalSuccess = Boolean(forcedTerminationSignal || signal) && (cleanTerminalAssistantStopReceived || agentSettledReceived) && !finalError;
|
|
1109
|
-
const forcedDrainAfterEmptyTerminal = forcedDrainAfterFinalSuccess && hasEmptyTerminalAssistantResponse(messages);
|
|
1110
|
-
const forcedDrainError = forcedDrainAfterEmptyTerminal && stderr.trim()
|
|
1111
|
-
? stderr.trim()
|
|
1112
|
-
: undefined;
|
|
1113
|
-
const signalError = isUnexplainedProcessSignal({
|
|
1114
|
-
processSignal: signal,
|
|
1115
|
-
interrupted,
|
|
1116
|
-
timedOut,
|
|
1117
|
-
stopped,
|
|
1118
|
-
forcedDrainAfterFinalSuccess: forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal,
|
|
1119
|
-
}) ? formatProcessSignalError(signal!) : undefined;
|
|
1120
|
-
resolve(omitUndefinedProperties({
|
|
1121
|
-
stderr,
|
|
1122
|
-
exitCode: timedOut || stopped ? 1 : interrupted || (forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal) ? 0 : forcedTerminationSignal || signal ? (exitCode ?? 1) : exitCode,
|
|
1123
|
-
messages,
|
|
1124
|
-
usage,
|
|
1125
|
-
toolCount,
|
|
1126
|
-
durationMs: Date.now() - startedAt,
|
|
1127
|
-
model,
|
|
1128
|
-
error: stopped ? (stopMessage ?? "Subagent stopped by user.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") : interrupted || (forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal) ? undefined : finalError ?? forcedDrainError ?? signalError,
|
|
1129
|
-
protocolError,
|
|
1130
|
-
finalOutput: (timedOut || stopped) && !finalOutput.trim() ? (stopped ? stopMessage ?? "Subagent stopped by user." : timeoutMessage ?? "Subagent timed out.") : finalOutput,
|
|
1131
|
-
outputState: finalOutput.trim() ? "present" : "absent",
|
|
1132
|
-
interrupted,
|
|
1133
|
-
timedOut,
|
|
1134
|
-
stopped,
|
|
1135
|
-
observedMutationAttempt,
|
|
1136
|
-
structuredOutputToolInvoked,
|
|
1137
|
-
structuredOutputMessageStartIndex,
|
|
1138
|
-
watchdog: childWatchdogState,
|
|
1139
|
-
processInstanceId,
|
|
1140
|
-
processCloseObservedAt,
|
|
1141
|
-
processSignal: signal,
|
|
1142
|
-
processTree,
|
|
1143
|
-
currentTool,
|
|
1144
|
-
currentToolArgs,
|
|
1145
|
-
currentPath,
|
|
1146
|
-
afterCompactionSettlement: afterCompactionSettlement || undefined,
|
|
1147
|
-
}));
|
|
1148
|
-
});
|
|
1149
|
-
|
|
1150
|
-
child.on("error", (spawnError) => {
|
|
1151
|
-
settled = true;
|
|
1152
|
-
try {
|
|
1153
|
-
onWriterProcess?.({ state: "none" });
|
|
1154
|
-
} catch {
|
|
1155
|
-
// The runner still owns and releases the lease during finalization.
|
|
1156
|
-
}
|
|
1157
|
-
registerInterrupt?.(undefined);
|
|
1158
|
-
registerTimeout?.(undefined);
|
|
1159
|
-
registerStop?.(undefined);
|
|
1160
|
-
clearDrainTimers();
|
|
1161
|
-
clearStdioGuard();
|
|
1162
|
-
stdoutReader.end();
|
|
1163
|
-
stderrReader.end();
|
|
1164
|
-
outputStream.end();
|
|
1165
|
-
const stderr = stderrTail.text();
|
|
1166
|
-
const finalOutput = getFinalOutput(messages) || rawStdoutTail.text().trim();
|
|
1167
|
-
const spawnErrorMessage = spawnError instanceof Error ? spawnError.message : String(spawnError);
|
|
1168
|
-
resolve(omitUndefinedProperties({ stderr, exitCode: 1, messages, usage, toolCount, durationMs: Date.now() - startedAt, model, error: stopped ? (stopMessage ?? "Subagent stopped by user.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") : error ?? assistantError ?? spawnErrorMessage, protocolError, finalOutput: (timedOut || stopped) && !finalOutput.trim() ? (stopped ? stopMessage ?? "Subagent stopped by user." : timeoutMessage ?? "Subagent timed out.") : finalOutput, outputState: finalOutput.trim() ? "present" : "absent", timedOut, stopped, observedMutationAttempt, structuredOutputToolInvoked, structuredOutputMessageStartIndex, watchdog: childWatchdogState, processInstanceId, processTree: { state: "unknown", reason: "verification-failed", diagnostic: spawnErrorMessage } }));
|
|
1169
|
-
});
|
|
1170
|
-
});
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
550
|
function resolvePiPackageRootFallback(): string {
|
|
1174
551
|
const root = resolveInstalledPiPackageRoot();
|
|
1175
552
|
if (root) return root;
|
|
@@ -1291,15 +668,17 @@ interface SingleStepContext {
|
|
|
1291
668
|
flatIndex: number;
|
|
1292
669
|
flatStepCount: number;
|
|
1293
670
|
outputFile: string;
|
|
1294
|
-
steerInboxDir?: string;
|
|
1295
|
-
steerCapabilityPath?: string;
|
|
1296
|
-
steerAckDir?: string;
|
|
1297
671
|
transcriptPath?: string;
|
|
1298
672
|
piPackageRoot?: string;
|
|
1299
|
-
|
|
673
|
+
/** Factory the runner creates this step's child session through. */
|
|
674
|
+
childSessions: ChildSessionFactory;
|
|
675
|
+
/** The launching executor's own child runtime; nested route, depth, and ceilings come from here. */
|
|
676
|
+
inheritedChildRuntime?: InheritedChildRuntime;
|
|
1300
677
|
registerInterrupt?: (interrupt: (() => void) | undefined) => void;
|
|
1301
678
|
registerTimeout?: (interrupt: (() => void) | undefined) => void;
|
|
1302
679
|
registerStop?: (stop: (() => void) | undefined) => void;
|
|
680
|
+
/** Receives the live child's steer handler while its session runs. */
|
|
681
|
+
registerSteer?: (steer: StepSteerHandler | undefined) => void;
|
|
1303
682
|
timeoutSignal?: AbortSignal;
|
|
1304
683
|
stopSignal?: AbortSignal;
|
|
1305
684
|
timeoutMessage?: string;
|
|
@@ -1315,18 +694,20 @@ interface SingleStepContext {
|
|
|
1315
694
|
runFanoutBudget?: RunFanoutBudgetDescriptor;
|
|
1316
695
|
onAttemptStart?: (attempt: { model?: string; thinking?: string; contextLimit?: number }) => void;
|
|
1317
696
|
onChildEvent?: (event: ChildEvent) => void;
|
|
1318
|
-
onWriterProcess?: (writer: { state: "none" | "spawning" } | { state: "running"; pid: number }) => void;
|
|
1319
697
|
onExternalProcess?: (process: ExternalProcessStatus) => void;
|
|
1320
698
|
onExternalJob?: (status: ExternalJobStatus) => void;
|
|
1321
699
|
skipAcceptance?: () => boolean;
|
|
1322
|
-
|
|
700
|
+
/** Authoritative owner decision after event delivery; undefined includes incomplete run-wide usage. */
|
|
701
|
+
usageBudgetExhausted?: () => boolean | undefined;
|
|
702
|
+
/** Existing run-owned budget configuration; cost allowance is not settled by the live token ledger. */
|
|
703
|
+
usageBudget?: UsageBudgetConfig;
|
|
1323
704
|
/** False when sibling work in the same Git worktree could have caused the tracked diff. */
|
|
1324
705
|
trackedMutationEvidenceForCompletionGuard?: boolean;
|
|
1325
706
|
orcaProgressTab?: OrcaProgressTab;
|
|
1326
707
|
}
|
|
1327
708
|
|
|
1328
709
|
/** Run a single pi agent step, returning output and metadata */
|
|
1329
|
-
async function runSingleStepInner(
|
|
710
|
+
export async function runSingleStepInner(
|
|
1330
711
|
step: SubagentStep,
|
|
1331
712
|
ctx: SingleStepContext,
|
|
1332
713
|
): Promise<StepResult & { completionGuardTriggered?: boolean }> {
|
|
@@ -1421,13 +802,11 @@ async function runSingleStepInner(
|
|
|
1421
802
|
model: step.model,
|
|
1422
803
|
modelCandidates: step.modelCandidates,
|
|
1423
804
|
mcpDirectTools: step.mcpDirectTools,
|
|
1424
|
-
mcpConfig: step.mcpConfig,
|
|
1425
|
-
runtimeServerNames: step.runtimeServerNames,
|
|
1426
805
|
cwd: step.cwd ?? ctx.cwd,
|
|
1427
806
|
requireReadTool: Boolean(step.skills?.length),
|
|
1428
807
|
structuredOutput: Boolean(effectiveStructuredOutput),
|
|
1429
808
|
capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
|
|
1430
|
-
inheritedCapabilityCeiling:
|
|
809
|
+
inheritedCapabilityCeiling: ctx.inheritedChildRuntime?.capabilityCeiling,
|
|
1431
810
|
permissionRules: step.permissionRules,
|
|
1432
811
|
}));
|
|
1433
812
|
const contractTools = resolvedTaskToolPlan.explicitToolAllowlist ? resolvedTaskToolPlan.effectiveToolAllowlist : undefined;
|
|
@@ -1456,8 +835,8 @@ async function runSingleStepInner(
|
|
|
1456
835
|
// Derive from the pre-acceptance task so internal acceptance/recovery
|
|
1457
836
|
// instructions never leak into the display name.
|
|
1458
837
|
const childSessionName = step.sessionName ?? deriveChildSessionName({ agent: step.agent, task, label: step.label });
|
|
1459
|
-
if (step.effectiveAcceptance) {
|
|
1460
|
-
const acceptancePrompt = formatAcceptancePrompt(step.effectiveAcceptance, { reportOptional:
|
|
838
|
+
if (step.effectiveAcceptance && (step.runner?.type === "external-cli" || step.runner?.type === "external-job")) {
|
|
839
|
+
const acceptancePrompt = formatAcceptancePrompt(step.effectiveAcceptance, { reportOptional: isAgentContract(step.agentContract), structuredOutput: Boolean(step.structuredOutput?.acceptanceReportPath) });
|
|
1461
840
|
if (acceptancePrompt) task = `${task}\n${acceptancePrompt}`;
|
|
1462
841
|
}
|
|
1463
842
|
const sessionEnabled = Boolean(step.sessionFile) || ctx.sessionEnabled;
|
|
@@ -1634,12 +1013,10 @@ async function runSingleStepInner(
|
|
|
1634
1013
|
let capabilityAudit: import("../shared/capability-ceiling.ts").SubagentCapabilityAudit | undefined;
|
|
1635
1014
|
let launchResolvedExtensions = step.launchResolvedExtensions;
|
|
1636
1015
|
const modelAttempts: ModelAttempt[] = [];
|
|
1637
|
-
const writerProcesses: PiWriterProcessInstanceExitV1[] = [];
|
|
1638
|
-
let writerAttemptCount = 0;
|
|
1639
1016
|
const attemptNotes: string[] = [];
|
|
1640
1017
|
const eventsPath = path.join(path.dirname(ctx.outputFile), "events.jsonl");
|
|
1641
1018
|
let finalRequiredOutputMissing: boolean | undefined;
|
|
1642
|
-
let finalResult:
|
|
1019
|
+
let finalResult: RunChildSessionResult | undefined;
|
|
1643
1020
|
let finalOutputSnapshot: SingleOutputSnapshot | undefined;
|
|
1644
1021
|
let structuredAcceptanceReport: unknown;
|
|
1645
1022
|
let structuredAcceptanceReportError: string | undefined;
|
|
@@ -1651,24 +1028,44 @@ async function runSingleStepInner(
|
|
|
1651
1028
|
let finalMutationEvidence = collectTrackedMutationEvidence(mutationSnapshot, step.cwd ?? ctx.cwd);
|
|
1652
1029
|
|
|
1653
1030
|
let modelIndex = 0;
|
|
1654
|
-
let startupAttemptIndex = 0;
|
|
1655
|
-
// Escalated to "file" after an unexplained zero-activity startup failure so
|
|
1656
|
-
// retries keep the task text out of argv (endpoint pre-exec scans may deny it).
|
|
1657
|
-
let taskDeliveryOverride: SubagentTaskDelivery | undefined;
|
|
1658
1031
|
let contextOverflow = false;
|
|
1659
1032
|
let launchWarningsEmitted = false;
|
|
1660
|
-
let
|
|
1033
|
+
let recoveryState: LogicalRecoveryState = "unused";
|
|
1034
|
+
let readonlyContinuation: RunChildSessionInput["readonlyContinuation"];
|
|
1035
|
+
const continuationBudget = () => {
|
|
1036
|
+
if (step.toolBudget) return "tool-budget-configured" as const;
|
|
1037
|
+
// Refresh the authoritative run ledger, already fed synchronously by onChildEvent.
|
|
1038
|
+
const exhausted = ctx.usageBudgetExhausted?.();
|
|
1039
|
+
if (exhausted === true) return "exhausted" as const;
|
|
1040
|
+
if (!ctx.usageBudget) return "unconfigured" as const;
|
|
1041
|
+
if (ctx.usageBudget.costUsd || !ctx.onChildEvent || exhausted !== false) return "unknown" as const;
|
|
1042
|
+
return "available" as const;
|
|
1043
|
+
};
|
|
1044
|
+
const lifecycleAllowsContinuation = () => !ctx.timeoutSignal?.aborted && !ctx.stopSignal?.aborted
|
|
1045
|
+
&& !ctx.skipAcceptance?.() && (ctx.deadlineAt === undefined || Date.now() < ctx.deadlineAt);
|
|
1046
|
+
const canContinue = () => lifecycleAllowsContinuation() && ["available", "unconfigured"].includes(continuationBudget());
|
|
1047
|
+
const failContinuationLaunch = (candidate: string | undefined, error: unknown) => {
|
|
1048
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1049
|
+
modelAttempts.push({ model: candidate ?? "default", success: false, exitCode: 1, error: message });
|
|
1050
|
+
if (candidate) attemptedModels.push(candidate);
|
|
1051
|
+
if (finalResult) finalResult = { ...finalResult, exitCode: 1, error: message };
|
|
1052
|
+
};
|
|
1661
1053
|
let nextAttemptTask = task;
|
|
1662
1054
|
modelAttemptsLoop: while (modelIndex < candidates.length) {
|
|
1663
1055
|
if (ctx.timeoutSignal?.aborted || ctx.stopSignal?.aborted || ctx.skipAcceptance?.()) break;
|
|
1664
|
-
|
|
1056
|
+
if (readonlyContinuation && !canContinue()) break;
|
|
1057
|
+
const recoveringAbort = recoveryState === "abort-recovery";
|
|
1665
1058
|
const attemptTask = nextAttemptTask;
|
|
1666
1059
|
const candidate = candidates[modelIndex];
|
|
1667
1060
|
const expectedModelForVerification = candidate && !(step.skipPrimaryModelVerification && modelIndex === 0) ? candidate : undefined;
|
|
1668
1061
|
try {
|
|
1669
|
-
assertThinkingWithinCeiling({ model: candidate, configThinking: step.thinking, ceiling: step.thinkingCeiling
|
|
1062
|
+
assertThinkingWithinCeiling({ model: candidate, configThinking: step.thinking, ceiling: step.thinkingCeiling, agent: step.agent, runId: ctx.id });
|
|
1670
1063
|
} catch (error) {
|
|
1671
1064
|
const message = error instanceof Error ? error.message : String(error);
|
|
1065
|
+
if (readonlyContinuation) {
|
|
1066
|
+
failContinuationLaunch(candidate, error);
|
|
1067
|
+
break modelAttemptsLoop;
|
|
1068
|
+
}
|
|
1672
1069
|
return omitUndefinedProperties({ agent: step.agent, output: message, error: message, exitCode: 1, context: step.context, thinkingCeiling: step.thinkingCeiling });
|
|
1673
1070
|
}
|
|
1674
1071
|
ctx.onAttemptStart?.(omitUndefinedProperties({
|
|
@@ -1693,64 +1090,28 @@ async function runSingleStepInner(
|
|
|
1693
1090
|
childIndex: ctx.flatIndex,
|
|
1694
1091
|
})
|
|
1695
1092
|
: undefined;
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
systemPromptMode: step.systemPromptMode,
|
|
1719
|
-
mcpDirectTools: step.mcpDirectTools,
|
|
1720
|
-
mcpConfig: step.mcpConfig,
|
|
1721
|
-
runtimeServerNames: step.runtimeServerNames,
|
|
1722
|
-
capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
|
|
1723
|
-
cwd: step.cwd ?? ctx.cwd,
|
|
1724
|
-
promptFileStem: step.agent,
|
|
1725
|
-
intercomSessionName: ctx.childIntercomTarget,
|
|
1726
|
-
sessionName: childSessionName,
|
|
1727
|
-
orchestratorIntercomTarget: ctx.orchestratorIntercomTarget,
|
|
1728
|
-
runId: ctx.id,
|
|
1729
|
-
childAgentName: step.agent,
|
|
1730
|
-
childIndex: ctx.flatIndex,
|
|
1731
|
-
parentEventSink: ctx.nestedRoute?.eventSink,
|
|
1732
|
-
parentControlInbox: ctx.nestedRoute?.controlInbox,
|
|
1733
|
-
parentRootRunId: ctx.nestedRoute?.rootRunId,
|
|
1734
|
-
parentCapabilityToken: ctx.nestedRoute?.capabilityToken,
|
|
1735
|
-
runFanoutBudget: ctx.runFanoutBudget ? {
|
|
1736
|
-
...ctx.runFanoutBudget,
|
|
1737
|
-
...(step.runFanoutPath ? { parentPath: `${ctx.runFanoutBudget.parentPath ? `${ctx.runFanoutBudget.parentPath}/` : ""}${step.runFanoutPath}` } : {}),
|
|
1738
|
-
} : undefined,
|
|
1739
|
-
steerInboxDir: ctx.steerInboxDir,
|
|
1740
|
-
steerCapabilityPath: ctx.steerCapabilityPath,
|
|
1741
|
-
steerAckDir: ctx.steerAckDir,
|
|
1742
|
-
structuredOutput: effectiveStructuredOutput,
|
|
1743
|
-
toolBudget: step.toolBudget,
|
|
1744
|
-
permissionRules: step.permissionRules,
|
|
1745
|
-
permissionAuditPath: step.permissionRules && ctx.artifactsDir
|
|
1746
|
-
? path.join(ctx.artifactsDir, "permission-audit", `${ctx.id}-${ctx.flatIndex}.jsonl`)
|
|
1747
|
-
: undefined,
|
|
1748
|
-
childWatchdog,
|
|
1749
|
-
waitToolEnabled: step.waitToolEnabled,
|
|
1750
|
-
waitToolDefaultTimeoutMs: step.waitToolDefaultTimeoutMs,
|
|
1751
|
-
thinkingCeiling: step.thinkingCeiling,
|
|
1752
|
-
extensionBindings,
|
|
1753
|
-
}));
|
|
1093
|
+
let watchdogSink: ((event: ChildWatchdogStatusEvent) => void) | undefined;
|
|
1094
|
+
let launch: ReturnType<typeof buildRunnerChildLaunch>;
|
|
1095
|
+
try {
|
|
1096
|
+
launch = buildRunnerChildLaunch(step, ctx, {
|
|
1097
|
+
sessionEnabled,
|
|
1098
|
+
sessionDir,
|
|
1099
|
+
model: candidate,
|
|
1100
|
+
sessionName: childSessionName,
|
|
1101
|
+
structuredOutput: effectiveStructuredOutput,
|
|
1102
|
+
childWatchdog,
|
|
1103
|
+
watchdogStatus: (event) => watchdogSink?.(event),
|
|
1104
|
+
});
|
|
1105
|
+
} catch (error) {
|
|
1106
|
+
if (!readonlyContinuation) throw error;
|
|
1107
|
+
failContinuationLaunch(candidate, error);
|
|
1108
|
+
break modelAttemptsLoop;
|
|
1109
|
+
}
|
|
1110
|
+
if (effectiveStructuredOutput && launch.config.structuredOutput) {
|
|
1111
|
+
// The runner reads the value back from the runtime's files after the run.
|
|
1112
|
+
launch.config.structuredOutput.capture = createStructuredOutputFileCapture(effectiveStructuredOutput);
|
|
1113
|
+
}
|
|
1114
|
+
const { warnings, capabilityAudit: attemptCapabilityAudit } = launch;
|
|
1754
1115
|
if (!launchWarningsEmitted && warnings.length > 0) {
|
|
1755
1116
|
for (const warning of warnings) console.warn(`[pi-subagents] ${warning}`);
|
|
1756
1117
|
launchWarningsEmitted = true;
|
|
@@ -1766,13 +1127,11 @@ async function runSingleStepInner(
|
|
|
1766
1127
|
model: step.model,
|
|
1767
1128
|
modelCandidates: step.modelCandidates,
|
|
1768
1129
|
mcpDirectTools: step.mcpDirectTools,
|
|
1769
|
-
mcpConfig: step.mcpConfig,
|
|
1770
|
-
runtimeServerNames: step.runtimeServerNames,
|
|
1771
1130
|
cwd: step.cwd ?? ctx.cwd,
|
|
1772
1131
|
requireReadTool: Boolean(step.skills?.length),
|
|
1773
1132
|
structuredOutput: Boolean(effectiveStructuredOutput),
|
|
1774
1133
|
capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
|
|
1775
|
-
inheritedCapabilityCeiling:
|
|
1134
|
+
inheritedCapabilityCeiling: ctx.inheritedChildRuntime?.capabilityCeiling,
|
|
1776
1135
|
permissionRules: step.permissionRules,
|
|
1777
1136
|
}));
|
|
1778
1137
|
launchResolvedExtensions = projectLaunchResolvedChildExtensions(toolPlan);
|
|
@@ -1801,59 +1160,53 @@ async function runSingleStepInner(
|
|
|
1801
1160
|
}));
|
|
1802
1161
|
}
|
|
1803
1162
|
capabilityAudit = attemptCapabilityAudit;
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
ctx.
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
ctx.
|
|
1163
|
+
// Each attempt rewrites the step output log; synchronous appends keep a
|
|
1164
|
+
// retried attempt from interleaving with the previous attempt's flush.
|
|
1165
|
+
fs.writeFileSync(ctx.outputFile, "", "utf-8");
|
|
1166
|
+
const run = await runChildSession(omitUndefinedProperties({
|
|
1167
|
+
factory: ctx.childSessions,
|
|
1168
|
+
launch,
|
|
1169
|
+
collectReadonlyEvidence: true,
|
|
1170
|
+
readonlyContinuation,
|
|
1171
|
+
canContinue,
|
|
1172
|
+
prompt: `Task: ${attemptTask}`,
|
|
1173
|
+
childWatchdog,
|
|
1174
|
+
childEventContext: { runId: ctx.id, stepIndex: ctx.flatIndex, agent: step.agent },
|
|
1175
|
+
appendChildEvent: (event) => appendDiagnosticJsonl(eventsPath, JSON.stringify(event), typeof event.type === "string" ? event.type : undefined),
|
|
1176
|
+
writeOutputLine: (line) => {
|
|
1177
|
+
try {
|
|
1178
|
+
fs.appendFileSync(ctx.outputFile, `${line}\n`, "utf-8");
|
|
1179
|
+
} catch {
|
|
1180
|
+
// The output log is observability only.
|
|
1181
|
+
}
|
|
1182
|
+
ctx.orcaProgressTab?.append(`${line}\n`);
|
|
1183
|
+
},
|
|
1184
|
+
registerInterrupt: ctx.registerInterrupt,
|
|
1185
|
+
registerTimeout: ctx.registerTimeout,
|
|
1186
|
+
registerStop: ctx.registerStop,
|
|
1187
|
+
registerSteer: ctx.registerSteer,
|
|
1188
|
+
registerWatchdogStatus: (sink) => { watchdogSink = sink; },
|
|
1189
|
+
timeoutMessage: ctx.timeoutMessage,
|
|
1190
|
+
stopMessage: ctx.stopMessage,
|
|
1191
|
+
onChildEvent: ctx.onChildEvent,
|
|
1816
1192
|
transcriptWriter,
|
|
1817
|
-
ctx.
|
|
1818
|
-
ctx.
|
|
1819
|
-
ctx.registerStop,
|
|
1820
|
-
ctx.stopMessage,
|
|
1821
|
-
ctx.onWriterProcess,
|
|
1822
|
-
ctx.toolTimeoutMs,
|
|
1823
|
-
ctx.deadlineAt,
|
|
1824
|
-
ctx.orcaProgressTab,
|
|
1193
|
+
toolTimeoutMs: ctx.toolTimeoutMs,
|
|
1194
|
+
runDeadlineAt: ctx.deadlineAt,
|
|
1825
1195
|
expectedModelForVerification,
|
|
1826
|
-
step.modelVerificationRegistry,
|
|
1827
|
-
step.
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
attempt: writerAttemptCount - 1,
|
|
1834
|
-
closeObservedAt: run.processCloseObservedAt,
|
|
1835
|
-
exitCode: run.exitCode,
|
|
1836
|
-
signal: run.processSignal ?? null,
|
|
1837
|
-
processTree: run.processTree,
|
|
1838
|
-
});
|
|
1839
|
-
}
|
|
1840
|
-
const toolAvailabilityError = run.exitCode === 0 && !run.error
|
|
1841
|
-
? readChildToolDiagnosticError(toolDiagnosticPath)
|
|
1842
|
-
: undefined;
|
|
1843
|
-
const runtimeAcknowledgedExtensions = readRuntimeAcknowledgedExtensions(runtimeAcknowledgedExtensionsPath);
|
|
1844
|
-
cleanupTempDir(tempDir);
|
|
1196
|
+
modelVerificationRegistry: step.modelVerificationRegistry,
|
|
1197
|
+
modelResponseAliases: step.modelResponseAliases,
|
|
1198
|
+
mutationTools: step.mutationTools,
|
|
1199
|
+
}));
|
|
1200
|
+
const toolDiagnostic = run.exitCode === 0 && !run.error ? launch.capture.toolDiagnostic() : undefined;
|
|
1201
|
+
const toolAvailabilityError = toolDiagnostic ? formatChildToolDiagnostic(toolDiagnostic) : undefined;
|
|
1202
|
+
const runtimeAcknowledgedExtensions = launch.capture.runtimeAcknowledgedExtensions();
|
|
1845
1203
|
const midToolExitError = run.currentTool
|
|
1846
1204
|
&& isOrdinaryToolForMidToolExit(run.currentTool)
|
|
1847
1205
|
&& !run.interrupted
|
|
1848
1206
|
&& !run.timedOut
|
|
1849
1207
|
&& !run.stopped
|
|
1850
|
-
&& !run.protocolError
|
|
1851
1208
|
&& !toolAvailabilityError
|
|
1852
|
-
? formatMidToolExitError({
|
|
1853
|
-
toolName: run.currentTool,
|
|
1854
|
-
exitCode: run.exitCode,
|
|
1855
|
-
processSignal: run.processSignal,
|
|
1856
|
-
})
|
|
1209
|
+
? formatMidToolExitError({ toolName: run.currentTool })
|
|
1857
1210
|
: undefined;
|
|
1858
1211
|
|
|
1859
1212
|
let structuredOutput: unknown;
|
|
@@ -1896,7 +1249,7 @@ async function runSingleStepInner(
|
|
|
1896
1249
|
&& (!hiddenError?.hasError || hasEmptyTerminalAssistantResponse(run.messages))
|
|
1897
1250
|
? formatEmptyTerminalAssistantResponseError(run.messages)
|
|
1898
1251
|
: undefined;
|
|
1899
|
-
const completionGuardEnabled =
|
|
1252
|
+
const completionGuardEnabled = isAgentContract(step.agentContract) ? step.completionGuard === true : step.completionGuard !== false;
|
|
1900
1253
|
const completionToolPlan = resolvedTaskToolPlan;
|
|
1901
1254
|
const completionTools = completionToolPlan ? (completionToolPlan.explicitToolAllowlist ? completionToolPlan.effectiveToolAllowlist : undefined) : step.tools;
|
|
1902
1255
|
const mutationEvidence = collectTrackedMutationEvidence(mutationSnapshot, step.cwd ?? ctx.cwd);
|
|
@@ -1915,14 +1268,27 @@ async function runSingleStepInner(
|
|
|
1915
1268
|
}))
|
|
1916
1269
|
: undefined;
|
|
1917
1270
|
const mutationAttemptObserved = run.observedMutationAttempt === true || completionMutationEvidence?.attemptedMutation === true;
|
|
1271
|
+
let arbitration = { triggered: completionGuard?.triggered === true && !mutationAttemptObserved, rescued: false };
|
|
1272
|
+
if (arbitration.triggered) {
|
|
1273
|
+
const modelContext = launch.capture.completionIntentContext?.();
|
|
1274
|
+
arbitration = await arbitrateCompletionGuardRescue({
|
|
1275
|
+
guardTriggered: true,
|
|
1276
|
+
task: taskForCompletionGuard,
|
|
1277
|
+
// Construct lazily too: the shared gate refuses overlength tasks before
|
|
1278
|
+
// registry/auth/model work. The child has already shut down normally.
|
|
1279
|
+
arbiter: modelContext ? async (task) => createTaskMutationArbiter(modelContext)?.(task) ?? "unavailable" : undefined,
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1918
1282
|
const completionEvidence = planCompletionEvidence({
|
|
1919
1283
|
guard: completionGuard,
|
|
1284
|
+
guardTriggered: arbitration.triggered,
|
|
1285
|
+
arbiterRescued: arbitration.rescued,
|
|
1920
1286
|
completionGuardEnabled,
|
|
1921
1287
|
mutationCapable: hasMutationToolCapability(completionTools, completionToolPlan?.effectiveMcpTools ?? step.mcpDirectTools),
|
|
1922
1288
|
implementationMutationExpected: expectsImplementationMutation(step.agent, taskForCompletionGuard),
|
|
1923
1289
|
mutationAttemptObserved,
|
|
1924
1290
|
mutationEvidence: completionMutationEvidence,
|
|
1925
|
-
|
|
1291
|
+
agentContractEnabled: isAgentContract(step.agentContract),
|
|
1926
1292
|
});
|
|
1927
1293
|
const finalOutputHasPersistableFileContent = run.exitCode === 0 && !run.error && !emptyOutputError && Boolean(stripAcceptanceReport(run.finalOutput).trim());
|
|
1928
1294
|
const requiredOutput = step.outputMode === "file-only" && step.outputPath
|
|
@@ -1940,19 +1306,10 @@ async function runSingleStepInner(
|
|
|
1940
1306
|
: run.error && run.exitCode === 0
|
|
1941
1307
|
? 1
|
|
1942
1308
|
: run.exitCode;
|
|
1943
|
-
const signalError = run.exitCode !== 0 && isUnexplainedProcessSignal(omitUndefinedProperties({
|
|
1944
|
-
processSignal: run.processSignal,
|
|
1945
|
-
interrupted: run.interrupted,
|
|
1946
|
-
timedOut: run.timedOut,
|
|
1947
|
-
stopped: run.stopped,
|
|
1948
|
-
})) ? formatProcessSignalError(run.processSignal!) : undefined;
|
|
1949
1309
|
const underlyingError = toolAvailabilityError
|
|
1950
1310
|
?? midToolExitError
|
|
1951
1311
|
?? structuredError
|
|
1952
1312
|
?? run.error
|
|
1953
|
-
?? signalError
|
|
1954
|
-
?? (run.exitCode !== 0 && run.stderr.trim() ? run.stderr.trim() : undefined)
|
|
1955
|
-
?? ((emptyOutputError || missingRequiredOutputError) && run.stderr.trim() ? run.stderr.trim() : undefined)
|
|
1956
1313
|
?? emptyOutputError
|
|
1957
1314
|
?? (missingRequiredOutputAfterMutation ? missingRequiredOutputError : undefined)
|
|
1958
1315
|
?? (hiddenError?.hasError
|
|
@@ -1960,13 +1317,7 @@ async function runSingleStepInner(
|
|
|
1960
1317
|
? `${hiddenError.errorType} failed (exit ${effectiveExitCode}): ${hiddenError.details}`
|
|
1961
1318
|
: `${hiddenError.errorType} failed with exit code ${effectiveExitCode}`
|
|
1962
1319
|
: undefined);
|
|
1963
|
-
const error =
|
|
1964
|
-
underlyingError ?? missingRequiredOutputError ?? completionEvidence.legacyFailureError,
|
|
1965
|
-
{
|
|
1966
|
-
agent: step.agent,
|
|
1967
|
-
ambientExtensionsEnabled: launchResolvedExtensions?.disableAmbientExtensions === false,
|
|
1968
|
-
},
|
|
1969
|
-
);
|
|
1320
|
+
const error = underlyingError ?? missingRequiredOutputError ?? completionEvidence.legacyFailureError;
|
|
1970
1321
|
const attempt: ModelAttempt = omitUndefinedProperties({
|
|
1971
1322
|
model: candidate ?? run.model ?? step.model ?? "default",
|
|
1972
1323
|
success: effectiveExitCode === 0 && !error,
|
|
@@ -1975,7 +1326,7 @@ async function runSingleStepInner(
|
|
|
1975
1326
|
usage: run.usage,
|
|
1976
1327
|
});
|
|
1977
1328
|
modelAttempts.push(attempt);
|
|
1978
|
-
if (!recoveringAbort && candidate
|
|
1329
|
+
if (!recoveringAbort && candidate) attemptedModels.push(candidate);
|
|
1979
1330
|
completionGuardTriggeredFinal = completionEvidence.guardTriggered && !underlyingError && !missingRequiredOutputError;
|
|
1980
1331
|
finalOutputSnapshot = outputSnapshot;
|
|
1981
1332
|
if (step.toolBudget) {
|
|
@@ -1992,13 +1343,12 @@ async function runSingleStepInner(
|
|
|
1992
1343
|
afterCompactionSettlement: run.afterCompactionSettlement === true,
|
|
1993
1344
|
});
|
|
1994
1345
|
const fileMutationEffect = completionEvidence.fileMutation ?? (missingRequiredOutputAfterMutation ? { status: "observed" as const, expected: completionEvidence.mutationExpected, attempted: true, evidence: mutationEvidence } : undefined);
|
|
1995
|
-
finalResult = { ...run, exitCode: effectiveExitCode, model: candidate ?? run.model, error, structuredOutput, runtimeAcknowledgedExtensions, ...(step.agentContract ? { agentContract: step.agentContract } : {}), ...(fileMutationEffect || settlementDiagnostic ? { effects: { ...(fileMutationEffect ? { fileMutation: fileMutationEffect } : {}), ...(settlementDiagnostic ? { settlementDiagnostic } : {}) } } : {}) } as
|
|
1346
|
+
finalResult = { ...run, exitCode: effectiveExitCode, model: candidate ?? run.model, error, structuredOutput, runtimeAcknowledgedExtensions, ...(step.agentContract ? { agentContract: step.agentContract } : {}), ...(fileMutationEffect || settlementDiagnostic ? { effects: { ...(fileMutationEffect ? { fileMutation: fileMutationEffect } : {}), ...(settlementDiagnostic ? { settlementDiagnostic } : {}) } } : {}) } as RunChildSessionResult;
|
|
1996
1347
|
const abortRecovery = !attempt.success ? planAbortRecovery({
|
|
1997
1348
|
messages: run.messages,
|
|
1998
1349
|
error,
|
|
1999
|
-
processSignal: run.processSignal,
|
|
2000
1350
|
sessionAvailable: Boolean(step.sessionFile && fs.existsSync(step.sessionFile)),
|
|
2001
|
-
alreadyResumed:
|
|
1351
|
+
alreadyResumed: recoveryState !== "unused",
|
|
2002
1352
|
stopped: run.stopped || ctx.stopSignal?.aborted || ctx.skipAcceptance?.(),
|
|
2003
1353
|
interrupted: run.interrupted,
|
|
2004
1354
|
timedOut: run.timedOut || ctx.timeoutSignal?.aborted,
|
|
@@ -2018,59 +1368,59 @@ async function runSingleStepInner(
|
|
|
2018
1368
|
if (run.stopped || run.timedOut || ctx.timeoutSignal?.aborted || ctx.stopSignal?.aborted || ctx.skipAcceptance?.()) break modelAttemptsLoop;
|
|
2019
1369
|
if (abortRecovery?.action === "settle" && abortRecovery.diagnostic) break modelAttemptsLoop;
|
|
2020
1370
|
if (attempt.success) break modelAttemptsLoop;
|
|
1371
|
+
// The shared token is consumed before sibling creation; no third dispatch of any kind.
|
|
1372
|
+
if (recoveryState === "readonly-continuation") break modelAttemptsLoop;
|
|
2021
1373
|
if (recoveringAbort) break modelAttemptsLoop;
|
|
2022
1374
|
if (abortRecovery?.action === "resume") {
|
|
2023
|
-
|
|
1375
|
+
recoveryState = "abort-recovery";
|
|
2024
1376
|
nextAttemptTask = abortRecovery.prompt;
|
|
2025
1377
|
attemptNotes.push("[abort-recovery] provider/transport abort after useful progress; resuming the retained child session once.");
|
|
2026
1378
|
continue;
|
|
2027
1379
|
}
|
|
2028
1380
|
if (completionEvidence.guardTriggered) break modelAttemptsLoop;
|
|
2029
1381
|
|
|
2030
|
-
const
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
model
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
1382
|
+
const source = getSettledReadonlyChild(run);
|
|
1383
|
+
if (source) {
|
|
1384
|
+
const captured = launch.capture.completionIntentContext?.();
|
|
1385
|
+
const sourceModel = captured?.model;
|
|
1386
|
+
const retained = getReadonlySessionEvidence(source)!;
|
|
1387
|
+
// Modality/capacity assessment, not another history/provenance validator.
|
|
1388
|
+
// Images and unknown content have no certified token bound in this host slice.
|
|
1389
|
+
const textOnly = (JSON.parse(retained.contextJson) as Message[]).every((message) => typeof message.content === "string"
|
|
1390
|
+
|| (Array.isArray(message.content) && message.content.every((block) => ["text", "thinking", "toolCall"].includes(block.type))));
|
|
1391
|
+
const retainedBytes = Buffer.byteLength(retained.contextJson, "utf8");
|
|
1392
|
+
const resolvedCandidates = candidates.map((reference, index) => {
|
|
1393
|
+
// Only exact registry identities qualify; aliases/default guesses remain ineligible.
|
|
1394
|
+
const base = reference ? splitKnownThinkingSuffix(reference).baseModel : "";
|
|
1395
|
+
const slash = base.indexOf("/");
|
|
1396
|
+
const model = slash > 0 ? captured?.modelRegistry.find(base.slice(0, slash), base.slice(slash + 1)) : undefined;
|
|
1397
|
+
// Reserve the source window for runtime/system/tool overhead, plus a conservative
|
|
1398
|
+
// byte bound for the complete retained text and new prompt; never assume a fit
|
|
1399
|
+
// merely because a 429 request was sent. Equal/smaller windows remain denied.
|
|
1400
|
+
const compatible = textOnly && model && sourceModel && model.input?.includes("text")
|
|
1401
|
+
&& Number.isFinite(sourceModel.contextWindow) && sourceModel.contextWindow > 0
|
|
1402
|
+
&& model.contextWindow >= sourceModel.contextWindow + retainedBytes + Buffer.byteLength(READONLY_CONTINUATION_PROMPT, "utf8")
|
|
1403
|
+
&& model.maxTokens > 0 && model.maxTokens <= sourceModel.maxTokens;
|
|
1404
|
+
return { resolved: model ? { provider: model.provider, model: model.id, api: model.api } : undefined,
|
|
1405
|
+
tried: index <= modelIndex, compatibility: compatible ? "compatible" as const : "unknown" as const };
|
|
2052
1406
|
});
|
|
2053
|
-
const
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
1407
|
+
const plan = planReadonlyModelContinuation({ source, recoveryState, candidates: resolvedCandidates, currentIndex: modelIndex,
|
|
1408
|
+
lifecycleAllowsContinuation: lifecycleAllowsContinuation() && !run.interrupted && !run.stopped && !run.timedOut,
|
|
1409
|
+
effectsAllowContinuation: !run.currentTool && !run.observedMutationAttempt && !mutationEvidence.attemptedMutation
|
|
1410
|
+
&& !structuredError && !effectiveStructuredOutput && !missingRequiredOutputError && !toolAvailabilityError
|
|
1411
|
+
&& !completionEvidence.guardTriggered && !midToolExitError && !hiddenError?.hasError,
|
|
1412
|
+
budget: continuationBudget(), knownContextOverflow: isContextOverflow(error) });
|
|
1413
|
+
if (plan.kind === "continue" && canContinue()) {
|
|
1414
|
+
recoveryState = plan.recoveryState;
|
|
1415
|
+
const selected = resolvedCandidates[plan.candidateIndex]!.resolved!;
|
|
1416
|
+
readonlyContinuation = { source, expected: plan.expected, modelId: `${selected.provider}/${selected.model}` };
|
|
1417
|
+
nextAttemptTask = plan.prompt;
|
|
1418
|
+
modelIndex = plan.candidateIndex;
|
|
1419
|
+
attemptNotes.push(`[readonly-continuation] ${attempt.model} returned HTTP 429 after read-only progress; continuing the retained session once with ${candidates[modelIndex]}.`);
|
|
1420
|
+
continue;
|
|
2058
1421
|
}
|
|
2059
|
-
attempt.error = retryNote;
|
|
2060
|
-
attemptNotes.push(retryNote);
|
|
2061
|
-
startupAttemptIndex += 1;
|
|
2062
|
-
continue;
|
|
2063
|
-
}
|
|
2064
|
-
if (startupFailure) {
|
|
2065
|
-
const startupError = formatSubagentStartupRetryExhaustedError({
|
|
2066
|
-
model: attempt.model,
|
|
2067
|
-
attempts: startupAttemptIndex + 1,
|
|
2068
|
-
});
|
|
2069
|
-
attempt.error = startupError;
|
|
2070
|
-
finalResult.error = startupError;
|
|
2071
|
-
finalResult.finalOutput = startupError;
|
|
2072
|
-
break modelAttemptsLoop;
|
|
2073
1422
|
}
|
|
1423
|
+
|
|
2074
1424
|
const retryableModelFailure = isRetryableModelFailureAttempt({ error, messages: run.messages, toolCount: run.toolCount });
|
|
2075
1425
|
if (retryableModelFailure) recordRetryableModelFailure(candidate ?? run.model ?? step.model, error);
|
|
2076
1426
|
if (isContextOverflow(error)) {
|
|
@@ -2081,7 +1431,6 @@ async function runSingleStepInner(
|
|
|
2081
1431
|
if (!retryableModelFailure || modelIndex === candidates.length - 1) break modelAttemptsLoop;
|
|
2082
1432
|
attemptNotes.push(formatModelAttemptNote(attempt, candidates[modelIndex + 1]));
|
|
2083
1433
|
modelIndex += 1;
|
|
2084
|
-
startupAttemptIndex = 0;
|
|
2085
1434
|
}
|
|
2086
1435
|
|
|
2087
1436
|
const rawOutput = finalResult?.finalOutput ?? "";
|
|
@@ -2109,7 +1458,7 @@ async function runSingleStepInner(
|
|
|
2109
1458
|
? extractChildWrittenOutput(finalResult?.messages, step.outputPath, step.cwd ?? ctx.cwd)
|
|
2110
1459
|
: undefined;
|
|
2111
1460
|
const outputState: SubagentOutputState = finalResult?.outputState === "present"
|
|
2112
|
-
|| (finalResult as (
|
|
1461
|
+
|| (finalResult as (RunChildSessionResult & { structuredOutput?: unknown }) | undefined)?.structuredOutput !== undefined
|
|
2113
1462
|
|| Boolean(childWrittenOutput?.trim())
|
|
2114
1463
|
? "present"
|
|
2115
1464
|
: resolvedOutput.savedPath
|
|
@@ -2153,7 +1502,7 @@ async function runSingleStepInner(
|
|
|
2153
1502
|
cwd: step.cwd ?? ctx.cwd,
|
|
2154
1503
|
signal: combinedAbortSignal([ctx.timeoutSignal, ctx.stopSignal]),
|
|
2155
1504
|
abortMessage: ctx.stopSignal?.aborted ? ctx.stopMessage ?? "Subagent stopped by user." : ctx.timeoutMessage ?? "Subagent timed out.",
|
|
2156
|
-
reportOptional:
|
|
1505
|
+
reportOptional: isAgentContract(step.agentContract),
|
|
2157
1506
|
artifactsDir: ctx.artifactsDir,
|
|
2158
1507
|
runId: ctx.id,
|
|
2159
1508
|
watchdog: finalResult?.watchdog,
|
|
@@ -2169,7 +1518,7 @@ async function runSingleStepInner(
|
|
|
2169
1518
|
: acceptance
|
|
2170
1519
|
: undefined;
|
|
2171
1520
|
const acceptanceFailure = effectiveAcceptance ? acceptanceFailureMessage(effectiveAcceptance) : undefined;
|
|
2172
|
-
const acceptanceCanFailRun = acceptanceFailure && effectiveAcceptance?.explicit && (finalResult?.exitCode ?? 1) === 0 && !finalResult?.interrupted && !timedOutAfterAcceptance && !stoppedAfterAcceptance && !
|
|
1521
|
+
const acceptanceCanFailRun = acceptanceFailure && effectiveAcceptance?.explicit && (finalResult?.exitCode ?? 1) === 0 && !finalResult?.interrupted && !timedOutAfterAcceptance && !stoppedAfterAcceptance && !isAgentContract(step.agentContract);
|
|
2173
1522
|
const effectiveFinalExitCode = timedOutAfterAcceptance || stoppedAfterAcceptance ? 1 : acceptanceCanFailRun ? 1 : finalResult?.exitCode ?? 1;
|
|
2174
1523
|
const intercomDetachReceipt = finalResult?.finalOutput === INTERCOM_DETACH_RECEIPT;
|
|
2175
1524
|
const baseFinalError = stoppedAfterAcceptance
|
|
@@ -2211,7 +1560,7 @@ async function runSingleStepInner(
|
|
|
2211
1560
|
...(capabilityAudit ? { capabilityCeiling: capabilityAudit.ceiling, capabilityAudit } : {}),
|
|
2212
1561
|
launchContractDigest: actualLaunchContractDigest,
|
|
2213
1562
|
launchResolvedExtensions,
|
|
2214
|
-
...((finalResult as (
|
|
1563
|
+
...((finalResult as (RunChildSessionResult & { runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensions }) | undefined)?.runtimeAcknowledgedExtensions ? { runtimeAcknowledgedExtensions: (finalResult as RunChildSessionResult & { runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensions }).runtimeAcknowledgedExtensions } : {}),
|
|
2215
1564
|
...(transcriptWriter ? { transcriptPath: artifactPaths.transcriptPath } : {}),
|
|
2216
1565
|
transcriptError: transcriptWriter?.getError(),
|
|
2217
1566
|
skills: step.skills,
|
|
@@ -2230,7 +1579,6 @@ async function runSingleStepInner(
|
|
|
2230
1579
|
outputState,
|
|
2231
1580
|
exitCode: effectiveFinalExitCode,
|
|
2232
1581
|
error: effectiveFinalError,
|
|
2233
|
-
protocolError: finalResult?.protocolError,
|
|
2234
1582
|
sessionFile: step.sessionFile,
|
|
2235
1583
|
intercomTarget: ctx.childIntercomTarget,
|
|
2236
1584
|
model: finalResult?.model,
|
|
@@ -2241,6 +1589,7 @@ async function runSingleStepInner(
|
|
|
2241
1589
|
totalCost: costSummaryFromAttempts(modelAttempts),
|
|
2242
1590
|
usage,
|
|
2243
1591
|
artifactPaths,
|
|
1592
|
+
savedOutputPath: finalizedOutput.savedPath,
|
|
2244
1593
|
outputSaveError: [resolvedOutput.saveError, artifactErrors.outputSaveError].filter(Boolean).join("\n") || undefined,
|
|
2245
1594
|
metadataSaveError: artifactErrors.metadataSaveError,
|
|
2246
1595
|
transcriptPath: transcriptWriter ? artifactPaths?.transcriptPath : undefined,
|
|
@@ -2248,24 +1597,21 @@ async function runSingleStepInner(
|
|
|
2248
1597
|
interrupted: timedOutAfterAcceptance || stoppedAfterAcceptance ? false : finalResult?.interrupted,
|
|
2249
1598
|
timedOut: timedOutAfterAcceptance ? true : finalResult?.timedOut,
|
|
2250
1599
|
stopped: stoppedAfterAcceptance ? true : finalResult?.stopped,
|
|
2251
|
-
processSignal: finalResult?.processSignal,
|
|
2252
1600
|
timeoutRecovery,
|
|
2253
1601
|
toolBudget,
|
|
2254
1602
|
toolBudgetBlocked: toolBudgetBlocked || undefined,
|
|
2255
1603
|
completionGuardTriggered: completionGuardTriggeredFinal,
|
|
2256
|
-
...((finalResult as (
|
|
2257
|
-
structuredOutput: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : (finalResult as (
|
|
1604
|
+
...((finalResult as (RunChildSessionResult & { effects?: import("../../shared/types.ts").EffectsProjection }) | undefined)?.effects ? { effects: (finalResult as RunChildSessionResult & { effects?: import("../../shared/types.ts").EffectsProjection }).effects } : {}),
|
|
1605
|
+
structuredOutput: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : (finalResult as (RunChildSessionResult & { structuredOutput?: unknown }) | undefined)?.structuredOutput,
|
|
2258
1606
|
structuredOutputPath: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : effectiveStructuredOutput?.outputPath,
|
|
2259
1607
|
structuredOutputSchemaPath: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : effectiveStructuredOutput?.schemaPath,
|
|
2260
1608
|
acceptance: effectiveAcceptance,
|
|
2261
1609
|
watchdog: finalResult?.watchdog,
|
|
2262
1610
|
...(capabilityAudit ? { capabilityCeiling: capabilityAudit.ceiling, capabilityAudit } : {}),
|
|
2263
1611
|
launchResolvedExtensions,
|
|
2264
|
-
...((finalResult as (
|
|
2265
|
-
writerProcesses,
|
|
2266
|
-
writerAttemptCount,
|
|
1612
|
+
...((finalResult as (RunChildSessionResult & { runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensions }) | undefined)?.runtimeAcknowledgedExtensions ? { runtimeAcknowledgedExtensions: (finalResult as RunChildSessionResult & { runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensions }).runtimeAcknowledgedExtensions } : {}),
|
|
2267
1613
|
});
|
|
2268
|
-
return
|
|
1614
|
+
return isAgentContract(step.agentContract) ? attachContractProjections(result as unknown as import("../../shared/types.ts").SingleResult) as unknown as typeof result : result;
|
|
2269
1615
|
}
|
|
2270
1616
|
|
|
2271
1617
|
async function runSingleStep(
|
|
@@ -2356,12 +1702,15 @@ function markParallelGroupSetupFailure(input: {
|
|
|
2356
1702
|
const statusStep = requiredStatusStep(input.statusPayload, flatTaskIndex);
|
|
2357
1703
|
const task = input.group.parallel[taskIndex];
|
|
2358
1704
|
if (!task) throw new Error(`Missing parallel task at index ${taskIndex}`);
|
|
2359
|
-
statusStep.
|
|
1705
|
+
const stopped = statusStep.stopped || statusStep.stopRequested || input.statusPayload.stopped;
|
|
1706
|
+
const paused = !stopped && input.statusPayload.state === "paused";
|
|
1707
|
+
statusStep.status = stopped ? "stopped" : paused ? "paused" : "failed";
|
|
2360
1708
|
statusStep.startedAt = input.failedAt;
|
|
2361
1709
|
statusStep.endedAt = input.failedAt;
|
|
2362
1710
|
statusStep.durationMs = 0;
|
|
2363
|
-
statusStep.exitCode = 1;
|
|
2364
|
-
|
|
1711
|
+
statusStep.exitCode = paused ? 0 : 1;
|
|
1712
|
+
statusStep.error ??= input.setupError;
|
|
1713
|
+
input.results.push(omitUndefinedProperties({ agent: task.agent, context: task.context, output: input.setupError, error: input.setupError, success: false, exitCode: paused ? 0 : 1, sessionFile: task.sessionFile, stopped: stopped || undefined, interrupted: paused || undefined, timedOut: input.statusPayload.timedOut || undefined }));
|
|
2365
1714
|
}
|
|
2366
1715
|
input.statusPayload.currentStep = input.groupStartFlatIndex;
|
|
2367
1716
|
input.statusPayload.lastUpdate = input.failedAt;
|
|
@@ -2518,7 +1867,10 @@ async function runSingleStepWithTimeout(
|
|
|
2518
1867
|
ctx: SingleStepContext,
|
|
2519
1868
|
parentDeadlineAt?: number,
|
|
2520
1869
|
): Promise<SingleStepResult> {
|
|
2521
|
-
if (step.timeoutMs === undefined) return runSingleStep(step, ctx
|
|
1870
|
+
if (step.timeoutMs === undefined) return runSingleStep(step, parentDeadlineAt === undefined ? ctx : {
|
|
1871
|
+
...ctx,
|
|
1872
|
+
deadlineAt: ctx.deadlineAt === undefined ? parentDeadlineAt : Math.min(ctx.deadlineAt, parentDeadlineAt),
|
|
1873
|
+
});
|
|
2522
1874
|
|
|
2523
1875
|
const parentRemainingMs = parentDeadlineAt === undefined ? undefined : Math.max(0, parentDeadlineAt - Date.now());
|
|
2524
1876
|
const timeoutMs = parentRemainingMs === undefined ? step.timeoutMs : Math.min(step.timeoutMs, parentRemainingMs);
|
|
@@ -2555,9 +1907,9 @@ async function runSingleStepWithTimeout(
|
|
|
2555
1907
|
}
|
|
2556
1908
|
}
|
|
2557
1909
|
|
|
2558
|
-
async function runSubagent(
|
|
1910
|
+
export async function runSubagent(
|
|
2559
1911
|
config: SubagentRunConfig,
|
|
2560
|
-
|
|
1912
|
+
childSessions: ChildSessionFactory,
|
|
2561
1913
|
): Promise<void> {
|
|
2562
1914
|
const { id, steps, resultPath, cwd, placeholder, taskIndex, totalTasks, maxOutput, artifactsDir, artifactConfig } =
|
|
2563
1915
|
config;
|
|
@@ -2577,9 +1929,11 @@ async function runSubagent(
|
|
|
2577
1929
|
const activeChildInterrupts = new Map<number, () => void>();
|
|
2578
1930
|
const activeChildTimeouts = new Map<number, () => void>();
|
|
2579
1931
|
const activeChildStops = new Map<number, () => void>();
|
|
1932
|
+
const activeChildSteers = new Map<number, StepSteerHandler>();
|
|
1933
|
+
/** Steers routed to a running step before its session was created. */
|
|
1934
|
+
const queuedStepSteers = new Map<number, SteerRequest[]>();
|
|
2580
1935
|
const childStopRequests = new Map<number, { childId: string; requestedAt: number }>();
|
|
2581
1936
|
const pendingStepSteers: SteerRequest[] = [];
|
|
2582
|
-
const steeringCapabilities = new Map<number, SteerCapability>();
|
|
2583
1937
|
let interrupted = false;
|
|
2584
1938
|
let currentActivityState: ActivityState | undefined;
|
|
2585
1939
|
let activityTimer: NodeJS.Timeout | undefined;
|
|
@@ -2591,6 +1945,8 @@ async function runSubagent(
|
|
|
2591
1945
|
const stopMessage = "Subagent stopped by user.";
|
|
2592
1946
|
const timeoutAbortController = new AbortController();
|
|
2593
1947
|
const stopAbortController = new AbortController();
|
|
1948
|
+
const setupInterruptController = new AbortController();
|
|
1949
|
+
const setupSignal = AbortSignal.any([timeoutAbortController.signal, stopAbortController.signal, setupInterruptController.signal]);
|
|
2594
1950
|
let previousCumulativeTokens: TokenUsage = { input: 0, output: 0, total: 0 };
|
|
2595
1951
|
let latestSessionFile: string | undefined;
|
|
2596
1952
|
|
|
@@ -2757,20 +2113,29 @@ async function runSubagent(
|
|
|
2757
2113
|
},
|
|
2758
2114
|
onError: (error, filePath) => console.error(`Failed to update async run index '${filePath}':`, error),
|
|
2759
2115
|
});
|
|
2760
|
-
const queueActiveRunIndex = (): void => {
|
|
2761
|
-
const state =
|
|
2116
|
+
const queueActiveRunIndex = (status: AsyncStatus): void => {
|
|
2117
|
+
const state = status.state;
|
|
2762
2118
|
if (state === lastIndexedStatusState && indexPersistence.pendingCount() === 0) return;
|
|
2763
|
-
indexPersistence.write(asyncDir, { state, toolCallId:
|
|
2119
|
+
indexPersistence.write(asyncDir, { state, toolCallId: status.toolCallId }, (_filePath, payload) => {
|
|
2764
2120
|
const indexPayload = payload as { state: AsyncStatus["state"]; toolCallId?: string };
|
|
2765
2121
|
updateActiveRunIndex(asyncDir, indexPayload.state, indexPayload.toolCallId, { retryCapacityErrors: true });
|
|
2766
2122
|
});
|
|
2767
2123
|
};
|
|
2124
|
+
let finalResultCommitted = false;
|
|
2125
|
+
let finalResultPublication: { resolve(): void; reject(error: unknown): void } | undefined;
|
|
2768
2126
|
const runPersistence = createCapacityResilientJsonWriter({
|
|
2769
2127
|
keepAlive: true,
|
|
2770
|
-
onSuccess: (filePath) => {
|
|
2771
|
-
if (filePath === statusPath) queueActiveRunIndex();
|
|
2128
|
+
onSuccess: (filePath, payload) => {
|
|
2129
|
+
if (filePath === statusPath) queueActiveRunIndex(payload as AsyncStatus);
|
|
2130
|
+
if (filePath === resultPath && finalResultPublication) {
|
|
2131
|
+
finalResultCommitted = true;
|
|
2132
|
+
finalResultPublication.resolve();
|
|
2133
|
+
}
|
|
2134
|
+
},
|
|
2135
|
+
onError: (error, filePath) => {
|
|
2136
|
+
console.error(`Failed to persist async run state '${filePath}':`, error);
|
|
2137
|
+
if (filePath === resultPath) finalResultPublication?.reject(error);
|
|
2772
2138
|
},
|
|
2773
|
-
onError: (error, filePath) => console.error(`Failed to persist async run state '${filePath}':`, error),
|
|
2774
2139
|
});
|
|
2775
2140
|
try {
|
|
2776
2141
|
fs.mkdirSync(asyncDir, { recursive: true });
|
|
@@ -2778,7 +2143,7 @@ async function runSubagent(
|
|
|
2778
2143
|
if (!isStorageCapacityError(error)) throw error;
|
|
2779
2144
|
console.error(`Failed to prepare async run storage '${asyncDir}' while storage is full:`, error);
|
|
2780
2145
|
}
|
|
2781
|
-
runPersistence.write(statusPath, statusPayload);
|
|
2146
|
+
runPersistence.write(statusPath, { ...statusPayload });
|
|
2782
2147
|
|
|
2783
2148
|
let pendingParallelUsageCost: CostSummary = { inputTokens: 0, outputTokens: 0, costUsd: 0 };
|
|
2784
2149
|
const currentUsageTotals = (): CostSummary => {
|
|
@@ -2797,6 +2162,13 @@ async function runSubagent(
|
|
|
2797
2162
|
setOptionalProperty(statusPayload, "usageBudget", usageBudgetState(config.usageBudget, currentUsageTotals()));
|
|
2798
2163
|
return statusPayload.usageBudget;
|
|
2799
2164
|
};
|
|
2165
|
+
// Continuation admission only: the existing ledger has no in-flight cost or
|
|
2166
|
+
// external/import usage coverage. Never change ordinary budget enforcement.
|
|
2167
|
+
let continuationUsageUncertain = config.steps.some(isDynamicRunnerGroup) || flatSteps.some((step) => Boolean(step.runner || step.importAsyncRoot));
|
|
2168
|
+
const continuationUsageBudgetExhausted = (): boolean | undefined => {
|
|
2169
|
+
const exhausted = refreshUsageBudget()?.exhausted === true;
|
|
2170
|
+
return exhausted ? true : config.usageBudget && continuationUsageUncertain ? undefined : false;
|
|
2171
|
+
};
|
|
2800
2172
|
const emitNestedSelfEvent = (type: "subagent.nested.updated" | "subagent.nested.completed"): void => {
|
|
2801
2173
|
if (!config.nestedRoute || !config.nestedSelf) return;
|
|
2802
2174
|
try {
|
|
@@ -2851,7 +2223,6 @@ async function runSubagent(
|
|
|
2851
2223
|
for (const node of graph.nodes) updateNode(node);
|
|
2852
2224
|
statusPayload.workflowGraph = graph;
|
|
2853
2225
|
};
|
|
2854
|
-
let finalResultCommitted = false;
|
|
2855
2226
|
const statusResultState = (): AsyncStatus["state"] | undefined => {
|
|
2856
2227
|
if (statusPayload.state === "running" || statusPayload.state === "queued") return undefined;
|
|
2857
2228
|
return statusPayload.state;
|
|
@@ -2913,14 +2284,15 @@ async function runSubagent(
|
|
|
2913
2284
|
}), (filePath, payload) => writePendingAsyncResultFile(filePath, payload as Record<string, unknown>));
|
|
2914
2285
|
};
|
|
2915
2286
|
const writeStatusPayloadNow = (): void => {
|
|
2287
|
+
if (finalResultPublication) return;
|
|
2916
2288
|
refreshWorkflowGraph();
|
|
2917
2289
|
writeRecoverableStatusResult();
|
|
2918
|
-
runPersistence.write(statusPath, statusPayload);
|
|
2290
|
+
runPersistence.write(statusPath, { ...statusPayload });
|
|
2919
2291
|
emitNestedSelfEvent(statusPayload.state === "running" || statusPayload.state === "queued" ? "subagent.nested.updated" : "subagent.nested.completed");
|
|
2920
2292
|
};
|
|
2921
2293
|
const statusWriteCoalescer = createFileCoalescer(writeStatusPayloadNow, 100);
|
|
2922
2294
|
const writeStatusPayload = (immediate = true): void => {
|
|
2923
|
-
if (immediate || statusPayload.state !== "running"
|
|
2295
|
+
if (immediate || statusPayload.state !== "running") {
|
|
2924
2296
|
if (!statusWriteCoalescer.flush(statusPath)) writeStatusPayloadNow();
|
|
2925
2297
|
return;
|
|
2926
2298
|
}
|
|
@@ -3037,6 +2409,16 @@ async function runSubagent(
|
|
|
3037
2409
|
activeChildStops.set(flatIndex, stop);
|
|
3038
2410
|
if (stopped || childStopRequests.has(flatIndex)) stop();
|
|
3039
2411
|
};
|
|
2412
|
+
const registerStepSteer = (flatIndex: number, steer: StepSteerHandler | undefined): void => {
|
|
2413
|
+
if (!steer) {
|
|
2414
|
+
activeChildSteers.delete(flatIndex);
|
|
2415
|
+
return;
|
|
2416
|
+
}
|
|
2417
|
+
activeChildSteers.set(flatIndex, steer);
|
|
2418
|
+
const queued = queuedStepSteers.get(flatIndex);
|
|
2419
|
+
queuedStepSteers.delete(flatIndex);
|
|
2420
|
+
for (const request of queued ?? []) steerLiveChild(flatIndex, request);
|
|
2421
|
+
};
|
|
3040
2422
|
const interruptActiveChildren = (): void => {
|
|
3041
2423
|
for (const interrupt of [...activeChildInterrupts.values()]) interrupt();
|
|
3042
2424
|
};
|
|
@@ -3489,22 +2871,15 @@ async function runSubagent(
|
|
|
3489
2871
|
if (!step) return { index, state: "failed" as const, reason: "child index out of range" };
|
|
3490
2872
|
if (step.status === "pending") return { index, state: "scheduled" as const };
|
|
3491
2873
|
if (step.status !== "running") return { index, state: "failed" as const, reason: `child is ${step.status}` };
|
|
3492
|
-
if (steeringCapabilities.get(index)?.supported === false) return { index, state: "failed" as const, reason: "child Pi session does not support steering" };
|
|
3493
2874
|
return { index, state: "routed" as const };
|
|
3494
2875
|
});
|
|
3495
2876
|
recordSteeringLifecycle(request, targetStates);
|
|
3496
2877
|
emitSteeringEvent("subagent.steer.requested", request, undefined, { targets: targetStates });
|
|
3497
2878
|
for (const target of targetStates) {
|
|
3498
2879
|
if (target.state === "routed") {
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
emitSteeringEvent("subagent.steer.routed", request, target.index);
|
|
3503
|
-
} catch (error) {
|
|
3504
|
-
markSteeringAttention(target.index);
|
|
3505
|
-
updateSteeringLifecycleTarget(request.id, target.index, "failed", now, { reason: error instanceof Error ? error.message : String(error) });
|
|
3506
|
-
emitSteeringEvent("subagent.steer.failed", request, target.index, { reason: error instanceof Error ? error.message : String(error) });
|
|
3507
|
-
}
|
|
2880
|
+
updateSteeringLifecycleTarget(request.id, target.index, "routed", now);
|
|
2881
|
+
emitSteeringEvent("subagent.steer.routed", request, target.index);
|
|
2882
|
+
steerLiveChild(target.index, request);
|
|
3508
2883
|
} else if (target.state === "failed") {
|
|
3509
2884
|
markSteeringAttention(target.index);
|
|
3510
2885
|
emitSteeringEvent("subagent.steer.failed", request, target.index, { reason: target.reason });
|
|
@@ -3516,27 +2891,42 @@ async function runSubagent(
|
|
|
3516
2891
|
statusPayload.lastUpdate = now;
|
|
3517
2892
|
writeStatusPayload();
|
|
3518
2893
|
};
|
|
3519
|
-
|
|
2894
|
+
/** Record the outcome of handing a routed steer to the live child session. */
|
|
2895
|
+
const applySteerDelivery = (requestId: string, index: number, delivery: { state: "delivered" | "queued" | "failed"; message: string }): void => {
|
|
3520
2896
|
const lifecycle = steeringStatus(statusPayload);
|
|
3521
|
-
const request = lifecycle.recent.find((candidate) => candidate.id ===
|
|
3522
|
-
if (!request || !request.targets.some((target) => target.index ===
|
|
3523
|
-
const late = fs.existsSync(steeringMarkerPath(
|
|
2897
|
+
const request = lifecycle.recent.find((candidate) => candidate.id === requestId);
|
|
2898
|
+
if (!request || !request.targets.some((target) => target.index === index)) return;
|
|
2899
|
+
const late = fs.existsSync(steeringMarkerPath(requestId));
|
|
3524
2900
|
const now = Date.now();
|
|
3525
|
-
if (
|
|
3526
|
-
updateSteeringLifecycleTarget(
|
|
3527
|
-
emitSteeringEvent("subagent.steer.delivered", { type: "steer", id:
|
|
3528
|
-
} else if (
|
|
3529
|
-
updateSteeringLifecycleTarget(
|
|
3530
|
-
emitSteeringEvent("subagent.steer.queued", { type: "steer", id:
|
|
2901
|
+
if (delivery.state === "delivered") {
|
|
2902
|
+
updateSteeringLifecycleTarget(requestId, index, late ? "late" : "delivered", now, omitUndefinedProperties({ reason: late ? "acknowledged after recovery commit" : undefined }));
|
|
2903
|
+
emitSteeringEvent("subagent.steer.delivered", { type: "steer", id: requestId, ts: now, message: delivery.message }, index, { late, deliveryStatus: "delivered", message: delivery.message });
|
|
2904
|
+
} else if (delivery.state === "queued") {
|
|
2905
|
+
updateSteeringLifecycleTarget(requestId, index, "queued", now);
|
|
2906
|
+
emitSteeringEvent("subagent.steer.queued", { type: "steer", id: requestId, ts: now, message: delivery.message }, index, { deliveryStatus: "queued", message: delivery.message });
|
|
3531
2907
|
} else {
|
|
3532
|
-
markSteeringAttention(
|
|
3533
|
-
updateSteeringLifecycleTarget(
|
|
3534
|
-
emitSteeringEvent("subagent.steer.failed", { type: "steer", id:
|
|
2908
|
+
markSteeringAttention(index);
|
|
2909
|
+
updateSteeringLifecycleTarget(requestId, index, "failed", now, { reason: delivery.message });
|
|
2910
|
+
emitSteeringEvent("subagent.steer.failed", { type: "steer", id: requestId, ts: now, message: delivery.message }, index, { reason: delivery.message });
|
|
3535
2911
|
}
|
|
3536
|
-
emitTerminalSteeringNotice(
|
|
2912
|
+
emitTerminalSteeringNotice(requestId, `Steering failed for run ${id}: ${delivery.message}`);
|
|
3537
2913
|
statusPayload.lastUpdate = now;
|
|
3538
2914
|
writeStatusPayload();
|
|
3539
2915
|
};
|
|
2916
|
+
/** Hand a routed steer to the step's live session, or hold it until the session exists. */
|
|
2917
|
+
const steerLiveChild = (index: number, request: SteerRequest): void => {
|
|
2918
|
+
const steer = activeChildSteers.get(index);
|
|
2919
|
+
if (!steer) {
|
|
2920
|
+
const queued = queuedStepSteers.get(index) ?? [];
|
|
2921
|
+
queued.push(request);
|
|
2922
|
+
queuedStepSteers.set(index, queued);
|
|
2923
|
+
return;
|
|
2924
|
+
}
|
|
2925
|
+
void steer(request).then(
|
|
2926
|
+
(delivery) => applySteerDelivery(request.id, index, delivery),
|
|
2927
|
+
(error) => applySteerDelivery(request.id, index, { state: "failed", message: error instanceof Error ? error.message : String(error) }),
|
|
2928
|
+
);
|
|
2929
|
+
};
|
|
3540
2930
|
const flushPendingStepSteers = (flatIndex: number): void => {
|
|
3541
2931
|
const remaining: SteerRequest[] = [];
|
|
3542
2932
|
for (const request of pendingStepSteers.splice(0)) {
|
|
@@ -3558,6 +2948,7 @@ async function runSubagent(
|
|
|
3558
2948
|
const updateStepFromChildEvent = (flatIndex: number, event: ChildEvent): void => {
|
|
3559
2949
|
const step = statusPayload.steps[flatIndex];
|
|
3560
2950
|
if (!step) return;
|
|
2951
|
+
const previousActivityState = step.activityState;
|
|
3561
2952
|
const now = Date.now();
|
|
3562
2953
|
statusPayload.currentStep = flatIndex;
|
|
3563
2954
|
if (isChildWatchdogStatusEvent(event)) {
|
|
@@ -3620,6 +3011,7 @@ async function runSubagent(
|
|
|
3620
3011
|
tokens: step.tokens?.total,
|
|
3621
3012
|
toolCount: step.toolCount,
|
|
3622
3013
|
currentTool: step.currentTool,
|
|
3014
|
+
toolCallId: event.toolCallId,
|
|
3623
3015
|
currentToolDurationMs: 0,
|
|
3624
3016
|
currentPath: step.currentPath,
|
|
3625
3017
|
})));
|
|
@@ -3691,6 +3083,10 @@ async function runSubagent(
|
|
|
3691
3083
|
appendRecentStepOutput(step, stripAcceptanceReport(extractTextFromContent(event.message.content)).split("\n").slice(-10));
|
|
3692
3084
|
step.turnCount = (step.turnCount ?? 0) + 1;
|
|
3693
3085
|
const usage = event.message.usage;
|
|
3086
|
+
if (config.usageBudget) {
|
|
3087
|
+
const known = (value: unknown) => typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
3088
|
+
if (!known(usage?.input ?? usage?.inputTokens) || !known(usage?.output ?? usage?.outputTokens)) continuationUsageUncertain = true;
|
|
3089
|
+
}
|
|
3694
3090
|
if (usage) {
|
|
3695
3091
|
const input = usage.input ?? usage.inputTokens ?? 0;
|
|
3696
3092
|
const output = usage.output ?? usage.outputTokens ?? 0;
|
|
@@ -3710,7 +3106,8 @@ async function runSubagent(
|
|
|
3710
3106
|
statusPayload.lastActivityAt = now;
|
|
3711
3107
|
statusPayload.lastUpdate = now;
|
|
3712
3108
|
maybeEmitActiveLongRunning(flatIndex, now);
|
|
3713
|
-
|
|
3109
|
+
// A sibling may keep aggregate attention unchanged; publish this step's transition.
|
|
3110
|
+
writeStatusPayload(step.activityState !== previousActivityState);
|
|
3714
3111
|
};
|
|
3715
3112
|
const updateRunnerActivityState = (now: number): boolean => {
|
|
3716
3113
|
if (!controlConfig.enabled) return false;
|
|
@@ -3782,10 +3179,55 @@ async function runSubagent(
|
|
|
3782
3179
|
activityTimer.unref?.();
|
|
3783
3180
|
}
|
|
3784
3181
|
|
|
3182
|
+
const publishSetupUnknown = (progress: WorktreeSetupProgress): void => {
|
|
3183
|
+
if (!progress.unknown) return;
|
|
3184
|
+
const proof = {
|
|
3185
|
+
version: 1 as const, state: "unknown" as const, runId: id,
|
|
3186
|
+
runnerProcessInstanceId: config.runnerProcessInstanceId ?? "unknown",
|
|
3187
|
+
reason: "process-tree-unverified" as const,
|
|
3188
|
+
diagnostic: `Worktree setup settlement unknown: ${progress.unknown}; manual reconciliation required; ${parallelHandoffPath(asyncDir)}`,
|
|
3189
|
+
};
|
|
3190
|
+
statusPayload.processTerminal = proof;
|
|
3191
|
+
// Publish the sticky proof independently of in-process child writer counts.
|
|
3192
|
+
writeAtomicJson(processTerminalPath(asyncDir), proof);
|
|
3193
|
+
};
|
|
3194
|
+
const finalizeWorktree = async (setup: WorktreeSetup, stepIndex: number, flatStartIndex: number, action: () => void, deadlineAt?: number): Promise<void> => {
|
|
3195
|
+
let admitted = false;
|
|
3196
|
+
try { await withWorktreeTransaction(() => {
|
|
3197
|
+
if (deadlineAt !== undefined && Date.now() >= deadlineAt) throw new Error("Run deadline expired before worktree cleanup");
|
|
3198
|
+
admitted = true;
|
|
3199
|
+
action();
|
|
3200
|
+
}); }
|
|
3201
|
+
catch (error) {
|
|
3202
|
+
if (admitted) throw error;
|
|
3203
|
+
const reason = `Worktree finalization retained; manual reconciliation required: ${error instanceof Error ? error.message : String(error)}`;
|
|
3204
|
+
statusPayload.parallelHandoff = writeParallelHandoffGroup({
|
|
3205
|
+
manifestPath: parallelHandoffPath(asyncDir), runId: id,
|
|
3206
|
+
mode: (config.resultMode ?? statusPayload.mode) === "parallel" ? "parallel" : (config.resultMode ?? statusPayload.mode) === "single" ? "single" : "chain",
|
|
3207
|
+
source: "async", cwd, stepIndex, flatStartIndex, setup, diffs: [], results: [],
|
|
3208
|
+
cleanup: { state: "partial", pruned: false, errors: [reason], tasks: setup.worktrees.map((worktree) => ({
|
|
3209
|
+
index: worktree.index, path: worktree.path, branch: worktree.branch, provider: worktree.provider, naming: worktree.naming,
|
|
3210
|
+
worktreeRemoved: false, branchRemoved: false, preserved: true, reason,
|
|
3211
|
+
})) },
|
|
3212
|
+
});
|
|
3213
|
+
previousOutput = [previousOutput, reason, formatParallelHandoffReference(statusPayload.parallelHandoff)].filter(Boolean).join("\n\n");
|
|
3214
|
+
writeStatusPayload();
|
|
3215
|
+
}
|
|
3216
|
+
};
|
|
3217
|
+
const cleanupRemainingWorktree = (setup: WorktreeSetup, stepIndex: number, flatStartIndex: number) => finalizeWorktree(setup, stepIndex, flatStartIndex, () => {
|
|
3218
|
+
const cleanup = cleanupWorktrees(setup);
|
|
3219
|
+
statusPayload.parallelHandoff = writeParallelHandoffGroup({
|
|
3220
|
+
manifestPath: parallelHandoffPath(asyncDir), runId: id,
|
|
3221
|
+
mode: (config.resultMode ?? statusPayload.mode) === "parallel" ? "parallel" : (config.resultMode ?? statusPayload.mode) === "single" ? "single" : "chain",
|
|
3222
|
+
source: "async", cwd, stepIndex, flatStartIndex, setup, diffs: [], results: [], cleanup,
|
|
3223
|
+
});
|
|
3224
|
+
writeStatusPayload();
|
|
3225
|
+
}, config.deadlineAt);
|
|
3785
3226
|
const interruptRunner = () => {
|
|
3786
3227
|
consumeInterruptRequest(asyncDir);
|
|
3787
3228
|
if (interrupted || statusPayload.state !== "running") return;
|
|
3788
3229
|
interrupted = true;
|
|
3230
|
+
setupInterruptController.abort();
|
|
3789
3231
|
const now = Date.now();
|
|
3790
3232
|
statusPayload.state = "paused";
|
|
3791
3233
|
currentActivityState = undefined;
|
|
@@ -3894,23 +3336,6 @@ async function runSubagent(
|
|
|
3894
3336
|
pendingStepSteers.push(request);
|
|
3895
3337
|
}
|
|
3896
3338
|
},
|
|
3897
|
-
onSteerCapability: (capability) => {
|
|
3898
|
-
steeringCapabilities.set(capability.index, capability);
|
|
3899
|
-
if (!capability.supported) {
|
|
3900
|
-
const now = Date.now();
|
|
3901
|
-
const lifecycle = steeringStatus(statusPayload);
|
|
3902
|
-
for (const request of lifecycle.recent) {
|
|
3903
|
-
if (!request.targets.some((target) => target.index === capability.index && (target.state === "routed" || target.state === "scheduled"))) continue;
|
|
3904
|
-
markSteeringAttention(capability.index);
|
|
3905
|
-
updateSteeringLifecycleTarget(request.id, capability.index, "failed", now, { reason: "child Pi session does not support steering" });
|
|
3906
|
-
emitSteeringEvent("subagent.steer.failed", { type: "steer", id: request.id, ts: request.requestedAt, message: "child Pi session does not support steering" }, capability.index, { reason: "child Pi session does not support steering" });
|
|
3907
|
-
emitTerminalSteeringNotice(request.id, `Steering failed for run ${id}: child ${capability.index} does not support steering.`);
|
|
3908
|
-
}
|
|
3909
|
-
statusPayload.lastUpdate = now;
|
|
3910
|
-
writeStatusPayload();
|
|
3911
|
-
}
|
|
3912
|
-
},
|
|
3913
|
-
onSteerAck: consumeSteerAck,
|
|
3914
3339
|
});
|
|
3915
3340
|
if (config.deadlineAt !== undefined) {
|
|
3916
3341
|
const remainingMs = Math.max(0, config.deadlineAt - Date.now());
|
|
@@ -3967,7 +3392,7 @@ async function runSubagent(
|
|
|
3967
3392
|
: [undefined]
|
|
3968
3393
|
: model ? [model] : [undefined];
|
|
3969
3394
|
for (const candidate of candidates) {
|
|
3970
|
-
assertThinkingWithinCeiling({ model: candidate, configThinking, ceiling: step.parallel.thinkingCeiling
|
|
3395
|
+
assertThinkingWithinCeiling({ model: candidate, configThinking, ceiling: step.parallel.thinkingCeiling, agent: step.parallel.agent, runId: id });
|
|
3971
3396
|
}
|
|
3972
3397
|
}
|
|
3973
3398
|
if (materialized.collectedOnEmpty) await validateDynamicCollection(step.collect.outputSchema, materialized.collectedOnEmpty);
|
|
@@ -4036,14 +3461,14 @@ async function runSubagent(
|
|
|
4036
3461
|
cwd,
|
|
4037
3462
|
signal: combinedAbortSignal([timeoutAbortController.signal, stopAbortController.signal]),
|
|
4038
3463
|
abortMessage: stopAbortController.signal.aborted ? stopMessage : timeoutMessage ?? "Subagent timed out.",
|
|
4039
|
-
reportOptional:
|
|
3464
|
+
reportOptional: isAgentContract(step.agentContract),
|
|
4040
3465
|
}))
|
|
4041
3466
|
: undefined;
|
|
4042
3467
|
const groupStopped = stopped || stopAbortController.signal.aborted;
|
|
4043
3468
|
const groupTimedOut = !groupStopped && (timedOut || timeoutAbortController.signal.aborted);
|
|
4044
3469
|
const effectiveGroupAcceptance = groupTimedOut || groupStopped ? undefined : groupAcceptance;
|
|
4045
3470
|
if (placeholder && effectiveGroupAcceptance) placeholder.acceptance = effectiveGroupAcceptance;
|
|
4046
|
-
const groupAcceptanceFailure = effectiveGroupAcceptance && (!
|
|
3471
|
+
const groupAcceptanceFailure = effectiveGroupAcceptance && (!isAgentContract(step.agentContract) || step.gateOn === "acceptance") ? acceptanceFailureMessage(effectiveGroupAcceptance) : undefined;
|
|
4047
3472
|
if (groupTimedOut || groupStopped || groupAcceptanceFailure) {
|
|
4048
3473
|
const errorMessage = groupStopped ? stopMessage : groupTimedOut ? timeoutMessage ?? "Subagent timed out." : groupAcceptanceFailure!;
|
|
4049
3474
|
statusPayload.state = groupStopped ? "stopped" : "failed";
|
|
@@ -4237,11 +3662,9 @@ async function runSubagent(
|
|
|
4237
3662
|
artifactsDir, artifactConfig, id,
|
|
4238
3663
|
flatIndex: fi, flatStepCount: Math.max(statusPayload.steps.length, 1),
|
|
4239
3664
|
outputFile: path.join(asyncDir, `output-${fi}.log`),
|
|
4240
|
-
steerInboxDir: stepSteerInboxDir(asyncDir, fi),
|
|
4241
|
-
steerCapabilityPath: steerCapabilityPath(asyncDir, fi),
|
|
4242
|
-
steerAckDir: steerAcksDir(asyncDir, fi),
|
|
4243
3665
|
piPackageRoot: config.piPackageRoot,
|
|
4244
|
-
|
|
3666
|
+
childSessions,
|
|
3667
|
+
inheritedChildRuntime: config.inheritedChildRuntime,
|
|
4245
3668
|
childIntercomTarget: config.childIntercomTargets?.[fi],
|
|
4246
3669
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
4247
3670
|
nestedRoute: config.nestedRoute,
|
|
@@ -4250,6 +3673,7 @@ async function runSubagent(
|
|
|
4250
3673
|
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
4251
3674
|
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
4252
3675
|
registerStop: (stop) => registerStepStop(fi, stop),
|
|
3676
|
+
registerSteer: (steer) => registerStepSteer(fi, steer),
|
|
4253
3677
|
timeoutSignal: timeoutAbortController.signal,
|
|
4254
3678
|
stopSignal: stopAbortController.signal,
|
|
4255
3679
|
trackedMutationEvidenceForCompletionGuard: false,
|
|
@@ -4258,11 +3682,11 @@ async function runSubagent(
|
|
|
4258
3682
|
toolTimeoutMs: task.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
4259
3683
|
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking, attempt.contextLimit),
|
|
4260
3684
|
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
4261
|
-
onWriterProcess,
|
|
4262
3685
|
onExternalProcess: (process) => updateExternalProcess(fi, process),
|
|
4263
3686
|
onExternalJob: (externalJob) => updateExternalJob(fi, externalJob),
|
|
4264
3687
|
skipAcceptance: () => timedOut || stopped || childStopRequests.has(fi),
|
|
4265
|
-
usageBudgetExhausted:
|
|
3688
|
+
usageBudgetExhausted: continuationUsageBudgetExhausted,
|
|
3689
|
+
usageBudget: config.usageBudget,
|
|
4266
3690
|
orcaProgressTab,
|
|
4267
3691
|
}), config.deadlineAt);
|
|
4268
3692
|
const taskEndTime = Date.now();
|
|
@@ -4339,7 +3763,6 @@ async function runSubagent(
|
|
|
4339
3763
|
output: pr.output,
|
|
4340
3764
|
outputState: pr.outputState,
|
|
4341
3765
|
error: pr.error,
|
|
4342
|
-
protocolError: pr.protocolError,
|
|
4343
3766
|
success: pr.stopped !== true && pr.interrupted !== true && pr.exitCode === 0,
|
|
4344
3767
|
exitCode: pr.interrupted === true ? 0 : pr.exitCode,
|
|
4345
3768
|
skipped: pr.skipped,
|
|
@@ -4379,7 +3802,7 @@ async function runSubagent(
|
|
|
4379
3802
|
const failures = parallelResults.filter((result) => result.exitCode !== 0 && result.exitCode !== -1);
|
|
4380
3803
|
const acceptanceFailures = parallelResults
|
|
4381
3804
|
.map((result, originalIndex) => ({ result, originalIndex, task: dynamicSteps[originalIndex] }))
|
|
4382
|
-
.filter(({ result, task }) =>
|
|
3805
|
+
.filter(({ result, task }) => isAgentContract(task?.agentContract ?? step.agentContract) && task?.gateOn === "acceptance" && result.acceptance?.status === "rejected");
|
|
4383
3806
|
if (acceptanceFailures.length > 0) {
|
|
4384
3807
|
const message = acceptanceFailures
|
|
4385
3808
|
.map(({ result, originalIndex }) => `Dynamic item ${originalIndex + 1} (${result.agent}, key ${materialized.items[originalIndex]?.key ?? originalIndex}) acceptance rejected: ${(result.acceptance ? acceptanceFailureMessage(result.acceptance) : undefined) ?? "acceptance rejected"}`)
|
|
@@ -4409,13 +3832,13 @@ async function runSubagent(
|
|
|
4409
3832
|
cwd,
|
|
4410
3833
|
signal: combinedAbortSignal([timeoutAbortController.signal, stopAbortController.signal]),
|
|
4411
3834
|
abortMessage: stopAbortController.signal.aborted ? stopMessage : timeoutMessage ?? "Subagent timed out.",
|
|
4412
|
-
reportOptional:
|
|
3835
|
+
reportOptional: isAgentContract(step.agentContract),
|
|
4413
3836
|
}))
|
|
4414
3837
|
: undefined;
|
|
4415
3838
|
const groupStopped = stopped || stopAbortController.signal.aborted;
|
|
4416
3839
|
const groupTimedOut = !groupStopped && (timedOut || timeoutAbortController.signal.aborted);
|
|
4417
3840
|
const effectiveGroupAcceptance = groupTimedOut || groupStopped ? undefined : groupAcceptance;
|
|
4418
|
-
const groupAcceptanceFailure = effectiveDynamicGroupAcceptance.explicit && effectiveGroupAcceptance && (!
|
|
3841
|
+
const groupAcceptanceFailure = effectiveDynamicGroupAcceptance.explicit && effectiveGroupAcceptance && (!isAgentContract(step.agentContract) || step.gateOn === "acceptance") ? acceptanceFailureMessage(effectiveGroupAcceptance) : undefined;
|
|
4419
3842
|
const groupError = groupStopped ? stopMessage : groupTimedOut ? timeoutMessage ?? "Subagent timed out." : groupAcceptanceFailure;
|
|
4420
3843
|
markDynamicGraphGroup(stepIndex, groupError ? groupStopped ? "stopped" : "failed" : "completed", groupError, effectiveGroupAcceptance);
|
|
4421
3844
|
if (groupError) {
|
|
@@ -4494,19 +3917,23 @@ async function runSubagent(
|
|
|
4494
3917
|
break;
|
|
4495
3918
|
}
|
|
4496
3919
|
try {
|
|
4497
|
-
worktreeSetup = createWorktrees(cwd, `${id}-s${stepIndex}`, group.parallel.length, omitUndefinedProperties({
|
|
3920
|
+
worktreeSetup = await createWorktrees(cwd, `${id}-s${stepIndex}`, group.parallel.length, omitUndefinedProperties({
|
|
3921
|
+
signal: setupSignal,
|
|
3922
|
+
deadlineAt: config.deadlineAt,
|
|
4498
3923
|
agents: group.parallel.map((task) => task.agent),
|
|
4499
3924
|
labels: group.parallel.map((task) => task.lane?.key ?? config.workflowKey ?? task.outputName ?? task.label),
|
|
4500
3925
|
tasks: group.parallel.map((task) => task.task),
|
|
4501
3926
|
provider: config.worktreeProvider,
|
|
3927
|
+
baseRef: config.baseRef,
|
|
4502
3928
|
branchPrefix: config.worktreeBranchPrefix,
|
|
4503
3929
|
setupHook: config.worktreeSetupHook
|
|
4504
3930
|
? omitUndefinedProperties({ hookPath: config.worktreeSetupHook, timeoutMs: config.worktreeSetupHookTimeoutMs })
|
|
4505
3931
|
: undefined,
|
|
4506
3932
|
baseDir: config.worktreeBaseDir,
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
const
|
|
3933
|
+
onProgress: (progress) => {
|
|
3934
|
+
publishSetupUnknown(progress);
|
|
3935
|
+
for (const worktree of progress.setup.worktrees) setStatusWorktreeReference(requiredStatusStep(statusPayload, groupStartFlatIndex + worktree.index), worktree);
|
|
3936
|
+
const pendingHandoff = writeWorktreeSetupHandoff({
|
|
4510
3937
|
manifestPath: parallelHandoffPath(asyncDir),
|
|
4511
3938
|
runId: id,
|
|
4512
3939
|
mode: (config.resultMode ?? statusPayload.mode) === "parallel" ? "parallel" : "chain",
|
|
@@ -4514,16 +3941,25 @@ async function runSubagent(
|
|
|
4514
3941
|
cwd,
|
|
4515
3942
|
stepIndex,
|
|
4516
3943
|
flatStartIndex: groupStartFlatIndex,
|
|
4517
|
-
|
|
3944
|
+
progress,
|
|
4518
3945
|
laneBindings: handoffWorkflowKey || config.lane ? [{ index: groupStartFlatIndex, taskIndex: 0, ...(handoffWorkflowKey ? { workflowKey: handoffWorkflowKey } : {}), ...(handoffChildRunId ? { runId: handoffChildRunId } : {}), ...(config.lane ? { lane: config.lane } : {}) }] : undefined,
|
|
4519
3946
|
});
|
|
3947
|
+
if (!pendingHandoff) return;
|
|
4520
3948
|
statusPayload.parallelHandoff = pendingHandoff;
|
|
4521
3949
|
statusPayload.lastUpdate = Date.now();
|
|
4522
3950
|
writeStatusPayload();
|
|
4523
3951
|
},
|
|
4524
3952
|
}));
|
|
3953
|
+
if (config.deadlineAt !== undefined && Date.now() >= config.deadlineAt) timeoutRunner();
|
|
3954
|
+
if (setupSignal.aborted) throw new Error(stopped ? stopMessage : timedOut ? timeoutMessage ?? "Subagent timed out." : "Subagent paused during worktree setup.");
|
|
4525
3955
|
} catch (error) {
|
|
3956
|
+
if (worktreeSetup) await cleanupRemainingWorktree(worktreeSetup, stepIndex, groupStartFlatIndex);
|
|
3957
|
+
if (config.deadlineAt !== undefined && Date.now() >= config.deadlineAt) timeoutRunner();
|
|
4526
3958
|
const setupError = error instanceof Error ? error.message : String(error);
|
|
3959
|
+
if (error instanceof WorktreeSetupError) publishSetupUnknown(error.snapshot);
|
|
3960
|
+
for (let index = groupStartFlatIndex; index < groupStartFlatIndex + group.parallel.length; index++) {
|
|
3961
|
+
if (childStopRequests.has(index)) markChildStopped(index);
|
|
3962
|
+
}
|
|
4527
3963
|
const failedAt = Date.now();
|
|
4528
3964
|
markParallelGroupSetupFailure({
|
|
4529
3965
|
statusPayload,
|
|
@@ -4636,11 +4072,9 @@ async function runSubagent(
|
|
|
4636
4072
|
artifactsDir, artifactConfig, id,
|
|
4637
4073
|
flatIndex: fi, flatStepCount: Math.max(statusPayload.steps.length, 1),
|
|
4638
4074
|
outputFile: path.join(asyncDir, `output-${fi}.log`),
|
|
4639
|
-
steerInboxDir: stepSteerInboxDir(asyncDir, fi),
|
|
4640
|
-
steerCapabilityPath: steerCapabilityPath(asyncDir, fi),
|
|
4641
|
-
steerAckDir: steerAcksDir(asyncDir, fi),
|
|
4642
4075
|
piPackageRoot: config.piPackageRoot,
|
|
4643
|
-
|
|
4076
|
+
childSessions,
|
|
4077
|
+
inheritedChildRuntime: config.inheritedChildRuntime,
|
|
4644
4078
|
childIntercomTarget: config.childIntercomTargets?.[fi],
|
|
4645
4079
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
4646
4080
|
nestedRoute: config.nestedRoute,
|
|
@@ -4649,6 +4083,7 @@ async function runSubagent(
|
|
|
4649
4083
|
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
4650
4084
|
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
4651
4085
|
registerStop: (stop) => registerStepStop(fi, stop),
|
|
4086
|
+
registerSteer: (steer) => registerStepSteer(fi, steer),
|
|
4652
4087
|
timeoutSignal: timeoutAbortController.signal,
|
|
4653
4088
|
stopSignal: stopAbortController.signal,
|
|
4654
4089
|
trackedMutationEvidenceForCompletionGuard: Boolean(worktreeSetup),
|
|
@@ -4657,11 +4092,11 @@ async function runSubagent(
|
|
|
4657
4092
|
toolTimeoutMs: taskForRun.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
4658
4093
|
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking, attempt.contextLimit),
|
|
4659
4094
|
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
4660
|
-
onWriterProcess,
|
|
4661
4095
|
onExternalProcess: (process) => updateExternalProcess(fi, process),
|
|
4662
4096
|
onExternalJob: (externalJob) => updateExternalJob(fi, externalJob),
|
|
4663
4097
|
skipAcceptance: () => timedOut || stopped || childStopRequests.has(fi),
|
|
4664
|
-
usageBudgetExhausted:
|
|
4098
|
+
usageBudgetExhausted: continuationUsageBudgetExhausted,
|
|
4099
|
+
usageBudget: config.usageBudget,
|
|
4665
4100
|
orcaProgressTab,
|
|
4666
4101
|
}), config.deadlineAt);
|
|
4667
4102
|
if (task.sessionFile) {
|
|
@@ -4785,7 +4220,6 @@ async function runSubagent(
|
|
|
4785
4220
|
output: pr.output,
|
|
4786
4221
|
outputState: pr.outputState,
|
|
4787
4222
|
error: pr.error,
|
|
4788
|
-
protocolError: pr.protocolError,
|
|
4789
4223
|
success: pr.stopped !== true && pr.interrupted !== true && pr.exitCode === 0,
|
|
4790
4224
|
exitCode: pr.interrupted === true ? 0 : pr.exitCode,
|
|
4791
4225
|
skipped: pr.skipped,
|
|
@@ -4840,47 +4274,50 @@ async function runSubagent(
|
|
|
4840
4274
|
})),
|
|
4841
4275
|
);
|
|
4842
4276
|
if (worktreeSetup) {
|
|
4843
|
-
const
|
|
4844
|
-
if (captured.summary) previousOutput = `${previousOutput}\n\n${captured.summary}`;
|
|
4277
|
+
const setup = worktreeSetup;
|
|
4845
4278
|
worktreeFinalized = true;
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4279
|
+
await finalizeWorktree(setup, stepIndex, groupStartFlatIndex, () => {
|
|
4280
|
+
const captured = captureParallelWorktreeDiffs(setup, asyncDir, stepIndex, group);
|
|
4281
|
+
if (captured.summary) previousOutput = `${previousOutput}\n\n${captured.summary}`;
|
|
4282
|
+
const manifestPath = parallelHandoffPath(asyncDir);
|
|
4283
|
+
const handoff = {
|
|
4284
|
+
manifestPath,
|
|
4285
|
+
runId: id,
|
|
4286
|
+
mode: (config.resultMode ?? statusPayload.mode) === "parallel" ? "parallel" as const : "chain" as const,
|
|
4287
|
+
source: "async" as const,
|
|
4288
|
+
cwd,
|
|
4289
|
+
stepIndex,
|
|
4290
|
+
flatStartIndex: groupStartFlatIndex,
|
|
4291
|
+
setup,
|
|
4292
|
+
diffs: captured.diffs,
|
|
4293
|
+
results: parallelResults.map((result) => ({
|
|
4294
|
+
agent: result.agent,
|
|
4295
|
+
...(handoffWorkflowKey ? { workflowKey: handoffWorkflowKey } : {}),
|
|
4296
|
+
...(handoffChildRunId ? { runId: handoffChildRunId } : {}),
|
|
4297
|
+
...(config.lane ? { lane: config.lane } : {}),
|
|
4298
|
+
status: result.stopped || (result.exitCode !== 0 && isUnexplainedProcessSignal(omitUndefinedProperties({
|
|
4299
|
+
processSignal: result.processSignal,
|
|
4300
|
+
interrupted: result.interrupted,
|
|
4301
|
+
timedOut: result.timedOut,
|
|
4302
|
+
stopped: result.stopped,
|
|
4303
|
+
}))) ? "stopped" as const : result.interrupted ? "paused" as const : result.exitCode === 0 ? "completed" as const : "failed" as const,
|
|
4304
|
+
summary: result.output || result.error || "(no output)",
|
|
4305
|
+
...(result.artifactPaths?.outputPath ? { outputPath: result.artifactPaths.outputPath } : {}),
|
|
4306
|
+
...(result.structuredOutput !== undefined ? { structuredOutput: result.structuredOutput } : {}),
|
|
4307
|
+
...(result.structuredOutputPath ? { structuredOutputPath: result.structuredOutputPath } : {}),
|
|
4308
|
+
...(result.sessionFile ? { sessionPath: result.sessionFile } : {}),
|
|
4309
|
+
})),
|
|
4310
|
+
};
|
|
4311
|
+
try {
|
|
4312
|
+
writeParallelHandoffGroup(handoff);
|
|
4313
|
+
const cleanup = cleanupWorktrees(setup, { kind: "preserve", capturedDiffs: captured.diffs, handoffManifestPath: manifestPath });
|
|
4314
|
+
statusPayload.parallelHandoff = writeParallelHandoffGroup({ ...handoff, cleanup });
|
|
4315
|
+
previousOutput = `${previousOutput}\n\n${formatParallelHandoffReference(statusPayload.parallelHandoff)}`;
|
|
4316
|
+
} catch (error) {
|
|
4317
|
+
previousOutput = `${previousOutput}\n\n${formatParallelHandoffError(error)}`;
|
|
4318
|
+
}
|
|
4319
|
+
writeStatusPayload();
|
|
4320
|
+
});
|
|
4884
4321
|
}
|
|
4885
4322
|
|
|
4886
4323
|
appendJsonl(eventsPath, JSON.stringify({
|
|
@@ -4889,12 +4326,12 @@ async function runSubagent(
|
|
|
4889
4326
|
runId: id,
|
|
4890
4327
|
stepIndex,
|
|
4891
4328
|
success: parallelResults.every((r) => r.exitCode === 0 || r.exitCode === -1)
|
|
4892
|
-
&& parallelResults.every((result, index) => !(
|
|
4329
|
+
&& parallelResults.every((result, index) => !(isAgentContract(group.parallel[index]?.agentContract) && group.parallel[index]?.gateOn === "acceptance" && result.acceptance?.status === "rejected")),
|
|
4893
4330
|
}));
|
|
4894
4331
|
|
|
4895
4332
|
const acceptanceGateFailure = parallelResults
|
|
4896
4333
|
.map((result, index) => ({ result, index, task: group.parallel[index] }))
|
|
4897
|
-
.find(({ result, task }) =>
|
|
4334
|
+
.find(({ result, task }) => isAgentContract(task?.agentContract) && task?.gateOn === "acceptance" && result.acceptance?.status === "rejected");
|
|
4898
4335
|
if (acceptanceGateFailure) {
|
|
4899
4336
|
statusPayload.error = (acceptanceGateFailure.result.acceptance ? acceptanceFailureMessage(acceptanceGateFailure.result.acceptance) : undefined) ?? "Parallel acceptance gate rejected the step.";
|
|
4900
4337
|
writeStatusPayload();
|
|
@@ -4904,7 +4341,7 @@ async function runSubagent(
|
|
|
4904
4341
|
break;
|
|
4905
4342
|
}
|
|
4906
4343
|
} finally {
|
|
4907
|
-
if (worktreeSetup && !worktreeFinalized)
|
|
4344
|
+
if (worktreeSetup && !worktreeFinalized) await cleanupRemainingWorktree(worktreeSetup, stepIndex, groupStartFlatIndex);
|
|
4908
4345
|
}
|
|
4909
4346
|
} else {
|
|
4910
4347
|
const seqStep = step as SubagentStep;
|
|
@@ -4931,22 +4368,26 @@ async function runSubagent(
|
|
|
4931
4368
|
let singleWorktreeSetup: WorktreeSetup | undefined;
|
|
4932
4369
|
if (seqStep.worktree) {
|
|
4933
4370
|
try {
|
|
4934
|
-
singleWorktreeSetup = createWorktrees(cwd, `${id}-s${stepIndex}`, 1, omitUndefinedProperties({
|
|
4371
|
+
singleWorktreeSetup = await createWorktrees(cwd, `${id}-s${stepIndex}`, 1, omitUndefinedProperties({
|
|
4372
|
+
signal: setupSignal,
|
|
4373
|
+
deadlineAt: config.deadlineAt,
|
|
4935
4374
|
agents: [seqStep.agent],
|
|
4936
4375
|
labels: [seqStep.lane?.key ?? config.workflowKey ?? seqStep.outputName ?? seqStep.label],
|
|
4937
4376
|
tasks: [seqStep.task],
|
|
4938
4377
|
provider: config.worktreeProvider,
|
|
4378
|
+
baseRef: config.baseRef,
|
|
4939
4379
|
branchPrefix: config.worktreeBranchPrefix,
|
|
4940
4380
|
setupHook: config.worktreeSetupHook
|
|
4941
4381
|
? omitUndefinedProperties({ hookPath: config.worktreeSetupHook, timeoutMs: config.worktreeSetupHookTimeoutMs })
|
|
4942
4382
|
: undefined,
|
|
4943
4383
|
baseDir: config.worktreeBaseDir,
|
|
4944
|
-
|
|
4945
|
-
|
|
4384
|
+
onProgress: (progress) => {
|
|
4385
|
+
publishSetupUnknown(progress);
|
|
4386
|
+
const worktree = progress.setup.worktrees[0];
|
|
4946
4387
|
if (worktree) {
|
|
4947
4388
|
setStatusWorktreeReference(requiredStatusStep(statusPayload, flatIndex), worktree);
|
|
4948
4389
|
}
|
|
4949
|
-
const pendingHandoff =
|
|
4390
|
+
const pendingHandoff = writeWorktreeSetupHandoff({
|
|
4950
4391
|
manifestPath: parallelHandoffPath(asyncDir),
|
|
4951
4392
|
runId: id,
|
|
4952
4393
|
mode: "single",
|
|
@@ -4954,19 +4395,47 @@ async function runSubagent(
|
|
|
4954
4395
|
cwd,
|
|
4955
4396
|
stepIndex,
|
|
4956
4397
|
flatStartIndex: flatIndex,
|
|
4957
|
-
|
|
4398
|
+
progress,
|
|
4958
4399
|
laneBindings: handoffWorkflowKey || config.lane ? [{ index: flatIndex, taskIndex: 0, ...(handoffWorkflowKey ? { workflowKey: handoffWorkflowKey } : {}), ...(handoffChildRunId ? { runId: handoffChildRunId } : {}), ...(config.lane ? { lane: config.lane } : {}) }] : undefined,
|
|
4959
4400
|
});
|
|
4401
|
+
if (!pendingHandoff) return;
|
|
4960
4402
|
statusPayload.parallelHandoff = pendingHandoff;
|
|
4961
4403
|
statusPayload.lastUpdate = Date.now();
|
|
4962
4404
|
writeStatusPayload();
|
|
4963
4405
|
},
|
|
4964
4406
|
}));
|
|
4965
4407
|
} catch (error) {
|
|
4966
|
-
if (
|
|
4967
|
-
|
|
4408
|
+
if (error instanceof WorktreeSetupError) publishSetupUnknown(error.snapshot);
|
|
4409
|
+
if (config.deadlineAt !== undefined && Date.now() >= config.deadlineAt) timeoutRunner();
|
|
4410
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4411
|
+
if (childStopRequests.has(flatIndex)) markChildStopped(flatIndex);
|
|
4412
|
+
const statusStep = requiredStatusStep(statusPayload, flatIndex);
|
|
4413
|
+
statusStep.status = stopped || childStopRequests.has(flatIndex) ? "stopped" : interrupted ? "paused" : "failed";
|
|
4414
|
+
statusStep.error ??= message;
|
|
4415
|
+
statusStep.exitCode = interrupted ? 0 : 1;
|
|
4416
|
+
statusStep.endedAt = Date.now();
|
|
4417
|
+
results.push(stopped ? stoppedStepResult(seqStep.agent, seqStep.context, seqStep.sessionName)
|
|
4418
|
+
: childStopRequests.has(flatIndex) ? childStopResult(flatIndex, seqStep.agent, seqStep.context)
|
|
4419
|
+
: timedOut ? timedOutStepResult(seqStep.agent, seqStep.context, seqStep.sessionName)
|
|
4420
|
+
: interrupted ? pausedStepResult(seqStep.agent, seqStep.context, seqStep.sessionName)
|
|
4421
|
+
: { agent: seqStep.agent, output: message, error: message, exitCode: 1, success: false });
|
|
4422
|
+
statusPayload.error ??= message;
|
|
4423
|
+
writeStatusPayload();
|
|
4424
|
+
flatIndex++;
|
|
4425
|
+
break;
|
|
4968
4426
|
}
|
|
4969
4427
|
}
|
|
4428
|
+
if (singleWorktreeSetup && config.deadlineAt !== undefined && Date.now() >= config.deadlineAt) timeoutRunner();
|
|
4429
|
+
if (singleWorktreeSetup && (timedOut || stopped || interrupted || childStopRequests.has(flatIndex))) {
|
|
4430
|
+
await cleanupRemainingWorktree(singleWorktreeSetup, stepIndex, flatIndex);
|
|
4431
|
+
results.push(stopped ? stoppedStepResult(seqStep.agent, seqStep.context, seqStep.sessionName)
|
|
4432
|
+
: childStopRequests.has(flatIndex) ? childStopResult(flatIndex, seqStep.agent, seqStep.context)
|
|
4433
|
+
: timedOut ? timedOutStepResult(seqStep.agent, seqStep.context, seqStep.sessionName)
|
|
4434
|
+
: pausedStepResult(seqStep.agent, seqStep.context, seqStep.sessionName));
|
|
4435
|
+
if (interrupted) requiredStatusStep(statusPayload, flatIndex).status = "paused";
|
|
4436
|
+
flatIndex++;
|
|
4437
|
+
continue;
|
|
4438
|
+
}
|
|
4970
4439
|
const singleCwd = singleWorktreeSetup?.worktrees[0]?.agentCwd ?? cwd;
|
|
4971
4440
|
const stepStartTime = Date.now();
|
|
4972
4441
|
statusPayload.currentStep = flatIndex;
|
|
@@ -5003,11 +4472,9 @@ async function runSubagent(
|
|
|
5003
4472
|
artifactsDir, artifactConfig, id,
|
|
5004
4473
|
flatIndex, flatStepCount: Math.max(statusPayload.steps.length, 1),
|
|
5005
4474
|
outputFile: path.join(asyncDir, `output-${flatIndex}.log`),
|
|
5006
|
-
steerInboxDir: stepSteerInboxDir(asyncDir, flatIndex),
|
|
5007
|
-
steerCapabilityPath: steerCapabilityPath(asyncDir, flatIndex),
|
|
5008
|
-
steerAckDir: steerAcksDir(asyncDir, flatIndex),
|
|
5009
4475
|
piPackageRoot: config.piPackageRoot,
|
|
5010
|
-
|
|
4476
|
+
childSessions,
|
|
4477
|
+
inheritedChildRuntime: config.inheritedChildRuntime,
|
|
5011
4478
|
childIntercomTarget: config.childIntercomTargets?.[flatIndex],
|
|
5012
4479
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
5013
4480
|
nestedRoute: config.nestedRoute,
|
|
@@ -5016,6 +4483,7 @@ async function runSubagent(
|
|
|
5016
4483
|
registerInterrupt: (interrupt) => registerStepInterrupt(flatIndex, interrupt),
|
|
5017
4484
|
registerTimeout: (interrupt) => registerStepTimeout(flatIndex, interrupt),
|
|
5018
4485
|
registerStop: (stop) => registerStepStop(flatIndex, stop),
|
|
4486
|
+
registerSteer: (steer) => registerStepSteer(flatIndex, steer),
|
|
5019
4487
|
timeoutSignal: timeoutAbortController.signal,
|
|
5020
4488
|
stopSignal: stopAbortController.signal,
|
|
5021
4489
|
timeoutMessage,
|
|
@@ -5023,15 +4491,15 @@ async function runSubagent(
|
|
|
5023
4491
|
toolTimeoutMs: seqStep.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
5024
4492
|
onAttemptStart: (attempt) => updateStepModel(flatIndex, attempt.model, attempt.thinking, attempt.contextLimit),
|
|
5025
4493
|
onChildEvent: (event) => updateStepFromChildEvent(flatIndex, event),
|
|
5026
|
-
onWriterProcess,
|
|
5027
4494
|
onExternalProcess: (process) => updateExternalProcess(flatIndex, process),
|
|
5028
4495
|
onExternalJob: (externalJob) => updateExternalJob(flatIndex, externalJob),
|
|
5029
4496
|
skipAcceptance: () => timedOut || stopped || childStopRequests.has(flatIndex),
|
|
5030
|
-
usageBudgetExhausted:
|
|
4497
|
+
usageBudgetExhausted: continuationUsageBudgetExhausted,
|
|
4498
|
+
usageBudget: config.usageBudget,
|
|
5031
4499
|
orcaProgressTab,
|
|
5032
4500
|
}), config.deadlineAt);
|
|
5033
4501
|
} catch (error) {
|
|
5034
|
-
if (singleWorktreeSetup)
|
|
4502
|
+
if (singleWorktreeSetup) await cleanupRemainingWorktree(singleWorktreeSetup, stepIndex, flatIndex);
|
|
5035
4503
|
throw error;
|
|
5036
4504
|
}
|
|
5037
4505
|
if (seqStep.sessionFile) {
|
|
@@ -5051,7 +4519,6 @@ async function runSubagent(
|
|
|
5051
4519
|
output: stopped || childStopped ? stopMessage : timedOut ? singleResult.output || (timeoutMessage ?? "Subagent timed out.") : singleResult.output,
|
|
5052
4520
|
outputState: singleResult.outputState,
|
|
5053
4521
|
error: stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.error,
|
|
5054
|
-
protocolError: singleResult.protocolError,
|
|
5055
4522
|
success: !stopped && !childStopped && !timedOut && singleResult.interrupted !== true && singleResult.exitCode === 0,
|
|
5056
4523
|
exitCode: stopped || childStopped ? 1 : timedOut ? 1 : singleResult.interrupted === true ? 0 : singleResult.exitCode,
|
|
5057
4524
|
sessionFile: singleResult.sessionFile,
|
|
@@ -5064,6 +4531,7 @@ async function runSubagent(
|
|
|
5064
4531
|
totalCost: singleResult.totalCost,
|
|
5065
4532
|
usage: singleResult.usage,
|
|
5066
4533
|
artifactPaths: singleResult.artifactPaths,
|
|
4534
|
+
savedOutputPath: singleResult.savedOutputPath,
|
|
5067
4535
|
transcriptPath: singleResult.transcriptPath,
|
|
5068
4536
|
transcriptError: singleResult.transcriptError,
|
|
5069
4537
|
effects: singleResult.effects,
|
|
@@ -5184,48 +4652,51 @@ async function runSubagent(
|
|
|
5184
4652
|
}));
|
|
5185
4653
|
if (stopped || childStopped) appendTerminalChildStatusEvent(flatIndex, stepEndTime);
|
|
5186
4654
|
if (singleWorktreeSetup && !singleResult.detached) {
|
|
5187
|
-
const
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
manifestPath
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
4655
|
+
const setup = singleWorktreeSetup;
|
|
4656
|
+
await finalizeWorktree(setup, stepIndex, flatIndex, () => {
|
|
4657
|
+
const diffs = diffWorktrees(setup, [seqStep.agent], path.join(asyncDir, "worktree-diffs", `step-${stepIndex}`));
|
|
4658
|
+
const diffSummary = formatWorktreeDiffSummary(diffs);
|
|
4659
|
+
const manifestPath = parallelHandoffPath(asyncDir);
|
|
4660
|
+
const handoff = {
|
|
4661
|
+
manifestPath,
|
|
4662
|
+
runId: id,
|
|
4663
|
+
mode: "single" as const,
|
|
4664
|
+
source: "async" as const,
|
|
4665
|
+
cwd,
|
|
4666
|
+
stepIndex,
|
|
4667
|
+
flatStartIndex: flatIndex,
|
|
4668
|
+
setup,
|
|
4669
|
+
diffs,
|
|
4670
|
+
results: [{
|
|
4671
|
+
agent: singleResult.agent,
|
|
4672
|
+
...(handoffWorkflowKey ? { workflowKey: handoffWorkflowKey } : {}),
|
|
4673
|
+
...(handoffChildRunId ? { runId: handoffChildRunId } : {}),
|
|
4674
|
+
...(config.lane ? { lane: config.lane } : {}),
|
|
4675
|
+
status: singleResult.stopped ? "stopped" as const : singleResult.interrupted ? "paused" as const : singleResult.exitCode === 0 ? "completed" as const : "failed" as const,
|
|
4676
|
+
summary: singleResult.output || singleResult.error || "(no output)",
|
|
4677
|
+
...(singleResult.artifactPaths?.outputPath ? { outputPath: singleResult.artifactPaths.outputPath } : {}),
|
|
4678
|
+
...(singleResult.structuredOutput !== undefined ? { structuredOutput: singleResult.structuredOutput } : {}),
|
|
4679
|
+
...(singleResult.structuredOutputPath ? { structuredOutputPath: singleResult.structuredOutputPath } : {}),
|
|
4680
|
+
...(singleResult.sessionFile ? { sessionPath: singleResult.sessionFile } : {}),
|
|
4681
|
+
}],
|
|
4682
|
+
};
|
|
4683
|
+
try {
|
|
4684
|
+
writeParallelHandoffGroup(handoff);
|
|
4685
|
+
const cleanup = cleanupWorktrees(setup, {
|
|
4686
|
+
kind: "preserve",
|
|
4687
|
+
capturedDiffs: diffs,
|
|
4688
|
+
handoffManifestPath: manifestPath,
|
|
4689
|
+
...(config.parentWorkflowRunId && singleResult.sessionFile && fs.existsSync(singleResult.sessionFile) && !singleResult.stopped
|
|
4690
|
+
? { cleanupBlocker: "retained child resume requires managed worktree cwd" }
|
|
4691
|
+
: {}),
|
|
4692
|
+
});
|
|
4693
|
+
statusPayload.parallelHandoff = writeParallelHandoffGroup({ ...handoff, cleanup });
|
|
4694
|
+
previousOutput = [previousOutput, diffSummary, formatParallelHandoffReference(statusPayload.parallelHandoff)].filter(Boolean).join("\n\n");
|
|
4695
|
+
} catch (error) {
|
|
4696
|
+
previousOutput = [previousOutput, diffSummary, formatParallelHandoffError(error)].filter(Boolean).join("\n\n");
|
|
4697
|
+
}
|
|
4698
|
+
writeStatusPayload();
|
|
4699
|
+
});
|
|
5229
4700
|
}
|
|
5230
4701
|
|
|
5231
4702
|
if (singleResult.completionGuardTriggered) {
|
|
@@ -5243,7 +4714,7 @@ async function runSubagent(
|
|
|
5243
4714
|
}
|
|
5244
4715
|
|
|
5245
4716
|
flatIndex++;
|
|
5246
|
-
if (
|
|
4717
|
+
if (isAgentContract(seqStep.agentContract) && seqStep.gateOn === "acceptance" && singleResult.acceptance?.status === "rejected") {
|
|
5247
4718
|
statusPayload.error = acceptanceFailureMessage(singleResult.acceptance) ?? "Chain acceptance gate rejected the step.";
|
|
5248
4719
|
writeStatusPayload();
|
|
5249
4720
|
break;
|
|
@@ -5319,7 +4790,7 @@ async function runSubagent(
|
|
|
5319
4790
|
clearTimeout(timeoutTimer);
|
|
5320
4791
|
timeoutTimer = undefined;
|
|
5321
4792
|
}
|
|
5322
|
-
if (!timedOut && !stopped && !interrupted && config.timeoutMs !== undefined && results.some((result) => result.timedOut === true && result.error
|
|
4793
|
+
if (!timedOut && !stopped && !interrupted && config.timeoutMs !== undefined && timeoutMessage !== undefined && results.some((result) => result.timedOut === true && result.error?.startsWith(timeoutMessage))) {
|
|
5323
4794
|
timedOut = true;
|
|
5324
4795
|
}
|
|
5325
4796
|
const signalTerminated = !stopped && !timedOut && !interrupted && results.some((result) => result.exitCode !== 0 && isUnexplainedProcessSignal(omitUndefinedProperties({
|
|
@@ -5329,6 +4800,11 @@ async function runSubagent(
|
|
|
5329
4800
|
stopped: result.stopped,
|
|
5330
4801
|
})));
|
|
5331
4802
|
const partialWithEvidence = !stopped && !signalTerminated && !timedOut && !usageBudgetExceeded && !interrupted && results.some(partialEvidenceResult) && !results.some(concreteFailureResult);
|
|
4803
|
+
// Flush while still nonterminal; deferred status retries retain that state snapshot.
|
|
4804
|
+
statusWriteCoalescer.flush(statusPath);
|
|
4805
|
+
const publication = new Promise<void>((resolve, reject) => {
|
|
4806
|
+
finalResultPublication = { resolve, reject };
|
|
4807
|
+
});
|
|
5332
4808
|
statusPayload.state = stopped || signalTerminated ? "stopped" : timedOut || usageBudgetExceeded ? "failed" : interrupted ? "paused" : results.every((r) => r.success) ? "complete" : partialWithEvidence ? "partial" : "failed";
|
|
5333
4809
|
closeSteerInbox(asyncDir, statusPayload.state, (filePath, payload) => runPersistence.write(filePath, payload));
|
|
5334
4810
|
disposeControlInbox();
|
|
@@ -5387,6 +4863,9 @@ async function runSubagent(
|
|
|
5387
4863
|
statusPayload.error = `Step failed: ${failedStep.agent}`;
|
|
5388
4864
|
}
|
|
5389
4865
|
}
|
|
4866
|
+
let childSessionDisposal: Promise<void> | undefined;
|
|
4867
|
+
const disposeChildSessions = (): Promise<void> => childSessionDisposal ??= childSessions.dispose()
|
|
4868
|
+
.catch((error: unknown) => console.error("Failed to dispose runner child sessions:", error));
|
|
5390
4869
|
try {
|
|
5391
4870
|
runPersistence.write(resultPath, {
|
|
5392
4871
|
lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
@@ -5409,7 +4888,6 @@ async function runSubagent(
|
|
|
5409
4888
|
output: r.output,
|
|
5410
4889
|
outputState: r.outputState,
|
|
5411
4890
|
error: r.error,
|
|
5412
|
-
protocolError: r.protocolError,
|
|
5413
4891
|
success: r.success,
|
|
5414
4892
|
skipped: r.skipped || undefined,
|
|
5415
4893
|
interrupted: r.interrupted || undefined,
|
|
@@ -5428,6 +4906,7 @@ async function runSubagent(
|
|
|
5428
4906
|
totalCost: r.totalCost,
|
|
5429
4907
|
usage: r.usage,
|
|
5430
4908
|
artifactPaths: r.artifactPaths,
|
|
4909
|
+
savedOutputPath: r.savedOutputPath,
|
|
5431
4910
|
outputSaveError: r.outputSaveError,
|
|
5432
4911
|
metadataSaveError: r.metadataSaveError,
|
|
5433
4912
|
truncated: r.truncated,
|
|
@@ -5482,16 +4961,19 @@ async function runSubagent(
|
|
|
5482
4961
|
...(taskIndex !== undefined && { taskIndex }),
|
|
5483
4962
|
...(totalTasks !== undefined && { totalTasks }),
|
|
5484
4963
|
}, (filePath, payload) => { writeAsyncResultFile(filePath, payload as Record<string, unknown>); });
|
|
5485
|
-
|
|
4964
|
+
// Only capacity deferral releases settled sessions before terminal publication.
|
|
4965
|
+
if (!finalResultCommitted) await Promise.all([publication, disposeChildSessions()]);
|
|
5486
4966
|
} catch (err) {
|
|
5487
4967
|
const message = `Failed to write result file ${resultPath}: ${err instanceof Error ? err.message : String(err)}`;
|
|
5488
4968
|
console.error(message, err);
|
|
5489
4969
|
statusPayload.state = "failed";
|
|
5490
4970
|
statusPayload.error = message;
|
|
5491
4971
|
statusPayload.lastUpdate = Date.now();
|
|
4972
|
+
} finally {
|
|
4973
|
+
finalResultPublication = undefined;
|
|
5492
4974
|
}
|
|
5493
4975
|
writeStatusPayload();
|
|
5494
|
-
orcaProgressTab?.finish(statusPayload.state === "complete" ? "completed" : statusPayload.state === "stopped" ? "stopped" : "failed", effectiveSessionFile);
|
|
4976
|
+
await orcaProgressTab?.finish(statusPayload.state === "complete" ? "completed" : statusPayload.state === "stopped" ? "stopped" : "failed", effectiveSessionFile);
|
|
5495
4977
|
appendJsonl(
|
|
5496
4978
|
eventsPath,
|
|
5497
4979
|
JSON.stringify({
|
|
@@ -5526,14 +5008,20 @@ async function runSubagent(
|
|
|
5526
5008
|
}), (filePath, content) => runPersistence.write(filePath, { content }, (_path, payload) => {
|
|
5527
5009
|
fs.writeFileSync(_path, (payload as { content: string }).content, "utf-8");
|
|
5528
5010
|
}));
|
|
5529
|
-
|
|
5530
|
-
|
|
5011
|
+
// Preserve normal-success disposal ordering, then drain remaining persistence retries.
|
|
5012
|
+
await disposeChildSessions();
|
|
5013
|
+
while (runPersistence.pendingCount() + indexPersistence.pendingCount() > 0) {
|
|
5014
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
5015
|
+
}
|
|
5016
|
+
runPersistence.dispose();
|
|
5017
|
+
indexPersistence.dispose();
|
|
5531
5018
|
if (config.runnerProcessInstanceId) {
|
|
5532
|
-
|
|
5019
|
+
// Children run inside this process, so no step has writer processes to prove terminal.
|
|
5020
|
+
const writers: Record<string, PiWriterProcessInstanceExit[]> = {};
|
|
5533
5021
|
const expectedWriters: Record<string, number> = {};
|
|
5534
|
-
for (const
|
|
5535
|
-
writers[String(index)] =
|
|
5536
|
-
expectedWriters[String(index)] =
|
|
5022
|
+
for (const index of results.keys()) {
|
|
5023
|
+
writers[String(index)] = [];
|
|
5024
|
+
expectedWriters[String(index)] = 0;
|
|
5537
5025
|
}
|
|
5538
5026
|
const candidate: ProcessTerminalCandidate = {
|
|
5539
5027
|
version: 1,
|
|
@@ -5615,7 +5103,16 @@ async function runConfiguredSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
5615
5103
|
}
|
|
5616
5104
|
}
|
|
5617
5105
|
}
|
|
5618
|
-
|
|
5106
|
+
const childSessions = await loadRunnerChildSessionFactory(config);
|
|
5107
|
+
try {
|
|
5108
|
+
await runSubagent(config, childSessions);
|
|
5109
|
+
} finally {
|
|
5110
|
+
try {
|
|
5111
|
+
await childSessions.dispose();
|
|
5112
|
+
} catch (error) {
|
|
5113
|
+
console.error("Failed to dispose runner child sessions:", error);
|
|
5114
|
+
}
|
|
5115
|
+
}
|
|
5619
5116
|
} catch (error) {
|
|
5620
5117
|
if (!startupCommitted) {
|
|
5621
5118
|
try {
|
|
@@ -5644,12 +5141,19 @@ async function runConfiguredSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
5644
5141
|
}
|
|
5645
5142
|
|
|
5646
5143
|
function startConfiguredSubagent(config: SubagentRunConfig): void {
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5144
|
+
// Child sessions and the extensions loaded into them may leave handles
|
|
5145
|
+
// behind even after shutdown; the run is fully persisted by now, so exit
|
|
5146
|
+
// explicitly instead of waiting for the event loop to drain.
|
|
5147
|
+
runConfiguredSubagent(config).then(
|
|
5148
|
+
() => process.exit(0),
|
|
5149
|
+
(runErr) => {
|
|
5150
|
+
console.error("Subagent runner error:", runErr);
|
|
5151
|
+
process.exit(1);
|
|
5152
|
+
},
|
|
5153
|
+
);
|
|
5651
5154
|
}
|
|
5652
5155
|
|
|
5156
|
+
if (isRunnerEntrypoint) {
|
|
5653
5157
|
const configArg = process.argv[2];
|
|
5654
5158
|
if (configArg) {
|
|
5655
5159
|
try {
|
|
@@ -5680,4 +5184,5 @@ if (configArg) {
|
|
|
5680
5184
|
process.exit(1);
|
|
5681
5185
|
}
|
|
5682
5186
|
});
|
|
5683
|
-
}
|
|
5187
|
+
}
|
|
5188
|
+
}
|