@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.
Files changed (355) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/host-html-template.ts +1 -1
  4. package/dist/builtin/mcp/init.ts +15 -2
  5. package/dist/builtin/mcp/mcp-callback-server.ts +10 -9
  6. package/dist/builtin/mcp/package.json +1 -1
  7. package/dist/builtin/mcp/ui-session.ts +9 -6
  8. package/dist/builtin/subagents/CHANGELOG.md +8 -1
  9. package/dist/builtin/subagents/README.md +39 -32
  10. package/dist/builtin/subagents/package.json +1 -1
  11. package/dist/builtin/subagents/skills/subagent/SKILL.md +11 -11
  12. package/dist/builtin/subagents/src/agents/agent-management.ts +6 -1
  13. package/dist/builtin/subagents/src/agents/agent-serializer.ts +2 -0
  14. package/dist/builtin/subagents/src/agents/agents.ts +44 -19
  15. package/dist/builtin/subagents/src/extension/config.ts +16 -0
  16. package/dist/builtin/subagents/src/extension/fanout-child.ts +246 -0
  17. package/dist/builtin/subagents/src/extension/index.ts +466 -603
  18. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +6 -4
  19. package/dist/builtin/subagents/src/intercom/result-intercom.ts +109 -1
  20. package/dist/builtin/subagents/src/runs/background/async-execution.ts +124 -19
  21. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +41 -6
  22. package/dist/builtin/subagents/src/runs/background/async-resume.ts +28 -15
  23. package/dist/builtin/subagents/src/runs/background/async-status.ts +60 -30
  24. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +111 -54
  25. package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +83 -0
  26. package/dist/builtin/subagents/src/runs/background/run-status.ts +79 -3
  27. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +46 -1
  28. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +66 -14
  29. package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +10 -3
  30. package/dist/builtin/subagents/src/runs/foreground/execution.ts +14 -2
  31. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +320 -23
  32. package/dist/builtin/subagents/src/runs/shared/completion-guard.ts +23 -1
  33. package/dist/builtin/subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +369 -0
  34. package/dist/builtin/subagents/src/runs/shared/nested-events.ts +935 -0
  35. package/dist/builtin/subagents/src/runs/shared/nested-path.ts +52 -0
  36. package/dist/builtin/subagents/src/runs/shared/nested-render.ts +115 -0
  37. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +1 -0
  38. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +82 -9
  39. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +1 -1
  40. package/dist/builtin/subagents/src/runs/shared/single-output.ts +12 -2
  41. package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +32 -10
  42. package/dist/builtin/subagents/src/runs/shared/worktree.ts +3 -2
  43. package/dist/builtin/subagents/src/shared/artifacts.ts +0 -1
  44. package/dist/builtin/subagents/src/shared/types.ts +96 -1
  45. package/dist/builtin/subagents/src/shared/utils.ts +10 -2
  46. package/dist/builtin/subagents/src/slash/slash-commands.ts +468 -625
  47. package/dist/builtin/subagents/src/tui/render.ts +1227 -2093
  48. package/dist/builtin/web-access/package.json +1 -1
  49. package/dist/builtin/workflows/CHANGELOG.md +24 -0
  50. package/dist/builtin/workflows/README.md +28 -11
  51. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +323 -40
  52. package/dist/builtin/workflows/builtin/ralph.ts +362 -176
  53. package/dist/builtin/workflows/package.json +2 -5
  54. package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
  55. package/dist/builtin/workflows/skills/skill-creator/LICENSE.txt +202 -0
  56. package/dist/builtin/workflows/skills/skill-creator/SKILL.md +489 -0
  57. package/dist/builtin/workflows/skills/skill-creator/agents/analyzer.md +274 -0
  58. package/dist/builtin/workflows/skills/skill-creator/agents/comparator.md +202 -0
  59. package/dist/builtin/workflows/skills/skill-creator/agents/grader.md +223 -0
  60. package/dist/builtin/workflows/skills/skill-creator/assets/eval_review.html +146 -0
  61. package/dist/builtin/workflows/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  62. package/dist/builtin/workflows/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  63. package/dist/builtin/workflows/skills/skill-creator/references/schemas.md +430 -0
  64. package/dist/builtin/workflows/skills/skill-creator/scripts/__init__.py +0 -0
  65. package/dist/builtin/workflows/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  66. package/dist/builtin/workflows/skills/skill-creator/scripts/generate_report.py +326 -0
  67. package/dist/builtin/workflows/skills/skill-creator/scripts/improve_description.py +247 -0
  68. package/dist/builtin/workflows/skills/skill-creator/scripts/package_skill.py +136 -0
  69. package/dist/builtin/workflows/skills/skill-creator/scripts/quick_validate.py +103 -0
  70. package/dist/builtin/workflows/skills/skill-creator/scripts/run_eval.py +310 -0
  71. package/dist/builtin/workflows/skills/skill-creator/scripts/run_loop.py +328 -0
  72. package/dist/builtin/workflows/skills/skill-creator/scripts/utils.py +47 -0
  73. package/dist/builtin/workflows/src/extension/index.ts +869 -93
  74. package/dist/builtin/workflows/src/extension/render-call.ts +34 -1
  75. package/dist/builtin/workflows/src/extension/render-result.ts +126 -21
  76. package/dist/builtin/workflows/src/extension/runtime.ts +91 -3
  77. package/dist/builtin/workflows/src/extension/wiring.ts +38 -12
  78. package/dist/builtin/workflows/src/extension/workflow-schema.ts +62 -5
  79. package/dist/builtin/workflows/src/runs/background/runner.ts +3 -3
  80. package/dist/builtin/workflows/src/runs/background/status.ts +42 -8
  81. package/dist/builtin/workflows/src/runs/foreground/executor.ts +410 -95
  82. package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +5 -2
  83. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +8 -0
  84. package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +6 -4
  85. package/dist/builtin/workflows/src/runs/shared/worktree.ts +3 -2
  86. package/dist/builtin/workflows/src/shared/persistence-restore.ts +138 -5
  87. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +30 -0
  88. package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +78 -120
  89. package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +193 -0
  90. package/dist/builtin/workflows/src/shared/store-types.ts +26 -1
  91. package/dist/builtin/workflows/src/shared/store.ts +145 -17
  92. package/dist/builtin/workflows/src/shared/timing.ts +6 -2
  93. package/dist/builtin/workflows/src/shared/workflow-failures.ts +375 -0
  94. package/dist/builtin/workflows/src/tui/chat-surface.ts +68 -17
  95. package/dist/builtin/workflows/src/tui/connectors.ts +2 -2
  96. package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +24 -26
  97. package/dist/builtin/workflows/src/tui/graph-canvas.ts +4 -8
  98. package/dist/builtin/workflows/src/tui/graph-view.ts +17 -14
  99. package/dist/builtin/workflows/src/tui/header.ts +38 -0
  100. package/dist/builtin/workflows/src/tui/inline-form-card.ts +161 -238
  101. package/dist/builtin/workflows/src/tui/inline-form-editor.ts +68 -73
  102. package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +2 -3
  103. package/dist/builtin/workflows/src/tui/inline-form-store.ts +2 -1
  104. package/dist/builtin/workflows/src/tui/inputs-overlay.ts +1 -3
  105. package/dist/builtin/workflows/src/tui/inputs-picker.ts +286 -399
  106. package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +11 -0
  107. package/dist/builtin/workflows/src/tui/node-card.ts +2 -1
  108. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +9 -1
  109. package/dist/builtin/workflows/src/tui/prompt-card.ts +46 -19
  110. package/dist/builtin/workflows/src/tui/run-detail.ts +63 -80
  111. package/dist/builtin/workflows/src/tui/session-confirm.ts +9 -3
  112. package/dist/builtin/workflows/src/tui/session-picker.ts +19 -16
  113. package/dist/builtin/workflows/src/tui/stage-chat-layout.ts +88 -0
  114. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +368 -879
  115. package/dist/builtin/workflows/src/tui/status-helpers.ts +4 -0
  116. package/dist/builtin/workflows/src/tui/status-list.ts +67 -75
  117. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +50 -12
  118. package/dist/builtin/workflows/src/tui/submit-pane.ts +164 -0
  119. package/dist/builtin/workflows/src/tui/switcher.ts +27 -4
  120. package/dist/builtin/workflows/src/tui/text-helpers.ts +98 -4
  121. package/dist/builtin/workflows/src/tui/widget.ts +90 -68
  122. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +23 -2
  123. package/dist/builtin/workflows/src/tui/workflow-list.ts +44 -68
  124. package/dist/cli/file-processor.d.ts.map +1 -1
  125. package/dist/cli/file-processor.js +2 -3
  126. package/dist/cli/file-processor.js.map +1 -1
  127. package/dist/config.d.ts.map +1 -1
  128. package/dist/config.js +3 -10
  129. package/dist/config.js.map +1 -1
  130. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  131. package/dist/core/agent-session-runtime.js +2 -1
  132. package/dist/core/agent-session-runtime.js.map +1 -1
  133. package/dist/core/agent-session-services.d.ts.map +1 -1
  134. package/dist/core/agent-session-services.js +3 -2
  135. package/dist/core/agent-session-services.js.map +1 -1
  136. package/dist/core/agent-session.d.ts +6 -0
  137. package/dist/core/agent-session.d.ts.map +1 -1
  138. package/dist/core/agent-session.js +16 -2
  139. package/dist/core/agent-session.js.map +1 -1
  140. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  141. package/dist/core/atomic-guide-command.js +8 -9
  142. package/dist/core/atomic-guide-command.js.map +1 -1
  143. package/dist/core/auth-storage.d.ts.map +1 -1
  144. package/dist/core/auth-storage.js +3 -2
  145. package/dist/core/auth-storage.js.map +1 -1
  146. package/dist/core/bash-executor.d.ts.map +1 -1
  147. package/dist/core/bash-executor.js +2 -1
  148. package/dist/core/bash-executor.js.map +1 -1
  149. package/dist/core/export-html/index.d.ts.map +1 -1
  150. package/dist/core/export-html/index.js +8 -6
  151. package/dist/core/export-html/index.js.map +1 -1
  152. package/dist/core/export-html/template.js +6 -3
  153. package/dist/core/extensions/loader.d.ts.map +1 -1
  154. package/dist/core/extensions/loader.js +12 -29
  155. package/dist/core/extensions/loader.js.map +1 -1
  156. package/dist/core/model-registry.d.ts.map +1 -1
  157. package/dist/core/model-registry.js +5 -1
  158. package/dist/core/model-registry.js.map +1 -1
  159. package/dist/core/package-manager.d.ts +8 -0
  160. package/dist/core/package-manager.d.ts.map +1 -1
  161. package/dist/core/package-manager.js +145 -58
  162. package/dist/core/package-manager.js.map +1 -1
  163. package/dist/core/prompt-templates.d.ts.map +1 -1
  164. package/dist/core/prompt-templates.js +6 -20
  165. package/dist/core/prompt-templates.js.map +1 -1
  166. package/dist/core/resource-loader.d.ts.map +1 -1
  167. package/dist/core/resource-loader.js +38 -31
  168. package/dist/core/resource-loader.js.map +1 -1
  169. package/dist/core/sdk.d.ts.map +1 -1
  170. package/dist/core/sdk.js +9 -4
  171. package/dist/core/sdk.js.map +1 -1
  172. package/dist/core/session-manager.d.ts.map +1 -1
  173. package/dist/core/session-manager.js +32 -24
  174. package/dist/core/session-manager.js.map +1 -1
  175. package/dist/core/settings-manager.d.ts.map +1 -1
  176. package/dist/core/settings-manager.js +8 -15
  177. package/dist/core/settings-manager.js.map +1 -1
  178. package/dist/core/skills.d.ts.map +1 -1
  179. package/dist/core/skills.js +8 -22
  180. package/dist/core/skills.js.map +1 -1
  181. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts +5 -4
  182. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
  183. package/dist/core/tools/ask-user-question/state/questionnaire-session.js +34 -11
  184. package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
  185. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts +1 -0
  186. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
  187. package/dist/core/tools/ask-user-question/state/selectors/contract.js.map +1 -1
  188. package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
  189. package/dist/core/tools/ask-user-question/state/selectors/projections.js +1 -0
  190. package/dist/core/tools/ask-user-question/state/selectors/projections.js.map +1 -1
  191. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts +1 -2
  192. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
  193. package/dist/core/tools/ask-user-question/state/state-reducer.js +26 -9
  194. package/dist/core/tools/ask-user-question/state/state-reducer.js.map +1 -1
  195. package/dist/core/tools/ask-user-question/state/state.d.ts +4 -0
  196. package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
  197. package/dist/core/tools/ask-user-question/state/state.js.map +1 -1
  198. package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts +1 -0
  199. package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts.map +1 -1
  200. package/dist/core/tools/ask-user-question/view/components/option-list-view.js +1 -0
  201. package/dist/core/tools/ask-user-question/view/components/option-list-view.js.map +1 -1
  202. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts +9 -6
  203. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
  204. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +28 -7
  205. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
  206. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
  207. package/dist/core/tools/ask-user-question/view/props-adapter.js +4 -1
  208. package/dist/core/tools/ask-user-question/view/props-adapter.js.map +1 -1
  209. package/dist/core/tools/bash.d.ts.map +1 -1
  210. package/dist/core/tools/bash.js +56 -53
  211. package/dist/core/tools/bash.js.map +1 -1
  212. package/dist/core/tools/edit-diff.d.ts +3 -1
  213. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  214. package/dist/core/tools/edit-diff.js +8 -1
  215. package/dist/core/tools/edit-diff.js.map +1 -1
  216. package/dist/core/tools/edit.d.ts +3 -1
  217. package/dist/core/tools/edit.d.ts.map +1 -1
  218. package/dist/core/tools/edit.js +44 -81
  219. package/dist/core/tools/edit.js.map +1 -1
  220. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  221. package/dist/core/tools/file-mutation-queue.js +27 -12
  222. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  223. package/dist/core/tools/find.d.ts.map +1 -1
  224. package/dist/core/tools/find.js +2 -3
  225. package/dist/core/tools/find.js.map +1 -1
  226. package/dist/core/tools/grep.d.ts.map +1 -1
  227. package/dist/core/tools/grep.js +3 -3
  228. package/dist/core/tools/grep.js.map +1 -1
  229. package/dist/core/tools/ls.d.ts.map +1 -1
  230. package/dist/core/tools/ls.js +5 -5
  231. package/dist/core/tools/ls.js.map +1 -1
  232. package/dist/core/tools/output-accumulator.d.ts +2 -0
  233. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  234. package/dist/core/tools/output-accumulator.js +11 -4
  235. package/dist/core/tools/output-accumulator.js.map +1 -1
  236. package/dist/core/tools/path-utils.d.ts +2 -0
  237. package/dist/core/tools/path-utils.d.ts.map +1 -1
  238. package/dist/core/tools/path-utils.js +39 -21
  239. package/dist/core/tools/path-utils.js.map +1 -1
  240. package/dist/core/tools/read.d.ts.map +1 -1
  241. package/dist/core/tools/read.js +9 -8
  242. package/dist/core/tools/read.js.map +1 -1
  243. package/dist/core/tools/truncate.d.ts.map +1 -1
  244. package/dist/core/tools/truncate.js +12 -2
  245. package/dist/core/tools/truncate.js.map +1 -1
  246. package/dist/core/tools/write.d.ts.map +1 -1
  247. package/dist/core/tools/write.js +20 -35
  248. package/dist/core/tools/write.js.map +1 -1
  249. package/dist/index.d.ts +2 -1
  250. package/dist/index.d.ts.map +1 -1
  251. package/dist/index.js +4 -1
  252. package/dist/index.js.map +1 -1
  253. package/dist/main.d.ts.map +1 -1
  254. package/dist/main.js +5 -6
  255. package/dist/main.js.map +1 -1
  256. package/dist/modes/interactive/chat-input-actions.d.ts +24 -0
  257. package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -0
  258. package/dist/modes/interactive/chat-input-actions.js +179 -0
  259. package/dist/modes/interactive/chat-input-actions.js.map +1 -0
  260. package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -0
  261. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  262. package/dist/modes/interactive/components/chat-message-renderer.js +14 -3
  263. package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
  264. package/dist/modes/interactive/components/chat-session-host.d.ts +157 -0
  265. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -0
  266. package/dist/modes/interactive/components/chat-session-host.js +1007 -0
  267. package/dist/modes/interactive/components/chat-session-host.js.map +1 -0
  268. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  269. package/dist/modes/interactive/components/config-selector.js +1 -1
  270. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  271. package/dist/modes/interactive/components/footer.d.ts +1 -0
  272. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  273. package/dist/modes/interactive/components/footer.js +14 -5
  274. package/dist/modes/interactive/components/footer.js.map +1 -1
  275. package/dist/modes/interactive/components/index.d.ts +1 -0
  276. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  277. package/dist/modes/interactive/components/index.js +1 -0
  278. package/dist/modes/interactive/components/index.js.map +1 -1
  279. package/dist/modes/interactive/components/login-dialog.d.ts +9 -1
  280. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  281. package/dist/modes/interactive/components/login-dialog.js +29 -4
  282. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  283. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  284. package/dist/modes/interactive/interactive-mode.js +18 -67
  285. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  286. package/dist/utils/child-process.d.ts +1 -0
  287. package/dist/utils/child-process.d.ts.map +1 -1
  288. package/dist/utils/child-process.js +8 -0
  289. package/dist/utils/child-process.js.map +1 -1
  290. package/dist/utils/clipboard-native.d.ts +3 -1
  291. package/dist/utils/clipboard-native.d.ts.map +1 -1
  292. package/dist/utils/clipboard-native.js +14 -8
  293. package/dist/utils/clipboard-native.js.map +1 -1
  294. package/dist/utils/image-resize-core.d.ts +30 -0
  295. package/dist/utils/image-resize-core.d.ts.map +1 -0
  296. package/dist/utils/image-resize-core.js +124 -0
  297. package/dist/utils/image-resize-core.js.map +1 -0
  298. package/dist/utils/image-resize-worker.d.ts +2 -0
  299. package/dist/utils/image-resize-worker.d.ts.map +1 -0
  300. package/dist/utils/image-resize-worker.js +31 -0
  301. package/dist/utils/image-resize-worker.js.map +1 -0
  302. package/dist/utils/image-resize.d.ts +7 -27
  303. package/dist/utils/image-resize.d.ts.map +1 -1
  304. package/dist/utils/image-resize.js +75 -115
  305. package/dist/utils/image-resize.js.map +1 -1
  306. package/dist/utils/paths.d.ts +16 -1
  307. package/dist/utils/paths.d.ts.map +1 -1
  308. package/dist/utils/paths.js +49 -7
  309. package/dist/utils/paths.js.map +1 -1
  310. package/docs/changelog.mdx +29 -0
  311. package/docs/compaction.md +1 -1
  312. package/docs/custom-provider.md +2 -2
  313. package/docs/development.md +1 -1
  314. package/docs/docs.json +98 -143
  315. package/docs/extensions.md +29 -16
  316. package/docs/favicon.svg +29 -0
  317. package/docs/images/interactive-mode.png +0 -0
  318. package/docs/images/tree-view.png +0 -0
  319. package/docs/images/workflow-command.png +0 -0
  320. package/docs/images/workflow-graph.png +0 -0
  321. package/docs/images/workflow-input-picker.png +0 -0
  322. package/docs/images/workflow-list.png +0 -0
  323. package/docs/index.md +10 -1
  324. package/docs/logo.svg +59 -0
  325. package/docs/packages.md +3 -3
  326. package/docs/providers.md +1 -1
  327. package/docs/quickstart.md +98 -2
  328. package/docs/rpc.md +8 -8
  329. package/docs/sdk.md +23 -12
  330. package/docs/sessions.md +1 -1
  331. package/docs/skills.md +15 -1
  332. package/docs/termux.md +11 -1
  333. package/docs/themes.md +6 -6
  334. package/docs/tui.md +18 -18
  335. package/docs/usage.md +1 -1
  336. package/docs/workflows.md +172 -2
  337. package/examples/extensions/subagent/index.ts +2 -1
  338. package/package.json +6 -6
  339. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/SKILL.md +0 -0
  340. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/element-attributes.md +0 -0
  341. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/playwright-tests.md +0 -0
  342. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/request-mocking.md +0 -0
  343. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/running-code.md +0 -0
  344. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/session-management.md +0 -0
  345. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/spec-driven-testing.md +0 -0
  346. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/storage-state.md +0 -0
  347. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/test-generation.md +0 -0
  348. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/tracing.md +0 -0
  349. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/video-recording.md +0 -0
  350. /package/dist/builtin/{workflows → subagents}/skills/tdd/SKILL.md +0 -0
  351. /package/dist/builtin/{workflows → subagents}/skills/tdd/deep-modules.md +0 -0
  352. /package/dist/builtin/{workflows → subagents}/skills/tdd/interface-design.md +0 -0
  353. /package/dist/builtin/{workflows → subagents}/skills/tdd/mocking.md +0 -0
  354. /package/dist/builtin/{workflows → subagents}/skills/tdd/refactoring.md +0 -0
  355. /package/dist/builtin/{workflows → subagents}/skills/tdd/tests.md +0 -0
