@osolmaz/pi-workflows 0.5.2 → 0.6.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 (81) hide show
  1. package/README.md +13 -3
  2. package/dist/builtins/catalog.js +1 -1
  3. package/dist/builtins/monitor.workflow.d.ts +25 -69
  4. package/dist/builtins/monitor.workflow.js +194 -123
  5. package/dist/builtins/monitor.workflow.js.map +1 -1
  6. package/dist/extension/executor.d.ts +7 -2
  7. package/dist/extension/executor.js +20 -14
  8. package/dist/extension/executor.js.map +1 -1
  9. package/dist/extension/index.js +56 -15
  10. package/dist/extension/index.js.map +1 -1
  11. package/dist/extension/step-message.d.ts +24 -0
  12. package/dist/extension/step-message.js +106 -0
  13. package/dist/extension/step-message.js.map +1 -0
  14. package/dist/extension/widget.d.ts +4 -2
  15. package/dist/extension/widget.js +111 -17
  16. package/dist/extension/widget.js.map +1 -1
  17. package/dist/extension/workflow-tool.d.ts +9 -0
  18. package/dist/extension/workflow-tool.js +10 -0
  19. package/dist/extension/workflow-tool.js.map +1 -1
  20. package/dist/host/rpc-bridge.js +25 -11
  21. package/dist/host/rpc-bridge.js.map +1 -1
  22. package/dist/host/rpc-executor.d.ts +2 -2
  23. package/dist/host/rpc-executor.js +23 -12
  24. package/dist/host/rpc-executor.js.map +1 -1
  25. package/dist/viewer/cli.js +1 -1
  26. package/dist/viewer/cli.js.map +1 -1
  27. package/dist/viewer/render.js +14 -0
  28. package/dist/viewer/render.js.map +1 -1
  29. package/dist/viewer/tui.js +1 -1
  30. package/dist/viewer/tui.js.map +1 -1
  31. package/dist/workflows/engine.d.ts +6 -1
  32. package/dist/workflows/engine.js +88 -6
  33. package/dist/workflows/engine.js.map +1 -1
  34. package/dist/workflows/index.d.ts +4 -2
  35. package/dist/workflows/index.js +2 -0
  36. package/dist/workflows/index.js.map +1 -1
  37. package/dist/workflows/progress.d.ts +34 -0
  38. package/dist/workflows/progress.js +268 -0
  39. package/dist/workflows/progress.js.map +1 -0
  40. package/dist/workflows/schema.js +21 -1
  41. package/dist/workflows/schema.js.map +1 -1
  42. package/dist/workflows/shell.d.ts +2 -2
  43. package/dist/workflows/shell.js +103 -25
  44. package/dist/workflows/shell.js.map +1 -1
  45. package/dist/workflows/store.d.ts +15 -2
  46. package/dist/workflows/store.js +44 -2
  47. package/dist/workflows/store.js.map +1 -1
  48. package/dist/workflows/types.d.ts +52 -1
  49. package/dist/workflows/updates.d.ts +15 -0
  50. package/dist/workflows/updates.js +188 -0
  51. package/dist/workflows/updates.js.map +1 -0
  52. package/docs/DESIGN_PHILOSOPHY.md +51 -0
  53. package/docs/MONITOR.md +282 -0
  54. package/docs/WORKFLOW_STEP_MESSAGES.md +141 -0
  55. package/docs/WORKFLOW_UPDATES.md +416 -0
  56. package/docs/development.md +7 -3
  57. package/docs/plans/2026-08-13-responsive-workflow-widget-plan.md +11 -3
  58. package/docs/plans/2026-08-16-workflow-updates-plan.md +494 -0
  59. package/docs/run-bundles.md +10 -2
  60. package/docs/workflows.md +57 -17
  61. package/package.json +1 -1
  62. package/src/builtins/catalog.ts +1 -1
  63. package/src/builtins/monitor.workflow.ts +217 -148
  64. package/src/extension/executor.ts +36 -14
  65. package/src/extension/index.ts +89 -23
  66. package/src/extension/step-message.ts +145 -0
  67. package/src/extension/widget.ts +158 -14
  68. package/src/extension/workflow-tool.ts +22 -0
  69. package/src/host/rpc-bridge.ts +37 -14
  70. package/src/host/rpc-executor.ts +35 -14
  71. package/src/viewer/cli.ts +1 -1
  72. package/src/viewer/render.ts +27 -0
  73. package/src/viewer/tui.ts +1 -1
  74. package/src/workflows/engine.ts +117 -4
  75. package/src/workflows/index.ts +32 -0
  76. package/src/workflows/progress.ts +326 -0
  77. package/src/workflows/schema.ts +23 -1
  78. package/src/workflows/shell.ts +109 -26
  79. package/src/workflows/store.ts +67 -2
  80. package/src/workflows/types.ts +78 -1
  81. package/src/workflows/updates.ts +208 -0
