@bastani/atomic 0.9.11-alpha.10 → 0.9.11-alpha.12

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 (224) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +1 -1
  3. package/dist/builtin/intercom/package.json +1 -1
  4. package/dist/builtin/intercom/subagent-relay.ts +8 -5
  5. package/dist/builtin/mcp/package.json +1 -1
  6. package/dist/builtin/subagents/package.json +1 -1
  7. package/dist/builtin/subagents/src/extension/notification-content.ts +14 -9
  8. package/dist/builtin/subagents/src/runs/background/notify.ts +20 -4
  9. package/dist/builtin/web-access/CHANGELOG.md +13 -0
  10. package/dist/builtin/web-access/content-tools.ts +1 -0
  11. package/dist/builtin/web-access/flat-string.ts +37 -0
  12. package/dist/builtin/web-access/github-api.ts +2 -1
  13. package/dist/builtin/web-access/github-extract.ts +3 -2
  14. package/dist/builtin/web-access/package.json +2 -2
  15. package/dist/builtin/workflows/CHANGELOG.md +10 -0
  16. package/dist/builtin/workflows/builtin/goal-ledger.ts +6 -4
  17. package/dist/builtin/workflows/builtin/goal-runner.ts +2 -1
  18. package/dist/builtin/workflows/builtin/goal.ts +1 -1
  19. package/dist/builtin/workflows/builtin/ralph-core.ts +8 -9
  20. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +2 -4
  21. package/dist/builtin/workflows/builtin/ralph-runner.ts +5 -8
  22. package/dist/builtin/workflows/builtin/ralph.ts +2 -1
  23. package/dist/builtin/workflows/package.json +1 -1
  24. package/dist/builtin/workflows/src/durable/completed-catalog.ts +14 -8
  25. package/dist/builtin/workflows/src/durable/completed-inspection.ts +10 -11
  26. package/dist/builtin/workflows/src/durable/resume-catalog.ts +1 -1
  27. package/dist/builtin/workflows/src/durable/resume-eligibility.ts +42 -0
  28. package/dist/builtin/workflows/src/durable/resume-runtime.ts +36 -37
  29. package/dist/builtin/workflows/src/durable/tool-primitive.ts +10 -2
  30. package/dist/builtin/workflows/src/engine/run.ts +1 -0
  31. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +12 -8
  32. package/dist/builtin/workflows/src/extension/hil-answer-notifications.ts +5 -2
  33. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1962 -876
  34. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +6 -3
  35. package/dist/builtin/workflows/src/extension/postmortem-deps.ts +3 -2
  36. package/dist/builtin/workflows/src/extension/render-result.ts +4 -11
  37. package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +17 -20
  38. package/dist/builtin/workflows/src/extension/runtime.ts +3 -3
  39. package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +1 -1
  40. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +3 -7
  41. package/dist/builtin/workflows/src/extension/workflow-durable-resume-command.ts +21 -24
  42. package/dist/builtin/workflows/src/extension/workflow-resume-picker-rows.ts +38 -26
  43. package/dist/builtin/workflows/src/extension/workflow-resume-shadow.ts +2 -1
  44. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +56 -64
  45. package/dist/builtin/workflows/src/extension/workflow-schema.ts +2 -2
  46. package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +0 -5
  47. package/dist/builtin/workflows/src/extension/workflow-targets.ts +17 -20
  48. package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +1 -1
  49. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +24 -51
  50. package/dist/builtin/workflows/src/extension/workflow-tool-inspection.ts +7 -29
  51. package/dist/builtin/workflows/src/extension/workflow-tool-send.ts +5 -13
  52. package/dist/builtin/workflows/src/extension/workflow-tool.ts +4 -13
  53. package/dist/builtin/workflows/src/runs/background/quit.ts +4 -3
  54. package/dist/builtin/workflows/src/runs/background/run-inspect.ts +8 -8
  55. package/dist/builtin/workflows/src/runs/background/status.ts +2 -1
  56. package/dist/builtin/workflows/src/runs/background/workflow-lifecycle-aggregate.ts +2 -1
  57. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +5 -5
  58. package/dist/builtin/workflows/src/runs/foreground/executor-lifecycle.ts +4 -0
  59. package/dist/builtin/workflows/src/runs/foreground/executor-scheduler.ts +2 -0
  60. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +23 -8
  61. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +6 -2
  62. package/dist/builtin/workflows/src/runs/foreground/executor-task-prompts.ts +5 -0
  63. package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +56 -10
  64. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +6 -0
  65. package/dist/builtin/workflows/src/runs/foreground/stage-runner-output.ts +137 -6
  66. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +2 -0
  67. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +2 -0
  68. package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +159 -10
  69. package/dist/builtin/workflows/src/shared/flat-string.ts +33 -0
  70. package/dist/builtin/workflows/src/shared/graph-store-snapshot.ts +144 -0
  71. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +2 -3
  72. package/dist/builtin/workflows/src/shared/run-id.ts +36 -0
  73. package/dist/builtin/workflows/src/shared/store-internal.ts +32 -3
  74. package/dist/builtin/workflows/src/shared/store-observation.ts +10 -0
  75. package/dist/builtin/workflows/src/shared/store-public-types.ts +8 -0
  76. package/dist/builtin/workflows/src/shared/store-run-methods.ts +14 -0
  77. package/dist/builtin/workflows/src/shared/store-types.ts +4 -0
  78. package/dist/builtin/workflows/src/shared/types.ts +2 -0
  79. package/dist/builtin/workflows/src/shared/workflow-artifacts.ts +261 -0
  80. package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +39 -23
  81. package/dist/builtin/workflows/src/tui/graph-canvas.ts +26 -9
  82. package/dist/builtin/workflows/src/tui/graph-view-constants.ts +3 -0
  83. package/dist/builtin/workflows/src/tui/graph-view-graph-render.ts +95 -98
  84. package/dist/builtin/workflows/src/tui/graph-view-render.ts +7 -20
  85. package/dist/builtin/workflows/src/tui/graph-view-state.ts +160 -13
  86. package/dist/builtin/workflows/src/tui/layout.ts +8 -2
  87. package/dist/builtin/workflows/src/tui/node-card.ts +57 -45
  88. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +4 -2
  89. package/dist/builtin/workflows/src/tui/prompt-card-render.ts +291 -21
  90. package/dist/builtin/workflows/src/tui/prompt-card.ts +1 -1
  91. package/dist/builtin/workflows/src/tui/renderers.ts +0 -1
  92. package/dist/builtin/workflows/src/tui/run-detail.ts +52 -35
  93. package/dist/builtin/workflows/src/tui/run-identity-rows.ts +97 -0
  94. package/dist/builtin/workflows/src/tui/session-overlays.ts +24 -3
  95. package/dist/builtin/workflows/src/tui/session-picker.ts +94 -41
  96. package/dist/builtin/workflows/src/tui/stage-chat-view-archive-history.ts +190 -26
  97. package/dist/builtin/workflows/src/tui/stage-chat-view-footer-status.ts +52 -18
  98. package/dist/builtin/workflows/src/tui/stage-chat-view-input.ts +9 -5
  99. package/dist/builtin/workflows/src/tui/stage-chat-view-state.ts +44 -4
  100. package/dist/builtin/workflows/src/tui/stage-chat-view-types.ts +3 -1
  101. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +3 -2
  102. package/dist/builtin/workflows/src/tui/status-list.ts +67 -45
  103. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +7 -6
  104. package/dist/builtin/workflows/src/tui/widget.ts +49 -40
  105. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +15 -10
  106. package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +21 -8
  107. package/dist/core/agent-session-accessors.d.ts.map +1 -1
  108. package/dist/core/agent-session-accessors.js +5 -0
  109. package/dist/core/agent-session-accessors.js.map +1 -1
  110. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  111. package/dist/core/agent-session-auto-compaction.js +5 -0
  112. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  113. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  114. package/dist/core/agent-session-compaction.js +3 -0
  115. package/dist/core/agent-session-compaction.js.map +1 -1
  116. package/dist/core/agent-session-events.d.ts.map +1 -1
  117. package/dist/core/agent-session-events.js +2 -1
  118. package/dist/core/agent-session-events.js.map +1 -1
  119. package/dist/core/agent-session-message-queue.d.ts +3 -7
  120. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  121. package/dist/core/agent-session-message-queue.js +27 -13
  122. package/dist/core/agent-session-message-queue.js.map +1 -1
  123. package/dist/core/agent-session-methods.d.ts +5 -3
  124. package/dist/core/agent-session-methods.d.ts.map +1 -1
  125. package/dist/core/agent-session-methods.js.map +1 -1
  126. package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
  127. package/dist/core/agent-session-persistent-custom-messages.js +6 -3
  128. package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
  129. package/dist/core/agent-session-post-tool-compaction.d.ts.map +1 -1
  130. package/dist/core/agent-session-post-tool-compaction.js +11 -2
  131. package/dist/core/agent-session-post-tool-compaction.js.map +1 -1
  132. package/dist/core/agent-session-tree.d.ts.map +1 -1
  133. package/dist/core/agent-session-tree.js +4 -0
  134. package/dist/core/agent-session-tree.js.map +1 -1
  135. package/dist/core/agent-session-types.d.ts +4 -0
  136. package/dist/core/agent-session-types.d.ts.map +1 -1
  137. package/dist/core/agent-session-types.js.map +1 -1
  138. package/dist/core/agent-session.d.ts +2 -1
  139. package/dist/core/agent-session.d.ts.map +1 -1
  140. package/dist/core/agent-session.js +1 -0
  141. package/dist/core/agent-session.js.map +1 -1
  142. package/dist/core/bounded-model-refresh.d.ts +14 -0
  143. package/dist/core/bounded-model-refresh.d.ts.map +1 -0
  144. package/dist/core/bounded-model-refresh.js +38 -0
  145. package/dist/core/bounded-model-refresh.js.map +1 -0
  146. package/dist/core/messages.d.ts +5 -1
  147. package/dist/core/messages.d.ts.map +1 -1
  148. package/dist/core/messages.js +2 -1
  149. package/dist/core/messages.js.map +1 -1
  150. package/dist/core/session-manager-core.d.ts +1 -1
  151. package/dist/core/session-manager-core.d.ts.map +1 -1
  152. package/dist/core/session-manager-core.js +2 -2
  153. package/dist/core/session-manager-core.js.map +1 -1
  154. package/dist/core/session-manager-entries.d.ts +1 -1
  155. package/dist/core/session-manager-entries.d.ts.map +1 -1
  156. package/dist/core/session-manager-entries.js +2 -1
  157. package/dist/core/session-manager-entries.js.map +1 -1
  158. package/dist/core/session-manager-history.d.ts.map +1 -1
  159. package/dist/core/session-manager-history.js +2 -2
  160. package/dist/core/session-manager-history.js.map +1 -1
  161. package/dist/core/session-manager-types.d.ts +3 -0
  162. package/dist/core/session-manager-types.d.ts.map +1 -1
  163. package/dist/core/session-manager-types.js.map +1 -1
  164. package/dist/index.d.ts +1 -1
  165. package/dist/index.d.ts.map +1 -1
  166. package/dist/index.js.map +1 -1
  167. package/dist/modes/interactive/components/chat-session-host-actions.d.ts +7 -2
  168. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  169. package/dist/modes/interactive/components/chat-session-host-actions.js +13 -4
  170. package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
  171. package/dist/modes/interactive/components/chat-session-host-editor.d.ts +3 -1
  172. package/dist/modes/interactive/components/chat-session-host-editor.d.ts.map +1 -1
  173. package/dist/modes/interactive/components/chat-session-host-editor.js +2 -2
  174. package/dist/modes/interactive/components/chat-session-host-editor.js.map +1 -1
  175. package/dist/modes/interactive/components/chat-session-host-events.d.ts +3 -0
  176. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  177. package/dist/modes/interactive/components/chat-session-host-events.js +5 -4
  178. package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
  179. package/dist/modes/interactive/components/chat-session-host.d.ts +16 -2
  180. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  181. package/dist/modes/interactive/components/chat-session-host.js +31 -4
  182. package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
  183. package/dist/modes/interactive/components/index.d.ts +1 -0
  184. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  185. package/dist/modes/interactive/components/index.js.map +1 -1
  186. package/dist/modes/interactive/components/model-selector.d.ts +0 -1
  187. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  188. package/dist/modes/interactive/components/model-selector.js +23 -45
  189. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  190. package/dist/modes/interactive/interactive-editor-actions.js +2 -1
  191. package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
  192. package/dist/modes/interactive/interactive-input-handling.js +1 -1
  193. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  194. package/dist/modes/interactive/interactive-mode-surface.d.ts +4 -1
  195. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  196. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  197. package/dist/modes/interactive/interactive-model-routing.js +4 -1
  198. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  199. package/dist/modes/interactive/interactive-pause.d.ts +3 -1
  200. package/dist/modes/interactive/interactive-pause.d.ts.map +1 -1
  201. package/dist/modes/interactive/interactive-pause.js +5 -1
  202. package/dist/modes/interactive/interactive-pause.js.map +1 -1
  203. package/dist/modes/interactive/interactive-queueing.js +3 -3
  204. package/dist/modes/interactive/interactive-queueing.js.map +1 -1
  205. package/dist/modes/interactive-engine/isolated-runtime.d.ts +1 -0
  206. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  207. package/dist/modes/interactive-engine/isolated-runtime.js +5 -0
  208. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  209. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  210. package/dist/modes/rpc/rpc-command-handler.js +1 -0
  211. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  212. package/dist/modes/rpc/rpc-types.d.ts +2 -1
  213. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  214. package/dist/modes/rpc/rpc-types.js.map +1 -1
  215. package/docs/index.md +10 -0
  216. package/docs/keybindings.md +1 -1
  217. package/docs/models.md +1 -1
  218. package/docs/quickstart.md +7 -1
  219. package/docs/termux.md +3 -1
  220. package/docs/usage.md +3 -2
  221. package/docs/workflows.md +82 -46
  222. package/npm-shrinkwrap.json +59 -27
  223. package/package.json +3 -3
  224. package/dist/builtin/workflows/src/tui/toast.ts +0 -107
