@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.
- package/README.md +13 -3
- package/dist/builtins/catalog.js +1 -1
- package/dist/builtins/monitor.workflow.d.ts +25 -69
- package/dist/builtins/monitor.workflow.js +194 -123
- package/dist/builtins/monitor.workflow.js.map +1 -1
- package/dist/extension/executor.d.ts +7 -2
- package/dist/extension/executor.js +20 -14
- package/dist/extension/executor.js.map +1 -1
- package/dist/extension/index.js +56 -15
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/step-message.d.ts +24 -0
- package/dist/extension/step-message.js +106 -0
- package/dist/extension/step-message.js.map +1 -0
- package/dist/extension/widget.d.ts +4 -2
- package/dist/extension/widget.js +111 -17
- package/dist/extension/widget.js.map +1 -1
- package/dist/extension/workflow-tool.d.ts +9 -0
- package/dist/extension/workflow-tool.js +10 -0
- package/dist/extension/workflow-tool.js.map +1 -1
- package/dist/host/rpc-bridge.js +25 -11
- package/dist/host/rpc-bridge.js.map +1 -1
- package/dist/host/rpc-executor.d.ts +2 -2
- package/dist/host/rpc-executor.js +23 -12
- package/dist/host/rpc-executor.js.map +1 -1
- package/dist/viewer/cli.js +1 -1
- package/dist/viewer/cli.js.map +1 -1
- package/dist/viewer/render.js +14 -0
- package/dist/viewer/render.js.map +1 -1
- package/dist/viewer/tui.js +1 -1
- package/dist/viewer/tui.js.map +1 -1
- package/dist/workflows/engine.d.ts +6 -1
- package/dist/workflows/engine.js +88 -6
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/index.d.ts +4 -2
- package/dist/workflows/index.js +2 -0
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/progress.d.ts +34 -0
- package/dist/workflows/progress.js +268 -0
- package/dist/workflows/progress.js.map +1 -0
- package/dist/workflows/schema.js +21 -1
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/shell.d.ts +2 -2
- package/dist/workflows/shell.js +103 -25
- package/dist/workflows/shell.js.map +1 -1
- package/dist/workflows/store.d.ts +15 -2
- package/dist/workflows/store.js +44 -2
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/types.d.ts +52 -1
- package/dist/workflows/updates.d.ts +15 -0
- package/dist/workflows/updates.js +188 -0
- package/dist/workflows/updates.js.map +1 -0
- package/docs/DESIGN_PHILOSOPHY.md +51 -0
- package/docs/MONITOR.md +282 -0
- package/docs/WORKFLOW_STEP_MESSAGES.md +141 -0
- package/docs/WORKFLOW_UPDATES.md +416 -0
- package/docs/development.md +7 -3
- package/docs/plans/2026-08-13-responsive-workflow-widget-plan.md +11 -3
- package/docs/plans/2026-08-16-workflow-updates-plan.md +494 -0
- package/docs/run-bundles.md +10 -2
- package/docs/workflows.md +57 -17
- package/package.json +1 -1
- package/src/builtins/catalog.ts +1 -1
- package/src/builtins/monitor.workflow.ts +217 -148
- package/src/extension/executor.ts +36 -14
- package/src/extension/index.ts +89 -23
- package/src/extension/step-message.ts +145 -0
- package/src/extension/widget.ts +158 -14
- package/src/extension/workflow-tool.ts +22 -0
- package/src/host/rpc-bridge.ts +37 -14
- package/src/host/rpc-executor.ts +35 -14
- package/src/viewer/cli.ts +1 -1
- package/src/viewer/render.ts +27 -0
- package/src/viewer/tui.ts +1 -1
- package/src/workflows/engine.ts +117 -4
- package/src/workflows/index.ts +32 -0
- package/src/workflows/progress.ts +326 -0
- package/src/workflows/schema.ts +23 -1
- package/src/workflows/shell.ts +109 -26
- package/src/workflows/store.ts +67 -2
- package/src/workflows/types.ts +78 -1
- package/src/workflows/updates.ts +208 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# Workflow updates
|
|
2
|
+
|
|
3
|
+
This specification defines durable, non-terminal updates from running Pi Workflows nodes. An update reports current state without finishing a node or choosing a graph route.
|
|
4
|
+
|
|
5
|
+
## Minimal examples
|
|
6
|
+
|
|
7
|
+
A function action publishes current state while it runs:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const processRows = action({
|
|
11
|
+
run: async (context) => {
|
|
12
|
+
await context.publishUpdate({
|
|
13
|
+
type: "progress",
|
|
14
|
+
key: "worker-a",
|
|
15
|
+
data: {
|
|
16
|
+
schema: "pi-workflows.progress.v1",
|
|
17
|
+
label: "Worker A",
|
|
18
|
+
status: "running",
|
|
19
|
+
phase: "processing",
|
|
20
|
+
completed: 420,
|
|
21
|
+
total: 1_000,
|
|
22
|
+
unit: "rows",
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return { processed: 420 };
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
An agent publishes the same update through the existing `workflow` tool:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"action": "update",
|
|
36
|
+
"step": "process",
|
|
37
|
+
"attempt": "017f5d57-83f1-4d2d-88e6-3dbf878fed17",
|
|
38
|
+
"update": {
|
|
39
|
+
"type": "progress",
|
|
40
|
+
"key": "worker-a",
|
|
41
|
+
"data": {
|
|
42
|
+
"schema": "pi-workflows.progress.v1",
|
|
43
|
+
"label": "Worker A",
|
|
44
|
+
"status": "running",
|
|
45
|
+
"completed": 420,
|
|
46
|
+
"total": 1000,
|
|
47
|
+
"unit": "rows"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The `update` tool action does not complete the agent step. The agent still calls `submit` once with the final step output.
|
|
54
|
+
|
|
55
|
+
## Place in the workflow model
|
|
56
|
+
|
|
57
|
+
Pi Workflows keeps its current node primitives:
|
|
58
|
+
|
|
59
|
+
- `agent` for model judgment and language work
|
|
60
|
+
- `compute` for pure local calculation
|
|
61
|
+
- `action` for external reads and effects
|
|
62
|
+
- `notify` for durable user messages
|
|
63
|
+
- `checkpoint` for human or external input
|
|
64
|
+
|
|
65
|
+
`shell` remains the command form of `action`. Updates are a capability available while an agent or action runs. They are not a node type.
|
|
66
|
+
|
|
67
|
+
Updates do not:
|
|
68
|
+
|
|
69
|
+
- finish a node
|
|
70
|
+
- write to `outputs` or `results`
|
|
71
|
+
- choose an edge
|
|
72
|
+
- change workflow input
|
|
73
|
+
- execute commands
|
|
74
|
+
- notify the user by themselves
|
|
75
|
+
- trigger a model turn
|
|
76
|
+
|
|
77
|
+
A node's final result remains the only value that completes the node and controls routing.
|
|
78
|
+
|
|
79
|
+
## Public types
|
|
80
|
+
|
|
81
|
+
The workflow layer adds these public types:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
export type WorkflowUpdateInput = {
|
|
85
|
+
type: string;
|
|
86
|
+
key: string;
|
|
87
|
+
data: Record<string, unknown>;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export type WorkflowUpdateRecord = {
|
|
91
|
+
updateId: string;
|
|
92
|
+
seq: number;
|
|
93
|
+
at: string;
|
|
94
|
+
runId: string;
|
|
95
|
+
nodeId: string;
|
|
96
|
+
attemptId: string;
|
|
97
|
+
type: string;
|
|
98
|
+
key: string;
|
|
99
|
+
data: Record<string, unknown>;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export type WorkflowUpdateReceipt = Pick<
|
|
103
|
+
WorkflowUpdateRecord,
|
|
104
|
+
"updateId" | "seq" | "at" | "type" | "key"
|
|
105
|
+
>;
|
|
106
|
+
|
|
107
|
+
export type WorkflowActionContext<TInput = unknown> = WorkflowNodeContext<TInput> & {
|
|
108
|
+
publishUpdate(update: WorkflowUpdateInput): Promise<WorkflowUpdateReceipt>;
|
|
109
|
+
};
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Function actions receive `WorkflowActionContext`. Compute and notify callbacks retain the read-only `WorkflowNodeContext`. Prompt builders and validators have the same restriction, as do checkpoint callbacks.
|
|
113
|
+
|
|
114
|
+
## Update envelope
|
|
115
|
+
|
|
116
|
+
### `type`
|
|
117
|
+
|
|
118
|
+
`type` names the update contract.
|
|
119
|
+
|
|
120
|
+
Rules:
|
|
121
|
+
|
|
122
|
+
- 1 to 64 characters
|
|
123
|
+
- lowercase ASCII letters, numbers, dots, and hyphens
|
|
124
|
+
- starts with a letter
|
|
125
|
+
- matches `[a-z][a-z0-9.-]{0,63}`
|
|
126
|
+
|
|
127
|
+
`progress` is the first package-defined type. Other update types may define their own data contracts.
|
|
128
|
+
|
|
129
|
+
### `key`
|
|
130
|
+
|
|
131
|
+
`key` identifies one current item within an update type. The pair `(type, key)` is unique within a run.
|
|
132
|
+
|
|
133
|
+
Rules:
|
|
134
|
+
|
|
135
|
+
- 1 to 128 characters
|
|
136
|
+
- ASCII letters, numbers, dots, underscores, colons, slashes, and hyphens
|
|
137
|
+
- starts with a letter or number
|
|
138
|
+
- matches `[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}`
|
|
139
|
+
|
|
140
|
+
Keys are case-sensitive. A publisher must reuse the same key for the same item throughout a run.
|
|
141
|
+
|
|
142
|
+
### `data`
|
|
143
|
+
|
|
144
|
+
`data` contains the full current state for the key. It is a non-null JSON object. A new update replaces the current projection for the same `(type, key)` pair. It does not merge with the previous object.
|
|
145
|
+
|
|
146
|
+
The generic update layer allows fields it does not understand inside `data`. A package-defined update type may reject unknown fields.
|
|
147
|
+
|
|
148
|
+
The encoded object must not exceed 64 KiB. Large logs and binary data do not belong in updates. Nodes should return large final values through normal outputs, where the run-bundle artifact rules apply.
|
|
149
|
+
|
|
150
|
+
### Runtime-owned fields
|
|
151
|
+
|
|
152
|
+
The runtime adds `updateId`, `seq`, `at`, `runId`, `nodeId`, and `attemptId`. Publishers cannot set or override them.
|
|
153
|
+
|
|
154
|
+
`seq` is the sequence of the matching trace event. `at` is the runtime receipt time in RFC 3339 UTC form. `updateId` is unique within the run.
|
|
155
|
+
|
|
156
|
+
## Agent tool action
|
|
157
|
+
|
|
158
|
+
The `workflow` tool adds this input variant:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
{
|
|
162
|
+
action: "update";
|
|
163
|
+
step: string;
|
|
164
|
+
attempt: string;
|
|
165
|
+
update: WorkflowUpdateInput;
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The engine accepts it only when:
|
|
170
|
+
|
|
171
|
+
- an agent step is active
|
|
172
|
+
- `step` matches the active node id
|
|
173
|
+
- `attempt` matches the active attempt id
|
|
174
|
+
- the attempt has not timed out, failed, completed, or been cancelled
|
|
175
|
+
- the caller still owns the run claim
|
|
176
|
+
- the update passes envelope and type-specific validation
|
|
177
|
+
|
|
178
|
+
A successful call returns `WorkflowUpdateReceipt`. It leaves the agent step open. Invalid calls return a tool error and do not write a trace event.
|
|
179
|
+
|
|
180
|
+
The Pi tool call id is the idempotency key for an agent update. Re-delivery of the same tool call returns the first receipt instead of appending a duplicate update.
|
|
181
|
+
|
|
182
|
+
The existing `status` action remains read-only. It reports what the engine knows and includes the current update projection. It does not publish an update or inspect an external target.
|
|
183
|
+
|
|
184
|
+
## Function actions
|
|
185
|
+
|
|
186
|
+
Function actions call `await context.publishUpdate(update)`. The promise resolves only after the trace event and state projection are durable.
|
|
187
|
+
|
|
188
|
+
An uncaught validation, fencing, storage, or rate error fails the action. An action may catch a rejected publication when losing a display update should not fail its main work. Cancellation and claim loss still apply.
|
|
189
|
+
|
|
190
|
+
Retries run under a new attempt id. Their updates form a new attempt history even when they reuse the same `(type, key)` pair.
|
|
191
|
+
|
|
192
|
+
## Shell actions
|
|
193
|
+
|
|
194
|
+
Shell actions may define a line parser:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
shell({
|
|
198
|
+
exec: () => ({ command: "worker", args: ["--progress=ndjson"] }),
|
|
199
|
+
updates: {
|
|
200
|
+
streams: ["stdout"],
|
|
201
|
+
parseLine: ({ stream, text }, context) => {
|
|
202
|
+
const value = JSON.parse(text);
|
|
203
|
+
if (value.kind !== "progress") return undefined;
|
|
204
|
+
return {
|
|
205
|
+
type: "progress",
|
|
206
|
+
key: value.worker,
|
|
207
|
+
data: value.data,
|
|
208
|
+
};
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
`streams` may contain `stdout`, `stderr`, or both. It defaults to `stdout`. `parseLine` receives complete UTF-8 lines and may return one update, an array of updates, or `undefined`.
|
|
215
|
+
|
|
216
|
+
The runtime keeps normal stdout and stderr capture. It applies backpressure while parsing and publishing updates. A line longer than 64 KiB, invalid UTF-8, a parser error, or a publication error terminates the command and fails the node. A final line without a newline is parsed when the stream closes.
|
|
217
|
+
|
|
218
|
+
The parser is workflow-author code. Update data never selects or changes the command, arguments, working directory, environment, or privileges.
|
|
219
|
+
|
|
220
|
+
## Controllers and hosts
|
|
221
|
+
|
|
222
|
+
The engine exposes the same publication operation through its runner interface. A controller or standalone host may publish only for a workflow run and attempt whose claim it owns.
|
|
223
|
+
|
|
224
|
+
Controller resource events remain controller events. They become workflow updates only when a controller deliberately publishes them to a linked workflow run.
|
|
225
|
+
|
|
226
|
+
## Persistence
|
|
227
|
+
|
|
228
|
+
Each accepted update appends one `update_published` event to `trace.ndjson`:
|
|
229
|
+
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"seq": 18,
|
|
233
|
+
"at": "2026-08-16T10:15:30.000Z",
|
|
234
|
+
"scope": "node",
|
|
235
|
+
"type": "update_published",
|
|
236
|
+
"runId": "20260816T100000Z-import-2f81a9c3",
|
|
237
|
+
"nodeId": "process",
|
|
238
|
+
"attemptId": "017f5d57-83f1-4d2d-88e6-3dbf878fed17",
|
|
239
|
+
"payload": {
|
|
240
|
+
"updateId": "upd_01K2GZVY3A4D7X8J9M0N",
|
|
241
|
+
"type": "progress",
|
|
242
|
+
"key": "worker-a",
|
|
243
|
+
"data": {
|
|
244
|
+
"schema": "pi-workflows.progress.v1",
|
|
245
|
+
"status": "running",
|
|
246
|
+
"completed": 420,
|
|
247
|
+
"total": 1000,
|
|
248
|
+
"unit": "rows"
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
`state.json` adds an optional `updates` array containing only the latest record for each `(type, key)` pair. The array is sorted by `seq`. Omission means that the run has never published an update. New runs write an empty array from their first state projection. The trace remains the full history and source of truth.
|
|
255
|
+
|
|
256
|
+
A resumed run keeps its latest projection and appends new update events. A checkpoint continuation starts a new run with an empty update projection. Updates are scoped to one run and are not carried into continuation bundles.
|
|
257
|
+
|
|
258
|
+
The state projection supports at most 1,024 current `(type, key)` pairs. A publication that would exceed the limit is rejected. Trace history remains append-only.
|
|
259
|
+
|
|
260
|
+
The update envelope is an additive part of `pi-workflows.trace-event.v1` and `pi-workflows.run-state.v1`. Existing bundles without `updates` remain valid under the omission rule. The implementation uses one current schema and does not add migration files, fallback formats, or dual writes. New bundles that contain updates are not required to work with older package releases.
|
|
261
|
+
|
|
262
|
+
## Ordering and limits
|
|
263
|
+
|
|
264
|
+
Updates use the run store's existing serialized write chain and claim fence. Their trace sequence defines their total order.
|
|
265
|
+
|
|
266
|
+
Default safety limits per run are:
|
|
267
|
+
|
|
268
|
+
| Limit | Value |
|
|
269
|
+
| --------------------------- | --------------------: |
|
|
270
|
+
| Encoded `data` size | 64 KiB |
|
|
271
|
+
| Current `(type, key)` pairs | 1,024 |
|
|
272
|
+
| Sustained publication rate | 20 updates per second |
|
|
273
|
+
| Publication burst | 100 updates |
|
|
274
|
+
|
|
275
|
+
The rate limiter uses a token bucket. A rejected update is not queued silently. Agent callers receive a tool error; code publishers receive a rejected promise.
|
|
276
|
+
|
|
277
|
+
The standalone host and Pi extension apply the same limits.
|
|
278
|
+
|
|
279
|
+
## Progress update type
|
|
280
|
+
|
|
281
|
+
`progress` is the standard optional profile for measurable work. Each key represents one process, worker, phase owner, or explicit overall total.
|
|
282
|
+
|
|
283
|
+
Minimal progress data:
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"schema": "pi-workflows.progress.v1",
|
|
288
|
+
"status": "running"
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Measured progress:
|
|
293
|
+
|
|
294
|
+
```json
|
|
295
|
+
{
|
|
296
|
+
"schema": "pi-workflows.progress.v1",
|
|
297
|
+
"label": "Worker A",
|
|
298
|
+
"status": "running",
|
|
299
|
+
"phase": "processing",
|
|
300
|
+
"completed": 420,
|
|
301
|
+
"total": 1000,
|
|
302
|
+
"unit": "rows",
|
|
303
|
+
"sourceUpdatedAt": "2026-08-16T10:15:27.000Z",
|
|
304
|
+
"sourceEstimatedFinishAt": "2026-08-16T10:34:00.000Z"
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Fields:
|
|
309
|
+
|
|
310
|
+
| Field | Required | Type | Meaning |
|
|
311
|
+
| ------------------------- | ----------- | ------ | ------------------------------------------------ |
|
|
312
|
+
| `schema` | Yes | string | Must equal `pi-workflows.progress.v1`. |
|
|
313
|
+
| `status` | Yes | string | Current state of the track. |
|
|
314
|
+
| `label` | No | string | User-facing name, at most 200 characters. |
|
|
315
|
+
| `phase` | No | string | Stable phase name, at most 128 characters. |
|
|
316
|
+
| `completed` | No | number | Work completed in `unit`. |
|
|
317
|
+
| `total` | No | number | Known total work in `unit`. |
|
|
318
|
+
| `unit` | Conditional | string | Required when `completed` or `total` is present. |
|
|
319
|
+
| `sourceUpdatedAt` | No | string | Time when the target produced the facts. |
|
|
320
|
+
| `sourceEstimatedFinishAt` | No | string | Finish estimate supplied by the target. |
|
|
321
|
+
|
|
322
|
+
`status` is one of `pending`, `running`, `waiting`, `blocked`, `completed`, `failed`, `cancelled`, or `unknown`.
|
|
323
|
+
|
|
324
|
+
`completed` must be finite and at least zero. `total` must be finite, greater than zero, and at least `completed`. `unit` is 1 to 32 printable characters after trimming. Time fields use RFC 3339 with an offset.
|
|
325
|
+
|
|
326
|
+
A progress object is a full snapshot for its key. Omitted optional fields clear their previous values. Publishers mark finished tracks with a terminal status instead of deleting them.
|
|
327
|
+
|
|
328
|
+
The reserved key `overall` represents an explicit aggregate supplied by the workflow. Pi Workflows never combines unrelated tracks automatically. Without `overall`, displays list independent tracks.
|
|
329
|
+
|
|
330
|
+
Unknown fields in `pi-workflows.progress.v1` are validation errors.
|
|
331
|
+
|
|
332
|
+
## Progress estimation
|
|
333
|
+
|
|
334
|
+
The workflows layer exports pure validation and estimation helpers. Reduction and formatting use the same module. The helpers import no Pi, extension, controller, or viewer code.
|
|
335
|
+
|
|
336
|
+
The estimator groups records by run and key. A new estimation epoch starts when:
|
|
337
|
+
|
|
338
|
+
- `phase` changes
|
|
339
|
+
- `unit` changes
|
|
340
|
+
- `total` changes
|
|
341
|
+
- `completed` decreases
|
|
342
|
+
- a terminal track returns to a non-terminal state
|
|
343
|
+
|
|
344
|
+
It derives elapsed time from runtime update timestamps. `sourceUpdatedAt` reports staleness. Trace sequence orders records.
|
|
345
|
+
|
|
346
|
+
A measured ETA requires a known total and at least two usable samples in the current epoch. The estimator calculates consecutive wall-clock rates over the latest eight usable intervals. Zero-progress intervals remain in the window. Intervals with non-positive elapsed time are ignored.
|
|
347
|
+
|
|
348
|
+
The median interval rate is the central estimate. The 25th and 75th percentile rates form the ETA range. The faster rate gives the lower remaining-time bound and the slower rate gives the upper bound. One usable interval has low confidence. With two through four intervals, a ratio of interquartile range to median no greater than 0.5 gives medium confidence; a wider spread gives low confidence. With five or more intervals, a ratio no greater than 0.25 gives high confidence, a ratio through 0.5 gives medium confidence, and a wider spread gives low confidence. A non-positive median makes ETA unavailable. A non-positive lower rate removes the upper time bound, so the formatter shows the central ETA with low confidence instead of a closed range.
|
|
349
|
+
|
|
350
|
+
A fresh `sourceEstimatedFinishAt` takes priority over a measured ETA and is labelled as a source estimate. It is fresh when it comes from the latest track update, is later than the matching `sourceUpdatedAt` or runtime receipt time, and has not passed. When `sourceUpdatedAt` is absent, the runtime receipt time is the source time. A passed source estimate is expired and does not override a measured estimate. Pi Workflows does not ask a model to invent an ETA. When the target supplies no usable estimate and the samples cannot support one, the formatter says `ETA unavailable` and states the reason.
|
|
351
|
+
|
|
352
|
+
For `waiting` or `blocked` tracks, measured ETA is paused and the display reports the current state. A source estimate may still be shown when the target reports one. For terminal tracks, remaining work and ETA are omitted.
|
|
353
|
+
|
|
354
|
+
The estimator never advances `completed` between samples. Live displays may update elapsed time, sample age, the next scheduled check, and the remaining duration to an estimated finish time.
|
|
355
|
+
|
|
356
|
+
## Presentation
|
|
357
|
+
|
|
358
|
+
The compact Pi widget and `piw` recognize `progress` updates. Other update types remain visible in trace and step inspection without receiving a special renderer.
|
|
359
|
+
|
|
360
|
+
The compact widget:
|
|
361
|
+
|
|
362
|
+
- behaves as it does today when no progress update exists
|
|
363
|
+
- shows the `overall` track first when present
|
|
364
|
+
- prioritizes failed, blocked, and waiting tracks
|
|
365
|
+
- uses remaining lines for active tracks
|
|
366
|
+
- stays within Pi's 10-line widget limit
|
|
367
|
+
- supports the existing manual scroll controls
|
|
368
|
+
- updates clocks from the existing widget ticker without model calls or state writes
|
|
369
|
+
|
|
370
|
+
A typical line is:
|
|
371
|
+
|
|
372
|
+
```text
|
|
373
|
+
Worker A 420/1,000 rows ETA 18–20m
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
A footer line may show:
|
|
377
|
+
|
|
378
|
+
```text
|
|
379
|
+
Last update 7m ago next check 23m
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
`piw` shows every track, estimate basis, sample count, confidence, update history, and source timestamps.
|
|
383
|
+
|
|
384
|
+
## Notifications and model context
|
|
385
|
+
|
|
386
|
+
Publishing an update does not notify the user. A workflow uses the existing `notify` node when it wants a durable report.
|
|
387
|
+
|
|
388
|
+
The extension delivers workflow notifications with a custom Pi message and explicit `triggerTurn: false`. The message remains in session history and later model context, but its arrival does not start an assistant response. Workflow reports do not use `sendUserMessage`.
|
|
389
|
+
|
|
390
|
+
Agent-step instructions use a separate message contract because they must start a model turn. See [WORKFLOW_STEP_MESSAGES.md](WORKFLOW_STEP_MESSAGES.md).
|
|
391
|
+
|
|
392
|
+
## Validation and errors
|
|
393
|
+
|
|
394
|
+
The implementation must test and reject:
|
|
395
|
+
|
|
396
|
+
- invalid type or key names
|
|
397
|
+
- non-object or non-JSON data
|
|
398
|
+
- oversized data
|
|
399
|
+
- too many current keys
|
|
400
|
+
- rate-limit violations
|
|
401
|
+
- updates for inactive or mismatched attempts
|
|
402
|
+
- updates after timeout or cancellation
|
|
403
|
+
- writes without the current claim
|
|
404
|
+
- malformed shell update lines
|
|
405
|
+
- invalid progress fields
|
|
406
|
+
- duplicate agent tool delivery
|
|
407
|
+
|
|
408
|
+
Validation errors must identify the field and rule. They must not include unrelated update data, private run contents, or command environment values.
|
|
409
|
+
|
|
410
|
+
## Security and boundaries
|
|
411
|
+
|
|
412
|
+
Updates are data. They do not grant permission to execute a command, retry work, change a target, publish an artifact, or increase spending.
|
|
413
|
+
|
|
414
|
+
Run bundles are private and may contain update data from external systems. Existing bundle permissions and export warnings apply.
|
|
415
|
+
|
|
416
|
+
This feature does not add remote transports, a metrics database, global aggregation, automatic polling, or a new Pi core API. Workflow authors remain responsible for the trust and cost of their agent, action, shell, and controller code.
|
package/docs/development.md
CHANGED
|
@@ -15,7 +15,9 @@ tui/ Rust piw viewer and live replay server
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
The dependency direction is enforced by `slophammer.yml`. `src/workflows`
|
|
18
|
-
imports nothing outside itself and never imports Pi.
|
|
18
|
+
imports nothing outside itself and never imports Pi. Durable updates,
|
|
19
|
+
progress validation, estimation, and text formatting stay in this layer so the
|
|
20
|
+
engine, extension, hosts, and viewers share one contract. `src/builtins` contains
|
|
19
21
|
package-owned definitions and imports only the public workflow engine.
|
|
20
22
|
`src/controllers` may import the public workflow engine for child-run
|
|
21
23
|
scheduling. `src/extension` and `src/host` may also import the built-in catalog.
|
|
@@ -38,8 +40,10 @@ in `src/viewer`
|
|
|
38
40
|
composes the full detail view (header, graph, step timeline, step inspector)
|
|
39
41
|
and stays pure so tests can assert on rendered lines.
|
|
40
42
|
|
|
41
|
-
The
|
|
42
|
-
|
|
43
|
+
The TypeScript and Rust viewers both show progress tracks, sample counts,
|
|
44
|
+
confidence, update time, and ETA from bundle data. The Rust viewer also keeps
|
|
45
|
+
graph layout and plain rendering in parity with `src/render`, then applies
|
|
46
|
+
ratatui-only presentation through semantic canvas
|
|
43
47
|
roles and `tui/src/theme`. Catppuccin is the default. Theme colors must be
|
|
44
48
|
chosen in the theme layer rather than directly in UI components. Graph node
|
|
45
49
|
surfaces are intentional styled spaces, so changing the sparse canvas must
|
|
@@ -18,6 +18,11 @@ The live workflow widget must remain easy to scan without using most of the conv
|
|
|
18
18
|
- Put the status glyph first and a one-column node-type glyph second.
|
|
19
19
|
- Use terminal-safe type glyphs: `●` agent, `ƒ` compute, `!` notification, `$` shell action, `*` function action, and `◆` checkpoint.
|
|
20
20
|
- Show concise runtime details when space permits: repeated visits, active elapsed time, latest completed duration, current status detail, and node errors.
|
|
21
|
+
- Use Pi's supplied theme for TUI colors. Keep RPC and other non-TUI widget output plain.
|
|
22
|
+
- Make the active node unmistakable by coloring its full line with the theme accent and making its name bold.
|
|
23
|
+
- Color a held or waiting focus line with the warning color.
|
|
24
|
+
- Color only the status glyph for completed and failed nodes, color failed error text, and dim pending nodes and non-focused type glyphs.
|
|
25
|
+
- Keep status glyphs as the non-color state signal.
|
|
21
26
|
- Keep the widget within Pi's 10-line budget.
|
|
22
27
|
- Ensure every line has a visible width less than or equal to the supplied width.
|
|
23
28
|
- Preserve manual vertical scrolling for long node lists.
|
|
@@ -42,14 +47,14 @@ Optional fields are omitted when they do not apply and truncated from the right
|
|
|
42
47
|
|
|
43
48
|
Node-type glyphs come from one shared formatter used by both the compact widget and graph viewer. The node snapshot already distinguishes shell actions from function actions, so this change needs no workflow schema change. A shell action named `sleep` appears as `$ sleep`; it does not become a new wait-node type.
|
|
44
49
|
|
|
45
|
-
The extension installs one Pi widget component for each active workflow. Its `render(width)` method reads the latest workflow state and calls `buildWidgetView` with that width. State changes request a normal Pi render by setting the component again. Pi calls the component with the current width after terminal resizes. The standalone viewer remains the place to inspect the full workflow graph.
|
|
50
|
+
The extension installs one Pi widget component for each active workflow. The documented component factory supplies Pi's current `Theme`. Its `render(width)` method reads the latest workflow state and calls `buildWidgetView` with that width and theme. State changes request a normal Pi render by setting the component again. Pi calls the component with the current width after terminal resizes. RPC mode keeps using serializable string arrays and does not receive color codes. The standalone viewer remains the place to inspect the full workflow graph.
|
|
46
51
|
|
|
47
52
|
## Contract impact
|
|
48
53
|
|
|
49
54
|
- Session state: no change.
|
|
50
55
|
- Other persistent data: no change.
|
|
51
56
|
- Pi internals: no change.
|
|
52
|
-
- Public API: documented component-form `ctx.ui.setWidget` and `Component.render(width)`.
|
|
57
|
+
- Public API: documented component-form `ctx.ui.setWidget`, its supplied `Theme`, and `Component.render(width)`.
|
|
53
58
|
|
|
54
59
|
## Acceptance criteria
|
|
55
60
|
|
|
@@ -59,7 +64,10 @@ The extension installs one Pi widget component for each active workflow. Its `re
|
|
|
59
64
|
- Every rendered line satisfies `visibleWidth(line) <= width`.
|
|
60
65
|
- Each real node and action subtype uses its documented one-column glyph.
|
|
61
66
|
- A repeated node shows its visit count, and active and completed nodes show useful timing.
|
|
62
|
-
- The active node remains visible in a long workflow.
|
|
67
|
+
- The active node remains visible in a long workflow and its full line uses the theme accent with a bold name.
|
|
68
|
+
- Completed, failed, waiting, and pending states use the specified theme roles without relying on color alone.
|
|
69
|
+
- TUI lines remain width-safe after color codes are added.
|
|
70
|
+
- RPC widget lines contain no ANSI color codes.
|
|
63
71
|
- Existing scrolling and workflow execution behavior remain unchanged.
|
|
64
72
|
|
|
65
73
|
## Verification
|