@jcheesepkg/nanobot 0.9.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/README.md +18 -18
  2. package/dist/agent/context.d.mts +4 -4
  3. package/dist/agent/context.d.mts.map +1 -1
  4. package/dist/agent/context.mjs +27 -28
  5. package/dist/agent/context.mjs.map +1 -1
  6. package/dist/agent/loop.d.mts +5 -3
  7. package/dist/agent/loop.d.mts.map +1 -1
  8. package/dist/agent/loop.mjs +64 -55
  9. package/dist/agent/loop.mjs.map +1 -1
  10. package/dist/agent/memory.d.mts.map +1 -1
  11. package/dist/agent/memory.mjs +3 -3
  12. package/dist/agent/memory.mjs.map +1 -1
  13. package/dist/agent/skills.d.mts.map +1 -1
  14. package/dist/agent/skills.mjs +4 -4
  15. package/dist/agent/skills.mjs.map +1 -1
  16. package/dist/agent/subagent.d.mts.map +1 -1
  17. package/dist/agent/subagent.mjs +22 -22
  18. package/dist/agent/subagent.mjs.map +1 -1
  19. package/dist/agent/tools/base.mjs +2 -2
  20. package/dist/agent/tools/base.mjs.map +1 -1
  21. package/dist/agent/tools/cron.d.mts +1 -1
  22. package/dist/agent/tools/cron.d.mts.map +1 -1
  23. package/dist/agent/tools/cron.mjs +11 -11
  24. package/dist/agent/tools/cron.mjs.map +1 -1
  25. package/dist/agent/tools/filesystem.d.mts +4 -4
  26. package/dist/agent/tools/filesystem.d.mts.map +1 -1
  27. package/dist/agent/tools/filesystem.mjs +20 -20
  28. package/dist/agent/tools/filesystem.mjs.map +1 -1
  29. package/dist/agent/tools/flex.d.mts +1 -1
  30. package/dist/agent/tools/flex.d.mts.map +1 -1
  31. package/dist/agent/tools/flex.mjs +112 -112
  32. package/dist/agent/tools/flex.mjs.map +1 -1
  33. package/dist/agent/tools/flex.test.mjs +60 -59
  34. package/dist/agent/tools/flex.test.mjs.map +1 -1
  35. package/dist/agent/tools/message.d.mts +1 -1
  36. package/dist/agent/tools/message.d.mts.map +1 -1
  37. package/dist/agent/tools/message.mjs +4 -4
  38. package/dist/agent/tools/message.mjs.map +1 -1
  39. package/dist/agent/tools/registry.d.mts.map +1 -1
  40. package/dist/agent/tools/registry.mjs +4 -4
  41. package/dist/agent/tools/registry.mjs.map +1 -1
  42. package/dist/agent/tools/shell.d.mts +1 -1
  43. package/dist/agent/tools/shell.mjs +4 -4
  44. package/dist/agent/tools/shell.mjs.map +1 -1
  45. package/dist/agent/tools/spawn.d.mts +1 -1
  46. package/dist/agent/tools/spawn.d.mts.map +1 -1
  47. package/dist/agent/tools/spawn.mjs +4 -4
  48. package/dist/agent/tools/spawn.mjs.map +1 -1
  49. package/dist/agent/tools/web.d.mts +2 -2
  50. package/dist/agent/tools/web.d.mts.map +1 -1
  51. package/dist/agent/tools/web.mjs +36 -36
  52. package/dist/agent/tools/web.mjs.map +1 -1
  53. package/dist/bus/events.mjs +1 -1
  54. package/dist/bus/events.mjs.map +1 -1
  55. package/dist/bus/queue.d.mts.map +1 -1
  56. package/dist/bus/queue.mjs.map +1 -1
  57. package/dist/channels/base.d.mts.map +1 -1
  58. package/dist/channels/base.mjs +2 -2
  59. package/dist/channels/base.mjs.map +1 -1
  60. package/dist/channels/line.d.mts +1 -0
  61. package/dist/channels/line.d.mts.map +1 -1
  62. package/dist/channels/line.mjs +65 -65
  63. package/dist/channels/line.mjs.map +1 -1
  64. package/dist/channels/line.test.mjs +26 -27
  65. package/dist/channels/line.test.mjs.map +1 -1
  66. package/dist/channels/manager.d.mts.map +1 -1
  67. package/dist/channels/manager.mjs +9 -9
  68. package/dist/channels/manager.mjs.map +1 -1
  69. package/dist/channels/telegram.mjs +34 -34
  70. package/dist/channels/telegram.mjs.map +1 -1
  71. package/dist/cli/index.mjs +36 -36
  72. package/dist/cli/index.mjs.map +1 -1
  73. package/dist/config/loader.d.mts.map +1 -1
  74. package/dist/config/loader.mjs +1 -1
  75. package/dist/config/loader.mjs.map +1 -1
  76. package/dist/config/schema.d.mts +381 -381
  77. package/dist/config/schema.d.mts.map +1 -1
  78. package/dist/config/schema.mjs +42 -42
  79. package/dist/config/schema.mjs.map +1 -1
  80. package/dist/gateway/server.d.mts.map +1 -1
  81. package/dist/gateway/server.mjs +48 -54
  82. package/dist/gateway/server.mjs.map +1 -1
  83. package/dist/heartbeat/service.d.mts.map +1 -1
  84. package/dist/heartbeat/service.mjs +8 -8
  85. package/dist/heartbeat/service.mjs.map +1 -1
  86. package/dist/index.d.mts +1 -1
  87. package/dist/index.d.mts.map +1 -1
  88. package/dist/index.mjs +2 -2
  89. package/dist/index.mjs.map +1 -1
  90. package/dist/node_modules/{@jridgewell → .bun/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell}/sourcemap-codec/dist/sourcemap-codec.mjs +1 -1
  91. package/dist/node_modules/.bun/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map +1 -0
  92. package/dist/node_modules/{@vitest → .bun/@vitest_expect@2.1.9/node_modules/@vitest}/expect/dist/index.mjs +8 -8
  93. package/dist/node_modules/.bun/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.mjs.map +1 -0
  94. package/dist/node_modules/{@vitest → .bun/@vitest_pretty-format@2.1.9/node_modules/@vitest}/pretty-format/dist/index.mjs +2 -2
  95. package/dist/node_modules/.bun/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.mjs.map +1 -0
  96. package/dist/node_modules/{@vitest → .bun/@vitest_runner@2.1.9/node_modules/@vitest}/runner/dist/chunk-tasks.mjs +1 -1
  97. package/dist/node_modules/.bun/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.mjs.map +1 -0
  98. package/dist/node_modules/{@vitest → .bun/@vitest_runner@2.1.9/node_modules/@vitest}/runner/dist/index.mjs +6 -6
  99. package/dist/node_modules/.bun/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.mjs.map +1 -0
  100. package/dist/node_modules/{@vitest → .bun/@vitest_snapshot@2.1.9/node_modules/@vitest}/snapshot/dist/index.mjs +5 -5
  101. package/dist/node_modules/.bun/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.mjs.map +1 -0
  102. package/dist/node_modules/{@vitest → .bun/@vitest_spy@2.1.9/node_modules/@vitest}/spy/dist/index.mjs +2 -2
  103. package/dist/node_modules/.bun/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.mjs.map +1 -0
  104. package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/chunk-_commonjsHelpers.mjs +3 -3
  105. package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.mjs.map +1 -0
  106. package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/diff.mjs +4 -4
  107. package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.mjs.map +1 -0
  108. package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/error.mjs +3 -3
  109. package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.mjs.map +1 -0
  110. package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/helpers.mjs +1 -1
  111. package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.mjs.map +1 -0
  112. package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/index.mjs +3 -3
  113. package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.mjs.map +1 -0
  114. package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/source-map.mjs +1 -1
  115. package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.mjs.map +1 -0
  116. package/dist/node_modules/{chai → .bun/chai@5.3.3/node_modules/chai}/index.mjs +1 -1
  117. package/dist/node_modules/.bun/chai@5.3.3/node_modules/chai/index.mjs.map +1 -0
  118. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/arguments.mjs +1 -1
  119. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/arguments.mjs.map +1 -0
  120. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/array.mjs +1 -1
  121. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/array.mjs.map +1 -0
  122. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/bigint.mjs +1 -1
  123. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/bigint.mjs.map +1 -0
  124. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/class.mjs +1 -1
  125. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/class.mjs.map +1 -0
  126. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/date.mjs +1 -1
  127. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/date.mjs.map +1 -0
  128. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/error.mjs +1 -1
  129. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/error.mjs.map +1 -0
  130. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/function.mjs +1 -1
  131. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/function.mjs.map +1 -0
  132. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/helpers.mjs +1 -1
  133. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/helpers.mjs.map +1 -0
  134. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/html.mjs +1 -1
  135. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/html.mjs.map +1 -0
  136. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/index.mjs +1 -1
  137. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/index.mjs.map +1 -0
  138. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/map.mjs +1 -1
  139. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/map.mjs.map +1 -0
  140. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/number.mjs +1 -1
  141. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/number.mjs.map +1 -0
  142. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/object.mjs +1 -1
  143. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/object.mjs.map +1 -0
  144. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/promise.mjs +6 -0
  145. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/promise.mjs.map +1 -0
  146. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/regexp.mjs +1 -1
  147. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/regexp.mjs.map +1 -0
  148. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/set.mjs +1 -1
  149. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/set.mjs.map +1 -0
  150. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/string.mjs +1 -1
  151. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/string.mjs.map +1 -0
  152. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/symbol.mjs +1 -1
  153. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/symbol.mjs.map +1 -0
  154. package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/typedarray.mjs +1 -1
  155. package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/typedarray.mjs.map +1 -0
  156. package/dist/node_modules/{magic-string → .bun/magic-string@0.30.21/node_modules/magic-string}/dist/magic-string.es.mjs +2 -2
  157. package/dist/node_modules/.bun/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.mjs.map +1 -0
  158. package/dist/node_modules/{@vitest/snapshot → .bun/pathe@1.1.2}/node_modules/pathe/dist/shared/pathe.ff20891b.mjs +1 -1
  159. package/dist/node_modules/.bun/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.mjs.map +1 -0
  160. package/dist/node_modules/{tinyrainbow → .bun/tinyrainbow@1.2.0/node_modules/tinyrainbow}/dist/chunk-BVHSVHOK.mjs +1 -1
  161. package/dist/node_modules/.bun/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.mjs.map +1 -0
  162. package/dist/node_modules/{tinyrainbow → .bun/tinyrainbow@1.2.0/node_modules/tinyrainbow}/dist/node.mjs +1 -1
  163. package/dist/node_modules/.bun/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.mjs.map +1 -0
  164. package/dist/node_modules/{tinyspy → .bun/tinyspy@3.0.2/node_modules/tinyspy}/dist/index.mjs +1 -1
  165. package/dist/node_modules/.bun/tinyspy@3.0.2/node_modules/tinyspy/dist/index.mjs.map +1 -0
  166. package/dist/node_modules/{vitest → .bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest}/dist/chunks/_commonjsHelpers.BFTU3MAI.mjs +1 -1
  167. package/dist/node_modules/.bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.mjs.map +1 -0
  168. package/dist/node_modules/{vitest → .bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest}/dist/chunks/date.W2xKR2qe.mjs +1 -1
  169. package/dist/node_modules/.bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest/dist/chunks/date.W2xKR2qe.mjs.map +1 -0
  170. package/dist/node_modules/{vitest → .bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest}/dist/chunks/utils.C8RiOc4B.mjs +2 -2
  171. package/dist/node_modules/.bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest/dist/chunks/utils.C8RiOc4B.mjs.map +1 -0
  172. package/dist/node_modules/{vitest → .bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest}/dist/chunks/vi.DgezovHB.mjs +11 -11
  173. package/dist/node_modules/.bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest/dist/chunks/vi.DgezovHB.mjs.map +1 -0
  174. package/dist/providers/base.d.mts +2 -2
  175. package/dist/providers/base.d.mts.map +1 -1
  176. package/dist/providers/openai-provider.d.mts.map +1 -1
  177. package/dist/providers/openai-provider.mjs +10 -9
  178. package/dist/providers/openai-provider.mjs.map +1 -1
  179. package/dist/providers/registry.d.mts +1 -1
  180. package/dist/providers/registry.d.mts.map +1 -1
  181. package/dist/providers/registry.mjs +99 -99
  182. package/dist/providers/registry.mjs.map +1 -1
  183. package/dist/session/manager.d.mts +2 -2
  184. package/dist/session/manager.d.mts.map +1 -1
  185. package/dist/session/manager.mjs +18 -19
  186. package/dist/session/manager.mjs.map +1 -1
  187. package/dist/utils/helpers.d.mts.map +1 -1
  188. package/dist/utils/helpers.mjs.map +1 -1
  189. package/package.json +11 -11
  190. package/skills/cron/SKILL.md +12 -8
  191. package/skills/daily-summary/SKILL.md +5 -1
  192. package/skills/english/SKILL.md +72 -14
  193. package/skills/expense/SKILL.md +15 -11
  194. package/skills/fortune/SKILL.md +25 -21
  195. package/skills/habit/SKILL.md +7 -6
  196. package/skills/hydration/SKILL.md +8 -5
  197. package/skills/memory/SKILL.md +1 -0
  198. package/skills/mood/SKILL.md +13 -9
  199. package/skills/skill-creator/SKILL.md +22 -0
  200. package/skills/summarize/SKILL.md +1 -0
  201. package/skills/weather/SKILL.md +11 -9
  202. package/dist/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map +0 -1
  203. package/dist/node_modules/@vitest/expect/dist/index.mjs.map +0 -1
  204. package/dist/node_modules/@vitest/pretty-format/dist/index.mjs.map +0 -1
  205. package/dist/node_modules/@vitest/runner/dist/chunk-tasks.mjs.map +0 -1
  206. package/dist/node_modules/@vitest/runner/dist/index.mjs.map +0 -1
  207. package/dist/node_modules/@vitest/snapshot/dist/index.mjs.map +0 -1
  208. package/dist/node_modules/@vitest/snapshot/node_modules/pathe/dist/shared/pathe.ff20891b.mjs.map +0 -1
  209. package/dist/node_modules/@vitest/spy/dist/index.mjs.map +0 -1
  210. package/dist/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.mjs.map +0 -1
  211. package/dist/node_modules/@vitest/utils/dist/diff.mjs.map +0 -1
  212. package/dist/node_modules/@vitest/utils/dist/error.mjs.map +0 -1
  213. package/dist/node_modules/@vitest/utils/dist/helpers.mjs.map +0 -1
  214. package/dist/node_modules/@vitest/utils/dist/index.mjs.map +0 -1
  215. package/dist/node_modules/@vitest/utils/dist/source-map.mjs.map +0 -1
  216. package/dist/node_modules/chai/index.mjs.map +0 -1
  217. package/dist/node_modules/loupe/lib/arguments.mjs.map +0 -1
  218. package/dist/node_modules/loupe/lib/array.mjs.map +0 -1
  219. package/dist/node_modules/loupe/lib/bigint.mjs.map +0 -1
  220. package/dist/node_modules/loupe/lib/class.mjs.map +0 -1
  221. package/dist/node_modules/loupe/lib/date.mjs.map +0 -1
  222. package/dist/node_modules/loupe/lib/error.mjs.map +0 -1
  223. package/dist/node_modules/loupe/lib/function.mjs.map +0 -1
  224. package/dist/node_modules/loupe/lib/helpers.mjs.map +0 -1
  225. package/dist/node_modules/loupe/lib/html.mjs.map +0 -1
  226. package/dist/node_modules/loupe/lib/index.mjs.map +0 -1
  227. package/dist/node_modules/loupe/lib/map.mjs.map +0 -1
  228. package/dist/node_modules/loupe/lib/number.mjs.map +0 -1
  229. package/dist/node_modules/loupe/lib/object.mjs.map +0 -1
  230. package/dist/node_modules/loupe/lib/promise.mjs +0 -6
  231. package/dist/node_modules/loupe/lib/promise.mjs.map +0 -1
  232. package/dist/node_modules/loupe/lib/regexp.mjs.map +0 -1
  233. package/dist/node_modules/loupe/lib/set.mjs.map +0 -1
  234. package/dist/node_modules/loupe/lib/string.mjs.map +0 -1
  235. package/dist/node_modules/loupe/lib/symbol.mjs.map +0 -1
  236. package/dist/node_modules/loupe/lib/typedarray.mjs.map +0 -1
  237. package/dist/node_modules/magic-string/dist/magic-string.es.mjs.map +0 -1
  238. package/dist/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.mjs.map +0 -1
  239. package/dist/node_modules/tinyrainbow/dist/node.mjs.map +0 -1
  240. package/dist/node_modules/tinyspy/dist/index.mjs.map +0 -1
  241. package/dist/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.mjs.map +0 -1
  242. package/dist/node_modules/vitest/dist/chunks/date.W2xKR2qe.mjs.map +0 -1
  243. package/dist/node_modules/vitest/dist/chunks/utils.C8RiOc4B.mjs.map +0 -1
  244. package/dist/node_modules/vitest/dist/chunks/vi.DgezovHB.mjs.map +0 -1
  245. /package/dist/node_modules/{@vitest → .bun/@vitest_runner@2.1.9/node_modules/@vitest}/runner/dist/utils.mjs +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"openai-provider.mjs","names":[],"sources":["../../src/providers/openai-provider.ts"],"sourcesContent":["import OpenAI from \"openai\";\nimport type {\n LLMProvider,\n LLMResponse,\n ToolCallRequest,\n ToolDefinition,\n ChatMessage,\n} from \"./base.js\";\n\n/**\n * LLM provider using the OpenAI SDK.\n *\n * Works with OpenAI, OpenRouter, Anthropic (via proxy), and any\n * OpenAI-compatible API by setting apiBase.\n */\nexport class OpenAIProvider implements LLMProvider {\n private client: OpenAI;\n private defaultModel: string;\n private extraHeaders: Record<string, string>;\n\n constructor(params: {\n apiKey: string;\n apiBase?: string | null;\n defaultModel?: string;\n extraHeaders?: Record<string, string> | null;\n }) {\n this.client = new OpenAI({\n apiKey: params.apiKey,\n baseURL: params.apiBase ?? undefined,\n defaultHeaders: params.extraHeaders ?? undefined,\n });\n this.defaultModel = params.defaultModel ?? \"anthropic/claude-sonnet-4-20250514\";\n this.extraHeaders = params.extraHeaders ?? {};\n }\n\n getDefaultModel(): string {\n return this.defaultModel;\n }\n\n async chat(params: {\n messages: ChatMessage[];\n tools?: ToolDefinition[];\n model?: string;\n maxTokens?: number;\n signal?: AbortSignal;\n }): Promise<LLMResponse> {\n const model = params.model ?? this.defaultModel;\n\n const requestParams: OpenAI.ChatCompletionCreateParams = {\n model,\n messages: params.messages as OpenAI.ChatCompletionMessageParam[],\n max_tokens: params.maxTokens ?? 8192,\n };\n\n if (params.tools && params.tools.length > 0) {\n requestParams.tools =\n params.tools as OpenAI.ChatCompletionTool[];\n }\n\n const response = await this.client.chat.completions.create(\n requestParams,\n params.signal ? { signal: params.signal } : undefined,\n );\n\n const choice = response.choices[0];\n if (!choice) {\n return {\n content: null,\n toolCalls: [],\n hasToolCalls: false,\n };\n }\n\n const message = choice.message;\n const toolCalls: ToolCallRequest[] = [];\n\n if (message.tool_calls) {\n for (const tc of message.tool_calls) {\n let args: Record<string, unknown> = {};\n try {\n args = JSON.parse(tc.function.arguments);\n } catch {\n // If JSON parse fails, wrap raw string\n args = { _raw: tc.function.arguments };\n }\n\n toolCalls.push({\n id: tc.id,\n name: tc.function.name,\n arguments: args,\n });\n }\n }\n\n return {\n content: message.content ?? null,\n toolCalls,\n hasToolCalls: toolCalls.length > 0,\n usage: response.usage\n ? {\n promptTokens: response.usage.prompt_tokens,\n completionTokens: response.usage.completion_tokens,\n totalTokens: response.usage.total_tokens,\n }\n : undefined,\n };\n }\n}\n"],"mappings":";;;;;;;;;AAeA,IAAa,iBAAb,MAAmD;CACjD,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YAAY,QAKT;AACD,OAAK,SAAS,IAAI,OAAO;GACvB,QAAQ,OAAO;GACf,SAAS,OAAO,WAAW;GAC3B,gBAAgB,OAAO,gBAAgB;GACxC,CAAC;AACF,OAAK,eAAe,OAAO,gBAAgB;AAC3C,OAAK,eAAe,OAAO,gBAAgB,EAAE;;CAG/C,kBAA0B;AACxB,SAAO,KAAK;;CAGd,MAAM,KAAK,QAMc;EAGvB,MAAM,gBAAmD;GACvD,OAHY,OAAO,SAAS,KAAK;GAIjC,UAAU,OAAO;GACjB,YAAY,OAAO,aAAa;GACjC;AAED,MAAI,OAAO,SAAS,OAAO,MAAM,SAAS,EACxC,eAAc,QACZ,OAAO;EAGX,MAAM,WAAW,MAAM,KAAK,OAAO,KAAK,YAAY,OAClD,eACA,OAAO,SAAS,EAAE,QAAQ,OAAO,QAAQ,GAAG,OAC7C;EAED,MAAM,SAAS,SAAS,QAAQ;AAChC,MAAI,CAAC,OACH,QAAO;GACL,SAAS;GACT,WAAW,EAAE;GACb,cAAc;GACf;EAGH,MAAM,UAAU,OAAO;EACvB,MAAM,YAA+B,EAAE;AAEvC,MAAI,QAAQ,WACV,MAAK,MAAM,MAAM,QAAQ,YAAY;GACnC,IAAI,OAAgC,EAAE;AACtC,OAAI;AACF,WAAO,KAAK,MAAM,GAAG,SAAS,UAAU;WAClC;AAEN,WAAO,EAAE,MAAM,GAAG,SAAS,WAAW;;AAGxC,aAAU,KAAK;IACb,IAAI,GAAG;IACP,MAAM,GAAG,SAAS;IAClB,WAAW;IACZ,CAAC;;AAIN,SAAO;GACL,SAAS,QAAQ,WAAW;GAC5B;GACA,cAAc,UAAU,SAAS;GACjC,OAAO,SAAS,QACZ;IACE,cAAc,SAAS,MAAM;IAC7B,kBAAkB,SAAS,MAAM;IACjC,aAAa,SAAS,MAAM;IAC7B,GACD;GACL"}
