clawmini 0.0.3 → 0.0.5

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 (333) hide show
  1. package/README.md +19 -0
  2. package/dist/adapter-discord/index.d.mts.map +1 -1
  3. package/dist/adapter-discord/index.mjs +398 -193
  4. package/dist/adapter-discord/index.mjs.map +1 -1
  5. package/dist/adapter-google-chat/index.d.mts +5 -0
  6. package/dist/adapter-google-chat/index.d.mts.map +1 -0
  7. package/dist/adapter-google-chat/index.mjs +1077 -0
  8. package/dist/adapter-google-chat/index.mjs.map +1 -0
  9. package/dist/cli/index.mjs +107 -14
  10. package/dist/cli/index.mjs.map +1 -1
  11. package/dist/cli/lite.mjs +175 -16
  12. package/dist/cli/lite.mjs.map +1 -1
  13. package/dist/cli/propose-policy.d.mts +1 -0
  14. package/dist/cli/propose-policy.mjs +7159 -0
  15. package/dist/cli/propose-policy.mjs.map +1 -0
  16. package/dist/daemon/index.d.mts.map +1 -1
  17. package/dist/daemon/index.mjs +1427 -513
  18. package/dist/daemon/index.mjs.map +1 -1
  19. package/dist/{lite-oSYSvaOr.mjs → lite-CBxOT1y5.mjs} +101 -24
  20. package/dist/lite-CBxOT1y5.mjs.map +1 -0
  21. package/dist/routing-D8rTxtaV.mjs +245 -0
  22. package/dist/routing-D8rTxtaV.mjs.map +1 -0
  23. package/dist/web/_app/immutable/assets/0.C-4eziNy.css +1 -0
  24. package/dist/web/_app/immutable/assets/4.Cc_xwLNl.css +1 -0
  25. package/dist/web/_app/immutable/chunks/B6YN0Nuq.js +1 -0
  26. package/dist/web/_app/immutable/chunks/{Dc-UOHw9.js → BmRlVmv6.js} +1 -1
  27. package/{web/.svelte-kit/output/client/_app/immutable/chunks/8YNcRyEk.js → dist/web/_app/immutable/chunks/C20lZMGz.js} +1 -1
  28. package/dist/web/_app/immutable/chunks/C9lbZ-kT.js +1 -0
  29. package/dist/web/_app/immutable/chunks/CK9JZLaG.js +2 -0
  30. package/dist/web/_app/immutable/chunks/CME08kGM.js +1 -0
  31. package/dist/web/_app/immutable/chunks/{BPy8HLo7.js → Ck-be5J2.js} +1 -1
  32. package/dist/web/_app/immutable/chunks/Ck3rYNON.js +1 -0
  33. package/dist/web/_app/immutable/chunks/DMtIqaiV.js +2 -0
  34. package/dist/web/_app/immutable/chunks/{B8yYFADm.js → DhD271EB.js} +1 -1
  35. package/dist/web/_app/immutable/chunks/{DcrmIfTj.js → DpuLqk8d.js} +1 -1
  36. package/dist/web/_app/immutable/chunks/{ZkLyk0mE.js → Drm9vgeP.js} +1 -1
  37. package/dist/web/_app/immutable/chunks/DsIToJCP.js +1 -0
  38. package/dist/web/_app/immutable/chunks/{CyNaE55B.js → Zeh-C-mx.js} +1 -1
  39. package/{web/.svelte-kit/output/client/_app/immutable/entry/app.DO5eYwVz.js → dist/web/_app/immutable/entry/app.BgB5VkRU.js} +2 -2
  40. package/dist/web/_app/immutable/entry/start.DuxJo6av.js +1 -0
  41. package/dist/web/_app/immutable/nodes/0.C9oFZP9h.js +1 -0
  42. package/dist/web/_app/immutable/nodes/1.BON2Wk6k.js +1 -0
  43. package/dist/web/_app/immutable/nodes/{2.CK3CLC0f.js → 2.BnwnD1Ki.js} +1 -1
  44. package/dist/web/_app/immutable/nodes/{3.ncP0xLO6.js → 3.CIs4tjjw.js} +1 -1
  45. package/dist/web/_app/immutable/nodes/4.DLarELN4.js +60 -0
  46. package/dist/web/_app/immutable/nodes/{5.BpJUN6QH.js → 5.CE_QKy_3.js} +1 -1
  47. package/dist/web/_app/version.json +1 -1
  48. package/dist/web/index.html +12 -12
  49. package/dist/{workspace-DjoNjhW0.mjs → workspace-BJmJBfKi.mjs} +103 -11
  50. package/dist/workspace-BJmJBfKi.mjs.map +1 -0
  51. package/docs/14_google_chat_adapter/development_log.md +40 -0
  52. package/docs/14_google_chat_adapter/notes.md +28 -0
  53. package/docs/14_google_chat_adapter/prd.md +35 -0
  54. package/docs/14_google_chat_adapter/questions.md +9 -0
  55. package/docs/14_google_chat_adapter/tickets.md +117 -0
  56. package/docs/15_sandbox_policies/tickets.md +33 -0
  57. package/docs/16_session_timeout/development_log.md +20 -0
  58. package/docs/16_session_timeout/notes.md +44 -0
  59. package/docs/16_session_timeout/prd.md +106 -0
  60. package/docs/16_session_timeout/questions.md +10 -0
  61. package/docs/16_session_timeout/tickets.md +64 -0
  62. package/docs/17_auto_approve_policy/development_log.md +29 -0
  63. package/docs/17_auto_approve_policy/notes.md +25 -0
  64. package/docs/17_auto_approve_policy/prd.md +34 -0
  65. package/docs/17_auto_approve_policy/questions.md +10 -0
  66. package/docs/17_auto_approve_policy/tickets.md +11 -0
  67. package/docs/18_clawmini_skills/development_log.md +36 -0
  68. package/docs/18_clawmini_skills/notes.md +8 -0
  69. package/docs/18_clawmini_skills/prd.md +45 -0
  70. package/docs/18_clawmini_skills/questions.md +10 -0
  71. package/docs/18_clawmini_skills/tickets.md +55 -0
  72. package/docs/19_subagents/development_log.md +69 -0
  73. package/docs/19_subagents/notes.md +18 -0
  74. package/docs/19_subagents/prd.md +156 -0
  75. package/docs/19_subagents/questions.md +13 -0
  76. package/docs/19_subagents/tickets.md +113 -0
  77. package/docs/20_chat_logs_cleanup/development_log.md +50 -0
  78. package/docs/20_chat_logs_cleanup/notes.md +43 -0
  79. package/docs/20_chat_logs_cleanup/prd.md +232 -0
  80. package/docs/20_chat_logs_cleanup/questions.md +2 -0
  81. package/docs/20_chat_logs_cleanup/tickets.md +98 -0
  82. package/docs/20_webui_markdown/development_log.md +36 -0
  83. package/docs/20_webui_markdown/notes.md +23 -0
  84. package/docs/20_webui_markdown/prd.md +49 -0
  85. package/docs/20_webui_markdown/questions.md +10 -0
  86. package/docs/20_webui_markdown/tickets.md +55 -0
  87. package/docs/21_adapter_filtering/development_log.md +29 -0
  88. package/docs/21_adapter_filtering/notes.md +25 -0
  89. package/docs/21_adapter_filtering/prd.md +44 -0
  90. package/docs/21_adapter_filtering/questions.md +12 -0
  91. package/docs/21_adapter_filtering/tickets.md +38 -0
  92. package/docs/21_built_in_routers/development_log.md +17 -0
  93. package/docs/21_built_in_routers/notes.md +27 -0
  94. package/docs/21_built_in_routers/prd.md +34 -0
  95. package/docs/21_built_in_routers/questions.md +4 -0
  96. package/docs/21_built_in_routers/tickets.md +25 -0
  97. package/docs/21_fancy_policies/development_log.md +38 -0
  98. package/docs/21_fancy_policies/notes.md +27 -0
  99. package/docs/21_fancy_policies/prd.md +58 -0
  100. package/docs/21_fancy_policies/questions.md +6 -0
  101. package/docs/21_fancy_policies/tickets.md +48 -0
  102. package/docs/22_adapter_multi_chat/development_log.md +76 -0
  103. package/docs/22_adapter_multi_chat/notes.md +42 -0
  104. package/docs/22_adapter_multi_chat/prd.md +76 -0
  105. package/docs/22_adapter_multi_chat/questions.md +16 -0
  106. package/docs/22_adapter_multi_chat/tickets.md +164 -0
  107. package/docs/23_custom_token_env/development_log.md +31 -0
  108. package/docs/23_custom_token_env/notes.md +16 -0
  109. package/docs/23_custom_token_env/prd.md +42 -0
  110. package/docs/23_custom_token_env/questions.md +8 -0
  111. package/docs/23_custom_token_env/tickets.md +54 -0
  112. package/docs/guides/discord_adapter_setup.md +15 -2
  113. package/docs/guides/google_chat_adapter_setup.md +145 -0
  114. package/napkin.md +5 -0
  115. package/package.json +7 -2
  116. package/src/adapter-discord/config.test.ts +27 -8
  117. package/src/adapter-discord/config.ts +6 -8
  118. package/src/adapter-discord/forwarder.test.ts +307 -114
  119. package/src/adapter-discord/forwarder.ts +260 -75
  120. package/src/adapter-discord/index.test.ts +278 -0
  121. package/src/adapter-discord/index.ts +160 -30
  122. package/src/adapter-discord/interactions.test.ts +96 -0
  123. package/src/adapter-discord/interactions.ts +156 -0
  124. package/src/adapter-discord/state.test.ts +9 -8
  125. package/src/adapter-discord/state.ts +51 -8
  126. package/src/adapter-google-chat/auth.test.ts +87 -0
  127. package/src/adapter-google-chat/auth.ts +132 -0
  128. package/src/adapter-google-chat/cards.ts +71 -0
  129. package/src/adapter-google-chat/client.test.ts +561 -0
  130. package/src/adapter-google-chat/client.ts +430 -0
  131. package/src/adapter-google-chat/config.test.ts +187 -0
  132. package/src/adapter-google-chat/config.ts +82 -0
  133. package/src/adapter-google-chat/cron.test.ts +143 -0
  134. package/src/adapter-google-chat/cron.ts +81 -0
  135. package/src/adapter-google-chat/forwarder.test.ts +537 -0
  136. package/src/adapter-google-chat/forwarder.ts +349 -0
  137. package/src/adapter-google-chat/index.test.ts +62 -0
  138. package/src/adapter-google-chat/index.ts +61 -0
  139. package/src/adapter-google-chat/state.test.ts +96 -0
  140. package/src/adapter-google-chat/state.ts +85 -0
  141. package/src/adapter-google-chat/subscriptions.ts +124 -0
  142. package/src/adapter-google-chat/upload.ts +88 -0
  143. package/src/adapter-google-chat/utils.test.ts +111 -0
  144. package/src/adapter-google-chat/utils.ts +133 -0
  145. package/src/cli/commands/init.ts +0 -7
  146. package/src/cli/commands/messages.ts +18 -3
  147. package/src/cli/commands/policies.ts +70 -0
  148. package/src/cli/commands/skills.ts +71 -0
  149. package/src/cli/commands/web-api/chats.ts +5 -1
  150. package/src/cli/e2e/basic.test.ts +1 -1
  151. package/src/cli/e2e/cron.test.ts +1 -1
  152. package/src/cli/e2e/daemon.test.ts +132 -4
  153. package/src/cli/e2e/export-lite-func.test.ts +54 -31
  154. package/src/cli/e2e/fallbacks.test.ts +8 -6
  155. package/src/cli/e2e/init.test.ts +7 -0
  156. package/src/cli/e2e/messages.test.ts +90 -55
  157. package/src/cli/e2e/propose-policy.test.ts +203 -0
  158. package/src/cli/e2e/requests.test.ts +15 -0
  159. package/src/cli/e2e/session-timeout.test.ts +192 -0
  160. package/src/cli/e2e/skills.test.ts +55 -0
  161. package/src/cli/e2e/slash-new.test.ts +93 -0
  162. package/src/cli/e2e/subagents.test.ts +106 -0
  163. package/src/cli/index.ts +4 -0
  164. package/src/cli/lite.ts +51 -11
  165. package/src/cli/propose-policy.ts +91 -0
  166. package/src/cli/subagent-commands.ts +215 -0
  167. package/src/daemon/agent/agent-context.ts +89 -0
  168. package/src/daemon/agent/agent-extractors.ts +68 -0
  169. package/src/daemon/agent/agent-runner.ts +153 -0
  170. package/src/daemon/agent/agent-session.ts +261 -0
  171. package/src/daemon/agent/chat-logger.test.ts +158 -0
  172. package/src/daemon/agent/chat-logger.ts +188 -0
  173. package/src/daemon/agent/task-scheduler.test.ts +202 -0
  174. package/src/daemon/agent/task-scheduler.ts +276 -0
  175. package/src/daemon/agent/types.ts +84 -0
  176. package/src/daemon/agent/utils.ts +7 -0
  177. package/src/daemon/api/agent-router.ts +166 -18
  178. package/src/daemon/api/index.test.ts +50 -18
  179. package/src/daemon/api/policy-request.test.ts +39 -2
  180. package/src/daemon/api/subagent-router.test.ts +108 -0
  181. package/src/daemon/api/subagent-router.ts +296 -0
  182. package/src/daemon/api/subagent-utils.test.ts +56 -0
  183. package/src/daemon/api/subagent-utils.ts +130 -0
  184. package/src/daemon/api/user-router.ts +30 -13
  185. package/src/daemon/auth.ts +1 -0
  186. package/src/daemon/chats.ts +6 -0
  187. package/src/daemon/cron.test.ts +66 -1
  188. package/src/daemon/cron.ts +35 -8
  189. package/src/daemon/index.ts +23 -0
  190. package/src/daemon/message-agent.test.ts +11 -25
  191. package/src/daemon/message-extraction.test.ts +10 -27
  192. package/src/daemon/message-fallbacks.test.ts +13 -35
  193. package/src/daemon/message-interruption.test.ts +70 -53
  194. package/src/daemon/message-jobs.test.ts +138 -0
  195. package/src/daemon/message-queue.test.ts +30 -43
  196. package/src/daemon/message-router.test.ts +12 -11
  197. package/src/daemon/message-session.test.ts +41 -28
  198. package/src/daemon/message-typing.test.ts +19 -6
  199. package/src/daemon/message.ts +103 -515
  200. package/src/daemon/policy-request-service.ts +8 -3
  201. package/src/daemon/policy-utils.ts +19 -1
  202. package/src/daemon/queue.ts +16 -0
  203. package/src/daemon/request-store.test.ts +4 -0
  204. package/src/daemon/routers/session-timeout.test.ts +122 -0
  205. package/src/daemon/routers/session-timeout.ts +71 -0
  206. package/src/daemon/routers/slash-new.ts +3 -1
  207. package/src/daemon/routers/slash-policies.test.ts +26 -13
  208. package/src/daemon/routers/slash-policies.ts +39 -29
  209. package/src/daemon/routers/types.ts +8 -0
  210. package/src/daemon/routers.ts +64 -2
  211. package/src/daemon/utils/spawn.ts +6 -8
  212. package/src/shared/adapters/commands.test.ts +155 -0
  213. package/src/shared/adapters/commands.ts +125 -0
  214. package/src/shared/adapters/filtering.test.ts +111 -0
  215. package/src/shared/adapters/filtering.ts +57 -0
  216. package/src/shared/adapters/routing.test.ts +144 -0
  217. package/src/shared/adapters/routing.ts +109 -0
  218. package/src/shared/agent-utils.ts +10 -0
  219. package/src/shared/chats.test.ts +145 -3
  220. package/src/shared/chats.ts +215 -18
  221. package/src/shared/config.ts +67 -15
  222. package/src/shared/lite.ts +22 -18
  223. package/src/shared/policies.ts +7 -0
  224. package/src/shared/workspace.test.ts +45 -1
  225. package/src/shared/workspace.ts +119 -6
  226. package/templates/debug/settings.json +5 -2
  227. package/templates/environments/cladding/env.json +2 -2
  228. package/templates/gemini/.gemini/hooks/check-subagents.mjs +23 -0
  229. package/templates/gemini/.gemini/hooks/clawmini-logging.sh +17 -0
  230. package/templates/gemini/.gemini/hooks/insert-pending.sh +9 -0
  231. package/templates/gemini/.gemini/settings.json +50 -0
  232. package/templates/gemini/settings.json +22 -8
  233. package/templates/gemini-claw/.gemini/base-system.md +100 -0
  234. package/templates/gemini-claw/.gemini/hooks/check-subagents.mjs +23 -0
  235. package/templates/gemini-claw/.gemini/hooks/clawmini-logging.sh +1 -1
  236. package/templates/gemini-claw/.gemini/settings.json +13 -0
  237. package/templates/gemini-claw/.gemini/subagent-system.md +7 -0
  238. package/templates/gemini-claw/.gemini/system.md +3 -99
  239. package/templates/gemini-claw/settings.json +27 -22
  240. package/templates/skills/clawmini-requests/SKILL.md +92 -0
  241. package/templates/skills/clawmini-subagents/SKILL.md +79 -0
  242. package/templates/skills/skill-creator/SKILL.md +60 -0
  243. package/tsdown.config.ts +10 -1
  244. package/web/.svelte-kit/generated/server/internal.js +2 -1
  245. package/web/.svelte-kit/non-ambient.d.ts +2 -0
  246. package/web/.svelte-kit/output/client/.vite/manifest.json +141 -138
  247. package/web/.svelte-kit/output/client/_app/immutable/assets/0.C-4eziNy.css +1 -0
  248. package/web/.svelte-kit/output/client/_app/immutable/assets/4.Cc_xwLNl.css +1 -0
  249. package/web/.svelte-kit/output/client/_app/immutable/chunks/B6YN0Nuq.js +1 -0
  250. package/web/.svelte-kit/output/client/_app/immutable/chunks/{Dc-UOHw9.js → BmRlVmv6.js} +1 -1
  251. package/{dist/web/_app/immutable/chunks/8YNcRyEk.js → web/.svelte-kit/output/client/_app/immutable/chunks/C20lZMGz.js} +1 -1
  252. package/web/.svelte-kit/output/client/_app/immutable/chunks/C9lbZ-kT.js +1 -0
  253. package/web/.svelte-kit/output/client/_app/immutable/chunks/CK9JZLaG.js +2 -0
  254. package/web/.svelte-kit/output/client/_app/immutable/chunks/CME08kGM.js +1 -0
  255. package/web/.svelte-kit/output/client/_app/immutable/chunks/{BPy8HLo7.js → Ck-be5J2.js} +1 -1
  256. package/web/.svelte-kit/output/client/_app/immutable/chunks/Ck3rYNON.js +1 -0
  257. package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +2 -0
  258. package/web/.svelte-kit/output/client/_app/immutable/chunks/{B8yYFADm.js → DhD271EB.js} +1 -1
  259. package/web/.svelte-kit/output/client/_app/immutable/chunks/{DcrmIfTj.js → DpuLqk8d.js} +1 -1
  260. package/web/.svelte-kit/output/client/_app/immutable/chunks/{ZkLyk0mE.js → Drm9vgeP.js} +1 -1
  261. package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +1 -0
  262. package/web/.svelte-kit/output/client/_app/immutable/chunks/{CyNaE55B.js → Zeh-C-mx.js} +1 -1
  263. package/{dist/web/_app/immutable/entry/app.DO5eYwVz.js → web/.svelte-kit/output/client/_app/immutable/entry/app.BgB5VkRU.js} +2 -2
  264. package/web/.svelte-kit/output/client/_app/immutable/entry/start.DuxJo6av.js +1 -0
  265. package/web/.svelte-kit/output/client/_app/immutable/nodes/0.C9oFZP9h.js +1 -0
  266. package/web/.svelte-kit/output/client/_app/immutable/nodes/1.BON2Wk6k.js +1 -0
  267. package/web/.svelte-kit/output/client/_app/immutable/nodes/{2.CK3CLC0f.js → 2.BnwnD1Ki.js} +1 -1
  268. package/web/.svelte-kit/output/client/_app/immutable/nodes/{3.ncP0xLO6.js → 3.CIs4tjjw.js} +1 -1
  269. package/web/.svelte-kit/output/client/_app/immutable/nodes/4.DLarELN4.js +60 -0
  270. package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.BpJUN6QH.js → 5.CE_QKy_3.js} +1 -1
  271. package/web/.svelte-kit/output/client/_app/version.json +1 -1
  272. package/web/.svelte-kit/output/server/.vite/manifest.json +12 -3
  273. package/web/.svelte-kit/output/server/_app/immutable/assets/_layout.C-4eziNy.css +1 -0
  274. package/web/.svelte-kit/output/server/_app/immutable/assets/_page.Cc_xwLNl.css +1 -0
  275. package/web/.svelte-kit/output/server/chunks/app-state.svelte.js +5 -0
  276. package/web/.svelte-kit/output/server/chunks/bot.js +4 -4
  277. package/web/.svelte-kit/output/server/chunks/client.js +2 -1
  278. package/web/.svelte-kit/output/server/chunks/exports.js +0 -1
  279. package/web/.svelte-kit/output/server/chunks/internal.js +2 -1
  280. package/web/.svelte-kit/output/server/chunks/root.js +482 -392
  281. package/web/.svelte-kit/output/server/entries/pages/_layout.svelte.js +57 -7
  282. package/web/.svelte-kit/output/server/entries/pages/chats/_id_/_page.svelte.js +234 -9
  283. package/web/.svelte-kit/output/server/index.js +82 -10
  284. package/web/.svelte-kit/output/server/manifest-full.js +1 -1
  285. package/web/.svelte-kit/output/server/manifest.js +1 -1
  286. package/web/.svelte-kit/output/server/nodes/0.js +2 -2
  287. package/web/.svelte-kit/output/server/nodes/1.js +1 -1
  288. package/web/.svelte-kit/output/server/nodes/2.js +1 -1
  289. package/web/.svelte-kit/output/server/nodes/3.js +1 -1
  290. package/web/.svelte-kit/output/server/nodes/4.js +2 -2
  291. package/web/.svelte-kit/output/server/nodes/5.js +1 -1
  292. package/web/.svelte-kit/types/src/routes/$types.d.ts +1 -2
  293. package/web/.svelte-kit/types/src/routes/agents/$types.d.ts +1 -2
  294. package/web/.svelte-kit/types/src/routes/chats/[id]/$types.d.ts +1 -2
  295. package/web/.svelte-kit/types/src/routes/chats/[id]/settings/$types.d.ts +1 -2
  296. package/web/package.json +8 -0
  297. package/web/src/lib/app-state.svelte.ts +5 -1
  298. package/web/src/lib/components/app/markdown-renderer.svelte +56 -0
  299. package/web/src/lib/components/app/markdown-renderer.svelte.spec.ts +44 -0
  300. package/web/src/lib/components/app/message-content.svelte +16 -0
  301. package/web/src/lib/types.ts +67 -3
  302. package/web/src/routes/+layout.svelte +31 -1
  303. package/web/src/routes/chats/[id]/+page.svelte +167 -18
  304. package/web/src/routes/chats/[id]/page.svelte.spec.ts +58 -7
  305. package/dist/lite-oSYSvaOr.mjs.map +0 -1
  306. package/dist/web/_app/immutable/assets/0.GI4C4dpV.css +0 -1
  307. package/dist/web/_app/immutable/chunks/B5abRDXp.js +0 -1
  308. package/dist/web/_app/immutable/chunks/Bi0jeV7Q.js +0 -1
  309. package/dist/web/_app/immutable/chunks/BmUXQ3wy.js +0 -2
  310. package/dist/web/_app/immutable/chunks/C3k55nDF.js +0 -1
  311. package/dist/web/_app/immutable/chunks/CpaGRn9L.js +0 -1
  312. package/dist/web/_app/immutable/chunks/DG5RZBw-.js +0 -2
  313. package/dist/web/_app/immutable/chunks/DQoygso7.js +0 -1
  314. package/dist/web/_app/immutable/entry/start.D48mVn1m.js +0 -1
  315. package/dist/web/_app/immutable/nodes/0.B-0CcADM.js +0 -1
  316. package/dist/web/_app/immutable/nodes/1.FixKgvRO.js +0 -1
  317. package/dist/web/_app/immutable/nodes/4.CQYJEgv8.js +0 -1
  318. package/dist/workspace-DjoNjhW0.mjs.map +0 -1
  319. package/src/daemon/message-verbosity.test.ts +0 -127
  320. package/web/.svelte-kit/output/client/_app/immutable/assets/0.GI4C4dpV.css +0 -1
  321. package/web/.svelte-kit/output/client/_app/immutable/chunks/B5abRDXp.js +0 -1
  322. package/web/.svelte-kit/output/client/_app/immutable/chunks/Bi0jeV7Q.js +0 -1
  323. package/web/.svelte-kit/output/client/_app/immutable/chunks/BmUXQ3wy.js +0 -2
  324. package/web/.svelte-kit/output/client/_app/immutable/chunks/C3k55nDF.js +0 -1
  325. package/web/.svelte-kit/output/client/_app/immutable/chunks/CpaGRn9L.js +0 -1
  326. package/web/.svelte-kit/output/client/_app/immutable/chunks/DG5RZBw-.js +0 -2
  327. package/web/.svelte-kit/output/client/_app/immutable/chunks/DQoygso7.js +0 -1
  328. package/web/.svelte-kit/output/client/_app/immutable/entry/start.D48mVn1m.js +0 -1
  329. package/web/.svelte-kit/output/client/_app/immutable/nodes/0.B-0CcADM.js +0 -1
  330. package/web/.svelte-kit/output/client/_app/immutable/nodes/1.FixKgvRO.js +0 -1
  331. package/web/.svelte-kit/output/client/_app/immutable/nodes/4.CQYJEgv8.js +0 -1
  332. package/web/.svelte-kit/output/server/_app/immutable/assets/_layout.GI4C4dpV.css +0 -1
  333. /package/templates/{gemini-claw/.gemini/skills → skills}/clawmini-jobs/SKILL.md +0 -0
