@nhtio/adk 0.1.0-master-f0aa531d

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 (297) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +3 -0
  3. package/batteries/index.d.ts +28 -0
  4. package/batteries/llm/index.d.ts +11 -0
  5. package/batteries/llm/openai_chat_completions/adapter.cjs +916 -0
  6. package/batteries/llm/openai_chat_completions/adapter.cjs.map +1 -0
  7. package/batteries/llm/openai_chat_completions/adapter.d.ts +101 -0
  8. package/batteries/llm/openai_chat_completions/adapter.mjs +914 -0
  9. package/batteries/llm/openai_chat_completions/adapter.mjs.map +1 -0
  10. package/batteries/llm/openai_chat_completions/exceptions.cjs +89 -0
  11. package/batteries/llm/openai_chat_completions/exceptions.cjs.map +1 -0
  12. package/batteries/llm/openai_chat_completions/exceptions.d.ts +97 -0
  13. package/batteries/llm/openai_chat_completions/exceptions.mjs +81 -0
  14. package/batteries/llm/openai_chat_completions/exceptions.mjs.map +1 -0
  15. package/batteries/llm/openai_chat_completions/helpers.cjs +819 -0
  16. package/batteries/llm/openai_chat_completions/helpers.cjs.map +1 -0
  17. package/batteries/llm/openai_chat_completions/helpers.d.ts +233 -0
  18. package/batteries/llm/openai_chat_completions/helpers.mjs +783 -0
  19. package/batteries/llm/openai_chat_completions/helpers.mjs.map +1 -0
  20. package/batteries/llm/openai_chat_completions/index.d.ts +27 -0
  21. package/batteries/llm/openai_chat_completions/types.cjs +1 -0
  22. package/batteries/llm/openai_chat_completions/types.d.ts +524 -0
  23. package/batteries/llm/openai_chat_completions/types.mjs +0 -0
  24. package/batteries/llm/openai_chat_completions/validation.cjs +190 -0
  25. package/batteries/llm/openai_chat_completions/validation.cjs.map +1 -0
  26. package/batteries/llm/openai_chat_completions/validation.d.ts +31 -0
  27. package/batteries/llm/openai_chat_completions/validation.mjs +187 -0
  28. package/batteries/llm/openai_chat_completions/validation.mjs.map +1 -0
  29. package/batteries/llm/openai_chat_completions.cjs +51 -0
  30. package/batteries/llm/openai_chat_completions.mjs +5 -0
  31. package/batteries/llm/webllm_chat_completions/adapter.cjs +658 -0
  32. package/batteries/llm/webllm_chat_completions/adapter.cjs.map +1 -0
  33. package/batteries/llm/webllm_chat_completions/adapter.d.ts +103 -0
  34. package/batteries/llm/webllm_chat_completions/adapter.mjs +656 -0
  35. package/batteries/llm/webllm_chat_completions/adapter.mjs.map +1 -0
  36. package/batteries/llm/webllm_chat_completions/exceptions.cjs +70 -0
  37. package/batteries/llm/webllm_chat_completions/exceptions.cjs.map +1 -0
  38. package/batteries/llm/webllm_chat_completions/exceptions.d.ts +74 -0
  39. package/batteries/llm/webllm_chat_completions/exceptions.mjs +65 -0
  40. package/batteries/llm/webllm_chat_completions/exceptions.mjs.map +1 -0
  41. package/batteries/llm/webllm_chat_completions/helpers.cjs +38 -0
  42. package/batteries/llm/webllm_chat_completions/helpers.d.ts +6 -0
  43. package/batteries/llm/webllm_chat_completions/helpers.mjs +2 -0
  44. package/batteries/llm/webllm_chat_completions/index.d.ts +25 -0
  45. package/batteries/llm/webllm_chat_completions/types.d.ts +31 -0
  46. package/batteries/llm/webllm_chat_completions/validation.cjs +115 -0
  47. package/batteries/llm/webllm_chat_completions/validation.cjs.map +1 -0
  48. package/batteries/llm/webllm_chat_completions/validation.d.ts +8 -0
  49. package/batteries/llm/webllm_chat_completions/validation.mjs +112 -0
  50. package/batteries/llm/webllm_chat_completions/validation.mjs.map +1 -0
  51. package/batteries/llm/webllm_chat_completions.cjs +50 -0
  52. package/batteries/llm/webllm_chat_completions.mjs +6 -0
  53. package/batteries/llm.cjs +63 -0
  54. package/batteries/llm.mjs +10 -0
  55. package/batteries/storage/flydrive/index.d.ts +167 -0
  56. package/batteries/storage/flydrive.cjs +249 -0
  57. package/batteries/storage/flydrive.cjs.map +1 -0
  58. package/batteries/storage/flydrive.mjs +249 -0
  59. package/batteries/storage/flydrive.mjs.map +1 -0
  60. package/batteries/storage/in_memory/index.d.ts +106 -0
  61. package/batteries/storage/in_memory.cjs +121 -0
  62. package/batteries/storage/in_memory.cjs.map +1 -0
  63. package/batteries/storage/in_memory.mjs +119 -0
  64. package/batteries/storage/in_memory.mjs.map +1 -0
  65. package/batteries/storage/index.d.ts +18 -0
  66. package/batteries/storage/opfs/index.d.ts +299 -0
  67. package/batteries/storage/opfs.cjs +368 -0
  68. package/batteries/storage/opfs.cjs.map +1 -0
  69. package/batteries/storage/opfs.mjs +366 -0
  70. package/batteries/storage/opfs.mjs.map +1 -0
  71. package/batteries/storage.cjs +4 -0
  72. package/batteries/storage.mjs +2 -0
  73. package/batteries/tools/color/index.d.ts +37 -0
  74. package/batteries/tools/color.cjs +659 -0
  75. package/batteries/tools/color.cjs.map +1 -0
  76. package/batteries/tools/color.mjs +655 -0
  77. package/batteries/tools/color.mjs.map +1 -0
  78. package/batteries/tools/comparison/index.d.ts +29 -0
  79. package/batteries/tools/comparison.cjs +171 -0
  80. package/batteries/tools/comparison.cjs.map +1 -0
  81. package/batteries/tools/comparison.mjs +168 -0
  82. package/batteries/tools/comparison.mjs.map +1 -0
  83. package/batteries/tools/data_structure/index.d.ts +30 -0
  84. package/batteries/tools/data_structure.cjs +270 -0
  85. package/batteries/tools/data_structure.cjs.map +1 -0
  86. package/batteries/tools/data_structure.mjs +267 -0
  87. package/batteries/tools/data_structure.mjs.map +1 -0
  88. package/batteries/tools/datetime_extended/index.d.ts +51 -0
  89. package/batteries/tools/datetime_extended.cjs +309 -0
  90. package/batteries/tools/datetime_extended.cjs.map +1 -0
  91. package/batteries/tools/datetime_extended.mjs +302 -0
  92. package/batteries/tools/datetime_extended.mjs.map +1 -0
  93. package/batteries/tools/datetime_math/index.d.ts +36 -0
  94. package/batteries/tools/datetime_math.cjs +175 -0
  95. package/batteries/tools/datetime_math.cjs.map +1 -0
  96. package/batteries/tools/datetime_math.mjs +171 -0
  97. package/batteries/tools/datetime_math.mjs.map +1 -0
  98. package/batteries/tools/encoding/index.d.ts +36 -0
  99. package/batteries/tools/encoding.cjs +156 -0
  100. package/batteries/tools/encoding.cjs.map +1 -0
  101. package/batteries/tools/encoding.mjs +152 -0
  102. package/batteries/tools/encoding.mjs.map +1 -0
  103. package/batteries/tools/formatting/index.d.ts +28 -0
  104. package/batteries/tools/formatting.cjs +120 -0
  105. package/batteries/tools/formatting.cjs.map +1 -0
  106. package/batteries/tools/formatting.mjs +117 -0
  107. package/batteries/tools/formatting.mjs.map +1 -0
  108. package/batteries/tools/geo_basics/index.d.ts +33 -0
  109. package/batteries/tools/geo_basics.cjs +136 -0
  110. package/batteries/tools/geo_basics.cjs.map +1 -0
  111. package/batteries/tools/geo_basics.mjs +132 -0
  112. package/batteries/tools/geo_basics.mjs.map +1 -0
  113. package/batteries/tools/index.d.ts +32 -0
  114. package/batteries/tools/math/index.d.ts +37 -0
  115. package/batteries/tools/math.cjs +136 -0
  116. package/batteries/tools/math.cjs.map +1 -0
  117. package/batteries/tools/math.mjs +133 -0
  118. package/batteries/tools/math.mjs.map +1 -0
  119. package/batteries/tools/memory/index.d.ts +73 -0
  120. package/batteries/tools/memory.cjs +193 -0
  121. package/batteries/tools/memory.cjs.map +1 -0
  122. package/batteries/tools/memory.mjs +187 -0
  123. package/batteries/tools/memory.mjs.map +1 -0
  124. package/batteries/tools/parsing/index.d.ts +47 -0
  125. package/batteries/tools/parsing.cjs +191 -0
  126. package/batteries/tools/parsing.cjs.map +1 -0
  127. package/batteries/tools/parsing.mjs +185 -0
  128. package/batteries/tools/parsing.mjs.map +1 -0
  129. package/batteries/tools/retrievables/index.d.ts +81 -0
  130. package/batteries/tools/retrievables.cjs +215 -0
  131. package/batteries/tools/retrievables.cjs.map +1 -0
  132. package/batteries/tools/retrievables.mjs +209 -0
  133. package/batteries/tools/retrievables.mjs.map +1 -0
  134. package/batteries/tools/standing_instructions/index.d.ts +64 -0
  135. package/batteries/tools/standing_instructions.cjs +126 -0
  136. package/batteries/tools/standing_instructions.cjs.map +1 -0
  137. package/batteries/tools/standing_instructions.mjs +121 -0
  138. package/batteries/tools/standing_instructions.mjs.map +1 -0
  139. package/batteries/tools/statistics/index.d.ts +46 -0
  140. package/batteries/tools/statistics.cjs +253 -0
  141. package/batteries/tools/statistics.cjs.map +1 -0
  142. package/batteries/tools/statistics.mjs +248 -0
  143. package/batteries/tools/statistics.mjs.map +1 -0
  144. package/batteries/tools/string_processing/index.d.ts +29 -0
  145. package/batteries/tools/string_processing.cjs +154 -0
  146. package/batteries/tools/string_processing.cjs.map +1 -0
  147. package/batteries/tools/string_processing.mjs +151 -0
  148. package/batteries/tools/string_processing.mjs.map +1 -0
  149. package/batteries/tools/structured_data/index.d.ts +34 -0
  150. package/batteries/tools/structured_data.cjs +189 -0
  151. package/batteries/tools/structured_data.cjs.map +1 -0
  152. package/batteries/tools/structured_data.mjs +185 -0
  153. package/batteries/tools/structured_data.mjs.map +1 -0
  154. package/batteries/tools/text_analysis/index.d.ts +31 -0
  155. package/batteries/tools/text_analysis.cjs +120 -0
  156. package/batteries/tools/text_analysis.cjs.map +1 -0
  157. package/batteries/tools/text_analysis.mjs +117 -0
  158. package/batteries/tools/text_analysis.mjs.map +1 -0
  159. package/batteries/tools/text_comparison/index.d.ts +28 -0
  160. package/batteries/tools/text_comparison.cjs +96 -0
  161. package/batteries/tools/text_comparison.cjs.map +1 -0
  162. package/batteries/tools/text_comparison.mjs +93 -0
  163. package/batteries/tools/text_comparison.mjs.map +1 -0
  164. package/batteries/tools/time/index.d.ts +27 -0
  165. package/batteries/tools/time.cjs +63 -0
  166. package/batteries/tools/time.cjs.map +1 -0
  167. package/batteries/tools/time.mjs +60 -0
  168. package/batteries/tools/time.mjs.map +1 -0
  169. package/batteries/tools/unit_conversion/index.d.ts +19 -0
  170. package/batteries/tools/unit_conversion.cjs +452 -0
  171. package/batteries/tools/unit_conversion.cjs.map +1 -0
  172. package/batteries/tools/unit_conversion.mjs +450 -0
  173. package/batteries/tools/unit_conversion.mjs.map +1 -0
  174. package/batteries/tools.cjs +80 -0
  175. package/batteries/tools.mjs +21 -0
  176. package/batteries.cjs +142 -0
  177. package/batteries.mjs +30 -0
  178. package/chunk-KmRHZBOW.js +35 -0
  179. package/common-DeZaonK1.mjs +208 -0
  180. package/common-DeZaonK1.mjs.map +1 -0
  181. package/common-Od8edUXU.js +232 -0
  182. package/common-Od8edUXU.js.map +1 -0
  183. package/common.cjs +31 -0
  184. package/common.d.ts +108 -0
  185. package/common.mjs +8 -0
  186. package/dispatch_runner-9j6bXHL3.mjs +1609 -0
  187. package/dispatch_runner-9j6bXHL3.mjs.map +1 -0
  188. package/dispatch_runner-CsoH0nld.js +1627 -0
  189. package/dispatch_runner-CsoH0nld.js.map +1 -0
  190. package/dispatch_runner.cjs +3 -0
  191. package/dispatch_runner.d.ts +17 -0
  192. package/dispatch_runner.mjs +2 -0
  193. package/exceptions-D5YrO9Vm.js +280 -0
  194. package/exceptions-D5YrO9Vm.js.map +1 -0
  195. package/exceptions-NrzIHw_R.mjs +244 -0
  196. package/exceptions-NrzIHw_R.mjs.map +1 -0
  197. package/exceptions.cjs +33 -0
  198. package/exceptions.d.ts +52 -0
  199. package/exceptions.mjs +3 -0
  200. package/factories.cjs +4 -0
  201. package/factories.d.ts +39 -0
  202. package/factories.mjs +2 -0
  203. package/forge.cjs +9 -0
  204. package/forge.d.ts +49 -0
  205. package/forge.mjs +5 -0
  206. package/guards.cjs +96 -0
  207. package/guards.cjs.map +1 -0
  208. package/guards.d.ts +83 -0
  209. package/guards.mjs +72 -0
  210. package/guards.mjs.map +1 -0
  211. package/index.cjs +107 -0
  212. package/index.cjs.map +1 -0
  213. package/index.d.ts +18 -0
  214. package/index.mjs +31 -0
  215. package/index.mjs.map +1 -0
  216. package/lib/classes/artifact_tool.d.ts +129 -0
  217. package/lib/classes/base_exception.d.ts +83 -0
  218. package/lib/classes/identity.d.ts +71 -0
  219. package/lib/classes/media.d.ts +326 -0
  220. package/lib/classes/memory.d.ts +72 -0
  221. package/lib/classes/message.d.ts +137 -0
  222. package/lib/classes/registry.d.ts +79 -0
  223. package/lib/classes/retrievable.d.ts +100 -0
  224. package/lib/classes/spooled_artifact.d.ts +296 -0
  225. package/lib/classes/spooled_json_artifact.d.ts +158 -0
  226. package/lib/classes/spooled_markdown_artifact.d.ts +202 -0
  227. package/lib/classes/thought.d.ts +142 -0
  228. package/lib/classes/tokenizable.d.ts +124 -0
  229. package/lib/classes/tool.d.ts +228 -0
  230. package/lib/classes/tool_call.d.ts +190 -0
  231. package/lib/classes/tool_registry.d.ts +159 -0
  232. package/lib/classes/turn_gate.d.ts +109 -0
  233. package/lib/contracts/dispatch_context.d.ts +345 -0
  234. package/lib/contracts/media_reader.d.ts +60 -0
  235. package/lib/contracts/spool_reader.d.ts +80 -0
  236. package/lib/contracts/spooled_artifact_constructor.d.ts +38 -0
  237. package/lib/contracts/turn_runner_config.d.ts +101 -0
  238. package/lib/contracts/turn_runner_context.d.ts +267 -0
  239. package/lib/dispatch_runner.d.ts +98 -0
  240. package/lib/exceptions/runtime.d.ts +370 -0
  241. package/lib/helpers/media_readers.d.ts +39 -0
  242. package/lib/turn_runner.d.ts +144 -0
  243. package/lib/types/dispatch_context.d.ts +233 -0
  244. package/lib/types/dispatch_runner.d.ts +387 -0
  245. package/lib/types/turn_runner.d.ts +322 -0
  246. package/lib/utils/canonical_json.d.ts +18 -0
  247. package/lib/utils/exceptions.d.ts +78 -0
  248. package/lib/utils/guards.d.ts +32 -0
  249. package/lib/utils/validation.d.ts +77 -0
  250. package/package.json +334 -0
  251. package/runtime-BJVkrGQe.js +519 -0
  252. package/runtime-BJVkrGQe.js.map +1 -0
  253. package/runtime-CrEPIFgr.mjs +346 -0
  254. package/runtime-CrEPIFgr.mjs.map +1 -0
  255. package/skills/adk-assembly/SKILL.md +109 -0
  256. package/skills/adk-assembly/references/assembly-contract.md +66 -0
  257. package/skills/adk-assembly/references/executors-tools-pipelines-events.md +113 -0
  258. package/skills/adk-assembly/references/first-integration.md +93 -0
  259. package/skills/adk-assembly/references/storage-and-context.md +102 -0
  260. package/spooled_artifact-C5ZtGxuJ.mjs +544 -0
  261. package/spooled_artifact-C5ZtGxuJ.mjs.map +1 -0
  262. package/spooled_artifact-Cm9Te22K.js +568 -0
  263. package/spooled_artifact-Cm9Te22K.js.map +1 -0
  264. package/spooled_artifact.cjs +7 -0
  265. package/spooled_artifact.d.ts +40 -0
  266. package/spooled_artifact.mjs +3 -0
  267. package/spooled_markdown_artifact-BpUJol0W.mjs +771 -0
  268. package/spooled_markdown_artifact-BpUJol0W.mjs.map +1 -0
  269. package/spooled_markdown_artifact-RRB113sy.js +786 -0
  270. package/spooled_markdown_artifact-RRB113sy.js.map +1 -0
  271. package/thought-CDb457b4.mjs +470 -0
  272. package/thought-CDb457b4.mjs.map +1 -0
  273. package/thought-DuN2PgdO.js +494 -0
  274. package/thought-DuN2PgdO.js.map +1 -0
  275. package/tool-COSeH8I6.js +302 -0
  276. package/tool-COSeH8I6.js.map +1 -0
  277. package/tool-D2WB1EA1.mjs +296 -0
  278. package/tool-D2WB1EA1.mjs.map +1 -0
  279. package/tool_call-BKyyxGaZ.mjs +578 -0
  280. package/tool_call-BKyyxGaZ.mjs.map +1 -0
  281. package/tool_call-DFgzcVcU.js +608 -0
  282. package/tool_call-DFgzcVcU.js.map +1 -0
  283. package/tool_registry-Dkfprsck.js +641 -0
  284. package/tool_registry-Dkfprsck.js.map +1 -0
  285. package/tool_registry-DqLOyGyG.mjs +592 -0
  286. package/tool_registry-DqLOyGyG.mjs.map +1 -0
  287. package/turn_runner-CMm2BHdX.js +615 -0
  288. package/turn_runner-CMm2BHdX.js.map +1 -0
  289. package/turn_runner-y7eyEcJH.mjs +603 -0
  290. package/turn_runner-y7eyEcJH.mjs.map +1 -0
  291. package/turn_runner.cjs +3 -0
  292. package/turn_runner.d.ts +21 -0
  293. package/turn_runner.mjs +2 -0
  294. package/types.cjs +1 -0
  295. package/types.d.ts +56 -0
  296. package/types.mjs +0 -0
  297. package/vite-env.d.ts +23 -0