@@ -0,0 +1,494 @@
1
+ ---
2
+ title: Add durable workflow updates and progress reporting
3
+ author: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
4
+ date: 2026-08-16
5
+ ---
6
+
7
+ # Add durable workflow updates and progress reporting
8
+
9
+ This plan implements the contracts in [WORKFLOW_UPDATES.md](../WORKFLOW_UPDATES.md), [WORKFLOW_STEP_MESSAGES.md](../WORKFLOW_STEP_MESSAGES.md), and [MONITOR.md](../MONITOR.md). It follows the [design philosophy](../DESIGN_PHILOSOPHY.md): add one general update capability, keep the node set small, and build progress, presentation, and monitoring through composition.
10
+
11
+ ## Outcome
12
+
13
+ Pi Workflows will let a running agent, function action, shell action, or claimed runner publish durable structured updates without completing a node. Progress will be one optional update type with shared estimation and presentation helpers.
14
+
15
+ The built-in monitor will report every accepted check, support optional progress tracks, show live timing in the widget, and deliver notifications without starting an assistant turn.
16
+
17
+ Interactive agent steps will keep their full model prompts while appearing as compact, expandable workflow cards in the conversation.
18
+
19
+ ## Scope
20
+
21
+ ### Pi Workflows engine
22
+
23
+ - Add public update types and the action context that publishes them.
24
+ - Add fenced update publication to the engine and run store.
25
+ - Append `update_published` trace events.
26
+ - Add the latest update projection to run state.
27
+ - Preserve update ordering and idempotency.
28
+ - Apply payload and key-count limits plus update rate limits.
29
+ - Expose update publication to claimed hosts and controllers.
30
+
31
+ ### Node and tool APIs
32
+
33
+ - Add `publishUpdate()` to function-action context.
34
+ - Add the non-completing `workflow` tool action `update`.
35
+ - Add structured line parsing to shell actions.
36
+ - Keep compute nodes pure.
37
+ - Keep final node outputs as the only routing input.
38
+ - Keep the existing `status` action read-only and include current updates in its details.
39
+
40
+ ### Progress support
41
+
42
+ - Add the `pi-workflows.progress.v1` validator and types.
43
+ - Add incremental reduction and conservative estimation helpers.
44
+ - Support independent tracks with stable keys.
45
+ - Support source ETA, measured ETA, confidence, stalls, resets, stale data, and unavailable estimates.
46
+ - Reserve `overall` for workflow-supplied aggregation.
47
+ - Add model-free formatters for notifications and displays.
48
+
49
+ ### Presentation
50
+
51
+ - Extend the compact Pi widget with an optional bounded progress panel.
52
+ - Extend `piw` and the TypeScript viewer with update history and full progress details.
53
+ - Reuse the current widget ticker for elapsed time and countdowns.
54
+ - Keep observed counters fixed between updates.
55
+ - Deliver workflow notifications with `triggerTurn: false` while retaining them in later model context.
56
+ - Deliver interactive agent steps as `pi-workflows-agent-step` custom messages with `triggerTurn: true`.
57
+ - Show workflow, node, and status details in a compact card and the complete prompt when expanded.
58
+ - Use the same complete model prompt in interactive and RPC execution.
59
+
60
+ ### Built-in monitor
61
+
62
+ - Replace quiet/report routes with `continue` and `stop`.
63
+ - Require a report after every accepted check.
64
+ - Remove `reportWhen`, report acknowledgement steps, and `presentationPrompt`.
65
+ - Add optional progress tracks to check output.
66
+ - Add estimate and publish stages, then schedule and report through existing nodes.
67
+ - Default to a 30-minute interval.
68
+ - Preserve the documented 1,000-check workflow safety ceiling.
69
+ - Keep explicit user stop as the fallback stop rule when no terminal condition is clear.
70
+
71
+ ### Integration repositories
72
+
73
+ - Release the package from `osolmaz/pi-workflows`.
74
+ - Update the exact package pin and upstream record in `osolmaz/onurpi`.
75
+ - Update the source monitor skill in `osolmaz/tools`, then run its sync script.
76
+
77
+ ## Non-goals
78
+
79
+ - No Pi core changes.
80
+ - No progress node type.
81
+ - No automatic aggregation of unrelated tracks.
82
+ - No arbitrary commands inside update data.
83
+ - No model-generated fallback ETA.
84
+ - No remote metrics service or global time-series database.
85
+ - No migration files, fallback formats, or dual writes for run bundles.
86
+ - No claim that a finite workflow is an indefinite controller.
87
+ - No controller-backed indefinite monitor in this release.
88
+ - No new workflow primitive or workflow-file option for step-message presentation.
89
+ - No Pi core change, private Pi API, or separate expansion state.
90
+ - No duplicate `sendUserMessage` fallback for new agent-step messages.
91
+ - No rewrite of existing Pi session entries.
92
+
93
+ ## Persistent contract impact
94
+
95
+ ### Run bundles
96
+
97
+ `trace.ndjson` gains `update_published` events. `state.json` gains the optional latest `updates` projection. The trace remains the source of truth.
98
+
99
+ Omission means that a run has never published an update. New runs write an empty array from their first projection. Existing bundles remain valid without migration or fallback code. The implementation keeps the current `pi-workflows.trace-event.v1` and `pi-workflows.run-state.v1` identifiers and does not add dual-write paths.
100
+
101
+ Checkpoint continuations begin with an empty update projection. Resume keeps updates because it continues the same run.
102
+
103
+ ### Pi sessions
104
+
105
+ Workflow notifications remain `pi-workflows-notification` custom messages. They remain in session history and model context. Delivery sets `triggerTurn: false`, so arrival does not start a model turn.
106
+
107
+ New interactive agent-step instructions become `pi-workflows-agent-step` custom messages. Their complete prompt remains in session history and model context. Delivery sets `triggerTurn: true`, so each accepted delivery starts the required model turn. The structured message details use `pi-workflows.agent-step-message.v1`.
108
+
109
+ Existing user-message entries remain readable and are not rewritten. No private Pi entry type, Pi schema change, or separate persistent store is introduced.
110
+
111
+ ### Controller storage
112
+
113
+ The controller database schema does not change for the first implementation. Controllers publish only through a claimed workflow run. Controller-native status remains in controller resources.
114
+
115
+ ## Public API impact
116
+
117
+ The release adds:
118
+
119
+ - `WorkflowUpdateInput`
120
+ - `WorkflowUpdateRecord`
121
+ - `WorkflowUpdateReceipt`
122
+ - `WorkflowActionContext`
123
+ - `context.publishUpdate()`
124
+ - the `workflow` tool action `update`
125
+ - shell update parsing
126
+ - progress validation and estimation exports, including reduction and formatting
127
+ - optional agent-step presentation metadata passed to executors
128
+ - the versioned `WorkflowAgentStepMessageDetails` extension contract
129
+
130
+ The built-in monitor check output changes incompatibly. Existing `continue_quiet`, `continue_report`, `stop_quiet`, and `stop_report` outputs become invalid. The new values are `continue` and `stop`, and both require `report`.
131
+
132
+ The recommended release is `0.6.0`. The package is pre-1.0, previous capability releases use minor versions, and this release adds public APIs while replacing the built-in monitor contract.
133
+
134
+ ## Implementation sequence
135
+
136
+ ### Core update records
137
+
138
+ Files expected to change:
139
+
140
+ - `src/workflows/types.ts`
141
+ - `src/workflows/json.ts`
142
+ - `src/workflows/schema.ts`
143
+ - `src/workflows/store.ts`
144
+ - `src/workflows/engine.ts`
145
+ - `src/workflows/index.ts`
146
+ - `test/store.test.ts`
147
+ - `test/engine.test.ts`
148
+ - `test/engine-more.test.ts`
149
+
150
+ Work:
151
+
152
+ 1. Add public update types and validators.
153
+ 2. Add `updates` to new and replayed run state, and treat omission as no published updates.
154
+ 3. Add a fenced store transition that writes `update_published` first and then replaces projections.
155
+ 4. Fold the latest record per `(type, key)` into state, sorted by trace sequence.
156
+ 5. Enforce payload and key limits with a token-bucket rate limit.
157
+ 6. Add idempotent publication by source id.
158
+ 7. Rebuild projections from trace and verify torn-tail repair.
159
+ 8. Ensure terminal or expired attempts reject updates.
160
+
161
+ Verification:
162
+
163
+ - unit tests for valid writes and receipts
164
+ - ordering under concurrent publication attempts
165
+ - duplicate tool source id returns the first receipt
166
+ - claim loss blocks writes
167
+ - resume reconstructs the same projection
168
+ - continuation starts empty
169
+ - state and trace agree after repair
170
+
171
+ ### Publishers
172
+
173
+ Files expected to change:
174
+
175
+ - `src/workflows/types.ts`
176
+ - `src/workflows/engine.ts`
177
+ - `src/workflows/shell.ts`
178
+ - `src/extension/workflow-tool.ts`
179
+ - `src/extension/index.ts`
180
+ - `src/host/rpc-executor.ts`
181
+ - `test/extension.test.ts`
182
+ - `test/workflow-args.test.ts`
183
+ - `test/e2e/workflow.e2e.test.ts`
184
+
185
+ Work:
186
+
187
+ 1. Give function actions `WorkflowActionContext`.
188
+ 2. Connect `publishUpdate()` to the current run, node, attempt, claim fence, and cancellation signal.
189
+ 3. Add the `workflow` tool `update` variant.
190
+ 4. Use the Pi tool call id as the agent-update source id.
191
+ 5. Keep the step open after a successful update.
192
+ 6. Reject mismatched and expired step contracts.
193
+ 7. Add shell stream selection, line framing, parser execution, and publication backpressure.
194
+ 8. Terminate shell actions on parser or publication failure.
195
+ 9. Expose the same claimed-run operation to the standalone host and controller scheduler boundary.
196
+ 10. Include the current update projection in `status` details.
197
+
198
+ Verification:
199
+
200
+ - an agent publishes several updates and submits once
201
+ - function action publication is durable before the promise resolves
202
+ - shell stdout and stderr parsing works with partial chunks and a final unterminated line
203
+ - malformed or oversized lines and invalid UTF-8 fail safely
204
+ - update data cannot change shell execution
205
+ - a tool update does not trigger an assistant turn
206
+
207
+ ### Progress profile
208
+
209
+ Expected new or changed files:
210
+
211
+ - `src/workflows/progress.ts`
212
+ - `src/workflows/index.ts`
213
+ - `test/progress.test.ts`
214
+
215
+ Work:
216
+
217
+ 1. Add strict `pi-workflows.progress.v1` validation.
218
+ 2. Add a reducer keyed by run and track key.
219
+ 3. Split estimation epochs after phase or unit changes, total or counter changes, and terminal-state resets.
220
+ 4. Keep the latest eight usable intervals.
221
+ 5. Calculate median rate, percentile range, confidence, remaining work, sample age, and stall duration.
222
+ 6. Prefer fresh source ETA and label its basis.
223
+ 7. Pause measured ETA for waiting or blocked tracks.
224
+ 8. Return a reason whenever ETA is unavailable.
225
+ 9. Add bounded plain-text formatting.
226
+
227
+ Verification fixtures must cover:
228
+
229
+ - no counts
230
+ - completed without total
231
+ - two-sample low-confidence ETA
232
+ - stable high-confidence samples
233
+ - bursty and zero-rate intervals
234
+ - counter rollback
235
+ - changed phase, unit, or total
236
+ - source ETA
237
+ - stale source facts
238
+ - waiting, blocked, failed, cancelled, and completed tracks
239
+ - several unrelated tracks
240
+ - explicit `overall`
241
+
242
+ ### Update presentation
243
+
244
+ Files expected to change:
245
+
246
+ - `src/extension/widget.ts`
247
+ - `src/extension/index.ts`
248
+ - `src/viewer/render.ts`
249
+ - `src/viewer/tui.ts`
250
+ - `src/viewer/watch.ts`
251
+ - `tui/src/`
252
+ - `test/widget.test.ts`
253
+ - viewer tests and Rust fixtures
254
+
255
+ Work:
256
+
257
+ 1. Add a pure progress view model under the workflow or render layer without importing Pi.
258
+ 2. Keep the graph visible and fit the optional panel inside the 10-line Pi limit.
259
+ 3. Prioritize `overall`, failures, blocked tracks, and active tracks.
260
+ 4. Preserve manual scrolling and active-node following.
261
+ 5. Recalculate elapsed values from `now` without changing state.
262
+ 6. Show expired ETA as awaiting a new sample.
263
+ 7. Add update and estimate inspection to both viewers.
264
+ 8. Keep TypeScript and Rust rendering behavior in parity.
265
+ 9. Make notifications explicitly non-triggering while retaining custom messages in context.
266
+
267
+ Verification:
268
+
269
+ - zero progress leaves current rendering unchanged
270
+ - one and many tracks fit at narrow widths
271
+ - timers advance without persisted writes
272
+ - completed counters remain fixed
273
+ - renderer output contains no assistant-trigger request
274
+ - replay at every trace position shows the correct update projection
275
+
276
+ ### Agent-step message presentation
277
+
278
+ Files expected to change:
279
+
280
+ - `src/workflows/types.ts`
281
+ - `src/workflows/engine.ts`
282
+ - `src/extension/executor.ts`
283
+ - `src/extension/index.ts`
284
+ - `src/extension/` message-renderer module
285
+ - `src/host/rpc-executor.ts`
286
+ - `test/executor.test.ts`
287
+ - `test/extension.test.ts`
288
+ - `test/e2e/workflow.e2e.test.ts`
289
+
290
+ Work:
291
+
292
+ 1. Keep one pure formatter for the complete model prompt.
293
+ 2. Add optional run-title and status-detail presentation data to `AgentStepRequest` without importing Pi into the workflow layer.
294
+ 3. Pass the prompt, contract, presentation data, delivery kind, and streaming state through `PromptDelivery`.
295
+ 4. Replace interactive `sendUserMessage` delivery with a `pi-workflows-agent-step` custom message.
296
+ 5. Set `triggerTurn: true` and select `steer` or `followUp` from the current streaming state.
297
+ 6. Register a renderer that reads versioned message details instead of parsing prompt text.
298
+ 7. Show a bounded workflow and node summary when collapsed.
299
+ 8. Show exact ids, expected output, and the complete prompt when expanded.
300
+ 9. Send reminders and repeated resume instructions through the same message type with their delivery kind.
301
+ 10. Keep RPC delivery on the same complete prompt and preserve run-bundle prompt recording.
302
+ 11. Remove the interactive `sendUserMessage` path without adding a duplicate fallback.
303
+
304
+ Verification:
305
+
306
+ - interactive and RPC model prompts match
307
+ - one step delivery starts one model turn
308
+ - collapsed and expanded rendering work at narrow widths
309
+ - reminders and resumed deliveries preserve the active attempt
310
+ - timed-out, cancelled, and stale attempts remain invalid
311
+ - replay restores the custom message and structured details
312
+ - notification delivery still does not start a model turn
313
+ - no duplicate user message appears
314
+
315
+ ### Built-in monitor contract
316
+
317
+ Files expected to change:
318
+
319
+ - `src/builtins/monitor.workflow.ts`
320
+ - `src/builtins/catalog.ts`
321
+ - `test/monitor-workflow.test.ts`
322
+ - `test/e2e/workflow.e2e.test.ts`
323
+
324
+ Work:
325
+
326
+ 1. Make `everyMinutes` optional with default 30.
327
+ 2. Remove `reportWhen`.
328
+ 3. Replace the route enum and validation.
329
+ 4. Require reports for `continue` and `stop`.
330
+ 5. Add optional strict progress tracks.
331
+ 6. Add estimate and publish-progress stages, then decide, schedule, and report stages.
332
+ 7. Publish `monitor.schedule` before each sleep.
333
+ 8. Notify once for every accepted check.
334
+ 9. Report before stopping on route or check limit.
335
+ 10. Remove report acknowledgement and presentation behavior.
336
+ 11. Update the built-in catalog revision.
337
+ 12. Keep the 1,000-check safety ceiling and disclose it in prompts and docs.
338
+
339
+ Verification:
340
+
341
+ - first check is immediate
342
+ - normal interval starts after the report is queued
343
+ - every accepted check queues exactly one notification
344
+ - quiet routes and missing reports fail validation
345
+ - stop report arrives before completion
346
+ - safety-limit report arrives before completion
347
+ - cancellation does not fabricate a report
348
+ - progress is optional
349
+ - several tracks publish independently
350
+ - notification arrival does not start an assistant turn
351
+
352
+ ### Canonical documentation
353
+
354
+ Update:
355
+
356
+ - `README.md`
357
+ - `docs/workflows.md`
358
+ - `docs/run-bundles.md`
359
+ - `docs/development.md`
360
+ - `docs/WORKFLOW_UPDATES.md`
361
+ - `docs/WORKFLOW_STEP_MESSAGES.md`
362
+ - `docs/MONITOR.md`
363
+ - examples that teach agent, action, shell, notify, or monitor behavior
364
+
365
+ The shipped docs must describe actual field names and limits along with lifecycle and error behavior. Remove accepted-design warnings only after implementation matches the specifications.
366
+
367
+ ### Release and integration
368
+
369
+ 1. Confirm no old active built-in monitor depends on the previous catalog revision.
370
+ 2. Run every required repository check.
371
+ 3. Commit coherent implementation slices with Conventional Commits.
372
+ 4. Prepare release `0.6.0` through the repository's GitHub Release publication flow.
373
+ 5. Verify npm package contents and the published exact version.
374
+ 6. Pull `osolmaz/onurpi` with `git pull --ff-only`.
375
+ 7. Update `packages/workflows/package.json`, lock data, tests, and `UPSTREAM.md` to the immutable release.
376
+ 8. Run the OnurPi wrapper checks and a real Pi smoke test after `/reload`.
377
+ 9. Pull `osolmaz/tools`, update `agents/skills/monitor/SKILL.md`, and run `agents/sync-skills.py monitor`.
378
+ 10. Verify the installed Pi skill matches the source copy.
379
+ 11. Commit and push each owned repository under its own rules.
380
+
381
+ ## Test matrix
382
+
383
+ ### Unit
384
+
385
+ - schemas and unknown fields
386
+ - update projection and replay
387
+ - claim fencing and idempotency
388
+ - rate limits and size limits
389
+ - progress epochs and estimates
390
+ - monitor validation and routing
391
+ - widget and viewer formatting
392
+
393
+ ### Integration
394
+
395
+ - action to trace to state to widget
396
+ - agent tool update followed by submit
397
+ - shell stream to update publication
398
+ - host park and resume
399
+ - notification outbox delivery to the origin session
400
+ - `status` with current updates
401
+
402
+ ### End to end
403
+
404
+ Use the real installed Pi runtime without a real model or destructive target:
405
+
406
+ 1. Start a fixture workflow with a scripted action that publishes progress.
407
+ 2. Observe progress in the in-Pi widget and `piw`.
408
+ 3. Verify the run trace and state projection.
409
+ 4. Start a short monitor fixture.
410
+ 5. Verify one custom notification per check.
411
+ 6. Verify no assistant turn starts from notification delivery.
412
+ 7. Start an interactive agent step and verify one compact custom message and one model turn.
413
+ 8. Expand the message and verify the full prompt and exact contract ids.
414
+ 9. Compare the provider-facing interactive prompt with the RPC prompt.
415
+ 10. Cancel during sleep and verify terminal state.
416
+ 11. Restart Pi and verify durable display and replay.
417
+
418
+ ## Required commands
419
+
420
+ Run in `osolmaz/pi-workflows` before release:
421
+
422
+ ```bash
423
+ npm run check
424
+ npm run test:e2e
425
+ npx slophammer-ts@latest dry .
426
+ npx slophammer-ts@latest check . --only ts.dependency-boundaries-required
427
+ git diff --check
428
+ ```
429
+
430
+ Run documentation checks:
431
+
432
+ ```bash
433
+ npx -y @simpledoc/simpledoc check
434
+ npx oxfmt --check README.md docs
435
+ python3 ~/.pi/agent/skills/kill-ai-smell/check.py docs/WORKFLOW_UPDATES.md
436
+ python3 ~/.pi/agent/skills/kill-ai-smell/check.py docs/WORKFLOW_STEP_MESSAGES.md
437
+ python3 ~/.pi/agent/skills/kill-ai-smell/check.py docs/MONITOR.md
438
+ python3 ~/.pi/agent/skills/kill-ai-smell/check.py docs/plans/2026-08-16-workflow-updates-plan.md
439
+ ```
440
+
441
+ Run package and real-Pi checks again after updating OnurPi. Run the tools skill sync in dry-run mode before applying it.
442
+
443
+ ## Acceptance criteria
444
+
445
+ - The public update API works from agent and action nodes.
446
+ - Shell updates use only workflow-author code and fixed execution settings.
447
+ - Every accepted update is ordered and fenced, with durable bounded storage and replay.
448
+ - Final outputs remain the only routing input.
449
+ - Workflows without updates preserve their current behavior and display.
450
+ - Progress is optional and supports several stable tracks.
451
+ - ETA is source-based or measured from facts and never invented by a model.
452
+ - Widget clocks update without a model call or per-tick write.
453
+ - Notifications remain in context and never start an assistant turn.
454
+ - Agent-step prompts remain complete for the model and appear as compact, expandable workflow cards.
455
+ - Interactive and RPC execution use the same model prompt.
456
+ - The built-in monitor reports every accepted check and has no quiet path.
457
+ - The monitor discloses its finite safety ceiling.
458
+ - TypeScript and Rust viewers agree on replayed progress.
459
+ - All required checks pass in Pi Workflows and OnurPi.
460
+ - The published package, OnurPi pin, monitor skill source, and installed Pi copy agree.
461
+
462
+ ## Risks and controls
463
+
464
+ ### Trace growth
465
+
466
+ High-rate updates can grow bundles quickly. Payload and rate limits apply alongside burst and current-key limits. Shell parsing applies backpressure.
467
+
468
+ ### Misleading ETA
469
+
470
+ The estimator uses observed facts, short rolling history, explicit confidence, and unavailable reasons. It never advances observed counters between samples.
471
+
472
+ ### Hidden behavior
473
+
474
+ Updates cannot route, execute, or notify. The graph retains control of completion, side effects, and user messages.
475
+
476
+ ### Duplicate notifications
477
+
478
+ Update idempotency and the existing notification occurrence index remain separate. Each monitor check reaches one notify node once.
479
+
480
+ ### Prompt and display drift
481
+
482
+ The renderer reads structured details, while one formatter produces the complete model prompt for interactive and RPC execution. Tests compare provider-facing content and verify that expansion shows the recorded prompt. The renderer never rebuilds instructions from display fields.
483
+
484
+ ### Upgrade interruption
485
+
486
+ The built-in monitor revision change can block resume of an old active monitor even though old bundles remain valid. Inventory active monitors before release and do not install the new built-in over work that still needs the old revision.
487
+
488
+ ### Rollback
489
+
490
+ Before any run publishes an update, rollback is an exact dependency pin to `0.5.3`. After update-bearing bundles exist, preserve them and keep the new reader available. Do not delete or rewrite bundles to force an old runtime to read fields it does not support.
491
+
492
+ ## Follow-up boundary
493
+
494
+ A truly indefinite monitor belongs in the controller runtime. This plan keeps the finite workflow safety ceiling and does not hide it behind a huge number or automatic restart. A later controller resource may use the same update and progress contracts alongside the notification and viewer contracts.
@@ -210,7 +210,8 @@ The full run projection (`WorkflowRunState` in
210
210
  "input": { "task": "fix the flaky test" },
211
211
  "outputs": {},
212
212
  "results": {},
213
- "steps": []
213
+ "steps": [],
214
+ "updates": []
214
215
  }
