pi-pstack-adapter 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +176 -141
- package/VERSION +1 -1
- package/adapter-skills/setup.md +38 -29
- package/adapter-skills/status.md +3 -3
- package/docs/compatibility.md +46 -23
- package/index.ts +27 -13
- package/package.json +1 -1
- package/src/certified.ts +2 -2
- package/src/config.ts +3 -2
- package/src/gate.ts +3 -2
- package/src/generate.ts +64 -43
- package/src/pools.ts +2 -2
- package/src/registry.ts +9 -9
- package/src/route.ts +318 -0
- package/src/status.ts +21 -14
- package/src/vocabulary.ts +3 -1
package/docs/compatibility.md
CHANGED
|
@@ -4,7 +4,7 @@ The compatibility registry in `src/registry.ts` assigns one support tier to ever
|
|
|
4
4
|
certified pstack revision. Generation reads it, the wrapper frontmatter publishes it, and
|
|
5
5
|
`/pistack-status` reports it.
|
|
6
6
|
|
|
7
|
-
Certified revision: pstack `0.15.
|
|
7
|
+
Certified revision: pstack `0.15.42` at `bf0c7cd80abdbf7fe47b634978a7d1b4071f71a7` in
|
|
8
8
|
`https://github.com/cursor/plugins`.
|
|
9
9
|
|
|
10
10
|
## Platforms
|
|
@@ -21,6 +21,22 @@ executables in the shell the skills run in, with Pi's `shellCommandPrefix` appli
|
|
|
21
21
|
that matters most, because Git Bash adds `/usr/bin` and `/mingw64/bin` to the `PATH` Pi
|
|
22
22
|
started with. A shell that is missing, inactive, or cannot run a command blocks every workflow.
|
|
23
23
|
|
|
24
|
+
pstack `0.15.42` was certified on Linux by the unattended release check. No skill or agent was
|
|
25
|
+
added or removed. Upstream changed its default models to Claude Opus 5.5, Claude Fable 5.2, and
|
|
26
|
+
Gemini 4 Carbon, made Fable its own family, and has arena pick its judge from another vendor,
|
|
27
|
+
where Fable and the other Claude models are one vendor. `how` now spawns one read-only subagent,
|
|
28
|
+
and `why` spawns none: it answers inline from commits and PR discussions, so it loses its
|
|
29
|
+
`delegation` gate and keeps `git` and `gh`. The `how explorer`, `how explainer`,
|
|
30
|
+
`why investigators`, and `why synthesizer` role lines are gone, and the host mapping no longer
|
|
31
|
+
names them. The host mapping gained the new default slugs, Fable and Gemini in the family note,
|
|
32
|
+
and a row saying that poteto-mode's `# defaults: 2` check does not apply to the adapter
|
|
33
|
+
configuration. The statechart scripts changed how they draw edges into nested states and how
|
|
34
|
+
they report a SANY parse failure; they need no new executable, and the `/bin/sh` launcher that
|
|
35
|
+
crashes on native Windows did not change. No tier changed. The release check runs no skill end
|
|
36
|
+
to end, because that would execute new upstream content unattended, so no `0.15.42` skill has
|
|
37
|
+
run in Pi, and `0.15.42` has not been checked on native Windows. The evidence below is from
|
|
38
|
+
`0.15.36` and earlier.
|
|
39
|
+
|
|
24
40
|
pstack `0.15.36` was certified on Linux. No skill or agent was added or removed. The skills
|
|
25
41
|
changed only in text, and the Orchestrate playbook's `orch` scripts were deleted; no adapter
|
|
26
42
|
gate named them. `0.15.36` has not been checked on native Windows. The statechart scripts did
|
|
@@ -47,8 +63,8 @@ small Git repository holding a token-bucket rate limiter with two marked bugs.
|
|
|
47
63
|
`recall` rises to `dependency-gated`, because the transcript was its only Cursor-specific
|
|
48
64
|
step. In a fresh session it read the two earlier chats itself, so its fan-out did not run. An
|
|
49
65
|
earlier fresh run looked in the default session folder, which the sandbox had moved, so the
|
|
50
|
-
host mapping now says to find the folder from `$PI_SESSION_FILE` first. `reflect` copied the
|
|
51
|
-
to a Pi skill location before editing it, but its create-skill route did not run, and
|
|
66
|
+
host mapping now says to find the folder from `$PI_SESSION_FILE` first. `reflect` copied the
|
|
67
|
+
upstream skill to a Pi skill location before editing it, but its create-skill route did not run, and
|
|
52
68
|
`automate-me` found nothing to draft, so both stay `experimental`.
|
|
53
69
|
- `poteto-mode`'s Orchestrate playbook ran a two-unit program in a linked worktree. Each unit had
|
|
54
70
|
an owner on its own branch and worktree, and a verifier from another model family checked the
|
|
@@ -223,10 +239,10 @@ Earlier Windows evidence, gathered at pstack `0.15.26` with the same toolchain:
|
|
|
223
239
|
- `create-formal-verification-skill` completed in the isolated install without `subagent` or
|
|
224
240
|
`ask_user`. It generated `.pi/skills/verify-inventory-reservations/SKILL.md` and a dependency-free
|
|
225
241
|
Node 22 explicit-state verifier that imports the fixture's actual reserve/release functions.
|
|
226
|
-
It checked 49 reachable states and 490 transitions for stock 0
|
|
242
|
+
It checked 49 reachable states and 490 transitions for stock from 0 to 3 and five request IDs, caught
|
|
227
243
|
six mutant copies, then caught a removed retry guard in the actual product file. Restoring
|
|
228
|
-
that file restored the passing result and its original hash. This supports `adapted
|
|
229
|
-
generated skill path
|
|
244
|
+
that file restored the passing result and its original hash. This supports `adapted`, because
|
|
245
|
+
the host mapping maps the generated skill path to Pi. It does not certify TLA+, proof assistants, installation
|
|
230
246
|
of other verifiers, unbounded inputs, or real database/async concurrency.
|
|
231
247
|
- The revised `ketchup` ran read-only in a separate fixture. It listed both supplied product
|
|
232
248
|
choices (CSV versus JSON and seven versus thirty days of history), drew a Mermaid sequence
|
|
@@ -237,8 +253,9 @@ Earlier Windows evidence, gathered at pstack `0.15.26` with the same toolchain:
|
|
|
237
253
|
`ketchup`, but still names `uptime` as missing for `benchmark-checklist`. Delegation and
|
|
238
254
|
structured questions remain unavailable in the isolated install; no delegating tier changed.
|
|
239
255
|
- A cache inside a Git worktree must own its worktree, Git directory, and common directory before
|
|
240
|
-
bootstrap or reuse can change Git state. The Windows run
|
|
241
|
-
empty nested cache
|
|
256
|
+
bootstrap or reuse can change Git state. The Windows run found that Git, started in an
|
|
257
|
+
empty nested cache, discovered the enclosing repository. That is fixed. Regression tests also
|
|
258
|
+
cover incomplete metadata, a foreign Git
|
|
242
259
|
file, foreign worktree/common directory settings, and short paths, while checking that the
|
|
243
260
|
enclosing repository's HEAD, config, index, and tracked bytes stay unchanged.
|
|
244
261
|
|
|
@@ -287,13 +304,13 @@ A session that replaces the `bash` tool with `powershell` cannot run the skills.
|
|
|
287
304
|
|
|
288
305
|
## Changing a tier
|
|
289
306
|
|
|
290
|
-
A tier is a claim about what
|
|
307
|
+
A tier is a claim about what a run verified, so raising one needs evidence from a run.
|
|
291
308
|
|
|
292
309
|
1. Run the workflow end to end in Pi against the certified revision.
|
|
293
310
|
2. Record what the workflow produced, not that it started. A `dependency-gated` skill rises to
|
|
294
311
|
`adapted` only when its delegation or question steps completed against a real provider.
|
|
295
312
|
3. An `experimental` skill rises only when every Cursor-specific step has a Pi equivalent that
|
|
296
|
-
|
|
313
|
+
a run exercised, not one the host mapping only describes.
|
|
297
314
|
4. An `unsupported` skill rises only when the missing host feature exists in Pi.
|
|
298
315
|
5. Add or update a test that fails if the claim regresses.
|
|
299
316
|
|
|
@@ -332,7 +349,7 @@ Lowering a tier needs one reproduction of the failure and a note saying what bro
|
|
|
332
349
|
| `principle-test-behavior-not-implementation` | `/skill:pistack-principle-test-behavior-not-implementation` | - | - | - |
|
|
333
350
|
| `principle-type-system-discipline` | `/skill:pistack-principle-type-system-discipline` | - | - | - |
|
|
334
351
|
| `setup-pstack` | `/skill:pistack-setup` | - | - | The upstream body only writes a Cursor .mdc rule, so the adapter replaces it with a Pi setup workflow. |
|
|
335
|
-
| `sharpen` | `/skill:pistack-sharpen` | - | - | Verified end to end in Pi on Linux at pstack 0.15.36: it handed back four short sections and a rewritten prompt for a vague gateway task, left the decisions that were the user's open, and did not start the task. Since pstack 0.15.29 it hands back a rewritten prompt and does not do the task. Its body is prompt-only, with no tool, delegation, or executable, and it runs only when you type it. |
|
|
352
|
+
| `sharpen` | `/skill:pistack-sharpen` | - | - | Verified end to end in Pi on Linux at pstack 0.15.36: it handed back four short sections and a rewritten prompt for a vague gateway task, left the decisions that were the user's open, and did not start the task. Since pstack 0.15.29 it hands back a rewritten prompt and does not do the task. Its body is prompt-only, with no tool, delegation, or executable, and it runs only when you type it. Since pstack 0.15.42 the rewritten prompt asks the agent to prototype any choice an experiment can settle and leaves only product or preference choices to the user. That wording has not been rerun in Pi. |
|
|
336
353
|
| `tdd` | `/skill:pistack-tdd` | - | - | - |
|
|
337
354
|
| `technical-writing` | `/skill:pistack-technical-writing` | - | - | - |
|
|
338
355
|
| `typescript-best-practices` | `/skill:pistack-typescript-best-practices` | - | - | - |
|
|
@@ -342,7 +359,7 @@ Lowering a tier needs one reproduction of the failure and a note saying what bro
|
|
|
342
359
|
|
|
343
360
|
| upstream skill | Pi skill | capabilities | executables | why |
|
|
344
361
|
| --- | --- | --- | --- | --- |
|
|
345
|
-
| `arena` | `/skill:pistack-arena` | `delegation` | `git` | The whole skill is spawning N candidate subagents plus a judge. Since pstack 0.15.36 the judge must come from a model family other than the parent's. The adapter maps the cross-judge pool to the `arena-cross-judge-pool` panel role, which uses `judgment`, then `general` while unset, so when the parent knows its model, the judge
|
|
362
|
+
| `arena` | `/skill:pistack-arena` | `delegation` | `git` | The whole skill is spawning N candidate subagents plus a judge. Since pstack 0.15.36 the judge must come from a model family other than the parent's, and since pstack 0.15.42 from another vendor, where Fable and the other Claude models are one vendor. Its default runners and pool are now one Fable, one Opus, and one Gemini model. The adapter maps the cross-judge pool to the `arena-cross-judge-pool` panel role, which uses `judgment`, then `general` while unset, so when the parent knows its model, the judge comes from the first pool entry from another vendor. When no entry qualifies, arena tries `judgment`, then `general`, and only when neither is from another vendor does it skip the judge, pick the base alone, and say so. The status report and `check` warn when that pool is on one model. When the parent does not know its model, arena tries the pool in order anyway and says the vendor check did not run. Verified end to end in Pi on Linux at pstack 0.15.36 with pi-subagents: two candidates on the `judgment` and `general` roles, then a judge from the family other than the parent's, which scored them. The parent picked a base, took the fixes for two bugs from the other candidate, fixed a third that both shared, and finished with passing tests. It left the candidate worktrees for comparison. The pstack 0.15.42 vendor rule has not run in Pi. |
|
|
346
363
|
| `create-formal-verification-skill` | `/skill:pistack-create-formal-verification-skill` | - | - | Manual invocation only; no delegation. It installs or runs the verification tool it chooses and temporarily breaks product code to prove the check. Generated skills belong under `.pi/skills/` in Pi. Verified on a Windows Node finite-state fixture; other toolchains need their own prerequisites. Since pstack 0.15.32, a generated TLA+ check tells the next agent to read its results with statechart, which has its own prerequisites. |
|
|
347
364
|
| `create-verification-skill` | `/skill:pistack-create-verification-skill` | - | - | Only the generated skill's output path needs remapping to Pi's skill directory. |
|
|
348
365
|
| `interrogate` | `/skill:pistack-interrogate` | `delegation` | `git` | Multi-model adversarial review is entirely one subagent per configured model. Since pstack 0.15.36 it picks adversaries the way arena picks runners, from the `interrogate reviewers` line, and gives each one its own worktree with the scope's uncommitted changes. Each adversary must prove a finding with a run, and the skill keeps only findings whose command it reruns and sees fail. It reports findings and applies no fixes, but adversaries write and run repro commands, and the skill reruns them, all with your permissions. A worktree is not a sandbox, so read each repro before it reruns, especially on code you do not trust. The rubric, the lead's judgment, and the code-quality lens are gone. Verified end to end in Pi on Linux at pstack 0.15.36 with pi-subagents: two adversaries on the `judgment` and `general` roles, from two model families, each in its own worktree, proved the same two real bugs, and the skill reran both repros before it reported them. It left the adversary worktrees in the temporary folder and named the commands that remove them. |
|
|
@@ -358,13 +375,13 @@ Lowering a tier needs one reproduction of the failure and a note saying what bro
|
|
|
358
375
|
| `blast-radius` | `/skill:pistack-blast-radius` | - | `git`, `gh` | Needs git and gh to read the diff, commits, and PR before any analysis. |
|
|
359
376
|
| `correct` | `/skill:pistack-correct` | - | `git` | Mines commit history, then commits fixes and edits lint, CI, and agent instruction files. Run it on a branch you will review. |
|
|
360
377
|
| `figure-it-out` | `/skill:pistack-figure-it-out` | `delegation` | - | Phase B fan-out and the architect/arena routing require parallel subagents. |
|
|
361
|
-
| `how` | `/skill:pistack-how` | `delegation` | - |
|
|
378
|
+
| `how` | `/skill:pistack-how` | `delegation` | - | Since pstack 0.15.42 it spawns one read-only subagent that explores and explains in one pass, and nothing runs inline. That path has not run in Pi. |
|
|
362
379
|
| `maintain-formal-verification-skill` | `/skill:pistack-maintain-formal-verification-skill` | - | `git` | Verified end to end on a Windows Node finite-state fixture at pstack 0.15.28. Manual invocation only; no delegation. It diffs the covered code with git from the verify skill's sync commit, edits only the verification and the verify skill, and temporarily breaks product code to prove an updated check can still fail. In Pi, look for the verify skill under `.pi/skills/`; the verification tool needs its own prerequisites. |
|
|
363
380
|
| `maintain-verification-skill` | `/skill:pistack-maintain-verification-skill` | `delegation` | - | Source wave launches one read-only subagent per feature file. |
|
|
364
381
|
| `no-comments` | `/skill:pistack-no-comments` | `delegation` | `git` | Step 1 must spawn the comment-sicko agent; the skill only triages its report. |
|
|
365
|
-
| `recall` | `/skill:pistack-recall` | `delegation` | `git`, `gh` | Its core corpus is Cursor's on-disk transcript JSONL. The host mapping points it at this workspace's Pi session files instead. Verified end to end in Pi on Linux at pstack 0.15.36: in a fresh session it found the two earlier chats in the folder that holds `$PI_SESSION_FILE`, read their user messages, checked their claims against git and the code, and rebuilt the threads, problems, and next move. With two chats to read, it read them itself, so it did not fan out to subagents, and that step has not run in Pi. A run before the host mapping said to start from `$PI_SESSION_FILE` looked in the default folder instead, which a custom `PI_CODING_AGENT_DIR` had moved, and found nothing. |
|
|
366
|
-
| `teach` | `/skill:pistack-teach` | `delegation` | - | It runs the how and why
|
|
367
|
-
| `why` | `/skill:pistack-why` |
|
|
382
|
+
| `recall` | `/skill:pistack-recall` | `delegation` | `git`, `gh` | Its core corpus is Cursor's on-disk transcript JSONL. The host mapping points it at this workspace's Pi session files instead. Verified end to end in Pi on Linux at pstack 0.15.36: in a fresh session it found the two earlier chats in the folder that holds `$PI_SESSION_FILE`, read their user messages, checked their claims against git and the code, and rebuilt the threads, problems, and next move. With two chats to read, it read them itself, so it did not fan out to subagents, and that step has not run in Pi. A run before the host mapping said to start from `$PI_SESSION_FILE` looked in the default folder instead, which a custom `PI_CODING_AGENT_DIR` had moved, and found nothing. Since pstack 0.15.42 its shared-record sweep spawns one investigator per source itself instead of reusing why's investigators, and that step has not run in Pi. |
|
|
383
|
+
| `teach` | `/skill:pistack-teach` | `delegation` | - | It runs the how skill, which needs a subagent, and the why skill, which since pstack 0.15.42 runs inline and needs git and gh. Check why's prerequisites too. |
|
|
384
|
+
| `why` | `/skill:pistack-why` | - | `git`, `gh` | Since pstack 0.15.42 it spawns no subagents. It answers inline from the commits and PR discussions behind the target, and from other sources when that trail is thin, so it needs git, and gh for the PR discussions. The inline path has not run in Pi. |
|
|
368
385
|
|
|
369
386
|
### experimental (7)
|
|
370
387
|
|
|
@@ -373,9 +390,9 @@ Lowering a tier needs one reproduction of the failure and a note saying what bro
|
|
|
373
390
|
| `automate-me` | `/skill:pistack-automate-me` | `delegation`, `structured-question` | `git`, `gh` | Its mining step reads this workspace's transcripts. The host mapping points it at this workspace's Pi session files, which use a different format. Verified end to end in Pi on Linux at pstack 0.15.36: it read only this workspace's sessions, told the parent chat from its subagents' files, found no stated preference there, and changed nothing. The built-in create-skill skill it drafts with has no Pi equivalent, and that step has not run in Pi. |
|
|
374
391
|
| `ketchup-video` | `/skill:pistack-ketchup-video` | - | - | New in pstack 0.15.32. One end-to-end Pi run on native Windows drew its slides and rendered an mp4 with `ffmpeg` alone, before pstack 0.15.36 added subtitles. Verified end to end in Pi on Linux at pstack 0.15.36: it drew five slides as SVG, rendered a 35-second 1280x720 mp4 with `ffmpeg`, with each subtitle burned into a strip below its slide, and checked frames from the file. Manual invocation only; no delegation or structured question. It follows ketchup, read-only, but shows what happened as a short slide video with no audio, rendered outside the repository, with the waiting calls as text under it. Since pstack 0.15.36 it burns a one-line subtitle into each slide's frames, in a strip below the slide, and measures each slide's hold by matching the rendered frames to the slide. Its body names no renderer, so it needs whatever video tool the session has, such as `ffmpeg`, and Pi has no image generation. Its reply embedded the video as an HTML `video` tag with the file path, and whether a Pi client plays it inline is unverified. When the render fails, it says so in one line and gives only the calls. |
|
|
375
392
|
| `poteto-help` | `/skill:pistack-poteto-help` | - | - | Its answers describe Cursor setup, the slash menu, and Custom Modes. A request for work runs poteto-mode, which is experimental. It asks a multiple-choice question only when the need is unclear. Its README and guide links resolve under the status report's plugin directory. |
|
|
376
|
-
| `poteto-mode` | `/skill:pistack-poteto-mode` | `delegation`, `structured-question` | `git`, `gh` | Sticky-mode hosting plus references to Cursor built-in and plugin skills only partially map to Pi. Since pstack 0.15.36 the Orchestrate playbook calls no scripts, because the `orch` helpers are gone, and watches PRs through event subscriptions only when the harness has them. Pi has none. Its Project tracker and Project store, and forking a worker with its built state, have no Pi equivalent; the host mapping says what to do instead. Verified end to end in Pi on Linux at pstack 0.15.36 with pi-subagents and a question tool: Orchestrate ran a two-unit program in a linked worktree, with an owner per unit on its own branch and worktree, a verifier from another model family at the owner's head, and local merges. It kept the tracker and store in a folder it added to the exclude file Git resolved for the worktree, and handed state to new agents instead of messaging old ones. Its other playbooks and sticky mode were not rerun. |
|
|
393
|
+
| `poteto-mode` | `/skill:pistack-poteto-mode` | `delegation`, `structured-question` | `git`, `gh` | Sticky-mode hosting plus references to Cursor built-in and plugin skills only partially map to Pi. Since pstack 0.15.36 the Orchestrate playbook calls no scripts, because the `orch` helpers are gone, and watches PRs through event subscriptions only when the harness has them. Pi standalone has none; a T3 Code host can provide them when it exposes `mcp__t3-code__watch_pull_request`. This is a host capability, not a guarantee of either delegation backend. Its Project tracker and Project store, and forking a worker with its built state, have no Pi equivalent; the host mapping says what to do instead. Verified end to end in Pi on Linux at pstack 0.15.36 with pi-subagents and a question tool: Orchestrate ran a two-unit program in a linked worktree, with an owner per unit on its own branch and worktree, a verifier from another model family at the owner's head, and local merges. It kept the tracker and store in a folder it added to the exclude file Git resolved for the worktree, and handed state to new agents instead of messaging old ones. Its other playbooks and sticky mode were not rerun. Since pstack 0.15.42 it tells the user to rerun setup when the model rule has no `# defaults: 2` line. The host mapping says that check does not apply to the adapter configuration. |
|
|
377
394
|
| `reflect` | `/skill:pistack-reflect` | `delegation` | - | Its reviewers read the active transcript, and the host mapping points them at the current Pi session file. Verified end to end in Pi on Linux at pstack 0.15.36 with pi-subagents: three reviewers on two model families and a synthesizer read the session file, and the run found a regression the earlier review missed. When told to apply two edits, it copied the upstream skill from the plugin directory to a Pi skill location under a new name and edited only the copy. Its create-skill routing for a new skill is Cursor-specific and has not run in Pi. It never opens a PR or pushes. |
|
|
378
|
-
| `statechart` | `/skill:pistack-statechart` | - | `node`, `sh`, `dirname`, `sed` | Lowered from dependency-gated at pstack 0.15.34, because its new scripts crash on native Windows, a certified platform. There, the launcher starts `/bin/sh` by that path to stop TLC's process group, Node cannot start it even when `sh` is on PATH, and with no error handler the run exits 1 with `spawn /bin/sh ENOENT`. It writes no output and leaves an empty hidden staging folder beside the output folder and an empty `statechart-` folder in the temporary folder, so delete those by hand. `/pistack-check` still clears it there, because the gates are found. Use it from Linux, macOS, or WSL. Verified end to end on a Linux checkout fixture at pstack 0.15.34, and again at pstack 0.15.36, where it found the uncapped refill in a token bucket as the shortest path to a broken rule. The Windows end-to-end evidence from pstack 0.15.32 and 0.15.30 describes the scripts it replaced. Manual invocation only; no delegation or structured question. `scripts/statechart` is a POSIX `sh` launcher for TypeScript scripts, and it runs `dirname` and `sed` on every start. It runs the scripts under Node 22.18 to 22.x or Node 23.6 or later, then Bun 1.1.5 or later, then Deno 2.0 or later, and exits with code 127 when none is found. The prerequisite check gates on `node` but cannot check its version. The launcher does: with only an older Node on PATH, it names the versions it needs and exits 127. The command checks a TLA+ spec with TLC and writes `report.md` and TLC output to a new or empty `--out` folder, `SPEC-chart` beside the spec by default, and refuses a folder that is not empty. It also writes `chart.mmd`, except when TLC finds more than 500 states and no rule breaks. It stages its work in a hidden folder beside the output folder and in the system temporary folder, and deletes both. TLC runs from `TLA2TOOLS`, the path to a downloaded `tla2tools.jar`, with the binary in `JAVA` or `java` from PATH. Without `TLA2TOOLS`, it uses TLC 1.7.4 and Java temurin-25 from `mise`, and installs whichever is missing with `mise`, which needs the network. `JAVA` then changes only the binary that runs TLC, and does not skip the `mise` lookup or install of Java. Either `mise` or `TLA2TOOLS` with Java works, so neither is gated, and the script names what is missing when neither is set up. It imports the `--view` module as JavaScript with full process permissions, so review an existing view before you run it. Since pstack 0.15.34 the scripts render no images and do not run `npm`. For an image, the body has the agent install `pele@1.9.0` with `npm` once, outside the repository, which needs `npm` and the network, and render SVG with it. The body does not ask for `--ignore-scripts`, so pass it, and rendering still imports Pele with full process permissions. Run it in a scratch folder outside the repository. Git for Windows bundles `sh`, `dirname`, and `sed`, but not `node`, `mise`, or Java. |
|
|
395
|
+
| `statechart` | `/skill:pistack-statechart` | - | `node`, `sh`, `dirname`, `sed` | Lowered from dependency-gated at pstack 0.15.34, because its new scripts crash on native Windows, a certified platform. There, the launcher starts `/bin/sh` by that path to stop TLC's process group, Node cannot start it even when `sh` is on PATH, and with no error handler the run exits 1 with `spawn /bin/sh ENOENT`. It writes no output and leaves an empty hidden staging folder beside the output folder and an empty `statechart-` folder in the temporary folder, so delete those by hand. `/pistack-check` still clears it there, because the gates are found. Use it from Linux, macOS, or WSL. Verified end to end on a Linux checkout fixture at pstack 0.15.34, and again at pstack 0.15.36, where it found the uncapped refill in a token bucket as the shortest path to a broken rule. The Windows end-to-end evidence from pstack 0.15.32 and 0.15.30 describes the scripts it replaced. Manual invocation only; no delegation or structured question. `scripts/statechart` is a POSIX `sh` launcher for TypeScript scripts, and it runs `dirname` and `sed` on every start. It runs the scripts under Node 22.18 to 22.x or Node 23.6 or later, then Bun 1.1.5 or later, then Deno 2.0 or later, and exits with code 127 when none is found. The prerequisite check gates on `node` but cannot check its version. The launcher does: with only an older Node on PATH, it names the versions it needs and exits 127. The command checks a TLA+ spec with TLC and writes `report.md` and TLC output to a new or empty `--out` folder, `SPEC-chart` beside the spec by default, and refuses a folder that is not empty. It also writes `chart.mmd`, except when TLC finds more than 500 states and no rule breaks. It stages its work in a hidden folder beside the output folder and in the system temporary folder, and deletes both. TLC runs from `TLA2TOOLS`, the path to a downloaded `tla2tools.jar`, with the binary in `JAVA` or `java` from PATH. Without `TLA2TOOLS`, it uses TLC 1.7.4 and Java temurin-25 from `mise`, and installs whichever is missing with `mise`, which needs the network. `JAVA` then changes only the binary that runs TLC, and does not skip the `mise` lookup or install of Java. Either `mise` or `TLA2TOOLS` with Java works, so neither is gated, and the script names what is missing when neither is set up. It imports the `--view` module as JavaScript with full process permissions, so review an existing view before you run it. Since pstack 0.15.34 the scripts render no images and do not run `npm`. For an image, the body has the agent install `pele@1.9.0` with `npm` once, outside the repository, which needs `npm` and the network, and render SVG with it. The body does not ask for `--ignore-scripts`, so pass it, and rendering still imports Pele with full process permissions. Run it in a scratch folder outside the repository. Git for Windows bundles `sh`, `dirname`, and `sed`, but not `node`, `mise`, or Java. At pstack 0.15.42 the Mermaid backend can draw the edges into a nested state as one edge into its parent plus start edges inside it, and a SANY parse failure reruns SANY to print its errors. Those script changes add no executable, leave the `/bin/sh` launcher unchanged, and have not run in Pi. |
|
|
379
396
|
| `swarm` | `/skill:pistack-swarm` | `delegation` | - | Cloud background workers and cloud_base_branch have no Pi mapping; only local fan-out approximates it. |
|
|
380
397
|
|
|
381
398
|
### unsupported (1)
|
|
@@ -395,8 +412,8 @@ T3 Code's `delegate_task` has no named agents. Generation writes each agent's pr
|
|
|
395
412
|
`adapter/agents/<pi-name>.md`, with skill paths relative to that file. The parent begins the
|
|
396
413
|
child's task by telling it to read that file as its system prompt.
|
|
397
414
|
|
|
398
|
-
Both agents also register through the `pi-subagents:runtime-agent-register:v1` contract.
|
|
399
|
-
that event, the status report says
|
|
415
|
+
Both agents also register through the `pi-subagents:runtime-agent-register:v1` contract. When no
|
|
416
|
+
extension handles that event, the status report says the agents did not register.
|
|
400
417
|
|
|
401
418
|
## Delegation providers
|
|
402
419
|
|
|
@@ -418,6 +435,13 @@ marker text is reported as a warning, with its bytes unchanged. An agent whose P
|
|
|
418
435
|
skill name rules is skipped with an error, before its name reaches a file name or any generated
|
|
419
436
|
text.
|
|
420
437
|
|
|
438
|
+
The generated instructions tell the model to name a role, not a model, by passing
|
|
439
|
+
`pstack:<role>` as the delegation's model. Before either tool runs, the adapter rewrites that
|
|
440
|
+
reference to the role's model and thinking, taking the first model of a pool that the model
|
|
441
|
+
filter allows, and then applies the filter as for any other call. Unit and integration tests drive
|
|
442
|
+
this through the real `tool_call` handler. No workflow has yet run end to end with role
|
|
443
|
+
references, so no tier claim rests on them.
|
|
444
|
+
|
|
421
445
|
Unit tests cover detection, activation order, and the generated instructions for both providers.
|
|
422
446
|
No tier changed when T3 support arrived, because no delegating workflow has yet run end to end
|
|
423
447
|
through `delegate_task`. That run is the evidence the tier rules above require.
|
|
@@ -427,11 +451,10 @@ through `delegate_task`. That run is the evidence the tier rules above require.
|
|
|
427
451
|
`setup-pstack` writes a Cursor `.mdc` rule, so the adapter substitutes its own body and
|
|
428
452
|
publishes it as `/skill:pistack-setup`. Upstream's optional closing offers to run
|
|
429
453
|
`create-verification-skill` and `create-formal-verification-skill` are left out of the Pi body;
|
|
430
|
-
both skills stay available by name. The upstream skill
|
|
454
|
+
both skills stay available by name. The registry still lists the upstream skill, and the
|
|
431
455
|
wrapper frontmatter records `pistack-upstream-skill: setup-pstack`.
|
|
432
456
|
|
|
433
457
|
## Unknown skills
|
|
434
458
|
|
|
435
459
|
A skill present upstream but absent from the registry is generated as `experimental` and reported
|
|
436
|
-
in diagnostics. A skill in the registry but absent upstream is reported as removed.
|
|
437
|
-
is silent.
|
|
460
|
+
in diagnostics. A skill in the registry but absent upstream is reported as removed.
|
package/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
sessionFilterFromEntries,
|
|
19
19
|
} from "./src/filter.ts";
|
|
20
20
|
import { gateDelegation } from "./src/gate.ts";
|
|
21
|
+
import { applyRewrite, routeDelegation } from "./src/route.ts";
|
|
21
22
|
import { checkPrerequisites, detectCapabilities, type ToolDescriptor } from "./src/capabilities.ts";
|
|
22
23
|
import {
|
|
23
24
|
type AdapterConfig,
|
|
@@ -149,24 +150,37 @@ export default function (pi: ExtensionAPI) {
|
|
|
149
150
|
sessionFilter = sessionFilterFromEntries(ctx.sessionManager.getBranch() as never[]);
|
|
150
151
|
});
|
|
151
152
|
|
|
152
|
-
//
|
|
153
|
-
//
|
|
153
|
+
// A `pstack:<role>` model is rewritten to the model the adapter picks for that role. The model
|
|
154
|
+
// filter then applies to every delegation in this session, pstack or not, because a call that
|
|
155
|
+
// names a concrete model names no role. A blocked call fails; nothing retries it.
|
|
154
156
|
pi.on("tool_call", async (event, ctx) => {
|
|
155
157
|
if (event.toolName !== T3_DELEGATE_TOOL && event.toolName !== SUBAGENT_TOOL) return;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
reason: `Blocked by the pstack adapter: its config could not be read, so it cannot tell whether a saved model filter applies. ${configError} Fix the config and reload, or ask the user whether to set an explicit session filter with \`set_filter\`.`,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
const decision = gateDelegation(event.toolName, event.input, {
|
|
158
|
+
const unreadable = configBlock();
|
|
159
|
+
if (unreadable) return unreadable;
|
|
160
|
+
const context = {
|
|
163
161
|
filter: filterOf(activeFilter(sessionFilter, savedFilter)),
|
|
164
162
|
parentModel: parentModelOf(ctx),
|
|
165
|
-
resolveModel: (model) => resolveRegistryModel(ctx, model),
|
|
166
|
-
}
|
|
163
|
+
resolveModel: (model: string) => resolveRegistryModel(ctx, model),
|
|
164
|
+
};
|
|
165
|
+
const route = await routeDelegation(event.toolName, event.input, { ...context, config: state?.config, parentThinking: ctx.thinkingLevel });
|
|
166
|
+
if (route.kind === "block") return { block: true, reason: route.reason };
|
|
167
|
+
if (route.kind === "rewrite") applyRewrite(event.input as Record<string, unknown>, route.input);
|
|
168
|
+
// Read the config state, filter, and parent model again: a pick that waits on a service could span
|
|
169
|
+
// a reload, a `set_filter`, or a model change.
|
|
170
|
+
const unreadableNow = configBlock();
|
|
171
|
+
if (unreadableNow) return unreadableNow;
|
|
172
|
+
const decision = gateDelegation(event.toolName, event.input, { ...context, filter: filterOf(activeFilter(sessionFilter, savedFilter)), parentModel: parentModelOf(ctx) });
|
|
167
173
|
if (decision.kind === "block") return { block: true, reason: decision.reason };
|
|
168
174
|
});
|
|
169
175
|
|
|
176
|
+
function configBlock(): { block: true; reason: string } | undefined {
|
|
177
|
+
if (sessionFilter !== undefined || configError === undefined) return undefined;
|
|
178
|
+
return {
|
|
179
|
+
block: true,
|
|
180
|
+
reason: `Blocked by the pstack adapter: its config could not be read, so it cannot tell whether a saved model filter applies. ${configError} Fix the config and reload, or ask the user whether to set an explicit session filter with \`set_filter\`.`,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
170
184
|
pi.on("resources_discover", async () => {
|
|
171
185
|
if (!state) return {};
|
|
172
186
|
return { skillPaths: [state.generation.skillsDir] };
|
|
@@ -210,7 +224,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
210
224
|
model: Type.Optional(
|
|
211
225
|
Type.Union([Type.String(), Type.Array(Type.String())], {
|
|
212
226
|
description:
|
|
213
|
-
"`provider/model` id, or `inherit` to clear the role. A list of ids gives an ordinary role a pool:
|
|
227
|
+
"`provider/model` id, or `inherit` to clear the role. A list of ids gives an ordinary role a pool, ranked in list order: a `pstack:<role>` delegation runs on the first one the model filter allows. For a panel role such as `arena-runners`, a string of entries separated by commas, one seat each.",
|
|
214
228
|
}),
|
|
215
229
|
),
|
|
216
230
|
thinking: Type.Optional(
|
|
@@ -467,7 +481,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
467
481
|
const listed = (typeof plan.value === "string" ? [plan.value] : plan.value).map((entry) => `\`${entry}\``).join(", ");
|
|
468
482
|
return typeof plan.value === "string"
|
|
469
483
|
? `Role \`${role}\` now uses ${listed}. A pool of one is written as a plain model. Written to ${path}.${note}`
|
|
470
|
-
: `Role \`${role}\` now has a pool of ${plan.value.length} models: ${listed}.
|
|
484
|
+
: `Role \`${role}\` now has a pool of ${plan.value.length} models: ${listed}. A \`pstack:${role}\` delegation runs on the first one the model filter allows. Written to ${path}.${note}`;
|
|
471
485
|
}
|
|
472
486
|
|
|
473
487
|
function setFilter(
|
package/package.json
CHANGED
package/src/certified.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
export const CERTIFIED = {
|
|
6
6
|
repo: "https://github.com/cursor/plugins",
|
|
7
7
|
ref: "main",
|
|
8
|
-
commit: "
|
|
8
|
+
commit: "bf0c7cd80abdbf7fe47b634978a7d1b4071f71a7",
|
|
9
9
|
pluginPath: "pstack",
|
|
10
|
-
pstackVersion: "0.15.
|
|
10
|
+
pstackVersion: "0.15.42",
|
|
11
11
|
skillCount: 58,
|
|
12
12
|
agentCount: 2,
|
|
13
13
|
} as const;
|
package/src/config.ts
CHANGED
|
@@ -10,8 +10,9 @@ import type { DelegationProvider, SourceSelection, Trust } from "./types.ts";
|
|
|
10
10
|
import { type ModelFilter, parseModelFilter } from "./filter.ts";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* A role's model. A list is a pool of alternatives for an ordinary role: each delegation
|
|
14
|
-
* eligible entry. A panel role keeps its
|
|
13
|
+
* A role's model. A list is a ranked pool of alternatives for an ordinary role: each delegation runs
|
|
14
|
+
* on the first eligible entry unless it overrides the ranking. A panel role keeps its
|
|
15
|
+
* space-separated string, one seat per entry.
|
|
15
16
|
*/
|
|
16
17
|
export type ModelValue = string | readonly string[];
|
|
17
18
|
|
package/src/gate.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { splitModelSpec } from "./config.ts";
|
|
2
2
|
import { SUBAGENT_TOOL, T3_DELEGATE_TOOL } from "./delegation.ts";
|
|
3
|
+
import { safeEcho } from "./echo.ts";
|
|
3
4
|
import { type ModelFilter, describeFilter, isRestricting, modelAllowed } from "./filter.ts";
|
|
4
5
|
|
|
5
6
|
export type GateDecision = { readonly kind: "allow" } | { readonly kind: "block"; readonly reason: string };
|
|
@@ -50,7 +51,7 @@ function checkModel(requested: unknown, where: string, context: GateContext & {
|
|
|
50
51
|
}
|
|
51
52
|
const resolved = context.resolveModel(splitModelSpec(requested.trim()).model);
|
|
52
53
|
if (resolved === undefined) {
|
|
53
|
-
return block(`${where} asks for
|
|
54
|
+
return block(`${where} asks for ${safeEcho(requested.trim())}, which this Pi installation does not know, so the filter cannot allow it.`, context.filter);
|
|
54
55
|
}
|
|
55
56
|
if (!modelAllowed(context.filter, resolved)) return block(`${where} asks for \`${resolved}\`, which the filter excludes.`, context.filter);
|
|
56
57
|
return ALLOW;
|
|
@@ -83,7 +84,7 @@ function gateT3(args: Record<string, unknown>, context: GateContext): GateDecisi
|
|
|
83
84
|
}
|
|
84
85
|
const provider = [providerInstanceId, driverKind].find((value) => value !== undefined && value !== "pi");
|
|
85
86
|
if (provider !== undefined) {
|
|
86
|
-
return block(`\`delegate_task\` targets T3 provider
|
|
87
|
+
return block(`\`delegate_task\` targets T3 provider ${safeEcho(provider)}, whose models are not Pi registry ids, so the filter cannot check them. Use \`providerInstanceId: "pi"\`.`, filter);
|
|
87
88
|
}
|
|
88
89
|
if (model === undefined) return checkParent("`delegate_task`", scoped);
|
|
89
90
|
return checkModel(model, "`delegate_task`", scoped);
|
package/src/generate.ts
CHANGED
|
@@ -464,31 +464,52 @@ ${agentSystemPrompt(notes, upstreamPrompt)}
|
|
|
464
464
|
* strongest-judgment delegate's helpers would all run at its level. The rule travels in every brief.
|
|
465
465
|
*/
|
|
466
466
|
const NESTED_DELEGATION_RULE = `Nested delegation: every agent that delegates, a child included, gives each new child an
|
|
467
|
-
explicit role
|
|
468
|
-
agent's model or thinking. The one exception is a panel seat
|
|
469
|
-
\`panel roles\` table, which runs on the delegating agent's
|
|
470
|
-
configured it so. Put this line in each brief, verbatim,
|
|
471
|
-
no-commit rule: "If you start agents,
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
467
|
+
explicit role as \`pstack:<role>\`, using \`pstack:general\` when no other role fits, and
|
|
468
|
+
never lets it inherit the delegating agent's model or thinking. The one exception is a panel seat
|
|
469
|
+
from an \`inherit\` entry in the \`panel roles\` table, which runs on the delegating agent's
|
|
470
|
+
model and thinking because the user configured it so. Put this line in each brief, verbatim,
|
|
471
|
+
just before any read-only or no-commit rule: "If you start agents, pass each one's model as \`pstack:<role>\` for its own pstack role, using \`pstack:general\` when no role fits, so the pstack adapter gives it the model and thinking that the \`pstack_adapter\` status report shows for that role. Never let one inherit your model or thinking, except a panel seat from an \`inherit\` entry in that report's \`panel roles\` table, and never raise a role above its configured level."`;
|
|
472
|
+
|
|
473
|
+
const MODEL_POLICY_SECTION = `Model roles, pools, and the model filter work the same on both backends.
|
|
474
|
+
|
|
475
|
+
- Name a role, not a model. For every pstack delegation, pass \`pstack:<role>\` as the model, such
|
|
476
|
+
as \`pstack:feature\`, using \`pstack:general\` when no other role fits. Before the call runs, the
|
|
477
|
+
adapter replaces it with the role's model and thinking, or, when the role inherits, with no
|
|
478
|
+
model, so the child inherits the parent model and thinking. The backend sections below say where the
|
|
479
|
+
reference goes.
|
|
480
|
+
- A role whose \`model roles\` row reads "pool, ranked" lists alternatives in rank order. The
|
|
481
|
+
adapter picks the first one the model filter allows, with that entry's thinking. To override the
|
|
482
|
+
ranking for one task, pass \`pstack:<role>@<provider/model>\` with another allowed model from the
|
|
483
|
+
role's pool, and say in your reply which model you picked and why. The adapter blocks an
|
|
484
|
+
override that is not an allowed entry of the pool.
|
|
485
|
+
A pool is not a retry list and not a panel: start one child per task, and when a delegation
|
|
486
|
+
fails, report the failure instead of trying another model from the pool.
|
|
487
|
+
- To lower a role's thinking, end the reference with \`:<level>\`, as in \`pstack:feature:low\` or
|
|
488
|
+
\`pstack:feature@<provider/model>:low\`. A level above the role's configured one runs at the
|
|
489
|
+
configured one, and a role whose thinking reads "backend default" keeps the backend's default.
|
|
490
|
+
- The adapter blocks a reference to an unknown role, to a panel role, or to a blocked role, and
|
|
491
|
+
the block reason says what to do next. Report a blocked call instead of retrying it.
|
|
492
|
+
- A concrete \`provider/model\` still runs, checked only by the model filter. Use it for a
|
|
493
|
+
configured panel seat, which names its own model, and not for a pstack role.
|
|
481
494
|
- A role whose row reads "BLOCKED" has no model the model filter allows. Do not delegate its work,
|
|
482
495
|
and do not move the work to \`general\` or to your own model. Tell the user which role is blocked
|
|
483
496
|
and why.
|
|
484
497
|
- A panel seat that reads "BLOCKED" does not run. Run the other seats, and say in your reply which
|
|
485
|
-
seat did not run and why.
|
|
498
|
+
seat did not run and why. The seats of an unset panel are roles: delegate them as
|
|
499
|
+
\`pstack:judgment\` and \`pstack:general\`, so a seat that reads "ranked" runs once on the first
|
|
500
|
+
allowed model unless you override the ranking as for a pool. A configured seat names its model
|
|
501
|
+
and thinking, and the adapter does not rewrite a concrete model, so pass both yourself: on T3,
|
|
502
|
+
\`target: { providerInstanceId: "pi", model: "<provider/model>", options: { thinking: "<level>" } }\`;
|
|
503
|
+
on pi-subagents, \`model: "<provider/model>:<level>"\`. For a seat at "backend default", pass
|
|
504
|
+
\`options: {}\` on T3, which clears the inherited thinking, or no suffix on pi-subagents. An
|
|
505
|
+
\`inherit\` seat omits \`target\` on T3 and passes your own model on pi-subagents.
|
|
486
506
|
- When the \`model filter\` row of the status report names a filter, the adapter blocks any
|
|
487
507
|
\`delegate_task\` or \`subagent\` call in this session whose model the filter excludes, or whose
|
|
488
508
|
model it cannot tell. That covers a call with no model while your own model is excluded, a model
|
|
489
509
|
Pi does not know, a T3 provider other than \`pi\`, a \`subagent\` child without \`model\`, a
|
|
490
510
|
\`subagent\` workflow script, and a \`subagent\` action that starts, restarts, steers, or schedules
|
|
491
|
-
a child or configures a watchdog. A T3 target with a named environment is blocked too.
|
|
511
|
+
a child or configures a watchdog. A T3 target with a named environment is blocked too. The
|
|
512
|
+
adapter replaces a \`pstack:<role>\` reference first, and checks the model it picked.
|
|
492
513
|
Report a blocked call instead of retrying it. Keep the active filter unchanged unless the user
|
|
493
514
|
requests a change. Wait for \`set_filter\` to finish before issuing delegation calls.
|
|
494
515
|
- Children do not inherit a session filter. Put the brief line that the \`check\` report gives into
|
|
@@ -520,20 +541,19 @@ function t3Section(namespace: string): string {
|
|
|
520
541
|
\`task\` with "Read <path> and follow it as your system prompt.", where <path> is the absolute
|
|
521
542
|
path of \`agents/${namespace}-poteto-agent.md\` beside this file. The \`agent prompts\` row of
|
|
522
543
|
the status report gives that directory.
|
|
523
|
-
- Model:
|
|
524
|
-
|
|
525
|
-
the
|
|
526
|
-
\`
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
544
|
+
- Model: pass \`target: { providerInstanceId: "pi", model: "pstack:<role>" }\`. The adapter
|
|
545
|
+
writes the picked model into \`target.model\` and its thinking into \`target.options.thinking\`.
|
|
546
|
+
When the role's thinking reads "backend default", it removes that option and sends
|
|
547
|
+
\`options\` empty, which clears the parent's settings.
|
|
548
|
+
An unconfigured role uses the \`general\` role. Only when \`general\` is unconfigured too does
|
|
549
|
+
the adapter remove the model and its thinking, so the child inherits the parent model and
|
|
550
|
+
thinking. A role
|
|
551
|
+
reference always runs on the \`pi\` provider, so the adapter blocks one with another
|
|
552
|
+
\`providerInstanceId\` or \`driverKind\`.
|
|
531
553
|
- ${NESTED_DELEGATION_RULE}
|
|
532
|
-
- If
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
\`options\` and tell the user, because the closest listed level would turn thinking on.
|
|
536
|
-
For a different model family, call \`${T3_CAPABILITIES_TOOL}\` and use a provider and model it lists.
|
|
554
|
+
- If T3 rejects the thinking level for the picked model, lower it with a \`:<level>\` suffix to a
|
|
555
|
+
level that \`${T3_CAPABILITIES_TOOL}\` lists for that model, and tell the user. Do not switch to
|
|
556
|
+
another model.
|
|
537
557
|
- Background: \`mode: "async"\`. A finished child wakes this thread, so end the turn instead of
|
|
538
558
|
polling. Call \`${T3_STATUS_TOOL}\` only when you need a result mid-turn.
|
|
539
559
|
- Call \`${T3_CAPABILITIES_TOOL}\` or \`${T3_STATUS_TOOL}\` only when the \`check\` report allows it.
|
|
@@ -556,12 +576,12 @@ function subagentSection(namespace: string): string {
|
|
|
556
576
|
|
|
557
577
|
- Agent names are namespaced, for example \`${namespace}-poteto-agent\`. Call
|
|
558
578
|
\`${SUBAGENT_TOOL}({ action: "list" })\` to see them.
|
|
559
|
-
- Model: pass
|
|
560
|
-
|
|
561
|
-
\`provider/model:<level
|
|
562
|
-
pi-subagents applies its own default.
|
|
563
|
-
\`
|
|
564
|
-
|
|
579
|
+
- Model: pass \`model: "pstack:<role>"\` for each child, including each entry of \`tasks\`, each
|
|
580
|
+
\`chain\` step, and each \`parallel\` entry of a step. The adapter replaces it with
|
|
581
|
+
\`provider/model:<level>\`, or \`provider/model\` when the role's thinking reads "backend
|
|
582
|
+
default", so pi-subagents applies its own default.
|
|
583
|
+
An unconfigured role uses the \`general\` role. When \`general\` is unconfigured too, the adapter
|
|
584
|
+
passes your own model and thinking, or omits \`model\` when Pi does not report the model.
|
|
565
585
|
- ${NESTED_DELEGATION_RULE}
|
|
566
586
|
- Background: \`async: true\`.`;
|
|
567
587
|
}
|
|
@@ -602,15 +622,16 @@ write no \`Skipped in Pi\` line for it.
|
|
|
602
622
|
| \`Grep\`, \`Glob\` | \`rg\` through \`bash\`, or Pi's \`grep\` and \`find\` tools when the session has them. Without \`rg\`, use \`grep -rn\` and \`find\`. |
|
|
603
623
|
| \`AskUserQuestion\`, \`AskQuestion\` | the \`ask_user\` tool. |
|
|
604
624
|
| \`TodoWrite\`, \`TaskCreate\`, \`TaskUpdate\`, Open a todolist | no Pi equivalent. Keep an uncommitted \`todo.md\` checklist instead. |
|
|
605
|
-
| \`claude-opus-5-5-xhigh\`, \`
|
|
606
|
-
| "pick each subagent's reasoning level and fast or standard speed", a role's starting level from the \`/setup-pstack\` budget | the thinking column of the \`model roles\` table is a role's ceiling. When it names a level, you may lower it along \`max\`, \`xhigh\`, \`high\`, \`medium\`, \`low\`, \`minimal\`, \`off\` as the body asks,
|
|
607
|
-
| a role line valued \`auto\` or \`inherit-parent\` | an unconfigured role.
|
|
625
|
+
| \`claude-opus-5-5-xhigh\`, \`claude-fable-5-2-max\`, \`gemini-4-carbon-high\`, \`grok-*\`, \`gpt-*\`, other Cursor model slugs | the role models configured by \`/skill:${namespace}-setup\`. Pass the role as the model, \`pstack:<role>\`, as [Delegation](#delegation) says, and the adapter picks the role's model and thinking. Call the \`pstack_adapter\` tool with \`action: "status"\` to read the current role table. An effort word in a slug, such as \`-xhigh\` or \`-max\`, is not a model id. The role table's thinking column carries that level, and \`-fast\` has no equivalent. An unconfigured role uses the \`general\` role. When \`general\` is unconfigured too, the adapter lets the child inherit the parent model and thinking. |
|
|
626
|
+
| "pick each subagent's reasoning level and fast or standard speed", a role's starting level from the \`/setup-pstack\` budget | the thinking column of the \`model roles\` table is a role's ceiling. When it names a level, you may lower it along \`max\`, \`xhigh\`, \`high\`, \`medium\`, \`low\`, \`minimal\`, \`off\` as the body asks, by ending the role reference with \`:<level>\`, as in \`pstack:feature:low\`. Never raise a role above its configured level, even where the body says to raise it for the hardest work. Send that work to \`strongest-judgment\` at its own level instead. A role whose thinking reads "backend default" keeps that setting, because it has no level to move. An unconfigured role takes the \`general\` role's level as its ceiling, and keeps the parent's setting only when \`general\` is unconfigured too. Fast or standard speed has no Pi equivalent, so leave it out. If the backend rejects the moved level, pass \`pstack:<role>\` with no suffix to use the role's configured level, and say so. |
|
|
627
|
+
| a role line valued \`auto\` or \`inherit-parent\` | an unconfigured role. Pass \`pstack:<role>\` as for any role. The adapter uses the \`general\` role, and lets the child inherit the parent model only when \`general\` is unconfigured too. |
|
|
608
628
|
| \`~/.cursor/rules/pstack-models.mdc\` | adapter configuration written by \`/skill:${namespace}-setup\`. |
|
|
629
|
+
| the rule's \`# defaults: 2\` line, a rule without it, telling the user to rerun \`/setup-pstack\` | does not apply. The adapter configuration has no \`# defaults\` line, and an unset role uses the \`general\` role rather than an upstream default slug, so no older setup left stale defaults in it. Do not tell the user to rerun setup for this. |
|
|
609
630
|
| other \`.cursor/rules/*.mdc\` files, an always-applied rule | Pi reads no rule files. It loads \`AGENTS.md\` or \`CLAUDE.md\` from \`~/.pi/agent/\`, the working directory, and its parent directories into every session, so durable instructions go there. Model roles are the exception, as the row above says. |
|
|
610
631
|
| role lines \`feature, refactoring\`, \`bug-fix\`, \`perf-issue\`, \`hillclimb\`, \`judgment and prose\`, \`hardest tasks\` | adapter roles \`feature\` or \`refactoring\`, \`bug-fix\`, \`perf-issue\`, \`hillclimb\`, \`judgment\`, and \`strongest-judgment\`. |
|
|
611
|
-
| role
|
|
612
|
-
| role lines \`
|
|
613
|
-
| panel role lines \`arena runners\`, \`arena cross-judge pool\`, \`architect runners\`, \`interrogate reviewers\` | adapter panel roles \`arena-runners\`, \`arena-cross-judge-pool\`, \`architect-runners\`, and \`interrogate-reviewers\`. The \`panel roles\` table of the \`pstack_adapter\` status report lists each panel's seats in order, one per entry, and a seat's thinking is its ceiling, passed by the backend rules in [Delegation](#delegation). A \`model of the agent that starts the panel\` seat comes from an \`inherit\` entry: start it with no \`target
|
|
632
|
+
| role line \`reflect judgment, divergent, synthesizer\` | adapter role \`judgment\`. |
|
|
633
|
+
| role lines \`reflect tooling\`, \`swarm workers\`, and verification | adapter role \`general\`. |
|
|
634
|
+
| panel role lines \`arena runners\`, \`arena cross-judge pool\`, \`architect runners\`, \`interrogate reviewers\` | adapter panel roles \`arena-runners\`, \`arena-cross-judge-pool\`, \`architect-runners\`, and \`interrogate-reviewers\`. The \`panel roles\` table of the \`pstack_adapter\` status report lists each panel's seats in order, one per entry, and a seat's thinking is its ceiling, passed by the backend rules in [Delegation](#delegation). A \`model of the agent that starts the panel\` seat comes from an \`inherit\` entry: on T3, start it with no \`target\`; on pi-subagents, pass your own model as \`model\`, as [Delegation](#delegation) says. It then runs on your own model even when \`general\` is configured. The \`auto\` and \`inherit-parent\` row does not apply to it. An unset panel uses \`judgment\`, then \`general\`, and those are also the defaults where the body names its own default list or default pool. Families and vendors go by the model id after the provider prefix, such as \`claude-fable-*\`, \`claude-*\`, \`gemini-*\`, \`gpt-*\`, or \`grok-*\`, not by the provider, because one provider can serve several families. Apply the body's own family and vendor rules to those ids. |
|
|
614
635
|
| a panel whose seats all run on one model, or a claim that a model or model family is unavailable | A panel the status report warns about runs on one model because of the adapter configuration. Say that in your reply, name the panel, and name \`/skill:${namespace}-setup\` as the fix. Do not attribute it to which models exist. A \`model of the agent that starts the panel\` seat is your own model, so a panel whose other seats are all on your model is one-model too. Never claim a model or family is unavailable. A model listing is not proof of absence: \`${T3_CAPABILITIES_TOOL}\` filters by \`search\` and omits models beyond its limit, and the \`pstack_adapter\` tool's \`action: "models"\` shows only the session's scoped models when the user scoped some. Say which models the configuration uses instead. |
|
|
615
636
|
| a delegate no role line names, such as an ad-hoc helper | adapter role \`general\`. |
|
|
616
637
|
| a Custom Mode, "Use as Mode", or Option+Enter / Alt+Enter on \`/poteto-mode\` | \`/${namespace}-mode on\`, which keeps Poteto Mode active for the Pi session until \`/${namespace}-mode off\`. |
|
|
@@ -626,11 +647,11 @@ write no \`Skipped in Pi\` line for it.
|
|
|
626
647
|
| \`environment: "cloud"\`, \`cloud_base_branch\` | not available. Run the fan-out locally instead. |
|
|
627
648
|
| the agent's store (path in the system prompt), the Project store, the Project tracker (\`notes.md\`) | no Pi equivalent; Pi's system prompt names no store. Keep those files in a folder Git ignores, such as \`.work/<project>/\` after adding \`.work/\` to the exclude file that \`git rev-parse --git-path info/exclude\` prints (in a linked worktree \`.git\` is a file, so never open \`.git/info/exclude\` directly), or outside the repository. Give its absolute path to each child that reads or writes them. |
|
|
628
649
|
| Cursor \`/loop\`, \`/loop <interval>\` | not available in Pi, which has no timer that wakes an agent. When \`/loop\` drives an autonomous run to a predicate, keep working in this session: re-check the predicate after each iteration, and stop only when it passes or at a real dead end. When it arms a timed tick, such as \`/loop 1h\` for an audit, **skip** arming it, say that no tick is armed, and give the user the tick prompt to run again. Do not start a sleep or polling loop in its place. |
|
|
629
|
-
| PR and CI event subscriptions | not
|
|
650
|
+
| PR and CI event subscriptions | Pi standalone has no PR or CI event subscriptions. If the host exposes \`mcp__t3-code__watch_pull_request\`, use it according to its tool instructions: end the turn after subscribing and wait for host events instead of polling. Do not assume every host or delegation backend provides it. Without that tool, check the PR's live state with \`gh pr view\` and \`gh pr checks\` when you report on it, and do not start a polling loop in their place. A wake is not a merge decision; check readiness yourself and respect the user's merge instructions. |
|
|
630
651
|
| forking a worker to reuse its built state, such as its checkout, uncommitted changes, or a running process | no Pi equivalent; no delegation backend copies a checkout or a running process into a new child. Wait until the earlier worker has finished or stopped, so one writer at a time uses that worktree. Then start a new child, as the \`resume\` row says, and give it the earlier worker's brief and result, the absolute path of the worktree, and every fact it needs. If the earlier worker left a process running, tell the child to reuse it when it can reach it, or to stop it before starting another, so two copies never compete for one port or file. Stop only a process confirmed to have been started by the earlier worker for this task. Never stop an unidentified process that holds the port or file; when ownership is unclear, the child reports it to the parent instead. |
|
|
631
652
|
| Bugbot | not available in Pi. Bugbot comments on a PR are ordinary review threads, so triage them with the forge command the step gives, such as \`gh\`. When the PR has no Bugbot check or comment, **skip** a step that starts, waits for, or configures Bugbot. |
|
|
632
653
|
| Cursor cloud agents, a cloud-agent URL | not available. Work that would go to a cloud agent runs locally, as the \`environment: "cloud"\` row says. **Skip** a step that needs the cloud agent itself, such as its status in the Cursor dashboard or its own machine. |
|
|
633
|
-
| a built-in PR tool | Pi has none.
|
|
654
|
+
| a built-in PR tool | Pi standalone has none. A host may expose PR tools; use them only when available and follow their instructions. Otherwise use the forge command the step gives, such as \`gh pr create\`. This is not a skip, so write no skip line. |
|
|
634
655
|
|
|
635
656
|
A body instruction that conflicts with Pi policy or with the user's explicit instruction loses.
|
|
636
657
|
|
package/src/pools.ts
CHANGED
|
@@ -33,8 +33,8 @@ export function parentProblem(context: ResolutionContext): string | undefined {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export function resolveRole(config: AdapterConfig, role: string, context: ResolutionContext): RoleResolution {
|
|
36
|
-
const own = config.models[role];
|
|
37
|
-
const from = own !== undefined ? role : config.models
|
|
36
|
+
const own = Object.hasOwn(config.models, role) ? config.models[role] : undefined;
|
|
37
|
+
const from = own !== undefined ? role : Object.hasOwn(config.models, GENERAL_ROLE) ? GENERAL_ROLE : undefined;
|
|
38
38
|
if (from === undefined) {
|
|
39
39
|
const problem = parentProblem(context);
|
|
40
40
|
return problem ? { kind: "blocked", from: role, reason: `it inherits, and ${problem}`, choices: [] } : { kind: "inherit", from: role };
|