@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
@@ -9,13 +9,17 @@ import { PROVIDER_CATALOG } from "../providers/model-catalog.js";
9
9
  import { credentialKey } from "../security/credential-key.js";
10
10
  import { getLogger } from "../util/logger.js";
11
11
  import {
12
- getEffectiveProfiles,
12
+ getEffectiveProfilesForProvider,
13
13
  MANAGED_PROFILE_NAMES,
14
14
  MANAGED_PROFILE_TEMPLATES,
15
15
  } from "./default-profile-catalog.js";
16
16
  import { loadRawConfig, saveRawConfig } from "./loader.js";
17
17
  import { isDispatchableProfile } from "./profile-dispatchability.js";
18
- import { isDefaultProviderChoice, type ProfileEntry } from "./schemas/llm.js";
18
+ import {
19
+ type DefaultProviderConfig,
20
+ isDefaultProviderChoice,
21
+ type ProfileEntry,
22
+ } from "./schemas/llm.js";
19
23
 
20
24
  const log = getLogger("seed-inference-profiles");
21
25
 
@@ -141,8 +145,13 @@ export function seedInferenceProfiles(
141
145
  // Profile lookups below go through the effective view: a default profile
142
146
  // resolves from the code catalog whether or not the workspace carries a
143
147
  // stub for it, and a stub contributes only its status/label overlays.
144
- const effectiveProfiles = getEffectiveProfiles(
148
+ // Resolved against `llm.defaultProvider` so the managed backups count as
149
+ // available only on the managed column, matching what the schema accepts
150
+ // as a reference: an `activeProfile` naming a backup on a BYOK install is
151
+ // then repaired here rather than preserved as a dangling selection.
152
+ const effectiveProfiles = getEffectiveProfilesForProvider(
145
153
  profiles as Record<string, ProfileEntry>,
154
+ seedResolutionDefaultProvider(llm),
146
155
  ) as Record<string, Record<string, unknown>>;
147
156
 
148
157
  // Active profile resolution.
@@ -230,6 +239,26 @@ export function seedInferenceProfiles(
230
239
  saveRawConfig(config);
231
240
  }
232
241
 
242
+ /**
243
+ * The `llm.defaultProvider` to resolve the effective catalog against, read
244
+ * from RAW config. Only a value the matrix can implement is used: a
245
+ * hand-edited or legacy provider outside `DEFAULT_PROVIDER_CHOICES` has no
246
+ * column and no verified intent resolution, so materializing against it
247
+ * could throw and fail the whole boot seed. Such a value falls back to
248
+ * `null`, which resolves the managed column.
249
+ */
250
+ function seedResolutionDefaultProvider(
251
+ llm: Record<string, unknown>,
252
+ ): DefaultProviderConfig | null {
253
+ const raw = readObject(llm.defaultProvider);
254
+ const provider = readString(raw?.provider);
255
+ if (provider === undefined || !isDefaultProviderChoice(provider)) {
256
+ return null;
257
+ }
258
+ const connectionName = readString(raw?.connectionName);
259
+ return { provider, ...(connectionName ? { connectionName } : {}) };
260
+ }
261
+
233
262
  export function readObject(value: unknown): Record<string, unknown> | null {
234
263
  return value !== null && typeof value === "object" && !Array.isArray(value)
235
264
  ? (value as Record<string, unknown>)
@@ -1,3 +1,4 @@
1
+ import { isSkillCompatibleWithPlatform } from "../skills/platform-compatibility.js";
1
2
  import { isAssistantFeatureFlagEnabled } from "./assistant-feature-flags.js";
2
3
  import type { AssistantConfig, SkillEntryConfig } from "./schema.js";
3
4
  import type { SkillSummary } from "./skills.js";
@@ -31,6 +32,10 @@ export function resolveSkillStates(
31
32
  };
32
33
 
33
34
  for (const skill of catalog) {
35
+ if (!isSkillCompatibleWithPlatform(skill)) {
36
+ continue;
37
+ }
38
+
34
39
  // Assistant feature flag gate: if the skill declares a flag and it's disabled, skip it
35
40
  const flagKey = skillFlagKey(skill);
36
41
  if (flagKey && !isAssistantFeatureFlagEnabled(flagKey, config)) {
@@ -18,11 +18,17 @@ import {
18
18
 
19
19
  import { z } from "zod";
20
20
 
21
+ import type { ClientOs } from "../channels/types.js";
21
22
  import { getDefaultPluginSkillRoots } from "../plugins/defaults/main.js";
22
23
  import { isPluginDisabled } from "../plugins/disabled-state.js";
23
24
  import { parseFrontmatterFields } from "../skills/frontmatter.js";
24
25
  import type { InlineCommandExpansion } from "../skills/inline-command-expansions.js";
25
26
  import { parseInlineCommandExpansions } from "../skills/inline-command-expansions.js";
27
+ import {
28
+ normalizeSkillPlatforms,
29
+ SKILL_PLATFORM_VALUES,
30
+ type SkillPlatform,
31
+ } from "../skills/platform-compatibility.js";
26
32
  import { parseToolManifestFile } from "../skills/tool-manifest.js";
27
33
  import { computeSkillVersionHash } from "../skills/version-hash.js";
28
34
  import type { OwnerInfo } from "../tools/types.js";
@@ -50,6 +56,7 @@ const VellumMetadataSchema = z
50
56
  "avoid-when": z.array(z.string()).optional(),
51
57
  category: z.string().optional(),
52
58
  "always-candidate": z.boolean().optional(),
59
+ platforms: z.array(z.enum(SKILL_PLATFORM_VALUES)).optional(),
53
60
  })
54
61
  .passthrough();
55
62
 
@@ -128,6 +135,8 @@ export interface SkillSummary {
128
135
  * the model must judge, not embedding similarity.
129
136
  */
130
137
  alwaysCandidate?: boolean;
138
+ /** Host operating systems on which this skill may be offered and loaded. */
139
+ platforms?: SkillPlatform[];
131
140
  /** Parsed inline command expansion descriptors (`!\`command\``) found in the skill body. */
132
141
  inlineCommandExpansions?: InlineCommandExpansion[];
133
142
  }
@@ -184,6 +193,8 @@ export interface SkillToolEntry {
184
193
  executor: string;
185
194
  /** Where the tool script runs. */
186
195
  execution_target: "host" | "sandbox";
196
+ /** Client operating systems that may expose this tool. Unset means all. */
197
+ supported_client_os?: ClientOs[];
187
198
  }
188
199
 
189
200
  /**
@@ -251,6 +262,7 @@ interface ParsedFrontmatter {
251
262
  avoidWhen?: string[];
252
263
  category?: string;
253
264
  alwaysCandidate?: boolean;
265
+ platforms?: SkillPlatform[];
254
266
  inlineCommandExpansions?: InlineCommandExpansion[];
255
267
  }
256
268
 
@@ -373,6 +385,8 @@ function parseFrontmatter(
373
385
  ? vellum["always-candidate"]
374
386
  : undefined;
375
387
 
388
+ const platforms = normalizeSkillPlatforms(vellum?.platforms);
389
+
376
390
  const strippedBody = stripCommentLines(body);
377
391
 
378
392
  // Parse inline command expansions from the body (after frontmatter/comment stripping)
@@ -398,6 +412,7 @@ function parseFrontmatter(
398
412
  avoidWhen,
399
413
  category,
400
414
  alwaysCandidate,
415
+ platforms,
401
416
  inlineCommandExpansions,
402
417
  };
403
418
  }
@@ -558,6 +573,7 @@ function readSkillFromDirectory(
558
573
  avoidWhen: parsed.avoidWhen,
559
574
  category: parsed.category,
560
575
  alwaysCandidate: parsed.alwaysCandidate,
576
+ platforms: parsed.platforms,
561
577
  inlineCommandExpansions: parsed.inlineCommandExpansions,
562
578
  };
563
579
  } catch (err) {
@@ -614,6 +630,7 @@ function readBundledSkillFromDirectory(
614
630
  avoidWhen: parsed.avoidWhen,
615
631
  category: parsed.category,
616
632
  alwaysCandidate: parsed.alwaysCandidate,
633
+ platforms: parsed.platforms,
617
634
  inlineCommandExpansions: parsed.inlineCommandExpansions,
618
635
  };
619
636
  } catch (err) {
@@ -682,6 +699,7 @@ function loadBundledSkills(): SkillSummary[] {
682
699
  avoidWhen: skill.avoidWhen,
683
700
  category: skill.category,
684
701
  alwaysCandidate: skill.alwaysCandidate,
702
+ platforms: skill.platforms,
685
703
  inlineCommandExpansions: skill.inlineCommandExpansions,
686
704
  });
687
705
  }
@@ -972,6 +990,7 @@ function skillSummaryFromDefinition(
972
990
  avoidWhen: skill.avoidWhen,
973
991
  category: skill.category,
974
992
  alwaysCandidate: skill.alwaysCandidate,
993
+ platforms: skill.platforms,
975
994
  inlineCommandExpansions: skill.inlineCommandExpansions,
976
995
  };
977
996
  }
@@ -1035,6 +1054,7 @@ export function loadSkillCatalog(
1035
1054
  avoidWhen: parsed.avoidWhen,
1036
1055
  category: parsed.category,
1037
1056
  alwaysCandidate: parsed.alwaysCandidate,
1057
+ platforms: parsed.platforms,
1038
1058
  inlineCommandExpansions: parsed.inlineCommandExpansions,
1039
1059
  });
1040
1060
  } catch (err) {
@@ -1182,6 +1202,7 @@ export function loadSkillCatalog(
1182
1202
  avoidWhen: parsed.avoidWhen,
1183
1203
  category: parsed.category,
1184
1204
  alwaysCandidate: parsed.alwaysCandidate,
1205
+ platforms: parsed.platforms,
1185
1206
  inlineCommandExpansions: parsed.inlineCommandExpansions,
1186
1207
  };
1187
1208
 
@@ -1,3 +1,4 @@
1
+ import { invalidateAllContactThresholdCaches } from "../permissions/gateway-threshold-reader.js";
1
2
  import { broadcastMessage } from "../runtime/assistant-event-hub.js";
2
3
  import { invalidateGuardianContactCache } from "./guardian-contact-reader.js";
3
4
  import { invalidateGuardianDeliveryCache } from "./guardian-delivery-reader.js";
@@ -7,11 +8,13 @@ import { invalidateGuardianDeliveryCache } from "./guardian-delivery-reader.js";
7
8
  * a successful contact write:
8
9
  *
9
10
  * - broadcasts `contacts_changed` so connected clients refresh their view, and
10
- * - drops the guardian-contact and guardian-delivery caches so the next read
11
- * refetches from the gateway instead of serving a stale set.
11
+ * - drops the guardian-contact, guardian-delivery, and contact-threshold
12
+ * caches so the next read refetches from the gateway instead of serving
13
+ * a stale set.
12
14
  */
13
15
  export function notifyContactsChanged(): void {
14
16
  broadcastMessage({ type: "contacts_changed" });
15
17
  invalidateGuardianContactCache();
16
18
  invalidateGuardianDeliveryCache();
19
+ invalidateAllContactThresholdCaches();
17
20
  }
@@ -75,6 +75,40 @@ const COMPACTION_CALL_SITE: LLMCallSite = "mainAgent";
75
75
  */
76
76
  const COMPACTION_LOG_CALL_SITE: LLMCallSite = "compactionAgent";
77
77
 
78
+ /**
79
+ * What actually served a compaction call, for attribution on both the request
80
+ * log and the usage row. A wrapper may reroute the request away from the
81
+ * caller's resolution (`RetryProvider`'s fallback-profile escalation), in
82
+ * which case the response carries the provider and profile that answered.
83
+ *
84
+ * Single source of truth on purpose: the request log and the usage row must
85
+ * never disagree about which provider ran, and every result-construction site
86
+ * below spreads {@link servedAttribution} rather than re-deriving the pair.
87
+ */
88
+ function servedProvider(
89
+ response: ProviderResponse,
90
+ provider: Provider,
91
+ ): string {
92
+ return response.actualProvider ?? provider.name;
93
+ }
94
+
95
+ function servedAttribution(
96
+ response: ProviderResponse,
97
+ provider: Provider,
98
+ ): Pick<
99
+ CompactionRunResult,
100
+ "summaryActualProvider" | "summaryActualInferenceProfile"
101
+ > {
102
+ return {
103
+ summaryActualProvider: servedProvider(response, provider),
104
+ // Only present on a reroute. Absent leaves `summaryOverrideProfile` in
105
+ // charge, which is what the caller resolved and what actually ran.
106
+ ...(response.actualInferenceProfile !== undefined
107
+ ? { summaryActualInferenceProfile: response.actualInferenceProfile }
108
+ : {}),
109
+ };
110
+ }
111
+
78
112
  /**
79
113
  * Best-effort: persist a successful compaction LLM call into
80
114
  * `llm_request_logs` with `call_site = "compactionAgent"`. The compactor
@@ -100,7 +134,7 @@ function recordCompactionRequestLog(
100
134
  JSON.stringify(response.rawRequest),
101
135
  JSON.stringify(response.rawResponse),
102
136
  undefined,
103
- response.actualProvider ?? provider.name,
137
+ servedProvider(response, provider),
104
138
  COMPACTION_LOG_CALL_SITE,
105
139
  );
106
140
  } catch (err) {
@@ -321,6 +355,22 @@ export interface CompactionRunResult {
321
355
  summaryModel: string;
322
356
  summaryCallSite?: LLMCallSite;
323
357
  summaryOverrideProfile?: string | null;
358
+ /**
359
+ * Provider that actually served the summary call: the response's
360
+ * `actualProvider` when a wrapper rerouted the request, otherwise the
361
+ * configured provider's name. Set on every path that made a call, so the
362
+ * usage row's `provider` follows a fallback the way its `model` already
363
+ * does. Absent only where no call happened (see {@link emptyResult}), where
364
+ * the caller's own provider stands.
365
+ */
366
+ summaryActualProvider?: string;
367
+ /**
368
+ * Inference profile that actually governed the summary call, set only when
369
+ * a wrapper rerouted the request away from `summaryOverrideProfile`
370
+ * (`RetryProvider`'s fallback-profile escalation). Absent on the normal
371
+ * path, where `summaryOverrideProfile` is already correct.
372
+ */
373
+ summaryActualInferenceProfile?: string;
324
374
  summaryCacheCreationInputTokens?: number;
325
375
  summaryCacheReadInputTokens?: number;
326
376
  summaryRawResponses?: unknown[];
@@ -1052,6 +1102,18 @@ export function buildSummaryMemoryText(
1052
1102
  // Orchestrator
1053
1103
  // ---------------------------------------------------------------------------
1054
1104
 
1105
+ /**
1106
+ * A result for every path that compacted nothing: the pre-call bail-outs
1107
+ * (disabled, below threshold, no messages, bad boundary) and the two
1108
+ * provider-error paths, which are spread over with `summaryFailed: true`.
1109
+ *
1110
+ * Deliberately carries no `summaryActualProvider` /
1111
+ * `summaryActualInferenceProfile`: no call was served, so there is nothing to
1112
+ * attribute to, and leaving them absent keeps the caller's own provider and
1113
+ * profile in charge. Nothing is billed either way, since the zero token
1114
+ * counts here make `recordUsage` return before it writes a row. The
1115
+ * call-bearing paths below spread {@link servedAttribution} on top.
1116
+ */
1055
1117
  function emptyResult(
1056
1118
  args: CompactionRunArgs,
1057
1119
  thresholdTokens: number,
@@ -1340,6 +1402,7 @@ export async function runAssistantDrivenCompaction(
1340
1402
  summaryInputTokens: response.usage.inputTokens,
1341
1403
  summaryOutputTokens: response.usage.outputTokens,
1342
1404
  summaryModel: response.model,
1405
+ ...servedAttribution(response, args.provider),
1343
1406
  summaryCacheCreationInputTokens:
1344
1407
  response.usage.cacheCreationInputTokens ?? 0,
1345
1408
  summaryCacheReadInputTokens: response.usage.cacheReadInputTokens ?? 0,
@@ -1378,6 +1441,7 @@ export async function runAssistantDrivenCompaction(
1378
1441
  summaryInputTokens: response.usage.inputTokens,
1379
1442
  summaryOutputTokens: response.usage.outputTokens,
1380
1443
  summaryModel: response.model,
1444
+ ...servedAttribution(response, args.provider),
1381
1445
  summaryCacheCreationInputTokens:
1382
1446
  response.usage.cacheCreationInputTokens ?? 0,
1383
1447
  summaryCacheReadInputTokens: response.usage.cacheReadInputTokens ?? 0,
@@ -1548,6 +1612,7 @@ export async function runAssistantDrivenCompaction(
1548
1612
  summaryInputTokens: response.usage.inputTokens,
1549
1613
  summaryOutputTokens: response.usage.outputTokens,
1550
1614
  summaryModel: response.model,
1615
+ ...servedAttribution(response, args.provider),
1551
1616
  summaryCacheCreationInputTokens:
1552
1617
  response.usage.cacheCreationInputTokens ?? 0,
1553
1618
  summaryCacheReadInputTokens: response.usage.cacheReadInputTokens ?? 0,
@@ -1623,6 +1688,7 @@ export async function runAssistantDrivenCompaction(
1623
1688
  summaryModel: response.model,
1624
1689
  summaryCallSite: COMPACTION_CALL_SITE,
1625
1690
  summaryOverrideProfile: args.overrideProfile ?? null,
1691
+ ...servedAttribution(response, args.provider),
1626
1692
  summaryCacheCreationInputTokens:
1627
1693
  response.usage.cacheCreationInputTokens ?? 0,
1628
1694
  summaryCacheReadInputTokens: response.usage.cacheReadInputTokens ?? 0,
@@ -1808,6 +1874,12 @@ export async function runEmergencyCompaction(
1808
1874
  summaryInputTokens: response.usage.inputTokens,
1809
1875
  summaryOutputTokens: response.usage.outputTokens,
1810
1876
  summaryModel: response.model,
1877
+ // This path bills real tokens, so its provider must follow a reroute
1878
+ // like every other call-bearing path. It is the one such path that sets
1879
+ // no `summaryCallSite`/`summaryOverrideProfile`, which leaves its
1880
+ // profile attribution null; that gap predates this change and is not
1881
+ // widened by it.
1882
+ ...servedAttribution(response, args.provider),
1811
1883
  };
1812
1884
  }
1813
1885
 
@@ -1855,6 +1927,7 @@ export async function runEmergencyCompaction(
1855
1927
  summaryModel: response.model,
1856
1928
  summaryCallSite: COMPACTION_CALL_SITE,
1857
1929
  summaryOverrideProfile: args.overrideProfile ?? null,
1930
+ ...servedAttribution(response, args.provider),
1858
1931
  summaryCacheCreationInputTokens:
1859
1932
  response.usage.cacheCreationInputTokens ?? 0,
1860
1933
  summaryCacheReadInputTokens: response.usage.cacheReadInputTokens ?? 0,
@@ -36,14 +36,28 @@ import type { ChannelCapabilities } from "../conversation-runtime-assembly.js";
36
36
 
37
37
  // Control how many capable clients the hub reports per capability.
38
38
  const mockClientCountByCapability = new Map<string, number>();
39
+ const mockClientsByCapability = new Map<
40
+ string,
41
+ Array<{
42
+ clientId: string;
43
+ capabilities: string[];
44
+ interfaceId: string;
45
+ actorPrincipalId?: string;
46
+ }>
47
+ >();
39
48
 
40
49
  mock.module("../../runtime/assistant-event-hub.js", () => ({
41
50
  assistantEventHub: {
42
51
  listClientsByCapability: (cap: string) => {
52
+ const clients = mockClientsByCapability.get(cap);
53
+ if (clients) {
54
+ return clients;
55
+ }
43
56
  const count = mockClientCountByCapability.get(cap) ?? 0;
44
57
  return Array.from({ length: count }, (_, i) => ({
45
58
  clientId: `mock-${cap}-client-${i}`,
46
59
  capabilities: [cap],
60
+ interfaceId: "macos",
47
61
  }));
48
62
  },
49
63
  },
@@ -58,6 +72,9 @@ const {
58
72
  HOST_TOOL_TO_CAPABILITY,
59
73
  isToolActiveForContext,
60
74
  } = await import("../conversation-tool-setup.js");
75
+ const { registerSkillTools, unregisterSkillTools } =
76
+ await import("../../tools/registry.js");
77
+ const { finalizeTool } = await import("../../tools/tool-defaults.js");
61
78
  type SkillProjectionCache =
62
79
  import("../conversation-skill-tools.js").SkillProjectionCache;
63
80
 
@@ -76,9 +93,129 @@ function makeCtx(
76
93
 
77
94
  beforeEach(() => {
78
95
  mockClientCountByCapability.clear();
96
+ mockClientsByCapability.clear();
79
97
  });
80
98
 
81
- describe("isToolActiveForContext Slack task_progress UI exception", () => {
99
+ describe("isToolActiveForContext - client OS eligibility", () => {
100
+ test("hides a macOS-only skill tool from Windows turns", () => {
101
+ const skillId = "client-os-test-skill";
102
+ registerSkillTools(skillId, [
103
+ finalizeTool({
104
+ name: "client_os_test_tool",
105
+ supportedClientOs: ["macos"],
106
+ }),
107
+ ]);
108
+
109
+ try {
110
+ expect(
111
+ isToolActiveForContext(
112
+ "client_os_test_tool",
113
+ makeCtx({
114
+ clientOs: "windows",
115
+ currentTurnClientOs: "windows",
116
+ }),
117
+ ),
118
+ ).toBe(false);
119
+ expect(
120
+ isToolActiveForContext(
121
+ "client_os_test_tool",
122
+ makeCtx({ clientOs: "macos", currentTurnClientOs: "macos" }),
123
+ ),
124
+ ).toBe(true);
125
+ } finally {
126
+ unregisterSkillTools(skillId);
127
+ }
128
+ });
129
+
130
+ test("uses same-user target desktops for cross-client tool eligibility", () => {
131
+ const skillId = "cross-client-os-test-skill";
132
+ registerSkillTools(skillId, [
133
+ finalizeTool({
134
+ name: "computer_use_platform_test",
135
+ supportedClientOs: ["macos"],
136
+ }),
137
+ ]);
138
+ const ctx = makeCtx({
139
+ clientOs: "web",
140
+ currentTurnClientOs: "web",
141
+ transportInterface: "web",
142
+ getTurnActorPrincipalId: () => "actor-1",
143
+ });
144
+
145
+ try {
146
+ mockClientsByCapability.set("host_cu", [
147
+ {
148
+ clientId: "mac-client",
149
+ capabilities: ["host_cu"],
150
+ interfaceId: "macos",
151
+ actorPrincipalId: "actor-1",
152
+ },
153
+ ]);
154
+ expect(isToolActiveForContext("computer_use_platform_test", ctx)).toBe(
155
+ true,
156
+ );
157
+
158
+ mockClientsByCapability.set("host_cu", [
159
+ {
160
+ clientId: "windows-client",
161
+ capabilities: ["host_cu"],
162
+ interfaceId: "windows",
163
+ actorPrincipalId: "actor-1",
164
+ },
165
+ ]);
166
+ expect(isToolActiveForContext("computer_use_platform_test", ctx)).toBe(
167
+ false,
168
+ );
169
+ } finally {
170
+ unregisterSkillTools(skillId);
171
+ }
172
+ });
173
+
174
+ test("uses pinned client OS without falling through to live context", () => {
175
+ const skillId = "pinned-client-os-test-skill";
176
+ registerSkillTools(skillId, [
177
+ finalizeTool({
178
+ name: "client_os_pinned_test_tool",
179
+ supportedClientOs: ["macos"],
180
+ }),
181
+ ]);
182
+
183
+ try {
184
+ expect(
185
+ isToolActiveForContext(
186
+ "client_os_pinned_test_tool",
187
+ makeCtx({
188
+ clientOs: "windows",
189
+ currentTurnClientOs: "windows",
190
+ toolContextPin: {
191
+ hasNoClient: false,
192
+ transportInterface: "macos",
193
+ clientOs: "macos",
194
+ },
195
+ }),
196
+ ),
197
+ ).toBe(true);
198
+ expect(
199
+ isToolActiveForContext(
200
+ "client_os_pinned_test_tool",
201
+ makeCtx({
202
+ clientOs: "macos",
203
+ currentTurnClientOs: "macos",
204
+ toolContextPin: {
205
+ hasNoClient: false,
206
+ transportInterface: "windows",
207
+ clientOs: "windows",
208
+ },
209
+ }),
210
+ ),
211
+ ).toBe(false);
212
+ } finally {
213
+ unregisterSkillTools(skillId);
214
+ }
215
+ });
216
+ });
217
+
218
+ describe("isToolActiveForContext - Slack task_progress UI exception", () => {
82
219
  test("ui_show and ui_update are active for Slack task_progress turns", () => {
83
220
  const ctx = makeCtx({
84
221
  hasNoClient: false,
@@ -15,6 +15,8 @@ import {
15
15
  } from "node:fs";
16
16
  import { basename, join } from "node:path";
17
17
 
18
+ import { AVATAR_IMAGE_FILENAME } from "@vellumai/avatar-manifest";
19
+
18
20
  import { getConfig, invalidateConfigCache } from "../config/loader.js";
19
21
  import type { MemoryCleanupConfig } from "../config/schemas/memory-lifecycle.js";
20
22
  import { resetCleanupScheduleThrottle } from "../persistence/cleanup-schedule-state.js";
@@ -37,7 +39,6 @@ import { WORKSPACE_THEME_RELATIVE_PATH } from "../theme/workspace-theme.js";
37
39
  import { DebouncerMap } from "../util/debounce.js";
38
40
  import { getLogger } from "../util/logger.js";
39
41
  import {
40
- AVATAR_IMAGE_FILENAME,
41
42
  getAvatarDir,
42
43
  getSignalsDir,
43
44
  getSoundsDir,
@@ -211,6 +211,15 @@ export interface EventHandlerState {
211
211
  firstAssistantText: string;
212
212
  /** Most recent resolved provider for the current exchange's usage accounting. */
213
213
  exchangeProviderName: string | undefined;
214
+ /**
215
+ * Inference profile the most recent LLM call actually ran under, set only
216
+ * when a wrapper rerouted that call (`RetryProvider`'s fallback-profile
217
+ * escalation). Overwritten by every call, exactly like
218
+ * `exchangeProviderName` and `model`, so the ledger row's provider, model,
219
+ * and profile all describe the same call. `undefined` means the last call
220
+ * was not rerouted and the conversation's own profile resolution stands.
221
+ */
222
+ exchangeInferenceProfile: string | undefined;
214
223
  exchangeInputTokens: number;
215
224
  exchangeCacheCreationInputTokens: number;
216
225
  exchangeCacheReadInputTokens: number;
@@ -236,6 +245,14 @@ export interface EventHandlerState {
236
245
  * the loop persists the failure as an assistant message.
237
246
  */
238
247
  providerErrorCategory: string | null;
248
+ /**
249
+ * Connection and profile attribution of the most recent provider error
250
+ * (`classifyConversationError(...).connectionName` / `.profileName`).
251
+ * Carried into the turn's outcome stamp so failure consumers scope
252
+ * identity by the route the call actually resolved.
253
+ */
254
+ providerErrorConnection: string | null;
255
+ providerErrorProfile: string | null;
239
256
  persistProviderErrorAsAssistantMessage: boolean;
240
257
  lastAssistantMessageId: string | undefined;
241
258
  /**
@@ -578,6 +595,7 @@ export function createEventHandlerState(): EventHandlerState {
578
595
  pendingDirectiveDisplayBuffer: "",
579
596
  firstAssistantText: "",
580
597
  exchangeProviderName: undefined,
598
+ exchangeInferenceProfile: undefined,
581
599
  exchangeInputTokens: 0,
582
600
  exchangeCacheCreationInputTokens: 0,
583
601
  exchangeCacheReadInputTokens: 0,
@@ -589,6 +607,8 @@ export function createEventHandlerState(): EventHandlerState {
589
607
  providerErrorUserMessage: null,
590
608
  providerErrorCode: null,
591
609
  providerErrorCategory: null,
610
+ providerErrorConnection: null,
611
+ providerErrorProfile: null,
592
612
  persistProviderErrorAsAssistantMessage: false,
593
613
  lastAssistantMessageId: undefined,
594
614
  assistantRowAwaitingFinalization: false,
@@ -2722,6 +2742,8 @@ function handleError(
2722
2742
  state.providerErrorUserMessage = classified.userMessage;
2723
2743
  state.providerErrorCode = classified.code;
2724
2744
  state.providerErrorCategory = classified.errorCategory;
2745
+ state.providerErrorConnection = classified.connectionName ?? null;
2746
+ state.providerErrorProfile = classified.profileName ?? null;
2725
2747
  state.persistProviderErrorAsAssistantMessage =
2726
2748
  shouldPersistProviderErrorAsAssistantMessage(classified);
2727
2749
  }
@@ -3071,6 +3093,11 @@ function handleUsage(
3071
3093
  ): void {
3072
3094
  const providerName = event.actualProvider ?? deps.ctx.provider.name;
3073
3095
  state.exchangeProviderName = providerName;
3096
+ // Assigned unconditionally, not merged: a later non-rerouted call clearing
3097
+ // this back to `undefined` is correct, because provider and model are being
3098
+ // overwritten from that same call. Keeping a stale profile from an earlier
3099
+ // fallback would reintroduce the very contradiction this field prevents.
3100
+ state.exchangeInferenceProfile = event.actualInferenceProfile;
3074
3101
  state.exchangeLlmCallCount += 1;
3075
3102
  state.exchangeInputTokens += event.inputTokens;
3076
3103
  state.lastCallInputTokens = event.inputTokens;