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
@@ -20,7 +20,9 @@ export class PolicyRequestService {
20
20
  args: string[],
21
21
  fileMappings: Record<string, string>,
22
22
  chatId: string,
23
- agentId: string
23
+ agentId: string,
24
+ skipSave: boolean = false,
25
+ subagentId?: string
24
26
  ): Promise<PolicyRequest> {
25
27
  const allRequests = await this.store.list();
26
28
  const pendingCount = allRequests.filter((r) => r.state === 'Pending').length;
@@ -45,13 +47,16 @@ export class PolicyRequestService {
45
47
  commandName,
46
48
  args,
47
49
  fileMappings: snapshotMappings,
48
- state: 'Pending',
50
+ state: skipSave ? 'Approved' : 'Pending',
49
51
  createdAt: Date.now(),
50
52
  chatId,
51
53
  agentId,
54
+ ...(subagentId ? { subagentId } : {}),
52
55
  };
53
56
 
54
- await this.store.save(request);
57
+ if (!skipSave) {
58
+ await this.store.save(request);
59
+ }
55
60
 
56
61
  return request;
57
62
  }
@@ -4,7 +4,7 @@ import path from 'node:path';
4
4
  import { randomBytes } from 'node:crypto';
5
5
  import { spawn } from 'node:child_process';
6
6
  import { pathIsInsideDir } from '../shared/utils/fs.js';
7
- import type { PolicyRequest } from '../shared/policies.js';
7
+ import type { PolicyRequest, PolicyDefinition } from '../shared/policies.js';
8
8
 
9
9
  export const MAX_SNAPSHOT_SIZE = 5 * 1024 * 1024;
10
10
 
@@ -127,6 +127,24 @@ export function executeSafe(
127
127
  });
128
128
  }
129
129
 
130
+ export async function executeRequest(
131
+ request: PolicyRequest,
132
+ policy: PolicyDefinition,
133
+ cwd?: string
134
+ ): Promise<{ stdout: string; stderr: string; exitCode: number; commandStr: string }> {
135
+ const fullArgs = [...(policy.args || []), ...request.args];
136
+ const interpolatedArgs = interpolateArgs(fullArgs, request.fileMappings);
137
+
138
+ const { stdout, stderr, exitCode } = await executeSafe(
139
+ policy.command,
140
+ interpolatedArgs,
141
+ cwd ? { cwd } : undefined
142
+ );
143
+
144
+ const commandStr = `${policy.command} ${interpolatedArgs.join(' ')}`;
145
+ return { stdout, stderr, exitCode, commandStr };
146
+ }
147
+
130
148
  export async function generateRequestPreview(request: PolicyRequest): Promise<string> {
131
149
  let previewContent = `Sandbox Policy Request: ${request.commandName}\n`;
132
150
  previewContent += `ID: ${request.id}\n`;
@@ -85,6 +85,22 @@ export class Queue<TPayload = string> {
85
85
 
86
86
  return extracted;
87
87
  }
88
+
89
+ interrupt(predicate?: (payload: TPayload) => boolean): TPayload[] {
90
+ const payloads: TPayload[] = [];
91
+
92
+ const currentMatches =
93
+ !predicate || (this.currentPayload !== undefined && predicate(this.currentPayload));
94
+ if (currentMatches && this.currentPayload !== undefined) {
95
+ payloads.push(this.currentPayload);
96
+ }
97
+
98
+ this.abortCurrent(predicate);
99
+
100
+ payloads.push(...this.extractPending(predicate));
101
+
102
+ return payloads;
103
+ }
88
104
  }
89
105
 
