clawmini 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. package/.gemini/settings.json +46 -0
  2. package/.prettierrc +7 -0
  3. package/GEMINI.md +11 -0
  4. package/README.md +137 -0
  5. package/dist/adapter-discord/index.d.mts +5 -0
  6. package/dist/adapter-discord/index.d.mts.map +1 -0
  7. package/dist/adapter-discord/index.mjs +456 -0
  8. package/dist/adapter-discord/index.mjs.map +1 -0
  9. package/dist/chats-DKgTeU7i.mjs +91 -0
  10. package/dist/chats-DKgTeU7i.mjs.map +1 -0
  11. package/dist/chats-Zd_HXDHx.mjs +29 -0
  12. package/dist/chats-Zd_HXDHx.mjs.map +1 -0
  13. package/dist/cli/index.d.mts +1 -0
  14. package/dist/cli/index.mjs +850 -0
  15. package/dist/cli/index.mjs.map +1 -0
  16. package/dist/cli/lite.d.mts +1 -0
  17. package/dist/cli/lite.mjs +4434 -0
  18. package/dist/cli/lite.mjs.map +1 -0
  19. package/dist/daemon/index.d.mts +5 -0
  20. package/dist/daemon/index.d.mts.map +1 -0
  21. package/dist/daemon/index.mjs +1222 -0
  22. package/dist/daemon/index.mjs.map +1 -0
  23. package/dist/fetch-BjZVyU3Z.mjs +37 -0
  24. package/dist/fetch-BjZVyU3Z.mjs.map +1 -0
  25. package/dist/fs-B5wW0oaH.mjs +14 -0
  26. package/dist/fs-B5wW0oaH.mjs.map +1 -0
  27. package/dist/lite-Dl7WXyaH.mjs +80 -0
  28. package/dist/lite-Dl7WXyaH.mjs.map +1 -0
  29. package/dist/rolldown-runtime-95iHPtFO.mjs +18 -0
  30. package/dist/web/_app/env.js +1 -0
  31. package/dist/web/_app/immutable/assets/0.GI4C4dpV.css +1 -0
  32. package/dist/web/_app/immutable/chunks/B5abRDXp.js +1 -0
  33. package/dist/web/_app/immutable/chunks/B8yYFADm.js +1 -0
  34. package/dist/web/_app/immutable/chunks/BPy8HLo7.js +5 -0
  35. package/dist/web/_app/immutable/chunks/Bi0jeV7Q.js +1 -0
  36. package/dist/web/_app/immutable/chunks/BmUXQ3wy.js +2 -0
  37. package/dist/web/_app/immutable/chunks/C3k55nDF.js +1 -0
  38. package/dist/web/_app/immutable/chunks/COekwvP2.js +1 -0
  39. package/dist/web/_app/immutable/chunks/CSvS_NwK.js +1 -0
  40. package/dist/web/_app/immutable/chunks/CpaGRn9L.js +1 -0
  41. package/dist/web/_app/immutable/chunks/CyNaE55B.js +1 -0
  42. package/dist/web/_app/immutable/chunks/DG5RZBw-.js +2 -0
  43. package/dist/web/_app/immutable/chunks/Dc-UOHw9.js +1 -0
  44. package/dist/web/_app/immutable/chunks/DcrmIfTj.js +1 -0
  45. package/dist/web/_app/immutable/chunks/ZkLyk0mE.js +1 -0
  46. package/dist/web/_app/immutable/entry/app.B-vZe7PN.js +2 -0
  47. package/dist/web/_app/immutable/entry/start.oP1AgKhs.js +1 -0
  48. package/dist/web/_app/immutable/nodes/0.B5WFN0zw.js +1 -0
  49. package/dist/web/_app/immutable/nodes/1.D1wtJb2k.js +1 -0
  50. package/dist/web/_app/immutable/nodes/2.CK3CLC0f.js +1 -0
  51. package/dist/web/_app/immutable/nodes/3.BB5wCoBf.js +4 -0
  52. package/dist/web/_app/immutable/nodes/4.Dr2jvAXK.js +1 -0
  53. package/dist/web/_app/immutable/nodes/5.BJl7oM3b.js +1 -0
  54. package/dist/web/_app/version.json +1 -0
  55. package/dist/web/index.html +37 -0
  56. package/dist/web/robots.txt +3 -0
  57. package/dist/workspace-CSgfo_2J.mjs +383 -0
  58. package/dist/workspace-CSgfo_2J.mjs.map +1 -0
  59. package/docs/01_chats/development_log.md +36 -0
  60. package/docs/01_chats/notes.md +27 -0
  61. package/docs/01_chats/prd.md +47 -0
  62. package/docs/01_chats/questions.md +19 -0
  63. package/docs/01_chats/tickets.md +67 -0
  64. package/docs/02_sessions/development_log.md +79 -0
  65. package/docs/02_sessions/notes.md +40 -0
  66. package/docs/02_sessions/prd.md +75 -0
  67. package/docs/02_sessions/questions.md +7 -0
  68. package/docs/02_sessions/tickets.md +68 -0
  69. package/docs/03_web_interface/development_log.md +60 -0
  70. package/docs/03_web_interface/notes.md +29 -0
  71. package/docs/03_web_interface/prd.md +42 -0
  72. package/docs/03_web_interface/questions.md +8 -0
  73. package/docs/03_web_interface/tickets.md +59 -0
  74. package/docs/04_agents/development_log.md +54 -0
  75. package/docs/04_agents/notes.md +45 -0
  76. package/docs/04_agents/prd.md +47 -0
  77. package/docs/04_agents/questions.md +13 -0
  78. package/docs/04_agents/tickets.md +107 -0
  79. package/docs/05_routers/development_log.md +13 -0
  80. package/docs/05_routers/notes.md +40 -0
  81. package/docs/05_routers/prd.md +55 -0
  82. package/docs/05_routers/questions.md +21 -0
  83. package/docs/05_routers/tickets.md +109 -0
  84. package/docs/06_agent_templates/development_log.md +38 -0
  85. package/docs/06_agent_templates/notes.md +25 -0
  86. package/docs/06_agent_templates/prd.md +34 -0
  87. package/docs/06_agent_templates/questions.md +11 -0
  88. package/docs/06_agent_templates/tickets.md +49 -0
  89. package/docs/06_cron/development_log.md +51 -0
  90. package/docs/06_cron/notes.md +14 -0
  91. package/docs/06_cron/prd.md +92 -0
  92. package/docs/06_cron/questions.md +15 -0
  93. package/docs/06_cron/tickets.md +75 -0
  94. package/docs/07_web_chat_ux/development_log.md +30 -0
  95. package/docs/07_web_chat_ux/notes.md +25 -0
  96. package/docs/07_web_chat_ux/prd.md +46 -0
  97. package/docs/07_web_chat_ux/questions.md +7 -0
  98. package/docs/07_web_chat_ux/tickets.md +48 -0
  99. package/docs/08_agent_api/development_log.md +52 -0
  100. package/docs/08_agent_api/notes.md +31 -0
  101. package/docs/08_agent_api/prd.md +56 -0
  102. package/docs/08_agent_api/questions.md +14 -0
  103. package/docs/08_agent_api/tickets.md +104 -0
  104. package/docs/09_agent_fallbacks/development_log.md +52 -0
  105. package/docs/09_agent_fallbacks/notes.md +40 -0
  106. package/docs/09_agent_fallbacks/prd.md +55 -0
  107. package/docs/09_agent_fallbacks/questions.md +10 -0
  108. package/docs/09_agent_fallbacks/tickets.md +88 -0
  109. package/docs/09_discord_adapter/development_log.md +95 -0
  110. package/docs/09_discord_adapter/notes.md +18 -0
  111. package/docs/09_discord_adapter/prd.md +57 -0
  112. package/docs/09_discord_adapter/questions.md +16 -0
  113. package/docs/09_discord_adapter/tickets.md +116 -0
  114. package/docs/10_file_attachments/development_log.md +55 -0
  115. package/docs/10_file_attachments/notes.md +59 -0
  116. package/docs/10_file_attachments/prd.md +73 -0
  117. package/docs/10_file_attachments/questions.md +15 -0
  118. package/docs/10_file_attachments/tickets.md +88 -0
  119. package/docs/11_message_verbosity/development_log.md +43 -0
  120. package/docs/11_message_verbosity/notes.md +26 -0
  121. package/docs/11_message_verbosity/prd.md +44 -0
  122. package/docs/11_message_verbosity/questions.md +8 -0
  123. package/docs/11_message_verbosity/tickets.md +33 -0
  124. package/docs/12_environments/development_log.md +43 -0
  125. package/docs/12_environments/notes.md +45 -0
  126. package/docs/12_environments/prd.md +113 -0
  127. package/docs/12_environments/questions.md +17 -0
  128. package/docs/12_environments/tickets.md +87 -0
  129. package/docs/12_setup_flow_improvements/development_log.md +40 -0
  130. package/docs/12_setup_flow_improvements/notes.md +34 -0
  131. package/docs/12_setup_flow_improvements/prd.md +35 -0
  132. package/docs/12_setup_flow_improvements/questions.md +8 -0
  133. package/docs/12_setup_flow_improvements/tickets.md +122 -0
  134. package/docs/13_discord_typing_indicators/development_log.md +38 -0
  135. package/docs/13_discord_typing_indicators/notes.md +18 -0
  136. package/docs/13_discord_typing_indicators/prd.md +41 -0
  137. package/docs/13_discord_typing_indicators/questions.md +6 -0
  138. package/docs/13_discord_typing_indicators/tickets.md +60 -0
  139. package/docs/14_interruptions/development_log.md +50 -0
  140. package/docs/14_interruptions/notes.md +38 -0
  141. package/docs/14_interruptions/prd.md +46 -0
  142. package/docs/14_interruptions/questions.md +12 -0
  143. package/docs/14_interruptions/tickets.md +69 -0
  144. package/docs/15_sandbox_policies/development_log.md +95 -0
  145. package/docs/15_sandbox_policies/notes.md +33 -0
  146. package/docs/15_sandbox_policies/prd.md +163 -0
  147. package/docs/15_sandbox_policies/questions.md +10 -0
  148. package/docs/15_sandbox_policies/tickets.md +196 -0
  149. package/docs/CHECKS.md +9 -0
  150. package/docs/guides/discord_adapter_setup.md +69 -0
  151. package/docs/guides/sandbox_policies.md +76 -0
  152. package/eslint.config.js +47 -0
  153. package/napkin.md +21 -0
  154. package/package.json +50 -0
  155. package/scripts/create_worktree.sh +49 -0
  156. package/scripts/get_pr_comments.sh +36 -0
  157. package/src/adapter-discord/client.test.ts +65 -0
  158. package/src/adapter-discord/client.ts +41 -0
  159. package/src/adapter-discord/config.test.ts +156 -0
  160. package/src/adapter-discord/config.ts +61 -0
  161. package/src/adapter-discord/forwarder.test.ts +493 -0
  162. package/src/adapter-discord/forwarder.ts +246 -0
  163. package/src/adapter-discord/index.test.ts +399 -0
  164. package/src/adapter-discord/index.ts +147 -0
  165. package/src/adapter-discord/state.test.ts +65 -0
  166. package/src/adapter-discord/state.ts +44 -0
  167. package/src/cli/client.ts +46 -0
  168. package/src/cli/commands/agents.ts +138 -0
  169. package/src/cli/commands/chats.ts +79 -0
  170. package/src/cli/commands/down.ts +32 -0
  171. package/src/cli/commands/environments.ts +39 -0
  172. package/src/cli/commands/export-lite.ts +62 -0
  173. package/src/cli/commands/init.ts +79 -0
  174. package/src/cli/commands/jobs.ts +141 -0
  175. package/src/cli/commands/messages.ts +103 -0
  176. package/src/cli/commands/up.ts +26 -0
  177. package/src/cli/commands/web-api/agents.ts +138 -0
  178. package/src/cli/commands/web-api/chats.ts +213 -0
  179. package/src/cli/commands/web-api/utils.ts +27 -0
  180. package/src/cli/commands/web.ts +105 -0
  181. package/src/cli/e2e/adapter-discord.test.ts +76 -0
  182. package/src/cli/e2e/agents.test.ts +140 -0
  183. package/src/cli/e2e/basic.test.ts +43 -0
  184. package/src/cli/e2e/cron.test.ts +132 -0
  185. package/src/cli/e2e/daemon.test.ts +293 -0
  186. package/src/cli/e2e/environments.test.ts +66 -0
  187. package/src/cli/e2e/export-lite-func.test.ts +155 -0
  188. package/src/cli/e2e/export-lite.test.ts +51 -0
  189. package/src/cli/e2e/fallbacks.test.ts +169 -0
  190. package/src/cli/e2e/global-setup.ts +15 -0
  191. package/src/cli/e2e/init.test.ts +70 -0
  192. package/src/cli/e2e/messages.test.ts +294 -0
  193. package/src/cli/e2e/requests.test.ts +165 -0
  194. package/src/cli/e2e/utils.ts +66 -0
  195. package/src/cli/index.test.ts +7 -0
  196. package/src/cli/index.ts +29 -0
  197. package/src/cli/lite.ts +247 -0
  198. package/src/cli/utils.ts +4 -0
  199. package/src/daemon/auth.test.ts +50 -0
  200. package/src/daemon/auth.ts +69 -0
  201. package/src/daemon/chats.ts +26 -0
  202. package/src/daemon/cron.test.ts +28 -0
  203. package/src/daemon/cron.ts +159 -0
  204. package/src/daemon/events.ts +15 -0
  205. package/src/daemon/index.ts +212 -0
  206. package/src/daemon/message-agent.test.ts +132 -0
  207. package/src/daemon/message-extraction.test.ts +166 -0
  208. package/src/daemon/message-fallbacks.test.ts +313 -0
  209. package/src/daemon/message-interruption.test.ts +125 -0
  210. package/src/daemon/message-queue.test.ts +143 -0
  211. package/src/daemon/message-router.test.ts +106 -0
  212. package/src/daemon/message-session.test.ts +127 -0
  213. package/src/daemon/message-test-utils.ts +41 -0
  214. package/src/daemon/message-typing.test.ts +93 -0
  215. package/src/daemon/message-verbosity.test.ts +127 -0
  216. package/src/daemon/message.ts +600 -0
  217. package/src/daemon/observation.test.ts +118 -0
  218. package/src/daemon/policy-request-service.test.ts +87 -0
  219. package/src/daemon/policy-request-service.ts +62 -0
  220. package/src/daemon/policy-utils.test.ts +138 -0
  221. package/src/daemon/policy-utils.ts +152 -0
  222. package/src/daemon/queue.test.ts +89 -0
  223. package/src/daemon/queue.ts +87 -0
  224. package/src/daemon/request-store.test.ts +103 -0
  225. package/src/daemon/request-store.ts +96 -0
  226. package/src/daemon/router-policy-request.test.ts +99 -0
  227. package/src/daemon/router.test.ts +380 -0
  228. package/src/daemon/router.ts +510 -0
  229. package/src/daemon/routers/slash-command.test.ts +145 -0
  230. package/src/daemon/routers/slash-command.ts +58 -0
  231. package/src/daemon/routers/slash-interrupt.test.ts +30 -0
  232. package/src/daemon/routers/slash-interrupt.ts +7 -0
  233. package/src/daemon/routers/slash-new.test.ts +59 -0
  234. package/src/daemon/routers/slash-new.ts +14 -0
  235. package/src/daemon/routers/slash-policies.test.ts +167 -0
  236. package/src/daemon/routers/slash-policies.ts +131 -0
  237. package/src/daemon/routers/slash-stop.test.ts +30 -0
  238. package/src/daemon/routers/slash-stop.ts +3 -0
  239. package/src/daemon/routers/types.ts +10 -0
  240. package/src/daemon/routers/utils.ts +22 -0
  241. package/src/daemon/routers.test.ts +141 -0
  242. package/src/daemon/routers.ts +115 -0
  243. package/src/daemon/utils/spawn.ts +61 -0
  244. package/src/shared/agent-utils.ts +30 -0
  245. package/src/shared/chats.test.ts +112 -0
  246. package/src/shared/chats.ts +164 -0
  247. package/src/shared/config.test.ts +90 -0
  248. package/src/shared/config.ts +100 -0
  249. package/src/shared/event-source.ts +121 -0
  250. package/src/shared/fetch.ts +45 -0
  251. package/src/shared/lite.ts +129 -0
  252. package/src/shared/policies.ts +24 -0
  253. package/src/shared/utils/env.ts +27 -0
  254. package/src/shared/utils/fs.ts +13 -0
  255. package/src/shared/workspace.test.ts +345 -0
  256. package/src/shared/workspace.ts +500 -0
  257. package/templates/environments/cladding/env.json +7 -0
  258. package/templates/environments/macos/env.json +8 -0
  259. package/templates/environments/macos/sandbox.sb +21 -0
  260. package/templates/environments/macos-proxy/allowlist.txt +1 -0
  261. package/templates/environments/macos-proxy/env.json +14 -0
  262. package/templates/environments/macos-proxy/proxy.mjs +86 -0
  263. package/templates/environments/macos-proxy/sandbox.sb +34 -0
  264. package/templates/gemini/settings.json +11 -0
  265. package/templates/gemini-claw/.gemini/hooks/clawmini-logging.sh +17 -0
  266. package/templates/gemini-claw/.gemini/settings.json +24 -0
  267. package/templates/gemini-claw/.gemini/skills/clawmini-jobs/SKILL.md +40 -0
  268. package/templates/gemini-claw/.gemini/system.md +98 -0
  269. package/templates/gemini-claw/BOOTSTRAP.md +54 -0
  270. package/templates/gemini-claw/GEMINI.md +107 -0
  271. package/templates/gemini-claw/HEARTBEAT.md +3 -0
  272. package/templates/gemini-claw/MEMORY.md +2 -0
  273. package/templates/gemini-claw/SOUL.md +42 -0
  274. package/templates/gemini-claw/TOOLS.md +38 -0
  275. package/templates/gemini-claw/USER.md +15 -0
  276. package/templates/gemini-claw/memory/.gitkeep +0 -0
  277. package/templates/gemini-claw/settings.json +24 -0
  278. package/templates/opencode/settings.json +11 -0
  279. package/tsconfig.json +42 -0
  280. package/tsdown.config.ts +19 -0
  281. package/vitest.config.ts +9 -0
  282. package/web/.svelte-kit/ambient.d.ts +382 -0
  283. package/web/.svelte-kit/generated/client/app.js +35 -0
  284. package/web/.svelte-kit/generated/client/matchers.js +1 -0
  285. package/web/.svelte-kit/generated/client/nodes/0.js +3 -0
  286. package/web/.svelte-kit/generated/client/nodes/1.js +1 -0
  287. package/web/.svelte-kit/generated/client/nodes/2.js +1 -0
  288. package/web/.svelte-kit/generated/client/nodes/3.js +1 -0
  289. package/web/.svelte-kit/generated/client/nodes/4.js +3 -0
  290. package/web/.svelte-kit/generated/client/nodes/5.js +3 -0
  291. package/web/.svelte-kit/generated/client-optimized/app.js +35 -0
  292. package/web/.svelte-kit/generated/client-optimized/matchers.js +1 -0
  293. package/web/.svelte-kit/generated/client-optimized/nodes/0.js +3 -0
  294. package/web/.svelte-kit/generated/client-optimized/nodes/1.js +1 -0
  295. package/web/.svelte-kit/generated/client-optimized/nodes/2.js +1 -0
  296. package/web/.svelte-kit/generated/client-optimized/nodes/3.js +1 -0
  297. package/web/.svelte-kit/generated/client-optimized/nodes/4.js +3 -0
  298. package/web/.svelte-kit/generated/client-optimized/nodes/5.js +3 -0
  299. package/web/.svelte-kit/generated/root.js +3 -0
  300. package/web/.svelte-kit/generated/root.svelte +68 -0
  301. package/web/.svelte-kit/generated/server/internal.js +53 -0
  302. package/web/.svelte-kit/non-ambient.d.ts +46 -0
  303. package/web/.svelte-kit/output/client/.vite/manifest.json +251 -0
  304. package/web/.svelte-kit/output/client/_app/immutable/assets/0.GI4C4dpV.css +1 -0
  305. package/web/.svelte-kit/output/client/_app/immutable/chunks/B5abRDXp.js +1 -0
  306. package/web/.svelte-kit/output/client/_app/immutable/chunks/B8yYFADm.js +1 -0
  307. package/web/.svelte-kit/output/client/_app/immutable/chunks/BPy8HLo7.js +5 -0
  308. package/web/.svelte-kit/output/client/_app/immutable/chunks/Bi0jeV7Q.js +1 -0
  309. package/web/.svelte-kit/output/client/_app/immutable/chunks/BmUXQ3wy.js +2 -0
  310. package/web/.svelte-kit/output/client/_app/immutable/chunks/C3k55nDF.js +1 -0
  311. package/web/.svelte-kit/output/client/_app/immutable/chunks/COekwvP2.js +1 -0
  312. package/web/.svelte-kit/output/client/_app/immutable/chunks/CSvS_NwK.js +1 -0
  313. package/web/.svelte-kit/output/client/_app/immutable/chunks/CpaGRn9L.js +1 -0
  314. package/web/.svelte-kit/output/client/_app/immutable/chunks/CyNaE55B.js +1 -0
  315. package/web/.svelte-kit/output/client/_app/immutable/chunks/DG5RZBw-.js +2 -0
  316. package/web/.svelte-kit/output/client/_app/immutable/chunks/Dc-UOHw9.js +1 -0
  317. package/web/.svelte-kit/output/client/_app/immutable/chunks/DcrmIfTj.js +1 -0
  318. package/web/.svelte-kit/output/client/_app/immutable/chunks/ZkLyk0mE.js +1 -0
  319. package/web/.svelte-kit/output/client/_app/immutable/entry/app.B-vZe7PN.js +2 -0
  320. package/web/.svelte-kit/output/client/_app/immutable/entry/start.oP1AgKhs.js +1 -0
  321. package/web/.svelte-kit/output/client/_app/immutable/nodes/0.B5WFN0zw.js +1 -0
  322. package/web/.svelte-kit/output/client/_app/immutable/nodes/1.D1wtJb2k.js +1 -0
  323. package/web/.svelte-kit/output/client/_app/immutable/nodes/2.CK3CLC0f.js +1 -0
  324. package/web/.svelte-kit/output/client/_app/immutable/nodes/3.BB5wCoBf.js +4 -0
  325. package/web/.svelte-kit/output/client/_app/immutable/nodes/4.Dr2jvAXK.js +1 -0
  326. package/web/.svelte-kit/output/client/_app/immutable/nodes/5.BJl7oM3b.js +1 -0
  327. package/web/.svelte-kit/output/client/_app/version.json +1 -0
  328. package/web/.svelte-kit/output/client/robots.txt +3 -0
  329. package/web/.svelte-kit/output/prerendered/dependencies/_app/env.js +1 -0
  330. package/web/.svelte-kit/output/server/.vite/manifest.json +215 -0
  331. package/web/.svelte-kit/output/server/_app/immutable/assets/_layout.GI4C4dpV.css +1 -0
  332. package/web/.svelte-kit/output/server/chunks/Icon.js +153 -0
  333. package/web/.svelte-kit/output/server/chunks/bot.js +2753 -0
  334. package/web/.svelte-kit/output/server/chunks/client.js +47 -0
  335. package/web/.svelte-kit/output/server/chunks/environment.js +34 -0
  336. package/web/.svelte-kit/output/server/chunks/exports.js +231 -0
  337. package/web/.svelte-kit/output/server/chunks/false.js +4 -0
  338. package/web/.svelte-kit/output/server/chunks/index-server.js +20 -0
  339. package/web/.svelte-kit/output/server/chunks/index.js +24 -0
  340. package/web/.svelte-kit/output/server/chunks/internal.js +133 -0
  341. package/web/.svelte-kit/output/server/chunks/plus.js +81 -0
  342. package/web/.svelte-kit/output/server/chunks/root.js +4076 -0
  343. package/web/.svelte-kit/output/server/chunks/shared.js +789 -0
  344. package/web/.svelte-kit/output/server/chunks/utils.js +43 -0
  345. package/web/.svelte-kit/output/server/entries/fallbacks/error.svelte.js +11 -0
  346. package/web/.svelte-kit/output/server/entries/pages/_layout.svelte.js +3944 -0
  347. package/web/.svelte-kit/output/server/entries/pages/_layout.ts.js +28 -0
  348. package/web/.svelte-kit/output/server/entries/pages/_page.svelte.js +7 -0
  349. package/web/.svelte-kit/output/server/entries/pages/agents/_page.svelte.js +379 -0
  350. package/web/.svelte-kit/output/server/entries/pages/chats/_id_/_page.svelte.js +292 -0
  351. package/web/.svelte-kit/output/server/entries/pages/chats/_id_/_page.ts.js +17 -0
  352. package/web/.svelte-kit/output/server/entries/pages/chats/_id_/settings/_page.svelte.js +259 -0
  353. package/web/.svelte-kit/output/server/entries/pages/chats/_id_/settings/_page.ts.js +17 -0
  354. package/web/.svelte-kit/output/server/index.js +3748 -0
  355. package/web/.svelte-kit/output/server/internal.js +14 -0
  356. package/web/.svelte-kit/output/server/manifest-full.js +63 -0
  357. package/web/.svelte-kit/output/server/manifest.js +63 -0
  358. package/web/.svelte-kit/output/server/nodes/0.js +13 -0
  359. package/web/.svelte-kit/output/server/nodes/1.js +8 -0
  360. package/web/.svelte-kit/output/server/nodes/2.js +8 -0
  361. package/web/.svelte-kit/output/server/nodes/3.js +8 -0
  362. package/web/.svelte-kit/output/server/nodes/4.js +13 -0
  363. package/web/.svelte-kit/output/server/nodes/5.js +13 -0
  364. package/web/.svelte-kit/output/server/remote-entry.js +557 -0
  365. package/web/.svelte-kit/tsconfig.json +67 -0
  366. package/web/.svelte-kit/types/route_meta_data.json +17 -0
  367. package/web/.svelte-kit/types/src/routes/$types.d.ts +26 -0
  368. package/web/.svelte-kit/types/src/routes/agents/$types.d.ts +18 -0
  369. package/web/.svelte-kit/types/src/routes/chats/[id]/$types.d.ts +21 -0
  370. package/web/.svelte-kit/types/src/routes/chats/[id]/proxy+page.ts +20 -0
  371. package/web/.svelte-kit/types/src/routes/chats/[id]/settings/$types.d.ts +21 -0
  372. package/web/.svelte-kit/types/src/routes/chats/[id]/settings/proxy+page.ts +19 -0
  373. package/web/.svelte-kit/types/src/routes/proxy+layout.ts +35 -0
  374. package/web/README.md +42 -0
  375. package/web/components.json +16 -0
  376. package/web/package.json +41 -0
  377. package/web/src/app.css +121 -0
  378. package/web/src/app.d.ts +13 -0
  379. package/web/src/app.html +11 -0
  380. package/web/src/demo.spec.ts +7 -0
  381. package/web/src/lib/app-state.svelte.ts +3 -0
  382. package/web/src/lib/assets/favicon.svg +1 -0
  383. package/web/src/lib/components/app/app-sidebar-test-wrapper.svelte +10 -0
  384. package/web/src/lib/components/app/app-sidebar.svelte +171 -0
  385. package/web/src/lib/components/app/app-sidebar.svelte.spec.ts +13 -0
  386. package/web/src/lib/components/ui/button/button.svelte +82 -0
  387. package/web/src/lib/components/ui/button/index.ts +17 -0
  388. package/web/src/lib/components/ui/dialog/dialog-close.svelte +7 -0
  389. package/web/src/lib/components/ui/dialog/dialog-content.svelte +45 -0
  390. package/web/src/lib/components/ui/dialog/dialog-description.svelte +17 -0
  391. package/web/src/lib/components/ui/dialog/dialog-footer.svelte +20 -0
  392. package/web/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
  393. package/web/src/lib/components/ui/dialog/dialog-overlay.svelte +20 -0
  394. package/web/src/lib/components/ui/dialog/dialog-portal.svelte +7 -0
  395. package/web/src/lib/components/ui/dialog/dialog-title.svelte +17 -0
  396. package/web/src/lib/components/ui/dialog/dialog-trigger.svelte +7 -0
  397. package/web/src/lib/components/ui/dialog/dialog.svelte +7 -0
  398. package/web/src/lib/components/ui/dialog/index.ts +34 -0
  399. package/web/src/lib/components/ui/input/index.ts +7 -0
  400. package/web/src/lib/components/ui/input/input.svelte +52 -0
  401. package/web/src/lib/components/ui/separator/index.ts +7 -0
  402. package/web/src/lib/components/ui/separator/separator.svelte +21 -0
  403. package/web/src/lib/components/ui/sheet/index.ts +34 -0
  404. package/web/src/lib/components/ui/sheet/sheet-close.svelte +7 -0
  405. package/web/src/lib/components/ui/sheet/sheet-content.svelte +60 -0
  406. package/web/src/lib/components/ui/sheet/sheet-description.svelte +17 -0
  407. package/web/src/lib/components/ui/sheet/sheet-footer.svelte +20 -0
  408. package/web/src/lib/components/ui/sheet/sheet-header.svelte +20 -0
  409. package/web/src/lib/components/ui/sheet/sheet-overlay.svelte +20 -0
  410. package/web/src/lib/components/ui/sheet/sheet-portal.svelte +7 -0
  411. package/web/src/lib/components/ui/sheet/sheet-title.svelte +17 -0
  412. package/web/src/lib/components/ui/sheet/sheet-trigger.svelte +7 -0
  413. package/web/src/lib/components/ui/sheet/sheet.svelte +7 -0
  414. package/web/src/lib/components/ui/sidebar/constants.ts +6 -0
  415. package/web/src/lib/components/ui/sidebar/context.svelte.ts +79 -0
  416. package/web/src/lib/components/ui/sidebar/index.ts +75 -0
  417. package/web/src/lib/components/ui/sidebar/sidebar-content.svelte +24 -0
  418. package/web/src/lib/components/ui/sidebar/sidebar-footer.svelte +21 -0
  419. package/web/src/lib/components/ui/sidebar/sidebar-group-action.svelte +36 -0
  420. package/web/src/lib/components/ui/sidebar/sidebar-group-content.svelte +21 -0
  421. package/web/src/lib/components/ui/sidebar/sidebar-group-label.svelte +34 -0
  422. package/web/src/lib/components/ui/sidebar/sidebar-group.svelte +21 -0
  423. package/web/src/lib/components/ui/sidebar/sidebar-header.svelte +21 -0
  424. package/web/src/lib/components/ui/sidebar/sidebar-input.svelte +21 -0
  425. package/web/src/lib/components/ui/sidebar/sidebar-inset.svelte +24 -0
  426. package/web/src/lib/components/ui/sidebar/sidebar-menu-action.svelte +43 -0
  427. package/web/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte +29 -0
  428. package/web/src/lib/components/ui/sidebar/sidebar-menu-button.svelte +103 -0
  429. package/web/src/lib/components/ui/sidebar/sidebar-menu-item.svelte +21 -0
  430. package/web/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte +36 -0
  431. package/web/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte +43 -0
  432. package/web/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte +21 -0
  433. package/web/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte +25 -0
  434. package/web/src/lib/components/ui/sidebar/sidebar-menu.svelte +21 -0
  435. package/web/src/lib/components/ui/sidebar/sidebar-provider.svelte +53 -0
  436. package/web/src/lib/components/ui/sidebar/sidebar-rail.svelte +36 -0
  437. package/web/src/lib/components/ui/sidebar/sidebar-separator.svelte +19 -0
  438. package/web/src/lib/components/ui/sidebar/sidebar-trigger.svelte +35 -0
  439. package/web/src/lib/components/ui/sidebar/sidebar.svelte +104 -0
  440. package/web/src/lib/components/ui/skeleton/index.ts +7 -0
  441. package/web/src/lib/components/ui/skeleton/skeleton.svelte +17 -0
  442. package/web/src/lib/components/ui/switch/index.ts +7 -0
  443. package/web/src/lib/components/ui/switch/switch.svelte +29 -0
  444. package/web/src/lib/components/ui/textarea/index.ts +7 -0
  445. package/web/src/lib/components/ui/textarea/textarea.svelte +23 -0
  446. package/web/src/lib/components/ui/tooltip/index.ts +19 -0
  447. package/web/src/lib/components/ui/tooltip/tooltip-content.svelte +52 -0
  448. package/web/src/lib/components/ui/tooltip/tooltip-portal.svelte +7 -0
  449. package/web/src/lib/components/ui/tooltip/tooltip-provider.svelte +7 -0
  450. package/web/src/lib/components/ui/tooltip/tooltip-trigger.svelte +7 -0
  451. package/web/src/lib/components/ui/tooltip/tooltip.svelte +7 -0
  452. package/web/src/lib/hooks/is-mobile.svelte.ts +9 -0
  453. package/web/src/lib/index.ts +1 -0
  454. package/web/src/lib/types.ts +23 -0
  455. package/web/src/lib/utils.ts +13 -0
  456. package/web/src/routes/+layout.svelte +67 -0
  457. package/web/src/routes/+layout.ts +34 -0
  458. package/web/src/routes/+page.svelte +7 -0
  459. package/web/src/routes/agents/+page.svelte +206 -0
  460. package/web/src/routes/chats/[id]/+page.svelte +406 -0
  461. package/web/src/routes/chats/[id]/+page.ts +19 -0
  462. package/web/src/routes/chats/[id]/page.svelte.spec.ts +102 -0
  463. package/web/src/routes/chats/[id]/settings/+page.svelte +165 -0
  464. package/web/src/routes/chats/[id]/settings/+page.ts +18 -0
  465. package/web/src/routes/page.svelte.spec.ts +13 -0
  466. package/web/static/robots.txt +3 -0
  467. package/web/svelte.config.js +21 -0
  468. package/web/tsconfig.json +20 -0
  469. package/web/vite.config.ts +41 -0
