@orkestrel/probe 0.0.1 → 0.0.3

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.
@@ -187,6 +187,43 @@ export declare interface Inspection {
187
187
  readonly claim: Claim;
188
188
  }
189
189
 
190
+ /**
191
+ * Reports whether a fault means the host refuses the name a caller supplied for a file to create.
192
+ *
193
+ * @remarks
194
+ * A host reports one refusal with a code of its own and several unrelated conditions with one
195
+ * shared code, so the code alone does not separate a name the host will not accept from a file
196
+ * that is merely absent. `ENAMETOOLONG` names the refusal outright. `ERR_INVALID_ARG_VALUE` names
197
+ * every argument Node rejects, so it names the refusal only for a path carrying the NUL byte Node
198
+ * refuses; the same code arrives for a bad `flag` and a bad encoding, which say nothing about the
199
+ * name. An `ENOENT` raised while the parent stats as a directory names the refusal too, because an
200
+ * ordinary absent file beneath an existing directory would have been created instead; a Windows
201
+ * host reports an overlong component and a character its filesystem reserves that way. Measured on
202
+ * 2026-08-21 on Windows 11 with Node v24.18.1 over NTFS, `writeFileSync` reported `ENOENT` for a
203
+ * 300-character final component and never reported `ENAMETOOLONG`.
204
+ *
205
+ * This reads the fault the host reported rather than applying a length or character policy of its
206
+ * own, so a host that accepts a name another host refuses is not second-guessed and no platform
207
+ * branch decides the answer. Every read of the fault is guarded, so it is total over every value a
208
+ * published caller can supply: a proxy whose `has` trap throws, a value whose `code` getter throws,
209
+ * a fault carrying no code, and a parent this host cannot stat each report false.
210
+ *
211
+ * @param file - The path whose creation failed
212
+ * @param error - The fault the failed operation raised
213
+ * @returns True when the fault means the host refuses this name; false otherwise
214
+ *
215
+ * @example
216
+ * ```ts
217
+ * try {
218
+ * writeFileSync('tmp/probe/greeting\0.test.ts', '')
219
+ * } catch (error) {
220
+ * isRefusedName('tmp/probe/greeting\0.test.ts', error) // true
221
+ * }
222
+ * isRefusedName('tmp/probe/greeting.test.ts', new Error('the runtime stage was destroyed')) // false
223
+ * ```
224
+ */
225
+ export declare function isRefusedName(file: string, error: unknown): boolean;
226
+
190
227
  /**
191
228
  * Inspects virtual documents through one resident Oxlint language server.
192
229
  *
@@ -728,11 +765,12 @@ export declare function resolveWorkspaceModule(workspace: string, specifier: str
728
765
  * as a path string, into which the stage can install no overlay, and a specification path that
729
766
  * crosses a symbolic link, whose existing components this host cannot inspect, or whose directory
730
767
  * the target tree blocks probe from creating. An `origin: 'instrument'` issue names this package's
731
- * own machinery: a specification it could not write after its directory exists, or one it could
732
- * not run, evict, or delete, in each case for a reason the target tree does not own, and a run
733
- * that reported no test. An unmapped caller test and a test whose named project is absent are claimant failures with
734
- * `code: 'missing'`, thrown rather than reported, because a test that never ran is no evidence
735
- * about the candidate.
768
+ * own machinery: a specification it could not write after its directory exists and the host did not
769
+ * refuse the caller's own name, or one it could not run, evict, or delete, in each case for a
770
+ * reason the target tree does not own, and a run that reported no test. An unmapped caller test and
771
+ * a test whose named project is absent are claimant failures with `code: 'missing'`, and a declared
772
+ * test path the host refuses to create is a claimant failure with `code: 'refused'`. Each is thrown
773
+ * rather than reported, because a test that never ran is no evidence about the candidate.
736
774
  *
737
775
  * The write path's physical-containment guarantee covers the claim inputs and the target tree as
738
776
  * inspected. A concurrent process that mutates a path component between the final inspection and
@@ -187,6 +187,43 @@ export declare interface Inspection {
187
187
  readonly claim: Claim;
188
188
  }
189
189
 
190
+ /**
191
+ * Reports whether a fault means the host refuses the name a caller supplied for a file to create.
192
+ *
193
+ * @remarks
194
+ * A host reports one refusal with a code of its own and several unrelated conditions with one
195
+ * shared code, so the code alone does not separate a name the host will not accept from a file
196
+ * that is merely absent. `ENAMETOOLONG` names the refusal outright. `ERR_INVALID_ARG_VALUE` names
197
+ * every argument Node rejects, so it names the refusal only for a path carrying the NUL byte Node
198
+ * refuses; the same code arrives for a bad `flag` and a bad encoding, which say nothing about the
199
+ * name. An `ENOENT` raised while the parent stats as a directory names the refusal too, because an
200
+ * ordinary absent file beneath an existing directory would have been created instead; a Windows
201
+ * host reports an overlong component and a character its filesystem reserves that way. Measured on
202
+ * 2026-08-21 on Windows 11 with Node v24.18.1 over NTFS, `writeFileSync` reported `ENOENT` for a
203
+ * 300-character final component and never reported `ENAMETOOLONG`.
204
+ *
205
+ * This reads the fault the host reported rather than applying a length or character policy of its
206
+ * own, so a host that accepts a name another host refuses is not second-guessed and no platform
207
+ * branch decides the answer. Every read of the fault is guarded, so it is total over every value a
208
+ * published caller can supply: a proxy whose `has` trap throws, a value whose `code` getter throws,
209
+ * a fault carrying no code, and a parent this host cannot stat each report false.
210
+ *
211
+ * @param file - The path whose creation failed
212
+ * @param error - The fault the failed operation raised
213
+ * @returns True when the fault means the host refuses this name; false otherwise
214
+ *
215
+ * @example
216
+ * ```ts
217
+ * try {
218
+ * writeFileSync('tmp/probe/greeting\0.test.ts', '')
219
+ * } catch (error) {
220
+ * isRefusedName('tmp/probe/greeting\0.test.ts', error) // true
221
+ * }
222
+ * isRefusedName('tmp/probe/greeting.test.ts', new Error('the runtime stage was destroyed')) // false
223
+ * ```
224
+ */
225
+ export declare function isRefusedName(file: string, error: unknown): boolean;
226
+
190
227
  /**
191
228
  * Inspects virtual documents through one resident Oxlint language server.
192
229
  *
@@ -728,11 +765,12 @@ export declare function resolveWorkspaceModule(workspace: string, specifier: str
728
765
  * as a path string, into which the stage can install no overlay, and a specification path that
729
766
  * crosses a symbolic link, whose existing components this host cannot inspect, or whose directory
730
767
  * the target tree blocks probe from creating. An `origin: 'instrument'` issue names this package's
731
- * own machinery: a specification it could not write after its directory exists, or one it could
732
- * not run, evict, or delete, in each case for a reason the target tree does not own, and a run
733
- * that reported no test. An unmapped caller test and a test whose named project is absent are claimant failures with
734
- * `code: 'missing'`, thrown rather than reported, because a test that never ran is no evidence
735
- * about the candidate.
768
+ * own machinery: a specification it could not write after its directory exists and the host did not
769
+ * refuse the caller's own name, or one it could not run, evict, or delete, in each case for a
770
+ * reason the target tree does not own, and a run that reported no test. An unmapped caller test and
771
+ * a test whose named project is absent are claimant failures with `code: 'missing'`, and a declared
772
+ * test path the host refuses to create is a claimant failure with `code: 'refused'`. Each is thrown
773
+ * rather than reported, because a test that never ran is no evidence about the candidate.
736
774
  *
737
775
  * The write path's physical-containment guarantee covers the claim inputs and the target tree as
738
776
  * inspected. A concurrent process that mutates a path component between the final inspection and
@@ -106,6 +106,53 @@ function resolveWorkspaceFile(workspace, target, mutate = false) {
106
106
  return file;
107
107
  }
108
108
  /**
109
+ * Reports whether a fault means the host refuses the name a caller supplied for a file to create.
110
+ *
111
+ * @remarks
112
+ * A host reports one refusal with a code of its own and several unrelated conditions with one
113
+ * shared code, so the code alone does not separate a name the host will not accept from a file
114
+ * that is merely absent. `ENAMETOOLONG` names the refusal outright. `ERR_INVALID_ARG_VALUE` names
115
+ * every argument Node rejects, so it names the refusal only for a path carrying the NUL byte Node
116
+ * refuses; the same code arrives for a bad `flag` and a bad encoding, which say nothing about the
117
+ * name. An `ENOENT` raised while the parent stats as a directory names the refusal too, because an
118
+ * ordinary absent file beneath an existing directory would have been created instead; a Windows
119
+ * host reports an overlong component and a character its filesystem reserves that way. Measured on
120
+ * 2026-08-21 on Windows 11 with Node v24.18.1 over NTFS, `writeFileSync` reported `ENOENT` for a
121
+ * 300-character final component and never reported `ENAMETOOLONG`.
122
+ *
123
+ * This reads the fault the host reported rather than applying a length or character policy of its
124
+ * own, so a host that accepts a name another host refuses is not second-guessed and no platform
125
+ * branch decides the answer. Every read of the fault is guarded, so it is total over every value a
126
+ * published caller can supply: a proxy whose `has` trap throws, a value whose `code` getter throws,
127
+ * a fault carrying no code, and a parent this host cannot stat each report false.
128
+ *
129
+ * @param file - The path whose creation failed
130
+ * @param error - The fault the failed operation raised
131
+ * @returns True when the fault means the host refuses this name; false otherwise
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * try {
136
+ * writeFileSync('tmp/probe/greeting\0.test.ts', '')
137
+ * } catch (error) {
138
+ * isRefusedName('tmp/probe/greeting\0.test.ts', error) // true
139
+ * }
140
+ * isRefusedName('tmp/probe/greeting.test.ts', new Error('the runtime stage was destroyed')) // false
141
+ * ```
142
+ */
143
+ function isRefusedName(file, error) {
144
+ if (typeof error !== "object" || error === null) return false;
145
+ const fault = error;
146
+ const reading = attempt(() => "code" in fault ? fault.code : void 0);
147
+ if (!reading.success) return false;
148
+ const code = reading.value;
149
+ if (code === "ENAMETOOLONG") return true;
150
+ if (code === "ERR_INVALID_ARG_VALUE") return file.includes("\0");
151
+ if (code !== "ENOENT") return false;
152
+ const parent = attempt(() => statSync(dirname(file), { throwIfNoEntry: false }));
153
+ return parent.success && parent.value !== void 0 && parent.value.isDirectory();
154
+ }
155
+ /**
109
156
  * Projects an absolute tool path into the workspace-relative form issues expose.
110
157
  *
111
158
  * @param workspace - The target workspace root
@@ -638,11 +685,11 @@ var Overlay = class {
638
685
  };
639
686
  //#endregion
640
687
  //#region package.json
641
- var version = "0.0.1";
688
+ var version = "0.0.3";
642
689
  var peerDependencies = {
643
- "oxlint": "^1.77.0",
644
- "typescript": "^6.0.0",
645
- "vitest": "^4.1.0"
690
+ "oxlint": "^1.79.0",
691
+ "typescript": "^6.0.3",
692
+ "vitest": "^4.1.11"
646
693
  };
647
694
  //#endregion
648
695
  //#region src/server/stages/LintStage.ts
@@ -1026,11 +1073,12 @@ var LintStage = class {
1026
1073
  * as a path string, into which the stage can install no overlay, and a specification path that
1027
1074
  * crosses a symbolic link, whose existing components this host cannot inspect, or whose directory
1028
1075
  * the target tree blocks probe from creating. An `origin: 'instrument'` issue names this package's
1029
- * own machinery: a specification it could not write after its directory exists, or one it could
1030
- * not run, evict, or delete, in each case for a reason the target tree does not own, and a run
1031
- * that reported no test. An unmapped caller test and a test whose named project is absent are claimant failures with
1032
- * `code: 'missing'`, thrown rather than reported, because a test that never ran is no evidence
1033
- * about the candidate.
1076
+ * own machinery: a specification it could not write after its directory exists and the host did not
1077
+ * refuse the caller's own name, or one it could not run, evict, or delete, in each case for a
1078
+ * reason the target tree does not own, and a run that reported no test. An unmapped caller test and
1079
+ * a test whose named project is absent are claimant failures with `code: 'missing'`, and a declared
1080
+ * test path the host refuses to create is a claimant failure with `code: 'refused'`. Each is thrown
1081
+ * rather than reported, because a test that never ran is no evidence about the candidate.
1034
1082
  *
1035
1083
  * The write path's physical-containment guarantee covers the claim inputs and the target tree as
1036
1084
  * inspected. A concurrent process that mutates a path component between the final inspection and
@@ -1093,7 +1141,7 @@ var RuntimeStage = class {
1093
1141
  elapsed: Math.round(performance.now() - started),
1094
1142
  issues: [specification]
1095
1143
  };
1096
- const file = specification;
1144
+ const file = sep === "\\" ? normalizePath(specification) : specification;
1097
1145
  this.#specifications += 1;
1098
1146
  this.#revisions.add(file);
1099
1147
  let issues = [];
@@ -1243,9 +1291,11 @@ var RuntimeStage = class {
1243
1291
  }
1244
1292
  #specification(test) {
1245
1293
  let creating = false;
1294
+ let generated;
1246
1295
  const outcome = attempt(() => {
1247
1296
  const revision = `${process.pid}-${randomUUID()}`;
1248
1297
  const file = createRevisionFile(this.#workspace, test.path, revision);
1298
+ generated = file;
1249
1299
  const target = relative(this.#workspace, file);
1250
1300
  resolveWorkspaceFile(this.#workspace, target, true);
1251
1301
  creating = true;
@@ -1268,6 +1318,15 @@ var RuntimeStage = class {
1268
1318
  },
1269
1319
  cause: outcome.error.cause
1270
1320
  });
1321
+ if (!creating && generated !== void 0 && isRefusedName(generated, outcome.error)) throw new ProbeError(`The runtime stage cannot write a specification beside a test path the host refuses (${describeUnknown(outcome.error)})`, {
1322
+ origin: "claimant",
1323
+ code: "refused",
1324
+ context: {
1325
+ stage: this.stage,
1326
+ path: test.path
1327
+ },
1328
+ cause: outcome.error
1329
+ });
1271
1330
  return {
1272
1331
  origin: outcome.error instanceof ProbeError && outcome.error.origin === "workspace" || creating ? "workspace" : "instrument",
1273
1332
  path: test.path,
@@ -1366,8 +1425,9 @@ var RuntimeStage = class {
1366
1425
  for (const [path, digest] of modules) this.#modules.set(path, digest);
1367
1426
  }
1368
1427
  #invalidate(vitest, path) {
1369
- vitest.invalidateFile(path);
1370
- vitest.watcher.invalidates.add(path);
1428
+ const id = sep === "\\" ? normalizePath(path) : path;
1429
+ vitest.invalidateFile(id);
1430
+ vitest.watcher.invalidates.add(id);
1371
1431
  }
1372
1432
  #snapshot() {
1373
1433
  const modules = /* @__PURE__ */ new Map();
