@vellumai/assistant 0.10.2 → 0.10.3-dev.202606252046.9075fd5

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 (546) hide show
  1. package/bun.lock +20 -0
  2. package/docs/workspace-tools.md +42 -33
  3. package/eslint-rules/cli-no-daemon-internals.js +6 -0
  4. package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
  5. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
  6. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +44 -0
  7. package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
  8. package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
  9. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
  10. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  11. package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +28 -0
  12. package/node_modules/@vellumai/service-contracts/src/channels.ts +41 -0
  13. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  14. package/openapi.yaml +196 -56
  15. package/package.json +4 -1
  16. package/scripts/test.sh +36 -15
  17. package/src/__tests__/actor-token-service.test.ts +36 -14
  18. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
  19. package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
  20. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  21. package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
  22. package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
  23. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  24. package/src/__tests__/approval-cascade.test.ts +2 -0
  25. package/src/__tests__/assistant-attachments.test.ts +42 -0
  26. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
  27. package/src/__tests__/btw-routes.test.ts +2 -0
  28. package/src/__tests__/build-persisted-content.test.ts +2 -0
  29. package/src/__tests__/call-controller.test.ts +19 -0
  30. package/src/__tests__/channel-approval-routes.test.ts +21 -26
  31. package/src/__tests__/channel-delivery-store.test.ts +28 -0
  32. package/src/__tests__/channel-guardian.test.ts +164 -78
  33. package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
  34. package/src/__tests__/channel-reply-delivery.test.ts +2 -0
  35. package/src/__tests__/code-search-tool.test.ts +585 -0
  36. package/src/__tests__/compaction-events.test.ts +2 -0
  37. package/src/__tests__/compaction-ledger-store.test.ts +128 -0
  38. package/src/__tests__/compaction.benchmark.test.ts +2 -0
  39. package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
  40. package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
  41. package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
  42. package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
  43. package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
  44. package/src/__tests__/config-loader-backfill.test.ts +123 -10
  45. package/src/__tests__/config-schema.test.ts +1 -0
  46. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
  47. package/src/__tests__/consult-deadline.test.ts +60 -0
  48. package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
  49. package/src/__tests__/contact-store-user-file.test.ts +7 -10
  50. package/src/__tests__/contacts-relay-reads.test.ts +19 -24
  51. package/src/__tests__/contacts-write.test.ts +0 -2
  52. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  53. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
  54. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  55. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
  56. package/src/__tests__/conversation-agent-loop.test.ts +134 -0
  57. package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
  58. package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
  59. package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
  61. package/src/__tests__/conversation-fork-crud.test.ts +354 -24
  62. package/src/__tests__/conversation-history-web-search.test.ts +2 -0
  63. package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
  64. package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
  65. package/src/__tests__/conversation-pairing.test.ts +2 -0
  66. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  67. package/src/__tests__/conversation-process-callsite.test.ts +2 -0
  68. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  69. package/src/__tests__/conversation-queue.test.ts +91 -0
  70. package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
  72. package/src/__tests__/conversation-slash-queue.test.ts +2 -0
  73. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  74. package/src/__tests__/conversation-speed-override.test.ts +2 -0
  75. package/src/__tests__/conversation-surfaces-task-progress.test.ts +29 -0
  76. package/src/__tests__/conversation-title-service.test.ts +2 -0
  77. package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
  78. package/src/__tests__/conversation-usage.test.ts +2 -0
  79. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
  80. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  81. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  82. package/src/__tests__/credential-security-invariants.test.ts +1 -1
  83. package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
  84. package/src/__tests__/db-migration-rollback.test.ts +205 -171
  85. package/src/__tests__/db-test-helpers.ts +5 -4
  86. package/src/__tests__/delete-propagation.test.ts +5 -3
  87. package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
  88. package/src/__tests__/disk-pressure-guard.test.ts +41 -0
  89. package/src/__tests__/dm-backfill.test.ts +6 -4
  90. package/src/__tests__/dm-persistence.test.ts +2 -0
  91. package/src/__tests__/emit-signal-routing-intent.test.ts +7 -6
  92. package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
  93. package/src/__tests__/exploration-drift-hook.test.ts +3 -2
  94. package/src/__tests__/filing-service.test.ts +2 -0
  95. package/src/__tests__/guardian-binding-drift-heal.test.ts +104 -19
  96. package/src/__tests__/guardian-dispatch.test.ts +140 -1
  97. package/src/__tests__/guardian-expiry-notifier.test.ts +282 -0
  98. package/src/__tests__/guardian-outbound-http.test.ts +13 -0
  99. package/src/__tests__/guardian-routing-state.test.ts +6 -10
  100. package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
  101. package/src/__tests__/heartbeat-service.test.ts +2 -0
  102. package/src/__tests__/helpers/channel-test-adapter.ts +46 -19
  103. package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
  104. package/src/__tests__/helpers/mock-logger.ts +1 -0
  105. package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
  106. package/src/__tests__/host-app-control-routes.test.ts +24 -30
  107. package/src/__tests__/host-bash-routes.test.ts +31 -41
  108. package/src/__tests__/host-browser-routes.test.ts +26 -32
  109. package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
  110. package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
  111. package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
  112. package/src/__tests__/http-user-message-parity.test.ts +290 -8
  113. package/src/__tests__/inbound-invite-redemption.test.ts +115 -10
  114. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  115. package/src/__tests__/invite-redemption-service.test.ts +471 -53
  116. package/src/__tests__/invite-routes-http.test.ts +34 -0
  117. package/src/__tests__/invite-service-ipc.test.ts +65 -2
  118. package/src/__tests__/llm-context-normalization.test.ts +105 -0
  119. package/src/__tests__/llm-request-log-error-payload.test.ts +71 -9
  120. package/src/__tests__/llm-usage-store.test.ts +25 -0
  121. package/src/__tests__/mcp-config-secret-boundary.test.ts +392 -0
  122. package/src/__tests__/mcp-health-check.test.ts +2 -1
  123. package/src/__tests__/media-stream-server-integration.test.ts +127 -0
  124. package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
  125. package/src/__tests__/messaging-send-tool.test.ts +2 -0
  126. package/src/__tests__/migration-import-from-url.test.ts +2 -2
  127. package/src/__tests__/mtime-cache.test.ts +275 -5
  128. package/src/__tests__/native-web-search.test.ts +2 -0
  129. package/src/__tests__/non-member-access-request.test.ts +191 -17
  130. package/src/__tests__/notification-broadcaster.test.ts +4 -0
  131. package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
  132. package/src/__tests__/notification-deep-link.test.ts +6 -0
  133. package/src/__tests__/notification-guardian-path.test.ts +19 -0
  134. package/src/__tests__/onboarding-persona-write.test.ts +52 -22
  135. package/src/__tests__/openai-provider.test.ts +22 -12
  136. package/src/__tests__/openai-responses-provider.test.ts +12 -2
  137. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  138. package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
  139. package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
  140. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  141. package/src/__tests__/persona-resolver.test.ts +75 -45
  142. package/src/__tests__/plugin-bootstrap.test.ts +21 -83
  143. package/src/__tests__/plugin-disabled-state.test.ts +190 -0
  144. package/src/__tests__/plugin-pipeline.test.ts +96 -0
  145. package/src/__tests__/plugin-route-contribution.test.ts +6 -19
  146. package/src/__tests__/plugin-tool-contribution.test.ts +5 -20
  147. package/src/__tests__/plugin-types.test.ts +2 -4
  148. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  149. package/src/__tests__/process-message-display-content.test.ts +2 -0
  150. package/src/__tests__/provider-error-scenarios.test.ts +5 -4
  151. package/src/__tests__/provider-usage-tracking.test.ts +39 -0
  152. package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
  153. package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
  154. package/src/__tests__/reaction-persistence.test.ts +51 -4
  155. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
  156. package/src/__tests__/registry.test.ts +4 -1
  157. package/src/__tests__/relay-server.test.ts +758 -32
  158. package/src/__tests__/runtime-attachment-metadata.test.ts +9 -12
  159. package/src/__tests__/secret-ingress-http.test.ts +14 -0
  160. package/src/__tests__/send-endpoint-busy.test.ts +30 -8
  161. package/src/__tests__/settings-routes.test.ts +32 -0
  162. package/src/__tests__/skills.test.ts +44 -0
  163. package/src/__tests__/slack-inbound-verification.test.ts +47 -2
  164. package/src/__tests__/sse-actor-principal-guardian-source.test.ts +79 -0
  165. package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
  166. package/src/__tests__/stt-hints.test.ts +49 -15
  167. package/src/__tests__/subagent-detail.test.ts +27 -0
  168. package/src/__tests__/subagent-disposal.test.ts +65 -0
  169. package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
  170. package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
  171. package/src/__tests__/subagent-notify-parent.test.ts +2 -0
  172. package/src/__tests__/subagent-role-registry.test.ts +24 -6
  173. package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
  174. package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
  175. package/src/__tests__/subagent-tools.test.ts +398 -1
  176. package/src/__tests__/suggestion-routes.test.ts +2 -0
  177. package/src/__tests__/thread-backfill.test.ts +3 -3
  178. package/src/__tests__/title-generate-hook.test.ts +2 -0
  179. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
  180. package/src/__tests__/tool-executor.test.ts +16 -11
  181. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  182. package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
  183. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  184. package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
  185. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +12 -12
  186. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
  187. package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
  188. package/src/__tests__/trusted-contact-verification.test.ts +79 -54
  189. package/src/__tests__/twilio-routes.test.ts +96 -0
  190. package/src/__tests__/ui-file-upload-surface.test.ts +86 -0
  191. package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
  192. package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
  193. package/src/__tests__/voice-invite-redemption.test.ts +216 -20
  194. package/src/__tests__/web-search-backend-failure.test.ts +2 -0
  195. package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
  196. package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +208 -0
  197. package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
  198. package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
  199. package/src/__tests__/workspace-migration-remove-hooks.test.ts +14 -35
  200. package/src/__tests__/workspace-tool-loader.test.ts +195 -2
  201. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
  202. package/src/agent/loop-exclusive-tool.test.ts +154 -0
  203. package/src/agent/loop-native-web-search.test.ts +200 -0
  204. package/src/agent/loop.ts +164 -1
  205. package/src/api/constants/sse-replay.ts +41 -0
  206. package/src/api/events/conversation-notice.ts +26 -0
  207. package/src/api/index.ts +19 -1
  208. package/src/api/responses/llm-request-log-entry.ts +29 -0
  209. package/src/api/responses/subagent-detail.ts +17 -0
  210. package/src/api/surfaces.ts +39 -3
  211. package/src/approvals/guardian-channel-delivery.ts +30 -0
  212. package/src/approvals/guardian-expiry-notifier.ts +148 -0
  213. package/src/approvals/guardian-request-resolvers.ts +17 -15
  214. package/src/calls/__tests__/relay-setup-router.test.ts +267 -17
  215. package/src/calls/call-domain.ts +3 -3
  216. package/src/calls/guardian-dispatch.ts +14 -9
  217. package/src/calls/inbound-trust-reader.ts +24 -2
  218. package/src/calls/media-stream-server.ts +21 -0
  219. package/src/calls/relay-access-wait.ts +6 -6
  220. package/src/calls/relay-server.ts +188 -51
  221. package/src/calls/relay-setup-router.ts +47 -17
  222. package/src/calls/relay-verification.ts +4 -4
  223. package/src/calls/stt-hints.ts +9 -12
  224. package/src/calls/twilio-routes.ts +14 -4
  225. package/src/channels/types.ts +10 -20
  226. package/src/cli/commands/__tests__/cache.test.ts +8 -1
  227. package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
  228. package/src/cli/commands/cache.ts +194 -181
  229. package/src/cli/commands/contacts.ts +10 -7
  230. package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
  231. package/src/cli/commands/db/status.ts +37 -1
  232. package/src/cli/commands/mcp.ts +252 -218
  233. package/src/cli/commands/memory/__tests__/worker.test.ts +432 -0
  234. package/src/cli/commands/memory/index.ts +2 -0
  235. package/src/cli/commands/memory/worker.ts +242 -0
  236. package/src/cli/commands/plugins.ts +125 -13
  237. package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
  238. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
  239. package/src/cli/lib/__tests__/publish-plugin.test.ts +404 -0
  240. package/src/cli/lib/list-installed-plugins.ts +179 -1
  241. package/src/cli/lib/publish-plugin.ts +633 -0
  242. package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
  243. package/src/config/__tests__/sync-gated-profiles.test.ts +16 -10
  244. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
  245. package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
  246. package/src/config/bundled-skills/subagent/SKILL.md +17 -2
  247. package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
  248. package/src/config/call-site-defaults.ts +0 -6
  249. package/src/config/feature-flag-registry.json +5 -13
  250. package/src/config/llm-resolver.ts +0 -3
  251. package/src/config/loader.ts +38 -5
  252. package/src/config/prune-seeded-callsite-defaults.ts +110 -0
  253. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  254. package/src/config/schemas/call-site-catalog.ts +0 -7
  255. package/src/config/schemas/heartbeat.ts +2 -5
  256. package/src/config/schemas/llm.ts +3 -12
  257. package/src/config/schemas/memory-lifecycle.ts +12 -0
  258. package/src/config/schemas/memory-v3.ts +7 -0
  259. package/src/config/schemas/memory.ts +4 -0
  260. package/src/config/schemas/timeouts.ts +8 -0
  261. package/src/config/seed-inference-profiles.ts +30 -34
  262. package/src/config/skills.ts +27 -5
  263. package/src/config/sync-gated-profiles.ts +12 -16
  264. package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +128 -0
  265. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
  266. package/src/contacts/__tests__/member-write-relay.test.ts +226 -0
  267. package/src/contacts/contact-store.ts +27 -237
  268. package/src/contacts/contacts-write.ts +18 -55
  269. package/src/contacts/gateway-channel-read.ts +51 -0
  270. package/src/contacts/guardian-delivery-reader.ts +223 -0
  271. package/src/contacts/member-write-relay.ts +183 -0
  272. package/src/contacts/types.ts +3 -15
  273. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +35 -0
  274. package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
  275. package/src/daemon/assistant-attachments.ts +27 -4
  276. package/src/daemon/conversation-agent-loop.ts +28 -0
  277. package/src/daemon/conversation-notices.ts +60 -0
  278. package/src/daemon/conversation-process.ts +35 -16
  279. package/src/daemon/conversation-surfaces.ts +111 -38
  280. package/src/daemon/conversation-tool-setup.ts +71 -30
  281. package/src/daemon/conversation.ts +23 -1
  282. package/src/daemon/disk-pressure-guard.ts +12 -2
  283. package/src/daemon/event-loop-watchdog.ts +28 -1
  284. package/src/daemon/external-plugins-bootstrap.ts +17 -41
  285. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
  286. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
  287. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -2
  288. package/src/daemon/handlers/__tests__/config-channels.test.ts +220 -0
  289. package/src/daemon/handlers/config-a2a.ts +6 -14
  290. package/src/daemon/handlers/config-channels.ts +67 -26
  291. package/src/daemon/handlers/conversations.ts +77 -0
  292. package/src/daemon/lifecycle.ts +16 -0
  293. package/src/daemon/mcp-reload-service.ts +10 -0
  294. package/src/daemon/memory-v2-startup.test.ts +72 -0
  295. package/src/daemon/memory-v2-startup.ts +87 -19
  296. package/src/daemon/message-types/conversations.ts +2 -0
  297. package/src/daemon/message-types/surfaces.ts +12 -12
  298. package/src/daemon/server.ts +0 -4
  299. package/src/daemon/shutdown-handlers.ts +20 -0
  300. package/src/daemon/tool-setup-types.ts +9 -0
  301. package/src/heartbeat/heartbeat-service.ts +5 -0
  302. package/src/home/relationship-state-writer.ts +5 -0
  303. package/src/hooks/hook-loader.ts +341 -0
  304. package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
  305. package/src/ipc/assistant-server.ts +2 -2
  306. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -0
  307. package/src/mcp/client.ts +15 -1
  308. package/src/mcp/mcp-auth-orchestrator.ts +6 -1
  309. package/src/mcp/mcp-header-store.ts +134 -0
  310. package/src/mcp/mcp-oauth-provider.ts +19 -8
  311. package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
  312. package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
  313. package/src/memory/__tests__/prompt-override.test.ts +192 -0
  314. package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
  315. package/src/memory/compaction-ledger-store.ts +107 -0
  316. package/src/memory/conversation-crud.ts +129 -61
  317. package/src/memory/db-connection.ts +22 -3
  318. package/src/memory/db-init.ts +37 -503
  319. package/src/memory/db-singleton.ts +6 -4
  320. package/src/memory/jobs-worker.ts +104 -0
  321. package/src/memory/llm-request-log-store.ts +26 -1
  322. package/src/memory/llm-usage-store.ts +48 -20
  323. package/src/memory/memory-retrospective-job.ts +14 -8
  324. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +152 -56
  325. package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
  326. package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
  327. package/src/memory/migrations/302-create-compaction-events.ts +107 -0
  328. package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +297 -0
  329. package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
  330. package/src/memory/migrations/run-migrations.ts +106 -10
  331. package/src/memory/migrations/validate-migration-state.ts +105 -67
  332. package/src/memory/prompt-override.ts +129 -0
  333. package/src/memory/schema/contacts.ts +6 -2
  334. package/src/memory/schema/conversations.ts +37 -0
  335. package/src/memory/schema/infrastructure.ts +20 -0
  336. package/src/memory/steps.ts +1294 -0
  337. package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
  338. package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
  339. package/src/memory/v2/cli-command-store.ts +75 -38
  340. package/src/memory/v2/prompts/consolidation.ts +13 -82
  341. package/src/memory/v2/prompts/router.ts +21 -93
  342. package/src/memory/v2/skill-store.ts +68 -31
  343. package/src/memory/watchdog-events-store.ts +87 -0
  344. package/src/memory/worker-control.ts +204 -0
  345. package/src/memory/worker-process.ts +89 -0
  346. package/src/messaging/channel-binding-metadata.ts +31 -0
  347. package/src/messaging/provider-types.ts +8 -0
  348. package/src/messaging/providers/slack/binding-metadata.ts +60 -0
  349. package/src/notifications/__tests__/broadcaster.test.ts +8 -8
  350. package/src/notifications/__tests__/connected-channels.test.ts +86 -0
  351. package/src/notifications/__tests__/decision-engine.test.ts +78 -9
  352. package/src/notifications/__tests__/destination-resolver.test.ts +151 -0
  353. package/src/notifications/broadcaster.ts +8 -1
  354. package/src/notifications/decision-engine.ts +15 -7
  355. package/src/notifications/destination-resolver.ts +53 -25
  356. package/src/notifications/emit-signal.ts +34 -15
  357. package/src/onboarding/checkin-event.test.ts +222 -0
  358. package/src/onboarding/checkin-event.ts +321 -0
  359. package/src/onboarding/schedule-checkin.ts +190 -0
  360. package/src/permissions/question-prompter.test.ts +1 -1
  361. package/src/permissions/question-prompter.ts +7 -4
  362. package/src/plugin-api/index.ts +12 -12
  363. package/src/plugin-api/types.ts +12 -14
  364. package/src/plugin-api/vision-support.test.ts +28 -4
  365. package/src/plugin-api/vision-support.ts +66 -31
  366. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +2 -2
  367. package/src/plugins/defaults/empty-response/hooks/stop.ts +2 -2
  368. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +4 -3
  369. package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +2 -2
  370. package/src/plugins/defaults/history-repair/hooks/stop.ts +2 -2
  371. package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +2 -2
  372. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
  373. package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +12 -13
  374. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +14 -22
  375. package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
  376. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +2 -2
  377. package/src/plugins/defaults/image-recovery/hooks/stop.ts +2 -2
  378. package/src/plugins/defaults/index.ts +0 -35
  379. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +2 -2
  380. package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +2 -2
  381. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +2 -2
  382. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  383. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +33 -3
  384. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +48 -4
  385. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +4 -8
  386. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +2 -2
  387. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +2 -2
  388. package/src/plugins/defaults/memory-v3-shadow/injector.ts +43 -15
  389. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
  390. package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
  391. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +77 -13
  392. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +12 -11
  393. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +2 -2
  394. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +2 -2
  395. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -2
  396. package/src/plugins/defaults/title-generate/hooks/stop.ts +2 -2
  397. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +2 -2
  398. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +2 -2
  399. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +2 -2
  400. package/src/plugins/disabled-state.ts +31 -0
  401. package/src/plugins/external-plugin-loader.ts +2 -2
  402. package/src/plugins/mtime-cache.ts +186 -330
  403. package/src/plugins/pipeline.ts +111 -13
  404. package/src/plugins/registry.ts +59 -16
  405. package/src/plugins/surface-import.ts +121 -0
  406. package/src/plugins/types.ts +7 -9
  407. package/src/prompts/persona-resolver.ts +43 -11
  408. package/src/providers/anthropic/client.ts +5 -0
  409. package/src/providers/call-site-routing.ts +45 -0
  410. package/src/providers/model-catalog.ts +16 -0
  411. package/src/providers/openai/__tests__/api-error-normalization.test.ts +321 -0
  412. package/src/providers/openai/api-error-normalization.ts +270 -0
  413. package/src/providers/openai/chat-completions-provider.ts +37 -83
  414. package/src/providers/openai/responses-provider.ts +50 -46
  415. package/src/providers/openrouter/client.ts +5 -0
  416. package/src/providers/provider-send-message.ts +10 -0
  417. package/src/providers/ratelimit.ts +10 -0
  418. package/src/providers/registry.ts +1 -1
  419. package/src/providers/retry.ts +10 -0
  420. package/src/providers/types.ts +22 -0
  421. package/src/providers/usage-tracking.ts +10 -0
  422. package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
  423. package/src/runtime/__tests__/guardian-vellum-migration.test.ts +184 -0
  424. package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
  425. package/src/runtime/__tests__/local-principal-trust.test.ts +162 -0
  426. package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
  427. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +402 -123
  428. package/src/runtime/access-request-helper.ts +18 -39
  429. package/src/runtime/actor-trust-resolver.ts +46 -19
  430. package/src/runtime/anchored-guardian.test.ts +109 -0
  431. package/src/runtime/anchored-guardian.ts +86 -0
  432. package/src/runtime/assistant-event-hub.ts +1 -1
  433. package/src/runtime/assistant-stream-state.ts +9 -2
  434. package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
  435. package/src/runtime/auth/require-bound-guardian.ts +21 -11
  436. package/src/runtime/channel-verification-service.ts +56 -31
  437. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
  438. package/src/runtime/guardian-vellum-migration.ts +69 -8
  439. package/src/runtime/invite-redemption-service.ts +213 -187
  440. package/src/runtime/local-actor-identity.test.ts +108 -0
  441. package/src/runtime/local-actor-identity.ts +85 -13
  442. package/src/runtime/local-principal-trust.ts +52 -0
  443. package/src/runtime/member-verdict-cache.ts +0 -0
  444. package/src/runtime/pending-interactions.ts +11 -1
  445. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
  446. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  447. package/src/runtime/routes/__tests__/contact-routes.test.ts +305 -0
  448. package/src/runtime/routes/__tests__/global-search-routes.test.ts +92 -0
  449. package/src/runtime/routes/__tests__/http-adapter-actor-header.test.ts +129 -0
  450. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +188 -0
  451. package/src/runtime/routes/browser-routes.ts +1 -1
  452. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +13 -5
  453. package/src/runtime/routes/channel-verification-routes.ts +3 -3
  454. package/src/runtime/routes/contact-routes.ts +48 -57
  455. package/src/runtime/routes/conversation-cli-routes.ts +4 -5
  456. package/src/runtime/routes/conversation-list-routes.ts +4 -7
  457. package/src/runtime/routes/conversation-query-routes.ts +72 -0
  458. package/src/runtime/routes/conversation-routes.ts +84 -85
  459. package/src/runtime/routes/events-routes.ts +2 -2
  460. package/src/runtime/routes/global-search-routes.ts +3 -1
  461. package/src/runtime/routes/guardian-action-routes.ts +4 -5
  462. package/src/runtime/routes/host-app-control-routes.ts +5 -4
  463. package/src/runtime/routes/host-bash-routes.ts +5 -4
  464. package/src/runtime/routes/host-browser-routes.ts +9 -11
  465. package/src/runtime/routes/host-cu-routes.ts +5 -4
  466. package/src/runtime/routes/host-file-routes.ts +5 -4
  467. package/src/runtime/routes/host-transfer-routes.ts +6 -6
  468. package/src/runtime/routes/http-adapter.ts +16 -1
  469. package/src/runtime/routes/identity-routes.ts +3 -2
  470. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  471. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
  472. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -59
  473. package/src/runtime/routes/inbound-stages/background-dispatch.ts +13 -5
  474. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
  475. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
  476. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
  477. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
  478. package/src/runtime/routes/index.ts +2 -0
  479. package/src/runtime/routes/llm-context-normalization.ts +83 -0
  480. package/src/runtime/routes/mcp-auth-routes.ts +171 -19
  481. package/src/runtime/routes/migration-rollback-routes.ts +4 -3
  482. package/src/runtime/routes/migration-routes.ts +4 -1
  483. package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
  484. package/src/runtime/routes/settings-routes.ts +8 -3
  485. package/src/runtime/routes/subagents-routes.ts +5 -0
  486. package/src/runtime/routes/surface-action-routes.ts +42 -56
  487. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  488. package/src/runtime/services/conversation-serializer.ts +13 -58
  489. package/src/runtime/tool-grant-request-helper.ts +3 -3
  490. package/src/runtime/trust-verdict-consumer.ts +111 -40
  491. package/src/runtime/verification-outbound-actions.ts +18 -18
  492. package/src/signals/user-message.ts +16 -0
  493. package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
  494. package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
  495. package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +22 -32
  496. package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
  497. package/src/subagent/index.ts +1 -1
  498. package/src/subagent/manager.ts +254 -33
  499. package/src/subagent/types.ts +11 -4
  500. package/src/telemetry/types.ts +34 -1
  501. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
  502. package/src/telemetry/usage-telemetry-reporter.ts +87 -3
  503. package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
  504. package/src/tools/ask-question/ask-question-tool.ts +13 -0
  505. package/src/tools/executor.ts +4 -4
  506. package/src/tools/filesystem/search.ts +543 -0
  507. package/src/tools/registry.ts +38 -0
  508. package/src/tools/shared/filesystem/path-policy.ts +12 -5
  509. package/src/tools/subagent/consult-deadline.ts +49 -0
  510. package/src/tools/subagent/spawn.ts +234 -5
  511. package/src/tools/tool-approval-handler.ts +1 -1
  512. package/src/tools/tool-defaults.ts +9 -2
  513. package/src/tools/tool-manifest.ts +3 -0
  514. package/src/tools/types.ts +131 -25
  515. package/src/tools/workspace-tools/loader.ts +348 -244
  516. package/src/util/errors.ts +26 -1
  517. package/src/util/logger.ts +9 -0
  518. package/src/util/platform.ts +19 -0
  519. package/src/util/telemetry-db-path.ts +24 -0
  520. package/src/workflows/library.test.ts +140 -0
  521. package/src/workflows/library.ts +82 -28
  522. package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
  523. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
  524. package/src/workspace/migrations/031-drop-user-md.ts +232 -148
  525. package/src/workspace/migrations/048-remove-workspace-hooks.ts +14 -66
  526. package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +134 -0
  527. package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
  528. package/src/workspace/migrations/registry.ts +4 -0
  529. package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
  530. package/src/daemon/workspace-tools-watcher.ts +0 -328
  531. package/src/memory/migrations/registry.ts +0 -573
  532. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
  533. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
  534. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
  535. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -153
  536. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
  537. package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
  538. package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
  539. package/src/plugins/defaults/advisor/config.ts +0 -21
  540. package/src/plugins/defaults/advisor/consult.ts +0 -93
  541. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
  542. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
  543. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
  544. package/src/plugins/defaults/advisor/package.json +0 -14
  545. package/src/plugins/defaults/advisor/tools/advisor.ts +0 -65
  546. package/src/providers/openai/__tests__/api-error-detail.test.ts +0 -120
