@bastani/atomic 0.8.13-0 → 0.8.14-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/host-html-template.ts +1 -1
- package/dist/builtin/mcp/init.ts +15 -2
- package/dist/builtin/mcp/mcp-callback-server.ts +10 -9
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/mcp/ui-session.ts +9 -6
- package/dist/builtin/subagents/CHANGELOG.md +8 -1
- package/dist/builtin/subagents/README.md +39 -32
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/skills/subagent/SKILL.md +11 -11
- package/dist/builtin/subagents/src/agents/agent-management.ts +6 -1
- package/dist/builtin/subagents/src/agents/agent-serializer.ts +2 -0
- package/dist/builtin/subagents/src/agents/agents.ts +44 -19
- package/dist/builtin/subagents/src/extension/config.ts +16 -0
- package/dist/builtin/subagents/src/extension/fanout-child.ts +246 -0
- package/dist/builtin/subagents/src/extension/index.ts +466 -603
- package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +6 -4
- package/dist/builtin/subagents/src/intercom/result-intercom.ts +109 -1
- package/dist/builtin/subagents/src/runs/background/async-execution.ts +124 -19
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +41 -6
- package/dist/builtin/subagents/src/runs/background/async-resume.ts +28 -15
- package/dist/builtin/subagents/src/runs/background/async-status.ts +60 -30
- package/dist/builtin/subagents/src/runs/background/result-watcher.ts +111 -54
- package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +83 -0
- package/dist/builtin/subagents/src/runs/background/run-status.ts +79 -3
- package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +46 -1
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +66 -14
- package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +10 -3
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +14 -2
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +320 -23
- package/dist/builtin/subagents/src/runs/shared/completion-guard.ts +23 -1
- package/dist/builtin/subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +369 -0
- package/dist/builtin/subagents/src/runs/shared/nested-events.ts +935 -0
- package/dist/builtin/subagents/src/runs/shared/nested-path.ts +52 -0
- package/dist/builtin/subagents/src/runs/shared/nested-render.ts +115 -0
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +1 -0
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +82 -9
- package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/single-output.ts +12 -2
- package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +32 -10
- package/dist/builtin/subagents/src/runs/shared/worktree.ts +3 -2
- package/dist/builtin/subagents/src/shared/artifacts.ts +0 -1
- package/dist/builtin/subagents/src/shared/types.ts +96 -1
- package/dist/builtin/subagents/src/shared/utils.ts +10 -2
- package/dist/builtin/subagents/src/slash/slash-commands.ts +468 -625
- package/dist/builtin/subagents/src/tui/render.ts +1227 -2093
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +24 -0
- package/dist/builtin/workflows/README.md +28 -11
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +323 -40
- package/dist/builtin/workflows/builtin/ralph.ts +362 -176
- package/dist/builtin/workflows/package.json +2 -5
- package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
- package/dist/builtin/workflows/skills/skill-creator/LICENSE.txt +202 -0
- package/dist/builtin/workflows/skills/skill-creator/SKILL.md +489 -0
- package/dist/builtin/workflows/skills/skill-creator/agents/analyzer.md +274 -0
- package/dist/builtin/workflows/skills/skill-creator/agents/comparator.md +202 -0
- package/dist/builtin/workflows/skills/skill-creator/agents/grader.md +223 -0
- package/dist/builtin/workflows/skills/skill-creator/assets/eval_review.html +146 -0
- package/dist/builtin/workflows/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/dist/builtin/workflows/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/dist/builtin/workflows/skills/skill-creator/references/schemas.md +430 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/__init__.py +0 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/generate_report.py +326 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/improve_description.py +247 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/package_skill.py +136 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/run_eval.py +310 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/run_loop.py +328 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/utils.py +47 -0
- package/dist/builtin/workflows/src/extension/index.ts +869 -93
- package/dist/builtin/workflows/src/extension/render-call.ts +34 -1
- package/dist/builtin/workflows/src/extension/render-result.ts +126 -21
- package/dist/builtin/workflows/src/extension/runtime.ts +91 -3
- package/dist/builtin/workflows/src/extension/wiring.ts +38 -12
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +62 -5
- package/dist/builtin/workflows/src/runs/background/runner.ts +3 -3
- package/dist/builtin/workflows/src/runs/background/status.ts +42 -8
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +410 -95
- package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +5 -2
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +8 -0
- package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +6 -4
- package/dist/builtin/workflows/src/runs/shared/worktree.ts +3 -2
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +138 -5
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +30 -0
- package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +78 -120
- package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +193 -0
- package/dist/builtin/workflows/src/shared/store-types.ts +26 -1
- package/dist/builtin/workflows/src/shared/store.ts +145 -17
- package/dist/builtin/workflows/src/shared/timing.ts +6 -2
- package/dist/builtin/workflows/src/shared/workflow-failures.ts +375 -0
- package/dist/builtin/workflows/src/tui/chat-surface.ts +68 -17
- package/dist/builtin/workflows/src/tui/connectors.ts +2 -2
- package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +24 -26
- package/dist/builtin/workflows/src/tui/graph-canvas.ts +4 -8
- package/dist/builtin/workflows/src/tui/graph-view.ts +17 -14
- package/dist/builtin/workflows/src/tui/header.ts +38 -0
- package/dist/builtin/workflows/src/tui/inline-form-card.ts +161 -238
- package/dist/builtin/workflows/src/tui/inline-form-editor.ts +68 -73
- package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +2 -3
- package/dist/builtin/workflows/src/tui/inline-form-store.ts +2 -1
- package/dist/builtin/workflows/src/tui/inputs-overlay.ts +1 -3
- package/dist/builtin/workflows/src/tui/inputs-picker.ts +286 -399
- package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +11 -0
- package/dist/builtin/workflows/src/tui/node-card.ts +2 -1
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +9 -1
- package/dist/builtin/workflows/src/tui/prompt-card.ts +46 -19
- package/dist/builtin/workflows/src/tui/run-detail.ts +63 -80
- package/dist/builtin/workflows/src/tui/session-confirm.ts +9 -3
- package/dist/builtin/workflows/src/tui/session-picker.ts +19 -16
- package/dist/builtin/workflows/src/tui/stage-chat-layout.ts +88 -0
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +368 -879
- package/dist/builtin/workflows/src/tui/status-helpers.ts +4 -0
- package/dist/builtin/workflows/src/tui/status-list.ts +67 -75
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +50 -12
- package/dist/builtin/workflows/src/tui/submit-pane.ts +164 -0
- package/dist/builtin/workflows/src/tui/switcher.ts +27 -4
- package/dist/builtin/workflows/src/tui/text-helpers.ts +98 -4
- package/dist/builtin/workflows/src/tui/widget.ts +90 -68
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +23 -2
- package/dist/builtin/workflows/src/tui/workflow-list.ts +44 -68
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -3
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -10
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +2 -1
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +3 -2
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +6 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +16 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +8 -9
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +3 -2
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +2 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js +8 -6
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/template.js +6 -3
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +12 -29
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +5 -1
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/package-manager.d.ts +8 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +145 -58
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +6 -20
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +38 -31
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +9 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +32 -24
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +8 -15
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +8 -22
- package/dist/core/skills.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts +5 -4
- package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/questionnaire-session.js +34 -11
- package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts +1 -0
- package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/contract.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/projections.js +1 -0
- package/dist/core/tools/ask-user-question/state/selectors/projections.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/state-reducer.d.ts +1 -2
- package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/state-reducer.js +26 -9
- package/dist/core/tools/ask-user-question/state/state-reducer.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/state.d.ts +4 -0
- package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/state.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts +1 -0
- package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/option-list-view.js +1 -0
- package/dist/core/tools/ask-user-question/view/components/option-list-view.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts +9 -6
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +28 -7
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/props-adapter.js +4 -1
- package/dist/core/tools/ask-user-question/view/props-adapter.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +56 -53
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +3 -1
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +8 -1
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts +3 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +44 -81
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js +27 -12
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +2 -3
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +3 -3
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +5 -5
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +2 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +11 -4
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/path-utils.d.ts +2 -0
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js +39 -21
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +9 -8
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/truncate.d.ts.map +1 -1
- package/dist/core/tools/truncate.js +12 -2
- package/dist/core/tools/truncate.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +20 -35
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +5 -6
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/chat-input-actions.d.ts +24 -0
- package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -0
- package/dist/modes/interactive/chat-input-actions.js +179 -0
- package/dist/modes/interactive/chat-input-actions.js.map +1 -0
- package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -0
- package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.js +14 -3
- package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.d.ts +157 -0
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -0
- package/dist/modes/interactive/components/chat-session-host.js +1007 -0
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -0
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +1 -1
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +14 -5
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +9 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +29 -4
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +18 -67
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/child-process.d.ts +1 -0
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js +8 -0
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +3 -1
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js +14 -8
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts +30 -0
- package/dist/utils/image-resize-core.d.ts.map +1 -0
- package/dist/utils/image-resize-core.js +124 -0
- package/dist/utils/image-resize-core.js.map +1 -0
- package/dist/utils/image-resize-worker.d.ts +2 -0
- package/dist/utils/image-resize-worker.d.ts.map +1 -0
- package/dist/utils/image-resize-worker.js +31 -0
- package/dist/utils/image-resize-worker.js.map +1 -0
- package/dist/utils/image-resize.d.ts +7 -27
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +75 -115
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/paths.d.ts +16 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +49 -7
- package/dist/utils/paths.js.map +1 -1
- package/docs/changelog.mdx +29 -0
- package/docs/compaction.md +1 -1
- package/docs/custom-provider.md +2 -2
- package/docs/development.md +1 -1
- package/docs/docs.json +98 -143
- package/docs/extensions.md +29 -16
- package/docs/favicon.svg +29 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/images/tree-view.png +0 -0
- package/docs/images/workflow-command.png +0 -0
- package/docs/images/workflow-graph.png +0 -0
- package/docs/images/workflow-input-picker.png +0 -0
- package/docs/images/workflow-list.png +0 -0
- package/docs/index.md +10 -1
- package/docs/logo.svg +59 -0
- package/docs/packages.md +3 -3
- package/docs/providers.md +1 -1
- package/docs/quickstart.md +98 -2
- package/docs/rpc.md +8 -8
- package/docs/sdk.md +23 -12
- package/docs/sessions.md +1 -1
- package/docs/skills.md +15 -1
- package/docs/termux.md +11 -1
- package/docs/themes.md +6 -6
- package/docs/tui.md +18 -18
- package/docs/usage.md +1 -1
- package/docs/workflows.md +172 -2
- package/examples/extensions/subagent/index.ts +2 -1
- package/package.json +6 -6
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/SKILL.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/element-attributes.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/playwright-tests.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/request-mocking.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/running-code.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/session-management.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/spec-driven-testing.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/storage-state.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/test-generation.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/tracing.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/video-recording.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/SKILL.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/deep-modules.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/interface-design.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/mocking.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/refactoring.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/tests.md +0 -0
|
@@ -9,6 +9,7 @@ import { renderInputsSchema } from "../shared/render-inputs-schema.js";
|
|
|
9
9
|
import { WorkflowParametersSchema } from "./workflow-schema.js";
|
|
10
10
|
import { renderRunBanner, renderRunSummary } from "./renderers.js";
|
|
11
11
|
import type { RunEndPayload, RunStartPayload } from "./renderers.js";
|
|
12
|
+
import type { StageSnapshot, StageStatus, ToolEvent } from "../shared/store-types.js";
|
|
12
13
|
import { store } from "../shared/store.js";
|
|
13
14
|
import { restoreOnSessionStart } from "../shared/persistence-restore.js";
|
|
14
15
|
import type { SessionManager } from "../shared/persistence-restore.js";
|
|
@@ -19,11 +20,13 @@ import {
|
|
|
19
20
|
destroyAllRuns,
|
|
20
21
|
resumeRun,
|
|
21
22
|
pauseRun,
|
|
23
|
+
pauseAllRuns,
|
|
22
24
|
interruptRun,
|
|
23
25
|
interruptAllRuns,
|
|
24
26
|
inspectRun,
|
|
25
27
|
} from "../runs/background/status.js";
|
|
26
28
|
import { cancellationRegistry } from "../runs/background/cancellation-registry.js";
|
|
29
|
+
import { stageControlRegistry } from "../runs/foreground/stage-control-registry.js";
|
|
27
30
|
import { registerIntercomParentSession } from "../intercom/intercom-bridge.js";
|
|
28
31
|
import { subscribeIntercomControl } from "../intercom/result-intercom.js";
|
|
29
32
|
import { buildIntercomCallbacks } from "../intercom/intercom-routing.js";
|
|
@@ -205,6 +208,7 @@ export interface PiToolOpts<TArgs, TDetails> {
|
|
|
205
208
|
label: string;
|
|
206
209
|
description: string;
|
|
207
210
|
parameters: unknown; // TypeBox TSchema — pi consumes it opaquely
|
|
211
|
+
renderShell?: "default" | "self";
|
|
208
212
|
/**
|
|
209
213
|
* Pi calls execute positionally: `(toolCallId, params, signal, onUpdate, ctx)`.
|
|
210
214
|
* cross-ref: pi-coding-agent dist/core/extensions/types.d.ts ToolDefinition.execute
|
|
@@ -272,9 +276,8 @@ export interface ExtensionAPI {
|
|
|
272
276
|
renderer: (payload: unknown) => string,
|
|
273
277
|
) => void;
|
|
274
278
|
/**
|
|
275
|
-
* Inject a custom message into
|
|
276
|
-
*
|
|
277
|
-
* "workflows:input-form"`. The card stays in scrollback and is
|
|
279
|
+
* Inject a custom message into chat history. Used by inline workflow surfaces
|
|
280
|
+
* such as `workflows:input-form`; cards stay in scrollback and are
|
|
278
281
|
* re-rendered by the registered renderer on every `tui.requestRender()`.
|
|
279
282
|
*/
|
|
280
283
|
sendMessage?: <T = unknown>(
|
|
@@ -397,9 +400,15 @@ export interface WorkflowToolArgs extends StageOptions {
|
|
|
397
400
|
| "list"
|
|
398
401
|
| "get"
|
|
399
402
|
| "status"
|
|
403
|
+
| "stages"
|
|
404
|
+
| "stage"
|
|
405
|
+
| "transcript"
|
|
406
|
+
| "send"
|
|
407
|
+
| "pause"
|
|
400
408
|
| "interrupt"
|
|
401
409
|
| "kill"
|
|
402
410
|
| "resume"
|
|
411
|
+
| "reload"
|
|
403
412
|
| "inputs";
|
|
404
413
|
/** Canonical run identifier or unique prefix for status/interrupt/kill/resume. */
|
|
405
414
|
runId?: string;
|
|
@@ -409,6 +418,16 @@ export interface WorkflowToolArgs extends StageOptions {
|
|
|
409
418
|
stageId?: string;
|
|
410
419
|
/** Optional message forwarded when resuming paused work. */
|
|
411
420
|
message?: string;
|
|
421
|
+
statusFilter?: StageStatus | "all";
|
|
422
|
+
format?: "text" | "json";
|
|
423
|
+
limit?: number;
|
|
424
|
+
tail?: number;
|
|
425
|
+
includeToolOutput?: boolean;
|
|
426
|
+
text?: string;
|
|
427
|
+
response?: unknown;
|
|
428
|
+
delivery?: "auto" | "answer" | "prompt" | "steer" | "followUp" | "resume";
|
|
429
|
+
promptId?: string;
|
|
430
|
+
reason?: string;
|
|
412
431
|
/** Direct single-task mode, or root task string when chain is present. */
|
|
413
432
|
task?: WorkflowDirectTaskItem | string;
|
|
414
433
|
/** Direct top-level parallel mode. */
|
|
@@ -510,6 +529,136 @@ function workflowRunResultFromDetails(
|
|
|
510
529
|
};
|
|
511
530
|
}
|
|
512
531
|
|
|
532
|
+
function stringifyWorkflowToolResult(result: WorkflowToolResult): string {
|
|
533
|
+
return JSON.stringify(result, null, 2);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function compactWorkflowToolMessage(
|
|
537
|
+
result: Extract<WorkflowToolResult, {
|
|
538
|
+
action: "send" | "pause" | "reload" | "interrupt" | "kill" | "resume";
|
|
539
|
+
}>,
|
|
540
|
+
): string {
|
|
541
|
+
if (result.action === "reload") {
|
|
542
|
+
return `${result.action}: ${result.status} — ${result.message}`;
|
|
543
|
+
}
|
|
544
|
+
const target = [
|
|
545
|
+
result.runId,
|
|
546
|
+
result.action === "send" ? result.stageId : undefined,
|
|
547
|
+
].filter((part): part is string => part !== undefined && part.length > 0)
|
|
548
|
+
.join("/");
|
|
549
|
+
return `${result.action}:${target ? ` ${target}` : ""} ${result.status} — ${result.message}`;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function renderTranscriptToolContent(
|
|
553
|
+
result: Extract<WorkflowToolResult, { action: "transcript" }>,
|
|
554
|
+
): string {
|
|
555
|
+
const lines = [
|
|
556
|
+
`action: transcript`,
|
|
557
|
+
`runId: ${result.runId}`,
|
|
558
|
+
`stageId: ${result.stageId}`,
|
|
559
|
+
`source: ${result.source}`,
|
|
560
|
+
`truncated: ${result.truncated}`,
|
|
561
|
+
];
|
|
562
|
+
if (result.sessionId) lines.push(`sessionId: ${result.sessionId}`);
|
|
563
|
+
if (result.sessionFile) lines.push(`sessionFile: ${result.sessionFile}`);
|
|
564
|
+
if (result.entries.length === 0) {
|
|
565
|
+
lines.push("entries: none");
|
|
566
|
+
return lines.join("\n");
|
|
567
|
+
}
|
|
568
|
+
lines.push("entries:");
|
|
569
|
+
result.entries.forEach((entry, index) => {
|
|
570
|
+
const metadata = [
|
|
571
|
+
`[${index + 1}]`,
|
|
572
|
+
`role=${entry.role}`,
|
|
573
|
+
entry.toolName ? `tool=${entry.toolName}` : undefined,
|
|
574
|
+
entry.timestamp !== undefined ? `timestamp=${entry.timestamp}` : undefined,
|
|
575
|
+
].filter((part): part is string => part !== undefined);
|
|
576
|
+
lines.push(metadata.join(" "));
|
|
577
|
+
if (entry.text !== undefined) lines.push(entry.text);
|
|
578
|
+
if (entry.output !== undefined) {
|
|
579
|
+
lines.push("tool output:");
|
|
580
|
+
lines.push(entry.output);
|
|
581
|
+
}
|
|
582
|
+
if (entry.text === undefined && entry.output === undefined) {
|
|
583
|
+
lines.push("(no body)");
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
return lines.join("\n");
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function renderStagesToolContent(
|
|
590
|
+
result: Extract<WorkflowToolResult, { action: "stages" }>,
|
|
591
|
+
): string {
|
|
592
|
+
const lines = [
|
|
593
|
+
"action: stages",
|
|
594
|
+
`runId: ${result.runId}`,
|
|
595
|
+
`filter: ${result.filter}`,
|
|
596
|
+
];
|
|
597
|
+
if (result.error) lines.push(`error: ${result.error}`);
|
|
598
|
+
if (result.stages.length === 0) {
|
|
599
|
+
lines.push("stages: none");
|
|
600
|
+
return lines.join("\n");
|
|
601
|
+
}
|
|
602
|
+
lines.push("stages:");
|
|
603
|
+
result.stages.forEach((stage, index) => {
|
|
604
|
+
lines.push(`[${index + 1}] ${stage.name} (${stage.id}) ${stage.status}`);
|
|
605
|
+
if (stage.sessionId) lines.push(`sessionId: ${stage.sessionId}`);
|
|
606
|
+
if (stage.sessionFile) lines.push(`sessionFile: ${stage.sessionFile}`);
|
|
607
|
+
if (stage.error) lines.push(`error: ${stage.error}`);
|
|
608
|
+
if (stage.awaitingInputSince !== undefined) {
|
|
609
|
+
lines.push(`awaitingInputSince: ${stage.awaitingInputSince}`);
|
|
610
|
+
}
|
|
611
|
+
if (stage.pendingPrompt !== undefined) {
|
|
612
|
+
lines.push("pendingPrompt:");
|
|
613
|
+
lines.push(JSON.stringify(stage.pendingPrompt, null, 2));
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
return lines.join("\n");
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function renderStageToolContent(
|
|
620
|
+
result: Extract<WorkflowToolResult, { action: "stage" }>,
|
|
621
|
+
): string {
|
|
622
|
+
const lines = ["action: stage", `runId: ${result.runId}`];
|
|
623
|
+
if (result.error || result.stage === undefined) {
|
|
624
|
+
lines.push(`error: ${result.error ?? "stage not found"}`);
|
|
625
|
+
return lines.join("\n");
|
|
626
|
+
}
|
|
627
|
+
lines.push("stage:");
|
|
628
|
+
lines.push(JSON.stringify(result.stage, null, 2));
|
|
629
|
+
return lines.join("\n");
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function renderWorkflowToolContent(
|
|
633
|
+
result: WorkflowToolResult,
|
|
634
|
+
args: WorkflowToolArgs,
|
|
635
|
+
): string {
|
|
636
|
+
if (args.format === "json") return stringifyWorkflowToolResult(result);
|
|
637
|
+
|
|
638
|
+
switch (result.action) {
|
|
639
|
+
case "transcript":
|
|
640
|
+
return renderTranscriptToolContent(result);
|
|
641
|
+
case "stages":
|
|
642
|
+
return renderStagesToolContent(result);
|
|
643
|
+
case "stage":
|
|
644
|
+
return renderStageToolContent(result);
|
|
645
|
+
case "send":
|
|
646
|
+
case "pause":
|
|
647
|
+
case "reload":
|
|
648
|
+
case "interrupt":
|
|
649
|
+
case "kill":
|
|
650
|
+
case "resume":
|
|
651
|
+
return compactWorkflowToolMessage(result);
|
|
652
|
+
case "list":
|
|
653
|
+
case "status":
|
|
654
|
+
case "statusDetail":
|
|
655
|
+
case "inputs":
|
|
656
|
+
case "get":
|
|
657
|
+
case "run":
|
|
658
|
+
return stringifyWorkflowToolResult(result);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
513
662
|
function workflowGetResult(
|
|
514
663
|
runtime: ExtensionRuntime,
|
|
515
664
|
args: WorkflowToolArgs,
|
|
@@ -549,6 +698,243 @@ function workflowGetResult(
|
|
|
549
698
|
};
|
|
550
699
|
}
|
|
551
700
|
|
|
701
|
+
// ---------------------------------------------------------------------------
|
|
702
|
+
// Stage tool helpers
|
|
703
|
+
// ---------------------------------------------------------------------------
|
|
704
|
+
|
|
705
|
+
type WorkflowStageSummary = {
|
|
706
|
+
id: string;
|
|
707
|
+
name: string;
|
|
708
|
+
status: StageStatus;
|
|
709
|
+
sessionId?: string;
|
|
710
|
+
sessionFile?: string;
|
|
711
|
+
error?: string;
|
|
712
|
+
awaitingInputSince?: number;
|
|
713
|
+
pendingPrompt?: StageSnapshot["pendingPrompt"];
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
type WorkflowTranscriptEntry = {
|
|
717
|
+
role: string;
|
|
718
|
+
text?: string;
|
|
719
|
+
toolName?: string;
|
|
720
|
+
output?: string;
|
|
721
|
+
timestamp?: number;
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
type MessageContentBlock = { readonly type?: string; readonly text?: string };
|
|
725
|
+
type MessageLike = {
|
|
726
|
+
readonly role?: string;
|
|
727
|
+
readonly content?: string | readonly MessageContentBlock[];
|
|
728
|
+
readonly name?: string;
|
|
729
|
+
readonly toolName?: string;
|
|
730
|
+
readonly timestamp?: number;
|
|
731
|
+
readonly createdAt?: number;
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
function cloneStage(stage: StageSnapshot): StageSnapshot {
|
|
735
|
+
return structuredClone(stage);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
function summarizeStage(stage: StageSnapshot): WorkflowStageSummary {
|
|
739
|
+
return {
|
|
740
|
+
id: stage.id,
|
|
741
|
+
name: stage.name,
|
|
742
|
+
status: stage.status,
|
|
743
|
+
sessionId: stage.sessionId,
|
|
744
|
+
sessionFile: stage.sessionFile,
|
|
745
|
+
error: stage.error,
|
|
746
|
+
awaitingInputSince: stage.awaitingInputSince,
|
|
747
|
+
pendingPrompt: stage.pendingPrompt === undefined
|
|
748
|
+
? undefined
|
|
749
|
+
: structuredClone(stage.pendingPrompt),
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const DEFAULT_TRANSCRIPT_LIMIT = 50;
|
|
754
|
+
|
|
755
|
+
function boundedCount(args: WorkflowToolArgs): number {
|
|
756
|
+
const raw = args.tail ?? args.limit;
|
|
757
|
+
if (raw === undefined) return DEFAULT_TRANSCRIPT_LIMIT;
|
|
758
|
+
if (!Number.isFinite(raw) || raw <= 0) return 0;
|
|
759
|
+
return Math.floor(raw);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function applyEntryLimit<T>(
|
|
763
|
+
entries: readonly T[],
|
|
764
|
+
args: WorkflowToolArgs,
|
|
765
|
+
): { entries: T[]; truncated: boolean } {
|
|
766
|
+
const count = boundedCount(args);
|
|
767
|
+
if (count === 0) {
|
|
768
|
+
return { entries: [], truncated: false };
|
|
769
|
+
}
|
|
770
|
+
if (entries.length <= count) {
|
|
771
|
+
return { entries: [...entries], truncated: false };
|
|
772
|
+
}
|
|
773
|
+
return { entries: entries.slice(entries.length - count), truncated: true };
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function messageText(content: MessageLike["content"]): string | undefined {
|
|
777
|
+
if (typeof content === "string") return content;
|
|
778
|
+
if (!Array.isArray(content)) return undefined;
|
|
779
|
+
let sawTextBlock = false;
|
|
780
|
+
const text = content
|
|
781
|
+
.map((block) => {
|
|
782
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
783
|
+
sawTextBlock = true;
|
|
784
|
+
return block.text;
|
|
785
|
+
}
|
|
786
|
+
return "";
|
|
787
|
+
})
|
|
788
|
+
.join("");
|
|
789
|
+
return sawTextBlock ? text : undefined;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
function transcriptEntryFromMessage(message: MessageLike): WorkflowTranscriptEntry {
|
|
793
|
+
const entry: WorkflowTranscriptEntry = { role: message.role ?? "unknown" };
|
|
794
|
+
const text = messageText(message.content);
|
|
795
|
+
if (text !== undefined) entry.text = text;
|
|
796
|
+
const toolName = message.toolName ?? message.name;
|
|
797
|
+
if (toolName !== undefined) entry.toolName = toolName;
|
|
798
|
+
const timestamp = message.timestamp ?? message.createdAt;
|
|
799
|
+
if (timestamp !== undefined) entry.timestamp = timestamp;
|
|
800
|
+
return entry;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
function transcriptEntriesFromToolEvents(
|
|
804
|
+
events: readonly ToolEvent[],
|
|
805
|
+
includeOutput: boolean,
|
|
806
|
+
): WorkflowTranscriptEntry[] {
|
|
807
|
+
return events.map((event) => ({
|
|
808
|
+
role: "tool",
|
|
809
|
+
toolName: event.name,
|
|
810
|
+
output: includeOutput ? event.output : undefined,
|
|
811
|
+
timestamp: event.endedAt ?? event.startedAt,
|
|
812
|
+
}));
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
function hasPayloadProperty(args: WorkflowToolArgs): boolean {
|
|
816
|
+
return (
|
|
817
|
+
args.text !== undefined ||
|
|
818
|
+
args.response !== undefined ||
|
|
819
|
+
args.message !== undefined
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function promptPayloadFromArgs(args: WorkflowToolArgs): unknown {
|
|
824
|
+
if (args.response !== undefined) return args.response;
|
|
825
|
+
if (args.text !== undefined) return args.text;
|
|
826
|
+
return args.message;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function textPayloadFromArgs(args: WorkflowToolArgs): string | undefined {
|
|
830
|
+
if (args.text !== undefined) return args.text;
|
|
831
|
+
if (typeof args.response === "string") {
|
|
832
|
+
return args.response;
|
|
833
|
+
}
|
|
834
|
+
if (args.message !== undefined) return args.message;
|
|
835
|
+
return undefined;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
type WorkflowSendToolResult = Extract<WorkflowToolResult, { action: "send" }>;
|
|
839
|
+
|
|
840
|
+
function workflowSendResult(
|
|
841
|
+
runId: string,
|
|
842
|
+
stageId: string,
|
|
843
|
+
delivery: WorkflowSendToolResult["delivery"],
|
|
844
|
+
status: WorkflowSendToolResult["status"],
|
|
845
|
+
message: string,
|
|
846
|
+
): WorkflowSendToolResult {
|
|
847
|
+
return { action: "send", runId, stageId, delivery, status, message };
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
function sortTranscriptEntriesChronologically(
|
|
851
|
+
entries: readonly WorkflowTranscriptEntry[],
|
|
852
|
+
): WorkflowTranscriptEntry[] {
|
|
853
|
+
return entries
|
|
854
|
+
.map((entry, index) => ({ entry, index }))
|
|
855
|
+
.sort((a, b) => {
|
|
856
|
+
const aTimestamp = a.entry.timestamp;
|
|
857
|
+
const bTimestamp = b.entry.timestamp;
|
|
858
|
+
if (
|
|
859
|
+
typeof aTimestamp === "number" &&
|
|
860
|
+
typeof bTimestamp === "number" &&
|
|
861
|
+
aTimestamp !== bTimestamp
|
|
862
|
+
) {
|
|
863
|
+
return aTimestamp - bTimestamp;
|
|
864
|
+
}
|
|
865
|
+
return a.index - b.index;
|
|
866
|
+
})
|
|
867
|
+
.map(({ entry }) => entry);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
function terminalTranscriptEntry(
|
|
871
|
+
role: "assistant" | "notice",
|
|
872
|
+
text: string,
|
|
873
|
+
endedAt: number | undefined,
|
|
874
|
+
): WorkflowTranscriptEntry {
|
|
875
|
+
const entry: WorkflowTranscriptEntry = { role, text };
|
|
876
|
+
if (endedAt !== undefined) entry.timestamp = endedAt;
|
|
877
|
+
return entry;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
function snapshotTranscriptEntries(
|
|
881
|
+
snapshot: StageSnapshot | undefined,
|
|
882
|
+
includeOutput: boolean,
|
|
883
|
+
): WorkflowTranscriptEntry[] {
|
|
884
|
+
if (snapshot === undefined) return [];
|
|
885
|
+
const entries: WorkflowTranscriptEntry[] = [
|
|
886
|
+
...transcriptEntriesFromToolEvents(snapshot.toolEvents ?? [], includeOutput),
|
|
887
|
+
];
|
|
888
|
+
if (snapshot.result !== undefined) {
|
|
889
|
+
entries.push(terminalTranscriptEntry("assistant", snapshot.result, snapshot.endedAt));
|
|
890
|
+
}
|
|
891
|
+
if (snapshot.error !== undefined) {
|
|
892
|
+
entries.push(terminalTranscriptEntry("notice", snapshot.error, snapshot.endedAt));
|
|
893
|
+
}
|
|
894
|
+
return sortTranscriptEntriesChronologically(entries);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
function stageFailureMessage(
|
|
898
|
+
runId: string,
|
|
899
|
+
resultReason: string,
|
|
900
|
+
action: "pause" | "interrupt",
|
|
901
|
+
): string {
|
|
902
|
+
switch (resultReason) {
|
|
903
|
+
case "not_found":
|
|
904
|
+
return `Run not found: ${runId}`;
|
|
905
|
+
case "already_ended":
|
|
906
|
+
return `Run already ended: ${runId}`;
|
|
907
|
+
case "stage_not_found":
|
|
908
|
+
return `Stage not found for run: ${runId}`;
|
|
909
|
+
default:
|
|
910
|
+
return `No active stages to ${action} for run: ${runId}`;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
function inFlightRunCount(): number {
|
|
915
|
+
return store.runs().filter((run) => run.endedAt === undefined).length;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
function reloadBlockedMessage(count = inFlightRunCount()): string {
|
|
919
|
+
return `Reload skipped: ${count} workflow run(s) still in flight. Wait for them to finish, or pause/kill them before reloading workflow resources.`;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function allStageConflictMessage(action: "pause" | "interrupt" | "kill"): string {
|
|
923
|
+
return `Cannot ${action} --all with a stageId; omit stageId or target a single run.`;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
class WorkflowReloadBlockedError extends Error {
|
|
927
|
+
constructor(message: string) {
|
|
928
|
+
super(message);
|
|
929
|
+
this.name = "WorkflowReloadBlockedError";
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
function reloadFailureMessage(error: unknown): string {
|
|
934
|
+
if (error instanceof WorkflowReloadBlockedError) return error.message;
|
|
935
|
+
return `Reload failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
936
|
+
}
|
|
937
|
+
|
|
552
938
|
// ---------------------------------------------------------------------------
|
|
553
939
|
// Tool execute — dispatch with real registry for list/inputs/run (Phase E)
|
|
554
940
|
// + real status/interrupt/resume (Phase D)
|
|
@@ -557,6 +943,7 @@ function workflowGetResult(
|
|
|
557
943
|
export function makeExecuteWorkflowTool(
|
|
558
944
|
runtime: ExtensionRuntime | ((ctx: PiExecuteContext) => ExtensionRuntime),
|
|
559
945
|
getPersistence: () => WorkflowPersistencePort | undefined,
|
|
946
|
+
reloadWorkflowResources: () => Promise<void> | void,
|
|
560
947
|
) {
|
|
561
948
|
return async function executeWorkflowTool(
|
|
562
949
|
args: WorkflowToolArgs,
|
|
@@ -614,15 +1001,322 @@ export function makeExecuteWorkflowTool(
|
|
|
614
1001
|
const snapshots = store.runs().filter((r) => r.endedAt === undefined);
|
|
615
1002
|
return {
|
|
616
1003
|
action: "status",
|
|
617
|
-
snapshots: snapshots.map(
|
|
618
|
-
|
|
619
|
-
|
|
1004
|
+
snapshots: snapshots.map((snapshot) => structuredClone(snapshot)),
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
case "stages": {
|
|
1009
|
+
const target = resolveToolRunTarget(args, "No active run to inspect.");
|
|
1010
|
+
const filter = args.statusFilter ?? "all";
|
|
1011
|
+
if (target.kind === "all") {
|
|
1012
|
+
return {
|
|
1013
|
+
action: "stages",
|
|
1014
|
+
runId: "--all",
|
|
1015
|
+
filter,
|
|
1016
|
+
stages: [],
|
|
1017
|
+
error: "Stage listing requires a single run.",
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
if (target.kind === "ambiguous") {
|
|
1021
|
+
return {
|
|
1022
|
+
action: "stages",
|
|
1023
|
+
runId: target.target,
|
|
1024
|
+
filter,
|
|
1025
|
+
stages: [],
|
|
1026
|
+
error: ambiguousRunMessage(target.target, target.matches),
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
if (target.kind === "not_found") {
|
|
1030
|
+
return {
|
|
1031
|
+
action: "stages",
|
|
1032
|
+
runId: target.target,
|
|
1033
|
+
filter,
|
|
1034
|
+
stages: [],
|
|
1035
|
+
error: target.message,
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
const run = store.runs().find((r) => r.id === target.runId);
|
|
1039
|
+
const stages = (run?.stages ?? [])
|
|
1040
|
+
.filter((stage) => filter === "all" || stage.status === filter)
|
|
1041
|
+
.map(summarizeStage);
|
|
1042
|
+
return { action: "stages", runId: target.runId, filter, stages };
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
case "stage": {
|
|
1046
|
+
const target = resolveToolRunTarget(args, "No active run to inspect.");
|
|
1047
|
+
if (target.kind === "all") {
|
|
1048
|
+
return {
|
|
1049
|
+
action: "stage",
|
|
1050
|
+
runId: "--all",
|
|
1051
|
+
error: "Stage inspection requires a single run.",
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
if (target.kind === "ambiguous") {
|
|
1055
|
+
return {
|
|
1056
|
+
action: "stage",
|
|
1057
|
+
runId: target.target,
|
|
1058
|
+
error: ambiguousRunMessage(target.target, target.matches),
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
if (target.kind === "not_found") {
|
|
1062
|
+
return {
|
|
1063
|
+
action: "stage",
|
|
1064
|
+
runId: target.target,
|
|
1065
|
+
error: target.message,
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
const stage = resolveToolStageTarget(target.runId, args.stageId);
|
|
1069
|
+
if (!stage.ok || stage.stageId === undefined) {
|
|
1070
|
+
return {
|
|
1071
|
+
action: "stage",
|
|
1072
|
+
runId: target.runId,
|
|
1073
|
+
error: stage.ok
|
|
1074
|
+
? "Stage id, prefix, or name is required."
|
|
1075
|
+
: stage.message,
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
const run = store.runs().find((r) => r.id === target.runId);
|
|
1079
|
+
const snapshot = run?.stages.find((s) => s.id === stage.stageId);
|
|
1080
|
+
return snapshot
|
|
1081
|
+
? { action: "stage", runId: target.runId, stage: cloneStage(snapshot) }
|
|
1082
|
+
: {
|
|
1083
|
+
action: "stage",
|
|
1084
|
+
runId: target.runId,
|
|
1085
|
+
error: `Stage not found in run ${target.runId.slice(0, 8)}: ${stage.stageId}`,
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
case "transcript": {
|
|
1090
|
+
const target = resolveToolRunTarget(args, "No active run to inspect.");
|
|
1091
|
+
if (target.kind === "all") {
|
|
1092
|
+
return {
|
|
1093
|
+
action: "transcript",
|
|
1094
|
+
runId: "--all",
|
|
1095
|
+
stageId: "",
|
|
1096
|
+
source: "error",
|
|
1097
|
+
entries: [],
|
|
1098
|
+
truncated: false,
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
if (target.kind === "ambiguous") {
|
|
1102
|
+
return {
|
|
1103
|
+
action: "transcript",
|
|
1104
|
+
runId: target.target,
|
|
1105
|
+
stageId: "",
|
|
1106
|
+
source: "error",
|
|
1107
|
+
entries: [
|
|
1108
|
+
{ role: "notice", text: ambiguousRunMessage(target.target, target.matches) },
|
|
1109
|
+
],
|
|
1110
|
+
truncated: false,
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
if (target.kind === "not_found") {
|
|
1114
|
+
return {
|
|
1115
|
+
action: "transcript",
|
|
1116
|
+
runId: target.target,
|
|
1117
|
+
stageId: "",
|
|
1118
|
+
source: "error",
|
|
1119
|
+
entries: [{ role: "notice", text: target.message }],
|
|
1120
|
+
truncated: false,
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
const stage = resolveToolStageTarget(target.runId, args.stageId);
|
|
1124
|
+
if (!stage.ok || stage.stageId === undefined) {
|
|
1125
|
+
return {
|
|
1126
|
+
action: "transcript",
|
|
1127
|
+
runId: target.runId,
|
|
1128
|
+
stageId: "",
|
|
1129
|
+
source: "error",
|
|
1130
|
+
entries: [
|
|
1131
|
+
{
|
|
1132
|
+
role: "notice",
|
|
1133
|
+
text: stage.ok
|
|
1134
|
+
? "Stage id, prefix, or name is required."
|
|
1135
|
+
: stage.message,
|
|
1136
|
+
},
|
|
1137
|
+
],
|
|
1138
|
+
truncated: false,
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
const run = store.runs().find((r) => r.id === target.runId);
|
|
1142
|
+
const snapshot = run?.stages.find((s) => s.id === stage.stageId);
|
|
1143
|
+
const liveHandle = stageControlRegistry.get(target.runId, stage.stageId);
|
|
1144
|
+
if (liveHandle !== undefined) {
|
|
1145
|
+
const limited = applyEntryLimit(
|
|
1146
|
+
liveHandle.messages.map((m) => transcriptEntryFromMessage(m as MessageLike)),
|
|
1147
|
+
args,
|
|
1148
|
+
);
|
|
1149
|
+
return {
|
|
1150
|
+
action: "transcript",
|
|
1151
|
+
runId: target.runId,
|
|
1152
|
+
stageId: stage.stageId,
|
|
1153
|
+
source: "live",
|
|
1154
|
+
entries: limited.entries,
|
|
1155
|
+
truncated: limited.truncated,
|
|
1156
|
+
sessionId: liveHandle.sessionId,
|
|
1157
|
+
sessionFile: liveHandle.sessionFile,
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
const fallback = snapshotTranscriptEntries(snapshot, args.includeToolOutput === true);
|
|
1161
|
+
const limited = applyEntryLimit(fallback, args);
|
|
1162
|
+
return {
|
|
1163
|
+
action: "transcript",
|
|
1164
|
+
runId: target.runId,
|
|
1165
|
+
stageId: stage.stageId,
|
|
1166
|
+
source: "snapshot",
|
|
1167
|
+
entries: limited.entries,
|
|
1168
|
+
truncated: limited.truncated,
|
|
1169
|
+
sessionId: snapshot?.sessionId,
|
|
1170
|
+
sessionFile: snapshot?.sessionFile,
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
case "send": {
|
|
1175
|
+
const target = resolveToolRunTarget(args, "No active run to message.");
|
|
1176
|
+
const requestedDelivery = args.delivery ?? "auto";
|
|
1177
|
+
if (target.kind === "all") {
|
|
1178
|
+
return workflowSendResult("--all", "", requestedDelivery, "noop", "Send requires a single run.");
|
|
1179
|
+
}
|
|
1180
|
+
if (target.kind === "ambiguous") {
|
|
1181
|
+
return workflowSendResult(target.target, "", requestedDelivery, "noop", ambiguousRunMessage(target.target, target.matches));
|
|
1182
|
+
}
|
|
1183
|
+
if (target.kind === "not_found") {
|
|
1184
|
+
return workflowSendResult(target.target, "", requestedDelivery, "noop", target.message);
|
|
1185
|
+
}
|
|
1186
|
+
const stage = resolveToolStageTarget(target.runId, args.stageId);
|
|
1187
|
+
if (!stage.ok || stage.stageId === undefined) {
|
|
1188
|
+
return workflowSendResult(
|
|
1189
|
+
target.runId,
|
|
1190
|
+
"",
|
|
1191
|
+
requestedDelivery,
|
|
1192
|
+
"noop",
|
|
1193
|
+
stage.ok ? "Stage id, prefix, or name is required." : stage.message,
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
const run = store.runs().find((r) => r.id === target.runId);
|
|
1197
|
+
const snapshot = run?.stages.find((s) => s.id === stage.stageId);
|
|
1198
|
+
const targetsPrompt =
|
|
1199
|
+
requestedDelivery === "answer" ||
|
|
1200
|
+
args.promptId !== undefined ||
|
|
1201
|
+
(requestedDelivery === "auto" && snapshot?.pendingPrompt !== undefined);
|
|
1202
|
+
if (targetsPrompt) {
|
|
1203
|
+
const promptId = args.promptId ?? snapshot?.pendingPrompt?.id;
|
|
1204
|
+
if (promptId === undefined) {
|
|
1205
|
+
return workflowSendResult(target.runId, stage.stageId, "answer", "noop", "No pending prompt to answer.");
|
|
1206
|
+
}
|
|
1207
|
+
if (!hasPayloadProperty(args)) {
|
|
1208
|
+
return workflowSendResult(target.runId, stage.stageId, "answer", "noop", "Send requires text, response, or message.");
|
|
1209
|
+
}
|
|
1210
|
+
const ok = store.resolveStagePendingPrompt(target.runId, stage.stageId, promptId, promptPayloadFromArgs(args));
|
|
1211
|
+
return workflowSendResult(
|
|
1212
|
+
target.runId,
|
|
1213
|
+
stage.stageId,
|
|
1214
|
+
"answer",
|
|
1215
|
+
ok ? "ok" : "noop",
|
|
1216
|
+
ok ? `Answered prompt ${promptId}.` : `No matching pending prompt ${promptId}.`,
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
const text = textPayloadFromArgs(args);
|
|
1220
|
+
if (text === undefined) {
|
|
1221
|
+
return workflowSendResult(target.runId, stage.stageId, requestedDelivery, "noop", "Send requires text, response, or message.");
|
|
1222
|
+
}
|
|
1223
|
+
const handle = stageControlRegistry.get(target.runId, stage.stageId);
|
|
1224
|
+
if (handle === undefined) {
|
|
1225
|
+
return workflowSendResult(target.runId, stage.stageId, requestedDelivery, "noop", "No live handle for stage.");
|
|
1226
|
+
}
|
|
1227
|
+
if (requestedDelivery === "resume" || (requestedDelivery === "auto" && handle.status === "paused")) {
|
|
1228
|
+
await handle.resume(text);
|
|
1229
|
+
return workflowSendResult(target.runId, stage.stageId, "resume", "ok", "Resumed stage with message.");
|
|
1230
|
+
}
|
|
1231
|
+
if (requestedDelivery === "steer" || (requestedDelivery === "auto" && handle.isStreaming)) {
|
|
1232
|
+
await handle.steer(text);
|
|
1233
|
+
return workflowSendResult(target.runId, stage.stageId, "steer", "ok", "Steered live stage.");
|
|
1234
|
+
}
|
|
1235
|
+
if (requestedDelivery === "prompt") {
|
|
1236
|
+
await handle.prompt(text);
|
|
1237
|
+
return workflowSendResult(target.runId, stage.stageId, "prompt", "ok", "Prompt sent to stage.");
|
|
1238
|
+
}
|
|
1239
|
+
await handle.followUp(text);
|
|
1240
|
+
return workflowSendResult(target.runId, stage.stageId, "followUp", "ok", "Follow-up queued for stage.");
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
case "pause": {
|
|
1244
|
+
const target = resolveToolRunTarget(args, "No in-flight runs to pause.");
|
|
1245
|
+
if (target.kind === "all") {
|
|
1246
|
+
if (args.stageId !== undefined && args.stageId.length > 0) {
|
|
1247
|
+
return {
|
|
1248
|
+
action,
|
|
1249
|
+
runId: "--all",
|
|
1250
|
+
status: "noop",
|
|
1251
|
+
message: allStageConflictMessage("pause"),
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
const results = pauseAllRuns();
|
|
1255
|
+
const paused = results.filter((r) => r.ok).length;
|
|
1256
|
+
return {
|
|
1257
|
+
action,
|
|
1258
|
+
runId: "--all",
|
|
1259
|
+
status: paused > 0 ? "paused" : "noop",
|
|
1260
|
+
message: paused > 0
|
|
1261
|
+
? `Paused ${paused} run(s).`
|
|
1262
|
+
: "No in-flight runs to pause.",
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
if (target.kind === "ambiguous") return { action, runId: target.target, status: "noop", message: ambiguousRunMessage(target.target, target.matches) };
|
|
1266
|
+
if (target.kind === "not_found") return { action, runId: target.target, status: "noop", message: target.message };
|
|
1267
|
+
const stage = resolveToolStageTarget(target.runId, args.stageId);
|
|
1268
|
+
if (!stage.ok) return { action, runId: target.runId, status: "noop", message: stage.message };
|
|
1269
|
+
const result = pauseRun(target.runId, { stageId: stage.stageId });
|
|
1270
|
+
return result.ok
|
|
1271
|
+
? { action, runId: result.runId, status: "paused", message: `Paused ${result.paused.length} stage(s) on run ${result.runId.slice(0, 8)}.` }
|
|
1272
|
+
: {
|
|
1273
|
+
action,
|
|
1274
|
+
runId: target.runId,
|
|
1275
|
+
status: "noop",
|
|
1276
|
+
message: stageFailureMessage(target.runId, result.reason, "pause"),
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
case "reload": {
|
|
1281
|
+
// Fast UX check; reloadWorkflowResourcesNow re-checks inside the
|
|
1282
|
+
// serialized reload queue and remains the authoritative TOCTOU guard.
|
|
1283
|
+
const activeRuns = inFlightRunCount();
|
|
1284
|
+
if (activeRuns > 0) {
|
|
1285
|
+
return {
|
|
1286
|
+
action: "reload",
|
|
1287
|
+
status: "noop",
|
|
1288
|
+
message: reloadBlockedMessage(activeRuns),
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
try {
|
|
1292
|
+
await reloadWorkflowResources();
|
|
1293
|
+
} catch (error) {
|
|
1294
|
+
return {
|
|
1295
|
+
action: "reload",
|
|
1296
|
+
status: "noop",
|
|
1297
|
+
message: reloadFailureMessage(error),
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
return {
|
|
1301
|
+
action: "reload",
|
|
1302
|
+
status: "ok",
|
|
1303
|
+
message: args.reason?.trim()
|
|
1304
|
+
? `Reloaded workflow resources (${args.reason.trim()}).`
|
|
1305
|
+
: "Reloaded workflow resources.",
|
|
620
1306
|
};
|
|
621
1307
|
}
|
|
622
1308
|
|
|
623
1309
|
case "kill": {
|
|
624
1310
|
const target = resolveToolRunTarget(args, "No in-flight runs to kill.");
|
|
625
1311
|
if (target.kind === "all") {
|
|
1312
|
+
if (args.stageId !== undefined && args.stageId.length > 0) {
|
|
1313
|
+
return {
|
|
1314
|
+
action,
|
|
1315
|
+
runId: "--all",
|
|
1316
|
+
status: "noop",
|
|
1317
|
+
message: allStageConflictMessage("kill"),
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
626
1320
|
const results = destroyAllRuns({
|
|
627
1321
|
cancellation: cancellationRegistry,
|
|
628
1322
|
persistence: getPersistence(),
|
|
@@ -668,6 +1362,14 @@ export function makeExecuteWorkflowTool(
|
|
|
668
1362
|
// Interrupt is resumable: it pauses live work and keeps runs in history/status.
|
|
669
1363
|
const target = resolveToolRunTarget(args, "No in-flight runs to interrupt.");
|
|
670
1364
|
if (target.kind === "all") {
|
|
1365
|
+
if (args.stageId !== undefined && args.stageId.length > 0) {
|
|
1366
|
+
return {
|
|
1367
|
+
action,
|
|
1368
|
+
runId: "--all",
|
|
1369
|
+
status: "noop",
|
|
1370
|
+
message: allStageConflictMessage("interrupt"),
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
671
1373
|
const results = interruptAllRuns();
|
|
672
1374
|
const interrupted = results.filter((r) => r.ok).length;
|
|
673
1375
|
return {
|
|
@@ -686,27 +1388,26 @@ export function makeExecuteWorkflowTool(
|
|
|
686
1388
|
if (target.kind === "not_found") {
|
|
687
1389
|
return { action, runId: target.target, status: "noop", message: target.message };
|
|
688
1390
|
}
|
|
689
|
-
const
|
|
1391
|
+
const stage = resolveToolStageTarget(target.runId, args.stageId);
|
|
1392
|
+
if (!stage.ok) {
|
|
1393
|
+
return { action, runId: target.runId, status: "noop", message: stage.message };
|
|
1394
|
+
}
|
|
1395
|
+
const result = interruptRun(target.runId, { stageId: stage.stageId });
|
|
690
1396
|
if (result.ok) {
|
|
691
1397
|
return {
|
|
692
1398
|
action,
|
|
693
1399
|
runId: result.runId,
|
|
694
1400
|
status: "paused",
|
|
695
|
-
message:
|
|
1401
|
+
message: stage.stageId
|
|
1402
|
+
? `Stage ${stage.stageId} interrupted on run ${result.runId} and can be resumed.`
|
|
1403
|
+
: `Run ${result.runId} interrupted and can be resumed.`,
|
|
696
1404
|
};
|
|
697
1405
|
}
|
|
698
1406
|
return {
|
|
699
1407
|
action,
|
|
700
1408
|
runId: target.runId,
|
|
701
1409
|
status: "noop",
|
|
702
|
-
message:
|
|
703
|
-
result.reason === "not_found"
|
|
704
|
-
? `Run not found: ${target.runId}`
|
|
705
|
-
: result.reason === "already_ended"
|
|
706
|
-
? `Run already ended: ${target.runId}`
|
|
707
|
-
: result.reason === "stage_not_found"
|
|
708
|
-
? `Stage not found for run: ${target.runId}`
|
|
709
|
-
: `No active stages to interrupt for run: ${target.runId}`,
|
|
1410
|
+
message: stageFailureMessage(target.runId, result.reason, "interrupt"),
|
|
710
1411
|
};
|
|
711
1412
|
}
|
|
712
1413
|
|
|
@@ -729,13 +1430,22 @@ export function makeExecuteWorkflowTool(
|
|
|
729
1430
|
const isPaused =
|
|
730
1431
|
run?.status === "paused" ||
|
|
731
1432
|
(run?.stages.some((s) => s.status === "paused") ?? false);
|
|
1433
|
+
if (!isPaused && run?.status === "failed" && run.endedAt !== undefined && run.resumable !== false) {
|
|
1434
|
+
const continuation = activeRuntime.resumeFailedRun(target.runId, stage.stageId);
|
|
1435
|
+
return {
|
|
1436
|
+
action: "resume",
|
|
1437
|
+
runId: continuation.ok ? continuation.runId : target.runId,
|
|
1438
|
+
status: continuation.ok ? "running" : "noop",
|
|
1439
|
+
message: continuation.message,
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
732
1442
|
const result = resumeRun(target.runId, { stageId: stage.stageId, message: args.message });
|
|
733
1443
|
if (result.ok) {
|
|
734
|
-
const message = isPaused
|
|
1444
|
+
const message = result.message ?? (isPaused
|
|
735
1445
|
? result.resumed.length === 0
|
|
736
1446
|
? `No paused stages on run ${result.runId.slice(0, 8)}.`
|
|
737
1447
|
: `Resumed ${result.resumed.length} stage(s) on run ${result.runId.slice(0, 8)}${args.message ? ` with message: "${args.message}"` : ""}.`
|
|
738
|
-
: `Snapshot available: run ${result.runId} (${result.snapshot.name}) — status: ${result.snapshot.status}, stages: ${result.snapshot.stages.length}
|
|
1448
|
+
: `Snapshot available: run ${result.runId} (${result.snapshot.name}) — status: ${result.snapshot.status}, stages: ${result.snapshot.stages.length}`);
|
|
739
1449
|
return {
|
|
740
1450
|
action: "resume",
|
|
741
1451
|
runId: result.runId,
|
|
@@ -946,16 +1656,34 @@ type ToolStageTarget =
|
|
|
946
1656
|
| { ok: true; stageId?: string }
|
|
947
1657
|
| { ok: false; message: string };
|
|
948
1658
|
|
|
949
|
-
function
|
|
1659
|
+
function stageMatchesIdentifier(stage: { readonly id: string; readonly name: string }, target: string): boolean {
|
|
1660
|
+
return stage.id === target || stage.name === target || stage.id.startsWith(target);
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
function stageMatchLabel(stage: { readonly id: string; readonly name: string }): string {
|
|
1664
|
+
return `${stage.name} (${stage.id.slice(0, 12)})`;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
function resolveStageTarget(runId: string, stageTarget?: string): ToolStageTarget {
|
|
950
1668
|
const target = stageTarget?.trim();
|
|
951
1669
|
if (!target) return { ok: true };
|
|
952
1670
|
|
|
953
1671
|
const run = store.runs().find((r) => r.id === runId);
|
|
954
|
-
const
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
return { ok: true, stageId:
|
|
1672
|
+
const exactId = run?.stages.find((stage) => stage.id === target);
|
|
1673
|
+
if (exactId !== undefined) return { ok: true, stageId: exactId.id };
|
|
1674
|
+
|
|
1675
|
+
const exactNames = run?.stages.filter((stage) => stage.name === target) ?? [];
|
|
1676
|
+
if (exactNames.length === 1) return { ok: true, stageId: exactNames[0]!.id };
|
|
1677
|
+
if (exactNames.length > 1) return { ok: false, message: `Ambiguous stage identifier "${target}" matches: ${exactNames.map(stageMatchLabel).join(", ")}` };
|
|
1678
|
+
|
|
1679
|
+
const matches = run?.stages.filter((stage) => stageMatchesIdentifier(stage, target)) ?? [];
|
|
1680
|
+
if (matches.length === 0) return { ok: false, message: `Stage not found in run ${runId.slice(0, 8)}: ${target}` };
|
|
1681
|
+
if (matches.length > 1) return { ok: false, message: `Ambiguous stage identifier "${target}" matches: ${matches.map(stageMatchLabel).join(", ")}` };
|
|
1682
|
+
return { ok: true, stageId: matches[0]!.id };
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
function resolveToolStageTarget(runId: string, stageTarget?: string): ToolStageTarget {
|
|
1686
|
+
return resolveStageTarget(runId, stageTarget);
|
|
959
1687
|
}
|
|
960
1688
|
|
|
961
1689
|
function ambiguousRunMessage(target: string, matches: readonly string[]): string {
|
|
@@ -1255,6 +1983,9 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1255
1983
|
runDirect(args) {
|
|
1256
1984
|
return runtimeRef.current.runDirect(args);
|
|
1257
1985
|
},
|
|
1986
|
+
resumeFailedRun(sourceRunId, stageId) {
|
|
1987
|
+
return runtimeRef.current.resumeFailedRun(sourceRunId, stageId);
|
|
1988
|
+
},
|
|
1258
1989
|
};
|
|
1259
1990
|
|
|
1260
1991
|
function modelFullId(model: PiRuntimeModel): string {
|
|
@@ -1314,10 +2045,84 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1314
2045
|
}
|
|
1315
2046
|
|
|
1316
2047
|
let intercomControlUnsubscribe: (() => void) | null = null;
|
|
2048
|
+
let workflowReloadQueue: Promise<void> = Promise.resolve();
|
|
2049
|
+
|
|
2050
|
+
async function reloadWorkflowResources(options?: { allowInFlight?: boolean }): Promise<void> {
|
|
2051
|
+
const reload = workflowReloadQueue.then(() => reloadWorkflowResourcesNow(options));
|
|
2052
|
+
workflowReloadQueue = reload.catch(() => {});
|
|
2053
|
+
await reload;
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
async function reloadWorkflowResourcesNow(options?: { allowInFlight?: boolean }): Promise<void> {
|
|
2057
|
+
const activeRuns = inFlightRunCount();
|
|
2058
|
+
if (options?.allowInFlight !== true) {
|
|
2059
|
+
if (activeRuns > 0) {
|
|
2060
|
+
throw new WorkflowReloadBlockedError(reloadBlockedMessage(activeRuns));
|
|
2061
|
+
}
|
|
2062
|
+
} else if (activeRuns > 0 && process.env.ATOMIC_WORKFLOW_DEBUG === "1") {
|
|
2063
|
+
console.warn(
|
|
2064
|
+
`Workflow reload bypassed in-flight guard with ${activeRuns} active run(s).`,
|
|
2065
|
+
);
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
const configResult = await loadWorkflowConfig();
|
|
2069
|
+
configLoadRef.current = configResult;
|
|
2070
|
+
|
|
2071
|
+
// Build scope-aware DiscoveryConfig: global entries → globalWorkflows (resolved
|
|
2072
|
+
// under <homeDir>/.atomic/agent), project entries → projectWorkflows (resolved under
|
|
2073
|
+
// projectRoot). Project keys override global keys. Paths pre-resolved to absolute.
|
|
2074
|
+
const { homedir } = await import("node:os");
|
|
2075
|
+
const hasGlobal = configResult.globalConfig != null;
|
|
2076
|
+
const hasProject = configResult.projectConfig != null;
|
|
2077
|
+
const discoveryConfig =
|
|
2078
|
+
hasGlobal || hasProject
|
|
2079
|
+
? toScopedDiscoveryConfig(
|
|
2080
|
+
configResult.globalConfig ?? null,
|
|
2081
|
+
configResult.projectConfig ?? null,
|
|
2082
|
+
{ projectRoot: process.cwd(), homeDir: homedir() },
|
|
2083
|
+
)
|
|
2084
|
+
: undefined;
|
|
2085
|
+
|
|
2086
|
+
const packageWorkflowPaths = (pi.getWorkflowResources?.() ?? [])
|
|
2087
|
+
.filter((resource) => resource.enabled !== false)
|
|
2088
|
+
.map((resource) => resource.path);
|
|
2089
|
+
const result = await discoverWorkflows({ config: discoveryConfig, packageWorkflowPaths });
|
|
2090
|
+
discoveryRef.current = result;
|
|
2091
|
+
|
|
2092
|
+
// Resolve effective config (fills in all defaults) and build WorkflowRuntimeConfig.
|
|
2093
|
+
const effectiveConfig = withWorkflowDefaults(configResult.config ?? {});
|
|
2094
|
+
runtimeConfigRef.current = {
|
|
2095
|
+
maxDepth: effectiveConfig.maxDepth,
|
|
2096
|
+
defaultConcurrency: effectiveConfig.defaultConcurrency,
|
|
2097
|
+
persistRuns: effectiveConfig.persistRuns,
|
|
2098
|
+
statusFile: effectiveConfig.statusFile,
|
|
2099
|
+
resumeInFlight: effectiveConfig.resumeInFlight,
|
|
2100
|
+
};
|
|
2101
|
+
|
|
2102
|
+
// Replace status writer with one that reflects the resolved config.
|
|
2103
|
+
// Unsubscribe the prior (no-op) writer before creating the new one.
|
|
2104
|
+
statusWriterRef.unsubscribe();
|
|
2105
|
+
statusWriterRef = createStatusWriter(store, runtimeConfigRef.current);
|
|
2106
|
+
|
|
2107
|
+
persistenceRef.current = makePersistencePort(
|
|
2108
|
+
pi,
|
|
2109
|
+
effectiveConfig.persistRuns,
|
|
2110
|
+
);
|
|
2111
|
+
runtimeRef.current = createExtensionRuntime({
|
|
2112
|
+
registry: result.registry,
|
|
2113
|
+
adapters,
|
|
2114
|
+
cancellation: cancellationRegistry,
|
|
2115
|
+
persistence: persistenceRef.current,
|
|
2116
|
+
mcp: mcpPort,
|
|
2117
|
+
intercom: intercomPort,
|
|
2118
|
+
config: runtimeConfigRef.current,
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
1317
2121
|
|
|
1318
2122
|
const executeWorkflowTool = makeExecuteWorkflowTool(
|
|
1319
2123
|
(ctx) => runtimeForContext(ctx),
|
|
1320
2124
|
() => persistenceRef.current,
|
|
2125
|
+
reloadWorkflowResources,
|
|
1321
2126
|
);
|
|
1322
2127
|
let storeWidgetUnsubscribe: (() => void) | null = null;
|
|
1323
2128
|
|
|
@@ -1327,59 +2132,7 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1327
2132
|
// Load startup config before discovery so workflow paths and tunables are applied.
|
|
1328
2133
|
const discoveryPromise = pi.disableAsyncDiscovery
|
|
1329
2134
|
? Promise.resolve()
|
|
1330
|
-
:
|
|
1331
|
-
configLoadRef.current = configResult;
|
|
1332
|
-
|
|
1333
|
-
// Build scope-aware DiscoveryConfig: global entries → globalWorkflows (resolved
|
|
1334
|
-
// under <homeDir>/.atomic/agent), project entries → projectWorkflows (resolved under
|
|
1335
|
-
// projectRoot). Project keys override global keys. Paths pre-resolved to absolute.
|
|
1336
|
-
const { homedir } = await import("node:os");
|
|
1337
|
-
const hasGlobal = configResult.globalConfig != null;
|
|
1338
|
-
const hasProject = configResult.projectConfig != null;
|
|
1339
|
-
const discoveryConfig =
|
|
1340
|
-
hasGlobal || hasProject
|
|
1341
|
-
? toScopedDiscoveryConfig(
|
|
1342
|
-
configResult.globalConfig ?? null,
|
|
1343
|
-
configResult.projectConfig ?? null,
|
|
1344
|
-
{ projectRoot: process.cwd(), homeDir: homedir() },
|
|
1345
|
-
)
|
|
1346
|
-
: undefined;
|
|
1347
|
-
|
|
1348
|
-
const packageWorkflowPaths = (pi.getWorkflowResources?.() ?? [])
|
|
1349
|
-
.filter((resource) => resource.enabled !== false)
|
|
1350
|
-
.map((resource) => resource.path);
|
|
1351
|
-
const result = await discoverWorkflows({ config: discoveryConfig, packageWorkflowPaths });
|
|
1352
|
-
discoveryRef.current = result;
|
|
1353
|
-
|
|
1354
|
-
// Resolve effective config (fills in all defaults) and build WorkflowRuntimeConfig.
|
|
1355
|
-
const effectiveConfig = withWorkflowDefaults(configResult.config ?? {});
|
|
1356
|
-
runtimeConfigRef.current = {
|
|
1357
|
-
maxDepth: effectiveConfig.maxDepth,
|
|
1358
|
-
defaultConcurrency: effectiveConfig.defaultConcurrency,
|
|
1359
|
-
persistRuns: effectiveConfig.persistRuns,
|
|
1360
|
-
statusFile: effectiveConfig.statusFile,
|
|
1361
|
-
resumeInFlight: effectiveConfig.resumeInFlight,
|
|
1362
|
-
};
|
|
1363
|
-
|
|
1364
|
-
// Replace status writer with one that reflects the resolved config.
|
|
1365
|
-
// Unsubscribe the prior (no-op) writer before creating the new one.
|
|
1366
|
-
statusWriterRef.unsubscribe();
|
|
1367
|
-
statusWriterRef = createStatusWriter(store, runtimeConfigRef.current);
|
|
1368
|
-
|
|
1369
|
-
persistenceRef.current = makePersistencePort(
|
|
1370
|
-
pi,
|
|
1371
|
-
effectiveConfig.persistRuns,
|
|
1372
|
-
);
|
|
1373
|
-
runtimeRef.current = createExtensionRuntime({
|
|
1374
|
-
registry: result.registry,
|
|
1375
|
-
adapters,
|
|
1376
|
-
cancellation: cancellationRegistry,
|
|
1377
|
-
persistence: persistenceRef.current,
|
|
1378
|
-
mcp: mcpPort,
|
|
1379
|
-
intercom: intercomPort,
|
|
1380
|
-
config: runtimeConfigRef.current,
|
|
1381
|
-
});
|
|
1382
|
-
});
|
|
2135
|
+
: reloadWorkflowResources({ allowInFlight: true });
|
|
1383
2136
|
|
|
1384
2137
|
// -------------------------------------------------------------------------
|
|
1385
2138
|
// 1. Register the `workflow` tool
|
|
@@ -1395,12 +2148,13 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1395
2148
|
label: "workflow",
|
|
1396
2149
|
description: "Run a defined multi-stage workflow by name.",
|
|
1397
2150
|
parameters: workflowParameters,
|
|
2151
|
+
renderShell: "self",
|
|
1398
2152
|
execute: async (_toolCallId, params, _signal, _onUpdate, ctx) => {
|
|
1399
2153
|
// Overlay is opt-in via F2 / ctrl+h; do not auto-open from a
|
|
1400
2154
|
// tool-call dispatch path.
|
|
1401
2155
|
const details = await executeWorkflowTool(params, ctx);
|
|
1402
2156
|
return {
|
|
1403
|
-
content: [{ type: "text", text:
|
|
2157
|
+
content: [{ type: "text", text: renderWorkflowToolContent(details, params) }],
|
|
1404
2158
|
details,
|
|
1405
2159
|
};
|
|
1406
2160
|
},
|
|
@@ -1850,22 +2604,20 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1850
2604
|
}
|
|
1851
2605
|
let stageId: string | undefined;
|
|
1852
2606
|
const run = store.runs().find((r) => r.id === runId);
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
s.id.startsWith(stageTarget) ||
|
|
1858
|
-
s.name === stageTarget,
|
|
1859
|
-
);
|
|
1860
|
-
if (!stage) {
|
|
1861
|
-
print(`Stage not found in run ${runId.slice(0, 8)}: ${stageTarget}`);
|
|
1862
|
-
return true;
|
|
1863
|
-
}
|
|
1864
|
-
stageId = stage.id;
|
|
2607
|
+
const resolvedStage = resolveStageTarget(runId, stageTarget);
|
|
2608
|
+
if (!resolvedStage.ok) {
|
|
2609
|
+
print(resolvedStage.message);
|
|
2610
|
+
return true;
|
|
1865
2611
|
}
|
|
2612
|
+
stageId = resolvedStage.stageId;
|
|
1866
2613
|
const isPaused =
|
|
1867
2614
|
run?.status === "paused" ||
|
|
1868
2615
|
(run?.stages.some((s) => s.status === "paused") ?? false);
|
|
2616
|
+
if (!isPaused && run?.status === "failed" && run.endedAt !== undefined && run.resumable !== false) {
|
|
2617
|
+
const continuation = runtimeForContext(ctx).resumeFailedRun(runId, stageId);
|
|
2618
|
+
print(continuation.message);
|
|
2619
|
+
return true;
|
|
2620
|
+
}
|
|
1869
2621
|
const result = resumeRun(runId, { stageId, message });
|
|
1870
2622
|
if (!result.ok) {
|
|
1871
2623
|
print(`Run not found: ${runId.slice(0, 8)}`);
|
|
@@ -1875,7 +2627,7 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1875
2627
|
// Non-paused fallback: reopen the orchestrator overlay as before.
|
|
1876
2628
|
overlay.open(result.runId, overlaySurfaceFromContext(ctx));
|
|
1877
2629
|
print(
|
|
1878
|
-
`Snapshot available: run ${result.runId} (${result.snapshot.name}) \u2014 status: ${result.snapshot.status}, stages: ${result.snapshot.stages.length}`,
|
|
2630
|
+
result.message ?? `Snapshot available: run ${result.runId} (${result.snapshot.name}) \u2014 status: ${result.snapshot.status}, stages: ${result.snapshot.stages.length}`,
|
|
1879
2631
|
);
|
|
1880
2632
|
return true;
|
|
1881
2633
|
}
|
|
@@ -1901,7 +2653,7 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1901
2653
|
"workflow",
|
|
1902
2654
|
{
|
|
1903
2655
|
description:
|
|
1904
|
-
"Run or inspect pi workflows. Usage: /workflow <name> [key=value…] | /workflow [list|status|connect|attach|interrupt|kill|pause|resume|inputs] [args]",
|
|
2656
|
+
"Run or inspect pi workflows. Usage: /workflow <name> [key=value…] | /workflow [list|status|connect|attach|interrupt|kill|pause|resume|inputs|reload] [args]",
|
|
1905
2657
|
handler: async (args: string, ctx: PiCommandContext) => {
|
|
1906
2658
|
const print = (msg: string): void => ctx.ui.notify(msg, "info");
|
|
1907
2659
|
// Quote-aware split so `prompt="map the codebase"` stays a single
|
|
@@ -1989,6 +2741,26 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1989
2741
|
return;
|
|
1990
2742
|
}
|
|
1991
2743
|
|
|
2744
|
+
// -----------------------------------------------------------------------
|
|
2745
|
+
// reload — refresh workflow resources in-process when no workflows are
|
|
2746
|
+
// currently running. Reload swaps runtime/persistence wiring, so doing it
|
|
2747
|
+
// mid-flight would split active runs across old and new resources.
|
|
2748
|
+
// -----------------------------------------------------------------------
|
|
2749
|
+
if (subcommand === "reload") {
|
|
2750
|
+
const activeRuns = inFlightRunCount();
|
|
2751
|
+
if (activeRuns > 0) {
|
|
2752
|
+
print(reloadBlockedMessage(activeRuns));
|
|
2753
|
+
return;
|
|
2754
|
+
}
|
|
2755
|
+
try {
|
|
2756
|
+
await reloadWorkflowResources();
|
|
2757
|
+
print("Reloaded workflow resources.");
|
|
2758
|
+
} catch (error) {
|
|
2759
|
+
print(reloadFailureMessage(error));
|
|
2760
|
+
}
|
|
2761
|
+
return;
|
|
2762
|
+
}
|
|
2763
|
+
|
|
1992
2764
|
// -----------------------------------------------------------------------
|
|
1993
2765
|
// interrupt — top-level chat fast path (no confirmation overlay).
|
|
1994
2766
|
// -----------------------------------------------------------------------
|
|
@@ -2180,7 +2952,6 @@ function factory(pi: ExtensionAPI): void {
|
|
|
2180
2952
|
});
|
|
2181
2953
|
}
|
|
2182
2954
|
if (pickerResult.kind === "cancel") {
|
|
2183
|
-
print(`Cancelled. /workflow ${workflowName} not started.`);
|
|
2184
2955
|
return;
|
|
2185
2956
|
}
|
|
2186
2957
|
if (pickerResult.kind === "run") {
|
|
@@ -2315,6 +3086,11 @@ function factory(pi: ExtensionAPI): void {
|
|
|
2315
3086
|
label: "inputs",
|
|
2316
3087
|
description: "Show a workflow's input schema",
|
|
2317
3088
|
},
|
|
3089
|
+
{
|
|
3090
|
+
value: "reload ",
|
|
3091
|
+
label: "reload",
|
|
3092
|
+
description: "Reload workflow resources",
|
|
3093
|
+
},
|
|
2318
3094
|
];
|
|
2319
3095
|
|
|
2320
3096
|
const parts = partial.trim().split(/\s+/).filter(Boolean);
|