pi-fabric 0.64.0 → 0.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +1 -0
  2. package/dist/chunks/{chunk-22RY7UMR.js → chunk-2YCOXT4W.js} +4 -13
  3. package/dist/chunks/chunk-2YCOXT4W.js.map +7 -0
  4. package/dist/chunks/{chunk-EBGSP7Q7.js → chunk-ASBRTAJ4.js} +74 -16
  5. package/dist/chunks/chunk-ASBRTAJ4.js.map +7 -0
  6. package/dist/chunks/chunk-CIJPRYAP.js +16 -0
  7. package/dist/chunks/chunk-CIJPRYAP.js.map +7 -0
  8. package/dist/chunks/{chunk-UACA2AKF.js → chunk-FL52NCFS.js} +1 -1
  9. package/dist/chunks/{chunk-UACA2AKF.js.map → chunk-FL52NCFS.js.map} +2 -2
  10. package/dist/chunks/{chunk-GFZYWVIK.js → chunk-HDKEA77S.js} +1 -1
  11. package/dist/chunks/chunk-HDKEA77S.js.map +7 -0
  12. package/dist/chunks/{chunk-OKQHLYTU.js → chunk-LDZWE6OA.js} +156 -52
  13. package/dist/chunks/chunk-LDZWE6OA.js.map +7 -0
  14. package/dist/chunks/{chunk-TGRIZ7E4.js → chunk-LKH5MCNR.js} +48 -1
  15. package/dist/chunks/chunk-LKH5MCNR.js.map +7 -0
  16. package/dist/chunks/{chunk-725BGPJU.js → chunk-LNLQOUUR.js} +3 -3
  17. package/dist/chunks/{fabric-runtime-state-STCXP7AK.js → fabric-runtime-state-EW66PRZY.js} +616 -123
  18. package/dist/chunks/fabric-runtime-state-EW66PRZY.js.map +7 -0
  19. package/dist/chunks/scanner-4IGGTOEI.js +182 -0
  20. package/dist/chunks/scanner-4IGGTOEI.js.map +7 -0
  21. package/dist/chunks/{settings-GYHFWT4K.js → settings-WDHCKZZK.js} +7 -3
  22. package/dist/chunks/settings-WDHCKZZK.js.map +7 -0
  23. package/dist/commands/fabric.d.ts.map +1 -1
  24. package/dist/config.d.ts +21 -0
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/core/action-registry.d.ts +23 -0
  27. package/dist/core/action-registry.d.ts.map +1 -1
  28. package/dist/core/action-registry.js +4 -3
  29. package/dist/fabric-runtime-state.d.ts +5 -0
  30. package/dist/fabric-runtime-state.d.ts.map +1 -1
  31. package/dist/fabric-state.d.ts +4 -0
  32. package/dist/fabric-state.d.ts.map +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +65 -11
  35. package/dist/index.js.map +2 -2
  36. package/dist/prewalk/controller.d.ts +1 -0
  37. package/dist/prewalk/controller.d.ts.map +1 -1
  38. package/dist/prewalk/fs-drift.d.ts +1 -0
  39. package/dist/prewalk/fs-drift.d.ts.map +1 -1
  40. package/dist/prewalk/handoff.d.ts +1 -0
  41. package/dist/prewalk/handoff.d.ts.map +1 -1
  42. package/dist/protocol.d.ts +8 -0
  43. package/dist/protocol.d.ts.map +1 -1
  44. package/dist/protocol.js +3 -2
  45. package/dist/providers/compact-provider.d.ts.map +1 -1
  46. package/dist/providers/mcp-advisory.d.ts.map +1 -1
  47. package/dist/providers/mcp-descriptor-cache.d.ts +7 -0
  48. package/dist/providers/mcp-descriptor-cache.d.ts.map +1 -1
  49. package/dist/providers/mcp-provider.d.ts.map +1 -1
  50. package/dist/residency/host.js +5 -4
  51. package/dist/residency/host.js.map +1 -1
  52. package/dist/speculation/eligibility.d.ts +22 -0
  53. package/dist/speculation/eligibility.d.ts.map +1 -0
  54. package/dist/speculation/freshness.d.ts +10 -0
  55. package/dist/speculation/freshness.d.ts.map +1 -0
  56. package/dist/speculation/partial-json.d.ts +9 -0
  57. package/dist/speculation/partial-json.d.ts.map +1 -0
  58. package/dist/speculation/scanner.d.ts +13 -0
  59. package/dist/speculation/scanner.d.ts.map +1 -0
  60. package/dist/speculation/store.d.ts +35 -0
  61. package/dist/speculation/store.d.ts.map +1 -0
  62. package/dist/speculation/stream-tap.d.ts +27 -0
  63. package/dist/speculation/stream-tap.d.ts.map +1 -0
  64. package/dist/speculation/types.d.ts +43 -0
  65. package/dist/speculation/types.d.ts.map +1 -0
  66. package/dist/topology/peer-settle.d.ts +2 -3
  67. package/dist/topology/peer-settle.d.ts.map +1 -1
  68. package/dist/ui/settings.d.ts.map +1 -1
  69. package/dist/worker.js +0 -0
  70. package/docs/configuration.md +4 -0
  71. package/docs/speculation.md +119 -0
  72. package/package.json +14 -16
  73. package/dist/chunks/chunk-22RY7UMR.js.map +0 -7
  74. package/dist/chunks/chunk-EBGSP7Q7.js.map +0 -7
  75. package/dist/chunks/chunk-GFZYWVIK.js.map +0 -7
  76. package/dist/chunks/chunk-OKQHLYTU.js.map +0 -7
  77. package/dist/chunks/chunk-TGRIZ7E4.js.map +0 -7
  78. package/dist/chunks/fabric-runtime-state-STCXP7AK.js.map +0 -7
  79. package/dist/chunks/settings-GYHFWT4K.js.map +0 -7
  80. /package/dist/chunks/{chunk-725BGPJU.js.map → chunk-LNLQOUUR.js.map} +0 -0
