@vellumai/assistant 0.3.4 → 0.3.6

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 (506) hide show
  1. package/Dockerfile +2 -0
  2. package/README.md +88 -2
  3. package/eslint.config.mjs +31 -0
  4. package/package.json +1 -1
  5. package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
  6. package/scripts/ipc/generate-swift.ts +31 -2
  7. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +438 -1
  8. package/src/__tests__/approval-conversation-turn.test.ts +214 -0
  9. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
  10. package/src/__tests__/approval-message-composer.test.ts +253 -0
  11. package/src/__tests__/browser-manager.test.ts +1 -0
  12. package/src/__tests__/call-conversation-messages.test.ts +130 -0
  13. package/src/__tests__/call-domain.test.ts +12 -2
  14. package/src/__tests__/call-orchestrator.test.ts +799 -249
  15. package/src/__tests__/call-pointer-messages.test.ts +148 -0
  16. package/src/__tests__/call-recovery.test.ts +3 -0
  17. package/src/__tests__/call-routes-http.test.ts +32 -2
  18. package/src/__tests__/call-store.test.ts +3 -0
  19. package/src/__tests__/channel-approval-routes.test.ts +1277 -98
  20. package/src/__tests__/channel-approval.test.ts +37 -0
  21. package/src/__tests__/channel-approvals.test.ts +36 -50
  22. package/src/__tests__/channel-guardian.test.ts +630 -22
  23. package/src/__tests__/channel-readiness-service.test.ts +324 -0
  24. package/src/__tests__/checker.test.ts +14 -7
  25. package/src/__tests__/clarification-resolver.test.ts +44 -24
  26. package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
  27. package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
  28. package/src/__tests__/config-schema.test.ts +14 -8
  29. package/src/__tests__/context-window-manager.test.ts +30 -2
  30. package/src/__tests__/contradiction-checker.test.ts +20 -5
  31. package/src/__tests__/credential-security-invariants.test.ts +7 -2
  32. package/src/__tests__/daemon-lifecycle.test.ts +13 -12
  33. package/src/__tests__/db-migration-rollback.test.ts +752 -0
  34. package/src/__tests__/dictation-mode-detection.test.ts +63 -0
  35. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
  36. package/src/__tests__/entity-search.test.ts +615 -0
  37. package/src/__tests__/fuzzy-match-property.test.ts +5 -5
  38. package/src/__tests__/guardian-action-store.test.ts +123 -0
  39. package/src/__tests__/guardian-action-sweep.test.ts +277 -0
  40. package/src/__tests__/guardian-dispatch.test.ts +389 -0
  41. package/src/__tests__/guardian-question-copy.test.ts +47 -0
  42. package/src/__tests__/handlers-telegram-config.test.ts +4 -2
  43. package/src/__tests__/handlers-twilio-config.test.ts +533 -0
  44. package/src/__tests__/intent-routing.test.ts +2 -0
  45. package/src/__tests__/ipc-snapshot.test.ts +291 -1
  46. package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
  47. package/src/__tests__/messaging-send-tool.test.ts +65 -0
  48. package/src/__tests__/model-intents.test.ts +96 -0
  49. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
  50. package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
  51. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
  52. package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
  53. package/src/__tests__/provider-error-scenarios.test.ts +621 -0
  54. package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
  55. package/src/__tests__/qdrant-manager.test.ts +27 -20
  56. package/src/__tests__/relay-server.test.ts +779 -40
  57. package/src/__tests__/run-orchestrator-assistant-events.test.ts +6 -0
  58. package/src/__tests__/run-orchestrator.test.ts +42 -4
  59. package/src/__tests__/runtime-runs-http.test.ts +17 -1
  60. package/src/__tests__/runtime-runs.test.ts +16 -0
  61. package/src/__tests__/schedule-store.test.ts +18 -4
  62. package/src/__tests__/scheduler-recurrence.test.ts +13 -4
  63. package/src/__tests__/session-abort-tool-results.test.ts +6 -0
  64. package/src/__tests__/session-agent-loop.test.ts +857 -0
  65. package/src/__tests__/session-conflict-gate.test.ts +6 -0
  66. package/src/__tests__/session-pre-run-repair.test.ts +6 -0
  67. package/src/__tests__/session-profile-injection.test.ts +6 -0
  68. package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
  69. package/src/__tests__/session-queue.test.ts +6 -0
  70. package/src/__tests__/session-runtime-assembly.test.ts +321 -13
  71. package/src/__tests__/session-slash-known.test.ts +6 -0
  72. package/src/__tests__/session-slash-queue.test.ts +6 -0
  73. package/src/__tests__/session-slash-unknown.test.ts +6 -0
  74. package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
  75. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
  76. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
  77. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
  78. package/src/__tests__/session-workspace-injection.test.ts +6 -0
  79. package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
  80. package/src/__tests__/skills.test.ts +2 -0
  81. package/src/__tests__/sms-messaging-provider.test.ts +126 -0
  82. package/src/__tests__/starter-task-flow.test.ts +2 -0
  83. package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
  84. package/src/__tests__/system-prompt.test.ts +2 -0
  85. package/src/__tests__/task-management-tools.test.ts +2 -2
  86. package/src/__tests__/task-runner.test.ts +14 -4
  87. package/src/__tests__/terminal-tools.test.ts +25 -19
  88. package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
  89. package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
  90. package/src/__tests__/tool-executor.test.ts +23 -24
  91. package/src/__tests__/trust-store.test.ts +3 -3
  92. package/src/__tests__/twilio-rest.test.ts +29 -0
  93. package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
  94. package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
  95. package/src/__tests__/twilio-routes.test.ts +167 -11
  96. package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
  97. package/src/__tests__/user-reference.test.ts +2 -0
  98. package/src/__tests__/voice-quality.test.ts +222 -0
  99. package/src/__tests__/web-search.test.ts +46 -30
  100. package/src/__tests__/work-item-output.test.ts +110 -0
  101. package/src/agent/loop.ts +1 -1
  102. package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
  103. package/src/amazon/client.ts +1418 -0
  104. package/src/amazon/request-extractor.ts +135 -0
  105. package/src/amazon/session.ts +109 -0
  106. package/src/autonomy/autonomy-store.ts +5 -5
  107. package/src/browser-extension-relay/client.ts +124 -0
  108. package/src/browser-extension-relay/protocol.ts +63 -0
  109. package/src/browser-extension-relay/server.ts +177 -0
  110. package/src/bundler/app-bundler.ts +3 -3
  111. package/src/bundler/bundle-signer.ts +1 -1
  112. package/src/bundler/signature-verifier.ts +1 -1
  113. package/src/calls/call-conversation-messages.ts +33 -0
  114. package/src/calls/call-domain.ts +114 -10
  115. package/src/calls/call-orchestrator.ts +268 -59
  116. package/src/calls/call-pointer-messages.ts +53 -0
  117. package/src/calls/call-recovery.ts +3 -8
  118. package/src/calls/call-store.ts +69 -87
  119. package/src/calls/elevenlabs-config.ts +3 -2
  120. package/src/calls/guardian-action-sweep.ts +105 -0
  121. package/src/calls/guardian-dispatch.ts +203 -0
  122. package/src/calls/guardian-question-copy.ts +133 -0
  123. package/src/calls/relay-server.ts +466 -8
  124. package/src/calls/speaker-identification.ts +1 -1
  125. package/src/calls/twilio-config.ts +22 -14
  126. package/src/calls/twilio-provider.ts +6 -4
  127. package/src/calls/twilio-rest.ts +308 -7
  128. package/src/calls/twilio-routes.ts +65 -12
  129. package/src/calls/types.ts +3 -1
  130. package/src/channels/types.ts +25 -0
  131. package/src/cli/amazon.ts +815 -0
  132. package/src/cli/config-commands.ts +2 -2
  133. package/src/cli/core-commands.ts +4 -3
  134. package/src/cli/influencer.ts +244 -0
  135. package/src/cli/map.ts +89 -6
  136. package/src/cli.ts +1 -1
  137. package/src/config/agent-schema.ts +171 -0
  138. package/src/config/bundled-skills/amazon/SKILL.md +127 -0
  139. package/src/config/bundled-skills/amazon/icon.svg +13 -0
  140. package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
  141. package/src/config/bundled-skills/browser/SKILL.md +1 -0
  142. package/src/config/bundled-skills/browser/TOOLS.json +17 -0
  143. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
  144. package/src/config/bundled-skills/doordash/SKILL.md +51 -51
  145. package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
  146. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
  147. package/src/config/bundled-skills/influencer/SKILL.md +144 -0
  148. package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
  149. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
  150. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
  151. package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
  152. package/src/config/bundled-skills/media-processing/SKILL.md +176 -0
  153. package/src/config/bundled-skills/media-processing/TOOLS.json +230 -0
  154. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
  155. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
  156. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
  157. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
  158. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
  159. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
  160. package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
  161. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +259 -0
  162. package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
  163. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +136 -0
  164. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +59 -0
  165. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
  166. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
  167. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +143 -0
  168. package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
  169. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +65 -0
  170. package/src/config/bundled-skills/messaging/SKILL.md +33 -8
  171. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
  172. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
  173. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
  174. package/src/config/bundled-skills/phone-calls/SKILL.md +88 -23
  175. package/src/config/bundled-skills/twitter/SKILL.md +19 -3
  176. package/src/config/bundled-skills/twitter/icon.svg +14 -0
  177. package/src/config/bundled-tool-registry.ts +310 -0
  178. package/src/config/calls-schema.ts +181 -0
  179. package/src/config/core-schema.ts +309 -0
  180. package/src/config/defaults.ts +28 -3
  181. package/src/config/env-registry.ts +162 -0
  182. package/src/config/env.ts +175 -0
  183. package/src/config/loader.ts +6 -6
  184. package/src/config/memory-schema.ts +528 -0
  185. package/src/config/sandbox-schema.ts +55 -0
  186. package/src/config/schema.ts +158 -1133
  187. package/src/config/skill-state.ts +1 -1
  188. package/src/config/skills-schema.ts +32 -0
  189. package/src/config/skills.ts +35 -24
  190. package/src/config/system-prompt.ts +131 -56
  191. package/src/config/templates/IDENTITY.md +2 -2
  192. package/src/config/templates/SOUL.md +1 -1
  193. package/src/config/types.ts +1 -0
  194. package/src/config/user-reference.ts +4 -9
  195. package/src/config/vellum-skills/catalog.json +6 -7
  196. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
  197. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +4 -3
  198. package/src/config/vellum-skills/sms-setup/SKILL.md +216 -0
  199. package/src/config/vellum-skills/twilio-setup/SKILL.md +40 -8
  200. package/src/context/window-manager.ts +27 -7
  201. package/src/daemon/approval-generators.ts +186 -0
  202. package/src/daemon/approved-devices-store.ts +140 -0
  203. package/src/daemon/assistant-attachments.ts +1 -1
  204. package/src/daemon/classifier.ts +35 -32
  205. package/src/daemon/config-watcher.ts +1 -1
  206. package/src/daemon/daemon-control.ts +217 -0
  207. package/src/daemon/handlers/apps.ts +2 -3
  208. package/src/daemon/handlers/config-channels.ts +158 -0
  209. package/src/daemon/handlers/config-inbox.ts +540 -0
  210. package/src/daemon/handlers/config-ingress.ts +231 -0
  211. package/src/daemon/handlers/config-integrations.ts +258 -0
  212. package/src/daemon/handlers/config-model.ts +143 -0
  213. package/src/daemon/handlers/config-parental.ts +163 -0
  214. package/src/daemon/handlers/config-scheduling.ts +172 -0
  215. package/src/daemon/handlers/config-slack.ts +92 -0
  216. package/src/daemon/handlers/config-telegram.ts +301 -0
  217. package/src/daemon/handlers/config-tools.ts +177 -0
  218. package/src/daemon/handlers/config-trust.ts +104 -0
  219. package/src/daemon/handlers/config-twilio.ts +1080 -0
  220. package/src/daemon/handlers/config.ts +53 -1689
  221. package/src/daemon/handlers/diagnostics.ts +1 -1
  222. package/src/daemon/handlers/dictation.ts +180 -0
  223. package/src/daemon/handlers/documents.ts +18 -32
  224. package/src/daemon/handlers/identity.ts +14 -23
  225. package/src/daemon/handlers/index.ts +11 -0
  226. package/src/daemon/handlers/misc.ts +3 -5
  227. package/src/daemon/handlers/pairing.ts +98 -0
  228. package/src/daemon/handlers/sessions.ts +56 -5
  229. package/src/daemon/handlers/shared.ts +6 -1
  230. package/src/daemon/handlers/skills.ts +1 -1
  231. package/src/daemon/handlers/twitter-auth.ts +2 -0
  232. package/src/daemon/handlers/work-items.ts +17 -9
  233. package/src/daemon/handlers/workspace-files.ts +4 -3
  234. package/src/daemon/install-cli-launchers.ts +113 -0
  235. package/src/daemon/ipc-contract/apps.ts +356 -0
  236. package/src/daemon/ipc-contract/browser.ts +74 -0
  237. package/src/daemon/ipc-contract/computer-use.ts +151 -0
  238. package/src/daemon/ipc-contract/diagnostics.ts +56 -0
  239. package/src/daemon/ipc-contract/documents.ts +74 -0
  240. package/src/daemon/ipc-contract/inbox.ts +209 -0
  241. package/src/daemon/ipc-contract/integrations.ts +284 -0
  242. package/src/daemon/ipc-contract/memory.ts +48 -0
  243. package/src/daemon/ipc-contract/messages.ts +211 -0
  244. package/src/daemon/ipc-contract/pairing.ts +45 -0
  245. package/src/daemon/ipc-contract/parental-control.ts +95 -0
  246. package/src/daemon/ipc-contract/schedules.ts +97 -0
  247. package/src/daemon/ipc-contract/sessions.ts +315 -0
  248. package/src/daemon/ipc-contract/shared.ts +42 -0
  249. package/src/daemon/ipc-contract/skills.ts +120 -0
  250. package/src/daemon/ipc-contract/subagents.ts +58 -0
  251. package/src/daemon/ipc-contract/surfaces.ts +250 -0
  252. package/src/daemon/ipc-contract/trust.ts +60 -0
  253. package/src/daemon/ipc-contract/work-items.ts +225 -0
  254. package/src/daemon/ipc-contract/workspace.ts +113 -0
  255. package/src/daemon/ipc-contract-inventory.json +70 -0
  256. package/src/daemon/ipc-contract-inventory.ts +55 -29
  257. package/src/daemon/ipc-contract.ts +229 -2426
  258. package/src/daemon/ipc-protocol.ts +1 -1
  259. package/src/daemon/ipc-validate.ts +7 -0
  260. package/src/daemon/lifecycle.ts +97 -377
  261. package/src/daemon/pairing-store.ts +177 -0
  262. package/src/daemon/providers-setup.ts +43 -0
  263. package/src/daemon/ride-shotgun-handler.ts +68 -3
  264. package/src/daemon/server.ts +66 -46
  265. package/src/daemon/session-agent-loop-handlers.ts +421 -0
  266. package/src/daemon/session-agent-loop.ts +117 -275
  267. package/src/daemon/session-dynamic-profile.ts +1 -1
  268. package/src/daemon/session-history.ts +1 -1
  269. package/src/daemon/session-media-retry.ts +1 -1
  270. package/src/daemon/session-messaging.ts +37 -2
  271. package/src/daemon/session-notifiers.ts +5 -25
  272. package/src/daemon/session-process.ts +99 -59
  273. package/src/daemon/session-queue-manager.ts +96 -4
  274. package/src/daemon/session-runtime-assembly.ts +199 -10
  275. package/src/daemon/session-surfaces.ts +19 -4
  276. package/src/daemon/session-tool-setup.ts +30 -30
  277. package/src/daemon/session-workspace.ts +1 -1
  278. package/src/daemon/session.ts +35 -2
  279. package/src/daemon/shutdown-handlers.ts +122 -0
  280. package/src/daemon/trace-emitter.ts +1 -1
  281. package/src/daemon/watch-handler.ts +36 -33
  282. package/src/doordash/cart-queries.ts +787 -0
  283. package/src/doordash/client.ts +144 -127
  284. package/src/doordash/order-queries.ts +85 -0
  285. package/src/doordash/queries.ts +10 -1308
  286. package/src/doordash/search-queries.ts +203 -0
  287. package/src/doordash/session.ts +3 -2
  288. package/src/doordash/store-queries.ts +246 -0
  289. package/src/doordash/types.ts +367 -0
  290. package/src/email/providers/agentmail.ts +2 -1
  291. package/src/email/providers/index.ts +3 -2
  292. package/src/email/service.ts +3 -2
  293. package/src/errors.ts +43 -0
  294. package/src/home-base/prebuilt/seed.ts +1 -1
  295. package/src/hooks/cli.ts +6 -5
  296. package/src/hooks/config.ts +6 -8
  297. package/src/hooks/discovery.ts +6 -5
  298. package/src/hooks/manager.ts +4 -3
  299. package/src/hooks/runner.ts +2 -2
  300. package/src/hooks/templates.ts +5 -5
  301. package/src/inbound/public-ingress-urls.ts +6 -4
  302. package/src/index.ts +4 -2
  303. package/src/influencer/client.ts +1104 -0
  304. package/src/instrument.ts +4 -3
  305. package/src/logfire.ts +4 -3
  306. package/src/memory/admin.ts +25 -35
  307. package/src/memory/attachments-store.ts +4 -7
  308. package/src/memory/channel-delivery-store.ts +30 -1
  309. package/src/memory/channel-guardian-store.ts +202 -2
  310. package/src/memory/clarification-resolver.ts +37 -33
  311. package/src/memory/conflict-store.ts +67 -61
  312. package/src/memory/contradiction-checker.ts +141 -117
  313. package/src/memory/conversation-store.ts +335 -51
  314. package/src/memory/db-connection.ts +27 -4
  315. package/src/memory/db-init.ts +265 -4
  316. package/src/memory/db.ts +14 -1
  317. package/src/memory/embedding-backend.ts +27 -5
  318. package/src/memory/embedding-ollama.ts +2 -1
  319. package/src/memory/entity-extractor.ts +38 -35
  320. package/src/memory/guardian-action-store.ts +430 -0
  321. package/src/memory/inbox-escalation-projection.ts +59 -0
  322. package/src/memory/inbox-thread-store.ts +218 -0
  323. package/src/memory/ingress-invite-store.ts +338 -0
  324. package/src/memory/ingress-member-store.ts +350 -0
  325. package/src/memory/items-extractor.ts +91 -97
  326. package/src/memory/job-handlers/index-maintenance.ts +3 -3
  327. package/src/memory/job-handlers/media-processing.ts +69 -0
  328. package/src/memory/job-handlers/summarization.ts +32 -26
  329. package/src/memory/job-utils.ts +3 -10
  330. package/src/memory/jobs-store.ts +8 -10
  331. package/src/memory/jobs-worker.ts +55 -36
  332. package/src/memory/media-store.ts +759 -0
  333. package/src/memory/migrations/001-job-deferrals.ts +45 -0
  334. package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
  335. package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
  336. package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
  337. package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
  338. package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
  339. package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
  340. package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
  341. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
  342. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
  343. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
  344. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
  345. package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
  346. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
  347. package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
  348. package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
  349. package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
  350. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
  351. package/src/memory/migrations/index.ts +24 -0
  352. package/src/memory/migrations/registry.ts +79 -0
  353. package/src/memory/migrations/validate-migration-state.ts +69 -0
  354. package/src/memory/qdrant-manager.ts +49 -8
  355. package/src/memory/query-builder.ts +1 -1
  356. package/src/memory/raw-query.ts +119 -0
  357. package/src/memory/recall-cache.ts +4 -1
  358. package/src/memory/retriever.ts +165 -47
  359. package/src/memory/schema-migration.ts +25 -984
  360. package/src/memory/schema.ts +228 -7
  361. package/src/memory/search/entity.ts +205 -31
  362. package/src/memory/search/lexical.ts +81 -52
  363. package/src/memory/search/ranking.ts +27 -23
  364. package/src/memory/search/semantic.ts +157 -19
  365. package/src/memory/search/types.ts +24 -0
  366. package/src/memory/shared-app-links-store.ts +4 -5
  367. package/src/memory/validation.ts +19 -0
  368. package/src/messaging/draft-store.ts +5 -6
  369. package/src/messaging/provider-types.ts +2 -0
  370. package/src/messaging/providers/sms/adapter.ts +201 -0
  371. package/src/messaging/providers/sms/client.ts +93 -0
  372. package/src/messaging/providers/sms/types.ts +7 -0
  373. package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
  374. package/src/messaging/providers/whatsapp/adapter.ts +136 -0
  375. package/src/messaging/providers/whatsapp/client.ts +67 -0
  376. package/src/messaging/style-analyzer.ts +5 -4
  377. package/src/messaging/thread-summarizer.ts +61 -69
  378. package/src/messaging/triage-engine.ts +62 -71
  379. package/src/migrations/config-merge.ts +53 -0
  380. package/src/migrations/data-layout.ts +68 -0
  381. package/src/migrations/data-merge.ts +33 -0
  382. package/src/migrations/hooks-merge.ts +90 -0
  383. package/src/migrations/index.ts +6 -0
  384. package/src/migrations/log.ts +23 -0
  385. package/src/migrations/skills-merge.ts +33 -0
  386. package/src/migrations/workspace-layout.ts +79 -0
  387. package/src/permissions/checker.ts +133 -11
  388. package/src/permissions/prompter.ts +14 -0
  389. package/src/permissions/shell-identity.ts +31 -1
  390. package/src/permissions/trust-store.ts +21 -1
  391. package/src/providers/anthropic/client.ts +4 -4
  392. package/src/providers/failover.ts +2 -2
  393. package/src/providers/model-intents.ts +70 -0
  394. package/src/providers/ollama/client.ts +2 -1
  395. package/src/providers/provider-send-message.ts +176 -0
  396. package/src/providers/registry.ts +71 -30
  397. package/src/providers/retry.ts +35 -1
  398. package/src/providers/types.ts +12 -1
  399. package/src/runtime/approval-conversation-turn.ts +97 -0
  400. package/src/runtime/approval-message-composer.ts +253 -0
  401. package/src/runtime/channel-approval-parser.ts +36 -2
  402. package/src/runtime/channel-approvals.ts +11 -24
  403. package/src/runtime/channel-guardian-service.ts +88 -21
  404. package/src/runtime/channel-readiness-service.ts +418 -0
  405. package/src/runtime/channel-readiness-types.ts +35 -0
  406. package/src/runtime/channel-retry-sweep.ts +184 -0
  407. package/src/runtime/guardian-context-resolver.ts +108 -0
  408. package/src/runtime/http-server.ts +275 -717
  409. package/src/runtime/http-types.ts +59 -3
  410. package/src/runtime/middleware/auth.ts +116 -0
  411. package/src/runtime/middleware/error-handler.ts +33 -0
  412. package/src/runtime/middleware/twilio-validation.ts +127 -0
  413. package/src/runtime/routes/app-routes.ts +1 -1
  414. package/src/runtime/routes/call-routes.ts +51 -7
  415. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  416. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  417. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  418. package/src/runtime/routes/channel-route-shared.ts +144 -0
  419. package/src/runtime/routes/channel-routes.ts +32 -1588
  420. package/src/runtime/routes/conversation-routes.ts +50 -7
  421. package/src/runtime/routes/events-routes.ts +2 -2
  422. package/src/runtime/routes/identity-routes.ts +126 -0
  423. package/src/runtime/routes/pairing-routes.ts +143 -0
  424. package/src/runtime/routes/run-routes.ts +15 -1
  425. package/src/runtime/run-orchestrator.ts +86 -35
  426. package/src/schedule/schedule-store.ts +36 -32
  427. package/src/schedule/scheduler.ts +3 -3
  428. package/src/security/encrypted-store.ts +5 -7
  429. package/src/security/oauth2.ts +45 -15
  430. package/src/security/parental-control-store.ts +183 -0
  431. package/src/security/secret-allowlist.ts +4 -3
  432. package/src/security/secret-scanner.ts +5 -5
  433. package/src/security/secure-keys.ts +1 -1
  434. package/src/security/token-manager.ts +3 -2
  435. package/src/services/vercel-deploy.ts +6 -2
  436. package/src/skills/tool-manifest.ts +3 -3
  437. package/src/skills/vellum-catalog-remote.ts +75 -16
  438. package/src/slack/slack-webhook.ts +2 -1
  439. package/src/swarm/orchestrator.ts +92 -1
  440. package/src/swarm/router-planner.ts +6 -9
  441. package/src/swarm/worker-prompts.ts +9 -12
  442. package/src/tasks/task-compiler.ts +19 -28
  443. package/src/tasks/task-runner.ts +1 -1
  444. package/src/tools/assets/materialize.ts +2 -2
  445. package/src/tools/assets/search.ts +15 -14
  446. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  447. package/src/tools/browser/auto-navigate.ts +1 -0
  448. package/src/tools/browser/browser-execution.ts +10 -1
  449. package/src/tools/browser/browser-manager.ts +119 -4
  450. package/src/tools/browser/network-recorder.ts +5 -0
  451. package/src/tools/calls/call-start.ts +1 -0
  452. package/src/tools/credentials/broker.ts +11 -2
  453. package/src/tools/credentials/metadata-store.ts +18 -14
  454. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  455. package/src/tools/credentials/vault.ts +49 -23
  456. package/src/tools/execution-target.ts +11 -1
  457. package/src/tools/executor.ts +68 -9
  458. package/src/tools/host-terminal/cli-discover.ts +1 -1
  459. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  460. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  461. package/src/tools/network/script-proxy/server.ts +1 -1
  462. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  463. package/src/tools/network/web-fetch.ts +18 -2
  464. package/src/tools/network/web-search.ts +8 -4
  465. package/src/tools/reminder/reminder-store.ts +14 -15
  466. package/src/tools/schedule/create.ts +1 -0
  467. package/src/tools/schedule/list.ts +2 -1
  468. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  469. package/src/tools/skills/skill-script-runner.ts +24 -9
  470. package/src/tools/skills/skill-tool-factory.ts +1 -0
  471. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  472. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  473. package/src/tools/terminal/parser.ts +50 -0
  474. package/src/tools/types.ts +2 -0
  475. package/src/tools/watcher/delete.ts +6 -0
  476. package/src/tools/weather/service.ts +1 -1
  477. package/src/twitter/client.ts +190 -24
  478. package/src/twitter/router.ts +1 -1
  479. package/src/twitter/session.ts +4 -3
  480. package/src/util/clipboard.ts +1 -1
  481. package/src/util/errors.ts +65 -8
  482. package/src/util/fs.ts +40 -0
  483. package/src/util/json.ts +10 -0
  484. package/src/util/log-redact.ts +189 -0
  485. package/src/util/logger.ts +19 -17
  486. package/src/util/object.ts +3 -0
  487. package/src/util/platform.ts +105 -363
  488. package/src/util/pricing.ts +1 -1
  489. package/src/util/promise-guard.ts +1 -1
  490. package/src/util/retry.ts +19 -0
  491. package/src/util/row-mapper.ts +79 -0
  492. package/src/util/silently.ts +21 -0
  493. package/src/watcher/engine.ts +5 -1
  494. package/src/watcher/provider-types.ts +20 -0
  495. package/src/watcher/providers/github.ts +156 -0
  496. package/src/watcher/providers/gmail.ts +1 -0
  497. package/src/watcher/providers/google-calendar.ts +1 -0
  498. package/src/watcher/providers/linear.ts +460 -0
  499. package/src/watcher/providers/slack.ts +1 -0
  500. package/src/work-items/work-item-runner.ts +1 -1
  501. package/src/workspace/git-service.ts +1 -1
  502. package/src/workspace/provider-commit-message-generator.ts +51 -22
  503. package/src/__tests__/call-bridge.test.ts +0 -517
  504. package/src/__tests__/session-process-bridge.test.ts +0 -244
  505. package/src/calls/call-bridge.ts +0 -168
  506. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Helper utilities for provider callsites that should stay decoupled from
