@withbuddi/buddi 0.1.0-pre.28 → 0.1.0-pre.30

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 (1190) hide show
  1. package/README.md +3 -1
  2. package/extension/manifest.json +2 -2
  3. package/node_modules/@buddi/cli/dist/.tsbuildinfo +1 -1
  4. package/node_modules/@buddi/cli/dist/args.d.ts +19 -3
  5. package/node_modules/@buddi/cli/dist/args.d.ts.map +1 -1
  6. package/node_modules/@buddi/cli/dist/args.js +19 -5
  7. package/node_modules/@buddi/cli/dist/args.js.map +1 -1
  8. package/node_modules/@buddi/cli/dist/commands.d.ts.map +1 -1
  9. package/node_modules/@buddi/cli/dist/commands.js +80 -8
  10. package/node_modules/@buddi/cli/dist/commands.js.map +1 -1
  11. package/node_modules/@buddi/cli/dist/dashboard-cmd.d.ts +3 -0
  12. package/node_modules/@buddi/cli/dist/dashboard-cmd.d.ts.map +1 -1
  13. package/node_modules/@buddi/cli/dist/dashboard-cmd.js +63 -0
  14. package/node_modules/@buddi/cli/dist/dashboard-cmd.js.map +1 -1
  15. package/node_modules/@buddi/cli/dist/doctor-probes.d.ts.map +1 -1
  16. package/node_modules/@buddi/cli/dist/doctor-probes.js +11 -4
  17. package/node_modules/@buddi/cli/dist/doctor-probes.js.map +1 -1
  18. package/node_modules/@buddi/cli/dist/doctor.d.ts +1 -1
  19. package/node_modules/@buddi/cli/dist/doctor.d.ts.map +1 -1
  20. package/node_modules/@buddi/cli/dist/jobs-cmd.d.ts +4 -0
  21. package/node_modules/@buddi/cli/dist/jobs-cmd.d.ts.map +1 -1
  22. package/node_modules/@buddi/cli/dist/jobs-cmd.js +53 -4
  23. package/node_modules/@buddi/cli/dist/jobs-cmd.js.map +1 -1
  24. package/node_modules/@buddi/cli/dist/main.d.ts.map +1 -1
  25. package/node_modules/@buddi/cli/dist/main.js +27 -2
  26. package/node_modules/@buddi/cli/dist/main.js.map +1 -1
  27. package/node_modules/@buddi/cli/dist/mcp/gateway-client.d.ts +19 -17
  28. package/node_modules/@buddi/cli/dist/mcp/gateway-client.d.ts.map +1 -1
  29. package/node_modules/@buddi/cli/dist/mcp/gateway-client.js +128 -29
  30. package/node_modules/@buddi/cli/dist/mcp/gateway-client.js.map +1 -1
  31. package/node_modules/@buddi/cli/dist/mcp/tools.d.ts +6 -0
  32. package/node_modules/@buddi/cli/dist/mcp/tools.d.ts.map +1 -1
  33. package/node_modules/@buddi/cli/dist/mcp/tools.js +20 -1
  34. package/node_modules/@buddi/cli/dist/mcp/tools.js.map +1 -1
  35. package/node_modules/@buddi/cli/dist/telegram-cmd.d.ts.map +1 -1
  36. package/node_modules/@buddi/cli/dist/telegram-cmd.js +5 -4
  37. package/node_modules/@buddi/cli/dist/telegram-cmd.js.map +1 -1
  38. package/node_modules/@buddi/core/dist/.tsbuildinfo +1 -1
  39. package/node_modules/@buddi/core/dist/agents/catalog.d.ts.map +1 -1
  40. package/node_modules/@buddi/core/dist/agents/catalog.js +7 -6
  41. package/node_modules/@buddi/core/dist/agents/catalog.js.map +1 -1
  42. package/node_modules/@buddi/core/dist/agents/frontmatter.d.ts +12 -12
  43. package/node_modules/@buddi/core/dist/agents/skills.d.ts +2 -2
  44. package/node_modules/@buddi/core/dist/clock-widget.d.ts +38 -0
  45. package/node_modules/@buddi/core/dist/clock-widget.d.ts.map +1 -0
  46. package/node_modules/@buddi/core/dist/clock-widget.js +114 -0
  47. package/node_modules/@buddi/core/dist/clock-widget.js.map +1 -0
  48. package/node_modules/@buddi/core/dist/failures/owner-message.d.ts +25 -0
  49. package/node_modules/@buddi/core/dist/failures/owner-message.d.ts.map +1 -1
  50. package/node_modules/@buddi/core/dist/failures/owner-message.js +90 -0
  51. package/node_modules/@buddi/core/dist/failures/owner-message.js.map +1 -1
  52. package/node_modules/@buddi/core/dist/feedback/index.d.ts +23 -2
  53. package/node_modules/@buddi/core/dist/feedback/index.d.ts.map +1 -1
  54. package/node_modules/@buddi/core/dist/feedback/index.js +23 -3
  55. package/node_modules/@buddi/core/dist/feedback/index.js.map +1 -1
  56. package/node_modules/@buddi/core/dist/groups.d.ts +33 -0
  57. package/node_modules/@buddi/core/dist/groups.d.ts.map +1 -1
  58. package/node_modules/@buddi/core/dist/groups.js +76 -4
  59. package/node_modules/@buddi/core/dist/groups.js.map +1 -1
  60. package/node_modules/@buddi/core/dist/home.d.ts +5 -5
  61. package/node_modules/@buddi/core/dist/host/build.d.ts +19 -1
  62. package/node_modules/@buddi/core/dist/host/build.d.ts.map +1 -1
  63. package/node_modules/@buddi/core/dist/host/build.js +122 -2
  64. package/node_modules/@buddi/core/dist/host/build.js.map +1 -1
  65. package/node_modules/@buddi/core/dist/host/types.d.ts +33 -0
  66. package/node_modules/@buddi/core/dist/host/types.d.ts.map +1 -1
  67. package/node_modules/@buddi/core/dist/index.d.ts +8 -0
  68. package/node_modules/@buddi/core/dist/index.d.ts.map +1 -1
  69. package/node_modules/@buddi/core/dist/index.js +8 -0
  70. package/node_modules/@buddi/core/dist/index.js.map +1 -1
  71. package/node_modules/@buddi/core/dist/lock.d.ts +82 -0
  72. package/node_modules/@buddi/core/dist/lock.d.ts.map +1 -0
  73. package/node_modules/@buddi/core/dist/lock.js +182 -0
  74. package/node_modules/@buddi/core/dist/lock.js.map +1 -0
  75. package/node_modules/@buddi/core/dist/metrics.d.ts.map +1 -1
  76. package/node_modules/@buddi/core/dist/metrics.js +4 -2
  77. package/node_modules/@buddi/core/dist/metrics.js.map +1 -1
  78. package/node_modules/@buddi/core/dist/onboarding/store.d.ts +5 -0
  79. package/node_modules/@buddi/core/dist/onboarding/store.d.ts.map +1 -1
  80. package/node_modules/@buddi/core/dist/onboarding/store.js +35 -5
  81. package/node_modules/@buddi/core/dist/onboarding/store.js.map +1 -1
  82. package/node_modules/@buddi/core/dist/onboarding/types.d.ts +10 -0
  83. package/node_modules/@buddi/core/dist/onboarding/types.d.ts.map +1 -1
  84. package/node_modules/@buddi/core/dist/onboarding/types.js +2 -0
  85. package/node_modules/@buddi/core/dist/onboarding/types.js.map +1 -1
  86. package/node_modules/@buddi/core/dist/owner-format.d.ts +14 -0
  87. package/node_modules/@buddi/core/dist/owner-format.d.ts.map +1 -0
  88. package/node_modules/@buddi/core/dist/owner-format.js +47 -0
  89. package/node_modules/@buddi/core/dist/owner-format.js.map +1 -0
  90. package/node_modules/@buddi/core/dist/owner-timezone.d.ts +46 -0
  91. package/node_modules/@buddi/core/dist/owner-timezone.d.ts.map +1 -0
  92. package/node_modules/@buddi/core/dist/owner-timezone.js +43 -0
  93. package/node_modules/@buddi/core/dist/owner-timezone.js.map +1 -0
  94. package/node_modules/@buddi/core/dist/pages.d.ts +13 -39
  95. package/node_modules/@buddi/core/dist/pages.d.ts.map +1 -1
  96. package/node_modules/@buddi/core/dist/pages.js +12 -1
  97. package/node_modules/@buddi/core/dist/pages.js.map +1 -1
  98. package/node_modules/@buddi/core/dist/places.d.ts +110 -0
  99. package/node_modules/@buddi/core/dist/places.d.ts.map +1 -0
  100. package/node_modules/@buddi/core/dist/places.js +239 -0
  101. package/node_modules/@buddi/core/dist/places.js.map +1 -0
  102. package/node_modules/@buddi/core/dist/plugin/index.d.ts +9 -2
  103. package/node_modules/@buddi/core/dist/plugin/index.d.ts.map +1 -1
  104. package/node_modules/@buddi/core/dist/plugin/index.js +3 -0
  105. package/node_modules/@buddi/core/dist/plugin/index.js.map +1 -1
  106. package/node_modules/@buddi/core/dist/plugin/requires.d.ts +75 -0
  107. package/node_modules/@buddi/core/dist/plugin/requires.d.ts.map +1 -0
  108. package/node_modules/@buddi/core/dist/plugin/requires.js +77 -0
  109. package/node_modules/@buddi/core/dist/plugin/requires.js.map +1 -0
  110. package/node_modules/@buddi/core/dist/plugin/uses.d.ts +1 -1
  111. package/node_modules/@buddi/core/dist/plugin/uses.d.ts.map +1 -1
  112. package/node_modules/@buddi/core/dist/plugin/uses.js +2 -0
  113. package/node_modules/@buddi/core/dist/plugin/uses.js.map +1 -1
  114. package/node_modules/@buddi/core/dist/plugin/version.d.ts +1 -1
  115. package/node_modules/@buddi/core/dist/plugin/version.js +1 -1
  116. package/node_modules/@buddi/core/dist/queue/failures.d.ts +84 -0
  117. package/node_modules/@buddi/core/dist/queue/failures.d.ts.map +1 -0
  118. package/node_modules/@buddi/core/dist/queue/failures.js +195 -0
  119. package/node_modules/@buddi/core/dist/queue/failures.js.map +1 -0
  120. package/node_modules/@buddi/core/dist/queue/index.d.ts +1 -0
  121. package/node_modules/@buddi/core/dist/queue/index.d.ts.map +1 -1
  122. package/node_modules/@buddi/core/dist/queue/index.js +1 -0
  123. package/node_modules/@buddi/core/dist/queue/index.js.map +1 -1
  124. package/node_modules/@buddi/core/dist/queue/jobs.d.ts +39 -3
  125. package/node_modules/@buddi/core/dist/queue/jobs.d.ts.map +1 -1
  126. package/node_modules/@buddi/core/dist/queue/jobs.js +54 -4
  127. package/node_modules/@buddi/core/dist/queue/jobs.js.map +1 -1
  128. package/node_modules/@buddi/core/dist/queue/retry-policy.d.ts +5 -0
  129. package/node_modules/@buddi/core/dist/queue/retry-policy.d.ts.map +1 -1
  130. package/node_modules/@buddi/core/dist/queue/retry-policy.js +19 -2
  131. package/node_modules/@buddi/core/dist/queue/retry-policy.js.map +1 -1
  132. package/node_modules/@buddi/core/dist/queue/types.d.ts +20 -0
  133. package/node_modules/@buddi/core/dist/queue/types.d.ts.map +1 -1
  134. package/node_modules/@buddi/core/dist/queue/types.js +17 -0
  135. package/node_modules/@buddi/core/dist/queue/types.js.map +1 -1
  136. package/node_modules/@buddi/core/dist/registry.d.ts +37 -2
  137. package/node_modules/@buddi/core/dist/registry.d.ts.map +1 -1
  138. package/node_modules/@buddi/core/dist/registry.js +137 -3
  139. package/node_modules/@buddi/core/dist/registry.js.map +1 -1
  140. package/node_modules/@buddi/core/dist/scheduler/enqueue.d.ts +71 -0
  141. package/node_modules/@buddi/core/dist/scheduler/enqueue.d.ts.map +1 -0
  142. package/node_modules/@buddi/core/dist/scheduler/enqueue.js +116 -0
  143. package/node_modules/@buddi/core/dist/scheduler/enqueue.js.map +1 -0
  144. package/node_modules/@buddi/core/dist/scheduler/index.d.ts +1 -0
  145. package/node_modules/@buddi/core/dist/scheduler/index.d.ts.map +1 -1
  146. package/node_modules/@buddi/core/dist/scheduler/index.js +1 -0
  147. package/node_modules/@buddi/core/dist/scheduler/index.js.map +1 -1
  148. package/node_modules/@buddi/core/dist/scheduler/missions.d.ts +15 -0
  149. package/node_modules/@buddi/core/dist/scheduler/missions.d.ts.map +1 -1
  150. package/node_modules/@buddi/core/dist/scheduler/missions.js +43 -4
  151. package/node_modules/@buddi/core/dist/scheduler/missions.js.map +1 -1
  152. package/node_modules/@buddi/core/dist/scheduler/types.d.ts +10 -0
  153. package/node_modules/@buddi/core/dist/scheduler/types.d.ts.map +1 -1
  154. package/node_modules/@buddi/core/dist/scheduler/types.js +3 -0
  155. package/node_modules/@buddi/core/dist/scheduler/types.js.map +1 -1
  156. package/node_modules/@buddi/core/dist/secrets/settings.d.ts +6 -6
  157. package/node_modules/@buddi/core/dist/secrets/settings.d.ts.map +1 -1
  158. package/node_modules/@buddi/core/dist/secrets/settings.js +23 -3
  159. package/node_modules/@buddi/core/dist/secrets/settings.js.map +1 -1
  160. package/node_modules/@buddi/core/dist/semver.d.ts +17 -0
  161. package/node_modules/@buddi/core/dist/semver.d.ts.map +1 -1
  162. package/node_modules/@buddi/core/dist/semver.js +130 -0
  163. package/node_modules/@buddi/core/dist/semver.js.map +1 -1
  164. package/node_modules/@buddi/core/dist/sentinels/digest.d.ts +8 -1
  165. package/node_modules/@buddi/core/dist/sentinels/digest.d.ts.map +1 -1
  166. package/node_modules/@buddi/core/dist/sentinels/digest.js +10 -3
  167. package/node_modules/@buddi/core/dist/sentinels/digest.js.map +1 -1
  168. package/node_modules/@buddi/core/dist/sentinels/index.d.ts +1 -0
  169. package/node_modules/@buddi/core/dist/sentinels/index.d.ts.map +1 -1
  170. package/node_modules/@buddi/core/dist/sentinels/index.js +1 -0
  171. package/node_modules/@buddi/core/dist/sentinels/index.js.map +1 -1
  172. package/node_modules/@buddi/core/dist/sentinels/mutes.d.ts +51 -0
  173. package/node_modules/@buddi/core/dist/sentinels/mutes.d.ts.map +1 -0
  174. package/node_modules/@buddi/core/dist/sentinels/mutes.js +86 -0
  175. package/node_modules/@buddi/core/dist/sentinels/mutes.js.map +1 -0
  176. package/node_modules/@buddi/core/dist/sentinels/run.d.ts +20 -5
  177. package/node_modules/@buddi/core/dist/sentinels/run.d.ts.map +1 -1
  178. package/node_modules/@buddi/core/dist/sentinels/run.js +105 -42
  179. package/node_modules/@buddi/core/dist/sentinels/run.js.map +1 -1
  180. package/node_modules/@buddi/core/dist/sentinels/types.d.ts +165 -2
  181. package/node_modules/@buddi/core/dist/sentinels/types.d.ts.map +1 -1
  182. package/node_modules/@buddi/core/dist/sentinels/types.js +126 -2
  183. package/node_modules/@buddi/core/dist/sentinels/types.js.map +1 -1
  184. package/node_modules/@buddi/core/dist/sources/run.d.ts +8 -0
  185. package/node_modules/@buddi/core/dist/sources/run.d.ts.map +1 -1
  186. package/node_modules/@buddi/core/dist/sources/run.js +1 -1
  187. package/node_modules/@buddi/core/dist/sources/run.js.map +1 -1
  188. package/node_modules/@buddi/core/dist/sources/watch.d.ts +3 -0
  189. package/node_modules/@buddi/core/dist/sources/watch.d.ts.map +1 -1
  190. package/node_modules/@buddi/core/dist/sources/watch.js +1 -1
  191. package/node_modules/@buddi/core/dist/sources/watch.js.map +1 -1
  192. package/node_modules/@buddi/core/dist/time.d.ts +17 -3
  193. package/node_modules/@buddi/core/dist/time.d.ts.map +1 -1
  194. package/node_modules/@buddi/core/dist/time.js +27 -3
  195. package/node_modules/@buddi/core/dist/time.js.map +1 -1
  196. package/node_modules/@buddi/core/dist/tools.d.ts +30 -1
  197. package/node_modules/@buddi/core/dist/tools.d.ts.map +1 -1
  198. package/node_modules/@buddi/core/dist/tools.js.map +1 -1
  199. package/node_modules/@buddi/core/dist/views.d.ts +164 -164
  200. package/node_modules/@buddi/core/dist/views.d.ts.map +1 -1
  201. package/node_modules/@buddi/core/dist/views.js +5 -0
  202. package/node_modules/@buddi/core/dist/views.js.map +1 -1
  203. package/node_modules/@buddi/core/dist/widget-settings.d.ts +161 -0
  204. package/node_modules/@buddi/core/dist/widget-settings.d.ts.map +1 -0
  205. package/node_modules/@buddi/core/dist/widget-settings.js +314 -0
  206. package/node_modules/@buddi/core/dist/widget-settings.js.map +1 -0
  207. package/node_modules/@buddi/core/dist/widgets.d.ts +206 -0
  208. package/node_modules/@buddi/core/dist/widgets.d.ts.map +1 -0
  209. package/node_modules/@buddi/core/dist/widgets.js +247 -0
  210. package/node_modules/@buddi/core/dist/widgets.js.map +1 -0
  211. package/node_modules/@buddi/core/migrations/059_dashboard_lock.sql +33 -0
  212. package/node_modules/@buddi/core/migrations/060_owner_places.sql +30 -0
  213. package/node_modules/@buddi/core/migrations/061_owner_formats.sql +10 -0
  214. package/node_modules/@buddi/core/migrations/062_group_deleted.sql +7 -0
  215. package/node_modules/@buddi/core/migrations/063_provider_account_limits.sql +20 -0
  216. package/node_modules/@buddi/core/migrations/064_mission_coalesce.sql +12 -0
  217. package/node_modules/@buddi/core/migrations/065_api_tokens.sql +28 -0
  218. package/node_modules/@buddi/core/migrations/066_alert_decisions.sql +45 -0
  219. package/node_modules/@buddi/core/migrations/067_job_acknowledged.sql +21 -0
  220. package/node_modules/@buddi/gateway/dist/.tsbuildinfo +1 -1
  221. package/node_modules/@buddi/gateway/dist/account-limits.d.ts +45 -0
  222. package/node_modules/@buddi/gateway/dist/account-limits.d.ts.map +1 -0
  223. package/node_modules/@buddi/gateway/dist/account-limits.js +87 -0
  224. package/node_modules/@buddi/gateway/dist/account-limits.js.map +1 -0
  225. package/node_modules/@buddi/gateway/dist/accounts-cli.d.ts +88 -0
  226. package/node_modules/@buddi/gateway/dist/accounts-cli.d.ts.map +1 -0
  227. package/node_modules/@buddi/gateway/dist/accounts-cli.js +216 -0
  228. package/node_modules/@buddi/gateway/dist/accounts-cli.js.map +1 -0
  229. package/node_modules/@buddi/gateway/dist/agents/canvas.d.ts +20 -20
  230. package/node_modules/@buddi/gateway/dist/agents/learning-digest.d.ts +4 -0
  231. package/node_modules/@buddi/gateway/dist/agents/learning-digest.d.ts.map +1 -1
  232. package/node_modules/@buddi/gateway/dist/agents/learning-digest.js +18 -5
  233. package/node_modules/@buddi/gateway/dist/agents/learning-digest.js.map +1 -1
  234. package/node_modules/@buddi/gateway/dist/agents/owner-tools.d.ts.map +1 -1
  235. package/node_modules/@buddi/gateway/dist/agents/owner-tools.js +3 -2
  236. package/node_modules/@buddi/gateway/dist/agents/owner-tools.js.map +1 -1
  237. package/node_modules/@buddi/gateway/dist/agents/platform.d.ts +24 -24
  238. package/node_modules/@buddi/gateway/dist/api-token-cli.d.ts +17 -0
  239. package/node_modules/@buddi/gateway/dist/api-token-cli.d.ts.map +1 -0
  240. package/node_modules/@buddi/gateway/dist/api-token-cli.js +108 -0
  241. package/node_modules/@buddi/gateway/dist/api-token-cli.js.map +1 -0
  242. package/node_modules/@buddi/gateway/dist/bootstrap.d.ts +6 -2
  243. package/node_modules/@buddi/gateway/dist/bootstrap.d.ts.map +1 -1
  244. package/node_modules/@buddi/gateway/dist/bootstrap.js +25 -7
  245. package/node_modules/@buddi/gateway/dist/bootstrap.js.map +1 -1
  246. package/node_modules/@buddi/gateway/dist/chat/conversations.d.ts.map +1 -1
  247. package/node_modules/@buddi/gateway/dist/chat/conversations.js +8 -2
  248. package/node_modules/@buddi/gateway/dist/chat/conversations.js.map +1 -1
  249. package/node_modules/@buddi/gateway/dist/chat/session.d.ts +5 -0
  250. package/node_modules/@buddi/gateway/dist/chat/session.d.ts.map +1 -1
  251. package/node_modules/@buddi/gateway/dist/chat/session.js +11 -1
  252. package/node_modules/@buddi/gateway/dist/chat/session.js.map +1 -1
  253. package/node_modules/@buddi/gateway/dist/cli.d.ts.map +1 -1
  254. package/node_modules/@buddi/gateway/dist/cli.js +14 -3
  255. package/node_modules/@buddi/gateway/dist/cli.js.map +1 -1
  256. package/node_modules/@buddi/gateway/dist/index.d.ts +3 -0
  257. package/node_modules/@buddi/gateway/dist/index.d.ts.map +1 -1
  258. package/node_modules/@buddi/gateway/dist/index.js +3 -0
  259. package/node_modules/@buddi/gateway/dist/index.js.map +1 -1
  260. package/node_modules/@buddi/gateway/dist/missions/dead-letter.d.ts +2 -1
  261. package/node_modules/@buddi/gateway/dist/missions/dead-letter.d.ts.map +1 -1
  262. package/node_modules/@buddi/gateway/dist/missions/dead-letter.js +19 -5
  263. package/node_modules/@buddi/gateway/dist/missions/dead-letter.js.map +1 -1
  264. package/node_modules/@buddi/gateway/dist/missions/defaults.d.ts +6 -0
  265. package/node_modules/@buddi/gateway/dist/missions/defaults.d.ts.map +1 -1
  266. package/node_modules/@buddi/gateway/dist/missions/defaults.js +10 -2
  267. package/node_modules/@buddi/gateway/dist/missions/defaults.js.map +1 -1
  268. package/node_modules/@buddi/gateway/dist/missions/execute.d.ts.map +1 -1
  269. package/node_modules/@buddi/gateway/dist/missions/execute.js +49 -9
  270. package/node_modules/@buddi/gateway/dist/missions/execute.js.map +1 -1
  271. package/node_modules/@buddi/gateway/dist/missions/goals.d.ts +12 -1
  272. package/node_modules/@buddi/gateway/dist/missions/goals.d.ts.map +1 -1
  273. package/node_modules/@buddi/gateway/dist/missions/goals.js +21 -1
  274. package/node_modules/@buddi/gateway/dist/missions/goals.js.map +1 -1
  275. package/node_modules/@buddi/gateway/dist/missions/inline.js +1 -1
  276. package/node_modules/@buddi/gateway/dist/missions/inline.js.map +1 -1
  277. package/node_modules/@buddi/gateway/dist/missions/sentinel-wake.d.ts +14 -0
  278. package/node_modules/@buddi/gateway/dist/missions/sentinel-wake.d.ts.map +1 -1
  279. package/node_modules/@buddi/gateway/dist/missions/sentinel-wake.js +41 -2
  280. package/node_modules/@buddi/gateway/dist/missions/sentinel-wake.js.map +1 -1
  281. package/node_modules/@buddi/gateway/dist/missions-cli.d.ts.map +1 -1
  282. package/node_modules/@buddi/gateway/dist/missions-cli.js +5 -5
  283. package/node_modules/@buddi/gateway/dist/missions-cli.js.map +1 -1
  284. package/node_modules/@buddi/gateway/dist/nudges-cli.js +2 -2
  285. package/node_modules/@buddi/gateway/dist/nudges-cli.js.map +1 -1
  286. package/node_modules/@buddi/gateway/dist/owner-seed.js +2 -2
  287. package/node_modules/@buddi/gateway/dist/owner-seed.js.map +1 -1
  288. package/node_modules/@buddi/gateway/dist/plugins/live.d.ts +8 -1
  289. package/node_modules/@buddi/gateway/dist/plugins/live.d.ts.map +1 -1
  290. package/node_modules/@buddi/gateway/dist/plugins/live.js +25 -7
  291. package/node_modules/@buddi/gateway/dist/plugins/live.js.map +1 -1
  292. package/node_modules/@buddi/gateway/dist/plugins/load.d.ts +55 -0
  293. package/node_modules/@buddi/gateway/dist/plugins/load.d.ts.map +1 -1
  294. package/node_modules/@buddi/gateway/dist/plugins/load.js +100 -4
  295. package/node_modules/@buddi/gateway/dist/plugins/load.js.map +1 -1
  296. package/node_modules/@buddi/gateway/dist/plugins/requires.d.ts +79 -0
  297. package/node_modules/@buddi/gateway/dist/plugins/requires.d.ts.map +1 -0
  298. package/node_modules/@buddi/gateway/dist/plugins/requires.js +178 -0
  299. package/node_modules/@buddi/gateway/dist/plugins/requires.js.map +1 -0
  300. package/node_modules/@buddi/gateway/dist/plugins/stage.d.ts +6 -0
  301. package/node_modules/@buddi/gateway/dist/plugins/stage.d.ts.map +1 -1
  302. package/node_modules/@buddi/gateway/dist/plugins/stage.js +6 -1
  303. package/node_modules/@buddi/gateway/dist/plugins/stage.js.map +1 -1
  304. package/node_modules/@buddi/gateway/dist/plugins/update.d.ts +21 -0
  305. package/node_modules/@buddi/gateway/dist/plugins/update.d.ts.map +1 -1
  306. package/node_modules/@buddi/gateway/dist/plugins/update.js +16 -0
  307. package/node_modules/@buddi/gateway/dist/plugins/update.js.map +1 -1
  308. package/node_modules/@buddi/gateway/dist/plugins-cli.d.ts +1 -1
  309. package/node_modules/@buddi/gateway/dist/plugins-cli.d.ts.map +1 -1
  310. package/node_modules/@buddi/gateway/dist/plugins-cli.js +6 -2
  311. package/node_modules/@buddi/gateway/dist/plugins-cli.js.map +1 -1
  312. package/node_modules/@buddi/gateway/dist/provider-accounts.d.ts +4 -0
  313. package/node_modules/@buddi/gateway/dist/provider-accounts.d.ts.map +1 -1
  314. package/node_modules/@buddi/gateway/dist/provider-accounts.js +68 -10
  315. package/node_modules/@buddi/gateway/dist/provider-accounts.js.map +1 -1
  316. package/node_modules/@buddi/gateway/dist/provider-diagnostics.d.ts.map +1 -1
  317. package/node_modules/@buddi/gateway/dist/provider-diagnostics.js +9 -0
  318. package/node_modules/@buddi/gateway/dist/provider-diagnostics.js.map +1 -1
  319. package/node_modules/@buddi/gateway/dist/reminders-cli.js +2 -2
  320. package/node_modules/@buddi/gateway/dist/reminders-cli.js.map +1 -1
  321. package/node_modules/@buddi/gateway/dist/serve.d.ts +2 -0
  322. package/node_modules/@buddi/gateway/dist/serve.d.ts.map +1 -1
  323. package/node_modules/@buddi/gateway/dist/serve.js +66 -8
  324. package/node_modules/@buddi/gateway/dist/serve.js.map +1 -1
  325. package/node_modules/@buddi/gateway/dist/surfaces/failure.d.ts.map +1 -1
  326. package/node_modules/@buddi/gateway/dist/surfaces/failure.js +9 -1
  327. package/node_modules/@buddi/gateway/dist/surfaces/failure.js.map +1 -1
  328. package/node_modules/@buddi/gateway/dist/surfaces/offered-actions.d.ts +17 -0
  329. package/node_modules/@buddi/gateway/dist/surfaces/offered-actions.d.ts.map +1 -1
  330. package/node_modules/@buddi/gateway/dist/surfaces/offered-actions.js +27 -0
  331. package/node_modules/@buddi/gateway/dist/surfaces/offered-actions.js.map +1 -1
  332. package/node_modules/@buddi/gateway/dist/system-context.d.ts +18 -0
  333. package/node_modules/@buddi/gateway/dist/system-context.d.ts.map +1 -1
  334. package/node_modules/@buddi/gateway/dist/system-context.js +46 -4
  335. package/node_modules/@buddi/gateway/dist/system-context.js.map +1 -1
  336. package/node_modules/@buddi/gateway/dist/telegram/main.js +3 -3
  337. package/node_modules/@buddi/gateway/dist/telegram/main.js.map +1 -1
  338. package/node_modules/@buddi/gateway/dist/telegram/outbound.d.ts +1 -1
  339. package/node_modules/@buddi/gateway/dist/telegram/outbound.d.ts.map +1 -1
  340. package/node_modules/@buddi/gateway/dist/telegram/outbound.js +7 -2
  341. package/node_modules/@buddi/gateway/dist/telegram/outbound.js.map +1 -1
  342. package/node_modules/@buddi/gateway/dist/telegram/reactions.d.ts.map +1 -1
  343. package/node_modules/@buddi/gateway/dist/telegram/reactions.js +12 -2
  344. package/node_modules/@buddi/gateway/dist/telegram/reactions.js.map +1 -1
  345. package/node_modules/@buddi/gateway/dist/telegram/surface.d.ts +4 -1
  346. package/node_modules/@buddi/gateway/dist/telegram/surface.d.ts.map +1 -1
  347. package/node_modules/@buddi/gateway/dist/telegram/surface.js +13 -3
  348. package/node_modules/@buddi/gateway/dist/telegram/surface.js.map +1 -1
  349. package/node_modules/@buddi/gateway/dist/tips/engine.d.ts +2 -0
  350. package/node_modules/@buddi/gateway/dist/tips/engine.d.ts.map +1 -1
  351. package/node_modules/@buddi/gateway/dist/tips/engine.js +8 -5
  352. package/node_modules/@buddi/gateway/dist/tips/engine.js.map +1 -1
  353. package/node_modules/@buddi/gateway/dist/tips/facts.d.ts +23 -0
  354. package/node_modules/@buddi/gateway/dist/tips/facts.d.ts.map +1 -1
  355. package/node_modules/@buddi/gateway/dist/tips/facts.js +19 -0
  356. package/node_modules/@buddi/gateway/dist/tips/facts.js.map +1 -1
  357. package/node_modules/@buddi/gateway/dist/tips/route.js +2 -2
  358. package/node_modules/@buddi/gateway/dist/tips/route.js.map +1 -1
  359. package/node_modules/@buddi/gateway/dist/tips/rules.d.ts +3 -2
  360. package/node_modules/@buddi/gateway/dist/tips/rules.d.ts.map +1 -1
  361. package/node_modules/@buddi/gateway/dist/tips/rules.js +26 -0
  362. package/node_modules/@buddi/gateway/dist/tips/rules.js.map +1 -1
  363. package/node_modules/@buddi/gateway/dist/web/alerts.d.ts +179 -0
  364. package/node_modules/@buddi/gateway/dist/web/alerts.d.ts.map +1 -0
  365. package/node_modules/@buddi/gateway/dist/web/alerts.js +363 -0
  366. package/node_modules/@buddi/gateway/dist/web/alerts.js.map +1 -0
  367. package/node_modules/@buddi/gateway/dist/web/api-routes.d.ts +103 -0
  368. package/node_modules/@buddi/gateway/dist/web/api-routes.d.ts.map +1 -0
  369. package/node_modules/@buddi/gateway/dist/web/api-routes.js +1239 -0
  370. package/node_modules/@buddi/gateway/dist/web/api-routes.js.map +1 -0
  371. package/node_modules/@buddi/gateway/dist/web/api-scan.d.ts +17 -0
  372. package/node_modules/@buddi/gateway/dist/web/api-scan.d.ts.map +1 -0
  373. package/node_modules/@buddi/gateway/dist/web/api-scan.js +206 -0
  374. package/node_modules/@buddi/gateway/dist/web/api-scan.js.map +1 -0
  375. package/node_modules/@buddi/gateway/dist/web/api-tokens.d.ts +58 -0
  376. package/node_modules/@buddi/gateway/dist/web/api-tokens.d.ts.map +1 -0
  377. package/node_modules/@buddi/gateway/dist/web/api-tokens.js +136 -0
  378. package/node_modules/@buddi/gateway/dist/web/api-tokens.js.map +1 -0
  379. package/node_modules/@buddi/gateway/dist/web/backups.js +2 -2
  380. package/node_modules/@buddi/gateway/dist/web/backups.js.map +1 -1
  381. package/node_modules/@buddi/gateway/dist/web/chat.d.ts +1 -0
  382. package/node_modules/@buddi/gateway/dist/web/chat.d.ts.map +1 -1
  383. package/node_modules/@buddi/gateway/dist/web/chat.js +4 -3
  384. package/node_modules/@buddi/gateway/dist/web/chat.js.map +1 -1
  385. package/node_modules/@buddi/gateway/dist/web/group-lifecycle.d.ts +18 -0
  386. package/node_modules/@buddi/gateway/dist/web/group-lifecycle.d.ts.map +1 -0
  387. package/node_modules/@buddi/gateway/dist/web/group-lifecycle.js +46 -0
  388. package/node_modules/@buddi/gateway/dist/web/group-lifecycle.js.map +1 -0
  389. package/node_modules/@buddi/gateway/dist/web/lock-image.d.ts +37 -0
  390. package/node_modules/@buddi/gateway/dist/web/lock-image.d.ts.map +1 -0
  391. package/node_modules/@buddi/gateway/dist/web/lock-image.js +253 -0
  392. package/node_modules/@buddi/gateway/dist/web/lock-image.js.map +1 -0
  393. package/node_modules/@buddi/gateway/dist/web/lock.d.ts +165 -0
  394. package/node_modules/@buddi/gateway/dist/web/lock.d.ts.map +1 -0
  395. package/node_modules/@buddi/gateway/dist/web/lock.js +478 -0
  396. package/node_modules/@buddi/gateway/dist/web/lock.js.map +1 -0
  397. package/node_modules/@buddi/gateway/dist/web/pages.d.ts +1 -0
  398. package/node_modules/@buddi/gateway/dist/web/pages.d.ts.map +1 -1
  399. package/node_modules/@buddi/gateway/dist/web/pages.js +12 -1
  400. package/node_modules/@buddi/gateway/dist/web/pages.js.map +1 -1
  401. package/node_modules/@buddi/gateway/dist/web/places.d.ts +27 -0
  402. package/node_modules/@buddi/gateway/dist/web/places.d.ts.map +1 -0
  403. package/node_modules/@buddi/gateway/dist/web/places.js +90 -0
  404. package/node_modules/@buddi/gateway/dist/web/places.js.map +1 -0
  405. package/node_modules/@buddi/gateway/dist/web/plugins.d.ts +28 -0
  406. package/node_modules/@buddi/gateway/dist/web/plugins.d.ts.map +1 -1
  407. package/node_modules/@buddi/gateway/dist/web/plugins.js +83 -9
  408. package/node_modules/@buddi/gateway/dist/web/plugins.js.map +1 -1
  409. package/node_modules/@buddi/gateway/dist/web/read.d.ts +52 -26
  410. package/node_modules/@buddi/gateway/dist/web/read.d.ts.map +1 -1
  411. package/node_modules/@buddi/gateway/dist/web/read.js +52 -36
  412. package/node_modules/@buddi/gateway/dist/web/read.js.map +1 -1
  413. package/node_modules/@buddi/gateway/dist/web/secrets.d.ts +61 -1
  414. package/node_modules/@buddi/gateway/dist/web/secrets.d.ts.map +1 -1
  415. package/node_modules/@buddi/gateway/dist/web/secrets.js +111 -3
  416. package/node_modules/@buddi/gateway/dist/web/secrets.js.map +1 -1
  417. package/node_modules/@buddi/gateway/dist/web/server.d.ts +9 -0
  418. package/node_modules/@buddi/gateway/dist/web/server.d.ts.map +1 -1
  419. package/node_modules/@buddi/gateway/dist/web/server.js +531 -46
  420. package/node_modules/@buddi/gateway/dist/web/server.js.map +1 -1
  421. package/node_modules/@buddi/gateway/dist/web/sessions.d.ts +43 -2
  422. package/node_modules/@buddi/gateway/dist/web/sessions.d.ts.map +1 -1
  423. package/node_modules/@buddi/gateway/dist/web/sessions.js +67 -3
  424. package/node_modules/@buddi/gateway/dist/web/sessions.js.map +1 -1
  425. package/node_modules/@buddi/gateway/dist/web/signed-out.d.ts +17 -18
  426. package/node_modules/@buddi/gateway/dist/web/signed-out.d.ts.map +1 -1
  427. package/node_modules/@buddi/gateway/dist/web/signed-out.js +135 -33
  428. package/node_modules/@buddi/gateway/dist/web/signed-out.js.map +1 -1
  429. package/node_modules/@buddi/gateway/dist/web/stream.d.ts.map +1 -1
  430. package/node_modules/@buddi/gateway/dist/web/stream.js +14 -0
  431. package/node_modules/@buddi/gateway/dist/web/stream.js.map +1 -1
  432. package/node_modules/@buddi/gateway/dist/web/tailscale.d.ts +33 -4
  433. package/node_modules/@buddi/gateway/dist/web/tailscale.d.ts.map +1 -1
  434. package/node_modules/@buddi/gateway/dist/web/tailscale.js +76 -16
  435. package/node_modules/@buddi/gateway/dist/web/tailscale.js.map +1 -1
  436. package/node_modules/@buddi/gateway/dist/web/widgets.d.ts +131 -0
  437. package/node_modules/@buddi/gateway/dist/web/widgets.d.ts.map +1 -0
  438. package/node_modules/@buddi/gateway/dist/web/widgets.js +478 -0
  439. package/node_modules/@buddi/gateway/dist/web/widgets.js.map +1 -0
  440. package/node_modules/@buddi/gateway/dist/web/write.d.ts +23 -0
  441. package/node_modules/@buddi/gateway/dist/web/write.d.ts.map +1 -1
  442. package/node_modules/@buddi/gateway/dist/web/write.js +46 -1
  443. package/node_modules/@buddi/gateway/dist/web/write.js.map +1 -1
  444. package/node_modules/@buddi/install/dist/.tsbuildinfo +1 -1
  445. package/node_modules/@buddi/runtime/dist/.tsbuildinfo +1 -1
  446. package/node_modules/@buddi/runtime/dist/anthropic.d.ts +15 -0
  447. package/node_modules/@buddi/runtime/dist/anthropic.d.ts.map +1 -1
  448. package/node_modules/@buddi/runtime/dist/anthropic.js +26 -9
  449. package/node_modules/@buddi/runtime/dist/anthropic.js.map +1 -1
  450. package/node_modules/@buddi/runtime/dist/codex-direct.d.ts +2 -1
  451. package/node_modules/@buddi/runtime/dist/codex-direct.d.ts.map +1 -1
  452. package/node_modules/@buddi/runtime/dist/codex-direct.js +13 -6
  453. package/node_modules/@buddi/runtime/dist/codex-direct.js.map +1 -1
  454. package/node_modules/@buddi/runtime/dist/index.d.ts +1 -0
  455. package/node_modules/@buddi/runtime/dist/index.d.ts.map +1 -1
  456. package/node_modules/@buddi/runtime/dist/index.js +1 -0
  457. package/node_modules/@buddi/runtime/dist/index.js.map +1 -1
  458. package/node_modules/@buddi/runtime/dist/loop.d.ts +2 -0
  459. package/node_modules/@buddi/runtime/dist/loop.d.ts.map +1 -1
  460. package/node_modules/@buddi/runtime/dist/loop.js +22 -0
  461. package/node_modules/@buddi/runtime/dist/loop.js.map +1 -1
  462. package/node_modules/@buddi/runtime/dist/openai.d.ts +34 -1
  463. package/node_modules/@buddi/runtime/dist/openai.d.ts.map +1 -1
  464. package/node_modules/@buddi/runtime/dist/openai.js +85 -19
  465. package/node_modules/@buddi/runtime/dist/openai.js.map +1 -1
  466. package/node_modules/@buddi/runtime/dist/rate-limit.d.ts +91 -0
  467. package/node_modules/@buddi/runtime/dist/rate-limit.d.ts.map +1 -0
  468. package/node_modules/@buddi/runtime/dist/rate-limit.js +291 -0
  469. package/node_modules/@buddi/runtime/dist/rate-limit.js.map +1 -0
  470. package/node_modules/@buddi/runtime/dist/retry.d.ts +40 -0
  471. package/node_modules/@buddi/runtime/dist/retry.d.ts.map +1 -1
  472. package/node_modules/@buddi/runtime/dist/retry.js +42 -8
  473. package/node_modules/@buddi/runtime/dist/retry.js.map +1 -1
  474. package/node_modules/@buddi/tool-artifacts/dist/.tsbuildinfo +1 -1
  475. package/node_modules/@buddi/tool-browser/dist/.tsbuildinfo +1 -1
  476. package/node_modules/@buddi/tool-email/dist/.tsbuildinfo +1 -1
  477. package/node_modules/@buddi/tool-email/dist/agent.js +1 -1
  478. package/node_modules/@buddi/tool-email/dist/folders.d.ts +14 -0
  479. package/node_modules/@buddi/tool-email/dist/folders.d.ts.map +1 -1
  480. package/node_modules/@buddi/tool-email/dist/folders.js +23 -0
  481. package/node_modules/@buddi/tool-email/dist/folders.js.map +1 -1
  482. package/node_modules/@buddi/tool-email/dist/imap/fake.d.ts +55 -0
  483. package/node_modules/@buddi/tool-email/dist/imap/fake.d.ts.map +1 -1
  484. package/node_modules/@buddi/tool-email/dist/imap/fake.js +163 -4
  485. package/node_modules/@buddi/tool-email/dist/imap/fake.js.map +1 -1
  486. package/node_modules/@buddi/tool-email/dist/imap/imapflow-client.d.ts +8 -0
  487. package/node_modules/@buddi/tool-email/dist/imap/imapflow-client.d.ts.map +1 -1
  488. package/node_modules/@buddi/tool-email/dist/imap/imapflow-client.js +124 -4
  489. package/node_modules/@buddi/tool-email/dist/imap/imapflow-client.js.map +1 -1
  490. package/node_modules/@buddi/tool-email/dist/index.d.ts.map +1 -1
  491. package/node_modules/@buddi/tool-email/dist/index.js +3 -0
  492. package/node_modules/@buddi/tool-email/dist/index.js.map +1 -1
  493. package/node_modules/@buddi/tool-email/dist/mail.d.ts +48 -0
  494. package/node_modules/@buddi/tool-email/dist/mail.d.ts.map +1 -1
  495. package/node_modules/@buddi/tool-email/dist/mail.js +119 -0
  496. package/node_modules/@buddi/tool-email/dist/mail.js.map +1 -1
  497. package/node_modules/@buddi/tool-email/dist/mailbox/actions.d.ts +90 -5
  498. package/node_modules/@buddi/tool-email/dist/mailbox/actions.d.ts.map +1 -1
  499. package/node_modules/@buddi/tool-email/dist/mailbox/actions.js +643 -200
  500. package/node_modules/@buddi/tool-email/dist/mailbox/actions.js.map +1 -1
  501. package/node_modules/@buddi/tool-email/dist/mailbox/select.d.ts +6 -0
  502. package/node_modules/@buddi/tool-email/dist/mailbox/select.d.ts.map +1 -1
  503. package/node_modules/@buddi/tool-email/dist/mailbox/select.js +20 -2
  504. package/node_modules/@buddi/tool-email/dist/mailbox/select.js.map +1 -1
  505. package/node_modules/@buddi/tool-email/dist/metrics.d.ts +8 -9
  506. package/node_modules/@buddi/tool-email/dist/metrics.d.ts.map +1 -1
  507. package/node_modules/@buddi/tool-email/dist/metrics.js +8 -9
  508. package/node_modules/@buddi/tool-email/dist/metrics.js.map +1 -1
  509. package/node_modules/@buddi/tool-email/dist/needs-you.d.ts +58 -0
  510. package/node_modules/@buddi/tool-email/dist/needs-you.d.ts.map +1 -0
  511. package/node_modules/@buddi/tool-email/dist/needs-you.js +97 -0
  512. package/node_modules/@buddi/tool-email/dist/needs-you.js.map +1 -0
  513. package/node_modules/@buddi/tool-email/dist/pages/descriptors.d.ts.map +1 -1
  514. package/node_modules/@buddi/tool-email/dist/pages/descriptors.js +126 -80
  515. package/node_modules/@buddi/tool-email/dist/pages/descriptors.js.map +1 -1
  516. package/node_modules/@buddi/tool-email/dist/pages/drafts-tools.d.ts +2 -2
  517. package/node_modules/@buddi/tool-email/dist/pages/queries.d.ts +14 -0
  518. package/node_modules/@buddi/tool-email/dist/pages/queries.d.ts.map +1 -1
  519. package/node_modules/@buddi/tool-email/dist/pages/queries.js +65 -7
  520. package/node_modules/@buddi/tool-email/dist/pages/queries.js.map +1 -1
  521. package/node_modules/@buddi/tool-email/dist/policies/auto.d.ts +9 -9
  522. package/node_modules/@buddi/tool-email/dist/policies/auto.d.ts.map +1 -1
  523. package/node_modules/@buddi/tool-email/dist/policies/auto.js +79 -24
  524. package/node_modules/@buddi/tool-email/dist/policies/auto.js.map +1 -1
  525. package/node_modules/@buddi/tool-email/dist/ports.d.ts +55 -0
  526. package/node_modules/@buddi/tool-email/dist/ports.d.ts.map +1 -1
  527. package/node_modules/@buddi/tool-email/dist/ports.js.map +1 -1
  528. package/node_modules/@buddi/tool-email/dist/sentinels/suspicious-sender.d.ts.map +1 -1
  529. package/node_modules/@buddi/tool-email/dist/sentinels/suspicious-sender.js +4 -1
  530. package/node_modules/@buddi/tool-email/dist/sentinels/suspicious-sender.js.map +1 -1
  531. package/node_modules/@buddi/tool-email/dist/sentinels/waiting-on-me.d.ts +19 -22
  532. package/node_modules/@buddi/tool-email/dist/sentinels/waiting-on-me.d.ts.map +1 -1
  533. package/node_modules/@buddi/tool-email/dist/sentinels/waiting-on-me.js +44 -63
  534. package/node_modules/@buddi/tool-email/dist/sentinels/waiting-on-me.js.map +1 -1
  535. package/node_modules/@buddi/tool-email/dist/sources/idle.d.ts +10 -2
  536. package/node_modules/@buddi/tool-email/dist/sources/idle.d.ts.map +1 -1
  537. package/node_modules/@buddi/tool-email/dist/sources/idle.js +70 -3
  538. package/node_modules/@buddi/tool-email/dist/sources/idle.js.map +1 -1
  539. package/node_modules/@buddi/tool-email/dist/sources/inbox-poll.d.ts +69 -2
  540. package/node_modules/@buddi/tool-email/dist/sources/inbox-poll.d.ts.map +1 -1
  541. package/node_modules/@buddi/tool-email/dist/sources/inbox-poll.js +143 -13
  542. package/node_modules/@buddi/tool-email/dist/sources/inbox-poll.js.map +1 -1
  543. package/node_modules/@buddi/tool-email/dist/sources/presence.d.ts +47 -0
  544. package/node_modules/@buddi/tool-email/dist/sources/presence.d.ts.map +1 -0
  545. package/node_modules/@buddi/tool-email/dist/sources/presence.js +128 -0
  546. package/node_modules/@buddi/tool-email/dist/sources/presence.js.map +1 -0
  547. package/node_modules/@buddi/tool-email/dist/suspicions-store.d.ts +6 -0
  548. package/node_modules/@buddi/tool-email/dist/suspicions-store.d.ts.map +1 -1
  549. package/node_modules/@buddi/tool-email/dist/suspicions-store.js +16 -1
  550. package/node_modules/@buddi/tool-email/dist/suspicions-store.js.map +1 -1
  551. package/node_modules/@buddi/tool-email/dist/threads.d.ts +19 -2
  552. package/node_modules/@buddi/tool-email/dist/threads.d.ts.map +1 -1
  553. package/node_modules/@buddi/tool-email/dist/threads.js +47 -17
  554. package/node_modules/@buddi/tool-email/dist/threads.js.map +1 -1
  555. package/node_modules/@buddi/tool-email/dist/tools/mailbox.d.ts +3 -0
  556. package/node_modules/@buddi/tool-email/dist/tools/mailbox.d.ts.map +1 -1
  557. package/node_modules/@buddi/tool-email/dist/tools/mailbox.js +9 -4
  558. package/node_modules/@buddi/tool-email/dist/tools/mailbox.js.map +1 -1
  559. package/node_modules/@buddi/tool-email/dist/tools/read.d.ts.map +1 -1
  560. package/node_modules/@buddi/tool-email/dist/tools/read.js +29 -2
  561. package/node_modules/@buddi/tool-email/dist/tools/read.js.map +1 -1
  562. package/node_modules/@buddi/tool-email/dist/tools/threads.d.ts +7 -3
  563. package/node_modules/@buddi/tool-email/dist/tools/threads.d.ts.map +1 -1
  564. package/node_modules/@buddi/tool-email/dist/tools/threads.js +18 -5
  565. package/node_modules/@buddi/tool-email/dist/tools/threads.js.map +1 -1
  566. package/node_modules/@buddi/tool-email/dist/watchers.d.ts +91 -7
  567. package/node_modules/@buddi/tool-email/dist/watchers.d.ts.map +1 -1
  568. package/node_modules/@buddi/tool-email/dist/watchers.js +164 -1
  569. package/node_modules/@buddi/tool-email/dist/watchers.js.map +1 -1
  570. package/node_modules/@buddi/tool-email/dist/widgets.d.ts +14 -0
  571. package/node_modules/@buddi/tool-email/dist/widgets.d.ts.map +1 -0
  572. package/node_modules/@buddi/tool-email/dist/widgets.js +51 -0
  573. package/node_modules/@buddi/tool-email/dist/widgets.js.map +1 -0
  574. package/node_modules/@buddi/tool-email/migrations/020_mailbox_presence.sql +44 -0
  575. package/node_modules/@buddi/tool-email/migrations/021_learned_line.sql +9 -0
  576. package/node_modules/@buddi/tool-email/migrations/022_bulk_checked.sql +13 -0
  577. package/node_modules/@buddi/tool-email/migrations/023_attention.sql +100 -0
  578. package/node_modules/@buddi/tool-email/migrations/024_auth_domain.sql +9 -0
  579. package/node_modules/@buddi/tool-email/migrations/025_thread_ignore.sql +77 -0
  580. package/node_modules/@buddi/tool-email/package.json +1 -1
  581. package/node_modules/@buddi/tool-host/dist/.tsbuildinfo +1 -1
  582. package/node_modules/@buddi/tool-mcp/dist/.tsbuildinfo +1 -1
  583. package/node_modules/@buddi/tool-memory/dist/.tsbuildinfo +1 -1
  584. package/node_modules/@buddi/tool-memory/dist/admin.d.ts +9 -0
  585. package/node_modules/@buddi/tool-memory/dist/admin.d.ts.map +1 -1
  586. package/node_modules/@buddi/tool-memory/dist/admin.js +11 -0
  587. package/node_modules/@buddi/tool-memory/dist/admin.js.map +1 -1
  588. package/node_modules/@buddi/tool-web/dist/.tsbuildinfo +1 -1
  589. package/node_modules/ip-address/README.md +34 -34
  590. package/node_modules/ip-address/dist/ipv4.js +7 -0
  591. package/node_modules/ip-address/dist/ipv4.js.map +1 -1
  592. package/node_modules/ip-address/package.json +1 -1
  593. package/package.json +2 -2
  594. package/packages/cli/dist/.tsbuildinfo +1 -1
  595. package/packages/cli/dist/args.d.ts +19 -3
  596. package/packages/cli/dist/args.d.ts.map +1 -1
  597. package/packages/cli/dist/args.js +19 -5
  598. package/packages/cli/dist/args.js.map +1 -1
  599. package/packages/cli/dist/commands.d.ts.map +1 -1
  600. package/packages/cli/dist/commands.js +80 -8
  601. package/packages/cli/dist/commands.js.map +1 -1
  602. package/packages/cli/dist/dashboard-cmd.d.ts +3 -0
  603. package/packages/cli/dist/dashboard-cmd.d.ts.map +1 -1
  604. package/packages/cli/dist/dashboard-cmd.js +63 -0
  605. package/packages/cli/dist/dashboard-cmd.js.map +1 -1
  606. package/packages/cli/dist/doctor-probes.d.ts.map +1 -1
  607. package/packages/cli/dist/doctor-probes.js +11 -4
  608. package/packages/cli/dist/doctor-probes.js.map +1 -1
  609. package/packages/cli/dist/doctor.d.ts +1 -1
  610. package/packages/cli/dist/doctor.d.ts.map +1 -1
  611. package/packages/cli/dist/jobs-cmd.d.ts +4 -0
  612. package/packages/cli/dist/jobs-cmd.d.ts.map +1 -1
  613. package/packages/cli/dist/jobs-cmd.js +53 -4
  614. package/packages/cli/dist/jobs-cmd.js.map +1 -1
  615. package/packages/cli/dist/main.d.ts.map +1 -1
  616. package/packages/cli/dist/main.js +27 -2
  617. package/packages/cli/dist/main.js.map +1 -1
  618. package/packages/cli/dist/mcp/gateway-client.d.ts +19 -17
  619. package/packages/cli/dist/mcp/gateway-client.d.ts.map +1 -1
  620. package/packages/cli/dist/mcp/gateway-client.js +128 -29
  621. package/packages/cli/dist/mcp/gateway-client.js.map +1 -1
  622. package/packages/cli/dist/mcp/tools.d.ts +6 -0
  623. package/packages/cli/dist/mcp/tools.d.ts.map +1 -1
  624. package/packages/cli/dist/mcp/tools.js +20 -1
  625. package/packages/cli/dist/mcp/tools.js.map +1 -1
  626. package/packages/cli/dist/telegram-cmd.d.ts.map +1 -1
  627. package/packages/cli/dist/telegram-cmd.js +5 -4
  628. package/packages/cli/dist/telegram-cmd.js.map +1 -1
  629. package/packages/core/dist/.tsbuildinfo +1 -1
  630. package/packages/core/dist/agents/catalog.d.ts.map +1 -1
  631. package/packages/core/dist/agents/catalog.js +7 -6
  632. package/packages/core/dist/agents/catalog.js.map +1 -1
  633. package/packages/core/dist/agents/frontmatter.d.ts +12 -12
  634. package/packages/core/dist/agents/skills.d.ts +2 -2
  635. package/packages/core/dist/clock-widget.d.ts +38 -0
  636. package/packages/core/dist/clock-widget.d.ts.map +1 -0
  637. package/packages/core/dist/clock-widget.js +114 -0
  638. package/packages/core/dist/clock-widget.js.map +1 -0
  639. package/packages/core/dist/failures/owner-message.d.ts +25 -0
  640. package/packages/core/dist/failures/owner-message.d.ts.map +1 -1
  641. package/packages/core/dist/failures/owner-message.js +90 -0
  642. package/packages/core/dist/failures/owner-message.js.map +1 -1
  643. package/packages/core/dist/feedback/index.d.ts +23 -2
  644. package/packages/core/dist/feedback/index.d.ts.map +1 -1
  645. package/packages/core/dist/feedback/index.js +23 -3
  646. package/packages/core/dist/feedback/index.js.map +1 -1
  647. package/packages/core/dist/groups.d.ts +33 -0
  648. package/packages/core/dist/groups.d.ts.map +1 -1
  649. package/packages/core/dist/groups.js +76 -4
  650. package/packages/core/dist/groups.js.map +1 -1
  651. package/packages/core/dist/home.d.ts +5 -5
  652. package/packages/core/dist/host/build.d.ts +19 -1
  653. package/packages/core/dist/host/build.d.ts.map +1 -1
  654. package/packages/core/dist/host/build.js +122 -2
  655. package/packages/core/dist/host/build.js.map +1 -1
  656. package/packages/core/dist/host/types.d.ts +33 -0
  657. package/packages/core/dist/host/types.d.ts.map +1 -1
  658. package/packages/core/dist/index.d.ts +8 -0
  659. package/packages/core/dist/index.d.ts.map +1 -1
  660. package/packages/core/dist/index.js +8 -0
  661. package/packages/core/dist/index.js.map +1 -1
  662. package/packages/core/dist/lock.d.ts +82 -0
  663. package/packages/core/dist/lock.d.ts.map +1 -0
  664. package/packages/core/dist/lock.js +182 -0
  665. package/packages/core/dist/lock.js.map +1 -0
  666. package/packages/core/dist/metrics.d.ts.map +1 -1
  667. package/packages/core/dist/metrics.js +4 -2
  668. package/packages/core/dist/metrics.js.map +1 -1
  669. package/packages/core/dist/onboarding/store.d.ts +5 -0
  670. package/packages/core/dist/onboarding/store.d.ts.map +1 -1
  671. package/packages/core/dist/onboarding/store.js +35 -5
  672. package/packages/core/dist/onboarding/store.js.map +1 -1
  673. package/packages/core/dist/onboarding/types.d.ts +10 -0
  674. package/packages/core/dist/onboarding/types.d.ts.map +1 -1
  675. package/packages/core/dist/onboarding/types.js +2 -0
  676. package/packages/core/dist/onboarding/types.js.map +1 -1
  677. package/packages/core/dist/owner-format.d.ts +14 -0
  678. package/packages/core/dist/owner-format.d.ts.map +1 -0
  679. package/packages/core/dist/owner-format.js +47 -0
  680. package/packages/core/dist/owner-format.js.map +1 -0
  681. package/packages/core/dist/owner-timezone.d.ts +46 -0
  682. package/packages/core/dist/owner-timezone.d.ts.map +1 -0
  683. package/packages/core/dist/owner-timezone.js +43 -0
  684. package/packages/core/dist/owner-timezone.js.map +1 -0
  685. package/packages/core/dist/pages.d.ts +13 -39
  686. package/packages/core/dist/pages.d.ts.map +1 -1
  687. package/packages/core/dist/pages.js +12 -1
  688. package/packages/core/dist/pages.js.map +1 -1
  689. package/packages/core/dist/places.d.ts +110 -0
  690. package/packages/core/dist/places.d.ts.map +1 -0
  691. package/packages/core/dist/places.js +239 -0
  692. package/packages/core/dist/places.js.map +1 -0
  693. package/packages/core/dist/plugin/index.d.ts +9 -2
  694. package/packages/core/dist/plugin/index.d.ts.map +1 -1
  695. package/packages/core/dist/plugin/index.js +3 -0
  696. package/packages/core/dist/plugin/index.js.map +1 -1
  697. package/packages/core/dist/plugin/requires.d.ts +75 -0
  698. package/packages/core/dist/plugin/requires.d.ts.map +1 -0
  699. package/packages/core/dist/plugin/requires.js +77 -0
  700. package/packages/core/dist/plugin/requires.js.map +1 -0
  701. package/packages/core/dist/plugin/uses.d.ts +1 -1
  702. package/packages/core/dist/plugin/uses.d.ts.map +1 -1
  703. package/packages/core/dist/plugin/uses.js +2 -0
  704. package/packages/core/dist/plugin/uses.js.map +1 -1
  705. package/packages/core/dist/plugin/version.d.ts +1 -1
  706. package/packages/core/dist/plugin/version.js +1 -1
  707. package/packages/core/dist/queue/failures.d.ts +84 -0
  708. package/packages/core/dist/queue/failures.d.ts.map +1 -0
  709. package/packages/core/dist/queue/failures.js +195 -0
  710. package/packages/core/dist/queue/failures.js.map +1 -0
  711. package/packages/core/dist/queue/index.d.ts +1 -0
  712. package/packages/core/dist/queue/index.d.ts.map +1 -1
  713. package/packages/core/dist/queue/index.js +1 -0
  714. package/packages/core/dist/queue/index.js.map +1 -1
  715. package/packages/core/dist/queue/jobs.d.ts +39 -3
  716. package/packages/core/dist/queue/jobs.d.ts.map +1 -1
  717. package/packages/core/dist/queue/jobs.js +54 -4
  718. package/packages/core/dist/queue/jobs.js.map +1 -1
  719. package/packages/core/dist/queue/retry-policy.d.ts +5 -0
  720. package/packages/core/dist/queue/retry-policy.d.ts.map +1 -1
  721. package/packages/core/dist/queue/retry-policy.js +19 -2
  722. package/packages/core/dist/queue/retry-policy.js.map +1 -1
  723. package/packages/core/dist/queue/types.d.ts +20 -0
  724. package/packages/core/dist/queue/types.d.ts.map +1 -1
  725. package/packages/core/dist/queue/types.js +17 -0
  726. package/packages/core/dist/queue/types.js.map +1 -1
  727. package/packages/core/dist/registry.d.ts +37 -2
  728. package/packages/core/dist/registry.d.ts.map +1 -1
  729. package/packages/core/dist/registry.js +137 -3
  730. package/packages/core/dist/registry.js.map +1 -1
  731. package/packages/core/dist/scheduler/enqueue.d.ts +71 -0
  732. package/packages/core/dist/scheduler/enqueue.d.ts.map +1 -0
  733. package/packages/core/dist/scheduler/enqueue.js +116 -0
  734. package/packages/core/dist/scheduler/enqueue.js.map +1 -0
  735. package/packages/core/dist/scheduler/index.d.ts +1 -0
  736. package/packages/core/dist/scheduler/index.d.ts.map +1 -1
  737. package/packages/core/dist/scheduler/index.js +1 -0
  738. package/packages/core/dist/scheduler/index.js.map +1 -1
  739. package/packages/core/dist/scheduler/missions.d.ts +15 -0
  740. package/packages/core/dist/scheduler/missions.d.ts.map +1 -1
  741. package/packages/core/dist/scheduler/missions.js +43 -4
  742. package/packages/core/dist/scheduler/missions.js.map +1 -1
  743. package/packages/core/dist/scheduler/types.d.ts +10 -0
  744. package/packages/core/dist/scheduler/types.d.ts.map +1 -1
  745. package/packages/core/dist/scheduler/types.js +3 -0
  746. package/packages/core/dist/scheduler/types.js.map +1 -1
  747. package/packages/core/dist/secrets/settings.d.ts +6 -6
  748. package/packages/core/dist/secrets/settings.d.ts.map +1 -1
  749. package/packages/core/dist/secrets/settings.js +23 -3
  750. package/packages/core/dist/secrets/settings.js.map +1 -1
  751. package/packages/core/dist/semver.d.ts +17 -0
  752. package/packages/core/dist/semver.d.ts.map +1 -1
  753. package/packages/core/dist/semver.js +130 -0
  754. package/packages/core/dist/semver.js.map +1 -1
  755. package/packages/core/dist/sentinels/digest.d.ts +8 -1
  756. package/packages/core/dist/sentinels/digest.d.ts.map +1 -1
  757. package/packages/core/dist/sentinels/digest.js +10 -3
  758. package/packages/core/dist/sentinels/digest.js.map +1 -1
  759. package/packages/core/dist/sentinels/index.d.ts +1 -0
  760. package/packages/core/dist/sentinels/index.d.ts.map +1 -1
  761. package/packages/core/dist/sentinels/index.js +1 -0
  762. package/packages/core/dist/sentinels/index.js.map +1 -1
  763. package/packages/core/dist/sentinels/mutes.d.ts +51 -0
  764. package/packages/core/dist/sentinels/mutes.d.ts.map +1 -0
  765. package/packages/core/dist/sentinels/mutes.js +86 -0
  766. package/packages/core/dist/sentinels/mutes.js.map +1 -0
  767. package/packages/core/dist/sentinels/run.d.ts +20 -5
  768. package/packages/core/dist/sentinels/run.d.ts.map +1 -1
  769. package/packages/core/dist/sentinels/run.js +105 -42
  770. package/packages/core/dist/sentinels/run.js.map +1 -1
  771. package/packages/core/dist/sentinels/types.d.ts +165 -2
  772. package/packages/core/dist/sentinels/types.d.ts.map +1 -1
  773. package/packages/core/dist/sentinels/types.js +126 -2
  774. package/packages/core/dist/sentinels/types.js.map +1 -1
  775. package/packages/core/dist/sources/run.d.ts +8 -0
  776. package/packages/core/dist/sources/run.d.ts.map +1 -1
  777. package/packages/core/dist/sources/run.js +1 -1
  778. package/packages/core/dist/sources/run.js.map +1 -1
  779. package/packages/core/dist/sources/watch.d.ts +3 -0
  780. package/packages/core/dist/sources/watch.d.ts.map +1 -1
  781. package/packages/core/dist/sources/watch.js +1 -1
  782. package/packages/core/dist/sources/watch.js.map +1 -1
  783. package/packages/core/dist/time.d.ts +17 -3
  784. package/packages/core/dist/time.d.ts.map +1 -1
  785. package/packages/core/dist/time.js +27 -3
  786. package/packages/core/dist/time.js.map +1 -1
  787. package/packages/core/dist/tools.d.ts +30 -1
  788. package/packages/core/dist/tools.d.ts.map +1 -1
  789. package/packages/core/dist/tools.js.map +1 -1
  790. package/packages/core/dist/views.d.ts +164 -164
  791. package/packages/core/dist/views.d.ts.map +1 -1
  792. package/packages/core/dist/views.js +5 -0
  793. package/packages/core/dist/views.js.map +1 -1
  794. package/packages/core/dist/widget-settings.d.ts +161 -0
  795. package/packages/core/dist/widget-settings.d.ts.map +1 -0
  796. package/packages/core/dist/widget-settings.js +314 -0
  797. package/packages/core/dist/widget-settings.js.map +1 -0
  798. package/packages/core/dist/widgets.d.ts +206 -0
  799. package/packages/core/dist/widgets.d.ts.map +1 -0
  800. package/packages/core/dist/widgets.js +247 -0
  801. package/packages/core/dist/widgets.js.map +1 -0
  802. package/packages/core/migrations/059_dashboard_lock.sql +33 -0
  803. package/packages/core/migrations/060_owner_places.sql +30 -0
  804. package/packages/core/migrations/061_owner_formats.sql +10 -0
  805. package/packages/core/migrations/062_group_deleted.sql +7 -0
  806. package/packages/core/migrations/063_provider_account_limits.sql +20 -0
  807. package/packages/core/migrations/064_mission_coalesce.sql +12 -0
  808. package/packages/core/migrations/065_api_tokens.sql +28 -0
  809. package/packages/core/migrations/066_alert_decisions.sql +45 -0
  810. package/packages/core/migrations/067_job_acknowledged.sql +21 -0
  811. package/packages/gateway/dist/.tsbuildinfo +1 -1
  812. package/packages/gateway/dist/account-limits.d.ts +45 -0
  813. package/packages/gateway/dist/account-limits.d.ts.map +1 -0
  814. package/packages/gateway/dist/account-limits.js +87 -0
  815. package/packages/gateway/dist/account-limits.js.map +1 -0
  816. package/packages/gateway/dist/accounts-cli.d.ts +88 -0
  817. package/packages/gateway/dist/accounts-cli.d.ts.map +1 -0
  818. package/packages/gateway/dist/accounts-cli.js +216 -0
  819. package/packages/gateway/dist/accounts-cli.js.map +1 -0
  820. package/packages/gateway/dist/agents/canvas.d.ts +20 -20
  821. package/packages/gateway/dist/agents/learning-digest.d.ts +4 -0
  822. package/packages/gateway/dist/agents/learning-digest.d.ts.map +1 -1
  823. package/packages/gateway/dist/agents/learning-digest.js +18 -5
  824. package/packages/gateway/dist/agents/learning-digest.js.map +1 -1
  825. package/packages/gateway/dist/agents/owner-tools.d.ts.map +1 -1
  826. package/packages/gateway/dist/agents/owner-tools.js +3 -2
  827. package/packages/gateway/dist/agents/owner-tools.js.map +1 -1
  828. package/packages/gateway/dist/agents/platform.d.ts +24 -24
  829. package/packages/gateway/dist/api-token-cli.d.ts +17 -0
  830. package/packages/gateway/dist/api-token-cli.d.ts.map +1 -0
  831. package/packages/gateway/dist/api-token-cli.js +108 -0
  832. package/packages/gateway/dist/api-token-cli.js.map +1 -0
  833. package/packages/gateway/dist/bootstrap.d.ts +6 -2
  834. package/packages/gateway/dist/bootstrap.d.ts.map +1 -1
  835. package/packages/gateway/dist/bootstrap.js +25 -7
  836. package/packages/gateway/dist/bootstrap.js.map +1 -1
  837. package/packages/gateway/dist/chat/conversations.d.ts.map +1 -1
  838. package/packages/gateway/dist/chat/conversations.js +8 -2
  839. package/packages/gateway/dist/chat/conversations.js.map +1 -1
  840. package/packages/gateway/dist/chat/session.d.ts +5 -0
  841. package/packages/gateway/dist/chat/session.d.ts.map +1 -1
  842. package/packages/gateway/dist/chat/session.js +11 -1
  843. package/packages/gateway/dist/chat/session.js.map +1 -1
  844. package/packages/gateway/dist/cli.d.ts.map +1 -1
  845. package/packages/gateway/dist/cli.js +14 -3
  846. package/packages/gateway/dist/cli.js.map +1 -1
  847. package/packages/gateway/dist/index.d.ts +3 -0
  848. package/packages/gateway/dist/index.d.ts.map +1 -1
  849. package/packages/gateway/dist/index.js +3 -0
  850. package/packages/gateway/dist/index.js.map +1 -1
  851. package/packages/gateway/dist/missions/dead-letter.d.ts +2 -1
  852. package/packages/gateway/dist/missions/dead-letter.d.ts.map +1 -1
  853. package/packages/gateway/dist/missions/dead-letter.js +19 -5
  854. package/packages/gateway/dist/missions/dead-letter.js.map +1 -1
  855. package/packages/gateway/dist/missions/defaults.d.ts +6 -0
  856. package/packages/gateway/dist/missions/defaults.d.ts.map +1 -1
  857. package/packages/gateway/dist/missions/defaults.js +10 -2
  858. package/packages/gateway/dist/missions/defaults.js.map +1 -1
  859. package/packages/gateway/dist/missions/execute.d.ts.map +1 -1
  860. package/packages/gateway/dist/missions/execute.js +49 -9
  861. package/packages/gateway/dist/missions/execute.js.map +1 -1
  862. package/packages/gateway/dist/missions/goals.d.ts +12 -1
  863. package/packages/gateway/dist/missions/goals.d.ts.map +1 -1
  864. package/packages/gateway/dist/missions/goals.js +21 -1
  865. package/packages/gateway/dist/missions/goals.js.map +1 -1
  866. package/packages/gateway/dist/missions/inline.js +1 -1
  867. package/packages/gateway/dist/missions/inline.js.map +1 -1
  868. package/packages/gateway/dist/missions/sentinel-wake.d.ts +14 -0
  869. package/packages/gateway/dist/missions/sentinel-wake.d.ts.map +1 -1
  870. package/packages/gateway/dist/missions/sentinel-wake.js +41 -2
  871. package/packages/gateway/dist/missions/sentinel-wake.js.map +1 -1
  872. package/packages/gateway/dist/missions-cli.d.ts.map +1 -1
  873. package/packages/gateway/dist/missions-cli.js +5 -5
  874. package/packages/gateway/dist/missions-cli.js.map +1 -1
  875. package/packages/gateway/dist/nudges-cli.js +2 -2
  876. package/packages/gateway/dist/nudges-cli.js.map +1 -1
  877. package/packages/gateway/dist/owner-seed.js +2 -2
  878. package/packages/gateway/dist/owner-seed.js.map +1 -1
  879. package/packages/gateway/dist/plugins/live.d.ts +8 -1
  880. package/packages/gateway/dist/plugins/live.d.ts.map +1 -1
  881. package/packages/gateway/dist/plugins/live.js +25 -7
  882. package/packages/gateway/dist/plugins/live.js.map +1 -1
  883. package/packages/gateway/dist/plugins/load.d.ts +55 -0
  884. package/packages/gateway/dist/plugins/load.d.ts.map +1 -1
  885. package/packages/gateway/dist/plugins/load.js +100 -4
  886. package/packages/gateway/dist/plugins/load.js.map +1 -1
  887. package/packages/gateway/dist/plugins/requires.d.ts +79 -0
  888. package/packages/gateway/dist/plugins/requires.d.ts.map +1 -0
  889. package/packages/gateway/dist/plugins/requires.js +178 -0
  890. package/packages/gateway/dist/plugins/requires.js.map +1 -0
  891. package/packages/gateway/dist/plugins/stage.d.ts +6 -0
  892. package/packages/gateway/dist/plugins/stage.d.ts.map +1 -1
  893. package/packages/gateway/dist/plugins/stage.js +6 -1
  894. package/packages/gateway/dist/plugins/stage.js.map +1 -1
  895. package/packages/gateway/dist/plugins/update.d.ts +21 -0
  896. package/packages/gateway/dist/plugins/update.d.ts.map +1 -1
  897. package/packages/gateway/dist/plugins/update.js +16 -0
  898. package/packages/gateway/dist/plugins/update.js.map +1 -1
  899. package/packages/gateway/dist/plugins-cli.d.ts +1 -1
  900. package/packages/gateway/dist/plugins-cli.d.ts.map +1 -1
  901. package/packages/gateway/dist/plugins-cli.js +6 -2
  902. package/packages/gateway/dist/plugins-cli.js.map +1 -1
  903. package/packages/gateway/dist/provider-accounts.d.ts +4 -0
  904. package/packages/gateway/dist/provider-accounts.d.ts.map +1 -1
  905. package/packages/gateway/dist/provider-accounts.js +68 -10
  906. package/packages/gateway/dist/provider-accounts.js.map +1 -1
  907. package/packages/gateway/dist/provider-diagnostics.d.ts.map +1 -1
  908. package/packages/gateway/dist/provider-diagnostics.js +9 -0
  909. package/packages/gateway/dist/provider-diagnostics.js.map +1 -1
  910. package/packages/gateway/dist/reminders-cli.js +2 -2
  911. package/packages/gateway/dist/reminders-cli.js.map +1 -1
  912. package/packages/gateway/dist/serve.d.ts +2 -0
  913. package/packages/gateway/dist/serve.d.ts.map +1 -1
  914. package/packages/gateway/dist/serve.js +66 -8
  915. package/packages/gateway/dist/serve.js.map +1 -1
  916. package/packages/gateway/dist/surfaces/failure.d.ts.map +1 -1
  917. package/packages/gateway/dist/surfaces/failure.js +9 -1
  918. package/packages/gateway/dist/surfaces/failure.js.map +1 -1
  919. package/packages/gateway/dist/surfaces/offered-actions.d.ts +17 -0
  920. package/packages/gateway/dist/surfaces/offered-actions.d.ts.map +1 -1
  921. package/packages/gateway/dist/surfaces/offered-actions.js +27 -0
  922. package/packages/gateway/dist/surfaces/offered-actions.js.map +1 -1
  923. package/packages/gateway/dist/system-context.d.ts +18 -0
  924. package/packages/gateway/dist/system-context.d.ts.map +1 -1
  925. package/packages/gateway/dist/system-context.js +46 -4
  926. package/packages/gateway/dist/system-context.js.map +1 -1
  927. package/packages/gateway/dist/telegram/main.js +3 -3
  928. package/packages/gateway/dist/telegram/main.js.map +1 -1
  929. package/packages/gateway/dist/telegram/outbound.d.ts +1 -1
  930. package/packages/gateway/dist/telegram/outbound.d.ts.map +1 -1
  931. package/packages/gateway/dist/telegram/outbound.js +7 -2
  932. package/packages/gateway/dist/telegram/outbound.js.map +1 -1
  933. package/packages/gateway/dist/telegram/reactions.d.ts.map +1 -1
  934. package/packages/gateway/dist/telegram/reactions.js +12 -2
  935. package/packages/gateway/dist/telegram/reactions.js.map +1 -1
  936. package/packages/gateway/dist/telegram/surface.d.ts +4 -1
  937. package/packages/gateway/dist/telegram/surface.d.ts.map +1 -1
  938. package/packages/gateway/dist/telegram/surface.js +13 -3
  939. package/packages/gateway/dist/telegram/surface.js.map +1 -1
  940. package/packages/gateway/dist/tips/engine.d.ts +2 -0
  941. package/packages/gateway/dist/tips/engine.d.ts.map +1 -1
  942. package/packages/gateway/dist/tips/engine.js +8 -5
  943. package/packages/gateway/dist/tips/engine.js.map +1 -1
  944. package/packages/gateway/dist/tips/facts.d.ts +23 -0
  945. package/packages/gateway/dist/tips/facts.d.ts.map +1 -1
  946. package/packages/gateway/dist/tips/facts.js +19 -0
  947. package/packages/gateway/dist/tips/facts.js.map +1 -1
  948. package/packages/gateway/dist/tips/route.js +2 -2
  949. package/packages/gateway/dist/tips/route.js.map +1 -1
  950. package/packages/gateway/dist/tips/rules.d.ts +3 -2
  951. package/packages/gateway/dist/tips/rules.d.ts.map +1 -1
  952. package/packages/gateway/dist/tips/rules.js +26 -0
  953. package/packages/gateway/dist/tips/rules.js.map +1 -1
  954. package/packages/gateway/dist/web/alerts.d.ts +179 -0
  955. package/packages/gateway/dist/web/alerts.d.ts.map +1 -0
  956. package/packages/gateway/dist/web/alerts.js +363 -0
  957. package/packages/gateway/dist/web/alerts.js.map +1 -0
  958. package/packages/gateway/dist/web/api-routes.d.ts +103 -0
  959. package/packages/gateway/dist/web/api-routes.d.ts.map +1 -0
  960. package/packages/gateway/dist/web/api-routes.js +1239 -0
  961. package/packages/gateway/dist/web/api-routes.js.map +1 -0
  962. package/packages/gateway/dist/web/api-scan.d.ts +17 -0
  963. package/packages/gateway/dist/web/api-scan.d.ts.map +1 -0
  964. package/packages/gateway/dist/web/api-scan.js +206 -0
  965. package/packages/gateway/dist/web/api-scan.js.map +1 -0
  966. package/packages/gateway/dist/web/api-tokens.d.ts +58 -0
  967. package/packages/gateway/dist/web/api-tokens.d.ts.map +1 -0
  968. package/packages/gateway/dist/web/api-tokens.js +136 -0
  969. package/packages/gateway/dist/web/api-tokens.js.map +1 -0
  970. package/packages/gateway/dist/web/backups.js +2 -2
  971. package/packages/gateway/dist/web/backups.js.map +1 -1
  972. package/packages/gateway/dist/web/chat.d.ts +1 -0
  973. package/packages/gateway/dist/web/chat.d.ts.map +1 -1
  974. package/packages/gateway/dist/web/chat.js +4 -3
  975. package/packages/gateway/dist/web/chat.js.map +1 -1
  976. package/packages/gateway/dist/web/group-lifecycle.d.ts +18 -0
  977. package/packages/gateway/dist/web/group-lifecycle.d.ts.map +1 -0
  978. package/packages/gateway/dist/web/group-lifecycle.js +46 -0
  979. package/packages/gateway/dist/web/group-lifecycle.js.map +1 -0
  980. package/packages/gateway/dist/web/lock-image.d.ts +37 -0
  981. package/packages/gateway/dist/web/lock-image.d.ts.map +1 -0
  982. package/packages/gateway/dist/web/lock-image.js +253 -0
  983. package/packages/gateway/dist/web/lock-image.js.map +1 -0
  984. package/packages/gateway/dist/web/lock.d.ts +165 -0
  985. package/packages/gateway/dist/web/lock.d.ts.map +1 -0
  986. package/packages/gateway/dist/web/lock.js +478 -0
  987. package/packages/gateway/dist/web/lock.js.map +1 -0
  988. package/packages/gateway/dist/web/pages.d.ts +1 -0
  989. package/packages/gateway/dist/web/pages.d.ts.map +1 -1
  990. package/packages/gateway/dist/web/pages.js +12 -1
  991. package/packages/gateway/dist/web/pages.js.map +1 -1
  992. package/packages/gateway/dist/web/places.d.ts +27 -0
  993. package/packages/gateway/dist/web/places.d.ts.map +1 -0
  994. package/packages/gateway/dist/web/places.js +90 -0
  995. package/packages/gateway/dist/web/places.js.map +1 -0
  996. package/packages/gateway/dist/web/plugins.d.ts +28 -0
  997. package/packages/gateway/dist/web/plugins.d.ts.map +1 -1
  998. package/packages/gateway/dist/web/plugins.js +83 -9
  999. package/packages/gateway/dist/web/plugins.js.map +1 -1
  1000. package/packages/gateway/dist/web/read.d.ts +52 -26
  1001. package/packages/gateway/dist/web/read.d.ts.map +1 -1
  1002. package/packages/gateway/dist/web/read.js +52 -36
  1003. package/packages/gateway/dist/web/read.js.map +1 -1
  1004. package/packages/gateway/dist/web/secrets.d.ts +61 -1
  1005. package/packages/gateway/dist/web/secrets.d.ts.map +1 -1
  1006. package/packages/gateway/dist/web/secrets.js +111 -3
  1007. package/packages/gateway/dist/web/secrets.js.map +1 -1
  1008. package/packages/gateway/dist/web/server.d.ts +9 -0
  1009. package/packages/gateway/dist/web/server.d.ts.map +1 -1
  1010. package/packages/gateway/dist/web/server.js +531 -46
  1011. package/packages/gateway/dist/web/server.js.map +1 -1
  1012. package/packages/gateway/dist/web/sessions.d.ts +43 -2
  1013. package/packages/gateway/dist/web/sessions.d.ts.map +1 -1
  1014. package/packages/gateway/dist/web/sessions.js +67 -3
  1015. package/packages/gateway/dist/web/sessions.js.map +1 -1
  1016. package/packages/gateway/dist/web/signed-out.d.ts +17 -18
  1017. package/packages/gateway/dist/web/signed-out.d.ts.map +1 -1
  1018. package/packages/gateway/dist/web/signed-out.js +135 -33
  1019. package/packages/gateway/dist/web/signed-out.js.map +1 -1
  1020. package/packages/gateway/dist/web/stream.d.ts.map +1 -1
  1021. package/packages/gateway/dist/web/stream.js +14 -0
  1022. package/packages/gateway/dist/web/stream.js.map +1 -1
  1023. package/packages/gateway/dist/web/tailscale.d.ts +33 -4
  1024. package/packages/gateway/dist/web/tailscale.d.ts.map +1 -1
  1025. package/packages/gateway/dist/web/tailscale.js +76 -16
  1026. package/packages/gateway/dist/web/tailscale.js.map +1 -1
  1027. package/packages/gateway/dist/web/widgets.d.ts +131 -0
  1028. package/packages/gateway/dist/web/widgets.d.ts.map +1 -0
  1029. package/packages/gateway/dist/web/widgets.js +478 -0
  1030. package/packages/gateway/dist/web/widgets.js.map +1 -0
  1031. package/packages/gateway/dist/web/write.d.ts +23 -0
  1032. package/packages/gateway/dist/web/write.d.ts.map +1 -1
  1033. package/packages/gateway/dist/web/write.js +46 -1
  1034. package/packages/gateway/dist/web/write.js.map +1 -1
  1035. package/packages/install/dist/.tsbuildinfo +1 -1
  1036. package/packages/runtime/dist/.tsbuildinfo +1 -1
  1037. package/packages/runtime/dist/anthropic.d.ts +15 -0
  1038. package/packages/runtime/dist/anthropic.d.ts.map +1 -1
  1039. package/packages/runtime/dist/anthropic.js +26 -9
  1040. package/packages/runtime/dist/anthropic.js.map +1 -1
  1041. package/packages/runtime/dist/codex-direct.d.ts +2 -1
  1042. package/packages/runtime/dist/codex-direct.d.ts.map +1 -1
  1043. package/packages/runtime/dist/codex-direct.js +13 -6
  1044. package/packages/runtime/dist/codex-direct.js.map +1 -1
  1045. package/packages/runtime/dist/index.d.ts +1 -0
  1046. package/packages/runtime/dist/index.d.ts.map +1 -1
  1047. package/packages/runtime/dist/index.js +1 -0
  1048. package/packages/runtime/dist/index.js.map +1 -1
  1049. package/packages/runtime/dist/loop.d.ts +2 -0
  1050. package/packages/runtime/dist/loop.d.ts.map +1 -1
  1051. package/packages/runtime/dist/loop.js +22 -0
  1052. package/packages/runtime/dist/loop.js.map +1 -1
  1053. package/packages/runtime/dist/openai.d.ts +34 -1
  1054. package/packages/runtime/dist/openai.d.ts.map +1 -1
  1055. package/packages/runtime/dist/openai.js +85 -19
  1056. package/packages/runtime/dist/openai.js.map +1 -1
  1057. package/packages/runtime/dist/rate-limit.d.ts +91 -0
  1058. package/packages/runtime/dist/rate-limit.d.ts.map +1 -0
  1059. package/packages/runtime/dist/rate-limit.js +291 -0
  1060. package/packages/runtime/dist/rate-limit.js.map +1 -0
  1061. package/packages/runtime/dist/retry.d.ts +40 -0
  1062. package/packages/runtime/dist/retry.d.ts.map +1 -1
  1063. package/packages/runtime/dist/retry.js +42 -8
  1064. package/packages/runtime/dist/retry.js.map +1 -1
  1065. package/packages/tools/artifacts/dist/.tsbuildinfo +1 -1
  1066. package/packages/tools/browser/dist/.tsbuildinfo +1 -1
  1067. package/packages/tools/email/dist/.tsbuildinfo +1 -1
  1068. package/packages/tools/email/dist/agent.js +1 -1
  1069. package/packages/tools/email/dist/folders.d.ts +14 -0
  1070. package/packages/tools/email/dist/folders.d.ts.map +1 -1
  1071. package/packages/tools/email/dist/folders.js +23 -0
  1072. package/packages/tools/email/dist/folders.js.map +1 -1
  1073. package/packages/tools/email/dist/imap/fake.d.ts +55 -0
  1074. package/packages/tools/email/dist/imap/fake.d.ts.map +1 -1
  1075. package/packages/tools/email/dist/imap/fake.js +163 -4
  1076. package/packages/tools/email/dist/imap/fake.js.map +1 -1
  1077. package/packages/tools/email/dist/imap/imapflow-client.d.ts +8 -0
  1078. package/packages/tools/email/dist/imap/imapflow-client.d.ts.map +1 -1
  1079. package/packages/tools/email/dist/imap/imapflow-client.js +124 -4
  1080. package/packages/tools/email/dist/imap/imapflow-client.js.map +1 -1
  1081. package/packages/tools/email/dist/index.d.ts.map +1 -1
  1082. package/packages/tools/email/dist/index.js +3 -0
  1083. package/packages/tools/email/dist/index.js.map +1 -1
  1084. package/packages/tools/email/dist/mail.d.ts +48 -0
  1085. package/packages/tools/email/dist/mail.d.ts.map +1 -1
  1086. package/packages/tools/email/dist/mail.js +119 -0
  1087. package/packages/tools/email/dist/mail.js.map +1 -1
  1088. package/packages/tools/email/dist/mailbox/actions.d.ts +90 -5
  1089. package/packages/tools/email/dist/mailbox/actions.d.ts.map +1 -1
  1090. package/packages/tools/email/dist/mailbox/actions.js +643 -200
  1091. package/packages/tools/email/dist/mailbox/actions.js.map +1 -1
  1092. package/packages/tools/email/dist/mailbox/select.d.ts +6 -0
  1093. package/packages/tools/email/dist/mailbox/select.d.ts.map +1 -1
  1094. package/packages/tools/email/dist/mailbox/select.js +20 -2
  1095. package/packages/tools/email/dist/mailbox/select.js.map +1 -1
  1096. package/packages/tools/email/dist/metrics.d.ts +8 -9
  1097. package/packages/tools/email/dist/metrics.d.ts.map +1 -1
  1098. package/packages/tools/email/dist/metrics.js +8 -9
  1099. package/packages/tools/email/dist/metrics.js.map +1 -1
  1100. package/packages/tools/email/dist/needs-you.d.ts +58 -0
  1101. package/packages/tools/email/dist/needs-you.d.ts.map +1 -0
  1102. package/packages/tools/email/dist/needs-you.js +97 -0
  1103. package/packages/tools/email/dist/needs-you.js.map +1 -0
  1104. package/packages/tools/email/dist/pages/descriptors.d.ts.map +1 -1
  1105. package/packages/tools/email/dist/pages/descriptors.js +126 -80
  1106. package/packages/tools/email/dist/pages/descriptors.js.map +1 -1
  1107. package/packages/tools/email/dist/pages/drafts-tools.d.ts +2 -2
  1108. package/packages/tools/email/dist/pages/queries.d.ts +14 -0
  1109. package/packages/tools/email/dist/pages/queries.d.ts.map +1 -1
  1110. package/packages/tools/email/dist/pages/queries.js +65 -7
  1111. package/packages/tools/email/dist/pages/queries.js.map +1 -1
  1112. package/packages/tools/email/dist/policies/auto.d.ts +9 -9
  1113. package/packages/tools/email/dist/policies/auto.d.ts.map +1 -1
  1114. package/packages/tools/email/dist/policies/auto.js +79 -24
  1115. package/packages/tools/email/dist/policies/auto.js.map +1 -1
  1116. package/packages/tools/email/dist/ports.d.ts +55 -0
  1117. package/packages/tools/email/dist/ports.d.ts.map +1 -1
  1118. package/packages/tools/email/dist/ports.js.map +1 -1
  1119. package/packages/tools/email/dist/sentinels/suspicious-sender.d.ts.map +1 -1
  1120. package/packages/tools/email/dist/sentinels/suspicious-sender.js +4 -1
  1121. package/packages/tools/email/dist/sentinels/suspicious-sender.js.map +1 -1
  1122. package/packages/tools/email/dist/sentinels/waiting-on-me.d.ts +19 -22
  1123. package/packages/tools/email/dist/sentinels/waiting-on-me.d.ts.map +1 -1
  1124. package/packages/tools/email/dist/sentinels/waiting-on-me.js +44 -63
  1125. package/packages/tools/email/dist/sentinels/waiting-on-me.js.map +1 -1
  1126. package/packages/tools/email/dist/sources/idle.d.ts +10 -2
  1127. package/packages/tools/email/dist/sources/idle.d.ts.map +1 -1
  1128. package/packages/tools/email/dist/sources/idle.js +70 -3
  1129. package/packages/tools/email/dist/sources/idle.js.map +1 -1
  1130. package/packages/tools/email/dist/sources/inbox-poll.d.ts +69 -2
  1131. package/packages/tools/email/dist/sources/inbox-poll.d.ts.map +1 -1
  1132. package/packages/tools/email/dist/sources/inbox-poll.js +143 -13
  1133. package/packages/tools/email/dist/sources/inbox-poll.js.map +1 -1
  1134. package/packages/tools/email/dist/sources/presence.d.ts +47 -0
  1135. package/packages/tools/email/dist/sources/presence.d.ts.map +1 -0
  1136. package/packages/tools/email/dist/sources/presence.js +128 -0
  1137. package/packages/tools/email/dist/sources/presence.js.map +1 -0
  1138. package/packages/tools/email/dist/suspicions-store.d.ts +6 -0
  1139. package/packages/tools/email/dist/suspicions-store.d.ts.map +1 -1
  1140. package/packages/tools/email/dist/suspicions-store.js +16 -1
  1141. package/packages/tools/email/dist/suspicions-store.js.map +1 -1
  1142. package/packages/tools/email/dist/threads.d.ts +19 -2
  1143. package/packages/tools/email/dist/threads.d.ts.map +1 -1
  1144. package/packages/tools/email/dist/threads.js +47 -17
  1145. package/packages/tools/email/dist/threads.js.map +1 -1
  1146. package/packages/tools/email/dist/tools/mailbox.d.ts +3 -0
  1147. package/packages/tools/email/dist/tools/mailbox.d.ts.map +1 -1
  1148. package/packages/tools/email/dist/tools/mailbox.js +9 -4
  1149. package/packages/tools/email/dist/tools/mailbox.js.map +1 -1
  1150. package/packages/tools/email/dist/tools/read.d.ts.map +1 -1
  1151. package/packages/tools/email/dist/tools/read.js +29 -2
  1152. package/packages/tools/email/dist/tools/read.js.map +1 -1
  1153. package/packages/tools/email/dist/tools/threads.d.ts +7 -3
  1154. package/packages/tools/email/dist/tools/threads.d.ts.map +1 -1
  1155. package/packages/tools/email/dist/tools/threads.js +18 -5
  1156. package/packages/tools/email/dist/tools/threads.js.map +1 -1
  1157. package/packages/tools/email/dist/watchers.d.ts +91 -7
  1158. package/packages/tools/email/dist/watchers.d.ts.map +1 -1
  1159. package/packages/tools/email/dist/watchers.js +164 -1
  1160. package/packages/tools/email/dist/watchers.js.map +1 -1
  1161. package/packages/tools/email/dist/widgets.d.ts +14 -0
  1162. package/packages/tools/email/dist/widgets.d.ts.map +1 -0
  1163. package/packages/tools/email/dist/widgets.js +51 -0
  1164. package/packages/tools/email/dist/widgets.js.map +1 -0
  1165. package/packages/tools/email/migrations/020_mailbox_presence.sql +44 -0
  1166. package/packages/tools/email/migrations/021_learned_line.sql +9 -0
  1167. package/packages/tools/email/migrations/022_bulk_checked.sql +13 -0
  1168. package/packages/tools/email/migrations/023_attention.sql +100 -0
  1169. package/packages/tools/email/migrations/024_auth_domain.sql +9 -0
  1170. package/packages/tools/email/migrations/025_thread_ignore.sql +77 -0
  1171. package/packages/tools/email/package.json +1 -1
  1172. package/packages/tools/host/dist/.tsbuildinfo +1 -1
  1173. package/packages/tools/mcp/dist/.tsbuildinfo +1 -1
  1174. package/packages/tools/memory/dist/.tsbuildinfo +1 -1
  1175. package/packages/tools/memory/dist/admin.d.ts +9 -0
  1176. package/packages/tools/memory/dist/admin.d.ts.map +1 -1
  1177. package/packages/tools/memory/dist/admin.js +11 -0
  1178. package/packages/tools/memory/dist/admin.js.map +1 -1
  1179. package/packages/tools/web/dist/.tsbuildinfo +1 -1
  1180. package/packages/web/dist/assets/earth-2560-Ce3xs8Cb.webp +0 -0
  1181. package/packages/web/dist/assets/earth-phone-BqC7cDhk.webp +0 -0
  1182. package/packages/web/dist/assets/index-WS4UjGsy.js +129 -0
  1183. package/packages/web/dist/assets/index-Zrt_o2UG.css +1 -0
  1184. package/packages/web/dist/assets/{lottie_light-CSeMXM8D.js → lottie_light-Ls7i1ATS.js} +1 -1
  1185. package/packages/web/dist/build.json +1 -1
  1186. package/packages/web/dist/index.html +2 -2
  1187. package/packages/web/dist/precache.json +4 -4
  1188. package/packages/web/dist/sw.js +3 -2
  1189. package/packages/web/dist/assets/index-CKuhKCWw.js +0 -131
  1190. package/packages/web/dist/assets/index-DsK8UWCJ.css +0 -1
