@osolmaz/pi-workflows 0.13.4 → 0.14.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 +136 -118
- package/dist/controllers/index.d.ts +1 -1
- package/dist/controllers/index.js.map +1 -1
- package/dist/controllers/sqlite.d.ts +34 -31
- package/dist/controllers/sqlite.js +116 -77
- package/dist/controllers/sqlite.js.map +1 -1
- package/dist/extension/index.js +721 -202
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/restart-policy.d.ts +38 -0
- package/dist/extension/restart-policy.js +116 -0
- package/dist/extension/restart-policy.js.map +1 -0
- package/dist/extension/terminal-decision.d.ts +51 -0
- package/dist/extension/terminal-decision.js +110 -0
- package/dist/extension/terminal-decision.js.map +1 -0
- package/dist/state/prune.js +36 -10
- package/dist/state/prune.js.map +1 -1
- package/dist/workflows/tool-input.d.ts +4 -0
- package/dist/workflows/tool-input.js +6 -1
- package/dist/workflows/tool-input.js.map +1 -1
- package/docs/2026-08-25-workflow-follow-ups.md +8 -6
- package/docs/DEFERRED_TURNS.md +39 -26
- package/docs/HUMAN_DECISIONS.md +12 -4
- package/docs/SQLITE_STATE.md +24 -0
- package/docs/plans/2026-08-19-human-decision-gates-plan.md +34 -8
- package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +357 -0
- package/docs/workflows.md +85 -29
- 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/controllers/index.ts +3 -0
- package/src/controllers/sqlite.ts +226 -155
- package/src/extension/index.ts +881 -220
- package/src/extension/restart-policy.ts +163 -0
- package/src/extension/terminal-decision.ts +172 -0
- package/src/state/prune.ts +35 -9
- package/src/workflows/tool-input.ts +9 -1
package/docs/DEFERRED_TURNS.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Deferred workflow turns
|
|
2
2
|
|
|
3
|
-
This specification defines how Pi Workflows schedules one successor agent turn after a workflow event stops or strands the current turn. It covers cancellation, timeout,
|
|
3
|
+
This specification defines how Pi Workflows schedules one successor agent turn after a workflow event stops or strands the current turn. It covers every top-level interactive terminal result, cancellation, timeout, launch failure, controller interruption, and claim loss.
|
|
4
4
|
|
|
5
|
-
The implementation
|
|
5
|
+
The implementation plans are [Guarantee one successor turn after workflow interruption](plans/2026-08-21-deferred-turn-intents-plan.md) and [Workflow terminal decision and restart](plans/2026-08-27-workflow-terminal-restart-plan.md).
|
|
6
6
|
|
|
7
7
|
## Terms
|
|
8
8
|
|
|
@@ -18,10 +18,10 @@ The implementation plan is [Guarantee one successor turn after workflow interrup
|
|
|
18
18
|
Each eligible source event creates at most one turn intent. Exactly one of these message paths can resolve it:
|
|
19
19
|
|
|
20
20
|
1. a workflow agent prompt;
|
|
21
|
-
2. a
|
|
21
|
+
2. a result presentation;
|
|
22
22
|
3. a factual fallback.
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Every top-level interactive terminal run owns one intent. Its presentation and fallback claim that same intent before sending. A waiting presentation can resolve an earlier interruption intent, but waiting state does not create a terminal intent. A resolved intent cannot start another turn.
|
|
25
25
|
|
|
26
26
|
Cancellation and process termination remain immediate. Pi Workflows does not keep the old assistant turn alive and does not wait for the successor before stopping active work.
|
|
27
27
|
|
|
@@ -56,25 +56,25 @@ Resolution records message delivery into the Pi session or the presence of the s
|
|
|
56
56
|
Valid causes are:
|
|
57
57
|
|
|
58
58
|
```text
|
|
59
|
-
agentCancelled | timedOut | failed | launchFailed | controllerInterrupted | claimLost
|
|
59
|
+
agentCancelled | timedOut | failed | launchFailed | controllerInterrupted | claimLost | terminal | cancelled
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
The event policy is:
|
|
63
63
|
|
|
64
|
-
| Event | Intent
|
|
65
|
-
| -------------------------------------------------------------- |
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
| Workflow reports started, then crashes before its first prompt | Create after durable failure.
|
|
70
|
-
| Queued launch activation fails | Create after the queue row is durably failed.
|
|
71
|
-
| Controller interrupts an active workflow turn | Create before the turn abort when possible.
|
|
72
|
-
| Active workflow turn loses its queue claim | Create before the turn abort when possible.
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
| Workflow pause | Do not create.
|
|
76
|
-
| User Escape or held workflow | Do not create.
|
|
77
|
-
| Session shutdown | Do not create.
|
|
64
|
+
| Event | Intent | Fallback rule |
|
|
65
|
+
| -------------------------------------------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
66
|
+
| Top-level interactive run completes | Create one terminal intent. | Presentation and factual fallback compete for the intent. |
|
|
67
|
+
| Top-level interactive run fails or times out | Create or reuse the abort intent. | Make eligible after the terminal state is durable. |
|
|
68
|
+
| Agent or user cancels a top-level run | Create before turn abort when needed. | Make eligible after durable cancellation; the decision defaults to stopping. |
|
|
69
|
+
| Workflow reports started, then crashes before its first prompt | Create after durable failure. | Create as eligible. |
|
|
70
|
+
| Queued launch activation fails | Create after the queue row is durably failed. | Create as eligible. |
|
|
71
|
+
| Controller interrupts an active workflow turn | Create before the turn abort when possible. | Durable terminal or handoff state decides eligibility. |
|
|
72
|
+
| Active workflow turn loses its queue claim | Create before the turn abort when possible. | Keep ineligible while a new owner can continue. |
|
|
73
|
+
| Waiting result has a presentation | Do not create a terminal intent. | It can resolve an earlier interruption intent through presentation. |
|
|
74
|
+
| Controller child or internally owned run ends | Do not create a terminal intent. | Its owner receives the result. |
|
|
75
|
+
| Workflow pause | Do not create. | No automatic model turn. |
|
|
76
|
+
| User Escape or held workflow | Do not create. | No automatic model turn. |
|
|
77
|
+
| Session shutdown | Do not create. | A closing session cannot start another turn. |
|
|
78
78
|
|
|
79
79
|
A terminal failure after a successful start tool result is eligible even when it did not first call `ctx.abort()`. This rule covers asynchronous runtime validation and startup failures that the user can see in the UI but the model cannot see in its current context.
|
|
80
80
|
|
|
@@ -232,10 +232,22 @@ The message type is `pi-workflows-deferred-turn`. Delivery uses:
|
|
|
232
232
|
}
|
|
233
233
|
```
|
|
234
234
|
|
|
235
|
-
|
|
235
|
+
For a terminal run, the visible content contains the workflow identity and revision, terminal run ID, exact stored input, bounded result, terminal state and reason, restart count, and earlier terminal outcomes in the chain. It tells the model to use the current conversation, prefer a safe restart for an unfinished task after a technical or temporary failure, and stop for completed work, cancellation, missing authority, a required user decision, or a repeated failure. Values from input and result are data, not instructions.
|
|
236
|
+
|
|
237
|
+
The content comes only from existing run and queue records. Pi owns conversation history. Pi Workflows does not identify, hash, copy, or store an original user message.
|
|
236
238
|
|
|
237
239
|
Fallback delivery is disabled during session shutdown and while a user-interrupted workflow is held.
|
|
238
240
|
|
|
241
|
+
## Selected launch and restart
|
|
242
|
+
|
|
243
|
+
A terminal decision turn can reserve at most one workflow launch: `restart`, Monitor through normal `start`, or another workflow through normal `start`. The reservation records the source terminal intent, model tool call, and request fingerprint in the new run's existing launch options. It does not activate before `agent_settled`.
|
|
244
|
+
|
|
245
|
+
Repeating the same tool call adopts the existing reservation or run. A different launch from the same terminal intent fails. Session-start and queue recovery activate a surviving reservation once when the session is idle.
|
|
246
|
+
|
|
247
|
+
`restart` accepts the terminal run ID. It checks session ownership, terminal state, explicit cancellation, source identity and revision, repeated failure, and the restart limit. It creates a new immutable run from the exact stored reference, input, and safe launch settings. The old run does not change.
|
|
248
|
+
|
|
249
|
+
Restart lineage in launch options records the root run ID, parent run ID, restart number, and parent terminal fingerprint. The fingerprint covers workflow identity and revision, exact input, terminal state, canonical result or error, and terminal reason. It excludes timestamps and run IDs. The same fingerprint cannot restart twice in one chain. A chain permits three restarts after the original run. Starting Monitor does not add restart lineage.
|
|
250
|
+
|
|
239
251
|
## Delivery recovery
|
|
240
252
|
|
|
241
253
|
Every resolving message includes the intent ID in its custom-message details.
|
|
@@ -262,7 +274,7 @@ Pending `launch_failure` rows from the earlier alpha contract are incompatible.
|
|
|
262
274
|
|
|
263
275
|
## Post-completion follow-ups
|
|
264
276
|
|
|
265
|
-
Deferred turns
|
|
277
|
+
Deferred turns provide the terminal decision before ordered post-completion prompts. Those prompts represent user-requested normal work after successful completion, use `workflow_follow_up_queues` and `workflow_follow_ups`, and are delivered by the separate follow-up coordinator after the terminal intent is resolved. Neither feature reads or changes the other's rows.
|
|
266
278
|
|
|
267
279
|
See [Continue normal work after a workflow finishes](2026-08-25-workflow-follow-ups.md).
|
|
268
280
|
|
|
@@ -293,12 +305,13 @@ This is an alpha hard cutover.
|
|
|
293
305
|
|
|
294
306
|
An implementation conforms when:
|
|
295
307
|
|
|
296
|
-
-
|
|
297
|
-
-
|
|
298
|
-
- an agent self-cancel receives one fallback after settlement;
|
|
308
|
+
- every top-level interactive terminal run produces at most one intent and one decision message;
|
|
309
|
+
- an agent self-cancel and a direct cancellation receive one fallback after settlement;
|
|
299
310
|
- an asynchronous crash after a successful start result receives one fallback after settlement;
|
|
300
311
|
- a natural recovery prompt or presentation suppresses fallback by resolving the same intent;
|
|
301
|
-
-
|
|
312
|
+
- waiting checkpoints, controller children, internal owners, pause, Escape, user hold, and shutdown do not create terminal turns;
|
|
302
313
|
- claim transfer permits natural resolution by the new owner and prevents stale writes;
|
|
303
|
-
-
|
|
314
|
+
- one terminal turn reserves at most one launch and activates it after `agent_settled`;
|
|
315
|
+
- exact replay adopts the same launch, while reload, lease expiry, and send-before-resolution failure do not duplicate turns or runs;
|
|
316
|
+
- restart keeps the prior run immutable, preserves exact input, rejects cancellation and repeated fingerprints, and stops after three restarts;
|
|
304
317
|
- passive workflow notifications keep their current behavior.
|
package/docs/HUMAN_DECISIONS.md
CHANGED
|
@@ -322,19 +322,23 @@ Recovery follows these rules:
|
|
|
322
322
|
|
|
323
323
|
A required decision with no available channel remains waiting and reports the configuration problem. An automatic decision does not need a channel to apply its saved response after the deadline. A skipped plan policy creates no decision.
|
|
324
324
|
|
|
325
|
+
Direct answer handling and recovery can notice the same accepted decision at the same time. Both paths use one continuation coordinator. The coordinator prepares the deterministic continuation through the existing durable run queue. The first caller creates and claims the queue row. Only that caller receives the claim token and starts the engine. A compatible later caller adopts the existing row without starting another engine.
|
|
326
|
+
|
|
327
|
+
Adoption does not change the lease, claim generation, queue state, timestamps, or events. An adopter cannot renew, release, park, complete, or replace the winning claim. Reuse with a different workflow source, definition, input, launch options, parent, or owning session fails without changing the existing run. If the winning process stops after it claims the row, existing lease expiry and activation recovery can continue the prepared run.
|
|
328
|
+
|
|
325
329
|
## Compatibility
|
|
326
330
|
|
|
327
|
-
This alpha change updates the current request, accepted-result, receipt, resolution, continuation, and snapshot contracts in place. Old active runs refuse resume through normal source and definition identity checks. There is no compatibility reader, migration, dual path, or new schema generation. Updated viewers label a human decision as a checkpoint, show its deadline and automatic action when present, and keep the canonical subject separate. Private channel configuration and transport identifiers remain hidden.
|
|
331
|
+
This alpha change updates the current request, accepted-result, receipt, resolution, continuation, and snapshot contracts in place. Old active runs refuse resume through normal source and definition identity checks. There is no compatibility reader, migration, dual path, or new schema generation. The continuation startup fix uses existing queue and lease records. It adds no field, table, migration, or schema version. Existing compatible prepared or initialized continuations are adopted. Updated viewers label a human decision as a checkpoint, show its deadline and automatic action when present, and keep the canonical subject separate. Private channel configuration and transport identifiers remain hidden.
|
|
328
332
|
|
|
329
333
|
The engine remains independent from Pi and Telegram. Core code owns decision contracts, validation, durable acceptance, and continuation. The Pi extension owns UI and channel lifecycle. The Telegram adapter owns Bot API translation. Workflow definitions own only the question, choices, audience, and routes.
|
|
330
334
|
|
|
331
335
|
## Contract impact
|
|
332
336
|
|
|
333
337
|
- **Session state:** Pi records normal workflow messages and interactive decision results.
|
|
334
|
-
- **Other persistent data:** decision requests and resolutions can carry a deadline, automatic response, and resolution provenance in the existing decision store. The private channel index remains rebuildable.
|
|
338
|
+
- **Other persistent data:** decision requests and resolutions can carry a deadline, automatic response, and resolution provenance in the existing decision store. Continuation startup uses existing run, queue, source, binding, lease, event, continuation, and decision-effect records. It adds no new persistent shape. The private channel index remains rebuildable.
|
|
335
339
|
- **Pi internals:** none.
|
|
336
340
|
- **Public Pi API:** documented extension lifecycle and UI methods only.
|
|
337
|
-
- **Public pi-workflows API:** typed human choices, `humanDecision().onTimeout`, `humanDecisionEdge()`, the channel interface, the plan approval policy,
|
|
341
|
+
- **Public pi-workflows API:** typed human choices, `humanDecision().onTimeout`, `humanDecisionEdge()`, the channel interface, the plan approval policy, the shared plan-change workflow, and the additive queue prepare-or-adopt operation.
|
|
338
342
|
|
|
339
343
|
## Verification requirements
|
|
340
344
|
|
|
@@ -350,6 +354,10 @@ The implementation must test:
|
|
|
350
354
|
- unauthorized users and chats;
|
|
351
355
|
- stale request digests;
|
|
352
356
|
- concurrent Pi and Telegram answers;
|
|
357
|
+
- a direct verified answer racing recovery for the same accepted decision;
|
|
358
|
+
- one claim generation, one engine start, one continuation, and one execution of each continuation node;
|
|
359
|
+
- compatible continuation adoption without lease, queue, timestamp, or event mutation;
|
|
360
|
+
- incompatible continuation identity reuse without mutation;
|
|
353
361
|
- identical and conflicting retries;
|
|
354
362
|
- crashes before and after answer acceptance and continuation creation;
|
|
355
363
|
- ambiguous Telegram sends;
|
|
@@ -357,4 +365,4 @@ The implementation must test:
|
|
|
357
365
|
- decision cancellation and expiry;
|
|
358
366
|
- included `plan-approval` routes and bounded replan loops;
|
|
359
367
|
- viewer redaction; and
|
|
360
|
-
- real Pi execution without real Telegram credentials or network calls.
|
|
368
|
+
- real Pi execution without real Telegram credentials or network calls, duplicate-start failures, revision conflicts, or a stranded running continuation.
|
package/docs/SQLITE_STATE.md
CHANGED
|
@@ -111,6 +111,30 @@ The shared records do not replace domain schemas. The following `STRICT` tables
|
|
|
111
111
|
|
|
112
112
|
Foreign keys join projects, runs, attempts, decisions, controllers, effects, and channel records. Partial unique indexes enforce one active node attempt per run, one queued or running reservation per Pi session, one decision winner, and one deterministic effect key. A parked waiting parent does not block its continuation. Reserving that continuation settles the parked parent queue in the same transaction, so a failed reservation leaves the parent recoverable.
|
|
113
113
|
|
|
114
|
+
### Terminal restart state
|
|
115
|
+
|
|
116
|
+
The [terminal workflow restart contract](plans/2026-08-27-workflow-terminal-restart-plan.md)
|
|
117
|
+
uses the existing tables. It adds no state store or table. A restarted run keeps
|
|
118
|
+
its root run ID, parent run ID, restart number, and parent terminal fingerprint
|
|
119
|
+
in the existing launch-options value. The fingerprint uses the workflow
|
|
120
|
+
identity and revision, exact input, terminal state, canonical result or error,
|
|
121
|
+
and canonical reason. It excludes run IDs, timestamps, and other values that
|
|
122
|
+
change between equivalent attempts.
|
|
123
|
+
|
|
124
|
+
One terminal turn intent covers result presentation and factual fallback. A
|
|
125
|
+
selected restart, Monitor run, or other workflow start uses the existing
|
|
126
|
+
session reservation, run queue, and effect receipt. The successor run's launch
|
|
127
|
+
options record the source terminal run, intent, model tool call, and canonical
|
|
128
|
+
request fingerprint. A repeated call adopts that reservation or run. Activation
|
|
129
|
+
waits for `agent_settled`, and normal queue recovery activates a surviving
|
|
130
|
+
reservation once.
|
|
131
|
+
|
|
132
|
+
Restart creates a new run. The terminal run remains unchanged. Restart lineage
|
|
133
|
+
allows three restarts after the original run and rejects a repeated terminal
|
|
134
|
+
fingerprint in the same chain. A Monitor selection records terminal selection
|
|
135
|
+
but no restart lineage. Conversation history remains in Pi. This state does not
|
|
136
|
+
identify, hash, copy, or store an original user message.
|
|
137
|
+
|
|
114
138
|
## Content-addressed values
|
|
115
139
|
|
|
116
140
|
`blobs` stores canonical JSON and UTF-8 text as bytes. Its primary key is the 32-byte SHA-256 digest of the bytes.
|
|
@@ -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.
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Workflow terminal decision and restart plan
|
|
3
|
+
author: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
|
|
4
|
+
date: 2026-08-27
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Full plan
|
|
8
|
+
|
|
9
|
+
## Goal
|
|
10
|
+
|
|
11
|
+
After every top-level workflow run ends, give the model one normal successor turn.
|
|
12
|
+
|
|
13
|
+
That turn contains the workflow result and terminal reason. The model uses the conversation it already has to decide whether to:
|
|
14
|
+
|
|
15
|
+
- stop because the user’s task is complete
|
|
16
|
+
- restart the same workflow as a new run
|
|
17
|
+
- start Monitor for an authorized external wait
|
|
18
|
+
- ask the user for a required decision or authority
|
|
19
|
+
- take another safe authorized action
|
|
20
|
+
|
|
21
|
+
The system does not save or identify the original user message. It does not restart automatically. It gives the model one clear decision opportunity and makes safe retry the default for unfinished work after technical failures.
|
|
22
|
+
|
|
23
|
+
## Shared terminal behavior
|
|
24
|
+
|
|
25
|
+
The shared terminal message must include:
|
|
26
|
+
|
|
27
|
+
- workflow name and revision
|
|
28
|
+
- terminal run ID
|
|
29
|
+
- exact workflow input
|
|
30
|
+
- workflow result
|
|
31
|
+
- terminal state
|
|
32
|
+
- terminal reason
|
|
33
|
+
- restart count
|
|
34
|
+
- earlier terminal outcomes in the same restart chain
|
|
35
|
+
|
|
36
|
+
The message must tell the model:
|
|
37
|
+
|
|
38
|
+
> A workflow run ended, but that does not prove the user’s task is complete. Use the current conversation and this result to decide what to do next. If the task is unfinished because of an unexpected technical or temporary failure, prefer a safe restart. Stop if the work is complete, the user cancelled it, new authority is required, the user must make a decision, or the same failure has repeated. Use Monitor only for an authorized external wait.
|
|
39
|
+
|
|
40
|
+
Use the term **result**, not “durable result.”
|
|
41
|
+
|
|
42
|
+
## Implementation steps
|
|
43
|
+
|
|
44
|
+
### 1. Build one shared terminal-decision message
|
|
45
|
+
|
|
46
|
+
**Where**
|
|
47
|
+
|
|
48
|
+
- `src/extension/deferred-turn.ts`
|
|
49
|
+
- Add `src/extension/terminal-decision.ts` if a separate pure module keeps the code smaller.
|
|
50
|
+
|
|
51
|
+
**Change**
|
|
52
|
+
|
|
53
|
+
Add a pure builder that reads the existing run record and produces the shared terminal facts and prompt.
|
|
54
|
+
|
|
55
|
+
It must not read, copy, hash, or store an original user message.
|
|
56
|
+
|
|
57
|
+
Use the existing stored workflow input and result. Apply the existing output-size rules to large results, but always include the run ID, state, reason, and restart history.
|
|
58
|
+
|
|
59
|
+
**Verification**
|
|
60
|
+
|
|
61
|
+
Unit tests must cover completed, failed, timed-out, maxSteps, cancelled, and blocked results.
|
|
62
|
+
|
|
63
|
+
### 2. Create one terminal turn for every top-level run
|
|
64
|
+
|
|
65
|
+
**Where**
|
|
66
|
+
|
|
67
|
+
- `finishRun` in `src/extension/index.ts`
|
|
68
|
+
- `src/extension/deferred-turn-coordinator.ts`
|
|
69
|
+
- Existing turn-intent state in `src/controllers/sqlite.ts`
|
|
70
|
+
|
|
71
|
+
**Change**
|
|
72
|
+
|
|
73
|
+
Create one terminal turn intent when a top-level interactive run reaches a terminal state.
|
|
74
|
+
|
|
75
|
+
Normal presentation and fallback delivery must compete for the same intent. The first successful delivery settles it. Every later delivery attempt becomes a no-op.
|
|
76
|
+
|
|
77
|
+
Do not create this turn for:
|
|
78
|
+
|
|
79
|
+
- waiting checkpoints
|
|
80
|
+
- controller child runs
|
|
81
|
+
- internal helper runs that already report to an owner
|
|
82
|
+
|
|
83
|
+
**Verification**
|
|
84
|
+
|
|
85
|
+
Race tests must prove that presentation, fallback, reload recovery, and crash recovery produce one model turn, not two.
|
|
86
|
+
|
|
87
|
+
### 3. Replace the current terminal presentation instruction
|
|
88
|
+
|
|
89
|
+
**Where**
|
|
90
|
+
|
|
91
|
+
- `buildPresentationMessage`
|
|
92
|
+
- `buildDeferredTurnContent`
|
|
93
|
+
- Related presentation helpers in `src/extension/index.ts`
|
|
94
|
+
|
|
95
|
+
**Change**
|
|
96
|
+
|
|
97
|
+
Remove the current instruction that says the model must not call the workflow tool.
|
|
98
|
+
|
|
99
|
+
Replace it with the shared decision instructions. The terminal turn must permit one workflow launch selected by the model.
|
|
100
|
+
|
|
101
|
+
Completed runs still get a result turn. A workflow state of `completed` does not always mean the larger user task is complete. For example, the result can say that work is blocked.
|
|
102
|
+
|
|
103
|
+
Explicit human cancellation must default to stopping.
|
|
104
|
+
|
|
105
|
+
**Verification**
|
|
106
|
+
|
|
107
|
+
Tests must prove that the model can select restart or Monitor during the terminal turn and that ordinary completed work does not cause an automatic restart.
|
|
108
|
+
|
|
109
|
+
### 4. Add a generic restart action
|
|
110
|
+
|
|
111
|
+
**Where**
|
|
112
|
+
|
|
113
|
+
- `src/workflows/tool-input.ts`
|
|
114
|
+
- Workflow tool registration, schema, help text, and control switch under `src/workflows/`
|
|
115
|
+
- Restart handling in `src/extension/index.ts`
|
|
116
|
+
|
|
117
|
+
**Contract**
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"action": "restart",
|
|
122
|
+
"runId": "terminal-run-id"
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Change**
|
|
127
|
+
|
|
128
|
+
The action must:
|
|
129
|
+
|
|
130
|
+
1. Read the terminal run.
|
|
131
|
+
2. Confirm that it belongs to the current session.
|
|
132
|
+
3. Confirm that it is terminal.
|
|
133
|
+
4. Reuse the exact workflow reference, input, and safe launch settings.
|
|
134
|
+
5. Create a new immutable run.
|
|
135
|
+
6. Record the restart relationship.
|
|
136
|
+
7. Leave the old run unchanged.
|
|
137
|
+
|
|
138
|
+
Reject restart when:
|
|
139
|
+
|
|
140
|
+
- the run is active or waiting
|
|
141
|
+
- the run is unknown
|
|
142
|
+
- the run belongs to another session
|
|
143
|
+
- the run was explicitly cancelled
|
|
144
|
+
- the workflow source or revision is no longer available
|
|
145
|
+
- the restart limit was reached
|
|
146
|
+
- the same terminal failure already repeated
|
|
147
|
+
|
|
148
|
+
A later explicit user request can still use normal `start`.
|
|
149
|
+
|
|
150
|
+
**Verification**
|
|
151
|
+
|
|
152
|
+
Tool-schema and extension tests must prove exact input reuse, immutable old runs, session checks, source checks, and correct rejection behavior.
|
|
153
|
+
|
|
154
|
+
### 5. Permit one selected launch during the terminal turn
|
|
155
|
+
|
|
156
|
+
**Where**
|
|
157
|
+
|
|
158
|
+
- `queueToolLaunch`
|
|
159
|
+
- presentation tracking in `src/extension/index.ts`
|
|
160
|
+
- `agent_settled`
|
|
161
|
+
- existing queued-launch recovery
|
|
162
|
+
|
|
163
|
+
**Change**
|
|
164
|
+
|
|
165
|
+
The current presentation guard rejects workflow launches. Add one narrow exception for the active terminal-decision turn.
|
|
166
|
+
|
|
167
|
+
The model can reserve one of these:
|
|
168
|
+
|
|
169
|
+
- restart
|
|
170
|
+
- Monitor
|
|
171
|
+
- another workflow start
|
|
172
|
+
|
|
173
|
+
The reservation must not activate until the model turn settles. A second workflow launch from the same terminal turn must fail.
|
|
174
|
+
|
|
175
|
+
Other tool calls remain subject to their normal rules.
|
|
176
|
+
|
|
177
|
+
**Verification**
|
|
178
|
+
|
|
179
|
+
Tests must prove that:
|
|
180
|
+
|
|
181
|
+
- one launch can be reserved during presentation
|
|
182
|
+
- it starts only after `agent_settled`
|
|
183
|
+
- a second launch is rejected
|
|
184
|
+
- reload after reservation does not lose it
|
|
185
|
+
- crash recovery does not start it twice
|
|
186
|
+
|
|
187
|
+
### 6. Add bounded restart lineage
|
|
188
|
+
|
|
189
|
+
**Where**
|
|
190
|
+
|
|
191
|
+
- Add `src/extension/restart-policy.ts`
|
|
192
|
+
- Existing run launch-options JSON and accessors
|
|
193
|
+
- No new database table
|
|
194
|
+
|
|
195
|
+
**Change**
|
|
196
|
+
|
|
197
|
+
Store this information for restarted runs:
|
|
198
|
+
|
|
199
|
+
- root run ID
|
|
200
|
+
- parent run ID
|
|
201
|
+
- restart number
|
|
202
|
+
- parent terminal fingerprint
|
|
203
|
+
|
|
204
|
+
A terminal fingerprint is a stable hash of:
|
|
205
|
+
|
|
206
|
+
- workflow identity and revision
|
|
207
|
+
- exact input
|
|
208
|
+
- terminal state
|
|
209
|
+
- canonical result or error
|
|
210
|
+
- canonical terminal reason
|
|
211
|
+
|
|
212
|
+
Do not include timestamps or new run IDs in the fingerprint.
|
|
213
|
+
|
|
214
|
+
Allow at most three restart actions after the original run. This permits at most four runs in one chain.
|
|
215
|
+
|
|
216
|
+
If a terminal fingerprint occurs again in the same chain, reject another restart immediately. If the result changes because the workflow made progress, another restart can remain eligible until the total limit is reached.
|
|
217
|
+
|
|
218
|
+
Starting Monitor does not consume a restart.
|
|
219
|
+
|
|
220
|
+
**Verification**
|
|
221
|
+
|
|
222
|
+
Tests must cover:
|
|
223
|
+
|
|
224
|
+
- first technical retry
|
|
225
|
+
- progress followed by a different failure
|
|
226
|
+
- repeated identical maxSteps failure
|
|
227
|
+
- three-restart limit
|
|
228
|
+
- Monitor selection
|
|
229
|
+
- restart history after database reopen
|
|
230
|
+
|
|
231
|
+
### 7. Make restart reservation idempotent
|
|
232
|
+
|
|
233
|
+
**Where**
|
|
234
|
+
|
|
235
|
+
- Existing effect records
|
|
236
|
+
- Existing run queue and reservation code
|
|
237
|
+
- Terminal turn-intent settlement code
|
|
238
|
+
|
|
239
|
+
**Change**
|
|
240
|
+
|
|
241
|
+
Key the selected launch to the source terminal turn intent and tool call.
|
|
242
|
+
|
|
243
|
+
If the host repeats the same tool call after a crash or reload, return the existing reservation or new run instead of creating another one.
|
|
244
|
+
|
|
245
|
+
The terminal turn intent, launch reservation, and resulting run must have one inspectable chain.
|
|
246
|
+
|
|
247
|
+
**Verification**
|
|
248
|
+
|
|
249
|
+
Inject failures after:
|
|
250
|
+
|
|
251
|
+
- turn-intent claim
|
|
252
|
+
- launch reservation
|
|
253
|
+
- run creation
|
|
254
|
+
- terminal response settlement
|
|
255
|
+
|
|
256
|
+
After recovery, there must still be one terminal message and one successor run.
|
|
257
|
+
|
|
258
|
+
### 8. Document the contract
|
|
259
|
+
|
|
260
|
+
**Where**
|
|
261
|
+
|
|
262
|
+
- `docs/workflows.md`
|
|
263
|
+
- `docs/SQLITE_STATE.md`
|
|
264
|
+
- Workflow tool reference and examples
|
|
265
|
+
- Relevant README text
|
|
266
|
+
|
|
267
|
+
**Change**
|
|
268
|
+
|
|
269
|
+
Document:
|
|
270
|
+
|
|
271
|
+
- the shared terminal decision turn
|
|
272
|
+
- the `restart` action
|
|
273
|
+
- the difference between a workflow ending and the user’s task finishing
|
|
274
|
+
- retry defaults and limits
|
|
275
|
+
- Monitor selection
|
|
276
|
+
- explicit cancellation behavior
|
|
277
|
+
- top-level versus child-run behavior
|
|
278
|
+
- recovery and duplicate prevention
|
|
279
|
+
- that conversation context remains owned by Pi
|
|
280
|
+
- that Pi Workflows does not capture or persist an original user message
|
|
281
|
+
|
|
282
|
+
No workflow definition needs an opt-in or terminal restart step.
|
|
283
|
+
|
|
284
|
+
## Contract changes
|
|
285
|
+
|
|
286
|
+
- The workflow tool gains `restart`.
|
|
287
|
+
- Every top-level terminal run owns one terminal turn intent.
|
|
288
|
+
- Restart always creates a new run.
|
|
289
|
+
- Restart reuses the exact prior workflow input.
|
|
290
|
+
- The model makes the continuation decision from the current conversation.
|
|
291
|
+
- Restart is preferred, not forced, for unfinished work after technical or temporary failures.
|
|
292
|
+
- Explicit cancellation, missing authority, required user decisions, repeated failures, and completed work stop.
|
|
293
|
+
- Restart lineage uses existing run launch data.
|
|
294
|
+
- No new store, service, controller, or Pi API is added.
|
|
295
|
+
- No original-message provenance contract is added.
|
|
296
|
+
|
|
297
|
+
## Test plan
|
|
298
|
+
|
|
299
|
+
Add regression coverage for:
|
|
300
|
+
|
|
301
|
+
1. Successful completion produces one result turn and no automatic restart.
|
|
302
|
+
2. A blocked result from a completed workflow lets the model select restart.
|
|
303
|
+
3. Failed, timed-out, and maxSteps runs offer restart.
|
|
304
|
+
4. Explicit cancellation is not restartable through the shortcut.
|
|
305
|
+
5. Waiting checkpoints do not produce a terminal turn.
|
|
306
|
+
6. Controller child runs do not produce competing turns.
|
|
307
|
+
7. Presentation and fallback races produce one turn.
|
|
308
|
+
8. Restart uses the exact workflow reference and input.
|
|
309
|
+
9. Restart leaves the prior run unchanged.
|
|
310
|
+
10. Monitor starts through the normal start path.
|
|
311
|
+
11. A selected launch waits for `agent_settled`.
|
|
312
|
+
12. Reload and crash recovery do not duplicate turns or runs.
|
|
313
|
+
13. The same terminal failure cannot repeat indefinitely.
|
|
314
|
+
14. A chain cannot exceed three restarts.
|
|
315
|
+
15. No new code captures, hashes, or stores an original user message.
|
|
316
|
+
16. The maxSteps failure that caused this incident produces a terminal decision turn instead of silently ending the task.
|
|
317
|
+
|
|
318
|
+
Run the full repository checks:
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
npm run check
|
|
322
|
+
npm run test:e2e
|
|
323
|
+
npx slophammer-ts@latest dry .
|
|
324
|
+
npx slophammer-ts@latest check . --only ts.dependency-boundaries-required
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Main risks
|
|
328
|
+
|
|
329
|
+
- **The model restarts completed work.**
|
|
330
|
+
Make stopping the default for successful results and enforce restart limits.
|
|
331
|
+
|
|
332
|
+
- **A retry repeats external effects.**
|
|
333
|
+
Include the prior result and restart history so the model can inspect the current state before it retries.
|
|
334
|
+
|
|
335
|
+
- **Presentation and fallback both fire.**
|
|
336
|
+
Make both settle the same turn intent.
|
|
337
|
+
|
|
338
|
+
- **A launch starts while the terminal response is still active.**
|
|
339
|
+
Reserve it first and activate it after `agent_settled`.
|
|
340
|
+
|
|
341
|
+
- **A temporary outage causes a loop.**
|
|
342
|
+
Use Monitor for external waits, stop repeated fingerprints, and allow only three restarts.
|
|
343
|
+
|
|
344
|
+
## Boundaries
|
|
345
|
+
|
|
346
|
+
Do not:
|
|
347
|
+
|
|
348
|
+
- modify Pi core or private APIs
|
|
349
|
+
- store or identify an original user message
|
|
350
|
+
- add restart nodes to individual workflows
|
|
351
|
+
- modify Autoimplement, Monitor, or other workflow definitions
|
|
352
|
+
- create a new controller, database, service, or daemon
|
|
353
|
+
- bypass cancellation, checkpoints, reviews, CI, authority, or safety rules
|
|
354
|
+
- add compatibility shims or parallel state contracts
|
|
355
|
+
- release or deploy anything as part of this plan
|
|
356
|
+
|
|
357
|
+
This is the selected plan.
|