omp-fabric 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +1 -0
  3. package/dist/actors/global-registry.d.ts.map +1 -1
  4. package/dist/actors/manager.d.ts.map +1 -1
  5. package/dist/actors/types.d.ts +1 -0
  6. package/dist/actors/types.d.ts.map +1 -1
  7. package/dist/agents/handoff.d.ts +1 -1
  8. package/dist/agents/handoff.d.ts.map +1 -1
  9. package/dist/agents/manager.d.ts.map +1 -1
  10. package/dist/agents/types.d.ts +2 -1
  11. package/dist/agents/types.d.ts.map +1 -1
  12. package/dist/chunks/{chunk-3TWC6XOS.js → chunk-2WDZT27Q.js} +36 -13
  13. package/dist/chunks/chunk-2WDZT27Q.js.map +7 -0
  14. package/dist/chunks/{chunk-UOSJZXJV.js → chunk-3LI7YO4M.js} +1 -1
  15. package/dist/chunks/chunk-3LI7YO4M.js.map +7 -0
  16. package/dist/chunks/{chunk-WXZP5VWP.js → chunk-7MFYALJI.js} +16 -16
  17. package/dist/chunks/chunk-7MFYALJI.js.map +7 -0
  18. package/dist/chunks/{chunk-B2SQR2JY.js → chunk-BMIE4PIL.js} +2 -2
  19. package/dist/chunks/{chunk-NX4SNIMR.js → chunk-FX3XLQYI.js} +36 -7
  20. package/dist/chunks/chunk-FX3XLQYI.js.map +7 -0
  21. package/dist/chunks/{chunk-CVLO4GNW.js → chunk-GDZDWKJS.js} +3 -3
  22. package/dist/chunks/{chunk-YPYVUGON.js → chunk-JOWMM3EX.js} +6 -6
  23. package/dist/chunks/chunk-JOWMM3EX.js.map +7 -0
  24. package/dist/chunks/{chunk-L2FNMIOW.js → chunk-QNRGTJGE.js} +2 -1
  25. package/dist/chunks/chunk-QNRGTJGE.js.map +7 -0
  26. package/dist/chunks/{chunk-2GQC324T.js → chunk-TSU4AEPZ.js} +2 -2
  27. package/dist/chunks/{chunk-TLSJXU4F.js → chunk-VSSC4IDU.js} +180 -11
  28. package/dist/chunks/chunk-VSSC4IDU.js.map +7 -0
  29. package/dist/chunks/{chunk-HDJBUXDX.js → chunk-WQUMHDOS.js} +2 -2
  30. package/dist/chunks/{chunk-EZPERYEG.js → chunk-XRJYPNTI.js} +56 -2
  31. package/dist/chunks/chunk-XRJYPNTI.js.map +7 -0
  32. package/dist/chunks/{chunk-Z6HEQPXX.js → chunk-Z5AP4M6K.js} +70 -6
  33. package/dist/chunks/chunk-Z5AP4M6K.js.map +7 -0
  34. package/dist/chunks/{chunk-KIREBD2V.js → chunk-ZP3S74LW.js} +2 -1
  35. package/dist/codemap/budget.d.ts +3 -0
  36. package/dist/codemap/budget.d.ts.map +1 -0
  37. package/dist/codemap/cascade.d.ts +3 -0
  38. package/dist/codemap/cascade.d.ts.map +1 -0
  39. package/dist/codemap/symbols.d.ts +3 -0
  40. package/dist/codemap/symbols.d.ts.map +1 -0
  41. package/dist/codemap/types.d.ts +58 -0
  42. package/dist/codemap/types.d.ts.map +1 -0
  43. package/dist/compaction/hook.js +1 -1
  44. package/dist/config.d.ts +11 -0
  45. package/dist/config.d.ts.map +1 -1
  46. package/dist/core/system-guidance.d.ts.map +1 -1
  47. package/dist/entropy/index.js +2 -2
  48. package/dist/fabric-runtime-state.d.ts.map +1 -1
  49. package/dist/fabric-runtime-state.js +1806 -378
  50. package/dist/fabric-runtime-state.js.map +4 -4
  51. package/dist/index.js +12 -12
  52. package/dist/index.js.map +1 -1
  53. package/dist/memory/discovery.d.ts +10 -0
  54. package/dist/memory/discovery.d.ts.map +1 -1
  55. package/dist/memory/discovery.js +3 -1
  56. package/dist/prewalk/handoff.d.ts +2 -1
  57. package/dist/prewalk/handoff.d.ts.map +1 -1
  58. package/dist/prewalk/retirement.d.ts +35 -0
  59. package/dist/prewalk/retirement.d.ts.map +1 -0
  60. package/dist/providers/agents-actions.d.ts.map +1 -1
  61. package/dist/providers/agents-provider.d.ts.map +1 -1
  62. package/dist/providers/codemap-provider.d.ts +14 -0
  63. package/dist/providers/codemap-provider.d.ts.map +1 -0
  64. package/dist/providers/mcp-provider.d.ts.map +1 -1
  65. package/dist/providers/memory-provider.d.ts.map +1 -1
  66. package/dist/providers/memory-provider.js +2 -2
  67. package/dist/providers/mesh-provider.d.ts.map +1 -1
  68. package/dist/providers/omp-tools-provider.d.ts.map +1 -1
  69. package/dist/providers/write-preview.d.ts.map +1 -1
  70. package/dist/residency/host.js +6 -6
  71. package/dist/residency/omp-entry.js +6 -6
  72. package/dist/runtime/core-override-guest-types.js +1 -1
  73. package/dist/runtime/guest-types.d.ts +1 -1
  74. package/dist/runtime/guest-types.d.ts.map +1 -1
  75. package/dist/runtime/guest-types.js +1 -1
  76. package/dist/runtime/node-process-runtime.js +1 -1
  77. package/dist/runtime/quickjs-runtime.d.ts +1 -1
  78. package/dist/runtime/quickjs-runtime.d.ts.map +1 -1
  79. package/dist/runtime/quickjs-runtime.js +1 -1
  80. package/dist/ui/dashboard.js +3 -3
  81. package/dist/ui/settings.d.ts.map +1 -1
  82. package/dist/ui/settings.js +90 -1
  83. package/dist/ui/settings.js.map +2 -2
  84. package/docs/architecture.md +1 -1
  85. package/docs/codemap.md +95 -0
  86. package/docs/configuration.md +1 -1
  87. package/package.json +4 -1
  88. package/dist/chunks/chunk-3TWC6XOS.js.map +0 -7
  89. package/dist/chunks/chunk-EZPERYEG.js.map +0 -7
  90. package/dist/chunks/chunk-L2FNMIOW.js.map +0 -7
  91. package/dist/chunks/chunk-NX4SNIMR.js.map +0 -7
  92. package/dist/chunks/chunk-TLSJXU4F.js.map +0 -7
  93. package/dist/chunks/chunk-UOSJZXJV.js.map +0 -7
  94. package/dist/chunks/chunk-WXZP5VWP.js.map +0 -7
  95. package/dist/chunks/chunk-YPYVUGON.js.map +0 -7
  96. package/dist/chunks/chunk-Z6HEQPXX.js.map +0 -7
  97. /package/dist/chunks/{chunk-B2SQR2JY.js.map → chunk-BMIE4PIL.js.map} +0 -0
  98. /package/dist/chunks/{chunk-CVLO4GNW.js.map → chunk-GDZDWKJS.js.map} +0 -0
  99. /package/dist/chunks/{chunk-2GQC324T.js.map → chunk-TSU4AEPZ.js.map} +0 -0
  100. /package/dist/chunks/{chunk-HDJBUXDX.js.map → chunk-WQUMHDOS.js.map} +0 -0
  101. /package/dist/chunks/{chunk-KIREBD2V.js.map → chunk-ZP3S74LW.js.map} +0 -0
@@ -11,7 +11,7 @@ import {
11
11
  MemoryProvider,
12
12
  actionArgNormalizer,
13
13
  uniqueDeclaredKeyForSpelling
14
- } from "./chunks/chunk-3TWC6XOS.js";
14
+ } from "./chunks/chunk-2WDZT27Q.js";
15
15
  import "./chunks/chunk-2KEWFW56.js";
16
16
  import "./chunks/chunk-U5FYOMG5.js";
17
17
  import {
@@ -33,7 +33,7 @@ import {
33
33
  repairFabricGuestCode,
34
34
  resolveFabricIdentity,
35
35
  runFabricHandoffAtBoundary
36
- } from "./chunks/chunk-TLSJXU4F.js";
36
+ } from "./chunks/chunk-VSSC4IDU.js";
37
37
  import "./chunks/chunk-7VIQNUPS.js";
38
38
  import {
39
39
  AgentTranscriptReader,
@@ -41,7 +41,7 @@ import {
41
41
  countContentLines,
42
42
  recentTranscriptTools,
43
43
  writeContentForPreview
44
- } from "./chunks/chunk-HDJBUXDX.js";
44
+ } from "./chunks/chunk-WQUMHDOS.js";
45
45
  import "./chunks/chunk-DLXPTBPL.js";
46
46
  import {
47
47
  ActionRegistry,
@@ -63,7 +63,7 @@ import {
63
63
  withExclusiveFileLockAsync
64
64
  } from "./chunks/chunk-YWEXP724.js";
65
65
  import "./chunks/chunk-4A4MVZQW.js";
66
- import "./chunks/chunk-NX4SNIMR.js";
66
+ import "./chunks/chunk-FX3XLQYI.js";
67
67
  import "./chunks/chunk-6QBPSZJ5.js";
68
68
  import {
69
69
  FABRIC_COMPONENT_DISCOVER_EVENT,
@@ -94,7 +94,7 @@ import {
94
94
  resolveAgentCwd,
95
95
  resolveOmpBinary,
96
96
  validateAgentCwdRequest
97
- } from "./chunks/chunk-WXZP5VWP.js";
97
+ } from "./chunks/chunk-7MFYALJI.js";
98
98
  import "./chunks/chunk-VPPIJRNZ.js";
99
99
  import {
100
100
  executeFile,
@@ -104,7 +104,7 @@ import {
104
104
  import "./chunks/chunk-CT2AJ2UI.js";
105
105
  import {
106
106
  checkedHandoffCompaction
107
- } from "./chunks/chunk-YPYVUGON.js";
107
+ } from "./chunks/chunk-JOWMM3EX.js";
108
108
  import {
109
109
  DEFAULT_FABRIC_CONFIG,
110
110
  FUZZY_RESOLUTION_MARKERS,
@@ -114,12 +114,12 @@ import {
114
114
  resolveAgentDir,
115
115
  resolveAvailableOmpModel,
116
116
  resolveFabricModel
117
- } from "./chunks/chunk-EZPERYEG.js";
117
+ } from "./chunks/chunk-XRJYPNTI.js";
118
118
  import {
119
119
  FABRIC_ACTOR_HOST_EVENTS,
120
120
  isFabricActorHostEvent,
121
121
  readJsonlPage
122
- } from "./chunks/chunk-UOSJZXJV.js";
122
+ } from "./chunks/chunk-3LI7YO4M.js";
123
123
  import {
124
124
  isFabricThinking
125
125
  } from "./chunks/chunk-JSBTB3P6.js";
@@ -155,8 +155,9 @@ import {
155
155
  MAX_PRESERVE_ITEM_CHARS,
156
156
  compactionRequestBoundsError,
157
157
  encodeCompactionRequest,
158
+ estimateTokens,
158
159
  fabricExecTitleHintCached
159
- } from "./chunks/chunk-KIREBD2V.js";
160
+ } from "./chunks/chunk-ZP3S74LW.js";
160
161
  import "./chunks/chunk-3J7BKUNG.js";
161
162
  import "./chunks/chunk-4HQDSTHN.js";