@@ -0,0 +1,600 @@
1
+ /* eslint-disable max-lines */
2
+ import path from 'node:path';
3
+ import { appendMessage, type UserMessage, type CommandLogMessage } from './chats.js';
4
+ import { getQueue } from './queue.js';
5
+ import { executeRouterPipeline } from './routers.js';
6
+ import type { RouterState } from './routers/types.js';
7
+ import {
8
+ type Settings,
9
+ type Agent,
10
+ type AgentSessionSettings,
11
+ type FallbackSchema,
12
+ } from '../shared/config.js';
13
+ import {
14
+ readChatSettings,
15
+ writeChatSettings,
16
+ readAgentSessionSettings,
17
+ writeAgentSessionSettings,
18
+ getAgent,
19
+ getWorkspaceRoot,
20
+ getActiveEnvironmentInfo,
21
+ getEnvironmentPath,
22
+ readEnvironment,
23
+ } from '../shared/workspace.js';
24
+ import { getApiContext, generateToken } from './auth.js';
25
+ import { emitTyping } from './events.js';
26
+ import { applyEnvOverrides, getActiveEnvKeys } from '../shared/utils/env.js';
27
+ import { z } from 'zod';
28
+
29
+ type Fallback = z.infer<typeof FallbackSchema>;
30
+
31
+ export function calculateDelay(
32
+ attempt: number,
33
+ baseDelayMs: number,
34
+ isFallback: boolean = false
35
+ ): number {
36
+ const effectiveAttempt = isFallback ? attempt + 1 : attempt;
37
+ if (effectiveAttempt <= 0) return 0;
38
+ const delay = baseDelayMs * Math.pow(2, effectiveAttempt - 1);
39
+ return Math.min(delay, 15000);
40
+ }
41
+
42
+ const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
43
+
44
+ export type RunCommandResult = {
45
+ stdout: string;
46
+ stderr: string;
47
+ exitCode: number;
48
+ };
49
+
50
+ export type RunCommandFn = (args: {
51
+ command: string;
52
+ cwd: string;
53
+ env: Record<string, string>;
54
+ stdin?: string | undefined;
55
+ signal?: AbortSignal | undefined;
56
+ }) => Promise<RunCommandResult>;
57
+
58
+ async function resolveSessionState(
59
+ chatId: string,
60
+ cwd: string,
61
+ sessionId?: string,
62
+ overrideAgentId?: string
63
+ ) {
64
+ const chatSettings = await readChatSettings(chatId, cwd);
65
+ const agentId =
66
+ overrideAgentId ??
67
+ (typeof chatSettings?.defaultAgent === 'string' ? chatSettings.defaultAgent : 'default');
68
+
69
+ let targetSessionId = sessionId;
70
+ if (!targetSessionId) {
71
+ const sessions = chatSettings?.sessions || {};
72
+ targetSessionId = sessions[agentId] || 'default';
73
+ }
74
+
75
+ const agentSessionSettings = await readAgentSessionSettings(agentId, targetSessionId, cwd);
76
+ const isNewSession = !agentSessionSettings;
77
+
78
+ return { chatSettings, agentId, targetSessionId, agentSessionSettings, isNewSession };
79
+ }
80
+
81
+ function prepareCommandAndEnv(
82
+ agent: Agent,
83
+ message: string,
84
+ isNewSession: boolean,
85
+ agentSessionSettings: AgentSessionSettings | null,
86
+ fallback?: Fallback
87
+ ): { command: string; env: Record<string, string>; currentAgent: Agent } {
88
+ const currentAgent: Agent = {
89
+ ...agent,
90
+ commands: {
91
+ ...agent.commands,
92
+ ...(fallback?.commands || {}),
93
+ },
94
+ env: {
95
+ ...agent.env,
96
+ ...(fallback?.env || {}),
97
+ },
98
+ };
99
+
100
+ let command = currentAgent.commands?.new ?? '';
101
+ const env = {
102
+ ...process.env,
103
+ CLAW_CLI_MESSAGE: message,
104
+ } as Record<string, string>;
105
+
106
+ applyEnvOverrides(env, currentAgent.env);
107
+
108
+ if (!isNewSession && currentAgent.commands?.append) {
109
+ command = currentAgent.commands.append;
110
+ applyEnvOverrides(env, agentSessionSettings?.env);
111
+ }
112
+
113
+ return { command, env, currentAgent };
114
+ }
115
+
116
+ async function runExtractionCommand(
117
+ name: string,
118
+ command: string,
119
+ runCommand: RunCommandFn,
120
+ cwd: string,
121
+ env: Record<string, string>,
122
+ mainResult: RunCommandResult,
123
+ signal?: AbortSignal
124
+ ): Promise<{ result?: string; error?: string }> {
125
+ try {
126
+ console.log(`Executing extraction command (${name}): ${command}`);
127
+ const res = await runCommand({
128
+ command,
129
+ cwd,
130
+ env,
131
+ stdin: mainResult.stdout,
132
+ signal,
133
+ });
134
+ if (res.exitCode === 0) {
135
+ return { result: res.stdout.trim() };
136
+ } else {
137
+ return { error: `${name} failed: ${res.stderr}` };
138
+ }
139
+ } catch (e) {
140
+ return { error: `${name} error: ${(e as Error).message}` };
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Formats the environment prefix string by replacing placeholders with actual values.
146
+ * Available placeholders:
147
+ * - {WORKSPACE_DIR}: The root directory of the workspace.
148
+ * - {AGENT_DIR}: The directory where the agent is executing.
149
+ * - {ENV_DIR}: The directory of the active environment.
150
+ * - {HOME_DIR}: The home directory of the current user.
151
+ * - {ENV_ARGS}: The formatted environment arguments based on envFormat.
152
+ */
153
+ function formatEnvironmentPrefix(
154
+ prefix: string,
155
+ replacements: {
156
+ targetPath: string;
157
+ executionCwd: string;
158
+ envDir: string;
159
+ envArgs: string;
160
+ }
161
+ ): string {
162
+ const map: Record<string, string> = {
163
+ '{WORKSPACE_DIR}': replacements.targetPath,
164
+ '{AGENT_DIR}': replacements.executionCwd,
165
+ '{ENV_DIR}': replacements.envDir,
166
+ '{HOME_DIR}': process.env.HOME || '',
167
+ '{ENV_ARGS}': replacements.envArgs,
168
+ };
169
+ return prefix.replace(
170
+ /{(WORKSPACE_DIR|AGENT_DIR|ENV_DIR|HOME_DIR|ENV_ARGS)}/g,
171
+ (match) => map[match] || match
172
+ );
173
+ }
174
+
175
+ export async function executeDirectMessage(
176
+ chatId: string,
177
+ state: RouterState,
178
+ settings: Settings | undefined,
179
+ cwd: string,
180
+ runCommand: RunCommandFn,
181
+ noWait: boolean = false,
182
+ userMessageContent?: string
183
+ ) {
184
+ const userMsg: UserMessage = {
185
+ id: state.messageId ?? crypto.randomUUID(),
186
+ role: 'user',
187
+ content: userMessageContent ?? state.message,
188
+ timestamp: new Date().toISOString(),
189
+ };
190
+ await appendMessage(chatId, userMsg);
191
+
192
+ if (state.reply) {
193
+ const routerLogMsg: CommandLogMessage = {
194
+ id: crypto.randomUUID(),
195
+ messageId: userMsg.id,
196
+ role: 'log',
197
+ source: 'router',
198
+ content: state.reply,
199
+ stderr: '',
200
+ timestamp: new Date().toISOString(),
201
+ command: 'router',
202
+ cwd,
203
+ exitCode: 0,
204
+ ...(state.reply.includes('NO_REPLY_NECESSARY') ? { level: 'verbose' as const } : {}),
205
+ };
206
+ await appendMessage(chatId, routerLogMsg);
207
+ }
208
+
209
+ if (!state.message.trim() && state.action !== 'stop' && state.action !== 'interrupt') {
210
+ return;
211
+ }
212
+
213
+ const queue = getQueue(cwd);
214
+
215
+ if (state.action === 'stop') {
216
+ queue.abortCurrent();
217
+ queue.clear();
218
+ return;
219
+ }
220
+
221
+ if (state.action === 'interrupt') {
222
+ const currentPayload = queue.getCurrentPayload();
223
+ queue.abortCurrent();
224
+
225
+ const extracted = queue.extractPending();
226
+ const payloads = currentPayload ? [currentPayload, ...extracted] : extracted;
227
+
228
+ if (payloads.length > 0) {
229
+ const pendingText = payloads.map((text) => `<message>\n${text}\n</message>`).join('\n\n');
230
+ state.message = `${pendingText}\n\n<message>\n${state.message}\n</message>`.trim();
231
+ }
232
+ }
233
+
234
+ if (!state.message.trim()) {
235
+ return;
236
+ }
237
+
238
+ const routerEnv = state.env ?? {};
239
+
240
+ const taskPromise = queue.enqueue(async (signal) => {
241
+ const {
242
+ agentId,
243
+ agentSessionSettings,
244
+ isNewSession,
245
+ targetSessionId: finalSessionId,
246
+ } = await resolveSessionState(chatId, cwd, state.sessionId, state.agentId);
247
+
248
+ let mergedAgent: Agent = settings?.defaultAgent || {};
249
+ if (agentId !== 'default') {
250
+ try {
251
+ const customAgent = await getAgent(agentId, cwd);
252
+ if (customAgent) {
253
+ mergedAgent = {
254
+ ...mergedAgent,
255
+ ...customAgent,
256
+ commands: { ...mergedAgent.commands, ...customAgent.commands },
257
+ env: { ...mergedAgent.env, ...customAgent.env },
258
+ };
259
+ }
260
+ } catch {
261
+ // Fall back to default if agent not found
262
+ }
263
+ }
264
+
265
+ const fallbacks = mergedAgent.fallbacks || [];
266
+ const executionConfigs: { fallback?: Fallback; retries: number; delayMs: number }[] = [
267
+ { retries: 0, delayMs: 1000 },
268
+ ...fallbacks.map((f) => ({ fallback: f, retries: f.retries, delayMs: f.delayMs })),
269
+ ];
270
+
271
+ const workspaceRoot = getWorkspaceRoot(cwd);
272
+ let executionCwd = cwd;
273
+ if (mergedAgent.directory) {
274
+ executionCwd = path.resolve(workspaceRoot, mergedAgent.directory);
275
+ } else if (agentId !== 'default') {
276
+ executionCwd = path.resolve(workspaceRoot, agentId);
277
+ }
278
+
279
+ let lastLogMsg: CommandLogMessage | undefined;
280
+ let success = false;
281
+
282
+ for (let configIdx = 0; configIdx < executionConfigs.length; configIdx++) {
283
+ const config = executionConfigs[configIdx]!;
284
+ const isFallbackConfig = configIdx > 0;
285
+ for (let attempt = 0; attempt <= config.retries; attempt++) {
286
+ const delay = calculateDelay(attempt, config.delayMs, isFallbackConfig);
287
+ if (delay > 0) {
288
+ const retryLogMsg: CommandLogMessage = {
289
+ id: crypto.randomUUID(),
290
+ messageId: userMsg.id,
291
+ role: 'log',
292
+ content: `Error running agent, retrying in ${Math.round(delay / 1000)} seconds...`,
293
+ stderr: '',
294
+ timestamp: new Date().toISOString(),
295
+ command: 'retry-delay',
296
+ cwd: executionCwd,
297
+ exitCode: 0,
298
+ };
299
+ await appendMessage(chatId, retryLogMsg);
300
+ await sleep(delay);
301
+ }
302
+
303
+ const {
304
+ env,
305
+ currentAgent,
306
+ command: initialCommand,
307
+ } = prepareCommandAndEnv(
308
+ mergedAgent,
309
+ state.message,
310
+ isNewSession,
311
+ agentSessionSettings,
312
+ config.fallback
313
+ );
314
+ let command = initialCommand;
315
+
316
+ if (!command) {
317
+ continue;
318
+ }
319
+
320
+ const agentSpecificEnv = getActiveEnvKeys(
321
+ currentAgent.env,
322
+ !isNewSession ? agentSessionSettings?.env : undefined
323
+ );
324
+ agentSpecificEnv.add('CLAW_CLI_MESSAGE');
325
+
326
+ Object.assign(env, routerEnv);
327
+ Object.keys(routerEnv).forEach((k) => agentSpecificEnv.add(k));
328
+
329
+ const apiCtx = getApiContext(settings);
330
+ if (apiCtx) {
331
+ const proxyUrl = apiCtx.proxy_host
332
+ ? `${apiCtx.proxy_host}:${apiCtx.port}`
333
+ : `http://${apiCtx.host}:${apiCtx.port}`;
334
+ env['CLAW_API_URL'] = proxyUrl;
335
+ agentSpecificEnv.add('CLAW_API_URL');
336
+
337
+ const token = generateToken({
338
+ chatId,
339
+ agentId,
340
+ sessionId: finalSessionId,
341
+ timestamp: Date.now(),
342
+ });
343
+ env['CLAW_API_TOKEN'] = token;
344
+ agentSpecificEnv.add('CLAW_API_TOKEN');
345
+ }
346
+
347
+ const activeEnvInfo = await getActiveEnvironmentInfo(executionCwd, cwd);
348
+ if (activeEnvInfo) {
349
+ const activeEnvName = activeEnvInfo.name;
350
+ const activeEnv = await readEnvironment(activeEnvName, cwd);
351
+
352
+ if (activeEnv?.env) {
353
+ for (const [key, value] of Object.entries(activeEnv.env)) {
354
+ if (value === false) {
355
+ delete env[key];
356
+ agentSpecificEnv.delete(key);
357
+ } else {
358
+ let interpolatedValue = String(value);
359
+ interpolatedValue = interpolatedValue.replace(/\{PATH\}/g, process.env.PATH || '');
360
+ interpolatedValue = interpolatedValue.replace(
361
+ /\{ENV_DIR\}/g,
362
+ getEnvironmentPath(activeEnvName, cwd)
363
+ );
364
+ interpolatedValue = interpolatedValue.replace(
365
+ /\{WORKSPACE_DIR\}/g,
366
+ activeEnvInfo.targetPath
367
+ );
368
+ env[key] = interpolatedValue;
369
+ agentSpecificEnv.add(key);
370
+ }
371
+ }
372
+ }
373
+
374
+ if (activeEnv?.prefix) {
375
+ const envArgs = Array.from(agentSpecificEnv)
376
+ .map((key) => {
377
+ if (activeEnv.envFormat) {
378
+ return activeEnv.envFormat.replace('{key}', key);
379
+ }
380
+ return key;
381
+ })
382
+ .join(' ');
383
+
384
+ const prefixReplaced = formatEnvironmentPrefix(activeEnv.prefix, {
385
+ targetPath: activeEnvInfo.targetPath,
386
+ executionCwd,
387
+ envDir: getEnvironmentPath(activeEnvName, cwd),
388
+ envArgs,
389
+ });
390
+
391
+ if (prefixReplaced.includes('{COMMAND}')) {
392
+ command = prefixReplaced.replace('{COMMAND}', command);
393
+ } else {
394
+ command = `${prefixReplaced} ${command}`;
395
+ }
396
+ }
397
+ }
398
+
399
+ console.log(`Executing command: ${command}`);
400
+ let mainResult;
401
+ const typingInterval = setInterval(() => {
402
+ emitTyping(chatId);
403
+ }, 5000);
404
+ try {
405
+ mainResult = await runCommand({ command, cwd: executionCwd, env, signal });
406
+ } finally {
407
+ clearInterval(typingInterval);
408
+ }
409
+
410
+ const logMsg: CommandLogMessage = {
411
+ id: crypto.randomUUID(),
412
+ messageId: userMsg.id,
413
+ role: 'log',
414
+ content: mainResult.stdout,
415
+ stdout: mainResult.stdout,
416
+ stderr: '',
417
+ timestamp: new Date().toISOString(),
418
+ command,
419
+ cwd: executionCwd,
420
+ exitCode: mainResult.exitCode,
421
+ ...(mainResult.stdout.includes('NO_REPLY_NECESSARY')
422
+ ? { level: 'verbose' as const }
423
+ : {}),
424
+ };
425
+
426
+ const errors: string[] = [];
427
+ if (mainResult.stderr) {
428
+ errors.push(mainResult.stderr);
429
+ }
430
+
431
+ let currentSuccess = mainResult.exitCode === 0;
432
+
433
+ if (currentSuccess) {
434
+ if (currentAgent.commands?.getMessageContent) {
435
+ const { result, error } = await runExtractionCommand(
436
+ 'getMessageContent',
437
+ currentAgent.commands.getMessageContent,
438
+ runCommand,
439
+ executionCwd,
440
+ env,
441
+ mainResult,
442
+ signal
443
+ );
444
+ if (result !== undefined) {
445
+ logMsg.content = result;
446
+ logMsg.stdout = mainResult.stdout;
447
+ if (result.includes('NO_REPLY_NECESSARY')) {
448
+ logMsg.level = 'verbose';
449
+ } else {
450
+ delete logMsg.level;
451
+ }
452
+ if (result.trim() === '') {
453
+ currentSuccess = false;
454
+ }
455
+ }
456
+ if (error) {
457
+ errors.push(error);
458
+ }
459
+ }
460
+ }
461
+
462
+ logMsg.stderr = errors.join('\n\n');
463
+ lastLogMsg = logMsg;
464
+
465
+ if (currentSuccess) {
466
+ success = true;
467
+ if (isNewSession && currentAgent.commands?.getSessionId) {
468
+ const { result, error } = await runExtractionCommand(
469
+ 'getSessionId',
470
+ currentAgent.commands.getSessionId,
471
+ runCommand,
472
+ executionCwd,
473
+ env,
474
+ mainResult,
475
+ signal
476
+ );
477
+ if (result) {
478
+ await writeAgentSessionSettings(
479
+ agentId,
480
+ finalSessionId,
481
+ { env: { SESSION_ID: result } },
482
+ cwd
483
+ );
484
+ }
485
+ if (error) {
486
+ // We don't fail the whole thing for getSessionId error, but we log it.
487
+ logMsg.stderr = [logMsg.stderr, error].filter(Boolean).join('\n\n');
488
+ }
489
+ }
490
+ break;
491
+ }
492
+ }
493
+ if (success) break;
494
+ }
495
+
496
+ if (lastLogMsg) {
497
+ await appendMessage(chatId, lastLogMsg);
498
+ }
499
+ }, state.message);
500
+
501
+ if (!noWait) {
502
+ await taskPromise;
503
+ } else {
504
+ taskPromise.catch((err) => {
505
+ if (err.name !== 'AbortError') {
506
+ console.error('Task execution error:', err);
507
+ }
508
+ });
509
+ }
510
+ }
511
+
512
+ export async function getInitialRouterState(
513
+ chatId: string,
514
+ message: string,
515
+ cwd: string = process.cwd(),
516
+ overrideAgentId?: string,
517
+ overrideSessionId?: string,
518
+ overrideMessageId?: string
519
+ ): Promise<RouterState> {
520
+ const chatSettings = (await readChatSettings(chatId, cwd)) ?? {};
521
+ const agentId = overrideAgentId ?? chatSettings.defaultAgent ?? 'default';
522
+ const sessionId = overrideSessionId ?? chatSettings.sessions?.[agentId] ?? 'default';
523
+ const messageId = overrideMessageId ?? crypto.randomUUID();
524
+
525
+ return {
526
+ messageId,
527
+ message,
528
+ chatId,
529
+ agentId,
530
+ sessionId,
531
+ env: {},
532
+ };
533
+ }
534
+
535
+ export async function handleUserMessage(
536
+ chatId: string,
537
+ message: string,
538
+ settings: Settings | undefined,
539
+ cwd: string = process.cwd(),
540
+ noWait: boolean = false,
541
+ runCommand: RunCommandFn,
542
+ sessionId?: string,
543
+ overrideAgentId?: string
544
+ ): Promise<void> {
545
+ const chatSettings = (await readChatSettings(chatId, cwd)) ?? {};
546
+
547
+ if (overrideAgentId && chatSettings.defaultAgent !== overrideAgentId) {
548
+ chatSettings.defaultAgent = overrideAgentId;
549
+ await writeChatSettings(chatId, chatSettings, cwd);
550
+ }
551
+
552
+ const initialState = await getInitialRouterState(
553
+ chatId,
554
+ message,
555
+ cwd,
556
+ overrideAgentId,
557
+ sessionId
558
+ );
559
+ const initialAgent = initialState.agentId;
560
+
561
+ const routers = chatSettings.routers ?? settings?.routers ?? [];
562
+
563
+ const finalState = await executeRouterPipeline(initialState, routers);
564
+
565
+ const finalMessage = finalState.message;
566
+ const finalAgentId = finalState.agentId;
567
+ const finalSessionId = finalState.sessionId ?? crypto.randomUUID();
568
+ const routerEnv = finalState.env ?? {};
569
+
570
+ const currentAgentId = finalAgentId ?? chatSettings.defaultAgent ?? 'default';
571
+
572
+ let settingsChanged = false;
573
+ if (finalAgentId && finalAgentId !== initialAgent) {
574
+ chatSettings.defaultAgent = finalAgentId;
575
+ settingsChanged = true;
576
+ }
577
+
578
+ if (finalSessionId && chatSettings.sessions?.[currentAgentId] !== finalSessionId) {
579
+ chatSettings.sessions = chatSettings.sessions || {};
580
+ chatSettings.sessions[currentAgentId] = finalSessionId;
581
+ settingsChanged = true;
582
+ }
583
+
584
+ if (settingsChanged) {
585
+ await writeChatSettings(chatId, chatSettings, cwd);
586
+ }
587
+
588
+ const directState: RouterState = {
589
+ messageId: finalState.messageId,
590
+ message: finalMessage,
591
+ chatId,
592
+ env: routerEnv,
593
+ };
594
+ if (finalAgentId !== undefined) directState.agentId = finalAgentId;
595
+ if (finalSessionId !== undefined) directState.sessionId = finalSessionId;
596
+ if (finalState.reply !== undefined) directState.reply = finalState.reply;
597
+ if (finalState.action !== undefined) directState.action = finalState.action;
598
+
599
+ await executeDirectMessage(chatId, directState, settings, cwd, runCommand, noWait, message);
600
+ }
@@ -0,0 +1,118 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { appRouter } from './router.js';
3
+ import { daemonEvents, DAEMON_EVENT_MESSAGE_APPENDED } from './events.js';
4
+ import * as daemonChats from './chats.js';
5
+
6
+ vi.mock('./chats.js', async (importOriginal) => {
7
+ const actual = await importOriginal<typeof import('./chats.js')>();
8
+ return {
9
+ ...actual,
10
+ getMessages: vi.fn(),
11
+ getDefaultChatId: vi.fn(),
12
+ };
13
+ });
14
+
15
+ describe('Daemon Message Observation', () => {
16
+ beforeEach(() => {
17
+ vi.clearAllMocks();
18
+ daemonEvents.removeAllListeners();
19
+ });
20
+
21
+ it('getMessages should return messages from chats module', async () => {
22
+ const mockMessages = [{ id: '1', role: 'user', content: 'hello', timestamp: '...' }];
23
+ vi.mocked(daemonChats.getMessages).mockResolvedValue(
24
+ mockMessages as unknown as import('./chats.js').ChatMessage[]
25
+ );
26
+ vi.mocked(daemonChats.getDefaultChatId).mockResolvedValue('chat-1');
27
+
28
+ const caller = appRouter.createCaller({});
29
+ const result = await caller.getMessages({ chatId: 'chat-1', limit: 10 });
30
+
31
+ expect(result).toEqual(mockMessages);
32
+ expect(daemonChats.getMessages).toHaveBeenCalledWith('chat-1', 10);
33
+ });
34
+
35
+ it('waitForMessages should return new messages immediately if they exist after lastMessageId', async () => {
36
+ const mockMessages = [
37
+ { id: '1', role: 'user', content: 'hello', timestamp: '...' },
38
+ { id: '2', role: 'log', content: 'hi', timestamp: '...' },
39
+ { id: '3', role: 'user', content: 'how are you?', timestamp: '...' },
40
+ ];
41
+ vi.mocked(daemonChats.getMessages).mockResolvedValue(
42
+ mockMessages as unknown as import('./chats.js').ChatMessage[]
43
+ );
44
+ vi.mocked(daemonChats.getDefaultChatId).mockResolvedValue('chat-1');
45
+
46
+ const caller = appRouter.createCaller({});
47
+ const iterable = await caller.waitForMessages({ chatId: 'chat-1', lastMessageId: '1' });
48
+ const iterator = iterable[Symbol.asyncIterator]();
49
+ const result = (await iterator.next()).value;
50
+
51
+ expect(result).toHaveLength(2);
52
+ expect(result![0]!.id).toBe('2');
53
+ expect(result![1]!.id).toBe('3');
54
+ });
55
+
56
+ it('waitForMessages should wait for a new message if none are available after lastMessageId', async () => {
57
+ const mockMessages = [{ id: '1', role: 'user', content: 'hello', timestamp: '...' }];
58
+ vi.mocked(daemonChats.getMessages).mockResolvedValue(
59
+ mockMessages as unknown as import('./chats.js').ChatMessage[]
60
+ );
61
+ vi.mocked(daemonChats.getDefaultChatId).mockResolvedValue('chat-1');
62
+
63
+ const caller = appRouter.createCaller({});
64
+
65
+ const iterable = await caller.waitForMessages({ chatId: 'chat-1', lastMessageId: '1' });
66
+ const iterator = iterable[Symbol.asyncIterator]();
67
+
68
+ const waitPromise = iterator.next();
69
+
70
+ const newMessage = { id: '2', role: 'log', content: 'hi', timestamp: '...' };
71
+
72
+ // Simulate message arrival
73
+ setTimeout(() => {
74
+ daemonEvents.emit(DAEMON_EVENT_MESSAGE_APPENDED, { chatId: 'chat-1', message: newMessage });
75
+ }, 10);
76
+
77
+ const result = await waitPromise;
78
+ expect(result.value).toHaveLength(1);
79
+ expect(result.value![0]!.id).toBe('2');
80
+ });
81
+
82
+ it('waitForMessages should ignore messages for other chats while waiting', async () => {
83
+ vi.mocked(daemonChats.getMessages).mockResolvedValue([]);
84
+ vi.mocked(daemonChats.getDefaultChatId).mockResolvedValue('chat-1');
85
+
86
+ const caller = appRouter.createCaller({});
87
+
88
+ const iterable = await caller.waitForMessages({ chatId: 'chat-1' });
89
+ const iterator = iterable[Symbol.asyncIterator]();
90
+
91
+ // Try to get next value
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ let yieldedValue: any = null;
94
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
+ iterator.next().then((res: any) => (yieldedValue = res.value));
96
+
97
+ // Simulate message for another chat
98
+ daemonEvents.emit(DAEMON_EVENT_MESSAGE_APPENDED, {
99
+ chatId: 'other-chat',
100
+ message: { id: 'x', role: 'user', content: 'wrong', timestamp: '...' },
101
+ });
102
+
103
+ // Wait a tick
104
+ await new Promise((resolve) => setTimeout(resolve, 10));
105
+
106
+ expect(yieldedValue).toBeNull(); // Should still be waiting
107
+
108
+ // Now simulate the correct chat
109
+ daemonEvents.emit(DAEMON_EVENT_MESSAGE_APPENDED, {
110
+ chatId: 'chat-1',
111
+ message: { id: 'y', role: 'user', content: 'right', timestamp: '...' },
112
+ });
113
+
114
+ await new Promise((resolve) => setTimeout(resolve, 10));
115
+ expect(yieldedValue).toHaveLength(1);
116
+ expect(yieldedValue![0]!.id).toBe('y');
117
+ });
118
+ });