package/CHANGELOG.md CHANGED
@@ -2,6 +2,50 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.11-alpha.12] - 2026-08-03
6
+
7
+ ### Fixed
8
+
9
+ - Fixed PDF extraction dropping text and streaming `TypeError: Math.sumPrecise is not a function` warnings on Node runtimes that do not implement `Math.sumPrecise`. The `unpdf` dependency was declared as the floating range `^1.6.2`, which resolves to 1.8.0 in a published or global install; that release's bundled PDF.js v6.1 dropped the guarded fallback and calls the builtin unconditionally, so extraction threw per glyph, emitted font-substitution warnings, and silently returned fewer text items. `unpdf` is now exact-pinned to `1.7.0`, the newest release that still ships the fallback, so no install can float to 1.8.0 ([#2141](https://github.com/bastani-inc/atomic/issues/2141)).
10
+ - Upgraded the pinned `unpdf` from 1.6.2 to 1.7.0, which adds `cMapUrl` CJK font support under Node, destroys internally created document proxies and canvas resources, and preserves line breaks in `extractText` with `mergePages`.
11
+
12
+ ## [0.9.11-alpha.11] - 2026-08-03
13
+
14
+ ### Breaking Changes
15
+
16
+ - Workflow run targets now require the full 36-character run UUID. Typed prefixes are no longer resolved by any command or workflow-tool action that accepts `runId`, including `status`, `stages`, `stage`, `transcript`, `send`, `pause`, `resume`, `interrupt`, and `quit`, and by `/workflow connect`, `/workflow attach`, and `/workflow resume`. A target that is not a well-formed 8-4-4-4-12 hex UUID — a prefix, a 32-character dashless id, or a same-length non-hex string — is rejected with `Run id must be a full 36-character UUID; got "339e05a4" (8 chars).`, which is deliberately distinct from `Run not found:` so a truncated paste is diagnosable as truncated rather than looking like a stale run. Since every user-facing surface already prints the full id, copy it back verbatim. Because ids are unique and now matched exactly, run-target ambiguity is unreachable and the "Ambiguous run prefix" diagnostic is gone.
17
+ - Stage targets are now matched exactly. A `stageId` resolves by exact stage id — a bare UUID at the root, the full `runId:stageId` composite for a stage inside a nested `ctx.workflow(...)` import, or `tool:<argsHash>` for a `ctx.tool` node — or by exact stage or tool name. Prefixes and partial names no longer resolve, so `build` will not select `build-check`. Stage ids are deliberately **not** held to the 36-character rule, because nested and tool identifiers are legitimately longer or differently shaped; nested-stage and tool-node targeting are unchanged. Two stages sharing an exact name still return the existing ambiguity diagnostic.
18
+
19
+ ### Changed
20
+
21
+ - Removed the four-workflow display cap from the BACKGROUND widget so every qualifying top-level run is rendered.
22
+ - Moved durable workflow run artifacts—including goal ledgers, Ralph implementation notes, QA evidence video paths, and worktree task outputs—from per-invocation OS temp directories to the run-scoped durable root under the Atomic config directory (`~/.atomic/workflows/runs/<runId>/`, overridable with `ATOMIC_WORKFLOW_ARTIFACT_DIR`), so they survive OS temp purges and follow state-aware retention.
23
+ - Toggling tool-output expansion (`ctrl+o`) no longer prints a `Tool output: expanded` / `Tool output: collapsed` status line. The chat re-renders in the new state, which is the same information without the extra line.
24
+
25
+ - Workflow run identifiers are now shown as full UUIDs across the BACKGROUND widget, status and detail views, run pickers, control messages, and awaiting-input attribution banners. BACKGROUND cards use a two-line identity layout at 80 columns and wider, while narrow chat surfaces wrap full ids without cutting them or breaking their borders. The public `workflow({ action: "status", format: "json" })` payload no longer includes `runIdPrefix`; read `runId` instead, which now carries the full id. The `/workflow connect` run picker now shows five runs at a time and scrolls to additional retained runs with the arrow keys or mouse wheel. The picker's type-to-filter box still narrows by name substring or id prefix, since that selects a row from a list rather than resolving a typed id.
26
+
27
+ ### Fixed
28
+
29
+ - Fixed Escape aborts restoring queued steering and follow-up messages to the editor in the main interactive chat and attached workflow-stage chats instead of leaving them stuck in the pending-message queue.
30
+ - Fixed `/model <model_name>` waiting indefinitely when a model-catalog refresh never settles; it now stops after the interactive 15-second deadline and searches cached models.
31
+ - Quit workflow cards now expire from the BACKGROUND widget after the same recent-run window as finished cards while remaining resumable and discoverable through workflow status; the header count now matches the rendered cards after expiry.
32
+ - Fixed detached foreground subagent completion notifications rendering as an unstyled dump of the full child output. They now use the same structured, collapsed notification UI as background completions while preserving the distinct detached-task wording, and persisted notifications with custom labels remain parseable.
33
+ - Fixed workflow-stage re-attach status falling back to `Working...` while context compaction is still active; the active compaction reason now survives host remounts so manual compaction, threshold auto-compaction, and overflow recovery retain their factual status labels.
34
+ - Fixed workflow stage file output being lost when an admitted external turn (for example an async subagent completion) arrived while the stage was still running: the deterministic `output:` write re-derived its content from session state at close time, so a trailing acknowledgement could overwrite the stage's real deliverable. Every stage that declares `output:` now also gets a durable, searchable companion transcript outside the repository tree, and the receipt names both paths with the intended access pattern, so the work remains recoverable regardless of which turn was persisted. The runner does not attempt to infer which turn was the intended deliverable; it writes the stage's final assistant message, and the receipt reports one fact only — an empty artifact.
35
+ - Fixed the workflow resume picker offering stale or non-resumable runs. Picker and command paths now share one resumability predicate and revalidate durable state before displaying a target, while explicit ids retain their explanatory rejection.
36
+ - Fixed workflow `reads:` references to fail loudly when an artifact path no longer exists instead of handing the model an empty, silently missing context file.
37
+ - Fixed relayed terminal subagent results retaining their stable stage-admission identity when delivered through the intercom ordering barrier, so an acknowledgement turn cannot arrive without the admission marker that identifies it in a running stage's companion transcript.
38
+ - Fixed a failed companion-transcript write reporting a bogus `Output file error` for an output artifact that was written correctly, and returning the full stage text under `outputMode: "file-only"` instead of the compact reference. A transcript failure now degrades to a warning naming the transcript path while the saved-output reference is unchanged, and artifact-retention pruning failures no longer propagate into the stage receipt.
39
+ - Fixed `createWorkflowArtifactDirectory()` attributing artifacts to the ambient most-recently-started run rather than the workflow that created them, and returning one shared directory whose fixed artifact filenames could overwrite each other. Every builtin call site now passes its owning run id, and each call receives a unique directory beneath that run.
40
+ - Fixed the workflow resume picker re-offering a live run as a durable row after the shared resumability predicate rejected it for missing artifacts or checkpoints. Rejected non-running snapshots are now suppressed from live and durable resumable rows, while completed inspection rows remain available; active-running snapshots retain their existing durable fallback.
41
+
42
+ ### Added
43
+ - Added first-class Alpine/musl Linux support for x64 and arm64, including native search and PTY bindings and the `atomic-linux-x64-musl.tar.gz` and `atomic-linux-arm64-musl.tar.gz` release archives. The musl archives omit the clipboard native binding and glibc-linked `@embedded-postgres/*` binaries: Atomic falls back to Linux clipboard commands/OSC52, while durable Alpine workflows require external Postgres via `DBOS_SYSTEM_DATABASE_URL` or Docker; otherwise the existing loud non-durable in-memory fallback applies.
44
+ - Added state-aware retention for durable workflow artifact directories: only terminal or unowned runs older than `WORKFLOW_ARTIFACT_RETENTION_MS` are pruned, while running, paused, quit, blocked, and awaiting-input runs keep live resume dependencies. Aging out a terminal run now deletes its durable entry first and removes the artifact directory only when that deletion succeeds, so a surviving durable record can never reference deleted files.
45
+ - Added durable, searchable companion transcripts for every workflow stage that declares `output:`. The caller's receipt names both the artifact and transcript paths and says to search with `rg`, read narrow line ranges, and never read the transcript whole; under `outputMode: "file-only"`, that receipt is the entire handoff.
46
+ - Added explicit warnings for degenerate stage artifacts instead of silently persisting them: an empty write or a write that only points to its own output path directs the reader to the companion transcript.
47
+ - Added a real artifact-integrity probe to the shared resume predicate. A run whose snapshot references a run-scoped artifact path is offered for resume only while that run directory still exists, and durable-checkpoint eligibility is supplied from the authoritative loadable check, so `/workflow resume` matches the documented behaviour instead of relying on flags no caller set.
48
+
5
49
  ## [0.9.11-alpha.10] - 2026-08-01
