@descryy/mcp 0.11.5 → 0.11.6

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 (445) hide show
  1. package/dist/action-handshake.d.ts +38 -0
  2. package/dist/action-handshake.d.ts.map +1 -0
  3. package/dist/action-handshake.js +127 -0
  4. package/dist/action-handshake.js.map +1 -0
  5. package/dist/bin/descry-mcp.d.ts +7 -0
  6. package/dist/bin/descry-mcp.d.ts.map +1 -0
  7. package/dist/bin/descry-mcp.js +73 -0
  8. package/dist/bin/descry-mcp.js.map +1 -0
  9. package/dist/browser/auth.d.ts +84 -0
  10. package/dist/browser/auth.d.ts.map +1 -0
  11. package/dist/browser/auth.js +233 -0
  12. package/dist/browser/auth.js.map +1 -0
  13. package/dist/browser/capability-probe.d.ts +109 -0
  14. package/dist/browser/capability-probe.d.ts.map +1 -0
  15. package/dist/browser/capability-probe.js +201 -0
  16. package/dist/browser/capability-probe.js.map +1 -0
  17. package/dist/browser/driver.d.ts +591 -0
  18. package/dist/browser/driver.d.ts.map +1 -0
  19. package/dist/browser/driver.js +116 -0
  20. package/dist/browser/driver.js.map +1 -0
  21. package/dist/browser/evidence.d.ts +28 -0
  22. package/dist/browser/evidence.d.ts.map +1 -0
  23. package/dist/browser/evidence.js +82 -0
  24. package/dist/browser/evidence.js.map +1 -0
  25. package/dist/browser/fake-driver.d.ts +116 -0
  26. package/dist/browser/fake-driver.d.ts.map +1 -0
  27. package/dist/browser/fake-driver.js +416 -0
  28. package/dist/browser/fake-driver.js.map +1 -0
  29. package/dist/browser/fault-attribution.d.ts +178 -0
  30. package/dist/browser/fault-attribution.d.ts.map +1 -0
  31. package/dist/browser/fault-attribution.js +266 -0
  32. package/dist/browser/fault-attribution.js.map +1 -0
  33. package/dist/browser/graph-write.d.ts +45 -0
  34. package/dist/browser/graph-write.d.ts.map +1 -0
  35. package/dist/browser/graph-write.js +103 -0
  36. package/dist/browser/graph-write.js.map +1 -0
  37. package/dist/browser/identity-graph-write.d.ts +55 -0
  38. package/dist/browser/identity-graph-write.d.ts.map +1 -0
  39. package/dist/browser/identity-graph-write.js +45 -0
  40. package/dist/browser/identity-graph-write.js.map +1 -0
  41. package/dist/browser/page-probe.d.ts +81 -0
  42. package/dist/browser/page-probe.d.ts.map +1 -0
  43. package/dist/browser/page-probe.js +202 -0
  44. package/dist/browser/page-probe.js.map +1 -0
  45. package/dist/browser/playwright-driver.d.ts +115 -0
  46. package/dist/browser/playwright-driver.d.ts.map +1 -0
  47. package/dist/browser/playwright-driver.js +1545 -0
  48. package/dist/browser/playwright-driver.js.map +1 -0
  49. package/dist/browser/provider.d.ts +26 -0
  50. package/dist/browser/provider.d.ts.map +1 -0
  51. package/dist/browser/provider.js +14 -0
  52. package/dist/browser/provider.js.map +1 -0
  53. package/dist/browser/reach-recording-session.d.ts +44 -0
  54. package/dist/browser/reach-recording-session.d.ts.map +1 -0
  55. package/dist/browser/reach-recording-session.js +151 -0
  56. package/dist/browser/reach-recording-session.js.map +1 -0
  57. package/dist/browser/reachability.d.ts +168 -0
  58. package/dist/browser/reachability.d.ts.map +1 -0
  59. package/dist/browser/reachability.js +294 -0
  60. package/dist/browser/reachability.js.map +1 -0
  61. package/dist/browser/registry.d.ts +126 -0
  62. package/dist/browser/registry.d.ts.map +1 -0
  63. package/dist/browser/registry.js +177 -0
  64. package/dist/browser/registry.js.map +1 -0
  65. package/dist/browser/scenario-provenance.d.ts +23 -0
  66. package/dist/browser/scenario-provenance.d.ts.map +1 -0
  67. package/dist/browser/scenario-provenance.js +73 -0
  68. package/dist/browser/scenario-provenance.js.map +1 -0
  69. package/dist/browser/scenario-resolve.d.ts +28 -0
  70. package/dist/browser/scenario-resolve.d.ts.map +1 -0
  71. package/dist/browser/scenario-resolve.js +96 -0
  72. package/dist/browser/scenario-resolve.js.map +1 -0
  73. package/dist/browser/scenario-runner.d.ts +72 -0
  74. package/dist/browser/scenario-runner.d.ts.map +1 -0
  75. package/dist/browser/scenario-runner.js +353 -0
  76. package/dist/browser/scenario-runner.js.map +1 -0
  77. package/dist/browser/stack-parser.d.ts +19 -0
  78. package/dist/browser/stack-parser.d.ts.map +1 -0
  79. package/dist/browser/stack-parser.js +85 -0
  80. package/dist/browser/stack-parser.js.map +1 -0
  81. package/dist/browser/tool-support.d.ts +71 -0
  82. package/dist/browser/tool-support.d.ts.map +1 -0
  83. package/dist/browser/tool-support.js +216 -0
  84. package/dist/browser/tool-support.js.map +1 -0
  85. package/dist/browser/url-scheme.d.ts +14 -0
  86. package/dist/browser/url-scheme.d.ts.map +1 -0
  87. package/dist/browser/url-scheme.js +38 -0
  88. package/dist/browser/url-scheme.js.map +1 -0
  89. package/dist/browser/wait-target.d.ts +37 -0
  90. package/dist/browser/wait-target.d.ts.map +1 -0
  91. package/dist/browser/wait-target.js +54 -0
  92. package/dist/browser/wait-target.js.map +1 -0
  93. package/dist/cancellation.d.ts +20 -0
  94. package/dist/cancellation.d.ts.map +1 -0
  95. package/dist/cancellation.js +41 -0
  96. package/dist/cancellation.js.map +1 -0
  97. package/dist/capped.d.ts +39 -0
  98. package/dist/capped.d.ts.map +1 -0
  99. package/dist/capped.js +43 -0
  100. package/dist/capped.js.map +1 -0
  101. package/dist/credential-store.d.ts +22 -0
  102. package/dist/credential-store.d.ts.map +1 -0
  103. package/dist/credential-store.js +33 -0
  104. package/dist/credential-store.js.map +1 -0
  105. package/dist/disclosure-ledger.d.ts +14 -0
  106. package/dist/disclosure-ledger.d.ts.map +1 -0
  107. package/dist/disclosure-ledger.js +18 -0
  108. package/dist/disclosure-ledger.js.map +1 -0
  109. package/dist/evidence/observed-route-evidence.d.ts +25 -0
  110. package/dist/evidence/observed-route-evidence.d.ts.map +1 -0
  111. package/dist/evidence/observed-route-evidence.js +76 -0
  112. package/dist/evidence/observed-route-evidence.js.map +1 -0
  113. package/dist/heap.d.ts +61 -0
  114. package/dist/heap.d.ts.map +1 -0
  115. package/dist/heap.js +82 -0
  116. package/dist/heap.js.map +1 -0
  117. package/dist/heartbeat.d.ts +46 -0
  118. package/dist/heartbeat.d.ts.map +1 -0
  119. package/dist/heartbeat.js +66 -0
  120. package/dist/heartbeat.js.map +1 -0
  121. package/dist/index.d.ts +32 -0
  122. package/dist/index.d.ts.map +1 -0
  123. package/dist/index.js +19 -0
  124. package/dist/index.js.map +1 -0
  125. package/dist/module-trust.d.ts +37 -0
  126. package/dist/module-trust.d.ts.map +1 -0
  127. package/dist/module-trust.js +77 -0
  128. package/dist/module-trust.js.map +1 -0
  129. package/dist/path-confinement.d.ts +31 -0
  130. package/dist/path-confinement.d.ts.map +1 -0
  131. package/dist/path-confinement.js +44 -0
  132. package/dist/path-confinement.js.map +1 -0
  133. package/dist/planner/predict-then-propose.d.ts +134 -0
  134. package/dist/planner/predict-then-propose.d.ts.map +1 -0
  135. package/dist/planner/predict-then-propose.js +138 -0
  136. package/dist/planner/predict-then-propose.js.map +1 -0
  137. package/dist/planner/propose-journey.d.ts +75 -0
  138. package/dist/planner/propose-journey.d.ts.map +1 -0
  139. package/dist/planner/propose-journey.js +96 -0
  140. package/dist/planner/propose-journey.js.map +1 -0
  141. package/dist/protocol.d.ts +57 -0
  142. package/dist/protocol.d.ts.map +1 -0
  143. package/dist/protocol.js +90 -0
  144. package/dist/protocol.js.map +1 -0
  145. package/dist/ready-checks.d.ts +12 -0
  146. package/dist/ready-checks.d.ts.map +1 -0
  147. package/dist/ready-checks.js +43 -0
  148. package/dist/ready-checks.js.map +1 -0
  149. package/dist/registry.d.ts +103 -0
  150. package/dist/registry.d.ts.map +1 -0
  151. package/dist/registry.js +327 -0
  152. package/dist/registry.js.map +1 -0
  153. package/dist/render.d.ts +623 -0
  154. package/dist/render.d.ts.map +1 -0
  155. package/dist/render.js +968 -0
  156. package/dist/render.js.map +1 -0
  157. package/dist/runtime-registry.d.ts +22 -0
  158. package/dist/runtime-registry.d.ts.map +1 -0
  159. package/dist/runtime-registry.js +142 -0
  160. package/dist/runtime-registry.js.map +1 -0
  161. package/dist/sandbox-defaults.d.ts +41 -0
  162. package/dist/sandbox-defaults.d.ts.map +1 -0
  163. package/dist/sandbox-defaults.js +14 -0
  164. package/dist/sandbox-defaults.js.map +1 -0
  165. package/dist/scenarios/browser-run-plan-projection.d.ts +161 -0
  166. package/dist/scenarios/browser-run-plan-projection.d.ts.map +1 -0
  167. package/dist/scenarios/browser-run-plan-projection.js +264 -0
  168. package/dist/scenarios/browser-run-plan-projection.js.map +1 -0
  169. package/dist/scenarios/credential-ref.d.ts +102 -0
  170. package/dist/scenarios/credential-ref.d.ts.map +1 -0
  171. package/dist/scenarios/credential-ref.js +148 -0
  172. package/dist/scenarios/credential-ref.js.map +1 -0
  173. package/dist/scenarios/index.d.ts +7 -0
  174. package/dist/scenarios/index.d.ts.map +1 -0
  175. package/dist/scenarios/index.js +7 -0
  176. package/dist/scenarios/index.js.map +1 -0
  177. package/dist/scenarios/parse.d.ts +18 -0
  178. package/dist/scenarios/parse.d.ts.map +1 -0
  179. package/dist/scenarios/parse.js +212 -0
  180. package/dist/scenarios/parse.js.map +1 -0
  181. package/dist/scenarios/scenario.d.ts +93 -0
  182. package/dist/scenarios/scenario.d.ts.map +1 -0
  183. package/dist/scenarios/scenario.js +27 -0
  184. package/dist/scenarios/scenario.js.map +1 -0
  185. package/dist/scenarios/secret-ref.d.ts +86 -0
  186. package/dist/scenarios/secret-ref.d.ts.map +1 -0
  187. package/dist/scenarios/secret-ref.js +125 -0
  188. package/dist/scenarios/secret-ref.js.map +1 -0
  189. package/dist/scenarios/storage.d.ts +21 -0
  190. package/dist/scenarios/storage.d.ts.map +1 -0
  191. package/dist/scenarios/storage.js +103 -0
  192. package/dist/scenarios/storage.js.map +1 -0
  193. package/dist/server.d.ts +61 -0
  194. package/dist/server.d.ts.map +1 -0
  195. package/dist/server.js +642 -0
  196. package/dist/server.js.map +1 -0
  197. package/dist/session.d.ts +356 -0
  198. package/dist/session.d.ts.map +1 -0
  199. package/dist/session.js +779 -0
  200. package/dist/session.js.map +1 -0
  201. package/dist/tools/alias-kit.d.ts +29 -0
  202. package/dist/tools/alias-kit.d.ts.map +1 -0
  203. package/dist/tools/alias-kit.js +21 -0
  204. package/dist/tools/alias-kit.js.map +1 -0
  205. package/dist/tools/aliases.d.ts +42 -0
  206. package/dist/tools/aliases.d.ts.map +1 -0
  207. package/dist/tools/aliases.js +52 -0
  208. package/dist/tools/aliases.js.map +1 -0
  209. package/dist/tools/analyze-workspace.d.ts +52 -0
  210. package/dist/tools/analyze-workspace.d.ts.map +1 -0
  211. package/dist/tools/analyze-workspace.js +207 -0
  212. package/dist/tools/analyze-workspace.js.map +1 -0
  213. package/dist/tools/analyze.d.ts +163 -0
  214. package/dist/tools/analyze.d.ts.map +1 -0
  215. package/dist/tools/analyze.js +724 -0
  216. package/dist/tools/analyze.js.map +1 -0
  217. package/dist/tools/browser-click.d.ts +18 -0
  218. package/dist/tools/browser-click.d.ts.map +1 -0
  219. package/dist/tools/browser-click.js +110 -0
  220. package/dist/tools/browser-click.js.map +1 -0
  221. package/dist/tools/browser-close-session.d.ts +39 -0
  222. package/dist/tools/browser-close-session.d.ts.map +1 -0
  223. package/dist/tools/browser-close-session.js +120 -0
  224. package/dist/tools/browser-close-session.js.map +1 -0
  225. package/dist/tools/browser-fill.d.ts +18 -0
  226. package/dist/tools/browser-fill.d.ts.map +1 -0
  227. package/dist/tools/browser-fill.js +117 -0
  228. package/dist/tools/browser-fill.js.map +1 -0
  229. package/dist/tools/browser-navigate.d.ts +15 -0
  230. package/dist/tools/browser-navigate.d.ts.map +1 -0
  231. package/dist/tools/browser-navigate.js +106 -0
  232. package/dist/tools/browser-navigate.js.map +1 -0
  233. package/dist/tools/browser-read.d.ts +33 -0
  234. package/dist/tools/browser-read.d.ts.map +1 -0
  235. package/dist/tools/browser-read.js +105 -0
  236. package/dist/tools/browser-read.js.map +1 -0
  237. package/dist/tools/browser-run-scenario.d.ts +28 -0
  238. package/dist/tools/browser-run-scenario.d.ts.map +1 -0
  239. package/dist/tools/browser-run-scenario.js +238 -0
  240. package/dist/tools/browser-run-scenario.js.map +1 -0
  241. package/dist/tools/browser-save-scenario.d.ts +19 -0
  242. package/dist/tools/browser-save-scenario.d.ts.map +1 -0
  243. package/dist/tools/browser-save-scenario.js +447 -0
  244. package/dist/tools/browser-save-scenario.js.map +1 -0
  245. package/dist/tools/browser-select.d.ts +20 -0
  246. package/dist/tools/browser-select.d.ts.map +1 -0
  247. package/dist/tools/browser-select.js +120 -0
  248. package/dist/tools/browser-select.js.map +1 -0
  249. package/dist/tools/browser-snapshot.d.ts +46 -0
  250. package/dist/tools/browser-snapshot.d.ts.map +1 -0
  251. package/dist/tools/browser-snapshot.js +127 -0
  252. package/dist/tools/browser-snapshot.js.map +1 -0
  253. package/dist/tools/browser-start-session.d.ts +24 -0
  254. package/dist/tools/browser-start-session.d.ts.map +1 -0
  255. package/dist/tools/browser-start-session.js +653 -0
  256. package/dist/tools/browser-start-session.js.map +1 -0
  257. package/dist/tools/browser-submit.d.ts +18 -0
  258. package/dist/tools/browser-submit.d.ts.map +1 -0
  259. package/dist/tools/browser-submit.js +106 -0
  260. package/dist/tools/browser-submit.js.map +1 -0
  261. package/dist/tools/browser-type.d.ts +18 -0
  262. package/dist/tools/browser-type.d.ts.map +1 -0
  263. package/dist/tools/browser-type.js +115 -0
  264. package/dist/tools/browser-type.js.map +1 -0
  265. package/dist/tools/browser-wait-for.d.ts +45 -0
  266. package/dist/tools/browser-wait-for.d.ts.map +1 -0
  267. package/dist/tools/browser-wait-for.js +164 -0
  268. package/dist/tools/browser-wait-for.js.map +1 -0
  269. package/dist/tools/browser.d.ts +69 -0
  270. package/dist/tools/browser.d.ts.map +1 -0
  271. package/dist/tools/browser.js +326 -0
  272. package/dist/tools/browser.js.map +1 -0
  273. package/dist/tools/chain.d.ts +41 -0
  274. package/dist/tools/chain.d.ts.map +1 -0
  275. package/dist/tools/chain.js +101 -0
  276. package/dist/tools/chain.js.map +1 -0
  277. package/dist/tools/change-scope.d.ts +25 -0
  278. package/dist/tools/change-scope.d.ts.map +1 -0
  279. package/dist/tools/change-scope.js +103 -0
  280. package/dist/tools/change-scope.js.map +1 -0
  281. package/dist/tools/code-context.d.ts +83 -0
  282. package/dist/tools/code-context.d.ts.map +1 -0
  283. package/dist/tools/code-context.js +432 -0
  284. package/dist/tools/code-context.js.map +1 -0
  285. package/dist/tools/contracts.d.ts +74 -0
  286. package/dist/tools/contracts.d.ts.map +1 -0
  287. package/dist/tools/contracts.js +214 -0
  288. package/dist/tools/contracts.js.map +1 -0
  289. package/dist/tools/cross-pr.d.ts +178 -0
  290. package/dist/tools/cross-pr.d.ts.map +1 -0
  291. package/dist/tools/cross-pr.js +362 -0
  292. package/dist/tools/cross-pr.js.map +1 -0
  293. package/dist/tools/disclosure.d.ts +75 -0
  294. package/dist/tools/disclosure.d.ts.map +1 -0
  295. package/dist/tools/disclosure.js +325 -0
  296. package/dist/tools/disclosure.js.map +1 -0
  297. package/dist/tools/draw-conclusion.d.ts +55 -0
  298. package/dist/tools/draw-conclusion.d.ts.map +1 -0
  299. package/dist/tools/draw-conclusion.js +243 -0
  300. package/dist/tools/draw-conclusion.js.map +1 -0
  301. package/dist/tools/git-context.d.ts +39 -0
  302. package/dist/tools/git-context.d.ts.map +1 -0
  303. package/dist/tools/git-context.js +374 -0
  304. package/dist/tools/git-context.js.map +1 -0
  305. package/dist/tools/git-diff.d.ts +45 -0
  306. package/dist/tools/git-diff.d.ts.map +1 -0
  307. package/dist/tools/git-diff.js +338 -0
  308. package/dist/tools/git-diff.js.map +1 -0
  309. package/dist/tools/git-history.d.ts +16 -0
  310. package/dist/tools/git-history.d.ts.map +1 -0
  311. package/dist/tools/git-history.js +106 -0
  312. package/dist/tools/git-history.js.map +1 -0
  313. package/dist/tools/history.d.ts +34 -0
  314. package/dist/tools/history.d.ts.map +1 -0
  315. package/dist/tools/history.js +121 -0
  316. package/dist/tools/history.js.map +1 -0
  317. package/dist/tools/impact.d.ts +46 -0
  318. package/dist/tools/impact.d.ts.map +1 -0
  319. package/dist/tools/impact.js +225 -0
  320. package/dist/tools/impact.js.map +1 -0
  321. package/dist/tools/index.d.ts +59 -0
  322. package/dist/tools/index.d.ts.map +1 -0
  323. package/dist/tools/index.js +70 -0
  324. package/dist/tools/index.js.map +1 -0
  325. package/dist/tools/kit.d.ts +165 -0
  326. package/dist/tools/kit.d.ts.map +1 -0
  327. package/dist/tools/kit.js +153 -0
  328. package/dist/tools/kit.js.map +1 -0
  329. package/dist/tools/link-workspace.d.ts +24 -0
  330. package/dist/tools/link-workspace.d.ts.map +1 -0
  331. package/dist/tools/link-workspace.js +131 -0
  332. package/dist/tools/link-workspace.js.map +1 -0
  333. package/dist/tools/lookup.d.ts +19 -0
  334. package/dist/tools/lookup.d.ts.map +1 -0
  335. package/dist/tools/lookup.js +63 -0
  336. package/dist/tools/lookup.js.map +1 -0
  337. package/dist/tools/mark-incident.d.ts +28 -0
  338. package/dist/tools/mark-incident.d.ts.map +1 -0
  339. package/dist/tools/mark-incident.js +171 -0
  340. package/dist/tools/mark-incident.js.map +1 -0
  341. package/dist/tools/merged-browser.d.ts +11 -0
  342. package/dist/tools/merged-browser.d.ts.map +1 -0
  343. package/dist/tools/merged-browser.js +20 -0
  344. package/dist/tools/merged-browser.js.map +1 -0
  345. package/dist/tools/merged-context.d.ts +11 -0
  346. package/dist/tools/merged-context.d.ts.map +1 -0
  347. package/dist/tools/merged-context.js +21 -0
  348. package/dist/tools/merged-context.js.map +1 -0
  349. package/dist/tools/merged-observe.d.ts +13 -0
  350. package/dist/tools/merged-observe.d.ts.map +1 -0
  351. package/dist/tools/merged-observe.js +21 -0
  352. package/dist/tools/merged-observe.js.map +1 -0
  353. package/dist/tools/merged-workspace.d.ts +14 -0
  354. package/dist/tools/merged-workspace.d.ts.map +1 -0
  355. package/dist/tools/merged-workspace.js +21 -0
  356. package/dist/tools/merged-workspace.js.map +1 -0
  357. package/dist/tools/observe-runtime.d.ts +262 -0
  358. package/dist/tools/observe-runtime.d.ts.map +1 -0
  359. package/dist/tools/observe-runtime.js +1966 -0
  360. package/dist/tools/observe-runtime.js.map +1 -0
  361. package/dist/tools/observe-tests.d.ts +57 -0
  362. package/dist/tools/observe-tests.d.ts.map +1 -0
  363. package/dist/tools/observe-tests.js +391 -0
  364. package/dist/tools/observe-tests.js.map +1 -0
  365. package/dist/tools/observe.d.ts +28 -0
  366. package/dist/tools/observe.d.ts.map +1 -0
  367. package/dist/tools/observe.js +291 -0
  368. package/dist/tools/observe.js.map +1 -0
  369. package/dist/tools/pr-analysis.d.ts +189 -0
  370. package/dist/tools/pr-analysis.d.ts.map +1 -0
  371. package/dist/tools/pr-analysis.js +365 -0
  372. package/dist/tools/pr-analysis.js.map +1 -0
  373. package/dist/tools/pre-push.d.ts +168 -0
  374. package/dist/tools/pre-push.d.ts.map +1 -0
  375. package/dist/tools/pre-push.js +416 -0
  376. package/dist/tools/pre-push.js.map +1 -0
  377. package/dist/tools/predict-reach.d.ts +57 -0
  378. package/dist/tools/predict-reach.d.ts.map +1 -0
  379. package/dist/tools/predict-reach.js +183 -0
  380. package/dist/tools/predict-reach.js.map +1 -0
  381. package/dist/tools/propagation.d.ts +37 -0
  382. package/dist/tools/propagation.d.ts.map +1 -0
  383. package/dist/tools/propagation.js +162 -0
  384. package/dist/tools/propagation.js.map +1 -0
  385. package/dist/tools/propose-journey.d.ts +16 -0
  386. package/dist/tools/propose-journey.d.ts.map +1 -0
  387. package/dist/tools/propose-journey.js +132 -0
  388. package/dist/tools/propose-journey.js.map +1 -0
  389. package/dist/tools/prove-reach.d.ts +74 -0
  390. package/dist/tools/prove-reach.d.ts.map +1 -0
  391. package/dist/tools/prove-reach.js +274 -0
  392. package/dist/tools/prove-reach.js.map +1 -0
  393. package/dist/tools/questions.d.ts +30 -0
  394. package/dist/tools/questions.d.ts.map +1 -0
  395. package/dist/tools/questions.js +230 -0
  396. package/dist/tools/questions.js.map +1 -0
  397. package/dist/tools/refusal-fetch.d.ts +35 -0
  398. package/dist/tools/refusal-fetch.d.ts.map +1 -0
  399. package/dist/tools/refusal-fetch.js +123 -0
  400. package/dist/tools/refusal-fetch.js.map +1 -0
  401. package/dist/tools/runtime-incident.d.ts +33 -0
  402. package/dist/tools/runtime-incident.d.ts.map +1 -0
  403. package/dist/tools/runtime-incident.js +67 -0
  404. package/dist/tools/runtime-incident.js.map +1 -0
  405. package/dist/tools/runtime-journey-drive.d.ts +102 -0
  406. package/dist/tools/runtime-journey-drive.d.ts.map +1 -0
  407. package/dist/tools/runtime-journey-drive.js +247 -0
  408. package/dist/tools/runtime-journey-drive.js.map +1 -0
  409. package/dist/tools/scope.d.ts +45 -0
  410. package/dist/tools/scope.d.ts.map +1 -0
  411. package/dist/tools/scope.js +217 -0
  412. package/dist/tools/scope.js.map +1 -0
  413. package/dist/tools/similar-incidents.d.ts +43 -0
  414. package/dist/tools/similar-incidents.d.ts.map +1 -0
  415. package/dist/tools/similar-incidents.js +190 -0
  416. package/dist/tools/similar-incidents.js.map +1 -0
  417. package/dist/tools/validate.d.ts +133 -0
  418. package/dist/tools/validate.d.ts.map +1 -0
  419. package/dist/tools/validate.js +488 -0
  420. package/dist/tools/validate.js.map +1 -0
  421. package/dist/tools/verb.d.ts +46 -0
  422. package/dist/tools/verb.d.ts.map +1 -0
  423. package/dist/tools/verb.js +78 -0
  424. package/dist/tools/verb.js.map +1 -0
  425. package/dist/tools/verification-status.d.ts +38 -0
  426. package/dist/tools/verification-status.d.ts.map +1 -0
  427. package/dist/tools/verification-status.js +203 -0
  428. package/dist/tools/verification-status.js.map +1 -0
  429. package/dist/tools/verify-claim.d.ts +29 -0
  430. package/dist/tools/verify-claim.d.ts.map +1 -0
  431. package/dist/tools/verify-claim.js +218 -0
  432. package/dist/tools/verify-claim.js.map +1 -0
  433. package/dist/tools/workspace.d.ts +28 -0
  434. package/dist/tools/workspace.d.ts.map +1 -0
  435. package/dist/tools/workspace.js +94 -0
  436. package/dist/tools/workspace.js.map +1 -0
  437. package/dist/transport.d.ts +32 -0
  438. package/dist/transport.d.ts.map +1 -0
  439. package/dist/transport.js +85 -0
  440. package/dist/transport.js.map +1 -0
  441. package/dist/workspace-index.d.ts +164 -0
  442. package/dist/workspace-index.d.ts.map +1 -0
  443. package/dist/workspace-index.js +381 -0
  444. package/dist/workspace-index.js.map +1 -0
  445. package/package.json +3 -3
