dsh-over-ssh 0.1.0 → 0.1.1

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 (247) hide show
  1. package/README.md +10 -10
  2. package/agents/agent-manifest.json +1 -1
  3. package/lib/client.js +363 -314
  4. package/lib/file-references.js +80 -35
  5. package/lib/fs.js +158 -75
  6. package/lib/index.js +4103 -3397
  7. package/lib/terminal-wrapper.js +9 -4
  8. package/lib/types/src/config.d.ts +80 -9
  9. package/lib/types/src/file-references.d.ts +0 -1
  10. package/lib/types/src/host/app/backend.d.ts +9 -1
  11. package/lib/types/src/host/app/recycle-bin/recycle-bin-contract.d.ts +0 -1
  12. package/lib/types/src/host/app/recycle-bin/recycle-bin-service.d.ts +0 -1
  13. package/lib/types/src/host/app/session-admin/session-admin-contract.d.ts +12 -1
  14. package/lib/types/src/host/app/session-admin/session-admin-service.d.ts +0 -1
  15. package/lib/types/src/host/app/transport/transport-selector.d.ts +0 -1
  16. package/lib/types/src/host/app/transport/transport-service.d.ts +0 -1
  17. package/lib/types/src/host/domain/capability.d.ts +0 -1
  18. package/lib/types/src/host/domain/errors.d.ts +0 -1
  19. package/lib/types/src/host/domain/roots/connection.d.ts +0 -1
  20. package/lib/types/src/host/domain/roots/root.d.ts +24 -9
  21. package/lib/types/src/host/domain/roots/session-roots.d.ts +0 -1
  22. package/lib/types/src/host/domain/roots/ssh-config.d.ts +0 -1
  23. package/lib/types/src/host/domain/session/session-admin.d.ts +0 -1
  24. package/lib/types/src/host/domain/transfer/recycle-bin.d.ts +0 -1
  25. package/lib/types/src/host/domain/transfer/transfer.d.ts +0 -1
  26. package/lib/types/src/host/infra/transport/agent/agent-installer.d.ts +0 -1
  27. package/lib/types/src/host/infra/transport/ssh/ssh2-transport.d.ts +0 -1
  28. package/lib/types/src/host/infra/transport/transport-base.d.ts +0 -1
  29. package/lib/types/src/host/infra/transport/transport.d.ts +16 -2
  30. package/lib/types/src/host/integration/agent/agent-preflight.d.ts +0 -1
  31. package/lib/types/src/host/integration/fs/file-reference-provider.d.ts +0 -1
  32. package/lib/types/src/host/integration/fs/fs-provider.d.ts +0 -2
  33. package/lib/types/src/host/integration/transfer/transfer-service.d.ts +0 -1
  34. package/lib/types/src/index.d.ts +23 -1
  35. package/lib/types/src/local-bridge-refs.d.ts +0 -1
  36. package/lib/types/src/local-bridge.d.ts +0 -1
  37. package/lib/types/src/shared/root-keys.d.ts +120 -0
  38. package/lib/types/src/wiring/helpers.d.ts +0 -1
  39. package/package.json +5 -3
  40. package/lib/types/src/client/controls.d.ts +0 -131
  41. package/lib/types/src/client/core/card-focus-trap.d.ts +0 -45
  42. package/lib/types/src/client/core/controls.d.ts +0 -164
  43. package/lib/types/src/client/core/latest.d.ts +0 -48
  44. package/lib/types/src/client/core/locale.d.ts +0 -38
  45. package/lib/types/src/client/core/message.d.ts +0 -37
  46. package/lib/types/src/client/core/panel-model.d.ts +0 -134
  47. package/lib/types/src/client/core/portal.d.ts +0 -266
  48. package/lib/types/src/client/core/remote.d.ts +0 -84
  49. package/lib/types/src/client/core/styles.d.ts +0 -458
  50. package/lib/types/src/client/file-tree-io.d.ts +0 -19
  51. package/lib/types/src/client/file-tree.d.ts +0 -271
  52. package/lib/types/src/client/flows/directory-browser.d.ts +0 -587
  53. package/lib/types/src/client/flows/session-directory.d.ts +0 -415
  54. package/lib/types/src/client/flows/workspace-directory-flow.d.ts +0 -480
  55. package/lib/types/src/client/locale.d.ts +0 -31
  56. package/lib/types/src/client/panel-model.d.ts +0 -37
  57. package/lib/types/src/client/panel.d.ts +0 -77
  58. package/lib/types/src/client/roots/connection-test.d.ts +0 -69
  59. package/lib/types/src/client/roots/roots-panel-state.d.ts +0 -307
  60. package/lib/types/src/client/roots/roots-panel-view.d.ts +0 -280
  61. package/lib/types/src/client/roots/roots-panel.d.ts +0 -364
  62. package/lib/types/src/client/roots/transfer-panel-view.d.ts +0 -35
  63. package/lib/types/src/client/roots/transfer-panel.d.ts +0 -241
  64. package/lib/types/src/client/roots/transfer-progress.d.ts +0 -67
  65. package/lib/types/src/client/roots-panel-view.d.ts +0 -236
  66. package/lib/types/src/client/roots-panel.d.ts +0 -347
  67. package/lib/types/src/client/session/session-panel-view.d.ts +0 -336
  68. package/lib/types/src/client/session/session-panel.d.ts +0 -322
  69. package/lib/types/src/client/session/session-remote-mark.d.ts +0 -110
  70. package/lib/types/src/client/session/session-row-actions.d.ts +0 -398
  71. package/lib/types/src/client/session-panel.d.ts +0 -152
  72. package/lib/types/src/client/styles.d.ts +0 -252
  73. package/lib/types/src/client/workspace-directory-flow.d.ts +0 -342
  74. package/lib/types/src/fs.d.ts +0 -46
  75. package/lib/types/src/host/app/recycle-bin/recycle-bin-move.d.ts +0 -18
  76. package/lib/types/src/host/app/recycle-bin/recycle-bin-world.d.ts +0 -38
  77. package/lib/types/src/host/app/recycle-bin-contract.d.ts +0 -215
  78. package/lib/types/src/host/app/recycle-bin-move.d.ts +0 -18
  79. package/lib/types/src/host/app/recycle-bin-service.d.ts +0 -45
  80. package/lib/types/src/host/app/recycle-bin-world.d.ts +0 -35
  81. package/lib/types/src/host/app/roots/roots-api-connections.d.ts +0 -49
  82. package/lib/types/src/host/app/roots/roots-api-contract.d.ts +0 -369
  83. package/lib/types/src/host/app/roots/roots-api-directories.d.ts +0 -13
  84. package/lib/types/src/host/app/roots/roots-api-http.d.ts +0 -77
  85. package/lib/types/src/host/app/roots/roots-api-long-poll.d.ts +0 -130
  86. package/lib/types/src/host/app/roots/roots-api-projections.d.ts +0 -102
  87. package/lib/types/src/host/app/roots/roots-api-roots.d.ts +0 -13
  88. package/lib/types/src/host/app/roots/roots-api-state.d.ts +0 -22
  89. package/lib/types/src/host/app/roots/roots-api-transfers.d.ts +0 -26
  90. package/lib/types/src/host/app/roots/roots-api.d.ts +0 -55
  91. package/lib/types/src/host/app/roots/user-roots.d.ts +0 -552
  92. package/lib/types/src/host/app/roots-api-connections.d.ts +0 -49
  93. package/lib/types/src/host/app/roots-api-contract.d.ts +0 -339
  94. package/lib/types/src/host/app/roots-api-directories.d.ts +0 -13
  95. package/lib/types/src/host/app/roots-api-http.d.ts +0 -77
  96. package/lib/types/src/host/app/roots-api-projections.d.ts +0 -102
  97. package/lib/types/src/host/app/roots-api-roots.d.ts +0 -13
  98. package/lib/types/src/host/app/roots-api-state.d.ts +0 -22
  99. package/lib/types/src/host/app/roots-api-support.d.ts +0 -182
  100. package/lib/types/src/host/app/roots-api-transfers.d.ts +0 -13
  101. package/lib/types/src/host/app/roots-api.d.ts +0 -55
  102. package/lib/types/src/host/app/routed-fs.d.ts +0 -239
  103. package/lib/types/src/host/app/serialize.d.ts +0 -37
  104. package/lib/types/src/host/app/session-admin/session-admin-context.d.ts +0 -36
  105. package/lib/types/src/host/app/session-admin/session-admin-delete.d.ts +0 -34
  106. package/lib/types/src/host/app/session-admin-context.d.ts +0 -36
  107. package/lib/types/src/host/app/session-admin-contract.d.ts +0 -214
  108. package/lib/types/src/host/app/session-admin-delete.d.ts +0 -34
  109. package/lib/types/src/host/app/session-admin-service.d.ts +0 -38
  110. package/lib/types/src/host/app/takeover/takeover-plan.d.ts +0 -227
  111. package/lib/types/src/host/app/takeover/takeover-rows.d.ts +0 -55
  112. package/lib/types/src/host/app/transport-selector.d.ts +0 -98
  113. package/lib/types/src/host/app/transport-service.d.ts +0 -75
  114. package/lib/types/src/host/app/user-roots.d.ts +0 -461
  115. package/lib/types/src/host/domain/bin-location.d.ts +0 -79
  116. package/lib/types/src/host/domain/connection.d.ts +0 -95
  117. package/lib/types/src/host/domain/glob-pattern.d.ts +0 -62
  118. package/lib/types/src/host/domain/literal-text.d.ts +0 -63
  119. package/lib/types/src/host/domain/path-grammar.d.ts +0 -84
  120. package/lib/types/src/host/domain/recycle-bin.d.ts +0 -337
  121. package/lib/types/src/host/domain/root.d.ts +0 -74
  122. package/lib/types/src/host/domain/roots/bin-location.d.ts +0 -91
  123. package/lib/types/src/host/domain/roots/route-table.d.ts +0 -102
  124. package/lib/types/src/host/domain/route-table.d.ts +0 -80
  125. package/lib/types/src/host/domain/session/terminal-target.d.ts +0 -136
  126. package/lib/types/src/host/domain/session-admin.d.ts +0 -330
  127. package/lib/types/src/host/domain/shell.d.ts +0 -47
  128. package/lib/types/src/host/domain/ssh-config.d.ts +0 -73
  129. package/lib/types/src/host/domain/terminal-target.d.ts +0 -132
  130. package/lib/types/src/host/domain/tool-requests.d.ts +0 -369
  131. package/lib/types/src/host/domain/transfer.d.ts +0 -260
  132. package/lib/types/src/host/infra/backend/local-backend.d.ts +0 -84
  133. package/lib/types/src/host/infra/backend/local-version.d.ts +0 -43
  134. package/lib/types/src/host/infra/backend/platform-local-backend.d.ts +0 -89
  135. package/lib/types/src/host/infra/backend/remote-backend.d.ts +0 -239
  136. package/lib/types/src/host/infra/cache/lru.d.ts +0 -52
  137. package/lib/types/src/host/infra/cache/versioned.d.ts +0 -77
  138. package/lib/types/src/host/infra/temp-name.d.ts +0 -19
  139. package/lib/types/src/host/infra/transport/agent/agent-protocol-client.d.ts +0 -132
  140. package/lib/types/src/host/infra/transport/agent/agent-protocol-codec.d.ts +0 -140
  141. package/lib/types/src/host/infra/transport/agent/agent-protocol-errors.d.ts +0 -41
  142. package/lib/types/src/host/infra/transport/agent/agent-protocol-frames.d.ts +0 -197
  143. package/lib/types/src/host/infra/transport/agent/agent-protocol-types.d.ts +0 -564
  144. package/lib/types/src/host/infra/transport/agent/agent-transport.d.ts +0 -418
  145. package/lib/types/src/host/infra/transport/agent-installer.d.ts +0 -121
  146. package/lib/types/src/host/infra/transport/agent-protocol-client.d.ts +0 -123
  147. package/lib/types/src/host/infra/transport/agent-protocol-codec.d.ts +0 -131
  148. package/lib/types/src/host/infra/transport/agent-protocol-errors.d.ts +0 -41
  149. package/lib/types/src/host/infra/transport/agent-protocol-frames.d.ts +0 -197
  150. package/lib/types/src/host/infra/transport/agent-protocol-types.d.ts +0 -509
  151. package/lib/types/src/host/infra/transport/agent-transport.d.ts +0 -353
  152. package/lib/types/src/host/infra/transport/alias-transport.d.ts +0 -206
  153. package/lib/types/src/host/infra/transport/askpass-fs.d.ts +0 -20
  154. package/lib/types/src/host/infra/transport/askpass.d.ts +0 -133
  155. package/lib/types/src/host/infra/transport/command-pool.d.ts +0 -61
  156. package/lib/types/src/host/infra/transport/fs-commands.d.ts +0 -140
  157. package/lib/types/src/host/infra/transport/git-status-route.d.ts +0 -63
  158. package/lib/types/src/host/infra/transport/git-status.d.ts +0 -101
  159. package/lib/types/src/host/infra/transport/node-spawn.d.ts +0 -26
  160. package/lib/types/src/host/infra/transport/session-window.d.ts +0 -51
  161. package/lib/types/src/host/infra/transport/shell-quote.d.ts +0 -59
  162. package/lib/types/src/host/infra/transport/ssh/alias-transport.d.ts +0 -206
  163. package/lib/types/src/host/infra/transport/ssh/askpass-fs.d.ts +0 -20
  164. package/lib/types/src/host/infra/transport/ssh/askpass.d.ts +0 -133
  165. package/lib/types/src/host/infra/transport/ssh/shell-quote.d.ts +0 -62
  166. package/lib/types/src/host/infra/transport/ssh/ssh-argv.d.ts +0 -43
  167. package/lib/types/src/host/infra/transport/ssh-argv.d.ts +0 -43
  168. package/lib/types/src/host/infra/transport/ssh2-transport.d.ts +0 -302
  169. package/lib/types/src/host/integration/agent/agent-install-io.d.ts +0 -33
  170. package/lib/types/src/host/integration/agent/askpass-registry.d.ts +0 -66
  171. package/lib/types/src/host/integration/agent-install-io.d.ts +0 -33
  172. package/lib/types/src/host/integration/agent-preflight.d.ts +0 -56
  173. package/lib/types/src/host/integration/anchor-directory.d.ts +0 -56
  174. package/lib/types/src/host/integration/askpass-registry.d.ts +0 -59
  175. package/lib/types/src/host/integration/bin-root-resolver.d.ts +0 -81
  176. package/lib/types/src/host/integration/connection-test.d.ts +0 -73
  177. package/lib/types/src/host/integration/file-reference-provider.d.ts +0 -115
  178. package/lib/types/src/host/integration/fs/git-status-wiring.d.ts +0 -21
  179. package/lib/types/src/host/integration/fs/platform-errors.d.ts +0 -94
  180. package/lib/types/src/host/integration/fs/remote-file-tools.d.ts +0 -369
  181. package/lib/types/src/host/integration/fs/remote-index.d.ts +0 -177
  182. package/lib/types/src/host/integration/fs-provider.d.ts +0 -226
  183. package/lib/types/src/host/integration/lsp/lsp-framing.d.ts +0 -38
  184. package/lib/types/src/host/integration/lsp/lsp-registry.d.ts +0 -67
  185. package/lib/types/src/host/integration/lsp/lsp-session.d.ts +0 -130
  186. package/lib/types/src/host/integration/management-tools.d.ts +0 -64
  187. package/lib/types/src/host/integration/recycle-bin-service.d.ts +0 -247
  188. package/lib/types/src/host/integration/remote/connection-test.d.ts +0 -73
  189. package/lib/types/src/host/integration/remote/remote-face.d.ts +0 -127
  190. package/lib/types/src/host/integration/remote/remote-tool-backend.d.ts +0 -108
  191. package/lib/types/src/host/integration/remote-face.d.ts +0 -127
  192. package/lib/types/src/host/integration/remote-file-tools.d.ts +0 -241
  193. package/lib/types/src/host/integration/remote-home.d.ts +0 -69
  194. package/lib/types/src/host/integration/remote-index.d.ts +0 -165
  195. package/lib/types/src/host/integration/remote-job-tools.d.ts +0 -295
  196. package/lib/types/src/host/integration/remote-tool-backend.d.ts +0 -97
  197. package/lib/types/src/host/integration/remote-tools.d.ts +0 -206
  198. package/lib/types/src/host/integration/root-registry.d.ts +0 -115
  199. package/lib/types/src/host/integration/roots/anchor-directory.d.ts +0 -56
  200. package/lib/types/src/host/integration/roots/bin-root-resolver.d.ts +0 -81
  201. package/lib/types/src/host/integration/roots/remote-home.d.ts +0 -69
  202. package/lib/types/src/host/integration/roots/root-registry.d.ts +0 -144
  203. package/lib/types/src/host/integration/roots/roots-wiring.d.ts +0 -219
  204. package/lib/types/src/host/integration/roots/session-root-index.d.ts +0 -52
  205. package/lib/types/src/host/integration/roots-api.d.ts +0 -407
  206. package/lib/types/src/host/integration/roots-wiring.d.ts +0 -211
  207. package/lib/types/src/host/integration/session/session-api.d.ts +0 -88
  208. package/lib/types/src/host/integration/session/session-bin-lock.d.ts +0 -80
  209. package/lib/types/src/host/integration/session/session-header.d.ts +0 -151
  210. package/lib/types/src/host/integration/session/session-labels.d.ts +0 -56
  211. package/lib/types/src/host/integration/session/session-route-wiring.d.ts +0 -50
  212. package/lib/types/src/host/integration/session-admin-service.d.ts +0 -236
  213. package/lib/types/src/host/integration/session-api.d.ts +0 -81
  214. package/lib/types/src/host/integration/session-bin-lock.d.ts +0 -80
  215. package/lib/types/src/host/integration/session-header.d.ts +0 -118
  216. package/lib/types/src/host/integration/session-labels.d.ts +0 -56
  217. package/lib/types/src/host/integration/session-route-wiring.d.ts +0 -50
  218. package/lib/types/src/host/integration/session-scope.d.ts +0 -113
  219. package/lib/types/src/host/integration/session-tools.d.ts +0 -44
  220. package/lib/types/src/host/integration/strict-arguments.d.ts +0 -79
  221. package/lib/types/src/host/integration/takeover/takeover-api.d.ts +0 -62
  222. package/lib/types/src/host/integration/takeover/takeover-route-wiring.d.ts +0 -60
  223. package/lib/types/src/host/integration/takeover/takeover-service.d.ts +0 -167
  224. package/lib/types/src/host/integration/terminal-manifest.d.ts +0 -94
  225. package/lib/types/src/host/integration/tools/management-tools.d.ts +0 -245
  226. package/lib/types/src/host/integration/tools/remote-job-tools.d.ts +0 -316
  227. package/lib/types/src/host/integration/tools/remote-lsp-tools.d.ts +0 -43
  228. package/lib/types/src/host/integration/tools/remote-tools.d.ts +0 -236
  229. package/lib/types/src/host/integration/tools/session-target.d.ts +0 -52
  230. package/lib/types/src/host/integration/tools/session-tools.d.ts +0 -46
  231. package/lib/types/src/host/integration/tools/strict-arguments.d.ts +0 -79
  232. package/lib/types/src/host/integration/tools/terminal-manifest.d.ts +0 -94
  233. package/lib/types/src/host/integration/transfer/remote-copy.d.ts +0 -66
  234. package/lib/types/src/host/integration/transfer/transfer-adapters.d.ts +0 -48
  235. package/lib/types/src/host/integration/transfer/transfer-tracker.d.ts +0 -106
  236. package/lib/types/src/host/integration/transfer-adapters.d.ts +0 -44
  237. package/lib/types/src/host/integration/transfer-service.d.ts +0 -164
  238. package/lib/types/src/host/integration/transfer-tracker.d.ts +0 -106
  239. package/lib/types/src/host/integration/user-roots.d.ts +0 -461
  240. package/lib/types/src/wiring/connections.d.ts +0 -189
  241. package/lib/types/src/wiring/fs.d.ts +0 -13
  242. package/lib/types/src/wiring/install-memo.d.ts +0 -32
  243. package/lib/types/src/wiring/legacy-dirs.d.ts +0 -62
  244. package/lib/types/src/wiring/package-paths.d.ts +0 -35
  245. package/lib/types/src/wiring/roots.d.ts +0 -21
  246. package/lib/types/src/wiring/state-dir.d.ts +0 -29
  247. package/lib/types/src/wiring/tools.d.ts +0 -35