package/README.md CHANGED
@@ -131,6 +131,7 @@ See the [interface & commands reference](docs/interface.md) for every view, keyb
131
131
  - [Components & committed capabilities](docs/components.md): supervised effects, exact requirements, external per-model guidance and execution-profile replacement, rolling provider generations, actor commitments, and both formal calculi.
132
132
  - [External providers](docs/providers.md): the versioned provider protocol for extensions.
133
133
  - [Architecture & security](docs/architecture.md): the host bridge, sandboxing, tool-call robustness, and limits.
134
+ - [Speculative PTC](docs/speculation.md): pre-launching literal read calls while the program streams, with epoch + freshness guarantees.
134
135
  - [Skills](docs/skills.md): the core-first invocation policy and user-invoked advanced patterns.
135
136
 
136
137
  ## Development
@@ -1,14 +1,6 @@
1
- // src/core/stable-hash.ts
2
- import { createHash } from "node:crypto";
3
- var stableJsonValue = (value) => {
4
- if (Array.isArray(value)) return value.map(stableJsonValue);
5
- if (value instanceof URL) return value.href;
6
- if (value === null || typeof value !== "object") return value;
7
- return Object.fromEntries(
8
- Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, stableJsonValue(nested)])
9
- );
10
- };
11
- var stableJsonHash = (value) => createHash("sha256").update(JSON.stringify(stableJsonValue(value))).digest("hex");
1
+ import {
2
+ stableJsonHash
3
+ } from "./chunk-CIJPRYAP.js";
12
4
 
13
5
  // src/components/model-guidance.ts
14
6
  var FABRIC_EXECUTION_GUIDANCE_SLOT = "fabric.execution";
@@ -224,7 +216,6 @@ var resolveFabricModelGuidance = (guidance, options) => {
224
216
  };
225
217
 
