@vellumai/assistant 0.5.13 → 0.5.14

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 (425) hide show
  1. package/.env.example +1 -6
  2. package/AGENTS.md +4 -0
  3. package/ARCHITECTURE.md +0 -1
  4. package/bunfig.toml +1 -0
  5. package/docs/architecture/memory.md +3 -3
  6. package/openapi.yaml +127 -22
  7. package/package.json +1 -1
  8. package/src/__tests__/access-request-decision.test.ts +2 -32
  9. package/src/__tests__/actor-token-service.test.ts +1 -31
  10. package/src/__tests__/anthropic-provider.test.ts +53 -40
  11. package/src/__tests__/app-git-history.test.ts +9 -17
  12. package/src/__tests__/app-git-service.test.ts +14 -20
  13. package/src/__tests__/app-store-dir-names.test.ts +10 -20
  14. package/src/__tests__/approval-cascade.test.ts +2 -19
  15. package/src/__tests__/approval-primitive.test.ts +2 -27
  16. package/src/__tests__/approval-routes-http.test.ts +2 -30
  17. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
  19. package/src/__tests__/attachments-store.test.ts +5 -32
  20. package/src/__tests__/audit-log-rotation.test.ts +5 -36
  21. package/src/__tests__/avatar-e2e.test.ts +1 -9
  22. package/src/__tests__/avatar-generator.test.ts +1 -7
  23. package/src/__tests__/browser-fill-credential.test.ts +0 -4
  24. package/src/__tests__/browser-manager.test.ts +0 -6
  25. package/src/__tests__/call-controller.test.ts +1 -22
  26. package/src/__tests__/call-conversation-messages.test.ts +0 -21
  27. package/src/__tests__/call-domain.test.ts +0 -25
  28. package/src/__tests__/call-pointer-messages.test.ts +0 -21
  29. package/src/__tests__/call-recovery.test.ts +0 -22
  30. package/src/__tests__/call-routes-http.test.ts +0 -24
  31. package/src/__tests__/call-store.test.ts +0 -21
  32. package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
  33. package/src/__tests__/canonical-guardian-store.test.ts +48 -21
  34. package/src/__tests__/channel-approval-routes.test.ts +6 -26
  35. package/src/__tests__/channel-approvals.test.ts +1 -38
  36. package/src/__tests__/channel-delivery-store.test.ts +0 -21
  37. package/src/__tests__/channel-guardian.test.ts +0 -26
  38. package/src/__tests__/channel-reply-delivery.test.ts +5 -0
  39. package/src/__tests__/channel-retry-sweep.test.ts +0 -21
  40. package/src/__tests__/checker.test.ts +26 -61
  41. package/src/__tests__/clawhub.test.ts +9 -25
  42. package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
  43. package/src/__tests__/config-loader-backfill.test.ts +9 -28
  44. package/src/__tests__/config-schema-cmd.test.ts +5 -25
  45. package/src/__tests__/config-schema.test.ts +21 -40
  46. package/src/__tests__/config-watcher.test.ts +4 -91
  47. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
  48. package/src/__tests__/contacts-tools.test.ts +0 -21
  49. package/src/__tests__/context-memory-e2e.test.ts +0 -21
  50. package/src/__tests__/context-window-manager.test.ts +130 -3
  51. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
  52. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
  53. package/src/__tests__/conversation-agent-loop.test.ts +0 -4
  54. package/src/__tests__/conversation-attachments.test.ts +1 -24
  55. package/src/__tests__/conversation-attention-store.test.ts +0 -21
  56. package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
  57. package/src/__tests__/conversation-clear-safety.test.ts +0 -22
  58. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
  59. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
  60. package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
  61. package/src/__tests__/conversation-disk-view.test.ts +5 -27
  62. package/src/__tests__/conversation-error.test.ts +1 -1
  63. package/src/__tests__/conversation-fork-crud.test.ts +1 -33
  64. package/src/__tests__/conversation-fork-route.test.ts +0 -27
  65. package/src/__tests__/conversation-history-web-search.test.ts +23 -16
  66. package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
  67. package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
  68. package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
  69. package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
  70. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
  71. package/src/__tests__/conversation-queue.test.ts +8 -8
  72. package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
  74. package/src/__tests__/conversation-slash-commands.test.ts +5 -0
  75. package/src/__tests__/conversation-slash-queue.test.ts +0 -4
  76. package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
  77. package/src/__tests__/conversation-speed-override.test.ts +326 -0
  78. package/src/__tests__/conversation-starter-routes.test.ts +0 -23
  79. package/src/__tests__/conversation-store.test.ts +0 -21
  80. package/src/__tests__/conversation-unread-route.test.ts +0 -24
  81. package/src/__tests__/conversation-usage.test.ts +56 -21
  82. package/src/__tests__/conversation-wipe.test.ts +0 -21
  83. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
  84. package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
  85. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
  86. package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
  87. package/src/__tests__/credential-vault-unit.test.ts +9 -428
  88. package/src/__tests__/credentials-cli.test.ts +10 -10
  89. package/src/__tests__/daemon-assistant-events.test.ts +0 -19
  90. package/src/__tests__/date-context.test.ts +77 -97
  91. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
  92. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
  93. package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
  94. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
  95. package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
  96. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
  97. package/src/__tests__/email-cli.test.ts +6 -6
  98. package/src/__tests__/ephemeral-permissions.test.ts +5 -17
  99. package/src/__tests__/first-greeting.test.ts +4 -32
  100. package/src/__tests__/followup-tools.test.ts +0 -21
  101. package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
  102. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
  103. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
  104. package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
  105. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
  106. package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
  107. package/src/__tests__/guardian-action-store.test.ts +0 -21
  108. package/src/__tests__/guardian-action-sweep.test.ts +0 -21
  109. package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
  110. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
  111. package/src/__tests__/guardian-dispatch.test.ts +0 -21
  112. package/src/__tests__/guardian-grant-minting.test.ts +0 -22
  113. package/src/__tests__/guardian-outbound-http.test.ts +0 -22
  114. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
  115. package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
  116. package/src/__tests__/guardian-routing-state.test.ts +0 -22
  117. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
  118. package/src/__tests__/headless-browser-interactions.test.ts +0 -4
  119. package/src/__tests__/headless-browser-navigate.test.ts +0 -4
  120. package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
  121. package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
  122. package/src/__tests__/heartbeat-service.test.ts +99 -26
  123. package/src/__tests__/hooks-blocking.test.ts +3 -3
  124. package/src/__tests__/hooks-config.test.ts +7 -7
  125. package/src/__tests__/hooks-discovery.test.ts +3 -3
  126. package/src/__tests__/hooks-integration.test.ts +5 -5
  127. package/src/__tests__/hooks-manager.test.ts +3 -3
  128. package/src/__tests__/hooks-runner.test.ts +5 -23
  129. package/src/__tests__/hooks-settings.test.ts +3 -3
  130. package/src/__tests__/hooks-templates.test.ts +3 -3
  131. package/src/__tests__/http-conversation-lineage.test.ts +0 -27
  132. package/src/__tests__/identity-intro-cache.test.ts +0 -4
  133. package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
  134. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
  135. package/src/__tests__/intent-routing.test.ts +2 -55
  136. package/src/__tests__/invite-redemption-service.test.ts +0 -21
  137. package/src/__tests__/invite-routes-http.test.ts +0 -21
  138. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
  139. package/src/__tests__/journal-context.test.ts +8 -75
  140. package/src/__tests__/list-messages-attachments.test.ts +0 -22
  141. package/src/__tests__/llm-context-route-provider.test.ts +0 -21
  142. package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
  143. package/src/__tests__/llm-usage-store.test.ts +0 -21
  144. package/src/__tests__/log-export-workspace.test.ts +1 -1
  145. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
  146. package/src/__tests__/managed-store.test.ts +1 -1
  147. package/src/__tests__/mcp-cli.test.ts +7 -10
  148. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
  149. package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
  150. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
  151. package/src/__tests__/memory-recall-log-store.test.ts +0 -27
  152. package/src/__tests__/memory-recall-quality.test.ts +0 -21
  153. package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
  154. package/src/__tests__/memory-regressions.test.ts +282 -70
  155. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
  156. package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
  157. package/src/__tests__/messaging-send-tool.test.ts +201 -0
  158. package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
  159. package/src/__tests__/migration-export-http.test.ts +7 -1
  160. package/src/__tests__/migration-import-commit-http.test.ts +16 -14
  161. package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
  162. package/src/__tests__/migration-validate-http.test.ts +1 -28
  163. package/src/__tests__/native-web-search.test.ts +25 -22
  164. package/src/__tests__/non-member-access-request.test.ts +0 -22
  165. package/src/__tests__/notification-guardian-path.test.ts +0 -21
  166. package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
  167. package/src/__tests__/oauth-apps-routes.test.ts +103 -2
  168. package/src/__tests__/oauth-cli.test.ts +52 -0
  169. package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
  170. package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
  171. package/src/__tests__/oauth-providers-routes.test.ts +257 -0
  172. package/src/__tests__/oauth-store.test.ts +0 -21
  173. package/src/__tests__/onboarding-template-contract.test.ts +2 -2
  174. package/src/__tests__/openai-provider.test.ts +261 -0
  175. package/src/__tests__/pairing-concurrent.test.ts +6 -6
  176. package/src/__tests__/pairing-routes.test.ts +7 -1
  177. package/src/__tests__/path-policy.test.ts +1 -1
  178. package/src/__tests__/platform.test.ts +64 -88
  179. package/src/__tests__/playbook-execution.test.ts +0 -21
  180. package/src/__tests__/playbook-tools.test.ts +0 -21
  181. package/src/__tests__/pricing.test.ts +100 -0
  182. package/src/__tests__/relay-server.test.ts +1 -25
  183. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
  184. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
  185. package/src/__tests__/runtime-events-sse.test.ts +0 -24
  186. package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
  187. package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
  188. package/src/__tests__/schedule-store.test.ts +0 -21
  189. package/src/__tests__/schedule-tools.test.ts +0 -21
  190. package/src/__tests__/scheduler-recurrence.test.ts +0 -21
  191. package/src/__tests__/scoped-approval-grants.test.ts +0 -21
  192. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
  193. package/src/__tests__/secret-allowlist.test.ts +1 -1
  194. package/src/__tests__/secret-ingress-channel.test.ts +0 -5
  195. package/src/__tests__/secret-ingress-cli.test.ts +0 -6
  196. package/src/__tests__/secret-ingress-http.test.ts +0 -5
  197. package/src/__tests__/secret-ingress.test.ts +0 -5
  198. package/src/__tests__/send-endpoint-busy.test.ts +0 -24
  199. package/src/__tests__/sequence-store.test.ts +0 -21
  200. package/src/__tests__/server-history-render.test.ts +0 -24
  201. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
  202. package/src/__tests__/skill-load-inline-command.test.ts +9 -0
  203. package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
  204. package/src/__tests__/skill-load-tool.test.ts +11 -0
  205. package/src/__tests__/skills-uninstall.test.ts +10 -8
  206. package/src/__tests__/skills.test.ts +1 -1
  207. package/src/__tests__/slack-channel-config.test.ts +1 -1
  208. package/src/__tests__/slack-inbound-verification.test.ts +0 -22
  209. package/src/__tests__/starter-bundle.test.ts +4 -1
  210. package/src/__tests__/suggestion-routes.test.ts +2 -0
  211. package/src/__tests__/system-prompt.test.ts +1 -1
  212. package/src/__tests__/terminal-tools.test.ts +1 -1
  213. package/src/__tests__/test-preload.ts +31 -0
  214. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
  215. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  216. package/src/__tests__/tool-executor.test.ts +0 -20
  217. package/src/__tests__/tool-input-summary.test.ts +124 -0
  218. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
  219. package/src/__tests__/trust-store.test.ts +7 -1
  220. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
  221. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
  222. package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
  223. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  224. package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
  225. package/src/__tests__/twilio-routes.test.ts +1 -1
  226. package/src/__tests__/update-bulletin.test.ts +1 -1
  227. package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
  228. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
  229. package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
  230. package/src/__tests__/voice-session-bridge.test.ts +1 -1
  231. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
  232. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
  233. package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
  234. package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
  235. package/src/agent/loop.ts +6 -9
  236. package/src/approvals/guardian-decision-primitive.ts +46 -18
  237. package/src/approvals/guardian-request-resolvers.ts +19 -2
  238. package/src/calls/active-call-lease.ts +2 -2
  239. package/src/cli/AGENTS.md +1 -1
  240. package/src/cli/commands/doctor.ts +9 -9
  241. package/src/cli/commands/memory.ts +142 -0
  242. package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
  243. package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
  244. package/src/cli/commands/oauth/connect.ts +13 -12
  245. package/src/cli/commands/oauth/index.ts +1 -1
  246. package/src/cli/commands/oauth/providers.ts +47 -62
  247. package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
  248. package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
  249. package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
  250. package/src/cli/commands/platform/connect.ts +17 -7
  251. package/src/cli/commands/platform/disconnect.ts +28 -3
  252. package/src/cli/commands/platform/index.ts +3 -3
  253. package/src/cli.ts +1 -299
  254. package/src/config/assistant-feature-flags.ts +23 -15
  255. package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
  256. package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
  257. package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
  258. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
  259. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
  260. package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
  261. package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
  262. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
  263. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
  264. package/src/config/bundled-skills/messaging/SKILL.md +7 -7
  265. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
  266. package/src/config/bundled-skills/slack/SKILL.md +18 -0
  267. package/src/config/env-registry.ts +15 -11
  268. package/src/config/env.ts +1 -11
  269. package/src/config/feature-flag-registry.json +16 -0
  270. package/src/config/schema.ts +4 -0
  271. package/src/config/schemas/heartbeat.ts +6 -1
  272. package/src/config/schemas/inference.ts +14 -3
  273. package/src/config/schemas/memory-processing.ts +16 -8
  274. package/src/config/schemas/memory-retrieval.ts +3 -3
  275. package/src/config/skills.ts +1 -1
  276. package/src/context/window-manager.ts +174 -51
  277. package/src/credential-execution/executable-discovery.ts +2 -2
  278. package/src/daemon/approved-devices-store.ts +2 -2
  279. package/src/daemon/assistant-attachments.ts +2 -0
  280. package/src/daemon/config-watcher.ts +4 -50
  281. package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
  282. package/src/daemon/conversation-agent-loop.ts +12 -0
  283. package/src/daemon/conversation-error.ts +3 -5
  284. package/src/daemon/conversation-history.ts +7 -3
  285. package/src/daemon/conversation-lifecycle.ts +16 -0
  286. package/src/daemon/conversation-messaging.ts +1 -0
  287. package/src/daemon/conversation-notifiers.ts +67 -30
  288. package/src/daemon/conversation-process.ts +161 -2
  289. package/src/daemon/conversation-queue-manager.ts +2 -0
  290. package/src/daemon/conversation-runtime-assembly.ts +33 -11
  291. package/src/daemon/conversation-slash.ts +14 -3
  292. package/src/daemon/conversation-tool-setup.ts +2 -0
  293. package/src/daemon/conversation-usage.ts +32 -4
  294. package/src/daemon/conversation.ts +33 -1
  295. package/src/daemon/daemon-control.ts +32 -16
  296. package/src/daemon/date-context.ts +47 -45
  297. package/src/daemon/dictation-profile-store.ts +2 -2
  298. package/src/daemon/handlers/conversations.ts +19 -0
  299. package/src/daemon/handlers/shared.ts +14 -21
  300. package/src/daemon/lifecycle.ts +5 -7
  301. package/src/daemon/message-types/conversations.ts +2 -0
  302. package/src/daemon/message-types/guardian-actions.ts +3 -17
  303. package/src/daemon/message-types/integrations.ts +11 -1
  304. package/src/daemon/message-types/messages.ts +1 -0
  305. package/src/daemon/pairing-store.ts +2 -79
  306. package/src/daemon/server.ts +154 -8
  307. package/src/daemon/watch-handler.ts +65 -21
  308. package/src/email/guardrails.ts +3 -3
  309. package/src/heartbeat/heartbeat-service.ts +14 -7
  310. package/src/hooks/cli.ts +2 -2
  311. package/src/hooks/config.ts +2 -2
  312. package/src/hooks/discovery.ts +2 -2
  313. package/src/hooks/manager.ts +2 -2
  314. package/src/hooks/runner.ts +5 -2
  315. package/src/hooks/templates.ts +2 -2
  316. package/src/memory/admin.ts +181 -2
  317. package/src/memory/app-git-service.ts +61 -4
  318. package/src/memory/attachments-store.ts +2 -0
  319. package/src/memory/canonical-guardian-store.ts +16 -0
  320. package/src/memory/db-init.ts +8 -0
  321. package/src/memory/embedding-local.ts +5 -2
  322. package/src/memory/indexer.ts +44 -26
  323. package/src/memory/items-extractor.ts +34 -82
  324. package/src/memory/job-handlers/batch-extraction.ts +741 -0
  325. package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
  326. package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
  327. package/src/memory/jobs-store.ts +28 -0
  328. package/src/memory/jobs-worker.ts +56 -9
  329. package/src/memory/lifecycle-events-store.ts +4 -2
  330. package/src/memory/llm-request-log-store.ts +40 -2
  331. package/src/memory/llm-usage-store.ts +4 -3
  332. package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
  333. package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
  334. package/src/memory/migrations/index.ts +2 -0
  335. package/src/memory/query-expansion.ts +83 -0
  336. package/src/memory/retriever.test.ts +119 -0
  337. package/src/memory/retriever.ts +513 -105
  338. package/src/memory/schema/guardian.ts +4 -0
  339. package/src/memory/schema/infrastructure.ts +1 -0
  340. package/src/memory/search/formatting.test.ts +140 -0
  341. package/src/memory/search/formatting.ts +143 -198
  342. package/src/memory/search/mmr.ts +136 -0
  343. package/src/memory/search/staleness.ts +0 -15
  344. package/src/memory/search/tier-classifier.ts +10 -21
  345. package/src/memory/search/types.ts +17 -0
  346. package/src/messaging/providers/slack/adapter.ts +51 -5
  347. package/src/notifications/broadcaster.ts +13 -0
  348. package/src/notifications/copy-composer.ts +8 -0
  349. package/src/oauth/connect-orchestrator.ts +1 -1
  350. package/src/oauth/connection-resolver.ts +2 -2
  351. package/src/oauth/provider-serializer.ts +116 -0
  352. package/src/permissions/trust-store.ts +24 -7
  353. package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
  354. package/src/prompts/journal-context.ts +50 -35
  355. package/src/prompts/persona-resolver.ts +1 -1
  356. package/src/prompts/system-prompt.ts +27 -28
  357. package/src/prompts/templates/BOOTSTRAP.md +14 -1
  358. package/src/prompts/templates/HEARTBEAT.md +10 -0
  359. package/src/prompts/templates/NOW.md +19 -25
  360. package/src/prompts/templates/SOUL.md +13 -1
  361. package/src/prompts/templates/UPDATES.md +12 -0
  362. package/src/prompts/update-bulletin.ts +1 -1
  363. package/src/providers/anthropic/client.ts +89 -18
  364. package/src/providers/model-catalog.ts +22 -2
  365. package/src/providers/model-intents.ts +2 -2
  366. package/src/providers/openai/client.ts +40 -1
  367. package/src/providers/retry.ts +23 -4
  368. package/src/providers/types.ts +2 -0
  369. package/src/runtime/assistant-scope.ts +1 -1
  370. package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
  371. package/src/runtime/auth/route-policy.ts +1 -0
  372. package/src/runtime/auth/token-service.ts +51 -29
  373. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
  374. package/src/runtime/guardian-decision-types.ts +16 -10
  375. package/src/runtime/http-server.ts +3 -14
  376. package/src/runtime/http-types.ts +1 -0
  377. package/src/runtime/migrations/vbundle-builder.ts +7 -4
  378. package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
  379. package/src/runtime/migrations/vbundle-importer.ts +1 -1
  380. package/src/runtime/routes/conversation-query-routes.ts +40 -8
  381. package/src/runtime/routes/conversation-routes.ts +125 -3
  382. package/src/runtime/routes/guardian-action-routes.ts +9 -3
  383. package/src/runtime/routes/identity-routes.ts +25 -4
  384. package/src/runtime/routes/llm-context-normalization.ts +1 -0
  385. package/src/runtime/routes/log-export-routes.ts +34 -12
  386. package/src/runtime/routes/migration-routes.ts +6 -10
  387. package/src/runtime/routes/oauth-apps.ts +2 -9
  388. package/src/runtime/routes/oauth-providers.ts +60 -0
  389. package/src/runtime/routes/pairing-routes.ts +0 -8
  390. package/src/runtime/routes/settings-routes.ts +0 -1
  391. package/src/runtime/routes/telemetry-routes.ts +16 -4
  392. package/src/security/encrypted-store.ts +2 -2
  393. package/src/security/secret-allowlist.ts +3 -3
  394. package/src/signals/emit-event.ts +42 -0
  395. package/src/signals/user-message.ts +37 -0
  396. package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
  397. package/src/telemetry/usage-telemetry-reporter.ts +23 -17
  398. package/src/tools/browser/runtime-check.ts +2 -2
  399. package/src/tools/credentials/vault.ts +2 -249
  400. package/src/tools/memory/definitions.ts +1 -1
  401. package/src/tools/memory/handlers.test.ts +50 -8
  402. package/src/tools/memory/handlers.ts +3 -1
  403. package/src/tools/side-effects.ts +1 -6
  404. package/src/tools/terminal/safe-env.ts +3 -2
  405. package/src/tools/terminal/shell.ts +11 -14
  406. package/src/tools/tool-approval-handler.ts +20 -1
  407. package/src/tools/tool-input-summary.ts +66 -0
  408. package/src/tools/types.ts +4 -0
  409. package/src/usage/types.ts +4 -0
  410. package/src/util/device-id.ts +10 -10
  411. package/src/util/platform.ts +71 -33
  412. package/src/util/pricing.ts +19 -6
  413. package/src/util/strip-comment-lines.ts +28 -0
  414. package/src/workspace/git-service.ts +8 -18
  415. package/src/workspace/migrations/003-seed-device-id.ts +6 -4
  416. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
  417. package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
  418. package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
  419. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
  420. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
  421. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
  422. package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
  423. package/src/workspace/migrations/registry.ts +8 -0
  424. package/src/signals/confirm.ts +0 -82
  425. package/src/signals/trust-rule.ts +0 -174
