@vellumai/assistant 0.5.5 → 0.5.7

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 (382) hide show
  1. package/.env.example +16 -2
  2. package/ARCHITECTURE.md +6 -75
  3. package/Dockerfile +4 -5
  4. package/README.md +0 -2
  5. package/bun.lock +0 -414
  6. package/docs/architecture/keychain-broker.md +45 -240
  7. package/docs/architecture/security.md +0 -17
  8. package/docs/credential-execution-service.md +2 -2
  9. package/node_modules/@vellumai/ces-contracts/package.json +1 -0
  10. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +119 -0
  11. package/node_modules/@vellumai/credential-storage/package.json +1 -0
  12. package/node_modules/@vellumai/egress-proxy/package.json +1 -0
  13. package/package.json +2 -3
  14. package/src/__tests__/actor-token-service.test.ts +1 -2
  15. package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
  16. package/src/__tests__/browser-skill-endstate.test.ts +6 -5
  17. package/src/__tests__/btw-routes.test.ts +0 -39
  18. package/src/__tests__/call-domain.test.ts +0 -128
  19. package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
  20. package/src/__tests__/channel-approval-routes.test.ts +0 -5
  21. package/src/__tests__/channel-readiness-service.test.ts +1 -60
  22. package/src/__tests__/checker.test.ts +4 -2
  23. package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
  24. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  25. package/src/__tests__/config-schema.test.ts +3 -3
  26. package/src/__tests__/context-window-manager.test.ts +78 -0
  27. package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
  28. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  29. package/src/__tests__/conversation-skill-tools.test.ts +0 -54
  30. package/src/__tests__/conversation-title-service.test.ts +117 -1
  31. package/src/__tests__/credential-execution-feature-gates.test.ts +28 -14
  32. package/src/__tests__/credential-execution-managed-contract.test.ts +33 -18
  33. package/src/__tests__/credential-security-e2e.test.ts +0 -66
  34. package/src/__tests__/credential-security-invariants.test.ts +4 -45
  35. package/src/__tests__/credentials-cli.test.ts +78 -0
  36. package/src/__tests__/db-migration-rollback.test.ts +2015 -1
  37. package/src/__tests__/docker-signing-key-bootstrap.test.ts +98 -0
  38. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
  39. package/src/__tests__/guardian-routing-state.test.ts +0 -5
  40. package/src/__tests__/host-shell-tool.test.ts +6 -7
  41. package/src/__tests__/http-user-message-parity.test.ts +3 -103
  42. package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
  43. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
  44. package/src/__tests__/intent-routing.test.ts +0 -13
  45. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
  46. package/src/__tests__/keychain-broker-client.test.ts +161 -22
  47. package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
  48. package/src/__tests__/memory-regressions.test.ts +8 -30
  49. package/src/__tests__/migration-export-http.test.ts +2 -2
  50. package/src/__tests__/migration-import-commit-http.test.ts +2 -2
  51. package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
  52. package/src/__tests__/migration-validate-http.test.ts +2 -2
  53. package/src/__tests__/non-member-access-request.test.ts +0 -5
  54. package/src/__tests__/notification-decision-fallback.test.ts +4 -0
  55. package/src/__tests__/notification-decision-identity.test.ts +4 -0
  56. package/src/__tests__/permission-types.test.ts +1 -0
  57. package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
  58. package/src/__tests__/qdrant-manager.test.ts +28 -2
  59. package/src/__tests__/registry.test.ts +0 -6
  60. package/src/__tests__/require-fresh-approval.test.ts +4 -0
  61. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
  62. package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
  63. package/src/__tests__/secure-keys.test.ts +83 -263
  64. package/src/__tests__/shell-identity.test.ts +96 -6
  65. package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
  66. package/src/__tests__/skill-feature-flags.test.ts +46 -45
  67. package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
  68. package/src/__tests__/skill-load-inline-command.test.ts +8 -12
  69. package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
  70. package/src/__tests__/skill-load-tool.test.ts +0 -2
  71. package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
  72. package/src/__tests__/skills.test.ts +0 -2
  73. package/src/__tests__/slack-inbound-verification.test.ts +0 -4
  74. package/src/__tests__/suggestion-routes.test.ts +1 -32
  75. package/src/__tests__/system-prompt.test.ts +0 -1
  76. package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -0
  77. package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
  78. package/src/__tests__/tool-executor.test.ts +4 -0
  79. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
  80. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
  81. package/src/__tests__/update-bulletin.test.ts +0 -2
  82. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
  83. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -6
  84. package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
  85. package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +218 -0
  86. package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
  87. package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
  88. package/src/calls/audio-store.test.ts +97 -0
  89. package/src/calls/audio-store.ts +205 -0
  90. package/src/calls/call-controller.ts +85 -7
  91. package/src/calls/call-domain.ts +3 -0
  92. package/src/calls/call-store.ts +10 -3
  93. package/src/calls/fish-audio-client.ts +117 -0
  94. package/src/calls/relay-server.ts +27 -0
  95. package/src/calls/twilio-routes.ts +2 -1
  96. package/src/calls/types.ts +1 -0
  97. package/src/calls/voice-ingress-preflight.ts +0 -42
  98. package/src/calls/voice-quality.ts +26 -5
  99. package/src/calls/voice-session-bridge.ts +6 -12
  100. package/src/cli/commands/config.ts +1 -4
  101. package/src/cli/commands/conversations.ts +0 -18
  102. package/src/cli/commands/credentials.ts +34 -4
  103. package/src/cli/commands/oauth/index.ts +7 -0
  104. package/src/cli/commands/oauth/platform.ts +179 -0
  105. package/src/cli/commands/platform.ts +3 -3
  106. package/src/config/assistant-feature-flags.ts +186 -5
  107. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  108. package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
  109. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  110. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
  111. package/src/config/bundled-tool-registry.ts +1 -11
  112. package/src/config/env-registry.ts +1 -1
  113. package/src/config/env.ts +16 -16
  114. package/src/config/feature-flag-registry.json +48 -16
  115. package/src/config/loader.ts +98 -31
  116. package/src/config/schema.ts +4 -25
  117. package/src/config/schemas/calls.ts +13 -0
  118. package/src/config/schemas/fish-audio.ts +39 -0
  119. package/src/config/schemas/memory.ts +0 -4
  120. package/src/config/schemas/platform.ts +1 -1
  121. package/src/config/schemas/security.ts +4 -4
  122. package/src/config/types.ts +0 -1
  123. package/src/contacts/contact-store.ts +39 -0
  124. package/src/contacts/types.ts +2 -0
  125. package/src/context/window-manager.ts +53 -2
  126. package/src/credential-execution/approval-bridge.ts +1 -0
  127. package/src/credential-execution/executable-discovery.ts +28 -4
  128. package/src/credential-execution/feature-gates.ts +16 -0
  129. package/src/credential-execution/process-manager.ts +38 -0
  130. package/src/daemon/assistant-attachments.ts +9 -0
  131. package/src/daemon/config-watcher.ts +6 -4
  132. package/src/daemon/conversation-agent-loop.ts +0 -60
  133. package/src/daemon/conversation-memory.ts +0 -117
  134. package/src/daemon/conversation-runtime-assembly.ts +0 -2
  135. package/src/daemon/conversation-tool-setup.ts +0 -105
  136. package/src/daemon/conversation.ts +10 -1
  137. package/src/daemon/handlers/config-vercel.ts +92 -0
  138. package/src/daemon/handlers/conversations.ts +0 -11
  139. package/src/daemon/handlers/skills.ts +2 -15
  140. package/src/daemon/install-symlink.ts +195 -0
  141. package/src/daemon/lifecycle.ts +229 -96
  142. package/src/daemon/message-types/conversations.ts +3 -4
  143. package/src/daemon/message-types/diagnostics.ts +3 -22
  144. package/src/daemon/message-types/messages.ts +0 -2
  145. package/src/daemon/message-types/upgrades.ts +8 -0
  146. package/src/daemon/server.ts +30 -92
  147. package/src/events/domain-events.ts +2 -1
  148. package/src/followups/followup-store.ts +5 -2
  149. package/src/inbound/platform-callback-registration.ts +3 -3
  150. package/src/instrument.ts +8 -5
  151. package/src/memory/conversation-crud.ts +0 -236
  152. package/src/memory/conversation-title-service.ts +76 -11
  153. package/src/memory/db-init.ts +15 -11
  154. package/src/memory/indexer.ts +15 -106
  155. package/src/memory/items-extractor.ts +15 -1
  156. package/src/memory/job-handlers/conversation-starters.ts +4 -1
  157. package/src/memory/job-handlers/embedding.ts +0 -79
  158. package/src/memory/job-utils.ts +1 -1
  159. package/src/memory/jobs-store.ts +30 -13
  160. package/src/memory/jobs-worker.ts +31 -27
  161. package/src/memory/migrations/001-job-deferrals.ts +19 -0
  162. package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
  163. package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
  164. package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
  165. package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
  166. package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
  167. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
  168. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
  169. package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
  170. package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
  171. package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
  172. package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
  173. package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
  174. package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
  175. package/src/memory/migrations/116-messages-fts.ts +106 -1
  176. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
  177. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
  178. package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
  179. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
  180. package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
  181. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
  182. package/src/memory/migrations/141-rename-verification-table.ts +54 -0
  183. package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
  184. package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
  185. package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
  186. package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
  187. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
  188. package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
  189. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
  190. package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
  191. package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
  192. package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
  193. package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
  194. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
  195. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
  196. package/src/memory/migrations/189-drop-simplified-memory.ts +42 -0
  197. package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
  198. package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
  199. package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
  200. package/src/memory/migrations/index.ts +5 -3
  201. package/src/memory/migrations/registry.ts +90 -0
  202. package/src/memory/migrations/validate-migration-state.ts +137 -11
  203. package/src/memory/qdrant-circuit-breaker.ts +9 -0
  204. package/src/memory/qdrant-client.ts +4 -6
  205. package/src/memory/qdrant-manager.ts +64 -7
  206. package/src/memory/schema/calls.ts +1 -0
  207. package/src/memory/schema/contacts.ts +1 -0
  208. package/src/memory/schema/conversations.ts +0 -3
  209. package/src/memory/schema/index.ts +0 -2
  210. package/src/messaging/draft-store.ts +2 -2
  211. package/src/notifications/decision-engine.ts +4 -1
  212. package/src/oauth/connection-resolver.ts +6 -4
  213. package/src/permissions/checker.ts +0 -38
  214. package/src/permissions/defaults.ts +3 -3
  215. package/src/permissions/shell-identity.ts +76 -22
  216. package/src/permissions/trust-client.ts +2 -13
  217. package/src/permissions/trust-store.ts +8 -3
  218. package/src/permissions/types.ts +4 -2
  219. package/src/platform/client.ts +35 -7
  220. package/src/prompts/persona-resolver.ts +138 -0
  221. package/src/prompts/system-prompt.ts +36 -4
  222. package/src/prompts/templates/users/default.md +1 -0
  223. package/src/providers/registry.ts +27 -40
  224. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
  225. package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
  226. package/src/runtime/auth/external-assistant-id.ts +13 -59
  227. package/src/runtime/auth/route-policy.ts +29 -1
  228. package/src/runtime/auth/token-service.ts +53 -15
  229. package/src/runtime/channel-readiness-service.ts +1 -16
  230. package/src/runtime/http-server.ts +29 -2
  231. package/src/runtime/middleware/error-handler.ts +1 -9
  232. package/src/runtime/routes/audio-routes.ts +40 -0
  233. package/src/runtime/routes/btw-routes.ts +0 -17
  234. package/src/runtime/routes/conversation-management-routes.ts +0 -36
  235. package/src/runtime/routes/conversation-query-routes.ts +106 -2
  236. package/src/runtime/routes/conversation-routes.ts +4 -43
  237. package/src/runtime/routes/diagnostics-routes.ts +1 -477
  238. package/src/runtime/routes/identity-routes.ts +18 -29
  239. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
  240. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
  241. package/src/runtime/routes/integrations/vercel.ts +89 -0
  242. package/src/runtime/routes/log-export-routes.ts +5 -0
  243. package/src/runtime/routes/memory-item-routes.test.ts +221 -3
  244. package/src/runtime/routes/memory-item-routes.ts +144 -4
  245. package/src/runtime/routes/migration-rollback-routes.ts +209 -0
  246. package/src/runtime/routes/migration-routes.ts +17 -1
  247. package/src/runtime/routes/notification-routes.ts +58 -0
  248. package/src/runtime/routes/schedule-routes.ts +65 -0
  249. package/src/runtime/routes/settings-routes.ts +41 -1
  250. package/src/runtime/routes/tts-routes.ts +86 -0
  251. package/src/runtime/routes/upgrade-broadcast-routes.ts +175 -0
  252. package/src/runtime/routes/workspace-commit-routes.ts +62 -0
  253. package/src/runtime/routes/workspace-routes.test.ts +22 -1
  254. package/src/runtime/routes/workspace-routes.ts +1 -1
  255. package/src/runtime/routes/workspace-utils.ts +86 -2
  256. package/src/schedule/schedule-store.ts +0 -21
  257. package/src/security/ces-credential-client.ts +59 -22
  258. package/src/security/ces-rpc-credential-backend.ts +85 -0
  259. package/src/security/credential-backend.ts +12 -88
  260. package/src/security/keychain-broker-client.ts +10 -2
  261. package/src/security/secure-keys.ts +94 -113
  262. package/src/skills/catalog-install.ts +13 -7
  263. package/src/skills/inline-command-render.ts +5 -1
  264. package/src/skills/inline-command-runner.ts +30 -2
  265. package/src/telemetry/usage-telemetry-reporter.ts +4 -2
  266. package/src/tools/calls/call-start.ts +1 -0
  267. package/src/tools/executor.ts +0 -4
  268. package/src/tools/memory/handlers.ts +1 -129
  269. package/src/tools/network/script-proxy/session-manager.ts +19 -4
  270. package/src/tools/network/web-fetch.ts +3 -1
  271. package/src/tools/permission-checker.ts +18 -0
  272. package/src/tools/skills/execute.ts +1 -1
  273. package/src/tools/skills/load.ts +9 -2
  274. package/src/tools/types.ts +0 -8
  275. package/src/util/errors.ts +0 -12
  276. package/src/util/platform.ts +8 -55
  277. package/src/util/xml.ts +8 -0
  278. package/src/workspace/git-service.ts +5 -2
  279. package/src/workspace/heartbeat-service.ts +5 -24
  280. package/src/workspace/migrations/001-avatar-rename.ts +15 -0
  281. package/src/workspace/migrations/003-seed-device-id.ts +17 -1
  282. package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
  283. package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
  284. package/src/workspace/migrations/006-services-config.ts +49 -0
  285. package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
  286. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
  287. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
  288. package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
  289. package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
  290. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
  291. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
  292. package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
  293. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
  294. package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
  295. package/src/workspace/migrations/017-seed-persona-dirs.ts +95 -0
  296. package/src/workspace/migrations/migrate-to-workspace-volume.ts +23 -1
  297. package/src/workspace/migrations/registry.ts +8 -0
  298. package/src/workspace/migrations/runner.ts +106 -2
  299. package/src/workspace/migrations/types.ts +4 -0
  300. package/src/__tests__/archive-recall.test.ts +0 -560
  301. package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
  302. package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
  303. package/src/__tests__/conversation-memory-dirty-tail.test.ts +0 -150
  304. package/src/__tests__/conversation-switch-memory-reduction.test.ts +0 -474
  305. package/src/__tests__/db-memory-archive-migration.test.ts +0 -372
  306. package/src/__tests__/db-memory-brief-state-migration.test.ts +0 -213
  307. package/src/__tests__/db-memory-reducer-checkpoints.test.ts +0 -273
  308. package/src/__tests__/diagnostics-export.test.ts +0 -288
  309. package/src/__tests__/local-gateway-health.test.ts +0 -209
  310. package/src/__tests__/memory-brief-open-loops.test.ts +0 -530
  311. package/src/__tests__/memory-brief-time.test.ts +0 -285
  312. package/src/__tests__/memory-brief-wrapper.test.ts +0 -311
  313. package/src/__tests__/memory-chunk-archive.test.ts +0 -400
  314. package/src/__tests__/memory-chunk-dual-write.test.ts +0 -453
  315. package/src/__tests__/memory-episode-archive.test.ts +0 -370
  316. package/src/__tests__/memory-episode-dual-write.test.ts +0 -626
  317. package/src/__tests__/memory-observation-archive.test.ts +0 -375
  318. package/src/__tests__/memory-observation-dual-write.test.ts +0 -318
  319. package/src/__tests__/memory-reducer-job.test.ts +0 -538
  320. package/src/__tests__/memory-reducer-scheduling.test.ts +0 -473
  321. package/src/__tests__/memory-reducer-store.test.ts +0 -728
  322. package/src/__tests__/memory-reducer-types.test.ts +0 -707
  323. package/src/__tests__/memory-reducer.test.ts +0 -704
  324. package/src/__tests__/memory-simplified-config.test.ts +0 -281
  325. package/src/__tests__/secret-ingress-handler.test.ts +0 -120
  326. package/src/__tests__/simplified-memory-e2e.test.ts +0 -666
  327. package/src/__tests__/simplified-memory-runtime.test.ts +0 -616
  328. package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
  329. package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
  330. package/src/__tests__/swarm-orchestrator.test.ts +0 -463
  331. package/src/__tests__/swarm-plan-validator.test.ts +0 -384
  332. package/src/__tests__/swarm-recursion.test.ts +0 -197
  333. package/src/__tests__/swarm-router-planner.test.ts +0 -234
  334. package/src/__tests__/swarm-tool.test.ts +0 -185
  335. package/src/__tests__/swarm-worker-backend.test.ts +0 -144
  336. package/src/__tests__/swarm-worker-runner.test.ts +0 -288
  337. package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
  338. package/src/commands/cc-command-registry.ts +0 -248
  339. package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
  340. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
  341. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
  342. package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
  343. package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
  344. package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
  345. package/src/config/schemas/memory-simplified.ts +0 -101
  346. package/src/config/schemas/swarm.ts +0 -82
  347. package/src/logfire.ts +0 -135
  348. package/src/memory/archive-recall.ts +0 -516
  349. package/src/memory/archive-store.ts +0 -400
  350. package/src/memory/brief-formatting.ts +0 -33
  351. package/src/memory/brief-open-loops.ts +0 -266
  352. package/src/memory/brief-time.ts +0 -162
  353. package/src/memory/brief.ts +0 -75
  354. package/src/memory/job-handlers/backfill-simplified-memory.ts +0 -462
  355. package/src/memory/job-handlers/reduce-conversation-memory.ts +0 -229
  356. package/src/memory/migrations/185-memory-brief-state.ts +0 -52
  357. package/src/memory/migrations/186-memory-archive.ts +0 -109
  358. package/src/memory/migrations/187-memory-reducer-checkpoints.ts +0 -19
  359. package/src/memory/reducer-scheduler.ts +0 -242
  360. package/src/memory/reducer-store.ts +0 -271
  361. package/src/memory/reducer-types.ts +0 -106
  362. package/src/memory/reducer.ts +0 -467
  363. package/src/memory/schema/memory-archive.ts +0 -121
  364. package/src/memory/schema/memory-brief.ts +0 -55
  365. package/src/runtime/local-gateway-health.ts +0 -275
  366. package/src/security/secret-ingress.ts +0 -68
  367. package/src/swarm/backend-claude-code.ts +0 -225
  368. package/src/swarm/checkpoint.ts +0 -137
  369. package/src/swarm/graph-utils.ts +0 -53
  370. package/src/swarm/index.ts +0 -55
  371. package/src/swarm/limits.ts +0 -66
  372. package/src/swarm/orchestrator.ts +0 -424
  373. package/src/swarm/plan-validator.ts +0 -117
  374. package/src/swarm/router-planner.ts +0 -162
  375. package/src/swarm/router-prompts.ts +0 -39
  376. package/src/swarm/synthesizer.ts +0 -81
  377. package/src/swarm/types.ts +0 -72
  378. package/src/swarm/worker-backend.ts +0 -131
  379. package/src/swarm/worker-prompts.ts +0 -80
  380. package/src/swarm/worker-runner.ts +0 -170
  381. package/src/tools/claude-code/claude-code.ts +0 -610
  382. package/src/tools/swarm/delegate.ts +0 -205