215
216
  ```
216
217
 
@@ -235,6 +236,10 @@ The full run projection (`WorkflowRunState` in
235
236
  - Per-node data lives in `outputs` (the accepted output of each finished node,
236
237
  latest attempt wins on loops) and `results` (the full result record of the
237
238
  latest attempt, including outcome and timing).
239
+ - `updates` contains the latest durable record for each `(type, key)` pair,
240
+ sorted by trace sequence. New runs start with an empty array. Older bundles
241
+ can omit it. Resume keeps it; checkpoint continuation starts a new empty
242
+ projection. The trace keeps the complete update history.
238
243
  - `steps` is the ordered history, one record per node attempt:
239
244
 
240
245
  ```json
@@ -289,7 +294,10 @@ One event per line, monotonically sequenced per run, schema
289
294
  }
290
295
  ```
291
296
 
292
- `scope` is one of `run`, `node`, `agent`, `action`, or `session`. `nodeId` and
297
+ `scope` is one of `run`, `node`, `agent`, `action`, or `session`.
298
+ A node-scoped `update_published` event carries the runtime update ID, type, key, and data;
299
+ its event sequence and timestamp are the update sequence and timestamp. See
300
+ [WORKFLOW_UPDATES.md](WORKFLOW_UPDATES.md) for the full contract. `nodeId` and
293
301
  `attemptId` are present on node-scoped and agent-scoped events. Consumers must
294
302
  ignore unknown event types and unknown payload fields so new ones can be added
295
303
  within the same schema version.
package/docs/workflows.md CHANGED
@@ -90,6 +90,9 @@ Long-running compute, action, and checkpoint callbacks should observe
90
90
  steps). When the node times out or the run is cancelled, the engine stops
91
91
  waiting immediately, but only cooperative callbacks stop doing work.
92
92
 
93
+ Function actions receive `WorkflowActionContext`, which adds
94
+ `publishUpdate(update)`. Other callbacks keep the read-only node context.
95
+
93
96
  ## Durable runs, parking, and resume
94
97
 
95
98
  Every interactive `/workflow` run is tracked in the project run queue (see
@@ -218,6 +221,32 @@ commands cannot exhaust memory. Both action forms record a receipt (command,
218
221
  exit code, duration) in the step record for auditability, including when the
219
222
  command fails.
220
223
 
224
+ A function action can publish a durable update without completing the node:
225
+
226
+ ```typescript
227
+ action({
228
+ run: async ({ publishUpdate }) => {
229
+ await publishUpdate({
230
+ type: "progress",
231
+ key: "overall",
232
+ data: {
233
+ schema: "pi-workflows.progress.v1",
234
+ status: "running",
235
+ completed: 40,
236
+ total: 100,
237
+ unit: "rows",
238
+ },
239
+ });
240
+ },
241
+ });
242
+ ```
243
+
244
+ A shell action can parse complete lines from stdout or stderr and return one
245
+ or more updates through `updates.parseLine`. Parsing applies backpressure and
246
+ keeps normal output capture. Lines and update data are each limited to 64 KiB.
247
+ See [WORKFLOW_UPDATES.md](WORKFLOW_UPDATES.md) for the envelope, progress
248
+ schema, limits, estimation, and error rules.
249
+
221
250
  ### checkpoint
222
251
 
223
252
  Ends the run in a `waiting` state for human review. The checkpoint bundle is
@@ -294,13 +323,15 @@ The model sees one `workflow` tool. Its `action` field supports:
294
323
  - `status` for the active run or a supplied run ID.
295
324
  - `pause`, `resume`, and `cancel` for the active run.
296
325
  - `answer` with checkpoint input and an optional run ID.
326
+ - `update` for a non-completing update from the current agent attempt.
297
327
  - `submit` for the current workflow step contract.
298
328
 
299
329
  A model-started run is queued until the model's current turn settles. The first
300
330
  workflow prompt then starts a new turn. This keeps the requesting turn outside
301
331
  the workflow's first attempt and prevents an early missing-submission reminder.
302
332
  The normal extension offers all actions. The headless RPC bridge offers only
303
- `submit`, so a workflow child cannot recursively control other runs.
333
+ `update` and `submit`, so a workflow child cannot recursively control other
334
+ runs.
304
335
 
305
336
  ### Built-in monitor
306
337
 
@@ -310,24 +341,23 @@ one looping workflow run. Its input is:
310
341
  ```json
