@bastani/atomic 0.9.8 → 0.9.9-alpha.2

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 (814) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +12 -0
  3. package/dist/builtin/cursor/package.json +3 -3
  4. package/dist/builtin/intercom/CHANGELOG.md +13 -0
  5. package/dist/builtin/intercom/package.json +3 -3
  6. package/dist/builtin/mcp/CHANGELOG.md +13 -0
  7. package/dist/builtin/mcp/package.json +4 -4
  8. package/dist/builtin/subagents/CHANGELOG.md +13 -0
  9. package/dist/builtin/subagents/package.json +5 -5
  10. package/dist/builtin/subagents/src/runs/background/async-execution-chain.ts +2 -0
  11. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +7 -1
  12. package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +2 -0
  13. package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +8 -2
  14. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +2 -0
  15. package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +2 -0
  16. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +2 -1
  17. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +2 -0
  18. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +2 -0
  19. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-resume.ts +2 -0
  20. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +2 -0
  21. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +6 -0
  22. package/dist/builtin/subagents/src/shared/types-config.ts +4 -0
  23. package/dist/builtin/subagents/src/shared/types-depth.ts +29 -2
  24. package/dist/builtin/web-access/CHANGELOG.md +13 -0
  25. package/dist/builtin/web-access/package.json +3 -3
  26. package/dist/builtin/web-access/web-search-tool.ts +1 -2
  27. package/dist/builtin/workflows/CHANGELOG.md +29 -0
  28. package/dist/builtin/workflows/README.md +17 -8
  29. package/dist/builtin/workflows/builtin/goal-prompts.ts +8 -2
  30. package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +26 -9
  31. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +1 -1
  32. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +8 -3
  33. package/dist/builtin/workflows/builtin/ralph-runner.ts +2 -0
  34. package/dist/builtin/workflows/builtin/shared-prompts.ts +24 -1
  35. package/dist/builtin/workflows/package.json +3 -3
  36. package/dist/builtin/workflows/src/durable/backend.ts +53 -15
  37. package/dist/builtin/workflows/src/durable/completed-catalog.ts +245 -0
  38. package/dist/builtin/workflows/src/durable/completed-inspection.ts +213 -0
  39. package/dist/builtin/workflows/src/durable/dbos-backend.ts +120 -170
  40. package/dist/builtin/workflows/src/durable/dbos-envelope.ts +84 -8
  41. package/dist/builtin/workflows/src/durable/dbos-metadata.ts +98 -0
  42. package/dist/builtin/workflows/src/durable/dbos-tombstone.ts +27 -0
  43. package/dist/builtin/workflows/src/durable/factory.ts +1 -1
  44. package/dist/builtin/workflows/src/durable/file-backend.ts +247 -241
  45. package/dist/builtin/workflows/src/durable/file-lock.ts +153 -0
  46. package/dist/builtin/workflows/src/durable/file-state.ts +104 -0
  47. package/dist/builtin/workflows/src/durable/format-version.ts +12 -0
  48. package/dist/builtin/workflows/src/durable/index.ts +12 -0
  49. package/dist/builtin/workflows/src/durable/resume-catalog.ts +16 -27
  50. package/dist/builtin/workflows/src/durable/resume-eligibility.ts +20 -0
  51. package/dist/builtin/workflows/src/durable/resume-runtime.ts +29 -29
  52. package/dist/builtin/workflows/src/durable/scoped-backend.ts +20 -3
  53. package/dist/builtin/workflows/src/durable/stage-primitive.ts +29 -8
  54. package/dist/builtin/workflows/src/durable/types.ts +3 -0
  55. package/dist/builtin/workflows/src/engine/primitives/task.ts +1 -0
  56. package/dist/builtin/workflows/src/engine/run.ts +14 -14
  57. package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +39 -9
  58. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +10 -0
  59. package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +133 -0
  60. package/dist/builtin/workflows/src/extension/runtime.ts +15 -40
  61. package/dist/builtin/workflows/src/extension/wiring.ts +9 -6
  62. package/dist/builtin/workflows/src/extension/workflow-durable-resume-command.ts +226 -0
  63. package/dist/builtin/workflows/src/extension/workflow-prompts.ts +1 -0
  64. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +66 -112
  65. package/dist/builtin/workflows/src/extension/workflow-schema.ts +5 -5
  66. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +16 -1
  67. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +50 -43
  68. package/dist/builtin/workflows/src/runs/foreground/executor-direct-output.ts +91 -0
  69. package/dist/builtin/workflows/src/runs/foreground/executor-direct.ts +95 -46
  70. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +3 -1
  71. package/dist/builtin/workflows/src/runs/foreground/executor-task-prompts.ts +1 -1
  72. package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +3 -0
  73. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +1 -1
  74. package/dist/builtin/workflows/src/runs/foreground/stage-runner-options.ts +36 -7
  75. package/dist/builtin/workflows/src/runs/shared/worktree-cache-lifecycle.ts +25 -0
  76. package/dist/builtin/workflows/src/runs/shared/worktree-cwd.ts +103 -0
  77. package/dist/builtin/workflows/src/runs/shared/worktree-generation.ts +57 -0
  78. package/dist/builtin/workflows/src/runs/shared/worktree-git.ts +134 -8
  79. package/dist/builtin/workflows/src/runs/shared/worktree.ts +2 -0
  80. package/dist/builtin/workflows/src/shared/resumable-workflow-notices.ts +43 -0
  81. package/dist/builtin/workflows/src/shared/serializable.ts +1 -1
  82. package/dist/builtin/workflows/src/shared/timing.ts +4 -0
  83. package/dist/builtin/workflows/src/shared/types.ts +5 -3
  84. package/dist/builtin/workflows/src/tui/graph-view-constants.ts +1 -0
  85. package/dist/builtin/workflows/src/tui/graph-view-input.ts +24 -14
  86. package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +43 -13
  87. package/dist/bun/cli.d.ts.map +1 -1
  88. package/dist/bun/internal-intercom-broker.d.ts.map +1 -1
  89. package/dist/bun/register-bedrock.d.ts.map +1 -1
  90. package/dist/bun/restore-sandbox-env.d.ts.map +1 -1
  91. package/dist/bun/split-loader.d.ts.map +1 -1
  92. package/dist/cli/args.d.ts.map +1 -1
  93. package/dist/cli/config-selector.d.ts.map +1 -1
  94. package/dist/cli/file-processor.d.ts.map +1 -1
  95. package/dist/cli/initial-message.d.ts.map +1 -1
  96. package/dist/cli/list-models.d.ts.map +1 -1
  97. package/dist/cli/project-trust.d.ts.map +1 -1
  98. package/dist/cli/session-picker.d.ts.map +1 -1
  99. package/dist/cli/startup-ui.d.ts.map +1 -1
  100. package/dist/cli.d.ts.map +1 -1
  101. package/dist/config-self-update.d.ts.map +1 -1
  102. package/dist/config.d.ts.map +1 -1
  103. package/dist/core/agent-session-accessors.d.ts.map +1 -1
  104. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  105. package/dist/core/agent-session-auto-compaction.js +5 -7
  106. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  107. package/dist/core/agent-session-bash.d.ts.map +1 -1
  108. package/dist/core/agent-session-compaction.d.ts +6 -34
  109. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  110. package/dist/core/agent-session-compaction.js +95 -233
  111. package/dist/core/agent-session-compaction.js.map +1 -1
  112. package/dist/core/agent-session-events.d.ts.map +1 -1
  113. package/dist/core/agent-session-export.d.ts.map +1 -1
  114. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  115. package/dist/core/agent-session-message-queue.d.ts +0 -4
  116. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  117. package/dist/core/agent-session-message-queue.js +1 -5
  118. package/dist/core/agent-session-message-queue.js.map +1 -1
  119. package/dist/core/agent-session-methods.d.ts +5 -6
  120. package/dist/core/agent-session-methods.d.ts.map +1 -1
  121. package/dist/core/agent-session-methods.js.map +1 -1
  122. package/dist/core/agent-session-models.d.ts.map +1 -1
  123. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  124. package/dist/core/agent-session-retry.d.ts.map +1 -1
  125. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  126. package/dist/core/agent-session-services.d.ts.map +1 -1
  127. package/dist/core/agent-session-skill-block.d.ts.map +1 -1
  128. package/dist/core/agent-session-state.d.ts.map +1 -1
  129. package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
  130. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  131. package/dist/core/agent-session-tree.d.ts.map +1 -1
  132. package/dist/core/agent-session-types.d.ts +2 -12
  133. package/dist/core/agent-session-types.d.ts.map +1 -1
  134. package/dist/core/agent-session-types.js.map +1 -1
  135. package/dist/core/agent-session.d.ts.map +1 -1
  136. package/dist/core/anthropic-thinking-guard.d.ts.map +1 -1
  137. package/dist/core/async/format.d.ts.map +1 -1
  138. package/dist/core/async/job-manager.d.ts.map +1 -1
  139. package/dist/core/async/session-manager.d.ts.map +1 -1
  140. package/dist/core/async/types.d.ts.map +1 -1
  141. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  142. package/dist/core/auth-guidance.d.ts.map +1 -1
  143. package/dist/core/auth-storage-backends.d.ts.map +1 -1
  144. package/dist/core/auth-storage.d.ts +4 -0
  145. package/dist/core/auth-storage.d.ts.map +1 -1
  146. package/dist/core/bash-executor.d.ts.map +1 -1
  147. package/dist/core/builtin-packages.d.ts.map +1 -1
  148. package/dist/core/codex-fast-mode.d.ts.map +1 -1
  149. package/dist/core/compaction/branch-summarization.d.ts +1 -1
  150. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  151. package/dist/core/compaction/branch-summarization.js +1 -2
  152. package/dist/core/compaction/branch-summarization.js.map +1 -1
  153. package/dist/core/compaction/compaction-boundary.d.ts +9 -0
  154. package/dist/core/compaction/compaction-boundary.d.ts.map +1 -0
  155. package/dist/core/compaction/compaction-boundary.js +115 -0
  156. package/dist/core/compaction/compaction-boundary.js.map +1 -0
  157. package/dist/core/compaction/compaction-parameters.d.ts +5 -0
  158. package/dist/core/compaction/compaction-parameters.d.ts.map +1 -0
  159. package/dist/core/compaction/compaction-parameters.js +27 -0
  160. package/dist/core/compaction/compaction-parameters.js.map +1 -0
  161. package/dist/core/compaction/compaction-runner.d.ts +14 -0
  162. package/dist/core/compaction/compaction-runner.d.ts.map +1 -0
  163. package/dist/core/compaction/compaction-runner.js +29 -0
  164. package/dist/core/compaction/compaction-runner.js.map +1 -0
  165. package/dist/core/compaction/compaction-types.d.ts +79 -0
  166. package/dist/core/compaction/compaction-types.d.ts.map +1 -0
  167. package/dist/core/compaction/compaction-types.js +6 -0
  168. package/dist/core/compaction/compaction-types.js.map +1 -0
  169. package/dist/core/compaction/compaction.d.ts.map +1 -1
  170. package/dist/core/compaction/deleted-ranges.d.ts +6 -0
  171. package/dist/core/compaction/deleted-ranges.d.ts.map +1 -0
  172. package/dist/core/compaction/deleted-ranges.js +134 -0
  173. package/dist/core/compaction/deleted-ranges.js.map +1 -0
  174. package/dist/core/compaction/index.d.ts +7 -1
  175. package/dist/core/compaction/index.d.ts.map +1 -1
  176. package/dist/core/compaction/index.js +7 -1
  177. package/dist/core/compaction/index.js.map +1 -1
  178. package/dist/core/compaction/range-planner.d.ts +21 -0
  179. package/dist/core/compaction/range-planner.d.ts.map +1 -0
  180. package/dist/core/compaction/range-planner.js +145 -0
  181. package/dist/core/compaction/range-planner.js.map +1 -0
  182. package/dist/core/compaction/transcript-serialization.d.ts +11 -0
  183. package/dist/core/compaction/transcript-serialization.d.ts.map +1 -0
  184. package/dist/core/compaction/transcript-serialization.js +104 -0
  185. package/dist/core/compaction/transcript-serialization.js.map +1 -0
  186. package/dist/core/compaction/utils.d.ts.map +1 -1
  187. package/dist/core/context-window.d.ts.map +1 -1
  188. package/dist/core/copilot-anthropic-sse-repair.d.ts.map +1 -1
  189. package/dist/core/copilot-errors.d.ts.map +1 -1
  190. package/dist/core/copilot-gemini-payload-sanitizer.d.ts.map +1 -1
  191. package/dist/core/copilot-gemini-reasoning.d.ts.map +1 -1
  192. package/dist/core/copilot-gemini-tool-arguments.d.ts.map +1 -1
  193. package/dist/core/copilot-hosts.d.ts.map +1 -1
  194. package/dist/core/copilot-model-catalog.d.ts.map +1 -1
  195. package/dist/core/copilot-model-static-fallbacks.d.ts.map +1 -1
  196. package/dist/core/copilot-model-synthesis.d.ts.map +1 -1
  197. package/dist/core/defaults.d.ts.map +1 -1
  198. package/dist/core/diagnostics.d.ts.map +1 -1
  199. package/dist/core/event-bus.d.ts.map +1 -1
  200. package/dist/core/exec.d.ts.map +1 -1
  201. package/dist/core/experimental.d.ts.map +1 -1
  202. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
  203. package/dist/core/export-html/index.d.ts.map +1 -1
  204. package/dist/core/export-html/template-script.d.ts.map +1 -1
  205. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  206. package/dist/core/extensions/agent-events.d.ts.map +1 -1
  207. package/dist/core/extensions/api-types.d.ts.map +1 -1
  208. package/dist/core/extensions/command-types.d.ts.map +1 -1
  209. package/dist/core/extensions/context-types.d.ts +2 -2
  210. package/dist/core/extensions/context-types.d.ts.map +1 -1
  211. package/dist/core/extensions/context-types.js.map +1 -1
  212. package/dist/core/extensions/event-results.d.ts +2 -2
  213. package/dist/core/extensions/event-results.d.ts.map +1 -1
  214. package/dist/core/extensions/event-results.js.map +1 -1
  215. package/dist/core/extensions/event-types.d.ts.map +1 -1
  216. package/dist/core/extensions/index.d.ts.map +1 -1
  217. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  218. package/dist/core/extensions/loader-core.d.ts.map +1 -1
  219. package/dist/core/extensions/loader-discovery.d.ts.map +1 -1
  220. package/dist/core/extensions/loader-resources.d.ts.map +1 -1
  221. package/dist/core/extensions/loader-runtime.d.ts.map +1 -1
  222. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  223. package/dist/core/extensions/loader.d.ts.map +1 -1
  224. package/dist/core/extensions/message-types.d.ts.map +1 -1
  225. package/dist/core/extensions/provider-types.d.ts.map +1 -1
  226. package/dist/core/extensions/reactive-widget.d.ts.map +1 -1
  227. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  228. package/dist/core/extensions/runner-events.d.ts.map +1 -1
  229. package/dist/core/extensions/runner-handlers.d.ts.map +1 -1
  230. package/dist/core/extensions/runner-project-trust.d.ts.map +1 -1
  231. package/dist/core/extensions/runner-registries.d.ts.map +1 -1
  232. package/dist/core/extensions/runner-shortcuts.d.ts.map +1 -1
  233. package/dist/core/extensions/runner-ui.d.ts.map +1 -1
  234. package/dist/core/extensions/runner.d.ts.map +1 -1
  235. package/dist/core/extensions/runtime-types.d.ts.map +1 -1
  236. package/dist/core/extensions/session-events.d.ts +7 -7
  237. package/dist/core/extensions/session-events.d.ts.map +1 -1
  238. package/dist/core/extensions/session-events.js.map +1 -1
  239. package/dist/core/extensions/tool-events.d.ts.map +1 -1
  240. package/dist/core/extensions/tool-types.d.ts.map +1 -1
  241. package/dist/core/extensions/types.d.ts.map +1 -1
  242. package/dist/core/extensions/ui-types.d.ts.map +1 -1
  243. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  244. package/dist/core/flattened-tool-arguments.d.ts.map +1 -1
  245. package/dist/core/footer-data-provider.d.ts.map +1 -1
  246. package/dist/core/http-dispatcher.d.ts.map +1 -1
  247. package/dist/core/index.d.ts +1 -1
  248. package/dist/core/index.d.ts.map +1 -1
  249. package/dist/core/index.js.map +1 -1
  250. package/dist/core/keybindings.d.ts.map +1 -1
  251. package/dist/core/messages.d.ts +4 -0
  252. package/dist/core/messages.d.ts.map +1 -1
  253. package/dist/core/messages.js +11 -0
  254. package/dist/core/messages.js.map +1 -1
  255. package/dist/core/model-registry-auth.d.ts.map +1 -1
  256. package/dist/core/model-registry-builtins.d.ts.map +1 -1
  257. package/dist/core/model-registry-builtins.js.map +1 -1
  258. package/dist/core/model-registry-custom-loader.d.ts.map +1 -1
  259. package/dist/core/model-registry-custom-loader.js.map +1 -1
  260. package/dist/core/model-registry-dynamic.d.ts.map +1 -1
  261. package/dist/core/model-registry-loader.d.ts.map +1 -1
  262. package/dist/core/model-registry-schemas.d.ts +65 -13
  263. package/dist/core/model-registry-schemas.d.ts.map +1 -1
  264. package/dist/core/model-registry-schemas.js +5 -1
  265. package/dist/core/model-registry-schemas.js.map +1 -1
  266. package/dist/core/model-registry-types.d.ts.map +1 -1
  267. package/dist/core/model-registry.d.ts.map +1 -1
  268. package/dist/core/model-resolver-cli.d.ts.map +1 -1
  269. package/dist/core/model-resolver-defaults.d.ts.map +1 -1
  270. package/dist/core/model-resolver-initial.d.ts.map +1 -1
  271. package/dist/core/model-resolver-patterns.d.ts.map +1 -1
  272. package/dist/core/model-resolver-scope.d.ts.map +1 -1
  273. package/dist/core/model-resolver-types.d.ts.map +1 -1
  274. package/dist/core/model-resolver.d.ts.map +1 -1
  275. package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
  276. package/dist/core/output-guard.d.ts.map +1 -1
  277. package/dist/core/package-manager-auto-resources.d.ts.map +1 -1
  278. package/dist/core/package-manager-command.d.ts.map +1 -1
  279. package/dist/core/package-manager-constants.d.ts.map +1 -1
  280. package/dist/core/package-manager-env.d.ts.map +1 -1
  281. package/dist/core/package-manager-git.d.ts.map +1 -1
  282. package/dist/core/package-manager-manifest.d.ts.map +1 -1
  283. package/dist/core/package-manager-npm.d.ts.map +1 -1
  284. package/dist/core/package-manager-operations.d.ts.map +1 -1
  285. package/dist/core/package-manager-paths.d.ts.map +1 -1
  286. package/dist/core/package-manager-progress.d.ts.map +1 -1
  287. package/dist/core/package-manager-resolver.d.ts.map +1 -1
  288. package/dist/core/package-manager-resource-accumulator.d.ts.map +1 -1
  289. package/dist/core/package-manager-resource-collector.d.ts.map +1 -1
  290. package/dist/core/package-manager-resource-files.d.ts.map +1 -1
  291. package/dist/core/package-manager-resource-patterns.d.ts.map +1 -1
  292. package/dist/core/package-manager-settings.d.ts.map +1 -1
  293. package/dist/core/package-manager-source.d.ts.map +1 -1
  294. package/dist/core/package-manager-types.d.ts.map +1 -1
  295. package/dist/core/package-manager.d.ts.map +1 -1
  296. package/dist/core/project-trust.d.ts.map +1 -1
  297. package/dist/core/prompt-templates-async.d.ts.map +1 -1
  298. package/dist/core/prompt-templates.d.ts.map +1 -1
  299. package/dist/core/provider-attribution.d.ts.map +1 -1
  300. package/dist/core/provider-context-usage.d.ts.map +1 -1
  301. package/dist/core/provider-context-usage.js +1 -2
  302. package/dist/core/provider-context-usage.js.map +1 -1
  303. package/dist/core/provider-display-names.d.ts.map +1 -1
  304. package/dist/core/resolve-config-value.d.ts.map +1 -1
  305. package/dist/core/resource-loader-assets.d.ts.map +1 -1
  306. package/dist/core/resource-loader-context-files.d.ts.map +1 -1
  307. package/dist/core/resource-loader-core.d.ts.map +1 -1
  308. package/dist/core/resource-loader-discovery.d.ts.map +1 -1
  309. package/dist/core/resource-loader-extensions.d.ts.map +1 -1
  310. package/dist/core/resource-loader-helpers.d.ts.map +1 -1
  311. package/dist/core/resource-loader-internals.d.ts.map +1 -1
  312. package/dist/core/resource-loader-package-resources.d.ts.map +1 -1
  313. package/dist/core/resource-loader-paths.d.ts.map +1 -1
  314. package/dist/core/resource-loader-reload.d.ts.map +1 -1
  315. package/dist/core/resource-loader-source-info.d.ts.map +1 -1
  316. package/dist/core/resource-loader-types.d.ts.map +1 -1
  317. package/dist/core/resource-loader.d.ts.map +1 -1
  318. package/dist/core/sdk-exports.d.ts.map +1 -1
  319. package/dist/core/sdk-types.d.ts.map +1 -1
  320. package/dist/core/sdk.d.ts.map +1 -1
  321. package/dist/core/session-cwd.d.ts.map +1 -1
  322. package/dist/core/session-entry-normalization.d.ts.map +1 -1
  323. package/dist/core/session-manager-archive.d.ts +2 -1
  324. package/dist/core/session-manager-archive.d.ts.map +1 -1
  325. package/dist/core/session-manager-archive.js +2 -2
  326. package/dist/core/session-manager-archive.js.map +1 -1
  327. package/dist/core/session-manager-classification.d.ts +9 -0
  328. package/dist/core/session-manager-classification.d.ts.map +1 -0
  329. package/dist/core/session-manager-classification.js +37 -0
  330. package/dist/core/session-manager-classification.js.map +1 -0
  331. package/dist/core/session-manager-core.d.ts +4 -4
  332. package/dist/core/session-manager-core.d.ts.map +1 -1
  333. package/dist/core/session-manager-core.js +13 -8
  334. package/dist/core/session-manager-core.js.map +1 -1
  335. package/dist/core/session-manager-entries.d.ts +4 -3
  336. package/dist/core/session-manager-entries.d.ts.map +1 -1
  337. package/dist/core/session-manager-entries.js +12 -10
  338. package/dist/core/session-manager-entries.js.map +1 -1
  339. package/dist/core/session-manager-history.d.ts +6 -23
  340. package/dist/core/session-manager-history.d.ts.map +1 -1
  341. package/dist/core/session-manager-history.js +23 -256
  342. package/dist/core/session-manager-history.js.map +1 -1
  343. package/dist/core/session-manager-list.d.ts.map +1 -1
  344. package/dist/core/session-manager-list.js +3 -3
  345. package/dist/core/session-manager-list.js.map +1 -1
  346. package/dist/core/session-manager-migrations.d.ts.map +1 -1
  347. package/dist/core/session-manager-paths.d.ts.map +1 -1
  348. package/dist/core/session-manager-storage.d.ts +1 -1
  349. package/dist/core/session-manager-storage.d.ts.map +1 -1
  350. package/dist/core/session-manager-storage.js +3 -2
  351. package/dist/core/session-manager-storage.js.map +1 -1
  352. package/dist/core/session-manager-types.d.ts +14 -15
  353. package/dist/core/session-manager-types.d.ts.map +1 -1
  354. package/dist/core/session-manager-types.js.map +1 -1
  355. package/dist/core/session-manager-validation.d.ts.map +1 -1
  356. package/dist/core/session-manager.d.ts +3 -2
  357. package/dist/core/session-manager.d.ts.map +1 -1
  358. package/dist/core/session-manager.js +1 -1
  359. package/dist/core/session-manager.js.map +1 -1
  360. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  361. package/dist/core/settings-manager-core.d.ts.map +1 -1
  362. package/dist/core/settings-manager-internals.d.ts.map +1 -1
  363. package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
  364. package/dist/core/settings-manager-ui-accessors.d.ts.map +1 -1
  365. package/dist/core/settings-manager.d.ts.map +1 -1
  366. package/dist/core/settings-merge.d.ts.map +1 -1
  367. package/dist/core/settings-storage.d.ts.map +1 -1
  368. package/dist/core/settings-types.d.ts.map +1 -1
  369. package/dist/core/skills-async.d.ts.map +1 -1
  370. package/dist/core/skills.d.ts.map +1 -1
  371. package/dist/core/slash-commands.d.ts.map +1 -1
  372. package/dist/core/slash-commands.js +1 -1
  373. package/dist/core/slash-commands.js.map +1 -1
  374. package/dist/core/source-info.d.ts.map +1 -1
  375. package/dist/core/system-prompt.d.ts.map +1 -1
  376. package/dist/core/telemetry.d.ts.map +1 -1
  377. package/dist/core/thinking-blocks.d.ts.map +1 -1
  378. package/dist/core/timings.d.ts.map +1 -1
  379. package/dist/core/tools/artifact-protocol.d.ts.map +1 -1
  380. package/dist/core/tools/artifacts.d.ts.map +1 -1
  381. package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
  382. package/dist/core/tools/ask-user-question/config.d.ts.map +1 -1
  383. package/dist/core/tools/ask-user-question/index.d.ts.map +1 -1
  384. package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts.map +1 -1
  385. package/dist/core/tools/ask-user-question/state/inline-input.d.ts.map +1 -1
  386. package/dist/core/tools/ask-user-question/state/key-router.d.ts.map +1 -1
  387. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
  388. package/dist/core/tools/ask-user-question/state/row-intent.d.ts.map +1 -1
  389. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
  390. package/dist/core/tools/ask-user-question/state/selectors/derivations.d.ts.map +1 -1
  391. package/dist/core/tools/ask-user-question/state/selectors/focus.d.ts.map +1 -1
  392. package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
  393. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
  394. package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
  395. package/dist/core/tools/ask-user-question/tool/format-answer.d.ts.map +1 -1
  396. package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts.map +1 -1
  397. package/dist/core/tools/ask-user-question/tool/types.d.ts.map +1 -1
  398. package/dist/core/tools/ask-user-question/tool/validate-questionnaire.d.ts.map +1 -1
  399. package/dist/core/tools/ask-user-question/view/body-residual-spacer.d.ts.map +1 -1
  400. package/dist/core/tools/ask-user-question/view/component-binding.d.ts.map +1 -1
  401. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts.map +1 -1
  402. package/dist/core/tools/ask-user-question/view/components/multi-select-view.d.ts.map +1 -1
  403. package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts.map +1 -1
  404. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.d.ts.map +1 -1
  405. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts.map +1 -1
  406. package/dist/core/tools/ask-user-question/view/components/preview/preview-box-renderer.d.ts.map +1 -1
  407. package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.d.ts.map +1 -1
  408. package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.d.ts.map +1 -1
  409. package/dist/core/tools/ask-user-question/view/components/submit-picker.d.ts.map +1 -1
  410. package/dist/core/tools/ask-user-question/view/components/tab-bar.d.ts.map +1 -1
  411. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
  412. package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts.map +1 -1
  413. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
  414. package/dist/core/tools/ask-user-question/view/stateful-view.d.ts.map +1 -1
  415. package/dist/core/tools/ask-user-question/view/tab-components.d.ts.map +1 -1
  416. package/dist/core/tools/ask-user-question/view/tab-content-strategy.d.ts.map +1 -1
  417. package/dist/core/tools/bash-async-execution.d.ts.map +1 -1
  418. package/dist/core/tools/bash-async-jobs.d.ts.map +1 -1
  419. package/dist/core/tools/bash-async-output.d.ts.map +1 -1
  420. package/dist/core/tools/bash-interceptor.d.ts.map +1 -1
  421. package/dist/core/tools/bash-leading-cd.d.ts.map +1 -1
  422. package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
  423. package/dist/core/tools/bash.d.ts.map +1 -1
  424. package/dist/core/tools/block-resolver.d.ts.map +1 -1
  425. package/dist/core/tools/conflict-registry.d.ts.map +1 -1
  426. package/dist/core/tools/directory-tree.d.ts.map +1 -1
  427. package/dist/core/tools/edit-diff-preserve.d.ts.map +1 -1
  428. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  429. package/dist/core/tools/edit.d.ts.map +1 -1
  430. package/dist/core/tools/fetch-url.d.ts.map +1 -1
  431. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  432. package/dist/core/tools/find.d.ts.map +1 -1
  433. package/dist/core/tools/glob-path-utils.d.ts.map +1 -1
  434. package/dist/core/tools/grep.d.ts.map +1 -1
  435. package/dist/core/tools/hashline-engine/apply.d.ts.map +1 -1
  436. package/dist/core/tools/hashline-engine/block.d.ts.map +1 -1
  437. package/dist/core/tools/hashline-engine/diff-preview.d.ts.map +1 -1
  438. package/dist/core/tools/hashline-engine/format.d.ts.map +1 -1
  439. package/dist/core/tools/hashline-engine/fs.d.ts.map +1 -1
  440. package/dist/core/tools/hashline-engine/index.d.ts.map +1 -1
  441. package/dist/core/tools/hashline-engine/input.d.ts.map +1 -1
  442. package/dist/core/tools/hashline-engine/messages.d.ts.map +1 -1
  443. package/dist/core/tools/hashline-engine/mismatch.d.ts.map +1 -1
  444. package/dist/core/tools/hashline-engine/normalize.d.ts.map +1 -1
  445. package/dist/core/tools/hashline-engine/parser.d.ts.map +1 -1
  446. package/dist/core/tools/hashline-engine/patcher.d.ts.map +1 -1
  447. package/dist/core/tools/hashline-engine/prefixes.d.ts.map +1 -1
  448. package/dist/core/tools/hashline-engine/recovery.d.ts.map +1 -1
  449. package/dist/core/tools/hashline-engine/snapshots.d.ts.map +1 -1
  450. package/dist/core/tools/hashline-engine/stream.d.ts.map +1 -1
  451. package/dist/core/tools/hashline-engine/tokenizer.d.ts.map +1 -1
  452. package/dist/core/tools/hashline-engine/types.d.ts.map +1 -1
  453. package/dist/core/tools/hashline.d.ts.map +1 -1
  454. package/dist/core/tools/index.d.ts.map +1 -1
  455. package/dist/core/tools/ls.d.ts.map +1 -1
  456. package/dist/core/tools/notebook.d.ts.map +1 -1
  457. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  458. package/dist/core/tools/oversized-tool-result.d.ts.map +1 -1
  459. package/dist/core/tools/path-utils.d.ts.map +1 -1
  460. package/dist/core/tools/read-document-extract.d.ts.map +1 -1
  461. package/dist/core/tools/read-selectors.d.ts.map +1 -1
  462. package/dist/core/tools/read-url.d.ts.map +1 -1
  463. package/dist/core/tools/read.d.ts.map +1 -1
  464. package/dist/core/tools/render-utils.d.ts.map +1 -1
  465. package/dist/core/tools/resource-selectors.d.ts.map +1 -1
  466. package/dist/core/tools/search-details.d.ts.map +1 -1
  467. package/dist/core/tools/search-line-ranges.d.ts.map +1 -1
  468. package/dist/core/tools/search-native.d.ts.map +1 -1
  469. package/dist/core/tools/search.d.ts.map +1 -1
  470. package/dist/core/tools/structured-output.d.ts.map +1 -1
  471. package/dist/core/tools/todos-execute.d.ts.map +1 -1
  472. package/dist/core/tools/todos-locks.d.ts.map +1 -1
  473. package/dist/core/tools/todos-model.d.ts.map +1 -1
  474. package/dist/core/tools/todos-mutations.d.ts.map +1 -1
  475. package/dist/core/tools/todos-paths.d.ts.map +1 -1
  476. package/dist/core/tools/todos-render.d.ts.map +1 -1
  477. package/dist/core/tools/todos-storage.d.ts.map +1 -1
  478. package/dist/core/tools/todos-types.d.ts.map +1 -1
  479. package/dist/core/tools/todos-types.js +1 -1
  480. package/dist/core/tools/todos-types.js.map +1 -1
  481. package/dist/core/tools/todos.d.ts.map +1 -1
  482. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  483. package/dist/core/tools/tool-definition-wrapper.js +6 -2
  484. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  485. package/dist/core/tools/tool-limits.d.ts.map +1 -1
  486. package/dist/core/tools/truncate.d.ts.map +1 -1
  487. package/dist/core/tools/url-ip-guards.d.ts.map +1 -1
  488. package/dist/core/tools/write.d.ts.map +1 -1
  489. package/dist/core/trust-manager.d.ts.map +1 -1
  490. package/dist/core/typebox-compat.d.ts +12 -0
  491. package/dist/core/typebox-compat.d.ts.map +1 -0
  492. package/dist/core/typebox-compat.js +10 -0
  493. package/dist/core/typebox-compat.js.map +1 -0
  494. package/dist/index-extensions.d.ts.map +1 -1
  495. package/dist/index.d.ts +4 -2
  496. package/dist/index.d.ts.map +1 -1
  497. package/dist/index.js +3 -1
  498. package/dist/index.js.map +1 -1
  499. package/dist/main-app-mode.d.ts.map +1 -1
  500. package/dist/main-deferred-startup.d.ts.map +1 -1
  501. package/dist/main-early-input.d.ts.map +1 -1
  502. package/dist/main-session-options.d.ts.map +1 -1
  503. package/dist/main-session.d.ts +1 -0
  504. package/dist/main-session.d.ts.map +1 -1
  505. package/dist/main-session.js +7 -0
  506. package/dist/main-session.js.map +1 -1
  507. package/dist/main-stdio.d.ts.map +1 -1
  508. package/dist/main.d.ts.map +1 -1
  509. package/dist/main.js +2 -2
  510. package/dist/main.js.map +1 -1
  511. package/dist/migrations-config-values.d.ts.map +1 -1
  512. package/dist/migrations.d.ts.map +1 -1
  513. package/dist/modes/index.d.ts.map +1 -1
  514. package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -1
  515. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  516. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  517. package/dist/modes/interactive/components/atomic-banner.d.ts.map +1 -1
  518. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  519. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  520. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  521. package/dist/modes/interactive/components/chat-message-renderer.d.ts +4 -1
  522. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  523. package/dist/modes/interactive/components/chat-message-renderer.js +12 -22
  524. package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
  525. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  526. package/dist/modes/interactive/components/chat-session-host-editor.d.ts.map +1 -1
  527. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  528. package/dist/modes/interactive/components/chat-session-host-events.js +5 -11
  529. package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
  530. package/dist/modes/interactive/components/chat-session-host-rendering.d.ts.map +1 -1
  531. package/dist/modes/interactive/components/chat-session-host-rendering.js +3 -0
  532. package/dist/modes/interactive/components/chat-session-host-rendering.js.map +1 -1
  533. package/dist/modes/interactive/components/chat-session-host-runtime.d.ts.map +1 -1
  534. package/dist/modes/interactive/components/chat-session-host-state.d.ts +1 -0
  535. package/dist/modes/interactive/components/chat-session-host-state.d.ts.map +1 -1
  536. package/dist/modes/interactive/components/chat-session-host-state.js +1 -0
  537. package/dist/modes/interactive/components/chat-session-host-state.js.map +1 -1
  538. package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.d.ts.map +1 -1
  539. package/dist/modes/interactive/components/chat-session-host-types.d.ts.map +1 -1
  540. package/dist/modes/interactive/components/chat-session-host-utils.d.ts.map +1 -1
  541. package/dist/modes/interactive/components/chat-session-host-utils.js +1 -1
  542. package/dist/modes/interactive/components/chat-session-host-utils.js.map +1 -1
  543. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  544. package/dist/modes/interactive/components/chat-session-host.js +1 -0
  545. package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
  546. package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -1
  547. package/dist/modes/interactive/components/compaction-boundary-message.d.ts +20 -0
  548. package/dist/modes/interactive/components/compaction-boundary-message.d.ts.map +1 -0
  549. package/dist/modes/interactive/components/compaction-boundary-message.js +48 -0
  550. package/dist/modes/interactive/components/compaction-boundary-message.js.map +1 -0
  551. package/dist/modes/interactive/components/config-selector-list.d.ts.map +1 -1
  552. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  553. package/dist/modes/interactive/components/context-window-selector.d.ts.map +1 -1
  554. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  555. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  556. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  557. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  558. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  559. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  560. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
  561. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  562. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  563. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  564. package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +1 -1
  565. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  566. package/dist/modes/interactive/components/index.d.ts +1 -1
  567. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  568. package/dist/modes/interactive/components/index.js +1 -1
  569. package/dist/modes/interactive/components/index.js.map +1 -1
  570. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  571. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  572. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  573. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  574. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  575. package/dist/modes/interactive/components/session-selector-delete.d.ts.map +1 -1
  576. package/dist/modes/interactive/components/session-selector-header.d.ts.map +1 -1
  577. package/dist/modes/interactive/components/session-selector-list.d.ts.map +1 -1
  578. package/dist/modes/interactive/components/session-selector-list.js +3 -0
  579. package/dist/modes/interactive/components/session-selector-list.js.map +1 -1
  580. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  581. package/dist/modes/interactive/components/session-selector-tree.d.ts.map +1 -1
  582. package/dist/modes/interactive/components/session-selector-types.d.ts.map +1 -1
  583. package/dist/modes/interactive/components/session-selector-utils.d.ts.map +1 -1
  584. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  585. package/dist/modes/interactive/components/settings-selector-handlers.d.ts.map +1 -1
  586. package/dist/modes/interactive/components/settings-selector-items.d.ts.map +1 -1
  587. package/dist/modes/interactive/components/settings-selector-options.d.ts.map +1 -1
  588. package/dist/modes/interactive/components/settings-selector-submenus.d.ts.map +1 -1
  589. package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
  590. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  591. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  592. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  593. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  594. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  595. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  596. package/dist/modes/interactive/components/tree-selector-component.d.ts.map +1 -1
  597. package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
  598. package/dist/modes/interactive/components/tree-selector-help.d.ts.map +1 -1
  599. package/dist/modes/interactive/components/tree-selector-label-input.d.ts.map +1 -1
  600. package/dist/modes/interactive/components/tree-selector-list.d.ts.map +1 -1
  601. package/dist/modes/interactive/components/tree-selector-model.d.ts.map +1 -1
  602. package/dist/modes/interactive/components/tree-selector-types.d.ts.map +1 -1
  603. package/dist/modes/interactive/components/tree-selector-viewport.d.ts.map +1 -1
  604. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  605. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  606. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  607. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  608. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  609. package/dist/modes/interactive/components/working-status.d.ts.map +1 -1
  610. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  611. package/dist/modes/interactive/interactive-agent-events.js +3 -47
  612. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  613. package/dist/modes/interactive/interactive-auth-login.d.ts.map +1 -1
  614. package/dist/modes/interactive/interactive-auth-routing.d.ts.map +1 -1
  615. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  616. package/dist/modes/interactive/interactive-bash-compact.d.ts.map +1 -1
  617. package/dist/modes/interactive/interactive-child-ordering.d.ts.map +1 -1
  618. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  619. package/dist/modes/interactive/interactive-editor-actions.d.ts.map +1 -1
  620. package/dist/modes/interactive/interactive-extension-context.d.ts.map +1 -1
  621. package/dist/modes/interactive/interactive-extension-custom-ui.d.ts.map +1 -1
  622. package/dist/modes/interactive/interactive-extension-dialogs.d.ts.map +1 -1
  623. package/dist/modes/interactive/interactive-extension-runtime.d.ts.map +1 -1
  624. package/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  625. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  626. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  627. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  628. package/dist/modes/interactive/interactive-mode-deps.d.ts +2 -2
  629. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  630. package/dist/modes/interactive/interactive-mode-deps.js +1 -1
  631. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  632. package/dist/modes/interactive/interactive-mode-helpers.d.ts.map +1 -1
  633. package/dist/modes/interactive/interactive-mode-surface.d.ts +5 -3
  634. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  635. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  636. package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
  637. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  638. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  639. package/dist/modes/interactive/interactive-onboarding.d.ts.map +1 -1
  640. package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
  641. package/dist/modes/interactive/interactive-queueing.d.ts.map +1 -1
  642. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  643. package/dist/modes/interactive/interactive-render-chat.js +21 -12
  644. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  645. package/dist/modes/interactive/interactive-resource-disclosure.d.ts.map +1 -1
  646. package/dist/modes/interactive/interactive-resource-paths.d.ts.map +1 -1
  647. package/dist/modes/interactive/interactive-resource-rendering.d.ts.map +1 -1
  648. package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
  649. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  650. package/dist/modes/interactive/interactive-session-runtime.d.ts.map +1 -1
  651. package/dist/modes/interactive/interactive-slash-commands.d.ts.map +1 -1
  652. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  653. package/dist/modes/interactive/model-search.d.ts.map +1 -1
  654. package/dist/modes/interactive/theme/color-utils.d.ts.map +1 -1
  655. package/dist/modes/interactive/theme/export-colors.d.ts.map +1 -1
  656. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  657. package/dist/modes/interactive/theme/terminal-detection.d.ts.map +1 -1
  658. package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
  659. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  660. package/dist/modes/interactive/theme/theme-loading.d.ts.map +1 -1
  661. package/dist/modes/interactive/theme/theme-parse.d.ts.map +1 -1
  662. package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  663. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  664. package/dist/modes/interactive/theme/tui-theme.d.ts.map +1 -1
  665. package/dist/modes/interactive/whimsical-messages.d.ts.map +1 -1
  666. package/dist/modes/print-mode.d.ts.map +1 -1
  667. package/dist/modes/rpc/jsonl.d.ts.map +1 -1
  668. package/dist/modes/rpc/rpc-client.d.ts +3 -5
  669. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  670. package/dist/modes/rpc/rpc-client.js +1 -6
  671. package/dist/modes/rpc/rpc-client.js.map +1 -1
  672. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  673. package/dist/modes/rpc/rpc-command-handler.js +0 -4
  674. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  675. package/dist/modes/rpc/rpc-extension-ui.d.ts.map +1 -1
  676. package/dist/modes/rpc/rpc-input.d.ts.map +1 -1
  677. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  678. package/dist/modes/rpc/rpc-responses.d.ts.map +1 -1
  679. package/dist/modes/rpc/rpc-session-binding.d.ts.map +1 -1
  680. package/dist/modes/rpc/rpc-types.d.ts +2 -11
  681. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  682. package/dist/modes/rpc/rpc-types.js.map +1 -1
  683. package/dist/package-manager-cli-parser.d.ts.map +1 -1
  684. package/dist/package-manager-cli.d.ts.map +1 -1
  685. package/dist/rpc-entry.d.ts.map +1 -1
  686. package/dist/utils/ansi.d.ts.map +1 -1
  687. package/dist/utils/changelog.d.ts.map +1 -1
  688. package/dist/utils/child-process.d.ts.map +1 -1
  689. package/dist/utils/clipboard-image.d.ts.map +1 -1
  690. package/dist/utils/clipboard-native.d.ts.map +1 -1
  691. package/dist/utils/clipboard.d.ts.map +1 -1
  692. package/dist/utils/deprecation.d.ts.map +1 -1
  693. package/dist/utils/event-loop.d.ts.map +1 -1
  694. package/dist/utils/exif-orientation.d.ts.map +1 -1
  695. package/dist/utils/frontmatter.d.ts.map +1 -1
  696. package/dist/utils/fs-watch.d.ts.map +1 -1
  697. package/dist/utils/git-env.d.ts.map +1 -1
  698. package/dist/utils/git.d.ts.map +1 -1
  699. package/dist/utils/html.d.ts.map +1 -1
  700. package/dist/utils/image-convert.d.ts.map +1 -1
  701. package/dist/utils/image-process.d.ts.map +1 -1
  702. package/dist/utils/image-resize-core.d.ts.map +1 -1
  703. package/dist/utils/image-resize-worker.d.ts.map +1 -1
  704. package/dist/utils/image-resize.d.ts.map +1 -1
  705. package/dist/utils/json.d.ts.map +1 -1
  706. package/dist/utils/markit.d.ts.map +1 -1
  707. package/dist/utils/mime.d.ts.map +1 -1
  708. package/dist/utils/module-require.d.ts.map +1 -1
  709. package/dist/utils/open-browser.d.ts.map +1 -1
  710. package/dist/utils/paths.d.ts.map +1 -1
  711. package/dist/utils/photon.d.ts.map +1 -1
  712. package/dist/utils/pi-user-agent.d.ts.map +1 -1
  713. package/dist/utils/shell.d.ts.map +1 -1
  714. package/dist/utils/sleep.d.ts.map +1 -1
  715. package/dist/utils/split-launcher.d.ts.map +1 -1
  716. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  717. package/dist/utils/tools-manager.d.ts.map +1 -1
  718. package/dist/utils/version-check.d.ts.map +1 -1
  719. package/dist/utils/windows-self-update.d.ts.map +1 -1
  720. package/docs/compaction.md +97 -772
  721. package/docs/custom-provider.md +7 -1
  722. package/docs/extensions.md +31 -33
  723. package/docs/json.md +2 -2
  724. package/docs/rpc.md +32 -24
  725. package/docs/sdk.md +3 -3
  726. package/docs/session-format.md +49 -30
  727. package/docs/sessions.md +5 -3
  728. package/docs/settings.md +14 -4
  729. package/docs/usage.md +1 -1
  730. package/docs/workflows.md +30 -15
  731. package/examples/extensions/README.md +3 -1
  732. package/examples/extensions/custom-compaction.ts +14 -58
  733. package/examples/extensions/handoff.ts +2 -3
  734. package/examples/extensions/subagent/schemas.ts +1 -1
  735. package/examples/extensions/tic-tac-toe.ts +1 -2
  736. package/examples/extensions/todo.ts +1 -2
  737. package/npm-shrinkwrap.json +214 -98
  738. package/package.json +12 -12
  739. package/dist/core/compaction/context-assistant-turns.d.ts +0 -42
  740. package/dist/core/compaction/context-assistant-turns.d.ts.map +0 -1
  741. package/dist/core/compaction/context-assistant-turns.js +0 -87
  742. package/dist/core/compaction/context-assistant-turns.js.map +0 -1
  743. package/dist/core/compaction/context-compaction-critical.d.ts +0 -15
  744. package/dist/core/compaction/context-compaction-critical.d.ts.map +0 -1
  745. package/dist/core/compaction/context-compaction-critical.js +0 -57
  746. package/dist/core/compaction/context-compaction-critical.js.map +0 -1
  747. package/dist/core/compaction/context-compaction-eviction-alternates.d.ts +0 -18
  748. package/dist/core/compaction/context-compaction-eviction-alternates.d.ts.map +0 -1
  749. package/dist/core/compaction/context-compaction-eviction-alternates.js +0 -186
  750. package/dist/core/compaction/context-compaction-eviction-alternates.js.map +0 -1
  751. package/dist/core/compaction/context-compaction-eviction.d.ts +0 -12
  752. package/dist/core/compaction/context-compaction-eviction.d.ts.map +0 -1
  753. package/dist/core/compaction/context-compaction-eviction.js +0 -222
  754. package/dist/core/compaction/context-compaction-eviction.js.map +0 -1
  755. package/dist/core/compaction/context-compaction-metrics.d.ts +0 -28
  756. package/dist/core/compaction/context-compaction-metrics.d.ts.map +0 -1
  757. package/dist/core/compaction/context-compaction-metrics.js +0 -107
  758. package/dist/core/compaction/context-compaction-metrics.js.map +0 -1
  759. package/dist/core/compaction/context-compaction-prompt.d.ts +0 -9
  760. package/dist/core/compaction/context-compaction-prompt.d.ts.map +0 -1
  761. package/dist/core/compaction/context-compaction-prompt.js +0 -163
  762. package/dist/core/compaction/context-compaction-prompt.js.map +0 -1
  763. package/dist/core/compaction/context-compaction-runner.d.ts +0 -11
  764. package/dist/core/compaction/context-compaction-runner.d.ts.map +0 -1
  765. package/dist/core/compaction/context-compaction-runner.js +0 -273
  766. package/dist/core/compaction/context-compaction-runner.js.map +0 -1
  767. package/dist/core/compaction/context-compaction-strategy.d.ts +0 -5
  768. package/dist/core/compaction/context-compaction-strategy.d.ts.map +0 -1
  769. package/dist/core/compaction/context-compaction-strategy.js +0 -27
  770. package/dist/core/compaction/context-compaction-strategy.js.map +0 -1
  771. package/dist/core/compaction/context-compaction-types.d.ts +0 -75
  772. package/dist/core/compaction/context-compaction-types.d.ts.map +0 -1
  773. package/dist/core/compaction/context-compaction-types.js +0 -6
  774. package/dist/core/compaction/context-compaction-types.js.map +0 -1
  775. package/dist/core/compaction/context-compaction.d.ts +0 -10
  776. package/dist/core/compaction/context-compaction.d.ts.map +0 -1
  777. package/dist/core/compaction/context-compaction.js +0 -8
  778. package/dist/core/compaction/context-compaction.js.map +0 -1
  779. package/dist/core/compaction/context-deletion-application.d.ts +0 -14
  780. package/dist/core/compaction/context-deletion-application.d.ts.map +0 -1
  781. package/dist/core/compaction/context-deletion-application.js +0 -261
  782. package/dist/core/compaction/context-deletion-application.js.map +0 -1
  783. package/dist/core/compaction/context-deletion-store.d.ts +0 -78
  784. package/dist/core/compaction/context-deletion-store.d.ts.map +0 -1
  785. package/dist/core/compaction/context-deletion-store.js +0 -162
  786. package/dist/core/compaction/context-deletion-store.js.map +0 -1
  787. package/dist/core/compaction/context-deletion-targets.d.ts +0 -43
  788. package/dist/core/compaction/context-deletion-targets.d.ts.map +0 -1
  789. package/dist/core/compaction/context-deletion-targets.js +0 -292
  790. package/dist/core/compaction/context-deletion-targets.js.map +0 -1
  791. package/dist/core/compaction/context-deletion-tool-definitions.d.ts +0 -193
  792. package/dist/core/compaction/context-deletion-tool-definitions.d.ts.map +0 -1
  793. package/dist/core/compaction/context-deletion-tool-definitions.js +0 -98
  794. package/dist/core/compaction/context-deletion-tool-definitions.js.map +0 -1
  795. package/dist/core/compaction/context-deletion-tool-helpers.d.ts +0 -19
  796. package/dist/core/compaction/context-deletion-tool-helpers.d.ts.map +0 -1
  797. package/dist/core/compaction/context-deletion-tool-helpers.js +0 -210
  798. package/dist/core/compaction/context-deletion-tool-helpers.js.map +0 -1
  799. package/dist/core/compaction/context-deletion-tools.d.ts +0 -4
  800. package/dist/core/compaction/context-deletion-tools.d.ts.map +0 -1
  801. package/dist/core/compaction/context-deletion-tools.js +0 -403
  802. package/dist/core/compaction/context-deletion-tools.js.map +0 -1
  803. package/dist/core/compaction/context-transcript-analysis.d.ts +0 -11
  804. package/dist/core/compaction/context-transcript-analysis.d.ts.map +0 -1
  805. package/dist/core/compaction/context-transcript-analysis.js +0 -228
  806. package/dist/core/compaction/context-transcript-analysis.js.map +0 -1
  807. package/dist/core/session-manager-tool-dependencies.d.ts +0 -10
  808. package/dist/core/session-manager-tool-dependencies.d.ts.map +0 -1
  809. package/dist/core/session-manager-tool-dependencies.js +0 -133
  810. package/dist/core/session-manager-tool-dependencies.js.map +0 -1
  811. package/dist/modes/interactive/components/context-compaction-summary-message.d.ts +0 -17
  812. package/dist/modes/interactive/components/context-compaction-summary-message.d.ts.map +0 -1
  813. package/dist/modes/interactive/components/context-compaction-summary-message.js +0 -83
  814. package/dist/modes/interactive/components/context-compaction-summary-message.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"patcher.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/patcher.ts"],"names":[],"mappings":"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","sourcesContent":["// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).\n/**\n * High-level patch orchestrator. Reads each section's target file via the\n * configured {@link Filesystem}, strips BOM and normalizes line endings,\n * validates the section snapshot tag (with {@link Recovery}), applies the\n * result back through the same {@link Filesystem}.\n *\n * Two layers:\n * - {@link Patcher.apply} — high-level, preflight-atomic. It validates and\n * applies every section in memory before any write hits disk, then commits\n * in order. It is not write-transactional: a mid-batch filesystem failure\n * can leave earlier sections already written, and the thrown error reports\n * which sections landed.\n * - {@link Patcher.prepare} / {@link Patcher.commit} — granular primitives\n * for callers that need per-section control (e.g. batched LSP flush,\n * custom interleaving). `prepare` performs all the read-side work,\n * validates the section snapshot tag (with recovery), and applies the\n * edits in memory. `commit` writes the prepared result and records a\n * fresh snapshot.\n *\n * Because `prepare` already runs the full apply, a multi-section batch fails\n * before any write when parsing, stale-tag validation, or in-memory apply fails.\n *\n * The patcher itself is stateless across calls; reuse one instance per\n * filesystem configuration.\n */\nimport { applyEdits } from \"./apply.js\";\nimport { hasBlockEdit, resolveBlockEdits } from \"./block.js\";\nimport { computeFileHash, formatHashlineHeader } from \"./format.js\";\nimport type { Filesystem, WriteResult } from \"./fs.js\";\nimport { isNotFound } from \"./fs.js\";\nimport type { Patch, PatchSection } from \"./input.js\";\nimport { HEADTAIL_DRIFT_WARNING, missingSnapshotTagMessage } from \"./messages.js\";\nimport { MismatchError } from \"./mismatch.js\";\nimport { detectLineEnding, type LineEnding, normalizeToLF, restoreLineEndings, stripBom } from \"./normalize.js\";\nimport { Recovery, type RecoveryResult } from \"./recovery.js\";\nimport type { SnapshotStore } from \"./snapshots.js\";\nimport type { ApplyResult, BlockResolution, BlockResolver, Edit } from \"./types.js\";\n\nexport interface PatcherOptions {\n\t/** Storage backend used for all reads and writes. */\n\tfs: Filesystem;\n\t/** Snapshot store that minted and resolves hashline section tags. Required. */\n\tsnapshots: SnapshotStore;\n\t/**\n\t * Resolves `replace block N:` anchors to concrete line spans via tree-sitter.\n\t * Optional: when omitted, any `replace block N:` edit throws on apply (the\n\t * host did not wire a resolver). Plain line-range ops never need it.\n\t */\n\tblockResolver?: BlockResolver;\n}\n\n/** Per-section result returned by {@link Patcher.apply} / {@link Patcher.commit}. */\nexport interface PatchSectionResult {\n\t/** Section path (as authored, after cwd-resolution at parse time). */\n\tpath: string;\n\t/** Filesystem-canonical key for this section (e.g. absolute path). */\n\tcanonicalPath: string;\n\t/** `\"noop\"` when the apply produced no change; otherwise `\"create\"` / `\"update\"`. */\n\top: \"create\" | \"update\" | \"noop\";\n\t/** Pre-edit text (LF-normalized, BOM-stripped). */\n\tbefore: string;\n\t/** Post-edit text (LF-normalized, BOM-stripped). For `\"noop\"` equals `before`. */\n\tafter: string;\n\t/** Same text as `after` but with the original BOM and line ending restored. */\n\tpersisted: string;\n\t/** Final text that the {@link Filesystem} actually wrote (may differ if the FS transformed it). */\n\twritten: string;\n\t/** 4-hex content-hash tag for `after`. Use to anchor follow-up edits. */\n\tfileHash: string;\n\t/** Hashline section header (`[path#tag]`) of the post-edit content. */\n\theader: string;\n\t/** 1-indexed first changed line in `after`, or `undefined` for noops. */\n\tfirstChangedLine?: number;\n\t/** Warnings collected by the parser, applier, and (optionally) recovery. */\n\twarnings: string[];\n\t/**\n\t * Resolved spans for any `replace block`/`delete block` ops, present when the\n\t * apply matched the tagged content. Undefined for patches with no block ops\n\t * (and for resolutions routed through drift recovery, where numbers shift).\n\t */\n\tblockResolutions?: BlockResolution[];\n}\n\nexport interface PatcherApplyResult {\n\tsections: PatchSectionResult[];\n}\n\n/**\n * Opaque token returned by {@link Patcher.prepare}. Carries the section, the\n * raw file content read off disk, and the in-memory apply result.\n * {@link Patcher.commit} just writes the {@link PreparedSection.applyResult}.\n */\nexport class PreparedSection {\n\treadonly section: PatchSection;\n\treadonly canonicalPath: string;\n\treadonly exists: boolean;\n\treadonly rawContent: string;\n\treadonly bom: string;\n\treadonly lineEnding: LineEnding;\n\treadonly normalized: string;\n\treadonly applyResult: ApplyResult;\n\treadonly parseWarnings: readonly string[];\n\t/** @internal */\n\tconstructor(\n\t\tsection: PatchSection,\n\t\tcanonicalPath: string,\n\t\texists: boolean,\n\t\trawContent: string,\n\t\tbom: string,\n\t\tlineEnding: LineEnding,\n\t\tnormalized: string,\n\t\tapplyResult: ApplyResult,\n\t\tparseWarnings: readonly string[],\n\t) {\n\t\tthis.section = section;\n\t\tthis.canonicalPath = canonicalPath;\n\t\tthis.exists = exists;\n\t\tthis.rawContent = rawContent;\n\t\tthis.bom = bom;\n\t\tthis.lineEnding = lineEnding;\n\t\tthis.normalized = normalized;\n\t\tthis.applyResult = applyResult;\n\t\tthis.parseWarnings = parseWarnings;\n\t}\n\n\t/** Convenience: returns true when the apply produced no change. */\n\tget isNoop(): boolean {\n\t\treturn this.applyResult.text === this.normalized;\n\t}\n}\n\nfunction hasAnchorScopedEdit(edits: readonly Edit[]): boolean {\n\treturn edits.some(edit => {\n\t\tif (edit.kind === \"delete\") return true;\n\t\t// A `replace block N:` edit anchors to concrete content on line N.\n\t\tif (edit.kind === \"block\") return true;\n\t\treturn edit.cursor.kind === \"before_anchor\" || edit.cursor.kind === \"after_anchor\";\n\t});\n}\n\nfunction assertSectionHashPresent(sectionPath: string, fileHash: string | undefined): void {\n\tif (fileHash !== undefined) return;\n\tthrow new Error(missingSnapshotTagMessage(sectionPath));\n}\n\nfunction recoveryToApplyResult(result: RecoveryResult): ApplyResult {\n\treturn {\n\t\ttext: result.text,\n\t\tfirstChangedLine: result.firstChangedLine,\n\t\twarnings: result.warnings,\n\t};\n}\nfunction mergeWarnings(...sources: ReadonlyArray<readonly string[] | undefined>): string[] {\n\tconst out: string[] = [];\n\tfor (const source of sources) {\n\t\tif (!source) continue;\n\t\tfor (const warning of source) out.push(warning);\n\t}\n\treturn out;\n}\n\nfunction assertUniqueCanonicalPaths(prepared: readonly PreparedSection[]): void {\n\tconst seen = new Map<string, string>();\n\tfor (const entry of prepared) {\n\t\tconst previous = seen.get(entry.canonicalPath);\n\t\tif (previous !== undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`Multiple hashline sections resolve to the same file (${previous} and ${entry.section.path}). Merge their ops under one header before applying.`,\n\t\t\t);\n\t\t}\n\t\tseen.set(entry.canonicalPath, entry.section.path);\n\t}\n}\n\n/**\n * High-level patcher. Wires a {@link Filesystem} and a required\n * {@link SnapshotStore} together with the parsing + applying core.\n *\n * Construct once per FS configuration; reuse across patches.\n */\nexport class Patcher {\n\treadonly fs: Filesystem;\n\treadonly snapshots: SnapshotStore;\n\treadonly recovery: Recovery;\n\treadonly blockResolver: BlockResolver | undefined;\n\n\tconstructor(options: PatcherOptions) {\n\t\tif (!options.snapshots) {\n\t\t\tthrow new Error(\"Hashline Patcher requires a SnapshotStore; section tags are opaque store pointers.\");\n\t\t}\n\t\tthis.fs = options.fs;\n\t\tthis.snapshots = options.snapshots;\n\t\tthis.recovery = new Recovery(options.snapshots);\n\t\tthis.blockResolver = options.blockResolver;\n\t}\n\n\t/**\n\t * Apply every section in `patch`. `prepare` runs the full apply for each\n\t * section in memory before any write hits the filesystem, so a\n\t * multi-section batch fails before writing on parse/validation/apply errors.\n\t * Write commits are sequential rather than transactional; if a write fails,\n\t * earlier sections may already be on disk and are listed in the thrown error.\n\t * Returns one {@link PatchSectionResult} per section in the original patch order.\n\t */\n\tasync apply(patch: Patch): Promise<PatcherApplyResult> {\n\t\t// Single-section fast path.\n\t\tif (patch.sections.length === 1) {\n\t\t\tconst prepared = await this.prepare(patch.sections[0]);\n\t\t\treturn { sections: [await this.commit(prepared)] };\n\t\t}\n\n\t\t// Prepare every section first so any failure (stale hash, missing\n\t\t// file, parse error, in-memory no-op) surfaces before any write.\n\t\tconst prepared: PreparedSection[] = [];\n\t\tfor (const section of patch.sections) prepared.push(await this.prepare(section));\n\t\tassertUniqueCanonicalPaths(prepared);\n\t\tfor (const entry of prepared) {\n\t\t\tif (entry.isNoop) {\n\t\t\t\tthrow new Error(`Edits to ${entry.section.path} resulted in no changes being made.`);\n\t\t\t}\n\t\t}\n\n\t\tconst results: PatchSectionResult[] = [];\n\t\tfor (let index = 0; index < prepared.length; index++) {\n\t\t\ttry {\n\t\t\t\tresults.push(await this.commit(prepared[index]));\n\t\t\t} catch (error) {\n\t\t\t\t// A mid-batch write failure leaves earlier sections on disk with no\n\t\t\t\t// rollback; report exactly which sections landed so the caller can\n\t\t\t\t// re-issue only the missing ones instead of double-applying.\n\t\t\t\tconst written = prepared.slice(0, index).map(entry => entry.section.path);\n\t\t\t\tconst notWritten = prepared.slice(index + 1).map(entry => entry.section.path);\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to write ${prepared[index].section.path}: ${message}` +\n\t\t\t\t\t\t(written.length > 0 ? ` Sections already written: ${written.join(\", \")}.` : \"\") +\n\t\t\t\t\t\t(notWritten.length > 0 ? ` Sections not written: ${notWritten.join(\", \")}.` : \"\"),\n\t\t\t\t\t{ cause: error },\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn { sections: results };\n\t}\n\n\t/**\n\t * Run the preflight pass only: read, parse, validate, apply-in-memory.\n\t * No writes hit the filesystem. Use for CI checks and dry runs.\n\t */\n\tasync preflight(patch: Patch): Promise<void> {\n\t\tconst prepared: PreparedSection[] = [];\n\t\tfor (const section of patch.sections) prepared.push(await this.prepare(section));\n\t\tassertUniqueCanonicalPaths(prepared);\n\t\tfor (const entry of prepared) {\n\t\t\tif (entry.isNoop) {\n\t\t\t\tthrow new Error(`Edits to ${entry.section.path} resulted in no changes being made.`);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Read a section's target file, parse the section, validate the snapshot\n\t * tag (with recovery), and apply the edits in memory. Returns a\n\t * {@link PreparedSection} which can be fed to {@link commit} to land\n\t * the result on the filesystem.\n\t *\n\t * Throws on parse error, missing-file-for-anchored-edit, or unrecovered\n\t * tag mismatch ({@link MismatchError}).\n\t */\n\tasync prepare(section: PatchSection): Promise<PreparedSection> {\n\t\tconst { edits, warnings: parseWarnings } = section.parse();\n\t\tassertSectionHashPresent(section.path, section.fileHash);\n\n\t\tconst canonicalPath = this.fs.canonicalPath(section.path);\n\t\tawait this.fs.preflightWrite(section.path);\n\t\tconst { exists, rawContent } = await this.#tryRead(section.path);\n\t\tif (!exists) {\n\t\t\tthrow new Error(`File not found: ${section.path}. Use the write tool to create new files.`);\n\t\t}\n\n\t\tconst { bom, text } = stripBom(rawContent);\n\t\tconst lineEnding = detectLineEnding(text);\n\t\tconst normalized = normalizeToLF(text);\n\n\t\tconst applyResult = this.#applyWithRecovery({\n\t\t\tsection,\n\t\t\tcanonicalPath,\n\t\t\texists,\n\t\t\tnormalized,\n\t\t\tedits,\n\t\t});\n\n\t\treturn new PreparedSection(\n\t\t\tsection,\n\t\t\tcanonicalPath,\n\t\t\texists,\n\t\t\trawContent,\n\t\t\tbom,\n\t\t\tlineEnding,\n\t\t\tnormalized,\n\t\t\tapplyResult,\n\t\t\tparseWarnings,\n\t\t);\n\t}\n\n\t/**\n\t * Commit a previously {@link prepare}d section to the filesystem.\n\t * Restores line endings and BOM, writes via the {@link Filesystem}, and\n\t * records a fresh snapshot in the {@link SnapshotStore} keyed by the\n\t * filesystem-canonical path.\n\t */\n\tasync commit(prepared: PreparedSection): Promise<PatchSectionResult> {\n\t\tconst { section, normalized, bom, lineEnding, parseWarnings, exists, applyResult, canonicalPath } = prepared;\n\t\tconst after = applyResult.text;\n\t\tconst warnings = mergeWarnings(parseWarnings, applyResult.warnings);\n\n\t\tif (after === normalized) {\n\t\t\tconst hash = this.#recordFullSnapshot(canonicalPath, normalized);\n\t\t\treturn {\n\t\t\t\tpath: section.path,\n\t\t\t\tcanonicalPath,\n\t\t\t\top: \"noop\",\n\t\t\t\tbefore: normalized,\n\t\t\t\tafter: normalized,\n\t\t\t\tpersisted: prepared.rawContent,\n\t\t\t\twritten: prepared.rawContent,\n\t\t\t\tfileHash: hash,\n\t\t\t\theader: formatHashlineHeader(section.path, hash),\n\t\t\t\twarnings,\n\t\t\t};\n\t\t}\n\n\t\tconst persisted = bom + restoreLineEndings(after, lineEnding);\n\t\tconst write: WriteResult = await this.fs.writeText(section.path, persisted);\n\t\tconst fileHash = this.#recordFullSnapshot(canonicalPath, after);\n\t\tconst op = exists ? \"update\" : \"create\";\n\n\t\treturn {\n\t\t\tpath: section.path,\n\t\t\tcanonicalPath,\n\t\t\top,\n\t\t\tbefore: normalized,\n\t\t\tafter,\n\t\t\tpersisted,\n\t\t\twritten: write.text,\n\t\t\tfileHash,\n\t\t\theader: formatHashlineHeader(section.path, fileHash),\n\t\t\tfirstChangedLine: applyResult.firstChangedLine,\n\t\t\tblockResolutions: applyResult.blockResolutions,\n\t\t\twarnings,\n\t\t};\n\t}\n\n\tasync #tryRead(path: string): Promise<{ exists: boolean; rawContent: string }> {\n\t\ttry {\n\t\t\tconst content = await this.fs.readText(path);\n\t\t\treturn { exists: true, rawContent: content };\n\t\t} catch (error) {\n\t\t\tif (isNotFound(error)) return { exists: false, rawContent: \"\" };\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t#recordFullSnapshot(canonicalPath: string, normalized: string): string {\n\t\treturn this.snapshots.record(canonicalPath, normalized);\n\t}\n\t#mismatchError(\n\t\tsection: PatchSection,\n\t\tcanonicalPath: string,\n\t\tnormalized: string,\n\t\texpected: string,\n\t\thashRecognized: boolean,\n\t): MismatchError {\n\t\tconst actualFileHash = this.#recordFullSnapshot(canonicalPath, normalized);\n\t\treturn new MismatchError({\n\t\t\tpath: section.path,\n\t\t\texpectedFileHash: expected,\n\t\t\tactualFileHash,\n\t\t\tfileLines: normalized.split(\"\\n\"),\n\t\t\tanchorLines: section.collectAnchorLines(),\n\t\t\thashRecognized,\n\t\t});\n\t}\n\n\t#applyWithRecovery(args: {\n\t\tsection: PatchSection;\n\t\tcanonicalPath: string;\n\t\texists: boolean;\n\t\tnormalized: string;\n\t\tedits: readonly Edit[];\n\t}): ApplyResult {\n\t\tconst { section, canonicalPath, exists, normalized, edits } = args;\n\t\tconst expected = exists ? section.fileHash : undefined;\n\t\tconst expectedSnapshot = expected === undefined ? null : this.snapshots.byHash(canonicalPath, expected);\n\t\tconst liveHashMatches = expected !== undefined && computeFileHash(normalized) === expected;\n\t\tconst liveSnapshot = expected === undefined ? null : this.snapshots.byHashAndText(canonicalPath, expected, normalized);\n\t\tconst liveMatches = liveHashMatches && liveSnapshot !== null;\n\t\tif (liveHashMatches && !liveMatches) {\n\t\t\tthrow this.#mismatchError(section, canonicalPath, normalized, expected, expectedSnapshot !== null);\n\t\t}\n\n\t\t// Resolve `replace block N:` edits to concrete ranges before recovery\n\t\t// runs. Block anchors are expressed against the snapshot the section tag\n\t\t// names, so resolve against that exact text:\n\t\t// - live content matches the tag (or there is no tag) → resolve against\n\t\t// the live, normalized content;\n\t\t// - the file drifted → resolve against the tagged snapshot's text so the\n\t\t// resulting ranges flow through the 3-way-merge recovery below.\n\t\t// When a block edit needs the tagged snapshot but it is unavailable, the\n\t\t// range cannot be placed safely — reject with a MismatchError (re-read).\n\t\tconst blockResolutions: BlockResolution[] = [];\n\t\tconst resolveWarnings: string[] = [];\n\t\tlet resolved: readonly Edit[] = edits;\n\t\tif (hasBlockEdit(edits)) {\n\t\t\tconst baseText =\n\t\t\t\texpected === undefined || liveMatches ? normalized : expectedSnapshot?.text;\n\t\t\tif (baseText === undefined) {\n\t\t\t\tthrow this.#mismatchError(section, canonicalPath, normalized, expected ?? \"\", false);\n\t\t\t}\n\t\t\tresolved = resolveBlockEdits(edits, baseText, section.path, this.blockResolver, {\n\t\t\t\tonUnresolved: \"throw\",\n\t\t\t\tonResolved: resolution => blockResolutions.push(resolution),\n\t\t\t\tonWarning: warning => resolveWarnings.push(warning),\n\t\t\t});\n\t\t}\n\t\tconst withResolveWarnings = (result: ApplyResult): ApplyResult =>\n\t\t\tresolveWarnings.length === 0\n\t\t\t\t? result\n\t\t\t\t: { ...result, warnings: [...resolveWarnings, ...(result.warnings ?? [])] };\n\n\t\t// No tag, or the tag still names the live content: an edit anchored at any\n\t\t// line is safe to apply, and the resolved block spans line up with what\n\t\t// the caller read, so echo them back. (A drifted file falls through to\n\t\t// recovery below, where line numbers shift, so resolutions are dropped.)\n\t\tif (expected === undefined || liveMatches) {\n\t\t\tconst result = applyEdits(normalized, resolved);\n\t\t\treturn withResolveWarnings(blockResolutions.length > 0 ? { ...result, blockResolutions } : result);\n\t\t}\n\t\t// Head/tail-only inserts are position-stable: \"start\"/\"end\" cannot move\n\t\t// with content drift, so a stale tag is non-fatal. Apply onto the live\n\t\t// content and warn instead of hard-failing — unlike an anchored\n\t\t// mismatch, which cannot be safely relocated and must reject.\n\t\tif (!hasAnchorScopedEdit(resolved)) {\n\t\t\tconst result = applyEdits(normalized, resolved);\n\t\t\treturn withResolveWarnings({ ...result, warnings: [HEADTAIL_DRIFT_WARNING, ...(result.warnings ?? [])] });\n\t\t}\n\t\t// File drifted: try to replay the edit against the version the tag\n\t\t// names and 3-way-merge it onto the live content.\n\t\tconst recovered = this.recovery.tryRecover({\n\t\t\tpath: canonicalPath,\n\t\t\tcurrentText: normalized,\n\t\t\tfileHash: expected,\n\t\t\tedits: resolved,\n\t\t});\n\t\tif (recovered) return withResolveWarnings(recoveryToApplyResult(recovered));\n\t\tconst hashRecognized = expectedSnapshot !== null;\n\t\tthrow this.#mismatchError(section, canonicalPath, normalized, expected, hashRecognized);\n\t}\n}\n"]}