@@ -0,0 +1,136 @@
1
+ import { generateSparseEmbedding } from "../embedding-backend.js";
2
+ import type { SparseEmbedding } from "../embedding-types.js";
3
+ import type { TieredCandidate } from "./tier-classifier.js";
4
+
5
+ /**
6
+ * Compute cosine similarity between two sparse vectors.
7
+ * Returns 0 if either vector has zero magnitude.
8
+ */
9
+ function sparseCosine(a: SparseEmbedding, b: SparseEmbedding): number {
10
+ // Build index→value map for vector b
11
+ const bMap = new Map<number, number>();
12
+ for (let i = 0; i < b.indices.length; i++) {
13
+ bMap.set(b.indices[i]!, b.values[i]!);
14
+ }
15
+
16
+ // Compute dot product over shared indices
17
+ let dotProduct = 0;
18
+ for (let i = 0; i < a.indices.length; i++) {
19
+ const bVal = bMap.get(a.indices[i]!);
20
+ if (bVal !== undefined) {
21
+ dotProduct += a.values[i]! * bVal;
22
+ }
23
+ }
24
+
25
+ // Compute magnitudes
26
+ let magA = 0;
27
+ for (const v of a.values) magA += v * v;
28
+ magA = Math.sqrt(magA);
29
+
30
+ let magB = 0;
31
+ for (const v of b.values) magB += v * v;
32
+ magB = Math.sqrt(magB);
33
+
34
+ if (magA === 0 || magB === 0) return 0;
35
+ return dotProduct / (magA * magB);
36
+ }
37
+
38
+ /**
39
+ * Apply Maximal Marginal Relevance (MMR) diversity ranking to candidates.
40
+ *
41
+ * Items are re-ranked using a greedy selection loop that progressively
42
+ * penalizes candidates whose text is similar to already-selected ones.
43
+ * Non-item candidates (segments, summaries, media) pass through unpenalized
44
+ * since they represent different conversation windows.
45
+ *
46
+ * @param candidates - Scored candidates from upstream ranking
47
+ * @param penalty - Float 0..1. 0 = no diversity pressure, 1 = maximum
48
+ * @returns Re-ranked candidates with adjusted finalScores
49
+ */
50
+ export function applyMMR(
51
+ candidates: TieredCandidate[],
52
+ penalty: number,
53
+ ): TieredCandidate[] {
54
+ // Pre-compute sparse embeddings for all candidates
55
+ const embeddings = candidates.map((c) => generateSparseEmbedding(c.text));
56
+
57
+ // Separate items from non-items
58
+ const items: { index: number; candidate: TieredCandidate }[] = [];
59
+ const nonItems: TieredCandidate[] = [];
60
+
61
+ for (let i = 0; i < candidates.length; i++) {
62
+ const c = candidates[i]!;
63
+ if (c.type === "item") {
64
+ items.push({ index: i, candidate: c });
65
+ } else {
66
+ nonItems.push(c);
67
+ }
68
+ }
69
+
70
+ // If no items or no penalty, pass through in original order
71
+ if (items.length === 0 || penalty === 0) {
72
+ return candidates;
73
+ }
74
+
75
+ // Greedy MMR selection loop
76
+ const selected: number[] = [];
77
+ const remaining = new Set<number>(items.map((_, i) => i));
78
+ const adjustedScores = new Map<number, number>();
79
+
80
+ // Select the item with the highest finalScore first
81
+ let bestIdx = -1;
82
+ let bestScore = -Infinity;
83
+ for (const idx of remaining) {
84
+ const score = items[idx]!.candidate.finalScore;
85
+ if (score > bestScore) {
86
+ bestScore = score;
87
+ bestIdx = idx;
88
+ }
89
+ }
90
+ selected.push(bestIdx);
91
+ remaining.delete(bestIdx);
92
+ adjustedScores.set(bestIdx, items[bestIdx]!.candidate.finalScore);
93
+
94
+ // Iteratively select remaining items
95
+ while (remaining.size > 0) {
96
+ let nextBestIdx = -1;
97
+ let nextBestScore = -Infinity;
98
+
99
+ for (const idx of remaining) {
100
+ const itemEmbIdx = items[idx]!.index;
101
+
102
+ // Compute max similarity to any already-selected item
103
+ let maxSim = 0;
104
+ for (const selIdx of selected) {
105
+ const selEmbIdx = items[selIdx]!.index;
106
+ const sim = sparseCosine(
107
+ embeddings[itemEmbIdx]!,
108
+ embeddings[selEmbIdx]!,
109
+ );
110
+ if (sim > maxSim) maxSim = sim;
111
+ }
112
+
113
+ const adjustedScore =
114
+ items[idx]!.candidate.finalScore * (1 - maxSim * penalty);
115
+ if (adjustedScore > nextBestScore) {
116
+ nextBestScore = adjustedScore;
117
+ nextBestIdx = idx;
118
+ }
119
+ }
120
+
121
+ selected.push(nextBestIdx);
122
+ remaining.delete(nextBestIdx);
123
+ adjustedScores.set(nextBestIdx, nextBestScore);
124
+ }
125
+
126
+ // Rebuild output: non-items first (original order), then items in selected order
127
+ const result: TieredCandidate[] = [...nonItems];
128
+ for (const idx of selected) {
129
+ result.push({
130
+ ...items[idx]!.candidate,
131
+ finalScore: adjustedScores.get(idx)!,
132
+ });
133
+ }
134
+
135
+ return result;
136
+ }
@@ -1,4 +1,3 @@
1
- import type { TieredCandidate } from "./tier-classifier.js";
2
1
  import type { StalenessLevel } from "./types.js";
