better-dsh 0.2.3-e → 0.2.3-g

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 (270) hide show
  1. package/docs/50_test-reports/2026-09-13-preact-ui-shell/345/256/236/346/265/213/346/212/245/345/221/212.md +1 -1
  2. package/docs/50_test-reports/2026-09-14-4999-skill/346/270/205/345/215/225/344/270/216lsp-gate/345/256/236/346/265/213/346/212/245/345/221/212.md +54 -0
  3. package/docs/specs/agent/spec.md +54 -0
  4. package/docs/specs/ast/spec.md +34 -0
  5. package/docs/specs/compaction-recall/spec.md +46 -0
  6. package/docs/specs/ctx/spec.md +107 -0
  7. package/docs/specs/dsh/spec.md +47 -0
  8. package/docs/specs/dvc/spec.md +87 -0
  9. package/docs/specs/escalation-guidance/spec.md +44 -0
  10. package/docs/specs/fs-scheme-resolution/spec.md +37 -0
  11. package/docs/specs/hash-edit/spec.md +41 -0
  12. package/docs/specs/http-read/spec.md +73 -0
  13. package/docs/specs/kernel-provisioning/spec.md +53 -0
  14. package/docs/specs/lsp/spec.md +121 -0
  15. package/docs/specs/mobile-layout/spec.md +108 -0
  16. package/docs/specs/model-failover/spec.md +20 -0
  17. package/docs/specs/preact-ui-shell/spec.md +22 -0
  18. package/docs/specs/repl-dispatch-resilience/spec.md +21 -0
  19. package/docs/specs/skill/spec.md +58 -0
  20. package/docs/specs/tool-surface/spec.md +222 -0
  21. package/docs/specs/url-schema/spec.md +148 -0
  22. package/docs/specs/web-trust-fence/spec.md +43 -0
  23. package/dsh-docs/AGENTS.md +75 -0
  24. package/dsh-docs/agent-lifecycle.md +84 -0
  25. package/dsh-docs/agent-lifecycle.zh.md +86 -0
  26. package/dsh-docs/api-gateway.md +164 -0
  27. package/dsh-docs/api-gateway.zh.md +164 -0
  28. package/dsh-docs/architecture.md +150 -0
  29. package/dsh-docs/architecture.zh.md +154 -0
  30. package/dsh-docs/capability-seams.md +543 -0
  31. package/dsh-docs/capability-seams.zh.md +545 -0
  32. package/dsh-docs/config-catalog.md +3473 -0
  33. package/dsh-docs/config-catalog.zh.md +3474 -0
  34. package/dsh-docs/cookbook/adding-a-package.md +117 -0
  35. package/dsh-docs/cookbook/adding-a-package.zh.md +119 -0
  36. package/dsh-docs/cookbook/adding-a-remote-api.md +197 -0
  37. package/dsh-docs/cookbook/adding-a-remote-api.zh.md +197 -0
  38. package/dsh-docs/cookbook/adding-a-settings-card.md +102 -0
  39. package/dsh-docs/cookbook/adding-a-settings-card.zh.md +102 -0
  40. package/dsh-docs/cookbook/adding-a-tool.md +101 -0
  41. package/dsh-docs/cookbook/adding-a-tool.zh.md +103 -0
  42. package/dsh-docs/cookbook/adding-a-vendored-package.md +59 -0
  43. package/dsh-docs/cookbook/adding-a-vendored-package.zh.md +59 -0
  44. package/dsh-docs/cookbook/adding-an-llm-adapter.md +43 -0
  45. package/dsh-docs/cookbook/adding-an-llm-adapter.zh.md +43 -0
  46. package/dsh-docs/cookbook/extension-cookbook.md +132 -0
  47. package/dsh-docs/cookbook/extension-cookbook.zh.md +136 -0
  48. package/dsh-docs/cookbook/maintaining-dsh-code-review.md +64 -0
  49. package/dsh-docs/cookbook/maintaining-dsh-code-review.zh.md +64 -0
  50. package/dsh-docs/cookbook/responding-to-pr-review-on-a-stack.md +32 -0
  51. package/dsh-docs/cookbook/responding-to-pr-review-on-a-stack.zh.md +32 -0
  52. package/dsh-docs/cordis-api/context.md +364 -0
  53. package/dsh-docs/cordis-api/context.zh.md +366 -0
  54. package/dsh-docs/cordis-api/events.md +207 -0
  55. package/dsh-docs/cordis-api/events.zh.md +209 -0
  56. package/dsh-docs/cordis-api/fiber.md +375 -0
  57. package/dsh-docs/cordis-api/fiber.zh.md +377 -0
  58. package/dsh-docs/cordis-api/inherited.md +39 -0
  59. package/dsh-docs/cordis-api/registry.md +152 -0
  60. package/dsh-docs/cordis-api/registry.zh.md +154 -0
  61. package/dsh-docs/cordis-api/service.md +102 -0
  62. package/dsh-docs/cordis-api/service.zh.md +104 -0
  63. package/dsh-docs/cordis-primer.md +45 -0
  64. package/dsh-docs/cordis-primer.zh.md +51 -0
  65. package/dsh-docs/cordis-tutorial/01-first-plugin.md +95 -0
  66. package/dsh-docs/cordis-tutorial/01-first-plugin.zh.md +95 -0
  67. package/dsh-docs/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
  68. package/dsh-docs/cordis-tutorial/02-lifecycle-and-effects.zh.md +98 -0
  69. package/dsh-docs/cordis-tutorial/03-services.md +98 -0
  70. package/dsh-docs/cordis-tutorial/03-services.zh.md +98 -0
  71. package/dsh-docs/cordis-tutorial/04-events.md +144 -0
  72. package/dsh-docs/cordis-tutorial/04-events.zh.md +144 -0
  73. package/dsh-docs/cordis-tutorial/05-config.md +84 -0
  74. package/dsh-docs/cordis-tutorial/05-config.zh.md +84 -0
  75. package/dsh-docs/cordis-tutorial/06-composition-and-hmr.md +113 -0
  76. package/dsh-docs/cordis-tutorial/06-composition-and-hmr.zh.md +113 -0
  77. package/dsh-docs/cordis-tutorial/07-into-the-harness.md +108 -0
  78. package/dsh-docs/cordis-tutorial/07-into-the-harness.zh.md +108 -0
  79. package/dsh-docs/cordis-tutorial/index.md +60 -0
  80. package/dsh-docs/cordis-tutorial/index.zh.md +62 -0
  81. package/dsh-docs/deepseek-llm-api-wire-extensions.md +163 -0
  82. package/dsh-docs/deepseek-llm-api-wire-extensions.zh.md +163 -0
  83. package/dsh-docs/defensive-patterns.md +33 -0
  84. package/dsh-docs/defensive-patterns.zh.md +35 -0
  85. package/dsh-docs/development.md +167 -0
  86. package/dsh-docs/development.zh.md +173 -0
  87. package/dsh-docs/event-producer-consumer.md +86 -0
  88. package/dsh-docs/event-producer-consumer.zh.md +88 -0
  89. package/dsh-docs/glossary.md +45 -0
  90. package/dsh-docs/glossary.zh.md +45 -0
  91. package/dsh-docs/graph-atlas.md +22 -0
  92. package/dsh-docs/graph-atlas.zh.md +24 -0
  93. package/dsh-docs/i18n/README.md +60 -0
  94. package/dsh-docs/i18n/README.zh.md +62 -0
  95. package/dsh-docs/i18n/style-samples.md +87 -0
  96. package/dsh-docs/i18n/terminology.md +214 -0
  97. package/dsh-docs/i18n/translation-prompt.md +263 -0
  98. package/dsh-docs/i18n/translation-rules.md +69 -0
  99. package/dsh-docs/i18n/translation-rules.zh.md +69 -0
  100. package/dsh-docs/module-graph.md +1411 -0
  101. package/dsh-docs/module-graph.zh.md +1413 -0
  102. package/dsh-docs/persistence-catalog.md +1075 -0
  103. package/dsh-docs/persistence-catalog.zh.md +1077 -0
  104. package/dsh-docs/postmortem/0001-acp-default-export-drops-inject.md +113 -0
  105. package/dsh-docs/postmortem/0001-acp-default-export-drops-inject.zh.md +113 -0
  106. package/dsh-docs/postmortem/0002-js-expression-disabled-filesystem-tools.md +47 -0
  107. package/dsh-docs/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +47 -0
  108. package/dsh-docs/postmortem/0003-web-agent-gui-feedback-loop.md +53 -0
  109. package/dsh-docs/postmortem/0003-web-agent-gui-feedback-loop.zh.md +53 -0
  110. package/dsh-docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +55 -0
  111. package/dsh-docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +55 -0
  112. package/dsh-docs/postmortem/README.md +18 -0
  113. package/dsh-docs/postmortem/README.zh.md +18 -0
  114. package/dsh-docs/rescope.md +53 -0
  115. package/dsh-docs/rescope.zh.md +53 -0
  116. package/dsh-docs/subsystems/README.md +61 -0
  117. package/dsh-docs/subsystems/README.zh.md +61 -0
  118. package/dsh-docs/subsystems/agent-team.md +207 -0
  119. package/dsh-docs/subsystems/agent-team.zh.md +207 -0
  120. package/dsh-docs/subsystems/approval.md +170 -0
  121. package/dsh-docs/subsystems/approval.zh.md +170 -0
  122. package/dsh-docs/subsystems/attachment.md +351 -0
  123. package/dsh-docs/subsystems/attachment.zh.md +351 -0
  124. package/dsh-docs/subsystems/client-modules.md +168 -0
  125. package/dsh-docs/subsystems/client-modules.zh.md +168 -0
  126. package/dsh-docs/subsystems/code-runtime.md +195 -0
  127. package/dsh-docs/subsystems/code-runtime.zh.md +195 -0
  128. package/dsh-docs/subsystems/commands.md +219 -0
  129. package/dsh-docs/subsystems/commands.zh.md +219 -0
  130. package/dsh-docs/subsystems/compaction.md +238 -0
  131. package/dsh-docs/subsystems/compaction.zh.md +238 -0
  132. package/dsh-docs/subsystems/conversation.md +258 -0
  133. package/dsh-docs/subsystems/conversation.zh.md +258 -0
  134. package/dsh-docs/subsystems/core.md +1209 -0
  135. package/dsh-docs/subsystems/core.zh.md +1219 -0
  136. package/dsh-docs/subsystems/credentials.md +329 -0
  137. package/dsh-docs/subsystems/credentials.zh.md +329 -0
  138. package/dsh-docs/subsystems/extensions.md +382 -0
  139. package/dsh-docs/subsystems/extensions.zh.md +382 -0
  140. package/dsh-docs/subsystems/feedback.md +266 -0
  141. package/dsh-docs/subsystems/feedback.zh.md +266 -0
  142. package/dsh-docs/subsystems/filesystem.md +505 -0
  143. package/dsh-docs/subsystems/filesystem.zh.md +505 -0
  144. package/dsh-docs/subsystems/goal.md +277 -0
  145. package/dsh-docs/subsystems/goal.zh.md +277 -0
  146. package/dsh-docs/subsystems/invariants.md +88 -0
  147. package/dsh-docs/subsystems/invariants.zh.md +88 -0
  148. package/dsh-docs/subsystems/jobs.md +290 -0
  149. package/dsh-docs/subsystems/jobs.zh.md +290 -0
  150. package/dsh-docs/subsystems/llm-streaming.md +1080 -0
  151. package/dsh-docs/subsystems/llm-streaming.zh.md +1086 -0
  152. package/dsh-docs/subsystems/lsp.md +202 -0
  153. package/dsh-docs/subsystems/lsp.zh.md +202 -0
  154. package/dsh-docs/subsystems/permission-presets.md +131 -0
  155. package/dsh-docs/subsystems/permission-presets.zh.md +131 -0
  156. package/dsh-docs/subsystems/persistence.md +395 -0
  157. package/dsh-docs/subsystems/persistence.zh.md +395 -0
  158. package/dsh-docs/subsystems/plan.md +87 -0
  159. package/dsh-docs/subsystems/plan.zh.md +87 -0
  160. package/dsh-docs/subsystems/sandbox.md +220 -0
  161. package/dsh-docs/subsystems/sandbox.zh.md +220 -0
  162. package/dsh-docs/subsystems/schedule.md +192 -0
  163. package/dsh-docs/subsystems/schedule.zh.md +192 -0
  164. package/dsh-docs/subsystems/scope.md +59 -0
  165. package/dsh-docs/subsystems/scope.zh.md +59 -0
  166. package/dsh-docs/subsystems/session-projection.md +354 -0
  167. package/dsh-docs/subsystems/session-projection.zh.md +354 -0
  168. package/dsh-docs/subsystems/session-query.md +509 -0
  169. package/dsh-docs/subsystems/session-query.zh.md +509 -0
  170. package/dsh-docs/subsystems/session-reference.md +219 -0
  171. package/dsh-docs/subsystems/session-reference.zh.md +219 -0
  172. package/dsh-docs/subsystems/session-telemetry.md +194 -0
  173. package/dsh-docs/subsystems/session-telemetry.zh.md +194 -0
  174. package/dsh-docs/subsystems/session-title.md +204 -0
  175. package/dsh-docs/subsystems/session-title.zh.md +204 -0
  176. package/dsh-docs/subsystems/session.md +1155 -0
  177. package/dsh-docs/subsystems/session.zh.md +1159 -0
  178. package/dsh-docs/subsystems/settings.md +405 -0
  179. package/dsh-docs/subsystems/settings.zh.md +405 -0
  180. package/dsh-docs/subsystems/shell.md +303 -0
  181. package/dsh-docs/subsystems/shell.zh.md +303 -0
  182. package/dsh-docs/subsystems/skills.md +354 -0
  183. package/dsh-docs/subsystems/skills.zh.md +354 -0
  184. package/dsh-docs/subsystems/slots.md +175 -0
  185. package/dsh-docs/subsystems/slots.zh.md +175 -0
  186. package/dsh-docs/subsystems/spill.md +117 -0
  187. package/dsh-docs/subsystems/spill.zh.md +117 -0
  188. package/dsh-docs/subsystems/storage.md +260 -0
  189. package/dsh-docs/subsystems/storage.zh.md +260 -0
  190. package/dsh-docs/subsystems/subagent.md +766 -0
  191. package/dsh-docs/subsystems/subagent.zh.md +770 -0
  192. package/dsh-docs/subsystems/subprocess.md +324 -0
  193. package/dsh-docs/subsystems/subprocess.zh.md +324 -0
  194. package/dsh-docs/subsystems/system-prompt.md +220 -0
  195. package/dsh-docs/subsystems/system-prompt.zh.md +220 -0
  196. package/dsh-docs/subsystems/terminal.md +184 -0
  197. package/dsh-docs/subsystems/terminal.zh.md +184 -0
  198. package/dsh-docs/subsystems/todo.md +32 -0
  199. package/dsh-docs/subsystems/todo.zh.md +32 -0
  200. package/dsh-docs/subsystems/token-meter.md +105 -0
  201. package/dsh-docs/subsystems/token-meter.zh.md +105 -0
  202. package/dsh-docs/subsystems/tools.md +720 -0
  203. package/dsh-docs/subsystems/tools.zh.md +720 -0
  204. package/dsh-docs/subsystems/typert.md +343 -0
  205. package/dsh-docs/subsystems/typert.zh.md +343 -0
  206. package/dsh-docs/subsystems/user-questions.md +178 -0
  207. package/dsh-docs/subsystems/user-questions.zh.md +178 -0
  208. package/dsh-docs/subsystems/web-client.md +95 -0
  209. package/dsh-docs/subsystems/web-client.zh.md +95 -0
  210. package/dsh-docs/subsystems/web-server.md +154 -0
  211. package/dsh-docs/subsystems/web-server.zh.md +154 -0
  212. package/dsh-docs/subsystems/web.md +206 -0
  213. package/dsh-docs/subsystems/web.zh.md +206 -0
  214. package/dsh-docs/subsystems/webhook.md +70 -0
  215. package/dsh-docs/subsystems/webhook.zh.md +70 -0
  216. package/dsh-docs/subsystems/workflow.md +278 -0
  217. package/dsh-docs/subsystems/workflow.zh.md +278 -0
  218. package/dsh-docs/subsystems/workspace.md +321 -0
  219. package/dsh-docs/subsystems/workspace.zh.md +321 -0
  220. package/dsh-docs/testing.md +54 -0
  221. package/dsh-docs/testing.zh.md +54 -0
  222. package/dsh-docs/tool-catalog.md +2225 -0
  223. package/dsh-docs/tool-catalog.zh.md +2233 -0
  224. package/dsh-docs/tool-execution-pipeline.md +62 -0
  225. package/dsh-docs/tool-execution-pipeline.zh.md +64 -0
  226. package/dsh-docs/user/develop/basic/config.md +106 -0
  227. package/dsh-docs/user/develop/basic/config.zh.md +106 -0
  228. package/dsh-docs/user/develop/basic/index.md +144 -0
  229. package/dsh-docs/user/develop/basic/index.zh.md +144 -0
  230. package/dsh-docs/user/develop/basic/publish.md +183 -0
  231. package/dsh-docs/user/develop/basic/publish.zh.md +183 -0
  232. package/dsh-docs/user/develop/basic/tool.md +52 -0
  233. package/dsh-docs/user/develop/basic/tool.zh.md +52 -0
  234. package/dsh-docs/user/develop/framework/events.md +143 -0
  235. package/dsh-docs/user/develop/framework/events.zh.md +143 -0
  236. package/dsh-docs/user/develop/framework/index.md +137 -0
  237. package/dsh-docs/user/develop/framework/index.zh.md +137 -0
  238. package/dsh-docs/user/develop/framework/service.md +148 -0
  239. package/dsh-docs/user/develop/framework/service.zh.md +150 -0
  240. package/dsh-docs/user/develop/practice/dynamic-cordis.md +15 -0
  241. package/dsh-docs/user/develop/practice/dynamic-cordis.zh.md +15 -0
  242. package/dsh-docs/user/develop/practice/index.md +155 -0
  243. package/dsh-docs/user/develop/practice/index.zh.md +155 -0
  244. package/dsh-docs/user/develop/practice/llm-adapter.md +189 -0
  245. package/dsh-docs/user/develop/practice/llm-adapter.zh.md +189 -0
  246. package/dsh-docs/user/guide/github-review.md +102 -0
  247. package/dsh-docs/user/guide/github-review.zh.md +102 -0
  248. package/dsh-docs/user/guide/index.md +30 -0
  249. package/dsh-docs/user/guide/index.zh.md +30 -0
  250. package/dsh-docs/user/guide/mcp-memory.md +101 -0
  251. package/dsh-docs/user/guide/mcp-memory.zh.md +101 -0
  252. package/dsh-docs/user/guide/network-proxy.md +85 -0
  253. package/dsh-docs/user/guide/network-proxy.zh.md +85 -0
  254. package/dsh-docs/user/guide/providers.md +190 -0
  255. package/dsh-docs/user/guide/providers.zh.md +190 -0
  256. package/dsh-docs/user/guide/python-sdk.md +150 -0
  257. package/dsh-docs/user/guide/python-sdk.zh.md +150 -0
  258. package/dsh-docs/user/guide/schedule.md +21 -0
  259. package/dsh-docs/user/guide/schedule.zh.md +21 -0
  260. package/dsh-docs/user/index.md +11 -0
  261. package/dsh-docs/user/index.zh.md +11 -0
  262. package/dsh-docs/web-styling.md +29 -0
  263. package/dsh-docs/web-styling.zh.md +29 -0
  264. package/lib/client/index.js +268 -38
  265. package/lib/fs-aware/sandbox-plugin.js +1 -1
  266. package/lib/index.js +1112 -1261
  267. package/lib/lsp-server-registry-B8DNonhS.js +3 -0
  268. package/lib/lsp-server-registry-BexQagaK.js +943 -0
  269. package/lib/{wrap-DC8O3SYz.js → wrap-JFjcWwZf.js} +42 -16
  270. package/package.json +2 -1
