@vellumai/assistant 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (666) hide show
  1. package/ARCHITECTURE.md +6 -7
  2. package/Dockerfile +1 -0
  3. package/README.md +2 -2
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +79 -139
  5. package/bun.lock +3 -0
  6. package/docs/architecture/security.md +18 -16
  7. package/knip.json +1 -0
  8. package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +1 -5
  9. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -5
  10. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -16
  11. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +1 -9
  12. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +12 -12
  13. package/node_modules/@vellumai/slack-text/bun.lock +24 -0
  14. package/node_modules/@vellumai/slack-text/package.json +18 -0
  15. package/node_modules/@vellumai/slack-text/src/index.test.ts +153 -0
  16. package/node_modules/@vellumai/slack-text/src/index.ts +235 -0
  17. package/node_modules/@vellumai/slack-text/tsconfig.json +20 -0
  18. package/openapi.yaml +294 -107
  19. package/package.json +4 -2
  20. package/scripts/generate-openapi.ts +16 -111
  21. package/src/__tests__/agent-wake-override-profile.test.ts +23 -1
  22. package/src/__tests__/anthropic-provider.test.ts +56 -13
  23. package/src/__tests__/app-conversation-ids-backfill.test.ts +278 -0
  24. package/src/__tests__/app-conversation-ids.test.ts +151 -0
  25. package/src/__tests__/approval-cascade.test.ts +0 -15
  26. package/src/__tests__/approval-routes-http.test.ts +6 -17
  27. package/src/__tests__/assistant-event-hub.test.ts +126 -77
  28. package/src/__tests__/assistant-event.test.ts +0 -5
  29. package/src/__tests__/assistant-events-sse-hardening.test.ts +37 -15
  30. package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -29
  31. package/src/__tests__/background-shell-host-bash.test.ts +34 -43
  32. package/src/__tests__/call-controller.test.ts +1 -1
  33. package/src/__tests__/call-site-routing-provider.test.ts +193 -0
  34. package/src/__tests__/channel-approval-routes.test.ts +10 -296
  35. package/src/__tests__/channel-approvals.test.ts +25 -17
  36. package/src/__tests__/channel-guardian.test.ts +100 -146
  37. package/src/__tests__/checker.test.ts +20 -34
  38. package/src/__tests__/compact-event-conversation-id-guard.test.ts +50 -0
  39. package/src/__tests__/compaction-events.test.ts +2 -0
  40. package/src/__tests__/config-schema.test.ts +6 -48
  41. package/src/__tests__/config-watcher.test.ts +12 -0
  42. package/src/__tests__/connection-policy.test.ts +1 -52
  43. package/src/__tests__/contacts-write.test.ts +2 -64
  44. package/src/__tests__/context-image-dimensions.test.ts +1 -1
  45. package/src/__tests__/context-search-memory-source.test.ts +120 -1
  46. package/src/__tests__/context-search-memory-v2-source.test.ts +383 -0
  47. package/src/__tests__/context-search-pkb-source.test.ts +49 -0
  48. package/src/__tests__/context-search-workspace-source.test.ts +9 -22
  49. package/src/__tests__/context-window-manager.test.ts +46 -0
  50. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  51. package/src/__tests__/conversation-agent-loop-overflow.test.ts +102 -29
  52. package/src/__tests__/conversation-agent-loop.test.ts +980 -13
  53. package/src/__tests__/conversation-analysis-routes.test.ts +12 -10
  54. package/src/__tests__/conversation-attention-telegram.test.ts +11 -3
  55. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -291
  56. package/src/__tests__/conversation-history-web-search.test.ts +4 -3
  57. package/src/__tests__/conversation-inference-profile-route.test.ts +12 -23
  58. package/src/__tests__/conversation-lifecycle.test.ts +4 -4
  59. package/src/__tests__/conversation-process-callsite.test.ts +79 -2
  60. package/src/__tests__/conversation-queue.test.ts +3 -8
  61. package/src/__tests__/conversation-routes-disk-view.test.ts +1 -161
  62. package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -32
  63. package/src/__tests__/conversation-routes-slash-commands.test.ts +75 -66
  64. package/src/__tests__/conversation-runtime-assembly.test.ts +257 -3
  65. package/src/__tests__/conversation-slash-commands.test.ts +24 -4
  66. package/src/__tests__/conversation-slash-queue.test.ts +2 -0
  67. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  68. package/src/__tests__/conversation-starter-routes.test.ts +79 -2
  69. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -5
  70. package/src/__tests__/conversation-surfaces-standalone.test.ts +18 -14
  71. package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -2
  72. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +8 -46
  73. package/src/__tests__/conversation-usage.test.ts +253 -3
  74. package/src/__tests__/credential-execution-shell-lockdown.test.ts +0 -39
  75. package/src/__tests__/credential-health-service.test.ts +68 -0
  76. package/src/__tests__/credential-security-e2e.test.ts +4 -3
  77. package/src/__tests__/credential-security-invariants.test.ts +1 -5
  78. package/src/__tests__/credential-token-resolver.test.ts +180 -0
  79. package/src/__tests__/cu-unified-flow.test.ts +33 -16
  80. package/src/__tests__/daemon-assistant-events.test.ts +34 -21
  81. package/src/__tests__/daemon-credential-client.test.ts +4 -1
  82. package/src/__tests__/db-connection-isolation.test.ts +125 -0
  83. package/src/__tests__/db-migration-rollback.test.ts +101 -0
  84. package/src/__tests__/db-slack-compaction-watermark-migration.test.ts +169 -0
  85. package/src/__tests__/deterministic-verification-control-plane.test.ts +7 -80
  86. package/src/__tests__/document-conversations.test.ts +332 -0
  87. package/src/__tests__/embedding-managed-proxy-selection.test.ts +2 -2
  88. package/src/__tests__/emit-event-signal.test.ts +4 -6
  89. package/src/__tests__/events-client-registration.test.ts +193 -49
  90. package/src/__tests__/filing-service.test.ts +58 -7
  91. package/src/__tests__/first-greeting.test.ts +156 -150
  92. package/src/__tests__/fixtures/mock-chrome-extension.ts +108 -66
  93. package/src/__tests__/get-skill-detail-audit.test.ts +3 -8
  94. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  95. package/src/__tests__/guardian-dispatch.test.ts +1 -1
  96. package/src/__tests__/guardian-grant-minting.test.ts +7 -2
  97. package/src/__tests__/guardian-routing-invariants.test.ts +7 -2
  98. package/src/__tests__/guardian-routing-state.test.ts +1 -1
  99. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +32 -11
  100. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +2 -83
  101. package/src/__tests__/headless-browser-mode.test.ts +4 -9
  102. package/src/__tests__/headless-browser-navigate.test.ts +21 -20
  103. package/src/__tests__/heartbeat-service.test.ts +289 -7
  104. package/src/__tests__/helpers/channel-test-adapter.ts +2 -2
  105. package/src/__tests__/helpers/create-guardian-binding.ts +91 -0
  106. package/src/__tests__/host-bash-proxy.test.ts +46 -122
  107. package/src/__tests__/host-browser-e2e-cloud.test.ts +36 -497
  108. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +26 -96
  109. package/src/__tests__/host-browser-proxy.test.ts +111 -185
  110. package/src/__tests__/host-browser-routes.test.ts +45 -75
  111. package/src/__tests__/host-browser-ws-events-e2e.test.ts +26 -30
  112. package/src/__tests__/host-cu-proxy.test.ts +56 -111
  113. package/src/__tests__/host-file-proxy.test.ts +44 -98
  114. package/src/__tests__/host-file-read-tool.test.ts +42 -21
  115. package/src/__tests__/host-shell-tool.test.ts +33 -68
  116. package/src/__tests__/host-transfer-pending-interactions.test.ts +2 -18
  117. package/src/__tests__/host-transfer-proxy.test.ts +43 -53
  118. package/src/__tests__/http-user-message-parity.test.ts +0 -6
  119. package/src/__tests__/inbound-slack-persistence.test.ts +31 -0
  120. package/src/__tests__/injector-chain.test.ts +10 -5
  121. package/src/__tests__/injector-pkb-v2-silenced.test.ts +124 -0
  122. package/src/__tests__/inline-command-runner.test.ts +0 -66
  123. package/src/__tests__/inline-skill-load-permissions.test.ts +0 -2
  124. package/src/__tests__/install-skill-routing.test.ts +1 -13
  125. package/src/__tests__/llm-callsite-catalog.test.ts +34 -0
  126. package/src/__tests__/llm-catalog-parity.test.ts +90 -0
  127. package/src/__tests__/llm-context-resolution.test.ts +180 -0
  128. package/src/__tests__/llm-resolver.test.ts +80 -12
  129. package/src/__tests__/llm-usage-store.test.ts +269 -4
  130. package/src/__tests__/log-export-routes.test.ts +89 -0
  131. package/src/__tests__/managed-profile-guard.test.ts +225 -0
  132. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -10
  133. package/src/__tests__/manual-token-reconciliation.test.ts +334 -0
  134. package/src/__tests__/memory-v2-static-injector.test.ts +95 -0
  135. package/src/__tests__/migration-cross-version-compatibility.test.ts +197 -291
  136. package/src/__tests__/migration-export-http.test.ts +33 -26
  137. package/src/__tests__/migration-export-streaming.test.ts +18 -10
  138. package/src/__tests__/migration-export-to-gcs.test.ts +49 -9
  139. package/src/__tests__/migration-import-commit-http.test.ts +66 -21
  140. package/src/__tests__/migration-import-from-gcs.test.ts +50 -9
  141. package/src/__tests__/migration-import-from-url.test.ts +20 -6
  142. package/src/__tests__/migration-import-preflight-http.test.ts +95 -95
  143. package/src/__tests__/migration-parity-persistence.test.ts +62 -25
  144. package/src/__tests__/migration-transport.test.ts +115 -23
  145. package/src/__tests__/migration-validate-http.test.ts +105 -80
  146. package/src/__tests__/migration-wizard.test.ts +133 -27
  147. package/src/__tests__/non-member-access-request.test.ts +1 -1
  148. package/src/__tests__/notification-guardian-path.test.ts +1 -1
  149. package/src/__tests__/oauth-store.test.ts +19 -0
  150. package/src/__tests__/platform-bash-auto-approve.test.ts +21 -12
  151. package/src/__tests__/prechat-onboarding-contract.test.ts +31 -7
  152. package/src/__tests__/pricing.test.ts +68 -4
  153. package/src/__tests__/process-message-background-slack.test.ts +331 -0
  154. package/src/__tests__/provider-managed-proxy-integration.test.ts +153 -17
  155. package/src/__tests__/provider-send-message-override-profile.test.ts +50 -0
  156. package/src/__tests__/provider-usage-tracking.test.ts +208 -0
  157. package/src/__tests__/reaction-persistence.test.ts +9 -6
  158. package/src/__tests__/rebind-secrets-screen.test.ts +53 -16
  159. package/src/__tests__/recording-handler.test.ts +64 -81
  160. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +4 -3
  161. package/src/__tests__/relay-server.test.ts +18 -13
  162. package/src/__tests__/require-fresh-approval.test.ts +13 -22
  163. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
  164. package/src/__tests__/runtime-events-sse-parity.test.ts +3 -4
  165. package/src/__tests__/runtime-events-sse.test.ts +3 -12
  166. package/src/__tests__/search-skills-unified.test.ts +9 -15
  167. package/src/__tests__/secret-ingress-cli.test.ts +2 -5
  168. package/src/__tests__/secret-ingress-http.test.ts +0 -4
  169. package/src/__tests__/secret-onetime-send.test.ts +4 -2
  170. package/src/__tests__/secret-prompt-log-hygiene.test.ts +24 -7
  171. package/src/__tests__/secret-prompter-channel-fallback.test.ts +42 -47
  172. package/src/__tests__/secret-response-routing.test.ts +29 -15
  173. package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -1
  174. package/src/__tests__/secret-scanner.test.ts +2 -545
  175. package/src/__tests__/send-endpoint-busy.test.ts +9 -24
  176. package/src/__tests__/settings-routes.test.ts +1 -1
  177. package/src/__tests__/shell-credential-ref.test.ts +0 -8
  178. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -56
  179. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -11
  180. package/src/__tests__/skill-tool-factory.test.ts +97 -0
  181. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -30
  182. package/src/__tests__/skills-files-catalog-fallback.test.ts +11 -17
  183. package/src/__tests__/slack-inbound-verification.test.ts +1 -62
  184. package/src/__tests__/subagent-fork-notifications.test.ts +57 -47
  185. package/src/__tests__/subagent-manager-notify.test.ts +70 -70
  186. package/src/__tests__/subagent-notify-parent.test.ts +80 -83
  187. package/src/__tests__/system-prompt.test.ts +115 -13
  188. package/src/__tests__/terminal-tools.test.ts +0 -89
  189. package/src/__tests__/thread-backfill.test.ts +945 -31
  190. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -36
  191. package/src/__tests__/tool-execute-pipeline.test.ts +0 -6
  192. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -16
  193. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +9 -19
  194. package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -7
  195. package/src/__tests__/tool-executor.test.ts +12 -19
  196. package/src/__tests__/tool-metrics-listener.test.ts +0 -35
  197. package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
  198. package/src/__tests__/tool-trace-listener.test.ts +0 -17
  199. package/src/__tests__/transfer-progress-screen.test.ts +63 -26
  200. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -149
  201. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -4
  202. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  203. package/src/__tests__/tts-catalog-parity.test.ts +16 -5
  204. package/src/__tests__/usage-attribution.test.ts +247 -0
  205. package/src/__tests__/usage-cli.test.ts +143 -0
  206. package/src/__tests__/usage-grouped-buckets.test.ts +155 -0
  207. package/src/__tests__/usage-routes.test.ts +150 -0
  208. package/src/__tests__/validation-results-screen.test.ts +39 -16
  209. package/src/__tests__/vbundle-pax-and-symlink.test.ts +12 -3
  210. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +49 -137
  211. package/src/__tests__/verification-control-plane-policy.test.ts +4 -7
  212. package/src/__tests__/voice-session-bridge.test.ts +5 -5
  213. package/src/__tests__/workspace-migration-062-drop-memory-v2-edges-json.test.ts +103 -0
  214. package/src/__tests__/workspace-migration-063-release-notes-dynamic-model-context.test.ts +77 -0
  215. package/src/__tests__/workspace-migration-064-unwind-main-agent-opus-seed.test.ts +225 -0
  216. package/src/__tests__/workspace-migration-memory-v2-init.test.ts +8 -30
  217. package/src/acp/index.ts +0 -15
  218. package/src/acp/session-manager.ts +37 -34
  219. package/src/agent/loop.ts +16 -1
  220. package/src/approvals/AGENTS.md +4 -0
  221. package/src/approvals/__tests__/guardian-feed-event.test.ts +10 -3
  222. package/src/approvals/guardian-request-resolvers.ts +10 -2
  223. package/src/backup/__tests__/backup-worker.test.ts +36 -8
  224. package/src/backup/__tests__/paths.test.ts +2 -2
  225. package/src/backup/__tests__/restore.test.ts +45 -28
  226. package/src/backup/backup-worker.ts +36 -2
  227. package/src/backup/paths.ts +9 -6
  228. package/src/browser-session/events.ts +0 -9
  229. package/src/calls/call-store.ts +1 -34
  230. package/src/calls/guardian-question-copy.ts +0 -108
  231. package/src/calls/relay-server.ts +0 -24
  232. package/src/calls/twilio-rest.ts +0 -38
  233. package/src/calls/twilio-routes.ts +1 -1
  234. package/src/calls/voice-session-bridge.ts +7 -38
  235. package/src/channels/types.ts +1 -36
  236. package/src/cli/commands/__tests__/cache.test.ts +152 -5
  237. package/src/cli/commands/__tests__/memory-v2.test.ts +14 -28
  238. package/src/cli/commands/__tests__/trust.test.ts +21 -387
  239. package/src/cli/commands/backup.ts +4 -4
  240. package/src/cli/commands/cache-fs.ts +8 -0
  241. package/src/cli/commands/cache.ts +153 -82
  242. package/src/cli/commands/clients.ts +63 -5
  243. package/src/cli/commands/completions.ts +3 -3
  244. package/src/cli/commands/contacts.ts +231 -76
  245. package/src/cli/commands/keys.ts +4 -1
  246. package/src/cli/commands/memory-v2.ts +24 -52
  247. package/src/cli/commands/oauth/shared.ts +2 -29
  248. package/src/cli/commands/pending.ts +102 -0
  249. package/src/cli/commands/skills.ts +77 -35
  250. package/src/cli/commands/trust.ts +70 -430
  251. package/src/cli/commands/usage.ts +25 -16
  252. package/src/cli/lib/daemon-credential-client.ts +14 -0
  253. package/src/cli/program.ts +2 -0
  254. package/src/cli.ts +0 -21
  255. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  256. package/src/config/bundled-skills/messaging/TOOLS.json +14 -4
  257. package/src/config/env-registry.ts +12 -2
  258. package/src/config/env.ts +3 -14
  259. package/src/config/feature-flag-registry.json +30 -30
  260. package/src/config/llm-callsite-catalog.ts +12 -0
  261. package/src/config/llm-context-resolution.ts +80 -0
  262. package/src/config/llm-resolver.ts +58 -22
  263. package/src/config/loader.ts +3 -3
  264. package/src/config/schema.ts +2 -158
  265. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  266. package/src/config/schemas/call-site-catalog.ts +271 -0
  267. package/src/config/schemas/calls.ts +5 -5
  268. package/src/config/schemas/inference.ts +1 -1
  269. package/src/config/schemas/ingress.ts +1 -1
  270. package/src/config/schemas/llm.ts +31 -3
  271. package/src/config/schemas/memory-retrieval.ts +2 -2
  272. package/src/config/schemas/memory-v2.ts +9 -0
  273. package/src/config/schemas/security.ts +1 -42
  274. package/src/config/schemas/services.ts +6 -6
  275. package/src/config/schemas/skills.ts +5 -5
  276. package/src/config/schemas/tts.ts +1 -1
  277. package/src/config/seed-inference-profiles.ts +117 -0
  278. package/src/config/skills.ts +0 -90
  279. package/src/config/types.ts +3 -6
  280. package/src/contacts/contact-store.ts +0 -17
  281. package/src/contacts/contacts-write.ts +1 -105
  282. package/src/context/window-manager.ts +44 -5
  283. package/src/credential-execution/process-manager.ts +34 -10
  284. package/src/credential-health/credential-health-service.ts +21 -16
  285. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +75 -82
  286. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -9
  287. package/src/daemon/connection-policy.ts +1 -26
  288. package/src/daemon/conversation-agent-loop-handlers.ts +53 -4
  289. package/src/daemon/conversation-agent-loop.ts +277 -36
  290. package/src/daemon/conversation-history.ts +8 -8
  291. package/src/daemon/conversation-launch.ts +20 -135
  292. package/src/daemon/conversation-lifecycle.ts +1 -1
  293. package/src/daemon/conversation-messaging.ts +1 -0
  294. package/src/daemon/conversation-process.ts +83 -163
  295. package/src/daemon/conversation-runtime-assembly.ts +219 -76
  296. package/src/daemon/conversation-slash.ts +47 -5
  297. package/src/daemon/conversation-store.ts +7 -31
  298. package/src/daemon/conversation-surfaces.ts +22 -28
  299. package/src/daemon/conversation-tool-setup.ts +3 -33
  300. package/src/daemon/conversation-usage.ts +36 -0
  301. package/src/daemon/conversation.ts +117 -233
  302. package/src/daemon/daemon-control.ts +3 -71
  303. package/src/daemon/daemon-skill-host.ts +8 -11
  304. package/src/daemon/dictation-profile-store.ts +2 -26
  305. package/src/daemon/first-greeting.ts +44 -156
  306. package/src/daemon/handlers/config-channels.ts +12 -12
  307. package/src/daemon/handlers/config-ingress.ts +4 -165
  308. package/src/daemon/handlers/config-model.ts +1 -1
  309. package/src/daemon/handlers/config-voice.ts +0 -42
  310. package/src/daemon/handlers/conversations.ts +11 -190
  311. package/src/daemon/handlers/recording.ts +26 -158
  312. package/src/daemon/handlers/shared.ts +23 -71
  313. package/src/daemon/handlers/skills.ts +42 -93
  314. package/src/daemon/host-bash-proxy.ts +67 -45
  315. package/src/daemon/host-browser-proxy.ts +65 -27
  316. package/src/daemon/host-cu-proxy.ts +40 -39
  317. package/src/daemon/host-file-proxy.ts +58 -37
  318. package/src/daemon/host-transfer-proxy.ts +84 -46
  319. package/src/daemon/lifecycle.ts +49 -15
  320. package/src/daemon/message-types/conversations.ts +7 -0
  321. package/src/daemon/message-types/host-bash.ts +1 -0
  322. package/src/daemon/message-types/host-cu.ts +1 -0
  323. package/src/daemon/message-types/host-file.ts +1 -0
  324. package/src/daemon/message-types/host-transfer.ts +1 -0
  325. package/src/daemon/message-types/messages.ts +10 -9
  326. package/src/daemon/message-types/workspace.ts +1 -1
  327. package/src/daemon/process-message.ts +102 -239
  328. package/src/daemon/server.ts +13 -462
  329. package/src/daemon/shutdown-handlers.ts +2 -2
  330. package/src/daemon/tool-side-effects.ts +125 -107
  331. package/src/daemon/trust-context.ts +13 -0
  332. package/src/daemon/wake-target-adapter.ts +4 -9
  333. package/src/events/domain-events.ts +0 -8
  334. package/src/events/tool-audit-listener.ts +3 -1
  335. package/src/events/tool-domain-event-publisher.ts +0 -10
  336. package/src/events/tool-metrics-listener.ts +0 -17
  337. package/src/events/tool-trace-listener.ts +0 -14
  338. package/src/filing/filing-service.ts +13 -1
  339. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +6 -2
  340. package/src/heartbeat/heartbeat-service.ts +23 -5
  341. package/src/home/__tests__/feed-writer.test.ts +0 -4
  342. package/src/home/__tests__/relationship-state-writer.test.ts +30 -0
  343. package/src/home/feed-writer.ts +1 -2
  344. package/src/home/relationship-state-writer.ts +16 -3
  345. package/src/ipc/__tests__/browser-ipc.test.ts +2 -12
  346. package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -1
  347. package/src/ipc/assistant-server.ts +3 -10
  348. package/src/ipc/routes/__tests__/memory-v2-backfill.test.ts +39 -20
  349. package/src/ipc/routes/route-adapter.ts +1 -1
  350. package/src/ipc/routes/trust-rules.test.ts +0 -95
  351. package/src/ipc/skill-ipc-types.ts +41 -0
  352. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +13 -27
  353. package/src/ipc/skill-routes/__tests__/identity.test.ts +4 -23
  354. package/src/ipc/skill-routes/events.ts +12 -23
  355. package/src/ipc/skill-routes/identity.ts +4 -17
  356. package/src/ipc/skill-routes/index.ts +1 -1
  357. package/src/ipc/skill-server.ts +6 -39
  358. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +0 -8
  359. package/src/live-voice/protocol.ts +4 -13
  360. package/src/mcp/manager.ts +0 -5
  361. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +55 -0
  362. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +127 -0
  363. package/src/memory/app-git-service.ts +0 -32
  364. package/src/memory/app-store.ts +154 -0
  365. package/src/memory/attachments-store.ts +6 -0
  366. package/src/memory/context-search/sources/memory-v2.ts +578 -0
  367. package/src/memory/context-search/sources/memory.ts +5 -0
  368. package/src/memory/context-search/sources/pkb.ts +10 -1
  369. package/src/memory/context-search/sources/workspace.ts +3 -2
  370. package/src/memory/conversation-crud.ts +29 -4
  371. package/src/memory/conversation-disk-view.ts +1 -5
  372. package/src/memory/conversation-starter-checkpoints.ts +63 -0
  373. package/src/memory/db-connection.ts +62 -0
  374. package/src/memory/db-init.ts +14 -0
  375. package/src/memory/embedding-backend.ts +3 -21
  376. package/src/memory/embedding-gemini.ts +0 -2
  377. package/src/memory/embedding-local.ts +6 -6
  378. package/src/memory/embedding-ollama.ts +6 -6
  379. package/src/memory/embedding-openai.ts +6 -6
  380. package/src/memory/embedding-types.ts +21 -0
  381. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +3 -7
  382. package/src/memory/graph/conversation-graph-memory.ts +35 -13
  383. package/src/memory/graph/injection.test.ts +2 -2
  384. package/src/memory/graph/injection.ts +1 -1
  385. package/src/memory/guardian-action-store.ts +0 -83
  386. package/src/memory/guardian-approvals.ts +0 -48
  387. package/src/memory/indexer.ts +1 -15
  388. package/src/memory/job-handlers/conversation-starters.ts +36 -53
  389. package/src/memory/job-utils.ts +0 -6
  390. package/src/memory/jobs-store.ts +0 -1
  391. package/src/memory/jobs-worker.ts +2 -16
  392. package/src/memory/llm-request-log-store.ts +0 -41
  393. package/src/memory/llm-usage-store.ts +129 -43
  394. package/src/memory/memory-v2-activation-log-store.ts +115 -0
  395. package/src/memory/migrations/233-document-conversations.ts +54 -0
  396. package/src/memory/migrations/234-memory-v2-activation-logs.ts +55 -0
  397. package/src/memory/migrations/235-llm-usage-attribution.ts +31 -0
  398. package/src/memory/migrations/235-slack-compaction-watermark.ts +44 -0
  399. package/src/memory/migrations/236-tool-invocations-matched-rule-id.ts +26 -0
  400. package/src/memory/migrations/__tests__/234-memory-v2-activation-logs.test.ts +182 -0
  401. package/src/memory/migrations/index.ts +14 -0
  402. package/src/memory/migrations/registry.ts +24 -0
  403. package/src/memory/raw-query.ts +2 -68
  404. package/src/memory/schema/conversations.ts +7 -0
  405. package/src/memory/schema/infrastructure.ts +25 -0
  406. package/src/memory/search/semantic.ts +5 -16
  407. package/src/memory/tool-usage-store.ts +2 -0
  408. package/src/memory/usage-buckets.ts +40 -1
  409. package/src/memory/usage-grouped-buckets.ts +127 -0
  410. package/src/memory/v2/__tests__/activation.test.ts +289 -90
  411. package/src/memory/v2/__tests__/backfill-jobs.test.ts +2 -129
  412. package/src/memory/v2/__tests__/consolidation-job.test.ts +28 -11
  413. package/src/memory/v2/__tests__/edge-index.test.ts +278 -0
  414. package/src/memory/v2/__tests__/injection.test.ts +384 -15
  415. package/src/memory/v2/__tests__/migration.test.ts +64 -36
  416. package/src/memory/v2/__tests__/page-store.test.ts +191 -8
  417. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +181 -0
  418. package/src/memory/v2/__tests__/skill-store.test.ts +115 -3
  419. package/src/memory/v2/__tests__/static-context.test.ts +153 -0
  420. package/src/memory/v2/activation.ts +168 -97
  421. package/src/memory/v2/backfill-jobs.ts +15 -100
  422. package/src/memory/v2/consolidation-job.ts +14 -12
  423. package/src/memory/v2/edge-index.ts +191 -0
  424. package/src/memory/v2/injection.ts +182 -58
  425. package/src/memory/v2/migration.ts +57 -64
  426. package/src/memory/v2/now-text.ts +2 -3
  427. package/src/memory/v2/page-store.ts +168 -31
  428. package/src/memory/v2/prompts/consolidation.ts +118 -42
  429. package/src/memory/v2/prompts/sweep.ts +3 -3
  430. package/src/memory/v2/skill-store.ts +55 -7
  431. package/src/memory/v2/static-context.ts +62 -0
  432. package/src/memory/v2/types.ts +10 -20
  433. package/src/memory/validation.ts +0 -11
  434. package/src/messaging/draft-store.ts +0 -6
  435. package/src/messaging/provider-types.ts +8 -0
  436. package/src/messaging/provider.ts +7 -0
  437. package/src/messaging/providers/gmail/client.ts +1 -121
  438. package/src/messaging/providers/outlook/client.ts +0 -73
  439. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +226 -0
  440. package/src/messaging/providers/slack/adapter.ts +122 -21
  441. package/src/messaging/providers/slack/backfill.test.ts +95 -6
  442. package/src/messaging/providers/slack/backfill.ts +89 -11
  443. package/src/messaging/providers/slack/client.ts +10 -124
  444. package/src/messaging/providers/slack/message-metadata.ts +12 -2
  445. package/src/messaging/providers/slack/render-transcript.test.ts +56 -0
  446. package/src/messaging/providers/slack/render-transcript.ts +126 -25
  447. package/src/messaging/providers/slack/types.ts +1 -0
  448. package/src/oauth/connection-resolver.test.ts +8 -0
  449. package/src/oauth/connection-resolver.ts +8 -16
  450. package/src/oauth/credential-token-resolver.ts +97 -0
  451. package/src/oauth/manual-token-connection.ts +30 -34
  452. package/src/oauth/oauth-store.ts +6 -4
  453. package/src/outbound-proxy/certs.ts +0 -7
  454. package/src/outbound-proxy/config.ts +0 -74
  455. package/src/outbound-proxy/health.ts +0 -44
  456. package/src/outbound-proxy/index.ts +0 -22
  457. package/src/permissions/approval-provenance.test.ts +184 -0
  458. package/src/permissions/approval-provenance.ts +70 -0
  459. package/src/permissions/checker.ts +4 -1
  460. package/src/permissions/gateway-threshold-reader.ts +4 -1
  461. package/src/permissions/prompter.ts +9 -2
  462. package/src/permissions/secret-prompter.ts +21 -48
  463. package/src/permissions/types.ts +33 -0
  464. package/src/permissions/workspace-policy.ts +0 -5
  465. package/src/platform/sync-identity.ts +0 -8
  466. package/src/plugins/defaults/injectors.ts +69 -2
  467. package/src/plugins/defaults/overflow-reduce.ts +3 -2
  468. package/src/plugins/types.ts +8 -0
  469. package/src/prompts/system-prompt.ts +34 -70
  470. package/src/prompts/templates/BOOTSTRAP.md +52 -6
  471. package/src/prompts/update-bulletin-job.ts +2 -0
  472. package/src/providers/__tests__/retry-callsite.test.ts +138 -1
  473. package/src/providers/anthropic/client.ts +72 -33
  474. package/src/providers/call-site-routing.ts +42 -3
  475. package/src/providers/gemini/client.ts +18 -2
  476. package/src/providers/managed-proxy/context.ts +0 -5
  477. package/src/providers/model-catalog.ts +105 -19
  478. package/src/providers/openai/chat-completions-provider.ts +6 -0
  479. package/src/providers/openai/responses-provider.ts +7 -1
  480. package/src/providers/provider-send-message.ts +45 -2
  481. package/src/providers/ratelimit.ts +7 -2
  482. package/src/providers/registry.ts +14 -9
  483. package/src/providers/retry.ts +96 -8
  484. package/src/providers/types.ts +13 -0
  485. package/src/providers/usage-tracking.ts +96 -0
  486. package/src/runtime/AGENTS.md +10 -6
  487. package/src/runtime/__tests__/agent-wake.test.ts +89 -0
  488. package/src/runtime/agent-wake.ts +39 -2
  489. package/src/runtime/assistant-event-hub.ts +541 -45
  490. package/src/runtime/assistant-event.ts +1 -6
  491. package/src/runtime/auth/context.ts +0 -9
  492. package/src/runtime/auth/middleware.ts +1 -1
  493. package/src/runtime/auth/route-policy.ts +11 -9
  494. package/src/runtime/auth/token-service.ts +0 -11
  495. package/src/runtime/channel-approvals.ts +6 -2
  496. package/src/runtime/channel-verification-service.ts +3 -5
  497. package/src/runtime/http-errors.ts +0 -34
  498. package/src/runtime/http-router.ts +6 -3
  499. package/src/runtime/http-server.ts +22 -82
  500. package/src/runtime/http-types.ts +5 -0
  501. package/src/runtime/interactive-ui.ts +0 -1
  502. package/src/runtime/middleware/auth.ts +0 -20
  503. package/src/runtime/migrations/__tests__/v1-test-helpers.ts +112 -0
  504. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +11 -4
  505. package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +253 -0
  506. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +19 -6
  507. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +71 -27
  508. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +41 -2
  509. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +143 -79
  510. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +143 -23
  511. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +2 -2
  512. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +371 -0
  513. package/src/runtime/migrations/migration-transport.ts +46 -13
  514. package/src/runtime/migrations/migration-wizard.ts +2 -2
  515. package/src/runtime/migrations/origin-mode.ts +40 -0
  516. package/src/runtime/migrations/vbundle-builder.ts +133 -79
  517. package/src/runtime/migrations/vbundle-import-analyzer.ts +9 -7
  518. package/src/runtime/migrations/vbundle-importer.ts +7 -7
  519. package/src/runtime/migrations/vbundle-metadata-merge.ts +1 -1
  520. package/src/runtime/migrations/vbundle-streaming-importer.ts +3 -3
  521. package/src/runtime/migrations/vbundle-streaming-validator.ts +48 -26
  522. package/src/runtime/migrations/vbundle-validator.ts +214 -41
  523. package/src/runtime/pending-interactions.ts +13 -4
  524. package/src/runtime/routes/__tests__/acp-routes.test.ts +0 -1
  525. package/src/runtime/routes/__tests__/backup-routes.test.ts +28 -19
  526. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +235 -0
  527. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +58 -0
  528. package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +54 -0
  529. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +19 -6
  530. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +7 -7
  531. package/src/runtime/routes/acp-routes.test.ts +0 -3
  532. package/src/runtime/routes/acp-routes.ts +3 -7
  533. package/src/runtime/routes/app-management-routes.ts +18 -9
  534. package/src/runtime/routes/approval-routes.ts +55 -14
  535. package/src/runtime/routes/avatar-routes.ts +3 -5
  536. package/src/runtime/routes/browser-routes.ts +1 -15
  537. package/src/runtime/routes/channel-guardian-routes.ts +1 -5
  538. package/src/runtime/routes/channel-readiness-routes.ts +3 -7
  539. package/src/runtime/routes/channel-route-shared.ts +2 -28
  540. package/src/runtime/routes/client-routes.ts +45 -12
  541. package/src/runtime/routes/consolidation-routes.ts +115 -0
  542. package/src/runtime/routes/conversation-list-routes.ts +12 -29
  543. package/src/runtime/routes/conversation-management-routes.ts +14 -51
  544. package/src/runtime/routes/conversation-query-routes.ts +120 -8
  545. package/src/runtime/routes/conversation-routes.ts +44 -528
  546. package/src/runtime/routes/conversation-starter-routes.ts +19 -40
  547. package/src/runtime/routes/documents-routes.ts +53 -18
  548. package/src/runtime/routes/events-routes.ts +59 -91
  549. package/src/runtime/routes/filing-routes.ts +18 -1
  550. package/src/runtime/routes/guardian-action-routes.ts +4 -9
  551. package/src/runtime/routes/host-bash-routes.ts +3 -2
  552. package/src/runtime/routes/host-browser-routes.ts +9 -33
  553. package/src/runtime/routes/host-cu-routes.ts +6 -1
  554. package/src/runtime/routes/host-file-routes.ts +3 -2
  555. package/src/runtime/routes/host-transfer-routes.ts +11 -15
  556. package/src/runtime/routes/identity-routes.ts +78 -6
  557. package/src/runtime/routes/inbound-message-handler.ts +580 -137
  558. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -88
  559. package/src/runtime/routes/inbound-stages/background-dispatch.ts +3 -0
  560. package/src/runtime/routes/index.ts +4 -0
  561. package/src/runtime/routes/integrations/slack/channel.ts +0 -24
  562. package/src/runtime/routes/llm-call-sites-routes.ts +22 -0
  563. package/src/runtime/routes/memory-v2-routes.ts +10 -15
  564. package/src/runtime/routes/migration-routes.ts +188 -31
  565. package/src/runtime/routes/playground/guard.ts +1 -1
  566. package/src/runtime/routes/playground/index.ts +0 -2
  567. package/src/runtime/routes/recording-routes.ts +4 -24
  568. package/src/runtime/routes/rename-conversation-routes.ts +2 -6
  569. package/src/runtime/routes/schedule-routes.ts +3 -6
  570. package/src/runtime/routes/secret-routes.ts +87 -18
  571. package/src/runtime/routes/settings-routes.ts +29 -28
  572. package/src/runtime/routes/skills-routes.ts +12 -31
  573. package/src/runtime/routes/suggest-trust-rule-routes.ts +32 -1
  574. package/src/runtime/routes/task-routes.ts +6 -6
  575. package/src/runtime/routes/trust-rules-routes.ts +3 -94
  576. package/src/runtime/routes/types.ts +4 -4
  577. package/src/runtime/routes/upgrade-broadcast-routes.ts +3 -10
  578. package/src/runtime/routes/usage-routes.ts +87 -10
  579. package/src/runtime/routes/user-routes.ts +17 -31
  580. package/src/runtime/routes/work-items-routes.ts +1 -4
  581. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -2
  582. package/src/runtime/services/analyze-conversation.ts +7 -17
  583. package/src/runtime/services/conversation-serializer.ts +2 -4
  584. package/src/runtime/verification-outbound-actions.ts +1 -1
  585. package/src/runtime/verification-rate-limiter.ts +1 -1
  586. package/src/schedule/schedule-store.ts +0 -16
  587. package/src/security/secret-scanner.ts +14 -547
  588. package/src/security/secure-keys.ts +31 -11
  589. package/src/security/token-manager.ts +7 -3
  590. package/src/signals/cancel.ts +16 -25
  591. package/src/signals/conversation-undo.ts +2 -27
  592. package/src/signals/emit-event.ts +1 -2
  593. package/src/signals/user-message.ts +108 -22
  594. package/src/skills/catalog-install.ts +1 -0
  595. package/src/skills/clawhub.ts +2 -2
  596. package/src/skills/inline-command-runner.ts +1 -7
  597. package/src/subagent/manager.ts +67 -84
  598. package/src/tasks/task-store.ts +1 -28
  599. package/src/telemetry/types.ts +6 -0
  600. package/src/telemetry/usage-telemetry-reporter.test.ts +38 -15
  601. package/src/telemetry/usage-telemetry-reporter.ts +3 -5
  602. package/src/tools/acp/spawn.test.ts +1 -2
  603. package/src/tools/acp/steer.test.ts +1 -2
  604. package/src/tools/browser/__tests__/browser-status.test.ts +44 -127
  605. package/src/tools/browser/browser-execution.ts +31 -147
  606. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +92 -68
  607. package/src/tools/browser/cdp-client/factory.ts +48 -76
  608. package/src/tools/browser/cdp-client/index.ts +1 -14
  609. package/src/tools/executor.ts +44 -31
  610. package/src/tools/host-filesystem/edit.ts +3 -2
  611. package/src/tools/host-filesystem/read.ts +3 -2
  612. package/src/tools/host-filesystem/transfer.test.ts +45 -42
  613. package/src/tools/host-filesystem/transfer.ts +4 -3
  614. package/src/tools/host-filesystem/write.ts +3 -2
  615. package/src/tools/host-terminal/host-shell.ts +4 -3
  616. package/src/tools/network/script-proxy/index.ts +1 -10
  617. package/src/tools/permission-checker.ts +66 -1
  618. package/src/tools/skills/sandbox-runner.ts +1 -6
  619. package/src/tools/skills/skill-tool-factory.ts +32 -0
  620. package/src/tools/terminal/safe-env.ts +1 -0
  621. package/src/tools/terminal/shell.ts +2 -78
  622. package/src/tools/types.ts +12 -39
  623. package/src/tts/__tests__/provider-catalog.test.ts +2 -2
  624. package/src/tts/provider-catalog.ts +1 -1
  625. package/src/usage/actors.ts +2 -1
  626. package/src/usage/attribution.ts +185 -0
  627. package/src/usage/pricing.ts +166 -0
  628. package/src/usage/types.ts +14 -0
  629. package/src/util/json.ts +13 -0
  630. package/src/util/logger.ts +3 -3
  631. package/src/util/pricing.ts +50 -3
  632. package/src/work-items/work-item-runner.ts +15 -42
  633. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +4 -3
  634. package/src/workspace/migrations/052-seed-default-inference-profiles.ts +3 -3
  635. package/src/workspace/migrations/060-memory-v2-init.ts +2 -18
  636. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +59 -0
  637. package/src/workspace/migrations/062-drop-memory-v2-edges-json.ts +27 -0
  638. package/src/workspace/migrations/063-release-notes-dynamic-model-context.ts +70 -0
  639. package/src/workspace/migrations/064-unwind-main-agent-opus-seed.ts +64 -0
  640. package/src/workspace/migrations/registry.ts +8 -0
  641. package/src/workspace/provider-commit-message-generator.ts +3 -3
  642. package/src/__tests__/sandbox-diagnostics.test.ts +0 -138
  643. package/src/__tests__/sandbox-host-parity.test.ts +0 -1024
  644. package/src/__tests__/secret-detection-handler.test.ts +0 -67
  645. package/src/__tests__/secret-scanner-executor.test.ts +0 -450
  646. package/src/__tests__/tcc-sandbox-deny.test.ts +0 -198
  647. package/src/__tests__/terminal-sandbox.test.ts +0 -374
  648. package/src/__tests__/tool-notification-listener.test.ts +0 -65
  649. package/src/context/__tests__/microcompact.test.ts +0 -805
  650. package/src/context/microcompact.ts +0 -443
  651. package/src/daemon/handlers/slack-channel-oauth-install.ts +0 -197
  652. package/src/events/tool-notification-listener.ts +0 -17
  653. package/src/ipc/routes/__tests__/memory-v2-validate.test.ts +0 -219
  654. package/src/memory/v2/__tests__/edges.test.ts +0 -435
  655. package/src/memory/v2/edges.ts +0 -217
  656. package/src/prompts/__tests__/system-prompt-memory-v2.test.ts +0 -197
  657. package/src/runtime/__tests__/chrome-extension-registry.test.ts +0 -518
  658. package/src/runtime/__tests__/client-registry.test.ts +0 -271
  659. package/src/runtime/chrome-extension-registry.ts +0 -368
  660. package/src/runtime/client-registry.ts +0 -254
  661. package/src/runtime/routes/inbound-stages/verification-intercept.ts +0 -329
  662. package/src/tools/secret-detection-handler.ts +0 -269
  663. package/src/tools/terminal/backends/native.ts +0 -327
  664. package/src/tools/terminal/backends/types.ts +0 -37
  665. package/src/tools/terminal/sandbox-diagnostics.ts +0 -87
  666. package/src/tools/terminal/sandbox.ts +0 -40