1
+ {"version":3,"file":"patcher.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/patcher.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"prefixes.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/prefixes.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AAmBH;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElE;AAwCD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAwB9D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAQ/D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAOtF","sourcesContent":["// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).\n/**\n * When a hashline payload is authored against `read`/`search` output, each\n * line is prefixed with either a hashline-mode line number (`123:`) or, for\n * diff-style echoes, a leading `+`. These helpers detect that and recover\n * the raw text. Two strip modes are exposed:\n *\n * - {@link stripNewLinePrefixes} — opportunistic: strips when the input\n * clearly carries hashline or diff prefixes, leaves it alone otherwise.\n * - {@link stripHashlinePrefixes} — strict: only strips when every non-empty\n * content line is hashline-prefixed.\n *\n * These run *before* the tokenizer; they exist because hashline mode is the\n * common case for echoed file content, and erroneously echoed prefixes will\n * otherwise turn every content line into a (malformed) op.\n */\n\nimport { HL_FILE_HASH_LENGTH } from \"./format.js\";\n\nconst HL_PREFIX_RE = /^\\s*(?:>>>|>>)?\\s*(?:[+*-]\\s*)?\\d+:/;\nconst HL_PREFIX_PLUS_RE = /^\\s*(?:>>>|>>)?\\s*\\+\\s*\\d+:/;\nconst HL_HEADER_RE = new RegExp(`^\\\\s*\\\\[[^#\\\\r\\\\n]+#[0-9a-fA-F]{${HL_FILE_HASH_LENGTH}}\\\\]\\\\s*$`);\nconst DIFF_PLUS_RE = /^[+](?![+])/;\nconst READ_TRUNCATION_NOTICE_RE = /^\\[(?:Showing lines \\d+-\\d+ of \\d+|\\d+ more lines? in (?:file|\\S+))\\b.*\\bUse :L?\\d+/;\n\nfunction stripLeadingHashlinePrefixes(line: string): string {\n\tlet result = line;\n\tlet previous: string;\n\tdo {\n\t\tprevious = result;\n\t\tresult = result.replace(HL_PREFIX_RE, \"\");\n\t} while (result !== previous);\n\treturn result;\n}\n/**\n * Single-pass variant of {@link stripLeadingHashlinePrefixes} that strips at\n * most one leading hashline prefix (`N:`, `>>>N:`, `+N:` etc.) and does NOT\n * loop. Use this when the input carries at most one snapshot prefix (e.g. a\n * bare body row paste from `read` output) — recursive stripping would corrupt\n * content whose own text starts with `digits:`.\n */\nexport function stripOneLeadingHashlinePrefix(line: string): string {\n\treturn line.replace(HL_PREFIX_RE, \"\");\n}\n\ninterface LinePrefixStats {\n\tnonEmpty: number;\n\theaderCount: number;\n\thashPrefixCount: number;\n\tdiffPlusHashPrefixCount: number;\n\tdiffPlusCount: number;\n\ttruncationNoticeCount: number;\n}\n\nfunction collectLinePrefixStats(lines: string[]): LinePrefixStats {\n\tconst stats: LinePrefixStats = {\n\t\tnonEmpty: 0,\n\t\theaderCount: 0,\n\t\thashPrefixCount: 0,\n\t\tdiffPlusHashPrefixCount: 0,\n\t\tdiffPlusCount: 0,\n\t\ttruncationNoticeCount: 0,\n\t};\n\n\tfor (const line of lines) {\n\t\tif (line.length === 0) continue;\n\t\tif (READ_TRUNCATION_NOTICE_RE.test(line)) {\n\t\t\tstats.truncationNoticeCount++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (HL_HEADER_RE.test(line)) {\n\t\t\tstats.nonEmpty++;\n\t\t\tstats.headerCount++;\n\t\t\tcontinue;\n\t\t}\n\t\tstats.nonEmpty++;\n\t\tif (HL_PREFIX_RE.test(line)) stats.hashPrefixCount++;\n\t\tif (HL_PREFIX_PLUS_RE.test(line)) stats.diffPlusHashPrefixCount++;\n\t\tif (DIFF_PLUS_RE.test(line)) stats.diffPlusCount++;\n\t}\n\treturn stats;\n}\n\n/**\n * Strip whichever prefix scheme the lines appear to be carrying:\n * - hashline line-number prefixes (`123:`) when every content line has one\n * - leading `+` (diff style) when at least half the lines have one\n * - mixed `+<n>:` form when present\n *\n * Returns the lines untouched if no scheme is recognized.\n */\nexport function stripNewLinePrefixes(lines: string[]): string[] {\n\tconst stats = collectLinePrefixStats(lines);\n\tif (stats.nonEmpty === 0) return lines;\n\n\tconst contentLineCount = stats.nonEmpty - stats.headerCount;\n\tconst stripHash = contentLineCount > 0 && stats.hashPrefixCount === contentLineCount;\n\tconst stripPlus =\n\t\t!stripHash &&\n\t\tstats.diffPlusHashPrefixCount === 0 &&\n\t\tstats.diffPlusCount > 0 &&\n\t\tstats.diffPlusCount >= stats.nonEmpty * 0.5;\n\n\tif (!stripHash && !stripPlus && stats.diffPlusHashPrefixCount === 0) return lines;\n\n\treturn lines\n\t\t.filter(line => !READ_TRUNCATION_NOTICE_RE.test(line) && !(stripHash && HL_HEADER_RE.test(line)))\n\t\t.map(line => {\n\t\t\tif (stripHash) return stripLeadingHashlinePrefixes(line);\n\t\t\tif (stripPlus) return line.replace(DIFF_PLUS_RE, \"\");\n\t\t\tif (stats.diffPlusHashPrefixCount > 0 && HL_PREFIX_PLUS_RE.test(line)) {\n\t\t\t\treturn line.replace(HL_PREFIX_RE, \"\");\n\t\t\t}\n\t\t\treturn line;\n\t\t});\n}\n\n/**\n * Strict variant: strip hashline prefixes only when every content line is\n * hashline-prefixed. Returns the lines unchanged otherwise.\n */\nexport function stripHashlinePrefixes(lines: string[]): string[] {\n\tconst stats = collectLinePrefixStats(lines);\n\tif (stats.nonEmpty === 0) return lines;\n\tconst contentLineCount = stats.nonEmpty - stats.headerCount;\n\tif (contentLineCount === 0 || stats.hashPrefixCount !== contentLineCount) return lines;\n\treturn lines\n\t\t.filter(line => !READ_TRUNCATION_NOTICE_RE.test(line) && !HL_HEADER_RE.test(line))\n\t\t.map(line => stripLeadingHashlinePrefixes(line));\n}\n\n/**\n * Normalize line payloads by stripping read/search line prefixes. `null` /\n * `undefined` yield `[]`; a single multiline string is split on `\\n`.\n */\nexport function hashlineParseText(edit: string[] | string | null | undefined): string[] {\n\tif (edit == null) return [];\n\tif (typeof edit === \"string\") {\n\t\tconst trimmed = edit.endsWith(\"\\n\") ? edit.slice(0, -1) : edit;\n\t\tedit = trimmed.replaceAll(\"\\r\", \"\").split(\"\\n\");\n\t}\n\treturn stripNewLinePrefixes(edit);\n}\n"]}
1
+ {"version":3,"file":"prefixes.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/prefixes.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AAmBH;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElE;AAwCD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAwB9D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAQ/D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAOtF"}
@@ -1 +1 @@
1
- {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/recovery.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAY,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,KAAK,EAAuB,IAAI,EAAE,MAAM,YAAY,CAAC;AAO5D,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC9B,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,qFAAqF;IACrF,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AAkHD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,QAAQ;IACpB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,YAAY,KAAK,EAAE,aAAa,EAAyB;IACzD;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,cAAc,GAAG,IAAI,CAcpD;CACD","sourcesContent":["// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).\n/**\n * Recover from a stale section snapshot tag by replaying the would-be edit\n * against a cached pre-edit snapshot of the file and 3-way-merging the\n * result onto the current on-disk content.\n *\n * The patcher consults this when a section tag resolves to a snapshot that no\n * longer matches the live file content. The recovery class is stateless apart\n * from the {@link SnapshotStore} it queries; the snapshot store is the seam\n * lets you plug in your own caching strategy.\n */\nimport * as Diff from \"diff\";\nimport { applyEdits } from \"./apply.js\";\nimport { RECOVERY_EXTERNAL_WARNING, RECOVERY_SESSION_CHAIN_WARNING, RECOVERY_SESSION_REPLAY_WARNING } from \"./messages.js\";\nimport type { Snapshot, SnapshotStore } from \"./snapshots.js\";\nimport type { Anchor, ApplyResult, Edit } from \"./types.js\";\n\n// Section tags are line-precise; never let Diff.applyPatch slide a hunk\n// onto a duplicate closer 100+ lines away. If snapshot replay does not\n// align exactly, refuse and let the caller re-read.\nconst RECOVERY_FUZZ_FACTOR = 0;\n\nexport interface RecoveryArgs {\n\tpath: string;\n\tcurrentText: string;\n\tfileHash: string;\n\tedits: readonly Edit[];\n}\n\nexport interface RecoveryResult {\n\t/** Post-recovery text. */\n\ttext: string;\n\t/** First changed line (1-indexed) relative to the live `currentText`, or `undefined`. */\n\tfirstChangedLine: number | undefined;\n\t/** Warnings collected during recovery, including the user-facing recovery banner. */\n\twarnings: string[];\n}\n\nfunction applyEditsToSnapshot(\n\tpreviousText: string,\n\tcurrentText: string,\n\tedits: readonly Edit[],\n\trecoveryWarning: string,\n): RecoveryResult | null {\n\tlet applied: ApplyResult;\n\ttry {\n\t\tapplied = applyEdits(previousText, [...edits]);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (applied.text === previousText) return null;\n\n\tconst patch = Diff.structuredPatch(\"file\", \"file\", previousText, applied.text, \"\", \"\", { context: 3 });\n\tconst merged = Diff.applyPatch(currentText, patch, { fuzzFactor: RECOVERY_FUZZ_FACTOR });\n\tif (typeof merged !== \"string\" || merged === currentText) return null;\n\n\tconst firstChangedLine = findFirstChangedLine(currentText, merged) ?? applied.firstChangedLine;\n\tconst hasNetChange = firstChangedLine !== undefined;\n\tconst warnings = hasNetChange ? [recoveryWarning, ...(applied.warnings ?? [])] : [...(applied.warnings ?? [])];\n\n\treturn { text: merged, firstChangedLine, warnings };\n}\n\nfunction collectAnchorLines(edits: readonly Edit[]): number[] {\n\tconst lines: number[] = [];\n\tfor (const edit of edits) {\n\t\tfor (const anchor of getEditAnchors(edit)) lines.push(anchor.line);\n\t}\n\treturn lines;\n}\n\nfunction getEditAnchors(edit: Edit): Anchor[] {\n\tif (edit.kind === \"delete\") return [edit.anchor];\n\t// Recovery only ever receives already-resolved edits (no `block`); this arm\n\t// exists for type-exhaustiveness over the full `Edit` union.\n\tif (edit.kind === \"block\") return [edit.anchor];\n\treturn edit.cursor.kind === \"before_anchor\" || edit.cursor.kind === \"after_anchor\" ? [edit.cursor.anchor] : [];\n}\n\n/**\n * Returns true when every anchor line in `edits` has identical content in\n * `previousText` and `currentText`. The session-chain replay fast-path\n * requires this: if the prior in-session edit rewrote the line the model is\n * now re-targeting with a stale hash, replaying onto current would silently\n * overwrite the new content with whatever the model authored against the\n * old content — a corruption window, not a recovery.\n */\nfunction verifyAnchorContent(previousText: string, currentText: string, edits: readonly Edit[]): boolean {\n\tconst lines = collectAnchorLines(edits);\n\tif (lines.length === 0) return true;\n\tconst prev = previousText.split(\"\\n\");\n\tconst curr = currentText.split(\"\\n\");\n\tfor (const line of lines) {\n\t\tconst idx = line - 1;\n\t\tif (idx < 0 || idx >= prev.length || idx >= curr.length) return false;\n\t\tif (prev[idx] !== curr[idx]) return false;\n\t}\n\treturn true;\n}\n\nfunction replaySessionChainOnCurrent(\n\tpreviousText: string,\n\tcurrentText: string,\n\tedits: readonly Edit[],\n): RecoveryResult | null {\n\t// Two guards narrow the corruption window. Neither alone is sufficient,\n\t// and even together they don't fully prove correctness — replay is the\n\t// less-certain recovery mode and emits RECOVERY_SESSION_REPLAY_WARNING\n\t// so the caller can verify the diff.\n\t// - Equal line counts: every line number in `edits` still resolves to\n\t// SOME logical row (no net shift across the prior chain). A\n\t// coincidental insert+delete pair can still leave indices pointing\n\t// at different logical rows than the model anchored against.\n\t// - Anchor-content alignment: the row at each anchor's line index has\n\t// identical content in previous and current. Catches the common\n\t// case of a prior edit rewriting the targeted line; can still be\n\t// coincidentally satisfied by a duplicated row at the shifted\n\t// index.\n\tif (previousText.split(\"\\n\").length !== currentText.split(\"\\n\").length) return null;\n\tif (!verifyAnchorContent(previousText, currentText, edits)) return null;\n\tlet applied: ApplyResult;\n\ttry {\n\t\tapplied = applyEdits(currentText, [...edits]);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (applied.text === currentText) return null;\n\treturn {\n\t\ttext: applied.text,\n\t\tfirstChangedLine: applied.firstChangedLine,\n\t\twarnings: [RECOVERY_SESSION_REPLAY_WARNING, ...(applied.warnings ?? [])],\n\t};\n}\n\n/** First 1-indexed line at which `a` and `b` diverge, or `undefined` if equal. */\nfunction findFirstChangedLine(a: string, b: string): number | undefined {\n\tif (a === b) return undefined;\n\tconst aLines = a.split(\"\\n\");\n\tconst bLines = b.split(\"\\n\");\n\tconst max = Math.max(aLines.length, bLines.length);\n\tfor (let i = 0; i < max; i++) {\n\t\tif (aLines[i] !== bLines[i]) return i + 1;\n\t}\n\treturn undefined;\n}\n\nfunction isHeadSnapshot(head: Snapshot | null, snapshot: Snapshot): boolean {\n\treturn head === snapshot;\n}\n\n/**\n * Stateless recovery driver over a {@link SnapshotStore}. Construct once and\n * call {@link Recovery.tryRecover} per stale-tag incident. The default\n * implementation tries two strategies in order:\n *\n * 1. Apply the edits on the full-file version the tag names, then 3-way-merge\n * the resulting patch onto the live content (handles external writes).\n * 2. (Session chain) If that version wasn't the head, replay the edits onto\n * the live content directly when line counts match AND every edit's anchor\n * line content is unchanged between version and current — a prior in-session\n * edit advanced the tag and the model's anchors still name the same logical\n * rows. Emits a dedicated {@link RECOVERY_SESSION_REPLAY_WARNING} because\n * even with both guards a coincidental insert+delete pair on duplicate rows\n * can still land the edit on the wrong row; see {@link replaySessionChainOnCurrent}.\n */\nexport class Recovery {\n\treadonly store: SnapshotStore;\n\tconstructor(store: SnapshotStore) { this.store = store; }\n\t/**\n\t * Attempt recovery. Returns `null` when no path forward is found — the\n\t * caller should then surface a {@link MismatchError}.\n\t */\n\ttryRecover(args: RecoveryArgs): RecoveryResult | null {\n\t\tconst { path, currentText, fileHash, edits } = args;\n\t\tconst snapshot = this.store.byHash(path, fileHash);\n\t\tif (!snapshot) return null;\n\t\tconst isHead = isHeadSnapshot(this.store.head(path), snapshot);\n\t\tconst recoveryWarning = isHead ? RECOVERY_EXTERNAL_WARNING : RECOVERY_SESSION_CHAIN_WARNING;\n\t\tconst merged = applyEditsToSnapshot(snapshot.text, currentText, edits, recoveryWarning);\n\t\tif (merged !== null) return merged;\n\t\t// Session-chain fallback: the 3-way merge on the version refused.\n\t\t// Replay onto current is gated by line-count equality AND\n\t\t// anchor-content alignment — see `replaySessionChainOnCurrent`\n\t\t// for why both guards together still don't fully prove correctness.\n\t\tif (!isHead) return replaySessionChainOnCurrent(snapshot.text, currentText, edits);\n\t\treturn null;\n\t}\n}\n"]}
1
+ {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/recovery.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAY,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,KAAK,EAAuB,IAAI,EAAE,MAAM,YAAY,CAAC;AAO5D,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC9B,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,qFAAqF;IACrF,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AAkHD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,QAAQ;IACpB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,YAAY,KAAK,EAAE,aAAa,EAAyB;IACzD;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,cAAc,GAAG,IAAI,CAcpD;CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"snapshots.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/snapshots.ts"],"names":[],"mappings":"AA2BA;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACxB,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,8BAAsB,aAAa;IAClC,oEAAoE;IACpE,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAE7C,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAE7D,iFAAiF;IACjF,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAGvE;IAED,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAExD,kDAAkD;IAClD,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAExC,kCAAkC;IAClC,QAAQ,CAAC,KAAK,IAAI,IAAI,CAAC;CACvB;AAOD,MAAM,WAAW,4BAA4B;IAC5C,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,qBAAa,qBAAsB,SAAQ,aAAa;;IAIvD,YAAY,OAAO,GAAE,4BAAiC,EAYrD;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAElC;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAKlD;IAEQ,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAEhF;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAoB7C;IAED,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE7B;IAED,KAAK,IAAI,IAAI,CAEZ;CACD","sourcesContent":["// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).\n/**\n * Per-session snapshot store used by {@link Recovery} and {@link Patcher} to\n * bind hashline section tags to the exact file content that minted them.\n *\n * A section tag is a content-derived hash of the *whole file* (see\n * {@link computeFileHash}). Any read of byte-identical content mints the same\n * tag, so reads of one file state fuse onto one anchor and a follow-up edit\n * anchored at any line validates whenever the live file still hashes to it.\n *\n * Producers (typically `read` / `search` / `write` tools) call\n * {@link SnapshotStore.record} with the full normalized text they observed.\n * The store hashes it, dedups against the per-path history, and returns the\n * tag. Consumers (the patcher) resolve an unambiguous stale tag back to the\n * recorded full text via {@link SnapshotStore.byHash} and 3-way-merge the\n * would-be edit onto the live content.\n *\n * The abstract base class lets callers plug in whatever storage they like\n * (LRU, persistent SQLite, etc.). {@link InMemorySnapshotStore} ships as a\n * sensible default backed by `lru-cache`: a bounded set of paths, each with a\n * short history of full-file versions so in-session edit chains can still\n * recover against the version a stale tag names.\n */\nimport { LRUCache } from \"lru-cache/raw\";\nimport { computeFileHash } from \"./format.js\";\n\n/**\n * One full-file version observed at a point in time. The tag the model sees is\n * {@link Snapshot.hash}; recovery replays edits against {@link Snapshot.text}.\n */\nexport interface Snapshot {\n\t/** Canonical path this version belongs to. */\n\treadonly path: string;\n\t/** Full normalized (LF, no BOM) file text as observed. */\n\treadonly text: string;\n\t/** Content-derived tag for {@link Snapshot.text} (see {@link computeFileHash}). */\n\treadonly hash: string;\n\t/** Timestamp (ms since epoch) the version was recorded. */\n\trecordedAt: number;\n}\n\n/**\n * Storage seam for full-file version snapshots. The patcher calls {@link head}\n * for the latest version of a path and {@link byHash} when it needs the\n * specific historical version a section's stale tag names.\n */\nexport abstract class SnapshotStore {\n\t/** Most-recently recorded version for `path`, or `null` if none. */\n\tabstract head(path: string): Snapshot | null;\n\n\t/** Recorded unambiguous version for `path` whose tag equals `hash`, or `null`. */\n\tabstract byHash(path: string, hash: string): Snapshot | null;\n\n\t/** Recorded version for `path` whose tag and full text both match, or `null`. */\n\tbyHashAndText(path: string, hash: string, text: string): Snapshot | null {\n\t\tconst snapshot = this.byHash(path, hash);\n\t\treturn snapshot?.text === text ? snapshot : null;\n\t}\n\n\t/** Record the full normalized text of `path` and return its content tag. */\n\tabstract record(path: string, fullText: string): string;\n\n\t/** Drop the version history for a single path. */\n\tabstract invalidate(path: string): void;\n\n\t/** Drop every version history. */\n\tabstract clear(): void;\n}\n\nconst DEFAULT_MAX_PATHS = 30;\nconst DEFAULT_MAX_VERSIONS_PER_PATH = 4;\n/** Global ceiling on retained snapshot text across all paths (UTF-16 code units). */\nconst DEFAULT_MAX_TOTAL_BYTES = 64 * 1024 * 1024;\n\nexport interface InMemorySnapshotStoreOptions {\n\t/** Maximum number of distinct paths tracked at once (default 30). LRU eviction. */\n\tmaxPaths?: number;\n\t/** Maximum full-file versions retained per path (default 4). Oldest dropped first. */\n\tmaxVersionsPerPath?: number;\n\t/**\n\t * Global ceiling on retained snapshot text summed across every path's\n\t * version history, measured in UTF-16 code units (default 64 MiB).\n\t * Least-recently-used path histories are evicted to stay under it.\n\t */\n\tmaxTotalBytes?: number;\n}\n\n/**\n * In-memory {@link SnapshotStore} backed by `lru-cache`. Per-path history is a\n * short ring of full-file versions (oldest dropped first); per-session path\n * tracking is LRU-bounded so cold paths age out automatically.\n *\n * Recording byte-identical content again refreshes recency and reuses the\n * existing tag (read fusion); recording new content unshifts a fresh version\n * onto the front of the path history.\n */\nexport class InMemorySnapshotStore extends SnapshotStore {\n\treadonly #versions: LRUCache<string, Snapshot[]>;\n\treadonly #maxVersionsPerPath: number;\n\n\tconstructor(options: InMemorySnapshotStoreOptions = {}) {\n\t\tsuper();\n\t\tthis.#versions = new LRUCache<string, Snapshot[]>({\n\t\t\tmax: options.maxPaths ?? DEFAULT_MAX_PATHS,\n\t\t\tmaxSize: options.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES,\n\t\t\tsizeCalculation: history => {\n\t\t\t\tlet total = 1;\n\t\t\t\tfor (const version of history) total += version.text.length;\n\t\t\t\treturn total;\n\t\t\t},\n\t\t});\n\t\tthis.#maxVersionsPerPath = options.maxVersionsPerPath ?? DEFAULT_MAX_VERSIONS_PER_PATH;\n\t}\n\n\thead(path: string): Snapshot | null {\n\t\treturn this.#versions.get(path)?.[0] ?? null;\n\t}\n\n\tbyHash(path: string, hash: string): Snapshot | null {\n\t\tconst matches = this.#versions.get(path)?.filter(version => version.hash === hash) ?? [];\n\t\t// A 4-hex tag can collide. Recovery only proceeds when the tag maps to a\n\t\t// single retained snapshot; otherwise the caller must re-read.\n\t\treturn matches.length === 1 ? matches[0]! : null;\n\t}\n\n\toverride byHashAndText(path: string, hash: string, text: string): Snapshot | null {\n\t\treturn this.#versions.get(path)?.find(version => version.hash === hash && version.text === text) ?? null;\n\t}\n\n\trecord(path: string, fullText: string): string {\n\t\tconst hash = computeFileHash(fullText);\n\t\t// `get` refreshes LRU recency for `path`.\n\t\tconst history = this.#versions.get(path) ?? [];\n\t\tconst existing = history.find(version => version.hash === hash && version.text === fullText);\n\t\tif (existing) {\n\t\t\t// Same content state observed again: refresh recency and promote to\n\t\t\t// head (it is the current file content), then reuse the tag. Hash\n\t\t\t// collisions with different text must be retained as distinct\n\t\t\t// snapshots; the 4-hex tag is only a lookup key, not proof of identity.\n\t\t\texisting.recordedAt = Date.now();\n\t\t\tif (history[0] !== existing) {\n\t\t\t\tthis.#versions.set(path, [existing, ...history.filter(version => version !== existing)]);\n\t\t\t}\n\t\t\treturn hash;\n\t\t}\n\n\t\tconst snapshot: Snapshot = { path, text: fullText, hash, recordedAt: Date.now() };\n\t\tthis.#versions.set(path, [snapshot, ...history].slice(0, this.#maxVersionsPerPath));\n\t\treturn hash;\n\t}\n\n\tinvalidate(path: string): void {\n\t\tthis.#versions.delete(path);\n\t}\n\n\tclear(): void {\n\t\tthis.#versions.clear();\n\t}\n}\n"]}
1
+ {"version":3,"file":"snapshots.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/snapshots.ts"],"names":[],"mappings":"AA2BA;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACxB,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,8BAAsB,aAAa;IAClC,oEAAoE;IACpE,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAE7C,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAE7D,iFAAiF;IACjF,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAGvE;IAED,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAExD,kDAAkD;IAClD,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAExC,kCAAkC;IAClC,QAAQ,CAAC,KAAK,IAAI,IAAI,CAAC;CACvB;AAOD,MAAM,WAAW,4BAA4B;IAC5C,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,qBAAa,qBAAsB,SAAQ,aAAa;;IAIvD,YAAY,OAAO,GAAE,4BAAiC,EAYrD;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAElC;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAKlD;IAEQ,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAEhF;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAoB7C;IAED,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE7B;IAED,KAAK,IAAI,IAAI,CAEZ;CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/stream.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAoFhD,wBAAuB,eAAe,CACrC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC,EAC9D,OAAO,GAAE,aAAkB,GACzB,cAAc,CAAC,MAAM,CAAC,CAkCxB","sourcesContent":["// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).\n/**\n * Lazily format a stream of UTF-8 bytes into hashline-numbered lines, yielded\n * as bounded text chunks. Used to send `read`-style file content to consumers\n * without materializing the full file at once.\n *\n * Each yielded chunk is at most {@link StreamOptions.maxChunkLines} lines and\n * at most {@link StreamOptions.maxChunkBytes} UTF-8 bytes (whichever fires\n * first).\n */\nimport { formatNumberedLine } from \"./format.js\";\nimport type { StreamOptions } from \"./types.js\";\n\ninterface ResolvedStreamOptions {\n\tstartLine: number;\n\tmaxChunkLines: number;\n\tmaxChunkBytes: number;\n}\n\nfunction resolveStreamOptions(options: StreamOptions): ResolvedStreamOptions {\n\treturn {\n\t\tstartLine: options.startLine ?? 1,\n\t\tmaxChunkLines: options.maxChunkLines ?? 200,\n\t\tmaxChunkBytes: options.maxChunkBytes ?? 64 * 1024,\n\t};\n}\n\ninterface ChunkEmitter {\n\tpushLine: (line: string) => string[];\n\tflush: () => string | undefined;\n}\n\nfunction createChunkEmitter(options: ResolvedStreamOptions): ChunkEmitter {\n\tlet lineNumber = options.startLine;\n\tlet outLines: string[] = [];\n\tlet outBytes = 0;\n\n\tconst flush = (): string | undefined => {\n\t\tif (outLines.length === 0) return undefined;\n\t\tconst chunk = outLines.join(\"\\n\");\n\t\toutLines = [];\n\t\toutBytes = 0;\n\t\treturn chunk;\n\t};\n\n\tconst pushLine = (line: string): string[] => {\n\t\tconst formatted = formatNumberedLine(lineNumber, line);\n\t\tlineNumber++;\n\n\t\tconst chunks: string[] = [];\n\t\tconst sepBytes = outLines.length === 0 ? 0 : 1;\n\t\tconst lineBytes = Buffer.byteLength(formatted, \"utf-8\");\n\t\tconst wouldOverflow =\n\t\t\toutLines.length >= options.maxChunkLines || outBytes + sepBytes + lineBytes > options.maxChunkBytes;\n\n\t\tif (outLines.length > 0 && wouldOverflow) {\n\t\t\tconst flushed = flush();\n\t\t\tif (flushed) chunks.push(flushed);\n\t\t}\n\n\t\toutLines.push(formatted);\n\t\toutBytes += (outLines.length === 1 ? 0 : 1) + lineBytes;\n\n\t\tif (outLines.length >= options.maxChunkLines || outBytes >= options.maxChunkBytes) {\n\t\t\tconst flushed = flush();\n\t\t\tif (flushed) chunks.push(flushed);\n\t\t}\n\t\treturn chunks;\n\t};\n\n\treturn { pushLine, flush };\n}\n\nfunction isReadableStream(value: unknown): value is ReadableStream<Uint8Array> {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"getReader\" in value &&\n\t\ttypeof (value as { getReader?: unknown }).getReader === \"function\"\n\t);\n}\n\nasync function* bytesFromReadableStream(stream: ReadableStream<Uint8Array>): AsyncGenerator<Uint8Array> {\n\tconst reader = stream.getReader();\n\ttry {\n\t\twhile (true) {\n\t\t\tconst { done, value } = await reader.read();\n\t\t\tif (done) return;\n\t\t\tif (value) yield value;\n\t\t}\n\t} finally {\n\t\treader.releaseLock();\n\t}\n}\n\nexport async function* streamHashLines(\n\tsource: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>,\n\toptions: StreamOptions = {},\n): AsyncGenerator<string> {\n\tconst resolved = resolveStreamOptions(options);\n\tconst decoder = new TextDecoder(\"utf-8\");\n\tconst chunks = isReadableStream(source) ? bytesFromReadableStream(source) : source;\n\tconst emitter = createChunkEmitter(resolved);\n\n\tlet pending = \"\";\n\tlet sawAnyLine = false;\n\n\tfor await (const chunk of chunks) {\n\t\tpending += decoder.decode(chunk, { stream: true });\n\t\tlet nl = pending.indexOf(\"\\n\");\n\t\twhile (nl !== -1) {\n\t\t\tconst raw = pending.slice(0, nl);\n\t\t\tconst line = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n\t\t\tsawAnyLine = true;\n\t\t\tfor (const out of emitter.pushLine(line)) yield out;\n\t\t\tpending = pending.slice(nl + 1);\n\t\t\tnl = pending.indexOf(\"\\n\");\n\t\t}\n\t}\n\n\tpending += decoder.decode();\n\tif (pending.length > 0) {\n\t\tsawAnyLine = true;\n\t\tconst tail = pending.endsWith(\"\\r\") ? pending.slice(0, -1) : pending;\n\t\tfor (const out of emitter.pushLine(tail)) yield out;\n\t}\n\tif (!sawAnyLine) {\n\t\tfor (const out of emitter.pushLine(\"\")) yield out;\n\t}\n\n\tconst last = emitter.flush();\n\tif (last) yield last;\n}\n"]}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/stream.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAoFhD,wBAAuB,eAAe,CACrC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC,EAC9D,OAAO,GAAE,aAAkB,GACzB,cAAc,CAAC,MAAM,CAAC,CAkCxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/tokenizer.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA8D9D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBzD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIlD;AAoBD,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAW7D;AAsDD,MAAM,MAAM,WAAW,GACpB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GACf;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC;AAgKnB,UAAU,SAAS;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,KAAK,GACd,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,GAC/B,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAAC,GACxC,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,CAAC,GACtC,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,GAC/B,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACjE,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CAAC,GACvD,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GACvD,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AA6B/C,qBAAa,SAAS;;IAKrB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE,CAK3B;IAED,GAAG,IAAI,KAAK,EAAE,CASb;IAED,KAAK,IAAI,IAAI,CAIZ;IAED,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,EAAE,CAKjC;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,SAAI,GAAG,KAAK,CAEzC;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1B;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9B;IAED,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMtC;CAgBD;AAED,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC","sourcesContent":["// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).\n/**\n * Stateful, line-oriented classifier for hashline diff text.\n *\n * Format shape:\n * ```\n * [path/to/file.ts#1A2B]\n * replace 5..7:\n * +literal new line\n * ```\n */\nimport {\n\tdescribeAnchorExamples,\n\tHL_BLOCK_KEYWORD,\n\tHL_DELETE_KEYWORD,\n\tHL_FILE_HASH_LENGTH,\n\tHL_FILE_HASH_SEP,\n\tHL_FILE_PREFIX,\n\tHL_FILE_SUFFIX,\n\tHL_HEADER_COLON,\n\tHL_INSERT_AFTER,\n\tHL_INSERT_BEFORE,\n\tHL_INSERT_HEAD,\n\tHL_INSERT_KEYWORD,\n\tHL_INSERT_TAIL,\n\tHL_PAYLOAD_REPLACE,\n\tHL_REPLACE_KEYWORD,\n} from \"./format.js\";\nimport { ABORT_MARKER, BEGIN_PATCH_MARKER, END_PATCH_MARKER } from \"./messages.js\";\nimport type { Anchor, Cursor, ParsedRange } from \"./types.js\";\n\nconst CHAR_LINE_FEED = 10;\nconst CHAR_CARRIAGE_RETURN = 13;\nconst CHAR_ZERO = 48;\nconst CHAR_NINE = 57;\nconst CHAR_HASH = 35;\nconst CHAR_TAB = 9;\nconst CHAR_SPACE = 32;\nconst CHAR_DOT = 46;\nconst CHAR_HYPHEN = 45;\nconst CHAR_ELLIPSIS = 0x2026;\n\nconst CHAR_UPPER_A = 65;\nconst CHAR_UPPER_F = 70;\nconst CHAR_LOWER_A = 97;\nconst CHAR_LOWER_F = 102;\nconst CHAR_PAYLOAD_REPLACE = HL_PAYLOAD_REPLACE.charCodeAt(0);\nconst CHAR_COLON = HL_HEADER_COLON.charCodeAt(0);\nconst FILE_PREFIX_LENGTH = HL_FILE_PREFIX.length;\nconst FILE_SUFFIX_LENGTH = HL_FILE_SUFFIX.length;\n\nfunction isDigitCode(code: number): boolean {\n\treturn code >= CHAR_ZERO && code <= CHAR_NINE;\n}\n\nfunction isNonZeroDigitCode(code: number): boolean {\n\treturn code > CHAR_ZERO && code <= CHAR_NINE;\n}\n\nfunction isHexDigitCode(code: number): boolean {\n\treturn (\n\t\tisDigitCode(code) ||\n\t\t(code >= CHAR_UPPER_A && code <= CHAR_UPPER_F) ||\n\t\t(code >= CHAR_LOWER_A && code <= CHAR_LOWER_F)\n\t);\n}\n\nfunction isWhitespaceCode(code: number): boolean {\n\treturn code === CHAR_SPACE || (code >= CHAR_TAB && code <= CHAR_CARRIAGE_RETURN);\n}\n\nfunction skipWhitespace(line: string, index: number, end = line.length): number {\n\twhile (index < end && isWhitespaceCode(line.charCodeAt(index))) index++;\n\treturn index;\n}\n\nfunction trimEndIndex(line: string): number {\n\tlet end = line.length;\n\twhile (end > 0 && isWhitespaceCode(line.charCodeAt(end - 1))) end--;\n\treturn end;\n}\n\nfunction isEmptyLine(line: string): boolean {\n\treturn line.length === 0;\n}\n\nfunction markerLineEquals(line: string, marker: string): boolean {\n\tconst end = trimEndIndex(line);\n\treturn end === marker.length && line.startsWith(marker);\n}\n\nexport function splitHashlineLines(text: string): string[] {\n\tif (text.length === 0) return [\"\"];\n\tconst lines: string[] = [];\n\tlet start = 0;\n\tfor (let index = 0; index < text.length; index++) {\n\t\tif (text.charCodeAt(index) !== CHAR_LINE_FEED) continue;\n\t\tlet end = index;\n\t\tif (end > start && text.charCodeAt(end - 1) === CHAR_CARRIAGE_RETURN) end--;\n\t\tlines.push(text.slice(start, end));\n\t\tstart = index + 1;\n\t}\n\tif (start < text.length) {\n\t\tlet end = text.length;\n\t\tif (end > start && text.charCodeAt(end - 1) === CHAR_CARRIAGE_RETURN) end--;\n\t\tlines.push(text.slice(start, end));\n\t}\n\treturn lines;\n}\n\nexport function cloneCursor(cursor: Cursor): Cursor {\n\tif (cursor.kind === \"before_anchor\") return { kind: \"before_anchor\", anchor: { ...cursor.anchor } };\n\tif (cursor.kind === \"after_anchor\") return { kind: \"after_anchor\", anchor: { ...cursor.anchor } };\n\treturn cursor;\n}\n\ninterface NumberScan {\n\tline: number;\n\tnextIndex: number;\n}\n\nfunction scanLineNumber(line: string, index: number, end: number): NumberScan | null {\n\tif (index >= end || !isNonZeroDigitCode(line.charCodeAt(index))) return null;\n\tlet lineNumber = 0;\n\tlet nextIndex = index;\n\twhile (nextIndex < end) {\n\t\tconst code = line.charCodeAt(nextIndex);\n\t\tif (!isDigitCode(code)) break;\n\t\tlineNumber = lineNumber * 10 + (code - CHAR_ZERO);\n\t\tnextIndex++;\n\t}\n\treturn { line: lineNumber, nextIndex };\n}\n\n/** Parse a bare line-number anchor. Throws on malformed input. */\nexport function parseLid(raw: string, lineNum: number): Anchor {\n\tconst end = trimEndIndex(raw);\n\tconst numberStart = skipWhitespace(raw, 0, end);\n\tconst number = scanLineNumber(raw, numberStart, end);\n\tif (number === null || skipWhitespace(raw, number.nextIndex, end) !== end) {\n\t\tthrow new Error(\n\t\t\t`line ${lineNum}: expected a line number such as ${describeAnchorExamples(\"119\")}; ` +\n\t\t\t\t`got ${JSON.stringify(raw)}. Use ${HL_FILE_PREFIX}PATH${HL_FILE_HASH_SEP}hash${HL_FILE_SUFFIX} from your latest read for file-version binding.`,\n\t\t);\n\t}\n\treturn { line: number.line };\n}\n\ninterface RangeScan {\n\trange: ParsedRange;\n\tnextIndex: number;\n}\n\nfunction scanRangeSeparator(line: string, index: number, end: number): number | null {\n\tlet cursor = index;\n\tlet consumedSeparator = false;\n\twhile (cursor < end) {\n\t\tconst code = line.charCodeAt(cursor);\n\t\tif (isWhitespaceCode(code)) {\n\t\t\tcursor++;\n\t\t\tconsumedSeparator = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (code === CHAR_HYPHEN || code === CHAR_ELLIPSIS) {\n\t\t\tcursor++;\n\t\t\tconsumedSeparator = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (code === CHAR_DOT && cursor + 1 < end && line.charCodeAt(cursor + 1) === CHAR_DOT) {\n\t\t\tcursor += 2;\n\t\t\tconsumedSeparator = true;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\tif (!consumedSeparator) return null;\n\tif (cursor >= end || !isNonZeroDigitCode(line.charCodeAt(cursor))) return null;\n\treturn cursor;\n}\n\nfunction scanHeaderRange(line: string, index = 0, end = trimEndIndex(line), allowSingle = false): RangeScan | null {\n\tconst numberStart = skipWhitespace(line, index, end);\n\tconst start = scanLineNumber(line, numberStart, end);\n\tif (start === null) return null;\n\tconst afterFirst = scanRangeSeparator(line, start.nextIndex, end);\n\tif (afterFirst === null) {\n\t\tif (!allowSingle) return null;\n\t\treturn {\n\t\t\trange: { start: { line: start.line }, end: { line: start.line } },\n\t\t\tnextIndex: skipWhitespace(line, start.nextIndex, end),\n\t\t};\n\t}\n\tconst endNumber = scanLineNumber(line, afterFirst, end);\n\tif (endNumber === null) return null;\n\treturn {\n\t\trange: { start: { line: start.line }, end: { line: endNumber.line } },\n\t\tnextIndex: skipWhitespace(line, endNumber.nextIndex, end),\n\t};\n}\n\nexport type BlockTarget =\n\t| { kind: \"replace\"; range: ParsedRange }\n\t| { kind: \"block\"; anchor: Anchor }\n\t| { kind: \"delete\"; range: ParsedRange }\n\t| { kind: \"delete_block\"; anchor: Anchor }\n\t| { kind: \"insert_before\"; anchor: Anchor }\n\t| { kind: \"insert_after\"; anchor: Anchor }\n\t| { kind: \"insert_after_block\"; anchor: Anchor }\n\t| { kind: \"bof\" }\n\t| { kind: \"eof\" };\n\ninterface TargetScan {\n\ttarget: BlockTarget;\n\tnextIndex: number;\n}\n\nfunction scanKeyword(line: string, index: number, end: number, keyword: string): number | null {\n\tif (!line.startsWith(keyword, index)) return null;\n\tconst next = index + keyword.length;\n\tif (next < end) {\n\t\tconst code = line.charCodeAt(next);\n\t\tif (!isWhitespaceCode(code) && code !== CHAR_COLON) return null;\n\t}\n\treturn next;\n}\n\nfunction consumeOptionalColon(line: string, index: number, end: number): number {\n\tconst cursor = skipWhitespace(line, index, end);\n\treturn cursor < end && line.charCodeAt(cursor) === CHAR_COLON ? skipWhitespace(line, cursor + 1, end) : cursor;\n}\n\nfunction scanInsertTarget(line: string, index: number, end: number): TargetScan | null {\n\tconst cursor = skipWhitespace(line, index, end);\n\tconst beforeEnd = scanKeyword(line, cursor, end, HL_INSERT_BEFORE);\n\tif (beforeEnd !== null) {\n\t\tconst anchor = scanLineNumber(line, skipWhitespace(line, beforeEnd, end), end);\n\t\tif (anchor === null) return null;\n\t\tconst nextIndex = consumeOptionalColon(line, anchor.nextIndex, end);\n\t\treturn { target: { kind: \"insert_before\", anchor: { line: anchor.line } }, nextIndex };\n\t}\n\tconst afterEnd = scanKeyword(line, cursor, end, HL_INSERT_AFTER);\n\tif (afterEnd !== null) {\n\t\t// `insert after block N:` — resolve N to a tree-sitter block range at\n\t\t// apply time and insert after its last line. Try the `block` sub-keyword\n\t\t// before falling back to a literal `insert after N:` anchor.\n\t\tconst blockEnd = scanKeyword(line, skipWhitespace(line, afterEnd, end), end, HL_BLOCK_KEYWORD);\n\t\tif (blockEnd !== null) {\n\t\t\tconst anchor = scanLineNumber(line, skipWhitespace(line, blockEnd, end), end);\n\t\t\tif (anchor === null) return null;\n\t\t\tconst nextIndex = consumeOptionalColon(line, anchor.nextIndex, end);\n\t\t\treturn { target: { kind: \"insert_after_block\", anchor: { line: anchor.line } }, nextIndex };\n\t\t}\n\t\tconst anchor = scanLineNumber(line, skipWhitespace(line, afterEnd, end), end);\n\t\tif (anchor === null) return null;\n\t\tconst nextIndex = consumeOptionalColon(line, anchor.nextIndex, end);\n\t\treturn { target: { kind: \"insert_after\", anchor: { line: anchor.line } }, nextIndex };\n\t}\n\tconst headEnd = scanKeyword(line, cursor, end, HL_INSERT_HEAD);\n\tif (headEnd !== null) return { target: { kind: \"bof\" }, nextIndex: consumeOptionalColon(line, headEnd, end) };\n\tconst tailEnd = scanKeyword(line, cursor, end, HL_INSERT_TAIL);\n\tif (tailEnd !== null) return { target: { kind: \"eof\" }, nextIndex: consumeOptionalColon(line, tailEnd, end) };\n\treturn null;\n}\n\nfunction scanHunkAnchor(line: string, start: number, end: number): TargetScan | null {\n\tconst cursor = skipWhitespace(line, start, end);\n\tconst replaceEnd = scanKeyword(line, cursor, end, HL_REPLACE_KEYWORD);\n\tif (replaceEnd !== null) {\n\t\t// `replace block N:` — resolve N to a tree-sitter block range at apply\n\t\t// time. Try the `block` sub-keyword before falling back to a literal\n\t\t// `replace N..M:` range.\n\t\tconst blockEnd = scanKeyword(line, skipWhitespace(line, replaceEnd, end), end, HL_BLOCK_KEYWORD);\n\t\tif (blockEnd !== null) {\n\t\t\tconst anchor = scanLineNumber(line, skipWhitespace(line, blockEnd, end), end);\n\t\t\tif (anchor === null) return null;\n\t\t\treturn {\n\t\t\t\ttarget: { kind: \"block\", anchor: { line: anchor.line } },\n\t\t\t\tnextIndex: consumeOptionalColon(line, anchor.nextIndex, end),\n\t\t\t};\n\t\t}\n\t\tconst range = scanHeaderRange(line, replaceEnd, end, true);\n\t\tif (range === null) return null;\n\t\treturn {\n\t\t\ttarget: { kind: \"replace\", range: range.range },\n\t\t\tnextIndex: consumeOptionalColon(line, range.nextIndex, end),\n\t\t};\n\t}\n\tconst deleteEnd = scanKeyword(line, cursor, end, HL_DELETE_KEYWORD);\n\tif (deleteEnd !== null) {\n\t\t// `delete block N` — resolve N to a tree-sitter block range at apply\n\t\t// time and delete its whole span. Like `delete N..M`, it takes no body\n\t\t// and no trailing colon.\n\t\tconst blockEnd = scanKeyword(line, skipWhitespace(line, deleteEnd, end), end, HL_BLOCK_KEYWORD);\n\t\tif (blockEnd !== null) {\n\t\t\tconst anchor = scanLineNumber(line, skipWhitespace(line, blockEnd, end), end);\n\t\t\tif (anchor === null) return null;\n\t\t\tconst next = skipWhitespace(line, anchor.nextIndex, end);\n\t\t\tif (next < end && line.charCodeAt(next) === CHAR_COLON) return null;\n\t\t\treturn { target: { kind: \"delete_block\", anchor: { line: anchor.line } }, nextIndex: next };\n\t\t}\n\t\tconst range = scanHeaderRange(line, deleteEnd, end, true);\n\t\tif (range === null) return null;\n\t\tconst next = skipWhitespace(line, range.nextIndex, end);\n\t\tif (next < end && line.charCodeAt(next) === CHAR_COLON) return null;\n\t\treturn { target: { kind: \"delete\", range: range.range }, nextIndex: next };\n\t}\n\tconst insertEnd = scanKeyword(line, cursor, end, HL_INSERT_KEYWORD);\n\tif (insertEnd !== null) return scanInsertTarget(line, insertEnd, end);\n\treturn null;\n}\n\ninterface ParsedHunkHeader {\n\ttarget: BlockTarget;\n}\n\nfunction tryParseHunkHeader(line: string): ParsedHunkHeader | null {\n\tconst end = trimEndIndex(line);\n\tconst start = skipWhitespace(line, 0, end);\n\tif (start >= end) return null;\n\tconst scan = scanHunkAnchor(line, start, end);\n\tif (scan === null) return null;\n\tif (scan.nextIndex !== end) return null;\n\treturn { target: scan.target };\n}\n\nfunction tryParseHeader(line: string): { path: string; fileHash?: string } | null {\n\tif (!line.startsWith(HL_FILE_PREFIX)) return null;\n\tconst end = trimEndIndex(line);\n\tif (FILE_PREFIX_LENGTH + FILE_SUFFIX_LENGTH >= end) return null;\n\tif (!line.endsWith(HL_FILE_SUFFIX, end)) return null;\n\tconst bodyEnd = end - FILE_SUFFIX_LENGTH;\n\tif (FILE_PREFIX_LENGTH >= bodyEnd) return null;\n\n\t// The snapshot tag, when present, is the trailing `#XXXX` block inside the\n\t// bracketed header. We detect it from the suffix so the path may\n\t// legitimately contain whitespace (e.g. `OneDrive - Company/file.ts`).\n\tlet pathEnd = bodyEnd;\n\tlet fileHash: string | undefined;\n\tconst trailingHashStart = bodyEnd - HL_FILE_HASH_LENGTH - 1;\n\tif (trailingHashStart >= FILE_PREFIX_LENGTH && line.charCodeAt(trailingHashStart) === CHAR_HASH) {\n\t\tlet allHex = true;\n\t\tfor (let probe = trailingHashStart + 1; probe < bodyEnd; probe++) {\n\t\t\tif (!isHexDigitCode(line.charCodeAt(probe))) {\n\t\t\t\tallHex = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (allHex) {\n\t\t\tpathEnd = trailingHashStart;\n\t\t\tfileHash = line.slice(trailingHashStart + 1, bodyEnd).toUpperCase();\n\t\t}\n\t}\n\n\t// The hashline header grammar uses `#` as the path/tag separator and\n\t// does not allow `#` inside filenames. Anything `#` left in the path\n\t// body — short tags (`#1A2`), non-hex tags (`#1A2G`), over-long tags\n\t// (`#1A2B5`), stale-tag copy-paste (`#1A2B copied from read`), or\n\t// line-suffixed tags (`#1A2B:42`) — means the header is malformed.\n\t// Surface the focused diagnostic instead of silently mis-routing the\n\t// edit or reporting a missing tag downstream.\n\tfor (let i = FILE_PREFIX_LENGTH; i < pathEnd; i++) {\n\t\tif (line.charCodeAt(i) === CHAR_HASH) return null;\n\t}\n\n\tif (pathEnd === FILE_PREFIX_LENGTH) return null;\n\tconst path = line.slice(FILE_PREFIX_LENGTH, pathEnd);\n\treturn fileHash !== undefined ? { path, fileHash } : { path };\n}\n\ninterface TokenBase {\n\tlineNum: number;\n}\n\nexport type Token =\n\t| (TokenBase & { kind: \"blank\" })\n\t| (TokenBase & { kind: \"envelope-begin\" })\n\t| (TokenBase & { kind: \"envelope-end\" })\n\t| (TokenBase & { kind: \"abort\" })\n\t| (TokenBase & { kind: \"header\"; path: string; fileHash?: string })\n\t| (TokenBase & { kind: \"op-block\"; target: BlockTarget })\n\t| (TokenBase & { kind: \"payload-literal\"; text: string })\n\t| (TokenBase & { kind: \"raw\"; text: string });\n\nfunction classifyLine(line: string, lineNum: number): Token {\n\tif (isEmptyLine(line)) return { kind: \"blank\", lineNum };\n\tif (markerLineEquals(line, BEGIN_PATCH_MARKER)) return { kind: \"envelope-begin\", lineNum };\n\tif (markerLineEquals(line, END_PATCH_MARKER)) return { kind: \"envelope-end\", lineNum };\n\tif (markerLineEquals(line, ABORT_MARKER)) return { kind: \"abort\", lineNum };\n\tconst firstCode = line.charCodeAt(0);\n\tif (line.startsWith(HL_FILE_PREFIX)) {\n\t\tconst header = tryParseHeader(line);\n\t\tif (header !== null) {\n\t\t\treturn header.fileHash !== undefined\n\t\t\t\t? { kind: \"header\", lineNum, path: header.path, fileHash: header.fileHash }\n\t\t\t\t: { kind: \"header\", lineNum, path: header.path };\n\t\t}\n\t}\n\tconst lead = skipWhitespace(line, 0);\n\tconst isHunkLead =\n\t\tline.startsWith(HL_REPLACE_KEYWORD, lead) ||\n\t\tline.startsWith(HL_DELETE_KEYWORD, lead) ||\n\t\tline.startsWith(HL_INSERT_KEYWORD, lead);\n\tif (isHunkLead) {\n\t\tconst hunk = tryParseHunkHeader(line);\n\t\tif (hunk !== null) return { kind: \"op-block\", lineNum, target: hunk.target };\n\t}\n\tif (firstCode === CHAR_PAYLOAD_REPLACE) return { kind: \"payload-literal\", lineNum, text: line.slice(1) };\n\treturn { kind: \"raw\", lineNum, text: line };\n}\n\nexport class Tokenizer {\n\t#buffer = \"\";\n\t#nextLineNum = 1;\n\t#closed = false;\n\n\tfeed(chunk: string): Token[] {\n\t\tif (this.#closed) throw new Error(\"Tokenizer is closed; call reset() before reusing.\");\n\t\tif (chunk.length === 0) return [];\n\t\tthis.#buffer = this.#buffer ? this.#buffer + chunk : chunk;\n\t\treturn this.#drainCompleteLines();\n\t}\n\n\tend(): Token[] {\n\t\tif (this.#closed) return [];\n\t\tthis.#closed = true;\n\t\tconst buf = this.#buffer;\n\t\tthis.#buffer = \"\";\n\t\tif (buf.length === 0) return [];\n\t\tlet stop = buf.length;\n\t\tif (buf.charCodeAt(stop - 1) === CHAR_CARRIAGE_RETURN) stop--;\n\t\treturn [classifyLine(buf.slice(0, stop), this.#nextLineNum++)];\n\t}\n\n\treset(): void {\n\t\tthis.#buffer = \"\";\n\t\tthis.#nextLineNum = 1;\n\t\tthis.#closed = false;\n\t}\n\n\ttokenizeAll(text: string): Token[] {\n\t\tthis.reset();\n\t\tconst first = this.feed(text);\n\t\tconst last = this.end();\n\t\treturn last.length === 0 ? first : first.concat(last);\n\t}\n\n\ttokenize(line: string, lineNum = 0): Token {\n\t\treturn classifyLine(line, lineNum);\n\t}\n\n\tisOp(line: string): boolean {\n\t\treturn tryParseHunkHeader(line) !== null;\n\t}\n\n\tisHeader(line: string): boolean {\n\t\treturn tryParseHeader(line) !== null;\n\t}\n\n\tisEnvelopeMarker(line: string): boolean {\n\t\treturn (\n\t\t\tmarkerLineEquals(line, BEGIN_PATCH_MARKER) ||\n\t\t\tmarkerLineEquals(line, END_PATCH_MARKER) ||\n\t\t\tmarkerLineEquals(line, ABORT_MARKER)\n\t\t);\n\t}\n\n\t#drainCompleteLines(): Token[] {\n\t\tconst tokens: Token[] = [];\n\t\tconst buf = this.#buffer;\n\t\tlet start = 0;\n\t\tfor (let index = 0; index < buf.length; index++) {\n\t\t\tif (buf.charCodeAt(index) !== CHAR_LINE_FEED) continue;\n\t\t\tlet stop = index;\n\t\t\tif (stop > start && buf.charCodeAt(stop - 1) === CHAR_CARRIAGE_RETURN) stop--;\n\t\t\ttokens.push(classifyLine(buf.slice(start, stop), this.#nextLineNum++));\n\t\t\tstart = index + 1;\n\t\t}\n\t\tthis.#buffer = start < buf.length ? buf.slice(start) : \"\";\n\t\treturn tokens;\n\t}\n}\n\nexport type { ParsedRange } from \"./types.js\";\n"]}
1
+ {"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/tokenizer.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA8D9D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBzD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIlD;AAoBD,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAW7D;AAsDD,MAAM,MAAM,WAAW,GACpB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GACf;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC;AAgKnB,UAAU,SAAS;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,KAAK,GACd,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,GAC/B,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAAC,GACxC,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,CAAC,GACtC,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,GAC/B,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACjE,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CAAC,GACvD,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GACvD,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AA6B/C,qBAAa,SAAS;;IAKrB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE,CAK3B;IAED,GAAG,IAAI,KAAK,EAAE,CASb;IAED,KAAK,IAAI,IAAI,CAIZ;IAED,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,EAAE,CAKjC;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,SAAI,GAAG,KAAK,CAEzC;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1B;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9B;IAED,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMtC;CAgBD;AAED,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/types.ts"],"names":[],"mappings":"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","sourcesContent":["// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).\n/**\n * Pure data types shared across the hashline parser, applier, and patcher.\n * Nothing in this file references a filesystem, agent runtime, or schema\n * library — keep it that way.\n */\n\n/** A line-number anchor (1-indexed). */\nexport interface Anchor {\n\tline: number;\n}\n\n/** Where an `insert` edit should land relative to existing content. */\nexport type Cursor =\n\t| { kind: \"bof\" }\n\t| { kind: \"eof\" }\n\t| { kind: \"before_anchor\"; anchor: Anchor }\n\t| { kind: \"after_anchor\"; anchor: Anchor };\n\n/**\n * A single low-level edit produced by the parser and consumed by the applier.\n * Multi-line replacements decompose to one `insert` per replacement line plus\n * one `delete` per consumed line. Replacement payloads are tagged so the\n * applier can distinguish literal insertion from new content for a deleted\n * line.\n */\nexport type Edit =\n\t| {\n\t\t\tkind: \"insert\";\n\t\t\tcursor: Cursor;\n\t\t\ttext: string;\n\t\t\tlineNum: number;\n\t\t\tindex: number;\n\t\t\tmode?: \"replacement\";\n\t\t\t/**\n\t\t\t * Present on inserts lowered from `insert after block N:`: the\n\t\t\t * resolved block's first line. Lets the applier slide a body that\n\t\t\t * claims a depth inside the block back across the block's trailing\n\t\t\t * closer lines (never above this line).\n\t\t\t */\n\t\t\tblockStart?: number;\n\t }\n\t| { kind: \"delete\"; anchor: Anchor; lineNum: number; index: number; oldAssertion?: string }\n\t| {\n\t\t\t/**\n\t\t\t * Deferred block edit (`replace block N:` / `delete block N` /\n\t\t\t * `insert after block N:`). The exact line span is unknown at parse\n\t\t\t * time — it is computed by {@link resolveBlockEdits} once file text +\n\t\t\t * path (→ language) are available, then expanded into concrete edits:\n\t\t\t * a non-empty `payloads` without `mode` (from `replace block`) becomes\n\t\t\t * the same `replacement` inserts + deletes that `replace start..end:`\n\t\t\t * produces; an empty `payloads` (from `delete block`) becomes a pure\n\t\t\t * range deletion; `mode: \"insert_after\"` becomes plain `after_anchor`\n\t\t\t * inserts at the block's last line. `applyEdits` never sees this\n\t\t\t * variant.\n\t\t\t */\n\t\t\tkind: \"block\";\n\t\t\tanchor: Anchor;\n\t\t\tpayloads: string[];\n\t\t\tmode?: \"insert_after\";\n\t\t\tlineNum: number;\n\t\t\tindex: number;\n\t };\n\n/** Result of applying a parsed set of edits to a text body. */\nexport interface ApplyResult {\n\t/** Post-edit text body. */\n\ttext: string;\n\t/** First line number (1-indexed) that changed, or `undefined` for a no-op apply. */\n\tfirstChangedLine?: number;\n\t/** Diagnostic warnings collected by the parser, patcher, or recovery. */\n\twarnings?: string[];\n\t/**\n\t * Resolved spans for each `replace block`/`delete block` op in this apply,\n\t * in patch order. Present only when the apply matched the tagged content\n\t * (the common no-drift path), so the line numbers line up with what the\n\t * caller read. Absent when there were no block ops.\n\t */\n\tblockResolutions?: BlockResolution[];\n}\n\n/** A parsed `[A..B]` line range. */\nexport interface ParsedRange {\n\tstart: Anchor;\n\tend: Anchor;\n}\n\n/** Optional hints for {@link splitPatchInput}. */\nexport interface SplitOptions {\n\t/** Resolves absolute paths inside hashline headers to cwd-relative form. */\n\tcwd?: string;\n\t/**\n\t * Fallback path used when the input lacks a `[PATH]` header but contains\n\t * recognizable hashline operations. Lets streaming previews work before\n\t * the model has written the header.\n\t */\n\tpath?: string;\n}\n\n/** Streaming-formatter knobs for {@link streamHashLines}. */\nexport interface StreamOptions {\n\t/** First line number to use when formatting (1-indexed, default 1). */\n\tstartLine?: number;\n\t/** Maximum formatted lines per yielded chunk (default 200). */\n\tmaxChunkLines?: number;\n\t/** Maximum UTF-8 bytes per yielded chunk (default 64 KiB). */\n\tmaxChunkBytes?: number;\n}\n\n/** Result of {@link buildCompactDiffPreview}. */\nexport interface CompactDiffPreview {\n\tpreview: string;\n\taddedLines: number;\n\tremovedLines: number;\n}\n\n/** Optional knobs for {@link buildCompactDiffPreview}. */\nexport interface CompactDiffOptions {\n\t/** Added lines kept on each side of a long added-run elision (default 2). */\n\tmaxAddedRunContext?: number;\n\t/** Back-compat alias for {@link maxAddedRunContext}. */\n\tmaxUnchangedRun?: number;\n}\n\n/**\n * Resolved 1-indexed inclusive line span of a `replace block N:` target.\n */\nexport interface BlockSpan {\n\t/** First line of the block (1-indexed, inclusive). */\n\tstart: number;\n\t/** Last line of the block (1-indexed, inclusive). */\n\tend: number;\n}\n\n/**\n * One `replace block N:` / `delete block N` / `insert after block N:` anchor\n * resolved to its concrete line span. Surfaced on {@link ApplyResult} so the\n * host can echo \"block N → lines start..end\" and let the model catch a wrong\n * opener — e.g. a decorator or doc-comment that sits in a separate node\n * outside the resolved block.\n */\nexport interface BlockResolution {\n\t/** The 1-indexed line the block op was anchored on (the `N`). */\n\tanchorLine: number;\n\t/** First line of the resolved span (1-indexed, inclusive). */\n\tstart: number;\n\t/** Last line of the resolved span (1-indexed, inclusive). */\n\tend: number;\n\t/** Which block op produced this resolution. */\n\top: \"replace\" | \"delete\" | \"insert_after\";\n}\n\n/** Request handed to a {@link BlockResolver} to resolve one `replace block N:` anchor. */\nexport interface BlockResolverRequest {\n\t/** Target file path (used to infer language by extension). */\n\tpath: string;\n\t/** Full text the block must be resolved against (the snapshot the tag names). */\n\ttext: string;\n\t/** 1-indexed line the block must begin on. */\n\tline: number;\n}\n\n/**\n * Resolves a `replace block N:` anchor to the line span of the syntactic block\n * that begins on line N. Returns `null` when no block can be resolved\n * (unrecognized language, blank/out-of-range line, no node begins there, or the\n * resolved subtree has a syntax error). Pure seam: the hashline core declares\n * the contract; the host injects a tree-sitter-backed implementation.\n */\nexport type BlockResolver = (request: BlockResolverRequest) => BlockSpan | null;\n"]}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hashline.d.ts","sourceRoot":"","sources":["../../../src/core/tools/hashline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqF,KAAK,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAGnJ,MAAM,WAAW,gBAAgB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC7E,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAAC;CAC7E;AAMD,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAI7E;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,2BAA2B,IAAI,qBAAqB,CAkBnE;AAED,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,gBAAgB,CAEzI;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,SAAmB,EAAE,SAAS,SAAI,GAAG,MAAM,CAEnH;AAED,MAAM,WAAW,uBAAuB;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE;AAE/E,wBAAgB,uCAAuC,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,uBAAuB,CAoDnK;AAED,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAExI;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,QAAQ,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,CAExK","sourcesContent":["import { computeFileHash, formatHashlineHeader, formatNumberedLines, InMemorySnapshotStore, type SnapshotStore } from \"./hashline-engine/index.ts\";\nimport { isAbsolute, normalize as normalizePath, relative, sep } from \"node:path\";\n\nexport interface HashlineSnapshot {\n\tabsolutePath: string;\n\tdisplayPath: string;\n\ttag: string;\n\tcontent: string;\n}\n\nexport interface HashlineSnapshotStore {\n\treadonly snapshots: SnapshotStore;\n\trecord(absolutePath: string, cwd: string, content: string): HashlineSnapshot;\n\tfindByHeader(displayPath: string, tag: string): HashlineSnapshot | undefined;\n}\n\nfunction toPosixPath(filePath: string): string {\n\treturn filePath.split(sep).join(\"/\");\n}\n\nexport function hashlineDisplayPath(absolutePath: string, cwd: string): string {\n\tconst relativePath = relative(cwd, absolutePath);\n\tif (relativePath && !relativePath.startsWith(\"..\") && !isAbsolute(relativePath)) return toPosixPath(relativePath);\n\treturn toPosixPath(absolutePath);\n}\n\nexport function normalizeHashlineContent(content: string): string {\n\treturn content.replace(/^\\uFEFF/, \"\").replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n}\n\nexport function computeHashlineTag(content: string): string {\n\treturn computeFileHash(normalizeHashlineContent(content));\n}\n\nexport function createHashlineSnapshotStore(): HashlineSnapshotStore {\n\tconst snapshots = new InMemorySnapshotStore();\n\tconst headers = new Map<string, HashlineSnapshot>();\n\treturn {\n\t\tsnapshots,\n\t\trecord(absolutePath: string, cwd: string, content: string): HashlineSnapshot {\n\t\t\tconst normalizedPath = normalizePath(absolutePath);\n\t\t\tconst normalized = normalizeHashlineContent(content);\n\t\t\tconst displayPath = hashlineDisplayPath(normalizedPath, cwd);\n\t\t\tconst tag = snapshots.record(normalizedPath, normalized);\n\t\t\tconst snapshot = { absolutePath: normalizedPath, displayPath, tag, content: normalized };\n\t\t\theaders.set(`${displayPath}\\0${tag}`, snapshot);\n\t\t\treturn snapshot;\n\t\t},\n\t\tfindByHeader(displayPath: string, tag: string): HashlineSnapshot | undefined {\n\t\t\treturn headers.get(`${displayPath}\\0${tag.toUpperCase()}`);\n\t\t},\n\t};\n}\n\nexport function recordHashlineSnapshot(absolutePath: string, cwd: string, content: string, store: HashlineSnapshotStore): HashlineSnapshot {\n\treturn store.record(absolutePath, cwd, content);\n}\n\nexport function formatHashlineContent(snapshot: HashlineSnapshot, content = snapshot.content, startLine = 1): string {\n\treturn [formatHashlineHeader(snapshot.displayPath, snapshot.tag), formatNumberedLines(normalizeHashlineContent(content), startLine)].join(\"\\n\");\n}\n\nexport interface StrippedHashlineContent { content: string; stripped: boolean }\n\nexport function stripKnownHashlineCopiedContentWithMeta(content: string, _absolutePath: string, _cwd: string, store: HashlineSnapshotStore): StrippedHashlineContent {\n\tconst normalized = normalizeHashlineContent(content);\n\tconst lines = normalized.split(\"\\n\");\n\tconst headerIndex = lines.findIndex((line, index) => /^\\[[^\\]\\n]+#[0-9A-Fa-f]{4}\\]$/.test(line) && lines.slice(0, index).every((prefix) => prefix.trim() === \"\" || /^#\\s+.+\\/?$/.test(prefix)));\n\tif (headerIndex < 0) return { content, stripped: false };\n\tconst header = (lines[headerIndex] ?? \"\").match(/^\\[([^\\]\\n]+)#([0-9A-Fa-f]{4})\\]$/);\n\tif (!header) return { content, stripped: false };\n\tconst snapshot = store.findByHeader(header[1] ?? \"\", header[2] ?? \"\");\n\tif (!snapshot) return { content, stripped: false };\n\tconst body = lines.slice(headerIndex + 1);\n\tif (body.length === 0) return { content: snapshot.content, stripped: true };\n\tconst stripped: string[] = [];\n\tconst snapshotLines = snapshot.content.split(\"\\n\");\n\tlet sawRow = false;\n\t// Trailing tool chrome a model is likely to copy along with the hashline\n\t// body: the read/search continuation footers, the write tool's own\n\t// `Successfully wrote N bytes to <path>` confirmation (and its stripped-\n\t// note), and `Resolved …` conflict footers. These never carry a line\n\t// number, so they mark the end of the numbered body — they must not abort\n\t// stripping the way an arbitrary non-row line would.\n\tconst isToolFooter = (line: string): boolean =>\n\t\tline.trim() === \"\"\n\t\t|| /^\\[\\d+ more lines in file\\./.test(line)\n\t\t|| /^\\[Showing lines /.test(line)\n\t\t|| /^Successfully wrote \\d+ bytes to /.test(line)\n\t\t|| /^Resolved \\d+ conflicts?/.test(line)\n\t\t|| /^Resolved conflict \\d+/.test(line)\n\t\t|| /^Note: stripped copied hashline/.test(line)\n\t\t|| /^\\[[^\\]\\n]+#[0-9A-Fa-f]{4}\\]$/.test(line);\n\tlet onlyFooter = true;\n\tfor (const line of body) {\n\t\tif (isToolFooter(line)) {\n\t\t\t// A footer after the numbered body ends the body; a footer before any\n\t\t\t// row (leading blanks / a copied snapshot header) is just skipped.\n\t\t\tif (sawRow) break;\n\t\t\tcontinue;\n\t\t}\n\t\tonlyFooter = false;\n\t\tconst match = line.match(/^[* ]?(\\d+):(.*)$/s);\n\t\tif (!match) return { content, stripped: false };\n\t\tsawRow = true;\n\t\tconst lineNumber = Number.parseInt(match[1] ?? \"0\", 10);\n\t\tconst strippedLine = match[2] ?? \"\";\n\t\tif (snapshotLines[lineNumber - 1] !== strippedLine) return { content, stripped: false };\n\t\tstripped.push(strippedLine);\n\t}\n\tif (sawRow) return { content: stripped.join(\"\\n\"), stripped: true };\n\t// Header + only tool chrome (e.g. a copied write confirmation\n\t// `Successfully wrote N bytes to <path>`) names a known snapshot with no\n\t// numbered body to recover — resolve to the snapshot's stored content.\n\tif (onlyFooter) return { content: snapshot.content, stripped: true };\n\treturn { content, stripped: false };\n}\n\nexport function stripKnownHashlineCopiedContent(content: string, absolutePath: string, cwd: string, store: HashlineSnapshotStore): string {\n\treturn stripKnownHashlineCopiedContentWithMeta(content, absolutePath, cwd, store).content;\n}\n\nexport function formatCompactHashlineEditResult(snapshot: HashlineSnapshot, diff: { diff?: string; firstChangedLine?: number }, messages: readonly string[] = []): string {\n\treturn [formatHashlineHeader(snapshot.displayPath, snapshot.tag), ...messages, diff.diff?.trim() || `First changed line: ${diff.firstChangedLine ?? 1}`].join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"hashline.d.ts","sourceRoot":"","sources":["../../../src/core/tools/hashline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqF,KAAK,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAGnJ,MAAM,WAAW,gBAAgB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC7E,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAAC;CAC7E;AAMD,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAI7E;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,2BAA2B,IAAI,qBAAqB,CAkBnE;AAED,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,gBAAgB,CAEzI;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,SAAmB,EAAE,SAAS,SAAI,GAAG,MAAM,CAEnH;AAED,MAAM,WAAW,uBAAuB;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE;AAE/E,wBAAgB,uCAAuC,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,uBAAuB,CAoDnK;AAED,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAExI;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,QAAQ,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,CAExK"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/tools/index.ts"],"names":[],"mappings":"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","sourcesContent":["export {\n\ttype BashOperations,\n\ttype BashSpawnContext,\n\ttype BashSpawnHook,\n\ttype BashToolDetails,\n\ttype BashToolInput,\n\ttype BashToolOptions,\n\tcreateBashTool,\n\tcreateBashToolDefinition,\n\tcreateLocalBashOperations,\n} from \"./bash.ts\";\nexport {\n\tcreateEditTool,\n\tcreateEditToolDefinition,\n\ttype EditOperations,\n\ttype EditToolDetails,\n\ttype EditToolInput,\n\ttype EditToolOptions,\n} from \"./edit.ts\";\nexport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nexport {\n\tcreateFindTool,\n\tcreateFindToolDefinition,\n\ttype FindOperations,\n\ttype FindToolDetails,\n\ttype FindToolInput,\n\ttype FindToolOptions,\n} from \"./find.ts\";\nexport {\n\tcreateSearchTool,\n\tcreateSearchToolDefinition,\n\ttype SearchToolDetails,\n\ttype SearchToolInput,\n\ttype SearchToolOptions,\n} from \"./search.ts\";\nexport {\n\tcreateLsTool,\n\tcreateLsToolDefinition,\n\ttype LsOperations,\n\ttype LsToolDetails,\n\ttype LsToolInput,\n\ttype LsToolOptions,\n} from \"./ls.ts\";\nexport {\n\tcreateReadTool,\n\tcreateReadToolDefinition,\n\ttype ReadOperations,\n\ttype ReadToolDetails,\n\ttype ReadToolInput,\n\ttype ReadToolOptions,\n} from \"./read.ts\";\nexport {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\tformatSize,\n\ttype TruncationOptions,\n\ttype TruncationResult,\n\ttruncateHead,\n\ttruncateLine,\n\ttruncateTail,\n} from \"./truncate.ts\";\nexport {\n\tcreateWriteTool,\n\tcreateWriteToolDefinition,\n\ttype WriteOperations,\n\ttype WriteToolInput,\n\ttype WriteToolOptions,\n} from \"./write.ts\";\nexport { createAskUserQuestionToolDefinition } from \"./ask-user-question/index.ts\";\nexport {\n\tSTRUCTURED_OUTPUT_TOOL_NAME,\n\tcreateStructuredOutputCapture,\n\tcreateStructuredOutputTool,\n\ttype JsonObject,\n\ttype JsonPrimitive,\n\ttype JsonValue,\n\ttype StructuredOutputCapture,\n\ttype StructuredOutputFileCapture,\n\ttype StructuredOutputToolOptions,\n} from \"./structured-output.ts\";\nexport { createTodoToolDefinition } from \"./todos.ts\";\n\nimport type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { TSchema } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { createAskUserQuestionToolDefinition } from \"./ask-user-question/index.ts\";\nimport { createHashlineSnapshotStore, type HashlineSnapshotStore } from \"./hashline.ts\";\nimport { type BashToolOptions, createBashTool, createBashToolDefinition } from \"./bash.ts\";\nimport { createEditTool, createEditToolDefinition, type EditToolOptions } from \"./edit.ts\";\nimport { createFindTool, createFindToolDefinition, type FindToolOptions } from \"./find.ts\";\nimport { createLsTool, createLsToolDefinition, type LsToolOptions } from \"./ls.ts\";\nimport { createSearchTool, createSearchToolDefinition, type SearchToolOptions } from \"./search.ts\";\nimport { createReadTool, createReadToolDefinition, type ReadToolOptions } from \"./read.ts\";\nimport { createTodoToolDefinition } from \"./todos.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { createWriteTool, createWriteToolDefinition, type WriteToolOptions } from \"./write.ts\";\n\nexport type Tool = AgentTool<TSchema, unknown>;\nexport type ToolDef = ToolDefinition<TSchema, unknown>;\nexport type ToolName =\n\t| \"read\"\n\t| \"bash\"\n\t| \"edit\"\n\t| \"write\"\n\t| \"find\"\n\t| \"search\"\n\t| \"ls\"\n\t| \"ask_user_question\"\n\t| \"todo\";\nexport const allToolNames: Set<ToolName> = new Set([\n\t\"read\",\n\t\"bash\",\n\t\"edit\",\n\t\"write\",\n\t\"find\",\n\t\"search\",\n\t\"ls\",\n\t\"ask_user_question\",\n\t\"todo\",\n]);\n\nexport const defaultToolNames: readonly ToolName[] = [\n\t\"read\",\n\t\"bash\",\n\t\"edit\",\n\t\"write\",\n\t\"find\",\n\t\"search\",\n\t\"ask_user_question\",\n\t\"todo\",\n];\n\nexport interface ToolsOptions {\n\tread?: ReadToolOptions;\n\tbash?: BashToolOptions;\n\twrite?: WriteToolOptions;\n\tedit?: EditToolOptions;\n\tfind?: FindToolOptions;\n\tsearch?: SearchToolOptions;\n\tls?: LsToolOptions;\n\thashlineStore?: HashlineSnapshotStore;\n}\n\nexport function createToolDefinition(toolName: ToolName, cwd: string, options?: ToolsOptions): ToolDef {\n\t// Default a shared store so the singular factories don't hand read/edit/\n\t// write/search isolated stores (which silently degrades drift recovery).\n\tconst hashlineStore = options?.hashlineStore ?? createHashlineSnapshotStore();\n\tswitch (toolName) {\n\t\tcase \"read\":\n\t\t\treturn createReadToolDefinition(cwd, { ...options?.read, hashlineStore });\n\t\tcase \"bash\":\n\t\t\treturn createBashToolDefinition(cwd, options?.bash);\n\t\tcase \"edit\":\n\t\t\treturn createEditToolDefinition(cwd, { ...options?.edit, hashlineStore });\n\t\tcase \"write\":\n\t\t\treturn createWriteToolDefinition(cwd, { ...options?.write, hashlineStore });\n\t\tcase \"find\":\n\t\t\treturn createFindToolDefinition(cwd, options?.find);\n\t\tcase \"search\":\n\t\t\treturn createSearchToolDefinition(cwd, { ...options?.search, hashlineStore });\n\t\tcase \"ls\":\n\t\t\treturn createLsToolDefinition(cwd, options?.ls);\n\t\tcase \"ask_user_question\":\n\t\t\treturn createAskUserQuestionToolDefinition();\n\t\tcase \"todo\":\n\t\t\treturn createTodoToolDefinition(cwd);\n\t\tdefault:\n\t\t\tthrow new Error(`Unknown tool name: ${toolName}`);\n\t}\n}\n\nexport function createTool(toolName: ToolName, cwd: string, options?: ToolsOptions): Tool {\n\tconst hashlineStore = options?.hashlineStore ?? createHashlineSnapshotStore();\n\tswitch (toolName) {\n\t\tcase \"read\":\n\t\t\treturn createReadTool(cwd, { ...options?.read, hashlineStore });\n\t\tcase \"bash\":\n\t\t\treturn createBashTool(cwd, options?.bash);\n\t\tcase \"edit\":\n\t\t\treturn createEditTool(cwd, { ...options?.edit, hashlineStore });\n\t\tcase \"write\":\n\t\t\treturn createWriteTool(cwd, { ...options?.write, hashlineStore });\n\t\tcase \"find\":\n\t\t\treturn createFindTool(cwd, options?.find);\n\t\tcase \"search\":\n\t\t\treturn createSearchTool(cwd, { ...options?.search, hashlineStore });\n\t\tcase \"ls\":\n\t\t\treturn createLsTool(cwd, options?.ls);\n\t\tcase \"ask_user_question\":\n\t\t\treturn wrapToolDefinition(createAskUserQuestionToolDefinition());\n\t\tcase \"todo\":\n\t\t\treturn wrapToolDefinition(createTodoToolDefinition(cwd));\n\t\tdefault:\n\t\t\tthrow new Error(`Unknown tool name: ${toolName}`);\n\t}\n}\n\nexport function createCodingToolDefinitions(cwd: string, options?: ToolsOptions): ToolDef[] {\n\tconst hashlineStore = options?.hashlineStore ?? createHashlineSnapshotStore();\n\treturn [\n\t\tcreateReadToolDefinition(cwd, { ...options?.read, hashlineStore }),\n\t\tcreateBashToolDefinition(cwd, { asyncEnabled: true, ...options?.bash }),\n\t\tcreateEditToolDefinition(cwd, { ...options?.edit, hashlineStore }),\n\t\tcreateWriteToolDefinition(cwd, { ...options?.write, hashlineStore }),\n\t\tcreateFindToolDefinition(cwd, options?.find),\n\t\tcreateSearchToolDefinition(cwd, { ...options?.search, hashlineStore }),\n\t];\n}\n\nexport function createReadOnlyToolDefinitions(cwd: string, options?: ToolsOptions): ToolDef[] {\n\tconst hashlineStore = options?.hashlineStore ?? createHashlineSnapshotStore();\n\treturn [\n\t\tcreateReadToolDefinition(cwd, { ...options?.read, hashlineStore }),\n\t\tcreateFindToolDefinition(cwd, options?.find),\n\t\tcreateSearchToolDefinition(cwd, { ...options?.search, hashlineStore }),\n\t\tcreateLsToolDefinition(cwd, options?.ls),\n\t];\n}\n\nexport function createAllToolDefinitions(cwd: string, options?: ToolsOptions): Record<ToolName, ToolDef> {\n\tconst hashlineStore = options?.hashlineStore ?? createHashlineSnapshotStore();\n\treturn {\n\t\tread: createReadToolDefinition(cwd, { ...options?.read, hashlineStore }),\n\t\tbash: createBashToolDefinition(cwd, { asyncEnabled: true, ...options?.bash }),\n\t\tedit: createEditToolDefinition(cwd, { ...options?.edit, hashlineStore }),\n\t\twrite: createWriteToolDefinition(cwd, { ...options?.write, hashlineStore }),\n\t\tfind: createFindToolDefinition(cwd, options?.find),\n\t\tsearch: createSearchToolDefinition(cwd, { ...options?.search, hashlineStore }),\n\t\tls: createLsToolDefinition(cwd, options?.ls),\n\t\task_user_question: createAskUserQuestionToolDefinition(),\n\t\ttodo: createTodoToolDefinition(cwd),\n\t};\n}\n\nexport function createCodingTools(cwd: string, options?: ToolsOptions): Tool[] {\n\tconst hashlineStore = options?.hashlineStore ?? createHashlineSnapshotStore();\n\treturn [\n\t\tcreateReadTool(cwd, { ...options?.read, hashlineStore }),\n\t\tcreateBashTool(cwd, { asyncEnabled: true, ...options?.bash }),\n\t\tcreateEditTool(cwd, { ...options?.edit, hashlineStore }),\n\t\tcreateWriteTool(cwd, { ...options?.write, hashlineStore }),\n\t\tcreateFindTool(cwd, options?.find),\n\t\tcreateSearchTool(cwd, { ...options?.search, hashlineStore }),\n\t];\n}\n\nexport function createReadOnlyTools(cwd: string, options?: ToolsOptions): Tool[] {\n\tconst hashlineStore = options?.hashlineStore ?? createHashlineSnapshotStore();\n\treturn [\n\t\tcreateReadTool(cwd, { ...options?.read, hashlineStore }),\n\t\tcreateFindTool(cwd, options?.find),\n\t\tcreateSearchTool(cwd, { ...options?.search, hashlineStore }),\n\t\tcreateLsTool(cwd, options?.ls),\n\t];\n}\n\nexport function createAllTools(cwd: string, options?: ToolsOptions): Record<ToolName, Tool> {\n\tconst hashlineStore = options?.hashlineStore ?? createHashlineSnapshotStore();\n\treturn {\n\t\tread: createReadTool(cwd, { ...options?.read, hashlineStore }),\n\t\tbash: createBashTool(cwd, { asyncEnabled: true, ...options?.bash }),\n\t\tedit: createEditTool(cwd, { ...options?.edit, hashlineStore }),\n\t\twrite: createWriteTool(cwd, { ...options?.write, hashlineStore }),\n\t\tfind: createFindTool(cwd, options?.find),\n\t\tsearch: createSearchTool(cwd, { ...options?.search, hashlineStore }),\n\t\tls: createLsTool(cwd, options?.ls),\n\t\task_user_question: wrapToolDefinition(createAskUserQuestionToolDefinition()),\n\t\ttodo: wrapToolDefinition(createTodoToolDefinition(cwd)),\n\t};\n}\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/tools/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ls.d.ts","sourceRoot":"","sources":["../../../src/core/tools/ls.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAG/D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AAItF,OAAO,EAAiC,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAEnG,QAAA,MAAM,QAAQ;;;EAGZ,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,QAAQ,CAAC,CAAC;AAIlD,MAAM,WAAW,aAAa;IAC7B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC5B,2BAA2B;IAC3B,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC7D,wDAAwD;IACxD,IAAI,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,OAAO,CAAA;KAAE,CAAC,GAAG;QAAE,WAAW,EAAE,MAAM,OAAO,CAAA;KAAE,CAAC;IACzG,6BAA6B;IAC7B,OAAO,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC;CAChE;AAQD,MAAM,WAAW,aAAa;IAC7B,yEAAyE;IACzE,UAAU,CAAC,EAAE,YAAY,CAAC;CAC1B;AAkDD,wBAAgB,sBAAsB,CACrC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,aAAa,GACrB,cAAc,CAAC,OAAO,QAAQ,EAAE,aAAa,GAAG,SAAS,CAAC,CA4H5D;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC,OAAO,QAAQ,CAAC,CAE7F","sourcesContent":["import { readdir as fsReaddir, stat as fsStat } from \"node:fs/promises\";\nimport type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Text } from \"@earendil-works/pi-tui\";\nimport nodePath from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { pathExists, resolveToCwd } from \"./path-utils.ts\";\nimport { getTextOutput, invalidArgText, shortenPath, str } from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from \"./truncate.ts\";\n\nconst lsSchema = Type.Object({\n\tpath: Type.Optional(Type.String({ description: \"Directory to list (default: current directory)\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of entries to return (default: 500)\" })),\n});\n\nexport type LsToolInput = Static<typeof lsSchema>;\n\nconst DEFAULT_LIMIT = 500;\n\nexport interface LsToolDetails {\n\ttruncation?: TruncationResult;\n\tentryLimitReached?: number;\n}\n\n/**\n * Pluggable operations for the ls tool.\n * Override these to delegate directory listing to remote systems (for example SSH).\n */\nexport interface LsOperations {\n\t/** Check if path exists */\n\texists: (absolutePath: string) => Promise<boolean> | boolean;\n\t/** Get file or directory stats. Throws if not found. */\n\tstat: (absolutePath: string) => Promise<{ isDirectory: () => boolean }> | { isDirectory: () => boolean };\n\t/** Read directory entries */\n\treaddir: (absolutePath: string) => Promise<string[]> | string[];\n}\n\nconst defaultLsOperations: LsOperations = {\n\texists: pathExists,\n\tstat: fsStat,\n\treaddir: fsReaddir,\n};\n\nexport interface LsToolOptions {\n\t/** Custom operations for directory listing. Default: local filesystem */\n\toperations?: LsOperations;\n}\n\nfunction formatLsCall(\n\targs: { path?: string; limit?: number } | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.ts\").theme,\n): string {\n\tconst rawPath = str(args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath || \".\") : null;\n\tconst limit = args?.limit;\n\tconst invalidArg = invalidArgText(theme);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"ls\"))} ${path === null ? invalidArg : theme.fg(\"accent\", path)}`;\n\tif (limit !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` (limit ${limit})`);\n\t}\n\treturn text;\n}\n\nfunction formatLsResult(\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: LsToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.ts\").theme,\n\tshowImages: boolean,\n): string {\n\tconst output = getTextOutput(result, showImages).trim();\n\tlet text = \"\";\n\tif (output) {\n\t\tconst lines = output.split(\"\\n\");\n\t\tconst maxLines = options.expanded ? lines.length : 20;\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - maxLines;\n\t\ttext += `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines,`)} ${keyHint(\"app.tools.expand\", \"Expand\")}${theme.fg(\"muted\", \")\")}`;\n\t\t}\n\t}\n\n\tconst entryLimit = result.details?.entryLimitReached;\n\tconst truncation = result.details?.truncation;\n\tif (entryLimit || truncation?.truncated) {\n\t\tconst warnings: string[] = [];\n\t\tif (entryLimit) warnings.push(`${entryLimit} entries limit`);\n\t\tif (truncation?.truncated) warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);\n\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: ${warnings.join(\", \")}]`)}`;\n\t}\n\treturn text;\n}\n\nexport function createLsToolDefinition(\n\tcwd: string,\n\toptions?: LsToolOptions,\n): ToolDefinition<typeof lsSchema, LsToolDetails | undefined> {\n\tconst ops = options?.operations ?? defaultLsOperations;\n\treturn {\n\t\tname: \"ls\",\n\t\tlabel: \"ls\",\n\t\tdescription: `List directory contents. Returns entries sorted alphabetically, with '/' suffix for directories. Includes dotfiles. Output is truncated to ${DEFAULT_LIMIT} entries or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first).`,\n\t\tpromptSnippet: \"List directory contents\",\n\t\tparameters: lsSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ path, limit }: { path?: string; limit?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst onAbort = () => reject(new Error(\"Operation aborted\"));\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\t\t(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst dirPath = resolveToCwd(path || \".\", cwd);\n\t\t\t\t\t\tconst effectiveLimit = limit ?? DEFAULT_LIMIT;\n\n\t\t\t\t\t\t// Check if path exists.\n\t\t\t\t\t\tif (!(await ops.exists(dirPath))) {\n\t\t\t\t\t\t\treject(new Error(`Path not found: ${dirPath}`));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Check if path is a directory.\n\t\t\t\t\t\tconst stat = await ops.stat(dirPath);\n\t\t\t\t\t\tif (!stat.isDirectory()) {\n\t\t\t\t\t\t\treject(new Error(`Not a directory: ${dirPath}`));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Read directory entries.\n\t\t\t\t\t\tlet entries: string[];\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tentries = await ops.readdir(dirPath);\n\t\t\t\t\t\t} catch (e: unknown) {\n\t\t\t\t\t\t\tconst message = e instanceof Error ? e.message : String(e);\n\t\t\t\t\t\t\treject(new Error(`Cannot read directory: ${message}`));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Sort alphabetically, case-insensitive.\n\t\t\t\t\t\tentries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));\n\n\t\t\t\t\t\t// Format entries with directory indicators.\n\t\t\t\t\t\tconst results: string[] = [];\n\t\t\t\t\t\tlet entryLimitReached = false;\n\t\t\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\t\t\tif (results.length >= effectiveLimit) {\n\t\t\t\t\t\t\t\tentryLimitReached = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst fullPath = nodePath.join(dirPath, entry);\n\t\t\t\t\t\t\tlet suffix = \"\";\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst entryStat = await ops.stat(fullPath);\n\t\t\t\t\t\t\t\tif (entryStat.isDirectory()) suffix = \"/\";\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// Skip entries we cannot stat.\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresults.push(entry + suffix);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\n\t\t\t\t\t\tif (results.length === 0) {\n\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: \"(empty directory)\" }], details: undefined });\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst rawOutput = results.join(\"\\n\");\n\t\t\t\t\t\t// Apply byte truncation. There is no separate line limit because entry count is already capped.\n\t\t\t\t\t\tconst truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });\n\t\t\t\t\t\tlet output = truncation.content;\n\t\t\t\t\t\tconst details: LsToolDetails = {};\n\t\t\t\t\t\t// Build actionable notices for truncation and entry limits.\n\t\t\t\t\t\tconst notices: string[] = [];\n\t\t\t\t\t\tif (entryLimitReached) {\n\t\t\t\t\t\t\tnotices.push(`${effectiveLimit} entries limit reached. Use limit=${effectiveLimit * 2} for more`);\n\t\t\t\t\t\t\tdetails.entryLimitReached = effectiveLimit;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\t\t\tnotices.push(`${formatSize(DEFAULT_MAX_BYTES)} limit reached`);\n\t\t\t\t\t\t\tdetails.truncation = truncation;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (notices.length > 0) {\n\t\t\t\t\t\t\toutput += `\\n\\n[${notices.join(\". \")}]`;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: output }],\n\t\t\t\t\t\t\tdetails: Object.keys(details).length > 0 ? details : undefined,\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (e: unknown) {\n\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\treject(e);\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatLsCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatLsResult(result, options, theme, context.showImages));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createLsTool(cwd: string, options?: LsToolOptions): AgentTool<typeof lsSchema> {\n\treturn wrapToolDefinition(createLsToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"ls.d.ts","sourceRoot":"","sources":["../../../src/core/tools/ls.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAG/D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AAItF,OAAO,EAAiC,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAEnG,QAAA,MAAM,QAAQ;;;EAGZ,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,QAAQ,CAAC,CAAC;AAIlD,MAAM,WAAW,aAAa;IAC7B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC5B,2BAA2B;IAC3B,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC7D,wDAAwD;IACxD,IAAI,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,OAAO,CAAA;KAAE,CAAC,GAAG;QAAE,WAAW,EAAE,MAAM,OAAO,CAAA;KAAE,CAAC;IACzG,6BAA6B;IAC7B,OAAO,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC;CAChE;AAQD,MAAM,WAAW,aAAa;IAC7B,yEAAyE;IACzE,UAAU,CAAC,EAAE,YAAY,CAAC;CAC1B;AAkDD,wBAAgB,sBAAsB,CACrC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,aAAa,GACrB,cAAc,CAAC,OAAO,QAAQ,EAAE,aAAa,GAAG,SAAS,CAAC,CA4H5D;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC,OAAO,QAAQ,CAAC,CAE7F"}
@@ -1 +1 @@
1
- {"version":3,"file":"notebook.d.ts","sourceRoot":"","sources":["../../../src/core/tools/notebook.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,GAAG,KAAK,CAAC;AAE3D,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAMD,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAE5D;AAYD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,GAAG,MAAM,CAIzE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAE7D;AAED,wBAAgB,aAAa,IAAI,gBAAgB,CAEhD;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAIxF;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAQzE;AAkCD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAyBzH;AAED,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAG1F;AAED,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI3G","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\n\n/**\n * Editable notebook projection, mirrored from oh-my-pi's\n * `packages/coding-agent/src/edit/notebook.ts` at 15b5c1397fc.\n *\n * A notebook is projected to an editable text form using cell markers:\n *\n * # %% [code|markdown|raw] cell:<index>\n *\n * Source lines that themselves look like a cell marker are escaped by adding\n * one extra `%`. Edits applied to the projected text are mapped back to the\n * underlying notebook JSON, preserving cell metadata/outputs/execution counts.\n */\n\nexport type NotebookCellType = \"code\" | \"markdown\" | \"raw\";\n\nexport interface NotebookCell {\n\tcell_type: NotebookCellType;\n\tsource?: string | string[];\n\tmetadata?: Record<string, unknown>;\n\texecution_count?: number | null;\n\toutputs?: unknown[];\n\t[key: string]: unknown;\n}\n\nexport interface NotebookDocument {\n\tcells: NotebookCell[];\n\tmetadata: Record<string, unknown>;\n\tnbformat: number;\n\tnbformat_minor: number;\n\t[key: string]: unknown;\n}\n\nconst CELL_MARKER_RE = /^# %% \\[(code|markdown|raw)\\](?: cell:(\\d+))?$/;\nconst ESCAPABLE_MARKER_RE = /^# %%+ \\[(?:code|markdown|raw)\\](?: cell:\\d+)?$/;\nconst ESCAPED_MARKER_RE = /^# %%%+ \\[(?:code|markdown|raw)\\](?: cell:\\d+)?$/;\n\nexport function isNotebookPath(absolutePath: string): boolean {\n\treturn /\\.ipynb$/i.test(absolutePath);\n}\n\nfunction escapeMarkerLikeSourceLine(line: string): string {\n\tif (ESCAPABLE_MARKER_RE.test(line)) return `${line.slice(0, 2)}%${line.slice(2)}`;\n\treturn line;\n}\n\nfunction unescapeMarkerLikeSourceLine(line: string): string {\n\tif (ESCAPED_MARKER_RE.test(line)) return `${line.slice(0, 2)}${line.slice(3)}`;\n\treturn line;\n}\n\nexport function cellSourceToString(source: NotebookCell[\"source\"]): string {\n\tif (source === undefined) return \"\";\n\tif (typeof source === \"string\") return source;\n\treturn source.join(\"\");\n}\n\nexport function splitNotebookSource(content: string): string[] {\n\treturn content.match(/[^\\n]*\\n|[^\\n]+$/g) ?? [];\n}\n\nexport function emptyNotebook(): NotebookDocument {\n\treturn { cells: [], metadata: {}, nbformat: 4, nbformat_minor: 5 };\n}\n\nexport function newNotebookCell(cellType: NotebookCellType, source: string): NotebookCell {\n\tconst cell: NotebookCell = { cell_type: cellType, metadata: {}, source: splitNotebookSource(source) };\n\tif (cellType === \"code\") { cell.execution_count = null; cell.outputs = []; }\n\treturn cell;\n}\n\nexport function notebookToEditableText(notebook: NotebookDocument): string {\n\tconst lines: string[] = [];\n\tfor (const [index, cell] of notebook.cells.entries()) {\n\t\tconst source = cellSourceToString(cell.source);\n\t\tlines.push(`# %% [${cell.cell_type}] cell:${index}`);\n\t\tif (source !== \"\") for (const line of source.split(\"\\n\")) lines.push(escapeMarkerLikeSourceLine(line));\n\t}\n\treturn lines.join(\"\\n\");\n}\n\ninterface ParsedVirtualCell {\n\tcellType: NotebookCellType;\n\tcellIndex?: number;\n\tsource: string;\n}\n\nfunction parseNotebookEditableText(text: string, displayPath: string): ParsedVirtualCell[] {\n\tconst lines = text.split(\"\\n\");\n\tconst cells: ParsedVirtualCell[] = [];\n\tlet current: ParsedVirtualCell | null = null;\n\tlet sawMarker = false;\n\tfor (const line of lines) {\n\t\tconst match = line.match(CELL_MARKER_RE);\n\t\tif (match) {\n\t\t\tsawMarker = true;\n\t\t\tconst cellType = (match[1] ?? \"raw\") as NotebookCellType;\n\t\t\tconst cellIndex = match[2] !== undefined ? Number.parseInt(match[2], 10) : undefined;\n\t\t\tcurrent = { cellType, cellIndex, source: \"\" };\n\t\t\tcells.push(current);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!sawMarker) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid notebook editable representation for ${displayPath}: expected first line to be \"# %% [code] cell:0\", \"# %% [markdown] cell:0\", or \"# %% [raw] cell:0\".`,\n\t\t\t);\n\t\t}\n\t\tconst restored = unescapeMarkerLikeSourceLine(line);\n\t\tcurrent!.source = current!.source === \"\" ? restored : `${current!.source}\\n${restored}`;\n\t}\n\treturn cells;\n}\n\nexport function applyNotebookEditableText(notebook: NotebookDocument, text: string, displayPath: string): NotebookDocument {\n\tconst parsed = parseNotebookEditableText(text, displayPath);\n\tconst next = structuredClone(notebook) as NotebookDocument;\n\tconst used = new Set<number>();\n\tnext.cells = parsed.map((parsedCell) => {\n\t\tlet cell: NotebookCell;\n\t\tconst cellIndex = parsedCell.cellIndex;\n\t\tif (cellIndex !== undefined && cellIndex >= 0 && cellIndex < notebook.cells.length && !used.has(cellIndex)) {\n\t\t\tused.add(cellIndex);\n\t\t\tcell = structuredClone(notebook.cells[cellIndex]!) as NotebookCell;\n\t\t} else {\n\t\t\tcell = newNotebookCell(parsedCell.cellType, \"\");\n\t\t}\n\t\tcell.cell_type = parsedCell.cellType;\n\t\tcell.source = splitNotebookSource(parsedCell.source);\n\t\tif (parsedCell.cellType === \"code\") {\n\t\t\tif (cell.execution_count === undefined) cell.execution_count = null;\n\t\t\tif (cell.outputs === undefined) cell.outputs = [];\n\t\t} else {\n\t\t\tdelete cell.execution_count;\n\t\t\tdelete cell.outputs;\n\t\t}\n\t\treturn cell;\n\t});\n\treturn next;\n}\n\nexport function readEditableNotebookText(absolutePath: string, displayPath: string): string {\n\tconst notebook = existsSync(absolutePath) ? parseNotebookSafe(readFileSync(absolutePath, \"utf8\"), displayPath) : emptyNotebook();\n\treturn notebookToEditableText(notebook);\n}\n\nexport function serializeEditedNotebookText(absolutePath: string, displayPath: string, text: string): string {\n\tconst notebook = existsSync(absolutePath) ? parseNotebookSafe(readFileSync(absolutePath, \"utf8\"), displayPath) : emptyNotebook();\n\tconst next = applyNotebookEditableText(notebook, text, displayPath);\n\treturn JSON.stringify(next, null, 1);\n}\n\nfunction parseNotebookSafe(raw: string, displayPath: string): NotebookDocument {\n\tlet parsed: unknown;\n\ttry { parsed = JSON.parse(raw); } catch { throw new Error(`Invalid notebook JSON for ${displayPath}`); }\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) throw new Error(`Invalid notebook JSON for ${displayPath}`);\n\tconst doc = parsed as Partial<NotebookDocument>;\n\treturn { ...doc, cells: Array.isArray(doc.cells) ? doc.cells as NotebookCell[] : [], metadata: (doc.metadata ?? {}) as Record<string, unknown>, nbformat: doc.nbformat ?? 4, nbformat_minor: doc.nbformat_minor ?? 5 };\n}\n"]}
1
+ {"version":3,"file":"notebook.d.ts","sourceRoot":"","sources":["../../../src/core/tools/notebook.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,GAAG,KAAK,CAAC;AAE3D,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAMD,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAE5D;AAYD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,GAAG,MAAM,CAIzE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAE7D;AAED,wBAAgB,aAAa,IAAI,gBAAgB,CAEhD;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAIxF;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAQzE;AAkCD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAyBzH;AAED,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAG1F;AAED,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI3G"}
@@ -1 +1 @@
1
- {"version":3,"file":"output-accumulator.d.ts","sourceRoot":"","sources":["../../../src/core/tools/output-accumulator.ts"],"names":[],"mappings":"AAKA,OAAO,EAAwC,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAE1G,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAWD;;;;;;GAMG;AACH,qBAAa,iBAAiB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,wBAAwB,CAAQ;IACxC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAS;IAEzB,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,cAAc,CAA0B;IAEhD,YAAY,OAAO,GAAE,wBAA6B,EAKjD;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAczB;IAED,MAAM,IAAI,IAAI,CASb;IAED,QAAQ,CAAC,OAAO,GAAE;QAAE,kBAAkB,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,cAAc,CA4BvE;IAEK,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAqBnC;IAED,gBAAgB,IAAI,MAAM,CAEzB;IAED,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,QAAQ;IAiBhB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,cAAc;CAWtB","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { APP_NAME } from \"../../config.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, type TruncationResult, truncateTail } from \"./truncate.ts\";\n\nexport interface OutputAccumulatorOptions {\n\tmaxLines?: number;\n\tmaxBytes?: number;\n\ttempFilePrefix?: string;\n}\n\nexport interface OutputSnapshot {\n\tcontent: string;\n\ttruncation: TruncationResult;\n\tfullOutputPath?: string;\n}\n\nfunction defaultTempFilePath(prefix: string): string {\n\tconst id = randomBytes(8).toString(\"hex\");\n\treturn join(tmpdir(), `${prefix}-${id}.log`);\n}\n\nfunction byteLength(text: string): number {\n\treturn Buffer.byteLength(text, \"utf-8\");\n}\n\n/**\n * Incrementally tracks streaming output with bounded memory.\n *\n * Appends decode chunks with a streaming UTF-8 decoder, keeps only a decoded\n * tail for display snapshots, and opens a temp file when the full output needs\n * to be preserved.\n */\nexport class OutputAccumulator {\n\tprivate readonly maxLines: number;\n\tprivate readonly maxBytes: number;\n\tprivate readonly maxRollingBytes: number;\n\tprivate readonly tempFilePrefix: string;\n\tprivate readonly decoder = new TextDecoder();\n\n\tprivate rawChunks: Buffer[] = [];\n\tprivate tailText = \"\";\n\tprivate tailBytes = 0;\n\tprivate tailStartsAtLineBoundary = true;\n\tprivate totalRawBytes = 0;\n\tprivate totalDecodedBytes = 0;\n\tprivate completedLines = 0;\n\tprivate totalLines = 0;\n\tprivate currentLineBytes = 0;\n\tprivate hasOpenLine = false;\n\tprivate finished = false;\n\n\tprivate tempFilePath: string | undefined;\n\tprivate tempFileStream: WriteStream | undefined;\n\n\tconstructor(options: OutputAccumulatorOptions = {}) {\n\t\tthis.maxLines = options.maxLines ?? DEFAULT_MAX_LINES;\n\t\tthis.maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n\t\tthis.maxRollingBytes = Math.max(this.maxBytes * 2, 1);\n\t\tthis.tempFilePrefix = options.tempFilePrefix ?? `${APP_NAME}-output`;\n\t}\n\n\tappend(data: Buffer): void {\n\t\tif (this.finished) {\n\t\t\tthrow new Error(\"Cannot append to a finished output accumulator\");\n\t\t}\n\n\t\tthis.totalRawBytes += data.length;\n\t\tthis.appendDecodedText(this.decoder.decode(data, { stream: true }));\n\n\t\tif (this.tempFileStream || this.shouldUseTempFile()) {\n\t\t\tthis.ensureTempFile();\n\t\t\tthis.tempFileStream?.write(data);\n\t\t} else if (data.length > 0) {\n\t\t\tthis.rawChunks.push(data);\n\t\t}\n\t}\n\n\tfinish(): void {\n\t\tif (this.finished) {\n\t\t\treturn;\n\t\t}\n\t\tthis.finished = true;\n\t\tthis.appendDecodedText(this.decoder.decode());\n\t\tif (this.shouldUseTempFile()) {\n\t\t\tthis.ensureTempFile();\n\t\t}\n\t}\n\n\tsnapshot(options: { persistIfTruncated?: boolean } = {}): OutputSnapshot {\n\t\tconst tailTruncation = truncateTail(this.getSnapshotText(), {\n\t\t\tmaxLines: this.maxLines,\n\t\t\tmaxBytes: this.maxBytes,\n\t\t});\n\t\tconst truncated = this.totalLines > this.maxLines || this.totalDecodedBytes > this.maxBytes;\n\t\tconst truncatedBy = truncated\n\t\t\t? (tailTruncation.truncatedBy ?? (this.totalDecodedBytes > this.maxBytes ? \"bytes\" : \"lines\"))\n\t\t\t: null;\n\t\tconst truncation: TruncationResult = {\n\t\t\t...tailTruncation,\n\t\t\ttruncated,\n\t\t\ttruncatedBy,\n\t\t\ttotalLines: this.totalLines,\n\t\t\ttotalBytes: this.totalDecodedBytes,\n\t\t\tmaxLines: this.maxLines,\n\t\t\tmaxBytes: this.maxBytes,\n\t\t};\n\n\t\tif (options.persistIfTruncated && truncation.truncated) {\n\t\t\tthis.ensureTempFile();\n\t\t}\n\n\t\treturn {\n\t\t\tcontent: truncation.content,\n\t\t\ttruncation,\n\t\t\tfullOutputPath: this.tempFilePath,\n\t\t};\n\t}\n\n\tasync closeTempFile(): Promise<void> {\n\t\tif (!this.tempFileStream) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst stream = this.tempFileStream;\n\t\tthis.tempFileStream = undefined;\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst onError = (error: Error) => {\n\t\t\t\tstream.off(\"finish\", onFinish);\n\t\t\t\treject(error);\n\t\t\t};\n\t\t\tconst onFinish = () => {\n\t\t\t\tstream.off(\"error\", onError);\n\t\t\t\tresolve();\n\t\t\t};\n\t\t\tstream.once(\"error\", onError);\n\t\t\tstream.once(\"finish\", onFinish);\n\t\t\tstream.end();\n\t\t});\n\t}\n\n\tgetLastLineBytes(): number {\n\t\treturn this.currentLineBytes;\n\t}\n\n\tprivate appendDecodedText(text: string): void {\n\t\tif (text.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst bytes = byteLength(text);\n\t\tthis.totalDecodedBytes += bytes;\n\t\tthis.tailText += text;\n\t\tthis.tailBytes += bytes;\n\t\tif (this.tailBytes > this.maxRollingBytes * 2) {\n\t\t\tthis.trimTail();\n\t\t}\n\n\t\tlet newlines = 0;\n\t\tlet lastNewline = -1;\n\t\tfor (let i = text.indexOf(\"\\n\"); i !== -1; i = text.indexOf(\"\\n\", i + 1)) {\n\t\t\tnewlines++;\n\t\t\tlastNewline = i;\n\t\t}\n\t\tif (newlines === 0) {\n\t\t\tthis.currentLineBytes += bytes;\n\t\t\tthis.hasOpenLine = true;\n\t\t} else {\n\t\t\tthis.completedLines += newlines;\n\t\t\tconst tail = text.slice(lastNewline + 1);\n\t\t\tthis.currentLineBytes = byteLength(tail);\n\t\t\tthis.hasOpenLine = tail.length > 0;\n\t\t}\n\t\tthis.totalLines = this.completedLines + (this.hasOpenLine ? 1 : 0);\n\t}\n\n\tprivate trimTail(): void {\n\t\tconst buffer = Buffer.from(this.tailText, \"utf-8\");\n\t\tif (buffer.length <= this.maxRollingBytes) {\n\t\t\tthis.tailBytes = buffer.length;\n\t\t\treturn;\n\t\t}\n\n\t\tlet start = buffer.length - this.maxRollingBytes;\n\t\twhile (start < buffer.length && (buffer[start] & 0xc0) === 0x80) {\n\t\t\tstart++;\n\t\t}\n\n\t\tthis.tailStartsAtLineBoundary = start === 0 ? this.tailStartsAtLineBoundary : buffer[start - 1] === 0x0a;\n\t\tthis.tailText = buffer.subarray(start).toString(\"utf-8\");\n\t\tthis.tailBytes = byteLength(this.tailText);\n\t}\n\n\tprivate getSnapshotText(): string {\n\t\tif (this.tailStartsAtLineBoundary) {\n\t\t\treturn this.tailText;\n\t\t}\n\n\t\tconst firstNewline = this.tailText.indexOf(\"\\n\");\n\t\treturn firstNewline === -1 ? this.tailText : this.tailText.slice(firstNewline + 1);\n\t}\n\n\tprivate shouldUseTempFile(): boolean {\n\t\treturn (\n\t\t\tthis.totalRawBytes > this.maxBytes || this.totalDecodedBytes > this.maxBytes || this.totalLines > this.maxLines\n\t\t);\n\t}\n\n\tprivate ensureTempFile(): void {\n\t\tif (this.tempFilePath) {\n\t\t\treturn;\n\t\t}\n\t\tthis.tempFilePath = defaultTempFilePath(this.tempFilePrefix);\n\t\tthis.tempFileStream = createWriteStream(this.tempFilePath);\n\t\tfor (const chunk of this.rawChunks) {\n\t\t\tthis.tempFileStream.write(chunk);\n\t\t}\n\t\tthis.rawChunks = [];\n\t}\n}\n"]}
1
+ {"version":3,"file":"output-accumulator.d.ts","sourceRoot":"","sources":["../../../src/core/tools/output-accumulator.ts"],"names":[],"mappings":"AAKA,OAAO,EAAwC,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAE1G,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAWD;;;;;;GAMG;AACH,qBAAa,iBAAiB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,wBAAwB,CAAQ;IACxC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAS;IAEzB,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,cAAc,CAA0B;IAEhD,YAAY,OAAO,GAAE,wBAA6B,EAKjD;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAczB;IAED,MAAM,IAAI,IAAI,CASb;IAED,QAAQ,CAAC,OAAO,GAAE;QAAE,kBAAkB,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,cAAc,CA4BvE;IAEK,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAqBnC;IAED,gBAAgB,IAAI,MAAM,CAEzB;IAED,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,QAAQ;IAiBhB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,cAAc;CAWtB"}
@@ -1 +1 @@
1
- {"version":3,"file":"oversized-tool-result.d.ts","sourceRoot":"","sources":["../../../src/core/tools/oversized-tool-result.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAgB,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAU9E;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,0BAA0B,CAAC,EAAE,MAAM,GAAG,MAAM,CAQnF;AAED,MAAM,WAAW,gCAAgC,CAAC,QAAQ,GAAG,OAAO;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC,CAAC;IAC/D,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA8B;IAC9C,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAc1D;AAcD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CA4BxG;AA4FD;;;;;;;;;;;GAWG;AACH,wBAAsB,2BAA2B,CAAC,QAAQ,GAAG,OAAO,EACnE,KAAK,EAAE,gCAAgC,CAAC,QAAQ,CAAC,GAC/C,OAAO,CAAC,8BAA8B,GAAG,SAAS,CAAC,CAwCrD","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AgentToolResult } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport { APP_NAME } from \"../../config.js\";\nimport {\n\tDEFAULT_MAX_RESULT_SIZE_CHARS,\n\tPERSISTED_OUTPUT_CLOSING_TAG,\n\tPERSISTED_OUTPUT_TAG,\n\tPREVIEW_SIZE_BYTES,\n\tTOOL_RESULTS_SUBDIR,\n} from \"./tool-limits.js\";\n\n/**\n * Resolve the effective persistence threshold (in characters) for a tool.\n *\n * Mirrors the upstream `getPersistenceThreshold` convention: a tool may declare a\n * lower per-tool cap, but the global {@link DEFAULT_MAX_RESULT_SIZE_CHARS} acts as\n * a system-wide ceiling. `Infinity` is a hard opt-out for self-bounded tools whose\n * output is already a file the model reads back, where persisting would be circular.\n */\nexport function getPersistenceThreshold(declaredMaxResultSizeChars?: number): number {\n\tif (declaredMaxResultSizeChars === undefined) {\n\t\treturn DEFAULT_MAX_RESULT_SIZE_CHARS;\n\t}\n\tif (!Number.isFinite(declaredMaxResultSizeChars)) {\n\t\treturn declaredMaxResultSizeChars;\n\t}\n\treturn Math.min(declaredMaxResultSizeChars, DEFAULT_MAX_RESULT_SIZE_CHARS);\n}\n\nexport interface RedirectOversizedToolResultInput<TDetails = unknown> {\n\ttoolName: string;\n\ttoolCallId: string;\n\tresult: Pick<AgentToolResult<TDetails>, \"content\" | \"details\">;\n\tisError: boolean;\n\tsessionId: string;\n\tsessionDir?: string;\n\t/** Optional per-tool character cap; clamped by {@link DEFAULT_MAX_RESULT_SIZE_CHARS}. */\n\tmaxResultSizeChars?: number;\n}\n\nexport interface OversizedToolResultReplacement {\n\tcontent: TextContent[];\n\tdetails: unknown;\n\tisError: boolean;\n}\n\n/**\n * Human-readable byte size, e.g. `1536` → \"1.5KB\". Intentionally mirrors\n * upstream Claude Code `formatFileSize` wording instead of using tools/truncate.ts.\n */\nexport function formatFileSize(sizeInBytes: number): string {\n\tconst kb = sizeInBytes / 1024;\n\tif (kb < 1) {\n\t\treturn `${sizeInBytes} bytes`;\n\t}\n\tif (kb < 1024) {\n\t\treturn `${kb.toFixed(1).replace(/\\.0$/, \"\")}KB`;\n\t}\n\tconst mb = kb / 1024;\n\tif (mb < 1024) {\n\t\treturn `${mb.toFixed(1).replace(/\\.0$/, \"\")}MB`;\n\t}\n\tconst gb = mb / 1024;\n\treturn `${gb.toFixed(1).replace(/\\.0$/, \"\")}GB`;\n}\n\nfunction hasImageBlock(content: readonly (TextContent | ImageContent)[]): boolean {\n\treturn content.some((block) => block.type === \"image\");\n}\n\n/** Concatenate the text blocks of a tool result into a single string. */\nfunction collectText(content: readonly (TextContent | ImageContent)[]): string {\n\treturn content\n\t\t.filter((block): block is TextContent => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\"\");\n}\n\n/**\n * Generate a preview of content, truncating at a newline boundary when possible.\n * Ported from the reference `generatePreview`.\n */\nexport function generatePreview(content: string, maxBytes: number): { preview: string; hasMore: boolean } {\n\tif (Buffer.byteLength(content, \"utf8\") <= maxBytes) {\n\t\treturn { preview: content, hasMore: false };\n\t}\n\n\tlet bytes = 0;\n\tlet hardCutIndex = 0;\n\tlet lastNewlineIndex = -1;\n\tlet lastNewlineBytes = 0;\n\n\tfor (const character of content) {\n\t\tconst characterBytes = Buffer.byteLength(character, \"utf8\");\n\t\tif (bytes + characterBytes > maxBytes) {\n\t\t\tbreak;\n\t\t}\n\t\tconst characterStartIndex = hardCutIndex;\n\t\tbytes += characterBytes;\n\t\thardCutIndex += character.length;\n\t\tif (character === \"\\n\") {\n\t\t\tlastNewlineIndex = characterStartIndex;\n\t\t\tlastNewlineBytes = bytes - characterBytes;\n\t\t}\n\t}\n\n\t// Find the last newline within the byte limit to avoid cutting mid-line;\n\t// fall back to the hard byte limit when the newline is too close to the start.\n\tconst cutPoint = lastNewlineBytes > maxBytes * 0.5 ? lastNewlineIndex : hardCutIndex;\n\treturn { preview: content.slice(0, cutPoint), hasMore: true };\n}\n\n/**\n * Build the `<persisted-output>` preview message shown to the model in place of\n * the oversized content. Ported from the reference `buildLargeToolResultMessage`.\n */\nfunction buildPersistedOutputMessage(input: {\n\toriginalSizeBytes: number;\n\tfilepath: string;\n\tpreview: string;\n\thasMore: boolean;\n}): string {\n\tlet message = `${PERSISTED_OUTPUT_TAG}\\n`;\n\tmessage += `Output too large (${formatFileSize(input.originalSizeBytes)}). Full output saved to: ${input.filepath}\\n\\n`;\n\tmessage += `Preview (first ${formatFileSize(PREVIEW_SIZE_BYTES)}):\\n`;\n\tmessage += input.preview;\n\tmessage += input.hasMore ? \"\\n...\\n\" : \"\\n\";\n\tmessage += PERSISTED_OUTPUT_CLOSING_TAG;\n\treturn message;\n}\n\n/** Strip leading/trailing underscores with a linear scan (no backtracking regex). */\nfunction trimUnderscores(value: string): string {\n\tlet start = 0;\n\tlet end = value.length;\n\twhile (start < end && value[start] === \"_\") {\n\t\tstart++;\n\t}\n\twhile (end > start && value[end - 1] === \"_\") {\n\t\tend--;\n\t}\n\treturn value.slice(start, end);\n}\n\nfunction sanitizePathComponent(value: string, fallback: string): string {\n\t// Collapse disallowed characters to \"_\", then strip leading/trailing \"_\". The trim uses\n\t// a manual linear scan instead of a /^_+|_+$/ regex to avoid a polynomial-time ReDoS on\n\t// tool-call ids containing long runs of underscores (CodeQL js/polynomial-redos).\n\tconst sanitized = trimUnderscores(value.replace(/[^a-zA-Z0-9._-]+/g, \"_\")).slice(0, 64);\n\treturn sanitized.length > 0 ? sanitized : fallback;\n}\n\n/** Session-scoped directory for persisted tool results: `<sessionDir>/tool-results`. */\nfunction getToolResultsDir(input: { sessionDir?: string; sessionId: string }): string {\n\tif (input.sessionDir?.trim()) {\n\t\treturn join(input.sessionDir, TOOL_RESULTS_SUBDIR);\n\t}\n\t// Fall back to a stable session-scoped temp directory for in-memory sessions.\n\tconst safeApp = sanitizePathComponent(APP_NAME || \"atomic\", \"atomic\");\n\tconst safeSessionId = sanitizePathComponent(input.sessionId || \"session\", \"session\");\n\treturn join(tmpdir(), `${safeApp}-${TOOL_RESULTS_SUBDIR}`, safeSessionId);\n}\n\nfunction getErrnoCode(error: unknown): string | undefined {\n\tif (error && typeof error === \"object\" && \"code\" in error) {\n\t\tconst code = (error as { code?: unknown }).code;\n\t\treturn typeof code === \"string\" ? code : undefined;\n\t}\n\treturn undefined;\n}\n\n/**\n * Persist the full tool output to a session-scoped file and return its path.\n *\n * The tool_use_id is unique per invocation and the content is deterministic for a\n * given id, so we write with the `wx` flag and treat `EEXIST` as success — this\n * makes repeated calls (e.g. replay/compaction) idempotent without rewriting.\n * Returns `undefined` on any other write failure so the caller can fall back to\n * returning the original result unchanged.\n */\nasync function persistToolOutput(input: {\n\ttext: string;\n\tsessionDir?: string;\n\tsessionId: string;\n\ttoolCallId: string;\n}): Promise<string | undefined> {\n\tconst dir = getToolResultsDir({ sessionDir: input.sessionDir, sessionId: input.sessionId });\n\tconst fileName = `${sanitizePathComponent(input.toolCallId, \"tool-result\")}.txt`;\n\tconst filepath = join(dir, fileName);\n\ttry {\n\t\tawait mkdir(dir, { recursive: true, mode: 0o700 });\n\t\tawait writeFile(filepath, input.text, { encoding: \"utf8\", mode: 0o600, flag: \"wx\" });\n\t} catch (error) {\n\t\tif (getErrnoCode(error) === \"EEXIST\") {\n\t\t\t// Already persisted on a prior turn — reuse the existing file.\n\t\t\treturn filepath;\n\t\t}\n\t\treturn undefined;\n\t}\n\treturn filepath;\n}\n\n/**\n * When a tool result's text content exceeds the persistence threshold (default\n * 50,000 chars — {@link DEFAULT_MAX_RESULT_SIZE_CHARS}), persist the full output\n * to a session-scoped file and replace the model-visible content with a\n * `<persisted-output>` preview that references the saved file.\n *\n * Returns `undefined` (leave the result untouched) when the result is within the\n * threshold, contains image blocks, or cannot be persisted to disk.\n *\n * Convention ported from the upstream `maybePersistLargeToolResult`\n * (mehmoodosman/claude-code, `src/utils/toolResultStorage.ts`).\n */\nexport async function redirectOversizedToolResult<TDetails = unknown>(\n\tinput: RedirectOversizedToolResultInput<TDetails>,\n): Promise<OversizedToolResultReplacement | undefined> {\n\tconst content = input.result.content;\n\n\t// Image content must be sent to the model as-is; never persist.\n\tif (hasImageBlock(content)) {\n\t\treturn undefined;\n\t}\n\n\tconst text = collectText(content);\n\tconst threshold = getPersistenceThreshold(input.maxResultSizeChars);\n\tif (text.length <= threshold) {\n\t\treturn undefined;\n\t}\n\n\tconst filepath = await persistToolOutput({\n\t\ttext,\n\t\tsessionDir: input.sessionDir,\n\t\tsessionId: input.sessionId,\n\t\ttoolCallId: input.toolCallId,\n\t});\n\t// Graceful degradation: if persistence failed, leave the original result unchanged.\n\tif (filepath === undefined) {\n\t\treturn undefined;\n\t}\n\n\tconst { preview, hasMore } = generatePreview(text, PREVIEW_SIZE_BYTES);\n\tconst message = buildPersistedOutputMessage({\n\t\toriginalSizeBytes: Buffer.byteLength(text, \"utf8\"),\n\t\tfilepath,\n\t\tpreview,\n\t\thasMore,\n\t});\n\n\treturn {\n\t\tcontent: [{ type: \"text\", text: message }],\n\t\t// Details (tool metadata) are passed through untouched, matching the reference\n\t\t// convention which only replaces the model-visible content block.\n\t\tdetails: input.result.details,\n\t\tisError: input.isError,\n\t};\n}\n"]}
1
+ {"version":3,"file":"oversized-tool-result.d.ts","sourceRoot":"","sources":["../../../src/core/tools/oversized-tool-result.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAgB,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAU9E;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,0BAA0B,CAAC,EAAE,MAAM,GAAG,MAAM,CAQnF;AAED,MAAM,WAAW,gCAAgC,CAAC,QAAQ,GAAG,OAAO;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC,CAAC;IAC/D,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA8B;IAC9C,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAc1D;AAcD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CA4BxG;AA4FD;;;;;;;;;;;GAWG;AACH,wBAAsB,2BAA2B,CAAC,QAAQ,GAAG,OAAO,EACnE,KAAK,EAAE,gCAAgC,CAAC,QAAQ,CAAC,GAC/C,OAAO,CAAC,8BAA8B,GAAG,SAAS,CAAC,CAwCrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../../../src/core/tools/path-utils.ts"],"names":[],"mappings":"AA8BA,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnE;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAgCrE;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgCzF","sourcesContent":["import { accessSync, constants } from \"node:fs\";\nimport { access } from \"node:fs/promises\";\nimport { normalizePath, resolvePath } from \"../../utils/paths.ts\";\n\nconst NARROW_NO_BREAK_SPACE = \"\\u202F\";\n\nfunction tryMacOSScreenshotPath(filePath: string): string {\n\treturn filePath.replace(/ (AM|PM)\\./gi, `${NARROW_NO_BREAK_SPACE}$1.`);\n}\n\nfunction tryNFDVariant(filePath: string): string {\n\t// macOS stores filenames in NFD (decomposed) form, try converting user input to NFD\n\treturn filePath.normalize(\"NFD\");\n}\n\nfunction tryCurlyQuoteVariant(filePath: string): string {\n\t// macOS uses U+2019 (right single quotation mark) in screenshot names like \"Capture d'écran\"\n\t// Users typically type U+0027 (straight apostrophe)\n\treturn filePath.replace(/'/g, \"\\u2019\");\n}\n\nfunction fileExists(filePath: string): boolean {\n\ttry {\n\t\taccessSync(filePath, constants.F_OK);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport async function pathExists(filePath: string): Promise<boolean> {\n\ttry {\n\t\tawait access(filePath, constants.F_OK);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport function expandPath(filePath: string): string {\n\treturn normalizePath(filePath, { normalizeUnicodeSpaces: true, stripAtPrefix: true });\n}\n\n/**\n * Resolve a path relative to the given cwd.\n * Handles ~ expansion and absolute paths.\n */\nexport function resolveToCwd(filePath: string, cwd: string): string {\n\treturn resolvePath(filePath, cwd, { normalizeUnicodeSpaces: true, stripAtPrefix: true });\n}\n\nexport function resolveReadPath(filePath: string, cwd: string): string {\n\tconst resolved = resolveToCwd(filePath, cwd);\n\n\tif (fileExists(resolved)) {\n\t\treturn resolved;\n\t}\n\n\t// Try macOS AM/PM variant (narrow no-break space before AM/PM)\n\tconst amPmVariant = tryMacOSScreenshotPath(resolved);\n\tif (amPmVariant !== resolved && fileExists(amPmVariant)) {\n\t\treturn amPmVariant;\n\t}\n\n\t// Try NFD variant (macOS stores filenames in NFD form)\n\tconst nfdVariant = tryNFDVariant(resolved);\n\tif (nfdVariant !== resolved && fileExists(nfdVariant)) {\n\t\treturn nfdVariant;\n\t}\n\n\t// Try curly quote variant (macOS uses U+2019 in screenshot names)\n\tconst curlyVariant = tryCurlyQuoteVariant(resolved);\n\tif (curlyVariant !== resolved && fileExists(curlyVariant)) {\n\t\treturn curlyVariant;\n\t}\n\n\t// Try combined NFD + curly quote (for French macOS screenshots like \"Capture d'écran\")\n\tconst nfdCurlyVariant = tryCurlyQuoteVariant(nfdVariant);\n\tif (nfdCurlyVariant !== resolved && fileExists(nfdCurlyVariant)) {\n\t\treturn nfdCurlyVariant;\n\t}\n\n\treturn resolved;\n}\n\nexport async function resolveReadPathAsync(filePath: string, cwd: string): Promise<string> {\n\tconst resolved = resolveToCwd(filePath, cwd);\n\n\tif (await pathExists(resolved)) {\n\t\treturn resolved;\n\t}\n\n\t// Try macOS AM/PM variant (narrow no-break space before AM/PM)\n\tconst amPmVariant = tryMacOSScreenshotPath(resolved);\n\tif (amPmVariant !== resolved && (await pathExists(amPmVariant))) {\n\t\treturn amPmVariant;\n\t}\n\n\t// Try NFD variant (macOS stores filenames in NFD form)\n\tconst nfdVariant = tryNFDVariant(resolved);\n\tif (nfdVariant !== resolved && (await pathExists(nfdVariant))) {\n\t\treturn nfdVariant;\n\t}\n\n\t// Try curly quote variant (macOS uses U+2019 in screenshot names)\n\tconst curlyVariant = tryCurlyQuoteVariant(resolved);\n\tif (curlyVariant !== resolved && (await pathExists(curlyVariant))) {\n\t\treturn curlyVariant;\n\t}\n\n\t// Try combined NFD + curly quote (for French macOS screenshots like \"Capture d'écran\")\n\tconst nfdCurlyVariant = tryCurlyQuoteVariant(nfdVariant);\n\tif (nfdCurlyVariant !== resolved && (await pathExists(nfdCurlyVariant))) {\n\t\treturn nfdCurlyVariant;\n\t}\n\n\treturn resolved;\n}\n"]}
1
+ {"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../../../src/core/tools/path-utils.ts"],"names":[],"mappings":"AA8BA,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnE;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAgCrE;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgCzF"}
@@ -1 +1 @@
1
- {"version":3,"file":"read-document-extract.d.ts","sourceRoot":"","sources":["../../../src/core/tools/read-document-extract.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2C,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKlG,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAgD;AAwJ1G,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI7F;AAED,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQxF;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAMtL","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { convertBufferWithMarkit, convertFileWithMarkit } from \"../../utils/markit.ts\";\nimport { selectExactReadRanges, selectReadRanges, type ReadLineRange } from \"./read-selectors.ts\";\n\nconst DOCUMENT_EXTENSIONS = /\\.(?:pdf|doc|docx|ppt|pptx|xls|xlsx|rtf|epub|ipynb)(?:$|[?#])/i;\nconst MARKIT_EXTENSIONS = /\\.(?:pdf|doc|docx|ppt|pptx|xls|xlsx|rtf|epub)(?:$|[?#])/i;\n\nexport function isDocumentPath(pathValue: string): boolean { return DOCUMENT_EXTENSIONS.test(pathValue); }\n\nfunction documentExtensionFromContentType(contentType: string): string | undefined {\n\tif (/ipynb|jupyter/i.test(contentType)) return \".ipynb\";\n\tif (/pdf/i.test(contentType)) return \".pdf\";\n\tif (/msword/i.test(contentType)) return \".doc\";\n\tif (/wordprocessingml|officedocument\\.word/i.test(contentType)) return \".docx\";\n\tif (/presentationml|officedocument\\.presentation/i.test(contentType)) return \".pptx\";\n\tif (/ms-powerpoint|vnd\\.ms-powerpoint/i.test(contentType)) return \".ppt\";\n\tif (/spreadsheetml|officedocument\\.spreadsheet/i.test(contentType)) return \".xlsx\";\n\tif (/epub/i.test(contentType)) return \".epub\";\n\tif (/ms-excel|vnd\\.ms-excel/i.test(contentType)) return \".xls\";\n\tif (/rtf/i.test(contentType)) return \".rtf\";\n\treturn undefined;\n}\n\nfunction isHtmlWhitespace(char: string | undefined): boolean { return char === \" \" || char === \"\\t\" || char === \"\\n\" || char === \"\\r\" || char === \"\\f\"; }\n\nfunction findElementStart(lowerHtml: string, tagName: string, fromIndex: number): number {\n\tconst needle = `<${tagName}`;\n\tlet index = fromIndex;\n\tfor (;;) {\n\t\tconst found = lowerHtml.indexOf(needle, index);\n\t\tif (found < 0) return -1;\n\t\tconst next = lowerHtml[found + needle.length];\n\t\tif (next === undefined || next === \">\" || next === \"/\" || isHtmlWhitespace(next)) return found;\n\t\tindex = found + needle.length;\n\t}\n}\n\nfunction findClosingTag(lowerHtml: string, tagName: string, fromIndex: number): number {\n\tconst needle = `</${tagName}`;\n\tlet index = fromIndex;\n\tfor (;;) {\n\t\tconst found = lowerHtml.indexOf(needle, index);\n\t\tif (found < 0) return -1;\n\t\tlet cursor = found + needle.length;\n\t\twhile (isHtmlWhitespace(lowerHtml[cursor])) cursor++;\n\t\tif (lowerHtml[cursor] === \">\") return found;\n\t\tindex = found + needle.length;\n\t}\n}\n\nfunction stripElementBlocks(html: string, tagName: string): string {\n\tconst lowerHtml = html.toLowerCase();\n\tconst chunks: string[] = [];\n\tlet index = 0;\n\tfor (;;) {\n\t\tconst start = findElementStart(lowerHtml, tagName, index);\n\t\tif (start < 0) break;\n\t\tchunks.push(html.slice(index, start));\n\t\tconst startEnd = html.indexOf(\">\", start);\n\t\tif (startEnd < 0) { index = html.length; break; }\n\t\tconst closeStart = findClosingTag(lowerHtml, tagName, startEnd + 1);\n\t\tif (closeStart < 0) { index = html.length; break; }\n\t\tconst closeEnd = html.indexOf(\">\", closeStart);\n\t\tif (closeEnd < 0) { index = html.length; break; }\n\t\tindex = closeEnd + 1;\n\t}\n\tchunks.push(html.slice(index));\n\treturn chunks.join(\"\");\n}\n\nfunction extractElementText(html: string, tagName: string): string | undefined {\n\tconst lowerHtml = html.toLowerCase();\n\tconst start = findElementStart(lowerHtml, tagName, 0);\n\tif (start < 0) return undefined;\n\tconst startEnd = html.indexOf(\">\", start);\n\tif (startEnd < 0) return undefined;\n\tconst closeStart = findClosingTag(lowerHtml, tagName, startEnd + 1);\n\treturn closeStart < 0 ? undefined : html.slice(startEnd + 1, closeStart);\n}\n\nfunction readTagName(tagContent: string): { closing: boolean; name: string } {\n\tlet index = 0;\n\twhile (isHtmlWhitespace(tagContent[index])) index++;\n\tconst closing = tagContent[index] === \"/\";\n\tif (closing) index++;\n\twhile (isHtmlWhitespace(tagContent[index])) index++;\n\tconst start = index;\n\twhile (index < tagContent.length) {\n\t\tconst char = tagContent[index];\n\t\tif (!char || !(char >= \"a\" && char <= \"z\") && !(char >= \"A\" && char <= \"Z\") && !(char >= \"0\" && char <= \"9\")) break;\n\t\tindex++;\n\t}\n\treturn { closing, name: tagContent.slice(start, index).toLowerCase() };\n}\n\nfunction htmlMarkupToPlainText(html: string): string {\n\tlet output = \"\";\n\tlet index = 0;\n\twhile (index < html.length) {\n\t\tconst open = html.indexOf(\"<\", index);\n\t\tif (open < 0) { output += html.slice(index); break; }\n\t\toutput += html.slice(index, open);\n\t\tconst close = html.indexOf(\">\", open + 1);\n\t\tif (close < 0) break;\n\t\tconst tag = readTagName(html.slice(open + 1, close));\n\t\tif (tag.name === \"br\" || tag.closing && [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\", \"p\", \"div\", \"li\", \"tr\", \"blockquote\", \"pre\"].includes(tag.name)) output += \"\\n\";\n\t\telse if (!tag.closing && tag.name === \"li\") output += \"- \";\n\t\tindex = close + 1;\n\t}\n\treturn normalizeDecodedText(output);\n}\n\nfunction normalizeDecodedText(value: string): string {\n\treturn decodeEntities(value).split(\"\\n\").map((line) => line.trim()).filter(Boolean).join(\"\\n\");\n}\n\nfunction htmlToReadableText(html: string): string {\n\tconst stripped = stripElementBlocks(stripElementBlocks(html, \"script\"), \"style\");\n\tconst title = extractElementText(stripped, \"title\");\n\tconst titleText = title ? htmlMarkupToPlainText(title).split(\"\\n\").join(\" \").trim() : undefined;\n\tconst text = htmlMarkupToPlainText(stripped);\n\treturn titleText && !text.startsWith(titleText) ? `# ${titleText}\\n\\n${text}` : text;\n}\n\nfunction decodeEntities(value: string): string {\n\tconst entities: Record<string, string> = { nbsp: \" \", amp: \"&\", lt: \"<\", gt: \">\", quot: '\"', \"#39\": \"'\" };\n\tlet output = \"\";\n\tlet index = 0;\n\twhile (index < value.length) {\n\t\tconst ampersand = value.indexOf(\"&\", index);\n\t\tif (ampersand < 0) { output += value.slice(index); break; }\n\t\toutput += value.slice(index, ampersand);\n\t\tconst semicolon = value.indexOf(\";\", ampersand + 1);\n\t\tif (semicolon < 0 || semicolon - ampersand > 12) { output += \"&\"; index = ampersand + 1; continue; }\n\t\tconst entity = value.slice(ampersand + 1, semicolon).toLowerCase();\n\t\tconst decoded = entities[entity];\n\t\toutput += decoded ?? value.slice(ampersand, semicolon + 1);\n\t\tindex = semicolon + 1;\n\t}\n\treturn output;\n}\n\nfunction notebookMarkdown(buffer: Buffer, source: string): string {\n\tconst nb = JSON.parse(buffer.toString(\"utf8\")) as { cells?: Array<{ cell_type?: string; source?: string | string[] }> };\n\tconst cells = nb.cells ?? [];\n\treturn cells.map((cell, index) => {\n\t\tconst sourceText = Array.isArray(cell.source) ? cell.source.join(\"\") : cell.source ?? \"\";\n\t\treturn `# %% [${cell.cell_type ?? \"raw\"}] cell:${index}\\n${sourceText.trimEnd()}`;\n\t}).join(\"\\n\\n\") || `# ${source}\\n\\n(empty notebook)`;\n}\n\nfunction documentExtension(source: string): string { return `.${source.match(/\\.(pdf|docx?|pptx?|xlsx?|rtf|epub)(?:$|[?#])/i)?.[1]?.toLowerCase() ?? \"bin\"}`; }\n\nasync function extractMarkitDocument(buffer: Buffer, source: string): Promise<string> {\n\tconst ext = documentExtension(source);\n\tconst result = existsSync(source) ? await convertFileWithMarkit(source) : await convertBufferWithMarkit(buffer, ext);\n\treturn result.ok ? result.content : `[Cannot read ${ext} file: ${result.error || \"conversion failed\"}]`;\n}\n\nexport async function extractDocumentMarkdown(buffer: Buffer, source: string): Promise<string> {\n\tif (/\\.ipynb(?:$|[?#])/i.test(source)) return notebookMarkdown(buffer, source);\n\tif (MARKIT_EXTENSIONS.test(source)) return extractMarkitDocument(buffer, source);\n\treturn buffer.toString(\"utf8\");\n}\n\nexport async function decodeReadableUrl(response: Response, url: string): Promise<string> {\n\tconst contentType = response.headers.get(\"content-type\") ?? \"\";\n\tconst buffer = Buffer.from(await response.arrayBuffer());\n\tconst contentTypeExtension = documentExtensionFromContentType(contentType);\n\tif (contentTypeExtension || isDocumentPath(url)) return extractDocumentMarkdown(buffer, contentTypeExtension && !isDocumentPath(url) ? `${url}${contentTypeExtension}` : url);\n\tconst text = buffer.toString(\"utf8\");\n\tif (/html/i.test(contentType) || /<html[\\s>]/i.test(text)) return htmlToReadableText(text);\n\treturn text;\n}\n\nexport function applyReadLineSelection(allLines: string[], ranges: ReadLineRange[] | undefined, offset?: number, limit?: number, exact = false): { lines: string[]; firstLine: number } {\n\tconst rangeSelection = (exact ? selectExactReadRanges : selectReadRanges)(allLines, ranges);\n\tconst rangeStart = ranges?.[0]?.start;\n\tconst startLine = rangeSelection ? (rangeSelection.selectedLines.length === 0 ? rangeStart ?? rangeSelection.firstLine : rangeSelection.firstLine) - 1 : offset ? Math.max(0, offset - 1) : 0;\n\tconst endLine = limit !== undefined ? Math.min(startLine + limit, allLines.length) : allLines.length;\n\treturn { lines: rangeSelection?.selectedLines ?? allLines.slice(startLine, endLine), firstLine: startLine + 1 };\n}\n"]}
1
+ {"version":3,"file":"read-document-extract.d.ts","sourceRoot":"","sources":["../../../src/core/tools/read-document-extract.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2C,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKlG,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAgD;AAwJ1G,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI7F;AAED,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQxF;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAMtL"}
@@ -1 +1 @@
1
- {"version":3,"file":"read-selectors.d.ts","sourceRoot":"","sources":["../../../src/core/tools/read-selectors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE;AAE9D,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;IACzB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AA8BD,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEjE;AAuID,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAUzE;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,SAAS,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAYlI;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,SAAS,GAAG;IAAE,aAAa,EAAE,MAAM,EAAE,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAwBhO;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE,SAAS,SAAI,GAAG,MAAM,CAE1H","sourcesContent":["export interface ReadLineRange { start: number; end?: number }\n\nexport interface ReadLineSelector {\n\tpath: string;\n\toffset?: number;\n\tlimit?: number;\n\tranges?: ReadLineRange[];\n\traw?: boolean;\n\tconflicts?: boolean;\n}\n\nconst ARCHIVE_SELECTOR_EXTENSIONS = [\".tar.gz\", \".zip\", \".jar\", \".tar\", \".tgz\", \".gz\"];\nconst RESOURCE_SELECTOR_EXTENSIONS = [\".tar.gz\", \".sqlite\", \".zip\", \".jar\", \".tar\", \".tgz\", \".gz\", \".db\"];\n\nfunction isAsciiDigit(char: string | undefined): boolean { return char !== undefined && char >= \"0\" && char <= \"9\"; }\nfunction hasUrlScheme(value: string): boolean {\n\tconst schemeEnd = value.indexOf(\"://\");\n\tif (schemeEnd <= 0) return false;\n\tfor (let index = 0; index < schemeEnd; index++) {\n\t\tconst char = value[index]!;\n\t\tif (!((char >= \"a\" && char <= \"z\") || (char >= \"A\" && char <= \"Z\"))) return false;\n\t}\n\treturn true;\n}\nfunction selectorExtensionColonIndex(value: string, extensions: readonly string[]): number {\n\tconst lower = value.toLowerCase();\n\tlet best = Number.POSITIVE_INFINITY;\n\tfor (const extension of extensions) {\n\t\tlet from = 0;\n\t\tfor (;;) {\n\t\t\tconst extensionIndex = lower.indexOf(`${extension}:`, from);\n\t\t\tif (extensionIndex < 0) break;\n\t\t\tif (extensionIndex > 0) best = Math.min(best, extensionIndex + extension.length);\n\t\t\tfrom = extensionIndex + 1;\n\t\t}\n\t}\n\treturn Number.isFinite(best) ? best : -1;\n}\n\nexport function isReadResourceSelector(pathValue: string): boolean {\n\treturn selectorExtensionColonIndex(pathValue, RESOURCE_SELECTOR_EXTENSIONS) >= 0 || hasUrlScheme(pathValue) || pathValue.startsWith(\"skill://\");\n}\nfunction archiveSelectorColonIndex(value: string): number { return selectorExtensionColonIndex(value, ARCHIVE_SELECTOR_EXTENSIONS); }\nfunction isArchiveSelectorPath(value: string): boolean { return archiveSelectorColonIndex(value) >= 0; }\nfunction hasArchiveMember(value: string): boolean { const colonIndex = archiveSelectorColonIndex(value); return colonIndex >= 0 && colonIndex < value.length - 1; }\nfunction peelArchiveReadSuffixes(value: string, state: { raw: boolean; conflicts: boolean }): string {\n\tlet working = value;\n\tfor (;;) {\n\t\tconst lower = working.toLowerCase();\n\t\tif (lower.endsWith(\":raw\")) { state.raw = true; working = working.slice(0, -4); continue; }\n\t\tif (lower.endsWith(\":conflicts\")) { state.conflicts = true; working = working.slice(0, -10); continue; }\n\t\treturn working;\n\t}\n}\nfunction findInlineSelector(value: string, selector: \"raw\" | \"conflicts\"): number {\n\tconst needle = `:${selector}`;\n\tlet index = 0;\n\tfor (;;) {\n\t\tconst found = value.toLowerCase().indexOf(needle, index);\n\t\tif (found < 0) return -1;\n\t\tconst next = value[found + needle.length];\n\t\tif (next === undefined || next === \":\") return found;\n\t\tindex = found + needle.length;\n\t}\n}\nfunction peelInlineReadSuffixes(value: string, state: { raw: boolean; conflicts: boolean }): string {\n\tlet working = value;\n\tfor (;;) {\n\t\tconst rawIndex = findInlineSelector(working, \"raw\");\n\t\tif (rawIndex >= 0) { state.raw = true; working = `${working.slice(0, rawIndex)}${working.slice(rawIndex + 4)}`; continue; }\n\t\tconst conflictIndex = findInlineSelector(working, \"conflicts\");\n\t\tif (conflictIndex >= 0) { state.conflicts = true; working = `${working.slice(0, conflictIndex)}${working.slice(conflictIndex + 10)}`; continue; }\n\t\treturn working;\n\t}\n}\nfunction startsLikeLineRange(value: string): boolean {\n\tlet index = value[0]?.toLowerCase() === \"l\" ? 1 : 0;\n\treturn isAsciiDigit(value[index]);\n}\nfunction readNumber(value: string, start: number): { value: number; end: number } | undefined {\n\tlet index = start;\n\twhile (isAsciiDigit(value[index])) index++;\n\tif (index === start) return undefined;\n\treturn { value: Number.parseInt(value.slice(start, index), 10), end: index };\n}\nfunction parseLineRangeToken(token: string, invalid: (message: string) => void): ReadLineRange | undefined {\n\tlet index = token[0]?.toLowerCase() === \"l\" ? 1 : 0;\n\tconst startNumber = readNumber(token, index);\n\tif (!startNumber) return undefined;\n\tconst start = startNumber.value;\n\tif (start < 1) { invalid(\"Line selector 0 is invalid; lines are 1-indexed. Use :1.\"); return undefined; }\n\tindex = startNumber.end;\n\tif (index === token.length) return { start };\n\tlet separator: \"-\" | \"..\" | \"+\" | undefined;\n\tif (token.startsWith(\"..\", index)) { separator = \"..\"; index += 2; }\n\telse if (token[index] === \"-\" || token[index] === \"+\") { separator = token[index] as \"-\" | \"+\"; index++; }\n\tif (!separator) { invalid(`Invalid line selector: ${token}`); return undefined; }\n\tif (token[index]?.toLowerCase() === \"l\") index++;\n\tconst endNumber = readNumber(token, index);\n\tif (!endNumber) {\n\t\tif (separator === \"+\") invalid(`Invalid line selector :${token}; + requires a line count >= 1.`);\n\t\treturn separator === \"+\" ? undefined : { start };\n\t}\n\tif (endNumber.end !== token.length) { invalid(`Invalid line selector: ${token}`); return undefined; }\n\tconst parsed = endNumber.value;\n\tif (separator === \"+\") {\n\t\tif (parsed < 1) { invalid(`Invalid line selector :${token}; + count must be >= 1.`); return undefined; }\n\t\treturn { start, end: start + parsed - 1 };\n\t}\n\tif (parsed < start) { invalid(`Invalid line selector :${token}; end must be >= start.`); return undefined; }\n\treturn { start, end: parsed };\n}\nfunction isLineRangeListCandidate(value: string): boolean {\n\tif (!startsLikeLineRange(value)) return false;\n\tfor (const char of value) {\n\t\tif (!isAsciiDigit(char) && char !== \"L\" && char !== \"l\" && char !== \"+\" && char !== \"-\" && char !== \".\" && char !== \",\") return false;\n\t}\n\treturn true;\n}\n\nfunction parseLineRangeList(value: string, invalid: (message: string) => void): ReadLineRange[] | undefined {\n\tif (!isLineRangeListCandidate(value)) return undefined;\n\tconst ranges: ReadLineRange[] = [];\n\tfor (const token of value.split(\",\")) {\n\t\tif (!token) { invalid(`Invalid line selector: ${value}`); return undefined; }\n\t\tconst range = parseLineRangeToken(token, invalid);\n\t\tif (!range) return undefined;\n\t\tranges.push(range);\n\t}\n\treturn ranges;\n}\nfunction isBareLineNumber(value: string): boolean { return value.length > 0 && [...value].every(isAsciiDigit); }\nfunction extractTrailingLineRange(value: string, invalid: (message: string) => void): { path: string; ranges: ReadLineRange[] } | undefined {\n\tconst colonIndex = value.lastIndexOf(\":\");\n\tif (colonIndex < 0) return undefined;\n\tconst rangeText = value.slice(colonIndex + 1);\n\tconst ranges = parseLineRangeList(rangeText, invalid);\n\tif (!ranges) return undefined;\n\tconst path = value.slice(0, colonIndex);\n\t// Support the `file:START:END` (and grep-style `file:LINE:COL`) colon\n\t// convention: a trailing bare-number segment preceded by another bare-number\n\t// segment reads as START..END. Without this the leading number stays glued to\n\t// the path (`file:395`) and produces a broken filename.\n\tconst prevColon = path.lastIndexOf(\":\");\n\tif (prevColon >= 0 && ranges.length === 1 && ranges[0]!.end === undefined && isBareLineNumber(rangeText)) {\n\t\tconst prevSegment = path.slice(prevColon + 1);\n\t\tif (isBareLineNumber(prevSegment)) {\n\t\t\tconst start = Number.parseInt(prevSegment, 10);\n\t\t\tconst end = ranges[0]!.start;\n\t\t\tif (start >= 1) return { path: path.slice(0, prevColon), ranges: [end >= start ? { start, end } : { start }] };\n\t\t}\n\t}\n\treturn { path, ranges };\n}\nfunction selectorFromRanges(path: string, ranges: ReadLineRange[], raw: boolean, conflicts: boolean): ReadLineSelector {\n\tif (ranges.length === 1 && ranges[0]!.end === undefined) return { path, offset: ranges[0]!.start, raw, conflicts };\n\treturn { path, ranges, raw, conflicts };\n}\nfunction parseArchiveReadSelector(value: string): ReadLineSelector {\n\tconst state = { raw: false, conflicts: false };\n\tlet working = peelArchiveReadSuffixes(value, state);\n\tconst extracted = extractTrailingLineRange(working, () => undefined);\n\tif (!extracted) return { path: working, raw: state.raw, conflicts: state.conflicts };\n\tconst suffixState = { ...state };\n\tconst peeledPath = peelArchiveReadSuffixes(extracted.path, suffixState);\n\tconst selectorPath = hasArchiveMember(peeledPath) ? (state.raw = suffixState.raw, state.conflicts = suffixState.conflicts, peeledPath) : extracted.path;\n\tif (!hasArchiveMember(selectorPath)) return { path: working, raw: state.raw, conflicts: state.conflicts };\n\treturn selectorFromRanges(selectorPath, extracted.ranges, state.raw, state.conflicts);\n}\nfunction isHttpUrlWithPortOnly(value: string): boolean {\n\ttry {\n\t\tconst url = new URL(value);\n\t\treturn (url.protocol === \"http:\" || url.protocol === \"https:\") && url.port !== \"\" && url.pathname === \"/\" && url.search === \"\" && url.hash === \"\";\n\t} catch { return false; }\n}\n\nexport function splitReadLineSelector(pathValue: string): ReadLineSelector {\n\tconst state = { raw: false, conflicts: false };\n\tlet value = pathValue;\n\tif (isArchiveSelectorPath(value)) return parseArchiveReadSelector(value);\n\tvalue = peelInlineReadSuffixes(value, state);\n\tif (isHttpUrlWithPortOnly(value)) return { path: value, raw: state.raw, conflicts: state.conflicts };\n\tlet parseError: string | undefined;\n\tconst extracted = extractTrailingLineRange(value, (message) => { parseError = message; });\n\tif (parseError) throw new Error(parseError);\n\treturn extracted ? selectorFromRanges(extracted.path, extracted.ranges, state.raw, state.conflicts) : { path: value, raw: state.raw, conflicts: state.conflicts };\n}\n\nexport function selectExactReadRanges(allLines: string[], ranges: ReadLineRange[] | undefined): ReturnType<typeof selectReadRanges> {\n\tif (!ranges || ranges.length === 0) return undefined;\n\tconst selectedLines: string[] = [], lineNumbers: number[] = [], merged: ReadLineRange[] = [];\n\tfor (const range of [...ranges].sort((a, b) => a.start - b.start)) {\n\t\tif (range.start > allLines.length) continue;\n\t\tconst end = Math.min(range.end ?? allLines.length, allLines.length);\n\t\tconst previous = merged.at(-1);\n\t\tif (previous && range.start <= (previous.end ?? 0) + 1) previous.end = Math.max(previous.end ?? 0, end);\n\t\telse merged.push({ start: range.start, end });\n\t}\n\tfor (const range of merged) for (let line = range.start; line <= (range.end ?? allLines.length); line++) { selectedLines.push(allLines[line - 1] ?? \"\"); lineNumbers.push(line); }\n\treturn { selectedLines, selectedContent: selectedLines.join(\"\\n\"), firstLine: lineNumbers[0] ?? 1, lineNumbers, userLimitedLines: selectedLines.length };\n}\n\nexport function selectReadRanges(allLines: string[], ranges: ReadLineRange[] | undefined): { selectedLines: string[]; selectedContent: string; firstLine: number; lineNumbers?: number[]; userLimitedLines?: number } | undefined {\n\tif (!ranges || ranges.length === 0) return undefined;\n\tconst selectedLines: string[] = [];\n\tconst lineNumbers: number[] = [];\n\tconst merged: ReadLineRange[] = [];\n\tfor (const range of [...ranges].sort((a, b) => a.start - b.start)) {\n\t\tconst bounded = range.end !== undefined;\n\t\tif (range.start > allLines.length) continue;\n\t\tconst requestedEnd = Math.min(range.end ?? allLines.length, allLines.length);\n\t\tif (requestedEnd < range.start) continue;\n\t\tconst start = Math.max(1, range.start - (bounded ? 1 : 0));\n\t\tconst end = Math.min(allLines.length, requestedEnd + (bounded ? 3 : 0));\n\t\tconst previous = merged.at(-1);\n\t\tif (previous && start <= (previous.end ?? 0) + 1) previous.end = Math.max(previous.end ?? 0, end);\n\t\telse merged.push({ start, end });\n\t}\n\tfor (const range of merged) {\n\t\tconst end = Math.min(range.end ?? allLines.length, allLines.length);\n\t\tfor (let line = range.start; line <= end; line++) {\n\t\t\tselectedLines.push(allLines[line - 1] ?? \"\");\n\t\t\tlineNumbers.push(line);\n\t\t}\n\t}\n\treturn { selectedLines, selectedContent: selectedLines.join(\"\\n\"), firstLine: lineNumbers[0] ?? 1, lineNumbers, userLimitedLines: selectedLines.length };\n}\n\nexport function formatHashlineSelectedLines(header: string, lines: string[], lineNumbers?: number[], startLine = 1): string {\n\treturn [header, ...lines.map((line, index) => `${lineNumbers?.[index] ?? startLine + index}:${line}`)].join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"read-selectors.d.ts","sourceRoot":"","sources":["../../../src/core/tools/read-selectors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE;AAE9D,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;IACzB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AA8BD,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEjE;AAuID,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAUzE;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,SAAS,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAYlI;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,SAAS,GAAG;IAAE,aAAa,EAAE,MAAM,EAAE,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAwBhO;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE,SAAS,SAAI,GAAG,MAAM,CAE1H"}
@@ -1 +1 @@
1
- {"version":3,"file":"read-url.d.ts","sourceRoot":"","sources":["../../../src/core/tools/read-url.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAIjD,MAAM,WAAW,iBAAiB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,GAAG,SAAS,CAAC;IAC/D,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,KAAK;QAAE,OAAO,EAAE,WAAW,EAAE,CAAC;QAAC,OAAO,EAAE,eAAe,CAAA;KAAE,CAAC;IACzO,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,eAAe,CAAC;CAChD;AAED,MAAM,MAAM,mBAAmB,GAAG;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,OAAO,EAAE,eAAe,GAAG,SAAS,CAAA;CAAE,CAAC;AAEnG,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA6BzF","sourcesContent":["/**\n * URL branch of the `read` tool: routes plain URL reads through the\n * cache/artifact/llms.txt fetch pipeline ({@link executeReadUrl}); explicit\n * URL line selectors reuse the same safe rendering path before slicing lines.\n */\nimport { resolve as resolvePath } from \"node:path\";\nimport type { TextContent } from \"@earendil-works/pi-ai/compat\";\nimport type { ReadToolDetails } from \"./read.ts\";\nimport { applyReadLineSelection, decodeReadableUrl } from \"./read-document-extract.ts\";\nimport { executeReadUrl, loadPage } from \"./fetch-url.ts\";\n\nexport interface ReadUrlBranchArgs {\n\teffectivePath: string;\n\trawOutput: boolean;\n\teffectiveRanges: { start: number; end?: number }[] | undefined;\n\teffectiveOffset: number | undefined;\n\teffectiveLimit: number | undefined;\n\tcwd: string;\n\tctx: unknown;\n\tsignal?: AbortSignal;\n\tmaxChars: number;\n\tmaxBytes: number;\n\toversized: (details: { blocked: true; path: string; chars: number; maxChars: number; startLine: number; totalFileLines: number; firstLineBytes: number; byteGuidance: boolean }) => { content: TextContent[]; details: ReadToolDetails };\n\tsourceMeta: (source: string) => ReadToolDetails;\n}\n\nexport type ReadUrlBranchResult = { content: TextContent[]; details: ReadToolDetails | undefined };\n\nexport async function readUrlBranch(args: ReadUrlBranchArgs): Promise<ReadUrlBranchResult> {\n\tconst { effectivePath, rawOutput, effectiveRanges, effectiveOffset, effectiveLimit, cwd, ctx, signal, maxChars, maxBytes, oversized, sourceMeta } = args;\n\tconst session = (ctx as { sessionManager?: { getSessionDir?: () => string | undefined; getSessionId?: () => string | undefined } } | undefined)?.sessionManager;\n\tconst sessionDir = session?.getSessionDir?.();\n\tconst scope = session?.getSessionId?.() ?? cwd;\n\tconst artifactsDir = sessionDir ? resolvePath(sessionDir, \"artifacts\") : undefined;\n\tif (!effectiveRanges && !effectiveOffset && effectiveLimit === undefined) {\n\t\tconst result = await executeReadUrl(scope, { path: effectivePath, raw: rawOutput }, artifactsDir, signal);\n\t\tconst artifactId = result.artifactId ?? result.details.meta?.artifactId;\n\t\tconst truncation = result.details.meta?.truncation;\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: result.content }],\n\t\t\tdetails: {\n\t\t\t\t...(truncation ? { truncation } : {}),\n\t\t\t\tmeta: { ...(result.details.meta ?? {}), source: effectivePath, sourcePath: effectivePath, ...(artifactId ? { artifactId } : {}) },\n\t\t\t},\n\t\t};\n\t}\n\tconst page = await loadPage(effectivePath, 10_000, signal);\n\tconst textContent = rawOutput ? page.content : await decodeReadableUrl(new Response(page.content, { headers: { \"content-type\": page.contentType } }), page.finalUrl || effectivePath);\n\tconst selection = applyReadLineSelection(textContent.split(\"\\n\"), effectiveRanges, effectiveOffset, effectiveLimit, rawOutput), selectedText = selection.lines.join(\"\\n\");\n\tif ((effectiveRanges || effectiveOffset) && selection.lines.length === 0) {\n\t\tconst requested = effectiveRanges?.[0]?.start ?? effectiveOffset ?? 1;\n\t\treturn { content: [{ type: \"text\", text: `Requested line ${requested} is beyond end of resource (${textContent.split(\"\\n\").length} lines total).` }], details: undefined };\n\t}\n\tif (selectedText.length > maxChars || Buffer.byteLength(selectedText, \"utf8\") > maxBytes) {\n\t\treturn oversized({ blocked: true, path: effectivePath, chars: selectedText.length, maxChars, startLine: selection.firstLine, totalFileLines: textContent.split(\"\\n\").length, firstLineBytes: Buffer.byteLength(selection.lines[0] ?? \"\", \"utf8\"), byteGuidance: false });\n\t}\n\treturn { content: [{ type: \"text\", text: rawOutput ? selectedText : `URL: ${effectivePath}\\nStatus: ${page.status}\\nContent-Type: ${page.contentType || \"unknown\"}\\n\\n${selectedText}` }], details: sourceMeta(effectivePath) };\n}\n"]}
1
+ {"version":3,"file":"read-url.d.ts","sourceRoot":"","sources":["../../../src/core/tools/read-url.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAIjD,MAAM,WAAW,iBAAiB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,GAAG,SAAS,CAAC;IAC/D,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,KAAK;QAAE,OAAO,EAAE,WAAW,EAAE,CAAC;QAAC,OAAO,EAAE,eAAe,CAAA;KAAE,CAAC;IACzO,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,eAAe,CAAC;CAChD;AAED,MAAM,MAAM,mBAAmB,GAAG;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,OAAO,EAAE,eAAe,GAAG,SAAS,CAAA;CAAE,CAAC;AAEnG,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA6BzF"}