@osolmaz/pi-workflows 0.13.4 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -160
- 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 +1 -2
- package/dist/controllers/index.js +0 -1
- package/dist/controllers/index.js.map +1 -1
- package/dist/controllers/sqlite.d.ts +85 -31
- package/dist/controllers/sqlite.js +541 -126
- 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 -2928
- 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/prune.js +36 -10
- package/dist/state/prune.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 -22
- package/dist/workflows/tool-input.js +1 -44
- package/dist/workflows/tool-input.js.map +1 -1
- package/dist/workflows/types.d.ts +36 -4
- package/docs/2026-08-25-workflow-follow-ups.md +8 -6
- 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 +61 -27
- package/docs/HUMAN_DECISIONS.md +12 -4
- package/docs/SQLITE_STATE.md +42 -8
- 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-19-human-decision-gates-plan.md +34 -8
- package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +399 -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 +164 -156
- 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/skills/autodoc/SKILL.md +1 -1
- package/skills/autoimplement/SKILL.md +1 -1
- package/skills/autoplan/SKILL.md +1 -1
- package/skills/pi-workflows/SKILL.md +2 -0
- 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 +3 -5
- package/src/controllers/sqlite.ts +929 -225
- package/src/controllers/store.ts +1 -0
- package/src/extension/controller-command.ts +45 -0
- package/src/extension/index.ts +948 -3506
- 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/prune.ts +35 -9
- 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 +2 -60
- 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/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/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/session-events.ts +0 -121
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,32 +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
|
-
- `status` for the active run or a supplied run ID
|
|
434
|
-
- `pause
|
|
435
|
-
- `
|
|
436
|
-
- `
|
|
437
|
-
- `
|
|
438
|
-
- `remove-follow-up` to remove an unsent prompt created by the same model source.
|
|
439
|
-
- `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;
|
|
440
473
|
- `submit` for the current workflow step contract.
|
|
441
474
|
|
|
442
|
-
A
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
`
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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.
|
|
480
|
+
|
|
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.
|
|
485
|
+
|
|
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.
|
|
457
491
|
|
|
458
492
|
### Built-in plain summary
|
|
459
493
|
|
|
@@ -557,7 +591,7 @@ Only bounded final assistant text and safe operational facts leave a live child
|
|
|
557
591
|
|
|
558
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.
|
|
559
593
|
|
|
560
|
-
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.
|
|
561
595
|
|
|
562
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.
|
|
563
597
|
|
|
@@ -648,49 +682,22 @@ use an internal turn-intent contract instead of the notification outbox. See
|
|
|
648
682
|
[WORKFLOW_STEP_MESSAGES.md](WORKFLOW_STEP_MESSAGES.md) for the step-message contract
|
|
649
683
|
and [Deferred workflow turns](DEFERRED_TURNS.md) for the successor-turn contract.
|
|
650
684
|
|
|
651
|
-
##
|
|
685
|
+
## Visible responses
|
|
652
686
|
|
|
653
687
|
Workflow nodes normally produce structured values for routing and persistence.
|
|
654
|
-
When a person
|
|
655
|
-
`
|
|
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.
|
|
656
692
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
state.status === "waiting"
|
|
662
|
-
? `Explain this recommendation and ask the user to decide: ${JSON.stringify(finalOutput)}`
|
|
663
|
-
: "Summarize the completed result and any remaining limitations.",
|
|
664
|
-
// ...startAt, nodes, and edges
|
|
665
|
-
});
|
|
666
|
-
```
|
|
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.
|
|
667
697
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
Returning `undefined`, returning an empty string, or omitting
|
|
672
|
-
`presentationPrompt` produces no presentation. Failed, timed-out, and cancelled
|
|
673
|
-
runs are never presented. When one of those outcomes would otherwise strand an
|
|
674
|
-
agent after a workflow-caused turn abort or asynchronous crash, the extension
|
|
675
|
-
uses the deferred-turn contract to send one factual fallback after settlement.
|
|
676
|
-
Async prompt builders have 30 seconds to finish and receive an
|
|
677
|
-
`AbortSignal` that fires on timeout, session shutdown, or when a new workflow
|
|
678
|
-
or normal user turn starts; stale presentations are discarded. Once a presentation message has
|
|
679
|
-
been queued, another workflow cannot start until that assistant response
|
|
680
|
-
settles, so results cannot interleave.
|
|
681
|
-
|
|
682
|
-
An agent with `expectedOutput: assistantMessage()` is different. Its visible
|
|
683
|
-
assistant response is the node output, can appear before later nodes, and also
|
|
684
|
-
works inside an included workflow. A root `presentationPrompt` would add a
|
|
685
|
-
second response, so workflows that end with assistant-message output normally
|
|
686
|
-
omit it.
|
|
687
|
-
|
|
688
|
-
Presentation is outside the workflow graph: it cannot route to another node,
|
|
689
|
-
change the run status, or alter the SQLite run state. If prompt generation or message
|
|
690
|
-
delivery fails, the extension reports a warning and leaves the finished run
|
|
691
|
-
unchanged. Opting in adds one hidden custom message and one assistant response
|
|
692
|
-
to the normal Pi session; it adds no other persistent data and uses no Pi
|
|
693
|
-
internals.
|
|
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.
|
|
694
701
|
|
|
695
702
|
## Runtime behavior
|
|
696
703
|
|
|
@@ -701,35 +708,31 @@ possible. Defaults worth knowing:
|
|
|
701
708
|
- Node timeout is 15 minutes unless the node sets `timeoutMs` to a positive
|
|
702
709
|
number or context callback. A timed-out node has outcome `timed_out` and can
|
|
703
710
|
be routed with `$result.outcome`. A timed-out agent node also aborts its Pi
|
|
704
|
-
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.
|
|
705
714
|
- `maxSteps` (workflow-level, default 100) bounds loops built from cycles in
|
|
706
715
|
the graph.
|
|
707
|
-
- `/workflow pause`
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
is explicit user control. When no run is live but the widget still shows a parked or finished run,
|
|
728
|
-
the command clears the widget.
|
|
729
|
-
- One workflow runs per session at a time.
|
|
730
|
-
- 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.
|
|
731
|
-
- Agent nudges: if the model ends its turn without submitting the pending
|
|
732
|
-
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.
|
|
733
736
|
|
|
734
737
|
## Workflows started by controllers
|
|
735
738
|
|
|
@@ -754,14 +757,16 @@ if (run.state !== "succeeded") {
|
|
|
754
757
|
}
|
|
755
758
|
```
|
|
756
759
|
|
|
757
|
-
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.
|
|
758
761
|
|
|
759
762
|
See [CONTROLLERS.md](CONTROLLERS.md) for controller definitions and the full recovery contract.
|
|
760
763
|
|
|
761
|
-
## Using the engine outside
|
|
764
|
+
## Using the engine outside Pi
|
|
762
765
|
|
|
763
|
-
The engine
|
|
764
|
-
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.
|
|
765
770
|
|
|
766
771
|
```typescript
|
|
767
772
|
import { WorkflowEngine, type AgentStepExecutor } from "@osolmaz/pi-workflows";
|
|
@@ -774,6 +779,9 @@ const executor: AgentStepExecutor = {
|
|
|
774
779
|
},
|
|
775
780
|
};
|
|
776
781
|
|
|
777
|
-
const engine = new WorkflowEngine({
|
|
782
|
+
const engine = new WorkflowEngine({
|
|
783
|
+
executor,
|
|
784
|
+
databasePath: "/tmp/workflow-state.sqlite",
|
|
785
|
+
});
|
|
778
786
|
const { state } = await engine.run(workflow, { task: "..." });
|
|
779
787
|
```
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
action,
|
|
3
3
|
compute,
|
|
4
4
|
defineWorkflow,
|
|
5
|
+
manualEffect,
|
|
5
6
|
runCommandBatch,
|
|
6
7
|
type CommandBatchRequest,
|
|
7
8
|
} from "@osolmaz/pi-workflows";
|
|
@@ -40,6 +41,7 @@ export default defineWorkflow({
|
|
|
40
41
|
}) satisfies CommandBatchRequest,
|
|
41
42
|
}),
|
|
42
43
|
run: action({
|
|
44
|
+
effect: manualEffect("example.command-batch.run"),
|
|
43
45
|
run: async (context) =>
|
|
44
46
|
await runCommandBatch(context.outputs.prepare as CommandBatchRequest, {
|
|
45
47
|
signal: context.signal,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { compute, defineWorkflow, shell } from "@osolmaz/pi-workflows";
|
|
1
|
+
import { compute, defineWorkflow, manualEffect, shell } from "@osolmaz/pi-workflows";
|
|
2
2
|
|
|
3
3
|
type ShellInput = {
|
|
4
4
|
text?: string;
|
|
@@ -10,6 +10,7 @@ export default defineWorkflow({
|
|
|
10
10
|
startAt: "echo_text",
|
|
11
11
|
nodes: {
|
|
12
12
|
echo_text: shell({
|
|
13
|
+
effect: manualEffect("example.shell.echo-text"),
|
|
13
14
|
exec: ({ input }) => ({
|
|
14
15
|
command: "printf",
|
|
15
16
|
args: ["%s", (input as ShellInput).text ?? "hello from pi-workflows"],
|
package/herdr-plugin.toml
CHANGED
package/package.json
CHANGED
package/skills/autodoc/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: autodoc
|
|
3
|
-
description:
|
|
3
|
+
description: Records or updates an existing selected plan in canonical documentation without implementing it. Use only when the user explicitly asks to run autodoc.
|
|
4
4
|
compatibility: Requires pi-workflows and the built-in autodoc workflow.
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: autoimplement
|
|
3
|
-
description:
|
|
3
|
+
description: Implements an existing plan end to end, tests it, runs pi-reviewer until no P0/P1 issues remain, and verifies CI/CD. Use only when the user explicitly asks to run autoimplement.
|
|
4
4
|
compatibility: Requires Pi Workflows and the built-in autoimplement workflow.
|
|
5
5
|
---
|
|
6
6
|
|
package/skills/autoplan/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: autoplan
|
|
3
|
-
description:
|
|
3
|
+
description: Compares practical solutions with the ideal end state, selects the best in-scope option, and produces an implementation plan. Use only when the user explicitly asks to run autoplan.
|
|
4
4
|
compatibility: Requires pi-workflows and the built-in autoplan workflow.
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -22,6 +22,8 @@ Use the smallest applicable action:
|
|
|
22
22
|
- `update` publishes a non-completing durable update for the active step attempt.
|
|
23
23
|
- `submit` completes an active submitted agent step with its required output. An assistant-message step completes through its normal visible reply instead.
|
|
24
24
|
|
|
25
|
+
When the user asks to continue or resume the active workflow, call `workflow` with `action: "resume"` immediately. Do not use `workflow status` as a substitute or prerequisite.
|
|
26
|
+
|
|
25
27
|
Use `start` only once for one requested run. Before starting, load the matching workflow skill when one exists and build its complete input. Include scope, authority, constraints, identifiers, and finish criteria required by that skill. Do not start with placeholders that still need user or model repair.
|
|
26
28
|
|
|
27
29
|
For a workflow without a specialized skill, inspect its input contract and make one complete call. For example:
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
defineWorkflow,
|
|
12
12
|
includeWorkflow,
|
|
13
13
|
includedResult,
|
|
14
|
+
manualEffect,
|
|
14
15
|
} from "../workflows/definition.js";
|
|
15
16
|
import { digest } from "../workflows/human-decision.js";
|
|
16
17
|
import { allowSettingsPath, workflowSettings } from "../workflows/settings.js";
|
|
@@ -1768,6 +1769,7 @@ export const autoimplementWorkflow = defineWorkflow({
|
|
|
1768
1769
|
run: selectReviewCommands,
|
|
1769
1770
|
}),
|
|
1770
1771
|
runReview: action({
|
|
1772
|
+
effect: manualEffect("pi-workflows.autoimplement.review"),
|
|
1771
1773
|
statusDetail: "running pi-reviewer commands",
|
|
1772
1774
|
timeoutMs: (context) => {
|
|
1773
1775
|
const selected = latestOutput<ReviewCommandSelection>(context, ["selectReviewCommands"]);
|
|
@@ -1884,6 +1886,7 @@ export const autoimplementWorkflow = defineWorkflow({
|
|
|
1884
1886
|
validate: parseCiInspectionForPublished,
|
|
1885
1887
|
}),
|
|
1886
1888
|
trackCi: action({
|
|
1889
|
+
effect: manualEffect("pi-workflows.autoimplement.track-ci"),
|
|
1887
1890
|
statusDetail: "tracking pending CI commands",
|
|
1888
1891
|
timeoutMs: (context) => {
|
|
1889
1892
|
const inspected = latestOutput<CiInspectionBatch>(context, ["inspectCi"]);
|