package/dist/render.js ADDED
@@ -0,0 +1,968 @@
1
+ /**
2
+ * The one way an engine answer becomes an MCP tool result.
3
+ *
4
+ * ## Why this file exists rather than each tool formatting its own reply
5
+ *
6
+ * `QueryResult` carries three things a caller would rather not think about: a
7
+ * staleness stamp (§27.3), a `truncated` flag, and `notes` — the plain-words
8
+ * disclosures that populate the five result states' `refused`/`empty`/`failed`/
9
+ * `timed_out` cases. The engine computes all three correctly today. Every one of
10
+ * them is one careless `return result.data` away from never reaching a user.
11
+ *
12
+ * So the tool handler's return type is `QueryResult`, not content. A handler
13
+ * *cannot* build a reply; it can only produce an answer, and this module is the
14
+ * only thing that turns an answer into frames. Rule 7 — honest degradation, no
15
+ * silent doing-less — becomes a property of the type system rather than of
16
+ * whoever writes the next tool.
17
+ *
18
+ * ## `ai-tool-contract.md` §2 — the frozen envelope
19
+ *
20
+ * This module used to ship a two-value `status: "ok" | "not_analysable"`. The
21
+ * contract froze five: `ok` / `empty` / `refused` / `failed` / `timed_out`
22
+ * (§1), plus `class`, `toolVersion`, `budget` and a structured `truncated`
23
+ * object (§2). `AnswerStatus`/`renderAnswer`'s old two-state shape is gone
24
+ * rather than kept alongside the new one — two envelope shapes on the wire is
25
+ * the thing DEC-277's "one process mints" reasoning exists to avoid one layer
26
+ * up, applied here to a type rather than a decision number.
27
+ *
28
+ * ## Four invariants, asserted rather than documented (§2.1)
29
+ *
30
+ * `render.ts` already enforced the first two; the other two are new.
31
+ *
32
+ * 1. `truncated != null` with empty `disclosures` → throw.
33
+ * 2. `state` in `{refused, failed, timed_out}` with empty `disclosures` → throw.
34
+ * 3. `state == "refused"` with no verbatim reason in `disclosures` → throw (§4).
35
+ * 4. `class == "action"` returning anything other than a handshake envelope on
36
+ * the first call → throw (§7) — enforced in `action-handshake.ts` and
37
+ * `server.ts`, not here: this module never sees a mint, only a redeemed run.
38
+ *
39
+ * ## A fifth invariant, B6: a truncated answer must carry real counts
40
+ *
41
+ * `result.truncated === true` with no `answer.truncatedDetail` used to fall
42
+ * back to a sentinel, `{shown: -1, total: -1}` — chosen to be "unmistakably
43
+ * fake," except that it is a number, so it passes any presence check and then
44
+ * poisons whatever arithmetic reads `total - shown`. A field that lies is
45
+ * worse than a field that is absent, which is the same argument the five
46
+ * result states rest on. So the sentinel is gone: a tool that sets
47
+ * `result.truncated` without a `truncatedDetail` is a programming error,
48
+ * thrown in every environment except production, where the envelope instead
49
+ * reports `truncated: null` plus a disclosure naming the gap — degraded
50
+ * honestly rather than dishonestly precise. See `capped()` in `./capped.ts`
51
+ * for how a tool is meant to avoid ever hitting this.
52
+ */
53
+ import { randomUUID } from "node:crypto";
54
+ import { aliasDisclosure } from "./tools/aliases.js";
55
+ import { RELIABILITY_CLASSES, REPORT_CATEGORIES, RESULT_STATES, TOOL_CLASSES, existenceCategory as deriveExistenceCategory, reliabilityCap, reliabilityDisclosure, reportCategory as deriveReportCategory, } from "@descryy/ir";
56
+ import { sampleChangedFiles } from "@descryy/core";
57
+ /**
58
+ * MK-5 — which of the sixteen tools states a conclusion about the user's code
59
+ * versus which reports a fact about the graph.
60
+ *
61
+ * Lives here rather than in `@descryy/ir`: `tool-surface.ts`'s own header
62
+ * scopes that file to exactly `ResultState` and `ToolClass` ("and nothing
63
+ * else"), because those two cross into the pipeline and the desktop surface.
64
+ * This distinction is agent-mode-only (`plugin-and-mcp-surface.md` §3 PL-1),
65
+ * so a third shared union there would be scope creep the file's own doc
66
+ * disclaims. `kit.ts` imports it from here rather than the reverse, because
67
+ * `kit.ts` already depends on this module for `ToolAnswer`/`TruncationDetail`
68
+ * — defining it in `kit.ts` instead would make this module import back from
69
+ * `kit.ts` and cycle.
70
+ *
71
+ * - `conclusion` — the output is a statement about the user's code
72
+ * (`validate`, `contracts`, `cross_pr`). Never raw candidates: an
73
+ * already-adjudicated result carrying the category and the disclosure
74
+ * sentence as one payload.
75
+ * - `evidence` — the output is a fact the graph computed, already carrying
76
+ * its own resolution and qualification. The other thirteen.
77
+ */
78
+ export const TOOL_TIERS = ["conclusion", "evidence"];
79
+ const PROVENANCE = {
80
+ surface: "mcp",
81
+ adjudicatedBy: "descry",
82
+ presentedBy: "host-agent",
83
+ governancePipeline: false,
84
+ };
85
+ /**
86
+ * The ceiling on one rendered tool result, serialized — `content` frame and
87
+ * `structuredContent` envelope together, which is what a host actually
88
+ * receives and counts.
89
+ *
90
+ * A starting value, not a measurement, in this repo's usual sense: the one
91
+ * hard number behind it is a real failure, `get_git_diff` on an ordinary
92
+ * 48-file branch diff returning 84,516 characters and being rejected outright
93
+ * by the host before the caller saw any of it. 60,000 sits well under the
94
+ * smallest size observed to fail, with room for the narrated frame that
95
+ * repeats the disclosures alongside the envelope.
96
+ *
97
+ * Shared rather than per-tool on purpose: `get_git_diff` is the tool that hit
98
+ * this, twice, but nothing about the failure was specific to diffs. Any tool
99
+ * adopts the ceiling by implementing `ToolAnswer.fitToBudget`.
100
+ */
101
+ export const MAX_REPLY_BYTES = 60_000;
102
+ // ---------------------------------------------------------------------------
103
+ // The wire schema for everything above
104
+ // ---------------------------------------------------------------------------
105
+ /** `T | null`, in the JSON Schema this server's declared subset uses. */
106
+ function nullable(schema) {
107
+ return { anyOf: [schema, { type: "null" }] };
108
+ }
109
+ /**
110
+ * The envelope, declared for a host to validate against — MCP's `outputSchema`.
111
+ *
112
+ * ## Why this exists at all
113
+ *
114
+ * Every reply from this server carries `structuredContent`, and until this was
115
+ * added a host had no way to check what it received. `tools/list` already ships
116
+ * `class`, `tier` and `toolVersion` on the same reasoning — a fact about the
117
+ * reply that an external client cannot otherwise read — and this is the
118
+ * standard-shaped member of that group rather than a fourth non-standard field.
119
+ *
120
+ * ## `data` is deliberately open, and that is a disclosed gap
121
+ *
122
+ * The envelope's thirteen fields are uniform across all thirty-three tools, so they
123
+ * are described exactly. `data` is not: its shape is `ImpactData`, `ScopeData`
124
+ * and thirty-one others, and nothing today derives a JSON Schema from those
125
+ * TypeScript types. Hand-writing thirty-three schemas would put a second description
126
+ * of each shape in a second place with nothing checking the two agree — the
127
+ * drift this repo has already paid for once with the tool count in prose, and
128
+ * a schema that is confidently wrong about `data` is worse for a validating
129
+ * host than one that declines to describe it.
130
+ *
131
+ * So `data` is `{}` — "anything" — and this paragraph is the disclosure rather
132
+ * than a silent omission (rule 7). Tightening it is real work with a real
133
+ * prerequisite: a generator from the handler types, or the schemas checked
134
+ * against recorded replies. Until then the promise made here is exactly the
135
+ * promise kept.
136
+ *
137
+ * ## The enums are read, not restated
138
+ *
139
+ * `state`, `class`, `reportCategory` and `reliabilityCap` build their `enum`
140
+ * from the exported vocabulary arrays. A restated list is a second source of
141
+ * truth that goes stale in silence; spread from the array, adding a report
142
+ * category and forgetting the schema is a red test instead.
143
+ */
144
+ export const ANSWER_ENVELOPE_SCHEMA = {
145
+ type: "object",
146
+ title: "Descry answer envelope",
147
+ description: "The uniform reply shape for every Descry tool. `state` distinguishes " +
148
+ "'looked and found nothing' (empty) from 'could not look' (refused); read " +
149
+ "`disclosures` before summarising the answer.",
150
+ properties: {
151
+ tool: { type: "string", description: "The tool that produced this reply." },
152
+ toolVersion: {
153
+ type: "string",
154
+ description: "Bumped on any behaviour change; recorded with the run.",
155
+ },
156
+ callId: {
157
+ type: "string",
158
+ description: "This call's own id in this session's recorded tool calls. Cite it in verify_claim's " +
159
+ "\"callId\" to check a claim against this exact call's real result.",
160
+ },
161
+ state: {
162
+ type: "string",
163
+ enum: [...RESULT_STATES],
164
+ description: "ok = found something; empty = looked completely and found nothing (a " +
165
+ "fact about your code); refused = could not look (a fact about Descry, " +
166
+ "never about your code); failed = something broke; timed_out = ran out " +
167
+ "of budget, partial answer.",
168
+ },
169
+ class: {
170
+ type: "string",
171
+ enum: [...TOOL_CLASSES],
172
+ description: "Retry semantics: read, stateful-read, or action.",
173
+ },
174
+ headline: { type: "string", description: "One-sentence plain-English answer." },
175
+ data: {
176
+ description: "The tool's own payload, or null on refused/failed/timed_out. Shape is " +
177
+ "per-tool and is not described here — see this schema's own " +
178
+ "documentation for why, and the tool description for the fields.",
179
+ },
180
+ graph: {
181
+ type: "object",
182
+ description: "What the answer rests on.",
183
+ properties: {
184
+ commitSha: nullable({ type: "string" }),
185
+ builtAt: nullable({ type: "string" }),
186
+ irSchemaVersion: { type: "integer" },
187
+ resolutionFloor: {
188
+ ...nullable({ type: "integer", minimum: 0, maximum: 4 }),
189
+ description: "R0 syntax only; R1 imports resolved; R2 references resolved; R3 " +
190
+ "type checker; R4 observed at runtime. Null when the graph was not " +
191
+ "consulted at all.",
192
+ },
193
+ reliabilityCap: {
194
+ type: "string",
195
+ enum: [...RELIABILITY_CLASSES],
196
+ description: "C = unconfirmed behavioural prediction, must not be relayed as " +
197
+ "fact; B = structural or reference evidence; A = as strong as this " +
198
+ "graph makes it.",
199
+ },
200
+ freshness: nullable({
201
+ type: "object",
202
+ description: "Whether this graph still matches the repository on disk. Null only " +
203
+ "when commitSha is null (nothing analysed yet, so nothing to be " +
204
+ "fresh or stale relative to).",
205
+ properties: {
206
+ status: {
207
+ type: "string",
208
+ enum: ["current", "behind", "unknown"],
209
+ description: "current = HEAD is the build commit and no indexed file is " +
210
+ "uncommitted; behind = HEAD moved past the build, or an indexed " +
211
+ "file has an uncommitted edit; unknown = not a git repository, " +
212
+ "or git unavailable here — never reported as current.",
213
+ },
214
+ commitsSince: nullable({
215
+ type: "integer",
216
+ description: "Commits made after the build. Null when status isn't behind, " +
217
+ "or when behind but the exact count could not be determined " +
218
+ "(history rewritten since the build) — never a substitute for 0.",
219
+ }),
220
+ changedIndexedFiles: nullable({
221
+ type: "integer",
222
+ description: "How many indexed files differ from the build. Up to 5 of them " +
223
+ "are named in disclosures. Null when status isn't behind.",
224
+ }),
225
+ },
226
+ required: ["status", "commitsSince", "changedIndexedFiles"],
227
+ }),
228
+ },
229
+ required: ["commitSha", "builtAt", "irSchemaVersion", "resolutionFloor", "reliabilityCap", "freshness"],
230
+ },
231
+ truncated: nullable({
232
+ type: "object",
233
+ properties: {
234
+ shown: { type: "integer" },
235
+ total: { type: "integer", description: "The count before the cap." },
236
+ more: nullable({ type: "string" }),
237
+ },
238
+ required: ["shown", "total", "more"],
239
+ }),
240
+ refusals: nullable({
241
+ type: "object",
242
+ properties: {
243
+ count: { type: "integer" },
244
+ exemplar: { type: "string" },
245
+ handle: { type: "string", description: "Pass to refusal_fetch for the full list." },
246
+ },
247
+ required: ["count", "exemplar", "handle"],
248
+ }),
249
+ budget: {
250
+ type: "object",
251
+ properties: {
252
+ elapsedMs: { type: "integer" },
253
+ limitMs: { type: "integer" },
254
+ },
255
+ required: ["elapsedMs", "limitMs"],
256
+ },
257
+ disclosures: {
258
+ type: "array",
259
+ items: { type: "string" },
260
+ description: "What this answer does not cover. Never empty when the answer is " +
261
+ "degraded, truncated, refused, failed or timed out.",
262
+ },
263
+ configStale: {
264
+ type: "boolean",
265
+ description: "True when the running server's configuration no longer matches " +
266
+ ".descry/config.json on disk, so this reply (including graph and " +
267
+ "reportCategory) reflects the stale configuration loaded at startup. " +
268
+ "Restart the server to pick up the on-disk change; see disclosures " +
269
+ "for which fields changed.",
270
+ },
271
+ reportCategory: {
272
+ ...nullable({ type: "string", enum: [...REPORT_CATEGORIES] }),
273
+ description: "How strongly the CAUSE may be stated, capped by how well the code " +
274
+ "resolved. Null for conclusion-tier tools, and for replies that make no " +
275
+ "claim at all. 'confirmed' is unreachable from this surface by construction.",
276
+ },
277
+ existenceCategory: {
278
+ ...nullable({ type: "string", enum: [...REPORT_CATEGORIES] }),
279
+ description: "How strongly the evidence says the failure is REAL, read from what was " +
280
+ "observed and never capped by how well the code resolved. Read it beside " +
281
+ "reportCategory, not instead of it: a reply can be 'strongly supported' " +
282
+ "here and 'unconfirmed' there, which means the failure was witnessed and " +
283
+ "its cause was not established. Null wherever no evidence reading was " +
284
+ "taken — conclusion-tier tools, faults, and refusals.",
285
+ },
286
+ provenance: {
287
+ type: "object",
288
+ description: "Constant for this server. governancePipeline is always false here.",
289
+ properties: {
290
+ surface: { type: "string", enum: ["mcp"] },
291
+ adjudicatedBy: { type: "string", enum: ["descry"] },
292
+ presentedBy: { type: "string", enum: ["host-agent"] },
293
+ governancePipeline: { type: "boolean", enum: [false] },
294
+ },
295
+ required: ["surface", "adjudicatedBy", "presentedBy", "governancePipeline"],
296
+ },
297
+ },
298
+ // Every field is emitted on every path, including the ones that are always
299
+ // null on a fault — `renderFailed` and `renderTimedOut` build the whole
300
+ // envelope. So "required" here is the literal truth rather than a minimum.
301
+ required: [
302
+ "tool",
303
+ "toolVersion",
304
+ "callId",
305
+ "state",
306
+ "class",
307
+ "headline",
308
+ "data",
309
+ "graph",
310
+ "truncated",
311
+ "refusals",
312
+ "budget",
313
+ "disclosures",
314
+ "configStale",
315
+ "reportCategory",
316
+ "existenceCategory",
317
+ "provenance",
318
+ ],
319
+ };
320
+ /**
321
+ * The other shape a `tools/call` can return — `renderToolError`'s, now
322
+ * narrowed to the two cases where no tool was ever resolved (`name` not a
323
+ * string, or a name the registry does not have): there is no `ToolDefinition`
324
+ * to draw a real envelope from. A bad argument against a *resolved* tool goes
325
+ * through `renderRefusedInput` instead and is a real `state: "refused"`
326
+ * envelope — see that function's doc.
327
+ *
328
+ * Declared rather than quietly excluded. A schema that admitted only the
329
+ * envelope would be one the server itself violates on this narrower path.
330
+ * Declaring a union that is true beats declaring a single shape that is tidy.
331
+ */
332
+ export const TOOL_ERROR_SCHEMA = {
333
+ type: "object",
334
+ title: "Tool argument error",
335
+ description: "Returned with isError when the call itself was malformed — fix the " +
336
+ "arguments and retry. Not a statement about the code either way.",
337
+ properties: {
338
+ tool: { type: "string" },
339
+ status: { type: "string", enum: ["error"] },
340
+ message: { type: "string" },
341
+ },
342
+ required: ["tool", "status", "message"],
343
+ };
344
+ /**
345
+ * What every tool declares as its `outputSchema`: the envelope or the argument
346
+ * error, discriminated by their required fields.
347
+ *
348
+ * One shared constant rather than a per-tool object because the shape genuinely
349
+ * is shared — the only per-tool part is `data`, which is open. When `data`
350
+ * becomes describable this becomes a function of the tool.
351
+ */
352
+ export const TOOL_OUTPUT_SCHEMA = {
353
+ type: "object",
354
+ anyOf: [ANSWER_ENVELOPE_SCHEMA, TOOL_ERROR_SCHEMA],
355
+ };
356
+ export class DisclosureError extends Error {
357
+ constructor(message) {
358
+ super(message);
359
+ this.name = "DisclosureError";
360
+ }
361
+ }
362
+ /** Rule 7's label for a redemption that could not honour what the caller asked for. Names the
363
+ * arguments rather than counting them — "1 argument was discarded" tells a caller something went
364
+ * wrong without telling them what to fix — and names the recovery, which is the same one the
365
+ * `confirmToken` descriptions prescribe. See `action-handshake.ts`'s header. */
366
+ function discardedArgsNote(discarded) {
367
+ const names = discarded.map((key) => `"${key}"`).join(", ");
368
+ return (`This call supplied ${names} alongside a confirmToken, but a token runs the arguments frozen ` +
369
+ `when it was minted — the ones described for confirmation — so ${discarded.length === 1 ? "that value was" : "those values were"} discarded rather than used. ` +
370
+ "Call again without confirmToken to have the new arguments described, then confirm that token.");
371
+ }
372
+ /**
373
+ * MK-8's derivation, shared by every envelope-producing function below so the
374
+ * rule lives in one place rather than four.
375
+ */
376
+ function deriveEnvelopeCategory(tier, state, resolutionFloor, nameLevel, witnessed) {
377
+ if (tier === "conclusion")
378
+ return null;
379
+ if (state === "failed" || state === "timed_out")
380
+ return null;
381
+ if (state === "refused")
382
+ return "not analysable";
383
+ return deriveReportCategory({
384
+ // G3's precondition, answered by the tool rather than assumed: a tool that
385
+ // ran nothing declares nothing here and short-circuits to `unconfirmed`,
386
+ // exactly as before. A tool that watched a real application declares what
387
+ // it watched.
388
+ hasRuntimeEvidence: witnessed !== undefined,
389
+ // Pinned false, and deliberately not something a tool may declare. `M` is
390
+ // G4's one route to `confirmed`, and DEC-334 reserves that category for a
391
+ // result that passed the full governance pipeline — which does not run in
392
+ // this surface. Accepting `M` here would let a tool mint the one label the
393
+ // ruling holds back, so the field is not offered rather than offered and
394
+ // then clamped: a clamp is a silent downgrade, and rule 7 forbids those.
395
+ directMechanismEvidence: false,
396
+ independentSignalTypes: witnessed?.independentSignalTypes ?? 0,
397
+ // `renderAnswer`'s own `state`/`disclosures` invariants guarantee a real
398
+ // graph was consulted whenever state is ok/empty, so this is never the
399
+ // null-graph sentinel — cast is safe, not asserted blind.
400
+ resolution: (resolutionFloor ?? 0),
401
+ nameLevel,
402
+ humanAsserted: false,
403
+ }).category;
404
+ }
405
+ /**
406
+ * The existence half — `documents/plans/gate_loosening.md` §3.3.
407
+ *
408
+ * Reads the same witnessed evidence the sibling above does and takes no resolution argument at
409
+ * all, which is the whole point: the structural floor is evidence about the cause and this is a
410
+ * statement about the failure. The two are computed apart so that neither can silently acquire
411
+ * the other's inputs.
412
+ *
413
+ * `directMechanismEvidence` is pinned `false` for exactly the reason the sibling pins it —
414
+ * DEC-334 holds `confirmed` for a result that passed the full governance pipeline, which does
415
+ * not run in an MCP call. So this tops out at `strongly supported` too, and the split changes
416
+ * which failures are *visible*, never which label this surface may mint.
417
+ *
418
+ * Null on refusal is deliberate and is not the same as `unconfirmed`: a refusal is a fact about
419
+ * Descry rather than about the application, and reporting it as an evidence reading would teach
420
+ * the caller that we looked and saw nothing.
421
+ */
422
+ function deriveEnvelopeExistence(tier, state, witnessed) {
423
+ if (tier === "conclusion")
424
+ return null;
425
+ if (state === "failed" || state === "timed_out" || state === "refused")
426
+ return null;
427
+ return deriveExistenceCategory({
428
+ hasRuntimeEvidence: witnessed !== undefined,
429
+ directMechanismEvidence: false,
430
+ independentSignalTypes: witnessed?.independentSignalTypes ?? 0,
431
+ });
432
+ }
433
+ /**
434
+ * A refusal's reason must be the specific, verbatim thing that blocked it —
435
+ * never just the state name. This is a heuristic proxy for that rule (§4):
436
+ * `disclosures` must contain something a template did not produce, which we
437
+ * cannot verify from here, but we CAN verify the weaker, structurally
438
+ * checkable half — that at least one disclosure exists and it is not empty
439
+ * text — the same shape invariant 2 already asserts. The stronger claim (the
440
+ * reason is *this* refusal's actual cause, not a generic string) is a
441
+ * per-tool authoring discipline this function cannot see into; it is checked
442
+ * by `tool-contract-gate.mjs`'s exemplar reading instead.
443
+ */
444
+ /**
445
+ * The retired-name disclosure, for every render path rather than the happy one.
446
+ *
447
+ * A call under a dead name that *fails* still needs to be told the name is
448
+ * dead — arguably more than a call that succeeds, since the caller is already
449
+ * debugging and "no such tool" is one release away. `renderAnswer` is not the
450
+ * only exit: a refusal, a failure, a handshake and a timeout are all replies a
451
+ * retired name can produce, and each assembles its own disclosure list.
452
+ */
453
+ function aliasNotes(options) {
454
+ return options.aliasedFrom === undefined || options.aliasedTo === undefined
455
+ ? []
456
+ : [aliasDisclosure(options.aliasedFrom, options.aliasedTo)];
457
+ }
458
+ /**
459
+ * Lane "map-freshness" item 1 — `AnswerEnvelope.graph.freshness`'s structured
460
+ * half, shared by every envelope-building function in this file. `null` when
461
+ * `commitSha` is `null` (nothing to be fresh or stale relative to, same
462
+ * reasoning as `resolutionFloor: null` on an unconsulted graph). Otherwise
463
+ * `unknown` with both counts `null` when this call's `RenderOptions` never
464
+ * supplied a `freshness` — "not checked" must never render as the false
465
+ * positive "current" would be.
466
+ */
467
+ function freshnessField(commitSha, freshness) {
468
+ if (commitSha === null)
469
+ return null;
470
+ if (freshness === undefined)
471
+ return { status: "unknown", commitsSince: null, changedIndexedFiles: null };
472
+ return {
473
+ status: freshness.status,
474
+ commitsSince: freshness.status === "behind" ? freshness.commitsSince : null,
475
+ changedIndexedFiles: freshness.status === "behind" ? freshness.changedFiles.length : null,
476
+ };
477
+ }
478
+ /**
479
+ * The plain-words half — "up to 5 of those files, plus the total" (lane
480
+ * "map-freshness" item 1). `undefined` on `current`/`unknown-with-nothing-
481
+ * to-say`: a disclosure that repeats "the map is current" on every single
482
+ * reply is exactly the noise `disclosure-ledger.ts` exists to avoid for the
483
+ * reliability sentence, and this field has no standing-ledger mechanism of
484
+ * its own to fall back to, so it says nothing rather than something on
485
+ * every call.
486
+ */
487
+ function freshnessDisclosure(freshness) {
488
+ if (freshness === undefined || freshness.status === "current")
489
+ return undefined;
490
+ if (freshness.status === "unknown") {
491
+ return ("Whether the code map matches the current code could not be determined — this is not a git " +
492
+ "repository, or git is unavailable here.");
493
+ }
494
+ const { shown, total } = sampleChangedFiles(freshness);
495
+ const commitsPart = freshness.commitsSince === null
496
+ ? "an unknown number of commits (history was rewritten since the map was built)"
497
+ : freshness.commitsSince === 0
498
+ ? "0 commits"
499
+ : `${freshness.commitsSince} commit(s)`;
500
+ const filesPart = total === 0
501
+ ? "no indexed file is known to have changed"
502
+ : `${total} indexed file(s) changed since the map was built` +
503
+ (shown.length > 0 ? ` — including ${shown.join(", ")}${total > shown.length ? ", …" : ""}` : "");
504
+ return `The code map is behind the current code: ${commitsPart} since it was built. ${filesPart}.`;
505
+ }
506
+ /** Both halves of the lane "map-freshness" disclosure, shared by every
507
+ * envelope-building function in this file (see `RenderOptions.autoNotes`/
508
+ * `.freshness`'s own docs). Ordered ahead of everything else these
509
+ * functions add — a caller needs to know the map just changed, or is
510
+ * stale, before it needs any other caveat. */
511
+ function freshnessCrossCuttingNotes(options) {
512
+ const note = freshnessDisclosure(options.freshness);
513
+ return [...(options.autoNotes ?? []), ...(note === undefined ? [] : [note])];
514
+ }
515
+ export function renderAnswer(tool, answer, options) {
516
+ const { result, nameLevel } = answer;
517
+ const state = answer.state ?? "ok";
518
+ if (result.truncated && result.notes.length === 0) {
519
+ throw new DisclosureError(`${tool} returned a truncated answer with no disclosure. A partial result that does not ` +
520
+ "say it is partial reads as a complete one, which is the failure rule 7 exists to " +
521
+ "prevent. Fix the query, not this check.");
522
+ }
523
+ if ((state === "refused" || state === "failed" || state === "timed_out") && result.notes.length === 0) {
524
+ throw new DisclosureError(`${tool} reported "${state}" without saying why. The five result states exist to distinguish ` +
525
+ '"we found nothing" from "we could not look" from "something broke"; with no reason attached ' +
526
+ "this reply says none of them.");
527
+ }
528
+ // B6, fifth invariant (module header): a truncated answer must carry real
529
+ // counts. The old fallback here was a sentinel, `{shown: -1, total: -1}` —
530
+ // worse than absent, because it passes a presence check and then poisons
531
+ // arithmetic. A tool that sets `result.truncated` without supplying
532
+ // `answer.truncatedDetail` has not held the two together the way
533
+ // `capped()` (`./capped.ts`) makes automatic, so this is a defect in that
534
+ // tool, caught here rather than shipped: thrown everywhere except
535
+ // production, where the honest degradation is `null` plus a disclosure
536
+ // that the count itself is unavailable — never a fabricated one.
537
+ let missingTruncationDetail = null;
538
+ if (result.truncated && answer.truncatedDetail === undefined) {
539
+ if (process.env["NODE_ENV"] !== "production") {
540
+ throw new DisclosureError(`${tool} set result.truncated without a truncatedDetail (shown/total). Build the capped ` +
541
+ 'slice and its detail together — see capped() in "./capped.ts" — rather than capping ' +
542
+ "and reporting separately, where one of the two can be forgotten.");
543
+ }
544
+ missingTruncationDetail =
545
+ "this reply was truncated, but how much was withheld is unavailable — a defect in this " +
546
+ "tool, not a fact about your code";
547
+ }
548
+ const resolutionFloor = result.stamp.resolutionFloor;
549
+ const cap = reliabilityCap(resolutionFloor, nameLevel);
550
+ // Full the first time this connection meets this category, a short reference
551
+ // to it after — see `disclosure-ledger.ts`. The floor and the class are on
552
+ // every reply either way; only the argument for them collapses.
553
+ const reliability = reliabilityDisclosure(resolutionFloor, nameLevel);
554
+ const disclosures = [
555
+ ...result.notes,
556
+ // First among the disclosures, ahead of the reliability argument: a caller
557
+ // on a dead name needs to know that before it needs anything else.
558
+ ...aliasNotes(options),
559
+ ...(missingTruncationDetail === null ? [] : [missingTruncationDetail]),
560
+ options.disclosureLedger?.firstSighting(reliability.key) === false
561
+ ? reliability.brief
562
+ : reliability.full,
563
+ ...(result.stamp.commitSpread > 1
564
+ ? [
565
+ `a repository in this graph has runs spanning ${result.stamp.commitSpread} distinct ` +
566
+ "commits; part of its graph may describe code that is no longer current",
567
+ ]
568
+ : []),
569
+ // A neutral fact, never a warning — cross-repo sharing (`link_workspace`) is
570
+ // what makes cross-repo contracts/validate/similar_incidents work at all.
571
+ // UAT phase 5: this used to be the same sentence as the staleness warning
572
+ // above, driven by a global (not per-repo) commit count, so it fired on
573
+ // every single reply in a shared workspace and read as decay that was
574
+ // never real — training the reader to ignore it before the day a genuine
575
+ // staleness warning needed attention.
576
+ ...(result.stamp.repoCount > 1
577
+ ? [`this graph spans ${result.stamp.repoCount} repositories, each at its own HEAD`]
578
+ : []),
579
+ ...(options.configDrift?.note === undefined ? [] : [options.configDrift.note]),
580
+ ...(options.discardedArgs === undefined || options.discardedArgs.length === 0
581
+ ? []
582
+ : [discardedArgsNote(options.discardedArgs)]),
583
+ ];
584
+ // Lane "map-freshness" items 1-3: an automatic build/refresh note (if one
585
+ // ran before this call) and the freshness disclosure itself, both ahead of
586
+ // every other disclosure — a caller needs to know the map just changed, or
587
+ // is stale, before it needs anything else this reply does not cover. See
588
+ // `freshnessCrossCuttingNotes` above.
589
+ const crossCuttingNotes = freshnessCrossCuttingNotes(options);
590
+ // B10 — minted by the caller before this call, so it is threaded through
591
+ // rather than generated here; a fallback exists only for the direct callers
592
+ // (tests) that have no session id sequence to draw from. See `callId`'s own
593
+ // doc on `AnswerEnvelope` and `RenderOptions`.
594
+ const callId = options.callId ?? randomUUID();
595
+ const build = (data, extraNotes, fitTruncation) => ({
596
+ tool,
597
+ toolVersion: options.toolVersion,
598
+ callId,
599
+ state,
600
+ class: options.toolClass,
601
+ headline: answer.headline,
602
+ data,
603
+ graph: {
604
+ commitSha: result.stamp.commitSha,
605
+ builtAt: result.stamp.graphBuiltAt === null
606
+ ? null
607
+ : new Date(result.stamp.graphBuiltAt).toISOString(),
608
+ irSchemaVersion: result.stamp.irSchemaVersion,
609
+ resolutionFloor,
610
+ reliabilityCap: cap,
611
+ freshness: freshnessField(result.stamp.commitSha, options.freshness),
612
+ },
613
+ truncated: fitTruncation ?? (!result.truncated ? null : (answer.truncatedDetail ?? null)),
614
+ refusals: answer.refusalSummary ?? null,
615
+ budget: { elapsedMs: options.elapsedMs, limitMs: options.limitMs },
616
+ // The fit's own notes go first among the disclosures it added, ahead of
617
+ // the standing reliability sentence, for the same reason `narrate` puts
618
+ // disclosures last: what is specific to this call must not be buried
619
+ // under what is true of every call. `crossCuttingNotes` goes ahead of
620
+ // even that — see its own doc above.
621
+ disclosures: [
622
+ ...crossCuttingNotes,
623
+ ...(extraNotes.length === 0 ? disclosures : [...result.notes, ...extraNotes, ...disclosures.slice(result.notes.length)]),
624
+ ],
625
+ configStale: options.configDrift?.stale ?? false,
626
+ reportCategory: deriveEnvelopeCategory(options.toolTier, state, resolutionFloor, nameLevel, answer.runtimeEvidence),
627
+ existenceCategory: deriveEnvelopeExistence(options.toolTier, state, answer.runtimeEvidence),
628
+ provenance: PROVENANCE,
629
+ });
630
+ const frame = (envelope) => ({
631
+ content: [{ type: "text", text: narrate(envelope) }],
632
+ structuredContent: envelope,
633
+ });
634
+ const rendered = frame(build(result.data, [], null));
635
+ const ceiling = options.maxReplyBytes ?? MAX_REPLY_BYTES;
636
+ if (answer.fitToBudget === undefined || sizeOf(rendered) <= ceiling)
637
+ return rendered;
638
+ // The tool is asked for a payload that fits in what is left once everything
639
+ // else is paid for. Its own disclosures cost bytes too, so the first budget
640
+ // can be slightly optimistic — the loop re-measures and asks again with the
641
+ // corrected figure rather than assuming one pass converges. Two passes is
642
+ // the observed cost; the third exists so a pathological oscillation ends.
643
+ let fitted = rendered;
644
+ for (let attempt = 0; attempt < 3; attempt += 1) {
645
+ const payloadBytes = Buffer.byteLength(JSON.stringify(dataOf(fitted)) ?? "null", "utf8");
646
+ const budget = ceiling - (sizeOf(fitted) - payloadBytes);
647
+ const fit = answer.fitToBudget(Math.max(budget, 0));
648
+ if (fit.truncatedDetail !== undefined && fit.notes.length === 0) {
649
+ throw new DisclosureError(`${tool} dropped items to fit the reply's size budget without saying so. A reply shrunk in ` +
650
+ "silence reads as a complete one — the same failure invariant 1 covers for a query's own " +
651
+ "truncation, arriving by a different route.");
652
+ }
653
+ fitted = frame(build(fit.data, fit.notes, fit.truncatedDetail ?? null));
654
+ if (sizeOf(fitted) <= ceiling)
655
+ break;
656
+ }
657
+ // Returned even if the last pass is still over: a tool that cannot shrink
658
+ // below the ceiling has an oversized answer either way, and an oversized
659
+ // answer beats a fabricated small one.
660
+ return fitted;
661
+ }
662
+ function sizeOf(rendered) {
663
+ return Buffer.byteLength(JSON.stringify(rendered), "utf8");
664
+ }
665
+ /** The `data` inside a rendered frame, for measuring its share of the total. */
666
+ function dataOf(rendered) {
667
+ return rendered.structuredContent.data;
668
+ }
669
+ /**
670
+ * The text an agent actually reads.
671
+ *
672
+ * Ordered so that the disclosures are **last**, because that is what a model
673
+ * summarising the reply keeps. A caveat buried above the data is a caveat that
674
+ * gets dropped in the summary the developer sees.
675
+ */
676
+ function narrate(envelope) {
677
+ const lines = [envelope.headline];
678
+ if (envelope.state !== "ok" && envelope.state !== "empty") {
679
+ lines.push("", `**${envelope.state}.** ` +
680
+ (envelope.state === "refused"
681
+ ? "This is not a result — see below for why Descry could not look."
682
+ : envelope.state === "failed"
683
+ ? "Something broke while answering this call — see below."
684
+ : "The call ran out of its declared time budget — see below."));
685
+ }
686
+ else if (envelope.state === "empty") {
687
+ lines.push("", "**Empty.** Descry looked completely and there was nothing to find — see below.");
688
+ }
689
+ // Three cases, not two (F3 / Part A §3, UAT Phase 5): `commitSha === null`
690
+ // is the only condition that actually means "nothing has been analysed" —
691
+ // `renderRefusedInput`/`renderFailed`/`renderTimedOut` used to hardcode it
692
+ // regardless of what the stored graph held, so every argument-validation
693
+ // refusal against an already-populated graph asserted a specific, checkable,
694
+ // false claim about the user's code. `resolutionFloor === null` is the
695
+ // existing, already-true signal for "this call never consulted the graph"
696
+ // (see the field's own doc on `AnswerEnvelope.graph`) — reused here rather
697
+ // than adding a second one that could drift from it.
698
+ const stale = envelope.graph.commitSha === null
699
+ ? "The graph is empty — nothing has been analysed into it yet."
700
+ : envelope.graph.resolutionFloor === null
701
+ ? `Graph at ${envelope.graph.commitSha.slice(0, 12)}` +
702
+ (envelope.graph.builtAt === null ? "" : `, built ${envelope.graph.builtAt}`) +
703
+ " is stored, but this reply made no use of it — no resolution or reliability class applies here."
704
+ : `Graph at ${envelope.graph.commitSha.slice(0, 12)}` +
705
+ (envelope.graph.builtAt === null ? "" : `, built ${envelope.graph.builtAt}`) +
706
+ `. Weakest evidence R${envelope.graph.resolutionFloor}; findings here cap at ` +
707
+ `reliability class ${envelope.graph.reliabilityCap}.`;
708
+ lines.push("", stale);
709
+ if (envelope.disclosures.length > 0) {
710
+ lines.push("", "What this answer does not cover:");
711
+ for (const note of envelope.disclosures)
712
+ lines.push(`- ${note}`);
713
+ }
714
+ return lines.join("\n");
715
+ }
716
+ /**
717
+ * A malformed call the server cannot even attribute to one tool — the RPC
718
+ * named no string tool, or named a tool that does not exist in the registry.
719
+ *
720
+ * Returned as a tool result with `isError`, not as a JSON-RPC error, which is
721
+ * what the MCP spec asks for: protocol errors are for malformed protocol, tool
722
+ * errors are for tools, and an agent can act on the second only if it arrives as
723
+ * content it can read.
724
+ *
725
+ * Deliberately outside the five-state envelope, and narrowly so: there is no
726
+ * resolved `ToolDefinition` here to draw `toolVersion`/`class`/`tier` from, so
727
+ * no real envelope can be built — inventing placeholder values for them would
728
+ * be worse than the bare shape. Once a tool *is* identified, a bad argument
729
+ * against it is `renderRefusedInput`'s case, not this one — see that
730
+ * function's doc for why the two are different claims.
731
+ */
732
+ export function renderToolError(tool, message) {
733
+ return {
734
+ content: [{ type: "text", text: `${tool}: ${message}` }],
735
+ structuredContent: { tool, status: "error", message },
736
+ isError: true,
737
+ };
738
+ }
739
+ /**
740
+ * `state: "refused"` — the call named a real, resolved tool, but its input
741
+ * was wrong, so the tool never ran (`ToolInputError`, thrown by a tool's own
742
+ * argument reading, or by the dispatcher's own shape check on `arguments`
743
+ * once a tool is known).
744
+ *
745
+ * This used to fall through to `renderToolError`'s bare `{tool, status,
746
+ * message}` shape — measured as a real defect (a live `observe_runtime` call
747
+ * with a malformed `profile` field returned that bare shape instead of an
748
+ * envelope), because unlike the two cases `renderToolError` still covers, a
749
+ * `ToolDefinition` *is* available here: `toolVersion`, `class` and `tier` are
750
+ * all real, so a real envelope can be built rather than invented. "The tool
751
+ * was never run because its input was wrong" is a fact about this call, not
752
+ * about the code being examined — exactly what `state: "refused"` already
753
+ * means everywhere else in this file, so it is rendered the same way rather
754
+ * than through a second, parallel shape.
755
+ *
756
+ * `data` is always `null` (§2.2): nothing ran, so there is nothing to stamp.
757
+ * `isError` stays `true` for the same reason `renderFailed`/`renderTimedOut`
758
+ * set it — a generic client that only reads the transport-level signal still
759
+ * sees a failure; an AI-layer-aware caller additionally gets the real
760
+ * `state`/`disclosures`/`reportCategory` to reason about.
761
+ */
762
+ export function renderRefusedInput(tool, message, options) {
763
+ const stamp = options.graphStamp;
764
+ const envelope = {
765
+ tool,
766
+ toolVersion: options.toolVersion,
767
+ callId: options.callId ?? randomUUID(),
768
+ state: "refused",
769
+ class: options.toolClass,
770
+ headline: `${tool} could not run: ${message}`,
771
+ data: null,
772
+ graph: {
773
+ // Facts about the *stored* graph, independent of whether this refusal
774
+ // ever reached it (F3, UAT Phase 5) — `renderHandshake` already reads
775
+ // these from `graphStamp` the same way; `resolutionFloor` stays `null`
776
+ // regardless, because this call genuinely never consulted the graph.
777
+ commitSha: stamp?.commitSha ?? null,
778
+ builtAt: stamp?.graphBuiltAt === undefined || stamp.graphBuiltAt === null ? null : new Date(stamp.graphBuiltAt).toISOString(),
779
+ irSchemaVersion: stamp?.irSchemaVersion ?? 0,
780
+ resolutionFloor: null,
781
+ reliabilityCap: "C",
782
+ freshness: freshnessField(stamp?.commitSha ?? null, options.freshness),
783
+ },
784
+ truncated: null,
785
+ refusals: null,
786
+ budget: { elapsedMs: options.elapsedMs, limitMs: options.limitMs },
787
+ disclosures: [...aliasNotes(options), message, ...(options.configDrift?.note === undefined ? [] : [options.configDrift.note])],
788
+ configStale: options.configDrift?.stale ?? false,
789
+ // Same rule `deriveEnvelopeCategory` applies everywhere else: a
790
+ // conclusion-tier tool states its verdict in `headline`'s own words, so
791
+ // this stays null there; an evidence-tier tool's refusal maps to "not
792
+ // analysable" — "could not look" either way, whether the graph refused
793
+ // the query or the call never reached the graph at all.
794
+ reportCategory: options.toolTier === "conclusion" ? null : "not analysable",
795
+ // A refusal read no evidence, so it makes no existence claim — see the field doc.
796
+ existenceCategory: null,
797
+ provenance: PROVENANCE,
798
+ };
799
+ return {
800
+ content: [{ type: "text", text: narrate(envelope) }],
801
+ structuredContent: envelope,
802
+ isError: true,
803
+ };
804
+ }
805
+ /**
806
+ * `state: "failed"` — §1.1: "promoted from a transport error to a result."
807
+ *
808
+ * An unexpected/internal error, not a bad argument (`renderToolError` still
809
+ * handles those). `data` is always `null` here (§2.2): the tool does not know
810
+ * what happened, and anything it returned would be a guess about its own
811
+ * fault. `isError` stays `true` so a generic MCP client that only reads the
812
+ * transport-level signal still treats this as a failure; an AI-layer-aware
813
+ * caller additionally gets `state: "failed"` in `structuredContent` to reason
814
+ * about rather than a bare `{tool, status, message}` shape.
815
+ *
816
+ * `headline` is optional and exists for the *explained* half of "failed": a
817
+ * `ConfigError` (and any sibling raised specifically to be read, not merely
818
+ * caught) already carries a full written reason, and burying that behind a
819
+ * generic "X failed." while the real sentence sits only in `disclosures` is
820
+ * the bug 13.3.5 names — a caller reading just the headline learns nothing.
821
+ * An unexplained internal fault has no such sentence to offer, so it keeps
822
+ * the opaque default.
823
+ */
824
+ export function renderFailed(tool, message, options, headline) {
825
+ const stamp = options.graphStamp;
826
+ const envelope = {
827
+ tool,
828
+ toolVersion: options.toolVersion,
829
+ callId: options.callId ?? randomUUID(),
830
+ state: "failed",
831
+ class: options.toolClass,
832
+ headline: headline ?? `${tool} failed.`,
833
+ data: null,
834
+ graph: {
835
+ // See `renderRefusedInput`'s identical comment (F3, UAT Phase 5): a
836
+ // fault is not a claim about the graph, but "nothing has been analysed"
837
+ // is a claim about the graph, and stating it falsely is worse than not
838
+ // stating a stored commit this call happened not to read.
839
+ commitSha: stamp?.commitSha ?? null,
840
+ builtAt: stamp?.graphBuiltAt === undefined || stamp.graphBuiltAt === null ? null : new Date(stamp.graphBuiltAt).toISOString(),
841
+ irSchemaVersion: stamp?.irSchemaVersion ?? 0,
842
+ resolutionFloor: null,
843
+ reliabilityCap: "C",
844
+ freshness: freshnessField(stamp?.commitSha ?? null, options.freshness),
845
+ },
846
+ truncated: null,
847
+ refusals: null,
848
+ budget: { elapsedMs: options.elapsedMs, limitMs: options.limitMs },
849
+ disclosures: [...aliasNotes(options), message, ...(options.configDrift?.note === undefined ? [] : [options.configDrift.note])],
850
+ configStale: options.configDrift?.stale ?? false,
851
+ // A fault is not a claim about the code either way (§20) — no category applies.
852
+ reportCategory: null,
853
+ existenceCategory: null,
854
+ provenance: PROVENANCE,
855
+ };
856
+ return {
857
+ content: [{ type: "text", text: narrate(envelope) }],
858
+ structuredContent: envelope,
859
+ isError: true,
860
+ };
861
+ }
862
+ /**
863
+ * The one legitimate producer of a pending-handshake payload — §7's mint call.
864
+ *
865
+ * `class == "action"` returning anything other than this shape on an
866
+ * unconfirmed first call is invariant 4 (§2.1): "throw." This function is
867
+ * where that shape is produced, so the throw lives at the one call site that
868
+ * builds anything else for an action tool's first call — `server.ts`'s
869
+ * dispatch loop calls this and only this when `resolveActionDispatch` returns
870
+ * `{kind: "handshake"}`, never `tool.run`.
871
+ *
872
+ * `state: "ok"` deliberately: a mint is not a refusal, a failure or an empty
873
+ * result — it is a successful "here is what I would do", and the *pending*
874
+ * flag inside `data` is what tells a caller nothing was performed yet.
875
+ */
876
+ export function renderHandshake(tool, token, willDo, options) {
877
+ const stamp = options.graphStamp;
878
+ const envelope = {
879
+ tool,
880
+ toolVersion: options.toolVersion,
881
+ callId: options.callId ?? randomUUID(),
882
+ state: "ok",
883
+ class: options.toolClass,
884
+ headline: `Confirmation required: ${willDo}`,
885
+ data: { pending: true, token, willDo },
886
+ graph: {
887
+ commitSha: stamp?.commitSha ?? null,
888
+ builtAt: stamp?.graphBuiltAt === undefined || stamp.graphBuiltAt === null ? null : new Date(stamp.graphBuiltAt).toISOString(),
889
+ irSchemaVersion: stamp?.irSchemaVersion ?? 0,
890
+ // Nothing has been read for THIS call — a mint makes no claim, so it
891
+ // carries no resolution/reliability of its own, the same reasoning
892
+ // `renderFailed`/`renderTimedOut` use. Only commitSha/builtAt/
893
+ // irSchemaVersion are real facts about the stored graph, independent of
894
+ // whether this pending action has been confirmed yet.
895
+ resolutionFloor: null,
896
+ reliabilityCap: "C",
897
+ freshness: freshnessField(stamp?.commitSha ?? null, options.freshness),
898
+ },
899
+ truncated: null,
900
+ refusals: null,
901
+ budget: { elapsedMs: options.elapsedMs, limitMs: options.limitMs },
902
+ disclosures: [
903
+ ...aliasNotes(options),
904
+ `Nothing was performed. Call ${tool} again with { "confirmToken": "${token}" } to actually do this — ` +
905
+ "the token expires in five minutes and is single-use.",
906
+ ...(options.configDrift?.note === undefined ? [] : [options.configDrift.note]),
907
+ ],
908
+ configStale: options.configDrift?.stale ?? false,
909
+ // Nothing was analysed yet — a confirmation prompt makes no claim to categorize.
910
+ reportCategory: null,
911
+ existenceCategory: null,
912
+ provenance: PROVENANCE,
913
+ };
914
+ return {
915
+ content: [{ type: "text", text: narrate(envelope) }],
916
+ structuredContent: envelope,
917
+ };
918
+ }
919
+ /**
920
+ * `state: "timed_out"` — the call ran out of its declared budget.
921
+ *
922
+ * §6: "not a silent truncation and never a `failed`". `data` is `null` here
923
+ * because none of the registered tools currently produce genuine partial
924
+ * results mid-run (none checks `ctx.signal` internally yet — a disclosed gap,
925
+ * not a silent one, named in the returned disclosure); the envelope shape
926
+ * supports a populated partial `data` the day a tool does.
927
+ */
928
+ export function renderTimedOut(tool, options) {
929
+ const stamp = options.graphStamp;
930
+ const envelope = {
931
+ tool,
932
+ toolVersion: options.toolVersion,
933
+ callId: options.callId ?? randomUUID(),
934
+ state: "timed_out",
935
+ class: options.toolClass,
936
+ headline: `${tool} timed out.`,
937
+ data: null,
938
+ graph: {
939
+ // See `renderRefusedInput`'s identical comment (F3, UAT Phase 5).
940
+ commitSha: stamp?.commitSha ?? null,
941
+ builtAt: stamp?.graphBuiltAt === undefined || stamp.graphBuiltAt === null ? null : new Date(stamp.graphBuiltAt).toISOString(),
942
+ irSchemaVersion: stamp?.irSchemaVersion ?? 0,
943
+ resolutionFloor: null,
944
+ reliabilityCap: "C",
945
+ freshness: freshnessField(stamp?.commitSha ?? null, options.freshness),
946
+ },
947
+ truncated: null,
948
+ refusals: null,
949
+ budget: { elapsedMs: options.elapsedMs, limitMs: options.limitMs },
950
+ disclosures: [
951
+ ...aliasNotes(options),
952
+ `${tool} did not finish within its ${options.limitMs}ms budget. This is a partial answer, not ` +
953
+ "a finding of failure or of absence — retry, or narrow the request.",
954
+ ...(options.configDrift?.note === undefined ? [] : [options.configDrift.note]),
955
+ ],
956
+ configStale: options.configDrift?.stale ?? false,
957
+ // A partial run in progress makes no claim to categorize yet.
958
+ reportCategory: null,
959
+ existenceCategory: null,
960
+ provenance: PROVENANCE,
961
+ };
962
+ return {
963
+ content: [{ type: "text", text: narrate(envelope) }],
964
+ structuredContent: envelope,
965
+ isError: true,
966
+ };
967
+ }
968
+ //# sourceMappingURL=render.js.map