pi-daddy 0.13.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.
Files changed (105) hide show
  1. package/CHANGELOG.md +187 -0
  2. package/LICENSE +21 -0
  3. package/README.md +584 -0
  4. package/dist/approval-prompt.d.ts +116 -0
  5. package/dist/approval-prompt.d.ts.map +1 -0
  6. package/dist/approval-prompt.js +124 -0
  7. package/dist/approval-prompt.js.map +1 -0
  8. package/dist/approval-store.d.ts +149 -0
  9. package/dist/approval-store.d.ts.map +1 -0
  10. package/dist/approval-store.js +311 -0
  11. package/dist/approval-store.js.map +1 -0
  12. package/dist/approval.d.ts +221 -0
  13. package/dist/approval.d.ts.map +1 -0
  14. package/dist/approval.js +244 -0
  15. package/dist/approval.js.map +1 -0
  16. package/dist/capabilities.d.ts +31 -0
  17. package/dist/capabilities.d.ts.map +1 -0
  18. package/dist/capabilities.js +45 -0
  19. package/dist/capabilities.js.map +1 -0
  20. package/dist/catalog.d.ts +81 -0
  21. package/dist/catalog.d.ts.map +1 -0
  22. package/dist/catalog.js +164 -0
  23. package/dist/catalog.js.map +1 -0
  24. package/dist/definitions.d.ts +101 -0
  25. package/dist/definitions.d.ts.map +1 -0
  26. package/dist/definitions.js +182 -0
  27. package/dist/definitions.js.map +1 -0
  28. package/dist/delegate.d.ts +133 -0
  29. package/dist/delegate.d.ts.map +1 -0
  30. package/dist/delegate.js +261 -0
  31. package/dist/delegate.js.map +1 -0
  32. package/dist/fanout.d.ts +62 -0
  33. package/dist/fanout.d.ts.map +1 -0
  34. package/dist/fanout.js +84 -0
  35. package/dist/fanout.js.map +1 -0
  36. package/dist/file-lock.d.ts +54 -0
  37. package/dist/file-lock.d.ts.map +1 -0
  38. package/dist/file-lock.js +142 -0
  39. package/dist/file-lock.js.map +1 -0
  40. package/dist/index.d.ts +7 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +7 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/ledger-report.d.ts +106 -0
  45. package/dist/ledger-report.d.ts.map +1 -0
  46. package/dist/ledger-report.js +157 -0
  47. package/dist/ledger-report.js.map +1 -0
  48. package/dist/ledger.d.ts +139 -0
  49. package/dist/ledger.d.ts.map +1 -0
  50. package/dist/ledger.js +94 -0
  51. package/dist/ledger.js.map +1 -0
  52. package/dist/pane-reaper.d.ts +49 -0
  53. package/dist/pane-reaper.d.ts.map +1 -0
  54. package/dist/pane-reaper.js +120 -0
  55. package/dist/pane-reaper.js.map +1 -0
  56. package/dist/pi-tools.d.ts +20 -0
  57. package/dist/pi-tools.d.ts.map +1 -0
  58. package/dist/pi-tools.js +21 -0
  59. package/dist/pi-tools.js.map +1 -0
  60. package/dist/propagation.d.ts +185 -0
  61. package/dist/propagation.d.ts.map +1 -0
  62. package/dist/propagation.js +255 -0
  63. package/dist/propagation.js.map +1 -0
  64. package/dist/resolve.d.ts +110 -0
  65. package/dist/resolve.d.ts.map +1 -0
  66. package/dist/resolve.js +181 -0
  67. package/dist/resolve.js.map +1 -0
  68. package/dist/run-child.d.ts +50 -0
  69. package/dist/run-child.d.ts.map +1 -0
  70. package/dist/run-child.js +109 -0
  71. package/dist/run-child.js.map +1 -0
  72. package/dist/run-herdr.d.ts +91 -0
  73. package/dist/run-herdr.d.ts.map +1 -0
  74. package/dist/run-herdr.js +306 -0
  75. package/dist/run-herdr.js.map +1 -0
  76. package/dist/spawn.d.ts +60 -0
  77. package/dist/spawn.d.ts.map +1 -0
  78. package/dist/spawn.js +116 -0
  79. package/dist/spawn.js.map +1 -0
  80. package/extensions/approvals.ts +285 -0
  81. package/extensions/delegation.ts +237 -0
  82. package/extensions/grants-command.ts +310 -0
  83. package/extensions/grants.ts +346 -0
  84. package/extensions/run-delegation.ts +270 -0
  85. package/extensions/session.ts +234 -0
  86. package/package.json +122 -0
  87. package/src/approval-prompt.ts +222 -0
  88. package/src/approval-store.ts +383 -0
  89. package/src/approval.ts +364 -0
  90. package/src/capabilities.ts +51 -0
  91. package/src/catalog.ts +186 -0
  92. package/src/definitions.ts +233 -0
  93. package/src/delegate.ts +383 -0
  94. package/src/fanout.ts +96 -0
  95. package/src/file-lock.ts +142 -0
  96. package/src/index.ts +59 -0
  97. package/src/ledger-report.ts +227 -0
  98. package/src/ledger.ts +214 -0
  99. package/src/pane-reaper.ts +132 -0
  100. package/src/pi-tools.ts +25 -0
  101. package/src/propagation.ts +308 -0
  102. package/src/resolve.ts +235 -0
  103. package/src/run-child.ts +149 -0
  104. package/src/run-herdr.ts +357 -0
  105. package/src/spawn.ts +156 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,187 @@
