clawmini 0.0.7 → 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/{G_zz-Gou.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.CYS8iApT.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.Dr0ot9sV.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.BBGQ_i84.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 +42 -13
  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 -176
  274. package/web/.svelte-kit/generated/server/internal.js +1 -1
  275. package/web/.svelte-kit/output/client/.vite/manifest.json +127 -137
  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/{G_zz-Gou.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.CYS8iApT.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.Dr0ot9sV.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.BBGQ_i84.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/DMtIqaiV.js +0 -2
  328. package/dist/web/_app/immutable/chunks/DhD271EB.js +0 -1
  329. package/dist/web/_app/immutable/chunks/DpuLqk8d.js +0 -1
  330. package/dist/web/_app/immutable/chunks/DsIToJCP.js +0 -1
  331. package/dist/web/_app/immutable/chunks/bBmtyQMj.js +0 -1
  332. package/dist/web/_app/immutable/entry/app.CJmSwntr.js +0 -2
  333. package/dist/web/_app/immutable/entry/start.ZpUrT2ak.js +0 -1
  334. package/dist/web/_app/immutable/nodes/1.Bli0Hqzn.js +0 -1
  335. package/dist/web/_app/immutable/nodes/4.oBhvQhcA.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/DMtIqaiV.js +0 -2
  357. package/web/.svelte-kit/output/client/_app/immutable/chunks/DhD271EB.js +0 -1
  358. package/web/.svelte-kit/output/client/_app/immutable/chunks/DpuLqk8d.js +0 -1
  359. package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +0 -1
  360. package/web/.svelte-kit/output/client/_app/immutable/chunks/bBmtyQMj.js +0 -1
  361. package/web/.svelte-kit/output/client/_app/immutable/entry/app.CJmSwntr.js +0 -2
  362. package/web/.svelte-kit/output/client/_app/immutable/entry/start.ZpUrT2ak.js +0 -1
  363. package/web/.svelte-kit/output/client/_app/immutable/nodes/1.Bli0Hqzn.js +0 -1
  364. package/web/.svelte-kit/output/client/_app/immutable/nodes/4.oBhvQhcA.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
@@ -13,12 +13,14 @@ import { createUnixSocketEventSource } from '../shared/event-source.js';
13
13
  import type { GoogleChatConfig } from './config.js';
14
14
  import { isAuthorized, updateGoogleChatConfig } from './config.js';
15
15
  import { readGoogleChatState, updateGoogleChatState } from './state.js';
16
- import { downloadAttachment } from './utils.js';
16
+ import { recordInbound } from './inbound-cache.js';
17
+ import { downloadAttachment as defaultDownloadAttachment } from './utils.js';
17
18
  import { handleAdapterCommand, type CommandTrpcClient } from '../shared/adapters/commands.js';
18
19
  import { formatMessage, type FilteringConfig } from '../shared/adapters/filtering.js';
19
20
  import { google } from 'googleapis';
20
21
  import { getAuthClient } from './auth.js';
21
22
  import { handleRoutingCommand, type RoutingTrpcClient } from '../shared/adapters/routing.js';
23
+ import { prependBlockquote } from '../shared/adapters/blockquote.js';
22
24
 
23
25
  import { handleAddedToSpace, handleRemovedFromSpace } from './subscriptions.js';
24
26
  import { handleCardClicked } from './cards.js';
@@ -52,13 +54,74 @@ export function getTRPCClient(options: { socketPath?: string } = {}) {
52
54
  });
53
55
  }
54
56
 