1
+ {"version":3,"file":"openai-provider.mjs","names":[],"sources":["../../src/providers/openai-provider.ts"],"sourcesContent":["import { OpenAI } from \"openai\";\n\nimport type {\n LLMProvider,\n LLMResponse,\n ToolCallRequest,\n ToolDefinition,\n ChatMessage,\n} from \"./base.js\";\n\n/**\n * LLM provider using the OpenAI SDK.\n *\n * Works with OpenAI, OpenRouter, Anthropic (via proxy), and any\n * OpenAI-compatible API by setting apiBase.\n */\nexport class OpenAIProvider implements LLMProvider {\n private client: OpenAI;\n private defaultModel: string;\n private extraHeaders: Record<string, string>;\n\n constructor(params: {\n apiKey: string;\n apiBase?: string | null;\n defaultModel?: string;\n extraHeaders?: Record<string, string> | null;\n }) {\n this.client = new OpenAI({\n apiKey: params.apiKey,\n baseURL: params.apiBase ?? undefined,\n defaultHeaders: params.extraHeaders ?? undefined,\n });\n this.defaultModel =\n params.defaultModel ?? \"anthropic/claude-sonnet-4-20250514\";\n this.extraHeaders = params.extraHeaders ?? {};\n }\n\n getDefaultModel(): string {\n return this.defaultModel;\n }\n\n async chat(params: {\n messages: ChatMessage[];\n tools?: ToolDefinition[];\n model?: string;\n maxTokens?: number;\n signal?: AbortSignal;\n }): Promise<LLMResponse> {\n const model = params.model ?? this.defaultModel;\n\n const requestParams: OpenAI.ChatCompletionCreateParams = {\n max_tokens: params.maxTokens ?? 8192,\n messages: params.messages as OpenAI.ChatCompletionMessageParam[],\n model,\n };\n\n if (params.tools && params.tools.length > 0) {\n requestParams.tools = params.tools as OpenAI.ChatCompletionTool[];\n }\n\n const response = await this.client.chat.completions.create(\n requestParams,\n params.signal ? { signal: params.signal } : undefined\n );\n\n const choice = response.choices[0];\n if (!choice) {\n return {\n content: null,\n hasToolCalls: false,\n toolCalls: [],\n };\n }\n\n const { message } = choice;\n const toolCalls: ToolCallRequest[] = [];\n\n if (message.tool_calls) {\n for (const tc of message.tool_calls) {\n let args: Record<string, unknown> = {};\n try {\n args = JSON.parse(tc.function.arguments);\n } catch {\n // If JSON parse fails, wrap raw string\n args = { _raw: tc.function.arguments };\n }\n\n toolCalls.push({\n arguments: args,\n id: tc.id,\n name: tc.function.name,\n });\n }\n }\n\n return {\n content: message.content ?? null,\n hasToolCalls: toolCalls.length > 0,\n toolCalls,\n usage: response.usage\n ? {\n promptTokens: response.usage.prompt_tokens,\n completionTokens: response.usage.completion_tokens,\n totalTokens: response.usage.total_tokens,\n }\n : undefined,\n };\n }\n}\n"],"mappings":";;;;;;;;;AAgBA,IAAa,iBAAb,MAAmD;CACjD,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YAAY,QAKT;AACD,OAAK,SAAS,IAAI,OAAO;GACvB,QAAQ,OAAO;GACf,SAAS,OAAO,WAAW;GAC3B,gBAAgB,OAAO,gBAAgB;GACxC,CAAC;AACF,OAAK,eACH,OAAO,gBAAgB;AACzB,OAAK,eAAe,OAAO,gBAAgB,EAAE;;CAG/C,kBAA0B;AACxB,SAAO,KAAK;;CAGd,MAAM,KAAK,QAMc;EACvB,MAAM,QAAQ,OAAO,SAAS,KAAK;EAEnC,MAAM,gBAAmD;GACvD,YAAY,OAAO,aAAa;GAChC,UAAU,OAAO;GACjB;GACD;AAED,MAAI,OAAO,SAAS,OAAO,MAAM,SAAS,EACxC,eAAc,QAAQ,OAAO;EAG/B,MAAM,WAAW,MAAM,KAAK,OAAO,KAAK,YAAY,OAClD,eACA,OAAO,SAAS,EAAE,QAAQ,OAAO,QAAQ,GAAG,OAC7C;EAED,MAAM,SAAS,SAAS,QAAQ;AAChC,MAAI,CAAC,OACH,QAAO;GACL,SAAS;GACT,cAAc;GACd,WAAW,EAAE;GACd;EAGH,MAAM,EAAE,YAAY;EACpB,MAAM,YAA+B,EAAE;AAEvC,MAAI,QAAQ,WACV,MAAK,MAAM,MAAM,QAAQ,YAAY;GACnC,IAAI,OAAgC,EAAE;AACtC,OAAI;AACF,WAAO,KAAK,MAAM,GAAG,SAAS,UAAU;WAClC;AAEN,WAAO,EAAE,MAAM,GAAG,SAAS,WAAW;;AAGxC,aAAU,KAAK;IACb,WAAW;IACX,IAAI,GAAG;IACP,MAAM,GAAG,SAAS;IACnB,CAAC;;AAIN,SAAO;GACL,SAAS,QAAQ,WAAW;GAC5B,cAAc,UAAU,SAAS;GACjC;GACA,OAAO,SAAS,QACZ;IACE,cAAc,SAAS,MAAM;IAC7B,kBAAkB,SAAS,MAAM;IACjC,aAAa,SAAS,MAAM;IAC7B,GACD;GACL"}
@@ -40,7 +40,7 @@ interface ProviderSpec {
40
40
  /** Strip "provider/" from model before re-prefixing (for gateways like AiHubMix) */
41
41
  readonly stripModelPrefix: boolean;
42
42
  /** Per-model param overrides, e.g. { "kimi-k2.5": { temperature: 1.0 } } */
43
- readonly modelOverrides: ReadonlyArray<readonly [pattern: string, overrides: Record<string, unknown>]>;
43
+ readonly modelOverrides: readonly (readonly [pattern: string, overrides: Record<string, unknown>])[];
44
44
  }
