@osolmaz/pi-workflows 0.13.4 → 0.15.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 (237) hide show
  1. package/README.md +146 -160
  2. package/dist/builtins/autoimplement.workflow.js +3 -1
  3. package/dist/builtins/autoimplement.workflow.js.map +1 -1
  4. package/dist/builtins/change-verification.workflow.js +7 -2
  5. package/dist/builtins/change-verification.workflow.js.map +1 -1
  6. package/dist/builtins/metadata.d.ts +22 -0
  7. package/dist/builtins/metadata.js +10 -0
  8. package/dist/builtins/metadata.js.map +1 -0
  9. package/dist/builtins/monitor.workflow.js +4 -1
  10. package/dist/builtins/monitor.workflow.js.map +1 -1
  11. package/dist/builtins/sanity-check.workflow.js +4 -1
  12. package/dist/builtins/sanity-check.workflow.js.map +1 -1
  13. package/dist/builtins/workspace-preparation.workflow.js +3 -1
  14. package/dist/builtins/workspace-preparation.workflow.js.map +1 -1
  15. package/dist/controllers/index.d.ts +1 -2
  16. package/dist/controllers/index.js +0 -1
  17. package/dist/controllers/index.js.map +1 -1
  18. package/dist/controllers/sqlite.d.ts +85 -31
  19. package/dist/controllers/sqlite.js +541 -126
  20. package/dist/controllers/sqlite.js.map +1 -1
  21. package/dist/controllers/store.d.ts +1 -0
  22. package/dist/extension/controller-command.d.ts +22 -0
  23. package/dist/extension/controller-command.js +32 -0
  24. package/dist/extension/controller-command.js.map +1 -0
  25. package/dist/extension/index.d.ts +2 -9
  26. package/dist/extension/index.js +876 -2928
  27. package/dist/extension/index.js.map +1 -1
  28. package/dist/extension/message-card.d.ts +13 -0
  29. package/dist/extension/message-card.js +46 -0
  30. package/dist/extension/message-card.js.map +1 -0
  31. package/dist/extension/step-message.d.ts +5 -6
  32. package/dist/extension/step-message.js +41 -52
  33. package/dist/extension/step-message.js.map +1 -1
  34. package/dist/host/child-worker-supervisor.d.ts +50 -0
  35. package/dist/host/child-worker-supervisor.js +159 -0
  36. package/dist/host/child-worker-supervisor.js.map +1 -0
  37. package/dist/host/client.d.ts +48 -0
  38. package/dist/host/client.js +216 -0
  39. package/dist/host/client.js.map +1 -0
  40. package/dist/host/controller-worker-entry.d.ts +2 -0
  41. package/dist/host/controller-worker-entry.js +244 -0
  42. package/dist/host/controller-worker-entry.js.map +1 -0
  43. package/dist/host/controller-worker-protocol.d.ts +36 -0
  44. package/dist/host/controller-worker-protocol.js +49 -0
  45. package/dist/host/controller-worker-protocol.js.map +1 -0
  46. package/dist/host/controller-worker-supervisor.d.ts +21 -0
  47. package/dist/host/controller-worker-supervisor.js +54 -0
  48. package/dist/host/controller-worker-supervisor.js.map +1 -0
  49. package/dist/host/host-entry.d.ts +2 -0
  50. package/dist/host/host-entry.js +23 -0
  51. package/dist/host/host-entry.js.map +1 -0
  52. package/dist/host/processes.d.ts +17 -12
  53. package/dist/host/processes.js +154 -50
  54. package/dist/host/processes.js.map +1 -1
  55. package/dist/host/protocol.d.ts +38 -0
  56. package/dist/host/protocol.js +156 -0
  57. package/dist/host/protocol.js.map +1 -0
  58. package/dist/host/resolver-entry.d.ts +49 -0
  59. package/dist/host/resolver-entry.js +149 -0
  60. package/dist/host/resolver-entry.js.map +1 -0
  61. package/dist/host/rpc-executor.d.ts +14 -3
  62. package/dist/host/rpc-executor.js +63 -30
  63. package/dist/host/rpc-executor.js.map +1 -1
  64. package/dist/host/runner.d.ts +95 -29
  65. package/dist/host/runner.js +2311 -348
  66. package/dist/host/runner.js.map +1 -1
  67. package/dist/host/state.d.ts +174 -0
  68. package/dist/host/state.js +689 -0
  69. package/dist/host/state.js.map +1 -0
  70. package/dist/host/worker-entry.d.ts +10 -0
  71. package/dist/host/worker-entry.js +446 -0
  72. package/dist/host/worker-entry.js.map +1 -0
  73. package/dist/host/worker-protocol.d.ts +31 -0
  74. package/dist/host/worker-protocol.js +122 -0
  75. package/dist/host/worker-protocol.js.map +1 -0
  76. package/dist/host/worker-store.d.ts +80 -0
  77. package/dist/host/worker-store.js +91 -0
  78. package/dist/host/worker-store.js.map +1 -0
  79. package/dist/host/worker-supervisor.d.ts +22 -0
  80. package/dist/host/worker-supervisor.js +54 -0
  81. package/dist/host/worker-supervisor.js.map +1 -0
  82. package/dist/render/canvas.d.ts +2 -1
  83. package/dist/render/canvas.js +40 -15
  84. package/dist/render/canvas.js.map +1 -1
  85. package/dist/render/graph-render.d.ts +7 -3
  86. package/dist/render/graph-render.js +125 -74
  87. package/dist/render/graph-render.js.map +1 -1
  88. package/dist/state/database.js +3 -1
  89. package/dist/state/database.js.map +1 -1
  90. package/dist/state/index.d.ts +1 -0
  91. package/dist/state/index.js +1 -0
  92. package/dist/state/index.js.map +1 -1
  93. package/dist/state/prune.js +36 -10
  94. package/dist/state/prune.js.map +1 -1
  95. package/dist/state/schema.d.ts +1 -1
  96. package/dist/state/schema.js +171 -3
  97. package/dist/state/schema.js.map +1 -1
  98. package/dist/state/viewer.d.ts +46 -0
  99. package/dist/state/viewer.js +249 -0
  100. package/dist/state/viewer.js.map +1 -0
  101. package/dist/viewer/cli.d.ts +1 -1
  102. package/dist/viewer/cli.js +47 -18
  103. package/dist/viewer/cli.js.map +1 -1
  104. package/dist/workflows/composition.js +25 -2
  105. package/dist/workflows/composition.js.map +1 -1
  106. package/dist/workflows/definition.d.ts +3 -1
  107. package/dist/workflows/definition.js +25 -0
  108. package/dist/workflows/definition.js.map +1 -1
  109. package/dist/workflows/engine.d.ts +1 -0
  110. package/dist/workflows/engine.js +157 -42
  111. package/dist/workflows/engine.js.map +1 -1
  112. package/dist/workflows/errors.d.ts +3 -1
  113. package/dist/workflows/errors.js +4 -7
  114. package/dist/workflows/errors.js.map +1 -1
  115. package/dist/workflows/human-decision.d.ts +3 -0
  116. package/dist/workflows/human-decision.js +31 -0
  117. package/dist/workflows/human-decision.js.map +1 -1
  118. package/dist/workflows/index.d.ts +1 -1
  119. package/dist/workflows/index.js +1 -1
  120. package/dist/workflows/index.js.map +1 -1
  121. package/dist/workflows/schema.js +17 -6
  122. package/dist/workflows/schema.js.map +1 -1
  123. package/dist/{viewer → workflows}/session-reducer.d.ts +3 -1
  124. package/dist/{viewer → workflows}/session-reducer.js +4 -0
  125. package/dist/workflows/session-reducer.js.map +1 -0
  126. package/dist/workflows/store.d.ts +87 -1
  127. package/dist/workflows/store.js +892 -93
  128. package/dist/workflows/store.js.map +1 -1
  129. package/dist/workflows/tool-input.d.ts +0 -22
  130. package/dist/workflows/tool-input.js +1 -44
  131. package/dist/workflows/tool-input.js.map +1 -1
  132. package/dist/workflows/types.d.ts +36 -4
  133. package/docs/2026-08-25-workflow-follow-ups.md +8 -6
  134. package/docs/2026-08-30-out-of-process-workflow-host-plan.md +353 -0
  135. package/docs/CONTROLLERS.md +11 -11
  136. package/docs/DEFERRED_TURNS.md +61 -27
  137. package/docs/HUMAN_DECISIONS.md +12 -4
  138. package/docs/SQLITE_STATE.md +42 -8
  139. package/docs/WORKFLOW_HOST.md +452 -0
  140. package/docs/development.md +46 -30
  141. package/docs/live-replay-protocol.md +129 -100
  142. package/docs/plans/2026-08-19-human-decision-gates-plan.md +34 -8
  143. package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +399 -0
  144. package/docs/plans/2026-08-28-piw-incremental-viewer-plan.md +293 -0
  145. package/docs/plans/piw-viewer-experience-implementation-plan.md +11 -1
  146. package/docs/plans/tui-viewer-implementation-plan.md +5 -0
  147. package/docs/tui-viewer.md +18 -3
  148. package/docs/workflows.md +164 -156
  149. package/examples/workflows/command-batch.workflow.ts +2 -0
  150. package/examples/workflows/shell.workflow.ts +2 -1
  151. package/herdr-plugin.toml +1 -1
  152. package/package.json +1 -1
  153. package/skills/autodoc/SKILL.md +1 -1
  154. package/skills/autoimplement/SKILL.md +1 -1
  155. package/skills/autoplan/SKILL.md +1 -1
  156. package/skills/pi-workflows/SKILL.md +2 -0
  157. package/src/builtins/autoimplement.workflow.ts +3 -0
  158. package/src/builtins/change-verification.workflow.ts +7 -2
  159. package/src/builtins/metadata.ts +9 -0
  160. package/src/builtins/monitor.workflow.ts +4 -0
  161. package/src/builtins/sanity-check.workflow.ts +4 -0
  162. package/src/builtins/workspace-preparation.workflow.ts +3 -1
  163. package/src/controllers/index.ts +3 -5
  164. package/src/controllers/sqlite.ts +929 -225
  165. package/src/controllers/store.ts +1 -0
  166. package/src/extension/controller-command.ts +45 -0
  167. package/src/extension/index.ts +948 -3506
  168. package/src/extension/message-card.ts +61 -0
  169. package/src/extension/step-message.ts +58 -63
  170. package/src/host/child-worker-supervisor.ts +183 -0
  171. package/src/host/client.ts +293 -0
  172. package/src/host/controller-worker-entry.ts +311 -0
  173. package/src/host/controller-worker-protocol.ts +104 -0
  174. package/src/host/controller-worker-supervisor.ts +79 -0
  175. package/src/host/host-entry.ts +23 -0
  176. package/src/host/processes.ts +171 -54
  177. package/src/host/protocol.ts +196 -0
  178. package/src/host/resolver-entry.ts +241 -0
  179. package/src/host/rpc-executor.ts +76 -34
  180. package/src/host/runner.ts +2813 -422
  181. package/src/host/state.ts +1160 -0
  182. package/src/host/worker-entry.ts +533 -0
  183. package/src/host/worker-protocol.ts +165 -0
  184. package/src/host/worker-store.ts +229 -0
  185. package/src/host/worker-supervisor.ts +74 -0
  186. package/src/render/canvas.ts +44 -10
  187. package/src/render/graph-render.ts +145 -90
  188. package/src/state/database.ts +2 -1
  189. package/src/state/index.ts +14 -0
  190. package/src/state/prune.ts +35 -9
  191. package/src/state/schema.ts +171 -3
  192. package/src/state/viewer.ts +356 -0
  193. package/src/viewer/cli.ts +49 -17
  194. package/src/workflows/composition.ts +36 -2
  195. package/src/workflows/definition.ts +32 -0
  196. package/src/workflows/engine.ts +157 -54
  197. package/src/workflows/errors.ts +11 -2
  198. package/src/workflows/human-decision.ts +49 -0
  199. package/src/workflows/index.ts +2 -0
  200. package/src/workflows/schema.ts +19 -6
  201. package/src/{viewer → workflows}/session-reducer.ts +11 -1
  202. package/src/workflows/store.ts +1316 -108
  203. package/src/workflows/tool-input.ts +2 -60
  204. package/src/workflows/types.ts +32 -4
  205. package/dist/controllers/workflow-engine-scheduler.d.ts +0 -29
  206. package/dist/controllers/workflow-engine-scheduler.js +0 -175
  207. package/dist/controllers/workflow-engine-scheduler.js.map +0 -1
  208. package/dist/extension/controller-host.d.ts +0 -48
  209. package/dist/extension/controller-host.js +0 -110
  210. package/dist/extension/controller-host.js.map +0 -1
  211. package/dist/extension/deferred-turn-coordinator.d.ts +0 -32
  212. package/dist/extension/deferred-turn-coordinator.js +0 -143
  213. package/dist/extension/deferred-turn-coordinator.js.map +0 -1
  214. package/dist/extension/deferred-turn.d.ts +0 -44
  215. package/dist/extension/deferred-turn.js +0 -110
  216. package/dist/extension/deferred-turn.js.map +0 -1
  217. package/dist/extension/executor.d.ts +0 -86
  218. package/dist/extension/executor.js +0 -311
  219. package/dist/extension/executor.js.map +0 -1
  220. package/dist/extension/follow-up-coordinator.d.ts +0 -27
  221. package/dist/extension/follow-up-coordinator.js +0 -131
  222. package/dist/extension/follow-up-coordinator.js.map +0 -1
  223. package/dist/extension/recorder.d.ts +0 -84
  224. package/dist/extension/recorder.js +0 -528
  225. package/dist/extension/recorder.js.map +0 -1
  226. package/dist/extension/session-events.d.ts +0 -133
  227. package/dist/extension/session-events.js +0 -61
  228. package/dist/extension/session-events.js.map +0 -1
  229. package/dist/viewer/session-reducer.js.map +0 -1
  230. package/src/controllers/workflow-engine-scheduler.ts +0 -246
  231. package/src/extension/controller-host.ts +0 -167
  232. package/src/extension/deferred-turn-coordinator.ts +0 -171
  233. package/src/extension/deferred-turn.ts +0 -166
  234. package/src/extension/executor.ts +0 -411
  235. package/src/extension/follow-up-coordinator.ts +0 -151
  236. package/src/extension/recorder.ts +0 -655
  237. package/src/extension/session-events.ts +0 -121