90
106
  export interface MessageQueuePayload {
@@ -6,6 +6,10 @@ import path from 'path';
6
6
  import * as workspace from '../shared/workspace.js';
7
7
 
8
8
  vi.mock('../shared/workspace.js', () => ({
9
+ resolveAgentWorkDir: vi
10
+ .fn()
11
+ .mockImplementation((id, dir, root) => (dir ? `${root}/${dir}` : `${root}/${id}`)),
12
+
9
13
  getClawminiDir: vi.fn(),
10
14
  }));
11
15
 
@@ -0,0 +1,122 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { createSessionTimeoutRouter } from './session-timeout.js';
3
+ import type { RouterState } from './types.js';
4
+
5
+ // Mock crypto.randomUUID to return a predictable value
6
+ vi.mock('node:crypto', () => ({
7
+ randomUUID: () => 'mock-uuid',
8
+ }));
9
+
10
+ describe('sessionTimeoutRouter', () => {
11
+ it('refreshes the timeout job with default settings', () => {
12
+ const router = createSessionTimeoutRouter();
13
+ const initialState: RouterState = {
14
+ messageId: 'msg-1',
15
+ message: 'Hello!',
16
+ chatId: 'chat-1',
17
+ sessionId: 'session-123',
18
+ };
19
+
20
+ const nextState = router(initialState);
21
+
22
+ expect(nextState.nextSessionId).toBeUndefined(); // Should not modify current session
23
+ expect(nextState.jobs?.remove).toContain('__session_timeout__session-123');
24
+ expect(nextState.jobs?.add).toEqual(
25
+ expect.arrayContaining([
26
+ expect.objectContaining({
27
+ id: '__session_timeout__session-123',
28
+ schedule: { at: '60m' },
29
+ message:
30
+ 'This chat session has ended. Save any important details from it to your memory. When finished, reply with NO_REPLY_NECESSARY.',
31
+ reply: '[@clawmini/session-timeout] Starting a fresh session...',
32
+ nextSessionId: 'mock-uuid',
33
+ session: { type: 'existing', id: 'session-123' },
34
+ jobs: {
35
+ remove: ['__session_timeout__session-123'],
36
+ },
37
+ }),
38
+ ])
39
+ );
40
+ });
41
+
42
+ it('works correctly when sessionId is undefined', () => {
43
+ const router = createSessionTimeoutRouter();
44
+ const initialState: RouterState = {
45
+ messageId: 'msg-1',
46
+ message: 'Hello!',
47
+ chatId: 'chat-1',
48
+ };
49
+
50
+ const nextState = router(initialState);
51
+ const generatedSessionId = nextState.sessionId;
52
+
53
+ expect(generatedSessionId).toBeDefined();
54
+ expect(nextState.jobs?.remove).toContain(`__session_timeout__${generatedSessionId}`);
55
+ expect(nextState.jobs?.add).toEqual(
56
+ expect.arrayContaining([
57
+ expect.objectContaining({
58
+ id: `__session_timeout__${generatedSessionId}`,
59
+ schedule: { at: '60m' },
60
+ message:
61
+ 'This chat session has ended. Save any important details from it to your memory. When finished, reply with NO_REPLY_NECESSARY.',
62
+ reply: '[@clawmini/session-timeout] Starting a fresh session...',
63
+ nextSessionId: expect.any(String),
64
+ session: { type: 'existing', id: generatedSessionId },
65
+ env: { __SESSION_TIMEOUT__: 'true' },
66
+ jobs: {
67
+ remove: [`__session_timeout__${generatedSessionId}`],
68
+ },
69
+ }),
70
+ ])
71
+ );
72
+ });
73
+
74
+ it('respects custom timeout and prompt configuration', () => {
75
+ const router = createSessionTimeoutRouter({
76
+ timeout: '30m',
77
+ prompt: 'Custom prompt',
78
+ });
79
+ const initialState: RouterState = {
80
+ messageId: 'msg-2',
81
+ message: 'Hello again!',
82
+ chatId: 'chat-1',
83
+ sessionId: 'session-abc',
84
+ };
85
+
86
+ const nextState = router(initialState);
87
+
88
+ expect(nextState.jobs?.remove).toContain('__session_timeout__session-abc');
89
+ expect(nextState.jobs?.add).toEqual(
90
+ expect.arrayContaining([
91
+ expect.objectContaining({
92
+ id: '__session_timeout__session-abc',
93
+ schedule: { at: '30m' },
94
+ message: 'Custom prompt',
95
+ reply: '[@clawmini/session-timeout] Starting a fresh session...',
96
+ nextSessionId: 'mock-uuid',
97
+ session: { type: 'existing', id: 'session-abc' },
98
+ env: { __SESSION_TIMEOUT__: 'true' },
99
+ jobs: {
100
+ remove: ['__session_timeout__session-abc'],
101
+ },
102
+ }),
103
+ ])
104
+ );
105
+ });
106
+
107
+ it('bypasses timeout job creation if currently executing a timeout', () => {
108
+ const router = createSessionTimeoutRouter();
109
+ const initialState: RouterState = {
110
+ messageId: 'msg-3',
111
+ message: 'Timeout prompt',
112
+ chatId: 'chat-1',
113
+ sessionId: 'session-xyz',
114
+ env: { __SESSION_TIMEOUT__: 'true' },
115
+ jobs: { remove: ['__session_timeout__session-xyz'] },
116
+ };
117
+
118
+ const nextState = router(initialState);
119
+
120
+ expect(nextState).toBe(initialState); // Returns exactly the same state without modifications
121
+ });
122
+ });
@@ -0,0 +1,71 @@
1
+ import type { RouterState } from './types.js';
2
+ import { randomUUID } from 'node:crypto';
3
+
4
+ export interface SessionTimeoutConfig {
5
+ timeout?: string;
6
+ prompt?: string;
7
+ }
8
+
9
+ /**
10
+ * Router that automatically starts a new session after a period of inactivity.
11
+ *
12
+ * To register this router, add it to your `~/.gemini/settings.json`:
13
+ * ```json
14
+ * {
15
+ * "routers": [
16
+ * {
17
+ * "use": "session-timeout",
18
+ * "with": {
19
+ * "timeout": "60m",
20
+ * "prompt": "This chat session has ended. Save any important details from it to your memory. When finished, reply with NO_REPLY_NECESSARY."
21
+ * }
22
+ * }
23
+ * ]
24
+ * }
25
+ * ```
26
+ */
27
+ export function createSessionTimeoutRouter(config: SessionTimeoutConfig = {}) {
28
+ const timeStr = config.timeout ?? '60m';
29
+ const prompt =
30
+ config.prompt ??
31
+ 'This chat session has ended. Save any important details from it to your memory. When finished, reply with NO_REPLY_NECESSARY.';
32
+
33
+ return function (state: RouterState): RouterState {
34
+ if (state.env?.__SESSION_TIMEOUT__ === 'true') {
35
+ return state;
36
+ }
37
+
38
+ const sessionId = state.sessionId || crypto.randomUUID();
39
+ const jobId = `__session_timeout__${sessionId}`;
40
+
41
+ const jobs = {
42
+ ...state.jobs,
43
+ remove: [...(state.jobs?.remove || []), jobId, '__session_timeout__'],
44
+ };
45
+
46
+ return {
47
+ ...state,
48
+ sessionId,
49
+ jobs: {
50
+ ...jobs,
51
+ add: [
52
+ ...(jobs.add || []),
53
+ // Add a job after the timeout that will send the prompt, reply to the user,
54
+ // start a fresh session, and delete the job
55
+ {
56
+ id: jobId,
57
+ schedule: { at: timeStr },
58
+ message: prompt,
59
+ reply: '[@clawmini/session-timeout] Starting a fresh session...',
60
+ nextSessionId: randomUUID(),
61
+ session: { type: 'existing', id: sessionId },
62
+ env: { __SESSION_TIMEOUT__: 'true' },
63
+ jobs: {
64
+ remove: [jobId],
65
+ },
66
+ },
67
+ ],
68
+ },
69
+ };
70
+ };
71
+ }
@@ -3,10 +3,12 @@ import type { RouterState } from './types.js';
3
3
  export function slashNew(state: RouterState): RouterState {
4
4
  if (/^\/new(\s|$)/.test(state.message)) {
5
5
  const newMessage = state.message.replace(/^\/new(\s+|$)/, '').trim();
6
+ const id = crypto.randomUUID();
6
7
  return {
7
8
  ...state,
8
9
  message: newMessage,
9
- sessionId: crypto.randomUUID(),
10
+ sessionId: id,
11
+ nextSessionId: id,
10
12
  reply: '[@clawmini/slash-new] Starting a new session...',
11
13
  };
12
14
  }
@@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
3
3
  import { slashPolicies } from './slash-policies.js';
4
4
  import { RequestStore } from '../request-store.js';
5
5
  import { readPolicies } from '../../shared/workspace.js';
6
- import { executeSafe, interpolateArgs } from '../policy-utils.js';
6
+ import { executeRequest } from '../policy-utils.js';
7
7
  import { appendMessage } from '../chats.js';
8
8
  import type { PolicyRequest } from '../../shared/policies.js';
9
9
 
@@ -40,11 +40,11 @@ describe('slashPolicies', () => {
40
40
  },
41
41
  },
42
42
  });
