@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,36 +1,52 @@
1
1
  import type { Command } from "commander";
2
2
 
3
- import {
4
- getAssistantContactMetadata,
5
- getChannelById,
6
- getContact,
7
- listContacts,
8
- mergeContacts,
9
- searchContacts,
10
- updateChannelStatus,
11
- upsertContact,
12
- } from "../../contacts/contact-store.js";
13
- import type {
14
- AssistantContactMetadata,
15
- ChannelPolicy,
16
- ChannelStatus,
17
- ContactChannel,
18
- ContactRole,
19
- ContactType,
20
- ContactWithChannels,
21
- } from "../../contacts/types.js";
22
- import { cliIpcCall } from "../../ipc/cli-client.js";
23
- import { getDb } from "../../memory/db-connection.js";
24
- import {
25
- createIngressInvite,
26
- listIngressInvites,
27
- redeemIngressInvite,
28
- redeemVoiceInviteCode,
29
- revokeIngressInvite,
30
- } from "../../runtime/invite-service.js";
31
- import type { ContactPromptResult } from "../../runtime/routes/contact-prompt-routes.js";
3
+ import { cliIpcCall, exitFromIpcResult } from "../../ipc/cli-client.js";
4
+ import { registerCommand } from "../lib/register-command.js";
32
5
  import { shouldOutputJson, writeOutput } from "../output.js";
33
6
 
7
+ // ---------------------------------------------------------------------------
8
+ // IPC response shapes
9
+ // ---------------------------------------------------------------------------
10
+
11
+ interface ContactChannel {
12
+ id: string;
13
+ contactId: string;
14
+ type: string;
15
+ address: string;
16
+ status: string;
17
+ policy: string;
18
+ isPrimary?: boolean;
19
+ revokedReason?: string | null;
20
+ blockedReason?: string | null;
21
+ }
22
+
23
+ interface ContactWithChannels {
24
+ id: string;
25
+ displayName: string;
26
+ role: string;
27
+ contactType: string;
28
+ notes?: string;
29
+ principalId?: string;
30
+ createdAt: string | number;
31
+ updatedAt: string | number;
32
+ interactionCount: number;
33
+ channels: ContactChannel[];
34
+ }
35
+
36
+ interface AssistantContactMetadata {
37
+ species: string;
38
+ metadata?: Record<string, unknown> & { assistantId?: string };
39
+ }
40
+
41
+ interface ContactPromptResult {
42
+ ok: boolean;
43
+ error?: string;
44
+ channelType?: string;
45
+ address?: string;
46
+ channelId?: string;
47
+ contactId?: string;
48
+ }
49
+
34
50
  // ---------------------------------------------------------------------------
35
51
  // Human-readable formatters
36
52
  // ---------------------------------------------------------------------------
@@ -129,20 +145,6 @@ function formatContactDetail(
129
145
  return lines.join("\n");
130
146
  }
131
147
 
