@bastani/atomic 0.8.21 → 0.8.22-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.
Files changed (235) hide show
  1. package/CHANGELOG.md +40 -9
  2. package/dist/builtin/intercom/broker/broker.ts +3 -3
  3. package/dist/builtin/intercom/config.ts +3 -3
  4. package/dist/builtin/intercom/index.ts +1 -1
  5. package/dist/builtin/intercom/package.json +1 -1
  6. package/dist/builtin/intercom/ui/compose.ts +2 -2
  7. package/dist/builtin/mcp/host-html-template.ts +0 -3
  8. package/dist/builtin/mcp/package.json +1 -1
  9. package/dist/builtin/subagents/CHANGELOG.md +13 -4
  10. package/dist/builtin/subagents/agents/codebase-online-researcher.md +9 -9
  11. package/dist/builtin/subagents/agents/debugger.md +6 -6
  12. package/dist/builtin/subagents/package.json +1 -1
  13. package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +1 -1
  14. package/dist/builtin/subagents/skills/browser-use/SKILL.md +234 -0
  15. package/dist/builtin/subagents/skills/browser-use/references/cdp-python.md +76 -0
  16. package/dist/builtin/subagents/skills/browser-use/references/multi-session.md +92 -0
  17. package/dist/builtin/subagents/skills/subagent/SKILL.md +4 -4
  18. package/dist/builtin/subagents/src/agents/skills.ts +19 -1
  19. package/dist/builtin/subagents/src/extension/index.ts +24 -22
  20. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +7 -1
  21. package/dist/builtin/subagents/src/runs/background/async-execution.ts +23 -7
  22. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +98 -3
  23. package/dist/builtin/subagents/src/runs/background/async-status.ts +3 -1
  24. package/dist/builtin/subagents/src/runs/background/run-status.ts +1 -1
  25. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +3 -0
  26. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +37 -12
  27. package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +15 -15
  28. package/dist/builtin/subagents/src/runs/foreground/execution.ts +26 -2
  29. package/dist/builtin/subagents/src/runs/shared/nested-render.ts +1 -1
  30. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +7 -0
  31. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +28 -1
  32. package/dist/builtin/subagents/src/shared/fast-mode.ts +80 -0
  33. package/dist/builtin/subagents/src/shared/formatters.ts +4 -2
  34. package/dist/builtin/subagents/src/shared/types.ts +4 -2
  35. package/dist/builtin/subagents/src/shared/utils.ts +3 -61
  36. package/dist/builtin/subagents/src/tui/render.ts +303 -157
  37. package/dist/builtin/web-access/package.json +1 -1
  38. package/dist/builtin/workflows/CHANGELOG.md +95 -35
  39. package/dist/builtin/workflows/README.md +228 -41
  40. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +535 -541
  41. package/dist/builtin/workflows/builtin/goal.ts +39 -25
  42. package/dist/builtin/workflows/builtin/open-claude-design.ts +66 -69
  43. package/dist/builtin/workflows/builtin/ralph.ts +21 -21
  44. package/dist/builtin/workflows/package.json +6 -5
  45. package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
  46. package/dist/builtin/workflows/src/extension/background-ui-adapter.ts +2 -2
  47. package/dist/builtin/workflows/src/extension/discovery.ts +25 -146
  48. package/dist/builtin/workflows/src/extension/dispatcher.ts +72 -24
  49. package/dist/builtin/workflows/src/extension/hil-answer-notifications.ts +363 -0
  50. package/dist/builtin/workflows/src/extension/index.ts +690 -352
  51. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +99 -62
  52. package/dist/builtin/workflows/src/extension/render-call.ts +2 -1
  53. package/dist/builtin/workflows/src/extension/render-result.ts +9 -3
  54. package/dist/builtin/workflows/src/extension/renderers.ts +5 -3
  55. package/dist/builtin/workflows/src/extension/runtime.ts +68 -33
  56. package/dist/builtin/workflows/src/extension/status-writer.ts +1 -1
  57. package/dist/builtin/workflows/src/extension/wiring.ts +34 -13
  58. package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +142 -0
  59. package/dist/builtin/workflows/src/extension/workflow-schema.ts +4 -4
  60. package/dist/builtin/workflows/src/index.ts +2 -0
  61. package/dist/builtin/workflows/src/intercom/result-intercom.ts +1 -1
  62. package/dist/builtin/workflows/src/runs/background/runner.ts +6 -4
  63. package/dist/builtin/workflows/src/runs/background/status.ts +45 -21
  64. package/dist/builtin/workflows/src/runs/foreground/executor.ts +624 -52
  65. package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +1 -1
  66. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +80 -24
  67. package/dist/builtin/workflows/src/runs/shared/validate-inputs.ts +61 -24
  68. package/dist/builtin/workflows/src/runs/shared/workflow-runner.ts +32 -10
  69. package/dist/builtin/workflows/src/sdk-surface.ts +6 -0
  70. package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +178 -0
  71. package/dist/builtin/workflows/src/shared/persistence-restore.ts +92 -12
  72. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +21 -3
  73. package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +1 -2
  74. package/dist/builtin/workflows/src/shared/run-visibility.ts +9 -0
  75. package/dist/builtin/workflows/src/shared/schema-introspection.ts +121 -0
  76. package/dist/builtin/workflows/src/shared/serializable.ts +132 -0
  77. package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +91 -9
  78. package/dist/builtin/workflows/src/shared/store-types.ts +31 -3
  79. package/dist/builtin/workflows/src/shared/store.ts +58 -14
  80. package/dist/builtin/workflows/src/shared/types.ts +105 -40
  81. package/dist/builtin/workflows/src/tui/chat-surface-message.ts +129 -13
  82. package/dist/builtin/workflows/src/tui/chat-surface.ts +6 -1
  83. package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +3 -2
  84. package/dist/builtin/workflows/src/tui/graph-canvas.ts +1 -1
  85. package/dist/builtin/workflows/src/tui/graph-view.ts +91 -65
  86. package/dist/builtin/workflows/src/tui/inline-form-card.ts +1 -1
  87. package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +3 -2
  88. package/dist/builtin/workflows/src/tui/inputs-overlay.ts +3 -2
  89. package/dist/builtin/workflows/src/tui/inputs-picker.ts +8 -7
  90. package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +2 -0
  91. package/dist/builtin/workflows/src/tui/node-card.ts +34 -8
  92. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +4 -11
  93. package/dist/builtin/workflows/src/tui/prompt-card.ts +98 -50
  94. package/dist/builtin/workflows/src/tui/session-list.ts +7 -2
  95. package/dist/builtin/workflows/src/tui/session-picker.ts +2 -0
  96. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +226 -55
  97. package/dist/builtin/workflows/src/tui/status-helpers.ts +2 -0
  98. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +37 -158
  99. package/dist/builtin/workflows/src/tui/toast.ts +2 -2
  100. package/dist/builtin/workflows/src/tui/widget.ts +53 -12
  101. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +270 -19
  102. package/dist/builtin/workflows/src/tui/workflow-notice-card.ts +184 -0
  103. package/dist/builtin/workflows/src/workflows/define-workflow.ts +138 -43
  104. package/dist/config.d.ts +9 -0
  105. package/dist/config.d.ts.map +1 -1
  106. package/dist/config.js +45 -0
  107. package/dist/config.js.map +1 -1
  108. package/dist/core/agent-session.d.ts +27 -9
  109. package/dist/core/agent-session.d.ts.map +1 -1
  110. package/dist/core/agent-session.js +196 -17
  111. package/dist/core/agent-session.js.map +1 -1
  112. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  113. package/dist/core/atomic-guide-command.js +2 -2
  114. package/dist/core/atomic-guide-command.js.map +1 -1
  115. package/dist/core/codex-fast-mode.d.ts +36 -0
  116. package/dist/core/codex-fast-mode.d.ts.map +1 -0
  117. package/dist/core/codex-fast-mode.js +117 -0
  118. package/dist/core/codex-fast-mode.js.map +1 -0
  119. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  120. package/dist/core/compaction/branch-summarization.js +1 -1
  121. package/dist/core/compaction/branch-summarization.js.map +1 -1
  122. package/dist/core/compaction/compaction.d.ts.map +1 -1
  123. package/dist/core/compaction/compaction.js +1 -1
  124. package/dist/core/compaction/compaction.js.map +1 -1
  125. package/dist/core/extensions/index.d.ts +4 -1
  126. package/dist/core/extensions/index.d.ts.map +1 -1
  127. package/dist/core/extensions/index.js +1 -0
  128. package/dist/core/extensions/index.js.map +1 -1
  129. package/dist/core/extensions/loader.d.ts +7 -2
  130. package/dist/core/extensions/loader.d.ts.map +1 -1
  131. package/dist/core/extensions/loader.js +23 -8
  132. package/dist/core/extensions/loader.js.map +1 -1
  133. package/dist/core/extensions/reactive-widget.d.ts +58 -0
  134. package/dist/core/extensions/reactive-widget.d.ts.map +1 -0
  135. package/dist/core/extensions/reactive-widget.js +182 -0
  136. package/dist/core/extensions/reactive-widget.js.map +1 -0
  137. package/dist/core/extensions/runner.d.ts.map +1 -1
  138. package/dist/core/extensions/runner.js +1 -0
  139. package/dist/core/extensions/runner.js.map +1 -1
  140. package/dist/core/extensions/types.d.ts +26 -12
  141. package/dist/core/extensions/types.d.ts.map +1 -1
  142. package/dist/core/extensions/types.js.map +1 -1
  143. package/dist/core/messages.d.ts +1 -1
  144. package/dist/core/messages.d.ts.map +1 -1
  145. package/dist/core/messages.js +8 -2
  146. package/dist/core/messages.js.map +1 -1
  147. package/dist/core/model-registry.d.ts +4 -0
  148. package/dist/core/model-registry.d.ts.map +1 -1
  149. package/dist/core/model-registry.js +11 -0
  150. package/dist/core/model-registry.js.map +1 -1
  151. package/dist/core/resource-loader.d.ts +9 -1
  152. package/dist/core/resource-loader.d.ts.map +1 -1
  153. package/dist/core/resource-loader.js +49 -21
  154. package/dist/core/resource-loader.js.map +1 -1
  155. package/dist/core/sdk.d.ts.map +1 -1
  156. package/dist/core/sdk.js +22 -13
  157. package/dist/core/sdk.js.map +1 -1
  158. package/dist/core/session-manager.d.ts +7 -5
  159. package/dist/core/session-manager.d.ts.map +1 -1
  160. package/dist/core/session-manager.js +5 -3
  161. package/dist/core/session-manager.js.map +1 -1
  162. package/dist/core/settings-manager.d.ts +16 -0
  163. package/dist/core/settings-manager.d.ts.map +1 -1
  164. package/dist/core/settings-manager.js +64 -5
  165. package/dist/core/settings-manager.js.map +1 -1
  166. package/dist/core/slash-commands.d.ts.map +1 -1
  167. package/dist/core/slash-commands.js +1 -0
  168. package/dist/core/slash-commands.js.map +1 -1
  169. package/dist/core/system-prompt.d.ts.map +1 -1
  170. package/dist/core/system-prompt.js +7 -4
  171. package/dist/core/system-prompt.js.map +1 -1
  172. package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
  173. package/dist/core/tools/ask-user-question/ask-user-question.js +2 -2
  174. package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -1
  175. package/dist/index.d.ts +4 -3
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +3 -2
  178. package/dist/index.js.map +1 -1
  179. package/dist/main.d.ts +3 -0
  180. package/dist/main.d.ts.map +1 -1
  181. package/dist/main.js +12 -0
  182. package/dist/main.js.map +1 -1
  183. package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -1
  184. package/dist/modes/interactive/chat-input-actions.js.map +1 -1
  185. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  186. package/dist/modes/interactive/components/diff.js +0 -1
  187. package/dist/modes/interactive/components/diff.js.map +1 -1
  188. package/dist/modes/interactive/components/fast-mode-selector.d.ts +27 -0
  189. package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +1 -0
  190. package/dist/modes/interactive/components/fast-mode-selector.js +105 -0
  191. package/dist/modes/interactive/components/fast-mode-selector.js.map +1 -0
  192. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  193. package/dist/modes/interactive/components/footer.js +7 -12
  194. package/dist/modes/interactive/components/footer.js.map +1 -1
  195. package/dist/modes/interactive/components/index.d.ts +1 -0
  196. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  197. package/dist/modes/interactive/components/index.js +1 -0
  198. package/dist/modes/interactive/components/index.js.map +1 -1
  199. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  200. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  201. package/dist/modes/interactive/interactive-mode.js +132 -30
  202. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  203. package/dist/modes/print-mode.d.ts.map +1 -1
  204. package/dist/modes/print-mode.js +53 -6
  205. package/dist/modes/print-mode.js.map +1 -1
  206. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  207. package/dist/modes/rpc/rpc-mode.js +3 -0
  208. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  209. package/docs/compaction.md +1 -1
  210. package/docs/custom-provider.md +2 -2
  211. package/docs/development.md +2 -2
  212. package/docs/docs.json +2 -2
  213. package/docs/extensions.md +18 -13
  214. package/docs/providers.md +5 -1
  215. package/docs/quickstart.md +5 -3
  216. package/docs/rpc.md +5 -5
  217. package/docs/sdk.md +12 -12
  218. package/docs/settings.md +18 -0
  219. package/docs/themes.md +6 -6
  220. package/docs/tui.md +20 -18
  221. package/docs/usage.md +2 -0
  222. package/docs/workflows.md +403 -39
  223. package/examples/extensions/qna.ts +2 -2
  224. package/package.json +4 -4
  225. package/dist/builtin/subagents/skills/playwright-cli/SKILL.md +0 -392
  226. package/dist/builtin/subagents/skills/playwright-cli/references/element-attributes.md +0 -23
  227. package/dist/builtin/subagents/skills/playwright-cli/references/playwright-tests.md +0 -39
  228. package/dist/builtin/subagents/skills/playwright-cli/references/request-mocking.md +0 -87
  229. package/dist/builtin/subagents/skills/playwright-cli/references/running-code.md +0 -241
  230. package/dist/builtin/subagents/skills/playwright-cli/references/session-management.md +0 -225
  231. package/dist/builtin/subagents/skills/playwright-cli/references/spec-driven-testing.md +0 -305
  232. package/dist/builtin/subagents/skills/playwright-cli/references/storage-state.md +0 -275
  233. package/dist/builtin/subagents/skills/playwright-cli/references/test-generation.md +0 -134
  234. package/dist/builtin/subagents/skills/playwright-cli/references/tracing.md +0 -139
  235. package/dist/builtin/subagents/skills/playwright-cli/references/video-recording.md +0 -143
