@vellumai/assistant 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (692) hide show
  1. package/AGENTS.md +11 -0
  2. package/Dockerfile +5 -4
  3. package/README.md +2 -2
  4. package/docker-entrypoint.sh +16 -0
  5. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  6. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  7. package/eslint.config.mjs +12 -0
  8. package/knip.json +2 -1
  9. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  10. package/openapi.yaml +4847 -1698
  11. package/package.json +3 -1
  12. package/scripts/generate-openapi.ts +52 -4
  13. package/scripts/sync-llm-catalog.ts +165 -0
  14. package/scripts/sync-web-search-catalog.ts +107 -0
  15. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  16. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  17. package/src/__tests__/anthropic-provider.test.ts +92 -2
  18. package/src/__tests__/app-control-flow.test.ts +7 -0
  19. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  20. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  21. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  22. package/src/__tests__/btw-routes.test.ts +1 -0
  23. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  25. package/src/__tests__/channel-policy.test.ts +12 -0
  26. package/src/__tests__/checker.test.ts +89 -0
  27. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
  28. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  29. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  30. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  31. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  32. package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
  33. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  34. package/src/__tests__/config-schema.test.ts +14 -3
  35. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  36. package/src/__tests__/config-set-route.test.ts +198 -0
  37. package/src/__tests__/config-watcher.test.ts +6 -0
  38. package/src/__tests__/contacts-tools.test.ts +51 -199
  39. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  40. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  41. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  42. package/src/__tests__/context-search-fanout.test.ts +20 -157
  43. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  44. package/src/__tests__/context-search-types.test.ts +7 -2
  45. package/src/__tests__/context-window-manager.test.ts +389 -1
  46. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  47. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  48. package/src/__tests__/conversation-error.test.ts +38 -0
  49. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  50. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  51. package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
  52. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  54. package/src/__tests__/conversation-process-callsite.test.ts +21 -1
  55. package/src/__tests__/conversation-runtime-assembly.test.ts +4 -4
  56. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  57. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  58. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  59. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  60. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  61. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  62. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  63. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  64. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  65. package/src/__tests__/filing-service.test.ts +23 -3
  66. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  67. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  68. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  69. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -8
  70. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  71. package/src/__tests__/heartbeat-service.test.ts +50 -233
  72. package/src/__tests__/history-repair.test.ts +89 -0
  73. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  74. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  75. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  76. package/src/__tests__/host-browser-routes.test.ts +325 -33
  77. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  78. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  79. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  80. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  81. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  82. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  83. package/src/__tests__/install-skill-routing.test.ts +2 -2
  84. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +15 -0
  85. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  86. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  87. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  88. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  89. package/src/__tests__/llm-resolver.test.ts +46 -0
  90. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  91. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  92. package/src/__tests__/mcp-cli.test.ts +182 -220
  93. package/src/__tests__/mcp-health-check.test.ts +56 -27
  94. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  95. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  96. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  97. package/src/__tests__/oauth-cli.test.ts +38 -2009
  98. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  99. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  100. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  102. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  103. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  104. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  105. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  106. package/src/__tests__/plugin-types.test.ts +13 -11
  107. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  108. package/src/__tests__/profile-entry-status.test.ts +43 -0
  109. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  110. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  111. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  112. package/src/__tests__/relay-server.test.ts +118 -0
  113. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  114. package/src/__tests__/schedule-retry.test.ts +56 -4
  115. package/src/__tests__/schedule-routes.test.ts +104 -0
  116. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  117. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  118. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  119. package/src/__tests__/scheduler-wake.test.ts +0 -63
  120. package/src/__tests__/secret-allowlist.test.ts +1 -0
  121. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  122. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  123. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  124. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  125. package/src/__tests__/skill-load-tool.test.ts +2 -4
  126. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  127. package/src/__tests__/suggestion-routes.test.ts +3 -3
  128. package/src/__tests__/sync-message-contract.test.ts +63 -0
  129. package/src/__tests__/task-scheduler.test.ts +88 -23
  130. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  131. package/src/__tests__/usage-cli.test.ts +11 -73
  132. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  133. package/src/__tests__/vercel-config.test.ts +168 -0
  134. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  135. package/src/__tests__/web-search.test.ts +303 -2
  136. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  137. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  138. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
  139. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  140. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  141. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  142. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  143. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  144. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  145. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  146. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  147. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  148. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  149. package/src/acp/resolve-agent.test.ts +25 -0
  150. package/src/acp/resolve-agent.ts +13 -2
  151. package/src/acp/session-manager.ts +14 -0
  152. package/src/approvals/guardian-request-resolvers.ts +32 -87
  153. package/src/calls/relay-server.ts +35 -0
  154. package/src/calls/relay-setup-router.ts +36 -0
  155. package/src/calls/types.ts +1 -0
  156. package/src/calls/voice-session-bridge.ts +23 -4
  157. package/src/channels/config.ts +14 -1
  158. package/src/channels/types.ts +1 -0
  159. package/src/cli/AGENTS.md +164 -4
  160. package/src/cli/__tests__/notifications.test.ts +54 -0
  161. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  162. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  163. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  164. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  165. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  166. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  167. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  168. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  169. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  170. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  171. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  172. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  173. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  174. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  175. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  176. package/src/cli/commands/__tests__/status.test.ts +249 -0
  177. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  178. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  179. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  180. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  181. package/src/cli/commands/attachment.ts +8 -3
  182. package/src/cli/commands/audit.ts +95 -64
  183. package/src/cli/commands/auth.ts +61 -58
  184. package/src/cli/commands/avatar.ts +276 -390
  185. package/src/cli/commands/backup.ts +409 -505
  186. package/src/cli/commands/bash.ts +9 -5
  187. package/src/cli/commands/browser.ts +28 -9
  188. package/src/cli/commands/cache.ts +9 -4
  189. package/src/cli/commands/changelog.ts +414 -0
  190. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  191. package/src/cli/commands/clients.ts +8 -3
  192. package/src/cli/commands/completions.ts +9 -9
  193. package/src/cli/commands/config.ts +102 -72
  194. package/src/cli/commands/contacts.ts +575 -696
  195. package/src/cli/commands/conversations-defer.ts +17 -69
  196. package/src/cli/commands/conversations-import.ts +90 -253
  197. package/src/cli/commands/conversations.ts +346 -436
  198. package/src/cli/commands/credential-execution.ts +9 -6
  199. package/src/cli/commands/credentials.ts +456 -736
  200. package/src/cli/commands/domain.ts +128 -206
  201. package/src/cli/commands/email.ts +606 -794
  202. package/src/cli/commands/gateway.ts +8 -1
  203. package/src/cli/commands/image-generation.ts +157 -205
  204. package/src/cli/commands/inference-providers.ts +352 -0
  205. package/src/cli/commands/inference-session.ts +415 -0
  206. package/src/cli/commands/inference.ts +87 -65
  207. package/src/cli/commands/keys.ts +8 -3
  208. package/src/cli/commands/mcp.ts +103 -287
  209. package/src/cli/commands/memory-v2.ts +162 -516
  210. package/src/cli/commands/notifications.ts +33 -7
  211. package/src/cli/commands/oauth/apps.ts +292 -261
  212. package/src/cli/commands/oauth/connect.ts +176 -297
  213. package/src/cli/commands/oauth/disconnect.ts +16 -215
  214. package/src/cli/commands/oauth/index.ts +49 -45
  215. package/src/cli/commands/oauth/mode.ts +43 -199
  216. package/src/cli/commands/oauth/ping.ts +17 -125
  217. package/src/cli/commands/oauth/providers.ts +732 -921
  218. package/src/cli/commands/oauth/request.ts +60 -350
  219. package/src/cli/commands/oauth/shared.ts +11 -121
  220. package/src/cli/commands/oauth/status.ts +31 -121
  221. package/src/cli/commands/oauth/token.ts +13 -55
  222. package/src/cli/commands/pending.ts +19 -10
  223. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  224. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  225. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  226. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  227. package/src/cli/commands/platform/connect.ts +16 -80
  228. package/src/cli/commands/platform/disconnect.ts +14 -112
  229. package/src/cli/commands/platform/index.ts +177 -246
  230. package/src/cli/commands/routes.ts +153 -336
  231. package/src/cli/commands/sequence.ts +316 -360
  232. package/src/cli/commands/skills.ts +449 -671
  233. package/src/cli/commands/status.ts +58 -37
  234. package/src/cli/commands/stt.ts +94 -262
  235. package/src/cli/commands/task.ts +14 -40
  236. package/src/cli/commands/trust.ts +8 -3
  237. package/src/cli/commands/tts.ts +162 -167
  238. package/src/cli/commands/ui.ts +35 -42
  239. package/src/cli/commands/usage.ts +188 -126
  240. package/src/cli/commands/watchers.ts +8 -3
  241. package/src/cli/commands/webhooks.ts +99 -193
  242. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  243. package/src/cli/lib/daemon-credential-client.ts +4 -5
  244. package/src/cli/lib/nested-value.ts +44 -0
  245. package/src/cli/lib/open-browser.ts +36 -0
  246. package/src/cli/lib/register-command.ts +19 -0
  247. package/src/cli/lib/time-ago.ts +34 -0
  248. package/src/cli/program.ts +2 -4
  249. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  250. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  251. package/src/cli/utils/conversation-id.ts +30 -0
  252. package/src/cli/utils/parse-duration.ts +41 -0
  253. package/src/config/acp-defaults.test.ts +5 -1
  254. package/src/config/acp-defaults.ts +11 -4
  255. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  256. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  258. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  259. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  260. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  261. package/src/config/bundled-tool-registry.ts +0 -2
  262. package/src/config/feature-flag-registry.json +16 -0
  263. package/src/config/llm-resolver.ts +16 -1
  264. package/src/config/loader.ts +76 -14
  265. package/src/config/raw-config-utils.ts +2 -30
  266. package/src/config/schema.ts +4 -0
  267. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  268. package/src/config/schemas/call-site-catalog.ts +29 -7
  269. package/src/config/schemas/llm-request-logs.ts +57 -0
  270. package/src/config/schemas/llm.ts +52 -2
  271. package/src/config/schemas/memory-retrospective.ts +48 -0
  272. package/src/config/schemas/memory-v2.ts +32 -1
  273. package/src/config/schemas/memory.ts +4 -0
  274. package/src/config/schemas/services.ts +15 -12
  275. package/src/config/seed-inference-profiles.ts +195 -134
  276. package/src/contacts/contact-store.ts +0 -61
  277. package/src/context/window-manager.ts +191 -5
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +79 -0
  279. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  280. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  281. package/src/daemon/approval-generators.ts +23 -29
  282. package/src/daemon/config-watcher.ts +2 -0
  283. package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
  284. package/src/daemon/conversation-agent-loop.ts +127 -97
  285. package/src/daemon/conversation-error.ts +21 -0
  286. package/src/daemon/conversation-lifecycle.ts +46 -5
  287. package/src/daemon/conversation-process.ts +36 -19
  288. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  289. package/src/daemon/conversation-slash.ts +175 -23
  290. package/src/daemon/conversation-store.ts +17 -10
  291. package/src/daemon/conversation-surfaces.ts +76 -12
  292. package/src/daemon/conversation-tool-setup.ts +24 -14
  293. package/src/daemon/conversation.ts +48 -9
  294. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  295. package/src/daemon/guardian-action-generators.ts +7 -22
  296. package/src/daemon/handlers/config-model.ts +8 -126
  297. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  298. package/src/daemon/handlers/config-vercel.ts +3 -1
  299. package/src/daemon/handlers/skills.ts +84 -5
  300. package/src/daemon/history-repair.ts +33 -6
  301. package/src/daemon/host-app-control-proxy.ts +44 -19
  302. package/src/daemon/host-bash-proxy.ts +85 -158
  303. package/src/daemon/host-browser-proxy.ts +96 -35
  304. package/src/daemon/host-proxy-base.ts +13 -1
  305. package/src/daemon/host-proxy-preactivation.ts +25 -1
  306. package/src/daemon/identity-helpers.ts +19 -0
  307. package/src/daemon/lifecycle.ts +42 -43
  308. package/src/daemon/meet-host-supervisor.ts +15 -15
  309. package/src/daemon/memory-v2-startup.ts +9 -2
  310. package/src/daemon/message-protocol.ts +6 -0
  311. package/src/daemon/message-types/bookmarks.ts +18 -0
  312. package/src/daemon/message-types/conversations.ts +12 -9
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/sync.ts +60 -0
  315. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  316. package/src/daemon/pkb-reminder-builder.ts +21 -7
  317. package/src/daemon/process-message.ts +56 -23
  318. package/src/daemon/server.ts +23 -18
  319. package/src/daemon/shutdown-handlers.ts +0 -2
  320. package/src/daemon/tool-setup-types.ts +9 -0
  321. package/src/daemon/tool-side-effects.ts +6 -4
  322. package/src/daemon/wake-target-adapter.ts +11 -0
  323. package/src/export/transcript-formatter.ts +61 -2
  324. package/src/filing/filing-service.ts +40 -53
  325. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  326. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  327. package/src/heartbeat/heartbeat-service.ts +148 -127
  328. package/src/home/__tests__/feed-types.test.ts +63 -131
  329. package/src/home/__tests__/feed-writer.test.ts +77 -278
  330. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  331. package/src/home/feed-types.ts +19 -73
  332. package/src/home/feed-writer.ts +25 -156
  333. package/src/home/post-connect-feed.ts +1 -3
  334. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  335. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  336. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  337. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  338. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  339. package/src/ipc/assistant-server.ts +55 -6
  340. package/src/ipc/cli-client.ts +370 -50
  341. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  342. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  343. package/src/ipc/skill-routes/events.ts +30 -3
  344. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  345. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  346. package/src/live-voice/live-voice-session-manager.ts +11 -4
  347. package/src/live-voice/live-voice-session.ts +14 -6
  348. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  349. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  350. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  351. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  352. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  353. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  354. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  355. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  356. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  357. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  358. package/src/memory/bookmark-crud.ts +179 -0
  359. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  360. package/src/memory/context-search/agent-protocol.ts +5 -1
  361. package/src/memory/context-search/agent-runner.ts +60 -85
  362. package/src/memory/context-search/limits.ts +1 -4
  363. package/src/memory/context-search/search.ts +23 -113
  364. package/src/memory/context-search/sources/conversations.ts +18 -6
  365. package/src/memory/context-search/sources/memory-v2.ts +39 -14
  366. package/src/memory/context-search/sources/memory.ts +7 -0
  367. package/src/memory/context-search/sources/workspace.ts +13 -10
  368. package/src/memory/context-search/types.ts +1 -1
  369. package/src/memory/conversation-bootstrap.ts +11 -0
  370. package/src/memory/conversation-crud.ts +312 -10
  371. package/src/memory/conversation-queries.ts +9 -5
  372. package/src/memory/conversation-title-service.ts +1 -0
  373. package/src/memory/conversation-types.ts +16 -0
  374. package/src/memory/db-init.ts +14 -0
  375. package/src/memory/embedding-backend.ts +2 -1
  376. package/src/memory/embedding-runtime-manager.ts +1 -2
  377. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  378. package/src/memory/graph/conversation-graph-memory.ts +76 -5
  379. package/src/memory/graph/extraction.ts +4 -0
  380. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  381. package/src/memory/graph/tool-handlers.ts +17 -7
  382. package/src/memory/graph/tools.ts +44 -5
  383. package/src/memory/indexer.ts +17 -0
  384. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +13 -15
  385. package/src/memory/jobs/embed-concept-page.ts +45 -9
  386. package/src/memory/jobs-store.ts +51 -1
  387. package/src/memory/jobs-worker.ts +52 -3
  388. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  389. package/src/memory/llm-request-log-source-local.ts +26 -0
  390. package/src/memory/llm-request-log-source.ts +97 -0
  391. package/src/memory/llm-request-log-store.ts +1 -1
  392. package/src/memory/memory-retrospective-constants.ts +13 -0
  393. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  394. package/src/memory/memory-retrospective-job.ts +351 -0
  395. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  396. package/src/memory/memory-retrospective-state.ts +162 -0
  397. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  398. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  399. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  400. package/src/memory/message-content.ts +38 -1
  401. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  402. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  403. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  404. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  405. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  406. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  407. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  408. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  409. package/src/memory/migrations/243-provider-connections.ts +68 -0
  410. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  411. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  412. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  413. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  414. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  415. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  416. package/src/memory/migrations/index.ts +7 -0
  417. package/src/memory/published-pages-store.ts +16 -0
  418. package/src/memory/schema/bookmarks.ts +38 -0
  419. package/src/memory/schema/conversations.ts +2 -0
  420. package/src/memory/schema/index.ts +2 -0
  421. package/src/memory/schema/inference.ts +29 -0
  422. package/src/memory/schema/memory-core.ts +9 -0
  423. package/src/memory/search/semantic.ts +1 -4
  424. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  425. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  426. package/src/memory/v2/__tests__/activation.test.ts +11 -4
  427. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  428. package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
  429. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  430. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  431. package/src/memory/v2/__tests__/injection.test.ts +628 -10
  432. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  433. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  434. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  435. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  436. package/src/memory/v2/__tests__/qdrant.test.ts +72 -0
  437. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  438. package/src/memory/v2/__tests__/router.test.ts +516 -0
  439. package/src/memory/v2/__tests__/sim.test.ts +45 -1
  440. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  441. package/src/memory/v2/__tests__/static-context.test.ts +7 -22
  442. package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
  443. package/src/memory/v2/activation-store.ts +34 -5
  444. package/src/memory/v2/activation.ts +40 -27
  445. package/src/memory/v2/backfill-jobs.ts +17 -84
  446. package/src/memory/v2/consolidation-job.ts +85 -78
  447. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  448. package/src/memory/v2/injection.ts +440 -109
  449. package/src/memory/v2/migration.ts +117 -20
  450. package/src/memory/v2/page-index.ts +191 -0
  451. package/src/memory/v2/page-store.ts +3 -0
  452. package/src/memory/v2/prompts/consolidation.ts +9 -7
  453. package/src/memory/v2/prompts/router.ts +192 -0
  454. package/src/memory/v2/qdrant.ts +100 -87
  455. package/src/memory/v2/reranker.ts +14 -7
  456. package/src/memory/v2/router.ts +322 -0
  457. package/src/memory/v2/sim.ts +25 -12
  458. package/src/memory/v2/skill-store.ts +118 -29
  459. package/src/memory/v2/static-context.ts +16 -9
  460. package/src/memory/v2/sweep-job.ts +122 -96
  461. package/src/memory/v2/types.ts +10 -6
  462. package/src/memory/validation.ts +13 -0
  463. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  464. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  465. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  466. package/src/notifications/adapters/platform.ts +171 -0
  467. package/src/notifications/conversation-pairing.ts +2 -2
  468. package/src/notifications/copy-composer.ts +15 -0
  469. package/src/notifications/destination-resolver.ts +21 -0
  470. package/src/notifications/emit-signal.ts +28 -1
  471. package/src/notifications/home-feed-side-effect.ts +111 -0
  472. package/src/notifications/signal.ts +5 -0
  473. package/src/permissions/checker.ts +12 -0
  474. package/src/permissions/ipc-risk-types.ts +2 -0
  475. package/src/plugin-api/index.ts +13 -0
  476. package/src/plugin-api/package.json +12 -0
  477. package/src/plugin-api/types.ts +62 -0
  478. package/src/plugins/defaults/injectors.ts +19 -3
  479. package/src/plugins/external-plugin-loader.ts +294 -0
  480. package/src/plugins/types.ts +46 -30
  481. package/src/plugins/user-loader.ts +64 -41
  482. package/src/proactive-artifact/job.test.ts +12 -4
  483. package/src/proactive-artifact/job.ts +4 -0
  484. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  485. package/src/proactive-artifact/trigger-state.ts +4 -0
  486. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  487. package/src/prompts/system-prompt.ts +22 -1
  488. package/src/prompts/update-bulletin-job.ts +61 -73
  489. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  490. package/src/providers/__tests__/inference.test.ts +288 -0
  491. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  492. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  493. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  494. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  495. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  496. package/src/providers/anthropic/client.ts +95 -26
  497. package/src/providers/call-site-routing.ts +94 -16
  498. package/src/providers/connection-resolution.ts +163 -0
  499. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  500. package/src/providers/inference/adapter-factory.ts +173 -0
  501. package/src/providers/inference/auth.ts +112 -0
  502. package/src/providers/inference/backfill.ts +196 -0
  503. package/src/providers/inference/connections.ts +356 -0
  504. package/src/providers/inference/resolve-auth.ts +65 -0
  505. package/src/providers/model-catalog.ts +104 -6
  506. package/src/providers/openai/responses-provider.ts +4 -2
  507. package/src/providers/provider-env-vars.ts +17 -7
  508. package/src/providers/provider-secret-catalog.ts +49 -30
  509. package/src/providers/provider-send-message.ts +41 -20
  510. package/src/providers/registry.ts +143 -159
  511. package/src/providers/retry.ts +18 -10
  512. package/src/providers/search-provider-catalog.ts +121 -0
  513. package/src/runtime/AGENTS.md +18 -5
  514. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  515. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  516. package/src/runtime/actor-trust-resolver.ts +32 -10
  517. package/src/runtime/agent-wake.ts +35 -6
  518. package/src/runtime/assistant-event-hub.ts +3 -85
  519. package/src/runtime/auth/route-policy.ts +303 -8
  520. package/src/runtime/auth/same-actor.ts +2 -0
  521. package/src/runtime/background-job-runner.ts +339 -0
  522. package/src/runtime/btw-sidechain.ts +1 -0
  523. package/src/runtime/http-router.ts +36 -1
  524. package/src/runtime/http-server.ts +31 -5
  525. package/src/runtime/http-types.ts +2 -0
  526. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  527. package/src/runtime/middleware/request-logger.ts +62 -1
  528. package/src/runtime/pre-first-message-gate.ts +83 -0
  529. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  530. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  531. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  532. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  533. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  534. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  535. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  536. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
  537. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  538. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  539. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  540. package/src/runtime/routes/acp-routes.ts +10 -8
  541. package/src/runtime/routes/app-management-routes.ts +228 -3
  542. package/src/runtime/routes/approval-routes.ts +0 -18
  543. package/src/runtime/routes/audit-routes.ts +43 -0
  544. package/src/runtime/routes/auth-routes.ts +72 -0
  545. package/src/runtime/routes/avatar-routes.ts +273 -20
  546. package/src/runtime/routes/backup-routes.ts +406 -2
  547. package/src/runtime/routes/bookmark-routes.ts +154 -0
  548. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  549. package/src/runtime/routes/contact-routes.ts +0 -160
  550. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  551. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  552. package/src/runtime/routes/conversation-query-routes.ts +334 -86
  553. package/src/runtime/routes/conversation-routes.ts +31 -10
  554. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  555. package/src/runtime/routes/credential-routes.ts +540 -0
  556. package/src/runtime/routes/debug-routes.ts +2 -2
  557. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  558. package/src/runtime/routes/domain-routes.ts +167 -0
  559. package/src/runtime/routes/email-routes.ts +603 -0
  560. package/src/runtime/routes/errors.ts +2 -2
  561. package/src/runtime/routes/events-routes.ts +192 -0
  562. package/src/runtime/routes/home-feed-routes.ts +6 -78
  563. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  564. package/src/runtime/routes/host-browser-routes.ts +103 -22
  565. package/src/runtime/routes/http-adapter.ts +2 -0
  566. package/src/runtime/routes/identity-routes.ts +5 -0
  567. package/src/runtime/routes/image-generation-routes.ts +99 -0
  568. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  569. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  570. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  571. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
  572. package/src/runtime/routes/index.ts +36 -0
  573. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  574. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  575. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  576. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  577. package/src/runtime/routes/inference-send-routes.ts +115 -0
  578. package/src/runtime/routes/integrations/twilio.ts +1 -0
  579. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  580. package/src/runtime/routes/memory-v2-routes.ts +13 -398
  581. package/src/runtime/routes/notification-routes.ts +2 -0
  582. package/src/runtime/routes/oauth-apps.ts +112 -7
  583. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  584. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  585. package/src/runtime/routes/oauth-providers.ts +298 -8
  586. package/src/runtime/routes/platform-routes.ts +336 -0
  587. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  588. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  589. package/src/runtime/routes/playground/state.ts +2 -1
  590. package/src/runtime/routes/publish-routes.ts +221 -0
  591. package/src/runtime/routes/schedule-routes.ts +82 -0
  592. package/src/runtime/routes/sequence-routes.ts +291 -0
  593. package/src/runtime/routes/settings-routes.ts +2 -10
  594. package/src/runtime/routes/skills-routes.ts +31 -1
  595. package/src/runtime/routes/stt-routes.ts +240 -3
  596. package/src/runtime/routes/surface-action-routes.ts +43 -7
  597. package/src/runtime/routes/tts-routes.ts +67 -0
  598. package/src/runtime/routes/types.ts +32 -0
  599. package/src/runtime/routes/user-routes-cli.ts +243 -0
  600. package/src/runtime/routes/webhook-routes.ts +165 -0
  601. package/src/runtime/sync/resource-sync-events.ts +25 -0
  602. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  603. package/src/runtime/sync/sync-publisher.ts +21 -0
  604. package/src/schedule/scheduler.ts +200 -123
  605. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  606. package/src/security/secret-patterns.ts +3 -0
  607. package/src/sequence/engine.ts +38 -40
  608. package/src/subagent/manager.ts +20 -15
  609. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  610. package/src/tools/browser/browser-execution.ts +15 -4
  611. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  612. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  613. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  614. package/src/tools/browser/cdp-client/factory.ts +66 -5
  615. package/src/tools/browser/runtime-check.ts +77 -0
  616. package/src/tools/memory/register.test.ts +3 -3
  617. package/src/tools/memory/register.ts +9 -1
  618. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  619. package/src/tools/network/web-search.ts +280 -37
  620. package/src/tools/permission-checker.ts +13 -5
  621. package/src/tools/subagent/spawn.ts +3 -3
  622. package/src/tools/terminal/shell.ts +44 -0
  623. package/src/usage/attribution.ts +3 -2
  624. package/src/util/pricing.ts +86 -160
  625. package/src/watcher/__tests__/engine.test.ts +301 -0
  626. package/src/watcher/constants.ts +7 -0
  627. package/src/watcher/engine.ts +90 -90
  628. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  629. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  630. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  631. package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
  632. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  633. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  634. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  635. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  636. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  637. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  638. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  639. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  640. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  641. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  642. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  643. package/src/workspace/migrations/registry.ts +22 -0
  644. package/src/workspace/migrations/runner.ts +13 -2
  645. package/src/workspace/migrations/types.ts +13 -3
  646. package/src/workspace/provider-commit-message-generator.ts +3 -2
  647. package/src/__tests__/context-search-pkb-source.test.ts +0 -498
  648. package/src/__tests__/credentials-cli.test.ts +0 -1225
  649. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  650. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  651. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  652. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  653. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  654. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  655. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  656. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  657. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  658. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  659. package/src/cli/commands/autonomy.ts +0 -365
  660. package/src/cli/commands/memory.ts +0 -424
  661. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
  662. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  663. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  664. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  665. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  666. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  667. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  668. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  669. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  670. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  671. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  672. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  673. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  674. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  675. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  676. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  677. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  678. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  679. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  680. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  681. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  682. package/src/home/assistant-feed-authoring.ts +0 -135
  683. package/src/home/emit-feed-event.ts +0 -169
  684. package/src/home/feed-scheduler.ts +0 -281
  685. package/src/home/platform-gmail-digest.ts +0 -163
  686. package/src/home/rewrite-command-preview.ts +0 -66
  687. package/src/home/rewrite-feed-title.ts +0 -58
  688. package/src/home/rollup-producer.ts +0 -426
  689. package/src/memory/admin.ts +0 -326
  690. package/src/memory/context-search/sources/pkb.ts +0 -476
  691. package/src/memory/graph/compaction.ts +0 -299
  692. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -1,33 +1,14 @@