45
45
  declare const PROVIDERS: readonly ProviderSpec[];
46
46
  /** Match a standard provider by model-name keyword (case-insensitive).
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.mts","names":[],"sources":["../../src/providers/registry.ts"],"mappings":";;AAcA;;;;;;;;;;;;UAAiB,YAAA;EA2BN;EAAA,SAzBA,IAAA;EA+BA;EAAA,SA7BA,QAAA;EA8BG;EAAA,SA5BH,WAAA;EA4B+B;;;AAQ1C;;EAR0C,SArB/B,WAAA;EA6BsB;EAAA,SA3BtB,YAAA;EAgNK;EAAA,SA7ML,SAAA;;WAEA,OAAA;EA2M6C;EAAA,SAxM7C,iBAAA;EAyNgB;EAAA,SAvNhB,mBAAA;EA2NI;EAAA,SAzNJ,cAAA;EAuNT;EAAA,SApNS,gBAAA;EAsNR;EAAA,SAnNQ,cAAA,EAAgB,aAAA,WACb,OAAA,UAAiB,SAAA,EAAW,MAAA;AAAA;AAAA,cAQ7B,SAAA,WAAoB,YAAA;;;iBAqLjB,WAAA,CAAY,KAAA,WAAgB,YAAA;;AA4C5C;;;;;iBA3BgB,WAAA,CACd,YAAA,kBACA,MAAA,kBACA,OAAA,mBACC,YAAA;;iBAiBa,UAAA,CAAW,IAAA,WAAe,YAAA;;AA+B1C;iBAzBgB,YAAA,CACd,KAAA,UACA,OAAA,EAAS,YAAA;;iBAuBK,iBAAA,CACd,KAAA,WACC,MAAA"}
1
+ {"version":3,"file":"registry.d.mts","names":[],"sources":["../../src/providers/registry.ts"],"mappings":";;AAcA;;;;;;;;;;;;UAAiB,YAAA;EA2BN;EAAA,SAzBA,IAAA;EA+BA;EAAA,SA7BA,QAAA;EA+BP;EAAA,SA7BO,WAAA;EA6BU;;AAQrB;;;EARqB,SAtBV,WAAA;EA8BkC;EAAA,SA5BlC,YAAA;EAiNgB;EAAA,SA9MhB,SAAA;EA8MiB;EAAA,SA5MjB,OAAA;EA+NK;EAAA,SA5NL,iBAAA;;WAEA,mBAAA;EA2NT;EAAA,SAzNS,cAAA;EA2NT;EAAA,SAxNS,gBAAA;EAyNI;EAAA,SAtNJ,cAAA,sBACP,OAAA,UACA,SAAA,EAAW,MAAA;AAAA;AAAA,cAQF,SAAA,WAAoB,YAAA;AAwOjC;;AAAA,iBAnDgB,WAAA,CAAY,KAAA,WAAgB,YAAA;;;;;;;iBAmB5B,WAAA,CACd,YAAA,kBACA,MAAA,kBACA,OAAA,mBACC,YAAA;;iBAsBa,UAAA,CAAW,IAAA,WAAe,YAAA;;;iBAM1B,YAAA,CACd,KAAA,UACA,OAAA,EAAS,YAAA;;iBAwBK,iBAAA,CACd,KAAA,WACC,MAAA"}
@@ -1,49 +1,54 @@
1
1
  //#region src/providers/registry.ts
2
2
  const PROVIDERS = [
3
3
  {
4
- name: "openrouter",
5
- keywords: ["openrouter"],
4
+ defaultApiBase: "https://openrouter.ai/api/v1",
5
+ detectByBaseKeyword: "openrouter",
6
+ detectByKeyPrefix: "sk-or-",
6
7
  displayName: "OpenRouter",
7
- modelPrefix: "",
8
- skipPrefixes: [],
9
8
  isGateway: true,
10
9
  isLocal: false,
11
- detectByKeyPrefix: "sk-or-",
12
- detectByBaseKeyword: "openrouter",
13
- defaultApiBase: "https://openrouter.ai/api/v1",
14
- stripModelPrefix: false,
15
- modelOverrides: []
10
+ keywords: ["openrouter"],
11
+ modelOverrides: [],
12
+ modelPrefix: "",
13
+ name: "openrouter",
14
+ skipPrefixes: [],
15
+ stripModelPrefix: false
16
16
  },
17
17
  {
18
- name: "aihubmix",
19
- keywords: ["aihubmix"],
18
+ defaultApiBase: "https://aihubmix.com/v1",
19
+ detectByBaseKeyword: "aihubmix",
20
+ detectByKeyPrefix: "",
20
21
  displayName: "AiHubMix",
21
- modelPrefix: "",
22
- skipPrefixes: [],
23
22
  isGateway: true,
24
23
  isLocal: false,
25
- detectByKeyPrefix: "",
26
- detectByBaseKeyword: "aihubmix",
27
- defaultApiBase: "https://aihubmix.com/v1",
28
- stripModelPrefix: true,
29
- modelOverrides: []
24
+ keywords: ["aihubmix"],
25
+ modelOverrides: [],
26
+ modelPrefix: "",
27
+ name: "aihubmix",
28
+ skipPrefixes: [],
29
+ stripModelPrefix: true
30
30
  },
31
31
  {
32
- name: "anthropic",
33
- keywords: ["anthropic", "claude"],
32
+ defaultApiBase: "",
33
+ detectByBaseKeyword: "",
34
+ detectByKeyPrefix: "",
34
35
  displayName: "Anthropic",
35
- modelPrefix: "",
36
- skipPrefixes: [],
37
36
  isGateway: false,
38
37
  isLocal: false,
39
- detectByKeyPrefix: "",
40
- detectByBaseKeyword: "",
41
- defaultApiBase: "",
42
- stripModelPrefix: false,
43
- modelOverrides: []
38
+ keywords: ["anthropic", "claude"],
39
+ modelOverrides: [],
40
+ modelPrefix: "",
41
+ name: "anthropic",
42
+ skipPrefixes: [],
43
+ stripModelPrefix: false
44
44
  },
45
45
  {
46
- name: "openai",
46
+ defaultApiBase: "",
47
+ detectByBaseKeyword: "",
48
+ detectByKeyPrefix: "",
49
+ displayName: "OpenAI",
50
+ isGateway: false,
51
+ isLocal: false,
47
52
  keywords: [
48
53
  "openai",
49
54
  "gpt",
@@ -51,114 +56,109 @@ const PROVIDERS = [
51
56
  "o3",
52
57
  "o4"
53
58
  ],
54
- displayName: "OpenAI",
59
+ modelOverrides: [],
55
60
  modelPrefix: "",
61
+ name: "openai",
56
62
  skipPrefixes: [],
57
- isGateway: false,
58
- isLocal: false,
59
- detectByKeyPrefix: "",
60
- detectByBaseKeyword: "",
61
- defaultApiBase: "",
62
- stripModelPrefix: false,
63
- modelOverrides: []
63
+ stripModelPrefix: false
64
64
  },
65
65
  {
66
- name: "deepseek",
67
- keywords: ["deepseek"],
66
+ defaultApiBase: "https://api.deepseek.com/v1",
67
+ detectByBaseKeyword: "",
68
+ detectByKeyPrefix: "",
68
69
  displayName: "DeepSeek",
69
- modelPrefix: "deepseek",
70
- skipPrefixes: ["deepseek/"],
71
70
  isGateway: false,
72
71
  isLocal: false,
73
- detectByKeyPrefix: "",
74
- detectByBaseKeyword: "",
75
- defaultApiBase: "https://api.deepseek.com/v1",
76
- stripModelPrefix: false,
77
- modelOverrides: []
72
+ keywords: ["deepseek"],
73
+ modelOverrides: [],
74
+ modelPrefix: "deepseek",
75
+ name: "deepseek",
76
+ skipPrefixes: ["deepseek/"],
77
+ stripModelPrefix: false
78
78
  },
79
79
  {
80
- name: "gemini",
81
- keywords: ["gemini"],
80
+ defaultApiBase: "",
81
+ detectByBaseKeyword: "",
82
+ detectByKeyPrefix: "",
82
83
  displayName: "Gemini",
83
- modelPrefix: "gemini",
84
- skipPrefixes: ["gemini/"],
85
84
  isGateway: false,
86
85
  isLocal: false,
87
- detectByKeyPrefix: "",
88
- detectByBaseKeyword: "",
89
- defaultApiBase: "",
90
- stripModelPrefix: false,
91
- modelOverrides: []
86
+ keywords: ["gemini"],
87
+ modelOverrides: [],
88
+ modelPrefix: "gemini",
89
+ name: "gemini",
90
+ skipPrefixes: ["gemini/"],
91
+ stripModelPrefix: false
92
92
  },
93
93
  {
94
- name: "zhipu",
95
- keywords: ["zhipu", "glm"],
94
+ defaultApiBase: "https://open.bigmodel.cn/api/paas/v4",
95
+ detectByBaseKeyword: "",
96
+ detectByKeyPrefix: "",
96
97
  displayName: "Zhipu AI",
97
- modelPrefix: "",
98
- skipPrefixes: [],
99
98
  isGateway: false,
100
99
  isLocal: false,
101
- detectByKeyPrefix: "",
102
- detectByBaseKeyword: "",
103
- defaultApiBase: "https://open.bigmodel.cn/api/paas/v4",
104
- stripModelPrefix: false,
105
- modelOverrides: []
100
+ keywords: ["zhipu", "glm"],
101
+ modelOverrides: [],
102
+ modelPrefix: "",
103
+ name: "zhipu",
104
+ skipPrefixes: [],
105
+ stripModelPrefix: false
106
106
  },
107
107
  {
108
- name: "dashscope",
109
- keywords: ["qwen", "dashscope"],
108
+ defaultApiBase: "https://dashscope.aliyuncs.com/compatible-mode/v1",
109
+ detectByBaseKeyword: "",
110
+ detectByKeyPrefix: "",
110
111
  displayName: "DashScope",
111
- modelPrefix: "",
112
- skipPrefixes: [],
113
112
  isGateway: false,
114
113
  isLocal: false,
115
- detectByKeyPrefix: "",
116
- detectByBaseKeyword: "",
117
- defaultApiBase: "https://dashscope.aliyuncs.com/compatible-mode/v1",
118
- stripModelPrefix: false,
119
- modelOverrides: []
114
+ keywords: ["qwen", "dashscope"],
115
+ modelOverrides: [],
116
+ modelPrefix: "",
117
+ name: "dashscope",
118
+ skipPrefixes: [],
119
+ stripModelPrefix: false
120
120
  },
121
121
  {
122
- name: "moonshot",
123
- keywords: ["moonshot", "kimi"],
122
+ defaultApiBase: "https://api.moonshot.ai/v1",
123
+ detectByBaseKeyword: "",
124
+ detectByKeyPrefix: "",
124
125
  displayName: "Moonshot",
125
- modelPrefix: "",
126
- skipPrefixes: [],
127
126
  isGateway: false,
128
127
  isLocal: false,
129
- detectByKeyPrefix: "",
130
- detectByBaseKeyword: "",
131
- defaultApiBase: "https://api.moonshot.ai/v1",
132
- stripModelPrefix: false,
133
- modelOverrides: [["kimi-k2.5", { temperature: 1 }]]
128
+ keywords: ["moonshot", "kimi"],
129
+ modelOverrides: [["kimi-k2.5", { temperature: 1 }]],
130
+ modelPrefix: "",
131
+ name: "moonshot",
132
+ skipPrefixes: [],
133
+ stripModelPrefix: false
134
134
  },
135
135
  {
136
- name: "vllm",
137
- keywords: ["vllm"],
136
+ defaultApiBase: "",
137
+ detectByBaseKeyword: "",
138
+ detectByKeyPrefix: "",
138
139
  displayName: "vLLM/Local",
139
- modelPrefix: "",
140
- skipPrefixes: [],
141
140
  isGateway: false,
142
141
  isLocal: true,
143
- detectByKeyPrefix: "",
144
- detectByBaseKeyword: "",
145
- defaultApiBase: "",
146
- stripModelPrefix: false,
147
- modelOverrides: []
142
+ keywords: ["vllm"],
143
+ modelOverrides: [],
144
+ modelPrefix: "",
145
+ name: "vllm",
146
+ skipPrefixes: [],
147
+ stripModelPrefix: false
148
148
  },
149
149
  {
150
- name: "groq",
151
- keywords: ["groq"],
150
+ defaultApiBase: "https://api.groq.com/openai/v1",
151
+ detectByBaseKeyword: "",
152
+ detectByKeyPrefix: "",
152
153
  displayName: "Groq",
153
- modelPrefix: "",
154
- skipPrefixes: [],
155
154
  isGateway: false,
156
155
  isLocal: false,
157
- detectByKeyPrefix: "",
158
- detectByBaseKeyword: "",
159
- defaultApiBase: "https://api.groq.com/openai/v1",
160
- stripModelPrefix: false,
161
- modelOverrides: []
156
+ keywords: ["groq"],
157
+ modelOverrides: [],
158
+ modelPrefix: "",
159
+ name: "groq",
160
+ skipPrefixes: [],
161
+ stripModelPrefix: false
162
162
  }
163
163
  ];
164
164
  /** Match a standard provider by model-name keyword (case-insensitive).
@@ -1 +1 @@
1
- {"version":3,"file":"registry.mjs","names":[],"sources":["../../src/providers/registry.ts"],"sourcesContent":["/**\n * Provider Registry — single source of truth for LLM provider metadata.\n *\n * Adding a new provider:\n * 1. Add a ProviderSpec to PROVIDERS below.\n * 2. Add a field to ProvidersConfigSchema in config/schema.ts.\n * Done. Config matching, status display, model resolution all derive from here.\n *\n * Order matters — it controls match priority and fallback. Gateways first.\n *\n * Ported from the Python nanobot registry (providers/registry.py).\n * Adapted for the TS port which uses the OpenAI SDK directly (no LiteLLM).\n */\n\nexport interface ProviderSpec {\n /** Config field name, e.g. \"dashscope\" */\n readonly name: string;\n /** Model-name keywords for matching (lowercase) */\n readonly keywords: readonly string[];\n /** Display name for status output */\n readonly displayName: string;\n\n /**\n * Prefix to add to model names for API routing.\n * e.g. \"deepseek\" → model becomes \"deepseek/deepseek-chat\"\n * Empty string means no prefix needed (provider recognizes model natively).\n */\n readonly modelPrefix: string;\n /** Don't add prefix if model already starts with any of these */\n readonly skipPrefixes: readonly string[];\n\n /** Gateway: routes any model (OpenRouter, AiHubMix) */\n readonly isGateway: boolean;\n /** Local deployment (vLLM, Ollama) */\n readonly isLocal: boolean;\n\n /** Match api_key prefix, e.g. \"sk-or-\" for OpenRouter */\n readonly detectByKeyPrefix: string;\n /** Match substring in api_base URL */\n readonly detectByBaseKeyword: string;\n /** Fallback base URL for this provider */\n readonly defaultApiBase: string;\n\n /** Strip \"provider/\" from model before re-prefixing (for gateways like AiHubMix) */\n readonly stripModelPrefix: boolean;\n\n /** Per-model param overrides, e.g. { \"kimi-k2.5\": { temperature: 1.0 } } */\n readonly modelOverrides: ReadonlyArray<\n readonly [pattern: string, overrides: Record<string, unknown>]\n >;\n}\n\n// ---------------------------------------------------------------------------\n// PROVIDERS — the registry. Order = priority.\n// ---------------------------------------------------------------------------\n\nexport const PROVIDERS: readonly ProviderSpec[] = [\n // === Gateways =========================================================\n\n {\n name: \"openrouter\",\n keywords: [\"openrouter\"],\n displayName: \"OpenRouter\",\n modelPrefix: \"\",\n skipPrefixes: [],\n isGateway: true,\n isLocal: false,\n detectByKeyPrefix: \"sk-or-\",\n detectByBaseKeyword: \"openrouter\",\n defaultApiBase: \"https://openrouter.ai/api/v1\",\n stripModelPrefix: false,\n modelOverrides: [],\n },\n\n {\n name: \"aihubmix\",\n keywords: [\"aihubmix\"],\n displayName: \"AiHubMix\",\n modelPrefix: \"\",\n skipPrefixes: [],\n isGateway: true,\n isLocal: false,\n detectByKeyPrefix: \"\",\n detectByBaseKeyword: \"aihubmix\",\n defaultApiBase: \"https://aihubmix.com/v1\",\n stripModelPrefix: true,\n modelOverrides: [],\n },\n\n // === Standard providers ================================================\n\n {\n name: \"anthropic\",\n keywords: [\"anthropic\", \"claude\"],\n displayName: \"Anthropic\",\n modelPrefix: \"\",\n skipPrefixes: [],\n isGateway: false,\n isLocal: false,\n detectByKeyPrefix: \"\",\n detectByBaseKeyword: \"\",\n defaultApiBase: \"\",\n stripModelPrefix: false,\n modelOverrides: [],\n },\n\n {\n name: \"openai\",\n keywords: [\"openai\", \"gpt\", \"o1\", \"o3\", \"o4\"],\n displayName: \"OpenAI\",\n modelPrefix: \"\",\n skipPrefixes: [],\n isGateway: false,\n isLocal: false,\n detectByKeyPrefix: \"\",\n detectByBaseKeyword: \"\",\n defaultApiBase: \"\",\n stripModelPrefix: false,\n modelOverrides: [],\n },\n\n {\n name: \"deepseek\",\n keywords: [\"deepseek\"],\n displayName: \"DeepSeek\",\n modelPrefix: \"deepseek\",\n skipPrefixes: [\"deepseek/\"],\n isGateway: false,\n isLocal: false,\n detectByKeyPrefix: \"\",\n detectByBaseKeyword: \"\",\n defaultApiBase: \"https://api.deepseek.com/v1\",\n stripModelPrefix: false,\n modelOverrides: [],\n },\n\n {\n name: \"gemini\",\n keywords: [\"gemini\"],\n displayName: \"Gemini\",\n modelPrefix: \"gemini\",\n skipPrefixes: [\"gemini/\"],\n isGateway: false,\n isLocal: false,\n detectByKeyPrefix: \"\",\n detectByBaseKeyword: \"\",\n defaultApiBase: \"\",\n stripModelPrefix: false,\n modelOverrides: [],\n },\n\n {\n name: \"zhipu\",\n keywords: [\"zhipu\", \"glm\"],\n displayName: \"Zhipu AI\",\n modelPrefix: \"\",\n skipPrefixes: [],\n isGateway: false,\n isLocal: false,\n detectByKeyPrefix: \"\",\n detectByBaseKeyword: \"\",\n defaultApiBase: \"https://open.bigmodel.cn/api/paas/v4\",\n stripModelPrefix: false,\n modelOverrides: [],\n },\n\n {\n name: \"dashscope\",\n keywords: [\"qwen\", \"dashscope\"],\n displayName: \"DashScope\",\n modelPrefix: \"\",\n skipPrefixes: [],\n isGateway: false,\n isLocal: false,\n detectByKeyPrefix: \"\",\n detectByBaseKeyword: \"\",\n defaultApiBase: \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n stripModelPrefix: false,\n modelOverrides: [],\n },\n\n {\n name: \"moonshot\",\n keywords: [\"moonshot\", \"kimi\"],\n displayName: \"Moonshot\",\n modelPrefix: \"\",\n skipPrefixes: [],\n isGateway: false,\n isLocal: false,\n detectByKeyPrefix: \"\",\n detectByBaseKeyword: \"\",\n defaultApiBase: \"https://api.moonshot.ai/v1\",\n stripModelPrefix: false,\n modelOverrides: [[\"kimi-k2.5\", { temperature: 1.0 }]],\n },\n\n // === Local deployment ==================================================\n\n {\n name: \"vllm\",\n keywords: [\"vllm\"],\n displayName: \"vLLM/Local\",\n modelPrefix: \"\",\n skipPrefixes: [],\n isGateway: false,\n isLocal: true,\n detectByKeyPrefix: \"\",\n detectByBaseKeyword: \"\",\n defaultApiBase: \"\",\n stripModelPrefix: false,\n modelOverrides: [],\n },\n\n // === Auxiliary ==========================================================\n\n {\n name: \"groq\",\n keywords: [\"groq\"],\n displayName: \"Groq\",\n modelPrefix: \"\",\n skipPrefixes: [],\n isGateway: false,\n isLocal: false,\n detectByKeyPrefix: \"\",\n detectByBaseKeyword: \"\",\n defaultApiBase: \"https://api.groq.com/openai/v1\",\n stripModelPrefix: false,\n modelOverrides: [],\n },\n];\n\n// ---------------------------------------------------------------------------\n// Lookup helpers\n// ---------------------------------------------------------------------------\n\n/** Match a standard provider by model-name keyword (case-insensitive).\n * Skips gateways/local — those are matched by api_key/api_base instead. */\nexport function findByModel(model: string): ProviderSpec | null {\n const lower = model.toLowerCase();\n for (const spec of PROVIDERS) {\n if (spec.isGateway || spec.isLocal) continue;\n if (spec.keywords.some((kw) => lower.includes(kw))) {\n return spec;\n }\n }\n return null;\n}\n\n/** Detect gateway/local provider.\n *\n * Priority:\n * 1. providerName — if it maps to a gateway/local spec, use it directly.\n * 2. apiKey prefix — e.g. \"sk-or-\" → OpenRouter.\n * 3. apiBase keyword — e.g. \"aihubmix\" in URL → AiHubMix. */\nexport function findGateway(\n providerName?: string | null,\n apiKey?: string | null,\n apiBase?: string | null,\n): ProviderSpec | null {\n if (providerName) {\n const spec = findByName(providerName);\n if (spec && (spec.isGateway || spec.isLocal)) return spec;\n }\n for (const spec of PROVIDERS) {\n if (spec.detectByKeyPrefix && apiKey?.startsWith(spec.detectByKeyPrefix)) {\n return spec;\n }\n if (spec.detectByBaseKeyword && apiBase?.includes(spec.detectByBaseKeyword)) {\n return spec;\n }\n }\n return null;\n}\n\n/** Find a provider spec by config field name, e.g. \"dashscope\". */\nexport function findByName(name: string): ProviderSpec | null {\n return PROVIDERS.find((s) => s.name === name) ?? null;\n}\n\n/** Resolve model name: strip gateway prefix if needed, apply provider prefix.\n * Returns the model string the OpenAI SDK should send. */\nexport function resolveModel(\n model: string,\n gateway: ProviderSpec | null,\n): string {\n if (gateway) {\n if (gateway.stripModelPrefix) {\n model = model.split(\"/\").pop() ?? model;\n }\n const prefix = gateway.modelPrefix;\n if (prefix && !model.startsWith(`${prefix}/`)) {\n return `${prefix}/${model}`;\n }\n return model;\n }\n\n const spec = findByModel(model);\n if (spec?.modelPrefix) {\n if (!spec.skipPrefixes.some((p) => model.startsWith(p))) {\n return `${spec.modelPrefix}/${model}`;\n }\n }\n return model;\n}\n\n/** Get model-specific parameter overrides (e.g. temperature for kimi-k2.5). */\nexport function getModelOverrides(\n model: string,\n): Record<string, unknown> | null {\n const lower = model.toLowerCase();\n const spec = findByModel(model);\n if (!spec) return null;\n for (const [pattern, overrides] of spec.modelOverrides) {\n if (lower.includes(pattern)) return { ...overrides };\n }\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"registry.mjs","names":[],"sources":["../../src/providers/registry.ts"],"sourcesContent":["/**\n * Provider Registry — single source of truth for LLM provider metadata.\n *\n * Adding a new provider:\n * 1. Add a ProviderSpec to PROVIDERS below.\n * 2. Add a field to ProvidersConfigSchema in config/schema.ts.\n * Done. Config matching, status display, model resolution all derive from here.\n *\n * Order matters — it controls match priority and fallback. Gateways first.\n *\n * Ported from the Python nanobot registry (providers/registry.py).\n * Adapted for the TS port which uses the OpenAI SDK directly (no LiteLLM).\n */\n\nexport interface ProviderSpec {\n /** Config field name, e.g. \"dashscope\" */\n readonly name: string;\n /** Model-name keywords for matching (lowercase) */\n readonly keywords: readonly string[];\n /** Display name for status output */\n readonly displayName: string;\n\n /**\n * Prefix to add to model names for API routing.\n * e.g. \"deepseek\" → model becomes \"deepseek/deepseek-chat\"\n * Empty string means no prefix needed (provider recognizes model natively).\n */\n readonly modelPrefix: string;\n /** Don't add prefix if model already starts with any of these */\n readonly skipPrefixes: readonly string[];\n\n /** Gateway: routes any model (OpenRouter, AiHubMix) */\n readonly isGateway: boolean;\n /** Local deployment (vLLM, Ollama) */\n readonly isLocal: boolean;\n\n /** Match api_key prefix, e.g. \"sk-or-\" for OpenRouter */\n readonly detectByKeyPrefix: string;\n /** Match substring in api_base URL */\n readonly detectByBaseKeyword: string;\n /** Fallback base URL for this provider */\n readonly defaultApiBase: string;\n\n /** Strip \"provider/\" from model before re-prefixing (for gateways like AiHubMix) */\n readonly stripModelPrefix: boolean;\n\n /** Per-model param overrides, e.g. { \"kimi-k2.5\": { temperature: 1.0 } } */\n readonly modelOverrides: readonly (readonly [\n pattern: string,\n overrides: Record<string, unknown>,\n ])[];\n}\n\n// ---------------------------------------------------------------------------\n// PROVIDERS — the registry. Order = priority.\n// ---------------------------------------------------------------------------\n\nexport const PROVIDERS: readonly ProviderSpec[] = [\n // === Gateways =========================================================\n\n {\n defaultApiBase: \"https://openrouter.ai/api/v1\",\n detectByBaseKeyword: \"openrouter\",\n detectByKeyPrefix: \"sk-or-\",\n displayName: \"OpenRouter\",\n isGateway: true,\n isLocal: false,\n keywords: [\"openrouter\"],\n modelOverrides: [],\n modelPrefix: \"\",\n name: \"openrouter\",\n skipPrefixes: [],\n stripModelPrefix: false,\n },\n\n {\n defaultApiBase: \"https://aihubmix.com/v1\",\n detectByBaseKeyword: \"aihubmix\",\n detectByKeyPrefix: \"\",\n displayName: \"AiHubMix\",\n isGateway: true,\n isLocal: false,\n keywords: [\"aihubmix\"],\n modelOverrides: [],\n modelPrefix: \"\",\n name: \"aihubmix\",\n skipPrefixes: [],\n stripModelPrefix: true,\n },\n\n // === Standard providers ================================================\n\n {\n defaultApiBase: \"\",\n detectByBaseKeyword: \"\",\n detectByKeyPrefix: \"\",\n displayName: \"Anthropic\",\n isGateway: false,\n isLocal: false,\n keywords: [\"anthropic\", \"claude\"],\n modelOverrides: [],\n modelPrefix: \"\",\n name: \"anthropic\",\n skipPrefixes: [],\n stripModelPrefix: false,\n },\n\n {\n defaultApiBase: \"\",\n detectByBaseKeyword: \"\",\n detectByKeyPrefix: \"\",\n displayName: \"OpenAI\",\n isGateway: false,\n isLocal: false,\n keywords: [\"openai\", \"gpt\", \"o1\", \"o3\", \"o4\"],\n modelOverrides: [],\n modelPrefix: \"\",\n name: \"openai\",\n skipPrefixes: [],\n stripModelPrefix: false,\n },\n\n {\n defaultApiBase: \"https://api.deepseek.com/v1\",\n detectByBaseKeyword: \"\",\n detectByKeyPrefix: \"\",\n displayName: \"DeepSeek\",\n isGateway: false,\n isLocal: false,\n keywords: [\"deepseek\"],\n modelOverrides: [],\n modelPrefix: \"deepseek\",\n name: \"deepseek\",\n skipPrefixes: [\"deepseek/\"],\n stripModelPrefix: false,\n },\n\n {\n defaultApiBase: \"\",\n detectByBaseKeyword: \"\",\n detectByKeyPrefix: \"\",\n displayName: \"Gemini\",\n isGateway: false,\n isLocal: false,\n keywords: [\"gemini\"],\n modelOverrides: [],\n modelPrefix: \"gemini\",\n name: \"gemini\",\n skipPrefixes: [\"gemini/\"],\n stripModelPrefix: false,\n },\n\n {\n defaultApiBase: \"https://open.bigmodel.cn/api/paas/v4\",\n detectByBaseKeyword: \"\",\n detectByKeyPrefix: \"\",\n displayName: \"Zhipu AI\",\n isGateway: false,\n isLocal: false,\n keywords: [\"zhipu\", \"glm\"],\n modelOverrides: [],\n modelPrefix: \"\",\n name: \"zhipu\",\n skipPrefixes: [],\n stripModelPrefix: false,\n },\n\n {\n defaultApiBase: \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n detectByBaseKeyword: \"\",\n detectByKeyPrefix: \"\",\n displayName: \"DashScope\",\n isGateway: false,\n isLocal: false,\n keywords: [\"qwen\", \"dashscope\"],\n modelOverrides: [],\n modelPrefix: \"\",\n name: \"dashscope\",\n skipPrefixes: [],\n stripModelPrefix: false,\n },\n\n {\n defaultApiBase: \"https://api.moonshot.ai/v1\",\n detectByBaseKeyword: \"\",\n detectByKeyPrefix: \"\",\n displayName: \"Moonshot\",\n isGateway: false,\n isLocal: false,\n keywords: [\"moonshot\", \"kimi\"],\n modelOverrides: [[\"kimi-k2.5\", { temperature: 1 }]],\n modelPrefix: \"\",\n name: \"moonshot\",\n skipPrefixes: [],\n stripModelPrefix: false,\n },\n\n // === Local deployment ==================================================\n\n {\n defaultApiBase: \"\",\n detectByBaseKeyword: \"\",\n detectByKeyPrefix: \"\",\n displayName: \"vLLM/Local\",\n isGateway: false,\n isLocal: true,\n keywords: [\"vllm\"],\n modelOverrides: [],\n modelPrefix: \"\",\n name: \"vllm\",\n skipPrefixes: [],\n stripModelPrefix: false,\n },\n\n // === Auxiliary ==========================================================\n\n {\n defaultApiBase: \"https://api.groq.com/openai/v1\",\n detectByBaseKeyword: \"\",\n detectByKeyPrefix: \"\",\n displayName: \"Groq\",\n isGateway: false,\n isLocal: false,\n keywords: [\"groq\"],\n modelOverrides: [],\n modelPrefix: \"\",\n name: \"groq\",\n skipPrefixes: [],\n stripModelPrefix: false,\n },\n];\n\n// ---------------------------------------------------------------------------\n// Lookup helpers\n// ---------------------------------------------------------------------------\n\n/** Match a standard provider by model-name keyword (case-insensitive).\n * Skips gateways/local — those are matched by api_key/api_base instead. */\nexport function findByModel(model: string): ProviderSpec | null {\n const lower = model.toLowerCase();\n for (const spec of PROVIDERS) {\n if (spec.isGateway || spec.isLocal) {\n continue;\n }\n if (spec.keywords.some((kw) => lower.includes(kw))) {\n return spec;\n }\n }\n return null;\n}\n\n/** Detect gateway/local provider.\n *\n * Priority:\n * 1. providerName — if it maps to a gateway/local spec, use it directly.\n * 2. apiKey prefix — e.g. \"sk-or-\" → OpenRouter.\n * 3. apiBase keyword — e.g. \"aihubmix\" in URL → AiHubMix. */\nexport function findGateway(\n providerName?: string | null,\n apiKey?: string | null,\n apiBase?: string | null\n): ProviderSpec | null {\n if (providerName) {\n const spec = findByName(providerName);\n if (spec && (spec.isGateway || spec.isLocal)) {\n return spec;\n }\n }\n for (const spec of PROVIDERS) {\n if (spec.detectByKeyPrefix && apiKey?.startsWith(spec.detectByKeyPrefix)) {\n return spec;\n }\n if (\n spec.detectByBaseKeyword &&\n apiBase?.includes(spec.detectByBaseKeyword)\n ) {\n return spec;\n }\n }\n return null;\n}\n\n/** Find a provider spec by config field name, e.g. \"dashscope\". */\nexport function findByName(name: string): ProviderSpec | null {\n return PROVIDERS.find((s) => s.name === name) ?? null;\n}\n\n/** Resolve model name: strip gateway prefix if needed, apply provider prefix.\n * Returns the model string the OpenAI SDK should send. */\nexport function resolveModel(\n model: string,\n gateway: ProviderSpec | null\n): string {\n if (gateway) {\n if (gateway.stripModelPrefix) {\n model = model.split(\"/\").pop() ?? model;\n }\n const prefix = gateway.modelPrefix;\n if (prefix && !model.startsWith(`${prefix}/`)) {\n return `${prefix}/${model}`;\n }\n return model;\n }\n\n const spec = findByModel(model);\n if (spec?.modelPrefix) {\n // oxlint-disable-next-line unicorn/no-lonely-if\n if (!spec.skipPrefixes.some((p) => model.startsWith(p))) {\n return `${spec.modelPrefix}/${model}`;\n }\n }\n return model;\n}\n\n/** Get model-specific parameter overrides (e.g. temperature for kimi-k2.5). */\nexport function getModelOverrides(\n model: string\n): Record<string, unknown> | null {\n const lower = model.toLowerCase();\n const spec = findByModel(model);\n if (!spec) {\n return null;\n }\n for (const [pattern, overrides] of spec.modelOverrides) {\n if (lower.includes(pattern)) {\n return { ...overrides };\n }\n }\n return null;\n}\n"],"mappings":"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"}
@@ -49,12 +49,12 @@ declare class SessionManager {
49
49
  /** Delete a session. */
50
50
  delete(key: string): boolean;
51
51
  /** List all sessions. */
52
- listSessions(): Array<{
52
+ listSessions(): {
53
53
  key: string;
54
54
  createdAt?: string;
55
55
  updatedAt?: string;
56
56
  path: string;
57
- }>;
57
+ }[];
58
58
  }