@@ -12,7 +12,7 @@ export const MAILBOX_ACTION_KINDS = ['mark-read', 'mark-unread', 'archive', 'mov
12
12
  export const SEEN = '\\Seen';
13
13
  const GMAIL_CAPABILITY = 'X-GM-EXT-1';
14
14
  export const ACTION_COLUMNS = 'id, account_id, kind, destination, criteria, origin, actor, policy_id, run_id, action_id, message_ids, ' +
15
- 'items, changed, note, reverts, undone_at, undone_by, created_at';
15
+ 'items, changed, note, reverts, undone_at, undone_by, created_at, state, reverted_ids';
16
16
  function iso(value) {
17
17
  if (value === null || value === undefined)
18
18
  return null;
@@ -38,6 +38,8 @@ export function toAction(row) {
38
38
  undoneAt: iso(row.undone_at),
39
39
  undoneBy: row.undone_by ? String(row.undone_by) : null,
40
40
  createdAt: iso(row.created_at),
41
+ state: ['pending', 'done', 'partial', 'unknown'].includes(row.state) ? row.state : 'done',
42
+ revertedIds: Array.isArray(row.reverted_ids) ? row.reverted_ids.map(String) : [],
41
43
  };
42
44
  }
43
45
  /** A refusal whose message is the sentence the owner and the agent read. */
@@ -84,7 +86,7 @@ export async function loadTargets(db, ids) {
84
86
  if (ids.length === 0)
85
87
  return [];
86
88
  const { rows } = await db.query(`select m.id, m.account_id, m.folder_id, f.name as folder, m.uidvalidity, m.uid, m.message_id,
87
- m.subject, m.from_addr, m.flags
89
+ m.subject, m.from_addr, m.flags, m.gone_at
88
90
  from email.messages m join email.folders f on f.id = m.folder_id
89
91
  where m.id = any($1::uuid[])`, [ids]);
90
92
  const byId = new Map(rows.map((r) => [String(r.id), r]));
@@ -104,6 +106,7 @@ export async function loadTargets(db, ids) {
104
106
  subject: r.subject ?? '',
105
107
  from: r.from_addr ?? '',
106
108
  flags: Array.isArray(r.flags) ? r.flags : [],
109
+ gone: r.gone_at !== null && r.gone_at !== undefined,
107
110
  });
108
111
  }