311
342
  {
312
343
  "task": "Check pull request 123",
313
- "everyMinutes": 30,
314
- "reportWhen": "Checks fail or the state changes materially",
315
- "stopWhen": "The pull request is merged or closed",
316
- "maxChecks": 1000,
317
- "checkTimeoutMinutes": 60
344
+ "stopWhen": "The pull request is merged or closed"
318
345
  }
319
346
  ```
320
347
 
321
- The first check runs immediately. Each accepted check records a bounded current
322
- observation and chooses whether to continue, report, or stop. A report uses a
323
- separate agent node so its structured check result is validated before the user
324
- sees the message. The next check can read the previous accepted observation.
348
+ The first check runs immediately. `everyMinutes` defaults to 30. Each accepted
349
+ check must provide one concise report and choose `continue` or `stop`. The
350
+ runtime queues that report as a workflow notification with `triggerTurn:
351
+ false`, so it does not cause an assistant reply. A check can also provide
352
+ independent progress tracks. Pi Workflows validates the counts and calculates
353
+ rates, confidence, and ETA without a model.
325
354
 
326
- Intervals must be whole minutes from 1 through 1,440. `maxChecks` defaults to
327
- 1,000 and cannot exceed 1,000. `checkTimeoutMinutes` is optional and applies to
328
- check and report agent nodes. It must be from 5 through 1,440 minutes. Its
329
- default is the larger of 60 minutes and `everyMinutes`. The workflow also has
330
- a finite step limit and bounded observation and report sizes.
355
+ Intervals must be whole minutes from 1 through 1,440. When `stopWhen` is
356
+ omitted, the monitor stops only after an explicit user request. `maxChecks`
357
+ defaults to the disclosed safety ceiling of 1,000 and cannot exceed it. Callers
358
+ omit `maxChecks` unless the user requests a fixed count. `checkTimeoutMinutes`
359
+ is from 5 through 1,440 and defaults to the larger of 60 and `everyMinutes`.
360
+ See [MONITOR.md](MONITOR.md) for the check and progress schemas.
331
361
 
332
362
  The interval uses the existing shell action to launch the current Node
333
363
  executable with a timer. This works on every platform supported by Pi. The node
@@ -360,8 +390,18 @@ results. Submissions are rejected (with a reason the model sees) when no step
360
390
  is pending, the step id is wrong, the attempt id belongs to an earlier attempt
361
391
  of the same node (loops revisit node ids, so each attempt gets a fresh id), or
362
392
  `validate` throws.
363
- Acceptance resolves the step and the engine advances; the next agent prompt
364
- arrives as a new user message in the same conversation.
393
+ Acceptance resolves the step and the engine advances. In an interactive Pi
394
+ session, each agent prompt arrives as a `pi-workflows-agent-step` custom message
395
+ with `triggerTurn: true`. The model receives the complete prompt, while the
396
+ conversation shows a compact workflow and node card. Expanding tool output with
397
+ Ctrl+O shows the exact contract and full prompt. Reminders and resumed prompts
398
+ use the same card and keep the active attempt id.
399
+
400
+ Headless RPC execution receives the same complete prompt without TUI metadata.
401
+ Workflow notifications use a separate message type with `triggerTurn: false`,
402
+ so a notification does not start an assistant response. See
403
+ [WORKFLOW_STEP_MESSAGES.md](WORKFLOW_STEP_MESSAGES.md) for the message contract
404
+ and renderer rules.
365
405
 
366
406
  ## Result presentation
367
407
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osolmaz/pi-workflows",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Workflow and controller runtime with a live terminal viewer for the pi coding agent",
5
5
  "keywords": [
6
6
  "pi-package"
@@ -4,7 +4,7 @@ import monitorWorkflow from "./monitor.workflow.js";
4
4
  export const builtinWorkflowCatalog = new BuiltinWorkflowCatalog([
5
5
  {
6
6
  id: "monitor",
7
- revision: "2",
7
+ revision: "3",
8
8
  definition: monitorWorkflow,
9
9
  legacySources: [
10
10
  {