package/lib/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { a as RESERVED_ERROR_MEMBERS, i as RESERVED_BINDING_GLOBALS, n as DUNDER_MEMBER, o as ReplRuntime, r as PORTABLE_RESERVED_WORDS, t as isFlatBindableName } from "./py-sdk-Chvy92MB.js";
2
2
  import { s as resolveKernelEnv } from "./kernel-env-hxaihi9C.js";
3
- import { a as createDshHandler, c as createDvcHandler, d as registerDvcDevice, f as UrlSchemesError, i as createHttpHandler, l as dispatchDvcWrite, m as SCHEME_NAMES, n as wrapFsWithSchemes, o as UrlResolver, p as parseUrl, r as HTTP_SCHEMES, s as resolveDocsDir, u as listDvcDevices } from "./wrap-DC8O3SYz.js";
3
+ import { a as createDshHandler, c as createDvcHandler, d as registerDvcDevice, f as UrlSchemesError, i as createHttpHandler, l as dispatchDvcWrite, m as SCHEME_NAMES, n as wrapFsWithSchemes, o as UrlResolver, p as parseUrl, r as HTTP_SCHEMES, s as resolveDocsDir, u as listDvcDevices } from "./wrap-JFjcWwZf.js";
4
+ import { a as resolveCommandPath, i as primaryServerForFile, n as installHintFor, o as serverByName, s as serversForFile, t as findWorkspaceRoot } from "./lsp-server-registry-BexQagaK.js";
4
5
  import { createRequire } from "node:module";
5
6
  import { Context } from "@deepseek-ai/cordis";
6
7
  import { createHmac, randomBytes, randomUUID } from "node:crypto";