162
163
  import {
@@ -171,7 +172,7 @@ import {
171
172
  } from "./chunks/chunk-6N4XGAZY.js";
172
173
 
173
174
  // src/fabric-runtime-state.ts
174
- import path16 from "node:path";
175
+ import path19 from "node:path";
175
176
  import { fileURLToPath as fileURLToPath2 } from "node:url";
176
177
 
177
178
  // src/actors/global-registry.ts
@@ -293,6 +294,7 @@ var GlobalActorRegistry = class {
293
294
  ...patch.thinking !== void 0 ? { thinking: patch.thinking } : existing.thinking ? { thinking: existing.thinking } : {},
294
295
  ...patch.tools !== void 0 ? { tools: patch.tools } : existing.tools ? { tools: existing.tools } : {},
295
296
  ...patch.transport !== void 0 ? { transport: patch.transport } : existing.transport ? { transport: existing.transport } : {},
297
+ ...patch.cwd !== void 0 ? { cwd: patch.cwd } : existing.cwd ? { cwd: existing.cwd } : {},
296
298
  ...patch.timeoutMs !== void 0 ? { timeoutMs: patch.timeoutMs } : existing.timeoutMs ? { timeoutMs: existing.timeoutMs } : {},
297
299
  ...patch.extensions !== void 0 ? { extensions: patch.extensions } : typeof existing.extensions === "boolean" ? { extensions: existing.extensions } : {},
298
300
  ...patch.validWhile !== void 0 ? { validWhile: patch.validWhile } : existing.validWhile ? { validWhile: existing.validWhile } : {}
@@ -346,6 +348,7 @@ var GlobalActorRegistry = class {
346
348
  ...def.thinking ? { thinking: def.thinking } : {},
347
349
  ...def.tools ? { tools: [...def.tools] } : {},
348
350
  ...def.transport ? { transport: def.transport } : {},
351
+ ...def.cwd ? { cwd: def.cwd } : {},
349
352
  ...def.timeoutMs ? { timeoutMs: def.timeoutMs } : {},
350
353
  ...typeof def.extensions === "boolean" ? { extensions: def.extensions } : {},
351
354
  ...def.validWhile ? { validWhile: clone(def.validWhile) } : {}
@@ -386,6 +389,7 @@ var GlobalActorRegistry = class {
386
389
  const thinking = def.thinking !== void 0 && isFabricThinking(def.thinking) ? def.thinking : void 0;
387
390
  const tools = Array.isArray(def.tools) ? [...new Set(def.tools.filter((tool) => typeof tool === "string"))] : void 0;
388
391
  const transport = def.transport !== void 0 && TRANSPORTS.has(def.transport) ? def.transport : void 0;
392
+ const cwd = typeof def.cwd === "string" && def.cwd.trim().length > 0 ? def.cwd : void 0;
389
393
  const timeoutMs = typeof def.timeoutMs === "number" ? def.timeoutMs : void 0;
390
394
  const extensions = typeof def.extensions === "boolean" ? def.extensions : void 0;
391
395
  const requires = normalizeRequirements(def.requires);
@@ -406,6 +410,7 @@ var GlobalActorRegistry = class {
406
410
  ...thinking ? { thinking } : {},
407
411
  ...tools ? { tools } : {},
408
412
  ...transport ? { transport } : {},
413
+ ...cwd ? { cwd } : {},
409
414
  ...timeoutMs ? { timeoutMs } : {},
410
415
  ...extensions !== void 0 ? { extensions } : {},
411
416
  ...requires && requires.length > 0 ? { requires } : {},
@@ -425,36 +430,37 @@ var GlobalActorRegistry = class {
425
430
  if (!Array.isArray(records)) return;
426
431
  for (const value of records) {
427
432
  if (typeof value !== "object" || value === null || Array.isArray(value)) continue;
428
- const record = value;
429
- if (typeof record.id !== "string" || !/^[a-f0-9]{32}$/.test(record.id) || typeof record.name !== "string" || !ACTOR_NAME_PATTERN.test(record.name) || typeof record.instructions !== "string" || Buffer.byteLength(record.instructions, "utf8") > this.#maxBytes || typeof record.createdAt !== "number") {
433
+ const record2 = value;
434
+ if (typeof record2.id !== "string" || !/^[a-f0-9]{32}$/.test(record2.id) || typeof record2.name !== "string" || !ACTOR_NAME_PATTERN.test(record2.name) || typeof record2.instructions !== "string" || Buffer.byteLength(record2.instructions, "utf8") > this.#maxBytes || typeof record2.createdAt !== "number") {
430
435
  continue;
431
436
  }
432
- const events = Array.isArray(record.events) ? record.events.filter((event) => HOST_EVENTS.has(event)) : [];
433
- const topics = Array.isArray(record.topics) ? record.topics.filter(
437
+ const events = Array.isArray(record2.events) ? record2.events.filter((event) => HOST_EVENTS.has(event)) : [];
438
+ const topics = Array.isArray(record2.topics) ? record2.topics.filter(
434
439
  (topic) => typeof topic === "string" && TOPIC_PATTERN.test(topic)
435
440
  ) : [];
436
- const delivery = record.delivery === "steer" || record.delivery === "followUp" || record.delivery === "nextTurn" ? record.delivery : "mailbox";
437
- const responseMode = record.responseMode === "directive" ? "directive" : "text";
438
- const triggerTurn = (delivery === "steer" || delivery === "followUp") && record.triggerTurn === true;
439
- const coalesce = record.coalesce !== false;
440
- const residency = record.residency === "durable" ? "durable" : "session";
441
- const runner = record.runner === "claude" ? "claude" : "omp";
442
- const thinking = isFabricThinking(record.thinking) ? record.thinking : void 0;
443
- const tools = Array.isArray(record.tools) ? record.tools.filter((tool) => typeof tool === "string") : void 0;
444
- const transport = record.transport !== void 0 && TRANSPORTS.has(record.transport) ? record.transport : void 0;
445
- const timeoutMs = typeof record.timeoutMs === "number" ? record.timeoutMs : void 0;
446
- const extensions = typeof record.extensions === "boolean" ? record.extensions : void 0;
441
+ const delivery = record2.delivery === "steer" || record2.delivery === "followUp" || record2.delivery === "nextTurn" ? record2.delivery : "mailbox";
442
+ const responseMode = record2.responseMode === "directive" ? "directive" : "text";
443
+ const triggerTurn = (delivery === "steer" || delivery === "followUp") && record2.triggerTurn === true;
444
+ const coalesce = record2.coalesce !== false;
445
+ const residency = record2.residency === "durable" ? "durable" : "session";
446
+ const runner = record2.runner === "claude" ? "claude" : "omp";
447
+ const thinking = isFabricThinking(record2.thinking) ? record2.thinking : void 0;
448
+ const tools = Array.isArray(record2.tools) ? record2.tools.filter((tool) => typeof tool === "string") : void 0;
449
+ const transport = record2.transport !== void 0 && TRANSPORTS.has(record2.transport) ? record2.transport : void 0;
450
+ const cwd = typeof record2.cwd === "string" && record2.cwd.trim().length > 0 ? record2.cwd : void 0;
451
+ const timeoutMs = typeof record2.timeoutMs === "number" ? record2.timeoutMs : void 0;
452
+ const extensions = typeof record2.extensions === "boolean" ? record2.extensions : void 0;
447
453
  let requires;
448
454
  try {
449
- requires = normalizeRequirements(record.requires);
455
+ requires = normalizeRequirements(record2.requires);
450
456
  } catch {
451
457
  continue;
452
458
  }
453
- const validWhile = record.validWhile?.version === 1 && typeof record.validWhile.source === "string" && record.validWhile.source.length <= 16e3 ? clone(record.validWhile) : void 0;
459
+ const validWhile = record2.validWhile?.version === 1 && typeof record2.validWhile.source === "string" && record2.validWhile.source.length <= 16e3 ? clone(record2.validWhile) : void 0;
454
460
  const def = {
455
- id: record.id,
456
- name: record.name,
457
- instructions: record.instructions,
461
+ id: record2.id,
462
+ name: record2.name,
463
+ instructions: record2.instructions,
458
464
  events,
459
465
  topics,
460
466
  delivery,
@@ -463,12 +469,13 @@ var GlobalActorRegistry = class {
463
469
  coalesce,
464
470
  residency,
465
471
  runner,
466
- createdAt: record.createdAt,
467
- updatedAt: typeof record.updatedAt === "number" ? record.updatedAt : record.createdAt,
468
- ...typeof record.model === "string" && record.model ? { model: record.model } : {},
472
+ createdAt: record2.createdAt,
473
+ updatedAt: typeof record2.updatedAt === "number" ? record2.updatedAt : record2.createdAt,
474
+ ...typeof record2.model === "string" && record2.model ? { model: record2.model } : {},
469
475
  ...thinking ? { thinking } : {},
470
476
  ...tools ? { tools } : {},
471
477
  ...transport ? { transport } : {},
478
+ ...cwd ? { cwd } : {},
472
479
  ...timeoutMs ? { timeoutMs } : {},
473
480
  ...extensions !== void 0 ? { extensions } : {},
474
481
  ...requires && requires.length > 0 ? { requires } : {},
@@ -793,10 +800,10 @@ var normalizeRegistration = (registration, fallbackLabel) => {
793
800
  }
794
801
  };
795
802
  };
796
- var beginCleanup = (record) => {
797
- if (record.cleanupStarted) return;
798
- record.cleanupStarted = true;
799
- record.beforeCleanup?.();
803
+ var beginCleanup = (record2) => {
804
+ if (record2.cleanupStarted) return;
805
+ record2.cleanupStarted = true;
806
+ record2.beforeCleanup?.();
800
807
  };
801
808
  var closeIterator = async (iterator, disposers) => {
802
809
  if (!iterator.return) return;
@@ -809,20 +816,20 @@ var closeIterator = async (iterator, disposers) => {
809
816
  var checkTarget = async (guard) => {
810
817
  if (guard && !await guard()) throw new FabricEffectDivertedError();
811
818
  };
812
- var driveIterator = async (iterator, record, guard) => {
819
+ var driveIterator = async (iterator, record2, guard) => {
813
820
  try {
814
821
  for (; ; ) {
815
- if (!record.armed) {
816
- beginCleanup(record);
817
- await closeIterator(iterator, record.disposers);
822
+ if (!record2.armed) {
823
+ beginCleanup(record2);
824
+ await closeIterator(iterator, record2.disposers);
818
825
  return;
819
826
  }
820
827
  await checkTarget(guard);
821
828
  const step = await iterator.next();
822
- if (!step.done) collectDisposer(step.value, record.disposers);
823
- if (!record.armed) {
824
- beginCleanup(record);
825
- await closeIterator(iterator, record.disposers);
829
+ if (!step.done) collectDisposer(step.value, record2.disposers);
830
+ if (!record2.armed) {
831
+ beginCleanup(record2);
832
+ await closeIterator(iterator, record2.disposers);
826
833
  return;
827
834
  }
828
835
  if (step.done) {
@@ -833,8 +840,8 @@ var driveIterator = async (iterator, record, guard) => {
833
840
  } catch (error) {
834
841
  if (error instanceof FabricEffectDivertedError) {
835
842
  try {
836
- beginCleanup(record);
837
- await closeIterator(iterator, record.disposers);
843
+ beginCleanup(record2);
844
+ await closeIterator(iterator, record2.disposers);
838
845
  } catch (closeError) {
839
846
  throw new FabricEffectDivertedError(
840
847
  "Fabric effect target changed and iterator close failed",
@@ -845,19 +852,19 @@ var driveIterator = async (iterator, record, guard) => {
845
852
  throw error;
846
853
  }
847
854
  };
848
- var collectEffect = async (effect, record, guard) => {
855
+ var collectEffect = async (effect, record2, guard) => {
849
856
  const resolved = isPromiseLike(effect) ? await effect : effect;
850
857
  if (resolved === void 0 || resolved === null || typeof resolved === "function") {
851
- collectDisposer(resolved, record.disposers);
852
- if (record.armed) await checkTarget(guard);
858
+ collectDisposer(resolved, record2.disposers);
859
+ if (record2.armed) await checkTarget(guard);
853
860
  return;
854
861
  }
855
862
  if (isAsyncIterable(resolved)) {
856
- await driveIterator(resolved[Symbol.asyncIterator](), record, guard);
863
+ await driveIterator(resolved[Symbol.asyncIterator](), record2, guard);
857
864
  return;
858
865
  }
859
866
  if (isIterable(resolved)) {
860
- await driveIterator(resolved[Symbol.iterator](), record, guard);
867
+ await driveIterator(resolved[Symbol.iterator](), record2, guard);
861
868
  return;
862
869
  }
863
870
  throw new TypeError("Fabric effect returned an unsupported value");
@@ -876,10 +883,10 @@ var FabricEffectScope = class {
876
883
  }
877
884
  footprint(limit = Number.POSITIVE_INFINITY) {
878
885
  const effects = [];
879
- for (const record of this.#records) {
886
+ for (const record2 of this.#records) {
880
887
  if (effects.length >= limit) break;
881
- if (!record.disposed && record.effect) {
882
- effects.push({ ...record.effect, resources: [...record.effect.resources] });
888
+ if (!record2.disposed && record2.effect) {
889
+ effects.push({ ...record2.effect, resources: [...record2.effect.resources] });
883
890
  }
884
891
  }
885
892
  return effects;
@@ -889,7 +896,7 @@ var FabricEffectScope = class {
889
896
  throw new Error("Cannot create an effect on a disposing Fabric scope");
890
897
  }
891
898
  const normalized = normalizeRegistration(registration, "anonymous");
892
- const record = {
899
+ const record2 = {
893
900
  label: normalized.label,
894
901
  ...normalized.effect ? { effect: normalized.effect } : {},
895
902
  disposers: [],
@@ -902,9 +909,9 @@ var FabricEffectScope = class {
902
909
  ...hooks.beforeCleanup ? { beforeCleanup: hooks.beforeCleanup } : {}
903
910
  };
904
911
  const cleanupDisposers = async () => {
905
- beginCleanup(record);
912
+ beginCleanup(record2);
906
913
  const failures = [];
907
- for (const disposer of record.disposers.splice(0).reverse()) {
914
+ for (const disposer of record2.disposers.splice(0).reverse()) {
908
915
  try {
909
916
  await disposer();
910
917
  } catch (error) {
@@ -912,64 +919,64 @@ var FabricEffectScope = class {
912
919
  }
913
920
  }
914
921
  if (failures.length > 0) {
915
- throw new AggregateError(failures, `Fabric effect cleanup failed: ${record.label}`);
922
+ throw new AggregateError(failures, `Fabric effect cleanup failed: ${record2.label}`);
916
923
  }
917
924
  };
918
925
  let disposal;
919
- record.dispose = async () => {
920
- if (record.disposed) return disposal;
921
- record.disposed = true;
922
- record.armed = false;
926
+ record2.dispose = async () => {
927
+ if (record2.disposed) return disposal;
928
+ record2.disposed = true;
929
+ record2.armed = false;
923
930
  disposal = (async () => {
924
- await record.setup.catch(() => void 0);
931
+ await record2.setup.catch(() => void 0);
925
932
  await cleanupDisposers();
926
933
  })();
927
934
  return disposal;
928
935
  };
929
- this.#records.push(record);
930
- record.setup = (async () => {
936
+ this.#records.push(record2);
937
+ record2.setup = (async () => {
931
938
  try {
932
939
  if (this.#guard) await checkTarget(this.#guard);
933
- if (!record.armed) return;
934
- await collectEffect(setup(), record, this.#guard);
940
+ if (!record2.armed) return;
941
+ await collectEffect(setup(), record2, this.#guard);
935
942
  } catch (error) {
936
943
  try {
937
944
  await cleanupDisposers();
938
945
  } catch (cleanupError) {
939
946
  const failures = cleanupError instanceof AggregateError ? cleanupError.errors : [cleanupError];
940
947
  for (const failure of failures) {
941
- this.#setupCleanupFailures.push({ label: record.label, error: errorMessage(failure) });
948
+ this.#setupCleanupFailures.push({ label: record2.label, error: errorMessage(failure) });
942
949
  }
943
950
  throw new AggregateError(
944
951
  [error, cleanupError],
945
- `Fabric effect setup and rollback failed: ${record.label}`
952
+ `Fabric effect setup and rollback failed: ${record2.label}`
946
953
  );
947
954
  }
948
955
  throw error;
949
956
  }
950
957
  })();
951
958
  try {
952
- await record.setup;
959
+ await record2.setup;
953
960
  if (this.#state === "open") {
954
- const index = this.#records.indexOf(record);
961
+ const index = this.#records.indexOf(record2);
955
962
  if (index >= 0 && index !== this.#records.length - 1) {
956
963
  this.#records.splice(index, 1);
957
- this.#records.push(record);
964
+ this.#records.push(record2);
958
965
  }
959
966
  }
960
967
  } catch (error) {
961
- const index = this.#records.indexOf(record);
968
+ const index = this.#records.indexOf(record2);
962
969
  if (index >= 0) this.#records.splice(index, 1);
963
970
  throw error;
964
971
  }
965
- return record.dispose;
972
+ return record2.dispose;
966
973
  }
967
974
  defer(disposer, registration = "deferred") {
968
975
  if (this.#state !== "open") {
969
976
  throw new Error("Cannot defer cleanup on a disposing Fabric scope");
970
977
  }
971
978
  const normalized = normalizeRegistration(registration, "deferred");
972
- const record = {
979
+ const record2 = {
973
980
  label: normalized.label,
974
981
  ...normalized.effect ? { effect: normalized.effect } : {},
975
982
  disposers: [disposer],
@@ -981,13 +988,13 @@ var FabricEffectScope = class {
981
988
  cleanupStarted: false
982
989
  };
983
990
  let disposal;
984
- record.dispose = async () => {
985
- if (record.disposed) return disposal;
986
- record.disposed = true;
987
- record.armed = false;
991
+ record2.dispose = async () => {
992
+ if (record2.disposed) return disposal;
993
+ record2.disposed = true;
994
+ record2.armed = false;
988
995
  disposal = (async () => {
989
996
  const failures = [];
990
- for (const cleanup of record.disposers.splice(0).reverse()) {
997
+ for (const cleanup of record2.disposers.splice(0).reverse()) {
991
998
  try {
992
999
  await cleanup();
993
1000
  } catch (error) {
@@ -995,29 +1002,29 @@ var FabricEffectScope = class {
995
1002
  }
996
1003
  }
997
1004
  if (failures.length > 0) {
998
- throw new AggregateError(failures, `Fabric effect cleanup failed: ${record.label}`);
1005
+ throw new AggregateError(failures, `Fabric effect cleanup failed: ${record2.label}`);
999
1006
  }
1000
1007
  })();
1001
1008
  return disposal;
1002
1009
  };
1003
- this.#records.push(record);
1004
- return record.dispose;
1010
+ this.#records.push(record2);
1011
+ return record2.dispose;
1005
1012
  }
1006
1013
  dispose() {
1007
1014
  if (this.#cleanup) return this.#cleanup;
1008
1015
  this.#state = "disposing";
1009
1016
  this.#cleanup = (async () => {
1010
1017
  const failures = this.#setupCleanupFailures.splice(0);
1011
- for (const record of this.#records.splice(0).reverse()) {
1018
+ for (const record2 of this.#records.splice(0).reverse()) {
1012
1019
  try {
1013
- await record.dispose();
1020
+ await record2.dispose();
1014
1021
  } catch (error) {
1015
1022
  if (error instanceof AggregateError) {
1016
1023
  for (const nested of error.errors) {
1017
- failures.push({ label: record.label, error: errorMessage(nested) });
1024
+ failures.push({ label: record2.label, error: errorMessage(nested) });
1018
1025
  }
1019
1026
  } else {
1020
- failures.push({ label: record.label, error: errorMessage(error) });
1027
+ failures.push({ label: record2.label, error: errorMessage(error) });
1021
1028
  }
1022
1029
  }
1023
1030
  }
@@ -2218,10 +2225,10 @@ var FabricComponentSupervisor = class {
2218
2225
  adjacent.set(edge.from, targets);
2219
2226
  }
2220
2227
  const cycles = /* @__PURE__ */ new Map();
2221
- const visit = (node, path17, positions) => {
2228
+ const visit = (node, path20, positions) => {
2222
2229
  const position = positions.get(node);
2223
2230
  if (position !== void 0) {
2224
- const cycle = path17.slice(position);
2231
+ const cycle = path20.slice(position);
2225
2232
  const rotations = cycle.map((_, index) => [
2226
2233
  ...cycle.slice(index),
2227
2234
  ...cycle.slice(0, index)
@@ -2231,10 +2238,10 @@ var FabricComponentSupervisor = class {
2231
2238
  cycles.set(canonical.join("\0"), canonical);
2232
2239
  return;
2233
2240
  }
2234
- if (path17.length > this.#components.size) return;
2235
- const nextPositions = new Map(positions).set(node, path17.length);
2241
+ if (path20.length > this.#components.size) return;
2242
+ const nextPositions = new Map(positions).set(node, path20.length);
2236
2243
  for (const target of adjacent.get(node) ?? []) {
2237
- visit(target, [...path17, node], nextPositions);
2244
+ visit(target, [...path20, node], nextPositions);
2238
2245
  }
2239
2246
  };
2240
2247
  for (const id of this.#components.keys()) visit(id, [], /* @__PURE__ */ new Map());
@@ -2736,18 +2743,18 @@ var finiteNumber = (value) => typeof value === "number" && Number.isFinite(value
2736
2743
  var nonEmptyString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
2737
2744
  var decodeOutcome = (raw) => {
2738
2745
  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;
2746
+ const record2 = raw;
2747
+ const at = finiteNumber(record2.at);
2748
+ const requestedBy = nonEmptyString(record2.requestedBy);
2749
+ const status = record2.status;
2743
2750
  if (at === void 0 || requestedBy === void 0) return void 0;
2744
2751
  if (status !== "committed" && status !== "cancelled" && status !== "failed" && status !== "skipped") {
2745
2752
  return void 0;
2746
2753
  }
2747
- const summary = nonEmptyString(record.summary);
2748
- const error = nonEmptyString(record.error);
2749
- const tokensBefore = finiteNumber(record.tokensBefore);
2750
- const estimatedTokensAfter = finiteNumber(record.estimatedTokensAfter);
2754
+ const summary = nonEmptyString(record2.summary);
2755
+ const error = nonEmptyString(record2.error);
2756
+ const tokensBefore = finiteNumber(record2.tokensBefore);
2757
+ const estimatedTokensAfter = finiteNumber(record2.estimatedTokensAfter);
2751
2758
  return {
2752
2759
  at,
2753
2760
  requestedBy,
@@ -2964,7 +2971,7 @@ var FabricExecutionService = class {
2964
2971
  })) ?? []
2965
2972
  ) : void 0;
2966
2973
  const code = repairFabricGuestCode(options.code);
2967
- const checked = dependencies.typeCheckFabricCode(
2974
+ const checked2 = dependencies.typeCheckFabricCode(
2968
2975
  code,
2969
2976
  dependencies.guestTypeDeclarations(effectiveFullCodeMode, {
2970
2977
  excludeGlobals: [...unavailable.keys()],
@@ -2972,8 +2979,8 @@ var FabricExecutionService = class {
2972
2979
  ...coreOverrideDeclarations ? { coreOverrides: coreOverrideDeclarations } : {}
2973
2980
  })
2974
2981
  );
2975
- if (checked.errors.length > 0) {
2976
- for (const error of checked.errors) {
2982
+ if (checked2.errors.length > 0) {
2983
+ for (const error of checked2.errors) {
2977
2984
  const missing = /^Cannot find name '([^']+)'/.exec(error.message);
2978
2985
  const reason = missing?.[1] ? unavailable.get(missing[1]) : void 0;
2979
2986
  if (missing && reason) {
@@ -2990,10 +2997,10 @@ var FabricExecutionService = class {
2990
2997
  trace: traceRecorder.seal(
2991
2998
  "failed",
2992
2999
  [],
2993
- `Type checking failed (${checked.errors.length} ${checked.errors.length === 1 ? "error" : "errors"})`
3000
+ `Type checking failed (${checked2.errors.length} ${checked2.errors.length === 1 ? "error" : "errors"})`
2994
3001
  ),
2995
3002
  elapsedMs: performance.now() - startedAt,
2996
- typeErrors: checked.errors
3003
+ typeErrors: checked2.errors
2997
3004
  };
2998
3005
  }
2999
3006
  const classifierUsages = [];
@@ -3424,8 +3431,8 @@ var FabricExecutionService = class {
3424
3431
  memoryLimitBytes: this.config.executor.memoryLimitBytes,
3425
3432
  maxLogChars: this.config.executor.maxOutputChars,
3426
3433
  minimumTimeoutMsForHostCall,
3427
- ...checked.javascript ? { transpiledCode: checked.javascript } : {},
3428
- ...checked.sourceMap ? { transpiledSourceMap: checked.sourceMap } : {},
3434
+ ...checked2.javascript ? { transpiledCode: checked2.javascript } : {},
3435
+ ...checked2.sourceMap ? { transpiledSourceMap: checked2.sourceMap } : {},
3429
3436
  ...options.payloads ? { payloads: options.payloads } : {},
3430
3437
  ...Object.keys(proxyNames).length > 0 ? { proxyNames } : {},
3431
3438
  ...options.tokenBudget !== void 0 ? { tokenBudget: options.tokenBudget } : {},
@@ -4197,9 +4204,28 @@ var runProperties = {
4197
4204
  enum: ["off", "minimal", "low", "medium", "high", "xhigh", "max"]
4198
4205
  },
4199
4206
  tools: { type: "array", items: { type: "string" } },
4207
+ addTools: {
4208
+ type: "array",
4209
+ items: { type: "string" },
4210
+ description: "Extra tools merged onto the resolved tool list instead of replacing it; use with or without tools."
4211
+ },
4212
+ images: {
4213
+ type: "array",
4214
+ description: "Image blocks delivered with the task on the child agent's first turn.",
4215
+ items: {
4216
+ type: "object",
4217
+ properties: {
4218
+ type: { const: "image" },
4219
+ data: { type: "string", description: "Base64-encoded image bytes." },
4220
+ mimeType: { type: "string" }
4221
+ },
4222
+ required: ["type", "data", "mimeType"],
4223
+ additionalProperties: false
4224
+ }
4225
+ },
4200
4226
  timeoutMs: {
4201
4227
  type: "number",
4202
- description: "Optional longer wall-clock limit in milliseconds. Omit to use agents.timeoutMs (60 minutes by default); values below the configured default are ignored."
4228
+ description: "Optional wall-clock limit in milliseconds, raising or lowering agents.timeoutMs (60 minutes by default). Clamped to the supported range of 1 second to 24 hours."
4203
4229
  },
4204
4230
  extensions: { type: "boolean" },
4205
4231
  recursive: { type: "boolean" },
@@ -4492,6 +4518,10 @@ var AGENTS_ACTION_DESCRIPTORS = [
4492
4518
  thinking: runProperties.thinking,
4493
4519
  tools: runProperties.tools,
4494
4520
  transport: runProperties.transport,
4521
+ cwd: {
4522
+ ...runProperties.cwd,
4523
+ description: "Filesystem execution directory for every activation; relative paths resolve from the host cwd. An OMP actor must also pass extensions: false, because recursive Fabric activations do not support cwd."
4524
+ },
4495
4525
  timeoutMs: runProperties.timeoutMs,
4496
4526
  extensions: runProperties.extensions,
4497
4527
  requires: {
@@ -5032,10 +5062,10 @@ var ResidencyClient = class {
5032
5062
  statusAgent(id) {
5033
5063
  const metadata = this.#metadata(id);
5034
5064
  if (!metadata) throw new Error(`Unknown durable Fabric agent: ${id}`);
5035
- const record = readJson(path5.join(metadata.runDirectory, "status.json"));
5036
- if (!record || record.id !== metadata.id) return structuredClone(metadata.handle);
5065
+ const record2 = readJson(path5.join(metadata.runDirectory, "status.json"));
5066
+ if (!record2 || record2.id !== metadata.id) return structuredClone(metadata.handle);
5037
5067
  return {
5038
- ...record,
5068
+ ...record2,
5039
5069
  cwd: metadata.handle.cwd,
5040
5070
  residency: "durable",
5041
5071
  logFile: path5.join(metadata.runDirectory, "events.jsonl"),
@@ -5286,27 +5316,37 @@ var actorRunBinding = (args) => ({
5286
5316
  ...typeof args.model === "string" && args.model.trim() ? { model: args.model.trim() } : {},
5287
5317
  ...isFabricThinking(args.thinking) ? { thinking: args.thinking } : {}
5288
5318
  });
5289
- var longerTimeoutOverride = (value, manager) => {
5319
+ var imageContents = (value) => {
5320
+ if (!Array.isArray(value)) return void 0;
5321
+ const images = value.filter(
5322
+ (entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry) && entry.type === "image" && typeof entry.data === "string" && typeof entry.mimeType === "string"
5323
+ );
5324
+ return images.length > 0 ? images : void 0;
5325
+ };
5326
+ var boundedTimeoutOverride = (value, manager) => {
5290
5327
  if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
5291
- const effective = effectiveAgentTimeoutMs(manager.config.timeoutMs, value);
5292
- return effective > manager.config.timeoutMs ? effective : void 0;
5328
+ return effectiveAgentTimeoutMs(manager.config.timeoutMs, value);
5293
5329
  };
5294
5330
  var runRequest = (args, context, manager, options = {}) => {
5295
5331
  const transport = args.transport === "auto" || args.transport === "process" || args.transport === "tmux" || args.transport === "screen" || args.transport === "localterm" || args.transport === "herdr" ? args.transport : void 0;
5296
5332
  const thinking = isFabricThinking(args.thinking) ? args.thinking : void 0;
5297
5333
  const tools = stringArray(args.tools);
5298
- const timeoutMs = longerTimeoutOverride(args.timeoutMs, manager);
5334
+ const addTools = stringArray(args.addTools);
5335
+ const images = imageContents(args.images);
5336
+ const timeoutMs = boundedTimeoutOverride(args.timeoutMs, manager);
5299
5337
  const runner = args.runner === "omp" || args.runner === "claude" || args.runner === "veda" ? args.runner : manager.config.runner;
5300
5338
  const inheritedModel = runner === "omp" && !manager.config.model && context.extensionContext.model ? `${context.extensionContext.model.provider}/${context.extensionContext.model.id}` : void 0;
5301
5339
  return {
5302
5340
  task: String(args.task),
5303
5341
  runner,
5342
+ ...images ? { images } : {},
5304
5343
  ...typeof args.name === "string" ? { name: args.name } : {},
5305
5344
  ...transport ? { transport } : {},
5306
5345
  ...typeof args.model === "string" ? { model: args.model } : inheritedModel ? { model: inheritedModel } : {},
5307
5346
  ...typeof args.persona === "string" && args.persona.trim() ? { persona: args.persona.trim() } : {},
5308
5347
  ...thinking ? { thinking } : {},
5309
5348
  ...tools ? { tools } : {},
5349
+ ...addTools ? { addTools } : {},
5310
5350
  ...timeoutMs !== void 0 ? { timeoutMs } : {},
5311
5351
  ...typeof args.extensions === "boolean" ? { extensions: args.extensions } : {},
5312
5352
  ...typeof args.recursive === "boolean" ? { recursive: args.recursive } : {},
@@ -5364,7 +5404,7 @@ var actorRequest = (args, context, manager, inheritModel = true) => {
5364
5404
  },
5365
5405
  []
5366
5406
  ) : void 0;
5367
- const timeoutMs = longerTimeoutOverride(args.timeoutMs, manager);
5407
+ const timeoutMs = boundedTimeoutOverride(args.timeoutMs, manager);
5368
5408
  const validWhile = typeof args.validWhile === "object" && args.validWhile !== null && !Array.isArray(args.validWhile) && args.validWhile.version === 1 && typeof args.validWhile.source === "string" ? { version: 1, source: args.validWhile.source } : void 0;
5369
5409
  const runner = args.runner === "omp" || args.runner === "claude" || args.runner === "veda" ? args.runner : manager.config.runner;
5370
5410
  if (runner === "veda") {
@@ -5372,6 +5412,12 @@ var actorRequest = (args, context, manager, inheritModel = true) => {
5372
5412
  'The Veda runner does not support persistent actors: Veda executes one headless prompt per invocation. Use an OMP or Claude actor, or agents.run({ runner: "veda" }).'
5373
5413
  );
5374
5414
  }
5415
+ const requestedCwd = typeof args.cwd === "string" ? args.cwd : void 0;
5416
+ validateAgentCwdRequest({
5417
+ ...requestedCwd === void 0 ? {} : { cwd: requestedCwd },
5418
+ recursive: (typeof args.extensions === "boolean" ? args.extensions : true) && runner === "omp"
5419
+ });
5420
+ const cwd = requestedCwd === void 0 ? void 0 : manager.resolveCwd(requestedCwd);
5375
5421
  const inheritedModel = inheritModel && runner === "omp" && !manager.config.model && context.extensionContext.model ? `${context.extensionContext.model.provider}/${context.extensionContext.model.id}` : void 0;
5376
5422
  return {
5377
5423
  ...args.scope === "session" || args.scope === "project" ? { scope: args.scope } : {},
@@ -5389,6 +5435,7 @@ var actorRequest = (args, context, manager, inheritModel = true) => {
5389
5435
  ...isFabricThinking(args.thinking) ? { thinking: args.thinking } : {},
5390
5436
  ...tools ? { tools } : {},
5391
5437
  ...args.transport === "auto" || args.transport === "process" || args.transport === "tmux" || args.transport === "screen" || args.transport === "localterm" || args.transport === "herdr" ? { transport: args.transport } : {},
5438
+ ...cwd ? { cwd } : {},
5392
5439
  ...timeoutMs !== void 0 ? { timeoutMs } : {},
5393
5440
  ...typeof args.extensions === "boolean" ? { extensions: args.extensions } : {},
5394
5441
  ...requires ? { requires } : {},
@@ -5398,20 +5445,20 @@ var actorRequest = (args, context, manager, inheritModel = true) => {
5398
5445
  var collectAgentToolPreviewNodes = (records, options, depth = 0, budget = { remaining: options.maxNodes ?? AGENT_PREVIEW_TREE_MAX_NODES }) => {
5399
5446
  const maxDepth = options.maxDepth ?? AGENT_PREVIEW_TREE_MAX_DEPTH;
5400
5447
  const nodes = [];
5401
- for (const record of records) {
5448
+ for (const record2 of records) {
5402
5449
  if (budget.remaining <= 0) break;
5403
5450
  budget.remaining -= 1;
5404
- const descendants = Array.isArray(record.nestedAgents) ? record.nestedAgents : [];
5451
+ const descendants = Array.isArray(record2.nestedAgents) ? record2.nestedAgents : [];
5405
5452
  const agents = depth + 1 < maxDepth && descendants.length > 0 && budget.remaining > 0 ? collectAgentToolPreviewNodes(descendants, options, depth + 1, budget) : [];
5406
5453
  nodes.push({
5407
- id: record.id,
5408
- name: record.actorName ?? record.name,
5409
- status: record.status,
5410
- ...record.runner === "omp" || record.runner === "claude" || record.runner === "veda" ? { runner: record.runner } : {},
5411
- owner: record.actorId ? "actor" : "agent",
5412
- ...record.currentTool ? { currentTool: record.currentTool } : {},
5413
- ...record.text ? { text: tailCodePoints(record.text, AGENT_PREVIEW_TEXT_CODE_POINTS) } : {},
5414
- tools: options.tools(record),
5454
+ id: record2.id,
5455
+ name: record2.actorName ?? record2.name,
5456
+ status: record2.status,
5457
+ ...record2.runner === "omp" || record2.runner === "claude" || record2.runner === "veda" ? { runner: record2.runner } : {},
5458
+ owner: record2.actorId ? "actor" : "agent",
5459
+ ...record2.currentTool ? { currentTool: record2.currentTool } : {},
5460
+ ...record2.text ? { text: tailCodePoints(record2.text, AGENT_PREVIEW_TEXT_CODE_POINTS) } : {},
5461
+ tools: options.tools(record2),
5415
5462
  ...agents.length > 0 ? { agents } : {},
5416
5463
  ...descendants.length > agents.length ? { agentsTruncated: true } : {}
5417
5464
  });
@@ -6457,18 +6504,18 @@ var AgentsProvider = class {
6457
6504
  const scope = this.#participantScope(scopeValue, "local");
6458
6505
  if (scope === "local") return this.manager.list();
6459
6506
  const local = /* @__PURE__ */ new Map();
6460
- const append = (record) => {
6461
- local.set(record.id, record);
6462
- if ("nestedAgents" in record) {
6463
- for (const nested of record.nestedAgents ?? []) append(nested);
6507
+ const append = (record2) => {
6508
+ local.set(record2.id, record2);
6509
+ if ("nestedAgents" in record2) {
6510
+ for (const nested of record2.nestedAgents ?? []) append(nested);
6464
6511
  }
6465
6512
  };
6466
- for (const record of this.manager.list()) append(record);
6467
- for (const record of this.residency?.listAgents() ?? []) append(record);
6513
+ for (const record2 of this.manager.list()) append(record2);
6514
+ for (const record2 of this.residency?.listAgents() ?? []) append(record2);
6468
6515
  const listed = this.participants.list({ scope, kinds: ["agent"] }).map((participant) => local.get(participant.id) ?? participant);
6469
- const seen = new Set(listed.map((record) => record.id));
6470
- for (const record of this.residency?.listAgents() ?? []) {
6471
- if (!seen.has(record.id)) listed.push(record);
6516
+ const seen = new Set(listed.map((record2) => record2.id));
6517
+ for (const record2 of this.residency?.listAgents() ?? []) {
6518
+ if (!seen.has(record2.id)) listed.push(record2);
6472
6519
  }
6473
6520
  return listed;
6474
6521
  }
@@ -6624,9 +6671,9 @@ var CapturedToolsProvider = class {
6624
6671
  #allowedTools = readChildToolAllowlist();
6625
6672
  async list(request, _context) {
6626
6673
  const query = request.query?.trim().toLowerCase();
6627
- const descriptors6 = this.catalog.list().filter((entry) => !this.#allowedTools || this.#allowedTools.has(entry.name)).map(descriptorFrom);
6628
- if (!query) return descriptors6;
6629
- return descriptors6.filter(
6674
+ const descriptors7 = this.catalog.list().filter((entry) => !this.#allowedTools || this.#allowedTools.has(entry.name)).map(descriptorFrom);
6675
+ if (!query) return descriptors7;
6676
+ return descriptors7.filter(
6630
6677
  (descriptor) => `${descriptor.name} ${descriptor.description} ${descriptor.namespace ?? ""}`.toLowerCase().includes(query)
6631
6678
  );
6632
6679
  }
@@ -6762,32 +6809,1302 @@ var CapturedToolsProvider = class {
6762
6809
  }
6763
6810
  };
6764
6811
 
6765
- // src/providers/compact-provider.ts
6812
+ // src/providers/codemap-provider.ts
6813
+ import { statSync as statSync2 } from "node:fs";
6814
+ import path9 from "node:path";
6766
6815
  import { Type } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-typebox";
6767
6816
  import { validateJsonSchemaValue } from "@oh-my-pi/pi-ai/utils/schema";
6768
- var requestSchema = Type.Object({
6769
- reason: Type.Optional(Type.String({
6817
+
6818
+ // src/codemap/budget.ts
6819
+ var KIND_LETTERS = {
6820
+ fn: "f",
6821
+ class: "c",
6822
+ iface: "i",
6823
+ type: "t",
6824
+ const: "v",
6825
+ enum: "e",
6826
+ method: "m"
6827
+ };
6828
+ var LEGEND = "# f=fn c=class i=iface t=type v=const e=enum m=method";
6829
+ var MIN_FILE_ENTRY = 8;
6830
+ var FOCUS_WEIGHT = 1;
6831
+ var CASCADE_WEIGHT = 0.6;
6832
+ var NAME_EXACT_SCORE = 6;
6833
+ var NAME_WORD_SCORE = 3;
6834
+ var PATH_WORD_SCORE = 4;
6835
+ var SUBSTRING_SCORE = 1;
6836
+ var MIN_SUBSTRING_LENGTH = 3;
6837
+ var EXPORT_PASSES = [true, false];
6838
+ var isUpper = (code) => code >= 65 && code <= 90;
6839
+ var isLower = (code) => code >= 97 && code <= 122;
6840
+ var isDigit = (code) => code >= 48 && code <= 57;
6841
+ var isWordChar = (code) => isUpper(code) || isLower(code) || isDigit(code);
6842
+ var compareText = (left, right) => left < right ? -1 : left > right ? 1 : 0;
6843
+ var collectWords = (value, sink) => {
6844
+ let start = -1;
6845
+ for (let index = 0; index < value.length; index++) {
6846
+ const code = value.charCodeAt(index);
6847
+ if (!isWordChar(code)) {
6848
+ if (start >= 0) sink.add(value.slice(start, index).toLowerCase());
6849
+ start = -1;
6850
+ continue;
6851
+ }
6852
+ if (start < 0) {
6853
+ start = index;
6854
+ continue;
6855
+ }
6856
+ const previous = value.charCodeAt(index - 1);
6857
+ const next = index + 1 < value.length ? value.charCodeAt(index + 1) : 0;
6858
+ if (isUpper(code) && (!isUpper(previous) || isLower(next))) {
6859
+ sink.add(value.slice(start, index).toLowerCase());
6860
+ start = index;
6861
+ }
6862
+ }
6863
+ if (start >= 0) sink.add(value.slice(start).toLowerCase());
6864
+ };
6865
+ var focusTerms = (focus) => {
6866
+ if (focus === void 0 || focus.length === 0) return [];
6867
+ const words = /* @__PURE__ */ new Set();
6868
+ collectWords(focus, words);
6869
+ return [...words];
6870
+ };
6871
+ var lexiconFor = (group) => {
6872
+ const pathWords = /* @__PURE__ */ new Set();
6873
+ collectWords(group.file, pathWords);
6874
+ const names = /* @__PURE__ */ new Set();
6875
+ const nameWords = /* @__PURE__ */ new Set();
6876
+ const distinct = [];
6877
+ for (const symbol of group.symbols) {
6878
+ const lowered = symbol.name.toLowerCase();
6879
+ if (names.has(lowered)) continue;
6880
+ names.add(lowered);
6881
+ distinct.push(lowered);
6882
+ collectWords(symbol.name, nameWords);
6883
+ }
6884
+ return {
6885
+ pathWords,
6886
+ pathLower: group.file.toLowerCase(),
6887
+ names,
6888
+ nameWords,
6889
+ namesBlob: distinct.join("\n")
6890
+ };
6891
+ };
6892
+ var termScore = (term, lexicon) => {
6893
+ const wideEnough = term.length >= MIN_SUBSTRING_LENGTH;
6894
+ let score = 0;
6895
+ if (lexicon.names.has(term)) score += NAME_EXACT_SCORE;
6896
+ else if (lexicon.nameWords.has(term)) score += NAME_WORD_SCORE;
6897
+ else if (wideEnough && lexicon.namesBlob.includes(term)) score += SUBSTRING_SCORE;
6898
+ if (lexicon.pathWords.has(term)) score += PATH_WORD_SCORE;
6899
+ else if (wideEnough && lexicon.pathLower.includes(term)) score += SUBSTRING_SCORE;
6900
+ return score;
6901
+ };
6902
+ var groupByFile = (symbols) => {
6903
+ const byFile = /* @__PURE__ */ new Map();
6904
+ for (const symbol of symbols) {
6905
+ let group = byFile.get(symbol.file);
6906
+ if (group === void 0) {
6907
+ group = { file: symbol.file, symbols: [], score: 0 };
6908
+ byFile.set(symbol.file, group);
6909
+ }
6910
+ group.symbols.push(symbol);
6911
+ }
6912
+ for (const group of byFile.values()) {
6913
+ group.symbols.sort((left, right) => left.line - right.line || compareText(left.name, right.name) || compareText(left.kind, right.kind));
6914
+ }
6915
+ return [...byFile.values()];
6916
+ };
6917
+ var applyFocus = (groups, focus) => {
6918
+ const terms = focusTerms(focus);
6919
+ if (terms.length === 0) return;
6920
+ let peak = 0;
6921
+ for (const group of groups) {
6922
+ const lexicon = lexiconFor(group);
6923
+ let raw = 0;
6924
+ for (const term of terms) raw += termScore(term, lexicon);
6925
+ group.score = raw;
6926
+ if (raw > peak) peak = raw;
6927
+ }
6928
+ for (const group of groups) group.score = peak > 0 ? group.score / peak * FOCUS_WEIGHT : 0;
6929
+ };
6930
+ var applyCascade = (groups, request) => {
6931
+ const edges = request.cascade?.edges;
6932
+ if (edges === void 0 || edges.length === 0) return;
6933
+ const best = /* @__PURE__ */ new Map();
6934
+ let peak = 0;
6935
+ for (const edge of edges) {
6936
+ if (edge.score > peak) peak = edge.score;
6937
+ const previous = best.get(edge.file);
6938
+ if (previous === void 0 || edge.score > previous) best.set(edge.file, edge.score);
6939
+ }
6940
+ if (peak <= 0) return;
6941
+ for (const group of groups) {
6942
+ const edgeScore = best.get(group.file);
6943
+ if (edgeScore !== void 0 && edgeScore > 0) {
6944
+ group.score += edgeScore / peak * CASCADE_WEIGHT;
6945
+ }
6946
+ }
6947
+ };
6948
+ var rankGroups = (groups, request) => {
6949
+ applyFocus(groups, request.focus);
6950
+ applyCascade(groups, request);
6951
+ groups.sort((left, right) => right.score - left.score || right.symbols.length - left.symbols.length || compareText(left.file, right.file));
6952
+ };
6953
+ var spend = (groups, charBudget, reserved) => {
6954
+ const lines = [];
6955
+ let used = reserved;
6956
+ let filesShown = 0;
6957
+ let symbolsShown = 0;
6958
+ let omittedFiles = 0;
6959
+ let omittedSymbols = 0;
6960
+ for (const group of groups) {
6961
+ const headerCost = group.file.length + 1;
6962
+ if (used + headerCost >= charBudget) {
6963
+ omittedFiles++;
6964
+ omittedSymbols += group.symbols.length;
6965
+ continue;
6966
+ }
6967
+ const rendered = new Array(group.symbols.length);
6968
+ const picked = [];
6969
+ let cost = headerCost;
6970
+ for (const exported of EXPORT_PASSES) {
6971
+ for (let index = 0; index < group.symbols.length; index++) {
6972
+ const symbol = group.symbols[index];
6973
+ if (symbol.exported !== exported) continue;
6974
+ let line = rendered[index];
6975
+ if (line === void 0) {
6976
+ line = `${symbol.line} ${KIND_LETTERS[symbol.kind]} ${symbol.name}`;
6977
+ rendered[index] = line;
6978
+ }
6979
+ const lineCost = line.length + 1;
6980
+ if (used + cost + lineCost > charBudget) continue;
6981
+ cost += lineCost;
6982
+ picked.push(index);
6983
+ }
6984
+ }
6985
+ if (picked.length === 0) {
6986
+ omittedFiles++;
6987
+ omittedSymbols += group.symbols.length;
6988
+ continue;
6989
+ }
6990
+ picked.sort((left, right) => left - right);
6991
+ lines.push(group.file);
6992
+ for (const index of picked) lines.push(rendered[index]);
6993
+ used += cost;
6994
+ filesShown++;
6995
+ symbolsShown += picked.length;
6996
+ omittedSymbols += group.symbols.length - picked.length;
6997
+ }
6998
+ return { lines, filesShown, symbolsShown, omittedFiles, omittedSymbols };
6999
+ };
7000
+ function assembleMap(request) {
7001
+ const groups = groupByFile(request.index.symbols);
7002
+ rankGroups(groups, request);
7003
+ const charBudget = request.maxTokens > 0 ? Math.floor(request.maxTokens) * 4 : 0;
7004
+ const legendCost = LEGEND.length + 1;
7005
+ const reserved = charBudget >= legendCost + MIN_FILE_ENTRY ? legendCost : 0;
7006
+ let plan = spend(groups, charBudget, reserved);
7007
+ let legendShown = reserved > 0;
7008
+ if (plan.filesShown === 0 && reserved > 0) {
7009
+ plan = spend(groups, charBudget, 0);
7010
+ legendShown = false;
7011
+ }
7012
+ const body = plan.lines.join("\n");
7013
+ const text = plan.lines.length === 0 ? "" : legendShown ? `${LEGEND}
7014
+ ${body}
7015
+ ` : `${body}
7016
+ `;
7017
+ return {
7018
+ text,
7019
+ tokensEstimated: estimateTokens({ role: "user", content: text }),
7020
+ filesShown: plan.filesShown,
7021
+ symbolsShown: plan.symbolsShown,
7022
+ omittedFiles: plan.omittedFiles,
7023
+ omittedSymbols: plan.omittedSymbols,
7024
+ truncated: plan.omittedFiles > 0 || plan.omittedSymbols > 0
7025
+ };
7026
+ }
7027
+
7028
+ // src/codemap/cascade.ts
7029
+ import { execFile } from "node:child_process";
7030
+ import { existsSync, realpathSync } from "node:fs";
7031
+ import path7 from "node:path";
7032
+ import { promisify } from "node:util";
7033
+ var execFileAsync = promisify(execFile);
7034
+ var BULK_COMMIT_FILE_CEILING = 100;
7035
+ var GIT_TIMEOUT_MS = 2e4;
7036
+ var GIT_MAX_BUFFER = 64 << 20;
7037
+ var COMMIT_MARKER_CODE = 1;
7038
+ var QUOTE_CODE = 34;
7039
+ var ESCAPE_BYTES = /* @__PURE__ */ new Map([
7040
+ ["a", 7],
7041
+ ["b", 8],
7042
+ ["t", 9],
7043
+ ["n", 10],
7044
+ ["v", 11],
7045
+ ["f", 12],
7046
+ ["r", 13],
7047
+ ['"', 34],
7048
+ ["\\", 92]
7049
+ ]);
7050
+ var toPosix = (value) => path7.sep === "/" ? value : value.replaceAll(path7.sep, "/");
7051
+ var unquoteGitPath = (raw) => {
7052
+ if (raw.length < 2 || raw.charCodeAt(raw.length - 1) !== QUOTE_CODE) return raw;
7053
+ const bytes = [];
7054
+ for (let index = 1; index < raw.length - 1; index += 1) {
7055
+ const char = raw[index];
7056
+ if (char !== "\\") {
7057
+ const code = char.charCodeAt(0);
7058
+ if (code < 128) bytes.push(code);
7059
+ else for (const byte of Buffer.from(char, "utf8")) bytes.push(byte);
7060
+ continue;
7061
+ }
7062
+ const next = raw[index + 1];
7063
+ if (next === void 0) break;
7064
+ const mapped = ESCAPE_BYTES.get(next);
7065
+ if (mapped !== void 0) {
7066
+ bytes.push(mapped);
7067
+ index += 1;
7068
+ continue;
7069
+ }
7070
+ if (next >= "0" && next <= "7") {
7071
+ let value = 0;
7072
+ let digits = 0;
7073
+ while (digits < 3) {
7074
+ const digit = raw[index + 1 + digits];
7075
+ if (digit === void 0 || digit < "0" || digit > "7") break;
7076
+ value = value * 8 + (digit.charCodeAt(0) - 48);
7077
+ digits += 1;
7078
+ }
7079
+ bytes.push(value & 255);
7080
+ index += digits;
7081
+ continue;
7082
+ }
7083
+ bytes.push(next.charCodeAt(0));
7084
+ index += 1;
7085
+ }
7086
+ return Buffer.from(bytes).toString("utf8");
7087
+ };
7088
+ var realPath = (value) => {
7089
+ try {
7090
+ return realpathSync.native(value);
7091
+ } catch {
7092
+ return value;
7093
+ }
7094
+ };
7095
+ var relativeSeed = (root, top, seed) => {
7096
+ const absolute = path7.isAbsolute(seed) ? seed : path7.resolve(root, seed);
7097
+ for (const base of /* @__PURE__ */ new Set([top, realPath(top)])) {
7098
+ for (const candidate of /* @__PURE__ */ new Set([absolute, realPath(absolute), path7.resolve(realPath(root), seed)])) {
7099
+ const relative = path7.relative(base, candidate);
7100
+ if (!relative || relative === ".." || relative.startsWith(`..${path7.sep}`) || path7.isAbsolute(relative)) {
7101
+ continue;
7102
+ }
7103
+ return toPosix(relative);
7104
+ }
7105
+ }
7106
+ return void 0;
7107
+ };
7108
+ var emptyGraph = (seeds, unavailable) => ({
7109
+ seeds,
7110
+ edges: [],
7111
+ commitsScanned: 0,
7112
+ truncated: false,
7113
+ unavailable
7114
+ });
7115
+ async function coChange(request) {
7116
+ const requestedSeeds = request.seeds.filter((seed) => seed.length > 0);
7117
+ if (requestedSeeds.length === 0) return emptyGraph([], "no seed files were provided");
7118
+ if (!existsSync(request.root)) return emptyGraph(requestedSeeds, "root directory does not exist");
7119
+ const maxCommits = Math.max(1, Math.floor(request.maxCommits) || 1);
7120
+ const limit = Math.max(1, Math.floor(request.limit) || 1);
7121
+ let top;
7122
+ try {
7123
+ const revParse = await execFileAsync("git", ["rev-parse", "--show-toplevel"], {
7124
+ cwd: request.root,
7125
+ timeout: GIT_TIMEOUT_MS,
7126
+ encoding: "utf8"
7127
+ });
7128
+ top = revParse.stdout.trim();
7129
+ } catch (error) {
7130
+ const code = error.code;
7131
+ if (code === "ENOENT") return emptyGraph(requestedSeeds, "git is not available");
7132
+ return emptyGraph(requestedSeeds, "not a git repository");
7133
+ }
7134
+ if (!top) return emptyGraph(requestedSeeds, "not a git repository");
7135
+ const seedSet = /* @__PURE__ */ new Set();
7136
+ for (const seed of requestedSeeds) {
7137
+ const normalized = relativeSeed(request.root, top, seed);
7138
+ if (normalized) seedSet.add(normalized);
7139
+ }
7140
+ const seeds = [...seedSet];
7141
+ if (seeds.length === 0) return emptyGraph(requestedSeeds, "seed files are outside the repository");
7142
+ let stdout;
7143
+ try {
7144
+ const log = await execFileAsync(
7145
+ "git",
7146
+ [
7147
+ "log",
7148
+ `--max-count=${maxCommits}`,
7149
+ "--no-merges",
7150
+ "--name-only",
7151
+ `--pretty=format:%H`
7152
+ ],
7153
+ {
7154
+ cwd: top,
7155
+ timeout: GIT_TIMEOUT_MS,
7156
+ maxBuffer: GIT_MAX_BUFFER,
7157
+ encoding: "utf8"
7158
+ }
7159
+ );
7160
+ stdout = log.stdout;
7161
+ } catch (error) {
7162
+ const code = error.code;
7163
+ if (code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
7164
+ return emptyGraph(seeds, "commit history exceeded the read budget");
7165
+ }
7166
+ return emptyGraph(seeds, "no commit history");
7167
+ }
7168
+ const fileCommits = /* @__PURE__ */ new Map();
7169
+ const sharedCommits = /* @__PURE__ */ new Map();
7170
+ const current = [];
7171
+ let commitsScanned = 0;
7172
+ let seedCommits = 0;
7173
+ let inCommit = false;
7174
+ let bulk = false;
7175
+ const settle = () => {
7176
+ if (!inCommit) return;
7177
+ inCommit = false;
7178
+ if (bulk) {
7179
+ bulk = false;
7180
+ current.length = 0;
7181
+ return;
7182
+ }
7183
+ commitsScanned += 1;
7184
+ let touchesSeed = false;
7185
+ for (const file of current) {
7186
+ if (seedSet.has(file)) {
7187
+ touchesSeed = true;
7188
+ break;
7189
+ }
7190
+ }
7191
+ if (touchesSeed) seedCommits += 1;
7192
+ for (const file of current) {
7193
+ fileCommits.set(file, (fileCommits.get(file) ?? 0) + 1);
7194
+ if (touchesSeed && !seedSet.has(file)) {
7195
+ sharedCommits.set(file, (sharedCommits.get(file) ?? 0) + 1);
7196
+ }
7197
+ }
7198
+ current.length = 0;
7199
+ };
7200
+ let cursor = 0;
7201
+ while (cursor <= stdout.length) {
7202
+ let end = stdout.indexOf("\n", cursor);
7203
+ if (end < 0) end = stdout.length;
7204
+ const line = stdout.slice(cursor, end);
7205
+ cursor = end + 1;
7206
+ if (line.length === 0) continue;
7207
+ if (line.charCodeAt(0) === COMMIT_MARKER_CODE) {
7208
+ settle();
7209
+ inCommit = true;
7210
+ continue;
7211
+ }
7212
+ if (!inCommit || bulk) continue;
7213
+ if (current.length >= BULK_COMMIT_FILE_CEILING) {
7214
+ bulk = true;
7215
+ current.length = 0;
7216
+ continue;
7217
+ }
7218
+ current.push(line.charCodeAt(0) === QUOTE_CODE ? unquoteGitPath(line) : line);
7219
+ }
7220
+ settle();
7221
+ if (commitsScanned === 0) return emptyGraph(seeds, "no commit history");
7222
+ const edges = [];
7223
+ if (seedCommits > 0) {
7224
+ const seedRoot = Math.sqrt(seedCommits);
7225
+ for (const [file, shared] of sharedCommits) {
7226
+ const total = fileCommits.get(file) ?? shared;
7227
+ edges.push({ file, score: shared / (seedRoot * Math.sqrt(total)), commits: shared });
7228
+ }
7229
+ }
7230
+ edges.sort((left, right) => {
7231
+ if (right.score !== left.score) return right.score - left.score;
7232
+ if (right.commits !== left.commits) return right.commits - left.commits;
7233
+ return left.file < right.file ? -1 : left.file > right.file ? 1 : 0;
7234
+ });
7235
+ const truncated = edges.length > limit;
7236
+ if (truncated) edges.length = limit;
7237
+ if (seedCommits === 0) {
7238
+ return { seeds, edges, commitsScanned, truncated, unavailable: "seed files do not appear in the scanned history" };
7239
+ }
7240
+ return { seeds, edges, commitsScanned, truncated };
7241
+ }
7242
+
7243
+ // src/codemap/symbols.ts
7244
+ import { execFile as execFile2 } from "node:child_process";
7245
+ import { readFile, readdir } from "node:fs/promises";
7246
+ import { createRequire } from "node:module";
7247
+ import path8 from "node:path";
7248
+ import { promisify as promisify2 } from "node:util";
7249
+ var execFileAsync2 = promisify2(execFile2);
7250
+ var commonDirectory = (files) => {
7251
+ let prefix;
7252
+ for (const file of files) {
7253
+ const parts = file.split("/");
7254
+ parts.pop();
7255
+ if (prefix === void 0) {
7256
+ prefix = parts;
7257
+ continue;
7258
+ }
7259
+ let shared = 0;
7260
+ while (shared < prefix.length && shared < parts.length && prefix[shared] === parts[shared]) shared++;
7261
+ prefix.length = shared;
7262
+ if (shared === 0) return "";
7263
+ }
7264
+ return prefix === void 0 ? "" : prefix.join("/");
7265
+ };
7266
+ var importBundledNatives = async () => {
7267
+ try {
7268
+ const bundled = await import("@oh-my-pi/pi-natives");
7269
+ return typeof bundled.astGrep === "function" ? bundled : void 0;
7270
+ } catch {
7271
+ return void 0;
7272
+ }
7273
+ };
7274
+ var importPlatformNatives = () => {
7275
+ const load = createRequire(import.meta.url);
7276
+ return load(`@oh-my-pi/pi-natives-${process.platform}-${process.arch}`);
7277
+ };
7278
+ var nativesOnce;
7279
+ var codeNatives = () => {
7280
+ nativesOnce ??= importBundledNatives().then((bundled) => bundled ?? importPlatformNatives());
7281
+ return nativesOnce;
7282
+ };
7283
+ var SCRIPT_DECLARATION = /^(?:export\s+)?(?:default\s+)?(?:declare\s+)?(?:abstract\s+)?(?:async\s+)?(function|class|interface|type|enum|const|let|var)\b/;
7284
+ var ARROW_VALUE = /^(?:async\s*)?[(<]/;
7285
+ var classifyScript = (text, meta) => {
7286
+ const exported = text.startsWith("export");
7287
+ const declaration = SCRIPT_DECLARATION.exec(text);
7288
+ switch (declaration?.[1]) {
7289
+ case "function":
7290
+ return { kind: "fn", exported };
7291
+ case "class":
7292
+ return { kind: "class", exported };
7293
+ case "interface":
7294
+ return { kind: "iface", exported };
7295
+ case "type":
7296
+ return { kind: "type", exported };
7297
+ case "enum":
7298
+ return { kind: "enum", exported };
7299
+ default: {
7300
+ const value = meta?.V;
7301
+ if (value === void 0) return { kind: "fn", exported };
7302
+ const arrow = ARROW_VALUE.test(value) && value.includes("=>");
7303
+ return { kind: arrow ? "fn" : "const", exported };
7304
+ }
7305
+ }
7306
+ };
7307
+ var SCRIPT_VALUE_PATTERNS = [
7308
+ "function $NAME($$$A) { $$$B }",
7309
+ "export function $NAME($$$A) { $$$B }",
7310
+ "export default function $NAME($$$A) { $$$B }",
7311
+ "class $NAME { $$$B }",
7312
+ "export class $NAME { $$$B }",
7313
+ "export default class $NAME { $$$B }",
7314
+ "const $NAME = ($$$A) => $B",
7315
+ "const $NAME = $V",
7316
+ "export const $NAME = $V"
7317
+ ];
7318
+ var TYPED_SCRIPT_PATTERNS = [
7319
+ ...SCRIPT_VALUE_PATTERNS,
7320
+ "interface $NAME { $$$B }",
7321
+ "export interface $NAME { $$$B }",
7322
+ "type $NAME = $V",
7323
+ "export type $NAME = $V",
7324
+ "enum $NAME { $$$B }",
7325
+ "export enum $NAME { $$$B }"
7326
+ ];
7327
+ var PYTHON_DEFINITION = /^(?:async\s+)?def\b/;
7328
+ var PYTHON_CLASS = /^class\b/;
7329
+ var classifyPython = (text) => {
7330
+ if (PYTHON_DEFINITION.test(text)) return { kind: "fn", exported: true };
7331
+ if (PYTHON_CLASS.test(text)) return { kind: "class", exported: true };
7332
+ return { kind: "const", exported: true };
7333
+ };
7334
+ var GO_METHOD = /^func\s*\(/;
7335
+ var GO_FUNCTION = /^func\b/;
7336
+ var GO_STRUCT = /^type\s+\w+\s+struct\b/;
7337
+ var GO_INTERFACE = /^type\s+\w+\s+interface\b/;
7338
+ var GO_TYPE = /^type\b/;
7339
+ var classifyGo = (text) => {
7340
+ if (GO_METHOD.test(text)) return { kind: "method", exported: true };
7341
+ if (GO_FUNCTION.test(text)) return { kind: "fn", exported: true };
7342
+ if (GO_STRUCT.test(text)) return { kind: "class", exported: true };
7343
+ if (GO_INTERFACE.test(text)) return { kind: "iface", exported: true };
7344
+ if (GO_TYPE.test(text)) return { kind: "type", exported: true };
7345
+ return { kind: "const", exported: true };
7346
+ };
7347
+ var RUST_KEYWORD = /^(?:pub(?:\([^)]*\))?\s+)?(fn|struct|enum|trait|type|const|static)\b/;
7348
+ var classifyRust = (text) => {
7349
+ const exported = text.startsWith("pub");
7350
+ switch (RUST_KEYWORD.exec(text)?.[1]) {
7351
+ case "fn":
7352
+ return { kind: "fn", exported };
7353
+ case "struct":
7354
+ return { kind: "class", exported };
7355
+ case "enum":
7356
+ return { kind: "enum", exported };
7357
+ case "trait":
7358
+ return { kind: "iface", exported };
7359
+ case "type":
7360
+ return { kind: "type", exported };
7361
+ default:
7362
+ return { kind: "const", exported };
7363
+ }
7364
+ };
7365
+ var JAVA_KEYWORD = /^(?:(?:public|private|protected|static|final|abstract|synchronized|native|strictfp)\s+)*(class|interface|enum|record)\b/;
7366
+ var classifyJava = (text) => {
7367
+ const exported = text.startsWith("public");
7368
+ switch (JAVA_KEYWORD.exec(text)?.[1]) {
7369
+ case "class":
7370
+ case "record":
7371
+ return { kind: "class", exported };
7372
+ case "interface":
7373
+ return { kind: "iface", exported };
7374
+ case "enum":
7375
+ return { kind: "enum", exported };
7376
+ default:
7377
+ return { kind: "method", exported };
7378
+ }
7379
+ };
7380
+ var globFor = (extensions) => extensions.length === 1 ? `**/*.${extensions[0]}` : `**/*.{${extensions.join(",")}}`;
7381
+ var defineSpec = (id, lang, extensions, patterns, classify) => ({ id, lang, extensions, glob: globFor(extensions), patterns, classify });
7382
+ var LANGUAGE_SPECS = [
7383
+ defineSpec("ts", "ts", ["ts", "mts", "cts"], TYPED_SCRIPT_PATTERNS, classifyScript),
7384
+ defineSpec("tsx", "tsx", ["tsx"], TYPED_SCRIPT_PATTERNS, classifyScript),
7385
+ defineSpec("js", "js", ["js", "mjs", "cjs", "jsx"], SCRIPT_VALUE_PATTERNS, classifyScript),
7386
+ defineSpec(
7387
+ "python",
7388
+ "python",
7389
+ ["py", "pyi"],
7390
+ [
7391
+ "def $NAME($$$A): $$$B",
7392
+ "class $NAME($$$A): $$$B",
7393
+ "class $NAME: $$$B",
7394
+ "$NAME = $V"
7395
+ ],
7396
+ classifyPython
7397
+ ),
7398
+ defineSpec(
7399
+ "go",
7400
+ "go",
7401
+ ["go"],
7402
+ [
7403
+ "func $NAME($$$A) $$$R { $$$B }",
7404
+ "func ($$$RECV) $NAME($$$A) $$$R { $$$B }",
7405
+ "type $NAME struct { $$$B }",
7406
+ "type $NAME interface { $$$B }",
7407
+ "type $NAME = $V",
7408
+ "const $NAME = $V",
7409
+ "var $NAME = $V"
7410
+ ],
7411
+ classifyGo
7412
+ ),
7413
+ defineSpec(
7414
+ "rust",
7415
+ "rust",
7416
+ ["rs"],
7417
+ [
7418
+ "fn $NAME($$$A) { $$$B }",
7419
+ "fn $NAME($$$A) -> $R { $$$B }",
7420
+ "pub fn $NAME($$$A) { $$$B }",
7421
+ "pub fn $NAME($$$A) -> $R { $$$B }",
7422
+ "struct $NAME { $$$B }",
7423
+ "pub struct $NAME { $$$B }",
7424
+ "enum $NAME { $$$B }",
7425
+ "pub enum $NAME { $$$B }",
7426
+ "trait $NAME { $$$B }",
7427
+ "pub trait $NAME { $$$B }",
7428
+ "type $NAME = $V;",
7429
+ "pub type $NAME = $V;",
7430
+ "const $NAME: $T = $V;",
7431
+ "pub const $NAME: $T = $V;",
7432
+ "static $NAME: $T = $V;",
7433
+ "pub static $NAME: $T = $V;"
7434
+ ],
7435
+ classifyRust
7436
+ ),
7437
+ defineSpec(
7438
+ "java",
7439
+ "java",
7440
+ ["java"],
7441
+ [
7442
+ "class $NAME { $$$B }",
7443
+ "public class $NAME { $$$B }",
7444
+ "interface $NAME { $$$B }",
7445
+ "public interface $NAME { $$$B }",
7446
+ "enum $NAME { $$$B }",
7447
+ "public enum $NAME { $$$B }",
7448
+ "$RET $NAME($$$A) { $$$B }",
7449
+ "public $RET $NAME($$$A) { $$$B }",
7450
+ "private $RET $NAME($$$A) { $$$B }",
7451
+ "protected $RET $NAME($$$A) { $$$B }",
7452
+ "public static $RET $NAME($$$A) { $$$B }",
7453
+ "static $RET $NAME($$$A) { $$$B }"
7454
+ ],
7455
+ classifyJava
7456
+ )
7457
+ ];
7458
+ var EXTENSION_SPECS = /* @__PURE__ */ new Map();
7459
+ for (const spec of LANGUAGE_SPECS) {
7460
+ for (const extension of spec.extensions) EXTENSION_SPECS.set(extension, spec);
7461
+ }
7462
+ var FALLBACK_EXTENSIONS = /* @__PURE__ */ new Set([
7463
+ "c",
7464
+ "cc",
7465
+ "cpp",
7466
+ "cs",
7467
+ "cxx",
7468
+ "dart",
7469
+ "elm",
7470
+ "erl",
7471
+ "ex",
7472
+ "exs",
7473
+ "groovy",
7474
+ "h",
7475
+ "hh",
7476
+ "hpp",
7477
+ "hs",
7478
+ "jl",
7479
+ "kt",
7480
+ "kts",
7481
+ "lua",
7482
+ "nim",
7483
+ "php",
7484
+ "pl",
7485
+ "pm",
7486
+ "proto",
7487
+ "rb",
7488
+ "scala",
7489
+ "sh",
7490
+ "sql",
7491
+ "svelte",
7492
+ "swift",
7493
+ "vue",
7494
+ "zig"
7495
+ ]);
7496
+ var SKIP_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist"]);
7497
+ var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
7498
+ var KIND_RANK = {
7499
+ method: 5,
7500
+ fn: 4,
7501
+ class: 3,
7502
+ iface: 3,
7503
+ enum: 3,
7504
+ type: 2,
7505
+ const: 1
7506
+ };
7507
+ var MATCH_TIMEOUT_MS = 12e4;
7508
+ var MAX_FALLBACK_BYTES = 2e6;
7509
+ var extensionOf = (file) => {
7510
+ const slash = file.lastIndexOf("/");
7511
+ const dot = file.lastIndexOf(".");
7512
+ if (dot <= slash + 1) return void 0;
7513
+ return file.slice(dot + 1).toLowerCase();
7514
+ };
7515
+ var indexableExtension = (file) => {
7516
+ const extension = extensionOf(file);
7517
+ if (extension === void 0) return void 0;
7518
+ if (EXTENSION_SPECS.has(extension) || FALLBACK_EXTENSIONS.has(extension)) return extension;
7519
+ return void 0;
7520
+ };
7521
+ var GLOB_ESCAPE = /[.+^$()|[\]\\]/g;
7522
+ var globToRegExp = (pattern) => {
7523
+ let source = "";
7524
+ let braces = 0;
7525
+ for (let i = 0; i < pattern.length; i += 1) {
7526
+ const character = pattern[i];
7527
+ if (character === "*") {
7528
+ if (pattern[i + 1] === "*") {
7529
+ i += 1;
7530
+ if (pattern[i + 1] === "/") {
7531
+ i += 1;
7532
+ source += "(?:[^/]*/)*";
7533
+ } else {
7534
+ source += ".*";
7535
+ }
7536
+ continue;
7537
+ }
7538
+ source += "[^/]*";
7539
+ continue;
7540
+ }
7541
+ if (character === "?") {
7542
+ source += "[^/]";
7543
+ continue;
7544
+ }
7545
+ if (character === "{") {
7546
+ braces += 1;
7547
+ source += "(?:";
7548
+ continue;
7549
+ }
7550
+ if (character === "}" && braces > 0) {
7551
+ braces -= 1;
7552
+ source += ")";
7553
+ continue;
7554
+ }
7555
+ if (character === "," && braces > 0) {
7556
+ source += "|";
7557
+ continue;
7558
+ }
7559
+ source += character?.replace(GLOB_ESCAPE, "\\$&") ?? "";
7560
+ }
7561
+ return new RegExp(`^${source}$`);
7562
+ };
7563
+ var GIT_LIST_TIMEOUT_MS = 2e4;
7564
+ var GIT_LIST_MAX_BUFFER = 64 * 1024 * 1024;
7565
+ var gitTrackedFiles = async (root, matcher, signal) => {
7566
+ let stdout;
7567
+ try {
7568
+ const result = await execFileAsync2(
7569
+ "git",
7570
+ ["-C", root, "ls-files", "--cached", "--others", "--exclude-standard", "-z"],
7571
+ {
7572
+ maxBuffer: GIT_LIST_MAX_BUFFER,
7573
+ timeout: GIT_LIST_TIMEOUT_MS,
7574
+ ...signal ? { signal } : {}
7575
+ }
7576
+ );
7577
+ stdout = result.stdout;
7578
+ } catch {
7579
+ return void 0;
7580
+ }
7581
+ const found = [];
7582
+ let cursor = 0;
7583
+ while (cursor < stdout.length) {
7584
+ let end = stdout.indexOf("\0", cursor);
7585
+ if (end === -1) end = stdout.length;
7586
+ const relative = stdout.slice(cursor, end);
7587
+ cursor = end + 1;
7588
+ if (relative.length === 0) continue;
7589
+ if (relative.charCodeAt(0) === 46) continue;
7590
+ if (indexableExtension(relative) === void 0) continue;
7591
+ if (matcher !== void 0 && !matcher.test(relative)) continue;
7592
+ let skipped2 = false;
7593
+ let segmentStart = 0;
7594
+ while (segmentStart < relative.length) {
7595
+ let segmentEnd = relative.indexOf("/", segmentStart);
7596
+ if (segmentEnd === -1) break;
7597
+ const segment = relative.slice(segmentStart, segmentEnd);
7598
+ if (segment.charCodeAt(0) === 46 || SKIP_DIRECTORIES.has(segment)) {
7599
+ skipped2 = true;
7600
+ break;
7601
+ }
7602
+ segmentStart = segmentEnd + 1;
7603
+ }
7604
+ if (!skipped2) found.push(relative);
7605
+ }
7606
+ return found;
7607
+ };
7608
+ var walkFiles = async (root, matcher, signal) => {
7609
+ const found = [];
7610
+ const pending = [""];
7611
+ while (pending.length > 0) {
7612
+ const directory = pending.pop() ?? "";
7613
+ throwIfAborted(signal);
7614
+ let entries;
7615
+ try {
7616
+ entries = await readdir(directory === "" ? root : path8.join(root, directory), {
7617
+ withFileTypes: true
7618
+ });
7619
+ } catch {
7620
+ continue;
7621
+ }
7622
+ for (const entry of entries) {
7623
+ const name = entry.name;
7624
+ if (name.charCodeAt(0) === 46) continue;
7625
+ const relative = directory === "" ? name : `${directory}/${name}`;
7626
+ if (entry.isDirectory()) {
7627
+ if (!SKIP_DIRECTORIES.has(name)) pending.push(relative);
7628
+ continue;
7629
+ }
7630
+ if (!entry.isFile()) continue;
7631
+ if (indexableExtension(relative) === void 0) continue;
7632
+ if (matcher !== void 0 && !matcher.test(relative)) continue;
7633
+ found.push(relative);
7634
+ }
7635
+ }
7636
+ return found;
7637
+ };
7638
+ var discoverFiles = async (root, matcher, signal) => await gitTrackedFiles(root, matcher, signal) ?? await walkFiles(root, matcher, signal);
7639
+ var COMMENT_HEAD = /^(?:\/\/|\/\*|\*|#|--|<!--|;|%)/;
7640
+ var IMPORT_HEAD = /^(?:import|from|require|include|use|using|package|open|load|source)\b/;
7641
+ var DECLARATION_HEAD = /^(?:export|pub|public|private|protected|internal|static|final|abstract|override|virtual|inline|async|unsafe|extern|shared|readonly|declare|local|def|defp|defmodule|defstruct|defmacro|class|module|namespace|record|object|data|struct|enum|union|interface|trait|protocol|actor|impl|extension|fn|func|function|sub|procedure|method|const|let|var|val|type|typedef|typealias|template|operator|constructor|init|property|event|signal|macro|task)\b/;
7642
+ var ASSIGNMENT_HEAD = /^[A-Za-z_$][\w$]*\s*(?::[^=]*)?=[^=]/;
7643
+ var EXPORTED_HEAD = /^(?:export|pub|public)\b/;
7644
+ var IDENTIFIERS = /[A-Za-z_$][\w$]*/g;
7645
+ var FALLBACK_KINDS = /* @__PURE__ */ new Map([
7646
+ ["class", "class"],
7647
+ ["module", "class"],
7648
+ ["namespace", "class"],
7649
+ ["record", "class"],
7650
+ ["object", "class"],
7651
+ ["struct", "class"],
7652
+ ["impl", "class"],
7653
+ ["actor", "class"],
7654
+ ["defmodule", "class"],
7655
+ ["defstruct", "class"],
7656
+ ["interface", "iface"],
7657
+ ["trait", "iface"],
7658
+ ["protocol", "iface"],
7659
+ ["enum", "enum"],
7660
+ ["union", "enum"],
7661
+ ["def", "fn"],
7662
+ ["defp", "fn"],
7663
+ ["defmacro", "fn"],
7664
+ ["fn", "fn"],
7665
+ ["func", "fn"],
7666
+ ["function", "fn"],
7667
+ ["sub", "fn"],
7668
+ ["procedure", "fn"],
7669
+ ["method", "method"],
7670
+ ["constructor", "method"],
7671
+ ["init", "method"],
7672
+ ["type", "type"],
7673
+ ["typedef", "type"],
7674
+ ["typealias", "type"]
7675
+ ]);
7676
+ var FALLBACK_SKIP = /* @__PURE__ */ new Set([
7677
+ ...FALLBACK_KINDS.keys(),
7678
+ "export",
7679
+ "pub",
7680
+ "public",
7681
+ "private",
7682
+ "protected",
7683
+ "internal",
7684
+ "static",
7685
+ "final",
7686
+ "abstract",
7687
+ "override",
7688
+ "virtual",
7689
+ "inline",
7690
+ "async",
7691
+ "unsafe",
7692
+ "extern",
7693
+ "shared",
7694
+ "readonly",
7695
+ "declare",
7696
+ "local",
7697
+ "extension",
7698
+ "template",
7699
+ "operator",
7700
+ "property",
7701
+ "event",
7702
+ "signal",
7703
+ "macro",
7704
+ "task",
7705
+ "const",
7706
+ "let",
7707
+ "var",
7708
+ "val",
7709
+ "auto",
7710
+ "new",
7711
+ "return",
7712
+ "self",
7713
+ "this",
7714
+ "void",
7715
+ "int",
7716
+ "long",
7717
+ "short",
7718
+ "byte",
7719
+ "char",
7720
+ "bool",
7721
+ "boolean",
7722
+ "float",
7723
+ "double",
7724
+ "string",
7725
+ "str",
7726
+ "unsigned",
7727
+ "signed",
7728
+ "size_t",
7729
+ "mut",
7730
+ "end",
7731
+ "do",
7732
+ "then",
7733
+ "begin",
7734
+ "where",
7735
+ "with",
7736
+ "as",
7737
+ "in",
7738
+ "of",
7739
+ "is",
7740
+ "not",
7741
+ "and",
7742
+ "or",
7743
+ "if",
7744
+ "else",
7745
+ "for",
7746
+ "while",
7747
+ "switch",
7748
+ "match",
7749
+ "try",
7750
+ "catch",
7751
+ "throw",
7752
+ "throws",
7753
+ "extends",
7754
+ "implements"
7755
+ ]);
7756
+ var fallbackSymbol = (file, line, lineNumber) => {
7757
+ const trimmed = line.trim();
7758
+ if (trimmed.length === 0) return void 0;
7759
+ if (COMMENT_HEAD.test(trimmed) || IMPORT_HEAD.test(trimmed)) return void 0;
7760
+ if (!DECLARATION_HEAD.test(trimmed) && !ASSIGNMENT_HEAD.test(trimmed)) return void 0;
7761
+ IDENTIFIERS.lastIndex = 0;
7762
+ let kind = "const";
7763
+ let name;
7764
+ for (let token = IDENTIFIERS.exec(trimmed); token !== null; token = IDENTIFIERS.exec(trimmed)) {
7765
+ const word = token[0];
7766
+ if (!FALLBACK_SKIP.has(word)) {
7767
+ name = word;
7768
+ break;
7769
+ }
7770
+ const mapped = FALLBACK_KINDS.get(word);
7771
+ if (mapped !== void 0 && kind === "const") kind = mapped;
7772
+ }
7773
+ if (name === void 0 || !IDENTIFIER.test(name)) return void 0;
7774
+ const exported = EXPORTED_HEAD.test(trimmed) || name.charCodeAt(0) !== 95;
7775
+ return { file, line: lineNumber, kind, name, exported };
7776
+ };
7777
+ var countLines = (code) => {
7778
+ let lines = 1;
7779
+ for (let at = code.indexOf("\n"); at >= 0; at = code.indexOf("\n", at + 1)) lines += 1;
7780
+ return lines;
7781
+ };
7782
+ var record = (into, symbol) => {
7783
+ const key = `${symbol.file}\0${symbol.line}\0${symbol.name}`;
7784
+ const existing = into.get(key);
7785
+ if (existing === void 0) {
7786
+ into.set(key, symbol);
7787
+ return;
7788
+ }
7789
+ if (symbol.exported) existing.exported = true;
7790
+ if (KIND_RANK[symbol.kind] > KIND_RANK[existing.kind]) existing.kind = symbol.kind;
7791
+ };
7792
+ var toPosix2 = (value) => path8.sep === "/" ? value : value.split(path8.sep).join("/");
7793
+ var compareSymbols = (left, right) => {
7794
+ if (left.file !== right.file) return left.file < right.file ? -1 : 1;
7795
+ if (left.line !== right.line) return left.line - right.line;
7796
+ return left.name < right.name ? -1 : left.name > right.name ? 1 : 0;
7797
+ };
7798
+ async function buildSymbolIndex(request) {
7799
+ const startedAt = Date.now();
7800
+ const signal = request.signal;
7801
+ throwIfAborted(signal);
7802
+ const root = path8.resolve(request.root);
7803
+ const matcher = request.glob === void 0 ? void 0 : globToRegExp(request.glob);
7804
+ const discovered = await discoverFiles(root, matcher, signal);
7805
+ discovered.sort();
7806
+ let truncated = false;
7807
+ let files = discovered;
7808
+ if (files.length > request.maxFiles) {
7809
+ files = discovered.slice(0, request.maxFiles);
7810
+ truncated = true;
7811
+ }
7812
+ const languages = {};
7813
+ const assigned = /* @__PURE__ */ new Map();
7814
+ const activeSpecs = /* @__PURE__ */ new Set();
7815
+ const fallbackFiles = [];
7816
+ for (const file of files) {
7817
+ const extension = indexableExtension(file);
7818
+ if (extension === void 0) continue;
7819
+ const spec = EXTENSION_SPECS.get(extension);
7820
+ const label = spec?.id ?? extension;
7821
+ languages[label] = (languages[label] ?? 0) + 1;
7822
+ if (spec === void 0) {
7823
+ fallbackFiles.push(file);
7824
+ continue;
7825
+ }
7826
+ assigned.set(file, spec.id);
7827
+ activeSpecs.add(spec.id);
7828
+ }
7829
+ const natives = await codeNatives();
7830
+ const collected = /* @__PURE__ */ new Map();
7831
+ const matchLimit = Math.max(4096, request.maxSymbols * 2);
7832
+ for (const spec of LANGUAGE_SPECS) {
7833
+ if (!activeSpecs.has(spec.id)) continue;
7834
+ throwIfAborted(signal);
7835
+ const scoped = [];
7836
+ for (const [file, id] of assigned) if (id === spec.id) scoped.push(file);
7837
+ const prefix = commonDirectory(scoped);
7838
+ const result = await natives.astGrep({
7839
+ patterns: spec.patterns,
7840
+ lang: spec.lang,
7841
+ path: prefix === "" ? root : path8.join(root, prefix),
7842
+ glob: spec.glob,
7843
+ includeMeta: true,
7844
+ limit: matchLimit,
7845
+ timeoutMs: MATCH_TIMEOUT_MS,
7846
+ ...signal ? { signal } : {}
7847
+ });
7848
+ if (result.limitReached) truncated = true;
7849
+ for (const match of result.matches) {
7850
+ const relative = toPosix2(match.path);
7851
+ const file = prefix === "" ? relative : `${prefix}/${relative}`;
7852
+ if (assigned.get(file) !== spec.id) continue;
7853
+ const name = match.metaVariables?.NAME;
7854
+ if (name === void 0 || !IDENTIFIER.test(name)) continue;
7855
+ const classification = spec.classify(match.text, match.metaVariables);
7856
+ record(collected, {
7857
+ file,
7858
+ line: match.startLine,
7859
+ kind: classification.kind,
7860
+ name,
7861
+ exported: classification.exported
7862
+ });
7863
+ }
7864
+ }
7865
+ for (const file of fallbackFiles) {
7866
+ throwIfAborted(signal);
7867
+ let code;
7868
+ try {
7869
+ code = await readFile(path8.join(root, file), "utf8");
7870
+ } catch {
7871
+ continue;
7872
+ }
7873
+ if (code.length === 0 || code.length > MAX_FALLBACK_BYTES) continue;
7874
+ let summary;
7875
+ try {
7876
+ summary = natives.summarizeCode({
7877
+ code,
7878
+ path: file,
7879
+ unfoldUntilLines: Math.max(64, countLines(code) >> 2)
7880
+ });
7881
+ } catch {
7882
+ continue;
7883
+ }
7884
+ for (const segment of summary.segments) {
7885
+ if (segment.kind !== "kept" || segment.text === void 0) continue;
7886
+ const lines = segment.text.split("\n");
7887
+ for (let offset = 0; offset < lines.length; offset += 1) {
7888
+ const symbol = fallbackSymbol(file, lines[offset] ?? "", segment.startLine + offset);
7889
+ if (symbol !== void 0) record(collected, symbol);
7890
+ }
7891
+ }
7892
+ }
7893
+ const symbols = [...collected.values()];
7894
+ symbols.sort(compareSymbols);
7895
+ if (symbols.length > request.maxSymbols) {
7896
+ symbols.length = request.maxSymbols;
7897
+ truncated = true;
7898
+ }
7899
+ return {
7900
+ root,
7901
+ symbols,
7902
+ files,
7903
+ languages,
7904
+ fallbackFiles,
7905
+ truncated,
7906
+ elapsedMs: Date.now() - startedAt
7907
+ };
7908
+ }
7909
+
7910
+ // src/providers/codemap-provider.ts
7911
+ var INDEX_TTL_MS = 3e4;
7912
+ var pathField = Type.Optional(Type.String({
7913
+ maxLength: 4096,
7914
+ description: "Directory to index. Relative paths resolve against the session directory; defaults to it. Point this at a project root when the session runs elsewhere."
7915
+ }));
7916
+ var mapSchema = Type.Object({
7917
+ path: pathField,
7918
+ maxTokens: Type.Optional(Type.Number({
7919
+ minimum: 200,
7920
+ maximum: 2e5,
7921
+ description: "Token ceiling for the rendered map"
7922
+ })),
7923
+ glob: Type.Optional(Type.String({
7924
+ maxLength: 512,
7925
+ description: "Glob filter relative to the workspace root"
7926
+ })),
7927
+ focus: Type.Optional(Type.String({
7928
+ maxLength: 512,
7929
+ description: "Task description or symbol name the map should centre on"
7930
+ })),
7931
+ seeds: Type.Optional(Type.Array(Type.String({ maxLength: 1024 }), {
7932
+ maxItems: 32,
7933
+ description: "Files whose git co-change history should raise related files in the ranking"
7934
+ })),
7935
+ refresh: Type.Optional(Type.Boolean({
7936
+ description: "Rebuild the symbol index instead of reusing the cached one"
7937
+ }))
7938
+ }, { additionalProperties: false });
7939
+ var cascadeSchema = Type.Object({
7940
+ path: pathField,
7941
+ seeds: Type.Array(Type.String({ maxLength: 1024 }), {
7942
+ minItems: 1,
7943
+ maxItems: 32,
7944
+ description: "Files to rank co-change against"
7945
+ }),
7946
+ limit: Type.Optional(Type.Number({ minimum: 1, maximum: 200 })),
7947
+ maxCommits: Type.Optional(Type.Number({ minimum: 10, maximum: 2e4 }))
7948
+ }, { additionalProperties: false });
7949
+ var checked = (action, schema, args) => {
7950
+ const validation = validateJsonSchemaValue(schema.toJsonSchema(), args);
7951
+ if (!validation.success) {
7952
+ const message2 = validation.issues.slice(0, 5).map((issue) => issue.message).join("; ");
7953
+ throw new Error(`Invalid codemap.${action} arguments: ${message2}`);
7954
+ }
7955
+ return args;
7956
+ };
7957
+ var resolveRoot = (requested, cwd) => {
7958
+ if (requested === void 0) return cwd;
7959
+ const trimmed = requested.trim();
7960
+ if (trimmed.length === 0) {
7961
+ throw new Error("Invalid codemap path: expected a non-empty directory");
7962
+ }
7963
+ const resolved = path9.isAbsolute(trimmed) ? trimmed : path9.resolve(cwd, trimmed);
7964
+ let stats;
7965
+ try {
7966
+ stats = statSync2(resolved);
7967
+ } catch {
7968
+ throw new Error(`Invalid codemap path: ${resolved} does not exist`);
7969
+ }
7970
+ if (!stats.isDirectory()) {
7971
+ throw new Error(`Invalid codemap path: ${resolved} is not a directory`);
7972
+ }
7973
+ return resolved;
7974
+ };
7975
+ var descriptors = [
7976
+ {
7977
+ name: "map",
7978
+ description: "Repo-wide symbol map under an explicit token budget. Returns one line per symbol (line, kind, name) grouped by file, ranked by focus relevance and optional git co-change, with omission counts when the budget bites. Answers which files and symbols a task touches; use omp.grep for text and omp.read for source.",
7979
+ inputSchema: mapSchema.toJsonSchema(),
7980
+ risk: "read"
7981
+ },
7982
+ {
7983
+ name: "cascade",
7984
+ description: "Rank files by how often they historically changed in the same commits as the seed files, normalised so ubiquitous files (lockfiles, changelogs) do not dominate. Answers which files a change will drag along, which text search cannot.",
7985
+ inputSchema: cascadeSchema.toJsonSchema(),
7986
+ risk: "read"
7987
+ }
7988
+ ];
7989
+ var normalizeCodemapArgs = actionArgNormalizer(() => descriptors);
7990
+ var CodemapProvider = class {
7991
+ constructor(config) {
7992
+ this.config = config;
7993
+ }
7994
+ name = "codemap";
7995
+ description = "Structural repo map: budgeted symbol disclosure and git co-change ranking";
7996
+ #cache = /* @__PURE__ */ new Map();
7997
+ async list(request, _context) {
7998
+ const query = request.query?.toLowerCase();
7999
+ return query ? descriptors.filter(
8000
+ (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
8001
+ ) : descriptors;
8002
+ }
8003
+ async describe(actionName, _context) {
8004
+ return descriptors.find((descriptor) => descriptor.name === actionName);
8005
+ }
8006
+ prepareArguments(actionName, args) {
8007
+ return normalizeCodemapArgs(actionName, args);
8008
+ }
8009
+ async #index(root, glob, refresh, signal) {
8010
+ const key = `${root}\0${glob ?? ""}`;
8011
+ const cached = this.#cache.get(key);
8012
+ if (!refresh && cached && Date.now() - cached.at < INDEX_TTL_MS) return cached.index;
8013
+ const index = await buildSymbolIndex({
8014
+ root,
8015
+ ...glob !== void 0 ? { glob } : {},
8016
+ maxFiles: this.config.maxFiles,
8017
+ maxSymbols: this.config.maxSymbols,
8018
+ ...signal !== void 0 ? { signal } : {}
8019
+ });
8020
+ this.#cache.set(key, { index, at: Date.now() });
8021
+ return index;
8022
+ }
8023
+ async invoke(actionName, args, context) {
8024
+ switch (actionName) {
8025
+ case "map": {
8026
+ const input = checked("map", mapSchema, args);
8027
+ const target = resolveRoot(input.path, context.cwd);
8028
+ const index = await this.#index(target, input.glob, input.refresh === true, context.signal);
8029
+ let cascade;
8030
+ if (input.seeds && input.seeds.length > 0) {
8031
+ cascade = await coChange({
8032
+ root: target,
8033
+ seeds: input.seeds,
8034
+ maxCommits: this.config.cascadeCommits,
8035
+ limit: this.config.cascadeLimit
8036
+ });
8037
+ }
8038
+ const budgeted = assembleMap({
8039
+ index,
8040
+ maxTokens: input.maxTokens ?? this.config.defaultMaxTokens,
8041
+ ...input.focus !== void 0 ? { focus: input.focus } : {},
8042
+ ...cascade !== void 0 ? { cascade } : {}
8043
+ });
8044
+ context.activity?.({
8045
+ type: "progress",
8046
+ message: `Code map: ${budgeted.symbolsShown} symbols across ${budgeted.filesShown} files${budgeted.truncated ? ` (${budgeted.omittedSymbols} omitted)` : ""}`
8047
+ });
8048
+ return {
8049
+ ...budgeted,
8050
+ root: index.root,
8051
+ indexed: {
8052
+ files: index.files.length,
8053
+ symbols: index.symbols.length,
8054
+ languages: index.languages,
8055
+ fallbackFiles: index.fallbackFiles.length,
8056
+ truncated: index.truncated,
8057
+ elapsedMs: index.elapsedMs
8058
+ },
8059
+ ...cascade !== void 0 ? { cascade } : {}
8060
+ };
8061
+ }
8062
+ case "cascade": {
8063
+ const input = checked("cascade", cascadeSchema, args);
8064
+ const graph = await coChange({
8065
+ root: resolveRoot(input.path, context.cwd),
8066
+ seeds: input.seeds,
8067
+ maxCommits: input.maxCommits ?? this.config.cascadeCommits,
8068
+ limit: input.limit ?? this.config.cascadeLimit
8069
+ });
8070
+ context.activity?.({
8071
+ type: "progress",
8072
+ message: graph.unavailable ? `Co-change unavailable: ${graph.unavailable}` : `Co-change: ${graph.edges.length} related files from ${graph.commitsScanned} commits`
8073
+ });
8074
+ return graph;
8075
+ }
8076
+ default:
8077
+ throw new Error(`Unknown codemap action: ${actionName}`);
8078
+ }
8079
+ }
8080
+ };
8081
+
8082
+ // src/providers/compact-provider.ts
8083
+ import { Type as Type2 } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-typebox";
8084
+ import { validateJsonSchemaValue as validateJsonSchemaValue2 } from "@oh-my-pi/pi-ai/utils/schema";
8085
+ var requestSchema = Type2.Object({
8086
+ reason: Type2.Optional(Type2.String({
6770
8087
  maxLength: 1024,
6771
8088
  description: "Short human-readable reason for the compaction"
6772
8089
  })),
6773
- instructions: Type.Optional(Type.String({
8090
+ instructions: Type2.Optional(Type2.String({
6774
8091
  maxLength: MAX_COMPACTION_INSTRUCTIONS_CHARS,
6775
8092
  description: "Custom compaction instructions forwarded to OMP core"
6776
8093
  })),
6777
- preserve: Type.Optional(Type.Array(
6778
- Type.String({ maxLength: MAX_PRESERVE_ITEM_CHARS }),
8094
+ preserve: Type2.Optional(Type2.Array(
8095
+ Type2.String({ maxLength: MAX_PRESERVE_ITEM_CHARS }),
6779
8096
  {
6780
8097
  maxItems: MAX_PRESERVE_ITEMS,
6781
8098
  description: "Explicit bounded facts to preserve, encoded as a typed Fabric compaction request"
6782
8099
  }
6783
8100
  )),
6784
- requestedBy: Type.Optional(Type.String({
8101
+ requestedBy: Type2.Optional(Type2.String({
6785
8102
  maxLength: 256,
6786
8103
  description: "Who requested the compaction (default: model)"
6787
8104
  }))
6788
8105
  }, { additionalProperties: false });
6789
8106
  var checkedRequestArguments = (args) => {
6790
- const validation = validateJsonSchemaValue(requestSchema.toJsonSchema(), args);
8107
+ const validation = validateJsonSchemaValue2(requestSchema.toJsonSchema(), args);
6791
8108
  if (!validation.success) {
6792
8109
  const message2 = validation.issues.slice(0, 5).map((error) => error.message).join("; ");
6793
8110
  throw new Error(`Invalid compact.request arguments: ${message2}`);
@@ -6811,7 +8128,7 @@ var emptySchema = {
6811
8128
  properties: {},
6812
8129
  additionalProperties: false
6813
8130
  };
6814
- var descriptors = [
8131
+ var descriptors2 = [
6815
8132
  {
6816
8133
  name: "request",
6817
8134
  description: "Request an advisory compaction of the host session's context at the next safe boundary (agent_end). The host commits it only between turns, never mid-turn. A new request replaces any pending one.",
@@ -6834,7 +8151,7 @@ var descriptors = [
6834
8151
  risk: "write"
6835
8152
  }
6836
8153
  ];
6837
- var normalizeCompactArgs = actionArgNormalizer(() => descriptors);
8154
+ var normalizeCompactArgs = actionArgNormalizer(() => descriptors2);
6838
8155
  var CompactProvider = class {
6839
8156
  constructor(controller) {
6840
8157
  this.controller = controller;
@@ -6843,12 +8160,12 @@ var CompactProvider = class {
6843
8160
  description = "Programmatic, advisory-then-committed context compaction for the host OMP session";
6844
8161
  async list(request, _context) {
6845
8162
  const query = request.query?.toLowerCase();
6846
- return query ? descriptors.filter(
8163
+ return query ? descriptors2.filter(
6847
8164
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
6848
- ) : descriptors;
8165
+ ) : descriptors2;
6849
8166
  }
6850
8167
  async describe(actionName, _context) {
6851
- return descriptors.find((descriptor) => descriptor.name === actionName);
8168
+ return descriptors2.find((descriptor) => descriptor.name === actionName);
6852
8169
  }
6853
8170
  prepareArguments(actionName, args) {
6854
8171
  return normalizeCompactArgs(actionName, args);
@@ -6888,7 +8205,7 @@ var CompactProvider = class {
6888
8205
  };
6889
8206
 
6890
8207
  // src/providers/components-provider.ts
6891
- var descriptors2 = [
8208
+ var descriptors3 = [
6892
8209
  {
6893
8210
  name: "list",
6894
8211
  description: "List registered component definitions and configured component instances.",
@@ -6935,13 +8252,13 @@ var ComponentsProvider = class {
6935
8252
  description = "Supervised component lifecycle, exact capability dependencies, effect cleanup, and reload diagnostics.";
6936
8253
  async list(request, _context) {
6937
8254
  const query = request.query?.normalize("NFKC").trim().toLowerCase();
6938
- const filtered = query ? descriptors2.filter(
8255
+ const filtered = query ? descriptors3.filter(
6939
8256
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
6940
- ) : descriptors2;
8257
+ ) : descriptors3;
6941
8258
  return filtered.slice(0, Math.max(1, Math.min(request.limit ?? 100, 100)));
6942
8259
  }
6943
8260
  async describe(actionName, _context) {
6944
- return descriptors2.find((descriptor) => descriptor.name === actionName);
8261
+ return descriptors3.find((descriptor) => descriptor.name === actionName);
6945
8262
  }
6946
8263
  async invoke(actionName, args, _context) {
6947
8264
  switch (actionName) {
@@ -6976,7 +8293,7 @@ var ComponentsProvider = class {
6976
8293
  import fs5 from "node:fs/promises";
6977
8294
  import fsSync from "node:fs";
6978
8295
  import os from "node:os";
6979
- import path7 from "node:path";
8296
+ import path10 from "node:path";
6980
8297
  var MCP_DESCRIPTOR_CACHE_VERSION = 1;
6981
8298
  var homeDir = () => {
6982
8299
  const home = process.env.HOME;
@@ -6987,25 +8304,25 @@ var expandHome = (input) => {
6987
8304
  const home = homeDir();
6988
8305
  if (input === "~") return home;
6989
8306
  if (input.startsWith("~/") || input.startsWith("~\\")) {
6990
- return path7.join(home, input.slice(2));
8307
+ return path10.join(home, input.slice(2));
6991
8308
  }
6992
8309
  return input;
6993
8310
  };
6994
- var legacyMcporterDir = () => path7.join(homeDir(), ".mcporter");
8311
+ var legacyMcporterDir = () => path10.join(homeDir(), ".mcporter");
6995
8312
  var mcporterConfigDir = () => {
6996
8313
  const raw = process.env.XDG_CONFIG_HOME;
6997
8314
  if (raw && raw.trim().length > 0) {
6998
8315
  const resolved = expandHome(raw.trim());
6999
- if (path7.isAbsolute(resolved)) return path7.join(resolved, "mcporter");
8316
+ if (path10.isAbsolute(resolved)) return path10.join(resolved, "mcporter");
7000
8317
  }
7001
8318
  return legacyMcporterDir();
7002
8319
  };
7003
8320
  var mcporterConfigCandidates = () => {
7004
8321
  const base = mcporterConfigDir();
7005
- const candidates = [path7.join(base, "mcporter.json"), path7.join(base, "mcporter.jsonc")];
8322
+ const candidates = [path10.join(base, "mcporter.json"), path10.join(base, "mcporter.jsonc")];
7006
8323
  const legacy = legacyMcporterDir();
7007
8324
  if (base !== legacy) {
7008
- candidates.push(path7.join(legacy, "mcporter.json"), path7.join(legacy, "mcporter.jsonc"));
8325
+ candidates.push(path10.join(legacy, "mcporter.json"), path10.join(legacy, "mcporter.jsonc"));
7009
8326
  }
7010
8327
  return candidates;
7011
8328
  };
@@ -7020,12 +8337,12 @@ var pathExists = (filePath) => {
7020
8337
  var mcpConfigLayerPaths = (rootDir, configPath) => {
7021
8338
  const explicitRaw = configPath ?? process.env.MCPORTER_CONFIG;
7022
8339
  if (explicitRaw && explicitRaw.trim().length > 0) {
7023
- return [path7.resolve(expandHome(explicitRaw.trim()))];
8340
+ return [path10.resolve(expandHome(explicitRaw.trim()))];
7024
8341
  }
7025
8342
  const paths = [];
7026
8343
  const home = mcporterConfigCandidates().find(pathExists);
7027
8344
  if (home) paths.push(home);
7028
- const projectPath = path7.resolve(rootDir, "config", "mcporter.json");
8345
+ const projectPath = path10.resolve(rootDir, "config", "mcporter.json");
7029
8346
  if (pathExists(projectPath)) paths.push(projectPath);
7030
8347
  return paths;
7031
8348
  };
@@ -7076,8 +8393,8 @@ var parseCacheFile = (value) => {
7076
8393
  servers: value.servers
7077
8394
  };
7078
8395
  };
7079
- var projectMcpCachePath = (projectRoot) => path7.join(projectRoot, ".omp", "fabric", "mcp-cache.json");
7080
- var globalMcpCachePath = (agentDir) => path7.join(agentDir, "fabric", "mcp-descriptors.json");
8396
+ var projectMcpCachePath = (projectRoot) => path10.join(projectRoot, ".omp", "fabric", "mcp-cache.json");
8397
+ var globalMcpCachePath = (agentDir) => path10.join(agentDir, "fabric", "mcp-descriptors.json");
7081
8398
  var tempCounter = 0;
7082
8399
  var McpDescriptorCacheStore = class {
7083
8400
  constructor(filePath) {
@@ -7094,9 +8411,9 @@ var McpDescriptorCacheStore = class {
7094
8411
  // Atomic-ish write: temp file then rename, so a concurrent reader either
7095
8412
  // sees the previous cache or the next one, never a torn document.
7096
8413
  async save(file) {
7097
- const directory = path7.dirname(this.filePath);
8414
+ const directory = path10.dirname(this.filePath);
7098
8415
  await fs5.mkdir(directory, { recursive: true });
7099
- const tempPath = path7.join(
8416
+ const tempPath = path10.join(
7100
8417
  directory,
7101
8418
  `.mcp-cache-${process.pid}-${Date.now()}-${tempCounter++}.tmp`
7102
8419
  );
@@ -7111,7 +8428,7 @@ var McpDescriptorCacheStore = class {
7111
8428
  };
7112
8429
 
7113
8430
  // src/providers/mcp-provider.ts
7114
- import path8 from "node:path";
8431
+ import path11 from "node:path";
7115
8432
  var TOOL_METADATA_TTL_MS = 6e4;
7116
8433
  var REVALIDATE_CONCURRENCY = 3;
7117
8434
  var REVALIDATE_SERVER_TIMEOUT_MS = 2e4;
@@ -7119,6 +8436,7 @@ var MIN_REVALIDATE_SERVER_TIMEOUT_MS = 5e3;
7119
8436
  var NOTIFY_DEBOUNCE_MS = 100;
7120
8437
  var PERSIST_DEBOUNCE_MS = 150;
7121
8438
  var COLD_LIST_WAIT_MS = 2500;
8439
+ var MAX_CALL_TIMEOUT_MS = 9e5;
7122
8440
  var emptyObjectSchema = {
7123
8441
  type: "object",
7124
8442
  properties: {},
@@ -7169,7 +8487,12 @@ var managementDescriptors = [
7169
8487
  properties: {
7170
8488
  server: { type: "string" },
7171
8489
  tool: { type: "string" },
7172
- args: { type: "object", additionalProperties: true }
8490
+ args: { type: "object", additionalProperties: true },
8491
+ timeoutMs: {
8492
+ type: "number",
8493
+ minimum: 1,
8494
+ description: `Per-call timeout overriding mcp.callTimeoutMs, clamped to ${MAX_CALL_TIMEOUT_MS}ms.`
8495
+ }
7173
8496
  },
7174
8497
  required: ["server", "tool"],
7175
8498
  additionalProperties: false
@@ -7178,19 +8501,20 @@ var managementDescriptors = [
7178
8501
  namespace: "management"
7179
8502
  }
7180
8503
  ];
8504
+ var resolveMcpCallTimeoutMs = (value, fallback) => typeof value === "number" && Number.isFinite(value) && value >= 1 ? Math.min(Math.floor(value), MAX_CALL_TIMEOUT_MS) : fallback;
7181
8505
  var normalizeSchema = (schema) => typeof schema === "object" && schema !== null && !Array.isArray(schema) ? schema : emptyObjectSchema;
7182
8506
  var normalizeMcpResult = (result) => {
7183
8507
  if (typeof result !== "object" || result === null || Array.isArray(result)) return result;
7184
- const record = result;
7185
- if (!Array.isArray(record.content)) return result;
7186
- const text = record.content.filter(
8508
+ const record2 = result;
8509
+ if (!Array.isArray(record2.content)) return result;
8510
+ const text = record2.content.filter(
7187
8511
  (part) => typeof part === "object" && part !== null && part.type === "text" && typeof part.text === "string"
7188
8512
  ).map((part) => part.text).join("\n");
7189
- if (record.isError === true) throw new Error(text || "MCP tool returned an error");
8513
+ if (record2.isError === true) throw new Error(text || "MCP tool returned an error");
7190
8514
  return {
7191
8515
  text,
7192
- content: record.content,
7193
- structuredContent: record.structuredContent ?? null
8516
+ content: record2.content,
8517
+ structuredContent: record2.structuredContent ?? null
7194
8518
  };
7195
8519
  };
7196
8520
  var delay2 = (ms, signal) => new Promise((resolve2) => {
@@ -7265,9 +8589,9 @@ var McpProvider = class {
7265
8589
  this.#kickRevalidation();
7266
8590
  if (!request.namespace) await this.#awaitColdEnumeration(context.signal);
7267
8591
  const query = request.query?.toLowerCase();
7268
- const filterQuery = (descriptors6) => query ? descriptors6.filter(
8592
+ const filterQuery = (descriptors7) => query ? descriptors7.filter(
7269
8593
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
7270
- ) : descriptors6;
8594
+ ) : descriptors7;
7271
8595
  if (request.namespace) {
7272
8596
  const server = await this.#resolveKnownServer(request.namespace);
7273
8597
  if (!server) return [];
@@ -7365,7 +8689,13 @@ var McpProvider = class {
7365
8689
  const server = String(args.server);
7366
8690
  const tool = String(args.tool);
7367
8691
  const toolArgs = typeof args.args === "object" && args.args !== null && !Array.isArray(args.args) ? args.args : {};
7368
- return this.#call(server, tool, toolArgs, context.signal);
8692
+ return this.#call(
8693
+ server,
8694
+ tool,
8695
+ toolArgs,
8696
+ context.signal,
8697
+ resolveMcpCallTimeoutMs(args.timeoutMs, this.config.callTimeoutMs)
8698
+ );
7369
8699
  }
7370
8700
  const parsed = this.#parseToolName(actionName);
7371
8701
  if (!parsed) throw new Error(`Invalid MCP action: ${actionName}`);
@@ -7416,11 +8746,11 @@ var McpProvider = class {
7416
8746
  // Provider-fidelity descriptors for everything currently known, cached or
7417
8747
  // ephemeral.
7418
8748
  sliceDescriptors() {
7419
- const descriptors6 = [];
8749
+ const descriptors7 = [];
7420
8750
  for (const [server, entry] of this.#servers) {
7421
- for (const tool of entry.tools) descriptors6.push(this.#toolDescriptor(server, tool));
8751
+ for (const tool of entry.tools) descriptors7.push(this.#toolDescriptor(server, tool));
7422
8752
  }
7423
- return descriptors6;
8753
+ return descriptors7;
7424
8754
  }
7425
8755
  // Test/ops hook: await hydration and any in-flight background revalidation,
7426
8756
  // then flush pending persistence and notifications.
@@ -7438,8 +8768,8 @@ var McpProvider = class {
7438
8768
  }
7439
8769
  if (this.#dirtyPersist) await this.#persistNow().catch(() => void 0);
7440
8770
  }
7441
- async #call(serverName, toolName, args, signal) {
7442
- if (!this.#cacheOn) return this.#callLegacy(serverName, toolName, args, signal);
8771
+ async #call(serverName, toolName, args, signal, timeoutMs) {
8772
+ if (!this.#cacheOn) return this.#callLegacy(serverName, toolName, args, signal, timeoutMs);
7443
8773
  if (signal?.aborted) throw new Error("MCP call cancelled");
7444
8774
  await this.#hydrate();
7445
8775
  const server = await this.#resolveKnownServer(serverName);
@@ -7457,7 +8787,7 @@ var McpProvider = class {
7457
8787
  if (firstContact) this.#recontacted.add(server);
7458
8788
  const operation = runtime.callTool(server, tool.name, {
7459
8789
  args,
7460
- timeoutMs: this.config.callTimeoutMs,
8790
+ timeoutMs: timeoutMs ?? this.config.callTimeoutMs,
7461
8791
  disableOAuth: this.config.disableOAuth
7462
8792
  });
7463
8793
  try {
@@ -7822,7 +9152,7 @@ var McpProvider = class {
7822
9152
  kind: "stdio",
7823
9153
  command: args.command,
7824
9154
  args: commandArgs,
7825
- cwd: path8.resolve(this.cwd, typeof args.cwd === "string" ? args.cwd : ".")
9155
+ cwd: path11.resolve(this.cwd, typeof args.cwd === "string" ? args.cwd : ".")
7826
9156
  },
7827
9157
  ...env ? { env } : {}
7828
9158
  };
@@ -7887,13 +9217,13 @@ var McpProvider = class {
7887
9217
  return tools.map((tool) => this.#toolDescriptor(server, tool));
7888
9218
  })
7889
9219
  );
7890
- const descriptors6 = settled.flatMap(
9220
+ const descriptors7 = settled.flatMap(
7891
9221
  (entry) => entry.status === "fulfilled" ? entry.value : []
7892
9222
  );
7893
9223
  const query = request.query?.toLowerCase();
7894
- const filtered = query ? descriptors6.filter(
9224
+ const filtered = query ? descriptors7.filter(
7895
9225
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
7896
- ) : descriptors6;
9226
+ ) : descriptors7;
7897
9227
  return request.namespace ? filtered : [...managementDescriptors, ...filtered];
7898
9228
  }
7899
9229
  async #describeLegacy(actionName, _context) {
@@ -7905,7 +9235,7 @@ var McpProvider = class {
7905
9235
  const tool = await this.#findToolLegacy(runtime, server, parsed.tool);
7906
9236
  return tool ? this.#toolDescriptor(server, tool) : void 0;
7907
9237
  }
7908
- async #callLegacy(serverName, toolName, args, signal) {
9238
+ async #callLegacy(serverName, toolName, args, signal, timeoutMs) {
7909
9239
  if (signal?.aborted) throw new Error("MCP call cancelled");
7910
9240
  const runtime = await this.#getRuntime();
7911
9241
  const server = this.#resolveServerName(runtime, serverName);
@@ -7915,7 +9245,7 @@ var McpProvider = class {
7915
9245
  if (!tool) throw new Error(`Unknown MCP tool: ${serverName}.${toolName}`);
7916
9246
  const operation = runtime.callTool(server, tool.name, {
7917
9247
  args,
7918
- timeoutMs: this.config.callTimeoutMs,
9248
+ timeoutMs: timeoutMs ?? this.config.callTimeoutMs,
7919
9249
  disableOAuth: this.config.disableOAuth
7920
9250
  });
7921
9251
  try {
@@ -7965,7 +9295,7 @@ var assertReadableStateKey = (key) => {
7965
9295
  throw new Error(`Fabric mesh key is private host state: ${key}`);
7966
9296
  }
7967
9297
  };
7968
- var descriptors3 = [
9298
+ var descriptors4 = [
7969
9299
  {
7970
9300
  name: "self",
7971
9301
  description: "Return this Fabric participant's mesh identity",
@@ -8019,7 +9349,11 @@ var descriptors3 = [
8019
9349
  items: { type: "string", enum: ["root", "agent", "actor"] }
8020
9350
  },
8021
9351
  includeStale: { type: "boolean" },
8022
- limit: { type: "number", minimum: 1 }
9352
+ limit: { type: "number", minimum: 1 },
9353
+ withTotal: {
9354
+ type: "boolean",
9355
+ description: "Return { members, total, truncated } instead of a bare array, so a clipped page is distinguishable from a complete one."
9356
+ }
8023
9357
  },
8024
9358
  additionalProperties: false
8025
9359
  },
@@ -8045,7 +9379,11 @@ var descriptors3 = [
8045
9379
  type: "object",
8046
9380
  properties: {
8047
9381
  prefix: { type: "string" },
8048
- limit: { type: "number", minimum: 1 }
9382
+ limit: { type: "number", minimum: 1 },
9383
+ withTotal: {
9384
+ type: "boolean",
9385
+ description: "Return { entries, total, truncated } instead of a bare array, so a clipped page is distinguishable from a complete one."
9386
+ }
8049
9387
  },
8050
9388
  additionalProperties: false
8051
9389
  },
@@ -8084,7 +9422,7 @@ var descriptors3 = [
8084
9422
  namespace: "coordination"
8085
9423
  }
8086
9424
  ];
8087
- var normalizeMeshArgs = actionArgNormalizer(() => descriptors3);
9425
+ var normalizeMeshArgs = actionArgNormalizer(() => descriptors4);
8088
9426
  var MeshProvider = class {
8089
9427
  constructor(store, identity, participants) {
8090
9428
  this.store = store;
@@ -8095,12 +9433,12 @@ var MeshProvider = class {
8095
9433
  description = "Durable topics and compare-and-swap shared state for emergent agent coordination";
8096
9434
  async list(request, _context) {
8097
9435
  const query = request.query?.toLowerCase();
8098
- return query ? descriptors3.filter(
9436
+ return query ? descriptors4.filter(
8099
9437
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
8100
- ) : descriptors3;
9438
+ ) : descriptors4;
8101
9439
  }
8102
9440
  async describe(actionName, _context) {
8103
- return descriptors3.find((descriptor) => descriptor.name === actionName);
9441
+ return descriptors4.find((descriptor) => descriptor.name === actionName);
8104
9442
  }
8105
9443
  prepareArguments(actionName, args) {
8106
9444
  return normalizeMeshArgs(actionName, args);
@@ -8136,11 +9474,13 @@ var MeshProvider = class {
8136
9474
  ) : void 0;
8137
9475
  const scope = args.scope === "local" || args.scope === "lineage" || args.scope === "project" ? args.scope : "project";
8138
9476
  const limit = Math.max(1, Math.floor(typeof args.limit === "number" ? args.limit : 100));
8139
- return this.participants.list({
9477
+ const matched = this.participants.list({
8140
9478
  scope,
8141
9479
  ...kinds ? { kinds } : {},
8142
9480
  ...args.includeStale === true ? { includeStale: true } : {}
8143
- }).slice(0, limit);
9481
+ });
9482
+ const members = matched.slice(0, limit);
9483
+ return args.withTotal === true ? { members, total: matched.length, truncated: matched.length > members.length } : members;
8144
9484
  }
8145
9485
  case "get": {
8146
9486
  const key = String(args.key);
@@ -8157,11 +9497,13 @@ var MeshProvider = class {
8157
9497
  this.store.maxReadEvents
8158
9498
  )
8159
9499
  );
8160
- return this.store.listAll(prefix).filter(
9500
+ const matched = this.store.listAll(prefix).filter(
8161
9501
  (entry) => !PRIVATE_STATE_PREFIXES.some(
8162
9502
  (privatePrefix) => entry.key.startsWith(privatePrefix)
8163
9503
  )
8164
- ).slice(0, limit);
9504
+ );
9505
+ const entries = matched.slice(0, limit);
9506
+ return args.withTotal === true ? { entries, total: matched.length, truncated: matched.length > entries.length } : entries;
8165
9507
  }
8166
9508
  case "put": {
8167
9509
  const key = String(args.key);
@@ -8188,13 +9530,14 @@ var MeshProvider = class {
8188
9530
  };
8189
9531
 
8190
9532
  // src/providers/omp-tools-provider.ts
8191
- import { mkdirSync as mkdirSync2, mkdtempSync, readFileSync as readFileSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
9533
+ import { mkdirSync as mkdirSync2, mkdtempSync, readFileSync as readFileSync2, statSync as statSync4, writeFileSync as writeFileSync2 } from "node:fs";
8192
9534
  import { AsyncLocalStorage } from "node:async_hooks";
8193
9535
  import os2 from "node:os";
8194
- import path11 from "node:path";
9536
+ import path14 from "node:path";
8195
9537
  import { randomUUID as randomUUID3 } from "node:crypto";
8196
9538
  import { BashTool } from "@oh-my-pi/pi-coding-agent/tools/bash";
8197
9539
  import { EditTool } from "@oh-my-pi/pi-coding-agent/edit";
9540
+ import { GlobTool } from "@oh-my-pi/pi-coding-agent/tools/glob";
8198
9541
  import { GrepTool, MULTI_FILE_PER_FILE_MATCHES, SINGLE_FILE_MATCHES } from "@oh-my-pi/pi-coding-agent/tools/grep";
8199
9542
  import { ReadTool } from "@oh-my-pi/pi-coding-agent/tools/read";
8200
9543
  import { piEscapeRegexLiteral, piJoinPath } from "@oh-my-pi/pi-ai/providers/cursor-pi-args";
@@ -8207,7 +9550,7 @@ import {
8207
9550
 
8208
9551
  // src/agents/bash-worktree-add.ts
8209
9552
  import fs6 from "node:fs";
8210
- import path9 from "node:path";
9553
+ import path12 from "node:path";
8211
9554
  var BARE_SAFE = /^[A-Za-z0-9_./:@%+=,-]+$/;
8212
9555
  var parseGitWorktreeAdd = (command) => {
8213
9556
  const trimmed = command.trim();
@@ -8270,8 +9613,8 @@ var tryExecuteGitWorktreeAdd = async (args, sessionCwd) => {
8270
9613
  if (typeof command !== "string") return void 0;
8271
9614
  const parsed = parseGitWorktreeAdd(command);
8272
9615
  if (!parsed) return void 0;
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);
9616
+ const gitCwd = parsed.gitCwd ? path12.resolve(sessionCwd, parsed.gitCwd) : typeof args.cwd === "string" ? args.cwd : sessionCwd;
9617
+ const dest = path12.resolve(gitCwd, parsed.dest);
8275
9618
  let gitRoot;
8276
9619
  try {
8277
9620
  const root = await executeFile("git", ["rev-parse", "--show-toplevel"], {
@@ -8299,10 +9642,10 @@ var tryExecuteGitWorktreeAdd = async (args, sessionCwd) => {
8299
9642
  };
8300
9643
 
8301
9644
  // src/providers/omp-bash-cwd.ts
8302
- import { accessSync, constants, statSync as statSync2 } from "node:fs";
8303
- import path10 from "node:path";
9645
+ import { accessSync, constants, statSync as statSync3 } from "node:fs";
9646
+ import path13 from "node:path";
8304
9647
  import { createBashToolDefinition } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-pi-coding-agent-shim";
8305
- import { Type as Type2 } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-typebox";
9648
+ import { Type as Type3 } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-typebox";
8306
9649
  var OMP_BASH_CWD_KEY = "cwd";
8307
9650
  var resolveOmpShellCwd = (toolName, sessionCwd, requested) => {
8308
9651
  if (typeof requested !== "string" || requested.trim().length === 0) {
@@ -8310,9 +9653,9 @@ var resolveOmpShellCwd = (toolName, sessionCwd, requested) => {
8310
9653
  `Invalid omp.${toolName} cwd ${JSON.stringify(requested)}: path must be a non-empty string`
8311
9654
  );
8312
9655
  }
8313
- const resolved = path10.isAbsolute(requested) ? path10.resolve(requested) : path10.resolve(sessionCwd, requested);
9656
+ const resolved = path13.isAbsolute(requested) ? path13.resolve(requested) : path13.resolve(sessionCwd, requested);
8314
9657
  try {
8315
- if (!statSync2(resolved).isDirectory()) throw new Error("path is not a directory");
9658
+ if (!statSync3(resolved).isDirectory()) throw new Error("path is not a directory");
8316
9659
  accessSync(resolved, constants.R_OK | constants.X_OK);
8317
9660
  } catch (error) {
8318
9661
  const reason = error instanceof Error ? error.message : String(error);
@@ -8330,8 +9673,8 @@ var resolveShellCwdArgument = (toolName, sessionCwd, args) => Object.hasOwn(args
8330
9673
  args[OMP_BASH_CWD_KEY]
8331
9674
  )
8332
9675
  } : args;
8333
- var CWD_PROPERTY = Type2.Optional(
8334
- Type2.String({
9676
+ var CWD_PROPERTY = Type3.Optional(
9677
+ Type3.String({
8335
9678
  description: "Execution directory for this command; relative paths resolve from the session cwd."
8336
9679
  })
8337
9680
  );
@@ -8350,7 +9693,7 @@ var withShellCwdSchema = (schema) => {
8350
9693
  if (Object.hasOwn(document.properties, OMP_BASH_CWD_KEY)) return schema;
8351
9694
  const cwdSchema = schemaDocument2(CWD_PROPERTY);
8352
9695
  if (!cwdSchema) return schema;
8353
- return Type2.Unsafe({
9696
+ return Type3.Unsafe({
8354
9697
  ...document,
8355
9698
  properties: { ...document.properties, [OMP_BASH_CWD_KEY]: cwdSchema }
8356
9699
  });
@@ -8385,28 +9728,49 @@ var BashCwdDefinitions = class extends ShellCwdDefinitions {
8385
9728
 
8386
9729
  // src/providers/write-preview.ts
8387
9730
  import { homedir } from "node:os";
8388
- import { dirname, isAbsolute, resolve } from "node:path";
8389
- import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
8390
- import { Type as Type3 } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-typebox";
9731
+ import { dirname, isAbsolute, resolve, win32 } from "node:path";
9732
+ import { mkdir, readFile as readFile2, stat, writeFile } from "node:fs/promises";
9733
+ import { Type as Type4 } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-typebox";
8391
9734
  var mutationQueues = /* @__PURE__ */ new Map();
8392
- var withFileMutationQueue = async (path17, operation) => {
8393
- const previous = mutationQueues.get(path17) ?? Promise.resolve();
9735
+ var withFileMutationQueue = async (path20, operation) => {
9736
+ const previous = mutationQueues.get(path20) ?? Promise.resolve();
8394
9737
  let release;
8395
9738
  const current = new Promise((resolve2) => {
8396
9739
  release = resolve2;
8397
9740
  });
8398
- mutationQueues.set(path17, current);
9741
+ mutationQueues.set(path20, current);
8399
9742
  await previous;
8400
9743
  try {
8401
9744
  return await operation();
8402
9745
  } finally {
8403
9746
  release();
8404
- if (mutationQueues.get(path17) === current) mutationQueues.delete(path17);
9747
+ if (mutationQueues.get(path20) === current) mutationQueues.delete(path20);
8405
9748
  }
8406
9749
  };
9750
+ var URI_LIKE_WRITE_TARGET_RE = /^([a-z][a-z0-9+.-]*):\/{1,2}/i;
9751
+ var OmpWriteUriTargetError = class extends Error {
9752
+ path;
9753
+ constructor(message2, path20) {
9754
+ super(message2);
9755
+ this.name = "OmpWriteUriTargetError";
9756
+ this.path = path20;
9757
+ }
9758
+ };
9759
+ var assertFilesystemWriteTarget = (candidate, reported) => {
9760
+ const trimmed = candidate.trim();
9761
+ if (win32.isAbsolute(trimmed)) return;
9762
+ const scheme = URI_LIKE_WRITE_TARGET_RE.exec(trimmed)?.[1]?.toLowerCase();
9763
+ if (scheme === void 0) return;
9764
+ const guidance = scheme === "xd" ? "Tool devices are dispatched by the top-level `write` tool, which carries the xd:// transport; omp.write writes files." : "omp.write resolves filesystem paths only.";
9765
+ throw new OmpWriteUriTargetError(
9766
+ `Refusing to write '${reported}': '${scheme}://' is a URI scheme, not a directory. ${guidance} Prefix the path with './' to create a literal file by that name.`,
9767
+ reported
9768
+ );
9769
+ };
8407
9770
  var resolvePreviewPath = (filePath, cwd) => {
8408
9771
  let expanded = filePath.startsWith("@") ? filePath.slice(1) : filePath;
8409
9772
  expanded = expanded.replace(/[\u00a0\u2000-\u200a\u202f\u205f\u3000]/g, " ");
9773
+ assertFilesystemWriteTarget(expanded, filePath);
8410
9774
  if (expanded === "~") expanded = homedir();
8411
9775
  else if (expanded.startsWith("~/")) expanded = homedir() + expanded.slice(1);
8412
9776
  return isAbsolute(expanded) ? expanded : resolve(cwd, expanded);
@@ -8436,7 +9800,7 @@ var readExistingFileForPreview = async (filePath, cwd, nextContent) => {
8436
9800
  return skipped("previous file too large", fileStat.size, true);
8437
9801
  }
8438
9802
  try {
8439
- const content = await readFile(absolutePath, "utf8");
9803
+ const content = await readFile2(absolutePath, "utf8");
8440
9804
  const bytes = Buffer.byteLength(content, "utf8");
8441
9805
  return bytes > MAX_WRITE_DIFF_BYTES ? skipped("previous file too large", bytes, true) : { kind: "content", content };
8442
9806
  } catch {
@@ -8448,20 +9812,20 @@ var createPreviewWriteToolDefinition = (cwd) => {
8448
9812
  name: "write",
8449
9813
  label: "Write",
8450
9814
  description: "Write a file",
8451
- parameters: Type3.Object({ path: Type3.String(), content: Type3.String() }),
9815
+ parameters: Type4.Object({ path: Type4.String(), content: Type4.String() }),
8452
9816
  execute: async () => ({ content: [{ type: "text", text: "" }] })
8453
9817
  };
8454
9818
  return {
8455
9819
  ...original,
8456
9820
  async execute(_toolCallId, params, signal) {
8457
- const { path: path17, content } = params;
8458
- const absolutePath = resolvePreviewPath(path17, cwd);
9821
+ const { path: path20, content } = params;
9822
+ const absolutePath = resolvePreviewPath(path20, cwd);
8459
9823
  return withFileMutationQueue(absolutePath, async () => {
8460
9824
  const throwIfAborted2 = () => {
8461
9825
  if (signal?.aborted) throw new Error("Operation aborted");
8462
9826
  };
8463
9827
  throwIfAborted2();
8464
- const before = await readExistingFileForPreview(path17, cwd, content);
9828
+ const before = await readExistingFileForPreview(path20, cwd, content);
8465
9829
  throwIfAborted2();
8466
9830
  await mkdir(dirname(absolutePath), { recursive: true });
8467
9831
  throwIfAborted2();
@@ -8471,7 +9835,7 @@ var createPreviewWriteToolDefinition = (cwd) => {
8471
9835
  content: [
8472
9836
  {
8473
9837
  type: "text",
8474
- text: `Successfully wrote ${Buffer.byteLength(content, "utf8")} bytes to ${path17}`
9838
+ text: `Successfully wrote ${Buffer.byteLength(content, "utf8")} bytes to ${path20}`
8475
9839
  }
8476
9840
  ],
8477
9841
  details: { codePreviewBeforeWrite: before }
@@ -8486,7 +9850,7 @@ var MAX_RENDERER_ARGUMENT_CHARS = 2e5;
8486
9850
  var GUEST_READ_LINE_LIMIT = 1e5;
8487
9851
  var MAX_REPLACE_ALL_FILE_CHARS = 2e6;
8488
9852
  var createNativeSession = (cwd, artifactPaths) => {
8489
- const artifactRoot = artifactPaths ? mkdtempSync(path11.join(os2.tmpdir(), "omp-fabric-bash-")) : void 0;
9853
+ const artifactRoot = artifactPaths ? mkdtempSync(path14.join(os2.tmpdir(), "omp-fabric-bash-")) : void 0;
8490
9854
  return {
8491
9855
  cwd,
8492
9856
  hasUI: false,
@@ -8503,7 +9867,7 @@ var createNativeSession = (cwd, artifactPaths) => {
8503
9867
  try {
8504
9868
  mkdirSync2(artifactRoot, { recursive: true, mode: 448 });
8505
9869
  const id = randomUUID3();
8506
- const artifactPath = path11.join(artifactRoot, `${toolType}-${id}.log`);
9870
+ const artifactPath = path14.join(artifactRoot, `${toolType}-${id}.log`);
8507
9871
  writeFileSync2(artifactPath, "", { mode: 384 });
8508
9872
  artifactPaths.set(id, artifactPath);
8509
9873
  artifactAllocations.getStore()?.push(artifactPath);
@@ -8544,12 +9908,28 @@ var GREP_SKIP_PROPERTY = {
8544
9908
  type: "number",
8545
9909
  description: "Files to skip before collecting results \u2014 paginate when a search hit the file limit"
8546
9910
  };
8547
- var withGrepSkipSchema = (document) => {
8548
- const schema = asRecord(document);
9911
+ var GITIGNORE_PROPERTY = {
9912
+ type: "boolean",
9913
+ description: "Respect gitignore rules; pass false to search ignored files such as build output or .env"
9914
+ };
9915
+ var HIDDEN_PROPERTY = {
9916
+ type: "boolean",
9917
+ description: "Include dotfiles and hidden directories"
9918
+ };
9919
+ var withExtraProperties = (document, extras) => {
9920
+ const source = typeof document === "function" && "toJsonSchema" in document ? document.toJsonSchema() : document;
9921
+ const schema = asRecord(source);
8549
9922
  const properties = asRecord(schema?.properties);
8550
- if (!schema || !properties || Object.hasOwn(properties, "skip")) return document;
8551
- return { ...schema, properties: { ...properties, skip: GREP_SKIP_PROPERTY } };
9923
+ if (!schema || !properties) return document;
9924
+ const additions = {};
9925
+ for (const [name, property] of Object.entries(extras)) {
9926
+ if (!Object.hasOwn(properties, name)) additions[name] = property;
9927
+ }
9928
+ if (Object.keys(additions).length === 0) return document;
9929
+ return { ...schema, properties: { ...properties, ...additions } };
8552
9930
  };
9931
+ var withGrepSkipSchema = (document) => withExtraProperties(document, { skip: GREP_SKIP_PROPERTY, gitignore: GITIGNORE_PROPERTY });
9932
+ var withFindFilterSchema = (document) => withExtraProperties(document, { gitignore: GITIGNORE_PROPERTY, hidden: HIDDEN_PROPERTY });
8553
9933
  var grepSession = (cwd, context) => ({
8554
9934
  cwd,
8555
9935
  hasUI: false,
@@ -8566,20 +9946,50 @@ var createGrepDefinitionWithSkip = (cwd) => {
8566
9946
  ...legacy,
8567
9947
  parameters: withGrepSkipSchema(legacy.parameters),
8568
9948
  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;
9949
+ const record2 = asRecord(params) ?? {};
9950
+ const rawPattern = typeof record2.pattern === "string" ? record2.pattern : "";
9951
+ const searchPath = typeof record2.path === "string" ? record2.path : ".";
9952
+ const glob = typeof record2.glob === "string" ? record2.glob : void 0;
9953
+ const contextLines = typeof record2.context === "number" ? record2.context : void 0;
9954
+ const skip = typeof record2.skip === "number" ? record2.skip : void 0;
8575
9955
  const tool = contextLines === void 0 ? defaultTool : new GrepTool(grepSession(cwd, contextLines));
8576
9956
  return tool.execute(
8577
9957
  toolCallId,
8578
9958
  {
8579
- pattern: record.literal === true ? piEscapeRegexLiteral(rawPattern) : rawPattern,
9959
+ pattern: record2.literal === true ? piEscapeRegexLiteral(rawPattern) : rawPattern,
8580
9960
  path: glob ? piJoinPath(searchPath, glob) : searchPath,
8581
- case: record.ignoreCase === true ? false : void 0,
8582
- ...skip === void 0 ? {} : { skip }
9961
+ case: record2.ignoreCase === true ? false : void 0,
9962
+ ...skip === void 0 ? {} : { skip },
9963
+ ...typeof record2.gitignore === "boolean" ? { gitignore: record2.gitignore } : {}
9964
+ },
9965
+ signal,
9966
+ onUpdate,
9967
+ context
9968
+ );
9969
+ }
9970
+ };
9971
+ };
9972
+ var createFindDefinitionWithFilters = (cwd) => {
9973
+ const legacy = createFindToolDefinition(cwd);
9974
+ const tool = new GlobTool(grepSession(cwd));
9975
+ return {
9976
+ ...legacy,
9977
+ parameters: withFindFilterSchema(legacy.parameters),
9978
+ execute: (toolCallId, params, signal, onUpdate, context) => {
9979
+ const record2 = asRecord(params) ?? {};
9980
+ if (typeof record2.gitignore !== "boolean" && typeof record2.hidden !== "boolean") {
9981
+ return legacy.execute(toolCallId, params, signal, onUpdate, context);
9982
+ }
9983
+ const pattern = typeof record2.pattern === "string" ? record2.pattern : void 0;
9984
+ const searchPath = typeof record2.path === "string" ? record2.path : void 0;
9985
+ const limit = typeof record2.limit === "number" ? record2.limit : void 0;
9986
+ return tool.execute(
9987
+ toolCallId,
9988
+ {
9989
+ path: piJoinPath(searchPath ?? ".", pattern ?? "*"),
9990
+ ...limit === void 0 ? {} : { limit },
9991
+ ...typeof record2.gitignore === "boolean" ? { gitignore: record2.gitignore } : {},
9992
+ ...typeof record2.hidden === "boolean" ? { hidden: record2.hidden } : {}
8583
9993
  },
8584
9994
  signal,
8585
9995
  onUpdate,
@@ -8598,19 +10008,19 @@ var normalizeEditArguments = (args, replaceAll, cwd) => {
8598
10008
  if (typeof edit !== "object" || edit === null || Array.isArray(edit)) {
8599
10009
  throw new Error(`omp.edit edits[${index}] must be an object`);
8600
10010
  }
8601
- const record = edit;
8602
- const oldString = record.old_string ?? record.oldText ?? record.old;
8603
- const newString = record.new_string ?? record.newText ?? record.new ?? record.replacement;
10011
+ const record2 = edit;
10012
+ const oldString = record2.old_string ?? record2.oldText ?? record2.old;
10013
+ const newString = record2.new_string ?? record2.newText ?? record2.new ?? record2.replacement;
8604
10014
  if (typeof oldString !== "string" || typeof newString !== "string") {
8605
10015
  throw new Error(`omp.edit edits[${index}] requires old and new text strings`);
8606
10016
  }
8607
10017
  return {
8608
10018
  old_string: oldString,
8609
10019
  new_string: newString,
8610
- ...replaceAll || record.all === true || record.replace_all === true ? { replace_all: true } : {}
10020
+ ...replaceAll || record2.all === true || record2.replace_all === true ? { replace_all: true } : {}
8611
10021
  };
8612
10022
  });
8613
- const resolvedPath = path11.resolve(cwd, filePath.startsWith("@") ? filePath.slice(1) : filePath);
10023
+ const resolvedPath = path14.resolve(cwd, filePath.startsWith("@") ? filePath.slice(1) : filePath);
8614
10024
  const current = readFileSync2(resolvedPath, "utf8");
8615
10025
  if (current.length > MAX_REPLACE_ALL_FILE_CHARS) {
8616
10026
  throw new Error(`omp.edit refuses files over ${MAX_REPLACE_ALL_FILE_CHARS} characters`);
@@ -8722,7 +10132,7 @@ var keepLeadingLines = (text, count) => {
8722
10132
  }
8723
10133
  return text.slice(0, index - 1);
8724
10134
  };
8725
- var countLines = (text) => {
10135
+ var countLines2 = (text) => {
8726
10136
  if (text.length === 0) return 0;
8727
10137
  let lines = 1;
8728
10138
  for (let index = text.indexOf("\n"); index !== -1; index = text.indexOf("\n", index + 1)) lines++;
@@ -8764,7 +10174,7 @@ var restoreBashOutput = (artifactPath, maxChars, delivered) => {
8764
10174
  if (!artifactPath || maxChars <= 0) return void 0;
8765
10175
  let size;
8766
10176
  try {
8767
- size = statSync3(artifactPath).size;
10177
+ size = statSync4(artifactPath).size;
8768
10178
  } catch {
8769
10179
  return void 0;
8770
10180
  }
@@ -8791,11 +10201,11 @@ var withoutColumnLimit = (detailRecord, metaRecord) => {
8791
10201
  return remainingDetails;
8792
10202
  };
8793
10203
  var grepTruncationSignal = (details) => {
8794
- const record = asRecord(details);
8795
- if (!record || record.truncated !== true) return void 0;
8796
- const perFile = finiteNumber2(record.perFileLimitReached);
8797
- const maxColumn = finiteNumber2(asRecord(asRecord(asRecord(record.meta)?.limits)?.columnTruncated)?.maxColumn);
8798
- const fileCount = finiteNumber2(record.fileCount);
10204
+ const record2 = asRecord(details);
10205
+ if (!record2 || record2.truncated !== true) return void 0;
10206
+ const perFile = finiteNumber2(record2.perFileLimitReached);
10207
+ const maxColumn = finiteNumber2(asRecord(asRecord(asRecord(record2.meta)?.limits)?.columnTruncated)?.maxColumn);
10208
+ const fileCount = finiteNumber2(record2.fileCount);
8799
10209
  const reasons = [];
8800
10210
  const notes = [];
8801
10211
  let continuation = null;
@@ -8822,7 +10232,7 @@ var grepTruncationSignal = (details) => {
8822
10232
  tool: "grep",
8823
10233
  partial: true,
8824
10234
  reasons,
8825
- ...finiteNumber2(record.matchCount) === void 0 ? {} : { matchCount: record.matchCount },
10235
+ ...finiteNumber2(record2.matchCount) === void 0 ? {} : { matchCount: record2.matchCount },
8826
10236
  ...fileCount === void 0 ? {} : { fileCount },
8827
10237
  ...perFile === void 0 ? {} : { perFileMatchLimit: perFile },
8828
10238
  ...maxColumn === void 0 ? {} : { maxColumn },
@@ -8859,9 +10269,9 @@ var lineBudgetSignal = (meta, target, progress) => {
8859
10269
  };
8860
10270
  };
8861
10271
  var findTruncationSignal = (details) => {
8862
- const record = asRecord(details);
8863
- if (!record || record.truncated !== true) return void 0;
8864
- const reached = finiteNumber2(record.resultLimitReached) ?? finiteNumber2(record.fileCount);
10272
+ const record2 = asRecord(details);
10273
+ if (!record2 || record2.truncated !== true) return void 0;
10274
+ const reached = finiteNumber2(record2.resultLimitReached) ?? finiteNumber2(record2.fileCount);
8865
10275
  return {
8866
10276
  tool: "find",
8867
10277
  partial: true,
@@ -9006,7 +10416,7 @@ var OmpToolsProvider = class _OmpToolsProvider {
9006
10416
  edit: createNativeReplaceEditToolDefinition(cwd),
9007
10417
  write: createPreviewWriteToolDefinition(cwd),
9008
10418
  grep: createGrepDefinitionWithSkip(cwd),
9009
- find: createFindToolDefinition(cwd),
10419
+ find: createFindDefinitionWithFilters(cwd),
9010
10420
  ls: createLsToolDefinition(cwd)
9011
10421
  };
9012
10422
  this.#catalog = catalog;
@@ -9014,10 +10424,10 @@ var OmpToolsProvider = class _OmpToolsProvider {
9014
10424
  }
9015
10425
  async list(request, _context) {
9016
10426
  const query = request.query?.toLowerCase();
9017
- const descriptors6 = await Promise.all(
10427
+ const descriptors7 = await Promise.all(
9018
10428
  OMP_CORE_TOOL_NAMES.map((name) => this.describe(name, _context))
9019
10429
  );
9020
- return descriptors6.filter((descriptor) => descriptor !== void 0).filter(
10430
+ return descriptors7.filter((descriptor) => descriptor !== void 0).filter(
9021
10431
  (descriptor) => query ? `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query) : true
9022
10432
  );
9023
10433
  }
@@ -9041,13 +10451,13 @@ var OmpToolsProvider = class _OmpToolsProvider {
9041
10451
  if (typeof prepared !== "object" || prepared === null || Array.isArray(prepared)) {
9042
10452
  throw new Error(`OMP tool ${actionName} prepared non-object arguments`);
9043
10453
  }
9044
- const record = prepared;
10454
+ const record2 = prepared;
9045
10455
  if (isOmpShellToolName(actionName)) {
9046
- const shellArgs = Object.hasOwn(args, OMP_BASH_CWD_KEY) ? { ...record, [OMP_BASH_CWD_KEY]: args[OMP_BASH_CWD_KEY] } : record;
10456
+ const shellArgs = Object.hasOwn(args, OMP_BASH_CWD_KEY) ? { ...record2, [OMP_BASH_CWD_KEY]: args[OMP_BASH_CWD_KEY] } : record2;
9047
10457
  return resolveShellCwdArgument(actionName, this.#cwd, shellArgs);
9048
10458
  }
9049
- if (actionName === "read") return normalizeReadArguments(record);
9050
- return actionName === "edit" ? normalizeEditArguments(record, args.all === true, this.#cwd) : record;
10459
+ if (actionName === "read") return normalizeReadArguments(record2);
10460
+ return actionName === "edit" ? normalizeEditArguments(record2, args.all === true, this.#cwd) : record2;
9051
10461
  }
9052
10462
  #assertAllowed(name) {
9053
10463
  if (this.#allowedTools && !this.#allowedTools.has(name)) {
@@ -9257,7 +10667,7 @@ var OmpToolsProvider = class _OmpToolsProvider {
9257
10667
  }
9258
10668
  const budget = resultCharBound(context) - READ_MARKER_RESERVE_CHARS;
9259
10669
  let text = stripRepeatReadHint(textContent(result.content));
9260
- let covered = meta.shownEnd ?? countLines(text);
10670
+ let covered = meta.shownEnd ?? countLines2(text);
9261
10671
  let nextOffset = meta.nextOffset;
9262
10672
  let stopped;
9263
10673
  while (nextOffset !== void 0 && covered < target.endLimit) {
@@ -9281,7 +10691,7 @@ var OmpToolsProvider = class _OmpToolsProvider {
9281
10691
  const pageText = stripRepeatReadHint(textContent(page.content));
9282
10692
  const start = pageMeta?.shownStart ?? nextOffset;
9283
10693
  const body = dropLeadingLines(pageText, Math.max(0, covered - start + 1));
9284
- const pageEnd = pageMeta?.shownEnd ?? start + countLines(pageText) - 1;
10694
+ const pageEnd = pageMeta?.shownEnd ?? start + countLines2(pageText) - 1;
9285
10695
  if (body.length === 0 || pageEnd <= covered) {
9286
10696
  nextOffset = void 0;
9287
10697
  break;
@@ -9411,7 +10821,7 @@ var OmpToolsProvider = class _OmpToolsProvider {
9411
10821
  import { spawn } from "node:child_process";
9412
10822
  import { createHash as createHash3, randomBytes, randomUUID as randomUUID4 } from "node:crypto";
9413
10823
  import fs8 from "node:fs";
9414
- import path13 from "node:path";
10824
+ import path16 from "node:path";
9415
10825
 
9416
10826
  // src/schema/types.ts
9417
10827
  var stateBinding = (head) => head ? { transitionId: head.transitionId, version: head.version, to: head.to } : null;
@@ -9420,7 +10830,7 @@ var stateBinding = (head) => head ? { transitionId: head.transitionId, version:
9420
10830
  import { execFileSync } from "node:child_process";
9421
10831
  import { createHash as createHash2 } from "node:crypto";
9422
10832
  import fs7 from "node:fs";
9423
- import path12 from "node:path";
10833
+ import path15 from "node:path";
9424
10834
  var SNAPSHOT_MAX_FILES = 2e4;
9425
10835
  var SNAPSHOT_MAX_BYTES = 512 * 1024 * 1024;
9426
10836
  var FALLBACK_SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([
@@ -9438,10 +10848,10 @@ var git = (cwd, args) => execFileSync("git", ["-C", cwd, ...args], {
9438
10848
  stdio: ["ignore", "pipe", "ignore"]
9439
10849
  });
9440
10850
  var nulPaths = (buffer) => buffer.toString("utf8").split("\0").filter((item) => item.length > 0);
9441
- var normalizeRelative = (value) => value.split(path12.sep).join("/");
10851
+ var normalizeRelative = (value) => value.split(path15.sep).join("/");
9442
10852
  var isInside = (root, candidate) => {
9443
- const relative = path12.relative(root, candidate);
9444
- return relative === "" || !path12.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path12.sep}`);
10853
+ const relative = path15.relative(root, candidate);
10854
+ return relative === "" || !path15.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path15.sep}`);
9445
10855
  };
9446
10856
  var excluded = (absolute, exclusions) => exclusions.some((root) => isInside(root, absolute));
9447
10857
  var entryDigest = (absolute) => {
@@ -9467,12 +10877,12 @@ var buildSnapshot = (cwd, paths, metadata, exclusions) => {
9467
10877
  let bytes = 0;
9468
10878
  const unique = [...new Set(paths)].sort((left, right) => left.localeCompare(right));
9469
10879
  for (const relativeInput of unique) {
9470
- const absolute = path12.resolve(cwd, relativeInput);
10880
+ const absolute = path15.resolve(cwd, relativeInput);
9471
10881
  if (!isInside(cwd, absolute) || excluded(absolute, exclusions)) continue;
9472
10882
  if (Object.keys(entries).length >= SNAPSHOT_MAX_FILES) {
9473
10883
  throw new Error(`Schema workspace snapshot exceeds ${SNAPSHOT_MAX_FILES} files`);
9474
10884
  }
9475
- const relative = normalizeRelative(path12.relative(cwd, absolute));
10885
+ const relative = normalizeRelative(path15.relative(cwd, absolute));
9476
10886
  const entry = entryDigest(absolute);
9477
10887
  bytes += entry.bytes;
9478
10888
  if (bytes > SNAPSHOT_MAX_BYTES) {
@@ -9501,9 +10911,9 @@ var fallbackPaths = (cwd, exclusions) => {
9501
10911
  const entries = fs7.readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
9502
10912
  for (const entry of entries) {
9503
10913
  if (FALLBACK_SKIPPED_DIRECTORIES.has(entry.name)) continue;
9504
- const absolute = path12.join(directory, entry.name);
10914
+ const absolute = path15.join(directory, entry.name);
9505
10915
  if (excluded(absolute, exclusions)) continue;
9506
- const relative = normalizeRelative(path12.relative(cwd, absolute));
10916
+ const relative = normalizeRelative(path15.relative(cwd, absolute));
9507
10917
  if (entry.isDirectory()) visit(absolute);
9508
10918
  else paths.push(relative);
9509
10919
  if (paths.length > SNAPSHOT_MAX_FILES) {
@@ -9517,7 +10927,7 @@ var fallbackPaths = (cwd, exclusions) => {
9517
10927
  var snapshotWorkspace = (cwdInput, excludedRoots = []) => {
9518
10928
  const cwd = fs7.realpathSync(cwdInput);
9519
10929
  const exclusions = excludedRoots.map((root) => {
9520
- const absolute = path12.resolve(root);
10930
+ const absolute = path15.resolve(root);
9521
10931
  try {
9522
10932
  return fs7.realpathSync(absolute);
9523
10933
  } catch {
@@ -9555,18 +10965,18 @@ var snapshotWorkspace = (cwdInput, excludedRoots = []) => {
9555
10965
  );
9556
10966
  };
9557
10967
  var resolveWorkspaceFile = (cwdInput, requestedPath, options) => {
9558
- if (!requestedPath.trim() || path12.isAbsolute(requestedPath)) {
10968
+ if (!requestedPath.trim() || path15.isAbsolute(requestedPath)) {
9559
10969
  throw new Error(`Schema paths must be non-empty project-relative paths: ${requestedPath}`);
9560
10970
  }
9561
10971
  const cwd = fs7.realpathSync(cwdInput);
9562
- const absolute = path12.resolve(cwd, requestedPath);
10972
+ const absolute = path15.resolve(cwd, requestedPath);
9563
10973
  if (!isInside(cwd, absolute) || absolute === cwd) {
9564
10974
  throw new Error(`Schema path escapes the project workspace: ${requestedPath}`);
9565
10975
  }
9566
- const relativeParts = path12.relative(cwd, absolute).split(path12.sep);
10976
+ const relativeParts = path15.relative(cwd, absolute).split(path15.sep);
9567
10977
  let cursor = cwd;
9568
10978
  for (let index = 0; index < relativeParts.length; index++) {
9569
- cursor = path12.join(cursor, relativeParts[index]);
10979
+ cursor = path15.join(cursor, relativeParts[index]);
9570
10980
  let stat2;
9571
10981
  try {
9572
10982
  stat2 = fs7.lstatSync(cursor);
@@ -9575,9 +10985,9 @@ var resolveWorkspaceFile = (cwdInput, requestedPath, options) => {
9575
10985
  if (index !== relativeParts.length - 1 || !options.allowAbsent) {
9576
10986
  throw new Error(`Schema path does not exist: ${requestedPath}`);
9577
10987
  }
9578
- const parentReal = fs7.realpathSync(path12.dirname(cursor));
10988
+ const parentReal = fs7.realpathSync(path15.dirname(cursor));
9579
10989
  if (!isInside(cwd, parentReal)) throw new Error(`Schema path parent escapes the workspace: ${requestedPath}`);
9580
- return { absolute, relative: normalizeRelative(path12.relative(cwd, absolute)), exists: false };
10990
+ return { absolute, relative: normalizeRelative(path15.relative(cwd, absolute)), exists: false };
9581
10991
  }
9582
10992
  throw error;
9583
10993
  }
@@ -9589,7 +10999,7 @@ var resolveWorkspaceFile = (cwdInput, requestedPath, options) => {
9589
10999
  throw new Error(`Schema transaction paths must name regular files: ${requestedPath}`);
9590
11000
  }
9591
11001
  }
9592
- return { absolute, relative: normalizeRelative(path12.relative(cwd, absolute)), exists: true };
11002
+ return { absolute, relative: normalizeRelative(path15.relative(cwd, absolute)), exists: true };
9593
11003
  };
9594
11004
  var sha256File = (absolute) => `sha256:${digest(fs7.readFileSync(absolute))}`;
9595
11005
 
@@ -9640,8 +11050,8 @@ var SchemaController = class {
9640
11050
  this.identity = identity;
9641
11051
  this.state = state;
9642
11052
  this.cwd = fs8.realpathSync(cwd);
9643
- this.#journalRoot = path13.join(mesh.root, "schema-transactions");
9644
- this.#lockPath = path13.join(this.#journalRoot, ".commit.lock");
11053
+ this.#journalRoot = path16.join(mesh.root, "schema-transactions");
11054
+ this.#lockPath = path16.join(this.#journalRoot, ".commit.lock");
9645
11055
  this.#recoverJournals();
9646
11056
  }
9647
11057
  #activeHypotheses = /* @__PURE__ */ new Map();
@@ -9666,12 +11076,12 @@ var SchemaController = class {
9666
11076
  }
9667
11077
  status(parentToolCallId) {
9668
11078
  const workspace = this.#workspaceEntry();
9669
- const hypotheses = this.mesh.list(HYPOTHESIS_PREFIX, this.mesh.maxReadEvents).map((entry) => entry.value).filter((record) => !parentToolCallId || record.parentToolCallId === parentToolCallId).map((record) => ({
9670
- id: record.id,
9671
- label: record.label,
9672
- status: record.status,
9673
- generation: record.generation,
9674
- updatedAt: record.updatedAt
11079
+ const hypotheses = this.mesh.list(HYPOTHESIS_PREFIX, this.mesh.maxReadEvents).map((entry) => entry.value).filter((record2) => !parentToolCallId || record2.parentToolCallId === parentToolCallId).map((record2) => ({
11080
+ id: record2.id,
11081
+ label: record2.label,
11082
+ status: record2.status,
11083
+ generation: record2.generation,
11084
+ updatedAt: record2.updatedAt
9675
11085
  }));
9676
11086
  return {
9677
11087
  mode: this.config.mode,
@@ -9696,7 +11106,7 @@ var SchemaController = class {
9696
11106
  const snapshot = snapshotWorkspace(this.cwd, [this.mesh.root]);
9697
11107
  const generation = this.#generation();
9698
11108
  const now = Date.now();
9699
- const record = {
11109
+ const record2 = {
9700
11110
  id: randomUUID4(),
9701
11111
  label: input.label,
9702
11112
  summary: input.summary,
@@ -9710,66 +11120,66 @@ var SchemaController = class {
9710
11120
  createdAt: now,
9711
11121
  updatedAt: now
9712
11122
  };
9713
- await this.#publish("hypothesized", record);
11123
+ await this.#publish("hypothesized", record2);
9714
11124
  await this.mesh.put({
9715
- key: `${HYPOTHESIS_PREFIX}${record.id}`,
9716
- value: record,
11125
+ key: `${HYPOTHESIS_PREFIX}${record2.id}`,
11126
+ value: record2,
9717
11127
  ifVersion: 0,
9718
11128
  identity: this.identity
9719
11129
  });
9720
- this.#activeHypotheses.set(record.id, context.parentToolCallId);
9721
- context.update(`Schema hypothesis recorded: ${record.label}`);
11130
+ this.#activeHypotheses.set(record2.id, context.parentToolCallId);
11131
+ context.update(`Schema hypothesis recorded: ${record2.label}`);
9722
11132
  return {
9723
- hypothesisId: record.id,
9724
- status: record.status,
9725
- state: record.state,
9726
- fingerprint: record.fingerprint,
11133
+ hypothesisId: record2.id,
11134
+ status: record2.status,
11135
+ state: record2.state,
11136
+ fingerprint: record2.fingerprint,
9727
11137
  generation
9728
11138
  };
9729
11139
  }
9730
11140
  async verify(hypothesisId, context) {
9731
11141
  const entry = this.#requireHypothesis(hypothesisId);
9732
- const record = entry.value;
9733
- this.#assertInvocation(record.parentToolCallId, context.parentToolCallId);
9734
- if (record.status !== "active") throw new Error(`Schema hypothesis is not active: ${record.status}`);
9735
- if (record.evidence.length === 0) return this.#failedVerification(record, [], "missing evidence");
11142
+ const record2 = entry.value;
11143
+ this.#assertInvocation(record2.parentToolCallId, context.parentToolCallId);
11144
+ if (record2.status !== "active") throw new Error(`Schema hypothesis is not active: ${record2.status}`);
11145
+ if (record2.evidence.length === 0) return this.#failedVerification(record2, [], "missing evidence");
9736
11146
  let before;
9737
11147
  try {
9738
11148
  before = snapshotWorkspace(this.cwd, [this.mesh.root]);
9739
11149
  } catch (error) {
9740
- return this.#failedVerification(record, [], `workspace snapshot failed: ${errorMessage4(error)}`);
11150
+ return this.#failedVerification(record2, [], `workspace snapshot failed: ${errorMessage4(error)}`);
9741
11151
  }
9742
11152
  const currentState = stateBinding(this.state?.getHead() ?? null);
9743
- if (!sameBinding(record.state, currentState)) {
9744
- return this.#failedVerification(record, [], "state head changed since hypothesis");
11153
+ if (!sameBinding(record2.state, currentState)) {
11154
+ return this.#failedVerification(record2, [], "state head changed since hypothesis");
9745
11155
  }
9746
- if (record.generation !== this.#generation()) {
9747
- return this.#failedVerification(record, [], "workspace generation changed since hypothesis");
11156
+ if (record2.generation !== this.#generation()) {
11157
+ return this.#failedVerification(record2, [], "workspace generation changed since hypothesis");
9748
11158
  }
9749
- if (record.fingerprint !== before.fingerprint) {
9750
- return this.#failedVerification(record, [], "workspace fingerprint changed since hypothesis");
11159
+ if (record2.fingerprint !== before.fingerprint) {
11160
+ return this.#failedVerification(record2, [], "workspace fingerprint changed since hypothesis");
9751
11161
  }
9752
- const results = await this.#verifyEvidence(record.evidence, context);
11162
+ const results = await this.#verifyEvidence(record2.evidence, context);
9753
11163
  let after;
9754
11164
  try {
9755
11165
  after = snapshotWorkspace(this.cwd, [this.mesh.root]);
9756
11166
  } catch (error) {
9757
- return this.#failedVerification(record, results, `post-evidence snapshot failed: ${errorMessage4(error)}`);
11167
+ return this.#failedVerification(record2, results, `post-evidence snapshot failed: ${errorMessage4(error)}`);
9758
11168
  }
9759
11169
  const allConfirmed = results.length > 0 && results.every((result) => result.status === "confirmed");
9760
11170
  if (!allConfirmed || before.fingerprint !== after.fingerprint) {
9761
11171
  const reason = before.fingerprint !== after.fingerprint ? "workspace fingerprint changed while evidence ran" : "one or more evidence items were not confirmed";
9762
- return this.#failedVerification(record, results, reason);
11172
+ return this.#failedVerification(record2, results, reason);
9763
11173
  }
9764
11174
  const certificate = randomBytes(32).toString("hex");
9765
11175
  const issuedAt = Date.now();
9766
11176
  const certificateRecord = {
9767
11177
  tokenHash: hashToken(certificate),
9768
- hypothesisId: record.id,
11178
+ hypothesisId: record2.id,
9769
11179
  parentToolCallId: context.parentToolCallId,
9770
- state: record.state,
9771
- fingerprint: record.fingerprint,
9772
- generation: record.generation,
11180
+ state: record2.state,
11181
+ fingerprint: record2.fingerprint,
11182
+ generation: record2.generation,
9773
11183
  issuedAt,
9774
11184
  expiresAt: issuedAt + this.config.certificateTtlMs,
9775
11185
  status: "active"
@@ -9784,7 +11194,7 @@ var SchemaController = class {
9784
11194
  try {
9785
11195
  await this.mesh.put({
9786
11196
  key: entry.key,
9787
- value: { ...record, status: "verified", updatedAt: Date.now() },
11197
+ value: { ...record2, status: "verified", updatedAt: Date.now() },
9788
11198
  ifVersion: entry.version,
9789
11199
  identity: this.identity
9790
11200
  });
@@ -9803,16 +11213,16 @@ var SchemaController = class {
9803
11213
  this.#activeCertificates.set(certificateRecord.tokenHash, context.parentToolCallId);
9804
11214
  try {
9805
11215
  await this.#publish("verified", {
9806
- hypothesisId: record.id,
11216
+ hypothesisId: record2.id,
9807
11217
  tokenHash: certificateRecord.tokenHash,
9808
11218
  results: results.map((result) => ({
9809
11219
  kind: result.evidence.kind,
9810
11220
  status: result.status,
9811
11221
  detail: result.detail
9812
11222
  })),
9813
- state: record.state,
9814
- fingerprint: record.fingerprint,
9815
- generation: record.generation,
11223
+ state: record2.state,
11224
+ fingerprint: record2.fingerprint,
11225
+ generation: record2.generation,
9816
11226
  issuedAt,
9817
11227
  expiresAt: certificateRecord.expiresAt
9818
11228
  });
@@ -9821,7 +11231,7 @@ var SchemaController = class {
9821
11231
  context.update(`Schema evidence confirmed; certificate expires in ${this.config.certificateTtlMs}ms`);
9822
11232
  return {
9823
11233
  verified: true,
9824
- hypothesisId: record.id,
11234
+ hypothesisId: record2.id,
9825
11235
  certificate,
9826
11236
  issuedAt,
9827
11237
  expiresAt: certificateRecord.expiresAt,
@@ -9840,7 +11250,7 @@ var SchemaController = class {
9840
11250
  this.#assertPayloadBound(input);
9841
11251
  const release = this.#acquireCommitLock();
9842
11252
  const transactionId = randomUUID4();
9843
- const journalPath = path13.join(this.#journalRoot, `${transactionId}.json`);
11253
+ const journalPath = path16.join(this.#journalRoot, `${transactionId}.json`);
9844
11254
  let journal;
9845
11255
  let consumed = false;
9846
11256
  let committed = false;
@@ -10050,12 +11460,12 @@ var SchemaController = class {
10050
11460
  if (invocation !== parentToolCallId) continue;
10051
11461
  const entry = this.mesh.get(`${CERTIFICATE_PREFIX}${tokenHash}`);
10052
11462
  if (entry) {
10053
- const record = entry.value;
10054
- if (record.status === "active") {
11463
+ const record2 = entry.value;
11464
+ if (record2.status === "active") {
10055
11465
  try {
10056
11466
  await this.mesh.put({
10057
11467
  key: entry.key,
10058
- value: { ...record, status: "abandoned" },
11468
+ value: { ...record2, status: "abandoned" },
10059
11469
  ifVersion: entry.version,
10060
11470
  identity: this.identity
10061
11471
  });
@@ -10069,12 +11479,12 @@ var SchemaController = class {
10069
11479
  if (invocation !== parentToolCallId) continue;
10070
11480
  const entry = this.mesh.get(`${HYPOTHESIS_PREFIX}${hypothesisId}`);
10071
11481
  if (entry) {
10072
- const record = entry.value;
10073
- if (record.status === "active" || record.status === "verified") {
11482
+ const record2 = entry.value;
11483
+ if (record2.status === "active" || record2.status === "verified") {
10074
11484
  try {
10075
11485
  await this.mesh.put({
10076
11486
  key: entry.key,
10077
- value: { ...record, status: "abandoned", updatedAt: Date.now() },
11487
+ value: { ...record2, status: "abandoned", updatedAt: Date.now() },
10078
11488
  ifVersion: entry.version,
10079
11489
  identity: this.identity
10080
11490
  });
@@ -10085,16 +11495,16 @@ var SchemaController = class {
10085
11495
  this.#activeHypotheses.delete(hypothesisId);
10086
11496
  }
10087
11497
  }
10088
- async #failedVerification(record, results, reason) {
11498
+ async #failedVerification(record2, results, reason) {
10089
11499
  try {
10090
11500
  await this.#publish("verification_failed", {
10091
- hypothesisId: record.id,
11501
+ hypothesisId: record2.id,
10092
11502
  reason,
10093
11503
  results: results.map((result) => ({ kind: result.evidence.kind, status: result.status, detail: result.detail }))
10094
11504
  });
10095
11505
  } catch {
10096
11506
  }
10097
- return { verified: false, hypothesisId: record.id, reason, results };
11507
+ return { verified: false, hypothesisId: record2.id, reason, results };
10098
11508
  }
10099
11509
  async #verifyEvidence(evidence, context) {
10100
11510
  if (evidence.length === 0) return [];
@@ -10321,7 +11731,7 @@ ${Date.now()}
10321
11731
  }
10322
11732
  for (const name of fs8.readdirSync(this.#journalRoot)) {
10323
11733
  if (!name.endsWith(".json")) continue;
10324
- const filePath = path13.join(this.#journalRoot, name);
11734
+ const filePath = path16.join(this.#journalRoot, name);
10325
11735
  try {
10326
11736
  const journal = JSON.parse(fs8.readFileSync(filePath, "utf8"));
10327
11737
  if (journal.format !== 1 || journal.status !== "applying" || !Array.isArray(journal.before)) continue;
@@ -10441,7 +11851,7 @@ var operationSchema = {
10441
11851
  }
10442
11852
  ]
10443
11853
  };
10444
- var descriptors4 = [
11854
+ var descriptors5 = [
10445
11855
  {
10446
11856
  name: "status",
10447
11857
  description: "Read the fixed session Schema mode, transaction bounds, generation, and invocation hypotheses",
@@ -10511,7 +11921,7 @@ var descriptors4 = [
10511
11921
  namespace: "schema"
10512
11922
  }
10513
11923
  ];
10514
- var normalizeSchemaArgs = actionArgNormalizer(() => descriptors4);
11924
+ var normalizeSchemaArgs = actionArgNormalizer(() => descriptors5);
10515
11925
  var SchemaProvider = class {
10516
11926
  constructor(controller) {
10517
11927
  this.controller = controller;
@@ -10520,12 +11930,12 @@ var SchemaProvider = class {
10520
11930
  description = "Host-owned, opt-in Schema verification and bounded local-file transaction control plane";
10521
11931
  async list(request, _context) {
10522
11932
  const query = request.query?.toLowerCase();
10523
- return query ? descriptors4.filter(
11933
+ return query ? descriptors5.filter(
10524
11934
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
10525
- ) : descriptors4;
11935
+ ) : descriptors5;
10526
11936
  }
10527
11937
  async describe(actionName, _context) {
10528
- return descriptors4.find((descriptor) => descriptor.name === actionName);
11938
+ return descriptors5.find((descriptor) => descriptor.name === actionName);
10529
11939
  }
10530
11940
  prepareArguments(actionName, args) {
10531
11941
  return normalizeSchemaArgs(actionName, args);
@@ -10575,11 +11985,11 @@ var SchemaProvider = class {
10575
11985
 
10576
11986
  // src/state/store.ts
10577
11987
  import { createHash as createHash5 } from "node:crypto";
10578
- import path15 from "node:path";
11988
+ import path18 from "node:path";
10579
11989
 
10580
11990
  // src/state/complexity.ts
10581
11991
  import fs9 from "node:fs";
10582
- import path14 from "node:path";
11992
+ import path17 from "node:path";
10583
11993
  var isWordStart = (character) => character !== void 0 && (character >= "a" && character <= "z" || character >= "A" && character <= "Z" || character === "_" || character === "$");
10584
11994
  var isWordPart = (character) => isWordStart(character) || character !== void 0 && character >= "0" && character <= "9";
10585
11995
  var regularExpressionPrefixWords = /* @__PURE__ */ new Set([
@@ -10811,7 +12221,7 @@ var languageComplexities = [
10811
12221
  typeScriptJavaScriptComplexity
10812
12222
  ];
10813
12223
  var complexityForFile = (file, languages = languageComplexities) => {
10814
- const extension = path14.extname(file).toLowerCase();
12224
+ const extension = path17.extname(file).toLowerCase();
10815
12225
  return languages.find((language) => language.extensions.includes(extension));
10816
12226
  };
10817
12227
  var countFileComplexity = (file) => {
@@ -11630,10 +13040,10 @@ var StateStore = class {
11630
13040
  const currentHead = this.getHead();
11631
13041
  const records = [];
11632
13042
  for (const event of events) {
11633
- const record = toRecord(event, committedIds);
11634
- if (record) records.push(record);
13043
+ const record2 = toRecord(event, committedIds);
13044
+ if (record2) records.push(record2);
11635
13045
  }
11636
- if (currentHead && !records.some((record) => record.transitionId === currentHead.transitionId)) {
13046
+ if (currentHead && !records.some((record2) => record2.transitionId === currentHead.transitionId)) {
11637
13047
  const currentRecord = toHeadRecord(currentHead);
11638
13048
  if (currentRecord) records.push(currentRecord);
11639
13049
  }
@@ -11646,7 +13056,7 @@ var StateStore = class {
11646
13056
  }
11647
13057
  }
11648
13058
  const visibleRecords = input.includeArchived || lastRepresentation < 0 ? records : records.slice(lastRepresentation);
11649
- const visibleIds = new Set(visibleRecords.map((record) => record.transitionId));
13059
+ const visibleIds = new Set(visibleRecords.map((record2) => record2.transitionId));
11650
13060
  const latestOutcomes = latestTransitionOutcomes(events);
11651
13061
  const eventCertifications = events.map((event) => toCertificate(event, currentHead, latestOutcomes)).filter((certificate) => certificate !== void 0).filter(
11652
13062
  (certificate) => certificate.targets.every((target) => visibleIds.has(target.transitionId))
@@ -11662,32 +13072,32 @@ var StateStore = class {
11662
13072
  certifications.map((certificate) => [certificate.sequence, certificate])
11663
13073
  );
11664
13074
  const latestCertificate = /* @__PURE__ */ new Map();
11665
- for (const record of visibleRecords) {
11666
- const outcome = latestOutcomes.get(record.transitionId);
13075
+ for (const record2 of visibleRecords) {
13076
+ const outcome = latestOutcomes.get(record2.transitionId);
11667
13077
  if (outcome?.phase !== "certified") continue;
11668
13078
  const certificate = certificatesBySequence.get(outcome.event.sequence);
11669
- if (certificate) latestCertificate.set(record.transitionId, certificate);
13079
+ if (certificate) latestCertificate.set(record2.transitionId, certificate);
11670
13080
  }
11671
13081
  if (durableCertificate) {
11672
13082
  latestCertificate.set(currentHead.transitionId, durableCertificate);
11673
13083
  }
11674
13084
  const archiveBoundaryId = input.includeArchived !== true && lastRepresentation > 0 ? records[lastRepresentation]?.transitionId : void 0;
11675
13085
  const filtered = (input.label ? visibleRecords.filter(
11676
- (record) => record.label === input.label || record.to === input.label || record.from === input.label && record.transitionId !== archiveBoundaryId
11677
- ) : visibleRecords).map((record) => {
11678
- const certificate = latestCertificate.get(record.transitionId);
11679
- return certificate ? { ...record, certificationStatus: "certified", certificate } : record;
13086
+ (record2) => record2.label === input.label || record2.to === input.label || record2.from === input.label && record2.transitionId !== archiveBoundaryId
13087
+ ) : visibleRecords).map((record2) => {
13088
+ const certificate = latestCertificate.get(record2.transitionId);
13089
+ return certificate ? { ...record2, certificationStatus: "certified", certificate } : record2;
11680
13090
  });
11681
13091
  const limited = input.limit !== void 0 && input.limit > 0 ? filtered.slice(0, input.limit) : filtered;
11682
13092
  const labelSet = /* @__PURE__ */ new Set();
11683
13093
  const limitedIds = /* @__PURE__ */ new Set();
11684
- for (const record of limited) {
11685
- limitedIds.add(record.transitionId);
11686
- if (record.from && record.transitionId !== archiveBoundaryId) {
11687
- labelSet.add(record.from);
13094
+ for (const record2 of limited) {
13095
+ limitedIds.add(record2.transitionId);
13096
+ if (record2.from && record2.transitionId !== archiveBoundaryId) {
13097
+ labelSet.add(record2.from);
11688
13098
  }
11689
- labelSet.add(record.to);
11690
- labelSet.add(record.label);
13099
+ labelSet.add(record2.to);
13100
+ labelSet.add(record2.label);
11691
13101
  }
11692
13102
  return {
11693
13103
  transitions: limited,
@@ -11702,7 +13112,7 @@ var StateStore = class {
11702
13112
  const files = [];
11703
13113
  let netDelta = 0;
11704
13114
  for (const file of this.normalizeComplexityFiles(requestedFiles, input.cwd)) {
11705
- const measured = countFileComplexity(path15.resolve(input.cwd, file));
13115
+ const measured = countFileComplexity(path18.resolve(input.cwd, file));
11706
13116
  if (!measured) {
11707
13117
  files.push({ file, supported: false });
11708
13118
  continue;
@@ -11729,7 +13139,7 @@ var StateStore = class {
11729
13139
  const updates = [];
11730
13140
  let netDelta = 0;
11731
13141
  for (const file of this.normalizeComplexityFiles(files, cwd)) {
11732
- const measured = countFileComplexity(path15.resolve(cwd, file));
13142
+ const measured = countFileComplexity(path18.resolve(cwd, file));
11733
13143
  if (!measured) {
11734
13144
  deltas.push({ file, supported: false });
11735
13145
  continue;
@@ -11779,11 +13189,11 @@ var StateStore = class {
11779
13189
  const normalized = /* @__PURE__ */ new Set();
11780
13190
  for (const file of files) {
11781
13191
  if (!file.trim()) continue;
11782
- const relative = path15.relative(cwd, path15.resolve(cwd, file));
11783
- if (relative === ".." || relative.startsWith(`..${path15.sep}`) || path15.isAbsolute(relative)) {
13192
+ const relative = path18.relative(cwd, path18.resolve(cwd, file));
13193
+ if (relative === ".." || relative.startsWith(`..${path18.sep}`) || path18.isAbsolute(relative)) {
11784
13194
  throw new Error(`State complexity file must be inside the project cwd: ${file}`);
11785
13195
  }
11786
- normalized.add(relative.split(path15.sep).join("/"));
13196
+ normalized.add(relative.split(path18.sep).join("/"));
11787
13197
  }
11788
13198
  return [...normalized];
11789
13199
  }
@@ -11917,7 +13327,7 @@ var StateStore = class {
11917
13327
  includeArchived: input.includeArchived === true
11918
13328
  });
11919
13329
  const match = transitions.find(
11920
- (record) => record.transitionId === verificationHead.transitionId
13330
+ (record2) => record2.transitionId === verificationHead.transitionId
11921
13331
  );
11922
13332
  targets = match ? [match] : [];
11923
13333
  } else {
@@ -12225,7 +13635,7 @@ var checkGoalSchema = {
12225
13635
  additionalProperties: false
12226
13636
  };
12227
13637
  var emptySchema3 = { type: "object", properties: {}, additionalProperties: false };
12228
- var descriptors5 = [
13638
+ var descriptors6 = [
12229
13639
  {
12230
13640
  name: "transition",
12231
13641
  description: "Append a labeled, validated state transition and compare-and-swap advance the head",
@@ -12276,7 +13686,7 @@ var descriptors5 = [
12276
13686
  namespace: "state"
12277
13687
  }
12278
13688
  ];
12279
- var normalizeStateArgs = actionArgNormalizer(() => descriptors5);
13689
+ var normalizeStateArgs = actionArgNormalizer(() => descriptors6);
12280
13690
  var StateProvider = class {
12281
13691
  name = "state";
12282
13692
  description = "Schema-style labeled transition layer: an append-only timeline of validated transitions with a compare-and-swap head and evidence-based certification over mesh storage";
@@ -12291,12 +13701,12 @@ var StateProvider = class {
12291
13701
  }
12292
13702
  async list(request, _context) {
12293
13703
  const query = request.query?.toLowerCase();
12294
- return query ? descriptors5.filter(
13704
+ return query ? descriptors6.filter(
12295
13705
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
12296
- ) : descriptors5;
13706
+ ) : descriptors6;
12297
13707
  }
12298
13708
  async describe(actionName, _context) {
12299
- return descriptors5.find((descriptor) => descriptor.name === actionName);
13709
+ return descriptors6.find((descriptor) => descriptor.name === actionName);
12300
13710
  }
12301
13711
  prepareArguments(actionName, args) {
12302
13712
  return normalizeStateArgs(actionName, args);
@@ -12777,7 +14187,7 @@ var FabricRuntimeState = class {
12777
14187
  this.#mainAgent = mainAgent;
12778
14188
  const projectRoot = process.env.OMP_FABRIC_PROJECT_ROOT ?? context.cwd;
12779
14189
  const configuredMeshRoot = this.#config.mesh.root;
12780
- const meshRoot = process.env.OMP_FABRIC_MESH_ROOT ?? (configuredMeshRoot ? path16.resolve(projectRoot, configuredMeshRoot) : path16.join(projectRoot, ".omp", "fabric", "mesh"));
14190
+ const meshRoot = process.env.OMP_FABRIC_MESH_ROOT ?? (configuredMeshRoot ? path19.resolve(projectRoot, configuredMeshRoot) : path19.join(projectRoot, ".omp", "fabric", "mesh"));
12781
14191
  this.#mesh = new MeshStore(
12782
14192
  meshRoot,
12783
14193
  this.#config.mesh.maxEventBytes,
@@ -12842,6 +14252,19 @@ var FabricRuntimeState = class {
12842
14252
  description: "Host context compaction controller",
12843
14253
  create: () => new CompactProvider(this.#compact)
12844
14254
  }));
14255
+ if (this.#config.codemap.enabled) {
14256
+ const codemapConfig = this.#config.codemap;
14257
+ await installBuiltin(createProviderComponent({
14258
+ provider: "codemap",
14259
+ description: "Structural repo map and git co-change ranking",
14260
+ create: () => new CodemapProvider(codemapConfig)
14261
+ }));
14262
+ } else {
14263
+ this.#registry.markUnavailable(
14264
+ "codemap",
14265
+ 'disabled by configuration (codemap.enabled=false); set "codemap": { "enabled": true } in .omp/fabric.json or the agent fabric.json to enable codemap.* actions'
14266
+ );
14267
+ }
12845
14268
  const agentConfig = enforceSchema ? { ...this.#config.agents, enabled: false } : this.#config.agents;
12846
14269
  const modelsConfig = this.#config.models;
12847
14270
  const visibleOmpModels = () => {
@@ -12938,8 +14361,8 @@ var FabricRuntimeState = class {
12938
14361
  };
12939
14362
  const lineageAlive = (rootId) => this.#participants?.get(rootId) !== void 0;
12940
14363
  const actorRoots = {
12941
- project: path16.join(meshRoot, "actors"),
12942
- session: path16.join(meshRoot, "actors", fabricSessionId)
14364
+ project: path19.join(meshRoot, "actors"),
14365
+ session: path19.join(meshRoot, "actors", fabricSessionId)
12943
14366
  };
12944
14367
  const acquireActorCapabilityView = (requirements, signal) => this.#registry.acquireCapabilityView(requirements, {
12945
14368
  cwd: context.cwd,
@@ -13139,6 +14562,7 @@ ${escapeXmlText(text)}
13139
14562
  ...this.#config.mesh.enabled ? ["mesh", "state"] : [],
13140
14563
  "schema",
13141
14564
  "compact",
14565
+ ...this.#config.codemap.enabled ? ["codemap"] : [],
13142
14566
  "agents",
13143
14567
  ...this.#config.memory.enabled ? ["memory"] : []
13144
14568
  ]);
@@ -13279,7 +14703,11 @@ ${escapeXmlText(text)}
13279
14703
  pending,
13280
14704
  outerToolResult,
13281
14705
  context,
13282
- (update) => this.activity.updateCall(runId, callId, update)
14706
+ (update) => this.activity.updateCall(runId, callId, update),
14707
+ {
14708
+ handoffRetirement: this.#config?.prewalk.handoffRetirement ?? DEFAULT_FABRIC_CONFIG.prewalk.handoffRetirement,
14709
+ handoffRetirementKeep: this.#config?.prewalk.handoffRetirementKeep ?? DEFAULT_FABRIC_CONFIG.prewalk.handoffRetirementKeep
14710
+ }
13283
14711
  );
13284
14712
  const succeeded = result.completed === true || result.continued === true;
13285
14713
  const error = typeof result.error === "string" ? result.error : void 0;