@osolmaz/pi-workflows 0.16.5 → 0.16.6

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 (171) hide show
  1. package/README.md +5 -5
  2. package/dist/builtins/autoimplement.workflow.d.ts +5 -1
  3. package/dist/builtins/autoimplement.workflow.js +29 -10
  4. package/dist/builtins/autoimplement.workflow.js.map +1 -1
  5. package/dist/client/client.js +3 -1
  6. package/dist/client/client.js.map +1 -1
  7. package/dist/client/protocol.d.ts +1 -1
  8. package/dist/client/protocol.js +18 -1
  9. package/dist/client/protocol.js.map +1 -1
  10. package/dist/client/view.d.ts +7 -5
  11. package/dist/extension/index.d.ts +2 -1
  12. package/dist/extension/index.js +115 -165
  13. package/dist/extension/index.js.map +1 -1
  14. package/dist/extension/recorder.d.ts +1 -1
  15. package/dist/extension/recorder.js +2 -2
  16. package/dist/extension/recorder.js.map +1 -1
  17. package/dist/extension/step-message.d.ts +1 -1
  18. package/dist/extension/step-message.js +4 -4
  19. package/dist/extension/step-message.js.map +1 -1
  20. package/dist/extension/workflow-message-coordinator.d.ts +12 -1
  21. package/dist/extension/workflow-message-coordinator.js +65 -54
  22. package/dist/extension/workflow-message-coordinator.js.map +1 -1
  23. package/dist/resource-managers/index.d.ts +1 -2
  24. package/dist/resource-managers/index.js +1 -2
  25. package/dist/resource-managers/index.js.map +1 -1
  26. package/dist/resource-managers/sqlite.d.ts +2 -295
  27. package/dist/resource-managers/sqlite.js +71 -1528
  28. package/dist/resource-managers/sqlite.js.map +1 -1
  29. package/dist/resource-managers/types.d.ts +2 -7
  30. package/dist/server/resource-runner-entry.js +2 -6
  31. package/dist/server/resource-runner-entry.js.map +1 -1
  32. package/dist/server/rpc-bridge.js +1 -1
  33. package/dist/server/rpc-bridge.js.map +1 -1
  34. package/dist/server/rpc-executor.js +2 -4
  35. package/dist/server/rpc-executor.js.map +1 -1
  36. package/dist/server/server-entry.js +3 -0
  37. package/dist/server/server-entry.js.map +1 -1
  38. package/dist/server/server.d.ts +9 -8
  39. package/dist/server/server.js +364 -541
  40. package/dist/server/server.js.map +1 -1
  41. package/dist/server/state.d.ts +16 -29
  42. package/dist/server/state.js +105 -319
  43. package/dist/server/state.js.map +1 -1
  44. package/dist/server/view.d.ts +4 -5
  45. package/dist/server/view.js +51 -51
  46. package/dist/server/view.js.map +1 -1
  47. package/dist/server/workflow-runner-entry.d.ts +1 -1
  48. package/dist/server/workflow-runner-entry.js +1 -49
  49. package/dist/server/workflow-runner-entry.js.map +1 -1
  50. package/dist/server/workflow-runner-protocol.d.ts +2 -7
  51. package/dist/server/workflow-runner-protocol.js +5 -10
  52. package/dist/server/workflow-runner-protocol.js.map +1 -1
  53. package/dist/server/workflow-runner-store.d.ts +5 -6
  54. package/dist/server/workflow-runner-store.js +7 -14
  55. package/dist/server/workflow-runner-store.js.map +1 -1
  56. package/dist/state/attempt-time.d.ts +21 -0
  57. package/dist/state/attempt-time.js +95 -0
  58. package/dist/state/attempt-time.js.map +1 -0
  59. package/dist/state/json.d.ts +2 -1
  60. package/dist/state/json.js.map +1 -1
  61. package/dist/state/project-store.d.ts +39 -0
  62. package/dist/state/project-store.js +154 -0
  63. package/dist/state/project-store.js.map +1 -0
  64. package/dist/state/prune.js +0 -7
  65. package/dist/state/prune.js.map +1 -1
  66. package/dist/state/schema.js +23 -23
  67. package/dist/state/workflow-messages.d.ts +1 -1
  68. package/dist/state/workflow-messages.js +3 -1
  69. package/dist/state/workflow-messages.js.map +1 -1
  70. package/dist/viewer/backup.js +5 -0
  71. package/dist/viewer/backup.js.map +1 -1
  72. package/dist/workflows/diagnostics.d.ts +8 -0
  73. package/dist/workflows/diagnostics.js +61 -0
  74. package/dist/workflows/diagnostics.js.map +1 -0
  75. package/dist/workflows/engine.d.ts +2 -15
  76. package/dist/workflows/engine.js +125 -245
  77. package/dist/workflows/engine.js.map +1 -1
  78. package/dist/workflows/human-decision.d.ts +3 -7
  79. package/dist/workflows/human-decision.js +2 -56
  80. package/dist/workflows/human-decision.js.map +1 -1
  81. package/dist/workflows/index.d.ts +2 -1
  82. package/dist/workflows/index.js +1 -0
  83. package/dist/workflows/index.js.map +1 -1
  84. package/dist/workflows/queue.d.ts +267 -0
  85. package/dist/workflows/queue.js +1248 -0
  86. package/dist/workflows/queue.js.map +1 -0
  87. package/dist/workflows/requests.d.ts +47 -0
  88. package/dist/workflows/requests.js +158 -0
  89. package/dist/workflows/requests.js.map +1 -0
  90. package/dist/workflows/schema.js +2 -4
  91. package/dist/workflows/schema.js.map +1 -1
  92. package/dist/workflows/store.d.ts +11 -16
  93. package/dist/workflows/store.js +174 -169
  94. package/dist/workflows/store.js.map +1 -1
  95. package/dist/workflows/tool-input.d.ts +6 -9
  96. package/dist/workflows/tool-input.js +15 -12
  97. package/dist/workflows/tool-input.js.map +1 -1
  98. package/dist/workflows/transitions.d.ts +38 -0
  99. package/dist/workflows/transitions.js +276 -0
  100. package/dist/workflows/transitions.js.map +1 -0
  101. package/dist/workflows/types.d.ts +9 -42
  102. package/dist/workflows/workflow-message-content.d.ts +7 -1
  103. package/dist/workflows/workflow-message-content.js +22 -4
  104. package/dist/workflows/workflow-message-content.js.map +1 -1
  105. package/docs/2026-08-25-workflow-follow-ups.md +5 -5
  106. package/docs/2026-09-02-unify-workflow-messages-plan.md +2 -0
  107. package/docs/2026-09-04-workflow-run-state-plan.md +2 -0
  108. package/docs/2026-09-06-durable-execution-plan.md +176 -0
  109. package/docs/DEFERRED_TURNS.md +84 -105
  110. package/docs/HUMAN_DECISIONS.md +17 -19
  111. package/docs/HUMAN_DECISION_PRESENTATIONS.md +1 -1
  112. package/docs/SQLITE_STATE.md +68 -29
  113. package/docs/WORKFLOW_SERVER.md +21 -10
  114. package/docs/WORKFLOW_STEP_MESSAGES.md +72 -54
  115. package/docs/plans/2026-08-04-controller-runtime-plan.md +2 -0
  116. package/docs/plans/2026-08-21-deferred-turn-intents-plan.md +2 -0
  117. package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +2 -0
  118. package/docs/workflows.md +94 -45
  119. package/examples/workflows/autoresearch.workflow.ts +0 -2
  120. package/examples/workflows/branch.workflow.ts +0 -4
  121. package/examples/workflows/echo.workflow.ts +0 -2
  122. package/examples/workflows/live-settings.workflow.ts +0 -2
  123. package/examples/workflows/two-turn.workflow.ts +0 -2
  124. package/herdr-plugin.toml +1 -1
  125. package/package.json +1 -1
  126. package/protocol/fixtures/client-v1.json +1 -1
  127. package/skills/pi-workflows/SKILL.md +8 -7
  128. package/src/builtins/autoimplement.workflow.ts +31 -10
  129. package/src/client/client.ts +3 -1
  130. package/src/client/protocol.ts +21 -1
  131. package/src/client/view.ts +9 -5
  132. package/src/extension/index.ts +117 -189
  133. package/src/extension/recorder.ts +2 -2
  134. package/src/extension/step-message.ts +4 -4
  135. package/src/extension/workflow-message-coordinator.ts +78 -50
  136. package/src/resource-managers/index.ts +1 -9
  137. package/src/resource-managers/sqlite.ts +330 -2655
  138. package/src/resource-managers/types.ts +2 -3
  139. package/src/server/resource-runner-entry.ts +2 -7
  140. package/src/server/rpc-bridge.ts +1 -1
  141. package/src/server/rpc-executor.ts +4 -9
  142. package/src/server/server-entry.ts +3 -0
  143. package/src/server/server.ts +416 -641
  144. package/src/server/state.ts +122 -466
  145. package/src/server/view.ts +56 -60
  146. package/src/server/workflow-runner-entry.ts +2 -55
  147. package/src/server/workflow-runner-protocol.ts +5 -15
  148. package/src/server/workflow-runner-store.ts +12 -27
  149. package/src/state/attempt-time.ts +122 -0
  150. package/src/state/json.ts +2 -1
  151. package/src/state/project-store.ts +218 -0
  152. package/src/state/prune.ts +0 -13
  153. package/src/state/schema.ts +23 -23
  154. package/src/state/workflow-messages.ts +4 -2
  155. package/src/viewer/backup.ts +7 -0
  156. package/src/workflows/diagnostics.ts +78 -0
  157. package/src/workflows/engine.ts +133 -291
  158. package/src/workflows/human-decision.ts +3 -81
  159. package/src/workflows/index.ts +8 -2
  160. package/src/workflows/queue.ts +2091 -0
  161. package/src/workflows/requests.ts +283 -0
  162. package/src/workflows/schema.ts +2 -6
  163. package/src/workflows/store.ts +223 -231
  164. package/src/workflows/tool-input.ts +20 -12
  165. package/src/workflows/transitions.ts +330 -0
  166. package/src/workflows/types.ts +9 -46
  167. package/src/workflows/workflow-message-content.ts +28 -4
  168. package/dist/resource-managers/runtime.d.ts +0 -59
  169. package/dist/resource-managers/runtime.js +0 -423
  170. package/dist/resource-managers/runtime.js.map +0 -1
  171. package/src/resource-managers/runtime.ts +0 -576
