@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
@@ -20,17 +20,18 @@
20
20
  * const result = await discoverWorkflows({ cwd: process.cwd(), homeDir: os.homedir() });
21
21
  */
22
22
 
23
- import { existsSync } from "node:fs";
24
23
  import { readdir, stat } from "node:fs/promises";
25
- import { createRequire } from "node:module";
26
24
  import { join, resolve, extname, isAbsolute } from "node:path";
27
- import { CONFIG_DIR_NAMES, getProjectConfigPaths, isBunBinary } from "@bastani/atomic";
28
- import { createJiti } from "jiti/static";
25
+ import { CONFIG_DIR_NAMES, getProjectConfigPaths } from "@bastani/atomic";
29
26
  import type { WorkflowDefinition } from "../shared/types.js";
30
- import * as workflowsSdkSurface from "../sdk-surface.js";
31
27
  import { createRegistry } from "../workflows/registry.js";
32
28
  import type { WorkflowRegistry } from "../workflows/registry.js";
33
29
  import * as bundledManifest from "../../builtin/index.js";
30
+ import {
31
+ collectWorkflowModuleCandidates,
32
+ loadWorkflowModule,
33
+ validateWorkflowDefinitionShape as validateDefinitionShape,
34
+ } from "./workflow-module-loader.js";
34
35
 
35
36
  // ---------------------------------------------------------------------------
36
37
  // Public types
