@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
@@ -1,4 +1,3 @@
1
- import type { ChildProcess } from "node:child_process";
2
1
  import { spawn } from "node:child_process";
3
2
 
4
3
  import { z } from "zod";
@@ -10,6 +9,10 @@ import { wakeAgentForOpportunity } from "../../runtime/agent-wake.js";
10
9
  import { broadcastMessage } from "../../runtime/assistant-event-hub.js";
11
10
  import { conversationRevealNonce } from "../../runtime/reveal-nonce.js";
12
11
  import { redactSecrets } from "../../security/secret-scanner.js";
12
+ import {
13
+ buildShellInvocation,
14
+ terminateProcessTree,
15
+ } from "../../util/host-process.js";
13
16
  import { getLogger } from "../../util/logger.js";
14
17
  import { getDataDir } from "../../util/platform.js";
15
18
  import type { CompletedBackgroundTool } from "../background-tool-registry.js";
@@ -296,7 +299,7 @@ export const shellTool = {
296
299
  Object.assign(env, proxyEnv);
297
300
  }
298
301
 
299
- const wrapped = { command: "bash", args: ["-c", "--", command] };
302
+ const wrapped = buildShellInvocation(command);
300
303
 
301
304
  // -----------------------------------------------------------------------
302
305
  // Background mode: spawn and return immediately. The process output is
