@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
|
@@ -1,146 +1,175 @@
|
|
|
1
1
|
# Live replay protocol
|
|
2
2
|
|
|
3
|
-
The Rust viewer (`tui/`) can
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
The
|
|
10
|
-
[SQLITE_STATE.md](SQLITE_STATE.md)) and never writes them. The protocol has no
|
|
11
|
-
authentication, so the server only accepts loopback bind addresses and refuses
|
|
12
|
-
to start on anything else; workflow state contains private data, and remote viewing
|
|
13
|
-
goes through an SSH tunnel. Handshakes that
|
|
14
|
-
carry an `Origin` header are rejected: browsers always send one, and a web
|
|
15
|
-
page must not be able to read workflow state by opening a WebSocket to localhost.
|
|
16
|
-
|
|
17
|
-
## Transport and framing
|
|
18
|
-
|
|
19
|
-
A single WebSocket endpoint (`/ws`). Every message is one JSON object with a
|
|
20
|
-
`type` field. Unknown message types and unknown fields must be ignored by both
|
|
21
|
-
sides. The server sends `hello` on connect; a client that does not recognize
|
|
22
|
-
the protocol id must disconnect.
|
|
3
|
+
The Rust viewer (`tui/`) can read SQLite directly or connect to `piw serve`. Both modes use the same bounded viewer projection. The protocol ID is `pi-workflows.replay.v1`.
|
|
4
|
+
|
|
5
|
+
The server reads SQLite and never writes it. It accepts loopback addresses only. Remote use goes through an SSH tunnel. The server rejects WebSocket handshakes with an `Origin` header so a web page cannot read workflow state from localhost.
|
|
6
|
+
|
|
7
|
+
## Framing
|
|
8
|
+
|
|
9
|
+
The endpoint is `/ws`. Each message is one JSON object with a `type` field. Unknown message types and fields are ignored. The server sends `hello` first. A client disconnects when it does not support the protocol ID.
|
|
23
10
|
|
|
24
11
|
```json
|
|
25
12
|
{ "type": "hello", "protocol": "pi-workflows.replay.v1" }
|
|
26
13
|
```
|
|
27
14
|
|
|
28
|
-
##
|
|
15
|
+
## Bounded run view
|
|
29
16
|
|
|
30
|
-
|
|
31
|
-
run:
|
|
17
|
+
A snapshot contains one bounded run view:
|
|
32
18
|
|
|
33
19
|
```json
|
|
34
20
|
{
|
|
21
|
+
"presentationRevision": 42,
|
|
22
|
+
"graphRevision": 17,
|
|
35
23
|
"manifest": { … },
|
|
36
24
|
"workflow": { … },
|
|
37
|
-
"
|
|
38
|
-
|
|
25
|
+
"graphScene": {
|
|
26
|
+
"ranks": [ … ],
|
|
27
|
+
"edges": [ … ],
|
|
28
|
+
"segments": [ … ],
|
|
29
|
+
"rankOfNode": { … }
|
|
30
|
+
},
|
|
31
|
+
"graphSteps": [ … ],
|
|
32
|
+
"takenTransitions": [ "prepare->run" ],
|
|
33
|
+
"stepStart": 768,
|
|
34
|
+
"stepTotal": 1000,
|
|
35
|
+
"state": {
|
|
36
|
+
"steps": [ … ]
|
|
37
|
+
},
|
|
38
|
+
"tracePage": {
|
|
39
|
+
"presentationRevision": 42,
|
|
40
|
+
"start": 768,
|
|
41
|
+
"total": 1000,
|
|
42
|
+
"items": [ … ]
|
|
43
|
+
},
|
|
39
44
|
"session": {
|
|
45
|
+
"presentationRevision": 42,
|
|
40
46
|
"binding": { … },
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
"entryPage": {
|
|
48
|
+
"presentationRevision": 42,
|
|
49
|
+
"start": 768,
|
|
50
|
+
"total": 1000,
|
|
51
|
+
"items": [ … ]
|
|
52
|
+
},
|
|
53
|
+
"eventPage": {
|
|
54
|
+
"presentationRevision": 42,
|
|
55
|
+
"start": 768,
|
|
56
|
+
"total": 1000,
|
|
57
|
+
"items": [ … ]
|
|
58
|
+
},
|
|
45
59
|
"capture": { … }
|
|
46
60
|
},
|
|
61
|
+
"settingsScopes": [ … ],
|
|
62
|
+
"followUpQueue": { … },
|
|
47
63
|
"live": true,
|
|
48
64
|
"possiblyInterrupted": false
|
|
49
65
|
}
|
|
50
66
|
```
|
|
51
67
|
|
|
52
|
-
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
-
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
Each step, trace, session-entry, and session-event page contains at most 256 rows. `graphSteps` contains at most one latest attempt per node at the replay cursor. `takenTransitions` contains distinct transitions up to that cursor. `graphScene` is the retained language-neutral rank and route plan shared by Rust and TypeScript.
|
|
69
|
+
|
|
70
|
+
A snapshot does not contain complete trace or session history. A replay jump fetches the page that contains the requested zero-based cursor.
|
|
71
|
+
|
|
72
|
+
## Revisions and target patches
|
|
73
|
+
|
|
74
|
+
Each viewer-visible SQLite transaction advances the run presentation revision and commits ordered target patches with the same transaction. The server reads those patches. It does not build complete old and new run views to compare them.
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"type": "run_patch",
|
|
79
|
+
"runId": "run-1",
|
|
80
|
+
"revision": 43,
|
|
81
|
+
"targets": [
|
|
82
|
+
{
|
|
83
|
+
"targetType": "conversation",
|
|
84
|
+
"targetKey": "entries:tail",
|
|
85
|
+
"patch": [
|
|
86
|
+
{ "op": "replace", "path": "/presentationRevision", "value": 43 },
|
|
87
|
+
{ "op": "remove", "path": "/items/0" },
|
|
88
|
+
{ "op": "append", "path": "/items", "value": [ { "seq": 1001, … } ] },
|
|
89
|
+
{ "op": "replace", "path": "/start", "value": 745 },
|
|
90
|
+
{ "op": "replace", "path": "/total", "value": 1001 }
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The patch set supports `add`, `replace`, `remove`, and `append`. `append` adds each value to the target array in order. Sliding tail pages keep 256 rows by removing old leading rows when necessary. Session-event pages stay aligned to 256-event checkpoint boundaries. They append inside one block and request the next page when a write crosses a boundary.
|
|
98
|
+
|
|
99
|
+
A patch targets one bounded document or page. A client applies a tail patch only when it holds that tail page. Older loaded pages stay valid because committed history is immutable. A target that needs a fresh bounded projection causes a snapshot. This still avoids complete-run reads and complete-run JSON comparison.
|
|
100
|
+
|
|
101
|
+
Revisions must arrive in order. Duplicate state is harmless because a client ignores an old revision. A wrong run, malformed patch, missing path, stale page, future revision, or gap cannot replace the last good view. A gap or a cursor older than retained patches causes a bounded snapshot.
|
|
102
|
+
|
|
103
|
+
The database retains 256 presentation revisions per run. The server does not replay an unbounded patch backlog.
|
|
61
104
|
|
|
62
|
-
|
|
63
|
-
scrubbing is a pure client-side operation; rewinding never requires the
|
|
64
|
-
server. Clients order session events by `seq` and use `at` only for playback
|
|
65
|
-
timing.
|
|
105
|
+
## Pages
|
|
66
106
|
|
|
67
|
-
|
|
107
|
+
A client asks for a page with `fetch_page`:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"type": "fetch_page",
|
|
112
|
+
"runId": "run-1",
|
|
113
|
+
"kind": "session_events",
|
|
114
|
+
"cursor": 20000
|
|
115
|
+
}
|
|
116
|
+
```
|
|
68
117
|
|
|
69
|
-
|
|
70
|
-
subscribes to a run, the server sends one `run_snapshot`, then a stream of
|
|
71
|
-
`run_patch` messages:
|
|
118
|
+
`kind` is one of `steps`, `trace`, `trace_at_step`, `session_entries`, `session_events`, `settings`, `follow_ups`, or `updates`. `trace_at_step` uses a step index as its cursor and returns the trace page around that step's timestamp. The server answers with `run_page`:
|
|
72
119
|
|
|
73
120
|
```json
|
|
74
|
-
{
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
121
|
+
{
|
|
122
|
+
"type": "run_page",
|
|
123
|
+
"runId": "run-1",
|
|
124
|
+
"revision": 43,
|
|
125
|
+
"kind": "session_events",
|
|
126
|
+
"cursor": 20000,
|
|
127
|
+
"start": 19872,
|
|
128
|
+
"total": 48620,
|
|
129
|
+
"items": [ … ]
|
|
130
|
+
}
|
|
79
131
|
```
|
|
80
132
|
|
|
81
|
-
|
|
82
|
-
must resubscribe and take a fresh snapshot.
|
|
83
|
-
- `patch` is JSON Patch (RFC 6902) plus one extension op: `append`, whose
|
|
84
|
-
`value` is an array of items appended to the array at `path`. Semantically
|
|
85
|
-
`append` equals a sequence of `add` ops at `/-`; it exists so that the
|
|
86
|
-
common case (trace and session growth) stays compact and readable.
|
|
87
|
-
- Session growth uses `append` at `/session/entries` and `/session/events`.
|
|
88
|
-
Capture changes use `replace` at `/session/capture`. Changes to the derived
|
|
89
|
-
tail diagnostics use `replace` at `/session/eventsMalformed` and
|
|
90
|
-
`/session/eventsTornTail`.
|
|
91
|
-
- The server waits 50 ms after a filesystem notification before refreshing,
|
|
92
|
-
so one token burst normally becomes one revision. Batch boundaries never
|
|
93
|
-
merge or alter event records.
|
|
94
|
-
- Patches are computed against the previous view revision; applying them in
|
|
95
|
-
order reproduces the server's view exactly.
|
|
133
|
+
Page reads use bounded ranges. The response echoes the requested `cursor` and carries the presentation revision read in the same SQLite snapshot as its rows. A client ignores an older response when a newer cursor is pending. A step page also returns `graphCursor`, `graphSteps`, and `takenTransitions` for that exact replay point, even when the selected step was already in the prior page. A historical session-event page can return `replayCheckpoint`. The checkpoint contains only active message and tool state at the page boundary. The writer stores it at each 256-event boundary, so a page jump reads one checkpoint blob instead of predecessor event rows. It lets the client continue the temporal reducer without loading predecessor event pages. The client also requests the related entry page. Settings, follow-up, and current-update pages keep the Info inspector complete without loading every record. A page request does not change the shared watched-run projection or another client's cursor.
|
|
96
134
|
|
|
97
135
|
## Messages
|
|
98
136
|
|
|
99
137
|
Client to server:
|
|
100
138
|
|
|
101
|
-
|
|
|
102
|
-
| ---------------- |
|
|
103
|
-
| `watch_runs` |
|
|
104
|
-
| `watch_run` | `runId`
|
|
105
|
-
| `unwatch_run` | `runId`
|
|
106
|
-
| `
|
|
139
|
+
| Type | Fields | Meaning |
|
|
140
|
+
| ---------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
|
|
141
|
+
| `watch_runs` | none | Subscribe to run-list rows. |
|
|
142
|
+
| `watch_run` | `runId`, optional `revision`, `stepCursor`, `traceCursor`, `sessionEntryCursor`, `sessionEventCursor` | Subscribe or resume one run. |
|
|
143
|
+
| `unwatch_run` | `runId` | End one run subscription. |
|
|
144
|
+
| `fetch_page` | `runId`, `kind`, `cursor` | Read one bounded page. |
|
|
145
|
+
| `fetch_artifact` | `runId`, `path` | Unsupported for SQLite state; returns `error`. |
|
|
107
146
|
|
|
108
147
|
Server to client:
|
|
109
148
|
|
|
110
|
-
|
|
|
111
|
-
| -------------- |
|
|
112
|
-
| `hello` | `protocol`
|
|
113
|
-
| `runs` | `runs`
|
|
114
|
-
| `run_snapshot` | `runId`, `revision`, `view`
|
|
115
|
-
| `run_patch` | `runId`, `revision`, `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
149
|
+
| Type | Fields | Meaning |
|
|
150
|
+
| -------------- | ------------------------------------------------------ | ---------------------------------------- |
|
|
151
|
+
| `hello` | `protocol` | Identify the protocol. |
|
|
152
|
+
| `runs` | `runs` | Send all lightweight run-list rows. |
|
|
153
|
+
| `run_snapshot` | `runId`, `revision`, `view` | Send one bounded run view. |
|
|
154
|
+
| `run_patch` | `runId`, `revision`, `targets` | Apply direct bounded target patches. |
|
|
155
|
+
| `run_page` | `runId`, `revision`, `kind`, `start`, `total`, `items` | Return one bounded page. |
|
|
156
|
+
| `artifact` | `runId`, `path`, `content` | Reserved and not sent by SQLite servers. |
|
|
157
|
+
| `error` | `message`, optional `runId` | Report a sanitized request failure. |
|
|
118
158
|
|
|
119
|
-
|
|
120
|
-
an `error` because there is no artifact directory.
|
|
159
|
+
The run list contains `presentationRevision`, `manifest`, `live`, and `possiblyInterrupted`. It contains no payload bodies.
|
|
121
160
|
|
|
122
|
-
|
|
123
|
-
`possiblyInterrupted`:
|
|
161
|
+
## Several clients
|
|
124
162
|
|
|
125
|
-
|
|
126
|
-
{ "type": "runs", "runs": [ { "manifest": { … }, "live": true, "possiblyInterrupted": false } ] }
|
|
127
|
-
```
|
|
163
|
+
The server keeps one projection and graph scene for each watched run. The first watcher loads it. Later watchers reuse it. The last unwatch or disconnect releases it. Different watched runs load independently.
|
|
128
164
|
|
|
129
|
-
|
|
130
|
-
run views use patches.
|
|
165
|
+
Each client keeps its own revision and page cursors. Network sends happen outside the shared state lock. A slow client cannot stop another client. If a broadcast receiver falls behind, that client receives a bounded snapshot.
|
|
131
166
|
|
|
132
167
|
## Reconnection
|
|
133
168
|
|
|
134
|
-
The
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
bounded backoff, sends `watch_runs` after the next valid `hello`, and restores
|
|
138
|
-
the current `watch_run`. A reconnect receives a fresh snapshot before later
|
|
139
|
-
patches.
|
|
169
|
+
The client keeps the run list and selected run as desired state. After a disconnect, it keeps cached content visible with a stale or reconnecting label. It retries with bounded backoff, sends `watch_runs` after the next valid `hello`, and resumes `watch_run` from its revision and page cursors. A retained cursor receives patches. A stale cursor receives a bounded snapshot and requested pages.
|
|
170
|
+
|
|
171
|
+
## SQLite consistency
|
|
140
172
|
|
|
141
|
-
|
|
173
|
+
The server uses a query-only SQLite connection. A writer commits the domain change, presentation revision, and patch records atomically. A reader sees all of that transaction or none of it.
|
|
142
174
|
|
|
143
|
-
The
|
|
144
|
-
reads a committed run projection, immutable events, session rows, and blob
|
|
145
|
-
values. A transaction is either fully visible or not visible, so a client never
|
|
146
|
-
observes half of a state transition.
|
|
175
|
+
The refresh timer first checks `PRAGMA data_version`. An unchanged value causes no run-index query and no payload read. A changed value refreshes lightweight rows and only the watched projections whose presentation revisions changed.
|
|
@@ -52,6 +52,11 @@ Pi and Telegram implement one channel interface. Workflows address a named audie
|
|
|
52
52
|
- Accept the first valid response with a no-replace write.
|
|
53
53
|
- Adopt identical retries and reject conflicting responses.
|
|
54
54
|
- Derive one continuation identity from the accepted decision.
|
|
55
|
+
- Prepare that continuation through one atomic queue operation.
|
|
56
|
+
- Let the first caller create and claim the queue row.
|
|
57
|
+
- Let compatible concurrent or repeated callers adopt the row without changing its lease, claim generation, queue state, timestamps, or events.
|
|
58
|
+
- Reject incompatible workflow source, definition, input, launch options, parent, or owning session without changing the existing row.
|
|
59
|
+
- Give only the winning caller a claim token and permission to start the engine.
|
|
55
60
|
- Reject stale responses by decision ID and canonical request digest.
|
|
56
61
|
- Rebuild the pending-decision index from immutable records.
|
|
57
62
|
|
|
@@ -83,6 +88,10 @@ Pi and Telegram implement one channel interface. Workflows address a named audie
|
|
|
83
88
|
- Promise exactly-once Telegram message creation after an ambiguous Bot API response.
|
|
84
89
|
- Add arbitrary forms in the first release. Choice buttons and one text input cover the required flows.
|
|
85
90
|
- Change existing checkpoints or historical run bundles.
|
|
91
|
+
- Add a database field, table, migration, schema version, compatibility reader, or dual-write path for continuation startup.
|
|
92
|
+
- Coordinate continuation startup with a process-local promise map, timing guard, retry loop, or swallowed error.
|
|
93
|
+
- Change general queue claim behavior for unrelated launch paths.
|
|
94
|
+
- Repair an already stranded live continuation as part of this change.
|
|
86
95
|
- Enable human approval by default in existing built-in workflows.
|
|
87
96
|
|
|
88
97
|
## Design decisions
|
|
@@ -107,7 +116,13 @@ The Telegram adapter accepts replan text only as a reply to the exact `ForceRepl
|
|
|
107
116
|
|
|
108
117
|
### Make answer acceptance exact
|
|
109
118
|
|
|
110
|
-
Channel delivery can be retried or fail independently. Decision acceptance is one atomic no-replace operation. The accepted response and deterministic continuation identity prevent two channels from
|
|
119
|
+
Channel delivery can be retried or fail independently. Decision acceptance is one atomic no-replace operation. The accepted response and deterministic continuation identity prevent two channels from selecting different continuations.
|
|
120
|
+
|
|
121
|
+
### Prepare or adopt one continuation
|
|
122
|
+
|
|
123
|
+
Direct answer handling and periodic recovery can both see the accepted decision. They use one continuation coordinator and one durable queue operation. The operation creates and claims a missing queue row or returns a compatible existing row as adopted. Only a newly claimed preparation can call `WorkflowEngine.continueRun()`.
|
|
124
|
+
|
|
125
|
+
An adopted result is normal success. It does not start another engine or change the winning lease, claim generation, queue state, timestamps, or events. The adopter receives no claim token, so it cannot renew, release, park, complete, or replace the winning claim. Incompatible reuse fails without mutation. Existing cleanup handles failure before run initialization, and existing lease expiry and activation recovery handle a winning process that stops after preparation.
|
|
111
126
|
|
|
112
127
|
### Handle Telegram delivery limits honestly
|
|
113
128
|
|
|
@@ -171,10 +186,17 @@ A private SQLite index may track pending decisions, channel leases, Telegram upd
|
|
|
171
186
|
- Validate the accepted response through the node contract.
|
|
172
187
|
- Preserve the parent's original workflow input.
|
|
173
188
|
- Expose the response as the checkpoint output.
|
|
174
|
-
-
|
|
175
|
-
-
|
|
176
|
-
|
|
177
|
-
|
|
189
|
+
- Derive the deterministic continuation identity.
|
|
190
|
+
- Add a typed atomic prepare-or-adopt operation to the existing SQLite queue store.
|
|
191
|
+
- Compare the stored workflow source, definition digest, input, launch options, parent, and owning session before adoption.
|
|
192
|
+
- Build one prepared continuation value in the Pi extension.
|
|
193
|
+
- Route direct verified answers and recovery through one continuation coordinator.
|
|
194
|
+
- Start `WorkflowEngine.continueRun()` only with the new claim token.
|
|
195
|
+
- Return normal started or already-continuing success to the answer path.
|
|
196
|
+
- Keep enqueue and lease behavior unchanged for other launch paths.
|
|
197
|
+
- Leave legacy checkpoint continuation unchanged.
|
|
198
|
+
|
|
199
|
+
Test crashes before acceptance, after acceptance, during continuation creation, and after continuation completion. Add a focused race test for direct answer handling and recovery. It must prove one claim generation, one engine start, one continuation, one execution of each continuation node, coherent queue and run state, and no duplicate-start or revision-conflict failure. Add a real-Pi version of the same regression with a durable barrier instead of sleep-only timing.
|
|
178
200
|
|
|
179
201
|
### Channel management
|
|
180
202
|
|
|
@@ -248,7 +270,9 @@ The setup command uses an existing mode-`0600` token file instead of collecting
|
|
|
248
270
|
- The workflow tool cannot answer a protected human decision.
|
|
249
271
|
- Pi and Telegram can receive the same decision through one audience profile.
|
|
250
272
|
- The first concurrent valid answer wins and creates one continuation.
|
|
251
|
-
-
|
|
273
|
+
- Direct answer handling and recovery can race without starting it twice or replacing its lease.
|
|
274
|
+
- The first queue preparation starts the engine, and a compatible repeat adopts it without mutation.
|
|
275
|
+
- An incompatible continuation identity, conflicting answer, or stale answer is rejected without mutation.
|
|
252
276
|
- The original workflow input survives a human-decision continuation.
|
|
253
277
|
- Old checkpoints and old bundles pass their existing tests unchanged.
|
|
254
278
|
- The Telegram adapter accepts text only from the bound reply and approved numeric actor.
|
|
@@ -267,6 +291,8 @@ npx vitest run test/human-decision-api.test.ts test/human-decision-store.test.ts
|
|
|
267
291
|
npx vitest run test/human-decision-engine.test.ts test/run-resume.test.ts
|
|
268
292
|
npx vitest run test/pi-decision-channel.test.ts test/telegram-decision-channel.test.ts
|
|
269
293
|
npx vitest run test/plan-approval.test.ts test/composition.test.ts
|
|
294
|
+
npx vitest run test/run-queue.test.ts test/extension.test.ts
|
|
295
|
+
npx vitest run --config vitest.e2e.config.ts test/e2e/workflow.e2e.test.ts
|
|
270
296
|
```
|
|
271
297
|
|
|
272
298
|
Run all repository gates before review:
|
|
@@ -290,7 +316,7 @@ This work adds compatible public APIs and additive persisted records. Release it
|
|
|
290
316
|
## Contract impact
|
|
291
317
|
|
|
292
318
|
- **Session state:** normal workflow messages and interactive decision results.
|
|
293
|
-
- **Other persistent data:** additive decision records, a rebuildable private channel index,
|
|
319
|
+
- **Other persistent data:** additive decision records, a rebuildable private channel index, private channel configuration, and existing run queue and lease records. The continuation startup fix adds no persistent field, table, migration, or schema version.
|
|
294
320
|
- **Pi internals:** none.
|
|
295
321
|
- **Public Pi API:** documented extension lifecycle plus command and UI methods only.
|
|
296
|
-
- **Public pi-workflows API:** typed human choices, `humanDecision()`, `humanDecisionEdge()`, the channel interface,
|
|
322
|
+
- **Public pi-workflows API:** typed human choices, `humanDecision()`, `humanDecisionEdge()`, the channel interface, the `plan-approval` workflow, and an additive queue prepare-or-adopt operation.
|