@@ -1,10 +1,10 @@
1
1
  # Workflow messages in Pi
2
2
 
3
- Status: this is the implemented workflow-message contract. [Unify workflow messages and restore hosted behavior](2026-09-02-unify-workflow-messages-plan.md) records the approved design and implementation plan.
3
+ This is the current workflow-message contract. The [durable execution plan](2026-09-06-durable-execution-plan.md) supersedes the earlier automatic terminal-turn and reminder behavior.
4
4
 
5
5
  ## Goal
6
6
 
7
- Pi Workflows must add several kinds of content to an origin Pi conversation. These include interactive step prompts, protected human decisions, passive notifications, terminal results, and follow-up prompts. Initial, reminder, and resumed prompts are one step-message kind with different display reasons.
7
+ Pi Workflows must add several kinds of content to an origin Pi conversation. These include interactive step prompts, protected human decisions, passive notifications, terminal results, and follow-up prompts. Initial and resumed prompts are one step-message kind with different display reasons.
8
8
 
9
9
  The server saves all of them as workflow messages. One extension component sends them through documented Pi APIs. Feature records continue to own workflow results, answers, settings, and timeouts.
10
10
 
@@ -16,13 +16,13 @@ A workflow message is content that Pi Workflows requires Pi to add to one conver
16
16
 
17
17
  The message kinds are:
18
18
 
19
- | Kind | Pi behavior | Purpose |
20
- | -------------- | ----------------------------------------------- | ------------------------------------------------ |
21
- | `step` | Custom message that starts a model turn | Initial, reminder, or resumed interactive prompt |
22
- | `decision` | Custom message that does not start a model turn | Protected choice for a person |
23
- | `notification` | Custom message that does not start a model turn | Passive workflow notice |
24
- | `terminal` | Custom message that starts a model turn | Final result and safe recovery choice |
25
- | `followUp` | Custom message that starts normal work | Work saved for after successful completion |
19
+ | Kind | Pi behavior | Purpose |
20
+ | -------------- | ------------------------------------------------ | ------------------------------------------ |
21
+ | `step` | Custom message that starts a model turn | Initial or resumed interactive prompt |
22
+ | `decision` | Custom message that does not start a model turn | Protected choice for a person |
23
+ | `notification` | Custom message that does not start a model turn | Passive workflow notice |
24
+ | `terminal` | Visible message that does not start a model turn | Recorded terminal result |
25
+ | `followUp` | Custom message that starts normal work | Work saved for after successful completion |
26
26
 