package/README.md CHANGED
@@ -4,25 +4,17 @@
4
4
  <img src="assets/cover.svg" alt="pi-workflows: a representative multi-step workflow graph with plan, implement, verify, review, a fix loop, and a clean finish" width="880">
5
5
  </p>
6
6
 
7
- pi-workflows is a workflow extension for the [pi coding agent](https://pi.dev).
8
- It lets you define multi-step agent workflows as TypeScript graphs, trigger
9
- them at any point in a pi conversation with `/workflow`, and watch them run
10
- live in a standalone terminal viewer.
11
-
12
- The workflow model is a port of [openclaw/acpx](https://github.com/openclaw/acpx)
13
- flows into pi itself. Agent steps run inside your current pi conversation, so
14
- the model keeps everything it already knows from the discussion. A submitted
15
- agent calls the JSON `workflow` tool with structured output. An assistant agent
16
- writes a normal visible response that becomes the node output. See the [design
17
- philosophy](docs/DESIGN_PHILOSOPHY.md) for the principles behind the engine and
18
- its public parts. Running steps can publish durable [workflow
19
- updates](docs/WORKFLOW_UPDATES.md), including progress counts and ETA data.
20
- Agent instructions use compact [workflow step
21
- messages](docs/WORKFLOW_STEP_MESSAGES.md). Workflows can expose [settings that
22
- change during a run](docs/2026-08-25-workflow-settings.md) and queue [normal
23
- follow-up work after completion](docs/2026-08-25-workflow-follow-ups.md). The
24
- built-in [monitor](docs/MONITOR.md) reports every check without starting an
25
- extra assistant turn.
7
+ pi-workflows is a workflow extension for the [Pi coding agent](https://pi.dev).
8
+ It lets you define multi-step agent workflows as TypeScript graphs and trigger
9
+ them at any point in a Pi conversation with `/workflow`. A standalone terminal
10
+ viewer shows each run live.
11
+
12
+ Agent steps run inside your current Pi conversation, so the model keeps
13
+ everything it already knows from the discussion. A submitted agent step
14
+ returns structured output through the JSON `workflow` tool, while an
15
+ assistant step writes a normal visible response that becomes the node
16
+ output. The [design philosophy](docs/DESIGN_PHILOSOPHY.md) explains the
17
+ principles behind the engine and its public parts.
26
18
 
27
19
  ## Install
28
20
 
@@ -49,7 +41,7 @@ The Pi package includes the extension and six optional skills:
49
41
  - `autoplan` selects the best practical solution and writes an implementation plan.
50
42
  - `autodoc` records an existing plan in canonical documentation.
51
43
  - `autoimplement` implements an existing plan and verifies the result.
52
- - `sanity-check` reviews whether a contribution is necessary, focused, and well supported.
44
+ - `sanity-check` reviews whether a contribution is necessary, and whether it is focused and well supported.
53
45
 
54
46
  Pi discovers these skills when it loads the package. Use `pi config` to disable
55
47
  the extension, all bundled skills, or one skill independently. The equivalent
@@ -70,7 +62,7 @@ Set `"skills": []` to disable all bundled skills while keeping the extension.
70
62
  Set `"extensions": []` to keep the skills without loading the extension.
71
63
 
72
64
  Install the interactive terminal viewer separately from crates.io. The crate
73
- is named `pi-workflows`; its command is `piw`:
65
+ is named `pi-workflows` and installs the `piw` command:
74
66
 
75
67
  ```bash
76
68
  cargo install pi-workflows
@@ -88,35 +80,10 @@ All live workflow and controller state uses one local database:
88
80
  ```
89
81
 
90
82
  Runs, decisions, queues, claims, controllers, session capture, notifications,
91
- channel transport state, effects, and large text values share that database.
92
- Reads are read-only. Every write checks its actor, expected revision, and owner
93
- lease when required. See [SQLite state](docs/SQLITE_STATE.md).
94
-
95
- ## Herdr integration
96
-
97
- pi-workflows also ships as a [Herdr](https://herdr.dev) plugin. After installing
98
- `piw` and pi-workflows, synchronize the bundled plugin:
99
-
100
- ```bash
101
- pi-workflows herdr sync
102
- ```
103
-
104
- Run the same command after a pi-workflows update. It finds the package that
105
- provides the running CLI and repairs a Herdr link when npm moved that package.
106
- `pi-workflows herdr setup` remains an alias for existing installations. Use
107
- `--json` for versioned machine-readable output. The [Herdr plugin sync
108
- plan](docs/plans/2026-08-20-herdr-plugin-sync-plan.md) defines update and
109
- recovery behavior.
110
-
111
- When Pi runs inside Herdr, a workflow widget shows `Ctrl+Shift+R piw`. When the widget has hidden rows, this call to action shares the existing scroll-controls line instead of taking another line.
112
- The shortcut opens the exact SQLite run state and lets you choose a split, tab, or new
113
- workspace. `/piw` opens the same menu, and `/piw right`, `/piw below`, `/piw
114
- left`, `/piw above`, `/piw tab`, or `/piw workspace` selects a placement
115
- directly. If a viewer for that run already exists, pi-workflows focuses it
116
- instead of opening a duplicate.
117
-
118
- The plugin uses Herdr's public pane APIs and runs no service or polling loop. It
119
- is also available through the [Herdr plugin marketplace](https://herdr.dev/plugins/).
83
+ channel transport state, effects, and large text values all live there.
84
+ Viewers open the database read-only, and every write checks its actor,
85
+ expected revision, and owner lease when required. See
86
+ [SQLite state](docs/SQLITE_STATE.md).
120
87
 
121
88
  ## Quick start
122
89
 
@@ -129,7 +96,6 @@ import { agent, defineWorkflow } from "@osolmaz/pi-workflows";
129
96
 
130
97
  export default defineWorkflow({
131
98
  name: "echo",
132
- presentationPrompt: "Give the user the concise reply from the workflow result.",
133
99
  startAt: "reply",
134
100
  nodes: {
135
101
  reply: agent({
@@ -141,37 +107,26 @@ export default defineWorkflow({
141
107
  });
142
108
  ```
143
109
 
144
- Then, from any pi conversation:
110
+ Then, from any Pi conversation:
145
111
 
146
112
  ```
147
113
  /workflow echo summarize this repository
148
114
  ```
149
115
 
150
- A model-started workflow is saved before the tool reports it as queued. The returned run ID works
151
- with `workflow status` and `workflow cancel` before execution starts. pi-workflows waits for the
152
- current agent turn to settle before activation. If activation fails, it saves the failure and sends
153
- one follow-up turn so the model can correct the cause and start a new run.
154
-
155
116
  `/workflow` with no arguments lists discovered workflows. `/workflow pause`
156
- lets the current step finish and then holds the run before the next node. This
157
- is useful when you want to interject in the conversation mid-workflow.
158
- `/workflow resume` continues it. Pressing escape to interrupt a turn
159
- pauses the workflow automatically, so the run never nudges the model while
160
- you have taken the conversation back; `/workflow resume` re-delivers the
161
- pending step prompt. `/workflow cancel` stops the active run; if the last run
162
- already ended (for example parked at a checkpoint), it clears the leftover
163
- widget instead. Trailing text becomes `{ task: "..." }`; pass arbitrary input
164
- with `--input-json {"key": "value"}`. The names `answer`, `cancel`, `list`, `pause`, `resume`, and `status` are
165
- reserved and rejected as workflow names.
166
-
167
- While a run is on screen, the footer status bar shows a compact
168
- `wf <name> [status] <node>` indicator alongside the widget.
169
-
170
- `presentationPrompt` is optional. When present, pi-workflows uses it after the
171
- structured run ends to request one normal, human-readable assistant response.
172
- Without it, pi-workflows does not request a separate final response. If the
173
- model writes text after submitting its last agent step, that text stays visible.
174
- Shell-only and machine-consumed workflows remain model-free.
117
+ stops the worker and parks the run at its last durable boundary. `/workflow
118
+ resume` starts a new worker generation from that boundary. `/workflow cancel`
119
+ stops the active run; `/workflow cancel <run-id>` can also cancel a named stale
120
+ run when no live owner holds it. A checkpoint waits until `/workflow answer
121
+ <json-or-text>` supplies its input.
122
+
123
+ Trailing text becomes `{ task: "..." }`, and `--input-json {"key": "value"}`
124
+ passes arbitrary input. The names `answer`, `cancel`, `list`, `pause`, `resume`,
125
+ and `status` are reserved and rejected as workflow names.
126
+
127
+ A workflow can also expose [settings that change during a
128
+ run](docs/2026-08-25-workflow-settings.md) and queue [normal follow-up work
129
+ after completion](docs/2026-08-25-workflow-follow-ups.md).
175
130
 
176
131
  Use `expectedOutput: assistantMessage()` when a normal assistant response must
177
132
  be a node inside the graph rather than a presentation after the run. Its exact
@@ -179,7 +134,7 @@ visible text becomes the node output after the turn settles. The helper has no
179
134
  default character limit; a workflow can set one explicitly with
180
135
  `assistantMessage({ maxChars: 2_000 })`.
181
136
 
182
- ## Compose workflows
137
+ ## Workflow composition
183
138
 
184
139
  A workflow can import another workflow and connect its named exits without copying its nodes:
185
140
 
@@ -208,42 +163,60 @@ export default defineWorkflow({
208
163
  });
209
164
  ```
210
165
 
211
- Direct imports check child input and exit names in TypeScript. Names and paths remain available for dynamic discovery. Nested children share one run, trace, pause state, and cancellation state. See [Workflow composition](docs/WORKFLOW_COMPOSITION.md) for the complete contract.
166
+ Direct imports check child input and exit names in TypeScript, while names and
167
+ paths remain available for dynamic discovery. Nested children share one run,
168
+ trace, pause state, and cancellation state. See
169
+ [Workflow composition](docs/WORKFLOW_COMPOSITION.md) for the complete contract.
212
170
 
213
171
  ## Agent-managed workflows
214
172
 
215
- The model can use the same `workflow` tool to list, start, inspect, pause,
216
- resume, cancel, and answer workflows. Submitted-step contracts use the tool's
217
- `submit` action. Assistant-step contracts require a normal assistant response
218
- instead. Slash commands and model actions share one lifecycle implementation.
173
+ The model can use the `workflow` tool to list, start, inspect, pause, resume,
174
+ cancel, and answer ordinary checkpoints. It cannot answer a protected human
175
+ decision. A person must answer that request through `/workflow answer` in the
176
+ origin Pi session. The model uses `update` for durable progress from the current
177
+ attempt and `submit` for structured step output. Assistant-message steps require
178
+ a normal visible assistant response instead. The extension sends all lifecycle
179
+ mutations to the host and reports success only after the host commits them.
219
180
 
220
- pi-workflows includes a `monitor` workflow for plain-language requests such as:
181
+ A model-started workflow is saved before the tool reports it as accepted, so
182
+ the returned run ID works with `workflow status` and `workflow cancel`
183
+ immediately. Duplicate host commands and step submissions adopt their stored
184
+ receipts instead of repeating a committed transition.
185
+
186
+ pi-workflows includes a [monitor](docs/MONITOR.md) workflow for plain-language
187
+ requests such as:
221
188
 
222
189
  > Monitor PR 123 every 30 minutes. Report failed checks. Stop when it is merged or closed.
223
190
 
224
191
  The monitor checks immediately, reports only the states requested by the user,
225
192
  waits with a normal shell action, and loops until its stop condition or check
226
- limit. Its input supports `task`, `everyMinutes`, `stopWhen`, `maxChecks`, and
227
- an optional `checkTimeoutMinutes`.
228
-
229
- Monitor is observation-only by default. An explicit `repair` policy authorizes its composed `autoplan` and `autoimplement` path. The monitor checks the target again after repair and stops when the same issue and target evidence return without progress. Project and global workflows can replace the built-in `monitor` by using the same file name.
230
-
231
- A monitor occupies the session's one active workflow slot. If its Pi runner
232
- stops during the shell wait, the run parks and repeats that wait node when a
233
- runner resumes it.
234
-
235
- Because the workflow runs in your current conversation, you can have a long
236
- discussion first and then trigger a workflow that builds on it. The
237
- `autoplan` example does exactly that. It frames the problem and scope, devises
238
- an elegant production-ready solution, and compares it with the holy grail. It
193
+ limit. Every check is reported without starting an extra assistant turn. Its
194
+ input supports `task`, `everyMinutes`, `stopWhen`, `maxChecks`, and an
195
+ optional `checkTimeoutMinutes`.
196
+
197
+ Monitor is observation-only by default. An explicit `repair` policy authorizes
198
+ its composed `autoplan` and `autoimplement` path. The monitor checks the
199
+ target again after repair and stops when the same issue and target evidence
200
+ return without progress. Project and global workflows can replace the built-in
201
+ `monitor` by using the same file name.
202
+
203
+ A monitor occupies the session's one active workflow slot. If its worker or the
204
+ host stops during the shell wait, the run parks and repeats that idempotent wait
205
+ node when the host resumes it.
206
+
207
+ Because interactive agent steps run in the origin conversation, you can have a
208
+ long discussion first and then trigger a workflow that builds on it. The
209
+ `autoplan` example does exactly that. It frames the problem and scope, then
210
+ devises an elegant production-ready solution and compares it with the holy
211
+ grail. It
239
212
  then selects the best practical in-scope solution without asking the user to
240
213
  resolve the gap. The ideal can win when it is feasible, but work outside the
241
214
  current authority cannot block a valid practical solution. The workflow keeps
242
215
  the detailed implementation plan and shows one short assistant response with
243
216
  the selected plan and a gist of every rejected option. `autoplan` replaces the
244
- earlier `autodevise` name; the old command and export are not retained.
217
+ earlier `autodevise` name, and the old command and export are not retained.
245
218
 
246
- ## Watching a run
219
+ ## Viewers
247
220
 
248
221
  Runs persist in `~/.pi/agent/workflows/state.sqlite` as they execute. The
249
222
  viewer reads that database and re-renders on every state change:
@@ -255,74 +228,68 @@ pi-workflows runs # plain list of recent runs
255
228
  pi-workflows view --once # print a snapshot and exit (good for scripts)
256
229
  ```
257
230
 
258
- The run detail view draws the workflow as a boxed graph, like the acpx replay
259
- viewer. Included nodes use hierarchical labels such as `implementation redesign plan`. Every card has a centered step-name header and a divider above its
260
- structured metadata. Border characters keep the graph background, the body
261
- surface begins inside the border, and the header interior uses a separate
262
- surface. Node type, status, attempts, and timing use compact symbol rows; start
263
- and terminal markers sit outside the card. Node types have distinct
264
- semantic colors, active cards use a heavy border, branches carry their case
265
- labels, the taken path is highlighted, and loops route through a gutter on the
266
- right back into their target from above. `←/→` scrubs
267
- backwards and forwards through the recorded steps and re-derives every node's
268
- status as of that step, with the selected step's full output shown below;
269
- scrubbing to the end snaps back to following the run live.
270
-
271
- The Rust `piw` viewer under `tui/` adds a Catppuccin interface, selectable
272
- themes, centered active-node following, draggable browser and inspector sizes,
273
- detailed trace and conversation inspection, temporal replay, and reconnecting
274
- remote viewing. Full cards have one fixed graph-wide size, so streaming,
275
- selection, timer ticks, and replay never move nodes or edges. Live conversation
276
- capture shows text, thinking, tool calls, and tool execution as they happen,
277
- then reconciles settled messages to verbatim Pi entries. See
231
+ The run detail view draws the workflow as a boxed graph. `←/→` replays the
232
+ recorded steps with each step's full output, and scrubbing to the end snaps
233
+ back to following the run live.
234
+
235
+ The Rust `piw` viewer under `tui/` is the full interactive terminal UI, with
236
+ selectable themes, detailed trace and conversation inspection, temporal
237
+ replay, and reconnecting remote viewing. See
278
238
  [the piw guide](docs/tui-viewer.md).
279
239
 
240
+ Inside Pi, a compact widget above the editor shows one line per workflow node,
241
+ with glyphs for node status and type. Scroll it with `shift+↑` / `shift+↓`.
242
+ Use `piw` when you need the full boxed graph and its edges.
243
+
244
+ ## Herdr integration
245
+
246
+ pi-workflows also ships as a [Herdr](https://herdr.dev) plugin. After installing
247
+ `piw` and pi-workflows, synchronize the bundled plugin:
248
+
249
+ ```bash
250
+ pi-workflows herdr sync
280
251
  ```
281
- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
282
- ┃ review ┃
283
- ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
284
- ┃ ● agent ◐ running ┃
285
- ┃ ↻ 2 ◷ 12s ┃
286
- ┃ ◇ clean ┃
287
- ┃ ◇ issues_found ┃
288
- ┃ … reviewing implementation ┃
289
- ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
290
- ```
291
252
 
292
- Inside pi, a compact widget above the editor shows one line per workflow node.
293
- The first glyph is the node status. The second glyph is the node type: `●`
294
- agent, `ƒ` compute, `!` notification, `$` shell action, `*` function action, or
295
- `◆` checkpoint. Repeated visits, runtime details, and timing appear on the same
296
- line when they apply. Pi's current theme highlights the full active-node line,
297
- while status glyphs keep every state readable without color. Long workflows are
298
- windowed around the active node.
299
- Scroll the list with `shift+↑` / `shift+↓`; it snaps back to following the
300
- active node whenever the workflow advances a step. Use `piw` when you need the
301
- full boxed graph and its edges.
253
+ Run the same command after a pi-workflows update. `pi-workflows herdr setup`
254
+ remains an alias for existing installations.
255
+
256
+ When Pi runs inside Herdr, the workflow widget shows a `Ctrl+Shift+R piw`
257
+ shortcut. The shortcut opens the exact SQLite run state and lets you choose a
258
+ split, tab, or new workspace. `/piw` opens the same menu, and `/piw right`,
259
+ `/piw below`, `/piw left`, `/piw above`, `/piw tab`, or `/piw workspace`
260
+ selects a placement directly. If a viewer for that run already exists,
261
+ pi-workflows focuses it instead of opening a duplicate.
262
+
263
+ The plugin uses Herdr's public pane APIs and runs no service or polling loop. It
264
+ is also available through the [Herdr plugin marketplace](https://herdr.dev/plugins/).
302
265
 
303
266
  ## Node types
304
267
 
305
268
  A workflow is a graph of named nodes with exactly one entry point. Each node
306
269
  finishes with an output, and edges decide what runs next.
307
270
 
308
- An `agent` node sends a prompt into the pi conversation. By default, it waits
271
+ An `agent` node sends a prompt into the origin Pi conversation as a compact
272
+ [workflow step message](docs/WORKFLOW_STEP_MESSAGES.md). By default, it waits
309
273
  for structured output through the `workflow` tool. With
310
274
  `expectedOutput: assistantMessage()`, it waits for a normal visible assistant
311
275
  response and uses the exact text as its output. A `compute` node runs a pure
312
276
  TypeScript function. A `notify` node writes a durable message for the Pi
313
- session that started the run. An `action` node performs a side effect, either a
314
- TypeScript function (`action({ run })`) or a runtime-owned shell command
315
- (`shell({ exec, parse })`). A `checkpoint` node ends the run in a `waiting`
316
- state so a human can pick it up. On top of `agent`, the `decision` helper asks
277
+ session that started the run. An `action` or `shell` node must declare an
278
+ `idempotentEffect(...)` or `manualEffect(...)` recovery contract before it can
279
+ perform a side effect. A `checkpoint` node ends the run in a `waiting` state so
280
+ a human can pick it up. On top of `agent`, the `decision` helper asks
317
281
  the model to pick from a fixed set of choices and validates the answer, and
318
282
  `decisionEdge` routes on the result with compile-time case checking.
319
283
 
284
+ Running steps can publish durable [workflow updates](docs/WORKFLOW_UPDATES.md),
285
+ including progress counts and ETA data.
286
+
320
287
  See [docs/workflows.md](docs/workflows.md) for the full authoring reference
321
288
  and [docs/SQLITE_STATE.md](docs/SQLITE_STATE.md) for the on-disk run format.
322
289
 
323
290
  ## Controllers
324
291
 
325
- Controllers keep long-running automation aligned with current external state. They store desired state in `spec`, report observed state through conditions and `status`, and reconcile a deduplicated resource key whenever an event or retry makes it ready.
292
+ Controllers keep long-running automation aligned with current external state. They store desired state in `spec` and report observed state through conditions and `status`, then reconcile a deduplicated resource key whenever an event or retry makes it ready.
326
293
 
327
294
  Put `*.controller.ts` files in `.pi/controllers/` or `~/.pi/agent/controllers/`. Import the API from `@osolmaz/pi-workflows/controllers`:
328
295
 
@@ -348,21 +315,36 @@ Apply and inspect resources from Pi:
348
315
  /controller reconcile example item-1
349
316
  ```
350
317
 
351
- The standalone CLI provides read-only views with `pi-workflows controllers` and `pi-workflows controller <controller> <key>`. See [docs/CONTROLLERS.md](docs/CONTROLLERS.md) for reconciliation, queue, effect, and child workflow semantics.
318
+ The extension resolves controller initialization in a child process, then sends the declarative resource to the global host. Controller reconciliation also runs in supervised children. The standalone CLI provides read-only views with `pi-workflows controllers` and `pi-workflows controller <controller> <key>`. See [docs/CONTROLLERS.md](docs/CONTROLLERS.md) for reconciliation, queue, effect, and child workflow semantics.
352
319
 
353
320
  ## Always-on workflows
354
321
 
355
- Runs do not depend on the Pi window. Every `/workflow` run is claimed through a durable queue, so closing Pi mid-run **parks** the run instead of cancelling it. Another interactive session cannot claim it. Reopening the exact session that started the run resumes it. A standalone host can also resume it without changing where reports go. A checkpointed run waits durably until you answer it with `/workflow answer <json>`, which continues the graph in a linked run.
322
+ Every workflow enters one durable global queue. The extension starts the
323
+ package-owned host on demand. Closing Pi does not stop a compute, action, or
324
+ shell node. When a run reaches an interactive agent, assistant-message, or
325
+ human-decision step, the host parks it and saves a request for the origin Pi
326
+ session. Reopening that session presents the same request once. A protected
327
+ human decision does not start a model turn; a person answers it with
328
+ `/workflow answer`.
356
329
 
357
- Workflow reports use a durable session-addressed outbox. A report waits while its starting session is closed and is delivered only to that session when it opens again. Runs in the same database do not broadcast messages to each other's conversations.
330
+ The host also reconciles controllers. Controller child workflows without an
331
+ origin session can use headless `pi --mode rpc` agent steps. A child that needs
332
+ a visible assistant response must have an origin-session binding.
358
333
 
359
- For runs that must continue while Pi is closed, keep the standalone host running:
334
+ Use the CLI to inspect or control the on-demand process:
360
335
 
361
336
  ```bash
362
- pi-workflows host --project /path/to/project
337
+ pi-workflows host start
338
+ pi-workflows host status
339
+ pi-workflows host stop
340
+ pi-workflows host run # stay attached; stop with Ctrl-C
363
341
  ```
364
342
 
365
- The host claims parked runs and reconciles controllers without a Pi session. Conversation nodes execute in headless `pi --mode rpc` children that expose the same `workflow` tool contract. It is a foreground process — stop it with Ctrl-C; a crashed host's leftovers are reaped by the next one. See [docs/workflows.md](docs/workflows.md#durable-runs-parking-and-resume) for the model and [docs/SQLITE_STATE.md](docs/SQLITE_STATE.md) for the on-disk rules.
343
+ These commands manage one host for the complete user database, not one host per
344
+ project. They do not install an operating-system service. A new host reaps exact
345
+ orphan process identities and resumes safe work from committed state. See
346
+ [docs/workflows.md](docs/workflows.md#durable-runs-parking-and-resume) and
347
+ [docs/WORKFLOW_HOST.md](docs/WORKFLOW_HOST.md).
366
348
 
367
349
  ## Examples
368
350
 
@@ -373,8 +355,7 @@ workflow examples. Copy any of them into `.pi/workflows/` to use them:
373
355
  - `branch` classifies a task with a `decision` and routes to either a
374
356
  continue lane or a clarification checkpoint.
375
357
  - `shell` runs a runtime-owned shell command and parses its output, with no
376
- agent step at all. Shell and function actions can publish durable progress
377
- while they run.
358
+ agent step at all.
378
359
  - `two-turn` chains three agent steps that build on each other's outputs in
379
360
  the same conversation.
380
361
  - `plain-summary` turns structured source data into one visible assistant
@@ -386,10 +367,10 @@ workflow examples. Copy any of them into `.pi/workflows/` to use them:
386
367
  - `autoimplement` finds a clear existing plan, prepares a safe branch or
387
368
  worktree before mutation, documents it when needed, and verifies the current
388
369
  change against eligible base-branch failures. It writes and runs the exact
389
- pi-reviewer command, tracks P0 through P2, handles PR comments and CI, and
390
- finalizes the PR. P0 and P1 fixes require another review. P2-only work is
391
- verified without another reviewer round. A five-minute CI wait routes to
392
- additional useful local testing. New evidence can route through autoplan
370
+ pi-reviewer command and tracks P0 through P2 findings, then handles PR
371
+ comments and CI and finalizes the PR. P0 and P1 fixes require another review, while P2-only work
372
+ is verified without another reviewer round. A five-minute CI wait routes to
373
+ additional useful local testing, and new evidence can route through autoplan
393
374
  and autodoc before implementation resumes.
394
375
  - `human-decision` shows a reusable verified-human gate with a structured
395
376
  machine subject, a separate readable operator presentation, plain choices,
@@ -397,17 +378,22 @@ workflow examples. Copy any of them into `.pi/workflows/` to use them:
397
378
  - `approved-plan` includes the shared plan-change workflow, which composes
398
379
  autoplan, autodoc, the configurable plan decision, and bounded replanning.
399
380
  - `autoresearch` runs an iterative feature-search loop in the style of
400
- [karpathy/autoresearch](https://github.com/karpathy/autoresearch): setup
381
+ [karpathy/autoresearch](https://github.com/karpathy/autoresearch). Setup
401
382
  creates a frozen evaluation harness, one editable feature file, and a
402
- journal; each loop iteration runs one generation of experiments and
403
- journals every result; an assess decision keeps looping until a kept
404
- result plateaus or a diverse generation all fails, then conclusions are
383
+ journal. Each loop iteration then runs one generation of experiments and
384
+ journals every result, and an assess decision keeps looping until a kept
385
+ result plateaus or a diverse generation all fails. Conclusions are
405
386
  written before the winner is promoted out of the loop directory.
406
387
 
407
388
  The controller example at `examples/controllers/pull-request.controller.ts`
408
389
  shows child repair work and check polling. It also uses expected-head guards
409
390
  and recoverable merge effects.
410
391
 
392
+ ## Origins
393
+
394
+ The workflow model was originally ported from
395
+ [openclaw/acpx](https://github.com/openclaw/acpx) flows.
396
+
411
397
  ## License
412
398
 
413
399
  [MIT](LICENSE)
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { runCommandBatch, } from "../workflows/command-batch.js";
3
- import { action, agent, compute, defineWorkflow, includeWorkflow, includedResult, } from "../workflows/definition.js";
3
+ import { action, agent, compute, defineWorkflow, includeWorkflow, includedResult, manualEffect, } from "../workflows/definition.js";
4
4
  import { digest } from "../workflows/human-decision.js";
5
5
  import { allowSettingsPath, workflowSettings } from "../workflows/settings.js";
6
6
  import autodocWorkflow, {} from "./autodoc.workflow.js";
@@ -1413,6 +1413,7 @@ export const autoimplementWorkflow = defineWorkflow({
1413
1413
  run: selectReviewCommands,
1414
1414
  }),
1415
1415
  runReview: action({
1416
+ effect: manualEffect("pi-workflows.autoimplement.review"),
1416
1417
  statusDetail: "running pi-reviewer commands",
1417
1418
  timeoutMs: (context) => {
1418
1419
  const selected = latestOutput(context, ["selectReviewCommands"]);
@@ -1517,6 +1518,7 @@ export const autoimplementWorkflow = defineWorkflow({
1517
1518
  validate: parseCiInspectionForPublished,
1518
1519
  }),
1519
1520
  trackCi: action({
1521
+ effect: manualEffect("pi-workflows.autoimplement.track-ci"),
1520
1522
  statusDetail: "tracking pending CI commands",
1521
1523
  timeoutMs: (context) => {
1522
1524
  const inspected = latestOutput(context, ["inspectCi"]);