@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
|
@@ -33,7 +33,14 @@ import type {
|
|
|
33
33
|
WorkflowModelCatalogPort,
|
|
34
34
|
} from "../../shared/types.js";
|
|
35
35
|
import type { InternalStageContext, StageAdapters } from "./stage-runner.js";
|
|
36
|
-
import type {
|
|
36
|
+
import type {
|
|
37
|
+
RunStatus,
|
|
38
|
+
StageNotice,
|
|
39
|
+
StageSnapshot,
|
|
40
|
+
RunSnapshot,
|
|
41
|
+
WorkflowOverlayAdapter,
|
|
42
|
+
WorkflowFailureKind,
|
|
43
|
+
} from "../../shared/store-types.js";
|
|
37
44
|
import type { StageControlHandle, StageControlRegistry, AgentSessionEventListener } from "./stage-control-registry.js";
|
|
38
45
|
import type { Store } from "../../shared/store.js";
|
|
39
46
|
import type { CancellationRegistry } from "../background/cancellation-registry.js";
|
|
@@ -59,9 +66,16 @@ import {
|
|
|
59
66
|
appendRunEnd,
|
|
60
67
|
} from "../../shared/persistence-session-entries.js";
|
|
61
68
|
import { validateWorkflowModels } from "../shared/model-fallback.js";
|
|
69
|
+
import type { WorkflowFailure } from "../../shared/workflow-failures.js";
|
|
70
|
+
import { classifyWorkflowFailure } from "../../shared/workflow-failures.js";
|
|
62
71
|
|
|
63
72
|
export interface ResolvedInputs extends Record<string, unknown> {}
|
|
64
73
|
|
|
74
|
+
export interface RunContinuationOpts {
|
|
75
|
+
readonly source: RunSnapshot;
|
|
76
|
+
readonly resumeFromStageId: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
65
79
|
export interface RunOpts {
|
|
66
80
|
adapters?: StageAdapters;
|
|
67
81
|
/** HIL adapter injected by the pi runtime or test harness. */
|
|
@@ -113,6 +127,8 @@ export interface RunOpts {
|
|
|
113
127
|
* the runId before starting the background promise.
|
|
114
128
|
*/
|
|
115
129
|
runId?: string;
|
|
130
|
+
/** Replay completed stages from a failed source run, then resume at this stage. */
|
|
131
|
+
continuation?: RunContinuationOpts;
|
|
116
132
|
onRunStart?: (snapshot: RunSnapshot) => void;
|
|
117
133
|
onStageStart?: (runId: string, snapshot: StageSnapshot) => void;
|
|
118
134
|
onStageEnd?: (runId: string, snapshot: StageSnapshot) => void;
|
|
@@ -519,16 +535,22 @@ async function mapParallelSteps<T>(
|
|
|
519
535
|
concurrency: number | undefined,
|
|
520
536
|
failFast: boolean | undefined,
|
|
521
537
|
mapper: (step: WorkflowTaskStep) => Promise<T>,
|
|
538
|
+
onFirstFailure?: (error: unknown) => void,
|
|
522
539
|
): Promise<T[]> {
|
|
523
540
|
const limit = positiveConcurrency(concurrency) ?? steps.length;
|
|
541
|
+
const failFastEnabled = failFast !== false;
|
|
524
542
|
const results = new Array<T>(steps.length);
|
|
525
543
|
const failures: Array<{ readonly index: number; readonly error: unknown }> = [];
|
|
526
544
|
let nextIndex = 0;
|
|
527
545
|
let firstFailure: unknown;
|
|
546
|
+
let rejectFirstFailure: (reason: unknown) => void = () => {};
|
|
547
|
+
const firstFailurePromise = new Promise<never>((_, reject) => {
|
|
548
|
+
rejectFirstFailure = reject;
|
|
549
|
+
});
|
|
528
550
|
|
|
529
551
|
async function worker(): Promise<void> {
|
|
530
552
|
while (true) {
|
|
531
|
-
if (
|
|
553
|
+
if (failFastEnabled && firstFailure !== undefined) return;
|
|
532
554
|
const index = nextIndex;
|
|
533
555
|
nextIndex += 1;
|
|
534
556
|
const step = steps[index];
|
|
@@ -537,15 +559,29 @@ async function mapParallelSteps<T>(
|
|
|
537
559
|
results[index] = await mapper(step);
|
|
538
560
|
} catch (err) {
|
|
539
561
|
failures.push({ index, error: err });
|
|
540
|
-
firstFailure
|
|
541
|
-
|
|
562
|
+
if (firstFailure === undefined) {
|
|
563
|
+
firstFailure = err;
|
|
564
|
+
onFirstFailure?.(err);
|
|
565
|
+
if (failFastEnabled) rejectFirstFailure(err);
|
|
566
|
+
}
|
|
567
|
+
if (failFastEnabled) return;
|
|
542
568
|
}
|
|
543
569
|
}
|
|
544
570
|
}
|
|
545
571
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
572
|
+
const workers = Array.from({ length: Math.min(limit, steps.length) }, () => worker());
|
|
573
|
+
const allWorkers = Promise.all(workers);
|
|
574
|
+
|
|
575
|
+
if (!failFastEnabled) {
|
|
576
|
+
await allWorkers;
|
|
577
|
+
} else {
|
|
578
|
+
try {
|
|
579
|
+
await Promise.race([allWorkers, firstFailurePromise]);
|
|
580
|
+
} catch (err) {
|
|
581
|
+
void allWorkers.catch(() => {});
|
|
582
|
+
throw err;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
549
585
|
|
|
550
586
|
if (failures.length > 0) {
|
|
551
587
|
throw new AggregateError(
|
|
@@ -723,6 +759,10 @@ async function writeDirectOutput(
|
|
|
723
759
|
};
|
|
724
760
|
}
|
|
725
761
|
|
|
762
|
+
function directFailureMessage(error: unknown): string {
|
|
763
|
+
return classifyWorkflowFailure(error).userMessage;
|
|
764
|
+
}
|
|
765
|
+
|
|
726
766
|
function failedDirectDetails(
|
|
727
767
|
mode: WorkflowDetails["mode"],
|
|
728
768
|
runId: string,
|
|
@@ -738,7 +778,7 @@ function failedDirectDetails(
|
|
|
738
778
|
...(options.context !== undefined ? { context: options.context } : {}),
|
|
739
779
|
results: [],
|
|
740
780
|
progress: { completed: 0, total },
|
|
741
|
-
error:
|
|
781
|
+
error: directFailureMessage(error),
|
|
742
782
|
};
|
|
743
783
|
}
|
|
744
784
|
|
|
@@ -1013,6 +1053,11 @@ function appendRunEndWhenRecorded(
|
|
|
1013
1053
|
readonly runId: string;
|
|
1014
1054
|
readonly status: RunStatus;
|
|
1015
1055
|
readonly result?: Record<string, unknown>;
|
|
1056
|
+
readonly error?: string;
|
|
1057
|
+
readonly failureKind?: WorkflowFailureKind;
|
|
1058
|
+
readonly failureMessage?: string;
|
|
1059
|
+
readonly failedStageId?: string;
|
|
1060
|
+
readonly resumable?: boolean;
|
|
1016
1061
|
readonly ts: number;
|
|
1017
1062
|
},
|
|
1018
1063
|
): void {
|
|
@@ -1020,6 +1065,103 @@ function appendRunEndWhenRecorded(
|
|
|
1020
1065
|
appendRunEnd(persistence, payload);
|
|
1021
1066
|
}
|
|
1022
1067
|
|
|
1068
|
+
interface RunFailureMetadata {
|
|
1069
|
+
readonly errorMessage: string;
|
|
1070
|
+
readonly failureKind: WorkflowFailureKind;
|
|
1071
|
+
readonly failureMessage: string;
|
|
1072
|
+
readonly failedStageId?: string;
|
|
1073
|
+
readonly resumable: boolean;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
function applyFailureToStage(stage: StageSnapshot, failure: WorkflowFailure): void {
|
|
1077
|
+
stage.status = "failed";
|
|
1078
|
+
stage.error = failure.userMessage;
|
|
1079
|
+
stage.failureKind = failure.kind;
|
|
1080
|
+
stage.failureMessage = failure.message;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
function runFailureMetadata(err: unknown, stages: readonly StageSnapshot[]): RunFailureMetadata {
|
|
1084
|
+
const classified = classifyWorkflowFailure(err);
|
|
1085
|
+
const failedStage = stages.find((stage) => stage.status === "failed");
|
|
1086
|
+
const failureKind = failedStage?.failureKind ?? classified.kind;
|
|
1087
|
+
|
|
1088
|
+
return {
|
|
1089
|
+
errorMessage: classified.userMessage,
|
|
1090
|
+
failureKind,
|
|
1091
|
+
failureMessage: failedStage?.failureMessage ?? classified.message,
|
|
1092
|
+
...(failedStage !== undefined ? { failedStageId: failedStage.id } : {}),
|
|
1093
|
+
resumable: classified.resumable,
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
type ContinuationReplayDecision =
|
|
1098
|
+
| { readonly kind: "execute"; readonly source?: StageSnapshot }
|
|
1099
|
+
| { readonly kind: "replay"; readonly source: StageSnapshot };
|
|
1100
|
+
|
|
1101
|
+
interface ContinuationReplayIndex {
|
|
1102
|
+
decide(stageName: string, parentIds: readonly string[], stageId: string): ContinuationReplayDecision;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
function sameStringSet(left: readonly string[], right: readonly string[]): boolean {
|
|
1106
|
+
if (left.length !== right.length) return false;
|
|
1107
|
+
const rightSet = new Set(right);
|
|
1108
|
+
return left.every((value) => rightSet.has(value));
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
function createContinuationReplayIndex(continuation: RunContinuationOpts | undefined): ContinuationReplayIndex {
|
|
1112
|
+
if (continuation === undefined) return { decide: () => ({ kind: "execute" }) };
|
|
1113
|
+
const resumeStage = continuation.source.stages.find((stage) => stage.id === continuation.resumeFromStageId);
|
|
1114
|
+
if (resumeStage === undefined) {
|
|
1115
|
+
throw new Error(`pi-workflows: insufficient_state: resume stage ${continuation.resumeFromStageId} was not found in source run ${continuation.source.id}`);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
const stagesByName = new Map<string, StageSnapshot[]>();
|
|
1119
|
+
for (const stage of continuation.source.stages) {
|
|
1120
|
+
const stages = stagesByName.get(stage.name);
|
|
1121
|
+
if (stages === undefined) {
|
|
1122
|
+
stagesByName.set(stage.name, [stage]);
|
|
1123
|
+
} else {
|
|
1124
|
+
stages.push(stage);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
const consumedSourceStageIds = new Set<string>();
|
|
1129
|
+
const sourceStageIdByContinuationStageId = new Map<string, string>();
|
|
1130
|
+
return {
|
|
1131
|
+
decide(stageName: string, parentIds: readonly string[], stageId: string): ContinuationReplayDecision {
|
|
1132
|
+
const candidates = stagesByName.get(stageName)?.filter((stage) => !consumedSourceStageIds.has(stage.id)) ?? [];
|
|
1133
|
+
if (candidates.length === 0) return { kind: "execute" };
|
|
1134
|
+
|
|
1135
|
+
const translatedParentIds = parentIds.map((parentId) => sourceStageIdByContinuationStageId.get(parentId));
|
|
1136
|
+
const hasUnmappedParent = translatedParentIds.some((parentId) => parentId === undefined);
|
|
1137
|
+
const matches = hasUnmappedParent
|
|
1138
|
+
? []
|
|
1139
|
+
: candidates.filter((stage) => sameStringSet(translatedParentIds as string[], stage.parentIds));
|
|
1140
|
+
|
|
1141
|
+
if (matches.length !== 1) {
|
|
1142
|
+
const reason = matches.length === 0 ? "mismatch" : "ambiguous";
|
|
1143
|
+
throw new Error(`pi-workflows: insufficient_state: replay topology ${reason} for stage "${stageName}" in source run ${continuation.source.id}`);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
const source = matches[0]!;
|
|
1147
|
+
consumedSourceStageIds.add(source.id);
|
|
1148
|
+
sourceStageIdByContinuationStageId.set(stageId, source.id);
|
|
1149
|
+
if (source.status === "completed") return { kind: "replay", source };
|
|
1150
|
+
return { kind: "execute", source };
|
|
1151
|
+
},
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
interface ParallelFailFastStage {
|
|
1156
|
+
readonly skip: () => void;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
interface ParallelFailFastScope {
|
|
1160
|
+
failed: boolean;
|
|
1161
|
+
firstFailure?: unknown;
|
|
1162
|
+
readonly activeStages: Map<string, ParallelFailFastStage>;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1023
1165
|
// ---------------------------------------------------------------------------
|
|
1024
1166
|
// Shared killed finalizer — used for catch-abort and post-body abort check
|
|
1025
1167
|
// ---------------------------------------------------------------------------
|
|
@@ -1031,17 +1173,25 @@ function finalizeKilled(
|
|
|
1031
1173
|
persistence: WorkflowPersistencePort | undefined,
|
|
1032
1174
|
onRunEnd: RunOpts["onRunEnd"],
|
|
1033
1175
|
): RunResult {
|
|
1034
|
-
const
|
|
1035
|
-
|
|
1176
|
+
const errorMessage = "workflow killed";
|
|
1177
|
+
const metadata = {
|
|
1178
|
+
failureKind: "cancelled" as const,
|
|
1179
|
+
failureMessage: errorMessage,
|
|
1180
|
+
resumable: false,
|
|
1181
|
+
};
|
|
1182
|
+
const recorded = activeStore.recordRunEnd(runId, "killed", undefined, errorMessage, metadata);
|
|
1183
|
+
onRunEnd?.(runId, "killed", undefined, errorMessage);
|
|
1036
1184
|
appendRunEndWhenRecorded(persistence, recorded, {
|
|
1037
1185
|
runId,
|
|
1038
1186
|
status: "killed",
|
|
1187
|
+
error: errorMessage,
|
|
1188
|
+
...metadata,
|
|
1039
1189
|
ts: Date.now(),
|
|
1040
1190
|
});
|
|
1041
1191
|
return {
|
|
1042
1192
|
runId,
|
|
1043
1193
|
status: "killed",
|
|
1044
|
-
error:
|
|
1194
|
+
error: errorMessage,
|
|
1045
1195
|
stages: [...runSnapshot.stages],
|
|
1046
1196
|
};
|
|
1047
1197
|
}
|
|
@@ -1080,6 +1230,7 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1080
1230
|
|
|
1081
1231
|
// 2. Generate runId (or use pre-allocated seam from caller)
|
|
1082
1232
|
const runId = opts.runId ?? crypto.randomUUID();
|
|
1233
|
+
const replayIndex = createContinuationReplayIndex(opts.continuation);
|
|
1083
1234
|
|
|
1084
1235
|
// 2a. Create own AbortController; forward caller signal if provided
|
|
1085
1236
|
const ownController = new AbortController();
|
|
@@ -1100,6 +1251,10 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1100
1251
|
status: "running",
|
|
1101
1252
|
stages: [],
|
|
1102
1253
|
startedAt: Date.now(),
|
|
1254
|
+
...(opts.continuation !== undefined ? {
|
|
1255
|
+
resumedFromRunId: opts.continuation.source.id,
|
|
1256
|
+
resumeFromStageId: opts.continuation.resumeFromStageId,
|
|
1257
|
+
} : {}),
|
|
1103
1258
|
};
|
|
1104
1259
|
|
|
1105
1260
|
activeStore.recordRunStart(runSnapshot);
|
|
@@ -1120,6 +1275,8 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1120
1275
|
runId,
|
|
1121
1276
|
name: def.name,
|
|
1122
1277
|
inputs: resolvedInputs,
|
|
1278
|
+
...(runSnapshot.resumedFromRunId !== undefined ? { resumedFromRunId: runSnapshot.resumedFromRunId } : {}),
|
|
1279
|
+
...(runSnapshot.resumeFromStageId !== undefined ? { resumeFromStageId: runSnapshot.resumeFromStageId } : {}),
|
|
1123
1280
|
ts: runSnapshot.startedAt,
|
|
1124
1281
|
});
|
|
1125
1282
|
}
|
|
@@ -1147,7 +1304,7 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1147
1304
|
};
|
|
1148
1305
|
|
|
1149
1306
|
const isTerminalStage = (stage: StageSnapshot): boolean =>
|
|
1150
|
-
stage.status === "completed" || stage.status === "failed";
|
|
1307
|
+
stage.status === "completed" || stage.status === "failed" || stage.status === "skipped";
|
|
1151
1308
|
|
|
1152
1309
|
const stageById = (stageId: string): StageSnapshot | undefined =>
|
|
1153
1310
|
runSnapshot.stages.find((stage) => stage.id === stageId);
|
|
@@ -1280,7 +1437,7 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1280
1437
|
inputs: resolvedInputs as TInputs,
|
|
1281
1438
|
ui: opts.ui ?? makeUnavailableUIContext(),
|
|
1282
1439
|
|
|
1283
|
-
stage(name: string, options?: StageOptions) {
|
|
1440
|
+
stage(name: string, options?: StageOptions, stageFailFastScope?: ParallelFailFastScope) {
|
|
1284
1441
|
// a. Generate stageId
|
|
1285
1442
|
const stageId = crypto.randomUUID();
|
|
1286
1443
|
|
|
@@ -1288,12 +1445,24 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1288
1445
|
const parentIds = tracker.onSpawn(stageId, name);
|
|
1289
1446
|
|
|
1290
1447
|
// c. Create StageSnapshot as "pending"
|
|
1448
|
+
const replayDecision = replayIndex.decide(name, parentIds, stageId);
|
|
1449
|
+
const replaySource = replayDecision.kind === "replay" ? replayDecision.source : undefined;
|
|
1450
|
+
const shouldReplay = replaySource !== undefined;
|
|
1451
|
+
|
|
1291
1452
|
const stageSnapshot: StageSnapshot = {
|
|
1292
1453
|
id: stageId,
|
|
1293
1454
|
name,
|
|
1294
|
-
status: "pending",
|
|
1455
|
+
status: shouldReplay ? "completed" : "pending",
|
|
1295
1456
|
parentIds: Object.freeze(parentIds),
|
|
1296
1457
|
toolEvents: [],
|
|
1458
|
+
...(shouldReplay ? {
|
|
1459
|
+
startedAt: Date.now(),
|
|
1460
|
+
endedAt: Date.now(),
|
|
1461
|
+
durationMs: 0,
|
|
1462
|
+
...(replaySource.result !== undefined ? { result: replaySource.result } : {}),
|
|
1463
|
+
replayedFromStageId: replaySource.id,
|
|
1464
|
+
replayed: true,
|
|
1465
|
+
} : {}),
|
|
1297
1466
|
// Store mcp scope options on snapshot when provided
|
|
1298
1467
|
...(options?.mcp !== undefined
|
|
1299
1468
|
? { mcpScope: { allow: options.mcp.allow ?? null, deny: options.mcp.deny ?? null } }
|
|
@@ -1301,9 +1470,87 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1301
1470
|
// Mark attachable up-front: the live stage handle is registered
|
|
1302
1471
|
// below before the first onStageStart fires, so consumers that
|
|
1303
1472
|
// hook onStageStart see `attachable: true` for the pending stage.
|
|
1304
|
-
attachable:
|
|
1473
|
+
attachable: !shouldReplay,
|
|
1305
1474
|
};
|
|
1306
1475
|
|
|
1476
|
+
let stageStartEntryAppended = false;
|
|
1477
|
+
const appendStageStartOnce = (): void => {
|
|
1478
|
+
if (!opts.persistence || stageStartEntryAppended) return;
|
|
1479
|
+
stageStartEntryAppended = true;
|
|
1480
|
+
appendStageStart(opts.persistence, {
|
|
1481
|
+
runId,
|
|
1482
|
+
stageId,
|
|
1483
|
+
name,
|
|
1484
|
+
parentIds: stageSnapshot.parentIds,
|
|
1485
|
+
...(stageSnapshot.replayedFromStageId !== undefined ? { replayedFromStageId: stageSnapshot.replayedFromStageId } : {}),
|
|
1486
|
+
...(stageSnapshot.replayed !== undefined ? { replayed: stageSnapshot.replayed } : {}),
|
|
1487
|
+
ts: stageSnapshot.startedAt ?? Date.now(),
|
|
1488
|
+
});
|
|
1489
|
+
};
|
|
1490
|
+
|
|
1491
|
+
if (shouldReplay) {
|
|
1492
|
+
activeStore.recordStageStart(runId, stageSnapshot);
|
|
1493
|
+
opts.onStageStart?.(runId, stageSnapshot);
|
|
1494
|
+
appendStageStartOnce();
|
|
1495
|
+
let replayFinalized = false;
|
|
1496
|
+
const finalizeReplayStage = (): void => {
|
|
1497
|
+
if (replayFinalized) return;
|
|
1498
|
+
replayFinalized = true;
|
|
1499
|
+
activeStore.recordStageEnd(runId, stageSnapshot);
|
|
1500
|
+
opts.onStageEnd?.(runId, stageSnapshot);
|
|
1501
|
+
if (opts.persistence) {
|
|
1502
|
+
appendStageEnd(opts.persistence, {
|
|
1503
|
+
runId,
|
|
1504
|
+
stageId,
|
|
1505
|
+
status: "completed",
|
|
1506
|
+
durationMs: 0,
|
|
1507
|
+
...(stageSnapshot.result !== undefined ? { summary: stageSnapshot.result } : {}),
|
|
1508
|
+
replayedFromStageId: replaySource.id,
|
|
1509
|
+
replayed: true,
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
tracker.onSettle(stageId);
|
|
1513
|
+
};
|
|
1514
|
+
const replayResult = replaySource.result ?? "";
|
|
1515
|
+
const replayText = async (): Promise<string> => {
|
|
1516
|
+
await Promise.resolve();
|
|
1517
|
+
finalizeReplayStage();
|
|
1518
|
+
return replayResult;
|
|
1519
|
+
};
|
|
1520
|
+
const rejectReplayMutation = (action: string): never => {
|
|
1521
|
+
throw new Error(`pi-workflows: replayed stage "${name}" cannot ${action}`);
|
|
1522
|
+
};
|
|
1523
|
+
const replayContext: StageContext & Pick<InternalStageContext, "__modelFallbackMeta"> = {
|
|
1524
|
+
name,
|
|
1525
|
+
prompt: replayText,
|
|
1526
|
+
complete: replayText,
|
|
1527
|
+
steer: async () => rejectReplayMutation("steer"),
|
|
1528
|
+
followUp: async () => rejectReplayMutation("follow up"),
|
|
1529
|
+
subscribe: () => () => {},
|
|
1530
|
+
get sessionFile() { return replaySource.sessionFile; },
|
|
1531
|
+
get sessionId() { return replaySource.sessionId ?? ""; },
|
|
1532
|
+
setModel: async () => rejectReplayMutation("set model"),
|
|
1533
|
+
setThinkingLevel: () => rejectReplayMutation("set thinking level"),
|
|
1534
|
+
cycleModel: async () => rejectReplayMutation("cycle model"),
|
|
1535
|
+
cycleThinkingLevel: () => rejectReplayMutation("cycle thinking level"),
|
|
1536
|
+
get agent() { return undefined as never; },
|
|
1537
|
+
get model() { return replaySource.model as never; },
|
|
1538
|
+
get thinkingLevel() { return undefined as never; },
|
|
1539
|
+
get messages() { return [] as never; },
|
|
1540
|
+
get isStreaming() { return false; },
|
|
1541
|
+
navigateTree: async () => rejectReplayMutation("navigate conversation tree"),
|
|
1542
|
+
compact: async () => rejectReplayMutation("compact"),
|
|
1543
|
+
abortCompaction: () => rejectReplayMutation("abort compaction"),
|
|
1544
|
+
abort: async () => rejectReplayMutation("abort"),
|
|
1545
|
+
__modelFallbackMeta: () => ({
|
|
1546
|
+
...(replaySource.model !== undefined ? { model: replaySource.model } : {}),
|
|
1547
|
+
...(replaySource.attemptedModels !== undefined ? { attemptedModels: replaySource.attemptedModels } : {}),
|
|
1548
|
+
...(replaySource.modelAttempts !== undefined ? { modelAttempts: replaySource.modelAttempts } : {}),
|
|
1549
|
+
}),
|
|
1550
|
+
};
|
|
1551
|
+
return replayContext;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1307
1554
|
// d. Create inner AgentSession-like StageContext (raw, without lifecycle wrapping).
|
|
1308
1555
|
// Must come before the registry registration because the handle
|
|
1309
1556
|
// delegates to it for every operation.
|
|
@@ -1349,6 +1596,47 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1349
1596
|
unregisterStageHandle();
|
|
1350
1597
|
await disposeInnerContext();
|
|
1351
1598
|
};
|
|
1599
|
+
const hasQueuedLiveWork = (): boolean =>
|
|
1600
|
+
innerCtx.isStreaming || innerCtx.__pendingMessageCount() > 0 || activeAskUserQuestionCalls.size > 0;
|
|
1601
|
+
const releaseLiveHandleWhenIdle = async (): Promise<void> => {
|
|
1602
|
+
dropStageControlHandle();
|
|
1603
|
+
if (!hasQueuedLiveWork()) {
|
|
1604
|
+
await releaseLiveHandle();
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
// The queued-work branch installs asynchronous cleanup and returns once
|
|
1609
|
+
// the release watcher is armed. Inner-context events normally trigger
|
|
1610
|
+
// the subscription when streaming/pending-message counters change, but
|
|
1611
|
+
// SDK prompt/tool cleanup can also drain after the stage has stopped
|
|
1612
|
+
// emitting workflow-visible events. The unref'd 250 ms interval is a
|
|
1613
|
+
// fallback for that silent drain path and is cleared as soon as the
|
|
1614
|
+
// handle becomes idle.
|
|
1615
|
+
let unsubscribe = (): void => {};
|
|
1616
|
+
let pollTimer: ReturnType<typeof setInterval> | undefined;
|
|
1617
|
+
const cleanupWatcher = (): void => {
|
|
1618
|
+
unsubscribe();
|
|
1619
|
+
if (pollTimer !== undefined) {
|
|
1620
|
+
clearInterval(pollTimer);
|
|
1621
|
+
pollTimer = undefined;
|
|
1622
|
+
}
|
|
1623
|
+
};
|
|
1624
|
+
const releaseIfIdle = (): void => {
|
|
1625
|
+
if (liveHandleReleased) {
|
|
1626
|
+
cleanupWatcher();
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1629
|
+
if (hasQueuedLiveWork()) return;
|
|
1630
|
+
cleanupWatcher();
|
|
1631
|
+
void releaseLiveHandle().catch((error: unknown) => {
|
|
1632
|
+
console.debug("pi-workflows: failed to release idle stage handle", error);
|
|
1633
|
+
});
|
|
1634
|
+
};
|
|
1635
|
+
unsubscribe = innerCtx.subscribe(() => queueMicrotask(releaseIfIdle));
|
|
1636
|
+
pollTimer = setInterval(releaseIfIdle, 250);
|
|
1637
|
+
pollTimer.unref?.();
|
|
1638
|
+
releaseIfIdle();
|
|
1639
|
+
};
|
|
1352
1640
|
|
|
1353
1641
|
// e. Register a live stage-control handle so attached panes can
|
|
1354
1642
|
// prompt/steer/pause/resume the underlying Pi session lazily.
|
|
@@ -1372,6 +1660,9 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1372
1660
|
get isStreaming() {
|
|
1373
1661
|
return innerCtx.isStreaming;
|
|
1374
1662
|
},
|
|
1663
|
+
get isDisposed() {
|
|
1664
|
+
return liveHandleReleased;
|
|
1665
|
+
},
|
|
1375
1666
|
get messages() {
|
|
1376
1667
|
return innerCtx.messages;
|
|
1377
1668
|
},
|
|
@@ -1421,6 +1712,59 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1421
1712
|
return innerCtx.subscribe(listener);
|
|
1422
1713
|
},
|
|
1423
1714
|
};
|
|
1715
|
+
let stageFinalized = false;
|
|
1716
|
+
const finalizeStageSnapshot = (): boolean => {
|
|
1717
|
+
if (stageFinalized) return false;
|
|
1718
|
+
stageFinalized = true;
|
|
1719
|
+
stageSnapshot.endedAt = Date.now();
|
|
1720
|
+
stageSnapshot.durationMs = elapsedStageMs(stageSnapshot, stageSnapshot.endedAt);
|
|
1721
|
+
|
|
1722
|
+
const finalModelMeta = innerCtx.__modelFallbackMeta();
|
|
1723
|
+
if (finalModelMeta.model !== undefined) stageSnapshot.model = finalModelMeta.model;
|
|
1724
|
+
if (finalModelMeta.attemptedModels !== undefined) stageSnapshot.attemptedModels = finalModelMeta.attemptedModels;
|
|
1725
|
+
if (finalModelMeta.modelAttempts !== undefined) stageSnapshot.modelAttempts = finalModelMeta.modelAttempts;
|
|
1726
|
+
|
|
1727
|
+
activeStore.recordStageEnd(runId, stageSnapshot);
|
|
1728
|
+
opts.onStageEnd?.(runId, stageSnapshot);
|
|
1729
|
+
|
|
1730
|
+
if (opts.persistence) {
|
|
1731
|
+
appendStageStartOnce();
|
|
1732
|
+
appendStageEnd(opts.persistence, {
|
|
1733
|
+
runId,
|
|
1734
|
+
stageId,
|
|
1735
|
+
status: stageSnapshot.status,
|
|
1736
|
+
durationMs: stageSnapshot.durationMs,
|
|
1737
|
+
...(stageSnapshot.error !== undefined ? { error: stageSnapshot.error } : {}),
|
|
1738
|
+
...(stageSnapshot.failureKind !== undefined ? { failureKind: stageSnapshot.failureKind } : {}),
|
|
1739
|
+
...(stageSnapshot.failureMessage !== undefined ? { failureMessage: stageSnapshot.failureMessage } : {}),
|
|
1740
|
+
...(stageSnapshot.skippedReason !== undefined ? { skippedReason: stageSnapshot.skippedReason } : {}),
|
|
1741
|
+
...(stageSnapshot.result !== undefined && stageSnapshot.status === "completed" ? { summary: stageSnapshot.result } : {}),
|
|
1742
|
+
...(stageSnapshot.replayedFromStageId !== undefined ? { replayedFromStageId: stageSnapshot.replayedFromStageId } : {}),
|
|
1743
|
+
...(stageSnapshot.replayed !== undefined ? { replayed: stageSnapshot.replayed } : {}),
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
stageFailFastScope?.activeStages.delete(stageId);
|
|
1748
|
+
tracker.onSettle(stageId);
|
|
1749
|
+
return true;
|
|
1750
|
+
};
|
|
1751
|
+
let skippedForParallelFailFast = false;
|
|
1752
|
+
const markSkippedForParallelFailFast = (): void => {
|
|
1753
|
+
skippedForParallelFailFast = true;
|
|
1754
|
+
stageSnapshot.status = "skipped";
|
|
1755
|
+
stageSnapshot.skippedReason = "fail-fast";
|
|
1756
|
+
};
|
|
1757
|
+
const parallelFailFastError = (): unknown =>
|
|
1758
|
+
stageFailFastScope?.firstFailure ?? new Error("pi-workflows: skipped after parallel fail-fast");
|
|
1759
|
+
const skipForParallelFailFast = (): void => {
|
|
1760
|
+
if (isTerminalStage(stageSnapshot)) return;
|
|
1761
|
+
markSkippedForParallelFailFast();
|
|
1762
|
+
finalizeStageSnapshot();
|
|
1763
|
+
void innerCtx.abort().catch(() => {});
|
|
1764
|
+
void releaseLiveHandleWhenIdle().catch(() => {});
|
|
1765
|
+
};
|
|
1766
|
+
stageFailFastScope?.activeStages.set(stageId, { skip: skipForParallelFailFast });
|
|
1767
|
+
|
|
1424
1768
|
let stageControlDropped = false;
|
|
1425
1769
|
dropStageControlHandle = (): void => {
|
|
1426
1770
|
if (stageControlDropped) return;
|
|
@@ -1454,12 +1798,18 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1454
1798
|
|
|
1455
1799
|
const runTrackedStageCall = async (call: () => Promise<string>): Promise<string> => {
|
|
1456
1800
|
await waitForStageRelease();
|
|
1801
|
+
if (stageFinalized) {
|
|
1802
|
+
throw parallelFailFastError();
|
|
1803
|
+
}
|
|
1457
1804
|
|
|
1458
1805
|
// Block here until a concurrency slot is available for this run.
|
|
1459
1806
|
await limiter.acquire();
|
|
1460
1807
|
|
|
1461
1808
|
try {
|
|
1462
1809
|
await waitForStageRelease();
|
|
1810
|
+
if (stageFinalized) {
|
|
1811
|
+
throw parallelFailFastError();
|
|
1812
|
+
}
|
|
1463
1813
|
} catch (err) {
|
|
1464
1814
|
limiter.release();
|
|
1465
1815
|
throw err;
|
|
@@ -1470,15 +1820,7 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1470
1820
|
activeStore.recordStageStart(runId, stageSnapshot);
|
|
1471
1821
|
|
|
1472
1822
|
// Persistence: append stage.start entry
|
|
1473
|
-
|
|
1474
|
-
appendStageStart(opts.persistence, {
|
|
1475
|
-
runId,
|
|
1476
|
-
stageId,
|
|
1477
|
-
name,
|
|
1478
|
-
parentIds: stageSnapshot.parentIds,
|
|
1479
|
-
ts: stageSnapshot.startedAt,
|
|
1480
|
-
});
|
|
1481
|
-
}
|
|
1823
|
+
appendStageStartOnce();
|
|
1482
1824
|
|
|
1483
1825
|
const mcpAllow = options?.mcp?.allow ?? null;
|
|
1484
1826
|
const mcpDeny = options?.mcp?.deny ?? null;
|
|
@@ -1513,6 +1855,13 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1513
1855
|
if (modelMeta.attemptedModels !== undefined) stageSnapshot.attemptedModels = modelMeta.attemptedModels;
|
|
1514
1856
|
if (modelMeta.modelAttempts !== undefined) stageSnapshot.modelAttempts = modelMeta.modelAttempts;
|
|
1515
1857
|
}
|
|
1858
|
+
if (stageFailFastScope?.failed === true && stageFailFastScope.activeStages.has(stageId)) {
|
|
1859
|
+
markSkippedForParallelFailFast();
|
|
1860
|
+
throw parallelFailFastError();
|
|
1861
|
+
}
|
|
1862
|
+
if (stageFinalized) {
|
|
1863
|
+
throw parallelFailFastError();
|
|
1864
|
+
}
|
|
1516
1865
|
stageSnapshot.status = "completed";
|
|
1517
1866
|
const assistantText = innerCtx.__getLastAssistantText();
|
|
1518
1867
|
if (assistantText !== undefined) {
|
|
@@ -1520,77 +1869,24 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1520
1869
|
}
|
|
1521
1870
|
return result;
|
|
1522
1871
|
} catch (err) {
|
|
1523
|
-
if (!ownController.signal.aborted) {
|
|
1524
|
-
stageSnapshot
|
|
1525
|
-
stageSnapshot.error = err instanceof Error ? err.message : String(err);
|
|
1872
|
+
if (!ownController.signal.aborted && !skippedForParallelFailFast) {
|
|
1873
|
+
applyFailureToStage(stageSnapshot, classifyWorkflowFailure(err));
|
|
1526
1874
|
}
|
|
1527
1875
|
throw err;
|
|
1528
1876
|
} finally {
|
|
1529
|
-
stageSnapshot.endedAt = Date.now();
|
|
1530
|
-
stageSnapshot.durationMs = elapsedStageMs(stageSnapshot, stageSnapshot.endedAt);
|
|
1531
|
-
|
|
1532
|
-
const finalModelMeta = innerCtx.__modelFallbackMeta();
|
|
1533
|
-
if (finalModelMeta.model !== undefined) stageSnapshot.model = finalModelMeta.model;
|
|
1534
|
-
if (finalModelMeta.attemptedModels !== undefined) stageSnapshot.attemptedModels = finalModelMeta.attemptedModels;
|
|
1535
|
-
if (finalModelMeta.modelAttempts !== undefined) stageSnapshot.modelAttempts = finalModelMeta.modelAttempts;
|
|
1536
|
-
|
|
1537
1877
|
if (opts.mcp && hasMcpScope) {
|
|
1538
1878
|
opts.mcp.clearScope(stageId);
|
|
1539
1879
|
}
|
|
1540
1880
|
|
|
1541
|
-
|
|
1542
|
-
opts.onStageEnd?.(runId, stageSnapshot);
|
|
1543
|
-
|
|
1544
|
-
// Persistence: append stage.end entry
|
|
1545
|
-
if (opts.persistence) {
|
|
1546
|
-
appendStageEnd(opts.persistence, {
|
|
1547
|
-
runId,
|
|
1548
|
-
stageId,
|
|
1549
|
-
status: stageSnapshot.status,
|
|
1550
|
-
durationMs: stageSnapshot.durationMs,
|
|
1551
|
-
});
|
|
1552
|
-
}
|
|
1553
|
-
|
|
1554
|
-
tracker.onSettle(stageId);
|
|
1881
|
+
finalizeStageSnapshot();
|
|
1555
1882
|
// The stage has finished participating in workflow scheduling. Drop it
|
|
1556
|
-
// from run-level pause/resume and cascade-pause lookups immediately
|
|
1557
|
-
//
|
|
1558
|
-
//
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
const releaseWhenDetached = (force = false): void => {
|
|
1564
|
-
const currentRun = activeStore.runs().find((r) => r.id === runId);
|
|
1565
|
-
const currentStage = currentRun?.stages.find((s) => s.id === stageId);
|
|
1566
|
-
if (!force && currentStage?.attached === true) return;
|
|
1567
|
-
unsubscribeDetach?.();
|
|
1568
|
-
unsubscribeDetach = undefined;
|
|
1569
|
-
if (abortListener) {
|
|
1570
|
-
ownController.signal.removeEventListener("abort", abortListener);
|
|
1571
|
-
abortListener = undefined;
|
|
1572
|
-
}
|
|
1573
|
-
void releaseLiveHandle().catch(() => {});
|
|
1574
|
-
};
|
|
1575
|
-
unsubscribeDetach = activeStore.subscribe(() => releaseWhenDetached());
|
|
1576
|
-
abortListener = () => releaseWhenDetached(true);
|
|
1577
|
-
if (ownController.signal.aborted) releaseWhenDetached(true);
|
|
1578
|
-
else {
|
|
1579
|
-
ownController.signal.addEventListener(
|
|
1580
|
-
"abort",
|
|
1581
|
-
abortListener,
|
|
1582
|
-
{ once: true },
|
|
1583
|
-
);
|
|
1584
|
-
}
|
|
1585
|
-
releaseWhenDetached();
|
|
1586
|
-
limiter.release();
|
|
1587
|
-
} else {
|
|
1588
|
-
try {
|
|
1589
|
-
await releaseLiveHandle();
|
|
1590
|
-
} finally {
|
|
1591
|
-
limiter.release();
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1883
|
+
// from run-level pause/resume and cascade-pause lookups immediately.
|
|
1884
|
+
// If no SDK queue/active input remains, release the live chat handle so
|
|
1885
|
+
// the node reopens as a read-only archived session. Queued messages keep
|
|
1886
|
+
// the direct handle alive only until the SDK reports that the queue has
|
|
1887
|
+
// drained.
|
|
1888
|
+
await releaseLiveHandleWhenIdle().catch(() => {});
|
|
1889
|
+
limiter.release();
|
|
1594
1890
|
}
|
|
1595
1891
|
};
|
|
1596
1892
|
|
|
@@ -1678,9 +1974,13 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1678
1974
|
return stageContext;
|
|
1679
1975
|
},
|
|
1680
1976
|
|
|
1681
|
-
async task(name: string, options: WorkflowTaskOptions): Promise<WorkflowTaskResult> {
|
|
1977
|
+
async task(name: string, options: WorkflowTaskOptions, stageFailFastScope?: ParallelFailFastScope): Promise<WorkflowTaskResult> {
|
|
1682
1978
|
const runTaskOnce = async (taskOptions: WorkflowTaskOptions): Promise<WorkflowTaskResult> => {
|
|
1683
|
-
const stage = ctx.stage(
|
|
1979
|
+
const stage = (ctx.stage as typeof ctx.stage & ((stageName: string, stageOptions?: StageOptions, scope?: ParallelFailFastScope) => StageContext))(
|
|
1980
|
+
name,
|
|
1981
|
+
taskStageOptions(taskOptions),
|
|
1982
|
+
stageFailFastScope,
|
|
1983
|
+
);
|
|
1684
1984
|
const rawText = await stage.prompt(
|
|
1685
1985
|
applyTaskContext(`${taskReadInstruction(taskOptions)}${taskPrompt(taskOptions)}`, taskPrevious(taskOptions)),
|
|
1686
1986
|
taskPromptOptions(taskOptions),
|
|
@@ -1744,12 +2044,23 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1744
2044
|
|
|
1745
2045
|
async parallel(steps: readonly WorkflowTaskStep[], options: WorkflowParallelOptions = {}): Promise<WorkflowTaskResult[]> {
|
|
1746
2046
|
const fallback = parallelFallbackTask(steps, options);
|
|
2047
|
+
const failFastScope: ParallelFailFastScope | undefined = options.failFast === false
|
|
2048
|
+
? undefined
|
|
2049
|
+
: { failed: false, activeStages: new Map<string, ParallelFailFastStage>() };
|
|
1747
2050
|
return mapParallelSteps(steps, options.concurrency, options.failFast, async (step) => {
|
|
1748
2051
|
const prompt = replaceTaskPlaceholder(step.prompt ?? step.task ?? fallback, options.task ?? fallback);
|
|
1749
|
-
return ctx.task(
|
|
2052
|
+
return await (ctx.task as typeof ctx.task & ((taskName: string, taskOptions: WorkflowTaskOptions, scope?: ParallelFailFastScope) => Promise<WorkflowTaskResult>))(
|
|
1750
2053
|
step.name,
|
|
1751
2054
|
taskWithSharedDefaults(taskOptionsFromStep(step, prompt, taskPrevious(step)), options),
|
|
2055
|
+
failFastScope,
|
|
1752
2056
|
);
|
|
2057
|
+
}, (error) => {
|
|
2058
|
+
if (failFastScope === undefined) return;
|
|
2059
|
+
failFastScope.failed = true;
|
|
2060
|
+
failFastScope.firstFailure = error;
|
|
2061
|
+
for (const stage of failFastScope.activeStages.values()) {
|
|
2062
|
+
stage.skip();
|
|
2063
|
+
}
|
|
1753
2064
|
});
|
|
1754
2065
|
},
|
|
1755
2066
|
};
|
|
@@ -1792,21 +2103,25 @@ export async function run<TInputs extends Record<string, unknown>>(
|
|
|
1792
2103
|
return finalizeKilled(runId, runSnapshot, activeStore, opts.persistence, opts.onRunEnd);
|
|
1793
2104
|
}
|
|
1794
2105
|
|
|
1795
|
-
const
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
opts.onRunEnd?.(runId, "failed", undefined, errorMessage);
|
|
2106
|
+
const metadata = runFailureMetadata(err, runSnapshot.stages);
|
|
2107
|
+
const recorded = activeStore.recordRunEnd(runId, "failed", undefined, metadata.errorMessage, metadata);
|
|
2108
|
+
opts.onRunEnd?.(runId, "failed", undefined, metadata.errorMessage);
|
|
1799
2109
|
|
|
1800
2110
|
appendRunEndWhenRecorded(opts.persistence, recorded, {
|
|
1801
2111
|
runId,
|
|
1802
2112
|
status: "failed",
|
|
2113
|
+
error: metadata.errorMessage,
|
|
2114
|
+
failureKind: metadata.failureKind,
|
|
2115
|
+
failureMessage: metadata.failureMessage,
|
|
2116
|
+
...(metadata.failedStageId !== undefined ? { failedStageId: metadata.failedStageId } : {}),
|
|
2117
|
+
resumable: metadata.resumable,
|
|
1803
2118
|
ts: Date.now(),
|
|
1804
2119
|
});
|
|
1805
2120
|
|
|
1806
2121
|
return {
|
|
1807
2122
|
runId,
|
|
1808
2123
|
status: "failed",
|
|
1809
|
-
error: errorMessage,
|
|
2124
|
+
error: metadata.errorMessage,
|
|
1810
2125
|
stages: [...runSnapshot.stages],
|
|
1811
2126
|
};
|
|
1812
2127
|
} finally {
|