@selesai/code 0.8.3 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/dist/extensions/handoff-new.ts +1 -1
- package/dist/extensions/inline-skills.test.ts +1 -0
- package/dist/extensions/inline-skills.ts +1 -1
- package/dist/extensions/pi-intercom/CHANGELOG.md +25 -3
- package/dist/extensions/pi-intercom/README.md +34 -15
- package/dist/extensions/pi-intercom/broker/broker.ts +38 -141
- package/dist/extensions/pi-intercom/broker/client-liveness.test.ts +173 -0
- package/dist/extensions/pi-intercom/broker/client.ts +78 -150
- package/dist/extensions/pi-intercom/broker/extension-state.ts +18 -7
- package/dist/extensions/pi-intercom/broker/framing.ts +1 -1
- package/dist/extensions/pi-intercom/broker/protocol.ts +182 -0
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +106 -7
- package/dist/extensions/pi-intercom/broker/spawn.ts +37 -11
- package/dist/extensions/pi-intercom/config.test.ts +8 -10
- package/dist/extensions/pi-intercom/config.ts +3 -3
- package/dist/extensions/pi-intercom/index.ts +218 -147
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +891 -103
- package/dist/extensions/pi-intercom/package.json +3 -2
- package/dist/extensions/pi-intercom/project-agent.test.ts +145 -0
- package/dist/extensions/pi-intercom/project-agent.ts +324 -0
- package/dist/extensions/pi-intercom/reply-tracker.test.ts +86 -1
- package/dist/extensions/pi-intercom/reply-tracker.ts +41 -9
- package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +49 -9
- package/dist/extensions/pi-intercom/test/inline-message.test.ts +9 -9
- package/dist/extensions/pi-intercom/types.ts +3 -1
- package/dist/extensions/pi-intercom/ui/inline-message.ts +7 -7
- package/dist/extensions/pi-powerline-footer/.github/workflows/test.yml +2 -2
- package/dist/extensions/pi-powerline-footer/CHANGELOG.md +14 -1
- package/dist/extensions/pi-powerline-footer/README.md +20 -37
- package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +40 -25
- package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +149 -18
- package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +1 -0
- package/dist/extensions/pi-powerline-footer/editor-performance.ts +99 -0
- package/dist/extensions/pi-powerline-footer/git-status.ts +15 -0
- package/dist/extensions/pi-powerline-footer/index.ts +394 -317
- package/dist/extensions/pi-powerline-footer/package.json +1 -1
- package/dist/extensions/pi-powerline-footer/powerline-config.ts +0 -12
- package/dist/extensions/pi-powerline-footer/queue/store.ts +8 -56
- package/dist/extensions/pi-powerline-footer/queue/types.ts +1 -2
- package/dist/extensions/pi-powerline-footer/segments.ts +0 -3
- package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +231 -1
- package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +5 -19
- package/dist/extensions/pi-powerline-footer/tests/editor-performance.test.ts +43 -0
- package/dist/extensions/pi-powerline-footer/tests/git-optional-locks.test.ts +64 -0
- package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +116 -0
- package/dist/extensions/pi-powerline-footer/tests/queue-store.test.ts +19 -149
- package/dist/extensions/pi-powerline-footer/tests/remaining-regressions.test.ts +6 -0
- package/dist/extensions/pi-powerline-footer/tests/usage-display.test.ts +5 -5
- package/dist/extensions/pi-powerline-footer/types.ts +1 -2
- package/dist/extensions/pi-subagents/CHANGELOG.md +202 -14
- package/dist/extensions/pi-subagents/README.md +65 -1614
- package/dist/extensions/pi-subagents/agents/builder.md +0 -1
- package/dist/extensions/pi-subagents/agents/delegate.md +14 -0
- package/dist/extensions/pi-subagents/agents/oracle.md +74 -0
- package/dist/extensions/pi-subagents/agents/reviewer.md +78 -0
- package/dist/extensions/pi-subagents/agents/scout.md +50 -0
- package/dist/extensions/pi-subagents/agents/worker.md +58 -0
- package/dist/extensions/pi-subagents/docs/configuration.md +328 -0
- package/dist/extensions/pi-subagents/docs/extension-api.md +308 -0
- package/dist/extensions/pi-subagents/docs/missions.md +119 -0
- package/dist/extensions/pi-subagents/docs/models.md +192 -0
- package/dist/extensions/pi-subagents/docs/observability.md +174 -0
- package/dist/extensions/pi-subagents/docs/tool-reference.md +343 -0
- package/dist/extensions/pi-subagents/docs/watchdog.md +176 -0
- package/dist/extensions/pi-subagents/docs/workflows.md +163 -0
- package/dist/extensions/pi-subagents/inspector-runner.mjs +11 -0
- package/dist/extensions/pi-subagents/package-lock.json +152 -194
- package/dist/extensions/pi-subagents/package.json +9 -10
- package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
- package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
- package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
- package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
- package/dist/extensions/pi-subagents/prompts/review-loop.md +12 -12
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +22 -17
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +77 -72
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +177 -69
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +28 -8
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +88 -66
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +3 -3
- package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +624 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +29 -16
- package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +7 -3
- package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
- package/dist/extensions/pi-subagents/src/agents/skills.ts +2 -9
- package/dist/extensions/pi-subagents/src/api/delegation.ts +1 -2
- package/dist/extensions/pi-subagents/src/api/project-panes.ts +30 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +56 -8
- package/dist/extensions/pi-subagents/src/extension/control-notices.ts +2 -2
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +51 -26
- package/dist/extensions/pi-subagents/src/extension/index.ts +119 -50
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +72 -0
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +15 -15
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +58 -21
- package/dist/extensions/pi-subagents/src/extension/subagent-guide.ts +39 -0
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +47 -25
- package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +3 -3
- package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +460 -64
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +102 -4
- package/dist/extensions/pi-subagents/src/missions/actions.ts +77 -16
- package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +162 -0
- package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +65 -14
- package/dist/extensions/pi-subagents/src/missions/store.ts +147 -5
- package/dist/extensions/pi-subagents/src/missions/types.ts +58 -3
- package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +256 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -8
- package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +42 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +41 -7
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +294 -134
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +19 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +39 -6
- package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +267 -0
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +145 -60
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +114 -6
- package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +33 -0
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +68 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +71 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +21 -2
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +42 -14
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +12 -24
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +7 -5
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +152 -68
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +17 -4
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +146 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +21 -5
- package/dist/extensions/pi-subagents/src/runs/foreground/async-dismiss-action.ts +85 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +43 -18
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +20 -19
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +3 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +55 -26
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +137 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +798 -112
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +187 -0
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +165 -22
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +9 -5
- package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +12 -2
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +1 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +465 -116
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +10 -10
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +189 -32
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +3 -3
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +128 -3
- package/dist/extensions/pi-subagents/src/shared/display-text.ts +100 -0
- package/dist/extensions/pi-subagents/src/shared/file-coalescer.ts +9 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +13 -0
- package/dist/extensions/pi-subagents/src/shared/formatters.ts +4 -6
- package/dist/extensions/pi-subagents/src/shared/prompt-resources.ts +51 -0
- package/dist/extensions/pi-subagents/src/shared/settings.ts +41 -11
- package/dist/extensions/pi-subagents/src/shared/types.ts +100 -3
- package/dist/extensions/pi-subagents/src/shared/utf8.ts +11 -0
- package/dist/extensions/pi-subagents/src/shared/utils.ts +43 -33
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -14
- package/dist/extensions/pi-subagents/src/slash/inline-subagents.ts +1 -1
- package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +14 -12
- package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +176 -21
- package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +12 -6
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +52 -18
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +36 -5
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +30 -16
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +151 -31
- package/dist/extensions/pi-subagents/src/tui/render.ts +201 -68
- package/dist/extensions/pi-subagents/src/types/pi-runtime-compat.d.ts +0 -6
- package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +5 -4
- package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +0 -1
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +313 -25
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +15 -15
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/cli.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/register-bedrock.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/restore-sandbox-env.d.ts +17 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/args.d.ts +57 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/config-selector.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/file-processor.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/initial-message.d.ts +18 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/list-models.d.ts +9 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/project-trust.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/session-picker.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/startup-ui.d.ts +20 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/config.d.ts +96 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-runtime.d.ts +119 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-services.d.ts +85 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session.d.ts +620 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-guidance.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-storage.d.ts +56 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/bash-executor.d.ts +32 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/cache-stats.d.ts +49 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/branch-summarization.d.ts +93 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/compaction.d.ts +128 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/index.d.ts +7 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/utils.d.ts +38 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/defaults.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/diagnostics.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/event-bus.d.ts +9 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/exec.d.ts +29 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/experimental.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/ansi-to-html.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/index.d.ts +37 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/tool-renderer.d.ts +34 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/index.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/loader.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/runner.d.ts +171 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +1261 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/wrapper.d.ts +20 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/footer-data-provider.d.ts +54 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/http-dispatcher.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/index.d.ts +13 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/keybindings.d.ts +358 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/messages.d.ts +77 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-config.d.ts +512 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-registry.d.ts +43 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-resolver.d.ts +121 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-runtime.d.ts +82 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/models-store.d.ts +17 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/output-guard.d.ts +7 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/package-manager.d.ts +210 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/project-trust.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/prompt-templates.d.ts +54 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-attribution.d.ts +4 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-composer.d.ts +55 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/radius.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/remote-catalog-provider.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resolve-config-value.d.ts +30 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resource-loader.d.ts +206 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/runtime-credentials.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/sdk.d.ts +106 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-cwd.d.ts +19 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-manager.d.ts +356 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/settings-manager.d.ts +297 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/skills.d.ts +60 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/slash-commands.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/source-info.d.ts +18 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/system-prompt.d.ts +28 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/telemetry.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/timings.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/bash.d.ts +68 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit-diff.d.ts +106 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit.d.ts +51 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/file-mutation-queue.d.ts +6 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/find.d.ts +35 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/grep.d.ts +37 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/index.d.ts +40 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/ls.d.ts +37 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/output-accumulator.d.ts +52 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/path-utils.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/read.d.ts +35 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/render-utils.d.ts +24 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/truncate.d.ts +70 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/write.d.ts +26 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/trust-manager.d.ts +36 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/usage-totals.d.ts +19 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/index.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/client.d.ts +61 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/huggingface.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/index.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/provider.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/ui.d.ts +42 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/index.d.ts +35 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/main.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/migrations.d.ts +33 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/index.d.ts +9 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/armin.d.ts +34 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/assistant-message.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bash-execution.d.ts +34 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/config-selector.d.ts +102 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-editor.d.ts +21 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-entry.d.ts +19 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-message.d.ts +20 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/daxnuts.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/diff.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-editor.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-input.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-selector.d.ts +26 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/footer.d.ts +32 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/index.d.ts +34 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/keybinding-hints.d.ts +13 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/login-dialog.d.ts +52 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/model-selector.d.ts +54 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/oauth-selector.d.ts +33 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/scoped-models-selector.d.ts +42 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector.d.ts +95 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/settings-selector.d.ts +77 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/status-indicator.d.ts +28 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/theme-selector.d.ts +11 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tool-execution.d.ts +63 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tree-selector.d.ts +94 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/trust-selector.d.ts +23 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message.d.ts +14 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/interactive-mode.d.ts +394 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/model-search.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme-controller.d.ts +29 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme.d.ts +120 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/print-mode.d.ts +28 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/jsonl.d.ts +17 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-client.d.ts +246 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-mode.d.ts +20 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-types.d.ts +457 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/package-manager-cli.d.ts +8 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/rpc-entry.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/ansi.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/changelog.d.ts +22 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/child-process.d.ts +18 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-image.d.ts +11 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-native.d.ts +11 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard.d.ts +4 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/deprecation.d.ts +4 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/exif-orientation.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/frontmatter.d.ts +8 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/fs-watch.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/git.d.ts +26 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/html.d.ts +7 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-convert.d.ts +10 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-process.d.ts +18 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-core.d.ts +30 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-worker.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize.d.ts +16 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/json.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/mime.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/open-browser.d.ts +9 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/paths.d.ts +31 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/photon.d.ts +21 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/pi-user-agent.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/shell.d.ts +31 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/sleep.d.ts +5 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/syntax-highlight.d.ts +12 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/tools-manager.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/version-check.d.ts +15 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/windows-self-update.d.ts +3 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/index.mjs +84 -0
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/package.json +14 -0
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +15 -15
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +411 -300
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +309 -123
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +99 -56
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +62 -16
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +159 -108
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +56 -56
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +114 -45
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +57 -19
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +106 -106
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +416 -106
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +287 -17
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +809 -87
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +107 -20
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +26 -6
- package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +1 -1
- package/dist/extensions/pi-subagents/test/support/ts-loader.mjs +0 -8
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +163 -58
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
- package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +334 -248
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +52 -52
- package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +26 -26
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +99 -99
- package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +160 -0
- package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +43 -1
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +36 -10
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +387 -30
- package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +94 -40
- package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +5 -3
- package/dist/extensions/pi-subagents/test/unit/builtin-agent-documentation.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +19 -19
- package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +19 -19
- package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/chain-serializer.test.ts +20 -20
- package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +35 -5
- package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +52 -52
- package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +187 -0
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +125 -71
- package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +18 -3
- package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +12 -12
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +17 -23
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +25 -25
- package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/file-coalescer.test.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +62 -44
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +329 -37
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +4 -4
- package/dist/extensions/pi-subagents/test/unit/foreground-tool-call-compaction.test.ts +37 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +69 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +79 -2
- package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +102 -49
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +98 -32
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +5 -5
- package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +116 -0
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +55 -7
- package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +289 -13
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +28 -1
- package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +11 -2
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +84 -10
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +122 -8
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +8 -8
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +24 -24
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +16 -2
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +10 -10
- package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +26 -17
- package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +405 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +357 -92
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +22 -6
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +6 -6
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +43 -43
- package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +13 -13
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +4 -4
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +19 -19
- package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +243 -0
- package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +68 -168
- package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +58 -15
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +40 -0
- package/dist/extensions/pi-subagents/test/unit/reads-resolution.test.ts +66 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +47 -28
- package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +12 -12
- package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +57 -0
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +107 -27
- package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +56 -50
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +69 -28
- package/dist/extensions/pi-subagents/test/unit/scheduled-store-root.test.ts +67 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +59 -44
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +424 -49
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +23 -1
- package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +32 -5
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +13 -13
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +120 -91
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +85 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +35 -35
- package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +24 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +312 -22
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +150 -15
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +26 -26
- package/dist/extensions/pi-subagents/test/unit/timeout-defaults.test.ts +73 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +76 -25
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +5 -5
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +55 -6
- package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +13 -13
- package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +8 -8
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +6 -6
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +138 -11
- package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +8 -8
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +113 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +6 -6
- package/dist/extensions/workflow/extension.ts +21 -19
- package/dist/extensions/workflow/modes.ts +65 -0
- package/dist/skills/pi-subagents/SKILL.md +20 -15
- package/dist/skills/pi-subagents/references/constraints-and-recipes.md +77 -83
- package/dist/skills/pi-subagents/references/execution-controls.md +215 -106
- package/dist/skills/pi-subagents/references/management-authoring-rpc.md +29 -9
- package/dist/skills/pi-subagents/references/prompting-and-roles.md +58 -43
- package/docs/plans/workflow-autoloop-reference.md +192 -0
- package/docs/plans/workflow-subagent-consolidation-planb.md +374 -0
- package/docs/plans/workflow-subagent-scripted-merge.md +406 -0
- package/package.json +1 -1
- package/dist/extensions/workflow/adapter.ts +0 -949
- package/dist/extensions/workflow/modes/loop.ts +0 -74
- package/dist/extensions/workflow/modes/prototype.ts +0 -170
- package/dist/extensions/workflow/modes/quicktype.ts +0 -150
- package/dist/extensions/workflow/modes/task.ts +0 -105
- package/dist/extensions/workflow/run-state.ts +0 -124
- package/dist/extensions/workflow/state-machine.ts +0 -484
- package/dist/extensions/workflow/validators.ts +0 -42
|
@@ -96,6 +96,7 @@ interface AsyncStatusPayload {
|
|
|
96
96
|
currentTool?: string;
|
|
97
97
|
currentPath?: string;
|
|
98
98
|
state?: string;
|
|
99
|
+
endedAt?: number;
|
|
99
100
|
launchContractDigest?: string;
|
|
100
101
|
launchResolvedExtensions?: LaunchResolvedExtensions;
|
|
101
102
|
runtimeAcknowledgedExtensions?: RuntimeAcknowledgedExtensions;
|
|
@@ -191,7 +192,7 @@ function childWatchdogStatus(runId: string, phase: "idle" | "reviewing" | "autof
|
|
|
191
192
|
return {
|
|
192
193
|
type: CHILD_WATCHDOG_STATUS_EVENT,
|
|
193
194
|
runId,
|
|
194
|
-
agent: "
|
|
195
|
+
agent: "worker",
|
|
195
196
|
childIndex: 0,
|
|
196
197
|
stepIndex: 0,
|
|
197
198
|
seq,
|
|
@@ -230,6 +231,7 @@ interface AsyncExecutionModule {
|
|
|
230
231
|
|
|
231
232
|
interface UtilsModule {
|
|
232
233
|
readStatus(dir: string): { runId: string; state: string; mode: string } | null;
|
|
234
|
+
pruneStatusCacheForAsyncRoot(root: string, runIds: Iterable<string>): number;
|
|
233
235
|
}
|
|
234
236
|
|
|
235
237
|
interface TypesModule {
|
|
@@ -254,6 +256,7 @@ const isAsyncAvailable = asyncMod?.isAsyncAvailable;
|
|
|
254
256
|
const executeAsyncSingle = asyncMod?.executeAsyncSingle;
|
|
255
257
|
const executeAsyncChain = asyncMod?.executeAsyncChain;
|
|
256
258
|
const readStatus = utils?.readStatus;
|
|
259
|
+
const pruneStatusCacheForAsyncRoot = utils?.pruneStatusCacheForAsyncRoot;
|
|
257
260
|
const ASYNC_DIR = typesMod?.ASYNC_DIR;
|
|
258
261
|
const RESULTS_DIR = typesMod?.RESULTS_DIR;
|
|
259
262
|
const TEMP_ROOT_DIR = typesMod?.TEMP_ROOT_DIR;
|
|
@@ -455,9 +458,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
455
458
|
|
|
456
459
|
function launchProtocolTest(id: string): void {
|
|
457
460
|
executeAsyncSingle(id, {
|
|
458
|
-
agent: "
|
|
461
|
+
agent: "worker",
|
|
459
462
|
task: "Exercise child protocol",
|
|
460
|
-
agentConfig: makeAgent("
|
|
463
|
+
agentConfig: makeAgent("worker", { completionGuard: false }),
|
|
461
464
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
462
465
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
463
466
|
shareEnabled: false,
|
|
@@ -483,6 +486,16 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
483
486
|
assert.equal(payload.results[0]?.output, "你好 from fragmented async JSON");
|
|
484
487
|
});
|
|
485
488
|
|
|
489
|
+
it("persists terminal status before creating the result artifact", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
|
|
490
|
+
mockPi.onCall({ output: "completed output" });
|
|
491
|
+
const id = `async-terminal-status-${Date.now().toString(36)}`;
|
|
492
|
+
launchProtocolTest(id);
|
|
493
|
+
await waitForAsyncResultFile(id);
|
|
494
|
+
const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload;
|
|
495
|
+
assert.equal(status.state, "complete");
|
|
496
|
+
assert.equal(status.endedAt !== undefined, true);
|
|
497
|
+
});
|
|
498
|
+
|
|
486
499
|
it("persists absent output provenance when async lifecycle text is synthetic", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
|
|
487
500
|
mockPi.onCall({ jsonl: [mockAssistantMessage("", "tool_use")], stderr: "mock child failure", exitCode: 1 });
|
|
488
501
|
const id = `async-output-absent-${Date.now().toString(36)}`;
|
|
@@ -504,12 +517,12 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
504
517
|
});
|
|
505
518
|
|
|
506
519
|
it("matches preflight launch digest in equivalent foreground and async execution", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
|
|
507
|
-
const agentName = `contract-
|
|
520
|
+
const agentName = `contract-worker-${Date.now().toString(36)}`;
|
|
508
521
|
const task = "Compare the resolved launch inputs.";
|
|
509
522
|
const turnBudget = { maxTurns: 2, graceTurns: 1 } as const;
|
|
510
523
|
const agentPath = path.join(tempDir, ".selesai", "agents", `${agentName}.md`);
|
|
511
524
|
fs.mkdirSync(path.dirname(agentPath), { recursive: true });
|
|
512
|
-
fs.writeFileSync(agentPath, `---\nname: ${agentName}\ndescription: Contract comparison
|
|
525
|
+
fs.writeFileSync(agentPath, `---\nname: ${agentName}\ndescription: Contract comparison worker\n---\n`, "utf-8");
|
|
513
526
|
const discovered = discoverAgents(tempDir).agents.find((agent) => agent.name === agentName);
|
|
514
527
|
assert.ok(discovered, "expected temporary agent definition to be discovered");
|
|
515
528
|
const preflight = await resolveSubagentLaunchContract({ agent: agentName, cwd: tempDir, task, turnBudget, runId: "contract-preflight" });
|
|
@@ -548,9 +561,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
548
561
|
const id = `async-launch-digest-${Date.now().toString(36)}`;
|
|
549
562
|
const privateExtension = path.join(tempDir, "extensions", "private-extension.ts");
|
|
550
563
|
const launch = executeAsyncSingle(id, {
|
|
551
|
-
agent: "
|
|
564
|
+
agent: "worker",
|
|
552
565
|
task: "Exercise launch digest reporting",
|
|
553
|
-
agentConfig: makeAgent("
|
|
566
|
+
agentConfig: makeAgent("worker", { completionGuard: false, extensions: [privateExtension] }),
|
|
554
567
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
555
568
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
556
569
|
shareEnabled: false,
|
|
@@ -560,7 +573,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
560
573
|
});
|
|
561
574
|
assert.match(launch.details.launchContractDigest ?? "", /^[a-f0-9]{64}$/);
|
|
562
575
|
const payload = await readAsyncPayload(id);
|
|
563
|
-
const status =
|
|
576
|
+
const status = await waitForAsyncState(id, (candidate) => candidate.state === "complete" && candidate.runtimeAcknowledgedExtensions !== undefined);
|
|
564
577
|
assert.equal(payload.launchContractDigest, launch.details.launchContractDigest);
|
|
565
578
|
assert.equal(payload.results[0]?.launchContractDigest, launch.details.launchContractDigest);
|
|
566
579
|
assert.equal(status.launchContractDigest, launch.details.launchContractDigest);
|
|
@@ -612,7 +625,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
612
625
|
|
|
613
626
|
it("foreground checkpoint decisions use current-session control actions", { skip: !createSubagentExecutor ? "executor not available" : undefined }, async () => {
|
|
614
627
|
mockPi.onCall({ output: "analysis done" });
|
|
615
|
-
const executor = makeAsyncExecutor([makeAgent("analyst"), makeAgent("
|
|
628
|
+
const executor = makeAsyncExecutor([makeAgent("analyst"), makeAgent("worker")]);
|
|
616
629
|
const ctx = makeMinimalCtx(tempDir);
|
|
617
630
|
ctx.sessionManager.getSessionId = () => "session-foreground-checkpoint";
|
|
618
631
|
const id = `foreground-checkpoint-${Date.now().toString(36)}`;
|
|
@@ -621,7 +634,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
621
634
|
chain: [
|
|
622
635
|
{ agent: "analyst", task: "Analyze" },
|
|
623
636
|
{ checkpoint: "review", message: "Approve implementation?" },
|
|
624
|
-
{ agent: "
|
|
637
|
+
{ agent: "worker", task: "Implement" },
|
|
625
638
|
],
|
|
626
639
|
}, new AbortController().signal, undefined, ctx) as AsyncExecutionResult;
|
|
627
640
|
|
|
@@ -646,9 +659,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
646
659
|
chain: [
|
|
647
660
|
{ agent: "analyst", task: "Analyze" },
|
|
648
661
|
{ checkpoint: "review" },
|
|
649
|
-
{ agent: "
|
|
662
|
+
{ agent: "worker", task: "Implement" },
|
|
650
663
|
],
|
|
651
|
-
agents: [makeAgent("analyst"), makeAgent("
|
|
664
|
+
agents: [makeAgent("analyst"), makeAgent("worker")],
|
|
652
665
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
653
666
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
654
667
|
shareEnabled: false,
|
|
@@ -717,11 +730,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
717
730
|
const sessionFile = path.join(tempDir, "sessions", "parent-session", "session.jsonl");
|
|
718
731
|
const ctx = makeMinimalCtx(tempDir);
|
|
719
732
|
ctx.sessionManager.getSessionFile = () => sessionFile;
|
|
720
|
-
const executor = makeAsyncExecutor([makeAgent("
|
|
733
|
+
const executor = makeAsyncExecutor([makeAgent("worker", { completionGuard: false })], { artifactDir: "session" });
|
|
721
734
|
|
|
722
735
|
const launch = await executor.execute(
|
|
723
736
|
"async-session-artifact-dir",
|
|
724
|
-
{ agent: "
|
|
737
|
+
{ agent: "worker", task: "Write async session artifacts", async: true, runId: "async-session-artifacts", acceptance: false },
|
|
725
738
|
new AbortController().signal,
|
|
726
739
|
undefined,
|
|
727
740
|
ctx,
|
|
@@ -746,13 +759,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
746
759
|
const sessionId = `session-capability-${Date.now().toString(36)}`;
|
|
747
760
|
const handle = registerSubagentCapabilityCeiling({ sessionId, ceiling: { allowedTools: ["read"], denyExtensions: true }, source: "test" });
|
|
748
761
|
try {
|
|
749
|
-
const executor = makeAsyncExecutor([makeAgent("
|
|
762
|
+
const executor = makeAsyncExecutor([makeAgent("worker", { tools: ["read", "write"], completionGuard: false })]);
|
|
750
763
|
const id = `async-capability-${Date.now().toString(36)}`;
|
|
751
764
|
const ctx = makeMinimalCtx(tempDir);
|
|
752
765
|
ctx.sessionManager.getSessionId = () => sessionId;
|
|
753
766
|
const launch = await executor.execute(
|
|
754
767
|
id,
|
|
755
|
-
{ agent: "
|
|
768
|
+
{ agent: "worker", task: "Run with a restricted capability ceiling", async: true, runId: id, acceptance: false, artifacts: true },
|
|
756
769
|
new AbortController().signal,
|
|
757
770
|
undefined,
|
|
758
771
|
ctx,
|
|
@@ -787,9 +800,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
787
800
|
mockPi.onCall({ output: "", stderr: "model unavailable", exitCode: 1 });
|
|
788
801
|
const id = `async-empty-failure-artifact-${Date.now().toString(36)}`;
|
|
789
802
|
executeAsyncSingle(id, {
|
|
790
|
-
agent: "
|
|
803
|
+
agent: "worker",
|
|
791
804
|
task: "Fail before output",
|
|
792
|
-
agentConfig: makeAgent("
|
|
805
|
+
agentConfig: makeAgent("worker", { completionGuard: false }),
|
|
793
806
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
794
807
|
artifactConfig: { enabled: true, includeInput: true, includeOutput: true, includeJsonl: false, includeMetadata: true, cleanupDays: 7 },
|
|
795
808
|
artifactsDir: path.join(tempDir, ".pi-subagents", "artifacts"),
|
|
@@ -814,9 +827,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
814
827
|
const id = `async-artifact-dir-recovery-${Date.now().toString(36)}`;
|
|
815
828
|
const artifactsDir = path.join(tempDir, ".pi-subagents", "artifacts");
|
|
816
829
|
executeAsyncSingle(id, {
|
|
817
|
-
agent: "
|
|
830
|
+
agent: "worker",
|
|
818
831
|
task: "Complete after generated artifacts are cleaned",
|
|
819
|
-
agentConfig: makeAgent("
|
|
832
|
+
agentConfig: makeAgent("worker", { completionGuard: false }),
|
|
820
833
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
821
834
|
artifactConfig: { enabled: true, includeInput: true, includeOutput: true, includeJsonl: false, includeMetadata: true, cleanupDays: 7 },
|
|
822
835
|
artifactsDir,
|
|
@@ -899,9 +912,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
899
912
|
mockPi.onCall({ output: "OK" });
|
|
900
913
|
const id = `async-single-literal-output-ref-${Date.now().toString(36)}`;
|
|
901
914
|
const result = executeAsyncSingle(id, {
|
|
902
|
-
agent: "
|
|
915
|
+
agent: "worker",
|
|
903
916
|
task,
|
|
904
|
-
agentConfig: makeAgent("
|
|
917
|
+
agentConfig: makeAgent("worker"),
|
|
905
918
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
906
919
|
artifactConfig: {
|
|
907
920
|
enabled: false,
|
|
@@ -931,9 +944,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
931
944
|
mockPi.onCall({ output: "non-node exec async done" });
|
|
932
945
|
const id = `async-non-node-exec-${Date.now().toString(36)}`;
|
|
933
946
|
const result = executeAsyncSingle(id, {
|
|
934
|
-
agent: "
|
|
947
|
+
agent: "worker",
|
|
935
948
|
task: "Say non-node exec async done. Do not edit files.",
|
|
936
|
-
agentConfig: makeAgent("
|
|
949
|
+
agentConfig: makeAgent("worker"),
|
|
937
950
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
938
951
|
artifactConfig: {
|
|
939
952
|
enabled: false,
|
|
@@ -965,9 +978,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
965
978
|
mockPi.onCall({ output: "stale node exec async done" });
|
|
966
979
|
const id = `async-stale-node-exec-${Date.now().toString(36)}`;
|
|
967
980
|
const result = executeAsyncSingle(id, {
|
|
968
|
-
agent: "
|
|
981
|
+
agent: "worker",
|
|
969
982
|
task: "Say stale node exec async done. Do not edit files.",
|
|
970
|
-
agentConfig: makeAgent("
|
|
983
|
+
agentConfig: makeAgent("worker"),
|
|
971
984
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
972
985
|
artifactConfig: {
|
|
973
986
|
enabled: false,
|
|
@@ -1095,7 +1108,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1095
1108
|
await waitForMockPiCall(mockPi, 1, 10_000);
|
|
1096
1109
|
const resultPath = await waitForAsyncResultFile(id, 8_000);
|
|
1097
1110
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
1098
|
-
const status =
|
|
1111
|
+
const status = await waitForAsyncState(id, (candidate) => candidate.state === "failed");
|
|
1099
1112
|
assert.equal(payload.state, "failed");
|
|
1100
1113
|
assert.equal(payload.success, false);
|
|
1101
1114
|
assert.equal(payload.exitCode, 1);
|
|
@@ -1113,6 +1126,69 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1113
1126
|
assert.equal(mockPi.callCount(), 2);
|
|
1114
1127
|
});
|
|
1115
1128
|
|
|
1129
|
+
it("enforces an agent-level timeout on an async serial child without a composite deadline", { skip: !isAsyncAvailable() ? "jiti not available" : process.platform === "win32" ? "timeout signal delivery intermittent on Windows CI" : undefined }, async () => {
|
|
1130
|
+
mockPi.onCall({ delay: 5_000, output: "too late" });
|
|
1131
|
+
const id = `async-child-timeout-chain-${Date.now().toString(36)}`;
|
|
1132
|
+
executeAsyncChain(id, {
|
|
1133
|
+
chain: [{ agent: "slow", task: "Wait" }],
|
|
1134
|
+
agents: [makeAgent("slow", { defaultTimeoutMs: 150 })],
|
|
1135
|
+
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
1136
|
+
artifactConfig: {
|
|
1137
|
+
enabled: false,
|
|
1138
|
+
includeInput: false,
|
|
1139
|
+
includeOutput: false,
|
|
1140
|
+
includeJsonl: false,
|
|
1141
|
+
includeMetadata: false,
|
|
1142
|
+
cleanupDays: 7,
|
|
1143
|
+
},
|
|
1144
|
+
shareEnabled: false,
|
|
1145
|
+
maxSubagentDepth: 2,
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
const payload = await readAsyncPayload(id);
|
|
1149
|
+
assert.equal(payload.timeoutMs, undefined, "composite parent must remain unbounded by default");
|
|
1150
|
+
assert.equal(payload.state, "failed");
|
|
1151
|
+
assert.equal(payload.results[0]?.timedOut, true);
|
|
1152
|
+
assert.equal(payload.results[0]?.error, "Subagent timed out after 150ms.");
|
|
1153
|
+
});
|
|
1154
|
+
|
|
1155
|
+
it("enforces child timeouts on async parallel tasks without a composite deadline", { skip: !isAsyncAvailable() ? "jiti not available" : process.platform === "win32" ? "timeout signal delivery intermittent on Windows CI" : undefined }, async () => {
|
|
1156
|
+
mockPi.onCall({ delay: 5_000, output: "one too late" });
|
|
1157
|
+
mockPi.onCall({ delay: 5_000, output: "two too late" });
|
|
1158
|
+
const id = `async-child-timeout-parallel-${Date.now().toString(36)}`;
|
|
1159
|
+
executeAsyncChain(id, {
|
|
1160
|
+
chain: [{
|
|
1161
|
+
parallel: [
|
|
1162
|
+
{ agent: "slow-one", task: "Wait" },
|
|
1163
|
+
{ agent: "slow-two", task: "Wait" },
|
|
1164
|
+
],
|
|
1165
|
+
concurrency: 2,
|
|
1166
|
+
}],
|
|
1167
|
+
resultMode: "parallel",
|
|
1168
|
+
agents: [
|
|
1169
|
+
makeAgent("slow-one", { defaultTimeoutMs: 150 }),
|
|
1170
|
+
makeAgent("slow-two", { defaultTimeoutMs: 200 }),
|
|
1171
|
+
],
|
|
1172
|
+
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
1173
|
+
artifactConfig: {
|
|
1174
|
+
enabled: false,
|
|
1175
|
+
includeInput: false,
|
|
1176
|
+
includeOutput: false,
|
|
1177
|
+
includeJsonl: false,
|
|
1178
|
+
includeMetadata: false,
|
|
1179
|
+
cleanupDays: 7,
|
|
1180
|
+
},
|
|
1181
|
+
shareEnabled: false,
|
|
1182
|
+
maxSubagentDepth: 2,
|
|
1183
|
+
});
|
|
1184
|
+
|
|
1185
|
+
const payload = await readAsyncPayload(id);
|
|
1186
|
+
assert.equal(payload.timeoutMs, undefined, "composite parent must remain unbounded by default");
|
|
1187
|
+
assert.equal(payload.state, "failed");
|
|
1188
|
+
assert.deepEqual(payload.results.map((result) => result.timedOut), [true, true]);
|
|
1189
|
+
assert.deepEqual(payload.results.map((result) => result.error), ["Subagent timed out after 150ms.", "Subagent timed out after 200ms."]);
|
|
1190
|
+
});
|
|
1191
|
+
|
|
1116
1192
|
it("hard-kills async children that ignore timeout SIGTERM", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
|
|
1117
1193
|
mockPi.onCall({ delay: 60_000, ignoreSigterm: true, output: "too late" });
|
|
1118
1194
|
const id = `async-timeout-hard-kill-${Date.now().toString(36)}`;
|
|
@@ -1140,7 +1216,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1140
1216
|
const resultPath = await waitForAsyncResultFile(id, 10_000);
|
|
1141
1217
|
const elapsedMs = Date.now() - startedAt;
|
|
1142
1218
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
1143
|
-
const status =
|
|
1219
|
+
const status = await waitForAsyncState(id, (candidate) => candidate.state === "failed");
|
|
1144
1220
|
assert.equal(payload.state, "failed");
|
|
1145
1221
|
assert.equal(payload.timedOut, true);
|
|
1146
1222
|
assert.equal(payload.results[0]?.timedOut, true);
|
|
@@ -1151,15 +1227,15 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1151
1227
|
assert.equal(mockPi.callCount(), 1);
|
|
1152
1228
|
});
|
|
1153
1229
|
|
|
1154
|
-
it("cancels async acceptance verification when the run times out", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
|
|
1230
|
+
it("cancels async acceptance verification when the run times out", { skip: !isAsyncAvailable() ? "jiti not available" : process.platform === "win32" ? "timeout signal delivery intermittent on Windows CI" : undefined }, async () => {
|
|
1155
1231
|
mockPi.onCall({ output: "implementation complete" });
|
|
1156
1232
|
const id = `async-timeout-acceptance-${Date.now().toString(36)}`;
|
|
1157
1233
|
const timeoutMs = 1_000;
|
|
1158
1234
|
const startedAt = Date.now();
|
|
1159
1235
|
executeAsyncSingle(id, {
|
|
1160
|
-
agent: "
|
|
1236
|
+
agent: "worker",
|
|
1161
1237
|
task: "Implement with verified acceptance",
|
|
1162
|
-
agentConfig: makeAgent("
|
|
1238
|
+
agentConfig: makeAgent("worker"),
|
|
1163
1239
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
1164
1240
|
artifactConfig: {
|
|
1165
1241
|
enabled: true,
|
|
@@ -1182,7 +1258,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1182
1258
|
const resultPath = await waitForAsyncResultFile(id, 5_000);
|
|
1183
1259
|
const elapsedMs = Date.now() - startedAt;
|
|
1184
1260
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
1185
|
-
const status =
|
|
1261
|
+
const status = await waitForAsyncState(id, (candidate) => candidate.state === "failed");
|
|
1186
1262
|
assert.equal(payload.state, "failed");
|
|
1187
1263
|
assert.equal(payload.timedOut, true);
|
|
1188
1264
|
assert.equal(payload.results[0]?.timedOut, true);
|
|
@@ -1206,9 +1282,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1206
1282
|
});
|
|
1207
1283
|
const id = `async-turn-budget-soft-${Date.now().toString(36)}`;
|
|
1208
1284
|
executeAsyncSingle(id, {
|
|
1209
|
-
agent: "
|
|
1285
|
+
agent: "worker",
|
|
1210
1286
|
task: "Use the final grace turn to wrap up.",
|
|
1211
|
-
agentConfig: makeAgent("
|
|
1287
|
+
agentConfig: makeAgent("worker"),
|
|
1212
1288
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
1213
1289
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
1214
1290
|
shareEnabled: false,
|
|
@@ -1218,7 +1294,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1218
1294
|
|
|
1219
1295
|
const resultPath = await waitForAsyncResultFile(id, 10_000);
|
|
1220
1296
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
1221
|
-
const status =
|
|
1297
|
+
const status = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
|
|
1222
1298
|
assert.equal(payload.success, true);
|
|
1223
1299
|
assert.equal(payload.state, "complete");
|
|
1224
1300
|
assert.equal(payload.turnBudgetExceeded, undefined);
|
|
@@ -1245,9 +1321,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1245
1321
|
});
|
|
1246
1322
|
const id = `async-turn-budget-hard-${Date.now().toString(36)}`;
|
|
1247
1323
|
executeAsyncSingle(id, {
|
|
1248
|
-
agent: "
|
|
1324
|
+
agent: "worker",
|
|
1249
1325
|
task: "Exceed the turn budget.",
|
|
1250
|
-
agentConfig: makeAgent("
|
|
1326
|
+
agentConfig: makeAgent("worker"),
|
|
1251
1327
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
1252
1328
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
1253
1329
|
shareEnabled: false,
|
|
@@ -1257,7 +1333,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1257
1333
|
|
|
1258
1334
|
const resultPath = await waitForAsyncResultFile(id, 10_000);
|
|
1259
1335
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
1260
|
-
const status =
|
|
1336
|
+
const status = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
|
|
1261
1337
|
assert.equal(payload.success, true);
|
|
1262
1338
|
assert.equal(payload.state, "complete");
|
|
1263
1339
|
assert.equal(payload.exitCode, 0);
|
|
@@ -1295,9 +1371,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1295
1371
|
],
|
|
1296
1372
|
});
|
|
1297
1373
|
executeAsyncSingle(id, {
|
|
1298
|
-
agent: "
|
|
1374
|
+
agent: "worker",
|
|
1299
1375
|
task: "Finish active tool work before enforcing the hard limit.",
|
|
1300
|
-
agentConfig: makeAgent("
|
|
1376
|
+
agentConfig: makeAgent("worker"),
|
|
1301
1377
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
1302
1378
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
1303
1379
|
shareEnabled: false,
|
|
@@ -1319,7 +1395,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1319
1395
|
|
|
1320
1396
|
const resultPath = await waitForAsyncResultFile(id, 10_000);
|
|
1321
1397
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
1322
|
-
const status =
|
|
1398
|
+
const status = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
|
|
1323
1399
|
assert.equal(payload.state, "complete");
|
|
1324
1400
|
assert.equal(payload.turnBudgetExceeded, undefined);
|
|
1325
1401
|
assert.equal(payload.turnBudget?.outcome, "wrap-up-requested");
|
|
@@ -1345,9 +1421,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1345
1421
|
});
|
|
1346
1422
|
const id = `async-turn-budget-timeout-${Date.now().toString(36)}`;
|
|
1347
1423
|
executeAsyncSingle(id, {
|
|
1348
|
-
agent: "
|
|
1424
|
+
agent: "worker",
|
|
1349
1425
|
task: "Timeout while hard termination is deferred.",
|
|
1350
|
-
agentConfig: makeAgent("
|
|
1426
|
+
agentConfig: makeAgent("worker"),
|
|
1351
1427
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
1352
1428
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
1353
1429
|
shareEnabled: false,
|
|
@@ -1361,7 +1437,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1361
1437
|
deliverTimeoutRequest({ asyncDir, pid: pending.pid, source: "test" });
|
|
1362
1438
|
const resultPath = await waitForAsyncResultFile(id, 10_000);
|
|
1363
1439
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
1364
|
-
const status =
|
|
1440
|
+
const status = await waitForAsyncState(id, (candidate) => candidate.state === "failed");
|
|
1365
1441
|
assert.equal(payload.state, "failed");
|
|
1366
1442
|
assert.equal(payload.timedOut, true);
|
|
1367
1443
|
assert.equal(payload.turnBudgetExceeded, undefined);
|
|
@@ -1387,9 +1463,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1387
1463
|
});
|
|
1388
1464
|
const id = `async-turn-budget-stop-${Date.now().toString(36)}`;
|
|
1389
1465
|
executeAsyncSingle(id, {
|
|
1390
|
-
agent: "
|
|
1466
|
+
agent: "worker",
|
|
1391
1467
|
task: "Stop while hard termination is deferred.",
|
|
1392
|
-
agentConfig: makeAgent("
|
|
1468
|
+
agentConfig: makeAgent("worker"),
|
|
1393
1469
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
1394
1470
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
1395
1471
|
shareEnabled: false,
|
|
@@ -1402,7 +1478,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1402
1478
|
deliverStopRequest({ asyncDir, pid: pending.pid, source: "test" });
|
|
1403
1479
|
const resultPath = await waitForAsyncResultFile(id, 10_000);
|
|
1404
1480
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
1405
|
-
const status =
|
|
1481
|
+
const status = await waitForAsyncState(id, (candidate) => candidate.state === "stopped");
|
|
1406
1482
|
assert.equal(payload.state, "stopped");
|
|
1407
1483
|
assert.equal(payload.stopped, true);
|
|
1408
1484
|
assert.equal(payload.turnBudgetExceeded, undefined);
|
|
@@ -1445,13 +1521,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1445
1521
|
const wrappedTask = `Fork preamble: ${"execution ".repeat(20)}`;
|
|
1446
1522
|
const rawGoal = `Caller-facing goal: ${"raw ".repeat(40)}`;
|
|
1447
1523
|
const singleResult = executeAsyncSingle(singleId, {
|
|
1448
|
-
agent: "
|
|
1524
|
+
agent: "worker",
|
|
1449
1525
|
task: wrappedTask,
|
|
1450
1526
|
goal: rawGoal,
|
|
1451
|
-
agentConfig: makeAgent("
|
|
1527
|
+
agentConfig: makeAgent("worker"),
|
|
1452
1528
|
...commonParams,
|
|
1453
1529
|
});
|
|
1454
|
-
assert.match(singleResult.content[0]?.text ?? "", /Async:
|
|
1530
|
+
assert.match(singleResult.content[0]?.text ?? "", /Async: worker \[/);
|
|
1455
1531
|
assert.match(singleResult.content[0]?.text ?? "", /Do not run sleep timers or polling loops/);
|
|
1456
1532
|
assert.match(singleResult.content[0]?.text ?? "", /call subagent_wait\(\)/i);
|
|
1457
1533
|
assert.match(singleResult.content[0]?.text ?? "", /non-interactive run: Pi auto-drains current-session background work at agent_end/);
|
|
@@ -1462,9 +1538,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1462
1538
|
mockPi.onCall({ output: "interactive done" });
|
|
1463
1539
|
const interactiveId = `async-handoff-interactive-${Date.now().toString(36)}`;
|
|
1464
1540
|
const interactiveResult = executeAsyncSingle(interactiveId, {
|
|
1465
|
-
agent: "
|
|
1541
|
+
agent: "worker",
|
|
1466
1542
|
task: "Interactive handoff",
|
|
1467
|
-
agentConfig: makeAgent("
|
|
1543
|
+
agentConfig: makeAgent("worker"),
|
|
1468
1544
|
...commonParams,
|
|
1469
1545
|
ctx: { ...commonParams.ctx, interactive: true },
|
|
1470
1546
|
});
|
|
@@ -1478,9 +1554,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1478
1554
|
mockPi.onCall({ output: "parallel two done" });
|
|
1479
1555
|
const parallelId = `async-handoff-parallel-${Date.now().toString(36)}`;
|
|
1480
1556
|
const parallelResult = executeAsyncChain(parallelId, {
|
|
1481
|
-
chain: [{ parallel: [{ agent: "
|
|
1557
|
+
chain: [{ parallel: [{ agent: "worker", task: "Do one" }, { agent: "reviewer", task: "Do two" }] }],
|
|
1482
1558
|
resultMode: "parallel",
|
|
1483
|
-
agents: [makeAgent("
|
|
1559
|
+
agents: [makeAgent("worker"), makeAgent("reviewer")],
|
|
1484
1560
|
...commonParams,
|
|
1485
1561
|
});
|
|
1486
1562
|
assert.match(parallelResult.content[0]?.text ?? "", /Async parallel:/);
|
|
@@ -1490,7 +1566,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1490
1566
|
const parallelResultPath = await waitForAsyncResultFile(parallelId, 10_000);
|
|
1491
1567
|
const parallelPayload = JSON.parse(fs.readFileSync(parallelResultPath, "utf-8")) as { agent?: string; mode?: string };
|
|
1492
1568
|
assert.equal(parallelPayload.mode, "parallel");
|
|
1493
|
-
assert.equal(parallelPayload.agent, "parallel:
|
|
1569
|
+
assert.equal(parallelPayload.agent, "parallel:worker+reviewer");
|
|
1494
1570
|
|
|
1495
1571
|
mockPi.onCall({ output: "chain done" });
|
|
1496
1572
|
const chainId = `async-handoff-chain-${Date.now().toString(36)}`;
|
|
@@ -1498,8 +1574,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1498
1574
|
const chainChildTask = `Do chained work ${"child ".repeat(15)}`;
|
|
1499
1575
|
const chainResult = executeAsyncChain(chainId, {
|
|
1500
1576
|
task: chainGoal,
|
|
1501
|
-
chain: [{ agent: "
|
|
1502
|
-
agents: [makeAgent("
|
|
1577
|
+
chain: [{ agent: "worker", task: chainChildTask }],
|
|
1578
|
+
agents: [makeAgent("worker")],
|
|
1503
1579
|
...commonParams,
|
|
1504
1580
|
});
|
|
1505
1581
|
assert.match(chainResult.content[0]?.text ?? "", /Async chain:/);
|
|
@@ -1515,8 +1591,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1515
1591
|
const id = `async-missing-extension-tool-${Date.now().toString(36)}`;
|
|
1516
1592
|
|
|
1517
1593
|
executeAsyncChain(id, {
|
|
1518
|
-
chain: [{ agent: "extension-
|
|
1519
|
-
agents: [makeAgent("extension-
|
|
1594
|
+
chain: [{ agent: "extension-worker", task: "Use fixture search" }],
|
|
1595
|
+
agents: [makeAgent("extension-worker", { tools: ["read", "fixture_search"] })],
|
|
1520
1596
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
1521
1597
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
1522
1598
|
shareEnabled: false,
|
|
@@ -1533,11 +1609,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1533
1609
|
|
|
1534
1610
|
it("applies agent acceptance roles to inferred async acceptance", { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : undefined }, async () => {
|
|
1535
1611
|
mockPi.onCall({ output: "exploration complete" });
|
|
1536
|
-
const executor = makeAsyncExecutor([makeAgent("
|
|
1612
|
+
const executor = makeAsyncExecutor([makeAgent("worker", { acceptanceRole: "read-only" })]);
|
|
1537
1613
|
|
|
1538
1614
|
const result = await executor.execute(
|
|
1539
1615
|
"async-agent-acceptance-role",
|
|
1540
|
-
{ agent: "
|
|
1616
|
+
{ agent: "worker", task: "Explore the authentication flow", async: true, clarify: false },
|
|
1541
1617
|
new AbortController().signal,
|
|
1542
1618
|
undefined,
|
|
1543
1619
|
makeMinimalCtx(tempDir),
|
|
@@ -1551,11 +1627,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1551
1627
|
|
|
1552
1628
|
it("applies agent acceptance roles to inferred async parallel acceptance", { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : undefined }, async () => {
|
|
1553
1629
|
mockPi.onCall({ output: "parallel exploration complete" });
|
|
1554
|
-
const executor = makeAsyncExecutor([makeAgent("
|
|
1630
|
+
const executor = makeAsyncExecutor([makeAgent("worker", { acceptanceRole: "read-only" })]);
|
|
1555
1631
|
|
|
1556
1632
|
const result = await executor.execute(
|
|
1557
1633
|
"async-parallel-agent-acceptance-role",
|
|
1558
|
-
{ tasks: [{ agent: "
|
|
1634
|
+
{ tasks: [{ agent: "worker", task: "Explore the authentication flow" }], async: true, clarify: false },
|
|
1559
1635
|
new AbortController().signal,
|
|
1560
1636
|
undefined,
|
|
1561
1637
|
makeMinimalCtx(tempDir),
|
|
@@ -1599,6 +1675,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1599
1675
|
});
|
|
1600
1676
|
|
|
1601
1677
|
it("top-level async parallel conversion preserves output, reads, and progress", { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : undefined }, async () => {
|
|
1678
|
+
fs.writeFileSync(path.join(tempDir, "input.md"), "context");
|
|
1602
1679
|
mockPi.onCall({ output: "Async top-level report" });
|
|
1603
1680
|
const executor = createSubagentExecutor!({
|
|
1604
1681
|
pi: { events: createEventBus(), getSessionName: () => undefined },
|
|
@@ -1608,13 +1685,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1608
1685
|
tempArtifactsDir: tempDir,
|
|
1609
1686
|
getSubagentSessionRoot: () => tempDir,
|
|
1610
1687
|
expandTilde: (p: string) => p,
|
|
1611
|
-
discoverAgents: () => ({ agents: [makeAgent("
|
|
1688
|
+
discoverAgents: () => ({ agents: [makeAgent("worker", { defaultProgress: true })] }),
|
|
1612
1689
|
});
|
|
1613
1690
|
|
|
1614
1691
|
const result = await executor.execute(
|
|
1615
1692
|
"async-parallel-fields",
|
|
1616
1693
|
{
|
|
1617
|
-
tasks: [{ agent: "
|
|
1694
|
+
tasks: [{ agent: "worker", task: "Do async work", output: "async-top-output.md", reads: ["input.md"] }],
|
|
1618
1695
|
async: true,
|
|
1619
1696
|
clarify: false,
|
|
1620
1697
|
},
|
|
@@ -1667,11 +1744,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1667
1744
|
it(`async top-level parallel isolates inherited output (${outputOverride === true ? "output:true" : "omitted output"})`, { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : undefined }, async () => {
|
|
1668
1745
|
mockPi.onCall({ matchArgIncludes: "Write the first report", output: "first async report" });
|
|
1669
1746
|
mockPi.onCall({ matchArgIncludes: "Write the second report", output: "second async report" });
|
|
1670
|
-
const agent = makeAgent("
|
|
1747
|
+
const agent = makeAgent("worker", { output: "context.md", tools: ["read", "grep", "find", "ls"] });
|
|
1671
1748
|
const executor = makeAsyncExecutor([agent]);
|
|
1672
1749
|
const tasks = [
|
|
1673
|
-
{ agent: "
|
|
1674
|
-
{ agent: "
|
|
1750
|
+
{ agent: "worker", task: "Write the first report", ...(outputOverride !== undefined ? { output: outputOverride } : {}) },
|
|
1751
|
+
{ agent: "worker", task: "Write the second report", ...(outputOverride !== undefined ? { output: outputOverride } : {}) },
|
|
1675
1752
|
];
|
|
1676
1753
|
const launch = await executor.execute(
|
|
1677
1754
|
`async-inherited-output-${outputOverride === true ? "true" : "omitted"}`,
|
|
@@ -1688,8 +1765,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1688
1765
|
assert.equal(payload.results[1]?.output?.split("\n\nOutput saved to:")[0], "second async report");
|
|
1689
1766
|
const outputDir = path.join(tempDir, ".pi-subagents", "artifacts", "outputs", launch.details?.asyncId as string);
|
|
1690
1767
|
const authoritativePaths = [
|
|
1691
|
-
path.join(outputDir, "parallel-0", "0-
|
|
1692
|
-
path.join(outputDir, "parallel-0", "1-
|
|
1768
|
+
path.join(outputDir, "parallel-0", "0-worker", "context.md"),
|
|
1769
|
+
path.join(outputDir, "parallel-0", "1-worker", "context.md"),
|
|
1693
1770
|
];
|
|
1694
1771
|
assert.equal(fs.readFileSync(authoritativePaths[0]!, "utf-8"), "first async report");
|
|
1695
1772
|
assert.equal(fs.readFileSync(authoritativePaths[1]!, "utf-8"), "second async report");
|
|
@@ -1702,8 +1779,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1702
1779
|
const taskArgs = calls.map((name) => (JSON.parse(fs.readFileSync(path.join(mockPi.dir, name), "utf-8")) as MockPiCallRecord).args?.at(-1) ?? "");
|
|
1703
1780
|
const firstTask = taskArgs.find((task) => task.includes("Write the first report")) ?? "";
|
|
1704
1781
|
const secondTask = taskArgs.find((task) => task.includes("Write the second report")) ?? "";
|
|
1705
|
-
assert.ok(firstTask.includes(path.join("parallel-0", "0-
|
|
1706
|
-
assert.ok(secondTask.includes(path.join("parallel-0", "1-
|
|
1782
|
+
assert.ok(firstTask.includes(path.join("parallel-0", "0-worker", "context.md")));
|
|
1783
|
+
assert.ok(secondTask.includes(path.join("parallel-0", "1-worker", "context.md")));
|
|
1707
1784
|
for (const taskArg of [firstTask, secondTask]) {
|
|
1708
1785
|
assert.match(taskArg, /Return the complete artifact in your final response\./);
|
|
1709
1786
|
assert.match(taskArg, /Do not call contact_supervisor merely because no write-capable tool is available\./);
|
|
@@ -1713,13 +1790,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1713
1790
|
}
|
|
1714
1791
|
|
|
1715
1792
|
it("async top-level parallel rejects duplicate explicit output paths before spawning", { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : undefined }, async () => {
|
|
1716
|
-
const executor = makeAsyncExecutor([makeAgent("
|
|
1793
|
+
const executor = makeAsyncExecutor([makeAgent("worker")]);
|
|
1717
1794
|
const result = await executor.execute(
|
|
1718
1795
|
"async-duplicate-output",
|
|
1719
1796
|
{
|
|
1720
1797
|
tasks: [
|
|
1721
|
-
{ agent: "
|
|
1722
|
-
{ agent: "
|
|
1798
|
+
{ agent: "worker", task: "Write A", output: "same.md" },
|
|
1799
|
+
{ agent: "worker", task: "Write B", output: "same.md" },
|
|
1723
1800
|
],
|
|
1724
1801
|
async: true,
|
|
1725
1802
|
clarify: false,
|
|
@@ -1730,20 +1807,20 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1730
1807
|
);
|
|
1731
1808
|
|
|
1732
1809
|
assert.equal(result.isError, true);
|
|
1733
|
-
assert.match(result.content[0]?.text ?? "", /Parallel tasks 1 \(
|
|
1810
|
+
assert.match(result.content[0]?.text ?? "", /Parallel tasks 1 \(worker\) and 2 \(worker\).*same\.md/);
|
|
1734
1811
|
assert.equal(mockPi.callCount(), 0);
|
|
1735
1812
|
});
|
|
1736
1813
|
|
|
1737
1814
|
it("async top-level parallel preserves distinct explicit output destinations", { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : undefined }, async () => {
|
|
1738
1815
|
mockPi.onCall({ matchArgIncludes: "first.md", output: "first explicit report" });
|
|
1739
1816
|
mockPi.onCall({ matchArgIncludes: "second.md", output: "second explicit report" });
|
|
1740
|
-
const executor = makeAsyncExecutor([makeAgent("
|
|
1817
|
+
const executor = makeAsyncExecutor([makeAgent("worker", { output: "context.md" })]);
|
|
1741
1818
|
const launch = await executor.execute(
|
|
1742
1819
|
"async-distinct-explicit-output",
|
|
1743
1820
|
{
|
|
1744
1821
|
tasks: [
|
|
1745
|
-
{ agent: "
|
|
1746
|
-
{ agent: "
|
|
1822
|
+
{ agent: "worker", task: "Write A", output: "first.md" },
|
|
1823
|
+
{ agent: "worker", task: "Write B", output: "second.md" },
|
|
1747
1824
|
],
|
|
1748
1825
|
async: true,
|
|
1749
1826
|
clarify: false,
|
|
@@ -1758,8 +1835,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1758
1835
|
const outputDir = path.join(tempDir, ".pi-subagents", "artifacts", "outputs", launch.details?.asyncId as string);
|
|
1759
1836
|
assert.equal(fs.readFileSync(path.join(outputDir, "first.md"), "utf-8"), "first explicit report");
|
|
1760
1837
|
assert.equal(fs.readFileSync(path.join(outputDir, "second.md"), "utf-8"), "second explicit report");
|
|
1761
|
-
assert.ok(payload.results[0]?.artifactPaths?.outputPath?.endsWith("
|
|
1762
|
-
assert.ok(payload.results[1]?.artifactPaths?.outputPath?.endsWith("
|
|
1838
|
+
assert.ok(payload.results[0]?.artifactPaths?.outputPath?.endsWith("_worker_0_output.md"));
|
|
1839
|
+
assert.ok(payload.results[1]?.artifactPaths?.outputPath?.endsWith("_worker_1_output.md"));
|
|
1763
1840
|
});
|
|
1764
1841
|
|
|
1765
1842
|
it("async chain static parallel namespaces inherited default outputs", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
|
|
@@ -1767,8 +1844,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1767
1844
|
mockPi.onCall({ matchArgIncludes: "Write second", output: "chain second report" });
|
|
1768
1845
|
const id = `async-chain-parallel-output-${Date.now().toString(36)}`;
|
|
1769
1846
|
const launch = executeAsyncChain(id, {
|
|
1770
|
-
chain: [{ parallel: [{ agent: "
|
|
1771
|
-
agents: [makeAgent("
|
|
1847
|
+
chain: [{ parallel: [{ agent: "worker", task: "Write first" }, { agent: "worker", task: "Write second" }] }],
|
|
1848
|
+
agents: [makeAgent("worker", { output: "context.md" })],
|
|
1772
1849
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-chain-output" },
|
|
1773
1850
|
artifactConfig: { enabled: true, includeInput: false, includeOutput: true, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
1774
1851
|
artifactsDir: path.join(tempDir, ".pi-subagents", "artifacts"),
|
|
@@ -1781,8 +1858,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1781
1858
|
assert.equal(payload.success, true);
|
|
1782
1859
|
const outputDir = path.join(tempDir, ".pi-subagents", "artifacts", "outputs", id);
|
|
1783
1860
|
const authoritativePaths = [
|
|
1784
|
-
path.join(outputDir, "parallel-0", "0-
|
|
1785
|
-
path.join(outputDir, "parallel-0", "1-
|
|
1861
|
+
path.join(outputDir, "parallel-0", "0-worker", "context.md"),
|
|
1862
|
+
path.join(outputDir, "parallel-0", "1-worker", "context.md"),
|
|
1786
1863
|
];
|
|
1787
1864
|
assert.equal(fs.readFileSync(authoritativePaths[0]!, "utf-8"), "chain first report");
|
|
1788
1865
|
assert.equal(fs.readFileSync(authoritativePaths[1]!, "utf-8"), "chain second report");
|
|
@@ -1793,8 +1870,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1793
1870
|
assert.equal(fs.readFileSync(artifactPaths[1], "utf-8"), "chain second report");
|
|
1794
1871
|
const calls = fs.readdirSync(mockPi.dir).filter((name) => name.startsWith("call-")).sort();
|
|
1795
1872
|
const taskArgs = calls.map((name) => (JSON.parse(fs.readFileSync(path.join(mockPi.dir, name), "utf-8")) as MockPiCallRecord).args?.at(-1) ?? "");
|
|
1796
|
-
assert.ok(taskArgs.find((task) => task.includes("Write first"))?.includes(path.join("parallel-0", "0-
|
|
1797
|
-
assert.ok(taskArgs.find((task) => task.includes("Write second"))?.includes(path.join("parallel-0", "1-
|
|
1873
|
+
assert.ok(taskArgs.find((task) => task.includes("Write first"))?.includes(path.join("parallel-0", "0-worker", "context.md")));
|
|
1874
|
+
assert.ok(taskArgs.find((task) => task.includes("Write second"))?.includes(path.join("parallel-0", "1-worker", "context.md")));
|
|
1798
1875
|
});
|
|
1799
1876
|
|
|
1800
1877
|
it("async single preserves checked evidence while independent review is pending", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
|
|
@@ -1825,14 +1902,14 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1825
1902
|
};
|
|
1826
1903
|
const id = `async-acceptance-${Date.now().toString(36)}`;
|
|
1827
1904
|
executeAsyncSingle(id, {
|
|
1828
|
-
agent: "
|
|
1905
|
+
agent: "worker",
|
|
1829
1906
|
task: "Implement acceptance-covered fix",
|
|
1830
|
-
agentConfig: makeAgent("
|
|
1907
|
+
agentConfig: makeAgent("worker", { completionGuard: false }),
|
|
1831
1908
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-acceptance" },
|
|
1832
1909
|
artifactConfig,
|
|
1833
1910
|
shareEnabled: false,
|
|
1834
1911
|
maxSubagentDepth: 2,
|
|
1835
|
-
acceptance: { level: "checked", criteria: ["Patch bug"], review: { agent: "
|
|
1912
|
+
acceptance: { level: "checked", criteria: ["Patch bug"], review: { agent: "reviewer", required: true } },
|
|
1836
1913
|
});
|
|
1837
1914
|
const resultPath = await waitForAsyncResultFile(id, 10_000);
|
|
1838
1915
|
const result = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
@@ -1856,13 +1933,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1856
1933
|
tempArtifactsDir: tempDir,
|
|
1857
1934
|
getSubagentSessionRoot: () => tempDir,
|
|
1858
1935
|
expandTilde: (p: string) => p,
|
|
1859
|
-
discoverAgents: () => ({ agents: [makeAgent("
|
|
1936
|
+
discoverAgents: () => ({ agents: [makeAgent("reviewer", { defaultProgress: true })] }),
|
|
1860
1937
|
});
|
|
1861
1938
|
|
|
1862
1939
|
const result = await executor.execute(
|
|
1863
1940
|
"async-chain-read-only-progress",
|
|
1864
1941
|
{
|
|
1865
|
-
chain: [{ agent: "
|
|
1942
|
+
chain: [{ agent: "reviewer" }],
|
|
1866
1943
|
task: "Review-only. Do not edit files. Return findings.",
|
|
1867
1944
|
async: true,
|
|
1868
1945
|
clarify: false,
|
|
@@ -1934,7 +2011,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1934
2011
|
assert.ok(!result.isError);
|
|
1935
2012
|
const resultPath = await waitForAsyncResultFile(id, 10_000);
|
|
1936
2013
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
1937
|
-
const status =
|
|
2014
|
+
const status = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
|
|
1938
2015
|
assert.deepEqual(payload.results[0]?.structuredOutput, { value: "Alpha structured" });
|
|
1939
2016
|
assert.deepEqual(payload.outputs?.data?.structured, { value: "Alpha structured" });
|
|
1940
2017
|
assert.match(readMockPiArgs(mockPi, 1).at(-1) ?? "", /Alpha structured/);
|
|
@@ -1952,15 +2029,15 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1952
2029
|
mockPi.onCall({ matchArgIncludes: "Scout API", output: "Scout A async findings" });
|
|
1953
2030
|
mockPi.onCall({ matchArgIncludes: "Scout UI", output: "Scout B async findings" });
|
|
1954
2031
|
mockPi.onCall({ matchArgIncludes: "Synthesize:", output: "Async funnel synthesis" });
|
|
1955
|
-
mockPi.onCall({ matchArgIncludes: "Review funnel A:", output: "Async
|
|
1956
|
-
mockPi.onCall({ matchArgIncludes: "Review funnel B:", output: "Async
|
|
2032
|
+
mockPi.onCall({ matchArgIncludes: "Review funnel A:", output: "Async reviewer A done" });
|
|
2033
|
+
mockPi.onCall({ matchArgIncludes: "Review funnel B:", output: "Async reviewer B done" });
|
|
1957
2034
|
const id = `async-parallel-funnel-fanout-${Date.now().toString(36)}`;
|
|
1958
2035
|
const result = executeAsyncChain(id, {
|
|
1959
2036
|
chain: [
|
|
1960
2037
|
{
|
|
1961
2038
|
parallel: [
|
|
1962
|
-
{ agent: "
|
|
1963
|
-
{ agent: "
|
|
2039
|
+
{ agent: "scout-a", task: "Scout API" },
|
|
2040
|
+
{ agent: "scout-b", task: "Scout UI" },
|
|
1964
2041
|
],
|
|
1965
2042
|
concurrency: 2,
|
|
1966
2043
|
},
|
|
@@ -1973,7 +2050,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1973
2050
|
concurrency: 2,
|
|
1974
2051
|
},
|
|
1975
2052
|
],
|
|
1976
|
-
agents: [makeAgent("
|
|
2053
|
+
agents: [makeAgent("scout-a"), makeAgent("scout-b"), makeAgent("synthesizer"), makeAgent("review-a"), makeAgent("review-b")],
|
|
1977
2054
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-parallel-funnel-fanout" },
|
|
1978
2055
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
1979
2056
|
shareEnabled: false,
|
|
@@ -1983,14 +2060,14 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1983
2060
|
assert.ok(!result.isError, `should launch: ${JSON.stringify(result.content)}`);
|
|
1984
2061
|
const resultPath = await waitForAsyncResultFile(id, 10_000);
|
|
1985
2062
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
1986
|
-
const status =
|
|
2063
|
+
const status = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
|
|
1987
2064
|
assert.equal(payload.success, true);
|
|
1988
2065
|
assert.deepEqual(payload.results.map((entry) => entry.output), [
|
|
1989
2066
|
"Scout A async findings",
|
|
1990
2067
|
"Scout B async findings",
|
|
1991
2068
|
"Async funnel synthesis",
|
|
1992
|
-
"Async
|
|
1993
|
-
"Async
|
|
2069
|
+
"Async reviewer A done",
|
|
2070
|
+
"Async reviewer B done",
|
|
1994
2071
|
]);
|
|
1995
2072
|
assert.deepEqual(status.steps?.map((step) => step.status), ["complete", "complete", "complete", "complete", "complete"]);
|
|
1996
2073
|
assert.deepEqual(status.parallelGroups, [
|
|
@@ -1998,9 +2075,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
1998
2075
|
{ start: 3, count: 2, stepIndex: 2 },
|
|
1999
2076
|
]);
|
|
2000
2077
|
const funnelTask = readMockPiArgsMatching(mockPi, "Synthesize:").at(-1) ?? "";
|
|
2001
|
-
assert.match(funnelTask, /=== Parallel Task 1 \(
|
|
2078
|
+
assert.match(funnelTask, /=== Parallel Task 1 \(scout-a\) ===/);
|
|
2002
2079
|
assert.match(funnelTask, /Scout A async findings/);
|
|
2003
|
-
assert.match(funnelTask, /=== Parallel Task 2 \(
|
|
2080
|
+
assert.match(funnelTask, /=== Parallel Task 2 \(scout-b\) ===/);
|
|
2004
2081
|
assert.match(funnelTask, /Scout B async findings/);
|
|
2005
2082
|
assert.match(readMockPiArgsMatching(mockPi, "Review funnel A:").at(-1) ?? "", /Review funnel A:\nAsync funnel synthesis/);
|
|
2006
2083
|
assert.match(readMockPiArgsMatching(mockPi, "Review funnel B:").at(-1) ?? "", /Review funnel B:\nAsync funnel synthesis/);
|
|
@@ -2023,13 +2100,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2023
2100
|
{ agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
|
|
2024
2101
|
{
|
|
2025
2102
|
expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 },
|
|
2026
|
-
parallel: { agent: "
|
|
2103
|
+
parallel: { agent: "reviewer", task: "Review {target.path}", label: "Review {target.path}", outputSchema: { type: "object" } },
|
|
2027
2104
|
collect: { as: "reviews" },
|
|
2028
2105
|
concurrency: 1,
|
|
2029
2106
|
},
|
|
2030
2107
|
{ agent: "consumer", task: "Use {outputs.reviews}" },
|
|
2031
2108
|
],
|
|
2032
|
-
agents: [makeAgent("producer"), makeAgent("
|
|
2109
|
+
agents: [makeAgent("producer"), makeAgent("reviewer"), makeAgent("consumer")],
|
|
2033
2110
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-placeholder" },
|
|
2034
2111
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
2035
2112
|
shareEnabled: false,
|
|
@@ -2045,7 +2122,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2045
2122
|
if (fs.existsSync(statusPath)) status = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as AsyncStatusPayload;
|
|
2046
2123
|
else await new Promise((resolve) => setTimeout(resolve, 50));
|
|
2047
2124
|
}
|
|
2048
|
-
assert.deepEqual(status.steps?.map((step) => step.agent), ["producer", "expand:
|
|
2125
|
+
assert.deepEqual(status.steps?.map((step) => step.agent), ["producer", "expand:reviewer", "consumer"]);
|
|
2049
2126
|
assert.equal(status.steps?.[1]?.label, "Review {target.path}");
|
|
2050
2127
|
assert.equal(status.steps?.[1]?.outputName, "reviews");
|
|
2051
2128
|
assert.deepEqual(status.parallelGroups, [{ start: 1, count: 1, stepIndex: 1 }]);
|
|
@@ -2054,7 +2131,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2054
2131
|
const finalStatus = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as AsyncStatusPayload;
|
|
2055
2132
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
2056
2133
|
assert.equal(payload.success, true);
|
|
2057
|
-
assert.deepEqual(finalStatus.steps?.map((step) => step.agent), ["producer", "
|
|
2134
|
+
assert.deepEqual(finalStatus.steps?.map((step) => step.agent), ["producer", "reviewer", "reviewer", "consumer"]);
|
|
2058
2135
|
assert.deepEqual(finalStatus.parallelGroups, [{ start: 1, count: 2, stepIndex: 1 }]);
|
|
2059
2136
|
});
|
|
2060
2137
|
|
|
@@ -2070,7 +2147,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2070
2147
|
{
|
|
2071
2148
|
expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 },
|
|
2072
2149
|
parallel: {
|
|
2073
|
-
agent: "
|
|
2150
|
+
agent: "reviewer",
|
|
2074
2151
|
task: "Review {target.path}",
|
|
2075
2152
|
label: "Review {target.path}",
|
|
2076
2153
|
outputSchema: { type: "object" },
|
|
@@ -2080,7 +2157,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2080
2157
|
},
|
|
2081
2158
|
{ agent: "consumer", task: "Use {outputs.reviews}" },
|
|
2082
2159
|
],
|
|
2083
|
-
agents: [makeAgent("producer"), makeAgent("
|
|
2160
|
+
agents: [makeAgent("producer"), makeAgent("reviewer", { output: "context.md" }), makeAgent("consumer")],
|
|
2084
2161
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic" },
|
|
2085
2162
|
artifactConfig: { enabled: true, includeInput: false, includeOutput: true, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
2086
2163
|
artifactsDir: path.join(tempDir, ".pi-subagents", "artifacts"),
|
|
@@ -2102,8 +2179,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2102
2179
|
assert.deepEqual(collected.map((item) => item.structured), [{ ok: "a" }, { ok: "b" }]);
|
|
2103
2180
|
const outputDir = path.join(tempDir, ".pi-subagents", "artifacts", "outputs", id);
|
|
2104
2181
|
const dynamicOutputPaths = [
|
|
2105
|
-
path.join(outputDir, "dynamic-1", "0-
|
|
2106
|
-
path.join(outputDir, "dynamic-1", "1-
|
|
2182
|
+
path.join(outputDir, "dynamic-1", "0-reviewer", "context.md"),
|
|
2183
|
+
path.join(outputDir, "dynamic-1", "1-reviewer", "context.md"),
|
|
2107
2184
|
];
|
|
2108
2185
|
assert.equal(fs.readFileSync(dynamicOutputPaths[0]!, "utf-8"), "review-a");
|
|
2109
2186
|
assert.equal(fs.readFileSync(dynamicOutputPaths[1]!, "utf-8"), "review-b");
|
|
@@ -2130,13 +2207,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2130
2207
|
{ agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
|
|
2131
2208
|
{
|
|
2132
2209
|
expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 },
|
|
2133
|
-
parallel: { agent: "
|
|
2210
|
+
parallel: { agent: "reviewer", task: "Review {target.path}", outputSchema: { type: "object" } },
|
|
2134
2211
|
collect: { as: "reviews" },
|
|
2135
2212
|
concurrency: 1,
|
|
2136
2213
|
},
|
|
2137
2214
|
],
|
|
2138
2215
|
usageBudget: { tokens: { hard: 200 } },
|
|
2139
|
-
agents: [makeAgent("producer"), makeAgent("
|
|
2216
|
+
agents: [makeAgent("producer"), makeAgent("reviewer")],
|
|
2140
2217
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-budget" },
|
|
2141
2218
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
2142
2219
|
shareEnabled: false,
|
|
@@ -2151,7 +2228,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2151
2228
|
assert.equal(status.steps?.[1]?.status, "complete");
|
|
2152
2229
|
assert.equal(status.steps?.[2]?.status, "failed");
|
|
2153
2230
|
assert.match(status.steps?.[2]?.error ?? "", /Usage budget exhausted/);
|
|
2154
|
-
assert.equal(payload.results.find((result) => result.agent === "
|
|
2231
|
+
assert.equal(payload.results.find((result) => result.agent === "reviewer" && result.skipped)?.skipped, true);
|
|
2155
2232
|
});
|
|
2156
2233
|
|
|
2157
2234
|
it("rejects a shared explicit output before dynamic fanout children start", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
|
|
@@ -2162,12 +2239,12 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2162
2239
|
{ agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
|
|
2163
2240
|
{
|
|
2164
2241
|
expand: { from: { output: "targets", path: "/items" }, item: "target", maxItems: 2 },
|
|
2165
|
-
parallel: { agent: "
|
|
2242
|
+
parallel: { agent: "reviewer", task: "Review {target.path}", output: "shared.md" },
|
|
2166
2243
|
collect: { as: "reviews" },
|
|
2167
2244
|
concurrency: 2,
|
|
2168
2245
|
},
|
|
2169
2246
|
],
|
|
2170
|
-
agents: [makeAgent("producer"), makeAgent("
|
|
2247
|
+
agents: [makeAgent("producer"), makeAgent("reviewer")],
|
|
2171
2248
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-explicit-output" },
|
|
2172
2249
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
2173
2250
|
shareEnabled: false,
|
|
@@ -2196,7 +2273,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2196
2273
|
{
|
|
2197
2274
|
expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 2 },
|
|
2198
2275
|
parallel: {
|
|
2199
|
-
agent: "
|
|
2276
|
+
agent: "reviewer",
|
|
2200
2277
|
task: "Review {target.path}",
|
|
2201
2278
|
label: "Review {target.path}",
|
|
2202
2279
|
outputSchema: { type: "object" },
|
|
@@ -2205,7 +2282,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2205
2282
|
concurrency: 1,
|
|
2206
2283
|
},
|
|
2207
2284
|
],
|
|
2208
|
-
agents: [makeAgent("producer"), makeAgent("
|
|
2285
|
+
agents: [makeAgent("producer"), makeAgent("reviewer", { model: "anthropic/claude-sonnet-4-5:high", thinking: "high" })],
|
|
2209
2286
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic" },
|
|
2210
2287
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
2211
2288
|
shareEnabled: false,
|
|
@@ -2330,7 +2407,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2330
2407
|
{ agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
|
|
2331
2408
|
{
|
|
2332
2409
|
expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 },
|
|
2333
|
-
parallel: { agent: "
|
|
2410
|
+
parallel: { agent: "reviewer", task: "Review {target.path}", outputSchema: { type: "object" }, acceptance: { level: "checked" } },
|
|
2334
2411
|
collect: { as: "reviews" },
|
|
2335
2412
|
acceptance: {
|
|
2336
2413
|
level: "verified",
|
|
@@ -2338,7 +2415,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2338
2415
|
},
|
|
2339
2416
|
},
|
|
2340
2417
|
],
|
|
2341
|
-
agents: [makeAgent("producer"), makeAgent("
|
|
2418
|
+
agents: [makeAgent("producer"), makeAgent("reviewer")],
|
|
2342
2419
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-acceptance-timeout" },
|
|
2343
2420
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
2344
2421
|
shareEnabled: false,
|
|
@@ -2349,7 +2426,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2349
2426
|
const resultPath = await waitForAsyncResultFile(id, 5_000);
|
|
2350
2427
|
const elapsedMs = Date.now() - startedAt;
|
|
2351
2428
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
2352
|
-
const status =
|
|
2429
|
+
const status = await waitForAsyncState(id, (candidate) => candidate.state === "failed");
|
|
2353
2430
|
const dynamicNode = payload.workflowGraph?.nodes?.[1] as { status?: string; error?: string; acceptanceStatus?: string } | undefined;
|
|
2354
2431
|
assert.equal(payload.state, "failed");
|
|
2355
2432
|
assert.equal(payload.timedOut, true);
|
|
@@ -2373,13 +2450,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2373
2450
|
{ agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
|
|
2374
2451
|
{
|
|
2375
2452
|
expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 },
|
|
2376
|
-
parallel: { agent: "
|
|
2453
|
+
parallel: { agent: "reviewer", task: "Review {target.path}", outputSchema: { type: "object" } },
|
|
2377
2454
|
collect: { as: "reviews" },
|
|
2378
2455
|
concurrency: 1,
|
|
2379
2456
|
},
|
|
2380
2457
|
{ agent: "consumer", task: "Use {outputs.reviews}" },
|
|
2381
2458
|
],
|
|
2382
|
-
agents: [makeAgent("producer"), makeAgent("
|
|
2459
|
+
agents: [makeAgent("producer"), makeAgent("reviewer"), makeAgent("consumer")],
|
|
2383
2460
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-targets" },
|
|
2384
2461
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
2385
2462
|
shareEnabled: false,
|
|
@@ -2405,11 +2482,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2405
2482
|
{ agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
|
|
2406
2483
|
{
|
|
2407
2484
|
expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 1 },
|
|
2408
|
-
parallel: { agent: "
|
|
2485
|
+
parallel: { agent: "reviewer", task: "Review {target.path}" },
|
|
2409
2486
|
collect: { as: "reviews" },
|
|
2410
2487
|
},
|
|
2411
2488
|
],
|
|
2412
|
-
agents: [makeAgent("producer"), makeAgent("
|
|
2489
|
+
agents: [makeAgent("producer"), makeAgent("reviewer")],
|
|
2413
2490
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-fail" },
|
|
2414
2491
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
2415
2492
|
shareEnabled: false,
|
|
@@ -2419,7 +2496,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2419
2496
|
assert.ok(!result.isError);
|
|
2420
2497
|
const resultPath = await waitForAsyncResultFile(id, 10_000);
|
|
2421
2498
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
2422
|
-
const status =
|
|
2499
|
+
const status = await waitForAsyncState(id, (candidate) => candidate.state === "failed") as AsyncStatusPayload & { workflowGraph?: AsyncResultPayload["workflowGraph"]; error?: string };
|
|
2423
2500
|
assert.equal(payload.success, false);
|
|
2424
2501
|
assert.match(payload.results.at(-1)?.error ?? "", /exceeding maxItems 1/);
|
|
2425
2502
|
assert.equal(payload.workflowGraph?.nodes?.[1]?.status, "failed");
|
|
@@ -2438,11 +2515,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2438
2515
|
{ agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
|
|
2439
2516
|
{
|
|
2440
2517
|
expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 },
|
|
2441
|
-
parallel: { agent: "
|
|
2518
|
+
parallel: { agent: "reviewer", task: "Review {target.path}", outputSchema: { type: "object" } },
|
|
2442
2519
|
collect: { as: "reviews", outputSchema: { type: "object" } },
|
|
2443
2520
|
},
|
|
2444
2521
|
],
|
|
2445
|
-
agents: [makeAgent("producer"), makeAgent("
|
|
2522
|
+
agents: [makeAgent("producer"), makeAgent("reviewer")],
|
|
2446
2523
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-collect-fail" },
|
|
2447
2524
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
2448
2525
|
shareEnabled: false,
|
|
@@ -2459,7 +2536,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2459
2536
|
assert.match(payload.workflowGraph?.nodes?.[1]?.error ?? "", /Collected output validation failed/);
|
|
2460
2537
|
});
|
|
2461
2538
|
|
|
2462
|
-
it("top-level async worktree parallel
|
|
2539
|
+
it("top-level async worktree parallel keeps existing reads and writes output under project artifacts", { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : process.platform === "win32" ? "worktree path separators unreliable on Windows CI" : undefined }, async () => {
|
|
2463
2540
|
const repoDir = createRepo("pi-subagent-async-worktree-");
|
|
2464
2541
|
try {
|
|
2465
2542
|
mockPi.onCall({ output: "Worktree report" });
|
|
@@ -2471,13 +2548,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2471
2548
|
tempArtifactsDir: repoDir,
|
|
2472
2549
|
getSubagentSessionRoot: () => repoDir,
|
|
2473
2550
|
expandTilde: (p: string) => p,
|
|
2474
|
-
discoverAgents: () => ({ agents: [makeAgent("
|
|
2551
|
+
discoverAgents: () => ({ agents: [makeAgent("worker")] }),
|
|
2475
2552
|
});
|
|
2476
2553
|
|
|
2477
2554
|
const result = await executor.execute(
|
|
2478
2555
|
"async-parallel-worktree-fields",
|
|
2479
2556
|
{
|
|
2480
|
-
tasks: [{ agent: "
|
|
2557
|
+
tasks: [{ agent: "worker", task: "Do worktree work", output: "report.md", reads: ["input.md"] }],
|
|
2481
2558
|
async: true,
|
|
2482
2559
|
clarify: false,
|
|
2483
2560
|
worktree: true,
|
|
@@ -2493,11 +2570,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2493
2570
|
const worktreeCwd = path.join(os.tmpdir(), `pi-worktree-${asyncId}-s0-0`);
|
|
2494
2571
|
const args = await waitForMockPiArgs(mockPi, 0);
|
|
2495
2572
|
const taskArg = args.at(-1) ?? "";
|
|
2496
|
-
assert.
|
|
2573
|
+
assert.match(taskArg, new RegExp(`\\[Read from: ${escapeRegExp(path.join(worktreeCwd, "input.md"))}\\]`));
|
|
2497
2574
|
assert.ok(taskArg.includes(`Write your findings to exactly this path: ${path.join(repoDir, ".pi-subagents", "artifacts", "outputs", asyncId, "report.md")}`));
|
|
2498
2575
|
const resultPath = await waitForAsyncResultFile(asyncId, 90_000);
|
|
2499
2576
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
2500
|
-
const status =
|
|
2577
|
+
const status = await waitForAsyncState(asyncId, (candidate) => candidate.state === "complete");
|
|
2501
2578
|
assert.equal(payload.parallelHandoff?.version, 1);
|
|
2502
2579
|
assert.equal(payload.parallelHandoff?.path, path.join(result.details!.asyncDir!, "handoff.json"));
|
|
2503
2580
|
assert.deepEqual(status.parallelHandoff, payload.parallelHandoff);
|
|
@@ -2509,7 +2586,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2509
2586
|
groups: Array<{ children: Array<{ agent: string; status: string; patch: { path: string } }>; cleanup: { state: string } }>;
|
|
2510
2587
|
};
|
|
2511
2588
|
assert.equal(handoff.version, 1);
|
|
2512
|
-
assert.equal(handoff.groups[0]!.children[0]!.agent, "
|
|
2589
|
+
assert.equal(handoff.groups[0]!.children[0]!.agent, "worker");
|
|
2513
2590
|
assert.equal(handoff.groups[0]!.children[0]!.status, "completed");
|
|
2514
2591
|
assert.equal(handoff.groups[0]!.cleanup.state, "complete");
|
|
2515
2592
|
assert.equal(fs.existsSync(handoff.groups[0]!.children[0]!.patch.path), true, "patch artifact should outlive cleanup");
|
|
@@ -2518,33 +2595,49 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2518
2595
|
}
|
|
2519
2596
|
});
|
|
2520
2597
|
|
|
2521
|
-
it("readStatus caches
|
|
2522
|
-
const
|
|
2598
|
+
it("readStatus caches ordered sweeps above 50 files and invalidates same-mtime replacements", () => {
|
|
2599
|
+
const root = createTempDir();
|
|
2523
2600
|
try {
|
|
2524
|
-
const statusPath = path.join(dir, "status.json");
|
|
2525
2601
|
const fixedTimestamp = new Date(1_700_000_000_000);
|
|
2526
|
-
const
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2602
|
+
const dirs = Array.from({ length: 51 }, (_, index) => {
|
|
2603
|
+
const dir = path.join(root, `run-${index}`);
|
|
2604
|
+
const statusPath = path.join(dir, "status.json");
|
|
2605
|
+
fs.mkdirSync(dir);
|
|
2606
|
+
fs.writeFileSync(statusPath, JSON.stringify({
|
|
2607
|
+
runId: `cache-test-${index}`,
|
|
2608
|
+
state: "running",
|
|
2609
|
+
mode: "single",
|
|
2610
|
+
startedAt: fixedTimestamp.getTime(),
|
|
2611
|
+
}));
|
|
2612
|
+
fs.utimesSync(statusPath, fixedTimestamp, fixedTimestamp);
|
|
2613
|
+
return dir;
|
|
2614
|
+
});
|
|
2534
2615
|
|
|
2535
|
-
const cached = readStatus(dir);
|
|
2536
|
-
assert.ok(
|
|
2537
|
-
assert.strictEqual(readStatus(dir), cached);
|
|
2616
|
+
const cached = dirs.map((dir) => readStatus(dir));
|
|
2617
|
+
cached.forEach((status) => assert.ok(status));
|
|
2618
|
+
dirs.forEach((dir, index) => assert.strictEqual(readStatus(dir), cached[index]));
|
|
2538
2619
|
|
|
2539
|
-
|
|
2620
|
+
const replacedDir = dirs[25]!;
|
|
2621
|
+
const cachedStatus = cached[25];
|
|
2622
|
+
assert.ok(cachedStatus);
|
|
2623
|
+
const statusPath = path.join(replacedDir, "status.json");
|
|
2624
|
+
writeAtomicJson(statusPath, { ...cachedStatus, state: "stopped" });
|
|
2540
2625
|
fs.utimesSync(statusPath, fixedTimestamp, fixedTimestamp);
|
|
2541
2626
|
assert.equal(fs.statSync(statusPath).mtimeMs, fixedTimestamp.getTime());
|
|
2542
|
-
const replaced = readStatus(
|
|
2627
|
+
const replaced = readStatus(replacedDir);
|
|
2543
2628
|
assert.ok(replaced);
|
|
2544
2629
|
assert.equal(replaced.state, "stopped");
|
|
2545
|
-
assert.notStrictEqual(replaced,
|
|
2630
|
+
assert.notStrictEqual(replaced, cachedStatus);
|
|
2631
|
+
|
|
2632
|
+
fs.rmSync(statusPath);
|
|
2633
|
+
assert.equal(readStatus(replacedDir), null);
|
|
2634
|
+
|
|
2635
|
+
const removedDir = dirs[50]!;
|
|
2636
|
+
assert.ok(readStatus(removedDir));
|
|
2637
|
+
fs.rmSync(removedDir, { recursive: true, force: true });
|
|
2638
|
+
assert.equal(pruneStatusCacheForAsyncRoot(root, dirs.slice(0, 50).map((dir) => path.basename(dir))), 1);
|
|
2546
2639
|
} finally {
|
|
2547
|
-
removeTempDir(
|
|
2640
|
+
removeTempDir(root);
|
|
2548
2641
|
}
|
|
2549
2642
|
});
|
|
2550
2643
|
|
|
@@ -2578,10 +2671,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2578
2671
|
const asyncDir = path.join(ASYNC_DIR, id);
|
|
2579
2672
|
const resultPath = path.join(RESULTS_DIR, `${id}.json`);
|
|
2580
2673
|
const run = executeAsyncSingle(id, {
|
|
2581
|
-
agent: "
|
|
2674
|
+
agent: "worker",
|
|
2582
2675
|
task: "Do work",
|
|
2583
2676
|
acceptance: { level: "none", reason: "descriptor persistence coverage" },
|
|
2584
|
-
|
|
2677
|
+
turnBudget: { maxTurns: 8, graceTurns: 2 },
|
|
2678
|
+
agentConfig: makeAgent("worker", {
|
|
2585
2679
|
model: "openai/gpt-5-mini:high",
|
|
2586
2680
|
fallbackModels: ["anthropic/claude-sonnet-4:low"],
|
|
2587
2681
|
}),
|
|
@@ -2616,12 +2710,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2616
2710
|
const descriptorPath = path.join(asyncDir, "recovery-descriptor.json");
|
|
2617
2711
|
const descriptor = JSON.parse(fs.readFileSync(descriptorPath, "utf-8"));
|
|
2618
2712
|
assert.equal(descriptor.sourceRunId, id);
|
|
2619
|
-
assert.equal(descriptor.agent, "
|
|
2713
|
+
assert.equal(descriptor.agent, "worker");
|
|
2620
2714
|
assert.equal(descriptor.model, "openai/gpt-5-mini:high");
|
|
2621
2715
|
assert.deepEqual(descriptor.fallbackModels, ["anthropic/claude-sonnet-4:low"]);
|
|
2622
2716
|
assert.equal(descriptor.cwd, tempDir);
|
|
2623
2717
|
assert.equal(descriptor.sessionDir, path.join(sessionRoot, `async-${id}`));
|
|
2624
2718
|
assert.deepEqual(descriptor.acceptance, { level: "none", reason: "descriptor persistence coverage" });
|
|
2719
|
+
assert.deepEqual(descriptor.initialTurnBudget, { maxTurns: 8, graceTurns: 2 });
|
|
2625
2720
|
assert.equal(Object.hasOwn(descriptor.acceptance, "explicit"), false);
|
|
2626
2721
|
assert.equal(Object.hasOwn(descriptor.acceptance, "inferredReason"), false);
|
|
2627
2722
|
assert.equal(Object.hasOwn(descriptor, "task"), false);
|
|
@@ -2635,7 +2730,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2635
2730
|
assert.equal(payload.results[0].modelAttempts.length, 2);
|
|
2636
2731
|
assert.deepEqual(payload.results[0].totalCost, { inputTokens: 110, outputTokens: 55, costUsd: 0.011 });
|
|
2637
2732
|
assert.deepEqual(payload.totalCost, { inputTokens: 110, outputTokens: 55, costUsd: 0.011 });
|
|
2638
|
-
const statusPayload =
|
|
2733
|
+
const statusPayload = await waitForAsyncState(id, (candidate) => candidate.state === "complete" && candidate.totalCost !== undefined);
|
|
2639
2734
|
assert.equal(statusPayload.lifecycleArtifactVersion, SUBAGENT_LIFECYCLE_ARTIFACT_VERSION);
|
|
2640
2735
|
assert.equal(statusPayload.steps[0]?.model, "anthropic/claude-sonnet-4:low");
|
|
2641
2736
|
assert.equal(statusPayload.steps[0]?.thinking, "low");
|
|
@@ -2657,9 +2752,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2657
2752
|
mockPi.onCall({ output: "Recovered asynchronously after startup race" });
|
|
2658
2753
|
const id = `async-startup-retry-${Date.now().toString(36)}`;
|
|
2659
2754
|
executeAsyncSingle(id, {
|
|
2660
|
-
agent: "
|
|
2755
|
+
agent: "worker",
|
|
2661
2756
|
task: "Do work",
|
|
2662
|
-
agentConfig: makeAgent("
|
|
2757
|
+
agentConfig: makeAgent("worker", {
|
|
2663
2758
|
model: "openai/gpt-5-mini:high",
|
|
2664
2759
|
fallbackModels: ["anthropic/claude-sonnet-4:low"],
|
|
2665
2760
|
}),
|
|
@@ -2706,9 +2801,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2706
2801
|
mockPi.onCall({ output: "Recovered after stream failure" });
|
|
2707
2802
|
const id = `async-fallback-stream-${Date.now().toString(36)}`;
|
|
2708
2803
|
executeAsyncSingle(id, {
|
|
2709
|
-
agent: "
|
|
2804
|
+
agent: "worker",
|
|
2710
2805
|
task: "Do work",
|
|
2711
|
-
agentConfig: makeAgent("
|
|
2806
|
+
agentConfig: makeAgent("worker", {
|
|
2712
2807
|
model: "openai/gpt-5-mini:high",
|
|
2713
2808
|
fallbackModels: ["anthropic/claude-sonnet-4:low"],
|
|
2714
2809
|
}),
|
|
@@ -2747,9 +2842,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2747
2842
|
mockPi.onCall({ output: "fallback must not run" });
|
|
2748
2843
|
const id = `async-fallback-toolfail-${Date.now().toString(36)}`;
|
|
2749
2844
|
executeAsyncSingle(id, {
|
|
2750
|
-
agent: "
|
|
2845
|
+
agent: "worker",
|
|
2751
2846
|
task: "Do work",
|
|
2752
|
-
agentConfig: makeAgent("
|
|
2847
|
+
agentConfig: makeAgent("worker", {
|
|
2753
2848
|
model: "openai/gpt-5-mini:high",
|
|
2754
2849
|
fallbackModels: ["anthropic/claude-sonnet-4:low"],
|
|
2755
2850
|
}),
|
|
@@ -2795,9 +2890,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2795
2890
|
mockPi.onCall({ output: "Recovered asynchronously" });
|
|
2796
2891
|
const id = `async-fallback-thinking-off-${Date.now().toString(36)}`;
|
|
2797
2892
|
const run = executeAsyncSingle(id, {
|
|
2798
|
-
agent: "
|
|
2893
|
+
agent: "worker",
|
|
2799
2894
|
task: "Do work",
|
|
2800
|
-
agentConfig: makeAgent("
|
|
2895
|
+
agentConfig: makeAgent("worker", {
|
|
2801
2896
|
model: "openai/gpt-5-mini:high",
|
|
2802
2897
|
fallbackModels: ["anthropic/claude-sonnet-4:low"],
|
|
2803
2898
|
thinking: "high",
|
|
@@ -2850,9 +2945,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2850
2945
|
mockPi.onCall({ output: "Recovered asynchronously from empty output" });
|
|
2851
2946
|
const id = `async-empty-output-fallback-${Date.now().toString(36)}`;
|
|
2852
2947
|
executeAsyncSingle(id, {
|
|
2853
|
-
agent: "
|
|
2948
|
+
agent: "worker",
|
|
2854
2949
|
task: "Do work",
|
|
2855
|
-
agentConfig: makeAgent("
|
|
2950
|
+
agentConfig: makeAgent("worker", {
|
|
2856
2951
|
model: "openai/gpt-5-mini",
|
|
2857
2952
|
fallbackModels: ["anthropic/claude-sonnet-4"],
|
|
2858
2953
|
}),
|
|
@@ -2900,9 +2995,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2900
2995
|
mockPi.onCall({ output: "Recovered asynchronously on fallback" });
|
|
2901
2996
|
const id = `async-empty-output-after-tool-error-${Date.now().toString(36)}`;
|
|
2902
2997
|
executeAsyncSingle(id, {
|
|
2903
|
-
agent: "
|
|
2998
|
+
agent: "worker",
|
|
2904
2999
|
task: "Do work",
|
|
2905
|
-
agentConfig: makeAgent("
|
|
3000
|
+
agentConfig: makeAgent("worker", {
|
|
2906
3001
|
model: "openai/gpt-5-mini",
|
|
2907
3002
|
fallbackModels: ["anthropic/claude-sonnet-4"],
|
|
2908
3003
|
}),
|
|
@@ -2942,11 +3037,10 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2942
3037
|
exitCode: 0,
|
|
2943
3038
|
});
|
|
2944
3039
|
const id = `async-zero-exit-provider-error-${Date.now().toString(36)}`;
|
|
2945
|
-
const asyncDir = path.join(ASYNC_DIR, id);
|
|
2946
3040
|
executeAsyncSingle(id, {
|
|
2947
|
-
agent: "
|
|
3041
|
+
agent: "worker",
|
|
2948
3042
|
task: "Do work",
|
|
2949
|
-
agentConfig: makeAgent("
|
|
3043
|
+
agentConfig: makeAgent("worker", { model: "openai/gpt-5-mini" }),
|
|
2950
3044
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
2951
3045
|
artifactConfig: {
|
|
2952
3046
|
enabled: false,
|
|
@@ -2964,8 +3058,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2964
3058
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
2965
3059
|
assert.equal(payload.success, false);
|
|
2966
3060
|
assert.match(payload.results[0]?.error ?? "", /429 quota exceeded/);
|
|
2967
|
-
const statusPayload =
|
|
2968
|
-
assert.equal(statusPayload.state, "failed");
|
|
3061
|
+
const statusPayload = await waitForAsyncState(id, (status) => status.state === "failed");
|
|
2969
3062
|
assert.match(statusPayload.steps?.[0]?.error ?? "", /429 quota exceeded/);
|
|
2970
3063
|
});
|
|
2971
3064
|
|
|
@@ -2990,9 +3083,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
2990
3083
|
const id = `async-recovered-child-error-${Date.now().toString(36)}`;
|
|
2991
3084
|
const asyncDir = path.join(ASYNC_DIR, id);
|
|
2992
3085
|
executeAsyncSingle(id, {
|
|
2993
|
-
agent: "
|
|
3086
|
+
agent: "worker",
|
|
2994
3087
|
task: "Do work",
|
|
2995
|
-
agentConfig: makeAgent("
|
|
3088
|
+
agentConfig: makeAgent("worker", { model: "openai/gpt-5-mini" }),
|
|
2996
3089
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
2997
3090
|
artifactConfig: {
|
|
2998
3091
|
enabled: false,
|
|
@@ -3014,7 +3107,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3014
3107
|
assert.equal(payload.results[0]?.success, true);
|
|
3015
3108
|
assert.equal(payload.results[0]?.error, undefined);
|
|
3016
3109
|
assert.equal(payload.results[0]?.output, "Recovered asynchronously");
|
|
3017
|
-
const statusPayload =
|
|
3110
|
+
const statusPayload = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
|
|
3018
3111
|
assert.equal(statusPayload.state, "complete");
|
|
3019
3112
|
assert.equal(statusPayload.steps?.[0]?.status, "complete");
|
|
3020
3113
|
assert.equal(statusPayload.steps?.[0]?.exitCode, 0);
|
|
@@ -3040,9 +3133,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3040
3133
|
const id = `async-provider-error-empty-stop-${Date.now().toString(36)}`;
|
|
3041
3134
|
const asyncDir = path.join(ASYNC_DIR, id);
|
|
3042
3135
|
executeAsyncSingle(id, {
|
|
3043
|
-
agent: "
|
|
3136
|
+
agent: "worker",
|
|
3044
3137
|
task: "Do work",
|
|
3045
|
-
agentConfig: makeAgent("
|
|
3138
|
+
agentConfig: makeAgent("worker", { model: "openai/gpt-5-mini" }),
|
|
3046
3139
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
3047
3140
|
artifactConfig: {
|
|
3048
3141
|
enabled: false,
|
|
@@ -3064,7 +3157,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3064
3157
|
assert.equal(payload.results[0]?.success, false);
|
|
3065
3158
|
assert.match(payload.results[0]?.error ?? "", /provider transport failed/);
|
|
3066
3159
|
assert.equal(payload.results[0]?.output, "");
|
|
3067
|
-
const statusPayload =
|
|
3160
|
+
const statusPayload = await waitForAsyncState(id, (candidate) => candidate.state === "failed");
|
|
3068
3161
|
assert.equal(statusPayload.state, "failed");
|
|
3069
3162
|
assert.equal(statusPayload.steps?.[0]?.status, "failed");
|
|
3070
3163
|
assert.equal(statusPayload.steps?.[0]?.exitCode, 1);
|
|
@@ -3199,9 +3292,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3199
3292
|
mockPi.onCall({ output: "async inline report" });
|
|
3200
3293
|
const id = `async-string-false-output-${Date.now().toString(36)}`;
|
|
3201
3294
|
const run = executeAsyncSingle(id, {
|
|
3202
|
-
agent: "
|
|
3295
|
+
agent: "worker",
|
|
3203
3296
|
task: "Do work",
|
|
3204
|
-
agentConfig: makeAgent("
|
|
3297
|
+
agentConfig: makeAgent("worker", { output: "default-report.md" }),
|
|
3205
3298
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
3206
3299
|
artifactConfig: {
|
|
3207
3300
|
enabled: false,
|
|
@@ -3238,9 +3331,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3238
3331
|
const sessionRoot = path.join(tempDir, "sessions");
|
|
3239
3332
|
|
|
3240
3333
|
executeAsyncSingle(id, {
|
|
3241
|
-
agent: "
|
|
3334
|
+
agent: "worker",
|
|
3242
3335
|
task: "Deploy app",
|
|
3243
|
-
agentConfig: makeAgent("
|
|
3336
|
+
agentConfig: makeAgent("worker"),
|
|
3244
3337
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
3245
3338
|
artifactConfig: {
|
|
3246
3339
|
enabled: false,
|
|
@@ -3277,9 +3370,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3277
3370
|
const sessionRoot = path.join(tempDir, "sessions");
|
|
3278
3371
|
|
|
3279
3372
|
executeAsyncSingle(id, {
|
|
3280
|
-
agent: "
|
|
3373
|
+
agent: "worker",
|
|
3281
3374
|
task: "Implement the approved fixes",
|
|
3282
|
-
agentConfig: makeAgent("
|
|
3375
|
+
agentConfig: makeAgent("worker"),
|
|
3283
3376
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
3284
3377
|
artifactConfig: {
|
|
3285
3378
|
enabled: false,
|
|
@@ -3312,7 +3405,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3312
3405
|
const eventsPath = path.join(ASYNC_DIR, id, "events.jsonl");
|
|
3313
3406
|
const eventsText = fs.readFileSync(eventsPath, "utf-8");
|
|
3314
3407
|
assert.match(eventsText, /"reason":"completion_guard"/);
|
|
3315
|
-
assert.match(eventsText, /Subagent failed:
|
|
3408
|
+
assert.match(eventsText, /Subagent failed: worker/);
|
|
3316
3409
|
assert.doesNotMatch(eventsText, /Status:/);
|
|
3317
3410
|
assert.doesNotMatch(eventsText, /Interrupt:/);
|
|
3318
3411
|
});
|
|
@@ -3322,9 +3415,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3322
3415
|
const id = `async-v1-separate-${Date.now().toString(36)}`;
|
|
3323
3416
|
|
|
3324
3417
|
executeAsyncSingle(id, {
|
|
3325
|
-
agent: "
|
|
3418
|
+
agent: "worker",
|
|
3326
3419
|
task: "Implement the approved fixes",
|
|
3327
|
-
agentConfig: makeAgent("
|
|
3420
|
+
agentConfig: makeAgent("worker", { completionGuard: true }),
|
|
3328
3421
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
3329
3422
|
artifactConfig: {
|
|
3330
3423
|
enabled: false,
|
|
@@ -3343,7 +3436,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3343
3436
|
|
|
3344
3437
|
const resultPath = await waitForAsyncResultFile(id, 10_000);
|
|
3345
3438
|
const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
3346
|
-
const statusPayload =
|
|
3439
|
+
const statusPayload = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
|
|
3347
3440
|
|
|
3348
3441
|
assert.equal(payload.success, true);
|
|
3349
3442
|
assert.equal(payload.state, "complete");
|
|
@@ -3364,9 +3457,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3364
3457
|
const id = `async-single-schema-${Date.now().toString(36)}`;
|
|
3365
3458
|
|
|
3366
3459
|
executeAsyncSingle(id, {
|
|
3367
|
-
agent: "
|
|
3460
|
+
agent: "worker",
|
|
3368
3461
|
task: "Return structured data",
|
|
3369
|
-
agentConfig: makeAgent("
|
|
3462
|
+
agentConfig: makeAgent("worker", { completionGuard: false }),
|
|
3370
3463
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
3371
3464
|
artifactConfig: {
|
|
3372
3465
|
enabled: false,
|
|
@@ -3432,9 +3525,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3432
3525
|
const sessionRoot = path.join(tempDir, "sessions");
|
|
3433
3526
|
|
|
3434
3527
|
executeAsyncSingle(id, {
|
|
3435
|
-
agent: "
|
|
3528
|
+
agent: "worker",
|
|
3436
3529
|
task: "Do work",
|
|
3437
|
-
agentConfig: makeAgent("
|
|
3530
|
+
agentConfig: makeAgent("worker", { model: "gpt-5-mini" }),
|
|
3438
3531
|
ctx: {
|
|
3439
3532
|
pi: { events: { emit() {} } },
|
|
3440
3533
|
cwd: tempDir,
|
|
@@ -3494,14 +3587,14 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3494
3587
|
tempArtifactsDir: tempDir,
|
|
3495
3588
|
getSubagentSessionRoot: () => path.join(tempDir, "sessions"),
|
|
3496
3589
|
expandTilde: (p: string) => p,
|
|
3497
|
-
discoverAgents: () => ({ agents: [makeAgent("
|
|
3590
|
+
discoverAgents: () => ({ agents: [makeAgent("worker")] }),
|
|
3498
3591
|
});
|
|
3499
3592
|
const retainedCtx = makeMinimalCtx(tempDir);
|
|
3500
3593
|
retainedCtx.sessionManager.getSessionId = () => "session-a";
|
|
3501
3594
|
retainedCtx.model = { provider: "deepseek", id: "scheduled-model" };
|
|
3502
3595
|
const launch = await executor.executeScheduled(
|
|
3503
3596
|
`scheduled-owner-${Date.now().toString(36)}`,
|
|
3504
|
-
{ agent: "
|
|
3597
|
+
{ agent: "worker", task: "Run retained project timer", async: true, acceptance: false },
|
|
3505
3598
|
new AbortController().signal,
|
|
3506
3599
|
retainedCtx,
|
|
3507
3600
|
) as AsyncExecutionResult;
|
|
@@ -3514,7 +3607,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3514
3607
|
assert.deepEqual(state.subagentSpawns, { sessionId: "session-b", count: 1, configuredLimit: 1, granted: 1, grantHistory: [] });
|
|
3515
3608
|
const blocked = await executor.executeScheduled(
|
|
3516
3609
|
`scheduled-owner-blocked-${Date.now().toString(36)}`,
|
|
3517
|
-
{ agent: "
|
|
3610
|
+
{ agent: "worker", task: "Exceed the retained owner budget", async: true, acceptance: false },
|
|
3518
3611
|
new AbortController().signal,
|
|
3519
3612
|
retainedCtx,
|
|
3520
3613
|
);
|
|
@@ -3544,7 +3637,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3544
3637
|
tempArtifactsDir: tempDir,
|
|
3545
3638
|
getSubagentSessionRoot: () => path.join(tempDir, "sessions"),
|
|
3546
3639
|
expandTilde: (p: string) => p,
|
|
3547
|
-
discoverAgents: () => ({ agents: [makeAgent("
|
|
3640
|
+
discoverAgents: () => ({ agents: [makeAgent("worker")] }),
|
|
3548
3641
|
});
|
|
3549
3642
|
const retainedCtx = makeMinimalCtx(tempDir);
|
|
3550
3643
|
retainedCtx.sessionManager.getSessionId = () => "session-a";
|
|
@@ -3584,7 +3677,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3584
3677
|
tempArtifactsDir: tempDir,
|
|
3585
3678
|
getSubagentSessionRoot: () => path.join(tempDir, "sessions"),
|
|
3586
3679
|
expandTilde: (p: string) => p,
|
|
3587
|
-
discoverAgents: () => ({ agents: [makeAgent("
|
|
3680
|
+
discoverAgents: () => ({ agents: [makeAgent("worker")] }),
|
|
3588
3681
|
});
|
|
3589
3682
|
const initialCtx = makeMinimalCtx(tempDir);
|
|
3590
3683
|
initialCtx.sessionManager.getSessionId = () => "session-continued";
|
|
@@ -3595,7 +3688,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3595
3688
|
continuedCtx.sessionManager.getSessionId = () => "session-continued";
|
|
3596
3689
|
const launch = await executor.execute(
|
|
3597
3690
|
"continued-async-child",
|
|
3598
|
-
{ agent: "
|
|
3691
|
+
{ agent: "worker", task: "Do work", async: true, acceptance: false },
|
|
3599
3692
|
new AbortController().signal,
|
|
3600
3693
|
undefined,
|
|
3601
3694
|
continuedCtx,
|
|
@@ -3612,7 +3705,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3612
3705
|
|
|
3613
3706
|
it("foreground chains keep the last parent session model after continuation drops ctx.model", { skip: !createSubagentExecutor ? "executor not available" : undefined }, async () => {
|
|
3614
3707
|
mockPi.onCall({ output: "Done foreground" });
|
|
3615
|
-
const executor = makeAsyncExecutor([makeAgent("
|
|
3708
|
+
const executor = makeAsyncExecutor([makeAgent("worker", { completionGuard: false })]);
|
|
3616
3709
|
const initialCtx = makeMinimalCtx(tempDir);
|
|
3617
3710
|
initialCtx.sessionManager.getSessionId = () => "session-continued";
|
|
3618
3711
|
initialCtx.model = { provider: "deepseek", id: "deepseek-v4-flash" };
|
|
@@ -3622,7 +3715,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3622
3715
|
continuedCtx.sessionManager.getSessionId = () => "session-continued";
|
|
3623
3716
|
const result = await executor.execute(
|
|
3624
3717
|
"continued-foreground-chain",
|
|
3625
|
-
{ chain: [{ agent: "
|
|
3718
|
+
{ chain: [{ agent: "worker", task: "Do work" }], acceptance: false },
|
|
3626
3719
|
new AbortController().signal,
|
|
3627
3720
|
undefined,
|
|
3628
3721
|
continuedCtx,
|
|
@@ -3637,9 +3730,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3637
3730
|
|
|
3638
3731
|
const id = `async-single-parent-model-${Date.now().toString(36)}`;
|
|
3639
3732
|
executeAsyncSingle(id, {
|
|
3640
|
-
agent: "
|
|
3733
|
+
agent: "worker",
|
|
3641
3734
|
task: "Do work",
|
|
3642
|
-
agentConfig: makeAgent("
|
|
3735
|
+
agentConfig: makeAgent("worker"),
|
|
3643
3736
|
ctx: {
|
|
3644
3737
|
pi: { events: { emit() {} } },
|
|
3645
3738
|
cwd: tempDir,
|
|
@@ -3674,8 +3767,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3674
3767
|
|
|
3675
3768
|
const id = `async-chain-parent-model-${Date.now().toString(36)}`;
|
|
3676
3769
|
executeAsyncChain(id, {
|
|
3677
|
-
chain: [{ agent: "
|
|
3678
|
-
agents: [makeAgent("
|
|
3770
|
+
chain: [{ agent: "worker", task: "Do work" }],
|
|
3771
|
+
agents: [makeAgent("worker")],
|
|
3679
3772
|
ctx: {
|
|
3680
3773
|
pi: { events: { emit() {} } },
|
|
3681
3774
|
cwd: tempDir,
|
|
@@ -3710,8 +3803,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3710
3803
|
|
|
3711
3804
|
const id = `async-chain-empty-model-${Date.now().toString(36)}`;
|
|
3712
3805
|
executeAsyncChain(id, {
|
|
3713
|
-
chain: [{ agent: "
|
|
3714
|
-
agents: [makeAgent("
|
|
3806
|
+
chain: [{ agent: "worker", task: "Do work", model: "" }],
|
|
3807
|
+
agents: [makeAgent("worker", { model: "anthropic/claude-sonnet-4-5", thinking: "high" })],
|
|
3715
3808
|
ctx: {
|
|
3716
3809
|
pi: { events: { emit() {} } },
|
|
3717
3810
|
cwd: tempDir,
|
|
@@ -3754,8 +3847,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3754
3847
|
for (const [index, requestedModel] of [undefined, "", "inherit"].entries()) {
|
|
3755
3848
|
const id = `async-chain-no-parent-${index}-${Date.now().toString(36)}`;
|
|
3756
3849
|
executeAsyncChain(id, {
|
|
3757
|
-
chain: [{ agent: "
|
|
3758
|
-
agents: [makeAgent("
|
|
3850
|
+
chain: [{ agent: "worker", task: "Do work", ...(requestedModel !== undefined ? { model: requestedModel } : {}) }],
|
|
3851
|
+
agents: [makeAgent("worker", { model: "openai/gpt-5-mini", thinking: "high" })],
|
|
3759
3852
|
ctx: {
|
|
3760
3853
|
pi: { events: { emit() {} } },
|
|
3761
3854
|
cwd: tempDir,
|
|
@@ -3804,9 +3897,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3804
3897
|
try {
|
|
3805
3898
|
writePackageSkill(taskCwd, "async-task-cwd-skill");
|
|
3806
3899
|
executeAsyncSingle(id, {
|
|
3807
|
-
agent: "
|
|
3900
|
+
agent: "worker",
|
|
3808
3901
|
task: "Do work",
|
|
3809
|
-
agentConfig: makeAgent("
|
|
3902
|
+
agentConfig: makeAgent("worker", { skills: ["async-task-cwd-skill"] }),
|
|
3810
3903
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
3811
3904
|
cwd: taskCwd,
|
|
3812
3905
|
artifactConfig: {
|
|
@@ -3842,15 +3935,15 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3842
3935
|
it("injects agent-file-relative local skills into background single child prompts", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
|
|
3843
3936
|
mockPi.onCall({ output: "Done asynchronously" });
|
|
3844
3937
|
const id = `async-local-skill-${Date.now().toString(36)}`;
|
|
3845
|
-
const agentFile = path.join(tempDir, "agents", "
|
|
3938
|
+
const agentFile = path.join(tempDir, "agents", "worker", "worker.md");
|
|
3846
3939
|
const skillFile = path.join(path.dirname(agentFile), "skills", "local", "SKILL.md");
|
|
3847
3940
|
fs.mkdirSync(path.dirname(skillFile), { recursive: true });
|
|
3848
3941
|
fs.writeFileSync(skillFile, "---\ndescription: async local skill\n---\nLocal skill body\n", "utf-8");
|
|
3849
3942
|
|
|
3850
3943
|
executeAsyncSingle(id, {
|
|
3851
|
-
agent: "
|
|
3944
|
+
agent: "worker",
|
|
3852
3945
|
task: "Do work",
|
|
3853
|
-
agentConfig: makeAgent("
|
|
3946
|
+
agentConfig: makeAgent("worker", { filePath: agentFile, skills: ["local"], skillPath: ["./skills"] }),
|
|
3854
3947
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
3855
3948
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
3856
3949
|
shareEnabled: false,
|
|
@@ -3896,9 +3989,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3896
3989
|
it("background single runs report unavailable pi-subagents skill requests", () => {
|
|
3897
3990
|
const id = `async-pi-subagents-skill-${Date.now().toString(36)}`;
|
|
3898
3991
|
const result = executeAsyncSingle(id, {
|
|
3899
|
-
agent: "
|
|
3992
|
+
agent: "worker",
|
|
3900
3993
|
task: "Do work",
|
|
3901
|
-
agentConfig: makeAgent("
|
|
3994
|
+
agentConfig: makeAgent("worker"),
|
|
3902
3995
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
3903
3996
|
cwd: tempDir,
|
|
3904
3997
|
artifactConfig: {
|
|
@@ -3922,8 +4015,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3922
4015
|
it("background chains report unavailable pi-subagents skill requests", () => {
|
|
3923
4016
|
const id = `async-chain-pi-subagents-skill-${Date.now().toString(36)}`;
|
|
3924
4017
|
const result = executeAsyncChain(id, {
|
|
3925
|
-
chain: [{ agent: "
|
|
3926
|
-
agents: [makeAgent("
|
|
4018
|
+
chain: [{ agent: "worker", task: "Do work", skill: ["pi-subagents"] }],
|
|
4019
|
+
agents: [makeAgent("worker")],
|
|
3927
4020
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
3928
4021
|
cwd: tempDir,
|
|
3929
4022
|
artifactConfig: {
|
|
@@ -3954,8 +4047,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
3954
4047
|
try {
|
|
3955
4048
|
writePackageSkill(path.join(chainCwd, "packages", "app"), "async-chain-step-skill");
|
|
3956
4049
|
executeAsyncChain(id, {
|
|
3957
|
-
chain: [{ agent: "
|
|
3958
|
-
agents: [makeAgent("
|
|
4050
|
+
chain: [{ agent: "worker", task: "Do work", cwd: "packages/app", skill: ["async-chain-step-skill"] }],
|
|
4051
|
+
agents: [makeAgent("worker")],
|
|
3959
4052
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
3960
4053
|
cwd: chainCwd,
|
|
3961
4054
|
artifactConfig: {
|
|
@@ -4053,9 +4146,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4053
4146
|
fs.mkdirSync(path.join(TEMP_ROOT_DIR, `async-cfg-${id}.json`), { recursive: true });
|
|
4054
4147
|
|
|
4055
4148
|
const result = executeAsyncSingle(id, {
|
|
4056
|
-
agent: "
|
|
4149
|
+
agent: "worker",
|
|
4057
4150
|
task: "Do work",
|
|
4058
|
-
agentConfig: makeAgent("
|
|
4151
|
+
agentConfig: makeAgent("worker"),
|
|
4059
4152
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4060
4153
|
artifactConfig: {
|
|
4061
4154
|
enabled: false,
|
|
@@ -4080,9 +4173,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4080
4173
|
const missingCwd = path.join(tempDir, "missing-cwd");
|
|
4081
4174
|
|
|
4082
4175
|
const singleResult = executeAsyncSingle(id, {
|
|
4083
|
-
agent: "
|
|
4176
|
+
agent: "worker",
|
|
4084
4177
|
task: "Do work",
|
|
4085
|
-
agentConfig: makeAgent("
|
|
4178
|
+
agentConfig: makeAgent("worker"),
|
|
4086
4179
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4087
4180
|
cwd: missingCwd,
|
|
4088
4181
|
artifactConfig: {
|
|
@@ -4104,8 +4197,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4104
4197
|
|
|
4105
4198
|
const chainId = `async-missing-cwd-chain-${Date.now().toString(36)}`;
|
|
4106
4199
|
const chainResult = executeAsyncChain(chainId, {
|
|
4107
|
-
chain: [{ agent: "
|
|
4108
|
-
agents: [makeAgent("
|
|
4200
|
+
chain: [{ agent: "worker", task: "Do work" }],
|
|
4201
|
+
agents: [makeAgent("worker")],
|
|
4109
4202
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4110
4203
|
cwd: missingCwd,
|
|
4111
4204
|
artifactConfig: {
|
|
@@ -4135,9 +4228,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4135
4228
|
try {
|
|
4136
4229
|
const id = `async-spawn-fail-${Date.now().toString(36)}`;
|
|
4137
4230
|
const result = executeAsyncSingle(id, {
|
|
4138
|
-
agent: "
|
|
4231
|
+
agent: "worker",
|
|
4139
4232
|
task: "Do work",
|
|
4140
|
-
agentConfig: makeAgent("
|
|
4233
|
+
agentConfig: makeAgent("worker"),
|
|
4141
4234
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4142
4235
|
artifactConfig: {
|
|
4143
4236
|
enabled: false,
|
|
@@ -4172,8 +4265,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4172
4265
|
fs.mkdirSync(path.join(TEMP_ROOT_DIR, `async-cfg-${id}.json`), { recursive: true });
|
|
4173
4266
|
|
|
4174
4267
|
const result = executeAsyncChain(id, {
|
|
4175
|
-
chain: [{ agent: "
|
|
4176
|
-
agents: [makeAgent("
|
|
4268
|
+
chain: [{ agent: "worker", task: "Do work" }],
|
|
4269
|
+
agents: [makeAgent("worker")],
|
|
4177
4270
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4178
4271
|
artifactConfig: {
|
|
4179
4272
|
enabled: false,
|
|
@@ -4203,9 +4296,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4203
4296
|
|
|
4204
4297
|
const start = Date.now();
|
|
4205
4298
|
executeAsyncSingle(id, {
|
|
4206
|
-
agent: "
|
|
4299
|
+
agent: "worker",
|
|
4207
4300
|
task: "Do work",
|
|
4208
|
-
agentConfig: makeAgent("
|
|
4301
|
+
agentConfig: makeAgent("worker"),
|
|
4209
4302
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4210
4303
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
4211
4304
|
shareEnabled: false,
|
|
@@ -4237,9 +4330,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4237
4330
|
|
|
4238
4331
|
const start = Date.now();
|
|
4239
4332
|
executeAsyncSingle(id, {
|
|
4240
|
-
agent: "
|
|
4333
|
+
agent: "worker",
|
|
4241
4334
|
task: "Do work",
|
|
4242
|
-
agentConfig: makeAgent("
|
|
4335
|
+
agentConfig: makeAgent("worker"),
|
|
4243
4336
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4244
4337
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
4245
4338
|
shareEnabled: false,
|
|
@@ -4269,9 +4362,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4269
4362
|
|
|
4270
4363
|
const start = Date.now();
|
|
4271
4364
|
executeAsyncSingle(id, {
|
|
4272
|
-
agent: "
|
|
4365
|
+
agent: "worker",
|
|
4273
4366
|
task: "Do work",
|
|
4274
|
-
agentConfig: makeAgent("
|
|
4367
|
+
agentConfig: makeAgent("worker"),
|
|
4275
4368
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4276
4369
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
4277
4370
|
shareEnabled: false,
|
|
@@ -4304,9 +4397,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4304
4397
|
|
|
4305
4398
|
const start = Date.now();
|
|
4306
4399
|
executeAsyncSingle(id, {
|
|
4307
|
-
agent: "
|
|
4400
|
+
agent: "worker",
|
|
4308
4401
|
task: "Do work",
|
|
4309
|
-
agentConfig: makeAgent("
|
|
4402
|
+
agentConfig: makeAgent("worker"),
|
|
4310
4403
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4311
4404
|
artifactConfig: {
|
|
4312
4405
|
enabled: false,
|
|
@@ -4349,9 +4442,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4349
4442
|
|
|
4350
4443
|
const start = Date.now();
|
|
4351
4444
|
executeAsyncSingle(id, {
|
|
4352
|
-
agent: "
|
|
4445
|
+
agent: "scout",
|
|
4353
4446
|
task: "Inspect something",
|
|
4354
|
-
agentConfig: makeAgent("
|
|
4447
|
+
agentConfig: makeAgent("scout"),
|
|
4355
4448
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4356
4449
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
4357
4450
|
shareEnabled: false,
|
|
@@ -4394,9 +4487,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4394
4487
|
const resultPath = path.join(RESULTS_DIR, `${id}.json`);
|
|
4395
4488
|
|
|
4396
4489
|
executeAsyncSingle(id, {
|
|
4397
|
-
agent: "
|
|
4490
|
+
agent: "worker",
|
|
4398
4491
|
task: "Do work",
|
|
4399
|
-
agentConfig: makeAgent("
|
|
4492
|
+
agentConfig: makeAgent("worker"),
|
|
4400
4493
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4401
4494
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
4402
4495
|
shareEnabled: false,
|
|
@@ -4431,9 +4524,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4431
4524
|
const resultPath = path.join(RESULTS_DIR, `${id}.json`);
|
|
4432
4525
|
|
|
4433
4526
|
executeAsyncSingle(id, {
|
|
4434
|
-
agent: "
|
|
4527
|
+
agent: "scout",
|
|
4435
4528
|
task: "Investigate behavior",
|
|
4436
|
-
agentConfig: makeAgent("
|
|
4529
|
+
agentConfig: makeAgent("scout"),
|
|
4437
4530
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4438
4531
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
4439
4532
|
shareEnabled: false,
|
|
@@ -4499,9 +4592,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4499
4592
|
const eventsPath = path.join(asyncDir, "events.jsonl");
|
|
4500
4593
|
const resultPath = path.join(RESULTS_DIR, `${id}.json`);
|
|
4501
4594
|
executeAsyncSingle(id, {
|
|
4502
|
-
agent: "
|
|
4595
|
+
agent: "worker",
|
|
4503
4596
|
task: "Run the command",
|
|
4504
|
-
agentConfig: makeAgent("
|
|
4597
|
+
agentConfig: makeAgent("worker"),
|
|
4505
4598
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4506
4599
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
4507
4600
|
shareEnabled: false,
|
|
@@ -4541,23 +4634,25 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4541
4634
|
});
|
|
4542
4635
|
|
|
4543
4636
|
it("subagent_wait wakes when an async child is waiting on contact_supervisor", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
|
|
4637
|
+
const id = `async-supervisor-attention-${Date.now().toString(36)}`;
|
|
4638
|
+
const replyReleasePath = path.join(tempDir, `${id}.reply`);
|
|
4639
|
+
const finalReleasePath = path.join(tempDir, `${id}.final`);
|
|
4544
4640
|
mockPi.onCall({
|
|
4545
4641
|
steps: [
|
|
4546
4642
|
{ jsonl: [events.toolStart("contact_supervisor", { reason: "need_decision", message: "Need a decision" })] },
|
|
4547
|
-
{
|
|
4548
|
-
{
|
|
4643
|
+
{ waitForPath: replyReleasePath, jsonl: [events.toolEnd("contact_supervisor"), events.toolResult("contact_supervisor", "**Reply from supervisor:**\nProceed")] },
|
|
4644
|
+
{ waitForPath: finalReleasePath, jsonl: [events.assistantMessage("Done")] },
|
|
4549
4645
|
],
|
|
4550
4646
|
});
|
|
4551
4647
|
|
|
4552
|
-
const id = `async-supervisor-attention-${Date.now().toString(36)}`;
|
|
4553
4648
|
const asyncDir = path.join(ASYNC_DIR, id);
|
|
4554
4649
|
const eventsPath = path.join(asyncDir, "events.jsonl");
|
|
4555
4650
|
const resultPath = path.join(RESULTS_DIR, `${id}.json`);
|
|
4556
4651
|
const statusPath = path.join(asyncDir, "status.json");
|
|
4557
4652
|
executeAsyncSingle(id, {
|
|
4558
|
-
agent: "
|
|
4653
|
+
agent: "worker",
|
|
4559
4654
|
task: "Ask the supervisor for a blocking decision",
|
|
4560
|
-
agentConfig: makeAgent("
|
|
4655
|
+
agentConfig: makeAgent("worker"),
|
|
4561
4656
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4562
4657
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
4563
4658
|
shareEnabled: false,
|
|
@@ -4573,55 +4668,71 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4573
4668
|
},
|
|
4574
4669
|
});
|
|
4575
4670
|
|
|
4576
|
-
const
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4671
|
+
const releaseMockChild = () => {
|
|
4672
|
+
if (!fs.existsSync(replyReleasePath)) fs.writeFileSync(replyReleasePath, "release", "utf-8");
|
|
4673
|
+
if (!fs.existsSync(finalReleasePath)) fs.writeFileSync(finalReleasePath, "release", "utf-8");
|
|
4674
|
+
};
|
|
4675
|
+
const releaseSupervisorReply = () => {
|
|
4676
|
+
if (!fs.existsSync(replyReleasePath)) fs.writeFileSync(replyReleasePath, "release", "utf-8");
|
|
4677
|
+
};
|
|
4678
|
+
try {
|
|
4679
|
+
const attentionDeadline = Date.now() + 10_000;
|
|
4680
|
+
let statusDuringAttention: AsyncStatusPayload | undefined;
|
|
4681
|
+
while (Date.now() < attentionDeadline && !fs.existsSync(resultPath)) {
|
|
4682
|
+
if (fs.existsSync(statusPath)) {
|
|
4683
|
+
const nextStatus = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as AsyncStatusPayload;
|
|
4684
|
+
if (nextStatus.currentTool === "contact_supervisor" && nextStatus.activityState === "needs_attention") {
|
|
4685
|
+
statusDuringAttention = nextStatus;
|
|
4686
|
+
break;
|
|
4687
|
+
}
|
|
4584
4688
|
}
|
|
4689
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
4585
4690
|
}
|
|
4586
|
-
|
|
4587
|
-
}
|
|
4588
|
-
assert.ok(statusDuringAttention, "expected status.json to expose the blocking supervisor request");
|
|
4691
|
+
assert.ok(statusDuringAttention, "expected status.json to expose the blocking supervisor request");
|
|
4589
4692
|
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4693
|
+
try {
|
|
4694
|
+
const waitResult = await waitForSubagents({ id, timeoutMs: 3_500 }, undefined, {
|
|
4695
|
+
state: { currentSessionId: "session-1", foregroundRuns: new Map(), asyncJobs: new Map(), cleanupTimers: new Map(), resultFileCoalescer: new Map() },
|
|
4696
|
+
pollIntervalMs: 100,
|
|
4697
|
+
events: createEventBus(),
|
|
4698
|
+
});
|
|
4699
|
+
const waitText = waitResult.content[0]?.text ?? "";
|
|
4700
|
+
assert.equal(waitResult.isError, undefined);
|
|
4701
|
+
assert.match(waitText, /attention required/i);
|
|
4702
|
+
assert.match(waitText, new RegExp(id));
|
|
4703
|
+
assert.match(waitText, /intercom\(\{ action: "pending" \}\)/);
|
|
4704
|
+
assert.equal(fs.existsSync(resultPath), false, "wait should return before the child completes");
|
|
4705
|
+
} finally {
|
|
4706
|
+
releaseSupervisorReply();
|
|
4707
|
+
}
|
|
4601
4708
|
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4709
|
+
const eventText = fs.existsSync(eventsPath) ? fs.readFileSync(eventsPath, "utf-8") : "";
|
|
4710
|
+
assert.match(eventText, /"type":"needs_attention"/);
|
|
4711
|
+
assert.match(eventText, /"reason":"supervisor_request"/);
|
|
4712
|
+
assert.equal(statusDuringAttention.activityState, "needs_attention");
|
|
4713
|
+
assert.equal(statusDuringAttention.steps?.[0]?.activityState, "needs_attention");
|
|
4714
|
+
assert.equal(statusDuringAttention.currentTool, "contact_supervisor");
|
|
4715
|
+
assert.equal(statusDuringAttention.steps?.[0]?.currentTool, "contact_supervisor");
|
|
4716
|
+
|
|
4717
|
+
const clearDeadline = Date.now() + 10_000;
|
|
4718
|
+
let statusAfterReply: AsyncStatusPayload | undefined;
|
|
4719
|
+
while (Date.now() < clearDeadline && !fs.existsSync(resultPath)) {
|
|
4720
|
+
const nextStatus = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as AsyncStatusPayload;
|
|
4721
|
+
if (nextStatus.state === "running" && !nextStatus.currentTool && !nextStatus.steps?.[0]?.currentTool) {
|
|
4722
|
+
statusAfterReply = nextStatus;
|
|
4723
|
+
break;
|
|
4724
|
+
}
|
|
4725
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
4617
4726
|
}
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
assert.equal(statusAfterReply.activityState, undefined);
|
|
4622
|
-
assert.equal(statusAfterReply.steps?.[0]?.activityState, undefined);
|
|
4727
|
+
assert.ok(statusAfterReply, "expected the child to keep running after the supervisor reply");
|
|
4728
|
+
assert.equal(statusAfterReply.activityState, undefined);
|
|
4729
|
+
assert.equal(statusAfterReply.steps?.[0]?.activityState, undefined);
|
|
4623
4730
|
|
|
4624
|
-
|
|
4731
|
+
fs.writeFileSync(finalReleasePath, "release", "utf-8");
|
|
4732
|
+
await waitForAsyncResultFile(id);
|
|
4733
|
+
} finally {
|
|
4734
|
+
releaseMockChild();
|
|
4735
|
+
}
|
|
4625
4736
|
});
|
|
4626
4737
|
|
|
4627
4738
|
it("background runs escalate repeated mutating tool failures", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
|
|
@@ -4640,9 +4751,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4640
4751
|
const resultPath = path.join(RESULTS_DIR, `${id}.json`);
|
|
4641
4752
|
|
|
4642
4753
|
executeAsyncSingle(id, {
|
|
4643
|
-
agent: "
|
|
4754
|
+
agent: "worker",
|
|
4644
4755
|
task: "Implement the approved fixes",
|
|
4645
|
-
agentConfig: makeAgent("
|
|
4756
|
+
agentConfig: makeAgent("worker"),
|
|
4646
4757
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4647
4758
|
artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
|
|
4648
4759
|
shareEnabled: false,
|
|
@@ -4724,9 +4835,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4724
4835
|
const sessionRoot = path.join(tempDir, "sessions");
|
|
4725
4836
|
|
|
4726
4837
|
executeAsyncSingle(id, {
|
|
4727
|
-
agent: "
|
|
4838
|
+
agent: "worker",
|
|
4728
4839
|
task: "Stream noisy diagnostics",
|
|
4729
|
-
agentConfig: makeAgent("
|
|
4840
|
+
agentConfig: makeAgent("worker"),
|
|
4730
4841
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4731
4842
|
artifactConfig: {
|
|
4732
4843
|
enabled: false,
|
|
@@ -4775,9 +4886,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
|
|
|
4775
4886
|
const sessionRoot = path.join(tempDir, "sessions");
|
|
4776
4887
|
|
|
4777
4888
|
executeAsyncSingle(id, {
|
|
4778
|
-
agent: "
|
|
4889
|
+
agent: "worker",
|
|
4779
4890
|
task: "Stream detailed progress",
|
|
4780
|
-
agentConfig: makeAgent("
|
|
4891
|
+
agentConfig: makeAgent("worker"),
|
|
4781
4892
|
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
|
|
4782
4893
|
artifactConfig: {
|
|
4783
4894
|
enabled: false,
|