@bastani/atomic 0.9.9-alpha.1 → 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 (534) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +6 -0
  3. package/dist/builtin/cursor/package.json +3 -3
  4. package/dist/builtin/intercom/CHANGELOG.md +7 -0
  5. package/dist/builtin/intercom/package.json +3 -3
  6. package/dist/builtin/mcp/CHANGELOG.md +7 -0
  7. package/dist/builtin/mcp/package.json +4 -4
  8. package/dist/builtin/subagents/CHANGELOG.md +7 -0
  9. package/dist/builtin/subagents/package.json +5 -5
  10. package/dist/builtin/web-access/CHANGELOG.md +7 -0
  11. package/dist/builtin/web-access/package.json +3 -3
  12. package/dist/builtin/web-access/web-search-tool.ts +1 -2
  13. package/dist/builtin/workflows/CHANGELOG.md +7 -0
  14. package/dist/builtin/workflows/package.json +3 -3
  15. package/dist/builtin/workflows/src/shared/serializable.ts +1 -1
  16. package/dist/bun/cli.d.ts.map +1 -1
  17. package/dist/bun/internal-intercom-broker.d.ts.map +1 -1
  18. package/dist/bun/register-bedrock.d.ts.map +1 -1
  19. package/dist/bun/restore-sandbox-env.d.ts.map +1 -1
  20. package/dist/bun/split-loader.d.ts.map +1 -1
  21. package/dist/cli/args.d.ts.map +1 -1
  22. package/dist/cli/config-selector.d.ts.map +1 -1
  23. package/dist/cli/file-processor.d.ts.map +1 -1
  24. package/dist/cli/initial-message.d.ts.map +1 -1
  25. package/dist/cli/list-models.d.ts.map +1 -1
  26. package/dist/cli/project-trust.d.ts.map +1 -1
  27. package/dist/cli/session-picker.d.ts.map +1 -1
  28. package/dist/cli/startup-ui.d.ts.map +1 -1
  29. package/dist/cli.d.ts.map +1 -1
  30. package/dist/config-self-update.d.ts.map +1 -1
  31. package/dist/config.d.ts.map +1 -1
  32. package/dist/core/agent-session-accessors.d.ts.map +1 -1
  33. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  34. package/dist/core/agent-session-bash.d.ts.map +1 -1
  35. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  36. package/dist/core/agent-session-events.d.ts.map +1 -1
  37. package/dist/core/agent-session-export.d.ts.map +1 -1
  38. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  39. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  40. package/dist/core/agent-session-methods.d.ts.map +1 -1
  41. package/dist/core/agent-session-models.d.ts.map +1 -1
  42. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  43. package/dist/core/agent-session-retry.d.ts.map +1 -1
  44. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  45. package/dist/core/agent-session-services.d.ts.map +1 -1
  46. package/dist/core/agent-session-skill-block.d.ts.map +1 -1
  47. package/dist/core/agent-session-state.d.ts.map +1 -1
  48. package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
  49. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  50. package/dist/core/agent-session-tree.d.ts.map +1 -1
  51. package/dist/core/agent-session-types.d.ts.map +1 -1
  52. package/dist/core/agent-session.d.ts.map +1 -1
  53. package/dist/core/anthropic-thinking-guard.d.ts.map +1 -1
  54. package/dist/core/async/format.d.ts.map +1 -1
  55. package/dist/core/async/job-manager.d.ts.map +1 -1
  56. package/dist/core/async/session-manager.d.ts.map +1 -1
  57. package/dist/core/async/types.d.ts.map +1 -1
  58. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  59. package/dist/core/auth-guidance.d.ts.map +1 -1
  60. package/dist/core/auth-storage-backends.d.ts.map +1 -1
  61. package/dist/core/auth-storage.d.ts +4 -0
  62. package/dist/core/auth-storage.d.ts.map +1 -1
  63. package/dist/core/bash-executor.d.ts.map +1 -1
  64. package/dist/core/builtin-packages.d.ts.map +1 -1
  65. package/dist/core/codex-fast-mode.d.ts.map +1 -1
  66. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  67. package/dist/core/compaction/compaction-boundary.d.ts.map +1 -1
  68. package/dist/core/compaction/compaction-parameters.d.ts.map +1 -1
  69. package/dist/core/compaction/compaction-runner.d.ts.map +1 -1
  70. package/dist/core/compaction/compaction-types.d.ts.map +1 -1
  71. package/dist/core/compaction/compaction.d.ts.map +1 -1
  72. package/dist/core/compaction/deleted-ranges.d.ts.map +1 -1
  73. package/dist/core/compaction/index.d.ts.map +1 -1
  74. package/dist/core/compaction/range-planner.d.ts.map +1 -1
  75. package/dist/core/compaction/transcript-serialization.d.ts.map +1 -1
  76. package/dist/core/compaction/utils.d.ts.map +1 -1
  77. package/dist/core/context-window.d.ts.map +1 -1
  78. package/dist/core/copilot-anthropic-sse-repair.d.ts.map +1 -1
  79. package/dist/core/copilot-errors.d.ts.map +1 -1
  80. package/dist/core/copilot-gemini-payload-sanitizer.d.ts.map +1 -1
  81. package/dist/core/copilot-gemini-reasoning.d.ts.map +1 -1
  82. package/dist/core/copilot-gemini-tool-arguments.d.ts.map +1 -1
  83. package/dist/core/copilot-hosts.d.ts.map +1 -1
  84. package/dist/core/copilot-model-catalog.d.ts.map +1 -1
  85. package/dist/core/copilot-model-static-fallbacks.d.ts.map +1 -1
  86. package/dist/core/copilot-model-synthesis.d.ts.map +1 -1
  87. package/dist/core/defaults.d.ts.map +1 -1
  88. package/dist/core/diagnostics.d.ts.map +1 -1
  89. package/dist/core/event-bus.d.ts.map +1 -1
  90. package/dist/core/exec.d.ts.map +1 -1
  91. package/dist/core/experimental.d.ts.map +1 -1
  92. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
  93. package/dist/core/export-html/index.d.ts.map +1 -1
  94. package/dist/core/export-html/template-script.d.ts.map +1 -1
  95. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  96. package/dist/core/extensions/agent-events.d.ts.map +1 -1
  97. package/dist/core/extensions/api-types.d.ts.map +1 -1
  98. package/dist/core/extensions/command-types.d.ts.map +1 -1
  99. package/dist/core/extensions/context-types.d.ts.map +1 -1
  100. package/dist/core/extensions/event-results.d.ts.map +1 -1
  101. package/dist/core/extensions/event-types.d.ts.map +1 -1
  102. package/dist/core/extensions/index.d.ts.map +1 -1
  103. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  104. package/dist/core/extensions/loader-core.d.ts.map +1 -1
  105. package/dist/core/extensions/loader-discovery.d.ts.map +1 -1
  106. package/dist/core/extensions/loader-resources.d.ts.map +1 -1
  107. package/dist/core/extensions/loader-runtime.d.ts.map +1 -1
  108. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  109. package/dist/core/extensions/loader.d.ts.map +1 -1
  110. package/dist/core/extensions/message-types.d.ts.map +1 -1
  111. package/dist/core/extensions/provider-types.d.ts.map +1 -1
  112. package/dist/core/extensions/reactive-widget.d.ts.map +1 -1
  113. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  114. package/dist/core/extensions/runner-events.d.ts.map +1 -1
  115. package/dist/core/extensions/runner-handlers.d.ts.map +1 -1
  116. package/dist/core/extensions/runner-project-trust.d.ts.map +1 -1
  117. package/dist/core/extensions/runner-registries.d.ts.map +1 -1
  118. package/dist/core/extensions/runner-shortcuts.d.ts.map +1 -1
  119. package/dist/core/extensions/runner-ui.d.ts.map +1 -1
  120. package/dist/core/extensions/runner.d.ts.map +1 -1
  121. package/dist/core/extensions/runtime-types.d.ts.map +1 -1
  122. package/dist/core/extensions/session-events.d.ts.map +1 -1
  123. package/dist/core/extensions/tool-events.d.ts.map +1 -1
  124. package/dist/core/extensions/tool-types.d.ts.map +1 -1
  125. package/dist/core/extensions/types.d.ts.map +1 -1
  126. package/dist/core/extensions/ui-types.d.ts.map +1 -1
  127. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  128. package/dist/core/flattened-tool-arguments.d.ts.map +1 -1
  129. package/dist/core/footer-data-provider.d.ts.map +1 -1
  130. package/dist/core/http-dispatcher.d.ts.map +1 -1
  131. package/dist/core/index.d.ts.map +1 -1
  132. package/dist/core/keybindings.d.ts.map +1 -1
  133. package/dist/core/messages.d.ts.map +1 -1
  134. package/dist/core/model-registry-auth.d.ts.map +1 -1
  135. package/dist/core/model-registry-builtins.d.ts.map +1 -1
  136. package/dist/core/model-registry-builtins.js.map +1 -1
  137. package/dist/core/model-registry-custom-loader.d.ts.map +1 -1
  138. package/dist/core/model-registry-custom-loader.js.map +1 -1
  139. package/dist/core/model-registry-dynamic.d.ts.map +1 -1
  140. package/dist/core/model-registry-loader.d.ts.map +1 -1
  141. package/dist/core/model-registry-schemas.d.ts +65 -13
  142. package/dist/core/model-registry-schemas.d.ts.map +1 -1
  143. package/dist/core/model-registry-schemas.js +5 -1
  144. package/dist/core/model-registry-schemas.js.map +1 -1
  145. package/dist/core/model-registry-types.d.ts.map +1 -1
  146. package/dist/core/model-registry.d.ts.map +1 -1
  147. package/dist/core/model-resolver-cli.d.ts.map +1 -1
  148. package/dist/core/model-resolver-defaults.d.ts.map +1 -1
  149. package/dist/core/model-resolver-initial.d.ts.map +1 -1
  150. package/dist/core/model-resolver-patterns.d.ts.map +1 -1
  151. package/dist/core/model-resolver-scope.d.ts.map +1 -1
  152. package/dist/core/model-resolver-types.d.ts.map +1 -1
  153. package/dist/core/model-resolver.d.ts.map +1 -1
  154. package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
  155. package/dist/core/output-guard.d.ts.map +1 -1
  156. package/dist/core/package-manager-auto-resources.d.ts.map +1 -1
  157. package/dist/core/package-manager-command.d.ts.map +1 -1
  158. package/dist/core/package-manager-constants.d.ts.map +1 -1
  159. package/dist/core/package-manager-env.d.ts.map +1 -1
  160. package/dist/core/package-manager-git.d.ts.map +1 -1
  161. package/dist/core/package-manager-manifest.d.ts.map +1 -1
  162. package/dist/core/package-manager-npm.d.ts.map +1 -1
  163. package/dist/core/package-manager-operations.d.ts.map +1 -1
  164. package/dist/core/package-manager-paths.d.ts.map +1 -1
  165. package/dist/core/package-manager-progress.d.ts.map +1 -1
  166. package/dist/core/package-manager-resolver.d.ts.map +1 -1
  167. package/dist/core/package-manager-resource-accumulator.d.ts.map +1 -1
  168. package/dist/core/package-manager-resource-collector.d.ts.map +1 -1
  169. package/dist/core/package-manager-resource-files.d.ts.map +1 -1
  170. package/dist/core/package-manager-resource-patterns.d.ts.map +1 -1
  171. package/dist/core/package-manager-settings.d.ts.map +1 -1
  172. package/dist/core/package-manager-source.d.ts.map +1 -1
  173. package/dist/core/package-manager-types.d.ts.map +1 -1
  174. package/dist/core/package-manager.d.ts.map +1 -1
  175. package/dist/core/project-trust.d.ts.map +1 -1
  176. package/dist/core/prompt-templates-async.d.ts.map +1 -1
  177. package/dist/core/prompt-templates.d.ts.map +1 -1
  178. package/dist/core/provider-attribution.d.ts.map +1 -1
  179. package/dist/core/provider-context-usage.d.ts.map +1 -1
  180. package/dist/core/provider-display-names.d.ts.map +1 -1
  181. package/dist/core/resolve-config-value.d.ts.map +1 -1
  182. package/dist/core/resource-loader-assets.d.ts.map +1 -1
  183. package/dist/core/resource-loader-context-files.d.ts.map +1 -1
  184. package/dist/core/resource-loader-core.d.ts.map +1 -1
  185. package/dist/core/resource-loader-discovery.d.ts.map +1 -1
  186. package/dist/core/resource-loader-extensions.d.ts.map +1 -1
  187. package/dist/core/resource-loader-helpers.d.ts.map +1 -1
  188. package/dist/core/resource-loader-internals.d.ts.map +1 -1
  189. package/dist/core/resource-loader-package-resources.d.ts.map +1 -1
  190. package/dist/core/resource-loader-paths.d.ts.map +1 -1
  191. package/dist/core/resource-loader-reload.d.ts.map +1 -1
  192. package/dist/core/resource-loader-source-info.d.ts.map +1 -1
  193. package/dist/core/resource-loader-types.d.ts.map +1 -1
  194. package/dist/core/resource-loader.d.ts.map +1 -1
  195. package/dist/core/sdk-exports.d.ts.map +1 -1
  196. package/dist/core/sdk-types.d.ts.map +1 -1
  197. package/dist/core/sdk.d.ts.map +1 -1
  198. package/dist/core/session-cwd.d.ts.map +1 -1
  199. package/dist/core/session-entry-normalization.d.ts.map +1 -1
  200. package/dist/core/session-manager-archive.d.ts.map +1 -1
  201. package/dist/core/session-manager-classification.d.ts.map +1 -1
  202. package/dist/core/session-manager-core.d.ts.map +1 -1
  203. package/dist/core/session-manager-entries.d.ts.map +1 -1
  204. package/dist/core/session-manager-history.d.ts.map +1 -1
  205. package/dist/core/session-manager-list.d.ts.map +1 -1
  206. package/dist/core/session-manager-migrations.d.ts.map +1 -1
  207. package/dist/core/session-manager-paths.d.ts.map +1 -1
  208. package/dist/core/session-manager-storage.d.ts.map +1 -1
  209. package/dist/core/session-manager-types.d.ts.map +1 -1
  210. package/dist/core/session-manager-validation.d.ts.map +1 -1
  211. package/dist/core/session-manager.d.ts.map +1 -1
  212. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  213. package/dist/core/settings-manager-core.d.ts.map +1 -1
  214. package/dist/core/settings-manager-internals.d.ts.map +1 -1
  215. package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
  216. package/dist/core/settings-manager-ui-accessors.d.ts.map +1 -1
  217. package/dist/core/settings-manager.d.ts.map +1 -1
  218. package/dist/core/settings-merge.d.ts.map +1 -1
  219. package/dist/core/settings-storage.d.ts.map +1 -1
  220. package/dist/core/settings-types.d.ts.map +1 -1
  221. package/dist/core/skills-async.d.ts.map +1 -1
  222. package/dist/core/skills.d.ts.map +1 -1
  223. package/dist/core/slash-commands.d.ts.map +1 -1
  224. package/dist/core/source-info.d.ts.map +1 -1
  225. package/dist/core/system-prompt.d.ts.map +1 -1
  226. package/dist/core/telemetry.d.ts.map +1 -1
  227. package/dist/core/thinking-blocks.d.ts.map +1 -1
  228. package/dist/core/timings.d.ts.map +1 -1
  229. package/dist/core/tools/artifact-protocol.d.ts.map +1 -1
  230. package/dist/core/tools/artifacts.d.ts.map +1 -1
  231. package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
  232. package/dist/core/tools/ask-user-question/config.d.ts.map +1 -1
  233. package/dist/core/tools/ask-user-question/index.d.ts.map +1 -1
  234. package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts.map +1 -1
  235. package/dist/core/tools/ask-user-question/state/inline-input.d.ts.map +1 -1
  236. package/dist/core/tools/ask-user-question/state/key-router.d.ts.map +1 -1
  237. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
  238. package/dist/core/tools/ask-user-question/state/row-intent.d.ts.map +1 -1
  239. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
  240. package/dist/core/tools/ask-user-question/state/selectors/derivations.d.ts.map +1 -1
  241. package/dist/core/tools/ask-user-question/state/selectors/focus.d.ts.map +1 -1
  242. package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
  243. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
  244. package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
  245. package/dist/core/tools/ask-user-question/tool/format-answer.d.ts.map +1 -1
  246. package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts.map +1 -1
  247. package/dist/core/tools/ask-user-question/tool/types.d.ts.map +1 -1
  248. package/dist/core/tools/ask-user-question/tool/validate-questionnaire.d.ts.map +1 -1
  249. package/dist/core/tools/ask-user-question/view/body-residual-spacer.d.ts.map +1 -1
  250. package/dist/core/tools/ask-user-question/view/component-binding.d.ts.map +1 -1
  251. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts.map +1 -1
  252. package/dist/core/tools/ask-user-question/view/components/multi-select-view.d.ts.map +1 -1
  253. package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts.map +1 -1
  254. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.d.ts.map +1 -1
  255. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts.map +1 -1
  256. package/dist/core/tools/ask-user-question/view/components/preview/preview-box-renderer.d.ts.map +1 -1
  257. package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.d.ts.map +1 -1
  258. package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.d.ts.map +1 -1
  259. package/dist/core/tools/ask-user-question/view/components/submit-picker.d.ts.map +1 -1
  260. package/dist/core/tools/ask-user-question/view/components/tab-bar.d.ts.map +1 -1
  261. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
  262. package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts.map +1 -1
  263. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
  264. package/dist/core/tools/ask-user-question/view/stateful-view.d.ts.map +1 -1
  265. package/dist/core/tools/ask-user-question/view/tab-components.d.ts.map +1 -1
  266. package/dist/core/tools/ask-user-question/view/tab-content-strategy.d.ts.map +1 -1
  267. package/dist/core/tools/bash-async-execution.d.ts.map +1 -1
  268. package/dist/core/tools/bash-async-jobs.d.ts.map +1 -1
  269. package/dist/core/tools/bash-async-output.d.ts.map +1 -1
  270. package/dist/core/tools/bash-interceptor.d.ts.map +1 -1
  271. package/dist/core/tools/bash-leading-cd.d.ts.map +1 -1
  272. package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
  273. package/dist/core/tools/bash.d.ts.map +1 -1
  274. package/dist/core/tools/block-resolver.d.ts.map +1 -1
  275. package/dist/core/tools/conflict-registry.d.ts.map +1 -1
  276. package/dist/core/tools/directory-tree.d.ts.map +1 -1
  277. package/dist/core/tools/edit-diff-preserve.d.ts.map +1 -1
  278. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  279. package/dist/core/tools/edit.d.ts.map +1 -1
  280. package/dist/core/tools/fetch-url.d.ts.map +1 -1
  281. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  282. package/dist/core/tools/find.d.ts.map +1 -1
  283. package/dist/core/tools/glob-path-utils.d.ts.map +1 -1
  284. package/dist/core/tools/grep.d.ts.map +1 -1
  285. package/dist/core/tools/hashline-engine/apply.d.ts.map +1 -1
  286. package/dist/core/tools/hashline-engine/block.d.ts.map +1 -1
  287. package/dist/core/tools/hashline-engine/diff-preview.d.ts.map +1 -1
  288. package/dist/core/tools/hashline-engine/format.d.ts.map +1 -1
  289. package/dist/core/tools/hashline-engine/fs.d.ts.map +1 -1
  290. package/dist/core/tools/hashline-engine/index.d.ts.map +1 -1
  291. package/dist/core/tools/hashline-engine/input.d.ts.map +1 -1
  292. package/dist/core/tools/hashline-engine/messages.d.ts.map +1 -1
  293. package/dist/core/tools/hashline-engine/mismatch.d.ts.map +1 -1
  294. package/dist/core/tools/hashline-engine/normalize.d.ts.map +1 -1
  295. package/dist/core/tools/hashline-engine/parser.d.ts.map +1 -1
  296. package/dist/core/tools/hashline-engine/patcher.d.ts.map +1 -1
  297. package/dist/core/tools/hashline-engine/prefixes.d.ts.map +1 -1
  298. package/dist/core/tools/hashline-engine/recovery.d.ts.map +1 -1
  299. package/dist/core/tools/hashline-engine/snapshots.d.ts.map +1 -1
  300. package/dist/core/tools/hashline-engine/stream.d.ts.map +1 -1
  301. package/dist/core/tools/hashline-engine/tokenizer.d.ts.map +1 -1
  302. package/dist/core/tools/hashline-engine/types.d.ts.map +1 -1
  303. package/dist/core/tools/hashline.d.ts.map +1 -1
  304. package/dist/core/tools/index.d.ts.map +1 -1
  305. package/dist/core/tools/ls.d.ts.map +1 -1
  306. package/dist/core/tools/notebook.d.ts.map +1 -1
  307. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  308. package/dist/core/tools/oversized-tool-result.d.ts.map +1 -1
  309. package/dist/core/tools/path-utils.d.ts.map +1 -1
  310. package/dist/core/tools/read-document-extract.d.ts.map +1 -1
  311. package/dist/core/tools/read-selectors.d.ts.map +1 -1
  312. package/dist/core/tools/read-url.d.ts.map +1 -1
  313. package/dist/core/tools/read.d.ts.map +1 -1
  314. package/dist/core/tools/render-utils.d.ts.map +1 -1
  315. package/dist/core/tools/resource-selectors.d.ts.map +1 -1
  316. package/dist/core/tools/search-details.d.ts.map +1 -1
  317. package/dist/core/tools/search-line-ranges.d.ts.map +1 -1
  318. package/dist/core/tools/search-native.d.ts.map +1 -1
  319. package/dist/core/tools/search.d.ts.map +1 -1
  320. package/dist/core/tools/structured-output.d.ts.map +1 -1
  321. package/dist/core/tools/todos-execute.d.ts.map +1 -1
  322. package/dist/core/tools/todos-locks.d.ts.map +1 -1
  323. package/dist/core/tools/todos-model.d.ts.map +1 -1
  324. package/dist/core/tools/todos-mutations.d.ts.map +1 -1
  325. package/dist/core/tools/todos-paths.d.ts.map +1 -1
  326. package/dist/core/tools/todos-render.d.ts.map +1 -1
  327. package/dist/core/tools/todos-storage.d.ts.map +1 -1
  328. package/dist/core/tools/todos-types.d.ts.map +1 -1
  329. package/dist/core/tools/todos-types.js +1 -1
  330. package/dist/core/tools/todos-types.js.map +1 -1
  331. package/dist/core/tools/todos.d.ts.map +1 -1
  332. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  333. package/dist/core/tools/tool-definition-wrapper.js +6 -2
  334. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  335. package/dist/core/tools/tool-limits.d.ts.map +1 -1
  336. package/dist/core/tools/truncate.d.ts.map +1 -1
  337. package/dist/core/tools/url-ip-guards.d.ts.map +1 -1
  338. package/dist/core/tools/write.d.ts.map +1 -1
  339. package/dist/core/trust-manager.d.ts.map +1 -1
  340. package/dist/core/typebox-compat.d.ts +12 -0
  341. package/dist/core/typebox-compat.d.ts.map +1 -0
  342. package/dist/core/typebox-compat.js +10 -0
  343. package/dist/core/typebox-compat.js.map +1 -0
  344. package/dist/index-extensions.d.ts.map +1 -1
  345. package/dist/index.d.ts +1 -0
  346. package/dist/index.d.ts.map +1 -1
  347. package/dist/index.js +1 -0
  348. package/dist/index.js.map +1 -1
  349. package/dist/main-app-mode.d.ts.map +1 -1
  350. package/dist/main-deferred-startup.d.ts.map +1 -1
  351. package/dist/main-early-input.d.ts.map +1 -1
  352. package/dist/main-session-options.d.ts.map +1 -1
  353. package/dist/main-session.d.ts.map +1 -1
  354. package/dist/main-stdio.d.ts.map +1 -1
  355. package/dist/main.d.ts.map +1 -1
  356. package/dist/migrations-config-values.d.ts.map +1 -1
  357. package/dist/migrations.d.ts.map +1 -1
  358. package/dist/modes/index.d.ts.map +1 -1
  359. package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -1
  360. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  361. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  362. package/dist/modes/interactive/components/atomic-banner.d.ts.map +1 -1
  363. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  364. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  365. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  366. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  367. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  368. package/dist/modes/interactive/components/chat-session-host-editor.d.ts.map +1 -1
  369. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  370. package/dist/modes/interactive/components/chat-session-host-rendering.d.ts.map +1 -1
  371. package/dist/modes/interactive/components/chat-session-host-runtime.d.ts.map +1 -1
  372. package/dist/modes/interactive/components/chat-session-host-state.d.ts.map +1 -1
  373. package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.d.ts.map +1 -1
  374. package/dist/modes/interactive/components/chat-session-host-types.d.ts.map +1 -1
  375. package/dist/modes/interactive/components/chat-session-host-utils.d.ts.map +1 -1
  376. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  377. package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -1
  378. package/dist/modes/interactive/components/compaction-boundary-message.d.ts.map +1 -1
  379. package/dist/modes/interactive/components/config-selector-list.d.ts.map +1 -1
  380. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  381. package/dist/modes/interactive/components/context-window-selector.d.ts.map +1 -1
  382. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  383. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  384. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  385. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  386. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  387. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  388. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
  389. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  390. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  391. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  392. package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +1 -1
  393. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  394. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  395. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  396. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  397. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  398. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  399. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  400. package/dist/modes/interactive/components/session-selector-delete.d.ts.map +1 -1
  401. package/dist/modes/interactive/components/session-selector-header.d.ts.map +1 -1
  402. package/dist/modes/interactive/components/session-selector-list.d.ts.map +1 -1
  403. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  404. package/dist/modes/interactive/components/session-selector-tree.d.ts.map +1 -1
  405. package/dist/modes/interactive/components/session-selector-types.d.ts.map +1 -1
  406. package/dist/modes/interactive/components/session-selector-utils.d.ts.map +1 -1
  407. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  408. package/dist/modes/interactive/components/settings-selector-handlers.d.ts.map +1 -1
  409. package/dist/modes/interactive/components/settings-selector-items.d.ts.map +1 -1
  410. package/dist/modes/interactive/components/settings-selector-options.d.ts.map +1 -1
  411. package/dist/modes/interactive/components/settings-selector-submenus.d.ts.map +1 -1
  412. package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
  413. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  414. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  415. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  416. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  417. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  418. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  419. package/dist/modes/interactive/components/tree-selector-component.d.ts.map +1 -1
  420. package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
  421. package/dist/modes/interactive/components/tree-selector-help.d.ts.map +1 -1
  422. package/dist/modes/interactive/components/tree-selector-label-input.d.ts.map +1 -1
  423. package/dist/modes/interactive/components/tree-selector-list.d.ts.map +1 -1
  424. package/dist/modes/interactive/components/tree-selector-model.d.ts.map +1 -1
  425. package/dist/modes/interactive/components/tree-selector-types.d.ts.map +1 -1
  426. package/dist/modes/interactive/components/tree-selector-viewport.d.ts.map +1 -1
  427. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  428. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  429. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  430. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  431. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  432. package/dist/modes/interactive/components/working-status.d.ts.map +1 -1
  433. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  434. package/dist/modes/interactive/interactive-auth-login.d.ts.map +1 -1
  435. package/dist/modes/interactive/interactive-auth-routing.d.ts.map +1 -1
  436. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  437. package/dist/modes/interactive/interactive-bash-compact.d.ts.map +1 -1
  438. package/dist/modes/interactive/interactive-child-ordering.d.ts.map +1 -1
  439. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  440. package/dist/modes/interactive/interactive-editor-actions.d.ts.map +1 -1
  441. package/dist/modes/interactive/interactive-extension-context.d.ts.map +1 -1
  442. package/dist/modes/interactive/interactive-extension-custom-ui.d.ts.map +1 -1
  443. package/dist/modes/interactive/interactive-extension-dialogs.d.ts.map +1 -1
  444. package/dist/modes/interactive/interactive-extension-runtime.d.ts.map +1 -1
  445. package/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  446. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  447. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  448. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  449. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  450. package/dist/modes/interactive/interactive-mode-helpers.d.ts.map +1 -1
  451. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  452. package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
  453. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  454. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  455. package/dist/modes/interactive/interactive-onboarding.d.ts.map +1 -1
  456. package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
  457. package/dist/modes/interactive/interactive-queueing.d.ts.map +1 -1
  458. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  459. package/dist/modes/interactive/interactive-resource-disclosure.d.ts.map +1 -1
  460. package/dist/modes/interactive/interactive-resource-paths.d.ts.map +1 -1
  461. package/dist/modes/interactive/interactive-resource-rendering.d.ts.map +1 -1
  462. package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
  463. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  464. package/dist/modes/interactive/interactive-session-runtime.d.ts.map +1 -1
  465. package/dist/modes/interactive/interactive-slash-commands.d.ts.map +1 -1
  466. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  467. package/dist/modes/interactive/model-search.d.ts.map +1 -1
  468. package/dist/modes/interactive/theme/color-utils.d.ts.map +1 -1
  469. package/dist/modes/interactive/theme/export-colors.d.ts.map +1 -1
  470. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  471. package/dist/modes/interactive/theme/terminal-detection.d.ts.map +1 -1
  472. package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
  473. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  474. package/dist/modes/interactive/theme/theme-loading.d.ts.map +1 -1
  475. package/dist/modes/interactive/theme/theme-parse.d.ts.map +1 -1
  476. package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  477. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  478. package/dist/modes/interactive/theme/tui-theme.d.ts.map +1 -1
  479. package/dist/modes/interactive/whimsical-messages.d.ts.map +1 -1
  480. package/dist/modes/print-mode.d.ts.map +1 -1
  481. package/dist/modes/rpc/jsonl.d.ts.map +1 -1
  482. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  483. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  484. package/dist/modes/rpc/rpc-extension-ui.d.ts.map +1 -1
  485. package/dist/modes/rpc/rpc-input.d.ts.map +1 -1
  486. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  487. package/dist/modes/rpc/rpc-responses.d.ts.map +1 -1
  488. package/dist/modes/rpc/rpc-session-binding.d.ts.map +1 -1
  489. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  490. package/dist/package-manager-cli-parser.d.ts.map +1 -1
  491. package/dist/package-manager-cli.d.ts.map +1 -1
  492. package/dist/rpc-entry.d.ts.map +1 -1
  493. package/dist/utils/ansi.d.ts.map +1 -1
  494. package/dist/utils/changelog.d.ts.map +1 -1
  495. package/dist/utils/child-process.d.ts.map +1 -1
  496. package/dist/utils/clipboard-image.d.ts.map +1 -1
  497. package/dist/utils/clipboard-native.d.ts.map +1 -1
  498. package/dist/utils/clipboard.d.ts.map +1 -1
  499. package/dist/utils/deprecation.d.ts.map +1 -1
  500. package/dist/utils/event-loop.d.ts.map +1 -1
  501. package/dist/utils/exif-orientation.d.ts.map +1 -1
  502. package/dist/utils/frontmatter.d.ts.map +1 -1
  503. package/dist/utils/fs-watch.d.ts.map +1 -1
  504. package/dist/utils/git-env.d.ts.map +1 -1
  505. package/dist/utils/git.d.ts.map +1 -1
  506. package/dist/utils/html.d.ts.map +1 -1
  507. package/dist/utils/image-convert.d.ts.map +1 -1
  508. package/dist/utils/image-process.d.ts.map +1 -1
  509. package/dist/utils/image-resize-core.d.ts.map +1 -1
  510. package/dist/utils/image-resize-worker.d.ts.map +1 -1
  511. package/dist/utils/image-resize.d.ts.map +1 -1
  512. package/dist/utils/json.d.ts.map +1 -1
  513. package/dist/utils/markit.d.ts.map +1 -1
  514. package/dist/utils/mime.d.ts.map +1 -1
  515. package/dist/utils/module-require.d.ts.map +1 -1
  516. package/dist/utils/open-browser.d.ts.map +1 -1
  517. package/dist/utils/paths.d.ts.map +1 -1
  518. package/dist/utils/photon.d.ts.map +1 -1
  519. package/dist/utils/pi-user-agent.d.ts.map +1 -1
  520. package/dist/utils/shell.d.ts.map +1 -1
  521. package/dist/utils/sleep.d.ts.map +1 -1
  522. package/dist/utils/split-launcher.d.ts.map +1 -1
  523. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  524. package/dist/utils/tools-manager.d.ts.map +1 -1
  525. package/dist/utils/version-check.d.ts.map +1 -1
  526. package/dist/utils/windows-self-update.d.ts.map +1 -1
  527. package/docs/custom-provider.md +7 -1
  528. package/docs/extensions.md +6 -4
  529. package/examples/extensions/README.md +3 -1
  530. package/examples/extensions/subagent/schemas.ts +1 -1
  531. package/examples/extensions/tic-tac-toe.ts +1 -2
  532. package/examples/extensions/todo.ts +1 -2
  533. package/npm-shrinkwrap.json +214 -98
  534. package/package.json +12 -12