3
+ * provider SDK details. Includes provider resolution, timeout utilities,
4
+ * and response extraction helpers.
5
+ */
6
+
7
+ import type { Provider, ProviderResponse, Message, ContentBlock, ToolUseContent } from './types.js';
8
+ import { getFailoverProvider, listProviders, initializeProviders, resolveProviderSelection } from './registry.js';
9
+ import { getConfig } from '../config/loader.js';
10
+ import { getLogger } from '../util/logger.js';
11
+
12
+ export interface ConfiguredProviderResult {
13
+ provider: Provider;
14
+ configuredProviderName: string;
15
+ selectedProviderName: string;
16
+ usedFallbackPrimary: boolean;
17
+ }
18
+
19
+ const providerSelectionLog = getLogger('provider-selection');
20
+ let fallbackWarningLogged = false;
21
+
22
+ /**
23
+ * Resolve the configured provider with full selection metadata.
24
+ * If providers haven't been initialized yet (e.g. non-daemon code paths),
25
+ * performs a one-shot `initializeProviders(getConfig())`.
26
+ *
27
+ * Uses fail-open selection: if `config.provider` is unavailable but
28
+ * alternates from `config.providerOrder` exist, selects the first available.
29
+ *
30
+ * Returns `null` when no providers are available at all.
31
+ */
32
+ export function resolveConfiguredProvider(): ConfiguredProviderResult | null {
33
+ const config = getConfig();
34
+
35
+ if (listProviders().length === 0) {
36
+ try {
37
+ initializeProviders(config);
38
+ } catch {
39
+ return null;
40
+ }
41
+ }
42
+
43
+ const providerOrder = Array.isArray(config.providerOrder) ? config.providerOrder : [];
44
+ const selection = resolveProviderSelection(config.provider, providerOrder);
45
+
46
+ if (!selection.selectedPrimary) {
47
+ return null;
48
+ }
49
+
50
+ if (selection.usedFallbackPrimary) {
51
+ const level = fallbackWarningLogged ? 'debug' : 'warn';
52
+ providerSelectionLog[level](
53
+ { configured: config.provider, selected: selection.selectedPrimary },
54
+ 'Configured provider unavailable, using fallback',
55
+ );
56
+ fallbackWarningLogged = true;
57
+ }
58
+
59
+ try {
60
+ const provider = getFailoverProvider(config.provider, providerOrder);
61
+ return {
62
+ provider,
63
+ configuredProviderName: config.provider,
64
+ selectedProviderName: selection.selectedPrimary,
65
+ usedFallbackPrimary: selection.usedFallbackPrimary,
66
+ };
67
+ } catch {
68
+ return null;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Resolve the configured provider through the registry/failover path.
74
+ * Thin wrapper around `resolveConfiguredProvider()` for callsites
75
+ * that only need the Provider instance.
76
+ *
77
+ * Returns `null` when no providers are available.
78
+ */
79
+ export function getConfiguredProvider(): Provider | null {
80
+ const result = resolveConfiguredProvider();
81
+ return result?.provider ?? null;
82
+ }
83
+
84
+ /**
85
+ * Create an AbortSignal that fires after `ms` milliseconds.
86
+ * Returns the signal and a cleanup function to clear the timer.
87
+ */
88
+ export function createTimeout(ms: number): { signal: AbortSignal; cleanup: () => void } {
89
+ const controller = new AbortController();
90
+ const timer = setTimeout(() => controller.abort(), ms);
91
+ return {
92
+ signal: controller.signal,
93
+ cleanup: () => clearTimeout(timer),
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Extract the first text block's text from a ProviderResponse.
99
+ * Returns empty string if no text block is found.
100
+ */
101
+ export function extractText(response: ProviderResponse): string {
102
+ const block = response.content.find((b): b is Extract<ContentBlock, { type: 'text' }> => b.type === 'text');
103
+ return block?.text?.trim() ?? '';
104
+ }
105
+
106
+ /**
107
+ * Extract all text blocks from a ProviderResponse and join them.
108
+ */
109
+ export function extractAllText(response: ProviderResponse): string {
110
+ return response.content
111
+ .filter((b): b is Extract<ContentBlock, { type: 'text' }> => b.type === 'text')
112
+ .map((b) => b.text)
113
+ .join('');
114
+ }
115
+
116
+ /**
117
+ * Find the first tool_use block in a ProviderResponse.
118
+ */
119
+ export function extractToolUse(response: ProviderResponse): ToolUseContent | undefined {
120
+ return response.content.find((b): b is ToolUseContent => b.type === 'tool_use');
121
+ }
122
+
123
+ /**
124
+ * Build a single user message in the provider Message format.
125
+ */
126
+ export function userMessage(text: string): Message {
127
+ return { role: 'user', content: [{ type: 'text', text }] };
128
+ }
129
+
130
+ /**
131
+ * Build a single user message with image + text content.
132
+ */
133
+ export function userMessageWithImage(
134
+ imageBase64: string,
135
+ mediaType: string,
136
+ text: string,
137
+ ): Message {
138
+ return {
139
+ role: 'user',
140
+ content: [
141
+ {
142
+ type: 'image',
143
+ source: {
144
+ type: 'base64',
145
+ media_type: mediaType,
146
+ data: imageBase64,
147
+ },
148
+ },
149
+ { type: 'text', text },
150
+ ],
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Build a single user message with multiple images followed by a text block.
156
+ * Each image becomes its own content block; the text block comes last.
157
+ */
158
+ export function userMessageWithImages(
159
+ images: Array<{ base64: string; mediaType: string }>,
160
+ text: string,
161
+ ): Message {
162
+ return {
163
+ role: 'user',
164
+ content: [
165
+ ...images.map((img) => ({
166
+ type: 'image' as const,
167
+ source: {
168
+ type: 'base64' as const,
169
+ media_type: img.mediaType,
170
+ data: img.base64,
171
+ },
172
+ })),
173
+ { type: 'text' as const, text },
174
+ ],
175
+ };
176
+ }
@@ -9,15 +9,7 @@ import { RetryProvider } from "./retry.js";
9
9
  import { FailoverProvider } from "./failover.js";
10
10
  import { wrapWithLogfire } from "../logfire.js";
11
11
  import { ConfigError } from "../util/errors.js";
12
-
13
- const DEFAULT_MODELS: Record<string, string> = {
14
- anthropic: 'claude-opus-4-6',
15
- openai: 'gpt-5.2',
16
- gemini: 'gemini-3-flash',
17
- ollama: 'llama3.2',
18
- fireworks: 'accounts/fireworks/models/kimi-k2p5',
19
- openrouter: 'x-ai/grok-4',
20
- };
12
+ import { getProviderDefaultModel } from "./model-intents.js";
21
13
 
22
14
  const providers = new Map<string, Provider>();
23
15
  let cachedFailoverProvider: FailoverProvider | null = null;
@@ -37,33 +29,74 @@ export function getProvider(name: string): Provider {
37
29
  return provider;
38
30
  }
39
31
 
32
+ export interface ProviderSelection {
33
+ /** Ordered list of available provider names */
34
+ availableProviders: string[];
35
+ /** The selected (effective) primary provider name, or null if none available */
36
+ selectedPrimary: string | null;
37
+ /** Whether the effective primary differs from the requested primary */
38
+ usedFallbackPrimary: boolean;
39
+ }
40
+
40
41
  /**
41
- * Build a provider that tries the primary provider first, then falls back to
42
- * others in the configured order. If providerOrder is empty or only contains
43
- * the primary, returns the primary provider directly (no wrapper overhead).
42
+ * Resolve provider selection from requested primary and provider order.
43
+ * Dedupes [requestedPrimary, ...providerOrder], filtered to initialized providers.
44
+ * Returns null selectedPrimary when no providers are available.
45
+ */
46
+ export function resolveProviderSelection(
47
+ requestedPrimary: string,
48
+ providerOrder: string[],
49
+ ): ProviderSelection {
50
+ const ordered: string[] = [];
51
+ const seen = new Set<string>();
52
+
53
+ for (const name of [requestedPrimary, ...providerOrder]) {
54
+ if (seen.has(name)) continue;
55
+ seen.add(name);
56
+ if (providers.has(name)) {
57
+ ordered.push(name);
58
+ }
59
+ }
60
+
61
+ if (ordered.length === 0) {
62
+ return { availableProviders: [], selectedPrimary: null, usedFallbackPrimary: false };
63
+ }
64
+
65
+ return {
66
+ availableProviders: ordered,
67
+ selectedPrimary: ordered[0],
68
+ usedFallbackPrimary: ordered[0] !== requestedPrimary,
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Build a provider that tries the effective primary provider first, then falls
74
+ * back to others in the configured order. If the requested primary is not
75
+ * available, automatically selects the first available provider from the
76
+ * deduped [primaryName, ...providerOrder] list (fail-open).
77
+ *
78
+ * Throws ConfigError only when NO providers are available at all.
44
79
  * Caches the FailoverProvider instance so health state persists across calls.
45
80
  */
46
81
  export function getFailoverProvider(primaryName: string, providerOrder: string[]): Provider {
47
- const primary = getProvider(primaryName);
48
-
49
- // Build the ordered list: primary first, then remaining from providerOrder
50
- const orderedProviders: Provider[] = [primary];
51
- const seen = new Set<string>([primaryName]);
82
+ const selection = resolveProviderSelection(primaryName, providerOrder);
52
83
 
53
- for (const name of providerOrder) {
54
- if (seen.has(name)) continue;
55
- const p = providers.get(name);
56
- if (p) {
57
- orderedProviders.push(p);
58
- seen.add(name);
59
- }
84
+ if (!selection.selectedPrimary) {
85
+ throw new ConfigError(
86
+ `No providers available. Requested: "${primaryName}". Registered: ${listProviders().join(", ") || "none"}`,
87
+ );
60
88
  }
61
89
 
90
+ const orderedProviders: Provider[] = selection.availableProviders.map(
91
+ (name) => providers.get(name)!,
92
+ );
93
+
62
94
  if (orderedProviders.length === 1) {
63
- return primary;
95
+ return orderedProviders[0];
64
96
  }
65
97
 
66
- const cacheKey = `${primaryName}:${providerOrder.join(',')}`;
98
+ // Cache key from effective ordered providers (not raw input strings)
99
+ const cacheKey = selection.availableProviders.join(',');
67
100
  if (cachedFailoverProvider && cachedFailoverKey === cacheKey) {
68
101
  return cachedFailoverProvider;
69
102
  }
@@ -77,6 +110,14 @@ export function listProviders(): string[] {
77
110
  return Array.from(providers.keys());
78
111
  }
79
112
 
113
+ /**
114
+ * Return the default model for a given provider name.
115
+ * Falls back to the Anthropic default if the provider name is unknown.
116
+ */
117
+ export function getDefaultModel(providerName: string): string {
118
+ return getProviderDefaultModel(providerName);
119
+ }
120
+
80
121
  export interface ProvidersConfig {
81
122
  apiKeys: Record<string, string>;
82
123
  provider: string;
@@ -85,16 +126,16 @@ export interface ProvidersConfig {
85
126
  timeouts?: { providerStreamTimeoutSec?: number };
86
127
  }
87
128
 
88
- function resolveModel(config: ProvidersConfig, providerName: keyof typeof DEFAULT_MODELS): string {
129
+ function resolveModel(config: ProvidersConfig, providerName: string): string {
89
130
  if (config.provider === providerName) {
90
131
  // If a non-Anthropic provider is selected with the untouched global default
91
132
  // model, use a provider-appropriate fallback instead.
92
- if (providerName !== 'anthropic' && config.model === DEFAULT_MODELS.anthropic) {
93
- return DEFAULT_MODELS[providerName];
133
+ if (providerName !== 'anthropic' && config.model === getProviderDefaultModel('anthropic')) {
134
+ return getProviderDefaultModel(providerName);
94
135
  }
95
136
  return config.model;
96
137
  }
97
- return DEFAULT_MODELS[providerName];
138
+ return getProviderDefaultModel(providerName);
98
139
  }
99
140
 
100
141
  export function initializeProviders(config: ProvidersConfig): void {
@@ -1,6 +1,7 @@
1
1
  import type { Provider, ProviderResponse, SendMessageOptions, Message, ToolDefinition } from './types.js';
2
2
  import { ProviderError } from '../util/errors.js';
3
3
  import { getLogger, isDebug } from '../util/logger.js';
4
+ import { isModelIntent, resolveModelIntent } from './model-intents.js';
4
5
  import {
5
6
  computeRetryDelay,
6
7
  isRetryableNetworkError,
@@ -18,6 +19,37 @@ function isRetryableError(error: unknown): boolean {
18
19
  return isRetryableNetworkError(error);
19
20
  }
20
21
 
22
+ function normalizeSendMessageOptions(providerName: string, options?: SendMessageOptions): SendMessageOptions | undefined {
23
+ const config = options?.config;
24
+ if (!config) return options;
25
+
26
+ const explicitModel = typeof config.model === 'string' && config.model.trim().length > 0
27
+ ? config.model.trim()
28
+ : undefined;
29
+ const intent = isModelIntent(config.modelIntent) ? config.modelIntent : undefined;
30
+ const hasIntent = config.modelIntent !== undefined;
31
+
32
+ if (!hasIntent && explicitModel === config.model) {
33
+ return options;
34
+ }
35
+
36
+ const nextConfig: Record<string, unknown> = { ...config };
37
+ delete nextConfig.modelIntent;
38
+
39
+ if (explicitModel) {
40
+ nextConfig.model = explicitModel;
41
+ } else if (intent) {
42
+ nextConfig.model = resolveModelIntent(providerName, intent);
43
+ } else {
44
+ delete nextConfig.model;
45
+ }
46
+
47
+ return {
48
+ ...options,
49
+ config: nextConfig,
50
+ };
51
+ }
52
+
21
53
  export class RetryProvider implements Provider {
22
54
  public readonly name: string;
23
55
 
@@ -42,10 +74,12 @@ export class RetryProvider implements Provider {
42
74
  }, 'Provider sendMessage start');
43
75
  }
44
76
 
77
+ const normalizedOptions = normalizeSendMessageOptions(this.name, options);
78
+
45
79
  for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
46
80
  try {
47
81
  const start = Date.now();
48
- const result = await this.inner.sendMessage(messages, tools, systemPrompt, options);
82
+ const result = await this.inner.sendMessage(messages, tools, systemPrompt, normalizedOptions);
49
83
  if (debug) {
50
84
  log.debug({
51
85
  provider: this.name,
@@ -70,6 +70,11 @@ export interface ToolDefinition {
70
70
  input_schema: object;
71
71
  }
72
72
 
73
+ export type ModelIntent =
74
+ | 'latency-optimized'
75
+ | 'quality-optimized'
76
+ | 'vision-optimized';
77
+
73
78
  export interface ProviderResponse {
74
79
  content: ContentBlock[];
75
80
  model: string;
@@ -92,8 +97,14 @@ export type ProviderEvent =
92
97
  | { type: 'thinking_delta'; thinking: string }
93
98
  | { type: 'input_json_delta'; toolName: string; accumulatedJson: string };
94
99
 
100
+ export interface SendMessageConfig {
101
+ model?: string;
102
+ modelIntent?: ModelIntent;
103
+ [key: string]: unknown;
104
+ }
105
+
95
106
  export interface SendMessageOptions {
96
- config?: object;
107
+ config?: SendMessageConfig;
97
108
  onEvent?: (event: ProviderEvent) => void;
98
109
  signal?: AbortSignal;
99
110
  }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Approval conversation turn engine.
3
+ *
4
+ * Processes a single turn of the conversational approval flow by delegating
5
+ * to a generator function (typically backed by a language model) and
6
+ * validating the structured result. Fails closed on any error — returning
7
+ * a safe keep_pending disposition — so that a broken model call never
8
+ * silently approves or rejects a request.
9
+ */
10
+
11
+ // Hook point: a deterministic classifier could be inserted here as an
12
+ // alternative to model-based inference
13
+
14
+ import type {
15
+ ApprovalConversationContext,
16
+ ApprovalConversationGenerator,
17
+ ApprovalConversationResult,
18
+ ApprovalConversationDisposition,
19
+ } from './http-types.js';
20
+
21
+ const VALID_DISPOSITIONS: ReadonlySet<ApprovalConversationDisposition> = new Set([
22
+ 'keep_pending',
23
+ 'approve_once',
24
+ 'approve_always',
25
+ 'reject',
26
+ ]);
27
+
28
+ /** Dispositions that represent an actual decision (not just "keep waiting"). */
29
+ const DECISION_BEARING_DISPOSITIONS: ReadonlySet<ApprovalConversationDisposition> = new Set([
30
+ 'approve_once',
31
+ 'approve_always',
32
+ 'reject',
33
+ ]);
34
+
35
+ const FAIL_CLOSED_REPLY =
36
+ "I couldn't process that. Please reply with approve, deny, or cancel to decide on the pending request.";
37
+
38
+ function failClosed(): ApprovalConversationResult {
39
+ return { disposition: 'keep_pending', replyText: FAIL_CLOSED_REPLY };
40
+ }
41
+
42
+ function isValidResult(value: unknown): value is ApprovalConversationResult {
43
+ if (!value || typeof value !== 'object') return false;
44
+ const obj = value as Record<string, unknown>;
45
+ if (typeof obj.disposition !== 'string') return false;
46
+ if (!VALID_DISPOSITIONS.has(obj.disposition as ApprovalConversationDisposition)) return false;
47
+ if (typeof obj.replyText !== 'string' || obj.replyText.trim().length === 0) return false;
48
+ if (obj.targetRunId !== undefined && typeof obj.targetRunId !== 'string') return false;
49
+ return true;
50
+ }
51
+
52
+ /**
53
+ * Run one turn of the approval conversation engine.
54
+ *
55
+ * Calls the provided generator, validates the result, and returns a
56
+ * structured decision. On ANY failure (timeout, malformed output,
57
+ * exception) the function returns a safe keep_pending fallback.
58
+ */
59
+ export async function runApprovalConversationTurn(
60
+ context: ApprovalConversationContext,
61
+ generator: ApprovalConversationGenerator,
62
+ ): Promise<ApprovalConversationResult> {
63
+ let result: ApprovalConversationResult;
64
+
65
+ try {
66
+ result = await generator(context);
67
+ } catch {
68
+ return failClosed();
69
+ }
70
+
71
+ if (!isValidResult(result)) {
72
+ return failClosed();
73
+ }
74
+
75
+ // Enforce allowed-actions policy: the model must not return a disposition
76
+ // that the caller did not offer (keep_pending is always acceptable).
77
+ if (
78
+ result.disposition !== 'keep_pending'
79
+ && !context.allowedActions.includes(result.disposition)
80
+ ) {
81
+ return failClosed();
82
+ }
83
+
84
+ // Validate targetRunId for decision-bearing dispositions:
85
+ // 1. When multiple approvals are pending, targetRunId is required.
86
+ // 2. When targetRunId is present, it must match a known pending approval
87
+ // regardless of how many approvals are pending.
88
+ if (DECISION_BEARING_DISPOSITIONS.has(result.disposition)) {
89
+ if (context.pendingApprovals.length > 1 && !result.targetRunId) return failClosed();
90
+ if (result.targetRunId) {
91
+ const validRunIds = new Set(context.pendingApprovals.map((p) => p.runId));
92
+ if (!validRunIds.has(result.targetRunId)) return failClosed();
93
+ }
94
+ }
95
+
96
+ return result;
97
+ }