@jcheesepkg/nanobot 0.9.1 → 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 +387 -387
  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 +4 -0
  192. package/skills/english/SKILL.md +21 -7
  193. package/skills/expense/SKILL.md +11 -7
  194. package/skills/fortune/SKILL.md +24 -20
  195. package/skills/habit/SKILL.md +2 -1
  196. package/skills/hydration/SKILL.md +3 -0
  197. package/skills/memory/SKILL.md +1 -0
  198. package/skills/mood/SKILL.md +10 -6
  199. package/skills/skill-creator/SKILL.md +3 -0
  200. package/skills/summarize/SKILL.md +1 -0
  201. package/skills/weather/SKILL.md +10 -8
  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
package/README.md CHANGED
@@ -46,17 +46,17 @@ skills/ Bundled skill definitions (cron, github, summarize, weather, s
46
46
 
47
47
  ## CLI Commands
48
48
 
49
- | Command | Description |
50
- |---------|-------------|
51
- | `nanobot onboard` | Initialize config and workspace |
52
- | `nanobot gateway` | Start the full gateway (agent + channels + cron + heartbeat) |
53
- | `nanobot agent -m "..."` | Send a single message to the agent |
54
- | `nanobot agent` | Interactive chat mode |
55
- | `nanobot channels status` | Show channel configuration |
56
- | `nanobot cron list` | List scheduled jobs |
57
- | `nanobot cron add` | Add a scheduled job |
58
- | `nanobot cron remove <id>` | Remove a scheduled job |
59
- | `nanobot status` | Show config and API key status |
49
+ | Command | Description |
50
+ | -------------------------- | ------------------------------------------------------------ |
51
+ | `nanobot onboard` | Initialize config and workspace |
52
+ | `nanobot gateway` | Start the full gateway (agent + channels + cron + heartbeat) |
53
+ | `nanobot agent -m "..."` | Send a single message to the agent |
54
+ | `nanobot agent` | Interactive chat mode |
55
+ | `nanobot channels status` | Show channel configuration |
56
+ | `nanobot cron list` | List scheduled jobs |
57
+ | `nanobot cron add` | Add a scheduled job |
58
+ | `nanobot cron remove <id>` | Remove a scheduled job |
59
+ | `nanobot status` | Show config and API key status |
60
60
 
61
61
  ## Scripts
62
62
 
@@ -69,13 +69,13 @@ npm run start # Run built CLI
69
69
 
70
70
  ## Dependencies
71
71
 
72
- | Package | Purpose |
73
- |---------|---------|
74
- | `openai` | LLM provider (OpenAI-compatible API) |
75
- | `zod` | Config schema validation |
76
- | `commander` | CLI framework |
77
- | `grammy` | Telegram bot (grammY) |
78
- | `cron-parser` | Cron expression parsing |
72
+ | Package | Purpose |
73
+ | ------------- | ------------------------------------ |
74
+ | `openai` | LLM provider (OpenAI-compatible API) |
75
+ | `zod` | Config schema validation |
76
+ | `commander` | CLI framework |
77
+ | `grammy` | Telegram bot (grammY) |
78
+ | `cron-parser` | Cron expression parsing |
79
79
 
80
80
  ## Configuration
81
81
 
@@ -3,12 +3,12 @@ import { MemoryStore } from "./memory.mjs";
3
3
  import { SkillsLoader } from "./skills.mjs";
4
4
 
5
5
  //#region src/agent/context.d.ts
6
- type AgentIdentity = {
6
+ interface AgentIdentity {
7
7
  name?: string;
8
8
  emoji?: string;
9
9
  creature?: string;
10
10
  vibe?: string;
11
- };
11
+ }
12
12
  /**
13
13
  * Builds the context (system prompt + messages) for the agent.
14
14
  */
@@ -33,14 +33,14 @@ declare class ContextBuilder {
33
33
  /** Add a tool result to the message list. */
34
34
  addToolResult(messages: ChatMessage[], toolCallId: string, toolName: string, result: string): ChatMessage[];
35
35
  /** Add an assistant message to the message list. */
36
- addAssistantMessage(messages: ChatMessage[], content: string | null, toolCalls?: Array<{
36
+ addAssistantMessage(messages: ChatMessage[], content: string | null, toolCalls?: {
37
37
  id: string;
38
38
  type: "function";
39
39
  function: {
40
40
  name: string;
41
41
  arguments: string;
42
42
  };
43
- }>): ChatMessage[];
43
+ }[]): ChatMessage[];
44
44
  }
45
45
  //#endregion
46
46
  export { AgentIdentity, ContextBuilder };
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.mts","names":[],"sources":["../../src/agent/context.ts"],"mappings":";;;;;KA0CY,aAAA;EACV,IAAA;EACA,KAAA;EACA,QAAA;EACA,IAAA;AAAA;;;;cAsDW,cAAA;EAAA,QACH,SAAA;EAAA,SACC,MAAA,EAAQ,WAAA;EAAA,SACR,MAAA,EAAQ,YAAA;cAEL,SAAA;;EAOZ,iBAAA,CAAA;EAAA,QAyCQ,WAAA;EAAA,QAsEA,kBAAA;EAmBJ;EANJ,aAAA,CAAc,MAAA;IACZ,OAAA,EAAS,WAAA;IACT,cAAA;IACA,KAAA;IACA,OAAA;IACA,MAAA;EAAA,IACE,WAAA;EAAA,QAyBI,gBAAA;EArKC;EA0MT,aAAA,CACE,QAAA,EAAU,WAAA,IACV,UAAA,UACA,QAAA,UACA,MAAA,WACC,WAAA;EA9MM;EAyNT,mBAAA,CACE,QAAA,EAAU,WAAA,IACV,OAAA,iBACA,SAAA,GAAY,KAAA;IACV,EAAA;IACA,IAAA;IACA,QAAA;MAAY,IAAA;MAAc,SAAA;IAAA;EAAA,KAE3B,WAAA;AAAA"}
1
+ {"version":3,"file":"context.d.mts","names":[],"sources":["../../src/agent/context.ts"],"mappings":";;;;;UA4CiB,aAAA;EACf,IAAA;EACA,KAAA;EACA,QAAA;EACA,IAAA;AAAA;;;;cAkFW,cAAA;EAAA,QACH,SAAA;EAAA,SACC,MAAA,EAAQ,WAAA;EAAA,SACR,MAAA,EAAQ,YAAA;cAEL,SAAA;;EAOZ,iBAAA,CAAA;EAAA,QA0CQ,WAAA;EAAA,QAmFA,kBAAA;EAmBJ;EANJ,aAAA,CAAc,MAAA;IACZ,OAAA,EAAS,WAAA;IACT,cAAA;IACA,KAAA;IACA,OAAA;IACA,MAAA;EAAA,IACE,WAAA;EAAA,QAyBI,gBAAA;EAnLS;EAgOjB,aAAA,CACE,QAAA,EAAU,WAAA,IACV,UAAA,UACA,QAAA,UACA,MAAA,WACC,WAAA;EApOc;EA+OjB,mBAAA,CACE,QAAA,EAAU,WAAA,IACV,OAAA,iBACA,SAAA;IACE,EAAA;IACA,IAAA;IACA,QAAA;MAAY,IAAA;MAAc,SAAA;IAAA;EAAA,MAE3B,WAAA;AAAA"}
@@ -12,7 +12,7 @@ import { join } from "node:path";
12
12
  function resolveTimezone(workspace) {
13
13
  const userMd = join(workspace, "USER.md");
14
14
  if (existsSync(userMd)) try {
15
- const tzMatch = readFileSync(userMd, "utf-8").match(/^timezone:\s*(.+)/im);
15
+ const tzMatch = readFileSync(userMd, "utf8").match(/^timezone:\s*(.+)/im);
16
16
  if (tzMatch) {
17
17
  const tz = tzMatch[1].trim();
18
18
  if (tz && !tz.includes("not set")) {
@@ -39,15 +39,15 @@ function resolveIdentity(workspace) {
39
39
  const identityMd = join(workspace, "IDENTITY.md");
40
40
  if (!existsSync(identityMd)) return identity;
41
41
  try {
42
- const lines = readFileSync(identityMd, "utf-8").split(/\r?\n/);
42
+ const lines = readFileSync(identityMd, "utf8").split(/\r?\n/);
43
43
  for (const line of lines) {
44
44
  const cleaned = line.trim().replace(/^\s*-\s*/, "");
45
45
  const colonIndex = cleaned.indexOf(":");
46
46
  if (colonIndex === -1) continue;
47
- const label = cleaned.slice(0, colonIndex).replace(/[*_]/g, "").trim().toLowerCase();
48
- const value = cleaned.slice(colonIndex + 1).replace(/^[*_]+|[*_]+$/g, "").trim();
47
+ const label = cleaned.slice(0, colonIndex).replaceAll(/[*_]/g, "").trim().toLowerCase();
48
+ const value = cleaned.slice(colonIndex + 1).replaceAll(/^[*_]+|[*_]+$/g, "").trim();
49
49
  if (!value) continue;
50
- const normalized = value.toLowerCase().replace(/[\u2013\u2014]/g, "-").replace(/\s+/g, " ");
50
+ const normalized = value.toLowerCase().replaceAll(/[\u2013\u2014]/g, "-").replaceAll(/\s+/g, " ");
51
51
  if (IDENTITY_PLACEHOLDERS.has(normalized)) continue;
52
52
  if (/^\(.*\)$/.test(value) || /^_.*_$/.test(value)) continue;
53
53
  if (label === "name") identity.name = value;
@@ -79,8 +79,7 @@ var ContextBuilder = class {
79
79
  }
80
80
  /** Build the system prompt from bootstrap files, memory, and skills. */
81
81
  buildSystemPrompt() {
82
- const parts = [];
83
- parts.push(this.getIdentity());
82
+ const parts = [this.getIdentity()];
84
83
  const bootstrap = this.loadBootstrapFiles();
85
84
  if (bootstrap) parts.push(bootstrap);
86
85
  const memory = this.memory.getMemoryContext();
@@ -100,14 +99,14 @@ var ContextBuilder = class {
100
99
  const tz = resolveTimezone(this.workspace);
101
100
  const identity = resolveIdentity(this.workspace);
102
101
  const dateStr = now.toLocaleString("en-US", {
103
- timeZone: tz,
104
- year: "numeric",
105
- month: "2-digit",
106
102
  day: "2-digit",
107
103
  hour: "2-digit",
104
+ hour12: false,
108
105
  minute: "2-digit",
109
- hour12: false
110
- }).replace(/\//g, "-");
106
+ month: "2-digit",
107
+ timeZone: tz,
108
+ year: "numeric"
109
+ }).replaceAll("/", "-");
111
110
  const dayName = now.toLocaleDateString("en-US", {
112
111
  timeZone: tz,
113
112
  weekday: "long"
@@ -174,7 +173,7 @@ To recall past events, grep ${this.workspace}/memory/HISTORY.md`;
174
173
  for (const filename of BOOTSTRAP_FILES) {
175
174
  const filePath = join(this.workspace, filename);
176
175
  if (existsSync(filePath)) {
177
- const content = readFileSync(filePath, "utf-8");
176
+ const content = readFileSync(filePath, "utf8");
178
177
  parts.push(`## ${filename}\n\n${content}`);
179
178
  }
180
179
  }
@@ -186,14 +185,14 @@ To recall past events, grep ${this.workspace}/memory/HISTORY.md`;
186
185
  let systemPrompt = this.buildSystemPrompt();
187
186
  if (params.channel && params.chatId) systemPrompt += `\n\n## Current Session\nChannel: ${params.channel}\nChat ID: ${params.chatId}`;
188
187
  messages.push({
189
- role: "system",
190
- content: systemPrompt
188
+ content: systemPrompt,
189
+ role: "system"
191
190
  });
192
191
  for (const msg of params.history) messages.push(msg);
193
192
  const userContent = this.buildUserContent(params.currentMessage, params.media);
194
193
  messages.push({
195
- role: "user",
196
- content: userContent
194
+ content: userContent,
195
+ role: "user"
197
196
  });
198
197
  return messages;
199
198
  }
@@ -205,41 +204,41 @@ To recall past events, grep ${this.workspace}/memory/HISTORY.md`;
205
204
  try {
206
205
  const data = readFileSync(filePath);
207
206
  const mime = {
208
- jpg: "image/jpeg",
207
+ gif: "image/gif",
209
208
  jpeg: "image/jpeg",
209
+ jpg: "image/jpeg",
210
210
  png: "image/png",
211
- gif: "image/gif",
212
211
  webp: "image/webp"
213
212
  }[filePath.split(".").pop()?.toLowerCase() ?? ""];
214
213
  if (!mime) continue;
215
214
  const b64 = data.toString("base64");
216
215
  images.push({
217
- type: "image_url",
218
- image_url: { url: `data:${mime};base64,${b64}` }
216
+ image_url: { url: `data:${mime};base64,${b64}` },
217
+ type: "image_url"
219
218
  });
220
219
  } catch {}
221
220
  }
222
221
  if (images.length === 0) return text;
223
222
  return [...images, {
224
- type: "text",
225
- text
223
+ text,
224
+ type: "text"
226
225
  }];
227
226
  }
228
227
  /** Add a tool result to the message list. */
229
228
  addToolResult(messages, toolCallId, toolName, result) {
230
229
  messages.push({
231
- role: "tool",
232
- tool_call_id: toolCallId,
230
+ content: result,
233
231
  name: toolName,
234
- content: result
232
+ role: "tool",
233
+ tool_call_id: toolCallId
235
234
  });
236
235
  return messages;
237
236
  }
238
237
  /** Add an assistant message to the message list. */
239
238
  addAssistantMessage(messages, content, toolCalls) {
240
239
  const msg = {
241
- role: "assistant",
242
- content: content ?? ""
240
+ content: content ?? "",
241
+ role: "assistant"
243
242
  };
244
243
  if (toolCalls) msg.tool_calls = toolCalls;
245
244
  messages.push(msg);
@@ -1 +1 @@
1
- {"version":3,"file":"context.mjs","names":[],"sources":["../../src/agent/context.ts"],"sourcesContent":["import { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ChatMessage, ContentPart } from \"../providers/base.js\";\nimport { MemoryStore } from \"./memory.js\";\nimport { SkillsLoader } from \"./skills.js\";\n\n/**\n * Resolve user timezone from USER.md.\n * Looks for a line like: Timezone: Asia/Tokyo\n * Falls back to host detection / UTC.\n */\nfunction resolveTimezone(workspace: string): string {\n const userMd = join(workspace, \"USER.md\");\n if (existsSync(userMd)) {\n try {\n const content = readFileSync(userMd, \"utf-8\");\n const tzMatch = content.match(/^timezone:\\s*(.+)/im);\n if (tzMatch) {\n const tz = tzMatch[1].trim();\n if (tz && !tz.includes(\"not set\")) {\n // Validate IANA timezone\n new Intl.DateTimeFormat(\"en-US\", { timeZone: tz }).format(new Date());\n return tz;\n }\n }\n } catch {\n // Invalid value or read error, fall through\n }\n }\n\n const host = Intl.DateTimeFormat().resolvedOptions().timeZone;\n return host?.trim() || \"UTC\";\n}\n\n/** Placeholder values in IDENTITY.md that mean \"not yet filled in\". */\nconst IDENTITY_PLACEHOLDERS = new Set([\n \"(not set)\",\n \"(pick something you like)\",\n \"(your signature)\",\n \"(how do you come across?)\",\n]);\n\nexport type AgentIdentity = {\n name?: string;\n emoji?: string;\n creature?: string;\n vibe?: string;\n};\n\n/**\n * Parse IDENTITY.md from the workspace to resolve the agent's chosen identity.\n * Returns fields that have been filled in (skips placeholders).\n */\nfunction resolveIdentity(workspace: string): AgentIdentity {\n const identity: AgentIdentity = {};\n const identityMd = join(workspace, \"IDENTITY.md\");\n if (!existsSync(identityMd)) return identity;\n\n try {\n const content = readFileSync(identityMd, \"utf-8\");\n const lines = content.split(/\\r?\\n/);\n for (const line of lines) {\n // Strip list markers and bold markers\n const cleaned = line.trim().replace(/^\\s*-\\s*/, \"\");\n const colonIndex = cleaned.indexOf(\":\");\n if (colonIndex === -1) continue;\n\n const label = cleaned.slice(0, colonIndex).replace(/[*_]/g, \"\").trim().toLowerCase();\n const value = cleaned.slice(colonIndex + 1).replace(/^[*_]+|[*_]+$/g, \"\").trim();\n if (!value) continue;\n\n // Check if it's a placeholder\n const normalized = value.toLowerCase().replace(/[\\u2013\\u2014]/g, \"-\").replace(/\\s+/g, \" \");\n if (IDENTITY_PLACEHOLDERS.has(normalized)) continue;\n // Also check if it looks like a template hint wrapped in parens or italics\n if (/^\\(.*\\)$/.test(value) || /^_.*_$/.test(value)) continue;\n\n if (label === \"name\") identity.name = value;\n if (label === \"emoji\") identity.emoji = value;\n if (label === \"creature\") identity.creature = value;\n if (label === \"vibe\") identity.vibe = value;\n }\n } catch {\n // Read error, return empty\n }\n\n return identity;\n}\n\nconst BOOTSTRAP_FILES = [\n \"AGENTS.md\",\n \"SOUL.md\",\n \"USER.md\",\n \"TOOLS.md\",\n \"IDENTITY.md\",\n];\n\n/**\n * Builds the context (system prompt + messages) for the agent.\n */\nexport class ContextBuilder {\n private workspace: string;\n readonly memory: MemoryStore;\n readonly skills: SkillsLoader;\n\n constructor(workspace: string) {\n this.workspace = workspace;\n this.memory = new MemoryStore(workspace);\n this.skills = new SkillsLoader(workspace);\n }\n\n /** Build the system prompt from bootstrap files, memory, and skills. */\n buildSystemPrompt(): string {\n const parts: string[] = [];\n\n // Core identity\n parts.push(this.getIdentity());\n\n // Bootstrap files\n const bootstrap = this.loadBootstrapFiles();\n if (bootstrap) parts.push(bootstrap);\n\n // Memory context\n const memory = this.memory.getMemoryContext();\n if (memory) parts.push(`# Memory\\n\\n${memory}`);\n\n // Always-loaded skills\n const alwaysSkills = this.skills.getAlwaysSkills();\n if (alwaysSkills.length > 0) {\n console.log(`Skills always-loaded: ${alwaysSkills.join(\", \")}`);\n const alwaysContent = this.skills.loadSkillsForContext(alwaysSkills);\n if (alwaysContent) {\n parts.push(`# Active Skills\\n\\n${alwaysContent}`);\n }\n }\n\n // Available skills summary\n const skillsSummary = this.skills.buildSkillsSummary();\n if (skillsSummary) {\n parts.push(\n `# Skills (mandatory)\\n\\n` +\n `Before replying, scan the <skills> entries below.\\n` +\n `- If a skill clearly applies to the user's request: read its SKILL.md at the <location> path using read_file, then follow its instructions.\\n` +\n `- If multiple skills could apply: choose the most specific one, then read and follow it.\\n` +\n `- If no skill applies: respond normally without reading any SKILL.md.\\n` +\n `- Never improvise when a matching skill exists. Always read and follow the skill first.\\n\\n` +\n skillsSummary,\n );\n }\n\n return parts.join(\"\\n\\n---\\n\\n\");\n }\n\n private getIdentity(): string {\n const now = new Date();\n const tz = resolveTimezone(this.workspace);\n const identity = resolveIdentity(this.workspace);\n const dateStr = now.toLocaleString(\"en-US\", { timeZone: tz, year: \"numeric\", month: \"2-digit\", day: \"2-digit\", hour: \"2-digit\", minute: \"2-digit\", hour12: false }).replace(/\\//g, \"-\");\n const dayName = now.toLocaleDateString(\"en-US\", { timeZone: tz, weekday: \"long\" });\n\n // Use identity name if set, otherwise generic\n const agentName = identity.name || \"kodama\";\n const identityLine = identity.name\n ? `You are ${agentName}, a personal AI assistant.`\n : \"You are a personal AI assistant running inside kodamabot.\";\n\n return `# ${agentName}\n\n${identityLine} You have access to tools that allow you to:\n- Read, write, and edit files\n- Execute shell commands\n- Search the web and fetch web pages\n- Send messages to users on chat channels\n- Spawn subagents for complex background tasks\n\n## Current Time\n${dateStr} (${dayName})\nTimezone: ${tz}${tz === \"UTC\" ? \"\\nNote: Timezone is not yet configured. Do NOT assume the user's local time or make time-of-day references (morning, night, etc.). Ask the user where they are so you can set their timezone.\" : \"\"}\n\n## Workspace\nYour workspace is at: ${this.workspace}\n- Identity: ${this.workspace}/IDENTITY.md\n- User info: ${this.workspace}/USER.md\n- Persona: ${this.workspace}/SOUL.md\n- Instructions: ${this.workspace}/AGENTS.md\n- Heartbeat: ${this.workspace}/HEARTBEAT.md\n- Long-term memory: ${this.workspace}/memory/MEMORY.md\n- History log: ${this.workspace}/memory/HISTORY.md (grep-searchable)\n- Custom skills: ${this.workspace}/skills/{skill-name}/SKILL.md\n\nIMPORTANT: When responding to direct questions or conversations, reply directly with your text response.\nOnly use the 'message' tool when you need to send a message to a specific chat channel.\nFor normal conversation, just respond with text - do not call the message tool.\n\nIMPORTANT: When you decide to use tools, ALWAYS include a brief text message alongside your tool calls.\nThis text is sent to the user immediately so they know you're working. Keep it natural and conversational.\nNever mention internal file names (IDENTITY.md, USER.md, SOUL.md, MEMORY.md, etc.) to the user. These are implementation details. Just act naturally — save things silently, don't narrate file operations.\n\n## Installing Skills\nWhen asked to install a skill (from a URL, file, or any source):\n1. Fetch/read the skill content\n2. ALWAYS install it to: ${this.workspace}/skills/{skill-name}/SKILL.md\n3. If the skill references a different workspace path (e.g. .moltbot/, .otherbot/, etc.), rewrite ALL paths to use ${this.workspace}/ instead\n4. Preserve the skill's frontmatter, instructions, scripts, references, and assets — only change the workspace paths\n\n## Identity & Onboarding\nIf IDENTITY.md has no Name set, and USER.md contains \"(not set)\" values, this is a new user.\nOn their FIRST message, greet them warmly and ask:\n1. What they'd like to call you (your name — you're becoming someone, not just a chatbot)\n2. What they'd like to be called\n3. Where they're located (to determine timezone, e.g. Asia/Tokyo, America/New_York)\n\nThen update IDENTITY.md and USER.md with the real values using the edit_file tool. Use IANA timezone format.\nOnly do this ONCE — if both files already have real values, skip onboarding.\n\n## Persona\nIf SOUL.md is present, embody its persona and tone. Avoid stiff, generic replies; follow its guidance unless higher-priority instructions override it.\n\nAlways be helpful, accurate, and concise. When using tools, explain what you're doing.\nWhen remembering something important, write to ${this.workspace}/memory/MEMORY.md\nTo recall past events, grep ${this.workspace}/memory/HISTORY.md`;\n }\n\n private loadBootstrapFiles(): string {\n const parts: string[] = [];\n for (const filename of BOOTSTRAP_FILES) {\n const filePath = join(this.workspace, filename);\n if (existsSync(filePath)) {\n const content = readFileSync(filePath, \"utf-8\");\n parts.push(`## ${filename}\\n\\n${content}`);\n }\n }\n return parts.join(\"\\n\\n\");\n }\n\n /** Build the complete message list for an LLM call. */\n buildMessages(params: {\n history: ChatMessage[];\n currentMessage: string;\n media?: string[];\n channel?: string;\n chatId?: string;\n }): ChatMessage[] {\n const messages: ChatMessage[] = [];\n\n // System prompt\n let systemPrompt = this.buildSystemPrompt();\n if (params.channel && params.chatId) {\n systemPrompt += `\\n\\n## Current Session\\nChannel: ${params.channel}\\nChat ID: ${params.chatId}`;\n }\n messages.push({ role: \"system\", content: systemPrompt });\n\n // History — replay full rich messages (user, assistant w/ tool_calls, tool results, etc.)\n for (const msg of params.history) {\n messages.push(msg);\n }\n\n // Current message (with optional image attachments)\n const userContent = this.buildUserContent(\n params.currentMessage,\n params.media,\n );\n messages.push({ role: \"user\", content: userContent });\n\n return messages;\n }\n\n private buildUserContent(\n text: string,\n media?: string[],\n ): string | ContentPart[] {\n if (!media || media.length === 0) return text;\n\n const images: ContentPart[] = [];\n for (const filePath of media) {\n if (!existsSync(filePath)) continue;\n try {\n const data = readFileSync(filePath);\n const ext = filePath.split(\".\").pop()?.toLowerCase() ?? \"\";\n const mimeMap: Record<string, string> = {\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n };\n const mime = mimeMap[ext];\n if (!mime) continue;\n\n const b64 = data.toString(\"base64\");\n images.push({\n type: \"image_url\",\n image_url: { url: `data:${mime};base64,${b64}` },\n });\n } catch {\n // skip unreadable files\n }\n }\n\n if (images.length === 0) return text;\n return [...images, { type: \"text\", text }];\n }\n\n /** Add a tool result to the message list. */\n addToolResult(\n messages: ChatMessage[],\n toolCallId: string,\n toolName: string,\n result: string,\n ): ChatMessage[] {\n messages.push({\n role: \"tool\",\n tool_call_id: toolCallId,\n name: toolName,\n content: result,\n });\n return messages;\n }\n\n /** Add an assistant message to the message list. */\n addAssistantMessage(\n messages: ChatMessage[],\n content: string | null,\n toolCalls?: Array<{\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }>,\n ): ChatMessage[] {\n const msg: ChatMessage = {\n role: \"assistant\",\n content: content ?? \"\",\n };\n if (toolCalls) {\n msg.tool_calls = toolCalls;\n }\n messages.push(msg);\n return messages;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"context.mjs","names":[],"sources":["../../src/agent/context.ts"],"sourcesContent":["import { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport type { ChatMessage, ContentPart } from \"../providers/base.js\";\n\nimport { MemoryStore } from \"./memory.js\";\nimport { SkillsLoader } from \"./skills.js\";\n\n/**\n * Resolve user timezone from USER.md.\n * Looks for a line like: Timezone: Asia/Tokyo\n * Falls back to host detection / UTC.\n */\nfunction resolveTimezone(workspace: string): string {\n const userMd = join(workspace, \"USER.md\");\n if (existsSync(userMd)) {\n try {\n const content = readFileSync(userMd, \"utf8\");\n const tzMatch = content.match(/^timezone:\\s*(.+)/im);\n if (tzMatch) {\n const tz = tzMatch[1].trim();\n if (tz && !tz.includes(\"not set\")) {\n // Validate IANA timezone\n new Intl.DateTimeFormat(\"en-US\", { timeZone: tz }).format(new Date());\n return tz;\n }\n }\n } catch {\n // Invalid value or read error, fall through\n }\n }\n\n const host = Intl.DateTimeFormat().resolvedOptions().timeZone;\n return host?.trim() || \"UTC\";\n}\n\n/** Placeholder values in IDENTITY.md that mean \"not yet filled in\". */\nconst IDENTITY_PLACEHOLDERS = new Set([\n \"(not set)\",\n \"(pick something you like)\",\n \"(your signature)\",\n \"(how do you come across?)\",\n]);\n\nexport interface AgentIdentity {\n name?: string;\n emoji?: string;\n creature?: string;\n vibe?: string;\n}\n\n/**\n * Parse IDENTITY.md from the workspace to resolve the agent's chosen identity.\n * Returns fields that have been filled in (skips placeholders).\n */\nfunction resolveIdentity(workspace: string): AgentIdentity {\n const identity: AgentIdentity = {};\n const identityMd = join(workspace, \"IDENTITY.md\");\n if (!existsSync(identityMd)) {\n return identity;\n }\n\n try {\n const content = readFileSync(identityMd, \"utf8\");\n const lines = content.split(/\\r?\\n/);\n for (const line of lines) {\n // Strip list markers and bold markers\n const cleaned = line.trim().replace(/^\\s*-\\s*/, \"\");\n const colonIndex = cleaned.indexOf(\":\");\n if (colonIndex === -1) {\n continue;\n }\n\n const label = cleaned\n .slice(0, colonIndex)\n .replaceAll(/[*_]/g, \"\")\n .trim()\n .toLowerCase();\n const value = cleaned\n .slice(colonIndex + 1)\n .replaceAll(/^[*_]+|[*_]+$/g, \"\")\n .trim();\n if (!value) {\n continue;\n }\n\n // Check if it's a placeholder\n const normalized = value\n .toLowerCase()\n .replaceAll(/[\\u2013\\u2014]/g, \"-\")\n .replaceAll(/\\s+/g, \" \");\n if (IDENTITY_PLACEHOLDERS.has(normalized)) {\n continue;\n }\n // Also check if it looks like a template hint wrapped in parens or italics\n if (/^\\(.*\\)$/.test(value) || /^_.*_$/.test(value)) {\n continue;\n }\n\n if (label === \"name\") {\n identity.name = value;\n }\n if (label === \"emoji\") {\n identity.emoji = value;\n }\n if (label === \"creature\") {\n identity.creature = value;\n }\n if (label === \"vibe\") {\n identity.vibe = value;\n }\n }\n } catch {\n // Read error, return empty\n }\n\n return identity;\n}\n\nconst BOOTSTRAP_FILES = [\n \"AGENTS.md\",\n \"SOUL.md\",\n \"USER.md\",\n \"TOOLS.md\",\n \"IDENTITY.md\",\n];\n\n/**\n * Builds the context (system prompt + messages) for the agent.\n */\nexport class ContextBuilder {\n private workspace: string;\n readonly memory: MemoryStore;\n readonly skills: SkillsLoader;\n\n constructor(workspace: string) {\n this.workspace = workspace;\n this.memory = new MemoryStore(workspace);\n this.skills = new SkillsLoader(workspace);\n }\n\n /** Build the system prompt from bootstrap files, memory, and skills. */\n buildSystemPrompt(): string {\n const parts: string[] = [this.getIdentity()];\n\n // Bootstrap files\n const bootstrap = this.loadBootstrapFiles();\n if (bootstrap) {\n parts.push(bootstrap);\n }\n\n // Memory context\n const memory = this.memory.getMemoryContext();\n if (memory) {\n parts.push(`# Memory\\n\\n${memory}`);\n }\n\n // Always-loaded skills\n const alwaysSkills = this.skills.getAlwaysSkills();\n if (alwaysSkills.length > 0) {\n console.log(`Skills always-loaded: ${alwaysSkills.join(\", \")}`);\n const alwaysContent = this.skills.loadSkillsForContext(alwaysSkills);\n if (alwaysContent) {\n parts.push(`# Active Skills\\n\\n${alwaysContent}`);\n }\n }\n\n // Available skills summary\n const skillsSummary = this.skills.buildSkillsSummary();\n if (skillsSummary) {\n parts.push(\n `# Skills (mandatory)\\n\\n` +\n `Before replying, scan the <skills> entries below.\\n` +\n `- If a skill clearly applies to the user's request: read its SKILL.md at the <location> path using read_file, then follow its instructions.\\n` +\n `- If multiple skills could apply: choose the most specific one, then read and follow it.\\n` +\n `- If no skill applies: respond normally without reading any SKILL.md.\\n` +\n `- Never improvise when a matching skill exists. Always read and follow the skill first.\\n\\n` +\n skillsSummary\n );\n }\n\n return parts.join(\"\\n\\n---\\n\\n\");\n }\n\n private getIdentity(): string {\n const now = new Date();\n const tz = resolveTimezone(this.workspace);\n const identity = resolveIdentity(this.workspace);\n const dateStr = now\n .toLocaleString(\"en-US\", {\n day: \"2-digit\",\n hour: \"2-digit\",\n hour12: false,\n minute: \"2-digit\",\n month: \"2-digit\",\n timeZone: tz,\n year: \"numeric\",\n })\n .replaceAll(\"/\", \"-\");\n const dayName = now.toLocaleDateString(\"en-US\", {\n timeZone: tz,\n weekday: \"long\",\n });\n\n // Use identity name if set, otherwise generic\n const agentName = identity.name || \"kodama\";\n const identityLine = identity.name\n ? `You are ${agentName}, a personal AI assistant.`\n : \"You are a personal AI assistant running inside kodamabot.\";\n\n return `# ${agentName}\n\n${identityLine} You have access to tools that allow you to:\n- Read, write, and edit files\n- Execute shell commands\n- Search the web and fetch web pages\n- Send messages to users on chat channels\n- Spawn subagents for complex background tasks\n\n## Current Time\n${dateStr} (${dayName})\nTimezone: ${tz}${tz === \"UTC\" ? \"\\nNote: Timezone is not yet configured. Do NOT assume the user's local time or make time-of-day references (morning, night, etc.). Ask the user where they are so you can set their timezone.\" : \"\"}\n\n## Workspace\nYour workspace is at: ${this.workspace}\n- Identity: ${this.workspace}/IDENTITY.md\n- User info: ${this.workspace}/USER.md\n- Persona: ${this.workspace}/SOUL.md\n- Instructions: ${this.workspace}/AGENTS.md\n- Heartbeat: ${this.workspace}/HEARTBEAT.md\n- Long-term memory: ${this.workspace}/memory/MEMORY.md\n- History log: ${this.workspace}/memory/HISTORY.md (grep-searchable)\n- Custom skills: ${this.workspace}/skills/{skill-name}/SKILL.md\n\nIMPORTANT: When responding to direct questions or conversations, reply directly with your text response.\nOnly use the 'message' tool when you need to send a message to a specific chat channel.\nFor normal conversation, just respond with text - do not call the message tool.\n\nIMPORTANT: When you decide to use tools, ALWAYS include a brief text message alongside your tool calls.\nThis text is sent to the user immediately so they know you're working. Keep it natural and conversational.\nNever mention internal file names (IDENTITY.md, USER.md, SOUL.md, MEMORY.md, etc.) to the user. These are implementation details. Just act naturally — save things silently, don't narrate file operations.\n\n## Installing Skills\nWhen asked to install a skill (from a URL, file, or any source):\n1. Fetch/read the skill content\n2. ALWAYS install it to: ${this.workspace}/skills/{skill-name}/SKILL.md\n3. If the skill references a different workspace path (e.g. .moltbot/, .otherbot/, etc.), rewrite ALL paths to use ${this.workspace}/ instead\n4. Preserve the skill's frontmatter, instructions, scripts, references, and assets — only change the workspace paths\n\n## Identity & Onboarding\nIf IDENTITY.md has no Name set, and USER.md contains \"(not set)\" values, this is a new user.\nOn their FIRST message, greet them warmly and ask:\n1. What they'd like to call you (your name — you're becoming someone, not just a chatbot)\n2. What they'd like to be called\n3. Where they're located (to determine timezone, e.g. Asia/Tokyo, America/New_York)\n\nThen update IDENTITY.md and USER.md with the real values using the edit_file tool. Use IANA timezone format.\nOnly do this ONCE — if both files already have real values, skip onboarding.\n\n## Persona\nIf SOUL.md is present, embody its persona and tone. Avoid stiff, generic replies; follow its guidance unless higher-priority instructions override it.\n\nAlways be helpful, accurate, and concise. When using tools, explain what you're doing.\nWhen remembering something important, write to ${this.workspace}/memory/MEMORY.md\nTo recall past events, grep ${this.workspace}/memory/HISTORY.md`;\n }\n\n private loadBootstrapFiles(): string {\n const parts: string[] = [];\n for (const filename of BOOTSTRAP_FILES) {\n const filePath = join(this.workspace, filename);\n if (existsSync(filePath)) {\n const content = readFileSync(filePath, \"utf8\");\n parts.push(`## ${filename}\\n\\n${content}`);\n }\n }\n return parts.join(\"\\n\\n\");\n }\n\n /** Build the complete message list for an LLM call. */\n buildMessages(params: {\n history: ChatMessage[];\n currentMessage: string;\n media?: string[];\n channel?: string;\n chatId?: string;\n }): ChatMessage[] {\n const messages: ChatMessage[] = [];\n\n // System prompt\n let systemPrompt = this.buildSystemPrompt();\n if (params.channel && params.chatId) {\n systemPrompt += `\\n\\n## Current Session\\nChannel: ${params.channel}\\nChat ID: ${params.chatId}`;\n }\n messages.push({ content: systemPrompt, role: \"system\" });\n\n // History — replay full rich messages (user, assistant w/ tool_calls, tool results, etc.)\n for (const msg of params.history) {\n messages.push(msg);\n }\n\n // Current message (with optional image attachments)\n const userContent = this.buildUserContent(\n params.currentMessage,\n params.media\n );\n messages.push({ content: userContent, role: \"user\" });\n\n return messages;\n }\n\n private buildUserContent(\n text: string,\n media?: string[]\n ): string | ContentPart[] {\n if (!media || media.length === 0) {\n return text;\n }\n\n const images: ContentPart[] = [];\n for (const filePath of media) {\n if (!existsSync(filePath)) {\n continue;\n }\n try {\n const data = readFileSync(filePath);\n const ext = filePath.split(\".\").pop()?.toLowerCase() ?? \"\";\n const mimeMap: Record<string, string> = {\n gif: \"image/gif\",\n jpeg: \"image/jpeg\",\n jpg: \"image/jpeg\",\n png: \"image/png\",\n webp: \"image/webp\",\n };\n const mime = mimeMap[ext];\n if (!mime) {\n continue;\n }\n\n const b64 = data.toString(\"base64\");\n images.push({\n image_url: { url: `data:${mime};base64,${b64}` },\n type: \"image_url\",\n });\n } catch {\n // skip unreadable files\n }\n }\n\n if (images.length === 0) {\n return text;\n }\n return [...images, { text, type: \"text\" }];\n }\n\n /** Add a tool result to the message list. */\n addToolResult(\n messages: ChatMessage[],\n toolCallId: string,\n toolName: string,\n result: string\n ): ChatMessage[] {\n messages.push({\n content: result,\n name: toolName,\n role: \"tool\",\n tool_call_id: toolCallId,\n });\n return messages;\n }\n\n /** Add an assistant message to the message list. */\n addAssistantMessage(\n messages: ChatMessage[],\n content: string | null,\n toolCalls?: {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }[]\n ): ChatMessage[] {\n const msg: ChatMessage = {\n content: content ?? \"\",\n role: \"assistant\",\n };\n if (toolCalls) {\n msg.tool_calls = toolCalls;\n }\n messages.push(msg);\n return messages;\n }\n}\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  import { LLMProvider } from "../providers/base.mjs";
2
2
  import { ContextBuilder } from "./context.mjs";
3
3
  import { MessageBus } from "../bus/queue.mjs";
4
- import { Tool } from "./tools/base.mjs";
5
- import { ToolRegistry } from "./tools/registry.mjs";
6
4
  import { ExecToolConfig } from "../config/schema.mjs";
7
- import { SubagentManager } from "./subagent.mjs";
5
+ import { Tool } from "./tools/base.mjs";
8
6
  import { SessionManager } from "../session/manager.mjs";
7
+ import { SubagentManager } from "./subagent.mjs";
8
+ import { ToolRegistry } from "./tools/registry.mjs";
9
9
 
10
10
  //#region src/agent/loop.d.ts
11
11
  /**
@@ -33,6 +33,8 @@ declare class AgentLoop {
33
33
  private _running;
34
34
  /** In-flight AbortControllers keyed by session key. */
35
35
  private inflight;
36
+ /** Promises that resolve when an aborted processMessage finishes saving. */
37
+ private inflightDone;
36
38
  constructor(params: {
37
39
  bus: MessageBus;
38
40
  provider: LLMProvider;
@@ -1 +1 @@
1
- {"version":3,"file":"loop.d.mts","names":[],"sources":["../../src/agent/loop.ts"],"mappings":";;;;;;;;;;;;AAqCA;;;;;;;cAAa,SAAA;EAAA,QACH,GAAA;EAAA,QACA,QAAA;EAAA,QACA,SAAA;EAAA,QACA,KAAA;EAAA,QACA,kBAAA;EAAA,QACA,SAAA;EAAA,QACA,aAAA;EAAA,QACA,YAAA;EAAA,SAEC,OAAA,EAAS,cAAA;EAAA,SACT,QAAA,EAAU,cAAA;EAAA,SACV,KAAA,EAAO,YAAA;EAAA,SACP,SAAA,EAAW,eAAA;EAAA,QAEZ,QAAA;EARA;EAAA,QAWA,QAAA;cAEI,MAAA;IACV,GAAA,EAAK,UAAA;IACL,QAAA,EAAU,WAAA;IACV,SAAA;IACA,KAAA;IACA,kBAAA;IACA,SAAA;IACA,aAAA;IACA,YAAA;IACA,WAAA;IACA,UAAA,GAAa,cAAA;IACb,mBAAA;IACA,YAAA;IACA,aAAA;IACA,WAAA,GAAc,IAAA;EAAA;EAAA,QAqCR,oBAAA;EA9CN;EA+GI,GAAA,CAAA,GAAO,OAAA;EA7GX;EA8IF,IAAA,CAAA;EA5IE;EAAA,QAkJY,cAAA;EAAA,QA4FA,oBAAA;EAAA,QA4CA,YAAA;EAvRZ;EAAA,QA6XM,uBAAA;EAAA,QAcA,kBAAA;EAzYQ;EA2ZV,aAAA,CACJ,OAAA,UACA,UAAA,WACA,OAAA,WACA,MAAA,YACC,OAAA;EA3XK;EAAA,QAmZM,iBAAA;AAAA"}
1
+ {"version":3,"file":"loop.d.mts","names":[],"sources":["../../src/agent/loop.ts"],"mappings":";;;;;;;;;;;;AAwCA;;;;;;;cAAa,SAAA;EAAA,QACH,GAAA;EAAA,QACA,QAAA;EAAA,QACA,SAAA;EAAA,QACA,KAAA;EAAA,QACA,kBAAA;EAAA,QACA,SAAA;EAAA,QACA,aAAA;EAAA,QACA,YAAA;EAAA,SAEC,OAAA,EAAS,cAAA;EAAA,SACT,QAAA,EAAU,cAAA;EAAA,SACV,KAAA,EAAO,YAAA;EAAA,SACP,SAAA,EAAW,eAAA;EAAA,QAEZ,QAAA;EARA;EAAA,QAWA,QAAA;EARC;EAAA,QAWD,YAAA;cAEI,MAAA;IACV,GAAA,EAAK,UAAA;IACL,QAAA,EAAU,WAAA;IACV,SAAA;IACA,KAAA;IACA,kBAAA;IACA,SAAA;IACA,aAAA;IACA,YAAA;IACA,WAAA;IACA,UAAA,GAAa,cAAA;IACb,mBAAA;IACA,YAAA;IACA,aAAA;IACA,WAAA,GAAc,IAAA;EAAA;EAAA,QAqCR,oBAAA;EA7CN;EAgHI,GAAA,CAAA,GAAO,OAAA;EA9GX;EAqJF,IAAA,CAAA;EAnJE;EAAA,QAyJY,cAAA;EAAA,QA6GA,oBAAA;EAAA,QA4CA,YAAA;EA/SZ;EAAA,QA8ZM,uBAAA;EAAA,QAgBA,kBAAA;EA3bI;EA6cN,aAAA,CACJ,OAAA,UACA,UAAA,WACA,OAAA,WACA,MAAA,YACC,OAAA;EA5VG;EAAA,QAoXQ,iBAAA;AAAA"}
@@ -1,17 +1,17 @@
1
1
  import { createOutboundMessage } from "../bus/events.mjs";
2
+ import { SessionManager } from "../session/manager.mjs";
2
3
  import { MemoryStore } from "./memory.mjs";
3
4
  import { getBuiltinSkillsDir } from "./skills.mjs";
4
5
  import { ContextBuilder } from "./context.mjs";
5
- import { ToolRegistry } from "./tools/registry.mjs";
6
6
  import { EditFileTool, ListDirTool, ReadFileTool, WriteFileTool } from "./tools/filesystem.mjs";
7
+ import { ToolRegistry } from "./tools/registry.mjs";
7
8
  import { ExecTool } from "./tools/shell.mjs";
8
9
  import { WebFetchTool, WebSearchTool } from "./tools/web.mjs";
9
- import { MessageTool } from "./tools/message.mjs";
10
- import { SpawnTool } from "./tools/spawn.mjs";
10
+ import { SubagentManager } from "./subagent.mjs";
11
11
  import { CronTool } from "./tools/cron.mjs";
12
12
  import { FlexTool } from "./tools/flex.mjs";
13
- import { SubagentManager } from "./subagent.mjs";
14
- import { SessionManager } from "../session/manager.mjs";
13
+ import { MessageTool } from "./tools/message.mjs";
14
+ import { SpawnTool } from "./tools/spawn.mjs";
15
15
 
16
16
  //#region src/agent/loop.ts
17
17
  /**
@@ -39,6 +39,8 @@ var AgentLoop = class {
39
39
  _running = false;
40
40
  /** In-flight AbortControllers keyed by session key. */
41
41
  inflight = /* @__PURE__ */ new Map();
42
+ /** Promises that resolve when an aborted processMessage finishes saving. */
43
+ inflightDone = /* @__PURE__ */ new Map();
42
44
  constructor(params) {
43
45
  this.bus = params.bus;
44
46
  this.provider = params.provider;
@@ -54,13 +56,13 @@ var AgentLoop = class {
54
56
  this.sessions = new SessionManager(params.workspace);
55
57
  this.tools = new ToolRegistry();
56
58
  this.subagents = new SubagentManager({
57
- provider: params.provider,
58
- workspace: params.workspace,
59
- bus: params.bus,
60
- model: this.model,
61
59
  braveApiKey: params.braveApiKey,
60
+ bus: params.bus,
62
61
  execConfig,
63
- restrictToWorkspace
62
+ model: this.model,
63
+ provider: params.provider,
64
+ restrictToWorkspace,
65
+ workspace: params.workspace
64
66
  });
65
67
  this.registerDefaultTools(execConfig, restrictToWorkspace, params.braveApiKey, params.toolsEnabled, params.toolsDisabled, params.customTools);
66
68
  }
@@ -85,9 +87,9 @@ var AgentLoop = class {
85
87
  readOnlyPaths
86
88
  }));
87
89
  registerIfEnabled(new ExecTool({
88
- workingDir: this.workspace,
90
+ restrictToWorkspace,
89
91
  timeout: execConfig.timeout,
90
- restrictToWorkspace
92
+ workingDir: this.workspace
91
93
  }));
92
94
  registerIfEnabled(new WebSearchTool({ apiKey: braveApiKey }));
93
95
  registerIfEnabled(new WebFetchTool());
@@ -103,17 +105,19 @@ var AgentLoop = class {
103
105
  console.log("Agent loop started");
104
106
  while (this._running) try {
105
107
  const msg = await this.bus.consumeInboundTimeout(1e3);
106
- this.processMessage(msg).then(async (response) => {
108
+ const sessionKey = `${msg.channel}:${msg.chatId}`;
109
+ const done = this.processMessage(msg).then(async (response) => {
107
110
  if (response) await this.bus.publishOutbound(response);
108
- }).catch(async (err) => {
109
- if (isAbortError(err)) return;
110
- console.error("Error processing message:", err);
111
+ }).catch(async (error) => {
112
+ if (isAbortError(error)) return;
113
+ console.error("Error processing message:", error);
111
114
  await this.bus.publishOutbound(createOutboundMessage({
112
115
  channel: msg.channel,
113
116
  chatId: msg.chatId,
114
- content: `Sorry, I encountered an error: ${err instanceof Error ? err.message : err}`
117
+ content: `Sorry, I encountered an error: ${error instanceof Error ? error.message : error}`
115
118
  }));
116
119
  });
120
+ this.inflightDone.set(sessionKey, done);
117
121
  } catch {}
118
122
  }
119
123
  /** Stop the agent loop. */
@@ -131,17 +135,22 @@ var AgentLoop = class {
131
135
  console.log(`Aborting in-flight request for ${sessionKey}`);
132
136
  existing.abort();
133
137
  }
138
+ const prev = this.inflightDone.get(sessionKey);
139
+ if (prev) {
140
+ await prev;
141
+ this.inflightDone.delete(sessionKey);
142
+ }
134
143
  const controller = new AbortController();
135
144
  this.inflight.set(sessionKey, controller);
136
145
  const session = this.sessions.getOrCreate(sessionKey);
137
146
  if (session.history.length > this.memoryWindow) await this.consolidateMemory(session);
138
147
  this.updateToolContexts(msg.channel, msg.chatId);
139
148
  const messages = this.context.buildMessages({
140
- history: session.getHistory(),
141
- currentMessage: msg.content,
142
- media: msg.media.length > 0 ? msg.media : void 0,
143
149
  channel: msg.channel,
144
- chatId: msg.chatId
150
+ chatId: msg.chatId,
151
+ currentMessage: msg.content,
152
+ history: session.getHistory(),
153
+ media: msg.media.length > 0 ? msg.media : void 0
145
154
  });
146
155
  const newMsgStart = 1 + session.getHistory().length;
147
156
  try {
@@ -159,8 +168,8 @@ var AgentLoop = class {
159
168
  chatId: msg.chatId,
160
169
  content: result.content
161
170
  });
162
- } catch (err) {
163
- if (isAbortError(err)) {
171
+ } catch (error) {
172
+ if (isAbortError(error)) {
164
173
  const userMessages = messages.slice(newMsgStart).filter((m) => m.role === "user");
165
174
  if (userMessages.length > 0) {
166
175
  session.addTurnMessages(userMessages);
@@ -169,7 +178,7 @@ var AgentLoop = class {
169
178
  console.log(`Request aborted for ${sessionKey}, user message saved to history`);
170
179
  return null;
171
180
  }
172
- throw err;
181
+ throw error;
173
182
  } finally {
174
183
  if (this.inflight.get(sessionKey) === controller) this.inflight.delete(sessionKey);
175
184
  }
@@ -190,10 +199,10 @@ var AgentLoop = class {
190
199
  const session = this.sessions.getOrCreate(sessionKey);
191
200
  this.updateToolContexts(originChannel, originChatId);
192
201
  const messages = this.context.buildMessages({
193
- history: session.getHistory(),
194
- currentMessage: msg.content,
195
202
  channel: originChannel,
196
- chatId: originChatId
203
+ chatId: originChatId,
204
+ currentMessage: msg.content,
205
+ history: session.getHistory()
197
206
  });
198
207
  const newMsgStart = 1 + session.getHistory().length;
199
208
  const result = await this.runAgentLoop(messages);
@@ -209,13 +218,13 @@ var AgentLoop = class {
209
218
  let finalContent = null;
210
219
  let sentToolCallNotice = false;
211
220
  const toolsUsed = [];
212
- for (let i = 0; i < this.maxIterations; i++) {
221
+ for (let i = 0; i < this.maxIterations; i += 1) {
213
222
  const response = await this.provider.chat({
223
+ maxTokens: this.maxTokens,
214
224
  messages,
215
- tools: this.tools.getDefinitions(),
216
225
  model: this.model,
217
- maxTokens: this.maxTokens,
218
- signal
226
+ signal,
227
+ tools: this.tools.getDefinitions()
219
228
  });
220
229
  if (response.hasToolCalls) {
221
230
  if (!sentToolCallNotice && onToolCallText) {
@@ -224,12 +233,12 @@ var AgentLoop = class {
224
233
  sentToolCallNotice = true;
225
234
  }
226
235
  const toolCallDicts = response.toolCalls.map((tc) => ({
227
- id: tc.id,
228
- type: "function",
229
236
  function: {
230
237
  name: tc.name,
231
238
  arguments: JSON.stringify(tc.arguments)
232
- }
239
+ },
240
+ id: tc.id,
241
+ type: "function"
233
242
  }));
234
243
  this.context.addAssistantMessage(messages, response.content, toolCallDicts);
235
244
  for (const tc of response.toolCalls) {
@@ -250,26 +259,26 @@ var AgentLoop = class {
250
259
  finalContent = response.content;
251
260
  if ((!finalContent || finalContent.trim().length === 0) && i > 0) {
252
261
  messages.push({
253
- role: "assistant",
254
- content: ""
262
+ content: "",
263
+ role: "assistant"
255
264
  });
256
265
  messages.push({
257
- role: "user",
258
- content: "(You used tools but didn't respond to the user. Please provide a brief response summarizing what you did.)"
266
+ content: "(You used tools but didn't respond to the user. Please provide a brief response summarizing what you did.)",
267
+ role: "user"
259
268
  });
260
269
  continue;
261
270
  }
262
271
  messages.push({
263
- role: "assistant",
264
- content: finalContent ?? ""
272
+ content: finalContent ?? "",
273
+ role: "assistant"
265
274
  });
266
275
  break;
267
276
  }
268
277
  }
269
278
  if (!finalContent || finalContent.trim().length === 0) finalContent = "I've completed processing but have no response to give.";
270
- if (messages[messages.length - 1]?.role !== "assistant" || messages[messages.length - 1]?.content !== finalContent) messages.push({
271
- role: "assistant",
272
- content: finalContent
279
+ if (messages.at(-1)?.role !== "assistant" || messages.at(-1)?.content !== finalContent) messages.push({
280
+ content: finalContent,
281
+ role: "assistant"
273
282
  });
274
283
  return {
275
284
  content: finalContent,
@@ -280,10 +289,10 @@ var AgentLoop = class {
280
289
  getToolCallFallbackText(toolCalls) {
281
290
  const toolNames = toolCalls.map((tc) => tc.name);
282
291
  const fallbacks = {
283
- web_search: "検索中...",
284
- web_fetch: "ページを読み込み中...",
292
+ flex_message: "カードを作成中...",
285
293
  spawn: "サブエージェントを起動中...",
286
- flex_message: "カードを作成中..."
294
+ web_fetch: "ページを読み込み中...",
295
+ web_search: "検索中..."
287
296
  };
288
297
  for (const name of toolNames) if (fallbacks[name]) return fallbacks[name];
289
298
  return "ちょっと待ってね...";
@@ -301,10 +310,10 @@ var AgentLoop = class {
301
310
  const session = this.sessions.getOrCreate(sessionKey);
302
311
  this.updateToolContexts(channel, chatId);
303
312
  const messages = this.context.buildMessages({
304
- history: session.getHistory(),
305
- currentMessage: content,
306
313
  channel,
307
- chatId
314
+ chatId,
315
+ currentMessage: content,
316
+ history: session.getHistory()
308
317
  });
309
318
  const newMsgStart = 1 + session.getHistory().length;
310
319
  const result = await this.runAgentLoop(messages);
@@ -346,11 +355,11 @@ Respond with ONLY valid JSON, no markdown fences.`;
346
355
  try {
347
356
  let text = ((await this.provider.chat({
348
357
  messages: [{
349
- role: "system",
350
- content: "You are a memory consolidation agent. Respond only with valid JSON."
358
+ content: "You are a memory consolidation agent. Respond only with valid JSON.",
359
+ role: "system"
351
360
  }, {
352
- role: "user",
353
- content: prompt
361
+ content: prompt,
362
+ role: "user"
354
363
  }],
355
364
  model: this.consolidationModel
356
365
  })).content || "").trim();
@@ -364,8 +373,8 @@ Respond with ONLY valid JSON, no markdown fences.`;
364
373
  const update = typeof result.memory_update === "string" ? result.memory_update : JSON.stringify(result.memory_update, null, 2);
365
374
  memory.writeLongTerm(update);
366
375
  }
367
- } catch (err) {
368
- console.error("Memory consolidation failed:", err);
376
+ } catch (error) {
377
+ console.error("Memory consolidation failed:", error);
369
378
  const fallbackEntry = `[${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}] Consolidation failed, archiving raw messages:\n${conversation.slice(0, 2e3)}`;
370
379
  memory.appendHistory(fallbackEntry);
371
380
  }