@animalabs/agent-framework 0.26.1 → 0.27.0

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 (276) hide show
  1. package/README.md +57 -7
  2. package/dist/src/agent.d.ts +263 -22
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +531 -54
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/api/server.d.ts.map +1 -1
  7. package/dist/src/api/server.js +1 -2
  8. package/dist/src/api/server.js.map +1 -1
  9. package/dist/src/auto-continue.d.ts +33 -0
  10. package/dist/src/auto-continue.d.ts.map +1 -0
  11. package/dist/src/auto-continue.js +35 -0
  12. package/dist/src/auto-continue.js.map +1 -0
  13. package/dist/src/code-execution/isolate/scripts.d.ts +11 -0
  14. package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
  15. package/dist/src/code-execution/isolate/scripts.js +5 -1
  16. package/dist/src/code-execution/isolate/scripts.js.map +1 -1
  17. package/dist/src/framework.d.ts +829 -156
  18. package/dist/src/framework.d.ts.map +1 -1
  19. package/dist/src/framework.js +3906 -1008
  20. package/dist/src/framework.js.map +1 -1
  21. package/dist/src/gate/event-gate.d.ts +309 -46
  22. package/dist/src/gate/event-gate.d.ts.map +1 -1
  23. package/dist/src/gate/event-gate.js +2117 -395
  24. package/dist/src/gate/event-gate.js.map +1 -1
  25. package/dist/src/gate/index.d.ts +2 -2
  26. package/dist/src/gate/index.d.ts.map +1 -1
  27. package/dist/src/gate/index.js +1 -1
  28. package/dist/src/gate/index.js.map +1 -1
  29. package/dist/src/gate/recipe-reconcile.d.ts +103 -0
  30. package/dist/src/gate/recipe-reconcile.d.ts.map +1 -0
  31. package/dist/src/gate/recipe-reconcile.js +325 -0
  32. package/dist/src/gate/recipe-reconcile.js.map +1 -0
  33. package/dist/src/gate/types.d.ts +68 -8
  34. package/dist/src/gate/types.d.ts.map +1 -1
  35. package/dist/src/index.d.ts +6 -4
  36. package/dist/src/index.d.ts.map +1 -1
  37. package/dist/src/index.js +5 -2
  38. package/dist/src/index.js.map +1 -1
  39. package/dist/src/mcpl/channel-registry.d.ts +433 -113
  40. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  41. package/dist/src/mcpl/channel-registry.js +1152 -434
  42. package/dist/src/mcpl/channel-registry.js.map +1 -1
  43. package/dist/src/mcpl/conversation-router.d.ts +16 -8
  44. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  45. package/dist/src/mcpl/conversation-router.js +29 -15
  46. package/dist/src/mcpl/conversation-router.js.map +1 -1
  47. package/dist/src/mcpl/convert-block.d.ts.map +1 -1
  48. package/dist/src/mcpl/convert-block.js +14 -2
  49. package/dist/src/mcpl/convert-block.js.map +1 -1
  50. package/dist/src/mcpl/hook-orchestrator.d.ts +7 -1
  51. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
  52. package/dist/src/mcpl/hook-orchestrator.js +32 -7
  53. package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
  54. package/dist/src/mcpl/index.d.ts +5 -1
  55. package/dist/src/mcpl/index.d.ts.map +1 -1
  56. package/dist/src/mcpl/index.js +4 -0
  57. package/dist/src/mcpl/index.js.map +1 -1
  58. package/dist/src/mcpl/inference-router.d.ts +14 -2
  59. package/dist/src/mcpl/inference-router.d.ts.map +1 -1
  60. package/dist/src/mcpl/inference-router.js +73 -28
  61. package/dist/src/mcpl/inference-router.js.map +1 -1
  62. package/dist/src/mcpl/prose-grammar.d.ts +4 -0
  63. package/dist/src/mcpl/prose-grammar.d.ts.map +1 -1
  64. package/dist/src/mcpl/prose-grammar.js +6 -0
  65. package/dist/src/mcpl/prose-grammar.js.map +1 -1
  66. package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
  67. package/dist/src/mcpl/protocol-family.js +3 -0
  68. package/dist/src/mcpl/protocol-family.js.map +1 -1
  69. package/dist/src/mcpl/push-coalescer.d.ts +20 -2
  70. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  71. package/dist/src/mcpl/push-coalescer.js +33 -11
  72. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  73. package/dist/src/mcpl/reference-fetcher.d.ts +63 -42
  74. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  75. package/dist/src/mcpl/reference-fetcher.js +177 -145
  76. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  77. package/dist/src/mcpl/references.d.ts +380 -46
  78. package/dist/src/mcpl/references.d.ts.map +1 -1
  79. package/dist/src/mcpl/references.js +737 -78
  80. package/dist/src/mcpl/references.js.map +1 -1
  81. package/dist/src/mcpl/server-connection.d.ts +82 -3
  82. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  83. package/dist/src/mcpl/server-connection.js +132 -7
  84. package/dist/src/mcpl/server-connection.js.map +1 -1
  85. package/dist/src/mcpl/tool-lifecycle.d.ts +11 -0
  86. package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
  87. package/dist/src/mcpl/tool-lifecycle.js +21 -0
  88. package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
  89. package/dist/src/mcpl/tool-presence.d.ts +259 -0
  90. package/dist/src/mcpl/tool-presence.d.ts.map +1 -0
  91. package/dist/src/mcpl/tool-presence.js +628 -0
  92. package/dist/src/mcpl/tool-presence.js.map +1 -0
  93. package/dist/src/mcpl/tool-result-normalize.d.ts +9 -3
  94. package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -1
  95. package/dist/src/mcpl/tool-result-normalize.js +14 -6
  96. package/dist/src/mcpl/tool-result-normalize.js.map +1 -1
  97. package/dist/src/mcpl/tool-signature.d.ts +39 -0
  98. package/dist/src/mcpl/tool-signature.d.ts.map +1 -0
  99. package/dist/src/mcpl/tool-signature.js +292 -0
  100. package/dist/src/mcpl/tool-signature.js.map +1 -0
  101. package/dist/src/mcpl/toolset-host.d.ts +200 -0
  102. package/dist/src/mcpl/toolset-host.d.ts.map +1 -0
  103. package/dist/src/mcpl/toolset-host.js +621 -0
  104. package/dist/src/mcpl/toolset-host.js.map +1 -0
  105. package/dist/src/mcpl/toolsets-module.d.ts +80 -0
  106. package/dist/src/mcpl/toolsets-module.d.ts.map +1 -0
  107. package/dist/src/mcpl/toolsets-module.js +431 -0
  108. package/dist/src/mcpl/toolsets-module.js.map +1 -0
  109. package/dist/src/mcpl/transport.d.ts +2 -2
  110. package/dist/src/mcpl/transport.d.ts.map +1 -1
  111. package/dist/src/mcpl/transport.js +3 -2
  112. package/dist/src/mcpl/transport.js.map +1 -1
  113. package/dist/src/mcpl/types.d.ts +21 -1
  114. package/dist/src/mcpl/types.d.ts.map +1 -1
  115. package/dist/src/mcpl/types.js.map +1 -1
  116. package/dist/src/mcpl/visible-content.d.ts +7 -9
  117. package/dist/src/mcpl/visible-content.d.ts.map +1 -1
  118. package/dist/src/mcpl/visible-content.js +21 -9
  119. package/dist/src/mcpl/visible-content.js.map +1 -1
  120. package/dist/src/module-registry.d.ts +23 -8
  121. package/dist/src/module-registry.d.ts.map +1 -1
  122. package/dist/src/module-registry.js +68 -15
  123. package/dist/src/module-registry.js.map +1 -1
  124. package/dist/src/modules/discord/index.js +1 -1
  125. package/dist/src/modules/discord/index.js.map +1 -1
  126. package/dist/src/modules/history/index.d.ts +2 -1
  127. package/dist/src/modules/history/index.d.ts.map +1 -1
  128. package/dist/src/modules/history/index.js +51 -17
  129. package/dist/src/modules/history/index.js.map +1 -1
  130. package/dist/src/modules/history/semantic.d.ts +108 -6
  131. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  132. package/dist/src/modules/history/semantic.js +273 -57
  133. package/dist/src/modules/history/semantic.js.map +1 -1
  134. package/dist/src/modules/workspace/direct-write.d.ts +181 -0
  135. package/dist/src/modules/workspace/direct-write.d.ts.map +1 -0
  136. package/dist/src/modules/workspace/direct-write.js +384 -0
  137. package/dist/src/modules/workspace/direct-write.js.map +1 -0
  138. package/dist/src/modules/workspace/edit-text.d.ts +26 -12
  139. package/dist/src/modules/workspace/edit-text.d.ts.map +1 -1
  140. package/dist/src/modules/workspace/edit-text.js +58 -28
  141. package/dist/src/modules/workspace/edit-text.js.map +1 -1
  142. package/dist/src/modules/workspace/effects.d.ts +10 -0
  143. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  144. package/dist/src/modules/workspace/effects.js +1 -1
  145. package/dist/src/modules/workspace/effects.js.map +1 -1
  146. package/dist/src/modules/workspace/index.d.ts +246 -27
  147. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  148. package/dist/src/modules/workspace/index.js +1216 -236
  149. package/dist/src/modules/workspace/index.js.map +1 -1
  150. package/dist/src/modules/workspace/read-page.d.ts +41 -0
  151. package/dist/src/modules/workspace/read-page.d.ts.map +1 -0
  152. package/dist/src/modules/workspace/read-page.js +213 -0
  153. package/dist/src/modules/workspace/read-page.js.map +1 -0
  154. package/dist/src/modules/workspace/result-text.d.ts +67 -0
  155. package/dist/src/modules/workspace/result-text.d.ts.map +1 -0
  156. package/dist/src/modules/workspace/result-text.js +94 -0
  157. package/dist/src/modules/workspace/result-text.js.map +1 -0
  158. package/dist/src/modules/workspace/text.d.ts +29 -12
  159. package/dist/src/modules/workspace/text.d.ts.map +1 -1
  160. package/dist/src/modules/workspace/text.js +35 -33
  161. package/dist/src/modules/workspace/text.js.map +1 -1
  162. package/dist/src/modules/workspace/types.d.ts +41 -17
  163. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  164. package/dist/src/modules/workspace/types.js.map +1 -1
  165. package/dist/src/sent-rows.d.ts +59 -0
  166. package/dist/src/sent-rows.d.ts.map +1 -0
  167. package/dist/src/sent-rows.js +145 -0
  168. package/dist/src/sent-rows.js.map +1 -0
  169. package/dist/src/silent-heartbeat.d.ts +17 -10
  170. package/dist/src/silent-heartbeat.d.ts.map +1 -1
  171. package/dist/src/silent-heartbeat.js.map +1 -1
  172. package/dist/src/stable-hash.d.ts +3 -0
  173. package/dist/src/stable-hash.d.ts.map +1 -0
  174. package/dist/src/stable-hash.js +15 -0
  175. package/dist/src/stable-hash.js.map +1 -0
  176. package/dist/src/stored-injections.d.ts +99 -0
  177. package/dist/src/stored-injections.d.ts.map +1 -0
  178. package/dist/src/stored-injections.js +106 -0
  179. package/dist/src/stored-injections.js.map +1 -0
  180. package/dist/src/text-as-stored.d.ts +4 -3
  181. package/dist/src/text-as-stored.d.ts.map +1 -1
  182. package/dist/src/text-as-stored.js +4 -3
  183. package/dist/src/text-as-stored.js.map +1 -1
  184. package/dist/src/thinking-drops.d.ts +81 -0
  185. package/dist/src/thinking-drops.d.ts.map +1 -0
  186. package/dist/src/thinking-drops.js +103 -0
  187. package/dist/src/thinking-drops.js.map +1 -0
  188. package/dist/src/tool-image-ledger.d.ts +9 -8
  189. package/dist/src/tool-image-ledger.d.ts.map +1 -1
  190. package/dist/src/tool-image-ledger.js +9 -8
  191. package/dist/src/tool-image-ledger.js.map +1 -1
  192. package/dist/src/tool-presentation.d.ts.map +1 -1
  193. package/dist/src/tool-presentation.js +3 -1
  194. package/dist/src/tool-presentation.js.map +1 -1
  195. package/dist/src/tool-result-guard.d.ts +4 -2
  196. package/dist/src/tool-result-guard.d.ts.map +1 -1
  197. package/dist/src/tool-result-guard.js +21 -6
  198. package/dist/src/tool-result-guard.js.map +1 -1
  199. package/dist/src/tool-result-history.d.ts +186 -21
  200. package/dist/src/tool-result-history.d.ts.map +1 -1
  201. package/dist/src/tool-result-history.js +428 -89
  202. package/dist/src/tool-result-history.js.map +1 -1
  203. package/dist/src/tune-out/coordinator.d.ts +17 -9
  204. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  205. package/dist/src/tune-out/coordinator.js +84 -37
  206. package/dist/src/tune-out/coordinator.js.map +1 -1
  207. package/dist/src/types/agent.d.ts +186 -7
  208. package/dist/src/types/agent.d.ts.map +1 -1
  209. package/dist/src/types/events.d.ts +19 -2
  210. package/dist/src/types/events.d.ts.map +1 -1
  211. package/dist/src/types/framework.d.ts +24 -3
  212. package/dist/src/types/framework.d.ts.map +1 -1
  213. package/dist/src/types/module.d.ts +90 -7
  214. package/dist/src/types/module.d.ts.map +1 -1
  215. package/dist/src/types/trace.d.ts +57 -2
  216. package/dist/src/types/trace.d.ts.map +1 -1
  217. package/package.json +4 -3
  218. package/src/agent.ts +593 -60
  219. package/src/api/server.ts +1 -2
  220. package/src/auto-continue.ts +42 -0
  221. package/src/code-execution/isolate/scripts.ts +11 -3
  222. package/src/framework.ts +4004 -1026
  223. package/src/gate/event-gate.ts +2315 -389
  224. package/src/gate/index.ts +2 -1
  225. package/src/gate/recipe-reconcile.ts +355 -0
  226. package/src/gate/types.ts +70 -8
  227. package/src/index.ts +7 -4
  228. package/src/mcpl/channel-registry.ts +1220 -467
  229. package/src/mcpl/conversation-router.ts +34 -16
  230. package/src/mcpl/convert-block.ts +14 -1
  231. package/src/mcpl/hook-orchestrator.ts +33 -6
  232. package/src/mcpl/index.ts +6 -1
  233. package/src/mcpl/inference-router.ts +79 -34
  234. package/src/mcpl/prose-grammar.ts +7 -0
  235. package/src/mcpl/protocol-family.ts +2 -0
  236. package/src/mcpl/push-coalescer.ts +51 -13
  237. package/src/mcpl/reference-fetcher.ts +201 -162
  238. package/src/mcpl/references.ts +852 -78
  239. package/src/mcpl/server-connection.ts +145 -7
  240. package/src/mcpl/tool-lifecycle.ts +21 -0
  241. package/src/mcpl/tool-presence.ts +650 -0
  242. package/src/mcpl/tool-result-normalize.ts +18 -6
  243. package/src/mcpl/tool-signature.ts +285 -0
  244. package/src/mcpl/toolset-host.ts +637 -0
  245. package/src/mcpl/toolsets-module.ts +451 -0
  246. package/src/mcpl/transport.ts +3 -2
  247. package/src/mcpl/types.ts +22 -1
  248. package/src/mcpl/visible-content.ts +22 -9
  249. package/src/module-registry.ts +77 -24
  250. package/src/modules/discord/index.ts +1 -1
  251. package/src/modules/history/index.ts +48 -18
  252. package/src/modules/history/semantic.ts +308 -54
  253. package/src/modules/workspace/direct-write.ts +425 -0
  254. package/src/modules/workspace/edit-text.ts +82 -35
  255. package/src/modules/workspace/effects.ts +1 -1
  256. package/src/modules/workspace/index.ts +1274 -241
  257. package/src/modules/workspace/read-page.ts +222 -0
  258. package/src/modules/workspace/result-text.ts +119 -0
  259. package/src/modules/workspace/text.ts +58 -32
  260. package/src/modules/workspace/types.ts +41 -17
  261. package/src/sent-rows.ts +162 -0
  262. package/src/silent-heartbeat.ts +17 -10
  263. package/src/stable-hash.ts +14 -0
  264. package/src/stored-injections.ts +131 -0
  265. package/src/text-as-stored.ts +4 -3
  266. package/src/thinking-drops.ts +141 -0
  267. package/src/tool-image-ledger.ts +9 -8
  268. package/src/tool-presentation.ts +3 -1
  269. package/src/tool-result-guard.ts +19 -6
  270. package/src/tool-result-history.ts +488 -88
  271. package/src/tune-out/coordinator.ts +80 -37
  272. package/src/types/agent.ts +189 -7
  273. package/src/types/events.ts +19 -2
  274. package/src/types/framework.ts +21 -3
  275. package/src/types/module.ts +86 -7
  276. package/src/types/trace.ts +58 -2
