@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
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import type { Component, Focusable, TUI } from "@earendil-works/pi-tui";
|
|
3
|
+
import type { Store } from "./store.js";
|
|
4
|
+
import { store as defaultStore } from "./store.js";
|
|
5
|
+
import type { PiCustomOverlayFactory, PiCustomOverlayOptions, PiKeybindings, PiTheme } from "../extension/wiring.js";
|
|
6
|
+
|
|
7
|
+
export interface StageCustomUiRequest<T = unknown> {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly runId: string;
|
|
10
|
+
readonly stageId: string;
|
|
11
|
+
readonly factory: PiCustomOverlayFactory<T>;
|
|
12
|
+
readonly options?: PiCustomOverlayOptions;
|
|
13
|
+
readonly createdAt: number;
|
|
14
|
+
resolve(value: T): void;
|
|
15
|
+
reject(reason: unknown): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface StageCustomUiHost {
|
|
19
|
+
showCustomUi(request: StageCustomUiRequest): void;
|
|
20
|
+
hideCustomUi?(request: StageCustomUiRequest, reason: unknown): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function key(runId: string, stageId: string): string {
|
|
24
|
+
return `${runId}\0${stageId}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function nextRequestId(): string {
|
|
28
|
+
return `stage-ui-${randomUUID()}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class StageUiBroker {
|
|
32
|
+
private readonly store: Store;
|
|
33
|
+
private readonly pending = new Map<string, StageCustomUiRequest>();
|
|
34
|
+
private readonly hosts = new Map<string, StageCustomUiHost>();
|
|
35
|
+
|
|
36
|
+
constructor(store: Store = defaultStore) {
|
|
37
|
+
this.store = store;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private hideHost(host: StageCustomUiHost | undefined, request: StageCustomUiRequest, reason: unknown): void {
|
|
41
|
+
try {
|
|
42
|
+
host?.hideCustomUi?.(request, reason);
|
|
43
|
+
} catch {
|
|
44
|
+
// Host teardown is best-effort; request settlement must still continue.
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private showHostOrReject(host: StageCustomUiHost, request: StageCustomUiRequest): void {
|
|
49
|
+
try {
|
|
50
|
+
host.showCustomUi(request);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
this.reject(request, error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
registerHost(runId: string, stageId: string, host: StageCustomUiHost): () => void {
|
|
57
|
+
const hostKey = key(runId, stageId);
|
|
58
|
+
const previousHost = this.hosts.get(hostKey);
|
|
59
|
+
const request = this.pending.get(hostKey);
|
|
60
|
+
if (previousHost && previousHost !== host && request) {
|
|
61
|
+
this.hideHost(
|
|
62
|
+
previousHost,
|
|
63
|
+
request,
|
|
64
|
+
new Error(`pi-workflows: stage ${stageId} custom UI host replaced`),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
this.hosts.set(hostKey, host);
|
|
68
|
+
const activeRequest = this.pending.get(hostKey);
|
|
69
|
+
if (activeRequest) this.showHostOrReject(host, activeRequest);
|
|
70
|
+
return () => {
|
|
71
|
+
if (this.hosts.get(hostKey) !== host) return;
|
|
72
|
+
this.hosts.delete(hostKey);
|
|
73
|
+
const pendingRequest = this.pending.get(hostKey);
|
|
74
|
+
if (pendingRequest) {
|
|
75
|
+
this.reject(
|
|
76
|
+
pendingRequest,
|
|
77
|
+
new Error(`pi-workflows: stage ${stageId} custom UI host unregistered`),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
requestCustomUi<T>(
|
|
84
|
+
runId: string,
|
|
85
|
+
stageId: string,
|
|
86
|
+
factory: PiCustomOverlayFactory<T>,
|
|
87
|
+
options?: PiCustomOverlayOptions,
|
|
88
|
+
signal?: AbortSignal,
|
|
89
|
+
): Promise<T> {
|
|
90
|
+
if (signal?.aborted) {
|
|
91
|
+
return Promise.reject(signal.reason ?? new Error("pi-workflows: stage UI request aborted"));
|
|
92
|
+
}
|
|
93
|
+
const hostKey = key(runId, stageId);
|
|
94
|
+
const existing = this.pending.get(hostKey);
|
|
95
|
+
if (existing) {
|
|
96
|
+
return Promise.reject(new Error(`pi-workflows: stage ${stageId} already has a pending custom UI request`));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let request!: StageCustomUiRequest<T>;
|
|
100
|
+
const promise = new Promise<T>((resolve, reject) => {
|
|
101
|
+
request = {
|
|
102
|
+
id: nextRequestId(),
|
|
103
|
+
runId,
|
|
104
|
+
stageId,
|
|
105
|
+
factory,
|
|
106
|
+
...(options !== undefined ? { options } : {}),
|
|
107
|
+
createdAt: Date.now(),
|
|
108
|
+
resolve,
|
|
109
|
+
reject,
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const onAbort = (): void => {
|
|
114
|
+
this.reject(request, signal?.reason ?? new Error("pi-workflows: stage UI request aborted"));
|
|
115
|
+
};
|
|
116
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
117
|
+
|
|
118
|
+
this.pending.set(hostKey, request);
|
|
119
|
+
this.store.recordStageAwaitingInput(runId, stageId, true, request.createdAt);
|
|
120
|
+
const host = this.hosts.get(hostKey);
|
|
121
|
+
if (host) this.showHostOrReject(host, request);
|
|
122
|
+
// Re-check after listener registration and host display; AbortSignal does
|
|
123
|
+
// not replay an already-fired abort event for listeners added later.
|
|
124
|
+
if (signal?.aborted) onAbort();
|
|
125
|
+
|
|
126
|
+
return promise.finally(() => {
|
|
127
|
+
signal?.removeEventListener("abort", onAbort);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
resolve<T>(request: StageCustomUiRequest<T>, value: T): void {
|
|
132
|
+
const hostKey = key(request.runId, request.stageId);
|
|
133
|
+
if (this.pending.get(hostKey)?.id !== request.id) return;
|
|
134
|
+
this.pending.delete(hostKey);
|
|
135
|
+
this.store.recordStageAwaitingInput(request.runId, request.stageId, false);
|
|
136
|
+
this.hideHost(this.hosts.get(hostKey), request, undefined);
|
|
137
|
+
request.resolve(value);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
reject(request: StageCustomUiRequest, reason: unknown): void {
|
|
141
|
+
const hostKey = key(request.runId, request.stageId);
|
|
142
|
+
if (this.pending.get(hostKey)?.id !== request.id) return;
|
|
143
|
+
this.pending.delete(hostKey);
|
|
144
|
+
this.store.recordStageAwaitingInput(request.runId, request.stageId, false);
|
|
145
|
+
this.hideHost(this.hosts.get(hostKey), request, reason);
|
|
146
|
+
request.reject(reason);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface MountedStageCustomUi {
|
|
151
|
+
readonly request: StageCustomUiRequest;
|
|
152
|
+
readonly component: Component & { dispose?(): void };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function mountStageCustomUi(
|
|
156
|
+
request: StageCustomUiRequest,
|
|
157
|
+
tui: TUI,
|
|
158
|
+
theme: PiTheme,
|
|
159
|
+
keybindings: PiKeybindings,
|
|
160
|
+
broker: StageUiBroker,
|
|
161
|
+
onDone?: () => void,
|
|
162
|
+
): Promise<MountedStageCustomUi> {
|
|
163
|
+
const rawComponent = await request.factory(
|
|
164
|
+
tui as unknown as Parameters<StageCustomUiRequest["factory"]>[0],
|
|
165
|
+
theme,
|
|
166
|
+
keybindings,
|
|
167
|
+
(result: unknown) => {
|
|
168
|
+
broker.resolve(request, result);
|
|
169
|
+
onDone?.();
|
|
170
|
+
},
|
|
171
|
+
);
|
|
172
|
+
const component: Component & { dispose?(): void } & Partial<Focusable> = {
|
|
173
|
+
render: (width) => rawComponent.render(width),
|
|
174
|
+
...(rawComponent.handleInput !== undefined
|
|
175
|
+
? { handleInput: (data: string) => rawComponent.handleInput?.(data) }
|
|
176
|
+
: {}),
|
|
177
|
+
invalidate: () => rawComponent.invalidate?.(),
|
|
178
|
+
...(rawComponent.dispose !== undefined ? { dispose: () => rawComponent.dispose?.() } : {}),
|
|
179
|
+
};
|
|
180
|
+
if ("focused" in rawComponent) {
|
|
181
|
+
Object.defineProperty(component, "focused", {
|
|
182
|
+
get: () => (rawComponent as Component & Partial<Focusable>).focused,
|
|
183
|
+
set: (value: boolean) => {
|
|
184
|
+
(rawComponent as Component & Partial<Focusable>).focused = value;
|
|
185
|
+
},
|
|
186
|
+
enumerable: true,
|
|
187
|
+
configurable: true,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return { request, component };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export const stageUiBroker = new StageUiBroker();
|
|
@@ -11,7 +11,10 @@ export type StageStatus =
|
|
|
11
11
|
| "paused"
|
|
12
12
|
| "blocked"
|
|
13
13
|
| "completed"
|
|
14
|
-
| "failed"
|
|
14
|
+
| "failed"
|
|
15
|
+
| "skipped";
|
|
16
|
+
|
|
17
|
+
export type WorkflowFailureKind = "auth" | "rate_limit" | "provider" | "cancelled" | "unknown";
|
|
15
18
|
|
|
16
19
|
/**
|
|
17
20
|
* Human-in-the-loop prompt kind. Mirrors the four `WorkflowUIContext` methods.
|
|
@@ -66,9 +69,21 @@ export interface StageSnapshot {
|
|
|
66
69
|
durationMs?: number;
|
|
67
70
|
result?: string;
|
|
68
71
|
error?: string;
|
|
72
|
+
/** Structured workflow failure category for failed stages. */
|
|
73
|
+
failureKind?: WorkflowFailureKind;
|
|
74
|
+
/** Original unsanitized error text when different from `error`. */
|
|
75
|
+
failureMessage?: string;
|
|
76
|
+
/** Reason for stages skipped by fail-fast/cascade handling. */
|
|
77
|
+
skippedReason?: string;
|
|
78
|
+
/** Source stage id when this stage was replayed during failed-run continuation. */
|
|
79
|
+
replayedFromStageId?: string;
|
|
80
|
+
/** True when provider work was skipped by continuation replay. */
|
|
81
|
+
replayed?: boolean;
|
|
69
82
|
readonly toolEvents: ToolEvent[];
|
|
70
83
|
/** True while an in-stage ask_user_question tool is waiting on the user. */
|
|
71
84
|
awaitingInputSince?: number;
|
|
85
|
+
/** Pending human-in-the-loop prompt owned by this workflow stage/node. */
|
|
86
|
+
pendingPrompt?: PendingPrompt;
|
|
72
87
|
blockedByStageId?: string;
|
|
73
88
|
notices?: StageNotice[];
|
|
74
89
|
/**
|
|
@@ -125,6 +140,16 @@ export interface RunSnapshot {
|
|
|
125
140
|
resumedAt?: number;
|
|
126
141
|
result?: Record<string, unknown>;
|
|
127
142
|
error?: string;
|
|
143
|
+
/** Structured workflow failure category for failed runs. */
|
|
144
|
+
failureKind?: WorkflowFailureKind;
|
|
145
|
+
/** Original unsanitized error text when different from `error`. */
|
|
146
|
+
failureMessage?: string;
|
|
147
|
+
failedStageId?: string;
|
|
148
|
+
resumable?: boolean;
|
|
149
|
+
/** Source failed run when this run is a continuation. */
|
|
150
|
+
resumedFromRunId?: string;
|
|
151
|
+
/** Source stage id where continuation resumes real execution. */
|
|
152
|
+
resumeFromStageId?: string;
|
|
128
153
|
/**
|
|
129
154
|
* Pending human-in-the-loop prompt. Set when a background workflow calls
|
|
130
155
|
* `ctx.ui.input/confirm/select/editor`; cleared when the user responds via
|
|
@@ -11,6 +11,8 @@ import type {
|
|
|
11
11
|
StoreSnapshot,
|
|
12
12
|
ToolEvent,
|
|
13
13
|
RunStatus,
|
|
14
|
+
StageStatus,
|
|
15
|
+
WorkflowFailureKind,
|
|
14
16
|
WorkflowNotice,
|
|
15
17
|
} from "./store-types.js";
|
|
16
18
|
import { accumulatePausedDurationMs, elapsedRunMs } from "./timing.js";
|
|
@@ -18,6 +20,29 @@ import { accumulatePausedDurationMs, elapsedRunMs } from "./timing.js";
|
|
|
18
20
|
/** Statuses that represent a terminal run state — cannot be overwritten. */
|
|
19
21
|
const TERMINAL_STATUSES: ReadonlySet<RunStatus> = new Set(["completed", "failed", "killed"]);
|
|
20
22
|
|
|
23
|
+
function isTerminalStageStatus(status: StageStatus): boolean {
|
|
24
|
+
return status === "completed" || status === "failed" || status === "skipped";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function cannotAwaitInput(status: StageStatus): boolean {
|
|
28
|
+
return isTerminalStageStatus(status) || status === "paused" || status === "blocked";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function cannotBlock(status: StageStatus): boolean {
|
|
32
|
+
return isTerminalStageStatus(status) || status === "paused";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function cannotPause(status: StageStatus): boolean {
|
|
36
|
+
return isTerminalStageStatus(status) || status === "paused" || status === "blocked";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface RunEndMetadata {
|
|
40
|
+
readonly failureKind?: WorkflowFailureKind;
|
|
41
|
+
readonly failureMessage?: string;
|
|
42
|
+
readonly failedStageId?: string;
|
|
43
|
+
readonly resumable?: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
21
46
|
export interface Store {
|
|
22
47
|
runs(): readonly RunSnapshot[];
|
|
23
48
|
notices(): readonly WorkflowNotice[];
|
|
@@ -39,6 +64,7 @@ export interface Store {
|
|
|
39
64
|
status: RunStatus,
|
|
40
65
|
result?: Record<string, unknown>,
|
|
41
66
|
error?: string,
|
|
67
|
+
metadata?: RunEndMetadata,
|
|
42
68
|
): boolean;
|
|
43
69
|
/**
|
|
44
70
|
* Remove a run from live workflow history/status. Any pending HIL prompt
|
|
@@ -84,6 +110,17 @@ export interface Store {
|
|
|
84
110
|
* overlay-driven response. Foreground runs never call this.
|
|
85
111
|
*/
|
|
86
112
|
awaitPendingPrompt(runId: string, promptId: string): Promise<unknown>;
|
|
113
|
+
/** Record a pending HIL prompt for a specific workflow stage/node. */
|
|
114
|
+
recordStagePendingPrompt(runId: string, stageId: string, prompt: PendingPrompt): boolean;
|
|
115
|
+
/** Resolve a pending HIL prompt on a specific workflow stage/node. */
|
|
116
|
+
resolveStagePendingPrompt(
|
|
117
|
+
runId: string,
|
|
118
|
+
stageId: string,
|
|
119
|
+
promptId: string,
|
|
120
|
+
response: unknown,
|
|
121
|
+
): boolean;
|
|
122
|
+
/** Wait for a stage/node-scoped HIL prompt to resolve. */
|
|
123
|
+
awaitStagePendingPrompt(runId: string, stageId: string, promptId: string): Promise<unknown>;
|
|
87
124
|
/**
|
|
88
125
|
* Record Pi/pi SDK session metadata for a stage after lazy
|
|
89
126
|
* attach. The serializable snapshot tracks this so post-mortem reopen
|
|
@@ -186,6 +223,26 @@ export function createStore(): Store {
|
|
|
186
223
|
return run.stages.find((s) => s.id === stageId);
|
|
187
224
|
}
|
|
188
225
|
|
|
226
|
+
function rejectPrompt(promptId: string, reason: string): void {
|
|
227
|
+
const entry = _resolvers.get(promptId);
|
|
228
|
+
if (!entry) return;
|
|
229
|
+
_resolvers.delete(promptId);
|
|
230
|
+
entry.reject(new Error(reason));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function rejectStagePrompt(stage: StageSnapshot, reason: string): void {
|
|
234
|
+
const prompt = stage.pendingPrompt;
|
|
235
|
+
if (!prompt) return;
|
|
236
|
+
stage.pendingPrompt = undefined;
|
|
237
|
+
rejectPrompt(prompt.id, reason);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function rejectAllStagePrompts(run: RunSnapshot, reason: string): void {
|
|
241
|
+
for (const stage of run.stages) {
|
|
242
|
+
rejectStagePrompt(stage, reason);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
189
246
|
return {
|
|
190
247
|
runs(): readonly RunSnapshot[] {
|
|
191
248
|
return _runs;
|
|
@@ -274,7 +331,13 @@ export function createStore(): Store {
|
|
|
274
331
|
existing.durationMs = stage.durationMs;
|
|
275
332
|
existing.result = stage.result;
|
|
276
333
|
existing.error = stage.error;
|
|
334
|
+
existing.failureKind = stage.failureKind;
|
|
335
|
+
existing.failureMessage = stage.failureMessage;
|
|
336
|
+
existing.skippedReason = stage.skippedReason;
|
|
337
|
+
existing.replayedFromStageId = stage.replayedFromStageId;
|
|
338
|
+
existing.replayed = stage.replayed;
|
|
277
339
|
delete existing.awaitingInputSince;
|
|
340
|
+
rejectStagePrompt(existing, `pi-workflows: stage ${stage.id} ended before prompt resolved`);
|
|
278
341
|
_version++;
|
|
279
342
|
notify();
|
|
280
343
|
},
|
|
@@ -284,6 +347,7 @@ export function createStore(): Store {
|
|
|
284
347
|
status: RunStatus,
|
|
285
348
|
result?: Record<string, unknown>,
|
|
286
349
|
error?: string,
|
|
350
|
+
metadata?: RunEndMetadata,
|
|
287
351
|
): boolean {
|
|
288
352
|
const run = findRun(runId);
|
|
289
353
|
if (!run) return false;
|
|
@@ -306,20 +370,21 @@ export function createStore(): Store {
|
|
|
306
370
|
if ((status === "failed" || status === "killed") && error !== undefined) {
|
|
307
371
|
run.error = error;
|
|
308
372
|
}
|
|
373
|
+
if (metadata !== undefined) {
|
|
374
|
+
if (metadata.failureKind !== undefined) run.failureKind = metadata.failureKind;
|
|
375
|
+
if (metadata.failureMessage !== undefined) run.failureMessage = metadata.failureMessage;
|
|
376
|
+
if (metadata.failedStageId !== undefined) run.failedStageId = metadata.failedStageId;
|
|
377
|
+
if (metadata.resumable !== undefined) run.resumable = metadata.resumable;
|
|
378
|
+
}
|
|
309
379
|
// Abandon any waiting HIL prompt — workflow body never resumed past
|
|
310
380
|
// it, but the awaiter promise must reject so the executor's catch
|
|
311
381
|
// can finalise the run state cleanly.
|
|
312
382
|
const pending = run.pendingPrompt;
|
|
313
383
|
if (pending) {
|
|
314
384
|
run.pendingPrompt = undefined;
|
|
315
|
-
|
|
316
|
-
if (entry) {
|
|
317
|
-
_resolvers.delete(pending.id);
|
|
318
|
-
entry.reject(
|
|
319
|
-
new Error(`pi-workflows: run ${runId} ended before prompt resolved`),
|
|
320
|
-
);
|
|
321
|
-
}
|
|
385
|
+
rejectPrompt(pending.id, `pi-workflows: run ${runId} ended before prompt resolved`);
|
|
322
386
|
}
|
|
387
|
+
rejectAllStagePrompts(run, `pi-workflows: run ${runId} ended before prompt resolved`);
|
|
323
388
|
_version++;
|
|
324
389
|
notify();
|
|
325
390
|
return true;
|
|
@@ -331,14 +396,9 @@ export function createStore(): Store {
|
|
|
331
396
|
const run = _runs[index]!;
|
|
332
397
|
const pending = run.pendingPrompt;
|
|
333
398
|
if (pending) {
|
|
334
|
-
|
|
335
|
-
if (entry) {
|
|
336
|
-
_resolvers.delete(pending.id);
|
|
337
|
-
entry.reject(
|
|
338
|
-
new Error(`pi-workflows: run ${runId} was removed before prompt resolved`),
|
|
339
|
-
);
|
|
340
|
-
}
|
|
399
|
+
rejectPrompt(pending.id, `pi-workflows: run ${runId} was removed before prompt resolved`);
|
|
341
400
|
}
|
|
401
|
+
rejectAllStagePrompts(run, `pi-workflows: run ${runId} was removed before prompt resolved`);
|
|
342
402
|
_runs.splice(index, 1);
|
|
343
403
|
for (let i = _notices.length - 1; i >= 0; i--) {
|
|
344
404
|
if (_notices[i]?.runId === runId) _notices.splice(i, 1);
|
|
@@ -416,6 +476,74 @@ export function createStore(): Store {
|
|
|
416
476
|
});
|
|
417
477
|
},
|
|
418
478
|
|
|
479
|
+
recordStagePendingPrompt(runId: string, stageId: string, prompt: PendingPrompt): boolean {
|
|
480
|
+
const run = findRun(runId);
|
|
481
|
+
if (!run) return false;
|
|
482
|
+
if (TERMINAL_STATUSES.has(run.status)) return false;
|
|
483
|
+
const stage = findStage(run, stageId);
|
|
484
|
+
if (!stage) return false;
|
|
485
|
+
if (isTerminalStageStatus(stage.status)) return false;
|
|
486
|
+
if (stage.pendingPrompt !== undefined) return false;
|
|
487
|
+
stage.pendingPrompt = { ...prompt };
|
|
488
|
+
stage.status = "awaiting_input";
|
|
489
|
+
stage.awaitingInputSince = prompt.createdAt;
|
|
490
|
+
_version++;
|
|
491
|
+
notify();
|
|
492
|
+
return true;
|
|
493
|
+
},
|
|
494
|
+
|
|
495
|
+
resolveStagePendingPrompt(
|
|
496
|
+
runId: string,
|
|
497
|
+
stageId: string,
|
|
498
|
+
promptId: string,
|
|
499
|
+
response: unknown,
|
|
500
|
+
): boolean {
|
|
501
|
+
const run = findRun(runId);
|
|
502
|
+
if (!run) return false;
|
|
503
|
+
const stage = findStage(run, stageId);
|
|
504
|
+
if (!stage) return false;
|
|
505
|
+
const pending = stage.pendingPrompt;
|
|
506
|
+
if (!pending || pending.id !== promptId) return false;
|
|
507
|
+
stage.pendingPrompt = undefined;
|
|
508
|
+
if (stage.status === "awaiting_input") {
|
|
509
|
+
stage.status = "running";
|
|
510
|
+
delete stage.awaitingInputSince;
|
|
511
|
+
}
|
|
512
|
+
_version++;
|
|
513
|
+
notify();
|
|
514
|
+
const entry = _resolvers.get(promptId);
|
|
515
|
+
if (entry) {
|
|
516
|
+
_resolvers.delete(promptId);
|
|
517
|
+
entry.resolve(response);
|
|
518
|
+
}
|
|
519
|
+
return true;
|
|
520
|
+
},
|
|
521
|
+
|
|
522
|
+
awaitStagePendingPrompt(runId: string, stageId: string, promptId: string): Promise<unknown> {
|
|
523
|
+
return new Promise<unknown>((resolve, reject) => {
|
|
524
|
+
const run = findRun(runId);
|
|
525
|
+
if (!run) {
|
|
526
|
+
reject(new Error(`pi-workflows: run "${runId}" not found`));
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
const stage = findStage(run, stageId);
|
|
530
|
+
if (!stage) {
|
|
531
|
+
reject(new Error(`pi-workflows: stage "${stageId}" not found on run "${runId}"`));
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const pending = stage.pendingPrompt;
|
|
535
|
+
if (!pending || pending.id !== promptId) {
|
|
536
|
+
reject(
|
|
537
|
+
new Error(
|
|
538
|
+
`pi-workflows: pending prompt "${promptId}" not registered on stage "${stageId}" in run "${runId}"`,
|
|
539
|
+
),
|
|
540
|
+
);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
_resolvers.set(promptId, { promptId, resolve, reject });
|
|
544
|
+
});
|
|
545
|
+
},
|
|
546
|
+
|
|
419
547
|
recordStageSession(
|
|
420
548
|
runId: string,
|
|
421
549
|
stageId: string,
|
|
@@ -480,7 +608,7 @@ export function createStore(): Store {
|
|
|
480
608
|
if (TERMINAL_STATUSES.has(run.status)) return false;
|
|
481
609
|
const stage = findStage(run, stageId);
|
|
482
610
|
if (!stage) return false;
|
|
483
|
-
if (stage.status
|
|
611
|
+
if (cannotAwaitInput(stage.status)) return false;
|
|
484
612
|
|
|
485
613
|
if (awaiting) {
|
|
486
614
|
if (stage.status === "awaiting_input") return false;
|
|
@@ -502,7 +630,7 @@ export function createStore(): Store {
|
|
|
502
630
|
if (TERMINAL_STATUSES.has(run.status)) return false;
|
|
503
631
|
const stage = findStage(run, stageId);
|
|
504
632
|
if (!stage) return false;
|
|
505
|
-
if (stage.status
|
|
633
|
+
if (cannotBlock(stage.status)) return false;
|
|
506
634
|
if (stage.status === "blocked") {
|
|
507
635
|
if (stage.blockedByStageId === blockedBy) return false;
|
|
508
636
|
stage.blockedByStageId = blockedBy;
|
|
@@ -547,7 +675,7 @@ export function createStore(): Store {
|
|
|
547
675
|
if (TERMINAL_STATUSES.has(run.status)) return false;
|
|
548
676
|
const stage = findStage(run, stageId);
|
|
549
677
|
if (!stage) return false;
|
|
550
|
-
if (stage.status
|
|
678
|
+
if (cannotPause(stage.status)) return false;
|
|
551
679
|
stage.status = "paused";
|
|
552
680
|
stage.pausedAt = pausedAt ?? Date.now();
|
|
553
681
|
stage.resumedAt = undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
interface StageTimerSnapshot {
|
|
2
2
|
readonly startedAt?: number;
|
|
3
|
+
readonly endedAt?: number;
|
|
3
4
|
readonly durationMs?: number;
|
|
4
5
|
readonly pausedDurationMs?: number;
|
|
5
6
|
readonly pausedAt?: number;
|
|
@@ -7,6 +8,7 @@ interface StageTimerSnapshot {
|
|
|
7
8
|
|
|
8
9
|
interface RunTimerSnapshot {
|
|
9
10
|
readonly startedAt: number;
|
|
11
|
+
readonly endedAt?: number;
|
|
10
12
|
readonly durationMs?: number;
|
|
11
13
|
readonly pausedDurationMs?: number;
|
|
12
14
|
readonly pausedAt?: number;
|
|
@@ -39,10 +41,12 @@ export function accumulatePausedDurationMs(
|
|
|
39
41
|
export function elapsedStageMs(stage: StageTimerSnapshot, now = Date.now()): number | undefined {
|
|
40
42
|
if (stage.durationMs !== undefined) return nonNegative(stage.durationMs);
|
|
41
43
|
if (stage.startedAt === undefined) return undefined;
|
|
42
|
-
|
|
44
|
+
const effectiveNow = stage.endedAt ?? now;
|
|
45
|
+
return elapsedFromStart(stage.startedAt, effectiveNow, stage.pausedDurationMs, stage.pausedAt);
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
export function elapsedRunMs(run: RunTimerSnapshot, now = Date.now()): number {
|
|
46
49
|
if (run.durationMs !== undefined) return nonNegative(run.durationMs);
|
|
47
|
-
|
|
50
|
+
const effectiveNow = run.endedAt ?? now;
|
|
51
|
+
return elapsedFromStart(run.startedAt, effectiveNow, run.pausedDurationMs, run.pausedAt);
|
|
48
52
|
}
|