226
218
  export {
227
- stableJsonHash,
228
219
  FABRIC_EXECUTION_GUIDANCE_SLOT,
229
220
  MAX_FABRIC_MODEL_GUIDANCE_PER_COMPONENT,
230
221
  MAX_FABRIC_MODEL_GUIDANCE_CONTENT_CHARS,
@@ -237,4 +228,4 @@ export {
237
228
  fabricModelGuidanceInfo,
238
229
  resolveFabricModelGuidance
239
230
  };
240
- //# sourceMappingURL=chunk-22RY7UMR.js.map
231
+ //# sourceMappingURL=chunk-2YCOXT4W.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/components/model-guidance.ts"],
4
+ "sourcesContent": ["import { stableJsonHash } from \"../core/stable-hash.js\";\nimport type {\n FabricModelGuidance,\n FabricModelGuidanceInfo,\n FabricModelGuidancePlacement,\n FabricModelGuidanceTarget,\n} from \"./types.js\";\n\nexport const FABRIC_EXECUTION_GUIDANCE_SLOT = \"fabric.execution\";\nexport const MAX_FABRIC_MODEL_GUIDANCE_PER_COMPONENT = 64;\nexport const MAX_FABRIC_MODEL_GUIDANCE_CONTENT_CHARS = 32_000;\nexport const MAX_FABRIC_MODEL_GUIDANCE_TOTAL_CHARS = 64_000;\nexport const MAX_FABRIC_MODEL_GUIDANCE_REGISTRATIONS = 1_024;\nexport const MAX_FABRIC_MODEL_GUIDANCE_SNAPSHOT_CHARS = 1_000_000;\n\nconst MAX_LABEL_CHARS = 128;\nconst MAX_SLOT_CHARS = 128;\nconst MAX_MODEL_PATTERNS = 32;\nconst MAX_MODEL_PATTERN_CHARS = 256;\nconst CONTROL_CHARACTER = /[\\u0000-\\u001f\\u007f]/;\nconst REGEX_CHARACTER = /[\\\\^$+.[\\]{}()|]/;\n\nexport interface NormalizedFabricModelGuidance {\n label: string;\n models: string[];\n content: string;\n targets: FabricModelGuidanceTarget[];\n placement: FabricModelGuidancePlacement;\n slot?: string;\n}\n\nexport interface FabricOwnedModelGuidance extends NormalizedFabricModelGuidance {\n componentId: string;\n component: string;\n revision: number;\n}\n\nconst compareStableText = (left: string, right: string): number =>\n left < right ? -1 : left > right ? 1 : 0;\n\nexport const compareFabricOwnedModelGuidance = (\n left: FabricOwnedModelGuidance,\n right: FabricOwnedModelGuidance,\n): number =>\n compareStableText(left.componentId, right.componentId) ||\n compareStableText(left.label, right.label);\n\nexport interface FabricGuidanceDefaultSlot {\n slot: string;\n content: string;\n}\n\nexport interface FabricResolvedModelGuidance {\n slotText: string;\n appendText: string;\n digest: string;\n sources: Array<{\n componentId: string;\n component: string;\n label: string;\n placement: FabricModelGuidancePlacement;\n slot?: string;\n contentHash: string;\n }>;\n}\n\nconst guidanceError = (message: string): Error =>\n new Error(`Invalid Fabric model guidance: ${message}`);\n\nconst normalizedIdentifier = (value: unknown, field: \"label\" | \"slot\", max: number): string => {\n if (typeof value !== \"string\") throw guidanceError(`${field} must be a string`);\n const normalized = value.trim();\n if (!normalized) throw guidanceError(`${field} must not be empty`);\n if (normalized.length > max) throw guidanceError(`${field} exceeds ${max} characters`);\n if (CONTROL_CHARACTER.test(normalized)) {\n throw guidanceError(`${field} must not contain control characters`);\n }\n return normalized;\n};\n\nconst normalizeTargets = (\n targets: readonly FabricModelGuidanceTarget[] | undefined,\n): FabricModelGuidanceTarget[] => {\n if (targets === undefined) return [\"main\", \"participant\"];\n if (!Array.isArray(targets) || targets.length === 0) {\n throw guidanceError(\"targets must contain main and/or participant\");\n }\n const normalized: FabricModelGuidanceTarget[] = [];\n for (const target of targets) {\n if (target !== \"main\" && target !== \"participant\") {\n throw guidanceError(`unsupported target ${String(target)}`);\n }\n if (!normalized.includes(target)) normalized.push(target);\n }\n return normalized;\n};\n\nexport const normalizeFabricModelGuidance = (\n guidance: FabricModelGuidance,\n): NormalizedFabricModelGuidance => {\n if (!guidance || typeof guidance !== \"object\" || Array.isArray(guidance)) {\n throw guidanceError(\"registration must be an object\");\n }\n const label = normalizedIdentifier(guidance.label, \"label\", MAX_LABEL_CHARS);\n if (!Array.isArray(guidance.models) || guidance.models.length === 0) {\n throw guidanceError(`${label} must select at least one provider/model pattern`);\n }\n if (guidance.models.length > MAX_MODEL_PATTERNS) {\n throw guidanceError(`${label} selects more than ${MAX_MODEL_PATTERNS} model patterns`);\n }\n const models: string[] = [];\n for (const candidate of guidance.models) {\n if (typeof candidate !== \"string\") {\n throw guidanceError(`${label} model patterns must be strings`);\n }\n const pattern = candidate.trim();\n if (!pattern || pattern.length > MAX_MODEL_PATTERN_CHARS || !pattern.includes(\"/\")) {\n throw guidanceError(\n `${label} model pattern ${JSON.stringify(candidate)} must be a provider/model glob of at most ${MAX_MODEL_PATTERN_CHARS} characters`,\n );\n }\n if (CONTROL_CHARACTER.test(pattern)) {\n throw guidanceError(`${label} model patterns must not contain control characters`);\n }\n if (!models.includes(pattern)) models.push(pattern);\n }\n if (typeof guidance.content !== \"string\" || !guidance.content.trim()) {\n throw guidanceError(`${label} content must be a non-empty string`);\n }\n const content = guidance.content.trim();\n if (content.length > MAX_FABRIC_MODEL_GUIDANCE_CONTENT_CHARS) {\n throw guidanceError(\n `${label} content exceeds ${MAX_FABRIC_MODEL_GUIDANCE_CONTENT_CHARS} characters`,\n );\n }\n const placement = guidance.placement ?? \"append\";\n if (placement !== \"append\" && placement !== \"replace\") {\n throw guidanceError(`${label} placement must be append or replace`);\n }\n const slot = guidance.slot === undefined\n ? undefined\n : normalizedIdentifier(guidance.slot, \"slot\", MAX_SLOT_CHARS);\n if (placement === \"replace\" && !slot) {\n throw guidanceError(`${label} replacement guidance requires a slot`);\n }\n if (placement === \"append\" && slot) {\n throw guidanceError(`${label} append guidance must not declare a slot`);\n }\n return {\n label,\n models,\n content,\n targets: normalizeTargets(guidance.targets),\n placement,\n ...(slot ? { slot } : {}),\n };\n};\n\nexport const parseFabricOwnedModelGuidance = (value: unknown): FabricOwnedModelGuidance[] => {\n if (!Array.isArray(value) || value.length > MAX_FABRIC_MODEL_GUIDANCE_REGISTRATIONS) return [];\n try {\n const parsed = value.map((candidate) => {\n if (!candidate || typeof candidate !== \"object\" || Array.isArray(candidate)) {\n throw guidanceError(\"serialized owner must be an object\");\n }\n const record = candidate as Partial<FabricOwnedModelGuidance>;\n const normalized = normalizeFabricModelGuidance(record as FabricModelGuidance);\n if (\n typeof record.componentId !== \"string\" ||\n !record.componentId.trim() ||\n typeof record.component !== \"string\" ||\n !record.component.trim() ||\n !Number.isSafeInteger(record.revision) ||\n (record.revision ?? 0) < 1\n ) {\n throw guidanceError(\"serialized owner metadata is incomplete\");\n }\n return {\n ...normalized,\n componentId: record.componentId.trim(),\n component: record.component.trim(),\n revision: record.revision!,\n };\n });\n if (parsed.reduce((sum, entry) => sum + entry.content.length, 0) >\n MAX_FABRIC_MODEL_GUIDANCE_SNAPSHOT_CHARS) return [];\n return parsed;\n } catch {\n return [];\n }\n};\n\nexport const fabricModelGuidanceInfo = (\n guidance: NormalizedFabricModelGuidance,\n): FabricModelGuidanceInfo => ({\n label: guidance.label,\n models: [...guidance.models],\n targets: [...guidance.targets],\n placement: guidance.placement,\n ...(guidance.slot ? { slot: guidance.slot } : {}),\n contentChars: guidance.content.length,\n contentHash: stableJsonHash(guidance.content),\n});\n\nconst wildcardMatch = (pattern: string, value: string): boolean => {\n let expression = \"^\";\n for (const character of pattern) {\n if (character === \"*\") expression += \".*\";\n else if (character === \"?\") expression += \".\";\n else expression += REGEX_CHARACTER.test(character) ? `\\\\${character}` : character;\n }\n return new RegExp(`${expression}$`, \"u\").test(value);\n};\n\nconst fabricModelGuidanceMatches = (\n guidance: NormalizedFabricModelGuidance,\n model: string,\n target: FabricModelGuidanceTarget,\n): boolean =>\n guidance.targets.includes(target) && guidance.models.some((pattern) => wildcardMatch(pattern, model));\n\nconst sourceInfo = (guidance: FabricOwnedModelGuidance) => ({\n componentId: guidance.componentId,\n component: guidance.component,\n label: guidance.label,\n placement: guidance.placement,\n ...(guidance.slot ? { slot: guidance.slot } : {}),\n contentHash: stableJsonHash(guidance.content),\n});\n\nexport const resolveFabricModelGuidance = (\n guidance: readonly FabricOwnedModelGuidance[],\n options: {\n model?: string;\n target: FabricModelGuidanceTarget;\n defaults?: readonly FabricGuidanceDefaultSlot[];\n includeSlots?: boolean;\n },\n): FabricResolvedModelGuidance => {\n const includeSlots = options.includeSlots !== false;\n const matching = options.model\n ? guidance.filter((entry) => fabricModelGuidanceMatches(entry, options.model!, options.target))\n : [];\n const replacements = new Map<string, FabricOwnedModelGuidance[]>();\n const additions: FabricOwnedModelGuidance[] = [];\n for (const entry of matching) {\n if (entry.placement === \"append\") {\n additions.push(entry);\n continue;\n }\n if (!includeSlots || !entry.slot) continue;\n const existing = replacements.get(entry.slot) ?? [];\n existing.push(entry);\n replacements.set(entry.slot, existing);\n }\n\n const defaultSlots = options.defaults ?? [];\n const slotOrder = [\n ...defaultSlots.map((entry) => entry.slot),\n ...[...replacements.keys()]\n .filter((slot) => !defaultSlots.some((entry) => entry.slot === slot))\n .sort(compareStableText),\n ];\n const slotSections: string[] = [];\n const sources: FabricResolvedModelGuidance[\"sources\"] = [];\n if (includeSlots) {\n for (const slot of slotOrder) {\n const candidates = replacements.get(slot) ?? [];\n if (candidates.length > 1) {\n const owners = candidates\n .map((entry) => `${entry.componentId}:${entry.label}`)\n .sort(compareStableText)\n .join(\", \");\n throw new Error(\n `Fabric guidance slot ${slot} has multiple replacements for ${options.model ?? \"the current model\"}: ${owners}`,\n );\n }\n const replacement = candidates[0];\n if (replacement) {\n slotSections.push(replacement.content);\n sources.push(sourceInfo(replacement));\n continue;\n }\n const fallback = defaultSlots.find((entry) => entry.slot === slot)?.content.trim();\n if (fallback) slotSections.push(fallback);\n }\n }\n\n additions.sort(compareFabricOwnedModelGuidance);\n for (const addition of additions) sources.push(sourceInfo(addition));\n const appendSections = additions.map((entry) => entry.content);\n const slotText = slotSections.join(\"\\n\\n\");\n const appendText = appendSections.join(\"\\n\\n\");\n const resolvedChars = slotText.length + appendText.length +\n (slotText && appendText ? 2 : 0);\n if (resolvedChars > MAX_FABRIC_MODEL_GUIDANCE_TOTAL_CHARS) {\n throw new Error(\n `Resolved Fabric model guidance exceeds ${MAX_FABRIC_MODEL_GUIDANCE_TOTAL_CHARS} characters for ${options.model ?? \"the current model\"}`,\n );\n }\n return {\n slotText,\n appendText,\n sources,\n digest: stableJsonHash({\n model: options.model,\n target: options.target,\n slots: slotSections,\n additions: sources,\n }),\n };\n};\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -12,20 +12,20 @@ import {
12
12
  buildThinkingDigest,
13
13
  snapshotHandoffSession,
14
14
  thinkingTransferPolicy
15
- } from "./chunk-UACA2AKF.js";
15
+ } from "./chunk-FL52NCFS.js";
16
16
  import {
17
17
  isFabricThinking
18
18
  } from "./chunk-7B55UIZL.js";
