omp-fabric 0.85.0 → 1.0.2

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 (115) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/agents/manager.d.ts.map +1 -1
  3. package/dist/agents/transports/process-transport.d.ts.map +1 -1
  4. package/dist/agents/transports/process-utils.d.ts +6 -1
  5. package/dist/agents/transports/process-utils.d.ts.map +1 -1
  6. package/dist/agents/types.d.ts +1 -0
  7. package/dist/agents/types.d.ts.map +1 -1
  8. package/dist/audit/details.d.ts +10 -0
  9. package/dist/audit/details.d.ts.map +1 -1
  10. package/dist/chunks/{chunk-GKOFZ76R.js → chunk-2LGLDM34.js} +22 -4
  11. package/dist/chunks/chunk-2LGLDM34.js.map +7 -0
  12. package/dist/chunks/{chunk-T6OAX4FO.js → chunk-3TWC6XOS.js} +37 -9
  13. package/dist/chunks/chunk-3TWC6XOS.js.map +7 -0
  14. package/dist/chunks/{chunk-ZNAJDPT7.js → chunk-B2SQR2JY.js} +32 -24
  15. package/dist/chunks/{chunk-ZNAJDPT7.js.map → chunk-B2SQR2JY.js.map} +2 -2
  16. package/dist/chunks/{chunk-SXMWV447.js → chunk-DOKANNFC.js} +206 -3
  17. package/dist/chunks/chunk-DOKANNFC.js.map +7 -0
  18. package/dist/chunks/{chunk-UPCARIJP.js → chunk-FFWALVBN.js} +20 -9
  19. package/dist/chunks/chunk-FFWALVBN.js.map +7 -0
  20. package/dist/chunks/{chunk-4LEDA76O.js → chunk-HAJPYJPY.js} +3 -3
  21. package/dist/chunks/{chunk-ROKMBLGN.js → chunk-IFNDRFW7.js} +34 -14
  22. package/dist/chunks/chunk-IFNDRFW7.js.map +7 -0
  23. package/dist/chunks/{chunk-ZTM45GH5.js → chunk-JZWLICMS.js} +125 -42
  24. package/dist/chunks/chunk-JZWLICMS.js.map +7 -0
  25. package/dist/chunks/{chunk-5GTPD4BA.js → chunk-L2FNMIOW.js} +66 -22
  26. package/dist/chunks/chunk-L2FNMIOW.js.map +7 -0
  27. package/dist/chunks/chunk-NX4SNIMR.js +226 -0
  28. package/dist/chunks/chunk-NX4SNIMR.js.map +7 -0
  29. package/dist/chunks/{chunk-YZZIKJX6.js → chunk-RBA54WC7.js} +1 -1
  30. package/dist/chunks/{chunk-YZZIKJX6.js.map → chunk-RBA54WC7.js.map} +1 -1
  31. package/dist/chunks/{chunk-OPTP4PSE.js → chunk-YPYVUGON.js} +3 -23
  32. package/dist/chunks/chunk-YPYVUGON.js.map +7 -0
  33. package/dist/chunks/{chunk-JP4LIZFT.js → chunk-Z6HEQPXX.js} +23 -6
  34. package/dist/chunks/chunk-Z6HEQPXX.js.map +7 -0
  35. package/dist/commands/fabric.d.ts.map +1 -1
  36. package/dist/core/action-registry.d.ts +2 -1
  37. package/dist/core/action-registry.d.ts.map +1 -1
  38. package/dist/core/action-registry.js +2 -2
  39. package/dist/core/compact-controller.d.ts +28 -3
  40. package/dist/core/compact-controller.d.ts.map +1 -1
  41. package/dist/core/compact-outcome-store.d.ts +3 -0
  42. package/dist/core/compact-outcome-store.d.ts.map +1 -0
  43. package/dist/entropy/index.js +2 -2
  44. package/dist/entropy/sessions.d.ts.map +1 -1
  45. package/dist/execution-service.d.ts.map +1 -1
  46. package/dist/fabric-exec-tool.d.ts.map +1 -1
  47. package/dist/fabric-runtime-state.d.ts.map +1 -1
  48. package/dist/fabric-runtime-state.js +883 -367
  49. package/dist/fabric-runtime-state.js.map +4 -4
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +116 -18
  52. package/dist/index.js.map +3 -3
  53. package/dist/memory/discovery.d.ts +28 -14
  54. package/dist/memory/discovery.d.ts.map +1 -1
  55. package/dist/memory/discovery.js +9 -5
  56. package/dist/protocol.d.ts +5 -0
  57. package/dist/protocol.d.ts.map +1 -1
  58. package/dist/protocol.js +1 -1
  59. package/dist/providers/mcp-descriptor-cache.d.ts +2 -0
  60. package/dist/providers/mcp-descriptor-cache.d.ts.map +1 -1
  61. package/dist/providers/mcp-provider.d.ts +4 -2
  62. package/dist/providers/mcp-provider.d.ts.map +1 -1
  63. package/dist/providers/memory-provider.d.ts.map +1 -1
  64. package/dist/providers/memory-provider.js +2 -2
  65. package/dist/providers/omp-tools-provider.d.ts +1 -0
  66. package/dist/providers/omp-tools-provider.d.ts.map +1 -1
  67. package/dist/residency/host.js +5 -5
  68. package/dist/residency/omp-entry.js +5 -5
  69. package/dist/runtime/core-override-guest-types.js +1 -1
  70. package/dist/runtime/guest-types.d.ts +2 -2
  71. package/dist/runtime/guest-types.d.ts.map +1 -1
  72. package/dist/runtime/guest-types.js +1 -1
  73. package/dist/runtime/node-process-child-source.d.ts.map +1 -1
  74. package/dist/runtime/node-process-runtime.d.ts.map +1 -1
  75. package/dist/runtime/node-process-runtime.js +4 -2
  76. package/dist/runtime/node-process-runtime.js.map +2 -2
  77. package/dist/runtime/quickjs-runtime.d.ts +6 -1
  78. package/dist/runtime/quickjs-runtime.d.ts.map +1 -1
  79. package/dist/runtime/quickjs-runtime.js +1 -1
  80. package/dist/speculation/guest-args.d.ts +2 -0
  81. package/dist/speculation/guest-args.d.ts.map +1 -0
  82. package/dist/speculation/scanner.d.ts.map +1 -1
  83. package/dist/speculation/scanner.js +312 -14
  84. package/dist/speculation/scanner.js.map +3 -3
  85. package/dist/speculation/store.d.ts +2 -0
  86. package/dist/speculation/store.d.ts.map +1 -1
  87. package/dist/speculation/stream-tap.d.ts +1 -0
  88. package/dist/speculation/stream-tap.d.ts.map +1 -1
  89. package/dist/speculation/summary.d.ts +4 -0
  90. package/dist/speculation/summary.d.ts.map +1 -0
  91. package/dist/speculation/types.d.ts +4 -0
  92. package/dist/speculation/types.d.ts.map +1 -1
  93. package/dist/speculation/warmup.d.ts +17 -0
  94. package/dist/speculation/warmup.d.ts.map +1 -0
  95. package/dist/ui/settings.d.ts.map +1 -1
  96. package/dist/ui/settings.js +6 -6
  97. package/dist/ui/settings.js.map +2 -2
  98. package/docs/agents.md +1 -1
  99. package/docs/compaction.md +2 -2
  100. package/docs/configuration.md +26 -12
  101. package/docs/interface.md +2 -2
  102. package/docs/speculation.md +11 -6
  103. package/package.json +3 -2
  104. package/dist/chunks/chunk-5GTPD4BA.js.map +0 -7
  105. package/dist/chunks/chunk-7BIA5BJN.js +0 -112
  106. package/dist/chunks/chunk-7BIA5BJN.js.map +0 -7
  107. package/dist/chunks/chunk-GKOFZ76R.js.map +0 -7
  108. package/dist/chunks/chunk-JP4LIZFT.js.map +0 -7
  109. package/dist/chunks/chunk-OPTP4PSE.js.map +0 -7
  110. package/dist/chunks/chunk-ROKMBLGN.js.map +0 -7
  111. package/dist/chunks/chunk-SXMWV447.js.map +0 -7
  112. package/dist/chunks/chunk-T6OAX4FO.js.map +0 -7
  113. package/dist/chunks/chunk-UPCARIJP.js.map +0 -7
  114. package/dist/chunks/chunk-ZTM45GH5.js.map +0 -7
  115. /package/dist/chunks/{chunk-4LEDA76O.js.map → chunk-HAJPYJPY.js.map} +0 -0
@@ -11,7 +11,7 @@ import {
11
11
  MemoryProvider,
12
12
  actionArgNormalizer,
13
13
  uniqueDeclaredKeyForSpelling
14
- } from "./chunks/chunk-T6OAX4FO.js";
14
+ } from "./chunks/chunk-3TWC6XOS.js";
15
15
  import "./chunks/chunk-2KEWFW56.js";
16
16
  import "./chunks/chunk-U5FYOMG5.js";
17
17
  import {
@@ -22,6 +22,7 @@ import {
22
22
  FabricAutoApprovalClassifier,
23
23
  FabricProviderComponentManifest,
24
24
  FabricSessionApprovals,
25
+ FabricSpeculationStreamTap,
25
26
  MainAgentController,
26
27
  PrewalkController,
27
28
  PrewalkDriftTracker,
@@ -32,7 +33,7 @@ import {
32
33
  repairFabricGuestCode,
33
34
  resolveFabricIdentity,
34
35
  runFabricHandoffAtBoundary
35
- } from "./chunks/chunk-SXMWV447.js";
36
+ } from "./chunks/chunk-DOKANNFC.js";
36
37
  import "./chunks/chunk-7VIQNUPS.js";
37
38
  import {
38
39
  AgentTranscriptReader,
@@ -49,7 +50,7 @@ import {
49
50
  repairActionName,
50
51
  setActiveCompiledSurface,
51
52
  setActiveRepairCompiler
52
- } from "./chunks/chunk-GKOFZ76R.js";
53
+ } from "./chunks/chunk-2LGLDM34.js";
53
54
  import {
54
55
  formatFabricEffectConflict
55
56
  } from "./chunks/chunk-LMAVZN7J.js";
@@ -62,14 +63,14 @@ import {
62
63
  withExclusiveFileLockAsync
63
64
  } from "./chunks/chunk-YWEXP724.js";
64
65
  import "./chunks/chunk-4A4MVZQW.js";
65
- import "./chunks/chunk-7BIA5BJN.js";
66
+ import "./chunks/chunk-NX4SNIMR.js";
66
67
  import "./chunks/chunk-6QBPSZJ5.js";
67
68
  import {
68
69
  FABRIC_COMPONENT_DISCOVER_EVENT,
69
70
  FABRIC_PROVIDER_DISCOVER_EVENT,
70
71
  FABRIC_TOOL_RESULT_PROXY_KIND,
71
72
  readFabricToolResultProxyDetailsV1
72
- } from "./chunks/chunk-YZZIKJX6.js";
73
+ } from "./chunks/chunk-RBA54WC7.js";
73
74
  import {
74
75
  ActorDirectory,
75
76
  ActorRegistryOwnershipError,
@@ -93,27 +94,27 @@ import {
93
94
  resolveAgentCwd,
94
95
  resolveOmpBinary,
95
96
  validateAgentCwdRequest
96
- } from "./chunks/chunk-UPCARIJP.js";
97
+ } from "./chunks/chunk-FFWALVBN.js";
97
98
  import "./chunks/chunk-VPPIJRNZ.js";
98
99
  import {
99
100
  executeFile,
100
101
  processIsAlive,
101
102
  spawnDetached
102
- } from "./chunks/chunk-ZTM45GH5.js";
103
+ } from "./chunks/chunk-JZWLICMS.js";
103
104
  import "./chunks/chunk-CT2AJ2UI.js";
