@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
@@ -16,6 +16,13 @@
16
16
  * - `delete_credential` — Delete a credential by account name
17
17
  * - `list_credentials` — List all credential account names
18
18
  * - `bulk_set_credentials` — Store multiple credentials at once
19
+ *
20
+ * **Credential records** (non-secret identity + policy)
21
+ * - `get_credential_record` — Retrieve a credential record by account name
22
+ * - `set_credential_record` — Store or update a credential record
23
+ * - `delete_credential_record` — Delete a credential record by account name
24
+ * - `list_credential_records` — List all credential records
25
+ * - `bulk_set_credential_records` — Store multiple credential records at once
19
26
  */
20
27
 
21
28
  import { z } from "zod";
@@ -38,6 +45,16 @@ export const CesRpcMethod = {
38
45
  ListCredentials: "list_credentials",
39
46
  /** Bulk-import credentials (set multiple at once). */
40
47
  BulkSetCredentials: "bulk_set_credentials",
48
+ /** Retrieve a single credential record (identity + policy) by account name. */
49
+ GetCredentialRecord: "get_credential_record",
50
+ /** Store or update a credential record by account name. */
51
+ SetCredentialRecord: "set_credential_record",
52
+ /** Delete a credential record by account name. */
53
+ DeleteCredentialRecord: "delete_credential_record",
54
+ /** List all credential records. */
55
+ ListCredentialRecords: "list_credential_records",
56
+ /** Bulk-import credential records. */
57
+ BulkSetCredentialRecords: "bulk_set_credential_records",
41
58
  } as const;
42
59
 
43
60
  export type CesRpcMethod = (typeof CesRpcMethod)[keyof typeof CesRpcMethod];
@@ -168,6 +185,123 @@ export type BulkSetCredentialsResponse = z.infer<
168
185
  typeof BulkSetCredentialsResponseSchema
169
186
  >;
170
187
 
188
+ // ---------------------------------------------------------------------------
189
+ // Credential records (identity + policy, never secret values)
190
+ // ---------------------------------------------------------------------------
191
+
192
+ export const CredentialInjectionTemplateSchema = z.object({
193
+ hostPattern: z.string(),
194
+ injectionType: z.enum(["header", "query"]),
195
+ headerName: z.string().optional(),
196
+ valuePrefix: z.string().optional(),
197
+ queryParamName: z.string().optional(),
198
+ composeWith: z
199
+ .object({
200
+ service: z.string(),
201
+ field: z.string(),
202
+ separator: z.string(),
203
+ })
204
+ .optional(),
205
+ valueTransform: z.enum(["base64"]).optional(),
206
+ });
207
+ export type CredentialInjectionTemplate = z.infer<
208
+ typeof CredentialInjectionTemplateSchema
209
+ >;
210
+
211
+ export const CredentialRecordSchema = z.object({
212
+ credentialId: z.string(),
213
+ service: z.string(),
214
+ field: z.string(),
215
+ allowedTools: z.array(z.string()),
216
+ allowedDomains: z.array(z.string()),
217
+ usageDescription: z.string().optional(),
218
+ alias: z.string().optional(),
219
+ injectionTemplates: z.array(CredentialInjectionTemplateSchema).optional(),
220
+ createdAt: z.number(),
221
+ updatedAt: z.number(),
222
+ });
223
+ export type CredentialRecord = z.infer<typeof CredentialRecordSchema>;
224
+
225
+ export const GetCredentialRecordSchema = z.object({
226
+ /** The account name to look up (`credential/{service}/{field}`). */
227
+ account: z.string(),
228
+ });
229
+ export type GetCredentialRecord = z.infer<typeof GetCredentialRecordSchema>;
230
+
231
+ export const GetCredentialRecordResponseSchema = z.object({
232
+ found: z.boolean(),
233
+ record: CredentialRecordSchema.optional(),
234
+ });
235
+ export type GetCredentialRecordResponse = z.infer<
236
+ typeof GetCredentialRecordResponseSchema
237
+ >;
238
+
239
+ export const SetCredentialRecordSchema = z.object({
240
+ account: z.string(),
241
+ record: CredentialRecordSchema,
242
+ });
243
+ export type SetCredentialRecord = z.infer<typeof SetCredentialRecordSchema>;
244
+
245
+ export const SetCredentialRecordResponseSchema = z.object({
246
+ ok: z.boolean(),
247
+ });
248
+ export type SetCredentialRecordResponse = z.infer<
249
+ typeof SetCredentialRecordResponseSchema
250
+ >;
251
+
252
+ export const DeleteCredentialRecordSchema = z.object({
253
+ account: z.string(),
254
+ });
255
+ export type DeleteCredentialRecord = z.infer<
256
+ typeof DeleteCredentialRecordSchema
257
+ >;
258
+
259
+ export const DeleteCredentialRecordResponseSchema = z.object({
260
+ result: z.enum(["deleted", "not-found", "error"]),
261
+ });
262
+ export type DeleteCredentialRecordResponse = z.infer<
263
+ typeof DeleteCredentialRecordResponseSchema
264
+ >;
265
+
266
+ export const ListCredentialRecordsSchema = z.object({});
267
+ export type ListCredentialRecords = z.infer<typeof ListCredentialRecordsSchema>;
268
+
269
+ export const ListCredentialRecordsResponseSchema = z.object({
270
+ records: z.array(
271
+ z.object({
272
+ account: z.string(),
273
+ record: CredentialRecordSchema,
274
+ }),
275
+ ),
276
+ });
277
+ export type ListCredentialRecordsResponse = z.infer<
278
+ typeof ListCredentialRecordsResponseSchema
279
+ >;
280
+
281
+ export const BulkSetCredentialRecordsSchema = z.object({
282
+ records: z.array(
283
+ z.object({
284
+ account: z.string(),
285
+ record: CredentialRecordSchema,
286
+ }),
287
+ ),
288
+ });
289
+ export type BulkSetCredentialRecords = z.infer<
290
+ typeof BulkSetCredentialRecordsSchema
291
+ >;
292
+
293
+ export const BulkSetCredentialRecordsResponseSchema = z.object({
294
+ results: z.array(
295
+ z.object({
296
+ account: z.string(),
297
+ ok: z.boolean(),
298
+ }),
299
+ ),
300
+ });
301
+ export type BulkSetCredentialRecordsResponse = z.infer<
302
+ typeof BulkSetCredentialRecordsResponseSchema
303
+ >;
304
+
171
305
  // ---------------------------------------------------------------------------
