@osolmaz/pi-workflows 0.14.0 → 0.15.1
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 +78 -110
- package/dist/builtins/autoimplement.workflow.js +3 -1
- package/dist/builtins/autoimplement.workflow.js.map +1 -1
- package/dist/builtins/change-verification.workflow.js +7 -2
- package/dist/builtins/change-verification.workflow.js.map +1 -1
- package/dist/builtins/metadata.d.ts +22 -0
- package/dist/builtins/metadata.js +10 -0
- package/dist/builtins/metadata.js.map +1 -0
- package/dist/builtins/monitor.workflow.js +4 -1
- package/dist/builtins/monitor.workflow.js.map +1 -1
- package/dist/builtins/sanity-check.workflow.js +4 -1
- package/dist/builtins/sanity-check.workflow.js.map +1 -1
- package/dist/builtins/workspace-preparation.workflow.js +3 -1
- package/dist/builtins/workspace-preparation.workflow.js.map +1 -1
- package/dist/controllers/index.d.ts +0 -1
- package/dist/controllers/index.js +0 -1
- package/dist/controllers/index.js.map +1 -1
- package/dist/controllers/sqlite.d.ts +51 -0
- package/dist/controllers/sqlite.js +433 -57
- package/dist/controllers/sqlite.js.map +1 -1
- package/dist/controllers/store.d.ts +1 -0
- package/dist/extension/controller-command.d.ts +22 -0
- package/dist/extension/controller-command.js +32 -0
- package/dist/extension/controller-command.js.map +1 -0
- package/dist/extension/index.d.ts +2 -9
- package/dist/extension/index.js +876 -3447
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/message-card.d.ts +13 -0
- package/dist/extension/message-card.js +46 -0
- package/dist/extension/message-card.js.map +1 -0
- package/dist/extension/step-message.d.ts +5 -6
- package/dist/extension/step-message.js +41 -52
- package/dist/extension/step-message.js.map +1 -1
- package/dist/host/child-worker-supervisor.d.ts +50 -0
- package/dist/host/child-worker-supervisor.js +159 -0
- package/dist/host/child-worker-supervisor.js.map +1 -0
- package/dist/host/client.d.ts +48 -0
- package/dist/host/client.js +216 -0
- package/dist/host/client.js.map +1 -0
- package/dist/host/controller-worker-entry.d.ts +2 -0
- package/dist/host/controller-worker-entry.js +244 -0
- package/dist/host/controller-worker-entry.js.map +1 -0
- package/dist/host/controller-worker-protocol.d.ts +36 -0
- package/dist/host/controller-worker-protocol.js +49 -0
- package/dist/host/controller-worker-protocol.js.map +1 -0
- package/dist/host/controller-worker-supervisor.d.ts +21 -0
- package/dist/host/controller-worker-supervisor.js +54 -0
- package/dist/host/controller-worker-supervisor.js.map +1 -0
- package/dist/host/host-entry.d.ts +2 -0
- package/dist/host/host-entry.js +23 -0
- package/dist/host/host-entry.js.map +1 -0
- package/dist/host/processes.d.ts +17 -12
- package/dist/host/processes.js +154 -50
- package/dist/host/processes.js.map +1 -1
- package/dist/host/protocol.d.ts +38 -0
- package/dist/host/protocol.js +156 -0
- package/dist/host/protocol.js.map +1 -0
- package/dist/host/resolver-entry.d.ts +49 -0
- package/dist/host/resolver-entry.js +149 -0
- package/dist/host/resolver-entry.js.map +1 -0
- package/dist/host/rpc-executor.d.ts +14 -3
- package/dist/host/rpc-executor.js +63 -30
- package/dist/host/rpc-executor.js.map +1 -1
- package/dist/host/runner.d.ts +95 -29
- package/dist/host/runner.js +2311 -348
- package/dist/host/runner.js.map +1 -1
- package/dist/host/state.d.ts +174 -0
- package/dist/host/state.js +689 -0
- package/dist/host/state.js.map +1 -0
- package/dist/host/worker-entry.d.ts +10 -0
- package/dist/host/worker-entry.js +446 -0
- package/dist/host/worker-entry.js.map +1 -0
- package/dist/host/worker-protocol.d.ts +31 -0
- package/dist/host/worker-protocol.js +122 -0
- package/dist/host/worker-protocol.js.map +1 -0
- package/dist/host/worker-store.d.ts +80 -0
- package/dist/host/worker-store.js +91 -0
- package/dist/host/worker-store.js.map +1 -0
- package/dist/host/worker-supervisor.d.ts +22 -0
- package/dist/host/worker-supervisor.js +54 -0
- package/dist/host/worker-supervisor.js.map +1 -0
- package/dist/render/canvas.d.ts +2 -1
- package/dist/render/canvas.js +40 -15
- package/dist/render/canvas.js.map +1 -1
- package/dist/render/graph-render.d.ts +7 -3
- package/dist/render/graph-render.js +125 -74
- package/dist/render/graph-render.js.map +1 -1
- package/dist/state/database.js +3 -1
- package/dist/state/database.js.map +1 -1
- package/dist/state/index.d.ts +1 -0
- package/dist/state/index.js +1 -0
- package/dist/state/index.js.map +1 -1
- package/dist/state/schema.d.ts +1 -1
- package/dist/state/schema.js +171 -3
- package/dist/state/schema.js.map +1 -1
- package/dist/state/viewer.d.ts +46 -0
- package/dist/state/viewer.js +249 -0
- package/dist/state/viewer.js.map +1 -0
- package/dist/viewer/cli.d.ts +1 -1
- package/dist/viewer/cli.js +47 -18
- package/dist/viewer/cli.js.map +1 -1
- package/dist/workflows/composition.js +25 -2
- package/dist/workflows/composition.js.map +1 -1
- package/dist/workflows/definition.d.ts +3 -1
- package/dist/workflows/definition.js +25 -0
- package/dist/workflows/definition.js.map +1 -1
- package/dist/workflows/engine.d.ts +1 -0
- package/dist/workflows/engine.js +157 -42
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/errors.d.ts +3 -1
- package/dist/workflows/errors.js +4 -7
- package/dist/workflows/errors.js.map +1 -1
- package/dist/workflows/human-decision.d.ts +3 -0
- package/dist/workflows/human-decision.js +31 -0
- package/dist/workflows/human-decision.js.map +1 -1
- package/dist/workflows/index.d.ts +1 -1
- package/dist/workflows/index.js +1 -1
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/schema.js +17 -6
- package/dist/workflows/schema.js.map +1 -1
- package/dist/{viewer → workflows}/session-reducer.d.ts +3 -1
- package/dist/{viewer → workflows}/session-reducer.js +4 -0
- package/dist/workflows/session-reducer.js.map +1 -0
- package/dist/workflows/store.d.ts +87 -1
- package/dist/workflows/store.js +892 -93
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/tool-input.d.ts +0 -26
- package/dist/workflows/tool-input.js +2 -50
- package/dist/workflows/tool-input.js.map +1 -1
- package/dist/workflows/types.d.ts +36 -4
- package/docs/2026-08-30-out-of-process-workflow-host-plan.md +353 -0
- package/docs/CONTROLLERS.md +11 -11
- package/docs/DEFERRED_TURNS.md +23 -2
- package/docs/SQLITE_STATE.md +41 -31
- package/docs/WORKFLOW_HOST.md +452 -0
- package/docs/development.md +46 -30
- package/docs/live-replay-protocol.md +129 -100
- package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +42 -0
- package/docs/plans/2026-08-28-piw-incremental-viewer-plan.md +293 -0
- package/docs/plans/piw-viewer-experience-implementation-plan.md +11 -1
- package/docs/plans/tui-viewer-implementation-plan.md +5 -0
- package/docs/tui-viewer.md +18 -3
- package/docs/workflows.md +162 -210
- package/examples/workflows/command-batch.workflow.ts +2 -0
- package/examples/workflows/shell.workflow.ts +2 -1
- package/herdr-plugin.toml +1 -1
- package/package.json +1 -1
- package/src/builtins/autoimplement.workflow.ts +3 -0
- package/src/builtins/change-verification.workflow.ts +7 -2
- package/src/builtins/metadata.ts +9 -0
- package/src/builtins/monitor.workflow.ts +4 -0
- package/src/builtins/sanity-check.workflow.ts +4 -0
- package/src/builtins/workspace-preparation.workflow.ts +3 -1
- package/src/controllers/index.ts +0 -5
- package/src/controllers/sqlite.ts +709 -76
- package/src/controllers/store.ts +1 -0
- package/src/extension/controller-command.ts +45 -0
- package/src/extension/index.ts +944 -4163
- package/src/extension/message-card.ts +61 -0
- package/src/extension/step-message.ts +58 -63
- package/src/host/child-worker-supervisor.ts +183 -0
- package/src/host/client.ts +293 -0
- package/src/host/controller-worker-entry.ts +311 -0
- package/src/host/controller-worker-protocol.ts +104 -0
- package/src/host/controller-worker-supervisor.ts +79 -0
- package/src/host/host-entry.ts +23 -0
- package/src/host/processes.ts +171 -54
- package/src/host/protocol.ts +196 -0
- package/src/host/resolver-entry.ts +241 -0
- package/src/host/rpc-executor.ts +76 -34
- package/src/host/runner.ts +2813 -422
- package/src/host/state.ts +1160 -0
- package/src/host/worker-entry.ts +533 -0
- package/src/host/worker-protocol.ts +165 -0
- package/src/host/worker-store.ts +229 -0
- package/src/host/worker-supervisor.ts +74 -0
- package/src/render/canvas.ts +44 -10
- package/src/render/graph-render.ts +145 -90
- package/src/state/database.ts +2 -1
- package/src/state/index.ts +14 -0
- package/src/state/schema.ts +171 -3
- package/src/state/viewer.ts +356 -0
- package/src/viewer/cli.ts +49 -17
- package/src/workflows/composition.ts +36 -2
- package/src/workflows/definition.ts +32 -0
- package/src/workflows/engine.ts +157 -54
- package/src/workflows/errors.ts +11 -2
- package/src/workflows/human-decision.ts +49 -0
- package/src/workflows/index.ts +2 -0
- package/src/workflows/schema.ts +19 -6
- package/src/{viewer → workflows}/session-reducer.ts +11 -1
- package/src/workflows/store.ts +1316 -108
- package/src/workflows/tool-input.ts +3 -69
- package/src/workflows/types.ts +32 -4
- package/dist/controllers/workflow-engine-scheduler.d.ts +0 -29
- package/dist/controllers/workflow-engine-scheduler.js +0 -175
- package/dist/controllers/workflow-engine-scheduler.js.map +0 -1
- package/dist/extension/controller-host.d.ts +0 -48
- package/dist/extension/controller-host.js +0 -110
- package/dist/extension/controller-host.js.map +0 -1
- package/dist/extension/deferred-turn-coordinator.d.ts +0 -32
- package/dist/extension/deferred-turn-coordinator.js +0 -143
- package/dist/extension/deferred-turn-coordinator.js.map +0 -1
- package/dist/extension/deferred-turn.d.ts +0 -44
- package/dist/extension/deferred-turn.js +0 -110
- package/dist/extension/deferred-turn.js.map +0 -1
- package/dist/extension/executor.d.ts +0 -86
- package/dist/extension/executor.js +0 -311
- package/dist/extension/executor.js.map +0 -1
- package/dist/extension/follow-up-coordinator.d.ts +0 -27
- package/dist/extension/follow-up-coordinator.js +0 -131
- package/dist/extension/follow-up-coordinator.js.map +0 -1
- package/dist/extension/recorder.d.ts +0 -84
- package/dist/extension/recorder.js +0 -528
- package/dist/extension/recorder.js.map +0 -1
- package/dist/extension/restart-policy.d.ts +0 -38
- package/dist/extension/restart-policy.js +0 -116
- package/dist/extension/restart-policy.js.map +0 -1
- package/dist/extension/session-events.d.ts +0 -133
- package/dist/extension/session-events.js +0 -61
- package/dist/extension/session-events.js.map +0 -1
- package/dist/extension/terminal-decision.d.ts +0 -51
- package/dist/extension/terminal-decision.js +0 -110
- package/dist/extension/terminal-decision.js.map +0 -1
- package/dist/viewer/session-reducer.js.map +0 -1
- package/src/controllers/workflow-engine-scheduler.ts +0 -246
- package/src/extension/controller-host.ts +0 -167
- package/src/extension/deferred-turn-coordinator.ts +0 -171
- package/src/extension/deferred-turn.ts +0 -166
- package/src/extension/executor.ts +0 -411
- package/src/extension/follow-up-coordinator.ts +0 -151
- package/src/extension/recorder.ts +0 -655
- package/src/extension/restart-policy.ts +0 -163
- package/src/extension/session-events.ts +0 -121
- package/src/extension/terminal-decision.ts +0 -172
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# PIW viewer experience implementation plan
|
|
2
2
|
|
|
3
|
+
This plan records the earlier viewer experience work. Its graph-wide card sizing and
|
|
4
|
+
complete-run loading decisions are superseded by the
|
|
5
|
+
[incremental and virtualized viewer plan](2026-08-28-piw-incremental-viewer-plan.md).
|
|
6
|
+
The remaining sections are kept as implementation history and must not be used as the
|
|
7
|
+
target architecture for new viewer work.
|
|
8
|
+
|
|
3
9
|
## Objective
|
|
4
10
|
|
|
5
11
|
Make `piw` a state-rich, terminal-native workflow viewer with Catppuccin as
|
|
@@ -46,7 +52,11 @@ Adopt ACPX ideas that improve inspection and state comprehension:
|
|
|
46
52
|
Do not adopt ELK, React Flow, browser card decoration, smooth zoom, animated
|
|
47
53
|
edges, multiple ACP-session concepts, or character-by-character replay.
|
|
48
54
|
|
|
49
|
-
### Stable full node cards
|
|
55
|
+
### Stable full node cards (superseded)
|
|
56
|
+
|
|
57
|
+
The graph-wide sizing rule in this section is historical. The selected replacement gives
|
|
58
|
+
each node bounded adaptive dimensions and keeps the complete logical graph outside the
|
|
59
|
+
materialized viewport.
|
|
50
60
|
|
|
51
61
|
Box mode is the canonical graph presentation. Each node is a full card with a
|
|
52
62
|
border, an interior surface, and enough padding to keep its outer dimensions
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Rust TUI viewer — implementation plan
|
|
2
2
|
|
|
3
|
+
This is the historical plan for the first Rust viewer and its former run-bundle reader.
|
|
4
|
+
The selected replacement is the
|
|
5
|
+
[incremental and virtualized viewer plan](2026-08-28-piw-incremental-viewer-plan.md).
|
|
6
|
+
Use that plan for state revisions, paging, multiple clients, and graph sizing.
|
|
7
|
+
|
|
3
8
|
Goal: `piw`, a Rust TUI in `tui/` for live viewing and replaying workflow
|
|
4
9
|
runs, per [tui-viewer.md](../tui-viewer.md) and
|
|
5
10
|
[live-replay-protocol.md](../live-replay-protocol.md).
|
package/docs/tui-viewer.md
CHANGED
|
@@ -5,6 +5,16 @@ the same graph as the bundled TypeScript viewer, pinned by the golden fixtures
|
|
|
5
5
|
under `fixtures/layout/`, and adds live following, replay, detailed inspection,
|
|
6
6
|
a recorded Pi conversation, themes, and remote viewing.
|
|
7
7
|
|
|
8
|
+
## Incremental projection
|
|
9
|
+
|
|
10
|
+
The viewer uses the [incremental and virtualized viewer design](plans/2026-08-28-piw-incremental-viewer-plan.md).
|
|
11
|
+
|
|
12
|
+
The run browser reads small metadata rows. It does not load trace, step, session, settings, or follow-up payloads. The local viewer checks SQLite `data_version` on its timer. When the value is unchanged, the timer does not scan runs or read payloads.
|
|
13
|
+
|
|
14
|
+
The selected run contains bounded pages. Step, trace, session-entry, and session-event pages contain at most 256 rows. Replay can jump to any position. The viewer loads the page that contains that position and keeps only the current windows. A compact graph projection keeps the latest attempt for each node and the taken transitions up to the replay point.
|
|
15
|
+
|
|
16
|
+
Local page reads run outside input and drawing through one overwrite-only request slot. A newer selection replaces pending work. A failed first read leaves the run browser usable. A failed refresh keeps the last good view and marks it stale.
|
|
17
|
+
|
|
8
18
|
## Install
|
|
9
19
|
|
|
10
20
|
The crates.io package uses the project name and installs the shorter `piw`
|
|
@@ -44,6 +54,10 @@ show the shortcut.
|
|
|
44
54
|
|
|
45
55
|
## Layout
|
|
46
56
|
|
|
57
|
+
Boxed graph cards use only their own content. Their outer width is 24 through 32 cells. Their height is 7 through 10 rows. A switch with more than three branches shows its first two branch names and a `+N branches` row. Edge labels and the inspector keep the complete branch information.
|
|
58
|
+
|
|
59
|
+
Ranks use the tallest card in that rank. Cards are top-aligned. Edge ports, clipping, centering, keyboard movement, and mouse hits use each card's exact bounds. The server sends one retained language-neutral graph scene per watched run. The TUI reuses that scene across status changes and turns only viewport rows and columns into Ratatui spans.
|
|
60
|
+
|
|
47
61
|
The normal layout contains a run browser, graph, inspector, and two-line replay
|
|
48
62
|
timeline. Short terminals use a compact one-line transport. Terminals below 100
|
|
49
63
|
columns start with the run browser collapsed to a status rail; `b` toggles it.
|
|
@@ -195,7 +209,9 @@ at the bottom until the user moves to an older message and returns with End.
|
|
|
195
209
|
The client reconnects automatically with bounded backoff. It restores the run
|
|
196
210
|
listing and selected-run subscription after the server returns. Cached content
|
|
197
211
|
stays visible but is labeled reconnecting or disconnected, never current.
|
|
198
|
-
Revision gaps
|
|
212
|
+
Revision gaps force a bounded snapshot.
|
|
213
|
+
|
|
214
|
+
`piw serve` keeps one loaded projection and one graph scene for each watched run. The first watcher loads it. Later watchers reuse it. The last unwatch or disconnect releases it. Clients keep separate revision and page cursors, so one client's replay jump does not move another client. A lagged client gets a bounded snapshot instead of an unbounded patch backlog.
|
|
199
215
|
|
|
200
216
|
Expanded prompt and output fields come from content-addressed SQLite blobs.
|
|
201
217
|
The local reader uses query-only mode. Remote snapshots carry the same bounded
|
|
@@ -214,8 +230,7 @@ semantic view and do not expose a filesystem path.
|
|
|
214
230
|
- Browser: `b` expands or collapses it. Up/Down or `j`/`k` selects a run. Drag
|
|
215
231
|
its right border to resize it.
|
|
216
232
|
- Inspector: Enter expands the selected step, trace payload, or conversation
|
|
217
|
-
entry. In Trace, `v` changes scope. Page Up/Down scrolls long content. Drag
|
|
218
|
-
its top border to resize the bottom panel.
|
|
233
|
+
entry. In Trace, `v` changes scope. In Info, `<` and `>` load the previous or next settings, follow-up, and current-update pages. Page Up/Down scrolls long content. Drag its top border to resize the bottom panel.
|
|
219
234
|
- Theme: `,` opens the picker; arrows or `j`/`k` preview. Click `[✓ Apply]` or
|
|
220
235
|
`[× Cancel]`; Enter and Escape remain the keyboard equivalents.
|
|
221
236
|
- `q` or Ctrl-C quits.
|
package/docs/workflows.md
CHANGED
|
@@ -19,14 +19,12 @@ Pi Workflows includes built-in `plain-summary`, `autoplan`, `autodoc`,
|
|
|
19
19
|
`autoimplement`, `plan-approval`, `sanity-check`, and `monitor` workflows. `autoplan` is the current name for the
|
|
20
20
|
planning workflow that was first released as `autodevise`; the old command and
|
|
21
21
|
export are not retained. A project or global file named `monitor.workflow.ts`
|
|
22
|
-
replaces the built-in monitor.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
mismatch refuses resume. Project and global workflow files still reload on
|
|
29
|
-
each run and use their path and SHA-256 hash as their source identity.
|
|
22
|
+
replaces the built-in monitor. Each built-in has a stable reference such as
|
|
23
|
+
`builtin:monitor` and an explicit revision. A resolver child snapshots the
|
|
24
|
+
selected built-in before start, and each run worker verifies that identity
|
|
25
|
+
before execution. A revision mismatch refuses resume. Project and global
|
|
26
|
+
workflow files also use their absolute path and SHA-256 hash as source
|
|
27
|
+
identity.
|
|
30
28
|
|
|
31
29
|
The workflow's command name is the file stem, so `.pi/workflows/triage.workflow.ts`
|
|
32
30
|
runs as `/workflow triage`. A direct path also works: `/workflow ./somewhere/x.workflow.ts`.
|
|
@@ -40,7 +38,6 @@ import { agent, compute, defineWorkflow } from "@osolmaz/pi-workflows";
|
|
|
40
38
|
export default defineWorkflow({
|
|
41
39
|
name: "example",
|
|
42
40
|
title: ({ input }) => `example: ${(input as { task?: string }).task}`,
|
|
43
|
-
presentationPrompt: "Present the final answer clearly and concisely.",
|
|
44
41
|
startAt: "ask",
|
|
45
42
|
maxSteps: 50,
|
|
46
43
|
nodes: {
|
|
@@ -56,20 +53,19 @@ export default defineWorkflow({
|
|
|
56
53
|
|
|
57
54
|
Top-level fields:
|
|
58
55
|
|
|
59
|
-
| Field
|
|
60
|
-
|
|
|
61
|
-
| `name`
|
|
62
|
-
| `source`
|
|
63
|
-
| `contractId`
|
|
64
|
-
| `input`
|
|
65
|
-
| `title`
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `maxSteps` | `number` | Optional loop bound, default 100. The run fails when exceeded. |
|
|
56
|
+
| Field | Type | Notes |
|
|
57
|
+
| ------------ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
58
|
+
| `name` | `string` | Required. Used in run ids and the step contract. `answer`, `cancel`, `list`, `pause`, `resume`, and `status` are reserved for `/workflow` subcommands. |
|
|
59
|
+
| `source` | `string` | Optional `import.meta.url` for exact provenance when another TypeScript workflow imports this definition directly. |
|
|
60
|
+
| `contractId` | `string` | Optional stable input-and-exit contract identity. Dynamic overrides must match it. |
|
|
61
|
+
| `input` | `function` | Optional runtime input normalizer and validator. Its return type is the workflow input type. |
|
|
62
|
+
| `title` | `string` or function | Optional run title, resolved once at start from `{ input, workflowName }`. Async resolution is bounded (30s) and cancellable. |
|
|
63
|
+
| `startAt` | `string` | Required. Id of the first node. |
|
|
64
|
+
| `nodes` | `Record<string, node>` | Required, non-empty. Node ids must match `[A-Za-z_][A-Za-z0-9_-]*`. |
|
|
65
|
+
| `includes` | `Record<string, include>` | Optional imported or dynamically resolved child workflows. |
|
|
66
|
+
| `exits` | `Record<string, exit>` | Optional named successful terminal nodes used when another workflow includes this workflow. |
|
|
67
|
+
| `edges` | `WorkflowEdge[]` | Required. See routing below. |
|
|
68
|
+
| `maxSteps` | `number` | Optional loop bound, default 100. The run fails when exceeded. |
|
|
73
69
|
|
|
74
70
|
`defineWorkflow` validates the shape eagerly (node ids, edge shapes, function
|
|
75
71
|
fields) and validates the graph (unknown targets, duplicate outgoing edges,
|
|
@@ -112,36 +108,51 @@ Function actions receive `WorkflowActionContext`, which adds
|
|
|
112
108
|
|
|
113
109
|
## Durable runs, parking, and resume
|
|
114
110
|
|
|
115
|
-
Every
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
111
|
+
Every run enters one global SQLite queue. One package-owned host claims runs,
|
|
112
|
+
renews live claims, commits state, and supervises one child process for each
|
|
113
|
+
active run generation. The extension is a local host client. It does not run
|
|
114
|
+
the workflow engine or workflow definitions.
|
|
119
115
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
resumable. When a runner is available again (a reopened Pi session or the
|
|
123
|
-
standalone host), the run **resumes** at the node it stopped on. Completed
|
|
124
|
-
nodes replay from the recorded state; only the interrupted node and everything
|
|
125
|
-
downstream rerun. Resume repairs a torn trace tail, drops trace events the
|
|
126
|
-
state projection never recorded, and refuses to continue if the workflow
|
|
127
|
-
source changed since the run started (a forced resume records the mismatch).
|
|
128
|
-
|
|
129
|
-
The standalone host runs without any Pi session:
|
|
116
|
+
The host starts on demand when a Pi or CLI client needs it. These commands
|
|
117
|
+
control the same user-level host for all projects:
|
|
130
118
|
|
|
131
119
|
```bash
|
|
132
|
-
pi-workflows host
|
|
120
|
+
pi-workflows host start
|
|
121
|
+
pi-workflows host status
|
|
122
|
+
pi-workflows host stop
|
|
123
|
+
pi-workflows host run
|
|
133
124
|
```
|
|
134
125
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
and
|
|
126
|
+
`host run` stays attached. The other commands start, inspect, or stop the
|
|
127
|
+
on-demand process. No command installs an operating-system service.
|
|
128
|
+
|
|
129
|
+
A worker verifies the root and all mounted source identities before it loads
|
|
130
|
+
workflow modules. It then checks the resolved mounted-source map and executes
|
|
131
|
+
from committed state through a host-backed store. A source mismatch parks the
|
|
132
|
+
run with `workflowSourceChanged`; normal scheduling does not retry it. Restore
|
|
133
|
+
the recorded source and explicitly resume, or cancel the run. A headless Pi
|
|
134
|
+
child uses its own registered process group. The worker stops that group on
|
|
135
|
+
normal completion, and the host reaps it if the worker exits first. If the
|
|
136
|
+
worker stops for another recoverable reason, pure work can run again. A
|
|
137
|
+
protected write checks and renews the exact live token and generation in one
|
|
138
|
+
transaction. An expired or replaced owner cannot revive itself.
|
|
139
|
+
|
|
140
|
+
Interactive agent and assistant-message steps do not run headlessly for a Pi
|
|
141
|
+
session. The worker commits a durable interaction request and parks. The origin
|
|
142
|
+
session presents the request through documented Pi APIs and submits the exact
|
|
143
|
+
request, node, attempt, and revision. The host records submitted output as
|
|
144
|
+
provisional. A new supervised worker loads the workflow and runs its `validate`
|
|
145
|
+
function before the host accepts the submission. A validation error leaves the
|
|
146
|
+
same request pending and returns the error to the model. Closing Pi leaves that
|
|
147
|
+
request pending; reopening the same session adopts the existing session entry
|
|
148
|
+
or presents it once. Notifications use the durable session outbox. A root
|
|
149
|
+
`presentationPrompt` creates a durable terminal turn only after completion is
|
|
150
|
+
committed. A controller child without an origin session can use a supervised
|
|
151
|
+
headless `pi --mode rpc` child for structured agent steps.
|
|
152
|
+
|
|
153
|
+
Pause stops the worker and parks at the last durable boundary. Resume takes a
|
|
154
|
+
new generation. Cancellation can stop a live worker or atomically claim and
|
|
155
|
+
cancel an expired running row. Resume refuses changed workflow source.
|
|
145
156
|
|
|
146
157
|
## Node types
|
|
147
158
|
|
|
@@ -179,10 +190,13 @@ agent({
|
|
|
179
190
|
because an invalid response is already visible and must not be retried.
|
|
180
191
|
|
|
181
192
|
For submitted output, the engine appends the existing workflow-tool contract.
|
|
182
|
-
The
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
193
|
+
The host checks the durable transport identifiers, stores a `validating`
|
|
194
|
+
submission, and starts a supervised worker. In that worker, the output passes
|
|
195
|
+
through tolerant JSON normalization and then `validate`. The tool reports
|
|
196
|
+
success only after this check accepts the output. Rejected submissions return
|
|
197
|
+
the validation error and can retry in the same step. If the model settles
|
|
198
|
+
without submitting, the durable request stays pending until it receives valid
|
|
199
|
+
output, times out, or is cancelled. For assistant-message output, the engine appends a normal-response contract,
|
|
186
200
|
waits for `agent_settled`, rejects empty, failed, aborted, or tool-only results,
|
|
187
201
|
and never suppresses the visible text. Timeout and cancellation abort either
|
|
188
202
|
form's active Pi turn.
|
|
@@ -225,20 +239,34 @@ controller resource instead.
|
|
|
225
239
|
|
|
226
240
|
### action
|
|
227
241
|
|
|
228
|
-
Performs
|
|
229
|
-
|
|
242
|
+
Performs managed work. Every function action and shell action must declare how
|
|
243
|
+
the host recovers if the worker exits after the external operation but before
|
|
244
|
+
it saves a receipt.
|
|
245
|
+
|
|
246
|
+
Use `idempotentEffect(type)` only when the operation has a stable external
|
|
247
|
+
idempotency key or a read-back check that makes another attempt safe:
|
|
230
248
|
|
|
231
249
|
```typescript
|
|
232
|
-
|
|
250
|
+
import { action, idempotentEffect } from "@osolmaz/pi-workflows";
|
|
251
|
+
|
|
252
|
+
action({
|
|
253
|
+
effect: idempotentEffect("preview.deploy"),
|
|
254
|
+
run: async ({ input }) => await deployPreview(input),
|
|
255
|
+
});
|
|
233
256
|
```
|
|
234
257
|
|
|
258
|
+
Use `manualEffect(type)` when the external system cannot prove whether an
|
|
259
|
+
uncertain request applied. An uncertain worker exit marks that effect
|
|
260
|
+
`ambiguous`, parks the run, and requires explicit operator recovery. The host
|
|
261
|
+
does not retry it automatically.
|
|
262
|
+
|
|
235
263
|
The shell form (`shell` is a synonym that requires `exec`) runs a command owned
|
|
236
|
-
by the
|
|
237
|
-
timeout and captured output:
|
|
264
|
+
by the workflow definition, with a timeout and captured output:
|
|
238
265
|
|
|
239
266
|
```typescript
|
|
240
267
|
shell({
|
|
241
|
-
|
|
268
|
+
effect: idempotentEffect("repository.status"),
|
|
269
|
+
exec: () => ({
|
|
242
270
|
command: "git",
|
|
243
271
|
args: ["status", "--porcelain"],
|
|
244
272
|
cwd: "/path/to/repo",
|
|
@@ -261,6 +289,7 @@ A function action can publish a durable update without completing the node:
|
|
|
261
289
|
|
|
262
290
|
```typescript
|
|
263
291
|
action({
|
|
292
|
+
effect: idempotentEffect("dataset.process"),
|
|
264
293
|
run: async ({ publishUpdate }) => {
|
|
265
294
|
await publishUpdate({
|
|
266
295
|
type: "progress",
|
|
@@ -283,6 +312,12 @@ keeps normal output capture. Lines and update data are each limited to 64 KiB.
|
|
|
283
312
|
See [WORKFLOW_UPDATES.md](WORKFLOW_UPDATES.md) for the envelope, progress
|
|
284
313
|
schema, limits, estimation, and error rules.
|
|
285
314
|
|
|
315
|
+
The host reserves the effect before it lets the action run. A repeated key with
|
|
316
|
+
the same request adopts the durable record; the same key with another request
|
|
317
|
+
is a conflict. A normal caught error settles the attempt as rejected. After an
|
|
318
|
+
uncertain process exit, an idempotent effect returns to pending for retry, while
|
|
319
|
+
a manual effect becomes ambiguous. This is not an exactly-once claim.
|
|
320
|
+
|
|
286
321
|
### checkpoint
|
|
287
322
|
|
|
288
323
|
Ends the run in a `waiting` state for human review. The checkpoint run is
|
|
@@ -333,9 +368,9 @@ humanDecision({
|
|
|
333
368
|
});
|
|
334
369
|
```
|
|
335
370
|
|
|
336
|
-
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,
|
|
371
|
+
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 host 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. Existing `body` requests remain a legacy compatibility form and use deterministic readable formatting.
|
|
337
372
|
|
|
338
|
-
The model-facing workflow tool cannot answer a protected human decision. Pi
|
|
373
|
+
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 host-owned path. Ordinary checkpoints can also use the model-facing `answer` action.
|
|
339
374
|
|
|
340
375
|
See [Human decisions](HUMAN_DECISIONS.md) for channels, recovery, persistence, and plan approval.
|
|
341
376
|
|
|
@@ -428,72 +463,31 @@ The run records every mounted source and a digest of the resolved graph. Resume
|
|
|
428
463
|
|
|
429
464
|
The model sees one `workflow` tool. Its `action` field supports:
|
|
430
465
|
|
|
431
|
-
- `list` for discovered workflow names and sources
|
|
432
|
-
- `start` with a workflow name or path and structured input
|
|
433
|
-
- `
|
|
434
|
-
- `
|
|
435
|
-
- `
|
|
436
|
-
- `answer` with
|
|
437
|
-
- `
|
|
438
|
-
- `queue-follow-up` to save one ordered normal user prompt for after successful completion.
|
|
439
|
-
- `remove-follow-up` to remove an unsent prompt created by the same model source.
|
|
440
|
-
- `update` for a non-completing update from the current agent attempt.
|
|
466
|
+
- `list` for discovered workflow names and sources;
|
|
467
|
+
- `start` with a workflow name or path and structured input;
|
|
468
|
+
- `status` for the active run or a supplied run ID;
|
|
469
|
+
- `pause` and `resume` for the active session run;
|
|
470
|
+
- `cancel` for the active run or a supplied run ID;
|
|
471
|
+
- `answer` with checkpoint input and an optional run ID;
|
|
472
|
+
- `update` for a non-completing update from the current agent attempt;
|
|
441
473
|
- `submit` for the current workflow step contract.
|
|
442
474
|
|
|
443
|
-
A direct user request to continue or resume the active workflow maps to
|
|
444
|
-
`resume`
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
released and reports `resumed: true`. An active run that is already executing
|
|
449
|
-
returns normal success with `resumed: false` and `alreadyRunning: true`. It does
|
|
450
|
-
not change the run state. With no active run, `resume` still returns a warning.
|
|
451
|
-
|
|
452
|
-
Model-facing `status` keeps `status` as the durable workflow lifecycle state.
|
|
453
|
-
It also reports the host action fields `paused`, `workState`, and `resumable`.
|
|
454
|
-
For the current active run, `paused` is true when the host has requested or
|
|
455
|
-
applied a hold, or when the durable run state has `paused: true`. `workState`
|
|
456
|
-
is `running`, `pausing`, or `paused` for that active host run and `inactive`
|
|
457
|
-
when no current host run can act on the durable state. `resumable` is true only
|
|
458
|
-
when `resume` can release the current active run. Queue-only status uses its
|
|
459
|
-
launch state, such as `queued` or `starting`, as `workState`; queue-only and
|
|
460
|
-
no-run results report `paused: false` and `resumable: false`. Thus, a durable
|
|
461
|
-
`status: "running"` can correctly appear with `workState: "pausing"` or
|
|
462
|
-
`workState: "paused"`, and the status message names that actionable state
|
|
463
|
-
instead of saying only that the workflow is running.
|
|
464
|
-
|
|
465
|
-
Restart uses this contract:
|
|
475
|
+
A direct user request to continue or resume the active workflow maps to
|
|
476
|
+
`resume` immediately. The model does not call `status` instead of `resume` or
|
|
477
|
+
use it as a prerequisite. An already active run adopts the resume request. A
|
|
478
|
+
paused or parked run gets a new claim generation and worker. With no resumable
|
|
479
|
+
run, the host rejects the request.
|
|
466
480
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
472
|
-
```
|
|
481
|
+
`status` reports the durable queue projection. A host command succeeds only
|
|
482
|
+
after its transaction commits. The protocol stores request fingerprints and
|
|
483
|
+
receipts, so an exact duplicate adopts the committed result and conflicting
|
|
484
|
+
reuse is rejected.
|
|
473
485
|
|
|
474
|
-
The
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
A model-started run is queued until the model's current turn settles. A terminal
|
|
481
|
-
decision turn can reserve one restart, Monitor run, or other workflow start.
|
|
482
|
-
A second workflow launch from that turn fails. The first workflow prompt then
|
|
483
|
-
starts a new turn. This keeps the requesting turn outside
|
|
484
|
-
the workflow's first attempt and prevents an early missing-submission reminder.
|
|
485
|
-
The normal extension offers all actions. The headless RPC bridge offers only
|
|
486
|
-
`update` and `submit`, so a workflow child cannot recursively control other
|
|
487
|
-
runs. Direct `/workflow change-settings`, `queue-follow-up`, and
|
|
488
|
-
`remove-follow-up` commands use verified interactive provenance. Controller
|
|
489
|
-
code can use the matching `ctx.workflows` methods. All surfaces call the same
|
|
490
|
-
SQLite operations.
|
|
491
|
-
|
|
492
|
-
Follow-up prompts stay separate from workflow settings. Successful terminal
|
|
493
|
-
state is saved before delivery. A final presentation settles first. The Pi
|
|
494
|
-
extension then sends prompts in order as normal user messages without
|
|
495
|
-
reactivating the completed run. See [Continue normal work after a workflow
|
|
496
|
-
finishes](2026-08-25-workflow-follow-ups.md).
|
|
486
|
+
The normal extension offers these actions through the origin Pi session. The
|
|
487
|
+
headless RPC bridge offers only `update` and `submit`, so a controller child
|
|
488
|
+
cannot recursively control unrelated runs. Controller code can use its narrow
|
|
489
|
+
`ctx.workflows` methods for child runs, settings, and follow-up records. Those
|
|
490
|
+
methods also commit through the global host.
|
|
497
491
|
|
|
498
492
|
### Built-in plain summary
|
|
499
493
|
|
|
@@ -597,7 +591,7 @@ Only bounded final assistant text and safe operational facts leave a live child
|
|
|
597
591
|
|
|
598
592
|
The workflow publishes aggregate and per-agent `pi-workflows.progress.v1` tracks under `agents/review/*` and `agents/verification/*`. Progress contains role, the verified actual model when known, state, elapsed facts, and safe phases such as `thinking` or `tool: read`. The Pi widget shows the aggregate plus failed and active children within its ten-line limit. `piw` shows every durable child track and its samples. Both views use existing progress records, so no child workflow run or new persisted schema is needed.
|
|
599
593
|
|
|
600
|
-
Serial mode still uses two child sessions, and parallel mode still uses five. Review prompts, review areas, strict result validation, verdicts, and progress stay unchanged. After verification, an assistant-message agent shows the full bounded report verbatim. A mismatch stops before summary generation. The graph then includes `plain-summary`, which shows a short plain-language explanation with the verdict and the most important next action. The detailed response always settles before the summary starts. A final compute node returns the original strict result, so presentation cannot change the verdict. Sanity Check
|
|
594
|
+
Serial mode still uses two child sessions, and parallel mode still uses five. Review prompts, review areas, strict result validation, verdicts, and progress stay unchanged. After verification, an assistant-message agent shows the full bounded report verbatim. A mismatch stops before summary generation. The graph then includes `plain-summary`, which shows a short plain-language explanation with the verdict and the most important next action. The detailed response always settles before the summary starts. A final compute node returns the original strict result, so presentation cannot change the verdict. Sanity Check creates no extra terminal model turn.
|
|
601
595
|
|
|
602
596
|
The CLI, JSON or RPC stream, temporary prompt file, standard-output cap, subprocess fallback, shared child runtime, and blanket child-extension ban are not retained. See [the Sanity Check plan](plans/2026-08-21-sanity-check-plan.md) for the selected implementation and test boundaries.
|
|
603
597
|
|
|
@@ -688,59 +682,22 @@ use an internal turn-intent contract instead of the notification outbox. See
|
|
|
688
682
|
[WORKFLOW_STEP_MESSAGES.md](WORKFLOW_STEP_MESSAGES.md) for the step-message contract
|
|
689
683
|
and [Deferred workflow turns](DEFERRED_TURNS.md) for the successor-turn contract.
|
|
690
684
|
|
|
691
|
-
##
|
|
685
|
+
## Visible responses
|
|
692
686
|
|
|
693
687
|
Workflow nodes normally produce structured values for routing and persistence.
|
|
694
|
-
When a person
|
|
695
|
-
`
|
|
688
|
+
When a person must receive normal prose, use an agent node with
|
|
689
|
+
`expectedOutput: assistantMessage()`. The worker parks and records the exact
|
|
690
|
+
step request. The origin Pi session starts the model turn, and the visible
|
|
691
|
+
assistant text becomes the node output after the turn settles.
|
|
696
692
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
state.status === "waiting"
|
|
702
|
-
? `Explain this recommendation and ask the user to decide: ${JSON.stringify(finalOutput)}`
|
|
703
|
-
: "Summarize the completed result and any remaining limitations.",
|
|
704
|
-
// ...startAt, nodes, and edges
|
|
705
|
-
});
|
|
706
|
-
```
|
|
693
|
+
The request keeps its node and attempt ID across Pi reload. The extension first
|
|
694
|
+
looks for an existing session entry with the durable request ID. It inserts a
|
|
695
|
+
new visible message only when no adopted entry exists. A repeated submission
|
|
696
|
+
returns its stored receipt.
|
|
707
697
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
state and reason, restart count, and earlier terminal outcomes in the restart
|
|
712
|
-
chain. A completed state does not prove that the user's larger task is complete.
|
|
713
|
-
The model uses the current Pi conversation to stop, restart safely, start
|
|
714
|
-
Monitor for an authorized external wait, ask for a decision or authority, or
|
|
715
|
-
take another safe authorized action.
|
|
716
|
-
|
|
717
|
-
`presentationPrompt` adds workflow-specific presentation instructions to this
|
|
718
|
-
shared terminal decision message for completed runs. Returning `undefined`,
|
|
719
|
-
returning an empty string, omitting `presentationPrompt`, or ending in failure,
|
|
720
|
-
timeout, or cancellation uses the factual terminal fallback instead. Normal
|
|
721
|
-
presentation and fallback claim the same terminal turn intent, so races,
|
|
722
|
-
reload, crash recovery, and compaction cannot create a second decision turn.
|
|
723
|
-
Async prompt builders have 30 seconds to finish and receive an `AbortSignal`
|
|
724
|
-
that fires on timeout, session shutdown, or when a new workflow or normal user
|
|
725
|
-
turn starts; stale presentations are discarded.
|
|
726
|
-
|
|
727
|
-
Waiting checkpoints are not terminal and do not create a terminal decision
|
|
728
|
-
turn. Controller child runs and internally owned runs report to their owner and
|
|
729
|
-
do not create competing turns. Explicit cancellation produces terminal facts,
|
|
730
|
-
but its decision instruction defaults to stopping and the `restart` shortcut
|
|
731
|
-
rejects it.
|
|
732
|
-
|
|
733
|
-
An agent with `expectedOutput: assistantMessage()` is different. Its visible
|
|
734
|
-
assistant response is the node output, can appear before later nodes, and also
|
|
735
|
-
works inside an included workflow. A root `presentationPrompt` would add a
|
|
736
|
-
second response, so workflows that end with assistant-message output normally
|
|
737
|
-
omit it.
|
|
738
|
-
|
|
739
|
-
Presentation and terminal decisions are outside the workflow graph: they cannot
|
|
740
|
-
route to another node, change the terminal run, or alter its result. A selected
|
|
741
|
-
restart always creates a new run. Workflow definitions need no opt-in,
|
|
742
|
-
continuation node, or restart prompt. Pi owns conversation history. Pi Workflows
|
|
743
|
-
does not identify, hash, copy, or store an original user message.
|
|
698
|
+
A headless controller child cannot produce a visible assistant message without
|
|
699
|
+
an approved origin-session binding. Use structured agent output for detached
|
|
700
|
+
work. Terminal run state does not create an extra model turn.
|
|
744
701
|
|
|
745
702
|
## Runtime behavior
|
|
746
703
|
|
|
@@ -751,41 +708,31 @@ possible. Defaults worth knowing:
|
|
|
751
708
|
- Node timeout is 15 minutes unless the node sets `timeoutMs` to a positive
|
|
752
709
|
number or context callback. A timed-out node has outcome `timed_out` and can
|
|
753
710
|
be routed with `$result.outcome`. A timed-out agent node also aborts its Pi
|
|
754
|
-
turn, and late output for that attempt is rejected.
|
|
711
|
+
turn, and late output for that attempt is rejected. Interactive runs save the
|
|
712
|
+
resolved wall-clock deadline before they park. The host enforces that deadline
|
|
713
|
+
while Pi is closed and after host restart.
|
|
755
714
|
- `maxSteps` (workflow-level, default 100) bounds loops built from cycles in
|
|
756
715
|
the graph.
|
|
757
|
-
- `/workflow pause`
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
`
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
settle one terminal turn intent. The resulting decision defaults to stopping, and `restart`
|
|
778
|
-
rejects the cancelled run. When no run is live but the widget still shows a parked or finished
|
|
779
|
-
run, the command clears the widget.
|
|
780
|
-
- A restart chain allows at most three restart actions after the original run. The terminal
|
|
781
|
-
fingerprint excludes timestamps and run IDs. If the same workflow revision, exact input, state,
|
|
782
|
-
result or error, and reason occur again in that chain, another restart fails immediately. A
|
|
783
|
-
changed outcome can remain restartable until the chain limit. Starting Monitor does not consume
|
|
784
|
-
a restart.
|
|
785
|
-
- One workflow runs per session at a time.
|
|
786
|
-
- After the workflow tool accepts an agent-step submission, any assistant text that follows remains visible. The next workflow message continues the graph. A deferred intent makes a workflow prompt, presentation, and factual fallback compete to provide one successor turn, so an abort cannot produce two continuation turns.
|
|
787
|
-
- Agent nudges: if the model ends its turn without submitting the pending
|
|
788
|
-
step, it gets a reminder, twice by default, then the step fails.
|
|
716
|
+
- `/workflow pause` atomically parks the run with `paused: true`, stores the
|
|
717
|
+
receipt, and fences the worker before process-group shutdown. `/workflow
|
|
718
|
+
resume` takes a new generation and reruns only work after the last durable
|
|
719
|
+
boundary.
|
|
720
|
+
- Resuming an active run adopts the existing work. Duplicate start, control,
|
|
721
|
+
update, and submission messages return their stored receipts.
|
|
722
|
+
- A start is committed as `queued` with its final run ID before the command
|
|
723
|
+
reports success. Cancellation can use that run ID before its scheduled worker
|
|
724
|
+
starts. Active cancellation commits its terminal state and command receipt
|
|
725
|
+
together before worker shutdown. It cancels effects that have not started and
|
|
726
|
+
marks applying effects ambiguous for explicit recovery. `workflow status` and
|
|
727
|
+
`workflow cancel` can use the run ID immediately.
|
|
728
|
+
- One interactive workflow request is presented per Pi session. Other requests
|
|
729
|
+
remain durable and ordered.
|
|
730
|
+
- Each protected write renews only its exact live token and generation in the
|
|
731
|
+
same transaction. Claim loss does not write a failed run event.
|
|
732
|
+
- An uncommitted pure or idempotent node can run again after a worker crash. An
|
|
733
|
+
uncertain manual effect parks as ambiguous and never retries automatically.
|
|
734
|
+
- Host status reports safe counts and timestamps. It does not report session
|
|
735
|
+
IDs, project paths, prompts, payloads, tokens, process IDs, or credentials.
|
|
789
736
|
|
|
790
737
|
## Workflows started by controllers
|
|
791
738
|
|
|
@@ -810,14 +757,16 @@ if (run.state !== "succeeded") {
|
|
|
810
757
|
}
|
|
811
758
|
```
|
|
812
759
|
|
|
813
|
-
Child workflow completion queues the parent resource again.
|
|
760
|
+
Child workflow completion queues the parent resource again. The global host runs the child through the same queue and supervised worker model as any other run. A host or worker crash resumes the existing durable run when its committed effect state makes that safe. Consequential external mutations belong in the workflow or controller effect API; an uncertain result stops for explicit recovery.
|
|
814
761
|
|
|
815
762
|
See [CONTROLLERS.md](CONTROLLERS.md) for controller definitions and the full recovery contract.
|
|
816
763
|
|
|
817
|
-
## Using the engine outside
|
|
764
|
+
## Using the engine outside Pi
|
|
818
765
|
|
|
819
|
-
The engine
|
|
820
|
-
tests
|
|
766
|
+
The engine remains Pi-agnostic. `WorkflowEngine` takes any `AgentStepExecutor`,
|
|
767
|
+
so tests and custom library integrations can script agent steps. The package's
|
|
768
|
+
production extension does not use this as a selectable embedded runtime; it
|
|
769
|
+
always sends runs to the global host.
|
|
821
770
|
|
|
822
771
|
```typescript
|
|
823
772
|
import { WorkflowEngine, type AgentStepExecutor } from "@osolmaz/pi-workflows";
|
|
@@ -830,6 +779,9 @@ const executor: AgentStepExecutor = {
|
|
|
830
779
|
},
|
|
831
780
|
};
|
|
832
781
|
|
|
833
|
-
const engine = new WorkflowEngine({
|
|
782
|
+
const engine = new WorkflowEngine({
|
|
783
|
+
executor,
|
|
784
|
+
databasePath: "/tmp/workflow-state.sqlite",
|
|
785
|
+
});
|
|
834
786
|
const { state } = await engine.run(workflow, { task: "..." });
|
|
835
787
|
```
|