pi-fabric 0.76.2 → 0.78.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 (80) hide show
  1. package/README.md +1 -0
  2. package/dist/agents/bash-worktree-add.d.ts +24 -0
  3. package/dist/agents/bash-worktree-add.d.ts.map +1 -0
  4. package/dist/agents/clone-first-worktree.d.ts +17 -0
  5. package/dist/agents/clone-first-worktree.d.ts.map +1 -0
  6. package/dist/agents/cow-clone.d.ts +8 -0
  7. package/dist/agents/cow-clone.d.ts.map +1 -0
  8. package/dist/agents/worktree-manager.d.ts.map +1 -1
  9. package/dist/agents/worktree-paths.d.ts +5 -0
  10. package/dist/agents/worktree-paths.d.ts.map +1 -0
  11. package/dist/capture/catalog.d.ts +3 -0
  12. package/dist/capture/catalog.d.ts.map +1 -1
  13. package/dist/chunks/{chunk-PAAXKXWT.js → chunk-2AF5ANEB.js} +16 -2
  14. package/dist/chunks/chunk-2AF5ANEB.js.map +7 -0
  15. package/dist/chunks/{chunk-QXB75FLD.js → chunk-2E3CAABA.js} +8 -1
  16. package/dist/chunks/chunk-2E3CAABA.js.map +7 -0
  17. package/dist/chunks/{chunk-6ZKYQW33.js → chunk-3QPTSKDG.js} +139 -29
  18. package/dist/chunks/chunk-3QPTSKDG.js.map +7 -0
  19. package/dist/chunks/{chunk-BT6UELLP.js → chunk-DUIHQAIZ.js} +2 -2
  20. package/dist/chunks/{chunk-6BBFFEPO.js → chunk-DYEOFQ64.js} +15 -1
  21. package/dist/chunks/{chunk-6BBFFEPO.js.map → chunk-DYEOFQ64.js.map} +2 -2
  22. package/dist/chunks/{chunk-F2GKEXGI.js → chunk-PFF3X7OO.js} +272 -146
  23. package/dist/chunks/chunk-PFF3X7OO.js.map +7 -0
  24. package/dist/chunks/{fabric-runtime-state-VCLWTB55.js → fabric-runtime-state-H2NCRJWK.js} +797 -191
  25. package/dist/chunks/fabric-runtime-state-H2NCRJWK.js.map +7 -0
  26. package/dist/chunks/{settings-VEXI347I.js → settings-FUL3U3N4.js} +2 -2
  27. package/dist/commands/fabric.d.ts.map +1 -1
  28. package/dist/config.d.ts +4 -0
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/core/action-registry.d.ts +1 -0
  31. package/dist/core/action-registry.d.ts.map +1 -1
  32. package/dist/core/action-registry.js +1 -1
  33. package/dist/fabric-runtime-state.d.ts +2 -0
  34. package/dist/fabric-runtime-state.d.ts.map +1 -1
  35. package/dist/fabric-state.d.ts +1 -0
  36. package/dist/fabric-state.d.ts.map +1 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +218 -8
  39. package/dist/index.js.map +3 -3
  40. package/dist/providers/arg-normalization.d.ts +3 -0
  41. package/dist/providers/arg-normalization.d.ts.map +1 -1
  42. package/dist/providers/memory-provider.js +1 -1
  43. package/dist/providers/pi-tools-provider.d.ts.map +1 -1
  44. package/dist/repairs/active.d.ts +7 -0
  45. package/dist/repairs/active.d.ts.map +1 -0
  46. package/dist/repairs/apply.d.ts +7 -0
  47. package/dist/repairs/apply.d.ts.map +1 -0
  48. package/dist/repairs/catalog-digest.d.ts +5 -0
  49. package/dist/repairs/catalog-digest.d.ts.map +1 -0
  50. package/dist/repairs/classify.d.ts +10 -0
  51. package/dist/repairs/classify.d.ts.map +1 -0
  52. package/dist/repairs/compiler.d.ts +32 -0
  53. package/dist/repairs/compiler.d.ts.map +1 -0
  54. package/dist/repairs/promote.d.ts +4 -0
  55. package/dist/repairs/promote.d.ts.map +1 -0
  56. package/dist/repairs/store.d.ts +9 -0
  57. package/dist/repairs/store.d.ts.map +1 -0
  58. package/dist/repairs/types.d.ts +49 -0
  59. package/dist/repairs/types.d.ts.map +1 -0
  60. package/dist/residency/client.d.ts.map +1 -1
  61. package/dist/residency/host.js +3 -3
  62. package/dist/residency/pi-entry.js +3 -3
  63. package/dist/speculation/store.d.ts +11 -7
  64. package/dist/speculation/store.d.ts.map +1 -1
  65. package/dist/speculation/types.d.ts +2 -1
  66. package/dist/speculation/types.d.ts.map +1 -1
  67. package/docs/agents.md +1 -1
  68. package/docs/architecture.md +3 -1
  69. package/docs/configuration.md +4 -0
  70. package/docs/interface.md +1 -0
  71. package/docs/repairs.md +57 -0
  72. package/package.json +1 -1
  73. package/skills/fabric-exec/references/agents.md +3 -3
  74. package/dist/chunks/chunk-6ZKYQW33.js.map +0 -7
  75. package/dist/chunks/chunk-F2GKEXGI.js.map +0 -7
  76. package/dist/chunks/chunk-PAAXKXWT.js.map +0 -7
  77. package/dist/chunks/chunk-QXB75FLD.js.map +0 -7
  78. package/dist/chunks/fabric-runtime-state-VCLWTB55.js.map +0 -7
  79. /package/dist/chunks/{chunk-BT6UELLP.js.map → chunk-DUIHQAIZ.js.map} +0 -0
  80. /package/dist/chunks/{settings-VEXI347I.js.map → settings-FUL3U3N4.js.map} +0 -0
@@ -7,8 +7,9 @@ import {
7
7
  } from "./chunk-IGVDD2HT.js";
8
8
  import {
9
9
  MemoryProvider,
10
- actionArgNormalizer
11
- } from "./chunk-6BBFFEPO.js";
10
+ actionArgNormalizer,
11
+ uniqueDeclaredKeyForSpelling
12
+ } from "./chunk-DYEOFQ64.js";
12
13
  import "./chunk-BOIV6E3C.js";
13
14
  import "./chunk-NNFA2OIC.js";
14
15
  import "./chunk-E3V22XGU.js";
@@ -31,7 +32,7 @@ import {
31
32
  repairFabricGuestCode,
32
33
  resolveFabricIdentity,
33
34
  runFabricHandoffAtBoundary
34
- } from "./chunk-PAAXKXWT.js";
35
+ } from "./chunk-2AF5ANEB.js";
35
36
  import {
36
37
  AgentTranscriptReader,
37
38
  MAX_WRITE_DIFF_BYTES,
@@ -40,8 +41,11 @@ import {
40
41
  writeContentForPreview
41
42
  } from "./chunk-5XQA7QX2.js";
42
43
  import {
43
- ActionRegistry
44
- } from "./chunk-6ZKYQW33.js";
44
+ ActionRegistry,
45
+ clearActiveRepairCompiler,
46
+ repairActionName,
47
+ setActiveRepairCompiler
48
+ } from "./chunk-3QPTSKDG.js";
45
49
  import {
46
50
  formatFabricEffectConflict
47
51
  } from "./chunk-LMAVZN7J.js";
@@ -69,15 +73,17 @@ import {
69
73
  ParticipantDirectory,
70
74
  actorDeliveryNotice,
71
75
  actorParticipantRecord,
76
+ addCloneFirstWorktree,
72
77
  agentParticipantRecords,
73
78
  effectiveAgentTimeoutMs,
74
79
  isFabricLifecycleEventType,
80
+ isFabricWorktreePath,
75
81
  lifecycleSourceIdentity,
76
82
  resolveActorDeliveryPolicy,
77
83
  resolveAgentCwd,
78
84
  resolvePiBinary,
79
85
  validateAgentCwdRequest
80
- } from "./chunk-F2GKEXGI.js";
86
+ } from "./chunk-PFF3X7OO.js";
81
87
  import "./chunk-PM5I335G.js";
82
88
  import "./chunk-VPPIJRNZ.js";
83
89
  import {
@@ -100,7 +106,7 @@ import {
100
106
  MIN_AGENT_TIMEOUT_MS,
101
107
  loadFabricConfig,
102
108
  resolveFabricModel
103
- } from "./chunk-QXB75FLD.js";
109
+ } from "./chunk-2E3CAABA.js";
104
110
  import {
105
111
  isFabricThinking
106
112
  } from "./chunk-7B55UIZL.js";
@@ -149,7 +155,7 @@ import {
149
155
  } from "./chunk-IDTQ2B5W.js";
150
156
 
151
157
  // src/fabric-runtime-state.ts
152
- import path14 from "node:path";
158
+ import path16 from "node:path";
153
159
  import { fileURLToPath as fileURLToPath2 } from "node:url";
154
160
 
155
161
  // src/actors/global-registry.ts
@@ -2196,10 +2202,10 @@ var FabricComponentSupervisor = class {
2196
2202
  adjacent.set(edge.from, targets);
2197
2203
  }
2198
2204
  const cycles = /* @__PURE__ */ new Map();
