@vellumai/assistant 0.7.3 → 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 (778) hide show
  1. package/AGENTS.md +11 -0
  2. package/ARCHITECTURE.md +29 -28
  3. package/Dockerfile +6 -4
  4. package/README.md +2 -2
  5. package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
  6. package/bun.lock +3 -0
  7. package/docker-entrypoint.sh +16 -0
  8. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  9. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  10. package/eslint.config.mjs +12 -0
  11. package/knip.json +3 -1
  12. package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
  13. package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
  14. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
  15. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
  16. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
  17. package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  19. package/openapi.yaml +4126 -959
  20. package/package.json +5 -1
  21. package/scripts/generate-openapi.ts +52 -4
  22. package/scripts/sync-llm-catalog.ts +165 -0
  23. package/scripts/sync-web-search-catalog.ts +107 -0
  24. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  25. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  26. package/src/__tests__/annotate-risk-options.test.ts +291 -0
  27. package/src/__tests__/anthropic-provider.test.ts +92 -2
  28. package/src/__tests__/app-control-flow.test.ts +7 -0
  29. package/src/__tests__/approval-cascade.test.ts +8 -16
  30. package/src/__tests__/approval-routes-http.test.ts +6 -0
  31. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  32. package/src/__tests__/auto-analysis-end-to-end.test.ts +12 -25
  33. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  34. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  35. package/src/__tests__/btw-routes.test.ts +1 -0
  36. package/src/__tests__/call-constants.test.ts +10 -1
  37. package/src/__tests__/call-controller.test.ts +127 -0
  38. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  39. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  40. package/src/__tests__/channel-policy.test.ts +12 -0
  41. package/src/__tests__/checker.test.ts +89 -0
  42. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +88 -30
  43. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  44. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  45. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  46. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  47. package/src/__tests__/config-loader-platform-defaults.test.ts +345 -8
  48. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  49. package/src/__tests__/config-schema.test.ts +14 -3
  50. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  51. package/src/__tests__/config-set-route.test.ts +198 -0
  52. package/src/__tests__/config-watcher.test.ts +6 -0
  53. package/src/__tests__/contacts-tools.test.ts +51 -199
  54. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  55. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  56. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  57. package/src/__tests__/context-search-fanout.test.ts +20 -157
  58. package/src/__tests__/context-search-memory-source.test.ts +3 -26
  59. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  60. package/src/__tests__/context-search-types.test.ts +7 -2
  61. package/src/__tests__/context-window-manager.test.ts +389 -1
  62. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -6
  63. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  64. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
  65. package/src/__tests__/conversation-agent-loop.test.ts +3 -3
  66. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
  67. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  68. package/src/__tests__/conversation-error.test.ts +38 -0
  69. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  70. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  71. package/src/__tests__/conversation-init.benchmark.test.ts +2 -1
  72. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  73. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  74. package/src/__tests__/conversation-process-callsite.test.ts +22 -7
  75. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -6
  76. package/src/__tests__/conversation-runtime-assembly.test.ts +19 -10
  77. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  78. package/src/__tests__/conversation-slash-unknown.test.ts +1 -6
  79. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  81. package/src/__tests__/conversation-surfaces-data-persist.test.ts +73 -1
  82. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +59 -0
  83. package/src/__tests__/conversation-workspace-injection.test.ts +1 -7
  84. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -7
  85. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  86. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  87. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  88. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  89. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  90. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  91. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  92. package/src/__tests__/filing-service.test.ts +25 -22
  93. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  94. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  95. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  96. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -34
  97. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  98. package/src/__tests__/heartbeat-service.test.ts +50 -233
  99. package/src/__tests__/history-repair.test.ts +89 -0
  100. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  101. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  102. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  103. package/src/__tests__/host-browser-routes.test.ts +325 -33
  104. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  105. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  106. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  107. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  108. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  109. package/src/__tests__/injector-chain.test.ts +24 -16
  110. package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
  111. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  112. package/src/__tests__/install-skill-routing.test.ts +2 -2
  113. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +169 -67
  114. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  115. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  116. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  117. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  118. package/src/__tests__/llm-resolver.test.ts +46 -0
  119. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  120. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  121. package/src/__tests__/mcp-cli.test.ts +182 -220
  122. package/src/__tests__/mcp-health-check.test.ts +56 -27
  123. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  124. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  125. package/src/__tests__/notification-decision-fallback.test.ts +91 -0
  126. package/src/__tests__/notification-decision-strategy.test.ts +22 -0
  127. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  128. package/src/__tests__/oauth-cli.test.ts +38 -1888
  129. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  130. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  131. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  132. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  133. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  134. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  135. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  136. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  137. package/src/__tests__/plugin-types.test.ts +13 -11
  138. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  139. package/src/__tests__/profile-entry-status.test.ts +43 -0
  140. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  141. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  142. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  143. package/src/__tests__/relay-server.test.ts +164 -2
  144. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  145. package/src/__tests__/schedule-retry.test.ts +56 -4
  146. package/src/__tests__/schedule-routes.test.ts +104 -0
  147. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  148. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  149. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  150. package/src/__tests__/scheduler-wake.test.ts +0 -63
  151. package/src/__tests__/secret-allowlist.test.ts +1 -0
  152. package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
  153. package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
  154. package/src/__tests__/secret-response-routing.test.ts +7 -5
  155. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  156. package/src/__tests__/server-history-render.test.ts +82 -0
  157. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  158. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  159. package/src/__tests__/skill-include-graph.test.ts +31 -0
  160. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  161. package/src/__tests__/skill-load-tool.test.ts +42 -16
  162. package/src/__tests__/skills.test.ts +39 -0
  163. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  164. package/src/__tests__/suggestion-routes.test.ts +3 -3
  165. package/src/__tests__/sync-message-contract.test.ts +63 -0
  166. package/src/__tests__/task-scheduler.test.ts +88 -23
  167. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
  168. package/src/__tests__/tool-executor.test.ts +155 -0
  169. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  170. package/src/__tests__/usage-cli.test.ts +11 -73
  171. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  172. package/src/__tests__/vercel-config.test.ts +168 -0
  173. package/src/__tests__/voice-session-bridge.test.ts +3 -0
  174. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  175. package/src/__tests__/web-search.test.ts +303 -2
  176. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  177. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  178. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +153 -0
  179. package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
  180. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  181. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  182. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  183. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  184. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  185. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  186. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  187. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +15 -27
  188. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  189. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  190. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  191. package/src/acp/resolve-agent.test.ts +25 -0
  192. package/src/acp/resolve-agent.ts +13 -2
  193. package/src/acp/session-manager.ts +14 -0
  194. package/src/agent/loop.ts +11 -0
  195. package/src/approvals/guardian-decision-primitive.ts +0 -13
  196. package/src/approvals/guardian-request-resolvers.ts +19 -102
  197. package/src/calls/call-constants.ts +5 -8
  198. package/src/calls/call-controller.ts +130 -67
  199. package/src/calls/relay-server.ts +42 -1
  200. package/src/calls/relay-setup-router.ts +36 -0
  201. package/src/calls/types.ts +1 -0
  202. package/src/calls/voice-session-bridge.ts +24 -5
  203. package/src/channels/config.ts +14 -1
  204. package/src/channels/types.ts +1 -0
  205. package/src/cli/AGENTS.md +164 -4
  206. package/src/cli/__tests__/notifications.test.ts +54 -0
  207. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  208. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  209. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  210. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  211. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  212. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  213. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  214. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  215. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  216. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  217. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  218. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  219. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  220. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  221. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  222. package/src/cli/commands/__tests__/status.test.ts +249 -0
  223. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  224. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  225. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  226. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  227. package/src/cli/commands/attachment.ts +8 -3
  228. package/src/cli/commands/audit.ts +95 -64
  229. package/src/cli/commands/auth.ts +61 -58
  230. package/src/cli/commands/avatar.ts +276 -390
  231. package/src/cli/commands/backup.ts +409 -505
  232. package/src/cli/commands/bash.ts +9 -5
  233. package/src/cli/commands/browser.ts +28 -9
  234. package/src/cli/commands/cache.ts +9 -4
  235. package/src/cli/commands/changelog.ts +414 -0
  236. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  237. package/src/cli/commands/clients.ts +8 -3
  238. package/src/cli/commands/completions.ts +9 -9
  239. package/src/cli/commands/config.ts +102 -72
  240. package/src/cli/commands/contacts.ts +575 -696
  241. package/src/cli/commands/conversations-defer.ts +17 -69
  242. package/src/cli/commands/conversations-import.ts +90 -253
  243. package/src/cli/commands/conversations.ts +346 -436
  244. package/src/cli/commands/credential-execution.ts +9 -6
  245. package/src/cli/commands/credentials.ts +456 -736
  246. package/src/cli/commands/domain.ts +128 -206
  247. package/src/cli/commands/email.ts +606 -794
  248. package/src/cli/commands/gateway.ts +8 -1
  249. package/src/cli/commands/image-generation.ts +157 -205
  250. package/src/cli/commands/inference-providers.ts +352 -0
  251. package/src/cli/commands/inference-session.ts +415 -0
  252. package/src/cli/commands/inference.ts +87 -65
  253. package/src/cli/commands/keys.ts +8 -3
  254. package/src/cli/commands/mcp.ts +103 -287
  255. package/src/cli/commands/memory-v2.ts +163 -517
  256. package/src/cli/commands/notifications.ts +33 -7
  257. package/src/cli/commands/oauth/apps.ts +292 -261
  258. package/src/cli/commands/oauth/connect.ts +182 -345
  259. package/src/cli/commands/oauth/disconnect.ts +16 -215
  260. package/src/cli/commands/oauth/index.ts +49 -45
  261. package/src/cli/commands/oauth/mode.ts +43 -199
  262. package/src/cli/commands/oauth/ping.ts +17 -125
  263. package/src/cli/commands/oauth/providers.ts +732 -921
  264. package/src/cli/commands/oauth/request.ts +60 -350
  265. package/src/cli/commands/oauth/shared.ts +11 -121
  266. package/src/cli/commands/oauth/status.ts +31 -121
  267. package/src/cli/commands/oauth/token.ts +13 -55
  268. package/src/cli/commands/pending.ts +19 -10
  269. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  270. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  271. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  272. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  273. package/src/cli/commands/platform/connect.ts +16 -80
  274. package/src/cli/commands/platform/disconnect.ts +14 -112
  275. package/src/cli/commands/platform/index.ts +177 -246
  276. package/src/cli/commands/routes.ts +153 -336
  277. package/src/cli/commands/sequence.ts +316 -360
  278. package/src/cli/commands/skills.ts +449 -671
  279. package/src/cli/commands/status.ts +58 -37
  280. package/src/cli/commands/stt.ts +94 -262
  281. package/src/cli/commands/task.ts +14 -40
  282. package/src/cli/commands/trust.ts +8 -3
  283. package/src/cli/commands/tts.ts +162 -167
  284. package/src/cli/commands/ui.ts +35 -42
  285. package/src/cli/commands/usage.ts +188 -126
  286. package/src/cli/commands/watchers.ts +8 -3
  287. package/src/cli/commands/webhooks.ts +99 -193
  288. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  289. package/src/cli/lib/daemon-credential-client.ts +4 -5
  290. package/src/cli/lib/nested-value.ts +44 -0
  291. package/src/cli/lib/open-browser.ts +36 -0
  292. package/src/cli/lib/register-command.ts +19 -0
  293. package/src/cli/lib/time-ago.ts +34 -0
  294. package/src/cli/program.ts +2 -4
  295. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  296. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  297. package/src/cli/utils/conversation-id.ts +30 -0
  298. package/src/cli/utils/parse-duration.ts +41 -0
  299. package/src/config/acp-defaults.test.ts +5 -1
  300. package/src/config/acp-defaults.ts +11 -4
  301. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  302. package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
  303. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  304. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  305. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  306. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  307. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  308. package/src/config/bundled-tool-registry.ts +0 -2
  309. package/src/config/feature-flag-registry.json +17 -17
  310. package/src/config/llm-resolver.ts +16 -1
  311. package/src/config/loader.ts +148 -33
  312. package/src/config/raw-config-utils.ts +2 -30
  313. package/src/config/schema.ts +4 -0
  314. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  315. package/src/config/schemas/call-site-catalog.ts +29 -7
  316. package/src/config/schemas/llm-request-logs.ts +57 -0
  317. package/src/config/schemas/llm.ts +52 -2
  318. package/src/config/schemas/memory-retrospective.ts +48 -0
  319. package/src/config/schemas/memory-v2.ts +33 -2
  320. package/src/config/schemas/memory.ts +4 -0
  321. package/src/config/schemas/services.ts +15 -12
  322. package/src/config/seed-inference-profiles.ts +195 -134
  323. package/src/contacts/contact-store.ts +0 -61
  324. package/src/context/window-manager.ts +191 -5
  325. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +111 -0
  326. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  327. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  328. package/src/daemon/approval-generators.ts +23 -29
  329. package/src/daemon/config-watcher.ts +2 -0
  330. package/src/daemon/conversation-agent-loop-handlers.ts +56 -0
  331. package/src/daemon/conversation-agent-loop.ts +140 -107
  332. package/src/daemon/conversation-error.ts +21 -0
  333. package/src/daemon/conversation-lifecycle.ts +68 -13
  334. package/src/daemon/conversation-process.ts +36 -19
  335. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  336. package/src/daemon/conversation-slash.ts +175 -23
  337. package/src/daemon/conversation-store.ts +17 -10
  338. package/src/daemon/conversation-surfaces.ts +92 -26
  339. package/src/daemon/conversation-tool-setup.ts +33 -19
  340. package/src/daemon/conversation.ts +49 -10
  341. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  342. package/src/daemon/guardian-action-generators.ts +7 -22
  343. package/src/daemon/handlers/config-model.ts +8 -126
  344. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  345. package/src/daemon/handlers/config-vercel.ts +3 -1
  346. package/src/daemon/handlers/shared.ts +26 -0
  347. package/src/daemon/handlers/skills.ts +84 -5
  348. package/src/daemon/history-repair.ts +33 -6
  349. package/src/daemon/host-app-control-proxy.ts +44 -19
  350. package/src/daemon/host-bash-proxy.ts +85 -158
  351. package/src/daemon/host-browser-proxy.ts +97 -36
  352. package/src/daemon/host-cu-proxy.ts +1 -1
  353. package/src/daemon/host-file-proxy.ts +1 -1
  354. package/src/daemon/host-proxy-base.ts +13 -1
  355. package/src/daemon/host-proxy-preactivation.ts +25 -1
  356. package/src/daemon/host-transfer-proxy.ts +2 -2
  357. package/src/daemon/identity-helpers.ts +19 -0
  358. package/src/daemon/lifecycle.ts +128 -114
  359. package/src/daemon/meet-host-supervisor.ts +15 -15
  360. package/src/daemon/memory-v2-startup.ts +62 -14
  361. package/src/daemon/message-protocol.ts +6 -0
  362. package/src/daemon/message-types/bookmarks.ts +18 -0
  363. package/src/daemon/message-types/conversations.ts +12 -9
  364. package/src/daemon/message-types/messages.ts +28 -2
  365. package/src/daemon/message-types/sync.ts +60 -0
  366. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  367. package/src/daemon/pkb-reminder-builder.ts +21 -7
  368. package/src/daemon/process-message.ts +56 -23
  369. package/src/daemon/server.ts +23 -18
  370. package/src/daemon/shutdown-handlers.ts +0 -2
  371. package/src/daemon/tool-setup-types.ts +9 -0
  372. package/src/daemon/tool-side-effects.ts +6 -4
  373. package/src/daemon/wake-target-adapter.ts +11 -0
  374. package/src/documents/document-store.ts +35 -1
  375. package/src/export/transcript-formatter.ts +61 -2
  376. package/src/filing/filing-service.ts +42 -56
  377. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  378. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  379. package/src/heartbeat/heartbeat-service.ts +149 -128
  380. package/src/home/__tests__/feed-types.test.ts +63 -131
  381. package/src/home/__tests__/feed-writer.test.ts +77 -278
  382. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  383. package/src/home/feed-types.ts +19 -73
  384. package/src/home/feed-writer.ts +25 -156
  385. package/src/home/post-connect-feed.ts +1 -3
  386. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  387. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  388. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  389. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  390. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  391. package/src/ipc/assistant-server.ts +148 -42
  392. package/src/ipc/cli-client.ts +370 -50
  393. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  394. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  395. package/src/ipc/skill-routes/events.ts +30 -3
  396. package/src/ipc/skill-server.ts +99 -42
  397. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  398. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  399. package/src/live-voice/live-voice-session-manager.ts +11 -4
  400. package/src/live-voice/live-voice-session.ts +14 -6
  401. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  402. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  403. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  404. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  405. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +10 -57
  406. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  407. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  408. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  409. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  410. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  411. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  412. package/src/memory/bookmark-crud.ts +179 -0
  413. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  414. package/src/memory/context-search/agent-protocol.ts +5 -1
  415. package/src/memory/context-search/agent-runner.ts +60 -85
  416. package/src/memory/context-search/limits.ts +1 -4
  417. package/src/memory/context-search/search.ts +23 -113
  418. package/src/memory/context-search/sources/conversations.ts +18 -6
  419. package/src/memory/context-search/sources/memory-v2.ts +40 -31
  420. package/src/memory/context-search/sources/memory.ts +9 -2
  421. package/src/memory/context-search/sources/workspace.ts +13 -10
  422. package/src/memory/context-search/types.ts +1 -1
  423. package/src/memory/conversation-bootstrap.ts +11 -0
  424. package/src/memory/conversation-crud.ts +312 -10
  425. package/src/memory/conversation-queries.ts +9 -5
  426. package/src/memory/conversation-title-service.ts +1 -0
  427. package/src/memory/conversation-types.ts +16 -0
  428. package/src/memory/db-init.ts +14 -0
  429. package/src/memory/embedding-backend.ts +2 -1
  430. package/src/memory/embedding-runtime-manager.ts +1 -2
  431. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +104 -61
  432. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
  433. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  434. package/src/memory/graph/conversation-graph-memory.ts +108 -14
  435. package/src/memory/graph/extraction.ts +4 -0
  436. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  437. package/src/memory/graph/graph-search.test.ts +6 -5
  438. package/src/memory/graph/graph-search.ts +3 -4
  439. package/src/memory/graph/retriever.test.ts +12 -7
  440. package/src/memory/graph/retriever.ts +4 -5
  441. package/src/memory/graph/tool-handlers.ts +20 -11
  442. package/src/memory/graph/tools.ts +48 -9
  443. package/src/memory/indexer.ts +18 -2
  444. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +120 -6
  445. package/src/memory/jobs/embed-concept-page.ts +261 -89
  446. package/src/memory/jobs-store.ts +51 -1
  447. package/src/memory/jobs-worker.ts +60 -7
  448. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  449. package/src/memory/llm-request-log-source-local.ts +26 -0
  450. package/src/memory/llm-request-log-source.ts +97 -0
  451. package/src/memory/llm-request-log-store.ts +1 -1
  452. package/src/memory/memory-retrospective-constants.ts +13 -0
  453. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  454. package/src/memory/memory-retrospective-job.ts +351 -0
  455. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  456. package/src/memory/memory-retrospective-state.ts +162 -0
  457. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  458. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  459. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  460. package/src/memory/message-content.ts +38 -1
  461. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  462. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  463. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  464. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  465. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  466. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  467. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  468. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  469. package/src/memory/migrations/243-provider-connections.ts +68 -0
  470. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  471. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  472. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  473. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  474. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  475. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  476. package/src/memory/migrations/index.ts +7 -0
  477. package/src/memory/pkb/pkb-search.test.ts +6 -5
  478. package/src/memory/pkb/pkb-search.ts +4 -5
  479. package/src/memory/published-pages-store.ts +16 -0
  480. package/src/memory/qdrant-client.ts +3 -0
  481. package/src/memory/schema/bookmarks.ts +38 -0
  482. package/src/memory/schema/conversations.ts +2 -0
  483. package/src/memory/schema/index.ts +2 -0
  484. package/src/memory/schema/inference.ts +29 -0
  485. package/src/memory/schema/memory-core.ts +9 -0
  486. package/src/memory/search/semantic.ts +5 -9
  487. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  488. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  489. package/src/memory/v2/__tests__/activation.test.ts +46 -9
  490. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  491. package/src/memory/v2/__tests__/consolidation-job.test.ts +140 -163
  492. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  493. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  494. package/src/memory/v2/__tests__/injection.test.ts +768 -33
  495. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  496. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  497. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  498. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  499. package/src/memory/v2/__tests__/qdrant.test.ts +382 -9
  500. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  501. package/src/memory/v2/__tests__/router.test.ts +516 -0
  502. package/src/memory/v2/__tests__/sim.test.ts +163 -8
  503. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  504. package/src/memory/v2/__tests__/static-context.test.ts +8 -35
  505. package/src/memory/v2/__tests__/sweep-job.test.ts +114 -33
  506. package/src/memory/v2/activation-store.ts +34 -5
  507. package/src/memory/v2/activation.ts +40 -27
  508. package/src/memory/v2/backfill-jobs.ts +17 -84
  509. package/src/memory/v2/consolidation-job.ts +92 -86
  510. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  511. package/src/memory/v2/injection.ts +466 -115
  512. package/src/memory/v2/migration.ts +117 -20
  513. package/src/memory/v2/page-index.ts +191 -0
  514. package/src/memory/v2/page-store.ts +42 -0
  515. package/src/memory/v2/prompts/consolidation.ts +14 -7
  516. package/src/memory/v2/prompts/router.ts +192 -0
  517. package/src/memory/v2/qdrant.ts +307 -133
  518. package/src/memory/v2/reranker.ts +14 -7
  519. package/src/memory/v2/router.ts +322 -0
  520. package/src/memory/v2/sim.ts +88 -34
  521. package/src/memory/v2/skill-store.ts +118 -29
  522. package/src/memory/v2/static-context.ts +20 -17
  523. package/src/memory/v2/sweep-job.ts +127 -102
  524. package/src/memory/v2/types.ts +16 -5
  525. package/src/memory/validation.ts +13 -0
  526. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  527. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  528. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  529. package/src/notifications/adapters/platform.ts +171 -0
  530. package/src/notifications/conversation-pairing.ts +2 -2
  531. package/src/notifications/copy-composer.ts +61 -12
  532. package/src/notifications/decision-engine.ts +46 -0
  533. package/src/notifications/destination-resolver.ts +21 -0
  534. package/src/notifications/emit-signal.ts +28 -1
  535. package/src/notifications/home-feed-side-effect.ts +111 -0
  536. package/src/notifications/signal.ts +5 -0
  537. package/src/permissions/checker.ts +12 -0
  538. package/src/permissions/gateway-threshold-reader.ts +116 -8
  539. package/src/permissions/ipc-risk-types.ts +2 -0
  540. package/src/permissions/prompter.ts +86 -96
  541. package/src/permissions/secret-prompter.ts +31 -31
  542. package/src/plugin-api/index.ts +13 -0
  543. package/src/plugin-api/package.json +12 -0
  544. package/src/plugin-api/types.ts +62 -0
  545. package/src/plugins/defaults/injectors.ts +20 -5
  546. package/src/plugins/external-plugin-loader.ts +294 -0
  547. package/src/plugins/types.ts +46 -30
  548. package/src/plugins/user-loader.ts +64 -41
  549. package/src/proactive-artifact/job.test.ts +63 -8
  550. package/src/proactive-artifact/job.ts +20 -2
  551. package/src/proactive-artifact/message-copy.ts +18 -1
  552. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  553. package/src/proactive-artifact/trigger-state.ts +4 -0
  554. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  555. package/src/prompts/system-prompt.ts +22 -1
  556. package/src/prompts/templates/SOUL.md +13 -28
  557. package/src/prompts/update-bulletin-job.ts +61 -73
  558. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  559. package/src/providers/__tests__/inference.test.ts +288 -0
  560. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  561. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  562. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  563. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  564. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  565. package/src/providers/anthropic/client.ts +95 -26
  566. package/src/providers/call-site-routing.ts +94 -16
  567. package/src/providers/connection-resolution.ts +163 -0
  568. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  569. package/src/providers/inference/adapter-factory.ts +173 -0
  570. package/src/providers/inference/auth.ts +112 -0
  571. package/src/providers/inference/backfill.ts +196 -0
  572. package/src/providers/inference/connections.ts +356 -0
  573. package/src/providers/inference/resolve-auth.ts +65 -0
  574. package/src/providers/model-catalog.ts +104 -6
  575. package/src/providers/openai/responses-provider.ts +4 -2
  576. package/src/providers/provider-env-vars.ts +17 -7
  577. package/src/providers/provider-secret-catalog.ts +49 -30
  578. package/src/providers/provider-send-message.ts +41 -20
  579. package/src/providers/registry.ts +143 -159
  580. package/src/providers/retry.ts +18 -10
  581. package/src/providers/search-provider-catalog.ts +121 -0
  582. package/src/runtime/AGENTS.md +18 -5
  583. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  584. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  585. package/src/runtime/actor-trust-resolver.ts +32 -10
  586. package/src/runtime/agent-wake.ts +35 -6
  587. package/src/runtime/assistant-event-hub.ts +3 -85
  588. package/src/runtime/auth/route-policy.ts +304 -8
  589. package/src/runtime/auth/same-actor.ts +2 -0
  590. package/src/runtime/background-job-runner.ts +339 -0
  591. package/src/runtime/btw-sidechain.ts +1 -0
  592. package/src/runtime/channel-approvals.ts +3 -2
  593. package/src/runtime/guardian-reply-router.ts +0 -10
  594. package/src/runtime/http-router.ts +36 -1
  595. package/src/runtime/http-server.ts +31 -5
  596. package/src/runtime/http-types.ts +2 -0
  597. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  598. package/src/runtime/middleware/request-logger.ts +62 -1
  599. package/src/runtime/pending-interactions.ts +19 -15
  600. package/src/runtime/pre-first-message-gate.ts +83 -0
  601. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  602. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  603. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  604. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  605. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  606. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  607. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  608. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
  609. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  610. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  611. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  612. package/src/runtime/routes/acp-routes.ts +10 -8
  613. package/src/runtime/routes/app-management-routes.ts +228 -3
  614. package/src/runtime/routes/approval-routes.ts +7 -21
  615. package/src/runtime/routes/audit-routes.ts +43 -0
  616. package/src/runtime/routes/auth-routes.ts +72 -0
  617. package/src/runtime/routes/avatar-routes.ts +273 -20
  618. package/src/runtime/routes/backup-routes.ts +406 -2
  619. package/src/runtime/routes/bookmark-routes.ts +154 -0
  620. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  621. package/src/runtime/routes/consolidation-routes.ts +8 -9
  622. package/src/runtime/routes/contact-routes.ts +0 -160
  623. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  624. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  625. package/src/runtime/routes/conversation-query-routes.ts +373 -82
  626. package/src/runtime/routes/conversation-routes.ts +31 -10
  627. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  628. package/src/runtime/routes/credential-routes.ts +540 -0
  629. package/src/runtime/routes/debug-bash-routes.ts +2 -0
  630. package/src/runtime/routes/debug-routes.ts +2 -2
  631. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  632. package/src/runtime/routes/domain-routes.ts +167 -0
  633. package/src/runtime/routes/email-routes.ts +603 -0
  634. package/src/runtime/routes/errors.ts +2 -2
  635. package/src/runtime/routes/events-routes.ts +192 -0
  636. package/src/runtime/routes/filing-routes.ts +2 -3
  637. package/src/runtime/routes/home-feed-routes.ts +6 -78
  638. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  639. package/src/runtime/routes/host-browser-routes.ts +103 -22
  640. package/src/runtime/routes/http-adapter.ts +2 -0
  641. package/src/runtime/routes/identity-routes.ts +5 -0
  642. package/src/runtime/routes/image-generation-routes.ts +99 -0
  643. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  644. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  645. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  646. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -7
  647. package/src/runtime/routes/index.ts +36 -0
  648. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  649. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  650. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  651. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  652. package/src/runtime/routes/inference-send-routes.ts +115 -0
  653. package/src/runtime/routes/integrations/twilio.ts +1 -0
  654. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  655. package/src/runtime/routes/memory-item-routes.test.ts +3 -9
  656. package/src/runtime/routes/memory-item-routes.ts +5 -6
  657. package/src/runtime/routes/memory-v2-routes.ts +105 -404
  658. package/src/runtime/routes/notification-routes.ts +2 -0
  659. package/src/runtime/routes/oauth-apps.ts +112 -7
  660. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  661. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  662. package/src/runtime/routes/oauth-providers.ts +298 -8
  663. package/src/runtime/routes/platform-routes.ts +336 -0
  664. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  665. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  666. package/src/runtime/routes/playground/state.ts +2 -1
  667. package/src/runtime/routes/publish-routes.ts +221 -0
  668. package/src/runtime/routes/schedule-routes.ts +82 -0
  669. package/src/runtime/routes/sequence-routes.ts +291 -0
  670. package/src/runtime/routes/settings-routes.ts +2 -10
  671. package/src/runtime/routes/skills-routes.ts +31 -1
  672. package/src/runtime/routes/stt-routes.ts +240 -3
  673. package/src/runtime/routes/surface-action-routes.ts +43 -7
  674. package/src/runtime/routes/tts-routes.ts +67 -0
  675. package/src/runtime/routes/types.ts +32 -0
  676. package/src/runtime/routes/user-routes-cli.ts +243 -0
  677. package/src/runtime/routes/webhook-routes.ts +165 -0
  678. package/src/runtime/sync/resource-sync-events.ts +25 -0
  679. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  680. package/src/runtime/sync/sync-publisher.ts +21 -0
  681. package/src/schedule/scheduler.ts +200 -123
  682. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  683. package/src/security/secret-patterns.ts +3 -0
  684. package/src/sequence/engine.ts +38 -40
  685. package/src/skills/include-graph.ts +35 -13
  686. package/src/subagent/manager.ts +20 -15
  687. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  688. package/src/tools/browser/browser-execution.ts +15 -4
  689. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  690. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  691. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  692. package/src/tools/browser/cdp-client/factory.ts +66 -5
  693. package/src/tools/browser/runtime-check.ts +77 -0
  694. package/src/tools/document/document-tool.ts +20 -0
  695. package/src/tools/executor.ts +18 -2
  696. package/src/tools/memory/register.test.ts +10 -8
  697. package/src/tools/memory/register.ts +9 -1
  698. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  699. package/src/tools/network/web-search.ts +280 -37
  700. package/src/tools/permission-checker.ts +28 -5
  701. package/src/tools/skills/load.ts +24 -20
  702. package/src/tools/subagent/spawn.ts +3 -3
  703. package/src/tools/terminal/shell.ts +44 -0
  704. package/src/tools/tool-name-aliases.ts +19 -0
  705. package/src/tools/types.ts +19 -1
  706. package/src/usage/attribution.ts +3 -2
  707. package/src/util/pricing.ts +86 -160
  708. package/src/watcher/__tests__/engine.test.ts +301 -0
  709. package/src/watcher/constants.ts +7 -0
  710. package/src/watcher/engine.ts +90 -90
  711. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  712. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  713. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  714. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +4 -62
  715. package/src/workspace/migrations/069-seed-onboarding-threads.ts +34 -0
  716. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
  717. package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
  718. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  719. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  720. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  721. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  722. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  723. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  724. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  725. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  726. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  727. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  728. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  729. package/src/workspace/migrations/registry.ts +28 -0
  730. package/src/workspace/migrations/runner.ts +13 -2
  731. package/src/workspace/migrations/types.ts +13 -3
  732. package/src/workspace/provider-commit-message-generator.ts +3 -2
  733. package/src/__tests__/context-search-pkb-source.test.ts +0 -492
  734. package/src/__tests__/credentials-cli.test.ts +0 -1225
  735. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  736. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  737. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  738. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  739. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  740. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  741. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  742. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  743. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  744. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  745. package/src/cli/commands/autonomy.ts +0 -365
  746. package/src/cli/commands/memory.ts +0 -424
  747. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -1201
  748. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  749. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  750. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  751. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  752. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  753. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  754. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  755. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  756. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  757. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  758. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  759. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  760. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  761. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  762. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  763. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  764. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  765. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  766. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  767. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  768. package/src/home/assistant-feed-authoring.ts +0 -135
  769. package/src/home/emit-feed-event.ts +0 -169
  770. package/src/home/feed-scheduler.ts +0 -281
  771. package/src/home/platform-gmail-digest.ts +0 -163
  772. package/src/home/rewrite-command-preview.ts +0 -66
  773. package/src/home/rewrite-feed-title.ts +0 -58
  774. package/src/home/rollup-producer.ts +0 -426
  775. package/src/memory/admin.ts +0 -326
  776. package/src/memory/context-search/sources/pkb.ts +0 -477
  777. package/src/memory/graph/compaction.ts +0 -299
  778. /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
  }