@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
@@ -0,0 +1,779 @@
1
+ /**
2
+ * One repo, one graph, one process — owns the SQLite handle, config, sources; tools
3
+ * own nothing. `.descry/config.json` is optional, defaults to local-first, except the
4
+ * source list (this repo names no adapter, `registry.ts`): unconfigured sessions can
5
+ * query, not build. Graph opens lazily so a missing one fails as a tool result, not a crash. */
6
+ import { existsSync } from "node:fs";
7
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
8
+ import { basename, isAbsolute, join, resolve } from "node:path";
9
+ import { isConfirmedFactScope } from "@descryy/ir";
10
+ import { computeGraphFreshness, createStoreProvider, distinctFilePaths, gitHead, isGitRepository, NodeSqliteDriver, openStore, rebuildStaleGraph, } from "@descryy/core";
11
+ import { BROWSER_BACKEND_NOT_INSTALLED } from "./browser/provider.js";
12
+ import { createBrowserSessionRegistry } from "./browser/registry.js";
13
+ import { normaliseSpec } from "./registry.js";
14
+ export const CONFIG_RELATIVE_PATH = join(".descry", "config.json");
15
+ export const DEFAULT_GRAPH_RELATIVE_PATH = join(".descry", "graph.db");
16
+ function parseSavedReadiness(raw, configPath, service) {
17
+ const e = (typeof raw === "object" && raw !== null ? raw : {});
18
+ const kind = e["kind"];
19
+ if (kind !== "http" && kind !== "tcp-port" && kind !== "command") {
20
+ throw new ConfigError(`${configPath}: savedStart.services.${service}.readiness.kind must be "http", "tcp-port" or "command"`);
21
+ }
22
+ const path = typeof e["path"] === "string" ? e["path"] : undefined;
23
+ const expectedStatus = typeof e["expectedStatus"] === "number" ? e["expectedStatus"] : undefined;
24
+ const host = typeof e["host"] === "string" ? e["host"] : undefined;
25
+ const command = typeof e["command"] === "string" ? e["command"] : undefined;
26
+ const rawArgs = e["args"];
27
+ const args = Array.isArray(rawArgs) && rawArgs.every((a) => typeof a === "string") ? rawArgs : undefined;
28
+ const timeoutMs = typeof e["timeoutMs"] === "number" ? e["timeoutMs"] : undefined;
29
+ return {
30
+ kind,
31
+ ...(path === undefined ? {} : { path }),
32
+ ...(expectedStatus === undefined ? {} : { expectedStatus }),
33
+ ...(host === undefined ? {} : { host }),
34
+ ...(command === undefined ? {} : { command }),
35
+ ...(args === undefined ? {} : { args }),
36
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
37
+ };
38
+ }
39
+ /** `savedStart`'s reader, paired with `writeSavedStart` below. Lenient the same way
40
+ * `parseConfirmedIncidents` is about a malformed sub-object — coerced to `{}` rather
41
+ * than thrown on — because this file's own writer is the only thing that ever
42
+ * produces this shape; a hand-edited config that gets it wrong is refused field by
43
+ * field below, same as every other section here. */
44
+ function parseSavedStart(raw, configPath) {
45
+ if (raw === undefined)
46
+ return undefined;
47
+ const e = (typeof raw === "object" && raw !== null ? raw : {});
48
+ const savedAt = e["savedAt"];
49
+ if (typeof savedAt !== "string" || savedAt === "") {
50
+ throw new ConfigError(`${configPath}: "savedStart.savedAt" must be a non-empty string`);
51
+ }
52
+ const servicesRaw = e["services"];
53
+ if (typeof servicesRaw !== "object" || servicesRaw === null || Array.isArray(servicesRaw)) {
54
+ throw new ConfigError(`${configPath}: "savedStart.services" must be an object`);
55
+ }
56
+ const services = {};
57
+ for (const [name, value] of Object.entries(servicesRaw)) {
58
+ const v = (typeof value === "object" && value !== null ? value : {});
59
+ const command = typeof v["command"] === "string" ? v["command"] : undefined;
60
+ const cwd = v["cwd"];
61
+ if (typeof cwd !== "string" || cwd === "") {
62
+ throw new ConfigError(`${configPath}: "savedStart.services.${name}.cwd" must be a non-empty string`);
63
+ }
64
+ const port = typeof v["port"] === "number" ? v["port"] : undefined;
65
+ const rawDependsOn = v["dependsOn"];
66
+ const dependsOn = Array.isArray(rawDependsOn) && rawDependsOn.every((d) => typeof d === "string")
67
+ ? rawDependsOn
68
+ : undefined;
69
+ const rawEnvNames = v["envNames"];
70
+ const envNames = Array.isArray(rawEnvNames) && rawEnvNames.every((n) => typeof n === "string")
71
+ ? rawEnvNames
72
+ : undefined;
73
+ const attachRaw = v["attach"];
74
+ let attach;
75
+ if (attachRaw !== undefined) {
76
+ const a = (typeof attachRaw === "object" && attachRaw !== null ? attachRaw : {});
77
+ if (typeof a["pid"] !== "number" || typeof a["logFilePath"] !== "string") {
78
+ throw new ConfigError(`${configPath}: "savedStart.services.${name}.attach" must have a numeric "pid" and a string "logFilePath"`);
79
+ }
80
+ attach = { pid: a["pid"], logFilePath: a["logFilePath"] };
81
+ }
82
+ services[name] = {
83
+ ...(command === undefined ? {} : { command }),
84
+ cwd,
85
+ ...(port === undefined ? {} : { port }),
86
+ ...(dependsOn === undefined ? {} : { dependsOn }),
87
+ ...(envNames === undefined ? {} : { envNames }),
88
+ ...(attach === undefined ? {} : { attach }),
89
+ readiness: parseSavedReadiness(v["readiness"], configPath, name),
90
+ };
91
+ }
92
+ return { services, savedAt };
93
+ }
94
+ function parseConfirmedIncidents(raw, configPath) {
95
+ if (raw === undefined)
96
+ return [];
97
+ if (!Array.isArray(raw)) {
98
+ throw new ConfigError(`${configPath}: "confirmedIncidents" must be an array`);
99
+ }
100
+ return raw.map((entry, index) => {
101
+ const e = entry;
102
+ for (const key of ["repo", "summary", "confirmedBy", "confirmedAt"]) {
103
+ if (typeof e[key] !== "string" || e[key] === "") {
104
+ throw new ConfigError(`${configPath}: confirmedIncidents[${index}].${key} must be a non-empty string`);
105
+ }
106
+ }
107
+ const files = e["files"];
108
+ if (!Array.isArray(files) || files.length === 0 || files.some((f) => typeof f !== "string" || f === "")) {
109
+ throw new ConfigError(`${configPath}: confirmedIncidents[${index}].files must be a non-empty array of paths — ` +
110
+ "an incident naming no file has nothing to attach it to");
111
+ }
112
+ for (const key of ["errorText", "fixedAtCommit", "observedInRun"]) {
113
+ if (e[key] !== undefined && (typeof e[key] !== "string" || e[key] === "")) {
114
+ throw new ConfigError(`${configPath}: confirmedIncidents[${index}].${key} must be a non-empty string when present`);
115
+ }
116
+ }
117
+ const occurrences = e["occurrences"];
118
+ if (occurrences !== undefined &&
119
+ (!Array.isArray(occurrences) ||
120
+ occurrences.length === 0 ||
121
+ occurrences.some((o) => typeof o !== "string" || o === ""))) {
122
+ throw new ConfigError(`${configPath}: confirmedIncidents[${index}].occurrences must be a non-empty array of timestamps when present`);
123
+ }
124
+ return {
125
+ repo: e["repo"],
126
+ summary: e["summary"],
127
+ files: files,
128
+ ...(e["errorText"] === undefined ? {} : { errorText: e["errorText"] }),
129
+ ...(e["fixedAtCommit"] === undefined ? {} : { fixedAtCommit: e["fixedAtCommit"] }),
130
+ ...(e["observedInRun"] === undefined ? {} : { observedInRun: e["observedInRun"] }),
131
+ confirmedBy: e["confirmedBy"],
132
+ confirmedAt: e["confirmedAt"],
133
+ ...(occurrences === undefined ? {} : { occurrences: occurrences }),
134
+ };
135
+ });
136
+ }
137
+ function parseConfirmedFacts(raw, configPath) {
138
+ if (raw === undefined)
139
+ return [];
140
+ if (!Array.isArray(raw)) {
141
+ throw new ConfigError(`${configPath}: "confirmedFacts" must be an array`);
142
+ }
143
+ return raw.map((entry, index) => {
144
+ const e = entry;
145
+ for (const key of ["repo", "blockedBy", "value", "confirmedBy", "confirmedAt"]) {
146
+ if (typeof e[key] !== "string" || e[key] === "") {
147
+ throw new ConfigError(`${configPath}: confirmedFacts[${index}].${key} must be a non-empty string`);
148
+ }
149
+ }
150
+ for (const key of ["environment", "confirmedAtCommit", "workspace"]) {
151
+ if (e[key] !== undefined && (typeof e[key] !== "string" || e[key] === "")) {
152
+ throw new ConfigError(`${configPath}: confirmedFacts[${index}].${key} must be a non-empty string when present`);
153
+ }
154
+ }
155
+ if (e["scope"] !== undefined && !isConfirmedFactScope(e["scope"])) {
156
+ throw new ConfigError(`${configPath}: confirmedFacts[${index}].scope must be "repo" or "workspace" when present`);
157
+ }
158
+ return {
159
+ repo: e["repo"],
160
+ blockedBy: e["blockedBy"],
161
+ value: e["value"],
162
+ ...(e["environment"] === undefined ? {} : { environment: e["environment"] }),
163
+ confirmedBy: e["confirmedBy"],
164
+ confirmedAt: e["confirmedAt"],
165
+ // Round-tripped rather than dropped — `confirmedAtCommit` was written by
166
+ // `writeConfirmedFact`; discarding it on reload would silence the staleness disclosure.
167
+ ...(e["confirmedAtCommit"] === undefined ? {} : { confirmedAtCommit: e["confirmedAtCommit"] }),
168
+ ...(e["scope"] === undefined ? {} : { scope: e["scope"] }),
169
+ ...(e["workspace"] === undefined ? {} : { workspace: e["workspace"] }),
170
+ };
171
+ });
172
+ }
173
+ /** `(repo, blockedBy, environment)` — `undefined` environment is its own value, never a wildcard. */
174
+ function sameFactKey(a, b) {
175
+ return a.repo === b.repo && a.blockedBy === b.blockedBy && a.environment === b.environment;
176
+ }
177
+ export class ConfigError extends Error {
178
+ constructor(message) {
179
+ super(message);
180
+ this.name = "ConfigError";
181
+ }
182
+ }
183
+ export async function loadConfig(repoPath) {
184
+ const configPath = join(repoPath, CONFIG_RELATIVE_PATH);
185
+ let raw = {};
186
+ let found = null;
187
+ try {
188
+ raw = JSON.parse(await readFile(configPath, "utf8"));
189
+ found = configPath;
190
+ }
191
+ catch (error) {
192
+ const code = error.code;
193
+ if (code !== "ENOENT") {
194
+ // A config that exists and can't be parsed is refused, not defaulted past —
195
+ // silently falling back would run the session under settings the user thinks they overrode.
196
+ throw new ConfigError(`${configPath} exists but could not be read: ${error.message}`);
197
+ }
198
+ }
199
+ if (raw.sources !== undefined && !Array.isArray(raw.sources)) {
200
+ throw new ConfigError(`${configPath}: "sources" must be an array`);
201
+ }
202
+ const sources = (raw.sources ?? []).map((entry, index) => {
203
+ if (typeof entry === "string")
204
+ return normaliseSpec(entry);
205
+ if (typeof entry === "object" && entry !== null && typeof entry.module === "string") {
206
+ return entry;
207
+ }
208
+ throw new ConfigError(`${configPath}: sources[${index}] must be a module specifier or an object with a "module" field`);
209
+ });
210
+ const workspace = typeof raw.workspace === "string" && raw.workspace !== "" ? raw.workspace : undefined;
211
+ const workspaceRepos = parseWorkspaceRepos(raw.workspaceRepos, configPath);
212
+ const savedStart = parseSavedStart(raw.savedStart, configPath);
213
+ return {
214
+ // Directory name is a defensible default and poor identity — changes on clone,
215
+ // and every repo-scoped node id changes with it. Disclosed in `analyze`, not
216
+ // discovered when two machines produce different graphs.
217
+ repo: typeof raw.repo === "string" && raw.repo !== "" ? raw.repo : basename(resolve(repoPath)),
218
+ workspace,
219
+ graph: typeof raw.graph === "string" && raw.graph !== "" ? raw.graph : DEFAULT_GRAPH_RELATIVE_PATH,
220
+ sources,
221
+ confirmedFacts: parseConfirmedFacts(raw.confirmedFacts, configPath),
222
+ confirmedIncidents: parseConfirmedIncidents(raw.confirmedIncidents, configPath),
223
+ workspaceRepos,
224
+ linkedFacts: await readLinkedFacts(repoPath, workspace, workspaceRepos),
225
+ ...(savedStart === undefined ? {} : { savedStart }),
226
+ configPath: found,
227
+ };
228
+ }
229
+ /**
230
+ * Fields compared for staleness (13.3.16 / N2). Deliberately excludes `linkedFacts`:
231
+ * it is derived from sibling repos' own files at load time, not something a local
232
+ * edit to *this* repo's config changes, and diffing it here would double-count
233
+ * `workspaceRepos` drift under a second name.
234
+ */
235
+ const DRIFT_FIELDS = [
236
+ "repo",
237
+ "workspace",
238
+ "graph",
239
+ "sources",
240
+ "confirmedFacts",
241
+ "confirmedIncidents",
242
+ "workspaceRepos",
243
+ "savedStart",
244
+ "configPath",
245
+ ];
246
+ function driftedKeys(onDisk, loaded) {
247
+ return DRIFT_FIELDS.filter((key) => JSON.stringify(onDisk[key]) !== JSON.stringify(loaded[key]));
248
+ }
249
+ /**
250
+ * 13.3.16 (N2): a count match on `sources` is not a content match — two sessions
251
+ * swapping one adapter for another, or changing an adapter's options, leave
252
+ * `sources.length` unchanged, which is exactly the gap that let a concurrent edit
253
+ * go undetected and left a graph stuck at R0 with no disclosure of why. Every
254
+ * field is compared by value, and the note names which ones actually changed —
255
+ * `graph`, `confirmedFacts`/`confirmedIncidents` and `workspaceRepos` were
256
+ * previously not compared at all.
257
+ */
258
+ export function configDriftNote(onDisk, loaded) {
259
+ if (onDisk === null)
260
+ return undefined;
261
+ const changed = driftedKeys(onDisk, loaded);
262
+ if (changed.length === 0)
263
+ return undefined;
264
+ return ("The on-disk .descry/config.json no longer matches what this server loaded at startup " +
265
+ `(changed: ${changed.join(", ")}). Configuration is read once per process, not on every call ` +
266
+ "(session.ts) — this run is still using the configuration this server started with, not the " +
267
+ "current file. Restart the MCP server for on-disk changes to take effect; the notes below " +
268
+ "describe the stale, running configuration, not what is actually on disk now.");
269
+ }
270
+ /**
271
+ * 13.3.16 (N2) steps 2-3: called once per tool call from `server.ts`, not only from
272
+ * `analyze`, so every reply discloses a stale session rather than the one tool that
273
+ * happened to re-read the config. A config that cannot even be read right now
274
+ * (deleted mid-session, or edited into something `loadConfig` refuses) is itself
275
+ * drift and must not read as "no drift" merely because there is nothing to diff
276
+ * against — the previous `.catch(() => null)` collapsed that case into silence.
277
+ */
278
+ export async function checkConfigDrift(session) {
279
+ if (!existsSync(session.repoPath))
280
+ return { stale: false, note: undefined };
281
+ try {
282
+ const onDisk = await loadConfig(session.repoPath);
283
+ const note = configDriftNote(onDisk, session.config);
284
+ return { stale: note !== undefined, note };
285
+ }
286
+ catch (error) {
287
+ const reason = error instanceof Error ? error.message : String(error);
288
+ return {
289
+ stale: true,
290
+ note: `The on-disk .descry/config.json could not be read as of this call (${reason}). This run ` +
291
+ "is still using the configuration loaded at startup; whether that configuration still " +
292
+ "matches what is on disk now cannot be determined.",
293
+ };
294
+ }
295
+ }
296
+ /**
297
+ * Lane "map-freshness" item 3 — starting value only, **not measured**
298
+ * (lightweight mode: this lane could not run a 1-file-change refresh against
299
+ * a large reference repo and time it). `DEC-NEXT-auto-refresh-budget` records
300
+ * this exact number and says plainly that it is a guess, not a measurement —
301
+ * whoever next runs `npm run build && npm run lint && node --test` with a
302
+ * real reference repo available should replace it with a measured p50/p95
303
+ * and update that decision file rather than this comment alone.
304
+ */
305
+ export const AUTO_REFRESH_BUDGET_MS = 10_000;
306
+ /**
307
+ * Lane "map-freshness" item 1 — whether the stored graph still matches the
308
+ * repository on disk, read once per call from `server.ts`, the same place
309
+ * `checkConfigDrift` is read from. `undefined` when there is nothing honest
310
+ * to report: the graph is empty (`baseStamp().commitSha === null` — nothing
311
+ * to be fresh or stale *relative to*, same reasoning `computeGraphFreshness`
312
+ * itself already documents) or the stamp/driver could not be read at all
313
+ * (`freshnessField()` in `render.ts` reports that as `unknown` once a real
314
+ * `commitSha` exists downstream, never as a false `current`).
315
+ *
316
+ * Deliberately not a `Session` method (`Session#checkFreshness()` in an
317
+ * earlier note here was this function's own working name) — `checkConfigDrift`
318
+ * is a standalone function taking `session` for the same reason: it needs no
319
+ * private state this class doesn't already expose, and keeping it a plain
320
+ * function keeps this module free of a growing method surface for what are,
321
+ * one at a time, one-shot per-call reads.
322
+ */
323
+ export async function checkFreshness(session) {
324
+ let commitSha;
325
+ try {
326
+ commitSha = session.provider().baseStamp().commitSha;
327
+ }
328
+ catch {
329
+ return undefined;
330
+ }
331
+ if (commitSha === null)
332
+ return undefined;
333
+ try {
334
+ const indexedFiles = distinctFilePaths(session.store().driver);
335
+ return await computeGraphFreshness({ repoPath: session.repoPath, commitSha, indexedFiles });
336
+ }
337
+ catch {
338
+ return undefined;
339
+ }
340
+ }
341
+ function parseWorkspaceRepos(raw, configPath) {
342
+ if (raw === undefined)
343
+ return [];
344
+ if (!Array.isArray(raw)) {
345
+ throw new ConfigError(`${configPath}: "workspaceRepos" must be an array`);
346
+ }
347
+ return raw.map((entry, index) => {
348
+ const e = entry;
349
+ for (const key of ["path", "repo"]) {
350
+ if (typeof e[key] !== "string" || e[key] === "") {
351
+ throw new ConfigError(`${configPath}: workspaceRepos[${index}].${key} must be a non-empty string`);
352
+ }
353
+ }
354
+ return { path: e["path"], repo: e["repo"] };
355
+ });
356
+ }
357
+ /** Workspace-scoped facts from this workspace's *other* repos, one level only (no
358
+ * recursive follow of a sibling's own `workspaceRepos`). Every failure here is silent
359
+ * by design — a broken/uncloned sibling must not block this session; `questions` discloses it. */
360
+ async function readLinkedFacts(repoPath, workspace, repos) {
361
+ if (workspace === undefined || repos.length === 0)
362
+ return [];
363
+ const self = resolve(repoPath);
364
+ const out = [];
365
+ for (const entry of repos) {
366
+ if (resolve(entry.path) === self)
367
+ continue;
368
+ const path = join(entry.path, CONFIG_RELATIVE_PATH);
369
+ let facts;
370
+ try {
371
+ const raw = JSON.parse(await readFile(path, "utf8"));
372
+ facts = parseConfirmedFacts(raw.confirmedFacts, path);
373
+ }
374
+ catch {
375
+ continue;
376
+ }
377
+ // Checked on the fact itself, not inferred from being found in a linked repo —
378
+ // a repo-scoped fact in a linked config still does not cross.
379
+ for (const fact of facts) {
380
+ if (fact.scope === "workspace" && fact.workspace === workspace)
381
+ out.push(fact);
382
+ }
383
+ }
384
+ return out;
385
+ }
386
+ /** `.descry/config.json`'s writer, paired with `loadConfig`. One writer used by both
387
+ * `descry init` and `link_workspace` so they can't drift into writing different
388
+ * shapes. Patches `repo`/`workspace` only — other fields survive untouched. */
389
+ export async function writeConfig(repoPath, patch) {
390
+ const path = join(repoPath, CONFIG_RELATIVE_PATH);
391
+ let existing = {};
392
+ try {
393
+ existing = JSON.parse(await readFile(path, "utf8"));
394
+ }
395
+ catch (error) {
396
+ // An unparseable config is fixed here (start from empty), unlike `loadConfig`
397
+ // which refuses — this writer's job is to repair, not run a session. ENOENT
398
+ // takes the same path.
399
+ void error;
400
+ existing = {};
401
+ }
402
+ const previousWorkspace = typeof existing.workspace === "string" ? existing.workspace : undefined;
403
+ const next = { ...existing, repo: patch.repo };
404
+ if (patch.workspace !== undefined)
405
+ next.workspace = patch.workspace;
406
+ await mkdir(join(repoPath, ".descry"), { recursive: true });
407
+ await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
408
+ return { path, previousWorkspace };
409
+ }
410
+ /** `workspaceRepos`' writer — one writer per field a shared file owns (see
411
+ * `writeConfig`, `writeConfirmedFact`, `writeSources`). Replaces the whole list
412
+ * rather than merging: membership is one declaration made at one moment. */
413
+ export async function writeWorkspaceRepos(repoPath, repos) {
414
+ const path = join(repoPath, CONFIG_RELATIVE_PATH);
415
+ let existing = {};
416
+ try {
417
+ existing = JSON.parse(await readFile(path, "utf8"));
418
+ }
419
+ catch (error) {
420
+ void error;
421
+ existing = {};
422
+ }
423
+ const next = {
424
+ ...existing,
425
+ workspaceRepos: repos.map((r) => ({ path: r.path, repo: r.repo })),
426
+ };
427
+ await mkdir(join(repoPath, ".descry"), { recursive: true });
428
+ await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
429
+ return { path };
430
+ }
431
+ /** `confirmedFacts`' writer, separate from `writeConfig` (one writer per field). Keyed
432
+ * on exact `(repo, blockedBy, environment)` (DEC-223 §3) — undefined environment is its
433
+ * own key, not a wildcard. Same key replaces; a different environment adds a new fact. */
434
+ export async function writeConfirmedFact(repoPath, fact) {
435
+ const path = join(repoPath, CONFIG_RELATIVE_PATH);
436
+ let existing = {};
437
+ try {
438
+ existing = JSON.parse(await readFile(path, "utf8"));
439
+ }
440
+ catch (error) {
441
+ void error;
442
+ existing = {};
443
+ }
444
+ const priorRaw = Array.isArray(existing["confirmedFacts"]) ? existing["confirmedFacts"] : [];
445
+ const prior = parseConfirmedFacts(priorRaw, path);
446
+ const replaced = prior.find((f) => sameFactKey(f, fact)) ?? null;
447
+ const rest = prior.filter((f) => !sameFactKey(f, fact));
448
+ const next = { ...existing, confirmedFacts: [...rest, fact] };
449
+ await mkdir(join(repoPath, ".descry"), { recursive: true });
450
+ await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
451
+ return { path, replaced };
452
+ }
453
+ /** `confirmedIncidents`' writer — appends a genuinely new incident, but merges a
454
+ * resubmission of the same content into the record already on file (UAT phase 5,
455
+ * F9). `confirmedAt` used to be the dedup key alongside `summary`, and a retried
456
+ * submission always carries a fresh timestamp — so every retry minted a second
457
+ * `INCIDENT` node, silently doubling its weight in blast-radius bias and
458
+ * `similar_incidents` scoring. Identity is now content (`repo` + `summary` +
459
+ * `errorText`, matching `confirmedIncidentId`), and a match appends this call's
460
+ * timestamp to `occurrences` and unions `files` rather than minting a new record.
461
+ * A retry and a genuine recurrence look identical from content alone; both are
462
+ * representable and countable this way, and neither doubles the graph. */
463
+ export async function writeConfirmedIncident(repoPath, incident) {
464
+ const path = join(repoPath, CONFIG_RELATIVE_PATH);
465
+ let existing = {};
466
+ try {
467
+ existing = JSON.parse(await readFile(path, "utf8"));
468
+ }
469
+ catch (error) {
470
+ void error;
471
+ existing = {};
472
+ }
473
+ const priorRaw = Array.isArray(existing["confirmedIncidents"]) ? existing["confirmedIncidents"] : [];
474
+ const prior = parseConfirmedIncidents(priorRaw, path);
475
+ const same = (a) => a.repo === incident.repo &&
476
+ a.summary === incident.summary &&
477
+ (a.errorText ?? "") === (incident.errorText ?? "");
478
+ const match = prior.find(same) ?? null;
479
+ let next;
480
+ let record;
481
+ let occurrences;
482
+ if (match === null) {
483
+ next = [...prior, incident];
484
+ record = incident;
485
+ occurrences = 1;
486
+ }
487
+ else {
488
+ record = {
489
+ ...match,
490
+ // The incident may have been reported with a narrower or wider file list
491
+ // each time — the union is what it has ever been correlated to.
492
+ files: [...new Set([...match.files, ...incident.files])].sort(),
493
+ ...(incident.fixedAtCommit === undefined ? {} : { fixedAtCommit: incident.fixedAtCommit }),
494
+ occurrences: [...(match.occurrences ?? [match.confirmedAt]), incident.confirmedAt],
495
+ };
496
+ next = [...prior.filter((i) => !same(i)), record];
497
+ occurrences = record.occurrences.length;
498
+ }
499
+ await mkdir(join(repoPath, ".descry"), { recursive: true });
500
+ await writeFile(path, `${JSON.stringify({ ...existing, confirmedIncidents: next }, null, 2)}\n`, "utf8");
501
+ return { path, total: next.length, merged: match !== null, occurrences, record, records: next };
502
+ }
503
+ /** `savedStart`'s writer, one writer per field like the others. Called only by
504
+ * `observe-runtime.ts`'s `run`, and only once that run's execution state is
505
+ * `"COMPLETED"` — never on a failed run, so this field's presence already means
506
+ * "reached readiness at least once" without this writer having to track that
507
+ * itself. Overwrites unconditionally: the newest successful run is what is worth
508
+ * remembering, not a merge with whatever an older one declared. `services` must
509
+ * already exclude secret values by the time it reaches here — this writer trusts
510
+ * its caller the same way `writeConfirmedIncident` does, rather than re-scanning
511
+ * a shape it did not define. */
512
+ export async function writeSavedStart(repoPath, services) {
513
+ const path = join(repoPath, CONFIG_RELATIVE_PATH);
514
+ let existing = {};
515
+ try {
516
+ existing = JSON.parse(await readFile(path, "utf8"));
517
+ }
518
+ catch (error) {
519
+ void error;
520
+ existing = {};
521
+ }
522
+ const savedAt = new Date().toISOString();
523
+ const next = { ...existing, savedStart: { services, savedAt } };
524
+ await mkdir(join(repoPath, ".descry"), { recursive: true });
525
+ await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
526
+ return { path, savedAt };
527
+ }
528
+ /** `sources`' writer, one writer per field like the others. Unconditional — the caller
529
+ * (`descry init`) decides whether overwriting is appropriate, not this primitive. */
530
+ export async function writeSources(repoPath, sources) {
531
+ const path = join(repoPath, CONFIG_RELATIVE_PATH);
532
+ let existing = {};
533
+ try {
534
+ existing = JSON.parse(await readFile(path, "utf8"));
535
+ }
536
+ catch (error) {
537
+ void error;
538
+ existing = {};
539
+ }
540
+ const next = { ...existing, sources: [...sources] };
541
+ await mkdir(join(repoPath, ".descry"), { recursive: true });
542
+ await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
543
+ return { path };
544
+ }
545
+ export class Session {
546
+ repoPath;
547
+ // Not `readonly` — `reloadConfig()` is the one caller-initiated exception to
548
+ // "configuration is read once per process" (UAT phase 5, §0 gap 2). Every
549
+ // other read of this field still sees whatever `reloadConfig()` last set,
550
+ // exactly as it did the config loaded at startup.
551
+ config;
552
+ #store = null;
553
+ #provider = null;
554
+ #browserSessions = null;
555
+ #browserDriverFactory = null;
556
+ /**
557
+ * Literal credential values a browser session on this connection was seeded
558
+ * with — B1.
559
+ *
560
+ * Held so the things this server *writes* can be checked against them. Exact
561
+ * values, never patterns, because `@descryy/core`'s redactor is
562
+ * literal-substring for the reason its own header gives: a "looks like a
563
+ * token" regex either destroys real data or misses a secret shaped unlike
564
+ * it. Per connection, in memory only, and compared against rather than
565
+ * printed at every call site that reads it.
566
+ */
567
+ #browserSecrets = new Set();
568
+ #openGraph;
569
+ #calls = [];
570
+ #nextCallId = 1;
571
+ /**
572
+ * Where a `${credential:NAME}` step resolves its value, for a
573
+ * `browser_run_scenario` replay on this connection — RG-1(4) / lane
574
+ * "delivery". Mutable, not constructor-injected, and `undefined` by
575
+ * default: every existing `new Session(...)` call site (54 of them at
576
+ * last count) stays unchanged, and a caller that wants sign-in-by-profile
577
+ * sets it after construction, the same way `config` is mutated in place
578
+ * by `reloadConfig()`. `replayScenario` (`browser/scenario-runner.ts`)
579
+ * already accepts this exact type and already refuses a `${credential:...}`
580
+ * step by name when it is `undefined` — this field is what lets
581
+ * `browser-run-scenario.ts`'s tool actually have one to pass, which
582
+ * nothing did before.
583
+ */
584
+ credentialStore = undefined;
585
+ /**
586
+ * Lane "map-freshness" item 3 — how long `server.ts`'s auto-refresh
587
+ * (`ensureFreshGraph`) will let a `behind` graph's refresh run before
588
+ * giving up and answering from the old graph instead, with the `behind`
589
+ * disclosure standing in place of a "refreshed" one. `undefined` (the
590
+ * default for every existing call site) means "use `AUTO_REFRESH_BUDGET_MS`"
591
+ * — mutable, same shape as `credentialStore` above. `0` skips the refresh
592
+ * outright, which is how a test reaches the answer-from-the-old-graph path
593
+ * deterministically: a near-zero timeout does not, because the budget only
594
+ * stops sources that check the abort signal (see `ensureFreshGraph`).
595
+ */
596
+ autoRefreshBudgetMs = undefined;
597
+ constructor(repoPath, config, openGraph) {
598
+ this.repoPath = repoPath;
599
+ this.config = config;
600
+ this.#openGraph = openGraph ?? ((path) => openStore(path));
601
+ }
602
+ static async open(options) {
603
+ const repoPath = resolve(options.repoPath);
604
+ const config = options.config ?? (await loadConfig(repoPath));
605
+ return new Session(repoPath, config, options.openGraph);
606
+ }
607
+ /**
608
+ * Re-reads `.descry/config.json` from disk and replaces `config` in place —
609
+ * the explicit, caller-initiated reload UAT phase 5 (§0 gap 2) asked for.
610
+ *
611
+ * Deliberately not a file watcher: a watcher would re-run every trust check
612
+ * (`module-trust.ts`) on a specifier the instant a file the *scanned
613
+ * repository* controls changes, with no human in the loop — exactly the
614
+ * hazard those checks exist to gate. This method exists so a caller (a
615
+ * tool's own `reload` argument) can ask for the same effect on purpose,
616
+ * with an addressee for the trust decision that follows.
617
+ *
618
+ * Returns which top-level fields actually changed, using the identical
619
+ * comparison `checkConfigDrift`'s disclosure already makes, so "reload
620
+ * happened" and "reload changed something" stay distinguishable — a
621
+ * reload that finds nothing different is not a no-op that failed silently.
622
+ */
623
+ async reloadConfig() {
624
+ const fresh = await loadConfig(this.repoPath);
625
+ const changed = driftedKeys(fresh, this.config);
626
+ this.config = fresh;
627
+ return { changed };
628
+ }
629
+ get graphPath() {
630
+ return isAbsolute(this.config.graph)
631
+ ? this.config.graph
632
+ : join(this.repoPath, this.config.graph);
633
+ }
634
+ /** Open on first use; reuse thereafter. */
635
+ store() {
636
+ if (this.#store === null)
637
+ this.#store = this.#openGraph(this.graphPath);
638
+ return this.#store;
639
+ }
640
+ /** `analyze`'s own repair path — the only caller entitled to one (others refuse a
641
+ * version mismatch per DEC-017). Opens a short-lived driver directly, bypassing an
642
+ * injected test store; no-op there since an in-memory fixture is never stale. */
643
+ repairStaleGraph() {
644
+ if (this.#store !== null)
645
+ return { rebuilt: false };
646
+ const driver = new NodeSqliteDriver();
647
+ try {
648
+ driver.open(this.graphPath);
649
+ return rebuildStaleGraph(driver);
650
+ }
651
+ catch {
652
+ // Anything from opening (missing dir, corrupt file) is not this method's job —
653
+ // `store()` right after hits the same path via its own disclosed error handling.
654
+ return { rebuilt: false };
655
+ }
656
+ finally {
657
+ driver.close();
658
+ }
659
+ }
660
+ provider() {
661
+ if (this.#provider === null)
662
+ this.#provider = createStoreProvider(this.store().driver);
663
+ return this.#provider;
664
+ }
665
+ /** Repo root for an analysis run. Commit sha comes from git and is never defaulted
666
+ * when git is absent — every node/edge is stamped with it, and a placeholder would
667
+ * make the staleness stamp a lie nothing downstream could detect. */
668
+ async root() {
669
+ if (!(await isGitRepository(this.repoPath))) {
670
+ throw new ConfigError(`${this.repoPath} is not a git repository. Every node is stamped with the commit it was ` +
671
+ "read at, so there is nothing honest to record for a directory with no history.");
672
+ }
673
+ return {
674
+ repo: this.config.repo,
675
+ ...(this.config.workspace === undefined ? {} : { workspace: this.config.workspace }),
676
+ absolutePath: this.repoPath,
677
+ commitSha: await gitHead(this.repoPath),
678
+ };
679
+ }
680
+ /** Live browser sessions for this connection (`mcp-browser-tools.md` §4). Created
681
+ * lazily, same reason as the graph — most connections never touch a browser tool.
682
+ * Lives here, not a module-level map, so "per connection, never global" actually holds. */
683
+ browserSessions() {
684
+ if (this.#browserSessions === null)
685
+ this.#browserSessions = createBrowserSessionRegistry();
686
+ return this.#browserSessions;
687
+ }
688
+ /** The browser this connection drives, or a named reason there is none. Unavailable
689
+ * by default — `@descryy/runtime-browser` isn't installed yet — reported as a refusal
690
+ * with a remedy, never a silently-empty page (rule 7). */
691
+ browserBackend() {
692
+ if (this.#browserDriverFactory === null)
693
+ return BROWSER_BACKEND_NOT_INSTALLED;
694
+ return { available: true, createDriver: this.#browserDriverFactory };
695
+ }
696
+ /** Installs the driver this connection's browser tools use. A method, not a
697
+ * constructor arg — most sessions never touch a browser and shouldn't have to
698
+ * name one to be constructed. */
699
+ useBrowserDriver(driver) {
700
+ // One-driver form — every test that doesn't care about stack parsing wants this
701
+ // and shouldn't have to write a factory.
702
+ this.#browserDriverFactory = () => Promise.resolve(driver);
703
+ }
704
+ /** Installs a factory that builds a driver per session. A factory, not a driver,
705
+ * because the stack-trace parser (`browser_start_session`'s `stackParser`) is
706
+ * language-specific and named per call. */
707
+ useBrowserDriverFactory(factory) {
708
+ this.#browserDriverFactory = factory;
709
+ }
710
+ /** Record credential values a browser session was seeded with, so anything this server writes can refuse to carry them. */
711
+ noteBrowserSecrets(values) {
712
+ for (const value of values)
713
+ if (value.length > 0)
714
+ this.#browserSecrets.add(value);
715
+ }
716
+ /** The credential values this connection knows about. Compared against, never rendered. */
717
+ browserSecrets() {
718
+ return [...this.#browserSecrets];
719
+ }
720
+ /** Synchronous teardown, no I/O to wait on. A browser session can't close
721
+ * synchronously, so that reap lives in `dispose()` — this stays the method
722
+ * every existing no-browser caller keeps using unchanged. */
723
+ close() {
724
+ this.#browserSessions?.stop();
725
+ this.#store?.close();
726
+ this.#store = null;
727
+ this.#provider = null;
728
+ }
729
+ /** Full teardown, including any live browser — what a client disconnect must
730
+ * reach (§4). A leaked Chromium costs the developer's machine, not just a file
731
+ * descriptor. Awaits the reap first so the evidence flush has a live registry. */
732
+ async dispose() {
733
+ if (this.#browserSessions !== null)
734
+ await this.#browserSessions.reapAll("clientDisconnect");
735
+ this.close();
736
+ }
737
+ /** Mints the next call id without recording anything yet. `server.ts` calls this
738
+ * before render so the reply can carry `callId`, then records under the same id
739
+ * with `recordCallWithId` — one id used for both. */
740
+ allocateCallId() {
741
+ return `call-${this.#nextCallId++}`;
742
+ }
743
+ /** `verify_claim`'s fact boundary — called once per call with the exact
744
+ * `structuredContent` the host received. IDs are scoped to this process only, so a
745
+ * citation from a different run can't resolve (§2's G1 negative control). */
746
+ recordCall(tool, args, result) {
747
+ return this.recordCallWithId(this.allocateCallId(), tool, args, result);
748
+ }
749
+ /** `recordCall` under an id minted earlier by `allocateCallId`, not one minted here
750
+ * — so `recordedCall(id)` can find the exact id a caller read off the reply's
751
+ * `callId`, which didn't exist until after the envelope was built. */
752
+ recordCallWithId(id, tool, args, result) {
753
+ const call = { id, tool, args, result, at: Date.now() };
754
+ this.#calls.push(call);
755
+ return call;
756
+ }
757
+ /** All calls recorded this session, oldest first. */
758
+ recordedCalls() {
759
+ return this.#calls;
760
+ }
761
+ recordedCall(id) {
762
+ return this.#calls.find((c) => c.id === id);
763
+ }
764
+ }
765
+ /**
766
+ * What this repository's last successful `observe_runtime` run saved, if any — the
767
+ * accessor a caller outside `observe-runtime.ts` should use rather than reaching into
768
+ * `session.config` directly, so the field it reads stays this file's own concern.
769
+ *
770
+ * Exported for the browser tools (lane "preflight-browser" / lane 5): a saved
771
+ * service's `readiness` can carry an `http` path and, if declared, a fixed `port` —
772
+ * enough to recover a URL to navigate to without requiring a fresh `observe_runtime`
773
+ * call first. Not wired into any browser tool from here — that composition belongs
774
+ * to the tools that own it.
775
+ */
776
+ export function loadSavedStart(session) {
777
+ return session.config.savedStart;
778
+ }
779
+ //# sourceMappingURL=session.js.map