@@ -0,0 +1,375 @@
1
+ import type { WorkflowFailureKind } from "./store-types.js";
2
+
3
+ export interface WorkflowFailure {
4
+ readonly kind: WorkflowFailureKind;
5
+ /** Original error text, preserved for diagnostics. */
6
+ readonly message: string;
7
+ /** Sanitized workflow-facing text shown on run/stage snapshots. */
8
+ readonly userMessage: string;
9
+ readonly retryable: boolean;
10
+ readonly resumable: boolean;
11
+ readonly cause?: unknown;
12
+ }
13
+
14
+ export const WORKFLOW_AUTH_FAILURE_MESSAGE =
15
+ "You must be logged in to run workflows. Run /login and try again.";
16
+
17
+ const WORKFLOW_FAILURE_KINDS: ReadonlySet<WorkflowFailureKind> = new Set([
18
+ "auth",
19
+ "rate_limit",
20
+ "provider",
21
+ "cancelled",
22
+ "unknown",
23
+ ]);
24
+
25
+ export function isWorkflowFailureKind(kind: string): kind is WorkflowFailureKind {
26
+ return WORKFLOW_FAILURE_KINDS.has(kind as WorkflowFailureKind);
27
+ }
28
+
29
+ function makeWorkflowFailure(
30
+ kind: WorkflowFailureKind,
31
+ message: string,
32
+ opts: {
33
+ readonly retryable: boolean;
34
+ readonly resumable: boolean;
35
+ readonly cause: unknown;
36
+ readonly userMessage?: string;
37
+ },
38
+ ): WorkflowFailure {
39
+ return {
40
+ kind,
41
+ message,
42
+ userMessage: opts.userMessage ?? message,
43
+ retryable: opts.retryable,
44
+ resumable: opts.resumable,
45
+ cause: opts.cause,
46
+ };
47
+ }
48
+
49
+ function asRecord(value: unknown): Record<string, unknown> | undefined {
50
+ return value !== null && typeof value === "object" ? value as Record<string, unknown> : undefined;
51
+ }
52
+
53
+ function field(value: unknown, key: string): unknown {
54
+ return asRecord(value)?.[key];
55
+ }
56
+
57
+ function stringField(value: unknown, key: string): string | undefined {
58
+ const raw = field(value, key);
59
+ return typeof raw === "string" && raw.length > 0 ? raw : undefined;
60
+ }
61
+
62
+ function errorMessage(error: unknown): string {
63
+ const structuredMessage = structuredErrorMessage(error);
64
+ if (structuredMessage !== undefined) return structuredMessage;
65
+ if (error instanceof Error && typeof error.message === "string") return error.message;
66
+ if (typeof error === "string") return error;
67
+ return String(error);
68
+ }
69
+
70
+ function errorName(error: unknown): string | undefined {
71
+ return error instanceof Error ? error.name : stringField(error, "name");
72
+ }
73
+
74
+ function structuredErrorMessage(error: unknown): string | undefined {
75
+ return stringField(error, "errorMessage")
76
+ ?? stringField(error, "message")
77
+ ?? stringField(error, "statusText");
78
+ }
79
+
80
+ type StructuredSignal = {
81
+ readonly status?: number;
82
+ readonly code?: string | number;
83
+ readonly name?: string;
84
+ readonly stopReason?: string;
85
+ };
86
+
87
+ function integerFrom(value: unknown): number | undefined {
88
+ if (typeof value === "number" && Number.isInteger(value)) return value;
89
+ if (typeof value !== "string" || value.trim().length === 0) return undefined;
90
+ const parsed = Number(value.trim());
91
+ return Number.isInteger(parsed) ? parsed : undefined;
92
+ }
93
+
94
+ function structuredSignal(error: unknown): StructuredSignal {
95
+ const status = integerFrom(field(error, "status"))
96
+ ?? integerFrom(field(error, "statusCode"))
97
+ ?? integerFrom(field(error, "httpStatus"));
98
+ const rawCode = field(error, "code");
99
+ const code = typeof rawCode === "string" || typeof rawCode === "number" ? rawCode : undefined;
100
+ return {
101
+ ...(status !== undefined ? { status } : {}),
102
+ ...(code !== undefined ? { code } : {}),
103
+ ...(errorName(error) !== undefined ? { name: errorName(error)! } : {}),
104
+ ...(stringField(error, "stopReason") !== undefined ? { stopReason: stringField(error, "stopReason")! } : {}),
105
+ };
106
+ }
107
+
108
+ function causeOf(error: unknown): unknown {
109
+ if (error instanceof Error) return error.cause;
110
+ return field(error, "cause");
111
+ }
112
+
113
+ function diagnosticErrors(error: unknown): readonly unknown[] {
114
+ const diagnostics = field(error, "diagnostics");
115
+ if (!Array.isArray(diagnostics)) return [];
116
+ const errors: unknown[] = [];
117
+ for (const diagnostic of diagnostics) {
118
+ const diagnosticError = field(diagnostic, "error");
119
+ errors.push(diagnosticError ?? diagnostic);
120
+ }
121
+ return errors;
122
+ }
123
+
124
+ function normalizeCode(value: string | number | undefined): string | undefined {
125
+ if (value === undefined) return undefined;
126
+ return String(value).trim().toLowerCase().replaceAll("-", "_");
127
+ }
128
+
129
+ function kindFromStatus(status: number | undefined): WorkflowFailureKind | undefined {
130
+ switch (status) {
131
+ case 401:
132
+ case 403:
133
+ return "auth";
134
+ case 429:
135
+ return "rate_limit";
136
+ case 500:
137
+ case 502:
138
+ case 503:
139
+ case 504:
140
+ return "provider";
141
+ default:
142
+ return undefined;
143
+ }
144
+ }
145
+
146
+ function kindFromCode(code: string | number | undefined): WorkflowFailureKind | undefined {
147
+ const normalized = normalizeCode(code);
148
+ switch (normalized) {
149
+ case undefined:
150
+ return undefined;
151
+ case "401":
152
+ case "403":
153
+ case "auth":
154
+ case "auth_required":
155
+ case "authentication_required":
156
+ case "unauthorized":
157
+ case "forbidden":
158
+ case "invalid_api_key":
159
+ case "missing_api_key":
160
+ return "auth";
161
+ case "429":
162
+ case "rate_limit":
163
+ case "rate_limit_exceeded":
164
+ case "too_many_requests":
165
+ case "quota_exceeded":
166
+ return "rate_limit";
167
+ case "aborterror":
168
+ case "aborted":
169
+ case "cancelled":
170
+ case "canceled":
171
+ return "cancelled";
172
+ case "500":
173
+ case "502":
174
+ case "503":
175
+ case "504":
176
+ case "provider_error":
177
+ case "service_unavailable":
178
+ case "temporarily_unavailable":
179
+ case "overloaded":
180
+ return "provider";
181
+ default:
182
+ return undefined;
183
+ }
184
+ }
185
+
186
+ function structuredKind(error: unknown, seen = new Set<unknown>()): WorkflowFailureKind | undefined {
187
+ if (error === undefined || error === null || seen.has(error)) return undefined;
188
+ if (typeof error === "object") seen.add(error);
189
+
190
+ const signal = structuredSignal(error);
191
+ if (signal.stopReason?.toLowerCase() === "aborted") return "cancelled";
192
+ const statusKind = kindFromStatus(signal.status);
193
+ if (statusKind !== undefined) return statusKind;
194
+ const codeKind = kindFromCode(signal.code) ?? kindFromCode(signal.name);
195
+ if (codeKind !== undefined) return codeKind;
196
+
197
+ for (const diagnosticError of diagnosticErrors(error)) {
198
+ const diagnosticKind = structuredKind(diagnosticError, seen);
199
+ if (diagnosticKind !== undefined) return diagnosticKind;
200
+ }
201
+
202
+ return structuredKind(causeOf(error), seen);
203
+ }
204
+
205
+ function failureForKind(kind: WorkflowFailureKind, message: string, cause: unknown): WorkflowFailure {
206
+ switch (kind) {
207
+ case "auth":
208
+ return makeWorkflowFailure("auth", message, {
209
+ userMessage: WORKFLOW_AUTH_FAILURE_MESSAGE,
210
+ retryable: true,
211
+ resumable: true,
212
+ cause,
213
+ });
214
+ case "rate_limit":
215
+ return makeWorkflowFailure("rate_limit", message, {
216
+ retryable: true,
217
+ resumable: true,
218
+ cause,
219
+ });
220
+ case "cancelled":
221
+ return makeWorkflowFailure("cancelled", message, {
222
+ retryable: false,
223
+ resumable: false,
224
+ cause,
225
+ });
226
+ case "provider":
227
+ return makeWorkflowFailure("provider", message, {
228
+ retryable: true,
229
+ resumable: true,
230
+ cause,
231
+ });
232
+ case "unknown":
233
+ return makeWorkflowFailure("unknown", message, {
234
+ retryable: false,
235
+ resumable: true,
236
+ cause,
237
+ });
238
+ }
239
+ }
240
+
241
+ type TokenMatch = readonly string[];
242
+
243
+ function tokenize(value: string): readonly string[] {
244
+ const tokens: string[] = [];
245
+ let current = "";
246
+ for (const char of value.toLowerCase()) {
247
+ if ((char >= "a" && char <= "z") || (char >= "0" && char <= "9")) {
248
+ current += char;
249
+ } else if (current.length > 0) {
250
+ tokens.push(current);
251
+ current = "";
252
+ }
253
+ }
254
+ if (current.length > 0) tokens.push(current);
255
+ return tokens;
256
+ }
257
+
258
+ function hasPhrase(tokens: readonly string[], phrase: TokenMatch): boolean {
259
+ if (phrase.length === 0 || phrase.length > tokens.length) return false;
260
+ for (let index = 0; index <= tokens.length - phrase.length; index += 1) {
261
+ let matched = true;
262
+ for (let offset = 0; offset < phrase.length; offset += 1) {
263
+ if (tokens[index + offset] !== phrase[offset]) {
264
+ matched = false;
265
+ break;
266
+ }
267
+ }
268
+ if (matched) return true;
269
+ }
270
+ return false;
271
+ }
272
+
273
+ function hasAnyPhrase(tokens: readonly string[], phrases: readonly TokenMatch[]): boolean {
274
+ return phrases.some((phrase) => hasPhrase(tokens, phrase));
275
+ }
276
+
277
+ function tokenNearAny(tokens: readonly string[], anchor: string, candidates: ReadonlySet<string>, distance: number): boolean {
278
+ for (let index = 0; index < tokens.length; index += 1) {
279
+ if (tokens[index] !== anchor) continue;
280
+ const start = Math.max(0, index - distance);
281
+ const end = Math.min(tokens.length - 1, index + distance);
282
+ for (let cursor = start; cursor <= end; cursor += 1) {
283
+ if (cursor !== index && candidates.has(tokens[cursor]!)) return true;
284
+ }
285
+ }
286
+ return false;
287
+ }
288
+
289
+ const AUTH_PHRASES: readonly TokenMatch[] = [
290
+ ["no", "api", "key"],
291
+ ["api", "key", "not", "found"],
292
+ ["missing", "api", "key"],
293
+ ["no", "model", "selected"],
294
+ ["no", "models", "available"],
295
+ ["not", "logged", "in"],
296
+ ["log", "in"],
297
+ ["login", "required"],
298
+ ["authentication", "required"],
299
+ ["unauthorized"],
300
+ ];
301
+
302
+ const RATE_LIMIT_PHRASES: readonly TokenMatch[] = [
303
+ ["rate", "limit"],
304
+ ["429"],
305
+ ["quota"],
306
+ ["too", "many", "requests"],
307
+ ];
308
+
309
+ const CANCELLED_PHRASES: readonly TokenMatch[] = [
310
+ ["aborted"],
311
+ ["cancelled"],
312
+ ["canceled"],
313
+ ];
314
+
315
+ const PROVIDER_PHRASES: readonly TokenMatch[] = [
316
+ ["model", "not", "found"],
317
+ ["overloaded"],
318
+ ["temporarily", "unavailable"],
319
+ ["service", "unavailable"],
320
+ ["503"],
321
+ ];
322
+
323
+ const AUTH_CONTEXT = new Set([
324
+ "token",
325
+ "credential",
326
+ "credentials",
327
+ "required",
328
+ "expired",
329
+ "invalid",
330
+ "missing",
331
+ "unauthorized",
332
+ "login",
333
+ "signin",
334
+ ]);
335
+
336
+ const MODEL_PROVIDER_CONTEXT = new Set([
337
+ "unavailable",
338
+ "overloaded",
339
+ "temporarily",
340
+ "service",
341
+ ]);
342
+
343
+ const PROVIDER_CONTEXT = new Set([
344
+ "error",
345
+ "failure",
346
+ "failed",
347
+ "overloaded",
348
+ "unavailable",
349
+ "temporarily",
350
+ "service",
351
+ ]);
352
+
353
+ function fallbackKindFromMessage(message: string, name: string | undefined): WorkflowFailureKind | undefined {
354
+ const tokens = tokenize(message);
355
+ if (hasAnyPhrase(tokens, AUTH_PHRASES) || tokenNearAny(tokens, "oauth", AUTH_CONTEXT, 8)) return "auth";
356
+ if (hasAnyPhrase(tokens, RATE_LIMIT_PHRASES)) return "rate_limit";
357
+ if (name?.toLowerCase() === "aborterror" || hasAnyPhrase(tokens, CANCELLED_PHRASES)) return "cancelled";
358
+ if (
359
+ hasAnyPhrase(tokens, PROVIDER_PHRASES)
360
+ || tokenNearAny(tokens, "model", MODEL_PROVIDER_CONTEXT, 8)
361
+ || tokenNearAny(tokens, "provider", PROVIDER_CONTEXT, 8)
362
+ ) return "provider";
363
+ return undefined;
364
+ }
365
+
366
+ export function classifyWorkflowFailure(error: unknown): WorkflowFailure {
367
+ const message = errorMessage(error);
368
+ const structured = structuredKind(error);
369
+ if (structured !== undefined) return failureForKind(structured, message, error);
370
+
371
+ const fallback = fallbackKindFromMessage(message, errorName(error));
372
+ if (fallback !== undefined) return failureForKind(fallback, message, error);
373
+
374
+ return failureForKind("unknown", message, error);
375
+ }
@@ -82,6 +82,55 @@ export interface FlatBandBadge {
82
82
  fg?: string;
83
83
  }
