@selesai/code 0.8.2 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +4 -2
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.js +18 -0
- package/dist/cli/config-selector.js +2 -2
- package/dist/cli/startup-ui.d.ts +1 -1
- package/dist/cli/startup-ui.js +2 -2
- package/dist/core/keybindings.d.ts +44 -4
- package/dist/core/llama/provider.js +22 -7
- package/dist/core/model-resolver.js +2 -0
- package/dist/core/model-runtime.d.ts +4 -2
- package/dist/core/model-runtime.js +23 -2
- package/dist/core/remote-catalog-provider.js +72 -67
- package/dist/core/settings-manager.d.ts +8 -0
- package/dist/core/settings-manager.js +18 -0
- package/dist/extensions/handoff-new.ts +1 -1
- package/dist/extensions/inline-skills.test.ts +1 -0
- package/dist/extensions/inline-skills.ts +1 -1
- package/dist/extensions/pi-intercom/CHANGELOG.md +25 -3
- package/dist/extensions/pi-intercom/README.md +34 -15
- package/dist/extensions/pi-intercom/broker/broker.ts +38 -141
- package/dist/extensions/pi-intercom/broker/client-liveness.test.ts +173 -0
- package/dist/extensions/pi-intercom/broker/client.ts +78 -150
- package/dist/extensions/pi-intercom/broker/extension-state.ts +18 -7
- package/dist/extensions/pi-intercom/broker/framing.ts +1 -1
- package/dist/extensions/pi-intercom/broker/protocol.ts +182 -0
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +106 -7
- package/dist/extensions/pi-intercom/broker/spawn.ts +37 -11
- package/dist/extensions/pi-intercom/config.test.ts +8 -10
- package/dist/extensions/pi-intercom/config.ts +3 -3
- package/dist/extensions/pi-intercom/index.ts +218 -147
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +891 -103
- package/dist/extensions/pi-intercom/package.json +3 -2
- package/dist/extensions/pi-intercom/project-agent.test.ts +145 -0
- package/dist/extensions/pi-intercom/project-agent.ts +324 -0
- package/dist/extensions/pi-intercom/reply-tracker.test.ts +86 -1
- package/dist/extensions/pi-intercom/reply-tracker.ts +41 -9
- package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +49 -9
- package/dist/extensions/pi-intercom/test/inline-message.test.ts +9 -9
- package/dist/extensions/pi-intercom/types.ts +3 -1
- package/dist/extensions/pi-intercom/ui/inline-message.ts +7 -7
- package/dist/extensions/pi-powerline-footer/.github/workflows/test.yml +2 -2
- package/dist/extensions/pi-powerline-footer/CHANGELOG.md +15 -1
- package/dist/extensions/pi-powerline-footer/README.md +20 -37
- package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +40 -25
- package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +149 -18
- package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +1 -0
- package/dist/extensions/pi-powerline-footer/editor-performance.ts +99 -0
- package/dist/extensions/pi-powerline-footer/git-status.ts +15 -0
- package/dist/extensions/pi-powerline-footer/index.ts +394 -586
- package/dist/extensions/pi-powerline-footer/package.json +5 -6
- package/dist/extensions/pi-powerline-footer/powerline-config.ts +0 -18
- package/dist/extensions/pi-powerline-footer/queue/store.ts +8 -56
- package/dist/extensions/pi-powerline-footer/queue/types.ts +1 -2
- package/dist/extensions/pi-powerline-footer/segments.ts +0 -3
- package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +231 -1
- package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +5 -19
- package/dist/extensions/pi-powerline-footer/tests/editor-performance.test.ts +43 -0
- package/dist/extensions/pi-powerline-footer/tests/fixed-editor-border.test.ts +2 -2
- package/dist/extensions/pi-powerline-footer/tests/git-optional-locks.test.ts +64 -0
- package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +1 -1
- package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +86 -19
- package/dist/extensions/pi-powerline-footer/tests/queue-store.test.ts +19 -149
- package/dist/extensions/pi-powerline-footer/tests/remaining-regressions.test.ts +6 -0
- package/dist/extensions/pi-powerline-footer/tests/usage-display.test.ts +5 -5
- package/dist/extensions/pi-powerline-footer/tsconfig.json +1 -1
- package/dist/extensions/pi-powerline-footer/types.ts +1 -2
- package/dist/extensions/pi-subagents/CHANGELOG.md +202 -14
- package/dist/extensions/pi-subagents/README.md +65 -1614
- package/dist/extensions/pi-subagents/agents/builder.md +0 -1
- package/dist/extensions/pi-subagents/agents/delegate.md +14 -0
- package/dist/extensions/pi-subagents/agents/oracle.md +74 -0
- package/dist/extensions/pi-subagents/agents/reviewer.md +78 -0
- package/dist/extensions/pi-subagents/agents/scout.md +50 -0
- package/dist/extensions/pi-subagents/agents/worker.md +58 -0
- package/dist/extensions/pi-subagents/docs/configuration.md +328 -0
- package/dist/extensions/pi-subagents/docs/extension-api.md +308 -0
- package/dist/extensions/pi-subagents/docs/missions.md +119 -0
- package/dist/extensions/pi-subagents/docs/models.md +192 -0
- package/dist/extensions/pi-subagents/docs/observability.md +174 -0
- package/dist/extensions/pi-subagents/docs/tool-reference.md +343 -0
- package/dist/extensions/pi-subagents/docs/watchdog.md +176 -0
- package/dist/extensions/pi-subagents/docs/workflows.md +163 -0
- package/dist/extensions/pi-subagents/inspector-runner.mjs +11 -0
- package/dist/extensions/pi-subagents/package-lock.json +152 -194
- package/dist/extensions/pi-subagents/package.json +9 -10
- package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
- package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
- package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
- package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
- package/dist/extensions/pi-subagents/prompts/review-loop.md +12 -12
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +22 -17
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +77 -72
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +177 -69
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +28 -8
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +88 -66
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +3 -3
- package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +624 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +29 -16
- package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +7 -3
- package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
- package/dist/extensions/pi-subagents/src/agents/skills.ts +2 -9
- package/dist/extensions/pi-subagents/src/api/delegation.ts +1 -2
- package/dist/extensions/pi-subagents/src/api/project-panes.ts +30 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +56 -8
- package/dist/extensions/pi-subagents/src/extension/control-notices.ts +2 -2
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +51 -26
- package/dist/extensions/pi-subagents/src/extension/index.ts +119 -50
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +72 -0
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +15 -15
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +58 -21
- package/dist/extensions/pi-subagents/src/extension/subagent-guide.ts +39 -0
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +47 -25
- package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +3 -3
- package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +460 -64
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +102 -4
- package/dist/extensions/pi-subagents/src/missions/actions.ts +77 -16
- package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +162 -0
- package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +65 -14
- package/dist/extensions/pi-subagents/src/missions/store.ts +147 -5
- package/dist/extensions/pi-subagents/src/missions/types.ts +58 -3
- package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +256 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -8
- package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +42 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +41 -7
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +294 -134
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +19 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +39 -6
- package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +267 -0
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +145 -60
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +114 -6
- package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +33 -0
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +68 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +71 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +21 -2
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +42 -14
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +12 -24
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +7 -5
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +152 -68
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +17 -4
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +146 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +21 -5
- package/dist/extensions/pi-subagents/src/runs/foreground/async-dismiss-action.ts +85 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +43 -18
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +20 -19
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +3 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +55 -26
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +137 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +798 -112
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +187 -0
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +165 -22
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +9 -5
- package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +12 -2
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +1 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +465 -116
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +10 -10
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +189 -32
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +3 -3
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +128 -3
- package/dist/extensions/pi-subagents/src/shared/display-text.ts +100 -0
- package/dist/extensions/pi-subagents/src/shared/file-coalescer.ts +9 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +13 -0
- package/dist/extensions/pi-subagents/src/shared/formatters.ts +4 -6
- package/dist/extensions/pi-subagents/src/shared/prompt-resources.ts +51 -0
- package/dist/extensions/pi-subagents/src/shared/settings.ts +41 -11
- package/dist/extensions/pi-subagents/src/shared/types.ts +100 -3
- package/dist/extensions/pi-subagents/src/shared/utf8.ts +11 -0
- package/dist/extensions/pi-subagents/src/shared/utils.ts +43 -33
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -14
- package/dist/extensions/pi-subagents/src/slash/inline-subagents.ts +1 -1
- package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +14 -12
- package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +176 -21
- package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +12 -6
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +52 -18
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +36 -5
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +30 -16
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +151 -31
- package/dist/extensions/pi-subagents/src/tui/render.ts +201 -68
- package/dist/extensions/pi-subagents/src/types/pi-runtime-compat.d.ts +0 -6
- package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +5 -4
- package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +0 -1
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +313 -25
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +15 -15
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/cli.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/register-bedrock.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/restore-sandbox-env.d.ts +17 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/args.d.ts +57 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/config-selector.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/file-processor.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/initial-message.d.ts +18 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/list-models.d.ts +9 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/project-trust.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/session-picker.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/startup-ui.d.ts +20 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/config.d.ts +96 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-runtime.d.ts +119 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-services.d.ts +85 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session.d.ts +620 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-guidance.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-storage.d.ts +56 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/bash-executor.d.ts +32 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/cache-stats.d.ts +49 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/branch-summarization.d.ts +93 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/compaction.d.ts +128 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/index.d.ts +7 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/utils.d.ts +38 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/defaults.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/diagnostics.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/event-bus.d.ts +9 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/exec.d.ts +29 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/experimental.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/ansi-to-html.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/index.d.ts +37 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/tool-renderer.d.ts +34 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/index.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/loader.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/runner.d.ts +171 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +1261 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/wrapper.d.ts +20 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/footer-data-provider.d.ts +54 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/http-dispatcher.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/index.d.ts +13 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/keybindings.d.ts +358 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/messages.d.ts +77 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-config.d.ts +512 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-registry.d.ts +43 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-resolver.d.ts +121 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-runtime.d.ts +82 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/models-store.d.ts +17 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/output-guard.d.ts +7 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/package-manager.d.ts +210 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/project-trust.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/prompt-templates.d.ts +54 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-attribution.d.ts +4 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-composer.d.ts +55 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/radius.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/remote-catalog-provider.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resolve-config-value.d.ts +30 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resource-loader.d.ts +206 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/runtime-credentials.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/sdk.d.ts +106 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-cwd.d.ts +19 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-manager.d.ts +356 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/settings-manager.d.ts +297 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/skills.d.ts +60 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/slash-commands.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/source-info.d.ts +18 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/system-prompt.d.ts +28 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/telemetry.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/timings.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/bash.d.ts +68 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit-diff.d.ts +106 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit.d.ts +51 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/file-mutation-queue.d.ts +6 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/find.d.ts +35 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/grep.d.ts +37 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/index.d.ts +40 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/ls.d.ts +37 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/output-accumulator.d.ts +52 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/path-utils.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/read.d.ts +35 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/render-utils.d.ts +24 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/truncate.d.ts +70 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/write.d.ts +26 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/trust-manager.d.ts +36 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/usage-totals.d.ts +19 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/index.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/client.d.ts +61 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/huggingface.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/index.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/provider.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/ui.d.ts +42 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/index.d.ts +35 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/main.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/migrations.d.ts +33 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/index.d.ts +9 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/armin.d.ts +34 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/assistant-message.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bash-execution.d.ts +34 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/config-selector.d.ts +102 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-editor.d.ts +21 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-entry.d.ts +19 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-message.d.ts +20 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/daxnuts.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/diff.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-editor.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-input.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-selector.d.ts +26 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/footer.d.ts +32 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/index.d.ts +34 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/keybinding-hints.d.ts +13 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/login-dialog.d.ts +52 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/model-selector.d.ts +54 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/oauth-selector.d.ts +33 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/scoped-models-selector.d.ts +42 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector.d.ts +95 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/settings-selector.d.ts +77 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/status-indicator.d.ts +28 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/theme-selector.d.ts +11 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tool-execution.d.ts +63 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tree-selector.d.ts +94 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/trust-selector.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message.d.ts +14 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/interactive-mode.d.ts +394 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/model-search.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme-controller.d.ts +29 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme.d.ts +120 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/print-mode.d.ts +28 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/jsonl.d.ts +17 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-client.d.ts +246 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-mode.d.ts +20 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-types.d.ts +457 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/package-manager-cli.d.ts +8 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/rpc-entry.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/ansi.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/changelog.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/child-process.d.ts +18 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-image.d.ts +11 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-native.d.ts +11 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard.d.ts +4 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/deprecation.d.ts +4 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/exif-orientation.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/frontmatter.d.ts +8 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/fs-watch.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/git.d.ts +26 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/html.d.ts +7 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-convert.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-process.d.ts +18 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-core.d.ts +30 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-worker.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/json.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/mime.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/open-browser.d.ts +9 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/paths.d.ts +31 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/photon.d.ts +21 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/pi-user-agent.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/shell.d.ts +31 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/sleep.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/syntax-highlight.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/tools-manager.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/version-check.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/windows-self-update.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/index.mjs +84 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/package.json +14 -0
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +15 -15
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +411 -300
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +309 -123
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +99 -56
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +62 -16
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +159 -108
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +56 -56
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +114 -45
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +57 -19
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +106 -106
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +416 -106
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +287 -17
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +809 -87
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +107 -20
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +26 -6
- package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +1 -1
- package/dist/extensions/pi-subagents/test/support/ts-loader.mjs +0 -8
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +163 -58
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
- package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +334 -248
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +52 -52
- package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +26 -26
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +99 -99
- package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +160 -0
- package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +43 -1
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +36 -10
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +387 -30
- package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +94 -40
- package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +5 -3
- package/dist/extensions/pi-subagents/test/unit/builtin-agent-documentation.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +19 -19
- package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +19 -19
- package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/chain-serializer.test.ts +20 -20
- package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +35 -5
- package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +52 -52
- package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +187 -0
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +125 -71
- package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +18 -3
- package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +12 -12
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +17 -23
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +25 -25
- package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/file-coalescer.test.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +62 -44
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +329 -37
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +4 -4
- package/dist/extensions/pi-subagents/test/unit/foreground-tool-call-compaction.test.ts +37 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +69 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +79 -2
- package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +102 -49
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +98 -32
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +5 -5
- package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +116 -0
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +55 -7
- package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +289 -13
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +28 -1
- package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +11 -2
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +84 -10
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +122 -8
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +8 -8
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +24 -24
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +16 -2
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +10 -10
- package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +26 -17
- package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +405 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +357 -92
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +22 -6
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +6 -6
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +43 -43
- package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +13 -13
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +4 -4
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +19 -19
- package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +243 -0
- package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +68 -168
- package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +58 -15
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +40 -0
- package/dist/extensions/pi-subagents/test/unit/reads-resolution.test.ts +66 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +47 -28
- package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +12 -12
- package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +57 -0
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +107 -27
- package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +56 -50
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +69 -28
- package/dist/extensions/pi-subagents/test/unit/scheduled-store-root.test.ts +67 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +59 -44
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +424 -49
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +23 -1
- package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +32 -5
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +13 -13
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +120 -91
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +85 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +35 -35
- package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +24 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +312 -22
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +150 -15
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +26 -26
- package/dist/extensions/pi-subagents/test/unit/timeout-defaults.test.ts +73 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +76 -25
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +5 -5
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +55 -6
- package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +13 -13
- package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +8 -8
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +6 -6
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +138 -11
- package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +8 -8
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +113 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +6 -6
- package/dist/extensions/workflow/extension.ts +21 -19
- package/dist/extensions/workflow/modes.ts +65 -0
- package/dist/index.d.ts +1 -1
- package/dist/main.js +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +6 -2
- package/dist/modes/interactive/components/settings-selector.js +20 -0
- package/dist/modes/interactive/interactive-mode.d.ts +28 -0
- package/dist/modes/interactive/interactive-mode.js +214 -28
- package/dist/modes/interactive/theme/dark.json +1 -0
- package/dist/modes/interactive/theme/light.json +1 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +3 -0
- package/dist/modes/interactive/theme/theme-controller.js +10 -1
- package/dist/modes/interactive/theme/theme.d.ts +2 -2
- package/dist/modes/interactive/theme/theme.js +24 -6
- package/dist/skills/pi-subagents/SKILL.md +20 -15
- package/dist/skills/pi-subagents/references/constraints-and-recipes.md +77 -83
- package/dist/skills/pi-subagents/references/execution-controls.md +215 -106
- package/dist/skills/pi-subagents/references/management-authoring-rpc.md +29 -9
- package/dist/skills/pi-subagents/references/prompting-and-roles.md +58 -43
- package/docs/keybindings.md +35 -7
- package/docs/plans/workflow-autoloop-reference.md +192 -0
- package/docs/plans/workflow-subagent-consolidation-planb.md +374 -0
- package/docs/plans/workflow-subagent-scripted-merge.md +406 -0
- package/docs/settings.md +16 -14
- package/docs/usage.md +5 -0
- package/package.json +5 -5
- package/dist/extensions/pi-powerline-footer/fixed-editor/cluster.ts +0 -113
- package/dist/extensions/pi-powerline-footer/fixed-editor/terminal-split.ts +0 -1080
- package/dist/extensions/pi-powerline-footer/tests/fixed-editor.test.ts +0 -1452
- package/dist/extensions/workflow/adapter.ts +0 -949
- package/dist/extensions/workflow/modes/loop.ts +0 -74
- package/dist/extensions/workflow/modes/prototype.ts +0 -170
- package/dist/extensions/workflow/modes/quicktype.ts +0 -150
- package/dist/extensions/workflow/modes/task.ts +0 -105
- package/dist/extensions/workflow/run-state.ts +0 -124
- package/dist/extensions/workflow/state-machine.ts +0 -484
- package/dist/extensions/workflow/validators.ts +0 -42
|
@@ -4,7 +4,7 @@ import * as path from "node:path";
|
|
|
4
4
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
5
5
|
import type { ExtensionAPI, ExtensionContext } from "@selesai/code";
|
|
6
6
|
import { resolveAgentName, type AgentConfig, type AgentScope } from "../../agents/agents.ts";
|
|
7
|
-
import { getArtifactsDir, getChainRunsDir } from "../../shared/artifacts.ts";
|
|
7
|
+
import { getArtifactsDir, getChainRunsDir, getProjectArtifactPackagingWarning, getProjectSubagentsDir } from "../../shared/artifacts.ts";
|
|
8
8
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
9
9
|
import { ChainClarifyComponent, type ChainClarifyResult } from "./chain-clarify.ts";
|
|
10
10
|
import { resolveEffectiveThinking, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
@@ -17,13 +17,18 @@ import {
|
|
|
17
17
|
settleForegroundSchedulingOwner,
|
|
18
18
|
updateForegroundChild,
|
|
19
19
|
} from "./foreground-control.ts";
|
|
20
|
+
import { persistForegroundRunHistory, MAX_REMEMBERED_FOREGROUND_RUNS } from "./foreground-history.ts";
|
|
20
21
|
import { resolveExecutionAgentScope } from "../../agents/agent-scope.ts";
|
|
21
22
|
import { handleManagementAction } from "../../agents/agent-management.ts";
|
|
23
|
+
import { handleRefinementAction } from "../../agents/agent-refinements.ts";
|
|
22
24
|
import { buildDoctorReport } from "../../extension/doctor.ts";
|
|
25
|
+
import { readSubagentGuide } from "../../extension/subagent-guide.ts";
|
|
26
|
+
import { normalizePublicSubagentExecution } from "../../extension/public-execution.ts";
|
|
23
27
|
import { runSync } from "./execution.ts";
|
|
24
28
|
import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
|
|
25
29
|
import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
|
|
26
30
|
import { buildModelCandidates, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelCandidate, type ParentModel } from "../shared/model-fallback.ts";
|
|
31
|
+
import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.ts";
|
|
27
32
|
import type { ModelScopeConfig } from "../shared/model-scope.ts";
|
|
28
33
|
import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
|
|
29
34
|
import { recordRun } from "../shared/run-history.ts";
|
|
@@ -33,6 +38,8 @@ import {
|
|
|
33
38
|
getStepAgents,
|
|
34
39
|
isParallelStep,
|
|
35
40
|
isDynamicParallelStep,
|
|
41
|
+
resolveChainPath,
|
|
42
|
+
resolveExistingReadPaths,
|
|
36
43
|
resolveStepBehavior,
|
|
37
44
|
suppressProgressForReadOnlyTask,
|
|
38
45
|
taskDisallowsFileUpdates,
|
|
@@ -45,11 +52,12 @@ import {
|
|
|
45
52
|
type StepOverrides,
|
|
46
53
|
} from "../../shared/settings.ts";
|
|
47
54
|
import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.ts";
|
|
48
|
-
import { buildAsyncRunnerSteps, executeAsyncChain, executeAsyncSingle, formatAsyncStartedMessage, isAsyncAvailable } from "../background/async-execution.ts";
|
|
49
|
-
import
|
|
55
|
+
import { buildAsyncRunnerSteps, DEFAULT_ASYNC_TIMEOUT_MS, executeAsyncChain, executeAsyncSingle, formatAsyncStartedMessage, isAsyncAvailable, workflowAwaitedAsyncResultPath } from "../background/async-execution.ts";
|
|
56
|
+
import { updateActiveRunIndex } from "../background/active-run-index.ts";
|
|
57
|
+
import { isScheduledRunAction, type ScheduledRunAction } from "../background/scheduled-runs.ts";
|
|
50
58
|
import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
|
|
51
59
|
import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
|
|
52
|
-
import { validateExecutionAcceptance } from "../shared/acceptance.ts";
|
|
60
|
+
import { normalizeGateAcceptance, validateExecutionAcceptance } from "../shared/acceptance.ts";
|
|
53
61
|
import { createForkContextResolver, forkedChildRequiresThinkingOff } from "../../shared/fork-context.ts";
|
|
54
62
|
import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
|
|
55
63
|
import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBridge, resolveIntercomSessionTarget, resolveSubagentIntercomTarget, type IntercomBridgeState } from "../../intercom/intercom-bridge.ts";
|
|
@@ -75,23 +83,35 @@ import {
|
|
|
75
83
|
stripDetailsOutputsForIntercomReceipt,
|
|
76
84
|
} from "../../intercom/result-intercom.ts";
|
|
77
85
|
import { applySteeringRecoveryAgentConfig, buildRevivedAsyncTask, resolveAsyncResumeTarget, resolveAsyncRunLocation } from "../background/async-resume.ts";
|
|
78
|
-
import { deliverCheckpointDecisionRequest, deliverInterruptRequest, requestAsyncSteer } from "../background/control-channel.ts";
|
|
79
|
-
import { waitForSteeringAction } from "../background/steering.ts";
|
|
86
|
+
import { deliverCheckpointDecisionRequest, deliverInterruptRequest, readRevivalBriefs, requestAsyncSteer, type SteerDeliveryMode } from "../background/control-channel.ts";
|
|
87
|
+
import { updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
|
|
80
88
|
import { steerAsyncRun } from "./async-steering-action.ts";
|
|
89
|
+
import {
|
|
90
|
+
removeWorkflowForegroundSteeringRoute,
|
|
91
|
+
resolveWorkflowForegroundSteeringTarget,
|
|
92
|
+
steerWorkflowForegroundTarget,
|
|
93
|
+
workflowForegroundSteeringDir,
|
|
94
|
+
workflowForegroundSteeringLaunchOptions,
|
|
95
|
+
} from "./workflow-foreground-steering.ts";
|
|
81
96
|
import { stopAsyncRun } from "./async-stop-action.ts";
|
|
97
|
+
import { dismissRecoveredWorkflow } from "./async-dismiss-action.ts";
|
|
82
98
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
83
|
-
import { resolveAsyncRootResultPath } from "../background/chain-root-attachment.ts";
|
|
99
|
+
import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.ts";
|
|
84
100
|
import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedRunResolutionScope } from "../shared/nested-events.ts";
|
|
85
101
|
import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
|
|
86
102
|
import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
|
|
87
103
|
import { inspectSubagentStatus } from "../background/run-status.ts";
|
|
88
104
|
import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.ts";
|
|
89
105
|
import { handleMissionAction, MISSION_ACTIONS } from "../../missions/actions.ts";
|
|
90
|
-
import { attachMissionToLaunchResult, prepareMissionLaunch, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
|
|
106
|
+
import { attachMissionToLaunchResult, prepareMissionLaunch, writeMissionAsyncBinding, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
|
|
107
|
+
import { updateMission } from "../../missions/store.ts";
|
|
108
|
+
import type { MissionWorkflowChildUpdate } from "../../missions/types.ts";
|
|
109
|
+
import { createMissionWorkflowState } from "../../missions/workflow-state.ts";
|
|
91
110
|
import { resolveAuthorityDecision } from "../../policy/authority.ts";
|
|
92
111
|
import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspectors/herdr/actions.ts";
|
|
93
112
|
import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
|
|
94
|
-
import { runWorkflowScript, WorkflowScriptError, type WorkflowScriptChildResult } from "../../workflows/scripted-workflow.ts";
|
|
113
|
+
import { previewSimpleWorkflowRun, runWorkflowScript, WorkflowScriptError, type WorkflowScriptChildResult } from "../../workflows/scripted-workflow.ts";
|
|
114
|
+
import { renderWorkflowPrompt } from "../../shared/prompt-resources.ts";
|
|
95
115
|
import { resolveWorkflowChatProgress, type WorkflowChatProgressProjection } from "../../workflows/chat-progress.ts";
|
|
96
116
|
import {
|
|
97
117
|
cleanupWorktrees,
|
|
@@ -144,7 +164,48 @@ import {
|
|
|
144
164
|
wrapForkTask,
|
|
145
165
|
} from "../../shared/types.ts";
|
|
146
166
|
|
|
147
|
-
const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure", "mission.create", "mission.update", "mission.attach-run", "mission.close", "inspector.open", "inspector.close", "project.open", "project.close", "worktree.discard", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
|
|
167
|
+
const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure", "mission.create", "mission.update", "mission.resolve-decision", "mission.attach-run", "mission.close", "inspector.open", "inspector.close", "project.open", "project.close", "worktree.discard", "refine", "refine.rollback", "dismiss", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
|
|
168
|
+
const DESTRUCTIVE_MANAGEMENT_ACTIONS = new Set(["delete", "eject", "disable", "reset", "mission.close", "worktree.discard", "refine.rollback", "inspector.close", "project.close", "stop", "interrupt", "reject-checkpoint", "schedule.delete"]);
|
|
169
|
+
|
|
170
|
+
function editDistance(left: string, right: string): number {
|
|
171
|
+
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
172
|
+
for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
|
|
173
|
+
let diagonal = previous[0]!;
|
|
174
|
+
previous[0] = leftIndex;
|
|
175
|
+
for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) {
|
|
176
|
+
const above = previous[rightIndex]!;
|
|
177
|
+
previous[rightIndex] = left[leftIndex - 1] === right[rightIndex - 1]
|
|
178
|
+
? diagonal
|
|
179
|
+
: Math.min(diagonal, above, previous[rightIndex - 1]!) + 1;
|
|
180
|
+
diagonal = above;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return previous[right.length]!;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function hasSingleAdjacentTransposition(left: string, right: string): boolean {
|
|
187
|
+
if (left.length !== right.length) return false;
|
|
188
|
+
const mismatch = [...left].findIndex((character, index) => character !== right[index]);
|
|
189
|
+
return mismatch >= 0
|
|
190
|
+
&& left[mismatch] === right[mismatch + 1]
|
|
191
|
+
&& left[mismatch + 1] === right[mismatch]
|
|
192
|
+
&& left.slice(mismatch + 2) === right.slice(mismatch + 2);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function unknownSubagentActionMessage(action: string): string {
|
|
196
|
+
const requested = action.toLowerCase();
|
|
197
|
+
const suggestion = SUBAGENT_ACTIONS.find((candidate) => {
|
|
198
|
+
const distance = editDistance(requested, candidate);
|
|
199
|
+
const closeMatch = distance <= Math.max(1, Math.floor(candidate.length / 4)) || hasSingleAdjacentTransposition(requested, candidate);
|
|
200
|
+
if (DESTRUCTIVE_MANAGEMENT_ACTIONS.has(candidate)) return distance === 1 && requested.length >= candidate.length - 1;
|
|
201
|
+
return closeMatch;
|
|
202
|
+
});
|
|
203
|
+
const nextStep = 'Use subagent({ action: "status" }) to inspect runs or subagent({ action: "list" }) to inspect agents.';
|
|
204
|
+
const validActions = `Valid: ${SUBAGENT_ACTIONS.join(", ")}.`;
|
|
205
|
+
return suggestion
|
|
206
|
+
? `Unknown action: ${action}. Did you mean ${suggestion}? ${nextStep} ${validActions}`
|
|
207
|
+
: `Unknown action: ${action}. ${nextStep} ${validActions}`;
|
|
208
|
+
}
|
|
148
209
|
|
|
149
210
|
type UndefinedOmitted<T extends object> = {
|
|
150
211
|
[K in keyof T as undefined extends T[K] ? never : K]: T[K];
|
|
@@ -203,20 +264,25 @@ export interface SubagentParamsLike {
|
|
|
203
264
|
index?: number;
|
|
204
265
|
view?: "fleet" | "transcript";
|
|
205
266
|
lines?: number;
|
|
267
|
+
topic?: string;
|
|
206
268
|
chainName?: string;
|
|
207
269
|
config?: unknown;
|
|
208
270
|
name?: string;
|
|
209
271
|
type?: string;
|
|
210
272
|
agent?: string;
|
|
211
273
|
task?: string;
|
|
274
|
+
/** Retained async child run id. Valid only on workflow runs.run items. */
|
|
275
|
+
resume?: string;
|
|
212
276
|
message?: string;
|
|
213
277
|
steeringRecovery?: boolean;
|
|
278
|
+
mode?: SteerDeliveryMode;
|
|
214
279
|
workflowScript?: string;
|
|
215
280
|
chatProgress?: "auto" | "off" | "terminal" | "milestones" | "live-card";
|
|
216
281
|
step?: ChainStep;
|
|
217
282
|
/** Internal workflow ownership metadata; not part of the public schema. */
|
|
218
283
|
workflowParentRunId?: string;
|
|
219
284
|
workflowKey?: string;
|
|
285
|
+
workflowChildAsyncId?: string;
|
|
220
286
|
suppressRoutineResultIntercom?: boolean;
|
|
221
287
|
/** Internal durable-run compatibility fields. Public callers must use workflowScript. */
|
|
222
288
|
chain?: ChainStep[];
|
|
@@ -248,11 +314,14 @@ export interface SubagentParamsLike {
|
|
|
248
314
|
focus?: boolean;
|
|
249
315
|
skill?: string | string[] | boolean;
|
|
250
316
|
output?: string | boolean;
|
|
317
|
+
/** Internal-only; not part of the public tool schema. Wired for single-run reads (chain steps use their own field). */
|
|
318
|
+
reads?: string[] | false;
|
|
251
319
|
outputMode?: "inline" | "file-only";
|
|
252
320
|
outputSchema?: JsonSchemaObject;
|
|
253
321
|
agentScope?: unknown;
|
|
254
322
|
chainDir?: string;
|
|
255
323
|
acceptance?: AcceptanceInput;
|
|
324
|
+
gate?: string;
|
|
256
325
|
agentContract?: AgentContract;
|
|
257
326
|
schedule?: string;
|
|
258
327
|
scheduleName?: string;
|
|
@@ -344,6 +413,7 @@ function resolveRequestedCwd(runtimeCwd: string, requestedCwd: string | undefine
|
|
|
344
413
|
function removeForegroundControlIfIdle(state: SubagentState, runId: string): boolean {
|
|
345
414
|
const control = state.foregroundControls.get(runId);
|
|
346
415
|
if (control && (!foregroundSchedulingSettled(control) || (control.activeChildren?.size ?? 0) > 0)) return false;
|
|
416
|
+
if (control) removeWorkflowForegroundSteeringRoute(control);
|
|
347
417
|
state.foregroundControls.delete(runId);
|
|
348
418
|
if (state.lastForegroundControlId === runId) state.lastForegroundControlId = null;
|
|
349
419
|
return true;
|
|
@@ -461,7 +531,7 @@ function foregroundStatusResult(control: SubagentState["foregroundControls"] ext
|
|
|
461
531
|
|
|
462
532
|
function trimRememberedForegroundRuns(state: SubagentState): void {
|
|
463
533
|
if (!state.foregroundRuns) return;
|
|
464
|
-
while (state.foregroundRuns.size >
|
|
534
|
+
while (state.foregroundRuns.size > MAX_REMEMBERED_FOREGROUND_RUNS) {
|
|
465
535
|
const oldestTerminal = [...state.foregroundRuns.values()]
|
|
466
536
|
.filter((run) => !run.children.some((child) => child.status === "detached"))
|
|
467
537
|
.sort((left, right) => left.updatedAt - right.updatedAt)[0];
|
|
@@ -470,6 +540,14 @@ function trimRememberedForegroundRuns(state: SubagentState): void {
|
|
|
470
540
|
}
|
|
471
541
|
}
|
|
472
542
|
|
|
543
|
+
function persistRememberedForegroundRuns(state: SubagentState): void {
|
|
544
|
+
try {
|
|
545
|
+
persistForegroundRunHistory(state);
|
|
546
|
+
} catch (error) {
|
|
547
|
+
console.error("Failed to persist foreground run history:", error);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
473
551
|
function foregroundChildActivityFromProgress(progress: SingleResult["progress"] | undefined) {
|
|
474
552
|
return {
|
|
475
553
|
...(progress?.activityState ? { activityState: progress.activityState } : {}),
|
|
@@ -536,6 +614,7 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
536
614
|
}),
|
|
537
615
|
});
|
|
538
616
|
trimRememberedForegroundRuns(state);
|
|
617
|
+
persistRememberedForegroundRuns(state);
|
|
539
618
|
}
|
|
540
619
|
|
|
541
620
|
function applyControlEventToRememberedForegroundRun(state: SubagentState, event: ControlEvent): void {
|
|
@@ -611,6 +690,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
611
690
|
...(input.result.capabilityAudit ? { capabilityAudit: input.result.capabilityAudit } : {}),
|
|
612
691
|
});
|
|
613
692
|
trimRememberedForegroundRuns(state);
|
|
693
|
+
persistRememberedForegroundRuns(state);
|
|
614
694
|
const output = getSingleResultOutput(input.result).trim();
|
|
615
695
|
const success = terminalStatus === "completed";
|
|
616
696
|
const summary = !success && input.result.error
|
|
@@ -790,6 +870,20 @@ function getAsyncInterruptTarget(
|
|
|
790
870
|
return newest ? { asyncId: newest.asyncId, asyncDir: newest.asyncDir } : undefined;
|
|
791
871
|
}
|
|
792
872
|
|
|
873
|
+
function isStaleExtensionContextError(error: unknown): boolean {
|
|
874
|
+
if (!(error instanceof Error)) return false;
|
|
875
|
+
return /extension ctx is stale|stale after session replacement or reload/i.test(error.message);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
function emitAdvisoryControlEvent(pi: ExtensionAPI, channel: string, payload: unknown): void {
|
|
879
|
+
try {
|
|
880
|
+
pi.events.emit(channel, payload);
|
|
881
|
+
} catch (error) {
|
|
882
|
+
if (isStaleExtensionContextError(error)) return;
|
|
883
|
+
throw error;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
793
887
|
function emitControlNotification(input: {
|
|
794
888
|
pi: ExtensionAPI;
|
|
795
889
|
controlConfig: ResolvedControlConfig;
|
|
@@ -807,10 +901,10 @@ function emitControlNotification(input: {
|
|
|
807
901
|
noticeText: formatControlNoticeMessage(input.event, childIntercomTarget),
|
|
808
902
|
};
|
|
809
903
|
if (input.controlConfig.notifyChannels.includes("event")) {
|
|
810
|
-
input.pi
|
|
904
|
+
emitAdvisoryControlEvent(input.pi, SUBAGENT_CONTROL_EVENT, payload);
|
|
811
905
|
}
|
|
812
906
|
if (input.event.type !== "active_long_running" && input.controlConfig.notifyChannels.includes("intercom") && input.intercomBridge.active && input.intercomBridge.orchestratorTarget) {
|
|
813
|
-
input.pi
|
|
907
|
+
emitAdvisoryControlEvent(input.pi, SUBAGENT_CONTROL_INTERCOM_EVENT, {
|
|
814
908
|
...payload,
|
|
815
909
|
to: input.intercomBridge.orchestratorTarget,
|
|
816
910
|
message: formatControlIntercomMessage(input.event, childIntercomTarget),
|
|
@@ -842,8 +936,15 @@ function interruptAsyncRun(
|
|
|
842
936
|
details: { mode: "management", results: [] },
|
|
843
937
|
};
|
|
844
938
|
}
|
|
939
|
+
if (status.mode === "workflow") {
|
|
940
|
+
return {
|
|
941
|
+
content: [{ type: "text", text: `Interrupt is unsupported for async workflow ${target.asyncId}; use stop instead.` }],
|
|
942
|
+
isError: true,
|
|
943
|
+
details: { mode: "management", results: [] },
|
|
944
|
+
};
|
|
945
|
+
}
|
|
845
946
|
try {
|
|
846
|
-
deliverInterruptRequest(
|
|
947
|
+
deliverInterruptRequest({ asyncDir: target.asyncDir, source: "interrupt-action" });
|
|
847
948
|
const tracked = state.asyncJobs.get(target.asyncId);
|
|
848
949
|
if (tracked) {
|
|
849
950
|
delete tracked.activityState;
|
|
@@ -1152,7 +1253,7 @@ function directNestedAsyncInterrupt(target: ResolvedSubagentRunId & { kind: "nes
|
|
|
1152
1253
|
const pid = typeof status?.pid === "number" && status.pid > 0 ? status.pid : run.pid;
|
|
1153
1254
|
if (!status || status.state !== "running" || typeof pid !== "number" || pid <= 0) return undefined;
|
|
1154
1255
|
try {
|
|
1155
|
-
deliverInterruptRequest({ asyncDir,
|
|
1256
|
+
deliverInterruptRequest({ asyncDir, source: "nested-interrupt" });
|
|
1156
1257
|
return { content: [{ type: "text", text: `Interrupt requested for nested async run ${run.id}.` }], details: { mode: "management", results: [] } };
|
|
1157
1258
|
} catch (error) {
|
|
1158
1259
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1160,7 +1261,7 @@ function directNestedAsyncInterrupt(target: ResolvedSubagentRunId & { kind: "nes
|
|
|
1160
1261
|
}
|
|
1161
1262
|
}
|
|
1162
1263
|
|
|
1163
|
-
async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details> | undefined> {
|
|
1264
|
+
async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; mode?: SteerDeliveryMode; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details> | undefined> {
|
|
1164
1265
|
const run = input.target.match.run;
|
|
1165
1266
|
const asyncDir = resolveNestedAsyncDir(input.target.match.rootRunId, run);
|
|
1166
1267
|
if (!asyncDir) return undefined;
|
|
@@ -1182,6 +1283,7 @@ async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & {
|
|
|
1182
1283
|
try {
|
|
1183
1284
|
requestAsyncSteer(asyncDir, {
|
|
1184
1285
|
message: input.message,
|
|
1286
|
+
mode: input.mode,
|
|
1185
1287
|
...(effectiveTargetIndex !== undefined ? { targetIndex: effectiveTargetIndex } : { targetIndexes }),
|
|
1186
1288
|
source: "nested-steer",
|
|
1187
1289
|
id: requestId,
|
|
@@ -1191,12 +1293,12 @@ async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & {
|
|
|
1191
1293
|
}
|
|
1192
1294
|
const targets = targetIndexes.map((index) => ({ index, state: steps[index]?.status === "pending" ? "scheduled" as const : "pending" as const }));
|
|
1193
1295
|
if (targets.every((target) => target.state === "scheduled")) {
|
|
1194
|
-
const scheduled = { requestId, state: "scheduled" as const, sourceRunId: run.id, targets };
|
|
1296
|
+
const scheduled = { requestId, state: "scheduled" as const, deliveryStatus: "queued" as const, sourceRunId: run.id, targets };
|
|
1195
1297
|
return { content: [{ type: "text", text: `Steering scheduled for nested async run ${run.id} (request ${requestId}).` }], details: { mode: "management", results: [], steering: scheduled } };
|
|
1196
1298
|
}
|
|
1197
1299
|
const waited = await waitForSteeringAction(omitUndefinedProperties({ asyncDir, sourceRunId: run.id, requestId, timeoutMs: 3_000, signal: input.signal }));
|
|
1198
|
-
const result = waited ?? { requestId, state: "pending" as const, sourceRunId: run.id, targets };
|
|
1199
|
-
const stateText = result.state === "
|
|
1300
|
+
const result = waited ?? { requestId, state: "pending" as const, deliveryStatus: "queued" as const, sourceRunId: run.id, targets };
|
|
1301
|
+
const stateText = result.state === "failed" ? "failed" : result.state === "partial" ? "partial" : result.deliveryStatus === "queued" ? "queued" : result.state === "delivered" ? "delivered" : "pending";
|
|
1200
1302
|
return { content: [{ type: "text", text: `Steering ${stateText} for nested async run ${run.id} (request ${requestId}).` }], ...(result.state === "failed" || result.state === "partial" ? { isError: true } : {}), details: { mode: "management", results: [], steering: result } };
|
|
1201
1303
|
}
|
|
1202
1304
|
|
|
@@ -1219,7 +1321,7 @@ async function resumeLiveNestedRun(input: { target: ResolvedSubagentRunId & { ki
|
|
|
1219
1321
|
return { content: [{ type: "text", text: `Nested run ${run.id} appears live but its owner route is not reachable. Wait for completion, then retry action='resume'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1220
1322
|
}
|
|
1221
1323
|
|
|
1222
|
-
async function steerNestedRun(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details>> {
|
|
1324
|
+
async function steerNestedRun(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; mode?: SteerDeliveryMode; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details>> {
|
|
1223
1325
|
const run = input.target.match.run;
|
|
1224
1326
|
if (run.state !== "running" && run.state !== "queued") return { content: [{ type: "text", text: `Nested run ${run.id} is ${run.state} and cannot be steered.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1225
1327
|
const direct = await directNestedAsyncSteer(input);
|
|
@@ -1243,6 +1345,7 @@ async function resumeAsyncRun(input: {
|
|
|
1243
1345
|
deps: ExecutorDeps;
|
|
1244
1346
|
parentModel?: ParentModel;
|
|
1245
1347
|
absoluteDeadlineAt?: number;
|
|
1348
|
+
signal?: AbortSignal;
|
|
1246
1349
|
}): Promise<AgentToolResult<Details>> {
|
|
1247
1350
|
const followUp = (input.params.message ?? input.params.task ?? "").trim();
|
|
1248
1351
|
const attachChain = (input.params.chain?.length ?? 0) > 0 ? input.params.chain as ChainStep[] : undefined;
|
|
@@ -1358,7 +1461,7 @@ async function resumeAsyncRun(input: {
|
|
|
1358
1461
|
inheritProjectContext: recoveryDescriptor.inheritProjectContext,
|
|
1359
1462
|
inheritSkills: recoveryDescriptor.inheritSkills,
|
|
1360
1463
|
source: "project",
|
|
1361
|
-
filePath: recoveryDescriptor.agentFilePath ?? path.join(recoveryDescriptor.cwd, "
|
|
1464
|
+
filePath: recoveryDescriptor.agentFilePath ?? path.join(getProjectSubagentsDir(recoveryDescriptor.cwd), "recovery-agent"),
|
|
1362
1465
|
} : undefined);
|
|
1363
1466
|
if (!agentConfig) {
|
|
1364
1467
|
return {
|
|
@@ -1449,6 +1552,9 @@ async function resumeAsyncRun(input: {
|
|
|
1449
1552
|
return { content: [{ type: "text", text: formatAsyncStartedMessage(lines.join("\n"), input.ctx.hasUI) }], details: result.details };
|
|
1450
1553
|
}
|
|
1451
1554
|
|
|
1555
|
+
const sourceAsyncDir = target.source === "async" ? target.asyncDir : undefined;
|
|
1556
|
+
const queuedBriefs = sourceAsyncDir ? readRevivalBriefs(sourceAsyncDir) : [];
|
|
1557
|
+
const effectiveFollowUp = [...queuedBriefs.map(({ request }) => request.message), followUp].filter(Boolean).join("\n\n");
|
|
1452
1558
|
const revivalSessionFile = target.sessionFile;
|
|
1453
1559
|
if (!revivalSessionFile) {
|
|
1454
1560
|
return { content: [{ type: "text", text: `Async child '${target.runId}' has no persisted session file to resume.` }], isError: true, details: { mode: "management", results: [] } };
|
|
@@ -1461,8 +1567,8 @@ async function resumeAsyncRun(input: {
|
|
|
1461
1567
|
const parentModel = input.parentModel;
|
|
1462
1568
|
const result = executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
|
|
1463
1569
|
agent: target.agent,
|
|
1464
|
-
task: buildRevivedAsyncTask(target as Parameters<typeof buildRevivedAsyncTask>[0],
|
|
1465
|
-
goal:
|
|
1570
|
+
task: buildRevivedAsyncTask(target as Parameters<typeof buildRevivedAsyncTask>[0], effectiveFollowUp),
|
|
1571
|
+
goal: effectiveFollowUp,
|
|
1466
1572
|
agentConfig: recoveryAgentConfig,
|
|
1467
1573
|
ctx: compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
|
|
1468
1574
|
pi: input.deps.pi,
|
|
@@ -1512,10 +1618,72 @@ async function resumeAsyncRun(input: {
|
|
|
1512
1618
|
...(input.params.turnBudget !== undefined ? { turnBudget: input.params.turnBudget } : {}),
|
|
1513
1619
|
...(input.params.toolBudget !== undefined ? { toolBudget: input.params.toolBudget } : {}),
|
|
1514
1620
|
capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, recoveryDescriptor?.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
|
|
1621
|
+
parentWorkflowRunId: input.params.workflowParentRunId,
|
|
1622
|
+
workflowKey: input.params.workflowKey,
|
|
1515
1623
|
}));
|
|
1516
1624
|
if (result.isError) return result;
|
|
1625
|
+
for (const brief of queuedBriefs) fs.rmSync(brief.path, { force: true });
|
|
1626
|
+
if (queuedBriefs.length > 0 && sourceAsyncDir) {
|
|
1627
|
+
const sourceStatus = readStatus(sourceAsyncDir);
|
|
1628
|
+
if (sourceStatus?.steering) {
|
|
1629
|
+
for (const brief of queuedBriefs) updateSteeringTarget(sourceStatus.steering, brief.request.id, target.index, "delivered", Date.now());
|
|
1630
|
+
writeAtomicJson(path.join(sourceAsyncDir, "status.json"), sourceStatus);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1517
1633
|
|
|
1518
1634
|
const revivedId = result.details.asyncId ?? runId;
|
|
1635
|
+
if (input.params.workflowParentRunId !== undefined && result.details.asyncDir) {
|
|
1636
|
+
const asyncDir = result.details.asyncDir;
|
|
1637
|
+
const resultPath = workflowAwaitedAsyncResultPath(asyncDir);
|
|
1638
|
+
const stopOnAbort = () => { stopAsyncRun(input.deps.state, revivedId, input.deps.kill, { asyncDir, resolvedId: revivedId }); };
|
|
1639
|
+
if (input.signal?.aborted) stopOnAbort();
|
|
1640
|
+
else input.signal?.addEventListener("abort", stopOnAbort, { once: true });
|
|
1641
|
+
let completed: Awaited<ReturnType<typeof waitForImportedAsyncRoot>>;
|
|
1642
|
+
try {
|
|
1643
|
+
completed = await waitForImportedAsyncRoot({ runId: revivedId, asyncDir, resultPath, index: 0 });
|
|
1644
|
+
} finally {
|
|
1645
|
+
input.signal?.removeEventListener("abort", stopOnAbort);
|
|
1646
|
+
}
|
|
1647
|
+
fs.rmSync(resultPath, { force: true });
|
|
1648
|
+
const totalCost = completed.totalCost;
|
|
1649
|
+
const childResult: SingleResult = {
|
|
1650
|
+
index: 0,
|
|
1651
|
+
agent: completed.agent,
|
|
1652
|
+
task: effectiveFollowUp,
|
|
1653
|
+
exitCode: completed.exitCode,
|
|
1654
|
+
usage: {
|
|
1655
|
+
input: totalCost?.inputTokens ?? 0,
|
|
1656
|
+
output: totalCost?.outputTokens ?? 0,
|
|
1657
|
+
cacheRead: 0,
|
|
1658
|
+
cacheWrite: 0,
|
|
1659
|
+
cost: totalCost?.costUsd ?? 0,
|
|
1660
|
+
turns: 0,
|
|
1661
|
+
},
|
|
1662
|
+
finalOutput: completed.output,
|
|
1663
|
+
outputState: completed.output.trim() ? "present" : "absent",
|
|
1664
|
+
...(completed.error ? { error: completed.error } : {}),
|
|
1665
|
+
...(completed.timedOut ? { timedOut: true } : {}),
|
|
1666
|
+
...(completed.stopped ? { stopped: true } : {}),
|
|
1667
|
+
...(completed.sessionFile ? { sessionFile: completed.sessionFile } : {}),
|
|
1668
|
+
...(completed.model ? { model: completed.model } : {}),
|
|
1669
|
+
...(completed.attemptedModels ? { attemptedModels: completed.attemptedModels } : {}),
|
|
1670
|
+
...(completed.modelAttempts ? { modelAttempts: completed.modelAttempts } : {}),
|
|
1671
|
+
...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
|
|
1672
|
+
...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
|
|
1673
|
+
...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
|
|
1674
|
+
...(completed.acceptance ? { acceptance: completed.acceptance } : {}),
|
|
1675
|
+
};
|
|
1676
|
+
return {
|
|
1677
|
+
content: [{ type: "text", text: completed.output || completed.error || `Revived ${target.source} subagent ${revivedId} completed without output.` }],
|
|
1678
|
+
...(completed.success ? {} : { isError: true }),
|
|
1679
|
+
details: {
|
|
1680
|
+
...result.details,
|
|
1681
|
+
runId: revivedId,
|
|
1682
|
+
results: [childResult],
|
|
1683
|
+
...(target.launchContractDigest ? { sourceLaunchContractDigest: target.launchContractDigest } : {}),
|
|
1684
|
+
},
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1519
1687
|
const revivedTarget = intercomBridge.active ? resolveSubagentIntercomTarget(revivedId, target.agent, 0) : undefined;
|
|
1520
1688
|
const sourceLabel = target.source;
|
|
1521
1689
|
const lines = [
|
|
@@ -1631,6 +1799,7 @@ async function maybeBuildForegroundIntercomReceipt(input: {
|
|
|
1631
1799
|
mode: SubagentRunMode;
|
|
1632
1800
|
details: Details;
|
|
1633
1801
|
nestedChildren?: NestedRunSummary[];
|
|
1802
|
+
preserveDetailsOutputs?: boolean;
|
|
1634
1803
|
}): Promise<{ text: string; details: Details } | null> {
|
|
1635
1804
|
const payload = await emitForegroundResultIntercom({
|
|
1636
1805
|
pi: input.pi,
|
|
@@ -1645,7 +1814,7 @@ async function maybeBuildForegroundIntercomReceipt(input: {
|
|
|
1645
1814
|
if (!payload) return null;
|
|
1646
1815
|
return {
|
|
1647
1816
|
text: formatSubagentResultReceipt({ mode: input.mode, runId: input.runId, payload }),
|
|
1648
|
-
details: stripDetailsOutputsForIntercomReceipt(input.details),
|
|
1817
|
+
details: input.preserveDetailsOutputs ? input.details : stripDetailsOutputsForIntercomReceipt(input.details),
|
|
1649
1818
|
};
|
|
1650
1819
|
}
|
|
1651
1820
|
|
|
@@ -1842,6 +2011,19 @@ function getRequestedModeLabel(params: SubagentParamsLike): Details["mode"] {
|
|
|
1842
2011
|
return "single";
|
|
1843
2012
|
}
|
|
1844
2013
|
|
|
2014
|
+
function formatStatusTargetLabel(params: Pick<SubagentParamsLike, "dir" | "index" | "view">, targetRunId: string | undefined): string {
|
|
2015
|
+
let target: string;
|
|
2016
|
+
if (targetRunId) {
|
|
2017
|
+
target = `run ${targetRunId}`;
|
|
2018
|
+
} else if (params.dir) {
|
|
2019
|
+
target = `dir ${params.dir}`;
|
|
2020
|
+
} else {
|
|
2021
|
+
target = params.view === "transcript" ? "active run" : "active runs";
|
|
2022
|
+
}
|
|
2023
|
+
if (params.view !== "transcript") return `Status target: ${target}`;
|
|
2024
|
+
return `Transcript target: ${target}${params.index !== undefined ? ` · child ${params.index}` : ""}`;
|
|
2025
|
+
}
|
|
2026
|
+
|
|
1845
2027
|
interface AgentDefaultContextPolicy {
|
|
1846
2028
|
params: SubagentParamsLike;
|
|
1847
2029
|
contextForAgent(agentName: string): ContextMode;
|
|
@@ -1925,7 +2107,17 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
|
|
|
1925
2107
|
|
|
1926
2108
|
export const DEFAULT_FOREGROUND_TIMEOUT_MS = 30 * 60 * 1000;
|
|
1927
2109
|
|
|
1928
|
-
|
|
2110
|
+
// Async single-agent runs also need a wall-clock backstop: a child whose bash
|
|
2111
|
+
// tool blocks forever (e.g. a background process inheriting the terminal with
|
|
2112
|
+
// no bash `timeout` arg) would otherwise hang the parent indefinitely with
|
|
2113
|
+
// zero signal. Same generous default as foreground; explicit timeoutMs/
|
|
2114
|
+
// maxRuntimeMs and agent-level defaultTimeoutMs remain authoritative.
|
|
2115
|
+
//
|
|
2116
|
+
// Deliberately NOT applied at the workflow level: async scripted workflows
|
|
2117
|
+
// stay unbounded as a whole, while each runner child has its own deadline.
|
|
2118
|
+
export { DEFAULT_ASYNC_TIMEOUT_MS };
|
|
2119
|
+
|
|
2120
|
+
export function resolveForegroundTimeout(params: SubagentParamsLike, defaultTimeoutMs?: number): { timeoutMs?: number; error?: string } {
|
|
1929
2121
|
const rawTimeout = params.timeoutMs;
|
|
1930
2122
|
const rawMaxRuntime = params.maxRuntimeMs;
|
|
1931
2123
|
if (rawTimeout === undefined && rawMaxRuntime === undefined) {
|
|
@@ -1944,6 +2136,21 @@ function resolveForegroundTimeout(params: SubagentParamsLike, defaultTimeoutMs?:
|
|
|
1944
2136
|
return timeoutMs === undefined ? {} : { timeoutMs };
|
|
1945
2137
|
}
|
|
1946
2138
|
|
|
2139
|
+
/**
|
|
2140
|
+
* Resolve the effective launch timeout for a single-agent run, applying the
|
|
2141
|
+
* async/foreground default when neither the caller nor the agent set one.
|
|
2142
|
+
*
|
|
2143
|
+
* The async default is deliberately applied only to plain single-agent
|
|
2144
|
+
* launches. Composite launches keep their top-level execution unbounded when
|
|
2145
|
+
* no timeout is set; their runner children resolve separate deadlines.
|
|
2146
|
+
* Exported so the executor wiring is directly testable.
|
|
2147
|
+
*/
|
|
2148
|
+
export function resolveSingleAgentLaunchTimeout(params: SubagentParamsLike, async: boolean): { timeoutMs?: number; error?: string } {
|
|
2149
|
+
const isComposite = (params.chain?.length ?? 0) > 0 || (params.tasks?.length ?? 0) > 0 || params.workflowScript !== undefined;
|
|
2150
|
+
const defaultTimeoutMs = !async ? DEFAULT_FOREGROUND_TIMEOUT_MS : isComposite ? undefined : DEFAULT_ASYNC_TIMEOUT_MS;
|
|
2151
|
+
return resolveForegroundTimeout(params, defaultTimeoutMs);
|
|
2152
|
+
}
|
|
2153
|
+
|
|
1947
2154
|
function resolveToolBudget(
|
|
1948
2155
|
raw: unknown,
|
|
1949
2156
|
label = "toolBudget",
|
|
@@ -2353,7 +2560,8 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2353
2560
|
details: { mode: "single" as const, results: [] },
|
|
2354
2561
|
};
|
|
2355
2562
|
}
|
|
2356
|
-
const
|
|
2563
|
+
const requestedWorkflowChildAsyncId = typeof params.workflowChildAsyncId === "string" ? params.workflowChildAsyncId.trim() : "";
|
|
2564
|
+
const id = requestedWorkflowChildAsyncId && path.basename(requestedWorkflowChildAsyncId) === requestedWorkflowChildAsyncId ? requestedWorkflowChildAsyncId : randomUUID();
|
|
2357
2565
|
const parentModel = data.parentModel;
|
|
2358
2566
|
const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
|
|
2359
2567
|
pi: deps.pi,
|
|
@@ -2511,6 +2719,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2511
2719
|
skills,
|
|
2512
2720
|
output: effectiveOutput,
|
|
2513
2721
|
outputMode: effectiveOutputMode,
|
|
2722
|
+
...(params.reads !== undefined ? { reads: params.reads } : {}),
|
|
2514
2723
|
outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
|
|
2515
2724
|
modelOverride,
|
|
2516
2725
|
thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
|
|
@@ -2982,6 +3191,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2982
3191
|
const result = await runSync(input.ctx.cwd, input.agents, task.agent, taskText, compactOptional<Parameters<typeof runSync>[4]>({
|
|
2983
3192
|
permissions: input.permissions,
|
|
2984
3193
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
3194
|
+
...workflowForegroundSteeringLaunchOptions(input.foregroundControl, index),
|
|
2985
3195
|
context: input.contextPolicy.contextForAgent(task.agent),
|
|
2986
3196
|
cwd: taskCwd,
|
|
2987
3197
|
signal: input.signal,
|
|
@@ -3300,10 +3510,11 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3300
3510
|
);
|
|
3301
3511
|
if (errorResult) return errorResult;
|
|
3302
3512
|
|
|
3303
|
-
let
|
|
3513
|
+
let worktreeCleanupHandled = false;
|
|
3514
|
+
let pendingHandoff: Details["parallelHandoff"];
|
|
3304
3515
|
try {
|
|
3305
3516
|
if (worktreeSetup) {
|
|
3306
|
-
writePendingParallelHandoff({
|
|
3517
|
+
pendingHandoff = writePendingParallelHandoff({
|
|
3307
3518
|
manifestPath: parallelHandoffPath(artifactsDir, runId),
|
|
3308
3519
|
runId,
|
|
3309
3520
|
mode: "parallel",
|
|
@@ -3406,10 +3617,13 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3406
3617
|
updateForegroundNestedProjection(foregroundControl);
|
|
3407
3618
|
attachRootChildrenToSteps(runId, results, foregroundControl.nestedChildren);
|
|
3408
3619
|
}
|
|
3620
|
+
const detached = results.find((result) => result.detached);
|
|
3409
3621
|
let handoff: ReturnType<typeof finalizeParallelWorktreeHandoff> | undefined;
|
|
3410
3622
|
if (worktreeSetup) {
|
|
3411
|
-
|
|
3412
|
-
handoff =
|
|
3623
|
+
worktreeCleanupHandled = true;
|
|
3624
|
+
handoff = detached
|
|
3625
|
+
? { suffix: pendingHandoff ? formatParallelHandoffReference(pendingHandoff) : "", reference: pendingHandoff }
|
|
3626
|
+
: finalizeParallelWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: effectiveCwd, tasks, results });
|
|
3413
3627
|
}
|
|
3414
3628
|
const interrupted = results.find((result) => result.interrupted);
|
|
3415
3629
|
const totalCost = sumResultsCost(results);
|
|
@@ -3432,11 +3646,10 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3432
3646
|
details,
|
|
3433
3647
|
};
|
|
3434
3648
|
}
|
|
3435
|
-
const detachedIndex = results.findIndex((result) => result.detached);
|
|
3436
|
-
const detached = detachedIndex >= 0 ? results[detachedIndex] : undefined;
|
|
3437
3649
|
if (detached) {
|
|
3650
|
+
const handoffSuffix = handoff?.suffix ? `\n\n${handoff.suffix}` : "";
|
|
3438
3651
|
return {
|
|
3439
|
-
content: [{ type: "text", text: `Parallel run detached for intercom coordination (${detached.agent}). Reply to the supervisor request first, then wait with subagent_wait({ id: "${runId}" }). Use subagent({ action: "status", id: "${runId}" }) to recover the result; do not resume or launch a replacement while it remains detached
|
|
3652
|
+
content: [{ type: "text", text: `Parallel run detached for intercom coordination (${detached.agent}). Reply to the supervisor request first, then wait with subagent_wait({ id: "${runId}" }). Use subagent({ action: "status", id: "${runId}" }) to recover the result; do not resume or launch a replacement while it remains detached.${handoffSuffix}` }],
|
|
3440
3653
|
details,
|
|
3441
3654
|
};
|
|
3442
3655
|
}
|
|
@@ -3450,6 +3663,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3450
3663
|
runId,
|
|
3451
3664
|
mode: "parallel",
|
|
3452
3665
|
details,
|
|
3666
|
+
...(params.workflowParentRunId !== undefined ? { preserveDetailsOutputs: true } : {}),
|
|
3453
3667
|
...(foregroundControl?.nestedChildren?.length ? { nestedChildren: foregroundControl.nestedChildren } : {}),
|
|
3454
3668
|
});
|
|
3455
3669
|
if (intercomReceipt) {
|
|
@@ -3484,7 +3698,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3484
3698
|
details,
|
|
3485
3699
|
};
|
|
3486
3700
|
} finally {
|
|
3487
|
-
if (worktreeSetup && !
|
|
3701
|
+
if (worktreeSetup && !worktreeCleanupHandled) cleanupWorktrees(worktreeSetup);
|
|
3488
3702
|
}
|
|
3489
3703
|
}
|
|
3490
3704
|
|
|
@@ -3535,6 +3749,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3535
3749
|
data.modelScope === undefined ? {} : { scope: data.modelScope },
|
|
3536
3750
|
);
|
|
3537
3751
|
let skillOverride: string[] | false | undefined = normalizeSkillInput(params.skill);
|
|
3752
|
+
let readsOverride: string[] | false | undefined = params.reads;
|
|
3538
3753
|
const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
|
|
3539
3754
|
let effectiveOutput = normalizeSingleOutputOverride(rawOutput, agentConfig.output);
|
|
3540
3755
|
const effectiveOutputMode = params.outputMode ?? "inline";
|
|
@@ -3572,6 +3787,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3572
3787
|
if (override?.model !== undefined) modelOverride = resolveEffectiveSubagentModel(override.model, agentConfig.model, parentModel, availableModels, currentProvider, data.modelScope === undefined ? {} : { scope: data.modelScope });
|
|
3573
3788
|
if (override?.output !== undefined) effectiveOutput = normalizeSingleOutputOverride(override.output, agentConfig.output);
|
|
3574
3789
|
if (override?.skills !== undefined) skillOverride = override.skills;
|
|
3790
|
+
if (override?.reads !== undefined) readsOverride = override.reads;
|
|
3575
3791
|
|
|
3576
3792
|
if (result.runInBackground) {
|
|
3577
3793
|
if (!isAsyncAvailable()) {
|
|
@@ -3611,6 +3827,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3611
3827
|
skills: skillOverride === false ? [] : skillOverride,
|
|
3612
3828
|
output: effectiveOutput,
|
|
3613
3829
|
outputMode: effectiveOutputMode,
|
|
3830
|
+
...(readsOverride !== undefined ? { reads: readsOverride } : {}),
|
|
3614
3831
|
outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
|
|
3615
3832
|
modelOverride,
|
|
3616
3833
|
thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
|
|
@@ -3647,6 +3864,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3647
3864
|
const structuredRuntime = params.outputSchema
|
|
3648
3865
|
? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ? path.join(artifactsDir, "structured-output", runId) : undefined)
|
|
3649
3866
|
: undefined;
|
|
3867
|
+
// Reads: caller override > agent defaultReads > none. `~`/`~/` expand to home;
|
|
3868
|
+
// absolute paths pass through; relative paths resolve against the child cwd.
|
|
3869
|
+
const reads = readsOverride !== undefined ? readsOverride : agentConfig.defaultReads ?? false;
|
|
3870
|
+
const readPaths = Array.isArray(reads) ? resolveExistingReadPaths(reads, effectiveCwd) : [];
|
|
3871
|
+
const readsInstruction = readPaths.length > 0
|
|
3872
|
+
? `[Read from: ${readPaths.join(", ")}]\n\n`
|
|
3873
|
+
: "";
|
|
3874
|
+
task = readsInstruction + task;
|
|
3650
3875
|
task = injectSingleOutputInstruction(task, outputPath, agentConfig);
|
|
3651
3876
|
|
|
3652
3877
|
let effectiveSkills: string[] | undefined;
|
|
@@ -3688,6 +3913,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3688
3913
|
r = await runSync(ctx.cwd, agents, params.agent!, task, compactOptional<Parameters<typeof runSync>[4]>({
|
|
3689
3914
|
permissions: deps.config.permissions,
|
|
3690
3915
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
3916
|
+
...workflowForegroundSteeringLaunchOptions(foregroundControl, 0),
|
|
3691
3917
|
context: data.contextPolicy.contextForAgent(params.agent!),
|
|
3692
3918
|
cwd: effectiveCwd,
|
|
3693
3919
|
signal,
|
|
@@ -3810,6 +4036,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3810
4036
|
runId,
|
|
3811
4037
|
mode: "single",
|
|
3812
4038
|
details,
|
|
4039
|
+
...(params.workflowParentRunId !== undefined ? { preserveDetailsOutputs: true } : {}),
|
|
3813
4040
|
...(foregroundControl?.nestedChildren?.length ? { nestedChildren: foregroundControl.nestedChildren } : {}),
|
|
3814
4041
|
});
|
|
3815
4042
|
if (intercomReceipt) {
|
|
@@ -3865,8 +4092,70 @@ function duplicateSubagentCallResult(params: SubagentParamsLike): AgentToolResul
|
|
|
3865
4092
|
};
|
|
3866
4093
|
}
|
|
3867
4094
|
|
|
4095
|
+
const workflowLaunchObservers = new WeakMap<object, (launch: { agent: string; sessionFile?: string }) => void>();
|
|
4096
|
+
|
|
4097
|
+
function recordMissionWorkflowChild(
|
|
4098
|
+
binding: MissionLaunchBinding | undefined,
|
|
4099
|
+
workflowRunId: string,
|
|
4100
|
+
key: string,
|
|
4101
|
+
update: Omit<MissionWorkflowChildUpdate, "workflowRunId" | "key">,
|
|
4102
|
+
): void {
|
|
4103
|
+
if (!binding) return;
|
|
4104
|
+
try {
|
|
4105
|
+
updateMission(binding.location, binding.missionId, { upsertWorkflowChildren: [{ workflowRunId, key, ...update }] });
|
|
4106
|
+
} catch (error) {
|
|
4107
|
+
console.warn(`[pi-subagents] Failed to record mission workflow child '${key}': ${error instanceof Error ? error.message : String(error)}`);
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
|
|
4111
|
+
export function missionWorkflowChildStatus(result: AgentToolResult<Details>): string {
|
|
4112
|
+
const childResults = result.details.results;
|
|
4113
|
+
if (childResults.some((child) => child.detached || child.interrupted)) return "paused";
|
|
4114
|
+
if (result.isError === true || childResults.some((child) => child.exitCode !== 0)) return "failed";
|
|
4115
|
+
if (childResults.length === 0 && (result.details.asyncId || result.details.asyncDir)) return "running";
|
|
4116
|
+
return "completed";
|
|
4117
|
+
}
|
|
4118
|
+
|
|
4119
|
+
export async function runMissionWorkflowChild(
|
|
4120
|
+
binding: MissionLaunchBinding | undefined,
|
|
4121
|
+
workflowRunId: string,
|
|
4122
|
+
key: string,
|
|
4123
|
+
phase: string | undefined,
|
|
4124
|
+
run: () => Promise<AgentToolResult<Details>>,
|
|
4125
|
+
): Promise<AgentToolResult<Details>> {
|
|
4126
|
+
try {
|
|
4127
|
+
return await run();
|
|
4128
|
+
} catch (error) {
|
|
4129
|
+
recordMissionWorkflowChild(binding, workflowRunId, key, {
|
|
4130
|
+
status: "failed",
|
|
4131
|
+
completedAt: new Date().toISOString(),
|
|
4132
|
+
heartbeat: { status: "failed", ...(phase ? { phase } : {}), message: error instanceof Error ? error.message : String(error) },
|
|
4133
|
+
});
|
|
4134
|
+
throw error;
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
|
|
4138
|
+
export function bindMissionWorkflowChildAsyncLaunch(
|
|
4139
|
+
params: SubagentParamsLike,
|
|
4140
|
+
binding: MissionLaunchBinding | undefined,
|
|
4141
|
+
asyncByDefault: boolean,
|
|
4142
|
+
asyncId: string = randomUUID(),
|
|
4143
|
+
): SubagentParamsLike {
|
|
4144
|
+
const requestedAsync = params.async ?? asyncByDefault;
|
|
4145
|
+
if (!binding || !requestedAsync || params.clarify === true) return params;
|
|
4146
|
+
const id = asyncId.trim();
|
|
4147
|
+
if (!id || path.basename(id) !== id) throw new Error("workflow child async id must be a single path segment");
|
|
4148
|
+
writeMissionAsyncBinding(path.join(DIRS.async, id), binding);
|
|
4149
|
+
return { ...params, workflowChildAsyncId: id };
|
|
4150
|
+
}
|
|
4151
|
+
|
|
3868
4152
|
function workflowChildResult(key: string, result: AgentToolResult<Details>): WorkflowScriptChildResult {
|
|
3869
|
-
const
|
|
4153
|
+
const receiptOutput = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n");
|
|
4154
|
+
const output = result.details.results.length === 1 && result.details.results[0]?.finalOutput !== undefined
|
|
4155
|
+
? result.details.results[0].finalOutput
|
|
4156
|
+
: receiptOutput;
|
|
4157
|
+
const detached = result.details.results.some((child) => child.detached);
|
|
4158
|
+
const ok = result.isError !== true && !detached;
|
|
3870
4159
|
const artifactPaths = new Set<string>();
|
|
3871
4160
|
if (result.details.asyncDir) artifactPaths.add(result.details.asyncDir);
|
|
3872
4161
|
if (result.details.parallelHandoff?.path) artifactPaths.add(result.details.parallelHandoff.path);
|
|
@@ -3876,18 +4165,75 @@ function workflowChildResult(key: string, result: AgentToolResult<Details>): Wor
|
|
|
3876
4165
|
if (child.sessionFile) artifactPaths.add(child.sessionFile);
|
|
3877
4166
|
}
|
|
3878
4167
|
const structured = result.details.results.map((child) => child.structuredOutput).filter((value) => value !== undefined);
|
|
4168
|
+
const resolvedAgents = [...new Set(result.details.results.map((child) => child.agent).filter((agent): agent is string => Boolean(agent)))];
|
|
3879
4169
|
return {
|
|
3880
4170
|
key,
|
|
3881
|
-
ok
|
|
4171
|
+
ok,
|
|
4172
|
+
...(resolvedAgents.length === 1 ? { agent: resolvedAgents[0] } : {}),
|
|
3882
4173
|
...(result.details.runId || result.details.asyncId ? { runId: result.details.runId ?? result.details.asyncId } : {}),
|
|
3883
4174
|
output,
|
|
3884
|
-
...(
|
|
4175
|
+
...(!ok ? { error: receiptOutput || output || "Child run failed." } : {}),
|
|
3885
4176
|
...(structured.length === 1 ? { structuredOutput: structured[0] } : structured.length > 1 ? { structuredOutput: structured } : {}),
|
|
3886
4177
|
artifactPaths: [...artifactPaths],
|
|
3887
4178
|
results: result.details.results,
|
|
3888
4179
|
};
|
|
3889
4180
|
}
|
|
3890
4181
|
|
|
4182
|
+
export function prepareWorkflowLaunchParams(
|
|
4183
|
+
workflowDefaults: SubagentParamsLike,
|
|
4184
|
+
childParams: Record<string, unknown>,
|
|
4185
|
+
parentWorkflowRunId: string,
|
|
4186
|
+
workflowKey: string,
|
|
4187
|
+
options: { missionDetached?: boolean; suppressRoutineResultIntercom?: boolean } = {},
|
|
4188
|
+
): SubagentParamsLike {
|
|
4189
|
+
if (typeof childParams.resume === "string") {
|
|
4190
|
+
if (childParams.gate !== undefined || workflowDefaults.gate !== undefined) {
|
|
4191
|
+
throw new Error("gate is not supported with retained resume; resume uses the retained child contract.");
|
|
4192
|
+
}
|
|
4193
|
+
const timeoutMs = childParams.timeoutMs ?? childParams.maxRuntimeMs ?? workflowDefaults.timeoutMs ?? workflowDefaults.maxRuntimeMs;
|
|
4194
|
+
const turnBudget = childParams.turnBudget ?? workflowDefaults.turnBudget;
|
|
4195
|
+
const toolBudget = childParams.toolBudget ?? workflowDefaults.toolBudget;
|
|
4196
|
+
return {
|
|
4197
|
+
action: "resume",
|
|
4198
|
+
id: childParams.resume.trim(),
|
|
4199
|
+
message: typeof childParams.task === "string" ? childParams.task.trim() : "",
|
|
4200
|
+
workflowParentRunId: parentWorkflowRunId,
|
|
4201
|
+
workflowKey,
|
|
4202
|
+
...(options.missionDetached ? { mission: false } : {}),
|
|
4203
|
+
...(timeoutMs !== undefined ? { timeoutMs: timeoutMs as number } : {}),
|
|
4204
|
+
...(turnBudget !== undefined ? { turnBudget: turnBudget as TurnBudgetConfig } : {}),
|
|
4205
|
+
...(toolBudget !== undefined ? { toolBudget: toolBudget as ToolBudgetConfig } : {}),
|
|
4206
|
+
};
|
|
4207
|
+
}
|
|
4208
|
+
const launchParams = {
|
|
4209
|
+
...workflowDefaults,
|
|
4210
|
+
async: false,
|
|
4211
|
+
...childParams,
|
|
4212
|
+
...(options.missionDetached ? { mission: false } : {}),
|
|
4213
|
+
workflowParentRunId: parentWorkflowRunId,
|
|
4214
|
+
workflowKey,
|
|
4215
|
+
...(options.suppressRoutineResultIntercom ? { suppressRoutineResultIntercom: true } : {}),
|
|
4216
|
+
} as SubagentParamsLike;
|
|
4217
|
+
const normalizedGate = normalizeGateParams(launchParams);
|
|
4218
|
+
if (!normalizedGate.ok) throw new Error(normalizedGate.error);
|
|
4219
|
+
return prepareWorkflowChildParams(normalizedGate.params);
|
|
4220
|
+
}
|
|
4221
|
+
|
|
4222
|
+
type GateParamsNormalizationResult =
|
|
4223
|
+
| { ok: true; params: SubagentParamsLike }
|
|
4224
|
+
| { ok: false; error: string };
|
|
4225
|
+
|
|
4226
|
+
function normalizeGateParams(params: SubagentParamsLike): GateParamsNormalizationResult {
|
|
4227
|
+
if (params.gate !== undefined && params.action === "resume") {
|
|
4228
|
+
return { ok: false, error: "gate is not supported with action='resume'; resume uses the retained child contract." };
|
|
4229
|
+
}
|
|
4230
|
+
const normalized = normalizeGateAcceptance(params.gate, params.acceptance);
|
|
4231
|
+
if (!normalized.ok) return { ok: false, error: normalized.error };
|
|
4232
|
+
if (params.gate === undefined) return { ok: true, params };
|
|
4233
|
+
const { gate: _gate, ...rest } = params;
|
|
4234
|
+
return { ok: true, params: { ...rest, ...(normalized.acceptance !== undefined ? { acceptance: normalized.acceptance } : {}) } };
|
|
4235
|
+
}
|
|
4236
|
+
|
|
3891
4237
|
function prepareWorkflowChildParams(params: SubagentParamsLike): SubagentParamsLike {
|
|
3892
4238
|
if (params.worktree !== true || !params.agent) return params;
|
|
3893
4239
|
const {
|
|
@@ -3901,6 +4247,7 @@ function prepareWorkflowChildParams(params: SubagentParamsLike): SubagentParamsL
|
|
|
3901
4247
|
acceptance,
|
|
3902
4248
|
agentContract,
|
|
3903
4249
|
toolBudget,
|
|
4250
|
+
reads,
|
|
3904
4251
|
...runParams
|
|
3905
4252
|
} = params;
|
|
3906
4253
|
return {
|
|
@@ -3912,6 +4259,7 @@ function prepareWorkflowChildParams(params: SubagentParamsLike): SubagentParamsL
|
|
|
3912
4259
|
...(model !== undefined ? { model } : {}),
|
|
3913
4260
|
...(skill !== undefined ? { skill } : {}),
|
|
3914
4261
|
...(output !== undefined ? { output } : {}),
|
|
4262
|
+
...(reads !== undefined ? { reads } : {}),
|
|
3915
4263
|
...(outputMode !== undefined ? { outputMode } : {}),
|
|
3916
4264
|
...(outputSchema !== undefined ? { outputSchema } : {}),
|
|
3917
4265
|
...(acceptance !== undefined ? { acceptance } : {}),
|
|
@@ -3943,21 +4291,6 @@ function workflowChatProgressUpdate(
|
|
|
3943
4291
|
};
|
|
3944
4292
|
}
|
|
3945
4293
|
|
|
3946
|
-
function omitExecutionModeActionAlias(params: SubagentParamsLike): SubagentParamsLike {
|
|
3947
|
-
const action = params.action?.toLowerCase();
|
|
3948
|
-
if (action === "single" && (params.agent !== undefined || params.task !== undefined)) {
|
|
3949
|
-
const rest = { ...params };
|
|
3950
|
-
delete rest.action;
|
|
3951
|
-
return rest;
|
|
3952
|
-
}
|
|
3953
|
-
if ((action === "parallel" || action === "tasks") && (params.tasks?.length ?? 0) > 0) {
|
|
3954
|
-
const rest = { ...params };
|
|
3955
|
-
delete rest.action;
|
|
3956
|
-
return rest;
|
|
3957
|
-
}
|
|
3958
|
-
return params;
|
|
3959
|
-
}
|
|
3960
|
-
|
|
3961
4294
|
function createScheduledOwnerState(source: SubagentState, ownerSessionId: string, ctx: ExtensionContext): SubagentState {
|
|
3962
4295
|
const ownerSpawns = source.subagentSpawns?.sessionId === ownerSessionId
|
|
3963
4296
|
? {
|
|
@@ -3996,6 +4329,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3996
4329
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
3997
4330
|
ctx: ExtensionContext,
|
|
3998
4331
|
) => Promise<AgentToolResult<Details>>;
|
|
4332
|
+
/** Public/model-facing execution boundary. Internal direct launch primitives use execute or executeDelegated. */
|
|
4333
|
+
executePublic: (
|
|
4334
|
+
id: string,
|
|
4335
|
+
params: SubagentParamsLike,
|
|
4336
|
+
signal: AbortSignal,
|
|
4337
|
+
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
4338
|
+
ctx: ExtensionContext,
|
|
4339
|
+
) => Promise<AgentToolResult<Details>>;
|
|
3999
4340
|
/**
|
|
4000
4341
|
* Correlated extension-to-extension delegation owns its request IDs and
|
|
4001
4342
|
* cancellation controllers, so independent requests may execute concurrently.
|
|
@@ -4018,6 +4359,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4018
4359
|
} {
|
|
4019
4360
|
const delegatedThinkingOverrides = new WeakMap<object, AgentConfig["thinking"]>();
|
|
4020
4361
|
const delegatedZeroToolBudgets = new WeakSet<object>();
|
|
4362
|
+
const warnedArtifactPackageDirs = new Set<string>();
|
|
4021
4363
|
const scheduledOwnerExecutors = new Map<string, ReturnType<typeof createSubagentExecutor>>();
|
|
4022
4364
|
const execute = async (
|
|
4023
4365
|
_id: string,
|
|
@@ -4027,30 +4369,77 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4027
4369
|
ctx: ExtensionContext,
|
|
4028
4370
|
preserveActiveSession = false,
|
|
4029
4371
|
): Promise<AgentToolResult<Details>> => {
|
|
4372
|
+
const workflowLaunchObserver = workflowLaunchObservers.get(params);
|
|
4030
4373
|
const delegatedThinkingOverride = delegatedThinkingOverrides.get(params);
|
|
4031
4374
|
const allowZeroToolBudget = delegatedZeroToolBudgets.has(params);
|
|
4032
4375
|
if (!preserveActiveSession) deps.state.baseCwd = ctx.cwd;
|
|
4033
4376
|
deps.state.foregroundRuns ??= new Map();
|
|
4034
4377
|
deps.state.foregroundControls ??= new Map();
|
|
4035
4378
|
deps.state.lastForegroundControlId ??= null;
|
|
4036
|
-
const
|
|
4037
|
-
if (
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
return { content: [{ type: "text", text: "workflowScript does not support clarify UI." }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4044
|
-
}
|
|
4045
|
-
const timeout = requestParams.timeoutMs ?? requestParams.maxRuntimeMs ?? DEFAULT_FOREGROUND_TIMEOUT_MS;
|
|
4379
|
+
const normalizedGate = normalizeGateParams(params);
|
|
4380
|
+
if (!normalizedGate.ok) return buildRequestedModeError(params, normalizedGate.error);
|
|
4381
|
+
const requestParams = normalizedGate.params;
|
|
4382
|
+
const normalizedAction = typeof requestParams.action === "string" ? requestParams.action.trim() : requestParams.action;
|
|
4383
|
+
if (requestParams.workflowScript !== undefined && normalizedAction === undefined) {
|
|
4384
|
+
const parentCwd = ctx.cwd;
|
|
4385
|
+
const timeout = requestParams.timeoutMs ?? requestParams.maxRuntimeMs ?? (requestParams.async === false ? DEFAULT_FOREGROUND_TIMEOUT_MS : undefined);
|
|
4046
4386
|
const workflowUsageBudget = validateUsageBudgetConfig(requestParams.usageBudget ?? deps.config.usageBudget, requestParams.usageBudget ? "usageBudget" : "config.usageBudget");
|
|
4047
4387
|
if (workflowUsageBudget.error) return buildRequestedModeError(requestParams, workflowUsageBudget.error);
|
|
4048
|
-
const workflowCwd = resolveRequestedCwd(
|
|
4049
|
-
const
|
|
4388
|
+
const workflowCwd = resolveRequestedCwd(parentCwd, requestParams.cwd);
|
|
4389
|
+
const workflowPrompts = { render: (ref: string, vars?: unknown) => renderWorkflowPrompt(ref, vars, workflowCwd) };
|
|
4390
|
+
const chatProgressResult = resolveWorkflowChatProgress({ requested: requestParams.chatProgress, parentCwd, workflowCwd, background: requestParams.async !== false });
|
|
4050
4391
|
if (chatProgressResult.error) return { content: [{ type: "text", text: chatProgressResult.error }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4051
4392
|
const chatProgress = chatProgressResult.projection!;
|
|
4393
|
+
const explicitMission = requestParams.missionId !== undefined || requestParams.mission !== undefined;
|
|
4394
|
+
const autoMission = !explicitMission;
|
|
4395
|
+
const workflowPreview = autoMission ? previewSimpleWorkflowRun(requestParams.workflowScript) : undefined;
|
|
4396
|
+
const previewTask = workflowPreview?.task?.trim() || undefined;
|
|
4397
|
+
const previewAgent = workflowPreview?.agent?.trim() || undefined;
|
|
4398
|
+
const scriptFirstLine = requestParams.workflowScript.split(/\r?\n/).map((line) => line.trim()).find(Boolean) || "Workflow";
|
|
4399
|
+
const boundedScriptPreview = scriptFirstLine.length > 100 ? `${scriptFirstLine.slice(0, 97)}...` : scriptFirstLine;
|
|
4400
|
+
const derivedObjective = previewTask || (previewAgent ? `Workflow: ${previewAgent}` : boundedScriptPreview);
|
|
4401
|
+
let missionBinding: MissionLaunchBinding | undefined;
|
|
4402
|
+
let missionWarning: string | undefined;
|
|
4403
|
+
try {
|
|
4404
|
+
missionBinding = prepareMissionLaunch({
|
|
4405
|
+
params: autoMission ? { ...requestParams, task: derivedObjective } : requestParams,
|
|
4406
|
+
projectRoot: workflowCwd,
|
|
4407
|
+
...(deps.config.missions ? { config: deps.config.missions } : {}),
|
|
4408
|
+
ownerSessionId: resolveCurrentSessionId(ctx.sessionManager),
|
|
4409
|
+
});
|
|
4410
|
+
} catch (error) {
|
|
4411
|
+
if (explicitMission) return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "workflow", results: [] } };
|
|
4412
|
+
missionWarning = `Mission tracking unavailable: ${error instanceof Error ? error.message : String(error)}`;
|
|
4413
|
+
}
|
|
4414
|
+
let shouldPatchMissionObjective = autoMission && previewTask === undefined && missionBinding !== undefined;
|
|
4415
|
+
const patchMissionObjective = (task: unknown): void => {
|
|
4416
|
+
if (!shouldPatchMissionObjective || !missionBinding || typeof task !== "string" || !task.trim()) return;
|
|
4417
|
+
shouldPatchMissionObjective = false;
|
|
4418
|
+
const objective = task.trim();
|
|
4419
|
+
const firstLine = objective.split(/\r?\n/, 1)[0]?.trim() || objective;
|
|
4420
|
+
const title = firstLine.length > 100 ? `${firstLine.slice(0, 97)}...` : firstLine;
|
|
4421
|
+
try {
|
|
4422
|
+
updateMission(missionBinding.location, missionBinding.missionId, { title, objective });
|
|
4423
|
+
} catch (error) {
|
|
4424
|
+
console.warn(`[pi-subagents] Failed to update automatic mission objective: ${error instanceof Error ? error.message : String(error)}`);
|
|
4425
|
+
}
|
|
4426
|
+
};
|
|
4427
|
+
const detachWorkflowChildMissions = autoMission || missionBinding !== undefined || requestParams.mission === false;
|
|
4428
|
+
const workflowState = missionBinding ? createMissionWorkflowState(missionBinding.location, missionBinding.missionId) : undefined;
|
|
4429
|
+
const attachWorkflowMission = (result: AgentToolResult<Details>): AgentToolResult<Details> => {
|
|
4430
|
+
if (!missionBinding) return missionWarning ? { ...result, details: { ...result.details, missionWarning } } : result;
|
|
4431
|
+
try {
|
|
4432
|
+
return attachMissionToLaunchResult({ binding: missionBinding, result });
|
|
4433
|
+
} catch (error) {
|
|
4434
|
+
const warning = `Mission tracking unavailable after launch: ${error instanceof Error ? error.message : String(error)}`;
|
|
4435
|
+
return explicitMission
|
|
4436
|
+
? { ...result, isError: true, content: [...result.content, { type: "text", text: warning }], details: { ...result.details, missionWarning: warning } }
|
|
4437
|
+
: { ...result, details: { ...result.details, missionWarning: warning } };
|
|
4438
|
+
}
|
|
4439
|
+
};
|
|
4052
4440
|
if (requestParams.async !== false) {
|
|
4053
|
-
const
|
|
4441
|
+
const toolCallId = _id;
|
|
4442
|
+
const workflowRunId = randomUUID();
|
|
4054
4443
|
const asyncDir = path.join(DIRS.async, workflowRunId);
|
|
4055
4444
|
const resultPath = path.join(DIRS.results, `${workflowRunId}.json`);
|
|
4056
4445
|
const statusPath = path.join(asyncDir, "status.json");
|
|
@@ -4064,26 +4453,39 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4064
4453
|
deps.state.workflowControllers.set(workflowRunId, controller);
|
|
4065
4454
|
let status: AsyncStatus = {
|
|
4066
4455
|
runId: workflowRunId,
|
|
4456
|
+
toolCallId,
|
|
4067
4457
|
sessionId: currentSessionId ?? undefined,
|
|
4068
4458
|
mode: "workflow",
|
|
4069
4459
|
state: "running",
|
|
4070
4460
|
startedAt,
|
|
4071
4461
|
lastUpdate: startedAt,
|
|
4072
|
-
deadlineAt: startedAt + timeout,
|
|
4073
|
-
|
|
4074
|
-
cwd: ctx.cwd,
|
|
4462
|
+
...(timeout !== undefined ? { deadlineAt: startedAt + timeout, timeoutMs: timeout } : {}),
|
|
4463
|
+
cwd: workflowCwd,
|
|
4075
4464
|
pid: process.pid,
|
|
4076
4465
|
steps: [],
|
|
4077
4466
|
workflow: { trace: [], emits: [], console: [] },
|
|
4078
4467
|
};
|
|
4079
4468
|
const appendWorkflowEvent = (event: Record<string, unknown>) => fs.appendFileSync(eventsPath, `${JSON.stringify({ ts: Date.now(), runId: workflowRunId, ...event })}\n`, "utf-8");
|
|
4469
|
+
let indexedState: AsyncStatus["state"] | undefined;
|
|
4080
4470
|
const persist = () => {
|
|
4081
4471
|
status.lastUpdate = Date.now();
|
|
4082
4472
|
writeAtomicJson(statusPath, status);
|
|
4473
|
+
if (indexedState !== status.state) {
|
|
4474
|
+
updateActiveRunIndex(asyncDir, status.state);
|
|
4475
|
+
indexedState = status.state;
|
|
4476
|
+
}
|
|
4083
4477
|
const job = deps.state.asyncJobs.get(workflowRunId);
|
|
4084
4478
|
if (job) {
|
|
4085
4479
|
job.status = status.state;
|
|
4086
4480
|
job.updatedAt = status.lastUpdate;
|
|
4481
|
+
job.activityState = status.activityState;
|
|
4482
|
+
job.lastActivityAt = status.lastActivityAt;
|
|
4483
|
+
job.currentTool = status.currentTool;
|
|
4484
|
+
job.currentToolStartedAt = status.currentToolStartedAt;
|
|
4485
|
+
job.currentPath = status.currentPath;
|
|
4486
|
+
job.turnCount = status.turnCount;
|
|
4487
|
+
job.toolCount = status.toolCount;
|
|
4488
|
+
job.currentStep = status.currentStep;
|
|
4087
4489
|
if (status.steps) {
|
|
4088
4490
|
job.steps = status.steps.map((step, index) => ({ ...step, index }));
|
|
4089
4491
|
job.agents = status.steps.map((step) => step.agent);
|
|
@@ -4094,7 +4496,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4094
4496
|
job.workflow = status.workflow;
|
|
4095
4497
|
}
|
|
4096
4498
|
};
|
|
4097
|
-
const
|
|
4499
|
+
const projectWorkflowActivity = () => {
|
|
4500
|
+
const steps = status.steps ?? [];
|
|
4501
|
+
const runningSteps = steps.filter((step) => step.status === "running");
|
|
4502
|
+
const lastActivityAt = runningSteps.reduce<number | undefined>((latest, step) => step.lastActivityAt === undefined ? latest : Math.max(latest ?? step.lastActivityAt, step.lastActivityAt), undefined);
|
|
4503
|
+
const activeToolStep = runningSteps
|
|
4504
|
+
.filter((step) => step.currentTool)
|
|
4505
|
+
.sort((left, right) => (left.lastActivityAt ?? 0) - (right.lastActivityAt ?? 0))
|
|
4506
|
+
.at(-1);
|
|
4507
|
+
status.activityState = runningSteps.some((step) => step.activityState === "needs_attention")
|
|
4508
|
+
? "needs_attention"
|
|
4509
|
+
: runningSteps.some((step) => step.activityState === "active_long_running") ? "active_long_running" : undefined;
|
|
4510
|
+
status.lastActivityAt = lastActivityAt;
|
|
4511
|
+
status.currentTool = activeToolStep?.currentTool;
|
|
4512
|
+
status.currentToolStartedAt = activeToolStep?.currentToolStartedAt;
|
|
4513
|
+
status.currentPath = activeToolStep?.currentPath;
|
|
4514
|
+
const turnCounts = steps.flatMap((step) => step.turnCount === undefined ? [] : [step.turnCount]);
|
|
4515
|
+
const toolCounts = steps.flatMap((step) => step.toolCount === undefined ? [] : [step.toolCount]);
|
|
4516
|
+
status.turnCount = turnCounts.length > 0 ? turnCounts.reduce((total, count) => total + count, 0) : undefined;
|
|
4517
|
+
status.toolCount = toolCounts.length > 0 ? toolCounts.reduce((total, count) => total + count, 0) : undefined;
|
|
4518
|
+
status.currentStep = runningSteps.length === 1 ? steps.indexOf(runningSteps[0]!) : undefined;
|
|
4519
|
+
};
|
|
4520
|
+
const workflowJob: AsyncJobState = { asyncId: workflowRunId, asyncDir, cwd: workflowCwd, status: "running", sessionId: currentSessionId ?? undefined, mode: "workflow", agents: [], steps: [], startedAt, updatedAt: startedAt, ...(timeout !== undefined ? { timeoutMs: timeout, deadlineAt: startedAt + timeout } : {}), workflow: status.workflow };
|
|
4098
4521
|
deps.state.asyncJobs.set(workflowRunId, workflowJob);
|
|
4099
4522
|
deps.state.fleetJobs ??= new Map();
|
|
4100
4523
|
deps.state.fleetJobs.set(workflowRunId, workflowJob);
|
|
@@ -4103,23 +4526,43 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4103
4526
|
const { workflowScript, async: _workflowAsync, chatProgress: _chatProgress, ...workflowRequest } = requestParams;
|
|
4104
4527
|
void Promise.resolve().then(async () => {
|
|
4105
4528
|
const workflowResults: SingleResult[] = [];
|
|
4106
|
-
const { action: _action, agent: _agent, task: _task, tasks: _tasks, chain: _chain, concurrency: _concurrency, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, ...workflowChildDefaults } = workflowRequest;
|
|
4529
|
+
const { action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, missionId: _missionId, mission: _mission, ...workflowChildDefaults } = workflowRequest;
|
|
4530
|
+
const workflowSteps = new Map<string, NonNullable<AsyncStatus["steps"]>[number]>();
|
|
4531
|
+
let projectedTraceLength = 0;
|
|
4532
|
+
let projectedTraceTail: NonNullable<Details["workflow"]>["trace"][number] | undefined;
|
|
4107
4533
|
const updateTrace = (trace: NonNullable<Details["workflow"]>["trace"]) => {
|
|
4108
4534
|
status.workflow = { ...(status.workflow ?? { emits: [], console: [] }), trace };
|
|
4109
|
-
|
|
4110
|
-
|
|
4535
|
+
const rebuild = trace.length < projectedTraceLength
|
|
4536
|
+
|| (projectedTraceLength > 0 && trace[projectedTraceLength - 1] !== projectedTraceTail);
|
|
4537
|
+
if (rebuild) {
|
|
4538
|
+
workflowSteps.clear();
|
|
4539
|
+
for (const step of status.steps ?? []) {
|
|
4540
|
+
if (step.workflowKey) workflowSteps.set(step.workflowKey, step);
|
|
4541
|
+
}
|
|
4542
|
+
projectedTraceLength = 0;
|
|
4543
|
+
}
|
|
4544
|
+
for (let index = projectedTraceLength; index < trace.length; index += 1) {
|
|
4545
|
+
const entry = trace[index]!;
|
|
4546
|
+
if (entry.operation !== "run") continue;
|
|
4547
|
+
const existing = workflowSteps.get(entry.key);
|
|
4111
4548
|
if (entry.state === "reused" && existing) continue;
|
|
4112
4549
|
const mapped = entry.state === "started" || entry.state === "reused" ? "running" : entry.state === "completed" ? "completed" : "failed";
|
|
4113
4550
|
if (existing) {
|
|
4114
4551
|
existing.status = mapped;
|
|
4552
|
+
if (entry.agent) existing.agent = entry.agent;
|
|
4115
4553
|
if (entry.error === undefined) delete existing.error;
|
|
4116
4554
|
else existing.error = entry.error;
|
|
4117
4555
|
if (entry.durationMs === undefined) delete existing.durationMs;
|
|
4118
4556
|
else existing.durationMs = entry.durationMs;
|
|
4119
4557
|
} else {
|
|
4120
|
-
|
|
4558
|
+
const step: NonNullable<AsyncStatus["steps"]>[number] = { agent: entry.agent ?? entry.key, label: entry.key, workflowKey: entry.key, parentWorkflowRunId: workflowRunId, status: mapped, startedAt: Date.now() };
|
|
4559
|
+
status.steps?.push(step);
|
|
4560
|
+
workflowSteps.set(entry.key, step);
|
|
4121
4561
|
}
|
|
4122
4562
|
}
|
|
4563
|
+
projectedTraceLength = trace.length;
|
|
4564
|
+
projectedTraceTail = trace.at(-1);
|
|
4565
|
+
projectWorkflowActivity();
|
|
4123
4566
|
persist();
|
|
4124
4567
|
appendWorkflowEvent({ type: "subagent.workflow.trace", trace });
|
|
4125
4568
|
};
|
|
@@ -4128,6 +4571,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4128
4571
|
script: workflowScript,
|
|
4129
4572
|
timeoutMs: timeout,
|
|
4130
4573
|
signal: controller.signal,
|
|
4574
|
+
prompts: workflowPrompts,
|
|
4575
|
+
...(workflowState ? { state: workflowState } : {}),
|
|
4131
4576
|
onTrace: updateTrace,
|
|
4132
4577
|
onEmit: (emits) => {
|
|
4133
4578
|
// Each emit is validated at the host boundary in runWorkflowScript before onEmit fires.
|
|
@@ -4139,10 +4584,71 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4139
4584
|
if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
|
|
4140
4585
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
4141
4586
|
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
|
|
4142
|
-
|
|
4143
|
-
const
|
|
4587
|
+
patchMissionObjective(childParams.task);
|
|
4588
|
+
const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
|
|
4589
|
+
const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
|
|
4590
|
+
recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
|
|
4591
|
+
status: "running",
|
|
4592
|
+
...(typeof childParams.agent === "string" && childParams.agent.trim() ? { agent: childParams.agent.trim() } : {}),
|
|
4593
|
+
...(typeof childParams.task === "string" && childParams.task.trim() ? { task: childParams.task.trim() } : {}),
|
|
4594
|
+
...(childLabel ? { label: childLabel } : {}),
|
|
4595
|
+
...(childPhase ? { phase: childPhase } : {}),
|
|
4596
|
+
heartbeat: { status: "running", ...(childPhase ? { phase: childPhase } : {}) },
|
|
4597
|
+
});
|
|
4598
|
+
const result = await runMissionWorkflowChild(missionBinding, workflowRunId, key, childPhase, () => {
|
|
4599
|
+
const childRequest = bindMissionWorkflowChildAsyncLaunch(
|
|
4600
|
+
prepareWorkflowLaunchParams(workflowChildDefaults, childParams, workflowRunId, key, { missionDetached: detachWorkflowChildMissions }),
|
|
4601
|
+
missionBinding,
|
|
4602
|
+
deps.asyncByDefault,
|
|
4603
|
+
);
|
|
4604
|
+
workflowLaunchObservers.set(childRequest, (launch) => {
|
|
4605
|
+
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
4606
|
+
if (step) {
|
|
4607
|
+
step.agent = launch.agent;
|
|
4608
|
+
step.sessionFile = launch.sessionFile;
|
|
4609
|
+
persist();
|
|
4610
|
+
}
|
|
4611
|
+
recordMissionWorkflowChild(missionBinding, workflowRunId, key, { status: "running", agent: launch.agent, ...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}) });
|
|
4612
|
+
});
|
|
4613
|
+
return execute(randomUUID(), childRequest, workflowSignal, (update) => {
|
|
4614
|
+
const progress = update.details.progress?.[0];
|
|
4615
|
+
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
4616
|
+
if (!progress || !step) return;
|
|
4617
|
+
step.status = progress.status === "completed" ? "completed" : progress.status === "failed" ? "failed" : "running";
|
|
4618
|
+
step.activityState = progress.activityState;
|
|
4619
|
+
step.lastActivityAt = progress.lastActivityAt;
|
|
4620
|
+
step.currentTool = progress.currentTool;
|
|
4621
|
+
step.currentToolArgs = progress.currentToolArgs;
|
|
4622
|
+
step.currentToolStartedAt = progress.currentToolStartedAt;
|
|
4623
|
+
step.currentPath = progress.currentPath;
|
|
4624
|
+
step.recentTools = progress.recentTools.map((tool) => ({ ...tool }));
|
|
4625
|
+
step.recentOutput = [...progress.recentOutput];
|
|
4626
|
+
step.turnCount = progress.turnCount;
|
|
4627
|
+
step.toolCount = progress.toolCount;
|
|
4628
|
+
step.model = progress.model;
|
|
4629
|
+
step.thinking = progress.thinking;
|
|
4630
|
+
step.error = progress.error;
|
|
4631
|
+
projectWorkflowActivity();
|
|
4632
|
+
persist();
|
|
4633
|
+
recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
|
|
4634
|
+
status: step.status,
|
|
4635
|
+
heartbeat: { status: step.status, ...(childPhase ? { phase: childPhase } : {}) },
|
|
4636
|
+
});
|
|
4637
|
+
}, ctx, preserveActiveSession);
|
|
4638
|
+
});
|
|
4144
4639
|
workflowResults.push(...result.details.results);
|
|
4640
|
+
if (result.details.asyncDir && missionBinding) writeMissionAsyncBinding(result.details.asyncDir, missionBinding);
|
|
4145
4641
|
const child = workflowChildResult(key, result);
|
|
4642
|
+
const childStatus = missionWorkflowChildStatus(result);
|
|
4643
|
+
recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
|
|
4644
|
+
status: childStatus,
|
|
4645
|
+
...(child.runId ? { runId: child.runId } : {}),
|
|
4646
|
+
...(result.details.results[0]?.agent ? { agent: result.details.results[0].agent } : {}),
|
|
4647
|
+
...(result.details.results[0]?.sessionFile ? { sessionPath: result.details.results[0].sessionFile } : {}),
|
|
4648
|
+
artifactPaths: child.artifactPaths,
|
|
4649
|
+
...(["completed", "failed"].includes(childStatus) ? { completedAt: new Date().toISOString() } : {}),
|
|
4650
|
+
heartbeat: { status: childStatus, ...(childPhase ? { phase: childPhase } : {}) },
|
|
4651
|
+
});
|
|
4146
4652
|
if (result.details.asyncId) {
|
|
4147
4653
|
const childJob = deps.state.asyncJobs.get(result.details.asyncId);
|
|
4148
4654
|
if (childJob) { childJob.parentWorkflowRunId = workflowRunId; childJob.workflowKey = key; }
|
|
@@ -4156,26 +4662,26 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4156
4662
|
const summary = `Workflow completed with ${workflow.children.length} child run(s). Return: ${returnPreview}${emitPreview} Trace: ${workflow.trace.length} event(s).`;
|
|
4157
4663
|
const workflowUsage = sumResultsUsage(workflowResults);
|
|
4158
4664
|
status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console }, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
|
|
4665
|
+
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary, output: summary, results: workflow.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: workflowCwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
|
|
4159
4666
|
persist();
|
|
4160
4667
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: "complete" });
|
|
4161
|
-
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary, output: summary, results: workflow.children.map((child) => ({ agent: child.key, output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: ctx.cwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
|
|
4162
4668
|
} catch (error) {
|
|
4163
4669
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
4164
4670
|
const stopped = controller.signal.aborted;
|
|
4165
4671
|
status = compactOptional<AsyncStatus>({ ...status, state: stopped ? "stopped" : "failed", stopped: stopped || undefined, error: error instanceof Error ? error.message : String(error), endedAt: Date.now(), workflow: { trace: partial.trace, emits: partial.emits, console: partial.console } });
|
|
4672
|
+
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: false, state: status.state, summary: status.error, error: status.error, stopped: status.stopped, results: partial.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: workflowCwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
|
|
4166
4673
|
persist();
|
|
4167
4674
|
appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, error: status.error });
|
|
4168
|
-
writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, agent: "workflow", mode: "workflow", success: false, state: status.state, summary: status.error, error: status.error, stopped: status.stopped, results: partial.children.map((child) => ({ agent: child.key, output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: ctx.cwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
|
|
4169
4675
|
} finally {
|
|
4170
4676
|
deps.state.workflowControllers?.delete(workflowRunId);
|
|
4171
4677
|
}
|
|
4172
4678
|
});
|
|
4173
|
-
return {
|
|
4679
|
+
return attachWorkflowMission({
|
|
4174
4680
|
content: [{ type: "text", text: formatAsyncStartedMessage(`Async workflow [${workflowRunId}]`, ctx.hasUI === true) }],
|
|
4175
|
-
details: { mode: "workflow", runId: workflowRunId, asyncId: workflowRunId, asyncDir, results: [], chatProgress },
|
|
4176
|
-
};
|
|
4681
|
+
details: { mode: "workflow", runId: workflowRunId, toolCallId, asyncId: workflowRunId, asyncDir, results: [], chatProgress },
|
|
4682
|
+
});
|
|
4177
4683
|
}
|
|
4178
|
-
const { workflowScript: _workflowScript, action: _action, agent: _agent, task: _task, tasks: _tasks, chain: _chain, concurrency: _concurrency, async: _async, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, chatProgress: _chatProgress, ...workflowChildDefaults } = requestParams;
|
|
4684
|
+
const { workflowScript: _workflowScript, action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, async: _async, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, chatProgress: _chatProgress, missionId: _missionId, mission: _mission, ...workflowChildDefaults } = requestParams;
|
|
4179
4685
|
const workflowResults: SingleResult[] = [];
|
|
4180
4686
|
let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [] };
|
|
4181
4687
|
const sendWorkflowProgress = () => {
|
|
@@ -4187,6 +4693,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4187
4693
|
script: requestParams.workflowScript,
|
|
4188
4694
|
timeoutMs: timeout,
|
|
4189
4695
|
signal,
|
|
4696
|
+
prompts: workflowPrompts,
|
|
4697
|
+
...(workflowState ? { state: workflowState } : {}),
|
|
4190
4698
|
onTrace: (trace) => {
|
|
4191
4699
|
liveWorkflow = { ...liveWorkflow, trace };
|
|
4192
4700
|
sendWorkflowProgress();
|
|
@@ -4199,10 +4707,52 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4199
4707
|
if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
|
|
4200
4708
|
const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
|
|
4201
4709
|
if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
|
|
4202
|
-
|
|
4203
|
-
const
|
|
4710
|
+
patchMissionObjective(childParams.task);
|
|
4711
|
+
const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
|
|
4712
|
+
const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
|
|
4713
|
+
recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
4714
|
+
status: "running",
|
|
4715
|
+
...(typeof childParams.agent === "string" && childParams.agent.trim() ? { agent: childParams.agent.trim() } : {}),
|
|
4716
|
+
...(typeof childParams.task === "string" && childParams.task.trim() ? { task: childParams.task.trim() } : {}),
|
|
4717
|
+
...(childLabel ? { label: childLabel } : {}),
|
|
4718
|
+
...(childPhase ? { phase: childPhase } : {}),
|
|
4719
|
+
heartbeat: { status: "running", ...(childPhase ? { phase: childPhase } : {}) },
|
|
4720
|
+
});
|
|
4721
|
+
const result = await runMissionWorkflowChild(missionBinding, _id, key, childPhase, () => {
|
|
4722
|
+
const childRequest = bindMissionWorkflowChildAsyncLaunch(
|
|
4723
|
+
prepareWorkflowLaunchParams(workflowChildDefaults, childParams, _id, key, { missionDetached: detachWorkflowChildMissions, suppressRoutineResultIntercom: chatProgress.mode === "live-card" }),
|
|
4724
|
+
missionBinding,
|
|
4725
|
+
deps.asyncByDefault,
|
|
4726
|
+
);
|
|
4727
|
+
workflowLaunchObservers.set(childRequest, (launch) => recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
4728
|
+
status: "running",
|
|
4729
|
+
agent: launch.agent,
|
|
4730
|
+
...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}),
|
|
4731
|
+
}));
|
|
4732
|
+
return execute(randomUUID(), childRequest, workflowSignal, (update) => {
|
|
4733
|
+
const progress = update.details.progress?.[0];
|
|
4734
|
+
if (!progress) return;
|
|
4735
|
+
const progressStatus = progress.status === "completed" ? "completed" : progress.status === "failed" ? "failed" : "running";
|
|
4736
|
+
recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
4737
|
+
status: progressStatus,
|
|
4738
|
+
heartbeat: { status: progressStatus, ...(childPhase ? { phase: childPhase } : {}) },
|
|
4739
|
+
});
|
|
4740
|
+
}, ctx, preserveActiveSession);
|
|
4741
|
+
});
|
|
4204
4742
|
workflowResults.push(...result.details.results);
|
|
4205
|
-
|
|
4743
|
+
if (result.details.asyncDir && missionBinding) writeMissionAsyncBinding(result.details.asyncDir, missionBinding);
|
|
4744
|
+
const child = workflowChildResult(key, result);
|
|
4745
|
+
const childStatus = missionWorkflowChildStatus(result);
|
|
4746
|
+
recordMissionWorkflowChild(missionBinding, _id, key, {
|
|
4747
|
+
status: childStatus,
|
|
4748
|
+
...(child.runId ? { runId: child.runId } : {}),
|
|
4749
|
+
...(result.details.results[0]?.agent ? { agent: result.details.results[0].agent } : {}),
|
|
4750
|
+
...(result.details.results[0]?.sessionFile ? { sessionPath: result.details.results[0].sessionFile } : {}),
|
|
4751
|
+
artifactPaths: child.artifactPaths,
|
|
4752
|
+
...(["completed", "failed"].includes(childStatus) ? { completedAt: new Date().toISOString() } : {}),
|
|
4753
|
+
heartbeat: { status: childStatus, ...(childPhase ? { phase: childPhase } : {}) },
|
|
4754
|
+
});
|
|
4755
|
+
return child;
|
|
4206
4756
|
},
|
|
4207
4757
|
status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession)),
|
|
4208
4758
|
});
|
|
@@ -4211,10 +4761,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4211
4761
|
if (workflow.emits.length > 0) sections.push(`Emitted:\n${workflow.emits.map(formatWorkflowValue).join("\n")}`);
|
|
4212
4762
|
if (workflow.console.length > 0) sections.push(`Console:\n${workflow.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
|
|
4213
4763
|
if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
|
|
4214
|
-
return {
|
|
4764
|
+
return attachWorkflowMission({
|
|
4215
4765
|
content: [{ type: "text", text: sections.join("\n\n") }],
|
|
4216
4766
|
details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflow.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console }, chatProgress }),
|
|
4217
|
-
};
|
|
4767
|
+
});
|
|
4218
4768
|
} catch (error) {
|
|
4219
4769
|
const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
|
|
4220
4770
|
const text = error instanceof Error ? error.message : String(error);
|
|
@@ -4223,11 +4773,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4223
4773
|
if (partial.emits.length > 0) sections.push(`Emitted:\n${partial.emits.map(formatWorkflowValue).join("\n")}`);
|
|
4224
4774
|
if (partial.console.length > 0) sections.push(`Console:\n${partial.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
|
|
4225
4775
|
if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
|
|
4226
|
-
return {
|
|
4776
|
+
return attachWorkflowMission({
|
|
4227
4777
|
content: [{ type: "text", text: sections.join("\n\n") }],
|
|
4228
4778
|
isError: true,
|
|
4229
4779
|
details: compactOptional<Details>({ mode: "workflow", runId: _id, results: partial.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: partial.trace, emits: partial.emits, console: partial.console }, chatProgress }),
|
|
4230
|
-
};
|
|
4780
|
+
});
|
|
4231
4781
|
}
|
|
4232
4782
|
}
|
|
4233
4783
|
const directParams = prepareWorkflowChildParams(requestParams);
|
|
@@ -4244,7 +4794,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4244
4794
|
? normalizeParentModel(ctx.model)
|
|
4245
4795
|
: rememberParentModel(deps.state, requestSessionId, ctx.model);
|
|
4246
4796
|
} catch (error) {
|
|
4247
|
-
if (action?.toLowerCase() !== "doctor") throw error;
|
|
4797
|
+
if (action?.toLowerCase() !== "doctor" && action?.toLowerCase() !== "guide") throw error;
|
|
4248
4798
|
requestParentModel = normalizeParentModel(ctx.model);
|
|
4249
4799
|
}
|
|
4250
4800
|
if (action) {
|
|
@@ -4331,6 +4881,29 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4331
4881
|
}
|
|
4332
4882
|
return handleWatchdogToolAction(action, paramsWithResolvedCwd, ctx, deps.watchdog);
|
|
4333
4883
|
}
|
|
4884
|
+
if (action === "refine" || action === "refine.show" || action === "refine.rollback") {
|
|
4885
|
+
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
4886
|
+
return {
|
|
4887
|
+
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
4888
|
+
isError: true,
|
|
4889
|
+
details: { mode: "management", results: [] },
|
|
4890
|
+
};
|
|
4891
|
+
}
|
|
4892
|
+
return handleRefinementAction(action, paramsWithResolvedCwd, {
|
|
4893
|
+
cwd: requestCwd,
|
|
4894
|
+
state: deps.state,
|
|
4895
|
+
signal,
|
|
4896
|
+
launchProposalChild: (task, outputSchema, proposalSignal) => execute(randomUUID(), {
|
|
4897
|
+
agent: "reviewer",
|
|
4898
|
+
task,
|
|
4899
|
+
context: "fresh",
|
|
4900
|
+
async: false,
|
|
4901
|
+
artifacts: false,
|
|
4902
|
+
outputSchema,
|
|
4903
|
+
toolBudget: { hard: 1, block: ["write", "edit", "bash"] },
|
|
4904
|
+
}, proposalSignal, undefined, ctx, true),
|
|
4905
|
+
});
|
|
4906
|
+
}
|
|
4334
4907
|
if (action === "grant-spawn-budget") {
|
|
4335
4908
|
if (deps.allowMutatingManagementActions === false || !ctx.hasUI) {
|
|
4336
4909
|
return {
|
|
@@ -4403,6 +4976,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4403
4976
|
details: { mode: "management", results: [], spawnBudget: granted.snapshot },
|
|
4404
4977
|
};
|
|
4405
4978
|
}
|
|
4979
|
+
if (action === "guide") {
|
|
4980
|
+
try {
|
|
4981
|
+
return {
|
|
4982
|
+
content: [{ type: "text", text: readSubagentGuide(paramsWithResolvedCwd.topic) }],
|
|
4983
|
+
details: { mode: "management", results: [] },
|
|
4984
|
+
};
|
|
4985
|
+
} catch (error) {
|
|
4986
|
+
return {
|
|
4987
|
+
content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
|
|
4988
|
+
isError: true,
|
|
4989
|
+
details: { mode: "management", results: [] },
|
|
4990
|
+
};
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4993
|
+
if (action === "children.list") {
|
|
4994
|
+
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
4995
|
+
const children = listRetainedChildren(DIRS.async, deps.state.currentSessionId);
|
|
4996
|
+
return {
|
|
4997
|
+
content: [{ type: "text", text: formatRetainedChildren(children) }],
|
|
4998
|
+
details: { mode: "management", results: [] },
|
|
4999
|
+
};
|
|
5000
|
+
}
|
|
4406
5001
|
if (action === "doctor") {
|
|
4407
5002
|
let currentSessionFile: string | null = null;
|
|
4408
5003
|
let currentSessionId = deps.state.currentSessionId;
|
|
@@ -4441,13 +5036,18 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4441
5036
|
}
|
|
4442
5037
|
if (action === "status") {
|
|
4443
5038
|
if (!preserveActiveSession) deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
4444
|
-
const withBudget = (result: AgentToolResult<Details>) => withSpawnBudgetStatus(
|
|
4445
|
-
result,
|
|
4446
|
-
deps.state,
|
|
4447
|
-
deps.config,
|
|
4448
|
-
deps.state.currentSessionId,
|
|
4449
|
-
);
|
|
4450
5039
|
const targetRunId = paramsWithResolvedCwd.id ?? paramsWithResolvedCwd.runId;
|
|
5040
|
+
const hasDirectoryTarget = Boolean(paramsWithResolvedCwd.dir);
|
|
5041
|
+
const targetLabel = formatStatusTargetLabel(paramsWithResolvedCwd, targetRunId);
|
|
5042
|
+
const withBudget = (result: AgentToolResult<Details>) => {
|
|
5043
|
+
const budgeted = withSpawnBudgetStatus(result, deps.state, deps.config, deps.state.currentSessionId);
|
|
5044
|
+
return {
|
|
5045
|
+
...budgeted,
|
|
5046
|
+
content: budgeted.content.map((item, index) => index === 0 && item.type === "text"
|
|
5047
|
+
? { ...item, text: `${targetLabel}\n${item.text}` }
|
|
5048
|
+
: item),
|
|
5049
|
+
};
|
|
5050
|
+
};
|
|
4451
5051
|
const nestedScope = nestedResolutionScopeForExecutor(deps);
|
|
4452
5052
|
const sessionRoots = trustedSessionRootsForStatus(ctx, deps);
|
|
4453
5053
|
if (paramsWithResolvedCwd.view === "fleet") {
|
|
@@ -4472,7 +5072,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4472
5072
|
const message = error instanceof Error ? error.message : String(error);
|
|
4473
5073
|
return withBudget({ content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } });
|
|
4474
5074
|
}
|
|
4475
|
-
} else {
|
|
5075
|
+
} else if (!hasDirectoryTarget) {
|
|
4476
5076
|
const foreground = getForegroundControl(deps.state, undefined);
|
|
4477
5077
|
if (foreground && paramsWithResolvedCwd.view !== "transcript") return withBudget(foregroundStatusResult(foreground));
|
|
4478
5078
|
if (foreground && paramsWithResolvedCwd.view === "transcript") {
|
|
@@ -4540,7 +5140,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4540
5140
|
}
|
|
4541
5141
|
}
|
|
4542
5142
|
if (action === "resume") {
|
|
4543
|
-
return resumeAsyncRun(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel }));
|
|
5143
|
+
return resumeAsyncRun(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel, signal }));
|
|
4544
5144
|
}
|
|
4545
5145
|
if (action === "steer") {
|
|
4546
5146
|
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
@@ -4551,6 +5151,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4551
5151
|
try {
|
|
4552
5152
|
const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
|
|
4553
5153
|
const runId = location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir);
|
|
5154
|
+
const directoryStatus = location.asyncDir ? readStatus(location.asyncDir) : null;
|
|
5155
|
+
if (directoryStatus?.mode === "workflow") {
|
|
5156
|
+
const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: directoryStatus.runId || runId, asyncDirRoot: DIRS.async });
|
|
5157
|
+
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
5158
|
+
return steerWorkflowForegroundTarget({ target: route.target, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal });
|
|
5159
|
+
}
|
|
4554
5160
|
if (location.asyncDir) {
|
|
4555
5161
|
const unsupported = externalRunnerControlError(location.asyncDir, "steer");
|
|
4556
5162
|
if (unsupported) return unsupported;
|
|
@@ -4559,6 +5165,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4559
5165
|
state: deps.state,
|
|
4560
5166
|
runId,
|
|
4561
5167
|
message,
|
|
5168
|
+
mode: paramsWithResolvedCwd.mode,
|
|
4562
5169
|
index: paramsWithResolvedCwd.index,
|
|
4563
5170
|
kill: deps.kill,
|
|
4564
5171
|
location,
|
|
@@ -4584,9 +5191,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4584
5191
|
const text = error instanceof Error ? error.message : String(error);
|
|
4585
5192
|
return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
|
|
4586
5193
|
}
|
|
4587
|
-
if (resolved?.kind === "nested") return steerNestedRun(omitUndefinedProperties({ target: resolved, message, index: paramsWithResolvedCwd.index, signal }));
|
|
4588
|
-
if (resolved?.kind === "foreground")
|
|
5194
|
+
if (resolved?.kind === "nested") return steerNestedRun(omitUndefinedProperties({ target: resolved, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal }));
|
|
5195
|
+
if (resolved?.kind === "foreground") {
|
|
5196
|
+
const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, childRunId: resolved.id, asyncDirRoot: DIRS.async });
|
|
5197
|
+
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
5198
|
+
return steerWorkflowForegroundTarget({ target: route.target, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal });
|
|
5199
|
+
}
|
|
4589
5200
|
if (resolved?.kind !== "async") return { content: [{ type: "text", text: `No async run found for '${targetRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5201
|
+
const resolvedStatus = resolved.location.asyncDir ? readStatus(resolved.location.asyncDir) : null;
|
|
5202
|
+
if (resolvedStatus?.mode === "workflow") {
|
|
5203
|
+
const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: resolvedStatus.runId || resolved.id, asyncDirRoot: DIRS.async });
|
|
5204
|
+
if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
|
|
5205
|
+
return steerWorkflowForegroundTarget({ target: route.target, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal });
|
|
5206
|
+
}
|
|
4590
5207
|
if (resolved.location.asyncDir) {
|
|
4591
5208
|
const unsupported = externalRunnerControlError(resolved.location.asyncDir, "steer");
|
|
4592
5209
|
if (unsupported) return unsupported;
|
|
@@ -4595,6 +5212,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4595
5212
|
state: deps.state,
|
|
4596
5213
|
runId: resolved.id,
|
|
4597
5214
|
message,
|
|
5215
|
+
mode: paramsWithResolvedCwd.mode,
|
|
4598
5216
|
index: paramsWithResolvedCwd.index,
|
|
4599
5217
|
kill: deps.kill,
|
|
4600
5218
|
location: resolved.location,
|
|
@@ -4619,6 +5237,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4619
5237
|
return appendStepToAsyncChain(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel }));
|
|
4620
5238
|
}
|
|
4621
5239
|
if (action.startsWith("schedule.")) {
|
|
5240
|
+
if (!isScheduledRunAction(action)) {
|
|
5241
|
+
return { content: [{ type: "text", text: unknownSubagentActionMessage(action) }], isError: true, details: { mode: "management", results: [] } };
|
|
5242
|
+
}
|
|
4622
5243
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
4623
5244
|
return {
|
|
4624
5245
|
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
@@ -4635,6 +5256,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4635
5256
|
}
|
|
4636
5257
|
return deps.handleScheduledRunAction(paramsWithResolvedCwd, ctx);
|
|
4637
5258
|
}
|
|
5259
|
+
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
5260
|
+
return {
|
|
5261
|
+
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
5262
|
+
isError: true,
|
|
5263
|
+
details: { mode: "management" as const, results: [] },
|
|
5264
|
+
};
|
|
5265
|
+
}
|
|
5266
|
+
if (action === "dismiss") {
|
|
5267
|
+
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
5268
|
+
if (!targetRunId) return { content: [{ type: "text", text: "action='dismiss' requires id." }], isError: true, details: { mode: "management", results: [] } };
|
|
5269
|
+
let resolved: ResolvedSubagentRunId | undefined;
|
|
5270
|
+
try {
|
|
5271
|
+
resolved = resolveSubagentRunId(targetRunId, omitUndefinedProperties({ state: deps.state, nested: nestedResolutionScopeForExecutor(deps) }));
|
|
5272
|
+
} catch (error) {
|
|
5273
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5274
|
+
return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
|
|
5275
|
+
}
|
|
5276
|
+
if (resolved?.kind !== "async") {
|
|
5277
|
+
return { content: [{ type: "text", text: `Run '${targetRunId}' is not a recovered workflow.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5278
|
+
}
|
|
5279
|
+
return dismissRecoveredWorkflow(deps.state, resolved.location);
|
|
5280
|
+
}
|
|
4638
5281
|
if (action === "stop") {
|
|
4639
5282
|
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
4640
5283
|
const workflowController = targetRunId ? deps.state.workflowControllers?.get(targetRunId) : undefined;
|
|
@@ -4646,6 +5289,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4646
5289
|
if (paramsWithResolvedCwd.dir) {
|
|
4647
5290
|
try {
|
|
4648
5291
|
const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
|
|
5292
|
+
const existingStatus = readStatus(location.asyncDir ?? "");
|
|
5293
|
+
if (existingStatus?.mode === "workflow" && existingStatus.state === "running") {
|
|
5294
|
+
return { content: [{ type: "text", text: `Workflow ${existingStatus.runId} is not controlled by this extension runtime; reload recovery cannot stop it safely.` }], isError: true, details: { mode: "management", results: [] } };
|
|
5295
|
+
}
|
|
4649
5296
|
const stopResult = stopAsyncRun(deps.state, location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir), deps.kill, location);
|
|
4650
5297
|
return stopResult ?? { content: [{ type: "text", text: `No running or queued async run was found for '${targetRunId ?? paramsWithResolvedCwd.dir}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
4651
5298
|
} catch (error) {
|
|
@@ -4725,14 +5372,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4725
5372
|
}
|
|
4726
5373
|
if (!(SUBAGENT_ACTIONS as readonly string[]).includes(action)) {
|
|
4727
5374
|
return {
|
|
4728
|
-
content: [{ type: "text", text:
|
|
4729
|
-
isError: true,
|
|
4730
|
-
details: { mode: "management" as const, results: [] },
|
|
4731
|
-
};
|
|
4732
|
-
}
|
|
4733
|
-
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
4734
|
-
return {
|
|
4735
|
-
content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
|
|
5375
|
+
content: [{ type: "text", text: unknownSubagentActionMessage(action) }],
|
|
4736
5376
|
isError: true,
|
|
4737
5377
|
details: { mode: "management" as const, results: [] },
|
|
4738
5378
|
};
|
|
@@ -4889,9 +5529,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4889
5529
|
if (externalAgent && (!effectiveAsync || effectiveParams.foregroundOnly === true)) {
|
|
4890
5530
|
return buildRequestedModeError(effectiveParams, `Agent '${externalAgent.name}' uses runner.type='external-cli', which currently supports async/background execution only. Omit async or pass async:true; clarify and foregroundOnly are unsupported.`);
|
|
4891
5531
|
}
|
|
4892
|
-
const foregroundTimeout =
|
|
5532
|
+
const foregroundTimeout = resolveSingleAgentLaunchTimeout(
|
|
4893
5533
|
effectiveParams,
|
|
4894
|
-
effectiveAsync
|
|
5534
|
+
effectiveAsync,
|
|
4895
5535
|
);
|
|
4896
5536
|
if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
|
|
4897
5537
|
const controlConfig = resolveControlConfig(deps.config.control, effectiveParams.control);
|
|
@@ -4902,6 +5542,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4902
5542
|
dir: deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir,
|
|
4903
5543
|
});
|
|
4904
5544
|
const artifactsDir = getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
5545
|
+
if (artifactConfig.dir === "project" && !warnedArtifactPackageDirs.has(effectiveCwd)) {
|
|
5546
|
+
warnedArtifactPackageDirs.add(effectiveCwd);
|
|
5547
|
+
const warning = getProjectArtifactPackagingWarning(effectiveCwd);
|
|
5548
|
+
if (warning) console.warn(`[pi-subagents] ${warning}`);
|
|
5549
|
+
}
|
|
4905
5550
|
|
|
4906
5551
|
let sessionRoot: string;
|
|
4907
5552
|
if (effectiveParams.sessionDir) {
|
|
@@ -5036,12 +5681,26 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5036
5681
|
const foregroundDescription = effectiveParams.task?.trim()
|
|
5037
5682
|
|| effectiveParams.tasks?.[0]?.task?.trim()
|
|
5038
5683
|
|| (effectiveParams.chain ? firstRawChainTask(effectiveParams.chain)?.trim() : undefined);
|
|
5684
|
+
const parentWorkflowStatus = effectiveParams.workflowParentRunId
|
|
5685
|
+
? readStatus(path.join(DIRS.async, effectiveParams.workflowParentRunId))
|
|
5686
|
+
: null;
|
|
5687
|
+
const workflowSteeringDir = effectiveParams.workflowParentRunId
|
|
5688
|
+
&& requestSessionId
|
|
5689
|
+
&& deps.state.workflowControllers?.has(effectiveParams.workflowParentRunId)
|
|
5690
|
+
&& parentWorkflowStatus?.mode === "workflow"
|
|
5691
|
+
&& (parentWorkflowStatus.state === "running" || parentWorkflowStatus.state === "queued")
|
|
5692
|
+
&& parentWorkflowStatus.sessionId === requestSessionId
|
|
5693
|
+
? workflowForegroundSteeringDir(DIRS.async, effectiveParams.workflowParentRunId, runId)
|
|
5694
|
+
: undefined;
|
|
5039
5695
|
const foregroundControl: ForegroundRunControl | undefined = effectiveAsync
|
|
5040
5696
|
? undefined
|
|
5041
5697
|
: compactOptional<ForegroundRunControl>({
|
|
5042
5698
|
runId,
|
|
5043
5699
|
sessionId: requestSessionId,
|
|
5044
5700
|
mode: foregroundMode,
|
|
5701
|
+
...(effectiveParams.workflowParentRunId ? { parentWorkflowRunId: effectiveParams.workflowParentRunId } : {}),
|
|
5702
|
+
...(effectiveParams.workflowKey ? { workflowKey: effectiveParams.workflowKey } : {}),
|
|
5703
|
+
workflowSteeringDir,
|
|
5045
5704
|
startedAt: Date.now(),
|
|
5046
5705
|
updatedAt: Date.now(),
|
|
5047
5706
|
cwd: effectiveCwd,
|
|
@@ -5147,6 +5806,18 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5147
5806
|
|
|
5148
5807
|
let nestedForegroundStarted = false;
|
|
5149
5808
|
try {
|
|
5809
|
+
if (workflowLaunchObserver) {
|
|
5810
|
+
const singleTask = hasTasks && effectiveParams.tasks?.length === 1 ? effectiveParams.tasks[0] : undefined;
|
|
5811
|
+
const launch = hasSingle
|
|
5812
|
+
? { agent: effectiveParams.agent!, sessionFile: childSessionFileForTask(effectiveParams.agent!, 0, effectiveParams.model) }
|
|
5813
|
+
: singleTask
|
|
5814
|
+
? { agent: singleTask.agent, sessionFile: childSessionFileForTask(singleTask.agent, 0, singleTask.model) }
|
|
5815
|
+
: undefined;
|
|
5816
|
+
if (launch) {
|
|
5817
|
+
workflowLaunchObservers.delete(params);
|
|
5818
|
+
workflowLaunchObserver(launch);
|
|
5819
|
+
}
|
|
5820
|
+
}
|
|
5150
5821
|
const asyncResult = runAsyncPath(execData, deps);
|
|
5151
5822
|
if (asyncResult) return attachMission(withResolvedContext(withForkThinkingNotes(asyncResult, forkThinkingDowngrades), contextPolicy.contextSummary));
|
|
5152
5823
|
if (foregroundControl) {
|
|
@@ -5193,8 +5864,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5193
5864
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
5194
5865
|
ctx: ExtensionContext,
|
|
5195
5866
|
): Promise<AgentToolResult<Details>> => {
|
|
5196
|
-
const
|
|
5197
|
-
|
|
5867
|
+
const normalizedAction = typeof params.action === "string" ? params.action.trim() : params.action;
|
|
5868
|
+
const requestParams = normalizedAction ? { ...params, action: normalizedAction } : params;
|
|
5869
|
+
if (normalizedAction) return execute(id, requestParams, signal, onUpdate, ctx);
|
|
5198
5870
|
const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth);
|
|
5199
5871
|
const dispatchParams = applyForceTopLevelAsyncOverride(requestParams, depth, deps.config.forceTopLevelAsync === true);
|
|
5200
5872
|
const runsForeground = dispatchParams.clarify === true || (dispatchParams.async ?? deps.asyncByDefault) !== true;
|
|
@@ -5208,6 +5880,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5208
5880
|
}
|
|
5209
5881
|
};
|
|
5210
5882
|
|
|
5883
|
+
const executePublic = (
|
|
5884
|
+
id: string,
|
|
5885
|
+
params: SubagentParamsLike,
|
|
5886
|
+
signal: AbortSignal,
|
|
5887
|
+
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
5888
|
+
ctx: ExtensionContext,
|
|
5889
|
+
): Promise<AgentToolResult<Details>> => {
|
|
5890
|
+
const normalized = normalizePublicSubagentExecution(params);
|
|
5891
|
+
if (!normalized.ok) {
|
|
5892
|
+
return Promise.resolve({ content: [{ type: "text", text: normalized.error }], isError: true, details: { mode: normalized.mode, results: [] } });
|
|
5893
|
+
}
|
|
5894
|
+
return executeWithSingleDispatchGuard(id, normalized.params, signal, onUpdate, ctx);
|
|
5895
|
+
};
|
|
5896
|
+
|
|
5211
5897
|
const executeDelegated = async (
|
|
5212
5898
|
id: string,
|
|
5213
5899
|
params: SubagentParamsLike,
|
|
@@ -5247,5 +5933,5 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5247
5933
|
return ownerExecutor.execute(id, params, signal, undefined, ctx);
|
|
5248
5934
|
};
|
|
5249
5935
|
|
|
5250
|
-
return { execute: executeWithSingleDispatchGuard, executeDelegated, executeScheduled };
|
|
5936
|
+
return { execute: executeWithSingleDispatchGuard, executePublic, executeDelegated, executeScheduled };
|
|
5251
5937
|
}
|