@hera-al/server 1.6.1

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 (348) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +325 -0
  3. package/bundled/apple-notes/SKILL.md +77 -0
  4. package/bundled/apple-reminders/SKILL.md +96 -0
  5. package/bundled/blogwatcher/SKILL.md +69 -0
  6. package/bundled/camsnap/SKILL.md +45 -0
  7. package/bundled/discord/SKILL.md +578 -0
  8. package/bundled/gemini/SKILL.md +43 -0
  9. package/bundled/gifgrep/SKILL.md +79 -0
  10. package/bundled/github/SKILL.md +77 -0
  11. package/bundled/gog/SKILL.md +116 -0
  12. package/bundled/goplaces/SKILL.md +52 -0
  13. package/bundled/himalaya/SKILL.md +257 -0
  14. package/bundled/himalaya/references/configuration.md +184 -0
  15. package/bundled/himalaya/references/message-composition.md +199 -0
  16. package/bundled/homebrew/SKILL.md +82 -0
  17. package/bundled/local-places/SERVER_README.md +101 -0
  18. package/bundled/local-places/SKILL.md +102 -0
  19. package/bundled/local-places/pyproject.toml +21 -0
  20. package/bundled/local-places/src/local_places/__init__.py +2 -0
  21. package/bundled/local-places/src/local_places/google_places.py +314 -0
  22. package/bundled/local-places/src/local_places/main.py +65 -0
  23. package/bundled/local-places/src/local_places/schemas.py +107 -0
  24. package/bundled/markitdown/SKILL.md +96 -0
  25. package/bundled/mcporter/SKILL.md +61 -0
  26. package/bundled/merge-pr/SKILL.md +187 -0
  27. package/bundled/merge-pr/agents/openai.yaml +4 -0
  28. package/bundled/nano-banana-pro/SKILL.md +58 -0
  29. package/bundled/nano-banana-pro/scripts/generate_image.py +184 -0
  30. package/bundled/nano-pdf/SKILL.md +38 -0
  31. package/bundled/open-prose/README.md +25 -0
  32. package/bundled/open-prose/index.ts +5 -0
  33. package/bundled/open-prose/openclaw.plugin.json +11 -0
  34. package/bundled/open-prose/package.json +15 -0
  35. package/bundled/open-prose/skills/prose/LICENSE +21 -0
  36. package/bundled/open-prose/skills/prose/SKILL.md +323 -0
  37. package/bundled/open-prose/skills/prose/alt-borges.md +141 -0
  38. package/bundled/open-prose/skills/prose/alts/arabian-nights.md +358 -0
  39. package/bundled/open-prose/skills/prose/alts/borges.md +360 -0
  40. package/bundled/open-prose/skills/prose/alts/folk.md +322 -0
  41. package/bundled/open-prose/skills/prose/alts/homer.md +346 -0
  42. package/bundled/open-prose/skills/prose/alts/kafka.md +373 -0
  43. package/bundled/open-prose/skills/prose/compiler.md +2971 -0
  44. package/bundled/open-prose/skills/prose/examples/01-hello-world.prose +4 -0
  45. package/bundled/open-prose/skills/prose/examples/02-research-and-summarize.prose +6 -0
  46. package/bundled/open-prose/skills/prose/examples/03-code-review.prose +17 -0
  47. package/bundled/open-prose/skills/prose/examples/04-write-and-refine.prose +14 -0
  48. package/bundled/open-prose/skills/prose/examples/05-debug-issue.prose +20 -0
  49. package/bundled/open-prose/skills/prose/examples/06-explain-codebase.prose +17 -0
  50. package/bundled/open-prose/skills/prose/examples/07-refactor.prose +20 -0
  51. package/bundled/open-prose/skills/prose/examples/08-blog-post.prose +20 -0
  52. package/bundled/open-prose/skills/prose/examples/09-research-with-agents.prose +25 -0
  53. package/bundled/open-prose/skills/prose/examples/10-code-review-agents.prose +32 -0
  54. package/bundled/open-prose/skills/prose/examples/11-skills-and-imports.prose +27 -0
  55. package/bundled/open-prose/skills/prose/examples/12-secure-agent-permissions.prose +43 -0
  56. package/bundled/open-prose/skills/prose/examples/13-variables-and-context.prose +51 -0
  57. package/bundled/open-prose/skills/prose/examples/14-composition-blocks.prose +48 -0
  58. package/bundled/open-prose/skills/prose/examples/15-inline-sequences.prose +23 -0
  59. package/bundled/open-prose/skills/prose/examples/16-parallel-reviews.prose +19 -0
  60. package/bundled/open-prose/skills/prose/examples/17-parallel-research.prose +19 -0
  61. package/bundled/open-prose/skills/prose/examples/18-mixed-parallel-sequential.prose +36 -0
  62. package/bundled/open-prose/skills/prose/examples/19-advanced-parallel.prose +71 -0
  63. package/bundled/open-prose/skills/prose/examples/20-fixed-loops.prose +20 -0
  64. package/bundled/open-prose/skills/prose/examples/21-pipeline-operations.prose +35 -0
  65. package/bundled/open-prose/skills/prose/examples/22-error-handling.prose +51 -0
  66. package/bundled/open-prose/skills/prose/examples/23-retry-with-backoff.prose +63 -0
  67. package/bundled/open-prose/skills/prose/examples/24-choice-blocks.prose +86 -0
  68. package/bundled/open-prose/skills/prose/examples/25-conditionals.prose +114 -0
  69. package/bundled/open-prose/skills/prose/examples/26-parameterized-blocks.prose +100 -0
  70. package/bundled/open-prose/skills/prose/examples/27-string-interpolation.prose +105 -0
  71. package/bundled/open-prose/skills/prose/examples/28-automated-pr-review.prose +37 -0
  72. package/bundled/open-prose/skills/prose/examples/28-gas-town.prose +1572 -0
  73. package/bundled/open-prose/skills/prose/examples/29-captains-chair.prose +218 -0
  74. package/bundled/open-prose/skills/prose/examples/30-captains-chair-simple.prose +42 -0
  75. package/bundled/open-prose/skills/prose/examples/31-captains-chair-with-memory.prose +145 -0
  76. package/bundled/open-prose/skills/prose/examples/33-pr-review-autofix.prose +168 -0
  77. package/bundled/open-prose/skills/prose/examples/34-content-pipeline.prose +204 -0
  78. package/bundled/open-prose/skills/prose/examples/35-feature-factory.prose +296 -0
  79. package/bundled/open-prose/skills/prose/examples/36-bug-hunter.prose +237 -0
  80. package/bundled/open-prose/skills/prose/examples/37-the-forge.prose +1474 -0
  81. package/bundled/open-prose/skills/prose/examples/38-skill-scan.prose +455 -0
  82. package/bundled/open-prose/skills/prose/examples/39-architect-by-simulation.prose +277 -0
  83. package/bundled/open-prose/skills/prose/examples/40-rlm-self-refine.prose +32 -0
  84. package/bundled/open-prose/skills/prose/examples/41-rlm-divide-conquer.prose +38 -0
  85. package/bundled/open-prose/skills/prose/examples/42-rlm-filter-recurse.prose +46 -0
  86. package/bundled/open-prose/skills/prose/examples/43-rlm-pairwise.prose +50 -0
  87. package/bundled/open-prose/skills/prose/examples/44-run-endpoint-ux-test.prose +261 -0
  88. package/bundled/open-prose/skills/prose/examples/45-plugin-release.prose +159 -0
  89. package/bundled/open-prose/skills/prose/examples/45-run-endpoint-ux-test-with-remediation.prose +637 -0
  90. package/bundled/open-prose/skills/prose/examples/46-run-endpoint-ux-test-fast.prose +148 -0
  91. package/bundled/open-prose/skills/prose/examples/46-workflow-crystallizer.prose +225 -0
  92. package/bundled/open-prose/skills/prose/examples/47-language-self-improvement.prose +356 -0
  93. package/bundled/open-prose/skills/prose/examples/48-habit-miner.prose +445 -0
  94. package/bundled/open-prose/skills/prose/examples/49-prose-run-retrospective.prose +210 -0
  95. package/bundled/open-prose/skills/prose/examples/README.md +391 -0
  96. package/bundled/open-prose/skills/prose/examples/roadmap/README.md +22 -0
  97. package/bundled/open-prose/skills/prose/examples/roadmap/iterative-refinement.prose +20 -0
  98. package/bundled/open-prose/skills/prose/examples/roadmap/parallel-review.prose +18 -0
  99. package/bundled/open-prose/skills/prose/examples/roadmap/simple-pipeline.prose +17 -0
  100. package/bundled/open-prose/skills/prose/examples/roadmap/syntax/open-prose-syntax.prose +223 -0
  101. package/bundled/open-prose/skills/prose/guidance/antipatterns.md +951 -0
  102. package/bundled/open-prose/skills/prose/guidance/patterns.md +700 -0
  103. package/bundled/open-prose/skills/prose/guidance/system-prompt.md +180 -0
  104. package/bundled/open-prose/skills/prose/help.md +144 -0
  105. package/bundled/open-prose/skills/prose/lib/README.md +108 -0
  106. package/bundled/open-prose/skills/prose/lib/calibrator.prose +215 -0
  107. package/bundled/open-prose/skills/prose/lib/cost-analyzer.prose +174 -0
  108. package/bundled/open-prose/skills/prose/lib/error-forensics.prose +250 -0
  109. package/bundled/open-prose/skills/prose/lib/inspector.prose +196 -0
  110. package/bundled/open-prose/skills/prose/lib/profiler.prose +460 -0
  111. package/bundled/open-prose/skills/prose/lib/program-improver.prose +275 -0
  112. package/bundled/open-prose/skills/prose/lib/project-memory.prose +118 -0
  113. package/bundled/open-prose/skills/prose/lib/user-memory.prose +93 -0
  114. package/bundled/open-prose/skills/prose/lib/vm-improver.prose +243 -0
  115. package/bundled/open-prose/skills/prose/primitives/session.md +593 -0
  116. package/bundled/open-prose/skills/prose/prose.md +1237 -0
  117. package/bundled/open-prose/skills/prose/state/filesystem.md +498 -0
  118. package/bundled/open-prose/skills/prose/state/in-context.md +384 -0
  119. package/bundled/open-prose/skills/prose/state/postgres.md +880 -0
  120. package/bundled/open-prose/skills/prose/state/sqlite.md +574 -0
  121. package/bundled/peekaboo/SKILL.md +190 -0
  122. package/bundled/prepare-pr/SKILL.md +277 -0
  123. package/bundled/prepare-pr/agents/openai.yaml +4 -0
  124. package/bundled/review-pr/SKILL.md +228 -0
  125. package/bundled/review-pr/agents/openai.yaml +4 -0
  126. package/bundled/sag/SKILL.md +87 -0
  127. package/bundled/skill-creator/SKILL.md +370 -0
  128. package/bundled/skill-creator/license.txt +202 -0
  129. package/bundled/skill-creator/scripts/init_skill.py +378 -0
  130. package/bundled/skill-creator/scripts/package_skill.py +111 -0
  131. package/bundled/skill-creator/scripts/quick_validate.py +101 -0
  132. package/bundled/spotify-player/SKILL.md +64 -0
  133. package/bundled/ssh/SKILL.md +119 -0
  134. package/bundled/summarize/SKILL.md +87 -0
  135. package/bundled/video-frames/SKILL.md +46 -0
  136. package/bundled/video-frames/scripts/frame.sh +81 -0
  137. package/bundled/voice-call/SKILL.md +45 -0
  138. package/bundled/wacli/SKILL.md +72 -0
  139. package/bundled/weather/SKILL.md +54 -0
  140. package/dist/agent/agent-service.d.ts +88 -0
  141. package/dist/agent/agent-service.js +1 -0
  142. package/dist/agent/message-queue.d.ts +24 -0
  143. package/dist/agent/message-queue.js +1 -0
  144. package/dist/agent/prompt-builder.d.ts +58 -0
  145. package/dist/agent/prompt-builder.js +1 -0
  146. package/dist/agent/session-agent.d.ts +197 -0
  147. package/dist/agent/session-agent.js +1 -0
  148. package/dist/agent/session-db.d.ts +26 -0
  149. package/dist/agent/session-db.js +1 -0
  150. package/dist/agent/session-error-handler.d.ts +37 -0
  151. package/dist/agent/session-error-handler.js +1 -0
  152. package/dist/agent/session-manager.d.ts +19 -0
  153. package/dist/agent/session-manager.js +1 -0
  154. package/dist/agent/workspace-files.d.ts +51 -0
  155. package/dist/agent/workspace-files.js +1 -0
  156. package/dist/auth/auth-middleware.d.ts +9 -0
  157. package/dist/auth/auth-middleware.js +1 -0
  158. package/dist/auth/node-signature-db.d.ts +30 -0
  159. package/dist/auth/node-signature-db.js +1 -0
  160. package/dist/auth/token-db.d.ts +38 -0
  161. package/dist/auth/token-db.js +1 -0
  162. package/dist/browser/browser-service.d.ts +9 -0
  163. package/dist/browser/browser-service.js +1 -0
  164. package/dist/channels/channel.d.ts +2 -0
  165. package/dist/channels/channel.js +1 -0
  166. package/dist/channels/responses.d.ts +21 -0
  167. package/dist/channels/responses.js +1 -0
  168. package/dist/commands/clear.d.ts +7 -0
  169. package/dist/commands/clear.js +1 -0
  170. package/dist/commands/cmd.d.ts +7 -0
  171. package/dist/commands/cmd.js +1 -0
  172. package/dist/commands/coder.d.ts +12 -0
  173. package/dist/commands/coder.js +1 -0
  174. package/dist/commands/command-registry.d.ts +12 -0
  175. package/dist/commands/command-registry.js +1 -0
  176. package/dist/commands/command.d.ts +22 -0
  177. package/dist/commands/command.js +1 -0
  178. package/dist/commands/compact.d.ts +7 -0
  179. package/dist/commands/compact.js +1 -0
  180. package/dist/commands/customsubagents.d.ts +15 -0
  181. package/dist/commands/customsubagents.js +1 -0
  182. package/dist/commands/help.d.ts +9 -0
  183. package/dist/commands/help.js +1 -0
  184. package/dist/commands/mcp.d.ts +9 -0
  185. package/dist/commands/mcp.js +1 -0
  186. package/dist/commands/model.d.ts +22 -0
  187. package/dist/commands/model.js +1 -0
  188. package/dist/commands/models.d.ts +11 -0
  189. package/dist/commands/models.js +1 -0
  190. package/dist/commands/new.d.ts +7 -0
  191. package/dist/commands/new.js +1 -0
  192. package/dist/commands/plugin.d.ts +7 -0
  193. package/dist/commands/plugin.js +1 -0
  194. package/dist/commands/sandbox.d.ts +12 -0
  195. package/dist/commands/sandbox.js +1 -0
  196. package/dist/commands/showtool.d.ts +12 -0
  197. package/dist/commands/showtool.js +1 -0
  198. package/dist/commands/status.d.ts +24 -0
  199. package/dist/commands/status.js +1 -0
  200. package/dist/commands/stop.d.ts +10 -0
  201. package/dist/commands/stop.js +1 -0
  202. package/dist/commands/subagents.d.ts +12 -0
  203. package/dist/commands/subagents.js +1 -0
  204. package/dist/commands/usage.d.ts +25 -0
  205. package/dist/commands/usage.js +1 -0
  206. package/dist/commands/useplugin.d.ts +7 -0
  207. package/dist/commands/useplugin.js +1 -0
  208. package/dist/config-watcher.d.ts +14 -0
  209. package/dist/config-watcher.js +1 -0
  210. package/dist/config.d.ts +267 -0
  211. package/dist/config.js +1 -0
  212. package/dist/cron/cron-service.d.ts +57 -0
  213. package/dist/cron/cron-service.js +1 -0
  214. package/dist/cron/heartbeat-token.d.ts +29 -0
  215. package/dist/cron/heartbeat-token.js +1 -0
  216. package/dist/cron/schedule.d.ts +3 -0
  217. package/dist/cron/schedule.js +1 -0
  218. package/dist/cron/store.d.ts +4 -0
  219. package/dist/cron/store.js +1 -0
  220. package/dist/cron/types.d.ts +47 -0
  221. package/dist/cron/types.js +1 -0
  222. package/dist/gateway/bridge.d.ts +38 -0
  223. package/dist/gateway/bridge.js +1 -0
  224. package/dist/gateway/channel-manager.d.ts +45 -0
  225. package/dist/gateway/channel-manager.js +1 -0
  226. package/dist/gateway/channels/qr-image.d.ts +5 -0
  227. package/dist/gateway/channels/qr-image.js +1 -0
  228. package/dist/gateway/channels/telegram.d.ts +39 -0
  229. package/dist/gateway/channels/telegram.js +1 -0
  230. package/dist/gateway/channels/webchat.d.ts +51 -0
  231. package/dist/gateway/channels/webchat.js +1 -0
  232. package/dist/gateway/channels/whatsapp.d.ts +40 -0
  233. package/dist/gateway/channels/whatsapp.js +1 -0
  234. package/dist/gateway/node-registry.d.ts +38 -0
  235. package/dist/gateway/node-registry.js +1 -0
  236. package/dist/heracli/index.d.ts +3 -0
  237. package/dist/heracli/index.js +2 -0
  238. package/dist/heracli/logs.d.ts +13 -0
  239. package/dist/heracli/logs.js +1 -0
  240. package/dist/heracli/security/audit.d.ts +17 -0
  241. package/dist/heracli/security/audit.js +1 -0
  242. package/dist/heracli/security/checks/channel-policies.d.ts +6 -0
  243. package/dist/heracli/security/checks/channel-policies.js +1 -0
  244. package/dist/heracli/security/checks/credentials.d.ts +6 -0
  245. package/dist/heracli/security/checks/credentials.js +1 -0
  246. package/dist/heracli/security/checks/fs-permissions.d.ts +6 -0
  247. package/dist/heracli/security/checks/fs-permissions.js +1 -0
  248. package/dist/heracli/security/checks/network.d.ts +4 -0
  249. package/dist/heracli/security/checks/network.js +1 -0
  250. package/dist/heracli/security/report.d.ts +4 -0
  251. package/dist/heracli/security/report.js +1 -0
  252. package/dist/index.d.ts +3 -0
  253. package/dist/index.js +2 -0
  254. package/dist/installer/hera.d.ts +3 -0
  255. package/dist/installer/hera.js +2 -0
  256. package/dist/media/message-processor.d.ts +23 -0
  257. package/dist/media/message-processor.js +1 -0
  258. package/dist/memory/memory-manager.d.ts +21 -0
  259. package/dist/memory/memory-manager.js +1 -0
  260. package/dist/memory/memory-provider.d.ts +22 -0
  261. package/dist/memory/memory-provider.js +1 -0
  262. package/dist/memory/memory-search.d.ts +102 -0
  263. package/dist/memory/memory-search.js +1 -0
  264. package/dist/memory/recall-strategies.d.ts +2 -0
  265. package/dist/memory/recall-strategies.js +1 -0
  266. package/dist/nostromo/auth.d.ts +29 -0
  267. package/dist/nostromo/auth.js +1 -0
  268. package/dist/nostromo/nostromo.d.ts +23 -0
  269. package/dist/nostromo/nostromo.js +1 -0
  270. package/dist/nostromo/ui-html-layout.d.ts +3 -0
  271. package/dist/nostromo/ui-html-layout.js +1 -0
  272. package/dist/nostromo/ui-html-modals.d.ts +3 -0
  273. package/dist/nostromo/ui-html-modals.js +1 -0
  274. package/dist/nostromo/ui-js-agent.d.ts +3 -0
  275. package/dist/nostromo/ui-js-agent.js +1 -0
  276. package/dist/nostromo/ui-js-channels.d.ts +3 -0
  277. package/dist/nostromo/ui-js-channels.js +1 -0
  278. package/dist/nostromo/ui-js-competences.d.ts +3 -0
  279. package/dist/nostromo/ui-js-competences.js +1 -0
  280. package/dist/nostromo/ui-js-config.d.ts +3 -0
  281. package/dist/nostromo/ui-js-config.js +1 -0
  282. package/dist/nostromo/ui-js-core.d.ts +3 -0
  283. package/dist/nostromo/ui-js-core.js +1 -0
  284. package/dist/nostromo/ui-js-ops.d.ts +3 -0
  285. package/dist/nostromo/ui-js-ops.js +1 -0
  286. package/dist/nostromo/ui-js-prompts.d.ts +3 -0
  287. package/dist/nostromo/ui-js-prompts.js +1 -0
  288. package/dist/nostromo/ui-styles.d.ts +3 -0
  289. package/dist/nostromo/ui-styles.js +1 -0
  290. package/dist/nostromo/ui.d.ts +2 -0
  291. package/dist/nostromo/ui.js +1 -0
  292. package/dist/server.d.ts +80 -0
  293. package/dist/server.js +1 -0
  294. package/dist/stt/local-whisper.d.ts +9 -0
  295. package/dist/stt/local-whisper.js +1 -0
  296. package/dist/stt/openai-whisper.d.ts +14 -0
  297. package/dist/stt/openai-whisper.js +1 -0
  298. package/dist/stt/stt-loader.d.ts +4 -0
  299. package/dist/stt/stt-loader.js +1 -0
  300. package/dist/stt/stt-provider.d.ts +4 -0
  301. package/dist/stt/stt-provider.js +1 -0
  302. package/dist/tools/browser-tools.d.ts +9 -0
  303. package/dist/tools/browser-tools.js +1 -0
  304. package/dist/tools/cron-tools.d.ts +4 -0
  305. package/dist/tools/cron-tools.js +1 -0
  306. package/dist/tools/memory-tools.d.ts +3 -0
  307. package/dist/tools/memory-tools.js +1 -0
  308. package/dist/tools/message-tools.d.ts +5 -0
  309. package/dist/tools/message-tools.js +1 -0
  310. package/dist/tools/node-tools.d.ts +3 -0
  311. package/dist/tools/node-tools.js +1 -0
  312. package/dist/tools/server-tools.d.ts +2 -0
  313. package/dist/tools/server-tools.js +1 -0
  314. package/dist/tools/tts-tools.d.ts +3 -0
  315. package/dist/tools/tts-tools.js +1 -0
  316. package/dist/tts/tts-service.d.ts +19 -0
  317. package/dist/tts/tts-service.js +1 -0
  318. package/dist/utils/chunk.d.ts +3 -0
  319. package/dist/utils/chunk.js +1 -0
  320. package/dist/utils/logger.d.ts +16 -0
  321. package/dist/utils/logger.js +1 -0
  322. package/dist/utils/markdown/fences.d.ts +11 -0
  323. package/dist/utils/markdown/fences.js +1 -0
  324. package/dist/utils/markdown/ir.d.ts +33 -0
  325. package/dist/utils/markdown/ir.js +1 -0
  326. package/dist/utils/markdown/render.d.ts +19 -0
  327. package/dist/utils/markdown/render.js +1 -0
  328. package/dist/utils/markdown/tables.d.ts +3 -0
  329. package/dist/utils/markdown/tables.js +1 -0
  330. package/dist/utils/media-response.d.ts +29 -0
  331. package/dist/utils/media-response.js +1 -0
  332. package/dist/utils/package-paths.d.ts +5 -0
  333. package/dist/utils/package-paths.js +1 -0
  334. package/dist/utils/telegram-format.d.ts +13 -0
  335. package/dist/utils/telegram-format.js +1 -0
  336. package/installationPkg/.env.example +26 -0
  337. package/installationPkg/AGENTS.md +143 -0
  338. package/installationPkg/BOOTSTRAP.md +45 -0
  339. package/installationPkg/CBINT.json +16 -0
  340. package/installationPkg/HEARTBEAT.md +5 -0
  341. package/installationPkg/IDENTITY.md +7 -0
  342. package/installationPkg/SOUL.md +36 -0
  343. package/installationPkg/SYSTEM_PROMPT.md +55 -0
  344. package/installationPkg/SYSTEM_PROMPT_SUBAGENT.md +40 -0
  345. package/installationPkg/TOOLS.md +36 -0
  346. package/installationPkg/USER.md +11 -0
  347. package/installationPkg/config.example.yaml +291 -0
  348. package/package.json +95 -0