27
27
  The server stores one `WorkflowMessage` record before Pi can send it:
28
28
 
@@ -61,7 +61,7 @@ Every step message uses the custom type `pi-workflows-step`:
61
61
  export type WorkflowAgentStepMessageDetails = {
62
62
  schema: "pi-workflows.agent-step-message.v1";
63
63
  workflowMessageId: string;
64
- reason: "initial" | "reminder" | "resumed";
64
+ reason: "initial" | "resumed";
65
65
  contract: AgentStepContract;
66
66
  presentation?: {
67
67
  runTitle?: string;
@@ -84,7 +84,7 @@ pi.sendMessage(
84
84
 
85
85
  The renderer reads `details` and does not parse the prompt. It shows a compact summary by default and the complete prompt when expanded. If the renderer is unavailable, Pi still retains the custom message and its content.
86
86
 
87
- Submitted agent steps call the `workflow` tool. Assistant-message steps reply normally. Both forms keep the existing `agent` node and use `expectedOutput` to select the completion form.
87
+ Submitted agent steps call `workflow submit` or `workflow update` with the exact `requestId`. Assistant-message steps reply normally and are accepted only through the separate assistant-response path at `agent_settled`. Ordinary checkpoints accept `answer`; protected decisions accept only the verified human path or their declared timeout policy. Both forms keep the existing `agent` node and use `expectedOutput` to select the completion form.
88
88
 
89
89
  ## Engine boundary
90
90
 
@@ -103,10 +103,10 @@ A collapsed step card shows only the workflow identity and current work. For exa
103
103
  Checking the monitored target
104
104
  ```
105
105
 
106
- A reminder or resumed prompt adds a short label:
106
+ A resumed prompt adds a short label:
107
107
 
108
108
  ```text
109
- ↻ monitor › check · reminder
109
+ ↻ monitor › check · resumed
110
110
  Checking the monitored target
111
111
  ```
112
112
 
@@ -141,58 +141,75 @@ After Pi, the extension, or the server restarts, branch reporting runs before an
141
141
 
142
142
  ## Model-turn status
143
143
 
144
- `agent_start` has no message payload. The extension binds it through the current origin-session view. Turn binding ignores branch membership; only branch reporting and re-presentation inspect the active branch:
145
-
146
- - the latest sent step is open while its interaction remains pending and its run is not paused;
147
- - a terminal or follow-up message is open only until its first turn ends;
148
- - decisions and notifications never open a turn.
149
-
150
- A start against a closed message is rejected. Follow-up turns are reported for ordering but do not show the completed workflow as `running`.
151
-
152
- The extension creates one workflow turn ID at `agent_start` and keeps it through the matching `agent_end` and server reconnect. It buffers starts and ends until the session view and message receipt are ready.
153
-
154
- At `agent_end`, it derives `completed`, `aborted`, or `error` from the documented assistant messages. It reads response-entry evidence from `ctx.sessionManager.getBranch()`; the entry ID can be null. The server saves one immutable end result. A repeated report adopts it, and a stale turn ID cannot clear newer activity. A supervised runner can continue after accepted tool output while that Pi turn is still open. This normal continuation leaves the session capture recording until `agent_end`; it does not mark the capture as interrupted.
155
-
156
- The server applies the end and its workflow consequence in one transaction. An aborted turn sets the run pause and cancels the request's pending step messages; the interaction derives its paused state from the run. Resuming that submitted-output step atomically clears the pause, increments the interaction revision, and creates one new step message with reason `resumed`. That message starts a fresh Pi model turn. A protected decision does not start a model turn, so its revision and decision message do not change when its run resumes. A completed, recoverably failed, or proved-lost turn increments `unproductiveTurnEnds` only when the submitted-output step is pending, not paused, and has no accepted or validating submission. Values one and two create one step message with reason `reminder`; a value above two fails the attempt. A partial unique index enforces at most one pending step message for the request, regardless of reason. Acceptance, pause, cancel, timeout, and branch re-presentation cancel all pending step messages. A cancelled message did not start a turn and does not increment the counter.
157
-
158
- There is no activity heartbeat, refresh lease, or sequence counter. The server shows `running` from the matching start until the matching end. Server startup never marks a Pi turn lost. On `session_start`, only an idle-session branch report can close an open sent message with synthetic stop reason `lost`. Polling and time alone cannot create a reminder.
144
+ `agent_start` has no message payload. A locally delivered prompt binds its start
145
+ through the coordinator's saved message identity. Late binding and reconnect
146
+ also require the exact message in the active branch. A session view alone does
147
+ not prove that a message caused the current turn.
148
+
149
+ Only pending, unpaused agent requests and explicit follow-ups can open model
150
+ turns. Decisions, notifications, and terminal notices cannot. A stale start can
151
+ adopt an already closed result but cannot create new activity. An identity
152
+ mismatch is rejected before a turn changes.
153
+
154
+ `agent_end` records low-level stop information. The coordinator retains the
155
+ workflow turn through automatic Pi retries and waits for `agent_settled` before
156
+ submitting visible assistant text, recording the turn end, or delivering more
157
+ work. End reports carry `completed`, `aborted`, `error`, or `lost` and the exact
158
+ response-entry evidence when available. A delayed acknowledgment keeps the settled
159
+ response ID pending. Once message and turn ownership are confirmed, the coordinator
160
+ submits that exact response before it reports the end. Later events cannot replace
161
+ the pending turn or its response. Repeated reports adopt the saved result.
162
+
163
+ An aborted pending step pauses its run. Resume retains the request and attempt,
164
+ advances the request revision, and creates one resumed step message when needed.
165
+ A protected decision keeps its answer revision and decision message. A missing
166
+ submission stays pending: the host adds no reminder turn or hidden retry limit.
167
+
168
+ Execution status and Pi activity remain separate. A completed run remains
169
+ completed during reporting or follow-up work. A waiting run can have an active
170
+ origin-session turn without changing its execution status to running. Host
171
+ recovery closes active-time intervals at their last durable samples, not the Pi
172
+ turn itself. Only an idle-session branch report can prove an unended turn lost.
159
173
 
160
174
  ## Feature ownership
161
175
 
162
176
  The workflow message stores only Pi send facts. Other records remain authoritative:
163
177
 
164
- - interactive requests own step contracts, attempts, deadlines, validation, model submissions, and `unproductiveTurnEnds`;
165
- - human decisions own choices, verified answers, expiry, and continuation;
178
+ - interactive requests own exact response identity, kind, validation, and accepted submissions;
179
+ - node attempts and active intervals own execution history and the active-time budget;
180
+ - human decisions own choices, verified answers, and absolute expiry;
166
181
  - terminal runs own outcomes, reasons, restart lineage, and results;
167
182
  - notification nodes own their node results;
168
183
  - follow-up records own prompt source and authority;
169
184
  - settings records own current values and accepted changes.
170
185
 
171
- Submitted-output steps can use reminders. Assistant-message steps do not send a reminder after a visible response. An interrupted assistant-message step keeps its attempt ID and adopts a matching completed response from the active branch. A stale attempt or another branch remains invalid.
172
-
173
- Each source event creates its workflow message in the same SQLite transaction. The message cannot exist without its source fact, and a source fact cannot require a Pi message without the matching record.
186
+ Submitted and assistant-message steps keep their exact attempt while parked.
187
+ Recovery adopts accepted receipts and matching branch evidence; it does not use
188
+ the oldest pending request or accept text from another request or branch.
174
189
 
175
- ## Restored behavior
190
+ Requests and their required messages commit together. Terminal reporting can be
191
+ retried from recorded facts after execution settles; a reporting failure cannot
192
+ reverse execution or cancellation.
176
193
 
177
- The shared contract supports these features without separate send paths:
194
+ ## Terminal results and follow-ups
178
195
 
179
- - at most two reminder-reason step messages after model turns end without a valid submission;
180
- - one resumed-reason step message after a presented paused step resumes;
181
- - one terminal result and recovery turn for the final outcome of each interactive continuation chain;
182
- - safe restart with lineage, a limit of three, and repeated-failure protection;
183
- - ordered follow-up prompts after successful completion, terminal turn end, and release of later workflow reservations;
184
- - protected decisions in Pi and approved external channels;
185
- - passive notifications that do not start model turns;
186
- - terminal result retention while its message is pending or its first turn is open, and then for 60 seconds after that turn ends, in the widget and `piw`;
187
- - conversation recording linked to workflow attempts.
196
+ A terminal result is visible but does not start a model turn. It stays in the
197
+ origin-session view while pending and for 60 seconds after confirmed delivery.
198
+ An explicit restart targets the terminal run and revision and creates fresh
199
+ work without copying old steps, changed settings, approvals, or effects. It has
200
+ no terminal-turn prerequisite or hard-coded count limit.
188
201
 
189
- A terminal or follow-up message does not reopen the completed workflow. A slash-looking follow-up remains plain model input because `pi.sendMessage()` does not dispatch extension commands or expand prompt templates. External effects remain idempotent or explicitly ambiguous.
202
+ Explicit follow-ups wait for successful completion, terminal notice delivery,
203
+ prior follow-up settlement, and release of the session reservation. They remain
204
+ normal conversation work. Slash-looking text cannot dispatch an extension
205
+ command. External effects still require saved receipts or explicit recovery of
206
+ an ambiguous outcome. See [terminal workflow messages](DEFERRED_TURNS.md).
190
207
 
191
208
  ## Session recording
192
209
 
193
210
  The extension records workflow-related Pi events through a batched server client operation. It uses documented Pi events and does not read or edit Pi session files.
194
211
 
195
- The server deduplicates settled entries by Pi entry ID. It links attempts to their prompt, response, first, and last entries. A recording failure does not fail workflow execution.
212
+ The server deduplicates settled entries by Pi entry ID. It links attempts to their prompt, response, first, and last entries. The coordinator finalizes capture after the terminal notice has a confirmed entry on the current branch, without starting a model turn. Explicit follow-up turns have their own capture segment. A recording failure does not fail workflow execution or block the next delivery.
196
213
 
197
214
  ## Public API boundary
198
215
 
@@ -205,26 +222,27 @@ This design does not change Pi core, use private Pi APIs, or change Pi session s
205
222
  Tests must prove:
206
223
 
207
224
  - every message kind uses the one coordinator;
208
- - one workflow message ID creates at most one confirmed Pi entry and one automatic model turn;
225
+ - one workflow message ID adopts its confirmed Pi entry before another send;
226
+ - an automatic Pi retry cannot submit a partial response before the settled boundary;
209
227
  - a later manual turn uses a new workflow turn ID without creating another Pi entry;
210
228
  - two Pi processes that open one session cannot both send because one process-local coordinator epoch is active;
211
229
  - restart recovery reports the branch and adopts an existing entry before it sends;
212
230
  - branch absence is usable only when Pi is idle and has no pending input;
213
231
  - a crash after send leaves the message pending and cannot cause a resend before branch reporting;
214
232
  - messages remain in saved order without message-pointer deadlocks;
215
- - an early `agent_start` and `agent_end` wait for the message receipt and session view, then apply in order;
216
- - every matching model turn shows `running` for its full duration;
217
- - normal runner continuation leaves the active session capture open until the matching turn ends;
233
+ - an early `agent_start` and `agent_settled` wait for the message receipt and session view, then apply in order;
234
+ - model activity remains separate from durable execution status;
235
+ - normal runner progress leaves the active session capture open until the matching turn ends;
218
236
  - stale turn-end reports and starts against closed messages are rejected;
219
237
  - a manual turn cancels pending step messages that it supersedes;
220
238
  - a turn cannot bind to an interaction whose run is paused;
221
- - aborted turns pause without incrementing the unproductive-turn counter;
239
+ - aborted pending turns pause without adding reminder work;
222
240
  - resuming an aborted step creates one new resumed message and one fresh model turn;
223
241
  - resuming a protected decision keeps its answer revision and does not create a duplicate decision message;
224
242
  - server restart does not close a live Pi turn, while an idle-session branch report can close an unended turn as lost;
225
- - two reminder-reason steps are sent at most once and the next unproductive turn fails;
226
- - initial, reminder, and resumed prompts use the same step kind and differ only by reason;
227
- - terminal and follow-up messages each start only at their durable boundary;
243
+ - repeated missing submissions remain pending without automatic extra model turns;
244
+ - initial and resumed prompts use the same step kind and differ only by reason;
245
+ - terminal messages never start turns, and follow-ups start only at their declared boundary;
228
246
  - a branch switch creates one resumed-reason step only when that branch has no entry for the interaction;
229
247
  - a missing protected decision creates another decision message, not a step;
230
248
  - branch evidence changes a cancelled message to sent;
@@ -8,6 +8,8 @@ status: implemented
8
8
 
9
9
  # ResourceManager runtime plan
10
10
 
11
+ > Historical plan. The [durable execution plan](../2026-09-06-durable-execution-plan.md) supersedes its continuation, scheduling, and automatic model-turn choices. Use the current reference pages for the implemented contract.
12
+
11
13
  pi-workflows needs a controller mode for automation that spans repeated events, external state changes, and process restarts. The design in [RESOURCE_MANAGERS.md](../RESOURCE_MANAGERS.md) follows the Kubernetes controller pattern. Durable resources hold desired and observed state, events enqueue resource keys, and each reconciliation reads current facts before acting.
12
14
 
13
15
  The implementation keeps the graph engine focused on finite jobs. Controllers start and observe workflows through a child-run interface. Workflow graphs keep their finite execution model.
@@ -6,6 +6,8 @@ date: 2026-08-21
6
6
 
7
7
  # Guarantee one successor turn after workflow interruption
8
8
 
9
+ > Historical plan. The [durable execution plan](../2026-09-06-durable-execution-plan.md) supersedes its continuation, scheduling, and automatic model-turn choices. Use the current reference pages for the implemented contract.
10
+
9
11
  Pi Workflows can stop the active agent turn before the agent receives a tool result or can take its next action. This happens when an active workflow step is cancelled, times out, loses its queue claim, or is interrupted by a controller. The workflow can also return a successful start result and then fail asynchronously before it sends its first prompt. In that case, the user can see a UI error while the model still believes that the workflow started successfully.
10
12
 
11
13
  Add one general rule: an eligible workflow event creates one durable obligation for one later agent turn. The next normal workflow prompt or result presentation satisfies the obligation when one exists. Otherwise, Pi Workflows sends one factual fallback turn after the old turn settles.
@@ -6,6 +6,8 @@ date: 2026-08-27
6
6
 
7
7
  # Full plan
8
8
 
9
+ > Historical plan. The [durable execution plan](../2026-09-06-durable-execution-plan.md) supersedes its continuation, scheduling, and automatic model-turn choices. Use the current reference pages for the implemented contract.
10
+
9
11
  ## Goal
10
12
 
11
13
  After every top-level workflow run ends, give the model one normal successor turn.
package/docs/workflows.md CHANGED
@@ -126,12 +126,12 @@ pi-workflows server run
126
126
  `server run` stays attached. The other commands start, inspect, or stop the
127
127
  on-demand process. No command installs an operating-system service.
128
128
 
129
- The extension, CLI, and `piw` use the same version-1 client protocol over a Unix socket or Windows named pipe. The server sends byte-bounded run-list and run-view pages. Clients collect a complete run list for one revision and reject stale run pages whose cursor or revision no longer matches. The server reads only the selected history ranges and reuses an unchanged subscribed view after a lightweight revision check. It waits for a slow socket to drain and removes every subscription when its client unsubscribes. Large values stay available through verified content chunks. Server-generated aggregate values are saved and linked to the run before the server advertises them.
129
+ The extension, CLI, and `piw` use the same version-1 client protocol over a Unix socket or Windows named pipe. The server sends byte-bounded run-list and run-view pages. Clients collect a complete run list for one revision and reject stale run pages whose cursor or revision no longer matches. A run view's `revision` is the presentation cursor; `runRevision` is the execution resource revision used by restart and other exact execution commands. The server reads only the selected history ranges and reuses an unchanged subscribed view after a lightweight revision check. It waits for a slow socket to drain and removes every subscription when its client unsubscribes. Large values stay available through verified content chunks. Server-generated aggregate values are saved and linked to the run before the server advertises them.
130
130
 
131
131
  A runner verifies the root and all mounted source identities before it loads
132
132
  workflow modules. It then checks the resolved mounted-source map and executes
133
- from committed state through a server-backed store. Resume and continuation
134
- reads return only `WorkflowRunState`. Session messages, tool results, activity
133
+ from committed state through a server-backed store. Resume reads return only
134
+ `WorkflowRunState`. Session messages, tool results, activity
135
135
  events, viewer history, settings, follow-ups, and other inspection data stay in
136
136
  server-owned SQLite. If one required result exceeds the runner protocol frame,
137
137
  the server returns a digest-bound reference and the runner reads and verifies it
@@ -154,7 +154,7 @@ same request pending and returns the error to the model. Closing Pi leaves that
154
154
  request pending; reopening the same session adopts the existing session entry
155
155
  or presents it once. Step prompts, protected decisions, notifications, terminal
156
156
  results, and follow-ups use the server-owned `workflow_messages` table and one
157
- extension sender. Initial, reminder, and resumed prompts are the same step-message
157
+ extension sender. Initial and resumed prompts are the same step-message
158
158
  kind with different display reasons. A terminal workflow message becomes
159
159
  eligible only after the terminal outcome is committed. A resource manager child
160
160
  without an origin session can use a supervised headless `pi --mode rpc` child
@@ -208,10 +208,11 @@ the client stops waiting but does not cancel the durable server command. A retry
208
208
  uses a new transport request ID with the same durable submission identity and
209
209
  adopts the stored result. Rejected submissions return
210
210
  the validation error and can retry in the same step. If the model settles
211
- without submitting, the server increments the request's unproductive-turn counter
212
- and can create at most two step messages with reason `reminder`. The next
213
- unproductive turn fails the step. The timeout remains active during each
214
- reported model turn, and cancellation remains active throughout. For assistant-message output, the engine appends a normal-response contract,
211
+ without submitting, the exact request stays pending. The server does not add
212
+ reminder turns or apply a hidden retry limit. Additional model work must follow
213
+ a declared graph path or an explicit user request. The timeout remains active
214
+ during each reported model turn, and cancellation remains active throughout.
215
+ For assistant-message output, the engine appends a normal-response contract,
215
216
  waits for `agent_settled`, rejects empty, failed, aborted, or tool-only results,
216
217
  and never suppresses the visible text. Timeout and cancellation abort either
217
218
  form's active Pi turn.
@@ -226,6 +227,28 @@ function can use prepared outputs to select a policy for this run. It has 30
226
227
  seconds to return. Computed timeout functions are runtime code, so definition
227
228
  snapshots omit them. Snapshots keep fixed numbers and fixed `null` values.
228
229
 
230
+ ### Explicit summaries and fresh restarts
231
+
232
+ Terminal notices show the recorded status, result, and error. They are visible
233
+ messages, not model prompts. Use an explicit `assistantMessage()` node when a
234
+ workflow needs a written explanation. `presentationPrompt` is not supported.
235
+ Autoimplement prepares its structured result, runs an explicit summary node,
236
+ and then returns the prepared result. A failed summary cannot erase accepted work.
237
+
238
+ The extension submits visible assistant text and records the end of a workflow
239
+ turn at `agent_settled`. `agent_end` records only low-level activity. Automatic Pi
240
+ retries keep the same unsettled turn and cannot submit an unfinished response.
241
+ Branch evidence must identify the exact workflow message before reconnect can
242
+ adopt its turn.
243
+
244
+ A fresh restart requires an explicit user request and targets `runId` plus
245
+ `expectedRevision`. It starts from the original input in a new run. It does not
246
+ copy old steps, changed settings, decisions, or effects. The host rejects a stale
247
+ revision or unsettled external effects before reserving work. An identical retry
248
+ adopts the same child run. There is no terminal-message requirement or hard-coded
249
+ restart count. Explicit queued follow-ups become eligible after terminal notice
250
+ delivery; they do not wait for a terminal model turn.
251
+
229
252
  ### compute
230
253
 
231
254
  Runs a TypeScript function inline. Use it for pure data shaping.
@@ -340,20 +363,21 @@ a manual effect becomes ambiguous. This is not an exactly-once claim.
340
363
 
341
364
  ### checkpoint
342
365
 
343
- Ends the run in a `waiting` state for human review. The checkpoint run is
344
- terminal, so no process keeps running while the run waits. The human answers
345
- with `/workflow answer <json>` (or plain text), which starts a **continuation
346
- run**: a new run with its own state and events, linked to the checkpointed run
347
- through `parentRunId`. The continuation receives the answer as its input,
348
- carries forward every output the parent produced (including the checkpoint's),
349
- and continues routing along the checkpoint's outgoing edge. Outgoing edges
350
- from checkpoint nodes are allowed exactly so continuations have somewhere to
351
- go; step accounting carries over, so `maxSteps` bounds the whole chain.
366
+ Creates a durable input request and parks the worker. The run is waiting, not
367
+ terminal. Its input, settings, history, and active attempt stay unchanged.
368
+ The optional callback creates the request content once. It does not complete
369
+ the node.
370
+
371
+ An answer completes that exact request in the same run. The answer becomes the
372
+ checkpoint output, and execution follows its outgoing edge. A checkpoint at the
373
+ end of the graph completes the run with its answer. Each answered checkpoint
374
+ counts once toward `maxSteps`. Invalid responses do not create another run or
375
+ change another request.
352
376
 
353
377
  ```typescript
354
378
  checkpoint({
355
379
  summary: "human decides how to proceed",
356
- run: ({ outputs }) => outputs.reconcile, // optional; default output is { summary }
380
+ run: ({ outputs }) => outputs.reconcile, // optional request content; defaults to { summary }
357
381
  });
358
382
  ```
359
383
 
@@ -388,9 +412,9 @@ humanDecision({
388
412
  });
389
413
  ```
390
414
 
391
- The waiting run stores a versioned request and asks every channel configured for the logical audience. The structured `subject` remains machine data. Channels receive only the normalized `presentation`, title, choices, input prompts, and any deadline policy. The first valid verified human answer wins. When `onTimeout` is present and no human answer wins before the saved deadline, the server takes a control claim on the waiting parent and atomically applies the validated response with `timeout` provenance, closes the interaction, and reserves the continuation. This policy can continue without a configured channel. A continuation preserves the original workflow input and exposes the resolved response as the checkpoint output. `humanDecisionEdge()` provides exhaustive routing for the choices. The removed `body` request form is invalid under the alpha hard cut.
415
+ The waiting run stores the request and asks every channel configured for the audience. The structured `subject` remains machine data. Channels receive the normalized `presentation`, title, choices, input prompts, and deadline policy. The first valid verified human answer wins. When `onTimeout` is present and no human answer wins before the saved deadline, the server accepts the declared response with `timeout` provenance and resumes the same run. This policy can continue without a configured channel. The original input remains unchanged, and the resolved response becomes the checkpoint output. `humanDecisionEdge()` provides exhaustive routing for the choices.
392
416
 
393
- The model-facing workflow tool cannot answer a protected human decision. The origin Pi session displays the request without starting a model turn. A person uses `/workflow answer` to send the answer through the server-owned path. Ordinary checkpoints can also use the model-facing `answer` action.
417
+ The model-facing workflow tool cannot answer a protected human decision. The origin Pi session displays the request without starting a model turn. A person uses `/workflow answer <requestId> <response>` to send the answer through the server-owned path. Ordinary checkpoints can also use the model-facing `answer` action.
394
418
 
395
419
  See [Human decisions](HUMAN_DECISIONS.md) for channels, recovery, persistence, and plan approval.
396
420
 
@@ -488,9 +512,9 @@ The model sees one `workflow` tool. Its `action` field supports:
488
512
  - `status` for the active run or a supplied run ID;
489
513
  - `pause` and `resume` for the active session run;
490
514
  - `cancel` for the active run or a supplied run ID;
491
- - `answer` with checkpoint input and an optional run ID;
492
- - `update` for a non-completing update from the current agent attempt;
493
- - `submit` for the current workflow step contract.
515
+ - `answer` with an exact checkpoint `requestId` and input;
516
+ - `update` with an exact agent `requestId` and a non-completing update;
517
+ - `submit` with an exact agent `requestId` and its output.
494
518
 
495
519
  A direct user request to continue or resume the active workflow maps to
496
520
  `resume` immediately. The model does not call `status` instead of `resume` or
@@ -678,36 +702,42 @@ and resume later by running that wait again from the beginning.
678
702
  A monitor uses the session's single active workflow slot. It does not provide
679
703
  cron syntax, calendar scheduling, OS notifications, or a background service.
680
704
 
705
+ ## Worker capacity
706
+
707
+ Workflow runs and resource-manager reconciles share one host scheduler. Start, restart, resume, result validation, and timeout recovery all wait for the same worker capacity. The default is four execution workers; set `PI_WORKFLOWS_MAX_WORKERS` before starting the host to change it. Waiting and paused requests keep their durable identity without occupying a worker. A submitted candidate stays durable until a worker can validate it. Cancellation does not wait for capacity.
708
+
709
+ One unfinished interactive run reserves its origin Pi session, including while waiting or paused. Independent headless work does not reserve that session.
710
+
681
711
  ## The step contract
682
712
 
683
713
  Every `agent` prompt ends with a step contract block naming the workflow, the
684
- step id, the attempt id, and the expected output shape:
714
+ step id, attempt id, durable request id, and expected output shape:
685
715
 
686
716
  ```
687
717
  ---
688
718
  Workflow step contract (workflow: autoimplement, step: review, attempt: 6f9d…)
689
719
 
690
720
  Complete this step by calling the `workflow` tool exactly once with:
691
- {"action": "submit", "step": "review", "attempt": "6f9d…", "output": <your result>}
721
+ {"action": "submit", "requestId": "request-EXACT_ID_FROM_CONTRACT", "output": <your result>}
692
722
  Expected output: { "route": "clean" | "issues_found", "reason": "short justification" }
693
723
  The step is complete only after the workflow tool accepts the output.
694
724
  If the tool reports a validation error, correct the output and call it again.
695
725
  ```
696
726
 
697
- The `workflow` tool uses `{ action: "submit", step, attempt, output }` for step
698
- results. Submissions are rejected (with a reason the model sees) when no step
699
- is pending, the step id is wrong, the attempt id belongs to an earlier attempt
700
- of the same node (loops revisit node ids, so each attempt gets a fresh id), or
701
- `validate` throws.
727
+ The `workflow` tool uses `{ action: "submit", requestId, output }` for step
728
+ results. The request binds one run and one attempt. Loops and retries get new
729
+ request IDs; they cannot accept an earlier request's output. A missing, stale,
730
+ wrong-kind, or other-session request is rejected, as is output that fails
731
+ `validate`. The server never selects the oldest pending request.
702
732
  Acceptance resolves the step and the engine advances. In an interactive Pi
703
733
  session, each agent prompt arrives as a `pi-workflows-step` custom message
704
734
  with `triggerTurn: true`. The model receives the complete prompt, while the
705
735
  conversation shows a compact workflow and node card. Expanding tool output with
706
- Ctrl+O shows the exact contract and full prompt. Step messages with reason `reminder` or `resumed` use the same card and keep the active attempt ID.
736
+ Ctrl+O shows the exact contract and full prompt. Step messages with reason `resumed` use the same card and keep the active attempt ID.
707
737
 
708
738
  Headless RPC execution receives the same complete prompt without TUI metadata.
709
739
  Workflow notifications use a custom message with `triggerTurn: false`, so a
710
- notification does not start an assistant response. Step prompts, decisions, notifications, terminal results, and follow-ups use the same saved workflow-message contract and extension coordinator. Initial, reminder, and resumed prompts use the same step kind. See
740
+ notification does not start an assistant response. Step prompts, decisions, notifications, terminal results, and follow-ups use the same saved workflow-message contract and extension coordinator. Initial and resumed prompts use the same step kind. See
711
741
  [Workflow messages in Pi](WORKFLOW_STEP_MESSAGES.md) and the approved
712
742
  [workflow-message restoration plan](2026-09-02-unify-workflow-messages-plan.md).
713
743
 
@@ -726,31 +756,34 @@ returns its stored receipt.
726
756
 
727
757
  A headless resource manager child cannot produce a visible assistant message without
728
758
  an approved origin-session binding. Use structured agent output for detached
729
- work. A final continuation-chain outcome creates its own terminal workflow message through the shared coordinator only after the outcome is durable.
759
+ work. A terminal run creates its own terminal workflow message through the shared coordinator only after the outcome is durable.
730
760
 
731
761
  ## Runtime behavior
732
762
 
733
- Runs execute one node at a time. Every transition is persisted to the run
734
- database transaction before the engine moves on, which is what makes the live viewer
735
- possible. Defaults worth knowing:
763
+ Runs execute one node at a time. Workers propose narrow transitions; the host
764
+ validates and commits them before execution moves on. A start must follow the
765
+ accepted graph route. A completed run must return its accepted final node output.
766
+ Workers cannot skip a checkpoint, rewrite an active attempt's start, or replace
767
+ the full saved run state. Defaults worth knowing:
736
768
 
737
769
  - Node timeout is 15 minutes unless the node sets `timeoutMs` to a positive
738
770
  number or context callback. A timed-out node has outcome `timed_out` and can
739
771
  be routed with `$result.outcome`. A timed-out agent node also aborts its Pi
740
772
  turn, and late output for that attempt is rejected. Interactive runs save the
741
- resolved deadline before they park. The server advances it only during a
742
- reported model turn from an active connected origin session. Message delivery,
743
- waiting, pauses, disconnects, and server downtime do not consume the limit. This
744
- active-time budget survives server restart.
773
+ resolved budget before they park. The host records active intervals with a
774
+ monotonic clock. Message delivery, waiting, pauses, disconnects, wall-clock
775
+ changes, and server downtime do not consume the limit. Recovery keeps the last
776
+ saved elapsed-time sample without rewriting the attempt's start timestamp.
777
+ Human decision deadlines remain absolute wall-clock deadlines.
745
778
  - `maxSteps` (workflow-level, default 100) bounds loops built from cycles in
746
779
  the graph.
747
780
  - `/workflow pause` atomically parks the run with `paused: true`, stores the
748
781
  receipt, and fences the runner before process-group shutdown. `/workflow
749
782
  resume` takes a new generation and reruns only work after the last durable
750
783
  boundary.
751
- - The server tells each runner to `start`, `resume`, `continue`, or `restart`.
752
- A checkpoint continuation names its waiting parent. A restart begins at the
753
- workflow start and does not reuse checkpoint continuation rules.
784
+ - The server tells each runner to `start`, `resume`, or `restart`.
785
+ An answered checkpoint resumes its exact attempt in the same run. An explicit
786
+ restart creates a new run at the workflow start.
754
787
  - Resuming an active run adopts the existing work. Duplicate start, control,
755
788
  update, and submission messages return their stored receipts.
756
789
  - A start is committed as `queued` with its final run ID before the command
@@ -773,7 +806,7 @@ resume` takes a new generation and reruns only work after the last durable
773
806
 
774
807
  ## Run history retention
775
808
 
776
- Pi Workflows keeps a terminal root run and all its restart or continuation descendants for 30 days from `finished_at`. The server can remove the tree after that point only when every descendant is terminal and no protected work or outside reference remains.
809
+ Pi Workflows keeps a terminal root run and all its explicit restart descendants for 30 days from `finished_at`. The server can remove the tree after that point only when every descendant is terminal and no protected work or outside reference remains.
777
810
 
778
811
  Protected work includes waiting or parked runs, live queue rows, pending workflow messages, open workflow turns, pending interactions or decisions, recording session segments, queued follow-ups, active leases, unsettled effects, controller ownership, active runner content, resumable checkpoints, and undelivered terminal results.
779
812
 
@@ -862,8 +895,24 @@ npm run test:e2e:live -- \
862
895
  --model gpt-5.6-luna
863
896
  ```
864
897
 
898
+ For providers that reserve credit against the requested output allowance, pass
899
+ `--max-output-tokens N` with the generated profile. OpenRouter can reject a
900
+ small smoke test when the model's catalog default requests hundreds of thousands
901
+ of output tokens. This option changes only the exact built-in model's `maxTokens`
902
+ through Pi's documented `modelOverrides`. It does not change the provider, model
903
+ ID, API, endpoint, or credentials. The test verifies and reports the allowance,
904
+ keeps the complete response, and fails immediately on a provider error.
905
+
906
+ ```bash
907
+ npm run test:e2e:live -- \
908
+ --provider openrouter \
909
+ --model deepseek/deepseek-v4-flash \
910
+ --max-output-tokens 4096
911
+ ```
912
+
865
913
  For subscription authentication, use a dedicated Pi profile that has no other
866
- extensions or resources:
914
+ extensions or resources. The output-budget option cannot modify this existing
915
+ profile:
867
916
 
868
917
  ```bash
869
918
  npm run test:e2e:live -- \
@@ -24,8 +24,6 @@ export default defineWorkflow({
24
24
  const goal = (input as AutoresearchInput).goal;
25
25
  return goal ? `autoresearch: ${goal.slice(0, 60)}` : undefined;
26
26
  },
27
- presentationPrompt:
28
- "Report the research conclusion in plain language: the winner and its numbers, or the strongest negative result, plus the journal path.",
29
27
  maxSteps: 40,
30
28
  startAt: "setup",
31
29
  nodes: {
@@ -12,10 +12,6 @@ const classifyChoices = ["continue", "checkpoint"] as const;
12
12
  */
13
13
  export default defineWorkflow({
14
14
  name: "branch",
15
- presentationPrompt: ({ state }) =>
16
- state.status === "waiting"
17
- ? "Explain briefly why the task needs clarification, then ask the user one concrete clarification question."
18
- : "Tell the user in one concise sentence how the workflow recommends proceeding.",
19
15
  startAt: "classify",
20
16
  nodes: {
21
17
  classify: decision({
@@ -7,8 +7,6 @@ type EchoInput = {
7
7
  /** Smallest possible workflow: one agent step that submits a JSON reply. */
8
8
  export default defineWorkflow({
9
9
  name: "echo",
10
- presentationPrompt:
11
- "Give the user the concise reply from the workflow result, with no extra commentary.",
12
10
  startAt: "reply",
13
11
  nodes: {
14
12
  reply: agent({
@@ -88,6 +88,4 @@ export default defineWorkflow({
88
88
  switch: { on: "$.route", cases: { normal: "normal", careful: "careful" } },
89
89
  },
90
90
  ],
91
- presentationPrompt:
92
- "Explain which route and settings were used. Any queued follow-up prompts run only after this response settles.",
93
91
  });
@@ -11,8 +11,6 @@ type TwoTurnInput = {
11
11
  */
12
12
  export default defineWorkflow({
13
13
  name: "two-turn",
14
- presentationPrompt:
15
- "Present the final validation checklist clearly, including its concrete file references.",
16
14
  startAt: "inspect",
17
15
  nodes: {
18
16
  inspect: agent({
package/herdr-plugin.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  id = "osolmaz.pi-workflows"
2
2
  name = "pi-workflows"
3
- version = "0.16.5"
3
+ version = "0.16.6"
4
4
  min_herdr_version = "0.7.0"
5
5
  description = "Open the active pi-workflows run in piw from a managed Herdr pane."
6
6
  platforms = ["linux", "macos"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osolmaz/pi-workflows",
3
- "version": "0.16.5",
3
+ "version": "0.16.6",
4
4
  "description": "Workflow and resource manager runtime with a live terminal viewer for the pi coding agent",
5
5
  "keywords": [
6
6
  "pi-package"
@@ -8,7 +8,7 @@
8
8
  "{\"clientId\":\"client-1\",\"idempotencyKey\":\"key-content\",\"operation\":\"view.content\",\"payload\":{\"offset\":0,\"path\":\"artifacts/sha256/abc.json\"},\"requestId\":\"request-content\",\"runId\":\"run-1\",\"schema\":\"pi-workflows.client.v1\",\"type\":\"request\"}",
9
9
  "{\"outcome\":\"accepted\",\"receipt\":{\"live\":true},\"requestId\":\"request-1\",\"schema\":\"pi-workflows.client.v1\",\"type\":\"response\"}",
10
10
  "{\"outcome\":\"accepted\",\"receipt\":{\"large\":100000000000000000000,\"𐀀\":2,\"\":1},\"requestId\":\"request-canonical\",\"schema\":\"pi-workflows.client.v1\",\"type\":\"response\"}",
11
- "{\"event\":\"run_snapshot\",\"payload\":{\"revision\":7,\"runId\":\"run-1\",\"schema\":\"pi-workflows.run-view.v1\"},\"revision\":3,\"runId\":\"run-1\",\"schema\":\"pi-workflows.client.v1\",\"subscriptionId\":\"subscription-run\",\"type\":\"event\"}",
11
+ "{\"event\":\"run_snapshot\",\"payload\":{\"revision\":7,\"runId\":\"run-1\",\"runRevision\":5,\"schema\":\"pi-workflows.run-view.v1\"},\"revision\":3,\"runId\":\"run-1\",\"schema\":\"pi-workflows.client.v1\",\"subscriptionId\":\"subscription-run\",\"type\":\"event\"}",
12
12
  "{\"event\":\"session_snapshot\",\"payload\":{\"run\":null,\"schema\":\"pi-workflows.session-view.v1\",\"sessionId\":\"session-1\"},\"revision\":1,\"schema\":\"pi-workflows.client.v1\",\"subscriptionId\":\"subscription-1\",\"type\":\"event\"}"
13
13
  ],
14
14
  "invalid": [