1
1
  /**
2
- * `vellum backup` — manage automated backup configuration and list snapshots.
2
+ * `assistant backup` — manage automated backup configuration and list snapshots.
3
3
  *
4
- * All subcommands run in-process (they do not call the daemon HTTP port).
5
- * Config mutations go through `loadRawConfig` / `setNestedValue` / `saveRawConfig`
6
- * so the on-disk `config.json` is the single source of truth and the daemon's
7
- * config cache is invalidated via `saveRawConfig`.
4
+ * Thin IPC wrapper: each subcommand forwards its request to the daemon via
5
+ * cliIpcCall and never imports daemon-internal modules.
8
6
  */
9
7
 
10
- import { stat } from "node:fs/promises";
11
- import { dirname } from "node:path";
12
-
13
8
  import type { Command } from "commander";
14
9
 
15
- import {
16
- listSnapshotsInDir,
17
- type SnapshotEntry,
18
- } from "../../backup/list-snapshots.js";
19
- import {
20
- getLocalBackupsDir,
21
- resolveOffsiteDestinations,
22
- } from "../../backup/paths.js";
23
- import {
24
- getConfig,
25
- loadRawConfig,
26
- saveRawConfig,
27
- setNestedValue,
28
- } from "../../config/loader.js";
29
- import type { BackupDestination } from "../../config/schema.js";
30
- import { getMemoryCheckpoint } from "../../memory/checkpoints.js";
10
+ import { cliIpcCall, exitFromIpcResult } from "../../ipc/cli-client.js";
11
+ import { registerCommand } from "../lib/register-command.js";
31
12
  import { log } from "../logger.js";