@@ -41,6 +41,10 @@ const parseCallSession = createRowMapper<
41
41
  inviteGuardianName: "inviteGuardianName",
42
42
  callerIdentityMode: "callerIdentityMode",
43
43
  callerIdentitySource: "callerIdentitySource",
44
+ skipDisclosure: {
45
+ from: "skipDisclosure",
46
+ transform: (v: unknown) => v === 1,
47
+ },
44
48
  initiatedFromConversationId: "initiatedFromConversationId",
45
49
  startedAt: "startedAt",
46
50
  endedAt: "endedAt",
@@ -87,11 +91,13 @@ export function createCallSession(opts: {
87
91
  inviteGuardianName?: string;
88
92
  callerIdentityMode?: string;
89
93
  callerIdentitySource?: string;
94
+ skipDisclosure?: boolean;
90
95
  initiatedFromConversationId?: string;
91
96
  }): CallSession {
92
97
  const db = getDb();
93
98
  const now = Date.now();
94
- const session = {
99
+ const skipDisclosure = opts.skipDisclosure ?? false;
100
+ const row = {
95
101
  id: uuid(),
96
102
  conversationId: opts.conversationId,
97
103
  provider: opts.provider,
@@ -106,6 +112,7 @@ export function createCallSession(opts: {
106
112
  inviteGuardianName: opts.inviteGuardianName ?? null,
107
113
  callerIdentityMode: opts.callerIdentityMode ?? null,
108
114
  callerIdentitySource: opts.callerIdentitySource ?? null,
115
+ skipDisclosure: skipDisclosure ? 1 : 0,
109
116
  initiatedFromConversationId: opts.initiatedFromConversationId ?? null,
110
117
  startedAt: null,
111
118
  endedAt: null,
@@ -113,8 +120,8 @@ export function createCallSession(opts: {
113
120
  createdAt: now,
114
121
  updatedAt: now,
115
122
  };
116
- db.insert(callSessions).values(session).run();
117
- return session;
123
+ db.insert(callSessions).values(row).run();
124
+ return { ...row, skipDisclosure };
118
125
  }
119
126
 
120
127
  export function getCallSession(id: string): CallSession | null {
@@ -0,0 +1,117 @@
1
+ import type { FishAudioConfig } from "../config/schemas/fish-audio.js";
2
+ import { credentialKey } from "../security/credential-key.js";
3
+ import { getSecureKeyAsync } from "../security/secure-keys.js";
4
+ import { getLogger } from "../util/logger.js";
5
+
6
+ const log = getLogger("fish-audio-client");
7
+
8
+ /** Timeout waiting for the first chunk from Fish Audio (ms). */
9
+ const FIRST_CHUNK_TIMEOUT_MS = 10_000;
10
+
11
+ /** Timeout waiting between consecutive chunks (ms). */
12
+ const IDLE_TIMEOUT_MS = 5_000;
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Fish Audio REST API (POST /v1/tts)
16
+ // ---------------------------------------------------------------------------
17
+
18
+ interface SynthesizeOptions {
19
+ onChunk?: (chunk: Uint8Array) => void;
20
+ signal?: AbortSignal;
21
+ }
22
+
23
+ /**
24
+ * Synthesize text to audio using the Fish Audio REST API with the s2-pro
25
+ * model. Streams audio chunks via the optional `onChunk` callback as they
26
+ * arrive from the server's chunked transfer-encoded response. Returns the
27
+ * complete audio buffer when the response finishes.
28
+ *
29
+ * Pass an `AbortSignal` to cancel in-flight synthesis (e.g. on barge-in).
30
+ */
31
+ export async function synthesizeWithFishAudio(
32
+ text: string,
33
+ config: FishAudioConfig,
34
+ options?: SynthesizeOptions,
35
+ ): Promise<Buffer> {
36
+ const apiKey = await getSecureKeyAsync(
37
+ credentialKey("fish-audio", "api_key"),
38
+ );
39
+ if (!apiKey) {
40
+ throw new Error(
41
+ "Fish Audio API key not configured. Store it via: assistant credentials set --service fish-audio --field api_key <key>",
42
+ );
43
+ }
44
+
45
+ const body = {
46
+ text,
47
+ reference_id: config.referenceId || undefined,
48
+ model: "s2-pro",
49
+ format: config.format,
50
+ mp3_bitrate: 192,
51
+ chunk_length: config.chunkLength,
52
+ normalize: true,
53
+ latency: config.latency,
54
+ temperature: 1.0,
55
+ prosody: config.speed !== 1.0 ? { speed: config.speed } : undefined,
56
+ };
57
+
58
+ log.info(
59
+ {
60
+ referenceId: config.referenceId,
61
+ format: config.format,
62
+ textLength: text.length,
63
+ },
64
+ "Starting Fish Audio synthesis",
65
+ );
66
+
67
+ const response = await fetch("https://api.fish.audio/v1/tts", {
68
+ method: "POST",
69
+ headers: {
70
+ Authorization: `Bearer ${apiKey}`,
71
+ "Content-Type": "application/json",
72
+ },
73
+ body: JSON.stringify(body),
74
+ signal: options?.signal,
75
+ });
76
+
77
+ if (!response.ok) {
78
+ const errorText = await response.text();
79
+ throw new Error(`Fish Audio API error (${response.status}): ${errorText}`);
80
+ }
81
+
82
+ if (!response.body) {
83
+ throw new Error("Fish Audio API returned no body");
84
+ }
85
+
86
+ const chunks: Uint8Array[] = [];
87
+ const reader = response.body.getReader();
88
+ let isFirstChunk = true;
89
+
90
+ while (true) {
91
+ const timeoutMs = isFirstChunk ? FIRST_CHUNK_TIMEOUT_MS : IDLE_TIMEOUT_MS;
92
+ const timeout = new Promise<never>((_, reject) =>
93
+ setTimeout(
94
+ () => reject(new Error(`Fish Audio read timed out after ${timeoutMs}ms`)),
95
+ timeoutMs,
96
+ ),
97
+ );
98
+ const { done, value } = await Promise.race([reader.read(), timeout]);
99
+ if (done) break;
100
+ if (value) {
101
+ isFirstChunk = false;
102
+ chunks.push(value);
103
+ options?.onChunk?.(value);
104
+ }
105
+ }
106
+
107
+ const totalLength = chunks.reduce((sum, c) => sum + c.byteLength, 0);
108
+ const merged = new Uint8Array(totalLength);
109
+ let offset = 0;
110
+ for (const chunk of chunks) {
111
+ merged.set(chunk, offset);
112
+ offset += chunk.byteLength;
113
+ }
114
+
115
+ log.debug({ bytes: totalLength }, "Fish Audio synthesis complete");
116
+ return Buffer.from(merged);
117
+ }
@@ -139,6 +139,12 @@ export interface RelayEndMessage {
139
139
  handoffData?: string;
140
140
  }
141
141
 
142
+ export interface RelayPlayMessage {
143
+ type: "play";
144
+ source: string;
145
+ interruptible: boolean;
146
+ }
147
+
142
148
  // ── WebSocket data type ──────────────────────────────────────────────
143
149
 
144
150
  export interface RelayWebSocketData {
@@ -323,6 +329,27 @@ export class RelayConnection {
323
329
  }
324
330
  }
325
331
 
332
+ /**
333
+ * Send a play-audio URL to the caller. Used when the assistant handles
334
+ * TTS synthesis itself (e.g. Fish Audio) instead of relying on
335
+ * ConversationRelay's built-in TTS.
336
+ */
337
+ sendPlayUrl(url: string): void {
338
+ const message: RelayPlayMessage = {
339
+ type: "play",
340
+ source: url,
341
+ interruptible: true,
342
+ };
343
+ try {
344
+ this.ws.send(JSON.stringify(message));
345
+ } catch (err) {
346
+ log.error(
347
+ { err, callSessionId: this.callSessionId },
348
+ "Failed to send play URL",
349
+ );
350
+ }
351
+ }
352
+
326
353
  /**
327
354
  * End the ConversationRelay session.
328
355
  */
@@ -49,6 +49,7 @@ export function generateTwiML(
49
49
  profile: {
50
50
  language: string;
51
51
  transcriptionProvider: string;
52
+ speechModel?: string;
52
53
  ttsProvider: string;
53
54
  voice: string;
54
55
  },
@@ -91,7 +92,7 @@ export function generateTwiML(
91
92
  ${greetingAttr}
92
93
  voice="${escapeXml(profile.voice)}"
93
94
  language="${escapeXml(profile.language)}"
94
- transcriptionProvider="${escapeXml(profile.transcriptionProvider)}"
95
+ transcriptionProvider="${escapeXml(profile.transcriptionProvider)}"${profile.speechModel ? `\n speechModel="${escapeXml(profile.speechModel)}"` : ""}
95
96
  ttsProvider="${escapeXml(profile.ttsProvider)}"
96
97
  interruptible="true"
97
98
  dtmfDetection="true"
@@ -75,6 +75,7 @@ export interface CallSession {
75
75
  inviteGuardianName: string | null;
76
76
  callerIdentityMode: string | null;
77
77
  callerIdentitySource: string | null;
78
+ skipDisclosure: boolean;
78
79
  initiatedFromConversationId?: string | null;
79
80
  startedAt: number | null;
80
81
  endedAt: number | null;
@@ -29,18 +29,6 @@ function fail(error: string): VoiceIngressPreflightFailure {
29
29
  };
30
30
  }
31
31
 
32
- function buildGatewayUnhealthyMessage(
33
- target: string,
34
- error: string | undefined,
35
- afterRecoveryAttempt: boolean,
36
- ): string {
37
- const detail = error ?? "Unknown gateway health check failure";
38
- if (afterRecoveryAttempt) {
39
- return `Voice callback gateway is still unhealthy at ${target} after a local recovery attempt: ${detail}`;
40
- }
41
- return `Voice callback gateway is unhealthy at ${target}: ${detail}`;
42
- }
43
-
44
32
  export async function preflightVoiceIngress(): Promise<VoiceIngressPreflightResult> {
45
33
  const ingressConfig = loadConfig();
46
34
 
@@ -65,36 +53,6 @@ export async function preflightVoiceIngress(): Promise<VoiceIngressPreflightResu
65
53
  );
66
54
  }
67
55
 
68
- const { ensureLocalGatewayReady, probeLocalGatewayHealth } =
69
- await import("../runtime/local-gateway-health.js");
70
-
71
- const initialHealth = await probeLocalGatewayHealth();
72
- if (!initialHealth.healthy && !initialHealth.localDeployment) {
73
- return fail(
74
- buildGatewayUnhealthyMessage(
75
- initialHealth.target,
76
- initialHealth.error,
77
- false,
78
- ),
79
- );
80
- }
81
-
82
- if (initialHealth.localDeployment) {
83
- const recovery = await ensureLocalGatewayReady();
84
- // Re-probe after the wake flow so the dial path only continues when the
85
- // current gateway process is demonstrably serving the callback stack.
86
- const confirmedHealth = await probeLocalGatewayHealth();
87
- if (!confirmedHealth.healthy) {
88
- return fail(
89
- buildGatewayUnhealthyMessage(
90
- confirmedHealth.target,
91
- confirmedHealth.error ?? recovery.error,
92
- recovery.recoveryAttempted,
93
- ),
94
- );
95
- }
96
- }
97
-
98
56
  return {
99
57
  ok: true,
100
58
  ingressConfig: {
@@ -3,6 +3,7 @@ import { loadConfig } from "../config/loader.js";
3
3
  export interface VoiceQualityProfile {
4
4
  language: string;
5
5
  transcriptionProvider: string;
6
+ speechModel?: string;
6
7
  ttsProvider: string;
7
8
  voice: string;
8
9
  }
@@ -42,19 +43,39 @@ export function buildElevenLabsVoiceSpec(config: {
42
43
  /**
43
44
  * Resolve the effective voice quality profile from config.
44
45
  *
45
- * Always uses ElevenLabs TTS via Twilio ConversationRelay.
46
- * The voice ID comes from the shared `elevenlabs.voiceId` config
47
- * (defaults to Amelia ZF6FPAbjXT4488VcRRnw).
46
+ * Supports ElevenLabs (default) and Fish Audio TTS providers.
47
+ * When Fish Audio is selected, `ttsProvider` is set to `"Google"` as a
48
+ * placeholder ConversationRelay requires a valid provider in TwiML, but
49
+ * actual audio is delivered via `play` messages from the call-controller.
50
+ * The voice string is left empty since it is unused in that mode.
51
+ *
52
+ * For ElevenLabs, the voice ID comes from the shared `elevenlabs.voiceId`
53
+ * config (defaults to Amelia — ZF6FPAbjXT4488VcRRnw).
48
54
  */
49
55
  export function resolveVoiceQualityProfile(
50
56
  config?: ReturnType<typeof loadConfig>,
51
57
  ): VoiceQualityProfile {
52
58
  const cfg = config ?? loadConfig();
53
59
  const voice = cfg.calls.voice;
60
+ const configuredTts = voice.ttsProvider ?? "elevenlabs";
61
+ const fishAudio = configuredTts === "fish-audio";
54
62
  return {
55
63
  language: voice.language,
56
64
  transcriptionProvider: voice.transcriptionProvider,
57
- ttsProvider: "ElevenLabs",
58
- voice: buildElevenLabsVoiceSpec(cfg.elevenlabs),
65
+ speechModel:
66
+ voice.speechModel ??
67
+ (voice.transcriptionProvider === "Google" ? undefined : "nova-3"),
68
+ ttsProvider: fishAudio ? "Google" : "ElevenLabs",
69
+ voice: fishAudio ? "" : buildElevenLabsVoiceSpec(cfg.elevenlabs),
59
70
  };
60
71
  }
72
+
73
+ /**
74
+ * Check whether Fish Audio TTS is configured for phone calls.
75
+ */
76
+ export function isFishAudioTts(
77
+ config?: ReturnType<typeof loadConfig>,
78
+ ): boolean {
79
+ const cfg = config ?? loadConfig();
80
+ return cfg.calls.voice?.ttsProvider === "fish-audio";
81
+ }
@@ -20,9 +20,7 @@ import type { ServerMessage } from "../daemon/message-protocol.js";
20
20
  import { buildAssistantEvent } from "../runtime/assistant-event.js";
21
21
  import { assistantEventHub } from "../runtime/assistant-event-hub.js";
22
22
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
23
- import { checkIngressForSecrets } from "../security/secret-ingress.js";
24
23
  import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
25
- import { IngressBlockedError } from "../util/errors.js";
26
24
  import { getLogger } from "../util/logger.js";
27
25
  import {
28
26
  CALL_OPENING_MARKER,
@@ -95,6 +93,8 @@ export interface VoiceTurnOptions {
95
93
  isInbound: boolean;
96
94
  /** The outbound call task, if any. */
97
95
  task?: string | null;
96
+ /** When true, skip the disclosure announcement for this call. */
97
+ skipDisclosure?: boolean;
98
98
  /** Called for each streaming text token from the agent loop. */
99
99
  onTextDelta: (text: string) => void;
100
100
  /** Called when the agent loop completes a full response. */
@@ -128,9 +128,11 @@ function buildVoiceCallControlPrompt(opts: {
128
128
  isInbound: boolean;
129
129
  task?: string | null;
130
130
  isCallerGuardian?: boolean;
131
+ skipDisclosure?: boolean;
131
132
  }): string {
132
133
  const config = getConfig();
133
- const disclosureEnabled = config.calls?.disclosure?.enabled === true;
134
+ const disclosureEnabled =
135
+ config.calls?.disclosure?.enabled === true && !opts.skipDisclosure;
134
136
  const disclosureText = config.calls?.disclosure?.text?.trim();
135
137
  const disclosureRule =
136
138
  disclosureEnabled && disclosureText
@@ -240,15 +242,6 @@ export async function startVoiceTurn(
240
242
  );
241
243
  }
242
244
 
243
- // Block inbound content that contains secrets
244
- const ingressCheck = checkIngressForSecrets(opts.content);
245
- if (ingressCheck.blocked) {
246
- throw new IngressBlockedError(
247
- ingressCheck.userNotice!,
248
- ingressCheck.detectedTypes,
249
- );
250
- }
251
-
252
245
  const eventSink: VoiceRunEventSink = {
253
246
  onTextDelta: opts.onTextDelta,
254
247
  onMessageComplete: opts.onComplete,
@@ -286,6 +279,7 @@ export async function startVoiceTurn(
286
279
  isInbound: opts.isInbound,
287
280
  task: opts.task,
288
281
  isCallerGuardian,
282
+ skipDisclosure: opts.skipDisclosure,
289
283
  });
290
284
 
291
285
  // Get or create the conversation
@@ -6,7 +6,6 @@ import {
6
6
  loadRawConfig,
7
7
  saveRawConfig,
8
8
  setNestedValue,
9
- syncConfigToLockfile,
10
9
  } from "../../config/loader.js";
11
10
  import { AssistantConfigSchema } from "../../config/schema.js";
12
11
  import { getSchemaAtPath } from "../../config/schema-utils.js";
@@ -73,8 +72,7 @@ Arguments:
73
72
  true, "42" becomes number 42). Falls back to plain string if JSON
74
73
  parsing fails.
75
74
 
76
- After writing the value to config.json, the lockfile is automatically synced
77
- to reflect the updated configuration.
75
+ After writing the value to config.json, the change takes effect immediately.
78
76
 
79
77
  To manage API keys, use "assistant keys set <provider> <key>" instead.
80
78
 
@@ -93,7 +91,6 @@ Examples:
93
91
  }
94
92
  setNestedValue(raw, key, parsed);
95
93
  saveRawConfig(raw);
96
- syncConfigToLockfile();
97
94
  log.info(`Set ${key} = ${JSON.stringify(parsed)}`);
98
95
  });
99
96
 
@@ -375,24 +375,6 @@ Examples:
375
375
  targetId: summaryId,
376
376
  });
377
377
  }
378
- for (const obsId of result.deletedObservationIds) {
379
- enqueueMemoryJob("delete_qdrant_vectors", {
380
- targetType: "observation",
381
- targetId: obsId,
382
- });
383
- }
384
- for (const chunkId of result.deletedChunkIds) {
385
- enqueueMemoryJob("delete_qdrant_vectors", {
386
- targetType: "chunk",
387
- targetId: chunkId,
388
- });
389
- }
390
- for (const episodeId of result.deletedEpisodeIds) {
391
- enqueueMemoryJob("delete_qdrant_vectors", {
392
- targetType: "episode",
393
- targetId: episodeId,
394
- });
395
- }
396
378
 
397
379
  log.info(
398
380
  `Wiped conversation "${conversation.title ?? "Untitled"}". ` +
@@ -15,6 +15,7 @@ import { credentialKey } from "../../security/credential-key.js";
15
15
  import {
16
16
  deleteSecureKeyAsync,
17
17
  getSecureKeyAsync,
18
+ getSecureKeyResultAsync,
18
19
  setSecureKeyAsync,
19
20
  } from "../../security/secure-keys.js";
20
21
  import {
@@ -608,10 +609,19 @@ Examples:
608
609
  return;
609
610
  }
610
611
 
611
- const secret = await getSecureKeyAsync(storageKey);
612
+ const { value: secret, unreachable } =
613
+ await getSecureKeyResultAsync(storageKey);
612
614
 
613
615
  if (!metadata && (secret == null || secret.length === 0)) {
614
- writeOutput(cmd, { ok: false, error: "Credential not found" });
616
+ if (unreachable) {
617
+ writeOutput(cmd, {
618
+ ok: false,
619
+ error:
620
+ "Keychain broker is unreachable — restart the Vellum app and accept the macOS Keychain prompt",
621
+ });
622
+ } else {
623
+ writeOutput(cmd, { ok: false, error: "Credential not found" });
624
+ }
615
625
  process.exitCode = 1;
616
626
  return;
617
627
  }
@@ -646,10 +656,21 @@ Examples:
646
656
 
647
657
  const connection = safeGetConnectionByProvider(metadata.service);
648
658
  const output = buildCredentialOutput(metadata, secret, connection);
659
+
660
+ if (unreachable && (secret == null || secret.length === 0)) {
661
+ output.scrubbedValue = "(broker unreachable)";
662
+ output.brokerUnreachable = true;
663
+ }
664
+
649
665
  writeOutput(cmd, output);
650
666
 
651
667
  if (!shouldOutputJson(cmd)) {
652
668
  printCredentialHuman(output);
669
+ if (unreachable && (secret == null || secret.length === 0)) {
670
+ log.info(
671
+ " \u26A0 Keychain broker unreachable — restart the Vellum app and accept the macOS Keychain prompt to access credentials",
672
+ );
673
+ }
653
674
  }
654
675
  } catch (err) {
655
676
  const message = err instanceof Error ? err.message : String(err);
@@ -725,10 +746,19 @@ Examples:
725
746
  return;
726
747
  }
727
748
 
728
- const secret = await getSecureKeyAsync(storageKey);
749
+ const { value: secret, unreachable } =
750
+ await getSecureKeyResultAsync(storageKey);
729
751
 
730
752
  if (secret == null || secret.length === 0) {
731
- writeOutput(cmd, { ok: false, error: "Credential not found" });
753
+ if (unreachable) {
754
+ writeOutput(cmd, {
755
+ ok: false,
756
+ error:
757
+ "Keychain broker is unreachable — restart the Vellum app and accept the macOS Keychain prompt",
758
+ });
759
+ } else {
760
+ writeOutput(cmd, { ok: false, error: "Credential not found" });
761
+ }
732
762
  process.exitCode = 1;
733
763
  return;
734
764
  }
@@ -2,6 +2,7 @@ import type { Command } from "commander";
2
2
 
3
3
  import { registerAppCommands } from "./apps.js";
4
4
  import { registerConnectionCommands } from "./connections.js";
5
+ import { registerPlatformCommands } from "./platform.js";
5
6
  import { registerProviderCommands } from "./providers.js";
6
7
 
7
8
  export function registerOAuthCommand(program: Command): void {
@@ -49,4 +50,10 @@ Examples:
49
50
  // ---------------------------------------------------------------------------
50
51
 
51
52
  registerConnectionCommands(oauth);
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // platform — subcommand group
56
+ // ---------------------------------------------------------------------------
57
+
58
+ registerPlatformCommands(oauth);
52
59
  }