@vellumai/assistant 0.7.2 → 0.7.3

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 (347) hide show
  1. package/ARCHITECTURE.md +16 -1
  2. package/docs/architecture/memory.md +5 -2
  3. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
  4. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
  5. package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
  6. package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
  7. package/openapi.yaml +449 -22
  8. package/package.json +1 -1
  9. package/src/__tests__/app-control-flow.test.ts +21 -11
  10. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  11. package/src/__tests__/assistant-event.test.ts +0 -10
  12. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
  13. package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
  14. package/src/__tests__/auto-analysis-end-to-end.test.ts +62 -1
  15. package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
  16. package/src/__tests__/call-conversation-messages.test.ts +8 -2
  17. package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
  18. package/src/__tests__/channel-readiness-service.test.ts +4 -2
  19. package/src/__tests__/config-loader-backfill.test.ts +379 -0
  20. package/src/__tests__/config-schema.test.ts +1 -0
  21. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
  22. package/src/__tests__/config-watcher.test.ts +140 -69
  23. package/src/__tests__/context-search-agent-runner.test.ts +61 -3
  24. package/src/__tests__/context-search-conversations-source.test.ts +0 -24
  25. package/src/__tests__/context-search-fanout.test.ts +0 -1
  26. package/src/__tests__/context-search-memory-source.test.ts +3 -7
  27. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
  28. package/src/__tests__/context-search-pkb-source.test.ts +0 -1
  29. package/src/__tests__/context-search-workspace-source.test.ts +0 -1
  30. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  31. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
  32. package/src/__tests__/conversation-agent-loop.test.ts +454 -5
  33. package/src/__tests__/conversation-error.test.ts +150 -3
  34. package/src/__tests__/conversation-process-callsite.test.ts +43 -0
  35. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  36. package/src/__tests__/conversation-runtime-assembly.test.ts +65 -0
  37. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  38. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  39. package/src/__tests__/conversation-store.test.ts +0 -18
  40. package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
  41. package/src/__tests__/conversation-surfaces-data-persist.test.ts +404 -0
  42. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +2 -5
  43. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  44. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  45. package/src/__tests__/credentials-cli.test.ts +7 -0
  46. package/src/__tests__/cu-unified-flow.test.ts +176 -10
  47. package/src/__tests__/date-context.test.ts +164 -2
  48. package/src/__tests__/disk-pressure-guard.test.ts +262 -0
  49. package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
  50. package/src/__tests__/disk-pressure-policy.test.ts +241 -0
  51. package/src/__tests__/disk-pressure-routes.test.ts +379 -0
  52. package/src/__tests__/disk-pressure-tools.test.ts +277 -0
  53. package/src/__tests__/disk-usage.test.ts +150 -0
  54. package/src/__tests__/events-client-registration.test.ts +52 -0
  55. package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
  56. package/src/__tests__/file-write-tool.test.ts +4 -10
  57. package/src/__tests__/filing-service.test.ts +3 -4
  58. package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
  59. package/src/__tests__/heartbeat-service.test.ts +260 -11
  60. package/src/__tests__/host-app-control-proxy.test.ts +195 -25
  61. package/src/__tests__/host-bash-proxy.test.ts +227 -34
  62. package/src/__tests__/host-bash-routes.test.ts +178 -13
  63. package/src/__tests__/host-cu-proxy.test.ts +210 -3
  64. package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
  65. package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
  66. package/src/__tests__/host-file-proxy.test.ts +268 -6
  67. package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
  68. package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
  69. package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
  70. package/src/__tests__/http-user-message-parity.test.ts +107 -1
  71. package/src/__tests__/injector-chain.test.ts +18 -6
  72. package/src/__tests__/injector-disk-pressure.test.ts +224 -0
  73. package/src/__tests__/managed-profile-guard.test.ts +18 -0
  74. package/src/__tests__/mcp-abort-signal.test.ts +130 -0
  75. package/src/__tests__/memory-admin-recall.test.ts +3 -11
  76. package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
  77. package/src/__tests__/normalize-onboarding.test.ts +180 -0
  78. package/src/__tests__/oauth-connect-routes.test.ts +316 -0
  79. package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
  80. package/src/__tests__/onboarding-persona-write.test.ts +308 -0
  81. package/src/__tests__/openai-provider.test.ts +45 -8
  82. package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
  83. package/src/__tests__/platform-callback-registration.test.ts +21 -4
  84. package/src/__tests__/platform.test.ts +2 -1
  85. package/src/__tests__/playbook-execution.test.ts +0 -43
  86. package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
  87. package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
  88. package/src/__tests__/provider-tool-name.test.ts +23 -0
  89. package/src/__tests__/relay-server.test.ts +15 -4
  90. package/src/__tests__/runtime-events-sse.test.ts +4 -8
  91. package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
  92. package/src/__tests__/secret-ingress-http.test.ts +0 -1
  93. package/src/__tests__/suggestion-routes.test.ts +46 -0
  94. package/src/__tests__/twilio-validation.test.ts +2 -2
  95. package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
  96. package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
  97. package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
  98. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +90 -0
  99. package/src/approvals/guardian-decision-primitive.ts +13 -0
  100. package/src/approvals/guardian-request-resolvers.ts +16 -17
  101. package/src/backup/snapshot-lock.ts +2 -27
  102. package/src/bundler/compiler-tools.ts +3 -2
  103. package/src/calls/call-conversation-messages.ts +46 -10
  104. package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
  105. package/src/cli/commands/bash.ts +35 -108
  106. package/src/cli/commands/contacts.ts +64 -25
  107. package/src/cli/commands/credentials.ts +56 -0
  108. package/src/cli/commands/memory-v2.ts +7 -6
  109. package/src/cli/commands/oauth/__tests__/connect.test.ts +437 -1
  110. package/src/cli/commands/oauth/connect.ts +127 -1
  111. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
  112. package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
  113. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  114. package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
  115. package/src/cli/commands/platform/index.ts +16 -7
  116. package/src/cli/commands/status.ts +57 -0
  117. package/src/cli/program.ts +4 -2
  118. package/src/config/assistant-feature-flags.ts +13 -3
  119. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
  120. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
  121. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
  122. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
  123. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
  124. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
  125. package/src/config/env.ts +0 -8
  126. package/src/config/feature-flag-registry.json +27 -3
  127. package/src/config/loader.ts +127 -8
  128. package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
  129. package/src/config/schemas/call-site-catalog.ts +14 -0
  130. package/src/config/schemas/channels.ts +0 -5
  131. package/src/config/schemas/heartbeat.ts +1 -1
  132. package/src/config/schemas/llm.ts +2 -0
  133. package/src/config/schemas/memory-lifecycle.ts +13 -0
  134. package/src/config/schemas/memory-v2.ts +75 -11
  135. package/src/config/schemas/platform.ts +43 -3
  136. package/src/config/schemas/services.ts +28 -0
  137. package/src/config/seed-inference-profiles.ts +230 -33
  138. package/src/contacts/contact-store.ts +0 -25
  139. package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
  140. package/src/daemon/assistant-attachments.ts +4 -4
  141. package/src/daemon/config-watcher.ts +85 -57
  142. package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
  143. package/src/daemon/conversation-agent-loop.ts +170 -33
  144. package/src/daemon/conversation-error.ts +87 -15
  145. package/src/daemon/conversation-lifecycle.ts +1 -3
  146. package/src/daemon/conversation-process.ts +8 -0
  147. package/src/daemon/conversation-runtime-assembly.ts +26 -0
  148. package/src/daemon/conversation-store.ts +2 -2
  149. package/src/daemon/conversation-surfaces.ts +195 -15
  150. package/src/daemon/conversation-tool-setup.ts +57 -14
  151. package/src/daemon/conversation.ts +17 -22
  152. package/src/daemon/date-context.ts +71 -22
  153. package/src/daemon/disk-pressure-background-gate.ts +73 -0
  154. package/src/daemon/disk-pressure-guard.ts +343 -0
  155. package/src/daemon/disk-pressure-policy.ts +163 -0
  156. package/src/daemon/handlers/shared.ts +0 -1
  157. package/src/daemon/handlers/skills.ts +3 -4
  158. package/src/daemon/host-app-control-proxy.ts +137 -41
  159. package/src/daemon/host-bash-proxy.ts +46 -21
  160. package/src/daemon/host-cu-proxy.ts +49 -3
  161. package/src/daemon/host-file-proxy.ts +43 -7
  162. package/src/daemon/host-transfer-proxy.ts +95 -4
  163. package/src/daemon/lifecycle.ts +79 -28
  164. package/src/daemon/meet-host-supervisor.ts +4 -4
  165. package/src/daemon/meet-manifest-loader.ts +0 -1
  166. package/src/daemon/memory-v2-startup.ts +14 -4
  167. package/src/daemon/message-protocol.ts +3 -0
  168. package/src/daemon/message-types/conversations.ts +4 -0
  169. package/src/daemon/message-types/disk-pressure.ts +9 -0
  170. package/src/daemon/message-types/messages.ts +3 -0
  171. package/src/daemon/profiler-run-store.ts +5 -5
  172. package/src/daemon/tool-setup-types.ts +2 -2
  173. package/src/documents/document-store.ts +85 -0
  174. package/src/filing/filing-service.ts +30 -5
  175. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
  176. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
  177. package/src/heartbeat/heartbeat-run-store.ts +13 -0
  178. package/src/heartbeat/heartbeat-service.ts +205 -31
  179. package/src/home/feed-scheduler.ts +18 -0
  180. package/src/inbound/platform-callback-registration.ts +8 -15
  181. package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
  182. package/src/ipc/assistant-server.ts +56 -2
  183. package/src/ipc/gateway-client.ts +37 -3
  184. package/src/live-voice/live-voice-archive.ts +4 -4
  185. package/src/live-voice/protocol.ts +5 -7
  186. package/src/media/image-service.ts +1 -7
  187. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
  188. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +52 -22
  189. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
  190. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
  191. package/src/memory/admin.ts +5 -9
  192. package/src/memory/context-search/agent-runner.ts +19 -2
  193. package/src/memory/context-search/sources/conversations.ts +2 -11
  194. package/src/memory/context-search/sources/memory-v2.ts +5 -4
  195. package/src/memory/context-search/sources/memory.ts +0 -1
  196. package/src/memory/context-search/types.ts +0 -1
  197. package/src/memory/conversation-crud.ts +4 -12
  198. package/src/memory/db-init.ts +2 -0
  199. package/src/memory/embedding-runtime-manager.ts +119 -5
  200. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +32 -21
  201. package/src/memory/graph/conversation-graph-memory.ts +42 -54
  202. package/src/memory/graph/extraction.ts +1 -3
  203. package/src/memory/graph/graph-search.test.ts +10 -67
  204. package/src/memory/graph/graph-search.ts +1 -20
  205. package/src/memory/graph/retriever.test.ts +6 -0
  206. package/src/memory/graph/retriever.ts +6 -10
  207. package/src/memory/indexer.ts +54 -45
  208. package/src/memory/job-handlers/backfill.ts +2 -11
  209. package/src/memory/job-handlers/cleanup.ts +43 -0
  210. package/src/memory/job-handlers/embedding.ts +6 -8
  211. package/src/memory/job-handlers/summarization.ts +2 -7
  212. package/src/memory/jobs-store.ts +48 -0
  213. package/src/memory/jobs-worker.ts +81 -43
  214. package/src/memory/memory-v2-activation-log-store.ts +32 -14
  215. package/src/memory/memory-v2-concept-frequency.ts +169 -0
  216. package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
  217. package/src/memory/migrations/index.ts +1 -0
  218. package/src/memory/pkb/pkb-search.test.ts +6 -0
  219. package/src/memory/qdrant-client.ts +0 -13
  220. package/src/memory/rerank-local.ts +374 -0
  221. package/src/memory/search/semantic.ts +6 -67
  222. package/src/memory/trace-event-store.ts +1 -17
  223. package/src/memory/v2/__tests__/activation.test.ts +311 -250
  224. package/src/memory/v2/__tests__/consolidation-job.test.ts +40 -8
  225. package/src/memory/v2/__tests__/injection.test.ts +157 -167
  226. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
  227. package/src/memory/v2/__tests__/qdrant.test.ts +16 -0
  228. package/src/memory/v2/__tests__/reranker.test.ts +338 -0
  229. package/src/memory/v2/__tests__/sim.test.ts +5 -199
  230. package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
  231. package/src/memory/v2/__tests__/static-context.test.ts +76 -1
  232. package/src/memory/v2/activation.ts +149 -156
  233. package/src/memory/v2/consolidation-job.ts +62 -12
  234. package/src/memory/v2/injection.ts +47 -60
  235. package/src/memory/v2/prompts/consolidation.ts +36 -1
  236. package/src/memory/v2/qdrant.ts +99 -0
  237. package/src/memory/v2/reranker.ts +177 -0
  238. package/src/memory/v2/sim.ts +10 -84
  239. package/src/memory/v2/skill-content.ts +4 -3
  240. package/src/memory/v2/skill-store.ts +82 -59
  241. package/src/memory/v2/static-context.ts +22 -0
  242. package/src/memory/v2/types.ts +10 -10
  243. package/src/notifications/copy-composer.ts +13 -0
  244. package/src/notifications/signal.ts +4 -0
  245. package/src/oauth/AGENTS.md +3 -1
  246. package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
  247. package/src/oauth/connect-orchestrator.ts +2 -0
  248. package/src/oauth/connection-resolver.test.ts +66 -1
  249. package/src/oauth/connection-resolver.ts +55 -1
  250. package/src/oauth/oauth-connect-state.ts +77 -0
  251. package/src/oauth/seed-providers.ts +58 -1
  252. package/src/plugins/defaults/injectors.ts +35 -2
  253. package/src/plugins/defaults/memory-retrieval.ts +5 -6
  254. package/src/plugins/types.ts +7 -0
  255. package/src/proactive-artifact/aux-message-injector.ts +74 -0
  256. package/src/proactive-artifact/decision.test.ts +226 -0
  257. package/src/proactive-artifact/decision.ts +165 -0
  258. package/src/proactive-artifact/index.ts +7 -0
  259. package/src/proactive-artifact/job.test.ts +867 -0
  260. package/src/proactive-artifact/job.ts +352 -0
  261. package/src/proactive-artifact/message-copy.ts +41 -0
  262. package/src/proactive-artifact/trigger-state.test.ts +277 -0
  263. package/src/proactive-artifact/trigger-state.ts +119 -0
  264. package/src/prompts/normalize-onboarding.ts +80 -0
  265. package/src/prompts/persona-resolver.ts +101 -9
  266. package/src/prompts/system-prompt.ts +21 -7
  267. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  268. package/src/providers/__tests__/retry-callsite.test.ts +222 -1
  269. package/src/providers/model-intents.ts +7 -0
  270. package/src/providers/openrouter/client.ts +8 -0
  271. package/src/providers/retry.ts +50 -0
  272. package/src/providers/types.ts +1 -0
  273. package/src/runtime/__tests__/agent-wake.test.ts +456 -3
  274. package/src/runtime/agent-wake.ts +238 -100
  275. package/src/runtime/assistant-event-hub.ts +36 -6
  276. package/src/runtime/assistant-event.ts +0 -1
  277. package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
  278. package/src/runtime/auth/route-policy.ts +14 -1
  279. package/src/runtime/auth/same-actor.ts +216 -0
  280. package/src/runtime/channel-retry-sweep.ts +65 -1
  281. package/src/runtime/guardian-reply-router.ts +10 -0
  282. package/src/runtime/local-actor-identity.ts +52 -11
  283. package/src/runtime/pending-interactions.ts +8 -0
  284. package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
  285. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
  286. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  287. package/src/runtime/routes/client-routes.ts +20 -2
  288. package/src/runtime/routes/contact-routes.ts +0 -25
  289. package/src/runtime/routes/conversation-routes.ts +35 -26
  290. package/src/runtime/routes/debug-bash-routes.ts +163 -0
  291. package/src/runtime/routes/disk-pressure-routes.ts +121 -0
  292. package/src/runtime/routes/document-pdf-renderer.ts +6 -2
  293. package/src/runtime/routes/documents-routes.ts +2 -75
  294. package/src/runtime/routes/events-routes.ts +41 -9
  295. package/src/runtime/routes/host-bash-routes.ts +23 -3
  296. package/src/runtime/routes/host-cu-routes.ts +33 -6
  297. package/src/runtime/routes/host-file-routes.ts +32 -6
  298. package/src/runtime/routes/host-transfer-routes.ts +79 -16
  299. package/src/runtime/routes/identity-routes.ts +7 -138
  300. package/src/runtime/routes/inbound-message-handler.ts +77 -12
  301. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -0
  302. package/src/runtime/routes/index.ts +6 -0
  303. package/src/runtime/routes/memory-item-routes.test.ts +41 -15
  304. package/src/runtime/routes/memory-v2-routes.ts +33 -0
  305. package/src/runtime/routes/oauth-connect-routes.ts +153 -0
  306. package/src/runtime/verification-outbound-actions.ts +4 -4
  307. package/src/schedule/run-script.ts +37 -5
  308. package/src/schedule/scheduler.ts +20 -1
  309. package/src/security/encrypted-store.ts +2 -0
  310. package/src/security/secure-keys.ts +55 -0
  311. package/src/skills/remote-skill-policy.ts +4 -10
  312. package/src/subagent/index.ts +1 -7
  313. package/src/subagent/manager.ts +1 -15
  314. package/src/tasks/task-runner.ts +0 -1
  315. package/src/tasks/task-store.ts +0 -3
  316. package/src/tools/background-tool-registry.ts +17 -3
  317. package/src/tools/host-filesystem/edit.test.ts +151 -0
  318. package/src/tools/host-filesystem/edit.ts +43 -1
  319. package/src/tools/host-filesystem/read.test.ts +129 -0
  320. package/src/tools/host-filesystem/read.ts +43 -1
  321. package/src/tools/host-filesystem/transfer.test.ts +127 -2
  322. package/src/tools/host-filesystem/transfer.ts +56 -11
  323. package/src/tools/host-filesystem/write.test.ts +134 -0
  324. package/src/tools/host-filesystem/write.ts +43 -1
  325. package/src/tools/host-terminal/host-shell.ts +13 -6
  326. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  327. package/src/tools/memory/register.test.ts +12 -9
  328. package/src/tools/memory/register.ts +1 -2
  329. package/src/tools/provider-tool-name.ts +28 -0
  330. package/src/tools/registry.ts +30 -9
  331. package/src/tools/terminal/shell.ts +9 -1
  332. package/src/tools/tool-approval-handler.ts +31 -6
  333. package/src/tools/types.ts +24 -2
  334. package/src/tts/provider-catalog.ts +3 -5
  335. package/src/util/disk-usage.ts +138 -0
  336. package/src/util/platform.ts +21 -11
  337. package/src/util/process-liveness.ts +26 -0
  338. package/src/workspace/heartbeat-service.ts +19 -0
  339. package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
  340. package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
  341. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +72 -0
  342. package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
  343. package/src/workspace/migrations/registry.ts +8 -0
  344. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
  345. package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
  346. package/src/memory/v2/skill-qdrant.ts +0 -404
  347. package/src/signals/bash.ts +0 -198