84
84
 
85
+ export interface RenderRoundedBoxOpts {
86
+ /** Title embedded in the top border. Keep it short; it is truncated to fit. */
87
+ title?: string;
88
+ /** Body lines. ANSI is preserved and each line is width-clamped. */
89
+ bodyLines: readonly string[];
90
+ /** Full box width in visible cells. */
91
+ width?: number;
92
+ /** Optional theme for ANSI border/text chrome. */
93
+ theme?: GraphTheme;
94
+ /** Border/title colour. Defaults to theme.border or plain output. */
95
+ accent?: string;
96
+ }
97
+
98
+ /**
99
+ * Render a width-stable rounded box (`╭╮╰╯`) around arbitrary body lines.
100
+ * This is the shared workflow-tool output container: panels, cards, and
101
+ * compact notices all use the same border vocabulary.
102
+ */
103
+ export function renderRoundedBox(opts: RenderRoundedBoxOpts): string {
104
+ const width = chatWidth(opts.width);
105
+ return renderRoundedBoxLines({ ...opts, width }).join("\n");
106
+ }
107
+
108
+ /** Render a rounded box as individual width-stable lines. */
109
+ export function renderRoundedBoxLines(opts: RenderRoundedBoxOpts & { width: number }): string[] {
110
+ const width = Math.max(MIN_WIDTH, opts.width);
111
+ const inner = Math.max(2, width - 2);
112
+ const theme = opts.theme;
113
+ const border = theme ? hexToAnsi(opts.accent ?? theme.border) : "";
114
+ const reset = theme ? RESET : "";
115
+
116
+ const titleBudget = Math.max(0, inner - 2);
117
+ const titleText = opts.title && opts.title.length > 0
118
+ ? ` ${truncateToWidth(opts.title, titleBudget, ELLIPSIS)} `
119
+ : "";
120
+ const topFill = Math.max(0, inner - visibleWidth(titleText));
121
+ const top = `${border}╭${titleText}${"─".repeat(topFill)}╮${reset}`;
122
+
123
+ const body = opts.bodyLines.length > 0 ? opts.bodyLines : [""];
124
+ const rows = body.map((line) => {
125
+ const clipped = truncateToWidth(line, inner, ELLIPSIS, theme !== undefined);
126
+ const pad = Math.max(0, inner - visibleWidth(clipped));
127
+ return `${border}│${reset}${clipped}${" ".repeat(pad)}${border}│${reset}`;
128
+ });
129
+
130
+ const bottom = `${border}╰${"─".repeat(inner)}╯${reset}`;
131
+ return [top, ...rows, bottom];
132
+ }
133
+
85
134
  export interface RenderFlatBandOpts {
86
135
  /** Pill label, e.g. `"BACKGROUND"`, `"DISPATCHED"`, `"WORKFLOWS"`. */
87
136
  label: string;
@@ -100,8 +149,8 @@ export interface RenderFlatBandOpts {
100
149
  /**
101
150
  * Render one full-width band. Themed mode paints the line with the
102
151
  * `surface0` background and styles the label, subtitle, and badges with
103
- * ANSI colours. Plain mode emits `▎ LABEL subtitle badge` so the
104
- * shape carries through to logs.
152
+ * ANSI colours. Plain mode emits an indented `LABEL subtitle badge`
153
+ * shape so the layout carries through to logs.
105
154
  */
106
155
  export function renderFlatBand(opts: RenderFlatBandOpts): string {
107
156
  const width = chatWidth(opts.width);
@@ -187,8 +236,8 @@ function renderFlatBandPlain(
187
236
  const badgeSeg = badges.map((b) => b.text).join(" ");
188
237
  // Plain-mode band sits 1 cell from the chat content edge, matching the
189
238
  // themed band's leftPad and the card/hint left edges. Single-column
190
- // alignment for the band marker, card stripe, and hint arrow.
191
- const leftMarker = "";
239
+ // alignment for the band, card, and hint arrow.
240
+ const leftMarker = " ";
192
241
  const leftW = visibleWidth(leftMarker);
193
242
  const subtitleW = visibleWidth(subtitleSeg);
194
243
  const badgeW = visibleWidth(badgeSeg);
@@ -239,7 +288,7 @@ export interface RenderTaggedCardOpts {
239
288
  theme?: GraphTheme;
240
289
  }
241
290
 
242
- const STRIPE_CHAR_THEMED = "";
291
+ const STRIPE_CHAR_THEMED = " ";
243
292
  const STRIPE_CHAR_PLAIN = "│";
244
293
 
245
294
  /**
@@ -266,7 +315,7 @@ function renderTaggedCardThemed(
266
315
  const tagFg = hexToAnsi(opts.accent);
267
316
  const text = hexToAnsi(theme.text);
268
317
 
269
- // Row 1:[tag] title … ● running
318
+ // Row 1: [tag] title … ● running
270
319
  const tagSeg = `${tagBg}${tagFg}${BOLD} ${opts.tag} ${RESET}`;
271
320
  const tagW = opts.tag.length + 2;
272
321
  const tagSubtitleSeg = opts.tagSubtitle
@@ -281,14 +330,14 @@ function renderTaggedCardThemed(
281
330
  : "";
282
331
 
283
332
  // Row 1 sits one cell tighter against the stripe than the body rows
284
- // (`▎ [tag] title …`) so that the tag pill's interior text starts at
333
+ // (` [tag] title …`) so that the tag pill's interior text starts at
285
334
  // exactly the same column as every body row's leading character —
286
- // `▎ ` (2 cells) + bg-pill leading pad (1 cell) lands tag text at col 4,
287
- // and `▎ ` (3 cells) + body content also lands at col 4. The +1
335
+ // ` ` (2 cells) + bg-pill leading pad (1 cell) lands tag text at col 4,
336
+ // and ` ` (3 cells) + body content also lands at col 4. The +1
288
337
  // hanging indent on the body is what the mockup's §1 / §2 cards show
289
- // (ui/mockups.html · `▎ [tag] title` over `▎ body`).
290
- const row1StripePrefixW = 2; // ""
291
- const bodyStripePrefixW = 3; // ""
338
+ // (ui/mockups.html · ` [tag] title` over ` body`).
339
+ const row1StripePrefixW = 2; // " "
340
+ const bodyStripePrefixW = 3; // " "
292
341
  const trailingPad = 2;
293
342
  const titleSuffixW = opts.titleSuffix ? Math.max(0, opts.titleSuffixWidth ?? 0) : 0;
294
343
  const titleSuffixGap = opts.titleSuffix ? 2 : 0;
@@ -316,10 +365,10 @@ function renderTaggedCardThemed(
316
365
  width - row1StripePrefixW - tagW - titleSegW - suffixSegW - tagSubtitleW - trailingW - 1,
317
366
  );
318
367
 
319
- // 1-cell leading space on every card line so the stripe `▎` lands at
320
- // column 1 — column-aligned with the band's `[ LABEL ]` (which is itself
321
- // offset by `leftPad` inside its surface0 fill) and with the hint rows'
322
- // `▸` arrow. All three chat-surface families share the same left edge,
368
+ // 1-cell leading space on every card line so the card content is
369
+ // column-aligned with the band's `[ LABEL ]` (which is itself offset by
370
+ // `leftPad` inside its surface0 fill) and with the hint rows' `▸` arrow.
371
+ // All three chat-surface families share the same left edge,
323
372
  // matching the mockup's terminal-padding scheme.
324
373
  const row1 =
325
374
  ` ${stripe}${STRIPE_CHAR_THEMED}${RESET} ` +
@@ -455,6 +504,7 @@ function stageGlyph(status: StageStatus): string {
455
504
  case "completed": return "✓";
456
505
  case "running": return "●";
457
506
  case "failed": return "✗";
507
+ case "skipped": return "⊘";
458
508
  case "pending":
459
509
  default: return "○";
460
510
  }
@@ -465,6 +515,7 @@ function stageColor(status: StageStatus, theme: GraphTheme): string {
465
515
  case "completed": return hexToAnsi(theme.success);
466
516
  case "running": return hexToAnsi(theme.warning);
467
517
  case "failed": return hexToAnsi(theme.error);
518
+ case "skipped": return hexToAnsi(theme.dim);
468
519
  case "pending":
469
520
  default: return hexToAnsi(theme.dim);
470
521
  }
@@ -492,7 +543,7 @@ export interface HintRow {
492
543
  export function renderHintRows(rows: readonly HintRow[], theme?: GraphTheme): string {
493
544
  if (rows.length === 0) return "";
494
545
  // 1-cell leading space so the `▸` arrow column-aligns with the band's
495
- // `[ LABEL ]` opening bracket and the card stripe `▎`. All three share
546
+ // `[ LABEL ]` opening bracket and the card content. All three share
496
547
  // column 1 — see renderTaggedCardThemed for the alignment contract.
497
548
  const indent = " ";
498
549
  if (!theme) {
@@ -38,7 +38,7 @@ export function buildMergeConnector(fromXs: number[], toX: number): ConnectorRes
38
38
 
39
39
  // Line 0: top row connecting sources with ─, ┬ at source positions
40
40
  // Line 1: vertical drop │ from each source down to target row
41
- // Line 2: bottom row ─ connecting to target with ┴ at source positions, └─┘ style
41
+ // Line 2: bottom row ─ connecting to target with ┴ at source positions
42
42
 
43
43
  const fromSet = new Set(fromXs.map((x) => x - minX));
44
44
  const targetCol = toX - minX;
@@ -69,7 +69,7 @@ export function buildMergeConnector(fromXs: number[], toX: number): ConnectorRes
69
69
  for (const fx of fromSet) {
70
70
  botChars[fx] = "┴";
71
71
  }
72
- // Mark target with or ┘ or ┴ depending on position
72
+ // Mark target with a join glyph.
73
73
  botChars[targetCol] = "┴";
74
74
 
75
75
  // Middle line: vertical bars at each source position
@@ -3,9 +3,10 @@
3
3
  * ui/dispatch-mockup.html §1 (compact two-row redesign).
4
4
  *
5
5
  * Visual contract:
6
- * - One status-coloured tagged card:
7
- * row 1: stripe · [tag runId8] · bold workflowName · k=v · k=v · ● running
8
- * row 2 (only on overflow): stripe · k=v · k=v · +N more
6
+ * - One rounded `DISPATCHED` panel.
7
+ * - One status-coloured rounded run card:
8
+ * title: runId8 · workflowName · running
9
+ * body: compact `k=v · k=v · +N more` input summary when present
9
10
  * - One hint row: ▸ /workflow connect <id> attach & watch
10
11
  *
11
12
  * What we deliberately do NOT emit (was in the legacy 7-row layout):
@@ -22,8 +23,7 @@
22
23
  * intent (list other in-flight runs), already discoverable from a
23
24
  * bare `/workflow` invocation and the picker's confirm panel.
24
25
  *
25
- * Plain mode degrades the stripe to `│` and drops ANSI; layout shape is
26
- * preserved.
26
+ * Plain mode drops ANSI; the rounded panel/card layout shape is preserved.
27
27
  *
28
28
  * cross-ref:
29
29
  * - ui/dispatch-mockup.html (before / after side-by-side)
@@ -34,8 +34,8 @@
34
34
 
35
35
  import type { GraphTheme } from "./graph-theme.js";
36
36
  import {
37
- renderTaggedCard,
38
37
  renderHintRows,
38
+ renderRoundedBox,
39
39
  ELLIPSIS,
40
40
  chatWidth,
41
41
  } from "./chat-surface.js";
@@ -67,9 +67,10 @@ export interface RenderDispatchConfirmOpts {
67
67
  }
68
68
 
69
69
  /**
70
- * Render the post-dispatch confirmation: one tagged card carrying the
71
- * runId, workflow name, inputs summary, and a `● running` status badge,
72
- * followed by one hint row pointing at `/workflow connect <id>`.
70
+ * Render the post-dispatch confirmation: one rounded panel containing a
71
+ * rounded run card with runId, workflow name, inputs summary, and a
72
+ * `● running` status badge, followed by one hint row pointing at
73
+ * `/workflow connect <id>`.
73
74
  */
74
75
  export function renderDispatchConfirm(opts: RenderDispatchConfirmOpts): string {
75
76
  const width = effectiveWidth(opts.width);
@@ -87,7 +88,7 @@ export function renderDispatchConfirm(opts: RenderDispatchConfirmOpts): string {
87
88
  // workflow name) or wrap to a body row. Budget math mirrors
88
89
  // `renderTaggedCard`'s row-1 chrome accounting:
89
90
  //
90
- // " "(1)(1) " "(1) [tag](tag.length + 2) " "(2) title(titleW)
91
+ // " "(1) (1) " "(1) [tag](tag.length + 2) " "(2) title(titleW)
91
92
  // " "(2) suffix(suffixW) gap(≥1) trailing(trailingW) " "(1)
92
93
  //
93
94
  // Solve for the max suffix width that still leaves room for the
@@ -116,14 +117,12 @@ export function renderDispatchConfirm(opts: RenderDispatchConfirmOpts): string {
116
117
 
117
118
  const hasInputs = Object.keys(opts.inputs).length > 0;
118
119
  let titleSuffix: string | undefined;
119
- let titleSuffixWidth: number | undefined;
120
120
  const bodyRows: string[] = [];
121
121
 
122
122
  if (hasInputs && inlineBudget >= MIN_INLINE_INPUT_BUDGET) {
123
123
  const inline = renderInputsSegment(opts.inputs, inlineBudget, theme);
124
124
  if (inline && inline.fitted) {
125
125
  titleSuffix = inline.rendered;
126
- titleSuffixWidth = inline.visibleWidth;
127
126
  }
128
127
  }
129
128
 
@@ -131,30 +130,29 @@ export function renderDispatchConfirm(opts: RenderDispatchConfirmOpts): string {
131
130
  // tight, or the rendered inputs spilled past it. Either way, we use
132
131
  // the full body interior (width - body chrome) as the wider canvas.
133
132
  if (hasInputs && titleSuffix === undefined) {
134
- const BODY_PREFIX_W = 4; // "" — see renderTaggedCard body prefix
133
+ const BODY_PREFIX_W = 4; // " " — see renderTaggedCard body prefix
135
134
  const bodyBudget = Math.max(0, width - BODY_PREFIX_W - 1);
136
135
  const overflowSeg = renderInputsSegment(opts.inputs, bodyBudget, theme);
137
136
  if (overflowSeg) bodyRows.push(overflowSeg.rendered);
138
137
  }
139
138
 
140
- const card = renderTaggedCard({
141
- tag,
142
- title: opts.workflowName,
143
- titleSuffix,
144
- titleSuffixWidth,
145
- trailing,
146
- accent,
147
- width: opts.width,
148
- theme,
149
- bodyRows,
150
- });
139
+ const inputRows = bodyRows.length > 0
140
+ ? bodyRows.map((row) => ` ${row} `)
141
+ : [` ${titleSuffix ?? "started in background"} `];
142
+ const titleLine = ` ● ${tag} ${opts.workflowName} ${trailing.text} `;
151
143
 
152
144
  const hints = renderHintRows(
153
145
  [{ command: `/workflow connect ${tag}`, hint: "attach & watch" }],
154
146
  theme,
155
- );
147
+ ).split("\n").map((line) => ` ${line} `);
156
148
 
157
- return [card, hints].join("\n");
149
+ return renderRoundedBox({
150
+ title: "DISPATCHED",
151
+ bodyLines: [titleLine, ...inputRows, "", ...hints],
152
+ accent,
153
+ theme,
154
+ width,
155
+ });
158
156
  }
159
157
 
160
158
  /** First 8 chars of the run UUID — the canonical short form. */