@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,58 @@
1
+ import type { ProcessedMessage } from "../media/message-processor.js";
2
+ import type { AppConfig } from "../config.js";
3
+ import type { SessionContext } from "./session-agent.js";
4
+ import { type WorkspaceFile } from "./workspace-files.js";
5
+ export interface BuiltPrompt {
6
+ /** Text prompt for the agent (includes memory context, file refs, text) */
7
+ text: string;
8
+ /** Image content blocks for multimodal input */
9
+ images: Array<{
10
+ base64: string;
11
+ mimeType: string;
12
+ }>;
13
+ }
14
+ export interface ConnectedNode {
15
+ nodeId: string;
16
+ displayName?: string;
17
+ platform?: string;
18
+ hostname?: string;
19
+ commands: string[];
20
+ }
21
+ export interface SessionMeta {
22
+ sessionKey: string;
23
+ channel: string;
24
+ chatId: string;
25
+ connectedNodes?: ConnectedNode[];
26
+ }
27
+ export interface SystemPromptParams {
28
+ config: AppConfig;
29
+ sessionContext: SessionContext;
30
+ workspaceFiles: WorkspaceFile[];
31
+ mode: "full" | "minimal";
32
+ hasNodeTools: boolean;
33
+ hasMessageTools: boolean;
34
+ coderSkill?: boolean;
35
+ subagentTask?: string;
36
+ toolServers?: Array<{
37
+ name: string;
38
+ instance: unknown;
39
+ }>;
40
+ }
41
+ /**
42
+ * Build the complete system prompt by loading a template and resolving
43
+ * all {{PLACEHOLDER}} variables.
44
+ */
45
+ export declare function buildSystemPrompt(params: SystemPromptParams): string;
46
+ /**
47
+ * Resolve all {{PLACEHOLDER}} occurrences in a template string.
48
+ * Unknown placeholders are left as-is with a warning.
49
+ */
50
+ export declare function resolvePlaceholders(template: string, vars: Record<string, string>): string;
51
+ /**
52
+ * Build the user message prompt from processed message content.
53
+ * Includes connected nodes, memory context, text, images, and file references.
54
+ *
55
+ * Note: session_info is no longer included here — it's in the system prompt.
56
+ */
57
+ export declare function buildPrompt(processed: ProcessedMessage, memoryContext?: string, sessionMeta?: SessionMeta): BuiltPrompt;
58
+ //# sourceMappingURL=prompt-builder.d.ts.map
@@ -0,0 +1 @@
1
+ import{hostname as e,type as n,release as t,arch as s}from"node:os";import{loadTemplate as o,loadBuiltInTools as a,formatWorkspaceFiles as r,filterForSubagent as i}from"./workspace-files.js";import{createLogger as l}from"../utils/logger.js";const c=l("PromptBuilder");export function buildSystemPrompt(l){const{config:h,sessionContext:u,mode:p,hasNodeTools:g,hasMessageTools:f,coderSkill:y,subagentTask:T,toolServers:_}=l,E="minimal"===p?"SYSTEM_PROMPT_SUBAGENT.md":"SYSTEM_PROMPT.md",I=o(h.dataDir,E),S="minimal"===p?i(l.workspaceFiles):l.workspaceFiles;var w;const b=resolvePlaceholders(I,{SESSION_KEY:u.sessionKey,CHANNEL:u.channel,CHAT_ID:u.chatId,SESSION_ID:u.sessionId||"(new session)",MODEL:h.agent.model,HOSTNAME:e(),OS:`${n()} ${t()} (${s()})`,WORKSPACE_DIR:h.agent.workspacePath,DATA_DIR:h.dataDir,MEMORY_FILE:u.memoryFile||"(memory disabled)",ATTACHMENTS_DIR:u.attachmentsDir||"(memory disabled)",TIMEZONE:h.timezone||Intl.DateTimeFormat().resolvedOptions().timeZone,SUBAGENT_TASK:T??"",NODE_TOOLS_INSTRUCTIONS:g?"# Remote Nodes\n\nYou have access to remote nodes — external machines that you can control. Use the node tools to discover and interact with them.\n\n## Available tools\n\n- **list_nodes**: Call this to see which nodes are currently connected. Returns each node's ID, name, platform, hostname, and available commands. Always call this first before trying to execute commands, so you know which nodes are online and what their IDs are.\n\n- **node_exec**: Execute a command on a specific node. You must provide the nodeId (from list_nodes), the command name, and its parameters.\n\n## Supported commands\n\n- **shell.run**: Run a shell command on the node. Params: { cmd: string, args?: string[], cwd?: string, timeout?: number, env?: Record<string,string> }. Returns { stdout, stderr, exitCode }.\n- **shell.which**: Check if a binary exists on the node. Params: { cmd: string }. Returns { path } or null.\n\n## Guidelines\n\n- Always call list_nodes first to discover available nodes and their IDs. Do not guess node IDs.\n- When a user asks to run something on a remote machine, a node, or a specific hostname, call list_nodes to see what's online, then use node_exec with the appropriate nodeId.\n- If multiple nodes are connected, ask the user which one to use when the intent is ambiguous.\n- If no nodes are connected, inform the user that no remote nodes are available.\n- Report command results clearly: show stdout, note any stderr, and mention non-zero exit codes.":"",MESSAGE_TOOLS_INSTRUCTIONS:f?"# Messaging\n\nYou have tools to send messages to chat channels. Each message you process includes a <session_info> block with the current channel and chatId.\n\n## Available tools\n\n- **send_message**: Send a text message to a specific channel and chat. Use the channel and chatId from the session context to reply on the current conversation. You can also send to a different channel or chatId if instructed.\n\n- **list_channels**: List all registered channels. Returns each channel's name and whether it is active.\n\n## Guidelines\n\n- Use send_message when you need to proactively send a message outside of the normal response flow (e.g. notifications, forwarding, or sending to a different chat).\n- Your normal response text is already delivered to the user. Only use send_message for additional messages or cross-channel communication.\n- The channel and chatId from the session context identify the current conversation. Use them to send follow-up messages to the same chat.\n- If the user asks you to message someone on a different channel or chat, use the appropriate channel name and chatId.\n- Never spam or send unsolicited messages. Only send when explicitly asked or when it is clearly part of the task.":"",HEARTBEAT_INSTRUCTIONS:h.cron.enabled?(w=h.cron.heartbeat.message,`# Heartbeats\n\nHeartbeat prompt: ${w}\nIf you receive a heartbeat poll (a user message matching the heartbeat prompt above), and there is nothing that needs attention, reply exactly:\nHEARTBEAT_OK\nThe system treats a leading/trailing "HEARTBEAT_OK" as a heartbeat ack and may suppress it (not deliver to the user).\nIf something needs attention, do NOT include "HEARTBEAT_OK"; reply with the alert text instead.`):"",HEARTBEAT_PROMPT:h.cron.enabled?h.cron.heartbeat.message:"",CLAUDE_BUILT_IN_TOOLS:y?"":a(h.dataDir,p),SEARCH_IN_MEMORIES:"builtin-only"!==h.memory.recallStrategy?"## Memory Search Tools\n\nYou have access to memory search tools for recalling past conversations and knowledge:\n\n- `memory_search` — semantically searches Markdown chunks (~400 token target, 80-token overlap) from `MEMORY.md` + `memory/**/*.md`. It returns snippet text (capped ~700 chars), file path, line range, and score. No full file payload is returned.\n- `memory_get` — reads a specific memory Markdown file (workspace-relative), optionally from a starting line and for N lines. Paths outside `MEMORY.md` / `memory/` are rejected.\n\nUse `memory_search` to find relevant past context before answering questions that might relate to previous conversations. Use `memory_get` to read the full content of a memory file when a search snippet is not enough.":"",AVAILABLE_TOOLS:d(_),RUNTIME_LINE:m(h,u),WORKSPACE_FILES:r(S)}).replace(/\n{3,}/g,"\n\n");return c.debug(`System prompt built (mode=${p}, template=${E}, length=${b.length})`),b}export function resolvePlaceholders(e,n){return e.replace(/\{\{(\w+)\}\}/g,(e,t)=>t in n?n[t]:(c.warn(`Unknown placeholder: {{${t}}}`),`{{${t}}}`))}function d(e){if(!e||0===e.length)return"";const n=[];for(const t of e){const e=t,s=e.instance?._registeredTools;if(s)for(const[e,t]of Object.entries(s)){const s=t.description||"",o=t.inputSchema?.def?.shape,a=[];if(o)for(const[e,n]of Object.entries(o)){let t=n.type||"unknown";"ZodNumber"===t?t="number":"ZodString"===t?t="string":"ZodBoolean"===t?t="boolean":"ZodArray"===t?t="array":"ZodObject"===t?t="object":"ZodEnum"===t&&(t="enum");const s=n.isOptional?.()??!1;a.push(`${e}${s?"?":""}: ${t}`)}const r=a.length>0?`Params: { ${a.join(", ")} }`:"No parameters.",i=s.match(/\.\s*(Returns?\s.+?)\.?\s*$/i),l=i?i[1].trim():"";let c=`- **${e}**: ${i?s.slice(0,i.index).trim():s.trim()}. ${r}`;l&&(c+=`. ${l}.`),n.push(c)}}return 0===n.length?"":n.join("\n")}function m(o,a){return`host=${e()} | os=${n()} ${t()} (${s()}) | model=${o.agent.model} | channel=${a.channel} | session=${a.sessionKey}`}export function buildPrompt(e,n,t){const s=[],o=[];n&&s.push("<conversation_history>",n,"</conversation_history>","");for(const n of e.contentBlocks)"text"===n.type&&n.text?s.push(n.text):"image"===n.type&&n.imageBase64&&o.push({base64:n.imageBase64,mimeType:n.imageMimeType??"image/jpeg"});if(e.savedFiles.length>0){s.push(""),s.push("Files available in the current working directory:");for(const n of e.savedFiles)s.push(`- ${n}`)}return{text:s.join("\n"),images:o}}
@@ -0,0 +1,197 @@
1
+ import type { BuiltPrompt } from "./prompt-builder.js";
2
+ import { type AppConfig } from "../config.js";
3
+ /**
4
+ * Callback used by SessionAgent to forward messages to the user's channel
5
+ * (e.g. Telegram) while the agent is still processing.
6
+ */
7
+ export type ChannelSender = (channel: string, chatId: string, text: string, buttons?: Array<{
8
+ text: string;
9
+ callbackData: string;
10
+ }>) => Promise<void>;
11
+ /**
12
+ * Callback fired when the agent invokes a tool.
13
+ * Used to send tool-use notifications to the user's channel.
14
+ */
15
+ export type ToolUseNotifier = (channel: string, chatId: string, toolName: string) => Promise<void>;
16
+ /**
17
+ * Callback to set the typing indicator on the user's channel.
18
+ * Used after user answers a question so typing resumes while the agent processes.
19
+ */
20
+ export type TypingSetter = (channel: string, chatId: string) => Promise<void>;
21
+ /**
22
+ * Callback to clear the typing indicator on the user's channel.
23
+ * Used when the agent is waiting for user input (e.g. AskUserQuestion).
24
+ */
25
+ export type TypingClearer = (channel: string, chatId: string) => Promise<void>;
26
+ /**
27
+ * Callback to stream intermediate text blocks to the user's channel.
28
+ * Fired when the agent produces text followed by a tool call, so the user
29
+ * sees the text immediately instead of waiting for the entire turn to finish.
30
+ */
31
+ export type TextBlockStreamer = (channel: string, chatId: string, text: string) => Promise<void>;
32
+ /**
33
+ * Callback fired when usage data arrives in an SDK result message.
34
+ * Used to record per-session usage (cost, tokens, etc.) in memory.
35
+ */
36
+ export type UsageRecorder = (sessionKey: string, totalCostUsd: number | undefined, durationMs: number | undefined, numTurns: number | undefined, modelUsage: Record<string, any> | undefined) => void;
37
+ export type QueueMode = "queue" | "collect" | "steer";
38
+ export type QueueDropPolicy = "old" | "new" | "summarize";
39
+ export interface AgentResult {
40
+ response: string;
41
+ /** Complete response including parts already streamed to the channel. */
42
+ fullResponse?: string;
43
+ sessionId: string;
44
+ sessionReset: boolean;
45
+ errorType?: "max_turns" | "max_budget";
46
+ /** The API-level stop reason from the last assistant message. */
47
+ stopReason?: string | null;
48
+ }
49
+ export interface SessionContext {
50
+ /** Gateway session key, e.g. "telegram:12345" */
51
+ sessionKey: string;
52
+ /** Channel name, e.g. "telegram" */
53
+ channel: string;
54
+ /** Chat / conversation ID within the channel */
55
+ chatId: string;
56
+ /** SDK session ID for resumption (empty string if new session) */
57
+ sessionId: string;
58
+ /** Absolute path to the current memory conversation file */
59
+ memoryFile: string;
60
+ /** Absolute path to the current session's attachments folder */
61
+ attachmentsDir: string;
62
+ }
63
+ export declare class SessionAgent {
64
+ private sessionKey;
65
+ private config;
66
+ private queue;
67
+ private queryHandle;
68
+ private pendingResponses;
69
+ private currentResponse;
70
+ private currentSessionId;
71
+ private model;
72
+ private queueMode;
73
+ private closed;
74
+ private outputDone;
75
+ private initialized;
76
+ private opts;
77
+ private collectBuffer;
78
+ private lastCollectAt;
79
+ private debounceMs;
80
+ private debounceTimer;
81
+ private debounceResolve;
82
+ private queueCap;
83
+ private dropPolicy;
84
+ private droppedResolvers;
85
+ private droppedSummaries;
86
+ private sdkSlashCommands;
87
+ private channelSender;
88
+ private toolUseNotifier;
89
+ private typingSetter;
90
+ private typingClearer;
91
+ private textBlockStreamer;
92
+ private pendingTextBlock;
93
+ private streamedAny;
94
+ private usageRecorder;
95
+ private autoApproveTools;
96
+ private pendingPermission;
97
+ private pendingQuestion;
98
+ constructor(sessionKey: string, config: AppConfig, systemPrompt: string, subagentSystemPrompt: string, sessionId?: string, modelOverride?: string, nodeToolsServer?: unknown, messageToolsServer?: unknown, serverToolsServer?: unknown, cronToolsServer?: unknown, coderSkill?: boolean, subagentsEnabled?: boolean, customSubAgentsEnabled?: boolean, ttsToolsServer?: unknown, memoryToolsServer?: unknown, browserToolsServer?: unknown, sandboxEnabled?: boolean);
99
+ /**
100
+ * Send a prompt and wait for the agent's response.
101
+ * Behavior depends on queueMode when the agent is already busy.
102
+ */
103
+ send(prompt: BuiltPrompt): Promise<AgentResult>;
104
+ interrupt(): Promise<boolean>;
105
+ setModel(model: string): Promise<void>;
106
+ close(): void;
107
+ isActive(): boolean;
108
+ getSessionId(): string;
109
+ getModel(): string;
110
+ getSdkSlashCommands(): string[];
111
+ setChannelSender(sender: ChannelSender): void;
112
+ setToolUseNotifier(notifier: ToolUseNotifier): void;
113
+ setTypingSetter(setter: TypingSetter): void;
114
+ setTypingClearer(clearer: TypingClearer): void;
115
+ setTextBlockStreamer(streamer: TextBlockStreamer): void;
116
+ setUsageRecorder(recorder: UsageRecorder): void;
117
+ /**
118
+ * Build the env object for the SDK query() call.
119
+ *
120
+ * - "direct": the model has its own API base + key (e.g. another Anthropic
121
+ * account or a compatible endpoint). Pass everything through as-is, only
122
+ * override ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN / clear API_KEY.
123
+ *
124
+ * - "proxied": the model routes through a proxy (e.g. OpenRouter via
125
+ * atn-proxy). Uses fastUrl / fastProxyApiKey and strips env vars that
126
+ * are incompatible with proxy providers (ANTHROPIC_BETAS, EXTRA_BODY).
127
+ */
128
+ private buildEnvForModel;
129
+ /** Check if there is a pending interactive permission request waiting for user input. */
130
+ hasPendingPermission(): boolean;
131
+ /** Resolve a pending interactive permission request (called when user clicks Approve/Deny). */
132
+ resolvePermission(approved: boolean): void;
133
+ /** Check if the agent is currently processing a request. */
134
+ isBusy(): boolean;
135
+ /** Check if there is a pending AskUserQuestion waiting for user input. */
136
+ hasPendingQuestion(): boolean;
137
+ /** Resolve a pending AskUserQuestion (called when user clicks a button or sends text). */
138
+ resolveQuestion(answer: string): void;
139
+ /**
140
+ * Handle canUseTool callback from the SDK.
141
+ * In auto-approve mode, immediately allows all tools.
142
+ * In interactive mode, forwards the request to the user's channel with Approve/Deny buttons.
143
+ */
144
+ private handleCanUseTool;
145
+ /**
146
+ * Forward AskUserQuestion tool call to the user's channel as interactive
147
+ * messages with inline buttons. Multiple questions are sent in sequence.
148
+ */
149
+ private forwardAskUserQuestion;
150
+ /**
151
+ * Notify the user's channel about a tool invocation.
152
+ */
153
+ private notifyToolUse;
154
+ /**
155
+ * Send a buffered text block to the user's channel immediately.
156
+ * Called when a tool_use follows a text block so the user sees the
157
+ * text without waiting for the full processing to complete.
158
+ */
159
+ private flushPendingTextBlock;
160
+ /** queue mode: push directly to the SDK queue, one message = one response. */
161
+ private sendDirect;
162
+ /**
163
+ * collect mode: if the agent is busy, buffer the message. When the current
164
+ * turn finishes, all buffered messages are merged into a single prompt.
165
+ * Respects debounce, cap, and drop policy.
166
+ */
167
+ private sendCollect;
168
+ /**
169
+ * steer mode: if the agent is busy, interrupt it first, then send
170
+ * the new message. The interrupted caller gets whatever partial
171
+ * response was accumulated.
172
+ */
173
+ private sendSteer;
174
+ private applyDropPolicy;
175
+ /**
176
+ * Wait for the debounce period to expire, then flush the collect buffer.
177
+ * The timer resets each time a new message is collected (lastCollectAt moves).
178
+ */
179
+ private debounceThenFlush;
180
+ /**
181
+ * Merge all collected messages into a single prompt and push to the queue.
182
+ * Includes summaries of dropped messages if any.
183
+ */
184
+ private flushCollectBuffer;
185
+ /**
186
+ * Merge multiple prompts into a single prompt with a header.
187
+ * Includes dropped message summaries if any.
188
+ */
189
+ private mergePrompts;
190
+ private ensureInitialized;
191
+ private buildQueueMessage;
192
+ /**
193
+ * Background loop that reads SDK output and maps results to pending callers.
194
+ */
195
+ private processOutput;
196
+ }
197
+ //# sourceMappingURL=session-agent.d.ts.map
@@ -0,0 +1 @@
1
+ import{query as e}from"@anthropic-ai/claude-agent-sdk";import{MessageQueue as s}from"./message-queue.js";import{resolveModelId as t}from"../config.js";import{createLogger as o}from"../utils/logger.js";const i=o("SessionAgent");export class SessionAgent{sessionKey;config;queue;queryHandle=null;pendingResponses=[];currentResponse="";currentSessionId;model;queueMode;closed=!1;outputDone=!1;initialized=!1;opts;collectBuffer=[];lastCollectAt=0;debounceMs;debounceTimer=null;debounceResolve=null;queueCap;dropPolicy;droppedResolvers=[];droppedSummaries=[];sdkSlashCommands=[];channelSender=null;toolUseNotifier=null;typingSetter=null;typingClearer=null;textBlockStreamer=null;pendingTextBlock="";streamedAny=!1;usageRecorder=null;autoApproveTools;pendingPermission=null;pendingQuestion=null;constructor(e,o,n,r,l,a,u,h,c,p,d,g,m,f,y,$,b){this.sessionKey=e,this.config=o,this.currentSessionId=l??"";const T=a??o.agent.model;this.model=T?t(o,T):"",this.queueMode=o.agent.queueMode,this.debounceMs=Math.max(0,o.agent.queueDebounceMs),this.queueCap=Math.max(0,o.agent.queueCap),this.dropPolicy=o.agent.queueDropPolicy,this.autoApproveTools=o.agent.autoApproveTools,this.queue=new s,this.opts={...this.model?{model:this.model}:{},systemPrompt:d?{type:"preset",preset:"claude_code",append:n}:n,...o.agent.maxTurns>0?{maxTurns:o.agent.maxTurns}:{},cwd:o.agent.workspacePath,env:process.env,permissionMode:o.agent.permissionMode,allowDangerouslySkipPermissions:!1,...b?{sandbox:{enabled:!0,autoAllowBashIfSandboxed:!0,network:{allowLocalBinding:!0}}}:{},canUseTool:async(e,s)=>this.handleCanUseTool(e,s),hooks:{PreCompact:[{hooks:[async e=>{const s=e?.trigger??"auto";if(i.info(`[${this.sessionKey}] PreCompact hook fired (trigger=${s})`),this.channelSender){const e=this.sessionKey.indexOf(":");if(e>0){const t=this.sessionKey.substring(0,e),o=this.sessionKey.substring(e+1);if("cron"!==t){const e="auto"===s?"The conversation context is getting large — compacting memory to keep things running smoothly.":"Compacting conversation memory...";this.channelSender(t,o,e).catch(()=>{})}}}return{}}]}]},stderr:s=>{i.error(`[${e}] SDK stderr: ${s.trimEnd()}`)}};const w=o.agent.settingSources;"user"===w?this.opts.settingSources=["user"]:"project"===w?this.opts.settingSources=["project"]:"both"===w&&(this.opts.settingSources=["user","project"]);const S=o.agent.mainFallback;S&&(this.opts.fallbackModel=t(o,S)),o.agent.allowedTools.length>0&&(this.opts.allowedTools=o.agent.allowedTools),o.agent.disallowedTools.length>0&&(this.opts.disallowedTools=o.agent.disallowedTools);const v={};if(Object.keys(o.agent.mcpServers).length>0&&Object.assign(v,o.agent.mcpServers),u&&(v["node-tools"]=u),h&&(v["message-tools"]=h),c&&(v["server-tools"]=c),p&&(v["cron-tools"]=p),f&&(v["tts-tools"]=f),y&&(v["memory-tools"]=y),$&&(v["browser-tools"]=$),Object.keys(v).length>0&&(this.opts.mcpServers=v,this.opts.allowedTools&&this.opts.allowedTools.length>0))for(const e of Object.keys(v)){const s=`mcp__${e}__*`;this.opts.allowedTools.includes(s)||this.opts.allowedTools.push(s)}if(l&&(this.opts.resume=l),!1===g&&(this.opts.allowedTools&&this.opts.allowedTools.length>0?this.opts.allowedTools=this.opts.allowedTools.filter(e=>"Task"!==e):(this.opts.disallowedTools||(this.opts.disallowedTools=[]),this.opts.disallowedTools.includes("Task")||this.opts.disallowedTools.push("Task"))),m){const e={};for(const s of o.agent.customSubAgents){if(!s.enabled)continue;const t=s.expandContext?r+"\n\n"+s.prompt:s.prompt;e[s.name]={description:s.description,prompt:t,tools:s.tools,..."inherit"!==s.model?{model:s.model}:{}}}Object.keys(e).length>0&&(this.opts.agents=e)}const K=o.agent.plugins.filter(e=>e.enabled);K.length>0&&(this.opts.options={...this.opts.options,plugins:K.map(e=>({type:"local",path:e.path}))});const _=this.buildEnvForModel(this.model);this.opts.env=_.env,_.disableThinking&&(this.opts.maxThinkingTokens=0)}async send(e){if(this.closed||this.outputDone)throw new Error("SessionAgent is closed");switch(this.ensureInitialized(),this.queueMode){case"collect":return this.sendCollect(e);case"steer":return this.sendSteer(e);default:return this.sendDirect(e)}}async interrupt(){if(this.closed||!this.queryHandle)return!1;try{return await this.queryHandle.interrupt(),i.info(`[${this.sessionKey}] Interrupted`),!0}catch{return!1}}async setModel(e){if(this.queryHandle)try{await this.queryHandle.setModel(e),this.model=e,i.info(`[${this.sessionKey}] Model changed to ${e}`)}catch(e){i.error(`[${this.sessionKey}] Failed to set model: ${e}`)}}close(){if(this.closed)return;this.closed=!0,this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null),this.debounceResolve&&(this.debounceResolve(),this.debounceResolve=null),this.queue.close(),this.queryHandle&&this.queryHandle.close();const e=new Error("SessionAgent closed");for(const s of this.pendingResponses)s.reject(e);for(const s of this.collectBuffer)s.reject(e);for(const s of this.droppedResolvers)s.reject(e);this.pendingResponses=[],this.collectBuffer=[],this.droppedResolvers=[],this.droppedSummaries=[],i.info(`[${this.sessionKey}] Closed`)}isActive(){return!this.closed&&!this.outputDone}getSessionId(){return this.currentSessionId}getModel(){return this.model}getSdkSlashCommands(){return this.sdkSlashCommands}setChannelSender(e){this.channelSender=e}setToolUseNotifier(e){this.toolUseNotifier=e}setTypingSetter(e){this.typingSetter=e}setTypingClearer(e){this.typingClearer=e}setTextBlockStreamer(e){this.textBlockStreamer=e}setUsageRecorder(e){this.usageRecorder=e}buildEnvForModel(e){const s=this.config.models.find(s=>s.id===e);if(!s?.proxy||"not-used"===s.proxy)return{env:{...process.env},proxied:!1,disableThinking:!1};const t={...process.env};return"direct"===s.proxy?(t.ANTHROPIC_BASE_URL=s.baseURL,t.ANTHROPIC_AUTH_TOKEN=s.apiKey,t.ANTHROPIC_API_KEY="",i.info(`[${this.sessionKey}] Direct env applied for model ${e} (url=${t.ANTHROPIC_BASE_URL})`),{env:t,proxied:!0,disableThinking:!1}):(t.ANTHROPIC_BASE_URL=s.fastUrl||this.config.fastProxyUrl,t.ANTHROPIC_AUTH_TOKEN=s.fastProxyApiKey,t.ANTHROPIC_API_KEY="",delete t.ANTHROPIC_BETAS,delete t.CLAUDE_CODE_EXTRA_BODY,i.info(`[${this.sessionKey}] Proxy env applied for model ${e} (url=${t.ANTHROPIC_BASE_URL})`),{env:t,proxied:!0,disableThinking:!0})}hasPendingPermission(){return null!==this.pendingPermission}resolvePermission(e){if(!this.pendingPermission)return;const s=this.pendingPermission;this.pendingPermission=null,e?(i.info(`[${this.sessionKey}] Permission approved: ${s.toolName}`),s.resolve({behavior:"allow",updatedInput:s.input})):(i.info(`[${this.sessionKey}] Permission denied: ${s.toolName}`),s.resolve({behavior:"deny",message:"User denied this action"}))}isBusy(){return this.pendingResponses.length>0}hasPendingQuestion(){return null!==this.pendingQuestion}resolveQuestion(e){if(!this.pendingQuestion)return;const s=this.pendingQuestion;this.pendingQuestion=null,i.info(`[${this.sessionKey}] Question answered: "${e}" for "${s.questionText}"`),s.resolve(e)}async handleCanUseTool(e,s){if("AskUserQuestion"===e){if(!this.channelSender)return i.warn(`[${this.sessionKey}] No channel sender for AskUserQuestion, auto-approving`),{behavior:"allow",updatedInput:s};const e=this.sessionKey.indexOf(":");if(e<0)return{behavior:"allow",updatedInput:s};const t=this.sessionKey.substring(0,e),o=this.sessionKey.substring(e+1);if(!t||!o||"cron"===t)return{behavior:"allow",updatedInput:s};const n=s?.questions;if(!Array.isArray(n)||0===n.length)return{behavior:"allow",updatedInput:s};const r={};for(const e of n){const n=e.question||"?",l=Array.isArray(e.options)?e.options:[],a=[];if(e.header&&a.push(`*${e.header}*`),a.push(n),l.some(e=>e.description)){a.push("");for(const e of l){const s=e.description?`: ${e.description}`:"";a.push(`• ${e.label}${s}`)}}const u=a.join("\n");if(this.typingClearer)try{await this.typingClearer(t,o)}catch{}try{if(l.length>0){const e=l.map(e=>({text:e.label||String(e),callbackData:`__ask:${e.label||String(e)}`}));await this.channelSender(t,o,u,e)}else await this.channelSender(t,o,u)}catch(e){return i.error(`[${this.sessionKey}] Failed to send AskUserQuestion: ${e}`),{behavior:"allow",updatedInput:s}}const h=55e3,c=await new Promise(e=>{const s=setTimeout(()=>{if(this.pendingQuestion){this.pendingQuestion=null;const s=l.length>0?l[0].label||String(l[0]):"No answer";i.warn(`[${this.sessionKey}] Question timeout, defaulting to "${s}"`),this.channelSender&&this.channelSender(t,o,`[Timeout] Auto-selected: ${s}`).catch(()=>{}),e(s)}},h);this.pendingQuestion={resolve:t=>{clearTimeout(s),e(t)},questionText:n}});if(r[n]=c,this.typingSetter)try{await this.typingSetter(t,o)}catch{}}return i.info(`[${this.sessionKey}] AskUserQuestion answered: ${JSON.stringify(r)}`),{behavior:"allow",updatedInput:{questions:s.questions,answers:r}}}if(this.autoApproveTools)return i.debug(`[${this.sessionKey}] Auto-approving tool: ${e}`),{behavior:"allow",updatedInput:s};if(!this.channelSender)return i.warn(`[${this.sessionKey}] No channel sender for interactive permission, auto-approving: ${e}`),{behavior:"allow",updatedInput:s};const t=this.sessionKey.indexOf(":");if(t<0)return{behavior:"allow",updatedInput:s};const o=this.sessionKey.substring(0,t),n=this.sessionKey.substring(t+1);if(!o||!n||"cron"===o)return{behavior:"allow",updatedInput:s};const r=[`[Permission Request] Tool: ${e}`];if("Bash"===e&&s?.command)r.push(`Command: ${s.command}`),s.description&&r.push(`Description: ${s.description}`);else if("Write"===e&&s?.file_path)r.push(`File: ${s.file_path}`);else if("Edit"===e&&s?.file_path)r.push(`File: ${s.file_path}`);else if("ExitPlanMode"===e&&s?.plan){if(r.push(""),r.push(s.plan),Array.isArray(s.allowedPrompts)&&s.allowedPrompts.length>0){r.push(""),r.push("Requested permissions:");for(const e of s.allowedPrompts)r.push(` - [${e.tool}] ${e.prompt}`)}}else{const e=JSON.stringify(s);e.length<=300?r.push(`Input: ${e}`):r.push(`Input: ${e.slice(0,297)}...`)}r.push(""),r.push("Reply: approve to allow, deny to reject");const l=r.join("\n"),a=[{text:"Approve",callbackData:"__tool_perm:approve"},{text:"Deny",callbackData:"__tool_perm:deny"}];try{await this.channelSender(o,n,l,a)}catch(e){return i.error(`[${this.sessionKey}] Failed to send permission request: ${e}`),{behavior:"allow",updatedInput:s}}if(this.typingClearer)try{await this.typingClearer(o,n)}catch{}const u=12e4;return new Promise(t=>{const r=setTimeout(()=>{this.pendingPermission?.resolve===t&&(this.pendingPermission=null,i.warn(`[${this.sessionKey}] Permission timeout for ${e}, auto-denying`),this.channelSender&&this.channelSender(o,n,`[Permission timeout] Tool ${e} denied after 120s`).catch(()=>{}),t({behavior:"deny",message:"Permission request timed out"}))},u);this.pendingPermission={resolve:t,toolName:e,input:s};const l=t;this.pendingPermission.resolve=e=>{clearTimeout(r),l(e)}})}async forwardAskUserQuestion(e){if(!this.channelSender)return;const s=this.sessionKey.indexOf(":");if(s<0)return;const t=this.sessionKey.substring(0,s),o=this.sessionKey.substring(s+1);if(!t||!o||"cron"===t)return;const n=e?.questions;if(Array.isArray(n)){for(const e of n){const s=e.question||"?",n=Array.isArray(e.options)?e.options:[],r=[];if(e.header&&r.push(`*${e.header}*`),r.push(s),n.some(e=>e.description)){r.push("");for(const e of n){const s=e.description?`: ${e.description}`:"";r.push(`• ${e.label}${s}`)}}const l=r.join("\n");try{if(n.length>0){const e=n.map(e=>({text:e.label||String(e),callbackData:e.label||String(e)}));await this.channelSender(t,o,l,e)}else await this.channelSender(t,o,l)}catch(e){i.error(`[${this.sessionKey}] Failed to forward AskUserQuestion: ${e}`)}}if(this.typingClearer)try{await this.typingClearer(t,o)}catch(e){i.error(`[${this.sessionKey}] Failed to clear typing: ${e}`)}}}async notifyToolUse(e){if(!this.toolUseNotifier)return;const s=this.sessionKey.indexOf(":");if(s<0)return;const t=this.sessionKey.substring(0,s),o=this.sessionKey.substring(s+1);if(t&&o&&"cron"!==t)try{await this.toolUseNotifier(t,o,e)}catch(e){i.error(`[${this.sessionKey}] Failed to notify tool use: ${e}`)}}async flushPendingTextBlock(){if(!this.textBlockStreamer||!this.pendingTextBlock)return;const e=this.sessionKey.indexOf(":");if(e<0)return;const s=this.sessionKey.substring(0,e),t=this.sessionKey.substring(e+1);if(!s||!t||"cron"===s)return;const o=this.pendingTextBlock;this.pendingTextBlock="",this.streamedAny=!0;try{await this.textBlockStreamer(s,t,o)}catch(e){i.error(`[${this.sessionKey}] Text block stream error: ${e}`)}}sendDirect(e){if(this.queueCap>0&&this.pendingResponses.length>=this.queueCap)return i.warn(`[${this.sessionKey}] Queue cap reached (${this.queueCap}), rejecting message`),Promise.resolve({response:"Queue is full. Please wait for the current processing to complete.",sessionId:this.currentSessionId,sessionReset:!1});const s=this.buildQueueMessage(e);return new Promise((e,t)=>{this.pendingResponses.push({resolve:e,reject:t}),this.queue.push(s),i.info(`[${this.sessionKey}] Message queued (pending=${this.pendingResponses.length})`)})}sendCollect(e){return this.pendingResponses.length>0?this.queueCap>0&&this.collectBuffer.length>=this.queueCap?this.applyDropPolicy(e):(this.lastCollectAt=Date.now(),i.info(`[${this.sessionKey}] Collecting message (buffer=${this.collectBuffer.length+1})`),new Promise((s,t)=>{this.collectBuffer.push({prompt:e,resolve:s,reject:t})})):this.sendDirect(e)}async sendSteer(e){return this.pendingResponses.length>0&&(i.info(`[${this.sessionKey}] Steer: interrupting current processing`),await this.interrupt()),this.sendDirect(e)}applyDropPolicy(e){if("new"===this.dropPolicy)return i.warn(`[${this.sessionKey}] Queue cap reached, rejecting new message`),Promise.resolve({response:"Queue is full. Please wait for the current processing to complete.",sessionId:this.currentSessionId,sessionReset:!1});const s=this.collectBuffer.shift();return"summarize"===this.dropPolicy&&this.droppedSummaries.push(function(e,s){const t=e.replace(/\s+/g," ").trim();return t.length<=s?t:`${t.slice(0,s-1).trimEnd()}…`}(s.prompt.text,140)),this.droppedResolvers.push({resolve:s.resolve,reject:s.reject}),i.warn(`[${this.sessionKey}] Queue cap reached, dropped oldest message (policy=${this.dropPolicy}, dropped=${this.droppedResolvers.length})`),this.lastCollectAt=Date.now(),new Promise((s,t)=>{this.collectBuffer.push({prompt:e,resolve:s,reject:t})})}async debounceThenFlush(){if(this.debounceMs<=0||this.closed)this.flushCollectBuffer();else{for(;!this.closed&&this.collectBuffer.length>0;){const e=Date.now()-this.lastCollectAt;if(e>=this.debounceMs)break;await new Promise(s=>{this.debounceResolve=s,this.debounceTimer=setTimeout(s,this.debounceMs-e)}),this.debounceTimer=null,this.debounceResolve=null}this.closed||this.flushCollectBuffer()}}flushCollectBuffer(){if(0===this.collectBuffer.length&&0===this.droppedResolvers.length)return;const e=this.collectBuffer.splice(0),s=e.map(e=>e.prompt),t=this.mergePrompts(s),o=this.buildQueueMessage(t),n=[...this.droppedResolvers.splice(0),...e.map(e=>({resolve:e.resolve,reject:e.reject}))];this.droppedSummaries=[],this.pendingResponses.push({resolve:e=>{for(const s of n)s.resolve(e)},reject:e=>{for(const s of n)s.reject(e)}}),this.queue.push(o),i.info(`[${this.sessionKey}] Flushed ${e.length} collected message(s) as one prompt`)}mergePrompts(e){const s=[],t=[];if(this.droppedSummaries.length>0){s.push(`[${this.droppedSummaries.length} earlier message(s) dropped due to queue cap]`);for(const e of this.droppedSummaries)s.push(`- ${e}`);s.push("")}if(1===e.length&&0===this.droppedSummaries.length)return e[0];if(e.length>0){s.push("[Queued messages while agent was busy]");for(let o=0;o<e.length;o++)e[o].text&&s.push(`${o+1}. ${e[o].text}`),t.push(...e[o].images)}return{text:s.join("\n"),images:t}}ensureInitialized(){this.initialized||(this.initialized=!0,i.info(`[${this.sessionKey}] Starting agent: model=${this.model}, mode=${this.queueMode}, debounce=${this.debounceMs}ms, cap=${this.queueCap||"unlimited"}, drop=${this.dropPolicy}, session=${this.currentSessionId||"new"}`),this.queryHandle=e({prompt:this.queue,options:this.opts}),this.processOutput())}buildQueueMessage(e){if(0===e.images.length)return i.debug(`[${this.sessionKey}] SDK request: text-only (${e.text.length} chars): ${this.config.verboseDebugLogs?e.text:e.text.slice(0,15)+"..."}`),{type:"user",message:{role:"user",content:e.text}};const s=[];for(const t of e.images)s.push({type:"image",source:{type:"base64",media_type:t.mimeType,data:t.base64}});return e.text&&s.push({type:"text",text:e.text}),i.debug(`[${this.sessionKey}] SDK request: ${s.length} block(s) [${s.map(e=>"image"===e.type?`image/${e.source.media_type}`:`text(${e.text?.length??0})`).join(", ")}]`),{type:"user",message:{role:"user",content:s}}}async processOutput(){if(this.queryHandle)try{for await(const e of this.queryHandle){if(this.closed)break;if(i.debug(`[${this.sessionKey}] SDK message: type=${e.type}, subtype=${e.subtype??"-"}, keys=${Object.keys(e).join(",")}`),"system"===e.type){const s=e,t=s.subtype;if("init"===t){const e=s.slash_commands;Array.isArray(e)&&(this.sdkSlashCommands=e.map(e=>e.replace(/^\//,"")))}if("compact_boundary"===t){const e=s.compact_metadata,t=["Context compacted."];e?.pre_tokens&&t.push(`Pre-compaction tokens: ${e.pre_tokens}.`),e?.trigger&&t.push(`Trigger: ${e.trigger}.`),this.currentResponse=t.join(" "),i.info(`[${this.sessionKey}] Compact: ${this.currentResponse}`)}else if("init"!==t&&"status"!==t){const{type:e,...o}=s;this.currentResponse=JSON.stringify(o,null,2),i.info(`[${this.sessionKey}] System message (${t??"unknown"}): ${this.currentResponse.slice(0,200)}`)}}if("assistant"===e.type){const s=e.message.content,t=s.filter(e=>"text"===e.type).map(e=>e.text).join("");t&&(this.currentResponse=t,this.pendingTextBlock=t);const o=s.map(e=>e.type).join(", ");i.debug(`[${this.sessionKey}] SDK assistant message: blocks=[${o}], text length=${t.length}: ${this.config.verboseDebugLogs?t:t.slice(0,15)+"..."}`);s.some(e=>"tool_use"===e.type)&&this.pendingTextBlock&&this.textBlockStreamer&&await this.flushPendingTextBlock();for(const e of s)if("tool_use"===e.type){const s=JSON.stringify(e.input);i.debug(`[${this.sessionKey}] Tool call: ${e.name} ${this.config.verboseDebugLogs?s:s.slice(0,100)+(s.length>100?"...":"")}`),this.toolUseNotifier&&"AskUserQuestion"!==e.name&&this.notifyToolUse(e.name).catch(e=>{i.error(`[${this.sessionKey}] Tool use notification error: ${e}`)})}}if("tool_progress"===e.type){const s=e;i.debug(`[${this.sessionKey}] Tool progress: ${s.tool_name} (${s.elapsed_time_seconds}s)`)}if("result"===e.type){const s=e;let t;i.debug(`[${this.sessionKey}] SDK result: subtype=${s.subtype}, stop_reason=${s.stop_reason??"null"}, session=${s.session_id??"n/a"}, result length=${s.result?.length??0}`),"session_id"in s&&(this.currentSessionId=s.session_id),this.usageRecorder&&(void 0!==s.total_cost_usd||s.modelUsage)&&this.usageRecorder(this.sessionKey,s.total_cost_usd,s.duration_ms,s.num_turns,s.modelUsage);const o=s.stop_reason??null;if("success"===s.subtype){if(s.result)this.currentResponse=s.result;else if(!this.currentResponse&&(void 0!==s.total_cost_usd||s.usage)){const e=[];if(void 0!==s.total_cost_usd&&e.push(`Total cost: $${Number(s.total_cost_usd).toFixed(4)}`),void 0!==s.duration_ms&&e.push(`Duration: ${(s.duration_ms/1e3).toFixed(1)}s`),void 0!==s.num_turns&&e.push(`Turns: ${s.num_turns}`),s.modelUsage)for(const[t,o]of Object.entries(s.modelUsage)){const s=o,i=[` ${t}:`];s.inputTokens&&i.push(`input=${s.inputTokens}`),s.outputTokens&&i.push(`output=${s.outputTokens}`),s.cacheReadInputTokens&&i.push(`cache_read=${s.cacheReadInputTokens}`),s.cacheCreationInputTokens&&i.push(`cache_create=${s.cacheCreationInputTokens}`),void 0!==s.costUSD&&i.push(`cost=$${Number(s.costUSD).toFixed(4)}`),e.push(i.join(" "))}e.length>0&&(this.currentResponse=e.join("\n"))}"refusal"===o?(i.warn(`[${this.sessionKey}] Model refused the request`),this.currentResponse||(this.currentResponse="I'm unable to fulfill this request.")):"max_tokens"===o&&i.warn(`[${this.sessionKey}] Response truncated: output token limit reached`)}else if("error_max_turns"===s.subtype)t="max_turns",i.warn(`[${this.sessionKey}] Max turns reached`);else if("error_max_budget_usd"===s.subtype)t="max_budget",i.warn(`[${this.sessionKey}] Max budget reached`);else{const e=s.errors??[];e.some(e=>e.includes("aborted"))?i.info(`[${this.sessionKey}] Request aborted (steer interrupt)`):i.error(`[${this.sessionKey}] SDK error: ${JSON.stringify(s)}`)}const n=this.pendingResponses.shift();if(n){const e=this.currentResponse||"";let s=e;this.streamedAny&&(s=this.pendingTextBlock||""),i.info(`[${this.sessionKey}] Response ready: session=${this.currentSessionId}, length=${s.length}${this.streamedAny?` (streamed, full=${e.length})`:""}`),n.resolve({response:s,fullResponse:this.streamedAny?e:void 0,sessionId:this.currentSessionId,sessionReset:!1,errorType:t,stopReason:o})}this.currentResponse="",this.pendingTextBlock="",this.streamedAny=!1,"collect"===this.queueMode&&(this.collectBuffer.length>0||this.droppedResolvers.length>0)&&await this.debounceThenFlush()}}}catch(e){i.error(`[${this.sessionKey}] Output stream error: ${e}`);const s=this.pendingResponses.shift();s&&(this.currentSessionId?(i.warn(`[${this.sessionKey}] Session corrupted: ${this.currentSessionId}`),s.resolve({response:"",sessionId:"",sessionReset:!0})):s.reject(e instanceof Error?e:new Error(String(e))));const t=new Error("SessionAgent terminated");for(const e of this.pendingResponses)e.reject(t);for(const e of this.collectBuffer)e.reject(t);for(const e of this.droppedResolvers)e.reject(t);this.pendingResponses=[],this.collectBuffer=[],this.droppedResolvers=[],this.droppedSummaries=[]}finally{this.outputDone=!0}}}
@@ -0,0 +1,26 @@
1
+ export interface SessionRecord {
2
+ sessionKey: string;
3
+ sdkSessionId: string | null;
4
+ modelOverride: string | null;
5
+ createdAt: string;
6
+ lastActivity: string;
7
+ }
8
+ export declare class SessionDB {
9
+ private db;
10
+ constructor(dbPath: string);
11
+ private initSchema;
12
+ /** Migrate data from old `sessions` table if it exists */
13
+ private migrate;
14
+ getOrCreate(sessionKey: string): SessionRecord;
15
+ updateSdkSessionId(sessionKey: string, sdkSessionId: string): void;
16
+ updateModelOverride(sessionKey: string, model: string): void;
17
+ getBySessionKey(sessionKey: string): SessionRecord | null;
18
+ listSessions(): SessionRecord[];
19
+ /** Clear SDK session ID so the next call starts a fresh conversation, but keep modelOverride. */
20
+ clearSdkSessionId(sessionKey: string): void;
21
+ /** Return sessions with an active SDK session whose last_activity is older than `maxAgeMs` milliseconds. */
22
+ listStaleSessions(maxAgeMs: number): SessionRecord[];
23
+ deleteSession(sessionKey: string): void;
24
+ close(): void;
25
+ }
26
+ //# sourceMappingURL=session-db.d.ts.map
@@ -0,0 +1 @@
1
+ import s from"better-sqlite3";import{createLogger as e}from"../utils/logger.js";const i=e("SessionDB");function t(s){return{sessionKey:s.session_key,sdkSessionId:s.sdk_session_id,modelOverride:s.model_override,createdAt:s.created_at,lastActivity:s.last_activity}}export class SessionDB{db;constructor(e){this.db=new s(e),this.db.pragma("journal_mode = WAL"),this.initSchema(),this.migrate(),i.info(`Session database opened at ${e}`)}initSchema(){this.db.exec("\n CREATE TABLE IF NOT EXISTS sessions_map (\n session_key TEXT PRIMARY KEY NOT NULL,\n sdk_session_id TEXT,\n model_override TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n last_activity TEXT NOT NULL DEFAULT (datetime('now'))\n );\n ")}migrate(){this.db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='sessions'").get()&&(i.info("Migrating data from old 'sessions' table to 'sessions_map'..."),this.db.exec("\n INSERT OR IGNORE INTO sessions_map (session_key, sdk_session_id, model_override, created_at, last_activity)\n SELECT session_key, sdk_session_id, model_override, created_at, last_activity\n FROM sessions;\n "),this.db.exec("DROP TABLE sessions;"),i.info("Migration complete — old 'sessions' table dropped."))}getOrCreate(s){return this.db.prepare("INSERT INTO sessions_map (session_key)\n VALUES (?)\n ON CONFLICT(session_key) DO UPDATE SET\n last_activity = datetime('now')").run(s),this.getBySessionKey(s)}updateSdkSessionId(s,e){this.db.prepare("UPDATE sessions_map SET sdk_session_id = ?, last_activity = datetime('now') WHERE session_key = ?").run(e,s),i.info(`SDK session updated: ${s} -> ${e}`)}updateModelOverride(s,e){this.db.prepare("UPDATE sessions_map SET model_override = ?, last_activity = datetime('now') WHERE session_key = ?").run(e,s)}getBySessionKey(s){const e=this.db.prepare("SELECT * FROM sessions_map WHERE session_key = ?").get(s);return e?t(e):null}listSessions(){return this.db.prepare("SELECT * FROM sessions_map ORDER BY last_activity DESC").all().map(t)}clearSdkSessionId(s){this.db.prepare("UPDATE sessions_map SET sdk_session_id = NULL, last_activity = datetime('now') WHERE session_key = ?").run(s),i.info(`SDK session cleared (model preserved): ${s}`)}listStaleSessions(s){const e=new Date(Date.now()-s).toISOString().replace("T"," ").replace("Z","");return this.db.prepare("SELECT * FROM sessions_map WHERE sdk_session_id IS NOT NULL AND last_activity < ?").all(e).map(t)}deleteSession(s){this.db.prepare("DELETE FROM sessions_map WHERE session_key = ?").run(s),i.info(`Session deleted: ${s}`)}close(){this.db.close()}}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Enhanced error handling for session-related errors
3
+ * Distinguishes between different types of failures for better recovery
4
+ */
5
+ export declare enum SessionErrorType {
6
+ AGENT_CLOSED = "AGENT_CLOSED",
7
+ SESSION_CORRUPTED = "SESSION_CORRUPTED",
8
+ SESSION_NOT_FOUND = "SESSION_NOT_FOUND",
9
+ API_ERROR = "API_ERROR",
10
+ NETWORK_ERROR = "NETWORK_ERROR",
11
+ UNKNOWN = "UNKNOWN"
12
+ }
13
+ export interface SessionErrorContext {
14
+ sessionKey: string;
15
+ sessionId?: string;
16
+ error: Error;
17
+ timestamp: Date;
18
+ }
19
+ export declare class SessionErrorHandler {
20
+ /**
21
+ * Analyze error and determine its type
22
+ */
23
+ static analyzeError(err: Error, context: Partial<SessionErrorContext>): SessionErrorType;
24
+ /**
25
+ * Determine recovery strategy based on error type
26
+ */
27
+ static getRecoveryStrategy(errorType: SessionErrorType): RecoveryStrategy;
28
+ }
29
+ interface RecoveryStrategy {
30
+ action: "retry_with_new_agent" | "clear_and_retry" | "retry_with_backoff" | "retry_immediate" | "fallback";
31
+ clearSession: boolean;
32
+ notifyUser: boolean;
33
+ logLevel: string;
34
+ message: string;
35
+ }
36
+ export {};
37
+ //# sourceMappingURL=session-error-handler.d.ts.map
@@ -0,0 +1 @@
1
+ import{createLogger as e}from"../utils/logger.js";const r=e("SessionErrorHandler");export var SessionErrorType;!function(e){e.AGENT_CLOSED="AGENT_CLOSED",e.SESSION_CORRUPTED="SESSION_CORRUPTED",e.SESSION_NOT_FOUND="SESSION_NOT_FOUND",e.API_ERROR="API_ERROR",e.NETWORK_ERROR="NETWORK_ERROR",e.UNKNOWN="UNKNOWN"}(SessionErrorType||(SessionErrorType={}));export class SessionErrorHandler{static analyzeError(e,s){const o=e.message.toLowerCase();return o.includes("sessionagent closed")||o.includes("agent closed")||o.includes("closed")?(r.debug(`[${s.sessionKey}] Error type: AGENT_CLOSED`),SessionErrorType.AGENT_CLOSED):o.includes("session not found")||o.includes("invalid session")||o.includes("session does not exist")?(r.debug(`[${s.sessionKey}] Error type: SESSION_NOT_FOUND`),SessionErrorType.SESSION_NOT_FOUND):o.includes("api error")||o.includes("unauthorized")||o.includes("rate limit")?(r.debug(`[${s.sessionKey}] Error type: API_ERROR`),SessionErrorType.API_ERROR):o.includes("econnrefused")||o.includes("timeout")||o.includes("network")?(r.debug(`[${s.sessionKey}] Error type: NETWORK_ERROR`),SessionErrorType.NETWORK_ERROR):o.includes("corrupted")||o.includes("invalid state")||o.includes("resume failed")?(r.debug(`[${s.sessionKey}] Error type: SESSION_CORRUPTED`),SessionErrorType.SESSION_CORRUPTED):(r.debug(`[${s.sessionKey}] Error type: UNKNOWN - ${o}`),SessionErrorType.UNKNOWN)}static getRecoveryStrategy(e){switch(e){case SessionErrorType.AGENT_CLOSED:return{action:"retry_with_new_agent",clearSession:!1,notifyUser:!1,logLevel:"info",message:"Agent was closed (likely due to restart), retrying with new agent"};case SessionErrorType.SESSION_NOT_FOUND:case SessionErrorType.SESSION_CORRUPTED:return{action:"clear_and_retry",clearSession:!0,notifyUser:!1,logLevel:"warn",message:"Session invalid, starting fresh"};case SessionErrorType.API_ERROR:return{action:"retry_with_backoff",clearSession:!1,notifyUser:!0,logLevel:"error",message:"API error, retrying with backoff"};case SessionErrorType.NETWORK_ERROR:return{action:"retry_immediate",clearSession:!1,notifyUser:!1,logLevel:"warn",message:"Network error, retrying immediately"};default:return{action:"fallback",clearSession:!0,notifyUser:!0,logLevel:"error",message:"Unknown error, falling back to session reset"}}}}
@@ -0,0 +1,19 @@
1
+ import type { SessionDB } from "./session-db.js";
2
+ export interface SessionEntry {
3
+ sessionId: string | undefined;
4
+ sessionKey: string;
5
+ createdAt: number;
6
+ lastActivity: number;
7
+ model?: string;
8
+ }
9
+ export declare class SessionManager {
10
+ private sessionDb;
11
+ constructor(sessionDb: SessionDB);
12
+ getOrCreate(sessionKey: string): SessionEntry;
13
+ updateSessionId(key: string, sessionId: string): void;
14
+ getModel(key: string): string | undefined;
15
+ setModel(key: string, model: string): void;
16
+ resetSession(key: string): void;
17
+ destroy(): void;
18
+ }
19
+ //# sourceMappingURL=session-manager.d.ts.map
@@ -0,0 +1 @@
1
+ import{createLogger as s}from"../utils/logger.js";const e=s("SessionManager");export class SessionManager{sessionDb;constructor(s){this.sessionDb=s}getOrCreate(s){const i=this.sessionDb.getOrCreate(s);return e.info(`Session: ${i.sessionKey} (sdk=${i.sdkSessionId??"new"})`),function(s){return{sessionId:s.sdkSessionId??void 0,sessionKey:s.sessionKey,createdAt:new Date(s.createdAt+"Z").getTime(),lastActivity:new Date(s.lastActivity+"Z").getTime(),model:s.modelOverride??void 0}}(i)}updateSessionId(s,e){this.sessionDb.updateSdkSessionId(s,e)}getModel(s){const e=this.sessionDb.getBySessionKey(s);return e?.modelOverride??void 0}setModel(s,e){this.sessionDb.updateModelOverride(s,e)}resetSession(s){this.sessionDb.clearSdkSessionId(s),e.info(`Session reset: ${s}`)}destroy(){}}
@@ -0,0 +1,51 @@
1
+ export interface WorkspaceFile {
2
+ name: string;
3
+ path: string;
4
+ content: string;
5
+ missing: boolean;
6
+ }
7
+ /**
8
+ * Seed workspace files and system prompt templates on first run.
9
+ *
10
+ * - Workspace .md files: templates/ → gmabPath/
11
+ * - System prompt templates: templates/ → gmabPath/.templates/
12
+ *
13
+ * Does not overwrite existing files.
14
+ */
15
+ export declare function ensureWorkspaceFiles(dataDir: string): void;
16
+ /**
17
+ * Load all workspace .md files from gmabPath.
18
+ * Includes MEMORY.md/memory.md if present (not auto-created).
19
+ */
20
+ export declare function loadWorkspaceFiles(dataDir: string): WorkspaceFile[];
21
+ /**
22
+ * Filter workspace files for subagent mode — only AGENTS.md and TOOLS.md.
23
+ */
24
+ export declare function filterForSubagent(files: WorkspaceFile[]): WorkspaceFile[];
25
+ /**
26
+ * Truncate content if it exceeds maxChars.
27
+ * Keeps 70% from head, 20% from tail, with a marker in between.
28
+ */
29
+ export declare function truncateContent(content: string, fileName: string, maxChars?: number): string;
30
+ /**
31
+ * Format workspace files into a single string for the {{WORKSPACE_FILES}} placeholder.
32
+ * File contents are concatenated directly — each file already contains its own heading.
33
+ * HEARTBEAT.md and BOOTSTRAP.md are excluded (handled separately).
34
+ */
35
+ export declare function formatWorkspaceFiles(files: WorkspaceFile[]): string;
36
+ /**
37
+ * Load built-in tools from CBINT.json and format for the system prompt.
38
+ * Returns a formatted list of tools for the given mode (full = main agent, minimal = sub agent).
39
+ */
40
+ export declare function loadBuiltInTools(dataDir: string, mode: "full" | "minimal"): string;
41
+ /**
42
+ * Load a template file, returning empty string if it doesn't exist or is empty.
43
+ * Used for optional content blocks like CBINT.json.
44
+ */
45
+ export declare function loadTemplateOrEmpty(dataDir: string, templateName: string): string;
46
+ /**
47
+ * Load a system prompt template from gmabPath/.templates/.
48
+ * Falls back to the bundled seed in the repo's templates/ directory.
49
+ */
50
+ export declare function loadTemplate(dataDir: string, templateName: string): string;
51
+ //# sourceMappingURL=workspace-files.d.ts.map
@@ -0,0 +1 @@
1
+ import{readFileSync as t,copyFileSync as n,existsSync as e,mkdirSync as o}from"node:fs";import{join as r}from"node:path";import{createLogger as s}from"../utils/logger.js";import{resolveInstallPkgDir as a}from"../utils/package-paths.js";const c=s("WorkspaceFiles"),i=["AGENTS.md","SOUL.md","TOOLS.md","IDENTITY.md","USER.md","HEARTBEAT.md","BOOTSTRAP.md"],u=["SYSTEM_PROMPT.md","SYSTEM_PROMPT_SUBAGENT.md","CBINT.json"],f=["MEMORY.md","memory.md"],m=new Set(["AGENTS.md","TOOLS.md"]);export function ensureWorkspaceFiles(t){o(t,{recursive:!0});const s=p(),a=r(t,".templates");o(a,{recursive:!0});for(const o of i){const a=r(t,o);if(!e(a)){const t=r(s,o);e(t)&&(n(t,a),c.info(`Seeded ${o} → ${a}`))}}for(const t of u){const o=r(a,t);if(!e(o)){const a=r(s,t);e(a)&&(n(a,o),c.info(`Seeded template ${t} → ${o}`))}}}export function loadWorkspaceFiles(n){const o=[];for(const s of i){const a=r(n,s);if(e(a))try{const n=t(a,"utf-8");o.push({name:s,path:a,content:n,missing:!1})}catch(t){c.warn(`Failed to read ${a}: ${t}`),o.push({name:s,path:a,content:"",missing:!0})}}for(const s of f){const a=r(n,s);if(e(a)){try{const n=t(a,"utf-8");o.push({name:s,path:a,content:n,missing:!1})}catch(t){c.warn(`Failed to read ${a}: ${t}`)}break}}return o}export function filterForSubagent(t){return t.filter(t=>m.has(t.name))}export function truncateContent(t,n,e=2e4){if(t.length<=e)return t;const o=Math.floor(.7*e),r=Math.floor(.2*e),s=t.slice(0,o),a=t.slice(-r),i=t.length-o-r;return c.debug(`Truncated ${n}: ${t.length} → ${o+r} chars (skipped ${i})`),`${s}\n\n[... ${i} characters omitted from ${n} ...]\n\n${a}`}const l=new Set(["HEARTBEAT.md","BOOTSTRAP.md"]);export function formatWorkspaceFiles(t){const n=t.filter(t=>!t.missing&&t.content.trim().length>0&&!l.has(t.name));return 0===n.length?"(No workspace files found)":n.map(t=>truncateContent(t.content,t.name)).join("\n\n")}export function loadBuiltInTools(t,n){const e=loadTemplateOrEmpty(t,"CBINT.json");if(!e.trim())return"";try{const t=JSON.parse(e),o="full"===n?{...t.tools_main_agent||{},...t.tools_sub_agent||{}}:t.tools_sub_agent||{},r=Object.entries(o);if(0===r.length)return"";const s=[];for(const[t,n]of r)s.push(`- **${t}**: ${n}`);return s.join("\n")}catch(t){return c.warn(`Failed to parse CBINT.json: ${t}`),""}}export function loadTemplateOrEmpty(n,o){const s=r(n,".templates",o);if(e(s))try{return t(s,"utf-8")}catch{return""}const a=r(p(),o);if(e(a))try{return t(a,"utf-8")}catch{return""}return""}export function loadTemplate(n,o){const s=r(n,".templates",o);if(e(s))return t(s,"utf-8");const a=r(p(),o);if(e(a))return c.warn(`Template ${o} not found in ${n}/.templates/, using bundled seed`),t(a,"utf-8");throw new Error(`Template ${o} not found in ${n}/.templates/ or bundled seeds`)}function p(){return a()}
@@ -0,0 +1,9 @@
1
+ import type { TokenDB, TokenRecord } from "./token-db.js";
2
+ export interface AuthResult {
3
+ authorized: boolean;
4
+ token?: TokenRecord;
5
+ reason?: string;
6
+ }
7
+ export declare function validateBearerToken(tokenDb: TokenDB, authHeader: string | undefined, channel: string): AuthResult;
8
+ export declare function validateChannelUser(tokenDb: TokenDB, userId: string, channel: string, dmPolicy: string, allowFrom: Array<string | number>): AuthResult;
9
+ //# sourceMappingURL=auth-middleware.d.ts.map
@@ -0,0 +1 @@
1
+ import{createLogger as e}from"../utils/logger.js";const t=e("Auth");export function validateBearerToken(e,n,r){if(!n?.startsWith("Bearer "))return{authorized:!1,reason:"Missing or invalid Authorization header"};const o=n.slice(7),a=e.validateToken(o,r);return a?{authorized:!0,token:a}:(t.warn(`Invalid token attempt on channel=${r}`),{authorized:!1,reason:"Invalid or expired token"})}export function validateChannelUser(e,n,r,o,a){switch(o){case"open":default:return{authorized:!0};case"token":{const o=e.validateUserId(n,r);return o?{authorized:!0,token:o}:(t.warn(`Unauthorized user ${n} on channel=${r} (token policy)`),{authorized:!1,reason:"User not authorized. Register a token."})}case"allowlist":return a.some(e=>String(e)===String(n))?{authorized:!0}:(t.warn(`User ${n} not in allowlist for channel=${r}`),{authorized:!1,reason:"User not in allowlist."})}}
@@ -0,0 +1,30 @@
1
+ export interface NodeSignatureRecord {
2
+ id: number;
3
+ nodeId: string;
4
+ signature: string;
5
+ hostname: string;
6
+ displayName: string;
7
+ platform: string;
8
+ arch: string;
9
+ status: "pending" | "approved" | "revoked";
10
+ createdAt: string;
11
+ approvedAt: string | null;
12
+ lastSeenAt: string | null;
13
+ }
14
+ export declare class NodeSignatureDB {
15
+ private db;
16
+ constructor(dbPath: string);
17
+ private initSchema;
18
+ findBySignature(signature: string): NodeSignatureRecord | null;
19
+ getById(id: number): NodeSignatureRecord | null;
20
+ createOrUpdatePending(nodeId: string, signature: string, hostname: string, displayName: string, platform: string, arch: string): NodeSignatureRecord;
21
+ approve(id: number): void;
22
+ revoke(id: number): void;
23
+ delete(id: number): void;
24
+ updateLastSeen(id: number): void;
25
+ updateNodeInfo(id: number, nodeId: string, hostname: string, displayName: string, platform: string, arch: string): void;
26
+ listAll(): NodeSignatureRecord[];
27
+ listByStatus(status: string): NodeSignatureRecord[];
28
+ close(): void;
29
+ }
30
+ //# sourceMappingURL=node-signature-db.d.ts.map
@@ -0,0 +1 @@
1
+ import e from"better-sqlite3";import{createLogger as t}from"../utils/logger.js";const n=t("NodeSignatureDB");function a(e){return{id:e.id,nodeId:e.node_id,signature:e.signature,hostname:e.hostname,displayName:e.display_name,platform:e.platform,arch:e.arch,status:e.status,createdAt:e.created_at,approvedAt:e.approved_at,lastSeenAt:e.last_seen_at}}export class NodeSignatureDB{db;constructor(t){this.db=new e(t),this.db.pragma("journal_mode = WAL"),this.initSchema(),n.info(`Node signature database opened at ${t}`)}initSchema(){this.db.exec("\n CREATE TABLE IF NOT EXISTS node_signatures (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n node_id TEXT NOT NULL,\n signature TEXT UNIQUE NOT NULL,\n hostname TEXT NOT NULL DEFAULT '',\n display_name TEXT NOT NULL DEFAULT '',\n platform TEXT NOT NULL DEFAULT '',\n arch TEXT NOT NULL DEFAULT '',\n status TEXT NOT NULL DEFAULT 'pending',\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n approved_at TEXT,\n last_seen_at TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_node_sig_signature ON node_signatures(signature);\n CREATE INDEX IF NOT EXISTS idx_node_sig_status ON node_signatures(status);\n ")}findBySignature(e){const t=this.db.prepare("SELECT * FROM node_signatures WHERE signature = ?").get(e);return t?a(t):null}getById(e){const t=this.db.prepare("SELECT * FROM node_signatures WHERE id = ?").get(e);return t?a(t):null}createOrUpdatePending(e,t,a,s,r,d){const i=this.findBySignature(t);if(i)return i.status,this.db.prepare("UPDATE node_signatures\n SET node_id = ?, hostname = ?, display_name = ?, platform = ?, arch = ?\n WHERE id = ?").run(e,a,s,r,d,i.id),this.getById(i.id);const o=this.db.prepare("INSERT INTO node_signatures (node_id, signature, hostname, display_name, platform, arch)\n VALUES (?, ?, ?, ?, ?, ?)").run(e,t,a,s,r,d);return n.info(`New node signature registered: ${s} (${e})`),this.getById(Number(o.lastInsertRowid))}approve(e){this.db.prepare("UPDATE node_signatures SET status = 'approved', approved_at = datetime('now') WHERE id = ?").run(e),n.info(`Node signature id=${e} approved`)}revoke(e){this.db.prepare("UPDATE node_signatures SET status = 'revoked' WHERE id = ?").run(e),n.info(`Node signature id=${e} revoked`)}delete(e){this.db.prepare("DELETE FROM node_signatures WHERE id = ?").run(e),n.info(`Node signature id=${e} deleted`)}updateLastSeen(e){this.db.prepare("UPDATE node_signatures SET last_seen_at = datetime('now') WHERE id = ?").run(e)}updateNodeInfo(e,t,n,a,s,r){this.db.prepare("UPDATE node_signatures\n SET node_id = ?, hostname = ?, display_name = ?, platform = ?, arch = ?\n WHERE id = ?").run(t,n,a,s,r,e)}listAll(){return this.db.prepare("SELECT * FROM node_signatures ORDER BY created_at DESC").all().map(a)}listByStatus(e){return this.db.prepare("SELECT * FROM node_signatures WHERE status = ? ORDER BY created_at DESC").all(e).map(a)}close(){this.db.close()}}
@@ -0,0 +1,38 @@
1
+ export interface TokenRecord {
2
+ id: number;
3
+ token: string;
4
+ userId: string;
5
+ channel: string;
6
+ label: string | null;
7
+ permissions: string;
8
+ createdAt: string;
9
+ expiresAt: string | null;
10
+ active: boolean;
11
+ }
12
+ export declare class TokenDB {
13
+ private db;
14
+ constructor(dbPath: string);
15
+ private initSchema;
16
+ createToken(opts: {
17
+ token?: string;
18
+ userId: string;
19
+ channel?: string;
20
+ label?: string;
21
+ permissions?: string;
22
+ expiresAt?: string;
23
+ active?: boolean;
24
+ }): TokenRecord;
25
+ getToken(token: string): TokenRecord | null;
26
+ private getTokenById;
27
+ listTokens(filter?: {
28
+ userId?: string;
29
+ channel?: string;
30
+ }): TokenRecord[];
31
+ revokeToken(id: number): void;
32
+ approveToken(id: number): void;
33
+ deleteToken(id: number): void;
34
+ validateToken(token: string, channel: string): TokenRecord | null;
35
+ validateUserId(userId: string, channel: string): TokenRecord | null;
36
+ close(): void;
37
+ }
38
+ //# sourceMappingURL=token-db.d.ts.map
@@ -0,0 +1 @@
1
+ import e from"better-sqlite3";import n from"node:crypto";import{createLogger as t}from"../utils/logger.js";const r=t("TokenDB");function s(e){return{id:e.id,token:e.token,userId:e.user_id,channel:e.channel,label:e.label,permissions:e.permissions,createdAt:e.created_at,expiresAt:e.expires_at,active:1===e.active}}export class TokenDB{db;constructor(n){this.db=new e(n),this.db.pragma("journal_mode = WAL"),this.initSchema(),r.info(`Token database opened at ${n}`)}initSchema(){this.db.exec("\n CREATE TABLE IF NOT EXISTS tokens (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n token TEXT UNIQUE NOT NULL,\n user_id TEXT NOT NULL,\n channel TEXT NOT NULL DEFAULT '*',\n label TEXT,\n permissions TEXT NOT NULL DEFAULT '{}',\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n expires_at TEXT,\n active INTEGER NOT NULL DEFAULT 1\n );\n CREATE INDEX IF NOT EXISTS idx_tokens_token ON tokens(token);\n CREATE INDEX IF NOT EXISTS idx_tokens_user_channel ON tokens(user_id, channel);\n ")}createToken(e){const t=e.token??"sk-hera-"+n.randomBytes(32).toString("hex"),s=this.db.prepare("\n INSERT INTO tokens (token, user_id, channel, label, permissions, expires_at, active)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ").run(t,e.userId,e.channel??"*",e.label??null,e.permissions??"{}",e.expiresAt??null,!1!==e.active?1:0);return r.info(`Token created for user=${e.userId} channel=${e.channel??"*"} id=${s.lastInsertRowid}`),this.getTokenById(Number(s.lastInsertRowid))}getToken(e){const n=this.db.prepare("SELECT * FROM tokens WHERE token = ?").get(e);return n?s(n):null}getTokenById(e){const n=this.db.prepare("SELECT * FROM tokens WHERE id = ?").get(e);return n?s(n):null}listTokens(e){let n="SELECT * FROM tokens WHERE 1=1";const t=[];e?.userId&&(n+=" AND user_id = ?",t.push(e.userId)),e?.channel&&(n+=" AND (channel = ? OR channel = '*')",t.push(e.channel));return this.db.prepare(n).all(...t).map(s)}revokeToken(e){this.db.prepare("UPDATE tokens SET active = 0 WHERE id = ?").run(e),r.info(`Token id=${e} revoked`)}approveToken(e){this.db.prepare("UPDATE tokens SET active = 1 WHERE id = ?").run(e),r.info(`Token id=${e} approved`)}deleteToken(e){this.db.prepare("DELETE FROM tokens WHERE id = ?").run(e),r.info(`Token id=${e} deleted`)}validateToken(e,n){const t=this.db.prepare("SELECT * FROM tokens WHERE token = ? AND active = 1 AND (channel = ? OR channel = '*')").get(e,n);if(!t)return null;const r=s(t);if(r.expiresAt){if(new Date(r.expiresAt)<new Date)return null}return r}validateUserId(e,n){const t=this.db.prepare("SELECT * FROM tokens WHERE user_id = ? AND active = 1 AND (channel = ? OR channel = '*')").get(e,n);if(!t)return null;const r=s(t);if(r.expiresAt){if(new Date(r.expiresAt)<new Date)return null}return r}close(){this.db.close()}}
@@ -0,0 +1,9 @@
1
+ import { type BrowserConfig } from "@hera-al/browser-server/config";
2
+ export declare class BrowserService {
3
+ private running;
4
+ start(config: BrowserConfig | undefined): Promise<void>;
5
+ stop(): Promise<void>;
6
+ reconfigure(config: BrowserConfig | undefined): Promise<void>;
7
+ isRunning(): boolean;
8
+ }
9
+ //# sourceMappingURL=browser-service.d.ts.map
@@ -0,0 +1 @@
1
+ import{resolveBrowserConfig as r}from"@hera-al/browser-server/config";import{startBrowserServer as e,stopBrowserServer as s,reconfigureBrowserServer as i}from"@hera-al/browser-server";import{createLogger as n}from"../utils/logger.js";const t=n("BrowserService");export class BrowserService{running=!1;async start(s){const i=r(s);if(i.enabled)try{await e(i),this.running=!0,t.info("Browser service started")}catch(r){throw t.error(`Failed to start browser service: ${r instanceof Error?r.message:String(r)}`),r}else t.info("Browser service disabled")}async stop(){this.running&&(await s(),this.running=!1,t.info("Browser service stopped"))}async reconfigure(e){const s=r(e);s.enabled?this.running?await i(s):await this.start(e):this.running&&await this.stop()}isRunning(){return this.running}}
@@ -0,0 +1,2 @@
1
+ export type { ChannelAdapter, IncomingMessage, Attachment, MessageHandler } from "../gateway/bridge.js";
2
+ //# sourceMappingURL=channel.d.ts.map
@@ -0,0 +1 @@
1
+ export{};