@@ -0,0 +1,76 @@
1
+ # Development Log
2
+
3
+ ## Session 1
4
+ - Initialised Ticket 1: Update State and Config Schemas for Multi-Chat Support.
5
+ - Verified schema changes for Discord and Google Chat adapters in git diff.
6
+ - Identified test failures in `npm run validate`.
7
+ - Fixed `channelChatMap` to be an object instead of a string array in test and code logic for `src/adapter-discord/client.test.ts`, `src/adapter-discord/client.ts`, `src/adapter-discord/state.test.ts`, `src/adapter-discord/forwarder.ts`.
8
+ - Updated schema typing to allow undefined explicitly where required, maintaining strict structural compatibility.
9
+ - Re-run `npm run validate`, successfully passing all checks.
10
+ - Completed Ticket 1 in `tickets.md` and committed all changes.
11
+
12
+ ## Session 2
13
+ - Started work on Ticket 2: Implement "First Contact" Protocol (Discord).
14
+ - Refactored `startDiscordIngestion` in `src/adapter-discord/client.ts` to implement the required First Contact logic:
15
+ - Check if `targetChatId` is mapped for a given channel.
16
+ - If not, verify if it's the very first message processed by the adapter by checking if `channelChatMap` is empty.
17
+ - Automatically map the first channel to `config.chatId` (default) and process the message.
18
+ - If it's not the first message, reply with a strict, non-processed warning indicating the channel is unmapped and block the message from daemon propagation.
19
+ - Verified and fixed Discord tests to ensure correct behaviour:
20
+ - Ensured routing commands correctly populate `channelChatMap`.
21
+ - Added new mock properties `mockMessage.reply` and modified expected return shapes so subsequent tests relying on default properties (like `channelChatMap`) don't fail.
22
+ - When mocking discord.js Messages, ensure they match the properties required by the code paths (e.g. `channelId`, `attachments`, `reply`).
23
+ - Confirmed all checks (`npm run validate`) pass properly.
24
+
25
+ ## Session 3
26
+ - Started work on Ticket 3: Implement Dynamic Subscriptions (Discord Forwarder).
27
+ - Updated `startDaemonToDiscordForwarder` in `src/adapter-discord/forwarder.ts` to:
28
+ - Establish `trpc.waitForMessages.subscribe` for each chat mapped in `state.channelChatMap`.
29
+ - Track `lastSyncedMessageIds` for each daemon chat independently rather than globally.
30
+ - Set up an `fs.watch` event listener on `state.json` to dynamically synchronize subscriptions on changes.
31
+ - Correctly extract the target `channelId` to post messages by doing a reverse lookup on `state.channelChatMap`.
32
+ - Resolved a Vitest unhandled rejection hook failure where a thrown Error during `syncSubscriptions` wasn't properly caught within an async boundary.
33
+ - Fixed a bug causing an infinite loop in Vitest caused by `setInterval` inside `vi.runAllTimersAsync()` by refactoring tests to use `vi.advanceTimersByTimeAsync(30000)` instead.
34
+ - Confirmed all checks (`npm run validate`) pass properly.
35
+
36
+ ## Session 4
37
+ - Started work on Ticket 5: Add Google Chat Space Subscription Config & State Schemas.
38
+ - Updated `GoogleChatStateSchema` in `src/adapter-google-chat/state.ts` to use an object for `channelChatMap` handling space subscriptions (`chatId`, `subscriptionId`, `expirationDate`).
39
+ - Added schema migration paths in `readGoogleChatState` to support single-chat legacy formats and migrated `driveOauthTokens` to `oauthTokens`.
40
+ - Replaced `driveOauthClientId` and `driveOauthClientSecret` with generic `oauthClientId` and `oauthClientSecret` in `src/adapter-google-chat/config.ts`.
41
+ - Updated `src/adapter-google-chat/auth.ts` to use `getUserAuthClient()` matching the required new OAuth flows and multiple scopes for Drive and Chat readonly.
42
+ - Updated `src/adapter-google-chat/index.ts` to initialize `getUserAuthClient()` when OAuth secrets are present.
43
+ - Updated numerous test files to accommodate the structural schema changes and OAuth renaming.
44
+ - Successfully verified tests passing using `npm run validate`.
45
+ - Marked Ticket 5 as Completed.
46
+
47
+ ## Session 5
48
+ - Implemented Ticket 6: Space Subscription Lifecycle (`ADDED_TO_SPACE` & `REMOVED_FROM_SPACE`).
49
+ - Extracted `handleAddedToSpace` and `handleRemovedFromSpace` into `src/adapter-google-chat/subscriptions.ts` to keep `client.ts` clean and below the line limits.
50
+ - Extracted `handleCardClicked` into `src/adapter-google-chat/cards.ts` to reduce `client.ts` file length.
51
+ - Added API calls to `https://workspaceevents.googleapis.com/v1/subscriptions` using the Google User's OAuth tokens to dynamically subscribe to `google.workspace.chat.message.v1.created` when the bot is added to spaces.
52
+ - Intercepted `ADDED_TO_SPACE` and `REMOVED_FROM_SPACE` early in the Google Chat ingestion pipeline to bypass the first contact unmapped warnings.
53
+ - Updated `client.test.ts` to simulate and test subscriptions logic accurately using mock fetch interactions.
54
+ - Resolved various TypeScript alignment errors around strict typing of `mappedChatId` and nested objects inside the state JSON configuration.
55
+
56
+ ## Session 6
57
+ - Implemented Ticket 7: Dual-Pipeline Pub/Sub Worker Logic.
58
+ - Updated `startGoogleChatIngestion` in `src/adapter-google-chat/client.ts` to process native Bot Events and Workspace Events.
59
+ - Implemented a 60-second LRU cache (using Map) to deduplicate Message IDs and drop duplicate messages when the bot is mentioned and triggering both pipelines.
60
+ - Added explicit sender type check to drop messages from `BOT` to prevent infinite reply loops.
61
+ - Added tests simulating Workspace Event payloads and Bot Event payloads.
62
+ - Verified test coverage for duplicate IDs and BOT message dropping.
63
+ - Fixed linter warnings about missing `const` and explicit `any` types.
64
+ - Fixed maximum line constraints by simplifying logical conditionals.
65
+ - Ran `npm run validate` which passed format, lint, check, and unit/E2E tests.
66
+
67
+ ## Session 7
68
+ - Implemented Ticket 8: Background Renewal Cron for Subscriptions.
69
+ - Created `src/adapter-google-chat/cron.ts` to run a `setInterval` cron task every hour.
70
+ - Logic checks `channelChatMap` in `state.json` and patches subscriptions expiring in less than 48 hours to extend TTL to 7 days.
71
+ - Extracted and reused `getUserAuthClient` logic to grab access tokens.
72
+ - Created unit tests in `src/adapter-google-chat/cron.test.ts` to verify the cron executes and triggers fetches accurately based on mocked expiration dates.
73
+ - Updated `src/adapter-google-chat/index.ts` to import and start the cron on adapter initialization.
74
+ - Fixed TS compilation and ESLint strict type-checking issues with `unknown` casting in mock types.
75
+ - Ran `npm run format` and `npm run validate` which passed all format, lint, check, and tests.
76
+ - Marked Ticket 8 as Completed.
@@ -0,0 +1,42 @@
1
+ # Notes on Adapter Multi-Chat Support
2
+
3
+ ## Current State
4
+
5
+ ### Discord Adapter
6
+ - The bot initializes a single `daemon-to-discord` forwarder, listening to a single `chatId` (configured in `config.chatId` or falling back to `'default'`).
7
+ - In `src/adapter-discord/index.ts`, the incoming message handler explicitly ignores messages that occur in a guild (`if (message.guild) return;`). It only processes DMs.
8
+ - It validates the sender against `config.authorizedUserId`.
9
+ - When forwarding to the daemon, it always uses `config.chatId`.
10
+
11
+ ### Google Chat Adapter
12
+ - The bot initializes a single forwarder, listening to `config.chatId` or `'default'`.
13
+ - It maintains an `activeSpaceName` in state which determines where the forwarder sends replies. This means it essentially only supports a 1:1 mapping between one daemon chat and one active Google Chat space at a time.
14
+ - Incoming messages from Pub/Sub (ingestion) are sent to the single `chatId`.
15
+
16
+ ## Requirements for Multi-Chat Support
17
+
18
+ 1. **Mapping Configuration**:
19
+ - We need a way to map an external context (e.g., Discord Channel ID, Discord Thread ID, Google Space ID) to a specific Daemon Chat ID.
20
+ - This mapping could be stored in the adapter's state (`state.json`) so it persists across restarts.
21
+ 2. **Dynamic Subscriptions**:
22
+ - The forwarder functions (`startDaemonToDiscordForwarder` and `startDaemonToGoogleChatForwarder`) currently subscribe to a single `chatId` via `trpc.waitForMessages.subscribe`.
23
+ - To support multiple chats, the adapter must maintain a dynamic list of subscriptions, adding new ones as new mappings are created.
24
+ 3. **Handling Incoming Messages**:
25
+ - **Discord**: Remove the `if (message.guild) return;` block. Allow messages in channels/threads, but *only* if the user is authorized and perhaps if the bot is explicitly mentioned or configured to listen to that channel.
26
+ - When an incoming message arrives, determine its external context ID (e.g. `message.channelId` in Discord).
27
+ - Look up the mapped Daemon Chat ID. If none exists, use a default behavior (e.g., use the user's default chat, or create a new chat for that context).
28
+ 4. **User Commands for Routing**:
29
+ - The user needs a way to say "route this channel to chat X" or "route this channel to agent Y".
30
+ - This could be implemented via adapter-level chat commands (e.g., `!chat <id>`, `!agent <id>`).
31
+ - The adapter would need to validate these IDs against the daemon's TRPC API (`trpc.getChats`, `trpc.getAgents`).
32
+
33
+ ## Space Subscriptions (Google Chat) Research
34
+ - `src/adapter-google-chat/state.ts`: The `GoogleChatStateSchema` needs its `channelChatMap` updated to store subscription info alongside the chat ID. It should be: `channelChatMap: z.record(z.string(), z.object({ chatId: z.string().nullable().optional(), subscriptionId: z.string().optional(), expirationDate: z.string().optional() })).optional()`. We need a migration to convert existing `Record<string, string>` formats into this object format.
35
+ - `src/adapter-google-chat/auth.ts`: Currently handles Bot auth (Service Account) and Drive Auth (OAuth2 with offline access). We need to adapt the OAuth flow to request Google Chat scopes (likely `https://www.googleapis.com/auth/chat.messages.readonly` or similar depending on the exact event subscription) and capture the refresh token for the user.
36
+ - `src/adapter-google-chat/client.ts`: The `startGoogleChatIngestion` function currently handles `MESSAGE` and `CARD_CLICKED` events. We must update it to:
37
+ 1. Handle `ADDED_TO_SPACE` natively (if `space.type !== "DIRECT_MESSAGE"`) to create space subscriptions using the globally saved user OAuth tokens and save the result to state.
38
+ 2. Handle `REMOVED_FROM_SPACE` to cleanly delete the subscription and state entry.
39
+ 3. Distinguish between Workspace Events (via the `ce-type` Pub/Sub attribute) and native Bot Events.
40
+ 4. Deduplicate messages based on Message ID using a 60-second in-memory cache to prevent processing the same message twice when the bot is `@mentioned` in a subscribed space.
41
+ 5. Drop messages where `sender === 'BOT'` to prevent infinite loops.
42
+ - Background Renewal: We need to implement a mechanism (e.g., an hourly `setInterval` inside the adapter process or a separate cron) to check `expirationDate` of space subscriptions and renew those expiring in < 48 hours via `PATCH /v1/subscriptions/{id}`.
@@ -0,0 +1,76 @@
1
+ # Product Requirements Document: Adapter Multi-Chat Support
2
+
3
+ ## Vision
4
+ To provide a seamless, multi-threaded conversational experience across chat platforms (Discord and Google Chat) by allowing users to map distinct channels or spaces to different internal daemon chats or agents. This ensures users can context-switch effectively without mixing separate tasks or agent workflows in a single monolithic adapter stream.
5
+
6
+ ## Product/Market Background
7
+ Currently, the Clawmini bot integrates with external messaging platforms (Discord, Google Chat) using a simple 1:1 mapping strategy. The adapter connects to a single default daemon chat, meaning every message sent to the bot on that platform—regardless of the specific channel or thread—gets funneled into the same daemon conversation. As power users leverage specialized agents or distinct long-running chats, they need the ability to maintain concurrent, isolated workflows by directing different Discord channels or Google Chat spaces to different backend chats.
8
+
9
+ ## Use Cases
10
+ 1. **Context Separation:** A user is in a Discord server and creates two channels: `#coding-help` and `#general-research`. They want `#coding-help` routed to a dedicated coding chat/agent, and `#general-research` routed to their default chat.
11
+ 2. **On-the-Fly Agent Spawning:** A user in Google Chat wants to consult an expert agent for a specific topic. They create a new Space, invite the bot, and use `/agent expert-coder` to immediately spin up a new chat session bound to that Space.
12
+ 3. **Safe Defaults:** A user accidentally starts talking to the bot in a new channel without configuring it. The bot intercepts the message, warns them that the channel isn't configured, and provides instructions on how to route the channel.
13
+ 4. **Reliable Google Chat Event Subscriptions:** A user adds the bot to a new Space, expecting it to process all messages in that space (not just `@mentions`). The bot uses user-level OAuth to subscribe to Workspace Events natively, keeping the subscription alive in the background so no messages are missed.
14
+
15
+ ## Requirements
16
+
17
+ ### 1. Adapter State & 1:1 Mapping
18
+ - **State Schema Updates:** The `state.json` schema for both adapters must be updated:
19
+ - Add `channelChatMap: Record<string, string>` to map external context IDs (Channel ID / Space ID) to Daemon Chat IDs.
20
+ - Change `lastSyncedMessageId: string` to `lastSyncedMessageIds: Record<string, string>` to track the sync state for each individual chat independently.
21
+ - **Google Chat Specific:** Add `spaceSubscriptions: Record<string, { subscriptionId: string, expirationDate: string, refreshToken: string }>` to manage the lifecycle of user-level space event subscriptions.
22
+ - **Strict 1:1 Mapping Constraint:** To prevent "echo" loops where a single daemon message broadcasts to multiple external channels, multiple channels/spaces **cannot** be mapped to the same daemon chat. If a user attempts to route a channel to a chat that is already mapped elsewhere, the bot will reject the command with an error indicating which channel is currently using it.
23
+
24
+ ### 2. The "First Contact" Protocol
25
+ - **Detection:** When an incoming message is received from an authorized user in a context (channel/space) that does not exist in the `channelChatMap`.
26
+ - **Interception:** If the message is *not* a routing command (i.e. not starting with `/chat` or `/agent`), the message is **not** forwarded to the daemon.
27
+ - **Onboarding Reply:** The bot replies with an instructional message:
28
+ - "This channel/space is not currently mapped to a specific chat."
29
+ - "To route this channel to an existing chat, use `/chat <chat-id>`."
30
+ - "To route this channel to a specific agent, use `/agent <agent-id>`."
31
+ - **Persistent Warning:** The bot will continue to intercept and reply with this warning for every subsequent message until the user explicitly runs `/chat` or `/agent` to configure the channel. There is no automatic fallback.
32
+
33
+ ### 3. Routing Commands
34
+ - **Command Handling:** The adapters must intercept messages starting with `/chat` and `/agent` *before* sending them to the daemon.
35
+ - **`/chat [chat-id]`**:
36
+ - Fetches the list of all chats via `trpc.getChats.query()`.
37
+ - If no `[chat-id]` is provided, or if the provided ID does not exist, replies with a formatted list of all available chats.
38
+ - Checks if `[chat-id]` is already mapped to a *different* channel in `channelChatMap`. If so, returns an error.
39
+ - If valid and available, updates `channelChatMap` to map the current channel/space to `[chat-id]`.
40
+ - Replies with confirmation: "This channel is now routed to chat: `[chat-id]`."
41
+ - **`/agent [agent-id]`**:
42
+ - Fetches the list of all agents via `trpc.getAgents.query()`.
43
+ - If no `[agent-id]` is provided, or if the provided ID does not exist, replies with a formatted list of all available agents.
44
+ - If `[agent-id]` exists, generates a new chat ID formatted as `<agent-id>-<adapter-name>`. If a chat with that ID already exists, it appends a number (e.g., `<agent-id>-<adapter-name>-1`, `-2`, etc.) until a unique ID is found.
45
+ - Uses a TRPC endpoint to explicitly instruct the daemon to create the new chat and assign the specified agent to it.
46
+ - Updates `channelChatMap` to point to the newly generated chat ID.
47
+ - Replies with confirmation: "Created new chat and routed this channel to it: `<new-chat-id>` using agent `[agent-id]`."
48
+
49
+ ### 4. Dynamic Forwarding Subscriptions
50
+ - **Forwarder Adjustments:** Currently, the forwarders establish a single subscription to the default `chatId` using `trpc.waitForMessages.subscribe`.
51
+ - **Multi-Subscription:** The forwarders must be updated to manage subscriptions for *all* `chatIds` actively mapped in `channelChatMap`.
52
+ - **Dynamic Updates:** When a command (`/chat` or `/agent`) updates the map, the forwarder must dynamically establish a subscription to the newly mapped `chatId`. It must track `lastSyncedMessageIds[chatId]` independently to resume correctly.
53
+
54
+ ### 5. Platform-Specific Details
55
+ - **Discord:**
56
+ - Remove the restriction that ignores messages in guilds (`if (message.guild) return;`).
57
+ - Continue to rigorously enforce the `authorizedUserId` check. Only process commands and messages from the authorized user.
58
+ - **Mention Requirement:** Introduce a new adapter configuration property `requireMention` (boolean, defaults to `false`).
59
+ - If `false` (default): The bot responds to all messages from the authorized user in a mapped channel (assuming most channels are just the user + bot).
60
+ - If `true`: The bot will only process messages in mapped *Guild (Server) channels* if the bot is explicitly `@mentioned` (or the message is a direct reply to the bot). Direct Messages (DMs) bypass this check and are always processed.
61
+ - **Google Chat (Space Subscriptions):**
62
+ - **OAuth Upgrade:** Rename legacy `driveOauthClientId`, `driveOauthClientSecret`, and `driveOauthTokens` to generic `oauthClientId`, `oauthClientSecret`, and `oauthTokens` in state and config. Update the OAuth flow in `auth.ts` to request a combined scope array: `['https://www.googleapis.com/auth/drive.file', 'https://www.googleapis.com/auth/chat.messages.readonly']` and `access_type=offline` to capture a user-level Refresh Token. Ensure `index.ts` unconditionally initializes the `getUserAuthClient()` flow if OAuth credentials exist, regardless of the `driveUploadEnabled` setting.
63
+ - **`ADDED_TO_SPACE` Creation:** Intercept `ADDED_TO_SPACE` events natively. For non-DM spaces, use the globally stored user OAuth tokens to generate an Access Token and `POST https://workspaceevents.googleapis.com/v1/subscriptions` to subscribe to `google.workspace.chat.message.v1.created` using the configured notification endpoint topic. Save `subscriptionId` and `expirationDate` to the channel's entry in `channelChatMap` in `state.json`.
64
+ - **Background Renewal Cron:** Establish a daily recurring task that checks `channelChatMap` entries for expirations < 48 hours away. For those nearing expiration, fetch a fresh Access Token using the globally saved user OAuth tokens and `PATCH /v1/subscriptions/{id}` to renew the `ttl`, then save the new `expirationDate`.
65
+ - **`REMOVED_FROM_SPACE` Cleanup:** Intercept removal events to gracefully issue a `DELETE` request for the corresponding subscription ID and remove the subscription fields (or the entire entry if `chatId` is not set) from `channelChatMap` in `state.json`.
66
+ - **Dual-Pipeline Ingestion & Deduplication:** The Pub/Sub worker will receive both native Bot Events and new Workspace Events (from space subscriptions). It must:
67
+ 1. Identify the source via the `ce-type` attribute (if `google.workspace.chat.message.v1.created`, parse as Workspace Event; else, native Bot Event).
68
+ 2. Explicitly drop messages where the sender is `BOT` to prevent infinite reply loops.
69
+ 3. Maintain a 60-second in-memory LRU cache of processed Message IDs. If a bot `@mention` causes an event in both pipelines, drop the duplicate.
70
+ 4. Send all replies via the Bot's Service Account (not the user's token).
71
+
72
+ ## Privacy, Security & Accessibility
73
+ - **Authorization:** Only the explicitly `authorizedUserId` (Discord) or authorized emails (Google Chat) can trigger the First Contact protocol or use the `/chat` and `/agent` commands. All other users in the channel are strictly ignored.
74
+ - **Information Disclosure:** Validating chat and agent IDs prevents users from accidentally creating junk records in the state.
75
+ - **Noise Reduction:** The persistent warning and strict requirement for manual configuration prevents random channel chatter from polluting backend contexts. The `requireMention` setting in Discord provides an opt-in safety net for shared public channels.
76
+ - **Google Chat User Tokens:** User Refresh Tokens must be securely persisted locally in `state.json` solely for the purpose of maintaining space subscriptions. They must not be transmitted externally. The adapter must strictly reply to Google Chat using the bot's service account, keeping the user's identity decoupled from bot actions.e user's identity decoupled from bot actions.he bot's service account, keeping the user's identity decoupled from bot actions.e user's identity decoupled from bot actions.
@@ -0,0 +1,16 @@
1
+ # Questions
2
+
3
+ 1. **Explicit Routing Syntax:** To allow users to specify a chat or agent for a channel, what syntax should be used?
4
+ - **Answer:** We generally use slash commands, so `/chat <id>` or `/agent <id>`. If we use `/agent <id>`, we should create a new chat named `<agent-id>-<adapter-name>-1` and display it to the user so they know which chat it is.
5
+
6
+ 2. **New Contexts Default Behavior:** When a user sends a message in a *new* Discord channel or Google Chat space (where no mapping exists yet), what is the default behavior?
7
+ - **Answer:** The first message from a user (if not a `/chat` or `/agent` command) should be ignored in terms of passing it to the daemon. Instead, the bot should reply telling the user to use `/chat` or `/agent` to configure the channel, or inform them that if they continue talking, it will use the default chat (and explicitly state which chat that is).
8
+
9
+ 3. **Discord Scope:** In Discord, should we respond to *all* messages in a channel from authorized users, or only when the bot is `@mentioned`?
10
+ - **Answer:** We will respond to all messages from the authorized user in the mapped channel without requiring a `@mention`, as the new-channel onboarding flow explicitly tells them they can "continue talking" to use the default chat. (Bot and unauthorized user messages will continue to be ignored).
11
+
12
+ 4. **Validation API:** To validate if a user-specified chat or agent exists, should the adapters use new or existing TRPC queries (e.g., `trpc.getChats` / `trpc.getAgents`)?
13
+ - **Answer:** Using the existing list queries (`trpc.getChats` / `trpc.getAgents`) is fine because there won't be a massive number of entities to filter through.
14
+
15
+ 5. **Mapping Storage:** Should the mapping of `(Adapter Channel/Space ID) -> (Daemon Chat ID)` be stored in the adapter's local `state.json`?
16
+ - **Answer:** Yes, it should be added to the adapter's existing `state.json`.
@@ -0,0 +1,164 @@
1
+ # Tickets for Adapter Multi-Chat Support
2
+
3
+ ## Ticket 1: Update State and Config Schemas for Multi-Chat Support
4
+ **Status**: Completed
5
+
6
+ **Description**:
7
+ Update the state and configuration schemas for both the Discord and Google Chat adapters to prepare for multi-chat mapping and independent state tracking.
8
+
9
+ **Tasks**:
10
+ - Update adapter state schemas in `src/adapter-discord` and `src/adapter-google-chat` to replace `lastSyncedMessageId: string` with `lastSyncedMessageIds: Record<string, string>`.
11
+ - Add `channelChatMap: Record<string, string>` to the state schemas to map external context IDs (Channel ID / Space ID) to Daemon Chat IDs.
12
+ - Update the Discord adapter config schema to include an optional `requireMention: boolean` property, defaulting to `false`.
13
+ - Ensure backwards compatibility or safe migration for existing `state.json` files.
14
+
15
+ **Verification**:
16
+ - Verify schema changes by running tests that instantiate the adapters with updated and legacy configurations.
17
+ - Run `npm run validate` (which includes `npm run format`, `npm run lint:fix`, type-checking, and tests).
18
+
19
+ ---
20
+
21
+ ## Ticket 2: Implement First Contact Protocol and Message Pre-processing
22
+ **Status**: Completed
23
+
24
+ **Description**:
25
+ Adjust the message ingestion pipeline for both adapters to handle incoming messages in new contexts and implement platform-specific filtering rules.
26
+
27
+ **Tasks**:
28
+ - **Discord**: Remove the `if (message.guild) return;` block to allow processing messages in server channels/threads.
29
+ - **Discord**: Implement logic to enforce the `requireMention` config flag. If true, ignore messages in guild channels unless the bot is explicitly `@mentioned` or directly replied to. DM behavior remains unaffected.
30
+ - **Both Adapters**: Implement the "First Contact" protocol. Before forwarding a message to the daemon, check if the external context ID (Discord channel ID or Google Chat space ID) exists in `channelChatMap`.
31
+ - If unmapped and the message is *not* a routing command (`/chat` or `/agent`), intercept the message, do not forward it, and reply with the instructional warning: "This channel/space is not currently mapped...".
32
+
33
+ **Verification**:
34
+ - Write unit tests for Discord message filtering (e.g., verifying `requireMention` behavior for guilds vs DMs).
35
+ - Write unit tests for the First Contact protocol interception logic.
36
+ - Run `npm run validate`.
37
+
38
+ ---
39
+
40
+ ## Ticket 3: Implement Routing Commands (`/chat` and `/agent`)
41
+ **Status**: Completed
42
+
43
+ **Description**:
44
+ Allow users to dynamically map their current external context (channel/space) to a specific daemon chat or a new chat with a specific agent.
45
+
46
+ **Tasks**:
47
+ - Intercept and handle the `/chat [chat-id]` command:
48
+ - Fetch available chats via `trpc.getChats.query()`. Reply with a formatted list if the provided ID is missing or invalid.
49
+ - Enforce the **Strict 1:1 Mapping Constraint**: Return an error if the requested chat is already mapped to a different channel.
50
+ - Upon success, update `channelChatMap` to point the current channel to the requested chat, save state, and reply with confirmation.
51
+ - Intercept and handle the `/agent [agent-id]` command:
52
+ - Fetch available agents via `trpc.getAgents.query()`. Reply with a formatted list if the provided ID is missing or invalid.
53
+ - If valid, generate a unique chat ID (`<agent-id>-<adapter-name>[-n]`).
54
+ - Instruct the daemon to create the new chat and assign the agent.
55
+ - Update `channelChatMap` to point to the newly created chat ID, save state, and reply with confirmation.
56
+
57
+ **Verification**:
58
+ - Write unit tests for command parsing, list formatting, and 1:1 constraint validation.
59
+ - Mock TRPC endpoints in tests to verify correct interaction with the daemon for fetching lists and creating chats.
60
+ - Run `npm run validate`.
61
+
62
+ ---
63
+
64
+ ## Ticket 4: Implement Dynamic Forwarding Subscriptions
65
+ **Status**: Completed
66
+
67
+ **Description**:
68
+ Refactor the daemon-to-adapter forwarding logic to support multiple concurrent subscriptions and dynamic updates based on `channelChatMap`.
69
+
70
+ **Tasks**:
71
+ - Refactor `startDaemonToDiscordForwarder` and `startDaemonToGoogleChatForwarder` to manage a dynamic pool of `trpc.waitForMessages.subscribe` subscriptions instead of a single static one.
72
+ - The forwarders should iterate over unique `chatIds` in `channelChatMap` and establish a subscription for each.
73
+ - Implement an event or polling mechanism so that when `channelChatMap` changes (e.g., a new routing command is executed), the forwarder dynamically establishes subscriptions for newly mapped chats.
74
+ - Ensure the forwarder uses and updates `lastSyncedMessageIds[chatId]` independently for each subscription to prevent message loss on restart.
75
+
76
+ **Verification**:
77
+ - Write integration or unit tests verifying that multiple mocked subscriptions can run concurrently and route daemon messages to the correct mapped external channels.
78
+ - Verify that state updates dynamically trigger new subscriptions without requiring an adapter restart.
79
+ - Run `npm run validate`.
80
+
81
+ ---
82
+
83
+ ## Ticket 5: Add Google Chat Space Subscription Config & State Schemas
84
+ **Status**: Completed
85
+
86
+ **Description**:
87
+ Update the schemas to support user-level Space Subscriptions for Workspace Events.
88
+
89
+ **Tasks**:
90
+ - Update `GoogleChatStateSchema` in `src/adapter-google-chat/state.ts` using `zod` to expand `channelChatMap` to store subscription data: `z.record(z.string(), z.object({ chatId: z.string().nullable().optional(), subscriptionId: z.string().optional(), expirationDate: z.string().optional() })).optional()`.
91
+ - Ensure migrations cleanly default `channelChatMap` to handle single-chat legacy formats if present.
92
+ - Rename legacy `driveOauthClientId`, `driveOauthClientSecret`, and `driveOauthTokens` to generic `oauthClientId`, `oauthClientSecret`, and `oauthTokens` across schemas.
93
+ - Update `src/adapter-google-chat/auth.ts` to request a combined scope array: `['https://www.googleapis.com/auth/drive.file', 'https://www.googleapis.com/auth/chat.messages.readonly']` and `access_type=offline`. Rename `getDriveAuthClient` to `getUserAuthClient`.
94
+ - Update `src/adapter-google-chat/index.ts` to invoke `getUserAuthClient()` if OAuth credentials exist, removing the dependency on `driveUploadEnabled !== false`.
95
+
96
+ **Verification**:
97
+ - Add unit tests for schema parsing and single-chat legacy migrations for `channelChatMap` subscriptions.
98
+ - Run `npm run validate`.
99
+
100
+ ---
101
+
102
+ ## Ticket 6: Implement Space Subscription Lifecycle (`ADDED_TO_SPACE` & `REMOVED_FROM_SPACE`)
103
+ **Status**: Completed
104
+
105
+ **Description**:
106
+ Handle Google Chat bot events to automatically create and tear down Space Subscriptions when the bot is added to or removed from a Space.
107
+
108
+ **Tasks**:
109
+ - Update `startGoogleChatIngestion` in `src/adapter-google-chat/client.ts` to intercept `ADDED_TO_SPACE` events.
110
+ - If the space type is not `DIRECT_MESSAGE`:
111
+ 1. Generate a new Access Token using the saved user Refresh Token.
112
+ 2. Perform a `POST` request to `https://workspaceevents.googleapis.com/v1/subscriptions` to subscribe to `google.workspace.chat.message.v1.created` for the target space.
113
+ 3. Save the returned subscription info (`subscriptionId`, `expirationDate`) into `state.json` under the channel's entry in `channelChatMap`.
114
+ - Intercept `REMOVED_FROM_SPACE` events:
115
+ 1. Look up the `subscriptionId` in `channelChatMap`.
116
+ 2. Generate an Access Token and send a `DELETE` request to remove the subscription.
117
+ 3. Delete the subscription fields from the channel's entry in `channelChatMap`, and delete the entry entirely if `chatId` is not set.
118
+
119
+ **Verification**:
120
+ - Add unit tests mocking the Google Workspace Events API to ensure `ADDED_TO_SPACE` correctly creates subscriptions and writes to state.
121
+ - Add unit tests mocking `REMOVED_FROM_SPACE` deletion.
122
+ - Run `npm run validate`.
123
+
124
+ ---
125
+
126
+ ## Ticket 7: Dual-Pipeline Pub/Sub Worker Logic
127
+ **Status**: Completed
128
+
129
+ **Description**:
130
+ Refactor message ingestion to correctly process native Bot Events alongside the newly configured Workspace Events coming from the same Pub/Sub topic, ensuring no duplication or loops.
131
+
132
+ **Tasks**:
133
+ - Update `startGoogleChatIngestion` in `src/adapter-google-chat/client.ts`.
134
+ - Read the `ce-type` attribute of the Pub/Sub message. If it equals `google.workspace.chat.message.v1.created`, parse the payload as a Workspace Event. Otherwise, parse as a native Bot Event.
135
+ - Explicitly check the sender of the event. If the sender is `BOT`, `message.ack()` and drop it immediately to prevent infinite reply loops.
136
+ - Implement an in-memory 60-second LRU cache (or Map with cleanup) to store `Message ID`s. If a message ID has already been seen (because an `@mention` triggers both pipelines), drop the duplicate.
137
+ - Ensure all outbound replies continue to use the Bot's Service Account, not the user token.
138
+
139
+ **Verification**:
140
+ - Add tests mocking Workspace Event payloads vs Bot Event payloads, verifying both are formatted and forwarded properly.
141
+ - Add tests simulating an identical Message ID arriving twice in quick succession and verify only one daemon interaction occurs.
142
+ - Add tests verifying messages from `BOT` are dropped.
143
+ - Run `npm run validate`.
144
+
145
+ ---
146
+
147
+ ## Ticket 8: Background Renewal Cron for Subscriptions
148
+ **Status**: Completed
149
+
150
+ **Description**:
151
+ Implement a background process to prevent active Space Subscriptions from expiring after their default 7-day TTL.
152
+
153
+ **Tasks**:
154
+ - Implement a recurring background task (e.g., an hourly `setInterval` or cron script) in the Google Chat adapter.
155
+ - Parse `channelChatMap` from `state.json`.
156
+ - Identify subscriptions where `expirationDate` is less than 48 hours away.
157
+ - For expiring subscriptions, use the globally stored user OAuth tokens to fetch an Access Token.
158
+ - `PATCH https://workspaceevents.googleapis.com/v1/subscriptions/{subscriptionId}?updateMask=ttl` with `{ "ttl": "604800s" }`.
159
+ - Update `state.json` with the newly returned `expirationDate`.
160
+
161
+ **Verification**:
162
+ - Add unit tests for the background renewal logic using fake timers to verify renewals trigger when dates are close.
163
+ - Mock the PATCH request and verify state updates.
164
+ - Run `npm run validate`.
@@ -0,0 +1,31 @@
1
+ # Development Log
2
+
3
+ ## Initialization
4
+ - Started working on "Step 1: Update Agent Schema".
5
+ - Added `apiTokenEnvVar` and `apiUrlEnvVar` to `AgentSchema` in `src/shared/config.ts`.
6
+ - Ran `npm run format && npm run lint:fix && npm run validate`.
7
+ - Validations failed initially due to zombie processes on the e2e test port, so killed zombie node and daemon processes and retried successfully.
8
+ - Marked Step 1 as completed in `tickets.md`.
9
+
10
+ ## Step 2: Update Lite Client Resolution Logic
11
+ - Updated `src/cli/lite.ts` to dynamically resolve authentication environment variables.
12
+ - Read pointer variables `CLAW_LITE_API_VAR` and `CLAW_LITE_URL_VAR` to resolve the `API_TOKEN` and `API_URL` respectively.
13
+ - Updated error handling to log out the expected dynamically resolved variable names instead of the hardcoded names if missing.
14
+ - Ran `npm run format && npm run lint:fix && npm run validate` which succeeded.
15
+ - Marked Step 2 as completed in `tickets.md`.
16
+
17
+ ## Step 3: Update Daemon Environment Variable Injection
18
+ - Updated `src/daemon/agent/agent-session.ts` in `buildExecutionContext`.
19
+ - Added logic to check if `currentAgent.apiTokenEnvVar` and `currentAgent.apiUrlEnvVar` are defined.
20
+ - Injected token/URL into custom variables and updated pointer variables `CLAW_LITE_API_VAR` and `CLAW_LITE_URL_VAR`.
21
+ - If custom variables are not defined, gracefully falls back to `CLAW_API_TOKEN` and `CLAW_API_URL` respectively.
22
+ - Ran validations (`npm run format && npm run lint:fix && npm run validate`), all tests passed.
23
+ - Marked Step 3 as completed in `tickets.md`.
24
+
25
+ ## Step 4: Add and Update E2E Tests
26
+ - Added a new E2E test in `src/cli/e2e/daemon.test.ts` to verify injection and functioning of custom `apiTokenEnvVar` and `apiUrlEnvVar`.
27
+ - Created an agent with customized pointer variables and used it to send messages.
28
+ - Verified that `lite` dynamically correctly leverages `CLAW_LITE_API_VAR` and `CLAW_LITE_URL_VAR` to securely interact with the Daemon APIs.
29
+ - Reviewed `export-lite-func.test.ts` and `requests.test.ts`, confirming they inherently verify the fallback/default behavior remains completely intact.
30
+ - Ran formatting and final validation checks (`npm run validate`); all 380 E2E tests successfully pass.
31
+ - Marked Step 4 as completed in `tickets.md`.
@@ -0,0 +1,16 @@
1
+ # Notes on Custom Token Env
2
+
3
+ ## Current Behavior
4
+ - The daemon securely authenticates with the Agent API using a dynamically generated HMAC token (`CLAW_API_TOKEN`).
5
+ - `src/daemon/agent/agent-session.ts` injects `CLAW_API_URL` and `CLAW_API_TOKEN` into the environment of the executed agent process.
6
+ - `src/cli/lite.ts` (which compiles to `clawmini-lite.js`) reads these via `process.env.CLAW_API_URL` and `process.env.CLAW_API_TOKEN`.
7
+ - E2E tests (`src/cli/e2e/`) check for `CLAW_API_TOKEN=` in the environment variables.
8
+
9
+ ## Issue
10
+ - Gemini CLI strips out environment variables that include `TOKEN`, `KEY`, `SECRET`, etc., unless they have the `GEMINI_CLI_` prefix.
11
+ - Therefore, when Clawmini runs Gemini CLI as an agent, `CLAW_API_TOKEN` is stripped, and `clawmini-lite.js` (used for fallbacks or tool usage) cannot authenticate.
12
+
13
+ ## Solution Direction
14
+ - Allow configuring an alternative env var name to pass the token (e.g., `GEMINI_CLI_CLAW_API_TOKEN`).
15
+ - If this is configured, the daemon must inject the token using this alternative name.
16
+ - We need a way to tell `clawmini-lite.js` which environment variable contains the token, since it won't be `CLAW_API_TOKEN` anymore. This pointer variable itself must not contain the words `TOKEN`, `KEY`, or `SECRET` to avoid being stripped.
@@ -0,0 +1,42 @@
1
+ # Product Requirements Document: Custom Token Environment Variables
2
+
3
+ ## 1. Vision
4
+ Enable Clawmini to integrate seamlessly with strict sandboxes and external CLI tools (like Gemini CLI) that aggressively filter or strip environment variables containing sensitive keywords such as `TOKEN`, `KEY`, or `SECRET`. By allowing agents to configure custom names for the API URL and API Token environment variables, Clawmini ensures that injected authentication details reach the target agent process without being discarded by intermediate wrappers.
5
+
6
+ ## 2. Product/Market Background
7
+ Clawmini securely authenticates spawned agents with its API using a dynamically generated HMAC token passed via the `CLAW_API_TOKEN` environment variable. The agent uses the lightweight `clawmini-lite.js` script to execute tasks (e.g., fallbacks, tool usage). However, some agents—such as the Gemini CLI—employ strict security measures that strip out environment variables matching sensitive keyword patterns unless explicitly prefixed (e.g., `GEMINI_CLI_`). This behavior breaks Clawmini's authentication flow because `CLAW_API_TOKEN` is stripped before `clawmini-lite.js` runs.
8
+
9
+ ## 3. Use Cases
10
+ - **Running Gemini CLI as an Agent:** A user configures Gemini CLI as a Clawmini agent. They define `apiTokenEnvVar: 'GEMINI_CLI_CLAW_API_TOKEN'` in the agent configuration so that the authentication token bypasses Gemini CLI's environment variable filter.
11
+ - **Custom URL Overrides:** A user is running an agent inside an environment where `CLAW_API_URL` conflicts with an existing system variable or is stripped, and they need to map it to a custom variable name like `CUSTOM_API_HOST`.
12
+
13
+ ## 4. Requirements
14
+
15
+ ### 4.1. Configuration (`src/shared/config.ts`)
16
+ - Update the `AgentSchema` (and corresponding `Agent` type) to include two new optional string properties:
17
+ - `apiTokenEnvVar`: Specifies an alternative environment variable name for the API token.
18
+ - `apiUrlEnvVar`: Specifies an alternative environment variable name for the API URL.
19
+
20
+ ### 4.2. Daemon Injection (`src/daemon/agent/agent-session.ts`)
21
+ - When spawning an agent, check if `agent.apiTokenEnvVar` and/or `agent.apiUrlEnvVar` are set.
22
+ - If `apiTokenEnvVar` is defined:
23
+ - Inject the token into the specified custom environment variable (e.g., `GEMINI_CLI_CLAW_API_TOKEN`) instead of `CLAW_API_TOKEN`.
24
+ - Inject a "pointer" environment variable named `CLAW_LITE_API_VAR` whose value is the name of the custom token variable (e.g., `"GEMINI_CLI_CLAW_API_TOKEN"`). This pointer must *not* be stripped by target tools, hence avoiding keywords like `TOKEN`.
25
+ - If `apiUrlEnvVar` is defined:
26
+ - Inject the API URL into the specified custom environment variable instead of `CLAW_API_URL`.
27
+ - Also ensure that `clawmini-lite.js` knows where to look for the URL (e.g., using another pointer variable like `CLAW_LITE_URL_VAR`, or encoding both in a structured way). Given the structure, a matching pointer for the URL `CLAW_LITE_URL_VAR` should be introduced.
28
+
29
+ ### 4.3. Lite Client Updates (`src/cli/lite.ts`)
30
+ - `clawmini-lite.js` must be updated to dynamically resolve the environment variable names used for authentication.
31
+ - Read the pointer variables first (`process.env.CLAW_LITE_API_VAR` and `process.env.CLAW_LITE_URL_VAR`).
32
+ - If `CLAW_LITE_API_VAR` is set, read the token from `process.env[process.env.CLAW_LITE_API_VAR]`. Fallback to `process.env.CLAW_API_TOKEN` if the pointer is not set.
33
+ - If `CLAW_LITE_URL_VAR` is set, read the URL from `process.env[process.env.CLAW_LITE_URL_VAR]`. Fallback to `process.env.CLAW_API_URL` if the pointer is not set.
34
+ - Ensure appropriate error handling if the resolved variables are missing or empty.
35
+
36
+ ### 4.4. Security Considerations
37
+ - The introduction of custom environment variables does not alter the fundamental HMAC authentication model. The token remains dynamically generated per execution and is only valid for its intended scope.
38
+ - The pointer variables (`CLAW_LITE_API_VAR`, `CLAW_LITE_URL_VAR`) must not contain sensitive strings like `TOKEN` to ensure they bypass downstream filters.
39
+
40
+ ### 4.5. Testing
41
+ - **E2E Tests:** Update `src/cli/e2e/daemon.test.ts`, `src/cli/e2e/export-lite-func.test.ts`, and `src/cli/e2e/requests.test.ts` to accommodate or verify the fallback logic.
42
+ - Write a specific test case that configures an agent with `apiTokenEnvVar` and `apiUrlEnvVar`, ensuring the daemon properly injects the custom and pointer variables, and verifying that a mock `clawmini-lite.js` invocation successfully authenticates using the redirected variables.
@@ -0,0 +1,8 @@
1
+ # Questions for Custom Token Env
2
+
3
+ 1. Since we need a way to tell `clawmini-lite.js` which environment variable contains the token, what should we name this "pointer" environment variable? It cannot contain `TOKEN`, `KEY`, or `SECRET` (e.g., `CLAW_API_TOKEN_VAR` would be stripped). Would something like `CLAW_LITE_API_VAR` or `CLAW_AUTH_ENV` work?
4
+ - **Answer:** `CLAW_LITE_API_VAR` is a good choice as it avoids sensitive words.
5
+ 2. Where should the configuration for the alternative environment variable name live? Should we add an `apiTokenEnvVar` property to the `Agent` schema, the `Environment` schema, or both (with Agent overriding Environment)?
6
+ - **Answer:** The `Agent` schema is the appropriate place. The property name should be `apiTokenEnvVar`.
7
+ 3. Does `CLAW_API_URL` pass through Gemini CLI without issues, or do we also need to support renaming the URL environment variable?
8
+ - **Answer:** It generally passes through without issues, but we should also allow the user to override it via an `apiUrlEnvVar` property on the `Agent` schema.
@@ -0,0 +1,54 @@
1
+ # Tickets: Custom Token Environment Variables
2
+
3
+ ## Step 1: Update Agent Schema
4
+ **Status**: Completed
5
+
6
+ **Tasks:**
7
+ - Update `src/shared/config.ts`.
8
+ - Add `apiTokenEnvVar` (optional string) to `AgentSchema` and `Agent` type.
9
+ - Add `apiUrlEnvVar` (optional string) to `AgentSchema` and `Agent` type.
10
+
11
+ **Verification:**
12
+ - Run `npm run format` and `npm run lint:fix`.
13
+ - Run `npm run validate` to ensure type checking and tests pass.
14
+
15
+ ## Step 2: Update Lite Client Resolution Logic
16
+ **Status**: Completed
17
+
18
+ **Tasks:**
19
+ - Update `src/cli/lite.ts` to dynamically resolve authentication environment variables.
20
+ - Read pointer variables: `process.env.CLAW_LITE_API_VAR` and `process.env.CLAW_LITE_URL_VAR`.
21
+ - Resolve token: If `CLAW_LITE_API_VAR` is set, use `process.env[process.env.CLAW_LITE_API_VAR]`. Otherwise, fallback to `process.env.CLAW_API_TOKEN`.
22
+ - Resolve URL: If `CLAW_LITE_URL_VAR` is set, use `process.env[process.env.CLAW_LITE_URL_VAR]`. Otherwise, fallback to `process.env.CLAW_API_URL`.
23
+ - Ensure appropriate error handling if the resolved variables are missing or empty.
24
+
25
+ **Verification:**
26
+ - Run `npm run format` and `npm run lint:fix`.
27
+ - Run `npm run validate` to ensure code compiles.
28
+
29
+ ## Step 3: Update Daemon Environment Variable Injection
30
+ **Status**: Completed
31
+
32
+ **Tasks:**
33
+ - Update `src/daemon/agent/agent-session.ts`.
34
+ - When spawning an agent, check for `agent.apiTokenEnvVar` and `agent.apiUrlEnvVar`.
35
+ - If `apiTokenEnvVar` is defined, inject the token into the custom variable and set `CLAW_LITE_API_VAR` to the custom variable name. Do not inject `CLAW_API_TOKEN`.
36
+ - If `apiUrlEnvVar` is defined, inject the URL into the custom variable and set `CLAW_LITE_URL_VAR` to the custom variable name. Do not inject `CLAW_API_URL`.
37
+ - If they are not defined, inject `CLAW_API_TOKEN` and `CLAW_API_URL` as usual.
38
+
39
+ **Verification:**
40
+ - Run `npm run format` and `npm run lint:fix`.
41
+ - Run `npm run validate` to ensure tests still pass.
42
+
43
+ ## Step 4: Add and Update E2E Tests
44
+ **Status**: Completed
45
+
46
+ **Tasks:**
47
+ - Update existing E2E tests (e.g., `src/cli/e2e/daemon.test.ts`, `src/cli/e2e/export-lite-func.test.ts`, `src/cli/e2e/requests.test.ts`) if they explicitly check for the hardcoded `CLAW_API_TOKEN` but shouldn't, or update them to verify default behavior remains intact.
48
+ - Add a new specific test case configuring an agent with both `apiTokenEnvVar` and `apiUrlEnvVar`.
49
+ - Verify that the daemon correctly injects the custom variables and the pointer variables (`CLAW_LITE_API_VAR`, `CLAW_LITE_URL_VAR`).
50
+ - Verify that an invocation of `clawmini-lite` via the CLI or mock successfully authenticates using these redirected variables.
51
+
52
+ **Verification:**
53
+ - Run `npm run format` and `npm run lint:fix`.
54
+ - Run `npm run validate` to verify all new and existing tests pass successfully.
@@ -53,10 +53,12 @@ This will create a `config.json` file at `.clawmini/adapters/discord/config.json
53
53
  {
54
54
  "botToken": "YOUR_DISCORD_BOT_TOKEN",
55
55
  "authorizedUserId": "YOUR_DISCORD_USER_ID",
56
- "chatId": "default"
56
+ "chatId": "default",
57
+ "requireMention": false
57
58
  }
58
59
  ```
