pi-fabric 0.44.13 → 0.44.15

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 (48) hide show
  1. package/dist/actors/global-registry.d.ts.map +1 -1
  2. package/dist/actors/global-registry.js +2 -7
  3. package/dist/actors/global-registry.js.map +1 -1
  4. package/dist/actors/manager.d.ts.map +1 -1
  5. package/dist/actors/manager.js +2 -7
  6. package/dist/actors/manager.js.map +1 -1
  7. package/dist/agents/manager.d.ts.map +1 -1
  8. package/dist/agents/manager.js +2 -7
  9. package/dist/agents/manager.js.map +1 -1
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +2 -1
  12. package/dist/config.js.map +1 -1
  13. package/dist/core/atomic-write.d.ts +14 -0
  14. package/dist/core/atomic-write.d.ts.map +1 -0
  15. package/dist/core/atomic-write.js +62 -0
  16. package/dist/core/atomic-write.js.map +1 -0
  17. package/dist/core/capability-advisory.d.ts +1 -1
  18. package/dist/core/capability-advisory.d.ts.map +1 -1
  19. package/dist/core/capability-advisory.js +55 -19
  20. package/dist/core/capability-advisory.js.map +1 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +25 -16
  23. package/dist/index.js.map +1 -1
  24. package/dist/mesh/store.d.ts.map +1 -1
  25. package/dist/mesh/store.js +2 -7
  26. package/dist/mesh/store.js.map +1 -1
  27. package/dist/residency/client.d.ts.map +1 -1
  28. package/dist/residency/client.js +2 -7
  29. package/dist/residency/client.js.map +1 -1
  30. package/dist/residency/host.d.ts.map +1 -1
  31. package/dist/residency/host.js +2 -7
  32. package/dist/residency/host.js.map +1 -1
  33. package/dist/schema/controller.d.ts.map +1 -1
  34. package/dist/schema/controller.js +2 -4
  35. package/dist/schema/controller.js.map +1 -1
  36. package/dist/storage/retention.d.ts.map +1 -1
  37. package/dist/storage/retention.js +2 -13
  38. package/dist/storage/retention.js.map +1 -1
  39. package/dist/worker/run-record.d.ts.map +1 -1
  40. package/dist/worker/run-record.js +37 -1
  41. package/dist/worker/run-record.js.map +1 -1
  42. package/docs/capability-combustion.md +36 -23
  43. package/docs/configuration.md +1 -1
  44. package/package.json +1 -1
  45. package/dist/core/capability-advisory-store.d.ts +0 -4
  46. package/dist/core/capability-advisory-store.d.ts.map +0 -1
  47. package/dist/core/capability-advisory-store.js +0 -46
  48. package/dist/core/capability-advisory-store.js.map +0 -1
@@ -121,29 +121,42 @@ With the default fixture terms, "query the results table please" scores
121
121
  $s = 1.333$ (weak band): the EWMA crosses $0.9$ only on the second tidy prompt,
122
122
  and cool-off while the user edits code delays the fire as intended.
123
123
 
124
- ## Ash: permanent suppression with provenance
125
-
126
- Fired namespaces are burned. The record (ash) is append-only and machine-global,
127
- persisted at `<agent-dir>/fabric/capability-advisories.json`:
124
+ ## Ash: session-scoped suppression — the transcript is the ledger
125
+
126
+ Fired namespaces are burned, and the burn record lives in the session's own
127
+ transcript — there is no side store:
128
+
129
+ - `fired` — the hint itself is the record: every advisory is emitted as a
130
+ `pi-fabric-capability` custom-message entry whose `details` name the
131
+ namespaces shown. Burning happened *when the paper entered the fire*, and
132
+ the transcript is the fire.
133
+ - `organic` — the model invoked a captured tool from that namespace *without
134
+ a hint*. The tool call is already a transcript entry; the capability's
135
+ information potential was spent by discovery, so we burn it preemptively
136
+ (organic-discovery poisoning: the same one-way door, entered from the other
137
+ side).
138
+
139
+ Ash is *derived*, never stored: on `session_start` and on every branch switch
140
+ (`session_tree`) the advisor replays the current branch's entries
141
+ (`ctx.sessionManager.getBranch()`) and re-marks both burn kinds, with
142
+ provenance (`origin`) and wall time (`at`, taken from the entry's own
143
+ timestamp) rebuilt from the transcript:
128
144
 