@@ -0,0 +1,128 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import { eq } from "drizzle-orm";
4
+
5
+ import { makeMockLogger } from "./helpers/mock-logger.js";
6
+
7
+ mock.module("../util/logger.js", () => ({
8
+ getLogger: () => makeMockLogger(),
9
+ }));
10
+
11
+ import {
12
+ appendCompactionEvent,
13
+ forkCompactionLedger,
14
+ getLatestCompactionEventAtOrBefore,
15
+ } from "../memory/compaction-ledger-store.js";
16
+ import { getDb } from "../memory/db-connection.js";
17
+ import { initializeDb } from "../memory/db-init.js";
18
+ import {
19
+ conversationCompactionEvents,
20
+ conversations,
21
+ } from "../memory/schema.js";
22
+
23
+ await initializeDb();
24
+
25
+ function reset(): void {
26
+ const db = getDb();
27
+ db.delete(conversationCompactionEvents).run();
28
+ db.delete(conversations).run();
29
+ }
30
+
31
+ function makeConversation(id: string): void {
32
+ const now = Date.now();
33
+ getDb()
34
+ .insert(conversations)
35
+ .values({ id, title: id, createdAt: now, updatedAt: now })
36
+ .run();
37
+ }
38
+
39
+ function forkEventsFor(conversationId: string) {
40
+ return getDb()
41
+ .select()
42
+ .from(conversationCompactionEvents)
43
+ .where(eq(conversationCompactionEvents.conversationId, conversationId))
44
+ .all();
45
+ }
46
+
47
+ describe("compaction-ledger-store", () => {
48
+ beforeEach(reset);
49
+
50
+ test("getLatestCompactionEventAtOrBefore returns the newest event at-or-before the cutoff", () => {
51
+ makeConversation("conv");
52
+ appendCompactionEvent("conv", {
53
+ compactedAt: 100,
54
+ summary: "s100",
55
+ compactedMessageCount: 1,
56
+ });
57
+ appendCompactionEvent("conv", {
58
+ compactedAt: 200,
59
+ summary: "s200",
60
+ compactedMessageCount: 3,
61
+ });
62
+ appendCompactionEvent("conv", {
63
+ compactedAt: 300,
64
+ summary: "s300",
65
+ compactedMessageCount: 5,
66
+ });
67
+
68
+ expect(getLatestCompactionEventAtOrBefore("conv", 50)).toBeNull();
69
+ expect(
70
+ getLatestCompactionEventAtOrBefore("conv", 100)?.compactedMessageCount,
71
+ ).toBe(1);
72
+ expect(
73
+ getLatestCompactionEventAtOrBefore("conv", 250)?.compactedMessageCount,
74
+ ).toBe(3);
75
+ expect(
76
+ getLatestCompactionEventAtOrBefore("conv", 999)?.compactedMessageCount,
77
+ ).toBe(5);
78
+ expect(getLatestCompactionEventAtOrBefore("conv", null)).toBeNull();
79
+ });
80
+
81
+ test("getLatestCompactionEventAtOrBefore is scoped per conversation", () => {
82
+ makeConversation("a");
83
+ makeConversation("b");
84
+ appendCompactionEvent("a", {
85
+ compactedAt: 100,
86
+ summary: "a",
87
+ compactedMessageCount: 1,
88
+ });
89
+
90
+ expect(getLatestCompactionEventAtOrBefore("b", 999)).toBeNull();
91
+ });
92
+
93
+ test("forkCompactionLedger copies only events at-or-before the boundary", () => {
94
+ makeConversation("src");
95
+ makeConversation("fork");
96
+ appendCompactionEvent("src", {
97
+ compactedAt: 100,
98
+ summary: "s100",
99
+ compactedMessageCount: 1,
100
+ });
101
+ appendCompactionEvent("src", {
102
+ compactedAt: 300,
103
+ summary: "s300",
104
+ compactedMessageCount: 5,
105
+ });
106
+
107
+ forkCompactionLedger(getDb(), "src", "fork", 200);
108
+
109
+ const copied = forkEventsFor("fork");
110
+ expect(copied).toHaveLength(1);
111
+ expect(copied[0]?.compactedAt).toBe(100);
112
+ expect(copied[0]?.summary).toBe("s100");
113
+ });
114
+
115
+ test("forkCompactionLedger with a null boundary copies nothing", () => {
116
+ makeConversation("src");
117
+ makeConversation("fork");
118
+ appendCompactionEvent("src", {
119
+ compactedAt: 100,
120
+ summary: "s100",
121
+ compactedMessageCount: 1,
122
+ });
123
+
124
+ forkCompactionLedger(getDb(), "src", "fork", null);
125
+
126
+ expect(forkEventsFor("fork")).toHaveLength(0);
127
+ });
128
+ });
@@ -17,6 +17,8 @@ mock.module("../util/logger.js", () => ({
17
17
  // The compactor reads the conversation's image attachments from the DB to
18
18
  // build its manifest; with no images these return empty.
19
19
  mock.module("../memory/conversation-crud.js", () => ({
20
+ setConversationProcessingStartedAt: () => {},
21
+ isConversationProcessing: () => false,
20
22
  getMessages: () => [],
21
23
  }));
22
24
  mock.module("../memory/attachments-store.js", () => ({
@@ -30,6 +30,8 @@ mock.module("../util/logger.js", () => ({
30
30
  }));
31
31
 
32
32
  mock.module("../memory/conversation-crud.js", () => ({
33
+ setConversationProcessingStartedAt: () => {},
34
+ isConversationProcessing: () => false,
33
35
  getMessages: () => [],
34
36
  reserveMessage: mock(async () => ({ id: "msg-reserve" })),
35
37
  }));
@@ -32,6 +32,8 @@ mock.module("../util/logger.js", () => ({
32
32
  }));
33
33
 
34
34
  mock.module("../memory/conversation-crud.js", () => ({
35
+ setConversationProcessingStartedAt: () => {},
36
+ isConversationProcessing: () => false,
35
37
  getMessages: () => [],
36
38
  }));
37
39
 
@@ -22,6 +22,8 @@ mock.module("../util/logger.js", () => ({
22
22
  }));
23
23
 
24
24
  mock.module("../memory/conversation-crud.js", () => ({
25
+ setConversationProcessingStartedAt: () => {},
26
+ isConversationProcessing: () => false,
25
27
  getMessages: () => [],
26
28
  reserveMessage: mock(async () => ({ id: "msg-reserve" })),
27
29
  }));
@@ -32,6 +32,8 @@ mock.module("../util/logger.js", () => ({
32
32
  }));
33
33
 
34
34
  mock.module("../memory/conversation-crud.js", () => ({
35
+ setConversationProcessingStartedAt: () => {},
36
+ isConversationProcessing: () => false,
35
37
  getMessages: () => [],
36
38
  }));
37
39
 
@@ -22,6 +22,8 @@ mock.module("../util/logger.js", () => ({
22
22
  }));
23
23
 
24
24
  mock.module("../memory/conversation-crud.js", () => ({
25
+ setConversationProcessingStartedAt: () => {},
26
+ isConversationProcessing: () => false,
25
27
  getMessages: () => [],
26
28
  reserveMessage: mock(async () => ({ id: "msg-reserve" })),
27
29
  }));
@@ -98,6 +98,64 @@ function writeConfig(obj: unknown): void {
98
98
  writeFileSync(CONFIG_PATH, JSON.stringify(obj, null, 2) + "\n");
99
99
  }
100
100
 
101
+ function latencySeed(): Record<string, unknown> {
102
+ return {
103
+ model: "claude-haiku-4-5-20251001",
104
+ effort: "low",
105
+ thinking: { enabled: false },
106
+ };
107
+ }
108
+
109
+ function fullSeededCallSites(): Record<string, Record<string, unknown>> {
110
+ return {
111
+ guardianQuestionCopy: latencySeed(),
112
+ interactionClassifier: latencySeed(),
113
+ skillCategoryInference: latencySeed(),
114
+ inviteInstructionGenerator: latencySeed(),
115
+ notificationDecision: latencySeed(),
116
+ preferenceExtraction: latencySeed(),
117
+ commitMessage: {
118
+ model: "claude-haiku-4-5-20251001",
119
+ maxTokens: 120,
120
+ temperature: 0.2,
121
+ effort: "low",
122
+ thinking: { enabled: false },
123
+ },
124
+ conversationStarters: latencySeed(),
125
+ conversationSummarization: {
126
+ model: "claude-opus-4-7",
127
+ effort: "low",
128
+ thinking: { enabled: false },
129
+ },
130
+ recall: {
131
+ profile: "cost-optimized",
132
+ maxTokens: 4096,
133
+ effort: "low",
134
+ thinking: { enabled: false, streamThinking: false },
135
+ temperature: 0,
136
+ disableCache: true,
137
+ },
138
+ heartbeatAgent: {
139
+ profile: "cost-optimized",
140
+ maxTokens: 2048,
141
+ effort: "low",
142
+ temperature: 0,
143
+ thinking: { enabled: false, streamThinking: false },
144
+ contextWindow: { maxInputTokens: 16000 },
145
+ },
146
+ replySuggestion: {
147
+ model: "claude-haiku-4-5-20251001",
148
+ effort: "low",
149
+ thinking: { enabled: false },
150
+ disableCache: true,
151
+ },
152
+ memoryRouter: {
153
+ profile: "cost-optimized",
154
+ contextWindow: { maxInputTokens: 1_000_000 },
155
+ },
156
+ };
157
+ }
158
+
101
159
  function mergeDefaultConfigAndSeedInferenceProfiles(db?: DrizzleDb): void {
102
160
  const defaultConfigMerge = mergeDefaultWorkspaceConfig();
103
161
  seedInferenceProfiles({
@@ -372,6 +430,56 @@ describe("loadConfig startup behavior", () => {
372
430
  expect(config.memory.v2.bm25_b).toBe(0.4);
373
431
  });
374
432
 
433
+ test("default workspace config merge prunes exact seeded call-site defaults", () => {
434
+ const seededCallSites = fullSeededCallSites();
435
+ const overlayPath = join(WORKSPACE_DIR, "hatch-overlay.json");
436
+ writeFileSync(
437
+ overlayPath,
438
+ JSON.stringify(
439
+ {
440
+ gateway: {
441
+ unmappedPolicy: "default",
442
+ defaultAssistantId: "self",
443
+ },
444
+ llm: {
445
+ activeProfile: "balanced",
446
+ advisorProfile: "frontier",
447
+ callSites: {
448
+ ...seededCallSites,
449
+ recall: {
450
+ ...seededCallSites.recall,
451
+ disableCache: false,
452
+ },
453
+ customSite: { profile: "frontier" },
454
+ },
455
+ },
456
+ },
457
+ null,
458
+ 2,
459
+ ) + "\n",
460
+ );
461
+ process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH = overlayPath;
462
+
463
+ const result = mergeDefaultWorkspaceConfig();
464
+ const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8")) as Record<
465
+ string,
466
+ Record<string, unknown>
467
+ >;
468
+ const llm = raw.llm as Record<string, unknown>;
469
+ const callSites = llm.callSites as Record<string, Record<string, unknown>>;
470
+
471
+ expect(result.hadOverlay).toBe(true);
472
+ expect(raw.gateway).toEqual({
473
+ unmappedPolicy: "default",
474
+ defaultAssistantId: "self",
475
+ });
476
+ expect(llm.activeProfile).toBe("balanced");
477
+ expect(llm.advisorProfile).toBe("frontier");
478
+ expect(Object.keys(callSites).sort()).toEqual(["customSite", "recall"]);
479
+ expect(callSites.recall?.disableCache).toBe(false);
480
+ expect(callSites.customSite).toEqual({ profile: "frontier" });
481
+ });
482
+
375
483
  test("reloads cached config when config.json is updated externally", () => {
376
484
  // Models a CLI subprocess writing twilio.accountSid while the assistant
377
485
  // process already has an effective config cached in memory.
@@ -745,7 +853,11 @@ describe("loadConfig startup behavior", () => {
745
853
  );
746
854
  expect(raw.llm.profiles.frontier.provider).toBe("anthropic");
747
855
  expect(raw.llm.profiles.frontier.model).toBe("claude-opus-4-8");
748
- expect(raw.llm.profiles["cost-optimized"].provider).toBe("anthropic");
856
+ // Speed is served by DeepSeek V4 Flash on Fireworks.
857
+ expect(raw.llm.profiles["cost-optimized"].provider).toBe("fireworks");
858
+ expect(raw.llm.profiles["cost-optimized"].model).toBe(
859
+ "accounts/fireworks/models/deepseek-v4-flash",
860
+ );
749
861
  });
750
862
 
751
863
  test("off-platform managed profiles are overwritten on every boot", () => {
@@ -1591,8 +1703,8 @@ describe("seedInferenceProfiles BYOK-mode managed profile labels", () => {
1591
1703
  // ---------------------------------------------------------------------------
1592
1704
  // Tests: OS Beta flag-gated managed profile. The template is defined but
1593
1705
  // intentionally NOT part of MANAGED_PROFILE_TEMPLATES, so seedInferenceProfiles
1594
- // must never create it. A later PR reconciles it in/out based on the `os-beta`
1595
- // feature flag.
1706
+ // must never create it. The flag-gated reconcile creates or removes it based on
1707
+ // the `os-beta` feature flag.
1596
1708
  // ---------------------------------------------------------------------------
1597
1709
 
1598
1710
  describe("OS Beta managed profile template", () => {
@@ -1639,20 +1751,21 @@ describe("OS Beta managed profile template", () => {
1639
1751
  expect(MANAGED_PROFILE_NAMES.has("os-beta")).toBe(true);
1640
1752
  });
1641
1753
 
1642
- test("materializeProfile honors the explicit OS Beta model", () => {
1754
+ test("materializeProfile resolves OS Beta to the Balanced model with low effort", () => {
1643
1755
  const entry = materializeProfile(
1644
1756
  OS_BETA_PROFILE_TEMPLATE,
1645
- "fireworks",
1646
- "fireworks-managed",
1757
+ "together",
1758
+ "together-managed",
1647
1759
  );
1648
1760
 
1649
- expect(entry.model).toBe("accounts/fireworks/models/glm-5p2");
1650
- expect(entry.provider_connection).toBe("fireworks-managed");
1651
- expect(entry.provider).toBe("fireworks");
1761
+ expect(entry.model).toBe("MiniMaxAI/MiniMax-M3");
1762
+ expect(entry.provider_connection).toBe("together-managed");
1763
+ expect(entry.provider).toBe("together");
1652
1764
  expect(entry.label).toBe("OS Beta");
1653
1765
  expect(entry.source).toBe("managed");
1654
1766
  expect(entry.maxTokens).toBe(32000);
1655
- expect(entry.effort).toBe("high");
1767
+ expect(entry.effort).toBe("low");
1656
1768
  expect(entry.thinking?.enabled).toBe(true);
1769
+ expect(entry.topP).toBe(0.95);
1657
1770
  });
1658
1771
  });
@@ -120,6 +120,7 @@ describe("AssistantConfigSchema", () => {
120
120
  shellDefaultTimeoutSec: 120,
121
121
  shellMaxTimeoutSec: 600,
122
122
  permissionTimeoutSec: 300,
123
+ questionResponseTimeoutSec: 1800,
123
124
  toolExecutionTimeoutSec: 120,
124
125
  providerStreamTimeoutSec: 1800,
125
126
  backgroundTurnTimeoutSec: 1800,
@@ -135,11 +135,11 @@ describe("bridgeConfirmationRequestToGuardian", () => {
135
135
  mockOnConversationCreatedCallbacks.length = 0;
136
136
  });
137
137
 
138
- test("emits guardian.question for trusted-contact sessions", () => {
138
+ test("emits guardian.question for trusted-contact sessions", async () => {
139
139
  const canonicalRequest = makeCanonicalRequest();
140
140
  const trustContext = makeTrustedContactContext();
141
141
 
142
- const result = bridgeConfirmationRequestToGuardian({
142
+ const result = await bridgeConfirmationRequestToGuardian({
143
143
  canonicalRequest,
144
144
  trustContext,
145
145
  conversationId: "conv-1",
@@ -160,7 +160,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
160
160
  expect(payload.requesterIdentifier).toBe("@requester");
161
161
  });
162
162
 
163
- test("skips guardian actor sessions (self-approve)", () => {
163
+ test("skips guardian actor sessions (self-approve)", async () => {
164
164
  const canonicalRequest = makeCanonicalRequest();
165
165
  const trustContext: TrustContext = {
166
166
  sourceChannel: "telegram",
@@ -168,7 +168,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
168
168
  guardianExternalUserId: "guardian-1",
169
169
  };
170
170
 
171
- const result = bridgeConfirmationRequestToGuardian({
171
+ const result = await bridgeConfirmationRequestToGuardian({
172
172
  canonicalRequest,
173
173
  trustContext,
174
174
  conversationId: "conv-1",
@@ -182,14 +182,14 @@ describe("bridgeConfirmationRequestToGuardian", () => {
182
182
  expect(emittedSignals).toHaveLength(0);
183
183
  });
184
184
 
185
- test("skips unknown actor sessions", () => {
185
+ test("skips unknown actor sessions", async () => {
186
186
  const canonicalRequest = makeCanonicalRequest();
187
187
  const trustContext: TrustContext = {
188
188
  sourceChannel: "telegram",
189
189
  trustClass: "unknown",
190
190
  };
191
191
 
192
- const result = bridgeConfirmationRequestToGuardian({
192
+ const result = await bridgeConfirmationRequestToGuardian({
193
193
  canonicalRequest,
194
194
  trustContext,
195
195
  conversationId: "conv-1",
@@ -203,13 +203,13 @@ describe("bridgeConfirmationRequestToGuardian", () => {
203
203
  expect(emittedSignals).toHaveLength(0);
204
204
  });
205
205
 
206
- test("skips when guardian identity is missing", () => {
206
+ test("skips when guardian identity is missing", async () => {
207
207
  const canonicalRequest = makeCanonicalRequest();
208
208
  const trustContext = makeTrustedContactContext({
209
209
  guardianExternalUserId: undefined,
210
210
  });
211
211
 
212
- const result = bridgeConfirmationRequestToGuardian({
212
+ const result = await bridgeConfirmationRequestToGuardian({
213
213
  canonicalRequest,
214
214
  trustContext,
215
215
  conversationId: "conv-1",
@@ -223,13 +223,13 @@ describe("bridgeConfirmationRequestToGuardian", () => {
223
223
  expect(emittedSignals).toHaveLength(0);
224
224
  });
225
225
 
226
- test("skips when no guardian binding exists for channel", () => {
226
+ test("skips when no guardian binding exists for channel", async () => {
227
227
  const canonicalRequest = makeCanonicalRequest({ sourceChannel: "phone" });
228
228
  const trustContext = makeTrustedContactContext({
229
229
  sourceChannel: "phone",
230
230
  });
231
231
 
232
- const result = bridgeConfirmationRequestToGuardian({
232
+ const result = await bridgeConfirmationRequestToGuardian({
233
233
  canonicalRequest,
234
234
  trustContext,
235
235
  conversationId: "conv-1",
@@ -243,11 +243,11 @@ describe("bridgeConfirmationRequestToGuardian", () => {
243
243
  expect(emittedSignals).toHaveLength(0);
244
244
  });
245
245
 
246
- test("sets correct attention hints for urgency", () => {
246
+ test("sets correct attention hints for urgency", async () => {
247
247
  const canonicalRequest = makeCanonicalRequest();
248
248
  const trustContext = makeTrustedContactContext();
249
249
 
250
- bridgeConfirmationRequestToGuardian({
250
+ await bridgeConfirmationRequestToGuardian({
251
251
  canonicalRequest,
252
252
  trustContext,
253
253
  conversationId: "conv-1",
@@ -261,11 +261,11 @@ describe("bridgeConfirmationRequestToGuardian", () => {
261
261
  expect(hints.visibleInSourceNow).toBe(false);
262
262
  });
263
263
 
264
- test("uses dedupe key scoped to canonical request ID", () => {
264
+ test("uses dedupe key scoped to canonical request ID", async () => {
265
265
  const canonicalRequest = makeCanonicalRequest();
266
266
  const trustContext = makeTrustedContactContext();
267
267
 
268
- bridgeConfirmationRequestToGuardian({
268
+ await bridgeConfirmationRequestToGuardian({
269
269
  canonicalRequest,
270
270
  trustContext,
271
271
  conversationId: "conv-1",
@@ -277,11 +277,11 @@ describe("bridgeConfirmationRequestToGuardian", () => {
277
277
  );
278
278
  });
279
279
 
280
- test("creates vellum delivery row via onConversationCreated callback", () => {
280
+ test("creates vellum delivery row via onConversationCreated callback", async () => {
281
281
  const canonicalRequest = makeCanonicalRequest();
282
282
  const trustContext = makeTrustedContactContext();
283
283
 
284
- bridgeConfirmationRequestToGuardian({
284
+ await bridgeConfirmationRequestToGuardian({
285
285
  canonicalRequest,
286
286
  trustContext,
287
287
  conversationId: "conv-1",
@@ -298,18 +298,20 @@ describe("bridgeConfirmationRequestToGuardian", () => {
298
298
  });
299
299
 
300
300
  const deliveries = listCanonicalGuardianDeliveries(canonicalRequest.id);
301
- expect(deliveries).toHaveLength(1);
302
- expect(deliveries[0].destinationChannel).toBe("vellum");
303
- expect(deliveries[0].destinationConversationId).toBe(
301
+ const vellumDelivery = deliveries.find(
302
+ (d) => d.destinationChannel === "vellum",
303
+ );
304
+ expect(vellumDelivery).toBeDefined();
305
+ expect(vellumDelivery?.destinationConversationId).toBe(
304
306
  "guardian-conversation-1",
305
307
  );
306
308
  });
307
309
 
308
- test("uses custom assistantId when provided", () => {
310
+ test("uses custom assistantId when provided", async () => {
309
311
  const canonicalRequest = makeCanonicalRequest();
310
312
  const trustContext = makeTrustedContactContext();
311
313
 
312
- bridgeConfirmationRequestToGuardian({
314
+ await bridgeConfirmationRequestToGuardian({
313
315
  canonicalRequest,
314
316
  trustContext,
315
317
  conversationId: "conv-1",
@@ -324,13 +326,13 @@ describe("bridgeConfirmationRequestToGuardian", () => {
324
326
  expect(emittedSignals).toHaveLength(0);
325
327
  });
326
328
 
327
- test("does not pass assistantId to notification signal", () => {
329
+ test("does not pass assistantId to notification signal", async () => {
328
330
  const canonicalRequest = makeCanonicalRequest();
329
331
  const trustContext = makeTrustedContactContext();
330
332
 
331
333
  // assistantId is used internally for guardian binding lookup but is no
332
334
  // longer forwarded to the notification signal after the assistantId removal refactor.
333
- bridgeConfirmationRequestToGuardian({
335
+ await bridgeConfirmationRequestToGuardian({
334
336
  canonicalRequest,
335
337
  trustContext,
336
338
  conversationId: "conv-1",
@@ -340,13 +342,13 @@ describe("bridgeConfirmationRequestToGuardian", () => {
340
342
  expect(emittedSignals[0].assistantId).toBeUndefined();
341
343
  });
342
344
 
343
- test("includes requesterChatId as null when not provided", () => {
345
+ test("includes requesterChatId as null when not provided", async () => {
344
346
  const canonicalRequest = makeCanonicalRequest();
345
347
  const trustContext = makeTrustedContactContext({
346
348
  requesterChatId: undefined,
347
349
  });
348
350
 
349
- bridgeConfirmationRequestToGuardian({
351
+ await bridgeConfirmationRequestToGuardian({
350
352
  canonicalRequest,
351
353
  trustContext,
352
354
  conversationId: "conv-1",
@@ -357,7 +359,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
357
359
  expect(payload.requesterChatId).toBeNull();
358
360
  });
359
361
 
360
- test("skips when binding guardian identity does not match canonical request guardian", () => {
362
+ test("skips when binding guardian identity does not match canonical request guardian", async () => {
361
363
  // Create a canonical request where guardianExternalUserId differs from the
362
364
  // binding's guardianExternalUserId ('guardian-1' in the mock).
363
365
  const canonicalRequest = makeCanonicalRequest({
@@ -365,7 +367,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
365
367
  });
366
368
  const trustContext = makeTrustedContactContext();
367
369
 
368
- const result = bridgeConfirmationRequestToGuardian({
370
+ const result = await bridgeConfirmationRequestToGuardian({
369
371
  canonicalRequest,
370
372
  trustContext,
371
373
  conversationId: "conv-1",
@@ -379,7 +381,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
379
381
  expect(emittedSignals).toHaveLength(0);
380
382
  });
381
383
 
382
- test("does not skip when canonical request guardian identity is null", () => {
384
+ test("does not skip when canonical request guardian identity is null", async () => {
383
385
  // When guardianExternalUserId is null on the canonical request (e.g. desktop
384
386
  // flow), the identity check should be skipped and the bridge should proceed.
385
387
  const canonicalRequest = makeCanonicalRequest({
@@ -387,7 +389,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
387
389
  });
388
390
  const trustContext = makeTrustedContactContext();
389
391
 
390
- const result = bridgeConfirmationRequestToGuardian({
392
+ const result = await bridgeConfirmationRequestToGuardian({
391
393
  canonicalRequest,
392
394
  trustContext,
393
395
  conversationId: "conv-1",
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Tests for `createConsultDeadline` — the advisor consult's progress-aware
3
+ * timeout. Uses short real timers with generous (>=3x) margins so a streamed
4
+ * chunk reliably lands inside the idle window without timing flakiness.
5
+ */
6
+ import { describe, expect, test } from "bun:test";
7
+
8
+ import { createConsultDeadline } from "../tools/subagent/consult-deadline.js";
9
+
10
+ const delay = (ms: number): Promise<void> =>
11
+ new Promise((resolve) => setTimeout(resolve, ms));
12
+
13
+ describe("createConsultDeadline", () => {
14
+ test("streamed progress keeps the consult alive past the idle window", async () => {
15
+ // Record progress every 25ms against a 150ms idle window (6x margin). Each
16
+ // chunk resets the window, so it must never abort over ~200ms of streaming.
17
+ const d = createConsultDeadline({ idleMs: 150, maxMs: 10_000 });
18
+ try {
19
+ for (let i = 0; i < 8; i++) {
20
+ await delay(25);
21
+ expect(d.signal.aborted).toBe(false);
22
+ d.recordProgress();
23
+ }
24
+ } finally {
25
+ d.dispose();
26
+ }
27
+ });
28
+
29
+ test("aborts after the idle window once progress stops", async () => {
30
+ const d = createConsultDeadline({ idleMs: 50, maxMs: 10_000 });
31
+ try {
32
+ expect(d.signal.aborted).toBe(false);
33
+ await delay(250); // 5x the idle window with no progress
34
+ expect(d.signal.aborted).toBe(true);
35
+ } finally {
36
+ d.dispose();
37
+ }
38
+ });
39
+
40
+ test("aborts at the absolute max even under steady progress", async () => {
41
+ // Idle window can't fire (huge); the short max must, despite steady chunks.
42
+ const d = createConsultDeadline({ idleMs: 10_000, maxMs: 70 });
43
+ try {
44
+ for (let i = 0; i < 8; i++) {
45
+ await delay(25);
46
+ d.recordProgress();
47
+ }
48
+ expect(d.signal.aborted).toBe(true); // ~200ms elapsed > 70ms max
49
+ } finally {
50
+ d.dispose();
51
+ }
52
+ });
53
+
54
+ test("dispose() cancels both timers — no late abort", async () => {
55
+ const d = createConsultDeadline({ idleMs: 40, maxMs: 40 });
56
+ d.dispose();
57
+ await delay(150); // well past both windows
58
+ expect(d.signal.aborted).toBe(false);
59
+ });
60
+ });