@@ -153,25 +153,18 @@ describe("pre-chat onboarding contract", () => {
153
153
  const result = buildSystemPrompt({ onboardingContext: context });
154
154
  const dynamic = dynamicBlock(result);
155
155
 
156
- expect(dynamic).toContain("## Pre-chat Onboarding Context");
156
+ expect(dynamic).toContain("## First-Run User Context");
157
157
  expect(dynamic).toContain(
158
- "The user completed the native pre-chat onboarding.",
159
- );
160
- expect(dynamic).toContain('"tools"');
161
- expect(dynamic).toContain('"slack"');
162
- expect(dynamic).toContain('"linear"');
163
- expect(dynamic).toContain('"tasks"');
164
- expect(dynamic).toContain('"code-building"');
165
- expect(dynamic).toContain('"tone": "warm"');
166
- expect(dynamic).toContain('"userName": "Alex"');
167
- expect(dynamic).toContain('"assistantName": "Nova"');
168
- expect(dynamic).toContain("```json");
169
- expect(dynamic).toContain(
170
- "Use this to personalize your opener and skip redundant discovery.",
171
- );
172
- expect(dynamic).toContain(
173
- "If `assistantName` is present, it is the name the user chose for you; preserve it in IDENTITY.md.",
158
+ "The user completed setup before this conversation.",
174
159
  );
160
+ expect(dynamic).toContain("- Daily tools: Slack, Linear");
161
+ expect(dynamic).toContain("- Common work: builds code, apps, or tools");
162
+ expect(dynamic).toContain("- Name: Alex");
163
+ expect(dynamic).toContain("- Chosen assistant name: Nova");
164
+ expect(dynamic).toContain("Apply this context quietly.");
165
+
166
+ // Raw JSON must NOT be present
167
+ expect(dynamic).not.toContain("```json");
175
168
  });