129
145
  ```json
130
- { "format": 2, "burned": [
131
- { "namespace": "extension:pi-websearch", "origin": "fired", "at": "2026-08-10T16:00:00.000Z" },
132
- { "namespace": "extension:pi-fovea", "origin": "organic" } ] }
146
+ { "namespace": "extension:pi-websearch", "origin": "fired", "at": "2026-08-10T16:00:00.000Z" }
147
+ { "namespace": "extension:pi-fovea", "origin": "organic", "at": "2026-08-10T16:12:11.000Z" }
133
148
  ```
134
149
 
135
- Provenance is in `origin`:
136
-
137
- - `fired` — a hint was emitted for this capability.
138
- - `organic` — the model invoked a captured tool from that namespace *without a
139
- hint*. The capability's information potential is already spent; a hint would
140
- be redundant, so we burn it preemptively. This is organic-discovery poisoning:
141
- the same one-way door as a fire, entered from the other side.
142
-
143
- You don't unburn paper. Ash is never reclaimed: no expires, no release path, no
144
- defrost knob. `reset()` clears things that are inherently session-local (warmth,
145
- smoke streak, per-session cap) but never ash; `hydrate()` loads it on session
146
- start.
150
+ Thermodynamic consequence: the ash set is exactly "burns that happened up to
151
+ the current point in this branch's history." Fork a session and the fork
152
+ replays ashes up to the fork point; rewind with `/tree` and capabilities whose
153
+ burns live only in the abandoned future burn *back into paper* — you don't
154
+ unburn paper, but undoing time restores what hadn't happened yet. A new
155
+ session is a fresh urn: nothing carries across sessions, because the learner
156
+ the hint was meant for (the model's context) doesn't either. Within a session
157
+ there's still no release path: no expires, no defrost knob — `reset()` clears
158
+ only what is inherently transient (warmth, smoke streak, per-session cap),
159
+ never ash.
147
160
 
148
161
  ## Furnace feedback
149
162
 
@@ -162,15 +175,15 @@ zero — clean combustion keeps the furnace responsive. Smoke feedback only rais
162
175
  the *weak-band* ignition bar; strong matches still ignite instantly, because if
163
176
  the evidence is that unambiguous the furnace's skepticism isn't invited.
164
177
 
165
- Transient feedback lives session-locally too: smoke is lifted by the first clean
166
- burn, and unlike ash it doesn't persist across sessions (organic poisoning
167
- covers the durable learning).
178
+ Transient feedback lives session-locally too: smoke is lifted by the first
179
+ clean burn. Like everything else in this model, it does not cross sessions —
180
+ the new session brings a fresh transcript, hence a fresh urn.
168
181
 
169
182
  ## Per-session cap
170
183
 
171
184
  $\leq$ `maxPerSession` fires per session regardless of model behavior (default
172
185
  3). Unchanged from the pre-combustion design; the cap guards against prompt
173
- storms in a single session, ash guards against repetition across sessions.
186
+ storms in a single session, and ash guards against repetition within it.
174
187
 
175
188
  ## Summary
176
189
 
@@ -234,7 +234,7 @@ Fabric risk classes are `read`, `write`, `execute`, `network`, and `agent`; appr
234
234
  - Captured and directly registered tools default to the conservative `execute` risk because Pi tool definitions do not declare effects. Add exact tool-name overrides under `capture.risks`. Fovea's verified graph-navigation tools (`fovea_sketch`, `fovea_focus`, `fovea_dwell`, and `fovea_impact`) are read-only exceptions and default to `read`.
235
235
  - Set `capture.hideFromModel` to `false` to index non-core extension tools without hiding them from the model's active set.
236
236
  - `capture.keepVisible` names stay in both Fabric and Pi's model-facing active set, except that Pi core names are always Fabric-owned in full code mode.
