@vellumai/assistant 0.4.49 → 0.4.51

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 (353) hide show
  1. package/ARCHITECTURE.md +24 -33
  2. package/README.md +3 -3
  3. package/docs/architecture/integrations.md +2 -2
  4. package/docs/architecture/keychain-broker.md +6 -6
  5. package/docs/architecture/memory.md +180 -119
  6. package/knip.json +32 -0
  7. package/package.json +3 -2
  8. package/src/__tests__/agent-loop.test.ts +3 -1
  9. package/src/__tests__/anthropic-provider.test.ts +114 -23
  10. package/src/__tests__/approval-cascade.test.ts +1 -15
  11. package/src/__tests__/approval-routes-http.test.ts +2 -0
  12. package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
  13. package/src/__tests__/btw-routes.test.ts +61 -5
  14. package/src/__tests__/canonical-guardian-store.test.ts +95 -0
  15. package/src/__tests__/checker.test.ts +13 -0
  16. package/src/__tests__/config-schema.test.ts +1 -68
  17. package/src/__tests__/config-watcher.test.ts +8 -0
  18. package/src/__tests__/context-memory-e2e.test.ts +11 -100
  19. package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
  20. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  21. package/src/__tests__/credential-security-e2e.test.ts +1 -0
  22. package/src/__tests__/credential-security-invariants.test.ts +8 -7
  23. package/src/__tests__/credential-vault-unit.test.ts +23 -18
  24. package/src/__tests__/credential-vault.test.ts +30 -18
  25. package/src/__tests__/credentials-cli.test.ts +257 -82
  26. package/src/__tests__/cu-unified-flow.test.ts +532 -0
  27. package/src/__tests__/date-context.test.ts +93 -77
  28. package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
  29. package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
  30. package/src/__tests__/history-repair.test.ts +245 -0
  31. package/src/__tests__/host-cu-proxy.test.ts +165 -3
  32. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  33. package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
  34. package/src/__tests__/integration-status.test.ts +31 -30
  35. package/src/__tests__/invite-redemption-service.test.ts +166 -13
  36. package/src/__tests__/invite-routes-http.test.ts +166 -5
  37. package/src/__tests__/keychain-broker-client.test.ts +4 -4
  38. package/src/__tests__/list-messages-attachments.test.ts +193 -0
  39. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
  40. package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
  41. package/src/__tests__/memory-recall-quality.test.ts +244 -407
  42. package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
  43. package/src/__tests__/memory-regressions.test.ts +477 -2841
  44. package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
  45. package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
  46. package/src/__tests__/mime-builder.test.ts +28 -0
  47. package/src/__tests__/native-web-search.test.ts +1 -0
  48. package/src/__tests__/oauth-cli.test.ts +824 -31
  49. package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
  50. package/src/__tests__/oauth-store.test.ts +363 -17
  51. package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
  52. package/src/__tests__/registry.test.ts +0 -1
  53. package/src/__tests__/relay-server.test.ts +55 -1
  54. package/src/__tests__/schedule-tools.test.ts +32 -0
  55. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  56. package/src/__tests__/secret-onetime-send.test.ts +1 -0
  57. package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
  58. package/src/__tests__/secure-keys.test.ts +78 -18
  59. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  60. package/src/__tests__/server-history-render.test.ts +2 -2
  61. package/src/__tests__/session-abort-tool-results.test.ts +1 -14
  62. package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
  63. package/src/__tests__/session-agent-loop.test.ts +19 -15
  64. package/src/__tests__/session-confirmation-signals.test.ts +1 -15
  65. package/src/__tests__/session-error.test.ts +124 -2
  66. package/src/__tests__/session-history-web-search.test.ts +918 -0
  67. package/src/__tests__/session-pre-run-repair.test.ts +1 -14
  68. package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
  69. package/src/__tests__/session-queue.test.ts +37 -27
  70. package/src/__tests__/session-runtime-assembly.test.ts +54 -0
  71. package/src/__tests__/session-slash-known.test.ts +1 -15
  72. package/src/__tests__/session-slash-queue.test.ts +1 -15
  73. package/src/__tests__/session-slash-unknown.test.ts +1 -15
  74. package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
  75. package/src/__tests__/session-workspace-injection.test.ts +3 -37
  76. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
  77. package/src/__tests__/skills-install-extract.test.ts +93 -0
  78. package/src/__tests__/skills.test.ts +2 -2
  79. package/src/__tests__/skillssh-registry.test.ts +451 -0
  80. package/src/__tests__/slack-channel-config.test.ts +10 -8
  81. package/src/__tests__/trust-store.test.ts +15 -0
  82. package/src/__tests__/twilio-config.test.ts +11 -10
  83. package/src/__tests__/twilio-provider.test.ts +9 -4
  84. package/src/__tests__/voice-invite-redemption.test.ts +85 -5
  85. package/src/agent/ax-tree-compaction.test.ts +51 -0
  86. package/src/agent/loop.ts +39 -12
  87. package/src/approvals/AGENTS.md +1 -1
  88. package/src/approvals/guardian-request-resolvers.ts +14 -2
  89. package/src/bundler/compiler-tools.ts +66 -2
  90. package/src/calls/call-domain.ts +134 -3
  91. package/src/calls/call-store.ts +6 -0
  92. package/src/calls/relay-server.ts +44 -6
  93. package/src/calls/relay-setup-router.ts +17 -1
  94. package/src/calls/twilio-config.ts +5 -4
  95. package/src/calls/twilio-provider.ts +14 -9
  96. package/src/calls/twilio-rest.ts +10 -7
  97. package/src/calls/types.ts +3 -1
  98. package/src/cli/commands/config.ts +14 -9
  99. package/src/cli/commands/contacts.ts +3 -0
  100. package/src/cli/commands/credentials.ts +170 -174
  101. package/src/cli/commands/doctor.ts +11 -8
  102. package/src/cli/commands/keys.ts +9 -9
  103. package/src/cli/commands/mcp.ts +46 -59
  104. package/src/cli/commands/memory.ts +16 -165
  105. package/src/cli/commands/oauth/apps.ts +68 -10
  106. package/src/cli/commands/oauth/connections.ts +475 -105
  107. package/src/cli/commands/oauth/index.ts +3 -3
  108. package/src/cli/commands/oauth/providers.ts +18 -4
  109. package/src/cli/commands/sessions.ts +5 -2
  110. package/src/cli/commands/skills.ts +173 -1
  111. package/src/cli/http-client.ts +0 -20
  112. package/src/cli/main-screen.tsx +2 -2
  113. package/src/cli/program.ts +5 -6
  114. package/src/cli.ts +20 -22
  115. package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
  116. package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
  117. package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
  118. package/src/config/bundled-skills/contacts/SKILL.md +35 -11
  119. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
  120. package/src/config/bundled-skills/gmail/SKILL.md +1 -1
  121. package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
  122. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
  123. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
  124. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
  125. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
  126. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
  127. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
  128. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
  129. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
  130. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
  131. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
  132. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
  133. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
  134. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
  135. package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
  136. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
  137. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
  138. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
  139. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
  140. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
  141. package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
  142. package/src/config/bundled-skills/messaging/SKILL.md +1 -1
  143. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
  144. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  145. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
  146. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
  147. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
  148. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
  149. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
  150. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
  151. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
  152. package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
  153. package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
  154. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
  155. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
  156. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
  157. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
  158. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
  159. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
  160. package/src/config/bundled-tool-registry.ts +2 -5
  161. package/src/config/loader.ts +6 -42
  162. package/src/config/schema.ts +1 -12
  163. package/src/config/schemas/memory-lifecycle.ts +0 -9
  164. package/src/config/schemas/memory-processing.ts +0 -180
  165. package/src/config/schemas/memory-retrieval.ts +32 -104
  166. package/src/config/schemas/memory.ts +0 -10
  167. package/src/config/types.ts +0 -4
  168. package/src/contacts/contact-store.ts +39 -2
  169. package/src/contacts/contacts-write.ts +9 -0
  170. package/src/context/window-manager.ts +4 -1
  171. package/src/daemon/config-watcher.ts +55 -2
  172. package/src/daemon/daemon-control.ts +1 -1
  173. package/src/daemon/date-context.ts +114 -31
  174. package/src/daemon/handlers/config-ingress.ts +2 -2
  175. package/src/daemon/handlers/config-slack-channel.ts +59 -39
  176. package/src/daemon/handlers/config-telegram.ts +23 -14
  177. package/src/daemon/handlers/session-history.ts +1 -358
  178. package/src/daemon/handlers/sessions.ts +18 -13
  179. package/src/daemon/handlers/shared.ts +3 -17
  180. package/src/daemon/handlers/skills.ts +20 -1
  181. package/src/daemon/history-repair.ts +72 -8
  182. package/src/daemon/host-cu-proxy.ts +55 -26
  183. package/src/daemon/lifecycle.ts +39 -4
  184. package/src/daemon/mcp-reload-service.ts +2 -2
  185. package/src/daemon/message-types/computer-use.ts +1 -12
  186. package/src/daemon/message-types/memory.ts +4 -16
  187. package/src/daemon/message-types/messages.ts +1 -0
  188. package/src/daemon/message-types/sessions.ts +4 -42
  189. package/src/daemon/server.ts +6 -1
  190. package/src/daemon/session-agent-loop-handlers.ts +38 -0
  191. package/src/daemon/session-agent-loop.ts +334 -48
  192. package/src/daemon/session-error.ts +89 -6
  193. package/src/daemon/session-history.ts +17 -7
  194. package/src/daemon/session-media-retry.ts +6 -2
  195. package/src/daemon/session-memory.ts +69 -149
  196. package/src/daemon/session-process.ts +10 -1
  197. package/src/daemon/session-runtime-assembly.ts +49 -19
  198. package/src/daemon/session-slash.ts +3 -5
  199. package/src/daemon/session-surfaces.ts +4 -1
  200. package/src/daemon/session-tool-setup.ts +7 -1
  201. package/src/daemon/session.ts +12 -2
  202. package/src/email/providers/index.ts +2 -2
  203. package/src/instrument.ts +61 -1
  204. package/src/media/avatar-router.ts +1 -1
  205. package/src/memory/admin.ts +2 -191
  206. package/src/memory/canonical-guardian-store.ts +38 -2
  207. package/src/memory/conversation-crud.ts +0 -33
  208. package/src/memory/conversation-queries.ts +25 -83
  209. package/src/memory/db-init.ts +32 -0
  210. package/src/memory/embedding-backend.ts +84 -8
  211. package/src/memory/embedding-types.ts +9 -1
  212. package/src/memory/indexer.ts +7 -46
  213. package/src/memory/invite-store.ts +19 -0
  214. package/src/memory/items-extractor.ts +274 -76
  215. package/src/memory/job-handlers/backfill.ts +2 -127
  216. package/src/memory/job-handlers/cleanup.ts +2 -16
  217. package/src/memory/job-handlers/extraction.ts +2 -138
  218. package/src/memory/job-handlers/index-maintenance.ts +1 -6
  219. package/src/memory/job-handlers/summarization.ts +3 -148
  220. package/src/memory/job-utils.ts +21 -59
  221. package/src/memory/jobs-store.ts +1 -159
  222. package/src/memory/jobs-worker.ts +9 -52
  223. package/src/memory/migrations/104-core-indexes.ts +3 -3
  224. package/src/memory/migrations/149-oauth-tables.ts +2 -0
  225. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
  226. package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
  227. package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
  228. package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
  229. package/src/memory/migrations/154-drop-fts.ts +20 -0
  230. package/src/memory/migrations/155-drop-conflicts.ts +7 -0
  231. package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
  232. package/src/memory/migrations/157-invite-contact-id.ts +104 -0
  233. package/src/memory/migrations/index.ts +8 -0
  234. package/src/memory/migrations/registry.ts +6 -0
  235. package/src/memory/qdrant-client.ts +148 -51
  236. package/src/memory/raw-query.ts +1 -1
  237. package/src/memory/retriever.test.ts +294 -273
  238. package/src/memory/retriever.ts +421 -645
  239. package/src/memory/schema/calls.ts +2 -0
  240. package/src/memory/schema/contacts.ts +1 -0
  241. package/src/memory/schema/memory-core.ts +3 -48
  242. package/src/memory/schema/oauth.ts +2 -0
  243. package/src/memory/search/formatting.ts +263 -176
  244. package/src/memory/search/lexical.ts +1 -254
  245. package/src/memory/search/ranking.ts +0 -455
  246. package/src/memory/search/semantic.ts +100 -14
  247. package/src/memory/search/staleness.ts +47 -0
  248. package/src/memory/search/tier-classifier.ts +21 -0
  249. package/src/memory/search/types.ts +15 -77
  250. package/src/memory/task-memory-cleanup.ts +4 -6
  251. package/src/messaging/provider.ts +1 -1
  252. package/src/messaging/providers/gmail/adapter.ts +1 -1
  253. package/src/messaging/providers/gmail/mime-builder.ts +17 -7
  254. package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
  255. package/src/messaging/providers/whatsapp/adapter.ts +13 -9
  256. package/src/messaging/registry.ts +9 -5
  257. package/src/oauth/byo-connection.test.ts +40 -25
  258. package/src/oauth/connect-orchestrator.ts +4 -10
  259. package/src/oauth/connection-resolver.ts +20 -6
  260. package/src/oauth/manual-token-connection.ts +5 -5
  261. package/src/oauth/oauth-store.ts +183 -31
  262. package/src/oauth/platform-connection.test.ts +1 -1
  263. package/src/oauth/provider-behaviors.ts +503 -4
  264. package/src/oauth/seed-providers.ts +214 -8
  265. package/src/oauth/token-persistence.ts +31 -16
  266. package/src/permissions/defaults.ts +1 -0
  267. package/src/permissions/trust-store.ts +23 -1
  268. package/src/playbooks/playbook-compiler.ts +1 -1
  269. package/src/prompts/system-prompt.ts +18 -2
  270. package/src/providers/anthropic/client.ts +56 -126
  271. package/src/providers/types.ts +7 -1
  272. package/src/runtime/AGENTS.md +9 -0
  273. package/src/runtime/auth/route-policy.ts +6 -3
  274. package/src/runtime/channel-readiness-service.ts +48 -40
  275. package/src/runtime/guardian-reply-router.ts +24 -22
  276. package/src/runtime/http-server.ts +2 -2
  277. package/src/runtime/http-types.ts +2 -0
  278. package/src/runtime/invite-redemption-service.ts +72 -12
  279. package/src/runtime/invite-service.ts +43 -0
  280. package/src/runtime/middleware/twilio-validation.ts +1 -1
  281. package/src/runtime/pending-interactions.ts +2 -2
  282. package/src/runtime/routes/brain-graph-routes.ts +10 -90
  283. package/src/runtime/routes/btw-routes.ts +10 -5
  284. package/src/runtime/routes/conversation-routes.ts +56 -11
  285. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
  286. package/src/runtime/routes/integrations/slack/channel.ts +2 -2
  287. package/src/runtime/routes/integrations/telegram.ts +2 -2
  288. package/src/runtime/routes/integrations/twilio.ts +17 -17
  289. package/src/runtime/routes/invite-routes.ts +29 -4
  290. package/src/runtime/routes/memory-item-routes.test.ts +754 -0
  291. package/src/runtime/routes/memory-item-routes.ts +503 -0
  292. package/src/runtime/routes/secret-routes.ts +17 -0
  293. package/src/runtime/routes/session-management-routes.ts +3 -3
  294. package/src/runtime/routes/settings-routes.ts +3 -3
  295. package/src/runtime/routes/trust-rules-routes.ts +14 -0
  296. package/src/runtime/routes/workspace-routes.ts +9 -4
  297. package/src/runtime/routes/workspace-utils.ts +8 -2
  298. package/src/schedule/integration-status.ts +26 -19
  299. package/src/security/keychain-broker-client.ts +17 -4
  300. package/src/security/oauth2.ts +6 -7
  301. package/src/security/secure-keys.ts +44 -19
  302. package/src/security/token-manager.ts +46 -39
  303. package/src/services/vercel-deploy.ts +0 -24
  304. package/src/signals/confirm.ts +78 -0
  305. package/src/signals/mcp-reload.ts +18 -0
  306. package/src/skills/catalog-install.ts +74 -18
  307. package/src/skills/skillssh-registry.ts +503 -0
  308. package/src/tools/assets/search.ts +5 -1
  309. package/src/tools/computer-use/definitions.ts +0 -10
  310. package/src/tools/computer-use/registry.ts +1 -1
  311. package/src/tools/credentials/vault.ts +22 -7
  312. package/src/tools/memory/definitions.ts +4 -13
  313. package/src/tools/memory/handlers.test.ts +83 -103
  314. package/src/tools/memory/handlers.ts +50 -85
  315. package/src/tools/network/script-proxy/session-manager.ts +8 -8
  316. package/src/tools/schedule/create.ts +10 -3
  317. package/src/tools/schedule/update.ts +8 -1
  318. package/src/tools/skills/load.ts +25 -2
  319. package/src/watcher/provider-types.ts +1 -1
  320. package/src/watcher/providers/github.ts +1 -1
  321. package/src/watcher/providers/gmail.ts +3 -3
  322. package/src/watcher/providers/google-calendar.ts +3 -3
  323. package/src/watcher/providers/linear.ts +1 -1
  324. package/src/__tests__/clarification-resolver.test.ts +0 -193
  325. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
  326. package/src/__tests__/conflict-policy.test.ts +0 -269
  327. package/src/__tests__/conflict-store.test.ts +0 -372
  328. package/src/__tests__/contradiction-checker.test.ts +0 -361
  329. package/src/__tests__/entity-extractor.test.ts +0 -211
  330. package/src/__tests__/entity-search.test.ts +0 -1117
  331. package/src/__tests__/profile-compiler.test.ts +0 -392
  332. package/src/__tests__/session-conflict-gate.test.ts +0 -1228
  333. package/src/__tests__/session-profile-injection.test.ts +0 -557
  334. package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
  335. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
  336. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
  337. package/src/daemon/session-conflict-gate.ts +0 -167
  338. package/src/daemon/session-dynamic-profile.ts +0 -77
  339. package/src/memory/clarification-resolver.ts +0 -417
  340. package/src/memory/conflict-intent.ts +0 -205
  341. package/src/memory/conflict-policy.ts +0 -127
  342. package/src/memory/conflict-store.ts +0 -410
  343. package/src/memory/contradiction-checker.ts +0 -508
  344. package/src/memory/entity-extractor.ts +0 -535
  345. package/src/memory/format-recall.ts +0 -47
  346. package/src/memory/fts-reconciler.ts +0 -165
  347. package/src/memory/job-handlers/conflict.ts +0 -200
  348. package/src/memory/profile-compiler.ts +0 -195
  349. package/src/memory/recall-cache.ts +0 -117
  350. package/src/memory/search/entity.ts +0 -535
  351. package/src/memory/search/query-expansion.test.ts +0 -70
  352. package/src/memory/search/query-expansion.ts +0 -118
  353. package/src/runtime/routes/mcp-routes.ts +0 -20