3
2
 
4
3
  const BASE_LIFETIME_MS: Record<string, number> = {
@@ -39,17 +38,3 @@ export function computeStaleness(
39
38
  if (ratio <= 2) return { level: "stale", ratio };
40
39
  return { level: "very_stale", ratio };
41
40
  }
42
-
43
- /**
44
- * Demote very_stale tier-1 candidates to tier 2.
45
- */
46
- export function applyStaleDemotion(
47
- candidates: TieredCandidate[],
48
- ): TieredCandidate[] {
49
- return candidates.map((c) => {
50
- if (c.tier === 1 && c.staleness === "very_stale") {
51
- return { ...c, tier: 2 as const };
52
- }
53
- return c;
54
- });
55
- }
@@ -1,29 +1,18 @@
1
1
  import type { Candidate } from "./types.js";
2
2
 
3
- export type Tier = 1 | 2;
4
-
5
- export interface TieredCandidate extends Candidate {
6
- tier: Tier;
3
+ /** Backward-compatible alias downstream files import this type. */
4
+ export type TieredCandidate = Candidate & {
7
5
  /** Human-readable label for the source conversation/summary (e.g. conversation title). */
8
6
  sourceLabel?: string;
9
- }
7
+ };
8
+
9
+ const MIN_SCORE_THRESHOLD = 0.2;
10
10
 
11
11
  /**
12
- * Map a composite relevance score to an injection tier.
13
- *
14
- * Thresholds are intentionally set lower than raw-embedding ceilings because
15
- * the multiplicative scoring pipeline (semantic × recency × metadata) compresses
16
- * the effective score range. Lowering the gates lets moderately-relevant items
17
- * surface rather than being silently dropped.
12
+ * Filter candidates to those exceeding the minimum relevance threshold.
13
+ * Replaces the former tier 1/tier 2 classification — all surviving candidates
14
+ * are treated equally and ranked by score.
18
15
  */
19
- export function classifyTier(score: number): Tier | null {
20
- if (score > 0.6) return 1;
21
- if (score > 0.4) return 2;
22
- return null;
23
- }
24
-
25
- export function classifyTiers(candidates: Candidate[]): TieredCandidate[] {
26
- return candidates
27
- .map((c) => ({ ...c, tier: classifyTier(c.finalScore) }))
28
- .filter((c): c is TieredCandidate => c.tier != null);
16
+ export function filterByMinScore(candidates: Candidate[]): TieredCandidate[] {
17
+ return candidates.filter((c) => c.finalScore > MIN_SCORE_THRESHOLD);
29
18
  }
@@ -15,6 +15,10 @@ export interface Candidate {
15
15
  conversationId?: string;
16
16
  /** The source message ID this candidate was extracted from (segments only). */
17
17
  messageId?: string;
18
+ /** The ID of the memory item this candidate supersedes (items only). */
19
+ supersedes?: string;
20
+ /** Relative workspace path to the source conversation's messages file. */
21
+ sourcePath?: string;
18
22
  confidence: number;
19
23
  importance: number;
20
24
  createdAt: number;
@@ -32,6 +36,7 @@ export interface MemoryRecallCandiateDebug {
32
36
  finalScore: number;
33
37
  semantic: number;
34
38
  recency: number;
39
+ sourceLabel?: string;
35
40
  }
36
41
 
37
42
  export type DegradationReason =
@@ -67,6 +72,12 @@ export interface MemoryRecallResult {
67
72
  hybridSearchMs?: number;
68
73
  /** Whether sparse vectors were used in the hybrid search. */
69
74
  sparseVectorUsed?: boolean;
75
+ /** Whether HyDE query expansion was used for this retrieval. */
76
+ hydeExpanded?: boolean;
77
+ /** Number of hypothetical documents generated by HyDE expansion. */
78
+ hydeDocCount?: number;
79
+ /** Whether MMR diversity ranking was applied to candidates. */
80
+ mmrApplied?: boolean;
70
81
  }
71
82
 
72
83
  /**
@@ -94,6 +105,12 @@ export interface MemoryRecallOptions {
94
105
  */
95
106
  scopePolicyOverride?: ScopePolicyOverride;
96
107
  maxInjectTokensOverride?: number;
108
+ /**
109
+ * When true, use HyDE (Hypothetical Document Embeddings) query expansion
110
+ * to generate hypothetical memory documents, embed them alongside the raw
111
+ * query, and run parallel semantic searches for improved recall.
112
+ */
113
+ hydeEnabled?: boolean;
97
114
  }
98
115
 
99
116
  export interface ItemMetadata {
@@ -5,6 +5,8 @@
5
5
  * implements the MessagingProvider interface.
6
6
  */
7
7
 
8
+ import { findContactChannel } from "../../../contacts/contact-store.js";
9
+ import { upsertContactChannel } from "../../../contacts/contacts-write.js";
8
10
  import type { OAuthConnection } from "../../../oauth/connection.js";
9
11
  import { resolveOAuthConnection } from "../../../oauth/connection-resolver.js";
10
12
  import { isProviderConnected } from "../../../oauth/oauth-store.js";
@@ -62,6 +64,21 @@ async function resolveUserName(
62
64
  const cached = userNameCache.get(userId);
63
65
  if (cached) return cached;
64
66
 
67
+ // Check contacts DB for a persistent cache hit
68
+ try {
69
+ const result = findContactChannel({
70
+ channelType: "slack",
71
+ externalUserId: userId,
72
+ });
73
+ if (result) {
74
+ const name = result.contact.displayName;
75
+ userNameCache.set(userId, name);
76
+ return name;
77
+ }
78
+ } catch {
79
+ // Contact lookup failures are non-fatal — fall through to API
80
+ }
81
+
65
82
  try {
66
83
  const resp = await slack.userInfo(auth, userId);
67
84
  const name =
@@ -70,6 +87,18 @@ async function resolveUserName(
70
87
  resp.user.real_name ||
71
88
  resp.user.name;
72
89
  userNameCache.set(userId, name);
90
+
91
+ // Persist to contacts for future sessions
92
+ try {
93
+ upsertContactChannel({
94
+ sourceChannel: "slack",
95
+ externalUserId: userId,
96
+ displayName: name,
97
+ });
98
+ } catch {
99
+ // Non-fatal — caching failure shouldn't break messaging
100
+ }
101
+
73
102
  return name;
74
103
  } catch {
75
104
  return userId;
@@ -216,13 +245,30 @@ export const slackProvider: MessagingProvider = {
216
245
  (!options?.limit || conversations.length < options.limit)
217
246
  );
218
247
 
219
- // Resolve DM user names
248
+ // Resolve DM user names and cache channel mappings
220
249
  for (const conv of conversations) {
221
250
  if (conv.type === "dm" && conv.metadata?.dmUserId) {
222
- conv.name = await resolveUserName(
223
- auth,
224
- conv.metadata.dmUserId as string,
225
- );
251
+ const dmUserId = conv.metadata.dmUserId as string;
252
+ conv.name = await resolveUserName(auth, dmUserId);
253
+
254
+ // Persist the DM channel ID so future sends skip conversations.open
255
+ try {
256
+ const existing = findContactChannel({
257
+ channelType: "slack",
258
+ externalUserId: dmUserId,
259
+ });
260
+ if (existing && !existing.channel.externalChatId) {
261
+ upsertContactChannel({
262
+ contactId: existing.contact.id,
263
+ sourceChannel: "slack",
264
+ externalUserId: dmUserId,
265
+ externalChatId: conv.id,
266
+ displayName: conv.name,
267
+ });
268
+ }
269
+ } catch {
270
+ // Non-fatal
271
+ }
226
272
  }
227
273
  }
228
274
 
@@ -147,6 +147,19 @@ export class NotificationBroadcaster {
147
147
  };
148
148
  }
149
149
 
150
+ // For tool_grant_request signals, prefer the deterministic template seed
151
+ // over LLM-generated prose. The enriched questionText is already concise
152
+ // and informative — LLM rewording just adds noise.
153
+ if (signal.contextPayload?.requestKind === "tool_grant_request") {
154
+ if (!fallbackCopy) {
155
+ fallbackCopy = composeFallbackCopy(signal, decision.selectedChannels);
156
+ }
157
+ const templateSeed = fallbackCopy[channel]?.conversationSeedMessage;
158
+ if (templateSeed) {
159
+ copy = { ...copy, conversationSeedMessage: templateSeed };
160
+ }
161
+ }
162
+
150
163
  // Resolve the per-channel conversation action from the decision (default: start_new)
151
164
  const conversationAction: ConversationAction | undefined =
152
165
  decision.conversationActions?.[channel];
@@ -326,10 +326,17 @@ const TEMPLATES: Partial<Record<NotificationSourceEventName, CopyTemplate>> = {
326
326
  const requestCode = nonEmpty(
327
327
  typeof payload.requestCode === "string" ? payload.requestCode : undefined,
328
328
  );
329
+
330
+ // For tool_grant_request, the questionText already includes requester name + input summary.
331
+ // Use it directly as the conversation seed to avoid LLM-generated filler.
332
+ const isToolGrant = payload.requestKind === "tool_grant_request";
333
+ const conversationSeedMessage = isToolGrant ? question : undefined;
334
+
329
335
  if (!requestCode) {
330
336
  return {
331
337
  title: "Guardian Question",
332
338
  body: question,
339
+ conversationSeedMessage,
333
340
  };
334
341
  }
335
342
 
@@ -342,6 +349,7 @@ const TEMPLATES: Partial<Record<NotificationSourceEventName, CopyTemplate>> = {
342
349
  return {
343
350
  title: "Guardian Question",
344
351
  body: `${question}\n\n${instruction}`,
352
+ conversationSeedMessage,
345
353
  };
346
354
  },
347
355
 
@@ -229,7 +229,7 @@ export async function orchestrateOAuthConnect(
229
229
  return {
230
230
  success: false,
231
231
  error:
232
- "oauth2_connect from a non-interactive session requires a public ingress URL. Configure ingress.publicBaseUrl first.",
232
+ "OAuth connect from a non-interactive session requires a public ingress URL. Configure ingress.publicBaseUrl first.",
233
233
  safeError: true,
234
234
  };
235
235
  }
@@ -88,7 +88,7 @@ export async function resolveOAuthConnection(
88
88
  ? ` matching ${filters.join(" and ")}`
89
89
  : "";
90
90
  throw new Error(
91
- `No active OAuth connection found for "${providerKey}"${qualifier}. Connect the service first with oauth2_connect.`,
91
+ `No active OAuth connection found for "${providerKey}"${qualifier}. Connect the service first with \`assistant oauth connect ${providerKey}\`.`,
92
92
  );
93
93
  }
94
94
 
@@ -97,7 +97,7 @@ export async function resolveOAuthConnection(
97
97
  );
98
98
  if (!accessToken) {
99
99
  throw new Error(
100
- `OAuth connection for "${providerKey}" exists but has no access token. Re-authorize with oauth2_connect.`,
100
+ `OAuth connection for "${providerKey}" exists but has no access token. Re-authorize with \`assistant oauth connect ${providerKey}\`.`,
101
101
  );
102
102
  }
103
103
 
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Shared serialization utilities for OAuth provider rows.
3
+ *
4
+ * Used by both the CLI (providers commands) and runtime API endpoints to
5
+ * produce consistent, parsed representations of provider rows stored in
6
+ * the database.
7
+ */
8
+
9
+ import type { OAuthProviderRow } from "./oauth-store.js";
10
+
11
+ /**
12
+ * Full serialized representation of an OAuth provider row.
13
+ *
14
+ * JSON string fields are parsed into their native types, boolean/integer
15
+ * fields are normalised to booleans, timestamps are ISO 8601 strings,
16
+ * and a computed `supportsManagedMode` flag is included.
17
+ */
18
+ export type SerializedProvider = ReturnType<typeof serializeProvider> &
19
+ Record<string, unknown>;
20
+
21
+ /**
22
+ * Lightweight summary projection of an OAuth provider, suitable for API
23
+ * list responses where full detail is not needed. All keys are snake_case
24
+ * to match the HTTP API convention.
25
+ */
26
+ export interface SerializedProviderSummary {
27
+ provider_key: string;
28
+ display_name: string | null;
29
+ description: string | null;
30
+ dashboard_url: string | null;
31
+ client_id_placeholder: string | null;
32
+ requires_client_secret: boolean;
33
+ supports_managed_mode: boolean;
34
+ }
35
+
36
+ /**
37
+ * Serialize a full provider row from the database into a parsed object.
38
+ *
39
+ * JSON string columns are parsed, boolean/integer fields are normalised to
40
+ * booleans, and timestamps are converted to ISO 8601 strings. An optional
41
+ * `redirectUri` override can be supplied by the caller (e.g. the CLI,
42
+ * which resolves the redirect URI from config).
43
+ *
44
+ * Returns `undefined` when `row` is `undefined`, and `null` when `row` is
45
+ * `null`, preserving the caller's nullable semantics.
46
+ */
47
+ export function serializeProvider(
48
+ row: OAuthProviderRow | null | undefined,
49
+ options?: { redirectUri?: string | null },
50
+ ): ReturnType<typeof _serializeProvider> | null | undefined {
51
+ if (row === undefined) return undefined;
52
+ if (row === null) return null;
53
+ return _serializeProvider(row, options);
54
+ }
55
+
56
+ function _serializeProvider(
57
+ row: OAuthProviderRow,
58
+ options?: { redirectUri?: string | null },
59
+ ) {
60
+ return {
61
+ ...row,
62
+ displayName: row.displayName ?? null,
63
+ description: row.description ?? null,
64
+ dashboardUrl: row.dashboardUrl ?? null,
65
+ clientIdPlaceholder: row.clientIdPlaceholder ?? null,
66
+ requiresClientSecret: !!(row.requiresClientSecret ?? 1),
67
+ supportsManagedMode: !!row.managedServiceConfigKey,
68
+ defaultScopes: row.defaultScopes ? JSON.parse(row.defaultScopes) : [],
69
+ scopePolicy: row.scopePolicy ? JSON.parse(row.scopePolicy) : {},
70
+ extraParams: row.extraParams ? JSON.parse(row.extraParams) : null,
71
+ pingHeaders: row.pingHeaders ? JSON.parse(row.pingHeaders) : null,
72
+ pingBody: row.pingBody ? JSON.parse(row.pingBody) : null,
73
+ loopbackPort: row.loopbackPort ?? null,
74
+ injectionTemplates: row.injectionTemplates
75
+ ? JSON.parse(row.injectionTemplates)
76
+ : null,
77
+ appType: row.appType ?? null,
78
+ setupNotes: row.setupNotes ? JSON.parse(row.setupNotes) : null,
79
+ identityUrl: row.identityUrl ?? null,
80
+ identityMethod: row.identityMethod ?? null,
81
+ identityHeaders: row.identityHeaders
82
+ ? JSON.parse(row.identityHeaders)
83
+ : null,
84
+ identityBody: row.identityBody ? JSON.parse(row.identityBody) : null,
85
+ identityResponsePaths: row.identityResponsePaths
86
+ ? JSON.parse(row.identityResponsePaths)
87
+ : null,
88
+ identityFormat: row.identityFormat ?? null,
89
+ identityOkField: row.identityOkField ?? null,
90
+ redirectUri:
91
+ options?.redirectUri !== undefined ? options.redirectUri : null,
92
+ createdAt: new Date(row.createdAt).toISOString(),
93
+ updatedAt: new Date(row.updatedAt).toISOString(),
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Return a lightweight snake_case summary of a provider row, suitable for
99
+ * embedding in API list responses.
100
+ *
101
+ * Returns `null` when `row` is nullish.
102
+ */
103
+ export function serializeProviderSummary(
104
+ row: OAuthProviderRow | null | undefined,
105
+ ): SerializedProviderSummary | null {
106
+ if (!row) return null;
107
+ return {
108
+ provider_key: row.providerKey,
109
+ display_name: row.displayName ?? null,
110
+ description: row.description ?? null,
111
+ dashboard_url: row.dashboardUrl ?? null,
112
+ client_id_placeholder: row.clientIdPlaceholder ?? null,
113
+ requires_client_secret: !!(row.requiresClientSecret ?? 1),
114
+ supports_managed_mode: !!row.managedServiceConfigKey,
115
+ };
116
+ }
@@ -6,6 +6,7 @@ import {
6
6
  renameSync,
7
7
  writeFileSync,
8
8
  } from "node:fs";
9
+ import { homedir } from "node:os";
9
10
  import { dirname, join } from "node:path";
10
11
 
11
12
  import { Minimatch } from "minimatch";
@@ -13,7 +14,6 @@ import { v4 as uuid } from "uuid";
13
14
 
14
15
  import { getIsContainerized } from "../config/env-registry.js";
15
16
  import { getLogger } from "../util/logger.js";
16
- import { getRootDir } from "../util/platform.js";
17
17
  import { getDefaultRuleTemplates } from "./defaults.js";
18
18
  import * as trustClient from "./trust-client.js";
19
19
  import type {
@@ -128,7 +128,19 @@ function rebuildPatternCache(rules: TrustRule[]): void {
128
128
  }
129
129
 
130
130
  function getTrustPath(): string {
131
- return join(getRootDir(), "protected", "trust.json");
131
+ return join(getGatewaySecurityDir(), "trust.json");
132
+ }
133
+
134
+ /**
135
+ * Resolve the gateway security directory.
136
+ *
137
+ * Docker: `GATEWAY_SECURITY_DIR` env var.
138
+ * Local: falls back to `~/.vellum/` + `protected/`.
139
+ */
140
+ function getGatewaySecurityDir(): string {
141
+ const securityDir = process.env.GATEWAY_SECURITY_DIR;
142
+ if (securityDir) return securityDir;
143
+ return join(homedir(), ".vellum", "protected");
132
144
  }
133
145
 
134
146
  /**
@@ -1103,11 +1115,9 @@ function getGatewayTrustStore(): GatewayTrustStoreAdapter {
1103
1115
  * Returns the active trust store backend.
1104
1116
  *
1105
1117
  * When `IS_CONTAINERIZED=true`, returns a gateway-backed adapter that
1106
- * proxies all trust operations through the gateway HTTP API. The daemon
1107
- * never reads or writes `protected/trust.json` directly in Docker.
1118
+ * proxies all trust operations through the gateway HTTP API.
1108
1119
  *
1109
- * When `IS_CONTAINERIZED=false`, returns the file-based implementation
1110
- * (no change from previous behavior).
1120
+ * When `IS_CONTAINERIZED=false`, returns the file-based implementation.
1111
1121
  */
1112
1122
  export function getTrustStore(): TrustStoreBackend {
1113
1123
  if (getIsContainerized()) {
@@ -1133,7 +1143,14 @@ export function addRule(
1133
1143
  executionTarget?: string;
1134
1144
  },
1135
1145
  ): TrustRule {
1136
- return getTrustStore().addRule(tool, pattern, scope, decision, priority, options);
1146
+ return getTrustStore().addRule(
1147
+ tool,
1148
+ pattern,
1149
+ scope,
1150
+ decision,
1151
+ priority,
1152
+ options,
1153
+ );
1137
1154
  }
1138
1155
 
1139
1156
  export function updateRule(
@@ -23,6 +23,11 @@ describe("buildCliReferenceSection", () => {
23
23
  expect(result).toContain("assistant --help");
24
24
  });
25
25
 
26
+ test("mentions assistant platform for querying platform state", () => {
27
+ const result = buildCliReferenceSection();
28
+ expect(result).toContain("assistant platform status");
29
+ });
30
+
26
31
  test("does not embed the full CLI help output", () => {
27
32
  const result = buildCliReferenceSection();
28
33
  // The full help text used to be embedded; now the model should