@vellumai/assistant 0.11.6 → 0.11.7-staging.2

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 (478) hide show
  1. package/ARCHITECTURE.md +31 -19
  2. package/Dockerfile +1 -0
  3. package/docs/architecture/security.md +3 -3
  4. package/docs/flux-turn-detection-spike.md +8 -7
  5. package/docs/runbook-trusted-contacts.md +14 -0
  6. package/docs/stt-provider-onboarding.md +8 -4
  7. package/knip.json +1 -0
  8. package/node_modules/@vellumai/avatar-manifest/package.json +18 -0
  9. package/node_modules/@vellumai/avatar-manifest/src/__tests__/manifest.test.ts +173 -0
  10. package/node_modules/@vellumai/avatar-manifest/src/__tests__/read.test.ts +114 -0
  11. package/node_modules/@vellumai/avatar-manifest/src/index.ts +29 -0
  12. package/node_modules/@vellumai/avatar-manifest/src/layout.ts +18 -0
  13. package/node_modules/@vellumai/avatar-manifest/src/manifest.ts +156 -0
  14. package/node_modules/@vellumai/avatar-manifest/src/read.ts +109 -0
  15. package/node_modules/@vellumai/avatar-manifest/tsconfig.json +20 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
  17. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  18. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
  19. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
  20. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
  21. package/node_modules/@vellumai/ces-client/src/http-credentials.ts +161 -0
  22. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  23. package/node_modules/@vellumai/environments/package.json +2 -1
  24. package/node_modules/@vellumai/environments/src/shell.test.ts +56 -0
  25. package/node_modules/@vellumai/environments/src/shell.ts +65 -0
  26. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
  27. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  28. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
  29. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
  30. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
  31. package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +32 -0
  32. package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +1 -0
  33. package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +82 -0
  34. package/node_modules/@vellumai/gateway-client/src/__tests__/plugin-admission-denied-contract.test.ts +56 -0
  35. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +18 -0
  36. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +5 -11
  37. package/node_modules/@vellumai/gateway-client/src/channel-permission-contract.ts +3 -1
  38. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +9 -0
  39. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +4 -0
  40. package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +75 -0
  41. package/node_modules/@vellumai/gateway-client/src/index.ts +17 -0
  42. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +8 -0
  43. package/node_modules/@vellumai/gateway-client/src/plugin-admission-denied-contract.ts +47 -0
  44. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +11 -0
  45. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
  46. package/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  47. package/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
  48. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
  49. package/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
  50. package/openapi.yaml +419 -55
  51. package/package.json +4 -1
  52. package/scripts/sync-llm-catalog.ts +6 -0
  53. package/src/__tests__/approval-card-edit-inbound-handoff.test.ts +199 -0
  54. package/src/__tests__/approval-card-edit.test.ts +153 -0
  55. package/src/__tests__/attachments-store-heic-normalize.test.ts +1 -1
  56. package/src/__tests__/available-skills.test.ts +54 -0
  57. package/src/__tests__/avatar-identity-sync.test.ts +5 -1
  58. package/src/__tests__/catalog-install-normalize.test.ts +9 -1
  59. package/src/__tests__/channel-policy.test.ts +1 -3
  60. package/src/__tests__/channel-readiness-discord.test.ts +9 -70
  61. package/src/__tests__/compactor-call-site-logging.test.ts +61 -0
  62. package/src/__tests__/config-loader-backfill.test.ts +69 -0
  63. package/src/__tests__/config-schema.test.ts +2 -3
  64. package/src/__tests__/conversation-agent-loop.test.ts +238 -0
  65. package/src/__tests__/conversation-attachments.test.ts +0 -1
  66. package/src/__tests__/conversation-error.test.ts +44 -0
  67. package/src/__tests__/conversation-evictor.test.ts +19 -0
  68. package/src/__tests__/conversation-fork-referential.test.ts +73 -63
  69. package/src/__tests__/conversation-fork-retrospective.test.ts +99 -74
  70. package/src/__tests__/conversation-runtime-assembly.test.ts +23 -0
  71. package/src/__tests__/conversation-slash.test.ts +9 -0
  72. package/src/__tests__/conversation-title-service.test.ts +97 -0
  73. package/src/__tests__/conversation-tool-setup-attribution.test.ts +16 -0
  74. package/src/__tests__/credential-broker-server-use.test.ts +0 -218
  75. package/src/__tests__/credential-broker.test.ts +43 -420
  76. package/src/__tests__/credential-record-write-through.test.ts +78 -0
  77. package/src/__tests__/credential-routes.test.ts +17 -1
  78. package/src/__tests__/credential-security-invariants.test.ts +8 -9
  79. package/src/__tests__/default-profile-catalog-fallback.test.ts +289 -0
  80. package/src/__tests__/delete-propagation.test.ts +92 -2
  81. package/src/__tests__/discord-callback-round-trip.test.ts +93 -0
  82. package/src/__tests__/discord-channel-config.test.ts +237 -0
  83. package/src/__tests__/edit-propagation.test.ts +42 -2
  84. package/src/__tests__/evict-conversations-for-reload.test.ts +51 -1
  85. package/src/__tests__/fallback-breaker.test.ts +1335 -0
  86. package/src/__tests__/gateway-threshold-reader-mock.ts +6 -0
  87. package/src/__tests__/guardian-card-withdrawal.test.ts +30 -0
  88. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -2
  89. package/src/__tests__/image-conversion.test.ts +38 -4
  90. package/src/__tests__/install-skill-routing.test.ts +25 -0
  91. package/src/__tests__/llm-catalog-parity.test.ts +4 -0
  92. package/src/__tests__/llm-resolver.test.ts +74 -0
  93. package/src/__tests__/llm-schema-fallback-profile.test.ts +359 -0
  94. package/src/__tests__/managed-fallback-dispatch.test.ts +430 -0
  95. package/src/__tests__/managed-profile-guard.test.ts +28 -0
  96. package/src/__tests__/managed-speech-defaults.test.ts +196 -1
  97. package/src/__tests__/mcp-cli.test.ts +23 -1
  98. package/src/__tests__/mcp-tool-annotations-risk.test.ts +43 -34
  99. package/src/__tests__/media-stream-stt-session.test.ts +2 -0
  100. package/src/__tests__/notification-decision-fallback.test.ts +106 -0
  101. package/src/__tests__/notification-discord-adapter.test.ts +162 -0
  102. package/src/__tests__/oauth-connect-orchestrator.test.ts +95 -0
  103. package/src/__tests__/persist-unsendable-image-downscale.test.ts +6 -8
  104. package/src/__tests__/persist-unsendable-image.test.ts +3 -3
  105. package/src/__tests__/platform.test.ts +19 -1
  106. package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
  107. package/src/__tests__/provider-catalog-visibility.test.ts +17 -0
  108. package/src/__tests__/provider-platform-proxy-integration.test.ts +8 -1
  109. package/src/__tests__/provider-usage-tracking.test.ts +56 -0
  110. package/src/__tests__/require-fresh-approval.test.ts +5 -2
  111. package/src/__tests__/retry-fallback-escalation.test.ts +1093 -0
  112. package/src/__tests__/schedule-retry.test.ts +183 -4
  113. package/src/__tests__/scheduler-reuse-conversation.test.ts +6 -6
  114. package/src/__tests__/secret-routes-platform-proxy.test.ts +55 -0
  115. package/src/__tests__/skill-load-tool.test.ts +34 -0
  116. package/src/__tests__/skill-tool-factory.test.ts +6 -1
  117. package/src/__tests__/skill-tool-manifest.test.ts +20 -0
  118. package/src/__tests__/skills.test.ts +25 -0
  119. package/src/__tests__/subagent-manager-notify.test.ts +38 -0
  120. package/src/__tests__/terminal-tools.test.ts +2 -0
  121. package/src/__tests__/tool-approval-handler.test.ts +146 -2
  122. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +108 -2
  123. package/src/__tests__/tool-audit.test.ts +111 -40
  124. package/src/__tests__/tool-executor-lifecycle-events.test.ts +228 -18
  125. package/src/__tests__/ui-shape-teaching.test.ts +1 -1
  126. package/src/__tests__/verification-control-plane-policy.test.ts +6 -2
  127. package/src/__tests__/voice-config-update.test.ts +43 -0
  128. package/src/__tests__/workspace-migration-147-rename-colliding-backup-profile-names.test.ts +480 -0
  129. package/src/__tests__/workspace-migration-148-strip-unsupported-fallback-profiles.test.ts +167 -0
  130. package/src/__tests__/workspace-migration-149-repoint-backup-profile-selections.test.ts +144 -0
  131. package/src/agent/loop.ts +31 -2
  132. package/src/approvals/guardian-card-withdrawal.ts +22 -0
  133. package/src/avatar/__tests__/ensure-raster.test.ts +291 -0
  134. package/src/avatar/avatar-manifest.ts +48 -100
  135. package/src/avatar/avatar-store.ts +7 -7
  136. package/src/avatar/ensure-raster.ts +146 -0
  137. package/src/avatar/resvg-lazy.test.ts +0 -1
  138. package/src/avatar/traits-png-sync.ts +9 -10
  139. package/src/background-wake/wake-intent-hooks.test.ts +0 -1
  140. package/src/backup/__tests__/paths.test.ts +6 -2
  141. package/src/backup/paths.ts +6 -4
  142. package/src/bundler/app-compiler.ts +2 -2
  143. package/src/bundler/compiler-tools.test.ts +36 -0
  144. package/src/bundler/compiler-tools.ts +54 -18
  145. package/src/calls/__tests__/telephony-synthesis-language.test.ts +81 -0
  146. package/src/calls/__tests__/voice-session-bridge.test.ts +57 -0
  147. package/src/calls/media-stream-stt-session.ts +2 -1
  148. package/src/calls/telephony-synthesis-language.ts +13 -5
  149. package/src/calls/voice-session-bridge.ts +19 -4
  150. package/src/channels/config.ts +23 -12
  151. package/src/cli/AGENTS.md +3 -1
  152. package/src/cli/commands/backup.help.ts +10 -13
  153. package/src/cli/commands/contacts.help.ts +16 -4
  154. package/src/cli/commands/contacts.ts +5 -1
  155. package/src/cli/commands/credentials.help.ts +1 -1
  156. package/src/cli/commands/inference-providers.ts +18 -53
  157. package/src/cli/commands/mcp.help.ts +6 -5
  158. package/src/cli/commands/mcp.ts +1 -1
  159. package/src/cli/commands/memory/memory-retrospective.ts +1 -0
  160. package/src/cli/commands/stt.help.ts +2 -2
  161. package/src/config/__tests__/backup-schema.test.ts +1 -1
  162. package/src/config/__tests__/default-profile-catalog.test.ts +83 -2
  163. package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +1 -1
  164. package/src/config/__tests__/memory-retrospective-schema.test.ts +12 -15
  165. package/src/config/bundled-skills/computer-use/SKILL.md +6 -6
  166. package/src/config/bundled-skills/computer-use/TOOLS.json +9 -6
  167. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +3 -1
  168. package/src/config/bundled-skills/media-processing/services/preprocess.ts +1 -1
  169. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  170. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +16 -15
  171. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +2 -1
  172. package/src/config/bundled-skills/settings/TOOLS.json +11 -5
  173. package/src/config/bundled-skills/settings/tools/open-system-settings.test.ts +85 -0
  174. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +33 -8
  175. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +54 -3
  176. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +1 -1
  177. package/src/config/default-profile-catalog.ts +195 -10
  178. package/src/config/default-profile-names.ts +41 -0
  179. package/src/config/feature-flag-registry.json +34 -2
  180. package/src/config/inference-profile-validation.ts +11 -2
  181. package/src/config/llm-resolver.ts +25 -0
  182. package/src/config/loader.ts +3 -0
  183. package/src/config/managed-speech-defaults.ts +160 -10
  184. package/src/config/schema.ts +2 -0
  185. package/src/config/schemas/__tests__/live-voice.test.ts +7 -6
  186. package/src/config/schemas/__tests__/stt.test.ts +94 -0
  187. package/src/config/schemas/backup.ts +1 -1
  188. package/src/config/schemas/channels.ts +32 -0
  189. package/src/config/schemas/live-voice.ts +13 -4
  190. package/src/config/schemas/llm.ts +263 -15
  191. package/src/config/schemas/mcp.ts +10 -2
  192. package/src/config/schemas/memory-retrospective.ts +1 -19
  193. package/src/config/schemas/services.ts +24 -0
  194. package/src/config/schemas/stt.ts +128 -5
  195. package/src/config/seed-inference-profiles.ts +32 -3
  196. package/src/config/skill-state.ts +5 -0
  197. package/src/config/skills.ts +21 -0
  198. package/src/contacts/notify-contacts-changed.ts +5 -2
  199. package/src/context/compactor.ts +74 -1
  200. package/src/daemon/__tests__/conversation-tool-setup.test.ts +138 -1
  201. package/src/daemon/config-watcher.ts +2 -1
  202. package/src/daemon/conversation-agent-loop-handlers.ts +27 -0
  203. package/src/daemon/conversation-agent-loop.ts +86 -10
  204. package/src/daemon/conversation-error.ts +25 -0
  205. package/src/daemon/conversation-evictor.ts +2 -4
  206. package/src/daemon/conversation-runtime-assembly.ts +19 -7
  207. package/src/daemon/conversation-skill-tools.ts +5 -1
  208. package/src/daemon/conversation-slash.ts +2 -2
  209. package/src/daemon/conversation-store.ts +8 -1
  210. package/src/daemon/conversation-tool-setup.ts +37 -0
  211. package/src/daemon/conversation.ts +0 -4
  212. package/src/daemon/handlers/channel-config-result.ts +30 -0
  213. package/src/daemon/handlers/config-discord-channel.ts +301 -0
  214. package/src/daemon/handlers/config-ingress.ts +9 -0
  215. package/src/daemon/handlers/config-slack-channel.ts +13 -15
  216. package/src/daemon/handlers/config-telegram.ts +2 -5
  217. package/src/daemon/handlers/conversations.ts +0 -2
  218. package/src/daemon/handlers/skills.ts +25 -7
  219. package/src/daemon/lifecycle.ts +3 -1
  220. package/src/daemon/message-types/messages.ts +0 -2
  221. package/src/daemon/tool-setup-types.ts +5 -3
  222. package/src/daemon/trust-context-types.test.ts +33 -0
  223. package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
  224. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +58 -4
  225. package/src/live-voice/__tests__/live-voice-managed-speech-fallback.test.ts +8 -3
  226. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +73 -11
  227. package/src/live-voice/__tests__/live-voice-session-preflight.test.ts +148 -2
  228. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +64 -0
  229. package/src/live-voice/__tests__/live-voice-stt.test.ts +3 -0
  230. package/src/live-voice/__tests__/live-voice-text-turn.test.ts +391 -0
  231. package/src/live-voice/__tests__/protocol.test.ts +224 -0
  232. package/src/live-voice/live-voice-credential-preflight.ts +5 -1
  233. package/src/live-voice/live-voice-session-manager.ts +39 -2
  234. package/src/live-voice/live-voice-session.ts +224 -9
  235. package/src/live-voice/protocol.ts +187 -1
  236. package/src/messaging/providers/discord/api.ts +2 -1
  237. package/src/messaging/providers/discord/withdraw.ts +62 -0
  238. package/src/messaging/read-provider-metadata.ts +32 -0
  239. package/src/monitoring/__tests__/file-descriptors.test.ts +32 -0
  240. package/src/monitoring/__tests__/process-memory.test.ts +34 -1
  241. package/src/monitoring/db-integrity-sample.ts +1 -0
  242. package/src/monitoring/file-descriptors.ts +39 -6
  243. package/src/monitoring/process-memory.ts +37 -5
  244. package/src/monitoring/resource-sampler.ts +25 -8
  245. package/src/notifications/__tests__/activity-failed-dedupe.test.ts +161 -0
  246. package/src/notifications/__tests__/background-failure-signal.test.ts +107 -0
  247. package/src/notifications/__tests__/connected-channels.test.ts +12 -0
  248. package/src/notifications/__tests__/copy-composer.test.ts +83 -0
  249. package/src/notifications/__tests__/destination-resolver.test.ts +38 -0
  250. package/src/notifications/activity-failed-dedupe.ts +95 -0
  251. package/src/notifications/adapters/discord.ts +132 -0
  252. package/src/notifications/adapters/shared.ts +16 -0
  253. package/src/notifications/adapters/slack.ts +6 -1
  254. package/src/notifications/adapters/telegram.ts +5 -8
  255. package/src/notifications/approval-card-data.ts +94 -1
  256. package/src/notifications/background-failure-signal.ts +116 -0
  257. package/src/notifications/broadcaster.ts +3 -9
  258. package/src/notifications/copy-composer.ts +41 -8
  259. package/src/notifications/decision-engine.ts +55 -0
  260. package/src/notifications/destination-resolver.ts +45 -5
  261. package/src/notifications/emit-signal.ts +19 -5
  262. package/src/notifications/guardian-question-mode.ts +57 -0
  263. package/src/notifications/types.ts +2 -8
  264. package/src/oauth/__tests__/seed-providers-managed.test.ts +18 -0
  265. package/src/oauth/connect-orchestrator.ts +16 -0
  266. package/src/oauth/seed-providers.ts +262 -0
  267. package/src/permissions/checker.test.ts +21 -0
  268. package/src/permissions/checker.ts +6 -0
  269. package/src/permissions/gateway-threshold-reader.test.ts +184 -0
  270. package/src/permissions/gateway-threshold-reader.ts +144 -10
  271. package/src/permissions/prompter.ts +0 -6
  272. package/src/permissions/types.ts +5 -4
  273. package/src/persistence/__tests__/bulk-write-gate.test.ts +0 -38
  274. package/src/persistence/bulk-write-gate.ts +9 -10
  275. package/src/persistence/conversation-crud.ts +88 -134
  276. package/src/persistence/conversation-title-service.ts +43 -29
  277. package/src/persistence/embeddings/embedding-local.ts +14 -85
  278. package/src/persistence/embeddings/embedding-runtime-manager.ts +1 -0
  279. package/src/persistence/lifecycle-events-store.test.ts +50 -0
  280. package/src/persistence/lifecycle-events-store.ts +75 -5
  281. package/src/platform/platform-patch-queue.test.ts +368 -0
  282. package/src/platform/platform-patch-queue.ts +190 -0
  283. package/src/platform/sync-avatar.test.ts +527 -0
  284. package/src/platform/sync-avatar.ts +209 -0
  285. package/src/platform/sync-identity.test.ts +75 -0
  286. package/src/platform/sync-identity.ts +17 -110
  287. package/src/plugin-api/model-profiles.test.ts +2 -2
  288. package/src/plugin-api/model-profiles.ts +7 -7
  289. package/src/plugin-api/transcription-session.ts +1 -1
  290. package/src/plugins/defaults/compaction/window-manager.ts +4 -0
  291. package/src/plugins/defaults/image-recovery/recover.ts +2 -2
  292. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +133 -34
  293. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +21 -31
  294. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +13 -9
  295. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +31 -4
  296. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +0 -11
  297. package/src/plugins/defaults/memory/memory-retrospective-fork-boundary.ts +27 -13
  298. package/src/plugins/defaults/memory/memory-retrospective-job.ts +183 -113
  299. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +13 -13
  300. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +2 -2
  301. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +1 -1
  302. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +4 -2
  303. package/src/plugins/defaults/memory/substrate/sweep-job.ts +9 -42
  304. package/src/plugins/defaults/memory/v2/rerank-local.ts +1 -0
  305. package/src/plugins/defaults/memory/worker.ts +7 -0
  306. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  307. package/src/providers/__tests__/retry-callsite.test.ts +55 -0
  308. package/src/providers/__tests__/vellum-connection-routing.test.ts +21 -3
  309. package/src/providers/connection-resolution.ts +2 -17
  310. package/src/providers/fallback-breaker.ts +389 -0
  311. package/src/providers/inference/__tests__/adapter-factory-opencode.test.ts +92 -0
  312. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +16 -0
  313. package/src/providers/inference/__tests__/base-url-security.test.ts +12 -0
  314. package/src/providers/inference/adapter-factory.ts +246 -3
  315. package/src/providers/inference/auth.ts +15 -14
  316. package/src/providers/inference/chatgpt-subscription-auth.ts +70 -0
  317. package/src/providers/inference/chatgpt-subscription-credentials.ts +84 -0
  318. package/src/providers/inference/resolve-auth.ts +1 -1
  319. package/src/providers/media-resolve.ts +2 -3
  320. package/src/providers/model-catalog.ts +100 -0
  321. package/src/providers/openai/__tests__/api-error-normalization.test.ts +22 -0
  322. package/src/providers/openai/__tests__/chat-template-flatten.test.ts +198 -0
  323. package/src/providers/openai/api-error-normalization.ts +13 -0
  324. package/src/providers/openai/chat-completions-provider.ts +84 -0
  325. package/src/providers/opencode/client.test.ts +150 -0
  326. package/src/providers/opencode/client.ts +61 -0
  327. package/src/providers/platform-proxy/constants.ts +5 -0
  328. package/src/providers/provider-resolvability.ts +20 -0
  329. package/src/providers/provider-secret-catalog.ts +3 -2
  330. package/src/providers/retry.ts +956 -68
  331. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +42 -0
  332. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +136 -0
  333. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +120 -0
  334. package/src/providers/speech-to-text/__tests__/resolve.test.ts +316 -12
  335. package/src/providers/speech-to-text/deepgram-flux-frames.ts +83 -3
  336. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +132 -4
  337. package/src/providers/speech-to-text/provider-catalog.ts +191 -3
  338. package/src/providers/speech-to-text/resolve.ts +132 -24
  339. package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +296 -0
  340. package/src/providers/types.ts +39 -0
  341. package/src/providers/usage-tracking.ts +12 -1
  342. package/src/providers/vellum/client.ts +29 -0
  343. package/src/providers/vellum-model-routing.test.ts +2 -0
  344. package/src/providers/vellum-model-routing.ts +3 -3
  345. package/src/runtime/AGENTS.md +1 -2
  346. package/src/runtime/__tests__/agent-wake.test.ts +105 -3
  347. package/src/runtime/__tests__/background-job-runner.test.ts +89 -3
  348. package/src/runtime/agent-wake.ts +44 -5
  349. package/src/runtime/background-job-runner.ts +68 -50
  350. package/src/runtime/channel-readiness-service.ts +0 -39
  351. package/src/runtime/http-server.ts +12 -4
  352. package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +18 -18
  353. package/src/runtime/routes/__tests__/backup-routes.test.ts +9 -5
  354. package/src/runtime/routes/__tests__/chatgpt-subscription-auth-routes.test.ts +372 -0
  355. package/src/runtime/routes/__tests__/contact-routes.test.ts +7 -0
  356. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +96 -0
  357. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +37 -0
  358. package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +2 -1
  359. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +87 -0
  360. package/src/runtime/routes/acp-claude-auth-routes.ts +25 -54
  361. package/src/runtime/routes/approval-routes.ts +0 -12
  362. package/src/runtime/routes/avatar-routes.ts +17 -30
  363. package/src/runtime/routes/backup-routes.ts +2 -2
  364. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +296 -113
  365. package/src/runtime/routes/contact-prompt-routes.ts +65 -6
  366. package/src/runtime/routes/contact-routes.ts +20 -15
  367. package/src/runtime/routes/conversation-query-routes.ts +98 -10
  368. package/src/runtime/routes/conversation-routes.ts +2 -2
  369. package/src/runtime/routes/credential-routes.ts +2 -1
  370. package/src/runtime/routes/guardian-approval-interception.ts +12 -9
  371. package/src/runtime/routes/inbound-message-handler.ts +47 -48
  372. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +18 -13
  373. package/src/runtime/routes/inbound-stages/admission-policy.ts +2 -2
  374. package/src/runtime/routes/inbound-stages/edit-intercept.ts +20 -2
  375. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +9 -8
  376. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -1
  377. package/src/runtime/routes/index.ts +2 -0
  378. package/src/runtime/routes/inference-profile-session-handler.ts +2 -2
  379. package/src/runtime/routes/inference-profiles-routes.ts +12 -7
  380. package/src/runtime/routes/inference-provider-connection-routes.ts +2 -2
  381. package/src/runtime/routes/inference-send-routes.ts +2 -2
  382. package/src/runtime/routes/ingress-status-routes.ts +2 -2
  383. package/src/runtime/routes/integrations/discord.ts +119 -0
  384. package/src/runtime/routes/llm-call-sites-routes.ts +2 -2
  385. package/src/runtime/routes/mcp-auth-routes.ts +12 -6
  386. package/src/runtime/routes/migration-routes.ts +3 -1
  387. package/src/runtime/routes/oauth-pending-flows.ts +136 -0
  388. package/src/runtime/routes/secret-routes.ts +7 -0
  389. package/src/runtime/routes/stt-routes.ts +38 -6
  390. package/src/runtime/routes/wake-conversation-routes.ts +8 -1
  391. package/src/runtime/routes/watch-routes.ts +9 -5
  392. package/src/runtime/sync/resource-sync-events.ts +2 -0
  393. package/src/runtime/trust-verdict-consumer.ts +3 -3
  394. package/src/schedule/__tests__/worker-feature-flags.test.ts +58 -0
  395. package/src/schedule/retry-policy.ts +8 -8
  396. package/src/schedule/run-script.ts +14 -23
  397. package/src/schedule/schedule-recovery.ts +0 -1
  398. package/src/schedule/scheduler.ts +89 -51
  399. package/src/schedule/worker.ts +57 -0
  400. package/src/security/__tests__/openai-device-auth.test.ts +355 -0
  401. package/src/security/ces-rpc-record-backend.ts +123 -0
  402. package/src/security/oauth2.ts +7 -0
  403. package/src/security/openai-device-auth.ts +364 -0
  404. package/src/security/secure-keys.ts +15 -0
  405. package/src/skills/available-skills.ts +10 -1
  406. package/src/skills/catalog-install.ts +19 -1
  407. package/src/skills/inline-command-runner.ts +6 -2
  408. package/src/skills/platform-compatibility.test.ts +49 -0
  409. package/src/skills/platform-compatibility.ts +65 -0
  410. package/src/skills/tool-manifest.ts +20 -0
  411. package/src/stt/__tests__/roles.test.ts +152 -0
  412. package/src/stt/daemon-batch-transcriber.ts +4 -1
  413. package/src/stt/roles.ts +180 -0
  414. package/src/stt/stt-stream-session.ts +3 -2
  415. package/src/stt/types.ts +33 -3
  416. package/src/subagent/manager.ts +12 -0
  417. package/src/telemetry/__tests__/live-voice-funnel.test.ts +34 -0
  418. package/src/telemetry/live-voice-funnel.ts +12 -2
  419. package/src/telemetry/telemetry-wire.generated.ts +6 -0
  420. package/src/telemetry/tool-audit.ts +83 -30
  421. package/src/telemetry/tool-executed-events-store.test.ts +0 -1
  422. package/src/telemetry/turn-outcome.ts +75 -4
  423. package/src/tools/client-os.ts +115 -0
  424. package/src/tools/computer-use/definitions.ts +11 -7
  425. package/src/tools/computer-use/skill-proxy-bridge.ts +2 -2
  426. package/src/tools/credentials/broker-types.ts +0 -71
  427. package/src/tools/credentials/broker.ts +6 -201
  428. package/src/tools/credentials/metadata-store.ts +65 -16
  429. package/src/tools/credentials/store.ts +2 -0
  430. package/src/tools/executor.ts +2 -8
  431. package/src/tools/host-shell.test.ts +75 -0
  432. package/src/tools/host-shell.ts +24 -0
  433. package/src/tools/host-terminal/host-shell.ts +15 -44
  434. package/src/tools/mcp/mcp-tool-factory.ts +44 -9
  435. package/src/tools/permission-checker.ts +73 -21
  436. package/src/tools/policy-context.ts +1 -0
  437. package/src/tools/shared/filesystem/image-read.ts +1 -1
  438. package/src/tools/skills/find-similar-skills.ts +10 -2
  439. package/src/tools/skills/load.ts +41 -6
  440. package/src/tools/skills/sandbox-runner.ts +8 -16
  441. package/src/tools/skills/skill-tool-factory.ts +1 -0
  442. package/src/tools/terminal/__tests__/safe-env.test.ts +64 -1
  443. package/src/tools/terminal/safe-env.ts +39 -9
  444. package/src/tools/terminal/shell.ts +12 -31
  445. package/src/tools/tool-approval-handler.ts +64 -10
  446. package/src/tools/tool-types.ts +10 -0
  447. package/src/tools/types.ts +19 -3
  448. package/src/tools/ui-surface/surface-shape-docs.ts +4 -3
  449. package/src/tools/workflows/manage-workflows.ts +2 -2
  450. package/src/tools/workflows/run-workflow.test.ts +2 -1
  451. package/src/util/__tests__/file-use.test.ts +61 -0
  452. package/src/util/__tests__/process-table.test.ts +66 -0
  453. package/src/util/__tests__/process-tree.test.ts +46 -0
  454. package/src/util/errors.ts +1 -0
  455. package/src/util/file-use.ts +91 -0
  456. package/src/util/host-process.test.ts +17 -0
  457. package/src/util/host-process.ts +75 -0
  458. package/src/util/image-conversion.ts +39 -7
  459. package/src/util/platform.ts +32 -13
  460. package/src/util/process-table.ts +249 -0
  461. package/src/util/process-tree.ts +85 -129
  462. package/src/util/provider-error-patterns.ts +20 -0
  463. package/src/util/spawn.ts +9 -2
  464. package/src/watch/__tests__/watch-retro.test.ts +48 -1
  465. package/src/watch/watch-retro.ts +46 -4
  466. package/src/workspace/git-service.ts +4 -14
  467. package/src/workspace/migrations/147-rename-colliding-backup-profile-names.ts +360 -0
  468. package/src/workspace/migrations/148-strip-unsupported-fallback-profiles.ts +117 -0
  469. package/src/workspace/migrations/149-repoint-backup-profile-selections.ts +204 -0
  470. package/src/workspace/migrations/150-stt-flux-provider-to-model-family.ts +139 -0
  471. package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +160 -0
  472. package/src/workspace/migrations/registry.ts +8 -0
  473. package/src/__tests__/permission-types.test.ts +0 -17
  474. package/src/channels/gateway-discord-admission.ts +0 -32
  475. package/src/persistence/fork-message-copy.ts +0 -239
  476. package/src/plugins/defaults/memory/__tests__/fork-message-copy.test.ts +0 -221
  477. package/src/security/__tests__/oauth2-device-code.test.ts +0 -431
  478. package/src/security/oauth2-device-code.ts +0 -312