@@ -1717,8 +1777,9 @@ var TypeStage = class {
1717
1777
  const path = resolveWorkspaceFile(this.#workspace, project);
1718
1778
  const existing = this.#services.get(path);
1719
1779
  if (existing !== void 0) return existing;
1720
- const config = typescript.readConfigFile(path, typescript.sys.readFile);
1721
- if (config.error !== void 0) throw new ProbeError(typescript.flattenDiagnosticMessageText(config.error.messageText, "\n"), {
1780
+ const spelling = normalizePath(path);
1781
+ const config = typescript.readConfigFile(spelling, typescript.sys.readFile);
1782
+ if (config.error !== void 0) throw new ProbeError(this.#translate(typescript, config.error.messageText, path), {
1722
1783
  origin: "workspace",
1723
1784
  code: "malformed",
1724
1785
  context: {
@@ -1726,8 +1787,8 @@ var TypeStage = class {
1726
1787
  project
1727
1788
  }
1728
1789
  });
1729
- const parsed = typescript.parseJsonConfigFileContent(config.config, typescript.sys, dirname(path), void 0, path);
1730
- if (parsed.errors.length > 0) throw new ProbeError(typescript.flattenDiagnosticMessageText(parsed.errors[0]?.messageText ?? "", "\n"), {
1790
+ const parsed = typescript.parseJsonConfigFileContent(config.config, typescript.sys, dirname(spelling), void 0, spelling);
1791
+ if (parsed.errors.length > 0) throw new ProbeError(this.#translate(typescript, parsed.errors[0]?.messageText, path), {
1731
1792
  origin: "workspace",
1732
1793
  code: "malformed",
1733
1794
  context: {
@@ -1737,25 +1798,56 @@ var TypeStage = class {
1737
1798
  });
1738
1799
  this.#options.set(path, parsed.options);
1739
1800
  this.#files.set(path, parsed.fileNames);
1740
- const service = typescript.createLanguageService({
1741
- getCompilationSettings: () => this.#options.get(path) ?? {},
1742
- getScriptFileNames: () => [...this.#files.get(path) ?? [], ...this.#overlay.paths],
1743
- getScriptVersion: (file) => this.#version(file),
1744
- getScriptSnapshot: (file) => this.#snapshot(typescript, file),
1745
- getCurrentDirectory: () => this.#workspace,
1746
- getDefaultLibFileName: (options) => typescript.getDefaultLibFilePath(options),
1747
- fileExists: (file) => this.#overlay.text(file) !== void 0 || typescript.sys.fileExists(file),
1748
- readFile: (file) => this.#overlay.text(file) ?? typescript.sys.readFile(file),
1801
+ const host = {
1802
+ getCompilationSettings: this.#compilationSettings.bind(this, path),
1803
+ getScriptFileNames: this.#scriptFiles.bind(this, path),
1804
+ getScriptVersion: this.#version.bind(this),
1805
+ getScriptSnapshot: this.#snapshot.bind(this, typescript),
1806
+ getCurrentDirectory: this.#directory.bind(this),
1807
+ getDefaultLibFileName: this.#defaultLibrary.bind(this, typescript),
1808
+ fileExists: this.#fileExists.bind(this, typescript),
1809
+ readFile: this.#readFile.bind(this, typescript),
1749
1810
  readDirectory: typescript.sys.readDirectory,
1750
- directoryExists: (directory) => typescript.sys.directoryExists(directory) || this.#overlay.covers(directory),
1811
+ directoryExists: this.#directoryExists.bind(this, typescript),
1751
1812
  getDirectories: typescript.sys.getDirectories,
1752
- useCaseSensitiveFileNames: () => typescript.sys.useCaseSensitiveFileNames,
1753
- getNewLine: () => typescript.sys.newLine
1754
- });
1813
+ useCaseSensitiveFileNames: this.#caseSensitive.bind(this, typescript),
1814
+ getNewLine: this.#newline.bind(this, typescript)
1815
+ };
1816
+ const service = typescript.createLanguageService(host);
1755
1817
  this.#services.set(path, service);
1756
1818
  if (!this.#resident.has(path)) this.#recycle(path);
1757
1819
  return service;
1758
1820
  }
1821
+ #compilationSettings(path) {
1822
+ return this.#options.get(path) ?? {};
1823
+ }
1824
+ #scriptFiles(path) {
1825
+ return [...this.#files.get(path) ?? [], ...this.#overlay.paths];
1826
+ }
1827
+ #directory() {
1828
+ return this.#workspace;
1829
+ }
1830
+ #defaultLibrary(typescript, options) {
1831
+ return typescript.getDefaultLibFilePath(options);
1832
+ }
1833
+ #fileExists(typescript, file) {
1834
+ return this.#overlay.text(file) !== void 0 || typescript.sys.fileExists(file);
1835
+ }
1836
+ #readFile(typescript, file) {
1837
+ return this.#overlay.text(file) ?? typescript.sys.readFile(file);
1838
+ }
1839
+ #directoryExists(typescript, directory) {
1840
+ return typescript.sys.directoryExists(directory) || this.#overlay.covers(directory);
1841
+ }
1842
+ #caseSensitive(typescript) {
1843
+ return typescript.sys.useCaseSensitiveFileNames;
1844
+ }
1845
+ #newline(typescript) {
1846
+ return typescript.sys.newLine;
1847
+ }
1848
+ #translate(typescript, message, path) {
1849
+ return normalizePath(typescript.flattenDiagnosticMessageText(message, "\n")).replaceAll(normalizePath(path), relativeWorkspaceFile(this.#workspace, path));
1850
+ }
1759
1851
  #configure(service, project) {
1760
1852
  const path = resolveWorkspaceFile(this.#workspace, project);
1761
1853
  if (this.#diagnostics.has(path)) return;
@@ -1882,17 +1974,17 @@ var Probe = class {
1882
1974
  this.#typeQueue = createQueue({
1883
1975
  concurrency: 1,
1884
1976
  retries: 0,
1885
- handler: (inspection) => this.#inspectStage(this.#type, this.#type.progress, this.#type.inspect(inspection.subject, inspection.claim.project), inspection.claim)
1977
+ handler: this.#inspectType.bind(this)
1886
1978
  });
1887
1979
  this.#lintQueue = createQueue({
1888
1980
  concurrency: 1,
1889
1981
  retries: 0,
1890
- handler: (inspection) => this.#inspectStage(this.#lint, this.#lint.progress, this.#lint.inspect(inspection.subject), inspection.claim)
1982
+ handler: this.#inspectLint.bind(this)
1891
1983
  });