59
- *(Note: `chatId` defaults to `"default"`. You can change this if you want the bot to associate with a different chat).*
60
+
61
+ _(Note: `chatId` defaults to `"default"`. You can change this if you want the bot to associate with a different chat. `requireMention` defaults to `false` and can be set to `true` if you only want the bot to respond when explicitly mentioned)._
60
62
 
61
63
  ## Step 5: Start the Adapter
62
64
 
@@ -67,3 +69,14 @@ npx clawmini-adapter-discord
67
69
  ```
68
70
 
69
71
  The adapter will now forward authorized Discord DM messages to your Clawmini daemon and vice versa.
72
+
73
+ ## Routing and Creating Chats
74
+
75
+ By default, the adapter connects to a single chat (the one specified as `chatId` in your `config.json`). However, you can create new Discord channels (or use different DMs) to map to separate Clawmini chats.
76
+
77
+ 1. Create a new Text Channel in your Discord server (or open a new DM group).
78
+ 2. Invite the bot to the channel if necessary.
79
+ 3. Send the command `/agent [agent-id]` to automatically create a new Clawmini chat with that agent and map it to the current Discord channel.
80
+ 4. Alternatively, use `/chat [chat-id]` to map that specific Discord channel to an existing Clawmini chat.
81
+
82
+ _Note: Each Discord channel can only be mapped to one Clawmini chat, and each Clawmini chat can only be mapped to one channel/space across all adapters._