1
+ # Changelog — pi-daddy
2
+
3
+ Newest first. **Breaking changes are marked and say what to do about them.**
4
+
5
+ This file exists because the README had grown ninety lines of stacked version banners before a reader
6
+ reached what the package *does* — churn documentation in front of product documentation. The banners are
7
+ the record of how the package got here and are worth keeping; they are not worth reading first.
8
+
9
+ > **0.13.0 is the first PUBLISHED release.** Every version below it was developed in this repository and
10
+ > never shipped to npm, so if you are reading this as a new user, **none of the breaking changes described
11
+ > below can have affected you** — there was no earlier version to install. They are kept because they are
12
+ > the record of how the package arrived at what it does, and because the reasoning behind each one is
13
+ > usually the clearest statement of why the current behaviour is what it is.
14
+
15
+ ## 0.13.0 — the approvals file gets the lock the ledger already had, and two silences end
16
+
17
+ Closing the last items that were open rather than out of scope, then **red-teaming the result**: an
18
+ operator review and four independent agents, each given one hypothesis to attack. Between them they found
19
+ eight further defects **in that work**, all repaired here before release. The two worth knowing about
20
+ as a user of this package:
21
+
22
+ - **The file lock admitted two writers into the critical section.** `rm(lockPath)` deletes whatever is at
23
+ the path, not the lock this process created, so a broken stale lock cascaded: the old holder freed the new
24
+ owner's lock on the way out, and the next arrival — which raced nothing — walked in beside it. Reproduced
25
+ across real OS processes. Every lock now carries a token and proves ownership before deleting.
26
+ - **The ledger over-claimed human approval under concurrency.** One *Allow for this session* answered under
27
+ a fan-out of eight wrote eight lines each recording `approvalSource: "prompt"`. Riders now record
28
+ `session`, which is what actually happened.
29
+
30
+ One breaking change, and it is a type.
31
+
32
+ - **BREAKING: `revokeApproval` returns `"revoked" | "absent" | "failed" | "busy"`**, not a boolean. It had
33
+ two outcomes for four facts, and `/grants revoke` printed *"no persisted approval named X"* whenever the
34
+ write failed — telling an operator that the approval they are revoking does not exist **while it is still
35
+ in effect**. The most alarming outcome wore the most reassuring message. `busy` is separate because a lock
36
+ timeout happens *before* the load, so nothing was looked at and nothing may be claimed about the entry.
37
+ Callers switching on the boolean must switch on the string; `"revoked"` is the only success.
38
+ - **Approval writes hold a lock (R-49).** Every write is load → modify → write and none of them was
39
+ serialised, so session 1 could load, session 2 could revoke, and session 1's next save would restore the
40
+ revoked entry for the rest of its 30 days. The lock is the ledger's, moved to `src/file-lock.ts` and used
41
+ by both — one implementation, two callers, opposite failure policies: the ledger fails a delegation closed
42
+ when it cannot take the lock, the approvals store never fails your work, because it is a convenience cache.
43
+ - **`/grants ledger` counts where each approval came from.** ADR-0020 keeps the persistence layer on an
44
+ asserted fatigue argument and named the evidence that would settle it — `persisted` against `prompt` over
45
+ real use. The data was already recorded and nothing read it. **Two numbers, both labelled**: raw records
46
+ are an *upper bound* on prompts avoided, because within one session only the first would have been a
47
+ prompt and the rest come from the in-memory session cache; distinct `capability@subject` pairs are the
48
+ closer estimate. Reporting records alone overstated the layer twentyfold in the obvious case (R-63).
49
+ Records written before per-capability sources existed are reported as *not counted* rather than folded in,
50
+ because that older scalar over-claimed `prompt`.
51
+ - **A revoke is documented as taking effect at the next gate check**, not "immediately". A spawn whose gate
52
+ check already passed is not retracted by a revoke arriving microseconds later — inherent to revoking
53
+ anything, and no lock closes it.
54
+ - **An unreadable ledger no longer silences session start (R-60).** `verifyLedger` rethrows on anything but
55
+ a missing file, and that call sat inside an empty catch — so a `PI_GRANTS_LEDGER` pointing at a directory
56
+ produced no alarm, no warning, and not even the line saying governance was on.
57
+ - **Herdr panes opened by a killed process are closed at exit.** Coverage is exact and documented in
58
+ `src/pane-reaper.ts`: normal exit and `process.exit()` are covered; SIGKILL is not, and neither is a
59
+ SIGTERM nothing else in the process is listening for. **No signal handler is installed**, deliberately —
60
+ one here would suppress Node's default termination and turn pi's "interrupt this turn" into "exit pi".
61
+
62
+ ## 0.11.0 is a breaking change: per-project approvals, and an inherited yes names its instructions
63
+
64
+ Four decisions taken together after the first independent review of this package's approval layer
65
+ (ADR-0020…ADR-0023). Two of them break compatibility.
66
+
67
+ - **Approvals are stored one file per project**, under `$PI_CODING_AGENT_DIR/grants-approvals/`. The
68
+ single shared file could not express two checkouts holding an approval for a same-named definition —
69
+ `review`, `deploy`, i.e. what happens the moment you reuse your own conventions — and every write
70
+ touched every project's data, which is where four defects came from. **The old file is ignored, not
71
+ migrated**, and reported once; re-approve when next asked.
72
+ - **`PI_GRANTS_APPROVED` carries a body digest**: `capability@subject#sha256`. A child verifies it against
73
+ the definition **it** loaded, because a child is a fresh process that re-reads from disk — so a
74
+ `git pull` mid-tree could otherwise let a descendant run rewritten instructions under a yes given about
75
+ the old ones. A 0.10 parent and a 0.11 child do not understand each other's format.
76
+ - **The task is never stored.** `taskAtApproval` is gone: it put model-authored text in an always-on file
77
+ outside your repository for 30 days, which this package's own rule forbids — and it displayed as though
78
+ it scoped the approval, which it never did.
79
+ - **`agent:*` exists.** "May spawn any of our definitions, but never hand over `write`" was previously
80
+ unexpressible; the only wildcard was `tool:*`, which is authority to grant every tool. `agent:*` confers
81
+ no tool authority. Do not pair it with `tool:bash`.
82
+
83
+ ## 0.9.0 / 0.10.0: an approval is pinned to the *instructions* as well as the tools
84
+
85
+ **ADR-0018 and ADR-0019.** Two changes, and the second was a repair.
86
+
87
+ **Every spawn naming a definition now records a `definitionDigest`** — `{name, source, sha256}` over the
88
+ body, which is the exact text passed to the child as `--append-system-prompt`. So the ledger answers *"did
89
+ these four children run the same instructions?"* and *"has this definition changed since?"*. It does not
90
+ answer *what the instructions said*: the digest identifies text without reproducing it. **The task string
91
+ is never recorded, in any field, by decision** — it is assembled by the model from the parent's context and
92
+ could carry anything the parent could see.
93
+
94
+ **`always` approvals were unreachable and are not any more.** `offeredScopes` gated that scope on the
95
+ interceptor path, which 0.7.0 deleted — so **no version from 0.7.0 to 0.9.0 could create a persisted
96
+ approval at all**, and the whole store was guarding a file nothing could write. `delegate({agent: X})` now
97
+ approves against **`X`** and offers `always`; `delegate({tools: […]})` keeps the fixed `<delegate>` subject
98
+ and is still never offered it. A persisted entry pins the definition's `allowed-tools` **and** its body
99
+ digest, so rewriting what a child is told to do voids the approval (`instructions-changed`) — strictly
100
+ stronger than ADR-0010 designed, since the ceiling check alone could never have seen a body change. An
101
+ entry carrying no body pin fails closed: unverifiable is not unchanged.
102
+
103
+ ## 0.8.0 is a breaking change: spawning a definition requires `agent:<name>`
104
+
105
+ **ADR-0017.** The catalog has always emitted `agent:<name>` for every definition and the parser has always
106
+ accepted it, but **nothing ever checked it** (R-35). The only gate on `delegate({agent: "deploy"})` was
107
+ whether that definition's `allowed-tools` fitted inside the session's grant — so governance covered what a
108
+ child *can do* and never *which operator-authored instructions it was given*, and an operator could not
109
+ say "this session may spawn `review` but not `deploy`".
110
+
111
+ **What you must change:** an enumerated `PI_GRANTS_GRANT` now needs an `agent:` id per definition it may
112
+ spawn. `PI_GRANTS_GRANT="tool:read,tool:delegate"` can spawn nothing by name; add `agent:review` to allow
113
+ that one. `tool:*` satisfies any of them, so an **ungoverned session is unaffected** and `delegate({tools:
114
+ […]})` is untouched. The refusal names the missing capability and lists what the session *may* spawn.
115
+
116
+ It attenuates like every other capability: a definition's own `allowed-tools` may list `agent:other`,
117
+ which is how a delegator is told which definitions **it** may spawn — and a parent can never hand down one
118
+ it does not hold. The id authorises; it is never passed to `--tools`.
119
+
120
+ **Also fixed, and required for the above (R-36):** `deriveOwnGrant` filtered the inherited grant against
121
+ the session's *observed tools*, which silently dropped `skill:` and `agent:` capabilities at the first
122
+ provider request. A child holding `skill:review` therefore could not re-grant it, and `/grants` stopped
123
+ listing it. Only `tool:` and `ext:` are filtered now — an observation says nothing about a namespace that
124
+ is not tools.
125
+
126
+ ## 0.7.0 is a breaking change: this package is now the spawner, not a fence
127
+
128
+ **ADR-0016.** Earlier versions were a governance layer wrapped around `@tintinweb/pi-subagents`: the
129
+ product was a `tool_call` interceptor that decided whether *someone else's* spawn was permissible. It
130
+ could refuse or allow, never narrow, because that package's `Agent` tool has no `tools` parameter — a
131
+ ceiling no amount of local work could lift.
132
+
133
+ This version spawns children itself, so **the grant is an argument rather than a veto**. What changed:
134
+
135
+ - **`delegate({agent, task})` spawns a definition by name.** Definitions are **Agent Skills
136
+ (`SKILL.md`)** files — the open standard, already read by 16+ tools — and their `allowed-tools` field
137
+ becomes the grant. The spec calls that field *"pre-approved"* and **experimental**; it declares intent
138
+ and blocks nothing. Passed through `--tools` it becomes structural. **The standard declares intent;
139
+ this package makes it enforced.**
140
+ - **`delegate_all` runs several children concurrently**, each with its own grant, its own instructions,
141
+ and no knowledge of the others.
142
+ - **Two executors, one plan**: a captured child process (default) or a visible, attachable **herdr** pane
143
+ (`PI_GRANTS_HERDR=1`).
144
+ - **Skills and context files are no longer inherited.** Previously a child spawned with `--tools read`
145
+ still loaded every skill the operator had, plus `CLAUDE.md` — measured, `docs/probes/g16-herdr`. So the
146
+ `skill:` capability namespace enforced nothing. It does now.
147
+ - **A cardinality bound.** A subtree *budget* caps how many descendants may exist at all, because the old
148
+ `delegate` bounded that to one only by accident of being blocking.
149
+ - **Removed:** the pi-subagents ceiling port (`agent-types.ts`, `interceptor.ts`). The `tool_call` hook
150
+ remains as a **tripwire** that refuses third-party spawn tools — installing one is a single command,
151
+ and a silently ungoverned descendant is the thing this package exists to prevent.
152
+
153
+ Built because that guarantee does not exist elsewhere. `@tintinweb/pi-subagents` provisions statically per
154
+ agent type and cannot be narrowed per spawn; `pi-fabric` provisions dynamically but **cannot constrain a
155
+ recursive child at all** (measured: `docs/probes/pi-fabric-eval`).
156
+
157
+ ## Earlier
158
+
159
+ ### 0.6.0
160
+
161
+ - **The approvals store moved out of the workspace** and a legacy in-workspace file is ignored with a
162
+ warning (see *Approving a gated capability*).
163
+ - **`PI_GRANTS_APPROVED` carries `capability@subject` pairs** and `once` no longer crosses a boundary
164
+ (**ADR-0014**). A 0.5.x parent and a 0.6.x child do not understand each other's format.
165
+ - **`bash` is gated by default** in a governed session, and gating is closed under subsumption, so
166
+ `PI_GRANTS_GATED=tool:write` also gates `bash` (**ADR-0012**). Set `PI_GRANTS_GATED=""` for the old
167
+ behaviour.
168
+ - **`delegate` is registered only when the session may delegate**, which is what the docs always claimed.
169
+ - Library entry points are compiled to `dist/`, so consumer imports work at all.
170
+
171
+ ### 0.5.0 — hardening from two independent reviews
172
+
173
+ - **G1 · the argv channel.** Closed; see *Propagation is race-free by construction*.
174
+ - **G6 · the ledger.** It reported allowed wildcard spawns as escalation attempts, and dropped every
175
+ refusal decided before resolution. Both fixed at the type level, so a new early exit cannot reintroduce
176
+ them.
177
+ - **G7 · configuration.** Malformed bounds fail closed and say so; ungoverned sessions publish nothing; the
178
+ catalog is awaited rather than raced.
179
+ - **G8 · child processes.** Caps, timeout, abort-before-spawn, real errors for failed children.
180
+ - **ADR-0011 · universal capabilities** are refused on every path, and a wildcard-holding delegator no
181
+ longer bypasses a configured gate. Spawns that succeeded in 0.4.0 therefore fail — and **that reliance was
182
+ never sound**, since each of them handed a child either the entire catalog or a capability an operator had
183
+ explicitly gated. There is deliberately no override flag; the ledger names the capability and the reason.
184
+
185
+ ---
186
+
187
+ Requires pi ≥ 0.83.0, Node ≥ 22.19. MIT.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mojomanyana
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.