@theokit/sdk 5.9.1 → 5.10.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 (190) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/dist/{agent-enXXu447.d.ts → agent-CmKwSyyD.d.ts} +27 -0
  3. package/dist/{agent-5M7WLUWH.cjs → agent-TLRURB7W.cjs} +18 -16
  4. package/dist/{agent-5M7WLUWH.cjs.map → agent-TLRURB7W.cjs.map} +1 -1
  5. package/dist/{agent-Dh2YHSLj.d.cts → agent-XMkGzff_.d.cts} +27 -0
  6. package/dist/{agent-R5RERYFU.js → agent-XTLAVVYC.js} +17 -15
  7. package/dist/{agent-R5RERYFU.js.map → agent-XTLAVVYC.js.map} +1 -1
  8. package/dist/agent-session-store-ODD3IZCW.js +6 -0
  9. package/dist/{agent-session-store-AFML3AXC.js.map → agent-session-store-ODD3IZCW.js.map} +1 -1
  10. package/dist/agent-session-store-SVEJHSUA.cjs +23 -0
  11. package/dist/{agent-session-store-MTSIY3MH.cjs.map → agent-session-store-SVEJHSUA.cjs.map} +1 -1
  12. package/dist/auth/index.cjs +8 -7
  13. package/dist/auth/index.cjs.map +1 -1
  14. package/dist/auth/index.js +3 -2
  15. package/dist/auth/index.js.map +1 -1
  16. package/dist/{chunk-QCIARBLS.js → chunk-523O5UYB.js} +3 -3
  17. package/dist/{chunk-QCIARBLS.js.map → chunk-523O5UYB.js.map} +1 -1
  18. package/dist/chunk-7KIN3L2S.js +103 -0
  19. package/dist/chunk-7KIN3L2S.js.map +1 -0
  20. package/dist/chunk-ASK2OQM5.cjs +84 -0
  21. package/dist/chunk-ASK2OQM5.cjs.map +1 -0
  22. package/dist/{chunk-56BCEM26.cjs → chunk-CVEQ5366.cjs} +105 -91
  23. package/dist/chunk-CVEQ5366.cjs.map +1 -0
  24. package/dist/{chunk-R2C3HXR3.js → chunk-DPY2M74T.js} +3 -3
  25. package/dist/{chunk-R2C3HXR3.js.map → chunk-DPY2M74T.js.map} +1 -1
  26. package/dist/chunk-EHD25JBZ.cjs +10 -0
  27. package/dist/chunk-EHD25JBZ.cjs.map +1 -0
  28. package/dist/{chunk-NOO6GZTZ.js → chunk-EVH3OUMY.js} +13 -12
  29. package/dist/chunk-EVH3OUMY.js.map +1 -0
  30. package/dist/{chunk-5522X223.js → chunk-EXI7EHDN.js} +4 -4
  31. package/dist/{chunk-5522X223.js.map → chunk-EXI7EHDN.js.map} +1 -1
  32. package/dist/{chunk-PSLCVSCS.cjs → chunk-FR6COQXZ.cjs} +6 -6
  33. package/dist/{chunk-PSLCVSCS.cjs.map → chunk-FR6COQXZ.cjs.map} +1 -1
  34. package/dist/{chunk-JOFVLOFY.js → chunk-FTZTZBCU.js} +12 -4
  35. package/dist/chunk-FTZTZBCU.js.map +1 -0
  36. package/dist/{chunk-T5KOSR5Y.cjs → chunk-FYEHYQ7T.cjs} +8 -8
  37. package/dist/{chunk-T5KOSR5Y.cjs.map → chunk-FYEHYQ7T.cjs.map} +1 -1
  38. package/dist/{chunk-HOHR75KF.js → chunk-HI3W37FY.js} +4 -3
  39. package/dist/chunk-HI3W37FY.js.map +1 -0
  40. package/dist/chunk-HU666T3Q.cjs +1891 -0
  41. package/dist/chunk-HU666T3Q.cjs.map +1 -0
  42. package/dist/{chunk-53RDNK3H.js → chunk-I2ORT6QD.js} +3 -3
  43. package/dist/{chunk-53RDNK3H.js.map → chunk-I2ORT6QD.js.map} +1 -1
  44. package/dist/{chunk-UNZOJVVC.js → chunk-IZDDSLL2.js} +5 -4
  45. package/dist/chunk-IZDDSLL2.js.map +1 -0
  46. package/dist/{chunk-TTBIQTO5.cjs → chunk-L3C6CCBG.cjs} +15 -15
  47. package/dist/{chunk-TTBIQTO5.cjs.map → chunk-L3C6CCBG.cjs.map} +1 -1
  48. package/dist/{chunk-B3XXQX6L.cjs → chunk-NQVWSSX2.cjs} +10 -9
  49. package/dist/chunk-NQVWSSX2.cjs.map +1 -0
  50. package/dist/{chunk-LL4QA3DI.cjs → chunk-PCLRIQPX.cjs} +4 -4
  51. package/dist/{chunk-LL4QA3DI.cjs.map → chunk-PCLRIQPX.cjs.map} +1 -1
  52. package/dist/chunk-Q7GYMXVC.js +8 -0
  53. package/dist/chunk-Q7GYMXVC.js.map +1 -0
  54. package/dist/{chunk-ARMFXTY6.cjs → chunk-QY3CIMUV.cjs} +34 -33
  55. package/dist/chunk-QY3CIMUV.cjs.map +1 -0
  56. package/dist/chunk-R5YPDAKD.cjs +108 -0
  57. package/dist/chunk-R5YPDAKD.cjs.map +1 -0
  58. package/dist/chunk-RGQICWFU.js +82 -0
  59. package/dist/chunk-RGQICWFU.js.map +1 -0
  60. package/dist/{chunk-OOM236Z4.js → chunk-RTE2DD4S.js} +32 -18
  61. package/dist/chunk-RTE2DD4S.js.map +1 -0
  62. package/dist/chunk-SGC7LSHF.cjs +18 -0
  63. package/dist/{chunk-R3ZQBPA5.cjs.map → chunk-SGC7LSHF.cjs.map} +1 -1
  64. package/dist/{chunk-RA7IE4NK.cjs → chunk-SKDNCTXG.cjs} +4 -3
  65. package/dist/chunk-SKDNCTXG.cjs.map +1 -0
  66. package/dist/chunk-SVCPJRUB.js +1874 -0
  67. package/dist/chunk-SVCPJRUB.js.map +1 -0
  68. package/dist/{chunk-EJROML3Q.js → chunk-TX56N7FR.js} +4 -3
  69. package/dist/chunk-TX56N7FR.js.map +1 -0
  70. package/dist/{chunk-QDQQYRJI.js → chunk-W2ZVTEOD.js} +3 -3
  71. package/dist/{chunk-QDQQYRJI.js.map → chunk-W2ZVTEOD.js.map} +1 -1
  72. package/dist/{chunk-TC4RWP3Y.js → chunk-X3LOJJMO.js} +89 -29
  73. package/dist/chunk-X3LOJJMO.js.map +1 -0
  74. package/dist/{chunk-2WXHA33T.js → chunk-XERM7PDQ.js} +3 -3
  75. package/dist/{chunk-2WXHA33T.js.map → chunk-XERM7PDQ.js.map} +1 -1
  76. package/dist/{chunk-MEUBLCM4.cjs → chunk-YEHQ3SIC.cjs} +4 -3
  77. package/dist/chunk-YEHQ3SIC.cjs.map +1 -0
  78. package/dist/{chunk-YIFFF23U.cjs → chunk-ZAM7BJXJ.cjs} +5 -5
  79. package/dist/{chunk-YIFFF23U.cjs.map → chunk-ZAM7BJXJ.cjs.map} +1 -1
  80. package/dist/{chunk-76PIKR4P.cjs → chunk-ZD66GFH5.cjs} +91 -29
  81. package/dist/chunk-ZD66GFH5.cjs.map +1 -0
  82. package/dist/{chunk-GZV6AVRL.cjs → chunk-ZWACQM5U.cjs} +11 -3
  83. package/dist/chunk-ZWACQM5U.cjs.map +1 -0
  84. package/dist/{compact-session-RBPLUPCK.cjs → compact-session-4QHBBGEY.cjs} +16 -15
  85. package/dist/{compact-session-RBPLUPCK.cjs.map → compact-session-4QHBBGEY.cjs.map} +1 -1
  86. package/dist/{compact-session-2N3ZYJTW.js → compact-session-PVAWTKKL.js} +7 -6
  87. package/dist/{compact-session-2N3ZYJTW.js.map → compact-session-PVAWTKKL.js.map} +1 -1
  88. package/dist/{cron-DTuaPMVI.d.ts → cron-BPccQCO8.d.ts} +1 -1
  89. package/dist/{cron-QTgcwcZt.d.cts → cron-Blj6wMYg.d.cts} +1 -1
  90. package/dist/cron.cjs +17 -15
  91. package/dist/cron.d.cts +2 -2
  92. package/dist/cron.d.ts +2 -2
  93. package/dist/cron.js +16 -14
  94. package/dist/eval.cjs +20 -18
  95. package/dist/eval.cjs.map +1 -1
  96. package/dist/eval.js +16 -14
  97. package/dist/eval.js.map +1 -1
  98. package/dist/fs-session-store-3VI4CPTX.js +11 -0
  99. package/dist/{fs-session-store-STZZFXH2.js.map → fs-session-store-3VI4CPTX.js.map} +1 -1
  100. package/dist/fs-session-store-IQ6PEHYF.cjs +20 -0
  101. package/dist/{fs-session-store-JHBJFKVV.cjs.map → fs-session-store-IQ6PEHYF.cjs.map} +1 -1
  102. package/dist/{index-manager-ONQJSWX5.js → index-manager-JVYIVTZG.js} +6 -5
  103. package/dist/{index-manager-ONQJSWX5.js.map → index-manager-JVYIVTZG.js.map} +1 -1
  104. package/dist/{index-manager-ZGJN25FD.cjs → index-manager-YGXRFJMZ.cjs} +7 -6
  105. package/dist/{index-manager-ZGJN25FD.cjs.map → index-manager-YGXRFJMZ.cjs.map} +1 -1
  106. package/dist/index.cjs +68 -60
  107. package/dist/index.cjs.map +1 -1
  108. package/dist/index.d.cts +42 -6
  109. package/dist/index.d.ts +42 -6
  110. package/dist/index.js +31 -23
  111. package/dist/index.js.map +1 -1
  112. package/dist/{inject-session-YNNOE3O6.js → inject-session-O24XGRR6.js} +5 -5
  113. package/dist/{inject-session-YNNOE3O6.js.map → inject-session-O24XGRR6.js.map} +1 -1
  114. package/dist/{inject-session-3FPXJDJP.cjs → inject-session-OMLFL2V3.cjs} +7 -7
  115. package/dist/{inject-session-3FPXJDJP.cjs.map → inject-session-OMLFL2V3.cjs.map} +1 -1
  116. package/dist/internal/memory/adapters/index.cjs +3 -2
  117. package/dist/internal/memory/adapters/index.js +2 -1
  118. package/dist/internal/memory/dreaming/phases.d.ts +2 -0
  119. package/dist/internal/memory/dreaming/run.d.ts +5 -0
  120. package/dist/internal/memory/sdk-memory-peer-loader.d.ts +2 -0
  121. package/dist/internal/memory/storage/fact-ceiling.d.cts +19 -0
  122. package/dist/internal/memory/storage/fact-ceiling.d.ts +19 -0
  123. package/dist/internal/memory/storage/index.cjs +41 -32
  124. package/dist/internal/memory/storage/index.d.cts +1 -0
  125. package/dist/internal/memory/storage/index.d.ts +1 -0
  126. package/dist/internal/memory/storage/index.js +4 -3
  127. package/dist/internal/memory/storage/markdown-store.d.cts +3 -1
  128. package/dist/internal/memory/storage/markdown-store.d.ts +3 -1
  129. package/dist/internal/memory/types.d.ts +6 -0
  130. package/dist/internal/persistence/index.cjs +7 -6
  131. package/dist/internal/persistence/index.cjs.map +1 -1
  132. package/dist/internal/persistence/index.js +2 -1
  133. package/dist/internal/persistence/index.js.map +1 -1
  134. package/dist/internal/persistence/persistence-schema.d.cts +1 -1
  135. package/dist/internal/persistence/persistence-schema.d.ts +1 -1
  136. package/dist/internal/providers/catalog-loader.d.ts +30 -3
  137. package/dist/internal/runtime/registry/agent-registry-store.d.ts +2 -0
  138. package/dist/internal/runtime-fetch.d.ts +1 -0
  139. package/dist/models.cjs +12 -11
  140. package/dist/models.cjs.map +1 -1
  141. package/dist/models.js +4 -3
  142. package/dist/models.js.map +1 -1
  143. package/dist/persistence.cjs +14 -13
  144. package/dist/persistence.cjs.map +1 -1
  145. package/dist/persistence.js +4 -3
  146. package/dist/persistence.js.map +1 -1
  147. package/dist/providers.cjs +6 -5
  148. package/dist/providers.js +4 -3
  149. package/dist/{session-transcript-VTBTCGQX.js → session-transcript-AR7IBICT.js} +3 -3
  150. package/dist/{session-transcript-VTBTCGQX.js.map → session-transcript-AR7IBICT.js.map} +1 -1
  151. package/dist/{session-transcript-LGHSNVY5.cjs → session-transcript-RACG7BK2.cjs} +16 -16
  152. package/dist/{session-transcript-LGHSNVY5.cjs.map → session-transcript-RACG7BK2.cjs.map} +1 -1
  153. package/dist/subagents-loader.d.cts +1 -1
  154. package/dist/subagents-loader.d.ts +1 -1
  155. package/dist/subscription/index.cjs +2 -1
  156. package/dist/subscription/index.cjs.map +1 -1
  157. package/dist/subscription/index.js +2 -1
  158. package/dist/subscription/index.js.map +1 -1
  159. package/dist/types/agent.d.ts +27 -0
  160. package/docs/error-codes.md +40 -38
  161. package/docs/harness-capability-map.md +6 -1
  162. package/package.json +2 -2
  163. package/dist/agent-session-store-AFML3AXC.js +0 -6
  164. package/dist/agent-session-store-MTSIY3MH.cjs +0 -23
  165. package/dist/chunk-56BCEM26.cjs.map +0 -1
  166. package/dist/chunk-76PIKR4P.cjs.map +0 -1
  167. package/dist/chunk-ARMFXTY6.cjs.map +0 -1
  168. package/dist/chunk-B3XXQX6L.cjs.map +0 -1
  169. package/dist/chunk-BKMDRBJ7.js +0 -180
  170. package/dist/chunk-BKMDRBJ7.js.map +0 -1
  171. package/dist/chunk-EJROML3Q.js.map +0 -1
  172. package/dist/chunk-GZV6AVRL.cjs.map +0 -1
  173. package/dist/chunk-HOHR75KF.js.map +0 -1
  174. package/dist/chunk-JOFVLOFY.js.map +0 -1
  175. package/dist/chunk-MEUBLCM4.cjs.map +0 -1
  176. package/dist/chunk-MFZODA5Y.js +0 -583
  177. package/dist/chunk-MFZODA5Y.js.map +0 -1
  178. package/dist/chunk-NOO6GZTZ.js.map +0 -1
  179. package/dist/chunk-OF2CLCTH.cjs +0 -186
  180. package/dist/chunk-OF2CLCTH.cjs.map +0 -1
  181. package/dist/chunk-OOM236Z4.js.map +0 -1
  182. package/dist/chunk-R3ZQBPA5.cjs +0 -18
  183. package/dist/chunk-RA7IE4NK.cjs.map +0 -1
  184. package/dist/chunk-TC4RWP3Y.js.map +0 -1
  185. package/dist/chunk-UNZOJVVC.js.map +0 -1
  186. package/dist/chunk-YRJA2POB.cjs +0 -601
  187. package/dist/chunk-YRJA2POB.cjs.map +0 -1
  188. package/dist/fs-session-store-JHBJFKVV.cjs +0 -19
  189. package/dist/fs-session-store-STZZFXH2.js +0 -10
  190. package/dist/provider-catalog.json +0 -1295