@@ -148,37 +149,13 @@ export interface DiscoveryResult {
148
149
  // Internal helpers
149
150
  // ---------------------------------------------------------------------------
150
151
 
151
- /**
152
- * Validate a candidate value as a WorkflowDefinition by shape only.
153
- *
154
- * Discovery intentionally does not invoke workflow run functions: user-authored
155
- * run bodies may perform filesystem, network, or other side effects before the
156
- * first ctx.stage()/ctx.task()/ctx.chain()/ctx.parallel() call. Runtime empty
157
- * graph validation remains the authoritative guard that a workflow creates at
158
- * least one stage when it is actually invoked.
159
- *
160
- * Returns null when valid, or a human-readable rejection reason string.
161
- */
162
- function validateDefinitionShape(value: unknown): string | null {
163
- if (value === null || typeof value !== "object") {
164
- return "export is not an object";
165
- }
166
- const d = value as Record<string, unknown>;
167
-
168
- if (d["__piWorkflow"] !== true) {
169
- return "missing or incorrect __piWorkflow sentinel (expected true)";
170
- }
171
- if (typeof d["name"] !== "string" || (d["name"] as string).trim().length === 0) {
172
- return "name must be a non-empty string";
173
- }
174
- if (typeof d["normalizedName"] !== "string" || (d["normalizedName"] as string).trim().length === 0) {
175
- return "normalizedName must be a non-empty string";
176
- }
177
- if (typeof d["run"] !== "function") {
178
- return "run must be a function";
179
- }
180
- return null;
181
- }
152
+ type WorkflowModuleCandidateRecord = {
153
+ readonly value: unknown;
154
+ readonly exportKey: string;
155
+ readonly kind: DiscoveryKind;
156
+ readonly filePath?: string;
157
+ readonly configuredName?: string;
158
+ };
182
159
 
183
160
  /**
184
161
  * Validate DiscoveryConfig shape.
@@ -211,7 +188,7 @@ function validateConfig(config: unknown): string | null {
211
188
 
212
189
  /** Merge a batch of candidates into registry state, first-seen wins. */
213
190
  async function applyBatch(
214
- candidates: Array<{ value: unknown; exportKey: string; kind: DiscoveryKind; filePath?: string; configuredName?: string }>,
191
+ candidates: WorkflowModuleCandidateRecord[],
215
192
  registry: WorkflowRegistry,
216
193
  sources: DiscoverySource[],
217
194
  diagnostics: DiscoveryDiagnostic[],
@@ -255,7 +232,7 @@ async function applyBatch(
255
232
 
256
233
  /** Merge bundled startup candidates with shape-only validation to keep startup seeding synchronous. */
257
234
  function applyBatchShapeOnly(
258
- candidates: Array<{ value: unknown; exportKey: string; kind: DiscoveryKind; filePath?: string; configuredName?: string }>,
235
+ candidates: WorkflowModuleCandidateRecord[],
259
236
  registry: WorkflowRegistry,
260
237
  sources: DiscoverySource[],
261
238
  diagnostics: DiscoveryDiagnostic[],
@@ -312,98 +289,11 @@ async function scanWorkflowDir(dir: string): Promise<string[] | null> {
312
289
  }
313
290
  }
314
291
 
315
- /** Dynamically import a file and extract all WorkflowDefinition candidates.
316
- *
317
- * Strategy: try the default export first, then every named export.
318
- * Both are collected — a file may export multiple workflow definitions.
319
- * jiti loads package-authored .ts/.js/.mjs/.cjs files with the same
320
- * @bastani/workflows authoring import that project/user workflow files use.
321
- */
322
- type RunWorkflowFunction = typeof import("../runs/shared/workflow-runner.js").runWorkflow;
323
-
324
- const runWorkflow: RunWorkflowFunction = async (...args) => {
325
- const { runWorkflow: actualRunWorkflow } = await import("../runs/shared/workflow-runner.js");
326
- return actualRunWorkflow(...args);
327
- };
328
-
329
- const require = createRequire(import.meta.url);
330
- const WORKFLOWS_MODULE_SPECIFIER = "@bastani/workflows";
331
- // Keep this in sync with index.ts through sdk-surface.ts. runWorkflow stays as
332
- // a lazy wrapper because the public re-export comes from workflow-runner.ts,
333
- // which imports this discovery module and would otherwise reintroduce a cycle.
334
- const WORKFLOWS_SDK_MODULE: Record<string, unknown> = {
335
- ...workflowsSdkSurface,
336
- runWorkflow,
337
- };
338
- const WORKFLOWS_VIRTUAL_MODULES: Record<string, unknown> = {
339
- [WORKFLOWS_MODULE_SPECIFIER]: WORKFLOWS_SDK_MODULE,
340
- };
341
-
342
- function resolveWorkflowsSdkAlias(): string {
343
- // Resolve the package self-reference through package.json exports instead of
344
- // pinning discovery.ts to the current src/extension -> src/index.ts layout.
345
- const sdkEntry = require.resolve(WORKFLOWS_MODULE_SPECIFIER);
346
- if (!existsSync(sdkEntry)) {
347
- throw new Error(
348
- `Unable to resolve ${WORKFLOWS_MODULE_SPECIFIER} SDK entry at ${sdkEntry}. ` +
349
- "Check the package exports map for the workflows SDK entry.",
350
- );
351
- }
352
- return sdkEntry;
353
- }
354
-
355
- const workflowModuleLoader = createJiti(import.meta.url, {
356
- moduleCache: false,
357
- // Keep workflow-file import semantics deterministic: jiti owns .ts/.js/.mjs/.cjs
358
- // resolution instead of handing some imports back to native import().
359
- tryNative: false,
360
- ...(isBunBinary
361
- ? { virtualModules: WORKFLOWS_VIRTUAL_MODULES }
362
- : { alias: { [WORKFLOWS_MODULE_SPECIFIER]: resolveWorkflowsSdkAlias() } }),
363
- });
364
-
365
- function materializeModuleObject(mod: object): Record<string, unknown> {
366
- const materialized: Record<string, unknown> = {};
367
-
368
- // jiti's callable API can return an interop namespace proxy. Its own property
369
- // descriptors contain the authored export values, but property access may apply
370
- // default-export conveniences (and even expose a throwing inherited `then`
371
- // getter for `export default null`). Copy own descriptors into a plain object
372
- // so candidate collection sees the exact authored exports.
373
- for (const key of Object.getOwnPropertyNames(mod)) {
374
- const descriptor = Object.getOwnPropertyDescriptor(mod, key);
375
- if (descriptor === undefined) continue;
376
-
377
- const value = "value" in descriptor ? descriptor.value : descriptor.get?.call(mod);
378
- Object.defineProperty(materialized, key, {
379
- value,
380
- enumerable: descriptor.enumerable,
381
- configurable: true,
382
- writable: true,
383
- });
384
- }
385
-
386
- return materialized;
387
- }
388
-
389
- function normalizeWorkflowModule(mod: unknown): Record<string, unknown> {
390
- if (mod !== null && typeof mod === "object") {
391
- return materializeModuleObject(mod);
392
- }
393
- // CJS/default interop can return the exported value directly; wrap it so the
394
- // candidate collector can handle it the same way as an ESM default export.
395
- return { default: mod };
396
- }
397
-
398
- function loadWorkflowModule(filePath: string): Record<string, unknown> {
399
- return normalizeWorkflowModule(workflowModuleLoader(filePath));
400
- }
401
-
402
292
  async function importWorkflowFile(
403
293
  filePath: string,
404
294
  kind: DiscoveryKind,
405
295
  diagnostics: DiscoveryDiagnostic[],
406
- ): Promise<Array<{ value: unknown; exportKey: string; kind: DiscoveryKind; filePath: string }>> {
296
+ ): Promise<WorkflowModuleCandidateRecord[]> {
407
297
  let mod: Record<string, unknown>;
408
298
  try {
409
299
  mod = loadWorkflowModule(filePath);
@@ -417,22 +307,11 @@ async function importWorkflowFile(
417
307
  return [];
418
308
  }
419
309
 
420
- const candidates: Array<{ value: unknown; exportKey: string; kind: DiscoveryKind; filePath: string }> = [];
421
-
422
- // Default export first (RFC §5.12: check mod.default before named exports)
423
- if ("default" in mod && mod["default"] !== undefined) {
424
- candidates.push({ value: mod["default"], exportKey: "default", kind, filePath });
425
- }
426
-
427
- // Then all named exports (a file may export multiple workflow definitions)
428
- for (const [key, val] of Object.entries(mod)) {
429
- if (key === "default") continue;
430
- if (val !== undefined) {
431
- candidates.push({ value: val, exportKey: key, kind, filePath });
432
- }
433
- }
434
-
435
- return candidates;
310
+ return collectWorkflowModuleCandidates(mod).map((candidate) => ({
311
+ ...candidate,
312
+ kind,
313
+ filePath,
314
+ }));
436
315
  }
437
316
 
438
317
  /** Load workflows from a scanned directory. */
@@ -440,11 +319,11 @@ async function loadFromDir(
440
319
  dir: string,
441
320
  kind: DiscoveryKind,
442
321
  diagnostics: DiscoveryDiagnostic[],
443
- ): Promise<Array<{ value: unknown; exportKey: string; kind: DiscoveryKind; filePath: string }>> {
322
+ ): Promise<WorkflowModuleCandidateRecord[]> {
444
323
  const files = await scanWorkflowDir(dir);
445
324
  if (files === null) return [];
446
325
 
447
- const all: Array<{ value: unknown; exportKey: string; kind: DiscoveryKind; filePath: string }> = [];
326
+ const all: WorkflowModuleCandidateRecord[] = [];
448
327
  for (const filePath of files) {
449
328
  const candidates = await importWorkflowFile(filePath, kind, diagnostics);
450
329
  all.push(...candidates);
@@ -458,8 +337,8 @@ async function loadFromPaths(
458
337
  kind: DiscoveryKind,
459
338
  baseCwd: string,
460
339
  diagnostics: DiscoveryDiagnostic[],
461
- ): Promise<Array<{ value: unknown; exportKey: string; kind: DiscoveryKind; filePath: string; configuredName?: string }>> {
462
- const all: Array<{ value: unknown; exportKey: string; kind: DiscoveryKind; filePath: string; configuredName?: string }> = [];
340
+ ): Promise<WorkflowModuleCandidateRecord[]> {
341
+ const all: WorkflowModuleCandidateRecord[] = [];
463
342
 
464
343
  // Normalise to [ { rawPath, configuredName? } ] regardless of input shape
465
344
  const entries: Array<{ rawPath: string; configuredName?: string }> = Array.isArray(pathsOrMap)
@@ -9,14 +9,35 @@
9
9
 
10
10
  import type { WorkflowRegistry } from "../workflows/registry.js";
11
11
  import type { StageAdapters } from "../runs/foreground/stage-runner.js";
12
- import type { Store } from "../shared/store.js";
12
+ import { store as defaultStore, type Store } from "../shared/store.js";
13
13
  import type { CancellationRegistry } from "../runs/background/cancellation-registry.js";
14
- import type { JobTracker } from "../runs/background/job-tracker.js";
15
- import { resolveInputs } from "../runs/foreground/executor.js";
14
+ import { jobTracker as defaultJobTracker, type JobTracker } from "../runs/background/job-tracker.js";
15
+ import { resolveAndValidateInputs } from "../runs/foreground/executor.js";
16
16
  import { runDetached } from "../runs/background/runner.js";
17
17
  import type { WorkflowToolResult, WorkflowInputEntry } from "./render-result.js";
18
+ import { deriveInputFields, schemaIsRequired } from "../shared/schema-introspection.js";
18
19
  import type { WorkflowToolArgs } from "./index.js";
19
- import type { WorkflowPersistencePort, WorkflowMcpPort, WorkflowRuntimeConfig, WorkflowModelCatalogPort } from "../shared/types.js";
20
+ import {
21
+ INTERACTIVE_WORKFLOW_POLICY,
22
+ type WorkflowExecutionPolicy,
23
+ type WorkflowPersistencePort,
24
+ type WorkflowMcpPort,
25
+ type WorkflowRuntimeConfig,
26
+ type WorkflowModelCatalogPort,
27
+ } from "../shared/types.js";
28
+
29
+ type WorkflowRunResult = Extract<WorkflowToolResult, { action: "run" }>;
30
+
31
+ function failedRunResult(name: string | undefined, runId: string, error: string): WorkflowRunResult {
32
+ return {
33
+ action: "run",
34
+ name,
35
+ runId,
36
+ status: "failed",
37
+ error,
38
+ stages: [],
39
+ };
40
+ }
20
41
 
21
42
  // ---------------------------------------------------------------------------
22
43
  // Options
@@ -44,6 +65,10 @@ export interface DispatcherOpts {
44
65
  config?: WorkflowRuntimeConfig;
45
66
  /** Optional model catalog forwarded to workflow runs for fallback resolution. */
46
67
  models?: WorkflowModelCatalogPort;
68
+ /** Runtime-derived interaction policy for this dispatch. */
69
+ policy?: WorkflowExecutionPolicy;
70
+ /** Invocation cwd used for workflow execution. */
71
+ cwd?: string;
47
72
  }
48
73
 
49
74
  // ---------------------------------------------------------------------------
@@ -75,7 +100,7 @@ export async function dispatch(
75
100
  description: def.description,
76
101
  inputs: Object.entries(def.inputs).map(([iname, schema]) => ({
77
102
  name: iname,
78
- required: schema.required === true,
103
+ required: schemaIsRequired(schema),
79
104
  })),
80
105
  }));
81
106
  return { action: "list", items };
@@ -94,16 +119,7 @@ export async function dispatch(
94
119
  error: `Workflow not found: "${name}"`,
95
120
  };
96
121
  }
97
- const inputSchema: WorkflowInputEntry[] = Object.entries(def.inputs).map(
98
- ([iname, schema]) => ({
99
- name: iname,
100
- type: schema.type,
101
- description: schema.description,
102
- required: schema.required,
103
- default: "default" in schema ? schema.default : undefined,
104
- choices: schema.type === "select" ? schema.choices : undefined,
105
- }),
106
- );
122
+ const inputSchema: WorkflowInputEntry[] = deriveInputFields(def.inputs);
107
123
  return { action: "inputs", name, inputs: inputSchema };
108
124
  }
109
125
 
@@ -129,24 +145,24 @@ export async function dispatch(
129
145
  };
130
146
  }
131
147
 
148
+ const policy = opts.policy ?? INTERACTIVE_WORKFLOW_POLICY;
149
+
132
150
  // Pre-validate inputs against the workflow's declared schema. The
133
151
  // executor would otherwise throw the same TypeError deep inside the
134
152
  // background promise — silently from the caller's perspective. Catch
135
153
  // it here so the dispatch result names what's missing.
136
154
  try {
137
- resolveInputs(def.inputs, inputs);
155
+ resolveAndValidateInputs(def.inputs, inputs, `workflow "${def.name}"`);
138
156
  } catch (err) {
139
- return {
140
- action: "run",
141
- name: def.name,
142
- runId: "",
143
- status: "failed",
144
- error: err instanceof Error ? err.message : String(err),
145
- stages: [],
146
- };
157
+ return failedRunResult(
158
+ def.name,
159
+ "",
160
+ err instanceof Error ? err.message : String(err),
161
+ );
147
162
  }
148
163
 
149
164
  const accepted = runDetached(def, inputs, {
165
+ registry: opts.registry,
150
166
  adapters: opts.adapters,
151
167
  store: opts.store,
152
168
  cancellation: opts.cancellation,
@@ -155,7 +171,39 @@ export async function dispatch(
155
171
  mcp: opts.mcp,
156
172
  config: opts.config,
157
173
  models: opts.models,
174
+ executionMode: policy.mode,
175
+ cwd: opts.cwd,
158
176
  });
177
+ if (policy.awaitTerminalRun === true) {
178
+ const tracker = opts.jobs ?? defaultJobTracker;
179
+ const job = tracker.get(accepted.runId);
180
+ if (!job) {
181
+ return failedRunResult(
182
+ accepted.name,
183
+ accepted.runId,
184
+ `Workflow run ${accepted.runId} was accepted but no live job was registered to await`,
185
+ );
186
+ }
187
+ await job.promise;
188
+ const activeStore = opts.store ?? defaultStore;
189
+ const snapshot = activeStore.runs().find((run) => run.id === accepted.runId);
190
+ if (!snapshot) {
191
+ return failedRunResult(
192
+ accepted.name,
193
+ accepted.runId,
194
+ `Workflow run ${accepted.runId} ended without a retained snapshot`,
195
+ );
196
+ }
197
+ return {
198
+ action: "run",
199
+ name: snapshot.name,
200
+ runId: snapshot.id,
201
+ status: snapshot.status,
202
+ result: snapshot.result,
203
+ error: snapshot.error,
204
+ stages: snapshot.stages.map((stage) => structuredClone(stage)),
205
+ };
206
+ }
159
207
  return {
160
208
  action: "run",
161
209
  name: accepted.name,