pi-fabric 0.74.0 → 0.76.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 (89) hide show
  1. package/README.md +1 -0
  2. package/dist/actors/manager.d.ts.map +1 -1
  3. package/dist/agents/transports/process-utils.d.ts.map +1 -1
  4. package/dist/chunks/chunk-3SGRTVFP.js +60 -0
  5. package/dist/chunks/chunk-3SGRTVFP.js.map +7 -0
  6. package/dist/chunks/{chunk-ASUT6BU4.js → chunk-65RRHNBX.js} +7 -1
  7. package/dist/chunks/chunk-65RRHNBX.js.map +7 -0
  8. package/dist/chunks/{chunk-6F6YQ2DS.js → chunk-6BBFFEPO.js} +8 -8
  9. package/dist/chunks/{chunk-SJYOKQJT.js → chunk-6ZKYQW33.js} +4 -4
  10. package/dist/chunks/{chunk-NLY3MSQJ.js → chunk-BOIV6E3C.js} +5 -5
  11. package/dist/chunks/chunk-BT6UELLP.js +665 -0
  12. package/dist/chunks/chunk-BT6UELLP.js.map +7 -0
  13. package/dist/chunks/{chunk-M5ANI67T.js → chunk-F2GKEXGI.js} +20 -30
  14. package/dist/chunks/chunk-F2GKEXGI.js.map +7 -0
  15. package/dist/chunks/chunk-FRHBI5QB.js +87 -0
  16. package/dist/chunks/chunk-FRHBI5QB.js.map +7 -0
  17. package/dist/chunks/{chunk-YYODET6T.js → chunk-NI3ZXJZD.js} +4 -4
  18. package/dist/chunks/{chunk-GLEX5U5Q.js → chunk-QWIKS6CT.js} +3 -3
  19. package/dist/chunks/chunk-QWIKS6CT.js.map +7 -0
  20. package/dist/chunks/{chunk-6NYBEXUT.js → chunk-QXB75FLD.js} +32 -85
  21. package/dist/chunks/chunk-QXB75FLD.js.map +7 -0
  22. package/dist/chunks/{chunk-5OKJP5PD.js → chunk-USMTYVXY.js} +8 -8
  23. package/dist/chunks/chunk-USMTYVXY.js.map +7 -0
  24. package/dist/chunks/chunk-X2MKXKDA.js +17 -0
  25. package/dist/chunks/chunk-X2MKXKDA.js.map +7 -0
  26. package/dist/chunks/{core-override-guest-types-SENPG5IT.js → core-override-guest-types-ADPUUQWL.js} +2 -2
  27. package/dist/chunks/{dashboard-2RGAN2WT.js → dashboard-EEJW66FA.js} +4 -4
  28. package/dist/chunks/{fabric-runtime-state-FBDHAKPF.js → fabric-runtime-state-FBAJTXB2.js} +148 -94
  29. package/dist/chunks/fabric-runtime-state-FBAJTXB2.js.map +7 -0
  30. package/dist/chunks/{guest-types-NCGNPMCZ.js → guest-types-2P2LKCZE.js} +2 -2
  31. package/dist/chunks/{node-process-runtime-AFV55TEV.js → node-process-runtime-ITTLVGWY.js} +8 -8
  32. package/dist/chunks/{quickjs-runtime-C75KPH77.js → quickjs-runtime-6LAQLSB4.js} +3 -3
  33. package/dist/chunks/{settings-ISH7S23I.js → settings-VEXI347I.js} +4 -3
  34. package/dist/chunks/{settings-ISH7S23I.js.map → settings-VEXI347I.js.map} +1 -1
  35. package/dist/core/action-registry.js +2 -2
  36. package/dist/core/system-guidance.d.ts.map +1 -1
  37. package/dist/fabric-exec-arguments.d.ts +4 -0
  38. package/dist/fabric-exec-arguments.d.ts.map +1 -1
  39. package/dist/fabric-exec-tool.d.ts.map +1 -1
  40. package/dist/index.js +104 -50
  41. package/dist/index.js.map +2 -2
  42. package/dist/memory/search.js +2 -2
  43. package/dist/providers/agents-provider.d.ts.map +1 -1
  44. package/dist/providers/memory-provider.js +3 -3
  45. package/dist/residency/actor-client.d.ts +16 -0
  46. package/dist/residency/actor-client.d.ts.map +1 -0
  47. package/dist/residency/actor-client.js +9 -0
  48. package/dist/residency/actor-client.js.map +7 -0
  49. package/dist/residency/client.d.ts.map +1 -1
  50. package/dist/residency/host.d.ts +1 -24
  51. package/dist/residency/host.d.ts.map +1 -1
  52. package/dist/residency/host.js +11 -644
  53. package/dist/residency/host.js.map +3 -3
  54. package/dist/residency/launcher-owner.d.ts +7 -0
  55. package/dist/residency/launcher-owner.d.ts.map +1 -0
  56. package/dist/residency/launcher.d.ts +3 -0
  57. package/dist/residency/launcher.d.ts.map +1 -0
  58. package/dist/residency/launcher.js +154 -0
  59. package/dist/residency/launcher.js.map +7 -0
  60. package/dist/residency/pi-entry.d.ts +3 -0
  61. package/dist/residency/pi-entry.d.ts.map +1 -0
  62. package/dist/residency/pi-entry.js +41 -0
  63. package/dist/residency/pi-entry.js.map +7 -0
  64. package/dist/residency/protocol.d.ts +11 -1
  65. package/dist/residency/protocol.d.ts.map +1 -1
  66. package/dist/runtime/guest-types.d.ts +1 -1
  67. package/dist/runtime/guest-types.d.ts.map +1 -1
  68. package/dist/runtime/quickjs-runtime.d.ts +1 -1
  69. package/dist/runtime/quickjs-runtime.d.ts.map +1 -1
  70. package/dist/type-error-guidance.d.ts.map +1 -1
  71. package/docs/architecture.md +1 -1
  72. package/docs/residency-runtime.md +82 -0
  73. package/package.json +1 -1
  74. package/skills/fabric-exec/SKILL.md +1 -1
  75. package/dist/chunks/chunk-5OKJP5PD.js.map +0 -7
  76. package/dist/chunks/chunk-6NYBEXUT.js.map +0 -7
  77. package/dist/chunks/chunk-ASUT6BU4.js.map +0 -7
  78. package/dist/chunks/chunk-GLEX5U5Q.js.map +0 -7
  79. package/dist/chunks/chunk-M5ANI67T.js.map +0 -7
  80. package/dist/chunks/fabric-runtime-state-FBDHAKPF.js.map +0 -7
  81. /package/dist/chunks/{chunk-6F6YQ2DS.js.map → chunk-6BBFFEPO.js.map} +0 -0
  82. /package/dist/chunks/{chunk-SJYOKQJT.js.map → chunk-6ZKYQW33.js.map} +0 -0
  83. /package/dist/chunks/{chunk-NLY3MSQJ.js.map → chunk-BOIV6E3C.js.map} +0 -0
  84. /package/dist/chunks/{chunk-YYODET6T.js.map → chunk-NI3ZXJZD.js.map} +0 -0
  85. /package/dist/chunks/{core-override-guest-types-SENPG5IT.js.map → core-override-guest-types-ADPUUQWL.js.map} +0 -0
  86. /package/dist/chunks/{dashboard-2RGAN2WT.js.map → dashboard-EEJW66FA.js.map} +0 -0
  87. /package/dist/chunks/{guest-types-NCGNPMCZ.js.map → guest-types-2P2LKCZE.js.map} +0 -0
  88. /package/dist/chunks/{node-process-runtime-AFV55TEV.js.map → node-process-runtime-ITTLVGWY.js.map} +0 -0
  89. /package/dist/chunks/{quickjs-runtime-C75KPH77.js.map → quickjs-runtime-6LAQLSB4.js.map} +0 -0