7
- import { accessSync, constants, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
8
+ import { accessSync, constants, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
8
9
  import * as path from "node:path";
9
10
  import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
10
11
  import z from "@deepseek-ai/schemastery";
@@ -2266,7 +2267,7 @@ var DashrRuntime = class extends ReplRuntime {
2266
2267
  };
2267
2268
 
2268
2269
  //#endregion
2269
- //#region src/url-schemes/vendored/devices/ast/natives-loader.ts
2270
+ //#region src/devices/ast/natives-loader.ts
2270
2271
  /** Platform tags with a published leaf package (mirrors the upstream loader). */
2271
2272
  const SUPPORTED_PLATFORM_TAGS = [
2272
2273
  "linux-x64",
@@ -2379,7 +2380,7 @@ function loadPiNatives(options = {}) {
2379
2380
  }
2380
2381
 
2381
2382
  //#endregion
2382
- //#region src/url-schemes/vendored/devices/ast/ast-device.ts
2383
+ //#region src/devices/ast/ast-device.ts
2383
2384
  /** Per-run cap on distinct rewritten files (upstream `PI_MAX_AST_FILES` default). */
2384
2385
  const MAX_FILES = 1e3;
2385
2386
  /** Glob metacharacters that mark a path segment as a pattern (upstream `GLOB_PATH_CHARS`). */
@@ -2572,7 +2573,7 @@ const summaries = {
2572
2573
  };
2573
2574
 
2574
2575
  //#endregion
2575
- //#region src/url-schemes/vendored/devices/browser/browser-device.ts
2576
+ //#region src/devices/browser/browser-device.ts
2576
2577
  let puppeteerModule;
2577
2578
  /** Load puppeteer-core once; missing package → structured BROWSER_NO_PUPPETEER. */
2578
2579
  async function loadPuppeteer() {
@@ -2802,12 +2803,12 @@ function registerBrowserDevice(registry = registerDvcDevice) {
2802
2803
  }
2803
2804
 
2804
2805
  //#endregion
2805
- //#region src/url-schemes/vendored/devices/lsp/lsp-jsonrpc.ts
2806
+ //#region src/devices/lsp/lsp-jsonrpc.ts
2806
2807
  /**
2807
2808
  * Content-Length JSON-RPC framing for the vendored `dvc://lsp` device.
2808
2809
  *
2809
2810
  * Vendored from `upstream/oh-my-pi` (packages/coding-agent/src/jsonrpc/
2810
- * message-framing.ts, MIT — see ../LICENSE-OMP.md): near-verbatim port to
2811
+ * message-framing.ts, MIT — see ../NOTICE-OMP.md): near-verbatim port to
2811
2812
  * plain Node Buffer APIs; upstream's private `#` fields kept. This is the
2812
2813
  * whole vendored surface of that module — no trims were needed.
2813
2814
  */
@@ -2930,7 +2931,7 @@ var MessageFramer = class {
2930
2931
  };
2931
2932
 
2932
2933
  //#endregion
2933
- //#region src/url-schemes/vendored/devices/lsp/lsp-language.ts
2934
+ //#region src/devices/lsp/lsp-language.ts
2934
2935
  /** Extension (without dot) → LSP language id (defaults.json coverage). */
2935
2936
  const EXTENSION_LANGUAGE_ID = {
2936
2937
  rs: "rust",
@@ -3033,7 +3034,7 @@ function detectLanguageId(filePath) {
3033
3034
  }
3034
3035
 
3035
3036
  //#endregion
3036
- //#region src/url-schemes/vendored/devices/lsp/lsp-client.ts
3037
+ //#region src/devices/lsp/lsp-client.ts
3037
3038
  /** Default per-request timeout when no explicit budget is given (upstream 30s). */
3038
3039
  const DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
3039
3040
  /** Max time to wait for the `initialize` response (upstream WARMUP/init budget). */
@@ -3553,6 +3554,10 @@ async function shutdownClientInstance(client) {
3553
3554
  killClient(client);
3554
3555
  return waitForExit(client, EXIT_TIMEOUT_MS);
3555
3556
  }
3557
+ /** Snapshot of every live client (stage 2/3: workspace aggregation + fan-out). */
3558
+ function listLiveClients() {
3559
+ return [...clients.values()];
3560
+ }
3556
3561
  /** Shut down every live client (upstream shutdownAll). */
3557
3562
  async function shutdownAllLspClients() {
3558
3563
  const toShutdown = [...clients.values()];
@@ -3564,886 +3569,127 @@ if (typeof process !== "undefined") process.on("beforeExit", () => {
3564
3569
  });
3565
3570
 
3566
3571
  //#endregion
3567
- //#region src/url-schemes/vendored/devices/lsp/defaults.json
3568
- var defaults_default = {
3569
- "rust-analyzer": {
3570
- "command": "rust-analyzer",
3571
- "args": [],
3572
- "fileTypes": [".rs"],
3573
- "rootMarkers": ["Cargo.toml", "rust-analyzer.toml"],
3574
- "initOptions": {},
3575
- "settings": { "rust-analyzer": { "checkOnSave": false } },
3576
- "capabilities": {
3577
- "flycheck": true,
3578
- "ssr": true,
3579
- "expandMacro": true,
3580
- "runnables": true,
3581
- "relatedTests": true
3582
- }
3583
- },
3584
- tlaplus: {
3585
- "command": "tlapm_lsp",
3586
- "args": ["--stdio"],
3587
- "fileTypes": [".tla", ".tlaplus"],
3588
- "rootMarkers": ["*.tla"]
3589
- },
3590
- clangd: {
3591
- "command": "clangd",
3592
- "args": [
3593
- "--background-index",
3594
- "--clang-tidy",
3595
- "--header-insertion=iwyu"
3596
- ],
3597
- "fileTypes": [
3598
- ".c",
3599
- ".cpp",
3600
- ".cc",
3601
- ".cxx",
3602
- ".h",
3603
- ".hpp",
3604
- ".hxx",
3605
- ".m",
3606
- ".mm"
3607
- ],
3608
- "rootMarkers": [
3609
- "compile_commands.json",
3610
- "CMakeLists.txt",
3611
- ".clangd",
3612
- ".clang-format",
3613
- "Makefile"
3614
- ]
3615
- },
3616
- zls: {
3617
- "command": "zls",
3618
- "args": [],
3619
- "fileTypes": [".zig"],
3620
- "rootMarkers": [
3621
- "build.zig",
3622
- "build.zig.zon",
3623
- "zls.json"
3624
- ]
3625
- },
3626
- gopls: {
3627
- "command": "gopls",
3628
- "args": ["serve"],
3629
- "fileTypes": [
3630
- ".go",
3631
- ".mod",
3632
- ".sum"
3633
- ],
3634
- "rootMarkers": [
3635
- "go.mod",
3636
- "go.work",
3637
- "go.sum"
3638
- ],
3639
- "settings": { "gopls": {
3640
- "analyses": {
3641
- "unusedparams": true,
3642
- "shadow": true
3643
- },
3644
- "staticcheck": true,
3645
- "gofumpt": true
3646
- } }
3647
- },
3648
- "typescript-language-server": {
3649
- "command": "typescript-language-server",
3650
- "args": ["--stdio"],
3651
- "fileTypes": [
3652
- ".ts",
3653
- ".tsx",
3654
- ".js",
3655
- ".jsx",
3656
- ".mjs",
3657
- ".cjs"
3658
- ],
3659
- "rootMarkers": [
3660
- "package.json",
3661
- "tsconfig.json",
3662
- "jsconfig.json"
3663
- ],
3664
- "initOptions": {
3665
- "hostInfo": "omp-coding-agent",
3666
- "preferences": {
3667
- "includeInlayParameterNameHints": "all",
3668
- "includeInlayVariableTypeHints": true,
3669
- "includeInlayFunctionParameterTypeHints": true
3670
- }
3671
- }
3672
- },
3673
- biome: {
3674
- "command": "biome",
3675
- "args": ["lsp-proxy"],
3676
- "fileTypes": [
3677
- ".ts",
3678
- ".tsx",
3679
- ".js",
3680
- ".jsx",
3681
- ".mjs",
3682
- ".cjs",
3683
- ".json",
3684
- ".jsonc",
3685
- ".css"
3686
- ],
3687
- "rootMarkers": ["biome.json", "biome.jsonc"],
3688
- "isLinter": true
3689
- },
3690
- eslint: {
3691
- "command": "vscode-eslint-language-server",
3692
- "args": ["--stdio"],
3693
- "fileTypes": [
3694
- ".ts",
3695
- ".tsx",
3696
- ".js",
3697
- ".jsx",
3698
- ".mjs",
3699
- ".cjs",
3700
- ".vue",
3701
- ".svelte"
3702
- ],
3703
- "rootMarkers": [
3704
- ".eslintrc",
3705
- ".eslintrc.js",
3706
- ".eslintrc.json",
3707
- ".eslintrc.yml",
3708
- "eslint.config.js",
3709
- "eslint.config.mjs"
3710
- ],
3711
- "isLinter": true,
3712
- "settings": {
3713
- "validate": "on",
3714
- "run": "onType"
3715
- }
3716
- },
3717
- denols: {
3718
- "command": "deno",
3719
- "args": ["lsp"],
3720
- "fileTypes": [
3721
- ".ts",
3722
- ".tsx",
3723
- ".js",
3724
- ".jsx"
3725
- ],
3726
- "rootMarkers": [
3727
- "deno.json",
3728
- "deno.jsonc",
3729
- "deno.lock"
3730
- ],
3731
- "initOptions": {
3732
- "enable": true,
3733
- "lint": true,
3734
- "unstable": true
3735
- }
3736
- },
3737
- "vscode-html-language-server": {
3738
- "command": "vscode-html-language-server",
3739
- "args": ["--stdio"],
3740
- "fileTypes": [".html", ".htm"],
3741
- "rootMarkers": ["package.json", ".git"],
3742
- "initOptions": { "provideFormatter": true }
3743
- },
3744
- "vscode-css-language-server": {
3745
- "command": "vscode-css-language-server",
3746
- "args": ["--stdio"],
3747
- "fileTypes": [
3748
- ".css",
3749
- ".scss",
3750
- ".sass",
3751
- ".less"
3752
- ],
3753
- "rootMarkers": ["package.json", ".git"],
3754
- "initOptions": { "provideFormatter": true }
3755
- },
3756
- "vscode-json-language-server": {
3757
- "command": "vscode-json-language-server",
3758
- "args": ["--stdio"],
3759
- "fileTypes": [".json", ".jsonc"],
3760
- "rootMarkers": ["package.json", ".git"],
3761
- "initOptions": { "provideFormatter": true }
3762
- },
3763
- tailwindcss: {
3764
- "command": "tailwindcss-language-server",
3765
- "args": ["--stdio"],
3766
- "fileTypes": [
3767
- ".html",
3768
- ".css",
3769
- ".scss",
3770
- ".js",
3771
- ".jsx",
3772
- ".ts",
3773
- ".tsx",
3774
- ".vue",
3775
- ".svelte"
3776
- ],
3777
- "rootMarkers": [
3778
- "tailwind.config.js",
3779
- "tailwind.config.ts",
3780
- "tailwind.config.mjs",
3781
- "tailwind.config.cjs"
3782
- ]
3783
- },
3784
- svelte: {
3785
- "command": "svelteserver",
3786
- "args": ["--stdio"],
3787
- "fileTypes": [".svelte"],
3788
- "rootMarkers": [
3789
- "svelte.config.js",
3790
- "svelte.config.mjs",
3791
- "package.json"
3792
- ]
3793
- },
3794
- "vue-language-server": {
3795
- "command": "vue-language-server",
3796
- "args": ["--stdio"],
3797
- "fileTypes": [".vue"],
3798
- "rootMarkers": [
3799
- "vue.config.js",
3800
- "nuxt.config.js",
3801
- "nuxt.config.ts",
3802
- "package.json"
3803
- ]
3804
- },
3805
- astro: {
3806
- "command": "astro-ls",
3807
- "args": ["--stdio"],
3808
- "fileTypes": [".astro"],
3809
- "rootMarkers": [
3810
- "astro.config.mjs",
3811
- "astro.config.js",
3812
- "astro.config.ts"
3813
- ]
3814
- },
3815
- pyright: {
3816
- "command": "pyright-langserver",
3817
- "args": ["--stdio"],
3818
- "fileTypes": [".py", ".pyi"],
3819
- "rootMarkers": [
3820
- "pyproject.toml",
3821
- "pyrightconfig.json",
3822
- "setup.py",
3823
- "setup.cfg",
3824
- "requirements.txt",
3825
- "Pipfile"
3826
- ],
3827
- "settings": { "python": { "analysis": {
3828
- "autoSearchPaths": true,
3829
- "diagnosticMode": "openFilesOnly",
3830
- "useLibraryCodeForTypes": true
3831
- } } }
3832
- },
3833
- basedpyright: {
3834
- "command": "basedpyright-langserver",
3835
- "args": ["--stdio"],
3836
- "fileTypes": [".py", ".pyi"],
3837
- "rootMarkers": [
3838
- "pyproject.toml",
3839
- "pyrightconfig.json",
3840
- "setup.py",
3841
- "requirements.txt"
3842
- ],
3843
- "settings": { "basedpyright": { "analysis": {
3844
- "autoSearchPaths": true,
3845
- "diagnosticMode": "openFilesOnly",
3846
- "useLibraryCodeForTypes": true
3847
- } } }
3848
- },
3849
- pylsp: {
3850
- "command": "pylsp",
3851
- "args": [],
3852
- "fileTypes": [".py"],
3853
- "rootMarkers": [
3854
- "pyproject.toml",
3855
- "setup.py",
3856
- "setup.cfg",
3857
- "requirements.txt",
3858
- "Pipfile"
3859
- ]
3860
- },
3861
- ty: {
3862
- "command": "ty",
3863
- "args": ["server"],
3864
- "fileTypes": [".py", ".pyi"],
3865
- "rootMarkers": [
3866
- "pyproject.toml",
3867
- "ty.toml",
3868
- "setup.py",
3869
- "setup.cfg",
3870
- "requirements.txt",
3871
- "Pipfile"
3872
- ]
3873
- },
3874
- ruff: {
3875
- "command": "ruff",
3876
- "args": ["server"],
3877
- "fileTypes": [".py", ".pyi"],
3878
- "rootMarkers": [
3879
- "pyproject.toml",
3880
- "ruff.toml",
3881
- ".ruff.toml"
3882
- ],
3883
- "isLinter": true
3884
- },
3885
- jdtls: {
3886
- "command": "jdtls",
3887
- "args": [],
3888
- "fileTypes": [".java"],
3889
- "rootMarkers": [
3890
- "pom.xml",
3891
- "build.gradle",
3892
- "build.gradle.kts",
3893
- "settings.gradle",
3894
- ".project"
3895
- ]
3896
- },
3897
- "kotlin-lsp": {
3898
- "command": "kotlin-lsp",
3899
- "args": ["--stdio"],
3900
- "fileTypes": [".kt", ".kts"],
3901
- "rootMarkers": [
3902
- "build.gradle",
3903
- "build.gradle.kts",
3904
- "pom.xml",
3905
- "settings.gradle",
3906
- "settings.gradle.kts"
3907
- ]
3908
- },
3909
- metals: {
3910
- "command": "metals",
3911
- "args": [],
3912
- "fileTypes": [
3913
- ".scala",
3914
- ".sbt",
3915
- ".sc"
3916
- ],
3917
- "rootMarkers": [
3918
- "build.sbt",
3919
- "build.sc",
3920
- "build.gradle",
3921
- "pom.xml"
3922
- ],
3923
- "initOptions": {
3924
- "statusBarProvider": "show-message",
3925
- "isHttpEnabled": true
3926
- }
3927
- },
3928
- hls: {
3929
- "command": "haskell-language-server-wrapper",
3930
- "args": ["--lsp"],
3931
- "fileTypes": [".hs", ".lhs"],
3932
- "rootMarkers": [
3933
- "stack.yaml",
3934
- "cabal.project",
3935
- "hie.yaml",
3936
- "package.yaml",
3937
- "*.cabal"
3938
- ],
3939
- "settings": { "haskell": {
3940
- "formattingProvider": "ormolu",
3941
- "checkProject": true
3942
- } }
3943
- },
3944
- ocamllsp: {
3945
- "command": "ocamllsp",
3946
- "args": [],
3947
- "fileTypes": [
3948
- ".ml",
3949
- ".mli",
3950
- ".mll",
3951
- ".mly"
3952
- ],
3953
- "rootMarkers": [
3954
- "dune-project",
3955
- "dune-workspace",
3956
- "*.opam",
3957
- ".ocamlformat"
3958
- ]
3959
- },
3960
- elixirls: {
3961
- "command": "elixir-ls",
3962
- "args": [],
3963
- "fileTypes": [
3964
- ".ex",
3965
- ".exs",
3966
- ".heex",
3967
- ".eex"
3968
- ],
3969
- "rootMarkers": ["mix.exs", "mix.lock"],
3970
- "settings": { "elixirLS": {
3971
- "dialyzerEnabled": true,
3972
- "fetchDeps": false
3973
- } }
3974
- },
3975
- expert: {
3976
- "command": "expert",
3977
- "args": ["--stdio"],
3978
- "fileTypes": [
3979
- ".ex",
3980
- ".exs",
3981
- ".heex",
3982
- ".eex"
3983
- ],
3984
- "rootMarkers": ["mix.exs", "mix.lock"]
3985
- },
3986
- erlangls: {
3987
- "command": "erlang_ls",
3988
- "args": [],
3989
- "fileTypes": [".erl", ".hrl"],
3990
- "rootMarkers": [
3991
- "rebar.config",
3992
- "erlang.mk",
3993
- "rebar.lock"
3994
- ]
3995
- },
3996
- gleam: {
3997
- "command": "gleam",
3998
- "args": ["lsp"],
3999
- "fileTypes": [".gleam"],
4000
- "rootMarkers": ["gleam.toml"]
4001
- },
4002
- solargraph: {
4003
- "command": "solargraph",
4004
- "args": ["stdio"],
4005
- "fileTypes": [
4006
- ".rb",
4007
- ".rake",
4008
- ".gemspec"
4009
- ],
4010
- "rootMarkers": [
4011
- "Gemfile",
4012
- ".solargraph.yml",
4013
- "Rakefile"
4014
- ],
4015
- "initOptions": { "formatting": true },
4016
- "settings": { "solargraph": {
4017
- "diagnostics": true,
4018
- "completion": true,
4019
- "hover": true,
4020
- "formatting": true,
4021
- "references": true,
4022
- "rename": true,
4023
- "symbols": true
4024
- } }
4025
- },
4026
- "ruby-lsp": {
4027
- "command": "ruby-lsp",
4028
- "args": [],
4029
- "fileTypes": [
4030
- ".rb",
4031
- ".rake",
4032
- ".gemspec",
4033
- ".erb"
4034
- ],
4035
- "rootMarkers": [
4036
- "Gemfile",
4037
- ".ruby-version",
4038
- ".ruby-gemset"
4039
- ],
4040
- "initOptions": { "formatter": "auto" }
4041
- },
4042
- rubocop: {
4043
- "command": "rubocop",
4044
- "args": ["--lsp"],
4045
- "fileTypes": [".rb", ".rake"],
4046
- "rootMarkers": [".rubocop.yml", "Gemfile"],
4047
- "isLinter": true
4048
- },
4049
- bashls: {
4050
- "command": "bash-language-server",
4051
- "args": ["start"],
4052
- "fileTypes": [
4053
- ".sh",
4054
- ".bash",
4055
- ".zsh"
4056
- ],
4057
- "rootMarkers": [".git"],
4058
- "settings": { "bashIde": { "globPattern": "*@(.sh|.inc|.bash|.command)" } }
4059
- },
4060
- "lua-language-server": {
4061
- "command": "lua-language-server",
4062
- "args": [],
4063
- "fileTypes": [".lua"],
4064
- "rootMarkers": [
4065
- ".luarc.json",
4066
- ".luarc.jsonc",
4067
- ".luacheckrc",
4068
- ".stylua.toml",
4069
- "stylua.toml"
4070
- ],
4071
- "settings": { "Lua": {
4072
- "runtime": { "version": "LuaJIT" },
4073
- "diagnostics": { "globals": ["vim"] },
4074
- "workspace": { "checkThirdParty": false },
4075
- "telemetry": { "enable": false }
4076
- } }
4077
- },
4078
- intelephense: {
4079
- "command": "intelephense",
4080
- "args": ["--stdio"],
4081
- "fileTypes": [".php", ".phtml"],
4082
- "rootMarkers": [
4083
- "composer.json",
4084
- "composer.lock",
4085
- ".git"
4086
- ]
4087
- },
4088
- phpactor: {
4089
- "command": "phpactor",
4090
- "args": ["language-server"],
4091
- "fileTypes": [".php"],
4092
- "rootMarkers": [
4093
- "composer.json",
4094
- ".phpactor.json",
4095
- ".phpactor.yml"
4096
- ]
4097
- },
4098
- omnisharp: {
4099
- "command": "omnisharp",
4100
- "args": [
4101
- "-z",
4102
- "--hostPID",
4103
- "$PID",
4104
- "--encoding",
4105
- "utf-8",
4106
- "--languageserver"
4107
- ],
4108
- "fileTypes": [".cs", ".csx"],
4109
- "rootMarkers": [
4110
- "*.sln",
4111
- "*.csproj",
4112
- "omnisharp.json",
4113
- ".git"
4114
- ],
4115
- "settings": {
4116
- "FormattingOptions": { "EnableEditorConfigSupport": true },
4117
- "RoslynExtensionsOptions": { "EnableAnalyzersSupport": true }
4118
- }
4119
- },
4120
- yamlls: {
4121
- "command": "yaml-language-server",
4122
- "args": ["--stdio"],
4123
- "fileTypes": [".yaml", ".yml"],
4124
- "rootMarkers": [".git"],
4125
- "settings": {
4126
- "yaml": {
4127
- "validate": true,
4128
- "format": { "enable": true },
4129
- "hover": true,
4130
- "completion": true
4131
- },
4132
- "redhat": { "telemetry": { "enabled": false } }
4133
- }
4134
- },
4135
- terraformls: {
4136
- "command": "terraform-ls",
4137
- "args": ["serve"],
4138
- "fileTypes": [".tf", ".tfvars"],
4139
- "rootMarkers": [
4140
- ".terraform",
4141
- "terraform.tfstate",
4142
- "*.tf"
4143
- ]
4144
- },
4145
- dockerls: {
4146
- "command": "docker-langserver",
4147
- "args": ["--stdio"],
4148
- "fileTypes": [".dockerfile", "Dockerfile"],
4149
- "rootMarkers": [
4150
- "Dockerfile",
4151
- "docker-compose.yml",
4152
- "docker-compose.yaml",
4153
- ".dockerignore"
4154
- ]
4155
- },
4156
- "helm-ls": {
4157
- "command": "helm_ls",
4158
- "args": ["serve"],
4159
- "fileTypes": [
4160
- ".yaml",
4161
- ".yml",
4162
- ".tpl"
4163
- ],
4164
- "rootMarkers": ["Chart.yaml", "Chart.yml"]
4165
- },
4166
- nixd: {
4167
- "command": "nixd",
4168
- "args": [],
4169
- "fileTypes": [".nix"],
4170
- "rootMarkers": [
4171
- "flake.nix",
4172
- "default.nix",
4173
- "shell.nix"
4174
- ]
4175
- },
4176
- nil: {
4177
- "command": "nil",
4178
- "args": [],
4179
- "fileTypes": [".nix"],
4180
- "rootMarkers": [
4181
- "flake.nix",
4182
- "default.nix",
4183
- "shell.nix"
4184
- ]
4185
- },
4186
- ols: {
4187
- "command": "ols",
4188
- "args": [],
4189
- "fileTypes": [".odin"],
4190
- "rootMarkers": ["ols.json", ".git"]
4191
- },
4192
- dartls: {
4193
- "command": "dart",
4194
- "args": ["language-server", "--protocol=lsp"],
4195
- "fileTypes": [".dart"],
4196
- "rootMarkers": ["pubspec.yaml", "pubspec.lock"],
4197
- "initOptions": {
4198
- "closingLabels": true,
4199
- "flutterOutline": true,
4200
- "outline": true
4201
- }
4202
- },
4203
- marksman: {
4204
- "command": "marksman",
4205
- "args": ["server"],
4206
- "fileTypes": [".md", ".markdown"],
4207
- "rootMarkers": [".marksman.toml", ".git"],
4208
- "warmupTimeoutMs": 2e3
4209
- },
4210
- texlab: {
4211
- "command": "texlab",
4212
- "args": [],
4213
- "fileTypes": [
4214
- ".tex",
4215
- ".bib",
4216
- ".sty",
4217
- ".cls"
4218
- ],
4219
- "rootMarkers": [
4220
- ".latexmkrc",
4221
- "latexmkrc",
4222
- ".texlabroot",
4223
- "texlabroot",
4224
- "Tectonic.toml"
4225
- ],
4226
- "settings": { "texlab": {
4227
- "build": {
4228
- "executable": "latexmk",
4229
- "args": [
4230
- "-pdf",
4231
- "-interaction=nonstopmode",
4232
- "-synctex=1",
4233
- "%f"
4234
- ]
4235
- },
4236
- "chktex": { "onOpenAndSave": true }
4237
- } }
4238
- },
4239
- graphql: {
4240
- "command": "graphql-lsp",
4241
- "args": [
4242
- "server",
4243
- "-m",
4244
- "stream"
4245
- ],
4246
- "fileTypes": [".graphql", ".gql"],
4247
- "rootMarkers": [
4248
- ".graphqlrc",
4249
- ".graphqlrc.json",
4250
- ".graphqlrc.yml",
4251
- ".graphqlrc.yaml",
4252
- "graphql.config.js"
4253
- ]
4254
- },
4255
- prismals: {
4256
- "command": "prisma-language-server",
4257
- "args": ["--stdio"],
4258
- "fileTypes": [".prisma"],
4259
- "rootMarkers": ["schema.prisma", "prisma/schema.prisma"]
4260
- },
4261
- vimls: {
4262
- "command": "vim-language-server",
4263
- "args": ["--stdio"],
4264
- "fileTypes": [".vim", ".vimrc"],
4265
- "rootMarkers": [".git"],
4266
- "initOptions": {
4267
- "isNeovim": true,
4268
- "diagnostic": { "enable": true }
4269
- }
4270
- },
4271
- "emmet-language-server": {
4272
- "command": "emmet-language-server",
4273
- "args": ["--stdio"],
4274
- "fileTypes": [
4275
- ".html",
4276
- ".css",
4277
- ".scss",
4278
- ".less",
4279
- ".jsx",
4280
- ".tsx",
4281
- ".vue",
4282
- ".svelte"
4283
- ],
4284
- "rootMarkers": [".git"]
4285
- },
4286
- "sourcekit-lsp": {
4287
- "command": "sourcekit-lsp",
4288
- "args": [],
4289
- "fileTypes": [".swift"],
4290
- "rootMarkers": [
4291
- "Package.swift",
4292
- "*.xcodeproj",
4293
- "*.xcworkspace",
4294
- "project.yml",
4295
- ".swiftpm"
4296
- ]
4297
- },
4298
- swiftlint: {
4299
- "command": "swiftlint",
4300
- "args": [
4301
- "lint",
4302
- "--quiet",
4303
- "--reporter",
4304
- "json"
4305
- ],
4306
- "fileTypes": [".swift"],
4307
- "rootMarkers": [
4308
- ".swiftlint.yml",
4309
- ".swiftlint.yaml",
4310
- "Package.swift",
4311
- "*.xcodeproj"
4312
- ],
4313
- "isLinter": true
3572
+ //#region src/devices/lsp/lsp-gate.ts
3573
+ /** Nag ceiling per session (spec: at most 10 availability notices). */
3574
+ const NAG_CAP = 10;
3575
+ /** Languages eligible for the availability notice. */
3576
+ const NAG_LANGUAGES = new Set([
3577
+ "python",
3578
+ "typescript",
3579
+ "javascript",
3580
+ "rust",
3581
+ "go"
3582
+ ]);
3583
+ /** Must-have languages installed proactively at first availability check. */
3584
+ const MUST_HAVE = new Set([
3585
+ "python",
3586
+ "typescript",
3587
+ "javascript"
3588
+ ]);
3589
+ /** Language id from a file extension (table-driven; unknown → undefined). */
3590
+ function languageOf(filePath) {
3591
+ const ext = path.extname(filePath).toLowerCase();
3592
+ if (ext === ".py") return "python";
3593
+ if (ext === ".ts" || ext === ".tsx" || ext === ".mts" || ext === ".cts") return "typescript";
3594
+ if (ext === ".js" || ext === ".jsx" || ext === ".mjs" || ext === ".cjs") return "javascript";
3595
+ if (ext === ".rs") return "rust";
3596
+ if (ext === ".go") return "go";
3597
+ }
3598
+ const gates = /* @__PURE__ */ new Map();
3599
+ function gateOf(sessionId) {
3600
+ let gate = gates.get(sessionId);
3601
+ if (gate === void 0) {
3602
+ gate = {
3603
+ state: "unasked",
3604
+ nags: 0,
3605
+ installTried: /* @__PURE__ */ new Set(),
3606
+ warmed: /* @__PURE__ */ new Set()
3607
+ };
3608
+ gates.set(sessionId, gate);
4314
3609
  }
4315
- };
4316
-
4317
- //#endregion
4318
- //#region src/url-schemes/vendored/devices/lsp/lsp-server-registry.ts
4319
- /** The verbatim upstream registry (54 servers), keyed by server name. */
4320
- const REGISTRY = defaults_default;
4321
- /** Server names in registry (insertion) order — deterministic selection. */
4322
- const REGISTRY_NAMES = Object.keys(REGISTRY);
4323
- /** Cache resolved-command probes: command → absolute path or null (missing). */
4324
- const commandProbeCache = /* @__PURE__ */ new Map();
4325
- /**
4326
- * Find servers whose `fileTypes` cover `filePath` (upstream getServersForFile,
4327
- * extension match plus the basename entries defaults.json uses for Dockerfiles).
4328
- */
4329
- function serversForFile(filePath) {
4330
- const lower = filePath.toLowerCase();
4331
- const baseName = path.posix.basename(lower.replaceAll("\\", "/"));
4332
- const extension = path.extname(lower);
4333
- const matches = [];
4334
- for (const name$2 of REGISTRY_NAMES) {
4335
- const config = REGISTRY[name$2];
4336
- if (config === void 0) continue;
4337
- if (config.fileTypes.some((fileType) => fileType === extension || fileType !== "" && !fileType.startsWith(".") && fileType === baseName)) matches.push([name$2, config]);
4338
- }
4339
- return matches;
3610
+ return gate;
4340
3611
  }
4341
- /**
4342
- * The primary server for a file: first non-linter match in registry order,
4343
- * falling back to the first linter (upstream getServerForFile preference —
4344
- * type intelligence over linting).
4345
- */
4346
- function primaryServerForFile(filePath) {
4347
- const matches = serversForFile(filePath);
4348
- return matches.find(([, config]) => config.isLinter !== true) ?? matches[0] ?? null;
4349
- }
4350
- /** Look up a named registry entry (device `server` arg). */
4351
- function serverByName(name$2) {
4352
- return REGISTRY[name$2];
4353
- }
4354
- /** One-level wildcard marker match: `*` crosses everything but `/` (upstream glob markers). */
4355
- function markerMatchesEntry(marker, entry) {
4356
- const star = marker.indexOf("*");
4357
- if (star === -1) return marker === entry;
4358
- const prefix = marker.slice(0, star);
4359
- const suffix = marker.slice(star + 1);
4360
- return entry.length >= prefix.length + suffix.length && entry.startsWith(prefix) && entry.endsWith(suffix) && !entry.slice(prefix.length, entry.length - suffix.length).includes("/");
4361
- }
4362
- /** Whether any root marker exists directly in `dir` (upstream hasRootMarkers). */
4363
- function hasRootMarkers(dir, markers) {
4364
- let entries = null;
4365
- for (const marker of markers) {
4366
- if (marker.includes("*")) {
4367
- if (entries === null) try {
4368
- entries = readdirSync(dir);
4369
- } catch {
4370
- return false;
4371
- }
4372
- if (entries.some((entry) => markerMatchesEntry(marker, entry))) return true;
4373
- continue;
4374
- }
4375
- if (existsSync(path.join(dir, marker))) return true;
4376
- }
4377
- return false;
3612
+ /** Drop a session's gate state (agent dispose). */
3613
+ function disposeLspGate(sessionId) {
3614
+ gates.delete(sessionId);
3615
+ }
3616
+ /** Current gate state for a session (read side: `dvc://lsp` status). */
3617
+ function lspGateState(sessionId) {
3618
+ return gateOf(sessionId).state;
4378
3619
  }
4379
3620
  /**
4380
- * Nearest ancestor of `filePath` holding a root marker, stopping at the
4381
- * filesystem root; `null` when no ancestor matches (upstream
4382
- * hasRootMarkerAncestor walk, returning the dir instead of a boolean).
3621
+ * Must-have install (stage 5): for python/typescript/javascript, probe the
3622
+ * registry's primary server binary once per session; when missing, run its
3623
+ * package-manager install in the background (fail-soft — a failed install
3624
+ * degrades to the device's structured LSP_SERVER_MISSING at spawn time, and
3625
+ * is never retried within the session). Niche languages install only after
3626
+ * an explicit gate-on (spec: on-demand).
4383
3627
  */
4384
- function findWorkspaceRoot(filePath, markers) {
4385
- if (markers.length === 0) return null;
4386
- let dir = path.dirname(path.resolve(filePath));
4387
- for (;;) {
4388
- if (hasRootMarkers(dir, markers)) return dir;
4389
- const parent = path.dirname(dir);
4390
- if (parent === dir) return null;
4391
- dir = parent;
4392
- }
4393
- }
4394
- function isExecutableFile(candidate) {
4395
- try {
4396
- accessSync(candidate, constants.X_OK);
4397
- return true;
4398
- } catch {
4399
- return false;
4400
- }
3628
+ function ensureMustHave(gate, language) {
3629
+ if (!MUST_HAVE.has(language) || gate.installTried.has(language)) return;
3630
+ gate.installTried.add(language);
3631
+ (async () => {
3632
+ const registry = await import("./lsp-server-registry-B8DNonhS.js");
3633
+ const probePath = `/tmp/x.${language === "python" ? "py" : "ts"}`;
3634
+ const primary = registry.primaryServerForFile(probePath);
3635
+ if (primary === null) return;
3636
+ const [name$2, config] = primary;
3637
+ if (registry.resolveCommandPath(config.command, "/tmp") !== null) return;
3638
+ if (!await registry.installServer(config.command)) {}
3639
+ })().catch(() => {});
4401
3640
  }
4402
3641
  /**
4403
- * Resolve `command` to an absolute executable: the project's
4404
- * `node_modules/.bin`, then every `$PATH` directory (upstream
4405
- * resolveCommandFromLocalRoot + PATH scan). Absolute overrides (device
4406
- * `command` args, fake-server fixtures) are probed directly. Result cached
4407
- * per command; returns `null` when the binary is missing.
3642
+ * Maybe produce the availability notice for a just-landed mutation.
3643
+ * Returns undefined when silent (gate decided, cap reached, non-table
3644
+ * language). Pure — no side effects beyond the nag counter.
4408
3645
  */
4409
- function resolveCommandPath(command, cwd) {
4410
- const cacheKey$1 = `${command}\0${cwd}`;
4411
- const cached = commandProbeCache.get(cacheKey$1);
4412
- if (cached !== void 0) return cached;
4413
- const resolved = (path.isAbsolute(command) ? [command] : [path.join(cwd, "node_modules", ".bin", command), ...(process.env.PATH ?? "").split(path.delimiter).filter((dir) => dir !== "").map((dir) => path.join(dir, command))]).find((candidate) => isExecutableFile(candidate)) ?? null;
4414
- commandProbeCache.set(cacheKey$1, resolved);
4415
- return resolved;
4416
- }
4417
- /** Per-command install hints for the common servers (device-side aid; upstream only names the command). */
4418
- const INSTALL_HINTS = {
4419
- "typescript-language-server": "npm install -g typescript-language-server typescript",
4420
- "vscode-eslint-language-server": "npm install -g vscode-languageserver-types eslint vscode-eslint-language-server",
4421
- "pyright-langserver": "npm install -g pyright",
4422
- pylsp: "pip install \"python-lsp-server[all]\"",
4423
- gopls: "go install golang.org/x/tools/gopls@latest",
4424
- "rust-analyzer": "rustup component add rust-analyzer",
4425
- clangd: "apt install clangd (or brew install llvm)",
4426
- biome: "npm install -g @biomejs/biome",
4427
- ruff: "pip install ruff",
4428
- "bash-language-server": "npm install -g bash-language-server",
4429
- "lua-language-server": "install from https://github.com/LuaLS/lua-language-server/releases",
4430
- marksman: "install from https://github.com/artempyanykh/marksman/releases"
4431
- };
4432
- /** Install hint for a missing server binary; generic fallback otherwise. */
4433
- function installHintFor(command) {
4434
- const hint = INSTALL_HINTS[command];
4435
- if (hint !== void 0) return hint;
4436
- return `install the "${command}" language server and make sure it is on $PATH`;
3646
+ function lspGateNotice(sessionId, filePath) {
3647
+ const gate = gateOf(sessionId);
3648
+ if (gate.state !== "unasked") return void 0;
3649
+ const language = languageOf(filePath);
3650
+ if (language === void 0 || !NAG_LANGUAGES.has(language)) return void 0;
3651
+ ensureMustHave(gate, language);
3652
+ if (gate.nags >= NAG_CAP) return void 0;
3653
+ gate.nags += 1;
3654
+ const remaining = NAG_CAP - gate.nags;
3655
+ return `lsp diagnostics available for this language — write dvc://lsp {"action":"on"} to enable, {"action":"off"} to mute${remaining > 0 ? ` (${remaining} reminders left)` : " (final reminder)"}`;
3656
+ }
3657
+ /** Apply a gate decision. Returns a human-readable ack. */
3658
+ function lspGateDecide(sessionId, on) {
3659
+ const gate = gateOf(sessionId);
3660
+ gate.state = on ? "on" : "off";
3661
+ return on ? "lsp on for this session: servers warm-start per language on first file contact; no repo artifacts are written." : "lsp off for this session: no servers will spawn and notices are muted.";
3662
+ }
3663
+ let transport;
3664
+ /** The device registers its sync/spawn entry point at mount time. */
3665
+ function registerLspGateTransport(fn) {
3666
+ transport = fn;
3667
+ }
3668
+ function lspGateSyncOnLand(sessionId, kind, filePath, content) {
3669
+ if (gateOf(sessionId).state !== "on" || transport === void 0) return;
3670
+ if (languageOf(filePath) === void 0) return;
3671
+ transport({
3672
+ sessionId,
3673
+ kind,
3674
+ filePath,
3675
+ content
3676
+ }).catch(() => {});
4437
3677
  }
4438
3678
 
4439
3679
  //#endregion
4440
- //#region src/url-schemes/vendored/devices/lsp/lsp-device.ts
3680
+ //#region src/devices/lsp/lsp-device.ts
4441
3681
  const ACTIONS = new Set([
4442
3682
  "diagnostics",
4443
3683
  "definition",
4444
3684
  "references",
4445
3685
  "hover",
4446
- "format"
3686
+ "format",
3687
+ "on",
3688
+ "off",
3689
+ "status",
3690
+ "rename",
3691
+ "code_actions",
3692
+ "reload"
4447
3693
  ]);
4448
3694
  const SEVERITY_NAMES = {
4449
3695
  1: "error",
@@ -4655,12 +3901,107 @@ function isEmptyPositionResult(result) {
4655
3901
  }
4656
3902
  return false;
4657
3903
  }
3904
+ /** Apply a WorkspaceEdit file entry through the in-memory applier; returns the changed path. */
3905
+ async function applyFileTextEdits(uri, edits) {
3906
+ const filePath = uriToFile(uri);
3907
+ const updated = applyTextEdits(await fsPromises.readFile(filePath, "utf-8"), edits);
3908
+ await fsPromises.writeFile(filePath, updated, "utf-8");
3909
+ return filePath;
3910
+ }
3911
+ /** Apply or preview a WorkspaceEdit; returns per-file summaries. */
3912
+ async function applyOrPreviewWorkspaceEdit(edit, apply$2) {
3913
+ const perFile = [];
3914
+ for (const [uri, edits] of Object.entries(edit.changes ?? {})) perFile.push({
3915
+ uri,
3916
+ edits
3917
+ });
3918
+ for (const doc of edit.documentChanges ?? []) perFile.push({
3919
+ uri: doc.textDocument.uri,
3920
+ edits: doc.edits
3921
+ });
3922
+ if (perFile.length === 0) return [];
3923
+ if (!apply$2) return perFile.map(({ uri, edits }) => ` ${uriToFile(uri)}: ${edits.length} edit(s) (preview — pass "apply":true to apply)`);
3924
+ const out = [];
3925
+ for (const { uri, edits } of perFile) out.push(` ${await applyFileTextEdits(uri, edits)} (${edits.length} edit(s))`);
3926
+ return out;
3927
+ }
3928
+ const BARE_IDENTIFIER_RE = /^[$A-Za-z_][\w$]*$/;
3929
+ const IDENTIFIER_CHAR_RE = /[A-Za-z0-9_$]/;
3930
+ /** `symbol` / `symbol#N` — N-th 1-based occurrence of the identifier on the line. */
3931
+ function parseSymbolSpec(spec) {
3932
+ const match = spec.match(/^(.+)#(\d+)$/);
3933
+ if (match === null) return {
3934
+ symbol: spec,
3935
+ occurrence: 1
3936
+ };
3937
+ return {
3938
+ symbol: match[1] ?? spec,
3939
+ occurrence: Math.max(1, Number.parseInt(match[2] ?? "1", 10))
3940
+ };
3941
+ }
3942
+ /** Match indexes for the symbol on the line: exact (word-bounded for bare identifiers), then case-insensitive. */
3943
+ function findSymbolMatchIndexes(lineText, symbol, caseInsensitive = false) {
3944
+ if (symbol.length === 0) return [];
3945
+ const haystack = caseInsensitive ? lineText.toLowerCase() : lineText;
3946
+ const needle = caseInsensitive ? symbol.toLowerCase() : symbol;
3947
+ const requireWordBoundary = BARE_IDENTIFIER_RE.test(symbol);
3948
+ const indexes = [];
3949
+ let fromIndex = 0;
3950
+ for (;;) {
3951
+ const matchIndex = haystack.indexOf(needle, fromIndex);
3952
+ if (matchIndex === -1) break;
3953
+ if (requireWordBoundary) {
3954
+ const before = matchIndex > 0 ? haystack[matchIndex - 1] ?? "" : "";
3955
+ const afterIdx = matchIndex + needle.length;
3956
+ const after = afterIdx < haystack.length ? haystack[afterIdx] ?? "" : "";
3957
+ if (IDENTIFIER_CHAR_RE.test(before) || IDENTIFIER_CHAR_RE.test(after)) {
3958
+ fromIndex = matchIndex + 1;
3959
+ continue;
3960
+ }
3961
+ }
3962
+ indexes.push(matchIndex);
3963
+ fromIndex = matchIndex + needle.length;
3964
+ }
3965
+ return indexes;
3966
+ }
3967
+ /**
3968
+ * Resolve the query column on `line` (1-based): no `symbol` → first
3969
+ * non-whitespace column; `symbol` → the occurrence's 0-based column,
3970
+ * exact case-sensitive first, case-insensitive fallback. Mirrors upstream
3971
+ * resolveSymbolColumn (OMP utils.ts).
3972
+ */
3973
+ function resolveSymbolColumn(lines, line, symbolSpec) {
3974
+ const targetLine = lines[line - 1] ?? "";
3975
+ if (symbolSpec === void 0 || symbolSpec === "") {
3976
+ const match = targetLine.match(/\S/);
3977
+ return match && match.index !== void 0 ? match.index : 0;
3978
+ }
3979
+ const { symbol, occurrence } = parseSymbolSpec(symbolSpec);
3980
+ const exact = findSymbolMatchIndexes(targetLine, symbol);
3981
+ const fallback = exact.length > 0 ? exact : findSymbolMatchIndexes(targetLine, symbol, true);
3982
+ if (fallback.length === 0) throw new UrlSchemesError("LSP_SYMBOL_NOT_FOUND", `lsp device: symbol "${symbol}" not found on line ${line}`);
3983
+ if (occurrence > fallback.length) throw new UrlSchemesError("LSP_SYMBOL_NOT_FOUND", `lsp device: symbol "${symbol}" occurrence ${occurrence} out of bounds on line ${line} (found ${fallback.length})`);
3984
+ return fallback[occurrence - 1] ?? 0;
3985
+ }
4658
3986
  /** The `dvc://lsp` device: dispatch on `action`, structured errors on every bad path. */
4659
3987
  const lspDevice = {
4660
- async execute(args) {
3988
+ async execute(args, ctx) {
4661
3989
  const record = requireObjectArgs(args);
4662
3990
  const action = record.action;
4663
- if (typeof action !== "string" || !ACTIONS.has(action)) throw new UrlSchemesError("LSP_BAD_ARGS", `lsp device: unknown action ${JSON.stringify(action ?? null)} — expected "diagnostics", "definition", "references", "hover", or "format"`);
3991
+ const sessionId = ctx?.session;
3992
+ if (typeof action !== "string" || !ACTIONS.has(action)) throw new UrlSchemesError("LSP_BAD_ARGS", `lsp device: unknown action ${JSON.stringify(action ?? null)} — expected "diagnostics", "definition", "references", "hover", "format", "rename", "code_actions", "reload", "on", "off", or "status"`);
3993
+ if (action === "on" || action === "off" || action === "status") {
3994
+ if (sessionId === void 0) throw new UrlSchemesError("LSP_BAD_ARGS", "lsp device: gate actions require a session context (the write dispatcher injects it automatically)");
3995
+ if (action === "status") return {
3996
+ ok: true,
3997
+ gate: lspGateState(sessionId)
3998
+ };
3999
+ return {
4000
+ ok: true,
4001
+ gate: action,
4002
+ message: lspGateDecide(sessionId, action === "on")
4003
+ };
4004
+ }
4664
4005
  const file = record.file;
4665
4006
  if (typeof file !== "string" || file === "") throw new UrlSchemesError("LSP_BAD_ARGS", "lsp device: this action requires a non-empty \"file\" string");
4666
4007
  const content = record.content;
@@ -4671,6 +4012,8 @@ const lspDevice = {
4671
4012
  const filePath = path.resolve(file);
4672
4013
  if (content === void 0 && !existsSync(filePath)) throw new UrlSchemesError("LSP_BAD_ARGS", `lsp device: file not found: ${filePath}`);
4673
4014
  const position = requirePosition(record);
4015
+ let symbolCharacter;
4016
+ if (typeof record.symbol === "string" && record.symbol !== "" && position.character === 1 && existsSync(filePath)) symbolCharacter = resolveSymbolColumn((await fsPromises.readFile(filePath, "utf-8")).split("\n"), position.line, record.symbol);
4674
4017
  const { name: name$2, client } = await obtainClient(record, filePath);
4675
4018
  const base = {
4676
4019
  ok: true,
@@ -4681,13 +4024,134 @@ const lspDevice = {
4681
4024
  const uri = fileToUri(filePath);
4682
4025
  const wirePosition = {
4683
4026
  line: position.line - 1,
4684
- character: position.character - 1
4027
+ character: symbolCharacter ?? position.character - 1
4685
4028
  };
4686
4029
  const diagnosticsBaseline = client.diagnosticsVersion;
4687
4030
  if (content === void 0) await ensureFileOpen(client, filePath);
4688
4031
  else await syncFileContent(client, filePath, content);
4689
4032
  if (action !== "diagnostics" && client.config.isLinter !== true) await waitForProjectLoaded(client);
4690
4033
  const retryCold = action !== "diagnostics" && client.config.isLinter !== true;
4034
+ if (action === "reload") {
4035
+ const { name: name$3, client: client$1 } = await obtainClient(record, file);
4036
+ await shutdownClientInstance(client$1);
4037
+ return `${name$3}: reloaded — the next lsp call respawns the server fresh (config re-read from disk).`;
4038
+ }
4039
+ if (action === "diagnostics" && record.all === true) {
4040
+ const candidates = serversForFile(file);
4041
+ if (candidates.length <= 1) {} else {
4042
+ const results = [];
4043
+ for (const [srvName] of candidates) try {
4044
+ const outcome = await thisDispatch({
4045
+ ...record,
4046
+ all: void 0,
4047
+ server: srvName
4048
+ });
4049
+ results.push({
4050
+ server: srvName,
4051
+ diagnostics: outcome.diagnostics ?? [],
4052
+ summary: outcome.summary ?? ""
4053
+ });
4054
+ } catch (error) {
4055
+ results.push({
4056
+ server: srvName,
4057
+ diagnostics: [],
4058
+ summary: error instanceof Error ? error.message : String(error)
4059
+ });
4060
+ }
4061
+ const total = results.reduce((n, r) => n + r.diagnostics.length, 0);
4062
+ return {
4063
+ ok: true,
4064
+ fanout: true,
4065
+ servers: results.map((r) => r.server),
4066
+ diagnostics: results.flatMap((r) => r.diagnostics),
4067
+ summary: `fan-out ${file}: ${total} diagnostic(s) across ${results.length} server(s) — ${results.map((r) => `${r.server}: ${r.diagnostics.length}`).join(", ")}`
4068
+ };
4069
+ }
4070
+ }
4071
+ if (action === "diagnostics" && file === "*") {
4072
+ const servers = listLiveClients().filter((c) => c.status === "ready");
4073
+ const files = [];
4074
+ for (const c of servers) for (const [uri$1, published] of c.diagnostics) {
4075
+ const diags = published.diagnostics ?? [];
4076
+ files.push({
4077
+ file: uriToFile(uri$1),
4078
+ server: c.config.command,
4079
+ count: diags.length,
4080
+ errors: diags.filter((d) => (d.severity ?? 1) === 1).length,
4081
+ warnings: diags.filter((d) => (d.severity ?? 1) === 2).length
4082
+ });
4083
+ }
4084
+ files.sort((a, b) => b.count - a.count);
4085
+ const totalErrors = files.reduce((n, f) => n + f.errors, 0);
4086
+ const totalWarnings = files.reduce((n, f) => n + f.warnings, 0);
4087
+ const lines = files.slice(0, 50).map((f) => ` ${f.file} [${f.server}]: ${f.count} (${f.errors}E/${f.warnings}W)`);
4088
+ return {
4089
+ ok: true,
4090
+ scope: "workspace",
4091
+ servers: servers.length,
4092
+ files: files.length,
4093
+ errors: totalErrors,
4094
+ warnings: totalWarnings,
4095
+ summary: `workspace diagnostics: ${files.length} file(s) with findings across ${servers.length} live server(s) — ${totalErrors} error(s), ${totalWarnings} warning(s)`,
4096
+ detail: lines.length > 0 ? lines.join("\n") : "no cached diagnostics"
4097
+ };
4098
+ }
4099
+ if (action === "rename") {
4100
+ const newName = record.new_name;
4101
+ if (typeof newName !== "string" || newName === "") throw new UrlSchemesError("LSP_BAD_ARGS", "lsp device: \"rename\" requires a non-empty \"new_name\" string");
4102
+ const { name: name$3, client: client$1 } = await obtainClient(record, file);
4103
+ const { line } = requirePosition(record);
4104
+ const character = resolveSymbolColumn((await fsPromises.readFile(file, "utf-8")).split("\n"), line, typeof record.symbol === "string" ? record.symbol : void 0);
4105
+ await ensureFileOpen(client$1, file);
4106
+ const result = await lspPositionRequest(client$1, "textDocument/rename", {
4107
+ textDocument: { uri: fileToUri(file) },
4108
+ position: {
4109
+ line: line - 1,
4110
+ character
4111
+ },
4112
+ newName
4113
+ }, false);
4114
+ if (result === null || result.changes === void 0 && result.documentChanges === void 0) return `${name$3}: rename returned no edits`;
4115
+ const apply$2 = record.apply !== false;
4116
+ const lines = await applyOrPreviewWorkspaceEdit(result, apply$2);
4117
+ return `${name$3}: rename ${apply$2 ? "applied" : "preview"}:\n${lines.join("\n")}`;
4118
+ }
4119
+ if (action === "code_actions") {
4120
+ const { name: name$3, client: client$1 } = await obtainClient(record, file);
4121
+ const { line, character } = requirePosition(record);
4122
+ await ensureFileOpen(client$1, file);
4123
+ const actions = await lspPositionRequest(client$1, "textDocument/codeAction", {
4124
+ textDocument: { uri: fileToUri(file) },
4125
+ range: {
4126
+ start: {
4127
+ line: line - 1,
4128
+ character: character - 1
4129
+ },
4130
+ end: {
4131
+ line: line - 1,
4132
+ character: character - 1
4133
+ }
4134
+ },
4135
+ context: {
4136
+ diagnostics: client$1.diagnostics.get(fileToUri(file))?.diagnostics ?? [],
4137
+ triggerKind: 1
4138
+ }
4139
+ }, false) ?? [];
4140
+ if (actions.length === 0) return `${name$3}: no code actions available`;
4141
+ const applyIndex = typeof record.apply === "number" ? record.apply : void 0;
4142
+ if (applyIndex === void 0) return `${name$3}: code actions (pass "apply":<index> to apply):\n` + actions.map((a, i) => ` [${i}] ${a.title ?? "(untitled)"}${a.kind ? ` (${a.kind})` : ""}`).join("\n");
4143
+ const chosen = actions[applyIndex];
4144
+ if (chosen === void 0) throw new UrlSchemesError("LSP_BAD_ARGS", `lsp device: "apply" index ${applyIndex} out of range (0..${actions.length - 1})`);
4145
+ if (chosen.edit !== void 0) {
4146
+ const lines = await applyOrPreviewWorkspaceEdit(chosen.edit, true);
4147
+ return `${name$3}: applied code action "${chosen.title ?? applyIndex}":\n${lines.join("\n")}`;
4148
+ }
4149
+ if (chosen.command?.command !== void 0) {
4150
+ await sendRequest(client$1, "workspace/executeCommand", chosen.command);
4151
+ return `${name$3}: executed command "${chosen.command.command}" for "${chosen.title ?? applyIndex}"`;
4152
+ }
4153
+ return `${name$3}: code action "${chosen.title ?? applyIndex}" carries neither edit nor command`;
4154
+ }
4691
4155
  switch (action) {
4692
4156
  case "diagnostics": {
4693
4157
  if (saved !== true) {
@@ -4708,12 +4172,25 @@ const lspDevice = {
4708
4172
  timeoutMs: DIAGNOSTICS_FEEDBACK_WAIT_MS
4709
4173
  });
4710
4174
  const kept = waited.diagnostics.filter((diagnostic) => !(waited.timedOut && diagnostic.source === "rustc")).map(diagnosticRecord);
4711
- return {
4175
+ const out = {
4712
4176
  ...base,
4713
4177
  diagnostics: kept,
4714
4178
  summary: diagnosticsSummary(kept),
4715
4179
  check: waited.timedOut ? "timeout-dropped-rustc" : "completed"
4716
4180
  };
4181
+ if (client.serverCapabilities !== void 0 && "diagnosticProvider" in client.serverCapabilities) try {
4182
+ const pulled = await sendRequest(client, "textDocument/diagnostic", { textDocument: { uri } });
4183
+ if (pulled !== null && Array.isArray(pulled.items)) {
4184
+ const items = pulled.items.map((d) => diagnosticRecord(d));
4185
+ return {
4186
+ ...out,
4187
+ diagnostics: items,
4188
+ summary: diagnosticsSummary(items),
4189
+ pull: true
4190
+ };
4191
+ }
4192
+ } catch {}
4193
+ return out;
4717
4194
  }
4718
4195
  case "definition": {
4719
4196
  const result = await lspPositionRequest(client, "textDocument/definition", {
@@ -4780,13 +4257,59 @@ const lspDevice = {
4780
4257
  default: throw new UrlSchemesError("LSP_BAD_ARGS", `lsp device: unhandled action "${String(action)}"`);
4781
4258
  }
4782
4259
  },
4783
- summary: "LSP queries over stdio language servers (defaults.json registry) — diagnostics / definition / references / hover on {file,line,character}"
4260
+ summary: "LSP queries over stdio language servers (defaults.json registry) — diagnostics / definition / references / hover on {file,line,character}",
4261
+ read: lspDeviceRead
4784
4262
  };
4263
+ /** One-level recursion for fan-out: re-dispatch with a specific `server`. */
4264
+ const thisDispatch = (args) => lspDevice.execute(args);
4265
+ const READ_ACTIONS = new Set([
4266
+ "status",
4267
+ "diagnostics",
4268
+ "definition",
4269
+ "references",
4270
+ "hover"
4271
+ ]);
4272
+ function parseReadQuery(subpath) {
4273
+ const qIndex = subpath.indexOf("?");
4274
+ const action = qIndex === -1 ? subpath : subpath.slice(0, qIndex);
4275
+ if (!READ_ACTIONS.has(action)) return void 0;
4276
+ const args = { action };
4277
+ if (qIndex !== -1) for (const [k, v] of new URLSearchParams(subpath.slice(qIndex + 1))) args[k] = k === "all" || k === "apply" ? v === "1" || v === "true" : v;
4278
+ return {
4279
+ action,
4280
+ args
4281
+ };
4282
+ }
4283
+ async function lspDeviceRead(subpath, session) {
4284
+ const parsed = parseReadQuery(subpath);
4285
+ if (parsed === void 0) return `unknown read path dvc://lsp/${subpath} — reads: status | diagnostics?file=…[&all=1] | definition?file=&line=[&character=|&symbol=] | references?… | hover?…`;
4286
+ if (parsed.action === "status") {
4287
+ if (session !== void 0) return JSON.stringify({
4288
+ ok: true,
4289
+ gate: lspGateState(session)
4290
+ });
4291
+ return JSON.stringify({
4292
+ ok: true,
4293
+ note: "per-session gate — read from a session for its gate (session missing in read env)"
4294
+ });
4295
+ }
4296
+ const result = await lspDevice.execute(parsed.args);
4297
+ return JSON.stringify(result, null, 2);
4298
+ }
4785
4299
  /**
4786
4300
  * Mount the lsp device. `registry` defaults to the dvc:// module-level registry
4787
4301
  * (`registerDvcDevice`); tests may pass their own recorder instead.
4788
4302
  */
4789
4303
  function installLspDevices(registry = registerDvcDevice) {
4304
+ registerLspGateTransport(async ({ kind, filePath, content }) => {
4305
+ const { client } = await obtainClient({}, filePath);
4306
+ if (kind === "read") {
4307
+ await ensureFileOpen(client, filePath);
4308
+ return;
4309
+ }
4310
+ await syncFileContent(client, filePath, typeof content === "string" && content !== "" ? content : await import("node:fs/promises").then((fs) => fs.readFile(filePath, "utf-8")));
4311
+ await notifyDidSave(client, filePath);
4312
+ });
4790
4313
  registry("lsp", lspDevice);
4791
4314
  }
4792
4315
 
@@ -5364,8 +4887,8 @@ function loadSectionText() {
5364
4887
  }
5365
4888
  const SECTION_TEXT = loadSectionText();
5366
4889
  /** Render the section when the URL capability is on; `undefined` gates it off. */
5367
- function generalSection(gates) {
5368
- if (!gates.urlSchemes) return void 0;
4890
+ function generalSection(gates$1) {
4891
+ if (!gates$1.urlSchemes) return void 0;
5369
4892
  return {
5370
4893
  name: GENERAL_SECTION_NAME,
5371
4894
  order: GENERAL_SECTION_ORDER,
@@ -5418,9 +4941,24 @@ function lookupOptions(env) {
5418
4941
  * maps a URL to its on-disk location so `grep`/`glob` can search the real
5419
4942
  * files instead of resolved text.
5420
4943
  */
4944
+ /**
4945
+ * Render the cwd-scoped skill catalog: one line per winning summary
4946
+ * (`name — description`, plus `whenToUse` when present). Empty registry →
4947
+ * an explicit empty answer, never an error.
4948
+ */
4949
+ async function renderSkillList(skills, env) {
4950
+ const all = await skills.list(lookupOptions(env));
4951
+ if (all.length === 0) return "No skills available in this workspace scope.";
4952
+ const lines = all.map((skill) => {
4953
+ const when = skill.whenToUse !== void 0 && skill.whenToUse !== "" ? ` (use when: ${skill.whenToUse})` : "";
4954
+ return `skill://${skill.name} — ${skill.description}${when}`;
4955
+ });
4956
+ return `${all.length} available skill(s):\n${lines.join("\n")}`;
4957
+ }
5421
4958
  function createSkillHandler(deps) {
5422
4959
  return {
5423
4960
  async resolve(env, path$1) {
4961
+ if (path$1 === "" || path$1 === "/") return await renderSkillList(deps.skills, env);
5424
4962
  const { name: name$2, subpath } = splitSkillPath(path$1);
5425
4963
  const skill = await deps.skills.get(name$2, lookupOptions(env));
5426
4964
  if (skill === void 0) throw new UrlSchemesError("URL_SKILL_NOT_FOUND", `skill "${name$2}" is unknown or no longer available`);
@@ -5574,6 +5112,26 @@ function isSchemeUrl$2(raw) {
5574
5112
  throw error;
5575
5113
  }
5576
5114
  }
5115
+ /**
5116
+ * Split a URL-with-glob pattern into `{ urlPart, globTail }`: the first glob
5117
+ * metacharacter (`*?[`) in the path portion starts the glob tail, which the
5118
+ * path-backed branch applies WITHIN the resource's real disk directory
5119
+ * (`skill://grp/*` → root = the skill dir, pattern `*`). A URL with no
5120
+ * metacharacters keeps the bare-URL listing behavior (tail empty → the
5121
+ * all-depth default pattern).
5122
+ */
5123
+ function splitUrlGlob(pattern) {
5124
+ const metachar = pattern.search(/[*?\[]/);
5125
+ if (metachar === -1) return {
5126
+ urlPart: pattern,
5127
+ globTail: ""
5128
+ };
5129
+ const tail = pattern.slice(metachar).replace(/^\/+/, "");
5130
+ return {
5131
+ urlPart: pattern.slice(0, metachar).replace(/\/+$/, ""),
5132
+ globTail: tail
5133
+ };
5134
+ }
5577
5135
  /** Structured error for calls that cannot run without the native delegate. */
5578
5136
  function nativeUnavailable$1() {
5579
5137
  return new UrlSchemesError("NATIVE_GLOB_UNAVAILABLE", "the host did not deploy a native glob tool — URL-aware glob cannot delegate discovery");
@@ -5626,12 +5184,14 @@ function createGlobTool(deps) {
5626
5184
  async execute(args, exec) {
5627
5185
  if (isSchemeUrl$2(args.pattern)) {
5628
5186
  const env = execEnv$1(exec, args.pattern);
5629
- const diskDir = await resolver.resolvePath(env, args.pattern);
5187
+ const { urlPart, globTail } = splitUrlGlob(args.pattern);
5188
+ const diskDir = await resolver.resolvePath(env, urlPart);
5630
5189
  if (diskDir !== void 0) {
5631
5190
  if (nativeGlob === void 0) throw nativeUnavailable$1();
5191
+ const rootedPattern = globTail === "" ? "**/*" : globTail;
5632
5192
  return nativeGlob.execute({
5633
5193
  ...args,
5634
- pattern: "**/*",
5194
+ pattern: rootedPattern,
5635
5195
  path: diskDir
5636
5196
  }, exec);
5637
5197
  }
@@ -5752,10 +5312,25 @@ function createGrepTool(deps) {
5752
5312
  if (args.path !== void 0 && isSchemeUrl$1(args.path)) {
5753
5313
  const env = execEnv(exec, args.path);
5754
5314
  const diskPath = await resolver.resolvePath(env, args.path);
5755
- if (diskPath !== void 0) return nativeGrep.execute({
5756
- ...args,
5757
- path: diskPath
5758
- }, exec);
5315
+ if (diskPath !== void 0) {
5316
+ const result = await nativeGrep.execute({
5317
+ ...args,
5318
+ path: diskPath
5319
+ }, exec);
5320
+ if (!Array.isArray(result.matches)) return result;
5321
+ const prefix = diskPath.endsWith("/") ? diskPath : `${diskPath}/`;
5322
+ return { matches: result.matches.map((m) => {
5323
+ if (m.path.startsWith(prefix)) return {
5324
+ ...m,
5325
+ path: `${args.path}/${m.path.slice(prefix.length)}`
5326
+ };
5327
+ if (!m.path.startsWith("/")) return {
5328
+ ...m,
5329
+ path: `${args.path}/${m.path}`
5330
+ };
5331
+ return m;
5332
+ }) };
5333
+ }
5759
5334
  return withTempMaterialization(await resolver.resolve(env, args.path), (tempDir) => nativeGrep.execute({
5760
5335
  ...args,
5761
5336
  path: join(tempDir, "content.txt")
@@ -5767,7 +5342,231 @@ function createGrepTool(deps) {
5767
5342
  }
5768
5343
 
5769
5344
  //#endregion
5770
- //#region src/url-schemes/vendored/hashline/constants.js
5345
+ //#region src/url-schemes/tools/read.ts
5346
+ /** Mirrors `parseUrl`'s scheme prefix so the fork matches the resolver exactly. */
5347
+ /**
5348
+ * Shape args toward the delegate's declared parameters: move the file path
5349
+ * between the `path`/`file_path` aliases according to which key the
5350
+ * delegate's schema actually declares (host-native read = `file_path`,
5351
+ * hashline = `path`). Unknown-key tolerant delegates are unaffected.
5352
+ */
5353
+ function shapeArgsForDelegate(delegate, args, rawPath) {
5354
+ const shaped = { ...args };
5355
+ if (rawPath === "") {
5356
+ delete shaped.path;
5357
+ delete shaped.file_path;
5358
+ return shaped;
5359
+ }
5360
+ const props = delegate.parameters?.properties;
5361
+ if (props === void 0) return shaped;
5362
+ const declaresPath = props["path"] !== void 0;
5363
+ const declaresFilePath = props["file_path"] !== void 0;
5364
+ if (declaresFilePath && !declaresPath) {
5365
+ shaped.file_path = rawPath;
5366
+ delete shaped.path;
5367
+ } else if (declaresPath && !declaresFilePath) {
5368
+ shaped.path = rawPath;
5369
+ delete shaped.file_path;
5370
+ } else {
5371
+ shaped.path = rawPath;
5372
+ shaped.file_path = rawPath;
5373
+ }
5374
+ return shaped;
5375
+ }
5376
+ const SCHEME_URL_RE = /^[a-z][a-z0-9]*:\/\//;
5377
+ /**
5378
+ * Build the scheme-aware `read` wrapper. The URL branch builds the resolver
5379
+ * env per call (agent + cwd + rawUrl); the scheme handlers read whichever
5380
+ * fields they need off it. File paths delegate to the terminal delegate so
5381
+ * the owning feature (hashline anchors or the native read) surfaces its own
5382
+ * result — never a silent reimplementation here.
5383
+ */
5384
+ function createSchemeReadTool(deps) {
5385
+ const { resolver, capturedRead } = deps;
5386
+ return defineTool({
5387
+ name: "read",
5388
+ description: "Read a text file (each line returned as `HASH│content` with a 3-char hash anchor for later edit calls). File reads page with offset/limit. Also accepts resource URIs (skill://, agent://, dsh://, dvc://, ctx://, http(s)://).",
5389
+ parameters: {
5390
+ path: {
5391
+ type: "string",
5392
+ description: "File path, or a resource URI (skill://, agent://, dsh://, dvc://, ctx://, http(s)://)."
5393
+ },
5394
+ file_path: {
5395
+ type: "string",
5396
+ description: "Alias of `path` (accepted for host-native compatibility)."
5397
+ },
5398
+ offset: {
5399
+ type: "number",
5400
+ description: "Line number to start reading from (1-indexed, file reads only)"
5401
+ },
5402
+ limit: {
5403
+ type: "number",
5404
+ description: "Maximum number of lines to read (file reads only)"
5405
+ }
5406
+ },
5407
+ output: {
5408
+ schema: { type: "string" },
5409
+ render: (_args, value) => [{
5410
+ type: "text",
5411
+ text: value
5412
+ }]
5413
+ },
5414
+ async execute(args, exec) {
5415
+ const rawPath = typeof args?.path === "string" ? args.path : typeof args?.file_path === "string" ? args.file_path : "";
5416
+ if (resolver !== void 0 && SCHEME_URL_RE.test(rawPath)) {
5417
+ const cwd = exec.agent?.session.header.cwd;
5418
+ const env = cwd === void 0 ? {
5419
+ agent: exec.agent,
5420
+ rawUrl: rawPath
5421
+ } : {
5422
+ agent: exec.agent,
5423
+ cwd,
5424
+ rawUrl: rawPath
5425
+ };
5426
+ return resolver.resolve(env, rawPath);
5427
+ }
5428
+ if (capturedRead !== void 0 && capturedRead.execute !== void 0) {
5429
+ const delegated = await capturedRead.execute(shapeArgsForDelegate(capturedRead, args, rawPath), exec);
5430
+ if (typeof delegated === "string") return delegated;
5431
+ return delegated === void 0 || delegated === null ? "" : JSON.stringify(delegated);
5432
+ }
5433
+ throw new UrlSchemesError("NATIVE_READ_UNAVAILABLE", "no read delegate is available: neither hashline nor the native read is in place");
5434
+ }
5435
+ });
5436
+ }
5437
+
5438
+ //#endregion
5439
+ //#region src/url-schemes/tools/write.ts
5440
+ /**
5441
+ * Schemes the URL schema registers (v0.1.8d): the default write dispatch
5442
+ * keys off this table; the integration step can replace the whole
5443
+ * dispatch through `writeScheme` when it needs the live registry.
5444
+ *
5445
+ * Rendered from `catalog.ts` rather than written out here — this literal was
5446
+ * one of the hand-maintained copies that drifted from the scheme set.
5447
+ */
5448
+ const REGISTERED_SCHEMES = SCHEME_NAMES;
5449
+ /** Detects a `scheme://` prefix with the resolver layer's own parser. */
5450
+ function isSchemeUrl(raw) {
5451
+ try {
5452
+ parseUrl(raw);
5453
+ return true;
5454
+ } catch (error) {
5455
+ if (error instanceof UrlSchemesError && error.code === "URL_NO_SCHEME") return false;
5456
+ throw error;
5457
+ }
5458
+ }
5459
+ /**
5460
+ * Default scheme-write dispatch: `dvc://` routes through the device
5461
+ * registry (`dispatchDvcWrite`); every other write channel is rejected.
5462
+ */
5463
+ async function defaultSchemeWrite(scheme, path$1, content, meta = {}) {
5464
+ if (scheme === "dvc") {
5465
+ const result = await dispatchDvcWrite(path$1, content, meta.session);
5466
+ return {
5467
+ path: `dvc://${path$1}`,
5468
+ operation: "execute",
5469
+ before: "",
5470
+ after: JSON.stringify(result, null, 2)
5471
+ };
5472
+ }
5473
+ if (scheme === "ctx") throw new UrlSchemesError("URL_READ_ONLY", `ctx:// is a curated read-only snapshot — write to ctx://${path$1} is not supported`);
5474
+ if (REGISTERED_SCHEMES.includes(scheme)) throw new UrlSchemesError("URL_WRITE_UNSUPPORTED", `write to ${scheme}:// is not supported (read-only scheme, or its write channel is not wired yet)`);
5475
+ throw new UrlSchemesError("URL_UNREGISTERED_SCHEME", `no handler registered for scheme "${scheme}" (registered: ${REGISTERED_SCHEMES.join(", ")})`);
5476
+ }
5477
+ /**
5478
+ * Build the `write` {@link ToolDefinition}: `scheme://` paths route to the
5479
+ * scheme write dispatch, ordinary paths delegate to the captured native
5480
+ * write definition with args and exec passed through untouched.
5481
+ */
5482
+ function createWriteTool(deps) {
5483
+ const { nativeWrite, postWrite, preWriteFormat, sandbox } = deps;
5484
+ const writeScheme = deps.writeScheme ?? defaultSchemeWrite;
5485
+ return defineTool({
5486
+ name: "write",
5487
+ description: "Create or fully replace a UTF-8 text file.",
5488
+ parameters: {
5489
+ file_path: {
5490
+ type: "string",
5491
+ required: true,
5492
+ description: "Path to write, resolved by the filesystem backend."
5493
+ },
5494
+ content: {
5495
+ type: "string",
5496
+ required: true,
5497
+ description: "Full UTF-8 text content to write."
5498
+ },
5499
+ ...sandbox !== void 0 && sandbox.escalationModes.length > 0 ? sandbox.schemaFields() : {}
5500
+ },
5501
+ output: {
5502
+ schema: {
5503
+ type: "object",
5504
+ additionalProperties: false,
5505
+ properties: {
5506
+ path: {
5507
+ type: "string",
5508
+ required: true
5509
+ },
5510
+ operation: {
5511
+ type: "string",
5512
+ required: true,
5513
+ enum: [
5514
+ "create",
5515
+ "update",
5516
+ "execute"
5517
+ ]
5518
+ },
5519
+ before: {
5520
+ required: true,
5521
+ oneOf: [{ type: "string" }, { type: "null" }]
5522
+ },
5523
+ after: {
5524
+ type: "string",
5525
+ required: true
5526
+ },
5527
+ diagnostics: { type: "string" }
5528
+ }
5529
+ },
5530
+ render: (_args, value) => {
5531
+ const verb = value.operation === "update" ? "Updated" : value.operation === "execute" ? "Executed" : "Created";
5532
+ let text = value.diagnostics === void 0 ? `${verb} ${value.path}` : `${verb} ${value.path}\n${value.diagnostics}`;
5533
+ if (value.operation === "execute" && value.after !== void 0) text += `\n${value.after}`;
5534
+ return [{
5535
+ type: "text",
5536
+ text
5537
+ }];
5538
+ }
5539
+ },
5540
+ async execute(args, exec) {
5541
+ if (isSchemeUrl(args.file_path)) {
5542
+ const parsed = parseUrl(args.file_path);
5543
+ return writeScheme(parsed.scheme, parsed.path, args.content, { session: exec.agent?.id });
5544
+ }
5545
+ if (nativeWrite === void 0) throw new UrlSchemesError("NATIVE_WRITE_UNAVAILABLE", "the host did not deploy a native write tool — URL-aware write cannot delegate filesystem writes");
5546
+ let content = args.content;
5547
+ try {
5548
+ const formatted = await preWriteFormat?.(args.file_path, content);
5549
+ if (formatted !== void 0 && formatted !== content) content = formatted;
5550
+ } catch {}
5551
+ const writeArgs = content === args.content ? args : {
5552
+ ...args,
5553
+ content
5554
+ };
5555
+ const outcome = await nativeWrite.execute(writeArgs, exec);
5556
+ try {
5557
+ const diagnostics = await postWrite?.(args.file_path, content);
5558
+ if (diagnostics !== void 0) return {
5559
+ ...outcome,
5560
+ diagnostics
5561
+ };
5562
+ } catch {}
5563
+ return outcome;
5564
+ }
5565
+ });
5566
+ }
5567
+
5568
+ //#endregion
5569
+ //#region src/hashline/constants.js
5771
5570
  const MAX_BYTES = 100 * 1024 * 1024;
5772
5571
  const MAX_READ_LINE_BYTES = 200 * 1024;
5773
5572
  const HASH_STORE_BUSY_TIMEOUT = 1e3;
@@ -5779,7 +5578,7 @@ const NOOP_LOOP_THRESHOLD = 3;
5779
5578
  const NEW_CONTENT_NOT_STRING_MSG = "[E_BAD_SHAPE] \"replacement_text\" must be a string with \\n line separators, not an array. Do not pass an array of lines — pass the replacement text as one string: \"line1\\nline2\". Use \"\" to delete a range.";
5780
5579
 
5781
5580
  //#endregion
5782
- //#region src/url-schemes/vendored/hashline/utils.js
5581
+ //#region src/hashline/utils.js
5783
5582
  function isRec(value) {
5784
5583
  return typeof value === "object" && value !== null && !Array.isArray(value);
5785
5584
  }
@@ -5832,7 +5631,7 @@ function clipLine(line, maxLen = 200) {
5832
5631
  }
5833
5632
 
5834
5633
  //#endregion
5835
- //#region src/url-schemes/vendored/hashline/contract.js
5634
+ //#region src/hashline/contract.js
5836
5635
  const normalizedEdit = Symbol("normalizedEdit");
5837
5636
  function isNormalizedEdit(input) {
5838
5637
  return isRec(input) && input[normalizedEdit] === true;
@@ -5923,7 +5722,7 @@ function assertUndoRequest(request) {
5923
5722
  }
5924
5723
 
5925
5724
  //#endregion
5926
- //#region src/url-schemes/vendored/hashline/paths.js
5725
+ //#region src/hashline/paths.js
5927
5726
  /**
5928
5727
  * On-disk home for dsh-better-edit state: ONE centralized store per harness
5929
5728
  * home — `$DSH_HOME/storages/dsh-better-edit` (default
@@ -5940,11 +5739,11 @@ function hashStorePath() {
5940
5739
  }
5941
5740
 
5942
5741
  //#endregion
5943
- //#region src/url-schemes/vendored/hashline/fs-write.js
5742
+ //#region src/hashline/fs-write.js
5944
5743
  const STALE_TEMP_MS = 3600 * 1e3;
5945
5744
 
5946
5745
  //#endregion
5947
- //#region src/url-schemes/vendored/hashline/hashline/hash-assign.js
5746
+ //#region src/hashline/hashline/hash-assign.js
5948
5747
  let hasher = null;
5949
5748
  function getH() {
5950
5749
  if (hasher) return hasher;
@@ -6158,7 +5957,7 @@ function mapStableHashes(oldContent, oldHashes, newContent, removedHashes) {
6158
5957
  }
6159
5958
 
6160
5959
  //#endregion
6161
- //#region src/url-schemes/vendored/hashline/hash-store.js
5960
+ //#region src/hashline/hash-store.js
6162
5961
  function isValidHashList(value) {
6163
5962
  if (!Array.isArray(value)) return false;
6164
5963
  for (const hash of value) if (typeof hash !== "string" || !HASH_RE.test(hash)) return false;
@@ -6648,7 +6447,7 @@ async function upsertSnapshotFor(path$1, checksum, lineCount, hashes) {
6648
6447
  }
6649
6448
 
6650
6449
  //#endregion
6651
- //#region src/url-schemes/vendored/hashline/hashline/hash.js
6450
+ //#region src/hashline/hashline/hash.js
6652
6451
  let defaultHashSnapshotIO;
6653
6452
  function snapshotIOFor(store) {
6654
6453
  if (store) return {
@@ -6697,7 +6496,7 @@ async function lineHashes(content, path$1, previous, ioOrStore, persist) {
6697
6496
  }
6698
6497
 
6699
6498
  //#endregion
6700
- //#region src/url-schemes/vendored/hashline/session-view.js
6499
+ //#region src/hashline/session-view.js
6701
6500
  const current = new AsyncLocalStorage();
6702
6501
  function withWorkspace(cwd, fn) {
6703
6502
  return current.run(cwd, fn);
@@ -6918,7 +6717,7 @@ async function scanDrift(input) {
6918
6717
  }
6919
6718
 
6920
6719
  //#endregion
6921
- //#region src/url-schemes/vendored/hashline/hashline/anchor-pipeline.js
6720
+ //#region src/hashline/hashline/anchor-pipeline.js
6922
6721
  function diagRef(ref) {
6923
6722
  const trimmed = ref.trim();
6924
6723
  if (!trimmed.length) return `[E_BAD_REF] Invalid anchor. Expected a 3-char alphanumeric anchor (e.g. "aB3").`;
@@ -7561,7 +7360,7 @@ function changedRange(original, result) {
7561
7360
  }
7562
7361
 
7563
7362
  //#endregion
7564
- //#region src/url-schemes/vendored/hashline/edit-diff.js
7363
+ //#region src/hashline/edit-diff.js
7565
7364
  function detectEnding(content) {
7566
7365
  const lfIdx = content.indexOf("\n");
7567
7366
  if (lfIdx === -1) return content.indexOf("\r") >= 0 ? "\r" : "\n";
@@ -7676,7 +7475,7 @@ function genDiff(oldContent, newContent, contextLines = 2, newContentHashes, old
7676
7475
  }
7677
7476
 
7678
7477
  //#endregion
7679
- //#region src/url-schemes/vendored/hashline/file-view.js
7478
+ //#region src/hashline/file-view.js
7680
7479
  const MAX_HASH_LINES$1 = HASH_SPACE;
7681
7480
  const DEFAULT_MAX_LINES = 2e3;
7682
7481
  const DEFAULT_MAX_BYTES = 50 * 1024;
@@ -7900,7 +7699,7 @@ async function readView(io, path$1, cwd, opts = {}) {
7900
7699
  }
7901
7700
 
7902
7701
  //#endregion
7903
- //#region src/url-schemes/vendored/hashline/fs-bridge.js
7702
+ //#region src/hashline/fs-bridge.js
7904
7703
  /**
7905
7704
  * Map an `ctx.fs` failure onto the hashline model-facing vocabulary so the
7906
7705
  * model sees the same structured error codes as the pure pipeline.
@@ -7919,296 +7718,95 @@ function mapFsError(error, displayPath) {
7919
7718
  if (code === "FS_ABORTED") throw new Error("Operation aborted");
7920
7719
  }
7921
7720
  throw error;
7922
- }
7923
- /** FileIO over the deployment's `ctx.fs` service. */
7924
- function ctxFsIO(fs, ctx) {
7925
- return {
7926
- async resolve(path$1, cwd, signal) {
7927
- const target = await fs.resolve(path$1, {
7928
- ...cwd !== void 0 ? { cwd } : {},
7929
- ...signal !== void 0 ? { signal } : {}
7930
- });
7931
- return fs.processPath(target);
7932
- },
7933
- async readText(absolutePath, signal) {
7934
- try {
7935
- const target = await fs.resolve(absolutePath, { ...signal !== void 0 ? { signal } : {} });
7936
- return await fs.readText(target, signal);
7937
- } catch (error) {
7938
- return mapFsError(error, absolutePath);
7939
- }
7940
- },
7941
- async writeText(absolutePath, content, signal, exec, sandboxPolicy) {
7942
- try {
7943
- const target = await fs.resolve(absolutePath, { ...signal !== void 0 ? { signal } : {} });
7944
- const intent = await ctx.waterfall("fs/write-intent", target, exec, () => void 0);
7945
- const outcome = await fs.writeText(target, content, intent, signal, sandboxPolicy);
7946
- ctx.emit("fs/observed", target, {
7947
- kind: "present",
7948
- version: outcome.version
7949
- }, exec);
7950
- } catch (error) {
7951
- return mapFsError(error, absolutePath);
7952
- }
7953
- },
7954
- async emitObserved(absolutePath, exec, signal) {
7955
- try {
7956
- const target = await fs.resolve(absolutePath, { ...signal !== void 0 ? { signal } : {} });
7957
- const info = await fs.stat(target, signal);
7958
- if (info !== void 0) ctx.emit("fs/observed", target, {
7959
- kind: "present",
7960
- version: info.version
7961
- }, exec);
7962
- } catch (error) {
7963
- console.error(`dsh-better-edit: fs/observed emission failed for ${absolutePath}: ${error instanceof Error ? error.message : String(error)}`);
7964
- }
7965
- },
7966
- async statVersion(absolutePath, signal) {
7967
- try {
7968
- const target = await fs.resolve(absolutePath, { ...signal !== void 0 ? { signal } : {} });
7969
- return (await fs.stat(target, signal))?.version ?? void 0;
7970
- } catch {
7971
- return;
7972
- }
7973
- }
7974
- };
7975
- }
7976
-
7977
- //#endregion
7978
- //#region src/url-schemes/vendored/hashline/read-and-serve.js
7979
- /** Appended when the file had non-UTF-8 bytes; editing rewrites it as UTF-8. */
7980
- const UTF8_REWRITE_NOTE = "[Non-UTF-8 bytes shown as U+FFFD; editing rewrites the file as UTF-8.]";
7981
- /**
7982
- * Perform one read-and-serve: normalize the file at `rawPath`, render its
7983
- * hashline preview, record the shown rows as served for the session, and clear
7984
- * the reported-drift marks (a fresh read resets them). The returned text
7985
- * carries the UTF-8 rewrite note when the file had decode errors.
7986
- *
7987
- * Emits nothing on the fs-observation gate — callers that need the
7988
- * observation recorded (the `read` tool) do that themselves with their exec
7989
- * context.
7990
- */
7991
- async function readAndServe(io, rawPath, cwd, options) {
7992
- const { signal } = options;
7993
- abortIf(signal);
7994
- const view = await readView(io, rawPath, cwd, {
7995
- offset: options.offset,
7996
- limit: options.limit,
7997
- signal
7998
- });
7999
- if (view.served.length > 0) await recordServed(view.absolutePath, view.served, view.hashes.length);
8000
- await clearDriftReported(view.absolutePath);
8001
- return {
8002
- text: view.hadUtf8DecodeErrors ? `${view.text}\n\n${UTF8_REWRITE_NOTE}` : view.text,
8003
- served: view.served,
8004
- hadUtf8DecodeErrors: view.hadUtf8DecodeErrors,
8005
- absolutePath: view.absolutePath
8006
- };
8007
- }
8008
-
8009
- //#endregion
8010
- //#region src/url-schemes/tools/read.ts
8011
- /** Mirrors `parseUrl`'s scheme prefix so the fork matches the resolver exactly. */
8012
- const SCHEME_URL_RE = /^[a-z][a-z0-9]*:\/\//;
8013
- /**
8014
- * Build the URL-aware `read` tool.
8015
- *
8016
- * The URL branch builds the resolver env per call (agent + cwd + rawUrl); the
8017
- * scheme handlers read whichever fields they need off it.
8018
- */
8019
- function createReadTool(deps) {
8020
- const { resolver, fs, ctx, gates, capturedRead } = deps;
8021
- const io = ctxFsIO(fs, ctx);
8022
- return defineTool({
8023
- name: "read",
8024
- description: "Read a text file (each line returned as `HASH│content` with a 3-char hash anchor for later edit calls). File reads page with offset/limit.",
8025
- parameters: {
8026
- path: {
8027
- type: "string",
8028
- description: "File path (hashline-anchored read)."
8029
- },
8030
- offset: {
8031
- type: "number",
8032
- description: "Line number to start reading from (1-indexed, file reads only)"
8033
- },
8034
- limit: {
8035
- type: "number",
8036
- description: "Maximum number of lines to read (file reads only)"
8037
- }
8038
- },
8039
- output: {
8040
- schema: { type: "string" },
8041
- render: (_args, value) => [{
8042
- type: "text",
8043
- text: value
8044
- }]
8045
- },
8046
- async execute(args, exec) {
8047
- const canonical = normalizeRequest(args);
8048
- assertReadRequest(canonical);
8049
- const rawPath = canonical.path;
8050
- const isScheme = SCHEME_URL_RE.test(rawPath);
8051
- if (gates.urlSchemes && isScheme) {
8052
- const cwd = exec.agent?.session.header.cwd;
8053
- const env = cwd === void 0 ? {
8054
- agent: exec.agent,
8055
- rawUrl: rawPath
8056
- } : {
8057
- agent: exec.agent,
8058
- cwd,
8059
- rawUrl: rawPath
8060
- };
8061
- return resolver.resolve(env, rawPath);
8062
- }
8063
- if (gates.hashline && !isScheme) return withWorkspace(execCwd(exec), async () => {
8064
- const cwd = execCwd(exec);
8065
- const signal = exec.signal;
8066
- const { text, absolutePath } = await readAndServe(io, rawPath, cwd, {
8067
- signal,
8068
- offset: canonical.offset,
8069
- limit: canonical.limit
8070
- });
8071
- await io.emitObserved(absolutePath, exec, signal);
8072
- return text;
8073
- });
8074
- if (capturedRead !== void 0 && capturedRead.execute !== void 0) return capturedRead.execute(args, exec);
8075
- throw new UrlSchemesError("NATIVE_READ_UNAVAILABLE", "no read delegate is available: urlSchemes and hashline gates are both disabled");
8076
- }
8077
- });
8078
- }
8079
-
8080
- //#endregion
8081
- //#region src/url-schemes/tools/write.ts
8082
- /**
8083
- * Schemes the URL schema registers (v0.1.8d): the default write dispatch
8084
- * keys off this table; the integration step can replace the whole
8085
- * dispatch through `writeScheme` when it needs the live registry.
8086
- *
8087
- * Rendered from `catalog.ts` rather than written out here — this literal was
8088
- * one of the hand-maintained copies that drifted from the scheme set.
8089
- */
8090
- const REGISTERED_SCHEMES = SCHEME_NAMES;
8091
- /** Detects a `scheme://` prefix with the resolver layer's own parser. */
8092
- function isSchemeUrl(raw) {
8093
- try {
8094
- parseUrl(raw);
8095
- return true;
8096
- } catch (error) {
8097
- if (error instanceof UrlSchemesError && error.code === "URL_NO_SCHEME") return false;
8098
- throw error;
8099
- }
8100
- }
8101
- /**
8102
- * Default scheme-write dispatch: `dvc://` routes through the device
8103
- * registry (`dispatchDvcWrite`); every other write channel is rejected.
8104
- */
8105
- async function defaultSchemeWrite(scheme, path$1, content) {
8106
- if (scheme === "dvc") {
8107
- const result = await dispatchDvcWrite(path$1, content);
8108
- return {
8109
- path: `dvc://${path$1}`,
8110
- operation: "execute",
8111
- before: "",
8112
- after: JSON.stringify(result, null, 2)
8113
- };
8114
- }
8115
- if (scheme === "ctx") throw new UrlSchemesError("URL_READ_ONLY", `ctx:// is a curated read-only snapshot — write to ctx://${path$1} is not supported`);
8116
- if (REGISTERED_SCHEMES.includes(scheme)) throw new UrlSchemesError("URL_WRITE_UNSUPPORTED", `write to ${scheme}:// is not supported (read-only scheme, or its write channel is not wired yet)`);
8117
- throw new UrlSchemesError("URL_UNREGISTERED_SCHEME", `no handler registered for scheme "${scheme}" (registered: ${REGISTERED_SCHEMES.join(", ")})`);
8118
- }
8119
- /**
8120
- * Build the `write` {@link ToolDefinition}: `scheme://` paths route to the
8121
- * scheme write dispatch, ordinary paths delegate to the captured native
8122
- * write definition with args and exec passed through untouched.
8123
- */
8124
- function createWriteTool(deps) {
8125
- const { nativeWrite, postWrite, preWriteFormat, sandbox } = deps;
8126
- const writeScheme = deps.writeScheme ?? defaultSchemeWrite;
8127
- return defineTool({
8128
- name: "write",
8129
- description: "Create or fully replace a UTF-8 text file.",
8130
- parameters: {
8131
- file_path: {
8132
- type: "string",
8133
- required: true,
8134
- description: "Path to write, resolved by the filesystem backend."
8135
- },
8136
- content: {
8137
- type: "string",
8138
- required: true,
8139
- description: "Full UTF-8 text content to write."
8140
- },
8141
- ...sandbox !== void 0 && sandbox.escalationModes.length > 0 ? sandbox.schemaFields() : {}
8142
- },
8143
- output: {
8144
- schema: {
8145
- type: "object",
8146
- additionalProperties: false,
8147
- properties: {
8148
- path: {
8149
- type: "string",
8150
- required: true
8151
- },
8152
- operation: {
8153
- type: "string",
8154
- required: true,
8155
- enum: [
8156
- "create",
8157
- "update",
8158
- "execute"
8159
- ]
8160
- },
8161
- before: {
8162
- required: true,
8163
- oneOf: [{ type: "string" }, { type: "null" }]
8164
- },
8165
- after: {
8166
- type: "string",
8167
- required: true
8168
- },
8169
- diagnostics: { type: "string" }
8170
- }
8171
- },
8172
- render: (_args, value) => {
8173
- const verb = value.operation === "update" ? "Updated" : value.operation === "execute" ? "Executed" : "Created";
8174
- let text = value.diagnostics === void 0 ? `${verb} ${value.path}` : `${verb} ${value.path}\n${value.diagnostics}`;
8175
- if (value.operation === "execute" && value.after !== void 0) text += `\n${value.after}`;
8176
- return [{
8177
- type: "text",
8178
- text
8179
- }];
7721
+ }
7722
+ /** FileIO over the deployment's `ctx.fs` service. */
7723
+ function ctxFsIO(fs, ctx) {
7724
+ return {
7725
+ async resolve(path$1, cwd, signal) {
7726
+ const target = await fs.resolve(path$1, {
7727
+ ...cwd !== void 0 ? { cwd } : {},
7728
+ ...signal !== void 0 ? { signal } : {}
7729
+ });
7730
+ return fs.processPath(target);
7731
+ },
7732
+ async readText(absolutePath, signal) {
7733
+ try {
7734
+ const target = await fs.resolve(absolutePath, { ...signal !== void 0 ? { signal } : {} });
7735
+ return await fs.readText(target, signal);
7736
+ } catch (error) {
7737
+ return mapFsError(error, absolutePath);
8180
7738
  }
8181
7739
  },
8182
- async execute(args, exec) {
8183
- if (isSchemeUrl(args.file_path)) {
8184
- const parsed = parseUrl(args.file_path);
8185
- return writeScheme(parsed.scheme, parsed.path, args.content, {});
7740
+ async writeText(absolutePath, content, signal, exec, sandboxPolicy) {
7741
+ try {
7742
+ const target = await fs.resolve(absolutePath, { ...signal !== void 0 ? { signal } : {} });
7743
+ const intent = await ctx.waterfall("fs/write-intent", target, exec, () => void 0);
7744
+ const outcome = await fs.writeText(target, content, intent, signal, sandboxPolicy);
7745
+ ctx.emit("fs/observed", target, {
7746
+ kind: "present",
7747
+ version: outcome.version
7748
+ }, exec);
7749
+ } catch (error) {
7750
+ return mapFsError(error, absolutePath);
8186
7751
  }
8187
- if (nativeWrite === void 0) throw new UrlSchemesError("NATIVE_WRITE_UNAVAILABLE", "the host did not deploy a native write tool — URL-aware write cannot delegate filesystem writes");
8188
- let content = args.content;
7752
+ },
7753
+ async emitObserved(absolutePath, exec, signal) {
8189
7754
  try {
8190
- const formatted = await preWriteFormat?.(args.file_path, content);
8191
- if (formatted !== void 0 && formatted !== content) content = formatted;
8192
- } catch {}
8193
- const writeArgs = content === args.content ? args : {
8194
- ...args,
8195
- content
8196
- };
8197
- const outcome = await nativeWrite.execute(writeArgs, exec);
7755
+ const target = await fs.resolve(absolutePath, { ...signal !== void 0 ? { signal } : {} });
7756
+ const info = await fs.stat(target, signal);
7757
+ if (info !== void 0) ctx.emit("fs/observed", target, {
7758
+ kind: "present",
7759
+ version: info.version
7760
+ }, exec);
7761
+ } catch (error) {
7762
+ console.error(`dsh-better-edit: fs/observed emission failed for ${absolutePath}: ${error instanceof Error ? error.message : String(error)}`);
7763
+ }
7764
+ },
7765
+ async statVersion(absolutePath, signal) {
8198
7766
  try {
8199
- const diagnostics = await postWrite?.(args.file_path, content);
8200
- if (diagnostics !== void 0) return {
8201
- ...outcome,
8202
- diagnostics
8203
- };
8204
- } catch {}
8205
- return outcome;
7767
+ const target = await fs.resolve(absolutePath, { ...signal !== void 0 ? { signal } : {} });
7768
+ return (await fs.stat(target, signal))?.version ?? void 0;
7769
+ } catch {
7770
+ return;
7771
+ }
8206
7772
  }
7773
+ };
7774
+ }
7775
+
7776
+ //#endregion
7777
+ //#region src/hashline/read-and-serve.js
7778
+ /** Appended when the file had non-UTF-8 bytes; editing rewrites it as UTF-8. */
7779
+ const UTF8_REWRITE_NOTE = "[Non-UTF-8 bytes shown as U+FFFD; editing rewrites the file as UTF-8.]";
7780
+ /**
7781
+ * Perform one read-and-serve: normalize the file at `rawPath`, render its
7782
+ * hashline preview, record the shown rows as served for the session, and clear
7783
+ * the reported-drift marks (a fresh read resets them). The returned text
7784
+ * carries the UTF-8 rewrite note when the file had decode errors.
7785
+ *
7786
+ * Emits nothing on the fs-observation gate — callers that need the
7787
+ * observation recorded (the `read` tool) do that themselves with their exec
7788
+ * context.
7789
+ */
7790
+ async function readAndServe(io, rawPath, cwd, options) {
7791
+ const { signal } = options;
7792
+ abortIf(signal);
7793
+ const view = await readView(io, rawPath, cwd, {
7794
+ offset: options.offset,
7795
+ limit: options.limit,
7796
+ signal
8207
7797
  });
7798
+ if (view.served.length > 0) await recordServed(view.absolutePath, view.served, view.hashes.length);
7799
+ await clearDriftReported(view.absolutePath);
7800
+ return {
7801
+ text: view.hadUtf8DecodeErrors ? `${view.text}\n\n${UTF8_REWRITE_NOTE}` : view.text,
7802
+ served: view.served,
7803
+ hadUtf8DecodeErrors: view.hadUtf8DecodeErrors,
7804
+ absolutePath: view.absolutePath
7805
+ };
8208
7806
  }
8209
7807
 
8210
7808
  //#endregion
8211
- //#region src/url-schemes/vendored/hashline/sandbox.js
7809
+ //#region src/hashline/sandbox.js
8212
7810
  /**
8213
7811
  * The filesystem escalation API: advertisement gating, per-call policy
8214
7812
  * resolution, the one-approved wider retry, and denial-marker mapping. A pure
@@ -8294,7 +7892,7 @@ var FsSandboxController = class {
8294
7892
  };
8295
7893
 
8296
7894
  //#endregion
8297
- //#region src/url-schemes/vendored/hashline/undo-edit.js
7895
+ //#region src/hashline/undo-edit.js
8298
7896
  /** Load the last undo row for a path from the active store, if any. */
8299
7897
  async function readUndo(path$1) {
8300
7898
  return (await loadHashStore()).getUndo(path$1);
@@ -8376,7 +7974,7 @@ async function clearUndo(path$1) {
8376
7974
  }
8377
7975
 
8378
7976
  //#endregion
8379
- //#region src/url-schemes/vendored/hashline/noop-guard.js
7977
+ //#region src/hashline/noop-guard.js
8380
7978
  const noopLoopTracker = /* @__PURE__ */ new Map();
8381
7979
  function noopPayloadKey(absolutePath, removeFrom, removeTo, replacementText) {
8382
7980
  return JSON.stringify([
@@ -8400,7 +7998,7 @@ function clearNoopLoop(absolutePath) {
8400
7998
  }
8401
7999
 
8402
8000
  //#endregion
8403
- //#region src/url-schemes/vendored/hashline/edit-engine.js
8001
+ //#region src/hashline/edit-engine.js
8404
8002
  /** The hashes a range edit removes, for stable re-hash bookkeeping. */
8405
8003
  function collectRemovedHashes(edit, originalHashes) {
8406
8004
  const removedHashes = /* @__PURE__ */ new Set();
@@ -8735,7 +8333,7 @@ async function persistUndoAndWrite(opts) {
8735
8333
  }
8736
8334
 
8737
8335
  //#endregion
8738
- //#region src/url-schemes/vendored/hashline/edit-response.js
8336
+ //#region src/hashline/edit-response.js
8739
8337
  function buildMetrics(args) {
8740
8338
  const metrics = {
8741
8339
  edits_attempted: args.editsAttempted,
@@ -8826,7 +8424,7 @@ function buildBatchResult(sections) {
8826
8424
  }
8827
8425
 
8828
8426
  //#endregion
8829
- //#region src/url-schemes/vendored/hashline/mutation.js
8427
+ //#region src/hashline/mutation.js
8830
8428
  /**
8831
8429
  * Deep seam: execute the full mutation lifecycle.
8832
8430
  *
@@ -8929,7 +8527,7 @@ async function commit(opts) {
8929
8527
  }
8930
8528
 
8931
8529
  //#endregion
8932
- //#region src/url-schemes/vendored/hashline/prompts.js
8530
+ //#region src/hashline/prompts.js
8933
8531
  /**
8934
8532
  * Model-facing prompt text for the hashline tools, embedded so the bundle
8935
8533
  * ships no external prompt files. Each tool's schema `description` is short;
@@ -8964,7 +8562,7 @@ const UNDO_GUIDANCE = {
8964
8562
  };
8965
8563
 
8966
8564
  //#endregion
8967
- //#region src/url-schemes/vendored/hashline/tool-edit.js
8565
+ //#region src/hashline/tool-edit.js
8968
8566
  async function resolveNullPath(edits) {
8969
8567
  if (edits.length === 0) return void 0;
8970
8568
  const first = edits[0];
@@ -9064,7 +8662,7 @@ function registerEditTool(_rootCtx, agentCtx, io, sandbox) {
9064
8662
  }
9065
8663
 
9066
8664
  //#endregion
9067
- //#region src/url-schemes/vendored/hashline/tool-undo.js
8665
+ //#region src/hashline/tool-undo.js
9068
8666
  /**
9069
8667
  * Register the `undo_last_edit` tool on the calling agent's scope.
9070
8668
  * @param _rootCtx - host context.
@@ -9164,7 +8762,7 @@ function registerUndoTool(_rootCtx, agentCtx, io, sandbox) {
9164
8762
  }
9165
8763
 
9166
8764
  //#endregion
9167
- //#region src/url-schemes/vendored/hashline/guidance/parse.js
8765
+ //#region src/hashline/guidance/parse.js
9168
8766
  /**
9169
8767
  * Pure parser for per-preset guidance override files.
9170
8768
  *
@@ -9235,7 +8833,7 @@ function isBlankOverride(content) {
9235
8833
  }
9236
8834
 
9237
8835
  //#endregion
9238
- //#region src/url-schemes/vendored/hashline/guidance/resolve.js
8836
+ //#region src/hashline/guidance/resolve.js
9239
8837
  /**
9240
8838
  * Render one tool's guidance as its intro line, a blank line, then bullets.
9241
8839
  * Uniform across the four sections: no tool-schema description is duplicated
@@ -9333,7 +8931,7 @@ async function composeSections(presetId, homeDir) {
9333
8931
  }
9334
8932
 
9335
8933
  //#endregion
9336
- //#region src/url-schemes/vendored/hashline/guidance/materialize.js
8934
+ //#region src/hashline/guidance/materialize.js
9337
8935
  /** The presets shipped by the harness, each seeded with editable guidance. */
9338
8936
  const DEFAULT_PRESETS = [
9339
8937
  "standard",
@@ -9508,6 +9106,138 @@ async function ensurePresetGuidance(homeDir) {
9508
9106
  }));
9509
9107
  }
9510
9108
 
9109
+ //#endregion
9110
+ //#region src/hashline/install.ts
9111
+ /**
9112
+ * One-time process init: warm the hasher and materialize the editable
9113
+ * per-preset guidance overrides (idempotent; failures are noise, never a
9114
+ * failed boot).
9115
+ */
9116
+ function initHashlineRuntime() {
9117
+ initHasher().catch(() => {});
9118
+ ensurePresetGuidance(configDir()).catch(() => {});
9119
+ }
9120
+ /**
9121
+ * The hashline `read` doer: every path is a filesystem path run through the
9122
+ * anchored read-and-serve pipeline (`HASH│content` anchors + served-row store
9123
+ * that the `edit` tool consumes). No scheme knowledge — scheme-aware wrapping
9124
+ * is the caller's business (composition root chains this under its wrapper;
9125
+ * standalone callers register it as `read` directly).
9126
+ */
9127
+ function createHashlineReadTool(deps) {
9128
+ const { io } = deps;
9129
+ return defineTool({
9130
+ name: "read",
9131
+ description: "Read a text file (each line returned as `HASH│content` with a 3-char hash anchor for later edit calls). File reads page with offset/limit.",
9132
+ parameters: {
9133
+ path: {
9134
+ type: "string",
9135
+ description: "File path (hashline-anchored read)."
9136
+ },
9137
+ offset: {
9138
+ type: "number",
9139
+ description: "Line number to start reading from (1-indexed, file reads only)"
9140
+ },
9141
+ limit: {
9142
+ type: "number",
9143
+ description: "Maximum number of lines to read (file reads only)"
9144
+ }
9145
+ },
9146
+ output: {
9147
+ schema: { type: "string" },
9148
+ render: (_args, value) => [{
9149
+ type: "text",
9150
+ text: value
9151
+ }]
9152
+ },
9153
+ async execute(args, exec) {
9154
+ const canonical = normalizeRequest(args);
9155
+ assertReadRequest(canonical);
9156
+ const rawPath = canonical.path;
9157
+ return withWorkspace(execCwd(exec), async () => {
9158
+ const cwd = execCwd(exec);
9159
+ const signal = exec.signal;
9160
+ const { text, absolutePath } = await readAndServe(io, rawPath, cwd, {
9161
+ signal,
9162
+ offset: canonical.offset,
9163
+ limit: canonical.limit
9164
+ });
9165
+ await io.emitObserved(absolutePath, exec, signal);
9166
+ return text;
9167
+ });
9168
+ }
9169
+ });
9170
+ }
9171
+ /**
9172
+ * Register the hashline feature on one agent's own scope layer (async: the
9173
+ * `edit`/`undo` tools, the tool guidance sections (preset-aware, compiled
9174
+ * defaults on failure), and the lsp post-edit diagnostics hook. `read` is NOT
9175
+ * registered here — the caller owns the single `read` registration and should
9176
+ * register {@link createHashlineReadTool}'s output (directly or chained).
9177
+ */
9178
+ async function installHashline(rootCtx, agent, opts = {}) {
9179
+ const disposers = [];
9180
+ const sandbox = opts.sandbox ?? new FsSandboxController(rootCtx);
9181
+ const io = ctxFsIO(rootCtx.fs, rootCtx);
9182
+ const readTool = createHashlineReadTool({ io });
9183
+ disposers.push(registerEditTool(rootCtx, agent.ctx, io, sandbox));
9184
+ disposers.push(registerUndoTool(rootCtx, agent.ctx, io, sandbox));
9185
+ const diagnosticsHook = opts.editFeedback;
9186
+ if (diagnosticsHook !== void 0) disposers.push(agent.ctx.on("tools/post-execute", async (exec, result, next) => {
9187
+ const decision = await next();
9188
+ if (exec.name !== "edit" || result.isError) return decision;
9189
+ const rawPath = exec.arguments?.path;
9190
+ if (typeof rawPath !== "string" || rawPath === "") return decision;
9191
+ try {
9192
+ const content = await io.readText(rawPath, exec.signal);
9193
+ if (typeof content !== "string") return decision;
9194
+ const summary = await diagnosticsHook(rawPath, content);
9195
+ if (summary === void 0) return decision;
9196
+ const decisionRecord = decision;
9197
+ if (decisionRecord.kind !== "accept") return decision;
9198
+ decisionRecord.content = [...decisionRecord.content ?? result.content ?? [], {
9199
+ type: "text",
9200
+ text: `\n${summary}`
9201
+ }];
9202
+ return decision;
9203
+ } catch {
9204
+ return decision;
9205
+ }
9206
+ }));
9207
+ try {
9208
+ const agentPresets = rootCtx.get("agentPresets");
9209
+ let sections = GUIDANCE_SECTIONS.map((section) => ({
9210
+ name: section.name,
9211
+ order: section.defaultOrder,
9212
+ text: section.renderDefault()
9213
+ }));
9214
+ if (agentPresets !== void 0) try {
9215
+ sections = (await composeSections(agentPresets.composedPreset(agent.ctx), configDir())).map((section) => ({
9216
+ name: section.name,
9217
+ order: section.order,
9218
+ text: section.text
9219
+ }));
9220
+ } catch {}
9221
+ for (const section of sections) disposers.push(agent.ctx.systemPrompt.section(section));
9222
+ } catch {}
9223
+ return {
9224
+ disposers,
9225
+ io,
9226
+ sandbox,
9227
+ readTool
9228
+ };
9229
+ }
9230
+
9231
+ //#endregion
9232
+ //#region src/url-schemes/gates.ts
9233
+ /** Every key defaults on — the service is opt-out, not opt-in. */
9234
+ function resolveGates(config) {
9235
+ return {
9236
+ urlSchemes: config?.urlSchemes !== false,
9237
+ hashline: config?.hashline !== false
9238
+ };
9239
+ }
9240
+
9511
9241
  //#endregion
9512
9242
  //#region src/url-schemes/index.ts
9513
9243
  /** Cordis plugin name. */
@@ -9529,13 +9259,6 @@ const inject$1 = [
9529
9259
  "agents",
9530
9260
  "sessionPersistence"
9531
9261
  ];
9532
- /** Every key defaults on — the service is opt-out, not opt-in. */
9533
- function resolveGates(config) {
9534
- return {
9535
- urlSchemes: config?.urlSchemes !== false,
9536
- hashline: config?.hashline !== false
9537
- };
9538
- }
9539
9262
  /** Register the four URL-aware tools on one agent's own scope layer. */
9540
9263
  /**
9541
9264
  * The lsp feedback loop (native-tools Wave3): both hooks route through the
@@ -9588,71 +9311,39 @@ function buildLspWriteFeedback() {
9588
9311
  postWrite: (filePath, content) => lspFeedback("diagnostics", filePath, content)
9589
9312
  };
9590
9313
  }
9591
- function installAgentTools(rootCtx, agent, resolver, gates) {
9314
+ async function installAgentTools(rootCtx, agent, resolver, gates$1) {
9592
9315
  agent.ctx.effect(async () => {
9593
9316
  const native = captureNativeTools(rootCtx, agent);
9594
9317
  const disposers = [];
9595
9318
  const hashlineSandbox = new FsSandboxController(rootCtx);
9596
- const hashlineIo = ctxFsIO(rootCtx.fs, rootCtx);
9597
- if (gates.hashline) disposers.push(agent.ctx.tools.register(createReadTool({
9598
- resolver,
9599
- fs: rootCtx.fs,
9600
- ctx: rootCtx,
9601
- gates,
9602
- capturedRead: native.read
9603
- })));
9604
- if (gates.urlSchemes) disposers.push(agent.ctx.tools.register(createWriteTool({
9319
+ let readDelegate = native.read;
9320
+ if (gates$1.hashline) {
9321
+ const hashline = await installHashline(rootCtx, agent, {
9322
+ sandbox: hashlineSandbox,
9323
+ editFeedback: buildLspWriteFeedback().postWrite
9324
+ });
9325
+ disposers.push(...hashline.disposers);
9326
+ readDelegate = hashline.readTool;
9327
+ }
9328
+ if (gates$1.urlSchemes || gates$1.hashline) {
9329
+ const readTool = gates$1.urlSchemes && readDelegate !== void 0 ? createSchemeReadTool({
9330
+ resolver,
9331
+ capturedRead: readDelegate
9332
+ }) : readDelegate;
9333
+ if (readTool !== void 0) disposers.push(agent.ctx.tools.register(readTool));
9334
+ }
9335
+ if (gates$1.urlSchemes) disposers.push(agent.ctx.tools.register(createWriteTool({
9605
9336
  nativeWrite: native.write,
9606
9337
  sandbox: hashlineSandbox,
9607
9338
  ...buildLspWriteFeedback()
9608
9339
  })));
9609
- if (gates.hashline) {
9610
- disposers.push(registerEditTool(rootCtx, agent.ctx, hashlineIo, hashlineSandbox));
9611
- disposers.push(registerUndoTool(rootCtx, agent.ctx, hashlineIo, hashlineSandbox));
9612
- }
9613
- if (gates.hashline) {
9614
- const diagnosticsHook = buildLspWriteFeedback().postWrite;
9615
- disposers.push(agent.ctx.on("tools/post-execute", async (exec, result, next) => {
9616
- const decision = await next();
9617
- if (exec.name !== "edit" || result.isError) return decision;
9618
- const rawPath = exec.arguments?.path;
9619
- if (typeof rawPath !== "string" || rawPath === "") return decision;
9620
- try {
9621
- const content = await hashlineIo.readText(rawPath, exec.signal);
9622
- if (typeof content !== "string") return decision;
9623
- const summary = await diagnosticsHook(rawPath, content);
9624
- if (summary === void 0) return decision;
9625
- const decisionRecord = decision;
9626
- if (decisionRecord.kind !== "accept") return decision;
9627
- decisionRecord.content = [...decisionRecord.content ?? result.content ?? [], {
9628
- type: "text",
9629
- text: `\n${summary}`
9630
- }];
9631
- return decision;
9632
- } catch {
9633
- return decision;
9634
- }
9635
- }));
9636
- }
9637
9340
  try {
9638
- const agentPresets = rootCtx.get("agentPresets");
9639
- let sections = GUIDANCE_SECTIONS.map((section) => ({
9640
- name: section.name,
9641
- order: section.defaultOrder,
9642
- text: section.renderDefault()
9643
- }));
9644
- if (agentPresets !== void 0) try {
9645
- sections = (await composeSections(agentPresets.composedPreset(agent.ctx), configDir())).map((section) => ({
9646
- name: section.name,
9647
- order: section.order,
9648
- text: section.text
9649
- }));
9650
- } catch {}
9651
- for (const section of sections) disposers.push(agent.ctx.systemPrompt.section(section));
9652
- const general = generalSection(gates);
9653
- if (general !== void 0) disposers.push(agent.ctx.systemPrompt.section(general));
9341
+ if (gates$1.urlSchemes) {
9342
+ const general = generalSection(gates$1);
9343
+ if (general !== void 0) disposers.push(agent.ctx.systemPrompt.section(general));
9344
+ }
9654
9345
  } catch {}
9655
- if (gates.urlSchemes) {
9346
+ if (gates$1.urlSchemes) {
9656
9347
  disposers.push(agent.ctx.tools.register(createGrepTool({
9657
9348
  resolver,
9658
9349
  nativeGrep: native.grep
@@ -9662,6 +9353,31 @@ function installAgentTools(rootCtx, agent, resolver, gates) {
9662
9353
  nativeGlob: native.glob
9663
9354
  })));
9664
9355
  }
9356
+ const sessionId = agent.id;
9357
+ disposers.push(agent.ctx.on("tools/post-execute", async (exec, result, next) => {
9358
+ const decision = await next();
9359
+ const name$2 = exec.name;
9360
+ if (name$2 !== "read" && name$2 !== "edit" && name$2 !== "write") return decision;
9361
+ const args = exec.arguments;
9362
+ const filePath = args?.path ?? args?.file_path;
9363
+ if (typeof filePath !== "string" || filePath === "") return decision;
9364
+ if (name$2 === "read") {
9365
+ lspGateSyncOnLand(sessionId, "read", filePath);
9366
+ return decision;
9367
+ }
9368
+ lspGateSyncOnLand(sessionId, name$2, filePath);
9369
+ if (result.isError) return decision;
9370
+ const notice = lspGateNotice(sessionId, filePath);
9371
+ if (notice === void 0) return decision;
9372
+ const record = decision;
9373
+ if (record.kind !== "accept") return decision;
9374
+ record.content = [...record.content ?? result.content ?? [], {
9375
+ type: "text",
9376
+ text: `\n${notice}`
9377
+ }];
9378
+ return decision;
9379
+ }));
9380
+ disposers.push(() => disposeLspGate(sessionId));
9665
9381
  return () => {
9666
9382
  for (const dispose of disposers) dispose();
9667
9383
  };
@@ -9669,8 +9385,7 @@ function installAgentTools(rootCtx, agent, resolver, gates) {
9669
9385
  }
9670
9386
  /** Mount the resolver + scheme handlers, then install the tools per agent. */
9671
9387
  function apply$1(ctx, config) {
9672
- initHasher().catch(() => {});
9673
- ensurePresetGuidance(configDir()).catch(() => {});
9388
+ initHashlineRuntime();
9674
9389
  const resolver = new UrlResolver();
9675
9390
  if (resolveGates(config).urlSchemes && ctx.fs) try {
9676
9391
  wrapFsWithSchemes(ctx.fs, { settings: ctx.settings });
@@ -9702,11 +9417,9 @@ function apply$1(ctx, config) {
9702
9417
  ctx.on("agent/session-start", ({ agent }) => {
9703
9418
  if (registered.has(agent)) return;
9704
9419
  registered.add(agent);
9705
- try {
9706
- installAgentTools(ctx, agent, resolver, resolveGates(config));
9707
- } catch (error) {
9420
+ installAgentTools(ctx, agent, resolver, resolveGates(config)).catch((error) => {
9708
9421
  ctx.logger("dsh-url-schemes").warn(`failed to install URL-aware tools for agent ${agent.id}: ${error instanceof Error ? error.message : String(error)}`);
9709
- }
9422
+ });
9710
9423
  });
9711
9424
  }
9712
9425
  var url_schemes_default = {
@@ -10400,7 +10113,7 @@ function splitRoute(route) {
10400
10113
  //#endregion
10401
10114
  //#region src/failover/index.ts
10402
10115
  /** The `failover` settings schema: two optional fallback slots, both default "not set". */
10403
- const Config$1 = z.object({
10116
+ const Config$2 = z.object({
10404
10117
  fallback1: z.string().default(""),
10405
10118
  fallback2: z.string().default("")
10406
10119
  });
@@ -10416,9 +10129,9 @@ function overrideConfig(seed, to) {
10416
10129
  /** Install the failover. A no-op composition degrades gracefully (empty chain = pass-through). */
10417
10130
  function installFailover(ctx, config = defaultFailoverConfig) {
10418
10131
  const logger = ctx.logger("llm-failover");
10419
- let source = () => Config$1(config);
10132
+ let source = () => Config$2(config);
10420
10133
  ctx.inject(["settings"], (sctx) => {
10421
- sctx.settings.installSection(ctx, FAILOVER_SETTINGS_NS, Config$1, Config$1(config), {
10134
+ sctx.settings.installSection(ctx, FAILOVER_SETTINGS_NS, Config$2, Config$2(config), {
10422
10135
  setSource: (current$1) => {
10423
10136
  source = current$1;
10424
10137
  },
@@ -10472,6 +10185,143 @@ function installFailover(ctx, config = defaultFailoverConfig) {
10472
10185
  });
10473
10186
  }
10474
10187
 
10188
+ //#endregion
10189
+ //#region src/compaction/config.ts
10190
+ /**
10191
+ * The `compaction-tuning` General-row config: the share of the routed context
10192
+ * window at which automatic condensation starts.
10193
+ *
10194
+ * Pure module — no `@deepseek-ai/*` value imports — so the host half and the
10195
+ * client half may both import it without dragging either runtime into the
10196
+ * browser bundle: the host takes the namespace and the schema defaults, the
10197
+ * client takes the namespace literal plus the stepper bounds.
10198
+ *
10199
+ * @module dashr/compaction/config
10200
+ */
10201
+ /** The settings namespace the General row reads and writes. */
10202
+ const COMPACTION_SETTINGS_NS = "compaction-tuning";
10203
+ /** Upstream `compaction-basic`'s own default: condense at 80% of the window. */
10204
+ const DEFAULT_THRESHOLD_RATIO = .8;
10205
+ /**
10206
+ * Absolute recent-tail budget, in estimated tokens (~50 KB of English text).
10207
+ *
10208
+ * Composition-only, deliberately: the General row drives `thresholdRatio`
10209
+ * alone, so the recency window stays a deployment decision (its base layer is
10210
+ * the row config in `cordis.patch.yml`) rather than a second knob on the
10211
+ * settings page. The host half still applies it to the engine.
10212
+ */
10213
+ const DEFAULT_RETAIN_TOKENS = 5e4;
10214
+ /** Ratio bounds the settings schema accepts (a config file may use either end). */
10215
+ const THRESHOLD_MIN_RATIO = .05;
10216
+ /** @see THRESHOLD_MIN_RATIO */
10217
+ const THRESHOLD_MAX_RATIO = 1;
10218
+ /** Spec defaults — `Config({})` must equal this. */
10219
+ const defaultCompactionTuningConfig = {
10220
+ thresholdRatio: DEFAULT_THRESHOLD_RATIO,
10221
+ retainTokens: DEFAULT_RETAIN_TOKENS
10222
+ };
10223
+
10224
+ //#endregion
10225
+ //#region src/compaction/index.ts
10226
+ /**
10227
+ * The `compaction-tuning` settings schema.
10228
+ *
10229
+ * `thresholdRatio` is the field the General row drives; `retainTokens` is
10230
+ * composition-only (see {@link DEFAULT_RETAIN_TOKENS}) but still validated and
10231
+ * applied, so the recency window keeps one source of truth.
10232
+ */
10233
+ const Config$1 = z.object({
10234
+ thresholdRatio: z.number().step(.01).min(THRESHOLD_MIN_RATIO).max(THRESHOLD_MAX_RATIO).default(DEFAULT_THRESHOLD_RATIO),
10235
+ retainTokens: z.number().step(1).min(0).default(DEFAULT_RETAIN_TOKENS)
10236
+ });
10237
+ /**
10238
+ * Install the settings namespace and keep the live compaction engine in sync.
10239
+ * @param ctx - host context of the row that owns this capability.
10240
+ * @param config - composition base layer; schema defaults fill the rest.
10241
+ */
10242
+ function installCompactionTuning(ctx, config = defaultCompactionTuningConfig) {
10243
+ const logger = ctx.logger("compaction-tuning");
10244
+ let source = () => Config$1(config);
10245
+ /** Smallest routed context window seen so far (advisory guard, best effort). */
10246
+ let minWindow;
10247
+ let probing = false;
10248
+ /**
10249
+ * Learn the routed context windows so a write can be refused when it would
10250
+ * leave `retainTokens >= floor(window x thresholdRatio)` — the condition
10251
+ * under which the engine raises `TargetPressureConfigError` per route and
10252
+ * automatic condensation quietly stops for that route.
10253
+ */
10254
+ const probeWindows = () => {
10255
+ if (probing || minWindow !== void 0) return;
10256
+ const llm = ctx.get("llm");
10257
+ if (llm === void 0) return;
10258
+ probing = true;
10259
+ (async () => {
10260
+ try {
10261
+ let smallest;
10262
+ for (const provider of llm.listProviders()) for (const model of await llm.listModels(provider.id)) {
10263
+ const window = (await llm.resolveModelInfo(provider.id, model.id)).context?.contextWindow;
10264
+ if (typeof window !== "number" || window <= 0) continue;
10265
+ smallest = smallest === void 0 ? window : Math.min(smallest, window);
10266
+ }
10267
+ if (smallest !== void 0) {
10268
+ minWindow = smallest;
10269
+ logger.info(`smallest routed context window: ${smallest} tokens`);
10270
+ }
10271
+ } catch (error) {
10272
+ logger.warn(`context-window probe failed (retention guard disabled): ${error instanceof Error ? error.message : String(error)}`);
10273
+ } finally {
10274
+ probing = false;
10275
+ }
10276
+ })();
10277
+ };
10278
+ /**
10279
+ * Overwrite only this module's two fields on the engine's current policy and
10280
+ * hand the engine a fresh frozen object.
10281
+ * @param reason - short diagnostic tag for the log line.
10282
+ */
10283
+ const push = (reason) => {
10284
+ const compaction = ctx.get("compaction");
10285
+ if (compaction === void 0) return;
10286
+ const base = compaction.config;
10287
+ if (base === void 0) return;
10288
+ const value = source();
10289
+ const next = {
10290
+ ...base,
10291
+ thresholdRatio: value.thresholdRatio,
10292
+ retainTokens: value.retainTokens
10293
+ };
10294
+ delete next.retainRatio;
10295
+ compaction.config = Object.freeze(next);
10296
+ logger.info(`applied (${reason}): thresholdRatio=${value.thresholdRatio} retainTokens=${value.retainTokens}`);
10297
+ };
10298
+ ctx.inject(["compaction"], () => {
10299
+ push("compaction-ready");
10300
+ probeWindows();
10301
+ });
10302
+ ctx.inject(["settings"], (settingsCtx) => {
10303
+ if (settingsCtx.settings.get(COMPACTION_SETTINGS_NS) !== void 0) {
10304
+ logger.info("settings namespace already served; leaving it to its owner");
10305
+ return;
10306
+ }
10307
+ settingsCtx.settings.installSection(ctx, COMPACTION_SETTINGS_NS, Config$1, Config$1(config), {
10308
+ setSource: (current$1) => {
10309
+ source = current$1;
10310
+ },
10311
+ onChange: () => {
10312
+ push("settings-updated");
10313
+ },
10314
+ validate: (value) => {
10315
+ if (minWindow === void 0) return;
10316
+ const thresholdTokens = Math.floor(minWindow * value.thresholdRatio);
10317
+ if (value.retainTokens >= thresholdTokens) throw new Error(`retainTokens (${value.retainTokens}) must stay below the threshold tokens for every routed model; the smallest window here is ${minWindow}, so ${value.thresholdRatio} leaves only ${thresholdTokens}`);
10318
+ }
10319
+ });
10320
+ push("settings-installed");
10321
+ probeWindows();
10322
+ });
10323
+ }
10324
+
10475
10325
  //#endregion
10476
10326
  //#region src/mobile/zoom-guard.ts
10477
10327
  /**
@@ -11391,6 +11241,7 @@ function apply(ctx, config) {
11391
11241
  ctx.plugin(DashrRuntime, pickRuntimeConfig(config));
11392
11242
  ctx.plugin(url_schemes_default, config);
11393
11243
  installFailover(ctx);
11244
+ installCompactionTuning(ctx);
11394
11245
  installWebTrust(ctx, config);
11395
11246
  const logger = ctx.logger("dashr-repl");
11396
11247
  const maxParallel = resolveMaxParallelSubCalls(config.maxParallelSubCalls);