57
+ export type GoogleChatApi = ReturnType<typeof google.chat>;
58
+
59
+ interface QuotedSender {
60
+ type?: string | null;
61
+ email?: string | null;
62
+ name?: string | null;
63
+ displayName?: string | null;
64
+ }
65
+
66
+ /**
67
+ * Map a quoted message's sender to a short attribution label. Returns "Bot"
68
+ * for the assistant, "You" if it's one of the configured authorized users,
69
+ * and otherwise the sender's email (preferred) or `users/{id}` resource name.
70
+ */
71
+ export function formatQuotedSender(
72
+ sender: QuotedSender | undefined,
73
+ authorizedUsers: string[]
74
+ ): string | undefined {
75
+ if (!sender) return undefined;
76
+ if (sender.type === 'BOT') return 'Assistant';
77
+ const email = sender.email ?? undefined;
78
+ const name = sender.name ?? undefined;
79
+ if (
80
+ (email && isAuthorized(email, authorizedUsers)) ||
81
+ (name && isAuthorized(name, authorizedUsers))
82
+ ) {
83
+ return undefined;
84
+ }
85
+ return email || sender.displayName || name || undefined;
86
+ }
87
+
88
+ export interface MessageSourceLike {
89
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
+ on(event: string, listener: (...args: any[]) => void | Promise<void>): unknown;
91
+ }
92
+
93
+ export interface GoogleChatIngestionDeps {
94
+ /** Inbound message source (defaults to a real Pub/Sub subscription). */
95
+ subscription?: MessageSourceLike;
96
+ /** Google Chat API client (defaults to `google.chat()` with ADC credentials). */
97
+ chatApi?: GoogleChatApi;
98
+ /** Root directory for resolving adapter state/config (defaults to `process.cwd()`). */
99
+ startDir?: string;
100
+ /** Attachment downloader (defaults to the real Chat media endpoint). */
101
+ downloadAttachment?: (resourceName: string, maxSizeMB?: number) => Promise<Buffer>;
102
+ }
103
+
55
104
  export function startGoogleChatIngestion(
56
105
  config: GoogleChatConfig,
57
106
  trpc: ReturnType<typeof getTRPCClient>,
58
- filteringConfig: FilteringConfig
107
+ filteringConfig: FilteringConfig,
108
+ deps: GoogleChatIngestionDeps = {}
59
109
  ) {
60
- const pubsub = new PubSub({ projectId: config.projectId });
61
- const subscription = pubsub.subscription(config.subscriptionName);
110
+ const startDir = deps.startDir ?? process.cwd();
111
+ const subscription: MessageSourceLike =
112
+ deps.subscription ??
113
+ (() => {
114
+ const pubsub = new PubSub({ projectId: config.projectId });
115
+ return pubsub.subscription(config.subscriptionName);
116
+ })();
117
+
118
+ const getChatApi = async (): Promise<GoogleChatApi> => {
119
+ if (deps.chatApi) return deps.chatApi;
120
+ const authClient = await getAuthClient();
121
+ return google.chat({ version: 'v1', auth: authClient });
122
+ };
123
+
124
+ const downloadAttachment = deps.downloadAttachment ?? defaultDownloadAttachment;
62
125
 
63
126
  const seenMessageIds = new Map<string, number>();
64
127
 
@@ -99,9 +162,13 @@ export function startGoogleChatIngestion(
99
162
  : parsedData.space || eventMessage?.space;
100
163
  const senderType = eventMessage?.sender?.type || '';
101
164
  const messageId = eventMessage?.name || '';
102
- const text = (eventMessage?.argumentText || eventMessage?.text || '').trim();
165
+ const text = (eventMessage?.text || '').trim();
103
166
 
104
- if (senderType === 'BOT') return void message.ack();
167
+ // CARD_CLICKED events carry the bot's original card message as
168
+ // `parsedData.message`, so `sender.type` is BOT even though the
169
+ // interaction was initiated by a real user (`parsedData.user`).
170
+ // Only drop BOT-authored MESSAGE events here.
171
+ if (senderType === 'BOT' && eventType !== 'CARD_CLICKED') return void message.ack();
105
172
 
106
173
  if (messageId) {
107
174
  if (seenMessageIds.has(messageId)) return void message.ack();
@@ -142,7 +209,7 @@ export function startGoogleChatIngestion(
142
209
  `Automatically authorizing user ID ${senderName} based on authorized email ${email}`
143
210
  );
144
211
  config.authorizedUsers.push(senderName);
145
- updateGoogleChatConfig(config).catch((err) =>
212
+ updateGoogleChatConfig(config, startDir).catch((err) =>
146
213
  console.error('Failed to update config with new user ID:', err)
147
214
  );
148
215
  }
@@ -157,7 +224,7 @@ export function startGoogleChatIngestion(
157
224
  return;
158
225
  }
159
226
 
160
- const currentState = await readGoogleChatState();
227
+ const currentState = await readGoogleChatState(startDir);
161
228
 
162
229
  const externalContextId = spaceName;
163
230
  const mappedChatId = currentState.channelChatMap?.[externalContextId]?.chatId;
@@ -170,7 +237,8 @@ export function startGoogleChatIngestion(
170
237
  space?.type,
171
238
  mappedChatId,
172
239
  mappedChatId,
173
- config
240
+ config,
241
+ startDir
174
242
  );
175
243
  if (!text) {
176
244
  message.ack();
@@ -179,7 +247,7 @@ export function startGoogleChatIngestion(
179
247
  }
180
248
 
181
249
  if (eventType === 'REMOVED_FROM_SPACE') {
182
- await handleRemovedFromSpace(externalContextId, currentState, config);
250
+ await handleRemovedFromSpace(externalContextId, currentState, config, startDir);
183
251
  message.ack();
184
252
  return;
185
253
  }
@@ -198,20 +266,22 @@ export function startGoogleChatIngestion(
198
266
 
199
267
  if (routingResult) {
200
268
  if (routingResult.type === 'mapped') {
201
- await updateGoogleChatState((latestState) => ({
202
- channelChatMap: {
203
- ...(latestState.channelChatMap || {}),
204
- [externalContextId]: {
205
- ...(latestState.channelChatMap?.[externalContextId] || {}),
206
- chatId: routingResult.newChatId,
269
+ await updateGoogleChatState(
270
+ (latestState) => ({
271
+ channelChatMap: {
272
+ ...(latestState.channelChatMap || {}),
273
+ [externalContextId]: {
274
+ ...(latestState.channelChatMap?.[externalContextId] || {}),
275
+ chatId: routingResult.newChatId,
276
+ },
207
277
  },
208
- },
209
- }));
278
+ }),
279
+ startDir
280
+ );
210
281
  }
211
282
 
212
283
  try {
213
- const authClient = await getAuthClient();
214
- const chatApi = google.chat({ version: 'v1', auth: authClient });
284
+ const chatApi = await getChatApi();
215
285
  await chatApi.spaces.messages.create({
216
286
  parent: externalContextId,
217
287
  requestBody: { text: routingResult.text },
@@ -237,15 +307,18 @@ export function startGoogleChatIngestion(
237
307
  console.log(
238
308
  `First contact detected. Automatically mapping space ${externalContextId} to chat ${targetChatId}.`
239
309
  );
240
- await updateGoogleChatState((latestState) => ({
241
- channelChatMap: {
242
- ...(latestState.channelChatMap || {}),
243
- [externalContextId]: {
244
- ...(latestState.channelChatMap?.[externalContextId] || {}),
245
- chatId: targetChatId as string,
310
+ await updateGoogleChatState(
311
+ (latestState) => ({
312
+ channelChatMap: {
313
+ ...(latestState.channelChatMap || {}),
314
+ [externalContextId]: {
315
+ ...(latestState.channelChatMap?.[externalContextId] || {}),
316
+ chatId: targetChatId as string,
317
+ },
246
318
  },
247
- },
248
- }));
319
+ }),
320
+ startDir
321
+ );
249
322
  } else {
250
323
  const isDirectMessage =
251
324
  space?.type === 'DIRECT_MESSAGE' || space?.singleUserBotDm === true;
@@ -257,8 +330,7 @@ export function startGoogleChatIngestion(
257
330
  if (isDirectMessage || isMentioned || isSlashCommand) {
258
331
  console.log(`Unmapped space ${externalContextId}, sending first contact warning.`);
259
332
  try {
260
- const authClient = await getAuthClient();
261
- const chatApi = google.chat({ version: 'v1', auth: authClient });
333
+ const chatApi = await getChatApi();
262
334
  await chatApi.spaces.messages.create({
263
335
  parent: externalContextId,
264
336
  requestBody: {
@@ -300,8 +372,7 @@ export function startGoogleChatIngestion(
300
372
  let isReplyToBot = false;
301
373
  if (eventMessage?.threadReply && eventMessage.thread?.name) {
302
374
  try {
303
- const authClient = await getAuthClient();
304
- const chatApi = google.chat({ version: 'v1', auth: authClient });
375
+ const chatApi = await getChatApi();
305
376
  const response = await chatApi.spaces.messages.list({
306
377
  parent: externalContextId,
307
378
  filter: `thread.name="${eventMessage.thread.name}"`,
@@ -332,7 +403,8 @@ export function startGoogleChatIngestion(
332
403
  await handleCardClicked(
333
404
  parsedData,
334
405
  targetChatId as string,
335
- trpc as unknown as RoutingTrpcClient
406
+ trpc as unknown as RoutingTrpcClient,
407
+ getChatApi
336
408
  );
337
409
  message.ack();
338
410
  return;
@@ -350,7 +422,7 @@ export function startGoogleChatIngestion(
350
422
  if (commandResult.type === 'text') {
351
423
  if (commandResult.newConfig) {
352
424
  filteringConfig.filters = commandResult.newConfig.filters;
353
- await updateGoogleChatState({ filters: filteringConfig.filters });
425
+ await updateGoogleChatState({ filters: filteringConfig.filters }, startDir);
354
426
  }
355
427
  resultText = commandResult.text;
356
428
  } else if (commandResult.type === 'debug') {
@@ -361,8 +433,7 @@ export function startGoogleChatIngestion(
361
433
  commandResult.messages.map((msg) => formatMessage(msg)).join('\n\n---\n\n');
362
434
  }
363
435
 
364
- const authClient = await getAuthClient();
365
- const chatApi = google.chat({ version: 'v1', auth: authClient });
436
+ const chatApi = await getChatApi();
366
437
  await chatApi.spaces.messages.create({
367
438
  parent: spaceName as string,
368
439
  requestBody: { text: resultText },
@@ -373,7 +444,7 @@ export function startGoogleChatIngestion(
373
444
  const attachments = eventMessage?.attachment || [];
374
445
 
375
446
  if (attachments.length > 0) {
376
- const tmpDir = path.join(getClawminiDir(process.cwd()), 'tmp', 'google-chat');
447
+ const tmpDir = path.join(getClawminiDir(startDir), 'tmp', 'google-chat');
377
448
  await fsPromises.mkdir(tmpDir, { recursive: true });
378
449
 
379
450
  for (const att of attachments) {
@@ -392,15 +463,44 @@ export function startGoogleChatIngestion(
392
463
  }
393
464
  }
394
465
 
466
+ let forwardedText = text;
467
+ const quotedMetadata = eventMessage?.quotedMessageMetadata;
468
+ if (quotedMetadata) {
469
+ let quotedText: string | undefined = quotedMetadata.quotedMessageSnapshot?.text;
470
+ let quotedSender: QuotedSender | undefined = quotedMetadata.quotedMessageSnapshot?.sender;
471
+ if ((!quotedText || !quotedSender) && quotedMetadata.name) {
472
+ try {
473
+ const chatApi = await getChatApi();
474
+ const quotedRes = await chatApi.spaces.messages.get({ name: quotedMetadata.name });
475
+ quotedText = quotedText || quotedRes.data?.text || undefined;
476
+ quotedSender =
477
+ quotedSender || (quotedRes.data?.sender as QuotedSender | undefined) || undefined;
478
+ } catch (err) {
479
+ console.error('Failed to fetch quoted message:', err);
480
+ }
481
+ }
482
+ if (quotedText) {
483
+ const senderLabel = formatQuotedSender(quotedSender, config.authorizedUsers);
484
+ forwardedText = prependBlockquote(quotedText, text, senderLabel);
485
+ }
486
+ }
487
+
488
+ const gchatThreadName: string | undefined =
489
+ eventMessage?.thread?.name ?? eventMessage?.threadName ?? undefined;
490
+ if (messageId && gchatThreadName) {
491
+ recordInbound({ gchatMessageName: messageId, gchatThreadName });
492
+ }
493
+
395
494
  await trpc.sendMessage.mutate({
396
495
  type: 'send-message',
397
496
  client: 'cli',
398
497
  data: {
399
- message: text,
498
+ message: forwardedText,
400
499
  chatId: targetChatId,
401
500
  files: downloadedFiles.length > 0 ? downloadedFiles : undefined,
402
501
  adapter: 'google-chat',
403
502
  noWait: true,
503
+ ...(messageId ? { externalRef: messageId } : {}),
404
504
  },
405
505
  });
406
506
 
@@ -4,6 +4,24 @@ import { z } from 'zod';
4
4
  import { getClawminiDir } from '../shared/workspace.js';
5
5
  import fs from 'node:fs';
6
6
 
7
+ export const ThreadVisibilitySchema = z.object({
8
+ threads: z.boolean().default(true).optional(),
9
+ threadLog: z
10
+ .object({
11
+ maxToolPreview: z.number().default(400).optional(),
12
+ maxLogMessageChars: z.number().default(3500).optional(),
13
+ editDebounceMs: z.number().default(1000).optional(),
14
+ })
15
+ .optional(),
16
+ // Proactive (cron) turns have no inbound user message. `silent` (default)
17
+ // drops the cron system message and anchors any thread-log activity on the
18
+ // agent's eventual top-level reply; if the agent never replies, nothing
19
+ // posts. `header` posts a terse `🕒 <jobId>` header top-level to serve as
20
+ // the anchor, making scheduled work visible even when the agent stays
21
+ // silent.
22
+ jobs: z.enum(['silent', 'header']).default('silent').optional(),
23
+ });
24
+
7
25
  export const GoogleChatConfigSchema = z.looseObject({
8
26
  projectId: z.string().min(1, 'GCP Project ID is required.'),
9
27
  subscriptionName: z.string().min(1, 'Pub/Sub Subscription Name is required.'),
@@ -16,6 +34,7 @@ export const GoogleChatConfigSchema = z.looseObject({
16
34
  requireMention: z.boolean().default(false),
17
35
  oauthClientId: z.string().optional(),
18
36
  oauthClientSecret: z.string().optional(),
37
+ visibility: ThreadVisibilitySchema.optional(),
19
38
  });
20
39
 
21
40
  export type GoogleChatConfig = z.infer<typeof GoogleChatConfigSchema>;
@@ -1,6 +1,11 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { startDaemonToGoogleChatForwarder } from './forwarder.js';
3
3
 
4
+ // `waitForMessages` yields `ChatStreamItem` envelopes (discriminated by
5
+ // `kind`). Tests send raw message shapes; wrap them here.
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ const asEnvelopes = (messages: any[]) => messages.map((message) => ({ kind: 'message', message }));
8
+
4
9
  const mockConfig = {
5
10
  projectId: 'test',
6
11
  subscriptionName: 'test',
@@ -77,7 +82,7 @@ vi.mock('./state.js', () => ({
77
82
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
83
  updateGoogleChatState: (updates: any) => {
79
84
  const currentState = { lastSyncedMessageIds: { otherChat: 'msg-other' } };
80
- const result = typeof updates === 'function' ? updates(currentState as any) : updates;
85
+ const result = typeof updates === 'function' ? updates(currentState) : updates;
81
86
  mockStateDeps.mockWriteState(result);
82
87
  return Promise.resolve(result);
83
88
  },
@@ -158,14 +163,16 @@ describe('Daemon to Google Chat Forwarder', () => {
158
163
  expect.any(Object)
159
164
  );
160
165
 
161
- subscribeCallbacks.onData([
162
- {
163
- id: 'msg-2',
164
- role: 'agent',
165
- content: 'Agent response',
166
- timestamp: '',
167
- },
168
- ]);
166
+ subscribeCallbacks.onData(
167
+ asEnvelopes([
168
+ {
169
+ id: 'msg-2',
170
+ role: 'agent',
171
+ content: 'Agent response',
172
+ timestamp: '',
173
+ },
174
+ ])
175
+ );
169
176
 
170
177
  await vi.waitFor(() => expect(mockMessagesCreate).toHaveBeenCalled());
171
178
 
@@ -190,14 +197,16 @@ describe('Daemon to Google Chat Forwarder', () => {
190
197
 
191
198
  await vi.waitFor(() => expect(subscribeCallbacks).toBeTruthy());
192
199
 
193
- subscribeCallbacks.onData([
194
- {
195
- id: 'msg-2',
196
- role: 'agent',
197
- content: 'Here are the files',
198
- files: ['/tmp/file1.png', '/tmp/file2.txt'],
199
- },
200
- ]);
200
+ subscribeCallbacks.onData(
201
+ asEnvelopes([
202
+ {
203
+ id: 'msg-2',
204
+ role: 'agent',
205
+ content: 'Here are the files',
206
+ files: ['/tmp/file1.png', '/tmp/file2.txt'],
207
+ },
208
+ ])
209
+ );
201
210
 
202
211
  await vi.waitFor(() => expect(mockMessagesCreate).toHaveBeenCalled());
203
212
 
@@ -232,14 +241,16 @@ describe('Daemon to Google Chat Forwarder', () => {
232
241
 
233
242
  await vi.waitFor(() => expect(subscribeCallbacks).toBeTruthy());
234
243
 
235
- subscribeCallbacks.onData([
236
- {
237
- id: 'msg-3',
238
- role: 'agent',
239
- content: 'Here are the files',
240
- files: ['/tmp/file1.png', '/tmp/file2.txt'],
241
- },
242
- ]);
244
+ subscribeCallbacks.onData(
245
+ asEnvelopes([
246
+ {
247
+ id: 'msg-3',
248
+ role: 'agent',
249
+ content: 'Here are the files',
250
+ files: ['/tmp/file1.png', '/tmp/file2.txt'],
251
+ },
252
+ ])
253
+ );
243
254
 
244
255
  await vi.waitFor(() => expect(mockMessagesCreate).toHaveBeenCalled());
245
256
 
@@ -270,14 +281,16 @@ describe('Daemon to Google Chat Forwarder', () => {
270
281
 
271
282
  mockDriveFilesCreate.mockRejectedValueOnce(new Error('Drive Auth Failed'));
272
283
 
273
- subscribeCallbacks.onData([
274
- {
275
- id: 'msg-drive-fail',
276
- role: 'agent',
277
- content: 'Here are the files',
278
- files: ['/tmp/file1.png'],
279
- },
280
- ]);
284
+ subscribeCallbacks.onData(
285
+ asEnvelopes([
286
+ {
287
+ id: 'msg-drive-fail',
288
+ role: 'agent',
289
+ content: 'Here are the files',
290
+ files: ['/tmp/file1.png'],
291
+ },
292
+ ])
293
+ );
281
294
 
282
295
  await vi.waitFor(() => expect(mockMessagesCreate).toHaveBeenCalled());
283
296
 
@@ -313,10 +326,12 @@ describe('Daemon to Google Chat Forwarder', () => {
313
326
  return Promise.resolve();
314
327
  });
315
328
 
316
- subscribeCallbacks.onData([
317
- { id: 'msg-err-1', role: 'agent', content: 'Agent response 1' },
318
- { id: 'msg-err-2', role: 'agent', content: 'Agent response 2' },
319
- ]);
329
+ subscribeCallbacks.onData(
330
+ asEnvelopes([
331
+ { id: 'msg-err-1', role: 'agent', content: 'Agent response 1' },
332
+ { id: 'msg-err-2', role: 'agent', content: 'Agent response 2' },
333
+ ])
334
+ );
320
335
 
321
336
  // Wait for the second message to be processed, meaning the first one didn't break the loop
322
337
  await vi.waitFor(() => expect(callCount).toBe(2));
@@ -348,14 +363,16 @@ describe('Daemon to Google Chat Forwarder', () => {
348
363
 
349
364
  await vi.waitFor(() => expect(subscribeCallbacks).toBeTruthy());
350
365
 
351
- subscribeCallbacks.onData([
352
- {
353
- id: 'msg-2',
354
- role: 'policy',
355
- status: 'pending',
356
- content: 'Please approve this action',
357
- },
358
- ]);
366
+ subscribeCallbacks.onData(
367
+ asEnvelopes([
368
+ {
369
+ id: 'msg-2',
370
+ role: 'policy',
371
+ status: 'pending',
372
+ content: 'Please approve this action',
373
+ },
374
+ ])
375
+ );
359
376
 
360
377
  await vi.waitFor(() => expect(mockMessagesCreate).toHaveBeenCalled());
361
378
 
@@ -434,14 +451,16 @@ describe('Daemon to Google Chat Forwarder', () => {
434
451
  .mockRejectedValueOnce(new Error('Cannot send cardsV2'))
435
452
  .mockResolvedValueOnce({});
436
453
 
437
- subscribeCallbacks.onData([
438
- {
439
- id: 'msg-2',
440
- role: 'policy',
441
- status: 'pending',
442
- content: 'Please approve this action',
443
- },
444
- ]);
454
+ subscribeCallbacks.onData(
455
+ asEnvelopes([
456
+ {
457
+ id: 'msg-2',
458
+ role: 'policy',
459
+ status: 'pending',
460
+ content: 'Please approve this action',
461
+ },
462
+ ])
463
+ );
445
464
 
446
465
  await vi.waitFor(() => expect(mockMessagesCreate).toHaveBeenCalledTimes(2));
447
466
 
@@ -473,7 +492,9 @@ describe('Daemon to Google Chat Forwarder', () => {
473
492
  await vi.waitFor(() => expect(subscribeCallbacks).toBeTruthy(), { timeout: 1000 });
474
493
 
475
494
  // Send a message, this updates the local memory cache to msg-local
476
- subscribeCallbacks.onData([{ id: 'msg-local', role: 'agent', content: 'Agent response' }]);
495
+ subscribeCallbacks.onData(
496
+ asEnvelopes([{ id: 'msg-local', role: 'agent', content: 'Agent response' }])
497
+ );
477
498
 
478
499
  await vi.waitFor(
479
500
  () =>
@@ -500,7 +521,9 @@ describe('Daemon to Google Chat Forwarder', () => {
500
521
  await vi.runAllTicks();
501
522
 
502
523
  // Send another message to verify what the local cache holds
503
- subscribeCallbacks.onData([{ id: 'msg-latest', role: 'agent', content: 'Agent response 2' }]);
524
+ subscribeCallbacks.onData(
525
+ asEnvelopes([{ id: 'msg-latest', role: 'agent', content: 'Agent response 2' }])
526
+ );
504
527
 
505
528
  // If local memory wins, the new write state will only contain msg-latest and not msg-stale
506
529
  // If disk won, it would have reverted to msg-stale and then updated to msg-latest?
@@ -515,7 +538,9 @@ describe('Daemon to Google Chat Forwarder', () => {
515
538
 
516
539
  await vi.advanceTimersByTimeAsync(6000);
517
540
 
518
- subscribeCallbacks.onData([{ id: 'msg-latest', role: 'agent', content: 'Agent response 2' }]);
541
+ subscribeCallbacks.onData(
542
+ asEnvelopes([{ id: 'msg-latest', role: 'agent', content: 'Agent response 2' }])
543
+ );
519
544
 
520
545
  await vi.waitFor(
521
546
  () =>