@@ -0,0 +1,41 @@
1
+ import type { ToolResult } from '../../types/events.js';
2
+ import { type FileText } from './text.js';
3
+ /**
4
+ * One page of a read, held to the result limit as its text measures it: a
5
+ * model reads the page's text (result-text.ts), and what passes the caller's
6
+ * limit doesn't reach it whole. A model's result spills into a file, cut, and
7
+ * a script gets ResultTooLarge in its place. So a page stops before its text
8
+ * would pass the limit, and its note says where to continue. A script's
9
+ * answer carries the page's fields, its content among them, beside its text,
10
+ * and the limit counts the text alone. A script's limit counts each character
11
+ * at the six bytes it takes at most in the answer, and no character but a
12
+ * control character takes more than three, so the text and the fields
13
+ * together pass what the script takes only on a page of more than half that
14
+ * limit (about 3.7 million characters at the default 128 MiB of script
15
+ * memory) holding control characters.
16
+ */
17
+ /** How many lines a read shows when it isn't given a limit. */
18
+ export declare const DEFAULT_READ_LINE_LIMIT = 2000;
19
+ /** What a read says about the text it shows, besides the lines themselves. */
20
+ export declare function textNotes(file: FileText): string[];
21
+ /**
22
+ * Lines from `offset` (1-based), at most `limit` of them, each numbered and a
23
+ * long one cut with the offsetChars and limitChars that read the rest. The
24
+ * page is the longest whose text fits within `maxChars`; where none fits
25
+ * with its first line as a page shows it, that line is cut shorter, at the
26
+ * most the page has room for. Where the page's last line is cut, its note
27
+ * names where that line's rest starts and how long it is, since reading on in
28
+ * order starts there: before the next line, or, on the page that ends the
29
+ * file, before the file is read whole. A page whose own fields and least
30
+ * content pass `maxChars` still moves on, and passes it. With no `maxChars`,
31
+ * for a call made in process, which takes the data itself, the page holds to
32
+ * `limit` alone and shows every line whole.
33
+ */
34
+ export declare function linePage(path: string, file: FileText, offset: number, limit: number, maxChars: number | undefined, extraNotes?: string[]): ToolResult;
35
+ /**
36
+ * Raw text from `offsetChars`, at most `limitChars` code units (one more to
37
+ * keep a surrogate pair whole), stopped sooner where its page's text would
38
+ * pass `maxChars`, if there is one.
39
+ */
40
+ export declare function characterPage(path: string, file: FileText, offsetChars: number, limitChars: number, maxChars: number | undefined, extraNotes?: string[]): ToolResult;
41
+ //# sourceMappingURL=read-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-page.d.ts","sourceRoot":"","sources":["../../../../src/modules/workspace/read-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,KAAK,QAAQ,EAAyC,MAAM,WAAW,CAAC;AAEjF;;;;;;;;;;;;;GAaG;AAEH,+DAA+D;AAC/D,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,EAAE,CAclD;AAuBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,GAAE,MAAM,EAAO,GAAG,UAAU,CAiGzJ;AAOD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,GAAE,MAAM,EAAO,GAAG,UAAU,CAuCxK"}
@@ -0,0 +1,213 @@
1
+ import { answerText, fieldLine, sectionLead, sectionText, textAnswer } from './result-text.js';
2
+ import { MAX_LINE_CHARS, numberedLine, safeCut } from './text.js';
3
+ /**
4
+ * One page of a read, held to the result limit as its text measures it: a
5
+ * model reads the page's text (result-text.ts), and what passes the caller's
6
+ * limit doesn't reach it whole. A model's result spills into a file, cut, and
7
+ * a script gets ResultTooLarge in its place. So a page stops before its text
8
+ * would pass the limit, and its note says where to continue. A script's
9
+ * answer carries the page's fields, its content among them, beside its text,
10
+ * and the limit counts the text alone. A script's limit counts each character
11
+ * at the six bytes it takes at most in the answer, and no character but a
12
+ * control character takes more than three, so the text and the fields
13
+ * together pass what the script takes only on a page of more than half that
14
+ * limit (about 3.7 million characters at the default 128 MiB of script
15
+ * memory) holding control characters.
16
+ */
17
+ /** How many lines a read shows when it isn't given a limit. */
18
+ export const DEFAULT_READ_LINE_LIMIT = 2000;
19
+ /** What a read says about the text it shows, besides the lines themselves. */
20
+ export function textNotes(file) {
21
+ const notes = [];
22
+ if (file.crlfEnds > 0) {
23
+ // A line whose own text ends in a CR shows its line end's CR too (fileText).
24
+ const kept = file.crKept > 0
25
+ ? `, except on ${file.crKept} ${file.crKept === 1 ? 'line whose own text ends in a CR, which shows' : 'lines whose own text ends in a CR, which show'} both CRs`
26
+ : '';
27
+ notes.push(file.lfEnds > 0
28
+ ? `Line ends are mixed, ${file.crlfEnds} CRLF and ${file.lfEnds} LF, and a CRLF end is shown without its CR${kept}. An edit matches text as read shows it, and keeps each line's end.`
29
+ : `Line ends are CRLF, shown without the CR${kept}. An edit matches text as read shows it, and keeps the CRLF ends.`);
30
+ }
31
+ if (file.bom)
32
+ notes.push('The file starts with a UTF-8 BOM, which isn\'t shown. An edit keeps it.');
33
+ if (!file.utf8)
34
+ notes.push(NOT_UTF8);
35
+ return notes;
36
+ }
37
+ const NOT_UTF8 = 'The file isn\'t valid UTF-8: each invalid byte sequence shows as U+FFFD. Edit refuses this file, and write replaces it whole.';
38
+ const joined = (notes) => {
39
+ const kept = notes.filter((n) => n !== null);
40
+ return kept.length > 0 ? kept.join(' ') : undefined;
41
+ };
42
+ /** A page's text: its fields, one to a line in their order, and then its content under its label. */
43
+ function pageText(page) {
44
+ const header = Object.entries(page)
45
+ .filter((entry) => entry[0] !== 'content' && entry[1] !== undefined)
46
+ .map(([key, value]) => fieldLine(key, value));
47
+ return answerText(header, [sectionText('content', page.content)]);
48
+ }
49
+ /** A page as read answers it: its text for the model, and its fields for a script. */
50
+ const pageAnswer = (page) => textAnswer(pageText(page), page);
51
+ /**
52
+ * Lines from `offset` (1-based), at most `limit` of them, each numbered and a
53
+ * long one cut with the offsetChars and limitChars that read the rest. The
54
+ * page is the longest whose text fits within `maxChars`; where none fits
55
+ * with its first line as a page shows it, that line is cut shorter, at the
56
+ * most the page has room for. Where the page's last line is cut, its note
57
+ * names where that line's rest starts and how long it is, since reading on in
58
+ * order starts there: before the next line, or, on the page that ends the
59
+ * file, before the file is read whole. A page whose own fields and least
60
+ * content pass `maxChars` still moves on, and passes it. With no `maxChars`,
61
+ * for a call made in process, which takes the data itself, the page holds to
62
+ * `limit` alone and shows every line whole.
63
+ */
64
+ export function linePage(path, file, offset, limit, maxChars, extraNotes = []) {
65
+ const { lines, starts } = file;
66
+ const total = lines.length;
67
+ const first = offset - 1;
68
+ const notes = [...textNotes(file), ...extraNotes];
69
+ if (first >= total) {
70
+ // Nothing to show: an empty file, or an offset past the last line. No
71
+ // range is claimed, and the line count says where the file ends.
72
+ const note = joined([
73
+ // Reading an empty file from its start is reading it whole.
74
+ first > 0 ? `Line ${first + 1} is past the end: the file has ${total} line${total === 1 ? '' : 's'}.` : null,
75
+ ...notes,
76
+ ]);
77
+ return pageAnswer({ path, totalLines: total, ...(note !== undefined ? { note } : {}), content: '' });
78
+ }
79
+ const last = Math.min(first + limit, total);
80
+ const shown = (i, maxLineChars = MAX_LINE_CHARS) => numberedLine(lines[i], i + 1, maxLineChars, (cut) => `read the rest with offsetChars=${starts[i] + cut} and limitChars=${lines[i].length - cut}`);
81
+ // Where the rest of line i starts in the stored text, shown at most
82
+ // maxLineChars long, as its marker says; null for a line shown whole.
83
+ const restOf = (i, maxLineChars) => lines[i].length > maxLineChars ? starts[i] + safeCut(lines[i], maxLineChars) : null;
84
+ // The rest's length is what's left of the line as a page shows it, so a
85
+ // character read of it ends where the line does.
86
+ const cutLine = (to, rest) => `Line ${to} is cut: read its rest with offsetChars=${rest} and limitChars=${starts[to - 1] + lines[to - 1].length - rest}`;
87
+ const limitNote = (to, rest) => `Truncated at ${limit} lines (file has ${total}).${rest === null ? '' : ` ${cutLine(to, rest)}.`} Use offset/limit to read more.`;
88
+ const budgetNote = (to, rest) => `Stopped at line ${to} to stay within the ${maxChars}-character result limit (file has ${total}). `
89
+ + (rest === null ? `Continue with offset=${to + 1}.` : `${cutLine(to, rest)}, then continue with offset=${to + 1}.`);
90
+ const page = (to, content, note) => ({
91
+ path, totalLines: total, fromLine: first + 1, toLine: to, ...(note !== undefined ? { note } : {}), content,
92
+ });
93
+ if (maxChars === undefined) {
94
+ const whole = lines.slice(first, last).map((line, k) => numberedLine(line, first + k + 1, Infinity)).join('\n');
95
+ return pageAnswer(page(last, whole, joined([last < total ? limitNote(last, null) : null, ...notes])));
96
+ }
97
+ // The note of the page that ends at line `to`, its last line shown at most
98
+ // lastMax long: the page that ends where it was asked to, at the file's end
99
+ // or the line limit, says so, and any other says it stopped for the limit.
100
+ const noteAt = (to, lastMax = MAX_LINE_CHARS) => {
101
+ const rest = restOf(to - 1, lastMax);
102
+ const endNote = to < total ? limitNote(to, rest) : rest === null ? null : `${cutLine(to, rest)}.`;
103
+ return joined([to === last ? endNote : budgetNote(to, rest), ...notes]);
104
+ };
105
+ // A page's text's length: its fields' and note's, measured with no content,
106
+ // and its content's, under the content's label. A page here has a line.
107
+ const lengthAt = (to, contentChars, note) => pageText(page(to, '', note)).length + sectionLead('content') + contentChars;
108
+ // The page is the longest that fits. Its length isn't monotone in its
109
+ // lines: the page that ends the file carries no stop note, the one at the
110
+ // line limit a shorter one than a stop, and one whose last line is cut a
111
+ // longer one. But every page carries `notes` and a longer page shows more,
112
+ // so once a page passes the limit with those alone, no longer one fits.
113
+ const shownLines = [];
114
+ let contentChars = 0;
115
+ let best = 0;
116
+ for (let i = first; i < last; i++) {
117
+ const line = shown(i);
118
+ shownLines.push(line);
119
+ // Each line after the first comes after a line feed.
120
+ contentChars += line.length + (i > first ? 1 : 0);
121
+ if (lengthAt(i + 1, contentChars, noteAt(i + 1)) <= maxChars)
122
+ best = shownLines.length;
123
+ else if (lengthAt(i + 1, contentChars, joined(notes)) > maxChars)
124
+ break;
125
+ }
126
+ if (best > 0) {
127
+ shownLines.length = best;
128
+ return pageAnswer(page(first + best, shownLines.join('\n'), noteAt(first + best)));
129
+ }
130
+ // No page fits with its first line as a page shows it: the loop tried that
131
+ // page first, since a read's limit is at least 1, and it is pageWith(top).
132
+ // So the line is cut shorter, at the most the page has room for, and ends
133
+ // the page: its note names where the line's rest starts and how long it is,
134
+ // since that comes next. Each cut is measured on the page itself, since its
135
+ // note names the cut. Every cut below `top` shows a marker. A longer cut
136
+ // adds a character of text at least, and the offsets only grow, but the
137
+ // rest's length is in the marker and in the note, so where it loses a digit
138
+ // the page loses two characters: it can be one character shorter than the
139
+ // page a cut before. The search takes the page as growing with the cut.
140
+ // Such a dip is one cut, and the next is many cuts away, so the longest cut
141
+ // that fits is the one the search finds or, past a dip, at most two cuts
142
+ // further, four code units at most, and those are tried too.
143
+ const pageWith = (chars) => page(first + 1, shown(first, chars), noteAt(first + 1, chars));
144
+ const fitsAt = (chars) => pageText(pageWith(chars)).length <= maxChars;
145
+ const top = Math.min(lines[first].length, MAX_LINE_CHARS);
146
+ let fits = 0;
147
+ let over = top;
148
+ while (over - fits > 1) {
149
+ const mid = Math.floor((fits + over) / 2);
150
+ if (fitsAt(mid))
151
+ fits = mid;
152
+ else
153
+ over = mid;
154
+ }
155
+ // The search ends with `fits + 1` measured as too long, or as `top`.
156
+ const found = fits;
157
+ for (let chars = found + 2; chars <= found + 4 && chars < top; chars++)
158
+ if (fitsAt(chars))
159
+ fits = chars;
160
+ return pageAnswer(pageWith(fits));
161
+ }
162
+ const splitsPair = (text, at) => at > 0 && at < text.length
163
+ && text.charCodeAt(at - 1) >= 0xd800 && text.charCodeAt(at - 1) <= 0xdbff
164
+ && text.charCodeAt(at) >= 0xdc00 && text.charCodeAt(at) <= 0xdfff;
165
+ /**
166
+ * Raw text from `offsetChars`, at most `limitChars` code units (one more to
167
+ * keep a surrogate pair whole), stopped sooner where its page's text would
168
+ * pass `maxChars`, if there is one.
169
+ */
170
+ export function characterPage(path, file, offsetChars, limitChars, maxChars, extraNotes = []) {
171
+ const text = file.raw;
172
+ const start = Math.min(offsetChars, text.length);
173
+ if (splitsPair(text, start)) {
174
+ return { success: false, isError: true, error: 'offsetChars splits a surrogate pair; use nextOffsetChars from the previous page.' };
175
+ }
176
+ let end = start + Math.min(limitChars, text.length - start);
177
+ if (splitsPair(text, end))
178
+ end++;
179
+ const notes = [...(file.utf8 ? [] : [NOT_UTF8]), ...extraNotes];
180
+ // A page that stops short of what it was asked for gives what's left of
181
+ // that as limitChars, so a reader that follows its notes ends where it
182
+ // asked to: at a cut line's end, for the rest a line page's note names.
183
+ const stoppedNote = (to, left) => `Stopped at character ${to} to stay within the ${maxChars}-character result limit; read the rest of what you asked for with offsetChars=${to} and limitChars=${left}.`;
184
+ const page = (to, note) => ({
185
+ path,
186
+ totalChars: text.length,
187
+ offsetChars: start,
188
+ nextOffsetChars: to < text.length ? to : null,
189
+ ...(note !== undefined ? { note } : {}),
190
+ content: text.slice(start, to),
191
+ });
192
+ const whole = page(end, joined(notes));
193
+ if (maxChars === undefined || pageText(whole).length <= maxChars)
194
+ return pageAnswer(whole);
195
+ // As many characters as fit beside the rest of the page, which takes the
196
+ // most with the largest offset it could stop at and the most left. A page
197
+ // moves by whole characters, a surrogate pair at once, so it never stops
198
+ // inside one.
199
+ const overhead = pageText({ ...page(end, joined([stoppedNote(end, end - start), ...notes])), nextOffsetChars: end, content: '' }).length + sectionLead('content');
200
+ const step = (at) => (splitsPair(text, at + 1) ? 2 : 1);
201
+ let to = Math.min(end, start + Math.max(0, maxChars - overhead));
202
+ if (splitsPair(text, to))
203
+ to--;
204
+ // A page always moves on, by one character at least, though a limit too
205
+ // small for the page's own fields and its first character then passes it:
206
+ // with room for one code unit, a surrogate pair is still taken whole. Where
207
+ // that reaches the page's own end, it holds all it was asked for, so it
208
+ // names no stop.
209
+ if (to === start)
210
+ to = Math.min(end, start + step(start));
211
+ return pageAnswer(page(to, joined([to < end ? stoppedNote(to, end - to) : null, ...notes])));
212
+ }
213
+ //# sourceMappingURL=read-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-page.js","sourceRoot":"","sources":["../../../../src/modules/workspace/read-page.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import type { ToolResult } from '../../types/events.js';
2
+ /**
3
+ * A value as a header line, a section's label or a listed line writes it: as
4
+ * it is, or, where it holds a line break or starts with a quote, as a JSON
5
+ * string. A path can hold a line break, and so can a note naming it or a line
6
+ * of a file grep matched; written as it is, it would start a line that reads
7
+ * as one of the answer's own. A value written as it is never starts with a
8
+ * quote, so one that does is always the quoted form, which gives back exactly
9
+ * what the value holds: without that, a value that is itself a JSON string,
10
+ * such as a line of a file reading `"a\nb"`, would read as the quoted form of
11
+ * another. JSON leaves NEL, U+2028 and U+2029 as they are, so they're escaped
12
+ * too.
13
+ */
14
+ export declare function shownValue(value: string | number | boolean | null): string;
15
+ /** A header line: a field's key and its value as shownValue writes it. */
16
+ export declare const fieldLine: (key: string, value: string | number | boolean | null) => string;
17
+ /** A section's text, its label and then its lines; an empty section is no text. */
18
+ export declare const sectionText: (label: string, body: string) => string;
19
+ /** The answer's text: its header lines and then its sections, each on lines of its own. */
20
+ export declare const answerText: (header: string[], sections?: string[]) => string;
21
+ /** How much a section with any lines adds to its answer's text before them: the line feed before its label, and the label's line. */
22
+ export declare const sectionLead: (label: string) => number;
23
+ /** A success whose text is what the model reads and whose fields reach a script as its structuredContent. */
24
+ export declare const textAnswer: (text: string, structured: Record<string, unknown>) => ToolResult;
25
+ /** A grep result: the matches of one version of a file. */
26
+ export interface GrepResult {
27
+ file: string;
28
+ state?: string;
29
+ version: string;
30
+ lineEndings?: string;
31
+ matches: Array<{
32
+ line: number;
33
+ text: string;
34
+ context?: string[];
35
+ }>;
36
+ }
37
+ /** What grep found, as its structuredContent holds it. */
38
+ export interface GrepAnswer {
39
+ pattern: string;
40
+ results: GrepResult[];
41
+ totalMatches: number;
42
+ note?: string;
43
+ skipped?: Array<{
44
+ file: string;
45
+ reason: string;
46
+ size?: number;
47
+ mimeType?: string;
48
+ }>;
49
+ incomplete?: Array<{
50
+ path: string;
51
+ reason: string;
52
+ }>;
53
+ }
54
+ /**
55
+ * grep's text: its pattern, match count and note, then a section for each
56
+ * version of a file it matched, labelled with the file, the version, the
57
+ * file's state where grep gives one and its line ends where it has them, then
58
+ * what it skipped and where its search was incomplete. As grep writes them, a
59
+ * match's line reads `N:text`, and where the call asked for context, each
60
+ * line around it reads `N-text`: matches whose context meets or overlaps are
61
+ * one run of lines, each line once, and `--` stands between runs with lines
62
+ * left out between them. Each match's context starts `contextBefore` lines
63
+ * before it, or at the file's first line. A line's text is written through
64
+ * shownValue, since more of the answer follows it.
65
+ */
66
+ export declare function grepText(answer: GrepAnswer, contextBefore: number): string;
67
+ //# sourceMappingURL=result-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-text.d.ts","sourceRoot":"","sources":["../../../../src/modules/workspace/result-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAcxD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAI1E;AAED,0EAA0E;AAC1E,eAAO,MAAM,SAAS,QAAS,MAAM,SAAS,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,KAAG,MAAwC,CAAC;AAE1H,mFAAmF;AACnF,eAAO,MAAM,WAAW,UAAW,MAAM,QAAQ,MAAM,KAAG,MAA0D,CAAC;AAErH,2FAA2F;AAC3F,eAAO,MAAM,UAAU,WAAY,MAAM,EAAE,aAAY,MAAM,EAAE,KAAQ,MACE,CAAC;AAE1E,qIAAqI;AACrI,eAAO,MAAM,WAAW,UAAW,MAAM,KAAG,MAAuC,CAAC;AAEpF,6GAA6G;AAC7G,eAAO,MAAM,UAAU,SAAU,MAAM,cAAc,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,UACd,CAAC;AAElE,2DAA2D;AAC3D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CACpE;AAED,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpF,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAsC1E"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The text a model reads of read's, grep's, edit's and edit_many's answers:
3
+ * a header of the answer's own fields, one `key: value` to a line in the
4
+ * fields' order, then its sections, each a `--- label ---` line and the lines
5
+ * under it, an empty one left out. The answer's fields themselves reach a
6
+ * script as its structuredContent (ToolResult.structured), and a page or a
7
+ * receipt is held to the call's result limit as this text measures it.
8
+ */
9
+ /** The characters Unicode says end a line (UAX #14's mandatory breaks): LF, VT, FF, CR, NEL, U+2028 and U+2029. */
10
+ const LINE_BREAK = /[\n\v\f\r\u0085\u2028\u2029]/;
11
+ /**
12
+ * A value as a header line, a section's label or a listed line writes it: as
13
+ * it is, or, where it holds a line break or starts with a quote, as a JSON
14
+ * string. A path can hold a line break, and so can a note naming it or a line
15
+ * of a file grep matched; written as it is, it would start a line that reads
16
+ * as one of the answer's own. A value written as it is never starts with a
17
+ * quote, so one that does is always the quoted form, which gives back exactly
18
+ * what the value holds: without that, a value that is itself a JSON string,
19
+ * such as a line of a file reading `"a\nb"`, would read as the quoted form of
20
+ * another. JSON leaves NEL, U+2028 and U+2029 as they are, so they're escaped
21
+ * too.
22
+ */
23
+ export function shownValue(value) {
24
+ if (typeof value !== 'string')
25
+ return String(value);
26
+ if (!value.startsWith('"') && !LINE_BREAK.test(value))
27
+ return value;
28
+ return JSON.stringify(value).replace(/[\u0085\u2028\u2029]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
29
+ }
30
+ /** A header line: a field's key and its value as shownValue writes it. */
31
+ export const fieldLine = (key, value) => `${key}: ${shownValue(value)}`;
32
+ /** A section's text, its label and then its lines; an empty section is no text. */
33
+ export const sectionText = (label, body) => (body === '' ? '' : `--- ${label} ---\n${body}`);
34
+ /** The answer's text: its header lines and then its sections, each on lines of its own. */
35
+ export const answerText = (header, sections = []) => [...header, ...sections.filter((section) => section !== '')].join('\n');
36
+ /** How much a section with any lines adds to its answer's text before them: the line feed before its label, and the label's line. */
37
+ export const sectionLead = (label) => `\n--- ${label} ---\n`.length;
38
+ /** A success whose text is what the model reads and whose fields reach a script as its structuredContent. */
39
+ export const textAnswer = (text, structured) => ({ success: true, data: [{ type: 'text', text }], structured });
40
+ /**
41
+ * grep's text: its pattern, match count and note, then a section for each
42
+ * version of a file it matched, labelled with the file, the version, the
43
+ * file's state where grep gives one and its line ends where it has them, then
44
+ * what it skipped and where its search was incomplete. As grep writes them, a
45
+ * match's line reads `N:text`, and where the call asked for context, each
46
+ * line around it reads `N-text`: matches whose context meets or overlaps are
47
+ * one run of lines, each line once, and `--` stands between runs with lines
48
+ * left out between them. Each match's context starts `contextBefore` lines
49
+ * before it, or at the file's first line. A line's text is written through
50
+ * shownValue, since more of the answer follows it.
51
+ */
52
+ export function grepText(answer, contextBefore) {
53
+ const header = [fieldLine('pattern', answer.pattern), fieldLine('totalMatches', answer.totalMatches)];
54
+ if (answer.note !== undefined)
55
+ header.push(fieldLine('note', answer.note));
56
+ const sections = answer.results.map((result) => {
57
+ const label = `${shownValue(result.file)}: ${result.version}`
58
+ + (result.state !== undefined ? ` (${result.state})` : '')
59
+ + (result.lineEndings !== undefined ? `; lineEndings: ${result.lineEndings}` : '');
60
+ const lines = [];
61
+ const matched = new Set(result.matches.map((match) => match.line));
62
+ // The last line written: matches come in line order, so a window that
63
+ // meets or overlaps the run before it goes on from there.
64
+ let last = 0;
65
+ for (const match of result.matches) {
66
+ if (match.context === undefined) {
67
+ lines.push(`${String(match.line).padStart(6)}:${shownValue(match.text)}`);
68
+ continue;
69
+ }
70
+ const first = Math.max(1, match.line - contextBefore);
71
+ match.context.forEach((text, k) => {
72
+ const n = first + k;
73
+ if (n <= last)
74
+ return;
75
+ if (last > 0 && n > last + 1)
76
+ lines.push('--');
77
+ lines.push(`${String(n).padStart(6)}${matched.has(n) ? ':' : '-'}${shownValue(text)}`);
78
+ last = n;
79
+ });
80
+ }
81
+ return sectionText(label, lines.join('\n'));
82
+ });
83
+ if (answer.skipped !== undefined) {
84
+ sections.push(sectionText('skipped', answer.skipped.map((skip) => {
85
+ const facts = [...(skip.size !== undefined ? [`${skip.size} bytes`] : []), ...(skip.mimeType !== undefined ? [skip.mimeType] : [])];
86
+ return `${shownValue(skip.file)}: ${shownValue(skip.reason)}${facts.length ? ` (${facts.join(', ')})` : ''}`;
87
+ }).join('\n')));
88
+ }
89
+ if (answer.incomplete !== undefined) {
90
+ sections.push(sectionText('incomplete', answer.incomplete.map((gap) => `${shownValue(gap.path)}: ${shownValue(gap.reason)}`).join('\n')));
91
+ }
92
+ return answerText(header, sections);
93
+ }
94
+ //# sourceMappingURL=result-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-text.js","sourceRoot":"","sources":["../../../../src/modules/workspace/result-text.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,35 @@
1
1
  /**
2
- * Lines numbered the way `cat -n` numbers them, with a long one cut, and a
3
- * string's length as JSON, which a budget for a stored result counts. edit's
4
- * answers use them.
2
+ * A stored file's text as read shows it and grep searches it, and lines
3
+ * numbered the way `cat -n` numbers them, with a long one cut. Read, grep and
4
+ * edit's answers use them.
5
5
  */
6
6
  /** Whether the bytes open with a UTF-8 byte order mark. */
7
7
  export declare function hasBom(bytes: Uint8Array): boolean;
8
+ export interface FileText {
9
+ /** The bytes decoded as UTF-8, as character paging reads them: a BOM and every CR kept. */
10
+ raw: string;
11
+ /**
12
+ * Its lines, as read shows them and grep searches them: without the BOM, a
13
+ * CR before a line feed dropped, and a final line end ending the last line
14
+ * rather than starting an empty one. So "a\r\nb\r\n" is two lines, and an
15
+ * empty text has none. A line whose own text ends in a CR keeps its line
16
+ * end's CR too, as stored: an edit reads a CR before a line feed in its old
17
+ * text as part of the line end, so text copied from "a\r" and the next line
18
+ * would lose the CR that is the line's own.
19
+ */
20
+ lines: string[];
21
+ /** Where each line starts in `raw`. */
22
+ starts: number[];
23
+ bom: boolean;
24
+ /** How many line ends were CRLF, and how many a bare LF. */
25
+ crlfEnds: number;
26
+ lfEnds: number;
27
+ /** How many lines with a CRLF end keep its CR, since their own text ends in a CR. */
28
+ crKept: number;
29
+ /** Whether the bytes are valid UTF-8; where they aren't, `raw` holds U+FFFD. */
30
+ utf8: boolean;
31
+ }
32
+ export declare function fileText(bytes: Buffer): FileText;
8
33
  /** How long a line read shows before it cuts the line. */
9
34
  export declare const MAX_LINE_CHARS = 2000;
10
35
  /**
@@ -16,17 +41,9 @@ export declare function safeCut(value: string, at: number): number;
16
41
  /**
17
42
  * One line numbered the way `cat -n` numbers it: a six-wide number, a tab and
18
43
  * the line. A line longer than `maxChars` is cut, saying how many characters
19
- * it holds past the cut, and `readOn` adds how to read them.
44
+ * it holds past the cut, or, where `readOn` is given, how to read them.
20
45
  */
21
46
  export declare function numberedLine(line: string, n: number, maxChars?: number, readOn?: (cut: number) => string): string;
22
47
  /** Lines numbered from `start`, each as numberedLine shows it. */
23
48
  export declare function numbered(lines: string[], start: number, maxChars?: number): string;
24
- /**
25
- * How many characters a string takes inside JSON's quotes, as JSON.stringify
26
- * writes it: a quote, a backslash or a control character is escaped, and so
27
- * is a lone surrogate.
28
- */
29
- export declare function jsonLength(value: string): number;
30
- /** The JSON length of the code unit at `i`, as jsonLength counts it. */
31
- export declare function jsonCharLength(value: string, i: number): number;
32
49
  //# sourceMappingURL=text.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../../src/modules/workspace/text.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,2DAA2D;AAC3D,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAEjD;AAED,0DAA0D;AAC1D,eAAO,MAAM,cAAc,OAAO,CAAC;AAEnC;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAIzD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,SAAiB,EAAE,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAKzH;AAED,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAiB,GAAG,MAAM,CAE1F;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAa/D"}
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../../src/modules/workspace/text.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,2DAA2D;AAC3D,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAEjD;AAED,MAAM,WAAW,QAAQ;IACvB,2FAA2F;IAC3F,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;OAQG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,uCAAuC;IACvC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,MAAM,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAwBhD;AAED,0DAA0D;AAC1D,eAAO,MAAM,cAAc,OAAO,CAAC;AAEnC;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAIzD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,SAAiB,EAAE,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAKzH;AAED,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAiB,GAAG,MAAM,CAE1F"}
@@ -1,12 +1,42 @@
1
+ import { isUtf8 } from 'node:buffer';
1
2
  /**
2
- * Lines numbered the way `cat -n` numbers them, with a long one cut, and a
3
- * string's length as JSON, which a budget for a stored result counts. edit's
4
- * answers use them.
3
+ * A stored file's text as read shows it and grep searches it, and lines
4
+ * numbered the way `cat -n` numbers them, with a long one cut. Read, grep and
5
+ * edit's answers use them.
5
6
  */
6
7
  /** Whether the bytes open with a UTF-8 byte order mark. */
7
8
  export function hasBom(bytes) {
8
9
  return bytes.length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf;
9
10
  }
11
+ export function fileText(bytes) {
12
+ const raw = bytes.toString('utf-8');
13
+ const bom = hasBom(bytes);
14
+ const lines = [];
15
+ const starts = [];
16
+ let crlfEnds = 0;
17
+ let lfEnds = 0;
18
+ let crKept = 0;
19
+ // A BOM decodes to the single code unit U+FEFF.
20
+ for (let at = bom ? 1 : 0; at < raw.length;) {
21
+ const lf = raw.indexOf('\n', at);
22
+ starts.push(at);
23
+ if (lf === -1) {
24
+ lines.push(raw.slice(at));
25
+ break;
26
+ }
27
+ const crlf = lf > at && raw.charCodeAt(lf - 1) === 13;
28
+ if (crlf)
29
+ crlfEnds++;
30
+ else
31
+ lfEnds++;
32
+ const keepsCr = crlf && lf - 1 > at && raw.charCodeAt(lf - 2) === 13;
33
+ if (keepsCr)
34
+ crKept++;
35
+ lines.push(raw.slice(at, crlf && !keepsCr ? lf - 1 : lf));
36
+ at = lf + 1;
37
+ }
38
+ return { raw, lines, starts, bom, crlfEnds, lfEnds, crKept, utf8: isUtf8(bytes) };
39
+ }
10
40
  /** How long a line read shows before it cuts the line. */
11
41
  export const MAX_LINE_CHARS = 2000;
12
42
  /**
@@ -23,45 +53,17 @@ export function safeCut(value, at) {
23
53
  /**
24
54
  * One line numbered the way `cat -n` numbers it: a six-wide number, a tab and
25
55
  * the line. A line longer than `maxChars` is cut, saying how many characters
26
- * it holds past the cut, and `readOn` adds how to read them.
56
+ * it holds past the cut, or, where `readOn` is given, how to read them.
27
57
  */
28
58
  export function numberedLine(line, n, maxChars = MAX_LINE_CHARS, readOn) {
29
59
  const number = String(n).padStart(6);
30
60
  if (line.length <= maxChars)
31
61
  return `${number}\t${line}`;
32
62
  const cut = safeCut(line, maxChars);
33
- return `${number}\t${line.slice(0, cut)} [… ${line.length - cut} more chars${readOn ? `; ${readOn(cut)}` : ''}]`;
63
+ return `${number}\t${line.slice(0, cut)} [… ${readOn ? readOn(cut) : `${line.length - cut} more chars`}]`;
34
64
  }
35
65
  /** Lines numbered from `start`, each as numberedLine shows it. */
36
66
  export function numbered(lines, start, maxChars = MAX_LINE_CHARS) {
37
67
  return lines.map((line, i) => numberedLine(line, start + i, maxChars)).join('\n');
38
68
  }
39
- /**
40
- * How many characters a string takes inside JSON's quotes, as JSON.stringify
41
- * writes it: a quote, a backslash or a control character is escaped, and so
42
- * is a lone surrogate.
43
- */
44
- export function jsonLength(value) {
45
- let length = 0;
46
- for (let i = 0; i < value.length; i++)
47
- length += jsonCharLength(value, i);
48
- return length;
49
- }
50
- /** The JSON length of the code unit at `i`, as jsonLength counts it. */
51
- export function jsonCharLength(value, i) {
52
- const c = value.charCodeAt(i);
53
- if (c === 0x22 || c === 0x5c)
54
- return 2;
55
- if (c < 0x20)
56
- return c === 0x08 || c === 0x09 || c === 0x0a || c === 0x0c || c === 0x0d ? 2 : 6;
57
- if (c >= 0xd800 && c <= 0xdbff) {
58
- const next = value.charCodeAt(i + 1);
59
- return next >= 0xdc00 && next <= 0xdfff ? 1 : 6;
60
- }
61
- if (c >= 0xdc00 && c <= 0xdfff) {
62
- const before = i > 0 ? value.charCodeAt(i - 1) : 0;
63
- return before >= 0xd800 && before <= 0xdbff ? 1 : 6;
64
- }
65
- return 1;
66
- }
67
69
  //# sourceMappingURL=text.js.map