package/CHANGELOG.md CHANGED
@@ -1,5 +1,124 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#724](https://github.com/usetheokit/theokit-sdk/pull/724) [`bc2f665`](https://github.com/usetheokit/theokit-sdk/commit/bc2f665d4c5e03dda26858004ac60bd1f7d3f3bb) Thanks [@usetheodev](https://github.com/usetheodev)! - Memory can now be given a fact ceiling. Once the store is full, a new fact is refused with a
8
+ typed error. Before this, nothing stopped the store growing, and the one cap that did exist, 500
9
+ facts per dreaming sweep, left the rest out of clustering while the result still said
10
+ `status: "ok"`.
11
+
12
+ Set `memory.maxFacts` on an agent to declare a ceiling. A new fact at or over it is refused with a
13
+ `ConfigurationError` whose code is `memory_ceiling_reached`, and nothing is written. The message
14
+ gives the directory, how many facts it holds, the ceiling and what to consolidate. Recording a fact
15
+ that is already stored counts as a second observation, not a new fact, so it is still admitted. A
16
+ value that is not a positive integer is refused with `invalid_memory_ceiling` by `Agent.create`
17
+ and `Agent.resume`, before the agent exists, and again by the writer before anything touches
18
+ disk. A prompt-driven memory write that hits the ceiling reports it on one diagnostic line and
19
+ stores nothing. With no ceiling declared, nothing changes and no extra read happens.
20
+
21
+ The ceiling holds across processes. Every SDK process that writes one memory directory with a
22
+ ceiling takes a lock (`<directory>.lock`, beside the directory) around the count and the write, so
23
+ two processes one fact below the ceiling no longer both get in. The lock needs the optional
24
+ `proper-lockfile` peer. Without it the ceiling holds within one process only, and the SDK says so
25
+ once on its diagnostics channel. A writer that declares no ceiling, such as the Claude Code CLI,
26
+ takes no lock and can still add facts past it.
27
+
28
+ A resumed agent keeps its ceiling and its `memory.directory`. The directory is now saved with the
29
+ agent, and a `memory` object passed to `Agent.resume` replaces only the fields it sets (a field
30
+ set to `undefined` keeps its persisted value). Before
31
+ this, `Agent.resume(id, { memory: { enabled: true } })` came back with no ceiling, and any resumed
32
+ agent wrote to the default store instead of the directory it was created with.
33
+
34
+ `createInMemoryMarkdownProvider({ maxFacts })` in `@theokit/sdk-memory` applies the same ceiling
35
+ to the adapter's `write` and to the `memory_remember` tool. Each handle counts its own facts.
36
+
37
+ `DreamingSweepResult` gains `factsLeftOut`, the number of deduplicated facts past the 500-fact
38
+ cap that a sweep did not cluster. It is reported on both dispatch paths, the `@theokit/sdk-memory`
39
+ peer and the SDK's own fallback. It is 0 on a skipped sweep and absent on a failed one. It is also
40
+ absent when an older `@theokit/sdk-memory` runs the sweep, because that version cannot count it,
41
+ so read absence as unknown, not as 0. `ClusterResult`, which `@theokit/sdk-memory` exports, now
42
+ requires `factsLeftOut` too. Code that builds its own `ClusterResult`, such as a mock of
43
+ `remPhase`, has to add it.
44
+
45
+ `@theokit/sdk-memory` now needs `@theokit/sdk` `^5.10.0`, the first release that exports the
46
+ ceiling helpers it imports.
47
+
48
+ ## 5.9.2
49
+
50
+ ### Patch Changes
51
+
52
+ - [#713](https://github.com/usetheokit/theokit-sdk/pull/713) [`d1a0466`](https://github.com/usetheokit/theokit-sdk/commit/d1a0466257cebadbd4aaefee35e316091d641e22) Thanks [@usetheodev](https://github.com/usetheodev)! - A session transcript read on a runtime with no filesystem continues with an empty transcript instead
53
+ of failing the turn.
54
+
55
+ `readTranscript` treated only `ENOENT` as "there is no transcript yet" and rethrew everything else.
56
+ That is right for a real filesystem, where any other code is a genuine fault. On Workers there is no
57
+ filesystem at all, and `unenv`'s `createNotImplementedError` throws a plain `Error` carrying **no**
58
+ `code` — so the guard missed it and a turn died on a read whose only purpose is to recover history
59
+ that does not exist.
60
+
61
+ Measured on a deployed worker (theokit#705):
62
+
63
+ agent turn failed (SDK_ERROR): [unenv] fs.readFile is not implemented yet!
64
+
65
+ A codeless error inside a `try` that wraps only the `readFile` is a precise discriminator rather than
66
+ a catch-all: every real filesystem failure carries a code (`EACCES`, `EISDIR`, `EMFILE`), so nothing
67
+ that a filesystem can produce is swallowed by it. The absence of a code means the call never reached
68
+ a filesystem, which is the one case where an empty transcript is the correct answer. It is reported
69
+ through `diag`, so an operator who turns diagnostics on sees why the history is empty.
70
+
71
+ - [#713](https://github.com/usetheokit/theokit-sdk/pull/713) [`d1a0466`](https://github.com/usetheokit/theokit-sdk/commit/d1a0466257cebadbd4aaefee35e316091d641e22) Thanks [@usetheodev](https://github.com/usetheodev)! - `fetch` is bound to its receiver before it is stored or passed, so a provider transport works on
72
+ Cloudflare Workers.
73
+
74
+ Thirteen call sites across eleven files wrote `options.fetch ?? fetch`, which detaches the global
75
+ from its receiver. Node's undici tolerates that. workerd does not — calling the detached reference
76
+ as a property of another object, or as a bare function in a module where `this` is `undefined`,
77
+ throws `Illegal invocation: function called with incorrect this reference`.
78
+
79
+ Measured on a deployed worker while driving a real turn (theokit#705), and reached only after two
80
+ filesystem defects in this package were cleared, so it was the fourth layer of one deploy:
81
+
82
+ agent turn failed (AGENT_ERROR): openrouter transport failure on /v1/chat/completions:
83
+ Illegal invocation: function called with incorrect `this` reference.
84
+
85
+ The turn that failed went through `internal/llm/openai.ts`. Fixing only that one would have left
86
+ twelve identical landmines — every other provider, the memory adapter, the credential resolver, the
87
+ cloud-run client — each of them a deploy that fails on its first request. `internal/runtime-fetch.ts`
88
+ is now the one place that knows the rule: `boundFetch(override?)` returns an injected fetch
89
+ untouched, so a test double still reaches the transport as itself, and otherwise returns
90
+ `globalThis.fetch.bind(globalThis)`.
91
+
92
+ Binding where the reference is taken, rather than a `(0, f)(…)` at each invocation, fixes every call
93
+ that reference will ever receive and gives the sweep in `tests/runtime/fetch-is-bound.test.ts`
94
+ something it can assert: no source file under `src/` takes a detached reference to the global.
95
+
96
+ - [#713](https://github.com/usetheokit/theokit-sdk/pull/713) [`4f2c22c`](https://github.com/usetheokit/theokit-sdk/commit/4f2c22c9e86cdc07f0a6ccf5b0b00ef97587b709) Thanks [@usetheodev](https://github.com/usetheodev)! - The provider catalog is embedded at build time instead of read from disk, so the SDK runs on a
97
+ runtime with no filesystem.
98
+
99
+ `loadProviderCatalog` read `provider-catalog.json` with `readFileSync`, from a directory computed as
100
+ `dirname(fileURLToPath(import.meta.url))` at module scope. Both halves break on Cloudflare Workers,
101
+ Vercel Functions and any bundled deployment — and the catalog is only model METADATA, so an
102
+ environment that could not read it could not run a turn at all.
103
+
104
+ Measured end to end while deploying a TheoKit app (theokit#705). Cloudflare executes the top-level
105
+ module during validation, so the upload was refused before any request existed with
106
+ `The "path" argument must be of type string … Received undefined` (code 10021). With
107
+ `import.meta.url` substituted by the consumer's bundler the module initialised and the turn failed
108
+ with `[unenv] fs.readFileSync is not implemented yet!`; with a newer `compatibility_date` that became
109
+ the honest `ENOENT: readAll '/provider-catalog.json'`. The same `ENOENT` reached a deployed Vercel
110
+ function, whose bundle carries the JS and not the JSON.
111
+
112
+ No consumer's bundler could fix it from outside. Probed from inside a deployed worker, workerd's
113
+ whole virtual filesystem is `/bundle/worker.js`, an empty `/tmp` and `/dev` — there is nowhere to
114
+ place this package's private data file such that `join(dirname(<a file URL>), …)` finds it, short of
115
+ hardcoding our internal layout into their build.
116
+
117
+ The import uses `with { type: "json" }`, following `internal/budget/pricing-registry.ts`, which has
118
+ imported a sibling JSON that way in this package for as long as it has existed. Bundlers inline it,
119
+ so the emitted module carries the data and reaches for nothing. The build no longer copies the JSON
120
+ into `dist`, because nothing reads it there.
121
+
3
122
  ## 5.9.1
4
123
 
5
124
  ### Patch Changes
@@ -1437,6 +1437,33 @@ interface MemorySettings {
1437
1437
  * share the writes.
1438
1438
  */
1439
1439
  directory?: string;
1440
+ /**
1441
+ * The most facts this agent's memory may hold. Default: no ceiling.
1442
+ *
1443
+ * The ceiling counts the write directory only: the per-memory fact files and any legacy
1444
+ * `## Facts` bullets in that directory's `MEMORY.md`. A dreaming sweep's `factsBefore` counts
1445
+ * every root it reads, so the two numbers can differ.
1446
+ *
1447
+ * At the ceiling a new fact is refused with a `ConfigurationError` whose code is
1448
+ * `memory_ceiling_reached` and whose message names the directory, the count and what to
1449
+ * consolidate; nothing is written. Recording a fact that is already stored is a second
1450
+ * observation, not a new fact, and is still admitted. A value that is not a positive integer is
1451
+ * refused with `invalid_memory_ceiling` by `Agent.create` and `Agent.resume`, before the agent
1452
+ * exists, whether or not memory is enabled, and again by the writer before anything touches disk.
1453
+ *
1454
+ * The count and the write hold a lock that every SDK process writing this directory with a
1455
+ * ceiling takes, so several processes on one directory cannot together pass it. That lock is
1456
+ * the companion `<directory>.lock` beside the directory and needs the optional `proper-lockfile`
1457
+ * peer; without it the ceiling holds within one process only, and the SDK says so once on its
1458
+ * diagnostics channel. A writer that declares no ceiling, such as the Claude Code CLI, does not
1459
+ * take the lock and can still add facts past it. When the lock stays busy past its retries the
1460
+ * write is refused with `proper-lockfile`'s `ELOCKED` error, and nothing is written.
1461
+ *
1462
+ * A resumed agent keeps its ceiling and its `directory`: a `memory` object passed to
1463
+ * `Agent.resume` replaces only the fields it sets, and a field set to `undefined` keeps its
1464
+ * persisted value.
1465
+ */
1466
+ maxFacts?: number;
1440
1467
  /**
1441
1468
  * Whether the SDK auto-injects recalled facts as a `<memory>` block in the
1442
1469
  * LLM system prompt. Default `true`.
@@ -1,23 +1,23 @@
1
1
  'use strict';
2
2
 
3
- var chunk56BCEM26_cjs = require('./chunk-56BCEM26.cjs');
3
+ var chunkCVEQ5366_cjs = require('./chunk-CVEQ5366.cjs');
4
4
  require('./chunk-J6GXS5A2.cjs');
5
- require('./chunk-PSLCVSCS.cjs');
5
+ require('./chunk-FR6COQXZ.cjs');
6
6
  require('./chunk-BUUUWQMB.cjs');
7
- require('./chunk-TTBIQTO5.cjs');
7
+ require('./chunk-L3C6CCBG.cjs');
8
8
  require('./chunk-KIKKTW5O.cjs');
9
9
  require('./chunk-BV2MWEMV.cjs');
10
- require('./chunk-MEUBLCM4.cjs');
10
+ require('./chunk-YEHQ3SIC.cjs');
11
11
  require('./chunk-GHX4P3V2.cjs');
12
12
  require('./chunk-TMKTSYDS.cjs');
13
- require('./chunk-76PIKR4P.cjs');
14
- require('./chunk-B3XXQX6L.cjs');
15
- require('./chunk-ARMFXTY6.cjs');
13
+ require('./chunk-ZD66GFH5.cjs');
14
+ require('./chunk-NQVWSSX2.cjs');
15
+ require('./chunk-QY3CIMUV.cjs');
16
16
  require('./chunk-CQYBJOB2.cjs');
17
17
  require('./chunk-YJNYDJ2T.cjs');
18
18
  require('./chunk-WPO3VCY4.cjs');
19
19
  require('./chunk-QOGNZMHD.cjs');
20
- require('./chunk-YIFFF23U.cjs');
20
+ require('./chunk-ZAM7BJXJ.cjs');
21
21
  require('./chunk-2ZPEDVLM.cjs');
22
22
  require('./chunk-XKZFIP7Z.cjs');
23
23
  require('./chunk-N6OEX5TN.cjs');
@@ -37,10 +37,11 @@ require('./chunk-SA6K24NB.cjs');
37
37
  require('./chunk-IIYJMXYW.cjs');
38
38
  require('./chunk-NJSIAJEK.cjs');
39
39
  require('./chunk-NKIQTLFA.cjs');
40
- require('./chunk-GZV6AVRL.cjs');
40
+ require('./chunk-ZWACQM5U.cjs');
41
41
  require('./chunk-A4RAL2ER.cjs');
42
42
  require('./chunk-P22BZE7G.cjs');
43
- require('./chunk-OF2CLCTH.cjs');
43
+ require('./chunk-R5YPDAKD.cjs');
44
+ require('./chunk-ASK2OQM5.cjs');
44
45
  require('./chunk-ZF2LDKQQ.cjs');
45
46
  require('./chunk-GHHIJEFX.cjs');
46
47
  require('./chunk-JLRLCBJ4.cjs');
@@ -49,12 +50,13 @@ require('./chunk-TY56BKSK.cjs');
49
50
  require('./chunk-MPR2ISL2.cjs');
50
51
  require('./chunk-J5623RIV.cjs');
51
52
  require('./chunk-3EE6LVWT.cjs');
52
- require('./chunk-R3ZQBPA5.cjs');
53
- require('./chunk-YRJA2POB.cjs');
53
+ require('./chunk-SGC7LSHF.cjs');
54
+ require('./chunk-HU666T3Q.cjs');
54
55
  require('./chunk-NVXXWAU7.cjs');
55
- require('./chunk-RA7IE4NK.cjs');
56
+ require('./chunk-SKDNCTXG.cjs');
56
57
  require('./chunk-NAGPKWOD.cjs');
57
58
  require('./chunk-OLSEWLK5.cjs');
59
+ require('./chunk-EHD25JBZ.cjs');
58
60
  require('./chunk-UNFBHV46.cjs');
59
61
  require('./chunk-6LHQPOMI.cjs');
60
62
 
@@ -62,7 +64,7 @@ require('./chunk-6LHQPOMI.cjs');
62
64
 
63
65
  Object.defineProperty(exports, "Agent", {
64
66
  enumerable: true,
65
- get: function () { return chunk56BCEM26_cjs.Agent; }
67
+ get: function () { return chunkCVEQ5366_cjs.Agent; }
66
68
  });
67
- //# sourceMappingURL=agent-5M7WLUWH.cjs.map
68
- //# sourceMappingURL=agent-5M7WLUWH.cjs.map
69
+ //# sourceMappingURL=agent-TLRURB7W.cjs.map
70
+ //# sourceMappingURL=agent-TLRURB7W.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"agent-5M7WLUWH.cjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"agent-TLRURB7W.cjs"}
@@ -1437,6 +1437,33 @@ interface MemorySettings {
1437
1437
  * share the writes.
1438
1438
  */
1439
1439
  directory?: string;
1440
+ /**
1441
+ * The most facts this agent's memory may hold. Default: no ceiling.
1442
+ *
1443
+ * The ceiling counts the write directory only: the per-memory fact files and any legacy
1444
+ * `## Facts` bullets in that directory's `MEMORY.md`. A dreaming sweep's `factsBefore` counts
1445
+ * every root it reads, so the two numbers can differ.
1446
+ *
1447
+ * At the ceiling a new fact is refused with a `ConfigurationError` whose code is
1448
+ * `memory_ceiling_reached` and whose message names the directory, the count and what to
1449
+ * consolidate; nothing is written. Recording a fact that is already stored is a second
1450
+ * observation, not a new fact, and is still admitted. A value that is not a positive integer is
1451
+ * refused with `invalid_memory_ceiling` by `Agent.create` and `Agent.resume`, before the agent
1452
+ * exists, whether or not memory is enabled, and again by the writer before anything touches disk.
1453
+ *
1454
+ * The count and the write hold a lock that every SDK process writing this directory with a
1455
+ * ceiling takes, so several processes on one directory cannot together pass it. That lock is
1456
+ * the companion `<directory>.lock` beside the directory and needs the optional `proper-lockfile`
1457
+ * peer; without it the ceiling holds within one process only, and the SDK says so once on its
1458
+ * diagnostics channel. A writer that declares no ceiling, such as the Claude Code CLI, does not
1459
+ * take the lock and can still add facts past it. When the lock stays busy past its retries the
1460
+ * write is refused with `proper-lockfile`'s `ELOCKED` error, and nothing is written.
1461
+ *
1462
+ * A resumed agent keeps its ceiling and its `directory`: a `memory` object passed to
1463
+ * `Agent.resume` replaces only the fields it sets, and a field set to `undefined` keeps its
1464
+ * persisted value.
1465
+ */
1466
+ maxFacts?: number;
1440
1467
  /**
1441
1468
  * Whether the SDK auto-injects recalled facts as a `<memory>` block in the
1442
1469
  * LLM system prompt. Default `true`.
@@ -1,21 +1,21 @@
1
- export { Agent } from './chunk-OOM236Z4.js';
1
+ export { Agent } from './chunk-RTE2DD4S.js';
2
2
  import './chunk-XKMVJ443.js';
3
- import './chunk-5522X223.js';
3
+ import './chunk-EXI7EHDN.js';
4
4
  import './chunk-K2BQQ445.js';
5
- import './chunk-R2C3HXR3.js';
5
+ import './chunk-DPY2M74T.js';
6
6
  import './chunk-ZHWKT2NI.js';
7
7
  import './chunk-2SFBB54R.js';
8
- import './chunk-HOHR75KF.js';
8
+ import './chunk-HI3W37FY.js';
9
9
  import './chunk-H73MEMQB.js';
10
10
  import './chunk-OR6XIWLB.js';
11
- import './chunk-TC4RWP3Y.js';
12
- import './chunk-UNZOJVVC.js';
13
- import './chunk-NOO6GZTZ.js';
11
+ import './chunk-X3LOJJMO.js';
12
+ import './chunk-IZDDSLL2.js';
13
+ import './chunk-EVH3OUMY.js';
14
14
  import './chunk-SUVCHX4L.js';
15
15
  import './chunk-2XC6KDJM.js';
16
16
  import './chunk-TE6XVTOA.js';
17
17
  import './chunk-24MGQDK3.js';
18
- import './chunk-2WXHA33T.js';
18
+ import './chunk-XERM7PDQ.js';
19
19
  import './chunk-HKCKOAIO.js';
20
20
  import './chunk-7QJYKVP6.js';
21
21
  import './chunk-OPAKPEPG.js';
@@ -35,10 +35,11 @@ import './chunk-UKMBRMGT.js';
35
35
  import './chunk-7JAKO3NM.js';
36
36
  import './chunk-ZWFTECOE.js';
37
37
  import './chunk-GTX6FEDP.js';
38
- import './chunk-JOFVLOFY.js';
38
+ import './chunk-FTZTZBCU.js';
39
39
  import './chunk-WE22OXQA.js';
40
40
  import './chunk-XZHJYSPB.js';
41
- import './chunk-BKMDRBJ7.js';
41
+ import './chunk-7KIN3L2S.js';
42
+ import './chunk-RGQICWFU.js';
42
43
  import './chunk-Q5EWJPRY.js';
43
44
  import './chunk-GGRPAQ3B.js';
44
45
  import './chunk-3JHIFQ4I.js';
@@ -47,13 +48,14 @@ import './chunk-X7EUUHXU.js';
47
48
  import './chunk-MQGSOKR3.js';
48
49
  import './chunk-GMRFAX4I.js';
49
50
  import './chunk-R7WIIPUR.js';
50
- import './chunk-53RDNK3H.js';
51
- import './chunk-MFZODA5Y.js';
51
+ import './chunk-I2ORT6QD.js';
52
+ import './chunk-SVCPJRUB.js';
52
53
  import './chunk-AGACYRJ2.js';
53
- import './chunk-EJROML3Q.js';
54
+ import './chunk-TX56N7FR.js';
54
55
  import './chunk-5Q4RTLMP.js';
55
56
  import './chunk-H3CVJSFC.js';
57
+ import './chunk-Q7GYMXVC.js';
56
58
  import './chunk-B5MHRQJZ.js';
57
59
  import './chunk-CZJ6Q7CW.js';
58
- //# sourceMappingURL=agent-R5RERYFU.js.map
59
- //# sourceMappingURL=agent-R5RERYFU.js.map
60
+ //# sourceMappingURL=agent-XTLAVVYC.js.map
61
+ //# sourceMappingURL=agent-XTLAVVYC.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"agent-R5RERYFU.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"agent-XTLAVVYC.js"}
@@ -0,0 +1,6 @@
1
+ export { appendCompactBoundaryRecord, persistTurn, readSessionMessages } from './chunk-XERM7PDQ.js';
2
+ import './chunk-FTZTZBCU.js';
3
+ import './chunk-3JHIFQ4I.js';
4
+ import './chunk-CZJ6Q7CW.js';
5
+ //# sourceMappingURL=agent-session-store-ODD3IZCW.js.map
6
+ //# sourceMappingURL=agent-session-store-ODD3IZCW.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"agent-session-store-AFML3AXC.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"agent-session-store-ODD3IZCW.js"}
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ var chunkZAM7BJXJ_cjs = require('./chunk-ZAM7BJXJ.cjs');
4
+ require('./chunk-ZWACQM5U.cjs');
5
+ require('./chunk-JLRLCBJ4.cjs');
6
+ require('./chunk-6LHQPOMI.cjs');
7
+
8
+
9
+
10
+ Object.defineProperty(exports, "appendCompactBoundaryRecord", {
11
+ enumerable: true,
12
+ get: function () { return chunkZAM7BJXJ_cjs.appendCompactBoundaryRecord; }
13
+ });
14
+ Object.defineProperty(exports, "persistTurn", {
15
+ enumerable: true,
16
+ get: function () { return chunkZAM7BJXJ_cjs.persistTurn; }
17
+ });
18
+ Object.defineProperty(exports, "readSessionMessages", {
19
+ enumerable: true,
20
+ get: function () { return chunkZAM7BJXJ_cjs.readSessionMessages; }
21
+ });
22
+ //# sourceMappingURL=agent-session-store-SVEJHSUA.cjs.map
23
+ //# sourceMappingURL=agent-session-store-SVEJHSUA.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"agent-session-store-MTSIY3MH.cjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"agent-session-store-SVEJHSUA.cjs"}
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkFRITRLZQ_cjs = require('../chunk-FRITRLZQ.cjs');
4
- var chunkRA7IE4NK_cjs = require('../chunk-RA7IE4NK.cjs');
4
+ var chunkSKDNCTXG_cjs = require('../chunk-SKDNCTXG.cjs');
5
5
  var chunkNAGPKWOD_cjs = require('../chunk-NAGPKWOD.cjs');
6
6
  var chunkOLSEWLK5_cjs = require('../chunk-OLSEWLK5.cjs');
7
+ require('../chunk-EHD25JBZ.cjs');
7
8
  require('../chunk-UNFBHV46.cjs');
8
9
  require('../chunk-6LHQPOMI.cjs');
9
10
 
@@ -201,7 +202,7 @@ async function openaiDeviceLogin(config, deps, hooks) {
201
202
  "device poll returned no authorization_code"
202
203
  );
203
204
  }
204
- const tokens = await chunkRA7IE4NK_cjs.exchangeCode(
205
+ const tokens = await chunkSKDNCTXG_cjs.exchangeCode(
205
206
  config,
206
207
  { code: data.authorization_code, verifier: data.code_verifier },
207
208
  deps
@@ -249,23 +250,23 @@ Object.defineProperty(exports, "providerFromApiKeyPrefix", {
249
250
  });
250
251
  Object.defineProperty(exports, "ensureFreshCredential", {
251
252
  enumerable: true,
252
- get: function () { return chunkRA7IE4NK_cjs.ensureFreshCredential; }
253
+ get: function () { return chunkSKDNCTXG_cjs.ensureFreshCredential; }
253
254
  });
254
255
  Object.defineProperty(exports, "exchangeCode", {
255
256
  enumerable: true,
256
- get: function () { return chunkRA7IE4NK_cjs.exchangeCode; }
257
+ get: function () { return chunkSKDNCTXG_cjs.exchangeCode; }
257
258
  });
258
259
  Object.defineProperty(exports, "persistOAuthTokens", {
259
260
  enumerable: true,
260
- get: function () { return chunkRA7IE4NK_cjs.persistOAuthTokens; }
261
+ get: function () { return chunkSKDNCTXG_cjs.persistOAuthTokens; }
261
262
  });
262
263
  Object.defineProperty(exports, "refreshOAuthTokens", {
263
264
  enumerable: true,
264
- get: function () { return chunkRA7IE4NK_cjs.refreshOAuthTokens; }
265
+ get: function () { return chunkSKDNCTXG_cjs.refreshOAuthTokens; }
265
266
  });
266
267
  Object.defineProperty(exports, "resolveCredential", {
267
268
  enumerable: true,
268
- get: function () { return chunkRA7IE4NK_cjs.resolveCredential; }
269
+ get: function () { return chunkSKDNCTXG_cjs.resolveCredential; }
269
270
  });
270
271
  Object.defineProperty(exports, "CredentialError", {
271
272
  enumerable: true,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/internal/auth/oauth-device.ts"],"names":["AuthCallbackError","exchangeCode"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * M42 — the SDK OAuth device-authorization flows (RFC 8628 + OpenAI two-step headless). Split out of\n * oauth-engine.ts to respect the 500-line file budget; a sibling of the core engine. Promoted DOWN from\n * agent-builder's hardened `agents/lib/oauth-device.ts` (M37).\n *\n * Implements two device flows against their published specs: the RFC 8628 device-grant poll loop\n * (device_code → authorization_pending / slow_down) and OpenAI's two-step headless flow, including the\n * JWT-claim / account-id extraction used to attribute an account.\n * Design choices: network I/O + clock + sleep are INJECTED (deterministic tests), failures raise the SDK's\n * typed `AuthCallbackError`, and a device-code expiry DEADLINE guards each poll loop.\n *\n * @internal\n */\nimport { AuthCallbackError } from \"../../server/auth/errors.js\";\nimport { exchangeCode } from \"./oauth-engine.js\";\nimport type {\n DeviceCodeGrant,\n DeviceDeps,\n DeviceOAuthConfig,\n OAuthTokens,\n OpenAIDeviceConfig,\n} from \"./types.js\";\n\n// ─── OAuth 2.0 Device Authorization Grant (RFC 8628) — terminal-first / headless login ───\n\n/** Buffer added to every poll interval to avoid hitting the server a hair early. */\nconst POLLING_SAFETY_MARGIN_MS = 3000;\n\n/**\n * Parse a JSON body, or fail with this module's typed error instead of a raw `SyntaxError`.\n *\n * Every failure in this file is supposed to reach the caller as an `AuthCallbackError` carrying a code\n * the CLI can branch on. `res.json()` breaks that contract: a device endpoint that answers with HTML —\n * a captive portal, a corporate proxy's sign-in page, a load balancer's error page — makes it reject\n * with a `SyntaxError` that no `catch` here handles, so it escapes untyped past a caller prepared only\n * for `AuthCallbackError`.\n *\n * The body is quoted, truncated, because \"not JSON\" and \"not JSON, and it looks like a proxy login\n * page\" are different diagnoses for whoever is holding the terminal — and routing them to the provider\n * when the fault is their network is the expensive kind of wrong.\n */\nasync function parseJsonOrFail(res: Response, code: string, what: string): Promise<unknown> {\n const text = await res.text().catch(() => \"\");\n try {\n return JSON.parse(text);\n } catch {\n throw new AuthCallbackError(code, `${what} returned a non-JSON body: ${text.slice(0, 120)}`);\n }\n}\n\n/** Step 1 — request a device code. POSTs `{client_id, scope}` to the device endpoint. */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: RFC 8628 device-code request — cohesive network response parser ported verbatim (ADR D3).\nexport async function requestDeviceCode(\n config: DeviceOAuthConfig,\n deps: Pick<DeviceDeps, \"fetch\">,\n): Promise<DeviceCodeGrant> {\n let res: Response;\n try {\n res = await deps.fetch(config.deviceCodeEndpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\", accept: \"application/json\" },\n body: new URLSearchParams({\n client_id: config.clientId,\n scope: config.scopes.join(\" \"),\n }).toString(),\n });\n } catch (err) {\n throw new AuthCallbackError(\n \"oauth_device_authorization_failed\",\n `device request failed: ${(err as Error).message}`,\n );\n }\n if (!res.ok) {\n throw new AuthCallbackError(\n \"oauth_device_authorization_failed\",\n `device endpoint returned HTTP ${res.status}`,\n );\n }\n const data = (await parseJsonOrFail(\n res,\n \"oauth_device_authorization_failed\",\n \"device endpoint\",\n )) as {\n device_code?: unknown;\n user_code?: unknown;\n verification_uri?: unknown;\n verification_uri_complete?: unknown;\n interval?: unknown;\n expires_in?: unknown;\n };\n if (typeof data.device_code !== \"string\" || typeof data.user_code !== \"string\") {\n throw new AuthCallbackError(\n \"oauth_device_authorization_failed\",\n \"device response missing device_code/user_code\",\n );\n }\n const verification =\n typeof data.verification_uri === \"string\"\n ? data.verification_uri\n : typeof data.verification_uri_complete === \"string\"\n ? data.verification_uri_complete\n : \"\";\n return {\n deviceCode: data.device_code,\n userCode: data.user_code,\n verificationUri: verification,\n interval: typeof data.interval === \"number\" && data.interval > 0 ? data.interval : 5,\n expiresIn: typeof data.expires_in === \"number\" && data.expires_in > 0 ? data.expires_in : 900,\n };\n}\n\n/**\n * Step 2 — poll the token endpoint until the user approves (or the code expires). Handles RFC 8628\n * `authorization_pending` (keep waiting) and `slow_down` (back off), with an expiry deadline so it cannot\n * spin forever.\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: RFC 8628 poll state machine (authorization_pending / slow_down / success / expiry) — a cohesive loop ported verbatim (ADR D3); fragmenting it would obscure the protocol.\nexport async function pollDeviceToken(\n config: DeviceOAuthConfig,\n grant: DeviceCodeGrant,\n deps: DeviceDeps,\n): Promise<OAuthTokens> {\n const deadline = deps.now() + grant.expiresIn * 1000;\n let intervalMs = grant.interval * 1000;\n\n while (deps.now() < deadline) {\n let res: Response;\n try {\n res = await deps.fetch(config.tokenEndpoint, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/x-www-form-urlencoded\",\n accept: \"application/json\",\n },\n body: new URLSearchParams({\n client_id: config.clientId,\n device_code: grant.deviceCode,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n }).toString(),\n });\n } catch (err) {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `device token poll failed: ${(err as Error).message}`,\n );\n }\n // RFC 8628 §3.5: `authorization_pending` / `slow_down` arrive as a 400 with an `error` body — so the\n // body MUST be read before deciding failure. Parse first; only a non-OAuth-shaped body on a non-2xx fails.\n const data = (await res.json().catch(() => ({}))) as {\n access_token?: string;\n refresh_token?: string;\n expires_in?: number;\n id_token?: string;\n error?: string;\n interval?: number;\n };\n if (!res.ok && typeof data.error !== \"string\") {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `device token endpoint returned HTTP ${res.status}`,\n );\n }\n\n if (typeof data.access_token === \"string\" && data.access_token.length > 0) {\n const expiresIn = typeof data.expires_in === \"number\" ? data.expires_in : 0;\n const accountId = extractAccountId({\n access_token: data.access_token,\n id_token: data.id_token,\n });\n return {\n access: data.access_token,\n // Some device-grant providers (e.g. GitHub) issue no separate refresh token — reuse the access\n // token as the refresh handle.\n refresh: data.refresh_token ?? data.access_token,\n expires: deps.now() + expiresIn * 1000,\n ...(accountId !== undefined ? { accountId } : {}),\n };\n }\n\n if (data.error === \"authorization_pending\") {\n await deps.sleep(intervalMs + POLLING_SAFETY_MARGIN_MS);\n continue;\n }\n if (data.error === \"slow_down\") {\n // RFC 8628 §3.5: add 5s to the interval, or honor a server-provided one.\n intervalMs =\n (typeof data.interval === \"number\" && data.interval > 0\n ? data.interval\n : grant.interval + 5) * 1000;\n await deps.sleep(intervalMs + POLLING_SAFETY_MARGIN_MS);\n continue;\n }\n if (typeof data.error === \"string\") {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `device authorization rejected: ${data.error}`,\n );\n }\n await deps.sleep(intervalMs + POLLING_SAFETY_MARGIN_MS);\n }\n throw new AuthCallbackError(\n \"oauth_device_code_expired\",\n \"the device code expired before it was approved\",\n );\n}\n\n/**\n * The full device login: request a code, hand the user the verification URL + user code via `onPrompt`\n * (never printed here — the caller renders it, keeping this module output-free), then poll to completion.\n */\nexport async function deviceLogin(\n config: DeviceOAuthConfig,\n deps: DeviceDeps,\n hooks: {\n onPrompt: (p: { userCode: string; verificationUri: string; expiresIn: number }) => void;\n },\n): Promise<OAuthTokens> {\n const grant = await requestDeviceCode(config, deps);\n hooks.onPrompt({\n userCode: grant.userCode,\n verificationUri: grant.verificationUri,\n expiresIn: grant.expiresIn,\n });\n return pollDeviceToken(config, grant, deps);\n}\n\n// ─── OpenAI / ChatGPT \"headless\" device flow (two-step) ───\n\n/** Step 1 (OpenAI) — request the user code. */\nexport async function requestOpenAIUsercode(\n config: OpenAIDeviceConfig,\n deps: Pick<DeviceDeps, \"fetch\">,\n): Promise<{ deviceAuthId: string; userCode: string; interval: number }> {\n let res: Response;\n try {\n res = await deps.fetch(config.deviceUsercodeEndpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", accept: \"application/json\" },\n body: JSON.stringify({ client_id: config.clientId }),\n });\n } catch (err) {\n throw new AuthCallbackError(\n \"oauth_device_authorization_failed\",\n `usercode request failed: ${(err as Error).message}`,\n );\n }\n if (!res.ok) {\n throw new AuthCallbackError(\n \"oauth_device_authorization_failed\",\n `usercode endpoint returned HTTP ${res.status}`,\n );\n }\n const data = (await parseJsonOrFail(\n res,\n \"oauth_device_authorization_failed\",\n \"usercode endpoint\",\n )) as {\n device_auth_id?: unknown;\n user_code?: unknown;\n interval?: unknown;\n };\n if (typeof data.device_auth_id !== \"string\" || typeof data.user_code !== \"string\") {\n throw new AuthCallbackError(\n \"oauth_device_authorization_failed\",\n \"usercode response missing device_auth_id/user_code\",\n );\n }\n const interval = Math.max(\n typeof data.interval === \"string\" ? parseInt(data.interval, 10) || 5 : 5,\n 1,\n );\n return { deviceAuthId: data.device_auth_id, userCode: data.user_code, interval };\n}\n\n/**\n * Step 2+3 (OpenAI) — poll for the authorization code (200 = ready; 403/404 = pending; else fail), then\n * exchange it for tokens at the standard `/oauth/token` endpoint (reuses `exchangeCode`).\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: OpenAI two-step headless poll loop (200 ready / 403,404 pending / else fail) — cohesive network state machine ported verbatim (ADR D3).\nexport async function openaiDeviceLogin(\n config: OpenAIDeviceConfig,\n deps: DeviceDeps,\n hooks: { onPrompt: (p: { userCode: string; verificationUri: string }) => void },\n): Promise<OAuthTokens> {\n const { deviceAuthId, userCode, interval } = await requestOpenAIUsercode(config, deps);\n hooks.onPrompt({ userCode, verificationUri: config.verificationUri });\n\n const intervalMs = interval * 1000;\n // Bound the loop so it cannot spin forever if the provider never resolves (upstream loops unbounded).\n const deadline = deps.now() + 15 * 60 * 1000;\n while (deps.now() < deadline) {\n let res: Response;\n try {\n res = await deps.fetch(config.devicePollEndpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", accept: \"application/json\" },\n body: JSON.stringify({ device_auth_id: deviceAuthId, user_code: userCode }),\n });\n } catch (err) {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `device poll failed: ${(err as Error).message}`,\n );\n }\n if (res.ok) {\n const data = (await parseJsonOrFail(res, \"oauth_token_exchange_failed\", \"device poll\")) as {\n authorization_code?: unknown;\n code_verifier?: unknown;\n };\n if (typeof data.authorization_code !== \"string\" || typeof data.code_verifier !== \"string\") {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n \"device poll returned no authorization_code\",\n );\n }\n const tokens = await exchangeCode(\n config,\n { code: data.authorization_code, verifier: data.code_verifier },\n deps,\n );\n // M43 D4 fix #2 — the two-step exchange returns a JWT access token but `parseTokenResponse` only reads a\n // top-level `account_id`. JWT-extract the `chatgpt_account_id` (as the generic device poll already does)\n // so the login persists a non-empty account_id for the Codex `ChatGPT-Account-Id` header.\n return {\n ...tokens,\n accountId: tokens.accountId ?? extractAccountId({ access_token: tokens.access }),\n };\n }\n // 403/404 = still pending (user has not approved yet); anything else is a hard failure.\n if (res.status !== 403 && res.status !== 404) {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `device poll returned HTTP ${res.status}`,\n );\n }\n await deps.sleep(intervalMs + POLLING_SAFETY_MARGIN_MS);\n }\n throw new AuthCallbackError(\n \"oauth_device_code_expired\",\n \"the device authorization expired before approval\",\n );\n}\n\n/** JWT id/access-token claims we read to attribute an account. */\ninterface IdTokenClaims {\n chatgpt_account_id?: string;\n organizations?: Array<{ id: string }>;\n \"https://api.openai.com/auth\"?: { chatgpt_account_id?: string };\n}\n\n/** Decode a JWT's claim set (no signature verification — used only to read a self-reported account id). */\nexport function parseJwtClaims(token: string): IdTokenClaims | undefined {\n const parts = token.split(\".\");\n if (parts.length !== 3) return undefined;\n try {\n return JSON.parse(Buffer.from(parts[1] as string, \"base64url\").toString()) as IdTokenClaims;\n } catch {\n return undefined;\n }\n}\n\n/** Best-effort account id from an id/access token's claims (OpenAI/ChatGPT shape). */\nexport function extractAccountId(tokens: {\n id_token?: string;\n access_token?: string;\n}): string | undefined {\n for (const token of [tokens.id_token, tokens.access_token]) {\n if (token === undefined) continue;\n const claims = parseJwtClaims(token);\n const id =\n claims?.chatgpt_account_id ??\n claims?.[\"https://api.openai.com/auth\"]?.chatgpt_account_id ??\n claims?.organizations?.[0]?.id;\n if (id !== undefined) return id;\n }\n return undefined;\n}\n"]}
1
+ {"version":3,"sources":["../../src/internal/auth/oauth-device.ts"],"names":["AuthCallbackError","exchangeCode"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * M42 — the SDK OAuth device-authorization flows (RFC 8628 + OpenAI two-step headless). Split out of\n * oauth-engine.ts to respect the 500-line file budget; a sibling of the core engine. Promoted DOWN from\n * agent-builder's hardened `agents/lib/oauth-device.ts` (M37).\n *\n * Implements two device flows against their published specs: the RFC 8628 device-grant poll loop\n * (device_code → authorization_pending / slow_down) and OpenAI's two-step headless flow, including the\n * JWT-claim / account-id extraction used to attribute an account.\n * Design choices: network I/O + clock + sleep are INJECTED (deterministic tests), failures raise the SDK's\n * typed `AuthCallbackError`, and a device-code expiry DEADLINE guards each poll loop.\n *\n * @internal\n */\nimport { AuthCallbackError } from \"../../server/auth/errors.js\";\nimport { exchangeCode } from \"./oauth-engine.js\";\nimport type {\n DeviceCodeGrant,\n DeviceDeps,\n DeviceOAuthConfig,\n OAuthTokens,\n OpenAIDeviceConfig,\n} from \"./types.js\";\n\n// ─── OAuth 2.0 Device Authorization Grant (RFC 8628) — terminal-first / headless login ───\n\n/** Buffer added to every poll interval to avoid hitting the server a hair early. */\nconst POLLING_SAFETY_MARGIN_MS = 3000;\n\n/**\n * Parse a JSON body, or fail with this module's typed error instead of a raw `SyntaxError`.\n *\n * Every failure in this file is supposed to reach the caller as an `AuthCallbackError` carrying a code\n * the CLI can branch on. `res.json()` breaks that contract: a device endpoint that answers with HTML —\n * a captive portal, a corporate proxy's sign-in page, a load balancer's error page — makes it reject\n * with a `SyntaxError` that no `catch` here handles, so it escapes untyped past a caller prepared only\n * for `AuthCallbackError`.\n *\n * The body is quoted, truncated, because \"not JSON\" and \"not JSON, and it looks like a proxy login\n * page\" are different diagnoses for whoever is holding the terminal — and routing them to the provider\n * when the fault is their network is the expensive kind of wrong.\n */\nasync function parseJsonOrFail(res: Response, code: string, what: string): Promise<unknown> {\n const text = await res.text().catch(() => \"\");\n try {\n return JSON.parse(text);\n } catch {\n throw new AuthCallbackError(code, `${what} returned a non-JSON body: ${text.slice(0, 120)}`);\n }\n}\n\n/** Step 1 — request a device code. POSTs `{client_id, scope}` to the device endpoint. */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: RFC 8628 device-code request — cohesive network response parser ported verbatim (ADR D3).\nexport async function requestDeviceCode(\n config: DeviceOAuthConfig,\n deps: Pick<DeviceDeps, \"fetch\">,\n): Promise<DeviceCodeGrant> {\n let res: Response;\n try {\n res = await deps.fetch(config.deviceCodeEndpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\", accept: \"application/json\" },\n body: new URLSearchParams({\n client_id: config.clientId,\n scope: config.scopes.join(\" \"),\n }).toString(),\n });\n } catch (err) {\n throw new AuthCallbackError(\n \"oauth_device_authorization_failed\",\n `device request failed: ${(err as Error).message}`,\n );\n }\n if (!res.ok) {\n throw new AuthCallbackError(\n \"oauth_device_authorization_failed\",\n `device endpoint returned HTTP ${res.status}`,\n );\n }\n const data = (await parseJsonOrFail(\n res,\n \"oauth_device_authorization_failed\",\n \"device endpoint\",\n )) as {\n device_code?: unknown;\n user_code?: unknown;\n verification_uri?: unknown;\n verification_uri_complete?: unknown;\n interval?: unknown;\n expires_in?: unknown;\n };\n if (typeof data.device_code !== \"string\" || typeof data.user_code !== \"string\") {\n throw new AuthCallbackError(\n \"oauth_device_authorization_failed\",\n \"device response missing device_code/user_code\",\n );\n }\n const verification =\n typeof data.verification_uri === \"string\"\n ? data.verification_uri\n : typeof data.verification_uri_complete === \"string\"\n ? data.verification_uri_complete\n : \"\";\n return {\n deviceCode: data.device_code,\n userCode: data.user_code,\n verificationUri: verification,\n interval: typeof data.interval === \"number\" && data.interval > 0 ? data.interval : 5,\n expiresIn: typeof data.expires_in === \"number\" && data.expires_in > 0 ? data.expires_in : 900,\n };\n}\n\n/**\n * Step 2 — poll the token endpoint until the user approves (or the code expires). Handles RFC 8628\n * `authorization_pending` (keep waiting) and `slow_down` (back off), with an expiry deadline so it cannot\n * spin forever.\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: RFC 8628 poll state machine (authorization_pending / slow_down / success / expiry) — a cohesive loop ported verbatim (ADR D3); fragmenting it would obscure the protocol.\nexport async function pollDeviceToken(\n config: DeviceOAuthConfig,\n grant: DeviceCodeGrant,\n deps: DeviceDeps,\n): Promise<OAuthTokens> {\n const deadline = deps.now() + grant.expiresIn * 1000;\n let intervalMs = grant.interval * 1000;\n\n while (deps.now() < deadline) {\n let res: Response;\n try {\n res = await deps.fetch(config.tokenEndpoint, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/x-www-form-urlencoded\",\n accept: \"application/json\",\n },\n body: new URLSearchParams({\n client_id: config.clientId,\n device_code: grant.deviceCode,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n }).toString(),\n });\n } catch (err) {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `device token poll failed: ${(err as Error).message}`,\n );\n }\n // RFC 8628 §3.5: `authorization_pending` / `slow_down` arrive as a 400 with an `error` body — so the\n // body MUST be read before deciding failure. Parse first; only a non-OAuth-shaped body on a non-2xx fails.\n const data = (await res.json().catch(() => ({}))) as {\n access_token?: string;\n refresh_token?: string;\n expires_in?: number;\n id_token?: string;\n error?: string;\n interval?: number;\n };\n if (!res.ok && typeof data.error !== \"string\") {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `device token endpoint returned HTTP ${res.status}`,\n );\n }\n\n if (typeof data.access_token === \"string\" && data.access_token.length > 0) {\n const expiresIn = typeof data.expires_in === \"number\" ? data.expires_in : 0;\n const accountId = extractAccountId({\n access_token: data.access_token,\n id_token: data.id_token,\n });\n return {\n access: data.access_token,\n // Some device-grant providers (e.g. GitHub) issue no separate refresh token — reuse the access\n // token as the refresh handle.\n refresh: data.refresh_token ?? data.access_token,\n expires: deps.now() + expiresIn * 1000,\n ...(accountId !== undefined ? { accountId } : {}),\n };\n }\n\n if (data.error === \"authorization_pending\") {\n await deps.sleep(intervalMs + POLLING_SAFETY_MARGIN_MS);\n continue;\n }\n if (data.error === \"slow_down\") {\n // RFC 8628 §3.5: add 5s to the interval, or honor a server-provided one.\n intervalMs =\n (typeof data.interval === \"number\" && data.interval > 0\n ? data.interval\n : grant.interval + 5) * 1000;\n await deps.sleep(intervalMs + POLLING_SAFETY_MARGIN_MS);\n continue;\n }\n if (typeof data.error === \"string\") {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `device authorization rejected: ${data.error}`,\n );\n }\n await deps.sleep(intervalMs + POLLING_SAFETY_MARGIN_MS);\n }\n throw new AuthCallbackError(\n \"oauth_device_code_expired\",\n \"the device code expired before it was approved\",\n );\n}\n\n/**\n * The full device login: request a code, hand the user the verification URL + user code via `onPrompt`\n * (never printed here — the caller renders it, keeping this module output-free), then poll to completion.\n */\nexport async function deviceLogin(\n config: DeviceOAuthConfig,\n deps: DeviceDeps,\n hooks: {\n onPrompt: (p: { userCode: string; verificationUri: string; expiresIn: number }) => void;\n },\n): Promise<OAuthTokens> {\n const grant = await requestDeviceCode(config, deps);\n hooks.onPrompt({\n userCode: grant.userCode,\n verificationUri: grant.verificationUri,\n expiresIn: grant.expiresIn,\n });\n return pollDeviceToken(config, grant, deps);\n}\n\n// ─── OpenAI / ChatGPT \"headless\" device flow (two-step) ───\n\n/** Step 1 (OpenAI) — request the user code. */\nexport async function requestOpenAIUsercode(\n config: OpenAIDeviceConfig,\n deps: Pick<DeviceDeps, \"fetch\">,\n): Promise<{ deviceAuthId: string; userCode: string; interval: number }> {\n let res: Response;\n try {\n res = await deps.fetch(config.deviceUsercodeEndpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", accept: \"application/json\" },\n body: JSON.stringify({ client_id: config.clientId }),\n });\n } catch (err) {\n throw new AuthCallbackError(\n \"oauth_device_authorization_failed\",\n `usercode request failed: ${(err as Error).message}`,\n );\n }\n if (!res.ok) {\n throw new AuthCallbackError(\n \"oauth_device_authorization_failed\",\n `usercode endpoint returned HTTP ${res.status}`,\n );\n }\n const data = (await parseJsonOrFail(\n res,\n \"oauth_device_authorization_failed\",\n \"usercode endpoint\",\n )) as {\n device_auth_id?: unknown;\n user_code?: unknown;\n interval?: unknown;\n };\n if (typeof data.device_auth_id !== \"string\" || typeof data.user_code !== \"string\") {\n throw new AuthCallbackError(\n \"oauth_device_authorization_failed\",\n \"usercode response missing device_auth_id/user_code\",\n );\n }\n const interval = Math.max(\n typeof data.interval === \"string\" ? parseInt(data.interval, 10) || 5 : 5,\n 1,\n );\n return { deviceAuthId: data.device_auth_id, userCode: data.user_code, interval };\n}\n\n/**\n * Step 2+3 (OpenAI) — poll for the authorization code (200 = ready; 403/404 = pending; else fail), then\n * exchange it for tokens at the standard `/oauth/token` endpoint (reuses `exchangeCode`).\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: OpenAI two-step headless poll loop (200 ready / 403,404 pending / else fail) — cohesive network state machine ported verbatim (ADR D3).\nexport async function openaiDeviceLogin(\n config: OpenAIDeviceConfig,\n deps: DeviceDeps,\n hooks: { onPrompt: (p: { userCode: string; verificationUri: string }) => void },\n): Promise<OAuthTokens> {\n const { deviceAuthId, userCode, interval } = await requestOpenAIUsercode(config, deps);\n hooks.onPrompt({ userCode, verificationUri: config.verificationUri });\n\n const intervalMs = interval * 1000;\n // Bound the loop so it cannot spin forever if the provider never resolves (upstream loops unbounded).\n const deadline = deps.now() + 15 * 60 * 1000;\n while (deps.now() < deadline) {\n let res: Response;\n try {\n res = await deps.fetch(config.devicePollEndpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", accept: \"application/json\" },\n body: JSON.stringify({ device_auth_id: deviceAuthId, user_code: userCode }),\n });\n } catch (err) {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `device poll failed: ${(err as Error).message}`,\n );\n }\n if (res.ok) {\n const data = (await parseJsonOrFail(res, \"oauth_token_exchange_failed\", \"device poll\")) as {\n authorization_code?: unknown;\n code_verifier?: unknown;\n };\n if (typeof data.authorization_code !== \"string\" || typeof data.code_verifier !== \"string\") {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n \"device poll returned no authorization_code\",\n );\n }\n const tokens = await exchangeCode(\n config,\n { code: data.authorization_code, verifier: data.code_verifier },\n deps,\n );\n // M43 D4 fix #2 — the two-step exchange returns a JWT access token but `parseTokenResponse` only reads a\n // top-level `account_id`. JWT-extract the `chatgpt_account_id` (as the generic device poll already does)\n // so the login persists a non-empty account_id for the Codex `ChatGPT-Account-Id` header.\n return {\n ...tokens,\n accountId: tokens.accountId ?? extractAccountId({ access_token: tokens.access }),\n };\n }\n // 403/404 = still pending (user has not approved yet); anything else is a hard failure.\n if (res.status !== 403 && res.status !== 404) {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `device poll returned HTTP ${res.status}`,\n );\n }\n await deps.sleep(intervalMs + POLLING_SAFETY_MARGIN_MS);\n }\n throw new AuthCallbackError(\n \"oauth_device_code_expired\",\n \"the device authorization expired before approval\",\n );\n}\n\n/** JWT id/access-token claims we read to attribute an account. */\ninterface IdTokenClaims {\n chatgpt_account_id?: string;\n organizations?: Array<{ id: string }>;\n \"https://api.openai.com/auth\"?: { chatgpt_account_id?: string };\n}\n\n/** Decode a JWT's claim set (no signature verification — used only to read a self-reported account id). */\nexport function parseJwtClaims(token: string): IdTokenClaims | undefined {\n const parts = token.split(\".\");\n if (parts.length !== 3) return undefined;\n try {\n return JSON.parse(Buffer.from(parts[1] as string, \"base64url\").toString()) as IdTokenClaims;\n } catch {\n return undefined;\n }\n}\n\n/** Best-effort account id from an id/access token's claims (OpenAI/ChatGPT shape). */\nexport function extractAccountId(tokens: {\n id_token?: string;\n access_token?: string;\n}): string | undefined {\n for (const token of [tokens.id_token, tokens.access_token]) {\n if (token === undefined) continue;\n const claims = parseJwtClaims(token);\n const id =\n claims?.chatgpt_account_id ??\n claims?.[\"https://api.openai.com/auth\"]?.chatgpt_account_id ??\n claims?.organizations?.[0]?.id;\n if (id !== undefined) return id;\n }\n return undefined;\n}\n"]}
@@ -1,8 +1,9 @@
1
1
  export { providerFromApiKeyPrefix } from '../chunk-NAF4L3T6.js';
2
- import { exchangeCode } from '../chunk-EJROML3Q.js';
3
- export { ensureFreshCredential, exchangeCode, persistOAuthTokens, refreshOAuthTokens, resolveCredential } from '../chunk-EJROML3Q.js';
2
+ import { exchangeCode } from '../chunk-TX56N7FR.js';
3
+ export { ensureFreshCredential, exchangeCode, persistOAuthTokens, refreshOAuthTokens, resolveCredential } from '../chunk-TX56N7FR.js';
4
4
  export { CredentialError, assertSecureModes, authFilePath, credentialHome, readAuthFile, readStoredOAuth, writeCredential } from '../chunk-5Q4RTLMP.js';
5
5
  import { AuthCallbackError } from '../chunk-H3CVJSFC.js';
6
+ import '../chunk-Q7GYMXVC.js';
6
7
  import '../chunk-B5MHRQJZ.js';
7
8
  import '../chunk-CZJ6Q7CW.js';
8
9