pi-fabric 0.1.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 (203) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +635 -0
  3. package/THIRD_PARTY_NOTICES.md +9 -0
  4. package/dist/activity/store.d.ts +27 -0
  5. package/dist/activity/store.d.ts.map +1 -0
  6. package/dist/activity/store.js +474 -0
  7. package/dist/activity/store.js.map +1 -0
  8. package/dist/activity/types.d.ts +101 -0
  9. package/dist/activity/types.d.ts.map +1 -0
  10. package/dist/activity/types.js +2 -0
  11. package/dist/activity/types.js.map +1 -0
  12. package/dist/actors/context.d.ts +13 -0
  13. package/dist/actors/context.d.ts.map +1 -0
  14. package/dist/actors/context.js +155 -0
  15. package/dist/actors/context.js.map +1 -0
  16. package/dist/actors/manager.d.ts +33 -0
  17. package/dist/actors/manager.d.ts.map +1 -0
  18. package/dist/actors/manager.js +751 -0
  19. package/dist/actors/manager.js.map +1 -0
  20. package/dist/actors/types.d.ts +67 -0
  21. package/dist/actors/types.d.ts.map +1 -0
  22. package/dist/actors/types.js +2 -0
  23. package/dist/actors/types.js.map +1 -0
  24. package/dist/capture/catalog.d.ts +23 -0
  25. package/dist/capture/catalog.d.ts.map +1 -0
  26. package/dist/capture/catalog.js +53 -0
  27. package/dist/capture/catalog.js.map +1 -0
  28. package/dist/capture/interceptor.d.ts +13 -0
  29. package/dist/capture/interceptor.d.ts.map +1 -0
  30. package/dist/capture/interceptor.js +130 -0
  31. package/dist/capture/interceptor.js.map +1 -0
  32. package/dist/commands/fabric.d.ts +14 -0
  33. package/dist/commands/fabric.d.ts.map +1 -0
  34. package/dist/commands/fabric.js +237 -0
  35. package/dist/commands/fabric.js.map +1 -0
  36. package/dist/config.d.ts +89 -0
  37. package/dist/config.d.ts.map +1 -0
  38. package/dist/config.js +255 -0
  39. package/dist/config.js.map +1 -0
  40. package/dist/core/action-registry.d.ts +71 -0
  41. package/dist/core/action-registry.d.ts.map +1 -0
  42. package/dist/core/action-registry.js +273 -0
  43. package/dist/core/action-registry.js.map +1 -0
  44. package/dist/core/approval-controller.d.ts +11 -0
  45. package/dist/core/approval-controller.d.ts.map +1 -0
  46. package/dist/core/approval-controller.js +45 -0
  47. package/dist/core/approval-controller.js.map +1 -0
  48. package/dist/core/pi-tools.d.ts +4 -0
  49. package/dist/core/pi-tools.d.ts.map +1 -0
  50. package/dist/core/pi-tools.js +11 -0
  51. package/dist/core/pi-tools.js.map +1 -0
  52. package/dist/core/tool-ownership.d.ts +12 -0
  53. package/dist/core/tool-ownership.d.ts.map +1 -0
  54. package/dist/core/tool-ownership.js +41 -0
  55. package/dist/core/tool-ownership.js.map +1 -0
  56. package/dist/execution-service.d.ts +41 -0
  57. package/dist/execution-service.d.ts.map +1 -0
  58. package/dist/execution-service.js +210 -0
  59. package/dist/execution-service.js.map +1 -0
  60. package/dist/fabric-state.d.ts +38 -0
  61. package/dist/fabric-state.d.ts.map +1 -0
  62. package/dist/fabric-state.js +275 -0
  63. package/dist/fabric-state.js.map +1 -0
  64. package/dist/index.d.ts +4 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +491 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/mesh/store.d.ts +68 -0
  69. package/dist/mesh/store.d.ts.map +1 -0
  70. package/dist/mesh/store.js +387 -0
  71. package/dist/mesh/store.js.map +1 -0
  72. package/dist/protocol.d.ts +62 -0
  73. package/dist/protocol.d.ts.map +1 -0
  74. package/dist/protocol.js +3 -0
  75. package/dist/protocol.js.map +1 -0
  76. package/dist/providers/agents-provider.d.ts +15 -0
  77. package/dist/providers/agents-provider.d.ts.map +1 -0
  78. package/dist/providers/agents-provider.js +393 -0
  79. package/dist/providers/agents-provider.js.map +1 -0
  80. package/dist/providers/captured-tools-provider.d.ts +23 -0
  81. package/dist/providers/captured-tools-provider.d.ts.map +1 -0
  82. package/dist/providers/captured-tools-provider.js +170 -0
  83. package/dist/providers/captured-tools-provider.js.map +1 -0
  84. package/dist/providers/mcp-provider.d.ts +15 -0
  85. package/dist/providers/mcp-provider.d.ts.map +1 -0
  86. package/dist/providers/mcp-provider.js +289 -0
  87. package/dist/providers/mcp-provider.js.map +1 -0
  88. package/dist/providers/mesh-provider.d.ts +13 -0
  89. package/dist/providers/mesh-provider.d.ts.map +1 -0
  90. package/dist/providers/mesh-provider.js +175 -0
  91. package/dist/providers/mesh-provider.js.map +1 -0
  92. package/dist/providers/pi-tools-provider.d.ts +14 -0
  93. package/dist/providers/pi-tools-provider.d.ts.map +1 -0
  94. package/dist/providers/pi-tools-provider.js +200 -0
  95. package/dist/providers/pi-tools-provider.js.map +1 -0
  96. package/dist/runtime/guest-types.d.ts +3 -0
  97. package/dist/runtime/guest-types.d.ts.map +1 -0
  98. package/dist/runtime/guest-types.js +313 -0
  99. package/dist/runtime/guest-types.js.map +1 -0
  100. package/dist/runtime/quickjs-runtime.d.ts +18 -0
  101. package/dist/runtime/quickjs-runtime.d.ts.map +1 -0
  102. package/dist/runtime/quickjs-runtime.js +538 -0
  103. package/dist/runtime/quickjs-runtime.js.map +1 -0
  104. package/dist/runtime/type-checker.d.ts +10 -0
  105. package/dist/runtime/type-checker.d.ts.map +1 -0
  106. package/dist/runtime/type-checker.js +71 -0
  107. package/dist/runtime/type-checker.js.map +1 -0
  108. package/dist/subagents/budget-ledger.d.ts +61 -0
  109. package/dist/subagents/budget-ledger.d.ts.map +1 -0
  110. package/dist/subagents/budget-ledger.js +92 -0
  111. package/dist/subagents/budget-ledger.js.map +1 -0
  112. package/dist/subagents/manager.d.ts +27 -0
  113. package/dist/subagents/manager.d.ts.map +1 -0
  114. package/dist/subagents/manager.js +497 -0
  115. package/dist/subagents/manager.js.map +1 -0
  116. package/dist/subagents/semaphore.d.ts +7 -0
  117. package/dist/subagents/semaphore.d.ts.map +1 -0
  118. package/dist/subagents/semaphore.js +54 -0
  119. package/dist/subagents/semaphore.js.map +1 -0
  120. package/dist/subagents/transports/localterm-transport.d.ts +7 -0
  121. package/dist/subagents/transports/localterm-transport.d.ts.map +1 -0
  122. package/dist/subagents/transports/localterm-transport.js +54 -0
  123. package/dist/subagents/transports/localterm-transport.js.map +1 -0
  124. package/dist/subagents/transports/process-transport.d.ts +7 -0
  125. package/dist/subagents/transports/process-transport.d.ts.map +1 -0
  126. package/dist/subagents/transports/process-transport.js +17 -0
  127. package/dist/subagents/transports/process-transport.js.map +1 -0
  128. package/dist/subagents/transports/process-utils.d.ts +16 -0
  129. package/dist/subagents/transports/process-utils.d.ts.map +1 -0
  130. package/dist/subagents/transports/process-utils.js +57 -0
  131. package/dist/subagents/transports/process-utils.js.map +1 -0
  132. package/dist/subagents/transports/screen-transport.d.ts +7 -0
  133. package/dist/subagents/transports/screen-transport.d.ts.map +1 -0
  134. package/dist/subagents/transports/screen-transport.js +33 -0
  135. package/dist/subagents/transports/screen-transport.js.map +1 -0
  136. package/dist/subagents/transports/tmux-transport.d.ts +7 -0
  137. package/dist/subagents/transports/tmux-transport.d.ts.map +1 -0
  138. package/dist/subagents/transports/tmux-transport.js +41 -0
  139. package/dist/subagents/transports/tmux-transport.js.map +1 -0
  140. package/dist/subagents/types.d.ts +132 -0
  141. package/dist/subagents/types.d.ts.map +1 -0
  142. package/dist/subagents/types.js +2 -0
  143. package/dist/subagents/types.js.map +1 -0
  144. package/dist/subagents/worktree-manager.d.ts +12 -0
  145. package/dist/subagents/worktree-manager.d.ts.map +1 -0
  146. package/dist/subagents/worktree-manager.js +53 -0
  147. package/dist/subagents/worktree-manager.js.map +1 -0
  148. package/dist/ui/controller.d.ts +14 -0
  149. package/dist/ui/controller.d.ts.map +1 -0
  150. package/dist/ui/controller.js +153 -0
  151. package/dist/ui/controller.js.map +1 -0
  152. package/dist/ui/dashboard.d.ts +36 -0
  153. package/dist/ui/dashboard.d.ts.map +1 -0
  154. package/dist/ui/dashboard.js +620 -0
  155. package/dist/ui/dashboard.js.map +1 -0
  156. package/dist/ui/fabric-render.d.ts +28 -0
  157. package/dist/ui/fabric-render.d.ts.map +1 -0
  158. package/dist/ui/fabric-render.js +280 -0
  159. package/dist/ui/fabric-render.js.map +1 -0
  160. package/dist/ui/format.d.ts +7 -0
  161. package/dist/ui/format.d.ts.map +1 -0
  162. package/dist/ui/format.js +57 -0
  163. package/dist/ui/format.js.map +1 -0
  164. package/dist/ui/highlight.d.ts +13 -0
  165. package/dist/ui/highlight.d.ts.map +1 -0
  166. package/dist/ui/highlight.js +337 -0
  167. package/dist/ui/highlight.js.map +1 -0
  168. package/dist/ui/settings.d.ts +21 -0
  169. package/dist/ui/settings.d.ts.map +1 -0
  170. package/dist/ui/settings.js +480 -0
  171. package/dist/ui/settings.js.map +1 -0
  172. package/dist/ui/snapshot.d.ts +5 -0
  173. package/dist/ui/snapshot.d.ts.map +1 -0
  174. package/dist/ui/snapshot.js +120 -0
  175. package/dist/ui/snapshot.js.map +1 -0
  176. package/dist/ui/types.d.ts +57 -0
  177. package/dist/ui/types.d.ts.map +1 -0
  178. package/dist/ui/types.js +11 -0
  179. package/dist/ui/types.js.map +1 -0
  180. package/dist/ui/widget.d.ts +16 -0
  181. package/dist/ui/widget.d.ts.map +1 -0
  182. package/dist/ui/widget.js +199 -0
  183. package/dist/ui/widget.js.map +1 -0
  184. package/dist/util.d.ts +2 -0
  185. package/dist/util.d.ts.map +1 -0
  186. package/dist/util.js +10 -0
  187. package/dist/util.js.map +1 -0
  188. package/dist/worker.d.ts +3 -0
  189. package/dist/worker.d.ts.map +1 -0
  190. package/dist/worker.js +478 -0
  191. package/dist/worker.js.map +1 -0
  192. package/package.json +87 -0
  193. package/skills/fabric-advisor/SKILL.md +53 -0
  194. package/skills/fabric-ambient/SKILL.md +84 -0
  195. package/skills/fabric-council/SKILL.md +37 -0
  196. package/skills/fabric-exec/SKILL.md +39 -0
  197. package/skills/fabric-exec/references/agents.md +78 -0
  198. package/skills/fabric-exec/references/mcp.md +46 -0
  199. package/skills/fabric-exec/references/mesh.md +42 -0
  200. package/skills/fabric-rlm/SKILL.md +84 -0
  201. package/skills/fabric-supervisor/SKILL.md +63 -0
  202. package/skills/fabric-swarm/SKILL.md +85 -0
  203. package/skills/fabric-workflow/SKILL.md +69 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 monotykamary
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.
package/README.md ADDED
@@ -0,0 +1,635 @@
1
+ <div align="center">
2
+
3
+ # 🧵 pi-fabric
4
+
5
+ **A programmable tool and agent runtime for [Pi](https://github.com/earendil-works/pi-coding-agent)**
6
+
7
+ _One type-checked program for tools, MCP, agents, workflows, actors, mesh, councils, and recursion._
8
+
9
+ <p>
10
+ <img src="https://raw.githubusercontent.com/monotykamary/pi-fabric/main/media/cover.jpg" alt="Pi Fabric composing tools and agents in the Pi TUI" width="1100">
11
+ </p>
12
+
13
+ [![npm version](https://img.shields.io/npm/v/pi-fabric?style=for-the-badge&logo=npm&color=cb3837)](https://www.npmjs.com/package/pi-fabric)
14
+ [![checks](https://img.shields.io/github/actions/workflow/status/monotykamary/pi-fabric/test.yml?branch=main&style=for-the-badge&label=checks)](https://github.com/monotykamary/pi-fabric/actions/workflows/test.yml)
15
+ [![pi extension](https://img.shields.io/badge/pi-extension-8b5cf6?style=for-the-badge)](https://github.com/earendil-works/pi-coding-agent)
16
+ [![license](https://img.shields.io/badge/license-MIT-f4c430?style=for-the-badge)](LICENSE)
17
+
18
+ </div>
19
+
20
+ ---
21
+
22
+ Pi Fabric turns tool use into code. The model sees one `fabric_exec` tool and writes type-checked TypeScript that can compose Pi's core tools, lazily captured extension tools, MCP servers, child agents, persistent actors, and durable coordination. Intermediate values stay inside a QuickJS sandbox; only the final result returns to the model context.
23
+
24
+ ## Why Fabric?
25
+
26
+ | | Capability | What it unlocks |
27
+ | :-: | ---------- | --------------- |
28
+ | ⚡ | **Code mode** | One flat tool schema; branching, loops, fan-out, and data flow live in checked TypeScript. |
29
+ | 🧰 | **Capability routing** | Call Pi core tools, captured extension tools, MCP servers, or explicit Fabric providers through one runtime. |
30
+ | 🧑‍🤝‍🧑 | **Agent runtime** | Run one-shot workers, persistent event-driven actors, councils, and bounded recursive queries. |
31
+ | 🕸️ | **Workflows + mesh** | Track phases and progress while coordinating durable topics, shared tasks, and compare-and-swap state. |
32
+ | 🛡️ | **Guardrails** | Enforce approvals, isolation, timeouts, concurrency, recursion depth, and shared cost budgets at the host bridge. |
33
+ | 🎛️ | **Native TUI** | See structured nested previews, live activity, an interactive dashboard, and settings without leaving Pi. |
34
+
35
+ ## Install
36
+
37
+ From npm:
38
+
39
+ ```bash
40
+ pi install npm:pi-fabric
41
+ ```
42
+
43
+ From GitHub:
44
+
45
+ ```bash
46
+ pi install git:github.com/monotykamary/pi-fabric
47
+ ```
48
+
49
+ From a local checkout:
50
+
51
+ ```bash
52
+ pnpm install
53
+ pnpm build
54
+ pi install /absolute/path/to/pi-fabric
55
+ ```
56
+
57
+ For one development run:
58
+
59
+ ```bash
60
+ pi -e /absolute/path/to/pi-fabric
61
+ ```
62
+
63
+ Requires Node.js 24 or newer and Pi 0.80.6 or newer.
64
+
65
+ ## Quick start
66
+
67
+ Ask Fabric to compose multiple operations in one call:
68
+
69
+ ```ts
70
+ const [manifest, sources] = await Promise.all([
71
+ pi.read({ path: "package.json" }),
72
+ pi.find({ pattern: "**/*.ts", path: "src" }),
73
+ ]);
74
+
75
+ return {
76
+ package: JSON.parse(manifest).name,
77
+ sourceCount: sources.split("\n").filter(Boolean).length,
78
+ };
79
+ ```
80
+
81
+ Only the returned value enters the parent model's context. Everything else stays inside the execution.
82
+
83
+ ## Code API
84
+
85
+ With the default full code mode, `fabric_exec` exclusively owns Pi core tool execution. The parent model sees one programmable tool instead of direct `read`, `bash`, `edit`, `write`, `grep`, `find`, and `ls` schemas. Fabric programs use those capabilities through `pi.*`:
86
+
87
+ ```ts
88
+ const files = await pi.find({ pattern: "**/*.ts", path: "src" });
89
+ const matches = await pi.grep({ pattern: "TODO", path: "src" });
90
+ return { files, matches };
91
+ ```
92
+
93
+ Independent calls should be parallel:
94
+
95
+ ```ts
96
+ const [packageJson, readme] = await Promise.all([
97
+ pi.read({ path: "package.json" }),
98
+ pi.read({ path: "README.md" }),
99
+ ]);
100
+ return {
101
+ package: JSON.parse(packageJson).name,
102
+ readmeLines: readme.split("\n").length,
103
+ };
104
+ ```
105
+
106
+ ### Full code mode
107
+
108
+ `fullCodeMode: true` is the default. Fabric removes active Pi core tools from the parent model and exposes their implementations only inside `fabric_exec` through `pi.*`. Registered overrides such as security gates and code previews are captured too, so `pi.read()` continues to route through the override rather than bypassing it.
109
+
110
+ Fabric remembers which native core tools were active before taking ownership. Switching to orchestration-only mode or unloading Fabric restores that selection. Full-mode ownership is reasserted before user input and agent startup, so tools manually re-enabled during the session do not leak back into the parent schema.
111
+
112
+ ### Orchestration-only mode
113
+
114
+ Users who want Fabric for MCP, agents, ambient actors, parallel workflows, councils, and recursive delegation—but want Pi's core tools to remain entirely native—can opt out of full code mode:
115
+
116
+ ```json
117
+ {
118
+ "fullCodeMode": false
119
+ }
120
+ ```
121
+
122
+ In orchestration-only mode:
123
+
124
+ - Pi's `read`, `bash`, `edit`, `write`, `grep`, `find`, and `ls` tools stay on Pi's normal model-facing and execution paths.
125
+ - Registered extension tools also remain in Pi's native registry; Fabric does not hide, wrap, or expose them through `extensions.*`.
126
+ - `pi.*`, `extensions.*`, and equivalent `tools.call()` references are unavailable inside `fabric_exec`, including when TypeScript checks are bypassed.
127
+ - MCP providers, one-shot and recursive agents, persistent ambient actors, dynamic workflows, mesh coordination, councils, explicit Fabric providers, and the Fabric TUI remain available.
128
+ - Child agents continue using their allowed Pi tools directly, so parallel and ambient setups do not route their coding operations back through Fabric code mode.
129
+
130
+ The default is `true`. A project can set the flag in `.pi/fabric.json`, or a user can set it globally in `~/.pi/agent/fabric.json`.
131
+
132
+ ### Discovery and generic calls
133
+
134
+ ```ts
135
+ const providers = await tools.providers();
136
+ const candidates = await tools.search({ query: "GitHub issues" });
137
+ const schema = await tools.describe({ ref: candidates[0].ref });
138
+ const result = await tools.call({
139
+ ref: schema.ref,
140
+ args: { query: "is:open label:bug" },
141
+ });
142
+ return result;
143
+ ```
144
+
145
+ ### Captured extension tools
146
+
147
+ When `fullCodeMode` is enabled, Fabric intercepts Pi's `ExtensionRunner.getAllRegisteredTools()` registry chokepoint. This captures tools registered by other extensions at startup or later through `pi.registerTool()`, regardless of whether those extensions load before or after Fabric.
148
+
149
+ Captured custom tools are removed from Pi's model-facing registry by default, so their schemas, snippets, and guidelines do not consume the parent model context. The extension itself remains loaded: its commands, event handlers, state, and UI continue to work. Only tool discovery and invocation become lazy.
150
+
151
+ ```ts
152
+ const matches = await tools.search({ query: "deployment status" });
153
+ const schema = await tools.describe({ ref: matches[0].ref });
154
+ const result = await tools.call({
155
+ ref: schema.ref,
156
+ args: { environment: "staging" },
157
+ });
158
+ return result;
159
+ ```
160
+
161
+ For tool names valid as JavaScript properties, use the shorter proxy:
162
+
163
+ ```ts
164
+ const result = await extensions.project_status({ verbose: true });
165
+ return result.text;
166
+ ```
167
+
168
+ The result preserves `content`, text content as `text`, `details`, `isError`, `terminate`, and source provenance. Fabric runs the captured definition's `prepareArguments()` and original executor with its owning extension context. Pi's `tool_call`, `tool_result`, and `tool_execution_*` lifecycle handlers are also applied to nested captured calls.
169
+
170
+ Extension overrides of core tools are captured and hidden with their built-in counterparts in full code mode. Inside Fabric, `pi.read`, `pi.bash`, and the other built-ins automatically route through a captured override when one exists; `extensions.read` exposes the override's full native result shape. `capture.keepVisible` can retain non-core extension tools in Pi's direct registry, but core tool names are always excluded while full code mode owns them.
171
+
172
+ ### MCP through mcporter
173
+
174
+ Pi Fabric uses the public [`mcporter`](https://github.com/openclaw/mcporter) runtime. It inherits mcporter's config discovery, imports, OAuth cache, and connection pooling.
175
+
176
+ ```ts
177
+ const servers = await mcp.servers(); // names and transport metadata; credentials are never exposed
178
+ const result = await mcp.context7.resolve_library_id({
179
+ libraryName: "react",
180
+ query: "hooks documentation",
181
+ });
182
+ return result;
183
+ ```
184
+
185
+ Use `await mcp.reload()` after changing mcporter configuration. `mcp.call({ server, tool, args })` is available when a server or tool name cannot be expressed conveniently as property access.
186
+
187
+ A program can register an ephemeral server directly in mcporter's pooled runtime after host approval:
188
+
189
+ ```ts
190
+ await mcp.register({
191
+ name: "project-docs",
192
+ command: "npx",
193
+ args: ["-y", "@example/docs-mcp"],
194
+ cwd: ".",
195
+ });
196
+ return mcp.project_docs.search({ query: "authentication" });
197
+ ```
198
+
199
+ HTTP servers use `baseUrl` instead of `command`. Dynamic definitions live until `mcp.reload()` or session shutdown; they are not written to config.
200
+
201
+ ### Dynamic workflows
202
+
203
+ Fabric programs already keep orchestration and intermediate values in code. The workflow globals add Claude Code-style names and progress phases without introducing a second JavaScript runtime:
204
+
205
+ ```ts
206
+ await workflow.configure({
207
+ name: "Authentication audit",
208
+ description: "Discover relevant files, audit them in parallel, then verify findings",
209
+ });
210
+
211
+ await phase("Discover", { total: 1 });
212
+ const inventory = await agent<{ files: string[] }>(
213
+ "List source files relevant to authentication.",
214
+ {
215
+ label: "auth inventory",
216
+ tools: ["read", "grep", "find", "ls"],
217
+ schema: {
218
+ type: "object",
219
+ properties: { files: { type: "array", items: { type: "string" } } },
220
+ required: ["files"],
221
+ additionalProperties: false,
222
+ },
223
+ },
224
+ );
225
+
226
+ await phase("Audit", { total: inventory.files.length });
227
+ const findings = await parallel(
228
+ inventory.files.map(
229
+ (file) => () =>
230
+ agent(`Audit ${file} for concrete auth defects.`, {
231
+ label: `audit ${file}`,
232
+ tools: ["read", "grep", "find", "ls"],
233
+ }),
234
+ ),
235
+ { concurrency: 8 },
236
+ );
237
+
238
+ await phase("Verify", { total: 1 });
239
+ return agent(`Verify and synthesize these findings: ${JSON.stringify(findings)}`, {
240
+ label: "verify findings",
241
+ tools: ["read", "grep", "find", "ls"],
242
+ });
243
+ ```
244
+
245
+ Available helpers are `workflow.agent()`, `workflow.parallel()`, `workflow.pipeline()`, `workflow.configure()`, `workflow.phase()`, `workflow.item()`, `workflow.event()`, `workflow.log()`, and `workflow.budget`. `configure()` names the activity surface; phase options accept `id`, `description`, and an expected `total`. `item()` lets arbitrary non-agent work report status, detail, and progress without requiring a bespoke renderer. `event()` adds a bounded milestone to the run feed. The shorter `agent()`, `parallel()`, `pipeline()`, `phase()`, `log()`, and `budget` aliases are equivalent. `fabric_exec` accepts optional `agentBudget` and `tokenBudget` limits; configuration supplies a hard per-execution agent cap.
246
+
247
+ A JSON Schema on an agent request makes the worker return validated structured data through `result.value`. Workflow helpers return that value directly and otherwise return the agent's final text.
248
+
249
+ ### Subagents
250
+
251
+ ```ts
252
+ const result = await agents.run({
253
+ name: "security-review",
254
+ task: "Review the current diff for concrete security defects. Do not edit files.",
255
+ transport: "localterm",
256
+ tools: ["read", "grep", "find", "ls"],
257
+ });
258
+ return result;
259
+ ```
260
+
261
+ Background handles are explicit:
262
+
263
+ ```ts
264
+ const handle = await agents.spawn({
265
+ task: "Map the persistence layer and identify its public entry points.",
266
+ transport: "tmux",
267
+ });
268
+
269
+ // Do independent work here.
270
+
271
+ return await agents.wait({ id: handle.id });
272
+ ```
273
+
274
+ Children inherit the parent model unless `model` is specified. Their tool allowlist defaults to `subagents.defaultTools`.
275
+
276
+ Supported transports:
277
+
278
+ | Transport | Behavior | Attach command |
279
+ | ----------- | ---------------------------------------------------------- | ---------------------------- |
280
+ | `process` | Detached local worker process; default and lowest overhead | none |
281
+ | `tmux` | One detached tmux session per child | `tmux attach-session -t …` |
282
+ | `screen` | One detached GNU Screen session per child | `screen -r …` |
283
+ | `localterm` | One pinned LocalTerm PTY per child | `localterm session attach …` |
284
+ | `auto` | Tries LocalTerm, tmux, screen, then process | transport-specific |
285
+
286
+ LocalTerm already exposes the needed tmux-parity primitives: detached creation, pinning, listing, capture, exec, attach, and kill. Pi Fabric therefore requires no LocalTerm patch. Start its daemon before selecting it:
287
+
288
+ ```bash
289
+ localterm start
290
+ ```
291
+
292
+ Use `/fabric agents` to list children and `/fabric attach <id>` to display the appropriate attach command. Abort signals propagate to the transport and child Pi process.
293
+
294
+ Set `worktree: true` to create a dedicated Git worktree and `pi-fabric/<name>-<id>` branch. Worktrees are retained for inspection until `agents.cleanup()` is called.
295
+
296
+ ### Persistent actors and ambient agents
297
+
298
+ `agents.create()` creates a named actor with a persistent Pi session, a serial mailbox, and optional subscriptions to parent-session events or durable mesh topics:
299
+
300
+ ```ts
301
+ return agents.create({
302
+ name: "auth-supervisor",
303
+ instructions: `Watch the main session until the auth migration is complete and tested.
304
+ Prefer silence. Reply with a directive only for material drift, a blocker, or verified completion.`,
305
+ events: ["agent_settled", "tool_error"],
306
+ responseMode: "directive",
307
+ delivery: "steer",
308
+ triggerTurn: true,
309
+ tools: ["read", "grep", "find", "ls"],
310
+ });
311
+ ```
312
+
313
+ This is the primitive behind emergent supervisors and advisors; neither requires another extension. Host events include a bounded recent-session snapshot. Actors process messages one at a time, coalesce repeated host events by default, keep model context in their own session file, and resume when the same Pi session is reopened in a trusted project.
314
+
315
+ Two response modes are available:
316
+
317
+ - `text`: every non-empty response becomes an actor outbox message.
318
+ - `directive`: validated `{ action: "silent" | "message" | "stop", message?, data? }` output lets the actor decide whether intervention is useful.
319
+
320
+ Delivery can remain in `mailbox` or enter the main session as `steer`, `followUp`, or `nextTurn`. The creator fixes delivery policy; an actor cannot escalate it in a response. Use `agents.ask()` for a blocking exchange, `agents.tell()` for fire-and-forget mail, `agents.messages()` for history, and `agents.remove()` for cleanup.
321
+
322
+ ### Durable mesh coordination
323
+
324
+ The `mesh` API is a project-scoped, event-sourced coordination substrate:
325
+
326
+ ```ts
327
+ const event = await mesh.publish({
328
+ topic: "team.auth",
329
+ kind: "finding",
330
+ text: "Refresh-token rotation is not atomic",
331
+ data: { path: "src/auth/refresh.ts" },
332
+ });
333
+
334
+ const task = await mesh.put({
335
+ key: "tasks/auth-review",
336
+ value: { status: "ready", owner: null },
337
+ ifVersion: 0,
338
+ });
339
+
340
+ const claimed = await mesh.put({
341
+ key: task.key,
342
+ value: { status: "claimed", owner: "security-reviewer" },
343
+ ifVersion: task.version,
344
+ });
345
+ return { event, claimed };
346
+ ```
347
+
348
+ Topics provide durable channel and direct-message semantics with sequence cursors. `mesh.members()` discovers actor presence across live Fabric sessions. Versioned `get`/`put`/`delete` operations provide compare-and-swap state for task claims, leases, reservations, and decisions. Together with persistent actors, these are sufficient to express messenger-style swarms in Fabric code without a daemon or fixed planner/worker roles.
349
+
350
+ ### Councils
351
+
352
+ ```ts
353
+ return council.run({
354
+ task: "Review the current implementation and recommend whether it is ready to merge.",
355
+ roles: ["correctness reviewer", "security reviewer", "test reviewer"],
356
+ transport: "localterm",
357
+ synthesize: true,
358
+ });
359
+ ```
360
+
361
+ Council members run concurrently under the global subagent semaphore. With `synthesize: true`, a final child agent reconciles their reports.
362
+
363
+ ### Recursive queries
364
+
365
+ ```ts
366
+ return rlm.query({
367
+ task: "Recursively decompose this repository and produce a compact architecture map.",
368
+ transport: "process",
369
+ });
370
+ ```
371
+
372
+ `rlm.query()` is `agents.run()` with Fabric enabled in the child. Recursion is rejected at `subagents.maxDepth`. Approval of the initial recursive call delegates only the `agent` risk capability to recursive children; network, execution, and write approvals are not inherited. Each Fabric process enforces its own configured concurrency and timeout limits. When `subagents.budgetUsd` is set, a shared append-only cost ledger bounds total spend across the whole recursion tree: every node records the cost of the children it spawns into one ledger file inherited via environment, and each node rejects a new child when the accumulated spend reaches the budget. The check is best-effort (concurrent children can each pass before any cost lands, so a tree may slightly overshoot); the race-free ceiling remains `subagents.maxPerExecution`. The result and live status of every recursive child carry a `budget` summary (`limit`, `spent`, `remaining`, `tokens`).
373
+
374
+ ## Included skills
375
+
376
+ Pi discovers these package skills automatically:
377
+
378
+ | Command | Pattern |
379
+ | ---------------------------------- | ----------------------------------------------------------------------- |
380
+ | `/skill:fabric-supervisor <goal>` | Persistent goal watcher driven by `agent_settled` and tool-error events |
381
+ | `/skill:fabric-advisor [focus]` | Decision-point peer reviewer (idle and tool errors) that prefers silence |
382
+ | `/skill:fabric-ambient <role>` | Meta-pattern for custom event-driven ambient actors |
383
+ | `/skill:fabric-workflow <task>` | Code-held phases, fan-out, pipelines, structured output, and synthesis |
384
+ | `/skill:fabric-rlm <task>` | Recursive self-delegation via `rlm.query()` for tasks too big for one context window |
385
+ | `/skill:fabric-swarm <objective>` | Persistent actors, durable topics, and CAS-based shared tasks |
386
+ | `/skill:fabric-council <decision>` | Bounded independent perspectives plus synthesis |
387
+
388
+ `fabric-exec` is the one discoverable reference skill: it holds the full `fabric_exec` API (core `pi.*` tools, `tools` discovery, `π` strings, error recovery) plus `references/` files for MCP, agents/rlm, and mesh loaded by relative path (not separate skills). It appears in `<available_skills>`; load it via `read` before your first `fabric_exec` call or when a call errors.
389
+
390
+ Supervisor and advisor are deliberately skills rather than hard-coded host services: the skill writes ordinary Fabric code over the same actor primitive available to every other pattern.
391
+
392
+ ## Visual integration
393
+
394
+ `fabric_exec` uses the public `pi-code-previews` cooperative shell. It inherits the user's border/background mode, collapsed-result behavior, error styling, and tool-call timing without taking ownership of Pi's built-in tool renderers. Its renderer adds a numbered TypeScript preview, live phase/call activity, and compact phase/nested-call summaries. Nested `pi.read`/`pi.bash`/`pi.grep`/`pi.find`/`pi.ls`/`pi.write`/`pi.edit` calls render as structured previews (path/command headers, numbered content) instead of raw JSON. `pi.read` and `pi.write` content is syntax-highlighted with the same shiki theme configured for `pi-code-previews`, so colors match Pi's native tool previews; `pi.bash` commands are highlighted in the call title and `pi.edit` operations render as a `+`/`-` line diff (with shared context) using Pi core's diff colors. The highlighter initializes lazily and falls back to plain text until ready. Collapsed previews show the configured expand keybinding (e.g. `Ctrl-O`) to expand, matching Pi's built-in tool previews. Users do not need to install `pi-code-previews` separately.
395
+
396
+ Fabric also owns a general-purpose, theme-aware activity surface for any agent setup:
397
+
398
+ - A compact widget above the chat (like `pi-supervisor`) follows the current phase and shows active agents, actors, tools, custom items, shared tasks, token use, and elapsed time. It disappears after ordinary runs become quiet, while persistent actors remain visible as a compact ambient row.
399
+ - `/fabric dashboard` opens a responsive interactive overlay. Wide terminals use a Claude-workflow-style phase pane beside agents and work items; narrow terminals stack the same panels. Agent detail includes task, model, current tool, usage, result, worktree, and attach metadata. Actor mailboxes, mesh state, and recent mesh events use the same view rather than role-specific screens.
400
+ - `/fabric settings` opens an inline settings view that mirrors Pi core's `/settings` (top and bottom borders, fuzzy search, section submenus) and writes changes to `fabric.json`. Trusted projects write to `<project>/.pi/fabric.json`; untrusted sessions write to the global `~/.pi/agent/fabric.json`. Full code mode, capture, executor, approvals, and UI changes apply immediately; mesh, subagent, and MCP changes persist and take effect on the next `/fabric reload`. List editors for `subagents.defaultTools` and `capture.keepVisible` toggle known tools on and off; `keepVisible` candidates include `fabric_exec` plus every captured extension tool.
401
+ - `↑`/`↓` or `j`/`k` select, `←`/`→` or Tab switch panes, Enter drills into details, `f` cycles status filters, `[`/`]` switches retained runs, and Esc backs out or closes.
402
+
403
+ The surface is data-driven. Fabric automatically instruments nested provider calls, subagents, persistent actors, and task-shaped mesh entries. A workflow can add domain-specific labels and arbitrary progress without adding extension UI code:
404
+
405
+ ```ts
406
+ await workflow.configure({ name: "Release train", description: "Build, verify, and publish" });
407
+ await phase("Build", { total: packages.length });
408
+ await workflow.item({
409
+ id: "docs",
410
+ label: "Documentation",
411
+ status: "running",
412
+ completed: 2,
413
+ total: 5,
414
+ });
415
+ await workflow.event({ message: "Canary passed", level: "success" });
416
+ ```
417
+
418
+ External Fabric providers can emit structured `context.activity()` updates for an entity, progress message, or metrics. This keeps the TUI generic while allowing a virtual provider to expose richer live state.
419
+
420
+ ## Configuration
421
+
422
+ Pi Fabric reads:
423
+
424
+ 1. `~/.pi/agent/fabric.json`
425
+ 2. `<project>/.pi/fabric.json`, only for trusted projects
426
+
427
+ Project values override global values.
428
+
429
+ ```json
430
+ {
431
+ "fullCodeMode": true,
432
+ "executor": {
433
+ "timeoutMs": 120000,
434
+ "memoryLimitBytes": 67108864,
435
+ "maxOutputChars": 100000,
436
+ "maxNestedResultChars": 2000000
437
+ },
438
+ "approvals": {
439
+ "read": "allow",
440
+ "write": "allow",
441
+ "execute": "allow",
442
+ "network": "allow",
443
+ "agent": "allow"
444
+ },
445
+ "capture": {
446
+ "enabled": true,
447
+ "hideFromModel": true,
448
+ "keepVisible": ["fabric_exec"],
449
+ "defaultRisk": "execute",
450
+ "risks": {
451
+ "read": "read",
452
+ "grep": "read",
453
+ "find": "read",
454
+ "ls": "read",
455
+ "edit": "write",
456
+ "write": "write",
457
+ "bash": "execute"
458
+ }
459
+ },
460
+ "mcp": {
461
+ "enabled": true,
462
+ "disableOAuth": true,
463
+ "allowDynamicServers": true,
464
+ "callTimeoutMs": 120000
465
+ },
466
+ "subagents": {
467
+ "enabled": true,
468
+ "transport": "process",
469
+ "maxConcurrent": 4,
470
+ "maxPerExecution": 100,
471
+ "maxDepth": 2,
472
+ "timeoutMs": 600000,
473
+ "extensions": true,
474
+ "defaultTools": ["read", "bash", "edit", "write", "grep", "find", "ls"],
475
+ "retainRuns": false,
476
+ "notifyOnComplete": true,
477
+ "budgetUsd": 0
478
+ },
479
+ "ui": {
480
+ "enabled": true,
481
+ "widget": "auto",
482
+ "maxRows": 6,
483
+ "refreshMs": 500,
484
+ "lingerMs": 10000,
485
+ "eventHistory": 80
486
+ },
487
+ "mesh": {
488
+ "enabled": true,
489
+ "maxEventBytes": 262144,
490
+ "maxReadEvents": 500,
491
+ "actorPollMs": 250,
492
+ "actorQueueLimit": 32,
493
+ "eventContextChars": 40000
494
+ }
495
+ }
496
+ ```
497
+
498
+ `fullCodeMode` defaults to `true`. Full mode deactivates native core tools in the parent session and makes `fabric_exec` their exclusive model-facing owner. When false, Fabric uses orchestration-only mode: native Pi and registered extension tools remain direct, capture is disabled, and Fabric's internal registry omits the `pi` and `extensions` providers.
499
+
500
+ Fabric risk classes are `read`, `write`, `execute`, `network`, and `agent`; approval policy values are `allow`, `ask`, or `deny`. Captured tools default to the conservative `execute` risk because Pi tool definitions do not declare effects. Add exact tool-name overrides under `capture.risks`. Set `capture.hideFromModel` to `false` to index non-core extension tools without hiding them. `capture.keepVisible` names stay in both Fabric and Pi's direct registry, except that Pi core names are always Fabric-owned in full code mode. An `ask` policy is fail-closed in headless modes without interactive UI. Approval is cached by risk class for one `fabric_exec` execution.
501
+
502
+ When `mcp.disableOAuth` is true, MCP calls may use cached credentials but cannot launch a new interactive OAuth flow.
503
+
504
+ The UI `widget` mode is `auto`, `always`, or `hidden`. `auto` shows active work, recent completion, and live persistent actors. The widget renders above the chat (like `pi-supervisor`); set `ui.enabled` to `false` to disable both the widget and dashboard controller.
505
+
506
+ Mesh data defaults to `<project>/.pi/fabric/mesh`. Set `mesh.root` to a relative or absolute path to relocate durable topics, shared state, and actor sessions. Add `.pi/fabric/mesh/` to the project's ignore file unless the coordination log is intentionally versioned. Set `mesh.enabled` to `false` to disable both mesh actions and ambient actor restoration.
507
+
508
+ ## External provider protocol
509
+
510
+ Normal `pi.registerTool()` tools are captured automatically. Extensions can still opt into the versioned provider protocol when they need to expose non-tool capabilities, richer risk declarations, or a large virtual action catalog without registering one Pi tool per action:
511
+
512
+ ```ts
513
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
514
+ import {
515
+ FABRIC_PROVIDER_DISCOVER_EVENT,
516
+ FABRIC_PROVIDER_REGISTER_EVENT,
517
+ type FabricProvider,
518
+ type FabricProviderDiscovery,
519
+ } from "pi-fabric/protocol";
520
+
521
+ export default function extension(pi: ExtensionAPI) {
522
+ const provider: FabricProvider = {
523
+ name: "example",
524
+ description: "Example actions",
525
+ async list() {
526
+ return [];
527
+ },
528
+ async describe() {
529
+ return undefined;
530
+ },
531
+ async invoke() {
532
+ return null;
533
+ },
534
+ };
535
+
536
+ pi.events.emit(FABRIC_PROVIDER_REGISTER_EVENT, {
537
+ version: 1,
538
+ provider,
539
+ overwrite: true,
540
+ });
541
+
542
+ pi.events.on(FABRIC_PROVIDER_DISCOVER_EVENT, (event: FabricProviderDiscovery) => {
543
+ event.register(provider, { overwrite: true });
544
+ });
545
+ }
546
+ ```
547
+
548
+ Providers own their schemas, state, and execution semantics. Pi Fabric validates arguments, enforces the declared risk policy, records nested-call audits, and propagates cancellation. A provider can enrich the generic activity surface without registering a TUI component:
549
+
550
+ ```ts
551
+ async invoke(actionName, args, context) {
552
+ context.activity?.({ type: "entity", id: job.id, kind: "custom", name: job.name });
553
+ context.activity?.({ type: "progress", message: "Indexing package 3/12" });
554
+ context.activity?.({ type: "metrics", tokens: 4200, toolCalls: 9 });
555
+ return job.result;
556
+ }
557
+ ```
558
+
559
+ ## Commands
560
+
561
+ ```text
562
+ /fabric status
563
+ /fabric dashboard
564
+ /fabric settings
565
+ /fabric reload
566
+ /fabric providers
567
+ /fabric captured [query]
568
+ /fabric agents
569
+ /fabric actors
570
+ /fabric messages <actor-id>
571
+ /fabric attach <subagent-id>
572
+ /fabric stop <actor-or-subagent-id>
573
+ ```
574
+
575
+ ## Architecture
576
+
577
+ ```text
578
+ fabric_exec
579
+
580
+
581
+ TypeScript checker → QuickJS sandbox
582
+ │ JSON-only host bridge
583
+
584
+ ActionRegistry
585
+ ├── pi.* built-in Pi tool definitions
586
+ ├── extensions.* captured pi.registerTool definitions
587
+ ├── mcp.* pooled mcporter runtime
588
+ ├── agents.* one-shot workers + persistent mailbox actors
589
+ ├── mesh.* durable topics + compare-and-swap state
590
+ └── external explicit pi.events providers
591
+
592
+ ActivityStore → compact widget + footer status + interactive dashboard
593
+ ```
594
+
595
+ Guest code has no `process`, `require`, filesystem, network, or subprocess globals. All effects cross the host bridge, where schemas, approvals, audit records, timeouts, and cancellation apply. Each execution receives a fresh QuickJS context. Named strings passed in the `strings` tool parameter are available as `π.key`; accessing a key that was not provided throws a clear, actionable error listing the provided keys rather than silently returning `undefined`.
596
+
597
+ ## Tool-call robustness
598
+
599
+ The model-facing `fabric_exec` schema is intentionally flat — one large `code` string plus scalar/optional parameters — with no nested arrays-of-objects containing escaped content. Newer SOTA models are post-trained on one dominant harness's flat tool shapes and can invent trailing keys at the highest-entropy point of a nested escaped-JSON field (e.g. right after closing a long multiline string), which a strict schema rejects. The only nested field, `display`, ignores unknown keys: extras are accepted by the schema and filtered to `{ name, description }` before execution, mirroring the silent-filter behavior the dominant harness's client is trained against.
600
+
601
+ fabric's architecture is itself a mitigation for this class of bug. The model authors TypeScript that calls tools, so it never has to faithfully emit an alternative tool schema under sampling pressure; nested object construction happens in deterministic, type-checked code. The residual failure mode is incorrect TypeScript, caught by the QuickJS type-checker with an actionable, line-numbered error — the validate/report/retry loop at the code level rather than the JSON-schema level.
602
+
603
+ For sessions that also call pi tools directly (`read`/`write`/`edit`/`grep`/`find`/`ls`/`bash`), install [pi-tool-repair](https://github.com/monotykamary/pi-tool-repair) as a companion. It validates-then-repairs the finite set of tool-call mistakes those direct calls make — invented keys, wrong field names, stringified arrays, anchor bleed, and leaked tool-call grammars — before tools execute. It hooks `before_provider_request`/`message_end`/`tool_call`; fabric registers a tool, so the two do not conflict.
604
+
605
+ An external lever outside fabric's control is enabling Anthropic strict tool use at the provider, which prevents the server from sampling keys not in the schema. It is the strongest mitigation for schema drift but trades against Anthropic's complexity limits on strict tool definitions.
606
+
607
+ ## Security and limitations
608
+
609
+ - Pi Fabric invokes separately constructed Pi built-in definitions when no captured override exists. Those unoverridden built-in calls do not pass through Pi's top-level `tool_call` and `tool_result` hooks. Captured overrides and other extension calls do run those hooks; Fabric's approval and audit layer remains authoritative around every nested call.
610
+ - Captured tools execute with the full privileges of their owning extension. Hiding a tool schema is context optimization, not sandboxing. Captured tools retain their definitions and native renderers, but nested calls render as part of the enclosing Fabric execution rather than as separate native tool rows.
611
+ - Registry interception composes through the public `ExtensionRunner.getAllRegisteredTools()` method. An extension that replaces that method without delegating to the previous implementation can prevent capture.
612
+ - MCP servers and external providers execute with their own host privileges. Review their configuration and code.
613
+ - Type checking improves reliability but is not a security boundary; QuickJS isolation and the host capability bridge are the boundaries.
614
+ - Child Pi processes load normal extensions by default so provider-backed models continue to work. Their active tool list is restricted by `defaultTools`; `fabric_exec` is excluded unless recursion is explicitly requested.
615
+ - A Git worktree isolates files, not credentials, network access, processes, or external services.
616
+ - Background one-shot children are stopped when the parent Pi session shuts down. A detached `agents.spawn()` sends a follow-up completion message unless the caller later waits for it or `notifyOnComplete` is disabled. Completed worktrees are intentionally retained.
617
+ - Persistent actors are suspended on shutdown and restored for the same Pi session only when project trust is active. Their definitions, mailbox history, and child session files live under `.pi/fabric/mesh/actors/`; mesh topics and shared state are project-scoped. Do not place secrets in actor prompts, messages, or mesh state.
618
+ - Approving `agents.create()` delegates future subscribed events to that actor until it is stopped. Each activation uses the actor's fixed tool allowlist and model settings; review those settings before approving a persistent actor.
619
+ - Actor responses can enter the main context only through the delivery policy fixed at creation. Directive output is schema-validated, but it is still untrusted model output that the main agent should weigh.
620
+ - One Pi process should own a given session's restored actors at a time. Mesh topics are append-only and are not compacted automatically; archive or remove an old mesh root when its history is no longer useful.
621
+
622
+ ## Development
623
+
624
+ ```bash
625
+ pnpm install
626
+ pnpm typecheck
627
+ pnpm test
628
+ pnpm build
629
+ ```
630
+
631
+ The deterministic test suite covers configuration, schema validation, provider dispatch, registered-tool interception and execution, QuickJS isolation, Pi built-in invocation, direct-process subagents, workflow helpers, durable mesh state, actor mailboxes, subscriptions, and actor restoration.
632
+
633
+ ## License
634
+
635
+ MIT
@@ -0,0 +1,9 @@
1
+ # Third-party notices
2
+
3
+ Pi Fabric's QuickJS host-bridge design and TypeScript code-mode ergonomics were informed by the MIT-licensed [`boozedog/pi-codemode`](https://github.com/boozedog/pi-codemode) project.
4
+
5
+ Its subagent transport design was informed by the MIT-licensed [`sigilmakes/spindle`](https://github.com/sigilmakes/spindle), Pi's bundled subagent example, and LocalTerm's tmux-parity session API.
6
+
7
+ The workflow vocabulary was informed by Anthropic's dynamic workflows documentation and the MIT-licensed [`Michaelliv/pi-dynamic-workflows`](https://github.com/Michaelliv/pi-dynamic-workflows). The durable coordination model was informed by the MIT-licensed [`pi-messenger-swarm`](https://github.com/monotykamary/pi-messenger-swarm). Registered-tool interception follows the registry-chokepoint approach explored by the MIT-licensed [`pi-namespace`](https://github.com/monotykamary/pi-namespace) and [`pi-lazy-extensions`](https://github.com/monotykamary/pi-lazy-extensions) projects.
8
+
9
+ Pi Fabric uses the public cooperative rendering API from the MIT-licensed [`pi-code-previews`](https://github.com/mattleong/pi-code-previews) package.