@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.
Files changed (45) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +752 -0
  3. package/agents/step.md +17 -0
  4. package/dist/index.js +4576 -0
  5. package/examples/mr-comments.workflow.yaml +115 -0
  6. package/examples/prompts/mr-comments/implement.md +8 -0
  7. package/examples/prompts/mr-comments/inspect.md +5 -0
  8. package/examples/prompts/mr-comments/plan.md +13 -0
  9. package/examples/prompts/mr-comments/verify.md +7 -0
  10. package/examples/settings.yaml +19 -0
  11. package/package.json +81 -0
  12. package/schemas/settings.schema.json +22 -0
  13. package/schemas/workflow.schema.json +585 -0
  14. package/src/command-names.ts +46 -0
  15. package/src/commands.ts +80 -0
  16. package/src/config/ceiling.ts +153 -0
  17. package/src/config/command-conflicts.ts +31 -0
  18. package/src/config/load.ts +327 -0
  19. package/src/config/types.ts +187 -0
  20. package/src/config/validate.ts +1145 -0
  21. package/src/digest.ts +23 -0
  22. package/src/engine/checkpoint.ts +30 -0
  23. package/src/engine/resume.ts +44 -0
  24. package/src/engine/state.ts +186 -0
  25. package/src/engine/transitions.ts +426 -0
  26. package/src/harness.ts +1676 -0
  27. package/src/index.ts +15 -0
  28. package/src/integrations/plannotator.ts +235 -0
  29. package/src/integrations/prompt-gate.ts +54 -0
  30. package/src/integrations/subagents/child-runtime.ts +306 -0
  31. package/src/integrations/subagents/client.ts +239 -0
  32. package/src/integrations/subagents/protocol.ts +304 -0
  33. package/src/policy/approved-commands.ts +225 -0
  34. package/src/policy/bash.ts +355 -0
  35. package/src/policy/completion-batch.ts +36 -0
  36. package/src/policy/immutable-input.ts +18 -0
  37. package/src/policy/tools.ts +150 -0
  38. package/src/preflight.ts +76 -0
  39. package/src/prompt.ts +146 -0
  40. package/src/runtime/completion-tool.ts +22 -0
  41. package/src/runtime/main-step-runtime.ts +227 -0
  42. package/src/runtime/serial-task-queue.ts +17 -0
  43. package/src/runtime/step-result.ts +85 -0
  44. package/src/workflow-list.ts +25 -0
  45. package/src/workflow-status.ts +611 -0
package/README.md ADDED
@@ -0,0 +1,752 @@
1
+ [![codecov](https://codecov.io/gh/wichayutdew/pi-workflows/graph/badge.svg?token=33xrCBRM82)](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).