agents 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/README.md +21 -1
  2. package/dist/{agent-tool-types-CzGGB-20.d.ts → agent-routing-DE5zmCQ8.d.ts} +1844 -1336
  3. package/dist/agent-routing.d.ts +14 -0
  4. package/dist/agent-routing.js +187 -0
  5. package/dist/agent-routing.js.map +1 -0
  6. package/dist/agent-tool-types.d.ts +26 -26
  7. package/dist/{agent-tools-zR2d5uij.d.ts → agent-tools-DtXMTDGM.d.ts} +7 -7
  8. package/dist/agent-tools.d.ts +21 -21
  9. package/dist/agent-tools.js +2 -1
  10. package/dist/agent-tools.js.map +1 -1
  11. package/dist/browser/ai.js +6 -2
  12. package/dist/browser/ai.js.map +1 -1
  13. package/dist/browser/tanstack-ai.js.map +1 -1
  14. package/dist/callable-decorator-DP__HhBA.d.ts +72 -0
  15. package/dist/callable-decorator.d.ts +18 -0
  16. package/dist/callable-decorator.js +71 -0
  17. package/dist/callable-decorator.js.map +1 -0
  18. package/dist/capability-BjSKYpzg.js +42 -0
  19. package/dist/capability-BjSKYpzg.js.map +1 -0
  20. package/dist/capability-runner-Be_-PLR1.d.ts +459 -0
  21. package/dist/channel-Bnm4S7T2.d.ts +491 -0
  22. package/dist/channels/ai-sdk.d.ts +46 -0
  23. package/dist/channels/ai-sdk.js +120 -0
  24. package/dist/channels/ai-sdk.js.map +1 -0
  25. package/dist/channels/email.d.ts +95 -0
  26. package/dist/channels/email.js +323 -0
  27. package/dist/channels/email.js.map +1 -0
  28. package/dist/channels/index.d.ts +233 -0
  29. package/dist/channels/index.js +608 -0
  30. package/dist/channels/index.js.map +1 -0
  31. package/dist/channels/slack.d.ts +140 -0
  32. package/dist/channels/slack.js +614 -0
  33. package/dist/channels/slack.js.map +1 -0
  34. package/dist/channels/tanstack-ai.d.ts +39 -0
  35. package/dist/channels/tanstack-ai.js +17 -0
  36. package/dist/channels/tanstack-ai.js.map +1 -0
  37. package/dist/channels/telegram.d.ts +106 -0
  38. package/dist/channels/telegram.js +427 -0
  39. package/dist/channels/telegram.js.map +1 -0
  40. package/dist/channels/voice.d.ts +45 -0
  41. package/dist/channels/voice.js +122 -0
  42. package/dist/channels/voice.js.map +1 -0
  43. package/dist/chat/index.d.ts +2328 -2015
  44. package/dist/chat/index.js +891 -521
  45. package/dist/chat/index.js.map +1 -1
  46. package/dist/chat/react.d.ts +14 -1
  47. package/dist/chat/react.js +82 -52
  48. package/dist/chat/react.js.map +1 -1
  49. package/dist/chat/transport.js +1 -1
  50. package/dist/chat-sdk/index.d.ts +7 -7
  51. package/dist/chat-sdk/index.js +1 -1
  52. package/dist/{client-zqKcsyFa.js → client-jagG8a9_.js} +129 -37
  53. package/dist/client-jagG8a9_.js.map +1 -0
  54. package/dist/client.d.ts +1 -1
  55. package/dist/client.js +1 -1
  56. package/dist/{cloudflare-BduZwmYK.js → cloudflare-Dzvc7V2N.js} +10 -3
  57. package/dist/{cloudflare-BduZwmYK.js.map → cloudflare-Dzvc7V2N.js.map} +1 -1
  58. package/dist/context/index.d.ts +216 -0
  59. package/dist/context/index.js +454 -0
  60. package/dist/context/index.js.map +1 -0
  61. package/dist/current-agent-Da_C9a3b.d.ts +266 -0
  62. package/dist/current-agent-DhoDkSnH.js +51 -0
  63. package/dist/current-agent-DhoDkSnH.js.map +1 -0
  64. package/dist/diagnostics-BzvaX2UT.js +45 -0
  65. package/dist/diagnostics-BzvaX2UT.js.map +1 -0
  66. package/dist/diagnostics-C4jcz3VK.js +360 -0
  67. package/dist/diagnostics-C4jcz3VK.js.map +1 -0
  68. package/dist/{do-oauth-client-provider-VTZj2VtM.d.ts → do-oauth-client-provider-Tmf1vgKz.d.ts} +2 -2
  69. package/dist/{email-CL27preh.d.ts → email-7TatiTnl.d.ts} +38 -9
  70. package/dist/email-send.d.ts +15 -0
  71. package/dist/email-send.js +32 -0
  72. package/dist/email-send.js.map +1 -0
  73. package/dist/email.d.ts +14 -10
  74. package/dist/email.js.map +1 -1
  75. package/dist/{handler-stateless-C_bo-Ytq.d.ts → handler-stateless-DxYpJ_XF.d.ts} +3 -3
  76. package/dist/{handler-stateless-CIkKPETH.js → handler-stateless-VvrWSAVA.js} +5 -5
  77. package/dist/handler-stateless-VvrWSAVA.js.map +1 -0
  78. package/dist/index-BB0kqhIz.d.ts +101 -0
  79. package/dist/index-XDkuQ7zm.d.ts +89 -0
  80. package/dist/{index-BRnybD6X.d.ts → index-YSKgfgg9.d.ts} +21 -31
  81. package/dist/index.d.ts +113 -101
  82. package/dist/index.js +11 -7234
  83. package/dist/ingress-BfetZbMO.js +83 -0
  84. package/dist/ingress-BfetZbMO.js.map +1 -0
  85. package/dist/internal-CYlgHl1l.js +59 -0
  86. package/dist/internal-CYlgHl1l.js.map +1 -0
  87. package/dist/internal_context-BlxFEWfn.d.ts +19 -0
  88. package/dist/internal_context.d.ts +10 -4
  89. package/dist/internal_context.js +1 -10
  90. package/dist/{client-invoker-BNSZxAkv.d.ts → invoker-CG0_p_Wq.d.ts} +2 -2
  91. package/dist/{client-invoker-VNZ7X0nn.js → invoker-CHMnoxIA.js} +2 -2
  92. package/dist/invoker-CHMnoxIA.js.map +1 -0
  93. package/dist/lifecycle/index.d.ts +66 -0
  94. package/dist/lifecycle/index.js +4 -0
  95. package/dist/lifecycle-CMRGjZdw.js +1299 -0
  96. package/dist/lifecycle-CMRGjZdw.js.map +1 -0
  97. package/dist/mcp/{do-oauth-client-provider.d.ts → client/do-oauth-client-provider.d.ts} +1 -1
  98. package/dist/mcp/{do-oauth-client-provider.js → client/do-oauth-client-provider.js} +1 -1
  99. package/dist/mcp/client/do-oauth-client-provider.js.map +1 -0
  100. package/dist/mcp/client/index.d.ts +42 -0
  101. package/dist/mcp/{client.js → client/index.js} +1 -1
  102. package/dist/mcp/{x402.d.ts → client/x402.d.ts} +2 -2
  103. package/dist/mcp/{x402.js → client/x402.js} +2 -2
  104. package/dist/mcp/client/x402.js.map +1 -0
  105. package/dist/mcp/index.d.ts +36 -36
  106. package/dist/mcp/index.js +14 -16
  107. package/dist/mcp/index.js.map +1 -1
  108. package/dist/mcp/{server.d.ts → server/index.d.ts} +1 -1
  109. package/dist/mcp/{server.js → server/index.js} +1 -1
  110. package/dist/observability/ai/index.js +50 -35
  111. package/dist/observability/ai/index.js.map +1 -1
  112. package/dist/observability/index.d.ts +4 -4
  113. package/dist/observability/index.js +3 -50
  114. package/dist/observability/index.js.map +1 -1
  115. package/dist/{protocol-Dqc2MQxo.js → protocol-B0nh6KNf.js} +19 -21
  116. package/dist/protocol-B0nh6KNf.js.map +1 -0
  117. package/dist/react.d.ts +4 -4
  118. package/dist/react.js +1 -1
  119. package/dist/{retries-CAvxtG9d.d.ts → retries-D9Ds-1lz.d.ts} +17 -6
  120. package/dist/retries.d.ts +8 -6
  121. package/dist/retries.js +13 -1
  122. package/dist/retries.js.map +1 -1
  123. package/dist/routing/index.d.ts +137 -0
  124. package/dist/routing/index.js +244 -0
  125. package/dist/routing/index.js.map +1 -0
  126. package/dist/sanitize-D9TujEK8.js +79 -0
  127. package/dist/sanitize-D9TujEK8.js.map +1 -0
  128. package/dist/schedule.d.ts +25 -94
  129. package/dist/schedule.js +1 -98
  130. package/dist/schedule.js.map +1 -1
  131. package/dist/scheduler-DD9NdYbF.js +665 -0
  132. package/dist/scheduler-DD9NdYbF.js.map +1 -0
  133. package/dist/scheduler-Dwh85ZGl.d.ts +223 -0
  134. package/dist/schedules/index.d.ts +22 -0
  135. package/dist/schedules/index.js +2 -0
  136. package/dist/schedules/parser.d.ts +79 -0
  137. package/dist/schedules/parser.js +103 -0
  138. package/dist/schedules/parser.js.map +1 -0
  139. package/dist/sentence-chunker-BAidJ4DA.d.ts +68 -0
  140. package/dist/serializable.d.ts +1 -1
  141. package/dist/sessions/index.d.ts +441 -0
  142. package/dist/sessions/index.js +2063 -0
  143. package/dist/sessions/index.js.map +1 -0
  144. package/dist/skills/index.d.ts +99 -0
  145. package/dist/skills/index.js +254 -5
  146. package/dist/skills/index.js.map +1 -1
  147. package/dist/sql-error-CPY-GXyI.d.ts +12 -0
  148. package/dist/sql-error.d.ts +2 -0
  149. package/dist/sql-error.js +16 -0
  150. package/dist/sql-error.js.map +1 -0
  151. package/dist/src-DlSHshb2.js +6963 -0
  152. package/dist/src-DlSHshb2.js.map +1 -0
  153. package/dist/streams/index.d.ts +120 -0
  154. package/dist/streams/index.js +107 -0
  155. package/dist/streams/index.js.map +1 -0
  156. package/dist/streams-D6tJ0NN9.d.ts +370 -0
  157. package/dist/streams-DZKgAj9b.js +709 -0
  158. package/dist/streams-DZKgAj9b.js.map +1 -0
  159. package/dist/sub-routing.d.ts +12 -12
  160. package/dist/surface-bZZJqBka.js +17 -0
  161. package/dist/surface-bZZJqBka.js.map +1 -0
  162. package/dist/tasks/index.d.ts +64 -0
  163. package/dist/tasks/index.js +2 -0
  164. package/dist/tasks-BRJ5zgya.d.ts +517 -0
  165. package/dist/tasks-ylZgBjhj.js +1656 -0
  166. package/dist/tasks-ylZgBjhj.js.map +1 -0
  167. package/dist/text-segment-joiner-BtAFQSA_.js +57 -0
  168. package/dist/text-segment-joiner-BtAFQSA_.js.map +1 -0
  169. package/dist/text-stream-CpdiKrJB.js +272 -0
  170. package/dist/text-stream-CpdiKrJB.js.map +1 -0
  171. package/dist/tokens-nHAKcN6M.js +52 -0
  172. package/dist/tokens-nHAKcN6M.js.map +1 -0
  173. package/dist/tool-schema-CBjGPrsQ.js +31 -0
  174. package/dist/tool-schema-CBjGPrsQ.js.map +1 -0
  175. package/dist/types-B7LojTe4.d.ts +202 -0
  176. package/dist/types-_Faxb570.d.ts +439 -0
  177. package/dist/voice/client.d.ts +226 -0
  178. package/dist/voice/client.js +932 -0
  179. package/dist/voice/client.js.map +1 -0
  180. package/dist/voice/errors.d.ts +43 -0
  181. package/dist/voice/errors.js +41 -0
  182. package/dist/voice/errors.js.map +1 -0
  183. package/dist/voice/index.d.ts +271 -0
  184. package/dist/voice/index.js +1812 -0
  185. package/dist/voice/index.js.map +1 -0
  186. package/dist/voice/react.d.ts +167 -0
  187. package/dist/voice/react.js +234 -0
  188. package/dist/voice/react.js.map +1 -0
  189. package/dist/voice/sfu.d.ts +71 -0
  190. package/dist/voice/sfu.js +157 -0
  191. package/dist/voice/sfu.js.map +1 -0
  192. package/dist/voice/text.d.ts +6 -0
  193. package/dist/voice/text.js +2 -0
  194. package/dist/voice/types.d.ts +58 -0
  195. package/dist/voice/types.js +18 -0
  196. package/dist/voice/types.js.map +1 -0
  197. package/dist/voice/workers-ai.d.ts +136 -0
  198. package/dist/voice/workers-ai.js +568 -0
  199. package/dist/voice/workers-ai.js.map +1 -0
  200. package/dist/websockets/index.d.ts +192 -0
  201. package/dist/websockets/index.js +2 -0
  202. package/dist/websockets-DUfRHPRq.js +502 -0
  203. package/dist/websockets-DUfRHPRq.js.map +1 -0
  204. package/dist/workflow-types.d.ts +25 -25
  205. package/dist/workflows.d.ts +22 -22
  206. package/dist/workflows.js +2 -1
  207. package/dist/workflows.js.map +1 -1
  208. package/dist/{ws-chat-transport-CIoOBbO7.js → ws-chat-transport-rWwta645.js} +152 -15
  209. package/dist/ws-chat-transport-rWwta645.js.map +1 -0
  210. package/docs/agent-class.md +29 -87
  211. package/docs/agent-tools.md +2 -1
  212. package/docs/channels.md +323 -0
  213. package/docs/chat-agents.md +19 -25
  214. package/docs/context.md +131 -0
  215. package/docs/durable-execution.md +1 -1
  216. package/docs/http-websockets.md +1 -11
  217. package/docs/human-in-the-loop.md +1 -1
  218. package/docs/index.md +16 -12
  219. package/docs/lifecycle.md +370 -0
  220. package/docs/long-running-agents.md +4 -6
  221. package/docs/mcp-client.md +55 -0
  222. package/docs/mcp-servers.md +5 -1
  223. package/docs/observability.md +11 -11
  224. package/docs/resumable-streaming.md +2 -2
  225. package/docs/routing.md +105 -0
  226. package/docs/scheduling.md +175 -15
  227. package/docs/server-driven-messages.md +1 -1
  228. package/docs/sessions.md +237 -871
  229. package/docs/streams.md +213 -0
  230. package/docs/sub-agents.md +185 -125
  231. package/docs/tasks.md +246 -0
  232. package/docs/voice.md +745 -0
  233. package/package.json +144 -33
  234. package/dist/cli/index.js +0 -26
  235. package/dist/cli/index.js.map +0 -1
  236. package/dist/client-invoker-VNZ7X0nn.js.map +0 -1
  237. package/dist/client-zqKcsyFa.js.map +0 -1
  238. package/dist/compaction-helpers-iiKMr2TQ.js +0 -340
  239. package/dist/compaction-helpers-iiKMr2TQ.js.map +0 -1
  240. package/dist/compaction-helpers-wUz6M3us.d.ts +0 -621
  241. package/dist/experimental/memory/session/index.d.ts +0 -670
  242. package/dist/experimental/memory/session/index.js +0 -2374
  243. package/dist/experimental/memory/session/index.js.map +0 -1
  244. package/dist/experimental/memory/utils/index.d.ts +0 -96
  245. package/dist/experimental/memory/utils/index.js +0 -79
  246. package/dist/experimental/memory/utils/index.js.map +0 -1
  247. package/dist/handler-stateless-CIkKPETH.js.map +0 -1
  248. package/dist/index.js.map +0 -1
  249. package/dist/internal_context-Dg4Cgjcu.d.ts +0 -37
  250. package/dist/internal_context.js.map +0 -1
  251. package/dist/mcp/client.d.ts +0 -42
  252. package/dist/mcp/do-oauth-client-provider.js.map +0 -1
  253. package/dist/mcp/x402.js.map +0 -1
  254. package/dist/protocol-Dqc2MQxo.js.map +0 -1
  255. package/dist/tool-output-truncation-CNnnGZQ3.js +0 -98
  256. package/dist/tool-output-truncation-CNnnGZQ3.js.map +0 -1
  257. package/dist/ws-chat-transport-CIoOBbO7.js.map +0 -1
  258. /package/dist/{cli/index.d.ts → index-BVVgDSdq.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/sessions/chunking.ts","../../src/sessions/attachment-store.ts","../../src/sessions/attachment-ingest.ts","../../src/sessions/compaction-helpers.ts","../../src/sessions/overlays.ts","../../src/sessions/core.ts","../../src/sessions/handle.ts","../../src/sessions/sessions.ts"],"sourcesContent":["/**\n * Row chunking for the Sessions capability.\n *\n * SQLite on a Durable Object caps how many bytes one row may hold, so a\n * message whose serialized JSON exceeds the budget is split across the\n * message row and numbered continuation rows. Reads concatenate the slices\n * back into the original string, so a round-trip is exact and nothing is\n * ever truncated or too large to store.\n *\n * The split is on BYTES, not characters: SQLite's limit is a byte limit and\n * one character can be up to four of them. A boundary is never placed\n * between a high surrogate and its low surrogate either, because a lone\n * surrogate is not valid UTF-8 and would not survive the round-trip.\n * `splitContent(s).join(\"\")` always equals `s`.\n */\n\n/**\n * Serialized byte ceiling for one stored row. A message larger than this is\n * split across continuation rows; the overwhelmingly common message fits in\n * one row and costs exactly one billed row write, as it always has.\n */\nexport const MAX_INLINE_ROW_BYTES = 1536 * 1024;\n\n/**\n * Split a serialized message into row-sized slices, root slice first.\n *\n * Always returns at least one slice, and never an empty slice unless the\n * input itself is empty. Slice 0 lives in the message row; the rest become\n * continuation rows numbered from 1.\n */\nexport function splitContent(\n json: string,\n budget: number = MAX_INLINE_ROW_BYTES\n): string[] {\n if (json.length === 0) return [\"\"];\n\n const slices: string[] = [];\n let start = 0;\n let bytes = 0;\n let index = 0;\n\n while (index < json.length) {\n const code = json.charCodeAt(index);\n let width: number;\n let step = 1;\n if (code < 0x80) {\n width = 1;\n } else if (code < 0x800) {\n width = 2;\n } else if (code >= 0xd800 && code <= 0xdbff && index + 1 < json.length) {\n const low = json.charCodeAt(index + 1);\n if (low >= 0xdc00 && low <= 0xdfff) {\n // A surrogate PAIR is one 4-byte code point and moves as a unit, so\n // a boundary can never land between its halves.\n width = 4;\n step = 2;\n } else {\n width = 3;\n }\n } else {\n width = 3;\n }\n\n // `bytes > 0` keeps a single character that alone exceeds the budget in\n // its own slice rather than emitting an empty one.\n if (bytes > 0 && bytes + width > budget) {\n slices.push(json.slice(start, index));\n start = index;\n bytes = 0;\n }\n bytes += width;\n index += step;\n }\n\n slices.push(json.slice(start));\n return slices;\n}\n","/**\n * Content-addressed byte storage for session attachments.\n *\n * An attachment is a typed media payload that never belongs inside a message\n * row: an image, an audio clip, a PDF. The store holds the raw bytes under\n * their SHA-256 and hands back a content address; the message keeps only a\n * pointer part. Payloads larger than one SQLite row are split across numbered\n * chunk rows, the same way an oversized message is.\n *\n * Everything here is synchronous, so bytes and the message row that points at\n * them commit in ONE transaction. There is no window in which a stored pointer\n * has no bytes behind it, and no cleanup pass for half-written payloads.\n *\n * Content addressing buys idempotency — a replayed append re-derives the same\n * address and stores nothing new. It is not a space-saving claim: two messages\n * that happen to carry the same image share a record, but nothing in the design\n * depends on that being common.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { MAX_INLINE_ROW_BYTES } from \"./chunking\";\nimport type { SessionsIo } from \"./io\";\n\n/**\n * SQLite window for one attachment chunk. Attachments are immutable, so a\n * larger row means fewer billed writes; this leaves headroom below SQLite's\n * 2 MiB ceiling for the row key and record overhead.\n */\nconst ATTACHMENT_CHUNK_BYTES = MAX_INLINE_ROW_BYTES;\n\n/** @internal Bytes plus the type they were declared with. */\nexport interface AttachmentBytes {\n readonly bytes: Uint8Array;\n readonly mediaType: string;\n}\n\n/** The content address of a payload. Pure: no storage access. */\nexport function hashPayload(bytes: Uint8Array): string {\n return createHash(\"sha256\").update(bytes).digest(\"hex\");\n}\n\n/** A standalone copy of a slice, since `SqlParam` accepts only `ArrayBuffer`. */\nfunction chunkBuffer(\n bytes: Uint8Array,\n start: number,\n end: number\n): ArrayBuffer {\n return bytes.slice(start, end).buffer as ArrayBuffer;\n}\n\nexport class AttachmentStore {\n readonly #io: SessionsIo;\n\n constructor(io: SessionsIo) {\n this.#io = io;\n }\n\n ensureTables(): void {\n this.#io.sqlWrite(\n `CREATE TABLE IF NOT EXISTS cf_agents_session_attachment_meta (\n hash TEXT PRIMARY KEY,\n bytes INTEGER NOT NULL,\n media_type TEXT NOT NULL,\n chunks INTEGER NOT NULL\n ) WITHOUT ROWID`,\n []\n );\n this.#io.sqlWrite(\n `CREATE TABLE IF NOT EXISTS cf_agents_session_attachment_chunks (\n hash TEXT NOT NULL,\n idx INTEGER NOT NULL,\n data BLOB NOT NULL,\n PRIMARY KEY (hash, idx)\n ) WITHOUT ROWID`,\n []\n );\n // Reference rows give an attachment its lifetime. There is deliberately no\n // index on `hash`: this table takes a write on every attachment append, and\n // the reachability check that reads it is a scan of a small table, which is\n // far cheaper than maintaining an index on the write path.\n this.#io.sqlWrite(\n `CREATE TABLE IF NOT EXISTS cf_agents_session_attachment_refs (\n session_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n hash TEXT NOT NULL,\n PRIMARY KEY (session_id, message_id, hash)\n ) WITHOUT ROWID`,\n []\n );\n }\n\n /**\n * Store one payload under its precomputed address. Idempotent: bytes that\n * are already present cost a single read and no writes.\n *\n * Call inside the caller's transaction so the payload and the message that\n * references it commit together. The address is passed in rather than\n * derived so hashing stays outside the transaction.\n */\n put(payload: AttachmentBytes, hash: string): void {\n const { bytes, mediaType } = payload;\n const existing = this.#io.sql<{ hash: string }>(\n \"SELECT hash FROM cf_agents_session_attachment_meta WHERE hash = ?\",\n [hash]\n );\n if (existing.length > 0) return;\n\n let chunks = 0;\n for (let start = 0; start < bytes.length; start += ATTACHMENT_CHUNK_BYTES) {\n const end = Math.min(start + ATTACHMENT_CHUNK_BYTES, bytes.length);\n this.#io.sqlWrite(\n \"INSERT INTO cf_agents_session_attachment_chunks (hash, idx, data) VALUES (?, ?, ?)\",\n [hash, chunks, chunkBuffer(bytes, start, end)]\n );\n chunks++;\n }\n this.#io.sqlWrite(\n `INSERT INTO cf_agents_session_attachment_meta (hash, bytes, media_type, chunks)\n VALUES (?, ?, ?, ?)`,\n [hash, bytes.length, mediaType, chunks]\n );\n }\n\n /** Read one payload back, or `undefined` when the address is unknown. */\n get(hash: string): AttachmentBytes | undefined {\n const [meta] = this.#io.sql<{\n bytes: number;\n media_type: string;\n chunks: number;\n }>(\n \"SELECT bytes, media_type, chunks FROM cf_agents_session_attachment_meta WHERE hash = ?\",\n [hash]\n );\n if (!meta) return undefined;\n\n const out = new Uint8Array(meta.bytes);\n if (meta.chunks > 0) {\n const rows = this.#io.sql<{ idx: number; data: ArrayBuffer }>(\n \"SELECT idx, data FROM cf_agents_session_attachment_chunks WHERE hash = ? ORDER BY idx\",\n [hash]\n );\n let offset = 0;\n for (const row of rows) {\n const slice = new Uint8Array(row.data);\n out.set(slice, offset);\n offset += slice.length;\n }\n }\n return { mediaType: meta.media_type, bytes: out };\n }\n\n /** Record that one message references these payloads. */\n addRefs(\n sessionId: string,\n messageId: string,\n hashes: readonly string[]\n ): void {\n for (const hash of hashes) {\n this.#io.sqlWrite(\n `INSERT OR IGNORE INTO cf_agents_session_attachment_refs\n (session_id, message_id, hash) VALUES (?, ?, ?)`,\n [sessionId, messageId, hash]\n );\n }\n }\n\n /**\n * Point one message at exactly `hashes`, adding and dropping references to\n * match, and collect whatever that orphaned. Call after the new payloads are\n * stored, so a hash the message still uses is never briefly unreferenced.\n */\n replaceRefs(\n sessionId: string,\n messageId: string,\n hashes: readonly string[]\n ): void {\n const current = this.#io.sql<{ hash: string }>(\n `SELECT hash FROM cf_agents_session_attachment_refs\n WHERE session_id = ? AND message_id = ?`,\n [sessionId, messageId]\n );\n const wanted = new Set(hashes);\n const held = new Set(current.map((row) => row.hash));\n const dropped: string[] = [];\n for (const hash of held) {\n if (wanted.has(hash)) continue;\n this.#io.sqlWrite(\n `DELETE FROM cf_agents_session_attachment_refs\n WHERE session_id = ? AND message_id = ? AND hash = ?`,\n [sessionId, messageId, hash]\n );\n dropped.push(hash);\n }\n this.addRefs(\n sessionId,\n messageId,\n hashes.filter((hash) => !held.has(hash))\n );\n if (dropped.length > 0) this.#collect(dropped);\n }\n\n /**\n * Drop the references held by the given messages and collect any payload\n * that no longer has a reader.\n */\n releaseMessages(sessionId: string, messageIds: readonly string[]): void {\n if (messageIds.length === 0) return;\n const ids = JSON.stringify(messageIds);\n const orphanCandidates = this.#io.sql<{ hash: string }>(\n `SELECT DISTINCT hash FROM cf_agents_session_attachment_refs\n WHERE session_id = ? AND message_id IN (SELECT value FROM json_each(?))`,\n [sessionId, ids]\n );\n if (orphanCandidates.length === 0) return;\n this.#io.sqlWrite(\n `DELETE FROM cf_agents_session_attachment_refs\n WHERE session_id = ? AND message_id IN (SELECT value FROM json_each(?))`,\n [sessionId, ids]\n );\n this.#collect(orphanCandidates.map((row) => row.hash));\n }\n\n /** Drop every reference held by one session and collect what it orphaned. */\n releaseSession(sessionId: string): void {\n const candidates = this.#io.sql<{ hash: string }>(\n \"SELECT DISTINCT hash FROM cf_agents_session_attachment_refs WHERE session_id = ?\",\n [sessionId]\n );\n if (candidates.length === 0) return;\n this.#io.sqlWrite(\n \"DELETE FROM cf_agents_session_attachment_refs WHERE session_id = ?\",\n [sessionId]\n );\n this.#collect(candidates.map((row) => row.hash));\n }\n\n /** Delete payloads that no reference points at any more. */\n #collect(hashes: readonly string[]): void {\n for (const hash of hashes) {\n const stillReferenced = this.#io.sql<{ hash: string }>(\n \"SELECT hash FROM cf_agents_session_attachment_refs WHERE hash = ? LIMIT 1\",\n [hash]\n );\n if (stillReferenced.length > 0) continue;\n this.#io.sqlWrite(\n \"DELETE FROM cf_agents_session_attachment_chunks WHERE hash = ?\",\n [hash]\n );\n this.#io.sqlWrite(\n \"DELETE FROM cf_agents_session_attachment_meta WHERE hash = ?\",\n [hash]\n );\n }\n }\n}\n","/**\n * Which parts of a message become attachments, and how they come back.\n *\n * The rule is typed, not size-based: a part that DECLARES a non-text media\n * type and carries its payload inline is an attachment, whatever its size.\n * Text, reasoning and plain tool output are never extracted — they stay in the\n * row and, if they are too large for one, chunk across continuation rows.\n *\n * That distinction is the whole point. Sizing the rule off bytes would make a\n * message's stored shape depend on how big an image happened to be, which is\n * how the previous design ended up as a rescue mechanism that competed with\n * row chunking. Extraction here is uniform, so a reader never has to ask why\n * one image is a pointer and another is inline.\n *\n * Extraction is lossless: bytes move to the attachment store and a read puts\n * them back verbatim. Nothing that shapes what a model SEES belongs here — a\n * cap on tool output discards content and so lives on the read path, in\n * `agents/context`, where it can change without having destroyed anything.\n */\n\nimport { hashPayload, type AttachmentBytes } from \"./attachment-store\";\nimport type { SessionMessage, SessionMessagePart } from \"./types\";\n\n/** Pointer scheme written into a stored part in place of its payload. */\nconst ATTACHMENT_URL_PREFIX = \"attachment:sha256:\";\n\n/** Hostile or deeply nested tool output stops here rather than recursing forever. */\nconst MAX_WALK_DEPTH = 8;\n\n/** Build the pointer for a content address. */\nfunction attachmentUrl(hash: string): string {\n return `${ATTACHMENT_URL_PREFIX}${hash}`;\n}\n\n/** The content address in a pointer, or `null` when the value is not one. */\nfunction parseAttachmentUrl(url: unknown): string | null {\n if (typeof url !== \"string\" || !url.startsWith(ATTACHMENT_URL_PREFIX)) {\n return null;\n }\n const hash = url.slice(ATTACHMENT_URL_PREFIX.length);\n return /^[0-9a-f]{64}$/.test(hash) ? hash : null;\n}\n\ninterface ParsedDataUrl {\n mediaType: string;\n payload: string;\n}\n\n/** Parse a base64 `data:` URL. Non-base64 data URLs are left alone. */\nfunction parseDataUrl(url: string): ParsedDataUrl | null {\n if (!url.startsWith(\"data:\")) return null;\n const comma = url.indexOf(\",\");\n if (comma < 0) return null;\n const header = url.slice(5, comma);\n if (!header.endsWith(\";base64\")) return null;\n const mediaType =\n header.slice(0, -\";base64\".length) || \"application/octet-stream\";\n return { mediaType, payload: url.slice(comma + 1) };\n}\n\n/**\n * Text payloads stay in the message. They chunk perfectly well, they are what\n * FTS indexes, and moving them out would put prose behind a pointer for no gain.\n */\nfunction isTextMediaType(mediaType: string): boolean {\n return mediaType.startsWith(\"text/\");\n}\n\nfunction decodeBase64(payload: string): Uint8Array | null {\n const compact = /[\\t\\n\\f\\r ]/.test(payload)\n ? payload.replace(/[\\t\\n\\f\\r ]/g, \"\")\n : payload;\n if (compact.length % 4 === 1 || !/^[A-Za-z0-9+/]*={0,2}$/.test(compact))\n return null;\n let binary: string;\n try {\n binary = atob(compact);\n } catch {\n return null;\n }\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\nfunction encodeBase64(bytes: Uint8Array): string {\n // Chunked so a large payload never blows the argument limit of `apply`.\n const step = 0x8000;\n let binary = \"\";\n for (let i = 0; i < bytes.length; i += step) {\n binary += String.fromCharCode(...bytes.subarray(i, i + step));\n }\n return btoa(binary);\n}\n\n/** Rebuild the exact `data:` URL a payload was extracted from. */\nfunction dataUrl(mediaType: string, bytes: Uint8Array): string {\n return `data:${mediaType};base64,${encodeBase64(bytes)}`;\n}\n\n/**\n * A value carrying inline media: anything that declares a `mediaType` and\n * holds its bytes in a `url` data URL or a base64 `data` string. Covers AI SDK\n * file parts and the media entries tool results return, without either shape\n * being named here.\n */\nfunction inlineMediaOf(\n value: Record<string, unknown>\n): { field: \"url\" | \"data\"; mediaType: string; bytes: Uint8Array } | null {\n const declared = typeof value.mediaType === \"string\" ? value.mediaType : null;\n\n const url = value.url;\n if (typeof url === \"string\") {\n const parsed = parseDataUrl(url);\n if (!parsed) return null;\n const mediaType = declared ?? parsed.mediaType;\n if (isTextMediaType(mediaType)) return null;\n const bytes = decodeBase64(parsed.payload);\n return bytes ? { field: \"url\", mediaType, bytes } : null;\n }\n\n // A bare `data` string is only media when something declared it as such.\n if (\n declared &&\n typeof value.data === \"string\" &&\n !isTextMediaType(declared)\n ) {\n const bytes = decodeBase64(value.data);\n return bytes ? { field: \"data\", mediaType: declared, bytes } : null;\n }\n return null;\n}\n\n/** One payload lifted out of a message, already addressed. */\nexport interface PendingAttachment {\n readonly hash: string;\n readonly payload: AttachmentBytes;\n}\n\n/** What one extraction pass produced. */\nexport interface ExtractionResult {\n /** The message to store: identical when it carried no inline media. */\n message: SessionMessage;\n /** Payloads to write, deduplicated by address, in encounter order. */\n attachments: PendingAttachment[];\n}\n\n/**\n * Replace inline media with pointers, collecting the payloads to store.\n *\n * Returns the original message by reference when nothing was extracted, so the\n * overwhelmingly common text-only write allocates nothing and pays only a walk.\n */\nexport function extractAttachments(message: SessionMessage): ExtractionResult {\n const attachments: PendingAttachment[] = [];\n const seen = new Set<string>();\n\n const walk = (value: unknown, depth: number): unknown => {\n if (depth > MAX_WALK_DEPTH || value === null || typeof value !== \"object\") {\n return value;\n }\n\n if (Array.isArray(value)) {\n let changed = false;\n const next = value.map((entry) => {\n const walked = walk(entry, depth + 1);\n if (walked !== entry) changed = true;\n return walked;\n });\n return changed ? next : value;\n }\n\n const record = value as Record<string, unknown>;\n const media = inlineMediaOf(record);\n if (media) {\n // Hashing is pure CPU, so the address is known here and the write\n // transaction never has to hash anything.\n const hash = hashPayload(media.bytes);\n if (!seen.has(hash)) {\n seen.add(hash);\n attachments.push({\n hash,\n payload: { bytes: media.bytes, mediaType: media.mediaType }\n });\n }\n // The pointer replaces the payload IN PLACE, in whichever field held it.\n // Keeping the field means a resolve restores the original shape exactly,\n // rather than rewriting a `data` entry into a `url` one.\n return {\n ...record,\n mediaType: media.mediaType,\n [media.field]: attachmentUrl(hash)\n };\n }\n\n let changed = false;\n const next: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(record)) {\n const walked = walk(entry, depth + 1);\n if (walked !== entry) changed = true;\n next[key] = walked;\n }\n return changed ? next : value;\n };\n\n if (message.parts.length === 0) return { message, attachments };\n const parts = walk(message.parts, 0) as SessionMessagePart[];\n if (parts === message.parts) return { message, attachments };\n return { message: { ...message, parts }, attachments };\n}\n\n/**\n * Put payloads back inline, undoing extraction exactly.\n *\n * A pointer whose bytes cannot be loaded is left as it is: an unresolvable\n * pointer is a truthful record that the reference survived its payload, which\n * only a bug could produce, and inventing a placeholder would hide it.\n */\nexport function resolveAttachments(\n message: SessionMessage,\n load: (hash: string) => { mediaType: string; bytes: Uint8Array } | undefined\n): SessionMessage {\n const walk = (value: unknown, depth: number): unknown => {\n if (depth > MAX_WALK_DEPTH || value === null || typeof value !== \"object\") {\n return value;\n }\n if (Array.isArray(value)) {\n let changed = false;\n const next = value.map((entry) => {\n const walked = walk(entry, depth + 1);\n if (walked !== entry) changed = true;\n return walked;\n });\n return changed ? next : value;\n }\n\n const record = value as Record<string, unknown>;\n const urlHash = parseAttachmentUrl(record.url);\n if (urlHash) {\n const loaded = load(urlHash);\n if (!loaded) return value;\n return { ...record, url: dataUrl(loaded.mediaType, loaded.bytes) };\n }\n const dataHash = parseAttachmentUrl(record.data);\n if (dataHash) {\n const loaded = load(dataHash);\n if (!loaded) return value;\n // A `data` field held bare base64, not a data URL, so it is restored bare.\n return { ...record, data: encodeBase64(loaded.bytes) };\n }\n\n let changed = false;\n const next: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(record)) {\n const walked = walk(entry, depth + 1);\n if (walked !== entry) changed = true;\n next[key] = walked;\n }\n return changed ? next : value;\n };\n\n const parts = walk(message.parts, 0) as SessionMessagePart[];\n return parts === message.parts ? message : { ...message, parts };\n}\n","/**\n * Compaction Helpers\n *\n * Utilities for full compaction (LLM-based summarization).\n * Used by the reference compaction implementation and available\n * for custom CompactFunction implementations.\n */\n\nimport type { SessionMessage } from \"./types\";\nimport { estimateMessageTokens } from \"./tokens\";\n\n// ── Compaction ID constants ─────────────────────────────────────────\n\n/** Prefix for all compaction messages (overlays and summaries) */\nexport const COMPACTION_PREFIX = \"compaction_\";\n\n/** Head messages kept verbatim so the conversation's opening survives. */\nconst PROTECT_HEAD = 3;\n/** Tail messages kept verbatim regardless of the token budget. */\nconst MIN_TAIL_MESSAGES = 2;\n\n/** Check if a message is a compaction message */\nexport function isCompactionMessage(msg: SessionMessage): boolean {\n return msg.id.startsWith(COMPACTION_PREFIX);\n}\n\n// ── Tool Pair Alignment ──────────────────────────────────────────────\n\n/**\n * Check if a message contains tool invocations.\n */\nfunction hasToolCalls(msg: SessionMessage): boolean {\n return msg.parts.some(\n (p) => p.type.startsWith(\"tool-\") || p.type === \"dynamic-tool\"\n );\n}\n\n/**\n * Get tool call IDs from a message's parts.\n */\nfunction getToolCallIds(msg: SessionMessage): Set<string> {\n const ids = new Set<string>();\n for (const part of msg.parts) {\n if (\n (part.type.startsWith(\"tool-\") || part.type === \"dynamic-tool\") &&\n \"toolCallId\" in part\n ) {\n ids.add((part as { toolCallId: string }).toolCallId);\n }\n }\n return ids;\n}\n\n/**\n * Check if a message is a tool result referencing a specific call ID.\n */\nfunction isToolResultFor(msg: SessionMessage, callIds: Set<string>): boolean {\n return msg.parts.some(\n (p) =>\n (p.type.startsWith(\"tool-\") || p.type === \"dynamic-tool\") &&\n \"toolCallId\" in p &&\n callIds.has((p as { toolCallId: string }).toolCallId)\n );\n}\n\n/**\n * Align a boundary index forward to avoid splitting tool call/result groups.\n * If the boundary falls between an assistant message with tool calls and its\n * tool results, move it forward past the results.\n */\nexport function alignBoundaryForward(\n messages: SessionMessage[],\n idx: number\n): number {\n if (idx <= 0 || idx >= messages.length) return idx;\n\n // Check if the message before the boundary has tool calls\n const prev = messages[idx - 1];\n if (prev.role === \"assistant\" && hasToolCalls(prev)) {\n const callIds = getToolCallIds(prev);\n // Skip forward past any tool results for these calls\n while (idx < messages.length && isToolResultFor(messages[idx], callIds)) {\n idx++;\n }\n }\n\n return idx;\n}\n\n/**\n * Align a boundary index backward to avoid splitting tool call/result groups.\n * If the boundary falls in the middle of tool results, move it backward to\n * include the assistant message that made the calls.\n */\nexport function alignBoundaryBackward(\n messages: SessionMessage[],\n idx: number\n): number {\n if (idx <= 0 || idx >= messages.length) return idx;\n\n // If the message at idx is a tool result, walk backward to find the call\n while (idx > 0) {\n const msg = messages[idx];\n if (msg.role === \"assistant\" && hasToolCalls(msg)) {\n break; // This is a tool call message — include it\n }\n // Check if this looks like a tool result (assistant message following another)\n const prev = messages[idx - 1];\n if (prev.role === \"assistant\" && hasToolCalls(prev)) {\n const callIds = getToolCallIds(prev);\n if (isToolResultFor(msg, callIds)) {\n idx--; // Move back to include the call\n continue;\n }\n }\n break;\n }\n\n return idx;\n}\n\n// ── Token-Budget Tail Protection ─────────────────────────────────────\n\n/**\n * Find the compression end boundary using a token budget for the tail.\n * Walks backward from the end, accumulating tokens until budget is reached.\n * Returns the index where compression should stop (everything from this\n * index onward is protected).\n *\n * @param messages All messages\n * @param headEnd Index where the protected head ends (compression starts here)\n * @param tailTokenBudget Maximum tokens to keep in the tail\n * @param minTailMessages Minimum messages to protect in the tail (fallback)\n */\nexport function findTailCutByTokens(\n messages: SessionMessage[],\n headEnd: number,\n tailTokenBudget = 20000,\n minTailMessages = 2\n): number {\n const n = messages.length;\n let accumulated = 0;\n let tokenCut = n;\n\n for (let i = n - 1; i >= headEnd; i--) {\n const msgTokens = estimateMessageTokens([messages[i]]);\n\n if (accumulated + msgTokens > tailTokenBudget && tokenCut < n) {\n // Budget exceeded and we already have at least one tail message\n break;\n }\n accumulated += msgTokens;\n tokenCut = i;\n }\n\n // Protect whichever is larger: token-based tail or minTailMessages\n const minCut = n - minTailMessages;\n const cutIdx = minCut >= headEnd ? Math.min(tokenCut, minCut) : tokenCut;\n\n // Align to avoid splitting tool groups\n return alignBoundaryBackward(messages, cutIdx);\n}\n\nexport function computeSummaryBudget(messages: SessionMessage[]): number {\n const contentTokens = estimateMessageTokens(messages);\n // Summary is ~20% of the content being compressed.\n // The summary replaces the compressed middle, so it's sized relative\n // to what it's replacing — not the tail budget (they occupy different\n // slots in the context window).\n const budget = Math.floor(contentTokens * 0.2);\n return Math.max(100, budget);\n}\n\n// ── Structured Summary Prompt ────────────────────────────────────────\n\n/**\n * Build a prompt for LLM summarization of compressed messages.\n *\n * @param messages Messages to summarize\n * @param previousSummary Previous summary for iterative updates (or null for first compaction)\n * @param budget Target token count for the summary\n */\nexport function buildSummaryPrompt(\n messages: SessionMessage[],\n previousSummary: string | null,\n budget: number\n): string {\n const content = messages\n .map((msg) => {\n const textParts = msg.parts\n .filter((p) => p.type === \"text\")\n .map((p) => (p as { text: string }).text)\n .join(\"\\n\");\n\n const toolParts = msg.parts\n .filter((p) => p.type.startsWith(\"tool-\") || p.type === \"dynamic-tool\")\n .map((p) => {\n const tp = p as {\n toolName?: string;\n input?: unknown;\n output?: unknown;\n };\n const parts = [`[Tool: ${tp.toolName ?? \"unknown\"}]`];\n if (tp.input)\n parts.push(`Input: ${JSON.stringify(tp.input).slice(0, 500)}`);\n if (tp.output)\n parts.push(`Output: ${JSON.stringify(tp.output).slice(0, 500)}`);\n return parts.join(\"\\n\");\n })\n .join(\"\\n\");\n\n return `[${msg.role}]\\n${textParts}${toolParts ? \"\\n\" + toolParts : \"\"}`;\n })\n .join(\"\\n\\n---\\n\\n\");\n\n if (previousSummary) {\n return `You are updating a conversation summary. A previous summary exists below. New conversation turns have occurred since then and need to be incorporated.\n\nPREVIOUS SUMMARY:\n${previousSummary}\n\nNEW TURNS TO INCORPORATE:\n${content}\n\nUpdate the summary. PRESERVE existing information that is still relevant. ADD new information. Remove information only if it is clearly obsolete.\n\n## Topic\n[What the conversation is about]\n\n## Key Points\n[Important information, decisions, and conclusions from the conversation]\n\n## Current State\n[Where things stand now — what has been done, what is in progress]\n\n## Open Items\n[Unresolved questions, pending tasks, or next steps discussed]\n\nTarget ~${budget} tokens. Be factual — only include information that was explicitly discussed in the conversation. Do NOT invent file paths, commands, or details that were not mentioned. Write only the summary body.`;\n }\n\n return `Create a concise summary of this conversation that preserves the important information for future context.\n\nCONVERSATION TO SUMMARIZE:\n${content}\n\nUse this structure:\n\n## Topic\n[What the conversation is about]\n\n## Key Points\n[Important information, decisions, and conclusions from the conversation]\n\n## Current State\n[Where things stand now — what has been done, what is in progress]\n\n## Open Items\n[Unresolved questions, pending tasks, or next steps discussed]\n\nTarget ~${budget} tokens. Be factual — only include information that was explicitly discussed in the conversation. Do NOT invent file paths, commands, or details that were not mentioned. Write only the summary body.`;\n}\n\n// ── Reference Compaction Implementation ──────────────────────────────\n\n/**\n * Result of a compaction function — describes the overlay to store.\n */\nexport interface CompactResult {\n /** First message ID in the compacted range */\n fromMessageId: string;\n /** Last message ID in the compacted range */\n toMessageId: string;\n /** Summary text to store as the overlay */\n summary: string;\n}\n\nexport interface CompactOptions {\n /** Calls the model to summarize; receives a prompt, returns its text. */\n summarize: (prompt: string) => Promise<string>;\n /**\n * Token budget for the recent tail kept verbatim. Older messages above the\n * protected head are summarized into one overlay. Default 20,000.\n */\n keepRecentTokens?: number;\n}\n\n/**\n * Reference compaction implementation.\n *\n * Implements the full hermes-style compaction algorithm:\n * 1. Protect head messages (first N)\n * 2. Protect tail by token budget (walk backward)\n * 3. Align boundaries to tool call groups\n * 4. Summarize middle section with LLM (structured format)\n * 5. Iterative summary updates on subsequent compactions\n *\n * @example\n * ```typescript\n * import { createCompactFunction } from \"agents/sessions\";\n *\n * sessions\n * .session()\n * .onCompaction(\n * createCompactFunction({\n * summarize: (prompt) => generateText({ model, prompt }).then((r) => r.text)\n * })\n * )\n * .compactAfter(100_000);\n * ```\n */\nexport function createCompactFunction(opts: CompactOptions) {\n const keepRecentTokens = opts.keepRecentTokens ?? 20_000;\n\n return async (messages: SessionMessage[]): Promise<CompactResult | null> => {\n if (messages.length <= PROTECT_HEAD + MIN_TAIL_MESSAGES) return null;\n\n // 1. Find compression boundaries\n const compressStart = alignBoundaryForward(messages, PROTECT_HEAD);\n const compressEnd = findTailCutByTokens(\n messages,\n compressStart,\n keepRecentTokens,\n MIN_TAIL_MESSAGES\n );\n\n if (compressEnd <= compressStart) {\n return null;\n }\n\n // Filter out compaction overlay messages — they have virtual IDs\n // and should not be included in the summary prompt or used as range IDs\n const middleMessages = messages\n .slice(compressStart, compressEnd)\n .filter((m) => !isCompactionMessage(m));\n\n if (middleMessages.length === 0) return null;\n\n // 2. Generate summary — extract previous summary from compaction overlays\n const existingCompaction = messages.find(isCompactionMessage);\n const previousSummary = existingCompaction\n ? existingCompaction.parts\n .filter((p) => p.type === \"text\")\n .map((p) => (p as { text: string }).text)\n .join(\"\\n\")\n : null;\n\n const budget = computeSummaryBudget(middleMessages);\n const prompt = buildSummaryPrompt(middleMessages, previousSummary, budget);\n const summary = await opts.summarize(prompt);\n\n if (!summary.trim()) return null;\n\n return {\n fromMessageId: middleMessages[0].id,\n toMessageId: middleMessages[middleMessages.length - 1].id,\n summary\n };\n };\n}\n","/**\n * Compaction overlay planning shared by the read path and the stats\n * derivation. Reproduces the selection semantics of the legacy\n * `applyCompactions` exactly: walk the path root → leaf; at each position the\n * newest overlay that starts here and ends at-or-after here on this branch\n * wins; its span is skipped and later overlaps inside the span never apply.\n */\n\nimport { COMPACTION_PREFIX } from \"./compaction-helpers\";\nimport type { SessionMessage, StoredCompaction } from \"./types\";\n\n/** One planned overlay span over path indexes (inclusive). */\nexport interface OverlaySpan {\n startIndex: number;\n endIndex: number;\n compaction: StoredCompaction;\n}\n\n/** Plan overlay spans over an ordered list of path message ids. */\nexport function planOverlays(\n pathIds: readonly string[],\n compactions: readonly StoredCompaction[]\n): OverlaySpan[] {\n if (compactions.length === 0) return [];\n const indexById = new Map(pathIds.map((id, index) => [id, index]));\n const spans: OverlaySpan[] = [];\n let i = 0;\n while (i < pathIds.length) {\n // Sibling branches can have compactions with the same starting message.\n // Consider only ranges ending on this branch, then let the latest valid\n // compaction supersede earlier ranges on the same branch.\n const matching = compactions.filter(\n (compaction) =>\n compaction.fromMessageId === pathIds[i] &&\n (indexById.get(compaction.toMessageId) ?? -1) >= i\n );\n const compaction = matching.at(-1);\n if (compaction) {\n const endIndex = indexById.get(compaction.toMessageId) ?? -1;\n if (endIndex >= i) {\n spans.push({ startIndex: i, endIndex, compaction });\n i = endIndex + 1;\n continue;\n }\n }\n i++;\n }\n return spans;\n}\n\n/** The synthetic message an overlay span renders as. */\nexport function overlayMessage(compaction: StoredCompaction): SessionMessage {\n return {\n id: `${COMPACTION_PREFIX}${compaction.id}`,\n role: \"assistant\",\n parts: [{ type: \"text\", text: compaction.summary }],\n createdAt: new Date()\n };\n}\n","/**\n * @internal Storage engine behind the Sessions capability. One instance per\n * capability, owning the `cf_agents_session_*` tables. All methods assume the\n * caller has settled startup ordering (`lifecycle.ready()` for public API).\n *\n * Write economics: rows written cost ~1000× rows read on DO SQLite. Every\n * table is WITHOUT ROWID with no secondary index, so one row write bills one\n * row. State is derived from existing rows, never kept in counter rows, and\n * an unchanged update writes nothing. The only in-memory state is the tail\n * of each session (its leaf id and next `seq`), read once per object\n * lifetime because finding it means scanning the session's rows.\n */\n\nimport { extractAttachments, resolveAttachments } from \"./attachment-ingest\";\nimport { AttachmentStore } from \"./attachment-store\";\nimport { splitContent } from \"./chunking\";\nimport type { SessionsIo } from \"./io\";\nimport { overlayMessage, planOverlays, type OverlaySpan } from \"./overlays\";\nimport {\n estimateAttachmentTokens,\n estimatedDataUrlBytes,\n estimateMessageTokens,\n estimateStringTokens\n} from \"./tokens\";\nimport type {\n HistoryReadOptions,\n RecentHistoryResult,\n SearchResult,\n SessionChangeEvent,\n SessionChangeListener,\n SessionMessage,\n SessionRowStat,\n SessionsOptions,\n StoredCompaction\n} from \"./types\";\n\n/**\n * Bounds for each content-hydration query on a history path. In workerd the\n * SQLite allocator shares the isolate's memory budget with the JS heap, so\n * oversized transient result sets surface as SQLITE_NOMEM (#1710). Chunks\n * are bounded by BOTH row count and cumulative stored bytes.\n */\nconst HISTORY_CONTENT_CHUNK_SIZE = 50;\nconst HISTORY_CONTENT_CHUNK_BYTES = 4 * 1024 * 1024;\n/**\n * Rows per content window on a newest-first read. Such a read walks the path\n * by id alone — no per-row byte subqueries, so no byte-bounded chunking — and\n * is typically stopped by its consumer within the first few messages, so a\n * small fixed window keeps both the rows read and the memory held low.\n */\nconst NEWEST_FIRST_WINDOW_ROWS = 8;\n\n/**\n * Deepest path a history read follows: the root row is depth 0, so a read\n * returns at most this many rows plus one. A longer branch shows its most\n * recent rows only, and `getRecentHistory` reports that as truncated.\n */\nconst MAX_PATH_DEPTH = 10_000;\n\n/** What a hydration window needs from a path row: its id, and its stored size when the window is byte-bounded. */\ntype PathRow = { id: string; bytes: number };\n\n/** The newest row of a session: what an append attaches to and numbers from. */\ntype Tail = { leafId: string | null; nextSeq: number };\n\n/**\n * The memoised context-size estimate for a session's active path: the leaf it\n * was computed for, the ids on that path whose own estimate counts (rows under\n * a compaction overlay are replaced by the summary and do not), and the total.\n * A tail append and an update of a counted row adjust it in place; every other\n * write (branch append, delete, clear, compaction, import) drops it, and the\n * next `tokenEstimate` re-derives it from one path walk.\n */\ntype PathTokens = {\n leafId: string | null;\n counted: Set<string>;\n total: number;\n /** Rows the walk returned: the memo is only extended while under the path cap. */\n depth: number;\n};\n\nexport type UpdateOutcome = \"missing\" | \"unchanged\" | \"updated\";\n\nexport class SessionsCore {\n readonly io: SessionsIo;\n readonly #reservedMetadataKeys: readonly string[];\n readonly #listeners = new Set<SessionChangeListener>();\n readonly #tails = new Map<string, Tail>();\n readonly #pathTokens = new Map<string, PathTokens>();\n readonly #attachments: AttachmentStore;\n #tablesEnsured = false;\n /** True once the FTS index exists; it is built on the first `search()`. */\n #fts = false;\n\n constructor(options: SessionsOptions, io: SessionsIo) {\n this.io = io;\n this.#attachments = new AttachmentStore(io);\n this.#reservedMetadataKeys = options.reservedMetadataKeys ?? [];\n }\n\n // ── Schema ───────────────────────────────────────────────────────────────\n\n ensureTables(): void {\n if (this.#tablesEnsured) return;\n this.io.sqlWrite(\n `CREATE TABLE IF NOT EXISTS cf_agents_session_messages (\n session_id TEXT NOT NULL,\n id TEXT NOT NULL,\n seq INTEGER NOT NULL,\n parent_id TEXT,\n type TEXT NOT NULL DEFAULT 'message',\n role TEXT NOT NULL,\n content TEXT NOT NULL,\n content_chunks INTEGER NOT NULL DEFAULT 0,\n token_estimate INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL,\n PRIMARY KEY (session_id, id)\n ) WITHOUT ROWID`,\n []\n );\n this.io.sqlWrite(\n `CREATE TABLE IF NOT EXISTS cf_agents_session_message_chunks (\n session_id TEXT NOT NULL,\n id TEXT NOT NULL,\n idx INTEGER NOT NULL,\n content TEXT NOT NULL,\n PRIMARY KEY (session_id, id, idx)\n ) WITHOUT ROWID`,\n []\n );\n this.io.sqlWrite(\n `CREATE TABLE IF NOT EXISTS cf_agents_session_compactions (\n session_id TEXT NOT NULL,\n id TEXT NOT NULL,\n seq INTEGER NOT NULL,\n summary TEXT NOT NULL,\n from_message_id TEXT NOT NULL,\n to_message_id TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n PRIMARY KEY (session_id, id)\n ) WITHOUT ROWID`,\n []\n );\n this.io.sqlWrite(\n `CREATE TABLE IF NOT EXISTS cf_agents_session_config (\n session_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n PRIMARY KEY (session_id, key)\n ) WITHOUT ROWID`,\n []\n );\n this.#attachments.ensureTables();\n this.#fts = this.#tableExists(\"cf_agents_session_fts\");\n this.#tablesEnsured = true;\n }\n\n #tableExists(name: string): boolean {\n return (\n this.io.sql<{ name: string }>(\n \"SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?\",\n [name]\n ).length > 0\n );\n }\n\n /**\n * Build the FTS index on demand. Maintaining it costs an extra billed row\n * on every append, so it exists only on objects that have actually\n * searched; the first search pays a one-time SQL backfill instead.\n */\n #ensureFts(): void {\n if (this.#fts) return;\n this.io.sqlWrite(\n `CREATE VIRTUAL TABLE cf_agents_session_fts\n USING fts5(id UNINDEXED, session_id UNINDEXED, role UNINDEXED, content, tokenize='porter unicode61')`,\n []\n );\n this.#fts = true;\n this.#backfillMissingFtsRows();\n }\n\n /** Index rows that predate indexing, in SQL, without loading JSON into JS. */\n #backfillMissingFtsRows(): void {\n this.io.sqlWrite(\n `INSERT INTO cf_agents_session_fts (id, session_id, role, content)\n SELECT m.id, m.session_id, m.role,\n group_concat(json_extract(part.value, '$.text'), ' ')\n FROM cf_agents_session_messages AS m\n JOIN json_each(\n CASE WHEN json_valid(m.content) THEN m.content ELSE '{\"parts\":[]}' END,\n '$.parts'\n ) AS part\n WHERE json_extract(part.value, '$.type') = 'text'\n AND COALESCE(json_extract(part.value, '$.text'), '') <> ''\n AND NOT EXISTS (\n SELECT 1 FROM cf_agents_session_fts AS existing\n WHERE existing.id = m.id AND existing.session_id = m.session_id\n )\n GROUP BY m.id, m.session_id, m.role`,\n []\n );\n }\n\n /**\n * Lift the legacy `assistant_*` message and compaction tables.\n *\n * The copy is pure SQL, so SQLite streams it rather than materializing rows\n * in the isolate. Each source is then verified row by row against its\n * destination and DROPPED: keeping tombstones would leave every upgraded\n * object holding its history twice inside the same 10 GB. A table whose\n * verification fails is left in place with a `session:migration:incomplete`\n * event, and the method returns false so the caller leaves the schema\n * version unstamped and retries on a later start. `assistant_config`\n * belongs to Think, which lifts and drops it itself.\n */\n migrateLegacy(): boolean {\n this.#pathTokens.clear();\n let complete = true;\n const drop = (name: string): void => {\n if (this.#tableExists(name)) this.io.sqlWrite(`DROP TABLE ${name}`, []);\n };\n /**\n * Drop a lifted source only once every one of its rows has a copy holding\n * the same payload. Matching on the key alone would accept a destination\n * row that merely occupies the key.\n */\n const dropWhenCopied = (\n source: string,\n destination: string,\n payload: string\n ): void => {\n const [counts] = this.io.sql<{ source: number; copied: number }>(\n `SELECT\n (SELECT COUNT(*) FROM ${source}) AS source,\n (SELECT COUNT(*) FROM ${source} AS legacy\n JOIN ${destination} AS lifted\n ON lifted.session_id = legacy.session_id\n AND lifted.id = legacy.id\n AND lifted.${payload} = legacy.${payload}) AS copied`,\n []\n );\n if (counts && counts.source === counts.copied) {\n drop(source);\n return;\n }\n complete = false;\n this.io.emit(\"session:migration:incomplete\", {\n table: source,\n source: counts?.source ?? 0,\n copied: counts?.copied ?? 0\n });\n };\n\n if (this.#tableExists(\"assistant_messages\")) {\n this.io.sqlWrite(\n `INSERT OR IGNORE INTO cf_agents_session_messages\n (session_id, id, seq, parent_id, role, content, token_estimate, created_at)\n SELECT session_id, id,\n ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY created_at ASC, rowid ASC),\n parent_id, role, content,\n CAST(LENGTH(CAST(content AS BLOB)) / 4 AS INTEGER),\n COALESCE(CAST(strftime('%s', created_at) AS INTEGER), 0) * 1000\n FROM assistant_messages`,\n []\n );\n if (this.#fts) this.#backfillMissingFtsRows();\n dropWhenCopied(\n \"assistant_messages\",\n \"cf_agents_session_messages\",\n \"content\"\n );\n }\n if (this.#tableExists(\"assistant_compactions\")) {\n this.io.sqlWrite(\n `INSERT OR IGNORE INTO cf_agents_session_compactions\n (session_id, id, seq, summary, from_message_id, to_message_id, created_at)\n SELECT session_id, id,\n ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY created_at ASC, rowid ASC),\n summary, from_message_id, to_message_id,\n COALESCE(CAST(strftime('%s', created_at) AS INTEGER), 0) * 1000\n FROM assistant_compactions`,\n []\n );\n dropWhenCopied(\n \"assistant_compactions\",\n \"cf_agents_session_compactions\",\n \"summary\"\n );\n }\n // Neither carries data the new schema needs: session summaries derive\n // from message rows, and the index is rebuilt from message text on the\n // first search.\n drop(\"assistant_sessions\");\n drop(\"assistant_fts\");\n return complete;\n }\n\n // ── Change feed ──────────────────────────────────────────────────────────\n\n subscribe(listener: SessionChangeListener): () => void {\n this.#listeners.add(listener);\n return () => this.#listeners.delete(listener);\n }\n\n /**\n * Dispatch after a durable write. A listener that throws must not turn a\n * committed write into a rejected call, so failures are reported through\n * telemetry and dispatch continues.\n */\n async notify(event: SessionChangeEvent): Promise<void> {\n for (const listener of this.#listeners) {\n try {\n await listener(event);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n console.warn(`[Sessions] change listener failed: ${detail}`);\n this.io.emit(\"session:error\", {\n sessionId: event.sessionId,\n event: event.type,\n error: detail\n });\n }\n }\n }\n\n // ── Reads ────────────────────────────────────────────────────────────────\n\n /**\n * Continuation slices for the given ids, keyed by id and already joined in\n * `idx` order. One query, and only for ids the caller knows have them.\n */\n #continuations(\n sessionId: string,\n ids: readonly string[]\n ): Map<string, string> {\n const joined = new Map<string, string>();\n if (ids.length === 0) return joined;\n const rows = this.io.sql<{ id: string; content: string }>(\n `SELECT id, content FROM cf_agents_session_message_chunks\n WHERE session_id = ? AND id IN (SELECT value FROM json_each(?))\n ORDER BY id ASC, idx ASC`,\n [sessionId, JSON.stringify([...ids])]\n );\n for (const row of rows) {\n joined.set(row.id, (joined.get(row.id) ?? \"\") + row.content);\n }\n return joined;\n }\n\n /** Reassemble one stored row, reading continuations only when it has any. */\n #content(sessionId: string, id: string): string | null {\n const rows = this.io.sql<{ content: string; content_chunks: number }>(\n \"SELECT content, content_chunks FROM cf_agents_session_messages WHERE session_id = ? AND id = ?\",\n [sessionId, id]\n );\n if (rows.length === 0) return null;\n const row = rows[0];\n if (row.content_chunks === 0) return row.content;\n return row.content + (this.#continuations(sessionId, [id]).get(id) ?? \"\");\n }\n\n #hasParent(sessionId: string, id: string): boolean {\n const [row] = this.io.sql<{ parent_id: string | null }>(\n \"SELECT parent_id FROM cf_agents_session_messages WHERE session_id = ? AND id = ?\",\n [sessionId, id]\n );\n return row?.parent_id != null;\n }\n\n exists(sessionId: string, id: string): boolean {\n return (\n this.io.sql<{ id: string }>(\n \"SELECT id FROM cf_agents_session_messages WHERE session_id = ? AND id = ?\",\n [sessionId, id]\n ).length > 0\n );\n }\n\n getMessage(sessionId: string, id: string): SessionMessage | null {\n const content = this.#content(sessionId, id);\n const parsed = content === null ? null : this.#parse(content);\n return parsed && this.#inline(parsed);\n }\n\n /**\n * The session's newest row. Children insert after their parents, so the\n * max-seq row is provably childless: it is the active leaf, and the next\n * append numbers from it. Read once per object lifetime, since the table\n * is keyed by id and finding the max means scanning the session's rows.\n */\n #tail(sessionId: string): Tail {\n const cached = this.#tails.get(sessionId);\n if (cached) return cached;\n const [row] = this.io.sql<{ id: string; seq: number }>(\n \"SELECT id, seq FROM cf_agents_session_messages WHERE session_id = ? ORDER BY seq DESC LIMIT 1\",\n [sessionId]\n );\n const tail: Tail = row\n ? { leafId: row.id, nextSeq: row.seq + 1 }\n : { leafId: null, nextSeq: 1 };\n this.#tails.set(sessionId, tail);\n return tail;\n }\n\n /**\n * Drop the in-memory tail and token-total caches for a session. For a\n * caller whose enclosing transaction rolled back after an append or update\n * ran inside it: the rows are gone but the caches already moved. The next\n * read re-derives both from storage.\n */\n forgetCaches(sessionId: string): void {\n this.#tails.delete(sessionId);\n this.#pathTokens.delete(sessionId);\n }\n\n latestLeafId(sessionId: string): string | null {\n return this.#tail(sessionId).leafId;\n }\n\n #resolveLeafId(sessionId: string, leafId?: string | null): string | null {\n if (leafId) return this.exists(sessionId, leafId) ? leafId : null;\n return this.latestLeafId(sessionId);\n }\n\n getLatestLeaf(sessionId: string): SessionMessage | null {\n const leafId = this.latestLeafId(sessionId);\n return leafId ? this.getMessage(sessionId, leafId) : null;\n }\n\n getBranches(sessionId: string, messageId: string): SessionMessage[] {\n const rows = this.io.sql<{\n id: string;\n content: string;\n content_chunks: number;\n }>(\n `SELECT id, content, content_chunks FROM cf_agents_session_messages\n WHERE session_id = ? AND parent_id = ? ORDER BY seq ASC`,\n [sessionId, messageId]\n );\n const continued = this.#continuations(\n sessionId,\n rows.filter((row) => row.content_chunks > 0).map((row) => row.id)\n );\n const result: SessionMessage[] = [];\n for (const row of rows) {\n const parsed = this.#parse(\n row.content_chunks === 0\n ? row.content\n : row.content + (continued.get(row.id) ?? \"\")\n );\n if (parsed) result.push(this.#inline(parsed));\n }\n return result;\n }\n\n /**\n * The active branch path as content-free rows, root → leaf. Recurses over\n * (id, parent_id) only — carrying content through the recursive queue\n * materializes the transcript several times inside SQLite (#1710).\n * `bytes` counts the message row, its continuation rows, AND the payloads\n * it points at, charged at the size they take once inlined, so a byte\n * budget over these rows bounds real hydrated memory.\n */\n pathRowStats(sessionId: string, leafId?: string | null): SessionRowStat[] {\n const leaf = this.#resolveLeafId(sessionId, leafId);\n if (!leaf) return [];\n return this.io.sql<SessionRowStat>(\n `WITH RECURSIVE path(id, parent_id, depth) AS (\n SELECT id, parent_id, 0 FROM cf_agents_session_messages\n WHERE session_id = ? AND id = ?\n UNION ALL\n SELECT m.id, m.parent_id, p.depth + 1 FROM cf_agents_session_messages m\n JOIN path p ON m.id = p.parent_id\n WHERE m.session_id = ? AND p.depth < ${MAX_PATH_DEPTH}\n )\n SELECT path.id AS id, am.role AS role,\n LENGTH(CAST(am.content AS BLOB)) + CASE WHEN am.content_chunks = 0 THEN 0\n ELSE COALESCE((\n SELECT SUM(LENGTH(CAST(c.content AS BLOB)))\n FROM cf_agents_session_message_chunks c\n WHERE c.session_id = am.session_id AND c.id = am.id\n ), 0) END\n + COALESCE((\n SELECT SUM((meta.bytes + 2) / 3 * 4)\n FROM cf_agents_session_attachment_refs r\n JOIN cf_agents_session_attachment_meta meta ON meta.hash = r.hash\n WHERE r.session_id = am.session_id AND r.message_id = am.id\n ), 0) AS bytes,\n am.token_estimate AS tokenEstimate\n FROM path JOIN cf_agents_session_messages am\n ON am.session_id = ? AND am.id = path.id\n ORDER BY path.depth DESC`,\n [sessionId, leaf, sessionId, sessionId]\n );\n }\n\n /**\n * The active branch path as ids alone, root → leaf. The cheapest walk the\n * tree allows — one row per step, following `parent_id` by primary key —\n * for readers that will hydrate only a few rows and do not need the sizes\n * `pathRowStats` charges per row.\n */\n #pathIds(sessionId: string, leafId?: string | null): string[] {\n const leaf = this.#resolveLeafId(sessionId, leafId);\n if (!leaf) return [];\n return this.io\n .sql<{ id: string }>(\n `WITH RECURSIVE path(id, parent_id, depth) AS (\n SELECT id, parent_id, 0 FROM cf_agents_session_messages\n WHERE session_id = ? AND id = ?\n UNION ALL\n SELECT m.id, m.parent_id, p.depth + 1 FROM cf_agents_session_messages m\n JOIN path p ON m.id = p.parent_id\n WHERE m.session_id = ? AND p.depth < ${MAX_PATH_DEPTH}\n )\n SELECT id FROM path ORDER BY depth DESC`,\n [sessionId, leaf, sessionId]\n )\n .map((row) => row.id);\n }\n\n /**\n * Split path rows into bounded hydration queries: at most `maxRows` rows,\n * and — when the rows carry sizes — at most `HISTORY_CONTENT_CHUNK_BYTES`\n * of stored content, with a single oversized row always standing alone.\n */\n *#boundedStatsChunks(\n rows: readonly PathRow[],\n maxRows = HISTORY_CONTENT_CHUNK_SIZE\n ): Generator<readonly PathRow[], void, undefined> {\n let start = 0;\n while (start < rows.length) {\n let end = start;\n let bytes = 0;\n while (end < rows.length && end - start < maxRows) {\n const nextBytes = rows[end].bytes;\n if (end > start && bytes + nextBytes > HISTORY_CONTENT_CHUNK_BYTES) {\n break;\n }\n bytes += nextBytes;\n end++;\n }\n yield rows.slice(start, end);\n start = end;\n }\n }\n\n /**\n * Fetch and parse one already-bounded content window. The common window\n * has no continuation rows at all, so the second query is issued only for\n * the ids that actually carry them and never runs otherwise.\n */\n #contentByStats(\n sessionId: string,\n rows: readonly PathRow[]\n ): Map<string, SessionMessage> {\n const result = new Map<string, SessionMessage>();\n if (rows.length === 0) return result;\n const fetched = this.io.sql<{\n id: string;\n content: string;\n content_chunks: number;\n }>(\n `SELECT id, content, content_chunks FROM cf_agents_session_messages\n WHERE session_id = ? AND id IN (SELECT value FROM json_each(?))`,\n [sessionId, JSON.stringify(rows.map((row) => row.id))]\n );\n const continued = this.#continuations(\n sessionId,\n fetched.filter((row) => row.content_chunks > 0).map((row) => row.id)\n );\n for (const row of fetched) {\n const parsed = this.#parse(\n row.content_chunks === 0\n ? row.content\n : row.content + (continued.get(row.id) ?? \"\")\n );\n if (parsed) result.set(row.id, this.#inline(parsed));\n }\n return result;\n }\n\n /**\n * Stream a known path window without retaining earlier content chunks.\n *\n * The path is first cut into segments — a compaction overlay, or a run of\n * raw rows between overlays — in root → leaf order. `newestFirst` walks\n * those segments, and the rows inside each, from the leaf instead, in\n * small fixed windows, so the first content fetched is the newest and a\n * consumer that stops early never touches older rows.\n */\n async *#streamStats(\n sessionId: string,\n stats: readonly PathRow[],\n signal?: AbortSignal,\n newestFirst = false,\n plannedSpans?: readonly OverlaySpan[]\n ): AsyncGenerator<SessionMessage, void, undefined> {\n const spans =\n plannedSpans ??\n planOverlays(\n stats.map((row) => row.id),\n this.getCompactions(sessionId)\n );\n const spanByStart = new Map(spans.map((span) => [span.startIndex, span]));\n\n type Segment = { overlay: StoredCompaction } | { rows: readonly PathRow[] };\n const segments: Segment[] = [];\n let index = 0;\n while (index < stats.length) {\n const span = spanByStart.get(index);\n if (span) {\n segments.push({ overlay: span.compaction });\n index = span.endIndex + 1;\n continue;\n }\n let runEnd = index + 1;\n while (runEnd < stats.length && !spanByStart.has(runEnd)) runEnd++;\n segments.push({ rows: stats.slice(index, runEnd) });\n index = runEnd;\n }\n if (newestFirst) segments.reverse();\n\n for (const segment of segments) {\n if (signal?.aborted) {\n throw signal.reason ?? new Error(\"History read aborted\");\n }\n if (\"overlay\" in segment) {\n yield overlayMessage(segment.overlay);\n continue;\n }\n const rows = newestFirst ? [...segment.rows].reverse() : segment.rows;\n const windowRows = newestFirst\n ? NEWEST_FIRST_WINDOW_ROWS\n : HISTORY_CONTENT_CHUNK_SIZE;\n for (const chunk of this.#boundedStatsChunks(rows, windowRows)) {\n const content = this.#contentByStats(sessionId, chunk);\n for (const row of chunk) {\n const parsed = content.get(row.id);\n if (parsed) yield parsed;\n }\n if (signal?.aborted) {\n throw signal.reason ?? new Error(\"History read aborted\");\n }\n }\n }\n }\n\n /**\n * Stream the path ending at `leafId` (default: active leaf), root → leaf\n * (or leaf → root with `newestFirst`), compaction overlays collapsed. Peak\n * memory is one bounded content window — never the whole transcript.\n */\n async *streamHistory(\n sessionId: string,\n options: HistoryReadOptions\n ): AsyncGenerator<SessionMessage, void, undefined> {\n if (options.newestFirst === true) {\n yield* this.#walkFromLeaf(sessionId, options.leafId, options.signal);\n return;\n }\n const stats = this.pathRowStats(sessionId, options.leafId);\n if (stats.length === 0) return;\n yield* this.#streamStats(sessionId, stats, options.signal);\n }\n\n /**\n * The path leaf → root as a chain of point reads: each row names its\n * parent, so the next read is known before the current message is\n * yielded, and a consumer that stops early has paid for exactly the rows it\n * saw.\n *\n * Compaction overlays are honored without planning them up front. An\n * overlay that applies to this branch ends at a row the walk reaches\n * before any row it covers, so the raw walk is exact until it lands on\n * some compaction's `toMessageId`. Only then is the remaining prefix read\n * as ids and planned root → leaf — the order overlay selection is defined\n * in — and streamed leaf-first with the overlays collapsed. A lookup that\n * stops in the messages after the last compaction never pays for that.\n */\n async *#walkFromLeaf(\n sessionId: string,\n leafId: string | null | undefined,\n signal?: AbortSignal\n ): AsyncGenerator<SessionMessage, void, undefined> {\n const compactions = this.getCompactions(sessionId);\n const spanEnds = new Set(compactions.map((c) => c.toMessageId));\n let next = this.#resolveLeafId(sessionId, leafId);\n let depth = 0;\n while (next !== null && depth <= MAX_PATH_DEPTH) {\n if (signal?.aborted) {\n throw signal.reason ?? new Error(\"History read aborted\");\n }\n if (spanEnds.has(next)) {\n yield* this.#streamOverlaidPrefix(\n sessionId,\n leafId,\n next,\n compactions,\n signal\n );\n return;\n }\n const [row] = this.io.sql<{\n parent_id: string | null;\n content: string;\n content_chunks: number;\n }>(\n `SELECT parent_id, content, content_chunks FROM cf_agents_session_messages\n WHERE session_id = ? AND id = ?`,\n [sessionId, next]\n );\n if (!row) return;\n const json =\n row.content_chunks === 0\n ? row.content\n : row.content +\n (this.#continuations(sessionId, [next]).get(next) ?? \"\");\n const parsed = this.#parse(json);\n if (parsed) yield this.#inline(parsed);\n next = row.parent_id;\n depth++;\n }\n }\n\n /**\n * The path from `fromId` down to the root, leaf-first with overlays\n * collapsed. Spans are planned over the WHOLE path (selection is\n * root → leaf and a chosen span suppresses overlaps inside it), then only\n * those ending at or before `fromId` apply: `fromId` is some compaction's\n * end, and a chosen span reaching past it would have ended at a row the\n * raw walk visited first.\n */\n async *#streamOverlaidPrefix(\n sessionId: string,\n leafId: string | null | undefined,\n fromId: string,\n compactions: readonly StoredCompaction[],\n signal?: AbortSignal\n ): AsyncGenerator<SessionMessage, void, undefined> {\n const ids = this.#pathIds(sessionId, leafId);\n const end = ids.indexOf(fromId);\n if (end === -1) return;\n const spans = planOverlays(ids, compactions).filter(\n (span) => span.endIndex <= end\n );\n yield* this.#streamStats(\n sessionId,\n ids.slice(0, end + 1).map((id) => ({ id, bytes: 0 })),\n signal,\n true,\n spans\n );\n }\n\n async getHistory(\n sessionId: string,\n options: HistoryReadOptions\n ): Promise<SessionMessage[]> {\n const messages: SessionMessage[] = [];\n for await (const message of this.streamHistory(sessionId, options)) {\n messages.push(message);\n }\n return messages;\n }\n\n /**\n * Byte-budgeted read of the most recent messages on the active branch\n * path — the longest suffix whose stored size fits `maxContentBytes`.\n *\n * There is no message-count floor: one used to exist, and it admitted\n * rows regardless of size, so a window of media-heavy messages could\n * hydrate far past the limit meant to bound it. The newest message is\n * always returned even if it alone exceeds the budget, since returning\n * nothing is worse. Overlays whose anchors fall outside the window are\n * skipped, showing the raw recent messages.\n */\n async getRecentHistory(\n sessionId: string,\n maxContentBytes: number,\n leafId?: string | null\n ): Promise<RecentHistoryResult> {\n const stats = this.pathRowStats(sessionId, leafId);\n if (stats.length === 0) {\n return { messages: [], truncated: false, totalContentBytes: 0 };\n }\n const totalContentBytes = stats.reduce((sum, row) => sum + row.bytes, 0);\n let start = stats.length - 1;\n let used = stats[start].bytes;\n while (start > 0) {\n const next = stats[start - 1].bytes;\n if (used + next > maxContentBytes) break;\n start--;\n used += next;\n }\n\n const messages: SessionMessage[] = [];\n for await (const message of this.#streamStats(\n sessionId,\n stats.slice(start)\n )) {\n messages.push(message);\n }\n // The path cap hides older rows exactly as the budget does. A read that\n // filled the cap is truncated only if the oldest row it returned still\n // has a parent; a branch of exactly the cap's length is complete.\n const capped =\n stats.length > MAX_PATH_DEPTH && this.#hasParent(sessionId, stats[0].id);\n return { messages, truncated: start > 0 || capped, totalContentBytes };\n }\n\n /**\n * Heuristic token estimate for the active path with compaction overlays\n * applied: stamped per-row estimates, minus compacted spans, plus their\n * summaries. Derived from content-free rows on each call; it gates cheap\n * triggers only, and model-reported usage stays authoritative.\n */\n tokenEstimate(sessionId: string): number {\n const leafId = this.latestLeafId(sessionId);\n const memo = this.#pathTokens.get(sessionId);\n if (memo && memo.leafId === leafId)\n return Math.max(0, Math.ceil(memo.total));\n\n const stats = this.pathRowStats(sessionId);\n const counted = new Set(stats.map((row) => row.id));\n let tokens = stats.reduce((sum, row) => sum + row.tokenEstimate, 0);\n for (const span of planOverlays(\n stats.map((row) => row.id),\n this.getCompactions(sessionId)\n )) {\n for (let i = span.startIndex; i <= span.endIndex; i++) {\n tokens -= stats[i].tokenEstimate;\n counted.delete(stats[i].id);\n }\n tokens += estimateStringTokens(span.compaction.summary);\n }\n this.#pathTokens.set(sessionId, {\n leafId,\n counted,\n total: tokens,\n depth: stats.length\n });\n return Math.max(0, Math.ceil(tokens));\n }\n\n // ── Writes ───────────────────────────────────────────────────────────────\n\n stripReservedMetadata(message: SessionMessage): SessionMessage {\n if (\n this.#reservedMetadataKeys.length === 0 ||\n typeof message.metadata !== \"object\" ||\n message.metadata === null ||\n Array.isArray(message.metadata)\n ) {\n return message;\n }\n const metadata: Record<string, unknown> = { ...message.metadata };\n let changed = false;\n for (const key of this.#reservedMetadataKeys) {\n if (key in metadata) {\n delete metadata[key];\n changed = true;\n }\n }\n if (!changed) return message;\n if (Object.keys(metadata).length > 0) return { ...message, metadata };\n const { metadata: _dropped, ...withoutMetadata } = message;\n return withoutMetadata;\n }\n\n /**\n * Stamped row estimate: the part heuristic over the message as written,\n * plus a weight per inline file payload so media never counts as zero.\n */\n estimateRowTokens(message: SessionMessage): number {\n let tokens = estimateMessageTokens([message]);\n for (const part of message.parts) {\n if (part.type !== \"file\") continue;\n if (typeof part.url === \"string\" && part.url.startsWith(\"data:\")) {\n tokens += estimateAttachmentTokens(\n part.mediaType ?? \"application/octet-stream\",\n estimatedDataUrlBytes(part.url)\n );\n }\n }\n return tokens;\n }\n\n /**\n * Write one message's slices: the row itself plus its continuation rows.\n * Callers run this inside their own transaction, so a message and its\n * continuations always commit together.\n */\n #writeContinuations(\n sessionId: string,\n id: string,\n slices: readonly string[]\n ): void {\n for (let idx = 1; idx < slices.length; idx++) {\n this.io.sqlWrite(\n `INSERT OR REPLACE INTO cf_agents_session_message_chunks\n (session_id, id, idx, content) VALUES (?, ?, ?, ?)`,\n [sessionId, id, idx, slices[idx]]\n );\n }\n }\n\n /**\n * Durable append. The caller has already sanitized the message. Message,\n * continuation, attachment, and FTS rows commit in one synchronous SQLite\n * transaction. Returns the stored message: the input itself when it was\n * inserted (extraction is lossless, so the two are identical), or the row\n * already holding the id when it was not.\n */\n append(\n sessionId: string,\n message: SessionMessage,\n parentId: string | null | undefined,\n tokenEstimate: number\n ): { inserted: boolean; message: SessionMessage } {\n // A repeated append is answered from storage; the common path (a fresh\n // id) costs a key-only probe rather than a content read.\n if (this.exists(sessionId, message.id)) {\n const existing = this.getMessage(sessionId, message.id);\n if (existing) return { inserted: false, message: existing };\n }\n\n // `undefined` attaches to the tail and needs no validation read. A\n // caller-supplied id is untrusted and falls back to a root append when\n // it does not belong to this session.\n const tail = this.#tail(sessionId);\n let parent: string | null;\n if (parentId === undefined) {\n parent = tail.leafId;\n } else {\n parent = parentId && this.exists(sessionId, parentId) ? parentId : null;\n }\n\n // Inline media leaves the message before it is serialized, so the row\n // holds a pointer and never the payload. Addresses are computed here, out\n // of the transaction; the transaction only writes.\n const { message: staged, attachments } = extractAttachments(message);\n const slices = splitContent(JSON.stringify(staged));\n const seq = tail.nextSeq;\n this.io.transaction(() => {\n for (const attachment of attachments) {\n this.#attachments.put(attachment.payload, attachment.hash);\n }\n this.io.sqlWrite(\n `INSERT INTO cf_agents_session_messages\n (session_id, id, seq, parent_id, role, content, content_chunks, token_estimate, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n sessionId,\n message.id,\n seq,\n parent,\n message.role,\n slices[0],\n slices.length - 1,\n tokenEstimate,\n Date.now()\n ]\n );\n this.#writeContinuations(sessionId, message.id, slices);\n this.#attachments.addRefs(\n sessionId,\n message.id,\n attachments.map((attachment) => attachment.hash)\n );\n this.#indexFts(sessionId, staged, false);\n });\n\n // The freshly inserted row is the most recent childless node, so it is\n // now the latest leaf — true even for an explicit-parent branch append.\n this.#tails.set(sessionId, { leafId: message.id, nextSeq: seq + 1 });\n const memo = this.#pathTokens.get(sessionId);\n if (memo) {\n if (memo.leafId === parent && memo.depth <= MAX_PATH_DEPTH) {\n // The row extends the memoised path: count it, no re-walk.\n memo.leafId = message.id;\n memo.counted.add(message.id);\n memo.total += tokenEstimate;\n memo.depth += 1;\n } else {\n // A branch, or a path at the cap: the walk would drop its oldest\n // row, which the memo cannot see. Re-derive on the next read.\n this.#pathTokens.delete(sessionId);\n }\n }\n this.io.emit(\"session:message:appended\", {\n sessionId,\n messageId: message.id,\n tokenEstimate\n });\n return { inserted: true, message };\n }\n\n /**\n * Durable update of an existing row. An identical row writes nothing: no\n * row, no continuation, no FTS, no event. The no-op guard compares the\n * FULL reassembled content, not just the slice the message row holds.\n */\n update(\n sessionId: string,\n message: SessionMessage,\n tokenEstimate: number\n ): UpdateOutcome {\n const oldRows = this.io.sql<{\n content: string;\n content_chunks: number;\n token_estimate: number;\n }>(\n \"SELECT content, content_chunks, token_estimate FROM cf_agents_session_messages WHERE session_id = ? AND id = ?\",\n [sessionId, message.id]\n );\n if (oldRows.length === 0) return \"missing\";\n const old = oldRows[0];\n const oldContent =\n old.content_chunks === 0\n ? old.content\n : old.content +\n (this.#continuations(sessionId, [message.id]).get(message.id) ?? \"\");\n // Compare in stored form: a re-sent identical image extracts to the same\n // address, so an unchanged update still writes nothing.\n const { message: staged, attachments } = extractAttachments(message);\n const json = JSON.stringify(staged);\n if (oldContent === json) return \"unchanged\";\n\n const slices = splitContent(json);\n this.io.transaction(() => {\n for (const attachment of attachments) {\n this.#attachments.put(attachment.payload, attachment.hash);\n }\n this.io.sqlWrite(\n `UPDATE cf_agents_session_messages\n SET role = ?, content = ?, content_chunks = ?, token_estimate = ?\n WHERE session_id = ? AND id = ?`,\n [\n message.role,\n slices[0],\n slices.length - 1,\n tokenEstimate,\n sessionId,\n message.id\n ]\n );\n // A message that shrank leaves surplus continuations behind; they go\n // in the same transaction as the row that stopped referencing them.\n if (old.content_chunks > slices.length - 1) {\n this.io.sqlWrite(\n `DELETE FROM cf_agents_session_message_chunks\n WHERE session_id = ? AND id = ? AND idx > ?`,\n [sessionId, message.id, slices.length - 1]\n );\n }\n this.#writeContinuations(sessionId, message.id, slices);\n // Payloads are stored before references move, so a hash this message\n // still uses is never momentarily unreferenced and collected.\n this.#attachments.replaceRefs(\n sessionId,\n message.id,\n attachments.map((attachment) => attachment.hash)\n );\n this.#indexFts(sessionId, staged, true);\n });\n const memo = this.#pathTokens.get(sessionId);\n if (memo?.counted.has(message.id)) {\n memo.total += tokenEstimate - (old.token_estimate ?? 0);\n }\n this.io.emit(\"session:message:updated\", {\n sessionId,\n messageId: message.id\n });\n return \"updated\";\n }\n\n /**\n * Delete rows, SPLICING children to their grandparent so a mid-chain\n * delete never decapitates older history. Only surviving boundary children\n * are rewired: a prefix delete writes one boundary child, not one child\n * per deleted message.\n */\n deleteMessages(sessionId: string, messageIds: string[]): void {\n const uniqueIds = [...new Set(messageIds)];\n if (uniqueIds.length === 0) return;\n const ids = JSON.stringify(uniqueIds);\n\n this.io.transaction(() => {\n this.io.sqlWrite(\n `WITH RECURSIVE\n deleted(id) AS (SELECT value FROM json_each(?)),\n rewire(child_id, ancestor_id, depth) AS (\n SELECT child.id, child.parent_id, 0\n FROM cf_agents_session_messages AS child\n JOIN deleted ON deleted.id = child.parent_id\n WHERE child.session_id = ?\n AND child.id NOT IN (SELECT id FROM deleted)\n UNION ALL\n SELECT rewire.child_id, parent.parent_id, rewire.depth + 1\n FROM rewire\n JOIN cf_agents_session_messages AS parent\n ON parent.id = rewire.ancestor_id\n JOIN deleted ON deleted.id = parent.id\n WHERE parent.session_id = ? AND rewire.depth < 10000\n ),\n nearest(child_id, ancestor_id) AS (\n SELECT child_id, ancestor_id FROM rewire\n WHERE ancestor_id IS NULL\n OR ancestor_id NOT IN (SELECT id FROM deleted)\n )\n UPDATE cf_agents_session_messages\n SET parent_id = (\n SELECT nearest.ancestor_id FROM nearest\n WHERE nearest.child_id = cf_agents_session_messages.id\n )\n WHERE session_id = ?\n AND id IN (SELECT child_id FROM nearest)`,\n [ids, sessionId, sessionId, sessionId]\n );\n this.io.sqlWrite(\n `DELETE FROM cf_agents_session_messages\n WHERE session_id = ? AND id IN (SELECT value FROM json_each(?))`,\n [sessionId, ids]\n );\n this.io.sqlWrite(\n `DELETE FROM cf_agents_session_message_chunks\n WHERE session_id = ? AND id IN (SELECT value FROM json_each(?))`,\n [sessionId, ids]\n );\n this.#attachments.releaseMessages(sessionId, uniqueIds);\n if (this.#fts) {\n this.io.sqlWrite(\n `DELETE FROM cf_agents_session_fts\n WHERE session_id = ? AND id IN (SELECT value FROM json_each(?))`,\n [sessionId, ids]\n );\n }\n });\n // The leaf may be among the deleted rows; re-derive on the next append.\n this.#tails.delete(sessionId);\n this.#pathTokens.delete(sessionId);\n this.io.emit(\"session:messages:deleted\", {\n sessionId,\n count: uniqueIds.length\n });\n }\n\n clearMessages(sessionId: string): void {\n this.io.transaction(() => {\n this.io.sqlWrite(\n \"DELETE FROM cf_agents_session_messages WHERE session_id = ?\",\n [sessionId]\n );\n this.io.sqlWrite(\n \"DELETE FROM cf_agents_session_message_chunks WHERE session_id = ?\",\n [sessionId]\n );\n this.io.sqlWrite(\n \"DELETE FROM cf_agents_session_compactions WHERE session_id = ?\",\n [sessionId]\n );\n this.#attachments.releaseSession(sessionId);\n if (this.#fts) {\n this.io.sqlWrite(\n \"DELETE FROM cf_agents_session_fts WHERE session_id = ?\",\n [sessionId]\n );\n }\n });\n this.#tails.set(sessionId, { leafId: null, nextSeq: 1 });\n this.#pathTokens.delete(sessionId);\n this.io.emit(\"session:cleared\", { sessionId });\n }\n\n // ── Compaction storage ───────────────────────────────────────────────────\n\n addCompaction(\n sessionId: string,\n summary: string,\n fromMessageId: string,\n toMessageId: string\n ): StoredCompaction {\n const id = crypto.randomUUID();\n const now = Date.now();\n const seq =\n this.io.sql<{ seq: number }>(\n \"SELECT COALESCE(MAX(seq), 0) + 1 AS seq FROM cf_agents_session_compactions WHERE session_id = ?\",\n [sessionId]\n )[0]?.seq ?? 1;\n this.io.sqlWrite(\n `INSERT INTO cf_agents_session_compactions\n (session_id, id, seq, summary, from_message_id, to_message_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n [sessionId, id, seq, summary, fromMessageId, toMessageId, now]\n );\n // The new overlay changes which rows count; re-derive on the next read.\n this.#pathTokens.delete(sessionId);\n this.io.emit(\"session:compacted\", { sessionId, compactionId: id });\n return {\n id,\n summary,\n fromMessageId,\n toMessageId,\n createdAt: new Date(now).toISOString()\n };\n }\n\n getCompactions(sessionId: string): StoredCompaction[] {\n return this.io\n .sql<{\n id: string;\n summary: string;\n from_message_id: string;\n to_message_id: string;\n created_at: number;\n }>(\n `SELECT id, summary, from_message_id, to_message_id, created_at\n FROM cf_agents_session_compactions\n WHERE session_id = ? ORDER BY seq ASC`,\n [sessionId]\n )\n .map((row) => ({\n id: row.id,\n summary: row.summary,\n fromMessageId: row.from_message_id,\n toMessageId: row.to_message_id,\n createdAt: new Date(row.created_at).toISOString()\n }));\n }\n\n // ── Search ───────────────────────────────────────────────────────────────\n\n search(sessionId: string, query: string, limit: number): SearchResult[] {\n this.#ensureFts();\n // Quote the query as a literal phrase, escaping embedded double quotes,\n // so user input cannot inject FTS5 syntax.\n const sanitized = `\"${query.replace(/\"/g, '\"\"')}\"`;\n return this.io\n .sql<{ id: string; role: string; content: string }>(\n `SELECT f.id, f.role, f.content FROM cf_agents_session_fts f\n INNER JOIN cf_agents_session_messages m\n ON m.session_id = f.session_id AND m.id = f.id\n WHERE cf_agents_session_fts MATCH ? AND f.session_id = ?\n ORDER BY rank LIMIT ?`,\n [sanitized, sessionId, limit]\n )\n .map((row) => ({ id: row.id, role: row.role, content: row.content }));\n }\n\n /** Maintain the FTS row when the index exists; an unchanged text writes nothing. */\n #indexFts(\n sessionId: string,\n message: SessionMessage,\n replace: boolean\n ): void {\n if (!this.#fts) return;\n const text = message.parts\n .filter((part) => part.type === \"text\")\n .map((part) => part.text ?? \"\")\n .join(\" \");\n if (replace) {\n const existing = this.io.sql<{ content: string }>(\n \"SELECT content FROM cf_agents_session_fts WHERE id = ? AND session_id = ?\",\n [message.id, sessionId]\n );\n if (existing.length > 0) {\n if (existing[0].content === text) return;\n this.io.sqlWrite(\n \"DELETE FROM cf_agents_session_fts WHERE id = ? AND session_id = ?\",\n [message.id, sessionId]\n );\n }\n }\n if (text) {\n this.io.sqlWrite(\n \"INSERT INTO cf_agents_session_fts (id, session_id, role, content) VALUES (?, ?, ?, ?)\",\n [message.id, sessionId, message.role, text]\n );\n }\n }\n\n // ── Import ───────────────────────────────────────────────────────────────\n\n /**\n * Import one historical message verbatim (migrations, cross-DO moves):\n * explicit parent and timestamp, stamped estimate, no change-feed events.\n */\n /** Returns `false` when the id already exists and nothing was written. */\n importMessage(\n sessionId: string,\n message: SessionMessage,\n options: { parentId: string | null; createdAt: number }\n ): boolean {\n const { message: staged, attachments } = extractAttachments(message);\n const slices = splitContent(JSON.stringify(staged));\n const tail = this.#tail(sessionId);\n let inserted = 0;\n this.io.transaction(() => {\n for (const attachment of attachments) {\n this.#attachments.put(attachment.payload, attachment.hash);\n }\n inserted = this.io.sqlWrite(\n `INSERT OR IGNORE INTO cf_agents_session_messages\n (session_id, id, seq, parent_id, role, content, content_chunks, token_estimate, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n sessionId,\n message.id,\n tail.nextSeq,\n options.parentId,\n message.role,\n slices[0],\n slices.length - 1,\n this.estimateRowTokens(message),\n options.createdAt\n ]\n );\n if (inserted === 0) return;\n this.#writeContinuations(sessionId, message.id, slices);\n this.#attachments.addRefs(\n sessionId,\n message.id,\n attachments.map((attachment) => attachment.hash)\n );\n this.#indexFts(sessionId, staged, false);\n });\n if (inserted === 0) return false;\n this.#tails.set(sessionId, {\n leafId: message.id,\n nextSeq: tail.nextSeq + 1\n });\n this.#pathTokens.delete(sessionId);\n return true;\n }\n\n // ── Parsing ──────────────────────────────────────────────────────────────\n\n /** Put attachment payloads back inline, so a read returns what was written. */\n #inline(message: SessionMessage): SessionMessage {\n return resolveAttachments(message, (hash) => this.#attachments.get(hash));\n }\n\n #parse(json: string): SessionMessage | null {\n try {\n const message = JSON.parse(json);\n if (\n typeof message?.id === \"string\" &&\n typeof message?.role === \"string\" &&\n Array.isArray(message?.parts)\n ) {\n return message;\n }\n } catch {\n /* skip unparseable rows, matching legacy behavior */\n }\n return null;\n }\n}\n","/**\n * Per-session handle returned by `Sessions.session()`. Carries the\n * session-scoped compaction policy and orchestrates every public write:\n * sanitize → strip reserved client metadata → durable write → change feed.\n *\n * The handle stores messages. Prompt assembly (context blocks, frozen\n * prompts, skills) lives in `agents/context` and composes with this handle\n * rather than living inside it.\n */\n\nimport type { CompactResult } from \"./compaction-helpers\";\nimport { COMPACTION_PREFIX } from \"./compaction-helpers\";\nimport type { SessionsCore } from \"./core\";\nimport { byteLength, sanitizeMessage } from \"./sanitize\";\nimport type {\n AppendOptions,\n AppendResult,\n HistoryBatchReadOptions,\n HistoryReadOptions,\n RecentHistoryResult,\n SearchResult,\n SessionMessage,\n SessionRowStat,\n StoredCompaction,\n WriteOptions\n} from \"./types\";\n\n/** Summarizes a branch into an overlay. */\nexport type CompactionFunction = (\n messages: SessionMessage[]\n) => Promise<CompactResult | null>;\n\nexport class Session {\n readonly sessionId: string;\n readonly #core: SessionsCore;\n readonly #ready: () => Promise<void>;\n\n #compactionFn: CompactionFunction | null = null;\n #tokenThreshold: number | undefined;\n\n /** @internal Constructed by the Sessions capability only. */\n constructor(\n sessionId: string,\n core: SessionsCore,\n ready: () => Promise<void>\n ) {\n this.sessionId = sessionId;\n this.#core = core;\n this.#ready = ready;\n }\n\n // ── Builder ──────────────────────────────────────────────────────────────\n\n /** Register the function `compact()` calls to summarize a branch. */\n onCompaction(fn: CompactionFunction): this {\n this.#compactionFn = fn;\n return this;\n }\n\n /**\n * Auto-compact after an append once the estimated token count crosses the\n * threshold. Requires `onCompaction()`. The estimate is derived from the\n * stamped per-row estimates, never from the transcript.\n */\n compactAfter(tokenThreshold: number): this {\n this.#tokenThreshold = tokenThreshold;\n return this;\n }\n\n // ── Reads ────────────────────────────────────────────────────────────────\n\n /**\n * Stream the active branch path root → leaf (leaf → root with\n * `newestFirst`) with compaction overlays applied. Peak memory is one\n * bounded content window, never the whole transcript, and a consumer that\n * breaks out early leaves the rows it never reached unread.\n */\n async *history(\n options: HistoryReadOptions = {}\n ): AsyncGenerator<SessionMessage, void, undefined> {\n await this.#ready();\n yield* this.#core.streamHistory(this.sessionId, options);\n }\n\n /**\n * Stream history in bounded non-empty batches. Both message count and\n * serialized bytes bound each batch; a single large message is yielded by\n * itself.\n */\n async *historyBatches(\n options: HistoryBatchReadOptions = {}\n ): AsyncGenerator<SessionMessage[], void, undefined> {\n const batchSize = Math.max(1, Math.floor(options.batchSize ?? 50));\n const maxBatchBytes = Math.max(\n 1,\n Math.floor(options.maxBatchBytes ?? 4 * 1024 * 1024)\n );\n let batch: SessionMessage[] = [];\n let batchBytes = 0;\n\n for await (const message of this.history(options)) {\n const bytes = byteLength(JSON.stringify(message));\n if (\n batch.length > 0 &&\n (batch.length >= batchSize || batchBytes + bytes > maxBatchBytes)\n ) {\n yield batch;\n batch = [];\n batchBytes = 0;\n }\n batch.push(message);\n batchBytes += bytes;\n if (batch.length >= batchSize || batchBytes >= maxBatchBytes) {\n yield batch;\n batch = [];\n batchBytes = 0;\n }\n }\n if (batch.length > 0) yield batch;\n }\n\n /**\n * Materialize the whole selected path. Prefer `history()` or\n * `getRecentHistory()` inside a Durable Object: this holds every message\n * of the branch in memory at once.\n */\n async getHistory(\n options: HistoryReadOptions = {}\n ): Promise<SessionMessage[]> {\n await this.#ready();\n return this.#core.getHistory(this.sessionId, options);\n }\n\n /**\n * Byte-budgeted read of the most recent messages on the active branch path\n * (always at least the leaf). The budget counts each row, its continuation\n * rows, and the payloads it points at, so it bounds hydrated memory (#1710).\n */\n async getRecentHistory(\n maxContentBytes: number,\n options: Pick<HistoryReadOptions, \"leafId\"> = {}\n ): Promise<RecentHistoryResult> {\n await this.#ready();\n return this.#core.getRecentHistory(\n this.sessionId,\n maxContentBytes,\n options.leafId\n );\n }\n\n /**\n * Per-row stored sizes (row plus continuation rows and attachments) and\n * stamped token estimates for the active branch path (root → leaf)\n * WITHOUT loading message content.\n */\n async getHistoryRowStats(leafId?: string | null): Promise<SessionRowStat[]> {\n await this.#ready();\n return this.#core.pathRowStats(this.sessionId, leafId);\n }\n\n async getMessage(id: string): Promise<SessionMessage | null> {\n await this.#ready();\n return this.#core.getMessage(this.sessionId, id);\n }\n\n async getLatestLeaf(): Promise<SessionMessage | null> {\n await this.#ready();\n return this.#core.getLatestLeaf(this.sessionId);\n }\n\n async getBranches(messageId: string): Promise<SessionMessage[]> {\n await this.#ready();\n return this.#core.getBranches(this.sessionId, messageId);\n }\n\n /**\n * Full-text search over this session's text parts. The index is built on\n * the first call and maintained from then on.\n */\n async search(\n query: string,\n options?: { limit?: number }\n ): Promise<SearchResult[]> {\n await this.#ready();\n return this.#core.search(this.sessionId, query, options?.limit ?? 20);\n }\n\n // ── Writes ───────────────────────────────────────────────────────────────\n\n /**\n * Append one message. Idempotent on id: a repeated append returns the row\n * already stored and dispatches an `append` event with `inserted: false`.\n */\n async appendMessage(\n message: SessionMessage,\n options: AppendOptions = {}\n ): Promise<AppendResult> {\n await this.#ready();\n const prepared = this.#prepare(message, options.source);\n const result = this.#core.append(\n this.sessionId,\n prepared.message,\n options.parentId,\n prepared.tokenEstimate\n );\n await this.#core.notify({\n type: \"append\",\n sessionId: this.sessionId,\n message: result.message,\n parentId: options.parentId,\n inserted: result.inserted\n });\n if (result.inserted) await this.#maybeAutoCompact();\n return result;\n }\n\n /**\n * Update one stored row. Returns the stored form, or `null` when the id is\n * not in this session. An unchanged message writes nothing and dispatches\n * no event.\n */\n async updateMessage(\n message: SessionMessage,\n options: WriteOptions = {}\n ): Promise<SessionMessage | null> {\n await this.#ready();\n const prepared = this.#prepare(message, options.source);\n const outcome = this.#core.update(\n this.sessionId,\n prepared.message,\n prepared.tokenEstimate\n );\n if (outcome === \"missing\") return null;\n if (outcome === \"updated\") {\n await this.#core.notify({\n type: \"update\",\n sessionId: this.sessionId,\n message: prepared.message\n });\n }\n return prepared.message;\n }\n\n /**\n * @internal Synchronous write aperture for same-isolate first-party\n * machinery that must commit a message inside a caller-owned SQLite\n * transaction — the stream cutover, where the finished message and the\n * discard of its stream rows land together. Bypasses readiness (the\n * caller owns startup ordering) and defers everything that follows a\n * write — the change-feed dispatch and auto-compaction: call the returned\n * `after()` once the transaction commits, or subscribers (the host's\n * message mirror) never hear about the write. If the transaction rolls\n * back after an `upsert` ran inside it, call `abandon()`: the write is\n * gone but the in-memory tail and token-total caches already moved.\n * Will break without notice; never use from application code.\n */\n __DO_NOT_USE_WILL_BREAK__sync(): {\n upsert(\n message: SessionMessage,\n options?: AppendOptions\n ): { result: AppendResult; after: () => Promise<void> };\n abandon(): void;\n } {\n return {\n abandon: () => this.#core.forgetCaches(this.sessionId),\n upsert: (message, options = {}) => {\n const prepared = this.#prepare(message, options.source);\n if (!this.#core.exists(this.sessionId, message.id)) {\n const result = this.#core.append(\n this.sessionId,\n prepared.message,\n options.parentId,\n prepared.tokenEstimate\n );\n return {\n result,\n after: async () => {\n await this.#core.notify({\n type: \"append\",\n sessionId: this.sessionId,\n message: result.message,\n parentId: options.parentId,\n inserted: result.inserted\n });\n if (result.inserted) await this.#maybeAutoCompact();\n }\n };\n }\n const outcome = this.#core.update(\n this.sessionId,\n prepared.message,\n prepared.tokenEstimate\n );\n return {\n result: { inserted: false, message: prepared.message },\n after: () =>\n outcome === \"updated\"\n ? this.#core.notify({\n type: \"update\",\n sessionId: this.sessionId,\n message: prepared.message\n })\n : Promise.resolve()\n };\n }\n };\n }\n\n async upsertMessage(\n message: SessionMessage,\n options: AppendOptions = {}\n ): Promise<AppendResult> {\n await this.#ready();\n if (!this.#core.exists(this.sessionId, message.id)) {\n return this.appendMessage(message, options);\n }\n const stored = await this.updateMessage(message, {\n source: options.source\n });\n return { inserted: false, message: stored ?? message };\n }\n\n /**\n * Import one historical message verbatim (migrations, cross-object moves):\n * explicit parent and timestamp. A row actually written dispatches an\n * `import` change event so a host cache can mark itself stale; it is not\n * an `append`, so a cache does not patch itself per imported row, and an\n * id that already exists writes nothing and dispatches nothing.\n */\n async importMessage(\n message: SessionMessage,\n options: { parentId: string | null; createdAt: number }\n ): Promise<void> {\n await this.#ready();\n const inserted = this.#core.importMessage(this.sessionId, message, options);\n if (!inserted) return;\n await this.#core.notify({\n type: \"import\",\n sessionId: this.sessionId,\n message,\n parentId: options.parentId\n });\n }\n\n async deleteMessages(messageIds: string[]): Promise<void> {\n await this.#ready();\n this.#core.deleteMessages(this.sessionId, messageIds);\n await this.#core.notify({\n type: \"delete\",\n sessionId: this.sessionId,\n messageIds\n });\n }\n\n async clearMessages(): Promise<void> {\n await this.#ready();\n this.#core.clearMessages(this.sessionId);\n await this.#core.notify({ type: \"clear\", sessionId: this.sessionId });\n }\n\n // ── Compaction ───────────────────────────────────────────────────────────\n\n /**\n * Store an overlay directly. Dispatches a `compaction` change event: the\n * rows are untouched, but what a path read returns has changed.\n */\n async addCompaction(\n summary: string,\n fromMessageId: string,\n toMessageId: string\n ): Promise<StoredCompaction> {\n await this.#ready();\n const compaction = this.#core.addCompaction(\n this.sessionId,\n summary,\n fromMessageId,\n toMessageId\n );\n await this.#core.notify({\n type: \"compaction\",\n sessionId: this.sessionId,\n compaction\n });\n return compaction;\n }\n\n async getCompactions(): Promise<StoredCompaction[]> {\n await this.#ready();\n return this.#core.getCompactions(this.sessionId);\n }\n\n /**\n * Run the registered compaction function and store the result as an\n * overlay. When `leafId` is given, compact that root-to-leaf branch instead\n * of the active branch. Requires `onCompaction()`. A compaction function\n * that throws is reported through the `session:error` capability event and\n * the call returns `null`.\n */\n async compact(leafId?: string | null): Promise<CompactResult | null> {\n await this.#ready();\n const fn = this.#compactionFn;\n if (!fn) {\n throw new Error(\n \"No compaction function registered. Call onCompaction() first.\"\n );\n }\n const history = await this.#core.getHistory(this.sessionId, { leafId });\n\n let result: CompactResult | null;\n try {\n result = await fn(history);\n } catch (error) {\n this.#core.io.emit(\"session:error\", {\n sessionId: this.sessionId,\n error: error instanceof Error ? error.message : String(error)\n });\n return null;\n }\n if (!result) return null;\n\n const historyIds = new Set(history.map((message) => message.id));\n if (!historyIds.has(result.toMessageId)) return null;\n\n // Iterative compaction extends only an overlay visible on this branch.\n const existing = this.#core\n .getCompactions(this.sessionId)\n .filter(\n (compaction) =>\n historyIds.has(`${COMPACTION_PREFIX}${compaction.id}`) ||\n (historyIds.has(compaction.fromMessageId) &&\n historyIds.has(compaction.toMessageId))\n );\n const fromId =\n existing.length > 0 ? existing[0].fromMessageId : result.fromMessageId;\n\n this.#core.addCompaction(\n this.sessionId,\n result.summary,\n fromId,\n result.toMessageId\n );\n await this.#core.notify({ type: \"compact\", sessionId: this.sessionId });\n return { ...result, fromMessageId: fromId };\n }\n\n // ── Internal ─────────────────────────────────────────────────────────────\n\n /**\n * The shared write pipeline: sanitize provider metadata and strip reserved\n * metadata on client-source input. Content is never truncated and never\n * too large: a message that exceeds the row budget is split across\n * continuation rows by the durable write.\n */\n #prepare(\n message: SessionMessage,\n source: \"client\" | \"server\" | undefined\n ): { message: SessionMessage; tokenEstimate: number } {\n let prepared = sanitizeMessage(message);\n if (source === \"client\") {\n prepared = this.#core.stripReservedMetadata(prepared);\n }\n return {\n message: prepared,\n tokenEstimate: this.#core.estimateRowTokens(prepared)\n };\n }\n\n /** Gate on the derived estimate, then compact. Failures are non-fatal. */\n async #maybeAutoCompact(): Promise<void> {\n const threshold = this.#tokenThreshold;\n if (threshold == null || !this.#compactionFn) return;\n if (this.#core.tokenEstimate(this.sessionId) <= threshold) return;\n try {\n await this.compact();\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n console.warn(`[Sessions] auto-compaction failed: ${detail}`);\n this.#core.io.emit(\"session:error\", {\n sessionId: this.sessionId,\n error: detail\n });\n }\n }\n}\n","/**\n * Durable conversation history for Lifecycle Objects. `Sessions` owns the\n * `cf_agents_session_*` tables: tree-structured messages (branch\n * regeneration, latest-leaf paths), compaction overlays, and full-text\n * search whose index is built on first use. A message larger than one\n * SQLite row is split across continuation rows and reassembled on read, so\n * nothing is ever too large to store.\n *\n * Sessions consumes only the standard capability services — storage and\n * events. It needs no alarm, so it also works on facets (facets have\n * isolated SQLite but no independent alarm slot).\n *\n * @experimental The API surface may change before stabilizing.\n */\n\nimport { LifecycleCapability } from \"../lifecycle/capability\";\nimport { SqlError } from \"../sql-error\";\nimport { SessionsCore } from \"./core\";\nimport { Session } from \"./handle\";\nimport type { SqlParam } from \"./io\";\nimport type { SessionChangeListener, SessionsOptions } from \"./types\";\n\nconst SESSIONS_SCHEMA_VERSION_KEY = \"cf_agents:sessions_schema_version\";\nconst CURRENT_SESSIONS_SCHEMA_VERSION = 1;\n\n/**\n * Durable conversation history for a Lifecycle Object.\n *\n * `session()` returns a per-session handle for reads (streamed, byte\n * budgeted), writes (sanitized, media offloaded), branch navigation, and\n * compaction overlays. `subscribe()` is the change feed a cache-owning host\n * mirrors.\n *\n * @experimental The API surface may change before stabilizing.\n */\nexport class Sessions extends LifecycleCapability {\n readonly #core: SessionsCore;\n readonly #handles = new Map<string, Session>();\n\n constructor(options: SessionsOptions = {}) {\n super(\"sessions\");\n // Every service is reached lazily, so a handle or a subscription may be\n // taken before `Lifecycle.use()` installs the capability.\n const exec = (query: string, params: SqlParam[]) => {\n try {\n return this.lifecycle.storage.sql.exec(query, ...params);\n } catch (cause) {\n throw new SqlError(query, cause);\n }\n };\n this.#core = new SessionsCore(options, {\n // SAFETY: Sessions queries select from its own schema; T describes the\n // projected columns of the accompanying query text.\n sql: <T>(query: string, params: SqlParam[]) =>\n [...exec(query, params)] as T[],\n sqlWrite: (query, params) => exec(query, params).rowsWritten,\n transaction: (fn) => this.lifecycle.storage.transactionSync(fn),\n emit: (type, payload) => this.lifecycle.events.emit(type, payload)\n });\n }\n\n // ── Lifecycle capability hooks ───────────────────────────────────────────\n\n /** Migrate session storage during Lifecycle startup. */\n async onStart(): Promise<void> {\n const storage = this.lifecycle.storage;\n const version =\n (await storage.get<number>(SESSIONS_SCHEMA_VERSION_KEY)) ?? 0;\n this.#core.ensureTables();\n if (version < CURRENT_SESSIONS_SCHEMA_VERSION) {\n // Stamp the version only when every legacy source was fully lifted. A\n // partial migration that recorded success would never be retried, and\n // the rows it left behind would stay unreachable through the new API\n // forever. The lift is idempotent, so retrying costs reads and nothing\n // else.\n if (this.#core.migrateLegacy()) {\n await storage.put(\n SESSIONS_SCHEMA_VERSION_KEY,\n CURRENT_SESSIONS_SCHEMA_VERSION\n );\n }\n }\n }\n\n // ── Surface ──────────────────────────────────────────────────────────────\n\n /**\n * The handle for one session. The default (empty) id is the primary path:\n * in the one-object-per-conversation model a Durable Object holds exactly\n * one session. Handles are cached, so per-session configuration (the\n * compaction trigger) survives repeated calls.\n */\n session(sessionId = \"\"): Session {\n const existing = this.#handles.get(sessionId);\n if (existing) return existing;\n const handle = new Session(sessionId, this.#core, () =>\n this.lifecycle.ready()\n );\n this.#handles.set(sessionId, handle);\n return handle;\n }\n\n /**\n * Subscribe to the change feed: ordered dispatch after every durable\n * write, with the stored message. The host cache mirror lives here;\n * telemetry additionally flows through capability events.\n */\n subscribe(listener: SessionChangeListener): () => void {\n return this.#core.subscribe(listener);\n }\n}\n"],"mappings":"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"}
@@ -190,6 +190,78 @@ declare function runner(
190
190
  options: WorkerSkillScriptRunnerOptions
191
191
  ): SkillScriptRunner;
192
192
  //#endregion
193
+ //#region src/skills/workspace.d.ts
194
+ /**
195
+ * Computer-style Workspace shape used for Agent Skills projection.
196
+ * `@cloudflare/computer.Workspace` satisfies this through `workspace.fs`.
197
+ */
198
+ interface ComputerSkillWorkspace {
199
+ readonly fs: {
200
+ stat(path: string): Promise<unknown>;
201
+ readFile(
202
+ path: string,
203
+ options?: {
204
+ byteOffset?: number;
205
+ byteLength?: number;
206
+ }
207
+ ): Promise<ReadableStream<Uint8Array>>;
208
+ readFile(path: string, encoding: "utf8"): Promise<string>;
209
+ writeFile(path: string, content: string | Uint8Array): Promise<void>;
210
+ mkdir(
211
+ path: string,
212
+ options?: {
213
+ recursive?: boolean;
214
+ }
215
+ ): Promise<void>;
216
+ };
217
+ }
218
+ /**
219
+ * Legacy direct filesystem shape used for Agent Skills projection.
220
+ * `@cloudflare/shell.Workspace` and Think `WorkspaceLike` satisfy this shape.
221
+ */
222
+ interface LegacySkillWorkspace {
223
+ stat(path: string): Promise<unknown | null>;
224
+ readFile(path: string): Promise<string | null>;
225
+ readFileBytes(path: string): Promise<Uint8Array | null>;
226
+ writeFile(path: string, content: string): Promise<void>;
227
+ writeFileBytes(
228
+ path: string,
229
+ content: Uint8Array | ArrayBuffer,
230
+ mediaType?: string
231
+ ): Promise<void>;
232
+ mkdir(
233
+ path: string,
234
+ options?: {
235
+ recursive?: boolean;
236
+ }
237
+ ): Promise<void>;
238
+ }
239
+ /** Workspace accepted by `SkillRegistry.seedWorkspace()`. */
240
+ type SkillProjectionWorkspace = ComputerSkillWorkspace | LegacySkillWorkspace;
241
+ /** Options for projecting Agent Skills into a Workspace. */
242
+ interface SkillWorkspaceSeedOptions {
243
+ /**
244
+ * Destination directory. Defaults to `/workspace/.agents/skills` for a
245
+ * Computer Workspace and `/.agents/skills` for a legacy Shell Workspace.
246
+ */
247
+ readonly root?: string;
248
+ /** Existing-file policy. Default `"preserve"` keeps Workspace edits. */
249
+ readonly onConflict?: "preserve" | "replace";
250
+ }
251
+ /** Summary returned after projecting Agent Skills into a Workspace. */
252
+ interface SkillWorkspaceSeedResult {
253
+ /** Files written from source content. */
254
+ readonly written: number;
255
+ /** Existing files preserved without a write. */
256
+ readonly preserved: number;
257
+ /** Files skipped because their source content could not be read or written. */
258
+ readonly skipped: number;
259
+ /** Non-fatal per-file diagnostics. */
260
+ readonly warnings: readonly string[];
261
+ /** Absolute Workspace directory containing the projected skills. */
262
+ readonly root: string;
263
+ }
264
+ //#endregion
193
265
  //#region src/skills/registry.d.ts
194
266
  declare class SkillRegistry {
195
267
  readonly contextLabel = "think_skills";
@@ -204,10 +276,32 @@ declare class SkillRegistry {
204
276
  private descriptors;
205
277
  private sourceBySkill;
206
278
  private loaded;
279
+ private workspaceFiles;
280
+ private workspaceSkillNames;
281
+ private workspaceResources;
207
282
  constructor(sources: SkillSource[], scriptRunner?: SkillScriptRunner | null);
208
283
  get fingerprint(): string;
209
284
  load(): Promise<void>;
210
285
  refresh(): Promise<void>;
286
+ /**
287
+ * Seed resolved Agent Skills into Computer or legacy Shell storage.
288
+ * Existing files are preserved by default, and become authoritative for
289
+ * activation, resource reads, and script execution.
290
+ */
291
+ seedWorkspace(
292
+ workspace: SkillProjectionWorkspace,
293
+ options?: SkillWorkspaceSeedOptions
294
+ ): Promise<SkillWorkspaceSeedResult>;
295
+ /**
296
+ * Route skill reads through an already-seeded Workspace without probing or
297
+ * rewriting its files. Hosts use this when the durable source fingerprint
298
+ * matches the recorded projection fingerprint.
299
+ */
300
+ useWorkspace(
301
+ workspace: SkillProjectionWorkspace,
302
+ options?: SkillWorkspaceSeedOptions
303
+ ): Promise<void>;
304
+ private writeSeedFile;
211
305
  snapshot(): Promise<SkillRegistrySnapshot>;
212
306
  systemPrompt(): Promise<string | null>;
213
307
  loadSkill(name: string): Promise<SkillContent | null>;
@@ -218,12 +312,15 @@ declare class SkillRegistry {
218
312
  }
219
313
  //#endregion
220
314
  export {
315
+ type ComputerSkillWorkspace,
316
+ type LegacySkillWorkspace,
221
317
  type R2SkillSourceOptions,
222
318
  type SkillContent,
223
319
  type SkillDescriptor,
224
320
  type SkillManifest,
225
321
  type SkillManifestEntry,
226
322
  type SkillManifestResource,
323
+ type SkillProjectionWorkspace,
227
324
  SkillRegistry,
228
325
  type SkillRegistrySnapshot,
229
326
  type SkillResource,
@@ -234,6 +331,8 @@ export {
234
331
  type SkillScriptRunner,
235
332
  type SkillSource,
236
333
  type SkillWorkspace,
334
+ type SkillWorkspaceSeedOptions,
335
+ type SkillWorkspaceSeedResult,
237
336
  type WorkerSkillScriptRunnerOptions,
238
337
  fromManifest,
239
338
  parseSkillFrontmatter,