132
- function formatChannelDetail(ch: ContactChannel): string {
133
- const lines: string[] = [];
134
- lines.push(`ID: ${ch.id}`);
135
- lines.push(`Contact: ${ch.contactId}`);
136
- lines.push(`Type: ${ch.type}`);
137
- lines.push(`Address: ${ch.address}`);
138
- lines.push(`Status: ${ch.status}`);
139
- lines.push(`Policy: ${ch.policy}`);
140
- if (ch.isPrimary) lines.push(`Primary: yes`);
141
- if (ch.revokedReason) lines.push(`Revoked: ${ch.revokedReason}`);
142
- if (ch.blockedReason) lines.push(`Blocked: ${ch.blockedReason}`);
143
- return lines.join("\n");
144
- }
145
-
146
148
  function writeError(cmd: Command, message: string): void {
147
149
  if (shouldOutputJson(cmd)) {
148
150
  writeOutput(cmd, { ok: false, error: message });
@@ -152,14 +154,16 @@ function writeError(cmd: Command, message: string): void {
152
154
  }
153
155
 
154
156
  export function registerContactsCommand(program: Command): void {
155
- const contacts = program
156
- .command("contacts")
157
- .description("Manage and query the contact graph")
158
- .option("--json", "Machine-readable compact JSON output");
159
-
160
- contacts.addHelpText(
161
- "after",
162
- `
157
+ registerCommand(program, {
158
+ name: "contacts",
159
+ transport: "ipc",
160
+ description: "Manage and query the contact graph",
161
+ build: (contacts) => {
162
+ contacts.option("--json", "Machine-readable compact JSON output");
163
+
164
+ contacts.addHelpText(
165
+ "after",
166
+ `
163
167
  Contacts represent people and entities the assistant interacts with. Each
164
168
  contact is identified by a UUID, has a role (contact or guardian), and
165
169
  can be linked to external identifiers — phone numbers,
@@ -169,28 +173,33 @@ is the source of truth for identity resolution across all channels.
169
173
  Examples:
170
174
  $ assistant contacts list
171
175
  $ assistant contacts get abc-123
172
- $ assistant contacts upsert --display-name "Alice"
173
- $ assistant contacts merge keep-id merge-id
174
176
  $ assistant contacts invites list`,
175
- );
176
-
177
- contacts
178
- .command("list")
179
- .description("List contacts")
180
- .option("--role <role>", "Filter by role (contact, guardian, or omit for all)")
181
- .option("--limit <limit>", "Maximum number of contacts to return")
182
- .option("--query <query>", "Search query to filter contacts")
183
- .option(
184
- "--channel-address <address>",
185
- "Search by channel address (email, phone, handle)",
186
- )
187
- .option(
188
- "--channel-type <channelType>",
189
- "Filter by channel type (email, telegram, phone, whatsapp, slack)",
190
- )
191
- .addHelpText(
192
- "after",
193
- `
177
+ );
178
+
179
+ // -----------------------------------------------------------------------
180
+ // list
181
+ // -----------------------------------------------------------------------
182
+
183
+ contacts
184
+ .command("list")
185
+ .description("List contacts")
186
+ .option(
187
+ "--role <role>",
188
+ "Filter by role (contact, guardian, or omit for all)",
189
+ )
190
+ .option("--limit <limit>", "Maximum number of contacts to return")
191
+ .option("--query <query>", "Search query to filter contacts")
192
+ .option(
193
+ "--channel-address <address>",
194
+ "Search by channel address (email, phone, handle)",
195
+ )
196
+ .option(
197
+ "--channel-type <channelType>",
198
+ "Filter by channel type (email, telegram, phone, whatsapp, slack)",
199
+ )
200
+ .addHelpText(
201
+ "after",
202
+ `
194
203
  Lists contacts with optional filtering. The --role flag accepts: contact
195
204
  or guardian (omit to show all). The --limit flag sets
196
205
  the maximum number of results (defaults to 50).
@@ -210,59 +219,61 @@ Examples:
210
219
  $ assistant contacts list --channel-type telegram
211
220
  $ assistant contacts list --query "alice" --channel-type email
212
221
  $ assistant contacts list --role guardian --json`,
213
- )
214
- .action(
215
- async (
216
- opts: {
217
- role?: string;
218
- limit?: string;
219
- query?: string;
220
- channelAddress?: string;
221
- channelType?: string;
222
- },
223
- cmd: Command,
224
- ) => {
225
- try {
226
- getDb();
227
- const role = opts.role as ContactRole | undefined;
228
- const limit = opts.limit ? Number(opts.limit) : undefined;
229
-
230
- const effectiveLimit = limit ?? 50;
231
-
232
- const hasSearchParams =
233
- opts.query || opts.channelAddress || opts.channelType;
234
- const results = hasSearchParams
235
- ? searchContacts({
236
- query: opts.query,
237
- channelAddress: opts.channelAddress,
238
- channelType: opts.channelType,
239
- role,
240
- limit: effectiveLimit,
241
- })
242
- : listContacts(effectiveLimit, role);
222
+ )
223
+ .action(
224
+ async (
225
+ opts: {
226
+ role?: string;
227
+ limit?: string;
228
+ query?: string;
229
+ channelAddress?: string;
230
+ channelType?: string;
231
+ },
232
+ cmd: Command,
233
+ ) => {
234
+ const r = await cliIpcCall<{
235
+ ok: boolean;
236
+ contacts: ContactWithChannels[];
237
+ }>("listContacts", {
238
+ queryParams: {
239
+ ...(opts.role && { role: opts.role }),
240
+ ...(opts.limit && { limit: opts.limit }),
241
+ ...(opts.query && { query: opts.query }),
242
+ ...(opts.channelAddress && {
243
+ channelAddress: opts.channelAddress,
244
+ }),
245
+ ...(opts.channelType && { channelType: opts.channelType }),
246
+ },
247
+ });
243
248
 
244
- if (shouldOutputJson(cmd)) {
245
- writeOutput(cmd, { ok: true, contacts: results });
246
- } else if (results.length === 0) {
247
- process.stdout.write("No contacts found.\n");
248
- } else {
249
- process.stdout.write(formatContactTable(results) + "\n");
250
- process.stdout.write(`\n${results.length} contact(s)\n`);
251
- }
252
- } catch (err) {
253
- const message = err instanceof Error ? err.message : String(err);
254
- writeError(cmd, message);
255
- process.exitCode = 1;
256
- }
257
- },
258
- );
249
+ if (!r.ok)
250
+ return exitFromIpcResult(
251
+ r as { ok: false; error?: string; statusCode?: number },
252
+ cmd,
253
+ );
259
254
 
260
- contacts
261
- .command("get <id>")
262
- .description("Get a contact by ID")
263
- .addHelpText(
264
- "after",
265
- `
255
+ const results = r.result!.contacts;
256
+ if (shouldOutputJson(cmd)) {
257
+ writeOutput(cmd, { ok: true, contacts: results });
258
+ } else if (results.length === 0) {
259
+ process.stdout.write("No contacts found.\n");
260
+ } else {
261
+ process.stdout.write(formatContactTable(results) + "\n");
262
+ process.stdout.write(`\n${results.length} contact(s)\n`);
263
+ }
264
+ },
265
+ );
266
+
267
+ // -----------------------------------------------------------------------
268
+ // get
269
+ // -----------------------------------------------------------------------
270
+
271
+ contacts
272
+ .command("get <id>")
273
+ .description("Get a contact by ID")
274
+ .addHelpText(
275
+ "after",
276
+ `
266
277
  Arguments:
267
278
  id UUID of the contact to retrieve. Run 'assistant contacts list' to find IDs.
268
279
 
@@ -273,259 +284,144 @@ For assistant-type contacts, additional assistant metadata is included.
273
284
  Examples:
274
285
  $ assistant contacts get 7a3b1c2d-4e5f-6789-abcd-ef0123456789
275
286
  $ assistant contacts get abc-123 --json`,
276
- )
277
- .action(async (id: string, _opts: unknown, cmd: Command) => {
278
- try {
279
- getDb();
280
- const contact = getContact(id);
281
- if (!contact) {
282
- writeError(cmd, "Contact not found");
283
- process.exitCode = 1;
284
- return;
285
- }
286
- const assistantMeta =
287
- contact.contactType === "assistant"
288
- ? getAssistantContactMetadata(contact.id)
289
- : undefined;
290
- if (shouldOutputJson(cmd)) {
291
- writeOutput(cmd, {
292
- ok: true,
293
- contact,
294
- assistantMetadata: assistantMeta ?? undefined,
287
+ )
288
+ .action(async (id: string, _opts: unknown, cmd: Command) => {
289
+ const r = await cliIpcCall<{
290
+ ok: boolean;
291
+ contact: ContactWithChannels;
292
+ assistantMetadata?: AssistantContactMetadata;
293
+ }>("getContact", {
294
+ pathParams: { id },
295
295
  });
296
- } else {
297
- process.stdout.write(
298
- formatContactDetail(contact, assistantMeta ?? undefined) + "\n",
299
- );
300
- }
301
- } catch (err) {
302
- const message = err instanceof Error ? err.message : String(err);
303
- writeError(cmd, message);
304
- process.exitCode = 1;
305
- }
306
- });
307
-
308
- contacts
309
- .command("merge <keepId> <mergeId>")
310
- .description("Merge two contacts")
311
- .addHelpText(
312
- "after",
313
- `
314
- Arguments:
315
- keepId UUID of the surviving contact that will absorb the other.
316
- Run 'assistant contacts list' to find IDs.
317
- mergeId UUID of the contact to be merged and deleted
318
296
 
319
- All channel memberships, conversation history, and metadata from mergeId
320
- are transferred to keepId. After the merge, mergeId is permanently deleted.
321
- This operation is irreversible.
297
+ if (!r.ok)
298
+ return exitFromIpcResult(
299
+ r as { ok: false; error?: string; statusCode?: number },
300
+ cmd,
301
+ );
322
302
 
323
- Examples:
324
- $ assistant contacts merge 7a3b1c2d-4e5f-6789-abcd-ef0123456789 9f8e7d6c-5b4a-3210-fedc-ba9876543210
325
- $ assistant contacts merge keep-id merge-id --json`,
326
- )
327
- .action(
328
- async (keepId: string, mergeId: string, _opts: unknown, cmd: Command) => {
329
- try {
330
- getDb();
331
- const contact = mergeContacts(keepId, mergeId);
303
+ const { contact, assistantMetadata } = r.result!;
332
304
  if (shouldOutputJson(cmd)) {
333
- writeOutput(cmd, { ok: true, contact });
305
+ writeOutput(cmd, {
306
+ ok: true,
307
+ contact,
308
+ assistantMetadata: assistantMetadata ?? undefined,
309
+ });
334
310
  } else {
335
311
  process.stdout.write(
336
- `Merged ${mergeId} into ${keepId}\n` +
337
- formatContactDetail(contact) +
312
+ formatContactDetail(contact, assistantMetadata ?? undefined) +
338
313
  "\n",
339
314
  );
340
315
  }
341
- } catch (err) {
342
- const message = err instanceof Error ? err.message : String(err);
343
- writeError(cmd, message);
344
- process.exitCode = 1;
345
- }
346
- },
347
- );
316
+ });
317
+
318
+ // -----------------------------------------------------------------------
319
+ // prompt
320
+ // -----------------------------------------------------------------------
321
+
322
+ contacts
323
+ .command("prompt")
324
+ .description(
325
+ "Prompt user to register a contact channel via the app UI",
326
+ )
327
+ .option(
328
+ "--channel <channel>",
329
+ "Suggested channel type hint (e.g. phone, email, telegram)",
330
+ )
331
+ .option(
332
+ "--placeholder <placeholder>",
333
+ "Placeholder text for the address input field",
334
+ )
335
+ .option(
336
+ "--role <role>",
337
+ "Intended role: guardian, trusted-contact, or unknown (default: unknown)",
338
+ )
339
+ .option("--label <label>", "Display label shown in the prompt UI")
340
+ .option(
341
+ "--description <description>",
342
+ "Longer description shown in the prompt UI",
343
+ )
344
+ .option(
345
+ "--timeout <ms>",
346
+ "How long to wait for the user to submit (ms). Defaults to match the server-side prompt timeout.",
347
+ String(310_000),
348
+ )
349
+ .addHelpText(
350
+ "after",
351
+ `
352
+ Opens a contact address prompt in the user's app. The user enters a channel
353
+ address (phone number, email, Telegram ID, etc.). The address is saved with
354
+ status "unverified". Verification is a separate step.
355
+
356
+ Run \`assistant contacts prompt --help\` for full option details.`,
357
+ )
358
+ .action(
359
+ async (
360
+ opts: {
361
+ channel?: string;
362
+ placeholder?: string;
363
+ role?: string;
364
+ label?: string;
365
+ description?: string;
366
+ timeout?: string;
367
+ },
368
+ cmd: Command,
369
+ ) => {
370
+ const timeoutMs = opts.timeout ? parseInt(opts.timeout, 10) : 310_000;
371
+ const r = await cliIpcCall<ContactPromptResult>(
372
+ "contacts_prompt",
373
+ {
374
+ body: {
375
+ channel: opts.channel,
376
+ placeholder: opts.placeholder,
377
+ role: opts.role ?? "unknown",
378
+ label: opts.label,
379
+ description: opts.description,
380
+ },
381
+ },
382
+ { timeoutMs },
383
+ );
348
384
 
349
- contacts
350
- .command("upsert")
351
- .description("Create or update a contact")
352
- .requiredOption("--display-name <name>", "Display name for the contact")
353
- .option("--id <id>", "Contact ID — provide to update an existing contact")
354
- .option("--notes <notes>", "Free-text notes about the contact")
355
- .option(
356
- "--role <role>",
357
- "Contact role: contact or guardian (default: contact for new contacts)",
358
- )
359
- .option(
360
- "--contact-type <type>",
361
- "Contact type: human or assistant (default: human for new contacts)",
362
- )
363
- .option(
364
- "--channels <json>",
365
- "JSON array of channel objects, each with type, address, and optional isPrimary, externalUserId, externalChatId, status, policy",
366
- )
367
- .addHelpText(
368
- "after",
369
- `
370
- Creates a new contact or updates an existing one. When --id is provided and
371
- matches an existing contact, that contact is updated. When --id is omitted,
372
- a new contact is created with a generated UUID.
373
-
374
- The --channels flag accepts a JSON array of channel objects. Each object must
375
- have "type" (e.g. telegram, phone, email, whatsapp) and "address" fields.
376
- Optional channel fields: isPrimary (boolean), externalUserId, externalChatId,
377
- status (active, revoked, blocked), policy (allow, deny).
385
+ if (!r.ok)
386
+ return exitFromIpcResult(
387
+ r as { ok: false; error?: string; statusCode?: number },
388
+ cmd,
389
+ );
378
390
 
379
- Examples:
380
- $ assistant contacts upsert --display-name "Alice" --json
381
- $ assistant contacts upsert --display-name "Alice" --id abc-123 --notes "Updated notes" --json
382
- $ assistant contacts upsert --display-name "Bob" --role guardian --json
383
- $ assistant contacts upsert --display-name "Bob" --channels '[{"type":"telegram","address":"12345","externalUserId":"12345","status":"active","policy":"allow"}]' --json`,
384
- )
385
- .action(
386
- async (
387
- opts: {
388
- displayName: string;
389
- id?: string;
390
- notes?: string;
391
- role?: string;
392
- contactType?: string;
393
- channels?: string;
394
- },
395
- cmd: Command,
396
- ) => {
397
- try {
398
- getDb();
399
-
400
- let channels: unknown[] | undefined;
401
- if (opts.channels) {
402
- try {
403
- channels = JSON.parse(opts.channels);
404
- if (!Array.isArray(channels)) {
405
- writeError(cmd, "--channels must be a JSON array");
406
- process.exitCode = 1;
407
- return;
408
- }
409
- } catch {
391
+ if (!r.result?.ok) {
410
392
  writeError(
411
393
  cmd,
412
- `Invalid JSON for --channels: ${opts.channels}`,
394
+ r.result?.error ?? "Contact prompt failed",
413
395
  );
414
396
  process.exitCode = 1;
415
397
  return;
416
398
  }
417
- }
418
399
 
419
- const result = upsertContact({
420
- id: opts.id,
421
- displayName: opts.displayName,
422
- notes: opts.notes,
423
- role: opts.role as ContactRole | undefined,
424
- contactType: opts.contactType as ContactType | undefined,
425
- channels: channels as
426
- | {
427
- type: string;
428
- address: string;
429
- isPrimary?: boolean;
430
- externalUserId?: string;
431
- externalChatId?: string;
432
- status?: ChannelStatus;
433
- policy?: ChannelPolicy;
434
- }[]
435
- | undefined,
436
- });
437
-
438
- if (shouldOutputJson(cmd)) {
439
- writeOutput(cmd, { ok: true, contact: result });
440
- } else {
441
- process.stdout.write(formatContactDetail(result) + "\n");
442
- }
443
- } catch (err) {
444
- const message = err instanceof Error ? err.message : String(err);
445
- writeError(cmd, message);
446
- process.exitCode = 1;
447
- }
448
- },
449
- );
450
-
451
- contacts
452
- .command("prompt")
453
- .description("Prompt user to register a contact channel via the app UI")
454
- .option("--channel <channel>", "Suggested channel type hint (e.g. phone, email, telegram)")
455
- .option("--placeholder <placeholder>", "Placeholder text for the address input field")
456
- .option("--role <role>", "Intended role: guardian, trusted-contact, or unknown (default: unknown)")
457
- .option("--label <label>", "Display label shown in the prompt UI")
458
- .option("--description <description>", "Longer description shown in the prompt UI")
459
- .option("--timeout <ms>", "How long to wait for the user to submit (ms). Defaults to match the server-side prompt timeout.", String(310_000))
460
- .addHelpText(
461
- "after",
462
- `
463
- Opens a contact address prompt in the user's app. The user enters a channel
464
- address (phone number, email, Telegram ID, etc.). The address is saved with
465
- status "unverified". Verification is a separate step.
466
-
467
- Run \`assistant contacts prompt --help\` for full option details.`,
468
- )
469
- .action(
470
- async (
471
- opts: {
472
- channel?: string;
473
- placeholder?: string;
474
- role?: string;
475
- label?: string;
476
- description?: string;
477
- timeout?: string;
478
- },
479
- cmd: Command,
480
- ) => {
481
- try {
482
- const timeoutMs = opts.timeout ? parseInt(opts.timeout, 10) : 310_000;
483
- const ipc = await cliIpcCall<ContactPromptResult>(
484
- "contacts_prompt",
485
- {
486
- body: {
487
- channel: opts.channel,
488
- placeholder: opts.placeholder,
489
- role: opts.role ?? "unknown",
490
- label: opts.label,
491
- description: opts.description,
492
- },
493
- },
494
- { timeoutMs },
495
- );
496
-
497
- if (!ipc.ok || !ipc.result?.ok) {
498
- writeError(cmd, ipc.error ?? ipc.result?.error ?? "Contact prompt failed");
499
- process.exitCode = 1;
500
- return;
501
- }
400
+ const result = r.result;
401
+ if (shouldOutputJson(cmd)) {
402
+ writeOutput(cmd, result);
403
+ } else {
404
+ process.stdout.write(
405
+ `Registered ${result.channelType} channel: ${result.address}\n` +
406
+ ` Channel ID: ${result.channelId}\n` +
407
+ ` Contact ID: ${result.contactId}\n` +
408
+ ` Status: unverified\n`,
409
+ );
410
+ }
411
+ },
412
+ );
502
413
 
503
- const result = ipc.result;
504
- if (shouldOutputJson(cmd)) {
505
- writeOutput(cmd, result);
506
- } else {
507
- process.stdout.write(
508
- `Registered ${result.channelType} channel: ${result.address}\n` +
509
- ` Channel ID: ${result.channelId}\n` +
510
- ` Contact ID: ${result.contactId}\n` +
511
- ` Status: unverified\n`,
512
- );
513
- }
514
- } catch (err) {
515
- const message = err instanceof Error ? err.message : String(err);
516
- writeError(cmd, message);
517
- process.exitCode = 1;
518
- }
519
- },
520
- );
414
+ // -----------------------------------------------------------------------
415
+ // channels
416
+ // -----------------------------------------------------------------------
521
417
 
522
- const channelsCmds = contacts
523
- .command("channels")
524
- .description("Manage contact channels");
418
+ const channelsCmds = contacts
419
+ .command("channels")
420
+ .description("Manage contact channels");
525
421
 
526
- channelsCmds.addHelpText(
527
- "after",
528
- `
422
+ channelsCmds.addHelpText(
423
+ "after",
424
+ `
529
425
  Channels represent external communication endpoints linked to contacts —
530
426
  phone numbers, Telegram IDs, email addresses, etc. Each channel has a
531
427
  status (active, pending, revoked, blocked, unverified) and a policy
@@ -535,20 +431,23 @@ from that channel.
535
431
  Examples:
536
432
  $ assistant contacts channels update-status <channelId> --status revoked --reason "No longer needed"
537
433
  $ assistant contacts channels update-status <channelId> --policy deny`,
538
- );
539
-
540
- channelsCmds
541
- .command("update-status <channelId>")
542
- .description("Update a channel's status or policy")
543
- .option(
544
- "--status <status>",
545
- "New channel status: active, revoked, or blocked",
546
- )
547
- .option("--policy <policy>", "New channel policy: allow, deny, or escalate")
548
- .option("--reason <reason>", "Reason for the status change")
549
- .addHelpText(
550
- "after",
551
- `
434
+ );
435
+
436
+ channelsCmds
437
+ .command("update-status <channelId>")
438
+ .description("Update a channel's status or policy")
439
+ .option(
440
+ "--status <status>",
441
+ "New channel status: active, revoked, or blocked",
442
+ )
443
+ .option(
444
+ "--policy <policy>",
445
+ "New channel policy: allow, deny, or escalate",
446
+ )
447
+ .option("--reason <reason>", "Reason for the status change")
448
+ .addHelpText(
449
+ "after",
450
+ `
552
451
  Arguments:
553
452
  channelId UUID of the contact channel to update. Run 'assistant contacts get <contactId>'
554
453
  to see a contact's channel IDs.
@@ -568,85 +467,65 @@ Examples:
568
467
  $ assistant contacts channels update-status abc-123 --status blocked --reason "Spam" --json
569
468
  $ assistant contacts channels update-status abc-123 --policy deny --json
570
469
  $ assistant contacts channels update-status abc-123 --status active --policy allow --json`,
571
- )
572
- .action(
573
- async (
574
- channelId: string,
575
- opts: {
576
- status?: string;
577
- policy?: string;
578
- reason?: string;
579
- },
580
- cmd: Command,
581
- ) => {
582
- try {
583
- if (!opts.status && !opts.policy) {
584
- writeError(
585
- cmd,
586
- "At least one of --status or --policy must be provided",
587
- );
588
- process.exitCode = 1;
589
- return;
590
- }
591
-
592
- getDb();
593
-
594
- const existing = getChannelById(channelId);
595
- if (!existing) {
596
- writeError(cmd, `Channel not found: ${channelId}`);
597
- process.exitCode = 1;
598
- return;
599
- }
600
-
601
- const status = opts.status as ChannelStatus | undefined;
602
- const policy = opts.policy as ChannelPolicy | undefined;
470
+ )
471
+ .action(
472
+ async (
473
+ channelId: string,
474
+ opts: {
475
+ status?: string;
476
+ policy?: string;
477
+ reason?: string;
478
+ },
479
+ cmd: Command,
480
+ ) => {
481
+ if (!opts.status && !opts.policy) {
482
+ writeError(
483
+ cmd,
484
+ "At least one of --status or --policy must be provided",
485
+ );
486
+ process.exitCode = 1;
487
+ return;
488
+ }
603
489
 
604
- const revokedReason: string | null | undefined =
605
- status !== undefined
606
- ? status === "revoked"
607
- ? (opts.reason ?? null)
608
- : null
609
- : undefined;
610
- const blockedReason: string | null | undefined =
611
- status !== undefined
612
- ? status === "blocked"
613
- ? (opts.reason ?? null)
614
- : null
615
- : undefined;
490
+ const r = await cliIpcCall<{
491
+ ok: boolean;
492
+ contact?: ContactWithChannels;
493
+ }>("updateContactChannel", {
494
+ pathParams: { contactChannelId: channelId },
495
+ body: {
496
+ status: opts.status,
497
+ policy: opts.policy,
498
+ reason: opts.reason,
499
+ },
500
+ });
616
501
 
617
- const result = updateChannelStatus(channelId, {
618
- status,
619
- policy,
620
- revokedReason,
621
- blockedReason,
622
- });
502
+ if (!r.ok)
503
+ return exitFromIpcResult(
504
+ r as { ok: false; error?: string; statusCode?: number },
505
+ cmd,
506
+ );
623
507
 
624
- if (!result) {
625
- writeError(cmd, `Failed to update channel: ${channelId}`);
626
- process.exitCode = 1;
627
- return;
628
- }
508
+ if (shouldOutputJson(cmd)) {
509
+ writeOutput(cmd, r.result);
510
+ } else {
511
+ process.stdout.write(
512
+ `Updated channel ${channelId}\n`,
513
+ );
514
+ }
515
+ },
516
+ );
629
517
 
630
- if (shouldOutputJson(cmd)) {
631
- writeOutput(cmd, { ok: true, channel: result });
632
- } else {
633
- process.stdout.write(formatChannelDetail(result) + "\n");
634
- }
635
- } catch (err) {
636
- const message = err instanceof Error ? err.message : String(err);
637
- writeError(cmd, message);
638
- process.exitCode = 1;
639
- }
640
- },
641
- );
518
+ // -----------------------------------------------------------------------
519
+ // invites
520
+ // -----------------------------------------------------------------------
642
521
 
643
- const invites = contacts
644
- .command("invites")
645
- .description("Manage contact invites");
522
+ const invites = contacts
523
+ .command("invites")
524
+ .description("Manage contact invites");
646
525
 
647
- invites.addHelpText(
648
- "after",
649
- `
526
+ invites.addHelpText(
527
+ "after",
528
+ `
650
529
  Invites are tokens that grant channel access when redeemed. Each invite is
651
530
  tied to a source channel (telegram, phone, email, whatsapp) and can
652
531
  optionally have usage limits, expiration, and notes. When redeemed, the
@@ -658,16 +537,16 @@ Examples:
658
537
  $ assistant contacts invites create --source-channel telegram
659
538
  $ assistant contacts invites revoke abc-123
660
539
  $ assistant contacts invites redeem --token xyz-789 --source-channel telegram --external-user-id 12345`,
661
- );
662
-
663
- invites
664
- .command("list", { isDefault: true })
665
- .description("List invites")
666
- .option("--source-channel <sourceChannel>", "Filter by source channel")
667
- .option("--status <status>", "Filter by invite status")
668
- .addHelpText(
669
- "after",
670
- `
540
+ );
541
+
542
+ invites
543
+ .command("list", { isDefault: true })
544
+ .description("List invites")
545
+ .option("--source-channel <sourceChannel>", "Filter by source channel")
546
+ .option("--status <status>", "Filter by invite status")
547
+ .addHelpText(
548
+ "after",
549
+ `
671
550
  Lists all invites with optional filtering by source channel or status.
672
551
  Returns invite tokens, their source channels, usage counts, and expiration.
673
552
 
@@ -676,29 +555,42 @@ Examples:
676
555
  $ assistant contacts invites list --source-channel telegram
677
556
  $ assistant contacts invites list --status active
678
557
  $ assistant contacts invites list --source-channel phone --json`,
679
- )
680
- .action(
681
- async (
682
- opts: { sourceChannel?: string; status?: string },
683
- cmd: Command,
684
- ) => {
685
- try {
686
- getDb();
687
- const result = listIngressInvites({
688
- sourceChannel: opts.sourceChannel,
689
- status: opts.status,
690
- });
691
- if (!result.ok) {
692
- writeError(cmd, (result as { error?: string }).error ?? "Failed");
693
- process.exitCode = 1;
694
- return;
695
- }
696
- if (shouldOutputJson(cmd)) {
697
- writeOutput(cmd, { ok: true, invites: result.data });
698
- } else if (result.data.length === 0) {
699
- process.stdout.write("No invites found.\n");
700
- } else {
701
- for (const inv of result.data) {
558
+ )
559
+ .action(
560
+ async (
561
+ opts: { sourceChannel?: string; status?: string },
562
+ cmd: Command,
563
+ ) => {
564
+ const r = await cliIpcCall<{
565
+ ok: boolean;
566
+ invites: Array<{
567
+ id: string;
568
+ sourceChannel: string;
569
+ status: string;
570
+ token?: string;
571
+ }>;
572
+ }>("invites_list", {
573
+ queryParams: {
574
+ ...(opts.sourceChannel && {
575
+ sourceChannel: opts.sourceChannel,
576
+ }),
577
+ ...(opts.status && { status: opts.status }),
578
+ },
579
+ });
580
+
581
+ if (!r.ok)
582
+ return exitFromIpcResult(
583
+ r as { ok: false; error?: string; statusCode?: number },
584
+ cmd,
585
+ );
586
+
587
+ const invitesList = r.result!.invites;
588
+ if (shouldOutputJson(cmd)) {
589
+ writeOutput(cmd, { ok: true, invites: invitesList });
590
+ } else if (invitesList.length === 0) {
591
+ process.stdout.write("No invites found.\n");
592
+ } else {
593
+ for (const inv of invitesList) {
702
594
  const parts = [
703
595
  inv.id,
704
596
  inv.sourceChannel,
@@ -707,43 +599,44 @@ Examples:
707
599
  ].filter(Boolean);
708
600
  process.stdout.write(parts.join(" ") + "\n");
709
601
  }
710
- process.stdout.write(`\n${result.data.length} invite(s)\n`);
602
+ process.stdout.write(`\n${invitesList.length} invite(s)\n`);
711
603
  }
712
- } catch (err) {
713
- const message = err instanceof Error ? err.message : String(err);
714
- writeError(cmd, message);
715
- process.exitCode = 1;
716
- }
717
- },
718
- );
719
-
720
- invites
721
- .command("create")
722
- .description("Create a new invite")
723
- .requiredOption(
724
- "--source-channel <channel>",
725
- "Source channel (e.g. telegram, phone, email, whatsapp)",
726
- )
727
- .option("--note <note>", "Optional note")
728
- .option("--max-uses <n>", "Max redemptions")
729
- .option("--expires-in-ms <ms>", "Expiry duration in milliseconds")
730
- .option(
731
- "--contact-name <name>",
732
- "Contact name for personalizing instructions",
733
- )
734
- .option(
735
- "--expected-external-user-id <id>",
736
- "E.164 phone number (required for voice invites)",
737
- )
738
- .option("--friend-name <name>", "Friend name (required for voice invites)")
739
- .option(
740
- "--guardian-name <name>",
741
- "Guardian name (required for voice invites)",
742
- )
743
- .requiredOption("--contact-id <id>", "Contact ID to bind the invite to")
744
- .addHelpText(
745
- "after",
746
- `
604
+ },
605
+ );
606
+
607
+ invites
608
+ .command("create")
609
+ .description("Create a new invite")
610
+ .requiredOption(
611
+ "--source-channel <channel>",
612
+ "Source channel (e.g. telegram, phone, email, whatsapp)",
613
+ )
614
+ .option("--note <note>", "Optional note")
615
+ .option("--max-uses <n>", "Max redemptions")
616
+ .option("--expires-in-ms <ms>", "Expiry duration in milliseconds")
617
+ .option(
618
+ "--contact-name <name>",
619
+ "Contact name for personalizing instructions",
620
+ )
621
+ .option(
622
+ "--expected-external-user-id <id>",
623
+ "E.164 phone number (required for voice invites)",
624
+ )
625
+ .option(
626
+ "--friend-name <name>",
627
+ "Friend name (required for voice invites)",
628
+ )
629
+ .option(
630
+ "--guardian-name <name>",
631
+ "Guardian name (required for voice invites)",
632
+ )
633
+ .requiredOption(
634
+ "--contact-id <id>",
635
+ "Contact ID to bind the invite to",
636
+ )
637
+ .addHelpText(
638
+ "after",
639
+ `
747
640
  Creates a new invite token for the specified source channel. The --source-channel
748
641
  flag is required and must be one of: telegram, phone, email, whatsapp.
749
642
 
@@ -761,86 +654,89 @@ Voice invites require three additional fields:
761
654
  Examples:
762
655
  $ assistant contacts invites create --source-channel telegram --note "For Alice" --max-uses 1
763
656
  $ assistant contacts invites create --source-channel phone --expected-external-user-id "+15551234567" --friend-name "Alice" --guardian-name "Bob" --contact-name "Alice Smith"`,
764
- )
765
- .action(
766
- async (
767
- opts: {
768
- sourceChannel: string;
769
- note?: string;
770
- maxUses?: string;
771
- expiresInMs?: string;
772
- contactName?: string;
773
- expectedExternalUserId?: string;
774
- friendName?: string;
775
- guardianName?: string;
776
- contactId: string;
777
- },
778
- cmd: Command,
779
- ) => {
780
- try {
781
- const maxUses = opts.maxUses ? Number(opts.maxUses) : undefined;
782
- if (maxUses !== undefined && !Number.isFinite(maxUses)) {
783
- writeError(
784
- cmd,
785
- `--max-uses must be a number, got: ${opts.maxUses}`,
786
- );
787
- process.exitCode = 1;
788
- return;
789
- }
790
- const expiresInMs = opts.expiresInMs
791
- ? Number(opts.expiresInMs)
792
- : undefined;
793
- if (expiresInMs !== undefined && !Number.isFinite(expiresInMs)) {
794
- writeError(
795
- cmd,
796
- `--expires-in-ms must be a number, got: ${opts.expiresInMs}`,
797
- );
798
- process.exitCode = 1;
799
- return;
800
- }
801
- getDb();
802
- const result = await createIngressInvite({
803
- sourceChannel: opts.sourceChannel,
804
- note: opts.note,
805
- maxUses,
806
- expiresInMs,
807
- contactName: opts.contactName,
808
- expectedExternalUserId: opts.expectedExternalUserId,
809
- friendName: opts.friendName,
810
- guardianName: opts.guardianName,
811
- contactId: opts.contactId,
812
- });
813
- if (!result.ok) {
814
- writeError(
815
- cmd,
816
- (result as { error?: string }).error ?? "Failed",
817
- );
818
- process.exitCode = 1;
819
- return;
820
- }
821
- if (shouldOutputJson(cmd)) {
822
- writeOutput(cmd, { ok: true, invite: result.data });
657
+ )
658
+ .action(
659
+ async (
660
+ opts: {
661
+ sourceChannel: string;
662
+ note?: string;
663
+ maxUses?: string;
664
+ expiresInMs?: string;
665
+ contactName?: string;
666
+ expectedExternalUserId?: string;
667
+ friendName?: string;
668
+ guardianName?: string;
669
+ contactId: string;
670
+ },
671
+ cmd: Command,
672
+ ) => {
673
+ const maxUses = opts.maxUses ? Number(opts.maxUses) : undefined;
674
+ if (maxUses !== undefined && !Number.isFinite(maxUses)) {
675
+ writeError(
676
+ cmd,
677
+ `--max-uses must be a number, got: ${opts.maxUses}`,
678
+ );
679
+ process.exitCode = 1;
680
+ return;
681
+ }
682
+ const expiresInMs = opts.expiresInMs
683
+ ? Number(opts.expiresInMs)
684
+ : undefined;
685
+ if (expiresInMs !== undefined && !Number.isFinite(expiresInMs)) {
686
+ writeError(
687
+ cmd,
688
+ `--expires-in-ms must be a number, got: ${opts.expiresInMs}`,
689
+ );
690
+ process.exitCode = 1;
691
+ return;
692
+ }
693
+
694
+ const r = await cliIpcCall<{
695
+ ok: boolean;
696
+ invite: {
697
+ id: string;
698
+ sourceChannel: string;
699
+ token?: string;
700
+ };
701
+ }>("invites_create", {
702
+ body: {
703
+ sourceChannel: opts.sourceChannel,
704
+ note: opts.note,
705
+ maxUses,
706
+ expiresInMs,
707
+ contactName: opts.contactName,
708
+ expectedExternalUserId: opts.expectedExternalUserId,
709
+ friendName: opts.friendName,
710
+ guardianName: opts.guardianName,
711
+ contactId: opts.contactId,
712
+ },
713
+ });
714
+
715
+ if (!r.ok)
716
+ return exitFromIpcResult(
717
+ r as { ok: false; error?: string; statusCode?: number },
718
+ cmd,
719
+ );
720
+
721
+ const { invite } = r.result!;
722
+ if (shouldOutputJson(cmd)) {
723
+ writeOutput(cmd, { ok: true, invite });
823
724
  } else {
824
725
  process.stdout.write(
825
- `Created invite ${result.data.id} (${result.data.sourceChannel})\n`,
726
+ `Created invite ${invite.id} (${invite.sourceChannel})\n`,
826
727
  );
827
- if (result.data.token)
828
- process.stdout.write(`Token: ${result.data.token}\n`);
728
+ if (invite.token)
729
+ process.stdout.write(`Token: ${invite.token}\n`);
829
730
  }
830
- } catch (err) {
831
- const message = err instanceof Error ? err.message : String(err);
832
- writeError(cmd, message);
833
- process.exitCode = 1;
834
- }
835
- },
836
- );
837
-
838
- invites
839
- .command("revoke <inviteId>")
840
- .description("Revoke an active invite")
841
- .addHelpText(
842
- "after",
843
- `
731
+ },
732
+ );
733
+
734
+ invites
735
+ .command("revoke <inviteId>")
736
+ .description("Revoke an active invite")
737
+ .addHelpText(
738
+ "after",
739
+ `
844
740
  Arguments:
845
741
  inviteId UUID of the invite to revoke. Run 'assistant contacts invites list' to find IDs.
846
742
 
@@ -850,47 +746,47 @@ channel memberships are not affected. Returns the updated invite record.
850
746
  Examples:
851
747
  $ assistant contacts invites revoke 7a3b1c2d-4e5f-6789-abcd-ef0123456789
852
748
  $ assistant contacts invites revoke abc-123 --json`,
853
- )
854
- .action(async (inviteId: string, _opts: unknown, cmd: Command) => {
855
- try {
856
- getDb();
857
- const result = revokeIngressInvite(inviteId);
858
- if (!result.ok) {
859
- writeError(
860
- cmd,
861
- (result as { error?: string }).error ?? "Failed",
862
- );
863
- process.exitCode = 1;
864
- return;
865
- }
866
- if (shouldOutputJson(cmd)) {
867
- writeOutput(cmd, { ok: true, invite: result.data });
868
- } else {
869
- process.stdout.write(`Revoked invite ${inviteId}\n`);
870
- }
871
- } catch (err) {
872
- const message = err instanceof Error ? err.message : String(err);
873
- writeError(cmd, message);
874
- process.exitCode = 1;
875
- }
876
- });
877
-
878
- invites
879
- .command("redeem")
880
- .description("Redeem an invite via token or voice code")
881
- .option("--token <token>", "Invite token")
882
- .option("--source-channel <channel>", "Channel for redemption")
883
- .option("--external-user-id <id>", "External user ID")
884
- .option("--external-chat-id <id>", "External chat ID")
885
- .option("--code <code>", "6-digit voice code")
886
- .option(
887
- "--caller-external-user-id <phone>",
888
- "E.164 phone number for voice code redemption",
889
- )
890
- .option("--assistant-id <id>", "Assistant ID for voice code redemption")
891
- .addHelpText(
892
- "after",
893
- `
749
+ )
750
+ .action(async (inviteId: string, _opts: unknown, cmd: Command) => {
751
+ const r = await cliIpcCall<{
752
+ ok: boolean;
753
+ invite: unknown;
754
+ }>("invites_revoke", {
755
+ pathParams: { id: inviteId },
756
+ });
757
+
758
+ if (!r.ok)
759
+ return exitFromIpcResult(
760
+ r as { ok: false; error?: string; statusCode?: number },
761
+ cmd,
762
+ );
763
+
764
+ if (shouldOutputJson(cmd)) {
765
+ writeOutput(cmd, { ok: true, invite: r.result!.invite });
766
+ } else {
767
+ process.stdout.write(`Revoked invite ${inviteId}\n`);
768
+ }
769
+ });
770
+
771
+ invites
772
+ .command("redeem")
773
+ .description("Redeem an invite via token or voice code")
774
+ .option("--token <token>", "Invite token")
775
+ .option("--source-channel <channel>", "Channel for redemption")
776
+ .option("--external-user-id <id>", "External user ID")
777
+ .option("--external-chat-id <id>", "External chat ID")
778
+ .option("--code <code>", "6-digit voice code")
779
+ .option(
780
+ "--caller-external-user-id <phone>",
781
+ "E.164 phone number for voice code redemption",
782
+ )
783
+ .option(
784
+ "--assistant-id <id>",
785
+ "Assistant ID for voice code redemption",
786
+ )
787
+ .addHelpText(
788
+ "after",
789
+ `
894
790
  Two redemption modes:
895
791
 
896
792
  1. Token-based redemption: Provide --token, --source-channel, and at
@@ -905,24 +801,21 @@ Examples:
905
801
  $ assistant contacts invites redeem --token xyz-789 --source-channel telegram --external-user-id 12345
906
802
  $ assistant contacts invites redeem --code 123456 --caller-external-user-id "+15551234567"
907
803
  $ assistant contacts invites redeem --code 654321 --caller-external-user-id "+15559876543" --assistant-id asst-abc --json`,
908
- )
909
- .action(
910
- async (
911
- opts: {
912
- token?: string;
913
- sourceChannel?: string;
914
- externalUserId?: string;
915
- externalChatId?: string;
916
- code?: string;
917
- callerExternalUserId?: string;
918
- assistantId?: string;
919
- },
920
- cmd: Command,
921
- ) => {
922
- try {
923
- getDb();
924
- if (opts.code) {
925
- if (!opts.callerExternalUserId) {
804
+ )
805
+ .action(
806
+ async (
807
+ opts: {
808
+ token?: string;
809
+ sourceChannel?: string;
810
+ externalUserId?: string;
811
+ externalChatId?: string;
812
+ code?: string;
813
+ callerExternalUserId?: string;
814
+ assistantId?: string;
815
+ },
816
+ cmd: Command,
817
+ ) => {
818
+ if (opts.code && !opts.callerExternalUserId) {
926
819
  writeError(
927
820
  cmd,
928
821
  "--caller-external-user-id is required for voice code redemption",
@@ -930,61 +823,47 @@ Examples:
930
823
  process.exitCode = 1;
931
824
  return;
932
825
  }
933
- const result = redeemVoiceInviteCode({
934
- code: opts.code,
935
- callerExternalUserId: opts.callerExternalUserId,
936
- sourceChannel: "phone",
937
- ...(opts.assistantId ? { assistantId: opts.assistantId } : {}),
938
- });
939
- if (result.ok) {
940
- if (shouldOutputJson(cmd)) {
941
- writeOutput(cmd, {
942
- ok: true,
943
- type: result.type,
944
- memberId: result.memberId,
945
- ...(result.type === "redeemed"
946
- ? { inviteId: result.inviteId }
947
- : {}),
948
- });
949
- } else {
950
- process.stdout.write(
951
- `Redeemed (${result.type}), member: ${result.memberId}\n`,
952
- );
953
- }
954
- } else {
955
- writeError(cmd, result.reason);
956
- process.exitCode = 1;
957
- }
958
- } else {
959
- const result = redeemIngressInvite({
960
- token: opts.token,
961
- sourceChannel: opts.sourceChannel,
962
- ...(opts.externalUserId
963
- ? { externalUserId: opts.externalUserId }
964
- : {}),
965
- ...(opts.externalChatId
966
- ? { externalChatId: opts.externalChatId }
967
- : {}),
826
+
827
+ const r = await cliIpcCall<{
828
+ ok: boolean;
829
+ // Token path
830
+ invite?: unknown;
831
+ // Voice path
832
+ type?: string;
833
+ memberId?: string;
834
+ inviteId?: string;
835
+ }>("invites_redeem", {
836
+ body: {
837
+ token: opts.token,
838
+ sourceChannel: opts.sourceChannel,
839
+ externalUserId: opts.externalUserId,
840
+ externalChatId: opts.externalChatId,
841
+ code: opts.code,
842
+ callerExternalUserId: opts.callerExternalUserId,
843
+ assistantId: opts.assistantId,
844
+ },
968
845
  });
969
- if (!result.ok) {
970
- writeError(
846
+
847
+ if (!r.ok)
848
+ return exitFromIpcResult(
849
+ r as { ok: false; error?: string; statusCode?: number },
971
850
  cmd,
972
- (result as { error?: string }).error ?? "Failed",
973
851
  );
974
- process.exitCode = 1;
975
- return;
976
- }
852
+
853
+ const result = r.result!;
977
854
  if (shouldOutputJson(cmd)) {
978
- writeOutput(cmd, { ok: true, invite: result.data });
855
+ writeOutput(cmd, result);
856
+ } else if (result.type) {
857
+ // Voice code path
858
+ process.stdout.write(
859
+ `Redeemed (${result.type}), member: ${result.memberId}\n`,
860
+ );
979
861
  } else {
862
+ // Token path
980
863
  process.stdout.write("Invite redeemed.\n");
981
864
  }
982
- }
983
- } catch (err) {
984
- const message = err instanceof Error ? err.message : String(err);
985
- writeError(cmd, message);
986
- process.exitCode = 1;
987
- }
988
- },
989
- );
865
+ },
866
+ );
867
+ },
868
+ });
990
869
  }