59
59
  //#endregion
60
60
  export { Session, SessionManager, SessionMessage };
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.mts","names":[],"sources":["../../src/session/manager.ts"],"mappings":";;;;UAaiB,cAAA;EACf,IAAA;EACA,OAAA;EACA,SAAA;EAAA,CACC,GAAA;AAAA;;cAIU,OAAA;EACX,GAAA;EALY;EAOZ,OAAA,EAAS,WAAA;EACT,SAAA,EAAW,IAAA;EACX,SAAA,EAAW,IAAA;EACX,QAAA,EAAU,MAAA;EAHD;EAKT,SAAA;cAEY,MAAA;IACV,GAAA;IACA,OAAA,GAAU,WAAA;IACV,SAAA,GAAY,IAAA;IACZ,SAAA,GAAY,IAAA;IACZ,QAAA,GAAW,MAAA;IACX,SAAA;EAAA;EAqBwC;EAV1C,eAAA,CAAgB,QAAA,EAAU,WAAA,IAAe,SAAA;EA1BzC;EAoCA,UAAA,CAAW,WAAA,YAAoB,WAAA;EAlCtB;EA4CT,WAAA,CAAY,SAAA;EA3CD;EAkDX,KAAA,CAAA;AAAA;;cAOW,cAAA;EAAA,QACH,WAAA;EAAA,QACA,KAAA;cAEI,UAAA;EAAA,QAIJ,cAAA;EAzDI;EA+DZ,WAAA,CAAY,GAAA,WAAc,OAAA;EAAA,QAelB,IAAA;EA5EN;EA0HF,IAAA,CAAK,OAAA,EAAS,OAAA;EAzHZ;EAkJF,MAAA,CAAO,GAAA;EAjJL;EA4JF,YAAA,CAAA,GAAgB,KAAA;IACd,GAAA;IACA,SAAA;IACA,SAAA;IACA,IAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"manager.d.mts","names":[],"sources":["../../src/session/manager.ts"],"mappings":";;;;UAeiB,cAAA;EACf,IAAA;EACA,OAAA;EACA,SAAA;EAAA,CACC,GAAA;AAAA;;cAIU,OAAA;EACX,GAAA;EALY;EAOZ,OAAA,EAAS,WAAA;EACT,SAAA,EAAW,IAAA;EACX,SAAA,EAAW,IAAA;EACX,QAAA,EAAU,MAAA;EAHD;EAKT,SAAA;cAEY,MAAA;IACV,GAAA;IACA,OAAA,GAAU,WAAA;IACV,SAAA,GAAY,IAAA;IACZ,SAAA,GAAY,IAAA;IACZ,QAAA,GAAW,MAAA;IACX,SAAA;EAAA;EAuBwC;EAZ1C,eAAA,CAAgB,QAAA,EAAU,WAAA,IAAe,SAAA;EA1BzC;EAsCA,UAAA,CAAW,WAAA,YAAoB,WAAA;EApCtB;EAgDT,WAAA,CAAY,SAAA;EA/CD;EAsDX,KAAA,CAAA;AAAA;;cAOW,cAAA;EAAA,QACH,WAAA;EAAA,QACA,KAAA;cAEI,UAAA;EAAA,QAIJ,cAAA;EA7DI;EAmEZ,WAAA,CAAY,GAAA,WAAc,OAAA;EAAA,QAiBlB,IAAA;EAlFN;EAgIF,IAAA,CAAK,OAAA,EAAS,OAAA;EA/HZ;EAqJF,MAAA,CAAO,GAAA;EApJL;EA+JF,YAAA,CAAA;IACE,GAAA;IACA,SAAA;IACA,SAAA;IACA,IAAA;EAAA;AAAA"}
@@ -35,7 +35,7 @@ var Session = class {
35
35
  getHistory(maxMessages = 200) {
36
36
  if (this.history.length <= maxMessages) return this.history;
37
37
  let start = this.history.length - maxMessages;
38
- while (start < this.history.length && this.history[start].role !== "user") start++;
38
+ while (start < this.history.length && this.history[start].role !== "user") start += 1;
39
39
  return this.history.slice(start);
40
40
  }
41
41
  /** Trim history to keep only the last N messages. */
@@ -56,7 +56,7 @@ var SessionManager = class {
56
56
  this.sessionsDir = ensureDir(join(homedir(), ".nanobot", "sessions"));
57
57
  }
58
58
  getSessionPath(key) {
59
- const safeKey = safeFilename(key.replace(/:/g, "_"));
59
+ const safeKey = safeFilename(key.replaceAll(":", "_"));
60
60
  return join(this.sessionsDir, `${safeKey}.jsonl`);
61
61
  }
62
62
  /** Get an existing session or create a new one. */
@@ -76,7 +76,7 @@ var SessionManager = class {
76
76
  const path = this.getSessionPath(key);
77
77
  if (!existsSync(path)) return null;
78
78
  try {
79
- const lines = readFileSync(path, "utf-8").split("\n").filter((l) => l.trim());
79
+ const lines = readFileSync(path, "utf8").split("\n").filter((l) => l.trim());
80
80
  const history = [];
81
81
  let metadata = {};
82
82
  let createdAt;
@@ -89,32 +89,31 @@ var SessionManager = class {
89
89
  isRichFormat = data._format === "rich";
90
90
  } else if (isRichFormat) history.push(data);
91
91
  else history.push({
92
- role: data.role,
93
- content: data.content ?? ""
92
+ content: data.content ?? "",
93
+ role: data.role
94
94
  });
95
95
  }
96
96
  return new Session({
97
- key,
98
- history,
99
97
  createdAt: createdAt ?? /* @__PURE__ */ new Date(),
98
+ history,
99
+ key,
100
100
  metadata
101
101
  });
102
- } catch (err) {
103
- console.warn(`Failed to load session ${key}:`, err);
102
+ } catch (error) {
103
+ console.warn(`Failed to load session ${key}:`, error);
104
104
  return null;
105
105
  }
106
106
  }