6
50
 
7
51
  ### Fixed
package/README.md CHANGED
@@ -79,7 +79,7 @@ atomic
79
79
 
80
80
  Then just talk to Atomic. By default, Atomic gives the model six coding tools: `read`, `write`, `edit`, `bash`, `find`, and `search`. The model uses these to fulfill your requests. `read`, `search`, `write`, and successful `edit` calls emit session-scoped hashline anchors (`[path#TAG]` plus `LINE:text`) so the model can make stale-safe line edits; see [docs/tools.md](docs/tools.md). Add capabilities via [skills](#skills), [prompt templates](#prompt-templates), [extensions](#extensions), or [Atomic packages](#atomic-packages).
81
81
 
82
- **Platform notes:** [Windows](docs/windows.md) | [Termux (Android)](docs/termux.md) | [tmux](docs/tmux.md) | [Terminal setup](docs/terminal-setup.md) | [Shell aliases](docs/shell-aliases.md)
82
+ **Platform notes:** [Windows](docs/windows.md) | [Alpine/musl Linux](docs/index.md#alpine-and-musl-linux-archives) | [Termux (Android)](docs/termux.md) | [tmux](docs/tmux.md) | [Terminal setup](docs/terminal-setup.md) | [Shell aliases](docs/shell-aliases.md)
83
83
 
84
84
  ---
85
85
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/intercom",
3
- "version": "0.9.11-alpha.10",
3
+ "version": "0.9.11-alpha.12",
4
4
  "private": true,
5
5
  "description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
6
6
  "contributors": [
@@ -33,6 +33,7 @@ async function dispatchRelayFallback(
33
33
  pi: ExtensionAPI,
34
34
  prefix: Array<{ customType: string; content: string; display: boolean; details?: unknown }>,
35
35
  terminalMessage: { customType: string; content: string; display: boolean; details?: unknown },
36
+ stageAdmissionKey: string,
36
37
  ): Promise<void> {
37
38
  let delivered = 0;
38
39
  try {
@@ -40,7 +41,7 @@ async function dispatchRelayFallback(
40
41
  await pi.sendMessage(message, { deliverAs: "steer" });
41
42
  delivered += 1;
42
43
  }
43
- await pi.sendMessage(terminalMessage, { triggerTurn: true });
44
+ await pi.sendMessage(terminalMessage, { triggerTurn: true, stageAdmissionKey });
44
45
  } catch (error) {
45
46
  const failure = new Error(error instanceof Error ? error.message : String(error), { cause: error });
46
47
  Object.assign(failure, { terminalPreludeDelivered: delivered });
@@ -57,6 +58,7 @@ export function registerSubagentRelay(pi: ExtensionAPI, deps: SubagentRelayDeps)
57
58
  status: string,
58
59
  messageText: string,
59
60
  terminalBarrier?: { runId: string; terminalId?: string; sourceSessionTargets: string[] },
61
+ requestId?: string,
60
62
  ): Promise<void> {
61
63
  const now = Date.now();
62
64
  const entry = {
@@ -82,10 +84,11 @@ export function registerSubagentRelay(pi: ExtensionAPI, deps: SubagentRelayDeps)
82
84
  display: true,
83
85
  details: entry,
84
86
  };
87
+ const stageAdmissionKey = `intercom:${requestId ?? entry.message.id}`;
85
88
  if (typeof pi.sendMessages === "function") {
86
- return pi.sendMessages([...prefix, terminalMessage], { triggerTurn: true });
89
+ return pi.sendMessages([...prefix, terminalMessage], { triggerTurn: true, stageAdmissionKey });
87
90
  }
88
- return dispatchRelayFallback(pi, prefix, terminalMessage);
91
+ return dispatchRelayFallback(pi, prefix, terminalMessage, stageAdmissionKey);
89
92
  },
90
93
  };
91
94
  Object.defineProperty(payload, "terminalOwner", { value: pi });
@@ -139,13 +142,13 @@ export function registerSubagentRelay(pi: ExtensionAPI, deps: SubagentRelayDeps)
139
142
  void delivery.catch(() => {});
140
143
  localDeliveriesInFlight.set(parsed.requestId, { signature, completion: delivery });
141
144
  try {
142
- void deliverLocalSubagentRelayMessage(options.sender, options.status, parsed.message, options.terminalBarrier).then(deferred.resolve, deferred.reject);
145
+ void deliverLocalSubagentRelayMessage(options.sender, options.status, parsed.message, options.terminalBarrier, parsed.requestId).then(deferred.resolve, deferred.reject);
143
146
  } catch (error) {
144
147
  deferred.reject(error);
145
148
  }
146
149
  void delivery.finally(() => { localDeliveriesInFlight.delete(parsed.requestId!); }).catch(() => {});
147
150
  } else if (!delivery) {
148
- delivery = deliverLocalSubagentRelayMessage(options.sender, options.status, parsed.message, options.terminalBarrier);
151
+ delivery = deliverLocalSubagentRelayMessage(options.sender, options.status, parsed.message, options.terminalBarrier, parsed.requestId);
149
152
  }
150
153
  await delivery;
151
154
  if (parsed.requestId) localDeliveries.record(parsed.requestId, signature);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/mcp",
3
- "version": "0.9.11-alpha.10",
3
+ "version": "0.9.11-alpha.12",
4
4
  "private": true,
5
5
  "description": "Atomic extension that adapts MCP (Model Context Protocol) servers into the coding agent. Fork of: https://github.com/nicobailon/pi-mcp-adapter",
6
6
  "contributors": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/subagents",
3
- "version": "0.9.11-alpha.10",
3
+ "version": "0.9.11-alpha.12",
4
4
  "private": true,
5
5
  "description": "Atomic extension for delegating tasks to subagents with chains, parallel execution, and background runs. Fork of: https://github.com/nicobailon/pi-subagents",
6
6
  "contributors": [
@@ -1,10 +1,6 @@
1
1
  import type { SubagentNotifyDetails } from "../runs/background/notify.ts";
2
2
 
3
- const HEADER_PREFIXES = [
4
- ["Background task completed: **", "completed"],
5
- ["Background task failed: **", "failed"],
6
- ["Background task paused: **", "paused"],
7
- ] as const;
3
+ const HEADER_STATUSES = ["completed", "failed", "paused"] as const;
8
4
 
9
5
  function isHeaderLineTerminator(character: string): boolean {
10
6
  return character === "\r" || character === "\u2028" || character === "\u2029";
@@ -15,10 +11,19 @@ function isWhitespace(character: string | undefined): boolean {
15
11
  }
16
12
 
17
13
  function parseHeader(header: string): Pick<SubagentNotifyDetails, "agent" | "status" | "taskInfo"> | undefined {
18
- const matchedPrefix = HEADER_PREFIXES.find(([prefix]) => header.startsWith(prefix));
19
- if (!matchedPrefix) return undefined;
20
- const [prefix, status] = matchedPrefix;
21
- const suffix = header.slice(prefix.length);
14
+ let markerIndex = -1;
15
+ let status: SubagentNotifyDetails["status"] | undefined;
16
+ for (const candidateStatus of HEADER_STATUSES) {
17
+ const candidateMarker = ` ${candidateStatus}: **`;
18
+ const candidateIndex = header.indexOf(candidateMarker);
19
+ if (candidateIndex < 1 || (markerIndex >= 0 && candidateIndex >= markerIndex)) continue;
20
+ markerIndex = candidateIndex;
21
+ status = candidateStatus;
22
+ }
23
+ if (markerIndex < 1 || !status) return undefined;
24
+ const label = header.slice(0, markerIndex);
25
+ if (label !== label.trim() || [...label].some(isHeaderLineTerminator)) return undefined;
26
+ const suffix = header.slice(markerIndex + ` ${status}: **`.length);
22
27
 
23
28
  if (suffix.endsWith("**")) {
24
29
  const agent = suffix.slice(0, -2);
@@ -198,8 +198,8 @@ export default function registerSubagentNotify(pi: ExtensionAPI): () => void {
198
198
 
199
199
  const taskInfo =
200
200
  result.taskIndex !== undefined && result.totalTasks !== undefined
201
- ? ` (${result.taskIndex + 1}/${result.totalTasks})`
202
- : "";
201
+ ? `(${result.taskIndex + 1}/${result.totalTasks})`
202
+ : undefined;
203
203
 
204
204
  const sessionLine = result.shareUrl
205
205
  ? `Session: ${result.shareUrl}`
@@ -214,8 +214,23 @@ export default function registerSubagentNotify(pi: ExtensionAPI): () => void {
214
214
  typeof result.noticeLabel === "string" && result.noticeLabel.trim()
215
215
  ? result.noticeLabel.trim()
216
216
  : "Background task";
217
+ let sessionLabel: string | undefined;
218
+ let sessionValue: string | undefined;
219
+ if (sessionLine) {
220
+ const separator = sessionLine.indexOf(":");
221
+ sessionLabel = sessionLine.slice(0, separator).toLowerCase();
222
+ sessionValue = sessionLine.slice(separator + 1).trim();
223
+ }
224
+ const details: SubagentNotifyDetails = {
225
+ agent,
226
+ status,
227
+ ...(taskInfo ? { taskInfo } : {}),
228
+ resultPreview: displaySummary,
229
+ ...(result.durationMs !== undefined ? { durationMs: result.durationMs } : {}),
230
+ ...(sessionLabel && sessionValue ? { sessionLabel, sessionValue } : {}),
231
+ };
217
232
  const content = [
218
- `${noticeLabel} ${status}: **${agent}**${taskInfo}`,
233
+ `${noticeLabel} ${status}: **${agent}**${taskInfo ? ` ${taskInfo}` : ""}`,
219
234
  "",
220
235
  displaySummary,
221
236
  sessionLine ? "" : undefined,
@@ -224,10 +239,11 @@ export default function registerSubagentNotify(pi: ExtensionAPI): () => void {
224
239
  .filter((line) => line !== undefined)
225
240
  .join("\n");
226
241
 
227
- const terminalMessage = {
242
+ const terminalMessage: TerminalPreludeMessage = {
228
243
  customType: "subagent-notify",
229
244
  content,
230
245
  display: true,
246
+ details,
231
247
  };
232
248
  const deliveryOptions = { triggerTurn: true, stageAdmissionKey: `subagent:${key}` } as const;
233
249
  const succeed = (): void => {
@@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.11-alpha.12] - 2026-08-03
8
+
9
+ ### Fixed
10
+
11
+ - Fixed PDF extraction dropping text and streaming `TypeError: Math.sumPrecise is not a function` warnings on Node runtimes that do not implement `Math.sumPrecise`. The `unpdf` dependency was declared as the floating range `^1.6.2`, which resolves to 1.8.0 in a published or global install; that release's bundled PDF.js v6.1 dropped the guarded fallback and calls the builtin unconditionally, so extraction threw per glyph, emitted font-substitution warnings, and silently returned fewer text items. On the reported 17-page arXiv PDF this cost 69 text items and 221 characters. `unpdf` is now exact-pinned to `1.7.0`, the newest release that still ships the fallback, so no install can float to 1.8.0 ([#2141](https://github.com/bastani-inc/atomic/issues/2141)).
12
+ - Upgraded the pinned `unpdf` from 1.6.2 to 1.7.0, which adds `cMapUrl` CJK font support under Node, destroys internally created document proxies and canvas resources, and preserves line breaks in `extractText` with `mergePages`.
13
+
14
+ ## [0.9.11-alpha.11] - 2026-08-03
15
+
16
+ ### Fixed
17
+
18
+ - Fixed retained GitHub README truncations by copying bounded text into flat strings before storing long-lived results ([#2151](https://github.com/bastani-inc/atomic/issues/2151)).
19
+
7
20
  ## [0.9.11-alpha.1] - 2026-07-20
8
21
 
9
22
  ### Changed
@@ -44,6 +44,7 @@ export function registerContentTools(pi: ExtensionAPI, deps: RegisterContentTool
44
44
 
45
45
  renderCall(args, theme) {
46
46
  const { query } = args as { query?: string };
47
+ // This status-line preview is built and dropped within the same turn, so a bare slice is sufficient.
47
48
  const display = !query
48
49
  ? "(no query)"
49
50
  : query.length > 70 ? query.slice(0, 67) + "..." : query;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Truncation that actually releases the untruncated source.
3
+ *
4
+ * This local web-access copy mirrors the original helper in
5
+ * `packages/workflows/src/shared/flat-string.ts`; web-access is a standalone package and
6
+ * cannot import that workflows module.
7
+ *
8
+ * V8 does not copy characters for `String.prototype.slice`. For a flat parent of
9
+ * 13 characters or more it allocates a **SlicedString**, which is a length, an
10
+ * offset, and a pointer to the parent. Template interpolation then wraps that in
11
+ * a **ConsString**, which keeps the same pointer. So the obvious way to bound a
12
+ * value for storage:
13
+ *
14
+ * ```ts
15
+ * `${JSON.stringify(value).slice(0, 237)}...`
16
+ * ```
17
+ *
18
+ * produces a 240-character string that reports `length === 240` while holding
19
+ * the entire serialized payload alive. The bound is on what you can read, not on
20
+ * what you retain. An 8 MiB tool result summarized to 240 characters still costs
21
+ * 8 MiB for as long as anything keeps the summary.
22
+ *
23
+ * That is invisible in every ordinary test: the value is correct, its length is
24
+ * correct, and equality holds. It only shows up as heap that never comes back.
25
+ *
26
+ * `split("").join("")` copies the code units into a fresh flat string with no
27
+ * parent pointer. It is code-unit exact, including lone surrogates, because
28
+ * `split("")` splits on UTF-16 code units rather than code points.
29
+ *
30
+ * Use this at any truncation whose result outlives the value it came from —
31
+ * memoized projections, durable checkpoints, catalogs, persisted summaries.
32
+ * A truncation that is consumed and dropped within the same turn does not need
33
+ * it, and paying for a copy there is waste.
34
+ */
35
+ export function flattenTruncatedString(value: string): string {
36
+ return value.split("").join("");
37
+ }
@@ -1,5 +1,6 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import type { ExtractedContent } from "./extract.js";
3
+ import { flattenTruncatedString } from "./flat-string.js";
3
4
  import type { GitHubUrlInfo } from "./github-extract.js";
4
5
 
5
6
  const MAX_TREE_ENTRIES = 200;
@@ -97,7 +98,7 @@ async function fetchReadmeViaApi(owner: string, repo: string, ref: string): Prom
97
98
  }
98
99
  try {
99
100
  const decoded = Buffer.from(stdout.trim(), "base64").toString("utf-8");
100
- resolve(decoded.length > 8192 ? decoded.slice(0, 8192) + "\n\n[README truncated at 8K chars]" : decoded);
101
+ resolve(decoded.length > 8192 ? flattenTruncatedString(decoded.slice(0, 8192) + "\n\n[README truncated at 8K chars]") : decoded);
101
102
  } catch {
102
103
  resolve(null);
103
104
  }
@@ -3,6 +3,7 @@ import { execFile } from "node:child_process";
3
3
  import { extname, join, resolve as resolvePath, sep as pathSep } from "node:path";
4
4
  import { activityMonitor } from "./activity.js";
5
5
  import type { ExtractedContent } from "./extract.js";
6
+ import { flattenTruncatedString } from "./flat-string.js";
6
7
  import { checkGhAvailable, checkRepoSize, fetchViaApi, showGhHint } from "./github-api.js";
7
8
  import { BINARY_EXTENSIONS, MAX_INLINE_FILE_CHARS, MAX_TREE_ENTRIES, NOISE_DIRS, loadGitHubConfig, parseGitHubUrl, resetGitHubConfig, type GitHubCloneConfig, type GitHubUrlInfo } from "./github-config.js";
8
9
  export { parseGitHubUrl, type GitHubUrlInfo } from "./github-config.js";
@@ -225,14 +226,14 @@ function buildDirListing(rootPath: string, subPath: string): string {
225
226
  return lines.join("\n");
226
227
  }
227
228
 
228
- function readReadme(localPath: string): string | null {
229
+ export function readReadme(localPath: string): string | null {
229
230
  const candidates = ["README.md", "readme.md", "README", "README.txt", "README.rst"];
230
231
  for (const name of candidates) {
231
232
  const readmePath = join(localPath, name);
232
233
  if (existsSync(readmePath)) {
233
234
  try {
234
235
  const content = readFileSync(readmePath, "utf-8");
235
- return content.length > 8192 ? content.slice(0, 8192) + "\n\n[README truncated at 8K chars]" : content;
236
+ return content.length > 8192 ? flattenTruncatedString(content.slice(0, 8192) + "\n\n[README truncated at 8K chars]") : content;
236
237
  } catch {
237
238
  continue;
238
239
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/web-access",
3
- "version": "0.9.11-alpha.10",
3
+ "version": "0.9.11-alpha.12",
4
4
  "private": true,
5
5
  "description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
6
6
  "contributors": [
@@ -46,6 +46,6 @@
46
46
  "linkedom": "^0.18.13",
47
47
  "p-limit": "^7.3.0",
48
48
  "turndown": "^7.2.0",
49
- "unpdf": "^1.6.2"
49
+ "unpdf": "1.7.0"
50
50
  }
51
51
  }
@@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.11-alpha.11] - 2026-08-03
10
+
11
+ ### Fixed
12
+
13
+ - Fixed workflow graph rendering for large, wide fan-outs by clipping cards, edges, and composed rows to the visible viewport, retaining cached topology and layout across status-only updates, and making idle animation eligibility constant-time ([#2100](https://github.com/bastani-inc/atomic/issues/2100)).
14
+ - Fixed every interactive store mutation cloning the full run payload before the graph could repaint. A status or question update used to build a complete `JSON.stringify`/`JSON.parse` snapshot, traversing workflow inputs, authored stage result bodies, child output values, and tool input/output bodies, so mutation cost scaled with payload size rather than with the graph. Store observation now offers a synchronous invalidation-only channel plus one immutable memoized graph projection per store version, and the graph, overlay, attach pane, stage chat, widget, lifecycle and HIL notifications, resume picker, and send admission all read that projection. In the default configuration (`statusFile: false`), mutations no longer build the legacy full snapshot; enabling `statusFile` retains that snapshot and status-file serialization cost. Topology, status, timing, prompts, attachment state, notices, bounded returned-status fields, durable tool summaries, and child output counts are preserved; the existing `Store.snapshot()` / `Store.subscribe(snapshot)` contract is unchanged for external consumers ([#2100](https://github.com/bastani-inc/atomic/issues/2100)).
15
+ - Fixed the memoized graph projection keeping whole run results in memory. Truncating a result field with `slice()` leaves a V8 SlicedString that points at its untruncated parent, so a 1 KiB projected field kept a multi-megabyte run result alive — inside the store's cached projection, and inside every long-lived holder of one — long after the run itself was removed. Truncated fields are now copied into fresh flat strings, so a session with large workflow results no longer grows heap ([#2100](https://github.com/bastani-inc/atomic/issues/2100)).
16
+ - Fixed durable tool-result summaries retaining the payloads they summarize. `summarizeToolResult` and `summarizeCompletedToolResult` bounded a serialized value with `` `${serialized.slice(0, 237)}...` ``, which leaves a V8 SlicedString behind a ConsString, both pointing at the untruncated parent — so a 240-character summary written into a durable checkpoint or held in a completed-run catalog row kept the entire `JSON.stringify` output alive (measured at 8.4 MiB for one large tool result). Both summaries now copy into fresh flat strings via a shared `flattenTruncatedString` helper, which the memoized graph projection also uses ([#2100](https://github.com/bastani-inc/atomic/issues/2100)).
17
+ - Fixed Escape in an attached workflow-stage chat restoring queued steering and follow-up messages into the editor instead of leaving them stuck in the pending-message queue.
18
+
9
19
  ## [0.9.11-alpha.9] - 2026-08-01
10
20
 
11
21
  ### Added
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { mkdtemp, writeFile } from "node:fs/promises";
3
- import { tmpdir } from "node:os";
2
+ import { writeFile } from "node:fs/promises";
4
3
  import { join } from "node:path";
4
+ import { createWorkflowArtifactDirectory } from "../src/shared/workflow-artifacts.js";
5
5
  import { LEDGER_FILENAME, type GoalLedger, type GoalLifecycleEvent } from "./goal-types.js";
6
6
 
7
7
  type ModelVisibleGoalLedger = Omit<
@@ -55,11 +55,13 @@ export function appendLifecycleEvent(
55
55
  export async function createGoalLedger(
56
56
  objective: string,
57
57
  acceptanceCriteria = objective,
58
+ runId?: string,
58
59
  ): Promise<{ ledger: GoalLedger; ledgerPath: string; artifactDir: string }> {
59
- const artifactDir = await mkdtemp(join(tmpdir(), "atomic-goal-runner-"));
60
+ const goalId = randomUUID();
61
+ const artifactDir = await createWorkflowArtifactDirectory(runId);
60
62
  const now = new Date().toISOString();
61
63
  const ledger: GoalLedger = {
62
- goal_id: randomUUID(),
64
+ goal_id: goalId,
63
65
  objective,
64
66
  acceptance_criteria: acceptanceCriteria,
65
67
  status: "active",
@@ -64,6 +64,7 @@ function normalizeBranchInput(
64
64
  }
65
65
  type GoalRunnerContext = {
66
66
  readonly inputs: GoalWorkflowInputs;
67
+ readonly runId?: string;
67
68
  task(name: string, options: WorkflowTaskOptions): Promise<WorkflowTaskResult>;
68
69
  parallel(steps: readonly WorkflowTaskStep[], options: WorkflowParallelOptions): Promise<WorkflowTaskResult[]>;
69
70
  };
@@ -109,7 +110,7 @@ export async function runGoalWorkflow(ctx: GoalRunnerContext, options: GoalWorkf
109
110
  const reviewQuorum = DEFAULT_REVIEW_QUORUM;
110
111
  const blockerThreshold = Math.min(DEFAULT_BLOCKER_THRESHOLD, maxTurns);
111
112
  const comparisonBaseBranch = normalizeBranchInput(inputs.base_branch, "origin/main");
112
- const { ledger, ledgerPath, artifactDir } = await createGoalLedger(objective, acceptanceCriteria);
113
+ const { ledger, ledgerPath, artifactDir } = await createGoalLedger(objective, acceptanceCriteria, ctx.runId);
113
114
 
114
115
  let latestReviews: ReviewRecord[] = [];
115
116
  let latestReviewArtifactPaths: string[] = [];
@@ -47,7 +47,7 @@ export default workflow({
47
47
  goal_id: Type.Optional(Type.String({ description: "Per-run goal identifier stored in the ledger." })),
48
48
  objective: Type.Optional(Type.String({ description: "Raw goal objective used by the run." })),
49
49
  acceptance_criteria: Type.Optional(Type.String({ description: "Immutable acceptance criteria used by the run." })),
50
- ledger_path: Type.Optional(Type.String({ description: "OS-temp path to goal-ledger.json with receipts, reviewer decisions, blockers, and lifecycle events." })),
50
+ ledger_path: Type.Optional(Type.String({ description: "Durable run-scoped path to goal-ledger.json with receipts, reviewer decisions, blockers, and lifecycle events." })),
51
51
  turns_completed: Type.Optional(Type.Number({ description: "Orchestrator/review turns completed." })),
52
52
  iterations_completed: Type.Optional(Type.Number({ description: "Orchestrator/review turns completed, retained for status summaries." })),
53
53
  receipts: Type.Optional(Type.Array(Type.Object({
@@ -1,8 +1,8 @@
1
- import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
1
+ import { mkdir, writeFile } from "node:fs/promises";
3
2
  import { dirname, join } from "node:path";
4
3
  import { Type } from "typebox";
5
4
  import type { WorkflowTaskResult } from "../src/shared/types.js";
5
+ import { createWorkflowArtifactDirectory } from "../src/shared/workflow-artifacts.js";
6
6
  import {
7
7
  E2E_VERIFICATION_GUIDANCE,
8
8
  LITERAL_OBJECTIVE_CONTRACT,
@@ -170,8 +170,8 @@ export function defaultResearchPath(prompt: string, now = new Date()): string {
170
170
  return join(DEFAULT_RESEARCH_DIR, `${date}-${slugifyResearchTopic(prompt)}.md`);
171
171
  }
172
172
 
173
- export async function createImplementationNotesFile(prompt: string): Promise<string> {
174
- const notesDir = await mkdtemp(join(tmpdir(), "atomic-ralph-notes-"));
173
+ export async function createImplementationNotesFile(prompt: string, runId?: string): Promise<string> {
174
+ const notesDir = await createWorkflowArtifactDirectory(runId);
175
175
  const notesPath = join(notesDir, IMPLEMENTATION_NOTES_FILENAME);
176
176
  const initialNotes = [
177
177
  "# Implementation Notes",
@@ -195,8 +195,8 @@ export async function createImplementationNotesFile(prompt: string): Promise<str
195
195
  // write to it; the video file itself is produced by the orchestrator's QA pass
196
196
  // (and overwritten each iteration so it always reflects the latest state). The
197
197
  // final pull-request stage attaches it when it exists.
198
- export async function createQaEvidenceVideoPath(): Promise<string> {
199
- const qaDir = await mkdtemp(join(tmpdir(), "atomic-ralph-qa-"));
198
+ export async function createQaEvidenceVideoPath(runId?: string): Promise<string> {
199
+ const qaDir = await createWorkflowArtifactDirectory(runId);
200
200
  return join(qaDir, QA_E2E_VIDEO_FILENAME);
201
201
  }
202
202
 
@@ -367,7 +367,6 @@ export function renderResearchPrompt(args: {
367
367
  readonly acceptanceCriteria: string;
368
368
  readonly workflowCwdContext: PromptSection;
369
369
  readonly latestReviewReportPath: string | undefined;
370
- readonly researchPath: string;
371
370
  }): string {
372
371
  return taggedPrompt([
373
372
  ["acceptance_criteria", args.acceptanceCriteria],
@@ -386,8 +385,7 @@ export function renderResearchPrompt(args: {
386
385
  [
387
386
  "research_artifact",
388
387
  [
389
- "Return the complete research report as your final message. This workflow saves that final message verbatim as the run's research artifact; downstream implementation and review stages read it from that file.",
390
- `Do not write ${args.researchPath} yourself. Anything written there during this stage is replaced by your final message, so a file you author is lost and a final message that only points at the path leaves later stages with no findings. Skill-owned notes under research/docs/ and research/web/ are unaffected.`,
388
+ "Return the complete research report as your final message. Downstream implementation and review stages read it from there.",
391
389
  "Produce a complete Markdown report with codebase and useful online/contextual findings, implementation guidance, relevant files/tests/docs, unresolved-finding analysis, and validation recommendations. Lead with conclusions; keep facts, caveats, and implementation-relevant next steps; drop background and repetition.",
392
390
  "Before reporting progress, audit each claim against a tool result from this session. Report only work you can point to evidence for; say so explicitly when something is unverified.",
393
391
  "Do not author an RFC/spec or implement code changes.",
@@ -417,6 +415,7 @@ export type RalphWorkflowOptions = {
417
415
  readonly comparisonBaseBranch: string;
418
416
  readonly workflowStartCwd: string;
419
417
  readonly createPr: boolean;
418
+ readonly runId?: string;
420
419
  };
421
420
 
422
421
  export type RalphWorkflowResult = {
@@ -28,7 +28,6 @@ export function renderForkedResearchPromptRefinementPrompt(args: {
28
28
  export function renderForkedResearchPrompt(args: {
29
29
  readonly transformedResearchQuestion: string;
30
30
  readonly latestReviewReportPath: string | undefined;
31
- readonly researchPath: string;
32
31
  }): string {
33
32
  return taggedPrompt([
34
33
  [
@@ -43,9 +42,8 @@ export function renderForkedResearchPrompt(args: {
43
42
  [
44
43
  "research_artifact",
45
44
  [
46
- "Return the rewritten research report for this iteration as your final message. This workflow saves that final message verbatim as the run's research artifact; downstream implementation and review stages read it from that file.",
47
- `Do not write ${args.researchPath} yourself. Anything written there during this stage is replaced by your final message, so a file you author is lost and a final message that only points at the path leaves later stages with no findings. Skill-owned notes under research/docs/ and research/web/ are unaffected.`,
48
- "Restate the still-applicable findings in full rather than referring back to the previous iteration's artifact; it is overwritten by this message.",
45
+ "Return the rewritten research report for this iteration as your final message.",
46
+ "Restate the still-applicable findings in full rather than referring back to the previous iteration's artifact; the current artifact and transcript are the authoritative records for this iteration.",
49
47
  "Do not author an RFC/spec or implement code changes.",
50
48
  ].join("\n"),
51
49
  ],
@@ -1,8 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
- import { mkdtemp } from "node:fs/promises";
3
- import { tmpdir } from "node:os";
4
2
  import { join, resolve } from "node:path";
5
3
  import type { WorkflowRunContext, WorkflowTaskResult } from "../src/shared/types.js";
4
+ import { createWorkflowArtifactDirectory } from "../src/shared/workflow-artifacts.js";
6
5
  import {
7
6
  ACCEPTANCE_MATRIX_CONTRACT,
8
7
  CONTRACT_FIDELITY_AUDIT,
@@ -52,7 +51,7 @@ export async function runRalphWorkflow(
52
51
  ctx: WorkflowRunContext<RalphInputs>,
53
52
  options: RalphWorkflowOptions,
54
53
  ): Promise<RalphWorkflowResult> {
55
- const { prompt, acceptanceCriteria, maxLoops, comparisonBaseBranch, workflowStartCwd, createPr } = options;
54
+ const { prompt, acceptanceCriteria, maxLoops, comparisonBaseBranch, workflowStartCwd, createPr, runId } = options;
56
55
  let latestReviewReportPath: string | undefined;
57
56
  let finalPlan = "";
58
57
  let finalPlanPath = "";
@@ -63,9 +62,9 @@ export async function runRalphWorkflow(
63
62
  const workflowCwdContext = workflowCwdContextSection(workflowStartCwd);
64
63
  const workflowPrompt = prompt;
65
64
  const workflowResearchPath = resolve(workflowStartCwd, defaultResearchPath(workflowPrompt));
66
- const implementationNotesPath = await createImplementationNotesFile(workflowPrompt);
67
- const qaVideoPath = await createQaEvidenceVideoPath();
68
- const artifactDir = await mkdtemp(join(tmpdir(), "atomic-ralph-run-"));
65
+ const implementationNotesPath = await createImplementationNotesFile(workflowPrompt, runId);
66
+ const qaVideoPath = await createQaEvidenceVideoPath(runId);
67
+ const artifactDir = await createWorkflowArtifactDirectory(runId);
69
68
  let approved = false;
70
69
  let iterationsCompleted = 0;
71
70
  let previousResearchPromptRefinementSessionFile: string | undefined;
@@ -98,12 +97,10 @@ export async function runRalphWorkflow(
98
97
  acceptanceCriteria,
99
98
  workflowCwdContext,
100
99
  latestReviewReportPath,
101
- researchPath: workflowResearchPath,
102
100
  })
103
101
  : renderForkedResearchPrompt({
104
102
  transformedResearchQuestion: researchPromptRefinement.text,
105
103
  latestReviewReportPath,
106
- researchPath: workflowResearchPath,
107
104
  }),
108
105
  reads: latestReviewReportPath === undefined ? [] : [latestReviewReportPath],
109
106
  output: workflowResearchPath,
@@ -41,7 +41,7 @@ export default workflow({
41
41
  plan_path: Type.Optional(Type.String({ description: "Backward-compatible alias for research_path." })),
42
42
  research: Type.Optional(Type.String({ description: "Latest research report text or artifact reference." })),
43
43
  research_path: Type.Optional(Type.String({ description: "Path to the latest generated research artifact under research/." })),
44
- implementation_notes_path: Type.Optional(Type.String({ description: "OS-temp notes file containing decisions, deviations, blockers, and validation notes." })),
44
+ implementation_notes_path: Type.Optional(Type.String({ description: "Durable run-scoped notes file containing decisions, deviations, blockers, and validation notes." })),
45
45
  qa_video_path: Type.Optional(Type.String({ description: "Absolute path to the reviewable QA end-to-end proof video recorded with playwright-cli for UI-applicable changes, when one was produced." })),
46
46
  pr_report: Type.Optional(Type.String({ description: "Pull-request report emitted only when create_pr=true and the final pull-request stage runs." })),
47
47
  approved: Type.Optional(Type.Boolean({ description: "Whether the reviewer loop approved before completion or optional final handoff." })),
@@ -72,6 +72,7 @@ export default workflow({
72
72
  comparisonBaseBranch,
73
73
  workflowStartCwd,
74
74
  createPr,
75
+ runId: workflowCtx.runId,
75
76
  });
76
77
  },
77
78
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/workflows",
3
- "version": "0.9.11-alpha.10",
3
+ "version": "0.9.11-alpha.12",
4
4
  "private": true,
5
5
  "description": "Atomic extension for multi-stage workflow authoring and execution.",
6
6
  "contributors": [