19
19
  import {
20
20
  NESTED_TOOL_CALL_ID_PREFIX
21
- } from "./chunk-OKQHLYTU.js";
21
+ } from "./chunk-LDZWE6OA.js";
22
22
  import {
23
23
  runAbortable,
24
24
  throwIfAborted
25
25
  } from "./chunk-7TB7NXSL.js";
26
26
  import {
27
27
  stableJsonHash
28
- } from "./chunk-22RY7UMR.js";
28
+ } from "./chunk-CIJPRYAP.js";
29
29
  import {
30
30
  FabricTraceSafeError
31
31
  } from "./chunk-IDTQ2B5W.js";
@@ -178,6 +178,7 @@ var queuePrewalkFollowUp = (extension, customType, content, details) => {
178
178
  };
179
179
  var prewalkTriggerField = (pending) => ({
180
180
  ref: pending.triggerRef,
181
+ ...pending.triggerSeq !== void 0 ? { seq: pending.triggerSeq } : {},
181
182
  ...pending.triggerFiles && pending.triggerFiles.length > 0 ? {
182
183
  files: pending.triggerFiles,
183
184
  ...pending.triggerFilesTruncated ? { truncated: pending.triggerFilesTruncated } : {}
@@ -207,7 +208,7 @@ var filterPrewalkContinuationMessages = (messages, accept) => {
207
208
  };
208
209
  var prewalkArmedPrompt = (mode, model) => [
209
210
  `Prewalk armed \u2192 ${model} (${mode}): the first successful pi.edit / pi.write / schema.commit \u2014 or file changes produced by shell commands \u2014 inside fabric_exec hands off to the executor automatically; ${mode === "trajectory" ? "the executor takes over the implementation there, and a hidden follow-up asks you to verify its work and summarize when it finishes." : `this session switches to ${model} and keeps working.`}`,
210
- "Reads never fire it; for multi-step work, restate the remaining steps before your first edit."
211
+ "Reads never fire it; trigger reports mark the handoff moment only \u2014 the workspace is the source of truth, so verify file state with reads before continuing. For multi-step work, restate the remaining steps before your first edit."
211
212
  ].join("\n");
212
213
  var customMessageText = (content) => {
213
214
  if (typeof content === "string") return content;
@@ -286,14 +287,15 @@ var buildPrewalkPending = (claim, resultFormat) => {
286
287
  startedAt: Date.now(),
287
288
  tool: inPlace ? "prewalk" : "handoff",
288
289
  provider: inPlace ? "fabric" : "agents",
289
- args
290
+ args: { ...args, seq: claim.seq }
290
291
  };
291
292
  return {
292
293
  kind: inPlace ? "prewalk-in-place" : "prewalk-trajectory",
293
294
  args,
294
295
  audit,
295
296
  resultFormat,
296
- triggerRef: claim.mutation.ref
297
+ triggerRef: claim.mutation.ref,
298
+ triggerSeq: claim.seq
297
299
  };
298
300
  };
299
301
  var modelForKey = (key, context) => {
@@ -1332,13 +1334,15 @@ var loadCachedMcpDescriptors = async (options) => {
1332
1334
  const cached = parseCachedServer(raw);
1333
1335
  if (!cached) continue;
1334
1336
  for (const tool of cached.tools) {
1337
+ const annotations = tool.annotations;
1335
1338
  descriptors.push({
1336
1339
  name: `${server}.${tool.name}`,
1337
1340
  description: tool.description ?? `${tool.name} on MCP server ${server}`,
1338
1341
  inputSchema: normalizeSchema(tool.inputSchema),
1339
1342
  ...tool.outputSchema ? { outputSchema: normalizeSchema(tool.outputSchema) } : {},
1340
1343
  risk: "network",
1341
- namespace: server
1344
+ namespace: server,
1345
+ ...annotations ? { annotations: { ...annotations } } : {}
1342
1346
  });
1343
1347
  }
1344
1348
  }
@@ -1976,6 +1980,7 @@ var normalizedTask = (value) => {
1976
1980
  var PrewalkController = class {
1977
1981
  #status = { state: "idle" };
1978
1982
  #settling = /* @__PURE__ */ new Set();
1983
+ #claimSeq = /* @__PURE__ */ new Map();
1979
1984
  status() {
1980
1985
  return structuredClone(this.#status);
1981
1986
  }
@@ -2106,8 +2111,9 @@ var PrewalkController = class {
2106
2111
  if (!mutation) return void 0;
2107
2112
  const arm = this.#snapshotArm();
2108
2113
  if (!arm) return void 0;
2114
+ const seq = this.#nextClaimSeq(sessionId);
2109
2115
  this.#status = { state: "handing_off", ...arm };
2110
- return { arm, mutation };
2116
+ return { arm, mutation, seq };
2111
2117
  }
2112
2118
  // Filesystem-fallback claim for writes audits cannot attribute (shell
2113
2119
  // heredocs, sed -i, formatter binaries). The drift file list rides on the
@@ -2124,8 +2130,14 @@ var PrewalkController = class {
2124
2130
  success: true,
2125
2131
  ...files.length > 0 ? { args: { files: [...files] } } : {}
2126
2132
  };
2133
+ const seq = this.#nextClaimSeq(sessionId);
2127
2134
  this.#status = { state: "handing_off", ...arm };
2128
- return { arm, mutation };
2135
+ return { arm, mutation, seq };
2136
+ }
2137
+ #nextClaimSeq(sessionId) {
2138
+ const seq = (this.#claimSeq.get(sessionId) ?? 0) + 1;
2139
+ this.#claimSeq.set(sessionId, seq);
2140
+ return seq;
2129
2141
  }
2130
2142
  #snapshotArm() {
2131
2143
  if (this.#status.state !== "armed") return void 0;
@@ -2148,7 +2160,8 @@ var PrewalkController = class {
2148
2160
 
2149
2161
  // src/prewalk/fs-drift.ts
2150
2162
  import { execFile } from "node:child_process";
2151
- import { readdir, stat } from "node:fs/promises";
2163
+ import { createHash } from "node:crypto";
2164
+ import { readFile, readdir, stat } from "node:fs/promises";
2152
2165
  import path5 from "node:path";
2153
2166
  import { promisify } from "node:util";
2154
2167
  var execFileAsync = promisify(execFile);
@@ -2156,6 +2169,8 @@ var DEFAULT_MAX_TRACKED_FILES = 2e5;
2156
2169
  var MAX_REPORT_FILES = 100;
2157
2170
  var GIT_TIMEOUT_MS = 1e4;
2158
2171
  var STAT_CONCURRENCY = 32;
2172
+ var MAX_HASH_FILES_PER_EVAL = 256;
2173
+ var HASH_MAX_BYTES = 16 << 20;
2159
2174
  var WALK_SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules"]);
2160
2175
  var listGitFiles = async (cwd) => {
2161
2176
  try {
@@ -2256,8 +2271,8 @@ var PrewalkDriftTracker = class {
2256
2271
  if (current.files.size === 0 && baseline.files.size > 0) return rebaseline();
2257
2272
  const files = [];
2258
2273
  let added = 0;
2259
- let modified = 0;
2260
2274
  let deleted = 0;
2275
+ const candidates = [];
2261
2276
  for (const [file, now] of current.files) {
2262
2277
  const before = baseline.files.get(file);
2263
2278
  if (!before) {
@@ -2266,8 +2281,7 @@ var PrewalkDriftTracker = class {
2266
2281
  continue;
2267
2282
  }
2268
2283
  if (before.size !== now.size || Math.abs(before.mtimeMs - now.mtimeMs) > 1e-6) {
2269
- modified += 1;
2270
- files.push(file);
2284
+ candidates.push({ file, before, entry: now });
2271
2285
  }
2272
2286
  }
2273
2287
  for (const file of baseline.files.keys()) {
@@ -2276,6 +2290,8 @@ var PrewalkDriftTracker = class {
2276
2290
  files.push(file);
2277
2291
  }
2278
2292
  }
2293
+ const verified = await this.#verifyModified(current.root, candidates);
2294
+ files.push(...verified.changed);
2279
2295
  this.#baselines.set(sessionId, current);
2280
2296
  if (files.length === 0) return void 0;
2281
2297
  const shown = files.slice(0, MAX_REPORT_FILES);
@@ -2283,9 +2299,51 @@ var PrewalkDriftTracker = class {
2283
2299
  files: shown,
2284
2300
  truncated: files.length - shown.length,
2285
2301
  added,
2286
- modified,
2287
- deleted
2302
+ modified: verified.changed.length,
2303
+ deleted,
2304
+ unchanged: verified.unchanged
2305
+ };
2306
+ }
2307
+ // Content-verify stat-modified files against the last hash this tracker
2308
+ // recorded for them. The first stat-drift on a file teaches the fresh
2309
+ // baseline its SHA-1 while still reporting the change; a later window whose
2310
+ // recomputed hash matches the recorded one is mtime-only churn and leaves
2311
+ // the report. Files past the per-evaluation hash budget or the per-file
2312
+ // byte cap stay in the report unrecorded — baseline reads stay free and
2313
+ // unverifiable drift fails open toward claiming.
2314
+ async #verifyModified(root, candidates) {
2315
+ const changed = [];
2316
+ let unchanged = 0;
2317
+ let hashBudget = MAX_HASH_FILES_PER_EVAL;
2318
+ let cursor = 0;
2319
+ const worker = async () => {
2320
+ while (cursor < candidates.length) {
2321
+ const candidate = candidates[cursor];
2322
+ cursor += 1;
2323
+ if (!candidate) continue;
2324
+ const { file, before, entry } = candidate;
2325
+ if (hashBudget <= 0 || entry.size > HASH_MAX_BYTES || before.size > HASH_MAX_BYTES) {
2326
+ changed.push(file);
2327
+ continue;
2328
+ }
2329
+ hashBudget -= 1;
2330
+ let sha1;
2331
+ try {
2332
+ sha1 = createHash("sha1").update(await readFile(path5.join(root, file))).digest("hex");
2333
+ } catch {
2334
+ changed.push(file);
2335
+ continue;
2336
+ }
2337
+ entry.sha1 = sha1;
2338
+ if (before.sha1 === sha1) {
2339
+ unchanged += 1;
2340
+ continue;
2341
+ }
2342
+ changed.push(file);
2343
+ }
2288
2344
  };
2345
+ await Promise.all(Array.from({ length: STAT_CONCURRENCY }, () => worker()));
2346
+ return { changed, unchanged };
2289
2347
  }
2290
2348
  drop(sessionId) {
2291
2349
  this.#baselines.delete(sessionId);
@@ -2458,4 +2516,4 @@ export {
2458
2516
  resolveFabricIdentity,
2459
2517
  MainAgentController
2460
2518
  };
2461
- //# sourceMappingURL=chunk-EBGSP7Q7.js.map
2519
+ //# sourceMappingURL=chunk-ASBRTAJ4.js.map