@osolmaz/pi-workflows 0.12.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/builtins/autoimplement.workflow.d.ts +485 -126
- package/dist/builtins/autoimplement.workflow.js +17 -105
- package/dist/builtins/autoimplement.workflow.js.map +1 -1
- package/dist/builtins/catalog.js +4 -4
- package/dist/builtins/index.d.ts +2 -1
- package/dist/builtins/index.js +2 -1
- package/dist/builtins/index.js.map +1 -1
- package/dist/builtins/monitor.workflow.d.ts +2 -4
- package/dist/builtins/monitor.workflow.js +26 -128
- package/dist/builtins/monitor.workflow.js.map +1 -1
- package/dist/builtins/pi-agent-group.d.ts +72 -0
- package/dist/builtins/pi-agent-group.js +1087 -0
- package/dist/builtins/pi-agent-group.js.map +1 -0
- package/dist/builtins/plan-approval.workflow.d.ts +39 -5
- package/dist/builtins/plan-approval.workflow.js +92 -14
- package/dist/builtins/plan-approval.workflow.js.map +1 -1
- package/dist/builtins/plan-change.workflow.d.ts +301 -0
- package/dist/builtins/plan-change.workflow.js +256 -0
- package/dist/builtins/plan-change.workflow.js.map +1 -0
- package/dist/builtins/plan-presentation.js +2 -2
- package/dist/builtins/plan-presentation.js.map +1 -1
- package/dist/builtins/sanity-check.workflow.d.ts +5 -3
- package/dist/builtins/sanity-check.workflow.js +105 -21
- package/dist/builtins/sanity-check.workflow.js.map +1 -1
- package/dist/extension/decision-channels.d.ts +2 -2
- package/dist/extension/decision-channels.js +22 -28
- package/dist/extension/decision-channels.js.map +1 -1
- package/dist/extension/index.js +62 -33
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/session-events.d.ts +2 -2
- package/dist/extension/widget.js +23 -3
- package/dist/extension/widget.js.map +1 -1
- package/dist/render/graph-render.js +1 -2
- package/dist/render/graph-render.js.map +1 -1
- package/dist/viewer/render.js +7 -6
- package/dist/viewer/render.js.map +1 -1
- package/dist/workflows/catalog.js +7 -2
- package/dist/workflows/catalog.js.map +1 -1
- package/dist/workflows/composition.js +8 -0
- package/dist/workflows/composition.js.map +1 -1
- package/dist/workflows/decision-presentation.d.ts +1 -1
- package/dist/workflows/decision-presentation.js +51 -38
- package/dist/workflows/decision-presentation.js.map +1 -1
- package/dist/workflows/engine.d.ts +2 -2
- package/dist/workflows/engine.js +14 -13
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/errors.d.ts +13 -0
- package/dist/workflows/errors.js +15 -0
- package/dist/workflows/errors.js.map +1 -1
- package/dist/workflows/human-decision.d.ts +16 -4
- package/dist/workflows/human-decision.js +175 -72
- package/dist/workflows/human-decision.js.map +1 -1
- package/dist/workflows/index.d.ts +2 -2
- package/dist/workflows/index.js +1 -1
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/progress.d.ts +1 -0
- package/dist/workflows/progress.js +15 -3
- package/dist/workflows/progress.js.map +1 -1
- package/dist/workflows/schema.js +10 -0
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/store.js +5 -0
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/types.d.ts +33 -45
- package/docs/HUMAN_DECISIONS.md +25 -35
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +14 -24
- package/docs/MONITOR.md +5 -11
- package/docs/WORKFLOW_COMPOSITION.md +8 -7
- package/docs/plans/2026-08-21-plan-change-approval-policy-plan.md +322 -0
- package/docs/plans/2026-08-21-sanity-check-plan.md +202 -94
- package/docs/run-bundles.md +6 -6
- package/docs/workflows.md +26 -6
- package/examples/workflows/approved-plan.workflow.ts +19 -46
- package/herdr-plugin.toml +1 -1
- package/package.json +7 -7
- package/schemas/human-decision-accepted-v1.schema.json +15 -3
- package/schemas/human-decision-continuation-v1.schema.json +10 -1
- package/schemas/human-decision-delivery-v1.schema.json +8 -0
- package/schemas/human-decision-receipt-v1.schema.json +8 -0
- package/schemas/human-decision-request-v1.schema.json +24 -4
- package/skills/autoimplement/SKILL.md +27 -0
- package/skills/monitor/SKILL.md +31 -3
- package/skills/pi-workflows/SKILL.md +2 -1
- package/skills/sanity-check/SKILL.md +44 -0
- package/src/builtins/autoimplement.workflow.ts +19 -118
- package/src/builtins/catalog.ts +4 -4
- package/src/builtins/index.ts +11 -0
- package/src/builtins/monitor.workflow.ts +27 -150
- package/src/builtins/pi-agent-group.ts +1407 -0
- package/src/builtins/plan-approval.workflow.ts +157 -24
- package/src/builtins/plan-change.workflow.ts +321 -0
- package/src/builtins/plan-presentation.ts +2 -2
- package/src/builtins/sanity-check.workflow.ts +186 -41
- package/src/extension/decision-channels.ts +29 -59
- package/src/extension/index.ts +79 -41
- package/src/extension/session-events.ts +2 -2
- package/src/extension/widget.ts +24 -5
- package/src/render/graph-render.ts +1 -2
- package/src/viewer/render.ts +7 -6
- package/src/workflows/catalog.ts +7 -2
- package/src/workflows/composition.ts +9 -0
- package/src/workflows/decision-presentation.ts +56 -43
- package/src/workflows/engine.ts +17 -15
- package/src/workflows/errors.ts +24 -0
- package/src/workflows/human-decision.ts +218 -101
- package/src/workflows/index.ts +5 -11
- package/src/workflows/progress.ts +18 -3
- package/src/workflows/schema.ts +17 -0
- package/src/workflows/store.ts +5 -0
- package/src/workflows/types.ts +39 -56
- package/dist/builtins/sanity-check-session.d.ts +0 -17
- package/dist/builtins/sanity-check-session.js +0 -168
- package/dist/builtins/sanity-check-session.js.map +0 -1
- package/schemas/human-decision-accepted-v2.schema.json +0 -50
- package/schemas/human-decision-delivery-v2.schema.json +0 -36
- package/schemas/human-decision-receipt-v2.schema.json +0 -39
- package/schemas/human-decision-request-v2.schema.json +0 -69
- package/schemas/human-decision-resolution-v2.schema.json +0 -27
- package/src/builtins/sanity-check-session.ts +0 -205
|
@@ -1,28 +1,48 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
2
|
+
title: Run Sanity Check with Provider Extensions
|
|
3
3
|
author: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
|
|
4
4
|
date: 2026-08-21
|
|
5
|
+
updated: 2026-08-22
|
|
5
6
|
---
|
|
6
7
|
|
|
7
|
-
#
|
|
8
|
+
# Run Sanity Check with Provider Extensions
|
|
8
9
|
|
|
9
10
|
## Goal
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
Use direct Pi SDK sessions for the built-in `sanity-check` workflow. Each reviewer gets an independent in-memory context. Each child can load the extension that owns its exact configured model provider, while only the parent workflow action can control workflow state.
|
|
12
13
|
|
|
13
|
-
The
|
|
14
|
+
The child must use the configured provider, model, thinking level, and provider-owned authentication. It must fail before prompting if that exact dispatch is not available. It must never silently use OpenRouter, Kimi, a local model, or another fallback.
|
|
15
|
+
|
|
16
|
+
The workflow keeps only the final bounded answer and safe operational facts. It does not keep child prompts, reasoning, message history, tool arguments, tool results, repository content, credentials, or extension-private state.
|
|
14
17
|
|
|
15
18
|
## Scope
|
|
16
19
|
|
|
17
|
-
The change is limited to
|
|
20
|
+
The change is limited to Pi Workflows. It changes the private SDK agent-group runner, Sanity Check composition, extension admission, model runtime construction, tests, and canonical documentation.
|
|
21
|
+
|
|
22
|
+
The workflow continues to use existing `action`, `compute`, and `notify` nodes. `src/workflows` and `WorkflowActionContext` remain Pi-independent. The change does not add a workflow primitive, public agent-group export, persisted schema, child workflow run, service, queue, store, transport, Pi core change, or private Pi API.
|
|
23
|
+
|
|
24
|
+
Sanity Check keeps its existing input, review areas, prompts, evidence rules, session counts, strict result validation, verdicts, final notification, and progress schema.
|
|
25
|
+
|
|
26
|
+
## Child session contract
|
|
18
27
|
|
|
19
|
-
|
|
28
|
+
Each child session:
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
- has independent in-memory context and history;
|
|
31
|
+
- uses `SessionManager.inMemory` and creates no Pi session file;
|
|
32
|
+
- owns a separate `ModelRuntime`, provider instance, extension runtime, resource loader, and `AgentSession`;
|
|
33
|
+
- can use only the verified built-in `read`, `grep`, `find`, and `ls` tools;
|
|
34
|
+
- may load the extension that registers the exact configured provider;
|
|
35
|
+
- may load another behavior extension only through an explicit private allowlist;
|
|
36
|
+
- loads no skills, prompt templates, themes, or context files;
|
|
37
|
+
- does not receive the workflow tool, workflow commands, parent run id, node id, attempt id, update channel, or workflow callback;
|
|
38
|
+
- returns only bounded final assistant text and bounded safe lifecycle facts;
|
|
39
|
+
- shares the parent Node process and does not provide OS process isolation.
|
|
22
40
|
|
|
23
|
-
|
|
41
|
+
Pi extensions are trusted in-process code. The runner prevents normal model, tool, command, and callback access to workflow state. It does not sandbox an extension that directly uses the filesystem or network.
|
|
24
42
|
|
|
25
|
-
|
|
43
|
+
## Input and review modes
|
|
44
|
+
|
|
45
|
+
The workflow input stays unchanged. Serial mode remains the default.
|
|
26
46
|
|
|
27
47
|
```json
|
|
28
48
|
{
|
|
@@ -31,118 +51,192 @@ The workflow accepts a review mode and the base reference needed to inspect the
|
|
|
31
51
|
}
|
|
32
52
|
```
|
|
33
53
|
|
|
34
|
-
`mode` is `serial` or `parallel`. The current repository and checked-out branch are the contribution under review. When `baseRef` is omitted, the workflow tries the remote default branch, the current branch upstream, and the first parent, then uses `HEAD` for a working-tree-only review.
|
|
54
|
+
`mode` is `serial` or `parallel`. The current repository and checked-out branch are the contribution under review. When `baseRef` is omitted, the workflow tries the remote default branch, the current branch upstream, and the first parent, then uses `HEAD` for a working-tree-only review.
|
|
55
|
+
|
|
56
|
+
Serial mode creates one review session for all four review areas, then one verification session. It uses two model sessions.
|
|
57
|
+
|
|
58
|
+
Parallel mode creates four focused review sessions at the same time, then one verification session. It uses five model sessions.
|
|
59
|
+
|
|
60
|
+
The agent-group runner enforces maximum concurrency and returns results in request order. A material failure stops queued work, aborts active siblings, waits for every started child to settle, and keeps the first failure as the primary cause.
|
|
35
61
|
|
|
36
|
-
## Evidence
|
|
62
|
+
## Evidence and results
|
|
37
63
|
|
|
38
64
|
The first node collects facts without model judgment. It uses fixed, non-mutating commands to collect:
|
|
39
65
|
|
|
40
|
-
-
|
|
66
|
+
- pull request intent and linked issue context when available;
|
|
41
67
|
- stated acceptance criteria;
|
|
42
|
-
-
|
|
68
|
+
- base and head revisions;
|
|
43
69
|
- changed files;
|
|
44
70
|
- the diff and diff statistics;
|
|
45
71
|
- relevant new exports, schemas, persisted fields, and nearby existing code.
|
|
46
72
|
|
|
47
|
-
|
|
73
|
+
Pull request and repository text is untrusted evidence, not instructions. Evidence and review inputs stay bounded before they enter a model prompt or run bundle.
|
|
48
74
|
|
|
49
|
-
|
|
75
|
+
The verification session receives the evidence and review results. It must remove unsupported claims, require exact file and symbol references, separate facts from assumptions, resolve supported conflicts, and place unresolved questions in `unknowns` or contributor questions. It returns `keep`, `simplify`, `refactor`, `drop`, or `needs_evidence`.
|
|
50
76
|
|
|
51
|
-
|
|
77
|
+
The existing strict result parsers stay unchanged. They continue to enforce all review areas, evidence, acceptance case, verdict, string, and item limits.
|
|
52
78
|
|
|
53
|
-
|
|
79
|
+
## Provider-first extension profile
|
|
54
80
|
|
|
55
|
-
|
|
56
|
-
2. Duplication and refactoring opportunities.
|
|
57
|
-
3. New data models and public plugin or SDK APIs.
|
|
58
|
-
4. Scope and tests.
|
|
81
|
+
The runner resolves one immutable child profile before it starts the group.
|
|
59
82
|
|
|
60
|
-
|
|
83
|
+
### Resolve candidate paths
|
|
61
84
|
|
|
62
|
-
|
|
85
|
+
Use `SettingsManager` and `DefaultPackageManager.resolve()` to find enabled extension paths without executing extension factories. Canonicalize and deduplicate the paths.
|
|
63
86
|
|
|
64
|
-
|
|
87
|
+
The default candidate set contains enabled user-scope extensions. Project extensions are excluded unless the private policy admits them explicitly. Direct and wrapper paths for Pi Workflows are excluded before any extension factory runs.
|
|
65
88
|
|
|
66
|
-
|
|
67
|
-
2. Duplication and refactoring opportunities.
|
|
68
|
-
3. New data models and public plugin or SDK APIs.
|
|
69
|
-
4. Scope and tests.
|
|
89
|
+
### Preflight provider ownership
|
|
70
90
|
|
|
71
|
-
|
|
91
|
+
Load candidate extensions in a no-session `DefaultResourceLoader` preflight. Pass the paths through `additionalExtensionPaths` and set `noExtensions: true` so the loader does not perform a second discovery pass.
|
|
72
92
|
|
|
73
|
-
|
|
93
|
+
Inspect documented pending native and legacy provider registrations. Admit the one extension path that registers the exact configured provider. Permit other behavior extensions only through an explicit private allowlist.
|
|
74
94
|
|
|
75
|
-
|
|
95
|
+
Fail before session creation when:
|
|
76
96
|
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
97
|
+
- no extension registers the configured provider;
|
|
98
|
+
- more than one extension claims the configured provider;
|
|
99
|
+
- an extension fails to load;
|
|
100
|
+
- an admitted extension registers a reserved workflow tool or command;
|
|
101
|
+
- an extension replaces `read`, `grep`, `find`, or `ls`;
|
|
102
|
+
- a loaded path is outside the frozen candidate snapshot.
|
|
83
103
|
|
|
84
|
-
|
|
104
|
+
Before invalidation, dispatch `session_shutdown` through a temporary public `ExtensionRunner` so factory-owned setup can clean up. Preflight does not create a session or dispatch `session_start`. Always invalidate the preflight extension runtime in `finally`.
|
|
85
105
|
|
|
86
|
-
|
|
106
|
+
Extension factories run before their registrations can be inspected. Pi documents that factories must not start background resources. Pi Workflows relies on that contract and does not claim to contain a factory that violates it.
|
|
87
107
|
|
|
88
|
-
|
|
108
|
+
## Exact model dispatch
|
|
89
109
|
|
|
90
|
-
|
|
110
|
+
Resolve one immutable `{ provider, modelId, thinkingLevel }` value for the group. A complete explicit override wins. Otherwise, use the configured `SettingsManager` defaults. Reject partial overrides, missing defaults, unsupported thinking values, and prompts that start with an extension slash command.
|
|
91
111
|
|
|
92
|
-
|
|
112
|
+
Read and strictly validate the configured cached model catalog once. Keep it as an in-memory group snapshot. Create a deep-cloned in-memory model store for each child. Do the same for ordinary Pi credentials read from `auth.json`. Pi Workflows never writes these snapshots back.
|
|
93
113
|
|
|
94
|
-
|
|
114
|
+
Provider extensions use their existing provider-owned credential store in place. Pi Workflows does not copy, inspect, print, migrate, or persist those credentials.
|
|
95
115
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
116
|
+
For each child:
|
|
117
|
+
|
|
118
|
+
1. Create a fresh non-networked `ModelRuntime` from cloned snapshots.
|
|
119
|
+
2. Create a fresh resource loader with only the frozen admitted extension paths.
|
|
120
|
+
3. Disable secondary extension discovery, skills, prompt templates, themes, and context files.
|
|
121
|
+
4. Load a fresh extension and provider instance.
|
|
122
|
+
5. Find the exact cached model and pass it to `createAgentSession`.
|
|
123
|
+
6. Pass the exact configured thinking level.
|
|
124
|
+
7. Verify the session's actual provider, model, thinking level, authentication, extension state, active tools, and built-in tool sources.
|
|
125
|
+
8. Start the prompt only after all checks pass.
|
|
126
|
+
|
|
127
|
+
Any mismatch is terminal. The runner does not select another provider, model, or thinking level.
|
|
128
|
+
|
|
129
|
+
A transient model selected only in the parent TUI is not inherited. The runner enforces the configured process default unless the private request gives a complete explicit dispatch.
|
|
130
|
+
|
|
131
|
+
## Workflow authority boundary
|
|
132
|
+
|
|
133
|
+
The parent Sanity Check action is the only workflow owner.
|
|
105
134
|
|
|
106
|
-
|
|
135
|
+
Children receive only the built-in read-only tool instances requested by Sanity Check. Extension tools can register but remain inactive. The runner rejects same-name replacements for the built-in tools.
|
|
136
|
+
|
|
137
|
+
Children receive no:
|
|
138
|
+
|
|
139
|
+
- `workflow` tool;
|
|
140
|
+
- `/workflow`, `/piw`, `/controller`, or workflow-channel command;
|
|
141
|
+
- run, node, or attempt identifier;
|
|
142
|
+
- workflow update, answer, submit, pause, resume, or cancel callback;
|
|
143
|
+
- child workflow run or parent workflow handle.
|
|
144
|
+
|
|
145
|
+
The runner rejects prompts that would invoke extension slash commands. These controls prevent the normal child model and admitted extension bindings from inspecting or changing workflow state.
|
|
146
|
+
|
|
147
|
+
## Lifecycle and privacy
|
|
148
|
+
|
|
149
|
+
One owner controls each child from creation through cleanup.
|
|
150
|
+
|
|
151
|
+
The owner:
|
|
152
|
+
|
|
153
|
+
1. Creates the child runtime and session.
|
|
154
|
+
2. Subscribes before prompting.
|
|
155
|
+
3. Emits only bounded safe lifecycle phases.
|
|
156
|
+
4. Waits for prompt settlement.
|
|
157
|
+
5. Extracts only the latest final assistant text.
|
|
158
|
+
6. Bounds the returned text before validation.
|
|
159
|
+
7. Calls and awaits `abort()` on timeout or cancellation.
|
|
160
|
+
8. Waits for prompt settlement after abort.
|
|
161
|
+
9. Unsubscribes.
|
|
162
|
+
10. Disposes the session so extension shutdown runs.
|
|
163
|
+
11. Invalidates remaining extension runtime state.
|
|
164
|
+
12. Releases provider resources.
|
|
165
|
+
|
|
166
|
+
Cleanup runs for success, creation failure, authentication failure, provider failure, malformed output, timeout, parent cancellation, sibling failure, and disposal failure. A cleanup failure remains a bounded secondary diagnostic and does not replace an earlier primary error.
|
|
167
|
+
|
|
168
|
+
The workflow never copies extension events or extension-private state into progress or run bundles.
|
|
169
|
+
|
|
170
|
+
## Progress and visibility
|
|
171
|
+
|
|
172
|
+
Sanity Check keeps the existing `pi-workflows.progress.v1` records and keys:
|
|
107
173
|
|
|
108
174
|
```text
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
175
|
+
agents/review
|
|
176
|
+
agents/review/necessity
|
|
177
|
+
agents/review/duplication
|
|
178
|
+
agents/review/contracts
|
|
179
|
+
agents/review/scope_tests
|
|
180
|
+
agents/verification
|
|
181
|
+
agents/verification/verification
|
|
116
182
|
```
|
|
117
183
|
|
|
118
|
-
|
|
184
|
+
Aggregate tracks report completed and total sessions. Child tracks report a bounded role label, the verified actual model when known, and a safe phase such as `starting`, `thinking`, `tool: read`, `finalizing`, or a terminal phase.
|
|
119
185
|
|
|
120
|
-
|
|
186
|
+
Updates are deduplicated, throttled, and observational. They cannot change agent execution.
|
|
121
187
|
|
|
122
|
-
|
|
188
|
+
The Pi widget shows the aggregate plus failed and active children within its ten-line limit. `piw` shows all durable child tracks and samples. Both views use existing progress records. No new persisted field or schema is added.
|
|
123
189
|
|
|
124
|
-
|
|
190
|
+
## Implementation plan
|
|
125
191
|
|
|
126
|
-
|
|
192
|
+
1. Update the Pi SDK development baseline to one compatible 0.84.x release. Keep the Pi coding-agent, Pi AI, and Pi TUI packages aligned and set an honest peer compatibility floor. Do not add Pi Factory or a provider extension as a dependency.
|
|
193
|
+
2. Add the private dispatch and child extension profile contracts under `src/builtins`. Do not export them from package entry points.
|
|
194
|
+
3. Resolve enabled extension paths without execution. Canonicalize paths, exclude project extensions by default, and exclude direct and wrapper Pi Workflows paths.
|
|
195
|
+
4. Add the no-session extension preflight. Identify the exact native or legacy provider owner and reject reserved workflow capabilities, provider conflicts, load errors, and built-in tool overrides.
|
|
196
|
+
5. Keep the model-catalog snapshot work, but change it to one validated group snapshot and one clone per child. Add the same ownership for ordinary Pi credentials. Remove the previous empty-catalog behavior.
|
|
197
|
+
6. Replace the shared group `ModelRuntime` with one complete runtime per child.
|
|
198
|
+
7. Verify exact provider, model, thinking, authentication, admitted extensions, active tools, and tool sources before every prompt.
|
|
199
|
+
8. Complete provider, extension, and session cleanup on every exit path.
|
|
200
|
+
9. Pass the private profile and exact dispatch through Sanity Check without changing its review behavior or progress schema. Remove any `--no-extensions` launch guidance.
|
|
201
|
+
10. Change the built-in Sanity Check revision from 2 to 3.
|
|
202
|
+
11. Add temporary fixture extensions and full unit, integration, interactive Pi, and standalone host coverage.
|
|
203
|
+
12. Update this plan and `docs/workflows.md` to match the shipped behavior.
|
|
204
|
+
13. Run the complete repository gate and inspect the full public diff.
|
|
205
|
+
14. After mock-provider verification, run one bounded real acceptance on OpenClaw pull request 126028 with `openai-codex/gpt-5.6-sol` and high thinking. Abort immediately if any child reports another provider or model. Do not modify OpenClaw.
|
|
127
206
|
|
|
128
|
-
|
|
207
|
+
## Revision and compatibility
|
|
129
208
|
|
|
130
|
-
-
|
|
131
|
-
- serial mode starting exactly one review session and one verification session;
|
|
132
|
-
- parallel mode starting exactly four concurrent review sessions and one verification session;
|
|
133
|
-
- the four required review areas;
|
|
134
|
-
- the acceptance case and evidence requirements in every review prompt;
|
|
135
|
-
- read-only child tool arguments and disabled session, extension, and skill discovery;
|
|
136
|
-
- structured result validation and all five verdicts;
|
|
137
|
-
- unsupported and conflicting finding handling in the verification prompt;
|
|
138
|
-
- bounded output and error handling;
|
|
139
|
-
- child failure, malformed output, timeout, cancellation, and process cleanup;
|
|
140
|
-
- final notification delivery without a model turn;
|
|
141
|
-
- built-in discovery and export behavior.
|
|
209
|
+
Sanity Check moves from built-in revision 2 to revision 3.
|
|
142
210
|
|
|
143
|
-
|
|
211
|
+
This is an alpha hard cutover. Do not retain the revision-2 child runtime, fallback, compatibility runner, migration, alias, dual path, or feature flag. An unfinished revision-2 run must fail with clear cancel-and-restart guidance. Terminal revision-2 bundles remain readable historical evidence because the persisted schema does not change.
|
|
144
212
|
|
|
145
|
-
|
|
213
|
+
## Tests
|
|
214
|
+
|
|
215
|
+
Unit and integration tests must cover:
|
|
216
|
+
|
|
217
|
+
- dispatch parsing and exact provider, model, and thinking enforcement;
|
|
218
|
+
- missing authentication and no fallback;
|
|
219
|
+
- extension path resolution, canonicalization, scope filtering, disabled paths, and explicit behavior paths;
|
|
220
|
+
- direct and wrapper Pi Workflows exclusion;
|
|
221
|
+
- native and legacy provider-owner discovery;
|
|
222
|
+
- reserved workflow command and tool rejection;
|
|
223
|
+
- inactive extension tools and built-in tool override rejection;
|
|
224
|
+
- per-child runtime, provider, extension, loader, and history isolation under parallel execution;
|
|
225
|
+
- validated model and credential snapshots, deep clones, cancellation, malformed input, and no writes;
|
|
226
|
+
- provider-owned mock authentication without credential exposure;
|
|
227
|
+
- success, provider error, empty output, malformed output, oversized output, timeout, cancellation, fail-fast, and cleanup-error precedence;
|
|
228
|
+
- final-only retention and absence of private child content in results, errors, updates, and bundles;
|
|
229
|
+
- serial two-session and parallel five-session behavior;
|
|
230
|
+
- existing progress keys, model labels, throttling, and rendering;
|
|
231
|
+
- interactive Pi with normal extensions enabled and the local mock provider;
|
|
232
|
+
- standalone `WorkflowHost` through the same private runtime path;
|
|
233
|
+
- no child session files or child workflow runs;
|
|
234
|
+
- built-in revision 3 and historical terminal bundle reading;
|
|
235
|
+
- final notification without another model turn.
|
|
236
|
+
|
|
237
|
+
Tests use mock providers and temporary directories. They do not call real models or write outside temporary directories.
|
|
238
|
+
|
|
239
|
+
Before completion, run:
|
|
146
240
|
|
|
147
241
|
```bash
|
|
148
242
|
npm run check
|
|
@@ -151,25 +245,39 @@ npx slophammer-ts@latest dry .
|
|
|
151
245
|
npx slophammer-ts@latest check . --only ts.dependency-boundaries-required
|
|
152
246
|
```
|
|
153
247
|
|
|
248
|
+
After these checks and Pi Reviewer pass, perform the one explicitly authorized bounded GPT-5.6 Sol acceptance run.
|
|
249
|
+
|
|
154
250
|
## Acceptance criteria
|
|
155
251
|
|
|
156
252
|
The implementation is complete when:
|
|
157
253
|
|
|
158
|
-
- `/workflow sanity-check` discovers
|
|
159
|
-
-
|
|
160
|
-
- serial mode uses two
|
|
161
|
-
- parallel mode uses five
|
|
162
|
-
- child
|
|
163
|
-
-
|
|
164
|
-
-
|
|
165
|
-
-
|
|
166
|
-
-
|
|
167
|
-
-
|
|
254
|
+
- `/workflow sanity-check` discovers built-in revision 3;
|
|
255
|
+
- the parent Pi process runs with its normal configured extensions;
|
|
256
|
+
- serial mode uses two independent in-memory SDK sessions;
|
|
257
|
+
- parallel mode uses five independent in-memory SDK sessions, with four reviews running concurrently;
|
|
258
|
+
- every child loads the extension that owns the exact configured provider;
|
|
259
|
+
- every child reports the exact required provider, model, and thinking level before prompting;
|
|
260
|
+
- no child silently falls back to OpenRouter, Kimi, a local model, or another dispatch;
|
|
261
|
+
- children use only verified built-in read-only tools and create no session files;
|
|
262
|
+
- children cannot use normal workflow tools, commands, identifiers, or callbacks;
|
|
263
|
+
- the workflow keeps only bounded final answers and safe operational facts;
|
|
264
|
+
- child prompts, reasoning, tool payloads, histories, credentials, and extension state do not enter run bundles or progress updates;
|
|
265
|
+
- provider, extension, and session cleanup completes on every exit path;
|
|
266
|
+
- Sanity Check review behavior, strict validation, verdicts, progress, and final notification remain unchanged;
|
|
267
|
+
- interactive and headless runs use the same private SDK path;
|
|
268
|
+
- all required checks pass with coverage margin;
|
|
269
|
+
- the bounded acceptance run on OpenClaw pull request 126028 reports GPT-5.6 Sol for every child and completes with a strict verdict without modifying OpenClaw.
|
|
168
270
|
|
|
169
271
|
## Contract impact
|
|
170
272
|
|
|
171
273
|
- **Origin session:** The normal workflow start record and one final workflow notification.
|
|
172
|
-
- **
|
|
173
|
-
- **
|
|
174
|
-
- **
|
|
175
|
-
- **
|
|
274
|
+
- **Parent extensions:** The parent Pi process loads its normal configured extensions.
|
|
275
|
+
- **Child extensions:** Only the exact provider owner and explicit private behavior paths are admitted.
|
|
276
|
+
- **Child sessions:** Independent in-memory contexts and complete per-child runtimes in the same Node process. No child session file or child workflow run.
|
|
277
|
+
- **Model dispatch:** Exact provider, model, and thinking are required. Fallback is forbidden.
|
|
278
|
+
- **Credentials:** Pi Workflows does not copy or persist credentials. Provider extensions use their existing stores in place.
|
|
279
|
+
- **Other persistent data:** The normal workflow run bundle and existing progress updates only.
|
|
280
|
+
- **Private content:** Prompts, reasoning, intermediate messages, tool payloads, histories, credentials, and extension-private state are not persisted by Pi Workflows.
|
|
281
|
+
- **Pi public API:** Documented package manager, resource loader, extension and provider registration, model runtime, session, event, abort, and disposal APIs only.
|
|
282
|
+
- **Pi Workflows public API:** No change.
|
|
283
|
+
- **Isolation:** Workflow capability is withheld from normal child bindings. Arbitrary trusted in-process extension code is not sandboxed.
|
package/docs/run-bundles.md
CHANGED
|
@@ -40,18 +40,18 @@ Human decision records use a separate additive directory next to `runs/` so a wa
|
|
|
40
40
|
request.json
|
|
41
41
|
deliveries/<channel>/<attempt-id>.json
|
|
42
42
|
answers/<attempt-id>.json
|
|
43
|
-
resolution.json # atomic
|
|
44
|
-
accepted.json
|
|
45
|
-
cancelled.json #
|
|
43
|
+
resolution.json # atomic resolved-or-cancelled fence
|
|
44
|
+
accepted.json # human or timeout response with explicit provenance
|
|
45
|
+
cancelled.json # terminal cancellation tombstone
|
|
46
46
|
settlements/<channel>/<attempt-id>.json
|
|
47
47
|
continuation.json
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
The request links to the waiting run, node, attempt, workflow source, and canonical request digest.
|
|
50
|
+
The request links to the waiting run, node, attempt, workflow source, and canonical request digest. The single v1 request stores the canonical subject and a separate normalized operator presentation. Its subject, presentation, revision, choices, input prompts, optional deadline, and optional automatic response are bound to the request digest. Accepted records and redacted continuation receipts preserve the subject and presentation digests and state `human` or `timeout` provenance. A timeout record has no human source. Final records use no-replace creation and adopt only identical retries. `resolution.json` is the first resolved-or-cancelled fence. It materializes either `accepted.json` or `cancelled.json`; a crash can rebuild that detail from the resolution. A cancellation tombstone prevents later automatic continuation. `continuation.json` binds one resolved response and its provenance to one deterministic continuation run. Delivery and settlement records cannot change the result. Older human-decision record shapes are incompatible alpha state and require reset; there is no migration reader.
|
|
51
51
|
|
|
52
52
|
Telegram multipart delivery uses additive v2 delivery records for the overall intent, each part, and completion. Part records contain only recipient indexes, part indexes, counts, and content digests. Telegram chat and message IDs remain in the private disposable channel projection and never enter run or decision bundles. An ambiguous part remains unknown and is not retried blindly.
|
|
53
53
|
|
|
54
|
-
A human-decision continuation preserves the parent's original workflow input and replaces the carried checkpoint output with the
|
|
54
|
+
A human-decision continuation preserves the parent's original workflow input and replaces the carried checkpoint output with the resolved typed response for routing. Its `humanDecision` state is a redacted receipt with `human` or `timeout` provenance. A v2 receipt includes the subject digest, presentation digest, and revision, but not the subject itself. Verified human actor, channel, event, and idempotency details remain in the private sibling decision records and are not copied into the run bundle. Ordinary checkpoint continuations keep using the answer as the continuation input. This alpha contract changes current v1 and v2 field sets in place; old active definitions refuse resume instead of using a compatibility path.
|
|
55
55
|
|
|
56
56
|
Run ids are `<UTC timestamp>-<workflow slug>-<8 hex chars>`, so lexical order
|
|
57
57
|
is chronological order.
|
|
@@ -185,7 +185,7 @@ validators are not serialized. Each node keeps only its metadata (`nodeType`,
|
|
|
185
185
|
`timeoutMs`, `statusDetail`, `expectedOutput`, `summary`, `actionExecution`),
|
|
186
186
|
and edges are copied verbatim. A fixed `timeoutMs: null` is preserved and means
|
|
187
187
|
that the node has no wall-clock deadline. Timeout callbacks remain omitted.
|
|
188
|
-
Included nodes also record `mountPath`, `localNodeId`, and internal entry or exit status. The top-level `composition.mounts` list records every mount, entry, named exit, and child step limit. The snapshot is what lets viewers draw all nodes, including ones that have not run yet. It is immutable after run start.
|
|
188
|
+
A human-decision snapshot records fixed `onTimeout` duration and response values. A dynamic timeout callback is omitted and marked as dynamic. Included nodes also record `mountPath`, `localNodeId`, and internal entry or exit status. The top-level `composition.mounts` list records every mount, entry, named exit, and child step limit. The snapshot is what lets viewers draw all nodes, including ones that have not run yet. It is immutable after run start.
|
|
189
189
|
|
|
190
190
|
## Resume and repair
|
|
191
191
|
|
package/docs/workflows.md
CHANGED
|
@@ -291,6 +291,10 @@ const choices = defineHumanChoices({
|
|
|
291
291
|
humanDecision({
|
|
292
292
|
audience: "operator",
|
|
293
293
|
choices,
|
|
294
|
+
onTimeout: {
|
|
295
|
+
afterMs: 10 * 60_000,
|
|
296
|
+
response: { choice: "continue" },
|
|
297
|
+
},
|
|
294
298
|
request: ({ outputs }) => ({
|
|
295
299
|
title: "Approve plan",
|
|
296
300
|
subject: outputs.plan,
|
|
@@ -303,7 +307,7 @@ humanDecision({
|
|
|
303
307
|
});
|
|
304
308
|
```
|
|
305
309
|
|
|
306
|
-
The waiting run stores a versioned request and asks every channel configured for the logical audience. The structured `subject` remains machine data. Channels receive only the normalized `presentation`, title, choices, and
|
|
310
|
+
The waiting run stores a versioned request and asks every channel configured for the logical audience. The structured `subject` remains machine data. Channels receive only the normalized `presentation`, title, choices, input prompts, and any deadline policy. The first valid verified human answer wins. When `onTimeout` is present and no human answer wins before the saved deadline, recovery applies the validated response with `timeout` provenance. This policy can continue without a configured channel. A continuation preserves the original workflow input and exposes the resolved response as the checkpoint output. `humanDecisionEdge()` provides exhaustive routing for the choices. Existing `body` requests remain a legacy compatibility form and use deterministic readable formatting.
|
|
307
311
|
|
|
308
312
|
The model-facing workflow tool cannot answer a protected human decision. Pi interactive UI and configured external channels use a host-owned answer path. Ordinary checkpoints keep the existing `/workflow answer` behavior.
|
|
309
313
|
|
|
@@ -417,7 +421,9 @@ runs.
|
|
|
417
421
|
|
|
418
422
|
The built-in `autoplan` workflow selects a practical in-scope solution and writes a detailed plan. The standalone `autodoc` workflow finds an already selected plan, records it in canonical documentation, verifies those documents, and never devises or implements. The built-in `autoimplement` workflow finds a clear existing plan from explicit input, conversation context, or referenced canonical documents. It blocks when no clear plan exists. An explicit plan bypasses autodoc only when a current-document receipt carries its matching plan digest; otherwise autodoc inspects and adopts or updates the canonical documents. Later invalidating evidence returns to `autoplan` followed by `autodoc`.
|
|
419
423
|
|
|
420
|
-
The built-in `plan-approval` workflow offers
|
|
424
|
+
The built-in `plan-approval` workflow offers `continue`, `stop`, and exact-text `replan` exits. Its shared policy uses `auto`, `required`, or `skip` mode. Omitted policy defaults to `auto`: ask audience `operator`, then continue with the exact plan after 10 minutes without an answer. Required mode waits for a human. Skip mode creates no decision. Stop and replan always require a human answer.
|
|
425
|
+
|
|
426
|
+
The internal plan-change workflow composes Autoplan, Autodoc, plan approval, and bounded replanning. Autoimplement and Monitor use it whenever they create or change a plan. Existing supplied or discovered plans bypass the gate. A plan selected by Monitor enters Autoimplement without another decision for the same digest.
|
|
421
427
|
|
|
422
428
|
Autoimplement runs independent commands through bounded command batches. A batch is an ordinary function action that calls the public `runCommandBatch` helper. Each command has a stable ID, executable, arguments, absolute working directory, timeout, and output limit. Results stay separate and return in input order. One command uses the same path with concurrency one.
|
|
423
429
|
|
|
@@ -453,11 +459,25 @@ The built-in `sanity-check` workflow reviews a pull request or local contributio
|
|
|
453
459
|
}
|
|
454
460
|
```
|
|
455
461
|
|
|
456
|
-
Serial mode is the default. It runs one
|
|
462
|
+
Serial mode is the default. It runs one review session for all four review areas, then one verification session. Parallel mode runs four focused review sessions at the same time, then one verification session. Serial mode uses two model sessions. Parallel mode uses five. When `baseRef` is omitted, the workflow tries the remote default branch, the current branch upstream, and the first parent, then uses `HEAD` for a working-tree-only review.
|
|
463
|
+
|
|
464
|
+
The workflow collects pull request intent and repository diff evidence before model review. It bounds evidence and review results before prompt construction and marks truncated input. Every review must cite evidence and give the strongest case for accepting the current design. The verification session removes unsupported claims, requires exact file and symbol references, resolves supported conflicts, and returns `keep`, `simplify`, `refactor`, `drop`, or `needs_evidence`.
|
|
465
|
+
|
|
466
|
+
Sanity Check revision 3 creates child sessions directly through the documented Pi SDK. A private built-in runner uses `createAgentSession` with `SessionManager.inMemory`, one independent context per child, and only the verified built-in `read`, `grep`, `find`, and `ls` tools. Child sessions load no skills, prompt templates, themes, or context files. They create no Pi session file.
|
|
467
|
+
|
|
468
|
+
The parent Pi process keeps its normal configured extensions enabled. The child runner resolves enabled user extension paths, excludes Pi Workflows and project extensions by default, and preflights the remaining paths without creating a session. It admits only the extension that registers the exact configured provider, plus any behavior extension on an explicit private allowlist. It rejects competing provider owners, workflow tools or commands, and extensions that replace a built-in read-only tool. The admitted paths are frozen for the group, and each child loads only those explicit paths without a second discovery pass.
|
|
469
|
+
|
|
470
|
+
Each child owns a separate `ModelRuntime`, provider instance, extension runtime, resource loader, and in-memory session. The runner loads validated model and ordinary credential snapshots into per-child in-memory stores without writing them back. A provider extension uses its existing provider-owned credential store in place; Pi Workflows does not copy, print, migrate, or persist those credentials.
|
|
471
|
+
|
|
472
|
+
A complete explicit provider, model, and thinking override wins. Otherwise, the runner uses the configured process defaults. It passes the exact cached model and thinking level to the child, then verifies the actual provider, model, thinking level, authentication, extension state, active tools, and tool sources before prompting. A missing or different dispatch is terminal. The runner never silently falls back to OpenRouter, Kimi, a local model, or another provider or model. It does not promise to inherit a model selected temporarily in the origin Pi TUI.
|
|
473
|
+
|
|
474
|
+
Children do not receive the `workflow` tool, workflow commands, parent run identifiers, update channels, or workflow callbacks. Prompts that invoke extension slash commands are rejected. The parent Sanity Check action remains the only workflow owner. These controls block normal child model and extension bindings from inspecting or changing workflow state. Extensions still run as trusted code in the parent Node process, so this is not an OS sandbox against direct filesystem or network access.
|
|
475
|
+
|
|
476
|
+
Only bounded final assistant text and safe operational facts leave a live child session. Pi Workflows does not persist child prompts, reasoning, intermediate messages, tool arguments, tool results, message history, credentials, or extension-private state. Timeout and cancellation abort and settle active work before the runner disposes the session, shuts down extensions, and releases provider resources.
|
|
457
477
|
|
|
458
|
-
The workflow
|
|
478
|
+
The workflow publishes aggregate and per-agent `pi-workflows.progress.v1` tracks under `agents/review/*` and `agents/verification/*`. Progress contains role, the verified actual model when known, state, elapsed facts, and safe phases such as `thinking` or `tool: read`. The Pi widget shows the aggregate plus failed and active children within its ten-line limit. `piw` shows every durable child track and its samples. Both views use existing progress records, so no child workflow run or new persisted schema is needed.
|
|
459
479
|
|
|
460
|
-
|
|
480
|
+
Serial mode still uses two sessions, and parallel mode still uses five. Prompts, review areas, strict result validation, verdicts, and final notification stay unchanged. The workflow sends the final report through a final notification with `triggerTurn: false`, so the origin model does not produce another response. The CLI, JSON or RPC stream, temporary prompt file, standard-output cap, subprocess fallback, shared child runtime, and blanket child-extension ban are not retained. See [the Sanity Check plan](plans/2026-08-21-sanity-check-plan.md) for the selected implementation and test boundaries.
|
|
461
481
|
|
|
462
482
|
### Built-in monitor
|
|
463
483
|
|
|
@@ -475,7 +495,7 @@ one looping workflow run. Its input is:
|
|
|
475
495
|
}
|
|
476
496
|
```
|
|
477
497
|
|
|
478
|
-
The first check runs immediately. Omit `repair` for observation-only monitoring. An authorized repair routes through
|
|
498
|
+
The first check runs immediately. Omit `repair` for observation-only monitoring. An authorized repair routes through the shared plan-change workflow, Autoimplement, and a fresh check. A repeated issue with unchanged target evidence stops as blocked. Omit `repair.approval` for the 10-minute autonomous default. Use `approval.mode: "required"` to wait for an explicit answer or `approval.mode: "skip"` to continue without asking.
|
|
479
499
|
|
|
480
500
|
`everyMinutes` defaults to 30. Each accepted check must provide one concise report and choose `continue`, `repair` when authorized, or `stop`. The
|
|
481
501
|
runtime queues that report as a workflow notification with `triggerTurn:
|
|
@@ -1,58 +1,31 @@
|
|
|
1
|
-
import { compute, defineWorkflow, includeWorkflow
|
|
2
|
-
import
|
|
3
|
-
import autoplan from "../../src/builtins/autoplan.workflow.js";
|
|
4
|
-
import planApproval from "../../src/builtins/plan-approval.workflow.js";
|
|
1
|
+
import { compute, defineWorkflow, includeWorkflow } from "@osolmaz/pi-workflows";
|
|
2
|
+
import planChange from "../../src/builtins/plan-change.workflow.js";
|
|
5
3
|
|
|
6
4
|
export default defineWorkflow({
|
|
7
5
|
name: "approved-plan-example",
|
|
8
|
-
startAt: "
|
|
9
|
-
maxSteps:
|
|
6
|
+
startAt: "start",
|
|
7
|
+
maxSteps: 100,
|
|
10
8
|
includes: {
|
|
11
|
-
|
|
12
|
-
input: ({ input
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
| undefined;
|
|
17
|
-
return {
|
|
18
|
-
problem: (input as { task: string }).task,
|
|
19
|
-
...(prior?.exit === "ready" ? { previousPlan: prior.output?.plan } : {}),
|
|
20
|
-
...(answer?.exit === "replan" ? { newEvidence: answer.output?.instructions } : {}),
|
|
21
|
-
};
|
|
22
|
-
},
|
|
23
|
-
}),
|
|
24
|
-
documentation: includeWorkflow(autodoc, {
|
|
25
|
-
input: ({ input, outputs }) => {
|
|
26
|
-
const result = includedResult(autoplan, outputs.design);
|
|
27
|
-
if (result.exit !== "ready") throw new Error("design is not ready");
|
|
28
|
-
return { task: (input as { task: string }).task, plan: result.output.plan };
|
|
29
|
-
},
|
|
30
|
-
}),
|
|
31
|
-
approval: includeWorkflow(planApproval, {
|
|
32
|
-
input: ({ input, outputs }) => {
|
|
33
|
-
const result = includedResult(autodoc, outputs.documentation);
|
|
34
|
-
if (result.exit !== "ready") throw new Error("documentation is not ready");
|
|
35
|
-
return {
|
|
36
|
-
task: (input as { task: string }).task,
|
|
37
|
-
plan: result.output.plan,
|
|
38
|
-
planDigest: result.output.planDigest,
|
|
9
|
+
planChange: includeWorkflow(planChange, {
|
|
10
|
+
input: ({ input }) => ({
|
|
11
|
+
task: (input as { task: string }).task,
|
|
12
|
+
approval: {
|
|
13
|
+
mode: "auto" as const,
|
|
39
14
|
audience: "operator",
|
|
40
|
-
|
|
41
|
-
|
|
15
|
+
timeoutMinutes: 10,
|
|
16
|
+
maxReplans: 3,
|
|
17
|
+
},
|
|
18
|
+
}),
|
|
42
19
|
}),
|
|
43
20
|
},
|
|
44
21
|
nodes: {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
blocked: compute({ run: ({ outputs }) => ({ status: "blocked", outputs }) }),
|
|
22
|
+
start: compute({ run: () => ({ route: "plan" }) }),
|
|
23
|
+
done: compute({ run: ({ outputs }) => ({ status: "ready", plan: outputs.planChange }) }),
|
|
24
|
+
blocked: compute({ run: ({ outputs }) => ({ status: "blocked", plan: outputs.planChange }) }),
|
|
48
25
|
},
|
|
49
26
|
edges: [
|
|
50
|
-
{ from: "
|
|
51
|
-
{ from: "
|
|
52
|
-
{ from: "
|
|
53
|
-
{ from: "documentation.blocked", to: "blocked" },
|
|
54
|
-
{ from: "approval.continue", to: "done" },
|
|
55
|
-
{ from: "approval.stop", to: "stopped" },
|
|
56
|
-
{ from: "approval.replan", to: "design" },
|
|
27
|
+
{ from: "start", to: "planChange" },
|
|
28
|
+
{ from: "planChange.ready", to: "done" },
|
|
29
|
+
{ from: "planChange.blocked", to: "blocked" },
|
|
57
30
|
],
|
|
58
31
|
});
|
package/herdr-plugin.toml
CHANGED