104
105
  import {
105
- checkedHandoffCompaction,
106
- resolveAgentDir
107
- } from "./chunks/chunk-OPTP4PSE.js";
106
+ checkedHandoffCompaction
107
+ } from "./chunks/chunk-YPYVUGON.js";
108
108
  import {
109
109
  DEFAULT_FABRIC_CONFIG,
110
110
  FUZZY_RESOLUTION_MARKERS,
111
111
  MAX_AGENT_TIMEOUT_MS,
112
112
  MIN_AGENT_TIMEOUT_MS,
113
113
  loadFabricConfig,
114
+ resolveAgentDir,
114
115
  resolveAvailableOmpModel,
115
116
  resolveFabricModel
116
- } from "./chunks/chunk-ROKMBLGN.js";
117
+ } from "./chunks/chunk-IFNDRFW7.js";
117
118
  import {
118
119
  FABRIC_ACTOR_HOST_EVENTS,
119
120
  isFabricActorHostEvent,
@@ -170,7 +171,7 @@ import {
170
171
  } from "./chunks/chunk-6N4XGAZY.js";
171
172
 
172
173
  // src/fabric-runtime-state.ts
173
- import path15 from "node:path";
174
+ import path16 from "node:path";
174
175
  import { fileURLToPath as fileURLToPath2 } from "node:url";
175
176
 
176
177
  // src/actors/global-registry.ts
@@ -1039,8 +1040,8 @@ var MAX_CHILDREN_PER_COMPONENT = 256;
1039
1040
  var componentLifecycleStorage;
1040
1041
  var componentLifecycleStorageTask;
1041
1042
  var lifecycleStorage = () => {
1042
- componentLifecycleStorageTask ??= import("node:async_hooks").then(({ AsyncLocalStorage }) => {
1043
- componentLifecycleStorage ??= new AsyncLocalStorage();
1043
+ componentLifecycleStorageTask ??= import("node:async_hooks").then(({ AsyncLocalStorage: AsyncLocalStorage2 }) => {
1044
+ componentLifecycleStorage ??= new AsyncLocalStorage2();
1044
1045
  return componentLifecycleStorage;
1045
1046
  });
1046
1047
  return componentLifecycleStorageTask;
@@ -2217,10 +2218,10 @@ var FabricComponentSupervisor = class {
2217
2218
  adjacent.set(edge.from, targets);
2218
2219
  }
2219
2220
  const cycles = /* @__PURE__ */ new Map();
2220
- const visit = (node, path16, positions) => {
2221
+ const visit = (node, path17, positions) => {
2221
2222
  const position = positions.get(node);
2222
2223
  if (position !== void 0) {
2223
- const cycle = path16.slice(position);
2224
+ const cycle = path17.slice(position);
2224
2225
  const rotations = cycle.map((_, index) => [
2225
2226
  ...cycle.slice(index),
2226
2227
  ...cycle.slice(0, index)
@@ -2230,10 +2231,10 @@ var FabricComponentSupervisor = class {
2230
2231
  cycles.set(canonical.join("\0"), canonical);
2231
2232
  return;
2232
2233
  }
2233
- if (path16.length > this.#components.size) return;
2234
- const nextPositions = new Map(positions).set(node, path16.length);
2234
+ if (path17.length > this.#components.size) return;
2235
+ const nextPositions = new Map(positions).set(node, path17.length);
2235
2236
  for (const target of adjacent.get(node) ?? []) {
2236
- visit(target, [...path16, node], nextPositions);
2237
+ visit(target, [...path17, node], nextPositions);
2237
2238
  }
2238
2239
  };
2239
2240
  for (const id of this.#components.keys()) visit(id, [], /* @__PURE__ */ new Map());
@@ -2562,13 +2563,53 @@ var checkedPreserve = (value) => {
2562
2563
  }
2563
2564
  return [...value];
2564
2565
  };
2566
+ var finite = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
2567
+ var UNKNOWN_USAGE = {
2568
+ known: false,
2569
+ tokens: null,
2570
+ contextWindow: null,
2571
+ percent: null,
2572
+ remainingTokens: null
2573
+ };
2574
+ var readCompactContextUsage = (context) => {
2575
+ const modelWindow = finite(context?.model?.contextWindow);
2576
+ let usage;
2577
+ try {
2578
+ usage = context?.getContextUsage?.();
2579
+ } catch {
2580
+ usage = void 0;
2581
+ }
2582
+ if (!usage) return { ...UNKNOWN_USAGE, contextWindow: modelWindow };
2583
+ const tokens = finite(usage.tokens);
2584
+ const contextWindow = finite(usage.contextWindow) ?? modelWindow;
2585
+ const percent = finite(usage.percent) ?? (tokens !== null && contextWindow !== null && contextWindow > 0 ? tokens / contextWindow * 100 : null);
2586
+ return {
2587
+ known: tokens !== null || percent !== null,
2588
+ tokens,
2589
+ contextWindow,
2590
+ percent,
2591
+ remainingTokens: tokens !== null && contextWindow !== null ? Math.max(0, contextWindow - tokens) : null
2592
+ };
2593
+ };
2594
+ var sessionIdOf = (context) => {
2595
+ try {
2596
+ const id = context?.sessionManager?.getSessionId?.();
2597
+ return typeof id === "string" && id.length > 0 ? id : void 0;
2598
+ } catch {
2599
+ return void 0;
2600
+ }
2601
+ };
2565
2602
  var CompactController = class {
2566
2603
  #pending;
2567
2604
  #last;
2568
2605
  #inFlight;
2606
+ #restoredFor;
2607
+ #restored;
2569
2608
  #hooks;
2570
- constructor(hooks = {}) {
2609
+ #options;
2610
+ constructor(hooks = {}, options = {}) {
2571
2611
  this.#hooks = hooks;
2612
+ this.#options = options;
2572
2613
  }
2573
2614
  // Record a pending compaction intent. A single slot: a new request replaces
2574
2615
  // any pending one, keeping the latest instructions.
@@ -2597,10 +2638,39 @@ var CompactController = class {
2597
2638
  cancel() {
2598
2639
  this.#pending = void 0;
2599
2640
  }
2600
- status() {
2641
+ #settledOutcome(context) {
2642
+ if (this.#last) return this.#last;
2643
+ const store = this.#options.outcomes;
2644
+ if (!store) return void 0;
2645
+ const sessionId = sessionIdOf(context);
2646
+ if (sessionId === void 0) return void 0;
2647
+ if (this.#restoredFor !== sessionId) {
2648
+ this.#restoredFor = sessionId;
2649
+ this.#restored = store.load(sessionId);
2650
+ }
2651
+ return this.#restored;
2652
+ }
2653
+ #settle(context, outcome) {
2654
+ this.#last = outcome;
2655
+ this.#restoredFor = void 0;
2656
+ this.#restored = void 0;
2657
+ const store = this.#options.outcomes;
2658
+ const sessionId = store ? sessionIdOf(context) : void 0;
2659
+ if (store && sessionId !== void 0) store.save(sessionId, outcome);
2660
+ this.#hooks.onCommit?.(outcome);
2661
+ }
2662
+ status(context) {
2663
+ const settings = this.#options.settings?.();
2664
+ const last = this.#settledOutcome(context);
2665
+ const model = context?.model;
2666
+ const sessionId = sessionIdOf(context);
2601
2667
  return {
2602
2668
  ...this.#pending ? { pending: this.#pending } : {},
2603
- ...this.#last ? { last: this.#last } : {}
2669
+ ...last ? { last } : {},
2670
+ context: readCompactContextUsage(context),
2671
+ ...settings ? { engine: settings.engine, targetContextRatio: settings.targetContextRatio } : {},
2672
+ ...model ? { model: `${model.provider}/${model.id}` } : {},
2673
+ ...sessionId !== void 0 ? { sessionId } : {}
2604
2674
  };
2605
2675
  }
2606
2676
  async maybeCommit(context) {
@@ -2616,23 +2686,32 @@ var CompactController = class {
2616
2686
  const clearCommittedIntent = () => {
2617
2687
  if (this.#pending === committing) this.#pending = void 0;
2618
2688
  };
2689
+ const before = readCompactContextUsage(context);
2619
2690
  const completion = (async () => {
2620
2691
  try {
2621
2692
  await context.compact(instructions);
2622
2693
  clearCommittedIntent();
2623
- this.#last = { at: Date.now(), requestedBy, status: "committed" };
2624
- this.#hooks.onCommit?.(this.#last);
2694
+ const after = readCompactContextUsage(context);
2695
+ const measured = before.tokens !== null && after.tokens !== null;
2696
+ this.#settle(context, {
2697
+ at: Date.now(),
2698
+ requestedBy,
2699
+ status: measured && after.tokens >= before.tokens ? "skipped" : "committed",
2700
+ ...before.tokens !== null ? { tokensBefore: before.tokens } : {},
2701
+ ...after.tokens !== null ? { estimatedTokensAfter: after.tokens } : {},
2702
+ ...measured && after.tokens >= before.tokens ? { error: "Compaction ran but freed no context tokens" } : {}
2703
+ });
2625
2704
  } catch (error) {
2626
2705
  const message2 = error instanceof Error ? error.message : "Compaction failed";
2627
2706
  clearCommittedIntent();
2628
2707
  const cancelled = message2 === "Compaction cancelled" || message2 === "Already compacted";
2629
- this.#last = {
2708
+ this.#settle(context, {
2630
2709
  at: Date.now(),
2631
2710
  requestedBy,
2632
2711
  status: cancelled ? "cancelled" : "failed",
2712
+ ...before.tokens !== null ? { tokensBefore: before.tokens } : {},
2633
2713
  error: message2
2634
- };
2635
- this.#hooks.onCommit?.(this.#last);
2714
+ });
2636
2715
  }
2637
2716
  })();
2638
2717
  this.#inFlight = completion;
@@ -2644,6 +2723,80 @@ var CompactController = class {
2644
2723
  }
2645
2724
  };
2646
2725
 
2726
+ // src/core/compact-outcome-store.ts
2727
+ import { mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
2728
+ import path2 from "node:path";
2729
+ var OUTCOME_DIR = ["fabric", "compact"];
2730
+ var MAX_OUTCOME_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
2731
+ var sessionFileName = (sessionId) => {
2732
+ const safe = sessionId.replace(/[^A-Za-z0-9._-]/g, "");
2733
+ return safe.length > 0 && safe.length <= 128 ? `${safe}.json` : void 0;
2734
+ };
2735
+ var finiteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
2736
+ var nonEmptyString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
2737
+ var decodeOutcome = (raw) => {
2738
+ if (typeof raw !== "object" || raw === null) return void 0;
2739
+ const record = raw;
2740
+ const at = finiteNumber(record.at);
2741
+ const requestedBy = nonEmptyString(record.requestedBy);
2742
+ const status = record.status;
2743
+ if (at === void 0 || requestedBy === void 0) return void 0;
2744
+ if (status !== "committed" && status !== "cancelled" && status !== "failed" && status !== "skipped") {
2745
+ return void 0;
2746
+ }
2747
+ const summary = nonEmptyString(record.summary);
2748
+ const error = nonEmptyString(record.error);
2749
+ const tokensBefore = finiteNumber(record.tokensBefore);
2750
+ const estimatedTokensAfter = finiteNumber(record.estimatedTokensAfter);
2751
+ return {
2752
+ at,
2753
+ requestedBy,
2754
+ status,
2755
+ ...summary !== void 0 ? { summary } : {},
2756
+ ...tokensBefore !== void 0 ? { tokensBefore } : {},
2757
+ ...estimatedTokensAfter !== void 0 ? { estimatedTokensAfter } : {},
2758
+ ...error !== void 0 ? { error } : {},
2759
+ persisted: true
2760
+ };
2761
+ };
2762
+ var pruneStale = (dir, now) => {
2763
+ for (const entry of readdirSync(dir)) {
2764
+ if (!entry.endsWith(".json")) continue;
2765
+ const target = path2.join(dir, entry);
2766
+ try {
2767
+ if (now - statSync(target).mtimeMs > MAX_OUTCOME_AGE_MS) rmSync(target, { force: true });
2768
+ } catch {
2769
+ continue;
2770
+ }
2771
+ }
2772
+ };
2773
+ var fileCompactOutcomeStore = (agentDir = resolveAgentDir) => ({
2774
+ load(sessionId) {
2775
+ const name = sessionFileName(sessionId);
2776
+ if (name === void 0) return void 0;
2777
+ try {
2778
+ return decodeOutcome(JSON.parse(readFileSync(path2.join(agentDir(), ...OUTCOME_DIR, name), "utf8")));
2779
+ } catch {
2780
+ return void 0;
2781
+ }
2782
+ },
2783
+ save(sessionId, outcome) {
2784
+ const name = sessionFileName(sessionId);
2785
+ if (name === void 0) return;
2786
+ try {
2787
+ const dir = path2.join(agentDir(), ...OUTCOME_DIR);
2788
+ mkdirSync(dir, { recursive: true });
2789
+ const target = path2.join(dir, name);
2790
+ const staging = `${target}.${process.pid}.tmp`;
2791
+ writeFileSync(staging, JSON.stringify(outcome), "utf8");
2792
+ renameSync(staging, target);
2793
+ pruneStale(dir, Date.now());
2794
+ } catch {
2795
+ return;
2796
+ }
2797
+ }
2798
+ });
2799
+
2647
2800
  // src/core/tool-result-proxy.ts
2648
2801
  var nativeLifecycleProviders = /* @__PURE__ */ new Set(["omp", "extensions"]);
2649
2802
  var textFromContent = (content) => content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
@@ -2793,6 +2946,17 @@ var FabricExecutionService = class {
2793
2946
  },
2794
2947
  ...this.#capabilityView ? { capabilityView: this.#capabilityView } : {}
2795
2948
  });
2949
+ const proxyNames = {
2950
+ ...guestTypeSources.extensionTools?.length ? { extensions: guestTypeSources.extensionTools.map((tool) => tool.name) } : {},
2951
+ ...guestTypeSources.mcpServers?.length ? {
2952
+ mcp: Object.fromEntries(
2953
+ guestTypeSources.mcpServers.map((entry) => [
2954
+ entry.server,
2955
+ entry.tools.map((tool) => tool.name)
2956
+ ])
2957
+ )
2958
+ } : {}
2959
+ };
2796
2960
  const coreOverrideDeclarations = effectiveFullCodeMode ? dependencies.buildCoreOverrideGuestDeclarations(
2797
2961
  this.capturedTools?.list().map((entry) => ({
2798
2962
  name: entry.name,
@@ -3263,6 +3427,7 @@ var FabricExecutionService = class {
3263
3427
  ...checked.javascript ? { transpiledCode: checked.javascript } : {},
3264
3428
  ...checked.sourceMap ? { transpiledSourceMap: checked.sourceMap } : {},
3265
3429
  ...options.payloads ? { payloads: options.payloads } : {},
3430
+ ...Object.keys(proxyNames).length > 0 ? { proxyNames } : {},
3266
3431
  ...options.tokenBudget !== void 0 ? { tokenBudget: options.tokenBudget } : {},
3267
3432
  ...options.signal ? { signal: options.signal } : {}
3268
3433
  }
@@ -3370,7 +3535,7 @@ var actionAliasCandidate = (provider, actionName, declared) => {
3370
3535
 
3371
3536
  // src/repairs/store.ts
3372
3537
  import fs2 from "node:fs";
3373
- import path2 from "node:path";
3538
+ import path3 from "node:path";
3374
3539
 
3375
3540
  // src/repairs/types.ts
3376
3541
  var REPAIR_TABLE_VERSION = 1;
@@ -3391,8 +3556,8 @@ var emptyRepairTable = (catalogDigest, now = (/* @__PURE__ */ new Date()).toISOS
3391
3556
 
3392
3557
  // src/repairs/store.ts
3393
3558
  var LOCK_TIMEOUT_MESSAGE = "Timed out waiting for the Fabric repair table lock";
3394
- var repairsDirectory = (agentDir) => path2.join(agentDir, "fabric", "repairs");
3395
- var currentPath = (directory) => path2.join(directory, "current.json");
3559
+ var repairsDirectory = (agentDir) => path3.join(agentDir, "fabric", "repairs");
3560
+ var currentPath = (directory) => path3.join(directory, "current.json");
3396
3561
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3397
3562
  var errorCode = (error) => isRecord(error) && typeof error.code === "string" ? error.code : void 0;
3398
3563
  var parseRepair = (value) => {
@@ -3736,7 +3901,7 @@ var isSpeculationEligible = (action, mcpAllowlist) => {
3736
3901
 
3737
3902
  // src/speculation/freshness.ts
3738
3903
  import fs3 from "node:fs";
3739
- import path3 from "node:path";
3904
+ import path4 from "node:path";
3740
3905
  var statSignature = (filePath) => {
3741
3906
  try {
3742
3907
  const stats = fs3.statSync(filePath);
@@ -3750,7 +3915,7 @@ var signatureEquals = (left, right) => left === "missing" || right === "missing"
3750
3915
  var readFreshness = (args, cwd) => {
3751
3916
  const target = args.path;
3752
3917
  if (typeof target !== "string" || target.length === 0) return void 0;
3753
- const resolved = path3.resolve(cwd, target);
3918
+ const resolved = path4.resolve(cwd, target);
3754
3919
  const snapshot = statSignature(resolved);
3755
3920
  if (snapshot === "error") return void 0;
3756
3921
  return () => {
@@ -3770,6 +3935,7 @@ var FabricSpeculationStore = class _FabricSpeculationStore {
3770
3935
  #stats = {
3771
3936
  launched: 0,
3772
3937
  served: 0,
3938
+ absent: 0,
3773
3939
  epochInvalidated: 0,
3774
3940
  freshnessInvalidated: 0,
3775
3941
  failed: 0,
@@ -3779,6 +3945,7 @@ var FabricSpeculationStore = class _FabricSpeculationStore {
3779
3945
  #maxConcurrent;
3780
3946
  #maxEntries;
3781
3947
  #entryTtlMs;
3948
+ #launching = /* @__PURE__ */ new Map();
3782
3949
  constructor(config) {
3783
3950
  this.#maxConcurrent = config.maxConcurrent;
3784
3951
  this.#maxEntries = config.maxEntries;
@@ -3799,12 +3966,39 @@ ${ref}
3799
3966
  ${stableJsonHash(preparedArgs)}
3800
3967
  ${bindingToken}`;
3801
3968
  }
3969
+ beginLaunch(parentToolCallId) {
3970
+ const pending = this.#launching.get(parentToolCallId) ?? { count: 0, waiters: [] };
3971
+ pending.count += 1;
3972
+ this.#launching.set(parentToolCallId, pending);
3973
+ let released = false;
3974
+ return () => {
3975
+ if (released) return;
3976
+ released = true;
3977
+ pending.count -= 1;
3978
+ if (pending.count > 0) return;
3979
+ this.#launching.delete(parentToolCallId);
3980
+ for (const waiter of pending.waiters.splice(0)) waiter();
3981
+ };
3982
+ }
3983
+ async settleLaunches(parentToolCallId, timeoutMs) {
3984
+ const pending = this.#launching.get(parentToolCallId);
3985
+ if (!pending || pending.count === 0) return;
3986
+ await new Promise((resolvePromise) => {
3987
+ const timer = setTimeout(resolvePromise, timeoutMs);
3988
+ if (typeof timer.unref === "function") timer.unref();
3989
+ pending.waiters.push(() => {
3990
+ clearTimeout(timer);
3991
+ resolvePromise();
3992
+ });
3993
+ });
3994
+ }
3802
3995
  /**
3803
3996
  * Register and start a speculative invocation. Returns false when at
3804
3997
  * capacity; the candidate is dropped silently (a miss costs nothing, the
3805
3998
  * real call executes normally later).
3806
3999
  */
3807
4000
  launch(parentToolCallId, ref, preparedArgs, execute, freshness, replay, bindingToken) {
4001
+ this.#releaseLaunch(parentToolCallId);
3808
4002
  this.#sweepExpired(Date.now());
3809
4003
  if (this.#entries.size >= this.#maxEntries || this.#inFlightCount() >= this.#maxConcurrent) {
3810
4004
  this.#stats.skipped += 1;
@@ -3838,6 +4032,7 @@ ${bindingToken}`;
3838
4032
  const key = _FabricSpeculationStore.key(parentToolCallId, ref, preparedArgs, bindingToken);
3839
4033
  const entry = this.#entries.get(key);
3840
4034
  if (!entry || entry.parentToolCallId !== parentToolCallId) {
4035
+ this.#stats.absent += 1;
3841
4036
  return { hit: false, reason: "absent" };
3842
4037
  }
3843
4038
  this.#entries.delete(key);
@@ -3872,6 +4067,19 @@ ${bindingToken}`;
3872
4067
  reset() {
3873
4068
  for (const entry of this.#entries.values()) entry.controller.abort();
3874
4069
  this.#entries.clear();
4070
+ for (const pending of this.#launching.values()) {
4071
+ pending.count = 0;
4072
+ for (const waiter of pending.waiters.splice(0)) waiter();
4073
+ }
4074
+ this.#launching.clear();
4075
+ }
4076
+ #releaseLaunch(parentToolCallId) {
4077
+ const pending = this.#launching.get(parentToolCallId);
4078
+ if (!pending) return;
4079
+ pending.count -= 1;
4080
+ if (pending.count > 0) return;
4081
+ this.#launching.delete(parentToolCallId);
4082
+ for (const waiter of pending.waiters.splice(0)) waiter();
3875
4083
  }
3876
4084
  #inFlightCount() {
3877
4085
  let count = 0;
@@ -3890,201 +4098,6 @@ ${bindingToken}`;
3890
4098
  }
3891
4099
  };
3892
4100
 
3893
- // src/speculation/partial-json.ts
3894
- var CODE_KEY_PATTERN = /"code"\s*:\s*"/;
3895
- var ESCAPES = {
3896
- '"': '"',
3897
- "\\": "\\",
3898
- "/": "/",
3899
- b: "\b",
3900
- f: "\f",
3901
- n: "\n",
3902
- r: "\r",
3903
- t: " "
3904
- };
3905
- var PartialCodeFieldExtractor = class {
3906
- #raw = "";
3907
- #codeStart = -1;
3908
- #cursor = 0;
3909
- #decoded = "";
3910
- #complete = false;
3911
- #failed = false;
3912
- #maxBytes;
3913
- constructor(maxBytes) {
3914
- this.#maxBytes = maxBytes;
3915
- }
3916
- get complete() {
3917
- return this.#complete;
3918
- }
3919
- /** Decoded `code` content so far, or undefined before the key appears. */
3920
- get code() {
3921
- return this.#codeStart === -1 ? void 0 : this.#decoded;
3922
- }
3923
- push(delta) {
3924
- if (this.#complete || this.#failed) return;
3925
- if (this.#raw.length + delta.length > this.#maxBytes) {
3926
- this.#failed = true;
3927
- return;
3928
- }
3929
- this.#raw += delta;
3930
- if (this.#codeStart === -1) {
3931
- const match = CODE_KEY_PATTERN.exec(this.#raw);
3932
- if (!match) return;
3933
- this.#codeStart = match.index + match[0].length;
3934
- this.#cursor = this.#codeStart;
3935
- }
3936
- const raw = this.#raw;
3937
- while (this.#cursor < raw.length) {
3938
- const ch = raw[this.#cursor];
3939
- if (ch === '"') {
3940
- this.#complete = true;
3941
- this.#cursor = raw.length;
3942
- return;
3943
- }
3944
- if (ch === "\\") {
3945
- if (this.#cursor + 1 >= raw.length) return;
3946
- const esc = raw[this.#cursor + 1];
3947
- if (esc === "u") {
3948
- if (this.#cursor + 5 >= raw.length) return;
3949
- const hex = raw.slice(this.#cursor + 2, this.#cursor + 6);
3950
- if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
3951
- this.#failed = true;
3952
- return;
3953
- }
3954
- this.#decoded += String.fromCharCode(parseInt(hex, 16));
3955
- this.#cursor += 6;
3956
- continue;
3957
- }
3958
- const mapped = ESCAPES[esc];
3959
- if (mapped === void 0) {
3960
- this.#failed = true;
3961
- return;
3962
- }
3963
- this.#decoded += mapped;
3964
- this.#cursor += 2;
3965
- continue;
3966
- }
3967
- if (ch < " ") {
3968
- this.#failed = true;
3969
- return;
3970
- }
3971
- this.#decoded += ch;
3972
- this.#cursor += 1;
3973
- }
3974
- }
3975
- };
3976
-
3977
- // src/speculation/stream-tap.ts
3978
- var toolCallBlock = (partial, contentIndex) => {
3979
- const content = partial?.content;
3980
- if (!Array.isArray(content)) return {};
3981
- const block = content[contentIndex];
3982
- if (!block || typeof block !== "object") return {};
3983
- const record = block;
3984
- if (record.type !== "toolCall") return {};
3985
- return {
3986
- ...typeof record.name === "string" && record.name ? { name: record.name } : {},
3987
- ...typeof record.id === "string" && record.id ? { id: record.id } : {}
3988
- };
3989
- };
3990
- var PARSE_INTERVAL_MS = 50;
3991
- var FabricSpeculationStreamTap = class {
3992
- #options;
3993
- #streams = /* @__PURE__ */ new Map();
3994
- // The scanner depends on the TypeScript compiler; the factory arrives through
3995
- // a lazy dynamic import so session startup never pays for it. Streams opened
3996
- // before the factory lands are caught up in full (extractors buffer the
3997
- // whole decoded prefix, so no candidate is lost, only delayed).
3998
- #createScanner;
3999
- #lastParseAt = 0;
4000
- constructor(options) {
4001
- this.#options = options;
4002
- }
4003
- setScannerFactory(factory) {
4004
- this.#createScanner = factory;
4005
- for (const stream of this.#streams.values()) {
4006
- if (stream.scanner) continue;
4007
- stream.scanner = factory();
4008
- const code = stream.extractor.code;
4009
- if (!stream.isFabricExec || code === void 0) continue;
4010
- try {
4011
- for (const candidate of stream.scanner.push(code)) {
4012
- if (this.#options.isEligible(candidate.ref)) {
4013
- this.#pendingCatchUp.push({ stream, candidate });
4014
- }
4015
- }
4016
- } catch {
4017
- }
4018
- }
4019
- }
4020
- #pendingCatchUp = [];
4021
- /** Drain candidates recovered while the scanner module loaded. */
4022
- flushCatchUp(context) {
4023
- const pending = this.#pendingCatchUp.splice(0);
4024
- for (const { stream, candidate } of pending) {
4025
- this.#options.launch(stream.toolCallId, candidate, context);
4026
- }
4027
- }
4028
- /** New assistant message: content indices restart. */
4029
- reset() {
4030
- this.#streams.clear();
4031
- }
4032
- handleMessageUpdate(event, context) {
4033
- try {
4034
- if (!this.#options.enabled()) return;
4035
- if (this.#pendingCatchUp.length > 0) this.flushCatchUp(context);
4036
- const assistantEvent = event.assistantMessageEvent;
4037
- if (assistantEvent.type === "toolcall_start") {
4038
- const block = toolCallBlock(assistantEvent.partial, assistantEvent.contentIndex);
4039
- this.#streams.set(assistantEvent.contentIndex, {
4040
- toolCallId: block.id ?? `index-${assistantEvent.contentIndex}`,
4041
- isFabricExec: block.name !== void 0 ? block.name === "fabric_exec" : true,
4042
- extractor: new PartialCodeFieldExtractor(this.#options.maxBufferBytes()),
4043
- ...this.#createScanner ? { scanner: this.#createScanner() } : {}
4044
- });
4045
- return;
4046
- }
4047
- if (assistantEvent.type === "toolcall_delta") {
4048
- const stream = this.#streams.get(assistantEvent.contentIndex);
4049
- if (!stream) return;
4050
- const block = toolCallBlock(assistantEvent.partial, assistantEvent.contentIndex);
4051
- if (block.id) stream.toolCallId = block.id;
4052
- if (block.name !== void 0) stream.isFabricExec = block.name === "fabric_exec";
4053
- stream.extractor.push(assistantEvent.delta);
4054
- const code = stream.extractor.code;
4055
- if (!stream.isFabricExec || code === void 0) return;
4056
- this.#scan(stream, code, context, false);
4057
- return;
4058
- }
4059
- if (assistantEvent.type === "toolcall_end") {
4060
- const stream = this.#streams.get(assistantEvent.contentIndex);
4061
- if (!stream) return;
4062
- this.#streams.delete(assistantEvent.contentIndex);
4063
- const block = toolCallBlock(assistantEvent.toolCall, assistantEvent.contentIndex);
4064
- if (block.id) stream.toolCallId = block.id;
4065
- if (block.name !== void 0) stream.isFabricExec = block.name === "fabric_exec";
4066
- const code = stream.extractor.code;
4067
- if (stream.isFabricExec && code !== void 0) {
4068
- this.#scan(stream, code, context, true);
4069
- }
4070
- }
4071
- } catch {
4072
- }
4073
- }
4074
- #scan(stream, code, context, force) {
4075
- const now = Date.now();
4076
- if (!force && now - this.#lastParseAt < PARSE_INTERVAL_MS) return;
4077
- this.#lastParseAt = now;
4078
- if (!stream.scanner) return;
4079
- const candidates = stream.scanner.push(code);
4080
- for (const candidate of candidates) {
4081
- if (this.#options.isEligible(candidate.ref)) {
4082
- this.#options.launch(stream.toolCallId, candidate, context);
4083
- }
4084
- }
4085
- }
4086
- };
4087
-
4088
4101
  // src/lifecycle/delivery-scheduler.ts
4089
4102
  var DEFAULT_LIFECYCLE_COALESCE_MS = 2e3;
4090
4103
  var LifecycleDeliveryScheduler = class {
@@ -4829,7 +4842,7 @@ var AGENTS_ACTION_DESCRIPTORS = [
4829
4842
  // src/residency/client.ts
4830
4843
  import { randomUUID as randomUUID2 } from "node:crypto";
4831
4844
  import fs4 from "node:fs";
4832
- import path4 from "node:path";
4845
+ import path5 from "node:path";
4833
4846
  import { fileURLToPath } from "node:url";
4834
4847
  var STARTUP_TIMEOUT_MS = 3e4;
4835
4848
  var COMMAND_TIMEOUT_MS = 3e4;
@@ -4849,7 +4862,7 @@ var readJson = (filePath) => {
4849
4862
  var terminal = (status) => status === "completed" || status === "failed" || status === "stopped" || status === "timed_out";
4850
4863
  var samePath = (left, right) => {
4851
4864
  try {
4852
- return path4.relative(fs4.realpathSync.native(left), fs4.realpathSync.native(right)) === "";
4865
+ return path5.relative(fs4.realpathSync.native(left), fs4.realpathSync.native(right)) === "";
4853
4866
  } catch {
4854
4867
  return false;
4855
4868
  }
@@ -4878,12 +4891,12 @@ var ResidencyClient = class {
4878
4891
  constructor(options) {
4879
4892
  this.options = options;
4880
4893
  this.hostId = residentHostId(options.config.rootId);
4881
- this.#configPath = path4.join(options.config.residencyRoot, "config.json");
4882
- this.#ownerPath = path4.join(options.config.residencyRoot, "owner.json");
4883
- this.#errorPath = path4.join(options.config.residencyRoot, "error.json");
4884
- this.#requestsPath = path4.join(options.config.residencyRoot, "requests");
4885
- this.#responsesPath = path4.join(options.config.residencyRoot, "responses");
4886
- this.#agentsPath = path4.join(options.config.residencyRoot, "agents");
4894
+ this.#configPath = path5.join(options.config.residencyRoot, "config.json");
4895
+ this.#ownerPath = path5.join(options.config.residencyRoot, "owner.json");
4896
+ this.#errorPath = path5.join(options.config.residencyRoot, "error.json");
4897
+ this.#requestsPath = path5.join(options.config.residencyRoot, "requests");
4898
+ this.#responsesPath = path5.join(options.config.residencyRoot, "responses");
4899
+ this.#agentsPath = path5.join(options.config.residencyRoot, "agents");
4887
4900
  this.#deliveryPrefix = residentDeliveryPrefix(options.config.rootId);
4888
4901
  this.#hostPath = options.hostPath ?? fileURLToPath(new URL("./launcher.js", import.meta.url));
4889
4902
  }
@@ -4957,7 +4970,7 @@ var ResidencyClient = class {
4957
4970
  }
4958
4971
  const readIfPresent = (name) => {
4959
4972
  try {
4960
- return fs4.readFileSync(path4.join(this.options.config.residencyRoot, name), "utf8").trim();
4973
+ return fs4.readFileSync(path5.join(this.options.config.residencyRoot, name), "utf8").trim();
4961
4974
  } catch {
4962
4975
  return "";
4963
4976
  }
@@ -5019,13 +5032,13 @@ var ResidencyClient = class {
5019
5032
  statusAgent(id) {
5020
5033
  const metadata = this.#metadata(id);
5021
5034
  if (!metadata) throw new Error(`Unknown durable Fabric agent: ${id}`);
5022
- const record = readJson(path4.join(metadata.runDirectory, "status.json"));
5035
+ const record = readJson(path5.join(metadata.runDirectory, "status.json"));
5023
5036
  if (!record || record.id !== metadata.id) return structuredClone(metadata.handle);
5024
5037
  return {
5025
5038
  ...record,
5026
5039
  cwd: metadata.handle.cwd,
5027
5040
  residency: "durable",
5028
- logFile: path4.join(metadata.runDirectory, "events.jsonl"),
5041
+ logFile: path5.join(metadata.runDirectory, "events.jsonl"),
5029
5042
  ...metadata.handle.sessionId ? { sessionId: metadata.handle.sessionId } : {},
5030
5043
  ...metadata.handle.attachCommand ? { attachCommand: metadata.handle.attachCommand } : {}
5031
5044
  };
@@ -5066,9 +5079,9 @@ var ResidencyClient = class {
5066
5079
  readAgentLog(id, options = {}) {
5067
5080
  const metadata = this.#metadata(id);
5068
5081
  if (!metadata) throw new Error(`Unknown durable Fabric agent: ${id}`);
5069
- const logFile = path4.join(metadata.runDirectory, "events.jsonl");
5082
+ const logFile = path5.join(metadata.runDirectory, "events.jsonl");
5070
5083
  const page = readJsonlPage(logFile, Math.max(1, Math.min(options.lines ?? 200, 5e3)), options.before);
5071
- const status = readJson(path4.join(metadata.runDirectory, "status.json"));
5084
+ const status = readJson(path5.join(metadata.runDirectory, "status.json"));
5072
5085
  return {
5073
5086
  id,
5074
5087
  runDirectory: metadata.runDirectory,
@@ -5143,8 +5156,8 @@ var ResidencyClient = class {
5143
5156
  return { cleaned: true };
5144
5157
  }
5145
5158
  async #command(command, signal) {
5146
- const responsePath = path4.join(this.#responsesPath, `${command.requestId}.json`);
5147
- atomicWrite2(path4.join(this.#requestsPath, `${command.requestId}.json`), command);
5159
+ const responsePath = path5.join(this.#responsesPath, `${command.requestId}.json`);
5160
+ atomicWrite2(path5.join(this.#requestsPath, `${command.requestId}.json`), command);
5148
5161
  const deadline = Date.now() + COMMAND_TIMEOUT_MS;
5149
5162
  while (Date.now() < deadline) {
5150
5163
  if (signal?.aborted) throw new Error("Fabric residency request was aborted");
@@ -5172,12 +5185,12 @@ var ResidencyClient = class {
5172
5185
  throw new Error(`Timed out publishing durable Fabric ${kind} ${id} from ${this.hostId}`);
5173
5186
  }
5174
5187
  #metadataPath(id) {
5175
- return path4.join(this.#agentsPath, `${id}.json`);
5188
+ return path5.join(this.#agentsPath, `${id}.json`);
5176
5189
  }
5177
5190
  #metadata(id) {
5178
5191
  if (!AGENT_ID_PATTERN.test(id)) return void 0;
5179
5192
  const metadata = readJson(this.#metadataPath(id));
5180
- 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)) {
5193
+ 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)) {
5181
5194
  return void 0;
5182
5195
  }
5183
5196
  if (metadata.handle.worktree && !isFabricWorktreePath(metadata.handle.worktree, id)) {
@@ -6518,7 +6531,7 @@ var AgentsProvider = class {
6518
6531
  };
6519
6532
 
6520
6533
  // src/providers/captured-tools-provider.ts
6521
- import path5 from "node:path";
6534
+ import path6 from "node:path";
6522
6535
 
6523
6536
  // src/core/child-tool-allowlist.ts
6524
6537
  function readChildToolAllowlist(raw = process.env.OMP_FABRIC_TOOL_ALLOWLIST) {
@@ -6544,9 +6557,9 @@ var sourceLabel = (sourceInfo) => {
6544
6557
  while (index >= 0 && Object.hasOwn(SOURCE_DIRECTORIES, segments[index])) index -= 1;
6545
6558
  const owning = index >= 0 ? segments[index] : void 0;
6546
6559
  if (owning !== void 0 && PACKAGE_SEGMENT.test(owning)) return owning;
6547
- const filename = path5.basename(sourceInfo.path).replace(/\.[^.]+$/, "");
6560
+ const filename = path6.basename(sourceInfo.path).replace(/\.[^.]+$/, "");
6548
6561
  if (filename && filename !== "index") return filename;
6549
- return path5.basename(path5.dirname(sourceInfo.path)) || sourceInfo.source;
6562
+ return path6.basename(path6.dirname(sourceInfo.path)) || sourceInfo.source;
6550
6563
  };
6551
6564
  var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
6552
6565
  var schemaDocument = (schema) => {
@@ -6807,7 +6820,7 @@ var descriptors = [
6807
6820
  },
6808
6821
  {
6809
6822
  name: "status",
6810
- description: "Read the pending compaction intent and the last compaction outcome",
6823
+ description: "Read context occupancy (context.tokens/percent/contextWindow, context.known=false when the host has not measured yet), the configured engine and targetContextRatio, the pending compaction intent, and the last settled outcome (status committed | skipped | cancelled | failed, with tokensBefore/estimatedTokensAfter)",
6811
6824
  inputSchema: emptySchema,
6812
6825
  risk: "read"
6813
6826
  },
@@ -6863,7 +6876,7 @@ var CompactProvider = class {
6863
6876
  return { requested: true, intent };
6864
6877
  }
6865
6878
  case "status":
6866
- return this.controller.status();
6879
+ return this.controller.status(context.extensionContext);
6867
6880
  case "cancel":
6868
6881
  this.controller.cancel();
6869
6882
  context.activity?.({ type: "progress", message: "Compaction request cancelled" });
@@ -6963,7 +6976,7 @@ var ComponentsProvider = class {
6963
6976
  import fs5 from "node:fs/promises";
6964
6977
  import fsSync from "node:fs";
6965
6978
  import os from "node:os";
6966
- import path6 from "node:path";
6979
+ import path7 from "node:path";
6967
6980
  var MCP_DESCRIPTOR_CACHE_VERSION = 1;
6968
6981
  var homeDir = () => {
6969
6982
  const home = process.env.HOME;
@@ -6974,25 +6987,25 @@ var expandHome = (input) => {
6974
6987
  const home = homeDir();
6975
6988
  if (input === "~") return home;
6976
6989
  if (input.startsWith("~/") || input.startsWith("~\\")) {
6977
- return path6.join(home, input.slice(2));
6990
+ return path7.join(home, input.slice(2));
6978
6991
  }
6979
6992
  return input;
6980
6993
  };
6981
- var legacyMcporterDir = () => path6.join(homeDir(), ".mcporter");
6994
+ var legacyMcporterDir = () => path7.join(homeDir(), ".mcporter");
6982
6995
  var mcporterConfigDir = () => {
6983
6996
  const raw = process.env.XDG_CONFIG_HOME;
6984
6997
  if (raw && raw.trim().length > 0) {
6985
6998
  const resolved = expandHome(raw.trim());
6986
- if (path6.isAbsolute(resolved)) return path6.join(resolved, "mcporter");
6999
+ if (path7.isAbsolute(resolved)) return path7.join(resolved, "mcporter");
6987
7000
  }
6988
7001
  return legacyMcporterDir();
6989
7002
  };
6990
7003
  var mcporterConfigCandidates = () => {
6991
7004
  const base = mcporterConfigDir();
6992
- const candidates = [path6.join(base, "mcporter.json"), path6.join(base, "mcporter.jsonc")];
7005
+ const candidates = [path7.join(base, "mcporter.json"), path7.join(base, "mcporter.jsonc")];
6993
7006
  const legacy = legacyMcporterDir();
6994
7007
  if (base !== legacy) {
6995
- candidates.push(path6.join(legacy, "mcporter.json"), path6.join(legacy, "mcporter.jsonc"));
7008
+ candidates.push(path7.join(legacy, "mcporter.json"), path7.join(legacy, "mcporter.jsonc"));
6996
7009
  }
6997
7010
  return candidates;
6998
7011
  };
@@ -7007,12 +7020,12 @@ var pathExists = (filePath) => {
7007
7020
  var mcpConfigLayerPaths = (rootDir, configPath) => {
7008
7021
  const explicitRaw = configPath ?? process.env.MCPORTER_CONFIG;
7009
7022
  if (explicitRaw && explicitRaw.trim().length > 0) {
7010
- return [path6.resolve(expandHome(explicitRaw.trim()))];
7023
+ return [path7.resolve(expandHome(explicitRaw.trim()))];
7011
7024
  }
7012
7025
  const paths = [];
7013
7026
  const home = mcporterConfigCandidates().find(pathExists);
7014
7027
  if (home) paths.push(home);
7015
- const projectPath = path6.resolve(rootDir, "config", "mcporter.json");
7028
+ const projectPath = path7.resolve(rootDir, "config", "mcporter.json");
7016
7029
  if (pathExists(projectPath)) paths.push(projectPath);
7017
7030
  return paths;
7018
7031
  };
@@ -7063,6 +7076,8 @@ var parseCacheFile = (value) => {
7063
7076
  servers: value.servers
7064
7077
  };
7065
7078
  };
7079
+ var projectMcpCachePath = (projectRoot) => path7.join(projectRoot, ".omp", "fabric", "mcp-cache.json");
7080
+ var globalMcpCachePath = (agentDir) => path7.join(agentDir, "fabric", "mcp-descriptors.json");
7066
7081
  var tempCounter = 0;
7067
7082
  var McpDescriptorCacheStore = class {
7068
7083
  constructor(filePath) {
@@ -7079,9 +7094,9 @@ var McpDescriptorCacheStore = class {
7079
7094
  // Atomic-ish write: temp file then rename, so a concurrent reader either
7080
7095
  // sees the previous cache or the next one, never a torn document.
7081
7096
  async save(file) {
7082
- const directory = path6.dirname(this.filePath);
7097
+ const directory = path7.dirname(this.filePath);
7083
7098
  await fs5.mkdir(directory, { recursive: true });
7084
- const tempPath = path6.join(
7099
+ const tempPath = path7.join(
7085
7100
  directory,
7086
7101
  `.mcp-cache-${process.pid}-${Date.now()}-${tempCounter++}.tmp`
7087
7102
  );
@@ -7096,13 +7111,14 @@ var McpDescriptorCacheStore = class {
7096
7111
  };
7097
7112
 
7098
7113
  // src/providers/mcp-provider.ts
7099
- import path7 from "node:path";
7114
+ import path8 from "node:path";
7100
7115
  var TOOL_METADATA_TTL_MS = 6e4;
7101
7116
  var REVALIDATE_CONCURRENCY = 3;
7102
7117
  var REVALIDATE_SERVER_TIMEOUT_MS = 2e4;
7103
7118
  var MIN_REVALIDATE_SERVER_TIMEOUT_MS = 5e3;
7104
7119
  var NOTIFY_DEBOUNCE_MS = 100;
7105
7120
  var PERSIST_DEBOUNCE_MS = 150;
7121
+ var COLD_LIST_WAIT_MS = 2500;
7106
7122
  var emptyObjectSchema = {
7107
7123
  type: "object",
7108
7124
  properties: {},
@@ -7177,6 +7193,18 @@ var normalizeMcpResult = (result) => {
7177
7193
  structuredContent: record.structuredContent ?? null
7178
7194
  };
7179
7195
  };
7196
+ var delay2 = (ms, signal) => new Promise((resolve2) => {
7197
+ const timer = setTimeout(() => {
7198
+ signal?.removeEventListener("abort", onAbort);
7199
+ resolve2();
7200
+ }, ms);
7201
+ timer.unref?.();
7202
+ const onAbort = () => {
7203
+ clearTimeout(timer);
7204
+ resolve2();
7205
+ };
7206
+ signal?.addEventListener("abort", onAbort, { once: true });
7207
+ });
7180
7208
  var withTimeout = (operation, timeoutMs, onTimeout) => new Promise((resolve2, reject) => {
7181
7209
  const timer = setTimeout(() => {
7182
7210
  onTimeout?.();
@@ -7199,6 +7227,7 @@ var McpProvider = class {
7199
7227
  this.cwd = cwd;
7200
7228
  this.config = config;
7201
7229
  this.#store = options.cache;
7230
+ this.#globalStore = options.globalCache;
7202
7231
  this.#hooks = options.hooks ?? {};
7203
7232
  }
7204
7233
  name = "mcp";
@@ -7207,6 +7236,7 @@ var McpProvider = class {
7207
7236
  #runtimeCreation;
7208
7237
  #toolMetadata = /* @__PURE__ */ new Map();
7209
7238
  #store;
7239
+ #globalStore;
7210
7240
  #hooks;
7211
7241
  #generation = 0;
7212
7242
  #closed = false;
@@ -7217,8 +7247,10 @@ var McpProvider = class {
7217
7247
  #revalidateQueue = [];
7218
7248
  #revalidateQueued = /* @__PURE__ */ new Set();
7219
7249
  #recontacted = /* @__PURE__ */ new Set();
7250
+ #seeded = /* @__PURE__ */ new Set();
7220
7251
  #revalidating;
7221
7252
  #autoKicked = false;
7253
+ #coldWaited = false;
7222
7254
  #dirtyPersist = false;
7223
7255
  #dirtyNotify = false;
7224
7256
  #persistTimer;
@@ -7231,6 +7263,7 @@ var McpProvider = class {
7231
7263
  if (!this.#cacheOn) return this.#listLegacy(request, context);
7232
7264
  await this.#hydrate();
7233
7265
  this.#kickRevalidation();
7266
+ if (!request.namespace) await this.#awaitColdEnumeration(context.signal);
7234
7267
  const query = request.query?.toLowerCase();
7235
7268
  const filterQuery = (descriptors6) => query ? descriptors6.filter(
7236
7269
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
@@ -7298,6 +7331,8 @@ var McpProvider = class {
7298
7331
  this.#servers.clear();
7299
7332
  this.#pending.clear();
7300
7333
  this.#recontacted.clear();
7334
+ this.#seeded.clear();
7335
+ this.#coldWaited = false;
7301
7336
  this.#hydration = void 0;
7302
7337
  await this.#hydrate();
7303
7338
  this.#kickRevalidation(true);
@@ -7347,11 +7382,36 @@ var McpProvider = class {
7347
7382
  if (this.#dirtyPersist) await this.#persistNow().catch(() => void 0);
7348
7383
  await this.#resetRuntime();
7349
7384
  }
7350
- // Fire-and-forget session warm-up: hydrate from the descriptor cache, then
7351
- // start the background revalidation policy. Never awaited by session start.
7352
7385
  warmup() {
7353
- if (!this.config.enabled || !this.#cacheOn) return;
7354
- void this.#hydrate().then(() => this.#kickRevalidation()).catch(() => void 0);
7386
+ if (!this.config.enabled || !this.#cacheOn) return Promise.resolve();
7387
+ return this.#hydrate().then(() => this.#kickRevalidation()).catch(() => void 0);
7388
+ }
7389
+ listCoverage() {
7390
+ if (!this.config.enabled || !this.#cacheOn) return { complete: true, reasons: [] };
7391
+ const pending = [...this.#pending.keys()].sort();
7392
+ if (pending.length === 0) return { complete: true, reasons: [] };
7393
+ return {
7394
+ complete: false,
7395
+ reasons: [
7396
+ "enumeration_pending",
7397
+ ...pending.map((server) => `server_pending:${server}`)
7398
+ ]
7399
+ };
7400
+ }
7401
+ async #awaitColdEnumeration(signal) {
7402
+ if (this.#coldWaited || this.#closed) return;
7403
+ this.#coldWaited = true;
7404
+ if (this.#pending.size === 0 || this.#servers.size > 0) return;
7405
+ const budget = Math.min(COLD_LIST_WAIT_MS, Math.max(0, this.config.cache.revalidateBudgetMs));
7406
+ if (budget <= 0) return;
7407
+ const deadline = Date.now() + budget;
7408
+ while (!this.#closed && this.#pending.size > 0 && signal?.aborted !== true) {
7409
+ const remaining = deadline - Date.now();
7410
+ if (remaining <= 0) return;
7411
+ const inflight = this.#revalidating;
7412
+ if (!inflight) return;
7413
+ await Promise.race([inflight, delay2(remaining, signal)]);
7414
+ }
7355
7415
  }
7356
7416
  // Provider-fidelity descriptors for everything currently known, cached or
7357
7417
  // ephemeral.
@@ -7446,10 +7506,13 @@ var McpProvider = class {
7446
7506
  }
7447
7507
  async #hydrateInternal() {
7448
7508
  const generation = this.#generation;
7449
- const snapshot = this.#store ? await this.#store.load().catch(() => void 0) : void 0;
7509
+ const [snapshot, globalSnapshot] = await Promise.all([
7510
+ this.#store ? this.#store.load().catch(() => void 0) : Promise.resolve(void 0),
7511
+ this.#globalStore ? this.#globalStore.load().catch(() => void 0) : Promise.resolve(void 0)
7512
+ ]);
7450
7513
  this.#layerStats = await statConfigLayers(this.cwd, this.config.configPath);
7451
7514
  if (generation !== this.#generation) return;
7452
- if (snapshot && sameConfigLayers(snapshot.layers, this.#layerStats)) {
7515
+ if (snapshot && Object.keys(snapshot.servers).length > 0 && sameConfigLayers(snapshot.layers, this.#layerStats)) {
7453
7516
  for (const [name, raw] of Object.entries(snapshot.servers)) {
7454
7517
  const parsed = parseCachedServer(raw);
7455
7518
  if (parsed) this.#servers.set(name, this.#toWorking(parsed, false));
@@ -7465,15 +7528,21 @@ var McpProvider = class {
7465
7528
  const parsed = recorded ? parseCachedServer(recorded) : void 0;
7466
7529
  if (parsed && parsed.definitionHash === hash) {
7467
7530
  this.#servers.set(definition.name, this.#toWorking(parsed, false));
7468
- } else {
7469
- const existing = this.#pending.get(definition.name);
7470
- this.#pending.set(definition.name, {
7471
- definitionHash: hash,
7472
- transport: definition.command.kind,
7473
- description: definition.description ?? null,
7474
- ephemeral: existing?.ephemeral ?? false
7475
- });
7531
+ continue;
7532
+ }
7533
+ const seed = globalSnapshot ? parseCachedServer(globalSnapshot.servers[definition.name]) : void 0;
7534
+ if (seed && seed.definitionHash === hash && seed.tools.length > 0) {
7535
+ this.#servers.set(definition.name, { ...this.#toWorking(seed, false), stale: true });
7536
+ this.#seeded.add(definition.name);
7537
+ continue;
7476
7538
  }
7539
+ const existing = this.#pending.get(definition.name);
7540
+ this.#pending.set(definition.name, {
7541
+ definitionHash: hash,
7542
+ transport: definition.command.kind,
7543
+ description: definition.description ?? null,
7544
+ ephemeral: existing?.ephemeral ?? false
7545
+ });
7477
7546
  }
7478
7547
  this.#dirtyPersist = true;
7479
7548
  this.#schedulePersist();
@@ -7509,7 +7578,7 @@ var McpProvider = class {
7509
7578
  this.#autoKicked = true;
7510
7579
  const policy = this.config.cache.revalidate;
7511
7580
  if (policy === "off" && !forceAll) return;
7512
- const targets = forceAll || policy === "all" ? [...this.#servers.keys(), ...this.#pending.keys()] : [...this.#pending.keys()];
7581
+ const targets = forceAll || policy === "all" ? [...this.#servers.keys(), ...this.#pending.keys()] : [...this.#pending.keys(), ...this.#seeded];
7513
7582
  this.#scheduleRevalidate(targets);
7514
7583
  }
7515
7584
  #scheduleRevalidate(servers) {
@@ -7616,7 +7685,7 @@ var McpProvider = class {
7616
7685
  return sanitized.length === 1 ? sanitized[0] : void 0;
7617
7686
  }
7618
7687
  #schedulePersist() {
7619
- if (!this.#store || this.#closed) return;
7688
+ if (!this.#store && !this.#globalStore || this.#closed) return;
7620
7689
  this.#dirtyPersist = true;
7621
7690
  if (this.#persistTimer) return;
7622
7691
  this.#persistTimer = setTimeout(() => {
@@ -7627,7 +7696,7 @@ var McpProvider = class {
7627
7696
  }
7628
7697
  #persistNow() {
7629
7698
  this.#dirtyPersist = false;
7630
- if (!this.#store) return Promise.resolve();
7699
+ if (!this.#store && !this.#globalStore) return Promise.resolve();
7631
7700
  const servers = {};
7632
7701
  for (const [name, entry] of this.#servers) {
7633
7702
  if (entry.ephemeral) continue;
@@ -7649,11 +7718,36 @@ var McpProvider = class {
7649
7718
  })
7650
7719
  };
7651
7720
  }
7652
- return this.#store.save({
7721
+ const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
7722
+ return Promise.all([
7723
+ this.#store?.save({
7724
+ version: MCP_DESCRIPTOR_CACHE_VERSION,
7725
+ layers: this.#layerStats,
7726
+ updatedAt,
7727
+ servers
7728
+ }),
7729
+ this.#persistGlobal(servers, updatedAt)
7730
+ ]).then(() => void 0);
7731
+ }
7732
+ async #persistGlobal(servers, updatedAt) {
7733
+ const store = this.#globalStore;
7734
+ if (!store) return;
7735
+ const enumerated = Object.entries(servers).filter(
7736
+ ([, entry]) => entry.tools.length > 0 && !entry.stale
7737
+ );
7738
+ if (enumerated.length === 0) return;
7739
+ const existing = await store.load().catch(() => void 0);
7740
+ const merged = {};
7741
+ for (const [name, raw] of Object.entries(existing?.servers ?? {})) {
7742
+ const parsed = parseCachedServer(raw);
7743
+ if (parsed) merged[name] = parsed;
7744
+ }
7745
+ for (const [name, entry] of enumerated) merged[name] = { ...entry };
7746
+ await store.save({
7653
7747
  version: MCP_DESCRIPTOR_CACHE_VERSION,
7654
- layers: this.#layerStats,
7655
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
7656
- servers
7748
+ layers: [],
7749
+ updatedAt,
7750
+ servers: merged
7657
7751
  });
7658
7752
  }
7659
7753
  #scheduleNotify() {
@@ -7728,7 +7822,7 @@ var McpProvider = class {
7728
7822
  kind: "stdio",
7729
7823
  command: args.command,
7730
7824
  args: commandArgs,
7731
- cwd: path7.resolve(this.cwd, typeof args.cwd === "string" ? args.cwd : ".")
7825
+ cwd: path8.resolve(this.cwd, typeof args.cwd === "string" ? args.cwd : ".")
7732
7826
  },
7733
7827
  ...env ? { env } : {}
7734
7828
  };
@@ -8094,13 +8188,16 @@ var MeshProvider = class {
8094
8188
  };
8095
8189
 
8096
8190
  // src/providers/omp-tools-provider.ts
8097
- import { mkdtempSync, readFileSync, writeFileSync } from "node:fs";
8191
+ import { mkdirSync as mkdirSync2, mkdtempSync, readFileSync as readFileSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
8192
+ import { AsyncLocalStorage } from "node:async_hooks";
8098
8193
  import os2 from "node:os";
8099
- import path10 from "node:path";
8194
+ import path11 from "node:path";
8100
8195
  import { randomUUID as randomUUID3 } from "node:crypto";
8101
8196
  import { BashTool } from "@oh-my-pi/pi-coding-agent/tools/bash";
8102
8197
  import { EditTool } from "@oh-my-pi/pi-coding-agent/edit";
8198
+ import { GrepTool, MULTI_FILE_PER_FILE_MATCHES, SINGLE_FILE_MATCHES } from "@oh-my-pi/pi-coding-agent/tools/grep";
8103
8199
  import { ReadTool } from "@oh-my-pi/pi-coding-agent/tools/read";
8200
+ import { piEscapeRegexLiteral, piJoinPath } from "@oh-my-pi/pi-ai/providers/cursor-pi-args";
8104
8201
  import { Settings } from "@oh-my-pi/pi-coding-agent";
8105
8202
  import {
8106
8203
  createFindToolDefinition,
@@ -8110,7 +8207,7 @@ import {
8110
8207
 
8111
8208
  // src/agents/bash-worktree-add.ts
8112
8209
  import fs6 from "node:fs";
8113
- import path8 from "node:path";
8210
+ import path9 from "node:path";
8114
8211
  var BARE_SAFE = /^[A-Za-z0-9_./:@%+=,-]+$/;
8115
8212
  var parseGitWorktreeAdd = (command) => {
8116
8213
  const trimmed = command.trim();
@@ -8173,8 +8270,8 @@ var tryExecuteGitWorktreeAdd = async (args, sessionCwd) => {
8173
8270
  if (typeof command !== "string") return void 0;
8174
8271
  const parsed = parseGitWorktreeAdd(command);
8175
8272
  if (!parsed) return void 0;
8176
- const gitCwd = parsed.gitCwd ? path8.resolve(sessionCwd, parsed.gitCwd) : typeof args.cwd === "string" ? args.cwd : sessionCwd;
8177
- const dest = path8.resolve(gitCwd, parsed.dest);
8273
+ const gitCwd = parsed.gitCwd ? path9.resolve(sessionCwd, parsed.gitCwd) : typeof args.cwd === "string" ? args.cwd : sessionCwd;
8274
+ const dest = path9.resolve(gitCwd, parsed.dest);
8178
8275
  let gitRoot;
8179
8276
  try {
8180
8277
  const root = await executeFile("git", ["rev-parse", "--show-toplevel"], {
@@ -8202,8 +8299,8 @@ var tryExecuteGitWorktreeAdd = async (args, sessionCwd) => {
8202
8299
  };
8203
8300
 
8204
8301
  // src/providers/omp-bash-cwd.ts
8205
- import { accessSync, constants, statSync } from "node:fs";
8206
- import path9 from "node:path";
8302
+ import { accessSync, constants, statSync as statSync2 } from "node:fs";
8303
+ import path10 from "node:path";
8207
8304
  import { createBashToolDefinition } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-pi-coding-agent-shim";
8208
8305
  import { Type as Type2 } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-typebox";
8209
8306
  var OMP_BASH_CWD_KEY = "cwd";
@@ -8213,9 +8310,9 @@ var resolveOmpShellCwd = (toolName, sessionCwd, requested) => {
8213
8310
  `Invalid omp.${toolName} cwd ${JSON.stringify(requested)}: path must be a non-empty string`
8214
8311
  );
8215
8312
  }
8216
- const resolved = path9.isAbsolute(requested) ? path9.resolve(requested) : path9.resolve(sessionCwd, requested);
8313
+ const resolved = path10.isAbsolute(requested) ? path10.resolve(requested) : path10.resolve(sessionCwd, requested);
8217
8314
  try {
8218
- if (!statSync(resolved).isDirectory()) throw new Error("path is not a directory");
8315
+ if (!statSync2(resolved).isDirectory()) throw new Error("path is not a directory");
8219
8316
  accessSync(resolved, constants.R_OK | constants.X_OK);
8220
8317
  } catch (error) {
8221
8318
  const reason = error instanceof Error ? error.message : String(error);
@@ -8292,19 +8389,19 @@ import { dirname, isAbsolute, resolve } from "node:path";
8292
8389
  import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
8293
8390
  import { Type as Type3 } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-typebox";
8294
8391
  var mutationQueues = /* @__PURE__ */ new Map();
8295
- var withFileMutationQueue = async (path16, operation) => {
8296
- const previous = mutationQueues.get(path16) ?? Promise.resolve();
8392
+ var withFileMutationQueue = async (path17, operation) => {
8393
+ const previous = mutationQueues.get(path17) ?? Promise.resolve();
8297
8394
  let release;
8298
8395
  const current = new Promise((resolve2) => {
8299
8396
  release = resolve2;
8300
8397
  });
8301
- mutationQueues.set(path16, current);
8398
+ mutationQueues.set(path17, current);
8302
8399
  await previous;
8303
8400
  try {
8304
8401
  return await operation();
8305
8402
  } finally {
8306
8403
  release();
8307
- if (mutationQueues.get(path16) === current) mutationQueues.delete(path16);
8404
+ if (mutationQueues.get(path17) === current) mutationQueues.delete(path17);
8308
8405
  }
8309
8406
  };
8310
8407
  var resolvePreviewPath = (filePath, cwd) => {
@@ -8357,14 +8454,14 @@ var createPreviewWriteToolDefinition = (cwd) => {
8357
8454
  return {
8358
8455
  ...original,
8359
8456
  async execute(_toolCallId, params, signal) {
8360
- const { path: path16, content } = params;
8361
- const absolutePath = resolvePreviewPath(path16, cwd);
8457
+ const { path: path17, content } = params;
8458
+ const absolutePath = resolvePreviewPath(path17, cwd);
8362
8459
  return withFileMutationQueue(absolutePath, async () => {
8363
8460
  const throwIfAborted2 = () => {
8364
8461
  if (signal?.aborted) throw new Error("Operation aborted");
8365
8462
  };
8366
8463
  throwIfAborted2();
8367
- const before = await readExistingFileForPreview(path16, cwd, content);
8464
+ const before = await readExistingFileForPreview(path17, cwd, content);
8368
8465
  throwIfAborted2();
8369
8466
  await mkdir(dirname(absolutePath), { recursive: true });
8370
8467
  throwIfAborted2();
@@ -8374,7 +8471,7 @@ var createPreviewWriteToolDefinition = (cwd) => {
8374
8471
  content: [
8375
8472
  {
8376
8473
  type: "text",
8377
- text: `Successfully wrote ${Buffer.byteLength(content, "utf8")} bytes to ${path16}`
8474
+ text: `Successfully wrote ${Buffer.byteLength(content, "utf8")} bytes to ${path17}`
8378
8475
  }
8379
8476
  ],
8380
8477
  details: { codePreviewBeforeWrite: before }
@@ -8386,27 +8483,39 @@ var createPreviewWriteToolDefinition = (cwd) => {
8386
8483
 
8387
8484
  // src/providers/omp-tools-provider.ts
8388
8485
  var MAX_RENDERER_ARGUMENT_CHARS = 2e5;
8486
+ var GUEST_READ_LINE_LIMIT = 1e5;
8389
8487
  var MAX_REPLACE_ALL_FILE_CHARS = 2e6;
8390
8488
  var createNativeSession = (cwd, artifactPaths) => {
8391
- const artifactRoot = artifactPaths ? mkdtempSync(path10.join(os2.tmpdir(), "omp-fabric-bash-")) : void 0;
8489
+ const artifactRoot = artifactPaths ? mkdtempSync(path11.join(os2.tmpdir(), "omp-fabric-bash-")) : void 0;
8392
8490
  return {
8393
8491
  cwd,
8394
8492
  hasUI: false,
8395
8493
  hasEditTool: false,
8396
8494
  getSessionFile: () => null,
8397
8495
  getSessionSpawns: () => null,
8398
- settings: Settings.isolated({ readLineNumbers: false }),
8496
+ settings: Settings.isolated({
8497
+ readLineNumbers: false,
8498
+ "read.defaultLimit": GUEST_READ_LINE_LIMIT,
8499
+ "tools.outputMaxColumns": 0
8500
+ }),
8399
8501
  ...artifactRoot && artifactPaths ? {
8400
8502
  allocateOutputArtifact: async (toolType) => {
8401
- const id = randomUUID3();
8402
- const artifactPath = path10.join(artifactRoot, `${toolType}-${id}.log`);
8403
- writeFileSync(artifactPath, "", { mode: 384 });
8404
- artifactPaths.set(id, artifactPath);
8405
- return { id, path: artifactPath };
8503
+ try {
8504
+ mkdirSync2(artifactRoot, { recursive: true, mode: 448 });
8505
+ const id = randomUUID3();
8506
+ const artifactPath = path11.join(artifactRoot, `${toolType}-${id}.log`);
8507
+ writeFileSync2(artifactPath, "", { mode: 384 });
8508
+ artifactPaths.set(id, artifactPath);
8509
+ artifactAllocations.getStore()?.push(artifactPath);
8510
+ return { id, path: artifactPath };
8511
+ } catch {
8512
+ return {};
8513
+ }
8406
8514
  }
8407
8515
  } : {}
8408
8516
  };
8409
8517
  };
8518
+ var artifactAllocations = new AsyncLocalStorage();
8410
8519
  var nativeDefinition = (tool) => {
8411
8520
  const execute = (toolCallId, params, signal, onUpdate, context) => tool.execute(toolCallId, params, signal, onUpdate, context);
8412
8521
  return {
@@ -8420,6 +8529,65 @@ var nativeDefinition = (tool) => {
8420
8529
  var createNativeBashToolDefinition = (cwd, artifactPaths) => nativeDefinition(new BashTool(createNativeSession(cwd, artifactPaths)));
8421
8530
  var createNativeReadToolDefinition = (cwd) => nativeDefinition(new ReadTool(createNativeSession(cwd)));
8422
8531
  var createNativeReplaceEditToolDefinition = (cwd) => nativeDefinition(new EditTool(createNativeSession(cwd), "replace"));
8532
+ var normalizeReadArguments = (args) => {
8533
+ const offset = finiteNumber2(args.offset);
8534
+ const limit = finiteNumber2(args.limit);
8535
+ if (offset === void 0 && limit === void 0) return args;
8536
+ const { offset: _offset, limit: _limit, ...rest } = args;
8537
+ const target = parseReadTarget(args.path);
8538
+ if (!target || target.selected) return rest;
8539
+ const start = offset === void 0 ? 1 : Math.max(1, Math.floor(offset));
8540
+ const end = limit === void 0 ? Number.POSITIVE_INFINITY : start + Math.max(1, Math.floor(limit)) - 1;
8541
+ return { ...rest, path: readPageSelector({ ...target, endLimit: end }, start) };
8542
+ };
8543
+ var GREP_SKIP_PROPERTY = {
8544
+ type: "number",
8545
+ description: "Files to skip before collecting results \u2014 paginate when a search hit the file limit"
8546
+ };
8547
+ var withGrepSkipSchema = (document) => {
8548
+ const schema = asRecord(document);
8549
+ const properties = asRecord(schema?.properties);
8550
+ if (!schema || !properties || Object.hasOwn(properties, "skip")) return document;
8551
+ return { ...schema, properties: { ...properties, skip: GREP_SKIP_PROPERTY } };
8552
+ };
8553
+ var grepSession = (cwd, context) => ({
8554
+ cwd,
8555
+ hasUI: false,
8556
+ getSessionFile: () => null,
8557
+ getSessionSpawns: () => null,
8558
+ settings: Settings.isolated(
8559
+ context === void 0 ? void 0 : { "grep.contextBefore": Math.max(0, Math.floor(context)), "grep.contextAfter": Math.max(0, Math.floor(context)) }
8560
+ )
8561
+ });
8562
+ var createGrepDefinitionWithSkip = (cwd) => {
8563
+ const legacy = createGrepToolDefinition(cwd);
8564
+ const defaultTool = new GrepTool(grepSession(cwd));
8565
+ return {
8566
+ ...legacy,
8567
+ parameters: withGrepSkipSchema(legacy.parameters),
8568
+ execute: (toolCallId, params, signal, onUpdate, context) => {
8569
+ const record = asRecord(params) ?? {};
8570
+ const rawPattern = typeof record.pattern === "string" ? record.pattern : "";
8571
+ const searchPath = typeof record.path === "string" ? record.path : ".";
8572
+ const glob = typeof record.glob === "string" ? record.glob : void 0;
8573
+ const contextLines = typeof record.context === "number" ? record.context : void 0;
8574
+ const skip = typeof record.skip === "number" ? record.skip : void 0;
8575
+ const tool = contextLines === void 0 ? defaultTool : new GrepTool(grepSession(cwd, contextLines));
8576
+ return tool.execute(
8577
+ toolCallId,
8578
+ {
8579
+ pattern: record.literal === true ? piEscapeRegexLiteral(rawPattern) : rawPattern,
8580
+ path: glob ? piJoinPath(searchPath, glob) : searchPath,
8581
+ case: record.ignoreCase === true ? false : void 0,
8582
+ ...skip === void 0 ? {} : { skip }
8583
+ },
8584
+ signal,
8585
+ onUpdate,
8586
+ context
8587
+ );
8588
+ }
8589
+ };
8590
+ };
8423
8591
  var normalizeEditArguments = (args, replaceAll, cwd) => {
8424
8592
  const filePath = args.path;
8425
8593
  if (typeof filePath !== "string") throw new Error("omp.edit requires a path");
@@ -8442,8 +8610,8 @@ var normalizeEditArguments = (args, replaceAll, cwd) => {
8442
8610
  ...replaceAll || record.all === true || record.replace_all === true ? { replace_all: true } : {}
8443
8611
  };
8444
8612
  });
8445
- const resolvedPath = path10.resolve(cwd, filePath.startsWith("@") ? filePath.slice(1) : filePath);
8446
- const current = readFileSync(resolvedPath, "utf8");
8613
+ const resolvedPath = path11.resolve(cwd, filePath.startsWith("@") ? filePath.slice(1) : filePath);
8614
+ const current = readFileSync2(resolvedPath, "utf8");
8447
8615
  if (current.length > MAX_REPLACE_ALL_FILE_CHARS) {
8448
8616
  throw new Error(`omp.edit refuses files over ${MAX_REPLACE_ALL_FILE_CHARS} characters`);
8449
8617
  }
@@ -8503,6 +8671,203 @@ var imageBlocks = (content) => {
8503
8671
  }
8504
8672
  return blocks;
8505
8673
  };
8674
+ var TRUNCATION_MARKER = "[[omp-fabric:truncated]]";
8675
+ var asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
8676
+ var finiteNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
8677
+ var appendTruncationMarker = (text, signal) => `${text.endsWith("\n") || text.length === 0 ? text : `${text}
8678
+ `}${TRUNCATION_MARKER} ${JSON.stringify(signal)}
8679
+ `;
8680
+ var readTruncationMeta = (details) => {
8681
+ const truncation = asRecord(asRecord(asRecord(details)?.meta)?.truncation);
8682
+ if (!truncation) return void 0;
8683
+ const shown = asRecord(truncation.shownRange);
8684
+ const truncatedBy = typeof truncation.truncatedBy === "string" ? truncation.truncatedBy : void 0;
8685
+ const totalLines = finiteNumber2(truncation.totalLines);
8686
+ const totalBytes = finiteNumber2(truncation.totalBytes);
8687
+ const outputBytes = finiteNumber2(truncation.outputBytes);
8688
+ const nextOffset = finiteNumber2(truncation.nextOffset);
8689
+ const shownStart = finiteNumber2(shown?.start);
8690
+ const shownEnd = finiteNumber2(shown?.end);
8691
+ return {
8692
+ partialLine: truncation.partialLine === true,
8693
+ ...truncatedBy === void 0 ? {} : { truncatedBy },
8694
+ ...totalLines === void 0 ? {} : { totalLines },
8695
+ ...totalBytes === void 0 ? {} : { totalBytes },
8696
+ ...outputBytes === void 0 ? {} : { outputBytes },
8697
+ ...nextOffset === void 0 ? {} : { nextOffset },
8698
+ ...shownStart === void 0 ? {} : { shownStart },
8699
+ ...shownEnd === void 0 ? {} : { shownEnd }
8700
+ };
8701
+ };
8702
+ var UNREADABLE_READ_NOTE = /^\[Cannot read (?:binary file '|\.[^\s:]+ file: )/;
8703
+ var REPEAT_READ_HINT_PATTERN = /\n\n\[You have received this identical output \d+ times\. Re-reading '[\s\S]*?' will not change it — use a narrower selector \(path:A-B\), or proceed with the edit\.\]$/;
8704
+ var stripRepeatReadHint = (text) => text.endsWith("]") ? text.replace(REPEAT_READ_HINT_PATTERN, "") : text;
8705
+ var dropLeadingLines = (text, count) => {
8706
+ if (count <= 0) return text;
8707
+ let index = 0;
8708
+ for (let dropped = 0; dropped < count; dropped++) {
8709
+ const newline = text.indexOf("\n", index);
8710
+ if (newline === -1) return "";
8711
+ index = newline + 1;
8712
+ }
8713
+ return text.slice(index);
8714
+ };
8715
+ var keepLeadingLines = (text, count) => {
8716
+ if (count <= 0) return "";
8717
+ let index = 0;
8718
+ for (let kept = 0; kept < count; kept++) {
8719
+ const newline = text.indexOf("\n", index);
8720
+ if (newline === -1) return text;
8721
+ index = newline + 1;
8722
+ }
8723
+ return text.slice(0, index - 1);
8724
+ };
8725
+ var countLines = (text) => {
8726
+ if (text.length === 0) return 0;
8727
+ let lines = 1;
8728
+ for (let index = text.indexOf("\n"); index !== -1; index = text.indexOf("\n", index + 1)) lines++;
8729
+ return text.endsWith("\n") ? lines - 1 : lines;
8730
+ };
8731
+ var READ_SELECTOR_PATTERN = /:(raw:)?(\d+)(-(\d+)?)?$/i;
8732
+ var READ_RAW_SELECTOR_PATTERN = /:raw$/i;
8733
+ var readPageSelector = (target, offset) => target.endLimit === Number.POSITIVE_INFINITY ? `${target.base}:raw:${offset}-` : `${target.base}:raw:${offset}-${target.endLimit}`;
8734
+ var parseReadTarget = (rawPath) => {
8735
+ if (typeof rawPath !== "string" || rawPath.length === 0 || rawPath.includes("://")) return void 0;
8736
+ const match = READ_SELECTOR_PATTERN.exec(rawPath);
8737
+ if (match) {
8738
+ const end = match[3] === void 0 ? Number.POSITIVE_INFINITY : match[4] === void 0 ? Number.POSITIVE_INFINITY : Number(match[4]);
8739
+ return { base: rawPath.slice(0, match.index), start: Number(match[2]), endLimit: end, pageable: true, selected: true };
8740
+ }
8741
+ if (READ_RAW_SELECTOR_PATTERN.test(rawPath)) {
8742
+ return {
8743
+ base: rawPath.replace(READ_RAW_SELECTOR_PATTERN, ""),
8744
+ start: 1,
8745
+ endLimit: Number.POSITIVE_INFINITY,
8746
+ pageable: true,
8747
+ selected: true
8748
+ };
8749
+ }
8750
+ const lastColon = rawPath.lastIndexOf(":");
8751
+ const suffix = lastColon === -1 ? "" : rawPath.slice(lastColon + 1);
8752
+ const pageable = suffix.length === 0;
8753
+ return { base: rawPath, start: 1, endLimit: Number.POSITIVE_INFINITY, pageable, selected: false };
8754
+ };
8755
+ var unreadableReadMessage = (note, readPath) => {
8756
+ const target = typeof readPath === "string" ? readPath.replace(READ_SELECTOR_PATTERN, "") : void 0;
8757
+ const remedy = target ? ` Read the bytes with omp.read("${target}:raw").` : "";
8758
+ return `omp.read returned no text content: ${note.trim()}${remedy}`;
8759
+ };
8760
+ var restoreBashOutput = (artifactPath, maxChars, delivered) => {
8761
+ if (!artifactPath || maxChars <= 0) return void 0;
8762
+ let size;
8763
+ try {
8764
+ size = statSync3(artifactPath).size;
8765
+ } catch {
8766
+ return void 0;
8767
+ }
8768
+ if (size === 0 || size > maxChars) return void 0;
8769
+ let full;
8770
+ try {
8771
+ full = readFileSync2(artifactPath, "utf8");
8772
+ } catch {
8773
+ return void 0;
8774
+ }
8775
+ if (full.length < delivered.length) return void 0;
8776
+ const ellipsis = delivered.indexOf("\u2026");
8777
+ const prefix = ellipsis === -1 ? delivered : delivered.slice(0, ellipsis);
8778
+ return full.startsWith(prefix) ? full : void 0;
8779
+ };
8780
+ var withoutColumnLimit = (detailRecord, metaRecord) => {
8781
+ const limits = asRecord(metaRecord?.limits);
8782
+ if (!metaRecord || !limits || !Object.hasOwn(limits, "columnTruncated")) return detailRecord;
8783
+ const { columnTruncated: _dropped, ...remainingLimits } = limits;
8784
+ const { limits: _limits, ...remainingMeta } = metaRecord;
8785
+ const nextMeta = Object.keys(remainingLimits).length > 0 ? { ...remainingMeta, limits: remainingLimits } : remainingMeta;
8786
+ if (Object.keys(nextMeta).length > 0) return { ...detailRecord, meta: nextMeta };
8787
+ const { meta: _meta, ...remainingDetails } = detailRecord;
8788
+ return remainingDetails;
8789
+ };
8790
+ var grepTruncationSignal = (details) => {
8791
+ const record = asRecord(details);
8792
+ if (!record || record.truncated !== true) return void 0;
8793
+ const perFile = finiteNumber2(record.perFileLimitReached);
8794
+ const maxColumn = finiteNumber2(asRecord(asRecord(asRecord(record.meta)?.limits)?.columnTruncated)?.maxColumn);
8795
+ const fileCount = finiteNumber2(record.fileCount);
8796
+ const reasons = [];
8797
+ const notes = [];
8798
+ let continuation = null;
8799
+ if (perFile !== void 0) {
8800
+ reasons.push("matchLimit");
8801
+ notes.push(
8802
+ perFile <= MULTI_FILE_PER_FILE_MATCHES ? `Only ${perFile} matches per file are returned for a multi-file scope; point path at a single file to raise it to ${SINGLE_FILE_MATCHES}.` : `Only ${perFile} matches per file are returned; grep has no match cursor, so narrow the pattern or split the search by path.`
8803
+ );
8804
+ }
8805
+ if (maxColumn !== void 0) {
8806
+ reasons.push("columnLimit");
8807
+ notes.push(
8808
+ `Match lines are cut at ${maxColumn} bytes by the host, so a reported line need not contain the match; read the full line with omp.read("<file>:<line>").`
8809
+ );
8810
+ }
8811
+ if (reasons.length === 0) {
8812
+ reasons.push("fileLimit");
8813
+ if (fileCount !== void 0) continuation = { skip: fileCount };
8814
+ notes.push(
8815
+ fileCount === void 0 ? "More files matched than were returned; narrow the pattern or path." : `More files matched than were returned; pass skip=${fileCount} for the next page.`
8816
+ );
8817
+ }
8818
+ return {
8819
+ tool: "grep",
8820
+ partial: true,
8821
+ reasons,
8822
+ ...finiteNumber2(record.matchCount) === void 0 ? {} : { matchCount: record.matchCount },
8823
+ ...fileCount === void 0 ? {} : { fileCount },
8824
+ ...perFile === void 0 ? {} : { perFileMatchLimit: perFile },
8825
+ ...maxColumn === void 0 ? {} : { maxColumn },
8826
+ continue: continuation,
8827
+ note: notes.join(" ")
8828
+ };
8829
+ };
8830
+ var GUEST_RESULT_CHAR_FALLBACK = 1e6;
8831
+ var READ_MARKER_RESERVE_CHARS = 512;
8832
+ var resultCharBound = (context) => {
8833
+ const value = Reflect.get(context, "maxResultChars");
8834
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : GUEST_RESULT_CHAR_FALLBACK;
8835
+ };
8836
+ var byteBudgetSignal = (meta) => ({
8837
+ tool: "read",
8838
+ partial: true,
8839
+ reasons: ["byteBudget"],
8840
+ totalBytes: meta.totalBytes ?? null,
8841
+ deliveredBytes: meta.outputBytes ?? null,
8842
+ continue: null,
8843
+ note: "A single line exceeded the host per-read byte budget and no read selector continues a partial line; split the file (omp.bash with fold or split) and read the pieces."
8844
+ });
8845
+ var lineBudgetSignal = (meta, target, progress) => {
8846
+ const nextOffset = progress ? progress.nextOffset : meta.nextOffset;
8847
+ const continuation = target && nextOffset !== void 0 ? readPageSelector(target, nextOffset) : null;
8848
+ return {
8849
+ tool: "read",
8850
+ partial: true,
8851
+ reasons: [progress?.reason ?? "lineBudget"],
8852
+ totalLines: meta.totalLines ?? null,
8853
+ deliveredLines: progress?.delivered ?? null,
8854
+ continue: continuation,
8855
+ note: continuation ? `More lines remain; continue with omp.read("${continuation}").` : "More lines remain and the host offered no continuation offset."
8856
+ };
8857
+ };
8858
+ var findTruncationSignal = (details) => {
8859
+ const record = asRecord(details);
8860
+ if (!record || record.truncated !== true) return void 0;
8861
+ const reached = finiteNumber2(record.resultLimitReached) ?? finiteNumber2(record.fileCount);
8862
+ return {
8863
+ tool: "find",
8864
+ partial: true,
8865
+ reasons: ["resultLimit"],
8866
+ ...reached === void 0 ? {} : { resultLimit: reached },
8867
+ continue: null,
8868
+ note: `The host caps find at ${reached ?? "its result limit"} results and clamps any larger limit, so narrow pattern or path, or enumerate with omp.bash.`
8869
+ };
8870
+ };
8506
8871
  var resultHasTruncation = (details) => {
8507
8872
  if (details === null || typeof details !== "object" || Array.isArray(details)) return false;
8508
8873
  const meta = Reflect.get(details, "meta");
@@ -8516,15 +8881,26 @@ var resultExitCode = (details) => {
8516
8881
  const exitCode = Reflect.get(details, "exitCode");
8517
8882
  return Number.isSafeInteger(exitCode) && exitCode > 0 ? exitCode : void 0;
8518
8883
  };
8519
- var normalizeResult = (name, result, artifactPaths, appendReadNewline = true) => {
8520
- const text = isOmpShellToolName(name) ? stripOmpBashTiming(textContent(result.content)) : textContent(result.content);
8884
+ var normalizeResult = (name, result, artifactPaths, appendReadNewline = true, extras) => {
8885
+ const readSignal = extras?.read;
8886
+ const text = isOmpShellToolName(name) ? stripOmpBashTiming(textContent(result.content)) : name === "read" ? stripRepeatReadHint(textContent(result.content)) : textContent(result.content);
8521
8887
  if (result.isError && !resultHasTruncation(result.details)) throw new Error(text || `${name} failed`);
8522
8888
  if (name === "read") {
8523
- return appendReadNewline && text.length > 0 && !text.endsWith("\n") ? `${text}
8889
+ const body = appendReadNewline && text.length > 0 && !text.endsWith("\n") ? `${text}
8524
8890
  ` : text;
8891
+ return readSignal?.signal ? appendTruncationMarker(body, readSignal.signal) : body;
8892
+ }
8893
+ if (name === "grep") {
8894
+ const signal = grepTruncationSignal(result.details);
8895
+ return signal ? appendTruncationMarker(text, signal) : text;
8525
8896
  }
8526
- if (name === "grep" || name === "find" || name === "ls") return text;
8897
+ if (name === "find") {
8898
+ const signal = findTruncationSignal(result.details);
8899
+ return signal ? appendTruncationMarker(text, signal) : text;
8900
+ }
8901
+ if (name === "ls") return text;
8527
8902
  let details = result.details;
8903
+ let output = text;
8528
8904
  if (isOmpShellToolName(name) && details && typeof details === "object" && !Array.isArray(details)) {
8529
8905
  const detailRecord = details;
8530
8906
  const meta = detailRecord.meta;
@@ -8533,19 +8909,62 @@ var normalizeResult = (name, result, artifactPaths, appendReadNewline = true) =>
8533
8909
  if (truncation && typeof truncation === "object" && !Array.isArray(truncation)) {
8534
8910
  const truncationRecord = truncation;
8535
8911
  const artifactId = truncationRecord.artifactId;
8536
- const fullOutputPath = typeof artifactId === "string" ? artifactPaths?.get(artifactId) : void 0;
8912
+ const fullOutputPath = typeof artifactId === "string" ? artifactPaths?.get(artifactId) ?? extras?.bashArtifactPath : extras?.bashArtifactPath;
8537
8913
  details = {
8538
8914
  ...detailRecord,
8539
8915
  truncation: { ...truncationRecord, truncated: true },
8540
8916
  ...fullOutputPath ? { fullOutputPath } : {}
8541
8917
  };
8918
+ if (truncationRecord.truncated !== false) {
8919
+ const columnCap = finiteNumber2(asRecord(asRecord(metaRecord?.limits)?.columnTruncated)?.maxColumn);
8920
+ output = appendTruncationMarker(text, {
8921
+ tool: name,
8922
+ partial: true,
8923
+ reasons: columnCap === void 0 ? ["outputLimit"] : ["outputLimit", "columnLimit"],
8924
+ truncatedBy: truncationRecord.truncatedBy ?? null,
8925
+ totalLines: finiteNumber2(truncationRecord.totalLines) ?? null,
8926
+ totalBytes: finiteNumber2(truncationRecord.totalBytes) ?? null,
8927
+ deliveredBytes: finiteNumber2(truncationRecord.outputBytes) ?? null,
8928
+ ...columnCap === void 0 ? {} : { maxColumn: columnCap },
8929
+ fullOutputPath: fullOutputPath ?? null,
8930
+ continue: null,
8931
+ note: fullOutputPath ? `Output exceeded the host bash result budget; the complete stream is on disk at ${fullOutputPath}.` : "Output exceeded the host bash result budget and no full-output artifact was retained."
8932
+ });
8933
+ }
8934
+ } else {
8935
+ const columnCap = finiteNumber2(asRecord(asRecord(metaRecord?.limits)?.columnTruncated)?.maxColumn);
8936
+ if (columnCap !== void 0) {
8937
+ const restored = restoreBashOutput(extras?.bashArtifactPath, extras?.maxResultChars ?? 0, text);
8938
+ if (restored === void 0) {
8939
+ details = {
8940
+ ...detailRecord,
8941
+ columnTruncated: { maxColumn: columnCap, restored: false },
8942
+ ...extras?.bashArtifactPath ? { fullOutputPath: extras.bashArtifactPath } : {}
8943
+ };
8944
+ output = appendTruncationMarker(text, {
8945
+ tool: name,
8946
+ partial: true,
8947
+ reasons: ["columnLimit"],
8948
+ maxColumn: columnCap,
8949
+ fullOutputPath: extras?.bashArtifactPath ?? null,
8950
+ continue: null,
8951
+ note: `Output lines were cut at ${columnCap} bytes by the host bash executor, which reads global settings and ignores this session's tools.outputMaxColumns.`
8952
+ });
8953
+ } else {
8954
+ output = restored;
8955
+ details = {
8956
+ ...withoutColumnLimit(detailRecord, metaRecord),
8957
+ columnTruncated: { maxColumn: columnCap, restored: true }
8958
+ };
8959
+ }
8960
+ }
8542
8961
  }
8543
8962
  }
8544
8963
  if (name === "write" && details && typeof details === "object" && !Array.isArray(details)) {
8545
8964
  const { codePreviewBeforeWrite: _before, ...publicDetails } = details;
8546
8965
  details = Object.keys(publicDetails).length > 0 ? publicDetails : void 0;
8547
8966
  }
8548
- return { ok: true, output: text, details: details ?? null };
8967
+ return { ok: true, output, details: details ?? null };
8549
8968
  };
8550
8969
  var OmpToolsProvider = class _OmpToolsProvider {
8551
8970
  static async create(cwd, catalog, capturedTools) {
@@ -8567,7 +8986,7 @@ var OmpToolsProvider = class _OmpToolsProvider {
8567
8986
  bash: createNativeBashToolDefinition(cwd, this.#artifactPaths),
8568
8987
  edit: createNativeReplaceEditToolDefinition(cwd),
8569
8988
  write: createPreviewWriteToolDefinition(cwd),
8570
- grep: createGrepToolDefinition(cwd),
8989
+ grep: createGrepDefinitionWithSkip(cwd),
8571
8990
  find: createFindToolDefinition(cwd),
8572
8991
  ls: createLsToolDefinition(cwd)
8573
8992
  };
@@ -8608,6 +9027,7 @@ var OmpToolsProvider = class _OmpToolsProvider {
8608
9027
  const shellArgs = Object.hasOwn(args, OMP_BASH_CWD_KEY) ? { ...record, [OMP_BASH_CWD_KEY]: args[OMP_BASH_CWD_KEY] } : record;
8609
9028
  return resolveShellCwdArgument(actionName, this.#cwd, shellArgs);
8610
9029
  }
9030
+ if (actionName === "read") return normalizeReadArguments(record);
8611
9031
  return actionName === "edit" ? normalizeEditArguments(record, args.all === true, this.#cwd) : record;
8612
9032
  }
8613
9033
  #assertAllowed(name) {
@@ -8638,27 +9058,28 @@ var OmpToolsProvider = class _OmpToolsProvider {
8638
9058
  const intercepted = await tryExecuteGitWorktreeAdd(args, this.#cwd);
8639
9059
  if (intercepted) {
8640
9060
  this.#attachPreview(name, intercepted, args, context);
8641
- return this.#normalizeResult(name, intercepted, args);
9061
+ return this.#normalizeResult(name, intercepted, args, context);
8642
9062
  }
8643
9063
  }
8644
9064
  if (this.#catalog?.get(name)) {
8645
9065
  const result = await this.#capturedTools.invoke(name, args, context);
8646
9066
  this.#attachReadMedia(name, result, context);
8647
9067
  this.#attachReadNote(name, result, context);
8648
- return this.#normalizeResult(name, result, args, false);
9068
+ return this.#normalizeResult(name, result, args, context, false);
8649
9069
  }
8650
9070
  const tool = this.#definitionFor(name, args);
8651
9071
  const runner = this.#catalog?.runner;
8652
9072
  if (!runner) {
9073
+ const artifacts = [];
8653
9074
  const result = await runAbortable(
8654
9075
  context.signal,
8655
- () => tool.execute(
9076
+ () => artifactAllocations.run(artifacts, () => tool.execute(
8656
9077
  context.nestedToolCallId,
8657
9078
  args,
8658
9079
  context.signal,
8659
9080
  (partialResult) => this.#attachPartialPreview(name, partialResult, args, context),
8660
9081
  this.#executionContextFor(name, args, context.extensionContext)
8661
- )
9082
+ ))
8662
9083
  ).catch((error) => {
8663
9084
  throwIfAborted(context.signal);
8664
9085
  throw isOmpShellToolName(name) ? classifyOmpBashError(error) : error;
@@ -8670,7 +9091,7 @@ var OmpToolsProvider = class _OmpToolsProvider {
8670
9091
  this.#attachReadMedia(name, result, context);
8671
9092
  this.#attachReadNote(name, result, context);
8672
9093
  this.#attachPreview(name, result, args, context);
8673
- return this.#normalizeResult(name, result, args);
9094
+ return this.#normalizeResult(name, result, args, context, true, artifacts);
8674
9095
  }
8675
9096
  return this.#invokeWithEvents(name, tool, args, context, runner);
8676
9097
  }
@@ -8694,6 +9115,7 @@ var OmpToolsProvider = class _OmpToolsProvider {
8694
9115
  let isError = false;
8695
9116
  let thrown;
8696
9117
  let executionStarted = false;
9118
+ const artifacts = [];
8697
9119
  let updateTail = Promise.resolve();
8698
9120
  try {
8699
9121
  const preflight = await runAbortable(context.signal, () => runner.emitToolCall({
@@ -8707,7 +9129,7 @@ var OmpToolsProvider = class _OmpToolsProvider {
8707
9129
  throw new Error(preflight.reason || `OMP tool ${name} was blocked`);
8708
9130
  }
8709
9131
  executionStarted = true;
8710
- result = await runAbortable(context.signal, () => tool.execute(
9132
+ result = await runAbortable(context.signal, () => artifactAllocations.run(artifacts, () => tool.execute(
8711
9133
  toolCallId,
8712
9134
  args,
8713
9135
  context.signal,
@@ -8724,7 +9146,7 @@ var OmpToolsProvider = class _OmpToolsProvider {
8724
9146
  ).catch(() => void 0);
8725
9147
  },
8726
9148
  this.#executionContextFor(name, args, context.extensionContext)
8727
- ));
9149
+ )));
8728
9150
  isError = result.isError === true;
8729
9151
  } catch (error) {
8730
9152
  thrown = isOmpShellToolName(name) && executionStarted ? classifyOmpBashError(error) : error;
@@ -8777,16 +9199,110 @@ var OmpToolsProvider = class _OmpToolsProvider {
8777
9199
  throw new Error(text || (thrown instanceof Error ? thrown.message : `OMP tool ${name} failed`));
8778
9200
  }
8779
9201
  this.#attachPreview(name, result, args, context);
8780
- return this.#normalizeResult(name, result, args, patch?.content === void 0);
9202
+ return this.#normalizeResult(name, result, args, context, patch?.content === void 0, artifacts);
8781
9203
  }
8782
9204
  // Schema-enforce and early-startup calls may have no ExtensionRunner, so the
8783
9205
  // top-level tool_result marker middleware cannot run. Expand again at the
8784
9206
  // provider boundary; replacement is idempotent when middleware already ran.
8785
- #normalizeResult(name, result, args, appendReadNewline = true) {
8786
- const normalized = normalizeResult(name, result, this.#artifactPaths, appendReadNewline);
9207
+ async #normalizeResult(name, result, args, context, appendReadNewline = true, artifacts) {
9208
+ const extras = {};
9209
+ if (isOmpShellToolName(name)) {
9210
+ const artifactPath = artifacts?.at(-1);
9211
+ if (artifactPath !== void 0) extras.bashArtifactPath = artifactPath;
9212
+ extras.maxResultChars = resultCharBound(context);
9213
+ }
9214
+ let effective = result;
9215
+ if (name === "read" && !this.#catalog?.get(name)) {
9216
+ const paged = await this.#completeRead(result, args, context);
9217
+ effective = paged.result;
9218
+ extras.read = paged.read;
9219
+ }
9220
+ const normalized = normalizeResult(name, effective, this.#artifactPaths, appendReadNewline, extras);
8787
9221
  if (name !== "read" || typeof normalized !== "string") return normalized;
9222
+ if (UNREADABLE_READ_NOTE.test(normalized)) throw new Error(unreadableReadMessage(normalized, extras.read?.path));
8788
9223
  return expandSkillDirMarkersForRead(normalized, args, this.#cwd);
8789
9224
  }
9225
+ async #completeRead(result, args, context) {
9226
+ const rawPath = args.path;
9227
+ const readPath = typeof rawPath === "string" ? rawPath : void 0;
9228
+ const base = readPath === void 0 ? {} : { path: readPath };
9229
+ const meta = readTruncationMeta(result.details);
9230
+ const target = parseReadTarget(rawPath);
9231
+ if (!meta || !target || result.isError === true) return { result, read: base };
9232
+ if (meta.truncatedBy === "bytes" || meta.partialLine) {
9233
+ return { result, read: { ...base, signal: byteBudgetSignal(meta) } };
9234
+ }
9235
+ const tool = this.#tools.read;
9236
+ if (!tool || !target.pageable || meta.nextOffset === void 0) {
9237
+ return { result, read: { ...base, signal: lineBudgetSignal(meta, target, void 0) } };
9238
+ }
9239
+ const budget = resultCharBound(context) - READ_MARKER_RESERVE_CHARS;
9240
+ let text = stripRepeatReadHint(textContent(result.content));
9241
+ let covered = meta.shownEnd ?? countLines(text);
9242
+ let nextOffset = meta.nextOffset;
9243
+ let stopped;
9244
+ while (nextOffset !== void 0 && covered < target.endLimit) {
9245
+ if (text.length + covered >= budget) {
9246
+ stopped = "budget";
9247
+ break;
9248
+ }
9249
+ const selector = readPageSelector(target, nextOffset);
9250
+ const page = await runAbortable(
9251
+ context.signal,
9252
+ () => tool.execute(
9253
+ context.nestedToolCallId,
9254
+ { ...args, path: selector },
9255
+ context.signal,
9256
+ () => {
9257
+ },
9258
+ this.#executionContextFor("read", args, context.extensionContext)
9259
+ )
9260
+ );
9261
+ const pageMeta = readTruncationMeta(page.details);
9262
+ const pageText = stripRepeatReadHint(textContent(page.content));
9263
+ const start = pageMeta?.shownStart ?? nextOffset;
9264
+ const body = dropLeadingLines(pageText, Math.max(0, covered - start + 1));
9265
+ const pageEnd = pageMeta?.shownEnd ?? start + countLines(pageText) - 1;
9266
+ if (body.length === 0 || pageEnd <= covered) {
9267
+ nextOffset = void 0;
9268
+ break;
9269
+ }
9270
+ const candidate = `${text.endsWith("\n") ? text : `${text}
9271
+ `}${body}`;
9272
+ if (candidate.length + pageEnd >= budget) {
9273
+ stopped = "budget";
9274
+ break;
9275
+ }
9276
+ text = candidate;
9277
+ covered = pageEnd;
9278
+ nextOffset = pageMeta?.nextOffset;
9279
+ if (pageMeta?.truncatedBy === "bytes" || pageMeta?.partialLine === true) {
9280
+ stopped = "bytes";
9281
+ break;
9282
+ }
9283
+ }
9284
+ if (target.endLimit !== Number.POSITIVE_INFINITY && covered > target.endLimit) {
9285
+ text = keepLeadingLines(text, target.endLimit - target.start + 1);
9286
+ covered = target.endLimit;
9287
+ }
9288
+ const remaining = stopped !== void 0 || nextOffset !== void 0 && covered < target.endLimit;
9289
+ const paged = {
9290
+ content: [{ type: "text", text }],
9291
+ details: result.details
9292
+ };
9293
+ if (!remaining) return { result: paged, read: base };
9294
+ return {
9295
+ result: paged,
9296
+ read: {
9297
+ ...base,
9298
+ signal: lineBudgetSignal(meta, target, {
9299
+ nextOffset,
9300
+ delivered: covered,
9301
+ reason: stopped === "bytes" ? "byteBudget" : "resultBudget"
9302
+ })
9303
+ }
9304
+ };
9305
+ }
8790
9306
  #attachPartialPreview(name, partialResult, args, context) {
8791
9307
  const progress = textContent(partialResult.content).trim();
8792
9308
  const boundedProgress = Array.from(progress).slice(-4e3).join("");
@@ -8876,7 +9392,7 @@ var OmpToolsProvider = class _OmpToolsProvider {
8876
9392
  import { spawn } from "node:child_process";
8877
9393
  import { createHash as createHash3, randomBytes, randomUUID as randomUUID4 } from "node:crypto";
8878
9394
  import fs8 from "node:fs";
8879
- import path12 from "node:path";
9395
+ import path13 from "node:path";
8880
9396
 
8881
9397
  // src/schema/types.ts
8882
9398
  var stateBinding = (head) => head ? { transitionId: head.transitionId, version: head.version, to: head.to } : null;
@@ -8885,7 +9401,7 @@ var stateBinding = (head) => head ? { transitionId: head.transitionId, version:
8885
9401
  import { execFileSync } from "node:child_process";
8886
9402
  import { createHash as createHash2 } from "node:crypto";
8887
9403
  import fs7 from "node:fs";
8888
- import path11 from "node:path";
9404
+ import path12 from "node:path";
8889
9405
  var SNAPSHOT_MAX_FILES = 2e4;
8890
9406
  var SNAPSHOT_MAX_BYTES = 512 * 1024 * 1024;
8891
9407
  var FALLBACK_SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([
@@ -8903,10 +9419,10 @@ var git = (cwd, args) => execFileSync("git", ["-C", cwd, ...args], {
8903
9419
  stdio: ["ignore", "pipe", "ignore"]
8904
9420
  });
8905
9421
  var nulPaths = (buffer) => buffer.toString("utf8").split("\0").filter((item) => item.length > 0);
8906
- var normalizeRelative = (value) => value.split(path11.sep).join("/");
9422
+ var normalizeRelative = (value) => value.split(path12.sep).join("/");
8907
9423
  var isInside = (root, candidate) => {
8908
- const relative = path11.relative(root, candidate);
8909
- return relative === "" || !path11.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path11.sep}`);
9424
+ const relative = path12.relative(root, candidate);
9425
+ return relative === "" || !path12.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path12.sep}`);
8910
9426
  };
8911
9427
  var excluded = (absolute, exclusions) => exclusions.some((root) => isInside(root, absolute));
8912
9428
  var entryDigest = (absolute) => {
@@ -8932,12 +9448,12 @@ var buildSnapshot = (cwd, paths, metadata, exclusions) => {
8932
9448
  let bytes = 0;
8933
9449
  const unique = [...new Set(paths)].sort((left, right) => left.localeCompare(right));
8934
9450
  for (const relativeInput of unique) {
8935
- const absolute = path11.resolve(cwd, relativeInput);
9451
+ const absolute = path12.resolve(cwd, relativeInput);
8936
9452
  if (!isInside(cwd, absolute) || excluded(absolute, exclusions)) continue;
8937
9453
  if (Object.keys(entries).length >= SNAPSHOT_MAX_FILES) {
8938
9454
  throw new Error(`Schema workspace snapshot exceeds ${SNAPSHOT_MAX_FILES} files`);
8939
9455
  }
8940
- const relative = normalizeRelative(path11.relative(cwd, absolute));
9456
+ const relative = normalizeRelative(path12.relative(cwd, absolute));
8941
9457
  const entry = entryDigest(absolute);
8942
9458
  bytes += entry.bytes;
8943
9459
  if (bytes > SNAPSHOT_MAX_BYTES) {
@@ -8966,9 +9482,9 @@ var fallbackPaths = (cwd, exclusions) => {
8966
9482
  const entries = fs7.readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
8967
9483
  for (const entry of entries) {
8968
9484
  if (FALLBACK_SKIPPED_DIRECTORIES.has(entry.name)) continue;
8969
- const absolute = path11.join(directory, entry.name);
9485
+ const absolute = path12.join(directory, entry.name);
8970
9486
  if (excluded(absolute, exclusions)) continue;
8971
- const relative = normalizeRelative(path11.relative(cwd, absolute));
9487
+ const relative = normalizeRelative(path12.relative(cwd, absolute));
8972
9488
  if (entry.isDirectory()) visit(absolute);
8973
9489
  else paths.push(relative);
8974
9490
  if (paths.length > SNAPSHOT_MAX_FILES) {
@@ -8982,7 +9498,7 @@ var fallbackPaths = (cwd, exclusions) => {
8982
9498
  var snapshotWorkspace = (cwdInput, excludedRoots = []) => {
8983
9499
  const cwd = fs7.realpathSync(cwdInput);
8984
9500
  const exclusions = excludedRoots.map((root) => {
8985
- const absolute = path11.resolve(root);
9501
+ const absolute = path12.resolve(root);
8986
9502
  try {
8987
9503
  return fs7.realpathSync(absolute);
8988
9504
  } catch {
@@ -9020,18 +9536,18 @@ var snapshotWorkspace = (cwdInput, excludedRoots = []) => {
9020
9536
  );
9021
9537
  };
9022
9538
  var resolveWorkspaceFile = (cwdInput, requestedPath, options) => {
9023
- if (!requestedPath.trim() || path11.isAbsolute(requestedPath)) {
9539
+ if (!requestedPath.trim() || path12.isAbsolute(requestedPath)) {
9024
9540
  throw new Error(`Schema paths must be non-empty project-relative paths: ${requestedPath}`);
9025
9541
  }
9026
9542
  const cwd = fs7.realpathSync(cwdInput);
9027
- const absolute = path11.resolve(cwd, requestedPath);
9543
+ const absolute = path12.resolve(cwd, requestedPath);
9028
9544
  if (!isInside(cwd, absolute) || absolute === cwd) {
9029
9545
  throw new Error(`Schema path escapes the project workspace: ${requestedPath}`);
9030
9546
  }
9031
- const relativeParts = path11.relative(cwd, absolute).split(path11.sep);
9547
+ const relativeParts = path12.relative(cwd, absolute).split(path12.sep);
9032
9548
  let cursor = cwd;
9033
9549
  for (let index = 0; index < relativeParts.length; index++) {
9034
- cursor = path11.join(cursor, relativeParts[index]);
9550
+ cursor = path12.join(cursor, relativeParts[index]);
9035
9551
  let stat2;
9036
9552
  try {
9037
9553
  stat2 = fs7.lstatSync(cursor);
@@ -9040,9 +9556,9 @@ var resolveWorkspaceFile = (cwdInput, requestedPath, options) => {
9040
9556
  if (index !== relativeParts.length - 1 || !options.allowAbsent) {
9041
9557
  throw new Error(`Schema path does not exist: ${requestedPath}`);
9042
9558
  }
9043
- const parentReal = fs7.realpathSync(path11.dirname(cursor));
9559
+ const parentReal = fs7.realpathSync(path12.dirname(cursor));
9044
9560
  if (!isInside(cwd, parentReal)) throw new Error(`Schema path parent escapes the workspace: ${requestedPath}`);
9045
- return { absolute, relative: normalizeRelative(path11.relative(cwd, absolute)), exists: false };
9561
+ return { absolute, relative: normalizeRelative(path12.relative(cwd, absolute)), exists: false };
9046
9562
  }
9047
9563
  throw error;
9048
9564
  }
@@ -9054,7 +9570,7 @@ var resolveWorkspaceFile = (cwdInput, requestedPath, options) => {
9054
9570
  throw new Error(`Schema transaction paths must name regular files: ${requestedPath}`);
9055
9571
  }
9056
9572
  }
9057
- return { absolute, relative: normalizeRelative(path11.relative(cwd, absolute)), exists: true };
9573
+ return { absolute, relative: normalizeRelative(path12.relative(cwd, absolute)), exists: true };
9058
9574
  };
9059
9575
  var sha256File = (absolute) => `sha256:${digest(fs7.readFileSync(absolute))}`;
9060
9576
 
@@ -9105,8 +9621,8 @@ var SchemaController = class {
9105
9621
  this.identity = identity;
9106
9622
  this.state = state;
9107
9623
  this.cwd = fs8.realpathSync(cwd);
9108
- this.#journalRoot = path12.join(mesh.root, "schema-transactions");
9109
- this.#lockPath = path12.join(this.#journalRoot, ".commit.lock");
9624
+ this.#journalRoot = path13.join(mesh.root, "schema-transactions");
9625
+ this.#lockPath = path13.join(this.#journalRoot, ".commit.lock");
9110
9626
  this.#recoverJournals();
9111
9627
  }
9112
9628
  #activeHypotheses = /* @__PURE__ */ new Map();
@@ -9305,7 +9821,7 @@ var SchemaController = class {
9305
9821
  this.#assertPayloadBound(input);
9306
9822
  const release = this.#acquireCommitLock();
9307
9823
  const transactionId = randomUUID4();
9308
- const journalPath = path12.join(this.#journalRoot, `${transactionId}.json`);
9824
+ const journalPath = path13.join(this.#journalRoot, `${transactionId}.json`);
9309
9825
  let journal;
9310
9826
  let consumed = false;
9311
9827
  let committed = false;
@@ -9786,7 +10302,7 @@ ${Date.now()}
9786
10302
  }
9787
10303
  for (const name of fs8.readdirSync(this.#journalRoot)) {
9788
10304
  if (!name.endsWith(".json")) continue;
9789
- const filePath = path12.join(this.#journalRoot, name);
10305
+ const filePath = path13.join(this.#journalRoot, name);
9790
10306
  try {
9791
10307
  const journal = JSON.parse(fs8.readFileSync(filePath, "utf8"));
9792
10308
  if (journal.format !== 1 || journal.status !== "applying" || !Array.isArray(journal.before)) continue;
@@ -10040,11 +10556,11 @@ var SchemaProvider = class {
10040
10556
 
10041
10557
  // src/state/store.ts
10042
10558
  import { createHash as createHash5 } from "node:crypto";
10043
- import path14 from "node:path";
10559
+ import path15 from "node:path";
10044
10560
 
10045
10561
  // src/state/complexity.ts
10046
10562
  import fs9 from "node:fs";
10047
- import path13 from "node:path";
10563
+ import path14 from "node:path";
10048
10564
  var isWordStart = (character) => character !== void 0 && (character >= "a" && character <= "z" || character >= "A" && character <= "Z" || character === "_" || character === "$");
10049
10565
  var isWordPart = (character) => isWordStart(character) || character !== void 0 && character >= "0" && character <= "9";
10050
10566
  var regularExpressionPrefixWords = /* @__PURE__ */ new Set([
@@ -10276,7 +10792,7 @@ var languageComplexities = [
10276
10792
  typeScriptJavaScriptComplexity
10277
10793
  ];
10278
10794
  var complexityForFile = (file, languages = languageComplexities) => {
10279
- const extension = path13.extname(file).toLowerCase();
10795
+ const extension = path14.extname(file).toLowerCase();
10280
10796
  return languages.find((language) => language.extensions.includes(extension));
10281
10797
  };
10282
10798
  var countFileComplexity = (file) => {
@@ -11167,7 +11683,7 @@ var StateStore = class {
11167
11683
  const files = [];
11168
11684
  let netDelta = 0;
11169
11685
  for (const file of this.normalizeComplexityFiles(requestedFiles, input.cwd)) {
11170
- const measured = countFileComplexity(path14.resolve(input.cwd, file));
11686
+ const measured = countFileComplexity(path15.resolve(input.cwd, file));
11171
11687
  if (!measured) {
11172
11688
  files.push({ file, supported: false });
11173
11689
  continue;
@@ -11194,7 +11710,7 @@ var StateStore = class {
11194
11710
  const updates = [];
11195
11711
  let netDelta = 0;
11196
11712
  for (const file of this.normalizeComplexityFiles(files, cwd)) {
11197
- const measured = countFileComplexity(path14.resolve(cwd, file));
11713
+ const measured = countFileComplexity(path15.resolve(cwd, file));
11198
11714
  if (!measured) {
11199
11715
  deltas.push({ file, supported: false });
11200
11716
  continue;
@@ -11244,11 +11760,11 @@ var StateStore = class {
11244
11760
  const normalized = /* @__PURE__ */ new Set();
11245
11761
  for (const file of files) {
11246
11762
  if (!file.trim()) continue;
11247
- const relative = path14.relative(cwd, path14.resolve(cwd, file));
11248
- if (relative === ".." || relative.startsWith(`..${path14.sep}`) || path14.isAbsolute(relative)) {
11763
+ const relative = path15.relative(cwd, path15.resolve(cwd, file));
11764
+ if (relative === ".." || relative.startsWith(`..${path15.sep}`) || path15.isAbsolute(relative)) {
11249
11765
  throw new Error(`State complexity file must be inside the project cwd: ${file}`);
11250
11766
  }
11251
- normalized.add(relative.split(path14.sep).join("/"));
11767
+ normalized.add(relative.split(path15.sep).join("/"));
11252
11768
  }
11253
11769
  return [...normalized];
11254
11770
  }
@@ -12203,13 +12719,9 @@ var FabricRuntimeState = class {
12203
12719
  create: () => new McpProvider(context.cwd, this.#config.mcp, {
12204
12720
  ...this.#config.mcp.cache.enabled ? {
12205
12721
  cache: new McpDescriptorCacheStore(
12206
- path15.join(
12207
- process.env.OMP_FABRIC_PROJECT_ROOT ?? context.cwd,
12208
- ".omp",
12209
- "fabric",
12210
- "mcp-cache.json"
12211
- )
12212
- )
12722
+ projectMcpCachePath(process.env.OMP_FABRIC_PROJECT_ROOT ?? context.cwd)
12723
+ ),
12724
+ globalCache: new McpDescriptorCacheStore(globalMcpCachePath(resolveAgentDir()))
12213
12725
  } : {},
12214
12726
  hooks: {
12215
12727
  onSliceChanged: () => {
@@ -12223,9 +12735,7 @@ var FabricRuntimeState = class {
12223
12735
  unmounted: (provider) => {
12224
12736
  if (this.#mcpProvider === provider) this.#mcpProvider = void 0;
12225
12737
  },
12226
- start: (provider) => {
12227
- provider.warmup();
12228
- }
12738
+ start: (provider) => provider.warmup()
12229
12739
  }));
12230
12740
  if (capturedToolsProvider && !enforceSchema) {
12231
12741
  await installBuiltin(createProviderComponent({
@@ -12248,7 +12758,7 @@ var FabricRuntimeState = class {
12248
12758
  this.#mainAgent = mainAgent;
12249
12759
  const projectRoot = process.env.OMP_FABRIC_PROJECT_ROOT ?? context.cwd;
12250
12760
  const configuredMeshRoot = this.#config.mesh.root;
12251
- const meshRoot = process.env.OMP_FABRIC_MESH_ROOT ?? (configuredMeshRoot ? path15.resolve(projectRoot, configuredMeshRoot) : path15.join(projectRoot, ".omp", "fabric", "mesh"));
12761
+ const meshRoot = process.env.OMP_FABRIC_MESH_ROOT ?? (configuredMeshRoot ? path16.resolve(projectRoot, configuredMeshRoot) : path16.join(projectRoot, ".omp", "fabric", "mesh"));
12252
12762
  this.#mesh = new MeshStore(
12253
12763
  meshRoot,
12254
12764
  this.#config.mesh.maxEventBytes,
@@ -12301,6 +12811,12 @@ var FabricRuntimeState = class {
12301
12811
  this.#compact = new CompactController({
12302
12812
  onRequest: (intent) => void this.#publishCompactEvent("requested", intent),
12303
12813
  onCommit: (info) => void this.#publishCompactEvent(info.status, info)
12814
+ }, {
12815
+ settings: () => ({
12816
+ engine: this.#config?.compaction.engine ?? DEFAULT_FABRIC_CONFIG.compaction.engine,
12817
+ targetContextRatio: this.#config?.compaction.targetContextRatio ?? DEFAULT_FABRIC_CONFIG.compaction.targetContextRatio
12818
+ }),
12819
+ outcomes: fileCompactOutcomeStore()
12304
12820
  });
12305
12821
  await installBuiltin(createProviderComponent({
12306
12822
  provider: "compact",
@@ -12403,8 +12919,8 @@ var FabricRuntimeState = class {
12403
12919
  };
12404
12920
  const lineageAlive = (rootId) => this.#participants?.get(rootId) !== void 0;
12405
12921
  const actorRoots = {
12406
- project: path15.join(meshRoot, "actors"),
12407
- session: path15.join(meshRoot, "actors", fabricSessionId)
12922
+ project: path16.join(meshRoot, "actors"),
12923
+ session: path16.join(meshRoot, "actors", fabricSessionId)
12408
12924
  };
12409
12925
  const acquireActorCapabilityView = (requirements, signal) => this.#registry.acquireCapabilityView(requirements, {
12410
12926
  cwd: context.cwd,