109
112
  return out;
@@ -243,13 +246,25 @@ export function destinationFor(facts, kind, account, folder) {
243
246
  /* ------------------------------------------------------------------ *
244
247
  * Keeping buddi's rows in step
245
248
  * ------------------------------------------------------------------ */
246
- /** The folder row for a server name, created (unsynced, kind other) if new. Never changes an existing one. */
247
- async function folderRow(db, accountId, name) {
248
- const { rows } = await db.query(`insert into email.folders (account_id, name, kind, synced) values ($1, $2, 'other', false)
249
- on conflict (account_id, name) do update set name = email.folders.name
250
- returning id`, [accountId, name]);
249
+ /**
250
+ * The folder row for a server name, created (unsynced, kind other) if new.
251
+ * Never changes an existing row's kind or cursor; records what the server
252
+ * says the folder is for (`special_use`) when the caller knows it.
253
+ */
254
+ export async function folderRow(db, accountId, name, specialUse) {
255
+ const { rows } = await db.query(`insert into email.folders (account_id, name, kind, synced, special_use) values ($1, $2, 'other', false, $3)
256
+ on conflict (account_id, name) do update set special_use = coalesce(excluded.special_use, email.folders.special_use)
257
+ returning id`, [accountId, name, specialUse ?? null]);
251
258
  return String(rows[0].id);
252
259
  }
260
+ /** The SPECIAL-USE attribute the listing gives a folder, or null. */
261
+ export function specialUseOf(listing, name) {
262
+ const info = listing.find((f) => f.name === name);
263
+ if (!info)
264
+ return null;
265
+ const known = ['\\all', '\\archive', '\\trash', '\\junk', '\\sent', '\\drafts'];
266
+ return attributesOf(info).find((a) => known.includes(a)) ?? null;
267
+ }
253
268
  let unknownCounter = 0;
254
269
  /**
255
270
  * A placeholder uid for a message whose new uid the server did not report and
@@ -261,21 +276,34 @@ function placeholderUid() {
261
276
  unknownCounter = (unknownCounter + 1) % 1000;
262
277
  return -(Date.now() * 1000 + unknownCounter);
263
278
  }
264
- /** Point a row at where its message is now. Falls back to "uid unknown" if the poll got there first. */
265
- async function relocateRow(db, target, folderId, uidValidity, uid) {
279
+ /**
280
+ * Point a row at where its message is now: folder, UIDVALIDITY, uid — and,
281
+ * on Gmail, its labels when they are known. The row is no longer "gone"
282
+ * (buddi knows where it is), and a thread whose newest message this is keeps
283
+ * its tie-break fields (`last_folder_id`, `last_uidvalidity`, `last_uid`)
284
+ * pointing at the same place. Falls back to "uid unknown" if the poll got
285
+ * there first.
286
+ */
287
+ export async function relocateRow(db, target, folderId, uidValidity, uid, labels) {
288
+ const labelJson = labels === undefined ? null : JSON.stringify(labels ?? []);
289
+ let where = null;
266
290
  if (uidValidity !== null && uid !== null) {
267
- const result = await db.query(`update email.messages set folder_id = $2, uidvalidity = $3, uid = $4
291
+ const result = await db.query(`update email.messages set folder_id = $2, uidvalidity = $3, uid = $4, gone_at = null,
292
+ labels = case when $6::jsonb is null then labels else $6::jsonb end
268
293
  where id = $1
269
294
  and not exists (select 1 from email.messages o
270
- where o.account_id = $5 and o.folder_id = $2 and o.uidvalidity = $3 and o.uid = $4)`, [target.id, folderId, uidValidity, uid, target.accountId]);
295
+ where o.account_id = $5 and o.folder_id = $2 and o.uidvalidity = $3 and o.uid = $4
296
+ and o.id <> $1)`, [target.id, folderId, uidValidity, uid, target.accountId, labelJson]);
271
297
  if ((result.rowCount ?? 0) > 0)
272
- return;
298
+ where = { uidValidity, uid };
273
299
  }
274
- await db.query(`update email.messages set folder_id = $2, uidvalidity = 0, uid = $3 where id = $1`, [
275
- target.id,
276
- folderId,
277
- placeholderUid(),
278
- ]);
300
+ if (where === null) {
301
+ where = { uidValidity: 0, uid: placeholderUid() };
302
+ await db.query(`update email.messages set folder_id = $2, uidvalidity = 0, uid = $3, gone_at = null,
303
+ labels = case when $4::jsonb is null then labels else $4::jsonb end
304
+ where id = $1`, [target.id, folderId, where.uid, labelJson]);
305
+ }
306
+ await db.query(`update email.threads set last_folder_id = $2, last_uidvalidity = $3, last_uid = $4 where last_message_id = $1`, [target.id, folderId, where.uidValidity, where.uid]);
279
307
  }
280
308
  async function setSeenLocally(db, ids, seen) {
281
309
  if (ids.length === 0)
@@ -285,6 +313,117 @@ async function setSeenLocally(db, ids, seen) {
285
313
  where id = any($1::uuid[]) and not (flags @> '["\\\\Seen"]'::jsonb)`
286
314
  : `update email.messages set flags = flags - '\\Seen' where id = any($1::uuid[])`, [ids]);
287
315
  }
316
+ /* ------------------------------------------------------------------ *
317
+ * The trail, written before the server is touched
318
+ * ------------------------------------------------------------------ */
319
+ /**
320
+ * Trail rows of changes running in this process right now. A `pending` row
321
+ * not in here was left by a process that stopped mid-change, and the next
322
+ * poll reconciles it (`reconcileTrail`). One process per installation, so
323
+ * this set is the whole truth about what is in flight.
324
+ */
325
+ const inFlightTrail = new Set();
326
+ /** The trail rows a change in this process is still writing. */
327
+ export function trailInFlight() {
328
+ return [...inFlightTrail];
329
+ }
330
+ /** An item from before intent-first has no status: it was written after the change, so it happened. */
331
+ export function statusOf(item) {
332
+ return item.status ?? 'done';
333
+ }
334
+ /**
335
+ * The trail row, written before the first server command: `pending`, with
336
+ * the messages it is about and no items yet. Each folder's items are added
337
+ * as `planned` right before that folder's command and become `done` once the
338
+ * server answered, so a crash anywhere leaves a row that says what may have
339
+ * happened.
340
+ */
341
+ async function beginTrail(db, input) {
342
+ const p = input.provenance;
343
+ const { rows } = await db.query(`insert into email.mailbox_actions
344
+ (account_id, kind, destination, criteria, origin, actor, policy_id, run_id, action_id, message_ids,
345
+ items, changed, note, reverts, created_at, state)
346
+ values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::uuid[], '[]'::jsonb, 0, null, $11, $12, 'pending')
347
+ returning id`, [
348
+ input.accountId,
349
+ input.kind,
350
+ input.destination,
351
+ p.criteria ?? null,
352
+ p.origin,
353
+ p.actor,
354
+ p.policyId ?? null,
355
+ p.runId ?? null,
356
+ p.actionId ?? null,
357
+ input.messageIds,
358
+ input.reverts ?? null,
359
+ input.now,
360
+ ]);
361
+ const id = String(rows[0].id);
362
+ inFlightTrail.add(id);
363
+ return id;
364
+ }
365
+ /** The items as they stand, on the row. */
366
+ async function saveTrail(db, id, items) {
367
+ const kept = items.filter((i) => statusOf(i) !== 'dropped');
368
+ await db.query(`update email.mailbox_actions set items = $2::jsonb, changed = $3 where id = $1`, [id, JSON.stringify(kept), kept.filter((i) => statusOf(i) === 'done').length]);
369
+ }
370
+ /**
371
+ * Close a trail row. Without an error: `done`, or deleted when nothing
372
+ * changed. With one: `partial` when what was done is all known; still
373
+ * `pending` when a command was sent and its answer never came (the next poll
374
+ * asks the server); deleted when nothing reached the server.
375
+ */
376
+ async function finishTrail(db, id, items, outcome) {
377
+ inFlightTrail.delete(id);
378
+ const kept = items.filter((i) => statusOf(i) !== 'dropped');
379
+ const done = kept.filter((i) => statusOf(i) === 'done');
380
+ const uncertain = kept.filter((i) => statusOf(i) === 'planned');
381
+ const skipped = outcome.skippedNote ? ` Skipped ${outcome.skippedNote}.` : '';
382
+ let state;
383
+ let note;
384
+ if (outcome.error !== undefined) {
385
+ const why = outcome.error instanceof Error ? outcome.error.message : String(outcome.error);
386
+ if (uncertain.length > 0) {
387
+ state = 'pending';
388
+ note = `Interrupted after ${plural(done.length, 'message')}: ${why}. ${plural(uncertain.length, 'more was', 'more were')} sent to the server without an answer; buddi checks ${uncertain.length === 1 ? 'it' : 'them'} on its next poll.${skipped}`;
389
+ }
390
+ else if (done.length > 0) {
391
+ state = 'partial';
392
+ note = `Partial: stopped by an error after ${plural(done.length, 'message')}: ${why}.${skipped}`;
393
+ }
394
+ else {
395
+ await db.query(`delete from email.mailbox_actions where id = $1`, [id]);
396
+ return null;
397
+ }
398
+ }
399
+ else {
400
+ if (done.length === 0) {
401
+ await db.query(`delete from email.mailbox_actions where id = $1`, [id]);
402
+ return null;
403
+ }
404
+ state = 'done';
405
+ note = skipped ? skipped.trim() : null;
406
+ }
407
+ const { rows } = await db.query(`update email.mailbox_actions set state = $2, note = $3, items = $4::jsonb, message_ids = $5::uuid[], changed = $6
408
+ where id = $1 returning ${ACTION_COLUMNS}`, [id, state, note, JSON.stringify(kept), kept.map((i) => i.id), done.length]);
409
+ return rows[0] ? toAction(rows[0]) : null;
410
+ }
411
+ /**
412
+ * After an undo (or its reconcile): the messages it put back join the
413
+ * original's `reverted_ids`, and the original is stamped undone once the undo
414
+ * finished without an error or nothing of it is left to put back. A partial
415
+ * undo leaves it undoable, for the rest.
416
+ */
417
+ async function markReverted(db, original, doneIds, finished, undoId, now) {
418
+ const reverted = new Set([...original.revertedIds, ...doneIds]);
419
+ const remaining = original.items.filter((i) => statusOf(i) === 'done' && !reverted.has(i.id));
420
+ const complete = (finished && undoId !== null) || remaining.length === 0;
421
+ await db.query(`update email.mailbox_actions
422
+ set reverted_ids = $2::uuid[],
423
+ undone_at = case when $3 then $4 else undone_at end,
424
+ undone_by = case when $3 then $5::uuid else undone_by end
425
+ where id = $1`, [original.id, [...reverted], complete, now, undoId]);
426
+ }
288
427
  /**
289
428
  * Find where each target is now, on the server: its folder's generation must
290
429
  * match, and a row whose uid is unknown (UIDVALIDITY 0) is looked up by
@@ -345,34 +484,33 @@ async function landed(client, destination, moved, result) {
345
484
  }
346
485
  return { uidValidity, uids };
347
486
  }
348
- async function recordAction(db, input) {
349
- const p = input.provenance;
350
- const { rows } = await db.query(`insert into email.mailbox_actions
351
- (account_id, kind, destination, criteria, origin, actor, policy_id, run_id, action_id, message_ids,
352
- items, changed, note, reverts, created_at)
353
- values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::uuid[], $11::jsonb, $12, $13, $14, $15)
354
- returning ${ACTION_COLUMNS}`, [
355
- input.accountId,
356
- input.kind,
357
- input.destination,
358
- p.criteria ?? null,
359
- p.origin,
360
- p.actor,
361
- p.policyId ?? null,
362
- p.runId ?? null,
363
- p.actionId ?? null,
364
- input.messageIds,
365
- JSON.stringify(input.items),
366
- input.changed,
367
- input.note,
368
- input.reverts ?? null,
369
- input.now,
370
- ]);
371
- return toAction(rows[0]);
487
+ /** Gmail's own name for inbox membership, as X-GM-LABELS spells it. */
488
+ export const GMAIL_INBOX_LABEL = '\\Inbox';
489
+ /**
490
+ * On Gmail, a folder that is a label the owner made: not INBOX and not one
491
+ * of Gmail's own (All Mail, Trash, Spam, Sent, Drafts, Starred, Important).
492
+ */
493
+ export function isUserLabel(facts, name) {
494
+ if (!facts.gmail || name.toUpperCase() === INBOX)
495
+ return false;
496
+ const info = facts.listing.find((f) => f.name === name);
497
+ if (!info)
498
+ return false;
499
+ return !attributesOf(info).some((a) => a.startsWith('\\') && a !== '\\hasnochildren' && a !== '\\haschildren');
500
+ }
501
+ /** Labels without Gmail's inbox marker, deduplicated, in a stable order. */
502
+ function plainLabels(labels) {
503
+ return [...new Set((labels ?? []).filter((l) => l !== GMAIL_INBOX_LABEL))].sort();
504
+ }
505
+ function skippedWords(reasons) {
506
+ return [...reasons.entries()].map(([why, n]) => `${n} ${why}`).join(', ');
372
507
  }
373
508
  /**
374
509
  * Carry out one change on one account's server, keep the rows in step, and
375
- * write it on the trail.
510
+ * write it on the trail — intent first: the row exists, `pending`, before the
511
+ * first command, each folder's messages are on it as `planned` before that
512
+ * folder's command and `done` after it, so a crash between the server change
513
+ * and the record leaves a row the next poll reconciles (`reconcileTrail`).
376
514
  *
377
515
  * Messages no longer where buddi last saw them are skipped, never guessed at,
378
516
  * and the note says how many. Nothing is recorded when nothing changed.
@@ -387,11 +525,26 @@ export async function performAction(db, client, input) {
387
525
  if (n > 0)
388
526
  reasons.set(why, (reasons.get(why) ?? 0) + n);
389
527
  };
390
- // Whatever the server already did stays on the trail even if a later
391
- // folder fails (a dropped connection, a lookup): the change is recorded as
392
- // partial, with the error, and the error is thrown on.
528
+ // A message the poll found gone from its folder has no place on the server
529
+ // buddi knows: it was moved or deleted in another app.
530
+ const live = targets.filter((t) => !t.gone);
531
+ for (const t of targets)
532
+ if (t.gone)
533
+ skip('moved or deleted in another mail app');
534
+ if (live.length === 0) {
535
+ return { action: null, changed: 0, skipped: targets.length, note: `Nothing changed in ${account.address} (${skippedWords(reasons)}).` };
536
+ }
537
+ const trailId = await beginTrail(db, {
538
+ accountId: account.id,
539
+ kind,
540
+ destination,
541
+ provenance: input.provenance,
542
+ messageIds: live.map((t) => t.id),
543
+ now: input.now,
544
+ });
545
+ const save = () => saveTrail(db, trailId, items);
393
546
  try {
394
- for (const [folder, group] of byFolder(targets)) {
547
+ for (const [folder, group] of byFolder(live)) {
395
548
  const here = await locate(client, folder, group);
396
549
  skip(`no longer in ${folder}`, group.length - here.size);
397
550
  const present = group.filter((t) => here.has(t.id));
@@ -403,16 +556,21 @@ export async function performAction(db, client, input) {
403
556
  skip(seen ? 'already read' : 'already unread', present.length - change.length);
404
557
  if (change.length === 0)
405
558
  continue;
406
- await client.storeFlags(folder, change.map((t) => here.get(t.id).uid), [SEEN], seen ? 'add' : 'remove');
407
- for (const t of change) {
559
+ const planned = change.map((t) => {
408
560
  const at = here.get(t.id);
409
- items.push({
561
+ return {
410
562
  id: t.id, subject: t.subject, from: t.from, messageId: t.messageId,
411
563
  fromFolder: folder, fromUidValidity: t.uidValidity > 0 ? t.uidValidity : 0, fromUid: at.uid,
412
- prevFlags: at.flags,
413
- });
414
- }
564
+ prevFlags: at.flags, wantSeen: seen, status: 'planned',
565
+ };
566
+ });
567
+ items.push(...planned);
568
+ await save();
569
+ await client.storeFlags(folder, change.map((t) => here.get(t.id).uid), [SEEN], seen ? 'add' : 'remove');
570
+ for (const p of planned)
571
+ p.status = 'done';
415
572
  await setSeenLocally(db, change.map((t) => t.id), seen);
573
+ await save();
416
574
  continue;
417
575
  }
418
576
  // A move of some kind. Already there is not a change.
@@ -424,82 +582,77 @@ export async function performAction(db, client, input) {
424
582
  }
425
583
  if (movable.length === 0)
426
584
  continue;
585
+ /*
586
+ * Gmail, from the inbox to a label: the label goes on and Inbox comes
587
+ * off (X-GM-LABELS), so every other label stays exactly as it was.
588
+ * Anywhere else a MOVE (from another label it swaps that label for the
589
+ * new one, which is what moving between labels means).
590
+ */
591
+ const labelMove = kind === 'move' && facts.gmail && folder.toUpperCase() === INBOX && isUserLabel(facts, destination);
427
592
  const source = await client.open(folder);
428
- const labels = facts.gmail && kind === 'trash'
593
+ const labels = facts.gmail && (kind === 'trash' || labelMove)
429
594
  ? await client.fetchLabels(folder, movable.map((t) => here.get(t.id).uid))
430
595
  : new Map();
431
596
  const moved = movable.map((t) => ({ target: t, uid: here.get(t.id).uid }));
432
- const result = await client.move(folder, moved.map((m) => m.uid), destination);
433
- // The server has moved them: on the trail now, refined below. Should the
434
- // landing lookup fail, each row still follows to the destination with its
435
- // uid unknown (found by Message-ID next time), so undo can find it.
436
- const pushed = moved.map((m) => {
437
- const item = {
438
- id: m.target.id, subject: m.target.subject, from: m.target.from, messageId: m.target.messageId,
439
- fromFolder: folder, fromUidValidity: source.uidValidity, fromUid: m.uid,
440
- toFolder: destination, toUidValidity: 0, toUid: null,
441
- prevFlags: here.get(m.target.id).flags,
442
- ...(labels.has(m.uid) ? { prevLabels: labels.get(m.uid) } : {}),
443
- };
444
- items.push(item);
445
- return item;
446
- });
447
- const destId = await folderRow(db, account.id, destination);
597
+ const pushed = moved.map((m) => ({
598
+ id: m.target.id, subject: m.target.subject, from: m.target.from, messageId: m.target.messageId,
599
+ fromFolder: folder, fromUidValidity: source.uidValidity, fromUid: m.uid,
600
+ toFolder: destination, toUidValidity: 0, toUid: null,
601
+ prevFlags: here.get(m.target.id).flags,
602
+ ...(labels.has(m.uid) ? { prevLabels: plainLabels(labels.get(m.uid)) } : {}),
603
+ ...(labelMove ? { via: 'labels' } : {}),
604
+ status: 'planned',
605
+ }));
606
+ items.push(...pushed);
607
+ await save();
608
+ let result;
609
+ const uids = moved.map((m) => m.uid);
610
+ if (labelMove) {
611
+ await client.storeLabels(folder, uids, [destination], 'add');
612
+ await client.storeLabels(folder, uids, [GMAIL_INBOX_LABEL], 'remove');
613
+ result = { uidValidity: null, uidMap: new Map() };
614
+ }
615
+ else {
616
+ result = await client.move(folder, uids, destination);
617
+ }
618
+ // The server has moved them: done, refined below. Should the landing
619
+ // lookup fail, each row still follows to the destination with its uid
620
+ // unknown (found by Message-ID next time), so undo can find it.
621
+ for (const p of pushed)
622
+ p.status = 'done';
623
+ const labelsAfter = (m) => labelMove ? plainLabels([...(labels.get(m.uid) ?? []), destination]) : facts.gmail && kind === 'trash' ? [] : undefined;
624
+ const destId = await folderRow(db, account.id, destination, specialUseOf(facts.listing, destination));
448
625
  let where;
449
626
  try {
450
627
  where = await landed(client, destination, moved, result);
451
628
  }
452
629
  catch (err) {
453
630
  for (const m of moved)
454
- await relocateRow(db, m.target, destId, null, null);
631
+ await relocateRow(db, m.target, destId, null, null, labelsAfter(m));
455
632
  throw err;
456
633
  }
457
634
  for (const [i, m] of moved.entries()) {
458
635
  const newUid = where.uids.get(m.target.id) ?? null;
459
- await relocateRow(db, m.target, destId, newUid === null ? null : where.uidValidity, newUid);
636
+ await relocateRow(db, m.target, destId, newUid === null ? null : where.uidValidity, newUid, labelsAfter(m));
460
637
  pushed[i].toUidValidity = where.uidValidity ?? 0;
461
638
  pushed[i].toUid = newUid;
462
639
  }
640
+ await save();
463
641
  }
464
642
  }
465
643
  catch (err) {
466
- if (items.length > 0) {
467
- const why = err instanceof Error ? err.message : String(err);
468
- const skippedNote = [...reasons.entries()].map(([r, n]) => `${n} ${r}`).join(', ');
469
- await recordAction(db, {
470
- accountId: account.id,
471
- kind,
472
- destination,
473
- provenance: input.provenance,
474
- messageIds: items.map((i) => i.id),
475
- items,
476
- changed: items.length,
477
- note: `Partial: stopped by an error after ${plural(items.length, 'message')}: ${why}.${skippedNote ? ` Skipped ${skippedNote}.` : ''}`,
478
- now: input.now,
479
- }).catch(() => { });
480
- }
644
+ await finishTrail(db, trailId, items, { error: err, skippedNote: skippedWords(reasons) }).catch(() => { });
481
645
  throw err;
482
646
  }
483
647
  const skipped = [...reasons.values()].reduce((a, b) => a + b, 0);
484
- const skippedNote = [...reasons.entries()].map(([why, n]) => `${n} ${why}`).join(', ');
485
- const done = `${verbOf(kind, destination)}: ${plural(items.length, 'message')} in ${account.address}`;
486
- const note = items.length === 0
648
+ const skippedNote = skippedWords(reasons);
649
+ const action = await finishTrail(db, trailId, items, { skippedNote });
650
+ const changed = action?.changed ?? 0;
651
+ const done = `${verbOf(kind, destination)}: ${plural(changed, 'message')} in ${account.address}`;
652
+ const note = changed === 0
487
653
  ? `Nothing changed in ${account.address}${skippedNote ? ` (${skippedNote})` : ''}.`
488
654
  : `${done}.${skippedNote ? ` Skipped ${skippedNote}.` : ''}${kind === 'trash' ? ' They are in Trash, not deleted; undo puts them back while they are still there.' : ' Undo puts them back.'}`;
489
- if (items.length === 0)
490
- return { action: null, changed: 0, skipped, note };
491
- const action = await recordAction(db, {
492
- accountId: account.id,
493
- kind,
494
- destination,
495
- provenance: input.provenance,
496
- messageIds: items.map((i) => i.id),
497
- items,
498
- changed: items.length,
499
- note: skippedNote ? `Skipped ${skippedNote}.` : null,
500
- now: input.now,
501
- });
502
- return { action, changed: items.length, skipped, note };
655
+ return { action, changed, skipped, note };
503
656
  }
504
657
  /* ------------------------------------------------------------------ *
505
658
  * Undo
@@ -512,38 +665,81 @@ export async function findAction(db, id) {
512
665
  export async function lastUndoable(db, accountIds) {
513
666
  const { rows } = await db.query(`select ${ACTION_COLUMNS} from email.mailbox_actions
514
667
  where account_id = any($1::uuid[]) and kind <> 'undo' and undone_at is null
668
+ and state <> 'pending' and changed > 0
515
669
  order by created_at desc, seq desc limit 1`, [accountIds]);
516
670
  return rows[0] ? toAction(rows[0]) : null;
517
671
  }
672
+ /** The messages of a change an undo can still put back: done, and not put back already. */
673
+ export function undoableItems(action) {
674
+ const reverted = new Set(action.revertedIds);
675
+ return action.items.filter((i) => statusOf(i) === 'done' && !reverted.has(i.id));
676
+ }
518
677
  /** Why a change cannot be undone, or null when it can. */
519
678
  export function undoRefusal(action) {
520
679
  if (action.kind === 'undo')
521
680
  return 'That was itself an undo; buddi does not undo an undo. Make the change again instead.';
522
681
  if (action.undoneAt)
523
682
  return `That change was already undone (${action.undoneAt}).`;
683
+ if (action.state === 'pending') {
684
+ return 'buddi stopped while making that change, and has not yet checked with the server how far it got. It does on its next poll; undo it after that.';
685
+ }
686
+ if (undoableItems(action).length === 0)
687
+ return 'Nothing of that change is left to put back.';
524
688
  return null;
525
689
  }
690
+ /**
691
+ * How many of a change's messages buddi has seen move since, in another mail
692
+ * app: gone from the folder the change left them in, or now somewhere else.
693
+ * An undo leaves those where the owner put them.
694
+ */
695
+ export async function movedSince(db, action) {
696
+ const items = undoableItems(action);
697
+ if (items.length === 0)
698
+ return 0;
699
+ const { rows } = await db.query(`select m.id, f.name as folder, m.gone_at from email.messages m join email.folders f on f.id = m.folder_id
700
+ where m.id = any($1::uuid[])`, [items.map((i) => i.id)]);
701
+ const byId = new Map(rows.map((r) => [String(r.id), r]));
702
+ let n = 0;
703
+ for (const item of items) {
704
+ const row = byId.get(item.id);
705
+ if (!row)
706
+ continue;
707
+ if (row.gone_at !== null && row.gone_at !== undefined)
708
+ n += 1;
709
+ else if (item.toFolder && row.folder !== item.toFolder)
710
+ n += 1;
711
+ }
712
+ return n;
713
+ }
526
714
  /** The sentence an undo's approval and the Recent changes row say. */
527
- export function describeUndo(action, account) {
528
- const n = plural(action.changed, 'message');
715
+ export function describeUndo(action, account, moved = 0) {
716
+ const left = undoableItems(action).length;
717
+ const n = plural(left, 'message');
718
+ const rest = action.revertedIds.length > 0 ? ' the rest of' : '';
719
+ const since = moved > 0
720
+ ? ` ${plural(moved, 'of them has', 'of them have')} since been moved in another mail app and will stay where ${moved === 1 ? 'it is' : 'they are'}.`
721
+ : '';
529
722
  switch (action.kind) {
530
723
  case 'mark-read':
531
- return `Undo "${verbOf(action.kind)}" on ${n} in ${account}: they become unread again.`;
724
+ return `Undo${rest} "${verbOf(action.kind)}" on ${n} in ${account}: they become unread again.${since}`;
532
725
  case 'mark-unread':
533
- return `Undo "${verbOf(action.kind)}" on ${n} in ${account}: they become read again.`;
726
+ return `Undo${rest} "${verbOf(action.kind)}" on ${n} in ${account}: they become read again.${since}`;
534
727
  case 'trash':
535
- return `Undo "${verbOf(action.kind)}" on ${n} in ${account}: they move back from Trash to where they were (any already emptied from Trash cannot come back).`;
728
+ return `Undo${rest} "${verbOf(action.kind)}" on ${n} in ${account}: they move back from Trash to where they were (any already emptied from Trash cannot come back).${since}`;
536
729
  default:
537
- return `Undo "${verbOf(action.kind, action.destination)}" on ${n} in ${account}: they move back to where they were.`;
730
+ return `Undo${rest} "${verbOf(action.kind, action.destination)}" on ${n} in ${account}: they move back to where they were.${since}`;
538
731
  }
539
732
  }
540
733
  /**
541
734
  * Put one change back, as far as the mailbox still allows, and record the
542
- * undo as a change of its own.
735
+ * undo as a change of its own — intent first, like `performAction`.
543
736
  *
544
737
  * Each message is acted on where buddi's row says it is now — the row
545
- * followed it — and only if the server still has it there: a message emptied
546
- * from Trash, or moved again in another client, is skipped and counted.
738
+ * followed it — and only if that is still where the change left it: one the
739
+ * poll saw moved in another mail app is left where the owner put it, and one
740
+ * emptied from Trash is skipped; the note says which. An undo stopped by an
741
+ * error records what it put back, is marked partial, and leaves the change
742
+ * undoable for the rest.
547
743
  */
548
744
  export async function undoAction(db, client, input) {
549
745
  const { account, action } = input;
@@ -551,106 +747,353 @@ export async function undoAction(db, client, input) {
551
747
  if (refusal)
552
748
  throw new MailboxRefusal(refusal);
553
749
  const facts = await serverFacts(client);
554
- const current = await loadTargets(db, action.items.map((i) => i.id));
555
- const itemOf = new Map(action.items.map((i) => [i.id, i]));
750
+ const wanted = undoableItems(action);
751
+ const current = await loadTargets(db, wanted.map((i) => i.id));
752
+ const itemOf = new Map(wanted.map((i) => [i.id, i]));
556
753
  const items = [];
557
- let skipped = action.items.length - current.length;
558
- for (const [folder, group] of byFolder(current)) {
559
- const here = await locate(client, folder, group);
560
- skipped += group.length - here.size;
561
- const present = group.filter((t) => here.has(t.id));
562
- if (present.length === 0)
563
- continue;
564
- if (action.kind === 'mark-read' || action.kind === 'mark-unread') {
565
- const restoreSeen = present.filter((t) => (itemOf.get(t.id).prevFlags ?? []).includes(SEEN));
566
- const restoreUnseen = present.filter((t) => !(itemOf.get(t.id).prevFlags ?? []).includes(SEEN));
567
- if (restoreSeen.length > 0) {
568
- await client.storeFlags(folder, restoreSeen.map((t) => here.get(t.id).uid), [SEEN], 'add');
569
- await setSeenLocally(db, restoreSeen.map((t) => t.id), true);
570
- }
571
- if (restoreUnseen.length > 0) {
572
- await client.storeFlags(folder, restoreUnseen.map((t) => here.get(t.id).uid), [SEEN], 'remove');
573
- await setSeenLocally(db, restoreUnseen.map((t) => t.id), false);
754
+ const reasons = new Map();
755
+ const skip = (why, n = 1) => {
756
+ if (n > 0)
757
+ reasons.set(why, (reasons.get(why) ?? 0) + n);
758
+ };
759
+ skip('no longer known to buddi', wanted.length - current.length);
760
+ const isMove = action.kind !== 'mark-read' && action.kind !== 'mark-unread';
761
+ const eligible = current.filter((t) => {
762
+ const item = itemOf.get(t.id);
763
+ if (t.gone || (isMove && item.toFolder && t.folder !== item.toFolder)) {
764
+ skip('moved in another mail app since, so left where you put it');
765
+ return false;
766
+ }
767
+ return true;
768
+ });
769
+ const summary = (changed) => {
770
+ const skipped = [...reasons.values()].reduce((a, b) => a + b, 0);
771
+ const why = skippedWords(reasons);
772
+ return changed === 0
773
+ ? `Nothing could be put back in ${account.address}${skipped > 0 ? ` (${why})` : ''}.`
774
+ : `Undid "${verbOf(action.kind, action.destination)}" on ${plural(changed, 'message')} in ${account.address}.${skipped > 0 ? ` ${plural(skipped, 'message')} could not be put back: ${why}.` : ''}`;
775
+ };
776
+ if (eligible.length === 0) {
777
+ return { action: null, changed: 0, skipped: [...reasons.values()].reduce((a, b) => a + b, 0), note: summary(0) };
778
+ }
779
+ const trailId = await beginTrail(db, {
780
+ accountId: account.id,
781
+ kind: 'undo',
782
+ destination: null,
783
+ provenance: input.provenance,
784
+ messageIds: eligible.map((t) => t.id),
785
+ reverts: action.id,
786
+ now: input.now,
787
+ });
788
+ const save = () => saveTrail(db, trailId, items);
789
+ const doneIds = () => items.filter((i) => statusOf(i) === 'done').map((i) => i.id);
790
+ try {
791
+ for (const [folder, group] of byFolder(eligible)) {
792
+ const here = await locate(client, folder, group);
793
+ skip('no longer where the change left it', group.length - here.size);
794
+ const present = group.filter((t) => here.has(t.id));
795
+ if (present.length === 0)
796
+ continue;
797
+ if (!isMove) {
798
+ for (const seen of [true, false]) {
799
+ const list = present.filter((t) => (itemOf.get(t.id).prevFlags ?? []).includes(SEEN) === seen);
800
+ if (list.length === 0)
801
+ continue;
802
+ const planned = list.map((t) => ({
803
+ id: t.id, subject: t.subject, from: t.from, messageId: t.messageId,
804
+ fromFolder: folder, fromUidValidity: t.uidValidity, fromUid: here.get(t.id).uid,
805
+ prevFlags: here.get(t.id).flags, wantSeen: seen, status: 'planned',
806
+ }));
807
+ items.push(...planned);
808
+ await save();
809
+ await client.storeFlags(folder, list.map((t) => here.get(t.id).uid), [SEEN], seen ? 'add' : 'remove');
810
+ for (const p of planned)
811
+ p.status = 'done';
812
+ await setSeenLocally(db, list.map((t) => t.id), seen);
813
+ await save();
814
+ }
815
+ continue;
574
816
  }
817
+ // Moves: back to each message's own original folder.
818
+ const back = new Map();
575
819
  for (const t of present) {
576
- items.push({
577
- id: t.id, subject: t.subject, from: t.from, messageId: t.messageId,
578
- fromFolder: folder, fromUidValidity: t.uidValidity, fromUid: here.get(t.id).uid,
579
- prevFlags: here.get(t.id).flags,
580
- });
820
+ const original = itemOf.get(t.id).fromFolder;
821
+ if (original === folder) {
822
+ skip('already back where it was');
823
+ continue;
824
+ }
825
+ const list = back.get(original) ?? [];
826
+ list.push(t);
827
+ back.set(original, list);
581
828
  }
582
- continue;
583
- }
584
- // Moves: back to each message's own original folder.
585
- const back = new Map();
586
- for (const t of present) {
587
- const original = itemOf.get(t.id).fromFolder;
588
- if (original === folder) {
589
- skipped += 1;
590
- continue;
829
+ for (const [original, list] of back) {
830
+ if (!facts.listing.some((f) => f.name === original)) {
831
+ skip(`its old folder ${original} is gone`, list.length);
832
+ continue;
833
+ }
834
+ const source = await client.open(folder);
835
+ const viaLabels = list.filter((t) => facts.gmail && itemOf.get(t.id).via === 'labels' && original.toUpperCase() === INBOX);
836
+ const viaMove = list.filter((t) => !viaLabels.includes(t));
837
+ const destId = await folderRow(db, account.id, original, specialUseOf(facts.listing, original));
838
+ if (viaMove.length > 0) {
839
+ const moved = viaMove.map((t) => ({ target: t, uid: here.get(t.id).uid }));
840
+ const pushed = moved.map((m) => ({
841
+ id: m.target.id, subject: m.target.subject, from: m.target.from, messageId: m.target.messageId,
842
+ fromFolder: folder, fromUidValidity: source.uidValidity, fromUid: m.uid,
843
+ toFolder: original, toUidValidity: 0, toUid: null, status: 'planned',
844
+ }));
845
+ items.push(...pushed);
846
+ await save();
847
+ const result = await client.move(folder, moved.map((m) => m.uid), original);
848
+ for (const p of pushed)
849
+ p.status = 'done';
850
+ const where = await landed(client, original, moved, result);
851
+ for (const [i, m] of moved.entries()) {
852
+ const newUid = where.uids.get(m.target.id) ?? null;
853
+ const prev = itemOf.get(m.target.id).prevLabels;
854
+ await relocateRow(db, m.target, destId, newUid === null ? null : where.uidValidity, newUid, facts.gmail && prev ? plainLabels(prev) : undefined);
855
+ pushed[i].toUidValidity = where.uidValidity ?? 0;
856
+ pushed[i].toUid = newUid;
857
+ }
858
+ // Gmail drops a message's labels in Trash: give back the ones it had.
859
+ if (facts.gmail) {
860
+ const byLabels = new Map();
861
+ for (const m of moved) {
862
+ const prev = plainLabels(itemOf.get(m.target.id).prevLabels);
863
+ const uid = where.uids.get(m.target.id);
864
+ if (prev.length === 0 || uid === null || uid === undefined)
865
+ continue;
866
+ const key = JSON.stringify(prev);
867
+ byLabels.set(key, [...(byLabels.get(key) ?? []), uid]);
868
+ }
869
+ for (const [key, uids] of byLabels)
870
+ await client.addLabels(original, uids, JSON.parse(key));
871
+ }
872
+ await save();
873
+ }
874
+ if (viaLabels.length > 0) {
875
+ await undoLabelMove(db, client, { folder, original, destId, source, list: viaLabels, here, itemOf, items, save });
876
+ }
591
877
  }
592
- const list = back.get(original) ?? [];
593
- list.push(t);
594
- back.set(original, list);
595
878
  }
596
- for (const [original, list] of back) {
597
- if (!facts.listing.some((f) => f.name === original)) {
598
- skipped += list.length;
879
+ }
880
+ catch (err) {
881
+ const undo = await finishTrail(db, trailId, items, { error: err, skippedNote: skippedWords(reasons) }).catch(() => null);
882
+ await markReverted(db, action, doneIds(), false, undo?.id ?? null, input.now).catch(() => { });
883
+ throw err;
884
+ }
885
+ const undo = await finishTrail(db, trailId, items, { skippedNote: skippedWords(reasons) });
886
+ const changed = undo?.changed ?? 0;
887
+ if (undo)
888
+ await markReverted(db, action, doneIds(), true, undo.id, input.now);
889
+ return { action: undo, changed, skipped: [...reasons.values()].reduce((a, b) => a + b, 0), note: summary(changed) };
890
+ }
891
+ /**
892
+ * Undo a Gmail label move exactly: the labels the message had before come
893
+ * back, any added since go, and Inbox goes back on — over X-GM-LABELS, so the
894
+ * message never passes through a MOVE that would drop one. The label it was
895
+ * moved to comes off last, from the inbox copy, so the message is never
896
+ * without a home in between.
897
+ */
898
+ async function undoLabelMove(db, client, input) {
899
+ const { folder, original, list, here, itemOf, items, save } = input;
900
+ const uidOf = (t) => here.get(t.id).uid;
901
+ const now = await client.fetchLabels(folder, list.map(uidOf));
902
+ // One set of commands per distinct (before, now): usually all of them.
903
+ const groups = new Map();
904
+ for (const t of list) {
905
+ const prev = plainLabels(itemOf.get(t.id).prevLabels);
906
+ const current = plainLabels(now.get(uidOf(t)));
907
+ const key = JSON.stringify([prev, current]);
908
+ const g = groups.get(key) ?? { prev, current, targets: [] };
909
+ g.targets.push(t);
910
+ groups.set(key, g);
911
+ }
912
+ for (const { prev, current, targets } of groups.values()) {
913
+ const pushed = targets.map((t) => ({
914
+ id: t.id, subject: t.subject, from: t.from, messageId: t.messageId,
915
+ fromFolder: folder, fromUidValidity: input.source.uidValidity, fromUid: uidOf(t),
916
+ toFolder: original, toUidValidity: 0, toUid: null, prevLabels: current, via: 'labels', status: 'planned',
917
+ }));
918
+ items.push(...pushed);
919
+ await save();
920
+ const uids = targets.map(uidOf);
921
+ const add = prev.filter((l) => !current.includes(l));
922
+ const remove = current.filter((l) => !prev.includes(l) && l !== folder);
923
+ if (add.length > 0)
924
+ await client.storeLabels(folder, uids, add, 'add');
925
+ if (remove.length > 0)
926
+ await client.storeLabels(folder, uids, remove, 'remove');
927
+ await client.storeLabels(folder, uids, [GMAIL_INBOX_LABEL], 'add');
928
+ for (const p of pushed)
929
+ p.status = 'done';
930
+ // Back in the inbox; now find each one there and take the moved-to label off.
931
+ const status = await client.open(original);
932
+ for (const [i, t] of targets.entries()) {
933
+ const uid = t.messageId ? await client.findByMessageId(original, t.messageId) : null;
934
+ if (uid !== null && !prev.includes(folder))
935
+ await client.storeLabels(original, [uid], [folder], 'remove');
936
+ await relocateRow(db, t, input.destId, uid === null ? null : status.uidValidity, uid, prev);
937
+ pushed[i].toUidValidity = uid === null ? 0 : status.uidValidity;
938
+ pushed[i].toUid = uid;
939
+ }
940
+ await save();
941
+ }
942
+ }
943
+ /* ------------------------------------------------------------------ *
944
+ * After a crash: what the server says happened
945
+ * ------------------------------------------------------------------ */
946
+ /**
947
+ * Settle the `pending` trail rows a stopped process left for this account:
948
+ * for each message planned and never confirmed, ask the server by Message-ID
949
+ * where it is. Still where it was: it did not happen — the item goes. In
950
+ * the destination: it happened — the row follows and the item is done.
951
+ * Neither, or no Message-ID to ask with: unknown. The row becomes `done`, `partial`
952
+ * (something did not happen) or `unknown`, or goes when nothing happened. An
953
+ * interrupted undo puts what it did on its change's `reverted_ids`.
954
+ */
955
+ export async function reconcileTrail(db, client, input) {
956
+ const { rows } = await db.query(`select ${ACTION_COLUMNS} from email.mailbox_actions
957
+ where account_id = $1 and state = 'pending' and not (id = any($2::uuid[]))
958
+ order by created_at, seq`, [input.account.id, trailInFlight()]);
959
+ if (rows.length === 0)
960
+ return { settled: 0 };
961
+ const facts = await serverFacts(client);
962
+ let settled = 0;
963
+ for (const action of rows.map(toAction)) {
964
+ const items = action.items.map((i) => ({ ...i }));
965
+ let dropped = 0;
966
+ for (const item of items) {
967
+ if (statusOf(item) !== 'planned')
968
+ continue;
969
+ const target = (await loadTargets(db, [item.id]))[0];
970
+ if (item.toFolder) {
971
+ /*
972
+ * Gmail's label move is two commands — the label on, then Inbox off
973
+ * (an undo: the labels back, Inbox on, then the label off) — and a
974
+ * crash between them leaves the message where it was *and* changed.
975
+ * The Inbox step is the move itself, so a half-done one is finished
976
+ * here: its second command is sent, and the item goes on as done.
977
+ */
978
+ if (facts.gmail && item.via === 'labels' && item.messageId) {
979
+ await finishLabelMove(db, client, action, item);
980
+ }
981
+ // Where it was first: on Gmail, All Mail also holds what is still
982
+ // in the inbox, so "found in the destination" alone proves nothing.
983
+ const stayed = item.messageId ? await client.findByMessageId(item.fromFolder, item.messageId) : null;
984
+ if (stayed !== null) {
985
+ item.status = 'dropped';
986
+ dropped += 1;
987
+ continue;
988
+ }
989
+ const there = item.messageId ? await client.findByMessageId(item.toFolder, item.messageId) : null;
990
+ if (there !== null) {
991
+ const status = await client.open(item.toFolder);
992
+ item.status = 'done';
993
+ item.toUidValidity = status.uidValidity;
994
+ item.toUid = there;
995
+ if (target) {
996
+ const destId = await folderRow(db, input.account.id, item.toFolder, specialUseOf(facts.listing, item.toFolder));
997
+ let labels;
998
+ if (facts.gmail)
999
+ labels = plainLabels((await client.fetchLabels(item.toFolder, [there])).get(there));
1000
+ await relocateRow(db, target, destId, status.uidValidity, there, labels);
1001
+ }
1002
+ continue;
1003
+ }
1004
+ item.status = 'unknown';
599
1005
  continue;
600
1006
  }
601
- const source = await client.open(folder);
602
- const moved = list.map((t) => ({ target: t, uid: here.get(t.id).uid }));
603
- const result = await client.move(folder, moved.map((m) => m.uid), original);
604
- const where = await landed(client, original, moved, result);
605
- const destId = await folderRow(db, account.id, original);
606
- for (const m of moved) {
607
- const newUid = where.uids.get(m.target.id) ?? null;
608
- await relocateRow(db, m.target, destId, newUid === null ? null : where.uidValidity, newUid);
609
- items.push({
610
- id: m.target.id, subject: m.target.subject, from: m.target.from, messageId: m.target.messageId,
611
- fromFolder: folder, fromUidValidity: source.uidValidity, fromUid: m.uid,
612
- toFolder: original, toUidValidity: where.uidValidity ?? 0, toUid: newUid,
613
- });
1007
+ // A flag change: is the read mark what the change wanted?
1008
+ const status = await client.open(item.fromFolder);
1009
+ let uid = item.fromUidValidity === status.uidValidity && item.fromUid > 0 ? item.fromUid : null;
1010
+ if (uid === null && item.messageId)
1011
+ uid = await client.findByMessageId(item.fromFolder, item.messageId);
1012
+ const flags = uid === null ? undefined : (await client.fetchFlags(item.fromFolder, [uid]))[0]?.flags;
1013
+ if (!flags) {
1014
+ item.status = 'unknown';
614
1015
  }
615
- // Gmail drops a message's labels in Trash: give back the ones it had.
616
- if (facts.gmail) {
617
- const byLabels = new Map();
618
- for (const m of moved) {
619
- const prev = itemOf.get(m.target.id).prevLabels ?? [];
620
- const uid = where.uids.get(m.target.id);
621
- if (prev.length === 0 || uid === null || uid === undefined)
622
- continue;
623
- const key = JSON.stringify([...prev].sort());
624
- byLabels.set(key, [...(byLabels.get(key) ?? []), uid]);
625
- }
626
- for (const [key, uids] of byLabels)
627
- await client.addLabels(original, uids, JSON.parse(key));
1016
+ else if (flags.includes(SEEN) === (item.wantSeen ?? action.kind === 'mark-read')) {
1017
+ item.status = 'done';
1018
+ if (target)
1019
+ await setSeenLocally(db, [target.id], flags.includes(SEEN));
1020
+ }
1021
+ else {
1022
+ item.status = 'dropped';
1023
+ dropped += 1;
1024
+ }
1025
+ }
1026
+ const kept = items.filter((i) => statusOf(i) !== 'dropped');
1027
+ const done = kept.filter((i) => statusOf(i) === 'done');
1028
+ const unknown = kept.filter((i) => statusOf(i) === 'unknown');
1029
+ if (done.length === 0 && unknown.length === 0) {
1030
+ await db.query(`delete from email.mailbox_actions where id = $1`, [action.id]);
1031
+ }
1032
+ else {
1033
+ const state = unknown.length > 0 ? 'unknown' : dropped > 0 ? 'partial' : 'done';
1034
+ const note = `buddi stopped while making this change; checked with the server afterwards: ${plural(done.length, 'message')} changed` +
1035
+ (dropped > 0 ? `, ${dropped} not` : '') +
1036
+ (unknown.length > 0 ? `, ${unknown.length} could not be confirmed (not found where it was or where it was going)` : '') +
1037
+ '.';
1038
+ await db.query(`update email.mailbox_actions set state = $2, note = $3, items = $4::jsonb, message_ids = $5::uuid[], changed = $6 where id = $1`, [action.id, state, note, JSON.stringify(kept), kept.map((i) => i.id), done.length]);
1039
+ if (action.kind === 'undo' && action.reverts) {
1040
+ const original = await findAction(db, action.reverts);
1041
+ if (original)
1042
+ await markReverted(db, original, done.map((i) => i.id), false, null, input.now);
628
1043
  }
629
1044
  }
1045
+ settled += 1;
630
1046
  }
631
- const note = items.length === 0
632
- ? `Nothing could be put back in ${account.address}${skipped > 0 ? ` (${plural(skipped, 'message')} no longer where the change left ${skipped === 1 ? 'it' : 'them'})` : ''}.`
633
- : `Undid "${verbOf(action.kind, action.destination)}" on ${plural(items.length, 'message')} in ${account.address}.${skipped > 0 ? ` ${plural(skipped, 'message')} could not be put back: no longer where the change left ${skipped === 1 ? 'it' : 'them'}.` : ''}`;
634
- if (items.length === 0)
635
- return { action: null, changed: 0, skipped, note };
636
- const undo = await recordAction(db, {
637
- accountId: account.id,
638
- kind: 'undo',
639
- destination: null,
640
- provenance: input.provenance,
641
- messageIds: items.map((i) => i.id),
642
- items,
643
- changed: items.length,
644
- note: skipped > 0 ? `${plural(skipped, 'message')} could not be put back.` : null,
645
- reverts: action.id,
646
- now: input.now,
647
- });
648
- await db.query(`update email.mailbox_actions set undone_at = $2, undone_by = $3 where id = $1`, [
649
- action.id,
650
- input.now,
651
- undo.id,
652
- ]);
653
- return { action: undo, changed: items.length, skipped, note };
1047
+ return { settled };
1048
+ }
1049
+ /**
1050
+ * A Gmail label move (or its undo) a stopped process left between its two
1051
+ * commands, finished from what the server says (X-GM-LABELS):
1052
+ *
1053
+ * - a move (Inbox → label): still in the inbox but already carrying the
1054
+ * label it did not have before — Inbox comes off;
1055
+ * - an undo (label → Inbox): already back in the inbox but still carrying
1056
+ * the label it was moved to — the label comes off, unless the message
1057
+ * had it before the original move.
1058
+ *
1059
+ * Anything else is left for the ordinary check (never happened, happened,
1060
+ * or unknown). A message the owner touched since cannot be told apart, so
1061
+ * only the exact half-done shape is finished.
1062
+ */
1063
+ async function finishLabelMove(db, client, action, item) {
1064
+ const toInbox = item.toFolder.toUpperCase() === INBOX;
1065
+ const uid = await client.findByMessageId(INBOX, item.messageId);
1066
+ if (uid === null)
1067
+ return;
1068
+ const labels = plainLabels((await client.fetchLabels(INBOX, [uid])).get(uid));
1069
+ if (!toInbox) {
1070
+ const before = plainLabels(item.prevLabels);
1071
+ if (labels.includes(item.toFolder) && !before.includes(item.toFolder)) {
1072
+ await client.storeLabels(INBOX, [uid], [GMAIL_INBOX_LABEL], 'remove');
1073
+ }
1074
+ return;
1075
+ }
1076
+ if (!labels.includes(item.fromFolder))
1077
+ return;
1078
+ const original = action.reverts ? await findAction(db, action.reverts) : null;
1079
+ const before = original?.items.find((i) => i.id === item.id)?.prevLabels;
1080
+ if (before === undefined || plainLabels(before).includes(item.fromFolder))
1081
+ return;
1082
+ await client.storeLabels(INBOX, [uid], [item.fromFolder], 'remove');
1083
+ }
1084
+ /**
1085
+ * The poll's half of intent-first: when this account has `pending` trail rows
1086
+ * no change in this process is writing, open a writer and settle them
1087
+ * (`reconcileTrail`). One cheap query when there are none.
1088
+ */
1089
+ export async function reconcilePendingChanges(ctx, account, opts) {
1090
+ const db = ctx.buddi.db;
1091
+ const { rows } = await db.query(`select 1 from email.mailbox_actions
1092
+ where account_id = $1 and state = 'pending' and not (id = any($2::uuid[])) limit 1`, [account.id, trailInFlight()]);
1093
+ if (rows.length === 0)
1094
+ return 0;
1095
+ const { settled } = await withWriter(ctx, account, opts, (client) => reconcileTrail(db, client, { account, now: ctx.buddi.clock.now() }));
1096
+ return settled;
654
1097
  }
655
1098
  /** The newest changes, for the Mail page's Recent changes. */
656
1099
  export async function recentActions(db, accountIds, limit = 20) {