172
306
  // Full RPC contract type map
173
307
  // ---------------------------------------------------------------------------
@@ -201,6 +335,26 @@ export interface CesRpcContract {
201
335
  request: BulkSetCredentials;
202
336
  response: BulkSetCredentialsResponse;
203
337
  };
338
+ [CesRpcMethod.GetCredentialRecord]: {
339
+ request: GetCredentialRecord;
340
+ response: GetCredentialRecordResponse;
341
+ };
342
+ [CesRpcMethod.SetCredentialRecord]: {
343
+ request: SetCredentialRecord;
344
+ response: SetCredentialRecordResponse;
345
+ };
346
+ [CesRpcMethod.DeleteCredentialRecord]: {
347
+ request: DeleteCredentialRecord;
348
+ response: DeleteCredentialRecordResponse;
349
+ };
350
+ [CesRpcMethod.ListCredentialRecords]: {
351
+ request: ListCredentialRecords;
352
+ response: ListCredentialRecordsResponse;
353
+ };
354
+ [CesRpcMethod.BulkSetCredentialRecords]: {
355
+ request: BulkSetCredentialRecords;
356
+ response: BulkSetCredentialRecordsResponse;
357
+ };
204
358
  }
205
359
 
206
360
  /**
@@ -231,4 +385,24 @@ export const CesRpcSchemas = {
231
385
  request: BulkSetCredentialsSchema,
232
386
  response: BulkSetCredentialsResponseSchema,
233
387
  },
388
+ [CesRpcMethod.GetCredentialRecord]: {
389
+ request: GetCredentialRecordSchema,
390
+ response: GetCredentialRecordResponseSchema,
391
+ },
392
+ [CesRpcMethod.SetCredentialRecord]: {
393
+ request: SetCredentialRecordSchema,
394
+ response: SetCredentialRecordResponseSchema,
395
+ },
396
+ [CesRpcMethod.DeleteCredentialRecord]: {
397
+ request: DeleteCredentialRecordSchema,
398
+ response: DeleteCredentialRecordResponseSchema,
399
+ },
400
+ [CesRpcMethod.ListCredentialRecords]: {
401
+ request: ListCredentialRecordsSchema,
402
+ response: ListCredentialRecordsResponseSchema,
403
+ },
404
+ [CesRpcMethod.BulkSetCredentialRecords]: {
405
+ request: BulkSetCredentialRecordsSchema,
406
+ response: BulkSetCredentialRecordsResponseSchema,
407
+ },
234
408
  } as const;
@@ -20,6 +20,7 @@ describe("contact read contracts", () => {
20
20
  contactType: "human",
21
21
  lastInteraction: 1700000000,
22
22
  interactionCount: 12,
23
+ autoApproveThreshold: "medium",
23
24
  createdAt: 1699000000,
24
25
  updatedAt: 1700000000,
25
26
  channels: [
@@ -46,6 +47,7 @@ describe("contact read contracts", () => {
46
47
  const parsed = ContactReadSchema.parse(contact);
47
48
  expect(parsed.createdAt).toBe(1699000000);
48
49
  expect(parsed.updatedAt).toBe(1700000000);
50
+ expect(parsed.autoApproveThreshold).toBe("medium");
49
51
  });
50
52
 
51
53
  test("ContactReadSchema accepts null interactionCount (daemon-native gateway-telemetry outage fail-soft)", () => {
@@ -82,6 +84,36 @@ describe("contact read contracts", () => {
82
84
  const parsed = ContactReadSchema.parse(contact);
83
85
  expect(parsed.interactionCount).toBeNull();
84
86
  expect(parsed.channels[0].interactionCount).toBeNull();
87
+ expect(parsed.autoApproveThreshold).toBeUndefined();
88
+ });
89
+
90
+ test("ContactReadSchema accepts a null auto-approve threshold", () => {
91
+ const contact = {
92
+ id: "c1",
93
+ displayName: "Example User",
94
+ role: "contact",
95
+ interactionCount: 0,
96
+ autoApproveThreshold: null,
97
+ createdAt: 1699000000,
98
+ updatedAt: 1700000000,
99
+ channels: [],
100
+ };
101
+ expect(ContactReadSchema.parse(contact).autoApproveThreshold).toBeNull();
102
+ });
103
+
104
+ test("ContactReadSchema rejects an unknown auto-approve threshold", () => {
105
+ expect(() =>
106
+ ContactReadSchema.parse({
107
+ id: "c1",
108
+ displayName: "Example User",
109
+ role: "contact",
110
+ interactionCount: 0,
111
+ autoApproveThreshold: "full",
112
+ createdAt: 1699000000,
113
+ updatedAt: 1700000000,
114
+ channels: [],
115
+ }),
116
+ ).toThrow();
85
117
  });
86
118
 
87
119
  test("ContactReadSchema requires createdAt/updatedAt", () => {
@@ -30,6 +30,7 @@ describe("package independence", () => {
30
30
  "../http-delivery.ts",
31
31
  "../ipc-client.ts",
32
32
  "../gateway-ipc-contracts.ts",
33
+ "../plugin-admission-denied-contract.ts",
33
34
  ];
34
35
 
35
36
  for (const file of sourceFiles) {
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The kind derivation is the single reader of the field encodings, so its
3
+ * matrix is the compatibility contract: every shape an unstamped replayed
4
+ * retry payload can carry must classify exactly as the consumers that read
5
+ * those fields directly would classify it.
6
+ */
7
+ import { describe, expect, test } from "bun:test";
8
+
9
+ import {
10
+ inboundEventRefersToAnotherMessage,
11
+ resolveInboundEventKind,
12
+ } from "../inbound-event-kind.js";
13
+
14
+ describe("resolveInboundEventKind", () => {
15
+ test("a stamped kind wins over every legacy field", () => {
16
+ expect(
17
+ resolveInboundEventKind({
18
+ eventKind: "edit",
19
+ callbackData: "message_deleted",
20
+ }),
21
+ ).toBe("edit");
22
+ });
23
+
24
+ test("an unrecognized stamp falls back to legacy derivation", () => {
25
+ expect(
26
+ resolveInboundEventKind({ eventKind: "future-kind", isEdit: true }),
27
+ ).toBe("edit");
28
+ });
29
+
30
+ test("legacy isEdit classifies as edit", () => {
31
+ expect(resolveInboundEventKind({ isEdit: true })).toBe("edit");
32
+ });
33
+
34
+ test("the delete sentinel classifies as delete", () => {
35
+ expect(resolveInboundEventKind({ callbackData: "message_deleted" })).toBe(
36
+ "delete",
37
+ );
38
+ });
39
+
40
+ test("reaction prefixes classify as reaction, both directions", () => {
41
+ expect(resolveInboundEventKind({ callbackData: "reaction:+1" })).toBe(
42
+ "reaction",
43
+ );
44
+ expect(
45
+ resolveInboundEventKind({ callbackData: "reaction_removed:+1" }),
46
+ ).toBe("reaction");
47
+ });
48
+
49
+ test("any other callbackData classifies as button", () => {
50
+ expect(
51
+ resolveInboundEventKind({ callbackData: "apr:req-1:approve_once" }),
52
+ ).toBe("button");
53
+ expect(resolveInboundEventKind({ callbackData: "some-action-id" })).toBe(
54
+ "button",
55
+ );
56
+ });
57
+
58
+ test("an empty callbackData is not a family", () => {
59
+ // Mirrors the old hasCallbackData predicate, which required length > 0.
60
+ expect(resolveInboundEventKind({ callbackData: "" })).toBe("message");
61
+ });
62
+
63
+ test("callbackQueryId alone classifies as button", () => {
64
+ expect(resolveInboundEventKind({ callbackQueryId: "cbq-1" })).toBe(
65
+ "button",
66
+ );
67
+ });
68
+
69
+ test("nothing stated is a plain message", () => {
70
+ expect(resolveInboundEventKind({})).toBe("message");
71
+ });
72
+ });
73
+
74
+ describe("inboundEventRefersToAnotherMessage", () => {
75
+ test("only a plain message stands alone", () => {
76
+ expect(inboundEventRefersToAnotherMessage("message")).toBe(false);
77
+ expect(inboundEventRefersToAnotherMessage("edit")).toBe(true);
78
+ expect(inboundEventRefersToAnotherMessage("delete")).toBe(true);
79
+ expect(inboundEventRefersToAnotherMessage("reaction")).toBe(true);
80
+ expect(inboundEventRefersToAnotherMessage("button")).toBe(true);
81
+ });
82
+ });
@@ -0,0 +1,56 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ ACCESS_DENIED_NOT_APPROVED_REPLY,
5
+ PLUGIN_ADMISSION_DENIED_NOTICE_PATH,
6
+ PluginAdmissionDeniedNoticeSchema,
7
+ } from "../plugin-admission-denied-contract.js";
8
+
9
+ describe("plugin admission-denied contract", () => {
10
+ test("keeps the not-approved copy stable", () => {
11
+ expect(ACCESS_DENIED_NOT_APPROVED_REPLY).toBe(
12
+ "Sorry, you haven't been approved to message this assistant.",
13
+ );
14
+ });
15
+
16
+ test("names the internal plugin notice path", () => {
17
+ expect(PLUGIN_ADMISSION_DENIED_NOTICE_PATH).toBe(
18
+ "notices/admission-denied",
19
+ );
20
+ });
21
+
22
+ test("round-trips a floor-deny notice", () => {
23
+ const notice = {
24
+ reason: "admission_floor" as const,
25
+ plugin: "imessage",
26
+ ingressRoute: "events-linq",
27
+ admissionPolicy: "guardian_only" as const,
28
+ trustClass: "unknown" as const,
29
+ conversationExternalId: "chat-1",
30
+ actorExternalId: "+15550100",
31
+ externalMessageId: "msg-1",
32
+ replyText: ACCESS_DENIED_NOT_APPROVED_REPLY,
33
+ };
34
+
35
+ expect(PluginAdmissionDeniedNoticeSchema.parse(notice)).toEqual(notice);
36
+ });
37
+
38
+ test("rejects a notice that still carries a plugin-scoped id as the chat", () => {
39
+ // The send API addresses vendor chats. An empty chat id is the failure
40
+ // that matters; the schema only requires a non-empty string, so this
41
+ // asserts the required fields rather than the prefix rule.
42
+ expect(() =>
43
+ PluginAdmissionDeniedNoticeSchema.parse({
44
+ reason: "admission_floor",
45
+ plugin: "imessage",
46
+ ingressRoute: "events-linq",
47
+ admissionPolicy: "guardian_only",
48
+ trustClass: "unknown",
49
+ conversationExternalId: "",
50
+ actorExternalId: "+15550100",
51
+ externalMessageId: "msg-1",
52
+ replyText: ACCESS_DENIED_NOT_APPROVED_REPLY,
53
+ }),
54
+ ).toThrow();
55
+ });
56
+ });
@@ -31,6 +31,7 @@ const fullVerdict: TrustVerdict = {
31
31
  verifiedAt: 1699999999,
32
32
  interactionCount: 12,
33
33
  memberDisplayName: "Member Name",
34
+ autoApproveThreshold: "high",
34
35
  };