@@ -1,369 +0,0 @@
1
- /**
2
- * Reading and writing one remote file: `remote_read`, `remote_write`, `remote_edit`.
3
- *
4
- * The pain these close (audit 0053): the only way to look at a remote file was `cat` through `remote_exec`,
5
- * which inherited that tool's 64 KiB per-stream truncation and printed no line numbers — and the only way to
6
- * change one was to run `python`/`sed` through `remote_exec`, which is how a `\n` in a patch string turned
7
- * into a real newline and produced an unterminated string literal that then needed a second repair.
8
- *
9
- * Three decisions worth naming:
10
- *
11
- * - **`remote_read` numbers its lines.** A window is rendered as `${number}: ${text}`, so a caller can name
12
- * a line in a follow-up edit instead of guessing from context. `offset`/`limit` are **lines**, and the
13
- * footer states what was shown and what to ask for next.
14
- * - **`remote_edit` replaces a LITERAL string.** `old` containing the two characters `\` and `n` matches
15
- * exactly those two characters, never a newline; the replacement is spliced in by index, so `$&`, `` $` ``,
16
- * `` $' `` and `$$` inside `new` are inserted verbatim rather than being interpreted (verified:
17
- * `String.prototype.replace` with a string replacement expands all four — `.verify/probe-replace-semantics.mjs`).
18
- * A default edit must match **exactly once**; a non-unique match is refused with the occurrence count, and
19
- * `all: true` is the caller's explicit opt-in to replace every occurrence.
20
- * - **`remote_write` and `remote_edit` are guarded by a version token.** `expectedVersion` comes from a
21
- * previous `remote_read`; when it does not match, the write is refused instead of clobbering an edit
22
- * somebody else made in between. The comparison is done by the backend, which knows how each tier spells
23
- * its version (the Tier 2 agent reports `size:mtimeNs`, the ssh transports report `size:seconds` and
24
- * verify a digest), so the tool never has to assume one spelling.
25
- *
26
- * Every path goes through `resolveUnderRoot`, the same one rule the rest of the tool family uses: an
27
- * absolute path outside the root is refused by name, and a relative one is resolved against the root.
28
- *
29
- * @module dsh-over-ssh/integration/remote-file-tools
30
- */
31
- import type { Context } from '@deepseek-ai/cordis';
32
- import type { ContentBlock } from '@deepseek-ai/dsh-llm';
33
- import { type ToolDefinition, type ToolRunContext } from '@deepseek-ai/dsh-tools';
34
- import type { Backend } from '../../app/backend.ts';
35
- import { type SessionRootOutcome, type SessionRootTarget } from '../../domain/roots/session-roots.ts';
36
- /** Default number of lines one read returns. */
37
- export declare const DEFAULT_READ_LINES = 2000;
38
- /** Hard cap on the number of lines one read returns. */
39
- export declare const MAX_READ_LINES = 5000;
40
- /** Default maximum characters returned for a single line. */
41
- export declare const DEFAULT_MAX_LINE_LENGTH = 2000;
42
- /**
43
- * Byte cap applied to the whole file read that feeds a window.
44
- *
45
- * Pinned to the transport's own default read cap (`DEFAULT_MAX_READ_BYTES`): asking for more would only make
46
- * the transport truncate instead, and the truncation would then be reported one layer deeper where the
47
- * renderer cannot see it.
48
- */
49
- export declare const MAX_READ_FILE_BYTES: number;
50
- /** The remote root one file call targets. */
51
- export interface RemoteFileTarget {
52
- readonly connectionId: string;
53
- readonly root: string;
54
- /** Whether the root admits writes; `false` refuses every write tool before it touches the host. */
55
- readonly writable: boolean;
56
- }
57
- /** One read window request, after validation. */
58
- export interface ReadWindowRequest {
59
- /** 1-based first line. */
60
- readonly offset: number;
61
- /** Maximum number of lines. */
62
- readonly limit: number;
63
- /** Maximum characters per returned line. */
64
- readonly maxLineLength: number;
65
- }
66
- /** One numbered line of a window. */
67
- export interface ReadLine {
68
- /** 1-based line number **in the file**. */
69
- readonly number: number;
70
- readonly text: string;
71
- }
72
- /** The window {@link buildLineWindow} produces. */
73
- export interface LineWindow {
74
- readonly lines: readonly ReadLine[];
75
- /** Lines counted in the text that was read; **not** the file's total when `sourceTruncated` is true. */
76
- readonly totalLines: number;
77
- }
78
- /** 测试用:清空记忆,避免用例之间互相影响。 */
79
- export declare function resetRememberedVersions(): void;
80
- export interface RemoteFileBackend {
81
- /** Resolve an agent working directory to a remote root. */
82
- resolve(cwd: string): RemoteFileTarget | undefined;
83
- /**
84
- * The remote directory this session added with 「添加目录」, when the cwd itself names no remote root.
85
- *
86
- * Without this hook the three file tools were the only ones in the family that could not reach a directory
87
- * the user explicitly added to the conversation (audit 0086 fixed the fallback for `remote_exec`/`remote_grep`/
88
- * `remote_glob` and left these behind, so a session opened on a local folder could run a command in the added
89
- * directory but not read a file in it). The rule itself lives in `domain/roots/session-roots.ts`.
90
- *
91
- * Optional so every existing composition and test keeps working: absent means "no session fallback", which is
92
- * exactly the pre-existing behaviour.
93
- *
94
- * @param cwd - the agent's session cwd, used as the workspace identity the add action wrote.
95
- * @returns the single added remote root, a refusal naming several, or `none`.
96
- */
97
- resolveSessionRoot?(cwd: string, sessionId?: string): SessionRootOutcome;
98
- /** Whether one session-added root admits writes; absent means "read-only", the safe default. */
99
- writableForSessionRoot?(target: SessionRootTarget): boolean;
100
- /**
101
- * The whole-file read cap this backend actually applies, in bytes.
102
- *
103
- * Exposed because the refusal messages name the cap, and a deployment may override it: a message quoting
104
- * the default while the backend applied a different one would send the caller to the wrong number.
105
- */
106
- readonly maxReadBytes: number;
107
- /** Read one root-absolute file as text, capped by the backend's own limit. */
108
- read(target: RemoteFileTarget, absolute: string, signal: AbortSignal): Promise<{
109
- readonly text: string;
110
- readonly size: number;
111
- readonly truncated: boolean;
112
- readonly version: string;
113
- }>;
114
- /**
115
- * Write one root-absolute file as text.
116
- *
117
- * @param expectedVersion - when non-empty, the write must fail if the current version differs.
118
- */
119
- write(target: RemoteFileTarget, absolute: string, text: string, expectedVersion: string | undefined, signal: AbortSignal): Promise<{
120
- readonly bytes: number;
121
- readonly version: string;
122
- }>;
123
- /**
124
- * Atomic in-place edit done **by the agent** — one round trip, no file transfer.
125
- *
126
- * Absent on a backend that cannot do it (Tier 1 shell): the caller falls back to read → replace → write.
127
- * That fallback costs two round trips and ships the whole file down and back up; a 1 MiB file moves
128
- * ~2 MiB instead of ~200 bytes. The agent has had `fs.edit` for exactly this reason.
129
- */
130
- edit?(target: RemoteFileTarget, absolute: string, oldText: string, newText: string, all: boolean, expectedVersion: string | undefined, signal: AbortSignal): Promise<{
131
- readonly replaced: number;
132
- readonly version: string;
133
- readonly bytes: number;
134
- } | undefined>;
135
- /**
136
- * The version this process last saw for a path, if any.
137
- *
138
- * Used to guard `remote_write` / `remote_patch` when the caller omits `expectedVersion`.
139
- */
140
- recall(target: RemoteFileTarget, absolute: string): string | undefined;
141
- /**
142
- * Move (or rename) one path.
143
- *
144
- * `createParents` decides whether a missing destination directory is created or refused.
145
- */
146
- move(target: RemoteFileTarget, from: string, to: string, createParents: boolean, signal: AbortSignal): Promise<void>;
147
- /**
148
- * Metadata for one root-absolute path, without reading its content.
149
- *
150
- * This is the cheap question ("does it exist, how big, when did it change") that the tool surface
151
- * previously answered by reading the whole file or by shelling out to `ls -l` and parsing text.
152
- */
153
- stat(target: RemoteFileTarget, absolute: string, signal: AbortSignal): Promise<{
154
- readonly exists: boolean;
155
- readonly isDirectory: boolean;
156
- readonly isFile: boolean;
157
- readonly isSymlink: boolean;
158
- readonly size: number;
159
- readonly mtimeMs: number;
160
- readonly version: string;
161
- }>;
162
- /**
163
- * Apply a unified diff to one file, atomically.
164
- *
165
- * @param expectedVersion - when non-empty, the patch must fail if the current version differs.
166
- */
167
- patch(target: RemoteFileTarget, absolute: string, diff: string, expectedVersion: string | undefined, signal: AbortSignal): Promise<{
168
- readonly bytes: number;
169
- readonly version: string;
170
- readonly hunks: number;
171
- }>;
172
- /**
173
- * Change one file's permission bits, after a write.
174
- *
175
- * @param mode - 3- or 4-digit octal string (already normalized by `fileModeArg`).
176
- */
177
- chmod(target: RemoteFileTarget, absolute: string, mode: string, signal: AbortSignal): Promise<void>;
178
- }
179
- /** What the backend needs from its owner. All injected, so it is testable without a real host. */
180
- export interface RemoteFileBackendDeps {
181
- /** Resolve a caller's working directory to a remote root. */
182
- readonly resolveRoot: (cwd: string) => RemoteFileTarget | undefined;
183
- /** See {@link RemoteFileBackend.resolveSessionRoot}. */
184
- readonly resolveSessionRoot?: ((cwd: string, sessionId?: string) => SessionRootOutcome) | undefined;
185
- /**
186
- * Whether one session-added root admits writes.
187
- *
188
- * Kept separate from {@link resolveSessionRoot} because the persisted session entry does not carry the flag
189
- * (the domain rule deliberately ignores it) while `RemoteFileTarget` requires it. Absent means read-only:
190
- * an unknown root must not silently accept writes.
191
- */
192
- readonly writableForSessionRoot?: ((target: SessionRootTarget) => boolean) | undefined;
193
- /**
194
- * The backend serving one target.
195
- *
196
- * `undefined` means the root outlived its backend (the connection was removed), which is refused rather
197
- * than served by some other host's backend.
198
- */
199
- readonly backendFor: (target: RemoteFileTarget) => Backend | undefined;
200
- /** Override the whole-file read cap. */
201
- readonly maxReadBytes?: number;
202
- }
203
- /**
204
- * Build the file backend the entry registers.
205
- *
206
- * @param deps - injected host wiring.
207
- * @returns the backend consumed by {@link registerRemoteFileTools}.
208
- */
209
- export declare function createRemoteFileBackend(deps: RemoteFileBackendDeps): RemoteFileBackend;
210
- /** Turn a plain string into model-facing content. */
211
- export declare function text(value: string): ContentBlock[];
212
- /**
213
- * Resolve the caller's target, or fail loudly.
214
- *
215
- * Two rules, in the same order as the rest of the tool family (audit 0086):
216
- *
217
- * 1. **The session cwd decides** when it names a remote root.
218
- * 2. **Otherwise, the remote directories this session added with 「添加目录」** are the candidates: exactly
219
- * one is used, several are refused by name rather than guessed.
220
- *
221
- * @param backend - file backend.
222
- * @param exec - tool run context (carries the calling agent).
223
- * @returns the remote target.
224
- * @throws Error when neither rule names exactly one remote root.
225
- */
226
- export declare function targetFor(backend: RemoteFileBackend, exec: ToolRunContext): RemoteFileTarget;
227
- /**
228
- * Resolve one model-supplied path against the target root, or fail with the refusal's own message.
229
- *
230
- * @param target - the call's target: its root and the connection it belongs to.
231
- * @param path - the model's spelling (absolute, root-relative, or same-connection `ssh://`).
232
- * @returns the root-absolute path.
233
- * @throws Error naming the offending path and the root.
234
- */
235
- export declare function resolveOrThrow(target: RemoteFileTarget, path: string): string;
236
- /** The shared wording for "which spelling is accepted", used by every path-bearing parameter. */
237
- export declare const PATH_SPELLING: string;
238
- /**
239
- * Build one line window from a file's text.
240
- *
241
- * The scan always walks the whole text so `totalLines` is exact for what was read — the alternative (stop at
242
- * `limit`) would make the footer's "of N lines" a lie, and that number is what tells the caller whether to
243
- * page on. A line over `maxLineLength` is truncated **in place** rather than dropped, because dropping it
244
- * would silently renumber the window.
245
- *
246
- * @param source - the file text as read.
247
- * @param request - the validated window.
248
- * @returns the numbered lines plus the total line count of `source`.
249
- */
250
- export declare function buildLineWindow(source: string, request: ReadWindowRequest): LineWindow;
251
- /** The read value as rendered: the window plus what the read learned about the file. */
252
- export interface ReadView {
253
- /** Root-absolute path that was read. */
254
- readonly path: string;
255
- /** 1-based first line requested. */
256
- readonly offset: number;
257
- readonly lines: readonly ReadLine[];
258
- /** Exact line count of the file when the whole file was read; a lower bound when `sourceTruncated`. */
259
- readonly totalLines: number;
260
- /** The whole-file read was cut by the byte cap, so the window may be missing later lines. */
261
- readonly sourceTruncated: boolean;
262
- /** The byte cap that produced `sourceTruncated`, as the backend actually applied it. */
263
- readonly maxReadBytes: number;
264
- readonly size: number;
265
- /** Version token to pass to `remote_write` / `remote_edit` as `expectedVersion`. */
266
- readonly version: string;
267
- readonly limit: number;
268
- }
269
- /**
270
- * Model-facing rendering of one read window.
271
- *
272
- * The numbered lines and the footer mirror the platform's own `read` tool (`formatReadOutput`): a caller that
273
- * has used that tool already knows how to page with the footer's `offset=…`, and the last line number tells
274
- * it exactly which line it just saw.
275
- *
276
- * @param view - the read to render.
277
- * @returns the model-facing text block body.
278
- */
279
- export declare function formatRead(view: ReadView): string;
280
- /** The outcome of applying one literal edit. */
281
- export interface LiteralEditResult {
282
- readonly text: string;
283
- /** How many occurrences were replaced. */
284
- readonly replacements: number;
285
- /** How many occurrences the literal `old` had in the file. */
286
- readonly occurrences: number;
287
- }
288
- /**
289
- * Replace a literal string inside `text`.
290
- *
291
- * Two properties are the whole point of this function, and both are pinned by tests:
292
- *
293
- * - **The match is literal.** `old` is compared as text; a backslash-n in `old` matches a backslash and an
294
- * `n`, never a newline. The caller writes real newlines when it means newlines.
295
- * - **The replacement is verbatim.** The new text is spliced in by index (`replaceLiteral` in
296
- * `domain/literal-text.ts`), so `$&`, `` $` ``, `$'` and `$$` inside `new` are inserted as those
297
- * characters. `String.prototype.replace` with a string replacement would expand all four, silently
298
- * corrupting any patch that mentions a shell variable.
299
- *
300
- * @param source - the current file text.
301
- * @param request - `old`, `new` and the `all` opt-in.
302
- * @returns the new text plus the occurrence and replacement counts.
303
- * @throws Error when `old` is empty, absent, or ambiguous without `all`.
304
- */
305
- export declare function applyLiteralEdit(source: string, request: {
306
- readonly old: string;
307
- readonly new: string;
308
- readonly all: boolean;
309
- }): LiteralEditResult;
310
- /** The edit value as rendered. */
311
- export interface EditView {
312
- readonly path: string;
313
- readonly replacements: number;
314
- readonly occurrences: number;
315
- readonly version: string;
316
- readonly bytes: number;
317
- readonly all: boolean;
318
- }
319
- /** Model-facing rendering of one edit. */
320
- /** `remote_stat` 的输出形状。 */
321
- export interface StatView {
322
- readonly path: string;
323
- readonly exists: boolean;
324
- readonly kind: string;
325
- readonly size: number;
326
- readonly mtimeMs: number;
327
- readonly version: string;
328
- }
329
- /** Render one stat result for the model. */
330
- export declare function formatStat(result: StatView): string;
331
- /** `remote_patch` 的输出形状。 */
332
- export interface PatchView {
333
- readonly path: string;
334
- readonly hunks: number;
335
- readonly bytes: number;
336
- readonly version: string;
337
- }
338
- /** Render one patch result for the model. */
339
- export declare function formatPatch(result: PatchView): string;
340
- export declare function formatEdit(view: EditView): string;
341
- /** The write value as rendered. */
342
- export interface WriteView {
343
- readonly path: string;
344
- readonly bytes: number;
345
- readonly version: string;
346
- readonly guarded: boolean;
347
- /** The permission bits applied, when the caller asked for any. */
348
- readonly mode?: string | undefined;
349
- }
350
- /** Model-facing rendering of one write. */
351
- export declare function formatWrite(view: WriteView): string;
352
- /** Read a non-empty string argument. */
353
- export declare function requiredString(value: unknown, field: string): string;
354
- /**
355
- * Build the three file definitions.
356
- *
357
- * @param backend - injected implementations.
358
- * @returns the definitions, ready for `ctx.tools.register`.
359
- */
360
- export declare function remoteFileToolDefinitions(backend: RemoteFileBackend): ToolDefinition[];
361
- /**
362
- * Register the three file tools on one context.
363
- *
364
- * @param ctx - host context with the `tools` service.
365
- * @param backend - injected implementations.
366
- * @returns a disposer removing every registration (idempotent).
367
- */
368
- export declare function registerRemoteFileTools(ctx: Context, backend: RemoteFileBackend): () => void;
369
- //# sourceMappingURL=remote-file-tools.d.ts.map
@@ -1,177 +0,0 @@
1
- /**
2
- * The remote file index behind `@` completion.
3
- *
4
- * Requirement FR-2.5 asks for completion over a remote root: prefer `git ls-files` (fast, respects
5
- * `.gitignore`), fall back to a **bounded** `find` on hosts without git (our benchmark host has no git),
6
- * cache the result, and keep a 900 ms budget so typing never stalls.
7
- *
8
- * Two tiers serve it, and the choice is reported rather than hidden:
9
- *
10
- * - **Tier 2** (`agentScan`) runs the agent's `index.scan`, which measured ~0.16 ms/file on a real host;
11
- * - **Tier 1** runs one shell command over the transport (measured 187–248 ms for 2000 files).
12
- *
13
- * Budgets are enforced by passing them down (`timeoutMs`), not by hoping: an over-budget result comes back
14
- * with `timedOut: true` and whatever was enumerated, so the UI can show partial completion instead of
15
- * freezing. Nothing here silently returns a short list as if it were complete.
16
- *
17
- * @module dsh-over-ssh/integration/remote-index
18
- */
19
- import { type RemoteCapabilities } from '../../domain/capability.ts';
20
- /** Where a result came from. */
21
- export type IndexSource = 'agent-index' | 'git-ls-files' | 'find-bounded' | 'cache';
22
- /** What the index needs from the two tiers. All injected, so this module stays offline-testable. */
23
- export interface RemoteIndexTiers {
24
- /** Tier 2: the agent's `index.scan`. Absent when the connection has no agent. */
25
- readonly agentScan?: (root: string, options: {
26
- readonly maxEntries: number;
27
- readonly timeoutMs: number;
28
- readonly signal?: AbortSignal;
29
- }) => Promise<{
30
- readonly paths: readonly string[];
31
- readonly strategy: string;
32
- readonly truncated: boolean;
33
- }>;
34
- /**
35
- * Tier 2: the agent's `index.refresh` (incremental).
36
- *
37
- * Only useful **after** a scan the agent still holds a snapshot for; the agent falls back to a full scan
38
- * on its own when it cannot refresh, so a caller may always prefer this once it has scanned once. A
39
- * transport that predates the method reports `REMOTE_UNSUPPORTED`, which this module treats as "use
40
- * agentScan" — never as a failed completion.
41
- */
42
- readonly agentRefresh?: (root: string, options: {
43
- readonly maxEntries: number;
44
- readonly timeoutMs: number;
45
- readonly signal?: AbortSignal;
46
- }) => Promise<{
47
- readonly paths: readonly string[];
48
- readonly strategy: string;
49
- readonly truncated: boolean;
50
- }>;
51
- /** Tier 1: one shell command over the transport. */
52
- readonly exec: (command: string, options: {
53
- readonly timeoutMs: number;
54
- readonly maxBytes: number;
55
- readonly signal?: AbortSignal;
56
- }) => Promise<{
57
- readonly exitCode: number;
58
- readonly stdout: string;
59
- readonly stderr: string;
60
- readonly truncated?: boolean;
61
- }>;
62
- /** Tier 1 capability facts, used to pick the enumeration command. */
63
- readonly capabilities: () => Promise<RemoteCapabilities>;
64
- }
65
- /** Options for {@link RemoteIndex}. */
66
- export interface RemoteIndexOptions {
67
- readonly tiers: RemoteIndexTiers;
68
- /** How long an enumerated root stays usable. Default 30 s. */
69
- readonly ttlMs?: number;
70
- /** How many roots to keep. Default 8. */
71
- readonly maxRoots?: number;
72
- /**
73
- * Cap for the "already scanned" hint set (default {@link DEFAULTS.maxScannedRoots}).
74
- *
75
- * Exposed so a test can drive the bound with a small number instead of allocating 257 roots.
76
- */
77
- readonly maxScannedRoots?: number;
78
- /** Default enumeration budget. Default 900 ms (FR-2.5). */
79
- readonly budgetMs?: number;
80
- /** Default entry cap. Default 20 000. */
81
- readonly maxEntries?: number;
82
- /** Byte cap for the cached paths; defaults to {@link DEFAULTS.maxBytes}. */
83
- readonly maxBytes?: number;
84
- /** Injectable clock, for the TTL and the budget assertions. */
85
- readonly now?: () => number;
86
- }
87
- /** One enumeration result. */
88
- export interface IndexResult {
89
- readonly paths: readonly string[];
90
- readonly source: IndexSource;
91
- /** The agent's own strategy string, when Tier 2 served it (`find-bounded`, `git`, …). */
92
- readonly agentStrategy?: string;
93
- readonly truncated: boolean;
94
- /** True when the enumeration stopped because the budget ran out. */
95
- readonly timedOut: boolean;
96
- /**
97
- * Whether the result was cached.
98
- *
99
- * `false` only for an enumeration that never completed (killed at the deadline, connection lost): such a
100
- * result must not be remembered, while a slow-but-successful one must be — the whole point of the cache
101
- * is to spare the *next* keystroke, and throwing away a good list because it took 1.2 s would mean
102
- * re-running it for every keystroke (found by the real-host measurement).
103
- */
104
- readonly cacheable: boolean;
105
- readonly ms: number;
106
- readonly cached: boolean;
107
- }
108
- /** One completion request. */
109
- export interface CompletionRequest {
110
- readonly root: string;
111
- /** What the user has typed after `@`. */
112
- readonly query: string;
113
- readonly limit?: number;
114
- readonly budgetMs?: number;
115
- readonly signal?: AbortSignal;
116
- }
117
- /** One completion result. */
118
- export interface CompletionResult {
119
- readonly matches: readonly string[];
120
- readonly source: IndexSource;
121
- readonly truncated: boolean;
122
- readonly timedOut: boolean;
123
- readonly ms: number;
124
- readonly cached: boolean;
125
- }
126
- /** Enumerates a remote root, with TTL caching, ranking and budget accounting. */
127
- export declare class RemoteIndex {
128
- #private;
129
- constructor(options: RemoteIndexOptions);
130
- /** Enumerate one root (or serve it from cache while fresh). */
131
- enumerate(root: string, options?: {
132
- readonly maxEntries?: number;
133
- readonly budgetMs?: number;
134
- readonly force?: boolean;
135
- readonly signal?: AbortSignal;
136
- }): Promise<IndexResult>;
137
- /**
138
- * Completion for a partially typed `@` query.
139
- *
140
- * Ranking is deliberate and cheap: a basename prefix match beats a path prefix match, which beats a
141
- * substring match — so typing `@main` prefers `main.ts` over `src/domain/main-helper.ts`.
142
- */
143
- complete(request: CompletionRequest): Promise<CompletionResult>;
144
- /** Drop one root (its files changed) or everything. */
145
- invalidate(root?: string): void;
146
- /** Whether a root is currently cached and fresh (used by tests and by the status UI). */
147
- isFresh(root: string): boolean;
148
- }
149
- /**
150
- * `git ls-files` for one root.
151
- *
152
- * `--cached --others --exclude-standard` lists tracked and untracked-but-not-ignored files, which is what
153
- * completion should offer; `head -n` bounds the output before it crosses the wire.
154
- */
155
- export declare function gitCommand(root: string, maxEntries: number): string;
156
- /** Bounded `find` for hosts without git (the benchmark host): files only, no `.git` internals. */
157
- export declare function findCommand(root: string, maxEntries: number): string;
158
- /**
159
- * The cheap "is this root a git work tree" probe.
160
- *
161
- * `git ls-files` in the enumeration command is piped into `head`, so its failure (exit 128 in a
162
- * non-repository directory) never reaches the caller. This command has no pipe, so its status is git's own:
163
- * exit 0 plus `true` on stdout for a work tree, exit non-zero (or `false`) for anything else.
164
- */
165
- export declare function gitRepoProbeCommand(root: string): string;
166
- /** The cheap "does this directory exist" probe for the find strategy (see `#confirmEmpty`). */
167
- export declare function directoryProbeCommand(root: string): string;
168
- /**
169
- * Rank paths for a partially typed query.
170
- *
171
- * @param paths - enumerated paths (absolute or root-relative; both work).
172
- * @param query - what the user typed; empty means "show the shallowest entries first".
173
- * @param limit - maximum matches.
174
- * @returns up to `limit` paths, best first.
175
- */
176
- export declare function rankMatches(paths: readonly string[], query: string, limit: number): readonly string[];
177
- //# sourceMappingURL=remote-index.d.ts.map