1892
1984
  this.#runtimeQueue = createQueue({
1893
1985
  concurrency: 1,
1894
1986
  retries: 0,
1895
- handler: (inspection) => this.#inspectStage(this.#runtime, this.#runtime.progress, this.#runtime.inspect(inspection.subject), inspection.claim)
1987
+ handler: this.#inspectRuntime.bind(this)
1896
1988
  });
1897
1989
  this.#arming = this.#arm();
1898
1990
  this.#arming.catch(() => {});
@@ -2092,6 +2184,15 @@ var Probe = class {
2092
2184
  for (const pending of admitted) pending.catch(() => {});
2093
2185
  return Promise.all(admitted);
2094
2186
  }
2187
+ #inspectType(inspection) {
2188
+ return this.#inspectStage(this.#type, this.#type.progress, this.#type.inspect(inspection.subject, inspection.claim.project), inspection.claim);
2189
+ }
2190
+ #inspectLint(inspection) {
2191
+ return this.#inspectStage(this.#lint, this.#lint.progress, this.#lint.inspect(inspection.subject), inspection.claim);
2192
+ }
2193
+ #inspectRuntime(inspection) {
2194
+ return this.#inspectStage(this.#runtime, this.#runtime.progress, this.#runtime.inspect(inspection.subject), inspection.claim);
2195
+ }
2095
2196
  async #inspectStage(stage, progress, operation, claim) {