@@ -29,7 +29,7 @@ Adding workflow files under `.atomic/workflows/` (project scope) or `~/.atomic/a
29
29
 
30
30
  ### Workflow lifecycle notifications
31
31
 
32
- Workflow lifecycle notices are enabled by default. They send steer prompts into the main chat/model context when a run completes, fails, or pauses for input. Configure them in the same extension config file:
32
+ Workflow lifecycle notices are enabled by default. They send steer prompts into the main chat/model context when a run completes or fails. Awaiting-input prompts are tracked for dedupe/restore, but they do not wake the main chat agent. Configure lifecycle tracking in the same extension config file:
33
33
 
34
34
  ```json
35
35
  {
@@ -40,7 +40,9 @@ Workflow lifecycle notices are enabled by default. They send steer prompts into
40
40
  }
41
41
  ```
42
42
 
43
- Set `enabled` to `false` to disable all notices, or narrow `notifyOn` to a non-empty list of selected events. Emitted notices use steer delivery and wake an idle model so the lifecycle update enters the model context when it happens.
43
+ Set `enabled` to `false` to disable all lifecycle notices, or narrow `notifyOn` to a non-empty list of selected events. Completion and failure lifecycle notices are emitted for top-level workflow runs, use steer delivery, and wake an idle model so the lifecycle update enters the model context when it happens. Nested child workflow completion/failure is reflected inside the expanded parent graph instead of producing separate top-level completion cards. Awaiting-input states are tracked for dedupe/restore, but workflows do not enqueue main-chat `/workflow connect` cards for them; prompt state remains visible through workflow status/connect surfaces, avoiding stale actionable cards if a prompt resolves while the main chat is streaming.
44
+
45
+ When a stage human-in-the-loop prompt is answered from the workflow TUI/stage chat, workflows also emits a separate display-only `workflows:hil-answer-notice` custom message. It records the answer for user-visible audit, but it does not wake the main agent, enter LLM context, or authorize answering later workflow prompts. Answers sent by the main-chat `workflow` tool do not emit this notice because the tool result already tells the main agent what happened.
44
46
 
45
47
  ---
46
48
 
@@ -49,15 +51,12 @@ Set `enabled` to `false` to disable all notices, or narrow `notifyOn` to a non-e
49
51
  ### Example 1 — Single task
50
52
 
51
53
  ```typescript