107
107
  /** Save a session to disk. */
108
108
  save(session) {
109
109
  const path = this.getSessionPath(session.key);
110
- const lines = [];
111
- lines.push(JSON.stringify({
112
- _type: "metadata",
110
+ const lines = [JSON.stringify({
113
111
  _format: "rich",
112
+ _type: "metadata",
114
113
  created_at: session.createdAt.toISOString(),
115
- updated_at: session.updatedAt.toISOString(),
116
- metadata: session.metadata
117
- }));
114
+ metadata: session.metadata,
115
+ updated_at: session.updatedAt.toISOString()
116
+ })];
118
117
  for (const msg of session.history) lines.push(JSON.stringify(msg));
119
118
  writeFileSync(path, lines.join("\n") + "\n");
120
119
  this.cache.set(session.key, session);
@@ -137,14 +136,14 @@ var SessionManager = class {
137
136
  if (!file.endsWith(".jsonl")) continue;
138
137
  const filePath = join(this.sessionsDir, file);
139
138
  try {
140
- const firstLine = readFileSync(filePath, "utf-8").split("\n")[0]?.trim();
139
+ const firstLine = readFileSync(filePath, "utf8").split("\n")[0]?.trim();
141
140
  if (firstLine) {
142
141
  const data = JSON.parse(firstLine);
143
142
  if (data._type === "metadata") results.push({
144
- key: file.replace(".jsonl", "").replace(/_/g, ":"),
145
143
  createdAt: data.created_at,
146
- updatedAt: data.updated_at,
147
- path: filePath
144
+ key: file.replace(".jsonl", "").replaceAll("_", ":"),
145
+ path: filePath,
146
+ updatedAt: data.updated_at
148
147
  });
149
148
  }
150
149
  } catch {}
@@ -1 +1 @@
1
- {"version":3,"file":"manager.mjs","names":[],"sources":["../../src/session/manager.ts"],"sourcesContent":["import {\n readFileSync,\n writeFileSync,\n existsSync,\n readdirSync,\n unlinkSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { ensureDir, safeFilename } from \"../utils/helpers.js\";\nimport { homedir } from \"node:os\";\nimport type { ChatMessage } from \"../providers/base.js\";\n\n/** A conversation message (legacy simple format, kept for JSONL compat). */\nexport interface SessionMessage {\n role: string;\n content: string;\n timestamp: string;\n [key: string]: unknown;\n}\n\n/** A conversation session. */\nexport class Session {\n key: string;\n /** Rich message history preserving tool_calls, tool results, etc. */\n history: ChatMessage[];\n createdAt: Date;\n updatedAt: Date;\n metadata: Record<string, unknown>;\n /** Tools used in the last turn (for memory consolidation). */\n toolsUsed: string[];\n\n constructor(params: {\n key: string;\n history?: ChatMessage[];\n createdAt?: Date;\n updatedAt?: Date;\n metadata?: Record<string, unknown>;\n toolsUsed?: string[];\n }) {\n this.key = params.key;\n this.history = params.history ?? [];\n this.createdAt = params.createdAt ?? new Date();\n this.updatedAt = params.updatedAt ?? new Date();\n this.metadata = params.metadata ?? {};\n this.toolsUsed = params.toolsUsed ?? [];\n }\n\n /** Append full ChatMessage entries from an agent loop turn (excluding system prompt). */\n addTurnMessages(messages: ChatMessage[], toolsUsed?: string[]): void {\n for (const msg of messages) {\n if (msg.role === \"system\") continue;\n this.history.push(msg);\n }\n this.toolsUsed = toolsUsed ?? [];\n this.updatedAt = new Date();\n }\n\n /** Get the full rich history for replaying into the LLM. */\n getHistory(maxMessages = 200): ChatMessage[] {\n if (this.history.length <= maxMessages) return this.history;\n let start = this.history.length - maxMessages;\n while (start < this.history.length && this.history[start].role !== \"user\") {\n start++;\n }\n return this.history.slice(start);\n }\n\n /** Trim history to keep only the last N messages. */\n trimHistory(keepCount: number): void {\n if (this.history.length > keepCount) {\n this.history = this.history.slice(-keepCount);\n }\n }\n\n /** Clear all messages. */\n clear(): void {\n this.history = [];\n this.updatedAt = new Date();\n }\n}\n\n/** Manages conversation sessions stored as JSONL files. */\nexport class SessionManager {\n private sessionsDir: string;\n private cache = new Map<string, Session>();\n\n constructor(_workspace: string) {\n this.sessionsDir = ensureDir(join(homedir(), \".nanobot\", \"sessions\"));\n }\n\n private getSessionPath(key: string): string {\n const safeKey = safeFilename(key.replace(/:/g, \"_\"));\n return join(this.sessionsDir, `${safeKey}.jsonl`);\n }\n\n /** Get an existing session or create a new one. */\n getOrCreate(key: string): Session {\n const cached = this.cache.get(key);\n if (cached) return cached;\n\n const loaded = this.load(key);\n if (loaded) {\n this.cache.set(key, loaded);\n return loaded;\n }\n\n const session = new Session({ key });\n this.cache.set(key, session);\n return session;\n }\n\n private load(key: string): Session | null {\n const path = this.getSessionPath(key);\n if (!existsSync(path)) return null;\n\n try {\n const raw = readFileSync(path, \"utf-8\");\n const lines = raw.split(\"\\n\").filter((l) => l.trim());\n const history: ChatMessage[] = [];\n let metadata: Record<string, unknown> = {};\n let createdAt: Date | undefined;\n let isRichFormat = false;\n\n for (const line of lines) {\n const data = JSON.parse(line);\n if (data._type === \"metadata\") {\n metadata = data.metadata ?? {};\n createdAt = data.created_at\n ? new Date(data.created_at)\n : undefined;\n isRichFormat = data._format === \"rich\";\n } else if (isRichFormat) {\n // Rich format: data is a full ChatMessage\n history.push(data as ChatMessage);\n } else {\n // Legacy format: simple {role, content, timestamp} entries.\n // Convert to ChatMessage, skipping the timestamp field.\n history.push({\n role: data.role as ChatMessage[\"role\"],\n content: data.content ?? \"\",\n });\n }\n }\n\n return new Session({\n key,\n history,\n createdAt: createdAt ?? new Date(),\n metadata,\n });\n } catch (err) {\n console.warn(`Failed to load session ${key}:`, err);\n return null;\n }\n }\n\n /** Save a session to disk. */\n save(session: Session): void {\n const path = this.getSessionPath(session.key);\n const lines: string[] = [];\n\n // Metadata line first (mark as rich format)\n lines.push(\n JSON.stringify({\n _type: \"metadata\",\n _format: \"rich\",\n created_at: session.createdAt.toISOString(),\n updated_at: session.updatedAt.toISOString(),\n metadata: session.metadata,\n }),\n );\n\n // Message lines — full ChatMessage objects\n for (const msg of session.history) {\n lines.push(JSON.stringify(msg));\n }\n\n writeFileSync(path, lines.join(\"\\n\") + \"\\n\");\n this.cache.set(session.key, session);\n }\n\n /** Delete a session. */\n delete(key: string): boolean {\n this.cache.delete(key);\n const path = this.getSessionPath(key);\n if (existsSync(path)) {\n unlinkSync(path);\n return true;\n }\n return false;\n }\n\n /** List all sessions. */\n listSessions(): Array<{\n key: string;\n createdAt?: string;\n updatedAt?: string;\n path: string;\n }> {\n const results: Array<{\n key: string;\n createdAt?: string;\n updatedAt?: string;\n path: string;\n }> = [];\n\n if (!existsSync(this.sessionsDir)) return results;\n\n for (const file of readdirSync(this.sessionsDir)) {\n if (!file.endsWith(\".jsonl\")) continue;\n const filePath = join(this.sessionsDir, file);\n try {\n const raw = readFileSync(filePath, \"utf-8\");\n const firstLine = raw.split(\"\\n\")[0]?.trim();\n if (firstLine) {\n const data = JSON.parse(firstLine);\n if (data._type === \"metadata\") {\n results.push({\n key: file.replace(\".jsonl\", \"\").replace(/_/g, \":\"),\n createdAt: data.created_at,\n updatedAt: data.updated_at,\n path: filePath,\n });\n }\n }\n } catch {\n // skip invalid files\n }\n }\n\n return results.sort(\n (a, b) => (b.updatedAt ?? \"\").localeCompare(a.updatedAt ?? \"\"),\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"manager.mjs","names":[],"sources":["../../src/session/manager.ts"],"sourcesContent":["import {\n readFileSync,\n writeFileSync,\n existsSync,\n readdirSync,\n unlinkSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport type { ChatMessage } from \"../providers/base.js\";\n\nimport { ensureDir, safeFilename } from \"../utils/helpers.js\";\n\n/** A conversation message (legacy simple format, kept for JSONL compat). */\nexport interface SessionMessage {\n role: string;\n content: string;\n timestamp: string;\n [key: string]: unknown;\n}\n\n/** A conversation session. */\nexport class Session {\n key: string;\n /** Rich message history preserving tool_calls, tool results, etc. */\n history: ChatMessage[];\n createdAt: Date;\n updatedAt: Date;\n metadata: Record<string, unknown>;\n /** Tools used in the last turn (for memory consolidation). */\n toolsUsed: string[];\n\n constructor(params: {\n key: string;\n history?: ChatMessage[];\n createdAt?: Date;\n updatedAt?: Date;\n metadata?: Record<string, unknown>;\n toolsUsed?: string[];\n }) {\n this.key = params.key;\n this.history = params.history ?? [];\n this.createdAt = params.createdAt ?? new Date();\n this.updatedAt = params.updatedAt ?? new Date();\n this.metadata = params.metadata ?? {};\n this.toolsUsed = params.toolsUsed ?? [];\n }\n\n /** Append full ChatMessage entries from an agent loop turn (excluding system prompt). */\n addTurnMessages(messages: ChatMessage[], toolsUsed?: string[]): void {\n for (const msg of messages) {\n if (msg.role === \"system\") {\n continue;\n }\n this.history.push(msg);\n }\n this.toolsUsed = toolsUsed ?? [];\n this.updatedAt = new Date();\n }\n\n /** Get the full rich history for replaying into the LLM. */\n getHistory(maxMessages = 200): ChatMessage[] {\n if (this.history.length <= maxMessages) {\n return this.history;\n }\n let start = this.history.length - maxMessages;\n while (start < this.history.length && this.history[start].role !== \"user\") {\n start += 1;\n }\n return this.history.slice(start);\n }\n\n /** Trim history to keep only the last N messages. */\n trimHistory(keepCount: number): void {\n if (this.history.length > keepCount) {\n this.history = this.history.slice(-keepCount);\n }\n }\n\n /** Clear all messages. */\n clear(): void {\n this.history = [];\n this.updatedAt = new Date();\n }\n}\n\n/** Manages conversation sessions stored as JSONL files. */\nexport class SessionManager {\n private sessionsDir: string;\n private cache = new Map<string, Session>();\n\n constructor(_workspace: string) {\n this.sessionsDir = ensureDir(join(homedir(), \".nanobot\", \"sessions\"));\n }\n\n private getSessionPath(key: string): string {\n const safeKey = safeFilename(key.replaceAll(\":\", \"_\"));\n return join(this.sessionsDir, `${safeKey}.jsonl`);\n }\n\n /** Get an existing session or create a new one. */\n getOrCreate(key: string): Session {\n const cached = this.cache.get(key);\n if (cached) {\n return cached;\n }\n\n const loaded = this.load(key);\n if (loaded) {\n this.cache.set(key, loaded);\n return loaded;\n }\n\n const session = new Session({ key });\n this.cache.set(key, session);\n return session;\n }\n\n private load(key: string): Session | null {\n const path = this.getSessionPath(key);\n if (!existsSync(path)) {\n return null;\n }\n\n try {\n const raw = readFileSync(path, \"utf8\");\n const lines = raw.split(\"\\n\").filter((l) => l.trim());\n const history: ChatMessage[] = [];\n let metadata: Record<string, unknown> = {};\n let createdAt: Date | undefined;\n let isRichFormat = false;\n\n for (const line of lines) {\n const data = JSON.parse(line);\n if (data._type === \"metadata\") {\n metadata = data.metadata ?? {};\n createdAt = data.created_at ? new Date(data.created_at) : undefined;\n isRichFormat = data._format === \"rich\";\n } else if (isRichFormat) {\n // Rich format: data is a full ChatMessage\n history.push(data as ChatMessage);\n } else {\n // Legacy format: simple {role, content, timestamp} entries.\n // Convert to ChatMessage, skipping the timestamp field.\n history.push({\n content: data.content ?? \"\",\n role: data.role as ChatMessage[\"role\"],\n });\n }\n }\n\n return new Session({\n createdAt: createdAt ?? new Date(),\n history,\n key,\n metadata,\n });\n } catch (error) {\n console.warn(`Failed to load session ${key}:`, error);\n return null;\n }\n }\n\n /** Save a session to disk. */\n save(session: Session): void {\n const path = this.getSessionPath(session.key);\n const lines: string[] = [\n JSON.stringify({\n _format: \"rich\",\n _type: \"metadata\",\n created_at: session.createdAt.toISOString(),\n metadata: session.metadata,\n updated_at: session.updatedAt.toISOString(),\n }),\n ];\n\n // Message lines — full ChatMessage objects\n for (const msg of session.history) {\n lines.push(JSON.stringify(msg));\n }\n\n writeFileSync(path, lines.join(\"\\n\") + \"\\n\");\n this.cache.set(session.key, session);\n }\n\n /** Delete a session. */\n delete(key: string): boolean {\n this.cache.delete(key);\n const path = this.getSessionPath(key);\n if (existsSync(path)) {\n unlinkSync(path);\n return true;\n }\n return false;\n }\n\n /** List all sessions. */\n listSessions(): {\n key: string;\n createdAt?: string;\n updatedAt?: string;\n path: string;\n }[] {\n const results: {\n key: string;\n createdAt?: string;\n updatedAt?: string;\n path: string;\n }[] = [];\n\n if (!existsSync(this.sessionsDir)) {\n return results;\n }\n\n for (const file of readdirSync(this.sessionsDir)) {\n if (!file.endsWith(\".jsonl\")) {\n continue;\n }\n const filePath = join(this.sessionsDir, file);\n try {\n const raw = readFileSync(filePath, \"utf8\");\n const firstLine = raw.split(\"\\n\")[0]?.trim();\n if (firstLine) {\n const data = JSON.parse(firstLine);\n if (data._type === \"metadata\") {\n results.push({\n createdAt: data.created_at,\n key: file.replace(\".jsonl\", \"\").replaceAll(\"_\", \":\"),\n path: filePath,\n updatedAt: data.updated_at,\n });\n }\n }\n } catch {\n // skip invalid files\n }\n }\n\n return results.sort((a, b) =>\n (b.updatedAt ?? \"\").localeCompare(a.updatedAt ?? \"\")\n );\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.mts","names":[],"sources":["../../src/utils/helpers.ts"],"mappings":";;iBAKgB,SAAA,CAAU,OAAA;;iBAQV,WAAA,CAAA;;iBAKA,gBAAA,CAAiB,SAAA;AALjC;AAAA,iBAagB,eAAA,CAAA;;iBAKA,aAAA,CAAc,SAAA;;iBAMd,SAAA,CAAA;;iBAKA,cAAA,CACd,CAAA,UACA,MAAA,WACA,MAAA;;iBAOc,YAAA,CAAa,IAAA;AA1B7B;AAAA,iBAoCgB,eAAA,CAAgB,GAAA"}
1
+ {"version":3,"file":"helpers.d.mts","names":[],"sources":["../../src/utils/helpers.ts"],"mappings":";;iBAKgB,SAAA,CAAU,OAAA;;iBAQV,WAAA,CAAA;;iBAKA,gBAAA,CAAiB,SAAA;AALjC;AAAA,iBAagB,eAAA,CAAA;;iBAKA,aAAA,CAAc,SAAA;;iBAMd,SAAA,CAAA;;iBAKA,cAAA,CACd,CAAA,UACA,MAAA,WACA,MAAA;;iBASc,YAAA,CAAa,IAAA;AA5B7B;AAAA,iBAsCgB,eAAA,CAAgB,GAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.mjs","names":[],"sources":["../../src/utils/helpers.ts"],"sourcesContent":["import { mkdirSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n/** Ensure a directory exists, creating it if necessary. */\nexport function ensureDir(dirPath: string): string {\n if (!existsSync(dirPath)) {\n mkdirSync(dirPath, { recursive: true });\n }\n return dirPath;\n}\n\n/** Get the nanobot data directory (~/.nanobot). */\nexport function getDataPath(): string {\n return ensureDir(join(homedir(), \".nanobot\"));\n}\n\n/** Get the workspace path. Defaults to ~/.nanobot/workspace. */\nexport function getWorkspacePath(workspace?: string): string {\n const resolved = workspace\n ? workspace.replace(/^~/, homedir())\n : join(homedir(), \".nanobot\", \"workspace\");\n return ensureDir(resolved);\n}\n\n/** Get the sessions storage directory. */\nexport function getSessionsPath(): string {\n return ensureDir(join(getDataPath(), \"sessions\"));\n}\n\n/** Get the skills directory within the workspace. */\nexport function getSkillsPath(workspace?: string): string {\n const ws = workspace ?? getWorkspacePath();\n return ensureDir(join(ws, \"skills\"));\n}\n\n/** Get current timestamp in ISO format. */\nexport function timestamp(): string {\n return new Date().toISOString();\n}\n\n/** Truncate a string to max length, adding suffix if truncated. */\nexport function truncateString(\n s: string,\n maxLen = 100,\n suffix = \"...\",\n): string {\n if (s.length <= maxLen) return s;\n return s.slice(0, maxLen - suffix.length) + suffix;\n}\n\n/** Convert a string to a safe filename. */\nexport function safeFilename(name: string): string {\n const unsafe = '<>:\"/\\\\|?*';\n let result = name;\n for (const char of unsafe) {\n result = result.replaceAll(char, \"_\");\n }\n return result.trim();\n}\n\n/** Parse a session key into channel and chat_id. */\nexport function parseSessionKey(key: string): [string, string] {\n const idx = key.indexOf(\":\");\n if (idx === -1) {\n throw new Error(`Invalid session key: ${key}`);\n }\n return [key.slice(0, idx), key.slice(idx + 1)];\n}\n"],"mappings":";;;;;;AAKA,SAAgB,UAAU,SAAyB;AACjD,KAAI,CAAC,WAAW,QAAQ,CACtB,WAAU,SAAS,EAAE,WAAW,MAAM,CAAC;AAEzC,QAAO;;;AAIT,SAAgB,cAAsB;AACpC,QAAO,UAAU,KAAK,SAAS,EAAE,WAAW,CAAC;;;AAI/C,SAAgB,iBAAiB,WAA4B;AAI3D,QAAO,UAHU,YACb,UAAU,QAAQ,MAAM,SAAS,CAAC,GAClC,KAAK,SAAS,EAAE,YAAY,YAAY,CAClB;;;AAI5B,SAAgB,kBAA0B;AACxC,QAAO,UAAU,KAAK,aAAa,EAAE,WAAW,CAAC;;;AAInD,SAAgB,cAAc,WAA4B;AAExD,QAAO,UAAU,KADN,aAAa,kBAAkB,EAChB,SAAS,CAAC;;;AAItC,SAAgB,YAAoB;AAClC,yBAAO,IAAI,MAAM,EAAC,aAAa;;;AAIjC,SAAgB,eACd,GACA,SAAS,KACT,SAAS,OACD;AACR,KAAI,EAAE,UAAU,OAAQ,QAAO;AAC/B,QAAO,EAAE,MAAM,GAAG,SAAS,OAAO,OAAO,GAAG;;;AAI9C,SAAgB,aAAa,MAAsB;CACjD,MAAM,SAAS;CACf,IAAI,SAAS;AACb,MAAK,MAAM,QAAQ,OACjB,UAAS,OAAO,WAAW,MAAM,IAAI;AAEvC,QAAO,OAAO,MAAM;;;AAItB,SAAgB,gBAAgB,KAA+B;CAC7D,MAAM,MAAM,IAAI,QAAQ,IAAI;AAC5B,KAAI,QAAQ,GACV,OAAM,IAAI,MAAM,wBAAwB,MAAM;AAEhD,QAAO,CAAC,IAAI,MAAM,GAAG,IAAI,EAAE,IAAI,MAAM,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"helpers.mjs","names":[],"sources":["../../src/utils/helpers.ts"],"sourcesContent":["import { mkdirSync, existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Ensure a directory exists, creating it if necessary. */\nexport function ensureDir(dirPath: string): string {\n if (!existsSync(dirPath)) {\n mkdirSync(dirPath, { recursive: true });\n }\n return dirPath;\n}\n\n/** Get the nanobot data directory (~/.nanobot). */\nexport function getDataPath(): string {\n return ensureDir(join(homedir(), \".nanobot\"));\n}\n\n/** Get the workspace path. Defaults to ~/.nanobot/workspace. */\nexport function getWorkspacePath(workspace?: string): string {\n const resolved = workspace\n ? workspace.replace(/^~/, homedir())\n : join(homedir(), \".nanobot\", \"workspace\");\n return ensureDir(resolved);\n}\n\n/** Get the sessions storage directory. */\nexport function getSessionsPath(): string {\n return ensureDir(join(getDataPath(), \"sessions\"));\n}\n\n/** Get the skills directory within the workspace. */\nexport function getSkillsPath(workspace?: string): string {\n const ws = workspace ?? getWorkspacePath();\n return ensureDir(join(ws, \"skills\"));\n}\n\n/** Get current timestamp in ISO format. */\nexport function timestamp(): string {\n return new Date().toISOString();\n}\n\n/** Truncate a string to max length, adding suffix if truncated. */\nexport function truncateString(\n s: string,\n maxLen = 100,\n suffix = \"...\"\n): string {\n if (s.length <= maxLen) {\n return s;\n }\n return s.slice(0, maxLen - suffix.length) + suffix;\n}\n\n/** Convert a string to a safe filename. */\nexport function safeFilename(name: string): string {\n const unsafe = '<>:\"/\\\\|?*';\n let result = name;\n for (const char of unsafe) {\n result = result.replaceAll(char, \"_\");\n }\n return result.trim();\n}\n\n/** Parse a session key into channel and chat_id. */\nexport function parseSessionKey(key: string): [string, string] {\n const idx = key.indexOf(\":\");\n if (idx === -1) {\n throw new Error(`Invalid session key: ${key}`);\n }\n return [key.slice(0, idx), key.slice(idx + 1)];\n}\n"],"mappings":";;;;;;AAKA,SAAgB,UAAU,SAAyB;AACjD,KAAI,CAAC,WAAW,QAAQ,CACtB,WAAU,SAAS,EAAE,WAAW,MAAM,CAAC;AAEzC,QAAO;;;AAIT,SAAgB,cAAsB;AACpC,QAAO,UAAU,KAAK,SAAS,EAAE,WAAW,CAAC;;;AAI/C,SAAgB,iBAAiB,WAA4B;AAI3D,QAAO,UAHU,YACb,UAAU,QAAQ,MAAM,SAAS,CAAC,GAClC,KAAK,SAAS,EAAE,YAAY,YAAY,CAClB;;;AAI5B,SAAgB,kBAA0B;AACxC,QAAO,UAAU,KAAK,aAAa,EAAE,WAAW,CAAC;;;AAInD,SAAgB,cAAc,WAA4B;AAExD,QAAO,UAAU,KADN,aAAa,kBAAkB,EAChB,SAAS,CAAC;;;AAItC,SAAgB,YAAoB;AAClC,yBAAO,IAAI,MAAM,EAAC,aAAa;;;AAIjC,SAAgB,eACd,GACA,SAAS,KACT,SAAS,OACD;AACR,KAAI,EAAE,UAAU,OACd,QAAO;AAET,QAAO,EAAE,MAAM,GAAG,SAAS,OAAO,OAAO,GAAG;;;AAI9C,SAAgB,aAAa,MAAsB;CACjD,MAAM,SAAS;CACf,IAAI,SAAS;AACb,MAAK,MAAM,QAAQ,OACjB,UAAS,OAAO,WAAW,MAAM,IAAI;AAEvC,QAAO,OAAO,MAAM;;;AAItB,SAAgB,gBAAgB,KAA+B;CAC7D,MAAM,MAAM,IAAI,QAAQ,IAAI;AAC5B,KAAI,QAAQ,GACV,OAAM,IAAI,MAAM,wBAAwB,MAAM;AAEhD,QAAO,CAAC,IAAI,MAAM,GAAG,IAAI,EAAE,IAAI,MAAM,MAAM,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,13 +1,19 @@
1
1
  {
2
2
  "name": "@jcheesepkg/nanobot",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Lightweight AI assistant - TypeScript port",
5
- "type": "module",
6
- "main": "dist/index.mjs",
7
- "types": "dist/index.d.mts",
5
+ "license": "MIT",
8
6
  "bin": {
9
7
  "nanobot": "dist/cli/index.mjs"
10
8
  },
9
+ "files": [
10
+ "dist",
11
+ "skills",
12
+ "workspace"
13
+ ],
14
+ "type": "module",
15
+ "main": "dist/index.mjs",
16
+ "types": "dist/index.d.mts",
11
17
  "exports": {
12
18
  ".": {
13
19
  "types": "./dist/index.d.mts",
@@ -40,11 +46,5 @@
40
46
  },
41
47
  "engines": {
42
48
  "node": ">=18.0.0"
43
- },
44
- "files": [
45
- "dist",
46
- "skills",
47
- "workspace"
48
- ],
49
- "license": "MIT"
49
+ }
50
50
  }