176
169
 
177
170
  test("does NOT inject onboarding context when BOOTSTRAP.md does not exist", () => {
@@ -186,9 +179,9 @@ describe("pre-chat onboarding contract", () => {
186
179
  const result = buildSystemPrompt({ onboardingContext: context });
187
180
  const dynamic = dynamicBlock(result);
188
181
 
189
- expect(dynamic).not.toContain("## Pre-chat Onboarding Context");
190
- expect(dynamic).not.toContain("pre-chat onboarding");
191
- expect(dynamic).not.toContain('"tools"');
182
+ expect(dynamic).not.toContain("## First-Run User Context");
183
+ expect(dynamic).not.toContain("First-Run User Context");
184
+ expect(dynamic).not.toContain("- Daily tools:");
192
185
  });
193
186
 
194
187
  test("does NOT inject onboarding context when excludeBootstrap is true", () => {
@@ -209,7 +202,7 @@ describe("pre-chat onboarding contract", () => {
209
202
  });
210
203
  const dynamic = dynamicBlock(result);
211
204
 
212
- expect(dynamic).not.toContain("## Pre-chat Onboarding Context");
205
+ expect(dynamic).not.toContain("## First-Run User Context");
213
206
  expect(dynamic).not.toContain("First-Run Ritual");
214
207
  });
215
208
 
@@ -225,7 +218,7 @@ describe("pre-chat onboarding contract", () => {
225
218
  // Bootstrap should still be present
226
219
  expect(dynamic).toContain("First-Run Ritual");
227
220
  // But no onboarding context section
228
- expect(dynamic).not.toContain("## Pre-chat Onboarding Context");
221
+ expect(dynamic).not.toContain("## First-Run User Context");
229
222
  });
230
223
 
231
224
  test("accepts all four personality tones", () => {
@@ -247,12 +240,12 @@ describe("pre-chat onboarding contract", () => {
247
240
  const result = buildSystemPrompt({ onboardingContext: context });
248
241
  const dynamic = dynamicBlock(result);
249
242
 
250
- expect(dynamic).toContain("## Pre-chat Onboarding Context");
251
- expect(dynamic).toContain(`"tone": "${tone}"`);
243
+ expect(dynamic).toContain("## First-Run User Context");
244
+ expect(dynamic).toContain(`- Preferred initial voice: ${tone}`);
252
245
  }
253
246
  });
254
247
 
255
- test("serializes onboarding context as pretty-printed JSON", () => {
248
+ test("renders compact markdown, not JSON", () => {
256
249
  writeFileSync(
257
250
  join(TEST_DIR, "BOOTSTRAP.md"),
258
251
  "# Bootstrap\n\nOnboarding.",
@@ -269,9 +262,65 @@ describe("pre-chat onboarding contract", () => {
269
262
  const result = buildSystemPrompt({ onboardingContext: context });
270
263
  const dynamic = dynamicBlock(result);
271
264
 
272
- // Verify it contains the pretty-printed JSON (indented with 2 spaces)
265
+ // Should contain compact markdown lines
266
+ expect(dynamic).toContain("## First-Run User Context");
267
+ expect(dynamic).toContain("- Name: Jane");
268
+ expect(dynamic).toContain("- Common work: plans and coordinates work");
269
+ expect(dynamic).toContain("- Daily tools: Notion");
270
+ expect(dynamic).toContain("- Chosen assistant name: Kit");
271
+ expect(dynamic).toContain("- Preferred initial voice: warm");
272
+
273
+ // Must NOT contain JSON output
274
+ expect(dynamic).not.toContain("```json");
273
275
  const expectedJson = JSON.stringify(context, null, 2);
274
- expect(dynamic).toContain(expectedJson);
276
+ expect(dynamic).not.toContain(expectedJson);
277
+ });
278
+
279
+ test("empty tools/tasks arrays result in no Daily tools / Common work lines", () => {
280
+ writeFileSync(
281
+ join(TEST_DIR, "BOOTSTRAP.md"),
282
+ "# Bootstrap\n\nOnboarding.",
283
+ );
284
+
285
+ const context: OnboardingContext = {
286
+ tools: [],
287
+ tasks: [],
288
+ tone: "warm",
289
+ userName: "Alex",
290
+ };
291
+
292
+ const result = buildSystemPrompt({ onboardingContext: context });
293
+ const dynamic = dynamicBlock(result);
294
+
295
+ expect(dynamic).toContain("## First-Run User Context");
296
+ expect(dynamic).toContain("- Name: Alex");
297
+ expect(dynamic).not.toContain("- Daily tools:");
298
+ expect(dynamic).not.toContain("- Common work:");
299
+ });
300
+
301
+ test("absent userName results in no Name line", () => {
302
+ writeFileSync(
303
+ join(TEST_DIR, "BOOTSTRAP.md"),
304
+ "# Bootstrap\n\nOnboarding.",
305
+ );
306
+
307
+ const context: OnboardingContext = {
308
+ tools: ["slack"],
309
+ tasks: ["writing"],
310
+ tone: "warm",
311
+ };
312
+
313
+ const result = buildSystemPrompt({ onboardingContext: context });
314
+ const dynamic = dynamicBlock(result);
315
+
316
+ expect(dynamic).toContain("## First-Run User Context");
317
+ expect(dynamic).not.toContain("- Name:");
318
+ // Other fields should still be present
319
+ expect(dynamic).toContain("- Daily tools: Slack");
320
+ expect(dynamic).toContain(
321
+ "- Common work: writes docs, emails, or content",
322
+ );
323
+ expect(dynamic).toContain("- Preferred initial voice: warm");
275
324
  });
276
325
  });
277
326
 
@@ -290,4 +339,142 @@ describe("pre-chat onboarding contract", () => {
290
339
  expect(true).toBe(true); // structural acknowledgment
291
340
  });
292
341
  });
342
+
343
+ describe("end-to-end onboarding integration", () => {
344
+ test("with BOOTSTRAP.md present, onboarding context produces compact markdown with normalized labels", () => {
345
+ writeFileSync(
346
+ join(TEST_DIR, "BOOTSTRAP.md"),
347
+ "# Bootstrap\n\nWelcome to your first conversation.",
348
+ );
349
+
350
+ const context: OnboardingContext = {
351
+ tools: ["slack", "notion", "linear"],
352
+ tasks: ["code-building", "writing", "project-management"],
353
+ tone: "grounded",
354
+ userName: "Alice",
355
+ assistantName: "Pax",
356
+ };
357
+
358
+ const result = buildSystemPrompt({ onboardingContext: context });
359
+ const dynamic = dynamicBlock(result);
360
+
361
+ // Heading is present
362
+ expect(dynamic).toContain("## First-Run User Context");
363
+
364
+ // Normalized labels appear (capitalised tool names, human-readable task descriptions)
365
+ expect(dynamic).toContain("- Daily tools: Slack, Notion, Linear");
366
+ expect(dynamic).toContain("- Name: Alice");
367
+ expect(dynamic).toContain("- Chosen assistant name: Pax");
368
+ expect(dynamic).toContain("- Preferred initial voice: grounded");
369
+ // Common work descriptions are normalised from task IDs
370
+ expect(dynamic).toContain("- Common work:");
371
+
372
+ // No raw JSON anywhere in the dynamic block
373
+ expect(dynamic).not.toContain("```json");
374
+ expect(dynamic).not.toContain('"tools"');
375
+ expect(dynamic).not.toContain('"tasks"');
376
+ expect(dynamic).not.toContain('"tone"');
377
+ expect(dynamic).not.toContain('"userName"');
378
+ expect(dynamic).not.toContain('"assistantName"');
379
+ });
380
+
381
+ test("without BOOTSTRAP.md, onboarding context does NOT appear in system prompt", () => {
382
+ // No BOOTSTRAP.md created — simulates a returning user session
383
+ const context: OnboardingContext = {
384
+ tools: ["slack", "figma"],
385
+ tasks: ["design", "writing"],
386
+ tone: "warm",
387
+ userName: "Bob",
388
+ assistantName: "Kit",
389
+ };
390
+
391
+ const result = buildSystemPrompt({ onboardingContext: context });
392
+ const dynamic = dynamicBlock(result);
393
+
394
+ // Onboarding section must be absent
395
+ expect(dynamic).not.toContain("## First-Run User Context");
396
+ expect(dynamic).not.toContain("First-Run Ritual");
397
+ expect(dynamic).not.toContain("- Daily tools:");
398
+ expect(dynamic).not.toContain("- Name: Bob");
399
+ expect(dynamic).not.toContain("- Chosen assistant name:");
400
+ expect(dynamic).not.toContain("Apply this context quietly.");
401
+ });
402
+
403
+ test("excludeBootstrap suppresses both bootstrap and onboarding sections", () => {
404
+ writeFileSync(
405
+ join(TEST_DIR, "BOOTSTRAP.md"),
406
+ "# Bootstrap\n\nFirst run instructions.",
407
+ );
408
+
409
+ const context: OnboardingContext = {
410
+ tools: ["linear"],
411
+ tasks: ["code-building"],
412
+ tone: "energetic",
413
+ userName: "Charlie",
414
+ assistantName: "Nova",
415
+ };
416
+
417
+ const result = buildSystemPrompt({
418
+ onboardingContext: context,
419
+ excludeBootstrap: true,
420
+ });
421
+ const dynamic = dynamicBlock(result);
422
+
423
+ // Both bootstrap and onboarding must be suppressed
424
+ expect(dynamic).not.toContain("First-Run Ritual");
425
+ expect(dynamic).not.toContain("## First-Run User Context");
426
+ expect(dynamic).not.toContain("- Daily tools:");
427
+ expect(dynamic).not.toContain("- Name: Charlie");
428
+ expect(dynamic).not.toContain("Apply this context quietly.");
429
+ });
430
+
431
+ test("userPersona is included independently of onboarding context", () => {
432
+ // No BOOTSTRAP.md — the durable persona path after bootstrap is deleted
433
+ const personaContent =
434
+ "# User Persona\n\nPrefers concise answers. Works in fintech.";
435
+
436
+ const result = buildSystemPrompt({
437
+ userPersona: personaContent,
438
+ // No onboardingContext — simulates post-onboarding conversation
439
+ });
440
+ const dynamic = dynamicBlock(result);
441
+
442
+ // Persona content appears in prompt even without bootstrap or onboarding
443
+ expect(dynamic).toContain("# User Persona");
444
+ expect(dynamic).toContain("Prefers concise answers. Works in fintech.");
445
+
446
+ // No onboarding section should be present
447
+ expect(dynamic).not.toContain("## First-Run User Context");
448
+ expect(dynamic).not.toContain("First-Run Ritual");
449
+ });
450
+
451
+ test("userPersona appears alongside onboarding context during first run", () => {
452
+ writeFileSync(
453
+ join(TEST_DIR, "BOOTSTRAP.md"),
454
+ "# Bootstrap\n\nOnboarding flow.",
455
+ );
456
+
457
+ const personaContent =
458
+ "# User Persona\n\nEarly-stage startup founder. Likes bullet points.";
459
+ const context: OnboardingContext = {
460
+ tools: ["slack"],
461
+ tasks: ["writing"],
462
+ tone: "warm",
463
+ userName: "Dana",
464
+ };
465
+
466
+ const result = buildSystemPrompt({
467
+ userPersona: personaContent,
468
+ onboardingContext: context,
469
+ });
470
+ const dynamic = dynamicBlock(result);
471
+
472
+ // Both persona and onboarding context appear
473
+ expect(dynamic).toContain("# User Persona");
474
+ expect(dynamic).toContain("Likes bullet points.");
475
+ expect(dynamic).toContain("## First-Run User Context");
476
+ expect(dynamic).toContain("- Name: Dana");
477
+ expect(dynamic).toContain("- Daily tools: Slack");
478
+ });
479
+ });
293
480
  });
@@ -0,0 +1,23 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ isProviderSafeToolName,
5
+ toProviderSafeToolName,
6
+ } from "../tools/provider-tool-name.js";
7
+
8
+ describe("provider tool names", () => {
9
+ test("leaves already-safe names unchanged", () => {
10
+ expect(toProviderSafeToolName("deploy")).toBe("deploy");
11
+ expect(isProviderSafeToolName("deploy")).toBe(true);
12
+ });
13
+
14
+ test("preserves raw-name identity for names that differ by edge whitespace", () => {
15
+ const plain = toProviderSafeToolName("deploy");
16
+ const padded = toProviderSafeToolName(" deploy ");
17
+
18
+ expect(plain).toBe("deploy");
19
+ expect(padded).toMatch(/^deploy__[a-f0-9]{12}$/);
20
+ expect(padded).not.toBe(plain);
21
+ expect(isProviderSafeToolName(padded)).toBe(true);
22
+ });
23
+ });
@@ -362,11 +362,22 @@ function getLatestAssistantText(conversationId: string): string | null {
362
362
  if (Array.isArray(parsed)) {
363
363
  return parsed
364
364
  .filter(
365
- (block): block is { type: string; text?: string } =>
366
- typeof block === "object" && block != null,
365
+ (block): block is {
366
+ type: string;
367
+ text?: string;
368
+ surfaceType?: string;
369
+ data?: { summaryText?: string };
370
+ } => typeof block === "object" && block != null,
367
371
  )
368
- .filter((block) => block.type === "text")
369
- .map((block) => block.text ?? "")
372
+ .map((block) => {
373
+ if (block.type === "text") return block.text ?? "";
374
+ if (
375
+ block.type === "ui_surface" &&
376
+ block.surfaceType === "call_summary"
377
+ )
378
+ return block.data?.summaryText ?? "";
379
+ return "";
380
+ })
370
381
  .join("");
371
382
  }
372
383
  if (typeof parsed === "string") return parsed;
@@ -135,12 +135,10 @@ describe("SSE assistant-events endpoint", () => {
135
135
  // Read the first frame directly from the stream.
136
136
  const reader = stream.getReader();
137
137
 
138
- // The first chunk is the immediate heartbeat (comment + data event) enqueued in start().
138
+ // The first chunk is the immediate heartbeat comment enqueued in start().
139
139
  const initial = await reader.read();
140
140
  expect(initial.done).toBe(false);
141
- const initialText = new TextDecoder().decode(initial.value);
142
- expect(initialText).toContain(": heartbeat");
143
- expect(initialText).toContain('{"type":"heartbeat"}');
141
+ expect(new TextDecoder().decode(initial.value)).toBe(": heartbeat\n\n");
144
142
 
145
143
  // The second chunk is the actual assistant event.
146
144
  const { value, done } = await reader.read();
@@ -172,12 +170,10 @@ describe("SSE assistant-events endpoint", () => {
172
170
 
173
171
  const reader = stream.getReader();
174
172
 
175
- // Consume the initial heartbeat (comment + data event).
173
+ // Consume the initial heartbeat.
176
174
  const heartbeat = await reader.read();
177
175
  expect(heartbeat.done).toBe(false);
178
- const heartbeatText = new TextDecoder().decode(heartbeat.value);
179
- expect(heartbeatText).toContain(": heartbeat");
180
- expect(heartbeatText).toContain('{"type":"heartbeat"}');
176
+ expect(new TextDecoder().decode(heartbeat.value)).toBe(": heartbeat\n\n");
181
177
 
182
178
  // Publish events with two different conversationIds.
183
179
  const eventA = buildAssistantEvent({ type: "pong" }, "conversation-aaa");
@@ -0,0 +1,148 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ mock.module("../util/logger.js", () => ({
4
+ getLogger: () => ({
5
+ info: () => {},
6
+ debug: () => {},
7
+ warn: () => {},
8
+ error: () => {},
9
+ }),
10
+ truncateForLog: (value: string) => value,
11
+ }));
12
+
13
+ mock.module("../runtime/agent-wake.js", () => ({
14
+ wakeAgentForOpportunity: mock(() =>
15
+ Promise.resolve({ invoked: true, producedToolCalls: false }),
16
+ ),
17
+ }));
18
+
19
+ mock.module("../home/emit-feed-event.js", () => ({
20
+ emitFeedEvent: mock(() => Promise.resolve()),
21
+ }));
22
+
23
+ mock.module("../config/loader.js", () => ({
24
+ getConfig: () => ({}),
25
+ loadConfig: () => ({}),
26
+ loadRawConfig: () => ({}),
27
+ saveRawConfig: () => {},
28
+ getConfigReadOnly: () => ({}),
29
+ applyNestedDefaults: (config: unknown) => config,
30
+ deepMergeOverwrite: (base: unknown) => base,
31
+ mergeDefaultWorkspaceConfig: () => {},
32
+ getNestedValue: () => undefined,
33
+ setNestedValue: () => {},
34
+ API_KEY_PROVIDERS: [],
35
+ _appendQuarantineBulletin: () => {},
36
+ invalidateConfigCache: () => {},
37
+ }));
38
+
39
+ let locked = true;
40
+ mock.module("../daemon/disk-pressure-background-gate.js", () => ({
41
+ checkDiskPressureBackgroundGate: () =>
42
+ locked
43
+ ? {
44
+ action: "skip",
45
+ reason: "disk_pressure",
46
+ blockedCapability: "background-work",
47
+ status: {
48
+ enabled: true,
49
+ state: "critical",
50
+ locked: true,
51
+ acknowledged: true,
52
+ overrideActive: false,
53
+ effectivelyLocked: true,
54
+ lockId: "disk-pressure-test",
55
+ usagePercent: 98,
56
+ thresholdPercent: 95,
57
+ path: "/",
58
+ lastCheckedAt: "2026-05-05T00:00:00.000Z",
59
+ blockedCapabilities: [
60
+ "agent-turns",
61
+ "background-work",
62
+ "remote-ingress",
63
+ ],
64
+ error: null,
65
+ },
66
+ }
67
+ : {
68
+ action: "allow",
69
+ status: {
70
+ enabled: false,
71
+ state: "disabled",
72
+ locked: false,
73
+ acknowledged: false,
74
+ overrideActive: false,
75
+ effectivelyLocked: false,
76
+ lockId: null,
77
+ usagePercent: null,
78
+ thresholdPercent: 95,
79
+ path: null,
80
+ lastCheckedAt: null,
81
+ blockedCapabilities: [],
82
+ error: null,
83
+ },
84
+ },
85
+ diskPressureBackgroundSkipLogFields: () => ({
86
+ reason: "disk_pressure",
87
+ thresholdPercent: 95,
88
+ usagePercent: 98,
89
+ blockedCapability: "background-work",
90
+ lockId: "disk-pressure-test",
91
+ path: "/",
92
+ }),
93
+ shouldLogDiskPressureBackgroundSkip: () => true,
94
+ }));
95
+
96
+ import { getDb } from "../memory/db-connection.js";
97
+ import { initializeDb } from "../memory/db-init.js";
98
+ import { createSchedule } from "../schedule/schedule-store.js";
99
+ import { runScheduleOnce } from "../schedule/scheduler.js";
100
+
101
+ initializeDb();
102
+
103
+ function rawDb(): import("bun:sqlite").Database {
104
+ return (getDb() as unknown as { $client: import("bun:sqlite").Database })
105
+ .$client;
106
+ }
107
+
108
+ describe("scheduler disk pressure gate", () => {
109
+ beforeEach(() => {
110
+ locked = true;
111
+ const db = getDb();
112
+ db.run("DELETE FROM cron_runs");
113
+ db.run("DELETE FROM cron_jobs");
114
+ db.run("DELETE FROM task_runs");
115
+ db.run("DELETE FROM tasks");
116
+ db.run("DELETE FROM messages");
117
+ db.run("DELETE FROM conversations");
118
+ });
119
+
120
+ test("skips before claiming due schedules while disk pressure is locked", async () => {
121
+ const dueAt = Date.now() - 10_000;
122
+ const schedule = createSchedule({
123
+ name: "Due reminder",
124
+ message: "Do not fire while locked",
125
+ mode: "notify",
126
+ nextRunAt: dueAt,
127
+ });
128
+
129
+ const processMessage = mock(() => Promise.resolve());
130
+ const notify = mock(() => Promise.resolve());
131
+
132
+ const processed = await runScheduleOnce(processMessage, notify);
133
+
134
+ expect(processed).toBe(0);
135
+ expect(processMessage).not.toHaveBeenCalled();
136
+ expect(notify).not.toHaveBeenCalled();
137
+
138
+ const row = rawDb()
139
+ .query("SELECT status, next_run_at FROM cron_jobs WHERE id = ?")
140
+ .get(schedule.id) as { status: string; next_run_at: number } | null;
141
+ expect(row).toEqual({ status: "active", next_run_at: dueAt });
142
+
143
+ const runCount = rawDb()
144
+ .query("SELECT COUNT(*) AS count FROM cron_runs")
145
+ .get() as { count: number };
146
+ expect(runCount.count).toBe(0);
147
+ });
148
+ });
@@ -106,7 +106,6 @@ mock.module("../memory/conversation-crud.js", () => ({
106
106
  provenanceFromTrustContext: () => undefined,
107
107
  setConversationOriginChannelIfUnset: () => {},
108
108
  setConversationOriginInterfaceIfUnset: () => {},
109
- getConversationMemoryScopeId: () => undefined,
110
109
  }));
111
110
 
112
111
  mock.module("../runtime/local-actor-identity.js", () => ({
@@ -561,6 +561,52 @@ describe("GET /v1/suggestion", () => {
561
561
  expect(options?.config?.callSite).toBe("conversationStarters");
562
562
  });
563
563
 
564
+ test("disables thinking and zeros effort to avoid Anthropic temp/thinking 400", async () => {
565
+ // Regression guard: this call hardcodes `temperature: 0.7` for response
566
+ // variety. Anthropic 400s on `temperature` ≠ 1 whenever thinking is
567
+ // enabled or in adaptive mode, so any user profile that resolves
568
+ // thinking-enabled (Opus 4.x at `effort: high|xhigh`, etc.) would fail
569
+ // unless we explicitly opt out of thinking on this call site.
570
+ //
571
+ // Pinning `thinking: { type: "disabled" }` and `effort: "none"` ensures
572
+ // the call works on every profile shape. A 60-token reply chip doesn't
573
+ // benefit from extended thinking anyway.
574
+ const provider = makeMockProvider("Quick reply");
575
+ mockGetConfiguredProvider.mockImplementation(async () => provider);
576
+ mockGetConversationByKey.mockImplementation(() => ({
577
+ conversationId: "conv-test",
578
+ }));
579
+ mockGetMessages.mockImplementation(() => [
580
+ {
581
+ id: "msg-asst-thinking",
582
+ conversationId: "conv-test",
583
+ role: "assistant",
584
+ content: JSON.stringify([{ type: "text", text: "Hello!" }]),
585
+ createdAt: Date.now(),
586
+ metadata: null,
587
+ },
588
+ ]);
589
+
590
+ const args = makeArgs({ conversationKey: "test-key" });
591
+ const deps = makeDeps();
592
+ await handleGetSuggestion(args, deps);
593
+
594
+ expect(provider.sendMessage).toHaveBeenCalledTimes(1);
595
+ const callArgs = provider.sendMessage.mock.calls[0] as unknown[];
596
+ const options = callArgs[3] as
597
+ | {
598
+ config?: {
599
+ temperature?: number;
600
+ thinking?: { type?: string };
601
+ effort?: string;
602
+ };
603
+ }
604
+ | undefined;
605
+ expect(options?.config?.temperature).toBe(0.7);
606
+ expect(options?.config?.thinking).toEqual({ type: "disabled" });
607
+ expect(options?.config?.effort).toBe("none");
608
+ });
609
+
564
610
  test("does not send an assistant-role prefill message", async () => {
565
611
  // Regression guard: Anthropic rejects assistant-message prefill
566
612
  // whenever the request triggers extended thinking (e.g. Opus 4.x at
@@ -49,7 +49,7 @@ describe("Twilio validation middleware", () => {
49
49
  };
50
50
  });
51
51
 
52
- test("validates signatures against Twilio-specific public ingress first", async () => {
52
+ test("validates signatures against configured public ingress", async () => {
53
53
  mockConfig = {
54
54
  ingress: {
55
55
  publicBaseUrl: " https://twilio.example.com/// ",
@@ -77,7 +77,7 @@ describe("Twilio validation middleware", () => {
77
77
  ]);
78
78
  });
79
79
 
80
- test("falls back to generic public ingress when Twilio-specific ingress is empty", async () => {
80
+ test("uses configured public ingress for status callbacks", async () => {
81
81
  const req = new Request("http://127.0.0.1:7821/v1/calls/twilio/status", {
82
82
  method: "POST",
83
83
  headers: { "x-twilio-signature": "valid" },