43
- vi.mocked(interpolateArgs).mockReturnValue(['hello', 'world']);
44
- vi.mocked(executeSafe).mockResolvedValue({
43
+ vi.mocked(executeRequest).mockResolvedValue({
45
44
  stdout: 'hello world',
46
45
  stderr: '',
47
46
  exitCode: 0,
47
+ commandStr: 'echo hello world',
48
48
  });
49
49
  });
50
50
 
@@ -97,16 +97,19 @@ describe('slashPolicies', () => {
97
97
  const state = { message: '/approve req-1', messageId: 'mock-msg-id', chatId: 'chat-1' };
98
98
  const result = await slashPolicies(state);
99
99
 
100
- expect(mockStore.save).toHaveBeenCalledWith({ ...pendingReq, state: 'Approved' });
101
- expect(executeSafe).toHaveBeenCalledWith('echo', ['hello', 'world'], expect.any(Object));
100
+ expect(mockStore.save).toHaveBeenCalledWith({
101
+ ...pendingReq,
102
+ state: 'Approved',
103
+ executionResult: { stdout: 'hello world', stderr: '', exitCode: 0 },
104
+ });
105
+ expect(executeRequest).toHaveBeenCalledWith(pendingReq, expect.any(Object), undefined);
102
106
  expect(appendMessage).toHaveBeenCalledWith(
103
107
  'chat-1',
104
108
  expect.objectContaining({
105
- role: 'log',
106
- content: 'Request req-1 approved and executed.',
107
- command: 'echo hello world',
108
- stdout: 'hello world',
109
- exitCode: 0,
109
+ role: 'system',
110
+ event: 'policy_approved',
111
+ displayRole: 'user',
112
+ content: expect.stringContaining('Request req-1 approved.'),
110
113
  })
111
114
  );
112
115
  expect(result.action).toBeUndefined();
@@ -139,13 +142,23 @@ describe('slashPolicies', () => {
139
142
  state: 'Rejected',
140
143
  rejectionReason: 'Not allowed',
141
144
  });
145
+ expect(appendMessage).toHaveBeenCalledTimes(2);
146
+ expect(appendMessage).toHaveBeenCalledWith(
147
+ 'chat-1',
148
+ expect.objectContaining({
149
+ role: 'system',
150
+ event: 'policy_rejected',
151
+ displayRole: 'user',
152
+ content: 'Request req-1 rejected. Reason: Not allowed',
153
+ })
154
+ );
142
155
  expect(appendMessage).toHaveBeenCalledWith(
143
156
  'chat-1',
144
157
  expect.objectContaining({
145
- role: 'log',
158
+ role: 'system',
159
+ event: 'policy_rejected',
160
+ displayRole: 'agent',
146
161
  content: 'Request req-1 rejected. Reason: Not allowed',
147
- command: 'policy-request-reject req-1',
148
- exitCode: 1,
149
162
  })
150
163
  );
151
164
  expect(result.action).toBeUndefined();
@@ -2,9 +2,9 @@ import { randomUUID } from 'node:crypto';
2
2
  import type { RouterState } from './types.js';
3
3
  import { RequestStore } from '../request-store.js';
4
4
  import { readPolicies, getWorkspaceRoot } from '../../shared/workspace.js';
5
- import { executeSafe, interpolateArgs } from '../policy-utils.js';
5
+ import { executeRequest } from '../policy-utils.js';
6
6
  import { appendMessage } from '../chats.js';
7
- import type { CommandLogMessage } from '../../shared/chats.js';
7
+ import type { SystemMessage } from '../../shared/chats.js';
8
8
 
9
9
  async function loadAndValidateRequest(id: string, state: RouterState) {
10
10
  const store = new RequestStore(getWorkspaceRoot());
@@ -54,37 +54,32 @@ export async function slashPolicies(state: RouterState): Promise<RouterState> {
54
54
  }
55
55
 
56
56
  req.state = 'Approved';
57
- await store.save(req);
58
57
 
59
- const fullArgs = [...(policy.args || []), ...req.args];
60
- const interpolatedArgs = interpolateArgs(fullArgs, req.fileMappings);
58
+ const { stdout, stderr, exitCode } = await executeRequest(req, policy, getWorkspaceRoot());
61
59
 
62
- const { stdout, stderr, exitCode } = await executeSafe(policy.command, interpolatedArgs, {
63
- cwd: getWorkspaceRoot(),
64
- });
60
+ req.executionResult = { stdout, stderr, exitCode };
61
+ await store.save(req);
65
62
 
66
- const commandStr = `${policy.command} ${interpolatedArgs.join(' ')}`;
67
- const logMsg: CommandLogMessage = {
63
+ const agentMessage = `Request ${id} approved.\n\n${wrapInHtml('stdout', stdout)}\n\n${wrapInHtml('stderr', stderr)}\n\nExit Code: ${exitCode}`;
64
+
65
+ const logMsg: SystemMessage = {
68
66
  id: randomUUID(),
69
67
  messageId: state.messageId,
70
- role: 'log',
71
- source: 'router',
72
- content: `Request ${id} approved and executed.`,
73
- stderr,
74
- stdout,
68
+ role: 'system',
69
+ event: 'policy_approved',
70
+ displayRole: 'user',
71
+ content: agentMessage,
75
72
  timestamp: new Date().toISOString(),
76
- command: commandStr,
77
- cwd: getWorkspaceRoot(),
78
- exitCode,
73
+ ...(req.subagentId ? { subagentId: req.subagentId } : {}),
79
74
  };
80
75
 
81
76
  await appendMessage(state.chatId, logMsg);
82
77
 
83
- const agentMessage = `Request ${id} approved.\n\n${wrapInHtml('stdout', stdout)}\n\n${wrapInHtml('stderr', stderr)}\n\nExit Code: ${exitCode}`;
84
78
  return {
85
79
  ...state,
86
80
  message: agentMessage,
87
81
  reply: `Approved request, running ${req.commandName}`,
82
+ ...(req.subagentId ? { subagentId: req.subagentId } : {}),
88
83
  };
89
84
  }
90
85
 
@@ -101,23 +96,38 @@ export async function slashPolicies(state: RouterState): Promise<RouterState> {
101
96
  req.rejectionReason = reason;
102
97
  await store.save(req);
103
98
 
104
- const logMsg: CommandLogMessage = {
99
+ const agentMessage = `Request ${id} rejected. Reason: ${reason}`;
100
+
101
+ const logMsg: SystemMessage = {
105
102
  id: randomUUID(),
106
103
  messageId: state.messageId,
107
- role: 'log',
108
- source: 'router',
109
- content: `Request ${id} rejected. Reason: ${reason}`,
110
- stderr: '',
104
+ role: 'system',
105
+ event: 'policy_rejected',
106
+ displayRole: 'user',
107
+ content: agentMessage,
111
108
  timestamp: new Date().toISOString(),
112
- command: `policy-request-reject ${id}`,
113
- cwd: getWorkspaceRoot(),
114
- exitCode: 1,
109
+ ...(req.subagentId ? { subagentId: req.subagentId } : {}),
110
+ };
111
+
112
+ const userNotificationMsg: SystemMessage = {
113
+ id: randomUUID(),
114
+ messageId: state.messageId,
115
+ role: 'system',
116
+ event: 'policy_rejected',
117
+ displayRole: 'agent',
118
+ content: agentMessage,
119
+ timestamp: new Date().toISOString(),
120
+ ...(req.subagentId ? { subagentId: req.subagentId } : {}),
115
121
  };
116
122
 
117
123
  await appendMessage(state.chatId, logMsg);
124
+ await appendMessage(state.chatId, userNotificationMsg);
118
125
 
119
- const agentMessage = `Request ${id} rejected. Reason: ${reason}`;
120
- return { ...state, message: agentMessage };
126
+ return {
127
+ ...state,
128
+ message: agentMessage,
129
+ ...(req.subagentId ? { subagentId: req.subagentId } : {}),
130
+ };
121
131
  }
122
132
 
123
133
  return state;
@@ -1,10 +1,18 @@
1
+ import type { CronJob } from '../../shared/config.js';
2
+
1
3
  export interface RouterState {
2
4
  messageId: string;
3
5
  message: string;
4
6
  chatId: string;
5
7
  agentId?: string;
8
+ subagentId?: string;
6
9
  sessionId?: string;
10
+ nextSessionId?: string;
7
11
  env?: Record<string, string>;
8
12
  reply?: string;
9
13
  action?: 'stop' | 'interrupt' | 'continue';
14
+ jobs?: {
15
+ add?: CronJob[];
16
+ remove?: string[];
17
+ };
10
18
  }
@@ -5,18 +5,78 @@ import { slashCommand } from './routers/slash-command.js';
5
5
  import { slashStop } from './routers/slash-stop.js';
6
6
  import { slashInterrupt } from './routers/slash-interrupt.js';
7
7
  import { slashPolicies } from './routers/slash-policies.js';
8
+ import { createSessionTimeoutRouter } from './routers/session-timeout.js';
9
+ import type { RouterConfig } from '../shared/config.js';
10
+
11
+ export const GLOBAL_ROUTERS: RouterConfig[] = ['@clawmini/session-timeout'];
12
+
13
+ export const USER_ROUTERS: RouterConfig[] = [
14
+ '@clawmini/slash-new',
15
+ '@clawmini/slash-command',
16
+ '@clawmini/slash-stop',
17
+ '@clawmini/slash-interrupt',
18
+ '@clawmini/slash-policies',
19
+ ];
20
+
21
+ export function resolveRouters(
22
+ userRouters: RouterConfig[],
23
+ isUserMessage: boolean
24
+ ): RouterConfig[] {
25
+ const resolvedGlobals: RouterConfig[] = [];
26
+ const resolvedUsers: RouterConfig[] = [];
27
+
28
+ const userConfigMap = new Map<string, unknown>();
29
+ for (const r of userRouters) {
30
+ const name = typeof r === 'string' ? r : r.use;
31
+ const config = typeof r === 'string' ? {} : r.with || {};
32
+
33
+ if (name.startsWith('@clawmini/')) {
34
+ userConfigMap.set(name, config);
35
+ } else {
36
+ resolvedUsers.push(r);
37
+ }
38
+ }
39
+
40
+ for (const globalRouter of GLOBAL_ROUTERS) {
41
+ const name = typeof globalRouter === 'string' ? globalRouter : globalRouter.use;
42
+ const baseConfig = typeof globalRouter === 'string' ? {} : globalRouter.with || {};
43
+ const userConfig = userConfigMap.get(name) || {};
44
+ const mergedConfig = { ...baseConfig, ...userConfig };
45
+
46
+ resolvedGlobals.push({ use: name, with: mergedConfig });
47
+ }
48
+
49
+ const defaultUserRouters: RouterConfig[] = [];
50
+ for (const defaultUserRouter of USER_ROUTERS) {
51
+ const name = typeof defaultUserRouter === 'string' ? defaultUserRouter : defaultUserRouter.use;
52
+ const baseConfig = typeof defaultUserRouter === 'string' ? {} : defaultUserRouter.with || {};
53
+ const userConfig = userConfigMap.get(name) || {};
54
+ const mergedConfig = { ...baseConfig, ...userConfig };
55
+
56
+ defaultUserRouters.push({ use: name, with: mergedConfig });
57
+ }
58
+
59
+ if (isUserMessage) {
60
+ return [...resolvedGlobals, ...defaultUserRouters, ...resolvedUsers];
61
+ } else {
62
+ return resolvedGlobals;
63
+ }
64
+ }
8
65
 
9
66
  export async function executeRouterPipeline(
10
67
  initialState: RouterState,
11
- routers: string[]
68
+ routers: RouterConfig[]
12
69
  ): Promise<RouterState> {
13
70
  let state = { ...initialState };
14
71
 
15
- for (const router of routers) {
72
+ for (const routerDef of routers) {
16
73
  if (state.action === 'stop') {
17
74
  break;
18
75
  }
19
76
 
77
+ const router = typeof routerDef === 'string' ? routerDef : routerDef.use;
78
+ const config = typeof routerDef === 'string' ? {} : routerDef.with || {};
79
+
20
80
  if (router === '@clawmini/slash-new') {
21
81
  state = slashNew(state);
22
82
  } else if (router === '@clawmini/slash-command') {
@@ -27,6 +87,8 @@ export async function executeRouterPipeline(
27
87
  state = slashInterrupt(state);
28
88
  } else if (router === '@clawmini/slash-policies') {
29
89
  state = await slashPolicies(state);
90
+ } else if (router === '@clawmini/session-timeout') {
91
+ state = createSessionTimeoutRouter(config)(state);
30
92
  } else {
31
93
  // Execute as custom shell command
32
94
  try {
@@ -1,16 +1,14 @@
1
1
  import { spawn } from 'node:child_process';
2
- import type { RunCommandFn } from '../message.js';
2
+ import type { RunCommandFn } from '../agent/types.js';
3
3
 
4
- export const runCommand: RunCommandFn &
5
- ((
6
- args: Parameters<RunCommandFn>[0] & { logToTerminal?: boolean }
7
- ) => ReturnType<RunCommandFn>) = async ({
4
+ const LOG_TO_TERMINAL = true;
5
+
6
+ export const runCommand: RunCommandFn = async ({
8
7
  command,
9
8
  cwd,
10
9
  env,
11
10
  stdin,
12
11
  signal,
13
- logToTerminal,
14
12
  }: Parameters<RunCommandFn>[0] & { logToTerminal?: boolean }) => {
15
13
  return new Promise<{ stdout: string; stderr: string; exitCode: number }>((resolve, reject) => {
16
14
  const p = spawn(command, { shell: true, cwd, env, signal });
@@ -31,7 +29,7 @@ export const runCommand: RunCommandFn &
31
29
  if (p.stdout) {
32
30
  p.stdout.on('data', (data) => {
33
31
  stdout += data.toString();
34
- if (logToTerminal && !stdin) {
32
+ if (LOG_TO_TERMINAL && !stdin) {
35
33
  process.stdout.write(data);
36
34
  }
37
35
  });
@@ -40,7 +38,7 @@ export const runCommand: RunCommandFn &
40
38
  if (p.stderr) {
41
39
  p.stderr.on('data', (data) => {
42
40
  stderr += data.toString();
43
- if (logToTerminal && !stdin) {
41
+ if (LOG_TO_TERMINAL && !stdin) {
44
42
  process.stderr.write(data);
45
43
  }
46
44
  });