@osolmaz/pi-workflows 0.16.2 → 0.16.4
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 +41 -41
- package/dist/builtins/monitor.workflow.d.ts +1 -1
- package/dist/builtins/monitor.workflow.js +8 -8
- package/dist/builtins/pi-agent-group.js +3 -3
- package/dist/builtins/pi-agent-group.js.map +1 -1
- package/dist/channels/adapter-entry.js +14 -14
- package/dist/channels/adapter-entry.js.map +1 -1
- package/dist/client/client.d.ts +6 -6
- package/dist/client/client.js +27 -27
- package/dist/client/client.js.map +1 -1
- package/dist/client/protocol.d.ts +4 -1
- package/dist/client/protocol.js +17 -11
- package/dist/client/protocol.js.map +1 -1
- package/dist/client/resolver.d.ts +4 -4
- package/dist/client/view.d.ts +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/extension/index.js +32 -32
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/recorder.d.ts +1 -1
- package/dist/extension/recorder.js +1 -1
- package/dist/extension/recorder.js.map +1 -1
- package/dist/extension/remote-recorder-store.d.ts +1 -1
- package/dist/extension/remote-recorder-store.js +2 -2
- package/dist/extension/remote-recorder-store.js.map +1 -1
- package/dist/extension/{controller-command.d.ts → resource-manager-command.d.ts} +6 -6
- package/dist/extension/{controller-command.js → resource-manager-command.js} +7 -7
- package/dist/extension/resource-manager-command.js.map +1 -0
- package/dist/extension/session-delivery.d.ts +1 -1
- package/dist/extension/session-delivery.js +2 -2
- package/dist/extension/session-delivery.js.map +1 -1
- package/dist/extension/session-view.d.ts +1 -1
- package/dist/extension/session-view.js +1 -1
- package/dist/extension/session-view.js.map +1 -1
- package/dist/extension/workflow-message-coordinator.d.ts +1 -1
- package/dist/extension/workflow-message-coordinator.js +6 -6
- package/dist/extension/workflow-message-coordinator.js.map +1 -1
- package/dist/resource-managers/conditions.d.ts +6 -0
- package/dist/{controllers → resource-managers}/conditions.js +3 -3
- package/dist/resource-managers/conditions.js.map +1 -0
- package/dist/resource-managers/definition.d.ts +6 -0
- package/dist/resource-managers/definition.js +45 -0
- package/dist/resource-managers/definition.js.map +1 -0
- package/dist/resource-managers/effects.d.ts +15 -0
- package/dist/{controllers → resource-managers}/effects.js +2 -2
- package/dist/resource-managers/effects.js.map +1 -0
- package/dist/resource-managers/errors.d.ts +12 -0
- package/dist/resource-managers/errors.js +25 -0
- package/dist/resource-managers/errors.js.map +1 -0
- package/dist/resource-managers/index.d.ts +12 -0
- package/dist/resource-managers/index.js +12 -0
- package/dist/resource-managers/index.js.map +1 -0
- package/dist/resource-managers/json.js.map +1 -0
- package/dist/resource-managers/loader.d.ts +23 -0
- package/dist/resource-managers/loader.js +75 -0
- package/dist/resource-managers/loader.js.map +1 -0
- package/dist/resource-managers/results.d.ts +5 -0
- package/dist/resource-managers/results.js.map +1 -0
- package/dist/resource-managers/runtime.d.ts +59 -0
- package/dist/{controllers/manager.js → resource-managers/runtime.js} +57 -57
- package/dist/resource-managers/runtime.js.map +1 -0
- package/dist/{controllers → resource-managers}/sqlite.d.ts +44 -44
- package/dist/{controllers → resource-managers}/sqlite.js +139 -130
- package/dist/resource-managers/sqlite.js.map +1 -0
- package/dist/{controllers → resource-managers}/store.d.ts +33 -33
- package/dist/{controllers → resource-managers}/store.js.map +1 -1
- package/dist/{controllers → resource-managers}/types.d.ts +41 -41
- package/dist/{controllers → resource-managers}/types.js.map +1 -1
- package/dist/resource-managers/workflows.d.ts +34 -0
- package/dist/{controllers → resource-managers}/workflows.js +15 -15
- package/dist/resource-managers/workflows.js.map +1 -0
- package/dist/server/channel-effects.js.map +1 -0
- package/dist/{host → server}/channel-supervisor.d.ts +6 -6
- package/dist/{host → server}/channel-supervisor.js +3 -3
- package/dist/server/channel-supervisor.js.map +1 -0
- package/dist/{host/child-worker-supervisor.d.ts → server/child-runner-supervisor.d.ts} +15 -13
- package/dist/{host/child-worker-supervisor.js → server/child-runner-supervisor.js} +6 -5
- package/dist/server/child-runner-supervisor.js.map +1 -0
- package/dist/{host → server}/processes.d.ts +3 -3
- package/dist/{host → server}/processes.js +3 -3
- package/dist/server/processes.js.map +1 -0
- package/dist/{host → server}/resolver-entry.d.ts +6 -6
- package/dist/{host → server}/resolver-entry.js +14 -14
- package/dist/server/resolver-entry.js.map +1 -0
- package/dist/server/resource-runner-entry.d.ts +2 -0
- package/dist/{host/controller-worker-entry.js → server/resource-runner-entry.js} +33 -33
- package/dist/server/resource-runner-entry.js.map +1 -0
- package/dist/server/resource-runner-protocol.d.ts +36 -0
- package/dist/server/resource-runner-protocol.js +49 -0
- package/dist/server/resource-runner-protocol.js.map +1 -0
- package/dist/server/resource-runner-supervisor.d.ts +21 -0
- package/dist/{host/worker-supervisor.js → server/resource-runner-supervisor.js} +17 -17
- package/dist/server/resource-runner-supervisor.js.map +1 -0
- package/dist/{host → server}/rpc-bridge.d.ts +3 -3
- package/dist/{host → server}/rpc-bridge.js +3 -3
- package/dist/server/rpc-bridge.js.map +1 -0
- package/dist/{host → server}/rpc-executor.d.ts +1 -1
- package/dist/{host → server}/rpc-executor.js +3 -3
- package/dist/server/rpc-executor.js.map +1 -0
- package/dist/{host/host-entry.js → server/server-entry.js} +6 -6
- package/dist/server/server-entry.js.map +1 -0
- package/dist/{host/runner.d.ts → server/server.d.ts} +39 -38
- package/dist/{host/runner.js → server/server.js} +408 -374
- package/dist/server/server.js.map +1 -0
- package/dist/{host → server}/state.d.ts +25 -25
- package/dist/{host → server}/state.js +49 -49
- package/dist/server/state.js.map +1 -0
- package/dist/{host → server}/view.d.ts +7 -7
- package/dist/{host → server}/view.js +13 -13
- package/dist/server/view.js.map +1 -0
- package/dist/server/workflow-runner-content.d.ts +6 -0
- package/dist/server/workflow-runner-content.js +96 -0
- package/dist/server/workflow-runner-content.js.map +1 -0
- package/dist/{host/worker-entry.d.ts → server/workflow-runner-entry.d.ts} +3 -3
- package/dist/{host/worker-entry.js → server/workflow-runner-entry.js} +68 -35
- package/dist/server/workflow-runner-entry.js.map +1 -0
- package/dist/server/workflow-runner-protocol.d.ts +69 -0
- package/dist/server/workflow-runner-protocol.js +176 -0
- package/dist/server/workflow-runner-protocol.js.map +1 -0
- package/dist/{host/worker-store.d.ts → server/workflow-runner-store.d.ts} +11 -11
- package/dist/{host/worker-store.js → server/workflow-runner-store.js} +8 -8
- package/dist/server/workflow-runner-store.js.map +1 -0
- package/dist/server/workflow-runner-supervisor.d.ts +22 -0
- package/dist/server/workflow-runner-supervisor.js +56 -0
- package/dist/server/workflow-runner-supervisor.js.map +1 -0
- package/dist/state/prune.d.ts +1 -1
- package/dist/state/prune.js +17 -10
- package/dist/state/prune.js.map +1 -1
- package/dist/state/schema.js +2 -2
- package/dist/state/workflow-messages.d.ts +1 -1
- package/dist/state/workflow-messages.js +1 -1
- package/dist/state/workflow-messages.js.map +1 -1
- package/dist/viewer/backup.js +1 -1
- package/dist/viewer/backup.js.map +1 -1
- package/dist/viewer/cli.d.ts +2 -2
- package/dist/viewer/cli.js +46 -32
- package/dist/viewer/cli.js.map +1 -1
- package/dist/viewer/tui.d.ts +1 -1
- package/dist/viewer/tui.js +1 -1
- package/dist/viewer/tui.js.map +1 -1
- package/dist/workflows/command-batch.js +2 -2
- package/dist/workflows/engine.js +14 -15
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/store.d.ts +12 -11
- package/dist/workflows/store.js +24 -18
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/types.d.ts +2 -2
- package/docs/2026-08-20-durable-workflow-launch-plan.md +12 -12
- package/docs/2026-08-25-workflow-follow-ups.md +7 -7
- package/docs/2026-08-25-workflow-settings.md +3 -3
- package/docs/2026-08-30-out-of-process-workflow-host-plan.md +3 -3
- package/docs/2026-09-01-restore-session-delivery-controls-plan.md +5 -5
- package/docs/2026-09-01-unified-workflow-client-plan.md +6 -6
- package/docs/2026-09-02-installed-live-e2e-plan.md +3 -3
- package/docs/2026-09-02-unify-workflow-messages-plan.md +8 -8
- package/docs/2026-09-04-workflow-run-state-plan.md +6 -6
- package/docs/DEFERRED_TURNS.md +7 -7
- package/docs/DESIGN_PHILOSOPHY.md +1 -1
- package/docs/HUMAN_DECISIONS.md +11 -11
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +4 -4
- package/docs/MONITOR.md +1 -1
- package/docs/RESOURCE_MANAGERS.md +221 -0
- package/docs/SQLITE_STATE.md +35 -33
- package/docs/WORKFLOW_COMPOSITION.md +1 -1
- package/docs/{WORKFLOW_HOST.md → WORKFLOW_SERVER.md} +125 -119
- package/docs/WORKFLOW_STEP_MESSAGES.md +18 -18
- package/docs/WORKFLOW_UPDATES.md +16 -16
- package/docs/development.md +15 -14
- package/docs/live-replay-protocol.md +20 -20
- package/docs/plans/2026-08-04-controller-runtime-plan.md +25 -25
- package/docs/plans/2026-08-05-always-on-workflows-plan.md +8 -8
- package/docs/plans/2026-08-10-agent-managed-monitor-workflows-plan.md +2 -2
- package/docs/plans/2026-08-13-session-addressed-workflow-notifications-plan.md +1 -1
- package/docs/plans/2026-08-16-workflow-updates-plan.md +5 -5
- package/docs/plans/2026-08-19-human-decision-gates-plan.md +1 -1
- package/docs/plans/2026-08-20-bounded-command-batches-plan.md +1 -1
- package/docs/plans/2026-08-21-deferred-turn-intents-plan.md +3 -3
- package/docs/plans/2026-08-21-sanity-check-plan.md +2 -2
- package/docs/plans/2026-08-23-assistant-agent-completion-plan.md +5 -5
- package/docs/plans/2026-08-23-sqlite-state-plan.md +25 -25
- package/docs/plans/2026-08-25-live-workflow-settings-plan.md +4 -4
- package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +3 -3
- package/docs/plans/2026-09-04-workflow-runner-resume-state-plan.md +318 -0
- package/docs/plans/piw-viewer-experience-implementation-plan.md +1 -1
- package/docs/tui-viewer.md +9 -9
- package/docs/workflows.md +72 -66
- package/examples/{controllers/pull-request.controller.ts → resource-managers/pull-request.resource-manager.ts} +12 -12
- package/herdr-plugin.toml +1 -1
- package/package.json +12 -11
- package/protocol/client.v1.schema.json +7 -7
- package/protocol/fixtures/client-v1.json +1 -1
- package/src/builtins/monitor.workflow.ts +9 -9
- package/src/builtins/pi-agent-group.ts +3 -3
- package/src/channels/adapter-entry.ts +14 -14
- package/src/client/client.ts +33 -32
- package/src/client/protocol.ts +16 -11
- package/src/client/resolver.ts +4 -4
- package/src/client/view.ts +1 -1
- package/src/extension/index.ts +44 -34
- package/src/extension/recorder.ts +1 -1
- package/src/extension/remote-recorder-store.ts +2 -2
- package/src/extension/resource-manager-command.ts +45 -0
- package/src/extension/session-delivery.ts +2 -2
- package/src/extension/session-view.ts +1 -1
- package/src/extension/workflow-message-coordinator.ts +6 -6
- package/src/{controllers → resource-managers}/conditions.ts +23 -23
- package/src/resource-managers/definition.ts +71 -0
- package/src/{controllers → resource-managers}/effects.ts +9 -9
- package/src/resource-managers/errors.ts +27 -0
- package/src/resource-managers/index.ts +88 -0
- package/src/resource-managers/loader.ts +111 -0
- package/src/{controllers → resource-managers}/results.ts +4 -4
- package/src/{controllers/manager.ts → resource-managers/runtime.ts} +95 -92
- package/src/{controllers → resource-managers}/sqlite.ts +198 -186
- package/src/{controllers → resource-managers}/store.ts +46 -38
- package/src/{controllers → resource-managers}/types.ts +50 -41
- package/src/{controllers → resource-managers}/workflows.ts +43 -41
- package/src/{host → server}/channel-supervisor.ts +8 -8
- package/src/{host/child-worker-supervisor.ts → server/child-runner-supervisor.ts} +22 -18
- package/src/{host → server}/processes.ts +3 -3
- package/src/{host → server}/resolver-entry.ts +28 -25
- package/src/{host/controller-worker-entry.ts → server/resource-runner-entry.ts} +67 -65
- package/src/server/resource-runner-protocol.ts +103 -0
- package/src/server/resource-runner-supervisor.ts +76 -0
- package/src/{host → server}/rpc-bridge.ts +3 -3
- package/src/{host → server}/rpc-executor.ts +4 -4
- package/src/{host/host-entry.ts → server/server-entry.ts} +5 -5
- package/src/{host/runner.ts → server/server.ts} +526 -461
- package/src/{host → server}/state.ts +78 -67
- package/src/{host → server}/view.ts +15 -15
- package/src/server/workflow-runner-content.ts +119 -0
- package/src/{host/worker-entry.ts → server/workflow-runner-entry.ts} +98 -57
- package/src/server/workflow-runner-protocol.ts +241 -0
- package/src/{host/worker-store.ts → server/workflow-runner-store.ts} +18 -20
- package/src/server/workflow-runner-supervisor.ts +79 -0
- package/src/state/prune.ts +27 -9
- package/src/state/schema.ts +2 -2
- package/src/state/workflow-messages.ts +1 -1
- package/src/viewer/backup.ts +1 -1
- package/src/viewer/cli.ts +50 -36
- package/src/viewer/tui.ts +1 -1
- package/src/workflows/command-batch.ts +2 -2
- package/src/workflows/engine.ts +14 -15
- package/src/workflows/store.ts +31 -28
- package/src/workflows/types.ts +2 -2
- package/dist/controllers/conditions.d.ts +0 -6
- package/dist/controllers/conditions.js.map +0 -1
- package/dist/controllers/definition.d.ts +0 -6
- package/dist/controllers/definition.js +0 -45
- package/dist/controllers/definition.js.map +0 -1
- package/dist/controllers/effects.d.ts +0 -15
- package/dist/controllers/effects.js.map +0 -1
- package/dist/controllers/errors.d.ts +0 -12
- package/dist/controllers/errors.js +0 -25
- package/dist/controllers/errors.js.map +0 -1
- package/dist/controllers/index.d.ts +0 -12
- package/dist/controllers/index.js +0 -12
- package/dist/controllers/index.js.map +0 -1
- package/dist/controllers/json.js.map +0 -1
- package/dist/controllers/loader.d.ts +0 -23
- package/dist/controllers/loader.js +0 -74
- package/dist/controllers/loader.js.map +0 -1
- package/dist/controllers/manager.d.ts +0 -59
- package/dist/controllers/manager.js.map +0 -1
- package/dist/controllers/results.d.ts +0 -5
- package/dist/controllers/results.js.map +0 -1
- package/dist/controllers/sqlite.js.map +0 -1
- package/dist/controllers/workflows.d.ts +0 -34
- package/dist/controllers/workflows.js.map +0 -1
- package/dist/extension/controller-command.js.map +0 -1
- package/dist/host/channel-effects.js.map +0 -1
- package/dist/host/channel-supervisor.js.map +0 -1
- package/dist/host/child-worker-supervisor.js.map +0 -1
- package/dist/host/controller-worker-entry.d.ts +0 -2
- package/dist/host/controller-worker-entry.js.map +0 -1
- package/dist/host/controller-worker-protocol.d.ts +0 -36
- package/dist/host/controller-worker-protocol.js +0 -49
- package/dist/host/controller-worker-protocol.js.map +0 -1
- package/dist/host/controller-worker-supervisor.d.ts +0 -21
- package/dist/host/controller-worker-supervisor.js +0 -54
- package/dist/host/controller-worker-supervisor.js.map +0 -1
- package/dist/host/host-entry.js.map +0 -1
- package/dist/host/processes.js.map +0 -1
- package/dist/host/resolver-entry.js.map +0 -1
- package/dist/host/rpc-bridge.js.map +0 -1
- package/dist/host/rpc-executor.js.map +0 -1
- package/dist/host/runner.js.map +0 -1
- package/dist/host/state.js.map +0 -1
- package/dist/host/view.js.map +0 -1
- package/dist/host/worker-entry.js.map +0 -1
- package/dist/host/worker-protocol.d.ts +0 -46
- package/dist/host/worker-protocol.js +0 -122
- package/dist/host/worker-protocol.js.map +0 -1
- package/dist/host/worker-store.js.map +0 -1
- package/dist/host/worker-supervisor.d.ts +0 -22
- package/dist/host/worker-supervisor.js.map +0 -1
- package/docs/CONTROLLERS.md +0 -217
- package/src/controllers/definition.ts +0 -65
- package/src/controllers/errors.ts +0 -27
- package/src/controllers/index.ts +0 -88
- package/src/controllers/loader.ts +0 -104
- package/src/extension/controller-command.ts +0 -45
- package/src/host/controller-worker-protocol.ts +0 -104
- package/src/host/controller-worker-supervisor.ts +0 -79
- package/src/host/worker-protocol.ts +0 -176
- package/src/host/worker-supervisor.ts +0 -74
- /package/dist/{controllers → resource-managers}/json.d.ts +0 -0
- /package/dist/{controllers → resource-managers}/json.js +0 -0
- /package/dist/{controllers → resource-managers}/results.js +0 -0
- /package/dist/{controllers → resource-managers}/store.js +0 -0
- /package/dist/{controllers → resource-managers}/types.js +0 -0
- /package/dist/{host → server}/channel-effects.d.ts +0 -0
- /package/dist/{host → server}/channel-effects.js +0 -0
- /package/dist/{host/host-entry.d.ts → server/server-entry.d.ts} +0 -0
- /package/src/{controllers → resource-managers}/json.ts +0 -0
- /package/src/{host → server}/channel-effects.ts +0 -0
|
@@ -59,7 +59,7 @@ A request uses `pi-workflows.human-decision-request.v1`. It contains:
|
|
|
59
59
|
| `title` | Yes | Short decision title shown by every channel. |
|
|
60
60
|
| `subject` | Yes | Canonical JSON data used by the workflow. |
|
|
61
61
|
| `presentation` | Yes | Human-readable content defined below. |
|
|
62
|
-
| `audience` | Yes | Named audience resolved by the
|
|
62
|
+
| `audience` | Yes | Named audience resolved by the server. |
|
|
63
63
|
| `choices` | Yes | Typed choices and optional input contracts. |
|
|
64
64
|
| `revision` | Yes | Positive decision revision. |
|
|
65
65
|
| `subjectDigest` | Yes | SHA-256 digest of the canonical subject. |
|
|
@@ -216,13 +216,13 @@ The renderer:
|
|
|
216
216
|
|
|
217
217
|
The renderer never adds an ellipsis in place of omitted decision content.
|
|
218
218
|
|
|
219
|
-
The
|
|
219
|
+
The server saves each channel message before the adapter sends it. It records each part after an unambiguous response. If a send result is uncertain, the server marks that channel message `ambiguous` and does not retry that part or later parts automatically. Another configured channel can still answer the decision.
|
|
220
220
|
|
|
221
221
|
### Pi
|
|
222
222
|
|
|
223
|
-
The
|
|
223
|
+
The server creates one `decision` workflow message. The shared extension coordinator sends it through documented `pi.sendMessage()` without starting a model turn. Its custom renderer shows the complete presentation and fingerprint with the choices, input rules, request ID, and deadline. It wraps and scrolls with normal Pi custom-message behavior.
|
|
224
224
|
|
|
225
|
-
A verified operator answers through `/workflow answer` or the matching `piw` control. When Telegram or another channel accepts the decision, the
|
|
225
|
+
A verified operator answers through `/workflow answer` or the matching `piw` control. When Telegram or another channel accepts the decision, the server cancels an unsent Pi decision message. A sent card remains normal conversation history, while later answer controls return the saved winner. Pi Workflows does not open a blocking dialog, modify Pi core, or use undocumented TUI state.
|
|
226
226
|
|
|
227
227
|
### Other channels
|
|
228
228
|
|
package/docs/MONITOR.md
CHANGED
|
@@ -125,7 +125,7 @@ The action step returns whether the action succeeded, failed, or was blocked, wi
|
|
|
125
125
|
|
|
126
126
|
The repair input preserves the action request, target evidence, authority, constraints, repository, and delivery limits. Existing plan approval rules still apply when the recorded contract requires them.
|
|
127
127
|
|
|
128
|
-
Paid
|
|
128
|
+
Paid runners affected by a shared code or data defect must stop at safe boundaries before repair starts. Monitor preserves their durable outputs and failure evidence.
|
|
129
129
|
|
|
130
130
|
After a completed repair, Monitor runs `observe` immediately. If the same failure ID and target-state ID return, Monitor stops. It does not run the same repair cycle again.
|
|
131
131
|
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Resource manager specification
|
|
2
|
+
|
|
3
|
+
pi-workflows runs finite TypeScript graphs. A graph starts with an input, follows explicit edges, and ends with a result or checkpoint. This works well for one bounded task.
|
|
4
|
+
|
|
5
|
+
Long-running automation has a different job. It must keep comparing a requested state with the current state of another system. Events can arrive more than once, processes can stop between an external request and its local receipt, and the external state can change while work is running.
|
|
6
|
+
|
|
7
|
+
This specification adds Kubernetes-style resource management to pi-workflows. The resource manager runtime sits beside the graph engine. Resource managers manage durable resources, while workflows remain finite jobs that a resource manager can start and observe.
|
|
8
|
+
|
|
9
|
+
The design follows the Kubernetes [controller pattern](https://kubernetes.io/docs/concepts/architecture/controller/), its [`spec` and `status` split](https://kubernetes.io/docs/concepts/overview/working-with-objects/), and the [idempotent reconciliation guidance](https://book.kubebuilder.io/reference/good-practices).
|
|
10
|
+
|
|
11
|
+
## Scope
|
|
12
|
+
|
|
13
|
+
The resource manager runtime provides:
|
|
14
|
+
|
|
15
|
+
- Durable desired and observed state.
|
|
16
|
+
- Level-based reconciliation from current facts.
|
|
17
|
+
- A deduplicated work queue with delayed retries.
|
|
18
|
+
- Compare-and-swap writes for concurrent resource runners.
|
|
19
|
+
- Recoverable records for external effects.
|
|
20
|
+
- Child workflow runs with stable request keys.
|
|
21
|
+
- Conditions and generations, with cleanup and structured events.
|
|
22
|
+
- Local resource control through the Pi extension and reconciliation through the global server.
|
|
23
|
+
|
|
24
|
+
The first production use case is pull request automation. A resource manager can observe a pull request, start a review or repair workflow, wait for checks, validate the current head, and apply an approved change through deterministic code.
|
|
25
|
+
|
|
26
|
+
## Boundaries
|
|
27
|
+
|
|
28
|
+
The graph engine remains the execution layer for finite work. A finite workflow can include another finite workflow in the same run through `includeWorkflow()`. Use a resource manager child run when work needs an independent retry history, stable request key, parallel lifecycle, or indefinite reconciliation. The graph engine does not import the resource manager runtime. The resource manager runtime may start workflows through a narrow scheduler interface.
|
|
29
|
+
|
|
30
|
+
The Pi extension is a thin client. It resolves resource manager initialization in a dedicated child process and sends declarative resource commands to the one global server. The server schedules reconciliation but does not load resource manager definitions in its event loop. A supervised resource runner loads one definition and proposes bounded state changes through the server.
|
|
31
|
+
|
|
32
|
+
External events are wake-up hints. An event enqueues a resource key and carries no transition command. The reconciler reloads the resource and the external system before deciding what to do.
|
|
33
|
+
|
|
34
|
+
## Resource model
|
|
35
|
+
|
|
36
|
+
A managed resource is the durable record of one requested outcome. The resource manager owns `status`; callers own `spec`.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
export type ManagedResource<TSpec, TStatus> = {
|
|
40
|
+
metadata: {
|
|
41
|
+
uid: string;
|
|
42
|
+
resourceManager: string;
|
|
43
|
+
key: string;
|
|
44
|
+
resourceVersion: number;
|
|
45
|
+
generation: number;
|
|
46
|
+
deletionTimestamp?: string;
|
|
47
|
+
finalizers: string[];
|
|
48
|
+
};
|
|
49
|
+
spec: TSpec;
|
|
50
|
+
status: {
|
|
51
|
+
observedGeneration: number;
|
|
52
|
+
conditions: ManagedResourceCondition[];
|
|
53
|
+
workflowRun?: {
|
|
54
|
+
requestId: string;
|
|
55
|
+
runId?: string;
|
|
56
|
+
state: "pending" | "running" | "waiting" | "succeeded" | "failed" | "interrupted";
|
|
57
|
+
attempt: number;
|
|
58
|
+
};
|
|
59
|
+
resourceManagerStatus: TStatus;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type ManagedResourceCondition = {
|
|
64
|
+
type: string;
|
|
65
|
+
status: true | false | "unknown";
|
|
66
|
+
reason: string;
|
|
67
|
+
message?: string;
|
|
68
|
+
observedGeneration: number;
|
|
69
|
+
lastTransitionTime: string;
|
|
70
|
+
};
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`uid` stays stable for the life of the resource and is never reused. `resourceVersion` changes after every write and acts as the compare-and-swap token. `generation` changes only when `spec` changes. A condition describes the latest known state for one stable condition type.
|
|
74
|
+
|
|
75
|
+
`observedGeneration` shows which desired state produced the current status. A resource manager must not report a resource as ready when its conditions came from an older generation.
|
|
76
|
+
|
|
77
|
+
## Resource manager contract
|
|
78
|
+
|
|
79
|
+
A resource manager receives the latest resource, a cancellation signal, and runtime services. It returns after one bounded reconciliation pass.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import {
|
|
83
|
+
conditionFalse,
|
|
84
|
+
conditionTrue,
|
|
85
|
+
defineResourceManager,
|
|
86
|
+
} from "@osolmaz/pi-workflows/resource-managers";
|
|
87
|
+
|
|
88
|
+
export default defineResourceManager<PullRequestSpec, PullRequestStatus>({
|
|
89
|
+
name: "pull-request",
|
|
90
|
+
initialStatus: () => ({ phase: "observing" }),
|
|
91
|
+
|
|
92
|
+
async reconcile(ctx, resource) {
|
|
93
|
+
const pullRequest = await github.getPullRequest(resource.spec, ctx.signal);
|
|
94
|
+
|
|
95
|
+
if (pullRequest.merged) {
|
|
96
|
+
return ctx.settled({
|
|
97
|
+
resourceManagerStatus: { phase: "merged" },
|
|
98
|
+
conditions: [conditionTrue("Ready", "Merged")],
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (pullRequest.headSha !== resource.spec.expectedHeadSha) {
|
|
102
|
+
return ctx.settled({
|
|
103
|
+
resourceManagerStatus: { phase: "blocked" },
|
|
104
|
+
conditions: [conditionFalse("Ready", "HeadChanged")],
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const run = await ctx.workflows.ensure({
|
|
109
|
+
requestKey: `repair:${resource.metadata.generation}:${pullRequest.headSha}`,
|
|
110
|
+
workflow: "repair-pull-request",
|
|
111
|
+
input: { repository: resource.spec.repository, number: pullRequest.number },
|
|
112
|
+
});
|
|
113
|
+
if (run.state !== "succeeded") {
|
|
114
|
+
return ctx.requeueAfter(30_000);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
await ctx.effects.ensure({
|
|
118
|
+
key: `merge:${resource.metadata.generation}:${pullRequest.headSha}`,
|
|
119
|
+
kind: "github-merge",
|
|
120
|
+
request: { number: pullRequest.number, expectedHeadSha: pullRequest.headSha },
|
|
121
|
+
observe: (signal) => github.observeMerge(resource.spec, signal),
|
|
122
|
+
apply: (signal) => github.merge(resource.spec, pullRequest.headSha, signal),
|
|
123
|
+
});
|
|
124
|
+
return ctx.requeue();
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The runtime supports three normal results. `settled` removes the key from the queue until another event arrives. `requeue` asks for another pass as soon as capacity is available. `requeueAfter` schedules a later pass.
|
|
130
|
+
|
|
131
|
+
A returned error receives exponential backoff with jitter. A resource manager records a durable condition and returns `settled` for a problem that requires new input. Reconciliation retries do not depend on the event that caused the first attempt.
|
|
132
|
+
|
|
133
|
+
## Work queue
|
|
134
|
+
|
|
135
|
+
The queue contains one row for each resource manager and resource key. Repeated enqueue calls update that row instead of adding copies. A resource runner claims a key with an opaque claim token and an expiry time. The runtime prevents concurrent reconciliation of the same key.
|
|
136
|
+
|
|
137
|
+
A claim that expires returns to the queue. A successful settled result removes the queue row. A requested delay updates its available time. Consecutive errors increase an internal retry counter used for backoff.
|
|
138
|
+
|
|
139
|
+
The local implementation uses `better-sqlite3` in WAL mode. Transactions cover resource compare-and-swap writes, queue claims, and effect claims. `ResourceManagerStore` remains an interface so another server can supply a remote implementation. Pi servers use a store scoped to the canonical project directory, which prevents a same-named resource manager in another project from claiming its resources. The store limits each resource spec and status value to 1 MiB. Event payloads are limited to 64 KiB.
|
|
140
|
+
|
|
141
|
+
The resource store is the source of truth. Queue rows only describe delivery. A repair can rebuild the queue by enqueuing every resource; each reconciler then computes any needed delay again.
|
|
142
|
+
|
|
143
|
+
## External effects
|
|
144
|
+
|
|
145
|
+
An external effect can succeed while the local process is unable to save the response. The runtime records each effect before calling the provider.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
export type EffectRecord = {
|
|
149
|
+
key: string;
|
|
150
|
+
resourceUid: string;
|
|
151
|
+
generation: number;
|
|
152
|
+
requestFingerprint: string;
|
|
153
|
+
state: "pending" | "applied" | "rejected" | "indeterminate";
|
|
154
|
+
externalRef?: string;
|
|
155
|
+
startedAt: string;
|
|
156
|
+
completedAt?: string;
|
|
157
|
+
};
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The key names one intended effect. Reusing the key with another request fingerprint is an error. The next reconciliation observes the external system before retrying an existing pending or indeterminate effect. The effect can be treated as effectively once when the provider offers an idempotency token, a conditional request, or a reliable way to observe the requested result. The runtime does not promise generic exactly-once execution.
|
|
161
|
+
|
|
162
|
+
Mutation policy stays in deterministic effect drivers. Agent workflows return findings or artifacts for deterministic code to check and apply. Runner processes run as the same operating-system user and are not credential sandboxes. Deployments that require credential isolation should put authenticated effects behind a separate broker.
|
|
163
|
+
|
|
164
|
+
## Child workflows
|
|
165
|
+
|
|
166
|
+
`ctx.workflows.ensure()` creates or finds a workflow run by a stable request key and input fingerprint. Repeated reconciliations find the same active or completed request. A changed input must use a new key. An asynchronous child completion validates the reserved request and run IDs through a separate scheduler-completion command; it never reuses the resource manager claim that launched the child. The resource manager transaction reserves and saves each attempt's run ID before the scheduler starts it, so recovery can find the run row.
|
|
167
|
+
|
|
168
|
+
A child run has one durable execution record. The parent resource points to the current run, and workflow completion enqueues the parent key. The global server runs it through the same queue and supervised runner protocol as other workflows.
|
|
169
|
+
|
|
170
|
+
A stopped runner does not make the child failed by itself. The server reads the last committed node and effect state. It resumes pure or idempotent work in a new runner epoch. An uncertain manual effect becomes ambiguous and blocks automatic retry. A changed input still requires a new stable request key.
|
|
171
|
+
|
|
172
|
+
## Deletion and cleanup
|
|
173
|
+
|
|
174
|
+
Setting `deletionTimestamp` requests deletion. A resource manager with a finalizer first removes external resources it owns, then removes its finalizer. The store deletes the resource after the finalizer list becomes empty.
|
|
175
|
+
|
|
176
|
+
Resource managers should add finalizers only when they own something that needs cleanup, such as an isolated worktree or a remote action session. Ordinary completed resources can remain as history or be removed by a separate retention policy.
|
|
177
|
+
|
|
178
|
+
## Sources and runners
|
|
179
|
+
|
|
180
|
+
A source maps an external event to one or more resource keys. Sources include filesystem watches, webhooks, scheduled polling, and child workflow completion. They share the same enqueue API.
|
|
181
|
+
|
|
182
|
+
The global server claims resource manager keys and starts a supervised resource runner for each active reconciliation. A reconciliation deadline stops and requeues the child even when resource manager code ignores its abort signal. Reconciler code must still pass the signal to provider calls and keep consequential writes inside guarded effect drivers. One server owns the local database. Distributed leader election remains outside this local runtime.
|
|
183
|
+
|
|
184
|
+
The extension starts the package server on demand. Reconciliation continues when the Pi session closes. The package installs no operating-system service.
|
|
185
|
+
|
|
186
|
+
## Observability
|
|
187
|
+
|
|
188
|
+
Every reconciliation emits structured records with the resource manager name, resource key, generation, reconcile ID, outcome and duration, plus the requeue reason. Effect state changes and child workflow links are also recorded. Logs and viewer projections remain secondary to the resource and effect stores.
|
|
189
|
+
|
|
190
|
+
`pi-workflows resource-managers` lists resources and their current readiness condition. `pi-workflows resource-manager <resource-manager> <key>` prints one resource together with its effects, child workflows, and recent events. Run views read the same database through query-only connections.
|
|
191
|
+
|
|
192
|
+
## Safety rules
|
|
193
|
+
|
|
194
|
+
A production resource manager must follow these rules:
|
|
195
|
+
|
|
196
|
+
- Read current external state on every reconciliation.
|
|
197
|
+
- Check authorization and target boundaries in deterministic code.
|
|
198
|
+
- Use provider-side preconditions for consequential writes when available.
|
|
199
|
+
- Save status with the resource version that was read.
|
|
200
|
+
- Reconcile again after each consequential external effect.
|
|
201
|
+
- Keep model output separate from mutation authority.
|
|
202
|
+
- Bound runner counts and retry rates. Also bound timeouts and stored payload sizes.
|
|
203
|
+
- Redact credentials and private provider responses from logs and SQLite runs.
|
|
204
|
+
|
|
205
|
+
## Package and Pi integration
|
|
206
|
+
|
|
207
|
+
The resource manager API is exported from `@osolmaz/pi-workflows/resource-managers`. Resource manager definitions use a `.resource-manager.ts` suffix. Project definitions live under `.pi/resource-managers/`; global definitions live under `~/.pi/agent/resource-managers/`.
|
|
208
|
+
|
|
209
|
+
The implementation uses documented Pi extension APIs only. `/resource-manager` lists and inspects resources, applies specs, and requests reconciliation or deletion. There are no extension-local runner start or stop controls.
|
|
210
|
+
|
|
211
|
+
For `apply`, a source resolver child discovers the named resource manager, verifies its exported name, computes `initialStatus(spec)`, and hashes the source. The server accepts the proposal only while the path still follows discovery rules and the exact digest still matches. Reconcile code runs only in a supervised resource runner.
|
|
212
|
+
|
|
213
|
+
Managed resources use the canonical [SQLite state](SQLITE_STATE.md) database. `projects` separates repository-local resources by canonical project path. ResourceManager claims use the shared lease generation, token, expiry, and expected resource revision. Effects and child workflows use durable request keys and receipts.
|
|
214
|
+
|
|
215
|
+
One global server owns that database for all Pi sessions and projects. The extension and mutating CLI paths are local protocol clients; viewers remain read-only. A resource manager child without an origin session uses a headless `pi --mode rpc` process for structured agent steps.
|
|
216
|
+
|
|
217
|
+
Normal workflow prompts, tool calls, and replies remain part of the Pi session. No Pi internal type, private API, or persistent Pi schema changes.
|
|
218
|
+
|
|
219
|
+
## Exclusions
|
|
220
|
+
|
|
221
|
+
This specification does not add Kubernetes API compatibility, YAML resources, a cluster scheduler, or a general distributed database. GitHub policy and credentials belong in a provider adapter, leaving the resource manager core independent of GitHub. The first release also excludes automatic service installation and generic exactly-once claims.
|
package/docs/SQLITE_STATE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SQLite state
|
|
2
2
|
|
|
3
|
-
Status: this is the implemented single-
|
|
3
|
+
Status: this is the implemented single-server database contract. The [workflow-message plan](2026-09-02-unify-workflow-messages-plan.md) records the schema version 1 hard cut that unified Pi message state and restored hosted behavior.
|
|
4
4
|
|
|
5
5
|
Pi Workflows stores all live durable state in one database:
|
|
6
6
|
|
|
@@ -8,11 +8,11 @@ Pi Workflows stores all live durable state in one database:
|
|
|
8
8
|
~/.pi/agent/workflows/state.sqlite
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
There is one database for the user installation. Project and run IDs separate data inside it. The
|
|
11
|
+
There is one database for the user installation. Project and run IDs separate data inside it. The server is the only production process that opens this live database. Workflow targets, extensions, CLI clients, Herdr adapters, and `piw` do not open it. Live clients use `pi-workflows.client.v1`.
|
|
12
12
|
|
|
13
13
|
## Viewer projection
|
|
14
14
|
|
|
15
|
-
The database includes the [incremental and virtualized viewer design](plans/2026-08-28-piw-incremental-viewer-plan.md). The
|
|
15
|
+
The database includes the [incremental and virtualized viewer design](plans/2026-08-28-piw-incremental-viewer-plan.md). The server owns this projection and exposes it as the canonical live run view. Local and remote renderers do not recreate it or validate its SQLite tables.
|
|
16
16
|
|
|
17
17
|
`viewer_runs` stores one presentation revision and retained revision floor for each run. `viewer_deltas` stores ordered target patches by run, presentation revision, and delta index. `viewer_session_checkpoints` stores the bounded active message and tool state at each 256-event boundary. `run_view_content` stores generated reference bytes under the exact run ID, content digest, and media type. It is separate from general state blobs, and content reads require all three identities. A viewer-visible transaction writes the domain change, advances the presentation revision, and writes its patch blobs before the same commit. Session-event transactions write each reached replay checkpoint in that transaction.
|
|
18
18
|
|
|
@@ -27,12 +27,12 @@ This is an in-place alpha schema change. The schema name and version remain `pi-
|
|
|
27
27
|
The database stores:
|
|
28
28
|
|
|
29
29
|
- workflow definitions, runs, events, node attempts, outputs, and updates
|
|
30
|
-
- global
|
|
30
|
+
- global server epochs, command receipts, runner epochs, and runner messages
|
|
31
31
|
- durable origin-session interaction requests and submissions
|
|
32
32
|
- captured Pi session entries and events
|
|
33
|
-
- run and
|
|
33
|
+
- run and resource manager queues, claims, retries, and continuations
|
|
34
34
|
- human-decision requests, submissions, resolutions, and cancellations
|
|
35
|
-
-
|
|
35
|
+
- managed resources, finalizers, effects, and child workflows
|
|
36
36
|
- workflow messages that Pi must add to origin conversations
|
|
37
37
|
- workflow settings, accepted JSON Patch changes, and post-completion follow-up prompts
|
|
38
38
|
- nonsecret channel cursors, inbox records, messages, and settlement receipts
|
|
@@ -40,7 +40,7 @@ The database stores:
|
|
|
40
40
|
|
|
41
41
|
Credentials and raw secrets must not enter the database. Channel credential files stay in their existing private configuration directory.
|
|
42
42
|
|
|
43
|
-
The implementation does not create live run directories, artifact files, decision directories, project databases,
|
|
43
|
+
The implementation does not create live run directories, artifact files, decision directories, project databases, resource manager databases, or channel databases.
|
|
44
44
|
|
|
45
45
|
## Database settings
|
|
46
46
|
|
|
@@ -62,9 +62,9 @@ The database also uses:
|
|
|
62
62
|
- directory mode `0700`
|
|
63
63
|
- database and backup mode `0600`
|
|
64
64
|
|
|
65
|
-
The
|
|
65
|
+
The server opens the active database. It verifies the application ID, user version, schema metadata, compiled DDL digest, and exact SQLite schema shape. An incompatible database fails with the standard backup-and-reset instruction. Pi Workflows does not import, reinterpret, or delete that state.
|
|
66
66
|
|
|
67
|
-
The
|
|
67
|
+
The server completes this verification before it serves any client, and no other production process opens the active database. A maintenance verifier may open an explicit inactive backup with SQLite read-only mode and `PRAGMA query_only = ON`. That offline verification path is not a live client and cannot select the active state database. TypeScript and Rust clients validate the client protocol and package versions, not the SQLite DDL digest.
|
|
68
68
|
|
|
69
69
|
The normalized run layout is an in-place alpha cutover. It keeps SQLite user version `1` and the current `v1` public record identifiers. A database with the former nested run-snapshot layout is incompatible and must be moved or removed. There is no migration, compatibility reader, dual write, alias, or second schema generation.
|
|
70
70
|
|
|
@@ -74,7 +74,7 @@ Four record groups provide the common lifecycle rules.
|
|
|
74
74
|
|
|
75
75
|
### Resources
|
|
76
76
|
|
|
77
|
-
`resources` identifies each mutable aggregate and holds its current revision. Runs, settings scopes, follow-up items, decisions,
|
|
77
|
+
`resources` identifies each mutable aggregate and holds its current revision. Runs, settings scopes, follow-up items, decisions, managed resources, effects, channels, workflow messages, and session segments have stable resource identities.
|
|
78
78
|
|
|
79
79
|
Every accepted domain command compares its expected revision and increments it once.
|
|
80
80
|
|
|
@@ -104,9 +104,9 @@ A domain row and its event are written in one transaction. Normal APIs never upd
|
|
|
104
104
|
|
|
105
105
|
`effect_attempts` records each application attempt and ownership generation. A matching repeated request adopts the existing effect. A different request under the same key is a conflict.
|
|
106
106
|
|
|
107
|
-
Local effects use deterministic transactions. For workflow action nodes, the engine creates the idempotency key from the run ID, effect type, full compiled node path, and node visit number. A projected child-workflow view cannot replace that full identity. Run queue settlement effects are created only for runs that have a `run_queue` row; direct engine and
|
|
107
|
+
Local effects use deterministic transactions. For workflow action nodes, the engine creates the idempotency key from the run ID, effect type, full compiled node path, and node visit number. A projected child-workflow view cannot replace that full identity. Run queue settlement effects are created only for runs that have a `run_queue` row; direct engine and resource-manager child runs do not create phantom queue work. External effects use provider idempotency or observation when available. An uncertain result becomes `ambiguous` and is not repeated without evidence.
|
|
108
108
|
|
|
109
|
-
Telegram delivery and settlement use these shared effect records. The
|
|
109
|
+
Telegram delivery and settlement use these shared effect records. The server records one numbered attempt before it tells the supervised adapter child to act. A confirmed result stores Telegram message references in the effect result. `channel_messages` stores the decision feature's delivery or settlement receipt; it does not copy effect state or external message references.
|
|
110
110
|
|
|
111
111
|
## Domain tables
|
|
112
112
|
|
|
@@ -117,34 +117,36 @@ The shared records do not replace domain schemas. The following `STRICT` tables
|
|
|
117
117
|
| Schema and projects | `schema_meta`, `projects` |
|
|
118
118
|
| Content | `blobs`, `run_view_content` |
|
|
119
119
|
| Shared lifecycle | `resources`, `leases`, `events`, `workflow_host_state` |
|
|
120
|
-
|
|
|
120
|
+
| Server protocol | `host_commands`, `run_workers`, `worker_messages`, `interactive_requests`, `interactive_submissions` |
|
|
121
121
|
| Workflows | `workflow_definitions`, `runs`, `run_sources`, `run_steps`, `run_bindings`, `run_queue`, `node_attempts`, `workflow_updates` |
|
|
122
122
|
| Live settings | `workflow_settings`, `workflow_setting_changes` |
|
|
123
123
|
| Post-run follow-ups | `workflow_follow_ups` |
|
|
124
124
|
| Session capture | `session_segments`, `session_entries`, `attempt_entries`, `session_events` |
|
|
125
125
|
| Human decisions | `human_decisions`, `human_decision_resolutions`, `human_decision_submissions`, `continuations` |
|
|
126
|
-
|
|
|
126
|
+
| Managed resources | `controller_resources`, `controller_finalizers`, `controller_queue`, `controller_workflows` |
|
|
127
127
|
| Effects | `effects`, `effect_attempts` |
|
|
128
128
|
| Pi messages | `workflow_messages`, `workflow_turns` |
|
|
129
129
|
| Channels | `channels`, `channel_cursors`, `channel_messages` |
|
|
130
130
|
|
|
131
|
+
The `host_*`, `run_workers`, `worker_*`, and `controller_*` names remain version-1 internal SQLite identifiers. The `host` and `controller` actor and owner values and the `~/.pi/agent/workflows/host/` state directory also remain internal identifiers. Public APIs and documentation call these components the workflow server, workflow runner, resource manager, resource runner, and managed resource. The alpha hard cut adds no alias or second storage path.
|
|
132
|
+
|
|
131
133
|
`workflow_messages` is the only table that owns adding workflow content to Pi. It stores the target session, message kind, source record, content digest, session order, `pending`, `sent`, or `cancelled` state, confirmed Pi entry ID, and creation and update times. The table stores no sender, send lease, `sending` state, or separate sent time. Active-branch evidence changes `pending` or `cancelled` to `sent`. Initial, reminder, and resumed prompts are all `step` messages; their custom details contain the reason. Interactive requests, decisions, terminal runs, notifications, follow-ups, and settings keep their own domain state.
|
|
132
134
|
|
|
133
|
-
`workflow_turns` stores the
|
|
135
|
+
`workflow_turns` stores the server-approved ownership of one Pi model turn. Each row names the exact workflow message, run, session, and turn ID. A partial unique index permits only one open turn for a message. The server checks for an exact saved turn or another open turn before insertion, so a normal conflict returns a controlled protocol error instead of a raw SQLite error. Terminalization ends every open turn for that run as `lost` in the same transaction. It cancels pending step and decision messages, plus follow-ups when the run did not complete successfully. Committed notifications remain eligible. Matching late reports adopt the saved result, while conflicting identities remain errors.
|
|
134
136
|
|
|
135
137
|
`channels` stores configured channel resource identities. `channel_cursors` stores the last accepted external polling position. `channel_messages` stores immutable decision delivery and settlement records for audit and duplicate evidence. External application state and Telegram message references belong to `effects` and `effect_attempts`.
|
|
136
138
|
|
|
137
|
-
Foreign keys join projects, runs, attempts, decisions,
|
|
139
|
+
Foreign keys join projects, runs, attempts, decisions, managed resources, effects, and channel records. Partial unique indexes enforce one active node attempt per run, one pending step message per interaction request, one nonterminal interactive continuation-chain reservation per Pi session, one decision winner, and one deterministic effect key. A run waiting for a checkpoint or protected decision keeps that chain reservation. A parked waiting parent does not block its own continuation. Reserving that continuation transfers the reservation and settles the parked parent queue in the same transaction, so a failed reservation leaves the parent recoverable.
|
|
138
140
|
|
|
139
|
-
###
|
|
141
|
+
### ServerBacked commands and interactions
|
|
140
142
|
|
|
141
|
-
`workflow_host_state` stores the one current
|
|
143
|
+
`workflow_host_state` stores the one current server epoch and its live local
|
|
142
144
|
claim. `host_commands` stores each client request fingerprint, operation,
|
|
143
145
|
outcome, revision, and receipt or error. Repeating an exact request adopts the
|
|
144
146
|
stored receipt. Reusing an ID or idempotency key for another request is a
|
|
145
147
|
conflict.
|
|
146
148
|
|
|
147
|
-
`run_workers` records each
|
|
149
|
+
`run_workers` records each runner epoch before spawn and later records its exact
|
|
148
150
|
process identity and terminal outcome. `worker_messages` deduplicates accepted
|
|
149
151
|
state-changing child messages.
|
|
150
152
|
|
|
@@ -152,9 +154,9 @@ state-changing child messages.
|
|
|
152
154
|
|
|
153
155
|
## Content-addressed values
|
|
154
156
|
|
|
155
|
-
`blobs` stores canonical JSON and UTF-8 text as bytes. Its primary key is the 32-byte SHA-256 digest of the bytes. `run_view_content` keeps
|
|
157
|
+
`blobs` stores canonical JSON and UTF-8 text as bytes. Its primary key is the 32-byte SHA-256 digest of the bytes. An oversized required runner result uses this same content-addressed store, and the runner reads and verifies it in bounded parts. It does not copy session history into runner resume state. `run_view_content` separately keeps server-generated large view values reachable for the life of the run, including aggregate outputs that do not exist as one source record. The server creates this link before it sends a content reference. Deleting the run removes the link, and normal blob pruning can then remove unreferenced content.
|
|
156
158
|
|
|
157
|
-
Insertion verifies the digest, media type, byte length, and exact bytes. Repeated content adopts the existing row. This replaces separate artifact files while keeping outputs, errors, settled Pi entries, and rendered channel text deduplicated. Opening the database never deletes blobs. The explicit prune command removes unreferenced blobs after it deletes safe old run trees.
|
|
159
|
+
Insertion verifies the digest, media type, byte length, and exact bytes. Repeated content adopts the existing row. This replaces separate artifact files while keeping outputs, errors, settled Pi entries, and rendered channel text deduplicated. Opening the database never deletes blobs. The explicit prune command removes unreferenced blobs after it deletes safe old run trees. It retains each blob referenced by an active runner transfer until that runner exits.
|
|
158
160
|
|
|
159
161
|
Runs do not store a nested `WorkflowRunState` blob. `runs` stores run-level facts and hashes for independent values. `run_sources` stores source identity without source JSON blobs. `node_attempts` stores structured workflow outputs and small execution receipts. `session_entries` is the only stored copy of each settled Pi entry. `attempt_entries` links an attempt to its prompt, response, first, and last Pi entries. `run_steps` stores ordered attempt membership and only stores an output override when a continuation changes a carried checkpoint answer.
|
|
160
162
|
|
|
@@ -168,7 +170,7 @@ An agent definition records `expectedOutput` as either a submitted-output descri
|
|
|
168
170
|
|
|
169
171
|
A completed interactive assistant-message attempt stores the accepted visible text as its node output. Its small receipt keeps the text digest, final Pi session entry ID, optional author-supplied limit, and whether recovery adopted an existing response.
|
|
170
172
|
|
|
171
|
-
An interrupted assistant-message attempt keeps its attempt ID. The extension adopts a matching durable request and existing Pi branch entry instead of displaying the prompt or accepting the response twice. Submitted and non-agent attempts use a fresh execution attempt after an uncommitted
|
|
173
|
+
An interrupted assistant-message attempt keeps its attempt ID. The extension adopts a matching durable request and existing Pi branch entry instead of displaying the prompt or accepting the response twice. Submitted and non-agent attempts use a fresh execution attempt after an uncommitted runner exit.
|
|
172
174
|
|
|
173
175
|
## Write contract
|
|
174
176
|
|
|
@@ -201,13 +203,13 @@ A TypeScript write permit carries the expected facts between layers. It is not a
|
|
|
201
203
|
Reading or finding a row never gives write authority.
|
|
202
204
|
|
|
203
205
|
- A run owner may advance the run, apply automatic decision policy, create its continuation, settle its parent, and complete its queue work.
|
|
204
|
-
- A
|
|
206
|
+
- A resource manager claim owner may update resource manager status, reserve effects, and start child workflows for that resource.
|
|
205
207
|
- A verified human channel actor may submit one answer candidate for the named decision. It does not gain run ownership.
|
|
206
|
-
- The
|
|
208
|
+
- The server-owned channel adapter path may update only its channel cursor, decision delivery and settlement records, and exact managed effects.
|
|
207
209
|
- Control commands have narrow explicit operations, such as requesting cancellation or deletion.
|
|
208
210
|
- Model-originated workflow answers cannot resolve protected human decisions.
|
|
209
211
|
|
|
210
|
-
The global
|
|
212
|
+
The global server is the sole live database owner and the normal state writer. Its protected stores check ownership and renew the exact live claim in the same transaction as the write. A stale or expired owner cannot renew itself. Pi extensions, CLI commands, Herdr adapters, and the Rust `piw` program use the versioned client protocol for live reads and controls. They do not open the active database. Only explicit inactive backup verification remains a direct read-only SQLite operation.
|
|
211
213
|
|
|
212
214
|
## Competing outcomes
|
|
213
215
|
|
|
@@ -219,15 +221,15 @@ A deadline with a validated default response is timeout-policy acceptance. It ca
|
|
|
219
221
|
|
|
220
222
|
Late or repeated commands return or adopt the durable winner. They do not overwrite it.
|
|
221
223
|
|
|
222
|
-
The same rule applies to run terminal outcomes, continuation admission, queue settlement,
|
|
224
|
+
The same rule applies to run terminal outcomes, continuation admission, queue settlement, resource manager effects, retry scheduling, channel settlement, and workflow-turn reports through their domain constraints and expected revisions. A matching turn report adopts the saved ownership result. A different report for the same turn ID remains a conflict.
|
|
223
225
|
|
|
224
226
|
## Read contract
|
|
225
227
|
|
|
226
|
-
Durable status is a pure projection of domain rows, immutable facts, current leases, effect results, and exact workflow-turn start and end reports. The
|
|
228
|
+
Durable status is a pure projection of domain rows, immutable facts, current leases, effect results, and exact workflow-turn start and end reports. The server uses these facts to produce one live run view. An open workflow turn can change display status only. It cannot change workflow authority. Every renderer consumes the server-produced display status and allowed controls without running another status reducer.
|
|
227
229
|
|
|
228
230
|
A settings scope uses its resource revision as its public change number. Each accepted patch, current value, and node binding is saved in one transaction. A checkpoint continuation keeps the same settings resources and transfers them to the continuation run.
|
|
229
231
|
|
|
230
|
-
`workflow_follow_ups` records source acceptance order, removal, and cancellation. The source and message stay attached to the continuation-chain member that accepted them; rows are not rewritten when the chain continues. The
|
|
232
|
+
`workflow_follow_ups` records source acceptance order, removal, and cancellation. The source and message stay attached to the continuation-chain member that accepted them; rows are not rewritten when the chain continues. The server walks the chain to find its final outcome. `workflow_messages` owns message state and Pi entry evidence. Failure, timeout, and cancellation cancel unsent follow-up messages.
|
|
231
233
|
|
|
232
234
|
- A terminal run fact overrides stale message state and has no open workflow turn.
|
|
233
235
|
- An accepted decision is accepted even if its continuation effect is still pending.
|
|
@@ -239,9 +241,9 @@ Read paths do not repair state. Owner reconcilers apply pending effects and writ
|
|
|
239
241
|
|
|
240
242
|
## Projects and concurrency
|
|
241
243
|
|
|
242
|
-
All projects use the same file. `projects` stores a stable ID and canonical path. Project-scoped
|
|
244
|
+
All projects use the same file. `projects` stores a stable ID and canonical path. Project-scoped resource manager and run queries use that key. One global server owns the file for the user installation. Its socket, lock, and exact child-process registry are under `~/.pi/agent/workflows/host/`. A second live server is rejected even when it was started from another project.
|
|
243
245
|
|
|
244
|
-
SQLite WAL keeps bounded projection reads consistent with commits. Writers are serialized by SQLite and must keep transactions short. Hashing, model calls, shell work, and external requests happen outside write transactions. Production clients receive revisioned snapshots, patches, and pages from the
|
|
246
|
+
SQLite WAL keeps bounded projection reads consistent with commits. Writers are serialized by SQLite and must keep transactions short. Hashing, model calls, shell work, and external requests happen outside write transactions. Production clients receive revisioned snapshots, patches, and pages from the server instead of opening concurrent SQLite readers.
|
|
245
247
|
|
|
246
248
|
This contract is for local storage on one machine. It does not claim distributed consensus or network-filesystem safety.
|
|
247
249
|
|
|
@@ -268,15 +270,15 @@ pi-workflows state prune --before 2026-08-01T00:00:00Z --dry-run
|
|
|
268
270
|
pi-workflows state prune --before 2026-08-01T00:00:00Z --backup /absolute/path/to/before-prune.sqlite --apply
|
|
269
271
|
```
|
|
270
272
|
|
|
271
|
-
These commands send maintenance operations to the
|
|
273
|
+
These commands send maintenance operations to the server when they target the active database. Only `pi-workflows state verify` with an explicit inactive backup opens SQLite in the command process. It rejects the active database, including another path to the same file.
|
|
272
274
|
|
|
273
275
|
`status` reports only safe counts, file size, active leases, and unsettled effects.
|
|
274
276
|
It does not print actor IDs, channel references, payloads, or credentials.
|
|
275
277
|
|
|
276
|
-
`prune --dry-run` reports complete terminal run trees older than the cutoff and the trees that safety checks block. It does not change the database. `prune --apply` requires a new absolute backup path. It verifies the backup, locks maintenance, rechecks the same selection in an exclusive transaction, and refuses trees with live queues, active leases, unsettled effects,
|
|
278
|
+
`prune --dry-run` reports complete terminal run trees older than the cutoff and the trees that safety checks block. It does not change the database. `prune --apply` requires a new absolute backup path. It verifies the backup, locks maintenance, rechecks the same selection in an exclusive transaction, and refuses trees with live queues, active leases, unsettled effects, managed resource references, channel references, or step links from runs outside the tree. It deletes the safe aggregates, removes blobs with no remaining foreign-key reference, checkpoints the WAL, vacuums the file, and runs integrity and foreign-key checks. Pi Workflows never runs prune at startup.
|
|
277
279
|
|
|
278
280
|
## Alpha cutover
|
|
279
281
|
|
|
280
|
-
The persisted-state alpha boundary is a hard cut. Pi Workflows has no normal reader or writer for older live storage. It does not use dual reads, dual writes, aliases, versioned state roots, or automatic import. No direct live-state client, replay server reader, or Rust SQLite fallback remains outside the
|
|
282
|
+
The persisted-state alpha boundary is a hard cut. Pi Workflows has no normal reader or writer for older live storage. It does not use dual reads, dual writes, aliases, versioned state roots, or automatic import. No direct live-state client, replay server reader, or Rust SQLite fallback remains outside the server.
|
|
281
283
|
|
|
282
284
|
Older state remains untouched. Pi Workflows fails before mutation with this instruction: “Pi Workflows durable state is incompatible. Back up and move state.sqlite with its -wal and -shm files, then start Pi Workflows to create a new state.sqlite database. The incompatible state was not changed.”
|
|
@@ -320,7 +320,7 @@ A protected change to model choice, benchmark method, credentials, hardware, spe
|
|
|
320
320
|
|
|
321
321
|
## Compatibility
|
|
322
322
|
|
|
323
|
-
Workflows without inputs, exits, or includes run unchanged. Existing
|
|
323
|
+
Workflows without inputs, exits, or includes run unchanged. Existing resource manager child workflows remain unchanged. Existing terminal SQLite runs remain readable.
|
|
324
324
|
|
|
325
325
|
Composition and assistant-message output are compatible public API additions under the project's pre-1.0 policy.
|
|
326
326
|
|