pi-taskflow 0.0.17 → 0.0.19
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/CHANGELOG.md +48 -0
- package/README.md +57 -24
- package/README.zh-CN.md +50 -17
- package/examples/dynamic-plan-execute.json +34 -0
- package/examples/iterative-replan.json +30 -0
- package/extensions/index.ts +2 -2
- package/extensions/runtime.ts +169 -11
- package/extensions/schema.ts +56 -1
- package/extensions/store.ts +5 -1
- package/package.json +3 -4
- package/skills/taskflow/SKILL.md +146 -18
- package/skills/taskflow/configuration.md +115 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to pi-taskflow are documented here. This project follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format.
|
|
4
4
|
|
|
5
|
+
## [0.0.19] — 2026-06-10
|
|
6
|
+
|
|
7
|
+
### Documentation
|
|
8
|
+
- **Closed the SKILL coverage gap — the LLM can now author every shipped feature.** A schema-vs-SKILL.md audit (`docs/internal/skill-coverage-audit.md`, machine-checked + cross-adversarial reviewed) found several implemented + tested features that were undocumented in the LLM-facing skill, so the model never generated them. All ~46 user-facing schema fields are now documented across SKILL.md + configuration.md.
|
|
9
|
+
- **SKILL.md**: phase-type table now lists all 9 types (added `loop`, `tournament`) with a “details” column pointing each to its section; new **Loop phases** (`until`/`maxIterations`/`convergence`) and **Tournament phases** (`variants`/`judge`/`mode`/`judgeAgent`) sections; `eval` (zero-token machine gate) and `onBlock: "retry"` (self-healing rework loop) folded into the Gate section; cross-run `cache` pointer + `optional` + static `branches` notes.
|
|
10
|
+
- **SKILL.md**: new **Operating a run** section — run lifecycle (`running → completed/blocked/failed/paused`), cache-aware resume, when to resume vs. re-run, budget-mid-run behavior, and run inspection. Clarified action semantics (`define` vs `name`, save scope/collision, `verify`/`agents` actions).
|
|
11
|
+
- **configuration.md**: new **§2.1 Context pre-reading** (`context`/`contextLimit` — resolution order, per-file 8000-char cap, 200k total cap) and **§8 Cross-run caching** (`cache.scope`, `ttl`, full `fingerprint` prefix table for git/glob/glob!/file/env). Fixed a stale “5 phase types” → 9 cross-file drift.
|
|
12
|
+
- Every documented JSON example validates against the live schema; all run-status/resume claims verified against the runtime (`blocked` is terminal; `paused`/`failed` are resumable). 560 tests pass, zero regression.
|
|
13
|
+
|
|
14
|
+
### CI
|
|
15
|
+
- GitHub Packages publish is now best-effort (`continue-on-error`) so an unscoped-package 404 there can never block the npm publish or the GitHub Release.
|
|
16
|
+
|
|
17
|
+
## [0.0.18] — 2026-06-09
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- **Runtime dynamic sub-flows — `flow { def }`.** A `flow` phase may now carry an inline `def` (mutually exclusive with `use`) that is resolved at runtime — typically from an upstream phase's JSON output (`"def": "{steps.plan.json}"`) — validated, verified, and executed as a nested sub-flow. This is the declarative answer to code-mode `for`/`if`: a planner decides *at runtime* what work to spawn, and every generated plan is structurally checked (cycles / dangling refs / duplicate ids / dead-ends) before it spends a token.
|
|
21
|
+
- Accepts a full Taskflow `{name,phases}`, a bare `phases` array, or `{phases:[...]}` (markdown ```json fences tolerated). Pure data — no `eval`.
|
|
22
|
+
- **Iterative replanning**: pair with `loop` so round N's plan depends on round N-1's *result* (not a one-shot fan-out).
|
|
23
|
+
- **Fail-open**: a malformed/invalid/unverifiable def never aborts the run — the phase resolves as a no-op with a `defError` diagnostic and upstream output is preserved. An empty `phases` array is a valid no-op.
|
|
24
|
+
- New examples: `examples/dynamic-plan-execute.json`, `examples/iterative-replan.json`.
|
|
25
|
+
|
|
26
|
+
### Security
|
|
27
|
+
- **Hardening for runtime-generated (untrusted) sub-flows**, enforced only when content is LLM-authored:
|
|
28
|
+
- Breadth caps: `MAX_DYNAMIC_PHASES` (100), `MAX_DYNAMIC_CONCURRENCY` (16, flow- and phase-level), `MAX_DYNAMIC_MAP_ITEMS` (200, fan-out truncated not blocked).
|
|
29
|
+
- `cwd` containment: a generated phase cannot escape the run directory.
|
|
30
|
+
- Budget clamp: a generated def's budget is clamped to `min(child, parent)` per dimension — it can only ever be tighter, never looser.
|
|
31
|
+
- Nesting cap: `MAX_DYNAMIC_NESTING` (5) bounds inline self-spawning depth.
|
|
32
|
+
- Prototype-pollution defense: inline defs are deep-cloned and `__proto__`/`constructor`/`prototype` own-keys are stripped.
|
|
33
|
+
- Authored/saved flows (`use`) are unchanged and not subject to these dynamic caps.
|
|
34
|
+
|
|
35
|
+
### Notes
|
|
36
|
+
- 25 new tests (`test/flow-def.test.ts`); 560 total, zero regression. Design + two-round cross-adversarial review (engineering-risk / design-critic / architecture / security) recorded under `docs/internal/`.
|
|
37
|
+
|
|
38
|
+
## [0.0.17] — 2026-06-09
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
- **28 fixes from 3-round adversarial dogfooding across 11 files.**
|
|
42
|
+
- **store.ts**: validateRunId path-traversal guard in saveRun, cleanupTerminalRuns race condition mtime guard, saveFlow file locking (prevents concurrent write loss), saveFlow unified sanitization via safeFlowDirName, SharedArrayBuffer hoisted to module scope, empty flow name rejection, conditional .pi/ creation hint.
|
|
43
|
+
- **runner.ts**: signal kill detection (killedBySignal), idle timeout excluded from transient error retry, message cap (500) with truncation notice, stderr cap (64KB) with truncation notice.
|
|
44
|
+
- **runtime.ts**: loop abort semantics (stop: "aborted"), failed phase interpolation (sensible placeholder instead of raw template), tournament judge budget/abort guard, retry factor asymmetry documentation.
|
|
45
|
+
- **interpolate.ts**: tokenizer escaped quote handling (character-by-character loop), graceful dig() trailing path segment resolution.
|
|
46
|
+
- **index.ts**: /tf save and /tf verify tab completion, JSON string define parsing in renderCall label, escaped quote handling in parseArgsString.
|
|
47
|
+
- **agents.ts**: YAML tools type validation (reject non-string/array), atomic writeFileAtomic in syncBuiltinAgentsToProject.
|
|
48
|
+
- **cache.ts**: 30s timeout on execFileSync git calls.
|
|
49
|
+
- **verify.ts**: budget maxUSD overflow detection.
|
|
50
|
+
- **render.ts**: consistent numerator/denominator in summarizeRun.
|
|
51
|
+
- **runs-view.ts**: timeAgo negative timestamp guard, blocked status removed from isResumable.
|
|
52
|
+
|
|
5
53
|
## [0.0.16] — 2026-06-09
|
|
6
54
|
|
|
7
55
|
### Added
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="./assets/hero.png" alt="pi-taskflow — declarative
|
|
3
|
+
<img src="./assets/hero.png" alt="pi-taskflow — a declarative, verifiable graph of task nodes for Pi subagents: stateful, resumable, context-isolated" width="900">
|
|
4
4
|
|
|
5
5
|
<p>
|
|
6
6
|
<a href="https://www.npmjs.com/package/pi-taskflow"><img src="https://img.shields.io/npm/v/pi-taskflow?style=flat-square&color=B692FF&label=npm" alt="npm version"></a>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-43D9AD?style=flat-square" alt="MIT license"></a>
|
|
9
9
|
<a href="#whats-inside"><img src="https://img.shields.io/badge/runtime%20deps-0-43D9AD?style=flat-square" alt="zero runtime dependencies"></a>
|
|
10
10
|
<a href="https://github.com/heggria/pi-taskflow/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/heggria/pi-taskflow/ci.yml?branch=main&style=flat-square&label=CI" alt="CI status"></a>
|
|
11
|
-
<a href="#whats-inside"><img src="https://img.shields.io/badge/tests-
|
|
11
|
+
<a href="#whats-inside"><img src="https://img.shields.io/badge/tests-535-6E8BFF?style=flat-square" alt="535 tests"></a>
|
|
12
12
|
<a href="#whats-inside"><img src="https://img.shields.io/badge/dogfooded-%E2%9C%93-43D9AD?style=flat-square" alt="dogfooded"></a>
|
|
13
13
|
<a href="https://pi.dev"><img src="https://img.shields.io/badge/for-Pi%20coding%20agent-B692FF?style=flat-square" alt="for the Pi coding agent"></a>
|
|
14
14
|
</p>
|
|
@@ -16,16 +16,16 @@
|
|
|
16
16
|
<p align="center">
|
|
17
17
|
<b>English</b> ·
|
|
18
18
|
<a href="./README.zh-CN.md">简体中文</a> ·
|
|
19
|
-
<a href="./README.hi.md">हिन्दी</a> ·
|
|
20
|
-
<a href="./README.es.md">Español</a> ·
|
|
21
|
-
<a href="./README.ar.md">العربية</a> ·
|
|
22
|
-
<a href="./README.bn.md">বাংলা</a> ·
|
|
23
|
-
<a href="./README.pt.md">Português</a> ·
|
|
24
|
-
<a href="./README.ru.md">Русский</a>
|
|
19
|
+
<a href="./docs/i18n/README.hi.md">हिन्दी</a> ·
|
|
20
|
+
<a href="./docs/i18n/README.es.md">Español</a> ·
|
|
21
|
+
<a href="./docs/i18n/README.ar.md">العربية</a> ·
|
|
22
|
+
<a href="./docs/i18n/README.bn.md">বাংলা</a> ·
|
|
23
|
+
<a href="./docs/i18n/README.pt.md">Português</a> ·
|
|
24
|
+
<a href="./docs/i18n/README.ru.md">Русский</a>
|
|
25
25
|
</p>
|
|
26
26
|
|
|
27
|
-
<p><strong>
|
|
28
|
-
Fan out · gate · resume · save as a command — intermediate results stay out of your context.</p>
|
|
27
|
+
<p><strong>A declarative, verifiable <em>graph of tasks</em> for <a href="https://pi.dev">Pi</a> subagents.</strong><br/>
|
|
28
|
+
Not a workflow you script — a DAG you declare. Fan out · gate · resume · save as a command — intermediate results stay out of your context.</p>
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
pi install npm:pi-taskflow
|
|
@@ -35,23 +35,38 @@ pi install npm:pi-taskflow
|
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
**
|
|
38
|
+
**A `workflow` flows. A `taskflow` is a *graph*.** Other orchestrators let the model *script* the work — imperative code that flows step by step, with the graph hidden inside control flow. `pi-taskflow` does the opposite: you **declare** the work as a graph of discrete, named **task** nodes connected by `dependsOn` edges — and the runtime *verifies that graph before it spends a single token.*
|
|
39
39
|
|
|
40
40
|
You already know the built-in subagent tool's `task` / `tasks` / `chain`. `pi-taskflow` speaks the *same* shorthand — so your existing delegations instantly become **tracked, resumable, and saveable as a one-word `/tf:<name>` command**. When you outgrow the shorthand, the full DSL gives you a real DAG: dynamic fan-out over dozens of items, conditional routing, quality gates, human approvals, retries, and a hard spend ceiling.
|
|
41
41
|
|
|
42
42
|
And the whole time, **only the final phase reaches your conversation.** Every intermediate transcript stays in the runtime, never your context window.
|
|
43
43
|
|
|
44
|
+
## Why "taskflow" and not "workflow"?
|
|
45
|
+
|
|
46
|
+
The name is the thesis. In engineering, a **task** is a *discrete, declared unit of work* — the node of a task graph (the same `task` a build system, scheduler, or compiler wires into a DAG). **Work**, by contrast, is *fluid and unbounded* — the continuous, imperative act of doing.
|
|
47
|
+
|
|
48
|
+
That distinction is exactly the design split in the Pi ecosystem:
|
|
49
|
+
|
|
50
|
+
<div align="center">
|
|
51
|
+
<img src="./assets/task-vs-work.png" alt="work is a fluid imperative script whose graph hides in control flow and can't be verified before it runs; a taskflow is a declarative graph of discrete task nodes that is statically verified before any token is spent" width="900">
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
- A **`workflow`** (the dynamic, code-mode kind) is the model writing an **imperative script** that *flows*: `await agent(...)`, an `if`, a `for`, another `await`. Expressive — it's Turing-complete — but the graph only exists *as the code runs*. You can't see it, diff it, or prove it terminates before you pay for it.
|
|
55
|
+
- A **`taskflow`** moves the plan **out of code and into a declarative graph of `task` nodes.** Because the graph is *data*, the runtime can do what an imperative script structurally cannot: **statically verify it** (no cycles, no dead ends, no budget overflow, no dangling refs) before a single subagent spawns, **render it** (the live progress *is* the DAG), **resume it** phase-by-phase, and **save it** as a one-word command.
|
|
56
|
+
|
|
57
|
+
> **The trade we make on purpose:** we give up the raw expressivity of arbitrary code to gain something an imperative script can't have — a graph that is **verifiable, observable, replayable, and safe to generate with an LLM.** When a job needs twelve steps with branching fan-out and a review gate, you want a graph you can *check* — not a script you *hope* runs right.
|
|
58
|
+
|
|
44
59
|
## Why this exists
|
|
45
60
|
|
|
46
|
-
Here's the wall you hit with raw subagents: you describe a multi-step plan in prose, the model re-derives it every single run, the intermediate transcripts flood your context, and the moment one model call fails you start over from zero. There's no reuse, no recovery, no structure.
|
|
61
|
+
Here's the wall you hit with raw subagents: you describe a multi-step plan in prose, the model re-derives it every single run, the intermediate transcripts flood your context, and the moment one model call fails you start over from zero. There's no reuse, no recovery, no structure — and no way to *check* the plan before it burns tokens.
|
|
47
62
|
|
|
48
|
-
`pi-taskflow` moves the plan **out of the prompt and into a declarative
|
|
63
|
+
`pi-taskflow` moves the plan **out of the prompt and into a declarative graph of task nodes.** The runtime owns the DAG, the loops, the retries, and the intermediate state. You declare a pipeline once and run it a hundred times — by name. Because the plan is data, not prose and not code, it can be **validated, visualized, and replayed.**
|
|
49
64
|
|
|
50
65
|
<div align="center">
|
|
51
66
|
<img src="./assets/context-isolation.png" alt="With raw subagents every transcript floods your context; with pi-taskflow transcripts stay in the runtime and only the final result returns" width="900">
|
|
52
67
|
</div>
|
|
53
68
|
|
|
54
|
-
>
|
|
69
|
+
> Twelve steps, branching fan-out, a review gate, a spend cap — that's a graph, and you want to *see and check* it, not re-prompt it every run.
|
|
55
70
|
|
|
56
71
|
| | subagent (built-in) | **pi-taskflow** |
|
|
57
72
|
|---|---|---|
|
|
@@ -70,7 +85,23 @@ Here's the wall you hit with raw subagents: you describe a multi-step plan in pr
|
|
|
70
85
|
| **Live progress** | opaque while running | **live DAG render with timing + cost** |
|
|
71
86
|
| **Ergonomics** | inline JSON each time | **shorthand (`task`/`tasks`/`chain`) *or* DSL** |
|
|
72
87
|
|
|
73
|
-
It doesn't replace the subagent tool. It gives your subagents a
|
|
88
|
+
It doesn't replace the subagent tool. It gives your subagents a **graph**, a memory, and a name.
|
|
89
|
+
|
|
90
|
+
## Declarative graph vs. imperative script
|
|
91
|
+
|
|
92
|
+
The closest thing to `pi-taskflow` in spirit is the **dynamic / code-mode workflow** — where the model writes a JavaScript orchestration script. It's powerful and genuinely expressive. But it sits at the *opposite* end of one fundamental axis: **expressivity vs. verifiability.**
|
|
93
|
+
|
|
94
|
+
| | dynamic `workflow` (code-mode) | **`pi-taskflow`** (declarative graph) |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| **The plan is** | imperative JS the model writes & runs | **declarative JSON data the runtime executes** |
|
|
97
|
+
| **The graph** | implicit — hidden in `if`/`for`/`await` control flow | **explicit — `phases[]` + `dependsOn` edges, a first-class object** |
|
|
98
|
+
| **Verify before running** | ✗ Turing-complete; can't prove it terminates | **✓ static checks: no cycles, dead-ends, budget overflow, dangling refs** |
|
|
99
|
+
| **See it** | ✗ the graph only exists as the code runs | **✓ the live progress render *is* the DAG** |
|
|
100
|
+
| **Resume** | coarse (call-cache dedup) | **✓ phase-by-phase input-hash resume, cross-session** |
|
|
101
|
+
| **Safe to LLM-generate** | risky — it's executable code | **✓ it's just data — no `eval`; and a runtime-generated sub-flow is *structurally validated* (cycles / dangling refs / duplicate ids) before it runs** |
|
|
102
|
+
| **Expressivity ceiling** | **higher** — arbitrary control flow | bounded by the DSL, but `map`/`when`/`loop`/`gate` — plus **runtime-generated sub-flows (`flow {def}`)** for plan-then-execute and iterative replanning — cover most jobs |
|
|
103
|
+
|
|
104
|
+
We chose the **verifiable** side on purpose. The expressivity you give up is real; what you get back — a plan you can check, watch, replay, and safely let a model author — is what turns one-off prompting into durable orchestration.
|
|
74
105
|
|
|
75
106
|
## Compared to other Pi extensions
|
|
76
107
|
|
|
@@ -95,12 +126,12 @@ The Pi ecosystem now has **20+ delegation, workflow, and orchestration extension
|
|
|
95
126
|
|
|
96
127
|
- **`@pi-agents/orchid`** is the most feature-complete orchestrator in the ecosystem (DAG + worktrees + Ralph loop + agent mailbox) — but its DSL is a *fixed* 9-phase pipeline, it carries runtime deps + jiti, and it's beta. Reach for `pi-taskflow` when you want to **define your own graph** (not adopt an opinionated one) with **zero dependencies** and a one-command install.
|
|
97
128
|
- **`pi-crew` / `ultimate-pi`** go heavier — worktree isolation, durable async teams, multi-tier governance. If you want lightweight, declarative, and zero-dependency, that's this project.
|
|
98
|
-
- **`@zhushanwen/pi-workflow`** is the closest in spirit and also zero-dep, but you author workflows as **JavaScript scripts
|
|
129
|
+
- **`@zhushanwen/pi-workflow`** is the closest in spirit and also zero-dep, but it's the **imperative** side of the split above: you author workflows as **JavaScript scripts** the model writes and runs. `pi-taskflow`'s **declarative JSON DAG** is the verifiable side — statically checkable, visualizable, safe to LLM-generate, and resumable at phase granularity rather than call-cache dedup.
|
|
99
130
|
- **`@fiale-plus/pi-rogue-orchestration`** has a real **loop-until-done** (a feature `pi-taskflow` doesn't yet have). If your job is "keep going until the goal is met," it's worth a look; `pi-taskflow` is for *structured, branching* pipelines instead.
|
|
100
131
|
- **`pi-subagents` / `@gotgenes/pi-subagents`** are the mature picks for ad-hoc "use reviewer on this diff" delegation and background jobs. `pi-taskflow` is for when those delegations need to become a *repeatable, resumable pipeline*.
|
|
101
132
|
- **`pi-pipeline` / `pi-agent-flow`** ship *opinionated, fixed* flows. `pi-taskflow` ships an *empty canvas*: you (or the model) declare the graph that fits the job.
|
|
102
133
|
|
|
103
|
-
> The honest one-liner: **`pi-taskflow` is the only Pi extension that gives you a declarative,
|
|
134
|
+
> The honest one-liner: **`pi-taskflow` is the only Pi extension that gives you a *declarative, verifiable, resumable* DAG of task nodes — saved as a one-word command, with zero runtime dependencies and context isolation by design.** Where code-mode workflows let the model *script* the work, `pi-taskflow` lets it *declare a graph the runtime can prove correct before running.* The known gaps it's closing next: loop-until-done, worktree isolation, and non-blocking background runs (see [`STRATEGY.md`](./STRATEGY.md)).
|
|
104
135
|
|
|
105
136
|
## 30-second start
|
|
106
137
|
|
|
@@ -241,7 +272,7 @@ No scripting. No `eval`. Just data the runtime executes — safe enough to run L
|
|
|
241
272
|
| `gate` | quality/review step that can **halt the flow** | `task` |
|
|
242
273
|
| `reduce` | aggregate `from[]` phase outputs into one | `from`, `task` |
|
|
243
274
|
| `approval` | **human-in-the-loop** pause — approve / reject / edit | — |
|
|
244
|
-
| `flow` | run a **
|
|
275
|
+
| `flow` | run a **sub-flow** as one phase — a **saved** flow (`use`) or a **runtime-generated** one (`def`) | `use` \| `def` |
|
|
245
276
|
| `loop` | **iterate a task until done** — re-run a body until a condition, convergence, or a cap | `task`, `until` |
|
|
246
277
|
| `tournament` | **N variants compete**, a judge picks the best (or aggregates) | `task` \| `branches` |
|
|
247
278
|
|
|
@@ -263,6 +294,7 @@ Every phase needs a unique `id` and a `type` (defaults to `agent`). On top of th
|
|
|
263
294
|
| `final` | Marks the result-bearing phase (else the last phase wins) |
|
|
264
295
|
| `optional` | A failure here does **not** abort the run |
|
|
265
296
|
| `use` / `with` | (`flow`) saved sub-flow name + its args |
|
|
297
|
+
| `def` | (`flow`) inline sub-flow **generated at runtime** — usually `"{steps.plan.json}"` (mutually exclusive with `use`) |
|
|
266
298
|
| `cache` | `{ scope, ttl?, fingerprint? }` — cross-run memoization (see below) |
|
|
267
299
|
|
|
268
300
|
Flow-level keys: `name`, `description`, `args`, `concurrency` (default 8), `agentScope`, and `budget: { maxUSD?, maxTokens? }`.
|
|
@@ -273,7 +305,7 @@ Flow-level keys: `name`, `description`, `args`, `concurrency` (default 8), `agen
|
|
|
273
305
|
- **`join: "any"`** — an OR-join: the phase runs as soon as *one* dependency completes (default `"all"` waits for all).
|
|
274
306
|
- **`retry`** — `{ "max": 2, "backoffMs": 500, "factor": 2 }` retries a failing subagent with fixed or exponential backoff; usage is summed and the attempt count shows as `↻N` in the TUI. Transient provider errors (rate-limit / 5xx / timeout) **auto-retry even without an explicit policy**; hard errors don't.
|
|
275
307
|
- **`approval`** — pause for a human (Approve / Reject / Edit). Reject halts the flow; Edit injects the typed note as the phase output for downstream steps. Non-interactive runs auto-approve.
|
|
276
|
-
- **`flow`** — `{ "type": "flow", "use": "deep-research", "with": { "topic": "{item}" } }` runs a saved flow as a phase (recursion is detected and rejected).
|
|
308
|
+
- **`flow`** — `{ "type": "flow", "use": "deep-research", "with": { "topic": "{item}" } }` runs a **saved** flow as a phase (recursion is detected and rejected). Or **generate the sub-flow at runtime**: `{ "type": "flow", "def": "{steps.plan.json}" }` resolves an upstream phase's JSON output into a sub-flow, **validates it (cycles / dangling refs / duplicate ids), then runs it** — the number and shape of the generated phases is decided at runtime, not authored in advance. A malformed plan fails *open* (the phase is skipped with a `defError`, the run continues). This is how a planner decides *at runtime* what work to spawn — the declarative answer to a code-mode `for` loop, with each generated plan checked before it spends a token. Pair it with `loop` for **data-dependent iterative replanning** (round N's plan depends on round N-1's result). See [`examples/dynamic-plan-execute.json`](./examples/dynamic-plan-execute.json) and [`examples/iterative-replan.json`](./examples/iterative-replan.json).
|
|
277
309
|
|
|
278
310
|
### Loop-until-done (`loop`)
|
|
279
311
|
|
|
@@ -536,7 +568,6 @@ The model can also configure roles via the `taskflow` tool:
|
|
|
536
568
|
| `mode: "apply-defaults"` + `force: true` | Writes `RECOMMENDED_DEFAULTS` to `settings.json`, preserving stale keys. |
|
|
537
569
|
| `mode: "interactive"` | Launches the full action menu + picker flow (requires a UI session). |
|
|
538
570
|
|
|
539
|
-
> **v0.0.13 deprecation note:** If `mode` is omitted, the tool falls back to v0.0.12 behavior when `modelRoles` is empty (auto-writes defaults) with a `console.warn` deprecation notice. If `modelRoles` already exists, it behaves as `mode: "show"`. This bridge will be removed in v0.0.14.
|
|
540
571
|
|
|
541
572
|
### Custom agents
|
|
542
573
|
|
|
@@ -577,12 +608,12 @@ Copy one into `.pi/taskflows/<name>.json` (or `~/.pi/agent/taskflows/`) and it r
|
|
|
577
608
|
|
|
578
609
|
<div align="center">
|
|
579
610
|
|
|
580
|
-
**0 runtime dependencies** · **
|
|
611
|
+
**0 runtime dependencies** · **535 tests** · **9 phase types** · **cross-session resume** · **cross-run memoization** · **~5.4k LOC runtime**
|
|
581
612
|
|
|
582
613
|
</div>
|
|
583
614
|
|
|
584
615
|
- **Zero runtime dependencies.** No `dependencies` field — the runtime is built entirely on Node built-ins (`fs` / `path` / `os` / `child_process` / `crypto`). The file lock is `fs.openSync("wx")`, not a third-party library.
|
|
585
|
-
- **
|
|
616
|
+
- **535 tests across 21 test files** covering concurrency, atomic file locking (8-process race regressions), path-traversal hardening, cross-session resume, cross-run cache freshness (flow/thinking/tools key isolation, fingerprint invalidation, TTL/LRU eviction), gate verdicts, budget caps, retry/backoff, approval flows, loop termination, tournament judging, sub-flow composition, callback isolation, the idle watchdog, model-role init config, and parseModelFromLabel with parenthesized-model-name regression.
|
|
586
617
|
- **Hardened by design.** Path-traversal defense (lexical + `realpath`), runId validation, HTML/error sanitization, atomic writes, stale-lock stealing via `rename`, and an idle watchdog that kills wedged subagents.
|
|
587
618
|
- **Dogfooded.** Every new feature has to survive the project's own `self-improve` taskflow before it ships.
|
|
588
619
|
|
|
@@ -599,12 +630,14 @@ Our `self-improve` flow is a 10-phase DAG — it audits the codebase, patches de
|
|
|
599
630
|
| [Cross-run cache dogfood](./docs/rfc-cross-run-memoization.md) | Real runtime + on-disk store | Dedicated test harness | Cache correctness under adversarial fingerprints |
|
|
600
631
|
| [Adversarial cross-review](./docs/brainstorm-adversarial-review-report.md) | Multi-agent adversarial review | `tournament` + `gate` | P0 cache-key fix shipped |
|
|
601
632
|
| [Init redesign review](./docs/issue-necessity-review-report.md) | Necessity audit → parallel checks → verdict | 7 phases | Full redesign plan validated |
|
|
633
|
+
| [Round 2 adversarial audit](./docs/internal/dogfooding-report.md) | Phase-by-phase DAG execution — 12 findings across runner/runtime/interpolate/verify | 14 phases | 10 fixes applied, 0 regressions |
|
|
634
|
+
| [Round 3 adversarial audit](./docs/internal/dogfooding-report.md) | Integration layer + cross-module — 10 findings across index/agents/cache/render/runs-view | 9 phases | 10 fixes applied, 0 regressions |
|
|
602
635
|
|
|
603
636
|
> **Meta:** we used `pi-taskflow`'s `map` fan-out, `gate` verdicts, `approval` human-in-the-loop, `tournament` best-of-N, `loop` until-done, and `cross-run` cache — to build `pi-taskflow`.
|
|
604
637
|
|
|
605
638
|
## Status & limits
|
|
606
639
|
|
|
607
|
-
**v0.0.
|
|
640
|
+
**v0.0.17** — loop-until-done (`loop` phase: iterate to a condition, convergence, or cap), tournament (best-of-N with a judge), cross-run memoization (content-addressed cache with git/file/glob/env fingerprints and TTL), interactive `/tf init` with role-aware model pickers + diff preview + atomic merge-write, configurable built-in agents, 18 built-in agents with 6 model roles. Full control-flow & reliability layer (`when` guards, `join: any`, `retry`/backoff, `approval`, `flow` composition, `budget` caps, idle watchdog) on top of the DSL + DAG runtime (`agent`/`parallel`/`map`/`gate`/`reduce`). Inline + saved flows, cross-session resume, live progress, and isolated context. A run executes as one streaming tool call.
|
|
608
641
|
|
|
609
642
|
Known boundaries (tracked, bounded — no surprises mid-flow):
|
|
610
643
|
|
|
@@ -622,7 +655,7 @@ npm test # unit tests — no network, no process spawning
|
|
|
622
655
|
npm run test:e2e # real end-to-end (spawns live subagents; needs model access)
|
|
623
656
|
```
|
|
624
657
|
|
|
625
|
-
Runtime lives in `extensions/`, tests in `test/`, runnable examples in `examples
|
|
658
|
+
Runtime lives in `extensions/`, tests in `test/`, and runnable examples in `examples/`.
|
|
626
659
|
|
|
627
660
|
## Contributing
|
|
628
661
|
|
package/README.zh-CN.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="./assets/hero.png" alt="pi-taskflow —
|
|
3
|
+
<img src="./assets/hero.png" alt="pi-taskflow — 面向 Pi 子代理的声明式、可验证的任务节点图:有状态、可恢复、上下文隔离" width="900">
|
|
4
4
|
|
|
5
5
|
<p>
|
|
6
6
|
<a href="https://www.npmjs.com/package/pi-taskflow"><img src="https://img.shields.io/npm/v/pi-taskflow?style=flat-square&color=B692FF&label=npm" alt="npm version"></a>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-43D9AD?style=flat-square" alt="MIT license"></a>
|
|
9
9
|
<a href="#whats-inside"><img src="https://img.shields.io/badge/runtime%20deps-0-43D9AD?style=flat-square" alt="zero runtime dependencies"></a>
|
|
10
10
|
<a href="https://github.com/heggria/pi-taskflow/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/heggria/pi-taskflow/ci.yml?branch=main&style=flat-square&label=CI" alt="CI status"></a>
|
|
11
|
-
<a href="#whats-inside"><img src="https://img.shields.io/badge/tests-
|
|
11
|
+
<a href="#whats-inside"><img src="https://img.shields.io/badge/tests-535-6E8BFF?style=flat-square" alt="535 tests"></a>
|
|
12
12
|
<a href="#whats-inside"><img src="https://img.shields.io/badge/dogfooded-%E2%9C%93-43D9AD?style=flat-square" alt="dogfooded"></a>
|
|
13
13
|
<a href="https://pi.dev"><img src="https://img.shields.io/badge/for-Pi%20coding%20agent-B692FF?style=flat-square" alt="for the Pi coding agent"></a>
|
|
14
14
|
</p>
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
-->
|
|
27
27
|
</p>
|
|
28
28
|
|
|
29
|
-
<p><strong>面向 <a href="https://pi.dev">Pi</a> 子代理(subagent
|
|
30
|
-
|
|
29
|
+
<p><strong>面向 <a href="https://pi.dev">Pi</a> 子代理(subagent)的声明式、可验证的「任务图」。</strong><br/>
|
|
30
|
+
不是你要去「写脚本」的 workflow——而是你去「声明」的一张 DAG。并发分发(fan out)· 门控(gate)· 恢复(resume)· 保存为命令——中间结果始终远离你的上下文窗口(context window)。</p>
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
33
|
pi install npm:pi-taskflow
|
|
@@ -37,23 +37,38 @@ pi install npm:pi-taskflow
|
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
**`workflow` 是在「流动」,而 `taskflow` 是一张「图」。** 其他编排框架让模型去「写脚本」——命令式的代码逐步流动,而那张图藏在控制流里。`pi-taskflow` 恰恰相反:你把工作**声明**为一张由离散、具名的**任务(task)节点**、通过 `dependsOn` 边连接而成的图——而运行时会在花掉一个 token 之前,*先验证这张图。*
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
你已经熟悉内置子代理(subagent)工具的 `task` / `tasks` / `chain` 了。`pi-taskflow` 使用**完全相同的简写语法**——所以你现有的委托立刻就能变成**可追踪、可恢复、可保存为一条 `/tf:<name>` 命令**的流程。当你超越简写语法时,完整的 DSL 为你提供真正的 DAG:针对数十个项目的动态并发分发、条件路由、质量门控、人工审批、重试,以及硬性费用上限。
|
|
43
43
|
|
|
44
44
|
而且自始至终,**只有最终阶段(final phase)才会进入你的对话。** 每一个中间转录都留在运行时中,永远不会进入你的上下文窗口。
|
|
45
45
|
|
|
46
|
+
## 为什么叫 “taskflow” 而不是 “workflow”?
|
|
47
|
+
|
|
48
|
+
名字就是立论。在工程语境里,**task(任务)**是一个*离散、被声明出来的工作单元*——是任务图的节点(构建系统、调度器、编译器都把这种 `task` 连成 DAG)。而 **work(工作)**息息相反,是*流动的、无界的*——那种连续的、命令式的「干活」过程。
|
|
49
|
+
|
|
50
|
+
这个区别,恰恰就是 Pi 生态里的设计分水岭:
|
|
51
|
+
|
|
52
|
+
<div align="center">
|
|
53
|
+
<img src="./assets/task-vs-work.png" alt="work 是一段流动的命令式脚本,它的图藏在控制流里、运行前无法验证;taskflow 是一张由离散任务节点构成的声明式图,在花掉任何 token 之前就被静态验证" width="900">
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
- 一个 **`workflow`**(那种动态的、code-mode 的形态)是模型在写一段**「流动」的命令式脚本**:`await agent(...)`、一个 `if`、一个 `for`、又一个 `await`。很有表达力——它是图灵完备的——但那张图只在*代码跑起来的时候*才存在。你看不到它、diff 不了它,也无法在付费之前证明它会终止。
|
|
57
|
+
- 一个 **`taskflow`** 把计划**从代码中移出、放进一张由 `task` 节点构成的声明式图里。** 因为这张图是*数据*,运行时就能做到命令式脚本从结构上做不到的事:在任何子代理被启动之前就**静态验证它**(无环、无死端、不超预算、无悬空引用)、**渲染它**(实时进度*本身就是*那张 DAG)、**逐阶段恢复它**,以及把它**保存为一条命令**。
|
|
58
|
+
|
|
59
|
+
> **我们有意为之的取舍:**我们放弃了任意代码的极致表达力,换来了命令式脚本永远无法拥有的东西——一张**可验证、可观测、可重放、且能安全交给 LLM 生成**的图。当一个任务需要十二个步骤、带分支并发分发和一道审查门控时,你要的是一张能*检查*的图——而不是一段你只能*祈祷*它跑对的脚本。
|
|
60
|
+
|
|
46
61
|
## 为什么需要这个
|
|
47
62
|
|
|
48
|
-
|
|
63
|
+
这就是你在使用原生子代理时遇到的瓶颈:你用文字描述一个多步骤计划,模型每次都要重新推导,中间转录物塞满你的上下文,一旦某次模型调用失败你就得从头开始。没有复用,没有恢复,没有结构——也没有任何办法在烧掉 token 之前*检查*这个计划。
|
|
49
64
|
|
|
50
|
-
`pi-taskflow`
|
|
65
|
+
`pi-taskflow` 把计划**从提示词中移出,放入一张由任务节点构成的声明式图里。** 运行时(runtime)拥有 DAG、循环、重试和中间状态的所有权。你声明一次流水线,就能按名字运行上百次。因为这个计划是数据——不是文字,也不是代码——所以它可以被**验证、可视化、重放**。
|
|
51
66
|
|
|
52
67
|
<div align="center">
|
|
53
68
|
<img src="./assets/context-isolation.png" alt="使用原生子代理时每个转录物都涌入你的上下文;使用 pi-taskflow 时转录物留在运行时,只有最终结果返回" width="900">
|
|
54
69
|
</div>
|
|
55
70
|
|
|
56
|
-
>
|
|
71
|
+
> 十二个步骤、分支并发分发、一道审查门控、一个费用上限——这就是一张图,你想要*看到并检查*它,而不是每次运行都重新提示一遍。
|
|
57
72
|
|
|
58
73
|
| | 子代理(内置) | **pi-taskflow** |
|
|
59
74
|
|---|---|---|
|
|
@@ -72,7 +87,23 @@ pi install npm:pi-taskflow
|
|
|
72
87
|
| **实时进度** | 运行时不可见 | **实时 DAG 渲染,附带耗时和成本** |
|
|
73
88
|
| **易用性** | 每次内联 JSON | **简写语法(`task`/`tasks`/`chain`)*或* DSL** |
|
|
74
89
|
|
|
75
|
-
|
|
90
|
+
它没有取代子代理工具。它给你的子代理赋予了一张**图**、一份记忆和一个名字。
|
|
91
|
+
|
|
92
|
+
## 声明式图 vs 命令式脚本
|
|
93
|
+
|
|
94
|
+
精神上最接近 `pi-taskflow` 的,是那种**动态 / code-mode 的 workflow**——模型写一段 JavaScript 编排脚本。它强大、且确实很有表达力。但它位于某个根本轴的*另一极*:**表达力 vs 可验证性。**
|
|
95
|
+
|
|
96
|
+
| | 动态 `workflow`(code-mode) | **`pi-taskflow`**(声明式图) |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| **计划是什么** | 模型书写并运行的命令式 JS | **运行时执行的声明式 JSON 数据** |
|
|
99
|
+
| **那张图** | 隐式——藏在 `if`/`for`/`await` 控制流里 | **显式——`phases[]` + `dependsOn` 边,一等对象** |
|
|
100
|
+
| **运行前验证** | ✗ 图灵完备;无法证明会终止 | **✓ 静态检查:无环、无死端、不超预算、无悬空引用** |
|
|
101
|
+
| **看到它** | ✗ 图只在代码跑起来时存在 | **✓ 实时进度渲染*本身就是* DAG** |
|
|
102
|
+
| **恢复** | 粗粒度(调用缓存去重) | **✓ 逐阶段输入哈希恢复,跨会话** |
|
|
103
|
+
| **能否安全交给 LLM 生成** | 有风险——它是可执行代码 | **✓ 它只是数据——无 `eval`、无任意执行** |
|
|
104
|
+
| **表达力上限** | **更高**——任意控制流 | 受 DSL 限制(但 `map`/`when`/`loop`/`gate` 覆盖了大多数任务) |
|
|
105
|
+
|
|
106
|
+
我们有意选了**可验证**的那一边。你放弃的表达力是真实的;但你换回的——一张能检查、能看、能重放、能安全交给模型书写的计划——才是把一次性提示变成持久编排的关键。
|
|
76
107
|
|
|
77
108
|
## 与其他 Pi 扩展的对比
|
|
78
109
|
|
|
@@ -97,12 +128,12 @@ Pi 生态现在有 **20 多个委托、工作流和编排扩展**——每个在
|
|
|
97
128
|
|
|
98
129
|
- **`@pi-agents/orchid`** 是生态中功能最完整的编排器(DAG + worktree + Ralph 循环 + 代理邮箱)——但其 DSL 是*固定*的 9 阶段流水线,携带运行时依赖 + jiti,且处于 beta 阶段。当你想**定义自己的图结构**(而非采用别人的固定观点),并且追求**零依赖**和一条命令安装时,选 `pi-taskflow`。
|
|
99
130
|
- **`pi-crew` / `ultimate-pi`** 更重——worktree 隔离、持久的异步团队、多层治理。如果你想要轻量、声明式、零依赖,那就选本项目。
|
|
100
|
-
- **`@zhushanwen/pi-workflow`**
|
|
131
|
+
- **`@zhushanwen/pi-workflow`** 精神上最为接近,也是零依赖,但它站在上述分水岭的**命令式**那一边:你要以模型书写并运行的 **JavaScript 脚本**来编写工作流。`pi-taskflow` 的**声明式 JSON DAG** 是可验证的那一边——可静态检查、可可视化、可安全交给 LLM 生成,且恢复粒度精细到阶段级别而非调用缓存去重。
|
|
101
132
|
- **`@fiale-plus/pi-rogue-orchestration`** 拥有真正的**循环至完成**(`pi-taskflow` 尚不具备的功能)。如果你的任务是"一直做直到目标达成",它值得一看;而 `pi-taskflow` 适用于*结构化、分支式的*流水线。
|
|
102
133
|
- **`pi-subagents` / `@gotgenes/pi-subagents`** 是即席"用 reviewer 审查这个 diff"委托和后台作业的成熟选择。`pi-taskflow` 则适用于当这些委托需要变成*可重复、可恢复的流水线*时。
|
|
103
134
|
- **`pi-pipeline` / `pi-agent-flow`** 提供的是*固定观点、固定结构*的流程。`pi-taskflow` 提供的是*一张空白画布*:你(或模型)声明适合任务的图结构。
|
|
104
135
|
|
|
105
|
-
> 诚实的一句话总结:**`pi-taskflow`
|
|
136
|
+
> 诚实的一句话总结:**`pi-taskflow` 是唯一一个给你一张*声明式、可验证、可恢复*的任务节点 DAG 的 Pi 扩展——保存为一条单词命令,零运行时依赖,且从设计上就上下文隔离。** code-mode 的 workflow 让模型去*写脚本*跳动工作,`pi-taskflow` 则让它*声明一张运行时能在执行前证明其正确的图。* 已知正在弥补的缺口:循环至完成、worktree 隔离、非阻塞后台运行(详见 [`STRATEGY.md`](./STRATEGY.md))。
|
|
106
137
|
|
|
107
138
|
## 30 秒快速开始
|
|
108
139
|
|
|
@@ -538,7 +569,7 @@ Taskflow 自带 **18 个内置代理**——每个代理是一个 `.md` 文件
|
|
|
538
569
|
| `mode: "apply-defaults"` + `force: true` | 将 `RECOMMENDED_DEFAULTS` 写入 `settings.json`,保留旧键。 |
|
|
539
570
|
| `mode: "interactive"` | 启动完整的行动菜单 + 选择器流程(需要 UI 会话)。 |
|
|
540
571
|
|
|
541
|
-
|
|
572
|
+
|
|
542
573
|
|
|
543
574
|
### 自定义代理
|
|
544
575
|
|
|
@@ -579,12 +610,12 @@ provided files. Report violations grouped by file. No fixes.
|
|
|
579
610
|
|
|
580
611
|
<div align="center">
|
|
581
612
|
|
|
582
|
-
**0 个运行时依赖** · **
|
|
613
|
+
**0 个运行时依赖** · **535 个测试** · **9 种阶段类型** · **跨会话恢复** · **跨运行记忆化** · **~5.4k LOC 运行时**
|
|
583
614
|
|
|
584
615
|
</div>
|
|
585
616
|
|
|
586
617
|
- **零运行时依赖。** 没有 `dependencies` 字段——运行时完全基于 Node 内置模块(`fs` / `path` / `os` / `child_process` / `crypto`)。文件锁是 `fs.openSync("wx")`,不是第三方库。
|
|
587
|
-
- **
|
|
618
|
+
- **535 个测试分布在 21 个测试文件中**,涵盖并发、原子文件锁定(8 进程竞争回归测试)、路径穿越防御、跨会话恢复、跨运行缓存新鲜度(流程/推理/工具键隔离、指纹失效、TTL/LRU 淘汰)、门控判决、预算上限、重试/回退、审批流程、循环终止、锦标赛评判、子流程组合、回调隔离、空闲看门狗、模型角色 init 配置,以及带括号模型名称回归的 parseModelFromLabel。
|
|
588
619
|
- **经过强化的设计。** 路径穿越防御(词法 + `realpath`)、runId 验证、HTML/错误净化、原子写入、通过 `rename` 实现的过期锁窃取,以及杀死卡死子代理的空闲看门狗。
|
|
589
620
|
- **自产自用(dogfooded)。** 每个新功能必须在发布前通过项目自身的 `self-improve` taskflow 的考验。
|
|
590
621
|
|
|
@@ -601,12 +632,14 @@ provided files. Report violations grouped by file. No fixes.
|
|
|
601
632
|
| [跨运行缓存 dogfood](./docs/rfc-cross-run-memoization.md) | 真实运行时 + 磁盘存储 | 专用测试框架 | 在对抗性指纹下验证缓存正确性 |
|
|
602
633
|
| [对抗性交叉审查](./docs/brainstorm-adversarial-review-report.md) | 多代理对抗性审查 | `tournament` + `gate` | 修复 P0 缓存键问题并发布 |
|
|
603
634
|
| [Init 重设计审查](./docs/issue-necessity-review-report.md) | 必要性审计 → 并行检查 → 判决 | 7 阶段 | 完整重设计方案已验证 |
|
|
635
|
+
| [第 2 轮对抗性审计](./docs/internal/dogfooding-report.md) | 逐阶段 DAG 执行——12 个发现覆盖 runner/runtime/interpolate/verify | 14 阶段 | 已修复 10 项,0 退化 |
|
|
636
|
+
| [第 3 轮对抗性审计](./docs/internal/dogfooding-report.md) | 集成层 + 跨模块——10 个发现覆盖 index/agents/cache/render/runs-view | 9 阶段 | 已修复 10 项,0 退化 |
|
|
604
637
|
|
|
605
638
|
> **元点评:** 我们使用了 `pi-taskflow` 的 `map` 并发分发、`gate` 判决、`approval` 人机协作、`tournament` best-of-N、`loop` 循环至完成和 `cross-run` 缓存——来构建 `pi-taskflow`。
|
|
606
639
|
|
|
607
640
|
## 状态与边界
|
|
608
641
|
|
|
609
|
-
**v0.0.
|
|
642
|
+
**v0.0.17**——循环至完成(`loop` 阶段:迭代至条件满足、收敛或上限)、锦标赛(best-of-N 带评判者)、跨运行记忆化(基于 git/文件/glob/环境指纹和 TTL 的内容寻址缓存)、交互式 `/tf init`(带角色感知模型选择器 + 差异预览 + 原子合并写入)、18 个内置代理及 6 个模型角色。完整的控制流与可靠性层(`when` 守卫、`join: any`、`retry`/回退、`approval`、`flow` 组合、`budget` 上限、空闲看门狗)构建在 DSL + DAG 运行时(`agent`/`parallel`/`map`/`gate`/`reduce`)之上。支持内联 + 已保存流程、跨会话恢复、实时进度和上下文隔离。一次运行作为一个流式工具调用执行。
|
|
610
643
|
|
|
611
644
|
已知边界(已追踪、有限定——不会在流程中途出现意外):
|
|
612
645
|
|
|
@@ -624,7 +657,7 @@ npm test # 单元测试——无网络,无进程派生
|
|
|
624
657
|
npm run test:e2e # 真实端到端测试(派生真实子代理;需要模型访问权限)
|
|
625
658
|
```
|
|
626
659
|
|
|
627
|
-
运行时位于 `extensions/`,测试位于 `test/`,可运行示例位于 `examples
|
|
660
|
+
运行时位于 `extensions/`,测试位于 `test/`,可运行示例位于 `examples/`。
|
|
628
661
|
|
|
629
662
|
## 贡献
|
|
630
663
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dynamic-plan-execute",
|
|
3
|
+
"description": "Runtime plan-then-execute: a planner scans the codebase and EMITS a sub-flow (one audit phase per file). The flow phase resolves that JSON at runtime, validates it, and runs it as a nested sub-flow — the number and shape of audit phases is decided at runtime, not authored in advance. A gate then reports.",
|
|
4
|
+
"version": 1,
|
|
5
|
+
"args": {
|
|
6
|
+
"target": { "default": ".", "description": "Directory to scan and audit" }
|
|
7
|
+
},
|
|
8
|
+
"concurrency": 4,
|
|
9
|
+
"agentScope": "user",
|
|
10
|
+
"budget": { "maxUSD": 1.5 },
|
|
11
|
+
"phases": [
|
|
12
|
+
{
|
|
13
|
+
"id": "plan",
|
|
14
|
+
"type": "agent",
|
|
15
|
+
"agent": "planner",
|
|
16
|
+
"task": "Scan \"{args.target}\" and produce an audit plan. Output ONLY a JSON object of the form {\"name\":\"audit\",\"phases\":[ ... ]}. Emit ONE phase per source file worth auditing. Each phase must look like {\"id\":\"audit-<safe-name>\",\"type\":\"agent\",\"agent\":\"reviewer\",\"task\":\"Audit <path> for correctness, security, and dead code. Report findings.\"}. Give the LAST phase a \"final\": true and make it a reduce-style summary that depends on the others (\"type\":\"reduce\",\"from\":[<all audit ids>],\"agent\":\"reviewer\",\"task\":\"Summarize all audit findings into one report.\"). Use hyphens in ids, never underscores. Output JSON only — no prose, no markdown fence.",
|
|
17
|
+
"output": "json"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "execute-plan",
|
|
21
|
+
"type": "flow",
|
|
22
|
+
"def": "{steps.plan.json}",
|
|
23
|
+
"dependsOn": ["plan"]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "report-gate",
|
|
27
|
+
"type": "gate",
|
|
28
|
+
"agent": "reviewer",
|
|
29
|
+
"dependsOn": ["execute-plan"],
|
|
30
|
+
"task": "Here is the audit report:\n\n{steps.execute-plan.output}\n\nDecide whether the codebase is in acceptable shape. End with 'VERDICT: PASS' or 'VERDICT: BLOCK' and a one-line reason.",
|
|
31
|
+
"final": true
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "iterative-replan",
|
|
3
|
+
"description": "Data-dependent iterative replanning: a loop where each iteration's plan depends on the PREVIOUS iteration's RESULT (not a one-shot fan-out). Each round the planner reads the prior round's findings and decides either to emit more work or to signal done. This is the declarative equivalent of an imperative `for` loop that reads a result and decides the next step. Each round's generated plan is validated before it runs.",
|
|
4
|
+
"version": 1,
|
|
5
|
+
"args": {
|
|
6
|
+
"goal": { "description": "The investigation / refinement objective" }
|
|
7
|
+
},
|
|
8
|
+
"concurrency": 4,
|
|
9
|
+
"agentScope": "user",
|
|
10
|
+
"budget": { "maxUSD": 2.0 },
|
|
11
|
+
"phases": [
|
|
12
|
+
{
|
|
13
|
+
"id": "investigate",
|
|
14
|
+
"type": "loop",
|
|
15
|
+
"agent": "explorer",
|
|
16
|
+
"maxIterations": 5,
|
|
17
|
+
"until": "{steps.investigate.json.done} == true",
|
|
18
|
+
"output": "json",
|
|
19
|
+
"task": "Goal: {args.goal}\n\nPrevious round's result (empty on the first round):\n{previous.output}\n\nDecide the next step. If the goal is satisfied, output ONLY {\"done\": true, \"summary\": \"<what you concluded>\"}. Otherwise output ONLY {\"done\": false, \"findings\": \"<what you learned this round>\", \"next\": \"<what to investigate next round>\"}. Each round's plan must build on the previous round's findings. JSON only — no prose."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "final-report",
|
|
23
|
+
"type": "agent",
|
|
24
|
+
"agent": "reviewer",
|
|
25
|
+
"dependsOn": ["investigate"],
|
|
26
|
+
"task": "Write the final report for goal \"{args.goal}\" based on the converged investigation:\n\n{steps.investigate.output}",
|
|
27
|
+
"final": true
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
package/extensions/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* pi-taskflow —
|
|
2
|
+
* pi-taskflow — a declarative, verifiable graph of task nodes for the Pi coding agent.
|
|
3
3
|
*
|
|
4
4
|
* Registers:
|
|
5
5
|
* - tool `taskflow` : run inline / saved flows, save, resume (LLM-callable)
|
|
@@ -325,7 +325,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
325
325
|
"Interpolation: {args.X}, {steps.ID.output}, {steps.ID.json}, {item} (map), {previous.output}.",
|
|
326
326
|
].join(" "),
|
|
327
327
|
parameters: TaskflowParams,
|
|
328
|
-
promptSnippet: "
|
|
328
|
+
promptSnippet: "Declare a verifiable graph of subagent tasks (single, parallel, chain, or full DAG) — tracked, resumable, context-isolated. The runtime validates the graph before running. Replaces the subagent tool.",
|
|
329
329
|
promptGuidelines: [
|
|
330
330
|
"BEFORE FIRST USE: invoke skill_load('taskflow') to read the full skill documentation (DSL syntax, phase types, examples, best practices). This tool description is a condensed reference only — the skill is the authoritative guide.\n\nUse taskflow for ALL delegation — single tasks, parallel, chain, or full DAG orchestration. It fully replaces the subagent tool: every delegation is tracked with a runId, resumable across sessions, context-isolated (only final output returns), and saveable as /tf:<name>. Do NOT call the subagent tool directly; use taskflow shorthand (task/tasks/chain) for simple cases instead.",
|
|
331
331
|
"For complex multi-phase work (explore / 审计 / analyze the project, auditing endpoints, reviewing or migrating many files/modules, cross-checked research), use the full DSL with phases. For taskflow map phases, have the upstream phase emit a JSON array and set output:'json'.",
|