@@ -0,0 +1,771 @@
1
+ import { c as isObject, s as isInstanceOf } from "./tool_registry-DqLOyGyG.mjs";
2
+ import { i as ArtifactTool, n as defaultSerialise, t as SpooledArtifact } from "./spooled_artifact-C5ZtGxuJ.mjs";
3
+ import { validator } from "@nhtio/validation";
4
+ import JSON5 from "json5";
5
+ import { JSONPath } from "jsonpath-plus";
6
+ import { remark } from "remark";
7
+ import { visit } from "unist-util-visit";
8
+ import { load } from "js-yaml";
9
+ import remarkGfm from "remark-gfm";
10
+ import { toString } from "mdast-util-to-string";
11
+ import remarkFrontmatter from "remark-frontmatter";
12
+ //#region src/lib/classes/spooled_json_artifact.ts
13
+ /**
14
+ * Detects the {@link JsonArtifactFormat} of a raw string.
15
+ *
16
+ * @remarks
17
+ * Detection strategy (in order):
18
+ * 1. If the content parses as strict JSON → `json`.
19
+ * 2. If every non-empty line parses as strict JSON → `jsonl`.
20
+ * 3. If the content parses as JSON5 → `json5`.
21
+ * 4. Otherwise throws.
22
+ *
23
+ * Strict JSON is tried before JSON5 so that well-formed JSON files are not unnecessarily
24
+ * classified as JSON5.
25
+ *
26
+ * @param content - The full artifact text to inspect.
27
+ * @returns The inferred format.
28
+ * @throws `Error` when the content cannot be classified as any supported JSON format.
29
+ */
30
+ function inferFormat(content) {
31
+ const trimmed = content.trim();
32
+ try {
33
+ JSON.parse(trimmed);
34
+ return "json";
35
+ } catch {}
36
+ const nonEmptyLines = trimmed.split("\n").filter((l) => l.trim().length > 0);
37
+ if (nonEmptyLines.length > 0 && nonEmptyLines.every((l) => {
38
+ try {
39
+ JSON.parse(l);
40
+ return true;
41
+ } catch {
42
+ return false;
43
+ }
44
+ })) return "jsonl";
45
+ try {
46
+ JSON5.parse(trimmed);
47
+ return "json5";
48
+ } catch {}
49
+ throw new Error("Unable to infer JSON format: content is not valid JSON, JSONL, NDJSON, or JSON5");
50
+ }
51
+ /**
52
+ * A {@link @nhtio/adk!SpooledArtifact} specialisation that adds JSON-aware read operations.
53
+ *
54
+ * @typeParam T - The expected shape of each parsed record. Defaults to `unknown`.
55
+ *
56
+ * @remarks
57
+ * Construct with an optional `format` hint. When omitted the format is auto-detected on first
58
+ * access by reading the full artifact and running {@link inferFormat}. Once detected (or
59
+ * provided), the format is cached for the lifetime of the instance.
60
+ *
61
+ * All JSON methods are async, consistent with {@link @nhtio/adk!SpooledArtifact}.
62
+ *
63
+ * Path-based methods (`json_get`, `json_filter`, `json_pluck`) use
64
+ * [JSONPath-Plus](https://github.com/JSONPath-Plus/JSONPath) expressions. Full JSONPath syntax
65
+ * is supported, including recursive descent (`..`), filter expressions (`[?(@.age > 18)]`),
66
+ * and union selectors.
67
+ */
68
+ var SpooledJsonArtifact = class SpooledJsonArtifact extends SpooledArtifact {
69
+ #format;
70
+ #parsed;
71
+ /**
72
+ * @param reader - The backing store to read from.
73
+ * @param format - Optional format hint. When omitted, the format is inferred on first access.
74
+ */
75
+ constructor(reader, format) {
76
+ super(reader);
77
+ this.#format = format;
78
+ }
79
+ /**
80
+ * Returns `true` if `value` is a {@link SpooledJsonArtifact} instance.
81
+ *
82
+ * @remarks
83
+ * Uses the cross-realm-safe {@link @nhtio/adk!isInstanceOf} guard: `instanceof` first, then
84
+ * `Symbol.hasInstance`, then a `constructor.name` fallback. Matches the pattern used by every
85
+ * other class guard in the ADK; safe against the dual-module-copy case where two distinct
86
+ * `SpooledJsonArtifact` classes coexist in the same realm.
87
+ *
88
+ * @param value - The value to test.
89
+ * @returns `true` when `value` is a {@link SpooledJsonArtifact} instance.
90
+ */
91
+ static isSpooledJsonArtifact(value) {
92
+ return isInstanceOf(value, "SpooledJsonArtifact", SpooledJsonArtifact);
93
+ }
94
+ /**
95
+ * The JSON-specific artifact-query descriptors this class adds on top of the base set.
96
+ *
97
+ * @remarks
98
+ * Lists `artifact_json_type`, `artifact_json_keys`, `artifact_json_length`,
99
+ * `artifact_json_get`, `artifact_json_filter`, `artifact_json_slice`, `artifact_json_pluck`.
100
+ * The base seven descriptors (`artifact_head`, etc.) are NOT included here — they are
101
+ * forged separately by {@link SpooledJsonArtifact.forgeTools}, which calls
102
+ * `SpooledArtifact.forgeTools(ctx)` to produce the base-narrowed tools and then registers
103
+ * its own JSON tools on the result. Downstream consumers building custom subclasses
104
+ * should follow the same pattern: own only your own descriptors; override `forgeTools` to
105
+ * compose with the base output.
106
+ */
107
+ static toolMethods = Object.freeze([
108
+ {
109
+ name: "artifact_json_type",
110
+ method: "json_type",
111
+ description: "Return the JSON format (json | json5 | jsonl | ndjson) of a JSON artifact produced earlier in this turn.",
112
+ argsSchema: validator.object({})
113
+ },
114
+ {
115
+ name: "artifact_json_keys",
116
+ method: "json_keys",
117
+ description: "Return the top-level keys of a JSON artifact produced earlier in this turn.",
118
+ argsSchema: validator.object({})
119
+ },
120
+ {
121
+ name: "artifact_json_length",
122
+ method: "json_length",
123
+ description: "Return the record count of a JSON artifact produced earlier in this turn (1 for json/json5; line count for jsonl/ndjson).",
124
+ argsSchema: validator.object({})
125
+ },
126
+ {
127
+ name: "artifact_json_get",
128
+ method: "json_get",
129
+ description: "Evaluate a JSONPath expression against a JSON artifact produced earlier in this turn.",
130
+ argsSchema: validator.object({ path: validator.string().required().description("JSONPath expression, e.g. '$.user.name'.") })
131
+ },
132
+ {
133
+ name: "artifact_json_filter",
134
+ method: "json_filter",
135
+ description: "Return records of a JSON artifact (produced earlier in this turn) matched by a JSONPath filter.",
136
+ argsSchema: validator.object({ path: validator.string().required().description("JSONPath filter expression, e.g. '$[?(@.age>18)]'.") })
137
+ },
138
+ {
139
+ name: "artifact_json_slice",
140
+ method: "json_slice",
141
+ description: "Return a slice of records by index range from a JSON artifact produced earlier in this turn.",
142
+ argsSchema: validator.object({
143
+ start: validator.number().integer().min(0).optional().description("Start index (inclusive)."),
144
+ end: validator.number().integer().min(0).optional().description("End index (exclusive).")
145
+ })
146
+ },
147
+ {
148
+ name: "artifact_json_pluck",
149
+ method: "json_pluck",
150
+ description: "Return all values matched by a JSONPath expression across every record of a JSON artifact produced earlier in this turn.",
151
+ argsSchema: validator.object({ path: validator.string().required().description("JSONPath expression, e.g. '$..name'.") })
152
+ }
153
+ ]);
154
+ /**
155
+ * Forges base-class tools plus JSON-specific tools narrowed to {@link SpooledJsonArtifact}.
156
+ *
157
+ * @remarks
158
+ * Standard subclass extension pattern: call `SpooledArtifact.forgeTools(ctx)` to produce
159
+ * the base seven `artifact_*` tools narrowed to any `SpooledArtifact` in the turn, then
160
+ * register one `ArtifactTool` per JSON-specific descriptor narrowed to JSON artifacts.
161
+ * Downstream consumers building their own subclasses should follow the same shape.
162
+ */
163
+ static forgeTools(ctx) {
164
+ const registry = SpooledArtifact.forgeTools(ctx);
165
+ const requires = SpooledJsonArtifact;
166
+ const compatibleIds = [...ctx.turnToolCalls].filter((tc) => !tc.fromArtifactTool && isInstanceOf(tc.results, requires.name, requires)).map((tc) => tc.id);
167
+ if (compatibleIds.length === 0) return registry;
168
+ for (const descriptor of this.toolMethods) {
169
+ const callIdSchema = validator.string().valid(...compatibleIds).required().description("ToolCall id of the artifact to query.");
170
+ const argsSchema = (descriptor.argsSchema ?? validator.object({})).append({ callId: callIdSchema });
171
+ const tool = new ArtifactTool({
172
+ name: descriptor.name,
173
+ description: descriptor.description,
174
+ inputSchema: argsSchema,
175
+ ephemeral: true,
176
+ onCollision: "replace",
177
+ handler: async (rawArgs, ctxInner) => {
178
+ const args = rawArgs;
179
+ const tc = [...ctxInner.turnToolCalls].find((t) => t.id === args.callId);
180
+ if (!tc) return `Error: no tool call with id ${args.callId} in this turn`;
181
+ const artifact = tc.results;
182
+ if (!isInstanceOf(artifact, requires.name, requires)) return `Error: tool call ${args.callId} results are not a ${requires.name} instance`;
183
+ const methodArgs = [];
184
+ if (descriptor.method === "json_get" || descriptor.method === "json_filter" || descriptor.method === "json_pluck") methodArgs.push(args.path);
185
+ else if (descriptor.method === "json_slice") methodArgs.push(args.start, args.end);
186
+ const fn = artifact[descriptor.method];
187
+ if (typeof fn !== "function") return `Error: artifact has no method ${descriptor.method}`;
188
+ const result = await Promise.resolve(fn.apply(artifact, methodArgs));
189
+ return (descriptor.serialise ?? defaultSerialise)(result);
190
+ }
191
+ });
192
+ registry.register(tool);
193
+ }
194
+ return registry;
195
+ }
196
+ /**
197
+ * Resolves and caches the detected or provided format.
198
+ */
199
+ async #resolveFormat() {
200
+ if (this.#format !== void 0) return this.#format;
201
+ const lines = await this.cat();
202
+ this.#format = inferFormat(lines.join("\n"));
203
+ return this.#format;
204
+ }
205
+ /**
206
+ * Parses and caches all records from the artifact.
207
+ *
208
+ * @remarks
209
+ * For `json`/`json5` format: returns a single-element array containing the parsed root value.
210
+ * For `jsonl`/`ndjson` format: returns one element per non-empty line.
211
+ */
212
+ async #resolveRecords() {
213
+ if (this.#parsed !== void 0) return this.#parsed;
214
+ const format = await this.#resolveFormat();
215
+ const lines = await this.cat();
216
+ if (format === "json") this.#parsed = [JSON.parse(lines.join("\n"))];
217
+ else if (format === "json5") this.#parsed = [JSON5.parse(lines.join("\n"))];
218
+ else this.#parsed = lines.filter((l) => l.trim().length > 0).map((l) => JSON.parse(l));
219
+ return this.#parsed;
220
+ }
221
+ /**
222
+ * Returns the detected or provided format for this artifact.
223
+ *
224
+ * @returns One of `'json'`, `'json5'`, `'jsonl'`, or `'ndjson'`.
225
+ */
226
+ async json_type() {
227
+ return this.#resolveFormat();
228
+ }
229
+ /**
230
+ * Returns the top-level keys of the parsed content.
231
+ *
232
+ * @remarks
233
+ * - For `json`/`json5`: returns the keys of the root object, or `undefined` when the root is
234
+ * not a plain object (e.g. an array or scalar).
235
+ * - For `jsonl`/`ndjson`: returns the union of keys across all records that are plain objects.
236
+ * Duplicate keys are deduplicated.
237
+ *
238
+ * @returns Array of key strings, or `undefined` when no object keys are present.
239
+ */
240
+ async json_keys() {
241
+ const records = await this.#resolveRecords();
242
+ const format = await this.#resolveFormat();
243
+ if (format === "json" || format === "json5") {
244
+ const root = records[0];
245
+ if (isObject(root)) return Object.keys(root);
246
+ return;
247
+ }
248
+ const keySet = /* @__PURE__ */ new Set();
249
+ for (const record of records) if (isObject(record)) for (const key of Object.keys(record)) keySet.add(key);
250
+ return keySet.size > 0 ? Array.from(keySet) : void 0;
251
+ }
252
+ /**
253
+ * Returns the total number of records in the artifact.
254
+ *
255
+ * @remarks
256
+ * - For `json`/`json5`: always `1` (the entire artifact is a single value).
257
+ * - For `jsonl`/`ndjson`: the number of non-empty lines.
258
+ *
259
+ * @returns The record count.
260
+ */
261
+ async json_length() {
262
+ return (await this.#resolveRecords()).length;
263
+ }
264
+ /**
265
+ * Evaluates a JSONPath expression against the parsed content.
266
+ *
267
+ * @remarks
268
+ * Uses [JSONPath-Plus](https://github.com/JSONPath-Plus/JSONPath). Full JSONPath syntax is
269
+ * supported: recursive descent (`$..*`), filter expressions (`$[?(@.age > 18)]`), union
270
+ * selectors, and more.
271
+ *
272
+ * - For `json`/`json5`: evaluates the expression against the root value.
273
+ * - For `jsonl`/`ndjson`: evaluates the expression against each record and returns a flat
274
+ * array of all matches across all records.
275
+ *
276
+ * @param path - A JSONPath expression (e.g. `'$.user.address.city'`, `'$..name'`).
277
+ * @returns Array of matched values. Empty array when no matches are found.
278
+ */
279
+ async json_get(path) {
280
+ const records = await this.#resolveRecords();
281
+ const format = await this.#resolveFormat();
282
+ if (format === "json" || format === "json5") return JSONPath({
283
+ path,
284
+ json: records[0]
285
+ });
286
+ return records.flatMap((r) => JSONPath({
287
+ path,
288
+ json: r
289
+ }));
290
+ }
291
+ /**
292
+ * Returns a slice of the parsed records by index range.
293
+ *
294
+ * @remarks
295
+ * For `json`/`json5`: always returns `[root]` — the artifact is a single record so slicing is
296
+ * not meaningful. For `jsonl`/`ndjson`: behaves like `Array.prototype.slice`.
297
+ *
298
+ * @param start - Start index (inclusive). Defaults to `0`.
299
+ * @param end - End index (exclusive). Defaults to the record count.
300
+ * @returns Array of sliced records.
301
+ */
302
+ async json_slice(start, end) {
303
+ const records = await this.#resolveRecords();
304
+ const format = await this.#resolveFormat();
305
+ if (format === "json" || format === "json5") return records;
306
+ return records.slice(start, end);
307
+ }
308
+ /**
309
+ * Returns records matched by a JSONPath filter expression.
310
+ *
311
+ * @remarks
312
+ * Evaluates `path` against each record and returns those for which the expression produces at
313
+ * least one match. For `json`/`json5`, evaluates against the root value and returns it in an
314
+ * array if matched.
315
+ *
316
+ * @param path - A JSONPath expression (e.g. `'$[?(@.status === "active")]'`).
317
+ * @returns Array of matching records.
318
+ */
319
+ async json_filter(path) {
320
+ return (await this.#resolveRecords()).filter((r) => {
321
+ const matches = JSONPath({
322
+ path,
323
+ json: r
324
+ });
325
+ return Array.isArray(matches) && matches.length > 0;
326
+ });
327
+ }
328
+ /**
329
+ * Returns all values matched by a JSONPath expression across every record.
330
+ *
331
+ * @remarks
332
+ * Convenience over {@link SpooledJsonArtifact.json_get} with an identical signature — use
333
+ * whichever name better communicates intent at the call site. `json_pluck` reads well for
334
+ * extracting a single field column; `json_get` reads well for structured queries.
335
+ *
336
+ * @param path - A JSONPath expression (e.g. `'$..name'`).
337
+ * @returns Array of matched values.
338
+ */
339
+ async json_pluck(path) {
340
+ return this.json_get(path);
341
+ }
342
+ };
343
+ //#endregion
344
+ //#region src/lib/classes/spooled_markdown_artifact.ts
345
+ /**
346
+ * Returns a configured remark processor with frontmatter and GFM support.
347
+ */
348
+ function processor() {
349
+ return remark().use(remarkFrontmatter).use(remarkGfm);
350
+ }
351
+ /**
352
+ * Parses a heading line (e.g. `## My Heading`) and returns `{ depth, text }`, or `null` when
353
+ * the line is not an ATX heading.
354
+ */
355
+ function parseHeadingLine(line) {
356
+ const match = /^(#{1,6})\s+(.*)$/.exec(line);
357
+ if (!match) return null;
358
+ return {
359
+ depth: match[1].length,
360
+ text: match[2].trim()
361
+ };
362
+ }
363
+ /**
364
+ * Returns the length of a fence marker at the start of `line` (3+), or `0` if the line is not
365
+ * a fence opener/closer. Handles both backtick (` ``` `) and tilde (`~~~`) fences.
366
+ */
367
+ function fenceLength(line) {
368
+ const trimmed = line.trimStart();
369
+ const match = /^(`{3,}|~{3,})/.exec(trimmed);
370
+ return match ? match[1].length : 0;
371
+ }
372
+ /**
373
+ * Extracts the language identifier from a fence opener line (e.g. ` ```ts ` → `'ts'`), or
374
+ * `null` when none is present.
375
+ */
376
+ function fenceLang(line) {
377
+ const trimmed = line.trimStart();
378
+ const match = /^(?:`{3,}|~{3,})(\S+)/.exec(trimmed);
379
+ return match ? match[1] : null;
380
+ }
381
+ /**
382
+ * A {@link @nhtio/adk!SpooledArtifact} specialisation that adds markdown-aware structural queries.
383
+ *
384
+ * @remarks
385
+ * Designed for large markdown documents where loading the full content into memory is
386
+ * impractical. The structural index (heading positions, code block positions) is built by a
387
+ * single line-by-line scan of the {@link @nhtio/adk!SpoolReader} without retaining any content. Only the
388
+ * tiny metadata index and the parsed frontmatter object are cached.
389
+ *
390
+ * Content retrieval is always bounded — use `cat(start, end)` or the `startLine`/`endLine`
391
+ * parameters on inline methods to fetch only the lines you need.
392
+ *
393
+ * Inline methods (`md_links`, `md_images`, `md_text`, `md_ast`) accept optional line-range
394
+ * arguments. Without a range they read the full document — documented trade-off, caller
395
+ * responsibility to bound the range for large documents.
396
+ *
397
+ * The processor always applies `remark-gfm` (tables, task lists, strikethrough, autolinks)
398
+ * in addition to standard CommonMark and YAML frontmatter.
399
+ */
400
+ var SpooledMarkdownArtifact = class SpooledMarkdownArtifact extends SpooledArtifact {
401
+ #index;
402
+ #frontmatter;
403
+ /**
404
+ * @param reader - The backing store to read from.
405
+ */
406
+ constructor(reader) {
407
+ super(reader);
408
+ }
409
+ /**
410
+ * Returns `true` if `value` is a {@link SpooledMarkdownArtifact} instance.
411
+ *
412
+ * @remarks
413
+ * Uses the cross-realm-safe {@link @nhtio/adk!isInstanceOf} guard: `instanceof` first, then
414
+ * `Symbol.hasInstance`, then a `constructor.name` fallback. Matches the pattern used by every
415
+ * other class guard in the ADK; safe against the dual-module-copy case where two distinct
416
+ * `SpooledMarkdownArtifact` classes coexist in the same realm.
417
+ */
418
+ static isSpooledMarkdownArtifact(value) {
419
+ return isInstanceOf(value, "SpooledMarkdownArtifact", SpooledMarkdownArtifact);
420
+ }
421
+ /**
422
+ * The markdown-specific artifact-query descriptors this class adds on top of the base set.
423
+ *
424
+ * @remarks
425
+ * Lists `artifact_md_frontmatter`, `artifact_md_headings`, `artifact_md_code_blocks`,
426
+ * `artifact_md_sections`, `artifact_md_links`, `artifact_md_images`, `artifact_md_text`,
427
+ * `artifact_md_ast`. The base seven descriptors (`artifact_head`, etc.) are NOT included
428
+ * here — they are forged separately by {@link SpooledMarkdownArtifact.forgeTools}, which
429
+ * calls `SpooledArtifact.forgeTools(ctx)` to produce the base-narrowed tools and then
430
+ * registers its own markdown tools on the result. Downstream consumers building custom
431
+ * subclasses should follow the same pattern: own only your own descriptors; override
432
+ * `forgeTools` to compose with the base output.
433
+ */
434
+ static toolMethods = Object.freeze([
435
+ {
436
+ name: "artifact_md_frontmatter",
437
+ method: "md_frontmatter",
438
+ description: "Return parsed YAML frontmatter (or undefined) from a markdown artifact produced earlier in this turn.",
439
+ argsSchema: validator.object({})
440
+ },
441
+ {
442
+ name: "artifact_md_headings",
443
+ method: "md_headings",
444
+ description: "Return all headings, optionally filtered by depth, from a markdown artifact produced earlier in this turn.",
445
+ argsSchema: validator.object({ depth: validator.number().integer().min(1).max(6).optional().description("ATX heading depth (1-6).") })
446
+ },
447
+ {
448
+ name: "artifact_md_code_blocks",
449
+ method: "md_code_blocks",
450
+ description: "Return all fenced code block entries, optionally filtered by language, from a markdown artifact produced earlier in this turn.",
451
+ argsSchema: validator.object({ lang: validator.string().optional().description("Language identifier. Pass empty string to match blocks with no lang.") })
452
+ },
453
+ {
454
+ name: "artifact_md_sections",
455
+ method: "md_sections",
456
+ description: "Return document sections (line-range metadata only) from a markdown artifact produced earlier in this turn.",
457
+ argsSchema: validator.object({ depth: validator.number().integer().min(1).max(6).optional().description("ATX heading depth (1-6).") })
458
+ },
459
+ {
460
+ name: "artifact_md_links",
461
+ method: "md_links",
462
+ description: "Return all inline and reference links within the given line range from a markdown artifact produced earlier in this turn.",
463
+ argsSchema: validator.object({
464
+ startLine: validator.number().integer().min(0).optional().description("Start line (inclusive)."),
465
+ endLine: validator.number().integer().min(0).optional().description("End line (exclusive).")
466
+ })
467
+ },
468
+ {
469
+ name: "artifact_md_images",
470
+ method: "md_images",
471
+ description: "Return all images within the given line range from a markdown artifact produced earlier in this turn.",
472
+ argsSchema: validator.object({
473
+ startLine: validator.number().integer().min(0).optional().description("Start line (inclusive)."),
474
+ endLine: validator.number().integer().min(0).optional().description("End line (exclusive).")
475
+ })
476
+ },
477
+ {
478
+ name: "artifact_md_text",
479
+ method: "md_text",
480
+ description: "Return plain text with markup stripped, for the given line range, from a markdown artifact produced earlier in this turn.",
481
+ argsSchema: validator.object({
482
+ startLine: validator.number().integer().min(0).optional().description("Start line (inclusive)."),
483
+ endLine: validator.number().integer().min(0).optional().description("End line (exclusive).")
484
+ })
485
+ },
486
+ {
487
+ name: "artifact_md_ast",
488
+ method: "md_ast",
489
+ description: "Return the full MDAST Root for the specified line range from a markdown artifact produced earlier in this turn.",
490
+ argsSchema: validator.object({
491
+ startLine: validator.number().integer().min(0).optional().description("Start line (inclusive)."),
492
+ endLine: validator.number().integer().min(0).optional().description("End line (exclusive).")
493
+ })
494
+ }
495
+ ]);
496
+ /**
497
+ * Forges base-class tools plus markdown-specific tools narrowed to
498
+ * {@link SpooledMarkdownArtifact}.
499
+ *
500
+ * @remarks
501
+ * Standard subclass extension pattern: call `SpooledArtifact.forgeTools(ctx)` to produce
502
+ * the base seven `artifact_*` tools narrowed to any `SpooledArtifact` in the turn, then
503
+ * register one `ArtifactTool` per markdown-specific descriptor narrowed to markdown
504
+ * artifacts. Downstream consumers building their own subclasses should follow the same
505
+ * shape.
506
+ */
507
+ static forgeTools(ctx) {
508
+ const registry = SpooledArtifact.forgeTools(ctx);
509
+ const requires = SpooledMarkdownArtifact;
510
+ const compatibleIds = [...ctx.turnToolCalls].filter((tc) => !tc.fromArtifactTool && isInstanceOf(tc.results, requires.name, requires)).map((tc) => tc.id);
511
+ if (compatibleIds.length === 0) return registry;
512
+ for (const descriptor of this.toolMethods) {
513
+ const callIdSchema = validator.string().valid(...compatibleIds).required().description("ToolCall id of the artifact to query.");
514
+ const argsSchema = (descriptor.argsSchema ?? validator.object({})).append({ callId: callIdSchema });
515
+ const tool = new ArtifactTool({
516
+ name: descriptor.name,
517
+ description: descriptor.description,
518
+ inputSchema: argsSchema,
519
+ ephemeral: true,
520
+ onCollision: "replace",
521
+ handler: async (rawArgs, ctxInner) => {
522
+ const args = rawArgs;
523
+ const tc = [...ctxInner.turnToolCalls].find((t) => t.id === args.callId);
524
+ if (!tc) return `Error: no tool call with id ${args.callId} in this turn`;
525
+ const artifact = tc.results;
526
+ if (!isInstanceOf(artifact, requires.name, requires)) return `Error: tool call ${args.callId} results are not a ${requires.name} instance`;
527
+ const methodArgs = [];
528
+ if (descriptor.method === "md_headings" || descriptor.method === "md_sections") methodArgs.push(args.depth);
529
+ else if (descriptor.method === "md_code_blocks") methodArgs.push(args.lang);
530
+ else if (descriptor.method === "md_links" || descriptor.method === "md_images" || descriptor.method === "md_text" || descriptor.method === "md_ast") methodArgs.push(args.startLine, args.endLine);
531
+ const fn = artifact[descriptor.method];
532
+ if (typeof fn !== "function") return `Error: artifact has no method ${descriptor.method}`;
533
+ const result = await Promise.resolve(fn.apply(artifact, methodArgs));
534
+ return (descriptor.serialise ?? defaultSerialise)(result);
535
+ }
536
+ });
537
+ registry.register(tool);
538
+ }
539
+ return registry;
540
+ }
541
+ /**
542
+ * Builds the structural index in a single top-to-bottom pass, retaining only metadata.
543
+ */
544
+ async #resolveIndex() {
545
+ if (this.#index !== void 0) return this.#index;
546
+ const count = await this.lineCount();
547
+ const headingsRaw = [];
548
+ const codeBlocks = [];
549
+ let inFrontmatter = false;
550
+ let frontmatterClosed = false;
551
+ let inCodeBlock = false;
552
+ let openFenceLen = 0;
553
+ let openFenceStartLine = 0;
554
+ let openFenceLang = null;
555
+ for (let i = 0; i < count; i++) {
556
+ const l = await this.line(i) ?? "";
557
+ if (i === 0 && l.trim() === "---") {
558
+ inFrontmatter = true;
559
+ continue;
560
+ }
561
+ if (inFrontmatter) {
562
+ if (l.trim() === "---") {
563
+ inFrontmatter = false;
564
+ frontmatterClosed = true;
565
+ }
566
+ continue;
567
+ }
568
+ if (!frontmatterClosed && i === 0) {}
569
+ const fLen = fenceLength(l);
570
+ if (!inCodeBlock && fLen >= 3) {
571
+ inCodeBlock = true;
572
+ openFenceLen = fLen;
573
+ openFenceStartLine = i;
574
+ openFenceLang = fenceLang(l);
575
+ continue;
576
+ }
577
+ if (inCodeBlock) {
578
+ if (fLen >= openFenceLen) {
579
+ const openChar = (await this.line(openFenceStartLine) ?? "").trimStart()[0];
580
+ if (l.trimStart()[0] === openChar) {
581
+ codeBlocks.push({
582
+ lang: openFenceLang,
583
+ startLine: openFenceStartLine,
584
+ endLine: i
585
+ });
586
+ inCodeBlock = false;
587
+ openFenceLen = 0;
588
+ }
589
+ }
590
+ continue;
591
+ }
592
+ const heading = parseHeadingLine(l);
593
+ if (heading) headingsRaw.push({
594
+ ...heading,
595
+ startLine: i
596
+ });
597
+ }
598
+ if (inCodeBlock) codeBlocks.push({
599
+ lang: openFenceLang,
600
+ startLine: openFenceStartLine,
601
+ endLine: count - 1
602
+ });
603
+ const headings = headingsRaw.map((h, idx) => {
604
+ const nextBoundary = headingsRaw.slice(idx + 1).find((n) => n.depth <= h.depth);
605
+ const endLine = nextBoundary ? nextBoundary.startLine - 1 : count - 1;
606
+ return {
607
+ ...h,
608
+ endLine
609
+ };
610
+ });
611
+ this.#index = {
612
+ headings,
613
+ codeBlocks
614
+ };
615
+ return this.#index;
616
+ }
617
+ /**
618
+ * Returns the parsed YAML frontmatter, or `undefined` when no frontmatter block is present.
619
+ *
620
+ * @remarks
621
+ * Short-circuits after reading the frontmatter block — never reads the document body. Caches
622
+ * the result so subsequent calls are free. The result is `undefined` (not an empty object)
623
+ * when no frontmatter is found, distinguishing "no frontmatter" from "empty frontmatter".
624
+ */
625
+ async md_frontmatter() {
626
+ if (this.#frontmatter !== void 0) return this.#frontmatter ?? void 0;
627
+ if ((await this.line(0))?.trim() !== "---") {
628
+ this.#frontmatter = null;
629
+ return;
630
+ }
631
+ const maxScan = Math.min(await this.lineCount(), 200);
632
+ const yamlLines = [];
633
+ let closed = false;
634
+ for (let i = 1; i < maxScan; i++) {
635
+ const l = await this.line(i);
636
+ if (l?.trim() === "---") {
637
+ closed = true;
638
+ break;
639
+ }
640
+ yamlLines.push(l ?? "");
641
+ }
642
+ if (!closed) {
643
+ this.#frontmatter = null;
644
+ return;
645
+ }
646
+ this.#frontmatter = load(yamlLines.join("\n"));
647
+ return this.#frontmatter;
648
+ }
649
+ /**
650
+ * Returns all headings in document order, optionally filtered by depth.
651
+ *
652
+ * @remarks
653
+ * Uses the cached structural index — no content is fetched from the {@link @nhtio/adk!SpoolReader}.
654
+ *
655
+ * @param depth - When provided, only headings at this ATX depth (1–6) are returned.
656
+ */
657
+ async md_headings(depth) {
658
+ const index = await this.#resolveIndex();
659
+ if (depth === void 0) return index.headings.slice();
660
+ return index.headings.filter((h) => h.depth === depth);
661
+ }
662
+ /**
663
+ * Returns all fenced code block entries, optionally filtered by language identifier.
664
+ *
665
+ * @remarks
666
+ * Returns line-range metadata only — no content is fetched. Use `cat(entry.startLine + 1,
667
+ * entry.endLine)` to retrieve the code body (excluding fence lines).
668
+ *
669
+ * @param lang - When provided, only blocks with this exact lang identifier are returned.
670
+ * Pass an empty string to match blocks with no lang identifier.
671
+ */
672
+ async md_code_blocks(lang) {
673
+ const index = await this.#resolveIndex();
674
+ if (lang === void 0) return index.codeBlocks.slice();
675
+ const target = lang === "" ? null : lang;
676
+ return index.codeBlocks.filter((b) => b.lang === target);
677
+ }
678
+ /**
679
+ * Returns document sections derived from the structural index.
680
+ *
681
+ * @remarks
682
+ * Returns only line-range metadata — body content is never fetched. To retrieve the body of a
683
+ * section, call `cat(section.bodyStartLine, section.bodyEndLine + 1)`.
684
+ *
685
+ * When `depth` is provided, only sections introduced by a heading at that depth are returned;
686
+ * deeper headings become part of the body.
687
+ *
688
+ * @param depth - When provided, only sections at this ATX depth (1–6) are returned.
689
+ */
690
+ async md_sections(depth) {
691
+ const index = await this.#resolveIndex();
692
+ return (depth !== void 0 ? index.headings.filter((h) => h.depth === depth) : index.headings).map((h) => ({
693
+ depth: h.depth,
694
+ heading: h.text,
695
+ headingLine: h.startLine,
696
+ bodyStartLine: h.startLine + 1,
697
+ bodyEndLine: h.endLine
698
+ }));
699
+ }
700
+ /**
701
+ * Returns the full MDAST Root for the specified line range.
702
+ *
703
+ * @remarks
704
+ * Without a range, reads the full document — for large documents, use
705
+ * {@link SpooledMarkdownArtifact.md_sections} to locate sections and pass
706
+ * bounded line ranges here.
707
+ *
708
+ * @param startLine - 0-based start line (inclusive). Defaults to `0`.
709
+ * @param endLine - 0-based end line (exclusive). Defaults to `lineCount()`.
710
+ */
711
+ async md_ast(startLine, endLine) {
712
+ const lines = await this.cat(startLine, endLine);
713
+ return processor().parse(lines.join("\n"));
714
+ }
715
+ /**
716
+ * Returns all inline and reference links in the specified line range.
717
+ *
718
+ * @param startLine - 0-based start line (inclusive). Defaults to `0`.
719
+ * @param endLine - 0-based end line (exclusive). Defaults to `lineCount()`.
720
+ */
721
+ async md_links(startLine, endLine) {
722
+ const lines = await this.cat(startLine, endLine);
723
+ const ast = processor().parse(lines.join("\n"));
724
+ const results = [];
725
+ visit(ast, "link", (node) => {
726
+ results.push({
727
+ text: toString(node),
728
+ url: node.url,
729
+ title: node.title ?? void 0
730
+ });
731
+ });
732
+ return results;
733
+ }
734
+ /**
735
+ * Returns all images in the specified line range.
736
+ *
737
+ * @param startLine - 0-based start line (inclusive). Defaults to `0`.
738
+ * @param endLine - 0-based end line (exclusive). Defaults to `lineCount()`.
739
+ */
740
+ async md_images(startLine, endLine) {
741
+ const lines = await this.cat(startLine, endLine);
742
+ const ast = processor().parse(lines.join("\n"));
743
+ const results = [];
744
+ visit(ast, "image", (node) => {
745
+ results.push({
746
+ alt: node.alt ?? "",
747
+ url: node.url,
748
+ title: node.title ?? void 0
749
+ });
750
+ });
751
+ return results;
752
+ }
753
+ /**
754
+ * Returns all document text with markup stripped, for the specified line range.
755
+ *
756
+ * @remarks
757
+ * Uses `mdast-util-to-string` to extract plain text from the AST — code, link text, and
758
+ * alt text are included; markdown syntax is removed.
759
+ *
760
+ * @param startLine - 0-based start line (inclusive). Defaults to `0`.
761
+ * @param endLine - 0-based end line (exclusive). Defaults to `lineCount()`.
762
+ */
763
+ async md_text(startLine, endLine) {
764
+ const lines = await this.cat(startLine, endLine);
765
+ return toString(processor().parse(lines.join("\n")));
766
+ }
767
+ };
768
+ //#endregion
769
+ export { SpooledJsonArtifact as n, SpooledMarkdownArtifact as t };
770
+
771
+ //# sourceMappingURL=spooled_markdown_artifact-BpUJol0W.mjs.map