2199
- const visit = (node, path15, positions) => {
2205
+ const visit = (node, path17, positions) => {
2200
2206
  const position = positions.get(node);
2201
2207
  if (position !== void 0) {
2202
- const cycle = path15.slice(position);
2208
+ const cycle = path17.slice(position);
2203
2209
  const rotations = cycle.map((_, index) => [
2204
2210
  ...cycle.slice(index),
2205
2211
  ...cycle.slice(0, index)
@@ -2209,10 +2215,10 @@ var FabricComponentSupervisor = class {
2209
2215
  cycles.set(canonical.join("\0"), canonical);
2210
2216
  return;
2211
2217
  }
2212
- if (path15.length > this.#components.size) return;
2213
- const nextPositions = new Map(positions).set(node, path15.length);
2218
+ if (path17.length > this.#components.size) return;
2219
+ const nextPositions = new Map(positions).set(node, path17.length);
2214
2220
  for (const target of adjacent.get(node) ?? []) {
2215
- visit(target, [...path15, node], nextPositions);
2221
+ visit(target, [...path17, node], nextPositions);
2216
2222
  }
2217
2223
  };
2218
2224
  for (const id of this.#components.keys()) visit(id, [], /* @__PURE__ */ new Map());
@@ -3324,6 +3330,473 @@ var FabricExecutionService = class {
3324
3330
  }
3325
3331
  };
3326
3332
 
3333
+ // src/repairs/apply.ts
3334
+ var strictDeclaredPropertyNames = (schema) => {
3335
+ if (schema.type !== "object" || schema.additionalProperties !== false || schema.patternProperties !== void 0) {
3336
+ return void 0;
3337
+ }
3338
+ const properties = schema.properties;
3339
+ if (!properties || typeof properties !== "object" || Array.isArray(properties)) return void 0;
3340
+ return Object.keys(properties);
3341
+ };
3342
+ var applyKeyAliasRepairs = (ref, args, repairs, schema) => {
3343
+ const declared = strictDeclaredPropertyNames(schema);
3344
+ if (!declared) return { args, changed: false };
3345
+ const declaredSet = new Set(declared);
3346
+ const applicable = repairs.filter(
3347
+ (repair) => repair.kind === "keyAlias" && repair.ref === ref && repair.from in args && repair.from !== repair.to && !declaredSet.has(repair.from) && declaredSet.has(repair.to) && uniqueDeclaredKeyForSpelling(repair.from, declared) === repair.to
3348
+ );
3349
+ const sourceCountByTarget = /* @__PURE__ */ new Map();
3350
+ for (const repair of applicable) {
3351
+ sourceCountByTarget.set(repair.to, (sourceCountByTarget.get(repair.to) ?? 0) + 1);
3352
+ }
3353
+ let out = args;
3354
+ let changed = false;
3355
+ for (const repair of applicable) {
3356
+ const canonicalPresent = repair.to in args;
3357
+ if (!canonicalPresent && sourceCountByTarget.get(repair.to) !== 1) continue;
3358
+ if (out === args) out = { ...args };
3359
+ if (!(repair.to in out)) out[repair.to] = out[repair.from];
3360
+ delete out[repair.from];
3361
+ changed = true;
3362
+ }
3363
+ return { args: out, changed };
3364
+ };
3365
+ var applyActionAliasRepairs = (provider, actionName, repairs, declared) => {
3366
+ if (declared.includes(actionName)) return void 0;
3367
+ const liveTarget = repairActionName(declared, actionName).repaired;
3368
+ if (!liveTarget) return void 0;
3369
+ for (const repair of repairs) {
3370
+ if (repair.kind !== "actionAlias") continue;
3371
+ if (repair.provider !== provider) continue;
3372
+ if (repair.from !== actionName) continue;
3373
+ if (repair.to !== liveTarget) continue;
3374
+ return repair.to;
3375
+ }
3376
+ return void 0;
3377
+ };
3378
+ var applyCatalogArgRepairs = (ref, args, repairs, schema) => applyKeyAliasRepairs(ref, args, repairs, schema);
3379
+
3380
+ // src/repairs/catalog-digest.ts
3381
+ var catalogDigestFromSurface = (surface) => stableJsonHash({
3382
+ v: 1,
3383
+ providers: [...surface.providers].sort(),
3384
+ captured: [...surface.capturedTools].sort()
3385
+ });
3386
+
3387
+ // src/repairs/promote.ts
3388
+ var keyAliasCandidate = (ref, extraKeys, declared) => {
3389
+ if (extraKeys.length !== 1) return void 0;
3390
+ const from = extraKeys[0];
3391
+ const to = uniqueDeclaredKeyForSpelling(from, declared);
3392
+ if (!to) return void 0;
3393
+ return { kind: "keyAlias", ref, from, to };
3394
+ };
3395
+ var actionAliasCandidate = (provider, actionName, declared) => {
3396
+ const repair = repairActionName(declared, actionName);
3397
+ if (!repair.repaired || repair.repaired === actionName) return void 0;
3398
+ return {
3399
+ kind: "actionAlias",
3400
+ provider,
3401
+ from: actionName,
3402
+ to: repair.repaired
3403
+ };
3404
+ };
3405
+
3406
+ // src/repairs/store.ts
3407
+ import { randomUUID as randomUUID2 } from "node:crypto";
3408
+ import fs2 from "node:fs";
3409
+ import path2 from "node:path";
3410
+
3411
+ // src/repairs/types.ts
3412
+ var REPAIR_TABLE_VERSION = 1;
3413
+ var MAX_CATALOG_REPAIRS = 256;
3414
+ var repairIdentity = (repair) => {
3415
+ if (repair.kind === "keyAlias") {
3416
+ return `keyAlias:${repair.ref}:${repair.from}:${repair.to}`;
3417
+ }
3418
+ return `actionAlias:${repair.provider}:${repair.from}:${repair.to}`;
3419
+ };
3420
+ var emptyRepairTable = (catalogDigest, now = (/* @__PURE__ */ new Date()).toISOString()) => ({
3421
+ version: REPAIR_TABLE_VERSION,
3422
+ catalogDigest,
3423
+ repairs: [],
3424
+ createdAt: now,
3425
+ updatedAt: now
3426
+ });
3427
+
3428
+ // src/repairs/store.ts
3429
+ var REPAIR_LOCK_ATTEMPTS = 50;
3430
+ var REPAIR_LOCK_DELAY_MS = 5;
3431
+ var REPAIR_STALE_LOCK_MS = 3e4;
3432
+ var repairsDirectory = (agentDir) => path2.join(agentDir, "fabric", "repairs");
3433
+ var currentPath = (directory) => path2.join(directory, "current.json");
3434
+ var lockPath = (directory) => path2.join(directory, "current.lock");
3435
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3436
+ var errorCode = (error) => isRecord(error) && typeof error.code === "string" ? error.code : void 0;
3437
+ var sleepSync = (() => {
3438
+ try {
3439
+ const buffer = new Int32Array(new SharedArrayBuffer(4));
3440
+ return (ms) => {
3441
+ Atomics.wait(buffer, 0, 0, ms);
3442
+ };
3443
+ } catch {
3444
+ return () => void 0;
3445
+ }
3446
+ })();
3447
+ var parseRepair = (value) => {
3448
+ if (!isRecord(value) || typeof value.kind !== "string") return void 0;
3449
+ if (value.kind === "keyAlias") {
3450
+ if (typeof value.ref !== "string" || typeof value.from !== "string" || typeof value.to !== "string" || !value.ref.trim() || !value.from.trim() || !value.to.trim()) {
3451
+ return void 0;
3452
+ }
3453
+ return { kind: "keyAlias", ref: value.ref.trim(), from: value.from.trim(), to: value.to.trim() };
3454
+ }
3455
+ if (value.kind === "actionAlias") {
3456
+ if (typeof value.provider !== "string" || typeof value.from !== "string" || typeof value.to !== "string" || !value.provider.trim() || !value.from.trim() || !value.to.trim()) {
3457
+ return void 0;
3458
+ }
3459
+ return {
3460
+ kind: "actionAlias",
3461
+ provider: value.provider.trim(),
3462
+ from: value.from.trim(),
3463
+ to: value.to.trim()
3464
+ };
3465
+ }
3466
+ return "skip";
3467
+ };
3468
+ var parseRepairTable = (value) => {
3469
+ if (!isRecord(value) || value.version !== REPAIR_TABLE_VERSION) return void 0;
3470
+ if (typeof value.catalogDigest !== "string" || !value.catalogDigest.trim()) return void 0;
3471
+ if (!Array.isArray(value.repairs) || value.repairs.length > MAX_CATALOG_REPAIRS) return void 0;
3472
+ const repairs = [];
3473
+ const identities = /* @__PURE__ */ new Set();
3474
+ for (const entry of value.repairs) {
3475
+ const parsed = parseRepair(entry);
3476
+ if (parsed === "skip") continue;
3477
+ if (!parsed) return void 0;
3478
+ const identity = repairIdentity(parsed);
3479
+ if (identities.has(identity)) continue;
3480
+ identities.add(identity);
3481
+ repairs.push(parsed);
3482
+ }
3483
+ if (typeof value.createdAt !== "string" || typeof value.updatedAt !== "string") return void 0;
3484
+ return {
3485
+ version: REPAIR_TABLE_VERSION,
3486
+ catalogDigest: value.catalogDigest.trim(),
3487
+ repairs,
3488
+ createdAt: value.createdAt,
3489
+ updatedAt: value.updatedAt
3490
+ };
3491
+ };
3492
+ var errorText = (error) => (error instanceof Error ? error.message : String(error)).slice(0, 200);
3493
+ var writeTable = (file, table) => {
3494
+ writeJsonAtomic(file, table, { space: 2, newline: true, mode: 384, dirMode: 448 });
3495
+ };
3496
+ var processAlive = (pid) => {
3497
+ if (!Number.isSafeInteger(pid) || pid <= 0) return false;
3498
+ try {
3499
+ process.kill(pid, 0);
3500
+ return true;
3501
+ } catch {
3502
+ return false;
3503
+ }
3504
+ };
3505
+ var reapStaleLock = (lock, verify) => {
3506
+ const claim = `${lock}.reap-${process.pid}-${randomUUID2()}`;
3507
+ try {
3508
+ fs2.renameSync(lock, claim);
3509
+ } catch {
3510
+ return false;
3511
+ }
3512
+ if (!verify(claim)) {
3513
+ try {
3514
+ fs2.renameSync(claim, lock);
3515
+ } catch {
3516
+ if (verify(claim)) fs2.rmSync(claim, { recursive: true, force: true });
3517
+ }
3518
+ return false;
3519
+ }
3520
+ fs2.rmSync(claim, { recursive: true, force: true });
3521
+ return true;
3522
+ };
3523
+ var withRepairLock = (directory, operation) => {
3524
+ fs2.mkdirSync(directory, { recursive: true, mode: 448 });
3525
+ const lock = lockPath(directory);
3526
+ const ownerPath = path2.join(lock, "owner");
3527
+ const token = randomUUID2();
3528
+ let acquired = false;
3529
+ for (let attempt = 0; attempt < REPAIR_LOCK_ATTEMPTS; attempt++) {
3530
+ try {
3531
+ fs2.mkdirSync(lock, { mode: 448 });
3532
+ try {
3533
+ fs2.writeFileSync(ownerPath, `${token}
3534
+ ${process.pid}
3535
+ ${Date.now()}
3536
+ `, {
3537
+ encoding: "utf8",
3538
+ mode: 384
3539
+ });
3540
+ } catch (error) {
3541
+ fs2.rmSync(lock, { recursive: true, force: true });
3542
+ throw error;
3543
+ }
3544
+ acquired = true;
3545
+ break;
3546
+ } catch (error) {
3547
+ if (errorCode(error) !== "EEXIST") throw error;
3548
+ try {
3549
+ const firstOwner = fs2.readFileSync(ownerPath, "utf8");
3550
+ const [, pidText, createdText] = firstOwner.trim().split("\n");
3551
+ const stale = Date.now() - Number(createdText) > REPAIR_STALE_LOCK_MS;
3552
+ if (stale && !processAlive(Number(pidText))) {
3553
+ const secondOwner = fs2.readFileSync(ownerPath, "utf8");
3554
+ if (secondOwner === firstOwner && reapStaleLock(lock, (claimed) => {
3555
+ try {
3556
+ const owner = fs2.readFileSync(path2.join(claimed, "owner"), "utf8");
3557
+ const [, pid, created] = owner.trim().split("\n");
3558
+ return Date.now() - Number(created) > REPAIR_STALE_LOCK_MS && !processAlive(Number(pid));
3559
+ } catch {
3560
+ return false;
3561
+ }
3562
+ })) {
3563
+ continue;
3564
+ }
3565
+ }
3566
+ } catch {
3567
+ try {
3568
+ const first = fs2.statSync(lock);
3569
+ if (Date.now() - first.mtimeMs > REPAIR_STALE_LOCK_MS && reapStaleLock(lock, (claimed) => {
3570
+ try {
3571
+ return Date.now() - fs2.statSync(claimed).mtimeMs > REPAIR_STALE_LOCK_MS;
3572
+ } catch {
3573
+ return false;
3574
+ }
3575
+ })) {
3576
+ continue;
3577
+ }
3578
+ } catch {
3579
+ }
3580
+ }
3581
+ if (attempt === REPAIR_LOCK_ATTEMPTS - 1) break;
3582
+ sleepSync(REPAIR_LOCK_DELAY_MS);
3583
+ }
3584
+ }
3585
+ if (!acquired) throw new Error("Timed out waiting for the Fabric repair table lock");
3586
+ try {
3587
+ return operation();
3588
+ } finally {
3589
+ try {
3590
+ const owner = fs2.readFileSync(ownerPath, "utf8");
3591
+ if (owner.startsWith(`${token}
3592
+ `)) {
3593
+ fs2.rmSync(lock, { recursive: true, force: true });
3594
+ }
3595
+ } catch {
3596
+ }
3597
+ }
3598
+ };
3599
+ var loadRepairTable = (directory, catalogDigest) => {
3600
+ const file = currentPath(directory);
3601
+ let raw;
3602
+ try {
3603
+ raw = fs2.readFileSync(file, "utf8");
3604
+ } catch (error) {
3605
+ if (errorCode(error) === "ENOENT") return { table: emptyRepairTable(catalogDigest) };
3606
+ return {
3607
+ table: emptyRepairTable(catalogDigest),
3608
+ error: `repair table is unreadable: ${errorText(error)}`
3609
+ };
3610
+ }
3611
+ let parsed;
3612
+ try {
3613
+ parsed = JSON.parse(raw);
3614
+ } catch {
3615
+ return { table: emptyRepairTable(catalogDigest), error: "repair table is malformed JSON" };
3616
+ }
3617
+ const table = parseRepairTable(parsed);
3618
+ if (!table) {
3619
+ return { table: emptyRepairTable(catalogDigest), error: "repair table is invalid" };
3620
+ }
3621
+ if (table.catalogDigest !== catalogDigest) return { table: emptyRepairTable(catalogDigest) };
3622
+ return { table };
3623
+ };
3624
+ var saveRepairTable = (directory, table) => withRepairLock(directory, () => {
3625
+ const loaded = loadRepairTable(directory, table.catalogDigest);
3626
+ if (loaded.error) throw new Error(loaded.error);
3627
+ const persisted = loaded.table;
3628
+ const repairs = [];
3629
+ const identities = /* @__PURE__ */ new Set();
3630
+ for (const repair of [...persisted.repairs, ...table.repairs]) {
3631
+ const identity = repairIdentity(repair);
3632
+ if (identities.has(identity)) continue;
3633
+ if (repairs.length >= MAX_CATALOG_REPAIRS) break;
3634
+ identities.add(identity);
3635
+ repairs.push(repair);
3636
+ }
3637
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3638
+ const merged = {
3639
+ version: REPAIR_TABLE_VERSION,
3640
+ catalogDigest: table.catalogDigest,
3641
+ repairs,
3642
+ createdAt: persisted.repairs.length > 0 ? persisted.createdAt : table.createdAt,
3643
+ updatedAt: now
3644
+ };
3645
+ writeTable(currentPath(directory), merged);
3646
+ return merged;
3647
+ });
3648
+
3649
+ // src/repairs/compiler.ts
3650
+ var errorMessage3 = (error) => (error instanceof Error ? error.message : String(error)).slice(0, 240);
3651
+ var RepairCompiler = class {
3652
+ directory;
3653
+ enabled;
3654
+ #digest = "";
3655
+ #table;
3656
+ #counts = /* @__PURE__ */ new Map();
3657
+ #applyHits = 0;
3658
+ #invocationErrors = 0;
3659
+ #effectDropped = 0;
3660
+ #storeError;
3661
+ constructor(options) {
3662
+ this.directory = repairsDirectory(options.agentDir);
3663
+ this.enabled = options.enabled !== false;
3664
+ this.#table = emptyRepairTable("");
3665
+ }
3666
+ get catalogDigest() {
3667
+ return this.#digest;
3668
+ }
3669
+ get repairs() {
3670
+ return this.#table.repairs;
3671
+ }
3672
+ setCatalogSurface(surface) {
3673
+ const digest3 = catalogDigestFromSurface(surface);
3674
+ if (digest3 === this.#digest && this.#table.catalogDigest === digest3) return;
3675
+ this.#digest = digest3;
3676
+ if (!this.enabled) {
3677
+ this.#table = emptyRepairTable(digest3);
3678
+ this.#storeError = void 0;
3679
+ } else {
3680
+ const loaded = loadRepairTable(this.directory, digest3);
3681
+ this.#table = loaded.table;
3682
+ this.#storeError = loaded.error;
3683
+ }
3684
+ this.#counts.clear();
3685
+ }
3686
+ observe(classification, options) {
3687
+ if (classification.stage === "effect") {
3688
+ this.#effectDropped += 1;
3689
+ return void 0;
3690
+ }
3691
+ if (options?.countError !== false) this.#invocationErrors += 1;
3692
+ const existing = this.#counts.get(classification.fingerprint);
3693
+ const candidate = classification.candidate ?? existing?.candidate;
3694
+ const next = {
3695
+ stage: classification.stage,
3696
+ count: (existing?.count ?? 0) + 1,
3697
+ ...candidate ? { candidate } : {}
3698
+ };
3699
+ this.#counts.set(classification.fingerprint, next);
3700
+ if (!this.enabled || !next.candidate) return void 0;
3701
+ return this.#promote(next.candidate);
3702
+ }
3703
+ recordInvocationError() {
3704
+ this.#invocationErrors += 1;
3705
+ }
3706
+ observeInvalidArgs(ref, args, declared, message2, options) {
3707
+ const extra = options?.extraKeys ?? Object.keys(args).filter((key) => !declared.includes(key));
3708
+ const candidate = keyAliasCandidate(ref, extra, declared);
3709
+ return this.observe(
3710
+ {
3711
+ stage: "invocation_args",
3712
+ fingerprint: extra[0] ? `args:${ref}:${extra[0]}` : `args:${ref}:${message2.slice(0, 80)}`,
3713
+ ...candidate ? { candidate } : {}
3714
+ },
3715
+ options
3716
+ );
3717
+ }
3718
+ observeUnknownAction(provider, actionName, declared, options) {
3719
+ const candidate = actionAliasCandidate(provider, actionName, declared);
3720
+ return this.observe(
3721
+ {
3722
+ stage: "invocation_unknown_action",
3723
+ fingerprint: `unknown:${provider}.${actionName}`,
3724
+ ...candidate ? { candidate } : {}
3725
+ },
3726
+ options
3727
+ );
3728
+ }
3729
+ applyArgs(ref, args, schema) {
3730
+ if (!this.enabled || this.#table.repairs.length === 0) return args;
3731
+ const result = applyCatalogArgRepairs(ref, args, this.#table.repairs, schema);
3732
+ if (result.changed) this.#applyHits += 1;
3733
+ return result.args;
3734
+ }
3735
+ applyActionName(provider, actionName, declared) {
3736
+ if (!this.enabled || this.#table.repairs.length === 0) return actionName;
3737
+ const repaired = applyActionAliasRepairs(
3738
+ provider,
3739
+ actionName,
3740
+ this.#table.repairs,
3741
+ declared
3742
+ );
3743
+ if (repaired && repaired !== actionName) {
3744
+ this.#applyHits += 1;
3745
+ return repaired;
3746
+ }
3747
+ return actionName;
3748
+ }
3749
+ status() {
3750
+ const fingerprints = [...this.#counts.entries()].map(([fingerprint, state]) => ({
3751
+ fingerprint,
3752
+ stage: state.stage,
3753
+ count: state.count
3754
+ })).sort((left, right) => right.count - left.count || left.fingerprint.localeCompare(right.fingerprint)).slice(0, 20);
3755
+ return {
3756
+ enabled: this.enabled,
3757
+ catalogDigest: this.#digest,
3758
+ repairCount: this.#table.repairs.length,
3759
+ applyHits: this.#applyHits,
3760
+ invocationErrors: this.#invocationErrors,
3761
+ effectDropped: this.#effectDropped,
3762
+ ...this.#storeError ? { storeError: this.#storeError } : {},
3763
+ fingerprints,
3764
+ repairs: [...this.#table.repairs]
3765
+ };
3766
+ }
3767
+ #promote(candidate) {
3768
+ if (!this.#digest) return void 0;
3769
+ const identity = repairIdentity(candidate);
3770
+ if (this.#table.repairs.some((repair) => repairIdentity(repair) === identity)) {
3771
+ if (this.#storeError) this.#persist(this.#table);
3772
+ return void 0;
3773
+ }
3774
+ if (this.#table.repairs.length >= MAX_CATALOG_REPAIRS) return void 0;
3775
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3776
+ const next = {
3777
+ ...this.#table,
3778
+ catalogDigest: this.#digest || this.#table.catalogDigest,
3779
+ repairs: [...this.#table.repairs, candidate],
3780
+ updatedAt: now,
3781
+ createdAt: this.#table.createdAt || now
3782
+ };
3783
+ const saved = this.#persist(next);
3784
+ if (!saved) return candidate;
3785
+ return saved.repairs.some((repair) => repairIdentity(repair) === identity) ? candidate : void 0;
3786
+ }
3787
+ #persist(table) {
3788
+ try {
3789
+ this.#table = saveRepairTable(this.directory, table);
3790
+ this.#storeError = void 0;
3791
+ return this.#table;
3792
+ } catch (error) {
3793
+ this.#table = table;
3794
+ this.#storeError = errorMessage3(error);
3795
+ return void 0;
3796
+ }
3797
+ }
3798
+ };
3799
+
3327
3800
  // src/speculation/eligibility.ts
3328
3801
  var TIER_A_SPECULATION_REFS = /* @__PURE__ */ new Set([
3329
3802
  "pi.read",
@@ -3365,11 +3838,11 @@ var isSpeculationEligible = (action, mcpAllowlist) => {
3365
3838
  };
3366
3839
 
3367
3840
  // src/speculation/freshness.ts
3368
- import fs2 from "node:fs";
3369
- import path2 from "node:path";
3841
+ import fs3 from "node:fs";
3842
+ import path3 from "node:path";
3370
3843
  var statSignature = (filePath) => {
3371
3844
  try {
3372
- const stats = fs2.statSync(filePath);
3845
+ const stats = fs3.statSync(filePath);
3373
3846
  return { mtimeMs: stats.mtimeMs, size: stats.size };
3374
3847
  } catch (error) {
3375
3848
  const code = error instanceof Error && "code" in error ? error.code : void 0;
@@ -3380,7 +3853,7 @@ var signatureEquals = (left, right) => left === "missing" || right === "missing"
3380
3853
  var readFreshness = (args, cwd) => {
3381
3854
  const target = args.path;
3382
3855
  if (typeof target !== "string" || target.length === 0) return void 0;
3383
- const resolved = path2.resolve(cwd, target);
3856
+ const resolved = path3.resolve(cwd, target);
3384
3857
  const snapshot = statSignature(resolved);
3385
3858
  if (snapshot === "error") return void 0;
3386
3859
  return () => {
@@ -3423,23 +3896,24 @@ var FabricSpeculationStore = class _FabricSpeculationStore {
3423
3896
  bumpEpoch() {
3424
3897
  this.#epoch += 1;
3425
3898
  }
3426
- static key(parentToolCallId, ref, preparedArgs) {
3899
+ static key(parentToolCallId, ref, preparedArgs, bindingToken) {
3427
3900
  return `${parentToolCallId}
3428
3901
  ${ref}
3429
- ${stableJsonHash(preparedArgs)}`;
3902
+ ${stableJsonHash(preparedArgs)}
3903
+ ${bindingToken}`;
3430
3904
  }
3431
3905
  /**
3432
3906
  * Register and start a speculative invocation. Returns false when at
3433
3907
  * capacity; the candidate is dropped silently (a miss costs nothing, the
3434
3908
  * real call executes normally later).
3435
3909
  */
3436
- launch(parentToolCallId, ref, preparedArgs, execute, freshness, replay) {
3910
+ launch(parentToolCallId, ref, preparedArgs, execute, freshness, replay, bindingToken) {
3437
3911
  this.#sweepExpired(Date.now());
3438
3912
  if (this.#entries.size >= this.#maxEntries || this.#inFlightCount() >= this.#maxConcurrent) {
3439
3913
  this.#stats.skipped += 1;
3440
3914
  return false;
3441
3915
  }
3442
- const key = _FabricSpeculationStore.key(parentToolCallId, ref, preparedArgs);
3916
+ const key = _FabricSpeculationStore.key(parentToolCallId, ref, preparedArgs, bindingToken);
3443
3917
  if (this.#entries.has(key)) {
3444
3918
  this.#stats.skipped += 1;
3445
3919
  return false;
@@ -3463,8 +3937,8 @@ ${stableJsonHash(preparedArgs)}`;
3463
3937
  this.#stats.launched += 1;
3464
3938
  return true;
3465
3939
  }
3466
- async tryServe(parentToolCallId, ref, preparedArgs) {
3467
- const key = _FabricSpeculationStore.key(parentToolCallId, ref, preparedArgs);
3940
+ async tryServe(parentToolCallId, ref, preparedArgs, bindingToken) {
3941
+ const key = _FabricSpeculationStore.key(parentToolCallId, ref, preparedArgs, bindingToken);
3468
3942
  const entry = this.#entries.get(key);
3469
3943
  if (!entry || entry.parentToolCallId !== parentToolCallId) {
3470
3944
  return { hit: false, reason: "absent" };
@@ -3789,12 +4263,12 @@ var LifecycleDeliveryScheduler = class {
3789
4263
 
3790
4264
  // src/core/model-usage.ts
3791
4265
  import { readFileSync } from "node:fs";
3792
- import path3 from "node:path";
4266
+ import path4 from "node:path";
3793
4267
  var MODEL_USAGE_FILENAME = "pi-model-sort.json";
3794
4268
  var loadModelUsage = (agentDir = resolveAgentDir()) => {
3795
4269
  try {
3796
4270
  const raw = JSON.parse(
3797
- readFileSync(path3.join(agentDir, "extensions", MODEL_USAGE_FILENAME), "utf8")
4271
+ readFileSync(path4.join(agentDir, "extensions", MODEL_USAGE_FILENAME), "utf8")
3798
4272
  );
3799
4273
  const lastUsed = raw?.lastUsed;
3800
4274
  if (typeof lastUsed !== "object" || lastUsed === null) return {};
@@ -4467,10 +4941,9 @@ var AGENTS_ACTION_DESCRIPTORS = [
4467
4941
  ];
4468
4942
 
4469
4943
  // src/residency/client.ts
4470
- import { randomUUID as randomUUID2 } from "node:crypto";
4471
- import fs3 from "node:fs";
4472
- import os from "node:os";
4473
- import path4 from "node:path";
4944
+ import { randomUUID as randomUUID3 } from "node:crypto";
4945
+ import fs4 from "node:fs";
4946
+ import path5 from "node:path";
4474
4947
  import { fileURLToPath } from "node:url";
4475
4948
  var STARTUP_TIMEOUT_MS = 3e4;
4476
4949
  var COMMAND_TIMEOUT_MS = 3e4;
@@ -4482,7 +4955,7 @@ var atomicWrite2 = (filePath, value) => {
4482
4955
  };
4483
4956
  var readJson = (filePath) => {
4484
4957
  try {
4485
- return JSON.parse(fs3.readFileSync(filePath, "utf8"));
4958
+ return JSON.parse(fs4.readFileSync(filePath, "utf8"));
4486
4959
  } catch {
4487
4960
  return void 0;
4488
4961
  }
@@ -4490,7 +4963,7 @@ var readJson = (filePath) => {
4490
4963
  var terminal = (status) => status === "completed" || status === "failed" || status === "stopped" || status === "timed_out";
4491
4964
  var samePath = (left, right) => {
4492
4965
  try {
4493
- return path4.relative(fs3.realpathSync.native(left), fs3.realpathSync.native(right)) === "";
4966
+ return path5.relative(fs4.realpathSync.native(left), fs4.realpathSync.native(right)) === "";
4494
4967
  } catch {
4495
4968
  return false;
4496
4969
  }
@@ -4519,12 +4992,12 @@ var ResidencyClient = class {
4519
4992
  constructor(options) {
4520
4993
  this.options = options;
4521
4994
  this.hostId = residentHostId(options.config.rootId);
4522
- this.#configPath = path4.join(options.config.residencyRoot, "config.json");
4523
- this.#ownerPath = path4.join(options.config.residencyRoot, "owner.json");
4524
- this.#errorPath = path4.join(options.config.residencyRoot, "error.json");
4525
- this.#requestsPath = path4.join(options.config.residencyRoot, "requests");
4526
- this.#responsesPath = path4.join(options.config.residencyRoot, "responses");
4527
- this.#agentsPath = path4.join(options.config.residencyRoot, "agents");
4995
+ this.#configPath = path5.join(options.config.residencyRoot, "config.json");
4996
+ this.#ownerPath = path5.join(options.config.residencyRoot, "owner.json");
4997
+ this.#errorPath = path5.join(options.config.residencyRoot, "error.json");
4998
+ this.#requestsPath = path5.join(options.config.residencyRoot, "requests");
4999
+ this.#responsesPath = path5.join(options.config.residencyRoot, "responses");
5000
+ this.#agentsPath = path5.join(options.config.residencyRoot, "agents");
4528
5001
  this.#deliveryPrefix = residentDeliveryPrefix(options.config.rootId);
4529
5002
  this.#hostPath = options.hostPath ?? fileURLToPath(new URL("./launcher.js", import.meta.url));
4530
5003
  }
@@ -4562,7 +5035,7 @@ var ResidencyClient = class {
4562
5035
  if (serialized === this.#modelGuidanceJson) return;
4563
5036
  this.#modelGuidanceJson = serialized;
4564
5037
  this.options.config.modelGuidance = snapshot;
4565
- if (fs3.existsSync(this.options.config.residencyRoot)) {
5038
+ if (fs4.existsSync(this.options.config.residencyRoot)) {
4566
5039
  atomicWrite2(this.#configPath, this.options.config);
4567
5040
  }
4568
5041
  }
@@ -4571,7 +5044,7 @@ var ResidencyClient = class {
4571
5044
  atomicWrite2(this.#configPath, this.options.config);
4572
5045
  const existing = this.#liveOwner();
4573
5046
  if (existing) return existing;
4574
- fs3.rmSync(this.#errorPath, { force: true });
5047
+ fs4.rmSync(this.#errorPath, { force: true });
4575
5048
  await spawnDetached(
4576
5049
  this.#hostPath,
4577
5050
  ["--config", this.#configPath],
@@ -4589,7 +5062,7 @@ var ResidencyClient = class {
4589
5062
  }
4590
5063
  const readIfPresent = (name) => {
4591
5064
  try {
4592
- return fs3.readFileSync(path4.join(this.options.config.residencyRoot, name), "utf8").trim();
5065
+ return fs4.readFileSync(path5.join(this.options.config.residencyRoot, name), "utf8").trim();
4593
5066
  } catch {
4594
5067
  return "";
4595
5068
  }
@@ -4616,7 +5089,7 @@ var ResidencyClient = class {
4616
5089
  {
4617
5090
  format: RESIDENT_HOST_FORMAT,
4618
5091
  operation: "spawn",
4619
- requestId: randomUUID2(),
5092
+ requestId: randomUUID3(),
4620
5093
  rootId: this.options.config.rootId,
4621
5094
  request: { ...resolvedRequest, residency: "durable" },
4622
5095
  createdAt: Date.now()
@@ -4628,18 +5101,18 @@ var ResidencyClient = class {
4628
5101
  return response.handle;
4629
5102
  }
4630
5103
  hasAgent(id) {
4631
- return AGENT_ID_PATTERN.test(id) && fs3.existsSync(this.#metadataPath(id));
5104
+ return AGENT_ID_PATTERN.test(id) && fs4.existsSync(this.#metadataPath(id));
4632
5105
  }
4633
5106
  statusAgent(id) {
4634
5107
  const metadata = this.#metadata(id);
4635
5108
  if (!metadata) throw new Error(`Unknown durable Fabric agent: ${id}`);
4636
- const record = readJson(path4.join(metadata.runDirectory, "status.json"));
5109
+ const record = readJson(path5.join(metadata.runDirectory, "status.json"));
4637
5110
  if (!record || record.id !== metadata.id) return structuredClone(metadata.handle);
4638
5111
  return {
4639
5112
  ...record,
4640
5113
  cwd: metadata.handle.cwd,
4641
5114
  residency: "durable",
4642
- logFile: path4.join(metadata.runDirectory, "events.jsonl"),
5115
+ logFile: path5.join(metadata.runDirectory, "events.jsonl"),
4643
5116
  ...metadata.handle.sessionId ? { sessionId: metadata.handle.sessionId } : {},
4644
5117
  ...metadata.handle.attachCommand ? { attachCommand: metadata.handle.attachCommand } : {}
4645
5118
  };
@@ -4647,7 +5120,7 @@ var ResidencyClient = class {
4647
5120
  listAgents() {
4648
5121
  let entries;
4649
5122
  try {
4650
- entries = fs3.readdirSync(this.#agentsPath);
5123
+ entries = fs4.readdirSync(this.#agentsPath);
4651
5124
  } catch {
4652
5125
  return [];
4653
5126
  }
@@ -4664,7 +5137,7 @@ var ResidencyClient = class {
4664
5137
  await this.#command({
4665
5138
  format: RESIDENT_HOST_FORMAT,
4666
5139
  operation: "foreground",
4667
- requestId: randomUUID2(),
5140
+ requestId: randomUUID3(),
4668
5141
  rootId: this.options.config.rootId,
4669
5142
  id,
4670
5143
  createdAt: Date.now()
@@ -4680,9 +5153,9 @@ var ResidencyClient = class {
4680
5153
  readAgentLog(id, options = {}) {
4681
5154
  const metadata = this.#metadata(id);
4682
5155
  if (!metadata) throw new Error(`Unknown durable Fabric agent: ${id}`);
4683
- const logFile = path4.join(metadata.runDirectory, "events.jsonl");
5156
+ const logFile = path5.join(metadata.runDirectory, "events.jsonl");
4684
5157
  const page = readJsonlPage(logFile, Math.max(1, Math.min(options.lines ?? 200, 5e3)), options.before);
4685
- const status = readJson(path4.join(metadata.runDirectory, "status.json"));
5158
+ const status = readJson(path5.join(metadata.runDirectory, "status.json"));
4686
5159
  return {
4687
5160
  id,
4688
5161
  runDirectory: metadata.runDirectory,
@@ -4698,7 +5171,7 @@ var ResidencyClient = class {
4698
5171
  await this.#command({
4699
5172
  format: RESIDENT_HOST_FORMAT,
4700
5173
  operation: "removeActor",
4701
- requestId: randomUUID2(),
5174
+ requestId: randomUUID3(),
4702
5175
  rootId: this.options.config.rootId,
4703
5176
  id,
4704
5177
  createdAt: Date.now()
@@ -4714,7 +5187,7 @@ var ResidencyClient = class {
4714
5187
  response = await this.#command({
4715
5188
  format: RESIDENT_HOST_FORMAT,
4716
5189
  operation: "cleanup",
4717
- requestId: randomUUID2(),
5190
+ requestId: randomUUID3(),
4718
5191
  rootId: this.options.config.rootId,
4719
5192
  id,
4720
5193
  deleteBranch,
@@ -4752,19 +5225,19 @@ var ResidencyClient = class {
4752
5225
  } else if (deleteBranch) {
4753
5226
  throw new Error(`Durable Fabric agent ${metadata.id} has no worktree branch to delete`);
4754
5227
  }
4755
- fs3.rmSync(metadata.runDirectory, { recursive: true, force: true });
4756
- fs3.rmSync(this.#metadataPath(metadata.id), { force: true });
5228
+ fs4.rmSync(metadata.runDirectory, { recursive: true, force: true });
5229
+ fs4.rmSync(this.#metadataPath(metadata.id), { force: true });
4757
5230
  return { cleaned: true };
4758
5231
  }
4759
5232
  async #command(command, signal) {
4760
- const responsePath = path4.join(this.#responsesPath, `${command.requestId}.json`);
4761
- atomicWrite2(path4.join(this.#requestsPath, `${command.requestId}.json`), command);
5233
+ const responsePath = path5.join(this.#responsesPath, `${command.requestId}.json`);
5234
+ atomicWrite2(path5.join(this.#requestsPath, `${command.requestId}.json`), command);
4762
5235
  const deadline = Date.now() + COMMAND_TIMEOUT_MS;
4763
5236
  while (Date.now() < deadline) {
4764
5237
  if (signal?.aborted) throw new Error("Fabric residency request was aborted");
4765
5238
  const response = readJson(responsePath);
4766
5239
  if (response?.format === RESIDENT_HOST_FORMAT && response.requestId === command.requestId) {
4767
- fs3.rmSync(responsePath, { force: true });
5240
+ fs4.rmSync(responsePath, { force: true });
4768
5241
  if (!response.ok) throw new Error(response.error ?? "Fabric resident host rejected request");
4769
5242
  return response;
4770
5243
  }
@@ -4786,15 +5259,15 @@ var ResidencyClient = class {
4786
5259
  throw new Error(`Timed out publishing durable Fabric ${kind} ${id} from ${this.hostId}`);
4787
5260
  }
4788
5261
  #metadataPath(id) {
4789
- return path4.join(this.#agentsPath, `${id}.json`);
5262
+ return path5.join(this.#agentsPath, `${id}.json`);
4790
5263
  }
4791
5264
  #metadata(id) {
4792
5265
  if (!AGENT_ID_PATTERN.test(id)) return void 0;
4793
5266
  const metadata = readJson(this.#metadataPath(id));
4794
- if (metadata?.format !== RESIDENT_HOST_FORMAT || metadata.rootId !== this.options.config.rootId || metadata.id !== id || metadata.handle.id !== id || metadata.worktreeGitRoot !== void 0 && typeof metadata.worktreeGitRoot !== "string" || path4.resolve(metadata.runDirectory) !== path4.resolve(this.options.config.residencyRoot, "runs", id)) {
5267
+ if (metadata?.format !== RESIDENT_HOST_FORMAT || metadata.rootId !== this.options.config.rootId || metadata.id !== id || metadata.handle.id !== id || metadata.worktreeGitRoot !== void 0 && typeof metadata.worktreeGitRoot !== "string" || path5.resolve(metadata.runDirectory) !== path5.resolve(this.options.config.residencyRoot, "runs", id)) {
4795
5268
  return void 0;
4796
5269
  }
4797
- if (metadata.handle.worktree && path4.resolve(metadata.handle.worktree) !== path4.resolve(os.tmpdir(), "pi-fabric-worktrees", id)) {
5270
+ if (metadata.handle.worktree && !isFabricWorktreePath(metadata.handle.worktree, id)) {
4798
5271
  return void 0;
4799
5272
  }
4800
5273
  if (metadata.handle.branch && (!metadata.handle.branch.startsWith("pi-fabric/") || !metadata.handle.branch.endsWith(`-${id.slice(0, 8)}`))) {
@@ -6098,16 +6571,16 @@ var AgentsProvider = class {
6098
6571
  };
6099
6572
 
6100
6573
  // src/providers/captured-tools-provider.ts
6101
- import path5 from "node:path";
6574
+ import path6 from "node:path";
6102
6575
  var textFromContent2 = (content) => content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
6103
6576
  var sourceLabel = (sourceInfo) => {
6104
6577
  if (sourceInfo.path.startsWith("<")) return sourceInfo.source;
6105
6578
  const segments = sourceInfo.path.split(/[\\/]/);
6106
6579
  const packageSegment = [...segments].reverse().find((segment) => segment.startsWith("pi-"));
6107
6580
  if (packageSegment) return packageSegment;
6108
- const filename = path5.basename(sourceInfo.path).replace(/\.[^.]+$/, "");
6581
+ const filename = path6.basename(sourceInfo.path).replace(/\.[^.]+$/, "");
6109
6582
  if (filename && filename !== "index") return filename;
6110
- return path5.basename(path5.dirname(sourceInfo.path)) || sourceInfo.source;
6583
+ return path6.basename(path6.dirname(sourceInfo.path)) || sourceInfo.source;
6111
6584
  };
6112
6585
  var capturedToolNamespace = (entry) => `extension:${sourceLabel(entry.sourceInfo)}`;
6113
6586
  var descriptorFrom = (entry) => ({
@@ -6484,35 +6957,35 @@ var ComponentsProvider = class {
6484
6957
  };
6485
6958
 
6486
6959
  // src/providers/mcp-descriptor-cache.ts
6487
- import fs4 from "node:fs/promises";
6960
+ import fs5 from "node:fs/promises";
6488
6961
  import fsSync from "node:fs";
6489
- import os2 from "node:os";
6490
- import path6 from "node:path";
6962
+ import os from "node:os";
6963
+ import path7 from "node:path";
6491
6964
  var MCP_DESCRIPTOR_CACHE_VERSION = 1;
6492
6965
  var expandHome = (input) => {
6493
6966
  if (!input.startsWith("~")) return input;
6494
- const home = os2.homedir();
6967
+ const home = os.homedir();
6495
6968
  if (input === "~") return home;
6496
6969
  if (input.startsWith("~/") || input.startsWith("~\\")) {
6497
- return path6.join(home, input.slice(2));
6970
+ return path7.join(home, input.slice(2));
6498
6971
  }
6499
6972
  return input;
6500
6973
  };
6501
- var legacyMcporterDir = () => path6.join(os2.homedir(), ".mcporter");
6974
+ var legacyMcporterDir = () => path7.join(os.homedir(), ".mcporter");
6502
6975
  var mcporterConfigDir = () => {
6503
6976
  const raw = process.env.XDG_CONFIG_HOME;
6504
6977
  if (raw && raw.trim().length > 0) {
6505
6978
  const resolved = expandHome(raw.trim());
6506
- if (path6.isAbsolute(resolved)) return path6.join(resolved, "mcporter");
6979
+ if (path7.isAbsolute(resolved)) return path7.join(resolved, "mcporter");
6507
6980
  }
6508
6981
  return legacyMcporterDir();
6509
6982
  };
6510
6983
  var mcporterConfigCandidates = () => {
6511
6984
  const base = mcporterConfigDir();
6512
- const candidates = [path6.join(base, "mcporter.json"), path6.join(base, "mcporter.jsonc")];
6985
+ const candidates = [path7.join(base, "mcporter.json"), path7.join(base, "mcporter.jsonc")];
6513
6986
  const legacy = legacyMcporterDir();
6514
6987
  if (base !== legacy) {
6515
- candidates.push(path6.join(legacy, "mcporter.json"), path6.join(legacy, "mcporter.jsonc"));
6988
+ candidates.push(path7.join(legacy, "mcporter.json"), path7.join(legacy, "mcporter.jsonc"));
6516
6989
  }
6517
6990
  return candidates;
6518
6991
  };
@@ -6527,12 +7000,12 @@ var pathExists = (filePath) => {
6527
7000
  var mcpConfigLayerPaths = (rootDir, configPath) => {
6528
7001
  const explicitRaw = configPath ?? process.env.MCPORTER_CONFIG;
6529
7002
  if (explicitRaw && explicitRaw.trim().length > 0) {
6530
- return [path6.resolve(expandHome(explicitRaw.trim()))];
7003
+ return [path7.resolve(expandHome(explicitRaw.trim()))];
6531
7004
  }
6532
7005
  const paths = [];
6533
7006
  const home = mcporterConfigCandidates().find(pathExists);
6534
7007
  if (home) paths.push(home);
6535
- const projectPath = path6.resolve(rootDir, "config", "mcporter.json");
7008
+ const projectPath = path7.resolve(rootDir, "config", "mcporter.json");
6536
7009
  if (pathExists(projectPath)) paths.push(projectPath);
6537
7010
  return paths;
6538
7011
  };
@@ -6540,7 +7013,7 @@ var statConfigLayers = async (rootDir, configPath) => {
6540
7013
  const stats = await Promise.all(
6541
7014
  mcpConfigLayerPaths(rootDir, configPath).map(async (layerPath) => {
6542
7015
  try {
6543
- const stat2 = await fs4.stat(layerPath);
7016
+ const stat2 = await fs5.stat(layerPath);
6544
7017
  return { path: layerPath, mtimeMs: stat2.mtimeMs, size: stat2.size };
6545
7018
  } catch {
6546
7019
  return void 0;
@@ -6554,11 +7027,11 @@ var sameConfigLayers = (left, right) => left.length === right.length && left.eve
6554
7027
  return other !== void 0 && layer.path === other.path && layer.size === other.size && Math.abs(layer.mtimeMs - other.mtimeMs) <= 1;
6555
7028
  });
6556
7029
  var hashServerDefinition = (definition) => stableJsonHash(definition);
6557
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7030
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
6558
7031
  var parseCachedServer = (value) => {
6559
- if (!isRecord(value)) return void 0;
7032
+ if (!isRecord2(value)) return void 0;
6560
7033
  const tools = Array.isArray(value.tools) ? value.tools.filter(
6561
- (tool) => isRecord(tool) && typeof tool.name === "string"
7034
+ (tool) => isRecord2(tool) && typeof tool.name === "string"
6562
7035
  ) : void 0;
6563
7036
  if (typeof value.definitionHash !== "string" || tools === void 0) return void 0;
6564
7037
  return {
@@ -6571,10 +7044,10 @@ var parseCachedServer = (value) => {
6571
7044
  };
6572
7045
  };
6573
7046
  var parseCacheFile = (value) => {
6574
- if (!isRecord(value) || value.version !== MCP_DESCRIPTOR_CACHE_VERSION) return void 0;
6575
- if (!Array.isArray(value.layers) || !isRecord(value.servers)) return void 0;
7047
+ if (!isRecord2(value) || value.version !== MCP_DESCRIPTOR_CACHE_VERSION) return void 0;
7048
+ if (!Array.isArray(value.layers) || !isRecord2(value.servers)) return void 0;
6576
7049
  const layers = value.layers.filter(
6577
- (layer) => isRecord(layer) && typeof layer.path === "string" && typeof layer.mtimeMs === "number" && typeof layer.size === "number"
7050
+ (layer) => isRecord2(layer) && typeof layer.path === "string" && typeof layer.mtimeMs === "number" && typeof layer.size === "number"
6578
7051
  );
6579
7052
  return {
6580
7053
  version: MCP_DESCRIPTOR_CACHE_VERSION,
@@ -6590,7 +7063,7 @@ var McpDescriptorCacheStore = class {
6590
7063
  }
6591
7064
  async load() {
6592
7065
  try {
6593
- const raw = await fs4.readFile(this.filePath, "utf8");
7066
+ const raw = await fs5.readFile(this.filePath, "utf8");
6594
7067
  return parseCacheFile(JSON.parse(raw));
6595
7068
  } catch {
6596
7069
  return void 0;
@@ -6599,24 +7072,24 @@ var McpDescriptorCacheStore = class {
6599
7072
  // Atomic-ish write: temp file then rename, so a concurrent reader either
6600
7073
  // sees the previous cache or the next one, never a torn document.
6601
7074
  async save(file) {
6602
- const directory = path6.dirname(this.filePath);
6603
- await fs4.mkdir(directory, { recursive: true });
6604
- const tempPath = path6.join(
7075
+ const directory = path7.dirname(this.filePath);
7076
+ await fs5.mkdir(directory, { recursive: true });
7077
+ const tempPath = path7.join(
6605
7078
  directory,
6606
7079
  `.mcp-cache-${process.pid}-${Date.now()}-${tempCounter++}.tmp`
6607
7080
  );
6608
- await fs4.writeFile(tempPath, JSON.stringify(file, null, 2));
7081
+ await fs5.writeFile(tempPath, JSON.stringify(file, null, 2));
6609
7082
  try {
6610
- await fs4.rename(tempPath, this.filePath);
7083
+ await fs5.rename(tempPath, this.filePath);
6611
7084
  } catch (error) {
6612
- await fs4.rm(tempPath, { force: true }).catch(() => void 0);
7085
+ await fs5.rm(tempPath, { force: true }).catch(() => void 0);
6613
7086
  throw error;
6614
7087
  }
6615
7088
  }
6616
7089
  };
6617
7090
 
6618
7091
  // src/providers/mcp-provider.ts
6619
- import path7 from "node:path";
7092
+ import path8 from "node:path";
6620
7093
  var TOOL_METADATA_TTL_MS = 6e4;
6621
7094
  var REVALIDATE_CONCURRENCY = 3;
6622
7095
  var REVALIDATE_SERVER_TIMEOUT_MS = 2e4;
@@ -7248,7 +7721,7 @@ var McpProvider = class {
7248
7721
  kind: "stdio",
7249
7722
  command: args.command,
7250
7723
  args: commandArgs,
7251
- cwd: path7.resolve(this.cwd, typeof args.cwd === "string" ? args.cwd : ".")
7724
+ cwd: path8.resolve(this.cwd, typeof args.cwd === "string" ? args.cwd : ".")
7252
7725
  },
7253
7726
  ...env ? { env } : {}
7254
7727
  };
@@ -7615,7 +8088,7 @@ var MeshProvider = class {
7615
8088
 
7616
8089
  // src/providers/pi-tools-provider.ts
7617
8090
  import { readFileSync as readFileSync2 } from "node:fs";
7618
- import path9 from "node:path";
8091
+ import path11 from "node:path";
7619
8092
  import {
7620
8093
  createBashToolDefinition as createBashToolDefinition2,
7621
8094
  createEditToolDefinition,
@@ -7625,9 +8098,102 @@ import {
7625
8098
  createReadToolDefinition
7626
8099
  } from "@earendil-works/pi-coding-agent";
7627
8100
 
8101
+ // src/agents/bash-worktree-add.ts
8102
+ import fs6 from "node:fs";
8103
+ import path9 from "node:path";
8104
+ var BARE_SAFE = /^[A-Za-z0-9_./:@%+=,-]+$/;
8105
+ var parseGitWorktreeAdd = (command) => {
8106
+ const trimmed = command.trim();
8107
+ if (!trimmed || /[|&;()\n]/.test(trimmed)) return void 0;
8108
+ const tokens = trimmed.split(/\s+/);
8109
+ if (tokens[0] !== "git") return void 0;
8110
+ if (tokens.some((token) => !BARE_SAFE.test(token))) return void 0;
8111
+ let index = 1;
8112
+ let gitCwd;
8113
+ if (tokens[index] === "-C") {
8114
+ gitCwd = tokens[index + 1];
8115
+ if (!gitCwd) return void 0;
8116
+ index += 2;
8117
+ }
8118
+ if (tokens[index] !== "worktree" || tokens[index + 1] !== "add") return void 0;
8119
+ index += 2;
8120
+ let branch;
8121
+ let detach = false;
8122
+ let quiet = false;
8123
+ let flagsEnded = false;
8124
+ const positionals = [];
8125
+ while (index < tokens.length) {
8126
+ const token = tokens[index++];
8127
+ if (token === void 0) break;
8128
+ if (!flagsEnded && token === "--") {
8129
+ flagsEnded = true;
8130
+ continue;
8131
+ }
8132
+ if (!flagsEnded && (token === "-b" || token === "-B")) {
8133
+ if (branch) return void 0;
8134
+ const name = tokens[index++];
8135
+ if (!name) return void 0;
8136
+ branch = { flag: token, name };
8137
+ continue;
8138
+ }
8139
+ if (!flagsEnded && (token === "--detach" || token === "-d")) {
8140
+ detach = true;
8141
+ continue;
8142
+ }
8143
+ if (!flagsEnded && (token === "-q" || token === "--quiet")) {
8144
+ quiet = true;
8145
+ continue;
8146
+ }
8147
+ if (!flagsEnded && token.startsWith("-")) return void 0;
8148
+ positionals.push(token);
8149
+ }
8150
+ if (detach && branch) return void 0;
8151
+ if (positionals.length < 1 || positionals.length > 2) return void 0;
8152
+ return {
8153
+ ...gitCwd ? { gitCwd } : {},
8154
+ ...branch ? { branch } : {},
8155
+ detach,
8156
+ quiet,
8157
+ dest: positionals[0],
8158
+ ...positionals[1] ? { startPoint: positionals[1] } : {}
8159
+ };
8160
+ };
8161
+ var tryExecuteGitWorktreeAdd = async (args, sessionCwd) => {
8162
+ const command = args.command;
8163
+ if (typeof command !== "string") return void 0;
8164
+ const parsed = parseGitWorktreeAdd(command);
8165
+ if (!parsed) return void 0;
8166
+ const gitCwd = parsed.gitCwd ? path9.resolve(sessionCwd, parsed.gitCwd) : typeof args.cwd === "string" ? args.cwd : sessionCwd;
8167
+ const dest = path9.resolve(gitCwd, parsed.dest);
8168
+ let gitRoot;
8169
+ try {
8170
+ const root = await executeFile("git", ["rev-parse", "--show-toplevel"], {
8171
+ cwd: gitCwd,
8172
+ timeoutMs: 1e4
8173
+ });
8174
+ const output = root.stdout.trim();
8175
+ if (!output) return void 0;
8176
+ gitRoot = fs6.realpathSync(output);
8177
+ } catch {
8178
+ return void 0;
8179
+ }
8180
+ const result = await addCloneFirstWorktree({
8181
+ gitRoot,
8182
+ dest,
8183
+ ...parsed.branch ? { branch: parsed.branch } : {},
8184
+ detach: parsed.detach,
8185
+ quiet: parsed.quiet,
8186
+ ...parsed.startPoint ? { startPoint: parsed.startPoint } : {}
8187
+ });
8188
+ return {
8189
+ content: [{ type: "text", text: result.message || `Preparing worktree (${dest})` }],
8190
+ details: { exitCode: 0, cloned: result.cloned }
8191
+ };
8192
+ };
8193
+
7628
8194
  // src/providers/pi-bash-cwd.ts
7629
8195
  import { accessSync, constants, statSync } from "node:fs";
7630
- import path8 from "node:path";
8196
+ import path10 from "node:path";
7631
8197
  import { createBashToolDefinition } from "@earendil-works/pi-coding-agent";
7632
8198
  import { Type as Type2 } from "typebox";
7633
8199
  var PI_BASH_CWD_KEY = "cwd";
@@ -7637,7 +8203,7 @@ var resolvePiBashCwd = (sessionCwd, requested) => {
7637
8203
  `Invalid pi.bash cwd ${JSON.stringify(requested)}: path must be a non-empty string`
7638
8204
  );
7639
8205
  }
7640
- const resolved = path8.isAbsolute(requested) ? path8.resolve(requested) : path8.resolve(sessionCwd, requested);
8206
+ const resolved = path10.isAbsolute(requested) ? path10.resolve(requested) : path10.resolve(sessionCwd, requested);
7641
8207
  try {
7642
8208
  if (!statSync(resolved).isDirectory()) throw new Error("path is not a directory");
7643
8209
  accessSync(resolved, constants.R_OK | constants.X_OK);
@@ -7653,9 +8219,9 @@ var CWD_PROPERTY = Type2.Optional(
7653
8219
  description: "Execution directory for this command; relative paths resolve from the session cwd."
7654
8220
  })
7655
8221
  );
7656
- var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
8222
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7657
8223
  var withBashCwdSchema = (schema) => {
7658
- if (!isRecord2(schema) || !isRecord2(schema.properties)) return schema;
8224
+ if (!isRecord3(schema) || !isRecord3(schema.properties)) return schema;
7659
8225
  if (Object.hasOwn(schema.properties, PI_BASH_CWD_KEY)) return schema;
7660
8226
  const { type: _type, properties, required, ...rest } = schema;
7661
8227
  return Type2.Object(
@@ -7736,14 +8302,14 @@ var createPreviewWriteToolDefinition = (cwd) => {
7736
8302
  return {
7737
8303
  ...original,
7738
8304
  async execute(_toolCallId, params, signal) {
7739
- const { path: path15, content } = params;
7740
- const absolutePath = resolvePreviewPath(path15, cwd);
8305
+ const { path: path17, content } = params;
8306
+ const absolutePath = resolvePreviewPath(path17, cwd);
7741
8307
  return withFileMutationQueue(absolutePath, async () => {
7742
8308
  const throwIfAborted2 = () => {
7743
8309
  if (signal?.aborted) throw new Error("Operation aborted");
7744
8310
  };
7745
8311
  throwIfAborted2();
7746
- const before = await readExistingFileForPreview(path15, cwd, content);
8312
+ const before = await readExistingFileForPreview(path17, cwd, content);
7747
8313
  throwIfAborted2();
7748
8314
  await mkdir(dirname(absolutePath), { recursive: true });
7749
8315
  throwIfAborted2();
@@ -7753,7 +8319,7 @@ var createPreviewWriteToolDefinition = (cwd) => {
7753
8319
  content: [
7754
8320
  {
7755
8321
  type: "text",
7756
- text: `Successfully wrote ${Buffer.byteLength(content, "utf8")} bytes to ${path15}`
8322
+ text: `Successfully wrote ${Buffer.byteLength(content, "utf8")} bytes to ${path17}`
7757
8323
  }
7758
8324
  ],
7759
8325
  details: { codePreviewBeforeWrite: before }
@@ -7787,7 +8353,7 @@ var expandReplaceAllEdit = (cwd, args, allForEveryEdit) => {
7787
8353
  }
7788
8354
  return { oldText, newText, all: allForEveryEdit || record.all === true };
7789
8355
  });
7790
- const resolvedPath = path9.resolve(
8356
+ const resolvedPath = path11.resolve(
7791
8357
  cwd,
7792
8358
  filePath.startsWith("@") ? filePath.slice(1) : filePath
7793
8359
  );
@@ -7929,6 +8495,13 @@ var PiToolsProvider = class {
7929
8495
  async invoke(actionName, args, context) {
7930
8496
  if (!(actionName in this.#tools)) throw new Error(`Unknown Pi tool: ${actionName}`);
7931
8497
  const name = actionName;
8498
+ if (name === "bash") {
8499
+ const intercepted = await tryExecuteGitWorktreeAdd(args, this.#cwd);
8500
+ if (intercepted) {
8501
+ this.#attachPreview(name, intercepted, args, context);
8502
+ return this.#normalizeResult(name, intercepted, args);
8503
+ }
8504
+ }
7932
8505
  if (this.#catalog?.get(name)) {
7933
8506
  const result = await this.#capturedTools.invoke(name, args, context);
7934
8507
  this.#attachReadMedia(name, result, context);
@@ -8148,9 +8721,9 @@ var PiToolsProvider = class {
8148
8721
 
8149
8722
  // src/schema/controller.ts
8150
8723
  import { spawn } from "node:child_process";
8151
- import { createHash as createHash3, randomBytes, randomUUID as randomUUID3 } from "node:crypto";
8152
- import fs6 from "node:fs";
8153
- import path11 from "node:path";
8724
+ import { createHash as createHash3, randomBytes, randomUUID as randomUUID4 } from "node:crypto";
8725
+ import fs8 from "node:fs";
8726
+ import path13 from "node:path";
8154
8727
 
8155
8728
  // src/schema/types.ts
8156
8729
  var stateBinding = (head) => head ? { transitionId: head.transitionId, version: head.version, to: head.to } : null;
@@ -8158,8 +8731,8 @@ var stateBinding = (head) => head ? { transitionId: head.transitionId, version:
8158
8731
  // src/schema/workspace.ts
8159
8732
  import { execFileSync } from "node:child_process";
8160
8733
  import { createHash as createHash2 } from "node:crypto";
8161
- import fs5 from "node:fs";
8162
- import path10 from "node:path";
8734
+ import fs7 from "node:fs";
8735
+ import path12 from "node:path";
8163
8736
  var SNAPSHOT_MAX_FILES = 2e4;
8164
8737
  var SNAPSHOT_MAX_BYTES = 512 * 1024 * 1024;
8165
8738
  var FALLBACK_SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([
@@ -8177,16 +8750,16 @@ var git = (cwd, args) => execFileSync("git", ["-C", cwd, ...args], {
8177
8750
  stdio: ["ignore", "pipe", "ignore"]
8178
8751
  });
8179
8752
  var nulPaths = (buffer) => buffer.toString("utf8").split("\0").filter((item) => item.length > 0);
8180
- var normalizeRelative = (value) => value.split(path10.sep).join("/");
8753
+ var normalizeRelative = (value) => value.split(path12.sep).join("/");
8181
8754
  var isInside = (root, candidate) => {
8182
- const relative = path10.relative(root, candidate);
8183
- return relative === "" || !path10.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path10.sep}`);
8755
+ const relative = path12.relative(root, candidate);
8756
+ return relative === "" || !path12.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path12.sep}`);
8184
8757
  };
8185
8758
  var excluded = (absolute, exclusions) => exclusions.some((root) => isInside(root, absolute));
8186
8759
  var entryDigest = (absolute) => {
8187
8760
  let stat2;
8188
8761
  try {
8189
- stat2 = fs5.lstatSync(absolute);
8762
+ stat2 = fs7.lstatSync(absolute);
8190
8763
  } catch (error) {
8191
8764
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
8192
8765
  return { marker: "absent", bytes: 0 };
@@ -8194,24 +8767,24 @@ var entryDigest = (absolute) => {
8194
8767
  throw error;
8195
8768
  }
8196
8769
  if (stat2.isSymbolicLink()) {
8197
- return { marker: `symlink:${digest(fs5.readlinkSync(absolute))}`, bytes: 0 };
8770
+ return { marker: `symlink:${digest(fs7.readlinkSync(absolute))}`, bytes: 0 };
8198
8771
  }
8199
8772
  if (!stat2.isFile()) {
8200
8773
  throw new Error(`Schema workspace snapshot does not support non-file entry: ${absolute}`);
8201
8774
  }
8202
- return { marker: `file:${stat2.mode & 511}:${digest(fs5.readFileSync(absolute))}`, bytes: stat2.size };
8775
+ return { marker: `file:${stat2.mode & 511}:${digest(fs7.readFileSync(absolute))}`, bytes: stat2.size };
8203
8776
  };
8204
8777
  var buildSnapshot = (cwd, paths, metadata, exclusions) => {
8205
8778
  const entries = {};
8206
8779
  let bytes = 0;
8207
8780
  const unique = [...new Set(paths)].sort((left, right) => left.localeCompare(right));
8208
8781
  for (const relativeInput of unique) {
8209
- const absolute = path10.resolve(cwd, relativeInput);
8782
+ const absolute = path12.resolve(cwd, relativeInput);
8210
8783
  if (!isInside(cwd, absolute) || excluded(absolute, exclusions)) continue;
8211
8784
  if (Object.keys(entries).length >= SNAPSHOT_MAX_FILES) {
8212
8785
  throw new Error(`Schema workspace snapshot exceeds ${SNAPSHOT_MAX_FILES} files`);
8213
8786
  }
8214
- const relative = normalizeRelative(path10.relative(cwd, absolute));
8787
+ const relative = normalizeRelative(path12.relative(cwd, absolute));
8215
8788
  const entry = entryDigest(absolute);
8216
8789
  bytes += entry.bytes;
8217
8790
  if (bytes > SNAPSHOT_MAX_BYTES) {
@@ -8237,12 +8810,12 @@ var buildSnapshot = (cwd, paths, metadata, exclusions) => {
8237
8810
  var fallbackPaths = (cwd, exclusions) => {
8238
8811
  const paths = [];
8239
8812
  const visit = (directory) => {
8240
- const entries = fs5.readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
8813
+ const entries = fs7.readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
8241
8814
  for (const entry of entries) {
8242
8815
  if (FALLBACK_SKIPPED_DIRECTORIES.has(entry.name)) continue;
8243
- const absolute = path10.join(directory, entry.name);
8816
+ const absolute = path12.join(directory, entry.name);
8244
8817
  if (excluded(absolute, exclusions)) continue;
8245
- const relative = normalizeRelative(path10.relative(cwd, absolute));
8818
+ const relative = normalizeRelative(path12.relative(cwd, absolute));
8246
8819
  if (entry.isDirectory()) visit(absolute);
8247
8820
  else paths.push(relative);
8248
8821
  if (paths.length > SNAPSHOT_MAX_FILES) {
@@ -8254,11 +8827,11 @@ var fallbackPaths = (cwd, exclusions) => {
8254
8827
  return paths;
8255
8828
  };
8256
8829
  var snapshotWorkspace = (cwdInput, excludedRoots = []) => {
8257
- const cwd = fs5.realpathSync(cwdInput);
8830
+ const cwd = fs7.realpathSync(cwdInput);
8258
8831
  const exclusions = excludedRoots.map((root) => {
8259
- const absolute = path10.resolve(root);
8832
+ const absolute = path12.resolve(root);
8260
8833
  try {
8261
- return fs5.realpathSync(absolute);
8834
+ return fs7.realpathSync(absolute);
8262
8835
  } catch {
8263
8836
  return absolute;
8264
8837
  }
@@ -8294,29 +8867,29 @@ var snapshotWorkspace = (cwdInput, excludedRoots = []) => {
8294
8867
  );
8295
8868
  };
8296
8869
  var resolveWorkspaceFile = (cwdInput, requestedPath, options) => {
8297
- if (!requestedPath.trim() || path10.isAbsolute(requestedPath)) {
8870
+ if (!requestedPath.trim() || path12.isAbsolute(requestedPath)) {
8298
8871
  throw new Error(`Schema paths must be non-empty project-relative paths: ${requestedPath}`);
8299
8872
  }
8300
- const cwd = fs5.realpathSync(cwdInput);
8301
- const absolute = path10.resolve(cwd, requestedPath);
8873
+ const cwd = fs7.realpathSync(cwdInput);
8874
+ const absolute = path12.resolve(cwd, requestedPath);
8302
8875
  if (!isInside(cwd, absolute) || absolute === cwd) {
8303
8876
  throw new Error(`Schema path escapes the project workspace: ${requestedPath}`);
8304
8877
  }
8305
- const relativeParts = path10.relative(cwd, absolute).split(path10.sep);
8878
+ const relativeParts = path12.relative(cwd, absolute).split(path12.sep);
8306
8879
  let cursor = cwd;
8307
8880
  for (let index = 0; index < relativeParts.length; index++) {
8308
- cursor = path10.join(cursor, relativeParts[index]);
8881
+ cursor = path12.join(cursor, relativeParts[index]);
8309
8882
  let stat2;
8310
8883
  try {
8311
- stat2 = fs5.lstatSync(cursor);
8884
+ stat2 = fs7.lstatSync(cursor);
8312
8885
  } catch (error) {
8313
8886
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
8314
8887
  if (index !== relativeParts.length - 1 || !options.allowAbsent) {
8315
8888
  throw new Error(`Schema path does not exist: ${requestedPath}`);
8316
8889
  }
8317
- const parentReal = fs5.realpathSync(path10.dirname(cursor));
8890
+ const parentReal = fs7.realpathSync(path12.dirname(cursor));
8318
8891
  if (!isInside(cwd, parentReal)) throw new Error(`Schema path parent escapes the workspace: ${requestedPath}`);
8319
- return { absolute, relative: normalizeRelative(path10.relative(cwd, absolute)), exists: false };
8892
+ return { absolute, relative: normalizeRelative(path12.relative(cwd, absolute)), exists: false };
8320
8893
  }
8321
8894
  throw error;
8322
8895
  }
@@ -8328,9 +8901,9 @@ var resolveWorkspaceFile = (cwdInput, requestedPath, options) => {
8328
8901
  throw new Error(`Schema transaction paths must name regular files: ${requestedPath}`);
8329
8902
  }
8330
8903
  }
8331
- return { absolute, relative: normalizeRelative(path10.relative(cwd, absolute)), exists: true };
8904
+ return { absolute, relative: normalizeRelative(path12.relative(cwd, absolute)), exists: true };
8332
8905
  };
8333
- var sha256File = (absolute) => `sha256:${digest(fs5.readFileSync(absolute))}`;
8906
+ var sha256File = (absolute) => `sha256:${digest(fs7.readFileSync(absolute))}`;
8334
8907
 
8335
8908
  // src/schema/controller.ts
8336
8909
  var SCHEMA_TOPIC = "fabric.schema";
@@ -8340,7 +8913,7 @@ var CERTIFICATE_PREFIX = "schema/certificate/";
8340
8913
  var OUTPUT_LIMIT = 64 * 1024;
8341
8914
  var hashToken = (token) => createHash3("sha256").update(token).digest("hex");
8342
8915
  var sameBinding = (left, right) => JSON.stringify(left) === JSON.stringify(right);
8343
- var errorMessage3 = (error) => error instanceof Error ? error.message : String(error);
8916
+ var errorMessage4 = (error) => error instanceof Error ? error.message : String(error);
8344
8917
  var atomicJsonWrite = (filePath, value) => {
8345
8918
  writeJsonAtomic(filePath, value, { newline: true });
8346
8919
  };
@@ -8378,9 +8951,9 @@ var SchemaController = class {
8378
8951
  this.mesh = mesh;
8379
8952
  this.identity = identity;
8380
8953
  this.state = state;
8381
- this.cwd = fs6.realpathSync(cwd);
8382
- this.#journalRoot = path11.join(mesh.root, "schema-transactions");
8383
- this.#lockPath = path11.join(this.#journalRoot, ".commit.lock");
8954
+ this.cwd = fs8.realpathSync(cwd);
8955
+ this.#journalRoot = path13.join(mesh.root, "schema-transactions");
8956
+ this.#lockPath = path13.join(this.#journalRoot, ".commit.lock");
8384
8957
  this.#recoverJournals();
8385
8958
  }
8386
8959
  #activeHypotheses = /* @__PURE__ */ new Map();
@@ -8436,7 +9009,7 @@ var SchemaController = class {
8436
9009
  const generation = this.#generation();
8437
9010
  const now = Date.now();
8438
9011
  const record = {
8439
- id: randomUUID3(),
9012
+ id: randomUUID4(),
8440
9013
  label: input.label,
8441
9014
  summary: input.summary,
8442
9015
  evidence: input.evidence,
@@ -8476,7 +9049,7 @@ var SchemaController = class {
8476
9049
  try {
8477
9050
  before = snapshotWorkspace(this.cwd, [this.mesh.root]);
8478
9051
  } catch (error) {
8479
- return this.#failedVerification(record, [], `workspace snapshot failed: ${errorMessage3(error)}`);
9052
+ return this.#failedVerification(record, [], `workspace snapshot failed: ${errorMessage4(error)}`);
8480
9053
  }
8481
9054
  const currentState = stateBinding(this.state?.getHead() ?? null);
8482
9055
  if (!sameBinding(record.state, currentState)) {
@@ -8493,7 +9066,7 @@ var SchemaController = class {
8493
9066
  try {
8494
9067
  after = snapshotWorkspace(this.cwd, [this.mesh.root]);
8495
9068
  } catch (error) {
8496
- return this.#failedVerification(record, results, `post-evidence snapshot failed: ${errorMessage3(error)}`);
9069
+ return this.#failedVerification(record, results, `post-evidence snapshot failed: ${errorMessage4(error)}`);
8497
9070
  }
8498
9071
  const allConfirmed = results.length > 0 && results.every((result) => result.status === "confirmed");
8499
9072
  if (!allConfirmed || before.fingerprint !== after.fingerprint) {
@@ -8578,8 +9151,8 @@ var SchemaController = class {
8578
9151
  }
8579
9152
  this.#assertPayloadBound(input);
8580
9153
  const release = this.#acquireCommitLock();
8581
- const transactionId = randomUUID3();
8582
- const journalPath = path11.join(this.#journalRoot, `${transactionId}.json`);
9154
+ const transactionId = randomUUID4();
9155
+ const journalPath = path13.join(this.#journalRoot, `${transactionId}.json`);
8583
9156
  let journal;
8584
9157
  let consumed = false;
8585
9158
  let committed = false;
@@ -8619,14 +9192,14 @@ var SchemaController = class {
8619
9192
  before.push({ path: resolved.relative, absolute: resolved.absolute, existed: false });
8620
9193
  continue;
8621
9194
  }
8622
- const content = fs6.readFileSync(resolved.absolute);
9195
+ const content = fs8.readFileSync(resolved.absolute);
8623
9196
  beforeBytes += content.byteLength;
8624
9197
  before.push({
8625
9198
  path: resolved.relative,
8626
9199
  absolute: resolved.absolute,
8627
9200
  existed: true,
8628
9201
  content: content.toString("base64"),
8629
- mode: fs6.statSync(resolved.absolute).mode & 511
9202
+ mode: fs8.statSync(resolved.absolute).mode & 511
8630
9203
  });
8631
9204
  }
8632
9205
  if (payloadBytes + beforeBytes > this.config.maxBytes) {
@@ -8690,7 +9263,7 @@ var SchemaController = class {
8690
9263
  this.cwd
8691
9264
  ) : null;
8692
9265
  } catch (error) {
8693
- stateTransition = { error: errorMessage3(error) };
9266
+ stateTransition = { error: errorMessage4(error) };
8694
9267
  }
8695
9268
  try {
8696
9269
  await this.mesh.put({
@@ -8736,18 +9309,18 @@ var SchemaController = class {
8736
9309
  const outcome = rollbackError ? "quarantined" : "rolled_back";
8737
9310
  if (journal) {
8738
9311
  journal.status = outcome;
8739
- journal.error = rollbackError ? `${errorMessage3(error)}; rollback failed: ${rollbackError}` : errorMessage3(error);
9312
+ journal.error = rollbackError ? `${errorMessage4(error)}; rollback failed: ${rollbackError}` : errorMessage4(error);
8740
9313
  try {
8741
9314
  atomicJsonWrite(journalPath, journal);
8742
9315
  } catch {
8743
9316
  }
8744
9317
  }
8745
- await this.#recordFailedOutcome(outcome, transactionId, errorMessage3(error), rollbackError);
9318
+ await this.#recordFailedOutcome(outcome, transactionId, errorMessage4(error), rollbackError);
8746
9319
  context.update(`Schema transaction ${outcome}`);
8747
9320
  return {
8748
9321
  outcome,
8749
9322
  transactionId,
8750
- error: errorMessage3(error),
9323
+ error: errorMessage4(error),
8751
9324
  ...rollbackError ? { rollbackError } : {}
8752
9325
  };
8753
9326
  } finally {
@@ -8870,7 +9443,7 @@ var SchemaController = class {
8870
9443
  observedSha256: sha256File(resolved.absolute)
8871
9444
  });
8872
9445
  } else if (item.kind === "file_contains") {
8873
- const confirmed = fs6.readFileSync(resolved.absolute, "utf8").includes(item.literal);
9446
+ const confirmed = fs8.readFileSync(resolved.absolute, "utf8").includes(item.literal);
8874
9447
  results.push({
8875
9448
  evidence: item,
8876
9449
  status: confirmed ? "confirmed" : "nonconfirmed",
@@ -8887,7 +9460,7 @@ var SchemaController = class {
8887
9460
  });
8888
9461
  }
8889
9462
  } catch (error) {
8890
- results.push({ evidence: item, status: "error", detail: errorMessage3(error) });
9463
+ results.push({ evidence: item, status: "error", detail: errorMessage4(error) });
8891
9464
  }
8892
9465
  }
8893
9466
  return results;
@@ -8911,7 +9484,7 @@ var SchemaController = class {
8911
9484
  ...signal ? { signal } : {}
8912
9485
  });
8913
9486
  } catch (error) {
8914
- resolve2({ evidence, status: "error", detail: errorMessage3(error), exitCode: null });
9487
+ resolve2({ evidence, status: "error", detail: errorMessage4(error), exitCode: null });
8915
9488
  return;
8916
9489
  }
8917
9490
  const append = (chunk) => {
@@ -8941,22 +9514,22 @@ var SchemaController = class {
8941
9514
  throw new Error(`Schema precondition SHA-256 mismatch: ${operation.path}`);
8942
9515
  }
8943
9516
  }
8944
- fs6.writeFileSync(resolved.absolute, operation.content, "utf8");
9517
+ fs8.writeFileSync(resolved.absolute, operation.content, "utf8");
8945
9518
  return;
8946
9519
  }
8947
9520
  if (sha256File(resolved.absolute) !== operation.expectedSha256) {
8948
9521
  throw new Error(`Schema precondition SHA-256 mismatch: ${operation.path}`);
8949
9522
  }
8950
9523
  if (operation.kind === "delete") {
8951
- fs6.unlinkSync(resolved.absolute);
9524
+ fs8.unlinkSync(resolved.absolute);
8952
9525
  return;
8953
9526
  }
8954
- const content = fs6.readFileSync(resolved.absolute, "utf8");
9527
+ const content = fs8.readFileSync(resolved.absolute, "utf8");
8955
9528
  const first = content.indexOf(operation.oldText);
8956
9529
  if (first < 0 || content.indexOf(operation.oldText, first + operation.oldText.length) >= 0) {
8957
9530
  throw new Error(`Schema edit requires oldText to occur exactly once: ${operation.path}`);
8958
9531
  }
8959
- fs6.writeFileSync(resolved.absolute, `${content.slice(0, first)}${operation.newText}${content.slice(first + operation.oldText.length)}`, "utf8");
9532
+ fs8.writeFileSync(resolved.absolute, `${content.slice(0, first)}${operation.newText}${content.slice(first + operation.oldText.length)}`, "utf8");
8960
9533
  }
8961
9534
  #assertNoOutsideDrift(before, after, declared) {
8962
9535
  if (before.git !== after.git || before.head !== after.head || before.indexDigest !== after.indexDigest) {
@@ -8976,13 +9549,13 @@ var SchemaController = class {
8976
9549
  try {
8977
9550
  const resolved = resolveWorkspaceFile(this.cwd, image.path, { allowAbsent: true });
8978
9551
  if (!image.existed) {
8979
- if (resolved.exists) fs6.unlinkSync(resolved.absolute);
9552
+ if (resolved.exists) fs8.unlinkSync(resolved.absolute);
8980
9553
  } else {
8981
- fs6.writeFileSync(resolved.absolute, Buffer.from(image.content ?? "", "base64"));
8982
- if (image.mode !== void 0) fs6.chmodSync(resolved.absolute, image.mode);
9554
+ fs8.writeFileSync(resolved.absolute, Buffer.from(image.content ?? "", "base64"));
9555
+ if (image.mode !== void 0) fs8.chmodSync(resolved.absolute, image.mode);
8983
9556
  }
8984
9557
  } catch (error) {
8985
- errors.push(`${image.path}: ${errorMessage3(error)}`);
9558
+ errors.push(`${image.path}: ${errorMessage4(error)}`);
8986
9559
  }
8987
9560
  }
8988
9561
  return errors.length > 0 ? errors.join("; ") : void 0;
@@ -9027,25 +9600,25 @@ var SchemaController = class {
9027
9600
  return this.mesh.publish({ topic: SCHEMA_TOPIC, kind, from: this.identity, data });
9028
9601
  }
9029
9602
  #acquireCommitLock() {
9030
- fs6.mkdirSync(this.#journalRoot, { recursive: true, mode: 448 });
9603
+ fs8.mkdirSync(this.#journalRoot, { recursive: true, mode: 448 });
9031
9604
  try {
9032
- const descriptor = fs6.openSync(this.#lockPath, "wx", 384);
9033
- fs6.writeFileSync(descriptor, `${process.pid}
9605
+ const descriptor = fs8.openSync(this.#lockPath, "wx", 384);
9606
+ fs8.writeFileSync(descriptor, `${process.pid}
9034
9607
  ${Date.now()}
9035
9608
  `);
9036
- fs6.closeSync(descriptor);
9609
+ fs8.closeSync(descriptor);
9037
9610
  } catch (error) {
9038
9611
  if (error instanceof Error && "code" in error && error.code === "EEXIST") {
9039
9612
  throw new Error("Another Schema transaction is in progress");
9040
9613
  }
9041
9614
  throw error;
9042
9615
  }
9043
- return () => fs6.rmSync(this.#lockPath, { force: true });
9616
+ return () => fs8.rmSync(this.#lockPath, { force: true });
9044
9617
  }
9045
9618
  #recoverJournals() {
9046
- fs6.mkdirSync(this.#journalRoot, { recursive: true, mode: 448 });
9619
+ fs8.mkdirSync(this.#journalRoot, { recursive: true, mode: 448 });
9047
9620
  try {
9048
- const [pidText] = fs6.readFileSync(this.#lockPath, "utf8").split("\n");
9621
+ const [pidText] = fs8.readFileSync(this.#lockPath, "utf8").split("\n");
9049
9622
  const pid = Number(pidText);
9050
9623
  if (Number.isSafeInteger(pid) && pid > 0) {
9051
9624
  try {
@@ -9054,15 +9627,15 @@ ${Date.now()}
9054
9627
  } catch {
9055
9628
  }
9056
9629
  }
9057
- fs6.rmSync(this.#lockPath, { force: true });
9630
+ fs8.rmSync(this.#lockPath, { force: true });
9058
9631
  } catch (error) {
9059
9632
  if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
9060
9633
  }
9061
- for (const name of fs6.readdirSync(this.#journalRoot)) {
9634
+ for (const name of fs8.readdirSync(this.#journalRoot)) {
9062
9635
  if (!name.endsWith(".json")) continue;
9063
- const filePath = path11.join(this.#journalRoot, name);
9636
+ const filePath = path13.join(this.#journalRoot, name);
9064
9637
  try {
9065
- const journal = JSON.parse(fs6.readFileSync(filePath, "utf8"));
9638
+ const journal = JSON.parse(fs8.readFileSync(filePath, "utf8"));
9066
9639
  if (journal.format !== 1 || journal.status !== "applying" || !Array.isArray(journal.before)) continue;
9067
9640
  const workspace = this.#workspaceEntry()?.value;
9068
9641
  if (workspace?.lastOutcome === "committed" && workspace.lastTransactionId === journal.id) {
@@ -9314,11 +9887,11 @@ var SchemaProvider = class {
9314
9887
 
9315
9888
  // src/state/store.ts
9316
9889
  import { createHash as createHash5 } from "node:crypto";
9317
- import path13 from "node:path";
9890
+ import path15 from "node:path";
9318
9891
 
9319
9892
  // src/state/complexity.ts
9320
- import fs7 from "node:fs";
9321
- import path12 from "node:path";
9893
+ import fs9 from "node:fs";
9894
+ import path14 from "node:path";
9322
9895
  var isWordStart = (character) => character !== void 0 && (character >= "a" && character <= "z" || character >= "A" && character <= "Z" || character === "_" || character === "$");
9323
9896
  var isWordPart = (character) => isWordStart(character) || character !== void 0 && character >= "0" && character <= "9";
9324
9897
  var regularExpressionPrefixWords = /* @__PURE__ */ new Set([
@@ -9550,7 +10123,7 @@ var languageComplexities = [
9550
10123
  typeScriptJavaScriptComplexity
9551
10124
  ];
9552
10125
  var complexityForFile = (file, languages = languageComplexities) => {
9553
- const extension = path12.extname(file).toLowerCase();
10126
+ const extension = path14.extname(file).toLowerCase();
9554
10127
  return languages.find((language) => language.extensions.includes(extension));
9555
10128
  };
9556
10129
  var countFileComplexity = (file) => {
@@ -9559,7 +10132,7 @@ var countFileComplexity = (file) => {
9559
10132
  return {
9560
10133
  file,
9561
10134
  language: language.language,
9562
- count: language.count(fs7.readFileSync(file, "utf8"))
10135
+ count: language.count(fs9.readFileSync(file, "utf8"))
9563
10136
  };
9564
10137
  };
9565
10138
 
@@ -9567,7 +10140,7 @@ var countFileComplexity = (file) => {
9567
10140
  import { spawn as spawn2 } from "node:child_process";
9568
10141
  import { createHash as createHash4 } from "node:crypto";
9569
10142
  var COMMAND_OUTPUT_MAX_BYTES = 32 * 1024;
9570
- var errorMessage4 = (error) => error instanceof Error ? error.message : String(error);
10143
+ var errorMessage5 = (error) => error instanceof Error ? error.message : String(error);
9571
10144
  var truncateUtf8 = (value, maxBytes) => {
9572
10145
  const bytes = Buffer.from(value, "utf8");
9573
10146
  if (bytes.length <= maxBytes) return { value, omittedBytes: 0 };
@@ -9693,12 +10266,12 @@ var runCommand = (command, options) => new Promise((resolve2) => {
9693
10266
  windowsHide: true
9694
10267
  });
9695
10268
  } catch (error) {
9696
- finish("error", null, errorMessage4(error));
10269
+ finish("error", null, errorMessage5(error));
9697
10270
  return;
9698
10271
  }
9699
10272
  child.stdout?.on("data", collect);
9700
10273
  child.stderr?.on("data", collect);
9701
- child.once("error", (error) => finish("error", null, errorMessage4(error)));
10274
+ child.once("error", (error) => finish("error", null, errorMessage5(error)));
9702
10275
  child.once("close", (code) => {
9703
10276
  void (async () => {
9704
10277
  if (termination) await termination;
@@ -9742,7 +10315,7 @@ var EVENT_ROLLBACK_LIMIT = 8;
9742
10315
  var TRANSITION_PROTOCOL_VERSION = 1;
9743
10316
  var DURABLE_HEAD_PROTOCOL_VERSION = 2;
9744
10317
  var HEAD_COMMIT_PROOF_VERSION = 1;
9745
- var errorMessage5 = (error) => error instanceof Error ? error.message : String(error);
10318
+ var errorMessage6 = (error) => error instanceof Error ? error.message : String(error);
9746
10319
  var isCasError = (error) => error instanceof Error && /compare-and-swap failed/.test(error.message);
9747
10320
  var toStringArray = (value) => {
9748
10321
  if (!Array.isArray(value)) return void 0;
@@ -9761,9 +10334,9 @@ var truncateUtf82 = (value, maxBytes) => {
9761
10334
  const bounded = bytes.subarray(0, end).toString("utf8");
9762
10335
  return { value: bounded, omittedBytes: bytes.length - end };
9763
10336
  };
9764
- var boundedError = (error) => truncateUtf82(errorMessage5(error), REPORT_TEXT_MAX_BYTES).value;
10337
+ var boundedError = (error) => truncateUtf82(errorMessage6(error), REPORT_TEXT_MAX_BYTES).value;
9765
10338
  var casActualVersion = (error) => {
9766
- const match = errorMessage5(error).match(/found (\d+)$/);
10339
+ const match = errorMessage6(error).match(/found (\d+)$/);
9767
10340
  return match ? Number(match[1]) : void 0;
9768
10341
  };
9769
10342
  var toComplexityRecord = (value) => {
@@ -10219,7 +10792,7 @@ var StateStore = class {
10219
10792
  protocolVersion: TRANSITION_PROTOCOL_VERSION,
10220
10793
  phase: "rejected",
10221
10794
  transitionId: event.id,
10222
- error: truncateUtf82(errorMessage5(error), EVENT_TEXT_MAX_BYTES).value,
10795
+ error: truncateUtf82(errorMessage6(error), EVENT_TEXT_MAX_BYTES).value,
10223
10796
  rollback: {
10224
10797
  restored: rollback.errors.length === 0,
10225
10798
  deleted: deletedChunks[index],
@@ -10441,7 +11014,7 @@ var StateStore = class {
10441
11014
  const files = [];
10442
11015
  let netDelta = 0;
10443
11016
  for (const file of this.normalizeComplexityFiles(requestedFiles, input.cwd)) {
10444
- const measured = countFileComplexity(path13.resolve(input.cwd, file));
11017
+ const measured = countFileComplexity(path15.resolve(input.cwd, file));
10445
11018
  if (!measured) {
10446
11019
  files.push({ file, supported: false });
10447
11020
  continue;
@@ -10468,7 +11041,7 @@ var StateStore = class {
10468
11041
  const updates = [];
10469
11042
  let netDelta = 0;
10470
11043
  for (const file of this.normalizeComplexityFiles(files, cwd)) {
10471
- const measured = countFileComplexity(path13.resolve(cwd, file));
11044
+ const measured = countFileComplexity(path15.resolve(cwd, file));
10472
11045
  if (!measured) {
10473
11046
  deltas.push({ file, supported: false });
10474
11047
  continue;
@@ -10518,11 +11091,11 @@ var StateStore = class {
10518
11091
  const normalized = /* @__PURE__ */ new Set();
10519
11092
  for (const file of files) {
10520
11093
  if (!file.trim()) continue;
10521
- const relative = path13.relative(cwd, path13.resolve(cwd, file));
10522
- if (relative === ".." || relative.startsWith(`..${path13.sep}`) || path13.isAbsolute(relative)) {
11094
+ const relative = path15.relative(cwd, path15.resolve(cwd, file));
11095
+ if (relative === ".." || relative.startsWith(`..${path15.sep}`) || path15.isAbsolute(relative)) {
10523
11096
  throw new Error(`State complexity file must be inside the project cwd: ${file}`);
10524
11097
  }
10525
- normalized.add(relative.split(path13.sep).join("/"));
11098
+ normalized.add(relative.split(path15.sep).join("/"));
10526
11099
  }
10527
11100
  return [...normalized];
10528
11101
  }
@@ -11187,6 +11760,7 @@ var FabricRuntimeState = class {
11187
11760
  #mcpProvider;
11188
11761
  #config;
11189
11762
  #execution;
11763
+ #repairs;
11190
11764
  #speculationStore;
11191
11765
  #speculationTap;
11192
11766
  #agents;
@@ -11296,7 +11870,8 @@ var FabricRuntimeState = class {
11296
11870
  nestedToolCallId: "fabric-speculation",
11297
11871
  extensionContext: context,
11298
11872
  update() {
11299
- }
11873
+ },
11874
+ ...this.#sessionCapabilityLease?.view ? { capabilityView: this.#sessionCapabilityLease.view } : {}
11300
11875
  };
11301
11876
  const speculation = await registry.speculate(
11302
11877
  candidate.ref,
@@ -11311,7 +11886,8 @@ var FabricRuntimeState = class {
11311
11886
  speculation.preparedArgs,
11312
11887
  speculation.execute,
11313
11888
  createFreshnessChecker(candidate.ref, speculation.preparedArgs, context.cwd),
11314
- replay
11889
+ replay,
11890
+ speculation.bindingToken
11315
11891
  );
11316
11892
  }
11317
11893
  get registry() {
@@ -11375,6 +11951,10 @@ var FabricRuntimeState = class {
11375
11951
  if (!this.#compact) throw new Error("Pi Fabric has not initialized");
11376
11952
  return this.#compact;
11377
11953
  }
11954
+ get repairs() {
11955
+ if (!this.#repairs) throw new Error("Pi Fabric has not initialized");
11956
+ return this.#repairs;
11957
+ }
11378
11958
  async initialize(context, bootstrapConfig) {
11379
11959
  this.#suppressResidentGuidanceSync = true;
11380
11960
  try {
@@ -11404,9 +11984,11 @@ var FabricRuntimeState = class {
11404
11984
  new FabricToolResultProxy(() => this.capturedTools.runner)
11405
11985
  );
11406
11986
  this.#wireSpeculation();
11407
- this.#unsubscribeCapturedCatalog = this.capturedTools.subscribe(
11408
- () => this.#registry?.notifyCatalogChanged("extensions")
11409
- );
11987
+ this.#unsubscribeCapturedCatalog?.();
11988
+ this.#unsubscribeCapturedCatalog = this.capturedTools.subscribe(() => {
11989
+ this.#registry?.notifyCatalogChanged("extensions");
11990
+ this.#refreshRepairCatalog();
11991
+ });
11410
11992
  this.#componentSupervisor = new FabricComponentSupervisor(this.#registry, {
11411
11993
  invocationContext: () => ({
11412
11994
  cwd: context.cwd,
@@ -11468,7 +12050,7 @@ var FabricRuntimeState = class {
11468
12050
  create: () => new McpProvider(context.cwd, this.#config.mcp, {
11469
12051
  ...this.#config.mcp.cache.enabled ? {
11470
12052
  cache: new McpDescriptorCacheStore(
11471
- path14.join(
12053
+ path16.join(
11472
12054
  process.env.PI_FABRIC_PROJECT_ROOT ?? context.cwd,
11473
12055
  ".pi",
11474
12056
  "fabric",
@@ -11512,7 +12094,7 @@ var FabricRuntimeState = class {
11512
12094
  this.#mainAgent = mainAgent;
11513
12095
  const projectRoot = process.env.PI_FABRIC_PROJECT_ROOT ?? context.cwd;
11514
12096
  const configuredMeshRoot = this.#config.mesh.root;
11515
- const meshRoot = process.env.PI_FABRIC_MESH_ROOT ?? (configuredMeshRoot ? path14.resolve(projectRoot, configuredMeshRoot) : path14.join(projectRoot, ".pi", "fabric", "mesh"));
12097
+ const meshRoot = process.env.PI_FABRIC_MESH_ROOT ?? (configuredMeshRoot ? path16.resolve(projectRoot, configuredMeshRoot) : path16.join(projectRoot, ".pi", "fabric", "mesh"));
11516
12098
  this.#mesh = new MeshStore(
11517
12099
  meshRoot,
11518
12100
  this.#config.mesh.maxEventBytes,
@@ -11630,7 +12212,7 @@ var FabricRuntimeState = class {
11630
12212
  return participant ? participant.ownerHostId === hostId : void 0;
11631
12213
  };
11632
12214
  const lineageAlive = (rootId) => this.#participants?.get(rootId) !== void 0;
11633
- const persistentActorRoot = this.#config.mesh.actorScope === "session" ? path14.join(meshRoot, "actors", sessionId) : path14.join(meshRoot, "actors");
12215
+ const persistentActorRoot = this.#config.mesh.actorScope === "session" ? path16.join(meshRoot, "actors", sessionId) : path16.join(meshRoot, "actors");
11634
12216
  const acquireActorCapabilityView = (requirements, signal) => this.#registry.acquireCapabilityView(requirements, {
11635
12217
  cwd: context.cwd,
11636
12218
  signal,
@@ -11690,7 +12272,10 @@ ${escapeXmlText(text)}
11690
12272
  acquireCapabilityView: acquireActorCapabilityView
11691
12273
  }
11692
12274
  );
11693
- this.#registry.subscribeProviderChanges(() => this.#actors?.retryCapabilityWaiters());
12275
+ this.#registry.subscribeProviderChanges(() => {
12276
+ this.#actors?.retryCapabilityWaiters();
12277
+ this.#refreshRepairCatalog();
12278
+ });
11694
12279
  this.#lifecycle = new LifecycleBroker(
11695
12280
  this.#mesh,
11696
12281
  identity,
@@ -11878,6 +12463,12 @@ ${escapeXmlText(text)}
11878
12463
  this.#sessionCapabilityLease = lease;
11879
12464
  this.#execution.setCapabilityView(lease.view);
11880
12465
  }
12466
+ this.#repairs = new RepairCompiler({
12467
+ agentDir: resolveAgentDir(),
12468
+ enabled: this.#config.repairs.enabled
12469
+ });
12470
+ this.#refreshRepairCatalog();
12471
+ setActiveRepairCompiler(this.#repairs);
11881
12472
  }
11882
12473
  async ensure(context) {
11883
12474
  if (!this.initialized || this.#cwd !== context.cwd) await this.initialize(context);
@@ -12086,6 +12677,7 @@ ${escapeXmlText(text)}
12086
12677
  }
12087
12678
  async shutdown() {
12088
12679
  this.#suppressResidentGuidanceSync = true;
12680
+ this.#deactivateRepairs();
12089
12681
  await this.#participants?.quiesce().catch(() => void 0);
12090
12682
  await this.#componentLoader?.close();
12091
12683
  await Promise.allSettled([...this.#componentTransitionPublications]);
@@ -12149,7 +12741,21 @@ ${escapeXmlText(text)}
12149
12741
  } catch {
12150
12742
  }
12151
12743
  }
12744
+ #refreshRepairCatalog() {
12745
+ if (!this.#repairs || !this.#registry) return;
12746
+ if (this.capturedTools.suspended) return;
12747
+ this.#repairs.setCatalogSurface({
12748
+ providers: this.#registry.providers().map((provider) => provider.name),
12749
+ capturedTools: this.capturedTools.list().map((entry) => entry.name)
12750
+ });
12751
+ }
12752
+ #deactivateRepairs() {
12753
+ const repairs = this.#repairs;
12754
+ this.#repairs = void 0;
12755
+ clearActiveRepairCompiler(repairs);
12756
+ }
12152
12757
  async #closeInternal() {
12758
+ this.#deactivateRepairs();
12153
12759
  if (!this.#registry) return;
12154
12760
  await this.#participants?.quiesce().catch(() => void 0);
12155
12761
  await this.#componentLoader?.close();
@@ -12249,4 +12855,4 @@ var deepAssign = (target, source) => {
12249
12855
  export {
12250
12856
  FabricRuntimeState
12251
12857
  };
12252
- //# sourceMappingURL=fabric-runtime-state-VCLWTB55.js.map
12858
+ //# sourceMappingURL=fabric-runtime-state-H2NCRJWK.js.map