@@ -39,6 +39,14 @@ mock.module("../security/secure-keys.js", () => ({
39
39
  getSecureKeyAsync: async () => mockAccessToken,
40
40
  }));
41
41
 
42
+ mock.module("./credential-token-resolver.js", () => ({
43
+ getConnectionAccessTokenResult: async () => ({
44
+ value: mockAccessToken,
45
+ unreachable: false,
46
+ key: "mock-key",
47
+ }),
48
+ }));
49
+
42
50
  mock.module("../config/loader.js", () => ({
43
51
  getConfig: () => mockConfig,
44
52
  }));
@@ -1,15 +1,14 @@
1
- import { getIsPlatform } from "../config/env-registry.js";
2
- import { getConfig, getNestedValue, loadRawConfig } from "../config/loader.js";
1
+ import { getConfig } from "../config/loader.js";
3
2
  import {
4
3
  getServiceMode,
5
4
  type Services,
6
5
  ServicesSchema,
7
6
  } from "../config/schemas/services.js";
8
7
  import { VellumPlatformClient } from "../platform/client.js";
9
- import { getSecureKeyAsync } from "../security/secure-keys.js";
10
8
  import { getLogger } from "../util/logger.js";
11
9
  import { BYOOAuthConnection } from "./byo-connection.js";