@@ -21,7 +21,7 @@ import { revokeScopedApprovalGrantsForContext } from "../memory/scoped-approval-
21
21
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
22
22
  import { isGuardian } from "../runtime/channel-verification-service.js";
23
23
  import { credentialKey } from "../security/credential-key.js";
24
- import { getSecureKey } from "../security/secure-keys.js";
24
+ import { getSecureKeyAsync } from "../security/secure-keys.js";
25
25
  import { getLogger } from "../util/logger.js";
26
26
  import { upsertActiveCallLease } from "./active-call-lease.js";
27
27
  import { isDeniedNumber } from "./call-constants.js";
@@ -177,7 +177,7 @@ export async function resolveCallerIdentity(
177
177
  }
178
178
 
179
179
  if (mode === "assistant_number") {
180
- const twilioConfig = getTwilioConfig();
180
+ const twilioConfig = await getTwilioConfig();
181
181
  log.info(
182
182
  { mode, source, fromNumber: twilioConfig.phoneNumber },
183
183
  "Resolved caller identity",
@@ -193,7 +193,7 @@ export async function resolveCallerIdentity(
193
193
  userNumber = identityConfig.userNumber;
194
194
  numberSource = "user_config";
195
195
  } else {
196
- const secureKeyValue = getSecureKey(
196
+ const secureKeyValue = await getSecureKeyAsync(
197
197
  credentialKey("twilio", "user_phone_number"),
198
198
  );
199
199
  if (secureKeyValue) {
@@ -1017,3 +1017,134 @@ export async function startVerificationCall(
1017
1017
  };
1018
1018
  }
1019
1019
  }
1020
+
1021
+ // ── Invite call ───────────────────────────────────────────────────────
1022
+
1023
+ export type StartInviteCallInput = {
1024
+ phoneNumber: string;
1025
+ friendName: string;
1026
+ guardianName: string;
1027
+ assistantId?: string;
1028
+ };
1029
+
1030
+ export type StartInviteCallResult = { ok: true; callSid: string } | CallError;
1031
+
1032
+ /**
1033
+ * Initiate an outbound call to deliver a voice invite to a contact.
1034
+ *
1035
+ * Creates a minimal call session with a voice channel binding and
1036
+ * passes invite-specific custom parameters so the relay server can
1037
+ * detect this is an invite redemption call.
1038
+ */
1039
+ export async function startInviteCall(
1040
+ input: StartInviteCallInput,
1041
+ ): Promise<StartInviteCallResult> {
1042
+ const { phoneNumber, friendName, guardianName } = input;
1043
+
1044
+ if (!phoneNumber || !E164_REGEX.test(phoneNumber)) {
1045
+ return {
1046
+ ok: false,
1047
+ error: "phone_number must be in E.164 format",
1048
+ status: 400,
1049
+ };
1050
+ }
1051
+
1052
+ let sessionId: string | null = null;
1053
+
1054
+ try {
1055
+ const config = loadConfig();
1056
+ const provider = new TwilioConversationRelayProvider();
1057
+
1058
+ // Resolve the assistant's Twilio number as the caller ID
1059
+ const identityResult = await resolveCallerIdentity(config);
1060
+ if (!identityResult.ok) {
1061
+ return { ok: false, error: identityResult.error, status: 400 };
1062
+ }
1063
+
1064
+ const preflightResult = await preflightVoiceIngress();
1065
+ if (!preflightResult.ok) {
1066
+ return preflightResult;
1067
+ }
1068
+ const ingressConfig = preflightResult.ingressConfig;
1069
+
1070
+ // Create a minimal conversation so the call session has a valid FK,
1071
+ // and bind it to the voice channel so it never appears as an unbound
1072
+ // desktop thread.
1073
+ const timestamp = Date.now();
1074
+ const convKey = `invite-call:${phoneNumber}:${timestamp}`;
1075
+ const { conversationId } = getOrCreateConversation(convKey);
1076
+
1077
+ upsertBinding({
1078
+ conversationId,
1079
+ sourceChannel: "phone",
1080
+ externalChatId: `invite-call:${phoneNumber}:${timestamp}`,
1081
+ });
1082
+
1083
+ const session = createCallSession({
1084
+ conversationId,
1085
+ provider: "twilio",
1086
+ fromNumber: identityResult.fromNumber,
1087
+ toNumber: phoneNumber,
1088
+ callMode: "invite",
1089
+ inviteFriendName: friendName,
1090
+ inviteGuardianName: guardianName,
1091
+ });
1092
+ sessionId = session.id;
1093
+
1094
+ const webhookUrl = await resolveCallbackUrl(
1095
+ () => getTwilioVoiceWebhookUrl(ingressConfig, session.id),
1096
+ "webhooks/twilio/voice",
1097
+ "twilio_voice",
1098
+ { callSessionId: session.id },
1099
+ );
1100
+ const statusCallbackUrl = await resolveCallbackUrl(
1101
+ () => getTwilioStatusCallbackUrl(ingressConfig),
1102
+ "webhooks/twilio/status",
1103
+ "twilio_status",
1104
+ );
1105
+
1106
+ upsertActiveCallLease({ callSessionId: session.id });
1107
+
1108
+ const { callSid } = await provider.initiateCall({
1109
+ from: identityResult.fromNumber,
1110
+ to: phoneNumber,
1111
+ webhookUrl,
1112
+ statusCallbackUrl,
1113
+ });
1114
+
1115
+ updateCallSession(session.id, { providerCallSid: callSid });
1116
+
1117
+ log.info(
1118
+ {
1119
+ callSessionId: session.id,
1120
+ callSid,
1121
+ to: phoneNumber,
1122
+ friendName,
1123
+ guardianName,
1124
+ },
1125
+ "Invite call initiated",
1126
+ );
1127
+
1128
+ return { ok: true, callSid };
1129
+ } catch (err) {
1130
+ const msg = err instanceof Error ? err.message : String(err);
1131
+ log.error(
1132
+ { err, phoneNumber, friendName, guardianName },
1133
+ "Failed to initiate invite call",
1134
+ );
1135
+
1136
+ if (sessionId) {
1137
+ updateCallSession(sessionId, {
1138
+ status: "failed",
1139
+ endedAt: Date.now(),
1140
+ lastError: msg,
1141
+ });
1142
+ }
1143
+
1144
+ return {
1145
+ ok: false,
1146
+ error: `Error initiating invite call: ${msg}`,
1147
+ status: 500,
1148
+ };
1149
+ }
1150
+ }
@@ -37,6 +37,8 @@ const parseCallSession = createRowMapper<
37
37
  status: { from: "status", transform: cast<CallSession["status"]>() },
38
38
  callMode: { from: "callMode", transform: cast<CallSession["callMode"]>() },
39
39
  verificationSessionId: "verificationSessionId",
40
+ inviteFriendName: "inviteFriendName",
41
+ inviteGuardianName: "inviteGuardianName",
40
42
  callerIdentityMode: "callerIdentityMode",
41
43
  callerIdentitySource: "callerIdentitySource",
42
44
  initiatedFromConversationId: "initiatedFromConversationId",
@@ -81,6 +83,8 @@ export function createCallSession(opts: {
81
83
  task?: string;
82
84
  callMode?: string;
83
85
  verificationSessionId?: string;
86
+ inviteFriendName?: string;
87
+ inviteGuardianName?: string;
84
88
  callerIdentityMode?: string;
85
89
  callerIdentitySource?: string;
86
90
  initiatedFromConversationId?: string;
@@ -98,6 +102,8 @@ export function createCallSession(opts: {
98
102
  status: "initiated" as const,
99
103
  callMode: (opts.callMode ?? null) as CallSession["callMode"],
100
104
  verificationSessionId: opts.verificationSessionId ?? null,
105
+ inviteFriendName: opts.inviteFriendName ?? null,
106
+ inviteGuardianName: opts.inviteGuardianName ?? null,
101
107
  callerIdentityMode: opts.callerIdentityMode ?? null,
102
108
  callerIdentitySource: opts.callerIdentitySource ?? null,
103
109
  initiatedFromConversationId: opts.initiatedFromConversationId ?? null,
@@ -575,6 +575,7 @@ export class RelayConnection {
575
575
  outcome.fromNumber,
576
576
  outcome.friendName,
577
577
  outcome.guardianName,
578
+ !resolved.isInbound,
578
579
  );
579
580
  return;
580
581
  case "name_capture":
@@ -772,6 +773,12 @@ export class RelayConnection {
772
773
  fromNumber: string;
773
774
  callerName?: string;
774
775
  skipMemberActivation?: boolean;
776
+ activationReason?:
777
+ | "invite_redeemed"
778
+ | "access_approved"
779
+ | "trusted_contact_verified";
780
+ friendName?: string;
781
+ guardianName?: string;
775
782
  }): void {
776
783
  const { assistantId, fromNumber, callerName } = params;
777
784
 
@@ -808,7 +815,25 @@ export class RelayConnection {
808
815
  updateCallSession(this.callSessionId, { status: "in_progress" });
809
816
 
810
817
  const guardianLabel = this.resolveGuardianLabel();
811
- const handoffText = `Great! ${guardianLabel} said I can speak with you. How can I help?`;
818
+ let handoffText: string;
819
+
820
+ if (params.activationReason === "invite_redeemed") {
821
+ const name = params.friendName;
822
+ const assistantName = this.resolveAssistantLabel();
823
+ const gLabel = params.guardianName || guardianLabel;
824
+ if (name) {
825
+ handoffText = assistantName
826
+ ? `Great, I've verified that you are ${name}. It's nice to meet you! I'm ${assistantName}, ${gLabel}'s assistant. How can I help?`
827
+ : `Great, I've verified that you are ${name}. It's nice to meet you! How can I help?`;
828
+ } else {
829
+ handoffText = assistantName
830
+ ? `Great, I've verified your identity. It's nice to meet you! I'm ${assistantName}, ${gLabel}'s assistant. How can I help?`
831
+ : `Great, I've verified your identity. It's nice to meet you! How can I help?`;
832
+ }
833
+ } else {
834
+ handoffText = `Great! ${guardianLabel} said I can speak with you. How can I help?`;
835
+ }
836
+
812
837
  this.sendTextToken(handoffText, true);
813
838
 
814
839
  recordCallEvent(this.callSessionId, "assistant_spoke", {
@@ -1000,6 +1025,7 @@ export class RelayConnection {
1000
1025
  this.continueCallAfterTrustedContactActivation({
1001
1026
  assistantId,
1002
1027
  fromNumber,
1028
+ activationReason: "trusted_contact_verified",
1003
1029
  });
1004
1030
  } else {
1005
1031
  // Inbound guardian verification: binding already handled above,
@@ -1096,6 +1122,7 @@ export class RelayConnection {
1096
1122
  fromNumber: string,
1097
1123
  friendName: string | null,
1098
1124
  guardianName: string | null,
1125
+ isOutbound: boolean,
1099
1126
  ): void {
1100
1127
  this.inviteRedemptionActive = true;
1101
1128
  this.inviteRedemptionAssistantId = assistantId;
@@ -1116,14 +1143,21 @@ export class RelayConnection {
1116
1143
 
1117
1144
  const displayFriend = friendName ?? "there";
1118
1145
  const displayGuardian = guardianName ?? "your contact";
1119
- this.sendTextToken(
1120
- `Welcome ${displayFriend}. Please enter the 6-digit code that ${displayGuardian} provided you to verify your identity.`,
1121
- true,
1122
- );
1146
+
1147
+ let promptText: string;
1148
+ if (isOutbound) {
1149
+ const assistantName = this.resolveAssistantLabel();
1150
+ promptText = assistantName
1151
+ ? `Hi ${displayFriend}, this is ${assistantName}, ${displayGuardian}'s assistant. To get started, please enter the 6-digit code that ${displayGuardian} shared with you.`
1152
+ : `Hi ${displayFriend}, this is ${displayGuardian}'s assistant. To get started, please enter the 6-digit code that ${displayGuardian} shared with you.`;
1153
+ } else {
1154
+ promptText = `Welcome ${displayFriend}. Please enter the 6-digit code that ${displayGuardian} provided you to verify your identity.`;
1155
+ }
1156
+ this.sendTextToken(promptText, true);
1123
1157
 
1124
1158
  log.info(
1125
1159
  { callSessionId: this.callSessionId, assistantId },
1126
- "Inbound voice invite redemption started",
1160
+ `${isOutbound ? "Outbound" : "Inbound"} voice invite redemption started`,
1127
1161
  );
1128
1162
  }
1129
1163
 
@@ -1358,6 +1392,7 @@ export class RelayConnection {
1358
1392
  assistantId,
1359
1393
  fromNumber,
1360
1394
  callerName: callerName ?? undefined,
1395
+ activationReason: "access_approved",
1361
1396
  });
1362
1397
 
1363
1398
  recordCallEvent(
@@ -1541,6 +1576,9 @@ export class RelayConnection {
1541
1576
  fromNumber: this.inviteRedemptionFromNumber,
1542
1577
  callerName: this.inviteRedemptionFriendName ?? undefined,
1543
1578
  skipMemberActivation: true,
1579
+ activationReason: "invite_redeemed",
1580
+ friendName: this.inviteRedemptionFriendName ?? undefined,
1581
+ guardianName: this.inviteRedemptionGuardianName ?? undefined,
1544
1582
  });
1545
1583
  } else {
1546
1584
  this.inviteRedemptionActive = false;
@@ -99,8 +99,24 @@ export function routeSetup(ctx: SetupContext): {
99
99
  actorTrust,
100
100
  };
101
101
 
102
- // ── Outbound guardian verification (persisted mode) ──────────────
102
+ // ── Outbound flow selection based on persisted call mode ──────────
103
103
  const persistedMode = ctx.session?.callMode;
104
+
105
+ // ── Outbound invite redemption (persisted mode) ─────────────────
106
+ if (persistedMode === "invite") {
107
+ return {
108
+ outcome: {
109
+ action: "invite_redemption" as const,
110
+ assistantId,
111
+ fromNumber: ctx.to,
112
+ friendName: ctx.session?.inviteFriendName ?? null,
113
+ guardianName: ctx.session?.inviteGuardianName ?? null,
114
+ },
115
+ resolved,
116
+ };
117
+ }
118
+
119
+ // ── Outbound guardian verification (persisted mode) ──────────────
104
120
  const persistedVsId = ctx.session?.verificationSessionId;
105
121
  const customParamVsId = ctx.customParameters?.verificationSessionId;
106
122
  const verificationSessionId = persistedVsId ?? customParamVsId;
@@ -4,7 +4,7 @@ import {
4
4
  getTwilioRelayUrl,
5
5
  } from "../inbound/public-ingress-urls.js";
6
6
  import { credentialKey } from "../security/credential-key.js";
7
- import { getSecureKey } from "../security/secure-keys.js";
7
+ import { getSecureKeyAsync } from "../security/secure-keys.js";
8
8
  import { ConfigError } from "../util/errors.js";
9
9
  import { getLogger } from "../util/logger.js";
10
10
 
@@ -20,7 +20,7 @@ export interface TwilioConfig {
20
20
 
21
21
  /**
22
22
  * Resolve the Twilio phone number using a unified fallback chain so that
23
- * all callers (calls, SMS adapter, readiness checks, invite transports)
23
+ * all callers (calls, readiness checks, invite transports)
24
24
  * agree on the same number.
25
25
  *
26
26
  * Resolution order:
@@ -38,10 +38,11 @@ export function resolveTwilioPhoneNumber(): string {
38
38
  return "";
39
39
  }
40
40
 
41
- export function getTwilioConfig(): TwilioConfig {
41
+ export async function getTwilioConfig(): Promise<TwilioConfig> {
42
42
  const config = loadConfig();
43
43
  const accountSid = config.twilio?.accountSid || "";
44
- const authToken = getSecureKey(credentialKey("twilio", "auth_token")) || "";
44
+ const authToken =
45
+ (await getSecureKeyAsync(credentialKey("twilio", "auth_token"))) || "";
45
46
  const phoneNumber = resolveTwilioPhoneNumber();
46
47
  const webhookBaseUrl = getPublicBaseUrl(config);
47
48
 
@@ -1,7 +1,7 @@
1
1
  import { createHmac, timingSafeEqual } from "node:crypto";
2
2
 
3
3
  import { credentialKey } from "../security/credential-key.js";
4
- import { getSecureKey } from "../security/secure-keys.js";
4
+ import { getSecureKeyAsync } from "../security/secure-keys.js";
5
5
  import { ProviderError } from "../util/errors.js";
6
6
  import { getLogger } from "../util/logger.js";
7
7
  import {
@@ -24,8 +24,11 @@ export class TwilioConversationRelayProvider implements VoiceProvider {
24
24
 
25
25
  // ── Credential helpers ──────────────────────────────────────────────
26
26
 
27
- private getCredentials(): { accountSid: string; authToken: string } {
28
- return getTwilioCredentials();
27
+ private async getCredentials(): Promise<{
28
+ accountSid: string;
29
+ authToken: string;
30
+ }> {
31
+ return await getTwilioCredentials();
29
32
  }
30
33
 
31
34
  private authHeader(accountSid: string, authToken: string): string {
@@ -39,7 +42,7 @@ export class TwilioConversationRelayProvider implements VoiceProvider {
39
42
  // ── VoiceProvider interface ─────────────────────────────────────────
40
43
 
41
44
  async initiateCall(opts: InitiateCallOptions): Promise<{ callSid: string }> {
42
- const { accountSid, authToken } = this.getCredentials();
45
+ const { accountSid, authToken } = await this.getCredentials();
43
46
 
44
47
  const body = new URLSearchParams({
45
48
  From: opts.from,
@@ -104,7 +107,7 @@ export class TwilioConversationRelayProvider implements VoiceProvider {
104
107
  }
105
108
 
106
109
  async endCall(callSid: string): Promise<void> {
107
- const { accountSid, authToken } = this.getCredentials();
110
+ const { accountSid, authToken } = await this.getCredentials();
108
111
 
109
112
  log.info({ callSid }, "Ending Twilio call");
110
113
 
@@ -139,7 +142,7 @@ export class TwilioConversationRelayProvider implements VoiceProvider {
139
142
  }
140
143
 
141
144
  async getCallStatus(callSid: string): Promise<string> {
142
- const { accountSid, authToken } = this.getCredentials();
145
+ const { accountSid, authToken } = await this.getCredentials();
143
146
 
144
147
  const res = await fetch(
145
148
  `${this.baseUrl(accountSid)}/Calls/${callSid}.json`,
@@ -179,7 +182,7 @@ export class TwilioConversationRelayProvider implements VoiceProvider {
179
182
  async checkCallerIdEligibility(
180
183
  phoneNumber: string,
181
184
  ): Promise<{ eligible: boolean; reason?: string }> {
182
- const { accountSid, authToken } = this.getCredentials();
185
+ const { accountSid, authToken } = await this.getCredentials();
183
186
  const encodedNumber = encodeURIComponent(phoneNumber);
184
187
 
185
188
  let incomingOk = false;
@@ -280,8 +283,10 @@ export class TwilioConversationRelayProvider implements VoiceProvider {
280
283
  * Exposed as a static method so callers (e.g. the HTTP server webhook
281
284
  * middleware) can check availability independently.
282
285
  */
283
- static getAuthToken(): string | null {
284
- return getSecureKey(credentialKey("twilio", "auth_token")) || null;
286
+ static async getAuthToken(): Promise<string | null> {
287
+ return (
288
+ (await getSecureKeyAsync(credentialKey("twilio", "auth_token"))) || null
289
+ );
285
290
  }
286
291
 
287
292
  /**
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { loadConfig } from "../config/loader.js";
10
10
  import { credentialKey } from "../security/credential-key.js";
11
- import { getSecureKey } from "../security/secure-keys.js";
11
+ import { getSecureKeyAsync } from "../security/secure-keys.js";
12
12
  import { ConfigError, ProviderError } from "../util/errors.js";
13
13
 
14
14
  export interface TwilioCredentials {
@@ -28,14 +28,17 @@ function resolveAccountSid(): string | undefined {
28
28
  }
29
29
 
30
30
  /** Resolve the Twilio Auth Token from the credential store. */
31
- function resolveAuthToken(): string | undefined {
32
- return getSecureKey(credentialKey("twilio", "auth_token")) || undefined;
31
+ async function resolveAuthToken(): Promise<string | undefined> {
32
+ return (
33
+ (await getSecureKeyAsync(credentialKey("twilio", "auth_token"))) ||
34
+ undefined
35
+ );
33
36
  }
34
37
 
35
38
  /** Resolve Twilio credentials from config (SID) and credential store (token). Throws if not configured. */
36
- export function getTwilioCredentials(): TwilioCredentials {
39
+ export async function getTwilioCredentials(): Promise<TwilioCredentials> {
37
40
  const accountSid = resolveAccountSid();
38
- const authToken = resolveAuthToken();
41
+ const authToken = await resolveAuthToken();
39
42
  if (!accountSid || !authToken) {
40
43
  throw new ConfigError(
41
44
  "Twilio credentials not configured. Set twilio.accountSid via config and store auth token via credential store.",
@@ -45,9 +48,9 @@ export function getTwilioCredentials(): TwilioCredentials {
45
48
  }
46
49
 
47
50
  /** Check whether Twilio credentials are present (non-throwing). */
48
- export function hasTwilioCredentials(): boolean {
51
+ export async function hasTwilioCredentials(): Promise<boolean> {
49
52
  try {
50
- return !!resolveAccountSid() && !!resolveAuthToken();
53
+ return !!resolveAccountSid() && !!(await resolveAuthToken());
51
54
  } catch {
52
55
  return false;
53
56
  }
@@ -58,7 +58,7 @@ export type PendingQuestionStatus =
58
58
  * uses this as the primary signal for deterministic flow selection,
59
59
  * with Twilio setup custom parameters as a secondary/observability signal.
60
60
  */
61
- export type CallMode = "normal" | "verification";
61
+ export type CallMode = "normal" | "verification" | "invite";
62
62
 
63
63
  export interface CallSession {
64
64
  id: string;
@@ -71,6 +71,8 @@ export interface CallSession {
71
71
  status: CallStatus;
72
72
  callMode: CallMode | null;
73
73
  verificationSessionId: string | null;
74
+ inviteFriendName: string | null;
75
+ inviteGuardianName: string | null;
74
76
  callerIdentityMode: string | null;
75
77
  callerIdentitySource: string | null;
76
78
  initiatedFromConversationId?: string | null;
@@ -39,10 +39,13 @@ export function registerConfigCommand(program: Command): void {
39
39
  "after",
40
40
  `
41
41
  Configuration is stored in config.json in the assistant workspace directory.
42
- Keys support dotted paths for nested values (e.g. calls.enabled, apiKeys.anthropic).
42
+ Keys support dotted paths for nested values (e.g. calls.enabled, twilio.accountSid).
43
43
  Values are auto-parsed as JSON (booleans, numbers, objects) with fallback to
44
44
  plain string if parsing fails.
45
45
 
46
+ API keys are managed separately via secure storage. Use "assistant keys list"
47
+ and "assistant keys set <provider> <key>" to view and manage API keys.
48
+
46
49
  Examples:
47
50
  $ assistant config list
48
51
  $ assistant config get provider
@@ -53,14 +56,14 @@ Examples:
53
56
  config
54
57
  .command("set <key> <value>")
55
58
  .description(
56
- "Set a config value (supports dotted paths like apiKeys.anthropic)",
59
+ "Set a config value (supports dotted paths like calls.enabled)",
57
60
  )
58
61
  .addHelpText(
59
62
  "after",
60
63
  `
61
64
  Arguments:
62
65
  key Dotted path to the config key (e.g. provider, calls.enabled,
63
- apiKeys.anthropic). Intermediate objects are created automatically.
66
+ twilio.accountSid). Intermediate objects are created automatically.
64
67
  value The value to store. Parsed as JSON first (so "true" becomes boolean
65
68
  true, "42" becomes number 42). Falls back to plain string if JSON
66
69
  parsing fails.
@@ -68,10 +71,11 @@ Arguments:
68
71
  After writing the value to config.json, the lockfile is automatically synced
69
72
  to reflect the updated configuration.
70
73
 
74
+ To manage API keys, use "assistant keys set <provider> <key>" instead.
75
+
71
76
  Examples:
72
77
  $ assistant config set provider anthropic
73
- $ assistant config set calls.enabled true
74
- $ assistant config set apiKeys.anthropic sk-ant-abc123`,
78
+ $ assistant config set calls.enabled true`,
75
79
  )
76
80
  .action((key: string, value: string) => {
77
81
  const raw = loadRawConfig();
@@ -100,10 +104,11 @@ Arguments:
100
104
  Prints the value at the given key path. If the key is not set, prints
101
105
  "(not set)". Object values are pretty-printed as indented JSON.
102
106
 
107
+ To view API keys, use "assistant keys list" instead.
108
+
103
109
  Examples:
104
110
  $ assistant config get provider
105
- $ assistant config get calls.enabled
106
- $ assistant config get apiKeys`,
111
+ $ assistant config get calls.enabled`,
107
112
  )
108
113
  .action((key: string) => {
109
114
  const raw = loadRawConfig();
@@ -133,8 +138,8 @@ Dumps the full raw configuration from config.json as pretty-printed JSON.
133
138
  If no configuration has been set, prints "No configuration set".
134
139
 
135
140
  The --search flag filters results by case-insensitive substring match against
136
- flattened dotted key paths. For example, --search api matches apiKeys.anthropic,
137
- apiKeys.openai, and any other key containing "api".
141
+ flattened dotted key paths. For example, --search calls matches calls.enabled,
142
+ calls.recordingEnabled, and any other key containing "calls".
138
143
 
139
144
  Examples:
140
145
  $ assistant config list
@@ -497,6 +497,7 @@ Examples:
497
497
  "--guardian-name <name>",
498
498
  "Guardian name (required for voice invites)",
499
499
  )
500
+ .requiredOption("--contact-id <id>", "Contact ID to bind the invite to")
500
501
  .addHelpText(
501
502
  "after",
502
503
  `
@@ -529,6 +530,7 @@ Examples:
529
530
  expectedExternalUserId?: string;
530
531
  friendName?: string;
531
532
  guardianName?: string;
533
+ contactId: string;
532
534
  },
533
535
  cmd: Command,
534
536
  ) => {
@@ -563,6 +565,7 @@ Examples:
563
565
  expectedExternalUserId: opts.expectedExternalUserId,
564
566
  friendName: opts.friendName,
565
567
  guardianName: opts.guardianName,
568
+ contactId: opts.contactId,
566
569
  });
567
570
  if (result.ok) {
568
571
  writeOutput(cmd, { ok: true, invite: result.data });