@@ -10,8 +10,7 @@
10
10
  import { ApprovalUIMetadataSchema } from "@vellumai/gateway-client";
11
11
  import { z } from "zod";
12
12
 
13
- import type { ChannelPolicies } from "../channels/config.js";
14
- import type { ChannelId } from "../channels/types.js";
13
+ import type { DeliverableChannelId } from "../channels/config.js";
15
14
  import { AccessRequestPayloadSchema } from "./access-request-copy.js";
16
15
  import { ToolApprovalSourceViewSchema } from "./guardian-question-mode.js";
17
16
  import { UrgencySchema } from "./urgency.js";
@@ -20,12 +19,7 @@ import { UrgencySchema } from "./urgency.js";
20
19
  * Derived from the channel policy registry: only channels whose
21
20
  * deliveryEnabled flag is true are valid notification channels.
22
21
  */
23
- export type NotificationChannel = {
24
- [K in keyof ChannelPolicies]: ChannelPolicies[K]["notification"]["deliveryEnabled"] extends true
25
- ? K
26
- : never;
27
- }[keyof ChannelPolicies] &
28
- ChannelId;
22
+ export type NotificationChannel = DeliverableChannelId;
29
23
 
30
24
  export const NotificationDeliveryStatusSchema = z.enum([
31
25
  "pending",
@@ -11,6 +11,24 @@ describe("PROVIDER_SEED_DATA managed mode wiring", () => {
11
11
  expect("github-oauth" in ServicesSchema.shape).toBe(true);
12
12
  });
13
13
 
14
+ test("monday only exposes scopes supported by its OAuth API", () => {
15
+ const monday = PROVIDER_SEED_DATA.monday;
16
+ expect(monday).toBeDefined();
17
+ expect(monday.defaultScopes).not.toContain("items:read");
18
+ expect(monday.defaultScopes).not.toContain("items:write");
19
+
20
+ const availableScopes = monday.availableScopes;
21
+ expect(Array.isArray(availableScopes)).toBe(true);
22
+ if (Array.isArray(availableScopes)) {
23
+ expect(availableScopes.map(({ scope }) => scope)).not.toContain(
24
+ "items:read",
25
+ );
26
+ expect(availableScopes.map(({ scope }) => scope)).not.toContain(
27
+ "items:write",
28
+ );
29
+ }
30
+ });
31
+
14
32
  test("google base URL is host-only so relative paths select the product", () => {
15
33
  // A host-only base URL (no product path) lets a relative request path pick
16
34
  // the Google product — Gmail (/gmail/v1/...), Calendar (/calendar/v3/...),
@@ -19,6 +19,7 @@
19
19
  * - Running identity verifiers
20
20
  */
21
21
 
22
+ import { loadConfig } from "../config/loader.js";
22
23
  import { emitPostConnectNudge } from "../home/post-connect-feed.js";
23
24
  import { invalidateAssistantSuggestedPromptsCache } from "../home/suggested-prompts-cache.js";
24
25
  import type { TokenEndpointAuthMethod } from "../security/oauth2.js";
@@ -27,6 +28,7 @@ import { getLogger } from "../util/logger.js";
27
28
  import type { OAuthConnectResult } from "./connect-types.js";
28
29
  import { verifyIdentity } from "./identity-verifier.js";
29
30
  import { getProvider } from "./oauth-store.js";
31
+ import { isProviderVisible } from "./provider-visibility.js";
30
32
  import { storeOAuth2Tokens } from "./token-persistence.js";
31
33
 
32
34
  const log = getLogger("oauth-connect-orchestrator");
@@ -130,6 +132,20 @@ export async function orchestrateOAuthConnect(
130
132
  };
131
133
  }
132
134
 
135
+ // A provider gated behind a disabled feature flag is not connectable. This
136
+ // is the choke point every connect path funnels through (runtime routes,
137
+ // gateway, CLI, credential vault tool), so enforcing here covers all of
138
+ // them rather than relying on each entry point to check. The error matches
139
+ // the not-found case above so a gated provider is indistinguishable from an
140
+ // absent one, mirroring the provider routes.
141
+ if (!isProviderVisible(providerRow, loadConfig())) {
142
+ return {
143
+ success: false,
144
+ error: `No OAuth provider registered for "${options.service}". Ensure the provider is seeded in the database.`,
145
+ safeError: true,
146
+ };
147
+ }
148
+
133
149
  // Deserialize JSON fields from the DB row
134
150
  const dbAuthorizeParams = safeJsonParse<Record<string, string> | undefined>(
135
151
  providerRow.authorizeParams,
@@ -684,6 +684,268 @@ export const PROVIDER_SEED_DATA: Record<
684
684
  identityResponsePaths: ["email", "preferred_username"],
685
685
  },
686
686
 
687
+ monday: {
688
+ provider: "monday",
689
+ authorizeUrl: "https://auth.monday.com/oauth2/authorize",
690
+ tokenExchangeUrl: "https://auth.monday.com/oauth2/token",
691
+ pingUrl: "https://api.monday.com/v2",
692
+ pingMethod: "POST",
693
+ pingHeaders: { "Content-Type": "application/json" },
694
+ pingBody: { query: "{ me { id name email } }" },
695
+ baseUrl: "https://api.monday.com",
696
+ displayLabel: "monday.com",
697
+ description: "Boards, items, docs, and updates",
698
+ dashboardUrl: "https://auth.monday.com/apps",
699
+ clientIdPlaceholder: null,
700
+ // Simple Icons does not host a monday.com mark (both `monday` and
701
+ // `mondaydotcom` 404 on the CDN), so use the documented thesvg fallback.
702
+ logoUrl:
703
+ "https://cdn.jsdelivr.net/gh/glincker/thesvg@main/public/icons/monday/default.svg",
704
+ // monday tokens do not expire under the legacy OAuth flow and no refresh
705
+ // token is issued, so there is no refreshUrl. The newer OAuth 2.1 flow
706
+ // adds expiry/refresh/revocation but is opt-in per app version in the
707
+ // Developer Center and posts JSON (not form-encoded) credentials, so it
708
+ // is deliberately not used here.
709
+ defaultScopes: [
710
+ "me:read",
711
+ "account:read",
712
+ "users:read",
713
+ "teams:read",
714
+ "workspaces:read",
715
+ "boards:read",
716
+ "boards:write",
717
+ "docs:read",
718
+ "updates:read",
719
+ "updates:write",
720
+ "assets:read",
721
+ "tags:read",
722
+ "notifications:write",
723
+ ],
724
+ availableScopes: [
725
+ { scope: "me:read", description: "Read the user's profile information" },
726
+ {
727
+ scope: "account:read",
728
+ description: "Read general information about the account",
729
+ },
730
+ {
731
+ scope: "users:read",
732
+ description: "Read profile information of the account's users",
733
+ },
734
+ {
735
+ scope: "users:write",
736
+ description: "Modify profile information of the account's users",
737
+ },
738
+ {
739
+ scope: "teams:read",
740
+ description: "Read information about the account's teams",
741
+ },
742
+ { scope: "teams:write", description: "Modify the account's teams" },
743
+ {
744
+ scope: "workspaces:read",
745
+ description: "Read a user's workspaces data",
746
+ },
747
+ {
748
+ scope: "workspaces:write",
749
+ description: "Modify a user's workspaces data",
750
+ },
751
+ { scope: "boards:read", description: "Read a user's board data" },
752
+ { scope: "boards:write", description: "Modify a user's board data" },
753
+ { scope: "docs:read", description: "Read a user's docs" },
754
+ { scope: "docs:write", description: "Modify a user's docs" },
755
+ {
756
+ scope: "updates:read",
757
+ description: "Read updates and replies the user can see",
758
+ },
759
+ {
760
+ scope: "updates:write",
761
+ description: "Post or edit updates on behalf of the user",
762
+ },
763
+ {
764
+ scope: "assets:read",
765
+ description: "Read data from assets the user has access to",
766
+ },
767
+ { scope: "tags:read", description: "Read the account's tags" },
768
+ {
769
+ scope: "notifications:write",
770
+ description: "Send notifications on behalf of the user",
771
+ },
772
+ {
773
+ scope: "webhooks:read",
774
+ description: "Read existing webhooks configuration",
775
+ },
776
+ {
777
+ scope: "webhooks:write",
778
+ description: "Create and modify webhooks",
779
+ },
780
+ {
781
+ scope: "departments:read",
782
+ description: "Read the account's department data",
783
+ },
784
+ {
785
+ scope: "departments:write",
786
+ description: "Modify the account's departments",
787
+ },
788
+ ],
789
+ tokenEndpointAuthMethod: "client_secret_post",
790
+ // 17337/17338 are taken by eventbrite/calendly (all three providers
791
+ // landed in parallel and independently picked the next free port at the
792
+ // time); 17339 is the real next-free slot once all three are seeded
793
+ // together.
794
+ loopbackPort: 17339,
795
+ managedServiceConfigKey: "monday-oauth",
796
+ injectionTemplates: [
797
+ {
798
+ hostPattern: "api.monday.com",
799
+ injectionType: "header",
800
+ headerName: "Authorization",
801
+ valuePrefix: "Bearer ",
802
+ },
803
+ ],
804
+ appType: "App",
805
+ identityUrl: "https://api.monday.com/v2",
806
+ identityMethod: "POST",
807
+ identityHeaders: { "Content-Type": "application/json" },
808
+ identityBody: { query: "{ me { id name email } }" },
809
+ identityResponsePaths: ["data.me.name", "data.me.email"],
810
+ featureFlag: "monday-oauth",
811
+ },
812
+
813
+ eventbrite: {
814
+ provider: "eventbrite",
815
+ authorizeUrl: "https://www.eventbrite.com/oauth/authorize",
816
+ tokenExchangeUrl: "https://www.eventbrite.com/oauth/token",
817
+ refreshUrl: "https://www.eventbrite.com/oauth/token",
818
+ pingUrl: "https://www.eventbriteapi.com/v3/users/me/",
819
+ baseUrl: "https://www.eventbriteapi.com",
820
+ displayLabel: "Eventbrite",
821
+ description: "Events, attendees, and ticket orders",
822
+ dashboardUrl: "https://www.eventbrite.com/platform/api-keys/",
823
+ clientIdPlaceholder: null,
824
+ // Simple Icons does not host an Eventbrite mark (cdn.simpleicons.org
825
+ // 404s), so use the documented thesvg fallback.
826
+ logoUrl:
827
+ "https://cdn.jsdelivr.net/gh/glincker/thesvg@main/public/icons/eventbrite/default.svg",
828
+ // Eventbrite has no granular OAuth scope system: an access token carries
829
+ // the full permissions of the user who authorized it, and the authorize
830
+ // endpoint ignores a `scope` parameter. Seed an empty set rather than
831
+ // inventing scope strings the provider would silently drop (same shape
832
+ // as Notion).
833
+ defaultScopes: [],
834
+ tokenEndpointAuthMethod: "client_secret_post",
835
+ loopbackPort: 17337,
836
+ managedServiceConfigKey: "eventbrite-oauth",
837
+ injectionTemplates: [
838
+ {
839
+ hostPattern: "www.eventbriteapi.com",
840
+ injectionType: "header",
841
+ headerName: "Authorization",
842
+ valuePrefix: "Bearer ",
843
+ },
844
+ ],
845
+ appType: "App",
846
+ identityUrl: "https://www.eventbriteapi.com/v3/users/me/",
847
+ identityResponsePaths: ["name", "first_name"],
848
+ },
849
+
850
+ calendly: {
851
+ provider: "calendly",
852
+ authorizeUrl: "https://auth.calendly.com/oauth/authorize",
853
+ tokenExchangeUrl: "https://auth.calendly.com/oauth/token",
854
+ pingUrl: "https://api.calendly.com/users/me",
855
+ baseUrl: "https://api.calendly.com",
856
+ displayLabel: "Calendly",
857
+ description: "Scheduling links and meetings",
858
+ dashboardUrl: "https://calendly.com/integrations/api_webhooks",
859
+ clientIdPlaceholder: null,
860
+ logoUrl: "https://cdn.simpleicons.org/calendly",
861
+ // A :write scope implicitly grants the matching :read in the same domain,
862
+ // so the baseline stays at the six that map to reading schedule data and
863
+ // creating bookings/links. Organization-wide visibility is opt-in.
864
+ defaultScopes: [
865
+ "users:read",
866
+ "event_types:read",
867
+ "scheduled_events:read",
868
+ "availability:read",
869
+ "scheduled_events:write",
870
+ "scheduling_links:write",
871
+ ],
872
+ availableScopes: [
873
+ { scope: "users:read", description: "Read the connected user's profile" },
874
+ {
875
+ scope: "event_types:read",
876
+ description: "Read event types and their available times",
877
+ },
878
+ {
879
+ scope: "event_types:write",
880
+ description: "Create and update event types",
881
+ },
882
+ {
883
+ scope: "scheduled_events:read",
884
+ description: "Read scheduled events and invitees",
885
+ },
886
+ {
887
+ scope: "scheduled_events:write",
888
+ description: "Create invitees, cancel events, mark no-shows",
889
+ },
890
+ {
891
+ scope: "availability:read",
892
+ description: "Read busy times and availability schedules",
893
+ },
894
+ {
895
+ scope: "availability:write",
896
+ description: "Update event type availability",
897
+ },
898
+ {
899
+ scope: "scheduling_links:write",
900
+ description: "Create single-use scheduling links",
901
+ },
902
+ {
903
+ scope: "shares:write",
904
+ description: "Create customized single-use scheduling links",
905
+ },
906
+ {
907
+ scope: "organizations:read",
908
+ description: "Read organization data and memberships",
909
+ },
910
+ {
911
+ scope: "groups:read",
912
+ description: "Read group details and relationships",
913
+ },
914
+ {
915
+ scope: "webhooks:read",
916
+ description: "Read webhook subscriptions",
917
+ },
918
+ {
919
+ scope: "webhooks:write",
920
+ description: "Create and delete webhook subscriptions",
921
+ },
922
+ { scope: "contacts:read", description: "Read contact details" },
923
+ {
924
+ scope: "contacts:write",
925
+ description: "Create, update, and delete contacts",
926
+ },
927
+ ],
928
+ tokenEndpointAuthMethod: "client_secret_basic",
929
+ // 17337 is taken by eventbrite (both providers landed in parallel and
930
+ // independently picked the next free port at the time); 17338 is the
931
+ // real next-free slot once both are seeded together.
932
+ loopbackPort: 17338,
933
+ managedServiceConfigKey: "calendly-oauth",
934
+ injectionTemplates: [
935
+ {
936
+ hostPattern: "api.calendly.com",
937
+ injectionType: "header",
938
+ headerName: "Authorization",
939
+ valuePrefix: "Bearer ",
940
+ },
941
+ ],
942
+ revokeUrl: "https://auth.calendly.com/oauth/revoke",
943
+ revokeBodyTemplate: { token: "{access_token}" },
944
+ appType: "App",
945
+ identityUrl: "https://api.calendly.com/users/me",
946
+ identityResponsePaths: ["resource.email", "resource.name"],
947
+ },
948
+
687
949
  figma: {
688
950
  provider: "figma",
689
951
  authorizeUrl: "https://www.figma.com/oauth",
@@ -547,6 +547,27 @@ describe("Permission Checker (gateway IPC)", () => {
547
547
  expect(result.level).toBe(RiskLevel.Medium);
548
548
  });
549
549
 
550
+ test("builds PowerShell params for Windows host_bash", async () => {
551
+ mockIpcClassifyRiskResult = {
552
+ risk: "high",
553
+ reason: "PowerShell mutation",
554
+ matchType: "registry",
555
+ scopeOptions: [],
556
+ };
557
+
558
+ await classifyRisk(
559
+ "host_bash",
560
+ { command: "Remove-Item C:\\Temp\\data" },
561
+ "C:\\Users\\Example",
562
+ undefined,
563
+ undefined,
564
+ undefined,
565
+ "powershell",
566
+ );
567
+
568
+ expect(lastClassifyRiskParams?.shell).toBe("powershell");
569
+ });
570
+
550
571
  test("builds params for file tools", async () => {
551
572
  mockIpcClassifyRiskResult = {
552
573
  risk: "low",
@@ -420,6 +420,7 @@ function buildClassifyRiskParams(
420
420
  input: Record<string, unknown>,
421
421
  workingDir?: string,
422
422
  manifestOverride?: ManifestOverride,
423
+ shell?: ClassifyRiskIpcParams["shell"],
423
424
  ): ClassifyRiskIpcParams {
424
425
  // ── Bash/host_bash ──
425
426
  if (toolName === "bash" || toolName === "host_bash") {
@@ -437,6 +438,7 @@ function buildClassifyRiskParams(
437
438
  return {
438
439
  tool: toolName,
439
440
  command: getStringField(input, "command"),
441
+ shell,
440
442
  workingDir,
441
443
  workspaceRoot: getWorkspaceDir(),
442
444
  isContainerized: getIsContainerized(),
@@ -577,6 +579,7 @@ export async function classifyRisk(
577
579
  _preParsed?: unknown,
578
580
  manifestOverride?: ManifestOverride,
579
581
  signal?: AbortSignal,
582
+ shell?: ClassifyRiskIpcParams["shell"],
580
583
  ): Promise<RiskClassificationWithMeta> {
581
584
  signal?.throwIfAborted();
582
585
 
@@ -585,6 +588,7 @@ export async function classifyRisk(
585
588
  input,
586
589
  workingDir,
587
590
  manifestOverride,
591
+ shell,
588
592
  );
589
593
  const gatewayResult = await ipcClassifyRisk(ipcParams, signal);
590
594
  // A mid-retry cancellation should surface as an AbortError, not the
@@ -709,6 +713,7 @@ export async function check(
709
713
  policyContext?.conversationId,
710
714
  policyContext?.executionContext,
711
715
  cellQuery,
716
+ policyContext?.requesterContactId,
712
717
  );
713
718
  const approvalContext: ApprovalContext = {
714
719
  riskLevel: risk,
@@ -740,6 +745,7 @@ export async function check(
740
745
  policyContext?.conversationId,
741
746
  policyContext?.executionContext,
742
747
  cellQuery,
748
+ policyContext?.requesterContactId,
743
749
  );
744
750
  if (freshThreshold !== null && freshThreshold !== threshold) {
745
751
  approvalDecision = defaultApprovalPolicy.evaluate({
@@ -37,6 +37,9 @@ import {
37
37
  _clearGlobalCacheForTesting,
38
38
  _resetFailureCoalesceForTesting,
39
39
  getAutoApproveThreshold,
40
+ getContactAutoApproveThreshold,
41
+ invalidateAllContactThresholdCaches,
42
+ invalidateContactThresholdCache,
40
43
  refreshAutoApproveThreshold,
41
44
  } from "./gateway-threshold-reader.js";
42
45
 
@@ -633,3 +636,184 @@ describe("contact cell layer failure semantics", () => {
633
636
  ).toBe("low");
634
637
  });
635
638
  });
639
+
640
+ describe("contact-level ceiling", () => {
641
+ beforeEach(() => {
642
+ _clearGlobalCacheForTesting();
643
+ _resetFailureCoalesceForTesting();
644
+ ipcHandlers.clear();
645
+ ipcCallLog.length = 0;
646
+ });
647
+
648
+ test("the conversation override beats the contact ceiling", async () => {
649
+ ipcHandlers.set("get_conversation_threshold", () => ({
650
+ threshold: "none",
651
+ }));
652
+ ipcHandlers.set("get_contact_threshold", () => ({ threshold: "high" }));
653
+ ipcHandlers.set("resolve_channel_permission_threshold", () => ({
654
+ resolved: { threshold: "low", scope: "channel" },
655
+ }));
656
+ setGlobals("low");
657
+
658
+ expect(
659
+ await getAutoApproveThreshold(
660
+ "conv-ct1",
661
+ "conversation",
662
+ CELL_QUERY,
663
+ "contact-1",
664
+ ),
665
+ ).toBe("none");
666
+ expect(countCalls("get_contact_threshold")).toBe(0);
667
+ expect(countCalls("resolve_channel_permission_threshold")).toBe(0);
668
+ });
669
+
670
+ test("the contact ceiling beats the cell", async () => {
671
+ ipcHandlers.set("get_conversation_threshold", () => null);
672
+ ipcHandlers.set("get_contact_threshold", () => ({ threshold: "high" }));
673
+ ipcHandlers.set("resolve_channel_permission_threshold", () => ({
674
+ resolved: { threshold: "none", scope: "channel" },
675
+ }));
676
+ setGlobals("low");
677
+
678
+ expect(
679
+ await getAutoApproveThreshold(
680
+ "conv-ct2",
681
+ "conversation",
682
+ CELL_QUERY,
683
+ "contact-1",
684
+ ),
685
+ ).toBe("high");
686
+ expect(countCalls("resolve_channel_permission_threshold")).toBe(0);
687
+ });
688
+
689
+ test("the contact ceiling is not collapsed", async () => {
690
+ ipcHandlers.set("get_conversation_threshold", () => null);
691
+ ipcHandlers.set("get_contact_threshold", () => ({ threshold: "medium" }));
692
+ ipcHandlers.set("resolve_channel_permission_threshold", () => ({
693
+ resolved: { threshold: "none", scope: "channel" },
694
+ }));
695
+ setGlobals("low");
696
+
697
+ expect(
698
+ await getAutoApproveThreshold(
699
+ "conv-ct3",
700
+ "conversation",
701
+ CELL_QUERY,
702
+ "contact-1",
703
+ ),
704
+ ).toBe("medium");
705
+ });
706
+
707
+ test("a null contact ceiling inherits the cell cascade", async () => {
708
+ ipcHandlers.set("get_conversation_threshold", () => null);
709
+ ipcHandlers.set("get_contact_threshold", () => null);
710
+ ipcHandlers.set("resolve_channel_permission_threshold", () => ({
711
+ resolved: { threshold: "none", scope: "channel" },
712
+ }));
713
+ setGlobals("high");
714
+
715
+ expect(
716
+ await getAutoApproveThreshold(
717
+ "conv-ct4",
718
+ "conversation",
719
+ CELL_QUERY,
720
+ "contact-1",
721
+ ),
722
+ ).toBe("none");
723
+ });
724
+
725
+ test("a contact-threshold transport failure falls through to the cell", async () => {
726
+ ipcHandlers.set("get_conversation_threshold", () => null);
727
+ ipcHandlers.set("resolve_channel_permission_threshold", () => ({
728
+ resolved: { threshold: "none", scope: "channel" },
729
+ }));
730
+ setGlobals("high");
731
+
732
+ expect(
733
+ await getAutoApproveThreshold(
734
+ "conv-ct-fail",
735
+ "conversation",
736
+ CELL_QUERY,
737
+ "contact-1",
738
+ ),
739
+ ).toBe("none");
740
+ });
741
+
742
+ test("refresh honors the contact ceiling after a conversation miss", async () => {
743
+ ipcHandlers.set("get_conversation_threshold", () => null);
744
+ ipcHandlers.set("get_contact_threshold", () => ({ threshold: "high" }));
745
+ ipcHandlers.set("resolve_channel_permission_threshold", () => ({
746
+ resolved: { threshold: "none", scope: "channel" },
747
+ }));
748
+ setGlobals("low");
749
+
750
+ expect(
751
+ await refreshAutoApproveThreshold(
752
+ "conv-ct5",
753
+ "conversation",
754
+ CELL_QUERY,
755
+ "contact-1",
756
+ ),
757
+ ).toBe("high");
758
+ expect(countCalls("resolve_channel_permission_threshold")).toBe(0);
759
+ });
760
+
761
+ test("refresh keeps the prompt when the contact lookup fails", async () => {
762
+ ipcHandlers.set("get_conversation_threshold", () => null);
763
+ ipcHandlers.set("resolve_channel_permission_threshold", () => ({
764
+ resolved: { threshold: "low", scope: "channel" },
765
+ }));
766
+ setGlobals("high");
767
+
768
+ expect(
769
+ await refreshAutoApproveThreshold(
770
+ "conv-ct6",
771
+ "conversation",
772
+ CELL_QUERY,
773
+ "contact-1",
774
+ ),
775
+ ).toBeNull();
776
+ expect(countCalls("resolve_channel_permission_threshold")).toBe(0);
777
+ });
778
+
779
+ test("getContactAutoApproveThreshold returns a live ceiling and caches it", async () => {
780
+ ipcHandlers.set("get_contact_threshold", () => ({ threshold: "high" }));
781
+
782
+ expect(await getContactAutoApproveThreshold("contact-1")).toBe("high");
783
+ expect(await getContactAutoApproveThreshold("contact-1")).toBe("high");
784
+ expect(countCalls("get_contact_threshold")).toBe(1);
785
+ });
786
+
787
+ test("invalidateContactThresholdCache drops a cached ceiling", async () => {
788
+ ipcHandlers.set("get_contact_threshold", () => ({ threshold: "high" }));
789
+ expect(await getContactAutoApproveThreshold("contact-1")).toBe("high");
790
+
791
+ invalidateContactThresholdCache("contact-1");
792
+ ipcHandlers.set("get_contact_threshold", () => ({ threshold: "low" }));
793
+
794
+ expect(await getContactAutoApproveThreshold("contact-1")).toBe("low");
795
+ expect(countCalls("get_contact_threshold")).toBe(2);
796
+ });
797
+
798
+ test("invalidateAllContactThresholdCaches drops every cached ceiling", async () => {
799
+ ipcHandlers.set("get_contact_threshold", () => ({ threshold: "high" }));
800
+ expect(await getContactAutoApproveThreshold("contact-1")).toBe("high");
801
+
802
+ invalidateAllContactThresholdCaches();
803
+ ipcHandlers.set("get_contact_threshold", () => ({ threshold: "none" }));
804
+
805
+ expect(await getContactAutoApproveThreshold("contact-1")).toBe("none");
806
+ expect(countCalls("get_contact_threshold")).toBe(2);
807
+ });
808
+
809
+ test("getContactAutoApproveThreshold returns null for an unset contact", async () => {
810
+ ipcHandlers.set("get_contact_threshold", () => null);
811
+
812
+ expect(await getContactAutoApproveThreshold("contact-1")).toBeNull();
813
+ });
814
+
815
+ test("getContactAutoApproveThreshold returns null when contactId is missing", async () => {
816
+ expect(await getContactAutoApproveThreshold(undefined)).toBeNull();
817
+ expect(countCalls("get_contact_threshold")).toBe(0);
818
+ });
819
+ });