12
10
  import type { OAuthConnection } from "./connection.js";
11
+ import { getConnectionAccessTokenResult } from "./credential-token-resolver.js";
13
12
  import { getActiveConnection, getProvider } from "./oauth-store.js";
14
13
  import { PlatformOAuthConnection } from "./platform-connection.js";
15
14
 
@@ -52,15 +51,7 @@ export async function resolveOAuthConnection(
52
51
 
53
52
  if (managedKey && managedKey in ServicesSchema.shape) {
54
53
  const services: Services = getConfig().services;
55
- const explicitMode = getServiceMode(services, managedKey as keyof Services);
56
- // Treat as managed when explicitly configured OR when on a platform
57
- // instance and the user hasn't explicitly opted into "your-own".
58
- const effectivelyManaged =
59
- explicitMode === "managed" ||
60
- (getIsPlatform() &&
61
- getNestedValue(loadRawConfig(), `services.${managedKey}.mode`) ===
62
- undefined);
63
- if (effectivelyManaged) {
54
+ if (getServiceMode(services, managedKey as keyof Services) === "managed") {
64
55
  const client = await VellumPlatformClient.create();
65
56
  if (!client || !client.platformAssistantId) {
66
57
  const detail = !client
@@ -105,10 +96,11 @@ export async function resolveOAuthConnection(
105
96
  );
106
97
  }
107
98
 
108
- const accessToken = await getSecureKeyAsync(
109
- `oauth_connection/${conn.id}/access_token`,
110
- );
111
- if (!accessToken) {
99
+ const tokenResult = await getConnectionAccessTokenResult({
100
+ provider,
101
+ connectionId: conn.id,
102
+ });
103
+ if (!tokenResult.value) {
112
104
  throw new Error(
113
105
  `OAuth connection for "${provider}" exists but has no access token. Re-authorize with \`assistant oauth connect ${provider}\`.`,
114
106
  );
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Centralized access-token key resolution for OAuth and manual-token providers.
3
+ *
4
+ * All code that needs to read or check the presence of a provider's access
5
+ * token should go through {@link getConnectionAccessTokenResult} rather than
6
+ * inlining provider-specific path logic. This ensures that `oauth status`,
7
+ * `oauth ping`, credential health checks, and runtime token lookups all
8
+ * agree on where the access token lives.
9
+ *
10
+ * Manual-token providers (e.g. slack_channel, telegram) store their primary
11
+ * token under `credential/<provider>/<field>` via the generic credential
12
+ * store, while standard OAuth providers use `oauth_connection/<id>/access_token`.
13
+ */
14
+
15
+ import { oauthConnectionAccessTokenPath } from "@vellumai/credential-storage";
16
+
17
+ import { credentialKey } from "../security/credential-key.js";
18
+ import {
19
+ getSecureKeyResultAsync,
20
+ type SecureKeyResult,
21
+ } from "../security/secure-keys.js";
22
+
23
+ // ── Types ─────────────────────────────────────────────────────────────
24
+
25
+ export interface ConnectionAccessTokenResult {
26
+ /** The access token value, or undefined if not found / backend unreachable. */
27
+ value: string | undefined;
28
+ /** True when the credential backend is temporarily unreachable. */
29
+ unreachable: boolean;
30
+ /** The secure-store key that was resolved for this provider + connection. */
31
+ key: string;
32
+ }
33
+
34
+ // ── Helpers ──────────────────────────────────────────────────────────
35
+
36
+ /**
37
+ * Return the secure-store key holding the primary access token for a
38
+ * manual-token provider, or null for OAuth providers whose tokens live at
39
+ * `oauth_connection/<id>/access_token`.
40
+ *
41
+ * Manual-token providers store their tokens under `credential/<provider>/<field>`
42
+ * via the generic credential store, so any code that validates tokens for these
43
+ * providers (e.g. credential-health checks) must resolve the path through here
44
+ * rather than assuming the OAuth access-token path.
45
+ */
46
+ export function manualTokenAccessCredentialKey(
47
+ provider: string,
48
+ ): string | null {
49
+ switch (provider) {
50
+ case "slack_channel":
51
+ return credentialKey("slack_channel", "bot_token");
52
+ case "telegram":
53
+ return credentialKey("telegram", "bot_token");
54
+ default:
55
+ return null;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Resolve the secure-store key for a provider's access token.
61
+ *
62
+ * - `slack_channel` -> `credential/slack_channel/bot_token`
63
+ * - `telegram` -> `credential/telegram/bot_token`
64
+ * - all normal OAuth -> `oauth_connection/<connectionId>/access_token`
65
+ */
66
+ export function resolveAccessTokenKey(
67
+ provider: string,
68
+ connectionId: string,
69
+ ): string {
70
+ return (
71
+ manualTokenAccessCredentialKey(provider) ??
72
+ oauthConnectionAccessTokenPath(connectionId)
73
+ );
74
+ }
75
+
76
+ // ── Public API ───────────────────────────────────────────────────────
77
+
78
+ /**
79
+ * Look up the access token for a provider/connection pair, resolving the
80
+ * correct secure-store key automatically.
81
+ *
82
+ * Returns the token value, whether the backend was unreachable, and the
83
+ * key that was used — giving callers everything they need to diagnose
84
+ * token-path mismatches.
85
+ */
86
+ export async function getConnectionAccessTokenResult(opts: {
87
+ provider: string;
88
+ connectionId: string;
89
+ }): Promise<ConnectionAccessTokenResult> {
90
+ const key = resolveAccessTokenKey(opts.provider, opts.connectionId);
91
+ const result: SecureKeyResult = await getSecureKeyResultAsync(key);
92
+ return {
93
+ value: result.value,
94
+ unreachable: result.unreachable,
95
+ key,
96
+ };
97
+ }
@@ -9,7 +9,8 @@
9
9
  */
10
10
 
11
11
  import { credentialKey } from "../security/credential-key.js";
12
- import { getSecureKeyAsync } from "../security/secure-keys.js";
12
+ import { getSecureKeyResultAsync } from "../security/secure-keys.js";
13
+ import { getLogger } from "../util/logger.js";
13
14
  import {
14
15
  createConnection,
15
16
  deleteConnection,
@@ -18,32 +19,15 @@ import {
18
19
  upsertApp,
19
20
  } from "./oauth-store.js";
20
21
 
22
+ // Re-export from the centralized resolver so existing callers that import
23
+ // from this module continue to work without changes.
24
+ export { manualTokenAccessCredentialKey } from "./credential-token-resolver.js";
25
+
26
+ const log = getLogger("manual-token-connection");
27
+
21
28
  /** Sentinel client_id used for non-OAuth providers that don't have a real app. */
22
29
  const MANUAL_TOKEN_CLIENT_ID = "manual-config";
23
30
 
24
- /**
25
- * Return the secure-store key holding the primary access token for a
26
- * manual-token provider, or null for OAuth providers whose tokens live at
27
- * `oauth_connection/<id>/access_token`.
28
- *
29
- * Manual-token providers store their tokens under `credential/<provider>/<field>`
30
- * via the generic credential store, so any code that validates tokens for these
31
- * providers (e.g. credential-health checks) must resolve the path through here
32
- * rather than assuming the OAuth access-token path.
33
- */
34
- export function manualTokenAccessCredentialKey(
35
- provider: string,
36
- ): string | null {
37
- switch (provider) {
38
- case "slack_channel":
39
- return credentialKey("slack_channel", "bot_token");
40
- case "telegram":
41
- return credentialKey("telegram", "bot_token");
42
- default:
43
- return null;
44
- }
45
- }
46
-
47
31
  /**
48
32
  * Ensure an active oauth_connection row exists for the given manual-token
49
33
  * provider. Creates the synthetic oauth_app row on first use.
@@ -101,13 +85,19 @@ export async function syncManualTokenConnection(
101
85
  ): Promise<void> {
102
86
  switch (provider) {
103
87
  case "telegram": {
104
- const hasBotToken = !!(await getSecureKeyAsync(
88
+ const botTokenResult = await getSecureKeyResultAsync(
105
89
  credentialKey("telegram", "bot_token"),
106
- ));
107
- const hasWebhookSecret = !!(await getSecureKeyAsync(
90
+ );
91
+ const webhookSecretResult = await getSecureKeyResultAsync(
108
92
  credentialKey("telegram", "webhook_secret"),
109
- ));
110
- if (hasBotToken && hasWebhookSecret) {
93
+ );
94
+ if (botTokenResult.unreachable || webhookSecretResult.unreachable) {
95
+ log.warn(
96
+ "Skipping telegram manual-token reconciliation — credential backend unreachable",
97
+ );
98
+ return;
99
+ }
100
+ if (botTokenResult.value && webhookSecretResult.value) {
111
101
  await ensureManualTokenConnection(provider, accountInfo);
112
102
  } else {
113
103
  removeManualTokenConnection(provider);
@@ -116,13 +106,19 @@ export async function syncManualTokenConnection(
116
106
  }
117
107
 
118
108
  case "slack_channel": {
119
- const hasBotToken = !!(await getSecureKeyAsync(
109
+ const botTokenResult = await getSecureKeyResultAsync(
120
110
  credentialKey("slack_channel", "bot_token"),
121
- ));
122
- const hasAppToken = !!(await getSecureKeyAsync(
111
+ );
112
+ const appTokenResult = await getSecureKeyResultAsync(
123
113
  credentialKey("slack_channel", "app_token"),
124
- ));
125
- if (hasBotToken && hasAppToken) {
114
+ );
115
+ if (botTokenResult.unreachable || appTokenResult.unreachable) {
116
+ log.warn(
117
+ "Skipping slack_channel manual-token reconciliation — credential backend unreachable",
118
+ );
119
+ return;
120
+ }
121
+ if (botTokenResult.value && appTokenResult.value) {
126
122
  await ensureManualTokenConnection(provider, accountInfo);
127
123
  } else {
128
124
  removeManualTokenConnection(provider);
@@ -33,6 +33,7 @@ import {
33
33
  } from "../security/secure-keys.js";
34
34
  import { getLogger } from "../util/logger.js";
35
35
  import type { AvailableScopes } from "./connect-types.js";
36
+ import { getConnectionAccessTokenResult } from "./credential-token-resolver.js";
36
37
  import { tryRevokeOAuthToken } from "./revoke.js";
37
38
 
38
39
  const log = getLogger("oauth-store");
@@ -895,10 +896,11 @@ export function listActiveConnectionsByProvider(
895
896
  export async function isProviderConnected(provider: string): Promise<boolean> {
896
897
  const conn = getActiveConnection(provider);
897
898
  if (!conn || conn.status !== "active") return false;
898
- return (
899
- (await getSecureKeyAsync(oauthConnectionAccessTokenPath(conn.id))) !==
900
- undefined
901
- );
899
+ const tokenResult = await getConnectionAccessTokenResult({
900
+ provider,
901
+ connectionId: conn.id,
902
+ });
903
+ return tokenResult.value !== undefined;
902
904
  }
903
905
 
904
906
  /**
@@ -269,13 +269,6 @@ export async function ensureCombinedCABundle(
269
269
  }
270
270
  }
271
271
 
272
- /**
273
- * Return the path to the combined CA bundle for use as SSL_CERT_FILE.
274
- */
275
- export function getCombinedCAPath(dataDir: string): string {
276
- return join(dataDir, "proxy-ca", COMBINED_CA_FILENAME);
277
- }
278
-
279
272
  /**
280
273
  * Return the path to the local CA cert for use as NODE_EXTRA_CA_CERTS.
281
274
  */
@@ -18,77 +18,3 @@ export interface SidecarConfig {
18
18
  /** Log level for the sidecar process. */
19
19
  logLevel: "debug" | "info" | "warn" | "error";
20
20
  }
21
-
22
- /**
23
- * Parse and validate sidecar configuration from environment variables.
24
- *
25
- * Environment variables:
26
- * PROXY_PORT - Port to listen on (default: 8080)
27
- * PROXY_HOST - Host to bind to (default: "0.0.0.0")
28
- * PROXY_HEALTH_PORT - Port for health/readiness endpoints (default: 8081)
29
- * PROXY_CA_DIR - Path to CA directory for MITM (optional)
30
- * PROXY_LOG_LEVEL - Log level: debug | info | warn | error (default: "info")
31
- */
32
- export function loadConfig(
33
- env: Record<string, string | undefined> = process.env,
34
- ): SidecarConfig {
35
- const port = parsePort(env.PROXY_PORT, "PROXY_PORT");
36
- const defaultHealthPort = port === 8081 ? 8082 : 8081;
37
- const healthPort = parsePort(
38
- env.PROXY_HEALTH_PORT,
39
- "PROXY_HEALTH_PORT",
40
- defaultHealthPort,
41
- );
42
- if (healthPort === port) {
43
- throw new ConfigError(
44
- `PROXY_HEALTH_PORT (${healthPort}) must differ from PROXY_PORT (${port})`,
45
- );
46
- }
47
- const host = env.PROXY_HOST ?? "0.0.0.0";
48
- const caDir = env.PROXY_CA_DIR ?? null;
49
- const logLevel = parseLogLevel(env.PROXY_LOG_LEVEL);
50
-
51
- return { port, host, healthPort, caDir, logLevel };
52
- }
53
-
54
- function parsePort(
55
- raw: string | undefined,
56
- envName: string = "PROXY_PORT",
57
- defaultPort: number = 8080,
58
- ): number {
59
- if (raw === undefined || raw === "") return defaultPort;
60
-
61
- const port = Number(raw);
62
- if (!Number.isInteger(port) || port < 1 || port > 65535) {
63
- throw new ConfigError(
64
- `${envName} must be an integer between 1 and 65535, got "${raw}"`,
65
- );
66
- }
67
- return port;
68
- }
69
-
70
- const VALID_LOG_LEVELS = new Set(["debug", "info", "warn", "error"] as const);
71
- type LogLevel = "debug" | "info" | "warn" | "error";
72
-
73
- function parseLogLevel(raw: string | undefined): LogLevel {
74
- if (raw === undefined || raw === "") return "info";
75
-
76
- const lower = raw.toLowerCase();
77
- if (!VALID_LOG_LEVELS.has(lower as LogLevel)) {
78
- throw new ConfigError(
79
- `PROXY_LOG_LEVEL must be one of debug, info, warn, error — got "${raw}"`,
80
- );
81
- }
82
- return lower as LogLevel;
83
- }
84
-
85
- /**
86
- * Dedicated error class for configuration validation failures.
87
- * The entrypoint catches this to print a clean message without a stack trace.
88
- */
89
- export class ConfigError extends Error {
90
- constructor(message: string) {
91
- super(message);
92
- this.name = "ConfigError";
93
- }
94
- }
@@ -9,8 +9,6 @@
9
9
  * All other paths return 404. Non-GET methods return 405.
10
10
  */
11
11
 
12
- import { createServer, type Server } from "node:http";
13
-
14
12
  export interface HealthServerOptions {
15
13
  /**
16
14
  * Callback that returns `true` when the proxy server is ready to accept
@@ -18,45 +16,3 @@ export interface HealthServerOptions {
18
16
  */
19
17
  isReady: () => boolean;
20
18
  }
21
-
22
- /**
23
- * Create an HTTP server that serves health and readiness endpoints.
24
- *
25
- * The returned server is not yet listening -- the caller is responsible for
26
- * calling `.listen()`.
27
- */
28
- export function createHealthServer(options: HealthServerOptions): Server {
29
- const { isReady } = options;
30
-
31
- const server = createServer((req, res) => {
32
- // Only support GET requests
33
- if (req.method !== "GET") {
34
- res.writeHead(405, { "Content-Type": "text/plain", Allow: "GET" });
35
- res.end("Method Not Allowed\n");
36
- return;
37
- }
38
-
39
- switch (req.url) {
40
- case "/healthz": {
41
- res.writeHead(200, { "Content-Type": "application/json" });
42
- res.end(JSON.stringify({ status: "ok" }));
43
- return;
44
- }
45
-
46
- case "/readyz": {
47
- const ready = isReady();
48
- const statusCode = ready ? 200 : 503;
49
- res.writeHead(statusCode, { "Content-Type": "application/json" });
50
- res.end(JSON.stringify({ status: ready ? "ready" : "not_ready" }));
51
- return;
52
- }
53
-
54
- default: {
55
- res.writeHead(404, { "Content-Type": "text/plain" });
56
- res.end("Not Found\n");
57
- }
58
- }
59
- });
60
-
61
- return server;
62
- }
@@ -27,19 +27,6 @@ export type {
27
27
  // ---------------------------------------------------------------------------
28
28
 
29
29
  export type { ManagedSession, SessionStartHooks } from "@vellumai/egress-proxy";
30
- export {
31
- cloneSession,
32
- createSession,
33
- credentialIdsMatch,
34
- getActiveSession,
35
- getOrStartSession,
36
- getSessionEnv,
37
- getSessionsForConversation,
38
- SessionStore,
39
- startSession,
40
- stopAllSessions,
41
- stopSession,
42
- } from "@vellumai/egress-proxy";
43
30
 
44
31
  // ---------------------------------------------------------------------------
45
32
  // Host pattern matching
@@ -49,36 +36,28 @@ export type {
49
36
  HostMatchKind,
50
37
  MatchHostPatternOptions,
51
38
  } from "./host-pattern-match.js";
52
- export {
53
- compareMatchSpecificity,
54
- matchHostPattern,
55
- } from "./host-pattern-match.js";
56
39
 
57
40
  // Certificate management
58
41
  export {
59
42
  ensureCombinedCABundle,
60
43
  ensureLocalCA,
61
44
  getCAPath,
62
- getCombinedCAPath,
63
45
  issueLeafCert,
64
46
  } from "./certs.js";
65
47
 
66
48
  // MITM handler
67
49
  export type { RewriteCallback } from "./mitm-handler.js";
68
- export { handleMitm } from "./mitm-handler.js";
69
50
 
70
51
  // Router
71
52
  export type { RouteDecision, RouteReason } from "./router.js";
72
53
  export { routeConnection } from "./router.js";
73
54
 
74
55
  // CONNECT tunnel
75
- export { handleConnect } from "./connect-tunnel.js";
76
56
 
77
57
  // Policy engine
78
58
  export { evaluateRequest, evaluateRequestWithApproval } from "./policy.js";
79
59
 
80
60
  // HTTP forwarder
81
- export { forwardHttpRequest } from "./http-forwarder.js";
82
61
 
83
62
  // Proxy server
84
63
  export type { MitmHandlerConfig, ProxyServerConfig } from "./server.js";
@@ -89,7 +68,6 @@ export type { SidecarConfig } from "./config.js";
89
68
 
90
69
  // Health / readiness server
91
70
  export type { HealthServerOptions } from "./health.js";
92
- export { createHealthServer } from "./health.js";
93
71
 
94
72
  // Logging/diagnostics
95
73
  export type { CredentialRefTrace, ProxyDecisionTrace } from "./logging.js";
@@ -0,0 +1,184 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { mapApprovalProvenance } from "./approval-provenance.js";
4
+ import type { ApprovalMode, RiskThreshold } from "./types.js";
5
+ import { RISK_ORDINAL, THRESHOLD_ORDINAL } from "./types.js";
6
+
7
+ // ── mapApprovalProvenance — one test per row of the decision mapping table ────
8
+
9
+ describe("mapApprovalProvenance", () => {
10
+ test("platform_auto_approve → auto / platform_auto_approve", () => {
11
+ const r = mapApprovalProvenance("platform_auto_approve", {});
12
+ expect(r.approvalMode).toBe("auto");
13
+ expect(r.approvalReason).toBe("platform_auto_approve");
14
+ });
15
+
16
+ test("guardian_auto_approve → auto / within_threshold", () => {
17
+ const r = mapApprovalProvenance("guardian_auto_approve", {});
18
+ expect(r.approvalMode).toBe("auto");
19
+ expect(r.approvalReason).toBe("within_threshold");
20
+ });
21
+
22
+ test("allow + sandbox → auto / sandbox_auto_approve", () => {
23
+ const r = mapApprovalProvenance("allow", { hasSandboxAutoApprove: true });
24
+ expect(r.approvalMode).toBe("auto");
25
+ expect(r.approvalReason).toBe("sandbox_auto_approve");
26
+ });
27
+
28
+ test("allow + trust rule → auto / trust_rule_allowed", () => {
29
+ const r = mapApprovalProvenance("allow", { matchedTrustRuleId: "rule-abc" });
30
+ expect(r.approvalMode).toBe("auto");
31
+ expect(r.approvalReason).toBe("trust_rule_allowed");
32
+ });
33
+
34
+ test("allow (no trust rule, no sandbox) → auto / within_threshold", () => {
35
+ const r = mapApprovalProvenance("allow", {});
36
+ expect(r.approvalMode).toBe("auto");
37
+ expect(r.approvalReason).toBe("within_threshold");
38
+ });
39
+
40
+ test("allow + wasPrompted → prompted / user_approved", () => {
41
+ const r = mapApprovalProvenance("allow", { wasPrompted: true });
42
+ expect(r.approvalMode).toBe("prompted");
43
+ expect(r.approvalReason).toBe("user_approved");
44
+ });
45
+
46
+ test("deny (user pressed deny) → prompted / user_denied", () => {
47
+ const r = mapApprovalProvenance("deny", {});
48
+ expect(r.approvalMode).toBe("prompted");
49
+ expect(r.approvalReason).toBe("user_denied");
50
+ });
51
+
52
+ test("deny + wasTimeout → prompted / timed_out", () => {
53
+ const r = mapApprovalProvenance("deny", { wasTimeout: true });
54
+ expect(r.approvalMode).toBe("prompted");
55
+ expect(r.approvalReason).toBe("timed_out");
56
+ });
57
+
58
+ test("deny + wasSystemCancel → prompted / system_cancelled", () => {
59
+ const r = mapApprovalProvenance("deny", { wasSystemCancel: true });
60
+ expect(r.approvalMode).toBe("prompted");
61
+ expect(r.approvalReason).toBe("system_cancelled");
62
+ });
63
+
64
+ test("wasSystemCancel takes priority over wasTimeout", () => {
65
+ const r = mapApprovalProvenance("deny", { wasSystemCancel: true, wasTimeout: true });
66
+ expect(r.approvalReason).toBe("system_cancelled");
67
+ });
68
+
69
+ test("denied + matchedTrustRuleId → blocked / trust_rule_denied", () => {
70
+ const r = mapApprovalProvenance("denied", { matchedTrustRuleId: "rule-abc" });
71
+ expect(r.approvalMode).toBe("blocked");
72
+ expect(r.approvalReason).toBe("trust_rule_denied");
73
+ });
74
+
75
+ test("denied (no matchedTrustRuleId) → blocked / no_interactive_client", () => {
76
+ const r = mapApprovalProvenance("denied", {});
77
+ expect(r.approvalMode).toBe("blocked");
78
+ expect(r.approvalReason).toBe("no_interactive_client");
79
+ });
80
+
81
+ test("unknown decision → unknown / unknown", () => {
82
+ const r = mapApprovalProvenance("something_unexpected", {});
83
+ expect(r.approvalMode).toBe("unknown");
84
+ expect(r.approvalReason).toBe("unknown");
85
+ });
86
+
87
+ test("sandbox takes priority over trust rule when both present", () => {
88
+ const r = mapApprovalProvenance("allow", {
89
+ hasSandboxAutoApprove: true,
90
+ matchedTrustRuleId: "rule-xyz",
91
+ });
92
+ expect(r.approvalReason).toBe("sandbox_auto_approve");
93
+ });
94
+
95
+ test("wasPrompted takes priority over sandbox when both set", () => {
96
+ const r = mapApprovalProvenance("allow", {
97
+ wasPrompted: true,
98
+ hasSandboxAutoApprove: true,
99
+ });
100
+ expect(r.approvalMode).toBe("prompted");
101
+ expect(r.approvalReason).toBe("user_approved");
102
+ });
103
+ });
104
+
105
+ // ── RISK_ORDINAL — unknown riskLevel must not map to a safe value ─────────────
106
+
107
+ describe("RISK_ORDINAL semantics", () => {
108
+ test("known risk levels have expected ordinals", () => {
109
+ expect(RISK_ORDINAL["low"]).toBe(0);
110
+ expect(RISK_ORDINAL["medium"]).toBe(1);
111
+ expect(RISK_ORDINAL["high"]).toBe(2);
112
+ });
113
+
114
+ test("unknown riskLevel is absent so callers fall through to ?? 2 (high)", () => {
115
+ expect(RISK_ORDINAL["unknown"]).toBeUndefined();
116
+ });
117
+ });
118
+
119
+ // ── wasExpected derivation — all combinations ─────────────────────────────────
120
+
121
+ function wasExpected(
122
+ approvalMode: ApprovalMode,
123
+ riskLevel: string,
124
+ riskThreshold: RiskThreshold,
125
+ ): boolean {
126
+ if (approvalMode !== "auto") return true;
127
+ return RISK_ORDINAL[riskLevel] <= THRESHOLD_ORDINAL[riskThreshold];
128
+ }
129
+
130
+ describe("wasExpected derivation", () => {
131
+ test("prompted outcomes are always expected", () => {
132
+ expect(wasExpected("prompted", "high", "none")).toBe(true);
133
+ expect(wasExpected("prompted", "high", "low")).toBe(true);
134
+ });
135
+
136
+ test("blocked outcomes are always expected", () => {
137
+ expect(wasExpected("blocked", "high", "none")).toBe(true);
138
+ expect(wasExpected("blocked", "medium", "low")).toBe(true);
139
+ });
140
+
141
+ test("unknown (legacy) outcomes are always expected", () => {
142
+ expect(wasExpected("unknown", "high", "none")).toBe(true);
143
+ });
144
+
145
+ test("auto: risk within threshold → expected", () => {
146
+ expect(wasExpected("auto", "low", "low")).toBe(true);
147
+ expect(wasExpected("auto", "low", "medium")).toBe(true);
148
+ expect(wasExpected("auto", "medium", "medium")).toBe(true);
149
+ expect(wasExpected("auto", "high", "high")).toBe(true);
150
+ });
151
+
152
+ test("auto: risk above threshold → unexpected", () => {
153
+ expect(wasExpected("auto", "high", "low")).toBe(false);
154
+ expect(wasExpected("auto", "high", "medium")).toBe(false);
155
+ expect(wasExpected("auto", "medium", "low")).toBe(false);
156
+ expect(wasExpected("auto", "high", "none")).toBe(false);
157
+ expect(wasExpected("auto", "medium", "none")).toBe(false);
158
+ expect(wasExpected("auto", "low", "none")).toBe(false);
159
+ });
160
+ });
161
+
162
+ // ── Invariant: new records must not emit approvalMode: "unknown" ──────────────
163
+
164
+ describe("approvalMode unknown invariant", () => {
165
+ const knownDecisions = [
166
+ "allow",
167
+ "deny",
168
+ "denied",
169
+ "platform_auto_approve",
170
+ "guardian_auto_approve",
171
+ ];
172
+
173
+ test("every known decision produces a non-unknown approvalMode", () => {
174
+ for (const decision of knownDecisions) {
175
+ const r = mapApprovalProvenance(decision, {});
176
+ expect(r.approvalMode).not.toBe("unknown");
177
+ }
178
+ });
179
+
180
+ test("only unrecognised decision strings emit unknown", () => {
181
+ const r = mapApprovalProvenance("__legacy_unknown__", {});
182
+ expect(r.approvalMode).toBe("unknown");
183
+ });
184
+ });