237
- - `capture.advisory` injects a capability hint at `before_agent_start` when the prompt's terms match a captured source's tf-idf fingerprint (names + descriptions grouped by source namespace, no manifest declarations needed). `mode: "enabled"` (default) renders the hint in the transcript, `"hidden"` delivers it to the model only, and `"disabled"` turns it off. Each capability fires at most once — the fired set persists in `<agent-dir>/fabric/capability-advisories.json` across session restarts; `maxPerSession` (default 3) caps hints within a session, `threshold` (default 0.9) tunes sensitivity, `maxPerSession` (default 3) caps hints within a session, `threshold` (default 0.9) tunes sensitivity, and `budget` (default 512, clamped 128–8192 — same range as [pi-fovea](https://github.com/monotykamary/pi-fovea)'s `sync.budget`) caps the advisory text in tokens (estimated as chars/4; rendering walks a degradation ladder — indented tool tree with descriptions → names-only tree → flat per-source lines → `header + steer` — until a rung fits; the ladder keeps leftover tool names in a `+N more:` leaf so they stay addressable). The matcher follows a combustion model: 1/df-weighted term scoring, strong matches fire instantly, weak matches accumulate warmth ($W \leftarrow \frac{1}{2}W + \frac{1}{2}s$; ignites at $W \geq$ threshold), each namespace's fire-set is durable *ash* (`fired` or `organic` — organic covers tools you used without a hint) persisted in `<agent-dir>/fabric/capability-advisories.json` across restarts, and ignored fires push smoke streaks that raise the weak-band ignition point by $\theta/\tau^2$ per streak ($0.25\theta$ at the internal memory scale $\tau = 2$ — every internal constant projects from $\tau$ and the score quantum $q = 1$). See [docs/capability-combustion.md](docs/capability-combustion.md) for the full math. Skill markup pi expands into the prompt (`<available_skills>`/`<skill>` blocks) is stripped before matching so a loaded skill cannot trigger its own hint. The hint follows fovea's structured shape: headline, `Candidates:` refs grouped by source, a `Next:` schema/action line for the top ref, and a `Steer:` directive.
237
+ - `capture.advisory` injects a capability hint at `before_agent_start` when the prompt's terms match a captured source's tf-idf fingerprint (names + descriptions grouped by source namespace, no manifest declarations needed). `mode: "enabled"` (default) renders the hint in the transcript, `"hidden"` delivers it to the model only, and `"disabled"` turns it off. Each capability fires at most once per session — ash is derived from the session transcript itself (a fired hint is its own custom-message entry; organic use is its own tool call), so reloads and `/tree` branch rewinds replay ashes exactly up to the current point, and a brand-new session starts with a clean urn; `maxPerSession` (default 3) caps hints within a session, `threshold` (default 0.9) tunes sensitivity, `maxPerSession` (default 3) caps hints within a session, `threshold` (default 0.9) tunes sensitivity, and `budget` (default 512, clamped 128–8192 — same range as [pi-fovea](https://github.com/monotykamary/pi-fovea)'s `sync.budget`) caps the advisory text in tokens (estimated as chars/4; rendering walks a degradation ladder — indented tool tree with descriptions → names-only tree → flat per-source lines → `header + steer` — until a rung fits; the ladder keeps leftover tool names in a `+N more:` leaf so they stay addressable). The matcher follows a combustion model: 1/df-weighted term scoring, strong matches fire instantly, weak matches accumulate warmth ($W \leftarrow \frac{1}{2}W + \frac{1}{2}s$; ignites at $W \geq$ threshold), each namespace's fire-set is durable *ash* (`fired` or `organic` — organic covers tools you used without a hint) persisted in `<agent-dir>/fabric/capability-advisories.json` across restarts, and ignored fires push smoke streaks that raise the weak-band ignition point by $\theta/\tau^2$ per streak ($0.25\theta$ at the internal memory scale $\tau = 2$ — every internal constant projects from $\tau$ and the score quantum $q = 1$). See [docs/capability-combustion.md](docs/capability-combustion.md) for the full math. Skill markup pi expands into the prompt (`<available_skills>`/`<skill>` blocks) is stripped before matching so a loaded skill cannot trigger its own hint. The hint follows fovea's structured shape: headline, `Candidates:` refs grouped by source, a `Next:` schema/action line for the top ref, and a `Steer:` directive.
238
238
  - An `ask` policy emits a warning notification and opens an explicit **Allow once** / **Allow for this session** / **Deny** permission prompt, matching Claude-style approval scopes. **Allow once** authorizes only the requested action. **Allow for this session** authorizes that risk class until the current Pi session ends. The TUI uses an inline wizard; RPC clients receive the equivalent `select` dialog.
239
239
  - Concurrent requests are serialized so a one-time approval never silently widens to sibling calls. Session-wide grants are shared between native calls and `fabric_exec`. Escape, dismissal, unavailable interactive UI, and session restart all fail closed.
240
240
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-fabric",
3
- "version": "0.44.13",
3
+ "version": "0.44.15",
4
4
  "description": "A programmable tool and agent runtime for Pi",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,4 +0,0 @@
1
- import type { CapabilityBurn } from "./capability-advisory.js";
2
- export declare const loadCapabilityAdvisoryState: (filePath?: string) => CapabilityBurn[];
3
- export declare const saveCapabilityAdvisoryState: (burned: Iterable<CapabilityBurn>, filePath?: string) => void;
4
- //# sourceMappingURL=capability-advisory-store.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"capability-advisory-store.d.ts","sourceRoot":"","sources":["../../src/core/capability-advisory-store.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAyB/D,eAAO,MAAM,2BAA2B,GAAI,WAAW,MAAM,KAAG,cAAc,EA2B7E,CAAC;AAEF,eAAO,MAAM,2BAA2B,GACtC,QAAQ,QAAQ,CAAC,cAAc,CAAC,EAChC,WAAW,MAAM,KAChB,IAGF,CAAC"}
@@ -1,46 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import { getAgentDir } from "@earendil-works/pi-coding-agent";
5
- const ADVISORY_STATE_FILENAME = "capability-advisories.json";
6
- const defaultStatePath = () =>
7
- // Sits beside the global actor registry: fired capability hints belong to
8
- // the user's machine-global agent dir so once-fired advisories stay quiet
9
- // across session restarts in every project.
10
- path.join(getAgentDir(), "fabric", ADVISORY_STATE_FILENAME);
11
- const atomicWriteJson = (filePath, value) => {
12
- fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
13
- const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
14
- fs.writeFileSync(temporaryPath, JSON.stringify(value, null, 2), {
15
- encoding: "utf8",
16
- mode: 0o600,
17
- });
18
- fs.renameSync(temporaryPath, filePath);
19
- };
20
- export const loadCapabilityAdvisoryState = (filePath) => {
21
- try {
22
- const raw = JSON.parse(fs.readFileSync(filePath ?? defaultStatePath(), "utf8"));
23
- if (raw.format === 2 && Array.isArray(raw.burned)) {
24
- return raw.burned.filter((entry) => typeof entry === "object" &&
25
- entry !== null &&
26
- typeof entry.namespace === "string" &&
27
- (entry.origin === "fired" ||
28
- entry.origin === "organic"));
29
- }
30
- if (raw.format === 1 && Array.isArray(raw.fired)) {
31
- return raw.fired
32
- .filter((entry) => typeof entry === "string")
33
- .map((namespace) => ({ namespace, origin: "fired" }));
34
- }
35
- return [];
36
- }
37
- catch {
38
- // Missing or corrupt state means nothing has burned yet.
39
- return [];
40
- }
41
- };
42
- export const saveCapabilityAdvisoryState = (burned, filePath) => {
43
- const document = { format: 2, burned: [...burned] };
44
- atomicWriteJson(filePath ?? defaultStatePath(), document);
45
- };
46
- //# sourceMappingURL=capability-advisory-store.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"capability-advisory-store.js","sourceRoot":"","sources":["../../src/core/capability-advisory-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9D,MAAM,uBAAuB,GAAG,4BAA4B,CAAC;AAW7D,MAAM,gBAAgB,GAAG,GAAW,EAAE;AACpC,0EAA0E;AAC1E,0EAA0E;AAC1E,4CAA4C;AAC5C,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,uBAAuB,CAAC,CAAC;AAE9D,MAAM,eAAe,GAAG,CAAC,QAAgB,EAAE,KAAc,EAAQ,EAAE;IACjE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACvE,MAAM,aAAa,GAAG,GAAG,QAAQ,IAAI,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,MAAM,CAAC;IACvE,EAAE,CAAC,aAAa,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QAC9D,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;IACH,EAAE,CAAC,UAAU,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;AACzC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,QAAiB,EAAoB,EAAE;IACjF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,IAAI,gBAAgB,EAAE,EAAE,MAAM,CAAC,CAI7E,CAAC;QACF,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAClD,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,CACtB,CAAC,KAAK,EAA2B,EAAE,CACjC,OAAO,KAAK,KAAK,QAAQ;gBACzB,KAAK,KAAK,IAAI;gBACd,OAAQ,KAAwB,CAAC,SAAS,KAAK,QAAQ;gBACvD,CAAE,KAAwB,CAAC,MAAM,KAAK,OAAO;oBAC1C,KAAwB,CAAC,MAAM,KAAK,SAAS,CAAC,CACpD,CAAC;QACJ,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,OAAO,GAAG,CAAC,KAAK;iBACb,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC;iBAC7D,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,OAAgB,EAAE,CAAC,CAAC,CAAC;QACnE,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,yDAAyD;QACzD,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,MAAgC,EAChC,QAAiB,EACX,EAAE;IACR,MAAM,QAAQ,GAA2B,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC;IAC5E,eAAe,CAAC,QAAQ,IAAI,gBAAgB,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC5D,CAAC,CAAC"}