@@ -644,12 +647,11 @@ export const shellTool = {
644
647
 
645
648
  /**
646
649
  * Structured teardown log. Pairs with the `"Executing shell command"`
647
- * start log: every shell invocation now produces a start/exit pair so
650
+ * start log: every shell invocation produces a start/exit pair so
648
651
  * orphan-leak post-mortems can correlate command + exitCode + signal +
649
652
  * timedOut + duration without spelunking through prose hints. The
650
- * `signal === "SIGKILL"` + `timedOut === true` combination is the
651
- * fingerprint left by the timeout watcher SIGKILLing the process group
652
- * — i.e. the moment that creates the orphans.
653
+ * `signal === "SIGKILL"` + `timedOut === true` combination identifies a
654
+ * timeout-driven process termination.
653
655
  */
654
656
  function logShellExit(args: {
655
657
  toolName: string;
@@ -681,19 +683,10 @@ function logShellExit(args: {
681
683
  }
682
684
 
683
685
  /**
684
- * Kill the entire process tree of a child process. Tries the process group
685
- * first (negative PID), then falls back to killing the direct child if the
686
- * PID is unavailable or the group kill fails.
687
- *
688
- * Emits a structured `warn` log on every invocation: this is the
689
- * ground-truth event that creates orphaned subprocesses (the SIGKILL hits
690
- * the entire group, so the immediate bash child has no chance to reap its
691
- * grandchildren; under bun-as-PID-1 they accumulate as `<defunct>`).
692
- * `reason` lets the next zombie report point at a specific call site (the
693
- * timeout watcher in the foreground/background branches, or an abort).
686
+ * Terminate the process tree and record the call site for timeout diagnostics.
694
687
  */
695
688
  function buildKillTree(
696
- child: ChildProcess,
689
+ child: ReturnType<typeof spawn>,
697
690
  context: {
698
691
  toolName: string;
699
692
  conversationId: string;
@@ -715,20 +708,8 @@ function buildKillTree(
715
708
  groupPid,
716
709
  invocationId: context.invocationId,
717
710
  },
718
- "Shell process group SIGKILL'd — orphans expected to reparent to PID 1",
711
+ "Shell process group SIGKILL'd",
719
712
  );
720
- if (groupPid != null) {
721
- try {
722
- process.kill(-groupPid, "SIGKILL");
723
- return;
724
- } catch {
725
- // Process group may have already exited — fall through.
726
- }
727
- }
728
- try {
729
- child.kill("SIGKILL");
730
- } catch {
731
- // Child may have already exited.
732
- }
713
+ terminateProcessTree(child);
733
714
  };
734
715
  }
@@ -18,6 +18,7 @@ import {
18
18
  } from "../permissions/checker.js";
19
19
  import {
20
20
  channelNoCellDefault,
21
+ getContactAutoApproveThreshold,
21
22
  resolveChannelPermissionCell,
22
23
  } from "../permissions/gateway-threshold-reader.js";
23
24
  import {
@@ -525,6 +526,11 @@ function reachesPrivateNetwork(
525
526
  * Each stays on the capability floor, so a channel actor escalates to the
526
527
  * guardian for them at every level. None of this touches the guardian's own
527
528
  * lane — it decides what a *cell* may delegate, not how risk is classified.
529
+ *
530
+ * A contact-level ceiling is a different grant: the owner named this person,
531
+ * not a room. That ceiling may lift sandbox `bash`. It still cannot lift
532
+ * host reach, control-plane writes, unvetted tools, or private-network
533
+ * fetches; those stay on the floor regardless of the contact's threshold.
528
534
  */
529
535
  function isChannelLiftable(
530
536
  reach: SensitiveToolReach,
@@ -558,16 +564,54 @@ function isChannelLiftable(
558
564
  }
559
565
 
560
566
  /**
561
- * Threshold of the approval-matrix cell governing this invocation, read only
562
- * when it can change the outcome: a channel-liftable invocation whose actor
563
- * sits on the escalate floor. Guardians, non-sensitive tools, host reach,
564
- * everything {@link isChannelLiftable} excludes, and identity-less actors
565
- * return early, so the gateway lookup never lands on the paths where it could
567
+ * Whether a contact-level ceiling may lift the floor for this invocation.
568
+ *
569
+ * Same sandbox-only rule as {@link isChannelLiftable}, except sandbox `bash`
570
+ * is included: the owner set a per-person ceiling, and that ceiling is what
571
+ * authorizes code execution in the workspace. Host reach, control-plane
572
+ * writes, unvetted tools, and private-network fetches stay unliftable.
573
+ */
574
+ function isContactCeilingLiftable(
575
+ reach: SensitiveToolReach,
576
+ toolName: string,
577
+ input: Record<string, unknown>,
578
+ workingDir: string | undefined,
579
+ ): boolean {
580
+ if (reach !== "sandbox") {
581
+ return false;
582
+ }
583
+ if (
584
+ isWorkspaceWriteTool(toolName) &&
585
+ (!workingDir || isControlPlaneWorkspaceWrite(toolName, input, workingDir))
586
+ ) {
587
+ return false;
588
+ }
589
+ if (isUnvettedExtensionTool(toolName)) {
590
+ return false;
591
+ }
592
+ if (reachesPrivateNetwork(toolName, input)) {
593
+ return false;
594
+ }
595
+ return true;
596
+ }
597
+
598
+ /**
599
+ * Threshold governing this invocation's floor lift, read only when it can
600
+ * change the outcome: an escalate-floor actor whose invocation a cell or a
601
+ * contact ceiling may lift. Guardians, non-sensitive tools, host reach,
602
+ * everything those lift paths exclude, and identity-less actors return
603
+ * early, so the gateway lookup never lands on the paths where it could
566
604
  * only add latency — grant consumption for an already-approved call, and voice
567
605
  * abort handling.
568
606
  *
569
- * The permission checker reads this same cell later in the turn, within the
570
- * reader's cache window, so the lift costs at most one lookup per turn.
607
+ * A non-null contact ceiling wins over the matrix cell. It is the owner's
608
+ * override for that person and is not collapsed to the channel's two
609
+ * levels, so `medium` / `high` can lift sandbox `bash`. Cells still cannot
610
+ * lift bash.
611
+ *
612
+ * The permission checker reads this same contact ceiling / cell later in
613
+ * the turn, within the reader's cache window, so the lift costs at most
614
+ * one lookup per turn.
571
615
  *
572
616
  * Returns `undefined` when the turn has no channel coordinates or a lookup
573
617
  * fails — nothing lifts the floor then. A successful walk that finds no cell
@@ -583,10 +627,21 @@ async function resolveApprovalCellThreshold(
583
627
  sensitiveToolApproval: SensitiveToolApproval,
584
628
  context: ToolContext,
585
629
  ): Promise<ApprovalCellThreshold | undefined> {
630
+ if (sensitiveToolApproval !== "escalate-and-wait") {
631
+ return undefined;
632
+ }
633
+
634
+ const contactCeiling = await getContactAutoApproveThreshold(
635
+ context.requesterContactId,
636
+ );
586
637
  if (
587
- sensitiveToolApproval !== "escalate-and-wait" ||
588
- !isChannelLiftable(reach, toolName, input, context.workingDir)
638
+ contactCeiling !== null &&
639
+ isContactCeilingLiftable(reach, toolName, input, context.workingDir)
589
640
  ) {
641
+ return contactCeiling;
642
+ }
643
+
644
+ if (!isChannelLiftable(reach, toolName, input, context.workingDir)) {
590
645
  return undefined;
591
646
  }
592
647
  const query = buildChannelPermissionCellQuery(
@@ -708,7 +763,6 @@ export class ToolApprovalHandler {
708
763
  reason,
709
764
  riskLevel,
710
765
  durationMs: Date.now() - startTime,
711
- wasPrompted: false,
712
766
  });
713
767
  }
714
768
 
@@ -348,6 +348,8 @@ export interface ToolContext {
348
348
  * `InterfaceId` union.
349
349
  */
350
350
  transportInterface?: string;
351
+ /** Operating system reported by the client for the current turn. */
352
+ clientOs?: "web" | "ios" | "macos" | "windows" | "android";
351
353
  /**
352
354
  * True when the host browser proxy's sender was overridden by an
353
355
  * extension connection (WebSocket browser-relay).
@@ -374,6 +376,14 @@ export interface Tool {
374
376
  /** Declared execution target from the skill manifest. Used by resolveExecutionTarget
375
377
  * to accurately label lifecycle events for skill-provided tools. */
376
378
  executionTarget?: ExecutionTarget;
379
+ /** Client operating systems that may expose this tool. Unset means all. */
380
+ supportedClientOs?: readonly (
381
+ | "web"
382
+ | "ios"
383
+ | "macos"
384
+ | "windows"
385
+ | "android"
386
+ )[];
377
387
  getDefinition(): ToolDefinition;
378
388
  execute(
379
389
  input: Record<string, unknown>,
@@ -6,7 +6,11 @@ import type {
6
6
  import { z } from "zod";
7
7
 
8
8
  import type { AnsweredQuestion } from "../api/events/question-answered.js";
9
- import type { InterfaceId } from "../channels/types.js";
9
+ import {
10
+ CLIENT_OS_VALUES,
11
+ type ClientOs,
12
+ type InterfaceId,
13
+ } from "../channels/types.js";
10
14
  import type { LLMCallSite } from "../config/schemas/llm.js";
11
15
  import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
12
16
  import type { SecretPromptResult } from "../permissions/secret-prompt-types.js";
@@ -367,6 +371,12 @@ export interface ToolContext {
367
371
  * @legacy
368
372
  */
369
373
  requesterIdentifier?: string;
374
+ /**
375
+ * Contact ID of the requester's member record. Approval resolution looks
376
+ * up this contact's auto-approve ceiling from the gateway at use time.
377
+ * @legacy
378
+ */
379
+ requesterContactId?: string;
370
380
  /**
371
381
  * Preferred display name for the requester.
372
382
  * @legacy
@@ -406,6 +416,8 @@ export interface ToolContext {
406
416
  * @legacy
407
417
  */
408
418
  transportInterface?: InterfaceId;
419
+ /** Operating system reported by the client for the current turn. */
420
+ clientOs?: ClientOs;
409
421
  /**
410
422
  * The per-turn inference-profile override the agent loop is currently
411
423
  * running under, propagated through tool context so subagent-spawn tools
@@ -504,6 +516,8 @@ export const ToolDefinitionSchema = z.object({
504
516
  category: z.string().min(1).optional(),
505
517
  /** Where the tool runs — sandbox (assistant container) or host (guardian device via proxy). Resolved by `resolveExecutionTarget` if omitted. */
506
518
  executionTarget: z.enum(["sandbox", "host"]).optional(),
519
+ /** Client operating systems that may expose this tool. Unset means all. */
520
+ supportedClientOs: z.array(z.enum(CLIENT_OS_VALUES)).optional(),
507
521
  /**
508
522
  * Implementation invoked when the model calls the tool. Optional
509
523
  * because some `ToolDefinition` instances are schema-only (e.g.
@@ -551,8 +565,10 @@ export type ToolDefinition = z.infer<typeof ToolDefinitionSchema>;
551
565
  * it, and the agent loop reads it as `?.exclusive === true`, so forcing every
552
566
  * hand-built `Tool` (MCP/meet/test fixtures) to carry it would be noise.
553
567
  */
554
- export type Tool = Required<Omit<ToolDefinition, "exclusive">> &
555
- Pick<ToolDefinition, "exclusive">;
568
+ export type Tool = Required<
569
+ Omit<ToolDefinition, "exclusive" | "supportedClientOs">
570
+ > &
571
+ Pick<ToolDefinition, "exclusive" | "supportedClientOs">;
556
572
 
557
573
  /**
558
574
  * The kind of entity that owns a tool. `"default"` is the built-in tool set
@@ -202,14 +202,15 @@ export const SURFACE_SHAPE_DOCS: Record<string, SurfaceShapeDoc> = {
202
202
  "{ html, height? } — one self-contained HTML/SVG fragment (no DOCTYPE/html/head/body, no external resources); every colour comes from the injected design-token CSS variables, so hex/rgb literals and invented `var()` names are rejected. Load the `visualize` skill with `skill_load` for the full contract and the token vocabulary",
203
203
  },
204
204
  channel_setup: {
205
- purpose: "open the Slack/Telegram/Phone setup panel",
206
- shape: '{ channel: "slack" | "telegram" | "phone" }',
205
+ purpose: "open the Slack/Telegram/Discord/Phone setup panel",
206
+ shape: '{ channel: "slack" | "telegram" | "discord" | "phone" }',
207
207
  missingContent: (data) =>
208
208
  data.channel === "slack" ||
209
209
  data.channel === "telegram" ||
210
+ data.channel === "discord" ||
210
211
  data.channel === "phone"
211
212
  ? null
212
- : '`data.channel` must be one of "slack", "telegram", "phone"',
213
+ : '`data.channel` must be one of "slack", "telegram", "discord", "phone"',
213
214
  },
214
215
  voice_picker: {
215
216
  // The steering lives in `purpose` because that is the only part of a cold
@@ -15,7 +15,7 @@
15
15
 
16
16
  import { z } from "zod";
17
17
 
18
- import { getEffectiveProfilesForProvider } from "../../config/default-profile-catalog.js";
18
+ import { getUserSelectableProfilesForProvider } from "../../config/default-profile-catalog.js";
19
19
  import { loadConfig } from "../../config/loader.js";
20
20
  import { callerOwnsWorkflowRun } from "../../workflows/capabilities.js";
21
21
  import type { WorkflowRun } from "../../workflows/journal-store.js";
@@ -192,7 +192,7 @@ export async function executeManageWorkflows(
192
192
  // workspace-wide active profile. Read-only — leaves use this to pick a
193
193
  // valid `profile` for `run_workflow` (an unknown profile throws).
194
194
  const { llm } = loadConfig();
195
- const profiles = getEffectiveProfilesForProvider(
195
+ const profiles = getUserSelectableProfilesForProvider(
196
196
  llm?.profiles,
197
197
  llm?.defaultProvider ?? null,
198
198
  );
@@ -338,7 +338,8 @@ describe("manage_workflows", () => {
338
338
  );
339
339
  expect(res.isError).toBe(false);
340
340
  const parsed = JSON.parse(res.content);
341
- // The effective view always includes the code-catalog defaults.
341
+ // The user-selectable view includes the code-catalog defaults and omits
342
+ // managed backup routes.
342
343
  expect(parsed.profiles).toEqual([
343
344
  "balanced",
344
345
  "cost-optimized",
@@ -0,0 +1,61 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { isFileUnheld } from "../file-use.js";
4
+
5
+ describe("isFileUnheld", () => {
6
+ test("returns false when a process holds the file", async () => {
7
+ expect(
8
+ await isFileUnheld("lock", "linux", async () => ({ stdout: "123\n" })),
9
+ ).toBe(false);
10
+ });
11
+
12
+ test("uses an exclusive-open probe on Windows", async () => {
13
+ let invocation: { command: string; args: string[] } | undefined;
14
+ expect(
15
+ await isFileUnheld(
16
+ "C:\\Example Workspace\\.git\\index.lock",
17
+ "win32",
18
+ async (command, args) => {
19
+ invocation = { command, args };
20
+ return { stdout: "" };
21
+ },
22
+ ),
23
+ ).toBe(true);
24
+ expect(invocation?.command).toBe("powershell.exe");
25
+ expect(invocation?.args.at(-1)).toBe(
26
+ "C:\\Example Workspace\\.git\\index.lock",
27
+ );
28
+ });
29
+
30
+ test("returns false when Windows reports a sharing violation", async () => {
31
+ expect(
32
+ await isFileUnheld("lock", "win32", async () => {
33
+ throw Object.assign(new Error("held"), { code: 1 });
34
+ }),
35
+ ).toBe(false);
36
+ });
37
+
38
+ test("fails closed when the Windows probe errors", async () => {
39
+ expect(
40
+ await isFileUnheld("lock", "win32", async () => {
41
+ throw Object.assign(new Error("probe failed"), { code: 2 });
42
+ }),
43
+ ).toBe(false);
44
+ });
45
+
46
+ test("returns true when lsof reports no holder", async () => {
47
+ expect(
48
+ await isFileUnheld("lock", "linux", async () => {
49
+ throw Object.assign(new Error("no matches"), { code: 1 });
50
+ }),
51
+ ).toBe(true);
52
+ });
53
+
54
+ test("treats lsof errors as unheld to match prior behavior", async () => {
55
+ expect(
56
+ await isFileUnheld("lock", "linux", async () => {
57
+ throw Object.assign(new Error("missing"), { code: "ENOENT" });
58
+ }),
59
+ ).toBe(true);
60
+ });
61
+ });
@@ -0,0 +1,66 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ parsePsProcessTable,
5
+ parseWindowsProcessTable,
6
+ } from "../process-table.js";
7
+
8
+ describe("parsePsProcessTable", () => {
9
+ test("preserves command lines while parsing process relationships", () => {
10
+ expect(
11
+ parsePsProcessTable(
12
+ " 10 1 /usr/bin/bun run worker.ts arg\n 11 10 helper\n",
13
+ ),
14
+ ).toEqual([
15
+ { pid: 10, ppid: 1, command: "/usr/bin/bun run worker.ts arg" },
16
+ { pid: 11, ppid: 10, command: "helper" },
17
+ ]);
18
+ });
19
+ });
20
+
21
+ describe("parseWindowsProcessTable", () => {
22
+ test("parses PowerShell JSON and falls back to the image name", () => {
23
+ expect(
24
+ parseWindowsProcessTable(
25
+ JSON.stringify([
26
+ {
27
+ ProcessId: 100,
28
+ ParentProcessId: 4,
29
+ CommandLine: "C:\\Program Files\\Bun\\bun.exe worker.ts",
30
+ Name: "bun.exe",
31
+ WorkingSetSize: 4096,
32
+ HandleCount: 42,
33
+ },
34
+ {
35
+ ProcessId: 101,
36
+ ParentProcessId: 100,
37
+ CommandLine: null,
38
+ Name: "helper.exe",
39
+ },
40
+ ]),
41
+ ),
42
+ ).toEqual([
43
+ {
44
+ pid: 100,
45
+ ppid: 4,
46
+ command: "C:\\Program Files\\Bun\\bun.exe worker.ts",
47
+ rssBytes: 4096,
48
+ handleCount: 42,
49
+ },
50
+ { pid: 101, ppid: 100, command: "helper.exe" },
51
+ ]);
52
+ });
53
+
54
+ test("handles the single-object shape emitted for one process", () => {
55
+ expect(
56
+ parseWindowsProcessTable(
57
+ JSON.stringify({
58
+ ProcessId: 100,
59
+ ParentProcessId: 4,
60
+ CommandLine: "worker.exe",
61
+ Name: "worker.exe",
62
+ }),
63
+ ),
64
+ ).toEqual([{ pid: 100, ppid: 4, command: "worker.exe" }]);
65
+ });
66
+ });
@@ -9,6 +9,7 @@ import { describe, expect, test } from "bun:test";
9
9
 
10
10
  import {
11
11
  buildProcessTree,
12
+ buildSystemProcessTree,
12
13
  deriveName,
13
14
  deriveOrigin,
14
15
  type ProcInfo,
@@ -20,6 +21,14 @@ describe("deriveName", () => {
20
21
  expect(deriveName("qdrant")).toBe("qdrant");
21
22
  });
22
23
 
24
+ test("handles quoted Windows executable and script paths", () => {
25
+ expect(
26
+ deriveName(
27
+ '"C:\\Program Files\\Bun\\bun.exe" run "C:\\Example App\\jobs\\worker.ts"',
28
+ ),
29
+ ).toBe("jobs-worker");
30
+ });
31
+
23
32
  test("summarizes the script path as <parent>-<file> for interpreter invocations", () => {
24
33
  expect(deriveName("bun run /home/u/app/jobs/worker.ts")).toBe(
25
34
  "jobs-worker",
@@ -67,6 +76,12 @@ describe("deriveOrigin", () => {
67
76
  ).toBe("plugin:cognee");
68
77
  });
69
78
 
79
+ test("tags a plugin from a Windows command path", () => {
80
+ expect(
81
+ deriveOrigin("bun run C:\\Vellum\\workspace\\plugins\\cognee\\server.ts"),
82
+ ).toBe("plugin:cognee");
83
+ });
84
+
70
85
  test("tags a bundled default plugin as default-<name>", () => {
71
86
  expect(
72
87
  deriveOrigin("bun --smol run /app/src/plugins/defaults/memory/worker.ts"),
@@ -195,3 +210,34 @@ describe("buildProcessTree", () => {
195
210
  expect(tree.children[0].children).toEqual([]);
196
211
  });
197
212
  });
213
+
214
+ describe("buildSystemProcessTree", () => {
215
+ test("includes every process under a synthetic system root", () => {
216
+ const procs: ProcInfo[] = [
217
+ { pid: 4, ppid: 0, command: "System", origin: "workspace" },
218
+ { pid: 100, ppid: 4, command: "assistant.exe", origin: "workspace" },
219
+ { pid: 200, ppid: 100, command: "worker.exe", origin: "workspace" },
220
+ { pid: 300, ppid: 999, command: "orphan.exe", origin: "workspace" },
221
+ ];
222
+
223
+ const tree = buildSystemProcessTree(procs);
224
+ const allPids = (node: typeof tree): number[] => [
225
+ node.pid,
226
+ ...node.children.flatMap(allPids),
227
+ ];
228
+
229
+ expect(tree.name).toBe("system");
230
+ expect(allPids(tree)).toEqual([0, 4, 100, 200, 300]);
231
+ });
232
+
233
+ test("includes processes trapped in a parent cycle", () => {
234
+ const procs: ProcInfo[] = [
235
+ { pid: 10, ppid: 20, command: "a", origin: "workspace" },
236
+ { pid: 20, ppid: 10, command: "b", origin: "workspace" },
237
+ ];
238
+
239
+ const tree = buildSystemProcessTree(procs);
240
+ expect(tree.children.map((child) => child.pid)).toEqual([10]);
241
+ expect(tree.children[0].children.map((child) => child.pid)).toEqual([20]);
242
+ });
243
+ });
@@ -118,6 +118,7 @@ export type ProviderErrorReason =
118
118
  | "overloaded"
119
119
  | "context_overflow"
120
120
  | "vision_unsupported"
121
+ | "request_shape_unsupported"
121
122
  | "bad_request"
122
123
  | "server_error"
123
124
  | "network_error"
@@ -0,0 +1,91 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+
4
+ const execFileAsync = promisify(execFile);
5
+
6
+ const WINDOWS_EXCLUSIVE_OPEN_SCRIPT = String.raw`
7
+ Add-Type -TypeDefinition @'
8
+ using System;
9
+ using System.Runtime.InteropServices;
10
+ using Microsoft.Win32.SafeHandles;
11
+
12
+ public static class FileUseProbe {
13
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
14
+ public static extern SafeFileHandle CreateFile(
15
+ string fileName,
16
+ uint desiredAccess,
17
+ uint shareMode,
18
+ IntPtr securityAttributes,
19
+ uint creationDisposition,
20
+ uint flagsAndAttributes,
21
+ IntPtr templateFile);
22
+ }
23
+ '@
24
+
25
+ $handle = [FileUseProbe]::CreateFile($args[0], 0, 0, [IntPtr]::Zero, 3, 128, [IntPtr]::Zero)
26
+ if (-not $handle.IsInvalid) {
27
+ $handle.Dispose()
28
+ exit 0
29
+ }
30
+
31
+ $errorCode = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
32
+ if ($errorCode -eq 2) {
33
+ exit 0
34
+ }
35
+ if ($errorCode -eq 32 -or $errorCode -eq 33) {
36
+ exit 1
37
+ }
38
+
39
+ [Console]::Error.WriteLine($errorCode)
40
+ exit 2
41
+ `;
42
+
43
+ type ExecFileResult = { stdout: string };
44
+ type ExecFileRunner = (
45
+ command: string,
46
+ args: string[],
47
+ options: { encoding: "utf8"; timeout: number },
48
+ ) => Promise<ExecFileResult>;
49
+
50
+ const runExecFile: ExecFileRunner = async (command, args, options) => {
51
+ const { stdout } = await execFileAsync(command, args, options);
52
+ return { stdout };
53
+ };
54
+
55
+ /** Whether no process holds `path` open. Windows fails closed on probe errors. */
56
+ export async function isFileUnheld(
57
+ path: string,
58
+ platform: NodeJS.Platform = process.platform,
59
+ run: ExecFileRunner = runExecFile,
60
+ ): Promise<boolean> {
61
+ if (platform === "win32") {
62
+ try {
63
+ await run(
64
+ "powershell.exe",
65
+ [
66
+ "-NoProfile",
67
+ "-NonInteractive",
68
+ "-Command",
69
+ WINDOWS_EXCLUSIVE_OPEN_SCRIPT,
70
+ path,
71
+ ],
72
+ { encoding: "utf8", timeout: 3000 },
73
+ );
74
+ return true;
75
+ } catch {
76
+ return false;
77
+ }
78
+ }
79
+
80
+ try {
81
+ const { stdout } = await run("lsof", ["-t", path], {
82
+ encoding: "utf8",
83
+ timeout: 3000,
84
+ });
85
+ return stdout.length === 0;
86
+ } catch {
87
+ // lsof exits non-zero when no process holds the file. On hosts without
88
+ // lsof this degrades to unconditional removal, matching prior behavior.
89
+ return true;
90
+ }
91
+ }
@@ -0,0 +1,17 @@
1
+ import { expect, mock, test } from "bun:test";
2
+
3
+ import { terminateProcessTree } from "./host-process.js";
4
+
5
+ test("Windows process trees use taskkill and fall back to the direct child", () => {
6
+ const kill = mock(() => true);
7
+ let taskkillPid: number | undefined;
8
+
9
+ terminateProcessTree({ pid: 1234, kill }, "win32", (pid, onFailure) => {
10
+ taskkillPid = pid;
11
+ onFailure();
12
+ });
13
+
14
+ expect(taskkillPid).toBe(1234);
15
+ expect(kill).toHaveBeenCalledTimes(1);
16
+ expect(kill).toHaveBeenCalledWith();
17
+ });