@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
|
@@ -1,949 +0,0 @@
|
|
|
1
|
-
// ponytail: pi adapter over the pure WorkflowStateMachine. Owns all fs/pi
|
|
2
|
-
// wiring: tool/command/event registration, node:fs predicates, the git skip
|
|
3
|
-
// check, footer rendering, agent continuation, tool-result shaping. Every
|
|
4
|
-
// call site is one sm method + one applyEffect switch over domain effects.
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
ExtensionAPI,
|
|
8
|
-
ExtensionCommandContext,
|
|
9
|
-
ExtensionContext,
|
|
10
|
-
ToolDefinition,
|
|
11
|
-
} from "@selesai/code";
|
|
12
|
-
import { Text } from "@earendil-works/pi-tui";
|
|
13
|
-
import { Type } from "typebox";
|
|
14
|
-
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
15
|
-
import { randomUUID } from "node:crypto";
|
|
16
|
-
import { basename, resolve } from "node:path";
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
WorkflowStateMachine,
|
|
20
|
-
type WorkflowConfig,
|
|
21
|
-
type WorkflowDeps,
|
|
22
|
-
type WorkflowEffect,
|
|
23
|
-
type WorkflowEntry,
|
|
24
|
-
type FooterState,
|
|
25
|
-
type WorkflowSnapshot,
|
|
26
|
-
} from "./state-machine.ts";
|
|
27
|
-
import {
|
|
28
|
-
WORKFLOW_STATE_FILENAME,
|
|
29
|
-
type LoopState,
|
|
30
|
-
type PersistedWorkflowRun,
|
|
31
|
-
type WorkflowModes,
|
|
32
|
-
listResumableWorkflowRuns,
|
|
33
|
-
resolveWorkflowRun,
|
|
34
|
-
saveWorkflowRun,
|
|
35
|
-
} from "./run-state.ts";
|
|
36
|
-
import { MARKERS } from "./validators.ts";
|
|
37
|
-
|
|
38
|
-
function defaultArtifactPathFor(_goal: string, base: string): string {
|
|
39
|
-
return resolve(base, randomUUID());
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function artifactsBaseFor(config: WorkflowConfig): string {
|
|
43
|
-
return resolve(config.artifactsBase ?? "./.selesai/artifacts");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function realFileExists(path: string): Promise<boolean> {
|
|
47
|
-
try {
|
|
48
|
-
await access(path);
|
|
49
|
-
return true;
|
|
50
|
-
} catch {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function textFromToolResultContent(content: unknown): string | undefined {
|
|
56
|
-
if (!Array.isArray(content)) return undefined;
|
|
57
|
-
const parts: string[] = [];
|
|
58
|
-
for (const part of content) {
|
|
59
|
-
if (part && typeof part === "object" && (part as any).type === "text" && typeof (part as any).text === "string") {
|
|
60
|
-
parts.push((part as any).text);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
const joined = parts.join("\n\n").trim();
|
|
64
|
-
return joined || undefined;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Intercom-enabled foreground subagents replace normal inline tool content
|
|
68
|
-
// with a delivery receipt. Their real inline response remains in the result's
|
|
69
|
-
// output artifact, so the workflow loop must read that before parsing markers.
|
|
70
|
-
async function textFromSubagentResult(event: any): Promise<string | undefined> {
|
|
71
|
-
const inline = textFromToolResultContent(event.content);
|
|
72
|
-
if (inline?.match(LOOP_STATUS_RE)) return inline;
|
|
73
|
-
const outputPath = event.details?.results?.[0]?.artifactPaths?.outputPath;
|
|
74
|
-
if (typeof outputPath !== "string") return inline;
|
|
75
|
-
try {
|
|
76
|
-
return await readFile(outputPath, "utf8");
|
|
77
|
-
} catch {
|
|
78
|
-
return inline;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Mutates a workflow subagent invocation to suppress both its configured
|
|
83
|
-
// default output and any caller-provided child output path. `output: false`
|
|
84
|
-
// preserves the normal inline result, which the parent must write explicitly.
|
|
85
|
-
function disableSubagentOutput(input: Record<string, unknown>): void {
|
|
86
|
-
if (typeof input.agent === "string") input.output = false;
|
|
87
|
-
if (Array.isArray(input.tasks)) {
|
|
88
|
-
for (const task of input.tasks) {
|
|
89
|
-
if (task && typeof task === "object") task.output = false;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
if (Array.isArray(input.chain)) {
|
|
93
|
-
for (const step of input.chain) {
|
|
94
|
-
if (!step || typeof step !== "object") continue;
|
|
95
|
-
step.output = false;
|
|
96
|
-
if (Array.isArray(step.parallel)) {
|
|
97
|
-
for (const task of step.parallel) {
|
|
98
|
-
if (task && typeof task === "object") task.output = false;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export interface WorkflowAdapterOptions {
|
|
106
|
-
commandName: string;
|
|
107
|
-
commandDescription: string;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
interface WorkflowController {
|
|
111
|
-
pi: ExtensionAPI;
|
|
112
|
-
config: WorkflowConfig;
|
|
113
|
-
sm: WorkflowStateMachine;
|
|
114
|
-
deps: WorkflowDeps;
|
|
115
|
-
// The state machine stays pure; the adapter persists this orchestration
|
|
116
|
-
// detail alongside the state-machine snapshot.
|
|
117
|
-
loopState?: LoopState;
|
|
118
|
-
run?: PersistedWorkflowRun;
|
|
119
|
-
seenToolCallIds: Set<string>;
|
|
120
|
-
lastBlockedKey?: string;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const WORKFLOW_ARTIFACT_TOOL = "write_workflow_artifact";
|
|
124
|
-
const WORKFLOW_CONTROL_MESSAGE = "selesai-workflow-control";
|
|
125
|
-
// ponytail: shared across all module copies so prototype + quicktype registration
|
|
126
|
-
// can race. Module-level state alone is not enough when the loader runs each
|
|
127
|
-
// extension in its own module record.
|
|
128
|
-
const WORKFLOW_GLOBAL = Symbol.for("selesai.workflow.registry.v1");
|
|
129
|
-
type WorkflowRegistry = {
|
|
130
|
-
controllers: WorkflowController[];
|
|
131
|
-
writerRegisteredFor: WeakSet<object>;
|
|
132
|
-
toolsRegisteredFor: WeakSet<object>;
|
|
133
|
-
};
|
|
134
|
-
const registry: WorkflowRegistry = ((globalThis as any)[WORKFLOW_GLOBAL] ??= {
|
|
135
|
-
controllers: [],
|
|
136
|
-
writerRegisteredFor: new WeakSet(),
|
|
137
|
-
toolsRegisteredFor: new WeakSet(),
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
// ponytail: tests call this between cases to drop pi references and reset
|
|
141
|
-
// writer-registration state. Production code never calls it.
|
|
142
|
-
export function __resetWorkflowRegistryForTests(): void {
|
|
143
|
-
registry.controllers.length = 0;
|
|
144
|
-
(registry.writerRegisteredFor as WeakSet<object>) = new WeakSet();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function activeControllersFor(pi: ExtensionAPI): WorkflowController[] {
|
|
148
|
-
return registry.controllers.filter((c) => c.pi === pi && c.sm.snapshot.active);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function isRegisteredController(controller: WorkflowController): boolean {
|
|
152
|
-
return registry.controllers.includes(controller);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Extension reload can reuse the same ExtensionAPI object while this module's
|
|
156
|
-
// global registry survives. Replace a mode's old controller so its active,
|
|
157
|
-
// pre-reload state cannot handle new tool results without a persisted run.
|
|
158
|
-
function replaceControllerFor(pi: ExtensionAPI, mode: string): void {
|
|
159
|
-
for (let i = registry.controllers.length - 1; i >= 0; i--) {
|
|
160
|
-
const controller = registry.controllers[i]!;
|
|
161
|
-
if (controller.pi === pi && controller.config.mode === mode) {
|
|
162
|
-
registry.controllers.splice(i, 1);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function modesFor(config: WorkflowConfig): WorkflowModes {
|
|
168
|
-
return { [config.mode]: config.phases };
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function snapshotRun(controller: WorkflowController): PersistedWorkflowRun {
|
|
172
|
-
if (!controller.run) throw new Error("No workflow run is attached.");
|
|
173
|
-
const snapshot = controller.sm.snapshot;
|
|
174
|
-
return {
|
|
175
|
-
...controller.run,
|
|
176
|
-
status: snapshot.active ? "active" : "completed",
|
|
177
|
-
goal: snapshot.userPrompt,
|
|
178
|
-
artifactDir: snapshot.artifactDir,
|
|
179
|
-
phase: snapshot.phase,
|
|
180
|
-
autoArmed: snapshot.autoArmed,
|
|
181
|
-
loopState: controller.loopState,
|
|
182
|
-
updatedAt: new Date().toISOString(),
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async function persistRun(controller: WorkflowController): Promise<void> {
|
|
187
|
-
const run = snapshotRun(controller);
|
|
188
|
-
await saveWorkflowRun(run);
|
|
189
|
-
controller.run = run;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
interface ControllerCheckpoint {
|
|
193
|
-
snapshot: WorkflowSnapshot;
|
|
194
|
-
loopState?: LoopState;
|
|
195
|
-
run?: PersistedWorkflowRun;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function checkpoint(controller: WorkflowController): ControllerCheckpoint {
|
|
199
|
-
return {
|
|
200
|
-
snapshot: controller.sm.snapshot,
|
|
201
|
-
loopState: controller.loopState && { ...controller.loopState },
|
|
202
|
-
run: controller.run && { ...controller.run, loopState: controller.run.loopState && { ...controller.run.loopState } },
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function restoreCheckpoint(controller: WorkflowController, before: ControllerCheckpoint): void {
|
|
207
|
-
controller.sm.rehydrate(before.snapshot);
|
|
208
|
-
controller.loopState = before.loopState;
|
|
209
|
-
controller.run = before.run;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async function persistAfter(controller: WorkflowController, before: ControllerCheckpoint): Promise<void> {
|
|
213
|
-
try {
|
|
214
|
-
await persistRun(controller);
|
|
215
|
-
} catch (error) {
|
|
216
|
-
restoreCheckpoint(controller, before);
|
|
217
|
-
throw error;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
async function resumeController(
|
|
222
|
-
controller: WorkflowController,
|
|
223
|
-
ctx: ExtensionContext,
|
|
224
|
-
selector: string,
|
|
225
|
-
endTool: string,
|
|
226
|
-
): Promise<any> {
|
|
227
|
-
const { pi, config, sm } = controller;
|
|
228
|
-
if (activeControllersFor(pi).length) {
|
|
229
|
-
return { content: [{ type: "text", text: "A workflow is already active. End it before resuming another run." }], details: { alreadyActive: true } };
|
|
230
|
-
}
|
|
231
|
-
let selected;
|
|
232
|
-
try {
|
|
233
|
-
selected = await resolveWorkflowRun(selector, artifactsBaseFor(config), modesFor(config));
|
|
234
|
-
if (selected.run.status !== "active") throw new Error("Completed workflow runs cannot be resumed.");
|
|
235
|
-
if (selected.run.mode !== config.mode) throw new Error(`This is a ${selected.run.mode} run, not ${config.mode}.`);
|
|
236
|
-
} catch (error) {
|
|
237
|
-
return { content: [{ type: "text", text: `Cannot resume workflow: ${error instanceof Error ? error.message : String(error)}` }], details: { rejected: true } };
|
|
238
|
-
}
|
|
239
|
-
const before = checkpoint(controller);
|
|
240
|
-
sm.rehydrate({ active: true, phase: selected.run.phase, userPrompt: selected.run.goal, artifactDir: selected.run.artifactDir, autoArmed: selected.run.autoArmed });
|
|
241
|
-
controller.run = selected.run;
|
|
242
|
-
controller.loopState = selected.run.loopState;
|
|
243
|
-
controller.seenToolCallIds.clear();
|
|
244
|
-
controller.lastBlockedKey = undefined;
|
|
245
|
-
try {
|
|
246
|
-
await persistAfter(controller, before);
|
|
247
|
-
const reconcileBefore = checkpoint(controller);
|
|
248
|
-
let reconciled: WorkflowEffect = { kind: "noOp" };
|
|
249
|
-
try {
|
|
250
|
-
reconciled = await sm.onArtifactMaybe(controller.deps);
|
|
251
|
-
await persistRun(controller);
|
|
252
|
-
// ponytail: when a resumed run has multiple already-written parent-owned
|
|
253
|
-
// artifacts, one onArtifactMaybe only advances one phase. Keep reconciling
|
|
254
|
-
// until we either catch up to the durable file state or hit a blocked/
|
|
255
|
-
// terminal effect, so resume lands on the furthest valid phase.
|
|
256
|
-
while (sm.snapshot.active && sm.snapshot.autoArmed) {
|
|
257
|
-
const further = await sm.onArtifactMaybe(controller.deps);
|
|
258
|
-
if (
|
|
259
|
-
further.kind === "noOp" ||
|
|
260
|
-
further.kind === "blocked" ||
|
|
261
|
-
further.kind === "terminalNeedsArtifacts"
|
|
262
|
-
) break;
|
|
263
|
-
reconciled = further;
|
|
264
|
-
await persistRun(controller);
|
|
265
|
-
if (further.kind === "terminalReady") break;
|
|
266
|
-
}
|
|
267
|
-
} catch (error) {
|
|
268
|
-
// Atomic rollback: on any failure during reconciliation, restore both
|
|
269
|
-
// in-memory state and durable state to the checkpoint before the whole
|
|
270
|
-
// reconcile sequence. Do not leave the controller at an intermediate
|
|
271
|
-
// phase while the run file has already advanced further.
|
|
272
|
-
restoreCheckpoint(controller, reconcileBefore);
|
|
273
|
-
try {
|
|
274
|
-
await persistRun(controller);
|
|
275
|
-
} catch {
|
|
276
|
-
// Best-effort rollback failed; still report the original error.
|
|
277
|
-
}
|
|
278
|
-
throw error;
|
|
279
|
-
}
|
|
280
|
-
const current = sm.continueCurrent();
|
|
281
|
-
const terminal = config.phases[config.phases.length - 1];
|
|
282
|
-
const terminalReady = reconciled.kind === "terminalReady" || (sm.snapshot.phase === terminal && !sm.snapshot.autoArmed);
|
|
283
|
-
if (terminalReady) {
|
|
284
|
-
if (current.kind === "advanced") {
|
|
285
|
-
applyFooter(ctx, current.footer);
|
|
286
|
-
applyEntry(pi, config, current.entry, controller.run);
|
|
287
|
-
}
|
|
288
|
-
continueAgent(pi, ctx, `Workflow is terminal-ready. Verify ${sm.snapshot.artifactDir} and call ${endTool} to complete it.`);
|
|
289
|
-
} else if (sm.snapshot.phase === "loop" && controller.loopState?.stage === "maxed") {
|
|
290
|
-
if (current.kind === "advanced") {
|
|
291
|
-
applyFooter(ctx, current.footer);
|
|
292
|
-
applyEntry(pi, config, current.entry, controller.run);
|
|
293
|
-
}
|
|
294
|
-
ctx.ui.notify(`Workflow loop is paused after ${controller.loopState.reviewRound}/${controller.loopState.maxIterations} blocking review rounds. Inspect ${sm.snapshot.artifactDir}/${controller.loopState.reviewPath ?? "loop-review-<round>.md"} before continuing.`, "warning");
|
|
295
|
-
} else if (sm.snapshot.phase === "loop" && controller.loopState) {
|
|
296
|
-
if (current.kind === "advanced") {
|
|
297
|
-
applyFooter(ctx, current.footer);
|
|
298
|
-
applyEntry(pi, config, current.entry, controller.run);
|
|
299
|
-
}
|
|
300
|
-
const ls = controller.loopState;
|
|
301
|
-
continueAgent(pi, ctx, ls.stage === "reviewing"
|
|
302
|
-
? `Resume the loop at review round ${ls.reviewRound + 1}: call the subagent tool now with { agent: "commentator", task: "..." }. Have the commentator read ${sm.snapshot.artifactDir}/handoff.md and inspect the current uncommitted diff against the handoff acceptance criteria. End the review with WORKFLOW_REVIEW_STATUS: clean or WORKFLOW_REVIEW_STATUS: blocking.`
|
|
303
|
-
: `Resume the loop at review round ${ls.reviewRound}: call the subagent tool now with { agent: "builder", task: "..." } to address the feedback in ${sm.snapshot.artifactDir}/${ls.reviewPath ?? "loop-review-<round>.md"}, using ${sm.snapshot.artifactDir}/handoff.md as the source of truth for the goal and acceptance criteria.`);
|
|
304
|
-
} else {
|
|
305
|
-
applyControllerEffect(controller, ctx, current);
|
|
306
|
-
}
|
|
307
|
-
} catch (error) {
|
|
308
|
-
return { content: [{ type: "text", text: `Cannot resume workflow: state could not be persisted: ${error instanceof Error ? error.message : String(error)}` }], details: { persistenceError: true } };
|
|
309
|
-
}
|
|
310
|
-
return {
|
|
311
|
-
content: [{ type: "text", text: `Resumed ${config.mode} workflow ${controller.run!.id} at ${sm.snapshot.phase}. Artifacts: ${selected.statePath}` }],
|
|
312
|
-
details: { runId: controller.run!.id, statePath: selected.statePath, phase: sm.snapshot.phase },
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// ponytail: builds the deps bundle the adapter injects into the state machine.
|
|
317
|
-
// The skip predicate wraps pi.exec("git"...) — the only true-external seam.
|
|
318
|
-
function makeDeps(pi: ExtensionAPI, config: WorkflowConfig): WorkflowDeps {
|
|
319
|
-
return {
|
|
320
|
-
async artifactExists(phase, dir) {
|
|
321
|
-
const file = config.phaseArtifacts[phase];
|
|
322
|
-
if (!file) return true;
|
|
323
|
-
return realFileExists(`${dir}/${file}`);
|
|
324
|
-
},
|
|
325
|
-
async fileExists(path) {
|
|
326
|
-
return realFileExists(path);
|
|
327
|
-
},
|
|
328
|
-
async mkdirArtifactDir(path) {
|
|
329
|
-
await mkdir(path, { recursive: true });
|
|
330
|
-
},
|
|
331
|
-
artifactPathFor: defaultArtifactPathFor,
|
|
332
|
-
// ponytail: Plan 4 — read an artifact for semantic validation. Returns
|
|
333
|
-
// undefined on missing/unreadable so the SM treats it as "not written"
|
|
334
|
-
// rather than an empty-string that would always fail the validator.
|
|
335
|
-
async readArtifact(phase, dir) {
|
|
336
|
-
const file = config.phaseArtifacts[phase];
|
|
337
|
-
if (!file) return undefined;
|
|
338
|
-
try {
|
|
339
|
-
return await readFile(`${dir}/${file}`, "utf8");
|
|
340
|
-
} catch {
|
|
341
|
-
return undefined;
|
|
342
|
-
}
|
|
343
|
-
},
|
|
344
|
-
async readFile(path) {
|
|
345
|
-
try {
|
|
346
|
-
return await readFile(path, "utf8");
|
|
347
|
-
} catch {
|
|
348
|
-
return undefined;
|
|
349
|
-
}
|
|
350
|
-
},
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// ponytail: the git-based skip predicate shared by both modes today.
|
|
355
|
-
// A mode that wants a different skip rule supplies its own SkipRule.
|
|
356
|
-
async function hasNoGitHistory(pi: ExtensionAPI): Promise<boolean> {
|
|
357
|
-
try {
|
|
358
|
-
const result = await pi.exec("git", ["log", "--oneline", "-1"]);
|
|
359
|
-
return result.code !== 0 || !result.stdout.trim();
|
|
360
|
-
} catch {
|
|
361
|
-
return false;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// ponytail: Plan 3 — parse the commentator's machine-readable status line.
|
|
366
|
-
// First match wins; case-insensitive; trailing whitespace allowed. Returns
|
|
367
|
-
// undefined when the marker is absent — the loop treats that as blocking so
|
|
368
|
-
// a malformed review never silently advances the workflow.
|
|
369
|
-
const LOOP_STATUS_RE = /WORKFLOW_REVIEW_STATUS\s*:\s*(clean|blocking)\b/i;
|
|
370
|
-
function parseLoopReviewStatus(text: string | undefined): "clean" | "blocking" | undefined {
|
|
371
|
-
if (!text) return undefined;
|
|
372
|
-
const m = text.match(LOOP_STATUS_RE);
|
|
373
|
-
if (!m) return undefined;
|
|
374
|
-
return m[1]!.toLowerCase() as "clean" | "blocking";
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
function footerText(footer: FooterState, ctx: ExtensionContext): string | undefined {
|
|
378
|
-
if (!footer.visible) return undefined;
|
|
379
|
-
return ctx.ui.theme.fg("warning", footer.text);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function applyEntry(pi: ExtensionAPI, config: WorkflowConfig, entry: WorkflowEntry, run?: PersistedWorkflowRun): void {
|
|
383
|
-
pi.appendEntry(config.entryType, {
|
|
384
|
-
...entry,
|
|
385
|
-
...(run ? { workflowStatePath: resolve(run.artifactDir, WORKFLOW_STATE_FILENAME), runId: run.id } : {}),
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
function applyFooter(ctx: ExtensionContext, footer: FooterState): void {
|
|
390
|
-
ctx.ui.setStatus(footer.statusKey, footerText(footer, ctx));
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
function continueAgent(pi: ExtensionAPI, _ctx: ExtensionContext, prompt: string): void {
|
|
394
|
-
// Engine prompts are hidden custom messages. steer delivers after the current
|
|
395
|
-
// tool batch; triggerTurn starts the continuation when the agent is idle.
|
|
396
|
-
pi.sendMessage(
|
|
397
|
-
{ customType: WORKFLOW_CONTROL_MESSAGE, content: prompt, display: false },
|
|
398
|
-
{ triggerTurn: true, deliverAs: "steer" },
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
function isSoleToolCall(ctx: ExtensionContext, toolCallId: unknown): boolean {
|
|
403
|
-
if (typeof toolCallId !== "string") return false;
|
|
404
|
-
try {
|
|
405
|
-
const manager = (ctx as any)?.sessionManager;
|
|
406
|
-
if (!manager || typeof manager.getBranch !== "function") return false;
|
|
407
|
-
const entries = manager.getBranch();
|
|
408
|
-
if (!Array.isArray(entries)) return false;
|
|
409
|
-
const assistant = [...entries].reverse().find((entry: any) =>
|
|
410
|
-
entry?.type === "message" && entry.message?.role === "assistant",
|
|
411
|
-
)?.message;
|
|
412
|
-
if (!assistant || !Array.isArray(assistant.content)) return false;
|
|
413
|
-
const calls = assistant.content.filter((part: any) => part?.type === "toolCall");
|
|
414
|
-
return calls.length === 1 && calls[0]?.id === toolCallId;
|
|
415
|
-
} catch {
|
|
416
|
-
return false;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
function transitionBatchBlock(ctx: ExtensionContext, toolCallId: unknown): { block: true; reason: string } | undefined {
|
|
421
|
-
if (isSoleToolCall(ctx, toolCallId)) return undefined;
|
|
422
|
-
return {
|
|
423
|
-
block: true,
|
|
424
|
-
reason: "Workflow transition tools must be called alone. Retry this call without any other tool calls in the same assistant turn.",
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// Apply side effects for effects that carry entry/footer/prompt. Engine prompts
|
|
429
|
-
// are hidden custom messages; tool results remain concise and user-facing.
|
|
430
|
-
function applyEffect(
|
|
431
|
-
pi: ExtensionAPI,
|
|
432
|
-
ctx: ExtensionContext,
|
|
433
|
-
config: WorkflowConfig,
|
|
434
|
-
eff: WorkflowEffect,
|
|
435
|
-
run?: PersistedWorkflowRun,
|
|
436
|
-
queuePrompt = true,
|
|
437
|
-
): void {
|
|
438
|
-
switch (eff.kind) {
|
|
439
|
-
case "started":
|
|
440
|
-
case "advanced":
|
|
441
|
-
if ("entry" in eff) applyEntry(pi, config, eff.entry, run);
|
|
442
|
-
if ("footer" in eff) applyFooter(ctx, eff.footer);
|
|
443
|
-
if (queuePrompt && eff.prompt) continueAgent(pi, ctx, eff.prompt);
|
|
444
|
-
return;
|
|
445
|
-
case "closed":
|
|
446
|
-
if ("entry" in eff) applyEntry(pi, config, eff.entry, run);
|
|
447
|
-
if ("footer" in eff) applyFooter(ctx, eff.footer);
|
|
448
|
-
ctx.ui.notify(
|
|
449
|
-
`${config.footerLabel} workflow complete. Artifacts: ${eff.artifactDir}`,
|
|
450
|
-
"info",
|
|
451
|
-
);
|
|
452
|
-
return;
|
|
453
|
-
case "terminalNeedsArtifacts":
|
|
454
|
-
if (queuePrompt && eff.promptToQueue) continueAgent(pi, ctx, eff.promptToQueue);
|
|
455
|
-
return;
|
|
456
|
-
case "blocked":
|
|
457
|
-
if (queuePrompt && eff.reason) {
|
|
458
|
-
continueAgent(pi, ctx, `Phase ${eff.phase} artifact exists but is not approved: ${eff.reason}. Edit it via write_workflow_artifact to add the required marker, then the workflow will advance.`);
|
|
459
|
-
}
|
|
460
|
-
return;
|
|
461
|
-
case "terminalReady":
|
|
462
|
-
if (queuePrompt) continueAgent(pi, ctx, `Terminal artifacts are ready. Call the explicit end workflow tool to complete this run.`);
|
|
463
|
-
return;
|
|
464
|
-
case "endBlocked":
|
|
465
|
-
case "idle":
|
|
466
|
-
case "noOp":
|
|
467
|
-
case "alreadyActive":
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
function blockedKeyFor(eff: WorkflowEffect): string | undefined {
|
|
473
|
-
if (eff.kind !== "blocked") return undefined;
|
|
474
|
-
return `${eff.phase}:${eff.missing}:${eff.reason ?? ""}`;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
function applyControllerEffect(
|
|
478
|
-
controller: WorkflowController,
|
|
479
|
-
ctx: ExtensionContext,
|
|
480
|
-
eff: WorkflowEffect,
|
|
481
|
-
options: { queuePrompt?: boolean } = {},
|
|
482
|
-
): void {
|
|
483
|
-
const blockedKey = blockedKeyFor(eff);
|
|
484
|
-
if (blockedKey) {
|
|
485
|
-
if (controller.lastBlockedKey === blockedKey) return;
|
|
486
|
-
controller.lastBlockedKey = blockedKey;
|
|
487
|
-
} else if (eff.kind !== "noOp") {
|
|
488
|
-
controller.lastBlockedKey = undefined;
|
|
489
|
-
}
|
|
490
|
-
applyEffect(controller.pi, ctx, controller.config, eff, controller.run, options.queuePrompt);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
function isSubagentManagementAction(input: Record<string, unknown> | undefined): boolean {
|
|
494
|
-
return typeof input?.action === "string";
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
function commandHelp(config: WorkflowConfig, options: WorkflowAdapterOptions): string {
|
|
498
|
-
const command = `/${options.commandName}`;
|
|
499
|
-
return [
|
|
500
|
-
`${config.footerLabel} workflow`,
|
|
501
|
-
`${command} <goal> — start a new durable run.`,
|
|
502
|
-
`${command} resume — list resumable runs.`,
|
|
503
|
-
`${command} resume <run-id|artifact-dir|workflow.json> — explicitly resume one.`,
|
|
504
|
-
`Phases advance when their artifacts are written. Runs never auto-resume after reload.`,
|
|
505
|
-
"Only one run may be attached; end_workflow explicitly completes a terminal-ready run.",
|
|
506
|
-
].join("\n");
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
function registerSharedArtifactWriter(pi: ExtensionAPI): void {
|
|
510
|
-
if (registry.writerRegisteredFor.has(pi)) return;
|
|
511
|
-
registry.writerRegisteredFor.add(pi);
|
|
512
|
-
pi.registerTool({
|
|
513
|
-
name: WORKFLOW_ARTIFACT_TOOL,
|
|
514
|
-
label: "Write Workflow Artifact",
|
|
515
|
-
description: "Write the current workflow phase artifact. The workflow enforces the destination path; provide content only.",
|
|
516
|
-
parameters: Type.Object({
|
|
517
|
-
content: Type.String({ description: "Artifact markdown/text content to save." }),
|
|
518
|
-
}),
|
|
519
|
-
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
520
|
-
const active = activeControllersFor(pi);
|
|
521
|
-
if (active.length !== 1) {
|
|
522
|
-
const msg = active.length === 0
|
|
523
|
-
? "No active workflow."
|
|
524
|
-
: `Multiple active workflows (${active.map((c) => c.config.mode).join(", ")}); close one first.`;
|
|
525
|
-
return { content: [{ type: "text", text: msg }], details: { active: active.length } };
|
|
526
|
-
}
|
|
527
|
-
const controller = active[0]!;
|
|
528
|
-
const snap = controller.sm.snapshot;
|
|
529
|
-
const file = controller.config.phaseArtifacts[snap.phase];
|
|
530
|
-
if (!file) {
|
|
531
|
-
return {
|
|
532
|
-
content: [{ type: "text", text: `Phase ${snap.phase} has no workflow artifact.` }],
|
|
533
|
-
details: { phase: snap.phase, blocked: true },
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
const path = resolve(snap.artifactDir, file);
|
|
537
|
-
await mkdir(snap.artifactDir, { recursive: true });
|
|
538
|
-
await writeFile(path, params.content, "utf8");
|
|
539
|
-
const before = checkpoint(controller);
|
|
540
|
-
let eff: WorkflowEffect;
|
|
541
|
-
try {
|
|
542
|
-
eff = await controller.sm.onArtifactMaybe(controller.deps);
|
|
543
|
-
await persistAfter(controller, before);
|
|
544
|
-
} catch (error) {
|
|
545
|
-
return {
|
|
546
|
-
content: [{ type: "text", text: `Wrote ${path}, but could not persist workflow state: ${error instanceof Error ? error.message : String(error)}` }],
|
|
547
|
-
details: { mode: controller.config.mode, phase: snap.phase, path, persistenceError: true },
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
// Every valid artifact boundary is an engine transition. Queue exactly
|
|
551
|
-
// one hidden continuation and terminate this assistant turn.
|
|
552
|
-
const transition = eff.kind === "advanced" || eff.kind === "terminalReady" || eff.kind === "terminalNeedsArtifacts";
|
|
553
|
-
applyControllerEffect(controller, ctx, eff, { queuePrompt: transition });
|
|
554
|
-
if (eff.kind === "blocked" && eff.reason) {
|
|
555
|
-
return {
|
|
556
|
-
content: [{ type: "text", text: `Wrote ${path}, but it is not approved: ${eff.reason}. Re-write it via write_workflow_artifact to add the required marker.` }],
|
|
557
|
-
details: { mode: controller.config.mode, phase: snap.phase, path, file, blocked: true, reason: eff.reason },
|
|
558
|
-
};
|
|
559
|
-
}
|
|
560
|
-
const advanced = eff.kind === "advanced";
|
|
561
|
-
return {
|
|
562
|
-
content: [{ type: "text", text: advanced
|
|
563
|
-
? `Wrote ${path}. Phase advanced to ${eff.phase}; the next workflow phase is queued.`
|
|
564
|
-
: `Wrote ${path}.` }],
|
|
565
|
-
details: { mode: controller.config.mode, phase: snap.phase, path, file, advanced },
|
|
566
|
-
terminate: transition,
|
|
567
|
-
};
|
|
568
|
-
},
|
|
569
|
-
renderResult(result, _options, theme) {
|
|
570
|
-
const d = result.details as { path?: string; blocked?: boolean; reason?: string };
|
|
571
|
-
if (d.blocked) return new Text(theme.fg("warning", `○ workflow artifact not approved: ${d.reason ?? "missing marker"}`), 0, 0);
|
|
572
|
-
return new Text(theme.fg(d.path ? "success" : "warning", d.path ? `✓ wrote ${d.path}` : "○ no active workflow"), 0, 0);
|
|
573
|
-
},
|
|
574
|
-
} satisfies ToolDefinition);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
function controllerForMode(pi: ExtensionAPI, mode: string): WorkflowController | undefined {
|
|
578
|
-
return registry.controllers.find((controller) =>
|
|
579
|
-
controller.pi === pi && controller.config.mode === mode && isRegisteredController(controller),
|
|
580
|
-
);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
function unknownMode(mode: string) {
|
|
584
|
-
return {
|
|
585
|
-
content: [{ type: "text" as const, text: `Unknown workflow mode: ${mode}. Use an installed mode.` }],
|
|
586
|
-
details: { rejected: true, mode },
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
async function endController(controller: WorkflowController, ctx: ExtensionContext) {
|
|
591
|
-
const { config, sm, deps } = controller;
|
|
592
|
-
const before = checkpoint(controller);
|
|
593
|
-
const eff = await sm.end(deps);
|
|
594
|
-
if (eff.kind === "closed") {
|
|
595
|
-
try {
|
|
596
|
-
await persistAfter(controller, before);
|
|
597
|
-
} catch (error) {
|
|
598
|
-
return {
|
|
599
|
-
content: [{ type: "text" as const, text: `Cannot end: workflow state could not be persisted: ${error instanceof Error ? error.message : String(error)}` }],
|
|
600
|
-
details: { persistenceError: true },
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
applyControllerEffect(controller, ctx, eff);
|
|
605
|
-
if (eff.kind === "closed") {
|
|
606
|
-
controller.seenToolCallIds.clear();
|
|
607
|
-
controller.lastBlockedKey = undefined;
|
|
608
|
-
return {
|
|
609
|
-
content: [{ type: "text" as const, text: `Workflow ended and closed. It can no longer be used. Artifacts saved at ${eff.artifactDir}.` }],
|
|
610
|
-
details: { closed: true, mode: config.mode, phase: eff.phase, artifactDir: eff.artifactDir },
|
|
611
|
-
terminate: true,
|
|
612
|
-
};
|
|
613
|
-
}
|
|
614
|
-
if (eff.kind === "endBlocked") {
|
|
615
|
-
if (eff.reason) {
|
|
616
|
-
return {
|
|
617
|
-
content: [{ type: "text" as const, text: `Cannot end: ${eff.missing} is incomplete: ${eff.reason}.` }],
|
|
618
|
-
details: { mode: config.mode, phase: eff.phase, blocked: eff.missing, reason: eff.reason },
|
|
619
|
-
};
|
|
620
|
-
}
|
|
621
|
-
const isArtifactFile = !eff.missing.includes(" ") && eff.missing.includes(".");
|
|
622
|
-
const hint = isArtifactFile
|
|
623
|
-
? ` Write ${sm.snapshot.artifactDir}/${eff.missing} first.`
|
|
624
|
-
: ` Continue through the phases to ${config.phases[config.phases.length - 1]}.`;
|
|
625
|
-
return {
|
|
626
|
-
content: [{ type: "text" as const, text: `Cannot end: ${eff.missing}.${hint}` }],
|
|
627
|
-
details: { mode: config.mode, phase: eff.phase, blocked: eff.missing },
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
return {
|
|
631
|
-
content: [{ type: "text" as const, text: `No active ${config.mode} workflow to end.` }],
|
|
632
|
-
details: { active: false, mode: config.mode },
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
function registerSharedWorkflowTools(pi: ExtensionAPI): void {
|
|
637
|
-
if (registry.toolsRegisteredFor.has(pi)) return;
|
|
638
|
-
registry.toolsRegisteredFor.add(pi);
|
|
639
|
-
// Starting and resuming are user-only slash-command actions. Exposing them
|
|
640
|
-
// as model tools lets an agent opt the user into a workflow unprompted.
|
|
641
|
-
pi.registerTool({
|
|
642
|
-
name: "end_workflow",
|
|
643
|
-
label: "End Workflow",
|
|
644
|
-
description: "Close a terminal-ready durable workflow. Select its mode.",
|
|
645
|
-
parameters: Type.Object({
|
|
646
|
-
mode: Type.String({ description: "Workflow mode: prototype, quicktype, task, or another installed mode." }),
|
|
647
|
-
}),
|
|
648
|
-
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
649
|
-
const controller = controllerForMode(pi, params.mode);
|
|
650
|
-
return controller ? endController(controller, ctx) : unknownMode(params.mode);
|
|
651
|
-
},
|
|
652
|
-
} satisfies ToolDefinition);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
export function createWorkflowExtension(
|
|
656
|
-
config: WorkflowConfig,
|
|
657
|
-
options: WorkflowAdapterOptions,
|
|
658
|
-
): (pi: ExtensionAPI) => void {
|
|
659
|
-
return function workflowExtension(pi: ExtensionAPI): void {
|
|
660
|
-
replaceControllerFor(pi, config.mode);
|
|
661
|
-
const deps = makeDeps(pi, config);
|
|
662
|
-
// ponytail: default skip rule — reuse is skipped when the project has no
|
|
663
|
-
// git history. Shared by every mode today; a mode that wants a different
|
|
664
|
-
// rule supplies its own skipRules in config and we respect them as-is.
|
|
665
|
-
const skipRules = config.skipRules ?? [
|
|
666
|
-
{
|
|
667
|
-
phase: "reuse",
|
|
668
|
-
shouldSkip: async () => hasNoGitHistory(pi),
|
|
669
|
-
},
|
|
670
|
-
];
|
|
671
|
-
const sm = new WorkflowStateMachine({ ...config, skipRules });
|
|
672
|
-
const controller: WorkflowController = { pi, config, sm, deps, seenToolCallIds: new Set() };
|
|
673
|
-
registry.controllers.push(controller);
|
|
674
|
-
registerSharedArtifactWriter(pi);
|
|
675
|
-
registerSharedWorkflowTools(pi);
|
|
676
|
-
const { mode, footerLabel } = config;
|
|
677
|
-
const end = "end_workflow";
|
|
678
|
-
|
|
679
|
-
// Workflow tools are registered once per Pi instance below. Starting and
|
|
680
|
-
// resuming remain user-only through the mode-specific slash commands.
|
|
681
|
-
// ── session_start: disk state is never auto-attached. A session entry is
|
|
682
|
-
// merely a convenience pointer for rendering a stale-but-useful footer. ──
|
|
683
|
-
pi.on("session_start", async (_event: any, ctx: ExtensionContext) => {
|
|
684
|
-
if (!isRegisteredController(controller)) return;
|
|
685
|
-
const entries = ctx.sessionManager.getEntries();
|
|
686
|
-
for (let i = entries.length - 1; i >= 0; i--) {
|
|
687
|
-
const entry = entries[i] as { type: string; customType?: string; data?: any };
|
|
688
|
-
const data = entry.data;
|
|
689
|
-
if (entry.type !== "custom" || entry.customType !== config.entryType || !data || data.mode !== mode || typeof data.workflowStatePath !== "string") continue;
|
|
690
|
-
try {
|
|
691
|
-
const { run } = await resolveWorkflowRun(data.workflowStatePath, artifactsBaseFor(config), modesFor(config));
|
|
692
|
-
if (run.status === "active") {
|
|
693
|
-
ctx.ui.setStatus(config.statusKey, ctx.ui.theme.fg(
|
|
694
|
-
"warning",
|
|
695
|
-
`● ${config.footerLabel} · ${config.phases.indexOf(run.phase) + 1}/${config.phases.length} ${run.phase} (resume required)`,
|
|
696
|
-
));
|
|
697
|
-
}
|
|
698
|
-
} catch {
|
|
699
|
-
// Old/custom entries are intentionally non-authoritative.
|
|
700
|
-
}
|
|
701
|
-
break;
|
|
702
|
-
}
|
|
703
|
-
});
|
|
704
|
-
|
|
705
|
-
// ── tool_call: enforce parent-owned artifact boundaries and exclusive
|
|
706
|
-
// transition calls. The session manager exposes the current assistant
|
|
707
|
-
// message, so fail closed when its sole-call shape cannot be proven. ──
|
|
708
|
-
pi.on("tool_call", (event: any, ctx: ExtensionContext) => {
|
|
709
|
-
if (!isRegisteredController(controller)) return;
|
|
710
|
-
const tool = event.toolName;
|
|
711
|
-
const snap = sm.snapshot;
|
|
712
|
-
if (!snap.active) return;
|
|
713
|
-
const commentatorTransition = tool === "subagent" &&
|
|
714
|
-
event.input?.agent === "commentator" && snap.phase === "loop" &&
|
|
715
|
-
!isSubagentManagementAction(event.input);
|
|
716
|
-
const endTransition = tool === "end_workflow" && event.input?.mode === mode;
|
|
717
|
-
if (tool === WORKFLOW_ARTIFACT_TOOL || endTransition || commentatorTransition) {
|
|
718
|
-
const blocked = transitionBatchBlock(ctx, event.toolCallId);
|
|
719
|
-
if (blocked) return blocked;
|
|
720
|
-
}
|
|
721
|
-
if (tool !== "subagent" && tool !== "write" && tool !== "edit") return;
|
|
722
|
-
if (tool === "write" || tool === "edit") {
|
|
723
|
-
return {
|
|
724
|
-
block: true,
|
|
725
|
-
reason: `Workflow is active (${mode}/${snap.phase}). Use ${WORKFLOW_ARTIFACT_TOOL} for workflow artifacts; workspace edits must be delegated to subagents.`,
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
if (!isSubagentManagementAction(event.input) && event.input) {
|
|
729
|
-
// All child results return inline. The parent persists normal phase
|
|
730
|
-
// artifacts; the loop engine persists its own review files/marker.
|
|
731
|
-
disableSubagentOutput(event.input);
|
|
732
|
-
}
|
|
733
|
-
});
|
|
734
|
-
|
|
735
|
-
// ── tool_result: only the engine-owned loop persists subagent output. ──
|
|
736
|
-
pi.on("tool_result", async (event: any, ctx: ExtensionContext) => {
|
|
737
|
-
if (!isRegisteredController(controller)) return;
|
|
738
|
-
if (event.toolName !== "subagent" || event.isError) return;
|
|
739
|
-
// Parent-owned phases intentionally do nothing here: the main agent must
|
|
740
|
-
// inspect the inline child result and call write_workflow_artifact.
|
|
741
|
-
if (!sm.snapshot.active || sm.snapshot.phase !== "loop" || isSubagentManagementAction(event.input)) return;
|
|
742
|
-
const eventBefore = checkpoint(controller);
|
|
743
|
-
if (typeof event.toolCallId === "string") {
|
|
744
|
-
if (controller.seenToolCallIds.has(event.toolCallId)) return;
|
|
745
|
-
controller.seenToolCallIds.add(event.toolCallId);
|
|
746
|
-
}
|
|
747
|
-
// The adapter owns durable loop state; the normal subagent tool-result
|
|
748
|
-
// continuation lets the parent choose the next builder/commentator call.
|
|
749
|
-
// No synthetic follow-up chat is queued. loopState clears when the phase
|
|
750
|
-
// leaves "loop" (below).
|
|
751
|
-
if (event.toolName === "subagent" && sm.snapshot.active && sm.snapshot.phase === "loop") {
|
|
752
|
-
const dir = sm.snapshot.artifactDir;
|
|
753
|
-
const maxIt = config.loopMaxIterations ?? 3;
|
|
754
|
-
if (!controller.loopState) {
|
|
755
|
-
controller.loopState = { reviewRound: 0, maxIterations: maxIt, stage: "building" };
|
|
756
|
-
}
|
|
757
|
-
const ls = controller.loopState;
|
|
758
|
-
const agent = event.input?.agent;
|
|
759
|
-
|
|
760
|
-
if (agent === "builder") {
|
|
761
|
-
// The normal subagent result resumes the parent turn; record the
|
|
762
|
-
// next loop stage but do not inject a duplicate follow-up message.
|
|
763
|
-
ls.stage = "reviewing";
|
|
764
|
-
} else if (agent === "commentator") {
|
|
765
|
-
const reviewRound = ls.reviewRound + 1;
|
|
766
|
-
const reviewText = await textFromSubagentResult(event);
|
|
767
|
-
if (reviewText) {
|
|
768
|
-
const reviewPath = `loop-review-${reviewRound}.md`;
|
|
769
|
-
await mkdir(dir, { recursive: true });
|
|
770
|
-
await writeFile(resolve(dir, reviewPath), reviewText, "utf8");
|
|
771
|
-
ls.reviewPath = reviewPath;
|
|
772
|
-
}
|
|
773
|
-
ls.reviewRound = reviewRound;
|
|
774
|
-
const status = parseLoopReviewStatus(reviewText);
|
|
775
|
-
if (status === "clean") {
|
|
776
|
-
ls.stage = "clean";
|
|
777
|
-
await mkdir(dir, { recursive: true });
|
|
778
|
-
await writeFile(resolve(dir, config.phaseArtifacts["loop"]!), `Loop complete after ${ls.reviewRound} review round(s).\n${MARKERS.loopComplete}`, "utf8");
|
|
779
|
-
// onArtifactMaybe below sees loop-complete.md and advances to audit.
|
|
780
|
-
} else if (ls.reviewRound >= ls.maxIterations) {
|
|
781
|
-
// Cap reached without a clean review. Notify once, stop driving.
|
|
782
|
-
const wasMaxed = ls.stage === "maxed";
|
|
783
|
-
ls.stage = "maxed";
|
|
784
|
-
if (!wasMaxed) {
|
|
785
|
-
ctx.ui.notify(
|
|
786
|
-
`Workflow loop hit max iterations (${ls.maxIterations}) without a clean review. Last review status: ${status ?? "no marker"}. Resolve issues manually or re-run.`,
|
|
787
|
-
"warning",
|
|
788
|
-
);
|
|
789
|
-
}
|
|
790
|
-
// A genuine clean review on a later round still advances — the
|
|
791
|
-
// clean branch above runs first.
|
|
792
|
-
} else {
|
|
793
|
-
// Blocking (or no marker) → the parent continues with a builder
|
|
794
|
-
// fix round from the normal subagent result, without a queued chat.
|
|
795
|
-
ls.stage = "building";
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
// Non-builder/commentator subagent calls in loop fall through to
|
|
799
|
-
// onArtifactMaybe (a no-op unless loop-complete.md exists).
|
|
800
|
-
}
|
|
801
|
-
const eff = await sm.onArtifactMaybe(deps);
|
|
802
|
-
// Clear loop state once we have left the loop phase (advanced to audit).
|
|
803
|
-
if (sm.snapshot.phase !== "loop") {
|
|
804
|
-
controller.loopState = undefined;
|
|
805
|
-
}
|
|
806
|
-
try {
|
|
807
|
-
await persistAfter(controller, eventBefore);
|
|
808
|
-
} catch (error) {
|
|
809
|
-
ctx.ui.notify(`Workflow state was not saved: ${error instanceof Error ? error.message : String(error)}`, "warning");
|
|
810
|
-
return;
|
|
811
|
-
}
|
|
812
|
-
// The only artifact written in this handler is loop-complete.md, which
|
|
813
|
-
// is engine-owned. Parent-written phase artifacts advance inside
|
|
814
|
-
// write_workflow_artifact instead. A clean review is an exclusive
|
|
815
|
-
// transition boundary, so terminate and let the hidden steer continue.
|
|
816
|
-
const transition = eff.kind === "advanced" || eff.kind === "terminalReady" || eff.kind === "terminalNeedsArtifacts";
|
|
817
|
-
applyControllerEffect(controller, ctx, eff, { queuePrompt: transition });
|
|
818
|
-
return transition ? { terminate: true } : undefined;
|
|
819
|
-
});
|
|
820
|
-
|
|
821
|
-
// ── /<command> ──
|
|
822
|
-
pi.registerCommand(options.commandName, {
|
|
823
|
-
description: options.commandDescription,
|
|
824
|
-
handler: async (args: string, ctx: ExtensionCommandContext) => {
|
|
825
|
-
const trimmed = args.trim();
|
|
826
|
-
if (trimmed === "help") {
|
|
827
|
-
ctx.ui.notify(commandHelp(config, options), "info");
|
|
828
|
-
return;
|
|
829
|
-
}
|
|
830
|
-
if (!ctx.isIdle()) {
|
|
831
|
-
ctx.ui.notify(
|
|
832
|
-
`Agent is busy. Wait for it to finish before calling /${options.commandName}.`,
|
|
833
|
-
"warning",
|
|
834
|
-
);
|
|
835
|
-
return;
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
if (trimmed === "resume" || trimmed.startsWith("resume ")) {
|
|
839
|
-
if (sm.snapshot.active) {
|
|
840
|
-
ctx.ui.notify("A workflow is already active. End it before resuming another run.", "warning");
|
|
841
|
-
return;
|
|
842
|
-
}
|
|
843
|
-
const selector = trimmed.slice("resume".length).trim();
|
|
844
|
-
if (!selector) {
|
|
845
|
-
const runs = await listResumableWorkflowRuns(artifactsBaseFor(config), modesFor(config));
|
|
846
|
-
if (!runs.length) {
|
|
847
|
-
ctx.ui.notify(`No resumable ${mode} workflows found.`, "info");
|
|
848
|
-
return;
|
|
849
|
-
}
|
|
850
|
-
if (!ctx.hasUI) {
|
|
851
|
-
ctx.ui.notify(runs.map(({ run }) => `/${options.commandName} resume ${run.id} # ${run.phase}, ${run.goal}, ${run.updatedAt}`).join("\n"), "info");
|
|
852
|
-
return;
|
|
853
|
-
}
|
|
854
|
-
const labels = runs.map(({ run }) => `${run.id} · ${run.phase} · ${run.goal} · ${run.updatedAt}`);
|
|
855
|
-
const choice = await ctx.ui.select(`Resume ${mode} workflow`, labels);
|
|
856
|
-
if (choice === undefined) return;
|
|
857
|
-
const index = labels.indexOf(choice);
|
|
858
|
-
if (index < 0) return;
|
|
859
|
-
const result = await resumeController(controller, ctx, runs[index]!.run.id, end);
|
|
860
|
-
ctx.ui.notify(result.content[0].text, result.details.rejected || result.details.persistenceError ? "warning" : "info");
|
|
861
|
-
return;
|
|
862
|
-
}
|
|
863
|
-
const result = await resumeController(controller, ctx, selector, end);
|
|
864
|
-
ctx.ui.notify(result.content[0].text, result.details.rejected || result.details.persistenceError ? "warning" : "info");
|
|
865
|
-
return;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
if (sm.snapshot.active) {
|
|
869
|
-
let goal = args.trim();
|
|
870
|
-
const choice = ctx.hasUI
|
|
871
|
-
? await ctx.ui.select(
|
|
872
|
-
`Workflow already active (phase: ${sm.snapshot.phase}). What do you want to do?`,
|
|
873
|
-
[
|
|
874
|
-
`Continue current workflow at ${sm.snapshot.phase}`,
|
|
875
|
-
"Close current and start a new one",
|
|
876
|
-
],
|
|
877
|
-
)
|
|
878
|
-
: undefined;
|
|
879
|
-
if (choice === undefined) return;
|
|
880
|
-
if (choice.startsWith("Continue")) {
|
|
881
|
-
const eff = sm.continueCurrent();
|
|
882
|
-
// The explicit Continue action below sends exactly one prompt.
|
|
883
|
-
applyControllerEffect(controller, ctx, eff, { queuePrompt: false });
|
|
884
|
-
if (eff.kind === "advanced" && eff.prompt) {
|
|
885
|
-
// continueCurrent already applied footer+entry; send the prompt.
|
|
886
|
-
continueAgent(pi, ctx, eff.prompt);
|
|
887
|
-
}
|
|
888
|
-
return;
|
|
889
|
-
}
|
|
890
|
-
if (!goal && ctx.hasUI) {
|
|
891
|
-
goal = (await ctx.ui.input("Goal for the new workflow:")) ?? "";
|
|
892
|
-
}
|
|
893
|
-
if (!goal.trim()) {
|
|
894
|
-
ctx.ui.notify(
|
|
895
|
-
"No goal provided. Keeping current workflow active.",
|
|
896
|
-
"warning",
|
|
897
|
-
);
|
|
898
|
-
return;
|
|
899
|
-
}
|
|
900
|
-
const closed = sm.closeCurrent();
|
|
901
|
-
if (closed) {
|
|
902
|
-
// Detach only: the on-disk active record remains resumable. Only
|
|
903
|
-
// end_workflow({ mode }) writes status: completed.
|
|
904
|
-
applyEntry(pi, config, { ...closed, done: false }, controller.run);
|
|
905
|
-
ctx.ui.notify(`Detached ${mode} workflow at ${closed.artifactDir}; it remains resumable.`, "info");
|
|
906
|
-
}
|
|
907
|
-
args = goal;
|
|
908
|
-
} else if (!args.trim()) {
|
|
909
|
-
ctx.ui.notify(commandHelp(config, options), "info");
|
|
910
|
-
return;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
const other = activeControllersFor(pi).find((c) => c.sm !== sm);
|
|
914
|
-
if (other) {
|
|
915
|
-
ctx.ui.notify(
|
|
916
|
-
`${other.config.footerLabel} workflow already active at ${other.sm.snapshot.phase}. Close it before starting ${mode}.`,
|
|
917
|
-
"warning",
|
|
918
|
-
);
|
|
919
|
-
return;
|
|
920
|
-
}
|
|
921
|
-
const before = checkpoint(controller);
|
|
922
|
-
const eff = await sm.start(args, deps);
|
|
923
|
-
if (eff.kind === "started") {
|
|
924
|
-
controller.run = {
|
|
925
|
-
version: 1,
|
|
926
|
-
id: basename(sm.snapshot.artifactDir),
|
|
927
|
-
mode,
|
|
928
|
-
status: "active",
|
|
929
|
-
goal: sm.snapshot.userPrompt,
|
|
930
|
-
artifactDir: sm.snapshot.artifactDir,
|
|
931
|
-
phase: sm.snapshot.phase,
|
|
932
|
-
autoArmed: sm.snapshot.autoArmed,
|
|
933
|
-
createdAt: new Date().toISOString(),
|
|
934
|
-
updatedAt: new Date().toISOString(),
|
|
935
|
-
};
|
|
936
|
-
try {
|
|
937
|
-
await persistAfter(controller, before);
|
|
938
|
-
} catch (error) {
|
|
939
|
-
ctx.ui.notify(`Could not start durable workflow: ${error instanceof Error ? error.message : String(error)}`, "warning");
|
|
940
|
-
return;
|
|
941
|
-
}
|
|
942
|
-
controller.seenToolCallIds.clear();
|
|
943
|
-
controller.lastBlockedKey = undefined;
|
|
944
|
-
}
|
|
945
|
-
applyControllerEffect(controller, ctx, eff);
|
|
946
|
-
},
|
|
947
|
-
});
|
|
948
|
-
};
|
|
949
|
-
}
|