32
13
 
33
14
  // ---------------------------------------------------------------------------
@@ -43,7 +24,7 @@ function formatBytes(bytes: number): string {
43
24
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
44
25
  }
45
26
 
46
- /** Format a Date as `YYYY-MM-DD HH:MM UTC`. */
27
+ /** Format an ISO date string as `YYYY-MM-DD HH:MM UTC`. */
47
28
  function formatDate(date: Date): string {
48
29
  const y = date.getUTCFullYear().toString().padStart(4, "0");
49
30
  const mo = (date.getUTCMonth() + 1).toString().padStart(2, "0");
@@ -72,348 +53,19 @@ function formatDurationShort(ms: number): string {
72
53
  return `${days}d ${remHours}h`;
73
54
  }
74
55
 
75
- // ---------------------------------------------------------------------------
76
- // Reachability probe
77
- // ---------------------------------------------------------------------------
78
-
79
- /**
80
- * Check whether an offsite destination's parent directory exists. Mirrors the
81
- * reachability check in the backup worker — if the parent is missing (e.g.
82
- * iCloud Drive not enabled, external SSD unplugged) the destination is
83
- * considered unreachable and we skip it at runtime.
84
- */
85
- async function isDestinationReachable(destPath: string): Promise<boolean> {
86
- try {
87
- await stat(dirname(destPath));
88
- return true;
89
- } catch {
90
- return false;
91
- }
92
- }
93
-
94
- // ---------------------------------------------------------------------------
95
- // Exported handlers — exported so tests can drive them directly.
96
- // ---------------------------------------------------------------------------
97
-
98
- export interface EnableOptions {
99
- interval?: string;
100
- retention?: string;
101
- offsite?: boolean;
102
- }
103
-
104
- export function handleEnable(opts: EnableOptions): void {
105
- const raw = loadRawConfig();
106
- setNestedValue(raw, "backup.enabled", true);
107
-
108
- if (opts.interval !== undefined) {
109
- const hours = Number.parseInt(opts.interval, 10);
110
- if (!Number.isFinite(hours) || hours < 1) {
111
- log.error(
112
- `Invalid --interval "${opts.interval}". Must be a positive integer (hours). ` +
113
- `Run 'vellum backup enable --help' for usage.`,
114
- );
115
- process.exitCode = 1;
116
- return;
117
- }
118
- setNestedValue(raw, "backup.intervalHours", hours);
119
- }
120
-
121
- if (opts.retention !== undefined) {
122
- const count = Number.parseInt(opts.retention, 10);
123
- if (!Number.isFinite(count) || count < 1) {
124
- log.error(
125
- `Invalid --retention "${opts.retention}". Must be a positive integer. ` +
126
- `Run 'vellum backup enable --help' for usage.`,
127
- );
128
- process.exitCode = 1;
129
- return;
130
- }
131
- setNestedValue(raw, "backup.retention", count);
132
- }
133
-
134
- // commander's `.option("--no-offsite", ...)` sets `opts.offsite = false`
135
- // when the flag is present and leaves it `undefined` otherwise. Only a
136
- // literal `false` flips the offsite switch — we never touch `destinations`.
137
- if (opts.offsite === false) {
138
- setNestedValue(raw, "backup.offsite.enabled", false);
139
- }
140
-
141
- saveRawConfig(raw);
142
-
143
- const cfg = getConfig().backup;
144
- log.info(
145
- `Automatic backups enabled (interval=${cfg.intervalHours}h, retention=${cfg.retention}, offsite=${cfg.offsite.enabled ? "on" : "off"})`,
146
- );
147
- }
148
-
149
- export function handleDisable(): void {
150
- const raw = loadRawConfig();
151
- setNestedValue(raw, "backup.enabled", false);
152
- saveRawConfig(raw);
153
- log.info("Automatic backups disabled");
154
- }
155
-
156
- // ---------------------------------------------------------------------------
157
- // destinations subgroup handlers
158
- // ---------------------------------------------------------------------------
159
-
160
- /**
161
- * Load the raw destinations array, materializing the iCloud default on first
162
- * touch. Returns the array plus the raw config so callers can mutate and
163
- * re-persist.
164
- *
165
- * When `backup.offsite.destinations` is `null` in config, the runtime uses the
166
- * iCloud default — but that default is implicit. On first `add`/`remove`/
167
- * `set-encrypt`, we need to make it explicit so subsequent mutations have
168
- * something to mutate.
169
- */
170
- function loadDestinationsForMutation(): {
171
- raw: Record<string, unknown>;
172
- destinations: BackupDestination[];
173
- } {
174
- const raw = loadRawConfig();
175
- const current = getConfig().backup.offsite.destinations;
176
- const destinations = resolveOffsiteDestinations(current);
177
- return { raw, destinations };
178
- }
179
-
180
- export async function handleDestinationsList(): Promise<void> {
181
- const cfg = getConfig().backup;
182
- const destinations = resolveOffsiteDestinations(cfg.offsite.destinations);
183
-
184
- if (destinations.length === 0) {
185
- log.info("No offsite destinations configured");
186
- return;
187
- }
188
-
189
- const pathW = Math.max(4, ...destinations.map((d) => d.path.length));
190
- log.info("Path".padEnd(pathW) + " " + "Encrypted");
191
- log.info("-".repeat(pathW + 2 + 9));
192
- for (const d of destinations) {
193
- log.info(d.path.padEnd(pathW) + " " + (d.encrypt ? "yes" : "no"));
194
- }
195
- }
196
-
197
- export interface DestinationAddOptions {
198
- plaintext?: boolean;
199
- }
200
-
201
- export function handleDestinationsAdd(
202
- path: string,
203
- opts: DestinationAddOptions,
204
- ): void {
205
- const { raw, destinations } = loadDestinationsForMutation();
206
-
207
- if (destinations.some((d) => d.path === path)) {
208
- log.error(
209
- `Destination "${path}" already exists. Run 'vellum backup destinations list' to see configured destinations.`,
210
- );
211
- process.exitCode = 1;
212
- return;
213
- }
214
-
215
- const next: BackupDestination[] = [
216
- ...destinations,
217
- { path, encrypt: !opts.plaintext },
218
- ];
219
- setNestedValue(raw, "backup.offsite.destinations", next);
220
- saveRawConfig(raw);
221
- log.info(
222
- `Added destination ${path} (${opts.plaintext ? "plaintext" : "encrypted"})`,
223
- );
224
- }
225
-
226
- export function handleDestinationsRemove(path: string): void {
227
- const { raw, destinations } = loadDestinationsForMutation();
228
-
229
- const filtered = destinations.filter((d) => d.path !== path);
230
- if (filtered.length === destinations.length) {
231
- log.error(
232
- `Destination "${path}" not found. Run 'vellum backup destinations list' to see configured destinations.`,
233
- );
234
- process.exitCode = 1;
235
- return;
236
- }
237
-
238
- setNestedValue(raw, "backup.offsite.destinations", filtered);
239
- saveRawConfig(raw);
240
- log.info(`Removed destination ${path}`);
241
- }
242
-
243
- export function handleDestinationsSetEncrypt(
244
- path: string,
245
- value: string,
246
- ): void {
247
- const normalized = value.toLowerCase();
248
- if (normalized !== "true" && normalized !== "false") {
249
- log.error(
250
- `Invalid encrypt value "${value}". Must be "true" or "false". ` +
251
- `Run 'vellum backup destinations set-encrypt --help' for usage.`,
252
- );
253
- process.exitCode = 1;
254
- return;
255
- }
256
- const encrypt = normalized === "true";
257
-
258
- const { raw, destinations } = loadDestinationsForMutation();
259
- const idx = destinations.findIndex((d) => d.path === path);
260
- if (idx === -1) {
261
- log.error(
262
- `Destination "${path}" not found. Run 'vellum backup destinations list' to see configured destinations.`,
263
- );
264
- process.exitCode = 1;
265
- return;
266
- }
267
-
268
- const next = destinations.map((d, i) => (i === idx ? { ...d, encrypt } : d));
269
- setNestedValue(raw, "backup.offsite.destinations", next);
270
- saveRawConfig(raw);
271
- log.info(`Set ${path} encrypt=${encrypt ? "true" : "false"}`);
272
- }
273
-
274
- // ---------------------------------------------------------------------------
275
- // status
276
- // ---------------------------------------------------------------------------
277
-
278
- export async function handleStatus(): Promise<void> {
279
- const cfg = getConfig().backup;
280
-
281
- log.info(`Automatic backups: ${cfg.enabled ? "enabled" : "disabled"}`);
282
- log.info(`Interval: every ${cfg.intervalHours}h`);
283
- log.info(`Retention: ${cfg.retention} snapshots per destination`);
284
-
285
- // Last / next run — both gated on a valid checkpoint. The daemon records
286
- // `backup:last_run_at` as a unix-millis string.
287
- const lastRunRaw = getMemoryCheckpoint("backup:last_run_at");
288
- const lastRunMs = lastRunRaw ? Number.parseInt(lastRunRaw, 10) : NaN;
289
- const now = Date.now();
290
- if (!Number.isNaN(lastRunMs)) {
291
- const lastRunDate = new Date(lastRunMs);
292
- log.info(
293
- `Last run: ${formatDate(lastRunDate)} (${formatDurationShort(now - lastRunMs)} ago)`,
294
- );
295
- if (cfg.enabled) {
296
- const intervalMs = cfg.intervalHours * 3600 * 1000;
297
- const nextMs = lastRunMs + intervalMs;
298
- const delta = nextMs - now;
299
- if (delta <= 0) {
300
- log.info(`Next run: due now`);
301
- } else {
302
- log.info(`Next run: in ${formatDurationShort(delta)}`);
303
- }
304
- }
305
- } else {
306
- log.info(`Last run: never`);
307
- if (cfg.enabled) {
308
- log.info(`Next run: on next tick`);
309
- }
310
- }
311
-
312
- // Local directory line — include snapshot count so users can confirm the
313
- // pool size matches retention.
314
- const localDir = getLocalBackupsDir(cfg.localDirectory);
315
- const localSnapshots = await listSnapshotsInDir(localDir);
316
- log.info(
317
- `Local directory: ${localDir} (${localSnapshots.length} snapshots)`,
318
- );
319
-
320
- // Offsite destinations — resolve the iCloud default, probe reachability
321
- // for each, and report snapshot counts.
322
- log.info(`Offsite:`);
323
- if (!cfg.offsite.enabled) {
324
- log.info(` (disabled)`);
325
- return;
326
- }
327
- const destinations = resolveOffsiteDestinations(cfg.offsite.destinations);
328
- if (destinations.length === 0) {
329
- log.info(` (no destinations configured)`);
330
- return;
331
- }
332
- for (const dest of destinations) {
333
- const reachable = await isDestinationReachable(dest.path);
334
- const tag = reachable ? "[OK]" : "[unreachable]";
335
- const enc = dest.encrypt ? "encrypted" : "plaintext";
336
- const snapshots = reachable ? await listSnapshotsInDir(dest.path) : [];
337
- const suffix = reachable ? "" : " -- parent directory not reachable";
338
- log.info(
339
- ` ${tag} ${dest.path} (${enc}, ${snapshots.length} snapshots)${suffix}`,
340
- );
341
- }
342
- }
343
-
344
- // ---------------------------------------------------------------------------
345
- // list
346
- // ---------------------------------------------------------------------------
347
-
348
- /** Print a snapshot table for a group of entries. */
349
- function printSnapshotGroup(heading: string, entries: SnapshotEntry[]): void {
350
- log.info(heading);
351
- if (entries.length === 0) {
352
- log.info(" (none)");
353
- return;
354
- }
355
- const tsW = 19;
356
- const sizeW = 10;
357
- const encW = 9;
358
- log.info(
359
- " " +
360
- "Timestamp".padEnd(tsW) +
361
- " " +
362
- "Size".padEnd(sizeW) +
363
- " " +
364
- "Encrypted".padEnd(encW) +
365
- " " +
366
- "Filename",
367
- );
368
- for (const e of entries) {
369
- log.info(
370
- " " +
371
- formatDate(e.createdAt).padEnd(tsW) +
372
- " " +
373
- formatBytes(e.sizeBytes).padEnd(sizeW) +
374
- " " +
375
- (e.encrypted ? "yes" : "no").padEnd(encW) +
376
- " " +
377
- e.filename,
378
- );
379
- }
380
- }
381
-
382
- export async function handleList(): Promise<void> {
383
- const cfg = getConfig().backup;
384
- const localDir = getLocalBackupsDir(cfg.localDirectory);
385
- const localSnapshots = await listSnapshotsInDir(localDir);
386
- printSnapshotGroup(`Local: ${localDir}`, localSnapshots);
387
-
388
- if (!cfg.offsite.enabled) return;
389
- const destinations = resolveOffsiteDestinations(cfg.offsite.destinations);
390
- for (const dest of destinations) {
391
- const entries = await listSnapshotsInDir(dest.path);
392
- const tag = dest.encrypt ? "encrypted" : "plaintext";
393
- log.info("");
394
- printSnapshotGroup(`Offsite: ${dest.path} (${tag})`, entries);
395
- }
396
- }
397
-
398
- // ---------------------------------------------------------------------------
399
- // ---------------------------------------------------------------------------
400
-
401
-
402
-
403
56
  // ---------------------------------------------------------------------------
404
57
  // Command wiring
405
58
  // ---------------------------------------------------------------------------
406
59
 
407
60
  export function registerBackupCommand(program: Command): void {
408
- const backup = program
409
- .command("backup")
410
- .description(
411
- "Manage automated backup configuration and list snapshots",
412
- );
413
-
414
- backup.addHelpText(
415
- "after",
416
- `
61
+ registerCommand(program, {
62
+ name: "backup",
63
+ transport: "ipc",
64
+ description: "Manage automated backup configuration and list snapshots",
65
+ build: (backup) => {
66
+ backup.addHelpText(
67
+ "after",
68
+ `
417
69
  Backups capture a snapshot of the assistant workspace (config, conversations,
418
70
  trust rules, hooks, the SQLite database) as a .vbundle file. Credentials are
419
71
  NOT included — they live in the OS keychain / CES and users re-authenticate
@@ -427,71 +79,105 @@ plaintext — plaintext only makes sense when the user owns physical access to
427
79
  the medium (e.g. an external SSD).
428
80
 
429
81
  Examples:
430
- $ vellum backup enable --interval 6 --retention 3
431
- $ vellum backup destinations add /Volumes/BackupSSD/vellum --plaintext
432
- $ vellum backup status
433
- $ vellum backup list`,
434
- );
82
+ $ assistant backup enable --interval 6 --retention 3
83
+ $ assistant backup destinations add /Volumes/BackupSSD/vellum --plaintext
84
+ $ assistant backup status
85
+ $ assistant backup list`,
86
+ );
435
87
 
436
- backup
437
- .command("enable")
438
- .description("Enable automated backups")
439
- .option(
440
- "--interval <hours>",
441
- "Hours between automated backups (1-168). Defaults to 6.",
442
- )
443
- .option(
444
- "--retention <n>",
445
- "Snapshots to retain per destination (1-100). Defaults to 3.",
446
- )
447
- .option(
448
- "--no-offsite",
449
- "Disable offsite backup (local only). Does not touch the destinations list.",
450
- )
451
- .addHelpText(
452
- "after",
453
- `
88
+ backup
89
+ .command("enable")
90
+ .description("Enable automated backups")
91
+ .option(
92
+ "--interval <hours>",
93
+ "Hours between automated backups (1-168). Defaults to 6.",
94
+ )
95
+ .option(
96
+ "--retention <n>",
97
+ "Snapshots to retain per destination (1-100). Defaults to 3.",
98
+ )
99
+ .option(
100
+ "--no-offsite",
101
+ "Disable offsite backup (local only). Does not touch the destinations list.",
102
+ )
103
+ .addHelpText(
104
+ "after",
105
+ `
454
106
  Sets backup.enabled = true in config.json. Optionally overrides intervalHours,
455
107
  retention, and the offsite.enabled flag. Does NOT modify
456
- backup.offsite.destinations — use 'vellum backup destinations add/remove' to
108
+ backup.offsite.destinations — use 'assistant backup destinations add/remove' to
457
109
  manage those.
458
110
 
459
111
  Examples:
460
- $ vellum backup enable
461
- $ vellum backup enable --interval 12 --retention 14
462
- $ vellum backup enable --no-offsite`,
463
- )
464
- .action((opts: EnableOptions) => {
465
- handleEnable(opts);
466
- });
467
-
468
- backup
469
- .command("disable")
470
- .description("Disable automated backups")
471
- .addHelpText(
472
- "after",
473
- `
112
+ $ assistant backup enable
113
+ $ assistant backup enable --interval 12 --retention 14
114
+ $ assistant backup enable --no-offsite`,
115
+ )
116
+ .action(
117
+ async (
118
+ opts: { interval?: string; retention?: string; offsite?: boolean },
119
+ cmd: Command,
120
+ ) => {
121
+ const r = await cliIpcCall("backup_enable", {
122
+ body: {
123
+ ...(opts.interval !== undefined && {
124
+ intervalHours: Number.parseInt(opts.interval, 10),
125
+ }),
126
+ ...(opts.retention !== undefined && {
127
+ retention: Number.parseInt(opts.retention, 10),
128
+ }),
129
+ ...(opts.offsite === false && { offsiteEnabled: false }),
130
+ },
131
+ });
132
+ if (!r.ok)
133
+ return exitFromIpcResult(
134
+ { ok: false, error: r.error, statusCode: r.statusCode },
135
+ cmd,
136
+ );
137
+ const cfg = r.result as {
138
+ intervalHours: number;
139
+ retention: number;
140
+ offsite: { enabled: boolean };
141
+ };
142
+ log.info(
143
+ `Automatic backups enabled (interval=${cfg.intervalHours}h, retention=${cfg.retention}, offsite=${cfg.offsite.enabled ? "on" : "off"})`,
144
+ );
145
+ },
146
+ );
147
+
148
+ backup
149
+ .command("disable")
150
+ .description("Disable automated backups")
151
+ .addHelpText(
152
+ "after",
153
+ `
474
154
  Sets backup.enabled = false in config.json. Existing snapshots are untouched;
475
155
  only the automated worker stops creating new ones.
476
156
 
477
157
  Examples:
478
- $ vellum backup disable`,
479
- )
480
- .action(() => {
481
- handleDisable();
482
- });
483
-
484
- // ---------------------------------------------------------------------------
485
- // destinations — subgroup
486
- // ---------------------------------------------------------------------------
487
-
488
- const destinations = backup
489
- .command("destinations")
490
- .description("Manage offsite backup destinations");
491
-
492
- destinations.addHelpText(
493
- "after",
494
- `
158
+ $ assistant backup disable`,
159
+ )
160
+ .action(async (_opts: unknown, cmd: Command) => {
161
+ const r = await cliIpcCall("backup_disable");
162
+ if (!r.ok)
163
+ return exitFromIpcResult(
164
+ { ok: false, error: r.error, statusCode: r.statusCode },
165
+ cmd,
166
+ );
167
+ log.info("Automatic backups disabled");
168
+ });
169
+
170
+ // -----------------------------------------------------------------------
171
+ // destinations — subgroup
172
+ // -----------------------------------------------------------------------
173
+
174
+ const destinations = backup
175
+ .command("destinations")
176
+ .description("Manage offsite backup destinations");
177
+
178
+ destinations.addHelpText(
179
+ "after",
180
+ `
495
181
  Offsite destinations are absolute paths the backup worker writes a copy of
496
182
  each snapshot to after the local write succeeds. The default destination is
497
183
  the iCloud Drive VellumAssistant folder, and it is used implicitly until an
@@ -504,39 +190,57 @@ are written as .vbundle.enc (AES-256-GCM). When false, snapshots are copied
504
190
  as plaintext .vbundle — only use this for media you control physically.
505
191
 
506
192
  Examples:
507
- $ vellum backup destinations list
508
- $ vellum backup destinations add /Volumes/BackupSSD/vellum --plaintext
509
- $ vellum backup destinations remove /Volumes/BackupSSD/vellum
510
- $ vellum backup destinations set-encrypt /Volumes/BackupSSD/vellum false`,
511
- );
193
+ $ assistant backup destinations list
194
+ $ assistant backup destinations add /Volumes/BackupSSD/vellum --plaintext
195
+ $ assistant backup destinations remove /Volumes/BackupSSD/vellum
196
+ $ assistant backup destinations set-encrypt /Volumes/BackupSSD/vellum false`,
197
+ );
512
198
 
513
- destinations
514
- .command("list")
515
- .description("List configured offsite destinations")
516
- .addHelpText(
517
- "after",
518
- `
199
+ destinations
200
+ .command("list")
201
+ .description("List configured offsite destinations")
202
+ .addHelpText(
203
+ "after",
204
+ `
519
205
  Resolves the current destinations array (materializing the iCloud default if
520
206
  no explicit array is configured) and prints a table with the path and
521
207
  encryption flag per row.
522
208
 
523
209
  Examples:
524
- $ vellum backup destinations list`,
525
- )
526
- .action(async () => {
527
- await handleDestinationsList();
528
- });
529
-
530
- destinations
531
- .command("add <path>")
532
- .description("Add an offsite backup destination")
533
- .option(
534
- "--plaintext",
535
- "Write snapshots as plaintext .vbundle (default is AES-256-GCM encrypted .vbundle.enc)",
536
- )
537
- .addHelpText(
538
- "after",
539
- `
210
+ $ assistant backup destinations list`,
211
+ )
212
+ .action(async (_opts: unknown, cmd: Command) => {
213
+ const r = await cliIpcCall<{
214
+ destinations: Array<{ path: string; encrypt: boolean }>;
215
+ }>("backup_destinations_list");
216
+ if (!r.ok)
217
+ return exitFromIpcResult(
218
+ { ok: false, error: r.error, statusCode: r.statusCode },
219
+ cmd,
220
+ );
221
+ const { destinations: dests } = r.result!;
222
+ if (dests.length === 0) {
223
+ log.info("No offsite destinations configured");
224
+ return;
225
+ }
226
+ const pathW = Math.max(4, ...dests.map((d) => d.path.length));
227
+ log.info("Path".padEnd(pathW) + " " + "Encrypted");
228
+ log.info("-".repeat(pathW + 2 + 9));
229
+ for (const d of dests) {
230
+ log.info(d.path.padEnd(pathW) + " " + (d.encrypt ? "yes" : "no"));
231
+ }
232
+ });
233
+
234
+ destinations
235
+ .command("add <path>")
236
+ .description("Add an offsite backup destination")
237
+ .option(
238
+ "--plaintext",
239
+ "Write snapshots as plaintext .vbundle (default is AES-256-GCM encrypted .vbundle.enc)",
240
+ )
241
+ .addHelpText(
242
+ "after",
243
+ `
540
244
  Arguments:
541
245
  path Absolute path to the destination directory. Must be on a mount the
542
246
  caller controls; the backup worker writes files inside this
@@ -547,64 +251,107 @@ the iCloud default is materialized first so the new entry appends to a
547
251
  2-element array rather than replacing the default.
548
252
 
549
253
  Examples:
550
- $ vellum backup destinations add /Volumes/BackupSSD/vellum --plaintext
551
- $ vellum backup destinations add ~/Dropbox/VellumAssistant/backups`,
552
- )
553
- .action((path: string, opts: DestinationAddOptions) => {
554
- handleDestinationsAdd(path, opts);
555
- });
556
-
557
- destinations
558
- .command("remove <path>")
559
- .description("Remove an offsite backup destination by path")
560
- .addHelpText(
561
- "after",
562
- `
254
+ $ assistant backup destinations add /Volumes/BackupSSD/vellum --plaintext
255
+ $ assistant backup destinations add ~/Dropbox/VellumAssistant/backups`,
256
+ )
257
+ .action(
258
+ async (path: string, opts: { plaintext?: boolean }, cmd: Command) => {
259
+ const r = await cliIpcCall("backup_destinations_add", {
260
+ body: {
261
+ path,
262
+ encrypt: !opts.plaintext,
263
+ },
264
+ });
265
+ if (!r.ok)
266
+ return exitFromIpcResult(
267
+ { ok: false, error: r.error, statusCode: r.statusCode },
268
+ cmd,
269
+ );
270
+ log.info(
271
+ `Added destination ${path} (${opts.plaintext ? "plaintext" : "encrypted"})`,
272
+ );
273
+ },
274
+ );
275
+
276
+ destinations
277
+ .command("remove <path>")
278
+ .description("Remove an offsite backup destination by path")
279
+ .addHelpText(
280
+ "after",
281
+ `
563
282
  Arguments:
564
283
  path Exact path match of the destination to remove. Run
565
- 'vellum backup destinations list' to see configured paths.
284
+ 'assistant backup destinations list' to see configured paths.
566
285
 
567
286
  Errors if no destination with the given path exists.
568
287
 
569
288
  Examples:
570
- $ vellum backup destinations remove /Volumes/BackupSSD/vellum`,
571
- )
572
- .action((path: string) => {
573
- handleDestinationsRemove(path);
574
- });
575
-
576
- destinations
577
- .command("set-encrypt <path> <value>")
578
- .description("Toggle encryption for an existing destination")
579
- .addHelpText(
580
- "after",
581
- `
289
+ $ assistant backup destinations remove /Volumes/BackupSSD/vellum`,
290
+ )
291
+ .action(async (path: string, _opts: unknown, cmd: Command) => {
292
+ const r = await cliIpcCall("backup_destinations_remove", { body: { path } });
293
+ if (!r.ok)
294
+ return exitFromIpcResult(
295
+ { ok: false, error: r.error, statusCode: r.statusCode },
296
+ cmd,
297
+ );
298
+ log.info(`Removed destination ${path}`);
299
+ });
300
+
301
+ destinations
302
+ .command("set-encrypt <path> <value>")
303
+ .description("Toggle encryption for an existing destination")
304
+ .addHelpText(
305
+ "after",
306
+ `
582
307
  Arguments:
583
308
  path Exact path match of an existing destination. Run
584
- 'vellum backup destinations list' to see configured paths.
309
+ 'assistant backup destinations list' to see configured paths.
585
310
  value "true" to encrypt, "false" for plaintext writes.
586
311
 
587
312
  Errors if no destination with the given path exists. Existing snapshot files
588
313
  are not modified; only future writes honour the new setting.
589
314
 
590
315
  Examples:
591
- $ vellum backup destinations set-encrypt /Volumes/BackupSSD/vellum false
592
- $ vellum backup destinations set-encrypt /Volumes/BackupSSD/vellum true`,
593
- )
594
- .action((path: string, value: string) => {
595
- handleDestinationsSetEncrypt(path, value);
596
- });
597
-
598
- // ---------------------------------------------------------------------------
599
- // status / list
600
- // ---------------------------------------------------------------------------
601
-
602
- backup
603
- .command("status")
604
- .description("Show backup status and next-run timing")
605
- .addHelpText(
606
- "after",
607
- `
316
+ $ assistant backup destinations set-encrypt /Volumes/BackupSSD/vellum false
317
+ $ assistant backup destinations set-encrypt /Volumes/BackupSSD/vellum true`,
318
+ )
319
+ .action(
320
+ async (path: string, value: string, _opts: unknown, cmd: Command) => {
321
+ const normalized = value.toLowerCase();
322
+ if (normalized !== "true" && normalized !== "false") {
323
+ log.error(
324
+ `Invalid encrypt value "${value}". Must be "true" or "false". ` +
325
+ `Run 'assistant backup destinations set-encrypt --help' for usage.`,
326
+ );
327
+ process.exitCode = 1;
328
+ return;
329
+ }
330
+ const r = await cliIpcCall("backup_destinations_set_encrypt", {
331
+ body: {
332
+ path,
333
+ encrypt: normalized === "true",
334
+ },
335
+ });
336
+ if (!r.ok)
337
+ return exitFromIpcResult(
338
+ { ok: false, error: r.error, statusCode: r.statusCode },
339
+ cmd,
340
+ );
341
+ log.info(`Set ${path} encrypt=${normalized}`);
342
+ },
343
+ );
344
+
345
+ // -----------------------------------------------------------------------
346
+ // status / list
347
+ // -----------------------------------------------------------------------
348
+
349
+ backup
350
+ .command("status")
351
+ .description("Show backup status and next-run timing")
352
+ .addHelpText(
353
+ "after",
354
+ `
608
355
  Reports enabled/disabled state, interval and retention, last-run and next-run
609
356
  timing (from the backup:last_run_at memory checkpoint), and a per-destination
610
357
  reachability probe. Unreachable destinations (parent directory missing, e.g.
@@ -612,27 +359,184 @@ iCloud Drive not enabled or external volume unplugged) are flagged
612
359
  [unreachable] and skipped by the worker.
613
360
 
614
361
  Examples:
615
- $ vellum backup status`,
616
- )
617
- .action(async () => {
618
- await handleStatus();
619
- });
620
-
621
- backup
622
- .command("list")
623
- .description("List all backup snapshots, grouped by destination")
624
- .addHelpText(
625
- "after",
626
- `
362
+ $ assistant backup status`,
363
+ )
364
+ .action(async (_opts: unknown, cmd: Command) => {
365
+ const r = await cliIpcCall<{
366
+ enabled: boolean;
367
+ intervalHours: number;
368
+ retention: number;
369
+ lastRunAt: string | null;
370
+ nextRunAt: string | null;
371
+ localDir: string;
372
+ localSnapshotCount: number;
373
+ offsiteEnabled: boolean;
374
+ offsite: Array<{
375
+ path: string;
376
+ encrypt: boolean;
377
+ reachable: boolean;
378
+ snapshotCount: number;
379
+ }>;
380
+ }>("backup_status");
381
+ if (!r.ok)
382
+ return exitFromIpcResult(
383
+ { ok: false, error: r.error, statusCode: r.statusCode },
384
+ cmd,
385
+ );
386
+ const s = r.result!;
387
+ const now = Date.now();
388
+
389
+ log.info(`Automatic backups: ${s.enabled ? "enabled" : "disabled"}`);
390
+ log.info(`Interval: every ${s.intervalHours}h`);
391
+ log.info(
392
+ `Retention: ${s.retention} snapshots per destination`,
393
+ );
394
+
395
+ if (s.lastRunAt) {
396
+ const lastRunMs = new Date(s.lastRunAt).getTime();
397
+ log.info(
398
+ `Last run: ${formatDate(new Date(s.lastRunAt))} (${formatDurationShort(now - lastRunMs)} ago)`,
399
+ );
400
+ if (s.enabled && s.nextRunAt) {
401
+ const nextMs = new Date(s.nextRunAt).getTime();
402
+ const delta = nextMs - now;
403
+ if (delta <= 0) {
404
+ log.info(`Next run: due now`);
405
+ } else {
406
+ log.info(`Next run: in ${formatDurationShort(delta)}`);
407
+ }
408
+ }
409
+ } else {
410
+ log.info(`Last run: never`);
411
+ if (s.enabled) {
412
+ log.info(`Next run: on next tick`);
413
+ }
414
+ }
415
+
416
+ log.info(
417
+ `Local directory: ${s.localDir} (${s.localSnapshotCount} snapshots)`,
418
+ );
419
+
420
+ log.info(
421
+ `Offsite: ${s.offsiteEnabled ? "enabled" : "disabled"}`,
422
+ );
423
+ if (!s.offsiteEnabled) {
424
+ return;
425
+ }
426
+ if (s.offsite.length === 0) {
427
+ log.info(` (no destinations configured)`);
428
+ return;
429
+ }
430
+ for (const dest of s.offsite) {
431
+ const tag = dest.reachable ? "[OK]" : "[unreachable]";
432
+ const enc = dest.encrypt ? "encrypted" : "plaintext";
433
+ const suffix = dest.reachable
434
+ ? ""
435
+ : " -- parent directory not reachable";
436
+ log.info(
437
+ ` ${tag} ${dest.path} (${enc}, ${dest.snapshotCount} snapshots)${suffix}`,
438
+ );
439
+ }
440
+ });
441
+
442
+ backup
443
+ .command("list")
444
+ .description("List all backup snapshots, grouped by destination")
445
+ .addHelpText(
446
+ "after",
447
+ `
627
448
  Prints a per-destination table of snapshots with timestamp, size, and
628
449
  encryption flag. Local destination is listed first, followed by each offsite
629
450
  destination. Unreachable destinations are listed with an empty snapshot set.
630
451
 
631
452
  Examples:
632
- $ vellum backup list`,
633
- )
634
- .action(async () => {
635
- await handleList();
636
- });
453
+ $ assistant backup list`,
454
+ )
455
+ .action(async (_opts: unknown, cmd: Command) => {
456
+ const r = await cliIpcCall<{
457
+ local: Array<{
458
+ filename: string;
459
+ createdAt: string;
460
+ sizeBytes: number;
461
+ encrypted: boolean;
462
+ }>;
463
+ offsite: Array<{
464
+ destination: { path: string; encrypt: boolean };
465
+ snapshots: Array<{
466
+ filename: string;
467
+ createdAt: string;
468
+ sizeBytes: number;
469
+ encrypted: boolean;
470
+ }>;
471
+ reachable: boolean;
472
+ }>;
473
+ offsiteEnabled: boolean;
474
+ nextRunAt: string | null;
475
+ }>("backups_list");
476
+ if (!r.ok)
477
+ return exitFromIpcResult(
478
+ { ok: false, error: r.error, statusCode: r.statusCode },
479
+ cmd,
480
+ );
481
+ const data = r.result!;
482
+
483
+ printSnapshotGroup(`Local:`, data.local);
484
+
485
+ if (!data.offsiteEnabled) return;
486
+ for (const dest of data.offsite) {
487
+ const tag = dest.destination.encrypt ? "encrypted" : "plaintext";
488
+ log.info("");
489
+ printSnapshotGroup(
490
+ `Offsite: ${dest.destination.path} (${tag})`,
491
+ dest.snapshots,
492
+ );
493
+ }
494
+ });
495
+ },
496
+ });
497
+ }
637
498
 
