@selesai/code 0.6.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -1
- package/README.md +5 -3
- package/dist/bun/cli.d.ts +0 -1
- package/dist/bun/cli.js +0 -1
- package/dist/bun/register-bedrock.d.ts +0 -1
- package/dist/bun/register-bedrock.js +0 -1
- package/dist/bun/restore-sandbox-env.d.ts +0 -1
- package/dist/bun/restore-sandbox-env.js +0 -1
- package/dist/cli/args.d.ts +0 -1
- package/dist/cli/args.js +0 -1
- package/dist/cli/config-selector.d.ts +0 -1
- package/dist/cli/config-selector.js +0 -1
- package/dist/cli/credential-print.d.ts +0 -1
- package/dist/cli/credential-print.js +0 -1
- package/dist/cli/file-processor.d.ts +0 -1
- package/dist/cli/file-processor.js +0 -1
- package/dist/cli/initial-message.d.ts +0 -1
- package/dist/cli/initial-message.js +0 -1
- package/dist/cli/list-models.d.ts +0 -1
- package/dist/cli/list-models.js +0 -1
- package/dist/cli/project-trust.d.ts +0 -1
- package/dist/cli/project-trust.js +0 -1
- package/dist/cli/session-picker.d.ts +0 -1
- package/dist/cli/session-picker.js +0 -1
- package/dist/cli/startup-ui.d.ts +0 -1
- package/dist/cli/startup-ui.js +0 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -1
- package/dist/config.d.ts +0 -1
- package/dist/config.js +0 -1
- package/dist/core/agent-session-auto-handoff.test.d.ts +0 -1
- package/dist/core/agent-session-auto-handoff.test.js +0 -1
- package/dist/core/agent-session-runtime.d.ts +0 -1
- package/dist/core/agent-session-runtime.js +0 -1
- package/dist/core/agent-session-services.d.ts +0 -1
- package/dist/core/agent-session-services.js +0 -1
- package/dist/core/agent-session-skill-block.test.d.ts +0 -1
- package/dist/core/agent-session-skill-block.test.js +0 -1
- package/dist/core/agent-session.d.ts +0 -1
- package/dist/core/agent-session.js +0 -1
- package/dist/core/agents.d.ts +0 -1
- package/dist/core/agents.js +0 -1
- package/dist/core/auth-guidance.d.ts +0 -1
- package/dist/core/auth-guidance.js +0 -1
- package/dist/core/auth-storage.d.ts +0 -1
- package/dist/core/auth-storage.js +0 -1
- package/dist/core/bash-executor.d.ts +0 -1
- package/dist/core/bash-executor.js +0 -1
- package/dist/core/built-in-extensions.d.ts +0 -1
- package/dist/core/built-in-extensions.js +0 -1
- package/dist/core/cache-stats.d.ts +0 -1
- package/dist/core/cache-stats.js +0 -1
- package/dist/core/compaction/branch-summarization.d.ts +0 -1
- package/dist/core/compaction/branch-summarization.js +0 -1
- package/dist/core/compaction/compaction.d.ts +0 -1
- package/dist/core/compaction/compaction.js +0 -1
- package/dist/core/compaction/index.d.ts +0 -1
- package/dist/core/compaction/index.js +0 -1
- package/dist/core/compaction/utils.d.ts +0 -1
- package/dist/core/compaction/utils.js +0 -1
- package/dist/core/defaults.d.ts +0 -1
- package/dist/core/defaults.js +0 -1
- package/dist/core/diagnostics.d.ts +0 -1
- package/dist/core/diagnostics.js +0 -1
- package/dist/core/event-bus.d.ts +0 -1
- package/dist/core/event-bus.js +0 -1
- package/dist/core/exec.d.ts +0 -1
- package/dist/core/exec.js +0 -1
- package/dist/core/experimental.d.ts +0 -1
- package/dist/core/experimental.js +0 -1
- package/dist/core/export-html/ansi-to-html.d.ts +0 -1
- package/dist/core/export-html/ansi-to-html.js +0 -1
- package/dist/core/export-html/index.d.ts +0 -1
- package/dist/core/export-html/index.js +0 -1
- package/dist/core/export-html/tool-renderer.d.ts +0 -1
- package/dist/core/export-html/tool-renderer.js +0 -1
- package/dist/core/extensions/index.d.ts +0 -1
- package/dist/core/extensions/index.js +0 -1
- package/dist/core/extensions/loader.d.ts +0 -1
- package/dist/core/extensions/loader.js +0 -1
- package/dist/core/extensions/runner.d.ts +0 -1
- package/dist/core/extensions/runner.js +0 -1
- package/dist/core/extensions/types.d.ts +0 -1
- package/dist/core/extensions/types.js +0 -1
- package/dist/core/extensions/wrapper.d.ts +0 -1
- package/dist/core/extensions/wrapper.js +0 -1
- package/dist/core/footer-data-provider.d.ts +0 -1
- package/dist/core/footer-data-provider.js +0 -1
- package/dist/core/git-command.d.ts +0 -1
- package/dist/core/git-command.js +0 -1
- package/dist/core/http-dispatcher.d.ts +0 -1
- package/dist/core/http-dispatcher.js +0 -1
- package/dist/core/index.d.ts +0 -1
- package/dist/core/index.js +0 -1
- package/dist/core/keybindings.d.ts +0 -1
- package/dist/core/keybindings.js +0 -1
- package/dist/core/llama/client.d.ts +0 -1
- package/dist/core/llama/client.js +0 -1
- package/dist/core/llama/huggingface.d.ts +0 -1
- package/dist/core/llama/huggingface.js +0 -1
- package/dist/core/llama/index.d.ts +0 -1
- package/dist/core/llama/index.js +0 -1
- package/dist/core/llama/provider.d.ts +0 -1
- package/dist/core/llama/provider.js +0 -1
- package/dist/core/llama/ui.d.ts +0 -1
- package/dist/core/llama/ui.js +0 -1
- package/dist/core/messages.d.ts +0 -1
- package/dist/core/messages.js +0 -1
- package/dist/core/model-config.d.ts +0 -1
- package/dist/core/model-config.js +0 -1
- package/dist/core/model-registry.d.ts +0 -1
- package/dist/core/model-registry.js +0 -1
- package/dist/core/model-resolver.d.ts +0 -1
- package/dist/core/model-resolver.js +0 -1
- package/dist/core/model-runtime.d.ts +0 -1
- package/dist/core/model-runtime.js +0 -1
- package/dist/core/models-store.d.ts +0 -1
- package/dist/core/models-store.js +0 -1
- package/dist/core/output-guard.d.ts +0 -1
- package/dist/core/output-guard.js +0 -1
- package/dist/core/package-manager.d.ts +0 -1
- package/dist/core/package-manager.js +0 -1
- package/dist/core/package-manager.test.d.ts +0 -1
- package/dist/core/package-manager.test.js +0 -1
- package/dist/core/project-trust.d.ts +0 -1
- package/dist/core/project-trust.js +0 -1
- package/dist/core/prompt-templates.d.ts +0 -1
- package/dist/core/prompt-templates.js +0 -1
- package/dist/core/provider-attribution.d.ts +0 -1
- package/dist/core/provider-attribution.js +0 -1
- package/dist/core/provider-composer.d.ts +0 -1
- package/dist/core/provider-composer.js +0 -1
- package/dist/core/radius.d.ts +0 -1
- package/dist/core/radius.js +0 -1
- package/dist/core/remote-catalog-provider.d.ts +0 -1
- package/dist/core/remote-catalog-provider.js +0 -1
- package/dist/core/resolve-config-value.d.ts +0 -1
- package/dist/core/resolve-config-value.js +0 -1
- package/dist/core/resource-loader.d.ts +0 -1
- package/dist/core/resource-loader.js +0 -1
- package/dist/core/runtime-credentials.d.ts +0 -1
- package/dist/core/runtime-credentials.js +0 -1
- package/dist/core/sdk.d.ts +0 -1
- package/dist/core/sdk.js +0 -1
- package/dist/core/session-cwd.d.ts +0 -1
- package/dist/core/session-cwd.js +0 -1
- package/dist/core/session-manager.d.ts +0 -1
- package/dist/core/session-manager.js +0 -1
- package/dist/core/settings-manager-auto-handoff.test.d.ts +0 -1
- package/dist/core/settings-manager-auto-handoff.test.js +0 -1
- package/dist/core/settings-manager.d.ts +0 -4
- package/dist/core/settings-manager.js +0 -12
- package/dist/core/skills.d.ts +0 -1
- package/dist/core/skills.js +0 -1
- package/dist/core/slash-commands.d.ts +0 -1
- package/dist/core/slash-commands.js +0 -1
- package/dist/core/source-info.d.ts +0 -1
- package/dist/core/source-info.js +0 -1
- package/dist/core/system-prompt.d.ts +0 -1
- package/dist/core/system-prompt.js +0 -1
- package/dist/core/system-prompt.test.d.ts +0 -1
- package/dist/core/system-prompt.test.js +0 -1
- package/dist/core/telemetry.d.ts +0 -1
- package/dist/core/telemetry.js +0 -1
- package/dist/core/timings.d.ts +0 -1
- package/dist/core/timings.js +0 -1
- package/dist/core/tools/bash.d.ts +0 -1
- package/dist/core/tools/bash.js +0 -1
- package/dist/core/tools/edit-diff.d.ts +0 -1
- package/dist/core/tools/edit-diff.js +0 -1
- package/dist/core/tools/edit.d.ts +0 -1
- package/dist/core/tools/edit.js +0 -1
- package/dist/core/tools/file-mutation-queue.d.ts +0 -1
- package/dist/core/tools/file-mutation-queue.js +0 -1
- package/dist/core/tools/find.d.ts +0 -1
- package/dist/core/tools/find.js +0 -1
- package/dist/core/tools/grep.d.ts +0 -1
- package/dist/core/tools/grep.js +0 -1
- package/dist/core/tools/index.d.ts +0 -1
- package/dist/core/tools/index.js +0 -1
- package/dist/core/tools/ls.d.ts +0 -1
- package/dist/core/tools/ls.js +0 -1
- package/dist/core/tools/output-accumulator.d.ts +0 -1
- package/dist/core/tools/output-accumulator.js +0 -1
- package/dist/core/tools/path-utils.d.ts +0 -1
- package/dist/core/tools/path-utils.js +0 -1
- package/dist/core/tools/read.d.ts +0 -1
- package/dist/core/tools/read.js +0 -1
- package/dist/core/tools/render-utils.d.ts +0 -1
- package/dist/core/tools/render-utils.js +0 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts +0 -1
- package/dist/core/tools/tool-definition-wrapper.js +0 -1
- package/dist/core/tools/truncate.d.ts +0 -1
- package/dist/core/tools/truncate.js +0 -1
- package/dist/core/tools/write.d.ts +0 -1
- package/dist/core/tools/write.js +0 -1
- package/dist/core/trust-manager.d.ts +0 -1
- package/dist/core/trust-manager.js +0 -1
- package/dist/core/usage-totals.d.ts +0 -1
- package/dist/core/usage-totals.js +0 -1
- package/dist/defaults/models.json +8 -0
- package/dist/defaults/settings.json +6 -7
- package/dist/extensions/agent-browser.test.ts +195 -0
- package/dist/extensions/agent-browser.ts +158 -0
- package/dist/extensions/inline-skills.test.ts +27 -13
- package/dist/extensions/inline-skills.ts +18 -11
- package/dist/extensions/package.json +2 -1
- package/dist/extensions/pi-powerline-footer/.github/workflows/test.yml +27 -0
- package/dist/extensions/pi-powerline-footer/CHANGELOG.md +153 -0
- package/dist/extensions/pi-powerline-footer/CONTRIBUTING.md +16 -0
- package/dist/extensions/pi-powerline-footer/README.md +192 -44
- package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +19 -4
- package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +80 -25
- package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +81 -29
- package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +1 -1
- package/dist/extensions/pi-powerline-footer/cd-command.ts +190 -0
- package/dist/extensions/pi-powerline-footer/colors.ts +2 -1
- package/dist/extensions/pi-powerline-footer/context-usage.ts +121 -6
- package/dist/extensions/pi-powerline-footer/currency-rates.ts +142 -0
- package/dist/extensions/pi-powerline-footer/editor-composition.ts +32 -0
- package/dist/extensions/pi-powerline-footer/git-status.ts +108 -6
- package/dist/extensions/pi-powerline-footer/icons.ts +11 -2
- package/dist/extensions/pi-powerline-footer/index.ts +1056 -324
- package/dist/extensions/pi-powerline-footer/lifecycle.ts +10 -0
- package/dist/extensions/pi-powerline-footer/package.json +10 -6
- package/dist/extensions/pi-powerline-footer/paths.ts +22 -0
- package/dist/extensions/pi-powerline-footer/powerline-config.ts +297 -15
- package/dist/extensions/pi-powerline-footer/presets.ts +6 -25
- package/dist/extensions/pi-powerline-footer/queue/store.ts +361 -0
- package/dist/extensions/pi-powerline-footer/queue/types.ts +54 -0
- package/dist/extensions/pi-powerline-footer/render-scheduler.ts +12 -7
- package/dist/extensions/pi-powerline-footer/segments.ts +114 -107
- package/dist/extensions/pi-powerline-footer/session-usage.ts +1 -1
- package/dist/extensions/pi-powerline-footer/shortcuts.ts +17 -3
- package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +189 -17
- package/dist/extensions/pi-powerline-footer/tests/cd-command.test.ts +144 -0
- package/dist/extensions/pi-powerline-footer/tests/context-usage.test.ts +120 -2
- package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +245 -22
- package/dist/extensions/pi-powerline-footer/tests/editor-composition.test.ts +48 -0
- package/dist/extensions/pi-powerline-footer/tests/editor-responsiveness.test.ts +34 -0
- package/dist/extensions/pi-powerline-footer/tests/fixed-editor-border.test.ts +39 -0
- package/dist/extensions/pi-powerline-footer/tests/git-status.test.ts +92 -0
- package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +4 -1
- package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +33 -197
- package/dist/extensions/pi-powerline-footer/tests/mode-aware-trigger.test.ts +79 -0
- package/dist/extensions/pi-powerline-footer/tests/paths.test.ts +63 -0
- package/dist/extensions/pi-powerline-footer/tests/queue-store.test.ts +195 -0
- package/dist/extensions/pi-powerline-footer/tests/quit-cursor.test.ts +14 -0
- package/dist/extensions/pi-powerline-footer/tests/remaining-regressions.test.ts +167 -0
- package/dist/extensions/pi-powerline-footer/tests/stash-shortcut.test.ts +23 -18
- package/dist/extensions/pi-powerline-footer/tests/thinking-segment.test.ts +16 -1
- package/dist/extensions/pi-powerline-footer/tests/token-stats.test.ts +249 -0
- package/dist/extensions/pi-powerline-footer/tests/usage-display.test.ts +237 -0
- package/dist/extensions/pi-powerline-footer/tests/welcome.test.ts +199 -0
- package/dist/extensions/pi-powerline-footer/tests/working-vibes.test.ts +191 -100
- package/dist/extensions/pi-powerline-footer/theme.ts +32 -18
- package/dist/extensions/pi-powerline-footer/token-stats.ts +304 -0
- package/dist/extensions/pi-powerline-footer/tsconfig.json +13 -0
- package/dist/extensions/pi-powerline-footer/types.ts +74 -29
- package/dist/extensions/pi-powerline-footer/welcome-dismiss.ts +34 -0
- package/dist/extensions/pi-powerline-footer/welcome.ts +65 -10
- package/dist/extensions/pi-powerline-footer/working-vibes.ts +44 -11
- package/dist/extensions/pi-subagents/CHANGELOG.md +89 -12
- package/dist/extensions/pi-subagents/README.md +339 -604
- package/dist/extensions/pi-subagents/package-lock.json +190 -2079
- package/dist/extensions/pi-subagents/package.json +13 -5
- package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +1 -1
- package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +1 -1
- package/dist/extensions/pi-subagents/prompts/review-loop.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +10 -10
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +32 -43
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +106 -105
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +3 -3
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +28 -35
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +167 -124
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +19 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +163 -75
- package/dist/extensions/pi-subagents/src/agents/chain-serializer.ts +10 -7
- package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +5 -3
- package/dist/extensions/pi-subagents/src/agents/identity.ts +1 -1
- package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +14 -11
- package/dist/extensions/pi-subagents/src/agents/skills.ts +23 -6
- package/dist/extensions/pi-subagents/src/agents/task-aware-routing.ts +8 -1
- package/dist/extensions/pi-subagents/src/api/control-channel.ts +4 -0
- package/dist/extensions/pi-subagents/src/api/delegation.ts +26 -194
- package/dist/extensions/pi-subagents/src/api/external-runs.ts +129 -0
- package/dist/extensions/pi-subagents/src/api/intercom-bridge.ts +3 -0
- package/dist/extensions/pi-subagents/src/api/pi-args.ts +5 -0
- package/dist/extensions/pi-subagents/src/api/preflight.ts +4 -4
- package/dist/extensions/pi-subagents/src/api/shared-types.ts +19 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +10 -0
- package/dist/extensions/pi-subagents/src/extension/control-notices.ts +5 -39
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +10 -9
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +7 -4
- package/dist/extensions/pi-subagents/src/extension/index.ts +243 -77
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +18 -6
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +50 -39
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +36 -103
- package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +229 -0
- package/dist/extensions/pi-subagents/src/inspectors/herdr/client.ts +130 -0
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +141 -0
- package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +154 -0
- package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +330 -0
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +3 -2
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +5 -1
- package/dist/extensions/pi-subagents/src/missions/actions.ts +372 -0
- package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +314 -0
- package/dist/extensions/pi-subagents/src/missions/store.ts +442 -0
- package/dist/extensions/pi-subagents/src/missions/types.ts +135 -0
- package/dist/extensions/pi-subagents/src/policy/authority.ts +46 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +32 -6
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +103 -72
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +10 -2
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +6 -6
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +29 -1
- package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +3 -2
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +9 -7
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +3 -4
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +3 -28
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +5 -5
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +18 -68
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +35 -8
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +602 -375
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +612 -464
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +50 -9
- package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +253 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +12 -4
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +8 -4
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +74 -102
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +21 -22
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1050 -396
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +44 -18
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +0 -1
- package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +302 -22
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +130 -0
- package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +42 -1
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +59 -5
- package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +9 -4
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +86 -2
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +11 -2
- package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +95 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +11 -1
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +15 -6
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +9 -63
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +37 -6
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +5 -2
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +2 -23
- package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +6 -6
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +124 -13
- package/dist/extensions/pi-subagents/src/shared/accessible-dir.ts +29 -7
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +18 -1
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +3 -2
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +1 -0
- package/dist/extensions/pi-subagents/src/shared/settings.ts +10 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +158 -55
- package/dist/extensions/pi-subagents/src/shared/utils.ts +16 -66
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +33 -199
- package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +43 -126
- package/dist/extensions/pi-subagents/src/slash/inline-subagents.ts +136 -0
- package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +158 -205
- package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +22 -58
- package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +14 -0
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +44 -642
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +18 -14
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +156 -21
- package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +110 -5
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +60 -24
- package/dist/extensions/pi-subagents/src/tui/render.ts +296 -140
- package/dist/extensions/pi-subagents/src/types/pi-runtime-compat.d.ts +14 -0
- package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +1 -1
- package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +13 -7
- package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +2 -2
- package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +145 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +1 -1
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +1 -1
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +4 -1
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +3 -2
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +140 -0
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +415 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +41 -144
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +16 -16
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +415 -298
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +149 -82
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +40 -40
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +15 -15
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +193 -133
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +1 -49
- package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +62 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +56 -58
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +81 -68
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +20 -25
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +425 -317
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +73 -73
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +123 -98
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +653 -240
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +20 -1661
- 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 +3 -3
- package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +8 -5
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +6 -6
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +4 -20
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +25 -4
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +280 -295
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +47 -207
- package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +26 -26
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +89 -89
- package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +10 -0
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +33 -9
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +24 -2
- package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +11 -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 +40 -40
- package/dist/extensions/pi-subagents/test/unit/authority-policy.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +5 -39
- 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-protocol.test.ts +99 -0
- package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +41 -0
- package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +35 -5
- package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +7 -0
- package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +16 -82
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +162 -620
- 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/ensure-accessible-dir.test.ts +125 -0
- package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +94 -0
- package/dist/extensions/pi-subagents/test/unit/external-runs.test.ts +66 -0
- package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +242 -52
- package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +127 -0
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +197 -36
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +4 -4
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +245 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +494 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +261 -31
- package/dist/extensions/pi-subagents/test/unit/inline-subagents.test.ts +127 -0
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +6 -6
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +217 -0
- package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +182 -0
- package/dist/extensions/pi-subagents/test/unit/mock-pi.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +10 -10
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +20 -12
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +47 -5
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +49 -55
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +20 -3
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +37 -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/permissions.test.ts +46 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +31 -10
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +40 -16
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +42 -44
- 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 +75 -9
- package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +20 -84
- package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +15 -16
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +28 -28
- package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +19 -12
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +37 -21
- 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 +50 -50
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +396 -433
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +76 -153
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +411 -0
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +1 -91
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +139 -0
- package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +29 -0
- 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 +2 -2
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +35 -35
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +53 -18
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +13 -1
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +44 -11
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +2 -26
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +24 -148
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +11 -9
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +351 -0
- 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-permission-arbiter.test.ts +79 -0
- 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 +45 -9
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +197 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +8 -8
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +142 -12
- package/dist/extensions/pi-subagents/tsconfig.json +15 -0
- package/dist/extensions/ponytail/index.js +1 -31
- package/dist/extensions/ponytail/test/extension.test.js +5 -22
- package/dist/extensions/rtk.test.ts +141 -0
- package/dist/extensions/rtk.ts +78 -36
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/main.d.ts +0 -1
- package/dist/main.js +0 -1
- package/dist/migrations.d.ts +0 -1
- package/dist/migrations.js +0 -1
- package/dist/modes/index.d.ts +0 -1
- package/dist/modes/index.js +0 -1
- package/dist/modes/interactive/components/armin.d.ts +0 -1
- package/dist/modes/interactive/components/armin.js +0 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +0 -1
- package/dist/modes/interactive/components/assistant-message.js +0 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +0 -1
- package/dist/modes/interactive/components/bash-execution.js +0 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts +0 -1
- package/dist/modes/interactive/components/bordered-loader.js +0 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts +0 -1
- package/dist/modes/interactive/components/branch-summary-message.js +0 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +0 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +0 -1
- package/dist/modes/interactive/components/config-selector.d.ts +0 -1
- package/dist/modes/interactive/components/config-selector.js +0 -1
- package/dist/modes/interactive/components/countdown-timer.d.ts +0 -1
- package/dist/modes/interactive/components/countdown-timer.js +0 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +0 -1
- package/dist/modes/interactive/components/custom-editor.js +0 -1
- package/dist/modes/interactive/components/custom-entry.d.ts +0 -1
- package/dist/modes/interactive/components/custom-entry.js +0 -1
- package/dist/modes/interactive/components/custom-message.d.ts +0 -1
- package/dist/modes/interactive/components/custom-message.js +0 -1
- package/dist/modes/interactive/components/daxnuts.d.ts +0 -1
- package/dist/modes/interactive/components/daxnuts.js +0 -1
- package/dist/modes/interactive/components/diff.d.ts +0 -1
- package/dist/modes/interactive/components/diff.js +0 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts +0 -1
- package/dist/modes/interactive/components/dynamic-border.js +0 -1
- package/dist/modes/interactive/components/earendil-announcement.d.ts +0 -1
- package/dist/modes/interactive/components/earendil-announcement.js +0 -1
- package/dist/modes/interactive/components/extension-editor.d.ts +0 -1
- package/dist/modes/interactive/components/extension-editor.js +0 -1
- package/dist/modes/interactive/components/extension-input.d.ts +0 -1
- package/dist/modes/interactive/components/extension-input.js +0 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +0 -1
- package/dist/modes/interactive/components/extension-selector.js +0 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts +0 -1
- package/dist/modes/interactive/components/first-time-setup.js +0 -1
- package/dist/modes/interactive/components/footer.d.ts +0 -1
- package/dist/modes/interactive/components/footer.js +0 -1
- package/dist/modes/interactive/components/index.d.ts +0 -1
- package/dist/modes/interactive/components/index.js +0 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +0 -1
- package/dist/modes/interactive/components/keybinding-hints.js +0 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +0 -1
- package/dist/modes/interactive/components/login-dialog.js +0 -1
- package/dist/modes/interactive/components/model-selector.d.ts +0 -1
- package/dist/modes/interactive/components/model-selector.js +0 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts +0 -1
- package/dist/modes/interactive/components/oauth-selector.js +0 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +0 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +0 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts +0 -1
- package/dist/modes/interactive/components/session-selector-search.js +0 -1
- package/dist/modes/interactive/components/session-selector.d.ts +0 -1
- package/dist/modes/interactive/components/session-selector.js +0 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +0 -3
- package/dist/modes/interactive/components/settings-selector.js +1 -14
- package/dist/modes/interactive/components/show-images-selector.d.ts +0 -1
- package/dist/modes/interactive/components/show-images-selector.js +0 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +0 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +0 -1
- package/dist/modes/interactive/components/status-indicator.d.ts +0 -1
- package/dist/modes/interactive/components/status-indicator.js +0 -1
- package/dist/modes/interactive/components/theme-selector.d.ts +0 -1
- package/dist/modes/interactive/components/theme-selector.js +0 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts +0 -1
- package/dist/modes/interactive/components/thinking-selector.js +0 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +0 -1
- package/dist/modes/interactive/components/tool-execution.js +0 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +0 -1
- package/dist/modes/interactive/components/tree-selector.js +0 -1
- package/dist/modes/interactive/components/trust-selector.d.ts +0 -1
- package/dist/modes/interactive/components/trust-selector.js +0 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts +0 -1
- package/dist/modes/interactive/components/user-message-selector.js +0 -1
- package/dist/modes/interactive/components/user-message.d.ts +0 -1
- package/dist/modes/interactive/components/user-message.js +0 -1
- package/dist/modes/interactive/components/visual-truncate.d.ts +0 -1
- package/dist/modes/interactive/components/visual-truncate.js +0 -1
- package/dist/modes/interactive/external-editor.d.ts +0 -1
- package/dist/modes/interactive/external-editor.js +0 -1
- package/dist/modes/interactive/interactive-mode.d.ts +0 -2
- package/dist/modes/interactive/interactive-mode.js +1 -22
- package/dist/modes/interactive/model-search.d.ts +0 -1
- package/dist/modes/interactive/model-search.js +0 -1
- package/dist/modes/interactive/theme/theme-controller.d.ts +0 -1
- package/dist/modes/interactive/theme/theme-controller.js +0 -1
- package/dist/modes/interactive/theme/theme.d.ts +0 -1
- package/dist/modes/interactive/theme/theme.js +0 -1
- package/dist/modes/print-mode.d.ts +0 -1
- package/dist/modes/print-mode.js +0 -1
- package/dist/modes/rpc/jsonl.d.ts +0 -1
- package/dist/modes/rpc/jsonl.js +0 -1
- package/dist/modes/rpc/rpc-client.d.ts +1 -2
- package/dist/modes/rpc/rpc-client.js +1 -2
- package/dist/modes/rpc/rpc-mode.d.ts +0 -1
- package/dist/modes/rpc/rpc-mode.js +0 -9
- package/dist/modes/rpc/rpc-types.d.ts +0 -1
- package/dist/modes/rpc/rpc-types.js +0 -1
- package/dist/package-manager-cli.d.ts +0 -1
- package/dist/package-manager-cli.js +0 -1
- package/dist/rpc-entry.d.ts +0 -1
- package/dist/rpc-entry.js +0 -1
- package/dist/utils/ansi.d.ts +0 -1
- package/dist/utils/ansi.js +0 -1
- package/dist/utils/changelog.d.ts +0 -1
- package/dist/utils/changelog.js +0 -1
- package/dist/utils/child-process.d.ts +0 -1
- package/dist/utils/child-process.js +0 -1
- package/dist/utils/clipboard-image.d.ts +0 -1
- package/dist/utils/clipboard-image.js +0 -1
- package/dist/utils/clipboard-native.d.ts +0 -1
- package/dist/utils/clipboard-native.js +0 -1
- package/dist/utils/clipboard.d.ts +0 -1
- package/dist/utils/clipboard.js +0 -1
- package/dist/utils/deprecation.d.ts +0 -1
- package/dist/utils/deprecation.js +0 -1
- package/dist/utils/exif-orientation.d.ts +0 -1
- package/dist/utils/exif-orientation.js +0 -1
- package/dist/utils/frontmatter.d.ts +0 -1
- package/dist/utils/frontmatter.js +0 -1
- package/dist/utils/fs-watch.d.ts +0 -1
- package/dist/utils/fs-watch.js +0 -1
- package/dist/utils/git.d.ts +0 -1
- package/dist/utils/git.js +0 -1
- package/dist/utils/html.d.ts +0 -1
- package/dist/utils/html.js +0 -1
- package/dist/utils/image-convert.d.ts +0 -1
- package/dist/utils/image-convert.js +0 -1
- package/dist/utils/image-process.d.ts +0 -1
- package/dist/utils/image-process.js +0 -1
- package/dist/utils/image-resize-core.d.ts +0 -1
- package/dist/utils/image-resize-core.js +0 -1
- package/dist/utils/image-resize-worker.d.ts +0 -1
- package/dist/utils/image-resize-worker.js +0 -1
- package/dist/utils/image-resize.d.ts +0 -1
- package/dist/utils/image-resize.js +0 -1
- package/dist/utils/json.d.ts +0 -1
- package/dist/utils/json.js +0 -1
- package/dist/utils/mime.d.ts +0 -1
- package/dist/utils/mime.js +0 -1
- package/dist/utils/open-browser.d.ts +0 -1
- package/dist/utils/open-browser.js +0 -1
- package/dist/utils/paths.d.ts +0 -1
- package/dist/utils/paths.js +0 -1
- package/dist/utils/photon.d.ts +0 -1
- package/dist/utils/photon.js +0 -1
- package/dist/utils/pi-user-agent.d.ts +0 -1
- package/dist/utils/pi-user-agent.js +0 -1
- package/dist/utils/shell.d.ts +0 -1
- package/dist/utils/shell.js +0 -1
- package/dist/utils/sleep.d.ts +0 -1
- package/dist/utils/sleep.js +0 -1
- package/dist/utils/syntax-highlight.d.ts +0 -1
- package/dist/utils/syntax-highlight.js +0 -1
- package/dist/utils/thinking-tags.d.ts +0 -1
- package/dist/utils/thinking-tags.js +0 -1
- package/dist/utils/tools-manager.d.ts +3 -3
- package/dist/utils/tools-manager.js +125 -42
- package/dist/utils/version-check.d.ts +0 -1
- package/dist/utils/version-check.js +0 -1
- package/dist/utils/windows-self-update.d.ts +0 -1
- package/dist/utils/windows-self-update.js +0 -1
- package/docs/rpc.md +1 -2
- package/docs/settings.md +0 -1
- package/docs/skills.md +10 -18
- package/package.json +3 -2
- package/dist/bun/cli.d.ts.map +0 -1
- package/dist/bun/cli.js.map +0 -1
- package/dist/bun/register-bedrock.d.ts.map +0 -1
- package/dist/bun/register-bedrock.js.map +0 -1
- package/dist/bun/restore-sandbox-env.d.ts.map +0 -1
- package/dist/bun/restore-sandbox-env.js.map +0 -1
- package/dist/cli/args.d.ts.map +0 -1
- package/dist/cli/args.js.map +0 -1
- package/dist/cli/config-selector.d.ts.map +0 -1
- package/dist/cli/config-selector.js.map +0 -1
- package/dist/cli/credential-print.d.ts.map +0 -1
- package/dist/cli/credential-print.js.map +0 -1
- package/dist/cli/file-processor.d.ts.map +0 -1
- package/dist/cli/file-processor.js.map +0 -1
- package/dist/cli/initial-message.d.ts.map +0 -1
- package/dist/cli/initial-message.js.map +0 -1
- package/dist/cli/list-models.d.ts.map +0 -1
- package/dist/cli/list-models.js.map +0 -1
- package/dist/cli/project-trust.d.ts.map +0 -1
- package/dist/cli/project-trust.js.map +0 -1
- package/dist/cli/session-picker.d.ts.map +0 -1
- package/dist/cli/session-picker.js.map +0 -1
- package/dist/cli/startup-ui.d.ts.map +0 -1
- package/dist/cli/startup-ui.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/core/agent-session-auto-handoff.test.d.ts.map +0 -1
- package/dist/core/agent-session-auto-handoff.test.js.map +0 -1
- package/dist/core/agent-session-runtime.d.ts.map +0 -1
- package/dist/core/agent-session-runtime.js.map +0 -1
- package/dist/core/agent-session-services.d.ts.map +0 -1
- package/dist/core/agent-session-services.js.map +0 -1
- package/dist/core/agent-session-skill-block.test.d.ts.map +0 -1
- package/dist/core/agent-session-skill-block.test.js.map +0 -1
- package/dist/core/agent-session.d.ts.map +0 -1
- package/dist/core/agent-session.js.map +0 -1
- package/dist/core/agents.d.ts.map +0 -1
- package/dist/core/agents.js.map +0 -1
- package/dist/core/auth-guidance.d.ts.map +0 -1
- package/dist/core/auth-guidance.js.map +0 -1
- package/dist/core/auth-storage.d.ts.map +0 -1
- package/dist/core/auth-storage.js.map +0 -1
- package/dist/core/bash-executor.d.ts.map +0 -1
- package/dist/core/bash-executor.js.map +0 -1
- package/dist/core/built-in-extensions.d.ts.map +0 -1
- package/dist/core/built-in-extensions.js.map +0 -1
- package/dist/core/cache-stats.d.ts.map +0 -1
- package/dist/core/cache-stats.js.map +0 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +0 -1
- package/dist/core/compaction/branch-summarization.js.map +0 -1
- package/dist/core/compaction/compaction.d.ts.map +0 -1
- package/dist/core/compaction/compaction.js.map +0 -1
- package/dist/core/compaction/index.d.ts.map +0 -1
- package/dist/core/compaction/index.js.map +0 -1
- package/dist/core/compaction/utils.d.ts.map +0 -1
- package/dist/core/compaction/utils.js.map +0 -1
- package/dist/core/defaults.d.ts.map +0 -1
- package/dist/core/defaults.js.map +0 -1
- package/dist/core/diagnostics.d.ts.map +0 -1
- package/dist/core/diagnostics.js.map +0 -1
- package/dist/core/event-bus.d.ts.map +0 -1
- package/dist/core/event-bus.js.map +0 -1
- package/dist/core/exec.d.ts.map +0 -1
- package/dist/core/exec.js.map +0 -1
- package/dist/core/experimental.d.ts.map +0 -1
- package/dist/core/experimental.js.map +0 -1
- package/dist/core/export-html/ansi-to-html.d.ts.map +0 -1
- package/dist/core/export-html/ansi-to-html.js.map +0 -1
- package/dist/core/export-html/index.d.ts.map +0 -1
- package/dist/core/export-html/index.js.map +0 -1
- package/dist/core/export-html/tool-renderer.d.ts.map +0 -1
- package/dist/core/export-html/tool-renderer.js.map +0 -1
- package/dist/core/extensions/index.d.ts.map +0 -1
- package/dist/core/extensions/index.js.map +0 -1
- package/dist/core/extensions/loader.d.ts.map +0 -1
- package/dist/core/extensions/loader.js.map +0 -1
- package/dist/core/extensions/runner.d.ts.map +0 -1
- package/dist/core/extensions/runner.js.map +0 -1
- package/dist/core/extensions/types.d.ts.map +0 -1
- package/dist/core/extensions/types.js.map +0 -1
- package/dist/core/extensions/wrapper.d.ts.map +0 -1
- package/dist/core/extensions/wrapper.js.map +0 -1
- package/dist/core/footer-data-provider.d.ts.map +0 -1
- package/dist/core/footer-data-provider.js.map +0 -1
- package/dist/core/git-command.d.ts.map +0 -1
- package/dist/core/git-command.js.map +0 -1
- package/dist/core/http-dispatcher.d.ts.map +0 -1
- package/dist/core/http-dispatcher.js.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js.map +0 -1
- package/dist/core/keybindings.d.ts.map +0 -1
- package/dist/core/keybindings.js.map +0 -1
- package/dist/core/llama/client.d.ts.map +0 -1
- package/dist/core/llama/client.js.map +0 -1
- package/dist/core/llama/huggingface.d.ts.map +0 -1
- package/dist/core/llama/huggingface.js.map +0 -1
- package/dist/core/llama/index.d.ts.map +0 -1
- package/dist/core/llama/index.js.map +0 -1
- package/dist/core/llama/provider.d.ts.map +0 -1
- package/dist/core/llama/provider.js.map +0 -1
- package/dist/core/llama/ui.d.ts.map +0 -1
- package/dist/core/llama/ui.js.map +0 -1
- package/dist/core/messages.d.ts.map +0 -1
- package/dist/core/messages.js.map +0 -1
- package/dist/core/model-config.d.ts.map +0 -1
- package/dist/core/model-config.js.map +0 -1
- package/dist/core/model-registry.d.ts.map +0 -1
- package/dist/core/model-registry.js.map +0 -1
- package/dist/core/model-resolver.d.ts.map +0 -1
- package/dist/core/model-resolver.js.map +0 -1
- package/dist/core/model-runtime.d.ts.map +0 -1
- package/dist/core/model-runtime.js.map +0 -1
- package/dist/core/models-store.d.ts.map +0 -1
- package/dist/core/models-store.js.map +0 -1
- package/dist/core/output-guard.d.ts.map +0 -1
- package/dist/core/output-guard.js.map +0 -1
- package/dist/core/package-manager.d.ts.map +0 -1
- package/dist/core/package-manager.js.map +0 -1
- package/dist/core/package-manager.test.d.ts.map +0 -1
- package/dist/core/package-manager.test.js.map +0 -1
- package/dist/core/project-trust.d.ts.map +0 -1
- package/dist/core/project-trust.js.map +0 -1
- package/dist/core/prompt-templates.d.ts.map +0 -1
- package/dist/core/prompt-templates.js.map +0 -1
- package/dist/core/provider-attribution.d.ts.map +0 -1
- package/dist/core/provider-attribution.js.map +0 -1
- package/dist/core/provider-composer.d.ts.map +0 -1
- package/dist/core/provider-composer.js.map +0 -1
- package/dist/core/radius.d.ts.map +0 -1
- package/dist/core/radius.js.map +0 -1
- package/dist/core/remote-catalog-provider.d.ts.map +0 -1
- package/dist/core/remote-catalog-provider.js.map +0 -1
- package/dist/core/resolve-config-value.d.ts.map +0 -1
- package/dist/core/resolve-config-value.js.map +0 -1
- package/dist/core/resource-loader.d.ts.map +0 -1
- package/dist/core/resource-loader.js.map +0 -1
- package/dist/core/runtime-credentials.d.ts.map +0 -1
- package/dist/core/runtime-credentials.js.map +0 -1
- package/dist/core/sdk.d.ts.map +0 -1
- package/dist/core/sdk.js.map +0 -1
- package/dist/core/session-cwd.d.ts.map +0 -1
- package/dist/core/session-cwd.js.map +0 -1
- package/dist/core/session-manager.d.ts.map +0 -1
- package/dist/core/session-manager.js.map +0 -1
- package/dist/core/settings-manager-auto-handoff.test.d.ts.map +0 -1
- package/dist/core/settings-manager-auto-handoff.test.js.map +0 -1
- package/dist/core/settings-manager.d.ts.map +0 -1
- package/dist/core/settings-manager.js.map +0 -1
- package/dist/core/skills.d.ts.map +0 -1
- package/dist/core/skills.js.map +0 -1
- package/dist/core/slash-commands.d.ts.map +0 -1
- package/dist/core/slash-commands.js.map +0 -1
- package/dist/core/source-info.d.ts.map +0 -1
- package/dist/core/source-info.js.map +0 -1
- package/dist/core/system-prompt.d.ts.map +0 -1
- package/dist/core/system-prompt.js.map +0 -1
- package/dist/core/system-prompt.test.d.ts.map +0 -1
- package/dist/core/system-prompt.test.js.map +0 -1
- package/dist/core/telemetry.d.ts.map +0 -1
- package/dist/core/telemetry.js.map +0 -1
- package/dist/core/timings.d.ts.map +0 -1
- package/dist/core/timings.js.map +0 -1
- package/dist/core/tools/bash.d.ts.map +0 -1
- package/dist/core/tools/bash.js.map +0 -1
- package/dist/core/tools/edit-diff.d.ts.map +0 -1
- package/dist/core/tools/edit-diff.js.map +0 -1
- package/dist/core/tools/edit.d.ts.map +0 -1
- package/dist/core/tools/edit.js.map +0 -1
- package/dist/core/tools/file-mutation-queue.d.ts.map +0 -1
- package/dist/core/tools/file-mutation-queue.js.map +0 -1
- package/dist/core/tools/find.d.ts.map +0 -1
- package/dist/core/tools/find.js.map +0 -1
- package/dist/core/tools/grep.d.ts.map +0 -1
- package/dist/core/tools/grep.js.map +0 -1
- package/dist/core/tools/index.d.ts.map +0 -1
- package/dist/core/tools/index.js.map +0 -1
- package/dist/core/tools/ls.d.ts.map +0 -1
- package/dist/core/tools/ls.js.map +0 -1
- package/dist/core/tools/output-accumulator.d.ts.map +0 -1
- package/dist/core/tools/output-accumulator.js.map +0 -1
- package/dist/core/tools/path-utils.d.ts.map +0 -1
- package/dist/core/tools/path-utils.js.map +0 -1
- package/dist/core/tools/read.d.ts.map +0 -1
- package/dist/core/tools/read.js.map +0 -1
- package/dist/core/tools/render-utils.d.ts.map +0 -1
- package/dist/core/tools/render-utils.js.map +0 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +0 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +0 -1
- package/dist/core/tools/truncate.d.ts.map +0 -1
- package/dist/core/tools/truncate.js.map +0 -1
- package/dist/core/tools/write.d.ts.map +0 -1
- package/dist/core/tools/write.js.map +0 -1
- package/dist/core/trust-manager.d.ts.map +0 -1
- package/dist/core/trust-manager.js.map +0 -1
- package/dist/core/usage-totals.d.ts.map +0 -1
- package/dist/core/usage-totals.js.map +0 -1
- package/dist/extensions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/dist/extensions/pi-subagents/test/unit/slash-chain-groups.test.ts +0 -464
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/main.d.ts.map +0 -1
- package/dist/main.js.map +0 -1
- package/dist/migrations.d.ts.map +0 -1
- package/dist/migrations.js.map +0 -1
- package/dist/modes/index.d.ts.map +0 -1
- package/dist/modes/index.js.map +0 -1
- package/dist/modes/interactive/components/armin.d.ts.map +0 -1
- package/dist/modes/interactive/components/armin.js.map +0 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +0 -1
- package/dist/modes/interactive/components/assistant-message.js.map +0 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +0 -1
- package/dist/modes/interactive/components/bash-execution.js.map +0 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +0 -1
- package/dist/modes/interactive/components/bordered-loader.js.map +0 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +0 -1
- package/dist/modes/interactive/components/branch-summary-message.js.map +0 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +0 -1
- package/dist/modes/interactive/components/compaction-summary-message.js.map +0 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/config-selector.js.map +0 -1
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +0 -1
- package/dist/modes/interactive/components/countdown-timer.js.map +0 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +0 -1
- package/dist/modes/interactive/components/custom-editor.js.map +0 -1
- package/dist/modes/interactive/components/custom-entry.d.ts.map +0 -1
- package/dist/modes/interactive/components/custom-entry.js.map +0 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +0 -1
- package/dist/modes/interactive/components/custom-message.js.map +0 -1
- package/dist/modes/interactive/components/daxnuts.d.ts.map +0 -1
- package/dist/modes/interactive/components/daxnuts.js.map +0 -1
- package/dist/modes/interactive/components/diff.d.ts.map +0 -1
- package/dist/modes/interactive/components/diff.js.map +0 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +0 -1
- package/dist/modes/interactive/components/dynamic-border.js.map +0 -1
- package/dist/modes/interactive/components/earendil-announcement.d.ts.map +0 -1
- package/dist/modes/interactive/components/earendil-announcement.js.map +0 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +0 -1
- package/dist/modes/interactive/components/extension-editor.js.map +0 -1
- package/dist/modes/interactive/components/extension-input.d.ts.map +0 -1
- package/dist/modes/interactive/components/extension-input.js.map +0 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/extension-selector.js.map +0 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +0 -1
- package/dist/modes/interactive/components/first-time-setup.js.map +0 -1
- package/dist/modes/interactive/components/footer.d.ts.map +0 -1
- package/dist/modes/interactive/components/footer.js.map +0 -1
- package/dist/modes/interactive/components/index.d.ts.map +0 -1
- package/dist/modes/interactive/components/index.js.map +0 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +0 -1
- package/dist/modes/interactive/components/keybinding-hints.js.map +0 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +0 -1
- package/dist/modes/interactive/components/login-dialog.js.map +0 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/model-selector.js.map +0 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/oauth-selector.js.map +0 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/scoped-models-selector.js.map +0 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +0 -1
- package/dist/modes/interactive/components/session-selector-search.js.map +0 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/session-selector.js.map +0 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/settings-selector.js.map +0 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/show-images-selector.js.map +0 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +0 -1
- package/dist/modes/interactive/components/skill-invocation-message.js.map +0 -1
- package/dist/modes/interactive/components/status-indicator.d.ts.map +0 -1
- package/dist/modes/interactive/components/status-indicator.js.map +0 -1
- package/dist/modes/interactive/components/theme-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/theme-selector.js.map +0 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +0 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +0 -1
- package/dist/modes/interactive/components/tool-execution.js.map +0 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/tree-selector.js.map +0 -1
- package/dist/modes/interactive/components/trust-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/trust-selector.js.map +0 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/user-message-selector.js.map +0 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +0 -1
- package/dist/modes/interactive/components/user-message.js.map +0 -1
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +0 -1
- package/dist/modes/interactive/components/visual-truncate.js.map +0 -1
- package/dist/modes/interactive/external-editor.d.ts.map +0 -1
- package/dist/modes/interactive/external-editor.js.map +0 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +0 -1
- package/dist/modes/interactive/interactive-mode.js.map +0 -1
- package/dist/modes/interactive/model-search.d.ts.map +0 -1
- package/dist/modes/interactive/model-search.js.map +0 -1
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +0 -1
- package/dist/modes/interactive/theme/theme-controller.js.map +0 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +0 -1
- package/dist/modes/interactive/theme/theme.js.map +0 -1
- package/dist/modes/print-mode.d.ts.map +0 -1
- package/dist/modes/print-mode.js.map +0 -1
- package/dist/modes/rpc/jsonl.d.ts.map +0 -1
- package/dist/modes/rpc/jsonl.js.map +0 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +0 -1
- package/dist/modes/rpc/rpc-client.js.map +0 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +0 -1
- package/dist/modes/rpc/rpc-mode.js.map +0 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +0 -1
- package/dist/modes/rpc/rpc-types.js.map +0 -1
- package/dist/package-manager-cli.d.ts.map +0 -1
- package/dist/package-manager-cli.js.map +0 -1
- package/dist/rpc-entry.d.ts.map +0 -1
- package/dist/rpc-entry.js.map +0 -1
- package/dist/utils/ansi.d.ts.map +0 -1
- package/dist/utils/ansi.js.map +0 -1
- package/dist/utils/changelog.d.ts.map +0 -1
- package/dist/utils/changelog.js.map +0 -1
- package/dist/utils/child-process.d.ts.map +0 -1
- package/dist/utils/child-process.js.map +0 -1
- package/dist/utils/clipboard-image.d.ts.map +0 -1
- package/dist/utils/clipboard-image.js.map +0 -1
- package/dist/utils/clipboard-native.d.ts.map +0 -1
- package/dist/utils/clipboard-native.js.map +0 -1
- package/dist/utils/clipboard.d.ts.map +0 -1
- package/dist/utils/clipboard.js.map +0 -1
- package/dist/utils/deprecation.d.ts.map +0 -1
- package/dist/utils/deprecation.js.map +0 -1
- package/dist/utils/exif-orientation.d.ts.map +0 -1
- package/dist/utils/exif-orientation.js.map +0 -1
- package/dist/utils/frontmatter.d.ts.map +0 -1
- package/dist/utils/frontmatter.js.map +0 -1
- package/dist/utils/fs-watch.d.ts.map +0 -1
- package/dist/utils/fs-watch.js.map +0 -1
- package/dist/utils/git.d.ts.map +0 -1
- package/dist/utils/git.js.map +0 -1
- package/dist/utils/html.d.ts.map +0 -1
- package/dist/utils/html.js.map +0 -1
- package/dist/utils/image-convert.d.ts.map +0 -1
- package/dist/utils/image-convert.js.map +0 -1
- package/dist/utils/image-process.d.ts.map +0 -1
- package/dist/utils/image-process.js.map +0 -1
- package/dist/utils/image-resize-core.d.ts.map +0 -1
- package/dist/utils/image-resize-core.js.map +0 -1
- package/dist/utils/image-resize-worker.d.ts.map +0 -1
- package/dist/utils/image-resize-worker.js.map +0 -1
- package/dist/utils/image-resize.d.ts.map +0 -1
- package/dist/utils/image-resize.js.map +0 -1
- package/dist/utils/json.d.ts.map +0 -1
- package/dist/utils/json.js.map +0 -1
- package/dist/utils/mime.d.ts.map +0 -1
- package/dist/utils/mime.js.map +0 -1
- package/dist/utils/open-browser.d.ts.map +0 -1
- package/dist/utils/open-browser.js.map +0 -1
- package/dist/utils/paths.d.ts.map +0 -1
- package/dist/utils/paths.js.map +0 -1
- package/dist/utils/photon.d.ts.map +0 -1
- package/dist/utils/photon.js.map +0 -1
- package/dist/utils/pi-user-agent.d.ts.map +0 -1
- package/dist/utils/pi-user-agent.js.map +0 -1
- package/dist/utils/shell.d.ts.map +0 -1
- package/dist/utils/shell.js.map +0 -1
- package/dist/utils/sleep.d.ts.map +0 -1
- package/dist/utils/sleep.js.map +0 -1
- package/dist/utils/syntax-highlight.d.ts.map +0 -1
- package/dist/utils/syntax-highlight.js.map +0 -1
- package/dist/utils/thinking-tags.d.ts.map +0 -1
- package/dist/utils/thinking-tags.js.map +0 -1
- package/dist/utils/tools-manager.d.ts.map +0 -1
- package/dist/utils/tools-manager.js.map +0 -1
- package/dist/utils/version-check.d.ts.map +0 -1
- package/dist/utils/version-check.js.map +0 -1
- package/dist/utils/windows-self-update.d.ts.map +0 -1
- package/dist/utils/windows-self-update.js.map +0 -1
|
@@ -13,6 +13,7 @@ import { describe, it, before, after, beforeEach, afterEach } from "node:test";
|
|
|
13
13
|
import assert from "node:assert/strict";
|
|
14
14
|
import * as fs from "node:fs";
|
|
15
15
|
import * as path from "node:path";
|
|
16
|
+
import { execFileSync } from "node:child_process";
|
|
16
17
|
import type { MockPi } from "../support/helpers.ts";
|
|
17
18
|
import {
|
|
18
19
|
createMockPi,
|
|
@@ -27,19 +28,14 @@ import {
|
|
|
27
28
|
} from "../support/helpers.ts";
|
|
28
29
|
import registerSubagentExtension from "../../src/extension/index.ts";
|
|
29
30
|
import {
|
|
30
|
-
SUBAGENT_DELEGATION_PROTOCOL_VERSION,
|
|
31
|
-
SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
|
|
32
31
|
SUBAGENT_DELEGATION_REQUEST_EVENT,
|
|
33
32
|
SUBAGENT_DELEGATION_RESPONSE_EVENT,
|
|
34
33
|
SUBAGENT_DELEGATION_STARTED_EVENT,
|
|
35
|
-
|
|
34
|
+
type SubagentDelegationRequest,
|
|
36
35
|
type SubagentDelegationResponse,
|
|
37
|
-
type
|
|
38
|
-
type SubagentDelegationV2Request,
|
|
39
|
-
type SubagentDelegationV2Response,
|
|
40
|
-
type SubagentDelegationV2Started,
|
|
36
|
+
type SubagentDelegationStarted,
|
|
41
37
|
} from "../../src/api/delegation.ts";
|
|
42
|
-
import { INTERCOM_DETACH_REQUEST_EVENT, INTERCOM_DETACH_RESPONSE_EVENT, type SubagentState } from "../../src/shared/types.ts";
|
|
38
|
+
import { CHAIN_RUNS_DIR, DIRS, INTERCOM_DETACH_REQUEST_EVENT, INTERCOM_DETACH_RESPONSE_EVENT, type SubagentState } from "../../src/shared/types.ts";
|
|
43
39
|
import { CHILD_WATCHDOG_STATUS_EVENT } from "../../src/watchdog/child-status.ts";
|
|
44
40
|
import { WAIT_TOOL_ENABLED_ENV } from "../../src/runs/background/wait-config.ts";
|
|
45
41
|
import { TOOL_BUDGET_ENV, TOOL_BUDGET_ZERO_AUTH_ENV } from "../../src/runs/shared/tool-budget.ts";
|
|
@@ -49,9 +45,11 @@ import {
|
|
|
49
45
|
SUBAGENT_FANOUT_CHILD_ENV,
|
|
50
46
|
SUBAGENT_PARENT_CHILD_INDEX_ENV,
|
|
51
47
|
SUBAGENT_PARENT_CONTROL_INBOX_ENV,
|
|
48
|
+
SUBAGENT_PARENT_DEPTH_ENV,
|
|
52
49
|
SUBAGENT_PARENT_EVENT_SINK_ENV,
|
|
53
50
|
SUBAGENT_PARENT_RUN_ID_ENV,
|
|
54
51
|
} from "../../src/runs/shared/pi-args.ts";
|
|
52
|
+
import { createNestedRoute, nestedRouteEnv, parseNestedEventRecords } from "../../src/runs/shared/nested-events.ts";
|
|
55
53
|
|
|
56
54
|
interface ModelAttempt {
|
|
57
55
|
success?: boolean;
|
|
@@ -324,6 +322,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
324
322
|
initialSpawnState?: NonNullable<SubagentState["subagentSpawns"]>,
|
|
325
323
|
allowMutatingManagementActions = true,
|
|
326
324
|
initialAsyncJobs: SubagentState["asyncJobs"] = new Map(),
|
|
325
|
+
workflowControllers?: Map<string, AbortController>,
|
|
327
326
|
) {
|
|
328
327
|
return createSubagentExecutor!({
|
|
329
328
|
pi: { events: createEventBus(), getSessionName: () => undefined },
|
|
@@ -332,13 +331,14 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
332
331
|
currentSessionId: initialSpawnState?.sessionId ?? null,
|
|
333
332
|
...(initialSpawnState ? { subagentSpawns: initialSpawnState } : {}),
|
|
334
333
|
asyncJobs: initialAsyncJobs,
|
|
334
|
+
...(workflowControllers ? { workflowControllers } : {}),
|
|
335
335
|
foregroundControls: new Map(),
|
|
336
336
|
lastForegroundControlId: null,
|
|
337
337
|
},
|
|
338
338
|
config,
|
|
339
339
|
asyncByDefault,
|
|
340
340
|
tempArtifactsDir: tempDir,
|
|
341
|
-
getSubagentSessionRoot: () => tempDir,
|
|
341
|
+
getSubagentSessionRoot: () => path.join(tempDir, ".pi-subagents", "sessions"),
|
|
342
342
|
expandTilde: (value: string) => value,
|
|
343
343
|
discoverAgents: () => ({ agents }),
|
|
344
344
|
allowMutatingManagementActions,
|
|
@@ -374,11 +374,421 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
374
374
|
);
|
|
375
375
|
|
|
376
376
|
assert.equal(result.isError, undefined);
|
|
377
|
-
assert.match(result.content[0]?.text ?? "", /
|
|
378
|
-
|
|
377
|
+
assert.match(result.content[0]?.text ?? "", /single alias finished/);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("starts workflow scripts asynchronously by default and persists live status", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
381
|
+
mockPi.onCall({ output: "async workflow child" });
|
|
382
|
+
const executor = makeExecutor([makeAgent("echo")]);
|
|
383
|
+
const runId = `scripted-workflow-async-${Date.now()}`;
|
|
384
|
+
|
|
385
|
+
const result = await executor.execute(
|
|
386
|
+
runId,
|
|
387
|
+
{ workflowScript: `emit("starting"); await runs.run("work", { agent: "echo", task: "Async work" }); return { answer: 42 };` },
|
|
388
|
+
new AbortController().signal,
|
|
389
|
+
undefined,
|
|
390
|
+
makeMinimalCtx(tempDir),
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
assert.equal(result.isError, undefined);
|
|
394
|
+
assert.equal(result.details.mode, "workflow");
|
|
395
|
+
assert.equal(result.details.asyncId, runId);
|
|
396
|
+
assert.match(result.content[0]?.text ?? "", /Async workflow/);
|
|
397
|
+
const statusPath = path.join(result.details.asyncDir!, "status.json");
|
|
398
|
+
let status: { state?: string; workflow?: { value?: unknown; emits?: unknown[]; trace?: Array<{ key?: string; state?: string }> } } = {};
|
|
399
|
+
for (let attempt = 0; attempt < 100; attempt++) {
|
|
400
|
+
status = JSON.parse(fs.readFileSync(statusPath, "utf-8"));
|
|
401
|
+
if (status.state === "complete" || status.state === "failed") break;
|
|
402
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
403
|
+
}
|
|
404
|
+
assert.equal(status.state, "complete");
|
|
405
|
+
assert.deepEqual(status.workflow?.value, { answer: 42 });
|
|
406
|
+
assert.deepEqual(status.workflow?.emits, ["starting"]);
|
|
407
|
+
assert.ok(status.workflow?.trace?.some((entry) => entry.key === "work" && entry.state === "completed"));
|
|
408
|
+
const persistedResult = JSON.parse(fs.readFileSync(path.join(DIRS.results, `${runId}.json`), "utf-8")) as { agent?: string; summary?: string; workflow?: { value?: unknown } };
|
|
409
|
+
assert.equal(persistedResult.agent, "workflow");
|
|
410
|
+
assert.match(persistedResult.summary ?? "", /Return: \{\n "answer": 42\n\}/);
|
|
411
|
+
assert.deepEqual(persistedResult.workflow?.value, { answer: 42 });
|
|
412
|
+
fs.rmSync(result.details.asyncDir!, { recursive: true, force: true });
|
|
413
|
+
fs.rmSync(path.join(DIRS.results, `${runId}.json`), { force: true });
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it("rejects an invalid async workflow usage budget before creating run state", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
417
|
+
const asyncJobs: SubagentState["asyncJobs"] = new Map();
|
|
418
|
+
const executor = makeExecutor([makeAgent("echo")], {}, false, undefined, true, asyncJobs);
|
|
419
|
+
const runId = `scripted-workflow-invalid-budget-${Date.now()}`;
|
|
420
|
+
|
|
421
|
+
const result = await executor.execute(
|
|
422
|
+
runId,
|
|
423
|
+
{ workflowScript: `return "unreachable";`, usageBudget: { tokens: { hard: 0 } } },
|
|
424
|
+
new AbortController().signal,
|
|
425
|
+
undefined,
|
|
426
|
+
makeMinimalCtx(tempDir),
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
assert.equal(result.isError, true);
|
|
430
|
+
assert.match(result.content[0]?.text ?? "", /usageBudget\.tokens\.hard must be a positive number/);
|
|
431
|
+
assert.equal(result.details.asyncId, undefined);
|
|
432
|
+
assert.equal(asyncJobs.has(runId), false);
|
|
433
|
+
assert.equal(fs.existsSync(path.join(DIRS.async, runId)), false);
|
|
434
|
+
assert.equal(fs.existsSync(path.join(DIRS.results, `${runId}.json`)), false);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it("rejects async child launches from budgeted async workflows", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
438
|
+
const executor = makeExecutor([makeAgent("echo")]);
|
|
439
|
+
const runId = `scripted-workflow-budget-async-child-${Date.now()}`;
|
|
440
|
+
const started = await executor.execute(
|
|
441
|
+
runId,
|
|
442
|
+
{
|
|
443
|
+
workflowScript: `await runs.run("background", { agent: "echo", task: "Async child", async: true }); return "unreachable";`,
|
|
444
|
+
usageBudget: { tokens: { hard: 100 } },
|
|
445
|
+
},
|
|
446
|
+
new AbortController().signal,
|
|
447
|
+
undefined,
|
|
448
|
+
makeMinimalCtx(tempDir),
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
assert.equal(started.isError, undefined);
|
|
452
|
+
assert.equal(started.details.asyncId, runId);
|
|
453
|
+
const resultPath = path.join(DIRS.results, `${runId}.json`);
|
|
454
|
+
let persisted: { state?: string; summary?: string; results?: Array<{ success?: boolean; output?: string }> } = {};
|
|
455
|
+
for (let attempt = 0; attempt < 100; attempt++) {
|
|
456
|
+
if (fs.existsSync(resultPath)) persisted = JSON.parse(fs.readFileSync(resultPath, "utf-8"));
|
|
457
|
+
if (persisted.state === "failed") break;
|
|
458
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
459
|
+
}
|
|
460
|
+
assert.equal(persisted.state, "failed");
|
|
461
|
+
assert.match(persisted.summary ?? "", /workflow usageBudget does not support async runs\.run launches/);
|
|
462
|
+
assert.equal(persisted.results?.length, 1);
|
|
463
|
+
assert.equal(persisted.results?.[0]?.success, false);
|
|
464
|
+
assert.match(persisted.results?.[0]?.output ?? "", /workflow usageBudget does not support async runs\.run launches/);
|
|
465
|
+
assert.equal(mockPi.callCount(), 0);
|
|
466
|
+
fs.rmSync(started.details.asyncDir!, { recursive: true, force: true });
|
|
467
|
+
fs.rmSync(resultPath, { force: true });
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it("persists workflow parent metadata in an async worktree child status and result", { skip: !createSubagentExecutor || process.platform === "win32" ? "executor unavailable or worktree paths differ on Windows" : undefined }, async () => {
|
|
471
|
+
execFileSync("git", ["init"], { cwd: tempDir, stdio: "ignore" });
|
|
472
|
+
execFileSync("git", ["config", "user.email", "test@example.com"], { cwd: tempDir });
|
|
473
|
+
execFileSync("git", ["config", "user.name", "Test User"], { cwd: tempDir });
|
|
474
|
+
fs.writeFileSync(path.join(tempDir, "base.txt"), "base\n", "utf-8");
|
|
475
|
+
execFileSync("git", ["add", "base.txt"], { cwd: tempDir });
|
|
476
|
+
execFileSync("git", ["commit", "-m", "base"], { cwd: tempDir, stdio: "ignore" });
|
|
477
|
+
mockPi.onCall({ output: "async child done" });
|
|
478
|
+
const executor = makeExecutor([makeAgent("echo")]);
|
|
479
|
+
const workflowRunId = `scripted-workflow-parent-${Date.now()}`;
|
|
480
|
+
const started = await executor.execute(
|
|
481
|
+
workflowRunId,
|
|
482
|
+
{ workflowScript: `const child = await runs.run("background", { agent: "echo", task: "Async child", async: true, worktree: true }); return child.runId;` },
|
|
483
|
+
new AbortController().signal,
|
|
484
|
+
undefined,
|
|
485
|
+
makeMinimalCtx(tempDir),
|
|
486
|
+
);
|
|
487
|
+
const workflowResultPath = path.join(DIRS.results, `${workflowRunId}.json`);
|
|
488
|
+
let childRunId: string | undefined;
|
|
489
|
+
for (let attempt = 0; attempt < 150; attempt++) {
|
|
490
|
+
if (fs.existsSync(workflowResultPath)) {
|
|
491
|
+
const workflowResult = JSON.parse(fs.readFileSync(workflowResultPath, "utf-8")) as { workflow?: { value?: unknown } };
|
|
492
|
+
if (typeof workflowResult.workflow?.value === "string") { childRunId = workflowResult.workflow.value; break; }
|
|
493
|
+
}
|
|
494
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
495
|
+
}
|
|
496
|
+
assert.ok(childRunId);
|
|
497
|
+
const childDir = path.join(DIRS.async, childRunId);
|
|
498
|
+
const childStatusPath = path.join(childDir, "status.json");
|
|
499
|
+
let childStatus: { state?: string; mode?: string; parentWorkflowRunId?: string; workflowKey?: string; parallelHandoff?: { path?: string } } = {};
|
|
500
|
+
for (let attempt = 0; attempt < 200; attempt++) {
|
|
501
|
+
if (fs.existsSync(childStatusPath)) childStatus = JSON.parse(fs.readFileSync(childStatusPath, "utf-8"));
|
|
502
|
+
if (["complete", "failed", "stopped"].includes(childStatus.state ?? "")) break;
|
|
503
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
504
|
+
}
|
|
505
|
+
assert.equal(childStatus.mode, "parallel");
|
|
506
|
+
assert.equal(childStatus.parentWorkflowRunId, workflowRunId);
|
|
507
|
+
assert.equal(childStatus.workflowKey, "background");
|
|
508
|
+
assert.equal(typeof childStatus.parallelHandoff?.path, "string");
|
|
509
|
+
const childResultPath = path.join(DIRS.results, `${childRunId}.json`);
|
|
510
|
+
for (let attempt = 0; attempt < 200 && !fs.existsSync(childResultPath); attempt++) {
|
|
511
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
512
|
+
}
|
|
513
|
+
const childResult = JSON.parse(fs.readFileSync(childResultPath, "utf-8")) as { parentWorkflowRunId?: string; workflowKey?: string };
|
|
514
|
+
assert.equal(childResult.parentWorkflowRunId, workflowRunId);
|
|
515
|
+
assert.equal(childResult.workflowKey, "background");
|
|
516
|
+
fs.rmSync(started.details.asyncDir!, { recursive: true, force: true });
|
|
517
|
+
fs.rmSync(workflowResultPath, { force: true });
|
|
518
|
+
fs.rmSync(childDir, { recursive: true, force: true });
|
|
519
|
+
fs.rmSync(path.join(DIRS.results, `${childRunId}.json`), { force: true });
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
it("stops a live async workflow through its controller", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
523
|
+
const controller = new AbortController();
|
|
524
|
+
const executor = makeExecutor([makeAgent("echo")], {}, false, undefined, true, new Map(), new Map([["workflow-stop", controller]]));
|
|
525
|
+
const result = await executor.execute(
|
|
526
|
+
"stop-call",
|
|
527
|
+
{ action: "stop", id: "workflow-stop" },
|
|
528
|
+
new AbortController().signal,
|
|
529
|
+
undefined,
|
|
530
|
+
makeMinimalCtx(tempDir),
|
|
531
|
+
);
|
|
532
|
+
assert.equal(result.isError, undefined);
|
|
533
|
+
assert.equal(controller.signal.aborted, true);
|
|
534
|
+
assert.match(result.content[0]?.text ?? "", /Stop requested for async workflow workflow-stop/);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it("reports completed async workflows as not running when stopped after completion", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
538
|
+
const runId = `workflow-stop-complete-${Date.now()}`;
|
|
539
|
+
const asyncDir = path.join(DIRS.async, runId);
|
|
540
|
+
fs.mkdirSync(asyncDir, { recursive: true });
|
|
541
|
+
fs.writeFileSync(path.join(asyncDir, "status.json"), JSON.stringify({
|
|
542
|
+
runId,
|
|
543
|
+
sessionId: "session-123",
|
|
544
|
+
mode: "workflow",
|
|
545
|
+
state: "complete",
|
|
546
|
+
startedAt: Date.now(),
|
|
547
|
+
lastUpdate: Date.now(),
|
|
548
|
+
cwd: tempDir,
|
|
549
|
+
pid: process.pid,
|
|
550
|
+
}), "utf-8");
|
|
551
|
+
const asyncJobs: SubagentState["asyncJobs"] = new Map([[runId, {
|
|
552
|
+
asyncId: runId,
|
|
553
|
+
asyncDir,
|
|
554
|
+
cwd: tempDir,
|
|
555
|
+
status: "complete",
|
|
556
|
+
mode: "workflow",
|
|
557
|
+
agents: [],
|
|
558
|
+
steps: [],
|
|
559
|
+
startedAt: Date.now(),
|
|
560
|
+
updatedAt: Date.now(),
|
|
561
|
+
}]]);
|
|
562
|
+
const executor = makeExecutor([makeAgent("echo")], {}, false, undefined, true, asyncJobs);
|
|
563
|
+
|
|
564
|
+
const result = await executor.execute(
|
|
565
|
+
"stop-completed-workflow",
|
|
566
|
+
{ action: "stop", id: runId },
|
|
567
|
+
new AbortController().signal,
|
|
568
|
+
undefined,
|
|
569
|
+
makeMinimalCtx(tempDir),
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
assert.equal(result.isError, true);
|
|
573
|
+
assert.match(result.content[0]?.text ?? "", /No running or queued async run was found/);
|
|
574
|
+
assert.doesNotMatch(result.content[0]?.text ?? "", /reload recovery/);
|
|
575
|
+
fs.rmSync(asyncDir, { recursive: true, force: true });
|
|
379
576
|
});
|
|
380
577
|
|
|
381
|
-
it("
|
|
578
|
+
it("routes workflow script children through ordinary foreground execution", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
579
|
+
mockPi.onCall({ output: "scanned auth" });
|
|
580
|
+
mockPi.onCall({ output: "reviewed auth" });
|
|
581
|
+
const executor = makeExecutor([makeAgent("echo")]);
|
|
582
|
+
|
|
583
|
+
const result = await executor.execute(
|
|
584
|
+
"scripted-workflow",
|
|
585
|
+
{
|
|
586
|
+
async: false,
|
|
587
|
+
workflowScript: `
|
|
588
|
+
const scan = await runs.run("scan", { agent: "echo", task: "Scan auth" });
|
|
589
|
+
const review = await runs.run("review", { agent: "echo", task: "Review: " + scan.output });
|
|
590
|
+
return review.output;
|
|
591
|
+
`,
|
|
592
|
+
},
|
|
593
|
+
new AbortController().signal,
|
|
594
|
+
undefined,
|
|
595
|
+
makeMinimalCtx(tempDir),
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
assert.equal(result.isError, undefined);
|
|
599
|
+
assert.match(result.content[0]?.text ?? "", /reviewed auth/);
|
|
600
|
+
assert.equal(result.details.mode, "workflow");
|
|
601
|
+
assert.equal(result.details.results.length, 2);
|
|
602
|
+
assert.deepEqual(result.details.workflow?.trace.filter((entry) => entry.state === "completed").map((entry) => entry.key), ["scan", "review"]);
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
it("runs a direct single child in a managed worktree", { skip: !createSubagentExecutor || process.platform === "win32" ? "executor unavailable or worktree paths differ on Windows" : undefined }, async () => {
|
|
606
|
+
execFileSync("git", ["init"], { cwd: tempDir, stdio: "ignore" });
|
|
607
|
+
execFileSync("git", ["config", "user.email", "test@example.com"], { cwd: tempDir });
|
|
608
|
+
execFileSync("git", ["config", "user.name", "Test User"], { cwd: tempDir });
|
|
609
|
+
fs.writeFileSync(path.join(tempDir, "base.txt"), "base\n", "utf-8");
|
|
610
|
+
execFileSync("git", ["add", "base.txt"], { cwd: tempDir });
|
|
611
|
+
execFileSync("git", ["commit", "-m", "base"], { cwd: tempDir, stdio: "ignore" });
|
|
612
|
+
mockPi.onCall({ output: "isolated feature", writeFiles: [{ path: "feature.txt", content: "feature\n" }] });
|
|
613
|
+
const executor = makeExecutor([makeAgent("builder")]);
|
|
614
|
+
|
|
615
|
+
const result = await executor.execute(
|
|
616
|
+
"direct-worktree",
|
|
617
|
+
{ async: false, agent: "builder", task: "Implement feature", worktree: true },
|
|
618
|
+
new AbortController().signal,
|
|
619
|
+
undefined,
|
|
620
|
+
makeMinimalCtx(tempDir),
|
|
621
|
+
);
|
|
622
|
+
|
|
623
|
+
assert.equal(result.isError, undefined);
|
|
624
|
+
assert.equal(mockPi.callCount(), 1);
|
|
625
|
+
assert.equal(fs.existsSync(path.join(tempDir, "feature.txt")), false);
|
|
626
|
+
const handoffPath = (result.content[0]?.text ?? "").match(/([^\s]+\/handoffs\/[^\s]+\.json)/)?.[1];
|
|
627
|
+
assert.ok(handoffPath, result.content[0]?.text);
|
|
628
|
+
const handoff = JSON.parse(fs.readFileSync(handoffPath, "utf-8")) as {
|
|
629
|
+
groups: Array<{
|
|
630
|
+
cleanup: { state: string; tasks: Array<{ worktreeRemoved: boolean }> };
|
|
631
|
+
}>;
|
|
632
|
+
};
|
|
633
|
+
assert.equal(handoff.groups[0]?.cleanup.state, "complete");
|
|
634
|
+
assert.equal(handoff.groups[0]?.cleanup.tasks[0]?.worktreeRemoved, true);
|
|
635
|
+
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
it("lets runs.all siblings settle when one child fails", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
639
|
+
mockPi.onCall({ exitCode: 1, stderr: "first child failed" });
|
|
640
|
+
mockPi.onCall({ output: "second child completed" });
|
|
641
|
+
const executor = makeExecutor([makeAgent("echo")]);
|
|
642
|
+
|
|
643
|
+
const result = await executor.execute(
|
|
644
|
+
"scripted-workflow-settlement",
|
|
645
|
+
{
|
|
646
|
+
async: false,
|
|
647
|
+
workflowScript: `
|
|
648
|
+
const children = await runs.all([
|
|
649
|
+
{ key: "first", agent: "echo", task: "First task" },
|
|
650
|
+
{ key: "second", agent: "echo", task: "Second task" }
|
|
651
|
+
]);
|
|
652
|
+
return children.map(({ key, ok, error }) => error === undefined ? { key, ok } : { key, ok, error });
|
|
653
|
+
`,
|
|
654
|
+
},
|
|
655
|
+
new AbortController().signal,
|
|
656
|
+
undefined,
|
|
657
|
+
makeMinimalCtx(tempDir),
|
|
658
|
+
);
|
|
659
|
+
|
|
660
|
+
assert.equal(result.isError, undefined, result.content[0]?.text ?? "workflow failed");
|
|
661
|
+
assert.equal(mockPi.callCount(), 2);
|
|
662
|
+
const value = result.details.workflow?.value as Array<{ key: string; ok: boolean; error?: string }>;
|
|
663
|
+
assert.deepEqual(value.map(({ key }) => key), ["first", "second"]);
|
|
664
|
+
assert.deepEqual(value.map(({ ok }) => ok).sort(), [false, true]);
|
|
665
|
+
const failed = value.find(({ ok }) => !ok);
|
|
666
|
+
const succeeded = value.find(({ ok }) => ok);
|
|
667
|
+
assert.match(failed?.error ?? "", /first child failed/);
|
|
668
|
+
assert.equal(succeeded?.error, undefined);
|
|
669
|
+
assert.deepEqual(result.details.workflow?.trace.filter((entry) => entry.state !== "started").map(({ state }) => state).sort(), ["completed", "failed"]);
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
it("gives parallel workflow children separate managed worktrees and durable handoffs", { skip: !createSubagentExecutor || process.platform === "win32" ? "executor unavailable or worktree paths differ on Windows" : undefined }, async () => {
|
|
673
|
+
execFileSync("git", ["init"], { cwd: tempDir, stdio: "ignore" });
|
|
674
|
+
execFileSync("git", ["config", "user.email", "test@example.com"], { cwd: tempDir });
|
|
675
|
+
execFileSync("git", ["config", "user.name", "Test User"], { cwd: tempDir });
|
|
676
|
+
fs.writeFileSync(path.join(tempDir, "base.txt"), "base\n", "utf-8");
|
|
677
|
+
execFileSync("git", ["add", "base.txt"], { cwd: tempDir });
|
|
678
|
+
execFileSync("git", ["commit", "-m", "base"], { cwd: tempDir, stdio: "ignore" });
|
|
679
|
+
mockPi.onCall({ output: "feature a", writeFiles: [{ path: "feature-a.txt", content: "a\n" }] });
|
|
680
|
+
mockPi.onCall({ output: "feature b", writeFiles: [{ path: "feature-b.txt", content: "b\n" }] });
|
|
681
|
+
const executor = makeExecutor([makeAgent("builder")]);
|
|
682
|
+
|
|
683
|
+
const result = await executor.execute(
|
|
684
|
+
"scripted-workflow-worktrees",
|
|
685
|
+
{
|
|
686
|
+
async: false,
|
|
687
|
+
workflowScript: `
|
|
688
|
+
const children = await runs.all([
|
|
689
|
+
{ key: "feature-a", agent: "builder", task: "Implement A", worktree: true },
|
|
690
|
+
{ key: "feature-b", agent: "builder", task: "Implement B", worktree: true }
|
|
691
|
+
]);
|
|
692
|
+
return children.map(({ key, artifactPaths }) => ({ key, artifactPaths }));
|
|
693
|
+
`,
|
|
694
|
+
},
|
|
695
|
+
new AbortController().signal,
|
|
696
|
+
undefined,
|
|
697
|
+
makeMinimalCtx(tempDir),
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
assert.equal(result.isError, undefined);
|
|
701
|
+
assert.equal(mockPi.callCount(), 2, result.content[0]?.text ?? "workflow produced no output");
|
|
702
|
+
assert.equal(fs.existsSync(path.join(tempDir, "feature-a.txt")), false);
|
|
703
|
+
assert.equal(fs.existsSync(path.join(tempDir, "feature-b.txt")), false);
|
|
704
|
+
const output = result.content[0]?.text ?? "";
|
|
705
|
+
const handoffPaths = [...output.matchAll(/"([^"\n]*\/handoffs\/[^"\n]+\.json)"/g)].map((match) => match[1]!);
|
|
706
|
+
assert.equal(handoffPaths.length, 2, output);
|
|
707
|
+
const worktreePaths = new Set<string>();
|
|
708
|
+
for (const handoffPath of handoffPaths) {
|
|
709
|
+
const handoff = JSON.parse(fs.readFileSync(handoffPath, "utf-8")) as {
|
|
710
|
+
groups: Array<{
|
|
711
|
+
children: Array<{ patch: { changed: boolean; path: string } }>;
|
|
712
|
+
cleanup: { state: string; tasks: Array<{ path: string; worktreeRemoved: boolean; branchRemoved: boolean }> };
|
|
713
|
+
}>;
|
|
714
|
+
};
|
|
715
|
+
assert.equal(handoff.groups.length, 1);
|
|
716
|
+
assert.equal(handoff.groups[0]?.children.length, 1);
|
|
717
|
+
assert.equal(handoff.groups[0]?.children[0]?.patch.changed, true);
|
|
718
|
+
assert.equal(fs.existsSync(handoff.groups[0]!.children[0]!.patch.path), true);
|
|
719
|
+
assert.equal(handoff.groups[0]?.cleanup.state, "complete");
|
|
720
|
+
assert.equal(handoff.groups[0]?.cleanup.tasks[0]?.worktreeRemoved, true);
|
|
721
|
+
assert.equal(handoff.groups[0]?.cleanup.tasks[0]?.branchRemoved, true);
|
|
722
|
+
worktreePaths.add(handoff.groups[0]!.cleanup.tasks[0]!.path);
|
|
723
|
+
}
|
|
724
|
+
assert.equal(worktreePaths.size, 2);
|
|
725
|
+
for (const worktreePath of worktreePaths) assert.equal(fs.existsSync(worktreePath), false);
|
|
726
|
+
assert.match(result.content[0]?.text ?? "", /handoffs/);
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
it("inherits workflow-level worktree isolation and allows a child opt-out", { skip: !createSubagentExecutor || process.platform === "win32" ? "executor unavailable or worktree paths differ on Windows" : undefined }, async () => {
|
|
730
|
+
execFileSync("git", ["init"], { cwd: tempDir, stdio: "ignore" });
|
|
731
|
+
execFileSync("git", ["config", "user.email", "test@example.com"], { cwd: tempDir });
|
|
732
|
+
execFileSync("git", ["config", "user.name", "Test User"], { cwd: tempDir });
|
|
733
|
+
fs.writeFileSync(path.join(tempDir, "base.txt"), "base\n", "utf-8");
|
|
734
|
+
execFileSync("git", ["add", "base.txt"], { cwd: tempDir });
|
|
735
|
+
execFileSync("git", ["commit", "-m", "base"], { cwd: tempDir, stdio: "ignore" });
|
|
736
|
+
mockPi.onCall({ output: "isolated", writeFiles: [{ path: "isolated.txt", content: "isolated\n" }] });
|
|
737
|
+
mockPi.onCall({ output: "shared", writeFiles: [{ path: "shared.txt", content: "shared\n" }] });
|
|
738
|
+
const executor = makeExecutor([makeAgent("builder")]);
|
|
739
|
+
|
|
740
|
+
const result = await executor.execute(
|
|
741
|
+
"scripted-workflow-worktree-default",
|
|
742
|
+
{
|
|
743
|
+
async: false,
|
|
744
|
+
worktree: true,
|
|
745
|
+
workflowScript: `
|
|
746
|
+
const isolated = await runs.run("isolated", { agent: "builder", task: "Isolated" });
|
|
747
|
+
const shared = await runs.run("shared", { agent: "builder", task: "Shared", worktree: false });
|
|
748
|
+
return { isolated: isolated.artifactPaths, shared: shared.artifactPaths };
|
|
749
|
+
`,
|
|
750
|
+
},
|
|
751
|
+
new AbortController().signal,
|
|
752
|
+
undefined,
|
|
753
|
+
makeMinimalCtx(tempDir),
|
|
754
|
+
);
|
|
755
|
+
|
|
756
|
+
assert.equal(result.isError, undefined, result.content[0]?.text ?? "workflow failed");
|
|
757
|
+
assert.equal(fs.existsSync(path.join(tempDir, "isolated.txt")), false);
|
|
758
|
+
assert.equal(fs.readFileSync(path.join(tempDir, "shared.txt"), "utf-8"), "shared\n");
|
|
759
|
+
const output = result.content[0]?.text ?? "";
|
|
760
|
+
const handoffPaths = [...output.matchAll(/"([^"\n]*\/handoffs\/[^"\n]+\.json)"/g)].map((match) => match[1]!);
|
|
761
|
+
assert.equal(handoffPaths.length, 1, output);
|
|
762
|
+
assert.equal(fs.existsSync(handoffPaths[0]!), true);
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
it("applies a workflow usage budget across scripted child launches", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
766
|
+
mockPi.onCall({ output: "first result" });
|
|
767
|
+
const executor = makeExecutor([makeAgent("echo")]);
|
|
768
|
+
|
|
769
|
+
const result = await executor.execute(
|
|
770
|
+
"scripted-workflow-usage-budget",
|
|
771
|
+
{
|
|
772
|
+
async: false,
|
|
773
|
+
workflowScript: `
|
|
774
|
+
await runs.run("first", { agent: "echo", task: "First task" });
|
|
775
|
+
await runs.run("second", { agent: "echo", task: "Second task" });
|
|
776
|
+
`,
|
|
777
|
+
usageBudget: { tokens: { hard: 10 } },
|
|
778
|
+
},
|
|
779
|
+
new AbortController().signal,
|
|
780
|
+
undefined,
|
|
781
|
+
makeMinimalCtx(tempDir),
|
|
782
|
+
);
|
|
783
|
+
|
|
784
|
+
assert.equal(result.isError, true);
|
|
785
|
+
assert.match(result.content[0]?.text ?? "", /Usage budget exhausted/);
|
|
786
|
+
assert.equal(result.details.mode, "workflow");
|
|
787
|
+
assert.equal(mockPi.callCount(), 1);
|
|
788
|
+
assert.equal(result.details.usageBudget?.exhausted, true);
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
it("admits a zero run-level tool budget only for marked structured delegated execution", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
382
792
|
const zeroBudget = { hard: 0, block: "*" as const };
|
|
383
793
|
const params = { agent: "echo", task: "Answer without tools", toolBudget: zeroBudget };
|
|
384
794
|
const ctx = makeMinimalCtx(tempDir);
|
|
@@ -394,32 +804,27 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
394
804
|
assert.equal(ordinary.isError, true);
|
|
395
805
|
assert.match(ordinary.content[0]?.text ?? "", /toolBudget\.hard must be an integer >= 1/);
|
|
396
806
|
|
|
397
|
-
const
|
|
398
|
-
"
|
|
807
|
+
const unmarkedDelegated = await executor.executeDelegated(
|
|
808
|
+
"unmarked-delegated-zero-budget",
|
|
399
809
|
params,
|
|
400
810
|
new AbortController().signal,
|
|
401
811
|
undefined,
|
|
402
812
|
ctx,
|
|
403
813
|
);
|
|
404
|
-
assert.equal(
|
|
405
|
-
assert.match(
|
|
814
|
+
assert.equal(unmarkedDelegated.isError, true);
|
|
815
|
+
assert.match(unmarkedDelegated.content[0]?.text ?? "", /toolBudget\.hard must be an integer >= 1/);
|
|
406
816
|
|
|
407
817
|
mockPi.onCall({ echoEnv: [TOOL_BUDGET_ENV, TOOL_BUDGET_ZERO_AUTH_ENV] });
|
|
408
|
-
const
|
|
409
|
-
"
|
|
818
|
+
const structuredDelegated = await executor.executeDelegated(
|
|
819
|
+
"structured-delegated-zero-budget",
|
|
410
820
|
{ ...params, delegatedAllowZeroToolBudget: true },
|
|
411
821
|
new AbortController().signal,
|
|
412
822
|
undefined,
|
|
413
823
|
ctx,
|
|
414
824
|
);
|
|
415
|
-
assert.equal(
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const delegatedOutputsDir = path.join(tempDir, ".pi-subagents", "artifacts", "outputs");
|
|
419
|
-
const delegatedRunDirs = fs.readdirSync(delegatedOutputsDir);
|
|
420
|
-
assert.equal(delegatedRunDirs.length, 1);
|
|
421
|
-
const persistedEnv = fs.readFileSync(path.join(delegatedOutputsDir, delegatedRunDirs[0]!, "result.md"), "utf-8");
|
|
422
|
-
const env = JSON.parse(persistedEnv) as Record<string, string>;
|
|
825
|
+
assert.equal(structuredDelegated.isError, undefined);
|
|
826
|
+
assert.deepEqual(structuredDelegated.details.toolBudget, zeroBudget);
|
|
827
|
+
const env = JSON.parse(structuredDelegated.content[0]?.text ?? "{}") as Record<string, string>;
|
|
423
828
|
assert.deepEqual(JSON.parse(env[TOOL_BUDGET_ENV] ?? "null"), zeroBudget);
|
|
424
829
|
assert.equal(env[TOOL_BUDGET_ZERO_AUTH_ENV], "1");
|
|
425
830
|
assert.equal(mockPi.callCount(), 1);
|
|
@@ -461,6 +866,29 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
461
866
|
assert.equal(mockPi.callCount(), 0);
|
|
462
867
|
});
|
|
463
868
|
|
|
869
|
+
it("rejects invalid verified acceptance before spawning", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
870
|
+
const executor = makeExecutor([makeAgent("echo")]);
|
|
871
|
+
const invalidPolicies = [
|
|
872
|
+
"verified",
|
|
873
|
+
{ level: "verified" },
|
|
874
|
+
{ level: "verified", verify: [] },
|
|
875
|
+
] as const;
|
|
876
|
+
|
|
877
|
+
for (const [index, acceptance] of invalidPolicies.entries()) {
|
|
878
|
+
const result = await executor.execute(
|
|
879
|
+
`invalid-verified-acceptance-${index}`,
|
|
880
|
+
{ agent: "echo", task: "Do work", acceptance: acceptance as never },
|
|
881
|
+
new AbortController().signal,
|
|
882
|
+
undefined,
|
|
883
|
+
makeMinimalCtx(tempDir),
|
|
884
|
+
);
|
|
885
|
+
|
|
886
|
+
assert.equal(result.isError, true);
|
|
887
|
+
assert.match(result.content[0]?.text ?? "", /(?:verified.*object form|verify.*at least one command)/i);
|
|
888
|
+
}
|
|
889
|
+
assert.equal(mockPi.callCount(), 0);
|
|
890
|
+
});
|
|
891
|
+
|
|
464
892
|
it("rejects unknown action strings at runtime", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
465
893
|
const executor = makeExecutor([makeAgent("echo")]);
|
|
466
894
|
|
|
@@ -547,101 +975,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
547
975
|
assert.equal(mockPi.callCount(), 2);
|
|
548
976
|
});
|
|
549
977
|
|
|
550
|
-
it("routes registered
|
|
551
|
-
mockPi.onCall({
|
|
552
|
-
steps: [
|
|
553
|
-
{ jsonl: [events.toolStart("read", { path: "package.json" })], delay: 20 },
|
|
554
|
-
{ jsonl: [events.toolEnd("read"), events.toolResult("read", "{}")], delay: 20 },
|
|
555
|
-
{ jsonl: [events.assistantMessage("registered first delegated call")] },
|
|
556
|
-
],
|
|
557
|
-
});
|
|
558
|
-
mockPi.onCall({ output: "registered second delegated call", delay: 100 });
|
|
559
|
-
const extensionEvents = createEventBus();
|
|
560
|
-
const runtimeHandlers = new Map<string, Array<(event: unknown, ctx: ReturnType<typeof makeMinimalCtx>) => void>>();
|
|
561
|
-
const fakePi = new Proxy({
|
|
562
|
-
events: extensionEvents,
|
|
563
|
-
on(event: string, handler: (event: unknown, ctx: ReturnType<typeof makeMinimalCtx>) => void) {
|
|
564
|
-
const handlers = runtimeHandlers.get(event) ?? [];
|
|
565
|
-
handlers.push(handler);
|
|
566
|
-
runtimeHandlers.set(event, handlers);
|
|
567
|
-
return () => runtimeHandlers.set(event, (runtimeHandlers.get(event) ?? []).filter((entry) => entry !== handler));
|
|
568
|
-
},
|
|
569
|
-
registerTool() {},
|
|
570
|
-
registerCommand() {},
|
|
571
|
-
registerShortcut() {},
|
|
572
|
-
registerMessageRenderer() {},
|
|
573
|
-
sendMessage() {},
|
|
574
|
-
getSessionName() { return undefined; },
|
|
575
|
-
}, {
|
|
576
|
-
get(target, prop) {
|
|
577
|
-
if (prop in target) return target[prop as keyof typeof target];
|
|
578
|
-
return () => undefined;
|
|
579
|
-
},
|
|
580
|
-
});
|
|
581
|
-
const ctx = {
|
|
582
|
-
...makeMinimalCtx(tempDir),
|
|
583
|
-
sessionManager: {
|
|
584
|
-
getSessionId: () => "registered-delegation-session",
|
|
585
|
-
getSessionFile: () => path.join(tempDir, "registered-delegation-session.jsonl"),
|
|
586
|
-
getEntries: () => [],
|
|
587
|
-
},
|
|
588
|
-
};
|
|
589
|
-
const responses: SubagentDelegationResponse[] = [];
|
|
590
|
-
const updates: SubagentDelegationUpdate[] = [];
|
|
591
|
-
extensionEvents.on(SUBAGENT_DELEGATION_RESPONSE_EVENT, (payload) => responses.push(payload as SubagentDelegationResponse));
|
|
592
|
-
extensionEvents.on(SUBAGENT_DELEGATION_UPDATE_EVENT, (payload) => updates.push(payload as SubagentDelegationUpdate));
|
|
593
|
-
|
|
594
|
-
try {
|
|
595
|
-
registerSubagentExtension(fakePi as never);
|
|
596
|
-
for (const handler of runtimeHandlers.get("session_start") ?? []) {
|
|
597
|
-
await handler({ reason: "startup" }, ctx);
|
|
598
|
-
}
|
|
599
|
-
extensionEvents.emit(SUBAGENT_DELEGATION_REQUEST_EVENT, {
|
|
600
|
-
version: SUBAGENT_DELEGATION_PROTOCOL_VERSION,
|
|
601
|
-
requestId: "registered-a",
|
|
602
|
-
agent: "builder",
|
|
603
|
-
task: "First registered delegated call",
|
|
604
|
-
context: "fresh",
|
|
605
|
-
cwd: tempDir,
|
|
606
|
-
agentContract: { version: 1 },
|
|
607
|
-
});
|
|
608
|
-
extensionEvents.emit(SUBAGENT_DELEGATION_REQUEST_EVENT, {
|
|
609
|
-
version: SUBAGENT_DELEGATION_PROTOCOL_VERSION,
|
|
610
|
-
requestId: "registered-b",
|
|
611
|
-
agent: "commentator",
|
|
612
|
-
task: "Second registered delegated call",
|
|
613
|
-
context: "fresh",
|
|
614
|
-
cwd: tempDir,
|
|
615
|
-
agentContract: { version: 1 },
|
|
616
|
-
});
|
|
617
|
-
|
|
618
|
-
const callDeadlineAt = Date.now() + 30_000;
|
|
619
|
-
while (mockPi.callCount() < 2 && Date.now() < callDeadlineAt) {
|
|
620
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
621
|
-
}
|
|
622
|
-
assert.equal(mockPi.callCount(), 2, "registered strict v1 requests should bypass the ordinary one-call guard");
|
|
623
|
-
|
|
624
|
-
const responseDeadlineAt = Date.now() + 30_000;
|
|
625
|
-
while (responses.length < 2 && Date.now() < responseDeadlineAt) {
|
|
626
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
627
|
-
}
|
|
628
|
-
assert.deepEqual(responses.map((response) => response.requestId).sort(), ["registered-a", "registered-b"]);
|
|
629
|
-
assert.ok(responses.every((response) => response.status === "completed"));
|
|
630
|
-
assert.deepEqual(responses.map((response) => response.output).sort(), [
|
|
631
|
-
"registered first delegated call",
|
|
632
|
-
"registered second delegated call",
|
|
633
|
-
]);
|
|
634
|
-
const toolResponse = responses.find((response) => response.output === "registered first delegated call");
|
|
635
|
-
assert.ok(toolResponse);
|
|
636
|
-
assert.equal(updates.some((update) => update.requestId === toolResponse.requestId && update.currentTool === "read"), true);
|
|
637
|
-
} finally {
|
|
638
|
-
for (const handler of runtimeHandlers.get("session_shutdown") ?? []) {
|
|
639
|
-
await handler({}, ctx);
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
});
|
|
643
|
-
|
|
644
|
-
it("routes registered strict v2 text delegation through the concurrent executor", async () => {
|
|
978
|
+
it("routes registered structured text delegation through the concurrent executor", async () => {
|
|
645
979
|
const literalJsonText = '{"looks":"json"}';
|
|
646
980
|
mockPi.onCall({
|
|
647
981
|
steps: [
|
|
@@ -668,7 +1002,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
668
1002
|
},
|
|
669
1003
|
],
|
|
670
1004
|
});
|
|
671
|
-
mockPi.onCall({ output: "registered
|
|
1005
|
+
mockPi.onCall({ output: "registered structured second node", delay: 100 });
|
|
672
1006
|
const extensionEvents = createEventBus();
|
|
673
1007
|
const runtimeHandlers = new Map<string, Array<(event: unknown, ctx: ReturnType<typeof makeMinimalCtx>) => void>>();
|
|
674
1008
|
const fakePi = new Proxy({
|
|
@@ -697,28 +1031,27 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
697
1031
|
getAvailable: () => [{ provider: "mock", id: "test-model", reasoning: true }],
|
|
698
1032
|
},
|
|
699
1033
|
sessionManager: {
|
|
700
|
-
getSessionId: () => "registered-delegation-
|
|
701
|
-
getSessionFile: () => path.join(tempDir, "registered-delegation-
|
|
1034
|
+
getSessionId: () => "registered-delegation-session",
|
|
1035
|
+
getSessionFile: () => path.join(tempDir, "registered-delegation-session.jsonl"),
|
|
702
1036
|
getEntries: () => [],
|
|
703
1037
|
},
|
|
704
1038
|
};
|
|
705
|
-
const started:
|
|
706
|
-
const responses:
|
|
1039
|
+
const started: SubagentDelegationStarted[] = [];
|
|
1040
|
+
const responses: SubagentDelegationResponse[] = [];
|
|
707
1041
|
extensionEvents.on(SUBAGENT_DELEGATION_STARTED_EVENT, (payload) => {
|
|
708
|
-
if ((payload as {
|
|
709
|
-
started.push(payload as
|
|
1042
|
+
if ((payload as { ownerRunId?: unknown }).ownerRunId === "owner-delegation") {
|
|
1043
|
+
started.push(payload as SubagentDelegationStarted);
|
|
710
1044
|
}
|
|
711
1045
|
});
|
|
712
1046
|
extensionEvents.on(SUBAGENT_DELEGATION_RESPONSE_EVENT, (payload) => {
|
|
713
|
-
if ((payload as {
|
|
714
|
-
responses.push(payload as
|
|
1047
|
+
if ((payload as { ownerRunId?: unknown }).ownerRunId === "owner-delegation") {
|
|
1048
|
+
responses.push(payload as SubagentDelegationResponse);
|
|
715
1049
|
}
|
|
716
1050
|
});
|
|
717
1051
|
|
|
718
1052
|
const firstRequest = {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
ownerRunId: "owner-v2",
|
|
1053
|
+
requestId: "registered-a",
|
|
1054
|
+
ownerRunId: "owner-delegation",
|
|
722
1055
|
nodeId: "node-a",
|
|
723
1056
|
agent: "builder",
|
|
724
1057
|
task: "Return literal JSON-looking text",
|
|
@@ -727,11 +1060,10 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
727
1060
|
model: "mock/test-model",
|
|
728
1061
|
thinking: "high",
|
|
729
1062
|
result: { kind: "text" },
|
|
730
|
-
} satisfies
|
|
1063
|
+
} satisfies SubagentDelegationRequest;
|
|
731
1064
|
const secondRequest = {
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
ownerRunId: "owner-v2",
|
|
1065
|
+
requestId: "registered-b",
|
|
1066
|
+
ownerRunId: "owner-delegation",
|
|
735
1067
|
nodeId: "node-b",
|
|
736
1068
|
agent: "commentator",
|
|
737
1069
|
task: "Run the second logical node",
|
|
@@ -740,7 +1072,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
740
1072
|
model: "mock/test-model",
|
|
741
1073
|
thinking: "high",
|
|
742
1074
|
result: { kind: "text" },
|
|
743
|
-
} satisfies
|
|
1075
|
+
} satisfies SubagentDelegationRequest;
|
|
744
1076
|
|
|
745
1077
|
try {
|
|
746
1078
|
registerSubagentExtension(fakePi as never);
|
|
@@ -754,10 +1086,10 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
754
1086
|
while (mockPi.callCount() < 2 && responses.length < 2 && Date.now() < callDeadlineAt) {
|
|
755
1087
|
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
756
1088
|
}
|
|
757
|
-
assert.equal(mockPi.callCount(), 2, `different
|
|
1089
|
+
assert.equal(mockPi.callCount(), 2, `different logical nodes should use the concurrent delegated execution path: ${JSON.stringify(responses)}`);
|
|
758
1090
|
assert.deepEqual(started.map(({ requestId, ownerRunId, nodeId }) => ({ requestId, ownerRunId, nodeId })).sort((a, b) => a.nodeId.localeCompare(b.nodeId)), [
|
|
759
|
-
{ requestId: "registered-
|
|
760
|
-
{ requestId: "registered-
|
|
1091
|
+
{ requestId: "registered-a", ownerRunId: "owner-delegation", nodeId: "node-a" },
|
|
1092
|
+
{ requestId: "registered-b", ownerRunId: "owner-delegation", nodeId: "node-b" },
|
|
761
1093
|
]);
|
|
762
1094
|
|
|
763
1095
|
const responseDeadlineAt = Date.now() + 30_000;
|
|
@@ -769,7 +1101,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
769
1101
|
const terminalResponses = responses.filter((response) => response.status !== "invalid_request");
|
|
770
1102
|
assert.equal(terminalResponses.length, 2);
|
|
771
1103
|
for (const response of terminalResponses) {
|
|
772
|
-
assert.equal(response.ownerRunId, "owner-
|
|
1104
|
+
assert.equal(response.ownerRunId, "owner-delegation");
|
|
773
1105
|
assert.equal(response.model, "mock/test-model:high");
|
|
774
1106
|
assert.equal(response.thinking, "high");
|
|
775
1107
|
assert.match(response.launchContractDigest ?? "", /^[0-9a-f]{64}$/);
|
|
@@ -795,7 +1127,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
795
1127
|
toolCalls: 1,
|
|
796
1128
|
});
|
|
797
1129
|
assert.equal(typeof literalResponse.usage?.durationMs, "number");
|
|
798
|
-
const plainResponse = terminalResponses.find((response) => response.result?.kind === "text" && response.result.text === "registered
|
|
1130
|
+
const plainResponse = terminalResponses.find((response) => response.result?.kind === "text" && response.result.text === "registered structured second node");
|
|
799
1131
|
assert.ok(plainResponse);
|
|
800
1132
|
} finally {
|
|
801
1133
|
for (const handler of runtimeHandlers.get("session_shutdown") ?? []) {
|
|
@@ -1077,6 +1409,45 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1077
1409
|
assert.deepEqual(result.details?.totalCost, { inputTokens: 100, outputTokens: 50, costUsd: 0.001 });
|
|
1078
1410
|
});
|
|
1079
1411
|
|
|
1412
|
+
it("emits resolved model and thinking for nested foreground starts", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
1413
|
+
mockPi.onCall({ output: "nested result" });
|
|
1414
|
+
const route = createNestedRoute("root-nested-model");
|
|
1415
|
+
const envPatch = {
|
|
1416
|
+
...nestedRouteEnv(route),
|
|
1417
|
+
[SUBAGENT_PARENT_RUN_ID_ENV]: "parent-run",
|
|
1418
|
+
[SUBAGENT_PARENT_CHILD_INDEX_ENV]: "2",
|
|
1419
|
+
[SUBAGENT_PARENT_DEPTH_ENV]: "1",
|
|
1420
|
+
};
|
|
1421
|
+
const savedEnv = Object.fromEntries(Object.keys(envPatch).map((key) => [key, process.env[key]]));
|
|
1422
|
+
try {
|
|
1423
|
+
Object.assign(process.env, envPatch);
|
|
1424
|
+
const executor = makeExecutor([makeAgent("echo", { model: "openai/gpt-5-mini", thinking: "high" })]);
|
|
1425
|
+
|
|
1426
|
+
const result = await executor.execute(
|
|
1427
|
+
"nested-model-start",
|
|
1428
|
+
{ agent: "echo", task: "Nested task" },
|
|
1429
|
+
new AbortController().signal,
|
|
1430
|
+
undefined,
|
|
1431
|
+
makeMinimalCtx(tempDir),
|
|
1432
|
+
);
|
|
1433
|
+
|
|
1434
|
+
assert.equal(result.isError, undefined);
|
|
1435
|
+
const records = fs.readdirSync(route.eventSink)
|
|
1436
|
+
.sort()
|
|
1437
|
+
.flatMap((name) => parseNestedEventRecords(fs.readFileSync(path.join(route.eventSink, name), "utf-8"), route));
|
|
1438
|
+
const started = records.find((record) => record.type === "subagent.nested.started");
|
|
1439
|
+
assert.equal(started?.child.model, "openai/gpt-5-mini");
|
|
1440
|
+
assert.equal(started?.child.thinking, "high");
|
|
1441
|
+
assert.deepEqual(started?.child.steps, [{ agent: "echo", status: "running", model: "openai/gpt-5-mini", thinking: "high" }]);
|
|
1442
|
+
} finally {
|
|
1443
|
+
for (const [key, value] of Object.entries(savedEnv)) {
|
|
1444
|
+
if (value === undefined) delete process.env[key];
|
|
1445
|
+
else process.env[key] = value;
|
|
1446
|
+
}
|
|
1447
|
+
fs.rmSync(path.dirname(route.eventSink), { recursive: true, force: true });
|
|
1448
|
+
}
|
|
1449
|
+
});
|
|
1450
|
+
|
|
1080
1451
|
it("blocks later foreground chain children when hard reported usage is exhausted", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
1081
1452
|
mockPi.onCall({ output: "first result" });
|
|
1082
1453
|
const executor = makeExecutor([makeAgent("echo"), makeAgent("second")]);
|
|
@@ -1131,10 +1502,10 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1131
1502
|
|
|
1132
1503
|
it("fails implementation runs that complete without mutation attempts", async () => {
|
|
1133
1504
|
mockPi.onCall({ output: "Validation:\nlet rawFilename = params.filename.trim();" });
|
|
1134
|
-
const agents = [makeAgent("
|
|
1505
|
+
const agents = [makeAgent("builder")];
|
|
1135
1506
|
const controlEvents: Array<{ message: string }> = [];
|
|
1136
1507
|
|
|
1137
|
-
const result = await runSync(tempDir, agents, "
|
|
1508
|
+
const result = await runSync(tempDir, agents, "builder", "Implement the approved file changes", {
|
|
1138
1509
|
runId: "guard-run",
|
|
1139
1510
|
onControlEvent: (event: { message: string }) => controlEvents.push(event),
|
|
1140
1511
|
});
|
|
@@ -1144,18 +1515,18 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1144
1515
|
assert.equal(result.finalOutput, "Validation:\nlet rawFilename = params.filename.trim();");
|
|
1145
1516
|
assert.equal(result.progress.status, "failed");
|
|
1146
1517
|
assert.deepEqual(controlEvents.map((event) => event.message), [
|
|
1147
|
-
"
|
|
1518
|
+
"builder completed without making edits for an implementation task",
|
|
1148
1519
|
]);
|
|
1149
1520
|
assert.deepEqual(result.controlEvents?.map((event) => event.message), [
|
|
1150
|
-
"
|
|
1521
|
+
"builder completed without making edits for an implementation task",
|
|
1151
1522
|
]);
|
|
1152
1523
|
});
|
|
1153
1524
|
|
|
1154
1525
|
it("agent contract v1 reports omitted acceptance separately without injecting a prompt", async () => {
|
|
1155
1526
|
mockPi.onCall({ output: "Plan only" });
|
|
1156
|
-
const agents = [makeAgent("
|
|
1527
|
+
const agents = [makeAgent("builder", { tools: ["read", "write"] })];
|
|
1157
1528
|
|
|
1158
|
-
const result = await runSync(tempDir, agents, "
|
|
1529
|
+
const result = await runSync(tempDir, agents, "builder", "Implement the approved file changes", {
|
|
1159
1530
|
runId: "v1-no-acceptance",
|
|
1160
1531
|
agentContract: { version: 1 },
|
|
1161
1532
|
});
|
|
@@ -1172,9 +1543,9 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1172
1543
|
|
|
1173
1544
|
it("agent contract v1 keeps acceptance rejection out of execution status", async () => {
|
|
1174
1545
|
mockPi.onCall({ output: "Done\n```acceptance-report\n{\"criteriaSatisfied\":[{\"id\":\"criterion-1\",\"status\":\"not-satisfied\",\"evidence\":\"no proof\"}]}\n```" });
|
|
1175
|
-
const agents = [makeAgent("
|
|
1546
|
+
const agents = [makeAgent("builder", { tools: ["read"], completionGuard: false })];
|
|
1176
1547
|
|
|
1177
|
-
const result = await runSync(tempDir, agents, "
|
|
1548
|
+
const result = await runSync(tempDir, agents, "builder", "Summarize the fix", {
|
|
1178
1549
|
runId: "v1-acceptance-reject",
|
|
1179
1550
|
agentContract: { version: 1 },
|
|
1180
1551
|
acceptance: { level: "checked", criteria: ["Return required proof"] },
|
|
@@ -1190,9 +1561,9 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1190
1561
|
|
|
1191
1562
|
it("agent contract v1 records explicit completion guard as an effect", async () => {
|
|
1192
1563
|
mockPi.onCall({ output: "Plan only" });
|
|
1193
|
-
const agents = [makeAgent("
|
|
1564
|
+
const agents = [makeAgent("builder", { tools: ["read", "write"], completionGuard: true })];
|
|
1194
1565
|
|
|
1195
|
-
const result = await runSync(tempDir, agents, "
|
|
1566
|
+
const result = await runSync(tempDir, agents, "builder", "Implement the approved file changes", {
|
|
1196
1567
|
runId: "v1-completion-effect",
|
|
1197
1568
|
agentContract: { version: 1 },
|
|
1198
1569
|
});
|
|
@@ -1224,7 +1595,10 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1224
1595
|
);
|
|
1225
1596
|
|
|
1226
1597
|
assert.equal(result.isError, undefined);
|
|
1227
|
-
|
|
1598
|
+
const child = result.details?.results?.[0];
|
|
1599
|
+
assert.deepEqual(child?.structuredOutput, { ok: true, note: "captured" });
|
|
1600
|
+
assert.match(child?.finalOutput ?? "", /"ok": true/);
|
|
1601
|
+
if (child?.artifactPaths?.outputPath) assert.match(fs.readFileSync(child.artifactPaths.outputPath, "utf-8"), /"note": "captured"/);
|
|
1228
1602
|
});
|
|
1229
1603
|
|
|
1230
1604
|
it("rejects structured output capture files that were not produced by the structured_output tool", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
@@ -1255,7 +1629,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1255
1629
|
|
|
1256
1630
|
const result = await executor.execute(
|
|
1257
1631
|
"single-schema-file-only-missing-path",
|
|
1258
|
-
{ agent: "echo", task: "Return structured data", outputSchema: { type: "object", required: ["ok"], properties: { ok: { type: "boolean" } } }, outputMode: "file-only",
|
|
1632
|
+
{ agent: "echo", task: "Return structured data", outputSchema: { type: "object", required: ["ok"], properties: { ok: { type: "boolean" } } }, outputMode: "file-only", acceptance: false, artifacts: false },
|
|
1259
1633
|
new AbortController().signal,
|
|
1260
1634
|
undefined,
|
|
1261
1635
|
makeMinimalCtx(tempDir),
|
|
@@ -1308,11 +1682,11 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1308
1682
|
|
|
1309
1683
|
it("returns captured output when the foreground executor fails an implementation run", async () => {
|
|
1310
1684
|
mockPi.onCall({ output: "Oracle review:\n- finding one\n- finding two" });
|
|
1311
|
-
const executor = makeExecutor([makeAgent("
|
|
1685
|
+
const executor = makeExecutor([makeAgent("commentator")]);
|
|
1312
1686
|
|
|
1313
1687
|
const result = await executor.execute(
|
|
1314
1688
|
"failed-single-output",
|
|
1315
|
-
{ agent: "
|
|
1689
|
+
{ agent: "commentator", task: "Implement the approved file changes" },
|
|
1316
1690
|
new AbortController().signal,
|
|
1317
1691
|
undefined,
|
|
1318
1692
|
makeMinimalCtx(tempDir),
|
|
@@ -1321,21 +1695,15 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1321
1695
|
const text = result.content[0]?.text ?? "";
|
|
1322
1696
|
assert.equal(result.isError, true);
|
|
1323
1697
|
assert.match(text, /completed without making edits/);
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
assert.match(text, /Output saved to: /);
|
|
1327
|
-
assert.doesNotMatch(text, /Output:\nOracle review:/);
|
|
1328
|
-
const outputsDir = path.join(tempDir, ".pi-subagents", "artifacts", "outputs");
|
|
1329
|
-
const runDirs = fs.readdirSync(outputsDir);
|
|
1330
|
-
assert.equal(runDirs.length, 1);
|
|
1331
|
-
assert.equal(fs.readFileSync(path.join(outputsDir, runDirs[0]!, "result.md"), "utf-8"), "Oracle review:\n- finding one\n- finding two");
|
|
1698
|
+
assert.match(text, /Output:\nOracle review:\n- finding one\n- finding two/);
|
|
1699
|
+
assert.match(text, /Output artifact: /);
|
|
1332
1700
|
});
|
|
1333
1701
|
|
|
1334
1702
|
it("fails future-tense implementation summaries when no mutation attempt occurred", async () => {
|
|
1335
1703
|
mockPi.onCall({ output: "I’ll do that now and report back after implementing." });
|
|
1336
|
-
const agents = [makeAgent("
|
|
1704
|
+
const agents = [makeAgent("builder")];
|
|
1337
1705
|
|
|
1338
|
-
const result = await runSync(tempDir, agents, "
|
|
1706
|
+
const result = await runSync(tempDir, agents, "builder", "Implement the approved fixes", {
|
|
1339
1707
|
runId: "guard-future-tense",
|
|
1340
1708
|
});
|
|
1341
1709
|
|
|
@@ -1393,9 +1761,9 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1393
1761
|
events.assistantMessage("Applied edit"),
|
|
1394
1762
|
],
|
|
1395
1763
|
});
|
|
1396
|
-
const agents = [makeAgent("
|
|
1764
|
+
const agents = [makeAgent("builder")];
|
|
1397
1765
|
|
|
1398
|
-
const result = await runSync(tempDir, agents, "
|
|
1766
|
+
const result = await runSync(tempDir, agents, "builder", "Implement the approved file changes", {
|
|
1399
1767
|
runId: "guard-success",
|
|
1400
1768
|
});
|
|
1401
1769
|
|
|
@@ -1406,14 +1774,13 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1406
1774
|
|
|
1407
1775
|
it("resolves explicit agent aliases to canonical execution names", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
1408
1776
|
mockPi.onCall({ output: "Implemented" });
|
|
1409
|
-
const executor = makeExecutor([makeAgent("
|
|
1777
|
+
const executor = makeExecutor([makeAgent("builder", { aliases: ["developer"], completionGuard: false })]);
|
|
1410
1778
|
|
|
1411
1779
|
const result = await executor.execute("single", { agent: "developer", task: "Implement" }, new AbortController().signal, undefined, makeMinimalCtx(tempDir));
|
|
1412
1780
|
|
|
1413
1781
|
assert.equal(result.isError, undefined);
|
|
1414
|
-
assert.equal(result.details?.results[0]?.agent, "
|
|
1415
|
-
assert.match(result.content[0]?.text ?? "", /
|
|
1416
|
-
assert.doesNotMatch(result.content[0]?.text ?? "", /^Implemented$/);
|
|
1782
|
+
assert.equal(result.details?.results[0]?.agent, "builder");
|
|
1783
|
+
assert.match(result.content[0]?.text ?? "", /Implemented/);
|
|
1417
1784
|
});
|
|
1418
1785
|
|
|
1419
1786
|
it("returns error for unknown agent", async () => {
|
|
@@ -1465,10 +1832,10 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1465
1832
|
events.assistantMessage("I need to retry the same edit."),
|
|
1466
1833
|
],
|
|
1467
1834
|
});
|
|
1468
|
-
const agents = [makeAgent("
|
|
1835
|
+
const agents = [makeAgent("builder")];
|
|
1469
1836
|
const controlEvents: NonNullable<RunSyncResult["controlEvents"]> = [];
|
|
1470
1837
|
|
|
1471
|
-
const result = await runSync(tempDir, agents, "
|
|
1838
|
+
const result = await runSync(tempDir, agents, "builder", "Implement the approved fixes", {
|
|
1472
1839
|
runId: "run-failures",
|
|
1473
1840
|
controlConfig: { enabled: true, failedToolAttemptsBeforeAttention: 3, notifyOn: ["active_long_running", "needs_attention"] },
|
|
1474
1841
|
onControlEvent: (event: NonNullable<RunSyncResult["controlEvents"]>[number]) => controlEvents.push(event),
|
|
@@ -1517,12 +1884,12 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1517
1884
|
it("retries a zero-activity startup exit on the same model", async () => {
|
|
1518
1885
|
mockPi.onCall({ exitCode: 1 });
|
|
1519
1886
|
mockPi.onCall({ output: "Recovered after startup race" });
|
|
1520
|
-
const agents = [makeAgent("
|
|
1887
|
+
const agents = [makeAgent("builder", {
|
|
1521
1888
|
model: "openai/gpt-5-mini",
|
|
1522
1889
|
fallbackModels: ["anthropic/claude-sonnet-4"],
|
|
1523
1890
|
})];
|
|
1524
1891
|
|
|
1525
|
-
const result = await runSync(tempDir, agents, "
|
|
1892
|
+
const result = await runSync(tempDir, agents, "builder", "Do work", {
|
|
1526
1893
|
runId: "startup-retry-sync",
|
|
1527
1894
|
acceptance: false,
|
|
1528
1895
|
});
|
|
@@ -1540,12 +1907,12 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1540
1907
|
it("does not retry a non-zero exit after tool activity", async () => {
|
|
1541
1908
|
mockPi.onCall({ jsonl: [events.toolStart("read", { path: "package.json" })], exitCode: 1 });
|
|
1542
1909
|
mockPi.onCall({ output: "must not run" });
|
|
1543
|
-
const agents = [makeAgent("
|
|
1910
|
+
const agents = [makeAgent("builder", {
|
|
1544
1911
|
model: "openai/gpt-5-mini",
|
|
1545
1912
|
fallbackModels: ["anthropic/claude-sonnet-4"],
|
|
1546
1913
|
})];
|
|
1547
1914
|
|
|
1548
|
-
const result = await runSync(tempDir, agents, "
|
|
1915
|
+
const result = await runSync(tempDir, agents, "builder", "Read a file", {
|
|
1549
1916
|
runId: "startup-no-retry-after-tool",
|
|
1550
1917
|
acceptance: false,
|
|
1551
1918
|
});
|
|
@@ -1555,28 +1922,28 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1555
1922
|
assert.equal(mockPi.callCount(), 1);
|
|
1556
1923
|
});
|
|
1557
1924
|
|
|
1558
|
-
it("does not retry
|
|
1559
|
-
mockPi.onCall({ signal: "
|
|
1925
|
+
it("does not retry non-SIGKILL signaled child exits", { skip: process.platform === "win32" ? "POSIX child signal reporting is unavailable on Windows" : false }, async () => {
|
|
1926
|
+
mockPi.onCall({ signal: "SIGTERM" });
|
|
1560
1927
|
mockPi.onCall({ output: "must not run" });
|
|
1561
|
-
const agents = [makeAgent("
|
|
1928
|
+
const agents = [makeAgent("builder", { model: "openai/gpt-5-mini" })];
|
|
1562
1929
|
|
|
1563
|
-
const result = await runSync(tempDir, agents, "
|
|
1930
|
+
const result = await runSync(tempDir, agents, "builder", "Do work", {
|
|
1564
1931
|
runId: "startup-no-retry-after-signal",
|
|
1565
1932
|
acceptance: false,
|
|
1566
1933
|
});
|
|
1567
1934
|
|
|
1568
1935
|
assert.equal(result.exitCode, 1);
|
|
1569
|
-
assert.equal(result.processSignal, "
|
|
1570
|
-
assert.equal(result.error, "Subagent process terminated by signal
|
|
1936
|
+
assert.equal(result.processSignal, "SIGTERM");
|
|
1937
|
+
assert.equal(result.error, "Subagent process terminated by signal SIGTERM.");
|
|
1571
1938
|
assert.equal(result.modelAttempts?.length, 1);
|
|
1572
1939
|
assert.equal(mockPi.callCount(), 1);
|
|
1573
1940
|
});
|
|
1574
1941
|
|
|
1575
1942
|
it("prefers signal termination errors over stderr tails", { skip: process.platform === "win32" ? "POSIX child signal reporting is unavailable on Windows" : false }, async () => {
|
|
1576
1943
|
mockPi.onCall({ stderr: "INFO benign startup line\n", signal: "SIGTERM" });
|
|
1577
|
-
const agents = [makeAgent("
|
|
1944
|
+
const agents = [makeAgent("builder", { model: "openai/gpt-5-mini" })];
|
|
1578
1945
|
|
|
1579
|
-
const result = await runSync(tempDir, agents, "
|
|
1946
|
+
const result = await runSync(tempDir, agents, "builder", "Do work", {
|
|
1580
1947
|
runId: "signal-error-over-stderr",
|
|
1581
1948
|
acceptance: false,
|
|
1582
1949
|
});
|
|
@@ -1590,9 +1957,9 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1590
1957
|
it("does not retry a child exit with raw stdout diagnostics", async () => {
|
|
1591
1958
|
mockPi.onCall({ stdoutRaw: "configuration failed before protocol startup\n", exitCode: 1 });
|
|
1592
1959
|
mockPi.onCall({ output: "must not run" });
|
|
1593
|
-
const agents = [makeAgent("
|
|
1960
|
+
const agents = [makeAgent("builder", { model: "openai/gpt-5-mini" })];
|
|
1594
1961
|
|
|
1595
|
-
const result = await runSync(tempDir, agents, "
|
|
1962
|
+
const result = await runSync(tempDir, agents, "builder", "Do work", {
|
|
1596
1963
|
runId: "startup-no-retry-after-stdout-diagnostic",
|
|
1597
1964
|
acceptance: false,
|
|
1598
1965
|
});
|
|
@@ -1605,12 +1972,12 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1605
1972
|
|
|
1606
1973
|
it("reports an actionable error after startup retries are exhausted", async () => {
|
|
1607
1974
|
mockPi.onCall({ exitCode: 1 });
|
|
1608
|
-
const agents = [makeAgent("
|
|
1975
|
+
const agents = [makeAgent("builder", {
|
|
1609
1976
|
model: "openai/gpt-5-mini",
|
|
1610
1977
|
fallbackModels: ["anthropic/claude-sonnet-4"],
|
|
1611
1978
|
})];
|
|
1612
1979
|
|
|
1613
|
-
const result = await runSync(tempDir, agents, "
|
|
1980
|
+
const result = await runSync(tempDir, agents, "builder", "Do work", {
|
|
1614
1981
|
runId: "startup-retry-exhausted-sync",
|
|
1615
1982
|
acceptance: false,
|
|
1616
1983
|
});
|
|
@@ -1688,6 +2055,19 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
1688
2055
|
assert.equal(getFinalOutput(result.messages), "你好 from fragmented JSON");
|
|
1689
2056
|
});
|
|
1690
2057
|
|
|
2058
|
+
it("projects an oversized turn_end aggregate without losing the foreground result", async () => {
|
|
2059
|
+
mockPi.onCall({ jsonl: [
|
|
2060
|
+
events.assistantMessage("result before oversized aggregate"),
|
|
2061
|
+
{ type: "turn_end", message: {}, toolResults: [{ content: "x".repeat(MAX_CHILD_PENDING_LINE_BYTES) }] },
|
|
2062
|
+
{ type: "agent_end", willRetry: false },
|
|
2063
|
+
{ type: "agent_settled" },
|
|
2064
|
+
] });
|
|
2065
|
+
const result = await runSync(tempDir, makeAgentConfigs(["echo"]), "echo", "Read parallel images", { acceptance: false });
|
|
2066
|
+
assert.equal(result.exitCode, 0);
|
|
2067
|
+
assert.equal(result.protocolError, undefined);
|
|
2068
|
+
assert.equal(getFinalOutput(result.messages), "result before oversized aggregate");
|
|
2069
|
+
});
|
|
2070
|
+
|
|
1691
2071
|
it("fails with protocol_output_limit when a child emits an oversized stdout line", async () => {
|
|
1692
2072
|
mockPi.onCall({ stdoutRaw: "x".repeat(MAX_CHILD_PENDING_LINE_BYTES + 1) });
|
|
1693
2073
|
const result = await runSync(tempDir, makeAgentConfigs(["echo"]), "echo", "Emit malformed output", { acceptance: false });
|
|
@@ -2183,9 +2563,9 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
2183
2563
|
events.assistantMessage("Found 2 files: file1.txt and file2.txt"),
|
|
2184
2564
|
],
|
|
2185
2565
|
});
|
|
2186
|
-
const agents = makeAgentConfigs(["
|
|
2566
|
+
const agents = makeAgentConfigs(["explorer"]);
|
|
2187
2567
|
|
|
2188
|
-
const result = await runSync(tempDir, agents, "
|
|
2568
|
+
const result = await runSync(tempDir, agents, "explorer", "List files", {});
|
|
2189
2569
|
|
|
2190
2570
|
assert.equal(result.exitCode, 0);
|
|
2191
2571
|
const output = getFinalOutput(result.messages);
|
|
@@ -2212,13 +2592,13 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
2212
2592
|
|
|
2213
2593
|
it("injects an agent-file-relative local skill into the foreground child prompt", async () => {
|
|
2214
2594
|
mockPi.onCall({ output: "Done" });
|
|
2215
|
-
const agentFile = path.join(tempDir, "agents", "nested", "
|
|
2595
|
+
const agentFile = path.join(tempDir, "agents", "nested", "builder.md");
|
|
2216
2596
|
const skillFile = path.join(path.dirname(agentFile), "skills", "local", "SKILL.md");
|
|
2217
2597
|
fs.mkdirSync(path.dirname(skillFile), { recursive: true });
|
|
2218
2598
|
fs.writeFileSync(skillFile, "---\ndescription: local skill description\n---\nLocal skill body\n", "utf-8");
|
|
2219
|
-
const agents = [makeAgent("
|
|
2599
|
+
const agents = [makeAgent("builder", { filePath: agentFile, skills: ["local"], skillPath: ["./skills"] })];
|
|
2220
2600
|
|
|
2221
|
-
const result = await runSync(tempDir, agents, "
|
|
2601
|
+
const result = await runSync(tempDir, agents, "builder", "Task", {});
|
|
2222
2602
|
|
|
2223
2603
|
assert.equal(result.exitCode, 0);
|
|
2224
2604
|
assert.deepEqual(result.skills, ["local"]);
|
|
@@ -2242,9 +2622,9 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
2242
2622
|
});
|
|
2243
2623
|
|
|
2244
2624
|
it("fails foreground runs on explicit unavailable pi-subagents skill requests without spawning", async () => {
|
|
2245
|
-
const agents = [makeAgent("
|
|
2625
|
+
const agents = [makeAgent("builder")];
|
|
2246
2626
|
|
|
2247
|
-
const result = await runSync(tempDir, agents, "
|
|
2627
|
+
const result = await runSync(tempDir, agents, "builder", "Task", { skills: ["pi-subagents"] });
|
|
2248
2628
|
|
|
2249
2629
|
assert.equal(result.exitCode, 1);
|
|
2250
2630
|
assert.equal(result.error, "Skills not found: pi-subagents");
|
|
@@ -2252,9 +2632,9 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
2252
2632
|
});
|
|
2253
2633
|
|
|
2254
2634
|
it("fails foreground runs when an agent default requests pi-subagents skill", async () => {
|
|
2255
|
-
const agents = [makeAgent("
|
|
2635
|
+
const agents = [makeAgent("builder", { skills: ["pi-subagents"] })];
|
|
2256
2636
|
|
|
2257
|
-
const result = await runSync(tempDir, agents, "
|
|
2637
|
+
const result = await runSync(tempDir, agents, "builder", "Task", {});
|
|
2258
2638
|
|
|
2259
2639
|
assert.equal(result.exitCode, 1);
|
|
2260
2640
|
assert.equal(result.error, "Skills not found: pi-subagents");
|
|
@@ -2297,29 +2677,6 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
2297
2677
|
assert.ok(!JSON.stringify(result.launchResolvedExtensions).includes(tempDir), "projection should not expose raw extension paths");
|
|
2298
2678
|
});
|
|
2299
2679
|
|
|
2300
|
-
it("does not write debug artifacts by default", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
2301
|
-
mockPi.onCall({ output: "plain result" });
|
|
2302
|
-
const executor = makeExecutor([makeAgent("echo")]);
|
|
2303
|
-
const result = await executor.execute(
|
|
2304
|
-
"default-no-artifacts",
|
|
2305
|
-
{ agent: "echo", task: "Run without debug artifacts", runId: "default-no-artifacts" },
|
|
2306
|
-
new AbortController().signal,
|
|
2307
|
-
undefined,
|
|
2308
|
-
makeMinimalCtx(tempDir),
|
|
2309
|
-
);
|
|
2310
|
-
|
|
2311
|
-
assert.equal(result.isError, undefined);
|
|
2312
|
-
assert.equal(result.details?.artifacts, undefined);
|
|
2313
|
-
// Generated durable output persists even with artifacts:false; per-child
|
|
2314
|
-
// debug artifacts (_input/_output/_meta/_transcript) remain opt-in.
|
|
2315
|
-
const defaultOutputsDir = path.join(tempDir, ".pi-subagents", "artifacts", "outputs");
|
|
2316
|
-
const defaultRunDirs = fs.readdirSync(defaultOutputsDir);
|
|
2317
|
-
assert.equal(defaultRunDirs.length, 1);
|
|
2318
|
-
const defaultRunDir = path.join(defaultOutputsDir, defaultRunDirs[0]!);
|
|
2319
|
-
assert.equal(fs.readFileSync(path.join(defaultRunDir, "result.md"), "utf-8"), "plain result");
|
|
2320
|
-
assert.deepEqual(fs.readdirSync(defaultRunDir), ["result.md"]);
|
|
2321
|
-
});
|
|
2322
|
-
|
|
2323
2680
|
it("routes foreground artifacts to the configured session directory", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
2324
2681
|
mockPi.onCall({ output: "session artifact result" });
|
|
2325
2682
|
const sessionFile = path.join(tempDir, "sessions", "parent-session", "session.jsonl");
|
|
@@ -2329,7 +2686,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
2329
2686
|
|
|
2330
2687
|
const result = await executor.execute(
|
|
2331
2688
|
"session-artifact-dir",
|
|
2332
|
-
{ agent: "echo", task: "Write session-scoped artifacts", runId: "session-artifacts"
|
|
2689
|
+
{ agent: "echo", task: "Write session-scoped artifacts", runId: "session-artifacts" },
|
|
2333
2690
|
new AbortController().signal,
|
|
2334
2691
|
undefined,
|
|
2335
2692
|
ctx,
|
|
@@ -2343,6 +2700,30 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
2343
2700
|
assert.equal(fs.existsSync(path.join(tempDir, ".pi-subagents", "artifacts")), false);
|
|
2344
2701
|
});
|
|
2345
2702
|
|
|
2703
|
+
for (const artifactDir of ["session", "temp"] as const) {
|
|
2704
|
+
it(`keeps foreground chain scratch files out of the project for artifactDir=${artifactDir}`, { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
2705
|
+
mockPi.onCall({ output: `${artifactDir} chain result` });
|
|
2706
|
+
const sessionFile = path.join(tempDir, "sessions", "parent-session", "session.jsonl");
|
|
2707
|
+
const ctx = makeMinimalCtx(tempDir);
|
|
2708
|
+
ctx.sessionManager.getSessionFile = () => sessionFile;
|
|
2709
|
+
const executor = makeExecutor([makeAgent("echo")], { artifactDir });
|
|
2710
|
+
|
|
2711
|
+
const result = await executor.execute(
|
|
2712
|
+
`${artifactDir}-chain-artifact-dir`,
|
|
2713
|
+
{ chain: [{ agent: "echo", task: "Run without project-local scratch files in {chain_dir}" }] },
|
|
2714
|
+
new AbortController().signal,
|
|
2715
|
+
undefined,
|
|
2716
|
+
ctx,
|
|
2717
|
+
);
|
|
2718
|
+
|
|
2719
|
+
const taskArg = readCallArgs().at(-1) ?? "";
|
|
2720
|
+
assert.equal(result.isError, undefined);
|
|
2721
|
+
assert.ok(taskArg.includes(`${CHAIN_RUNS_DIR}${path.sep}`), taskArg);
|
|
2722
|
+
assert.equal(fs.existsSync(path.join(tempDir, ".pi-subagents", "chain-runs")), false);
|
|
2723
|
+
assert.equal(fs.existsSync(path.join(tempDir, ".pi-subagents", "artifacts")), false);
|
|
2724
|
+
});
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2346
2727
|
it("writes a failure stub to foreground output artifacts when no output was produced", async () => {
|
|
2347
2728
|
mockPi.onCall({ output: "", stderr: "model unavailable", exitCode: 1 });
|
|
2348
2729
|
const artifactsDir = path.join(tempDir, "artifacts-failed-output");
|
|
@@ -2578,7 +2959,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
2578
2959
|
{
|
|
2579
2960
|
action: "append-step",
|
|
2580
2961
|
id: "missing-run",
|
|
2581
|
-
|
|
2962
|
+
step: { agent: "echo", task: "Review the previous work", acceptance: { level: "reviewed" } },
|
|
2582
2963
|
},
|
|
2583
2964
|
new AbortController().signal,
|
|
2584
2965
|
undefined,
|
|
@@ -2653,6 +3034,39 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
2653
3034
|
assert.equal(agentResult.details?.timeoutMs, 4_000);
|
|
2654
3035
|
});
|
|
2655
3036
|
|
|
3037
|
+
it("runs omitted async launches in the background when the global default is enabled", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
3038
|
+
const executor = makeExecutor([makeAgent("echo")], {}, true);
|
|
3039
|
+
|
|
3040
|
+
const result = await executor.execute(
|
|
3041
|
+
"global-async-default",
|
|
3042
|
+
{ agent: "echo", task: "Task" },
|
|
3043
|
+
new AbortController().signal,
|
|
3044
|
+
undefined,
|
|
3045
|
+
makeMinimalCtx(tempDir),
|
|
3046
|
+
);
|
|
3047
|
+
|
|
3048
|
+
assert.equal(result.isError, undefined);
|
|
3049
|
+
assert.match(result.content[0]?.text ?? "", /Async:/);
|
|
3050
|
+
assert.equal(typeof result.details?.asyncId, "string");
|
|
3051
|
+
});
|
|
3052
|
+
|
|
3053
|
+
it("keeps omitted async launches foreground when the global default is disabled", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
3054
|
+
mockPi.onCall({ output: "config foreground default finished" });
|
|
3055
|
+
const executor = makeExecutor([makeAgent("echo")], {}, false);
|
|
3056
|
+
|
|
3057
|
+
const result = await executor.execute(
|
|
3058
|
+
"global-foreground-opt-out",
|
|
3059
|
+
{ agent: "echo", task: "Task" },
|
|
3060
|
+
new AbortController().signal,
|
|
3061
|
+
undefined,
|
|
3062
|
+
makeMinimalCtx(tempDir),
|
|
3063
|
+
);
|
|
3064
|
+
|
|
3065
|
+
assert.equal(result.isError, undefined);
|
|
3066
|
+
assert.match(result.content[0]?.text ?? "", /config foreground default finished/);
|
|
3067
|
+
assert.equal(result.details?.asyncId, undefined);
|
|
3068
|
+
});
|
|
3069
|
+
|
|
2656
3070
|
it("applies agent frontmatter defaults to single-agent launches", { skip: !createSubagentExecutor ? "executor not importable" : undefined }, async () => {
|
|
2657
3071
|
const executor = makeExecutor([
|
|
2658
3072
|
makeAgent("echo", {
|
|
@@ -2723,8 +3137,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
2723
3137
|
);
|
|
2724
3138
|
|
|
2725
3139
|
assert.equal(result.isError, undefined);
|
|
2726
|
-
assert.match(result.content[0]?.text ?? "", /
|
|
2727
|
-
assert.doesNotMatch(result.content[0]?.text ?? "", /agent foreground default finished/);
|
|
3140
|
+
assert.match(result.content[0]?.text ?? "", /agent foreground default finished/);
|
|
2728
3141
|
assert.equal(result.details?.asyncId, undefined);
|
|
2729
3142
|
});
|
|
2730
3143
|
|
|
@@ -2753,8 +3166,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
2753
3166
|
);
|
|
2754
3167
|
|
|
2755
3168
|
assert.equal(result.isError, undefined);
|
|
2756
|
-
assert.match(result.content[0]?.text ?? "", /
|
|
2757
|
-
assert.doesNotMatch(result.content[0]?.text ?? "", /explicit foreground finished/);
|
|
3169
|
+
assert.match(result.content[0]?.text ?? "", /explicit foreground finished/);
|
|
2758
3170
|
assert.equal(result.details?.asyncId, undefined);
|
|
2759
3171
|
});
|
|
2760
3172
|
|
|
@@ -2898,8 +3310,8 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
2898
3310
|
});
|
|
2899
3311
|
|
|
2900
3312
|
mockPi.onCall({ echoEnv: envKeys });
|
|
2901
|
-
const nonFanoutAgents = [makeAgent("
|
|
2902
|
-
const nonFanout = await runSync(tempDir, nonFanoutAgents, "
|
|
3313
|
+
const nonFanoutAgents = [makeAgent("builder", { tools: ["read"] })];
|
|
3314
|
+
const nonFanout = await runSync(tempDir, nonFanoutAgents, "builder", "Task", { runId: "non-fanout-run" });
|
|
2903
3315
|
assert.equal(nonFanout.exitCode, 0);
|
|
2904
3316
|
assert.deepEqual(JSON.parse(nonFanout.finalOutput ?? "{}"), {
|
|
2905
3317
|
SELESAI_SUBAGENT_FANOUT_CHILD: "0",
|
|
@@ -2945,9 +3357,9 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
2945
3357
|
|
|
2946
3358
|
it("fails with actionable diagnostics when a requested extension tool is not loaded", async () => {
|
|
2947
3359
|
mockPi.onCall({ output: "Model incorrectly claimed success", missingTools: ["fixture_search"] });
|
|
2948
|
-
const agents = [makeAgent("extension-
|
|
3360
|
+
const agents = [makeAgent("extension-builder", { tools: ["read", "fixture_search"], fallbackModels: ["mock/fallback-model"] })];
|
|
2949
3361
|
|
|
2950
|
-
const result = await runSync(tempDir, agents, "extension-
|
|
3362
|
+
const result = await runSync(tempDir, agents, "extension-builder", "Use fixture search", { runId: "missing-extension-tool" });
|
|
2951
3363
|
|
|
2952
3364
|
assert.equal(result.exitCode, 1);
|
|
2953
3365
|
assert.match(result.error ?? "", /requested unavailable child tools: fixture_search/);
|
|
@@ -3173,9 +3585,9 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
3173
3585
|
mockAssistantMessage("final wrapped output", "stop"),
|
|
3174
3586
|
],
|
|
3175
3587
|
});
|
|
3176
|
-
const agents = makeAgentConfigs(["
|
|
3588
|
+
const agents = makeAgentConfigs(["builder"]);
|
|
3177
3589
|
|
|
3178
|
-
const result = await runSync(tempDir, agents, "
|
|
3590
|
+
const result = await runSync(tempDir, agents, "builder", "Use the final grace turn to wrap up.", {
|
|
3179
3591
|
turnBudget: { maxTurns: 1, graceTurns: 1 },
|
|
3180
3592
|
runId: "foreground-turn-budget-soft",
|
|
3181
3593
|
});
|
|
@@ -3189,7 +3601,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
3189
3601
|
assert.match(result.finalOutput ?? "", /final wrapped output/);
|
|
3190
3602
|
});
|
|
3191
3603
|
|
|
3192
|
-
it("
|
|
3604
|
+
it("preserves a clean foreground completion after turn-budget work defers", async () => {
|
|
3193
3605
|
mockPi.onCall({
|
|
3194
3606
|
steps: [
|
|
3195
3607
|
{
|
|
@@ -3208,7 +3620,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
3208
3620
|
},
|
|
3209
3621
|
],
|
|
3210
3622
|
});
|
|
3211
|
-
const agents = makeAgentConfigs(["
|
|
3623
|
+
const agents = makeAgentConfigs(["builder"]);
|
|
3212
3624
|
const snapshots: Array<{
|
|
3213
3625
|
turnBudget?: { outcome?: string; terminationDeferredAtTurn?: number };
|
|
3214
3626
|
turnBudgetExceeded?: boolean;
|
|
@@ -3217,7 +3629,7 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
3217
3629
|
status?: string;
|
|
3218
3630
|
}> = [];
|
|
3219
3631
|
|
|
3220
|
-
const result = await runSync(tempDir, agents, "
|
|
3632
|
+
const result = await runSync(tempDir, agents, "builder", "Finish active tool work before enforcing the hard limit.", {
|
|
3221
3633
|
turnBudget: { maxTurns: 1, graceTurns: 0 },
|
|
3222
3634
|
runId: "foreground-turn-budget-deferred",
|
|
3223
3635
|
onUpdate(update: { details?: { results?: Array<{ turnBudget?: { outcome?: string; terminationDeferredAtTurn?: number }; turnBudgetExceeded?: boolean; error?: string }>; progress?: Array<{ currentTool?: string; status?: string }> } }) {
|
|
@@ -3239,8 +3651,9 @@ describe("single sync execution", { skip: !available ? "pi packages not availabl
|
|
|
3239
3651
|
assert.equal(duringTool.turnBudgetExceeded, undefined);
|
|
3240
3652
|
assert.equal(duringTool.error, undefined);
|
|
3241
3653
|
assert.equal(duringTool.status, "running");
|
|
3242
|
-
assert.equal(result.
|
|
3243
|
-
assert.equal(result.
|
|
3654
|
+
assert.equal(result.exitCode, 0);
|
|
3655
|
+
assert.equal(result.turnBudgetExceeded, undefined);
|
|
3656
|
+
assert.equal(result.turnBudget?.outcome, "wrap-up-requested");
|
|
3244
3657
|
assert.equal(result.turnBudget?.turnCount, 2);
|
|
3245
3658
|
assert.match(result.finalOutput ?? "", /safe assistant boundary reached/);
|
|
3246
3659
|
assert.ok(result.messages?.some((message) => message.role === "toolResult" && JSON.stringify(message.content).includes("build completed")));
|