@wichayutdew/pi-workflows 0.1.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/LICENSE +201 -0
- package/README.md +752 -0
- package/agents/step.md +17 -0
- package/dist/index.js +4576 -0
- package/examples/mr-comments.workflow.yaml +115 -0
- package/examples/prompts/mr-comments/implement.md +8 -0
- package/examples/prompts/mr-comments/inspect.md +5 -0
- package/examples/prompts/mr-comments/plan.md +13 -0
- package/examples/prompts/mr-comments/verify.md +7 -0
- package/examples/settings.yaml +19 -0
- package/package.json +81 -0
- package/schemas/settings.schema.json +22 -0
- package/schemas/workflow.schema.json +585 -0
- package/src/command-names.ts +46 -0
- package/src/commands.ts +80 -0
- package/src/config/ceiling.ts +153 -0
- package/src/config/command-conflicts.ts +31 -0
- package/src/config/load.ts +327 -0
- package/src/config/types.ts +187 -0
- package/src/config/validate.ts +1145 -0
- package/src/digest.ts +23 -0
- package/src/engine/checkpoint.ts +30 -0
- package/src/engine/resume.ts +44 -0
- package/src/engine/state.ts +186 -0
- package/src/engine/transitions.ts +426 -0
- package/src/harness.ts +1676 -0
- package/src/index.ts +15 -0
- package/src/integrations/plannotator.ts +235 -0
- package/src/integrations/prompt-gate.ts +54 -0
- package/src/integrations/subagents/child-runtime.ts +306 -0
- package/src/integrations/subagents/client.ts +239 -0
- package/src/integrations/subagents/protocol.ts +304 -0
- package/src/policy/approved-commands.ts +225 -0
- package/src/policy/bash.ts +355 -0
- package/src/policy/completion-batch.ts +36 -0
- package/src/policy/immutable-input.ts +18 -0
- package/src/policy/tools.ts +150 -0
- package/src/preflight.ts +76 -0
- package/src/prompt.ts +146 -0
- package/src/runtime/completion-tool.ts +22 -0
- package/src/runtime/main-step-runtime.ts +227 -0
- package/src/runtime/serial-task-queue.ts +17 -0
- package/src/runtime/step-result.ts +85 -0
- package/src/workflow-list.ts +25 -0
- package/src/workflow-status.ts +611 -0
package/README.md
ADDED
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
[](https://codecov.io/gh/wichayutdew/pi-workflows)
|
|
2
|
+
|
|
3
|
+
# Pi Workflows
|
|
4
|
+
|
|
5
|
+
A declarative, pauseable workflow harness for Pi.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
Pi Workflows keeps orchestration code generic and moves workflow behavior into
|
|
10
|
+
small YAML configuration and prompt files. Each workflow defines its
|
|
11
|
+
own steps, transitions, execution mode, tool access, MCP access, Bash policy,
|
|
12
|
+
extensions, skills, dependency checks, and optional human-review gate.
|
|
13
|
+
|
|
14
|
+
The harness owns state transitions. A step runs in the main Pi agent by default,
|
|
15
|
+
or in a separate [pi-subagents](https://github.com/nicobailon/pi-subagents)
|
|
16
|
+
child when it declares `subagent`. Either execution mode can advance only by
|
|
17
|
+
calling `workflow_complete_step` with an outcome declared by that step.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
Install Pi Workflows from npm:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pi install npm:@wichayutdew/pi-workflows
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For local development, install it from the repository instead:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pi install /absolute/path/to/pi-workflows
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
For the strongest context isolation and browser-based review experience, use it
|
|
34
|
+
with both [pi-subagents](https://github.com/nicobailon/pi-subagents) and
|
|
35
|
+
[Plannotator](https://github.com/backnotprop/plannotator):
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pi install npm:pi-subagents
|
|
39
|
+
pi install npm:@plannotator/pi-extension
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Neither integration is required, but both are highly recommended together for
|
|
43
|
+
the best isolation and review experience. Pi Workflows targets
|
|
44
|
+
pi-subagents `0.35.1` or newer. Run `/subagents-doctor` if an explicitly
|
|
45
|
+
delegated step cannot start.
|
|
46
|
+
|
|
47
|
+
Pi loads `src/index.ts` through the package manifest. Restart Pi or run `/reload` after changing extension source.
|
|
48
|
+
|
|
49
|
+
## Add a workflow
|
|
50
|
+
|
|
51
|
+
User workflows live in one of these formats:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
~/.pi/agent/workflows/*.workflow.yaml
|
|
55
|
+
~/.pi/agent/workflows/*.workflow.yml
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
YAML keeps nested steps and permission lists compact. The loader uses the
|
|
59
|
+
strict YAML 1.2 core schema: duplicate keys, merge keys, invalid tags,
|
|
60
|
+
multiple documents, non-1.2 directives, and excessive alias expansion fail
|
|
61
|
+
closed.
|
|
62
|
+
|
|
63
|
+
Set `PI_WORKFLOWS_DIR` to use another directory. The example can be copied as a starting point:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
mkdir -p ~/.pi/agent/workflows/prompts
|
|
67
|
+
cp examples/mr-comments.workflow.yaml ~/.pi/agent/workflows/
|
|
68
|
+
cp -R examples/prompts/mr-comments ~/.pi/agent/workflows/prompts/
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The example YAML language-server schema path is repository-relative. Adjust or
|
|
72
|
+
remove its first comment after copying.
|
|
73
|
+
|
|
74
|
+
Run `/workflow-reload`, then start by configured command:
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
/mr-comments <merge-request URL or description>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Every workflow is also available through:
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
/workflow-start mr-comments <merge-request URL or description>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Minimal workflow
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
# yaml-language-server: $schema=./schemas/workflow.schema.json
|
|
90
|
+
version: 1
|
|
91
|
+
id: fix
|
|
92
|
+
command: fix
|
|
93
|
+
description: Inspect, implement, and verify a change
|
|
94
|
+
start: inspect
|
|
95
|
+
steps:
|
|
96
|
+
inspect:
|
|
97
|
+
prompt: Inspect {{workflow.input}} without modifying files.
|
|
98
|
+
permissions:
|
|
99
|
+
tools: [read, grep, bash]
|
|
100
|
+
bash:
|
|
101
|
+
mode: read-only
|
|
102
|
+
requires:
|
|
103
|
+
tools: [read, bash]
|
|
104
|
+
transitions:
|
|
105
|
+
ready: implement
|
|
106
|
+
blocked: $pause
|
|
107
|
+
|
|
108
|
+
implement:
|
|
109
|
+
prompt:
|
|
110
|
+
file: prompts/implement.md
|
|
111
|
+
permissions:
|
|
112
|
+
tools: [read, edit, write, bash]
|
|
113
|
+
bash:
|
|
114
|
+
mode: allow-list
|
|
115
|
+
allow:
|
|
116
|
+
- executable: bun
|
|
117
|
+
argsPrefix: [test]
|
|
118
|
+
transitions:
|
|
119
|
+
done: $done
|
|
120
|
+
blocked: $pause
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The loader rejects unknown properties, duplicate identifiers, missing targets,
|
|
124
|
+
unsafe prompt paths, invalid gate contracts, Pi command-name conflicts, and
|
|
125
|
+
project permissions above the user ceiling. Conflicts with commands from other
|
|
126
|
+
loaded extensions or prompt resources are diagnosed before aliases register.
|
|
127
|
+
|
|
128
|
+
## Configuration
|
|
129
|
+
|
|
130
|
+
Top-level fields:
|
|
131
|
+
|
|
132
|
+
| Field | Required | Default | Description |
|
|
133
|
+
| ----------------- | -------- | ------- | -------------------------------------------------------------------------------------------------- |
|
|
134
|
+
| `version` | Yes | — | Configuration contract version. Currently `1`. |
|
|
135
|
+
| `id` | Yes | — | Stable workflow identifier. |
|
|
136
|
+
| `command` | Yes | — | Slash command without `/`. |
|
|
137
|
+
| `description` | Yes | — | Command description. |
|
|
138
|
+
| `start` | Yes | — | First step identifier. |
|
|
139
|
+
| `steps` | Yes | — | Step map. Order is controlled by transitions, not file order. |
|
|
140
|
+
| `maxStepVisits` | No | `5` | Loop guard for each step. |
|
|
141
|
+
| `summaryMaxChars` | No | `4000` | Maximum step `summary` length. Reviewed gate artifacts use their separate 200,000-character limit. |
|
|
142
|
+
|
|
143
|
+
Each step supports:
|
|
144
|
+
|
|
145
|
+
| Field | Required | Description |
|
|
146
|
+
| ------------- | -------- | ----------------------------------------------------------------------- |
|
|
147
|
+
| `title` | No | Human-readable name. Defaults to the step identifier. |
|
|
148
|
+
| `prompt` | Yes | Inline text or `{ "file": "relative/path.md" }`. |
|
|
149
|
+
| `subagent` | No | Opt into pi-subagents delegation and configure its profile and budgets. |
|
|
150
|
+
| `permissions` | No | Resources callable during this step. Everything defaults to denied. |
|
|
151
|
+
| `requires` | No | Dependencies that must be detectable before the step starts. |
|
|
152
|
+
| `transitions` | Yes | Exact outcome to next step, `$pause`, or `$done`. |
|
|
153
|
+
| `gate` | No | Built-in prompt or Plannotator human-review gate. |
|
|
154
|
+
|
|
155
|
+
Supported prompt variables:
|
|
156
|
+
|
|
157
|
+
```text
|
|
158
|
+
{{workflow.input}}
|
|
159
|
+
{{workflow.id}}
|
|
160
|
+
{{run.id}}
|
|
161
|
+
{{step.id}}
|
|
162
|
+
{{step.title}}
|
|
163
|
+
{{last.summary}}
|
|
164
|
+
{{gate.feedback}}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Unknown variables fail configuration loading.
|
|
168
|
+
|
|
169
|
+
`{{last.summary}}` normally contains the previous completed step's handoff.
|
|
170
|
+
After a step-requested `$pause`, it contains both the preserved incoming
|
|
171
|
+
approved/previous-step handoff and the latest paused-attempt summary.
|
|
172
|
+
|
|
173
|
+
### Per-step subagents
|
|
174
|
+
|
|
175
|
+
Omit `subagent` to execute the step entirely in the main Pi agent. This is the
|
|
176
|
+
portable default and requires no other extension:
|
|
177
|
+
|
|
178
|
+
```yaml
|
|
179
|
+
steps:
|
|
180
|
+
inspect:
|
|
181
|
+
prompt: Inspect the request.
|
|
182
|
+
permissions:
|
|
183
|
+
tools: [read]
|
|
184
|
+
transitions:
|
|
185
|
+
done: $done
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Add `subagent: {}` to delegate through the public
|
|
189
|
+
`pi-subagents/delegation` v1 API with safe defaults:
|
|
190
|
+
|
|
191
|
+
```yaml
|
|
192
|
+
steps:
|
|
193
|
+
inspect:
|
|
194
|
+
subagent: {}
|
|
195
|
+
prompt: Inspect the request.
|
|
196
|
+
transitions:
|
|
197
|
+
done: $done
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The expanded defaults are `agent: pi-workflows.step`, `context: fresh`,
|
|
201
|
+
`timeoutMs: 900000`, and `artifacts: false`.
|
|
202
|
+
|
|
203
|
+
Use a Pi Subagents runtime name directly when only the agent changes:
|
|
204
|
+
|
|
205
|
+
```yaml
|
|
206
|
+
steps:
|
|
207
|
+
inspect:
|
|
208
|
+
subagent: scout
|
|
209
|
+
prompt: Inspect the request.
|
|
210
|
+
transitions:
|
|
211
|
+
done: $done
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
This name-only form inherits the same defaults. Use the object form when the
|
|
215
|
+
step also needs a context, model, timeout, budget, or artifact override:
|
|
216
|
+
|
|
217
|
+
```yaml
|
|
218
|
+
subagent:
|
|
219
|
+
agent: reviewer
|
|
220
|
+
context: fresh
|
|
221
|
+
timeoutMs: 600000
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
`agent` is the same runtime name Pi Subagents uses. For example,
|
|
225
|
+
`subagent: worker` selects its builtin `worker`, then Pi Subagents applies the
|
|
226
|
+
matching `subagents.agentOverrides.worker` entry from
|
|
227
|
+
`~/.pi/agent/settings.json` and any higher-precedence project settings. Pi
|
|
228
|
+
Workflows does not parse that file or reimplement agent discovery. An
|
|
229
|
+
`agentOverrides` entry modifies a discovered builtin, package, user, or project
|
|
230
|
+
agent; the entry alone does not create a new agent.
|
|
231
|
+
|
|
232
|
+
This is separate from `~/.pi/agent/workflows/settings.yaml`, which configures
|
|
233
|
+
Pi Workflows project trust and permission ceilings. Use
|
|
234
|
+
`/subagents-models worker` to inspect Pi Subagents' live resolved profile and
|
|
235
|
+
`/subagents-doctor` to diagnose discovery or loading problems.
|
|
236
|
+
|
|
237
|
+
The bundled `pi-workflows.step` remains the default for `subagent: {}`. It
|
|
238
|
+
inherits project instructions but not the parent transcript or its skill
|
|
239
|
+
catalog. A named agent contributes its Pi Subagents system prompt, thinking,
|
|
240
|
+
model unless the step overrides it, extension loading, and initial tool
|
|
241
|
+
visibility. The workflow sends its configured step prompt and explicit skill
|
|
242
|
+
selection.
|
|
243
|
+
|
|
244
|
+
Supported fields:
|
|
245
|
+
|
|
246
|
+
| Field | Default | Description |
|
|
247
|
+
| ------------ | -------------------- | ----------------------------------------------------------------------------------- |
|
|
248
|
+
| `agent` | `pi-workflows.step` | Any discovered Pi Subagents runtime name, such as `scout`, `worker`, or `reviewer`. |
|
|
249
|
+
| `context` | `fresh` | `fresh` isolates the step; `fork` deliberately includes filtered parent context. |
|
|
250
|
+
| `model` | Agent/default model | Optional pi-subagents model override. |
|
|
251
|
+
| `timeoutMs` | `900000` | Child deadline, from 1 second through 24 hours. |
|
|
252
|
+
| `turnBudget` | pi-subagents default | `{ "maxTurns": n, "graceTurns": n }`. |
|
|
253
|
+
| `toolBudget` | pi-subagents default | `{ "soft": n, "hard": n, "block": "*" }`; `block` may instead be a tool-name array. |
|
|
254
|
+
| `artifacts` | `false` | Ask pi-subagents to retain its normal run artifacts. |
|
|
255
|
+
|
|
256
|
+
Builtin names are unqualified (`worker`); packaged names may be qualified, such
|
|
257
|
+
as `pi-workflows.step`. Pi Workflows installs an inert listener in every
|
|
258
|
+
Pi Subagents child. It registers `workflow_complete_step` and activates policy
|
|
259
|
+
only after a valid, single-use workflow capability arrives, so ordinary
|
|
260
|
+
subagent runs remain unchanged.
|
|
261
|
+
|
|
262
|
+
A selected profile's active tools and loaded extensions remain an outer
|
|
263
|
+
boundary. Effective step tools are the intersection of that profile and
|
|
264
|
+
`permissions`, plus the workflow completion tool. Pi Workflows can remove
|
|
265
|
+
access but cannot grant a normal tool or load an extension excluded by the
|
|
266
|
+
profile. If a custom profile declares `extensions`, it must keep the installed
|
|
267
|
+
Pi Workflows extension available; otherwise the child never receives the
|
|
268
|
+
policy runtime and the step fails closed.
|
|
269
|
+
|
|
270
|
+
Workflow `permissions.skills` is sent as Pi Subagents' request-level skill
|
|
271
|
+
selection, so it replaces the selected profile's normal skill list for that
|
|
272
|
+
step; an empty list disables injected skills. Pi Workflows also disables Pi
|
|
273
|
+
Subagents' separate acceptance report for these requests because the harness
|
|
274
|
+
already owns correlated completion, declared outcomes, and optional human
|
|
275
|
+
review gates.
|
|
276
|
+
|
|
277
|
+
At runtime:
|
|
278
|
+
|
|
279
|
+
1. The harness creates a correlated child policy and result channel.
|
|
280
|
+
2. pi-subagents starts one foreground child for the current step.
|
|
281
|
+
3. The child runtime enforces permissions and writes the validated result.
|
|
282
|
+
4. The parent harness applies the configured transition, then launches the next step.
|
|
283
|
+
|
|
284
|
+
Main-agent mode uses the same per-step tool, MCP, Bash, extension-tool, and
|
|
285
|
+
completion enforcement, but it cannot unload globally visible skills or
|
|
286
|
+
extension event handlers from the parent process. Use pi-subagents when fresh
|
|
287
|
+
context, skill isolation, process separation, model selection, or turn/tool
|
|
288
|
+
budgets matter.
|
|
289
|
+
|
|
290
|
+
### Per-step permissions
|
|
291
|
+
|
|
292
|
+
`tools` contains exact Pi tool names available to the active step.
|
|
293
|
+
|
|
294
|
+
`mcp` contains `server` or `server/tool` selectors for the generic `mcp` proxy:
|
|
295
|
+
|
|
296
|
+
```yaml
|
|
297
|
+
mcp:
|
|
298
|
+
- gitlab/get_merge_request
|
|
299
|
+
- gitlab/list_merge_request_discussions
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
The harness requires an explicit `server` and `tool` on every proxy call. Proxy search, discovery, connection, and authentication modes are blocked while a workflow step is running.
|
|
303
|
+
|
|
304
|
+
The generic `mcp` proxy is the portable choice for workflow steps. A custom
|
|
305
|
+
subagent profile may expose direct MCP tools through its own `mcp:` frontmatter;
|
|
306
|
+
Pi Workflows still requires each direct runtime name in `tools`.
|
|
307
|
+
|
|
308
|
+
`extensions` contains case-insensitive fragments matched against tool source metadata. Tools registered by matching extensions become visible and callable:
|
|
309
|
+
|
|
310
|
+
```yaml
|
|
311
|
+
extensions: [some-extension]
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The MCP adapter is excluded from this broad extension rule. Grant proxy access through `mcp`, and grant a direct MCP tool only by its exact name in `tools`.
|
|
315
|
+
|
|
316
|
+
For delegated steps, `skills` states which skills pi-subagents injects into the
|
|
317
|
+
child. In main-agent mode it documents and preflights the intended skills, but
|
|
318
|
+
Pi cannot hide other globally loaded skill text. Put mandatory resources under
|
|
319
|
+
`requires`:
|
|
320
|
+
|
|
321
|
+
```yaml
|
|
322
|
+
permissions:
|
|
323
|
+
skills: [superpowers:test-driven-development]
|
|
324
|
+
extensions: [plannotator]
|
|
325
|
+
requires:
|
|
326
|
+
skills: [superpowers:test-driven-development]
|
|
327
|
+
extensions: [plannotator]
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The harness preflights required resources, passes selected skills to
|
|
331
|
+
pi-subagents when delegation is enabled, restricts active tools, and authorizes
|
|
332
|
+
every model tool call. Loaded extension event handlers still execute in their
|
|
333
|
+
process; the tool policy does not unload extension code.
|
|
334
|
+
|
|
335
|
+
### Bash modes
|
|
336
|
+
|
|
337
|
+
| Mode | Behavior |
|
|
338
|
+
| -------------- | ---------------------------------------------------------------------------------------- |
|
|
339
|
+
| `deny` | Blocks Bash. This is the default. |
|
|
340
|
+
| `read-only` | Allows a small built-in inspection preset. Shell composition and expansion are rejected. |
|
|
341
|
+
| `allow-list` | Allows one executable plus configured argument prefixes. |
|
|
342
|
+
| `unrestricted` | Allows any Bash command. Use only in user-owned workflows. |
|
|
343
|
+
|
|
344
|
+
Restricted modes reject shell operators, substitutions, expansions, wrapper shells, environment assignments, and known execution options in the read-only preset.
|
|
345
|
+
|
|
346
|
+
Allow-list entries are OR alternatives. `argsPrefix` is one ordered token
|
|
347
|
+
sequence:
|
|
348
|
+
|
|
349
|
+
```yaml
|
|
350
|
+
mode: allow-list
|
|
351
|
+
allow:
|
|
352
|
+
- executable: git
|
|
353
|
+
argsPrefix: [status]
|
|
354
|
+
- executable: bun
|
|
355
|
+
argsPrefix: [test]
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
This permits `bun test --runInBand` but not `bun run build`.
|
|
359
|
+
|
|
360
|
+
Use `argsPrefixes` to merge several alternatives for one executable without
|
|
361
|
+
widening permission:
|
|
362
|
+
|
|
363
|
+
```yaml
|
|
364
|
+
mode: allow-list
|
|
365
|
+
allow:
|
|
366
|
+
- executable: git
|
|
367
|
+
argsPrefixes: [[status], [diff], [show, --stat]]
|
|
368
|
+
- executable: gh
|
|
369
|
+
argsPrefixes: [[pr, view], [pr, diff], [api]]
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
The inner arrays are OR alternatives. Tokens inside one inner array are an
|
|
373
|
+
ordered prefix. Therefore `argsPrefix: [status, diff]` means the literal
|
|
374
|
+
sequence `git status diff`; it does not mean “status or diff.” `argsPrefix` and
|
|
375
|
+
`argsPrefixes` are mutually exclusive in one rule. Omitting both allows that
|
|
376
|
+
executable with any safely tokenized arguments.
|
|
377
|
+
|
|
378
|
+
An allow-list may also import exact command strings from the run's most recent
|
|
379
|
+
human-reviewed gate artifact:
|
|
380
|
+
|
|
381
|
+
```yaml
|
|
382
|
+
mode: allow-list
|
|
383
|
+
allow:
|
|
384
|
+
- executable: git
|
|
385
|
+
argsPrefix: [status]
|
|
386
|
+
approvedSources: [verification-worker]
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Supported sources are:
|
|
390
|
+
|
|
391
|
+
| Source | Reviewed JSON path |
|
|
392
|
+
| ----------------------- | --------------------------------------------------------------------- |
|
|
393
|
+
| `verification-worker` | `repositories[].worker[].command` |
|
|
394
|
+
| `verification-reviewer` | `repositories[].reviewer[].command` |
|
|
395
|
+
| `remote-actions` | `actions[]` where `toolName` is `bash` and `input.command` is present |
|
|
396
|
+
|
|
397
|
+
The JSON must be the whole reviewed artifact or appear in a fenced `json`
|
|
398
|
+
block. The harness copies only exact strings into the correlated step policy.
|
|
399
|
+
Verification sources reject shell wrappers, remote-transfer programs,
|
|
400
|
+
`gh`/`glab`, publishing commands, and non-local Git operations. Remote actions
|
|
401
|
+
accept only `gh api`, `glab api`, or non-force `git push`. A model cannot widen
|
|
402
|
+
an approved command by adding arguments or shell composition.
|
|
403
|
+
|
|
404
|
+
Approved sources fail closed until a gate has actually been approved. Ordinary
|
|
405
|
+
step summaries never become command provenance. Legacy v1 checkpoints remain
|
|
406
|
+
readable, but they receive no reviewed-command capabilities until a new gate
|
|
407
|
+
produces an approved artifact.
|
|
408
|
+
|
|
409
|
+
Static allow-list rules for `gh api` and `glab api` are GET-only: mutation
|
|
410
|
+
flags such as fields, input, forms, or an explicit method are blocked. A
|
|
411
|
+
mutating API call therefore needs an exact `remote-actions` command from a
|
|
412
|
+
reviewed artifact.
|
|
413
|
+
|
|
414
|
+
#### How `approvedSources` works
|
|
415
|
+
|
|
416
|
+
`approvedSources` does not allow an executable, run a command, or read from the
|
|
417
|
+
current step summary. It tells the harness which fixed field in the most recent
|
|
418
|
+
human-approved artifact may contribute exact command strings. Approval may
|
|
419
|
+
come from the built-in Pi prompt gate or Plannotator.
|
|
420
|
+
|
|
421
|
+
For example, suppose the approved artifact contains:
|
|
422
|
+
|
|
423
|
+
```json
|
|
424
|
+
{
|
|
425
|
+
"repositories": [
|
|
426
|
+
{
|
|
427
|
+
"worker": [{ "command": "bun test" }]
|
|
428
|
+
}
|
|
429
|
+
]
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
A later step with `approvedSources: [verification-worker]` may run exactly
|
|
434
|
+
`bun test`. It may not run `bun test --watch`, because that is a different
|
|
435
|
+
string. `verification-reviewer` reads the sibling `reviewer` list instead.
|
|
436
|
+
`remote-actions` reads only Bash actions from `actions[]` and additionally
|
|
437
|
+
filters them to supported hosted-API mutations or non-force pushes.
|
|
438
|
+
|
|
439
|
+
The complete path is:
|
|
440
|
+
|
|
441
|
+
```text
|
|
442
|
+
step artifact -> human approval -> persisted reviewed artifact
|
|
443
|
+
-> source-specific extraction -> correlated step policy -> exact string check
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
If no gate has been approved, the selected field is absent, the command is
|
|
447
|
+
unsafe for that source, or the string differs at all, no permission is added.
|
|
448
|
+
|
|
449
|
+
Hard turn and tool-call budgets are best for read-only inspection and
|
|
450
|
+
verification steps. For a step that edits files, use a generous timeout and no
|
|
451
|
+
hard count budget unless partial edits are acceptable; inspect the working tree
|
|
452
|
+
after any interruption before resuming.
|
|
453
|
+
|
|
454
|
+
## Built-in review gate
|
|
455
|
+
|
|
456
|
+
Human review works without Plannotator. Omit `provider` (or set it to `prompt`)
|
|
457
|
+
to use Pi's built-in prompt panel:
|
|
458
|
+
|
|
459
|
+
```yaml
|
|
460
|
+
gate:
|
|
461
|
+
submitOutcome: submit
|
|
462
|
+
approvedOutcome: approved
|
|
463
|
+
rejectedOutcome: changes-requested
|
|
464
|
+
transitions:
|
|
465
|
+
approved: implement
|
|
466
|
+
changes-requested: plan
|
|
467
|
+
blocked: $pause
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
When the step completes with outcome `submit`, it must include the full content
|
|
471
|
+
in `artifact`. Pi shows Approve, Request changes, and Pause workflow. Requested
|
|
472
|
+
changes are returned through `{{gate.feedback}}`; approval persists the
|
|
473
|
+
artifact as the reviewed handoff. Dismissing the panel pauses the workflow and
|
|
474
|
+
keeps the pending artifact, so `/workflow-resume` reopens the same review.
|
|
475
|
+
|
|
476
|
+
Dialog-capable UI is available in Pi TUI and RPC modes. In print or JSON mode,
|
|
477
|
+
the gate pauses safely until resumed in TUI or RPC.
|
|
478
|
+
|
|
479
|
+
## Works great with Plannotator
|
|
480
|
+
|
|
481
|
+
[Plannotator](https://github.com/backnotprop/plannotator) gives Pi a local,
|
|
482
|
+
browser-based surface for visually reviewing and annotating plans. It is
|
|
483
|
+
optional, but highly recommended for rich plan feedback. Install its Pi
|
|
484
|
+
extension alongside Pi Workflows:
|
|
485
|
+
|
|
486
|
+
```bash
|
|
487
|
+
pi install npm:@plannotator/pi-extension
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
Pi Workflows uses Plannotator's shared extension API as a human approval gate:
|
|
491
|
+
|
|
492
|
+
- A workflow submits its plan or other Markdown artifact.
|
|
493
|
+
- Plannotator opens the visual review in your browser.
|
|
494
|
+
- Approval advances through the configured transition.
|
|
495
|
+
- Requested changes return structured feedback to the configured revision step.
|
|
496
|
+
- Pausing never discards a decision; resume queries the same review identifier.
|
|
497
|
+
|
|
498
|
+
This keeps workflow order and permissions declarative while Plannotator handles
|
|
499
|
+
the human review experience.
|
|
500
|
+
|
|
501
|
+
### Configure a Plannotator gate
|
|
502
|
+
|
|
503
|
+
A step can submit an artifact to the installed Plannotator extension:
|
|
504
|
+
|
|
505
|
+
```yaml
|
|
506
|
+
gate:
|
|
507
|
+
provider: plannotator
|
|
508
|
+
submitOutcome: submit
|
|
509
|
+
approvedOutcome: approved
|
|
510
|
+
rejectedOutcome: changes-requested
|
|
511
|
+
timeoutMs: 5000
|
|
512
|
+
transitions:
|
|
513
|
+
approved: implement
|
|
514
|
+
changes-requested: plan
|
|
515
|
+
blocked: $pause
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
Setting `provider: plannotator` is the entire opt-in; the harness preflights the
|
|
519
|
+
installed extension automatically. The active step calls
|
|
520
|
+
`workflow_complete_step` with outcome `submit` and the full content in
|
|
521
|
+
`artifact`. The harness correlates the Plannotator review identifier and
|
|
522
|
+
accepts only the matching decision. On approval, that reviewed artifact—not
|
|
523
|
+
the step's separate summary—becomes the authoritative handoff to the next step.
|
|
524
|
+
|
|
525
|
+
If review finishes while the workflow is paused, the result is checkpointed and applied only after `/workflow-resume`. Resume also queries Plannotator’s durable review status, so a decision made while Pi was closed is not lost.
|
|
526
|
+
|
|
527
|
+
## Pause, repair, resume
|
|
528
|
+
|
|
529
|
+
Use:
|
|
530
|
+
|
|
531
|
+
```text
|
|
532
|
+
/workflow-pause <optional reason>
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
The harness stops the active main-agent turn, dismisses a built-in review, or
|
|
536
|
+
sends the versioned pi-subagents cancellation event for a delegated step. It
|
|
537
|
+
keeps the exact current step and pending gate, then persists the checkpoint in
|
|
538
|
+
the Pi session. A late completion cannot advance a paused, aborted,
|
|
539
|
+
reconfigured, or replaced run.
|
|
540
|
+
|
|
541
|
+
When a step itself transitions to `$pause`, the checkpoint keeps both the
|
|
542
|
+
incoming reviewed/previous-step handoff and the latest failed-attempt summary.
|
|
543
|
+
The resumed execution sees both. Exact reviewed commands continue to derive only
|
|
544
|
+
from the separately persisted reviewed artifact, never from the failed attempt
|
|
545
|
+
or a legacy unreviewed summary.
|
|
546
|
+
|
|
547
|
+
For a delegated step, if child termination is not confirmed within five
|
|
548
|
+
seconds, the pause is recorded but main tools remain isolated and resume is
|
|
549
|
+
blocked. Wait for the terminal event; if the delegation channel has already
|
|
550
|
+
failed, restart Pi before resuming. This prevents an old writer and a resumed
|
|
551
|
+
writer from overlapping.
|
|
552
|
+
|
|
553
|
+
While paused, fix repository code, workflow YAML, prompts, MCP configuration,
|
|
554
|
+
an extension, or any other environmental problem. Then run:
|
|
555
|
+
|
|
556
|
+
```text
|
|
557
|
+
/workflow-resume
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
Resume reloads configuration before continuing:
|
|
561
|
+
|
|
562
|
+
- The paused step restarts in its configured main-agent or delegated mode.
|
|
563
|
+
- A changed current step restarts that step.
|
|
564
|
+
- A changed completed step restarts the earliest changed completed step.
|
|
565
|
+
- Future-only changes preserve the current checkpoint.
|
|
566
|
+
- Removing the current or a completed step fails closed and requires restoring configuration or aborting.
|
|
567
|
+
- Restoring a Pi session automatically pauses an in-progress workflow for inspection.
|
|
568
|
+
- A pending built-in prompt review reopens with the same artifact.
|
|
569
|
+
- An interrupted Plannotator submission without a review identifier restarts the current step for resubmission.
|
|
570
|
+
|
|
571
|
+
## Commands
|
|
572
|
+
|
|
573
|
+
In TUI mode, `/workflow-status` opens a read-only board for the current
|
|
574
|
+
checkpoint. It refreshes once per second and shows run timing, the current
|
|
575
|
+
execution or review, pause reasons, configuration drift, and the completed
|
|
576
|
+
attempt path. Press `q`, `Esc`, `Ctrl-C`, or `Ctrl-D` to close it. Non-TUI modes
|
|
577
|
+
receive the same checkpoint as text.
|
|
578
|
+
|
|
579
|
+
| Command | Purpose |
|
|
580
|
+
| ------------------------------- | ----------------------------------------------------- |
|
|
581
|
+
| `/workflow-list` | List loaded workflows and their configured commands. |
|
|
582
|
+
| `/workflow-start <id> [input]` | Start by workflow identifier. |
|
|
583
|
+
| `/<configured-command> [input]` | Start through a workflow alias. |
|
|
584
|
+
| `/workflow-status` | Open a live run-status board (text outside TUI mode). |
|
|
585
|
+
| `/workflow-pause [reason]` | Halt without losing the checkpoint. |
|
|
586
|
+
| `/workflow-resume` | Reload, reconcile, and continue. |
|
|
587
|
+
| `/workflow-abort [reason]` | End the active run and restore baseline tools. |
|
|
588
|
+
| `/workflow-reload` | Reload definitions while no workflow is running. |
|
|
589
|
+
|
|
590
|
+
## User and project configuration
|
|
591
|
+
|
|
592
|
+
User workflows are loaded first. A project may add workflows from:
|
|
593
|
+
|
|
594
|
+
```text
|
|
595
|
+
<project>/.pi/workflows/*.workflow.yaml
|
|
596
|
+
<project>/.pi/workflows/*.workflow.yml
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
Project workflows are disabled by default. Enable them in the user-owned
|
|
600
|
+
`~/.pi/agent/workflows/settings.yaml`:
|
|
601
|
+
|
|
602
|
+
```yaml
|
|
603
|
+
# yaml-language-server: $schema=/absolute/path/to/pi-workflows/schemas/settings.schema.json
|
|
604
|
+
version: 1
|
|
605
|
+
allowProjectWorkflows: true
|
|
606
|
+
permissionCeiling:
|
|
607
|
+
tools: [read, grep, bash]
|
|
608
|
+
mcp: []
|
|
609
|
+
extensions: []
|
|
610
|
+
skills: []
|
|
611
|
+
bash: { mode: read-only }
|
|
612
|
+
subagent:
|
|
613
|
+
agents: [pi-workflows.step, scout, worker, reviewer]
|
|
614
|
+
contexts: [fresh]
|
|
615
|
+
models: []
|
|
616
|
+
maxTimeoutMs: 900000
|
|
617
|
+
maxTurns: 40
|
|
618
|
+
maxGraceTurns: 3
|
|
619
|
+
maxToolCalls: 100
|
|
620
|
+
artifacts: false
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
Settings use the same strict YAML 1.2 parser as workflow definitions. The
|
|
624
|
+
`settings.schema.json` file remains JSON Schema so YAML-aware editors can
|
|
625
|
+
validate `settings.yaml`; adjust or remove the schema comment for your install
|
|
626
|
+
path.
|
|
627
|
+
|
|
628
|
+
Project workflows load only when Pi trusts the project and every step stays
|
|
629
|
+
within this ceiling. The `subagent` ceiling is optional for main-only project
|
|
630
|
+
workflows; if omitted, any project step that declares `subagent` is rejected.
|
|
631
|
+
Each delegated project step must declare `turnBudget` and `toolBudget` with
|
|
632
|
+
`"block": "*"`, so it cannot silently inherit unbounded child defaults or keep
|
|
633
|
+
mutation tools after reaching the hard limit. The ceiling also controls child
|
|
634
|
+
agent names, context inheritance, model overrides, timeouts, artifact
|
|
635
|
+
retention, and the Bash rules and approved sources that a project workflow may
|
|
636
|
+
request. Project workflows cannot override user workflow identifiers or
|
|
637
|
+
commands.
|
|
638
|
+
|
|
639
|
+
## Architecture
|
|
640
|
+
|
|
641
|
+
The package keeps the Pi entry point intentionally small:
|
|
642
|
+
|
|
643
|
+
| Module | Responsibility |
|
|
644
|
+
| --------------------------------- | --------------------------------------------------------------------- |
|
|
645
|
+
| `src/index.ts` | Pi entry point only. |
|
|
646
|
+
| `src/harness.ts` | Runtime orchestration and session lifecycle. |
|
|
647
|
+
| `src/commands.ts` | User command surface. |
|
|
648
|
+
| `src/config/` | Types, strict validation, prompt loading, precedence, ceilings. |
|
|
649
|
+
| `src/engine/` | Serializable run state and deterministic transitions. |
|
|
650
|
+
| `src/policy/` | Tool, MCP, and Bash enforcement. |
|
|
651
|
+
| `src/policy/approved-commands.ts` | Filtered exact-command extraction from human-reviewed JSON artifacts. |
|
|
652
|
+
| `src/integrations/subagents/` | Delegation client, child protocol, and child policy runtime. |
|
|
653
|
+
| `src/integrations/plannotator.ts` | Versioned Plannotator gate adapter. |
|
|
654
|
+
| `src/integrations/prompt-gate.ts` | Built-in Pi prompt review adapter. |
|
|
655
|
+
| `src/runtime/` | Shared completion parsing and main-agent step runtime. |
|
|
656
|
+
| `src/preflight.ts` | Required tool, extension, and skill checks. |
|
|
657
|
+
| `src/prompt.ts` | Template rendering and step contract. |
|
|
658
|
+
| `agents/step.md` | Bundled dynamic-policy pi-subagents profile. |
|
|
659
|
+
|
|
660
|
+
The engine and policy modules do not depend on Pi runtime types, so they are fast to test.
|
|
661
|
+
|
|
662
|
+
## Security model
|
|
663
|
+
|
|
664
|
+
Pi extensions are not an operating-system sandbox. Installed extensions execute with the user’s process permissions.
|
|
665
|
+
|
|
666
|
+
The harness provides model-level least privilege in both execution modes, plus
|
|
667
|
+
process separation when a step opts into pi-subagents:
|
|
668
|
+
|
|
669
|
+
- active-tool narrowing for every step;
|
|
670
|
+
- authoritative `tool_call` blocking and immutable authorized arguments;
|
|
671
|
+
- completion as the sole call in its tool batch;
|
|
672
|
+
- optional separate pi-subagents child process per delegated step;
|
|
673
|
+
- an idle, tool-isolated main agent while a delegated step runs;
|
|
674
|
+
- a single-use, parent-created child capability tied to the selected workflow agent;
|
|
675
|
+
- explicit MCP server and tool checks;
|
|
676
|
+
- restricted Bash parsing;
|
|
677
|
+
- exact Bash capabilities derived from a correlated human-reviewed artifact;
|
|
678
|
+
- project trust and a user-owned permission ceiling;
|
|
679
|
+
- fail-closed durable state, correlated child results, and correlated gate results.
|
|
680
|
+
|
|
681
|
+
It does not restrict commands the human explicitly runs with Pi’s `!` Bash
|
|
682
|
+
input. In main-agent mode it cannot hide globally loaded skills or isolate the
|
|
683
|
+
transcript. In either mode it cannot disable side effects performed
|
|
684
|
+
autonomously by a loaded extension. Review workflow, agent, and extension
|
|
685
|
+
source before installing or enabling it.
|
|
686
|
+
|
|
687
|
+
Step completion is structurally validated—policy digest, declared outcome,
|
|
688
|
+
non-empty bounded summary, required gate artifact, and sole completion call—but
|
|
689
|
+
the harness cannot prove that a model's semantic claims or test evidence are
|
|
690
|
+
true. Put exact checks in reviewed command contracts, use an independent
|
|
691
|
+
verification step, and keep consequential actions behind a human gate.
|
|
692
|
+
|
|
693
|
+
The harness does not provide exactly-once external effects. If a publish step
|
|
694
|
+
is interrupted after a remote action succeeds but before it checkpoints, a
|
|
695
|
+
resumed execution receives the same approved capability. Publish prompts should
|
|
696
|
+
query the remote effect first, skip only proven-complete actions, and pause on
|
|
697
|
+
ambiguous state.
|
|
698
|
+
|
|
699
|
+
## Development
|
|
700
|
+
|
|
701
|
+
```bash
|
|
702
|
+
bun install
|
|
703
|
+
bun run check
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
Tests cover graph validation, prompt confinement, command conflicts, project
|
|
707
|
+
ceilings, deterministic transitions, configuration reconciliation, pause/resume
|
|
708
|
+
state, gate handling, MCP isolation, Bash policy, extension tool selection,
|
|
709
|
+
main-agent completion, built-in feedback/approval, subagent request correlation
|
|
710
|
+
and cancellation, child policy enforcement, and dependency preflight,
|
|
711
|
+
including reviewed exact-command propagation and fail-closed legacy
|
|
712
|
+
checkpoints.
|
|
713
|
+
|
|
714
|
+
## Publishing checklist
|
|
715
|
+
|
|
716
|
+
Before publishing:
|
|
717
|
+
|
|
718
|
+
1. Confirm the package name and repository metadata.
|
|
719
|
+
2. Run `bun run check`.
|
|
720
|
+
3. Merge a Conventional Commit PR and verify its GitHub Release artifact.
|
|
721
|
+
|
|
722
|
+
The `pi-package` keyword makes the package discoverable by the Pi package gallery.
|
|
723
|
+
|
|
724
|
+
## Good next parameters
|
|
725
|
+
|
|
726
|
+
The current schema covers the execution harness requested here. Useful future
|
|
727
|
+
extensions, without hard-coding them into the orchestrator, are:
|
|
728
|
+
|
|
729
|
+
| Parameter | Why it belongs in configuration |
|
|
730
|
+
| --------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
731
|
+
| Retry and backoff | Let a step distinguish a transient child failure from a workflow-level pause. |
|
|
732
|
+
| Acceptance criteria | Give each step machine-checkable completion evidence and verification commands. |
|
|
733
|
+
| Working directory or worktree | Isolate mutating steps, monorepo packages, and concurrent branches. |
|
|
734
|
+
| Parallel groups and join policy | Run independent steps together and declare fail-fast, quorum, or all-success behavior. |
|
|
735
|
+
| Generic gates | Add ticket, CI, chat, or custom approval providers behind the same versioned gate contract. |
|
|
736
|
+
| Output schema and named artifacts | Pass structured data between steps instead of relying only on a summary. |
|
|
737
|
+
| Cost and token ceilings | Bound model spend independently from turn and tool-call budgets. |
|
|
738
|
+
| Environment and secret references | Select named credentials without embedding secret values in workflow files. |
|
|
739
|
+
| Logging and retention | Configure progress events, redaction, child artifact retention, and checkpoint history. |
|
|
740
|
+
|
|
741
|
+
## Current limits
|
|
742
|
+
|
|
743
|
+
- A delegated workflow step uses one foreground subagent. Parallel or chained children inside one step are not yet a workflow-level primitive.
|
|
744
|
+
- Gate providers are built-in prompt and Plannotator; custom providers are not yet configurable.
|
|
745
|
+
- A custom subagent profile can be narrower than a step, but Pi Workflows cannot widen that profile.
|
|
746
|
+
- Extension tools are enforced; autonomous extension event-handler side effects cannot be disabled per step.
|
|
747
|
+
- Completion evidence is model-reported; use reviewed executable checks and a fresh verification step when correctness matters.
|
|
748
|
+
- Workflow configuration uses YAML. Prompt bodies may live in separate Markdown files.
|
|
749
|
+
|
|
750
|
+
## License
|
|
751
|
+
|
|
752
|
+
Licensed under the [Apache License 2.0](./LICENSE).
|