499
+ // ---------------------------------------------------------------------------
500
+ // Snapshot table printer
501
+ // ---------------------------------------------------------------------------
502
+
503
+ function printSnapshotGroup(
504
+ heading: string,
505
+ entries: Array<{
506
+ filename: string;
507
+ createdAt: string;
508
+ sizeBytes: number;
509
+ encrypted: boolean;
510
+ }>,
511
+ ): void {
512
+ log.info(heading);
513
+ if (entries.length === 0) {
514
+ log.info(" (none)");
515
+ return;
516
+ }
517
+ const tsW = 19;
518
+ const sizeW = 10;
519
+ const encW = 9;
520
+ log.info(
521
+ " " +
522
+ "Timestamp".padEnd(tsW) +
523
+ " " +
524
+ "Size".padEnd(sizeW) +
525
+ " " +
526
+ "Encrypted".padEnd(encW) +
527
+ " " +
528
+ "Filename",
529
+ );
530
+ for (const e of entries) {
531
+ log.info(
532
+ " " +
533
+ formatDate(new Date(e.createdAt)).padEnd(tsW) +
534
+ " " +
535
+ formatBytes(e.sizeBytes).padEnd(sizeW) +
536
+ " " +
537
+ (e.encrypted ? "yes" : "no").padEnd(encW) +
538
+ " " +
539
+ e.filename,
540
+ );
541
+ }
638
542
  }