clawmini 0.0.8 → 0.0.9

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 (367) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +14 -0
  3. package/.github/workflows/release.yml +49 -0
  4. package/CHANGELOG.md +36 -0
  5. package/README.md +5 -4
  6. package/dist/adapter-discord/index.d.mts.map +1 -1
  7. package/dist/adapter-discord/index.mjs +465 -282
  8. package/dist/adapter-discord/index.mjs.map +1 -1
  9. package/dist/adapter-google-chat/index.mjs +367 -243
  10. package/dist/adapter-google-chat/index.mjs.map +1 -1
  11. package/dist/cli/index.mjs +684 -24
  12. package/dist/cli/index.mjs.map +1 -1
  13. package/dist/cli/lite.mjs +43 -13
  14. package/dist/cli/lite.mjs.map +1 -1
  15. package/dist/cli/{propose-policy.mjs → manage-policies.mjs} +270 -47
  16. package/dist/cli/manage-policies.mjs.map +1 -0
  17. package/dist/cli/run-host.d.mts +1 -0
  18. package/dist/cli/run-host.mjs +3090 -0
  19. package/dist/cli/run-host.mjs.map +1 -0
  20. package/dist/config-CPFQIGdG.mjs +57 -0
  21. package/dist/config-CPFQIGdG.mjs.map +1 -0
  22. package/dist/config-Dvl-Pov4.mjs +76 -0
  23. package/dist/config-Dvl-Pov4.mjs.map +1 -0
  24. package/dist/daemon/index.d.mts.map +1 -1
  25. package/dist/daemon/index.mjs +970 -332
  26. package/dist/daemon/index.mjs.map +1 -1
  27. package/dist/supervisor-actions-CiW56eLi.mjs +843 -0
  28. package/dist/supervisor-actions-CiW56eLi.mjs.map +1 -0
  29. package/dist/turn-log-buffer-DRgW53gl.mjs +767 -0
  30. package/dist/turn-log-buffer-DRgW53gl.mjs.map +1 -0
  31. package/dist/web/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
  32. package/dist/web/_app/immutable/chunks/BhRSsUCh.js +2 -0
  33. package/dist/web/_app/immutable/chunks/BiLeM2i1.js +1 -0
  34. package/{web/.svelte-kit/output/client/_app/immutable/chunks/CME08kGM.js → dist/web/_app/immutable/chunks/BmBj85Ll.js} +1 -1
  35. package/dist/web/_app/immutable/chunks/BrERcKAH.js +1 -0
  36. package/dist/web/_app/immutable/chunks/Bv9252RM.js +1 -0
  37. package/dist/web/_app/immutable/chunks/CIXNBPKi.js +1 -0
  38. package/dist/web/_app/immutable/chunks/DISKL3GN.js +2 -0
  39. package/dist/web/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
  40. package/dist/web/_app/immutable/chunks/DnQ3vS13.js +1 -0
  41. package/dist/web/_app/immutable/chunks/KsloHTKS.js +1 -0
  42. package/{web/.svelte-kit/output/client/_app/immutable/chunks/Ck-be5J2.js → dist/web/_app/immutable/chunks/RsHsUj-8.js} +2 -2
  43. package/dist/web/_app/immutable/chunks/{vDehDcuJ.js → wpfV79dV.js} +1 -1
  44. package/dist/web/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
  45. package/dist/web/_app/immutable/entry/start.Di0-Jhte.js +1 -0
  46. package/dist/web/_app/immutable/nodes/{0.CUGC2p-K.js → 0.DYyUA1au.js} +1 -1
  47. package/dist/web/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
  48. package/dist/web/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
  49. package/{web/.svelte-kit/output/client/_app/immutable/nodes/3.0arZe_Uf.js → dist/web/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
  50. package/dist/web/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
  51. package/dist/web/_app/immutable/nodes/{5.Bq2JzCEj.js → 5.DLbPVJY2.js} +1 -1
  52. package/dist/web/_app/version.json +1 -1
  53. package/dist/web/index.html +12 -12
  54. package/dist/workspace-oWmVh5mi.mjs +1001 -0
  55. package/dist/workspace-oWmVh5mi.mjs.map +1 -0
  56. package/docs/23_adapter_slash_autocomplete/development_log.md +19 -0
  57. package/docs/23_adapter_slash_autocomplete/notes.md +18 -0
  58. package/docs/23_adapter_slash_autocomplete/prd.md +46 -0
  59. package/docs/23_adapter_slash_autocomplete/questions.md +6 -0
  60. package/docs/23_adapter_slash_autocomplete/tickets.md +21 -0
  61. package/docs/24_subagent_job_policy_fixes/development_log.md +22 -0
  62. package/docs/24_subagent_job_policy_fixes/notes.md +28 -0
  63. package/docs/24_subagent_job_policy_fixes/prd.md +59 -0
  64. package/docs/24_subagent_job_policy_fixes/questions.md +3 -0
  65. package/docs/24_subagent_job_policy_fixes/tickets.md +49 -0
  66. package/docs/25_e2e_test_improvements/development_log.md +30 -0
  67. package/docs/25_e2e_test_improvements/notes.md +29 -0
  68. package/docs/25_e2e_test_improvements/prd.md +43 -0
  69. package/docs/25_e2e_test_improvements/questions.md +12 -0
  70. package/docs/25_e2e_test_improvements/tickets-2.md +22 -0
  71. package/docs/25_e2e_test_improvements/tickets.md +22 -0
  72. package/docs/25_policy_cwd/development_log.md +30 -0
  73. package/docs/25_policy_cwd/notes.md +28 -0
  74. package/docs/25_policy_cwd/prd.md +77 -0
  75. package/docs/25_policy_cwd/questions.md +6 -0
  76. package/docs/25_policy_cwd/tickets.md +77 -0
  77. package/docs/CLI_REFERENCE.md +3 -1
  78. package/docs/PHILOSOPHY.md +35 -0
  79. package/docs/adapter-visibility/SPEC.md +461 -0
  80. package/docs/adapter-visibility/SPEC_v2.md +202 -0
  81. package/docs/auto-update/SPEC.md +344 -0
  82. package/docs/backups/SPEC.md +296 -0
  83. package/docs/backups/clawmini.gitignore +69 -0
  84. package/docs/guides/assets/clawmini-avatar.png +0 -0
  85. package/docs/guides/backups.md +332 -0
  86. package/docs/guides/discord_adapter_setup.md +1 -1
  87. package/docs/guides/google_chat_adapter_setup.md +81 -0
  88. package/docs/unified-startup/SPEC.md +203 -0
  89. package/e2e/_helpers/test-environment.test.ts +49 -0
  90. package/e2e/_helpers/test-environment.ts +548 -0
  91. package/e2e/adapters/_google-chat-fixtures.ts +340 -0
  92. package/{src/cli/e2e → e2e/adapters}/adapter-discord.test.ts +22 -23
  93. package/e2e/adapters/adapter-google-chat-downtime.test.ts +157 -0
  94. package/e2e/adapters/adapter-google-chat-inbound.test.ts +697 -0
  95. package/e2e/adapters/adapter-google-chat-outbound.test.ts +297 -0
  96. package/e2e/adapters/adapter-google-chat-roundtrip.test.ts +56 -0
  97. package/e2e/adapters/adapter-google-chat-threads.test.ts +1078 -0
  98. package/e2e/agents/custom-api-env.test.ts +80 -0
  99. package/e2e/agents/export-lite-func.test.ts +104 -0
  100. package/e2e/agents/fallbacks.test.ts +124 -0
  101. package/e2e/agents/interrupt.test.ts +50 -0
  102. package/e2e/agents/no-reply-necessary.test.ts +57 -0
  103. package/e2e/agents/session-timeout-subagents.test.ts +76 -0
  104. package/e2e/agents/subagent-authorization.test.ts +246 -0
  105. package/e2e/agents/subagent-env.test.ts +49 -0
  106. package/e2e/agents/subagent-lifecycle.test.ts +782 -0
  107. package/e2e/agents/subagents-depth.test.ts +47 -0
  108. package/e2e/cli/agents.test.ts +176 -0
  109. package/e2e/cli/auto-update.test.ts +741 -0
  110. package/e2e/cli/basic.test.ts +44 -0
  111. package/{src/cli/e2e → e2e/cli}/export-lite.test.ts +16 -12
  112. package/e2e/cli/init-gitignore.test.ts +86 -0
  113. package/e2e/cli/init.test.ts +76 -0
  114. package/e2e/cli/messages.test.ts +363 -0
  115. package/e2e/cli/serve.test.ts +76 -0
  116. package/{src/cli/e2e → e2e/cli}/skills.test.ts +11 -10
  117. package/{src/cli/e2e → e2e/daemon}/daemon.test.ts +57 -195
  118. package/e2e/jobs/agent-jobs.test.ts +216 -0
  119. package/e2e/jobs/cron.test.ts +64 -0
  120. package/e2e/jobs/restart.test.ts +108 -0
  121. package/e2e/policies/approval-session.test.ts +69 -0
  122. package/e2e/policies/auto-create-policies-file.test.ts +35 -0
  123. package/e2e/policies/builtin-manage-policies.test.ts +184 -0
  124. package/e2e/policies/builtin-run-host.test.ts +180 -0
  125. package/e2e/policies/environment-policies.test.ts +177 -0
  126. package/e2e/policies/manage-policies.test.ts +566 -0
  127. package/e2e/policies/output-size.test.ts +98 -0
  128. package/e2e/policies/policies-context-cwd.test.ts +160 -0
  129. package/e2e/policies/relative-script-path.test.ts +60 -0
  130. package/e2e/policies/requests-show.test.ts +135 -0
  131. package/e2e/policies/requests.test.ts +208 -0
  132. package/e2e/policies/slash-policies.test.ts +308 -0
  133. package/e2e/policies/startup-cleanup.test.ts +48 -0
  134. package/e2e/routers/session-timeout.test.ts +106 -0
  135. package/e2e/routers/slash-model.test.ts +152 -0
  136. package/e2e/routers/slash-new.test.ts +50 -0
  137. package/e2e/routers/slash-restart-adapter.test.ts +96 -0
  138. package/e2e/routers/slash-restart.test.ts +114 -0
  139. package/e2e/routers/slash-shutdown.test.ts +55 -0
  140. package/e2e/routers/slash-stop.test.ts +232 -0
  141. package/e2e/routers/slash-upgrade.test.ts +88 -0
  142. package/{src/cli/e2e → e2e/sandbox}/environments.test.ts +14 -13
  143. package/eslint.config.js +6 -0
  144. package/napkin.md +1 -1
  145. package/package.json +8 -3
  146. package/src/adapter-discord/commands.test.ts +42 -0
  147. package/src/adapter-discord/commands.ts +33 -0
  148. package/src/adapter-discord/config.ts +12 -0
  149. package/src/adapter-discord/forwarder.test.ts +499 -21
  150. package/src/adapter-discord/forwarder.ts +343 -124
  151. package/src/adapter-discord/inbound-cache.test.ts +47 -0
  152. package/src/adapter-discord/inbound-cache.ts +37 -0
  153. package/src/adapter-discord/index.test.ts +67 -2
  154. package/src/adapter-discord/index.ts +84 -216
  155. package/src/adapter-discord/interactions.test.ts +54 -3
  156. package/src/adapter-discord/interactions.ts +97 -53
  157. package/src/adapter-discord/processMessage.ts +239 -0
  158. package/src/adapter-discord/state.ts +1 -0
  159. package/src/adapter-google-chat/auth.test.ts +9 -5
  160. package/src/adapter-google-chat/auth.ts +29 -23
  161. package/src/adapter-google-chat/cards.ts +7 -2
  162. package/src/adapter-google-chat/client.test.ts +37 -2
  163. package/src/adapter-google-chat/client.ts +138 -38
  164. package/src/adapter-google-chat/config.ts +19 -0
  165. package/src/adapter-google-chat/forwarder.test.ts +81 -56
  166. package/src/adapter-google-chat/forwarder.ts +394 -185
  167. package/src/adapter-google-chat/inbound-cache.test.ts +61 -0
  168. package/src/adapter-google-chat/inbound-cache.ts +36 -0
  169. package/src/adapter-google-chat/state.test.ts +1 -0
  170. package/src/adapter-google-chat/state.ts +9 -1
  171. package/src/adapter-google-chat/subscriptions.ts +8 -6
  172. package/src/cli/builtin-policies.ts +44 -0
  173. package/src/cli/commands/agents.ts +59 -5
  174. package/src/cli/commands/down.ts +54 -2
  175. package/src/cli/commands/environments.ts +8 -2
  176. package/src/cli/commands/init.ts +31 -0
  177. package/src/cli/commands/logs.ts +116 -0
  178. package/src/cli/commands/policies.ts +6 -4
  179. package/src/cli/commands/serve.test.ts +67 -0
  180. package/src/cli/commands/serve.ts +284 -0
  181. package/src/cli/commands/up.ts +122 -2
  182. package/src/cli/commands/web-api/agents.ts +3 -2
  183. package/src/cli/index.ts +4 -0
  184. package/src/cli/install-detection.test.ts +72 -0
  185. package/src/cli/install-detection.ts +48 -0
  186. package/src/cli/lite.ts +54 -22
  187. package/src/cli/manage-policies-utils.ts +104 -0
  188. package/src/cli/manage-policies.ts +291 -0
  189. package/src/cli/run-host.ts +45 -0
  190. package/src/cli/supervisor-actions.ts +267 -0
  191. package/src/cli/supervisor-control.test.ts +129 -0
  192. package/src/cli/supervisor-control.ts +155 -0
  193. package/src/cli/supervisor-pid.ts +68 -0
  194. package/src/cli/supervisor.ts +277 -0
  195. package/src/daemon/agent/agent-context.ts +11 -11
  196. package/src/daemon/agent/agent-session.ts +8 -1
  197. package/src/daemon/agent/chat-logger.test.ts +78 -9
  198. package/src/daemon/agent/chat-logger.ts +25 -5
  199. package/src/daemon/agent/turn-registry.test.ts +89 -0
  200. package/src/daemon/agent/turn-registry.ts +94 -0
  201. package/src/daemon/agent/types.ts +2 -0
  202. package/src/daemon/api/agent-policy-endpoints.ts +263 -0
  203. package/src/daemon/api/agent-router.ts +47 -126
  204. package/src/daemon/api/index.test.ts +1 -0
  205. package/src/daemon/api/policy-request.test.ts +7 -5
  206. package/src/daemon/api/router-utils.ts +6 -5
  207. package/src/daemon/api/subagent-router.ts +110 -74
  208. package/src/daemon/api/subagent-utils.test.ts +60 -0
  209. package/src/daemon/api/subagent-utils.ts +113 -87
  210. package/src/daemon/api/user-router.ts +34 -8
  211. package/src/daemon/auth.ts +1 -0
  212. package/src/daemon/cron.test.ts +62 -4
  213. package/src/daemon/cron.ts +42 -16
  214. package/src/daemon/events.ts +65 -0
  215. package/src/daemon/index.ts +24 -1
  216. package/src/daemon/message-interruption.test.ts +1 -0
  217. package/src/daemon/message-jobs.test.ts +1 -0
  218. package/src/daemon/message.ts +78 -14
  219. package/src/daemon/observation.test.ts +26 -18
  220. package/src/daemon/pending-replies.test.ts +112 -0
  221. package/src/daemon/pending-replies.ts +162 -0
  222. package/src/daemon/policy-request-service.ts +3 -1
  223. package/src/daemon/policy-utils.test.ts +66 -1
  224. package/src/daemon/policy-utils.ts +126 -1
  225. package/src/daemon/request-store.ts +31 -0
  226. package/src/daemon/routers/session-timeout.ts +4 -0
  227. package/src/daemon/routers/slash-model.test.ts +344 -0
  228. package/src/daemon/routers/slash-model.ts +207 -0
  229. package/src/daemon/routers/slash-policies.test.ts +38 -32
  230. package/src/daemon/routers/slash-policies.ts +84 -33
  231. package/src/daemon/routers/slash-restart.test.ts +69 -0
  232. package/src/daemon/routers/slash-restart.ts +36 -0
  233. package/src/daemon/routers/slash-shutdown.test.ts +50 -0
  234. package/src/daemon/routers/slash-shutdown.ts +28 -0
  235. package/src/daemon/routers/slash-upgrade.test.ts +116 -0
  236. package/src/daemon/routers/slash-upgrade.ts +76 -0
  237. package/src/daemon/routers/types.ts +7 -0
  238. package/src/daemon/routers.ts +16 -0
  239. package/src/shared/adapters/blockquote.test.ts +28 -0
  240. package/src/shared/adapters/blockquote.ts +20 -0
  241. package/src/shared/adapters/filtering.test.ts +224 -10
  242. package/src/shared/adapters/filtering.ts +95 -7
  243. package/src/shared/adapters/inbound-cache.test.ts +48 -0
  244. package/src/shared/adapters/inbound-cache.ts +54 -0
  245. package/src/shared/adapters/turn-log-buffer.ts +266 -0
  246. package/src/shared/adapters/turn-log.test.ts +389 -0
  247. package/src/shared/adapters/turn-log.ts +357 -0
  248. package/src/shared/agent-utils.ts +12 -5
  249. package/src/shared/chats.test.ts +4 -0
  250. package/src/shared/chats.ts +9 -0
  251. package/src/shared/config.ts +16 -1
  252. package/src/shared/lite.ts +76 -2
  253. package/src/shared/policies.ts +26 -0
  254. package/src/shared/template-manifest.ts +267 -0
  255. package/src/shared/utils/shell.ts +61 -0
  256. package/src/shared/version.ts +34 -0
  257. package/src/shared/workspace.test.ts +217 -0
  258. package/src/shared/workspace.ts +626 -48
  259. package/templates/environments/cladding/allowlist-domain.mjs +125 -0
  260. package/templates/environments/cladding/env.json +21 -1
  261. package/templates/environments/cladding/run-with-network.mjs +54 -0
  262. package/templates/environments/macos-proxy/allowlist-domain.mjs +95 -0
  263. package/templates/environments/macos-proxy/env.json +8 -1
  264. package/templates/environments/macos-proxy/proxy.mjs +0 -1
  265. package/templates/gemini/template.json +5 -0
  266. package/templates/gemini-claw/template.json +13 -0
  267. package/templates/skills/clawmini-requests/SKILL.md +69 -10
  268. package/templates/skills/run-host/SKILL.md +51 -0
  269. package/templates/skills/skill-creator/SKILL.md +4 -3
  270. package/templates/skills/skill-creator/scripts/validate.sh +52 -0
  271. package/tsdown.config.ts +10 -1
  272. package/vitest.config.ts +2 -2
  273. package/web/.svelte-kit/ambient.d.ts +292 -118
  274. package/web/.svelte-kit/generated/server/internal.js +1 -1
  275. package/web/.svelte-kit/output/client/.vite/manifest.json +126 -136
  276. package/web/.svelte-kit/output/client/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
  277. package/web/.svelte-kit/output/client/_app/immutable/chunks/BhRSsUCh.js +2 -0
  278. package/web/.svelte-kit/output/client/_app/immutable/chunks/BiLeM2i1.js +1 -0
  279. package/{dist/web/_app/immutable/chunks/CME08kGM.js → web/.svelte-kit/output/client/_app/immutable/chunks/BmBj85Ll.js} +1 -1
  280. package/web/.svelte-kit/output/client/_app/immutable/chunks/BrERcKAH.js +1 -0
  281. package/web/.svelte-kit/output/client/_app/immutable/chunks/Bv9252RM.js +1 -0
  282. package/web/.svelte-kit/output/client/_app/immutable/chunks/CIXNBPKi.js +1 -0
  283. package/web/.svelte-kit/output/client/_app/immutable/chunks/DISKL3GN.js +2 -0
  284. package/web/.svelte-kit/output/client/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
  285. package/web/.svelte-kit/output/client/_app/immutable/chunks/DnQ3vS13.js +1 -0
  286. package/web/.svelte-kit/output/client/_app/immutable/chunks/KsloHTKS.js +1 -0
  287. package/{dist/web/_app/immutable/chunks/Ck-be5J2.js → web/.svelte-kit/output/client/_app/immutable/chunks/RsHsUj-8.js} +2 -2
  288. package/web/.svelte-kit/output/client/_app/immutable/chunks/{vDehDcuJ.js → wpfV79dV.js} +1 -1
  289. package/web/.svelte-kit/output/client/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
  290. package/web/.svelte-kit/output/client/_app/immutable/entry/start.Di0-Jhte.js +1 -0
  291. package/web/.svelte-kit/output/client/_app/immutable/nodes/{0.CUGC2p-K.js → 0.DYyUA1au.js} +1 -1
  292. package/web/.svelte-kit/output/client/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
  293. package/web/.svelte-kit/output/client/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
  294. package/{dist/web/_app/immutable/nodes/3.0arZe_Uf.js → web/.svelte-kit/output/client/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
  295. package/web/.svelte-kit/output/client/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
  296. package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.Bq2JzCEj.js → 5.DLbPVJY2.js} +1 -1
  297. package/web/.svelte-kit/output/client/_app/version.json +1 -1
  298. package/web/.svelte-kit/output/server/.vite/manifest.json +12 -10
  299. package/web/.svelte-kit/output/server/chunks/Icon.js +1 -1
  300. package/web/.svelte-kit/output/server/chunks/client.js +1 -1
  301. package/web/.svelte-kit/output/server/chunks/exports.js +1 -1
  302. package/web/.svelte-kit/output/server/chunks/index-server.js +2 -1
  303. package/web/.svelte-kit/output/server/chunks/internal.js +1 -1
  304. package/web/.svelte-kit/output/server/chunks/render-context.js +77 -0
  305. package/web/.svelte-kit/output/server/chunks/root.js +739 -788
  306. package/web/.svelte-kit/output/server/chunks/shared.js +234 -21
  307. package/web/.svelte-kit/output/server/index.js +126 -90
  308. package/web/.svelte-kit/output/server/manifest-full.js +1 -1
  309. package/web/.svelte-kit/output/server/manifest.js +1 -1
  310. package/web/.svelte-kit/output/server/nodes/0.js +1 -1
  311. package/web/.svelte-kit/output/server/nodes/1.js +1 -1
  312. package/web/.svelte-kit/output/server/nodes/2.js +1 -1
  313. package/web/.svelte-kit/output/server/nodes/3.js +1 -1
  314. package/web/.svelte-kit/output/server/nodes/4.js +1 -1
  315. package/web/.svelte-kit/output/server/nodes/5.js +1 -1
  316. package/web/.svelte-kit/output/server/remote-entry.js +245 -81
  317. package/web/.svelte-kit/tsconfig.json +4 -1
  318. package/dist/cli/propose-policy.mjs.map +0 -1
  319. package/dist/lite-CBxOT1y5.mjs +0 -241
  320. package/dist/lite-CBxOT1y5.mjs.map +0 -1
  321. package/dist/routing-D8rTxtaV.mjs +0 -245
  322. package/dist/routing-D8rTxtaV.mjs.map +0 -1
  323. package/dist/web/_app/immutable/chunks/B6YN0Nuq.js +0 -1
  324. package/dist/web/_app/immutable/chunks/BmRlVmv6.js +0 -1
  325. package/dist/web/_app/immutable/chunks/CK9JZLaG.js +0 -2
  326. package/dist/web/_app/immutable/chunks/Ck3rYNON.js +0 -1
  327. package/dist/web/_app/immutable/chunks/D5iV40bG.js +0 -1
  328. package/dist/web/_app/immutable/chunks/DMtIqaiV.js +0 -2
  329. package/dist/web/_app/immutable/chunks/DhD271EB.js +0 -1
  330. package/dist/web/_app/immutable/chunks/DpuLqk8d.js +0 -1
  331. package/dist/web/_app/immutable/chunks/DsIToJCP.js +0 -1
  332. package/dist/web/_app/immutable/entry/app.BCSV3nrG.js +0 -2
  333. package/dist/web/_app/immutable/entry/start.D4eLEZUM.js +0 -1
  334. package/dist/web/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
  335. package/dist/web/_app/immutable/nodes/4.ClM1bXLE.js +0 -60
  336. package/dist/workspace-BJmJBfKi.mjs +0 -456
  337. package/dist/workspace-BJmJBfKi.mjs.map +0 -1
  338. package/src/cli/e2e/agents.test.ts +0 -140
  339. package/src/cli/e2e/basic.test.ts +0 -43
  340. package/src/cli/e2e/cron.test.ts +0 -132
  341. package/src/cli/e2e/export-lite-func.test.ts +0 -206
  342. package/src/cli/e2e/fallbacks.test.ts +0 -175
  343. package/src/cli/e2e/init.test.ts +0 -77
  344. package/src/cli/e2e/messages.test.ts +0 -332
  345. package/src/cli/e2e/propose-policy.test.ts +0 -203
  346. package/src/cli/e2e/requests.test.ts +0 -180
  347. package/src/cli/e2e/session-timeout.test.ts +0 -192
  348. package/src/cli/e2e/slash-new.test.ts +0 -93
  349. package/src/cli/e2e/subagents.test.ts +0 -106
  350. package/src/cli/e2e/utils.ts +0 -66
  351. package/src/cli/propose-policy.ts +0 -91
  352. package/web/.svelte-kit/output/client/_app/immutable/chunks/B6YN0Nuq.js +0 -1
  353. package/web/.svelte-kit/output/client/_app/immutable/chunks/BmRlVmv6.js +0 -1
  354. package/web/.svelte-kit/output/client/_app/immutable/chunks/CK9JZLaG.js +0 -2
  355. package/web/.svelte-kit/output/client/_app/immutable/chunks/Ck3rYNON.js +0 -1
  356. package/web/.svelte-kit/output/client/_app/immutable/chunks/D5iV40bG.js +0 -1
  357. package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +0 -2
  358. package/web/.svelte-kit/output/client/_app/immutable/chunks/DhD271EB.js +0 -1
  359. package/web/.svelte-kit/output/client/_app/immutable/chunks/DpuLqk8d.js +0 -1
  360. package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +0 -1
  361. package/web/.svelte-kit/output/client/_app/immutable/entry/app.BCSV3nrG.js +0 -2
  362. package/web/.svelte-kit/output/client/_app/immutable/entry/start.D4eLEZUM.js +0 -1
  363. package/web/.svelte-kit/output/client/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
  364. package/web/.svelte-kit/output/client/_app/immutable/nodes/4.ClM1bXLE.js +0 -60
  365. package/web/.svelte-kit/output/server/chunks/false.js +0 -4
  366. /package/dist/cli/{propose-policy.d.mts → manage-policies.d.mts} +0 -0
  367. /package/{src/cli/e2e → e2e/_helpers}/global-setup.ts +0 -0
@@ -0,0 +1,277 @@
1
+ import type { ChildProcess } from 'node:child_process';
2
+ import { spawn } from 'node:child_process';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import readline from 'node:readline';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ import { getSocketPath } from '../shared/workspace.js';
9
+
10
+ export type ServiceName = 'daemon' | 'web' | 'adapter-discord' | 'adapter-google-chat';
11
+
12
+ export const DISPLAY_NAMES: Record<ServiceName, string> = {
13
+ daemon: 'daemon',
14
+ web: 'web',
15
+ 'adapter-discord': 'discord',
16
+ 'adapter-google-chat': 'google-chat',
17
+ };
18
+
19
+ // Adapters and the web UI are mostly stateless — give them a tight window.
20
+ // The daemon runs `down` hooks (e.g. sandbox/container teardown) that can
21
+ // legitimately take tens of seconds, so it needs much longer to drain.
22
+ const ADAPTER_TERMINATE_TIMEOUT_MS = 10_000;
23
+ const DAEMON_TERMINATE_TIMEOUT_MS = 60_000;
24
+
25
+ interface ResolvedCommand {
26
+ command: string;
27
+ args: string[];
28
+ }
29
+
30
+ export function resolveServiceCommand(service: ServiceName): ResolvedCommand {
31
+ const cliPath = fileURLToPath(import.meta.url);
32
+ switch (service) {
33
+ case 'daemon':
34
+ return {
35
+ command: process.execPath,
36
+ args: [new URL('../daemon/index.mjs', import.meta.url).pathname],
37
+ };
38
+ case 'web':
39
+ return { command: process.execPath, args: [cliPath, 'web'] };
40
+ case 'adapter-discord':
41
+ return {
42
+ command: process.execPath,
43
+ args: [new URL('../adapter-discord/index.mjs', import.meta.url).pathname],
44
+ };
45
+ case 'adapter-google-chat':
46
+ return {
47
+ command: process.execPath,
48
+ args: [new URL('../adapter-google-chat/index.mjs', import.meta.url).pathname],
49
+ };
50
+ }
51
+ }
52
+
53
+ export class Supervisor {
54
+ private children = new Map<ServiceName, ChildProcess>();
55
+ private logFds = new Map<ServiceName, number>();
56
+ private shuttingDown = false;
57
+ private restarting = new Set<ServiceName>();
58
+ // Services that have ever been started in this supervisor's lifetime. Used
59
+ // by restartAll() to know what to bring back after a stopAllChildren().
60
+ // We don't remove entries when a service stops — a crash-and-restart of an
61
+ // adapter shouldn't drop it from the "originally enabled" set.
62
+ private enabledServices = new Set<ServiceName>();
63
+ private readonly logDir: string;
64
+
65
+ constructor(logDir: string) {
66
+ this.logDir = logDir;
67
+ if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
68
+ }
69
+
70
+ async startService(service: ServiceName): Promise<void> {
71
+ this.enabledServices.add(service);
72
+ const { command, args } = resolveServiceCommand(service);
73
+ const logPath = path.join(this.logDir, `${service}.log`);
74
+ const logFd = fs.openSync(logPath, 'a');
75
+ this.logFds.set(service, logFd);
76
+
77
+ fs.writeSync(
78
+ logFd,
79
+ `\n--- clawmini serve: ${service} starting at ${new Date().toISOString()} ---\n`
80
+ );
81
+
82
+ const child = spawn(command, args, {
83
+ stdio: ['ignore', 'pipe', 'pipe'],
84
+ env: process.env,
85
+ cwd: process.cwd(),
86
+ });
87
+
88
+ this.children.set(service, child);
89
+ this.attachPipe(service, child.stdout!, logFd, 'stdout');
90
+ this.attachPipe(service, child.stderr!, logFd, 'stderr');
91
+
92
+ child.on('exit', (code, signal) => {
93
+ const msg = `exited code=${code} signal=${signal}`;
94
+ process.stderr.write(`[${DISPLAY_NAMES[service]}] ${msg}\n`);
95
+ try {
96
+ fs.writeSync(logFd, `--- ${msg} at ${new Date().toISOString()} ---\n`);
97
+ fs.closeSync(logFd);
98
+ } catch {
99
+ // best-effort
100
+ }
101
+ this.logFds.delete(service);
102
+ this.children.delete(service);
103
+
104
+ if (service === 'daemon' && !this.shuttingDown && !this.restarting.has('daemon')) {
105
+ process.stderr.write('[supervisor] daemon exited unexpectedly — shutting down\n');
106
+ void this.shutdown(1);
107
+ }
108
+ });
109
+ }
110
+
111
+ private attachPipe(
112
+ service: ServiceName,
113
+ stream: NodeJS.ReadableStream,
114
+ logFd: number,
115
+ kind: 'stdout' | 'stderr'
116
+ ): void {
117
+ const prefix = `[${DISPLAY_NAMES[service]}] `;
118
+ const target = kind === 'stderr' ? process.stderr : process.stdout;
119
+ const rl = readline.createInterface({ input: stream });
120
+ rl.on('line', (line) => {
121
+ try {
122
+ fs.writeSync(logFd, line + '\n');
123
+ } catch {
124
+ // best-effort: logs on disk are not critical
125
+ }
126
+ target.write(prefix + line + '\n');
127
+ });
128
+ }
129
+
130
+ async waitForDaemonSocket(timeoutMs = 10_000): Promise<void> {
131
+ const socketPath = getSocketPath();
132
+ const start = Date.now();
133
+ while (Date.now() - start < timeoutMs) {
134
+ if (fs.existsSync(socketPath)) return;
135
+ if (!this.children.has('daemon')) {
136
+ throw new Error('Daemon exited before socket became available.');
137
+ }
138
+ await new Promise((r) => setTimeout(r, 100));
139
+ }
140
+ throw new Error(`Daemon did not start within ${timeoutMs}ms`);
141
+ }
142
+
143
+ async shutdown(exitCode = 0): Promise<void> {
144
+ if (this.shuttingDown) return;
145
+ this.shuttingDown = true;
146
+ process.stderr.write('\n[supervisor] shutting down...\n');
147
+
148
+ await this.stopAllChildren();
149
+
150
+ process.exit(exitCode);
151
+ }
152
+
153
+ /**
154
+ * Stop all children and close log fds without exiting the process. Used by
155
+ * `/upgrade`, which needs to tear down everything, run an install, then
156
+ * launch a fresh supervisor.
157
+ */
158
+ async stopAllChildren(): Promise<void> {
159
+ // Phase 1: stop adapters and the web UI in parallel. They depend on the
160
+ // daemon, so taking them down first lets the daemon's `down` hooks run
161
+ // without interference from disconnect noise.
162
+ const adapterStops: Promise<void>[] = [];
163
+ for (const [name, child] of this.children) {
164
+ if (name === 'daemon') continue;
165
+ adapterStops.push(Supervisor.terminateChild(name, child, ADAPTER_TERMINATE_TIMEOUT_MS));
166
+ }
167
+ await Promise.allSettled(adapterStops);
168
+
169
+ // Phase 2: stop the daemon with a generous timeout so its `down` hooks
170
+ // (sandbox/container teardown) can complete.
171
+ const daemonChild = this.children.get('daemon');
172
+ if (daemonChild) {
173
+ await Supervisor.terminateChild('daemon', daemonChild, DAEMON_TERMINATE_TIMEOUT_MS);
174
+ }
175
+
176
+ for (const fd of this.logFds.values()) {
177
+ try {
178
+ fs.closeSync(fd);
179
+ } catch {
180
+ // best-effort
181
+ }
182
+ }
183
+ this.logFds.clear();
184
+ }
185
+
186
+ /**
187
+ * Bounce every service that has ever been started under this supervisor.
188
+ * Daemon goes down with the adapters, then comes back first so the
189
+ * adapters can re-establish their tRPC subscriptions to the new daemon.
190
+ *
191
+ * This is what `/restart` and the `/upgrade` failure recovery paths call:
192
+ * just bouncing the daemon would leave adapter-discord (and any other
193
+ * adapter) holding a dead subscription, so outbound messages would never
194
+ * reach the chat. The user-visible symptom was "I send /restart and then
195
+ * the daemon's reply never shows up in Discord."
196
+ */
197
+ async restartAll(): Promise<void> {
198
+ if (this.shuttingDown) return;
199
+ // Suppress the daemon's unexpected-exit guard while we tear it down.
200
+ this.restarting.add('daemon');
201
+ try {
202
+ await this.stopAllChildren();
203
+ // Wait for the exit handlers to drain bookkeeping.
204
+ await new Promise((r) => setImmediate(r));
205
+
206
+ if (this.enabledServices.has('daemon')) {
207
+ await this.startService('daemon');
208
+ await this.waitForDaemonSocket();
209
+ }
210
+ for (const name of this.enabledServices) {
211
+ if (name === 'daemon') continue;
212
+ await this.startService(name);
213
+ }
214
+ } finally {
215
+ this.restarting.delete('daemon');
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Stop and re-spawn a single service. The exit handler is suppressed so a
221
+ * restarted daemon doesn't trigger a full shutdown.
222
+ */
223
+ async restartService(service: ServiceName): Promise<void> {
224
+ if (this.shuttingDown) return;
225
+ this.restarting.add(service);
226
+ try {
227
+ const child = this.children.get(service);
228
+ if (child) {
229
+ const timeoutMs =
230
+ service === 'daemon' ? DAEMON_TERMINATE_TIMEOUT_MS : ADAPTER_TERMINATE_TIMEOUT_MS;
231
+ await Supervisor.terminateChild(service, child, timeoutMs);
232
+ }
233
+ // Wait until the exit handler has cleared bookkeeping. The handler runs
234
+ // synchronously on the same tick as `exit`, so a microtask hop is enough.
235
+ await new Promise((r) => setImmediate(r));
236
+ await this.startService(service);
237
+ if (service === 'daemon') {
238
+ await this.waitForDaemonSocket();
239
+ }
240
+ } finally {
241
+ this.restarting.delete(service);
242
+ }
243
+ }
244
+
245
+ private static terminateChild(
246
+ name: ServiceName,
247
+ child: ChildProcess,
248
+ timeoutMs: number
249
+ ): Promise<void> {
250
+ return new Promise((resolve) => {
251
+ if (child.exitCode !== null || child.signalCode !== null) {
252
+ resolve();
253
+ return;
254
+ }
255
+ const timer = setTimeout(() => {
256
+ process.stderr.write(
257
+ `[supervisor] ${name} did not exit in ${Math.round(timeoutMs / 1000)}s, sending SIGKILL\n`
258
+ );
259
+ try {
260
+ child.kill('SIGKILL');
261
+ } catch {
262
+ // ignore
263
+ }
264
+ }, timeoutMs);
265
+ child.once('exit', () => {
266
+ clearTimeout(timer);
267
+ resolve();
268
+ });
269
+ try {
270
+ child.kill('SIGTERM');
271
+ } catch {
272
+ clearTimeout(timer);
273
+ resolve();
274
+ }
275
+ });
276
+ }
277
+ }
@@ -1,8 +1,9 @@
1
1
  import { type FallbackSchema } from '../../shared/config.js';
2
2
  import {
3
3
  getActiveEnvironmentInfo,
4
- getEnvironmentPath,
4
+ getEnvironmentSearchDirs,
5
5
  readEnvironment,
6
+ substituteLayeredEnvDir,
6
7
  } from '../../shared/workspace.js';
7
8
  import { z } from 'zod';
8
9
 
@@ -10,19 +11,21 @@ export type Fallback = z.infer<typeof FallbackSchema>;
10
11
 
11
12
  function formatEnvironmentPrefix(
12
13
  prefix: string,
13
- replacements: { targetPath: string; executionCwd: string; envDir: string; envArgs: string }
14
+ searchDirs: string[],
15
+ replacements: { targetPath: string; executionCwd: string; envArgs: string }
14
16
  ): string {
17
+ let out = substituteLayeredEnvDir(prefix, searchDirs);
15
18
  const map: Record<string, string> = {
16
19
  '{WORKSPACE_DIR}': replacements.targetPath,
17
20
  '{AGENT_DIR}': replacements.executionCwd,
18
- '{ENV_DIR}': replacements.envDir,
19
21
  '{HOME_DIR}': process.env.HOME || '',
20
22
  '{ENV_ARGS}': replacements.envArgs,
21
23
  };
22
- return prefix.replace(
23
- /{(WORKSPACE_DIR|AGENT_DIR|ENV_DIR|HOME_DIR|ENV_ARGS)}/g,
24
+ out = out.replace(
25
+ /{(WORKSPACE_DIR|AGENT_DIR|HOME_DIR|ENV_ARGS)}/g,
24
26
  (match) => map[match] || match
25
27
  );
28
+ return out;
26
29
  }
27
30
 
28
31
  export async function sandboxExecutionContext(
@@ -38,6 +41,7 @@ export async function sandboxExecutionContext(
38
41
 
39
42
  const activeEnvName = activeEnvInfo.name;
40
43
  const activeEnv = await readEnvironment(activeEnvName, cwd);
44
+ const searchDirs = await getEnvironmentSearchDirs(activeEnvName, cwd);
41
45
 
42
46
  if (activeEnv?.env) {
43
47
  for (const [key, value] of Object.entries(activeEnv.env)) {
@@ -47,10 +51,7 @@ export async function sandboxExecutionContext(
47
51
  } else {
48
52
  let interpolatedValue = String(value);
49
53
  interpolatedValue = interpolatedValue.replace(/\{PATH\}/g, process.env.PATH || '');
50
- interpolatedValue = interpolatedValue.replace(
51
- /\{ENV_DIR\}/g,
52
- getEnvironmentPath(activeEnvName, cwd)
53
- );
54
+ interpolatedValue = substituteLayeredEnvDir(interpolatedValue, searchDirs);
54
55
  interpolatedValue = interpolatedValue.replace(
55
56
  /\{WORKSPACE_DIR\}/g,
56
57
  activeEnvInfo.targetPath
@@ -71,10 +72,9 @@ export async function sandboxExecutionContext(
71
72
  })
72
73
  .join(' ');
73
74
 
74
- const prefixReplaced = formatEnvironmentPrefix(activeEnv.prefix, {
75
+ const prefixReplaced = formatEnvironmentPrefix(activeEnv.prefix, searchDirs, {
75
76
  targetPath: activeEnvInfo.targetPath,
76
77
  executionCwd: executionCwd,
77
- envDir: getEnvironmentPath(activeEnvName, cwd),
78
78
  envArgs,
79
79
  });
80
80
 
@@ -23,6 +23,7 @@ export class AgentSession {
23
23
  public readonly sessionId: string;
24
24
  public readonly chatId: string;
25
25
  public readonly subagentId: string | undefined;
26
+ public readonly turnId: string | undefined;
26
27
  public readonly settings: Agent;
27
28
  public readonly workspaceRoot: string;
28
29
  public readonly globalSettings: Settings | undefined;
@@ -33,6 +34,7 @@ export class AgentSession {
33
34
  sessionId: string;
34
35
  chatId: string;
35
36
  subagentId?: string;
37
+ turnId?: string;
36
38
  settings: Agent;
37
39
  workspaceRoot: string;
38
40
  globalSettings: Settings | undefined;
@@ -42,11 +44,13 @@ export class AgentSession {
42
44
  this.sessionId = config.sessionId;
43
45
  this.chatId = config.chatId;
44
46
  this.subagentId = config.subagentId;
47
+ this.turnId = config.turnId;
45
48
  this.settings = config.settings;
46
49
  this.workspaceRoot = config.workspaceRoot;
47
50
  this.globalSettings = config.globalSettings;
48
51
 
49
- this.logger = config.logger ?? createChatLogger(this.chatId, this.subagentId);
52
+ this.logger =
53
+ config.logger ?? createChatLogger(this.chatId, this.subagentId, this.sessionId, this.turnId);
50
54
  }
51
55
 
52
56
  async buildExecutionContext(
@@ -110,6 +114,7 @@ export class AgentSession {
110
114
  agentId: this.agentId,
111
115
  sessionId: this.sessionId,
112
116
  ...(this.subagentId ? { subagentId: this.subagentId } : {}),
117
+ ...(this.turnId ? { turnId: this.turnId } : {}),
113
118
  timestamp: Date.now(),
114
119
  });
115
120
 
@@ -215,6 +220,7 @@ export async function createAgentSession(options: {
215
220
  agentId: string;
216
221
  sessionId: string;
217
222
  subagentId?: string;
223
+ turnId?: string;
218
224
  cwd: string;
219
225
  settings?: Settings | undefined;
220
226
  logger?: Logger;
@@ -229,6 +235,7 @@ export async function createAgentSession(options: {
229
235
  sessionId: options.sessionId,
230
236
  chatId: options.chatId,
231
237
  ...(options.subagentId ? { subagentId: options.subagentId } : {}),
238
+ ...(options.turnId ? { turnId: options.turnId } : {}),
232
239
  settings: mergedAgent,
233
240
  workspaceRoot,
234
241
  globalSettings: settings,
@@ -38,11 +38,52 @@ describe('ChatLogger', () => {
38
38
  );
39
39
  });
40
40
 
41
+ it('should inject sessionId into outgoing messages', async () => {
42
+ const logger = createChatLogger('chat-1', undefined, 'session-42');
43
+ await logger.logUserMessage('hello session');
44
+ await logger.logAgentReply({ content: 'reply' });
45
+ await logger.logCommandResult({
46
+ messageId: 'm',
47
+ content: '',
48
+ command: 'echo',
49
+ cwd: '/tmp',
50
+ result: { stdout: '', stderr: '', exitCode: 0 },
51
+ });
52
+ await logger.logToolMessage({
53
+ content: 'c',
54
+ messageId: 'm',
55
+ name: 't',
56
+ payload: {},
57
+ });
58
+ await logger.logSystemMessage({ content: 's', event: 'cron' });
59
+
60
+ const calls = vi.mocked(daemonChats.appendMessage).mock.calls;
61
+ expect(calls).toHaveLength(5);
62
+ for (const [, msg] of calls) {
63
+ expect(msg).toEqual(expect.objectContaining({ sessionId: 'session-42' }));
64
+ expect(msg).not.toHaveProperty('subagentId');
65
+ }
66
+ });
67
+
41
68
  it('should filter incoming logs for the subagent', async () => {
42
69
  const mockMessages: ChatMessage[] = [
43
- { id: '1', role: 'user', content: 'root msg', timestamp: '1' },
44
- { id: '2', role: 'user', content: 'sub msg', timestamp: '2', subagentId: 'sub-1' },
45
- { id: '3', role: 'user', content: 'other sub', timestamp: '3', subagentId: 'sub-2' },
70
+ { id: '1', role: 'user', content: 'root msg', timestamp: '1', sessionId: undefined },
71
+ {
72
+ id: '2',
73
+ role: 'user',
74
+ content: 'sub msg',
75
+ timestamp: '2',
76
+ subagentId: 'sub-1',
77
+ sessionId: undefined,
78
+ },
79
+ {
80
+ id: '3',
81
+ role: 'user',
82
+ content: 'other sub',
83
+ timestamp: '3',
84
+ subagentId: 'sub-2',
85
+ sessionId: undefined,
86
+ },
46
87
  ];
47
88
  vi.mocked(daemonChats.getMessages).mockResolvedValue(mockMessages);
48
89
 
@@ -56,10 +97,31 @@ describe('ChatLogger', () => {
56
97
 
57
98
  it('should limit messages after filtering', async () => {
58
99
  const mockMessages: ChatMessage[] = [
59
- { id: '1', role: 'user', content: 'root msg', timestamp: '1' },
60
- { id: '2', role: 'user', content: 'sub msg 1', timestamp: '2', subagentId: 'sub-1' },
61
- { id: '3', role: 'user', content: 'other sub', timestamp: '3', subagentId: 'sub-2' },
62
- { id: '4', role: 'user', content: 'sub msg 2', timestamp: '4', subagentId: 'sub-1' },
100
+ { id: '1', role: 'user', content: 'root msg', timestamp: '1', sessionId: undefined },
101
+ {
102
+ id: '2',
103
+ role: 'user',
104
+ content: 'sub msg 1',
105
+ timestamp: '2',
106
+ subagentId: 'sub-1',
107
+ sessionId: undefined,
108
+ },
109
+ {
110
+ id: '3',
111
+ role: 'user',
112
+ content: 'other sub',
113
+ timestamp: '3',
114
+ subagentId: 'sub-2',
115
+ sessionId: undefined,
116
+ },
117
+ {
118
+ id: '4',
119
+ role: 'user',
120
+ content: 'sub msg 2',
121
+ timestamp: '4',
122
+ subagentId: 'sub-1',
123
+ sessionId: undefined,
124
+ },
63
125
  ];
64
126
  vi.mocked(daemonChats.getMessages).mockResolvedValue(mockMessages);
65
127
 
@@ -72,8 +134,15 @@ describe('ChatLogger', () => {
72
134
 
73
135
  it('should not return subagent messages if no subagentId', async () => {
74
136
  const mockMessages: ChatMessage[] = [
75
- { id: '1', role: 'user', content: 'root msg', timestamp: '1' },
76
- { id: '2', role: 'user', content: 'sub msg 1', timestamp: '2', subagentId: 'sub-1' },
137
+ { id: '1', role: 'user', content: 'root msg', timestamp: '1', sessionId: undefined },
138
+ {
139
+ id: '2',
140
+ role: 'user',
141
+ content: 'sub msg 1',
142
+ timestamp: '2',
143
+ subagentId: 'sub-1',
144
+ sessionId: undefined,
145
+ },
77
146
  ];
78
147
  vi.mocked(daemonChats.getMessages).mockResolvedValue(mockMessages);
79
148
 
@@ -13,11 +13,18 @@ import {
13
13
  } from '../chats.js';
14
14
  import type { Logger } from './types.js';
15
15
 
16
- export function createChatLogger(chatId: string, subagentId?: string): Logger {
16
+ export function createChatLogger(
17
+ chatId: string,
18
+ subagentId?: string,
19
+ sessionId?: string,
20
+ turnId?: string
21
+ ): Logger {
17
22
  async function append<T extends ChatMessage>(msg: T): Promise<T> {
18
- const finalMsg = subagentId ? { ...msg, subagentId } : msg;
23
+ let finalMsg: T = msg;
24
+ if (subagentId) finalMsg = { ...finalMsg, subagentId };
25
+ if (turnId) finalMsg = { ...finalMsg, turnId };
19
26
  await appendMessage(chatId, finalMsg);
20
- return finalMsg as T;
27
+ return finalMsg;
21
28
  }
22
29
 
23
30
  return {
@@ -45,6 +52,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
45
52
  role: 'user',
46
53
  content: msg,
47
54
  timestamp: new Date().toISOString(),
55
+ sessionId,
48
56
  } satisfies UserMessage),
49
57
 
50
58
  logCommandResult: async ({ messageId, content, command, cwd, result }) =>
@@ -53,6 +61,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
53
61
  role: 'command',
54
62
  content,
55
63
  timestamp: new Date().toISOString(),
64
+ sessionId,
56
65
 
57
66
  messageId,
58
67
 
@@ -61,7 +70,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
61
70
  stdout: result.stdout,
62
71
  stderr: result.stderr,
63
72
  exitCode: result.exitCode,
64
- }),
73
+ } satisfies CommandLogMessage),
65
74
 
66
75
  logSystemEvent: async ({ content }) =>
67
76
  append({
@@ -69,6 +78,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
69
78
  role: 'command',
70
79
  content,
71
80
  timestamp: new Date().toISOString(),
81
+ sessionId,
72
82
 
73
83
  messageId: crypto.randomUUID(),
74
84
 
@@ -85,6 +95,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
85
95
  role: 'system',
86
96
  content,
87
97
  timestamp: new Date().toISOString(),
98
+ sessionId,
88
99
 
89
100
  messageId,
90
101
  event: 'router',
@@ -97,6 +108,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
97
108
  role: 'command',
98
109
  content,
99
110
  timestamp: new Date().toISOString(),
111
+ sessionId,
100
112
 
101
113
  messageId,
102
114
 
@@ -108,13 +120,14 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
108
120
  exitCode: 0,
109
121
  } satisfies CommandLogMessage),
110
122
 
111
- logSystemMessage: async ({ content, event, messageId, displayRole }) => {
123
+ logSystemMessage: async ({ content, event, messageId, displayRole, jobId }) => {
112
124
  const msg: SystemMessage = {
113
125
  id: crypto.randomUUID(),
114
126
  role: 'system',
115
127
  content,
116
128
  event,
117
129
  timestamp: new Date().toISOString(),
130
+ sessionId,
118
131
  };
119
132
  if (messageId !== undefined) {
120
133
  msg.messageId = messageId;
@@ -122,6 +135,9 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
122
135
  if (displayRole !== undefined) {
123
136
  msg.displayRole = displayRole;
124
137
  }
138
+ if (jobId !== undefined) {
139
+ msg.jobId = jobId;
140
+ }
125
141
  return append<SystemMessage>(msg);
126
142
  },
127
143
 
@@ -133,6 +149,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
133
149
  subagentId: targetSubagentId,
134
150
  status,
135
151
  timestamp: new Date().toISOString(),
152
+ sessionId,
136
153
  };
137
154
  return append<SubagentStatusMessage>(msg);
138
155
  },
@@ -143,6 +160,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
143
160
  role: 'agent',
144
161
  content,
145
162
  timestamp: new Date().toISOString(),
163
+ sessionId,
146
164
  };
147
165
  if (files !== undefined) {
148
166
  msg.files = files;
@@ -159,6 +177,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
159
177
  name,
160
178
  payload,
161
179
  timestamp: new Date().toISOString(),
180
+ sessionId,
162
181
  };
163
182
  return append<ToolMessage>(msg);
164
183
  },
@@ -181,6 +200,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
181
200
  args,
182
201
  status,
183
202
  timestamp: new Date().toISOString(),
203
+ sessionId,
184
204
  };
185
205
  return append<PolicyRequestMessage>(msg);
186
206
  },