52
- import { defineWorkflow } from "@bastani/workflows";
54
+ import { defineWorkflow, Type } from "@bastani/workflows";
53
55
 
54
56
  export default defineWorkflow("summarize-pr")
55
57
  .description("Summarize a pull request in one task.")
56
- .input("pr_url", {
57
- type: "text",
58
- required: true,
59
- description: "URL of the pull request to summarize.",
60
- })
58
+ .input("pr_url", Type.String({ description: "URL of the pull request to summarize." }))
59
+ .output("summary", Type.String({ description: "One-task summary of the pull request." }))
61
60
  .run(async (ctx) => {
62
61
  const summary = await ctx.task("summarize", {
63
62
  prompt: `Summarize the pull request at ${String(ctx.inputs.pr_url)} clearly and concisely.`,
@@ -72,13 +71,14 @@ export default defineWorkflow("summarize-pr")
72
71
  Use `ctx.parallel` for independent specialist work. The aggregator receives the specialist outputs through typed task results instead of manual stage/session plumbing.
73
72
 
74
73
  ```typescript
75
- import { defineWorkflow } from "@bastani/workflows";
74
+ import { defineWorkflow, Type } from "@bastani/workflows";
76
75
 
77
76
  export default defineWorkflow("parallel-research")
78
77
  .description("Scout → three parallel specialists → aggregator.")
79
- .input("topic", { type: "text", required: true, description: "Research topic." })
78
+ .input("topic", Type.String({ description: "Research topic." }))
79
+ .output("summary", Type.String({ description: "Synthesized summary of the specialist reports." }))
80
80
  .run(async (ctx) => {
81
- const { topic } = ctx.inputs as { topic: string };
81
+ const topic = ctx.inputs.topic;
82
82
 
83
83
  const reports = await ctx.parallel([
84
84
  { name: "auth-specialist", task: `Research authentication patterns for: ${topic}` },
@@ -98,11 +98,13 @@ export default defineWorkflow("parallel-research")
98
98
  ### Example 3 — Human-in-the-loop (HIL)
99
99
 
100
100
  ```typescript
101
- import { defineWorkflow } from "@bastani/workflows";
101
+ import { defineWorkflow, Type } from "@bastani/workflows";
102
102
 
103
103
  export default defineWorkflow("review-and-merge")
104
104
  .description("Plan a change, ask for human approval, then execute.")
105
- .input("task", { type: "text", required: true, description: "What to implement." })
105
+ .input("task", Type.String({ description: "What to implement." }))
106
+ .output("status", Type.Optional(Type.String({ description: "Set to \"cancelled\" when the human rejects the plan." })))
107
+ .output("result", Type.Optional(Type.String({ description: "Implementation result when the plan is approved." })))
106
108
  .run(async (ctx) => {
107
109
  const plan = await ctx.task("planner", {
108
110
  prompt: `Create a concise implementation plan for: ${String(ctx.inputs.task)}`,
@@ -120,22 +122,87 @@ export default defineWorkflow("review-and-merge")
120
122
  .compile();
121
123
  ```
122
124
 
125
+ Human input is runtime-only: call `ctx.ui.input`, `ctx.ui.confirm`, `ctx.ui.select`, or `ctx.ui.editor` at the point where the workflow actually needs a decision. No builder-level declaration is required or supported.
126
+
127
+ ### Example 4 — Compose workflows
128
+
129
+ Prefer regular TypeScript module imports for reusable child workflows: import the compiled workflow definition, then pass it directly to `ctx.workflow(workflowDefinition, options)`.
130
+
131
+ ```typescript
132
+ import { defineWorkflow, Type } from "@bastani/workflows";
133
+ import goal from "@bastani/workflows/builtin/goal";
134
+ import sharedResearch from "./shared-research.js";
135
+
136
+ export default defineWorkflow("research-and-synthesize")
137
+ .input("topic", Type.String())
138
+ .output("final", Type.String({ description: "Synthesis of the child research and implementation." }))
139
+ .run(async (ctx) => {
140
+ const child = await ctx.workflow(sharedResearch, {
141
+ inputs: { topic: ctx.inputs.topic },
142
+ });
143
+
144
+ const implementation = await ctx.workflow(goal, {
145
+ inputs: { objective: `Implement improvements based on: ${String(child.outputs.summary)}` },
146
+ });
147
+
148
+ const final = await ctx.task("synthesize", {
149
+ prompt: `Synthesize this research and implementation:\n\n${String(child.outputs.summary)}\n\n${String(implementation.outputs.result)}`,
150
+ });
151
+ return { final: final.text };
152
+ })
153
+ .compile();
154
+ ```
155
+
156
+ The child executes as a nested workflow behind a parent boundary stage named `workflow:<workflow-name>` by default, but user-facing status and graph views flatten it into the parent run. In practice it should feel like inlining the child workflow code: child stages, HIL prompt nodes, and deeper imported children appear in one expanded parent graph, while implementation-owned child run ids stay hidden from top-level `/workflow status` lists. The child still has a run id internally so the graph can attach to, pause, interrupt, resume, or kill live child stages correctly. Inputs are strictly validated against the child workflow before it starts: unknown keys, missing required values, type mismatches, and invalid `select` choices fail before the child body runs. The parent receives the child's declared `.output(...)` outputs on `child.outputs` after those outputs pass their declared runtime type checks.
157
+
158
+ For workflows intended to be called as children, declare `.output(...)` for every non-default field a parent should rely on. `.output(...)` is only the schema/contract: use normal TypeScript in `.run()` to gather values from any stage/task/child workflow and return those keys.
159
+
160
+ **Return convention:** child outputs are return-object keys. Atomic never infers child workflow outputs from stage names, stage order, or the final assistant message. If a parent should read `child.outputs.summary`, the child workflow's `.run()` must both declare `.output("summary", schema)` and return `{ summary }`. `result` is not special and is never added for you: to expose `result`, declare `.output("result", schema)` and return `{ result }` like any other output. Returning a key that is not declared with `.output(...)` fails the run with `atomic-workflows: workflow "<name>" returned undeclared output "<key>"; declare it with .output("<key>", Type....) or remove it from the .run() return` (the child-call variant reports `... child "<alias>" returned undeclared output "<key>" from "<childName>"`).
161
+
162
+ A reusable child module can simply default-export a compiled workflow:
163
+
164
+ ```typescript
165
+ import { defineWorkflow, Type } from "@bastani/workflows";
166
+
167
+ export default defineWorkflow("shared-research")
168
+ .input("topic", Type.String())
169
+ .output("summary", Type.String())
170
+ .run(async (ctx) => {
171
+ const report = await ctx.task("research", {
172
+ prompt: `Research: ${String(ctx.inputs.topic)}`,
173
+ });
174
+ return { summary: report.text };
175
+ })
176
+ .compile();
177
+ ```
178
+
179
+ Builtin workflows are also callable as modules for reuse:
180
+
181
+ ```typescript
182
+ import { deepResearchCodebase, goal, openClaudeDesign, ralph } from "@bastani/workflows/builtin";
183
+ import goalWorkflow from "@bastani/workflows/builtin/goal";
184
+ import openClaudeDesignWorkflow from "@bastani/workflows/builtin/open-claude-design";
185
+ ```
186
+
187
+ Only compiled workflow definitions can be passed to `ctx.workflow(...)`; registry names, strings, and path objects are intentionally not supported for child workflow calls. Missing or invalid module imports fail when the workflow file itself is loaded. A parent receives the child's declared `.output(...)` outputs from the child `.run()` return object. Missing required outputs, schema type mismatches, returning an undeclared output, and non-JSON-serializable returned child values fail the child call before the parent continues.
188
+
123
189
  ### Reusable Git worktrees
124
190
 
125
191
  Use `gitWorktreeDir` when a workflow should run in a reusable Git worktree instead of the invoking checkout. The executor creates the worktree if it is missing, reuses it when it already exists as a same-repository worktree root, defaults workflow `ctx.cwd` to the matching path inside that worktree for `worktreeFromInputs`, and defaults stage/task `cwd` to that worktree path.
126
192
 
127
193
  ```typescript
128
- import { defineWorkflow } from "@bastani/workflows";
194
+ import { defineWorkflow, Type } from "@bastani/workflows";
129
195
 
130
196
  export default defineWorkflow("safe-implementation")
131
197
  .description("Run implementation stages in a reusable worktree.")
132
- .input("task", { type: "text", required: true })
133
- .input("worktree", { type: "string", default: "" })
134
- .input("base_branch", { type: "string", default: "origin/main" })
198
+ .input("task", Type.String())
199
+ .input("worktree", Type.String({ default: "" }))
200
+ .input("base_branch", Type.String({ default: "origin/main" }))
135
201
  .worktreeFromInputs({
136
202
  gitWorktreeDir: "worktree",
137
203
  baseBranch: "base_branch",
138
204
  })
205
+ .output("result", Type.String({ description: "Implementation result text." }))
139
206
  .run(async (ctx) => {
140
207
  const result = await ctx.task("implement", {
141
208
  task: String(ctx.inputs.task),
@@ -180,14 +247,18 @@ For advanced integrations, the SDK also exports `setupGitWorktree(options)`, whi
180
247
 
181
248
  ### Model fallbacks
182
249
 
183
- Stages and high-level task helpers can retry transient provider/model failures with an ordered `fallbackModels` list. The primary `model` is tried first, then each fallback, and finally the current pi-selected model when available. Fallbacks are only used for retryable model/provider failures such as rate limits, quota/auth/provider outages, unavailable models, network timeouts, and 5xx errors — ordinary tool, shell, validation, cancellation, and workflow-code failures are not retried.
250
+ Stages and high-level task helpers can retry transient provider/model failures with an ordered `fallbackModels` list. The primary `model` is tried first, then each fallback, and finally the current Atomic-selected model when available. Fallbacks are only used for retryable model/provider failures such as rate limits, quota/auth/provider outages, unavailable models, network timeouts, and 5xx errors — ordinary tool, shell, validation, cancellation, and workflow-code failures are not retried.
184
251
 
185
252
  ```typescript
186
- import { defineWorkflow } from "@bastani/workflows";
253
+ import { defineWorkflow, Type } from "@bastani/workflows";
187
254
 
188
255
  export default defineWorkflow("fallback-review")
189
256
  .description("Review with a model fallback chain.")
190
- .input("topic", { type: "text", required: true })
257
+ .input("topic", Type.String())
258
+ .output("review", Type.String({ description: "Reviewer output text." }))
259
+ .output("model", Type.String({ description: "Model that produced the review." }))
260
+ .output("attemptedModels", Type.Array(Type.String(), { description: "Models tried, in fallback order." }))
261
+ .output("modelAttempts", Type.Array(Type.Unknown(), { description: "Per-attempt model fallback details." }))
191
262
  .run(async (ctx) => {
192
263
  const review = await ctx.task("reviewer", {
193
264
  prompt: `Review this topic: ${String(ctx.inputs.topic)}`,
@@ -223,9 +294,9 @@ When pi exposes its model registry, workflow runs validate user-specified `model
223
294
  ```typescript
224
295
  import { createRegistry, defineWorkflow } from "@bastani/workflows";
225
296
 
226
- const alpha = defineWorkflow("alpha").run(async () => {}).compile();
227
- const beta = defineWorkflow("beta").run(async () => {}).compile();
228
- const gamma = defineWorkflow("gamma").run(async () => {}).compile();
297
+ const alpha = defineWorkflow("alpha").run(async () => ({})).compile();
298
+ const beta = defineWorkflow("beta").run(async () => ({})).compile();
299
+ const gamma = defineWorkflow("gamma").run(async () => ({})).compile();
229
300
 
230
301
  const registry = createRegistry()
231
302
  .register(alpha)
@@ -237,15 +308,115 @@ registry.all(); // compiled workflow definitions
237
308
  registry.get("alpha"); // compiled workflow definition | undefined
238
309
  ```
239
310
 
240
- ### Input types
311
+ ### Declaring inputs and outputs with TypeBox
312
+
313
+ Inputs and outputs are declared with [TypeBox](https://github.com/sinclairzx81/typebox) schemas. Import `Type` from `@bastani/workflows` (alongside `defineWorkflow`) and pass a schema to `.input(key, schema)` / `.output(key, schema)`. The builder infers precise static types for `ctx.inputs`, the `.run()` return, and `child.outputs` from those schemas, and the runtime validates against them with TypeBox `Value`.
314
+
315
+ **Prefer precise schemas.** A precise schema (`Type.Object({ topic: Type.String(), score: Type.Number() })`, `Type.Array(Type.String())`) gives consumers a precise `Static<>` type and makes runtime validation enforce the real shape. Reserve `Type.Unknown()`, `Type.Any()`, `Type.Array(Type.Unknown())`, and `Type.Object({}, { additionalProperties: true })` for genuinely dynamic data whose shape you cannot know ahead of time.
316
+
317
+ ```typescript
318
+ import { defineWorkflow, Type } from "@bastani/workflows";
319
+
320
+ defineWorkflow("example")
321
+ .input("prompt", Type.String({ description: "Required free-text input." })) // required key -> ctx.inputs.prompt: string
322
+ .input("ref", Type.Optional(Type.String())) // optional key -> string | undefined
323
+ .input("count", Type.Number({ default: 2 })) // defaulted -> required key, ctx.inputs.count: number
324
+ .input("flavor", Type.Union([Type.Literal("a"), Type.Literal("b")], { default: "a" })) // select
325
+ .output("packet", Type.Object({ topic: Type.String(), score: Type.Number() })) // required object output
326
+ .output("note", Type.Optional(Type.String())) // optional output
327
+ .run(async (ctx) => ({ packet: { topic: ctx.inputs.prompt, score: ctx.inputs.count } }))
328
+ .compile();
329
+ ```
330
+
331
+ `Static` and `TSchema` are also re-exported from `@bastani/workflows` for advanced typing.
332
+
333
+ ### Input schema reference
334
+
335
+ | Schema | Picker kind | Notes |
336
+ | ------------------------------------------------------------ | ----------- | ------------------------------------------------ |
337
+ | `Type.String({ default?, description? })` | `text` | Free-form string |
338
+ | `Type.Number({ default?, description? })` | `number` | Finite number |
339
+ | `Type.Integer({ default?, description? })` | `integer` | Integer |
340
+ | `Type.Boolean({ default?, description? })` | `boolean` | True/false toggle |
341
+ | `Type.Union([Type.Literal("a"), Type.Literal("b")], { default? })` | `select` | Enumerated string choices |
342
+ | `Type.Optional(schema)` | — | Makes the key optional (`T \| undefined`) |
343
+
344
+ A required input is any schema that is neither `Type.Optional(...)` nor carries a `default` (a defaulted input is a required key at the type level but optional for the caller to provide). Input validation is strict for named workflow runs and `ctx.workflow(...)` child calls: Atomic rejects unknown keys, missing required values, values whose runtime type does not match the declared schema, and `select` values outside the declared literals. It does not coerce strings like `"3"` into numbers; pass JSON numbers (`count=3`) for `Type.Number()`. `.input(...)` narrows `ctx.inputs` for intellisense: required/defaulted strings are `string`, numbers are `number`, booleans are `boolean`, selects are the literal union, and `Type.Optional(...)` inputs include `undefined`.
345
+
346
+ ### Output types
347
+
348
+ Declare outputs with `.output(key, schema?)` when a workflow result should be part of its runtime contract, especially when another workflow will call it as a child. Lead with the most precise schema you can express — the loose rows at the bottom are last resorts for genuinely dynamic data.
349
+
350
+ | Schema | Runtime value accepted |
351
+ | --------------------------------------------------- | --------------------------------------------------- |
352
+ | `Type.String()` | string |
353
+ | `Type.Number()` | finite number (rejects `NaN`) |
354
+ | `Type.Integer()` | integer |
355
+ | `Type.Boolean()` | boolean |
356
+ | `Type.Union([Type.Literal(...)])` | one of the declared literal strings |
357
+ | `Type.Array(Type.String())` | array of the declared element type (use the real type) |
358
+ | `Type.Object({ topic: Type.String(), ... })` | object matching the declared shape |
359
+ | `Type.Unsafe<T>(runtimeSchema)` | precise static `T`, lenient runtime (escape hatch) |
360
+ | `Type.Array(Type.Unknown())` | any JSON array (last resort, dynamic only) |
361
+ | `Type.Object({}, { additionalProperties: true })` | any JSON object (last resort, dynamic only) |
362
+ | `Type.Unknown()` / `Type.Any()` | any JSON-serializable value (last resort) |
363
+
364
+ Wrap an output schema in `Type.Optional(...)` to make the key optional; an un-wrapped output schema is required. `.run()` must return a JSON-serializable object. Functions, symbols, `undefined` properties, `NaN`, infinite numbers, and non-plain objects (e.g. `Date`) fail validation. Declared outputs are validated before a workflow is marked completed. A required output that is missing fails with `missing output "<key>"`, and a type mismatch fails with `output "<key>" expected <kind>, got <actual>`. A workflow exposes exactly the outputs it declares with `.output(...)`: there is no automatic `result` output, and returning a key that was not declared fails the run with `atomic-workflows: workflow "<name>" returned undeclared output "<key>"; declare it with .output("<key>", Type....) or remove it from the .run() return`. To expose `result`, declare `.output("result", schema)` and return `{ result }`. Child output replay still performs a structured-clone safety check after JSON validation so completed child boundaries can be replayed.
365
+
366
+ #### Why precise schemas
367
+
368
+ A loose schema types the value as `unknown`/`Record<string, unknown>` everywhere it is read and only checks "is this JSON?" at runtime. A precise schema types it exactly and validates the real shape:
369
+
370
+ ```typescript
371
+ // ❌ Loose: child.outputs.report is `unknown`; runtime only checks "is JSON".
372
+ .output("report", Type.Unknown())
373
+
374
+ // ✅ Precise: child.outputs.report is `{ topic: string; score: number; tags: string[] }`,
375
+ // and TypeBox rejects a returned value missing `score` or with a non-number `score`.
376
+ .output("report", Type.Object({
377
+ topic: Type.String(),
378
+ score: Type.Number(),
379
+ tags: Type.Array(Type.String()),
380
+ }))
381
+ ```
382
+
383
+ #### `Type.Unsafe<T>()` escape hatch
384
+
385
+ When you already have a precise TypeScript interface for a deeply-nested serializable value and don't want to hand-write the full TypeBox schema, wrap a permissive runtime schema with `Type.Unsafe<MyInterface>(...)`. The **static** type becomes exactly `MyInterface` (so `ctx.inputs`, the `.run()` return, and `child.outputs` stay precise), while the **runtime** stays as lenient as the wrapped schema:
386
+
387
+ ```typescript
388
+ import { defineWorkflow, Type } from "@bastani/workflows";
389
+
390
+ interface ResearchPacket {
391
+ readonly topic: string;
392
+ readonly score: number;
393
+ readonly sections: readonly { readonly heading: string; readonly body: string }[];
394
+ }
395
+
396
+ export default defineWorkflow("research-packet")
397
+ .input("topic", Type.String())
398
+ // Static type = ResearchPacket; runtime only checks "is a JSON object".
399
+ .output("packet", Type.Unsafe<ResearchPacket>(Type.Object({}, { additionalProperties: true })))
400
+ .run(async (ctx) => {
401
+ const packet: ResearchPacket = {
402
+ topic: ctx.inputs.topic,
403
+ score: 1,
404
+ sections: [{ heading: "overview", body: "…" }],
405
+ };
406
+ return { packet };
407
+ })
408
+ .compile();
409
+ ```
410
+
411
+ Tradeoff: `Type.Unsafe<T>()` does not deeply validate at runtime — it trusts the produced value matches `T`. Use it when the producing code already guarantees the shape; when you can express the shape directly, prefer a real `Type.Object(...)`/`Type.Array(...)` so runtime validation also catches drift. Keep bare `Type.Unknown()` and loose `additionalProperties` objects for genuinely dynamic data.
412
+
413
+ #### How types flow
241
414
 
242
- | Type | Description | Extra options |
243
- | --------- | ------------------ | ------------------------------------------ |
244
- | `text` | Free-form string | `default`, `required` |
245
- | `string` | Alias for `text` | `default`, `required` |
246
- | `number` | Numeric value | `default`, `required` |
247
- | `boolean` | True/false toggle | `default`, `required` |
248
- | `select` | Enumerated choices | `choices: string[]`, `default`, `required` |
415
+ - `ctx.inputs.x` is `Static<inputSchema>` — required/defaulted inputs are present, `Type.Optional(...)` adds `| undefined`.
416
+ - The `.run()` return is checked against declared outputs at compile time (missing-required and wrong-type are TypeScript errors) and at runtime via TypeBox `Value` (undeclared keys rejected, declared shape enforced recursively).
417
+ - `ctx.workflow(child).outputs` is typed from the child's declared `.output(...)` contract, so a parent reads precisely-typed child outputs without casting.
418
+
419
+ `Static` and `TSchema` are re-exported from `@bastani/workflows`; use `Static<typeof schema>` when you need a schema's inferred TypeScript type directly.
249
420
 
250
421
  ---
251
422
 
@@ -265,21 +436,25 @@ registry.get("alpha"); // compiled workflow definition | undefined
265
436
  | `/workflow interrupt [run-id\|--all]` | Pause active/named/all active runs so they can resume |
266
437
  | `/workflow kill [run-id\|--all]` | Kill in-flight workflow runs; killed runs are retained for inspection |
267
438
  | `/workflow resume <run-id>` | Resume paused work or re-open a run snapshot |
268
- | `/workflow reload` | Reload discovered workflow resources in-process |
439
+ | `/workflow reload` | Reload discovered workflow resources and package-manifest entries in-process |
269
440
  | `/workflow inputs <name>` | Print the input schema for a workflow |
270
441
 
271
- Input overrides are bare `key=value` tokens (no leading `--`). Values are JSON-parsed when possible, so numbers, booleans, and quoted strings work as expected (e.g. `count=3`, `flag=true`, `prompt="multi word value"`). A whole-object override can be passed as a single JSON token (e.g. `{"prompt":"...","count":3}`).
442
+ Input overrides are bare `key=value` tokens (no leading `--`). Values are JSON-parsed when possible, so numbers, booleans, and quoted strings work as expected (e.g. `count=3`, `flag=true`, `prompt="multi word value"`). A whole-object override can be passed as a single JSON token (e.g. `{"prompt":"...","count":3}`). Runtime validation is strict: unknown input keys, missing required values, type mismatches, and invalid `select` choices fail before a named workflow run starts.
443
+
444
+ Workflows always run as **background tasks** in interactive sessions — the chat editor stays free while a run executes. Press **F2** (or `/workflow connect <run-id>`) to attach to the live graph viewer; HIL prompts (`ctx.ui.input/confirm/select/editor`) appear as awaiting-input graph nodes. Press Enter on the node to answer locally, never as a modal dialog over the chat. Human input is detected when those runtime `ctx.ui.*` calls execute; workflows no longer have a declaration-time HIL flag.
272
445
 
273
- Workflows always run as **background tasks** the chat editor stays free while a run executes. Press **F2** (or `/workflow connect <run-id>`) to attach to the live graph viewer; HIL prompts (`ctx.ui.input/confirm/select/editor`) appear as awaiting-input graph nodes. Press Enter on the node to answer locally, never as a modal dialog over the chat.
446
+ Nested `ctx.workflow(...)` calls are displayed as an expanded graph within the top-level run. `/workflow status` and run pickers list only top-level user-launched workflows, not implementation-owned child runs. `/workflow stages`, `/workflow stage`, `/workflow transcript`, `send`, `pause`, `interrupt`, and `resume` can still target visible child stage ids, prefixes, or names from the expanded graph; Atomic routes the control action to the owning nested run internally.
274
447
 
275
448
  Prompt answer replay is live-memory only. `StageSnapshot.promptAnswerState` reports whether continuation can replay a prompt answer (`available`), must ask again because the private ledger entry is gone (`unavailable`), or must ask again because multiple matching prompt nodes are ambiguous (`ambiguous`). Raw answers stay in a private `PromptAnswerRecord` ledger, are never serialized to snapshots or persistence, and remain resident in memory until the answer is cleared, the run is removed, or the store is cleared. Replay keys include prompt kind, message text, select choices, input/editor initial value, and hashed author callsite, so changing any of those inputs may intentionally re-ask on continuation. Empty `ctx.ui.select(..., [])` calls throw before creating a prompt node.
276
449
 
277
450
  ### `workflow` tool (LLM-callable)
278
451
 
452
+ <!-- Keep the description below in sync with WORKFLOW_TOOL_DESCRIPTION in packages/workflows/src/extension/index.ts; integration tests assert this. -->
453
+
279
454
  ```json
280
455
  {
281
456
  "name": "workflow",
282
- "description": "Run a defined multi-stage workflow by name.",
457
+ "description": "Run named workflows or direct one-off task/tasks/chain workflows; discover with list/get/inputs, inspect status/stages/stage details, send prompt answers or steering, pause/resume/interrupt/kill runs, and reload workflow resources. For transcripts, prefer status/stages/stage to get sessionFile/transcriptPath, quote the exact path without rewriting separators (Windows backslashes are valid), search it with rg/grep, and read small ranges; transcript defaults to at most 5 recent entries and explicit tail/limit overrides that preview.",
283
458
  "parameters": {
284
459
  "workflow": "string (optional) — workflow ID or normalized name",
285
460
  "inputs": "object (optional) — key/value map of workflow inputs",
@@ -288,9 +463,9 @@ Prompt answer replay is live-memory only. `StageSnapshot.promptAnswerState` repo
288
463
  "stageId": "optional stage id, prefix, or name for stage-scoped actions; cannot be combined with all:true",
289
464
  "statusFilter": "optional stages filter: pending/running/awaiting_input/paused/blocked/completed/failed/skipped/all",
290
465
  "format": "optional agent-facing output format: text or json",
291
- "limit": "transcript-only maximum number of most recent entries; default 50",
292
- "tail": "transcript-only last-N entry count; overrides limit",
293
- "includeToolOutput": "transcript-only flag for snapshot tool-event output; live transcripts may not expose tool output",
466
+ "limit": "transcript-only explicit maximum number of recent entries; omitted with tail omitted uses the default 5-entry preview plus metadata/path",
467
+ "tail": "transcript-only explicit last-N entry count; overrides limit for quick recent-context checks",
468
+ "includeToolOutput": "transcript-only flag for explicit snapshot tool-event output; prefer rg/grep on the exact quoted sessionFile/transcriptPath for large outputs",
294
469
  "text": "optional string payload for send/resume; explicit empty text answers pending prompts",
295
470
  "response": "optional structured payload for answering pending prompts; explicit empty response is valid",
296
471
  "message": "optional string payload for send/resume when text is not provided",
@@ -305,7 +480,7 @@ Prompt answer replay is live-memory only. `StageSnapshot.promptAnswerState` repo
305
480
 
306
481
  - **`renderCall`** — renders a compact workflow call summary in the chat scroll.
307
482
  - **`renderResult`** — renders the result or dispatch banner; live progress continues through the widget and graph viewer. Named workflow runs are background-oriented.
308
- - **`transcript`** — uses a registered live stage handle when one exists, even before live messages arrive; otherwise it falls back to stored stage snapshots. Snapshot entries are ordered chronologically before `tail`/`limit` is applied, with terminal result/error entries kept after tool entries when timestamps are missing or tied. `includeToolOutput` applies to snapshot tool events; live session transcripts may not expose tool output.
483
+ - **`transcript`** — reference-first with a small preview by default: use `status`, `stages`, or `stage` to identify the stage and its `sessionFile`/`transcriptPath`, quote the exact path without changing platform separators (for example, preserve Windows backslashes), then search that file with `rg`/`grep` for targeted terms and read only small surrounding ranges. Text results include JSON-escaped `sessionFileJson`/`transcriptPathJson` lines for copy-safe path literals plus up to 5 recent entries by default. Passing explicit `tail` or `limit` overrides that preview for quick context checks. A registered live stage handle is used when one exists, even before live messages arrive; otherwise the action falls back to stored stage snapshots. Snapshot entries are ordered chronologically before `tail`/`limit` is applied, with terminal result/error entries kept after tool entries when timestamps are missing or tied. `includeToolOutput` applies to snapshot tool-event results; live session transcripts may not expose tool output.
309
484
  - **`send`** — answers pending stage prompts only when `text`, `response`, or `message` is present; an explicit empty string is a valid answer, while an omitted payload is a no-op. `delivery: "auto"` answers pending prompts first, then resumes paused stages, steers streaming stages, or queues a follow-up.
310
485
  - **`reload`** — refreshes workflow resources directly in-process instead of queuing a literal `/workflow reload` chat follow-up.
311
486
 
@@ -317,7 +492,11 @@ Press **F2** while a workflow is running to open the DAG overlay for the active
317
492
 
318
493
  `@bastani/workflows` follows pi's package/extension model: pi loads `src/extension/index.ts` from the package `pi.extensions` manifest, then the extension registers the `workflow` tool, `/workflow` slash command, renderers, widget, and lifecycle hooks in-process.
319
494
 
320
- For interactive use, run workflows through `/workflow <name> [key=value ...]` or let the LLM call the `workflow` tool. Both the `/workflow` command and the `workflow` tool are disabled in non-interactive (`-p` / `--print` / `--mode json`) sessions, which bind a no-op UI surface and therefore cannot drive workflow pickers, the graph overlay, or human-in-the-loop prompts. For library or scripted use, call the explicit programmatic runner instead:
495
+ For interactive use, run workflows through `/workflow <name> [key=value ...]` or let the LLM call the `workflow` tool. In non-interactive (`-p` / `--print` / `--mode json`) sessions, `/workflow <name> key=value` and LLM calls to the `workflow` tool remain available for deterministic workflows. The input picker and graph picker are disabled, top-level `ctx.ui.*` is unavailable, and stage child sessions exclude `ask_user_question`. Named workflow dispatch waits for the terminal run snapshot before returning.
496
+
497
+ Because human input is runtime-only and workflows no longer carry a declaration-time HIL marker, headless dispatch does not reject a workflow just because its source contains `ctx.ui.*`. If you copy the HIL example above into a non-interactive session, it can pass dispatch and then fail when execution reaches the prompt with an error such as `atomic-workflows: HIL ctx.ui.confirm is unavailable because Atomic runtime did not provide a UI adapter` (the primitive name varies). Run those workflows interactively, or guard/remove runtime `ctx.ui.*` calls before using headless mode.
498
+
499
+ For library or scripted use, you can also call the explicit programmatic runner:
321
500
 
322
501
  ```ts
323
502
  import { runWorkflow, type WorkflowOptions } from "@bastani/workflows";
@@ -375,10 +554,12 @@ Scout + research-history chain → two parallel specialist waves → aggregator.
375
554
  | ----------------- | -------- | -------- | ------- | --------------------------------------------------------- |
376
555
  | `prompt` | `text` | ✓ | — | Research question or topic to investigate. |
377
556
  | `max_partitions` | `number` | — | `100` | Maximum number of codebase partitions to explore. |
378
- | `max_concurrency` | `number` | — | `4` | Maximum number of workflow stages to run concurrently. |
557
+ | `max_concurrency` | `number` | — | `100` | Maximum number of workflow stages to run concurrently. |
379
558
 
380
559
  Final Markdown research documents are written to dated `research/` paths relative to the current working directory, with a numeric suffix if needed to avoid overwriting an existing document. Hidden run artifacts are written under `research/.deep-research-<run-id>/`.
381
560
 
561
+ Child workflow outputs: `result`, `findings`, `research_doc_path`, `artifact_dir`, `manifest_path`, `partitions`, `explorer_count`, `specialist_count`, `max_concurrency`, and `history`.
562
+
382
563
  ### `goal`
383
564
 
384
565
  Goal Runner workflow: initialize a persisted goal ledger with a per-run goal id and lifecycle events, render goal-continuation context, run bounded worker LM turns, append receipts, run three independent reviewers, and let a TypeScript reducer decide `complete`, `continue`, `blocked`, or `needs_human`. Token budget behavior is intentionally excluded.
@@ -395,6 +576,8 @@ Goal Runner workflow: initialize a persisted goal ledger with a per-run goal id
395
576
 
396
577
  `goal` defaults to 10 worker/review turns. Reviewer quorum is fixed internally at 2 reviewer `complete` votes. The repeated-blocker threshold defaults to 3 consecutive same-blocker turns and is clamped to `max_turns` when you run fewer than 3 turns.
397
578
 
579
+ Child workflow outputs: `result`, `status`, `approved`, `goal_id`, `objective`, `ledger_path`, `turns_completed`, `iterations_completed`, `receipts`, `remaining_work`, and `review_report`.
580
+
398
581
  ### `ralph`
399
582
 
400
583
  Plan → orchestrate → simplify → discover → review → PR-handoff workflow: write an RFC-style technical design document under `specs/`, delegate implementation through sub-agents, simplify recent changes, discover review infrastructure, run parallel reviewers, iterate until approval or the loop limit, then prepare a pull-request report.
@@ -410,6 +593,8 @@ Plan → orchestrate → simplify → discover → review → PR-handoff workflo
410
593
  | `base_branch` | `string` | — | `origin/main` | Branch reviewers and PR-prep compare the current delta with; also used to create a missing worktree. |
411
594
  | `git_worktree_dir` | `string` | — | `""` | Optional reusable Git worktree root. Empty runs in the invoking checkout; non-empty values run Ralph stages in the created/reused worktree. |
412
595
 
596
+ Child workflow outputs: `result`, `plan`, `plan_path`, `implementation_notes_path`, `pr_report`, `approved`, `iterations_completed`, and `review_report`.
597
+
413
598
  ### `open-claude-design`
414
599
 
415
600
  Design-system onboarding → reference import → generation → refinement → export/handoff pipeline.
@@ -426,6 +611,8 @@ Design-system onboarding → reference import → generation → refinement →
426
611
  | `design_system` | `text` | — | — | Existing design-system reference / Design.md path. |
427
612
  | `max_refinements` | `number` | — | `3` | Maximum critique/apply refinement iterations. |
428
613
 
614
+ Child workflow outputs: `output_type`, `design_system`, `artifact`, `handoff`, `approved_for_export`, `refinements_completed`, `import_context`, `run_id`, `artifact_dir`, `preview_path`, `preview_file_url`, `spec_path`, and `spec_file_url`. `open-claude-design` has no `result` output; it exposes only the declared fields listed here.
615
+
429
616
  ---
430
617
 
431
618
  ## Custom workflow discovery