2096
2197
  const timeout = createTimeout({ ms: this.#deadline });
2097
2198
  timeout.start();
@@ -2276,19 +2377,9 @@ var ProbeServer = class {
2276
2377
  const tools = createToolManager();
2277
2378
  tools.add(createTool({
2278
2379
  name: "prove",
2279
- description: "Proves a claim with type, lint, and runtime evidence.",
2380
+ description: "Answers whether a TypeScript edit compiles, lints, and passes its test in this workspace — call it before relying on such a claim, instead of reasoning or writing a throwaway probe. Supply the edit you believe is correct and an edit that must break with the stage it breaks at; the closing line is a receipt only when the case ran clean and the control broke exactly at its declared stage, and no receipt otherwise. For a performance claim, measure before proving: write a guarded bench block — if (import.meta.env.MODE === 'benchmark') — beside the probe test, run the workspace bench script, and record only magnitudes; a settled ratio then proves here as an ordinary runtime claim.",
2280
2381
  parameters,
2281
- execute: async (input) => {
2282
- if (!isClaim(input)) {
2283
- const refused = findRefusedPaths(input);
2284
- throw new ProbeError(refused.length === 0 ? "The prove tool requires a claim matching the advertised schema" : `The prove tool refuses ${refused.join(", ")}: a draft path must stay inside the workspace, which the advertised schema does not constrain`, {
2285
- origin: "claimant",
2286
- code: "refused",
2287
- context: { value: input }
2288
- });
2289
- }
2290
- return this.#probe.prove(input);
2291
- }
2382
+ execute: this.#prove.bind(this)
2292
2383
  }));
2293
2384
  return createMCPServer({
2294
2385
  identity: {
@@ -2296,26 +2387,38 @@ var ProbeServer = class {
2296
2387
  version
2297
2388
  },
2298
2389
  tools,
2299
- execution: async ({ call }) => {
2300
- const result = await tools.execute(call);
2301
- if (!result.success) return result;
2302
- if (!isVerdict(result.value)) throw new ProbeError("The prove tool returned an invalid verdict", {
2303
- origin: "instrument",
2304
- code: "malformed",
2305
- context: { value: result.value }
2306
- });
2307
- return {
2308
- resultType: "complete",
2309
- content: [{
2310
- type: "text",
2311
- text: formatVerdict(result.value)
2312
- }]
2313
- };
2314
- }
2390
+ execution: this.#execute.bind(this)
2315
2391
  });
2316
2392
  }
2393
+ async #prove(input) {
2394
+ if (!isClaim(input)) {
2395
+ const refused = findRefusedPaths(input);
2396
+ throw new ProbeError(refused.length === 0 ? "The prove tool requires a claim matching the advertised schema" : `The prove tool refuses ${refused.join(", ")}: a draft path must stay inside the workspace, which the advertised schema does not constrain`, {
2397
+ origin: "claimant",
2398
+ code: "refused",
2399
+ context: { value: input }
2400
+ });
2401
+ }
2402
+ return this.#probe.prove(input);
2403
+ }
2404
+ async #execute(context) {
2405
+ const result = await context.tools.execute(context.call);
2406
+ if (!result.success) return result;
2407
+ if (!isVerdict(result.value)) throw new ProbeError("The prove tool returned an invalid verdict", {
2408
+ origin: "instrument",
2409
+ code: "malformed",
2410
+ context: { value: result.value }
2411
+ });
2412
+ return {
2413
+ resultType: "complete",
2414
+ content: [{
2415
+ type: "text",
2416
+ text: formatVerdict(result.value)
2417
+ }]
2418
+ };
2419
+ }
2317
2420
  };
2318
2421
  //#endregion
2319
- export { LintStage, Overlay, Probe, ProbeServer, RuntimeStage, TypeStage, captureListeners, computeDigest, createRevisionFile, describeUnknown, findRefusedPaths, inferDocumentLanguage, inferTestProject, inferTypeProject, loadWorkspaceModule, matchesWorkspaceModule, normalizePath, normalizeValue, parseContentLength, readWorkspaceManifest, relativeWorkspaceFile, releaseListeners, resolveWorkspaceBinary, resolveWorkspaceFile, resolveWorkspaceModule };
2422
+ export { LintStage, Overlay, Probe, ProbeServer, RuntimeStage, TypeStage, captureListeners, computeDigest, createRevisionFile, describeUnknown, findRefusedPaths, inferDocumentLanguage, inferTestProject, inferTypeProject, isRefusedName, loadWorkspaceModule, matchesWorkspaceModule, normalizePath, normalizeValue, parseContentLength, readWorkspaceManifest, relativeWorkspaceFile, releaseListeners, resolveWorkspaceBinary, resolveWorkspaceFile, resolveWorkspaceModule };
2320
2423
 
2321
2424
  //# sourceMappingURL=index.js.map