@@ -0,0 +1,665 @@
1
+ import {
2
+ ActorManager,
3
+ AgentManager,
4
+ FabricControlPlane,
5
+ LifecycleBroker,
6
+ MeshStore,
7
+ ParticipantDirectory,
8
+ actorParticipantRecord,
9
+ agentParticipantRecords,
10
+ lifecycleSourceIdentity,
11
+ useBudgetLedger
12
+ } from "./chunk-F2GKEXGI.js";
13
+ import {
14
+ parseFabricOwnedModelGuidance,
15
+ resolveFabricModelGuidance
16
+ } from "./chunk-2YCOXT4W.js";
17
+ import {
18
+ RESIDENT_HOST_FORMAT,
19
+ residentDeliveryPrefix,
20
+ residentHostId
21
+ } from "./chunk-X2MKXKDA.js";
22
+ import {
23
+ writeJsonAtomic
24
+ } from "./chunk-3SGRTVFP.js";
25
+
26
+ // src/residency/host.ts
27
+ import { randomUUID } from "node:crypto";
28
+ import fs from "node:fs";
29
+ import path from "node:path";
30
+ import { fileURLToPath } from "node:url";
31
+ var REQUEST_POLL_MS = 50;
32
+ var IDLE_EXIT_MS = 3e4;
33
+ var COMPLETION_MAX_CHARS = 8e3;
34
+ var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
35
+ var atomicWrite = (filePath, value) => {
36
+ writeJsonAtomic(filePath, value, { space: 2 });
37
+ };
38
+ var readJson = (filePath) => {
39
+ try {
40
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
41
+ } catch {
42
+ return void 0;
43
+ }
44
+ };
45
+ var errorMessage = (error) => error instanceof Error ? error.message : String(error);
46
+ var processAlive = (pid) => {
47
+ if (!Number.isSafeInteger(pid) || pid <= 0) return false;
48
+ try {
49
+ process.kill(pid, 0);
50
+ return true;
51
+ } catch (error) {
52
+ return error instanceof Error && "code" in error && error.code === "EPERM";
53
+ }
54
+ };
55
+ var ResidentHostAlreadyRunning = class extends Error {
56
+ };
57
+ var parseResidentHostConfigPath = (argv) => {
58
+ const index = argv.indexOf("--config");
59
+ const value = index >= 0 ? argv[index + 1] : void 0;
60
+ if (!value) throw new Error("Missing resident host argument: --config");
61
+ return path.resolve(value);
62
+ };
63
+ var validateResidentHostConfig = (value, configPath) => {
64
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
65
+ throw new Error("Invalid Fabric resident host config");
66
+ }
67
+ const config = value;
68
+ if (config.format !== RESIDENT_HOST_FORMAT || typeof config.rootId !== "string" || typeof config.sessionId !== "string" || typeof config.cwd !== "string" || typeof config.projectRoot !== "string" || typeof config.meshRoot !== "string" || typeof config.actorRoot !== "string" || typeof config.residencyRoot !== "string" || typeof config.fullCodeMode !== "boolean" || typeof config.agents !== "object" || config.agents === null || typeof config.mesh !== "object" || config.mesh === null || typeof config.retention !== "object" || config.retention === null || typeof config.workerPath !== "string" || typeof config.fabricExtensionPath !== "string" || typeof config.piBinary !== "string" || typeof config.claudeBinary !== "string" || typeof config.vedaBinary !== "string") {
69
+ throw new Error("Fabric resident host config is incomplete");
70
+ }
71
+ if (path.resolve(config.residencyRoot) !== path.dirname(configPath)) {
72
+ throw new Error("Fabric resident host config is outside its residency root");
73
+ }
74
+ if (!config.mesh.enabled) throw new Error("Durable residency requires the Fabric mesh");
75
+ return config;
76
+ };
77
+ var ResidentHost = class {
78
+ constructor(config, onIdle = () => {
79
+ }) {
80
+ this.config = config;
81
+ this.onIdle = onIdle;
82
+ this.hostId = residentHostId(config.rootId);
83
+ this.identity = { id: this.hostId, name: "Fabric resident host", kind: "agent" };
84
+ this.#ownerPath = path.join(config.residencyRoot, "owner.json");
85
+ this.#lockPath = path.join(config.residencyRoot, "host.lock");
86
+ this.#errorPath = path.join(config.residencyRoot, "error.json");
87
+ this.#requestsPath = path.join(config.residencyRoot, "requests");
88
+ this.#processingPath = path.join(config.residencyRoot, "processing");
89
+ this.#responsesPath = path.join(config.residencyRoot, "responses");
90
+ this.#agentsPath = path.join(config.residencyRoot, "agents");
91
+ this.#deliveryPrefix = residentDeliveryPrefix(config.rootId);
92
+ this.mesh = new MeshStore(config.meshRoot, config.mesh.maxEventBytes, config.mesh.maxReadEvents);
93
+ this.participants = new ParticipantDirectory(this.mesh, {
94
+ enabled: true,
95
+ hostId: this.hostId,
96
+ rootId: config.rootId,
97
+ identity: this.identity
98
+ });
99
+ this.control = new FabricControlPlane(this.mesh, this.identity, {
100
+ enabled: true,
101
+ hostId: this.hostId,
102
+ pollMs: config.mesh.actorPollMs
103
+ });
104
+ if (config.agents.budgetUsd > 0) {
105
+ const budgetFile = path.join(config.residencyRoot, "budget.jsonl");
106
+ fs.mkdirSync(path.dirname(budgetFile), { recursive: true, mode: 448 });
107
+ if (!fs.existsSync(budgetFile)) fs.writeFileSync(budgetFile, "", { mode: 384 });
108
+ useBudgetLedger({
109
+ budget: config.agents.budgetUsd,
110
+ file: budgetFile,
111
+ id: this.hostId
112
+ });
113
+ }
114
+ const guidanceConfigPath = path.join(config.residencyRoot, "config.json");
115
+ const currentModelGuidance = () => {
116
+ const current = readJson(guidanceConfigPath);
117
+ return parseFabricOwnedModelGuidance(current?.modelGuidance ?? config.modelGuidance);
118
+ };
119
+ this.agents = new AgentManager(config.cwd, config.agents, {
120
+ workerPath: config.workerPath,
121
+ fabricExtensionPath: config.fabricExtensionPath,
122
+ piBinary: config.piBinary,
123
+ claudeBinary: config.claudeBinary,
124
+ vedaBinary: config.vedaBinary,
125
+ runRoot: path.join(config.residencyRoot, "runs"),
126
+ fullCodeMode: config.fullCodeMode,
127
+ mainAgentId: config.rootId,
128
+ meshRoot: config.meshRoot,
129
+ projectRoot: config.projectRoot,
130
+ hostId: this.hostId,
131
+ identityId: this.identity.id,
132
+ retention: config.retention,
133
+ resolveParticipantGuidance: ({ model }) => {
134
+ if (!model) return void 0;
135
+ return resolveFabricModelGuidance(currentModelGuidance(), {
136
+ model,
137
+ target: "participant",
138
+ includeSlots: false
139
+ }).appendText || void 0;
140
+ },
141
+ onLifecycle: (event) => void this.lifecycle?.publish(event).catch(() => void 0),
142
+ onBackgroundComplete: (result) => {
143
+ if (!config.agents.notifyOnComplete) return;
144
+ const durationMs = Math.max(0, (result.finishedAt ?? Date.now()) - result.startedAt);
145
+ const summary = (result.text || result.error || "no result").slice(0, COMPLETION_MAX_CHARS);
146
+ void this.#queueDelivery(
147
+ { id: result.id, name: result.name, kind: "agent" },
148
+ `Fabric agent ${result.id.slice(0, 8)} ${result.status} after ${Math.round(durationMs / 1e3)}s: ${summary}`,
149
+ "followUp",
150
+ true,
151
+ result
152
+ ).catch(() => void 0);
153
+ }
154
+ });
155
+ const canManageActor = (id) => {
156
+ const participant = this.participants.get(id);
157
+ return participant ? participant.ownerHostId === this.hostId : void 0;
158
+ };
159
+ const lineageAlive = (rootId) => this.participants.get(rootId) !== void 0;
160
+ this.actors = new ActorManager(
161
+ config.sessionId,
162
+ this.identity,
163
+ this.mesh,
164
+ config.mesh,
165
+ this.agents,
166
+ ({ actor, message, delivery, triggerTurn }) => {
167
+ if (!message.text) return;
168
+ const mode = delivery === "steer" ? "steer" : "followUp";
169
+ const triggers = delivery === "nextTurn" ? false : triggerTurn;
170
+ void this.#queueDelivery(
171
+ { id: actor.id, name: actor.name, kind: "actor" },
172
+ message.text,
173
+ mode,
174
+ triggers,
175
+ message.data
176
+ ).catch(() => void 0);
177
+ },
178
+ {
179
+ actorRoot: config.actorRoot,
180
+ persistent: true,
181
+ canManageActor,
182
+ lineageAlive,
183
+ claimResidency: "durable",
184
+ rootId: config.rootId,
185
+ meshCursorPath: path.join(config.residencyRoot, "actor-mesh-cursor.json"),
186
+ retention: config.retention
187
+ }
188
+ );
189
+ this.lifecycle = new LifecycleBroker(
190
+ this.mesh,
191
+ this.identity,
192
+ this.participants,
193
+ {
194
+ enabled: true,
195
+ pollMs: config.mesh.actorPollMs,
196
+ maxReadEvents: config.mesh.maxReadEvents
197
+ },
198
+ (subscription, event) => this.#deliverLifecycle(subscription, event)
199
+ );
200
+ }
201
+ hostId;
202
+ identity;
203
+ mesh;
204
+ participants;
205
+ control;
206
+ agents;
207
+ actors;
208
+ lifecycle;
209
+ #ownerPath;
210
+ #lockPath;
211
+ #errorPath;
212
+ #requestsPath;
213
+ #processingPath;
214
+ #responsesPath;
215
+ #agentsPath;
216
+ #deliveryPrefix;
217
+ #token = randomUUID();
218
+ #requestTimer;
219
+ #pollingRequests = false;
220
+ #closed = false;
221
+ #started = false;
222
+ #idleSince = Date.now();
223
+ async start() {
224
+ if (this.#started) return;
225
+ this.#acquireLock();
226
+ this.#started = true;
227
+ fs.mkdirSync(this.#requestsPath, { recursive: true, mode: 448 });
228
+ fs.mkdirSync(this.#processingPath, { recursive: true, mode: 448 });
229
+ fs.mkdirSync(this.#responsesPath, { recursive: true, mode: 448 });
230
+ fs.mkdirSync(this.#agentsPath, { recursive: true, mode: 448 });
231
+ this.#recoverInterruptedRequests();
232
+ const firstSeenAgents = /* @__PURE__ */ new Map();
233
+ this.participants.registerSource(
234
+ () => agentParticipantRecords(
235
+ this.agents.listForUi(),
236
+ this.config.rootId,
237
+ this.hostId,
238
+ this.identity.id,
239
+ this.config.rootId,
240
+ firstSeenAgents
241
+ )
242
+ );
243
+ this.participants.registerSource(
244
+ () => this.actors.listOwned().map(
245
+ (actor) => actorParticipantRecord(
246
+ actor,
247
+ this.config.rootId,
248
+ this.hostId,
249
+ this.identity.id,
250
+ this.config.rootId
251
+ )
252
+ )
253
+ );
254
+ this.agents.subscribeUi(() => this.participants.scheduleRefresh());
255
+ this.actors.subscribe(() => this.participants.scheduleRefresh());
256
+ this.control.start((command, from, signal) => this.#acceptControl(command, from, signal));
257
+ await this.participants.start().catch(() => void 0);
258
+ this.lifecycle.start();
259
+ this.#requestTimer = setInterval(
260
+ () => void this.#pollRequests().catch(() => void 0),
261
+ REQUEST_POLL_MS
262
+ );
263
+ const now = Date.now();
264
+ const owner = {
265
+ format: RESIDENT_HOST_FORMAT,
266
+ hostId: this.hostId,
267
+ pid: process.pid,
268
+ token: this.#token,
269
+ startedAt: now,
270
+ readyAt: now
271
+ };
272
+ atomicWrite(this.#ownerPath, owner);
273
+ fs.rmSync(this.#errorPath, { force: true });
274
+ await this.#pollRequests();
275
+ }
276
+ async close() {
277
+ if (this.#closed) return;
278
+ this.#closed = true;
279
+ if (this.#requestTimer) clearInterval(this.#requestTimer);
280
+ this.#requestTimer = void 0;
281
+ while (this.#pollingRequests) await delay(10);
282
+ await this.participants.quiesce().catch(() => void 0);
283
+ await this.lifecycle.close().catch(() => void 0);
284
+ await this.control.close().catch(() => void 0);
285
+ try {
286
+ await this.actors.close();
287
+ } finally {
288
+ await this.agents.close();
289
+ await this.participants.close().catch(() => void 0);
290
+ this.#releaseLock();
291
+ }
292
+ }
293
+ async #acceptControl(command, _from, signal) {
294
+ if (command.operation === "cancel") {
295
+ return { accepted: false, error: "Cancel commands are handled by the control plane" };
296
+ }
297
+ if (command.operation === "stop") {
298
+ try {
299
+ await this.agents.stop(command.targetId);
300
+ this.participants.scheduleRefresh();
301
+ return { accepted: true, messageId: command.commandId };
302
+ } catch (error) {
303
+ if (!(error instanceof Error && /Unknown Fabric agent/.test(error.message))) {
304
+ return { accepted: false, error: errorMessage(error) };
305
+ }
306
+ }
307
+ try {
308
+ if (!this.actors.owns(command.targetId)) {
309
+ return { accepted: false, error: `Resident host does not own ${command.targetId}` };
310
+ }
311
+ await this.actors.stop(command.targetId);
312
+ this.participants.scheduleRefresh();
313
+ return { accepted: true, messageId: command.commandId };
314
+ } catch (error) {
315
+ return { accepted: false, error: errorMessage(error) };
316
+ }
317
+ }
318
+ const message = command.message?.trim();
319
+ if (!message) return { accepted: false, error: "Fabric control message must not be empty" };
320
+ if (command.operation === "ask") {
321
+ try {
322
+ if (!this.actors.owns(command.targetId)) {
323
+ return { accepted: false, error: `Resident host does not own ${command.targetId}` };
324
+ }
325
+ const result = await this.actors.ask(
326
+ command.targetId,
327
+ message,
328
+ command.data,
329
+ signal,
330
+ command.binding !== void 0 ? { binding: command.binding } : {}
331
+ );
332
+ return { accepted: true, messageId: result.id, result };
333
+ } catch (error) {
334
+ return { accepted: false, error: errorMessage(error) };
335
+ }
336
+ }
337
+ try {
338
+ this.agents.status(command.targetId);
339
+ const result = command.operation === "steer" ? this.agents.steer(command.targetId, message, command.data) : this.agents.followUp(command.targetId, message, command.data);
340
+ return { accepted: true, messageId: result.messageId };
341
+ } catch (error) {
342
+ if (!(error instanceof Error && /Unknown Fabric agent/.test(error.message))) {
343
+ return { accepted: false, error: errorMessage(error) };
344
+ }
345
+ }
346
+ try {
347
+ if (!this.actors.owns(command.targetId)) {
348
+ return { accepted: false, error: `Resident host does not own ${command.targetId}` };
349
+ }
350
+ const result = this.actors.tell(
351
+ command.targetId,
352
+ message,
353
+ command.data,
354
+ command.binding !== void 0 ? { binding: command.binding } : {}
355
+ );
356
+ return { accepted: true, messageId: result.messageId };
357
+ } catch (error) {
358
+ return { accepted: false, error: errorMessage(error) };
359
+ }
360
+ }
361
+ async #deliverLifecycle(subscription, event) {
362
+ const message = `Fabric lifecycle ${event.event} from ${event.source.name} (${event.source.id})${event.status ? ` with status ${event.status}` : ""}.`;
363
+ if (subscription.to === this.config.rootId) {
364
+ await this.#queueDelivery(
365
+ lifecycleSourceIdentity(event.source),
366
+ message,
367
+ subscription.delivery,
368
+ subscription.triggerTurn,
369
+ event
370
+ );
371
+ return;
372
+ }
373
+ try {
374
+ this.agents.status(subscription.to);
375
+ if (subscription.delivery === "steer") this.agents.steer(subscription.to, message, event);
376
+ else this.agents.followUp(subscription.to, message, event);
377
+ return;
378
+ } catch (error) {
379
+ if (!(error instanceof Error && /Unknown Fabric agent/.test(error.message))) throw error;
380
+ }
381
+ try {
382
+ if (this.actors.owns(subscription.to)) {
383
+ this.actors.tell(subscription.to, message, event);
384
+ return;
385
+ }
386
+ } catch {
387
+ }
388
+ const target = this.participants.get(subscription.to);
389
+ if (!target) throw new Error(`Unknown Fabric lifecycle target: ${subscription.to}`);
390
+ await this.control.request(
391
+ target.ownerHostId,
392
+ target.id,
393
+ subscription.delivery,
394
+ { message, data: event, triggerTurn: subscription.triggerTurn },
395
+ target.ownerIdentityId
396
+ );
397
+ }
398
+ async #queueDelivery(from, message, delivery, triggerTurn, data) {
399
+ const id = randomUUID();
400
+ const record = {
401
+ format: RESIDENT_HOST_FORMAT,
402
+ id,
403
+ rootId: this.config.rootId,
404
+ from,
405
+ delivery,
406
+ triggerTurn,
407
+ message,
408
+ ...data === void 0 ? {} : { data },
409
+ createdAt: Date.now()
410
+ };
411
+ try {
412
+ await this.mesh.put({
413
+ key: `${this.#deliveryPrefix}${id}`,
414
+ value: record,
415
+ identity: this.identity,
416
+ ifVersion: 0
417
+ });
418
+ } catch {
419
+ await this.mesh.put({
420
+ key: `${this.#deliveryPrefix}${id}`,
421
+ value: {
422
+ ...record,
423
+ message: message.slice(0, Math.max(1, this.config.mesh.eventContextChars)),
424
+ data: { fabricTruncated: true }
425
+ },
426
+ identity: this.identity,
427
+ ifVersion: 0
428
+ });
429
+ }
430
+ }
431
+ async #pollRequests() {
432
+ if (this.#pollingRequests || this.#closed) return;
433
+ this.#pollingRequests = true;
434
+ try {
435
+ let entries;
436
+ try {
437
+ entries = fs.readdirSync(this.#requestsPath).filter((entry) => entry.endsWith(".json"));
438
+ } catch {
439
+ return;
440
+ }
441
+ for (const entry of entries.slice(0, 32)) {
442
+ const source = path.join(this.#requestsPath, entry);
443
+ const processing = path.join(this.#processingPath, entry);
444
+ try {
445
+ fs.renameSync(source, processing);
446
+ } catch {
447
+ continue;
448
+ }
449
+ await this.#processRequest(processing);
450
+ }
451
+ } finally {
452
+ this.#pollingRequests = false;
453
+ this.#checkIdle();
454
+ }
455
+ }
456
+ #checkIdle() {
457
+ const activeActor = this.actors.listOwned().some((actor) => actor.residency === "durable" && actor.status !== "stopped");
458
+ const activeAgent = this.agents.listForUi().some((agent) => agent.status === "queued" || agent.status === "running");
459
+ let pendingRequest = false;
460
+ try {
461
+ pendingRequest = fs.readdirSync(this.#requestsPath).some((entry) => entry.endsWith(".json"));
462
+ } catch {
463
+ }
464
+ if (activeActor || activeAgent || pendingRequest) {
465
+ this.#idleSince = Date.now();
466
+ return;
467
+ }
468
+ if (Date.now() - this.#idleSince >= IDLE_EXIT_MS) this.onIdle();
469
+ }
470
+ async #processRequest(filePath) {
471
+ const command = readJson(filePath);
472
+ const requestId = command?.requestId ?? path.basename(filePath, ".json");
473
+ let response;
474
+ try {
475
+ if (command?.format !== RESIDENT_HOST_FORMAT || command.rootId !== this.config.rootId || command.requestId !== requestId) {
476
+ throw new Error("Invalid Fabric residency request");
477
+ }
478
+ if (command.operation === "spawn") {
479
+ if (command.request.sessionSeed || command.request.sessionFile || command.request.actorId || command.request.actorName || command.request.meshRoot || command.request.runnerSessionId || command.request.systemPrompt || command.request.images) {
480
+ throw new Error("Durable agents.spawn accepts only its public task and run settings");
481
+ }
482
+ const handle = await this.agents.spawn({ ...command.request, residency: "durable" });
483
+ this.agents.detachSignal(handle.id);
484
+ const runDirectory = this.agents.runDirectory(handle.id);
485
+ if (!runDirectory) throw new Error(`Resident agent ${handle.id} has no run directory`);
486
+ const worktreeGitRoot = this.agents.worktreeGitRoot(handle.id);
487
+ const metadata = {
488
+ format: RESIDENT_HOST_FORMAT,
489
+ rootId: this.config.rootId,
490
+ id: handle.id,
491
+ runDirectory,
492
+ handle: { ...handle, residency: "durable" },
493
+ ...worktreeGitRoot ? { worktreeGitRoot } : {},
494
+ createdAt: Date.now(),
495
+ updatedAt: Date.now()
496
+ };
497
+ atomicWrite(path.join(this.#agentsPath, `${handle.id}.json`), metadata);
498
+ response = {
499
+ format: RESIDENT_HOST_FORMAT,
500
+ requestId,
501
+ ok: true,
502
+ handle: metadata.handle,
503
+ completedAt: Date.now()
504
+ };
505
+ } else if (command.operation === "foreground") {
506
+ this.agents.markForeground(command.id);
507
+ response = {
508
+ format: RESIDENT_HOST_FORMAT,
509
+ requestId,
510
+ ok: true,
511
+ completedAt: Date.now()
512
+ };
513
+ } else if (command.operation === "cleanup") {
514
+ await this.agents.wait(command.id);
515
+ await this.agents.cleanup(command.id, command.deleteBranch);
516
+ fs.rmSync(path.join(this.#agentsPath, `${command.id}.json`), { force: true });
517
+ response = {
518
+ format: RESIDENT_HOST_FORMAT,
519
+ requestId,
520
+ ok: true,
521
+ completedAt: Date.now()
522
+ };
523
+ } else if (command.operation === "createActor") {
524
+ if (command.request.residency !== "durable") {
525
+ throw new Error("Resident host createActor only supports durable residency");
526
+ }
527
+ const actor = await this.actors.create(command.request);
528
+ response = {
529
+ format: RESIDENT_HOST_FORMAT,
530
+ requestId,
531
+ ok: true,
532
+ actor,
533
+ completedAt: Date.now()
534
+ };
535
+ } else {
536
+ if (!this.actors.owns(command.id)) {
537
+ throw new Error(`Resident host does not own ${command.id}`);
538
+ }
539
+ await this.actors.remove(command.id);
540
+ response = {
541
+ format: RESIDENT_HOST_FORMAT,
542
+ requestId,
543
+ ok: true,
544
+ completedAt: Date.now()
545
+ };
546
+ }
547
+ } catch (error) {
548
+ response = {
549
+ format: RESIDENT_HOST_FORMAT,
550
+ requestId,
551
+ ok: false,
552
+ error: errorMessage(error),
553
+ completedAt: Date.now()
554
+ };
555
+ }
556
+ atomicWrite(path.join(this.#responsesPath, `${requestId}.json`), response);
557
+ fs.rmSync(filePath, { force: true });
558
+ this.participants.scheduleRefresh();
559
+ }
560
+ #recoverInterruptedRequests() {
561
+ let entries;
562
+ try {
563
+ entries = fs.readdirSync(this.#processingPath).filter((entry) => entry.endsWith(".json"));
564
+ } catch {
565
+ return;
566
+ }
567
+ for (const entry of entries) {
568
+ const requestId = path.basename(entry, ".json");
569
+ const response = {
570
+ format: RESIDENT_HOST_FORMAT,
571
+ requestId,
572
+ ok: false,
573
+ error: "Fabric residency outcome is indeterminate after resident host restart",
574
+ completedAt: Date.now()
575
+ };
576
+ atomicWrite(path.join(this.#responsesPath, entry), response);
577
+ fs.rmSync(path.join(this.#processingPath, entry), { force: true });
578
+ }
579
+ }
580
+ #acquireLock() {
581
+ fs.mkdirSync(this.config.residencyRoot, { recursive: true, mode: 448 });
582
+ const existing = readJson(this.#ownerPath);
583
+ if (existing && processAlive(existing.pid)) {
584
+ throw new ResidentHostAlreadyRunning(`Fabric resident host is already running (${existing.pid})`);
585
+ }
586
+ try {
587
+ const descriptor = fs.openSync(this.#lockPath, "wx", 384);
588
+ fs.writeFileSync(descriptor, JSON.stringify({ token: this.#token, pid: process.pid }));
589
+ fs.closeSync(descriptor);
590
+ } catch (error) {
591
+ if (error instanceof Error && "code" in error && error.code === "EEXIST") {
592
+ const locked = readJson(this.#lockPath);
593
+ if (typeof locked?.pid === "number" && processAlive(locked.pid)) {
594
+ throw new ResidentHostAlreadyRunning(`Fabric resident host is starting (${locked.pid})`);
595
+ }
596
+ fs.rmSync(this.#lockPath, { force: true });
597
+ const descriptor = fs.openSync(this.#lockPath, "wx", 384);
598
+ fs.writeFileSync(descriptor, JSON.stringify({ token: this.#token, pid: process.pid }));
599
+ fs.closeSync(descriptor);
600
+ } else {
601
+ throw error;
602
+ }
603
+ }
604
+ }
605
+ #releaseLock() {
606
+ const lock = readJson(this.#lockPath);
607
+ if (lock?.token === this.#token) fs.rmSync(this.#lockPath, { force: true });
608
+ const owner = readJson(this.#ownerPath);
609
+ if (owner?.token === this.#token) fs.rmSync(this.#ownerPath, { force: true });
610
+ }
611
+ };
612
+ var runResidentHost = async (config, signal) => {
613
+ let finishIdle;
614
+ const idle = new Promise((resolve) => {
615
+ finishIdle = resolve;
616
+ });
617
+ const host = new ResidentHost(config, () => finishIdle?.());
618
+ await host.start();
619
+ if (signal?.aborted) {
620
+ await host.close();
621
+ return;
622
+ }
623
+ await Promise.race([
624
+ idle,
625
+ new Promise((resolve) => {
626
+ const finish = () => resolve();
627
+ signal?.addEventListener("abort", finish, { once: true });
628
+ process.once("SIGTERM", finish);
629
+ process.once("SIGINT", finish);
630
+ })
631
+ ]);
632
+ await host.close();
633
+ };
634
+ var runResidentHostFromConfigPath = async (configPath, signal) => {
635
+ let config;
636
+ try {
637
+ config = validateResidentHostConfig(readJson(configPath), configPath);
638
+ await runResidentHost(config, signal);
639
+ } catch (error) {
640
+ if (error instanceof ResidentHostAlreadyRunning) return;
641
+ const residencyRoot = config?.residencyRoot ?? path.dirname(configPath);
642
+ try {
643
+ atomicWrite(path.join(residencyRoot, "error.json"), {
644
+ error: errorMessage(error),
645
+ occurredAt: Date.now()
646
+ });
647
+ } catch {
648
+ }
649
+ throw error;
650
+ }
651
+ };
652
+ var isMain = process.argv[1] && path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url));
653
+ if (isMain) {
654
+ const configPath = parseResidentHostConfigPath(process.argv);
655
+ try {
656
+ await runResidentHostFromConfigPath(configPath);
657
+ } catch {
658
+ process.exitCode = 1;
659
+ }
660
+ }
661
+
662
+ export {
663
+ runResidentHostFromConfigPath
664
+ };
665
+ //# sourceMappingURL=chunk-BT6UELLP.js.map