@@ -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"}
@@ -1 +1 @@
1
- {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAK/D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAa5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAA8E,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAMvI,OAAO,EAAoD,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AACtH,QAAA,MAAM,UAAU;;EAEmB,CAAC;AACpC,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;CACtB;AACD,MAAM,WAAW,eAAe;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,aAAa,CAAC,EAAE,oBAAoB,CAAC;IACrC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;CACrI;AAMD,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,mBAAmB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CACnF;AAMD,MAAM,WAAW,eAAe;IAC/B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,aAAa,CAAC,EAAE,qBAAqB,CAAC;CACtC;AAwJD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CAsP/I;AACD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG","sourcesContent":["import { basename, dirname, isAbsolute, relative, resolve as resolvePath, sep } from \"node:path\";\nimport type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { Api, ImageContent, Model, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport { Text } from \"@earendil-works/pi-tui\";\nimport { constants } from \"fs\";\nimport { access as fsAccess, readFile as fsReadFile, stat as fsStat } from \"fs/promises\";\nimport { type Static, Type } from \"typebox\";\nimport { getReadmePath } from \"../../config.ts\";\nimport { keyHint, keyText } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { parseConflictBlocks, registerConflictBlocks } from \"./conflict-registry.ts\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { processImage } from \"../../utils/image-process.ts\";\nimport { detectSupportedImageMimeTypeFromFile } from \"../../utils/mime.ts\";\nimport { formatPathRelativeToCwdOrAbsolute } from \"../../utils/paths.ts\";\nimport { buildDirectoryTree } from \"./directory-tree.ts\";\nimport { applyReadLineSelection, extractDocumentMarkdown, isDocumentPath } from \"./read-document-extract.ts\";\nimport { isReadableUrlPath } from \"./fetch-url.ts\";\nimport { readUrlBranch } from \"./read-url.ts\";\nimport { isNotebookPath, readEditableNotebookText } from \"./notebook.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { createHashlineSnapshotStore, formatHashlineContent, recordHashlineSnapshot, type HashlineSnapshotStore } from \"./hashline.ts\";\nimport { resolveReadPathAsync, resolveToCwd } from \"./path-utils.ts\";\nimport { getTextOutput, invalidArgText, replaceTabs, shortenPath, str } from \"./render-utils.ts\";\nimport { formatHashlineSelectedLines, isReadResourceSelector, selectExactReadRanges, selectReadRanges, splitReadLineSelector, type ReadLineSelector } from \"./read-selectors.ts\";\nimport { parseArchiveSelector, readArchiveSelector, readInternalSelector, readSqliteSelector, resolveArchiveSelector, resolveInternalSelector, sqliteSelectorForPath, type InternalResourceContext } from \"./resource-selectors.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateHead } from \"./truncate.ts\";\nconst readSchema = Type.Object({\n\tpath: Type.String({ description: \"File, directory, archive member, SQLite selector, internal resource, image, document, or URL to read. Append selectors such as :raw, :conflicts, :N, :A-B, :A+C, or :A-B,C-D to scope output.\" }),\n}, { additionalProperties: false });\nexport type ReadToolInput = Static<typeof readSchema>;\nconst READ_TOOL_MAX_RESULT_CHARS = 50_000;\nexport interface OversizedReadDetails {\n\tblocked: true;\n\tpath: string;\n\tchars: number;\n\tmaxChars: number;\n\tstartLine: number;\n\trequestedLimit?: number;\n\ttotalFileLines: number;\n\tfirstLineBytes: number;\n\tbyteGuidance: boolean;\n}\nexport interface ReadToolDetails {\n\tisDirectory?: boolean;\n\tresolvedPath?: string;\n\ttruncation?: TruncationResult;\n\toversizedRead?: OversizedReadDetails;\n\tmeta?: { source?: string; sourcePath?: string; artifactId?: string; truncation?: TruncationResult; limits?: Record<string, number> };\n}\ninterface CompactReadClassification {\n\tkind: \"docs\" | \"resource\" | \"skill\";\n\tlabel: string;\n}\nconst COMPACT_RESOURCE_FILE_NAMES = new Set([\"AGENTS.md\", \"AGENTS.MD\", \"CLAUDE.md\", \"CLAUDE.MD\"]);\nexport interface ReadOperations {\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\taccess: (absolutePath: string) => Promise<void>;\n\tdetectImageMimeType?: (absolutePath: string) => Promise<string | null | undefined>;\n}\nconst defaultReadOperations: ReadOperations = {\n\treadFile: (path) => fsReadFile(path),\n\taccess: (path) => fsAccess(path, constants.R_OK),\n\tdetectImageMimeType: detectSupportedImageMimeTypeFromFile,\n};\nexport interface ReadToolOptions {\n\tautoResizeImages?: boolean;\n\toperations?: ReadOperations;\n\thashlineStore?: HashlineSnapshotStore;\n}\ntype ReadRenderArgs = { path?: string };\nfunction formatReadLineRange(_args: ReadRenderArgs | undefined, _theme: Theme): string {\n\treturn \"\";\n}\nfunction formatReadCall(args: ReadRenderArgs | undefined, theme: Theme): string {\n\tconst rawPath = str(args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst invalidArg = invalidArgText(theme);\n\tconst pathDisplay = path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\");\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"read\"))} ${pathDisplay}${formatReadLineRange(args, theme)}`;\n}\nfunction trimTrailingEmptyLines(lines: string[]): string[] { let end = lines.length; while (end > 0 && lines[end - 1] === \"\") end--; return lines.slice(0, end); }\nfunction getNonVisionImageNote(model: Model<Api> | undefined): string | undefined { return !model || model.input.includes(\"image\") ? undefined : \"[Current model does not support images. The image will be omitted from this request.]\"; }\nfunction toPosixPath(filePath: string): string { return filePath.split(sep).join(\"/\"); }\nfunction formatCount(count: number): string { return count.toLocaleString(\"en-US\"); }\nfunction shellQuote(value: string): string {\n\treturn `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\nfunction buildOversizedReadMessage(details: OversizedReadDetails): string {\n\tconst pathForExample = JSON.stringify(details.path);\n\tconst rangePathForExample = JSON.stringify(`${details.path}:${details.startLine}+200`);\n\tconst shellPathForExample = shellQuote(details.path);\n\tconst requestedLimitLine =\n\t\tdetails.requestedLimit !== undefined ? [`Requested line limit: ${formatCount(details.requestedLimit)}`] : [];\n\tif (details.byteGuidance) {\n\t\treturn [\n\t\t\t`File read blocked: requested selected range is too large (${formatCount(details.chars)} chars; threshold: ${formatCount(details.maxChars)} chars).`,\n\t\t\t`Path: ${details.path}`,\n\t\t\t...requestedLimitLine,\n\t\t\t\"\",\n\t\t\t\"The selected content starts with a single oversized line, so line pagination is not useful. Read byte slices instead. Examples:\",\n\t\t\t`- Inspect the start of line ${details.startLine}: sed -n '${details.startLine}p' ${shellPathForExample} | head -c ${DEFAULT_MAX_BYTES}`,\n\t\t\t`- Inspect a later byte window: sed -n '${details.startLine}p' ${shellPathForExample} | tail -c +${DEFAULT_MAX_BYTES + 1} | head -c ${DEFAULT_MAX_BYTES}`,\n\t\t\t`- Search for relevant text first: search({ \"pattern\": \"functionName\", \"paths\": ${pathForExample} })`,\n\t\t].join(\"\\n\");\n\t}\n\tconst targetedSnippetOffset = Math.max(details.startLine, 120);\n\tconst snippetPathForExample = JSON.stringify(`${details.path}:${targetedSnippetOffset}+80`);\n\treturn [\n\t\t`File read blocked: requested selected range is too large (${formatCount(details.chars)} chars; threshold: ${formatCount(details.maxChars)} chars).`,\n\t\t`Path: ${details.path}`,\n\t\t...requestedLimitLine,\n\t\t\"\",\n\t\t\"Read only the needed context incrementally. Examples:\",\n\t\t`- Search for relevant symbols first: search({ \"pattern\": \"functionName\", \"paths\": ${pathForExample} })`,\n\t\t`- Read a smaller line range: read({ \"path\": ${rangePathForExample} })`,\n\t\t`- Read a targeted snippet around a match: read({ \"path\": ${snippetPathForExample} })`,\n\t].join(\"\\n\");\n}\nfunction readSourceMeta(source: string): ReadToolDetails { return { meta: { source, sourcePath: source } }; }\nfunction oversizedReadResult(details: OversizedReadDetails): { content: TextContent[]; details: ReadToolDetails } { return { content: [{ type: \"text\", text: buildOversizedReadMessage(details) }], details: { oversizedRead: details, meta: { source: details.path, sourcePath: details.path, limits: { maxChars: details.maxChars }, } } }; }\nfunction getPiDocsClassification(absolutePath: string): CompactReadClassification | undefined {\n\tconst packageRoot = dirname(getReadmePath());\n\tconst relativePath = relative(resolvePath(packageRoot), resolvePath(absolutePath));\n\tif (\n\t\trelativePath === \"\" ||\n\t\trelativePath === \"..\" ||\n\t\trelativePath.startsWith(`..${sep}`) ||\n\t\tisAbsolute(relativePath)\n\t) {\n\t\treturn undefined;\n\t}\n\tconst label = toPosixPath(relativePath);\n\tif (label === \"README.md\" || label.startsWith(\"docs/\") || label.startsWith(\"examples/\")) {\n\t\treturn { kind: \"docs\", label };\n\t}\n\treturn undefined;\n}\nfunction getCompactReadClassification(\n\targs: ReadRenderArgs | undefined,\n\tcwd: string,\n): CompactReadClassification | undefined {\n\tconst rawPath = str(args?.path);\n\tif (!rawPath) return undefined;\n\tconst absolutePath = resolveToCwd(rawPath, cwd);\n\tconst fileName = basename(absolutePath);\n\tif (fileName === \"SKILL.md\") {\n\t\treturn { kind: \"skill\", label: basename(dirname(absolutePath)) || fileName };\n\t}\n\tconst docsClassification = getPiDocsClassification(absolutePath);\n\tif (docsClassification) return docsClassification;\n\tif (COMPACT_RESOURCE_FILE_NAMES.has(fileName)) {\n\t\treturn { kind: \"resource\", label: formatPathRelativeToCwdOrAbsolute(absolutePath, cwd) };\n\t}\n\treturn undefined;\n}\nfunction formatCompactReadCall(\n\tclassification: CompactReadClassification,\n\targs: ReadRenderArgs | undefined,\n\ttheme: Theme,\n): string {\n\tconst expandHint = theme.fg(\"dim\", ` (${keyText(\"app.tools.expand\")} Expand)`);\n\tif (classification.kind === \"skill\") {\n\t\treturn (\n\t\t\ttheme.fg(\"customMessageLabel\", `\\x1b[1m[skill]\\x1b[22m `) +\n\t\t\ttheme.fg(\"customMessageText\", classification.label) +\n\t\t\tformatReadLineRange(args, theme) +\n\t\t\texpandHint\n\t\t);\n\t}\n\treturn (\n\t\ttheme.fg(\"toolTitle\", theme.bold(`read ${classification.kind}`)) +\n\t\t\" \" +\n\t\ttheme.fg(\"accent\", classification.label) +\n\t\tformatReadLineRange(args, theme) +\n\t\texpandHint\n\t);\n}\nfunction formatReadResult(\n\targs: ReadRenderArgs | undefined,\n\tresult: { content: (TextContent | ImageContent)[]; details?: ReadToolDetails },\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tshowImages: boolean,\n\t_cwd: string,\n\tisError: boolean,\n): string {\n\tconst oversizedRead = result.details?.oversizedRead;\n\tconst oversizedReadBlocked = oversizedRead?.blocked === true;\n\tif (!options.expanded && !isError && !oversizedReadBlocked) {\n\t\treturn \"\";\n\t}\n\tconst rawPath = str(args?.path);\n\tconst output = oversizedRead ? buildOversizedReadMessage(oversizedRead) : getTextOutput(result, showImages);\n\tconst lang = rawPath && !oversizedReadBlocked ? getLanguageFromPath(rawPath) : undefined;\n\tconst renderedLines = lang ? highlightCode(replaceTabs(output), lang) : output.split(\"\\n\");\n\tconst lines = trimTrailingEmptyLines(renderedLines);\n\tconst maxLines = options.expanded ? lines.length : 10;\n\tconst displayLines = lines.slice(0, maxLines);\n\tconst remaining = lines.length - maxLines;\n\tlet text = `\\n${displayLines.map((line) => (lang ? replaceTabs(line) : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\tif (remaining > 0) {\n\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines,`)} ${keyHint(\"app.tools.expand\", \"Expand\")}${theme.fg(\"muted\", \")\")}`;\n\t}\n\tconst truncation = result.details?.truncation;\n\tif (truncation?.truncated) {\n\t\tif (truncation.firstLineExceedsLimit) {\n\t\t\ttext += `\\n${theme.fg(\"warning\", `[First line exceeds ${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit]`)}`;\n\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${truncation.maxLines ?? DEFAULT_MAX_LINES} line limit)]`)}`;\n\t\t} else {\n\t\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)]`)}`;\n\t\t}\n\t}\n\treturn text;\n}\nfunction archiveSelectorMemberExists(pathValue: string, cwd: string): boolean { const archive = parseArchiveSelector(pathValue); if (!archive || !archive.memberPath) return false; try { readArchiveSelector(resolveArchiveSelector(archive, cwd)); return true; } catch { return false; } }\nfunction appendReadSelectors(pathValue: string, selector: ReadLineSelector): string {\n\tconst range = selector.ranges?.map((item) => item.end === undefined ? `${item.start}` : `${item.start}-${item.end}`).join(\",\") ?? (selector.offset ? `${selector.offset}` : \"\");\n\treturn `${pathValue}${range ? `:${range}` : \"\"}${selector.conflicts ? \":conflicts\" : \"\"}${selector.raw ? \":raw\" : \"\"}`;\n}\nexport function createReadToolDefinition(cwd: string, options?: ReadToolOptions): ToolDefinition<typeof readSchema, ReadToolDetails | undefined> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tconst ops = options?.operations ?? defaultReadOperations;\n\tconst hashlineStore = options?.hashlineStore ?? createHashlineSnapshotStore();\n\treturn {\n\t\tname: \"read\",\n\t\tlabel: \"read\",\n\t\tdescription: \"Read files, directories, archives, SQLite databases, internal resources, images, documents, and URLs through one path string.\",\n\t\tpromptSnippet: \"Read a path selector.\",\n\t\tpromptGuidelines: [\"Use read to inspect file and resource contents; use path selectors for line ranges, raw output, and conflict views.\"],\n\t\tparameters: readSchema,\n\t\tmaxResultSizeChars: Infinity,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ path }: ReadToolInput,\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\tctx?,\n\t\t) {\n\t\t\tconst resourceCtx = ctx as InternalResourceContext | undefined;\n\t\t\tconst splitSelector = splitReadLineSelector(path), markerless = path.replace(/:raw(?=(:|$))/g, \"\").replace(/:conflicts(?=(:|$))/g, \"\"), sqliteOriginal = sqliteSelectorForPath(markerless, cwd), sqliteDirect = sqliteSelectorForPath(path, cwd);\n\t\t\tconst selector = archiveSelectorMemberExists(path, cwd) ? { path } : sqliteDirect && (sqliteDirect.table === \"raw\" || sqliteDirect.table === \"conflicts\") ? { path } : sqliteOriginal?.rowId && splitSelector.path !== markerless ? { path: markerless, raw: splitSelector.raw, conflicts: splitSelector.conflicts } : sqliteOriginal && splitSelector.path === markerless && markerless !== path ? { path: markerless, raw: splitSelector.raw, conflicts: splitSelector.conflicts } : splitSelector.path === path && sqliteDirect ? { path } : splitSelector;\n\t\t\tconst effectivePath = selector.path;\n\t\t\tconst effectiveOffset = selector.offset;\n\t\t\tconst effectiveLimit = selector.limit;\n\t\t\tconst effectiveRanges = selector.ranges;\n\t\t\tconst rawOutput = selector.raw;\n\t\t\tconst conflictsOnly = selector.conflicts;\n\t\t\treturn new Promise<{ content: (TextContent | ImageContent)[]; details: ReadToolDetails | undefined }>(\n\t\t\t\t(resolve, reject) => {\n\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tlet aborted = false;\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\taborted = true;\n\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t};\n\t\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t\t(async () => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst archive = parseArchiveSelector(effectivePath);\n\t\t\t\t\t\t\tconst sqlite = sqliteSelectorForPath(effectivePath, cwd);\n\t\t\t\t\t\t\tif (isReadableUrlPath(effectivePath)) {\n\t\t\t\t\t\t\t\tresolve(await readUrlBranch({ effectivePath, rawOutput: rawOutput === true, effectiveRanges, effectiveOffset, effectiveLimit, cwd, ctx, signal, maxChars: READ_TOOL_MAX_RESULT_CHARS, maxBytes: DEFAULT_MAX_BYTES, oversized: oversizedReadResult, sourceMeta: readSourceMeta }));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (sqlite) {\n\t\t\t\t\t\t\t\tconst textContent = readSqliteSelector(sqlite), selection = applyReadLineSelection(textContent.split(\"\\n\"), effectiveRanges, effectiveOffset, effectiveLimit, rawOutput), selectedText = selection.lines.join(\"\\n\");\n\t\t\t\t\t\t\t\tif ((effectiveRanges || effectiveOffset) && selection.lines.length === 0) { const requested = effectiveRanges?.[0]?.start ?? effectiveOffset ?? 1; resolve({ content: [{ type: \"text\", text: `Requested line ${requested} is beyond end of resource (${textContent.split(\"\\n\").length} lines total).` }], details: undefined }); return; }\n\t\t\t\t\t\t\t\tif (selectedText.length > READ_TOOL_MAX_RESULT_CHARS || Buffer.byteLength(selectedText, \"utf8\") > DEFAULT_MAX_BYTES) { resolve(oversizedReadResult({ blocked: true, path: effectivePath, chars: selectedText.length, maxChars: READ_TOOL_MAX_RESULT_CHARS, startLine: selection.firstLine, totalFileLines: textContent.split(\"\\n\").length, firstLineBytes: Buffer.byteLength(selection.lines[0] ?? \"\", \"utf8\"), byteGuidance: false })); return; }\n\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: selectedText }], details: readSourceMeta(effectivePath) }); return;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (archive) {\n\t\t\t\t\t\t\t\tconst resolvedArchive = resolveArchiveSelector(archive, cwd);\n\t\t\t\t\t\t\t\tconst textContent = readArchiveSelector(resolvedArchive);\n\t\t\t\t\t\t\t\tlet allLines = textContent.split(\"\\n\");\n\t\t\t\t\t\t\t\tif (conflictsOnly) {\n\t\t\t\t\t\t\t\t\tlet inConflict = false;\n\t\t\t\t\t\t\t\t\tconst conflictLines = allLines.filter((line) => { if (line.startsWith(\"<<<<<<<\")) inConflict = true; const keep = inConflict; if (line.startsWith(\">>>>>>>\")) inConflict = false; return keep; });\n\t\t\t\t\t\t\t\t\tif (conflictLines.length === 0) { resolve({ content: [{ type: \"text\", text: \"No conflict markers found\" }], details: undefined }); return; }\n\t\t\t\t\t\t\t\t\tallLines = conflictLines;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst rangeSelection = (rawOutput ? selectExactReadRanges : selectReadRanges)(allLines, effectiveRanges);\n\t\t\t\t\t\t\t\tconst startLine = rangeSelection ? rangeSelection.firstLine - 1 : effectiveOffset ? Math.max(0, effectiveOffset - 1) : 0;\n\t\t\t\t\t\t\t\tif (startLine >= allLines.length || (effectiveRanges && rangeSelection?.selectedLines.length === 0)) {\n\t\t\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: `Requested line ${startLine + 1} is beyond end of resource (${allLines.length} lines total).` }], details: undefined });\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst endLine = effectiveLimit !== undefined ? Math.min(startLine + effectiveLimit, allLines.length) : allLines.length;\n\t\t\t\t\t\t\t\tconst selectedLines = rangeSelection?.selectedLines ?? allLines.slice(startLine, endLine);\n\t\t\t\t\t\t\t\tconst selectedText = selectedLines.join(\"\\n\");\n\t\t\t\t\t\t\t\tif (selectedText.length > READ_TOOL_MAX_RESULT_CHARS || Buffer.byteLength(selectedText, \"utf8\") > DEFAULT_MAX_BYTES) { resolve(oversizedReadResult({ blocked: true, path: `${resolvedArchive.archivePath}:${resolvedArchive.memberPath}`, chars: selectedText.length, maxChars: READ_TOOL_MAX_RESULT_CHARS, startLine: startLine + 1, totalFileLines: allLines.length, firstLineBytes: Buffer.byteLength(selectedLines[0] ?? \"\", \"utf8\"), byteGuidance: false })); return; }\n\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: selectedText }], details: readSourceMeta(`${resolvedArchive.archivePath}:${resolvedArchive.memberPath}`) });\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (/^(?:skill|agent|artifact|history|issue|local|memory|pr|conflict|omp|rule|mcp|vault):\\/\\//.test(effectivePath)) {\n\t\t\t\t\t\t\t\tconst sourcePath = effectivePath.startsWith(\"local://\") ? resolveInternalSelector(effectivePath, cwd) : undefined;\n\t\t\t\t\t\t\t\tif (sourcePath) { resolve(await createReadToolDefinition(cwd, options).execute(_toolCallId, { path: appendReadSelectors(sourcePath, selector) }, signal, _onUpdate, ctx as never)); return; }\n\t\t\t\t\t\t\t\tconst allLines = (await readInternalSelector(effectivePath, cwd, resourceCtx)).split(\"\\n\");\n\t\t\t\t\t\t\t\tconst rangeSelection = (rawOutput ? selectExactReadRanges : selectReadRanges)(allLines, effectiveRanges);\n\t\t\t\t\t\t\t\tconst startLine = rangeSelection ? rangeSelection.firstLine - 1 : effectiveOffset ? Math.max(0, effectiveOffset - 1) : 0;\n\t\t\t\t\t\t\t\tif (startLine >= allLines.length || (effectiveRanges && rangeSelection?.selectedLines.length === 0)) {\n\t\t\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: `Requested line ${startLine + 1} is beyond end of resource (${allLines.length} lines total).` }], details: undefined });\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst endLine = effectiveLimit !== undefined ? Math.min(startLine + effectiveLimit, allLines.length) : allLines.length;\n\t\t\t\t\t\t\t\tconst selectedLines = rangeSelection?.selectedLines ?? allLines.slice(startLine, endLine);\n\t\t\t\t\t\t\t\tconst selectedText = selectedLines.join(\"\\n\");\n\t\t\t\t\t\t\t\tif (selectedText.length > READ_TOOL_MAX_RESULT_CHARS || Buffer.byteLength(selectedText, \"utf8\") > DEFAULT_MAX_BYTES) { resolve(oversizedReadResult({ blocked: true, path: effectivePath, chars: selectedText.length, maxChars: READ_TOOL_MAX_RESULT_CHARS, startLine: startLine + 1, totalFileLines: allLines.length, firstLineBytes: Buffer.byteLength(selectedLines[0] ?? \"\", \"utf8\"), byteGuidance: false })); return; }\n\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: selectedText }], details: readSourceMeta(effectivePath) });\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (isReadResourceSelector(effectivePath)) throw new Error(`Read resource selectors are not supported by this filesystem backend: ${path}`);\n\t\t\t\t\t\t\tconst absolutePath = await resolveReadPathAsync(effectivePath, cwd);\n\t\t\t\t\t\t\tif (aborted) return;\n\t\t\t\t\t\t\tlet content: (TextContent | ImageContent)[];\n\t\t\t\t\t\t\tlet details: ReadToolDetails | undefined;\n\t\t\t\t\t\t\tawait ops.access(absolutePath);\n\t\t\t\t\t\t\tif (aborted) return;\n\t\t\t\t\t\t\tif (isDocumentPath(absolutePath) && !rawOutput && !isNotebookPath(absolutePath)) {\n\t\t\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\t\t\tconst textContent = await extractDocumentMarkdown(buffer, absolutePath);\n\t\t\t\t\t\t\t\tconst selection = applyReadLineSelection(textContent.split(\"\\n\"), effectiveRanges, effectiveOffset, effectiveLimit, rawOutput);\n\t\t\t\t\t\t\t\tif (selection.lines.length === 0) { resolve({ content: [{ type: \"text\", text: `Requested line ${selection.firstLine} is beyond end of document (${textContent.split(\"\\n\").length} lines total).` }], details: undefined }); return; }\n\t\t\t\t\t\t\t\tconst selectedText = selection.lines.join(\"\\n\");\n\t\t\t\t\t\t\tif (selectedText.length > READ_TOOL_MAX_RESULT_CHARS || Buffer.byteLength(selectedText, \"utf8\") > DEFAULT_MAX_BYTES) { resolve(oversizedReadResult({ blocked: true, path: absolutePath, chars: selectedText.length, maxChars: READ_TOOL_MAX_RESULT_CHARS, startLine: selection.firstLine, totalFileLines: textContent.split(\"\\n\").length, firstLineBytes: Buffer.byteLength(selection.lines[0] ?? \"\", \"utf8\"), byteGuidance: false })); return; }\n\t\t\t\t\t\t\tcontent = [{ type: \"text\", text: selectedText }]; resolve({ content, details: readSourceMeta(absolutePath) }); return;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!options?.operations && (await fsStat(absolutePath)).isDirectory()) {\n\t\t\t\t\t\t\t\tconst tree = await buildDirectoryTree(absolutePath, { maxDepth: 2, perDirLimit: 12, rootLimit: null });\n\t\t\t\t\t\t\t\tconst allLines = tree.rendered.split(\"\\n\"), rangeSelection = (rawOutput ? selectExactReadRanges : selectReadRanges)(allLines, effectiveRanges), startLine = rangeSelection ? rangeSelection.firstLine - 1 : effectiveOffset ? Math.max(0, effectiveOffset - 1) : 0;\n\t\t\t\t\t\t\t\tif (startLine >= allLines.length || (effectiveRanges && rangeSelection?.selectedLines.length === 0)) { resolve({ content: [{ type: \"text\", text: `Requested line ${startLine + 1} is beyond end of directory (${allLines.length} lines total).` }], details: undefined }); return; }\n\t\t\t\t\t\t\t\tconst endLine = effectiveLimit !== undefined ? Math.min(startLine + effectiveLimit, allLines.length) : allLines.length, selectedLines = rangeSelection?.selectedLines ?? allLines.slice(startLine, endLine);\n\t\t\t\t\t\t\t\tcontent = [{ type: \"text\", text: selectedLines.join(\"\\n\") }];\n\t\t\t\t\t\t\t\tconst meta = { ...readSourceMeta(absolutePath).meta, ...(tree.truncated ? { limits: { perDirLimit: 12, totalLines: tree.totalLines } } : {}) };\n\t\t\t\t\t\t\t\tresolve({ content, details: { isDirectory: true, resolvedPath: absolutePath, meta } });\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst mimeType = ops.detectImageMimeType ? await ops.detectImageMimeType(absolutePath) : undefined;\n\t\t\t\t\t\t\tconst nonVisionImageNote = getNonVisionImageNote(ctx?.model);\n\t\t\t\t\t\t\tif (mimeType) {\n\t\t\t\t\t\t\t\t// Read image as binary.\n\t\t\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\t\t\tconst processed = await processImage(buffer, mimeType, { autoResizeImages });\n\t\t\t\t\t\t\t\tif (!processed.ok) {\n\t\t\t\t\t\t\t\t\tlet textNote = `Read image file [${mimeType}]\\n${processed.message}`;\n\t\t\t\t\t\t\t\t\tif (nonVisionImageNote) textNote += `\\n${nonVisionImageNote}`;\n\t\t\t\t\t\t\t\t\tcontent = [{ type: \"text\", text: textNote }];\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tlet textNote = `Read image file [${processed.mimeType}]`;\n\t\t\t\t\t\t\t\t\tif (processed.hints.length > 0) textNote += `\\n${processed.hints.join(\"\\n\")}`;\n\t\t\t\t\t\t\t\t\tif (nonVisionImageNote) textNote += `\\n${nonVisionImageNote}`;\n\t\t\t\t\t\t\t\t\tcontent = [\n\t\t\t\t\t\t\t\t\t\t{ type: \"text\", text: textNote },\n\t\t\t\t\t\t\t\t\t\t{ type: \"image\", data: processed.data, mimeType: processed.mimeType },\n\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\t\t\tconst textContent = (isNotebookPath(absolutePath) && !rawOutput) ? readEditableNotebookText(absolutePath, effectivePath) : buffer.toString(\"utf-8\");\n\t\t\t\t\t\t\t\tlet allLines = textContent.split(\"\\n\");\n\t\t\t\t\t\t\t\tlet conflictLineNumbers: number[] | undefined;\n\t\t\t\t\t\t\t\tif (conflictsOnly) {\n\t\t\t\t\t\t\t\t\tregisterConflictBlocks(cwd, parseConflictBlocks(absolutePath, textContent));\n\t\t\t\t\t\t\t\t\tconst conflictLines: string[] = [];\n\t\t\t\t\t\t\t\t\tconflictLineNumbers = [];\n\t\t\t\t\t\t\t\t\tlet inConflict = false;\n\t\t\t\t\t\t\t\t\tallLines.forEach((line, index) => {\n\t\t\t\t\t\t\t\t\t\tif (line.startsWith(\"<<<<<<<\")) inConflict = true;\n\t\t\t\t\t\t\t\t\t\tif (inConflict) { conflictLines.push(line); conflictLineNumbers!.push(index + 1); }\n\t\t\t\t\t\t\t\t\t\tif (line.startsWith(\">>>>>>>\")) inConflict = false;\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tif (conflictLines.length > 0) allLines = conflictLines;\n\t\t\t\t\t\t\t\t\telse { signal?.removeEventListener(\"abort\", onAbort); resolve({ content: [{ type: \"text\", text: \"No conflict markers found\" }], details: undefined }); return; }\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst totalFileLines = allLines.length;\n\t\t\t\t\t\t\t\tconst rangeSelection = (rawOutput ? selectExactReadRanges : selectReadRanges)(allLines, effectiveRanges);\n\t\t\t\t\t\t\t\tconst startLine = rangeSelection ? rangeSelection.firstLine - 1 : effectiveOffset ? Math.max(0, effectiveOffset - 1) : 0;\n\t\t\t\t\t\t\t\tconst startLineDisplay = startLine + 1;\n\t\t\t\t\t\t\t\tif (startLine >= allLines.length || (effectiveRanges && rangeSelection?.selectedLines.length === 0)) {\n\t\t\t\t\t\t\t\t\tconst requested = effectiveRanges?.[0]?.start ?? startLineDisplay;\n\t\t\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: `Requested line ${requested} is beyond end of file (${allLines.length} lines total). Use ${effectivePath}:${Math.max(1, allLines.length)} to read the final line.` }], details: undefined });\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tlet selectedContent: string;\n\t\t\t\t\t\t\t\tlet selectedLines: string[];\n\t\t\t\t\t\t\t\tlet userLimitedLines: number | undefined;\n\t\t\t\t\t\t\t\tif (rangeSelection) {\n\t\t\t\t\t\t\t\t\tselectedLines = rangeSelection.selectedLines;\n\t\t\t\t\t\t\t\t\tselectedContent = rangeSelection.selectedContent;\n\t\t\t\t\t\t\t\t\tuserLimitedLines = rangeSelection.userLimitedLines;\n\t\t\t\t\t\t\t\t} else if (effectiveLimit !== undefined) {\n\t\t\t\t\t\t\t\t\tconst endLine = Math.min(startLine + effectiveLimit, allLines.length);\n\t\t\t\t\t\t\t\t\tselectedLines = allLines.slice(startLine, endLine);\n\t\t\t\t\t\t\t\t\tselectedContent = selectedLines.join(\"\\n\");\n\t\t\t\t\t\t\t\t\tuserLimitedLines = endLine - startLine;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tselectedLines = allLines.slice(startLine);\n\t\t\t\t\t\t\t\t\tselectedContent = selectedLines.join(\"\\n\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (selectedContent.length > READ_TOOL_MAX_RESULT_CHARS) {\n\t\t\t\t\t\t\t\t\tconst firstSelectedLine = allLines[startLine] ?? \"\";\n\t\t\t\t\t\t\t\t\tconst firstLineBytes = Buffer.byteLength(firstSelectedLine, \"utf-8\");\n\t\t\t\t\t\t\t\t\tconst selectedLineCount = trimTrailingEmptyLines(selectedLines).length;\n\t\t\t\t\t\t\t\t\tconst byteGuidance = selectedLineCount <= 1 || firstLineBytes > DEFAULT_MAX_BYTES;\n\t\t\t\t\t\t\t\t\tconst oversizedRead: OversizedReadDetails = { blocked: true, path: absolutePath, chars: selectedContent.length, maxChars: READ_TOOL_MAX_RESULT_CHARS, startLine: startLineDisplay, ...(effectiveLimit !== undefined ? { requestedLimit: effectiveLimit } : {}), totalFileLines, firstLineBytes, byteGuidance };\n\t\t\t\t\t\t\t\tdetails = { oversizedRead, meta: readSourceMeta(absolutePath).meta };\n\t\t\t\t\t\t\t\t\tcontent = [{ type: \"text\", text: buildOversizedReadMessage(oversizedRead) }];\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tconst truncation = truncateHead(selectedContent);\n\t\t\t\t\t\t\t\t\tlet outputText = truncation.content;\n\t\t\t\t\t\t\t\t\tif (truncation.firstLineExceedsLimit) {\n\t\t\t\t\t\t\t\t\t\tconst firstLineSize = formatSize(Buffer.byteLength(allLines[startLine], \"utf-8\"));\n\t\t\t\t\t\t\t\t\t\toutputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLineDisplay}p' ${effectivePath} | head -c ${DEFAULT_MAX_BYTES}]`;\n\t\t\t\t\t\tdetails = { truncation, meta: { source: absolutePath, sourcePath: absolutePath, truncation } };\n\t\t\t\t\t\t\t\t\t} else if (truncation.truncated) {\n\t\t\t\t\t\t\t\t\t\tconst endLineDisplay = startLineDisplay + truncation.outputLines - 1;\n\t\t\t\t\t\t\t\t\t\tconst nextOffset = endLineDisplay + 1;\n\t\t\t\t\t\t\t\t\t\toutputText += truncation.truncatedBy === \"lines\" ? `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Continue with path selector :${nextOffset}.]` : `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Continue with path selector :${nextOffset}.]`;\n\t\t\t\t\t\tdetails = { truncation, meta: { source: absolutePath, sourcePath: absolutePath, truncation } };\n\t\t\t\t\t\t\t\t\t} else if (!rawOutput && userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) {\n\t\t\t\t\t\t\t\t\t\tconst remaining = allLines.length - (startLine + userLimitedLines);\n\t\t\t\t\t\t\t\t\t\tconst nextOffset = startLine + userLimitedLines + 1;\n\t\t\t\t\t\t\t\t\t\toutputText = `${truncation.content}\\n\\n[${remaining} more lines in file. Continue with path selector :${nextOffset}.]`;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (truncation.firstLineExceedsLimit) content = [{ type: \"text\", text: outputText }];\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tconst snapshot = recordHashlineSnapshot(absolutePath, cwd, textContent, hashlineStore);\n\t\t\t\t\t\t\t\t\t\tconst visibleContent = truncation.truncated ? truncation.content : selectedContent;\n\t\t\t\t\t\t\t\t\t\tconst header = `[${snapshot.displayPath}#${snapshot.tag}]`;\n\t\t\t\t\t\t\t\t\t\tconst selectedConflictLineNumbers = conflictLineNumbers && rangeSelection?.lineNumbers ? rangeSelection.lineNumbers.map((line) => conflictLineNumbers![line - 1]).filter((line): line is number => typeof line === \"number\") : conflictLineNumbers ? conflictLineNumbers.slice(startLine, startLine + selectedLines.length) : undefined;\n\t\t\t\t\t\t\t\t\t\tlet hashlineOutput = rawOutput ? visibleContent : selectedConflictLineNumbers && visibleContent === selectedContent ? formatHashlineSelectedLines(header, selectedLines, selectedConflictLineNumbers) : rangeSelection && visibleContent === selectedContent ? formatHashlineSelectedLines(header, selectedLines, rangeSelection.lineNumbers) : formatHashlineContent(snapshot, visibleContent, startLineDisplay);\n\t\t\t\t\t\t\t\t\t\tif (outputText.startsWith(truncation.content) && outputText.length > truncation.content.length) hashlineOutput += outputText.slice(truncation.content.length);\n\t\t\t\t\t\t\t\t\t\tcontent = [{ type: \"text\", text: hashlineOutput }];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (aborted) return;\n\t\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\tresolve({ content, details: details ?? readSourceMeta(absolutePath) });\n\t\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\tif (!aborted) reject(error);\n\t\t\t\t\t\t}\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\tconst classification = !context.expanded ? getCompactReadClassification(args, context.cwd) : undefined;\n\t\t\ttext.setText(\n\t\t\t\tclassification ? formatCompactReadCall(classification, args, theme) : formatReadCall(args, theme),\n\t\t\t);\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(\n\t\t\t\tformatReadResult(context.args, result, options, theme, context.showImages, context.cwd, context.isError),\n\t\t\t);\n\t\t\treturn text;\n\t\t},\n\t};\n}\nexport function createReadTool(cwd: string, options?: ReadToolOptions): AgentTool<typeof readSchema> {\n\treturn wrapToolDefinition(createReadToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAK/D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAa5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAA8E,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAMvI,OAAO,EAAoD,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AACtH,QAAA,MAAM,UAAU;;EAEmB,CAAC;AACpC,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;CACtB;AACD,MAAM,WAAW,eAAe;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,aAAa,CAAC,EAAE,oBAAoB,CAAC;IACrC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;CACrI;AAMD,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,mBAAmB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CACnF;AAMD,MAAM,WAAW,eAAe;IAC/B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,aAAa,CAAC,EAAE,qBAAqB,CAAC;CACtC;AAwJD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CAsP/I;AACD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG"}
@@ -1 +1 @@
1
- {"version":3,"file":"render-utils.d.ts","sourceRoot":"","sources":["../../../src/core/tools/render-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAE9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAKzE,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAOjD;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIjF;AAED,wBAAgB,cAAc,CAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,KAAK,EAAE;IAAE,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAAE,EACzD,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAClC,MAAM,CAKR;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAIjD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,aAAa,CAC5B,MAAM,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,SAAS,EACzG,UAAU,EAAE,OAAO,GACjB,MAAM,CAsBR;AAED,MAAM,MAAM,oBAAoB,CAAC,QAAQ,IAAI;IAC5C,OAAO,EAAE,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,CAAC;IACxC,OAAO,EAAE,QAAQ,CAAC;CAClB,CAAC;AAEF,wBAAgB,cAAc,CAAC,KAAK,EAAE;IAAE,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAAE,GAAG,MAAM,CAEhG","sourcesContent":["import * as os from \"node:os\";\nimport { pathToFileURL } from \"node:url\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport { getCapabilities, getImageDimensions, hyperlink, imageFallback } from \"@earendil-works/pi-tui\";\nimport type { ThemeColor } from \"../../modes/interactive/theme/theme.ts\";\nimport { stripAnsi } from \"../../utils/ansi.ts\";\nimport { resolvePath } from \"../../utils/paths.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\n\nexport function shortenPath(path: unknown): string {\n\tif (typeof path !== \"string\") return \"\";\n\tconst home = os.homedir();\n\tif (path.startsWith(home)) {\n\t\treturn `~${path.slice(home.length)}`;\n\t}\n\treturn path;\n}\n\nexport function linkPath(styledText: string, rawPath: string, cwd: string): string {\n\tif (!getCapabilities().hyperlinks) return styledText;\n\tconst absolutePath = resolvePath(rawPath, cwd);\n\treturn hyperlink(styledText, pathToFileURL(absolutePath).href);\n}\n\nexport function renderToolPath(\n\trawPath: string | null,\n\ttheme: { fg: (name: ThemeColor, text: string) => string },\n\tcwd: string,\n\toptions?: { emptyFallback?: string },\n): string {\n\tif (rawPath === null) return invalidArgText(theme);\n\tconst value = rawPath || options?.emptyFallback;\n\tif (!value) return theme.fg(\"toolOutput\", \"...\");\n\treturn linkPath(theme.fg(\"accent\", shortenPath(value)), value, cwd);\n}\n\nexport function str(value: unknown): string | null {\n\tif (typeof value === \"string\") return value;\n\tif (value == null) return \"\";\n\treturn null;\n}\n\nexport function replaceTabs(text: string): string {\n\treturn text.replace(/\\t/g, \" \");\n}\n\nexport function normalizeDisplayText(text: string): string {\n\treturn text.replace(/\\r/g, \"\");\n}\n\nexport function getTextOutput(\n\tresult: { content: Array<{ type: string; text?: string; data?: string; mimeType?: string }> } | undefined,\n\tshowImages: boolean,\n): string {\n\tif (!result) return \"\";\n\n\tconst textBlocks = result.content.filter((c) => c.type === \"text\");\n\tconst imageBlocks = result.content.filter((c) => c.type === \"image\");\n\n\tlet output = textBlocks.map((c) => sanitizeBinaryOutput(stripAnsi(c.text || \"\")).replace(/\\r/g, \"\")).join(\"\\n\");\n\n\tconst caps = getCapabilities();\n\tif (imageBlocks.length > 0 && (!caps.images || !showImages)) {\n\t\tconst imageIndicators = imageBlocks\n\t\t\t.map((img) => {\n\t\t\t\tconst mimeType = img.mimeType ?? \"image/unknown\";\n\t\t\t\tconst dims =\n\t\t\t\t\timg.data && img.mimeType ? (getImageDimensions(img.data, img.mimeType) ?? undefined) : undefined;\n\t\t\t\treturn imageFallback(mimeType, dims);\n\t\t\t})\n\t\t\t.join(\"\\n\");\n\t\toutput = output ? `${output}\\n${imageIndicators}` : imageIndicators;\n\t}\n\n\treturn output;\n}\n\nexport type ToolRenderResultLike<TDetails> = {\n\tcontent: (TextContent | ImageContent)[];\n\tdetails: TDetails;\n};\n\nexport function invalidArgText(theme: { fg: (name: ThemeColor, text: string) => string }): string {\n\treturn theme.fg(\"error\", \"[invalid arg]\");\n}\n"]}
1
+ {"version":3,"file":"render-utils.d.ts","sourceRoot":"","sources":["../../../src/core/tools/render-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAE9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAKzE,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAOjD;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIjF;AAED,wBAAgB,cAAc,CAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,KAAK,EAAE;IAAE,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAAE,EACzD,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAClC,MAAM,CAKR;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAIjD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,aAAa,CAC5B,MAAM,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,SAAS,EACzG,UAAU,EAAE,OAAO,GACjB,MAAM,CAsBR;AAED,MAAM,MAAM,oBAAoB,CAAC,QAAQ,IAAI;IAC5C,OAAO,EAAE,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,CAAC;IACxC,OAAO,EAAE,QAAQ,CAAC;CAClB,CAAC;AAEF,wBAAgB,cAAc,CAAC,KAAK,EAAE;IAAE,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAAE,GAAG,MAAM,CAEhG"}
@@ -1 +1 @@
1
- {"version":3,"file":"resource-selectors.d.ts","sourceRoot":"","sources":["../../../src/core/tools/resource-selectors.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, isAbsolute, relative, resolve } from \"node:path\";\nimport { deflateRawSync, gunzipSync, gzipSync, inflateRawSync } from \"node:zlib\";\nimport { loadNativeSearchBinding } from \"./search-native.ts\";\n\nexport interface ArchiveSelector { archivePath: string; memberPath: string }\nexport interface SqliteSelector { databasePath: string; table?: string; rowId?: string; query?: string; limit?: number; offset?: number; where?: string; order?: string; schema?: boolean; sampleRows?: number }\nexport interface InternalResourceRouter {\n\tread?: (url: string) => string | Buffer | undefined | Promise<string | Buffer | undefined>;\n\twrite?: (url: string, content: string) => void | Promise<void>;\n\tresolve?: (url: string) => string | undefined | Promise<string | undefined>;\n}\nexport interface InternalResourceContext { internalRouter?: InternalResourceRouter; internalResourceRouter?: InternalResourceRouter; resolveInternalUrl?: (url: string) => string | undefined | Promise<string | undefined> }\ntype SqliteValue = string | number | boolean | null;\ninterface SqliteQuery { all(...params: SqliteValue[]): Record<string, SqliteValue>[]; get?(...params: SqliteValue[]): Record<string, SqliteValue> | undefined; iterate?(): Iterable<Record<string, SqliteValue>>; run(...params: SqliteValue[]): { changes?: number; lastInsertRowid?: number | bigint } }\ninterface SqliteDatabase { query(sql: string): SqliteQuery; close(): void }\ntype SqliteDatabaseConstructor = new (path: string, options?: { readonly?: boolean }) => SqliteDatabase;\nfunction sqliteDatabase(): SqliteDatabaseConstructor {\n\ttry { return (createRequire(import.meta.url)(\"bun:sqlite\") as { Database: SqliteDatabaseConstructor }).Database; }\n\tcatch { throw new Error(\"SQLite selectors require Atomic's Bun runtime with bun:sqlite support.\"); }\n}\nfunction existingSqliteFile(path: string): boolean | undefined { if (!existsSync(path)) return undefined; return readFileSync(path).subarray(0, 16).toString(\"binary\") === \"SQLite format 3\\0\"; }\nexport function sqliteSelectorForPath(value: string, cwd: string): SqliteSelector | undefined { const selector = parseSqliteSelector(value); if (!selector) return undefined; const absolute = resolveContainedLocalPath(cwd, selector.databasePath, \"SQLite selector\"); if (existingSqliteFile(absolute) !== true) return undefined; return { ...selector, databasePath: absolute }; }\n\nconst MAX_TAR_ARCHIVE_BYTES = 256 * 1024 * 1024;\nconst MAX_ARCHIVE_MEMBER_BYTES = 64 * 1024 * 1024;\nconst MAX_ARCHIVE_DIRECTORY_ENTRIES = 500;\n\nexport function parseArchiveSelector(value: string): ArchiveSelector | undefined {\n\tconst match = value.match(/^(.+\\.(?:zip|jar|tar|tgz|tar\\.gz|gz)):(.*)$/i);\n\treturn match ? { archivePath: match[1] ?? \"\", memberPath: match[2] ?? \"\" } : undefined;\n}\nexport function resolveArchiveSelector(selector: ArchiveSelector, cwd: string): ArchiveSelector {\n\treturn { ...selector, archivePath: resolveContainedLocalPath(cwd, selector.archivePath, \"Archive selector\") };\n}\nexport function parseSqliteSelector(value: string): SqliteSelector | undefined {\n\tconst match = value.match(/^(.+\\.(?:sqlite3?|db3?))(?:\\?q=(.+)|:([^:?]+)(?:\\?(.+))?(?::([^:?]+))?)?$/i);\n\tif (!match) return undefined;\n\tconst params = new URLSearchParams(match[4] ?? \"\");\n\tconst limit = Number.parseInt(params.get(\"limit\") ?? \"\", 10);\n\tconst offset = Number.parseInt(params.get(\"offset\") ?? \"\", 10);\n\tconst sampleRows = Number.parseInt(params.get(\"sampleRows\") ?? params.get(\"sample_rows\") ?? \"\", 10);\n\treturn { databasePath: match[1] ?? \"\", query: match[2] ? decodeURIComponent(match[2]) : undefined, table: match[3], rowId: match[5] ?? params.get(\"id\") ?? undefined, limit: Number.isFinite(limit) ? Math.max(0, Math.min(500, limit)) : undefined, offset: Number.isFinite(offset) ? Math.max(0, offset) : undefined, where: params.get(\"where\") ?? undefined, order: params.get(\"order\") ?? undefined, schema: params.get(\"schema\") === \"true\" || params.get(\"schema\") === \"1\", sampleRows: Number.isFinite(sampleRows) ? Math.max(0, Math.min(100, sampleRows)) : undefined };\n}\n\nfunction isZipArchive(path: string): boolean { return /\\.(?:zip|jar)$/i.test(path); }\nfunction isGzipTar(path: string): boolean { return /\\.(?:tgz|tar\\.gz|gz)$/i.test(path); }\nfunction crc32(data: Buffer): number {\n\tlet crc = ~0;\n\tfor (const byte of data) { crc ^= byte; for (let i = 0; i < 8; i++) crc = (crc >>> 1) ^ (0xedb88320 & -(crc & 1)); }\n\treturn ~crc >>> 0;\n}\nfunction assertZipRange(buf: Buffer, offset: number, length: number, label: string): void {\n\tif (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || offset < 0 || length < 0 || offset > buf.length - length) throw new Error(`Invalid zip entry bounds: ${label}`);\n}\nfunction zipEntryPayload(buf: Buffer, label: string, name: string, localOffset: number, compressedSize: number): Buffer {\n\tif (compressedSize > MAX_ARCHIVE_MEMBER_BYTES) throw new Error(`Archive member compressed data too large: ${name}`);\n\tassertZipRange(buf, localOffset, 30, label);\n\tif (buf.readUInt32LE(localOffset) !== 0x04034b50) throw new Error(`Invalid zip local header: ${label}`);\n\tconst localNameLen = buf.readUInt16LE(localOffset + 26), localExtraLen = buf.readUInt16LE(localOffset + 28), start = localOffset + 30 + localNameLen + localExtraLen;\n\tassertZipRange(buf, start, compressedSize, label);\n\treturn buf.subarray(start, start + compressedSize);\n}\nexport function readZipEntriesFromBuffer(buf: Buffer, label: string): Map<string, Buffer> {\n\tconst entries = new Map<string, Buffer>();\n\tlet eocd = -1; for (let i = buf.length - 22; i >= 0; i--) if (buf.readUInt32LE(i) === 0x06054b50) { eocd = i; break; }\n\tif (eocd < 0) throw new Error(`Invalid zip archive: ${label}`);\n\tlet ptr = buf.readUInt32LE(eocd + 16); const total = buf.readUInt16LE(eocd + 10);\n\tfor (let n = 0; n < total; n++) {\n\t\tassertZipRange(buf, ptr, 46, label);\n\t\tif (buf.readUInt32LE(ptr) !== 0x02014b50) throw new Error(`Invalid zip central directory: ${label}`);\n\t\tconst method = buf.readUInt16LE(ptr + 10), compressedSize = buf.readUInt32LE(ptr + 20), size = buf.readUInt32LE(ptr + 24), nameLen = buf.readUInt16LE(ptr + 28), extraLen = buf.readUInt16LE(ptr + 30), commentLen = buf.readUInt16LE(ptr + 32), localOffset = buf.readUInt32LE(ptr + 42);\n\t\tassertZipRange(buf, ptr + 46, nameLen + extraLen + commentLen, label);\n\t\tconst name = buf.subarray(ptr + 46, ptr + 46 + nameLen).toString(); ptr += 46 + nameLen + extraLen + commentLen;\n\t\tif (name.endsWith(\"/\")) continue;\n\t\tif (size > MAX_ARCHIVE_MEMBER_BYTES) throw new Error(`Archive member too large: ${name}`);\n\t\tconst data = zipEntryPayload(buf, label, name, localOffset, compressedSize);\n\t\tentries.set(name, method === 0 ? Buffer.from(data) : method === 8 ? inflateRawSync(data, { maxOutputLength: MAX_ARCHIVE_MEMBER_BYTES }) : (() => { throw new Error(`Unsupported zip compression method ${method} for ${name}`); })());\n\t}\n\treturn entries;\n}\nexport function readZipEntries(path: string): Map<string, Buffer> { return readZipEntriesFromBuffer(readFileSync(path), path); }\nfunction writeZipEntries(path: string, entries: Map<string, Buffer>): void {\n\tconst locals: Buffer[] = [], centrals: Buffer[] = []; let offset = 0;\n\tfor (const [name, data] of entries) {\n\t\tconst nameBuf = Buffer.from(name), compressed = deflateRawSync(data), crc = crc32(data);\n\t\tconst local = Buffer.alloc(30); local.writeUInt32LE(0x04034b50, 0); local.writeUInt16LE(20, 4); local.writeUInt16LE(8, 8); local.writeUInt32LE(crc, 14); local.writeUInt32LE(compressed.length, 18); local.writeUInt32LE(data.length, 22); local.writeUInt16LE(nameBuf.length, 26);\n\t\tlocals.push(local, nameBuf, compressed);\n\t\tconst central = Buffer.alloc(46); central.writeUInt32LE(0x02014b50, 0); central.writeUInt16LE(20, 4); central.writeUInt16LE(20, 6); central.writeUInt16LE(8, 10); central.writeUInt32LE(crc, 16); central.writeUInt32LE(compressed.length, 20); central.writeUInt32LE(data.length, 24); central.writeUInt16LE(nameBuf.length, 28); central.writeUInt32LE(offset, 42);\n\t\tcentrals.push(central, nameBuf); offset += local.length + nameBuf.length + compressed.length;\n\t}\n\tconst centralSize = centrals.reduce((sum, part) => sum + part.length, 0); const eocd = Buffer.alloc(22);\n\teocd.writeUInt32LE(0x06054b50, 0); eocd.writeUInt16LE(entries.size, 8); eocd.writeUInt16LE(entries.size, 10); eocd.writeUInt32LE(centralSize, 12); eocd.writeUInt32LE(offset, 16);\n\twriteFileSync(path, Buffer.concat([...locals, ...centrals, eocd]));\n}\nfunction parseTar(path: string): Map<string, Buffer> {\n\tconst raw = readFileSync(path);\n\tif (raw.length > MAX_TAR_ARCHIVE_BYTES) throw new Error(`Archive too large: ${path}`);\n\tconst buf = isGzipTar(path) ? gunzipSync(raw, { maxOutputLength: MAX_TAR_ARCHIVE_BYTES }) : raw;\n\tif (buf.length > MAX_TAR_ARCHIVE_BYTES) throw new Error(`Archive too large: ${path}`);\n\tconst entries = new Map<string, Buffer>();\n\tfor (let offset = 0; offset + 512 <= buf.length;) {\n\t\tconst header = buf.subarray(offset, offset + 512);\n\t\tif (header.every((byte) => byte === 0)) break;\n\t\tconst name = header.subarray(0, 100).toString().replace(/\\0.*$/, \"\");\n\t\tconst prefix = header.subarray(345, 500).toString().replace(/\\0.*$/, \"\");\n\t\tconst fullName = prefix ? `${prefix}/${name}` : name;\n\t\tconst size = Number.parseInt(header.subarray(124, 136).toString().replace(/\\0.*$/, \"\").trim() || \"0\", 8);\n\t\tconst type = String.fromCharCode(header[156] ?? 48);\n\t\tconst dataStart = offset + 512;\n\t\tif (type !== \"5\" && fullName) {\n\t\t\tif (size > MAX_ARCHIVE_MEMBER_BYTES) throw new Error(`Archive member too large: ${fullName}`);\n\t\t\tentries.set(fullName, Buffer.from(buf.subarray(dataStart, dataStart + size)));\n\t\t}\n\t\toffset = dataStart + Math.ceil(size / 512) * 512;\n\t}\n\treturn entries;\n}\nfunction tarHeader(name: string, size: number): Buffer {\n\tconst header = Buffer.alloc(512); const nameBytes = Buffer.from(name); nameBytes.copy(header, 0, 0, Math.min(100, nameBytes.length));\n\theader.write(\"0000644\\0\", 100); header.write(\"0000000\\0\", 108); header.write(\"0000000\\0\", 116); header.write(size.toString(8).padStart(11, \"0\") + \"\\0\", 124); header.write(\"00000000000\\0\", 136); header.fill(0x20, 148, 156); header[156] = 48; header.write(\"ustar\\0\", 257); header.write(\"00\", 263);\n\tlet sum = 0; for (const byte of header) sum += byte; header.write(sum.toString(8).padStart(6, \"0\") + \"\\0 \", 148); return header;\n}\nfunction writeTar(path: string, entries: Map<string, Buffer>): void {\n\tconst parts: Buffer[] = []; for (const [name, data] of entries) { parts.push(tarHeader(name, data.length), data); const pad = (512 - (data.length % 512)) % 512; if (pad) parts.push(Buffer.alloc(pad)); }\n\tparts.push(Buffer.alloc(1024)); const out = Buffer.concat(parts); writeFileSync(path, isGzipTar(path) ? gzipSync(out) : out);\n}\nfunction listArchiveDirectory(names: Iterable<string>, memberPath: string): string {\n\tconst prefix = memberPath ? `${memberPath.replace(/\\/+$/g, \"\")}/` : \"\";\n\tconst children = new Set<string>();\n\tfor (const name of names) { if (!name.startsWith(prefix) || name === prefix) continue; const rest = name.slice(prefix.length); const [first, ...more] = rest.split(\"/\"); if (first) children.add(more.length > 0 ? `${first}/` : first); }\n\treturn [...children].sort().slice(0, MAX_ARCHIVE_DIRECTORY_ENTRIES).join(\"\\n\");\n}\n\nfunction readZipSelector(path: string, memberPath: string): string {\n\tconst buf = readFileSync(path); let eocd = -1; for (let i = buf.length - 22; i >= 0; i--) if (buf.readUInt32LE(i) === 0x06054b50) { eocd = i; break; }\n\tif (eocd < 0) throw new Error(`Invalid zip archive: ${path}`);\n\tlet ptr = buf.readUInt32LE(eocd + 16); const total = buf.readUInt16LE(eocd + 10), names: string[] = [];\n\tfor (let n = 0; n < total; n++) {\n\t\tassertZipRange(buf, ptr, 46, path);\n\t\tif (buf.readUInt32LE(ptr) !== 0x02014b50) throw new Error(`Invalid zip central directory: ${path}`);\n\t\tconst method = buf.readUInt16LE(ptr + 10), compressedSize = buf.readUInt32LE(ptr + 20), size = buf.readUInt32LE(ptr + 24), nameLen = buf.readUInt16LE(ptr + 28), extraLen = buf.readUInt16LE(ptr + 30), commentLen = buf.readUInt16LE(ptr + 32), localOffset = buf.readUInt32LE(ptr + 42);\n\t\tassertZipRange(buf, ptr + 46, nameLen + extraLen + commentLen, path);\n\t\tconst name = buf.subarray(ptr + 46, ptr + 46 + nameLen).toString(); ptr += 46 + nameLen + extraLen + commentLen; if (name.endsWith(\"/\")) continue; names.push(name); if (memberPath && name !== memberPath) continue;\n\t\tif (!memberPath) continue; if (size > MAX_ARCHIVE_MEMBER_BYTES) throw new Error(`Archive member too large: ${memberPath}`);\n\t\tconst data = zipEntryPayload(buf, path, name, localOffset, compressedSize);\n\t\treturn (method === 0 ? Buffer.from(data) : method === 8 ? inflateRawSync(data, { maxOutputLength: MAX_ARCHIVE_MEMBER_BYTES }) : (() => { throw new Error(`Unsupported zip compression method ${method} for ${name}`); })()).toString(\"utf8\");\n\t}\n\tconst listing = listArchiveDirectory(names, memberPath);\n\tif (listing) return listing;\n\tif (!memberPath) return \"\";\n\tthrow new Error(`Archive member not found: ${memberPath}`);\n}\n\nexport function readArchiveSelector(selector: ArchiveSelector): string {\n\tif (isZipArchive(selector.archivePath)) return readZipSelector(selector.archivePath, selector.memberPath);\n\tconst entries = parseTar(selector.archivePath);\n\tif (!selector.memberPath) return listArchiveDirectory(entries.keys(), \"\");\n\tconst data = entries.get(selector.memberPath); if (data) { if (data.length > MAX_ARCHIVE_MEMBER_BYTES) throw new Error(`Archive member too large: ${selector.memberPath}`); return data.toString(\"utf8\"); }\n\tconst listing = listArchiveDirectory(entries.keys(), selector.memberPath); if (listing) return listing;\n\tthrow new Error(`Archive member not found: ${selector.memberPath}`);\n}\nfunction validateArchiveMemberPath(memberPath: string): void { if (!memberPath || memberPath.startsWith(\"/\") || memberPath.endsWith(\"/\") || memberPath.split(\"/\").includes(\"..\")) throw new Error(`Invalid archive member path: ${memberPath}`); }\nfunction writeZipEntrySelective(path: string, memberPath: string, data: Buffer): void {\n\tconst source = existsSync(path) ? readFileSync(path) : Buffer.alloc(0); const locals: Buffer[] = [], centrals: Buffer[] = []; let offset = 0;\n\tif (source.length > 0) {\n\t\tlet eocd = -1; for (let i = source.length - 22; i >= 0; i--) if (source.readUInt32LE(i) === 0x06054b50) { eocd = i; break; }\n\t\tif (eocd < 0) throw new Error(`Invalid zip archive: ${path}`);\n\t\tlet ptr = source.readUInt32LE(eocd + 16); const total = source.readUInt16LE(eocd + 10);\n\t\tfor (let n = 0; n < total; n++) {\n\t\t\tassertZipRange(source, ptr, 46, path);\n\t\t\tconst start = ptr, flags = source.readUInt16LE(ptr + 8), nameLen = source.readUInt16LE(ptr + 28), extraLen = source.readUInt16LE(ptr + 30), commentLen = source.readUInt16LE(ptr + 32), localOffset = source.readUInt32LE(ptr + 42), compressedSize = source.readUInt32LE(ptr + 20);\n\t\t\tassertZipRange(source, ptr + 46, nameLen + extraLen + commentLen, path);\n\t\t\tconst name = source.subarray(ptr + 46, ptr + 46 + nameLen).toString(); ptr += 46 + nameLen + extraLen + commentLen; if (name === memberPath) continue;\n\t\t\tif ((flags & 0x0008) !== 0) throw new Error(`Unsupported zip data descriptor during selective write: ${name}`);\n\t\t\tassertZipRange(source, localOffset, 30, path);\n\t\t\tconst localNameLen = source.readUInt16LE(localOffset + 26), localExtraLen = source.readUInt16LE(localOffset + 28), localEnd = localOffset + 30 + localNameLen + localExtraLen + compressedSize;\n\t\t\tassertZipRange(source, localOffset, localEnd - localOffset, path);\n\t\t\tconst local = source.subarray(localOffset, localEnd), central = Buffer.from(source.subarray(start, ptr)); central.writeUInt32LE(offset, 42); locals.push(local); centrals.push(central); offset += local.length;\n\t\t}\n\t}\n\tconst tmp = `${path}.atomic-entry-${Date.now()}`; writeZipEntries(tmp, new Map([[memberPath, data]])); const built = readFileSync(tmp); rmSync(tmp, { force: true });\n\tconst eocdStart = built.length - 22, localLen = built.readUInt32LE(eocdStart + 16), centralSizeOne = built.readUInt32LE(eocdStart + 12); const central = Buffer.from(built.subarray(localLen, localLen + centralSizeOne)); central.writeUInt32LE(offset, 42); locals.push(built.subarray(0, localLen)); centrals.push(central); offset += localLen;\n\tconst centralSize = centrals.reduce((sum, part) => sum + part.length, 0), eocd = Buffer.alloc(22); eocd.writeUInt32LE(0x06054b50, 0); eocd.writeUInt16LE(centrals.length, 8); eocd.writeUInt16LE(centrals.length, 10); eocd.writeUInt32LE(centralSize, 12); eocd.writeUInt32LE(offset, 16); writeFileSync(path, Buffer.concat([...locals, ...centrals, eocd]));\n}\n\nexport function writeArchiveSelector(selector: ArchiveSelector, content: string): void {\n\tvalidateArchiveMemberPath(selector.memberPath); mkdirSync(dirname(selector.archivePath), { recursive: true });\n\tif (isZipArchive(selector.archivePath)) { writeZipEntrySelective(selector.archivePath, selector.memberPath, Buffer.from(content)); return; }\n\tconst entries = existsSync(selector.archivePath) ? parseTar(selector.archivePath) : new Map<string, Buffer>();\n\tentries.set(selector.memberPath, Buffer.from(content)); writeTar(selector.archivePath, entries);\n}\nconst SEARCH_LINE_LIMIT = 512;\nfunction truncateSearchLine(line: string): string {\n\treturn line.length > SEARCH_LINE_LIMIT ? `${line.slice(0, SEARCH_LINE_LIMIT)}… [truncated]` : line;\n}\nfunction stripExtendedRegexWhitespace(pattern: string): string {\n\tlet out = \"\", escaped = false, inClass = false, inComment = false;\n\tfor (const ch of pattern) {\n\t\tif (inComment) { if (ch === \"\\n\" || ch === \"\\r\") inComment = false; continue; }\n\t\tif (escaped) { out += ch; escaped = false; continue; }\n\t\tif (ch === \"\\\\\") { out += ch; escaped = true; continue; }\n\t\tif (ch === \"[\") inClass = true; else if (ch === \"]\") inClass = false;\n\t\tif (!inClass && ch === \"#\") { inComment = true; continue; }\n\t\tif (!inClass && /\\s/.test(ch)) continue;\n\t\tout += ch;\n\t}\n\treturn out;\n}\n\nfunction normalizeSearchPattern(pattern: string, ignoreCase: boolean): { pattern: string; flags: string } {\n\tconst match = pattern.match(/^\\(\\?([imsUx-]+)\\)([\\s\\S]*)$/);\n\tconst flags = new Set<string>(); if (ignoreCase) flags.add(\"i\");\n\tif (match) { const inline = match[1] ?? \"\"; for (const flag of inline) { if (flag === \"i\" || flag === \"m\" || flag === \"s\") flags.add(flag); } return { pattern: inline.includes(\"x\") ? stripExtendedRegexWhitespace(match[2] ?? \"\") : match[2] ?? \"\", flags: [...flags].join(\"\") }; }\n\treturn { pattern, flags: [...flags].join(\"\") };\n}\n\nfunction searchTextSelectorLines(label: string, text: string, pattern: string, ignoreCase: boolean, literal: boolean, contextBefore = 1, contextAfter = 3): string[] {\n\tconst normalized = normalizeSearchPattern(pattern, ignoreCase);\n\tif (!literal && !normalized.pattern.includes(\"\\n\") && !pattern.includes(\"\\\\n\")) try { const native = loadNativeSearchBinding()?.search?.(text, { pattern: normalized.pattern, ignoreCase: normalized.flags.includes(\"i\"), multiline: normalized.flags.includes(\"m\") || normalized.flags.includes(\"s\"), contextBefore, contextAfter }); if (native && !native.error) return native.matches.flatMap((match) => [...(match.contextBefore ?? []).map((line) => `${label}-${line.lineNumber}- ${truncateSearchLine(line.line)}`), `${label}:${match.lineNumber}: ${truncateSearchLine(match.line)}`, ...(match.contextAfter ?? []).map((line) => `${label}-${line.lineNumber}- ${truncateSearchLine(line.line)}`)]); } catch {}\n\tconst matcher = literal ? undefined : new RegExp(normalized.pattern, normalized.flags);\n\tconst needle = normalized.flags.includes(\"i\") ? normalized.pattern.toLowerCase() : normalized.pattern;\n\tconst lines = text.split(\"\\n\");\n\tconst matchLines = new Set<number>();\n\tlines.forEach((line, index) => { const haystack = normalized.flags.includes(\"i\") ? line.toLowerCase() : line; if (matcher ? matcher.test(line) : haystack.includes(needle)) matchLines.add(index + 1); });\n\tif (matchLines.size === 0 && matcher) { const match = new RegExp(normalized.pattern, normalized.flags).exec(text); if (match?.[0]) { const start = text.slice(0, match.index).split(\"\\n\").length; for (let line = start; line < start + match[0].split(\"\\n\").length; line++) matchLines.add(line); } }\n\tconst outputLines = new Set<number>();\n\tfor (const line of matchLines) for (let n = Math.max(1, line - contextBefore); n <= Math.min(lines.length, line + contextAfter); n++) outputLines.add(n);\n\treturn [...outputLines].sort((a, b) => a - b).map((line) => `${label}${matchLines.has(line) ? \":\" : \"-\"}${line}${matchLines.has(line) ? \":\" : \"-\"} ${truncateSearchLine(lines[line - 1] ?? \"\")}`);\n}\n\nexport function searchArchiveSelector(selector: ArchiveSelector, pattern: string, ignoreCase = false, literal = false, contextBefore = 1, contextAfter = 3): string {\n\tif (isZipArchive(selector.archivePath) && selector.memberPath) return searchTextSelectorLines(`${selector.archivePath}:${selector.memberPath}`, readArchiveSelector(selector), pattern, ignoreCase, literal, contextBefore, contextAfter).join(\"\\n\");\n\tconst entries = isZipArchive(selector.archivePath) ? readZipEntries(selector.archivePath) : parseTar(selector.archivePath); const out: string[] = [];\n\tfor (const [name, data] of entries) { if (selector.memberPath && name !== selector.memberPath) continue; out.push(...searchTextSelectorLines(`${selector.archivePath}:${name}`, data.toString(\"utf8\"), pattern, ignoreCase, literal, contextBefore, contextAfter)); }\n\treturn out.join(\"\\n\");\n}\n\nfunction routerFromContext(context?: InternalResourceContext): InternalResourceRouter | undefined { return context?.internalRouter ?? context?.internalResourceRouter; }\nasync function resolveViaRouter(value: string, cwd: string, context?: InternalResourceContext): Promise<string | undefined> {\n\tconst router = routerFromContext(context); const routed = await router?.resolve?.(value); if (routed) return routed;\n\tconst resolved = await context?.resolveInternalUrl?.(value); if (resolved) return resolved;\n\treturn resolveInternalSelector(value, cwd);\n}\nfunction isContained(root: string, candidate: string): boolean {\n\tconst rel = relative(root, candidate);\n\treturn rel === \"\" || (!rel.startsWith(\"..\") && !isAbsolute(rel));\n}\nfunction nearestExistingAncestor(pathValue: string): string {\n\tlet current = pathValue;\n\twhile (!existsSync(current)) { const parent = dirname(current); if (parent === current) return current; current = parent; }\n\treturn current;\n}\nfunction realpathExisting(pathValue: string): string { return realpathSync.native(nearestExistingAncestor(pathValue)); }\nfunction resolveContainedPath(root: string, pathValue: string, label: string): string {\n\tconst lexicalRoot = resolve(root), resolved = resolve(root, pathValue);\n\tif (!isContained(lexicalRoot, resolved)) throw new Error(`${label} escapes the workspace: ${pathValue}`);\n\tif (!isContained(realpathExisting(lexicalRoot), realpathExisting(resolved))) throw new Error(`${label} escapes the workspace: ${pathValue}`);\n\treturn resolved;\n}\nfunction resolveContainedLocalPath(cwd: string, pathValue: string, label = \"local:// resource\"): string { return resolveContainedPath(cwd, pathValue, label); }\n\nfunction fallbackInternalPath(value: string, cwd: string): string | undefined {\n\tconst skill = value.match(/^skill:\\/\\/([^/]+)\\/?(.*)$/); if (skill) { const name = skill[1] ?? \"\", rest = skill[2] || \"SKILL.md\"; return [\".agents/skills\", \"packages/subagents/skills\", \"packages/workflows/skills\"].map((base) => resolveContainedPath(resolveContainedLocalPath(cwd, base, \"skill:// resource\"), `${name}/${rest}`, \"skill:// resource\")).find((candidate) => existsSync(candidate)); }\n\tconst local = value.match(/^local:\\/\\/(.+)$/); if (local) return resolveContainedLocalPath(cwd, local[1] ?? \"\");\n\treturn undefined;\n}\nexport function resolveInternalSelector(value: string, cwd: string): string | undefined { return fallbackInternalPath(value, cwd); }\nexport async function readInternalSelector(value: string, cwd: string, context?: InternalResourceContext): Promise<string> {\n\tconst router = routerFromContext(context); const routed = await router?.read?.(value); if (typeof routed === \"string\") return routed; if (Buffer.isBuffer(routed)) return routed.toString(\"utf8\");\n\tconst resolved = await resolveViaRouter(value, cwd, context); if (!resolved || !existsSync(resolved)) throw new Error(`Internal resource not found or no session router supports it: ${value}`); return readFileSync(resolved, \"utf8\");\n}\nexport async function writeInternalSelector(value: string, cwd: string, content: string, context?: InternalResourceContext): Promise<void> {\n\tconst router = routerFromContext(context); if (router?.write) { await router.write(value, content); return; }\n\tconst resolved = await resolveViaRouter(value, cwd, context); if (!resolved) throw new Error(`Unsupported writable internal resource without a session router: ${value}`); mkdirSync(dirname(resolved), { recursive: true }); writeFileSync(resolved, content);\n}\nexport async function searchInternalSelector(value: string, cwd: string, pattern: string, ignoreCase = false, literal = false, context?: InternalResourceContext, contextBefore = 1, contextAfter = 3): Promise<string> {\n\treturn searchTextSelectorLines(value, await readInternalSelector(value, cwd, context), pattern, ignoreCase, literal, contextBefore, contextAfter).join(\"\\n\");\n}\nfunction shellQuote(value: string): string { return `'${value.replace(/'/g, `'\\\\''`)}'`; }\nexport async function expandShellInternalUrls(text: string, cwd: string, context?: InternalResourceContext, quote = false): Promise<string> {\n\tlet output = text; const matches = [...new Set(text.match(/[a-z][a-z0-9+.-]*:\\/\\/[^\\s'\"`$)]+/gi) ?? [])];\n\tfor (const match of matches) { const resolved = await resolveViaRouter(match, cwd, context); if (resolved) output = output.split(match).join(quote ? shellQuote(resolved) : resolved); }\n\treturn output;\n}\n\nconst ROW_COUNT_PROBE_CAP = 50_000;\nconst MAX_RAW_QUERY_ROWS = 1000;\n\nfunction quoteSqliteIdent(value: string): string { return `\"${value.replace(/\"/g, '\"\"')}\"`; }\nfunction formatSqliteOrder(order: string | undefined): string {\n\tif (!order) return \"\";\n\treturn order.split(\",\").map((part) => {\n\t\tconst [column, direction] = part.split(\":\");\n\t\tif (!column || !/^[A-Za-z_][\\w$]*$/.test(column)) throw new Error(`Invalid SQLite order column: ${part}`);\n\t\tconst dir = direction?.toLowerCase() === \"desc\" ? \"desc\" : \"asc\";\n\t\treturn `${quoteSqliteIdent(column)} ${dir}`;\n\t}).join(\", \");\n}\nconst FORBIDDEN_WHERE_KEYWORDS = new Set([\"limit\", \"offset\", \"union\", \"intersect\", \"except\", \"attach\", \"detach\", \"pragma\", \"select\", \"load_extension\"]);\nfunction validateSqliteWhere(where: string | undefined): string | undefined {\n\tconst trimmed = where?.trim(); if (!trimmed) return undefined;\n\tlet quote = \"\", token = \"\";\n\tconst flush = () => { const lower = token.toLowerCase(); if (token && (FORBIDDEN_WHERE_KEYWORDS.has(lower) || lower.startsWith(\"pragma_\") || lower.startsWith(\"sqlite_\"))) throw new Error(\"Invalid SQLite where filter\"); token = \"\"; };\n\tfor (let i = 0; i < trimmed.length; i++) { const ch = trimmed[i], next = trimmed[i + 1]; if (quote === \"'\") { if (ch === \"'\" && next === \"'\") { i++; continue; } if (ch === \"'\") quote = \"\"; continue; } if (quote === \"\\\"\") { if (ch === \"\\\"\" && next === \"\\\"\") { i++; continue; } if (ch === \"\\\"\") { quote = \"\"; flush(); continue; } if (ch && /[A-Za-z0-9_]/.test(ch)) { token += ch; continue; } flush(); token = ch; continue; } if (ch === \"'\" || ch === \"\\\"\") { flush(); quote = ch; continue; } if (ch === \";\" || ch === \"-\" && next === \"-\" || ch === \"/\" && next === \"*\" || ch === \"*\" && next === \"/\") throw new Error(\"Invalid SQLite where filter\"); if (ch && /[A-Za-z0-9_]/.test(ch)) { token += ch; continue; } flush(); }\n\tflush();\n\treturn trimmed;\n}\nfunction rowsToJsonLines(rows: Record<string, SqliteValue>[]): string { return rows.map((row) => JSON.stringify(row)).join(\"\\n\"); }\n\nconst FORBIDDEN_RAW_QUERY_KEYWORDS = new Set([\"attach\", \"detach\", \"pragma\", \"insert\", \"update\", \"delete\", \"drop\", \"alter\", \"create\", \"replace\", \"vacuum\", \"reindex\", \"load_extension\", \"union\", \"intersect\", \"except\"]);\nfunction validateRawSqliteQuery(query: string): string {\n\tconst trimmed = query.trim(); if (!trimmed) throw new Error(\"SQLite raw query must not be empty\"); if (!/^select\\b/i.test(trimmed)) throw new Error(\"Invalid raw SQLite query\");\n\tlet quote = \"\", token = \"\";\n\tconst flush = () => { const lower = token.toLowerCase(); if (lower && (FORBIDDEN_RAW_QUERY_KEYWORDS.has(lower) || lower.startsWith(\"sqlite_\") || lower.startsWith(\"pragma_\"))) throw new Error(\"Invalid raw SQLite query\"); token = \"\"; };\n\tfor (let i = 0; i < trimmed.length; i++) { const ch = trimmed[i], next = trimmed[i + 1]; if (quote === \"'\") { if (ch === \"'\" && next === \"'\") { i++; continue; } if (ch === \"'\") quote = \"\"; continue; } if (quote === \"\\\"\") { if (ch === \"\\\"\" && next === \"\\\"\") { i++; continue; } if (ch === \"\\\"\") { quote = \"\"; flush(); continue; } if (ch && /[A-Za-z0-9_]/.test(ch)) { token += ch; continue; } flush(); token = ch; continue; } if (ch === \"'\" || ch === \"\\\"\") { flush(); quote = ch; continue; } if (ch === \";\" || ch === \"-\" && next === \"-\" || ch === \"/\" && next === \"*\" || ch === \"*\" && next === \"/\") throw new Error(\"Invalid raw SQLite query\"); if (ch && /[A-Za-z0-9_]/.test(ch)) { token += ch; continue; } flush(); }\n\tflush();\n\tif (quote) throw new Error(\"Invalid raw SQLite query\");\n\treturn trimmed;\n}\n\nfunction readRawSqliteRows(query: SqliteQuery): Record<string, SqliteValue>[] { const rows: Record<string, SqliteValue>[] = []; const iterable = query.iterate?.(); if (!iterable) return query.all().slice(0, MAX_RAW_QUERY_ROWS); for (const row of iterable) { if (rows.length >= MAX_RAW_QUERY_ROWS) break; rows.push(row); } return rows; }\n\nfunction sqliteTableColumns(db: SqliteDatabase, table: string): string[] { return (db.query(`pragma table_info(${quoteSqliteIdent(table)})`).all() as Array<{ name?: string }>).map((row) => String(row.name ?? \"\")).filter(Boolean); }\n\nfunction boundedSqliteRowCount(db: SqliteDatabase, table: string): string | number { const row = db.query(`select count(*) as count from (select 1 from ${quoteSqliteIdent(table)} limit ${ROW_COUNT_PROBE_CAP + 1})`).all()[0]; const count = Number(row?.count ?? 0); return count > ROW_COUNT_PROBE_CAP ? `${ROW_COUNT_PROBE_CAP}+` : count; }\nfunction normalizeSqliteWriteValue(value: unknown, column: string): SqliteValue {\n\tif (value === null || typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") return value;\n\tthrow new Error(`SQLite column '${column}' only accepts JSON scalar values or null`);\n}\n\nfunction sqlitePrimaryKey(db: SqliteDatabase, table: string): string { return (db.query(`pragma table_info(${quoteSqliteIdent(table)})`).all() as Array<{ name?: string; pk?: number }>).find((row) => row.pk === 1)?.name ?? \"rowid\"; }\nexport function readSqliteSelector(selector: SqliteSelector): string {\n\tconst Database = sqliteDatabase();\n\tconst db = new Database(selector.databasePath, { readonly: true });\n\ttry {\n\t\tif (selector.query) return rowsToJsonLines(readRawSqliteRows(db.query(validateRawSqliteQuery(selector.query))));\n\t\tif (!selector.table) {\n\t\t\tconst tables = db.query(\"select name from sqlite_master where type='table' and name not like 'sqlite_%' order by name\").all().slice(0, 500);\n\t\t\treturn tables.map((row) => { const name = String(row.name ?? \"\"); return JSON.stringify({ table: name, rows: boundedSqliteRowCount(db, name) }); }).join(\"\\n\");\n\t\t}\n\t\tconst schemaRows = db.query(`pragma table_info(${quoteSqliteIdent(selector.table)})`).all();\n\t\tif (selector.schema) return rowsToJsonLines(schemaRows);\n\t\tconst key = sqlitePrimaryKey(db, selector.table);\n\t\tconst where = selector.rowId ? `${quoteSqliteIdent(key)} = ?` : validateSqliteWhere(selector.where);\n\t\tconst order = formatSqliteOrder(selector.order);\n\t\tconst isQuery = selector.limit !== undefined || selector.offset !== undefined || selector.where || selector.order;\n\t\tconst limit = selector.sampleRows ?? selector.limit ?? (isQuery ? 20 : 5);\n\t\tconst offset = selector.offset ?? 0;\n\t\tconst sql = [`select * from ${quoteSqliteIdent(selector.table)}`, where ? `where ${where}` : \"\", order ? `order by ${order}` : \"\", \"limit ? offset ?\"].filter(Boolean).join(\" \");\n\t\tconst rows = selector.rowId ? db.query(sql).all(selector.rowId, limit, offset) : db.query(sql).all(limit, offset);\n\t\tif (selector.rowId || isQuery || selector.sampleRows !== undefined) return rowsToJsonLines(rows);\n\t\treturn [`# ${selector.table}`, \"Schema:\", rowsToJsonLines(schemaRows), \"Rows:\", rowsToJsonLines(rows)].join(\"\\n\");\n\t} finally { db.close(); }\n}\nfunction stripJson5Comments(input: string): string {\n\tlet out = \"\", quote = \"\", escaped = false;\n\tfor (let i = 0; i < input.length; i++) { const ch = input[i]!, next = input[i + 1]; if (escaped) { out += ch; escaped = false; continue; } if (quote) { out += ch; if (ch === \"\\\\\") escaped = true; else if (ch === quote) quote = \"\"; continue; } if (ch === '\"' || ch === \"'\") { quote = ch; out += ch; continue; } if (ch === \"/\" && next === \"/\") { while (i < input.length && input[i] !== \"\\n\") i++; out += \"\\n\"; continue; } if (ch === \"/\" && next === \"*\") { i += 2; while (i < input.length && !(input[i] === \"*\" && input[i + 1] === \"/\")) i++; i++; continue; } out += ch; }\n\treturn out;\n}\nfunction parseLooseJsonObject(content: string): Record<string, SqliteValue> {\n\tconst trimmed = stripJson5Comments(content.trim());\n\tif (!trimmed) throw new Error(\"SQLite write content must be a JSON object; empty content only deletes a row when a row id is present.\");\n\tlet parsed: unknown;\n\ttry { parsed = JSON.parse(trimmed); } catch {\n\t\tconst normalized = trimmed.replace(/([{,]\\s*)([A-Za-z_$][\\w$]*)\\s*:/g, '$1\"$2\":').replace(/:\\s*\\+(-?\\d+(?:\\.\\d+)?)/g, \":$1\").replace(/(:\\s*)0x[0-9a-fA-F]+/g, (match, prefix: string) => `${prefix}${Number.parseInt(match.slice(prefix.length + 2), 16)}`).replace(/'([^'\\\\]*(?:\\\\.[^'\\\\]*)*)'/g, (_match, body: string) => JSON.stringify(body.replace(/\\\\'/g, \"'\"))).replace(/,\\s*([}\\]])/g, \"$1\");\n\t\tparsed = JSON.parse(normalized);\n\t}\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) throw new Error(\"SQLite write content must be a JSON object.\");\n\treturn Object.fromEntries(Object.entries(parsed as Record<string, unknown>).map(([column, value]) => [column, normalizeSqliteWriteValue(value, column)]));\n}\nexport function writeSqliteSelector(selector: SqliteSelector, content: string): string {\n\tif (!selector.table) throw new Error(\"SQLite write target must include a table name\"); if (!existsSync(selector.databasePath)) throw new Error(`SQLite database does not exist: ${selector.databasePath}`); if (content.trim() === \"\") { if (!selector.rowId) throw new Error(\"SQLite empty content deletes a row only when a row id is present.\"); const Database = sqliteDatabase(); const db = new Database(selector.databasePath); try { const table = selector.table; const result = db.query(`delete from ${quoteSqliteIdent(table)} where ${quoteSqliteIdent(sqlitePrimaryKey(db, table))} = ?`).run(selector.rowId); return (result.changes ?? 0) > 0 ? `Deleted row '${selector.rowId}' in ${table}` : `No row deleted for '${selector.rowId}' in ${table}`; } finally { db.close(); } } const data = parseLooseJsonObject(content); const Database = sqliteDatabase(); const db = new Database(selector.databasePath);\n\ttry { const table = selector.table; const quotedTable = quoteSqliteIdent(table); const key = sqlitePrimaryKey(db, table); const quotedKey = quoteSqliteIdent(key); const validColumns = new Set(sqliteTableColumns(db, table)); if (!selector.rowId && key !== \"id\" && Object.hasOwn(data, \"id\") && !Object.hasOwn(data, key)) { data[key] = data.id; delete data.id; } const columns = Object.keys(data); for (const column of columns) if (!validColumns.has(column)) throw new Error(`SQLite table '${table}' has no column named '${column}'`); if (columns.length === 0) { if (!selector.rowId) { db.query(`insert into ${quotedTable} default values`).run(); return `Inserted row into ${table}`; } throw new Error(\"SQLite update content must include at least one column.\"); } if (!selector.rowId) { const quoted = columns.map(quoteSqliteIdent).join(\", \"); db.query(`insert into ${quotedTable} (${quoted}) values (${columns.map(() => \"?\").join(\", \")})`).run(...columns.map((column) => data[column])); return `Inserted row into ${table}`; } const assignments = columns.map((column) => `${quoteSqliteIdent(column)} = ?`).join(\", \"); const result = db.query(`update ${quotedTable} set ${assignments} where ${quotedKey} = ?`).run(...columns.map((column) => data[column]), selector.rowId); return (result.changes ?? 0) > 0 ? `Updated row '${selector.rowId}' in ${table}` : `No row updated for '${selector.rowId}' in ${table}`; } finally { db.close(); }\n}\nexport function searchSqliteSelector(selector: SqliteSelector, pattern: string, ignoreCase = false, contextBefore = 1, contextAfter = 3): string {\n\tconst normalized = normalizeSearchPattern(pattern, ignoreCase);\n\tconst matcher = new RegExp(normalized.pattern, normalized.flags);\n\tif (!selector.table || selector.query) return searchTextSelectorLines(`${selector.databasePath}:${selector.table ?? \"tables\"}`, readSqliteSelector(selector), pattern, ignoreCase, false, contextBefore, contextAfter).join(\"\\n\");\n\tconst Database = sqliteDatabase();\n\tconst db = new Database(selector.databasePath, { readonly: true });\n\ttry {\n\t\tconst key = sqlitePrimaryKey(db, selector.table);\n\t\tconst where = selector.rowId ? `${quoteSqliteIdent(key)} = ?` : validateSqliteWhere(selector.where);\n\t\tconst order = formatSqliteOrder(selector.order);\n\t\tconst limit = selector.sampleRows ?? selector.limit ?? 1000;\n\t\tconst offset = selector.offset ?? 0;\n\t\tconst rowIdAlias = \"__atomic_rowid__\";\n\t\tconst selectList = key === \"rowid\" ? `rowid as ${quoteSqliteIdent(rowIdAlias)}, *` : \"*\";\n\t\tconst sql = [`select ${selectList} from ${quoteSqliteIdent(selector.table)}`, where ? `where ${where}` : \"\", order ? `order by ${order}` : \"\", \"limit ? offset ?\"].filter(Boolean).join(\" \");\n\t\tconst rows = selector.rowId ? db.query(sql).all(selector.rowId, limit, offset) : db.query(sql).all(limit, offset);\n\t\treturn rows.map((rawRow, index) => {\n\t\t\tconst row = rawRow as Record<string, unknown>;\n\t\t\tconst rowKey = key === \"rowid\" ? row[rowIdAlias] : row[key];\n\t\t\tif (key === \"rowid\") delete row[rowIdAlias];\n\t\t\treturn { row, key: String(rowKey ?? offset + index + 1) };\n\t\t}).filter(({ row }) => matcher.test(JSON.stringify(row))).map(({ row, key }) => `${selector.databasePath}:${selector.table}:${key}: ${truncateSearchLine(JSON.stringify(row))}`).join(\"\\n\");\n\t} finally { db.close(); }\n}\n"]}
1
+ {"version":3,"file":"resource-selectors.d.ts","sourceRoot":"","sources":["../../../src/core/tools/resource-selectors.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"search-details.d.ts","sourceRoot":"","sources":["../../../src/core/tools/search-details.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;QAAC,MAAM,CAAC,EAAE;YAAE,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CAC3F;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAE,MAAM,EAAO,GAAG,iBAAiB,CAQpK","sourcesContent":["import type { TruncationResult } from \"./truncate.ts\";\n\nexport interface SearchToolDetails {\n\tscopePath?: string;\n\tsearchPath?: string;\n\tcwd?: string;\n\tmatchCount?: number;\n\tfileCount?: number;\n\tfiles?: string[];\n\tfileMatches?: Record<string, number>;\n\tmissingPaths?: string[];\n\tdisplayContent?: string;\n\tfileLimitReached?: boolean;\n\tperFileLimitReached?: boolean;\n\ttruncation?: TruncationResult;\n\tmatchLimitReached?: number;\n\tlinesTruncated?: boolean;\n\tmeta?: { source?: string; truncation?: TruncationResult; limits?: { fileLimit?: number } };\n}\n\nexport function buildSearchDetails(base: SearchToolDetails | undefined, text: string, cwd: string, scopePath: string, missingPaths: string[] = []): SearchToolDetails {\n\tconst files = new Set<string>(), fileMatches: Record<string, number> = {}; let currentPath = \"\", matchCount = 0;\n\tfor (const line of text.split(\"\\n\")) {\n\t\tconst header = line.match(/^\\[([^\\]#]+)#[0-9A-F]{4}\\]$/); if (header) { currentPath = header[1] ?? \"\"; files.add(currentPath); continue; }\n\t\tconst pathMatch = line.match(/^(.+?)(?::\\d+: |-\\d+- )/); if (pathMatch) { currentPath = pathMatch[1] ?? currentPath; files.add(currentPath); }\n\t\tconst isMatch = /^\\*\\d+:/.test(line) || /^.+?:\\d+: /.test(line); if (isMatch && currentPath) { matchCount++; fileMatches[currentPath] = (fileMatches[currentPath] ?? 0) + 1; }\n\t}\n\treturn { ...(base ?? {}), scopePath, searchPath: scopePath, cwd, matchCount, fileCount: files.size, files: [...files], fileMatches, ...(missingPaths.length ? { missingPaths } : {}), displayContent: text, meta: { ...(base?.meta ?? {}), source: scopePath, ...(base?.truncation ? { truncation: base.truncation } : {}) } };\n}\n"]}
1
+ {"version":3,"file":"search-details.d.ts","sourceRoot":"","sources":["../../../src/core/tools/search-details.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;QAAC,MAAM,CAAC,EAAE;YAAE,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CAC3F;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAE,MAAM,EAAO,GAAG,iBAAiB,CAQpK"}
@@ -1 +1 @@
1
- {"version":3,"file":"search-line-ranges.d.ts","sourceRoot":"","sources":["../../../src/core/tools/search-line-ranges.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,EAAE,GAAG,SAAS,CAuB9E;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,eAAe,EAAE,CAAA;CAAE,CAMtG;AAQD,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,eAAe,EAAE,GAAG,SAAS,EAAE,aAAa,SAAI,EAAE,YAAY,SAAI,GAAG,MAAM,CAevJ","sourcesContent":["export interface SearchLineRange {\n\tstart: number;\n\tend: number;\n}\n\nexport function parseLineRangeSpec(spec: string): SearchLineRange[] | undefined {\n\tconst ranges: SearchLineRange[] = [];\n\tfor (const part of spec.split(\",\")) {\n\t\tconst plus = part.match(/^(\\d+)\\+(\\d+)$/);\n\t\tconst dash = part.match(/^(\\d+)(?:-|\\.\\.)(\\d+)$/);\n\t\tconst single = part.match(/^(\\d+)$/);\n\t\tlet start = 0;\n\t\tlet end = 0;\n\t\tif (plus) {\n\t\t\tstart = Number.parseInt(plus[1] ?? \"0\", 10);\n\t\t\tconst count = Number.parseInt(plus[2] ?? \"0\", 10);\n\t\t\tif (count < 1) return undefined;\n\t\t\tend = start + count - 1;\n\t\t} else if (dash) {\n\t\t\tstart = Number.parseInt(dash[1] ?? \"0\", 10);\n\t\t\tend = Number.parseInt(dash[2] ?? \"0\", 10);\n\t\t} else if (single) {\n\t\t\tstart = end = Number.parseInt(single[1] ?? \"0\", 10);\n\t\t} else return undefined;\n\t\tif (start < 1 || end < start) return undefined;\n\t\tranges.push({ start, end });\n\t}\n\treturn ranges.length > 0 ? ranges : undefined;\n}\n\nexport function splitLineRangeSelector(value: string): { path: string; lineRanges?: SearchLineRange[] } {\n\tconst match = value.match(/^(.*):(\\d+(?:(?:-|\\.\\.|\\+)\\d*)?(?:,\\d+(?:(?:-|\\.\\.|\\+)\\d*)?)*)$/);\n\tif (!match) return { path: value };\n\tconst ranges = parseLineRangeSpec(match[2] ?? \"\");\n\tif (!ranges) throw new Error(`Invalid line-range selector: ${match[2] ?? \"\"}`);\n\treturn { path: match[1] ?? value, lineRanges: ranges };\n}\n\nfunction rowLineNumber(line: string): { lineNumber: number; isMatch: boolean } | undefined {\n\tconst match = line.match(/^.+?(?::(\\d+): |-(\\d+)- )/);\n\tif (!match) return undefined;\n\treturn { lineNumber: Number.parseInt(match[1] ?? match[2] ?? \"0\", 10), isMatch: match[1] !== undefined };\n}\n\nexport function filterSearchOutputByLineRange(text: string, ranges: readonly SearchLineRange[] | undefined, contextBefore = 1, contextAfter = 3): string {\n\tif (!ranges || ranges.length === 0) return text;\n\tconst inSelectedRange = (lineNumber: number): boolean => ranges.some((range) => lineNumber >= range.start && lineNumber <= range.end);\n\tconst acceptedMatches: number[] = [];\n\tfor (const line of text.split(\"\\n\")) {\n\t\tconst row = rowLineNumber(line);\n\t\tif (row?.isMatch && inSelectedRange(row.lineNumber)) acceptedMatches.push(row.lineNumber);\n\t}\n\tif (acceptedMatches.length === 0) return \"No matches found\";\n\tconst keepLine = (lineNumber: number): boolean => acceptedMatches.some((matchLine) => lineNumber >= matchLine - contextBefore && lineNumber <= matchLine + contextAfter);\n\tconst filtered = text.split(\"\\n\").filter((line) => {\n\t\tconst row = rowLineNumber(line);\n\t\treturn row !== undefined && keepLine(row.lineNumber);\n\t}).join(\"\\n\");\n\treturn filtered || \"No matches found\";\n}\n"]}
1
+ {"version":3,"file":"search-line-ranges.d.ts","sourceRoot":"","sources":["../../../src/core/tools/search-line-ranges.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,EAAE,GAAG,SAAS,CAuB9E;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,eAAe,EAAE,CAAA;CAAE,CAMtG;AAQD,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,eAAe,EAAE,GAAG,SAAS,EAAE,aAAa,SAAI,EAAE,YAAY,SAAI,GAAG,MAAM,CAevJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"search-native.d.ts","sourceRoot":"","sources":["../../../src/core/tools/search-native.ts"],"names":[],"mappings":"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","sourcesContent":["import { createModuleRequire } from \"../../utils/module-require.ts\";\n\ntype NativeBinding = {\n\tglob: (options: NativeGlobOptions) => Promise<NativeGlobResult>;\n\tgrep: (options: NativeGrepOptions) => Promise<NativeGrepResult>;\n\tsearch?: (content: string | Uint8Array, options: NativeTextSearchOptions) => NativeTextSearchResult;\n\thasMatch?: (content: string | Uint8Array, pattern: string | Uint8Array, ignoreCase?: boolean | null, multiline?: boolean | null) => boolean;\n\tinvalidateFsScanCache?: (path?: string | null) => void;\n\tblockRangeAt?: (options: { code: string; path: string; line: number }) => { startLine: number; endLine: number } | null;\n};\n\nexport type NativeGlobMatch = {\n\tpath: string;\n\tfileType: \"File\" | \"Dir\" | \"Symlink\" | number;\n\tmtime?: number;\n\tsize?: number;\n};\n\nexport type NativeGlobOptions = {\n\tpattern: string;\n\tpath: string;\n\tfileType?: \"File\" | \"Dir\" | \"Symlink\" | number;\n\trecursive?: boolean;\n\thidden?: boolean;\n\tmaxResults?: number;\n\tgitignore?: boolean;\n\tcache?: boolean;\n\tsortByMtime?: boolean;\n\tincludeNodeModules?: boolean;\n\tsignal?: AbortSignal;\n\ttimeoutMs?: number;\n};\n\nexport type NativeGlobResult = {\n\tmatches: NativeGlobMatch[];\n\ttotalMatches: number;\n};\n\nexport type NativeGrepMatch = {\n\tpath: string;\n\tlineNumber: number;\n\tline: string;\n\tcontextBefore?: Array<{ lineNumber: number; line: string }>;\n\tcontextAfter?: Array<{ lineNumber: number; line: string }>;\n\ttruncated?: boolean;\n\tmatchCount?: number;\n};\n\nexport type NativeGrepOptions = {\n\tpattern: string;\n\tpath: string;\n\tcwd?: string;\n\tglob?: string;\n\ttype?: string;\n\tignoreCase?: boolean;\n\tmultiline?: boolean;\n\thidden?: boolean;\n\tgitignore?: boolean;\n\tcache?: boolean;\n\tmaxCount?: number;\n\toffset?: number;\n\tcontextBefore?: number;\n\tcontextAfter?: number;\n\tcontext?: number;\n\tmaxColumns?: number;\n\tmode?: \"content\" | \"count\" | \"filesWithMatches\";\n\tmaxCountPerFile?: number;\n\tsignal?: AbortSignal;\n\ttimeoutMs?: number;\n};\n\nexport type NativeGrepResult = {\n\tmatches: NativeGrepMatch[];\n\ttotalMatches: number;\n\tfilesWithMatches: number;\n\tfilesSearched: number;\n\tlimitReached: boolean;\n\tskippedOversized?: number;\n\terror?: string;\n};\nexport type NativeTextSearchOptions = Omit<NativeGrepOptions, \"path\" | \"cwd\" | \"glob\" | \"type\" | \"hidden\" | \"gitignore\" | \"cache\" | \"maxCountPerFile\" | \"signal\" | \"timeoutMs\">;\nexport type NativeTextSearchResult = { matches: Array<Omit<NativeGrepMatch, \"path\">>; matchCount: number; limitReached: boolean; error?: string };\n\nlet cachedLoadResult: NativeBinding | null | false = null;\n\nexport function resetNativeSearchBindingCache(): void {\n\tcachedLoadResult = null;\n}\n\nexport function loadNativeSearchBinding(): NativeBinding | null {\n\tif (cachedLoadResult !== null) return cachedLoadResult || null;\n\ttry {\n\t\tconst require = createModuleRequire(import.meta.url);\n\t\tconst binding = require(\"@bastani/atomic-natives\") as Partial<NativeBinding>;\n\t\tif (typeof binding.glob !== \"function\" || typeof binding.grep !== \"function\") {\n\t\t\tcachedLoadResult = false;\n\t\t\treturn null;\n\t\t}\n\t\tcachedLoadResult = binding as NativeBinding;\n\t\treturn cachedLoadResult;\n\t} catch {\n\t\tcachedLoadResult = false;\n\t\treturn null;\n\t}\n}\n\nexport function invalidateNativeSearchCache(path?: string): void {\n\tloadNativeSearchBinding()?.invalidateFsScanCache?.(path ?? null);\n}\n"]}
1
+ {"version":3,"file":"search-native.d.ts","sourceRoot":"","sources":["../../../src/core/tools/search-native.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/core/tools/search.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAEN,KAAK,eAAe,EACpB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAuD,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAKhH,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGjF,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,QAAA,MAAM,YAAY;;;;;;;EAYiB,CAAC;AACpC,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;AAC1D,MAAM,MAAM,iBAAiB,GAAG,eAAe,GAAG;IAAE,aAAa,CAAC,EAAE,qBAAqB,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA2N3I,wBAAgB,0BAA0B,CACzC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,iBAAiB,GACzB,cAAc,CAAC,OAAO,YAAY,EAAE,iBAAiB,GAAG,SAAS,CAAC,CAyMpE;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC,OAAO,YAAY,CAAC,CAEzG","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile as fsReadFile, stat as fsStat } from \"node:fs/promises\";\nimport { dirname, join, resolve as resolvePath } from \"node:path\";\nimport type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Text } from \"@earendil-works/pi-tui\";\nimport { type Static, Type } from \"typebox\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport {\n\tcreateGrepToolDefinition,\n\ttype GrepToolOptions,\n} from \"./grep.ts\";\nimport { normalizePathLikeInput, splitPathLikeGlob } from \"./glob-path-utils.ts\";\nimport { createHashlineSnapshotStore, recordHashlineSnapshot, type HashlineSnapshotStore } from \"./hashline.ts\";\nimport { invalidArgText, shortenPath, str } from \"./render-utils.ts\";\nimport { resolveToCwd } from \"./path-utils.ts\";\nimport { parseArchiveSelector, readArchiveSelector, resolveArchiveSelector, resolveInternalSelector, searchArchiveSelector, searchInternalSelector, searchSqliteSelector, sqliteSelectorForPath, type InternalResourceContext, type SqliteSelector } from \"./resource-selectors.ts\";\nimport { DEFAULT_MAX_BYTES, formatSize, truncateHead } from \"./truncate.ts\";\nimport { buildSearchDetails, type SearchToolDetails } from \"./search-details.ts\";\nimport { filterSearchOutputByLineRange, splitLineRangeSelector, type SearchLineRange } from \"./search-line-ranges.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nexport type { SearchToolDetails } from \"./search-details.ts\";\nconst searchSchema = Type.Object({\n\tpattern: Type.String({ description: \"Regex pattern. Whitespace-only patterns are rejected; otherwise the pattern is preserved verbatim.\" }),\n\tpaths: Type.Optional(\n\t\tType.Union([\n\t\t\tType.String({ description: \"File, directory, glob, internal URL, or <file>:<lines> selector to search. Omitted or empty searches the workspace root.\" }),\n\t\t\tType.Array(Type.String({ description: \"File, directory, glob, internal URL, or <file>:<lines> selector to search.\" })),\n\t\t]),\n\t),\n\ti: Type.Optional(Type.Boolean({ description: \"Case-insensitive search.\" })),\n\tgitignore: Type.Optional(Type.Boolean({ description: \"Respect gitignore.\" })),\n\tcase: Type.Optional(Type.Boolean({ description: \"Set false for case-insensitive search (oh-my-pi compatibility).\" })),\n\tskip: Type.Optional(Type.Union([Type.Number({ description: \"Files to skip before collecting results; use to paginate when the previous call hit the file limit.\" }), Type.Null()])),\n}, { additionalProperties: false });\nexport type SearchToolInput = Static<typeof searchSchema>;\nexport type SearchToolOptions = GrepToolOptions & { hashlineStore?: HashlineSnapshotStore; contextBefore?: number; contextAfter?: number };\nfunction delimiterInExistingSearchGlobRoot(value: string, cwd: string): boolean { const selector = splitLineRangeSelector(value); const parsed = splitPathLikeGlob(selector.path); return !!parsed.glob && /[;,\\s]/.test(parsed.basePath) && existsSync(resolveToCwd(parsed.basePath, cwd)); }\nfunction archiveSelectorExists(value: string, cwd: string): boolean { const archive = parseArchiveSelector(value); if (!archive) return false; const resolved = resolveArchiveSelector(archive, cwd); if (!existsSync(resolved.archivePath)) return false; if (!resolved.memberPath) return true; try { readArchiveSelector(resolved); return true; } catch { return false; } }\nfunction searchPathResolvable(value: string, cwd: string): boolean { const selector = splitLineRangeSelector(value), archive = parseArchiveSelector(selector.path); if (archive) return archiveSelectorExists(selector.path, cwd); const sqlite = sqliteSelectorForPath(selector.path, cwd); return !!sqlite || /^(?:skill|agent|artifact|history|issue|local|memory|pr|conflict|omp|rule|mcp|vault):\\/\\//.test(selector.path) || existsSync(resolveToCwd(splitPathLikeGlob(selector.path).basePath, cwd)); }\n\nfunction normalizePaths(pathsValue: string | string[] | undefined, cwd: string): string[] {\n\tconst inputs = Array.isArray(pathsValue) ? (pathsValue.length > 0 ? pathsValue : [\".\"]) : pathsValue === undefined ? [\".\"] : [pathsValue];\n\tconst expanded: string[] = [];\n\tfor (const input of inputs) {\n\t\tconst raw = normalizePathLikeInput(input);\n\t\tif (raw === \"\") { expanded.push(\".\"); continue; }\n\t\tconst resourceLike = /^[a-z]+:\\/\\//i.test(raw) || /^[^:]+\\.(?:zip|jar|tar|tgz|gz|sqlite|db):/i.test(raw);\n\t\tif (existsSync(resolveToCwd(splitLineRangeSelector(raw).path, cwd)) || delimiterInExistingSearchGlobRoot(raw, cwd) || archiveSelectorExists(raw, cwd)) { expanded.push(raw); continue; }\n\t\tconst chunks = raw.split(/[;\\s]+/).map(normalizePathLikeInput).filter(Boolean);\n\t\tconst parts = chunks.flatMap((chunk) => searchPathResolvable(chunk, cwd) ? [chunk] : chunk.split(\",\").map(normalizePathLikeInput).filter(Boolean));\n\t\tconst commaOrSemicolon = /[;,]/.test(raw), canSplit = commaOrSemicolon ? parts.some((part) => searchPathResolvable(part, cwd)) : parts.every((part) => searchPathResolvable(part, cwd));\n\t\tif (parts.length > 1 && canSplit) expanded.push(...parts);\n\t\telse if (resourceLike) expanded.push(raw);\n\t\telse expanded.push(raw);\n\t}\n\treturn expanded;\n}\n\ninterface SearchTarget {\n\tpath: string;\n\tglob?: string;\n\tlineRanges?: SearchLineRange[];\n\tarchive?: ReturnType<typeof parseArchiveSelector>;\n\tsqlite?: SqliteSelector;\n\tinternal?: string;\n}\n\ninterface SearchOutputGroup {\n\tpath: string;\n\tlines: string[];\n}\n\ninterface PagedSearchOutputGroup {\n\ttargetPath: string;\n\tvirtual: boolean;\n\tgroup: SearchOutputGroup;\n}\n\nconst DEFAULT_LIMIT = 20, INTERNAL_SKIP_LIMIT = 2000, MULTI_FILE_PER_FILE_MATCHES = 20, SINGLE_FILE_MATCHES = 200;\n\nfunction normalizeSkip(skip: number | null | undefined): number {\n\tif (skip === undefined || skip === null) return 0;\n\tif (!Number.isFinite(skip) || skip < 0) throw new Error(\"Skip must be a non-negative number\");\n\treturn Math.floor(skip);\n}\nfunction markFileLimit(details: SearchToolDetails, limit: number): void {\n\tdetails.fileLimitReached = true;\n\tdetails.meta = { ...(details.meta ?? {}), limits: { ...(details.meta?.limits ?? {}), fileLimit: limit } };\n}\nfunction fileLimitDetails(limit: number): SearchToolDetails { const details: SearchToolDetails = {}; markFileLimit(details, limit); return details; }\nfunction continuationHint(limit: number, skip: number): string { return `[${limit} matching files shown. Use skip=${skip + limit} to view more.]`; }\nfunction internalCapHint(): string { return `[Search collected the first ${INTERNAL_SKIP_LIMIT} matches before pagination; refine the pattern or path for later results.]`; }\nfunction hasFileLimit(details: unknown): boolean { return typeof details === \"object\" && details !== null && (details as { fileLimitReached?: unknown }).fileLimitReached === true; }\n\nfunction isResourceSelector(value: string): boolean {\n\treturn /^[^:]+\\.(zip|jar|tar|tgz|gz|sqlite|db):/i.test(value) || /^[a-z]+:\\/\\//i.test(value) || value.startsWith(\"skill://\");\n}\n\nfunction normalizeSearchTargets(pathsValue: string | string[] | undefined, cwd: string): SearchTarget[] {\n\treturn normalizePaths(pathsValue, cwd).map((searchPath) => {\n\t\tconst directSqlite = sqliteSelectorForPath(searchPath, cwd);\n\t\tif (directSqlite?.rowId) return { path: searchPath, sqlite: directSqlite };\n\t\tif (archiveSelectorExists(searchPath, cwd)) { const archive = parseArchiveSelector(searchPath)!; return { path: searchPath, archive }; }\n\t\tconst selector = splitLineRangeSelector(searchPath);\n\t\tconst sqliteDirect = sqliteSelectorForPath(selector.path, cwd);\n\t\tif (sqliteDirect) return { path: selector.path, lineRanges: selector.lineRanges, sqlite: sqliteDirect };\n\t\tconst archive = parseArchiveSelector(selector.path);\n\t\tif (archive) return { path: selector.path, lineRanges: selector.lineRanges, archive };\n\t\tif (selector.path.startsWith(\"local://\")) { const sourcePath = resolveInternalSelector(selector.path, cwd); if (sourcePath) { const parsed = splitPathLikeGlob(sourcePath); return { path: parsed.basePath, glob: parsed.glob, lineRanges: selector.lineRanges }; } }\n\t\tif (/^(?:skill|agent|artifact|history|issue|local|memory|pr|conflict|omp|rule|mcp|vault):\\/\\//.test(selector.path)) return { path: selector.path, lineRanges: selector.lineRanges, internal: selector.path };\n\t\tif (isResourceSelector(selector.path)) throw new Error(`Search resource selectors are not supported by this filesystem backend: ${searchPath}`);\n\t\tconst parsed = splitPathLikeGlob(selector.path);\n\t\treturn { path: parsed.basePath, glob: parsed.glob, lineRanges: selector.lineRanges };\n\t});\n}\n\n\nfunction stripExtendedRegexWhitespace(pattern: string): string { let out = \"\", escaped = false, inClass = false, inComment = false; for (const ch of pattern) { if (inComment) { if (ch === \"\\n\" || ch === \"\\r\") inComment = false; continue; } if (escaped) { out += ch; escaped = false; continue; } if (ch === \"\\\\\") { out += ch; escaped = true; continue; } if (ch === \"[\") inClass = true; else if (ch === \"]\") inClass = false; if (!inClass && ch === \"#\") { inComment = true; continue; } if (!inClass && /\\s/.test(ch)) continue; out += ch; } return out; }\nfunction normalizeInlineSearchPattern(pattern: string, ignoreCase: boolean): { pattern: string; flags: string } { const match = pattern.match(/^\\(\\?([imsUx-]+)\\)([\\s\\S]*)$/), flags = new Set<string>(); if (ignoreCase) flags.add(\"i\"); if (match) { const inline = match[1] ?? \"\"; for (const flag of inline) if (flag === \"i\" || flag === \"m\" || flag === \"s\") flags.add(flag); return { pattern: inline.includes(\"x\") ? stripExtendedRegexWhitespace(match[2] ?? \"\") : match[2] ?? \"\", flags: [...flags].join(\"\") }; } return { pattern, flags: [...flags].join(\"\") }; }\n\nasync function searchFileLineRanges(target: SearchTarget, cwd: string, pattern: string, ignoreCase: boolean, contextBefore = 1, contextAfter = 3): Promise<string | undefined> {\n\tif (!target.lineRanges?.length || target.glob || target.archive || target.sqlite || target.internal) return undefined;\n\tconst absolutePath = resolveToCwd(target.path, cwd);\n\tif (!await fsStat(absolutePath).then((stat) => stat.isFile()).catch(() => false)) return undefined;\n\tconst source = (await fsReadFile(absolutePath, \"utf8\")).split(\"\\n\");\n\tconst normalized = normalizeInlineSearchPattern(pattern, ignoreCase);\n\tlet regex: RegExp;\n\ttry { regex = new RegExp(normalized.pattern, normalized.flags); } catch { return undefined; }\n\tconst matchLines = new Set<number>();\n\tsource.forEach((line, index) => { regex.lastIndex = 0; if (regex.test(line)) matchLines.add(index + 1); });\n\tconst outputLines = new Set<number>();\n\tfor (const line of matchLines) for (let n = Math.max(1, line - contextBefore); n <= Math.min(source.length, line + contextAfter); n++) outputLines.add(n);\n\tconst text = [...outputLines].sort((a, b) => a - b).map((n) => `${target.path}${matchLines.has(n) ? \":\" : \"-\"}${n}${matchLines.has(n) ? \":\" : \"-\"} ${source[n - 1] ?? \"\"}`).join(\"\\n\") || \"No matches found\";\n\treturn filterSearchOutputByLineRange(text, target.lineRanges, contextBefore, contextAfter);\n}\n\nfunction groupSearchOutput(text: string): SearchOutputGroup[] {\n\tconst groups: SearchOutputGroup[] = [];\n\tconst byPath = new Map<string, SearchOutputGroup>();\n\tfor (const line of text.split(\"\\n\")) {\n\t\tconst match = line.match(/^(.+?)(?::[^:]+: |:\\d+: |-\\d+- )/);\n\t\tif (!match) continue;\n\t\tconst filePath = match[1]!;\n\t\tlet current = byPath.get(filePath);\n\t\tif (!current) { current = { path: filePath, lines: [] }; byPath.set(filePath, current); groups.push(current); }\n\t\tif (!current.lines.includes(line)) current.lines.push(line);\n\t}\n\treturn groups;\n}\nfunction isVirtualSearchTarget(target: SearchTarget | undefined): boolean { return !!(target?.archive || target?.sqlite || target?.internal); }\n\n\nasync function addHashlineHeadersToSearchOutput(text: string, cwd: string, targetPath: string, hashlineStore: HashlineSnapshotStore): Promise<string> {\n\tconst groups = groupSearchOutput(text);\n\tif (groups.length === 0) return text;\n\tconst searchRoot = resolveToCwd(targetPath, cwd);\n\tconst targetIsFile = await fsStat(searchRoot).then((stat) => stat.isFile()).catch(() => false);\n\tconst rendered: string[] = [];\n\tlet lastDir = \"\";\n\tfor (const group of groups) {\n\t\tlet absolutePath = targetIsFile ? searchRoot : join(searchRoot, group.path);\n\t\ttry {\n\t\t\tawait fsReadFile(absolutePath);\n\t\t} catch {\n\t\t\tabsolutePath = resolvePath(cwd, group.path);\n\t\t}\n\t\ttry {\n\t\t\tconst content = await fsReadFile(absolutePath, \"utf-8\");\n\t\t\tconst snapshot = recordHashlineSnapshot(absolutePath, cwd, content, hashlineStore);\n\t\t\tconst dir = dirname(snapshot.displayPath);\n\t\t\tif (dir !== \".\" && dir !== lastDir) {\n\t\t\t\trendered.push(`# ${dir}/`);\n\t\t\t\tlastDir = dir;\n\t\t\t}\n\t\t\trendered.push(`[${snapshot.displayPath}#${snapshot.tag}]`);\n\t\t\tfor (const line of group.lines) {\n\t\t\t\tconst match = line.match(/^.+?(?::(\\d+): |-(\\d+)- )(.*)$/);\n\t\t\t\tif (match) rendered.push(`${match[1] ? \"*\" : \" \"}${match[1] ?? match[2]}:${match[3] ?? \"\"}`);\n\t\t\t}\n\t\t} catch {\n\t\t\trendered.push(...group.lines);\n\t\t}\n\t}\n\treturn rendered.join(\"\\n\");\n}\n\nfunction applyDefaultSearchContext(text: string, before = 1, after = 3): string {\n\tconst groups = groupSearchOutput(text);\n\tif (groups.length === 0) return text;\n\tconst output: string[] = [];\n\tfor (const group of groups) {\n\t\tconst byLine = new Map<number, { line: string; isMatch: boolean }>();\n\t\tfor (const line of group.lines) {\n\t\t\tconst match = line.match(/^(.+?)(?::(\\d+): |-(\\d+)- )(.*)$/);\n\t\t\tif (!match) continue;\n\t\t\tconst number = Number.parseInt(match[2] ?? match[3] ?? \"0\", 10);\n\t\t\tconst isMatch = match[2] !== undefined;\n\t\t\tconst existing = byLine.get(number);\n\t\t\tif (!existing || (isMatch && !existing.isMatch)) byLine.set(number, { line, isMatch });\n\t\t}\n\t\tconst matchNumbers = [...byLine.values()].filter((item) => item.isMatch).map((item) => Number.parseInt(item.line.match(/(?::(\\d+): |-(\\d+)- )/)?.[1] ?? item.line.match(/-(\\d+)- /)?.[1] ?? \"0\", 10));\n\t\tfor (const [number, item] of [...byLine.entries()].sort((a, b) => a[0] - b[0])) {\n\t\t\tif (item.isMatch || matchNumbers.some((lineNumber) => number >= lineNumber - before && number <= lineNumber + after)) output.push(item.line);\n\t\t}\n\t}\n\treturn output.join(\"\\n\");\n}\n\nfunction isSearchMatchLine(line: string): boolean { return /:\\d+: /.test(line); }\nfunction sliceGroupLinesByMatchCount(lines: readonly string[], matchLimit: number): string[] {\n\tif (matchLimit <= 0) return [];\n\tlet matches = 0; const output: string[] = [];\n\tfor (const line of lines) {\n\t\tif (isSearchMatchLine(line)) { if (matches >= matchLimit) break; matches++; }\n\t\toutput.push(line);\n\t}\n\treturn output;\n}\nfunction formatSearchGroups(groups: SearchOutputGroup[], limit: number, perFileLimit = 20): string {\n\treturn groups.slice(0, Math.max(0, limit)).flatMap((group) => sliceGroupLinesByMatchCount(group.lines, perFileLimit)).join(\"\\n\");\n}\n\nfunction dedupeRenderedSearchOutput(content: string): string {\n\tconst out: string[] = [], seen = new Set<string>(); let header = \"\", sawContinuationHint = false, sawCapHint = false;\n\tfor (const line of content.split(\"\\n\")) {\n\t\tconst h = line.match(/^\\[([^\\]#]+)#[0-9A-F]{4}\\]$/); if (h) header = h[1]!;\n\t\tif (/^\\[\\d+ matching files shown\\. Use skip=\\d+ to view more\\.\\]$/.test(line)) { if (sawContinuationHint) continue; sawContinuationHint = true; }\n\t\tif (line.startsWith(\"[Search collected the first \")) { if (sawCapHint) continue; sawCapHint = true; }\n\t\tconst m = header ? line.match(/^[* ]?(\\d+):/) : undefined;\n\t\tconst key = m ? `${header}:${m[1]}` : \"\"; if (key && seen.has(key)) continue; if (key) seen.add(key); out.push(line);\n\t}\n\treturn out.join(\"\\n\");\n}\nfunction formatSearchCall(\n\targs: { pattern: string; path?: string; paths?: string | string[]; glob?: string; limit?: number } | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.ts\").theme,\n): string {\n\tconst pattern = str(args?.pattern);\n\tconst rawPaths = Array.isArray(args?.paths) ? args.paths.join(\", \") : args?.paths;\n\tconst rawPath = str(args?.path ?? rawPaths);\n\tconst path = rawPath !== null ? shortenPath(rawPath || \".\") : null;\n\tconst glob = str(args?.glob);\n\tconst limit = args?.limit;\n\tconst invalidArg = invalidArgText(theme);\n\tlet text =\n\t\ttheme.fg(\"toolTitle\", theme.bold(\"search\")) +\n\t\t\" \" +\n\t\t(pattern === null ? invalidArg : theme.fg(\"accent\", `/${pattern || \"\"}/`)) +\n\t\ttheme.fg(\"toolOutput\", ` in ${path === null ? invalidArg : path}`);\n\tif (glob) text += theme.fg(\"toolOutput\", ` (${glob})`);\n\tif (limit !== undefined) text += theme.fg(\"toolOutput\", ` limit ${limit}`);\n\treturn text;\n}\n\nexport function createSearchToolDefinition(\n\tcwd: string,\n\toptions?: SearchToolOptions,\n): ToolDefinition<typeof searchSchema, SearchToolDetails | undefined> {\n\tconst grepDefinition = createGrepToolDefinition(cwd, { ...options, nativeCache: false });\n\tconst hashlineStore = options?.hashlineStore ?? createHashlineSnapshotStore();\n\treturn {\n\t\t...grepDefinition,\n\t\tname: \"search\",\n\t\tlabel: \"search\",\n\t\tdescription: \"Search file contents with a regex across files, directories, globs, and internal URLs.\",\n\t\tpromptSnippet: \"Search file contents with regex patterns.\",\n\t\tparameters: searchSchema,\n\t\tasync execute(toolCallId, params, signal, onUpdate, ctx) {\n\t\t\tconst resourceCtx = ctx as InternalResourceContext | undefined;\n\t\t\tif (params.pattern.trim() === \"\") throw new Error(\"Pattern must not be empty\");\n\t\t\tconst targets = normalizeSearchTargets(params.paths, cwd);\n\t\t\tconst ignoreCase = params.i === true || params.case === false;\n\t\t\tconst contextBefore = options?.contextBefore ?? 1, contextAfter = options?.contextAfter ?? 3, searchContext = Math.max(contextBefore, contextAfter);\n\t\t\tconst isSingleFileSearch = targets.length === 1 && await fsStat(resolveToCwd(targets[0]!.path, cwd)).then((stat) => stat.isFile()).catch(() => false);\n\t\t\tconst limit = isSingleFileSearch ? SINGLE_FILE_MATCHES : DEFAULT_LIMIT;\n\t\t\tlet skip = normalizeSkip(params.skip);\n\t\t\tconst singleFileSkipIgnored = skip > 0 && isSingleFileSearch;\n\t\t\tif (singleFileSkipIgnored) skip = 0;\n\t\t\tfor (const target of targets) {\n\t\t\t\tif (target.archive || target.sqlite || target.internal) continue;\n\t\t\t\tif (target.lineRanges && !await fsStat(resolveToCwd(target.path, cwd)).then((stat) => stat.isFile()).catch(() => false)) {\n\t\t\t\t\tthrow new Error(\"Line-range search selectors are only supported for single files\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst results: Awaited<ReturnType<typeof grepDefinition.execute>>[] = [];\n\t\t\tconst groups: PagedSearchOutputGroup[] = [];\n\t\t\tconst skippedMissingPaths: string[] = [];\n\t\t\tconst scopePath = targets.map((target) => target.path).join(\", \") || \".\";\n\t\t\tlet pageFullWithMore = false, internalSearchCapped = false;\n\t\t\tlet remaining = limit;\n\t\t\tconst targetHasMatch = async (target: SearchTarget): Promise<boolean> => {\n\t\t\t\tif (target.archive || target.sqlite || target.internal) {\n\t\t\t\t\tconst archive = target.archive ? resolveArchiveSelector(target.archive, cwd) : undefined;\n\t\t\t\t\tconst text = archive ? searchArchiveSelector(archive, params.pattern, ignoreCase, false, contextBefore, contextAfter) : target.sqlite ? searchSqliteSelector(target.sqlite, params.pattern, ignoreCase, contextBefore, contextAfter) : await searchInternalSelector(target.internal!, cwd, params.pattern, ignoreCase, false, resourceCtx, contextBefore, contextAfter);\n\t\t\t\t\treturn groupSearchOutput(filterSearchOutputByLineRange(text, target.lineRanges, contextBefore, contextAfter)).length > 0;\n\t\t\t\t}\n\t\t\t\tconst ranged = await searchFileLineRanges(target, cwd, params.pattern, ignoreCase, contextBefore, contextAfter);\n\t\t\t\tif (ranged !== undefined) return groupSearchOutput(ranged).length > 0;\n\t\t\t\tconst probe = await grepDefinition.execute(toolCallId, { pattern: params.pattern, path: target.path, glob: target.glob, ignoreCase, literal: false, context: 0, limit: 1, gitignore: params.gitignore }, signal, undefined, ctx);\n\t\t\t\treturn probe.content.some((item) => item.type === \"text\" && item.text && item.text !== \"No matches found\");\n\t\t\t};\n\t\t\tfor (const target of targets) {\n\t\t\t\tif (skip === 0 && remaining <= 0) { if (!target.archive && !target.sqlite && !target.internal && targets.length > 1 && await fsStat(resolveToCwd(target.path, cwd)).then(() => false).catch(() => true)) { skippedMissingPaths.push(target.path); continue; } if (await targetHasMatch(target)) { pageFullWithMore = true; break; } continue; }\n\t\t\t\tif (target.archive || target.sqlite || target.internal) {\n\t\t\t\t\tconst archive = target.archive ? resolveArchiveSelector(target.archive, cwd) : undefined;\n\t\t\t\t\tlet text = archive ? searchArchiveSelector(archive, params.pattern, ignoreCase, false, contextBefore, contextAfter) : target.sqlite ? searchSqliteSelector(target.sqlite, params.pattern, ignoreCase, contextBefore, contextAfter) : await searchInternalSelector(target.internal!, cwd, params.pattern, ignoreCase, false, resourceCtx, contextBefore, contextAfter);\n\t\t\t\t\tif (archive && target.archive) text = text.split(archive.archivePath).join(target.archive.archivePath); if (target.sqlite) text = text.split(target.sqlite.databasePath).join(target.path.match(/^(.+?\\.(?:sqlite3?|db3?))/i)?.[1] ?? target.sqlite.databasePath); text = text || \"No matches found\";\n\t\t\t\t\ttext = filterSearchOutputByLineRange(text, target.lineRanges, contextBefore, contextAfter);\n\t\t\t\t\tconst outputGroups = groupSearchOutput(text);\n\t\t\t\t\tif (skip > 0) groups.push(...outputGroups.map((group) => ({ targetPath: target.path, virtual: isVirtualSearchTarget(target), group })) );\n\t\t\t\t\telse if (outputGroups.length > 0) {\n\t\t\t\t\t\tconst hadMore = outputGroups.length > remaining;\n\t\t\t\t\t\ttext = formatSearchGroups(outputGroups, remaining);\n\t\t\t\t\t\tif (hadMore) { text += `\\n\\n${continuationHint(limit, skip)}`; pageFullWithMore = true; }\n\t\t\t\t\t\tremaining -= Math.min(remaining, outputGroups.length);\n\t\t\t\t\t} else if (text && text !== \"No matches found\") {\n\t\t\t\t\t\tremaining--;\n\t\t\t\t\t} else text = \"No matches found\";\n\t\t\t\t\tif (skip === 0) results.push({ content: [{ type: \"text\" as const, text }], details: undefined });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (targets.length > 1 && await fsStat(resolveToCwd(target.path, cwd)).then(() => false).catch(() => true)) {\n\t\t\t\t\tskippedMissingPaths.push(target.path);\n\t\t\t\t\tresults.push({ content: [{ type: \"text\" as const, text: \"No matches found\" }], details: undefined });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst rangedText = await searchFileLineRanges(target, cwd, params.pattern, ignoreCase, contextBefore, contextAfter);\n\t\t\t\tif (rangedText !== undefined) {\n\t\t\t\t\tlet text = applyDefaultSearchContext(rangedText, contextBefore, contextAfter) || \"No matches found\";\n\t\t\t\t\tconst result = { content: [{ type: \"text\" as const, text }], details: undefined as SearchToolDetails | undefined };\n\t\t\t\t\tresults.push(result);\n\t\t\t\t\tconst outputGroups = groupSearchOutput(text);\n\t\t\t\t\tif (skip > 0) groups.push(...outputGroups.map((group) => ({ targetPath: target.path, virtual: false, group })));\n\t\t\t\t\telse if (!isSingleFileSearch && outputGroups.length > 0) { const hadMore = outputGroups.length > remaining; text = formatSearchGroups(outputGroups, remaining); if (hadMore) { result.details = fileLimitDetails(limit); pageFullWithMore = true; } remaining -= Math.min(remaining, outputGroups.length); result.content = [{ type: \"text\", text }]; }\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst result = await grepDefinition.execute(\n\t\t\t\t\ttoolCallId,\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: params.pattern,\n\t\t\t\t\t\tpath: target.path,\n\t\t\t\t\t\tglob: target.glob,\n\t\t\t\t\t\tignoreCase,\n\t\t\t\t\t\tliteral: false,\n\t\t\t\t\t\tcontext: searchContext,\n\t\t\t\t\t\tmaxCountPerFile: isSingleFileSearch ? SINGLE_FILE_MATCHES : MULTI_FILE_PER_FILE_MATCHES,\n\t\t\t\t\t\tlimit: INTERNAL_SKIP_LIMIT,\n\t\t\t\t\t\tgitignore: params.gitignore,\n\t\t\t\t\t},\n\t\t\t\t\tsignal,\n\t\t\t\t\tonUpdate,\n\t\t\t\t\tctx,\n\t\t\t\t);\n\t\t\t\tresults.push(result);\n\t\t\t\tif (result.details?.matchLimitReached) internalSearchCapped = true;\n\t\t\t\tlet text = result.content\n\t\t\t\t\t.map((item) => (item.type === \"text\" ? item.text : undefined))\n\t\t\t\t\t.filter((text): text is string => typeof text === \"string\")\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\ttext = filterSearchOutputByLineRange(text, target.lineRanges, contextBefore, contextAfter);\n\t\t\t\tif (text !== \"No matches found\") text = applyDefaultSearchContext(text, contextBefore, contextAfter) || \"No matches found\";\n\t\t\t\tconst outputGroups = groupSearchOutput(text);\n\t\t\t\tif (skip > 0) groups.push(...outputGroups.map((group) => ({ targetPath: target.path, virtual: false, group })) );\n\t\t\t\telse if (!isSingleFileSearch && outputGroups.length > 0) {\n\t\t\t\t\tconst hadMore = outputGroups.length > remaining;\n\t\t\t\t\ttext = formatSearchGroups(outputGroups, remaining);\n\t\t\t\t\tif (hadMore) { result.details = { ...(result.details ?? {}), ...fileLimitDetails(limit) }; pageFullWithMore = true; }\n\t\t\t\t\tremaining -= Math.min(remaining, outputGroups.length);\n\t\t\t\t}\n\t\t\t\tresult.content = [{ type: \"text\", text }];\n\t\t\t}\n\n\t\t\tconst details: SearchToolDetails = {};\n\t\t\tfor (const result of results) {\n\t\t\t\tif (result.details?.truncation) details.truncation = result.details.truncation;\n\t\t\t\tif (result.details?.matchLimitReached) details.matchLimitReached = result.details.matchLimitReached;\n\t\t\t\tif (hasFileLimit(result.details)) markFileLimit(details, limit);\n\t\t\t\tif (result.details?.linesTruncated) details.linesTruncated = true;\n\t\t\t}\n\n\t\t\tif (skip > 0) {\n\t\t\t\tconst renderedPages: string[] = [];\n\t\t\t\tlet remainingMatches = limit;\n\t\t\t\tfor (const item of groups.slice(skip)) {\n\t\t\t\t\tif (remainingMatches <= 0) break;\n\t\t\t\t\tconst raw = formatSearchGroups([item.group], 1);\n\t\t\t\t\tremainingMatches--;\n\t\t\t\t\tif (raw) renderedPages.push(item.virtual ? raw : await addHashlineHeadersToSearchOutput(raw, cwd, item.targetPath, hashlineStore));\n\t\t\t\t}\n\t\t\t\tconst hasMorePages = groups.slice(skip + limit).length > 0;\n\t\t\t\tconst cappedBeforeRequestedPage = internalSearchCapped && skip >= groups.length;\n\t\t\t\tconst capNotice = internalSearchCapped && !hasMorePages ? `\\n\\n${internalCapHint()}` : \"\";\n\t\t\t\tconst output = `${renderedPages.join(\"\\n\\n\") || `No more results (skip=${skip})`}${hasMorePages ? `\\n\\n${continuationHint(limit, skip)}` : \"\"}${capNotice}`;\n\t\t\t\tif (hasMorePages || cappedBeforeRequestedPage || capNotice) markFileLimit(details, limit);\n\t\t\t\tconst truncation = truncateHead(output, { maxLines: Number.MAX_SAFE_INTEGER });\n\t\t\t\tlet content = truncation.content;\n\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\tdetails.truncation = truncation;\n\t\t\t\t\tcontent += `\\n\\n[${formatSize(DEFAULT_MAX_BYTES)} combined output limit reached]`;\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: content }],\n\t\t\t\t\tdetails: buildSearchDetails(details, content, cwd, scopePath, skippedMissingPaths),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (results.length === 1) {\n\t\t\t\tconst result = results[0]!;\n\t\t\t\tlet text = result.content\n\t\t\t\t\t.map((item) => (item.type === \"text\" ? item.text : undefined))\n\t\t\t\t\t.filter((value): value is string => typeof value === \"string\")\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\ttext = filterSearchOutputByLineRange(text, targets[0]?.lineRanges, contextBefore, contextAfter);\n\t\t\t\tif (!isVirtualSearchTarget(targets[0]) && text !== \"No matches found\") text = applyDefaultSearchContext(text, contextBefore, contextAfter) || \"No matches found\";\n\t\t\t\tconst skipNotice = singleFileSkipIgnored ? \"\\n\\n[skip is ignored for single-file search; refine the pattern/path or search a directory to page matching files.]\" : \"\";\n\t\t\t\tif (isSingleFileSearch && text !== \"No matches found\") text = formatSearchGroups(groupSearchOutput(text), limit, limit);\n\t\t\t\tif (text && text !== \"No matches found\") {\n\t\t\t\t\tconst renderedText = isVirtualSearchTarget(targets[0]) ? text : await addHashlineHeadersToSearchOutput(text, cwd, targets[0]?.path ?? \".\", hashlineStore);\n\t\t\t\t\tconst pagedText = `${hasFileLimit(result.details) ? `${renderedText}\\n\\n${continuationHint(limit, skip)}` : renderedText}${skipNotice}`;\n\t\t\t\t\tconst truncation = truncateHead(pagedText, { maxLines: Number.MAX_SAFE_INTEGER });\n\t\t\t\t\tif (truncation.truncated) return { ...result, content: [{ type: \"text\", text: `${truncation.content}\\n\\n[${formatSize(DEFAULT_MAX_BYTES)} combined output limit reached]` }], details: buildSearchDetails({ ...(result.details ?? {}), truncation }, truncation.content, cwd, scopePath, skippedMissingPaths) };\n\t\t\t\t\treturn { ...result, content: [{ type: \"text\", text: pagedText }], details: buildSearchDetails(result.details, pagedText, cwd, scopePath, skippedMissingPaths) };\n\t\t\t\t}\n\t\t\t\treturn { ...result, details: buildSearchDetails(result.details, text || \"No matches found\", cwd, scopePath, skippedMissingPaths) };\n\t\t\t}\n\n\t\t\tconst renderedResults = await Promise.all(results.map(async (result, index) => {\n\t\t\t\tlet text = result.content\n\t\t\t\t\t.map((item) => (item.type === \"text\" ? item.text : undefined))\n\t\t\t\t\t.filter((value): value is string => typeof value === \"string\" && value.length > 0)\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\ttext = filterSearchOutputByLineRange(text, targets[index]?.lineRanges, contextBefore, contextAfter);\n\t\t\t\tif (!isVirtualSearchTarget(targets[index]) && text !== \"No matches found\") text = applyDefaultSearchContext(text, contextBefore, contextAfter);\n\t\t\t\tif (!text || text === \"No matches found\") return `# ${targets[index]?.path ?? \".\"}\\n${text}`;\n\t\t\t\treturn isVirtualSearchTarget(targets[index]) ? text : await addHashlineHeadersToSearchOutput(text, cwd, targets[index]?.path ?? \".\", hashlineStore);\n\t\t\t}));\n\t\t\tconst content = dedupeRenderedSearchOutput(`${renderedResults.join(\"\\n\\n\")}${skippedMissingPaths.length > 0 ? `\\n\\n[Skipped missing paths: ${skippedMissingPaths.join(\", \")}]` : \"\"}${pageFullWithMore ? `\\n\\n${continuationHint(limit, skip)}` : \"\"}${internalSearchCapped && !pageFullWithMore ? `\\n\\n${internalCapHint()}` : \"\"}`);\n\t\t\tif (pageFullWithMore || internalSearchCapped) markFileLimit(details, limit);\n\t\t\tconst truncation = truncateHead(content, { maxLines: Number.MAX_SAFE_INTEGER });\n\t\t\tlet output = truncation.content;\n\t\t\tif (truncation.truncated) {\n\t\t\t\tdetails.truncation = truncation;\n\t\t\t\toutput += `\\n\\n[${formatSize(DEFAULT_MAX_BYTES)} combined output limit reached]`;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: output }],\n\t\t\t\tdetails: buildSearchDetails(details, output, cwd, scopePath, skippedMissingPaths),\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(formatSearchCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options: ToolRenderResultOptions, theme, context) {\n\t\t\treturn grepDefinition.renderResult?.(result, options, theme, context) ?? new Text(\"\", 0, 0);\n\t\t},\n\t};\n}\n\nexport function createSearchTool(cwd: string, options?: SearchToolOptions): AgentTool<typeof searchSchema> {\n\treturn wrapToolDefinition(createSearchToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/core/tools/search.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAEN,KAAK,eAAe,EACpB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAuD,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAKhH,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGjF,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,QAAA,MAAM,YAAY;;;;;;;EAYiB,CAAC;AACpC,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;AAC1D,MAAM,MAAM,iBAAiB,GAAG,eAAe,GAAG;IAAE,aAAa,CAAC,EAAE,qBAAqB,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA2N3I,wBAAgB,0BAA0B,CACzC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,iBAAiB,GACzB,cAAc,CAAC,OAAO,YAAY,EAAE,iBAAiB,GAAG,SAAS,CAAC,CAyMpE;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC,OAAO,YAAY,CAAC,CAEzG"}
@@ -1 +1 @@
1
- {"version":3,"file":"structured-output.d.ts","sourceRoot":"","sources":["../../../src/core/tools/structured-output.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAEzE,eAAO,MAAM,2BAA2B,sBAAsB,CAAC;AAE/D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAC7D,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,SAAS,EAAE,CAAC;AACjE,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAE/D,MAAM,WAAW,uBAAuB,CAAC,MAAM,GAAG,OAAO;IACxD,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,2BAA2B;IAC3C,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,2BAA2B,CAAC,UAAU,SAAS,OAAO;IACtE,6BAA6B;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,uBAAuB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IACtD,6DAA6D;IAC7D,MAAM,CAAC,EAAE,2BAA2B,CAAC;IACrC,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAyBD,wBAAgB,6BAA6B,CAAC,MAAM,GAAG,OAAO,KAAK,uBAAuB,CAAC,MAAM,CAAC,CAEjG;AAED,wBAAgB,0BAA0B,CAAC,UAAU,SAAS,OAAO,EACpE,OAAO,EAAE,2BAA2B,CAAC,UAAU,CAAC,GAC9C,cAAc,CAAC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAgChD","sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { AgentToolResult } from \"@earendil-works/pi-agent-core\";\nimport { type Static, type TSchema } from \"typebox\";\nimport { defineTool, type ToolDefinition } from \"../extensions/types.ts\";\n\nexport const STRUCTURED_OUTPUT_TOOL_NAME = \"structured_output\";\n\nexport type JsonPrimitive = string | number | boolean | null;\nexport type JsonValue = JsonPrimitive | JsonObject | JsonValue[];\nexport type JsonObject = { readonly [key: string]: JsonValue };\n\nexport interface StructuredOutputCapture<TValue = unknown> {\n\tvalue: TValue | undefined;\n\tcalled: boolean;\n}\n\nexport interface StructuredOutputFileCapture {\n\toutputPath: string;\n}\n\nexport interface StructuredOutputToolOptions<TSchemaDef extends TSchema> {\n\t/** Tool parameter schema. */\n\tschema: TSchemaDef;\n\t/** In-process result sink for SDK and workflow callers. */\n\tcapture?: StructuredOutputCapture<Static<TSchemaDef>>;\n\t/** Cross-process result sink for subagent child runtimes. */\n\toutput?: StructuredOutputFileCapture;\n\t/** Tool name. Defaults to `structured_output`. */\n\tname?: string;\n}\n\nfunction stringifyParams<TSchemaDef extends TSchema>(params: Static<TSchemaDef>): string {\n\ttry {\n\t\treturn JSON.stringify(params, null, 2);\n\t} catch (error) {\n\t\tthrow new Error(`Structured output must be JSON-serializable: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n}\n\nasync function writePrivateJsonFile(filePath: string, serializedJson: string): Promise<void> {\n\tawait fs.mkdir(path.dirname(filePath), { recursive: true });\n\tawait fs.writeFile(filePath, serializedJson, { mode: 0o600 });\n\t// Re-apply the private mode after writing so pre-existing looser files are tightened too.\n\tawait fs.chmod(filePath, 0o600);\n}\n\nasync function writeCapturedOutput(output: StructuredOutputFileCapture, serializedParams: string): Promise<void> {\n\ttry {\n\t\tawait writePrivateJsonFile(output.outputPath, serializedParams);\n\t} catch (error) {\n\t\tthrow new Error(`Failed to write structured output capture: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n}\n\nexport function createStructuredOutputCapture<TValue = unknown>(): StructuredOutputCapture<TValue> {\n\treturn { value: undefined, called: false };\n}\n\nexport function createStructuredOutputTool<TSchemaDef extends TSchema>(\n\toptions: StructuredOutputToolOptions<TSchemaDef>,\n): ToolDefinition<TSchemaDef, Static<TSchemaDef>> {\n\tconst name = options.name ?? STRUCTURED_OUTPUT_TOOL_NAME;\n\n\treturn defineTool({\n\t\tname,\n\t\tlabel: \"Structured Output\",\n\t\tdescription: \"Return the final machine-readable result.\",\n\t\tpromptSnippet: \"Return final machine-readable output\",\n\t\tpromptGuidelines: [\n\t\t\t`${name} is the final machine-readable result channel; call ${name} exactly once when done.`,\n\t\t\t`Do not write a prose final answer after calling ${name}.`,\n\t\t],\n\t\tparameters: options.schema,\n\t\tmaxResultSizeChars: Infinity,\n\t\tstructuredOutput: true,\n\t\tasync execute(_toolCallId, params): Promise<AgentToolResult<Static<TSchemaDef>>> {\n\t\t\tconst serializedParams = stringifyParams(params);\n\t\t\tif (options.output) {\n\t\t\t\tawait writeCapturedOutput(options.output, serializedParams);\n\t\t\t}\n\t\t\tif (options.capture) {\n\t\t\t\toptions.capture.value = params;\n\t\t\t\toptions.capture.called = true;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: serializedParams }],\n\t\t\t\tdetails: params,\n\t\t\t\tterminate: true,\n\t\t\t};\n\t\t},\n\t});\n}\n"]}
1
+ {"version":3,"file":"structured-output.d.ts","sourceRoot":"","sources":["../../../src/core/tools/structured-output.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAEzE,eAAO,MAAM,2BAA2B,sBAAsB,CAAC;AAE/D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAC7D,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,SAAS,EAAE,CAAC;AACjE,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAE/D,MAAM,WAAW,uBAAuB,CAAC,MAAM,GAAG,OAAO;IACxD,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,2BAA2B;IAC3C,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,2BAA2B,CAAC,UAAU,SAAS,OAAO;IACtE,6BAA6B;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,uBAAuB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IACtD,6DAA6D;IAC7D,MAAM,CAAC,EAAE,2BAA2B,CAAC;IACrC,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAyBD,wBAAgB,6BAA6B,CAAC,MAAM,GAAG,OAAO,KAAK,uBAAuB,CAAC,MAAM,CAAC,CAEjG;AAED,wBAAgB,0BAA0B,CAAC,UAAU,SAAS,OAAO,EACpE,OAAO,EAAE,2BAA2B,CAAC,UAAU,CAAC,GAC9C,cAAc,CAAC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAgChD"}