35
36
 
36
37
  describe("TrustVerdictSchema", () => {
@@ -38,6 +39,23 @@ describe("TrustVerdictSchema", () => {
38
39
  expect(TrustVerdictSchema.parse(fullVerdict)).toEqual(fullVerdict);
39
40
  });
40
41
 
42
+ test("parses a verdict with a null contact auto-approve threshold", () => {
43
+ const verdict = {
44
+ ...fullVerdict,
45
+ autoApproveThreshold: null,
46
+ } satisfies TrustVerdict;
47
+ expect(TrustVerdictSchema.parse(verdict)).toEqual(verdict);
48
+ });
49
+
50
+ test("rejects an unknown contact auto-approve threshold", () => {
51
+ expect(() =>
52
+ TrustVerdictSchema.parse({
53
+ ...fullVerdict,
54
+ autoApproveThreshold: "full",
55
+ }),
56
+ ).toThrow();
57
+ });
58
+
41
59
  test("parses a minimal verdict", () => {
42
60
  const minimal = {
43
61
  trustClass: "unknown",
@@ -3,9 +3,8 @@
3
3
  *
4
4
  * Both the gateway (channel admission policy storage + kill switch) and the
5
5
  * runtime (admission-policy stage) consume these values. Keeping the type
6
- * here avoids the runtime importing from `gateway/src` and avoids the
7
- * vocabulary drift the plan §2.1 flags for the verification-purpose
8
- * `trustClass` enum.
6
+ * here avoids the runtime importing from `gateway/src` and keeps both
7
+ * sides on a single shared vocabulary.
9
8
  */
10
9
 
11
10
  import { z } from "zod";
@@ -15,7 +14,7 @@ import type { TrustClass } from "./trust-verdict-contract.js";
15
14
  /**
16
15
  * Per-channel inbound admission policy — ordered from most-restrictive
17
16
  * (`no_one`, hard kill switch) to most-permissive (`strangers`, admits any
18
- * sender). See `unverified-contact-role-plan.md` §2.3.
17
+ * sender).
19
18
  */
20
19
  export const ADMISSION_POLICY_VALUES = [
21
20
  "no_one",
@@ -32,14 +31,14 @@ export const AdmissionPolicySchema = z.enum(ADMISSION_POLICY_VALUES);
32
31
  /**
33
32
  * Read-side default applied when a channel has no row in the DB. Matches
34
33
  * today's effective semantics: guardian + active contacts admitted,
35
- * strangers denied. See plan §2.2.
34
+ * strangers denied.
36
35
  */
37
36
  export const ADMISSION_POLICY_DEFAULT: AdmissionPolicy = "trusted_contacts";
38
37
 
39
38
  /**
40
39
  * Minimum trust rank required for each policy. Higher rank = more trusted.
41
40
  * `no_one` is 5 — above the maximum guardian rank (4) — so no class is ever
42
- * admitted. See plan §2.4 for the rank table.
41
+ * admitted.
43
42
  */
44
43
  export const ADMISSION_FLOOR: Record<AdmissionPolicy, number> = {
45
44
  no_one: 5,
@@ -82,15 +81,10 @@ export function isAdmissionPolicyExemptChannel(channelType: string): boolean {
82
81
  * `vellum` is the local desktop/web client surface; the guardian is always
83
82
  * max-rank there, so the seed default admits them regardless of the floor.
84
83
  *
85
- * `discord` has no ingress implementation, so there is nothing for a floor to
86
- * gate and nothing for the user to configure. Hiding keeps it pinned at the
87
- * seed default rather than offering a Channel Trust Floors row for a channel
88
- * that receives nothing.
89
84
  */
90
85
  export const ADMISSION_POLICY_HIDDEN_CHANNELS: ReadonlySet<string> = new Set([
91
86
  "vellum",
92
87
  "whatsapp",
93
- "discord",
94
88
  ]);
95
89
 
96
90
  export function isAdmissionPolicyHiddenChannel(channelType: string): boolean {
@@ -10,7 +10,9 @@
10
10
  *
11
11
  * Storage lives in the gateway DB (`channel_permission_overrides`) so the
12
12
  * assistant cannot tamper with it, matching `channel_admission_policy` and
13
- * `auto_approve_thresholds`.
13
+ * `auto_approve_thresholds`. A contact-level ceiling can live on
14
+ * `contacts.auto_approve_threshold` and is stamped on the trust verdict;
15
+ * that override is not a matrix cell.
14
16
  */
15
17
 
16
18
  import { z } from "zod";
@@ -5,6 +5,8 @@
5
5
  import { CHANNEL_IDS } from "@vellumai/service-contracts/channels";
6
6
  import { z } from "zod";
7
7
 
8
+ import { RiskThresholdSchema } from "./channel-permission-contract.js";
9
+
8
10
  export const GATEWAY_LOG_LEVEL_NAMES = [
9
11
  "trace",
10
12
  "debug",
@@ -275,6 +277,11 @@ export const ContactReadSchema = z.object({
275
277
  contactType: z.string().nullable().optional(),
276
278
  lastInteraction: z.number().nullable().optional(),
277
279
  interactionCount: z.number().nullable(),
280
+ /**
281
+ * Per-contact auto-approve ceiling. Null when unset (inherit cascade).
282
+ * Optional on the wire for version skew; gateway reads always emit it.
283
+ */
284
+ autoApproveThreshold: RiskThresholdSchema.nullable().optional(),
278
285
  createdAt: z.number(),
279
286
  updatedAt: z.number(),
280
287
  channels: z.array(ContactReadChannelSchema),
@@ -401,6 +408,8 @@ export type ClassifyRiskSkillMetadata = z.infer<
401
408
  export const ClassifyRiskIpcParamsSchema = z.object({
402
409
  tool: z.string().min(1),
403
410
  command: z.string().optional(),
411
+ /** Shell grammar used by host_bash on the target desktop. */
412
+ shell: z.enum(["bash", "powershell"]).optional(),
404
413
  url: z.string().optional(),
405
414
  path: z.string().optional(),
406
415
  /**
@@ -10,6 +10,7 @@
10
10
  * it in `handleChannelInbound()`.
11
11
  */
12
12
 
13
+ import { INBOUND_EVENT_KINDS } from "./inbound-event-kind.js";
13
14
  import { z } from "zod";
14
15
 
15
16
  import { ChannelConversationTypeSchema } from "./channel-permission-contract.js";
@@ -168,6 +169,9 @@ export const RuntimeInboundPayloadSchema = z.object({
168
169
  conversationExternalId: z.string(),
169
170
  externalMessageId: z.string(),
170
171
  content: z.string(),
172
+ /** The named event family; absent only on replayed retry payloads,
173
+ * where resolveInboundEventKind derives it from the legacy fields. */
174
+ eventKind: z.enum(INBOUND_EVENT_KINDS).optional(),
171
175
  isEdit: z.boolean().optional(),
172
176
  callbackQueryId: z.string().optional(),
173
177
  callbackData: z.string().optional(),
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The inbound event families, named.
3
+ *
4
+ * Every inbound event is one of these, and the kind decides which pipeline
5
+ * stages apply: only user-authored text (message, edit) can carry a
6
+ * verification code or an invite, only a message starts an agent turn, and
7
+ * edit/delete/reaction/button all refer to another message rather than
8
+ * standing alone. A payload can also encode its family in the field that
9
+ * carries it: `isEdit`, the sentinel string `"message_deleted"`, the
10
+ * `"reaction:"` prefix, the presence of `callbackData` at all. Replayed
11
+ * retry payloads arrive with only those encodings, so
12
+ * {@link resolveInboundEventKind} derives the kind from them exactly once;
13
+ * nothing else may sniff them.
14
+ */
15
+
16
+ export const INBOUND_EVENT_KINDS = [
17
+ "message",
18
+ "edit",
19
+ "delete",
20
+ "reaction",
21
+ "button",
22
+ ] as const;
23
+
24
+ export type InboundEventKind = (typeof INBOUND_EVENT_KINDS)[number];
25
+
26
+ export function isInboundEventKind(value: string): value is InboundEventKind {
27
+ return (INBOUND_EVENT_KINDS as readonly string[]).includes(value);
28
+ }
29
+
30
+ /**
31
+ * The one place the field encodings are read. A payload stamped with a
32
+ * kind wins outright; an unstamped one (a replayed retry payload) is
33
+ * classified by the fields that carry its family. The fallback order is
34
+ * specific-before-generic: reaction prefixes and the delete sentinel are
35
+ * particular `callbackData` values, so they are tested before the generic
36
+ * button reading.
37
+ */
38
+ export function resolveInboundEventKind(fields: {
39
+ eventKind?: string;
40
+ isEdit?: boolean;
41
+ callbackData?: string;
42
+ callbackQueryId?: string;
43
+ }): InboundEventKind {
44
+ if (fields.eventKind !== undefined && isInboundEventKind(fields.eventKind)) {
45
+ return fields.eventKind;
46
+ }
47
+ if (fields.isEdit === true) {
48
+ return "edit";
49
+ }
50
+ const cb = fields.callbackData;
51
+ if (cb !== undefined && cb.length > 0) {
52
+ if (cb === "message_deleted") {
53
+ return "delete";
54
+ }
55
+ if (cb.startsWith("reaction:") || cb.startsWith("reaction_removed:")) {
56
+ return "reaction";
57
+ }
58
+ return "button";
59
+ }
60
+ if (fields.callbackQueryId !== undefined) {
61
+ return "button";
62
+ }
63
+ return "message";
64
+ }
65
+
66
+ /**
67
+ * Whether an event of this kind refers to another message rather than
68
+ * standing alone as new content. Referring events never mint a
69
+ * conversation of their own and never carry ingestable attachments.
70
+ */
71
+ export function inboundEventRefersToAnotherMessage(
72
+ kind: InboundEventKind,
73
+ ): boolean {
74
+ return kind !== "message";
75
+ }
@@ -83,6 +83,23 @@ export {
83
83
 
84
84
  export type { AdmissionPolicy } from "./admission-policy-contract.js";
85
85
 
86
+ export {
87
+ INBOUND_EVENT_KINDS,
88
+ inboundEventRefersToAnotherMessage,
89
+ isInboundEventKind,
90
+ resolveInboundEventKind,
91
+ } from "./inbound-event-kind.js";
92
+ export type { InboundEventKind } from "./inbound-event-kind.js";
93
+
94
+ // Plugin admission-denied notice (gateway → plugin) — canned deny copy + envelope
95
+ export {
96
+ ACCESS_DENIED_NOT_APPROVED_REPLY,
97
+ PLUGIN_ADMISSION_DENIED_NOTICE_PATH,
98
+ PluginAdmissionDeniedNoticeSchema,
99
+ } from "./plugin-admission-denied-contract.js";
100
+
101
+ export type { PluginAdmissionDeniedNotice } from "./plugin-admission-denied-contract.js";
102
+
86
103
  // Trust verdict contract (gateway → daemon) — Zod schemas + derived types
87
104
  export {
88
105
  isTrustClass,
@@ -61,6 +61,14 @@ export const ApprovalUIMetadataSchema = z.object({
61
61
  actions: z.array(ApprovalActionOptionSchema),
62
62
  plainTextFallback: z.string(),
63
63
  permissionDetails: PermissionRequestDetailsSchema.optional(),
64
+ /**
65
+ * What the recipient is being asked to do, so a renderer can head the card
66
+ * with the right verb. Resolved once by the producer; a renderer that reads
67
+ * the actions to work it out would be parsing a payload it is meant only to
68
+ * draw. Absent means an approval, which is what every existing producer
69
+ * sends and what a renderer without this field already assumes.
70
+ */
71
+ intent: z.enum(["approval", "question"]).optional(),
64
72
  });
65
73
 
66
74
  export type ApprovalUIMetadata = z.infer<typeof ApprovalUIMetadataSchema>;