@@ -0,0 +1,80 @@
1
+ import type { AppConfig } from "./config.js";
2
+ import { TokenDB } from "./auth/token-db.js";
3
+ import { NodeSignatureDB } from "./auth/node-signature-db.js";
4
+ import { SessionDB } from "./agent/session-db.js";
5
+ import { ChannelManager } from "./gateway/channel-manager.js";
6
+ import { WebChatChannel } from "./gateway/channels/webchat.js";
7
+ import { AgentService } from "./agent/agent-service.js";
8
+ import { NodeRegistry } from "./gateway/node-registry.js";
9
+ import { MemoryManager } from "./memory/memory-manager.js";
10
+ import type { IncomingMessage } from "./gateway/bridge.js";
11
+ import { CronService } from "./cron/cron-service.js";
12
+ export declare class Server {
13
+ private config;
14
+ private tokenDb;
15
+ private sessionDb;
16
+ private nodeSignatureDb;
17
+ private channelManager;
18
+ private agentService;
19
+ private sessionManager;
20
+ private memoryManager;
21
+ private nodeRegistry;
22
+ private messageProcessor;
23
+ private commandRegistry;
24
+ private serverToolsServer;
25
+ private coderSkill;
26
+ private showToolUse;
27
+ private subagentsEnabled;
28
+ private customSubAgentsEnabled;
29
+ private chatSettings;
30
+ private cronService;
31
+ private browserService;
32
+ private memorySearch;
33
+ private whatsappQr;
34
+ private whatsappConnected;
35
+ private whatsappError;
36
+ private webChatChannel;
37
+ private autoRenewTimer;
38
+ constructor(config: AppConfig);
39
+ private getChatSetting;
40
+ private setChatSetting;
41
+ private createCronService;
42
+ private createMemorySearch;
43
+ private stopMemorySearch;
44
+ private collectBroadcastTargets;
45
+ private executeCronJob;
46
+ private setupCommands;
47
+ private registerChannels;
48
+ handleMessage(msg: IncomingMessage): Promise<string>;
49
+ start(): Promise<void>;
50
+ private initCronAndHeartbeat;
51
+ getConfig(): AppConfig;
52
+ getTokenDb(): TokenDB;
53
+ getSessionDb(): SessionDB;
54
+ getMemoryManager(): MemoryManager | null;
55
+ getNodeRegistry(): NodeRegistry;
56
+ getNodeSignatureDb(): NodeSignatureDB;
57
+ getChannelManager(): ChannelManager;
58
+ getAgentService(): AgentService;
59
+ getCronService(): CronService | null;
60
+ getCoderSkill(): boolean;
61
+ getWhatsAppQrState(): {
62
+ dataUrl: string | null;
63
+ connected: boolean;
64
+ error?: string;
65
+ };
66
+ getWebChatChannel(): WebChatChannel | null;
67
+ triggerRestart(): Promise<void>;
68
+ /**
69
+ * Send a message to all known chats across all active channels.
70
+ * Uses the same target collection as cron broadcast (allowFrom + session DB).
71
+ */
72
+ private notifyAllChannels;
73
+ private static readonly AUTO_RENEW_CHECK_INTERVAL_MS;
74
+ private startAutoRenewTimer;
75
+ private stopAutoRenewTimer;
76
+ private autoRenewStaleSessions;
77
+ reconfigure(newConfig: AppConfig): Promise<void>;
78
+ stop(): Promise<void>;
79
+ }
80
+ //# sourceMappingURL=server.d.ts.map
package/dist/server.js ADDED
@@ -0,0 +1 @@
1
+ import{readFileSync as e,mkdirSync as t,existsSync as s}from"node:fs";import{join as n}from"node:path";import{TokenDB as r}from"./auth/token-db.js";import{NodeSignatureDB as i}from"./auth/node-signature-db.js";import{SessionDB as o}from"./agent/session-db.js";import{ChannelManager as a}from"./gateway/channel-manager.js";import{TelegramChannel as h}from"./gateway/channels/telegram.js";import{WhatsAppChannel as c}from"./gateway/channels/whatsapp.js";import{WebChatChannel as g}from"./gateway/channels/webchat.js";import{ResponsesChannel as l}from"./channels/responses.js";import{AgentService as m}from"./agent/agent-service.js";import{SessionManager as d}from"./agent/session-manager.js";import{buildPrompt as u,buildSystemPrompt as p}from"./agent/prompt-builder.js";import{ensureWorkspaceFiles as f,loadWorkspaceFiles as b}from"./agent/workspace-files.js";import{NodeRegistry as S}from"./gateway/node-registry.js";import{MemoryManager as y}from"./memory/memory-manager.js";import{MessageProcessor as w}from"./media/message-processor.js";import{loadSTTProvider as v}from"./stt/stt-loader.js";import{CommandRegistry as M}from"./commands/command-registry.js";import{NewCommand as C}from"./commands/new.js";import{CompactCommand as R}from"./commands/compact.js";import{ModelCommand as T}from"./commands/model.js";import{StopCommand as A}from"./commands/stop.js";import{HelpCommand as k}from"./commands/help.js";import{McpCommand as x}from"./commands/mcp.js";import{ModelsCommand as j}from"./commands/models.js";import{CoderCommand as $}from"./commands/coder.js";import{SandboxCommand as E}from"./commands/sandbox.js";import{SubAgentsCommand as D}from"./commands/subagents.js";import{CustomSubAgentsCommand as I}from"./commands/customsubagents.js";import{StatusCommand as _}from"./commands/status.js";import{ShowToolCommand as U}from"./commands/showtool.js";import{UsageCommand as N}from"./commands/usage.js";import{CronService as P}from"./cron/cron-service.js";import{stripHeartbeatToken as K,isHeartbeatContentEffectivelyEmpty as H}from"./cron/heartbeat-token.js";import{createServerToolsServer as O}from"./tools/server-tools.js";import{createCronToolsServer as B}from"./tools/cron-tools.js";import{createTTSToolsServer as F}from"./tools/tts-tools.js";import{createMemoryToolsServer as L}from"./tools/memory-tools.js";import{createBrowserToolsServer as Q}from"./tools/browser-tools.js";import{BrowserService as W}from"./browser/browser-service.js";import{MemorySearch as z}from"./memory/memory-search.js";import{stripMediaLines as G}from"./utils/media-response.js";import{loadConfig as q}from"./config.js";import{createLogger as V}from"./utils/logger.js";import{SessionErrorHandler as J}from"./agent/session-error-handler.js";const X=V("Server");export class Server{config;tokenDb;sessionDb;nodeSignatureDb;channelManager;agentService;sessionManager;memoryManager=null;nodeRegistry;messageProcessor;commandRegistry;serverToolsServer;coderSkill;showToolUse=!1;subagentsEnabled=!0;customSubAgentsEnabled=!1;chatSettings=new Map;cronService=null;browserService;memorySearch=null;whatsappQr=null;whatsappConnected=!1;whatsappError;webChatChannel=null;autoRenewTimer=null;constructor(e){this.config=e,this.coderSkill=e.agent.builtinCoderSkill,this.tokenDb=new r(e.dbPath),this.sessionDb=new o(e.dbPath),this.nodeSignatureDb=new i(e.dbPath),this.nodeRegistry=new S,this.sessionManager=new d(this.sessionDb),e.memory.enabled&&(this.memoryManager=new y(e.memoryDir));const s=v(e),h=this.memoryManager?this.memoryManager.saveFile.bind(this.memoryManager):null;this.messageProcessor=new w(s,h),this.commandRegistry=new M,this.setupCommands(),this.channelManager=new a(e,this.tokenDb,e=>this.handleMessage(e)),this.registerChannels(),this.serverToolsServer=O(()=>this.triggerRestart(),e.timezone),this.cronService=this.createCronService();const c=this.createMemorySearch(e);this.browserService=new W;const g=e.browser?.enabled?Q({nodeRegistry:this.nodeRegistry,config:e}):void 0,l=this.cronService?B(this.cronService,()=>this.config):void 0,u=e.tts.enabled?F(()=>this.config):void 0;this.agentService=new m(e,this.nodeRegistry,this.channelManager,this.serverToolsServer,l,this.sessionDb,u,c,e=>this.getChatSetting(e,"showToolUse")??this.showToolUse,g),f(e.dataDir),t(n(e.agent.workspacePath,".claude","skills"),{recursive:!0}),t(n(e.agent.workspacePath,".claude","commands"),{recursive:!0}),t(n(e.agent.workspacePath,".plugins"),{recursive:!0})}getChatSetting(e,t){return this.chatSettings.get(e)?.[t]}setChatSetting(e,t,s){const n=this.chatSettings.get(e)??{};n[t]=s,this.chatSettings.set(e,n)}createCronService(){return new P({storePath:this.config.cronStorePath,enabled:this.config.cron.enabled,defaultTimezone:this.config.timezone,onExecute:e=>this.executeCronJob(e)})}createMemorySearch(e){if(!e.memory.enabled||!e.memory.search.enabled)return;const t=e.memory.search,s=e.models?.find(e=>e.name===t.modelRef),n=(s?.useEnvVar?process.env[s.useEnvVar]:s?.apiKey)||"",r=s?.baseURL||"";if(n)return this.memorySearch=new z(e.memoryDir,e.dataDir,{apiKey:n,baseURL:r||void 0,embeddingModel:t.embeddingModel,embeddingDimensions:t.embeddingDimensions,prefixQuery:t.prefixQuery,prefixDocument:t.prefixDocument,updateDebounceMs:t.updateDebounceMs,embedIntervalMs:t.embedIntervalMs,maxResults:t.maxResults,maxSnippetChars:t.maxSnippetChars,maxInjectedChars:t.maxInjectedChars,rrfK:t.rrfK}),L(this.memorySearch);X.warn(`Memory search enabled but no API key found for modelRef "${t.modelRef}". Search will not start.`)}stopMemorySearch(){this.memorySearch&&(this.memorySearch.stop(),this.memorySearch=null)}collectBroadcastTargets(){const e=new Set,t=[],s=(s,n)=>{const r=`${s}:${n}`;e.has(r)||(e.add(r),t.push({channel:s,chatId:n}))},n=this.config.channels;for(const[e,t]of Object.entries(n)){if("responses"===e)continue;if(!t?.enabled||!this.channelManager.getAdapter(e))continue;const n=t.accounts;if(n)for(const t of Object.values(n)){const n=t?.allowFrom;if(Array.isArray(n))for(const t of n){const n=String(t).trim();n&&s(e,n)}}}for(const e of this.sessionDb.listSessions()){const t=e.sessionKey.indexOf(":");if(t<0)continue;const n=e.sessionKey.substring(0,t),r=e.sessionKey.substring(t+1);"cron"!==n&&r&&(this.channelManager.getAdapter(n)&&s(n,r))}return t}async executeCronJob(t){const r=this.config.cron.broadcastEvents;if(!r&&!this.channelManager.getAdapter(t.channel))return X.warn(`Cron job "${t.name}": skipped (channel "${t.channel}" is not active)`),{response:"",delivered:!1};if(t.suppressToken&&"__heartbeat"===t.name){const r=n(this.config.dataDir,"HEARTBEAT.md");if(s(r))try{const s=e(r,"utf-8");if(H(s))return X.info(`Cron job "${t.name}": skipped (HEARTBEAT.md is empty)`),{response:"",delivered:!1}}catch{}}const i="boolean"==typeof t.isolated?t.isolated:this.config.cron.isolated,o=i?"cron":t.channel,a=i?t.name:t.chatId;X.info(`Cron job "${t.name}": session=${o}:${a}, delivery=${t.channel}:${t.chatId}${r?" (broadcast)":""}`);const h={chatId:a,userId:"cron",channelName:o,text:t.message,attachments:[]},c=await this.handleMessage(h);let g=c;if(t.suppressToken){const e=this.config.cron.heartbeat.ackMaxChars,{shouldSkip:s,text:n}=K(c,e);if(s)return X.info(`Cron job "${t.name}": response suppressed (HEARTBEAT_OK)`),{response:c,delivered:!1};g=n}if(r){const e=this.collectBroadcastTargets();X.info(`Cron job "${t.name}": broadcasting to ${e.length} target(s)`),await Promise.allSettled(e.map(e=>this.channelManager.sendResponse(e.channel,e.chatId,g)))}else await this.channelManager.sendResponse(t.channel,t.chatId,g);return{response:g,delivered:!0}}setupCommands(){this.commandRegistry.register(new C),this.commandRegistry.register(new R),this.commandRegistry.register(new T(()=>this.config.models??[],async(e,t)=>{this.sessionManager.setModel(e,t)})),this.commandRegistry.register(new j(()=>(this.config.models??[]).filter(e=>{const t=e.types||["external"];return t.includes("internal")||t.includes("external")&&e.proxy&&"not-used"!==e.proxy}))),this.commandRegistry.register(new $(e=>this.getChatSetting(e,"coderSkill")??this.coderSkill,(e,t)=>this.setChatSetting(e,"coderSkill",t))),this.commandRegistry.register(new E(e=>this.getChatSetting(e,"sandboxEnabled")??!1,(e,t)=>this.setChatSetting(e,"sandboxEnabled",t))),this.commandRegistry.register(new U(e=>this.getChatSetting(e,"showToolUse")??this.showToolUse,(e,t)=>this.setChatSetting(e,"showToolUse",t))),this.commandRegistry.register(new D(e=>this.getChatSetting(e,"subagentsEnabled")??this.subagentsEnabled,(e,t)=>this.setChatSetting(e,"subagentsEnabled",t))),this.commandRegistry.register(new I(e=>this.getChatSetting(e,"customSubAgentsEnabled")??this.customSubAgentsEnabled,(e,t)=>this.setChatSetting(e,"customSubAgentsEnabled",t),()=>this.config)),this.commandRegistry.register(new _(e=>({agentModel:this.sessionManager.getModel(e)||this.config.agent.model,fallbackModel:this.config.agent.mainFallback,coderSkill:this.getChatSetting(e,"coderSkill")??this.coderSkill,showToolUse:this.getChatSetting(e,"showToolUse")??this.showToolUse,subagentsEnabled:this.getChatSetting(e,"subagentsEnabled")??this.subagentsEnabled,customSubAgentsEnabled:this.getChatSetting(e,"customSubAgentsEnabled")??this.customSubAgentsEnabled,sandboxEnabled:this.getChatSetting(e,"sandboxEnabled")??!1,connectedNodes:this.nodeRegistry.listNodes().map(e=>({nodeId:e.nodeId,displayName:e.displayName,hostname:e.hostname}))}))),this.commandRegistry.register(new A(e=>this.agentService.interrupt(e))),this.commandRegistry.register(new x(()=>this.agentService.getSdkSlashCommands())),this.commandRegistry.register(new k(()=>this.agentService.getSdkSlashCommands())),this.commandRegistry.register(new N(e=>this.agentService.getUsage(e)))}registerChannels(){if(this.config.channels.telegram.enabled){const e=this.config.channels.telegram.accounts;for(const[t,s]of Object.entries(e)){if(!s.botToken){X.error(`Telegram account "${t}" has no botToken configured — skipping. Check your config.yaml.`);continue}const e=new h(s,this.tokenDb,this.config.agent.inflightTyping);this.channelManager.registerAdapter(e)}}if(this.config.channels.whatsapp.enabled){const e=this.config.channels.whatsapp.accounts;for(const[t,s]of Object.entries(e)){const e=new c(s,this.config.agent.inflightTyping);e.setQrCallback((e,t,s)=>{this.whatsappQr=e?{dataUrl:e,timestamp:Date.now()}:null,this.whatsappConnected=t,this.whatsappError=s}),this.channelManager.registerAdapter(e)}}if(this.config.channels.responses.enabled){const e=new l({host:this.config.host,port:this.config.channels.responses.port},this.tokenDb);this.channelManager.registerAdapter(e)}this.webChatChannel||(this.webChatChannel=new g),this.channelManager.registerAdapter(this.webChatChannel)}async handleMessage(e){const t=`${e.channelName}:${e.chatId}`,s=e.text?e.text.length>15?e.text.slice(0,15)+"...":e.text:"[no text]";X.info(`Message from ${t} (user=${e.userId}, ${e.username??"?"}): ${s}`),this.config.verboseDebugLogs&&X.debug(`Message from ${t} full text: ${e.text??"[no text]"}`);try{if(e.text){if(e.text.startsWith("__ask:")){const s=e.text.substring(6);return this.agentService.resolveQuestion(t,s),""}if(this.agentService.hasPendingQuestion(t)){const s=e.text.trim();return this.agentService.resolveQuestion(t,s),`Selected: ${s}`}if(e.text.startsWith("__tool_perm:")){const s="__tool_perm:approve"===e.text;return this.agentService.resolvePermission(t,s),s?"Tool approved.":"Tool denied."}if(this.agentService.hasPendingPermission(t)){const s=e.text.trim().toLowerCase();if("approve"===s||"approva"===s)return this.agentService.resolvePermission(t,!0),"Tool approved.";if("deny"===s||"vieta"===s||"blocca"===s)return this.agentService.resolvePermission(t,!1),"Tool denied."}}const s=!0===e.__passthrough;if(!s&&e.text&&this.commandRegistry.isCommand(e.text)){const s=await this.commandRegistry.dispatch(e.text,{sessionKey:t,chatId:e.chatId,channelName:e.channelName,userId:e.userId});if(s)return s.passthrough?this.handleMessage({...e,text:s.passthrough,__passthrough:!0}):(s.resetSession?(this.agentService.destroySession(t),this.sessionManager.resetSession(t),this.memoryManager&&this.memoryManager.clearSession(t)):s.resetAgent&&this.agentService.destroySession(t),s.text)}if(!s&&e.text?.startsWith("/")&&this.agentService.isBusy(t))return"I'm busy right now. Please resend this request later.";const n=this.sessionManager.getOrCreate(t),r=await this.messageProcessor.process(e),i=u(r,void 0,{sessionKey:t,channel:e.channelName,chatId:e.chatId});X.debug(`[${t}] Prompt to agent (${i.text.length} chars): ${this.config.verboseDebugLogs?i.text:i.text.slice(0,15)+"..."}${i.images.length>0?` [+${i.images.length} image(s)]`:""}`);const o=n.model,a={sessionKey:t,channel:e.channelName,chatId:e.chatId,sessionId:n.sessionId??"",memoryFile:this.memoryManager?this.memoryManager.getConversationFile(t):"",attachmentsDir:this.memoryManager?this.memoryManager.getAttachmentsDir(t):""},h=b(this.config.dataDir),c={config:this.config,sessionContext:a,workspaceFiles:h,mode:"full",hasNodeTools:this.agentService.hasNodeTools(),hasMessageTools:this.agentService.hasMessageTools(),coderSkill:this.getChatSetting(t,"coderSkill")??this.coderSkill,toolServers:this.agentService.getToolServers()},g=p(c),l=p({...c,mode:"minimal"});X.debug(`[${t}] System prompt (${g.length} chars): ${this.config.verboseDebugLogs?g:g.slice(0,15)+"..."}`);try{const s=await this.agentService.sendMessage(t,i,n.sessionId,g,l,o,this.getChatSetting(t,"coderSkill")??this.coderSkill,this.getChatSetting(t,"subagentsEnabled")??this.subagentsEnabled,this.getChatSetting(t,"customSubAgentsEnabled")??this.customSubAgentsEnabled,this.getChatSetting(t,"sandboxEnabled")??!1);if(s.sessionReset){if("[AGENT_CLOSED]"===s.response)return X.info(`[${t}] Agent closed during restart, keeping session ID for resume on next message`),"";{const e={sessionKey:t,sessionId:n.sessionId,error:new Error("Session corruption detected"),timestamp:new Date},s=J.analyzeError(e.error,e),r=J.getRecoveryStrategy(s);return X.warn(`[${t}] ${r.message}`),this.sessionManager.updateSessionId(t,""),r.clearSession&&(this.agentService.destroySession(t),this.memoryManager&&this.memoryManager.clearSession(t)),"[SESSION_CORRUPTED] The previous session is no longer valid. Use /new to start a new one."}}if(X.debug(`[${t}] Response from agent (session=${s.sessionId}, len=${s.response.length}): ${this.config.verboseDebugLogs?s.response:s.response.slice(0,15)+"..."}`),s.sessionId&&this.sessionManager.updateSessionId(t,s.sessionId),this.memoryManager&&"cron"!==e.userId){const e=(i.text||"[media]").trim();await this.memoryManager.append(t,"user",e,r.savedFiles.length>0?r.savedFiles:void 0),await this.memoryManager.append(t,"assistant",G(s.fullResponse??s.response))}if("max_turns"===s.errorType){const e="\n\n[MAX_TURNS] The agent reached the maximum number of turns. You can continue the conversation by sending another message.";return s.response?s.response+e:e.trim()}if("max_budget"===s.errorType){const e="\n\n[MAX_BUDGET] The agent reached the maximum budget for this request.";return s.response?s.response+e:e.trim()}if("refusal"===s.stopReason){const e="\n\n[REFUSAL] The model declined to fulfill this request.";return s.response?s.response+e:e.trim()}if("max_tokens"===s.stopReason){const e="\n\n[TRUNCATED] The response was cut short because it exceeded the output token limit. You can ask me to continue.";return s.response?s.response+e:e.trim()}return s.response}catch(e){const s=e instanceof Error?e.message:String(e);return s.includes("SessionAgent closed")||s.includes("agent closed")?(X.info(`[${t}] Agent closed during restart, keeping session ID for resume on next message`),""):(X.error(`Agent error for ${t}: ${e}`),`Error: ${s}`)}}finally{await this.channelManager.releaseTyping(e.channelName,e.chatId).catch(()=>{})}}async start(){X.info("Starting GrabMeABeer server...");const e=[];this.config.channels.telegram.enabled&&e.push("telegram"),this.config.channels.responses.enabled&&e.push("responses"),this.config.channels.whatsapp.enabled&&e.push("whatsapp"),this.config.channels.discord.enabled&&e.push("discord"),this.config.channels.slack.enabled&&e.push("slack"),X.info(`Enabled channels: ${e.join(", ")||"none"}`),await this.channelManager.startAll(),await this.browserService.start(this.config.browser).catch(e=>{X.warn(`Browser service failed to start: ${e}`)}),this.memorySearch&&await this.memorySearch.start(),this.cronService&&await this.initCronAndHeartbeat(),this.startAutoRenewTimer(),X.info("Server started successfully"),this.notifyAllChannels("Gateway started. Agent is alive!").catch(()=>{})}async initCronAndHeartbeat(){if(!this.cronService)return;await this.cronService.start();const e=this.config.cron.heartbeat,t=(await this.cronService.list({includeDisabled:!0})).find(e=>"__heartbeat"===e.name),s=!!e.channel&&!!this.channelManager.getAdapter(e.channel),n=!!e.message&&e.message.trim().length>=15;if(e.enabled&&e.channel&&e.chatId&&s&&n){const s={schedule:{kind:"every",everyMs:e.every},channel:e.channel,chatId:e.chatId,message:e.message};if(t){const e=t.schedule;(t.channel!==s.channel||t.chatId!==s.chatId||t.message!==s.message||t.isolated!==this.config.cron.isolated||"every"!==e.kind||"every"===e.kind&&e.everyMs!==s.schedule.everyMs||!t.enabled)&&(await this.cronService.update(t.id,{...s,isolated:this.config.cron.isolated,enabled:!0}),X.info("Heartbeat job updated from config"))}else await this.cronService.add({name:"__heartbeat",description:"Auto-generated heartbeat job",enabled:!0,isolated:this.config.cron.isolated,suppressToken:!0,...s}),X.info("Heartbeat job auto-added")}else t&&t.enabled&&(await this.cronService.update(t.id,{enabled:!1}),e.enabled&&!n?X.warn("Heartbeat job disabled: message is empty or too short (minimum 15 characters)"):e.enabled&&!s?X.warn(`Heartbeat job disabled: channel "${e.channel}" is not active`):X.info("Heartbeat job disabled (config changed)"))}getConfig(){return this.config}getTokenDb(){return this.tokenDb}getSessionDb(){return this.sessionDb}getMemoryManager(){return this.memoryManager}getNodeRegistry(){return this.nodeRegistry}getNodeSignatureDb(){return this.nodeSignatureDb}getChannelManager(){return this.channelManager}getAgentService(){return this.agentService}getCronService(){return this.cronService}getCoderSkill(){return this.coderSkill}getWhatsAppQrState(){return{dataUrl:this.whatsappQr?.dataUrl??null,connected:this.whatsappConnected,error:this.whatsappError}}getWebChatChannel(){return this.webChatChannel}async triggerRestart(){X.info("Trigger restart requested");const e=q();await this.reconfigure(e),this.notifyAllChannels("Gateway restarted. Agent is alive!").catch(()=>{})}async notifyAllChannels(e){const t=this.collectBroadcastTargets();X.info(`Broadcasting to ${t.length} target(s)`),await Promise.allSettled(t.map(async t=>{try{await this.channelManager.sendSystemMessage(t.channel,t.chatId,e),await this.channelManager.clearTyping(t.channel,t.chatId),X.debug(`Notified ${t.channel}:${t.chatId}`)}catch(e){X.warn(`Failed to notify ${t.channel}:${t.chatId}: ${e}`)}}))}static AUTO_RENEW_CHECK_INTERVAL_MS=9e5;startAutoRenewTimer(){this.stopAutoRenewTimer();const e=this.config.agent.autoRenew;e&&(X.info(`AutoRenew enabled — resetting sessions inactive for ${e}h (checking every 15 min)`),this.autoRenewTimer=setInterval(()=>{this.autoRenewStaleSessions().catch(e=>X.error(`AutoRenew error: ${e}`))},Server.AUTO_RENEW_CHECK_INTERVAL_MS))}stopAutoRenewTimer(){this.autoRenewTimer&&(clearInterval(this.autoRenewTimer),this.autoRenewTimer=null)}async autoRenewStaleSessions(){const e=this.config.agent.autoRenew;if(!e)return;const t=60*e*60*1e3,s=this.sessionDb.listStaleSessions(t);if(0!==s.length){X.info(`AutoRenew: found ${s.length} stale session(s)`);for(const t of s){const s=t.sessionKey;if(s.startsWith("cron:"))continue;if(this.agentService.isBusy(s))continue;this.agentService.destroySession(s),this.sessionManager.resetSession(s),this.memoryManager&&this.memoryManager.clearSession(s);const n=s.indexOf(":");if(n>0){const t=s.substring(0,n),r=s.substring(n+1),i=this.channelManager.getAdapter(t);if(i)try{await i.sendText(r,`Session renewed automatically after ${e}h of inactivity. Starting fresh!`)}catch(e){X.warn(`AutoRenew: failed to send courtesy message to ${s}: ${e}`)}}X.info(`AutoRenew: session reset — ${s}`)}}}async reconfigure(e){X.info("Reconfiguring server..."),this.cronService&&this.cronService.stop(),await this.channelManager.stopAll(),this.config=e,this.coderSkill=e.agent.builtinCoderSkill,this.sessionManager=new d(this.sessionDb),e.memory.enabled?this.memoryManager=new y(e.memoryDir):this.memoryManager=null;const t=v(e),s=this.memoryManager?this.memoryManager.saveFile.bind(this.memoryManager):null;this.messageProcessor=new w(t,s),this.commandRegistry=new M,this.setupCommands(),this.channelManager=new a(e,this.tokenDb,e=>this.handleMessage(e)),this.registerChannels(),this.agentService.destroyAll(),this.serverToolsServer=O(()=>this.triggerRestart(),e.timezone),this.cronService=this.createCronService(),this.stopMemorySearch();const n=this.createMemorySearch(e);await this.browserService.reconfigure(e.browser);const r=e.browser?.enabled?Q({nodeRegistry:this.nodeRegistry,config:e}):void 0,i=this.cronService?B(this.cronService,()=>this.config):void 0,o=e.tts.enabled?F(()=>this.config):void 0;this.agentService=new m(e,this.nodeRegistry,this.channelManager,this.serverToolsServer,i,this.sessionDb,o,n,e=>this.getChatSetting(e,"showToolUse")??this.showToolUse,r),f(e.dataDir),await this.channelManager.startAll(),this.memorySearch&&await this.memorySearch.start(),this.cronService&&await this.initCronAndHeartbeat(),this.startAutoRenewTimer(),X.info("Server reconfigured successfully")}async stop(){X.info("Shutting down..."),this.stopAutoRenewTimer(),this.cronService&&this.cronService.stop(),this.memorySearch&&this.memorySearch.stop(),await this.browserService.stop(),this.agentService.destroyAll(),await this.channelManager.stopAll(),this.sessionManager.destroy(),this.sessionDb.close(),this.tokenDb.close(),this.nodeSignatureDb.close(),X.info("Server stopped")}}
@@ -0,0 +1,9 @@
1
+ import type { STTProvider } from "./stt-provider.js";
2
+ export declare class LocalWhisperSTT implements STTProvider {
3
+ constructor(_opts: {
4
+ binaryPath?: string;
5
+ model?: string;
6
+ });
7
+ transcribe(_audio: Buffer, _mimeType?: string): Promise<string>;
8
+ }
9
+ //# sourceMappingURL=local-whisper.d.ts.map
@@ -0,0 +1 @@
1
+ import{createLogger as e}from"../utils/logger.js";const r=e("LocalWhisper");export class LocalWhisperSTT{constructor(e){r.warn("Local Whisper STT is a stub - not yet implemented")}async transcribe(e,r){throw new Error("Local Whisper STT is not yet implemented. Use openai-whisper instead.")}}
@@ -0,0 +1,14 @@
1
+ import type { STTProvider } from "./stt-provider.js";
2
+ export declare class OpenAIWhisperSTT implements STTProvider {
3
+ private client;
4
+ private model;
5
+ private language;
6
+ constructor(opts: {
7
+ apiKey: string;
8
+ baseURL?: string;
9
+ model?: string;
10
+ language?: string;
11
+ });
12
+ transcribe(audio: Buffer, mimeType?: string): Promise<string>;
13
+ }
14
+ //# sourceMappingURL=openai-whisper.d.ts.map
@@ -0,0 +1 @@
1
+ import e from"openai";import{createLogger as a}from"../utils/logger.js";const i=a("OpenAI-Whisper");export class OpenAIWhisperSTT{client;model;language;constructor(a){this.client=new e({apiKey:a.apiKey,...a.baseURL?{baseURL:a.baseURL}:{}}),this.model=a.model??"whisper-1",this.language=a.language??""}async transcribe(e,a){const o={"audio/ogg":"ogg","audio/mpeg":"mp3","audio/mp4":"m4a","audio/wav":"wav","audio/webm":"webm","audio/flac":"flac"}[a??"audio/ogg"]??"ogg";const t=new File([e],`audio.${o}`,{type:a??"audio/ogg"});i.info(`Transcribing audio (${e.length} bytes, ${a})`);const n=await this.client.audio.transcriptions.create({file:t,model:this.model,...this.language?{language:this.language}:{}});return i.info(`Transcription result: ${n.text.slice(0,100)}...`),n.text}}
@@ -0,0 +1,4 @@
1
+ import type { AppConfig } from "../config.js";
2
+ import type { STTProvider } from "./stt-provider.js";
3
+ export declare function loadSTTProvider(config: AppConfig): STTProvider | null;
4
+ //# sourceMappingURL=stt-loader.d.ts.map
@@ -0,0 +1 @@
1
+ import{OpenAIWhisperSTT as e}from"./openai-whisper.js";import{LocalWhisperSTT as r}from"./local-whisper.js";import{createLogger as o}from"../utils/logger.js";const i=o("STTLoader");export function loadSTTProvider(o){if(!o.stt.enabled)return i.info("STT disabled"),null;const n=o.stt.provider;switch(i.info(`Loading STT provider: ${n}`),n){case"openai-whisper":{const r=o.stt["openai-whisper"],n=o.models?.find(e=>e.name===r.modelRef),l=(n?.useEnvVar?process.env[n.useEnvVar]:n?.apiKey)||"",t=n?.baseURL||"";if(!l)return i.error("STT model API key not configured (check model registry for '%s')",r.modelRef||"(none)"),null;let s=r.model;if((n?.types?.includes("external")??!0)&&n?.id?.startsWith("__")){s=`${n.id.slice(2)}/${r.model}`,i.info(`External model with provider prefix: ${n.id} → STT model: ${s}`)}return new e({apiKey:l,baseURL:t||void 0,model:s,language:r.language})}case"local-whisper":{const e=o.stt["local-whisper"];return new r({binaryPath:e.binaryPath,model:e.model})}default:return i.error(`Unknown STT provider: ${n}`),null}}
@@ -0,0 +1,4 @@
1
+ export interface STTProvider {
2
+ transcribe(audio: Buffer, mimeType?: string): Promise<string>;
3
+ }
4
+ //# sourceMappingURL=stt-provider.d.ts.map
@@ -0,0 +1 @@
1
+ export{};
@@ -0,0 +1,9 @@
1
+ import type { NodeRegistry } from "../gateway/node-registry.js";
2
+ import type { AppConfig } from "../config.js";
3
+ type BrowserToolsDeps = {
4
+ nodeRegistry: NodeRegistry;
5
+ config: AppConfig;
6
+ };
7
+ export declare function createBrowserToolsServer(deps: BrowserToolsDeps): import("@anthropic-ai/claude-agent-sdk").McpSdkServerConfigWithInstance;
8
+ export {};
9
+ //# sourceMappingURL=browser-tools.d.ts.map
@@ -0,0 +1 @@
1
+ import{createSdkMcpServer as e,tool as t}from"@anthropic-ai/claude-agent-sdk";import{z as r}from"zod";import o from"node:fs/promises";import a from"node:os";import i from"node:path";import{formatErrorMessage as s}from"@hera-al/browser-server/utils";export function createBrowserToolsServer(n){return e({name:"browser-tools",version:"1.0.0",tools:[t("browser",'Control a Chrome browser via CDP. Supports navigation, clicking, typing, screenshots, DOM snapshots, and more.\n\nActions (pass as "action" parameter):\n- start: Launch/connect browser. Returns CDP URL.\n- stop: Stop browser.\n- status: Check browser status.\n- navigate: Navigate to URL. Params: url (required), timeoutMs.\n- click: Click element by ref. Params: ref (required), doubleClick, button.\n- type: Type text into element. Params: ref (required), text (required), submit, slowly.\n- press: Press keyboard key. Params: key (required).\n- hover: Hover over element. Params: ref (required).\n- scroll: Scroll element into view. Params: ref (required).\n- screenshot: Take screenshot. Params: ref, element, fullPage, type (png/jpeg). Returns file path.\n- snapshot: Get page structure. Params: mode (role/aria/dom/text/html/ai/query), selector, interactive, compact.\n- evaluate: Run JS in page. Params: fn (required), ref.\n- wait: Wait for condition. Params: timeMs, text, textGone, selector, url, loadState, fn.\n- tabs: List open tabs.\n- tab_open: Open new tab. Params: url.\n- tab_close: Close tab. Params: tabId (required).\n- tab_focus: Focus tab. Params: tabId (required).\n- cookies_get: Get cookies.\n- cookies_set: Set cookie. Params: cookie (required, {name, value, url/domain+path}).\n- cookies_clear: Clear all cookies.\n- storage_get: Get localStorage/sessionStorage. Params: kind (local/session), key.\n- storage_set: Set storage. Params: kind, key, value.\n- storage_clear: Clear storage. Params: kind.\n\nUse "node" param to target a remote node\'s browser, or omit for local/auto-routing.',{action:r.string().describe("The browser action to perform"),node:r.string().optional().describe("Target node ID for remote browser. Omit for local or auto-routing."),profile:r.string().optional().describe("Browser profile name (default: 'default')"),targetId:r.string().optional().describe("Target tab ID"),url:r.string().optional(),ref:r.string().optional(),text:r.string().optional(),key:r.string().optional(),fn:r.string().optional(),selector:r.string().optional(),element:r.string().optional(),mode:r.string().optional(),kind:r.string().optional(),value:r.string().optional(),tabId:r.string().optional(),fullPage:r.boolean().optional(),doubleClick:r.boolean().optional(),submit:r.boolean().optional(),slowly:r.boolean().optional(),interactive:r.boolean().optional(),compact:r.boolean().optional(),button:r.enum(["left","right","middle"]).optional(),type:r.enum(["png","jpeg"]).optional(),timeMs:r.number().optional(),timeoutMs:r.number().optional(),cookie:r.object({name:r.string(),value:r.string(),url:r.string().optional(),domain:r.string().optional(),path:r.string().optional(),expires:r.number().optional(),httpOnly:r.boolean().optional(),secure:r.boolean().optional(),sameSite:r.enum(["Lax","None","Strict"]).optional()}).optional()},async e=>{try{const t=n.config.browser?.controlPort??3002;let r=e.node;if(!r){const e=function(e){const t=e.listNodes();for(const e of t)if(e.capabilities?.includes("browser")||e.commands?.includes("browser.proxy"))return e.nodeId;return null}(n.nodeRegistry);e&&(r=e)}const s=!!r,l=async(e,l,c)=>s?await async function(e,t,r,s,n){const l=await e.executeCommand(t,"browser.proxy",{method:r,path:s,body:n});if(!l.ok)throw new Error(l.error||"Remote browser command failed");const c=l.result;if(c?.files&&Array.isArray(c.files))for(const e of c.files){const t=i.join(a.tmpdir(),"grabmeabeer","screenshots");await o.mkdir(t,{recursive:!0});const r=i.join(t,e.name);if(await o.writeFile(r,Buffer.from(e.base64,"base64")),c.result&&"object"==typeof c.result){const e=c.result;e.path&&(e.path=r)}}return c?.result??c}(n.nodeRegistry,r,e,l,c):await async function(e,t,r,o){const a=`http://127.0.0.1:${e}${r}`,i={method:t,headers:{"Content-Type":"application/json"}};o&&"GET"!==t&&(i.body=JSON.stringify(o));const s=await fetch(a,i);return await s.json()}(t,e,l,c);let c;switch(e.action){case"start":c=await l("POST","/start",{profile:e.profile});break;case"stop":c=await l("POST","/stop",{profile:e.profile});break;case"status":c=await l("GET",`/?profile=${e.profile||""}`);break;case"navigate":c=await l("POST","/act",{kind:"navigate",profile:e.profile,targetId:e.targetId,url:e.url,timeoutMs:e.timeoutMs});break;case"click":c=await l("POST","/act",{kind:"click",profile:e.profile,targetId:e.targetId,ref:e.ref,doubleClick:e.doubleClick,button:e.button,timeoutMs:e.timeoutMs});break;case"type":c=await l("POST","/act",{kind:"type",profile:e.profile,targetId:e.targetId,ref:e.ref,text:e.text,submit:e.submit,slowly:e.slowly,timeoutMs:e.timeoutMs});break;case"press":c=await l("POST","/act",{kind:"press",profile:e.profile,targetId:e.targetId,key:e.key,timeoutMs:e.timeoutMs});break;case"hover":c=await l("POST","/act",{kind:"hover",profile:e.profile,targetId:e.targetId,ref:e.ref,timeoutMs:e.timeoutMs});break;case"scroll":c=await l("POST","/act",{kind:"scroll",profile:e.profile,targetId:e.targetId,ref:e.ref,timeoutMs:e.timeoutMs});break;case"screenshot":c=await l("POST","/act",{kind:"screenshot",profile:e.profile,targetId:e.targetId,ref:e.ref,element:e.element,fullPage:e.fullPage,type:e.type});break;case"snapshot":{const t=new URLSearchParams;e.profile&&t.set("profile",e.profile),e.mode&&t.set("mode",e.mode),e.selector&&t.set("selector",e.selector),e.interactive&&t.set("interactive","true"),e.compact&&t.set("compact","true"),e.targetId&&t.set("targetId",e.targetId);const r=t.toString();c=await l("GET","/snapshot"+(r?`?${r}`:""));break}case"evaluate":c=await l("POST","/act",{kind:"evaluate",profile:e.profile,targetId:e.targetId,fn:e.fn,ref:e.ref,timeoutMs:e.timeoutMs});break;case"wait":c=await l("POST","/act",{kind:"wait",profile:e.profile,targetId:e.targetId,timeMs:e.timeMs,text:e.text,selector:e.selector,url:e.url,fn:e.fn,timeoutMs:e.timeoutMs});break;case"tabs":c=await l("GET",`/tabs?profile=${e.profile||""}`);break;case"tab_open":c=await l("POST","/tabs/open",{url:e.url||"about:blank",profile:e.profile});break;case"tab_close":if(!e.tabId)return{content:[{type:"text",text:"Error: tabId is required"}],isError:!0};c=await l("DELETE",`/tabs/${e.tabId}?profile=${e.profile||""}`);break;case"tab_focus":if(!e.tabId)return{content:[{type:"text",text:"Error: tabId is required"}],isError:!0};c=await l("POST","/tabs/focus",{id:e.tabId,profile:e.profile});break;case"cookies_get":c=await l("GET",`/cookies?profile=${e.profile||""}`);break;case"cookies_set":if(!e.cookie)return{content:[{type:"text",text:"Error: cookie is required"}],isError:!0};c=await l("POST","/cookies",{profile:e.profile,targetId:e.targetId,cookie:e.cookie});break;case"cookies_clear":c=await l("DELETE",`/cookies?profile=${e.profile||""}`);break;case"storage_get":c=await l("GET",`/storage/${e.kind||"local"}?profile=${e.profile||""}${e.key?`&key=${e.key}`:""}`);break;case"storage_set":c=await l("POST",`/storage/${e.kind||"local"}`,{profile:e.profile,targetId:e.targetId,key:e.key,value:e.value});break;case"storage_clear":c=await l("DELETE",`/storage/${e.kind||"local"}?profile=${e.profile||""}`);break;default:return{content:[{type:"text",text:`Unknown action: ${e.action}`}],isError:!0}}return{content:[{type:"text",text:"string"==typeof c?c:JSON.stringify(c,null,2)}]}}catch(e){return{content:[{type:"text",text:`Error: ${s(e)}`}],isError:!0}}})]})}
@@ -0,0 +1,4 @@
1
+ import type { CronService } from "../cron/cron-service.js";
2
+ import type { AppConfig } from "../config.js";
3
+ export declare function createCronToolsServer(cronService: CronService, getConfig: () => AppConfig): import("@anthropic-ai/claude-agent-sdk").McpSdkServerConfigWithInstance;
4
+ //# sourceMappingURL=cron-tools.d.ts.map
@@ -0,0 +1 @@
1
+ import{createSdkMcpServer as e,tool as t}from"@anthropic-ai/claude-agent-sdk";import{z as n}from"zod";import{createLogger as o}from"../utils/logger.js";const r=o("CronTools");export function createCronToolsServer(o,s){return e({name:"cron-tools",version:"1.0.0",tools:[t("cron_add","Create a new cron job. The job will send the specified message to the agent on the given schedule. Channel and chatId are taken from the current session (from <session_info>). The isolated setting is inherited from the global cron config.",{name:n.string().describe("Unique name for the job (e.g. 'daily-reminder')"),description:n.string().optional().describe("Optional description of what the job does"),message:n.string().describe("The message text to send to the agent when the job fires"),channel:n.string().describe("The channel to deliver responses to (from <session_info>)"),chatId:n.string().describe("The chat ID to deliver responses to (from <session_info>)"),scheduleKind:n.enum(["every","cron","at"]).describe("Schedule type: 'every' for interval, 'cron' for cron expression, 'at' for one-shot"),everyMs:n.number().optional().describe("Interval in milliseconds (required when scheduleKind is 'every')"),cronExpr:n.string().optional().describe("Cron expression like '0 9 * * *' (required when scheduleKind is 'cron')"),at:n.string().optional().describe("ISO datetime for one-shot execution (required when scheduleKind is 'at')"),suppressToken:n.boolean().optional().describe("If true, suppress HEARTBEAT_OK responses (default: false)")},async e=>{try{const t=s();let n;if("every"===e.scheduleKind){if(!e.everyMs)return{content:[{type:"text",text:"everyMs is required for 'every' schedule"}],isError:!0};n={kind:"every",everyMs:e.everyMs}}else if("cron"===e.scheduleKind){if(!e.cronExpr)return{content:[{type:"text",text:"cronExpr is required for 'cron' schedule"}],isError:!0};n={kind:"cron",expr:e.cronExpr}}else{if(!e.at)return{content:[{type:"text",text:"at is required for 'at' schedule"}],isError:!0};n={kind:"at",at:e.at}}const i=await o.add({name:e.name,description:e.description,channel:e.channel,chatId:e.chatId,message:e.message,schedule:n,isolated:t.cron.isolated,suppressToken:e.suppressToken??!1,enabled:!0});return r.info(`Cron job created by agent: "${i.name}" (${i.id})`),{content:[{type:"text",text:`Job "${i.name}" created (id: ${i.id}). Schedule: ${JSON.stringify(n)}. Isolated: ${t.cron.isolated}. Next run: ${i.state.nextRunAtMs?new Date(i.state.nextRunAtMs).toISOString():"none"}.`}]}}catch(e){const t=e instanceof Error?e.message:String(e);return r.error(`cron_add failed: ${t}`),{content:[{type:"text",text:`Failed to create job: ${t}`}],isError:!0}}}),t("cron_list","List all cron jobs with their status, schedule, and next run time.",{includeDisabled:n.boolean().optional().describe("Include disabled jobs (default: false)")},async e=>{try{const t=await o.list({includeDisabled:e.includeDisabled??!1});if(0===t.length)return{content:[{type:"text",text:"No cron jobs found."}]};const n=t.map(e=>({id:e.id,name:e.name,enabled:e.enabled,isolated:e.isolated,schedule:e.schedule,channel:e.channel,chatId:e.chatId,nextRun:e.state.nextRunAtMs?new Date(e.state.nextRunAtMs).toISOString():null,lastStatus:e.state.lastStatus??null}));return{content:[{type:"text",text:JSON.stringify(n,null,2)}]}}catch(e){return{content:[{type:"text",text:`Failed to list jobs: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}),t("cron_remove","Delete a cron job by its ID.",{id:n.string().describe("The job ID to delete")},async e=>{try{return(await o.remove(e.id)).removed?(r.info(`Cron job removed by agent: ${e.id}`),{content:[{type:"text",text:`Job ${e.id} deleted.`}]}):{content:[{type:"text",text:`Job ${e.id} not found.`}],isError:!0}}catch(e){return{content:[{type:"text",text:`Failed to delete job: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}),t("cron_update","Update an existing cron job. Only provide the fields you want to change.",{id:n.string().describe("The job ID to update"),name:n.string().optional().describe("New name for the job"),description:n.string().optional().describe("New description"),message:n.string().optional().describe("New message text"),enabled:n.boolean().optional().describe("Enable or disable the job"),suppressToken:n.boolean().optional().describe("Enable or disable HEARTBEAT_OK suppression")},async e=>{try{const t={};void 0!==e.name&&(t.name=e.name),void 0!==e.description&&(t.description=e.description),void 0!==e.message&&(t.message=e.message),void 0!==e.enabled&&(t.enabled=e.enabled),void 0!==e.suppressToken&&(t.suppressToken=e.suppressToken);const n=await o.update(e.id,t);return r.info(`Cron job updated by agent: "${n.name}" (${n.id})`),{content:[{type:"text",text:`Job "${n.name}" updated. Enabled: ${n.enabled}. Next run: ${n.state.nextRunAtMs?new Date(n.state.nextRunAtMs).toISOString():"none"}.`}]}}catch(e){return{content:[{type:"text",text:`Failed to update job: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}})]})}
@@ -0,0 +1,3 @@
1
+ import type { MemorySearch } from "../memory/memory-search.js";
2
+ export declare function createMemoryToolsServer(memorySearch: MemorySearch): import("@anthropic-ai/claude-agent-sdk").McpSdkServerConfigWithInstance;
3
+ //# sourceMappingURL=memory-tools.d.ts.map
@@ -0,0 +1 @@
1
+ import{createSdkMcpServer as e,tool as r}from"@anthropic-ai/claude-agent-sdk";import{z as t}from"zod";import{createLogger as o}from"../utils/logger.js";const n=o("MemoryTools");export function createMemoryToolsServer(o){return e({name:"memory-tools",version:"1.0.0",tools:[r("memory_search","Search conversation memory using hybrid keyword + semantic search. Returns matching chunks from past conversations with relevance scores. Formulate clear, specific queries. For broad searches, call multiple times with different angles or phrasings.",{query:t.string().describe("The search query — be specific and descriptive for best results"),maxResults:t.number().optional().describe("Maximum number of results to return (default 6)")},async e=>{try{const r=await o.search(e.query,e.maxResults);if(0===r.length)return{content:[{type:"text",text:"No matching memories found."}]};const t=o.getMaxInjectedChars();let s=JSON.stringify(r,null,2);if(t>0&&s.length>t){const e=[...r];for(;e.length>1&&(e.pop(),s=JSON.stringify(e,null,2),!(s.length<=t)););s.length>t&&(s=s.slice(0,t)+"\n... [truncated — result exceeded maxInjectedChars limit]"),n.info(`memory_search: trimmed from ${r.length} to ${e.length} results to fit maxInjectedChars=${t}`)}return{content:[{type:"text",text:s}]}}catch(e){const r=e instanceof Error?e.message:String(e);return n.error(`memory_search error: ${r}`),{content:[{type:"text",text:`Search error: ${r}`}],isError:!0}}}),r("memory_get","Read a memory file (full or a specific slice) to get surrounding context after a search hit. Use the path from memory_search results.",{path:t.string().describe("Relative path to the memory file (from memory_search results)"),from:t.number().optional().describe("Starting line number (1-based). Omit to read from the beginning."),lines:t.number().optional().describe("Number of lines to read. Omit to read the entire file.")},async e=>{try{const r=o.readFile(e.path,e.from,e.lines);return{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(e){const r=e instanceof Error?e.message:String(e);return n.error(`memory_get error: ${r}`),{content:[{type:"text",text:`Read error: ${r}`}],isError:!0}}})]})}
@@ -0,0 +1,5 @@
1
+ import type { AppConfig } from "../config.js";
2
+ import type { ChannelManager } from "../gateway/channel-manager.js";
3
+ import type { SessionDB } from "../agent/session-db.js";
4
+ export declare function createMessageToolsServer(channelManager: ChannelManager, getConfig: () => AppConfig, sessionDb: SessionDB): import("@anthropic-ai/claude-agent-sdk").McpSdkServerConfigWithInstance;
5
+ //# sourceMappingURL=message-tools.d.ts.map
@@ -0,0 +1 @@
1
+ import{createSdkMcpServer as e,tool as t}from"@anthropic-ai/claude-agent-sdk";import{z as n}from"zod";import{createLogger as s}from"../utils/logger.js";const a=s("MessageTools");export function createMessageToolsServer(s,o,r){return e({name:"message-tools",version:"1.0.0",tools:[t("send_message","Send a text message to a chat on a specific channel. Use the channel and chatId from <session_info> to reply on the current conversation, or specify a different channel/chatId to send elsewhere.",{channel:n.string().describe("The channel name to send to (e.g. 'telegram', 'whatsapp', 'responses')"),chatId:n.string().describe("The chat ID to send to (from <session_info> or another known chat)"),text:n.string().describe("The message text to send"),buttons:n.array(n.object({text:n.string().describe("Button label"),callbackData:n.string().optional().describe("Data sent back when button is tapped (defaults to text)"),url:n.string().optional().describe("URL to open when button is tapped (mutually exclusive with callbackData)")})).optional().describe("Optional inline buttons to attach to the message")},async e=>{try{return e.buttons&&e.buttons.length>0?await s.sendButtons(e.channel,e.chatId,e.text,e.buttons):await s.sendResponse(e.channel,e.chatId,e.text),a.info(`Message sent to ${e.channel}:${e.chatId} (${e.text.length} chars)`),{content:[{type:"text",text:`Message sent to ${e.channel}:${e.chatId}`}]}}catch(t){const n=t instanceof Error?t.message:String(t);return a.error(`Failed to send message to ${e.channel}:${e.chatId}: ${n}`),{content:[{type:"text",text:`Error sending message: ${n}`}],isError:!0}}}),t("list_models","List all models in the registry with their name, model ID, API base URL, and API key environment variable name.",{},async()=>{const e=(o().models||[]).filter(e=>{const t=e.types||["external"];return t.includes("internal")||t.includes("external")&&e.proxy&&"not-used"!==e.proxy});if(0===e.length)return{content:[{type:"text",text:"No models in registry."}]};const t=e.map(e=>({name:e.name,modelId:e.id,type:(e.types||["external"])[0],baseURL:e.baseURL||"",apiKeyEnvVar:e.useEnvVar||((e.types||["external"]).includes("internal")?"":"OPENAI_API_KEY")}));return{content:[{type:"text",text:JSON.stringify(t,null,2)}]}}),t("list_channels","List all registered channels, whether they are currently active, and known chat IDs for each channel (from config allowFrom + session history).",{},async()=>{const e=s.listAdapters();if(0===e.length)return{content:[{type:"text",text:"No channels registered."}]};const t=o(),n=r.listSessions(),a=e.map(e=>{const s=new Set,a=[],o=t.channels[e.name];if(o?.accounts)for(const e of Object.values(o.accounts)){const t=e?.allowFrom;if(Array.isArray(t))for(const e of t){const t=String(e).trim();t&&!s.has(t)&&(s.add(t),a.push({id:t,source:"config"}))}}for(const t of n){const n=t.sessionKey.indexOf(":");if(n<0)continue;const o=t.sessionKey.substring(0,n),r=t.sessionKey.substring(n+1);o===e.name&&r&&!s.has(r)&&(s.add(r),a.push({id:r,source:"session"}))}return{name:e.name,active:e.active,chatIds:a}});return{content:[{type:"text",text:JSON.stringify(a,null,2)}]}})]})}
@@ -0,0 +1,3 @@
1
+ import type { NodeRegistry } from "../gateway/node-registry.js";
2
+ export declare function createNodeToolsServer(nodeRegistry: NodeRegistry): import("@anthropic-ai/claude-agent-sdk").McpSdkServerConfigWithInstance;
3
+ //# sourceMappingURL=node-tools.d.ts.map
@@ -0,0 +1 @@
1
+ import{createSdkMcpServer as e,tool as t}from"@anthropic-ai/claude-agent-sdk";import{z as n}from"zod";export function createNodeToolsServer(a){return e({name:"node-tools",version:"1.0.0",tools:[t("list_nodes","List all connected nodes. Returns each node's ID, display name, platform, hostname, capabilities, available commands, and connection time.",{},async()=>{const e=a.listNodes();if(0===e.length)return{content:[{type:"text",text:"No nodes currently connected."}]};const t=e.map(e=>({nodeId:e.nodeId,displayName:e.displayName,platform:e.platform,arch:e.arch,hostname:e.hostname,capabilities:e.capabilities,commands:e.commands,connectedAt:new Date(e.connectedAt).toISOString()}));return{content:[{type:"text",text:JSON.stringify(t,null,2)}]}}),t("node_exec","Execute a command on a connected node. Use list_nodes first to find available nodes and their supported commands. Currently supported commands: shell.run (run a shell command), shell.which (resolve binary path). IMPORTANT: For shell.run, always pass the executable path in 'cmd' and arguments as an 'args' array — do NOT pass a single command string.",{nodeId:n.string().describe("The UUID of the target node (from list_nodes)"),command:n.string().describe("The command to execute (e.g. 'shell.run', 'shell.which')"),params:n.record(n.string(),n.any()).describe("Command parameters. For shell.run: { cmd: '/absolute/path/to/binary', args?: ['arg1', 'arg2'], cwd?, timeout?, env? }. For shell.which: { binary: 'name' }. IMPORTANT: 'cmd' must be the executable path (e.g. '/bin/ls', '/usr/bin/osascript'), NOT a full command string."),timeout:n.number().optional().describe("Timeout in milliseconds (default: 30000)")},async e=>{const t=await a.executeCommand(e.nodeId,e.command,e.params,e.timeout);return t.ok?{content:[{type:"text",text:JSON.stringify(t.result,null,2)}]}:{content:[{type:"text",text:`Error: ${t.error}`}],isError:!0}})]})}
@@ -0,0 +1,2 @@
1
+ export declare function createServerToolsServer(restartFn: () => Promise<void>, timezone?: string): import("@anthropic-ai/claude-agent-sdk").McpSdkServerConfigWithInstance;
2
+ //# sourceMappingURL=server-tools.d.ts.map
@@ -0,0 +1 @@
1
+ import{createSdkMcpServer as e,tool as t}from"@anthropic-ai/claude-agent-sdk";import{createLogger as r}from"../utils/logger.js";const n=r("ServerTools");export function createServerToolsServer(r,o){const i=o||Intl.DateTimeFormat().resolvedOptions().timeZone;return e({name:"server-tools",version:"1.0.0",tools:[t("restart_server","Restart the GrabMeABeer server. This reloads config from disk and reinitializes all channels, agents, and services. Active conversations will be interrupted. Use this when you need to apply configuration changes or recover from issues.",{},async()=>{try{return n.info("Server restart triggered by agent"),await r(),{content:[{type:"text",text:"Server restarted successfully. All channels and services have been reinitialized with fresh configuration."}]}}catch(e){const t=e instanceof Error?e.message:String(e);return n.error(`Agent-triggered restart failed: ${t}`),{content:[{type:"text",text:`Server restart failed: ${t}`}],isError:!0}}}),t("get_current_time","Get the current date and time in the server's configured timezone. Use this tool whenever you need to know the current time, date, day of the week, or any time-related information. Do not guess or estimate the current time — always call this tool.",{},async()=>{const e=new Date,t=e.toLocaleString("en-GB",{timeZone:i,weekday:"long",year:"numeric",month:"long",day:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1}),r=e.toISOString();return{content:[{type:"text",text:`Current time (${i}): ${t}\nISO 8601: ${r}\nUnix timestamp: ${Math.floor(e.getTime()/1e3)}`}]}})]})}
@@ -0,0 +1,3 @@
1
+ import type { AppConfig } from "../config.js";
2
+ export declare function createTTSToolsServer(getConfig: () => AppConfig): import("@anthropic-ai/claude-agent-sdk").McpSdkServerConfigWithInstance;
3
+ //# sourceMappingURL=tts-tools.d.ts.map
@@ -0,0 +1 @@
1
+ import{createSdkMcpServer as e,tool as t}from"@anthropic-ai/claude-agent-sdk";import{z as o}from"zod";import{textToSpeech as r}from"../tts/tts-service.js";import{createLogger as i}from"../utils/logger.js";const n=i("TTSTools");export function createTTSToolsServer(i){return e({name:"tts-tools",version:"1.0.0",tools:[t("tts","Convert text to speech audio. Returns a MEDIA: line that must be included verbatim in your response so the gateway can deliver the audio to the user. For Telegram, audio is sent as a voice bubble. Use when the user asks for audio/voice output, or when you want to provide a spoken response.",{text:o.string().describe("The text to convert to speech"),channel:o.string().optional().describe("Optional channel name (e.g. 'telegram') to pick the optimal audio format. Taken from <session_info> if available."),provider:o.enum(["edge","openai","elevenlabs"]).optional().describe("Override the TTS provider (default: from config)"),voice:o.string().optional().describe("Override the voice (provider-specific, e.g. 'alloy' for OpenAI, 'en-US-MichelleNeural' for Edge)")},async e=>{try{const t=i(),o=await r({text:e.text,config:t,channel:e.channel,provider:e.provider,voice:e.voice});if(o.success&&o.audioPath){const t=[];return o.voiceCompatible&&t.push("[[audio_as_voice]]"),t.push(`MEDIA:${o.audioPath}`),n.info(`TTS OK: ${o.provider}, ${o.latencyMs}ms, ${e.text.length} chars`),{content:[{type:"text",text:t.join("\n")}]}}return n.warn(`TTS failed: ${o.error}`),{content:[{type:"text",text:o.error??"TTS conversion failed"}],isError:!0}}catch(e){const t=e instanceof Error?e.message:String(e);return n.error(`TTS tool error: ${t}`),{content:[{type:"text",text:`TTS error: ${t}`}],isError:!0}}})]})}
@@ -0,0 +1,19 @@
1
+ import type { AppConfig } from "../config.js";
2
+ export type TtsResult = {
3
+ success: boolean;
4
+ audioPath?: string;
5
+ error?: string;
6
+ latencyMs?: number;
7
+ provider?: string;
8
+ voiceCompatible?: boolean;
9
+ };
10
+ type TtsProvider = "edge" | "openai" | "elevenlabs";
11
+ export declare function textToSpeech(params: {
12
+ text: string;
13
+ config: AppConfig;
14
+ channel?: string;
15
+ provider?: TtsProvider;
16
+ voice?: string;
17
+ }): Promise<TtsResult>;
18
+ export {};
19
+ //# sourceMappingURL=tts-service.d.ts.map
@@ -0,0 +1 @@
1
+ import{EdgeTTS as e}from"node-edge-tts";import{writeFileSync as t,rmSync as o,mkdtempSync as n}from"node:fs";import{tmpdir as i}from"node:os";import{join as s}from"node:path";import{createLogger as a}from"../utils/logger.js";const r=a("TTS"),l={openai:"opus",elevenlabs:"opus_48000_64",extension:".ogg",voiceCompatible:!0},c={openai:"mp3",elevenlabs:"mp3_44100_128",extension:".mp3",voiceCompatible:!0},m={openai:"mp3",elevenlabs:"mp3_44100_128",extension:".mp3",voiceCompatible:!1};function u(e,t){const o="openai"===t?e.tts.openai.modelRef:"elevenlabs"===t?e.tts.elevenlabs.modelRef:void 0;if(o){const t=e.models?.find(e=>e.name===o);if(t)return function(e){return e.useEnvVar?process.env[e.useEnvVar]:e.apiKey?e.apiKey:void 0}(t)}return process.env.TTS_API_KEY}function p(e,t){if("openai"===t){const t=e.tts.openai.modelRef;if(t){const o=e.models?.find(e=>e.name===t);if(o?.baseURL)return o.baseURL.replace(/\/+$/,"")}return"https://api.openai.com/v1"}if("elevenlabs"===t){const t=e.tts.elevenlabs.modelRef;if(t){const o=e.models?.find(e=>e.name===t);if(o?.baseURL)return o.baseURL.replace(/\/+$/,"")}return"https://api.elevenlabs.io"}return""}function d(e){setTimeout(()=>{try{o(e,{recursive:!0,force:!0})}catch{}},3e5).unref()}function f(e){const t=e.toLowerCase();return t.includes("webm")?".webm":t.includes("ogg")?".ogg":t.includes("opus")?".opus":t.includes("wav")||t.includes("riff")||t.includes("pcm")?".wav":".mp3"}async function v(t){const o=new e({voice:t.voice,lang:t.lang,outputFormat:t.outputFormat,timeout:t.timeoutMs});await o.ttsPromise(t.text,t.outputPath)}async function b(e){const t=new AbortController,o=setTimeout(()=>t.abort(),e.timeoutMs);try{const o=await fetch(`${e.baseUrl}/audio/speech`,{method:"POST",headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({model:e.model,input:e.text,voice:e.voice,response_format:e.responseFormat}),signal:t.signal});if(!o.ok)throw new Error(`OpenAI TTS API error (${o.status})`);return Buffer.from(await o.arrayBuffer())}finally{clearTimeout(o)}}async function g(e){const t=new AbortController,o=setTimeout(()=>t.abort(),e.timeoutMs);try{const o=new URL(`${e.baseUrl}/v1/text-to-speech/${e.voiceId}`);o.searchParams.set("output_format",e.outputFormat);const n=await fetch(o.toString(),{method:"POST",headers:{"xi-api-key":e.apiKey,"Content-Type":"application/json",Accept:"audio/mpeg"},body:JSON.stringify({text:e.text,model_id:e.modelId}),signal:t.signal});if(!n.ok)throw new Error(`ElevenLabs API error (${n.status})`);return Buffer.from(await n.arrayBuffer())}finally{clearTimeout(o)}}export async function textToSpeech(e){const{config:o,text:a}=e,h=o.tts;if(a.length>h.maxTextLength)return{success:!1,error:`Text too long (${a.length} chars, max ${h.maxTextLength})`};const w="telegram"===(T=e.channel)?l:"webchat"===T?c:m;var T;const $=function(e){return[e,...["edge","openai","elevenlabs"].filter(t=>t!==e)]}(e.provider??h.provider);let y;for(const l of $){const c=Date.now();try{if("edge"===l){const t=h.edge,o=t.outputFormat,m=f(o),u=n(s(i(),"tts-")),p=s(u,`voice-${Date.now()}${m}`);await v({text:a,outputPath:p,voice:e.voice??t.voice,lang:t.lang,outputFormat:o,timeoutMs:h.timeoutMs}),d(u);const b=".opus"===m||".ogg"===m||w.voiceCompatible;return r.info(`TTS: edge OK in ${Date.now()-c}ms`),{success:!0,audioPath:p,latencyMs:Date.now()-c,provider:l,voiceCompatible:b}}const m=u(o,l);if(!m){y=`No API key for ${l}`;continue}let T;if("openai"===l){const t=p(o,l);let n=h.openai.model;const i=h.openai.modelRef;if(i){const e=o.models?.find(e=>e.name===i);if((e?.types?.includes("external")??!0)&&e?.id?.startsWith("__")){n=`${e.id.slice(2)}/${h.openai.model}`,r.info(`External model with provider prefix: ${e.id} → TTS model: ${n}`)}}T=await b({text:a,apiKey:m,baseUrl:t,model:n,voice:e.voice??h.openai.voice,responseFormat:w.openai,timeoutMs:h.timeoutMs})}else{const e=p(o,l);let t=h.elevenlabs.modelId;const n=h.elevenlabs.modelRef;if(n){const e=o.models?.find(e=>e.name===n);if((e?.types?.includes("external")??!0)&&e?.id?.startsWith("__")){t=`${e.id.slice(2)}/${h.elevenlabs.modelId}`,r.info(`External model with provider prefix: ${e.id} → TTS model: ${t}`)}}T=await g({text:a,apiKey:m,baseUrl:e,voiceId:h.elevenlabs.voiceId,modelId:t,outputFormat:w.elevenlabs,timeoutMs:h.timeoutMs})}const $=n(s(i(),"tts-")),x=s($,`voice-${Date.now()}${w.extension}`);return t(x,T),d($),r.info(`TTS: ${l} OK in ${Date.now()-c}ms`),{success:!0,audioPath:x,latencyMs:Date.now()-c,provider:l,voiceCompatible:w.voiceCompatible}}catch(e){const t=e;y="AbortError"===t.name?`${l}: request timed out`:`${l}: ${t.message}`,r.warn(`TTS: ${y}`)}}return{success:!1,error:`TTS failed: ${y||"no providers available"}`}}
@@ -0,0 +1,3 @@
1
+ export declare function chunkText(text: string, limit: number): string[];
2
+ export declare function chunkMarkdownText(text: string, limit: number): string[];
3
+ //# sourceMappingURL=chunk.d.ts.map
@@ -0,0 +1 @@
1
+ import{findFenceSpanAt as t,isSafeFenceBreak as e,parseFenceSpans as n}from"./markdown/fences.js";export function chunkText(t,e){if(!t)return[];if(e<=0)return[t];if(t.length<=e)return[t];const n=[];let s=t;for(;s.length>e;){const t=s.slice(0,e),{lastNewline:l,lastWhitespace:i}=r(t);let o=l>0?l:i;o<=0&&(o=e);const c=s.slice(0,o).trimEnd();c.length>0&&n.push(c);const a=o<s.length&&/\s/.test(s[o]),h=Math.min(s.length,o+(a?1:0));s=s.slice(h).trimStart()}return s.length&&n.push(s),n}export function chunkMarkdownText(r,i){if(!r)return[];if(i<=0)return[r];if(r.length<=i)return[r];const o=[];let c=r;for(;c.length>i;){const r=n(c),a=l(c.slice(0,i),r);let h=a>0?a:i;const f=e(r,h)?void 0:t(r,h);let u=f;if(f){const e=`${f.indent}${f.marker}`,n=i-(e.length+1);if(n<=0)u=void 0,h=i;else{const s=Math.min(c.length,f.start+f.openLine.length+2),l=i-e.length;let o=!1,a=c.lastIndexOf("\n",Math.max(0,l-1));for(;-1!==a;){const e=a+1;if(e<s)break;const n=t(r,e);if(n&&n.start===f.start){h=Math.max(1,e),o=!0;break}a=c.lastIndexOf("\n",a-1)}o||(s>l?(u=void 0,h=i):h=Math.max(s,n))}const s=t(r,h);u=s&&s.start===f.start?s:void 0}let g=c.slice(0,h);if(!g)break;const m=h<c.length&&/\s/.test(c[h]),d=Math.min(c.length,h+(m?1:0));let p=c.slice(d);if(u){const t=`${u.indent}${u.marker}`;g=g.endsWith("\n")?`${g}${t}`:`${g}\n${t}`,p=`${u.openLine}\n${p}`}else p=s(p);o.push(g),c=p}return c.length&&o.push(c),o}function s(t){let e=0;for(;e<t.length&&"\n"===t[e];)e++;return e>0?t.slice(e):t}function l(t,n){const{lastNewline:s,lastWhitespace:l}=r(t,t=>e(n,t));return s>0?s:l>0?l:-1}function r(t,e=()=>!0){let n=-1,s=-1,l=0;for(let r=0;r<t.length;r++){if(!e(r))continue;const i=t[r];"("!==i?")"===i&&l>0?l-=1:0===l&&("\n"===i?n=r:/\s/.test(i)&&(s=r)):l+=1}return{lastNewline:n,lastWhitespace:s}}
@@ -0,0 +1,16 @@
1
+ type LogLevel = "debug" | "info" | "warn" | "error";
2
+ export declare function setLogLevel(level: LogLevel): void;
3
+ export declare function getLogLevel(): LogLevel;
4
+ /** Initialize file logging. Call once at startup. */
5
+ export declare function initLogFile(dir: string): void;
6
+ /** Returns the logs directory path (empty string if not initialized). */
7
+ export declare function getLogsDir(): string;
8
+ export declare function createLogger(tag: string): {
9
+ debug: (msg: string, ...args: unknown[]) => void;
10
+ info: (msg: string, ...args: unknown[]) => void;
11
+ warn: (msg: string, ...args: unknown[]) => void;
12
+ error: (msg: string, ...args: unknown[]) => void;
13
+ };
14
+ export type Logger = ReturnType<typeof createLogger>;
15
+ export {};
16
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ import{appendFileSync as t,statSync as o,renameSync as e,unlinkSync as r,mkdirSync as n,existsSync as i}from"node:fs";import{join as c}from"node:path";const a={debug:0,info:1,warn:2,error:3};let s="info";export function setLogLevel(t){s=t}export function getLogLevel(){return s}function h(t,o,e){return`${(new Date).toISOString()} [${t.toUpperCase().padEnd(5)}] [${o}] ${e}`}class g{dir;filePath;writeCount=0;constructor(t){this.dir=t,this.filePath=c(t,"gmab.log"),n(t,{recursive:!0})}append(o){try{t(this.filePath,o+"\n"),this.writeCount++,this.writeCount>=100&&(this.writeCount=0,this.checkRotation())}catch{}}checkRotation(){try{o(this.filePath).size>=10485760&&this.rotate()}catch{}}rotate(){const t=c(this.dir,"gmab.9.log");try{i(t)&&r(t)}catch{}for(let t=8;t>=1;t--){const o=c(this.dir,`gmab.${t}.log`),r=c(this.dir,`gmab.${t+1}.log`);try{e(o,r)}catch{}}try{e(this.filePath,c(this.dir,"gmab.1.log"))}catch{}}}let f=null,l="";export function initLogFile(t){l=t,f=new g(t)}export function getLogsDir(){return l}export function createLogger(t){const o=t=>a[t]>=a[s];return{debug:(e,...r)=>{if(o("debug")){const o=h("debug",t,e);console.debug(o,...r),f?.append(o)}},info:(e,...r)=>{if(o("info")){const o=h("info",t,e);console.log(o,...r),f?.append(o)}},warn:(e,...r)=>{if(o("warn")){const o=h("warn",t,e);console.warn(o,...r),f?.append(o)}},error:(e,...r)=>{if(o("error")){const o=h("error",t,e);console.error(o,...r),f?.append(o)}}}}
@@ -0,0 +1,11 @@
1
+ export type FenceSpan = {
2
+ start: number;
3
+ end: number;
4
+ openLine: string;
5
+ marker: string;
6
+ indent: string;
7
+ };
8
+ export declare function parseFenceSpans(buffer: string): FenceSpan[];
9
+ export declare function findFenceSpanAt(spans: FenceSpan[], index: number): FenceSpan | undefined;
10
+ export declare function isSafeFenceBreak(spans: FenceSpan[], index: number): boolean;
11
+ //# sourceMappingURL=fences.d.ts.map
@@ -0,0 +1 @@
1
+ export function parseFenceSpans(e){const n=[];let r,t=0;for(;t<=e.length;){const a=e.indexOf("\n",t),i=-1===a?e.length:a,o=e.slice(t,i),s=o.match(/^( {0,3})(`{3,}|~{3,})(.*)$/);if(s){const e=s[1],a=s[2],p=a[0],c=a.length;if(r){if(r.markerChar===p&&c>=r.markerLen){const e=i;n.push({start:r.start,end:e,openLine:r.openLine,marker:r.marker,indent:r.indent}),r=void 0}}else r={start:t,markerChar:p,markerLen:c,openLine:o,marker:a,indent:e}}if(-1===a)break;t=a+1}return r&&n.push({start:r.start,end:e.length,openLine:r.openLine,marker:r.marker,indent:r.indent}),n}export function findFenceSpanAt(e,n){return e.find(e=>n>e.start&&n<e.end)}export function isSafeFenceBreak(e,n){return!findFenceSpanAt(e,n)}
@@ -0,0 +1,33 @@
1
+ export type MarkdownTableMode = "off" | "bullets" | "code";
2
+ export type MarkdownStyle = "bold" | "italic" | "strikethrough" | "code" | "code_block" | "spoiler";
3
+ export type MarkdownStyleSpan = {
4
+ start: number;
5
+ end: number;
6
+ style: MarkdownStyle;
7
+ };
8
+ export type MarkdownLinkSpan = {
9
+ start: number;
10
+ end: number;
11
+ href: string;
12
+ };
13
+ export type MarkdownIR = {
14
+ text: string;
15
+ styles: MarkdownStyleSpan[];
16
+ links: MarkdownLinkSpan[];
17
+ };
18
+ export type MarkdownParseOptions = {
19
+ linkify?: boolean;
20
+ enableSpoilers?: boolean;
21
+ headingStyle?: "none" | "bold";
22
+ blockquotePrefix?: string;
23
+ autolink?: boolean;
24
+ /** How to render tables (off|bullets|code). Default: off. */
25
+ tableMode?: MarkdownTableMode;
26
+ };
27
+ export declare function markdownToIR(markdown: string, options?: MarkdownParseOptions): MarkdownIR;
28
+ export declare function markdownToIRWithMeta(markdown: string, options?: MarkdownParseOptions): {
29
+ ir: MarkdownIR;
30
+ hasTables: boolean;
31
+ };
32
+ export declare function chunkMarkdownIR(ir: MarkdownIR, limit: number): MarkdownIR[];
33
+ //# sourceMappingURL=ir.d.ts.map
@@ -0,0 +1 @@
1
+ import t from"markdown-it";import{chunkText as e}from"../chunk.js";function n(t,e){if(t.attrGet)return t.attrGet(e);if(t.attrs)for(const[n,s]of t.attrs)if(n===e)return s;return null}function s(t,e){return{...t,type:"text",content:e,children:void 0}}function l(t){const e=[],n={spoilerOpen:!1};for(const l of t){if("text"!==l.type){e.push(l);continue}const t=l.content??"";if(!t.includes("||")){e.push(l);continue}let o=0;for(;o<t.length;){const r=t.indexOf("||",o);if(-1===r){o<t.length&&e.push(s(l,t.slice(o)));break}r>o&&e.push(s(l,t.slice(o,r))),n.spoilerOpen=!n.spoilerOpen,e.push({type:n.spoilerOpen?"spoiler_open":"spoiler_close"}),o=r+2}}return e}function o(){return{text:"",styles:[],openStyles:[],links:[],linkStack:[]}}function r(t){return t.table?.currentCell??t}function a(t,e){if(!e)return;r(t).text+=e}function c(t,e){const n=r(t);n.openStyles.push({style:e,start:n.text.length})}function i(t,e){const n=r(t);for(let t=n.openStyles.length-1;t>=0;t-=1)if(n.openStyles[t]?.style===e){const s=n.openStyles[t].start;n.openStyles.splice(t,1);const l=n.text.length;return void(l>s&&n.styles.push({start:s,end:l,style:e}))}}function h(t){t.env.listStack.length>0||t.table||(t.text+="\n\n")}function f(t){const e=t.env.listStack,n=e[e.length-1];if(!n)return;n.index+=1;const s=" ".repeat(Math.max(0,e.length-1)),l="ordered"===n.type?`${n.index}. `:"• ";t.text+=`${s}${l}`}function u(t,e){if(!e)return;const n=r(t),s=n.text.length;n.text+=e,n.styles.push({start:s,end:s+e.length,style:"code"})}function b(t,e){let n=e??"";n.endsWith("\n")||(n=`${n}\n`);const s=r(t),l=s.text.length;s.text+=n,s.styles.push({start:l,end:l+n.length,style:"code_block"}),0===t.env.listStack.length&&(s.text+="\n")}function d(t){const e=r(t),n=e.linkStack.pop();if(!n?.href)return;const s=n.href.trim();if(!s)return;const l=n.labelStart,o=e.text.length;e.links.push({start:l,end:o,href:s})}function p(){return{headers:[],rows:[],currentRow:[],currentCell:null,inHeader:!1}}function x(t){return S(t),{text:t.text,styles:t.styles,links:t.links}}function k(t){const e=t.text;let n=0,s=e.length;for(;n<s&&/\s/.test(e[n]??"");)n+=1;for(;s>n&&/\s/.test(e[s-1]??"");)s-=1;if(0===n&&s===e.length)return t;const l=e.slice(n,s),o=l.length,r=[];for(const e of t.styles){const t=Math.max(0,e.start-n),s=Math.min(o,e.end-n);s>t&&r.push({start:t,end:s,style:e.style})}const a=[];for(const e of t.links){const t=Math.max(0,e.start-n),s=Math.min(o,e.end-n);s>t&&a.push({start:t,end:s,href:e.href})}return{text:l,styles:r,links:a}}function y(t,e){if(!e.text)return;const n=t.text.length;t.text+=e.text;for(const s of e.styles)t.styles.push({start:n+s.start,end:n+s.end,style:s.style});for(const s of e.links)t.links.push({start:n+s.start,end:n+s.end,href:s.href})}function g(t){if(!t.table)return;const e=t.table.headers.map(k),n=t.table.rows.map(t=>t.map(k));if(0===e.length&&0===n.length)return;if(e.length>1&&n.length>0)for(const s of n){if(0===s.length)continue;const n=s[0];if(n?.text){const e=t.text.length;y(t,n);const s=t.text.length;s>e&&t.styles.push({start:e,end:s,style:"bold"}),t.text+="\n"}for(let n=1;n<s.length;n++){const l=e[n],o=s[n];o?.text&&(t.text+="• ",l?.text?(y(t,l),t.text+=": "):t.text+=`Column ${n}: `,y(t,o),t.text+="\n")}t.text+="\n"}else for(const s of n){for(let n=0;n<s.length;n++){const l=e[n],o=s[n];o?.text&&(t.text+="• ",l?.text&&(y(t,l),t.text+=": "),y(t,o),t.text+="\n")}t.text+="\n"}}function m(t){if(!t.table)return;const e=t.table.headers.map(k),n=t.table.rows.map(t=>t.map(k)),s=Math.max(e.length,...n.map(t=>t.length));if(0===s)return;const l=Array.from({length:s},()=>0),o=t=>{for(let e=0;e<s;e+=1){const n=t[e],s=n?.text.length??0;l[e]<s&&(l[e]=s)}};o(e);for(const t of n)o(t);const r=t.text.length,a=e=>{t.text+="|";for(let n=0;n<s;n+=1){t.text+=" ";const s=e[n];s&&y(t,s);const o=l[n]-(s?.text.length??0);o>0&&(t.text+=" ".repeat(o)),t.text+=" |"}t.text+="\n"};a(e),(()=>{t.text+="|";for(let e=0;e<s;e+=1){const n=Math.max(3,l[e]);t.text+=` ${"-".repeat(n)} |`}t.text+="\n"})();for(const t of n)a(t);const c=t.text.length;c>r&&t.styles.push({start:r,end:c,style:"code_block"}),0===t.env.listStack.length&&(t.text+="\n")}function _(t,e){for(const s of t)switch(s.type){case"inline":default:s.children&&_(s.children,e);break;case"text":case"image":case"html_block":case"html_inline":a(e,s.content??"");break;case"em_open":c(e,"italic");break;case"em_close":i(e,"italic");break;case"strong_open":c(e,"bold");break;case"strong_close":i(e,"bold");break;case"s_open":c(e,"strikethrough");break;case"s_close":i(e,"strikethrough");break;case"code_inline":u(e,s.content??"");break;case"spoiler_open":e.enableSpoilers&&c(e,"spoiler");break;case"spoiler_close":e.enableSpoilers&&i(e,"spoiler");break;case"link_open":{const t=n(s,"href")??"",l=r(e);l.linkStack.push({href:t,labelStart:l.text.length});break}case"link_close":d(e);break;case"softbreak":case"hardbreak":a(e,"\n");break;case"paragraph_close":h(e);break;case"heading_open":"bold"===e.headingStyle&&c(e,"bold");break;case"heading_close":"bold"===e.headingStyle&&i(e,"bold"),h(e);break;case"blockquote_open":e.blockquotePrefix&&(e.text+=e.blockquotePrefix);break;case"blockquote_close":case"list_item_close":case"hr":e.text+="\n";break;case"bullet_list_open":e.env.listStack.push({type:"bullet",index:0});break;case"bullet_list_close":case"ordered_list_close":e.env.listStack.pop();break;case"ordered_list_open":{const t=Number(n(s,"start")??"1");e.env.listStack.push({type:"ordered",index:t-1});break}case"list_item_open":f(e);break;case"code_block":case"fence":b(e,s.content??"");break;case"table_open":"off"!==e.tableMode&&(e.table=p(),e.hasTables=!0);break;case"table_close":e.table&&("bullets"===e.tableMode?g(e):"code"===e.tableMode&&m(e)),e.table=null;break;case"thead_open":e.table&&(e.table.inHeader=!0);break;case"thead_close":e.table&&(e.table.inHeader=!1);break;case"tbody_open":case"tbody_close":break;case"tr_open":e.table&&(e.table.currentRow=[]);break;case"tr_close":e.table&&(e.table.inHeader?e.table.headers=e.table.currentRow:e.table.rows.push(e.table.currentRow),e.table.currentRow=[]);break;case"th_open":case"td_open":e.table&&(e.table.currentCell=o());break;case"th_close":case"td_close":e.table?.currentCell&&(e.table.currentRow.push(x(e.table.currentCell)),e.table.currentCell=null)}}function S(t){for(let e=t.openStyles.length-1;e>=0;e-=1){const n=t.openStyles[e],s=t.text.length;s>n.start&&t.styles.push({start:n.start,end:s,style:n.style})}t.openStyles=[]}function M(t,e){const n=[];for(const s of t){const t=Math.max(0,Math.min(s.start,e)),l=Math.max(t,Math.min(s.end,e));l>t&&n.push({start:t,end:l,style:s.style})}return n}function w(t,e){const n=[];for(const s of t){const t=Math.max(0,Math.min(s.start,e)),l=Math.max(t,Math.min(s.end,e));l>t&&n.push({start:t,end:l,href:s.href})}return n}function v(t){const e=[...t].sort((t,e)=>t.start!==e.start?t.start-e.start:t.end!==e.end?t.end-e.end:t.style.localeCompare(e.style)),n=[];for(const t of e){const e=n[n.length-1];e&&e.style===t.style&&t.start<=e.end?e.end=Math.max(e.end,t.end):n.push({...t})}return n}function R(t,e,n){if(0===t.length)return[];const s=[];for(const l of t){const t=Math.max(l.start,e),o=Math.min(l.end,n);o>t&&s.push({start:t-e,end:o-e,style:l.style})}return v(s)}function C(t,e,n){if(0===t.length)return[];const s=[];for(const l of t){const t=Math.max(l.start,e),o=Math.min(l.end,n);o>t&&s.push({start:t-e,end:o-e,href:l.href})}return s}export function markdownToIR(t,e={}){return markdownToIRWithMeta(t,e).ir}export function markdownToIRWithMeta(e,n={}){const s={listStack:[]},o=function(e){const n=new t({html:!1,linkify:e.linkify??!0,breaks:!1,typographer:!1});return n.enable("strikethrough"),e.tableMode&&"off"!==e.tableMode?n.enable("table"):n.disable("table"),!1===e.autolink&&n.disable("autolink"),n}(n),r=o.parse(e??"",s);n.enableSpoilers&&function(t){for(const e of t)e.children&&e.children.length>0&&(e.children=l(e.children))}(r);const a=n.tableMode??"off",c={text:"",styles:[],openStyles:[],links:[],linkStack:[],env:s,headingStyle:n.headingStyle??"none",blockquotePrefix:n.blockquotePrefix??"",enableSpoilers:n.enableSpoilers??!1,tableMode:a,table:null,hasTables:!1};_(r,c),S(c);const i=c.text.trimEnd().length;let h=0;for(const t of c.styles)"code_block"===t.style&&t.end>h&&(h=t.end);const f=Math.max(i,h);return{ir:{text:f===c.text.length?c.text:c.text.slice(0,f),styles:v(M(c.styles,f)),links:w(c.links,f)},hasTables:c.hasTables}}export function chunkMarkdownIR(t,n){if(!t.text)return[];if(n<=0||t.text.length<=n)return[t];const s=e(t.text,n),l=[];let o=0;return s.forEach((e,n)=>{if(!e)return;if(n>0)for(;o<t.text.length&&/\s/.test(t.text[o]??"");)o+=1;const s=o,r=Math.min(t.text.length,s+e.length);l.push({text:e,styles:R(t.styles,s,r),links:C(t.links,s,r)}),o=r}),l}
@@ -0,0 +1,19 @@
1
+ import type { MarkdownIR, MarkdownLinkSpan, MarkdownStyle } from "./ir.js";
2
+ export type RenderStyleMarker = {
3
+ open: string;
4
+ close: string;
5
+ };
6
+ export type RenderStyleMap = Partial<Record<MarkdownStyle, RenderStyleMarker>>;
7
+ export type RenderLink = {
8
+ start: number;
9
+ end: number;
10
+ open: string;
11
+ close: string;
12
+ };
13
+ export type RenderOptions = {
14
+ styleMarkers: RenderStyleMap;
15
+ escapeText: (text: string) => string;
16
+ buildLink?: (link: MarkdownLinkSpan, text: string) => RenderLink | null;
17
+ };
18
+ export declare function renderMarkdownWithMarkers(ir: MarkdownIR, options: RenderOptions): string;
19
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ const e=new Map(["code_block","code","bold","italic","strikethrough","spoiler"].map((e,t)=>[e,t]));export function renderMarkdownWithMarkers(t,n){const s=t.text??"";if(!s)return"";const o=n.styleMarkers,d=(r=t.styles.filter(e=>Boolean(o[e.style])),[...r].sort((t,n)=>t.start!==n.start?t.start-n.start:t.end!==n.end?n.end-t.end:(e.get(t.style)??0)-(e.get(n.style)??0)));var r;const l=new Set;l.add(0),l.add(s.length);const i=new Map;for(const e of d){if(e.start===e.end)continue;l.add(e.start),l.add(e.end);const t=i.get(e.start);t?t.push(e):i.set(e.start,[e])}for(const t of i.values())t.sort((t,n)=>t.end!==n.end?n.end-t.end:(e.get(t.style)??0)-(e.get(n.style)??0));const c=new Map;if(n.buildLink)for(const e of t.links){if(e.start===e.end)continue;const t=n.buildLink(e,s);if(!t)continue;l.add(t.start),l.add(t.end);const o=c.get(t.start);o?o.push(t):c.set(t.start,[t])}const a=[...l].sort((e,t)=>e-t),f=[];let p="";for(let t=0;t<a.length;t+=1){const d=a[t];for(;f.length&&f[f.length-1]?.end===d;){const e=f.pop();e&&(p+=e.close)}const r=[],l=c.get(d);if(l&&l.length>0)for(const[e,t]of l.entries())r.push({end:t.end,open:t.open,close:t.close,kind:"link",index:e});const k=i.get(d);if(k)for(const[e,t]of k.entries()){const n=o[t.style];n&&r.push({end:t.end,open:n.open,close:n.close,kind:"style",style:t.style,index:e})}if(r.length>0){r.sort((t,n)=>t.end!==n.end?n.end-t.end:t.kind!==n.kind?"link"===t.kind?-1:1:"style"===t.kind&&"style"===n.kind?(e.get(t.style)??0)-(e.get(n.style)??0):t.index-n.index);for(const e of r)p+=e.open,f.push({close:e.close,end:e.end})}const g=a[t+1];if(void 0===g)break;g>d&&(p+=n.escapeText(s.slice(d,g)))}return p}
@@ -0,0 +1,3 @@
1
+ import type { MarkdownTableMode } from "./ir.js";
2
+ export declare function convertMarkdownTables(markdown: string, mode: MarkdownTableMode): string;
3
+ //# sourceMappingURL=tables.d.ts.map
@@ -0,0 +1 @@
1
+ import{markdownToIRWithMeta as e}from"./ir.js";import{renderMarkdownWithMarkers as o}from"./render.js";const n={bold:{open:"**",close:"**"},italic:{open:"_",close:"_"},strikethrough:{open:"~~",close:"~~"},code:{open:"`",close:"`"},code_block:{open:"```\n",close:"```"}};export function convertMarkdownTables(r,t){if(!r||"off"===t)return r;const{ir:l,hasTables:s}=e(r,{linkify:!1,autolink:!1,headingStyle:"none",blockquotePrefix:"",tableMode:t});return s?o(l,{styleMarkers:n,escapeText:e=>e,buildLink:(e,o)=>{const n=e.href.trim();if(!n)return null;return o.slice(e.start,e.end)?{start:e.start,end:e.end,open:"[",close:`](${n})`}:null}}):r}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Utilities for parsing MEDIA: lines and [[audio_as_voice]] tags from agent responses.
3
+ *
4
+ * The agent's TTS tool returns lines like:
5
+ * [[audio_as_voice]]
6
+ * MEDIA:/tmp/tts-xxx/voice-123.opus
7
+ *
8
+ * These must be parsed out, the audio sent via the channel adapter, and the
9
+ * remaining text sent as a normal message.
10
+ */
11
+ export interface MediaEntry {
12
+ path: string;
13
+ asVoice: boolean;
14
+ }
15
+ export interface ParsedMedia {
16
+ textParts: string[];
17
+ mediaEntries: MediaEntry[];
18
+ }
19
+ /**
20
+ * Parse a response string for MEDIA: lines and [[audio_as_voice]] tags.
21
+ * Returns the non-media text parts and the media entries with their metadata.
22
+ */
23
+ export declare function parseMediaLines(text: string): ParsedMedia;
24
+ /**
25
+ * Remove all MEDIA: lines and [[audio_as_voice]] tags from a text string.
26
+ * Useful for stripping media references before writing to memory.
27
+ */
28
+ export declare function stripMediaLines(text: string): string;
29
+ //# sourceMappingURL=media-response.d.ts.map
@@ -0,0 +1 @@
1
+ export function parseMediaLines(t){const i=t.split("\n"),s=[],e=[];let n=!1;for(const t of i){const i=t.trim();if("[[audio_as_voice]]"!==i){if(i.startsWith("MEDIA:")){const t=i.slice(6).trim();t&&e.push({path:t,asVoice:n}),n=!1;continue}n=!1,s.push(t)}else n=!0}return{textParts:s,mediaEntries:e}}export function stripMediaLines(t){return t.split("\n").filter(t=>{const i=t.trim();return"[[audio_as_voice]]"!==i&&!i.startsWith("MEDIA:")}).join("\n").trim()}
@@ -0,0 +1,5 @@
1
+ export declare function getPackageRoot(): string;
2
+ export declare function resolvePackageAsset(...segments: string[]): string;
3
+ export declare function resolveBundledDir(): string | null;
4
+ export declare function resolveInstallPkgDir(): string;
5
+ //# sourceMappingURL=package-paths.d.ts.map
@@ -0,0 +1 @@
1
+ import{dirname as e,join as o,resolve as r}from"node:path";import{fileURLToPath as t}from"node:url";import{existsSync as n}from"node:fs";const s=r(e(t(import.meta.url)),"..","..");export function getPackageRoot(){return s}export function resolvePackageAsset(...e){return o(s,...e)}export function resolveBundledDir(){const e=resolvePackageAsset("bundled");return n(e)?e:null}export function resolveInstallPkgDir(){return resolvePackageAsset("installationPkg")}
@@ -0,0 +1,13 @@
1
+ import type { MarkdownTableMode } from "./markdown/ir.js";
2
+ export type TelegramFormattedChunk = {
3
+ html: string;
4
+ text: string;
5
+ };
6
+ export declare function markdownToTelegramHtml(markdown: string, options?: {
7
+ tableMode?: MarkdownTableMode;
8
+ }): string;
9
+ export declare function markdownToTelegramChunks(markdown: string, limit: number, options?: {
10
+ tableMode?: MarkdownTableMode;
11
+ }): TelegramFormattedChunk[];
12
+ export declare function markdownToTelegramHtmlChunks(markdown: string, limit: number): string[];
13
+ //# sourceMappingURL=telegram-format.d.ts.map
@@ -0,0 +1 @@
1
+ import{chunkMarkdownIR as e,markdownToIR as o}from"./markdown/ir.js";import{renderMarkdownWithMarkers as r}from"./markdown/render.js";function t(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function n(e,o){const r=e.href.trim();if(!r)return null;if(e.start===e.end)return null;const n=t(r).replace(/"/g,"&quot;");return{start:e.start,end:e.end,open:`<a href="${n}">`,close:"</a>"}}function l(e){return r(e,{styleMarkers:{bold:{open:"<b>",close:"</b>"},italic:{open:"<i>",close:"</i>"},strikethrough:{open:"<s>",close:"</s>"},code:{open:"<code>",close:"</code>"},code_block:{open:"<pre><code>",close:"</code></pre>"},spoiler:{open:"<tg-spoiler>",close:"</tg-spoiler>"}},escapeText:t,buildLink:n})}export function markdownToTelegramHtml(e,r={}){return l(o(e??"",{linkify:!0,enableSpoilers:!0,headingStyle:"none",blockquotePrefix:"",tableMode:r.tableMode}))}export function markdownToTelegramChunks(r,t,n={}){const a=o(r??"",{linkify:!0,enableSpoilers:!0,headingStyle:"none",blockquotePrefix:"",tableMode:n.tableMode});return e(a,t).map(e=>({html:l(e),text:e.text}))}export function markdownToTelegramHtmlChunks(e,o){return markdownToTelegramChunks(e,o,{tableMode:"bullets"}).map(e=>e.html)}
@@ -0,0 +1,26 @@
1
+ # [Hera over GMAB//GrabMeABeer Framework ] environment variables
2
+ # These are referenced in config.yaml via ${VAR_NAME}
3
+ ###########################################################
4
+ # External references:
5
+ # https://code.claude.com/docs/en/settings
6
+ # https://code.claude.com/docs/en/third-party-integrations
7
+ # https://code.claude.com/docs/en/agent-teams
8
+ ###########################################################
9
+ CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=70
10
+ CLAUDE_CODE_DISABLE_AUTO_MEMORY=0
11
+ ENABLE_TOOL_SEARCH=1
12
+ USE_API_CONTEXT_MANAGEMENT=1
13
+ ANTHROPIC_BETAS=context-management-2025-06-27
14
+ DISABLE_TELEMETRY=1
15
+ # Option 1 (includes memory_search)
16
+ #CLAUDE_CODE_EXTRA_BODY={"context_management":{"edits":[{"type":"clear_tool_uses_20250919","trigger":{"type":"input_tokens","value":100000},"keep":{"type":"tool_uses","value":5}}]}}
17
+ # Option 2 (plus thinking and memory_search excluded)
18
+ #CLAUDE_CODE_EXTRA_BODY={"context_management":{"edits":[{"type":"clear_thinking_20251015","keep":{"type":"thinking_turns","value":2}},{"type":"clear_tool_uses_20250919","trigger":{"type":"input_tokens","value":100000},"keep":{"type":"tool_uses","value":5}}]}}
19
+ # Option 3 (default excludes memory_search, thinkig automatically added by SDK)
20
+ CLAUDE_CODE_EXTRA_BODY={"context_management":{"edits":[{"type":"clear_tool_uses_20250919","trigger":{"type":"input_tokens","value":100000},"keep":{"type":"tool_uses","value":5},"clear_at_least":{"type":"input_tokens","value":5000},"exclude_tools":["memory_search"]}]}}
21
+ ###########################################################
22
+ #CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
23
+ #DISABLE_AUTO_COMPACT=1
24
+ #DISABLE_COMPACT=1
25
+ ###########################################################
26
+ # HERA/NOSTROMO