pi-fabric 0.21.2 → 0.21.4
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/README.md +56 -681
- package/dist/activity/store.d.ts.map +1 -1
- package/dist/activity/store.js +3 -0
- package/dist/activity/store.js.map +1 -1
- package/dist/activity/types.d.ts +2 -0
- package/dist/activity/types.d.ts.map +1 -1
- package/dist/audit/projection.d.ts.map +1 -1
- package/dist/audit/projection.js +1 -9
- package/dist/audit/projection.js.map +1 -1
- package/dist/audit/trace.d.ts.map +1 -1
- package/dist/audit/trace.js.map +1 -1
- package/dist/core/action-registry.d.ts.map +1 -1
- package/dist/core/action-registry.js +7 -3
- package/dist/core/action-registry.js.map +1 -1
- package/dist/core/call-preview.d.ts.map +1 -1
- package/dist/core/call-preview.js +1 -0
- package/dist/core/call-preview.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +60 -8
- package/dist/index.js.map +1 -1
- package/dist/ui/dashboard.d.ts +2 -0
- package/dist/ui/dashboard.d.ts.map +1 -1
- package/dist/ui/dashboard.js +272 -74
- package/dist/ui/dashboard.js.map +1 -1
- package/dist/ui/fabric-render.d.ts +27 -1
- package/dist/ui/fabric-render.d.ts.map +1 -1
- package/dist/ui/fabric-render.js +217 -1
- package/dist/ui/fabric-render.js.map +1 -1
- package/dist/ui/snapshot.d.ts.map +1 -1
- package/dist/ui/snapshot.js +1 -0
- package/dist/ui/snapshot.js.map +1 -1
- package/dist/ui/structured.d.ts +2 -0
- package/dist/ui/structured.d.ts.map +1 -0
- package/dist/ui/structured.js +17 -0
- package/dist/ui/structured.js.map +1 -0
- package/dist/ui/types.d.ts +1 -0
- package/dist/ui/types.d.ts.map +1 -1
- package/dist/ui/types.js.map +1 -1
- package/dist/ui/widget.d.ts.map +1 -1
- package/dist/ui/widget.js +62 -14
- package/dist/ui/widget.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -19,733 +19,108 @@ _One type-checked program for tools, MCP, agents, workflows, actors, mesh, counc
|
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
22
|
-
Pi
|
|
22
|
+
You keep talking to Pi the way you always do. Fabric gives the model **one programmable tool** — `fabric_exec` — that it uses to compose Pi's core tools, MCP servers, captured extension tools, child agents, persistent actors, and durable coordination into a single type-checked TypeScript program. The program runs in a QuickJS sandbox; only the final result comes back to the conversation. Branching, loops, fan-out, and data flow become code the model writes and type-checks — not a stack of separate tool calls you have to orchestrate.
|
|
23
23
|
|
|
24
24
|
## Why Fabric?
|
|
25
25
|
|
|
26
26
|
| | Capability | What it unlocks |
|
|
27
27
|
| :-: | ---------- | --------------- |
|
|
28
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,
|
|
30
|
-
| 🧑🤝🧑 | **Agent runtime** |
|
|
31
|
-
| 🕸️ | **Workflows + mesh** |
|
|
32
|
-
| 🛡️ | **Guardrails** |
|
|
33
|
-
| 🎛️ | **Native TUI** |
|
|
29
|
+
| 🧰 | **Capability routing** | Call Pi core tools, MCP servers, captured extension tools, or Fabric providers through one runtime. |
|
|
30
|
+
| 🧑🤝🧑 | **Agent runtime** | One-shot workers, persistent event-driven actors, councils, and bounded recursive queries. |
|
|
31
|
+
| 🕸️ | **Workflows + mesh** | Phased progress plus durable topics, shared tasks, and compare-and-swap state. |
|
|
32
|
+
| 🛡️ | **Guardrails** | Approvals, isolation, timeouts, concurrency, recursion depth, and shared cost budgets. |
|
|
33
|
+
| 🎛️ | **Native TUI** | Live activity, an interactive dashboard, and settings without leaving Pi. |
|
|
34
34
|
|
|
35
|
-
##
|
|
36
|
-
|
|
37
|
-
From npm:
|
|
35
|
+
## How it works
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
```
|
|
37
|
+
1. **You ask** in plain language, as usual.
|
|
38
|
+
2. **Pi writes one program** that calls the tools, agents, and MCP servers it needs. The program is type-checked before it runs.
|
|
39
|
+
3. **Only the result returns** to your conversation. Intermediate work stays in the sandbox and surfaces in the activity panel and dashboard.
|
|
62
40
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
## Quick start
|
|
66
|
-
|
|
67
|
-
Ask Fabric to compose multiple operations in one call:
|
|
41
|
+
Under the hood, the model writes something like this — you don't:
|
|
68
42
|
|
|
69
43
|
```ts
|
|
70
44
|
const [manifest, sources] = await Promise.all([
|
|
71
45
|
pi.read({ path: "package.json" }),
|
|
72
46
|
pi.find({ pattern: "**/*.ts", path: "src" }),
|
|
73
47
|
]);
|
|
74
|
-
|
|
75
48
|
return {
|
|
76
49
|
package: JSON.parse(manifest).name,
|
|
77
50
|
sourceCount: sources.split("\n").filter(Boolean).length,
|
|
78
51
|
};
|
|
79
52
|
```
|
|
80
53
|
|
|
81
|
-
|
|
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
|
-
`runner` is `"pi"` or `"claude"` and defaults to `subagents.runner` (`"pi"`). Pi children use `subagents.model` or inherit the parent model unless `model` is specified. Claude children use `subagents.claude.model` or Claude Code's own runtime default. Their tool allowlist defaults to `subagents.defaultTools`. Reasoning effort defaults to `subagents.thinking` (`medium`); Pi clamps it to model support, while Claude forwards it through `--effort` (`off`/`minimal` map to `low`).
|
|
275
|
-
|
|
276
|
-
#### Claude Code runner
|
|
54
|
+
Independent calls run in parallel; only the returned object enters the model context.
|
|
277
55
|
|
|
278
|
-
Install
|
|
279
|
-
|
|
280
|
-
```ts
|
|
281
|
-
const models = await agents.models({ runner: "claude" });
|
|
282
|
-
const haiku = models.find((model) => model.key === "claude/haiku");
|
|
283
|
-
return agents.run({
|
|
284
|
-
runner: "claude",
|
|
285
|
-
model: haiku?.key,
|
|
286
|
-
task: "Review the current diff. Do not edit files.",
|
|
287
|
-
tools: ["read", "grep", "find", "ls"],
|
|
288
|
-
});
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
`agents.models({ runner: "claude" })` asks the installed CLI for its initialization model catalog, including aliases, resolved IDs, descriptions, and supported effort levels. The list is not hard-coded and the handshake sends no user prompt or model inference request, so model discovery itself is not billable. Because it launches the configured local binary, model-authored `agents.models` calls carry Fabric's `execute` risk. Fabric caches it for 60 seconds. Claude model keys use `claude/<runtime-value>` (for example `claude/default`, `claude/sonnet`, or `claude/haiku`); Fabric strips that namespace before `--model`.
|
|
292
|
-
|
|
293
|
-
Claude runs use `claude -p` with stream-JSON input/output, partial messages, `--permission-mode dontAsk`, and both `--tools` and `--allowedTools`. Fabric maps its portable core allowlist as follows:
|
|
294
|
-
|
|
295
|
-
| Fabric tool | Claude Code tool |
|
|
296
|
-
| ------------ | ---------------- |
|
|
297
|
-
| `read` | `Read` |
|
|
298
|
-
| `grep` | `Grep` |
|
|
299
|
-
| `find`, `ls` | `Glob` |
|
|
300
|
-
| `bash` | `Bash` |
|
|
301
|
-
| `edit` | `Edit` |
|
|
302
|
-
| `write` | `Write` |
|
|
303
|
-
|
|
304
|
-
Unknown tools fail before launch. `extensions: false` starts Claude in safe mode; the default `true` preserves the user's normal Claude Code customizations while the explicit tool list still controls model-facing tools. JSON schemas use Claude's native `--json-schema`; usage, cost, turns, tool activity, errors, and Claude's session ID are normalized into the ordinary Fabric result and dashboard transcript. One-shot runs add `--no-session-persistence`.
|
|
305
|
-
|
|
306
|
-
Claude-backed children are intentionally **not recursively Fabric-equipped**: `recursive: true`, `fabric_exec`, and direct `mesh.*` access are rejected. Use `runner: "pi"` for RLM/recursive Fabric, or use a Claude-backed persistent actor for host-managed mailbox/event coordination.
|
|
307
|
-
|
|
308
|
-
Supported transports:
|
|
309
|
-
|
|
310
|
-
| Transport | Behavior | Attach command |
|
|
311
|
-
| ----------- | ---------------------------------------------------------- | ---------------------------- |
|
|
312
|
-
| `process` | Detached local worker process; default and lowest overhead | none |
|
|
313
|
-
| `tmux` | One detached tmux session per child | `tmux attach-session -t …` |
|
|
314
|
-
| `screen` | One detached GNU Screen session per child | `screen -r …` |
|
|
315
|
-
| `localterm` | One pinned LocalTerm PTY per child | `localterm session attach …` |
|
|
316
|
-
| `auto` | Tries LocalTerm, tmux, screen, then process | transport-specific |
|
|
56
|
+
## Install
|
|
317
57
|
|
|
318
|
-
|
|
58
|
+
Requires Node.js 24+ and Pi 0.80.6+.
|
|
319
59
|
|
|
320
60
|
```bash
|
|
321
|
-
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
Use `/fabric agents` to list children and `/fabric attach <id>` to display the appropriate attach command. Abort signals propagate to the transport and selected child process. When a program uses orchestration entry points (`agent`/`workflow.agent`, `agents.run`/`agents.wait`/`agents.ask`, `council.run`, `rlm.query`)—including `agents.*` refs invoked through `tools.call()` and refs computed at runtime—Fabric raises the whole-program `executor.timeoutMs` to at least `subagents.timeoutMs`, so the parent deadline cannot stop children that are still within their own per-agent budget.
|
|
325
|
-
|
|
326
|
-
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.
|
|
327
|
-
|
|
328
|
-
### Steering running agents
|
|
329
|
-
|
|
330
|
-
Any Fabric-equipped agent can steer a running one-shot subagent **between its turns** instead of stopping and respawning it, preserving the child's accumulated context — mirroring Pi core's RPC `steer`/`follow_up` queue:
|
|
331
|
-
|
|
332
|
-
```ts
|
|
333
|
-
const handle = await agents.spawn({ task: "Audit auth flows.", tools: ["read", "grep", "find", "ls"] });
|
|
334
|
-
const s = await agents.status({ id: handle.id });
|
|
335
|
-
if (s.text.includes("rotating refresh tokens")) {
|
|
336
|
-
await agents.steer({ id: handle.id, message: "Skip refresh-token rotation; focus on session expiry only." });
|
|
337
|
-
await agents.setSteeringMode({ id: handle.id, mode: "all" });
|
|
338
|
-
}
|
|
339
|
-
return await agents.wait({ id: handle.id });
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
`agents.steer({ id, message })` is delivered after the current turn's tool calls, before the next LLM call; `agents.followUp({ id, message })` is delivered after the agent finishes; `agents.setSteeringMode`/`setFollowUpMode` set `"all"` vs `"one-at-a-time"` delivery. Pi uses its RPC queue; Claude uses additional user records on the same `claude -p` stream so the session and context are preserved. `agents.status({ id }).pendingMessages` shows the live queue. For an id not local to this process, `agents.steer` publishes a `fabric.steer` mesh event the owning process relays — so an agent in one Pi process can steer an agent in another. See `skills/fabric-exec/references/agents.md`.
|
|
343
|
-
|
|
344
|
-
### Persistent actors and ambient agents
|
|
345
|
-
|
|
346
|
-
`agents.create()` creates a named actor with a fixed runner, a persistent runner session, a serial mailbox, and optional subscriptions to parent-session events or durable mesh topics:
|
|
347
|
-
|
|
348
|
-
```ts
|
|
349
|
-
return agents.create({
|
|
350
|
-
name: "auth-supervisor",
|
|
351
|
-
instructions: `Watch the main session until the auth migration is complete and tested.
|
|
352
|
-
Prefer silence. Reply with a directive only for material drift, a blocker, or verified completion.`,
|
|
353
|
-
events: ["agent_settled", "tool_error"],
|
|
354
|
-
responseMode: "directive",
|
|
355
|
-
delivery: "steer",
|
|
356
|
-
triggerTurn: true,
|
|
357
|
-
thinking: "high",
|
|
358
|
-
tools: ["read", "grep", "find", "ls"],
|
|
359
|
-
});
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
A host-managed Claude actor uses the same mailbox and event surface while retaining Claude Code context across activations:
|
|
363
|
-
|
|
364
|
-
```ts
|
|
365
|
-
return agents.create({
|
|
366
|
-
name: "claude-reviewer",
|
|
367
|
-
runner: "claude",
|
|
368
|
-
model: "claude/haiku",
|
|
369
|
-
instructions: "Review each delivered event and report only concrete regressions.",
|
|
370
|
-
events: ["agent_settled", "tool_error"],
|
|
371
|
-
responseMode: "directive",
|
|
372
|
-
delivery: "steer",
|
|
373
|
-
tools: ["read", "grep", "find", "ls"],
|
|
374
|
-
});
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
Claude actors can retain context, inspect/edit with mapped Claude Code tools, consume host events and mesh messages delivered by Fabric, and return text or directives. They cannot themselves call `fabric_exec`, `agents.*`, or `mesh.*`; use a Pi actor when the actor must recursively coordinate through Fabric. If Claude's private session has been removed, the next activation fails clearly rather than silently discarding actor context. Recreate the actor to start a fresh Claude session.
|
|
378
|
-
|
|
379
|
-
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, and restore with the trusted project actor registry. Pi actors keep model context in their Fabric-owned Pi session file. Claude actors persist the session ID emitted by the official CLI, reapply tools/permissions/schema/system-prompt flags on every activation, and use `--resume <id>` after the first message; Fabric also keeps a runner-neutral stream transcript instead of reading Claude's private JSONL format. Each actor's reasoning effort is its `thinking` level (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`), defaulting to `subagents.thinking` (`medium`); set it at creation or change it later with `e` from the dashboard.
|
|
380
|
-
|
|
381
|
-
Two response modes are available:
|
|
382
|
-
|
|
383
|
-
- `text`: every non-empty response becomes an actor outbox message.
|
|
384
|
-
- `directive`: validated `{ action: "silent" | "message" | "stop", message?, data? }` output lets the actor decide whether intervention is useful.
|
|
385
|
-
|
|
386
|
-
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.
|
|
387
|
-
|
|
388
|
-
### Global actor templates
|
|
389
|
-
|
|
390
|
-
Persistent actors live in a project mesh, but a persona worth reusing across projects belongs in a project-independent **template library** stored in your agent dir (`~/.pi/agent/fabric/actors/`). Templates carry only an actor definition — name, instructions, subscriptions, and run settings — never any history (mailbox, session transcript, or run logs). They are not live; you stamp one into a project to make it run.
|
|
391
|
-
|
|
392
|
-
```ts
|
|
393
|
-
// Save a reusable persona to the global registry (not a live actor).
|
|
394
|
-
return agents.create({
|
|
395
|
-
name: "security-reviewer",
|
|
396
|
-
instructions: "Review changes for security defects. Reply with a directive only for material drift.",
|
|
397
|
-
events: ["agent_settled"],
|
|
398
|
-
responseMode: "directive",
|
|
399
|
-
scope: "global",
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
// List templates, then stamp one into the current project as a fresh actor.
|
|
403
|
-
const [template] = agents.actors({ scope: "global" });
|
|
404
|
-
return agents.import({ name: template.name }); // fresh: no inherited history
|
|
405
|
-
return agents.import({ name: "security-reviewer", as: "security-reviewer-2" }); // rename on collision
|
|
406
|
-
|
|
407
|
-
// Promote a tuned project actor back to the global library (no history).
|
|
408
|
-
return agents.export({ id: actorId, overwrite: true });
|
|
409
|
-
|
|
410
|
-
// Refine a template's default instruction (the persona / system-prompt body).
|
|
411
|
-
return agents.setInstructions({ id: template.id, instructions: "Be brief.", scope: "global" });
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
`agents.setInstructions` also edits a live project actor (`scope: "project"`, the default); the new instruction takes effect on the actor's next queued message. History never crosses the project⇄global boundary — import and export move only the definition. Slash commands mirror the API: `/fabric global` lists templates, `/fabric import <name> [as <new>]` stamps one into the project, and `/fabric export <id> [--overwrite]` promotes a project actor. The dashboard lists global templates alongside live actors and lets you import, export, delete, and edit instructions without writing code.
|
|
415
|
-
|
|
416
|
-
### Durable mesh coordination
|
|
417
|
-
|
|
418
|
-
The `mesh` API is a project-scoped, event-sourced coordination substrate:
|
|
419
|
-
|
|
420
|
-
```ts
|
|
421
|
-
const event = await mesh.publish({
|
|
422
|
-
topic: "team.auth",
|
|
423
|
-
kind: "finding",
|
|
424
|
-
text: "Refresh-token rotation is not atomic",
|
|
425
|
-
data: { path: "src/auth/refresh.ts" },
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
const task = await mesh.put({
|
|
429
|
-
key: "tasks/auth-review",
|
|
430
|
-
value: { status: "ready", owner: null },
|
|
431
|
-
ifVersion: 0,
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
const claimed = await mesh.put({
|
|
435
|
-
key: task.key,
|
|
436
|
-
value: { status: "claimed", owner: "security-reviewer" },
|
|
437
|
-
ifVersion: task.version,
|
|
438
|
-
});
|
|
439
|
-
return { event, claimed };
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
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.
|
|
443
|
-
|
|
444
|
-
### Councils
|
|
445
|
-
|
|
446
|
-
```ts
|
|
447
|
-
return council.run({
|
|
448
|
-
task: "Review the current implementation and recommend whether it is ready to merge.",
|
|
449
|
-
roles: ["correctness reviewer", "security reviewer", "test reviewer"],
|
|
450
|
-
transport: "localterm",
|
|
451
|
-
synthesize: true,
|
|
452
|
-
});
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
Council members run concurrently under the global subagent semaphore. With `synthesize: true`, a final child agent reconciles their reports.
|
|
456
|
-
|
|
457
|
-
### Recursive queries
|
|
458
|
-
|
|
459
|
-
```ts
|
|
460
|
-
return rlm.query({
|
|
461
|
-
runner: "pi",
|
|
462
|
-
task: "Recursively decompose this repository and produce a compact architecture map.",
|
|
463
|
-
transport: "process",
|
|
464
|
-
});
|
|
61
|
+
pi install npm:pi-fabric
|
|
465
62
|
```
|
|
466
63
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
`subagents.maxTokensPerChild` (0 = disabled) bounds each child's cumulative token usage. The wall-clock `timeoutMs` and the cost `budgetUsd` bound time and money; this bounds a single runaway child's context before the host session compacts, terminating it with the same `timed_out` status and a `token limit` error.
|
|
470
|
-
|
|
471
|
-
## Included skills
|
|
472
|
-
|
|
473
|
-
Pi discovers these package skills automatically:
|
|
474
|
-
|
|
475
|
-
| Command | Pattern |
|
|
476
|
-
| ---------------------------------- | ----------------------------------------------------------------------- |
|
|
477
|
-
| `/skill:fabric-supervisor <goal>` | Persistent goal watcher driven by `agent_settled` and tool-error events |
|
|
478
|
-
| `/skill:fabric-advisor [focus]` | Decision-point peer reviewer (idle and tool errors) that prefers silence |
|
|
479
|
-
| `/skill:fabric-ambient <role>` | Meta-pattern for custom event-driven ambient actors |
|
|
480
|
-
| `/skill:fabric-workflow <task>` | Code-held phases, fan-out, pipelines, structured output, and synthesis |
|
|
481
|
-
| `/skill:fabric-rlm <task>` | Recursive self-delegation via `rlm.query()` for tasks too big for one context window |
|
|
482
|
-
| `/skill:fabric-swarm <objective>` | Persistent actors, durable topics, and CAS-based shared tasks |
|
|
483
|
-
| `/skill:fabric-council <decision>` | Bounded independent perspectives plus synthesis |
|
|
484
|
-
| `/skill:fabric-fusion <task>` | Multi-model deliberation: parallel panel plus a compare-not-merge judge |
|
|
485
|
-
|
|
486
|
-
`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.
|
|
64
|
+
<details>
|
|
65
|
+
<summary>Other install methods</summary>
|
|
487
66
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
## Visual integration
|
|
491
|
-
|
|
492
|
-
`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.
|
|
493
|
-
|
|
494
|
-
Fabric also owns a general-purpose, theme-aware activity surface for any agent setup:
|
|
495
|
-
|
|
496
|
-
- 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.
|
|
497
|
-
- `/fabric dashboard` opens a responsive interactive overlay. Wide terminals use an activity-group pane beside agents and work items; narrow terminals stack the same panels. Explicit workflow phases remain distinct, while direct calls, items, and agents launched without a phase appear under **Run activity** instead of disappearing. Activity groups show agent count, token use, and elapsed time; agent rows prioritize attention and summarize current work, errors, or results. Agent detail includes task, model, current tool, usage, result, worktree, and attach metadata. Press Space on an agent for a live transcript peek, or press `t` in its detail to switch between summary and transcript. The bounded ring-buffer transcript renders Pi RPC and Claude stream-JSON assistant text with Pi's native Markdown treatment while keeping tool activity as compact one-line markers; common credential fields and token shapes are redacted from tool previews. Oldest activity rolls off the top, scrolling pauses follow mode, and `G` resumes it. One-shot agent model and thinking settings are fixed when spawned, but selected agents can be steered, given a queued follow-up, or safely stopped from the dashboard. Persistent actors are editable: select an actor to see the available shortcuts, then press `m` for its model, `e` for thinking (reasoning effort), `v` for host events, or `i` for its default instruction. Model/thinking changes persist to the actor registry and take effect on the actor's next run; the model picker uses Pi's registry for Pi actors and Claude's runtime catalog for Claude actors, and Inherit falls back to that runner's Fabric/runtime default. Actor mailboxes, mesh state, recent mesh events, and global actor templates use the same view rather than role-specific screens. Press `x` to export a project actor to the global library, `p` to import a global template as a fresh project actor, or `d` to delete a global template.
|
|
498
|
-
- `/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`. The Subagents section selects the default runner and keeps independent Pi and runtime-enumerated Claude model pickers. List editors for `subagents.defaultTools` and `capture.keepVisible` toggle known tools on and off; `keepVisible` candidates include `fabric_exec` plus every captured extension tool.
|
|
499
|
-
- `↑`/`↓` or `j`/`k` select, `←`/`→` or Tab switch panes, Enter drills into details, `f` cycles status filters, `[` selects an older retained run, `]` a newer one, and `?` opens contextual help. On one-shot agents, Space peeks at the live transcript, `t` toggles transcript/summary detail, `s` opens a steer editor, `u` queues a follow-up, and pressing `x` twice stops an active run. On actors, `m` changes the model, `e` thinking, `v` host events, and `i` instructions; `x` exports to global, `p` imports a global template, `d` deletes one, and Esc backs out or closes.
|
|
500
|
-
|
|
501
|
-
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:
|
|
502
|
-
|
|
503
|
-
```ts
|
|
504
|
-
await workflow.configure({ name: "Release train", description: "Build, verify, and publish" });
|
|
505
|
-
await phase("Build", { total: packages.length });
|
|
506
|
-
await workflow.item({
|
|
507
|
-
id: "docs",
|
|
508
|
-
label: "Documentation",
|
|
509
|
-
status: "running",
|
|
510
|
-
completed: 2,
|
|
511
|
-
total: 5,
|
|
512
|
-
});
|
|
513
|
-
await workflow.event({ message: "Canary passed", level: "success" });
|
|
514
|
-
```
|
|
67
|
+
From GitHub:
|
|
515
68
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
## Configuration
|
|
519
|
-
|
|
520
|
-
Pi Fabric reads:
|
|
521
|
-
|
|
522
|
-
1. `~/.pi/agent/fabric.json`
|
|
523
|
-
2. `<project>/.pi/fabric.json`, only for trusted projects
|
|
524
|
-
|
|
525
|
-
Project values override global values.
|
|
526
|
-
|
|
527
|
-
```json
|
|
528
|
-
{
|
|
529
|
-
"fullCodeMode": true,
|
|
530
|
-
"executor": {
|
|
531
|
-
"timeoutMs": 120000,
|
|
532
|
-
"memoryLimitBytes": 67108864,
|
|
533
|
-
"maxOutputChars": 100000,
|
|
534
|
-
"maxNestedResultChars": 2000000
|
|
535
|
-
},
|
|
536
|
-
"approvals": {
|
|
537
|
-
"read": "allow",
|
|
538
|
-
"write": "allow",
|
|
539
|
-
"execute": "allow",
|
|
540
|
-
"network": "allow",
|
|
541
|
-
"agent": "allow"
|
|
542
|
-
},
|
|
543
|
-
"capture": {
|
|
544
|
-
"enabled": true,
|
|
545
|
-
"hideFromModel": true,
|
|
546
|
-
"keepVisible": ["fabric_exec"],
|
|
547
|
-
"defaultRisk": "execute",
|
|
548
|
-
"risks": {
|
|
549
|
-
"read": "read",
|
|
550
|
-
"grep": "read",
|
|
551
|
-
"find": "read",
|
|
552
|
-
"ls": "read",
|
|
553
|
-
"edit": "write",
|
|
554
|
-
"write": "write",
|
|
555
|
-
"bash": "execute"
|
|
556
|
-
}
|
|
557
|
-
},
|
|
558
|
-
"mcp": {
|
|
559
|
-
"enabled": true,
|
|
560
|
-
"disableOAuth": true,
|
|
561
|
-
"allowDynamicServers": true,
|
|
562
|
-
"callTimeoutMs": 120000
|
|
563
|
-
},
|
|
564
|
-
"subagents": {
|
|
565
|
-
"enabled": true,
|
|
566
|
-
"runner": "pi",
|
|
567
|
-
"transport": "process",
|
|
568
|
-
"claude": {
|
|
569
|
-
"binary": "claude"
|
|
570
|
-
},
|
|
571
|
-
"thinking": "medium",
|
|
572
|
-
"maxConcurrent": 4,
|
|
573
|
-
"maxPerExecution": 100,
|
|
574
|
-
"maxDepth": 2,
|
|
575
|
-
"timeoutMs": 3600000,
|
|
576
|
-
"extensions": true,
|
|
577
|
-
"defaultTools": ["read", "bash", "edit", "write", "grep", "find", "ls"],
|
|
578
|
-
"retainRuns": false,
|
|
579
|
-
"notifyOnComplete": true,
|
|
580
|
-
"budgetUsd": 0,
|
|
581
|
-
"maxTokensPerChild": 0
|
|
582
|
-
},
|
|
583
|
-
"ui": {
|
|
584
|
-
"enabled": true,
|
|
585
|
-
"widget": "auto",
|
|
586
|
-
"maxRows": 6,
|
|
587
|
-
"refreshMs": 500,
|
|
588
|
-
"lingerMs": 10000,
|
|
589
|
-
"eventHistory": 80
|
|
590
|
-
},
|
|
591
|
-
"compaction": {
|
|
592
|
-
"engine": "fabric"
|
|
593
|
-
},
|
|
594
|
-
"mesh": {
|
|
595
|
-
"enabled": true,
|
|
596
|
-
"actorScope": "project",
|
|
597
|
-
"maxEventBytes": 262144,
|
|
598
|
-
"maxReadEvents": 500,
|
|
599
|
-
"actorPollMs": 250,
|
|
600
|
-
"actorQueueLimit": 32,
|
|
601
|
-
"eventContextChars": 40000
|
|
602
|
-
}
|
|
603
|
-
}
|
|
69
|
+
```bash
|
|
70
|
+
pi install git:github.com/monotykamary/pi-fabric
|
|
604
71
|
```
|
|
605
72
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
`subagents.runner` selects the default harness (`"pi"` or `"claude"`). `subagents.model` is the optional Pi `provider/id` override; `subagents.claude.model` is the optional canonical Claude runtime key. `subagents.claude.binary` defaults to `claude` and can be an absolute path or wrapper; `PI_FABRIC_CLAUDE_BINARY` overrides it for the current process. `/fabric settings` enumerates Claude models from that binary and stores the two runner defaults independently.
|
|
609
|
-
|
|
610
|
-
`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.
|
|
611
|
-
|
|
612
|
-
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.
|
|
613
|
-
|
|
614
|
-
When `mcp.disableOAuth` is true, MCP calls may use cached credentials but cannot launch a new interactive OAuth flow.
|
|
615
|
-
|
|
616
|
-
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.
|
|
617
|
-
|
|
618
|
-
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.
|
|
619
|
-
|
|
620
|
-
`mesh.actorScope` controls where persistent actor definitions, mailboxes, and child sessions are stored and restored from. The default `"project"` keeps a single shared actor registry at `.pi/fabric/mesh/actors/`, so actors survive `/new` and carry over between Pi sessions in the same project without redefinition. Set it to `"session"` to isolate actors per Pi session (under `.pi/fabric/mesh/actors/<sessionId>/`), the previous default; use this when you run concurrent Pi sessions in one project and want each to own its own actors. With project scope, one Pi process should own the actor registry at a time — concurrent sessions sharing a registry may race on writes.
|
|
621
|
-
|
|
622
|
-
## External provider protocol
|
|
623
|
-
|
|
624
|
-
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:
|
|
73
|
+
From a local checkout:
|
|
625
74
|
|
|
626
|
-
```
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
FABRIC_PROVIDER_REGISTER_EVENT,
|
|
631
|
-
type FabricProvider,
|
|
632
|
-
type FabricProviderDiscovery,
|
|
633
|
-
} from "pi-fabric/protocol";
|
|
634
|
-
|
|
635
|
-
export default function extension(pi: ExtensionAPI) {
|
|
636
|
-
const provider: FabricProvider = {
|
|
637
|
-
name: "example",
|
|
638
|
-
description: "Example actions",
|
|
639
|
-
async list() {
|
|
640
|
-
return [];
|
|
641
|
-
},
|
|
642
|
-
async describe() {
|
|
643
|
-
return undefined;
|
|
644
|
-
},
|
|
645
|
-
async invoke() {
|
|
646
|
-
return null;
|
|
647
|
-
},
|
|
648
|
-
};
|
|
649
|
-
|
|
650
|
-
pi.events.emit(FABRIC_PROVIDER_REGISTER_EVENT, {
|
|
651
|
-
version: 1,
|
|
652
|
-
provider,
|
|
653
|
-
overwrite: true,
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
pi.events.on(FABRIC_PROVIDER_DISCOVER_EVENT, (event: FabricProviderDiscovery) => {
|
|
657
|
-
event.register(provider, { overwrite: true });
|
|
658
|
-
});
|
|
659
|
-
}
|
|
75
|
+
```bash
|
|
76
|
+
pnpm install
|
|
77
|
+
pnpm build
|
|
78
|
+
pi install /absolute/path/to/pi-fabric
|
|
660
79
|
```
|
|
661
80
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
```ts
|
|
665
|
-
async invoke(actionName, args, context) {
|
|
666
|
-
context.activity?.({ type: "entity", id: job.id, kind: "custom", name: job.name });
|
|
667
|
-
context.activity?.({ type: "progress", message: "Indexing package 3/12" });
|
|
668
|
-
context.activity?.({ type: "metrics", tokens: 4200, toolCalls: 9 });
|
|
669
|
-
return job.result;
|
|
670
|
-
}
|
|
671
|
-
```
|
|
81
|
+
For one development run:
|
|
672
82
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
```text
|
|
676
|
-
/fabric status
|
|
677
|
-
/fabric dashboard
|
|
678
|
-
/fabric settings
|
|
679
|
-
/fabric reload
|
|
680
|
-
/fabric providers
|
|
681
|
-
/fabric captured [query]
|
|
682
|
-
/fabric agents
|
|
683
|
-
/fabric actors
|
|
684
|
-
/fabric messages <actor-id>
|
|
685
|
-
/fabric attach <subagent-id>
|
|
686
|
-
/fabric stop <actor-or-subagent-id>
|
|
83
|
+
```bash
|
|
84
|
+
pi -e /absolute/path/to/pi-fabric
|
|
687
85
|
```
|
|
688
86
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
Pi already runs one-shot, non-interactive agents with `pi -p` (`--print`), and it reads piped stdin as part of the prompt — so a focused agent composes with pipes, cron, git hooks, and CI like a Unix program, with no wrapper needed:
|
|
87
|
+
</details>
|
|
692
88
|
|
|
693
|
-
|
|
694
|
-
git diff | pi -p --no-session -t read,grep "Review this diff for concrete defects."
|
|
695
|
-
pi -p --no-session --mode json -e <path-to-pi-fabric> "Map the persistence layer."
|
|
696
|
-
```
|
|
89
|
+
## What you can ask for
|
|
697
90
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
## Architecture
|
|
701
|
-
|
|
702
|
-
```text
|
|
703
|
-
fabric_exec
|
|
704
|
-
│
|
|
705
|
-
▼
|
|
706
|
-
TypeScript checker → QuickJS sandbox
|
|
707
|
-
│ JSON-only host bridge
|
|
708
|
-
▼
|
|
709
|
-
ActionRegistry
|
|
710
|
-
├── pi.* built-in Pi tool definitions
|
|
711
|
-
├── extensions.* captured pi.registerTool definitions
|
|
712
|
-
├── mcp.* pooled mcporter runtime
|
|
713
|
-
├── agents.* one-shot workers + persistent mailbox actors
|
|
714
|
-
├── mesh.* durable topics + compare-and-swap state
|
|
715
|
-
└── external explicit pi.events providers
|
|
716
|
-
|
|
717
|
-
ActivityStore → compact widget + footer status + interactive dashboard
|
|
718
|
-
```
|
|
91
|
+
Every pattern below is a skill Pi loads on demand. Invoke it with `/skill:<name>`, or just describe the task and let Pi pick it up.
|
|
719
92
|
|
|
720
|
-
|
|
93
|
+
| You want | Ask for | Skill |
|
|
94
|
+
| -------- | ------- | ----- |
|
|
95
|
+
| Parallel audits, migrations, or research with phased progress and a final synthesis | “Audit every auth file in parallel and synthesize findings.” | `/skill:fabric-workflow` |
|
|
96
|
+
| Work too big for one context window, decomposed recursively | “Produce a compact architecture map of this repo.” | `/skill:fabric-rlm` |
|
|
97
|
+
| A persistent watcher that steers only when you drift | “Watch this migration until it's complete and tested.” | `/skill:fabric-supervisor` |
|
|
98
|
+
| A quiet decision-point reviewer | “Review my decisions at idle and tool-error points.” | `/skill:fabric-advisor` |
|
|
99
|
+
| Several reviewers reconciled into one verdict | “Run correctness, security, and test reviewers, then merge.” | `/skill:fabric-council` |
|
|
100
|
+
| Multi-model deliberation with a compare-not-merge judge | “Deliberate this design across models.” | `/skill:fabric-fusion` |
|
|
101
|
+
| A durable team coordinating through shared tasks | “Stand up a team that claims tasks atomically and reports progress.” | `/skill:fabric-swarm` |
|
|
102
|
+
| Edits gated behind typed evidence and postconditions | “Make this parser change only if focused tests stay green.” | `/skill:fabric-schema` |
|
|
721
103
|
|
|
722
|
-
|
|
104
|
+
The foundation is the `fabric-exec` reference skill: the model loads it before its first `fabric_exec` call and again when a call errors on argument shape.
|
|
723
105
|
|
|
724
|
-
|
|
106
|
+
## The dashboard
|
|
725
107
|
|
|
726
|
-
|
|
108
|
+
Fabric adds a live activity surface to Pi, no extra extension required:
|
|
727
109
|
|
|
728
|
-
|
|
110
|
+
- A compact widget above the chat (like `pi-supervisor`) that follows the current phase and shows one priority activity at a time.
|
|
111
|
+
- `/fabric dashboard` — a phase sidebar, per-agent detail with live transcripts, and controls to steer, queue follow-ups, or stop runs.
|
|
112
|
+
- `/fabric settings` — mirrors Pi's `/settings` and writes changes to `fabric.json`.
|
|
729
113
|
|
|
730
|
-
|
|
114
|
+
See the [interface & commands reference](docs/interface.md) for every view, keybinding, and slash command.
|
|
731
115
|
|
|
732
|
-
##
|
|
116
|
+
## Reference
|
|
733
117
|
|
|
734
|
-
-
|
|
735
|
-
-
|
|
736
|
-
-
|
|
737
|
-
-
|
|
738
|
-
-
|
|
739
|
-
-
|
|
740
|
-
- Claude `extensions: true` preserves the user's normal Claude Code customizations, including applicable settings and hooks; those hooks execute with their usual host privileges. Use `extensions: false` for Claude safe mode. `Bash` remains unrestricted inside the child when allowed, just as Fabric's `bash` capability is.
|
|
741
|
-
- Claude model discovery uses a local initialization control request and does not invoke a model. Actual one-shot and actor activations use the account/API billing already configured in Claude Code; Fabric records the CLI's reported `total_cost_usd` in normal usage and budget ledgers.
|
|
742
|
-
- A Git worktree isolates files, not credentials, network access, processes, or external services.
|
|
743
|
-
- Agent transcripts are projected from local `events.jsonl` run logs. The dashboard redacts common credentials from compact tool previews, but the permission-restricted raw event log can contain assistant text, tool arguments/results, diagnostics, and extension protocol payloads; treat retained run directories as sensitive.
|
|
744
|
-
- 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.
|
|
745
|
-
- Persistent actors are suspended on shutdown and restored when project trust is active. Claude actor session IDs refer to Claude Code's own persisted session store; removing that private session makes resume fail, and removing a Fabric actor does not currently delete Claude Code's private transcript. By default (`mesh.actorScope: "project"`), their definitions, mailbox history, and child session files live under `.pi/fabric/mesh/actors/` and are shared across all Pi sessions in the project, so actors survive `/new`. Set `mesh.actorScope: "session"` to isolate actors per Pi session instead. Mesh topics and shared state are always project-scoped. Do not place secrets in actor prompts, messages, or mesh state.
|
|
746
|
-
- Approving `agents.create()` delegates future subscribed events to that actor until it is stopped. Each activation uses the actor's fixed runner/tool allowlist and its persisted model setting; review them before approving a persistent actor.
|
|
747
|
-
- 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.
|
|
748
|
-
- One Pi process should own the actor registry at a time. This is especially important with project scope, where concurrent Pi sessions in the same project share one registry and may race on writes. Mesh topics are append-only and are not compacted automatically; archive or remove an old mesh root when its history is no longer useful.
|
|
118
|
+
- [Configuration](docs/configuration.md) — `fabric.json`, code modes, tool capture, approvals, and budgets.
|
|
119
|
+
- [Interface & commands](docs/interface.md) — dashboard, settings, keybindings, slash commands, and headless runs.
|
|
120
|
+
- [Agents, actors & mesh](docs/agents.md) — subagents, the Claude runner, transports, steering, persistent actors, global templates, councils, recursive queries, and durable coordination.
|
|
121
|
+
- [External providers](docs/providers.md) — the versioned provider protocol for extensions.
|
|
122
|
+
- [Architecture & security](docs/architecture.md) — the host bridge, sandboxing, tool-call robustness, and limitations.
|
|
123
|
+
- [Skills](skills/) — the model-invoked patterns and the full `fabric_exec` API reference.
|
|
749
124
|
|
|
750
125
|
## Development
|
|
751
126
|
|
|
@@ -756,7 +131,7 @@ pnpm test
|
|
|
756
131
|
pnpm build
|
|
757
132
|
```
|
|
758
133
|
|
|
759
|
-
The
|
|
134
|
+
The test suite covers configuration, schema validation, provider dispatch, registered-tool interception and execution, QuickJS isolation, Pi built-in invocation, subagents, fake Claude stream-JSON and model discovery, workflows, durable mesh state, actor mailboxes and subscriptions, and Pi/Claude actor restoration. Claude fixtures never make a billable request.
|
|
760
135
|
|
|
761
136
|
## License
|
|
762
137
|
|