@vellumai/assistant 0.8.5 → 0.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (544) hide show
  1. package/AGENTS.md +33 -1
  2. package/ARCHITECTURE.md +1 -1
  3. package/bunfig.toml +6 -1
  4. package/docs/credential-execution-service.md +6 -6
  5. package/docs/plugins.md +4 -3
  6. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
  7. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
  8. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
  9. package/openapi.yaml +1900 -166
  10. package/package.json +1 -1
  11. package/src/__tests__/actor-token-service.test.ts +3 -2
  12. package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
  13. package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
  14. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  15. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  16. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  17. package/src/__tests__/annotate-risk-options.test.ts +1 -0
  18. package/src/__tests__/approval-cascade.test.ts +1 -0
  19. package/src/__tests__/approval-routes-http.test.ts +9 -13
  20. package/src/__tests__/assert-not-live-db.ts +79 -0
  21. package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
  22. package/src/__tests__/audit-log-rotation.test.ts +2 -2
  23. package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
  24. package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
  25. package/src/__tests__/browser-skill-endstate.test.ts +3 -3
  26. package/src/__tests__/btw-routes.test.ts +3 -2
  27. package/src/__tests__/call-controller.test.ts +3 -2
  28. package/src/__tests__/channel-approval-routes.test.ts +3 -2
  29. package/src/__tests__/channel-guardian.test.ts +3 -2
  30. package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
  31. package/src/__tests__/channel-reply-delivery.test.ts +35 -0
  32. package/src/__tests__/channel-retry-sweep.test.ts +320 -3
  33. package/src/__tests__/checker.test.ts +12 -12
  34. package/src/__tests__/compaction-events.test.ts +1 -0
  35. package/src/__tests__/compaction-trail-store.test.ts +264 -0
  36. package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
  37. package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
  38. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
  39. package/src/__tests__/computer-use-tools.test.ts +12 -14
  40. package/src/__tests__/config-loader-backfill.test.ts +13 -28
  41. package/src/__tests__/config-loader-corrupt.test.ts +5 -5
  42. package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
  43. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
  44. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
  45. package/src/__tests__/config-schema.test.ts +10 -10
  46. package/src/__tests__/connection-model-compat.test.ts +83 -0
  47. package/src/__tests__/contacts-tools.test.ts +3 -2
  48. package/src/__tests__/context-token-estimator.test.ts +22 -0
  49. package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
  50. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
  51. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
  52. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  53. package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
  54. package/src/__tests__/conversation-agent-loop.test.ts +488 -2
  55. package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
  56. package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
  57. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
  58. package/src/__tests__/conversation-attention-store.test.ts +101 -0
  59. package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  61. package/src/__tests__/conversation-error.test.ts +30 -0
  62. package/src/__tests__/conversation-fork-crud.test.ts +69 -8
  63. package/src/__tests__/conversation-fork-route.test.ts +3 -2
  64. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  65. package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
  66. package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
  67. package/src/__tests__/conversation-lifecycle.test.ts +1 -0
  68. package/src/__tests__/conversation-list-source.test.ts +3 -2
  69. package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
  70. package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
  71. package/src/__tests__/conversation-pairing.test.ts +53 -0
  72. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
  73. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  74. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
  75. package/src/__tests__/conversation-queue.test.ts +333 -291
  76. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
  77. package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
  78. package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
  79. package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
  80. package/src/__tests__/conversation-skill-tools.test.ts +38 -142
  81. package/src/__tests__/conversation-slash-queue.test.ts +84 -32
  82. package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
  83. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  84. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
  85. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
  90. package/src/__tests__/conversation-sync-tags.test.ts +128 -12
  91. package/src/__tests__/conversation-title-service.test.ts +1 -0
  92. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
  93. package/src/__tests__/conversation-usage.test.ts +1 -0
  94. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  95. package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
  96. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
  97. package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
  98. package/src/__tests__/credential-broker-server-use.test.ts +5 -5
  99. package/src/__tests__/credential-execution-client.test.ts +72 -1
  100. package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
  101. package/src/__tests__/credential-health-service.test.ts +252 -3
  102. package/src/__tests__/credential-security-invariants.test.ts +5 -5
  103. package/src/__tests__/credential-vault-unit.test.ts +19 -19
  104. package/src/__tests__/credential-vault.test.ts +5 -5
  105. package/src/__tests__/cross-provider-web-search.test.ts +56 -2
  106. package/src/__tests__/db-connection-isolation.test.ts +7 -6
  107. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
  108. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
  109. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
  110. package/src/__tests__/db-test-helpers.ts +58 -0
  111. package/src/__tests__/disk-pressure-guard.test.ts +58 -41
  112. package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
  113. package/src/__tests__/disk-pressure-routes.test.ts +0 -33
  114. package/src/__tests__/disk-pressure-tools.test.ts +0 -4
  115. package/src/__tests__/dm-persistence.test.ts +26 -40
  116. package/src/__tests__/document-create-dedupe.test.ts +189 -0
  117. package/src/__tests__/document-find-replace.test.ts +3 -2
  118. package/src/__tests__/document-tool-security.test.ts +81 -2
  119. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
  120. package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
  121. package/src/__tests__/encrypted-store.test.ts +11 -9
  122. package/src/__tests__/feature-flag-test-helpers.ts +53 -0
  123. package/src/__tests__/filing-service.test.ts +1 -0
  124. package/src/__tests__/first-greeting.test.ts +62 -12
  125. package/src/__tests__/gateway-flag-listener.test.ts +0 -1
  126. package/src/__tests__/gemini-provider.test.ts +26 -0
  127. package/src/__tests__/guardian-action-sweep.test.ts +3 -2
  128. package/src/__tests__/guardian-outbound-http.test.ts +3 -2
  129. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
  130. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
  131. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  132. package/src/__tests__/heartbeat-service.test.ts +1 -0
  133. package/src/__tests__/helpers/mock-logger.ts +26 -0
  134. package/src/__tests__/host-bash-routes.test.ts +1 -0
  135. package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
  136. package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
  137. package/src/__tests__/host-shell-tool.test.ts +5 -4
  138. package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
  139. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  140. package/src/__tests__/http-user-message-parity.test.ts +29 -7
  141. package/src/__tests__/identity-intro-cache.test.ts +133 -22
  142. package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
  143. package/src/__tests__/inference-profile-reaper.test.ts +3 -2
  144. package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
  145. package/src/__tests__/injector-disk-pressure.test.ts +3 -17
  146. package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
  147. package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
  148. package/src/__tests__/llm-context-normalization.test.ts +42 -0
  149. package/src/__tests__/llm-resolver.test.ts +331 -0
  150. package/src/__tests__/llm-schema.test.ts +1 -1
  151. package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
  152. package/src/__tests__/mcp-abort-signal.test.ts +14 -0
  153. package/src/__tests__/mcp-client-auth.test.ts +14 -0
  154. package/src/__tests__/messaging-send-tool.test.ts +1 -0
  155. package/src/__tests__/migration-import-from-url.test.ts +3 -3
  156. package/src/__tests__/mock-gateway-ipc.ts +18 -2
  157. package/src/__tests__/model-intents.test.ts +3 -3
  158. package/src/__tests__/native-web-search.test.ts +30 -2
  159. package/src/__tests__/notification-deep-link.test.ts +62 -0
  160. package/src/__tests__/oauth-commands-routes.test.ts +37 -0
  161. package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
  162. package/src/__tests__/oauth-store.test.ts +3 -2
  163. package/src/__tests__/onboarding-template-contract.test.ts +3 -2
  164. package/src/__tests__/openai-provider.test.ts +8 -9
  165. package/src/__tests__/openai-responses-provider.test.ts +70 -10
  166. package/src/__tests__/openrouter-provider-only.test.ts +27 -5
  167. package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
  168. package/src/__tests__/persistence-pipeline.test.ts +139 -1
  169. package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
  170. package/src/__tests__/plugin-bootstrap.test.ts +9 -11
  171. package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
  172. package/src/__tests__/process-message-background-slack.test.ts +21 -16
  173. package/src/__tests__/process-message-display-content.test.ts +19 -22
  174. package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
  175. package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
  176. package/src/__tests__/provider-registry-ollama.test.ts +45 -22
  177. package/src/__tests__/recording-handler.test.ts +1 -0
  178. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  179. package/src/__tests__/registry.test.ts +82 -76
  180. package/src/__tests__/relay-server.test.ts +10 -10
  181. package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
  182. package/src/__tests__/schedule-store.test.ts +16 -1
  183. package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
  184. package/src/__tests__/secret-ingress-http.test.ts +5 -1
  185. package/src/__tests__/secure-keys.test.ts +3 -3
  186. package/src/__tests__/send-endpoint-busy.test.ts +81 -42
  187. package/src/__tests__/server-history-render.test.ts +4 -1
  188. package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
  189. package/src/__tests__/skill-feature-flags.test.ts +14 -16
  190. package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
  191. package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
  192. package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
  193. package/src/__tests__/skill-tool-factory.test.ts +96 -95
  194. package/src/__tests__/slack-channel-config.test.ts +3 -3
  195. package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
  196. package/src/__tests__/subagent-disposal.test.ts +27 -8
  197. package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
  198. package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
  199. package/src/__tests__/subagent-manager-notify.test.ts +20 -8
  200. package/src/__tests__/subagent-notify-parent.test.ts +5 -4
  201. package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
  202. package/src/__tests__/subagent-tools.test.ts +2 -1
  203. package/src/__tests__/suggestion-routes.test.ts +1 -0
  204. package/src/__tests__/system-prompt.test.ts +38 -0
  205. package/src/__tests__/test-preload-verifier.ts +68 -0
  206. package/src/__tests__/test-preload.ts +32 -39
  207. package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
  208. package/src/__tests__/tool-executor.test.ts +55 -10
  209. package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
  210. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  211. package/src/__tests__/twilio-routes.test.ts +3 -2
  212. package/src/__tests__/validate-input.test.ts +381 -0
  213. package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
  214. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
  215. package/src/__tests__/voice-session-bridge.test.ts +37 -28
  216. package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
  217. package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
  218. package/src/acp/session-manager.ts +5 -6
  219. package/src/agent/loop.ts +80 -0
  220. package/src/api/README.md +124 -2
  221. package/src/api/constants/call-sites.ts +27 -0
  222. package/src/api/events/assistant-outbound-attachment.ts +51 -0
  223. package/src/api/events/assistant-text-delta.ts +32 -0
  224. package/src/api/events/assistant-turn-start.ts +33 -0
  225. package/src/api/events/document-comment-created.ts +48 -0
  226. package/src/api/events/document-comment-deleted.ts +24 -0
  227. package/src/api/events/document-comment-reopened.ts +25 -0
  228. package/src/api/events/document-comment-resolved.ts +27 -0
  229. package/src/api/events/generation-cancelled.ts +24 -0
  230. package/src/api/events/generation-handoff.ts +41 -0
  231. package/src/api/events/message-complete.ts +42 -0
  232. package/src/api/events/open-url.ts +30 -0
  233. package/src/{events → api/events}/relationship-state-updated.ts +3 -3
  234. package/src/api/events/tool-use-start.ts +32 -0
  235. package/src/api/index.ts +128 -3
  236. package/src/api/responses/llm-context-response.ts +39 -0
  237. package/src/api/responses/llm-request-log-entry.ts +93 -0
  238. package/src/api/responses/memory-recall-log.ts +65 -0
  239. package/src/api/responses/memory-v2-activation-log.ts +78 -0
  240. package/src/background-wake/background-wake-routes.test.ts +687 -52
  241. package/src/background-wake/platform-client.test.ts +308 -0
  242. package/src/background-wake/platform-client.ts +167 -0
  243. package/src/background-wake/publisher.ts +91 -0
  244. package/src/background-wake/runtime-registry.ts +2 -2
  245. package/src/background-wake/wake-intent-hooks.test.ts +282 -0
  246. package/src/calls/guardian-dispatch.ts +1 -0
  247. package/src/calls/voice-session-bridge.ts +4 -4
  248. package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
  249. package/src/cli/commands/__tests__/notifications.test.ts +184 -40
  250. package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
  251. package/src/cli/commands/channels/index.ts +229 -0
  252. package/src/cli/commands/memory-v3-render.ts +147 -0
  253. package/src/cli/commands/memory-v3.ts +255 -4
  254. package/src/cli/commands/notifications.ts +365 -55
  255. package/src/cli/lib/open-browser.ts +7 -2
  256. package/src/cli/program.ts +2 -0
  257. package/src/config/assistant-feature-flags.ts +23 -42
  258. package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
  259. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  260. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  261. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
  262. package/src/config/call-site-defaults.ts +1 -1
  263. package/src/config/feature-flag-cache.ts +86 -0
  264. package/src/config/feature-flag-registry.json +17 -17
  265. package/src/config/llm-context-resolution.ts +10 -1
  266. package/src/config/llm-resolver.ts +121 -15
  267. package/src/config/loader.ts +4 -5
  268. package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
  269. package/src/config/schemas/heartbeat.ts +1 -1
  270. package/src/config/schemas/llm.ts +90 -1
  271. package/src/config/schemas/memory-v2.ts +26 -0
  272. package/src/config/schemas/services.ts +6 -2
  273. package/src/config/seed-inference-profiles.ts +36 -16
  274. package/src/context/token-estimator.ts +10 -5
  275. package/src/credential-execution/executable-discovery.ts +40 -0
  276. package/src/credential-execution/process-manager.ts +6 -2
  277. package/src/credential-health/credential-health-service.ts +125 -40
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
  279. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
  280. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
  281. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
  282. package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
  283. package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
  284. package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
  285. package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
  286. package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
  287. package/src/daemon/conversation-agent-loop.ts +132 -28
  288. package/src/daemon/conversation-error.ts +33 -5
  289. package/src/daemon/conversation-messaging.ts +84 -43
  290. package/src/daemon/conversation-process.ts +74 -37
  291. package/src/daemon/conversation-runtime-assembly.ts +29 -9
  292. package/src/daemon/conversation-skill-tools.ts +14 -30
  293. package/src/daemon/conversation-surfaces.ts +69 -34
  294. package/src/daemon/conversation-tool-setup.ts +33 -48
  295. package/src/daemon/conversation.ts +26 -46
  296. package/src/daemon/daemon-control.ts +1 -1
  297. package/src/daemon/daemon-skill-host.ts +9 -2
  298. package/src/daemon/disk-pressure-guard.ts +27 -29
  299. package/src/daemon/first-greeting.ts +31 -13
  300. package/src/daemon/handlers/shared.ts +6 -1
  301. package/src/daemon/lifecycle.ts +12 -12
  302. package/src/daemon/mcp-reload-service.ts +1 -1
  303. package/src/daemon/meet-manifest-loader.ts +10 -17
  304. package/src/daemon/message-types/conversations.ts +20 -22
  305. package/src/daemon/message-types/document-comments.ts +8 -44
  306. package/src/daemon/message-types/home.ts +2 -2
  307. package/src/daemon/message-types/integrations.ts +2 -7
  308. package/src/daemon/message-types/messages.ts +23 -38
  309. package/src/daemon/message-types/subagents.ts +6 -0
  310. package/src/daemon/process-message.ts +9 -9
  311. package/src/daemon/providers-setup.ts +1 -1
  312. package/src/daemon/server.ts +16 -0
  313. package/src/daemon/switch-inference-profile-tool.ts +13 -3
  314. package/src/daemon/tool-setup-types.ts +0 -6
  315. package/src/daemon/wake-target-adapter.ts +10 -0
  316. package/src/documents/document-store.ts +38 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +1 -0
  318. package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
  319. package/src/heartbeat/heartbeat-service.ts +63 -0
  320. package/src/home/__tests__/feed-writer.test.ts +161 -0
  321. package/src/home/__tests__/post-connect-feed.test.ts +1 -0
  322. package/src/home/__tests__/suggested-prompts.test.ts +55 -59
  323. package/src/home/feed-writer.ts +146 -7
  324. package/src/home/suggested-prompts.ts +27 -145
  325. package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
  326. package/src/ipc/gateway-client.test.ts +4 -1
  327. package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
  328. package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
  329. package/src/ipc/skill-routes/memory.ts +4 -3
  330. package/src/ipc/skill-routes/registries.ts +28 -29
  331. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
  332. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
  333. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
  334. package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
  335. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
  336. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
  337. package/src/memory/conversation-attention-store.ts +17 -3
  338. package/src/memory/conversation-crud.ts +352 -112
  339. package/src/memory/db-connection.ts +29 -19
  340. package/src/memory/db-init.ts +4 -0
  341. package/src/memory/db-singleton.ts +77 -0
  342. package/src/memory/delivery-channels.ts +82 -0
  343. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
  344. package/src/memory/graph/retriever.test.ts +3 -3
  345. package/src/memory/job-handlers/embedding.test.ts +3 -2
  346. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
  347. package/src/memory/jobs-worker.ts +12 -1
  348. package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
  349. package/src/memory/llm-request-log-source-local.ts +24 -0
  350. package/src/memory/llm-request-log-source.ts +31 -0
  351. package/src/memory/llm-request-log-store.ts +188 -3
  352. package/src/memory/memory-v2-activation-log-store.ts +95 -1
  353. package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
  354. package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
  355. package/src/memory/migrations/index.ts +2 -0
  356. package/src/memory/schema/conversations.ts +9 -1
  357. package/src/memory/schema/inference.ts +0 -1
  358. package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
  359. package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
  362. package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
  363. package/src/memory/v2/harness/metrics.ts +5 -1
  364. package/src/memory/v2/harness/replay-input.ts +19 -3
  365. package/src/memory/v2/harness/runner.ts +6 -0
  366. package/src/memory/v2/harness/trace.ts +6 -0
  367. package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
  368. package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
  369. package/src/memory/v3/__tests__/edges.test.ts +144 -1
  370. package/src/memory/v3/__tests__/filter.test.ts +48 -0
  371. package/src/memory/v3/__tests__/gate.test.ts +96 -33
  372. package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
  373. package/src/memory/v3/__tests__/loop.test.ts +250 -5
  374. package/src/memory/v3/__tests__/scouts.test.ts +49 -0
  375. package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
  376. package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
  377. package/src/memory/v3/__tests__/traversal.test.ts +39 -0
  378. package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
  379. package/src/memory/v3/__tests__/validate.test.ts +32 -0
  380. package/src/memory/v3/coretrieval-seed.ts +240 -0
  381. package/src/memory/v3/edges.ts +58 -21
  382. package/src/memory/v3/filter.ts +27 -22
  383. package/src/memory/v3/gate.ts +51 -36
  384. package/src/memory/v3/index-composition.ts +18 -5
  385. package/src/memory/v3/loop.ts +65 -17
  386. package/src/memory/v3/scouts.ts +15 -4
  387. package/src/memory/v3/shadow-diff.ts +287 -0
  388. package/src/memory/v3/shadow-middleware.ts +44 -2
  389. package/src/memory/v3/traversal.ts +6 -1
  390. package/src/memory/v3/tree-walk.ts +6 -1
  391. package/src/memory/v3/validate.ts +56 -33
  392. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  393. package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
  394. package/src/notifications/adapters/slack.ts +45 -11
  395. package/src/notifications/broadcaster.ts +114 -63
  396. package/src/notifications/conversation-pairing.ts +23 -3
  397. package/src/notifications/decisions-store.ts +32 -1
  398. package/src/notifications/deliveries-store.ts +45 -0
  399. package/src/notifications/edit-notification.ts +201 -0
  400. package/src/notifications/emit-signal.ts +11 -1
  401. package/src/notifications/signal.ts +10 -0
  402. package/src/notifications/types.ts +37 -0
  403. package/src/oauth/byo-connection.test.ts +67 -3
  404. package/src/oauth/byo-connection.ts +32 -5
  405. package/src/oauth/connect-orchestrator.ts +9 -0
  406. package/src/oauth/connection-resolver.test.ts +76 -0
  407. package/src/oauth/connection-resolver.ts +49 -10
  408. package/src/oauth/manual-token-connection.ts +51 -3
  409. package/src/oauth/seed-providers.ts +3 -0
  410. package/src/permissions/approval-policy.test.ts +19 -5
  411. package/src/permissions/approval-policy.ts +14 -3
  412. package/src/permissions/checker.ts +21 -8
  413. package/src/platform/client.test.ts +24 -1
  414. package/src/platform/client.ts +8 -0
  415. package/src/platform/feature-gate.ts +15 -0
  416. package/src/plugins/defaults/injectors.ts +2 -8
  417. package/src/plugins/defaults/persistence.ts +25 -6
  418. package/src/plugins/types.ts +57 -13
  419. package/src/proactive-artifact/job.test.ts +1 -0
  420. package/src/prompts/__tests__/system-prompt.test.ts +4 -4
  421. package/src/prompts/system-prompt.ts +38 -40
  422. package/src/prompts/template-detection.ts +10 -4
  423. package/src/prompts/templates/BOOTSTRAP.md +7 -11
  424. package/src/prompts/templates/IDENTITY.md +0 -2
  425. package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
  426. package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
  427. package/src/providers/call-site-routing.ts +33 -9
  428. package/src/providers/connection-model-compat.ts +23 -0
  429. package/src/providers/connection-resolution.ts +39 -20
  430. package/src/providers/fireworks/client.ts +1 -0
  431. package/src/providers/gemini/client.ts +24 -3
  432. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
  433. package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
  434. package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
  435. package/src/providers/inference/auth.ts +0 -8
  436. package/src/providers/inference/connections.ts +3 -66
  437. package/src/providers/inference/resolve-auth.ts +2 -3
  438. package/src/providers/model-catalog.ts +35 -1
  439. package/src/providers/model-intents.ts +3 -3
  440. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  441. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
  442. package/src/providers/openai/chat-completions-provider.ts +110 -12
  443. package/src/providers/openai/codex-models.ts +2 -0
  444. package/src/providers/openai/responses-provider.ts +53 -53
  445. package/src/providers/openrouter/client.ts +13 -8
  446. package/src/providers/provider-send-message.ts +18 -9
  447. package/src/providers/registry.ts +48 -8
  448. package/src/providers/retry.ts +16 -4
  449. package/src/providers/search-provider-catalog.ts +17 -9
  450. package/src/providers/types.ts +9 -0
  451. package/src/runtime/__tests__/agent-wake.test.ts +1 -0
  452. package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
  453. package/src/runtime/access-request-helper.ts +1 -0
  454. package/src/runtime/auth/route-policy.ts +10 -0
  455. package/src/runtime/channel-readiness-service.ts +68 -0
  456. package/src/runtime/channel-reply-delivery.ts +23 -0
  457. package/src/runtime/channel-retry-sweep.ts +47 -14
  458. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  459. package/src/runtime/migrations/vbundle-builder.ts +3 -2
  460. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
  461. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
  462. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
  463. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  464. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
  465. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
  466. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
  467. package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
  468. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
  469. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
  470. package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
  471. package/src/runtime/routes/acp-routes-list.test.ts +3 -0
  472. package/src/runtime/routes/app-management-routes.ts +111 -4
  473. package/src/runtime/routes/background-wake-routes.ts +188 -20
  474. package/src/runtime/routes/btw-routes.ts +4 -4
  475. package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
  476. package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
  477. package/src/runtime/routes/conversation-list-routes.ts +147 -0
  478. package/src/runtime/routes/conversation-management-routes.ts +39 -14
  479. package/src/runtime/routes/conversation-query-routes.ts +60 -10
  480. package/src/runtime/routes/conversation-routes.ts +186 -140
  481. package/src/runtime/routes/conversations-import-routes.ts +19 -6
  482. package/src/runtime/routes/documents-routes.ts +10 -1
  483. package/src/runtime/routes/group-routes.ts +11 -0
  484. package/src/runtime/routes/home-feed-routes.ts +129 -0
  485. package/src/runtime/routes/identity-intro-cache.ts +61 -16
  486. package/src/runtime/routes/identity-routes.ts +30 -9
  487. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
  488. package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
  489. package/src/runtime/routes/index.ts +2 -0
  490. package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
  491. package/src/runtime/routes/integrations/vercel.ts +15 -0
  492. package/src/runtime/routes/llm-context-normalization.ts +7 -2
  493. package/src/runtime/routes/memory-v3-routes.ts +160 -2
  494. package/src/runtime/routes/migration-routes.ts +20 -13
  495. package/src/runtime/routes/notification-routes.ts +63 -1
  496. package/src/runtime/routes/oauth-commands-routes.ts +6 -1
  497. package/src/runtime/routes/surface-action-routes.ts +1 -38
  498. package/src/runtime/routes/surface-content-routes.ts +12 -5
  499. package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
  500. package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
  501. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
  502. package/src/runtime/slack-dm-text-delivery.ts +177 -0
  503. package/src/runtime/sync/resource-sync-events.ts +1 -1
  504. package/src/runtime/tool-grant-request-helper.ts +1 -0
  505. package/src/schedule/schedule-store.ts +8 -1
  506. package/src/schedule/scheduler.ts +111 -15
  507. package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
  508. package/src/security/encrypted-store.ts +7 -16
  509. package/src/security/store-path-override.ts +61 -0
  510. package/src/signals/user-message.ts +5 -8
  511. package/src/skills/validate-input.ts +177 -0
  512. package/src/subagent/manager.ts +13 -13
  513. package/src/subagent/types.ts +6 -0
  514. package/src/tasks/tool-sanitizer.ts +2 -2
  515. package/src/tools/apps/definitions.ts +35 -21
  516. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
  517. package/src/tools/computer-use/definitions.ts +268 -266
  518. package/src/tools/document/document-tool.ts +131 -8
  519. package/src/tools/execution-target.ts +2 -5
  520. package/src/tools/executor.ts +18 -55
  521. package/src/tools/host-filesystem/edit.test.ts +1 -0
  522. package/src/tools/host-filesystem/read.test.ts +1 -0
  523. package/src/tools/host-filesystem/transfer.test.ts +31 -6
  524. package/src/tools/host-filesystem/write.test.ts +1 -0
  525. package/src/tools/mcp/mcp-tool-factory.ts +0 -2
  526. package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
  527. package/src/tools/network/__tests__/web-search.test.ts +211 -3
  528. package/src/tools/network/managed-search-proxy.ts +183 -0
  529. package/src/tools/network/web-search.ts +199 -44
  530. package/src/tools/policy-context.ts +3 -1
  531. package/src/tools/registry.ts +146 -103
  532. package/src/tools/schedule/create.ts +1 -1
  533. package/src/tools/skills/skill-tool-factory.ts +17 -36
  534. package/src/tools/subagent/spawn.ts +3 -0
  535. package/src/tools/tool-approval-handler.ts +10 -4
  536. package/src/tools/tool-name-aliases.ts +72 -14
  537. package/src/tools/types.ts +17 -15
  538. package/src/tools/ui-surface/definitions.ts +98 -86
  539. package/src/types/onboarding-context.ts +6 -0
  540. package/src/usage/attribution.ts +32 -1
  541. package/src/util/browser.ts +7 -2
  542. package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
  543. package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
  544. package/src/workspace/migrations/registry.ts +4 -0
@@ -8,10 +8,11 @@ import { z } from "zod";
8
8
  import { getDb } from "../../memory/db-connection.js";
9
9
  import { notificationDeliveries } from "../../memory/schema.js";
10
10
  import { bufferIfDeferred } from "../../notifications/deferred-emit.js";
11
+ import { editNotification } from "../../notifications/edit-notification.js";
11
12
  import { emitNotificationSignal } from "../../notifications/emit-signal.js";
12
13
  import { listEvents } from "../../notifications/events-store.js";
13
14
  import type { AttentionHints } from "../../notifications/signal.js";
14
- import { BadRequestError } from "./errors.js";
15
+ import { BadRequestError, NotFoundError } from "./errors.js";
15
16
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
16
17
 
17
18
  function handleNotificationIntentResult({ body = {} }: RouteHandlerArgs) {
@@ -89,6 +90,26 @@ const ListNotificationEventsParams = z.object({
89
90
  sourceEventName: z.string().optional(),
90
91
  });
91
92
 
93
+ const EditNotificationParams = z
94
+ .object({
95
+ id: z.string().min(1).describe("Feed item id (notif:<uuid>) or bare uuid"),
96
+ title: z.string().optional(),
97
+ body: z.string().optional(),
98
+ urgency: z.enum(["low", "medium", "high", "critical"]).optional(),
99
+ status: z.enum(["new", "seen", "acted_on", "dismissed"]).optional(),
100
+ })
101
+ .refine(
102
+ (v) =>
103
+ v.title !== undefined ||
104
+ v.body !== undefined ||
105
+ v.urgency !== undefined ||
106
+ v.status !== undefined,
107
+ {
108
+ message:
109
+ "At least one of `title`, `body`, `urgency`, or `status` must be supplied",
110
+ },
111
+ );
112
+
92
113
  // ── Notification pipeline handlers ───────────────────────────────────
93
114
 
94
115
  async function handleEmitSignal({ body = {} }: RouteHandlerArgs) {
@@ -125,6 +146,19 @@ async function handleEmitSignal({ body = {} }: RouteHandlerArgs) {
125
146
  };
126
147
  }
127
148
 
149
+ async function handleEditNotification({ body = {} }: RouteHandlerArgs) {
150
+ const validated = EditNotificationParams.parse(body);
151
+ const result = await editNotification(validated);
152
+ if (!result) {
153
+ throw new NotFoundError(`No notification found for id ${validated.id}`);
154
+ }
155
+ return {
156
+ ok: true,
157
+ feedItem: result.feedItem,
158
+ channels: result.channels,
159
+ };
160
+ }
161
+
128
162
  function handleListEvents({ body = {} }: RouteHandlerArgs) {
129
163
  const validated = ListNotificationEventsParams.parse(body);
130
164
  const rows = listEvents({
@@ -175,6 +209,34 @@ export const ROUTES: RouteDefinition[] = [
175
209
  reason: z.string(),
176
210
  }),
177
211
  },
212
+ {
213
+ operationId: "edit_notification",
214
+ endpoint: "notifications/edit",
215
+ method: "POST",
216
+ handler: handleEditNotification,
217
+ summary: "Edit an already-sent notification",
218
+ description:
219
+ "Patch the home-feed entry for a notification and, where supported (Slack today), update the delivered message in place.",
220
+ tags: ["notifications"],
221
+ requestBody: EditNotificationParams,
222
+ responseBody: z.object({
223
+ ok: z.boolean(),
224
+ feedItem: z.record(z.string(), z.unknown()),
225
+ channels: z.array(
226
+ z.object({
227
+ channel: z.string(),
228
+ deliveryId: z.string(),
229
+ outcome: z.enum(["updated", "unsupported", "skipped", "failed"]),
230
+ reason: z.string().optional(),
231
+ }),
232
+ ),
233
+ }),
234
+ additionalResponses: {
235
+ "404": {
236
+ description: "No notification found for the supplied id",
237
+ },
238
+ },
239
+ },
178
240
  {
179
241
  operationId: "list_notification_events",
180
242
  endpoint: "notifications/events",
@@ -23,6 +23,7 @@ import {
23
23
  resolveOAuthConnection,
24
24
  type ResolveOAuthConnectionOptions,
25
25
  } from "../../oauth/connection-resolver.js";
26
+ import { syncManualTokenConnection } from "../../oauth/manual-token-connection.js";
26
27
  import {
27
28
  disconnectOAuthProvider,
28
29
  getActiveConnection,
@@ -516,6 +517,10 @@ async function handleStatus({ queryParams = {} }: RouteHandlerArgs) {
516
517
  }
517
518
 
518
519
  // BYO path
520
+ if (providerRow.authorizeUrl === "urn:manual-token") {
521
+ await syncManualTokenConnection(provider);
522
+ }
523
+
519
524
  const allConnections = listConnections(provider);
520
525
  const activeRows = allConnections.filter((r) => r.status === "active");
521
526
 
@@ -914,7 +919,7 @@ async function handleManagedConnect({ body = {} }: RouteHandlerArgs) {
914
919
  reqBody.requested_scopes = b.scopes;
915
920
  }
916
921
  reqBody.redirect_after_connect =
917
- b.redirect_after_connect ?? "/account/oauth/desktop-complete";
922
+ b.redirect_after_connect ?? "/account/oauth/complete";
918
923
 
919
924
  const response = await client.fetch(startPath, {
920
925
  method: "POST",
@@ -11,13 +11,6 @@ import { z } from "zod";
11
11
 
12
12
  import type { ChannelId } from "../../channels/types.js";
13
13
  import { isHttpAuthDisabled } from "../../config/env.js";
14
- import type { Conversation } from "../../daemon/conversation.js";
15
- import {
16
- findConversation,
17
- findConversationBySurfaceId,
18
- getOrCreateConversation,
19
- } from "../../daemon/conversation-store.js";
20
- import { rawGet } from "../../memory/raw-query.js";
21
14
  import { getLogger } from "../../util/logger.js";
22
15
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
23
16
  import { healGuardianBindingDrift } from "../guardian-vellum-migration.js";
@@ -27,6 +20,7 @@ import {
27
20
  withSourceChannel,
28
21
  } from "../trust-context-resolver.js";
29
22
  import { BadRequestError, InternalError, NotFoundError, RouteError } from "./errors.js";
23
+ import { resolveSurfaceConversation } from "./surface-conversation-resolver.js";
30
24
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
31
25
 
32
26
  const log = getLogger("surface-action-routes");
@@ -90,37 +84,6 @@ function applyTrustContext(
90
84
  }
91
85
  }
92
86
 
93
- /**
94
- * Resolve the conversation owning a surface, rehydrating from the DB when
95
- * the in-memory lookup misses (daemon restart or LRU eviction). The DB scan
96
- * is also the source of truth that the surface exists — without it,
97
- * `getOrCreateConversation` would silently create a phantom conversation
98
- * for any caller-supplied id.
99
- */
100
- async function resolveSurfaceConversation(
101
- conversationId: string | null | undefined,
102
- surfaceId: string,
103
- ): Promise<Conversation | undefined> {
104
- const found = conversationId
105
- ? findConversation(conversationId)
106
- : findConversationBySurfaceId(surfaceId);
107
- if (found) return found;
108
-
109
- // Escape LIKE wildcards so a `surfaceId` like "%" or "_" can't match
110
- // unrelated rows.
111
- const escaped = surfaceId.replace(/[\\%_]/g, "\\$&");
112
- const row = rawGet<{ conversation_id: string }>(
113
- `SELECT conversation_id FROM messages
114
- WHERE content LIKE ? ESCAPE '\\'
115
- ORDER BY created_at DESC
116
- LIMIT 1`,
117
- `%"surfaceId":"${escaped}"%`,
118
- );
119
- if (!row) return undefined;
120
- if (conversationId && conversationId !== row.conversation_id) return undefined;
121
- return await getOrCreateConversation(row.conversation_id);
122
- }
123
-
124
87
  // ---------------------------------------------------------------------------
125
88
  // Handlers
126
89
  // ---------------------------------------------------------------------------
@@ -2,14 +2,16 @@
2
2
  * Route handler for fetching surface content by ID.
3
3
  *
4
4
  * GET /v1/surfaces/:surfaceId — return the full surface payload from the
5
- * conversation's in-memory surface state. Used by clients to re-hydrate surfaces
6
- * whose data was stripped during memory compaction.
5
+ * conversation's in-memory surface state. Used by clients to re-hydrate
6
+ * surfaces whose data was stripped during memory compaction, or whose
7
+ * owning conversation has been evicted from the daemon's in-memory map
8
+ * (daemon restart, LRU eviction).
7
9
  */
8
10
  import { z } from "zod";
9
11
 
10
- import { findConversation } from "../../daemon/conversation-store.js";
11
12
  import { getLogger } from "../../util/logger.js";
12
13
  import { BadRequestError, NotFoundError } from "./errors.js";
14
+ import { resolveSurfaceConversation } from "./surface-conversation-resolver.js";
13
15
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
14
16
 
15
17
  const log = getLogger("surface-content-routes");
@@ -18,7 +20,7 @@ const log = getLogger("surface-content-routes");
18
20
  // GET /v1/surfaces/:surfaceId?conversationId=...
19
21
  // ---------------------------------------------------------------------------
20
22
 
21
- function handleGetSurfaceContent({
23
+ async function handleGetSurfaceContent({
22
24
  pathParams = {},
23
25
  queryParams = {},
24
26
  }: RouteHandlerArgs) {
@@ -32,7 +34,12 @@ function handleGetSurfaceContent({
32
34
  throw new BadRequestError("surfaceId path parameter is required");
33
35
  }
34
36
 
35
- const conversation = findConversation(conversationId);
37
+ // Resolve via the shared surface→conversation helper: in-memory first,
38
+ // falling back to a DB scan that rehydrates the conversation when the
39
+ // owning Conversation has been evicted or the daemon was restarted. The
40
+ // DB scan uses the surfaceId itself as the existence check so a stale
41
+ // or made-up conversationId can't materialize a phantom conversation.
42
+ const conversation = await resolveSurfaceConversation(conversationId, surfaceId);
36
43
  if (!conversation) {
37
44
  throw new NotFoundError(
38
45
  "No active conversation found for this conversationId",
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Shared helper for resolving the conversation that owns a surface.
3
+ *
4
+ * Used by both `surface-action-routes` (POST /v1/surface-actions) and
5
+ * `surface-content-routes` (GET /v1/surfaces/:surfaceId) so the
6
+ * in-memory-miss → DB-scan → rehydrate flow stays in one place.
7
+ *
8
+ * Why this exists: surfaces live on the in-memory `Conversation` object
9
+ * (`surfaceState` is rebuilt from `ui_surface` blocks in message
10
+ * history by `restoreSurfaceStateFromHistory()` whenever a Conversation
11
+ * is constructed). A bare `findConversation` lookup 404s after daemon
12
+ * restart or LRU eviction even though the surface data is still in the
13
+ * SQLite `messages` table. The DB scan below uses the surfaceId itself
14
+ * as the existence check so `getOrCreateConversation` can't be tricked
15
+ * into materializing a phantom conversation for any caller-supplied id.
16
+ */
17
+ import type { Conversation } from "../../daemon/conversation.js";
18
+ import {
19
+ findConversation,
20
+ findConversationBySurfaceId,
21
+ getOrCreateConversation,
22
+ } from "../../daemon/conversation-store.js";
23
+ import { rawGet } from "../../memory/raw-query.js";
24
+
25
+ /**
26
+ * Resolve the {@link Conversation} that owns the given surface.
27
+ *
28
+ * Lookup order:
29
+ * 1. In-memory map keyed by `conversationId` (or by `surfaceId` when no
30
+ * id is supplied).
31
+ * 2. SQLite `messages` table — find the conversation whose history
32
+ * contains a `ui_surface` block with this `surfaceId`. The result is
33
+ * validated against the caller's `conversationId` (when supplied) so
34
+ * a mismatched pair returns `undefined` rather than silently
35
+ * re-routing to a different conversation.
36
+ * 3. `getOrCreateConversation` rehydrates the row, which triggers
37
+ * `restoreSurfaceStateFromHistory()` and repopulates `surfaceState`.
38
+ *
39
+ * Returns `undefined` when neither lookup nor DB scan turns up a
40
+ * matching conversation. Callers are expected to translate that into
41
+ * the route-appropriate 404.
42
+ */
43
+ export async function resolveSurfaceConversation(
44
+ conversationId: string | null | undefined,
45
+ surfaceId: string,
46
+ ): Promise<Conversation | undefined> {
47
+ const found = conversationId
48
+ ? findConversation(conversationId)
49
+ : findConversationBySurfaceId(surfaceId);
50
+ if (found) return found;
51
+
52
+ // Escape LIKE wildcards so a `surfaceId` like "%" or "_" can't match
53
+ // unrelated rows.
54
+ const escaped = surfaceId.replace(/[\\%_]/g, "\\$&");
55
+ const row = rawGet<{ conversation_id: string }>(
56
+ `SELECT conversation_id FROM messages
57
+ WHERE content LIKE ? ESCAPE '\\'
58
+ ORDER BY created_at DESC
59
+ LIMIT 1`,
60
+ `%"surfaceId":"${escaped}"%`,
61
+ );
62
+ if (!row) return undefined;
63
+ if (conversationId && conversationId !== row.conversation_id) return undefined;
64
+ return await getOrCreateConversation(row.conversation_id);
65
+ }
@@ -5,6 +5,7 @@
5
5
  import { z } from "zod";
6
6
 
7
7
  import { destroyActiveConversation } from "../../daemon/conversation-store.js";
8
+ import { stripConversationIds } from "../../home/feed-writer.js";
8
9
  import {
9
10
  countConversationsByScheduleJobId,
10
11
  getConversation,
@@ -54,6 +55,8 @@ async function handleWipeConversation({ body = {} }: RouteHandlerArgs) {
54
55
  });
55
56
  }
56
57
 
58
+ void stripConversationIds(conversationId);
59
+
57
60
  return {
58
61
  wiped: true,
59
62
  unsupersededItems: 0,
@@ -46,6 +46,7 @@ mock.module("../../../memory/conversation-crud.js", () => ({
46
46
  addMessage: mockAddMessage,
47
47
  findAnalysisConversationFor: mockFindAnalysisConversationFor,
48
48
  getConversationSource: mockGetConversationSource,
49
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
49
50
  }));
50
51
 
51
52
  mock.module("../../../export/transcript-formatter.js", () => ({
@@ -80,6 +81,7 @@ const testHub = new AssistantEventHub();
80
81
  mock.module("../../assistant-event-hub.js", () => ({
81
82
  AssistantEventHub,
82
83
  assistantEventHub: testHub,
84
+ broadcastMessage: async () => {},
83
85
  }));
84
86
 
85
87
  import { analyzeConversation } from "../analyze-conversation.js";
@@ -0,0 +1,177 @@
1
+ import type { ChannelId } from "../channels/types.js";
2
+ import type { ServerMessage } from "../daemon/message-protocol.js";
3
+ import { getLogger } from "../util/logger.js";
4
+ import { deliverChannelReply } from "./gateway-client.js";
5
+
6
+ const log = getLogger("runtime-http");
7
+
8
+ const NO_RESPONSE_INLINE_RE = /<no_response\s*\/?>/gi;
9
+
10
+ export function isSlackDeliveryCallbackUrl(replyCallbackUrl?: string): boolean {
11
+ if (!replyCallbackUrl) return false;
12
+ try {
13
+ return new URL(replyCallbackUrl).pathname.endsWith("/deliver/slack");
14
+ } catch {
15
+ return replyCallbackUrl.endsWith("/deliver/slack");
16
+ }
17
+ }
18
+
19
+ export function shouldDeliverSlackDmTextResponses(params: {
20
+ sourceChannel: ChannelId;
21
+ chatType?: string;
22
+ replyCallbackUrl?: string;
23
+ }): boolean {
24
+ return (
25
+ params.sourceChannel === "slack" &&
26
+ params.chatType === "im" &&
27
+ isSlackDeliveryCallbackUrl(params.replyCallbackUrl)
28
+ );
29
+ }
30
+
31
+ export type SlackDmTextDeliveryController = {
32
+ observeEvent: (msg: ServerMessage) => void;
33
+ waitForPendingDeliveries: () => Promise<void>;
34
+ getFinalDeliveryResumeOptions: (
35
+ messageId: string | undefined,
36
+ ) => { startFromSegment: number; messageTs?: string } | undefined;
37
+ };
38
+
39
+ export function createSlackDmTextDeliveryController(params: {
40
+ sourceChannel: ChannelId;
41
+ chatType?: string;
42
+ replyCallbackUrl?: string;
43
+ chatId: string;
44
+ assistantId?: string;
45
+ deliveredTextResponseIndexes?: readonly number[];
46
+ onTextResponseDelivered?: (
47
+ responseIndex: number,
48
+ reason: "before_tool" | "message_complete",
49
+ ) => void;
50
+ }): SlackDmTextDeliveryController | undefined {
51
+ const { replyCallbackUrl } = params;
52
+ if (!shouldDeliverSlackDmTextResponses(params) || !replyCallbackUrl) {
53
+ return undefined;
54
+ }
55
+
56
+ const deliveredTextResponseIndexes = new Set(
57
+ params.deliveredTextResponseIndexes ?? [],
58
+ );
59
+ const messageIdToResponseIndexes = new Map<string, number[]>();
60
+ const responseIndexToMessageTs = new Map<number, string>();
61
+ let textResponseIndex = 0;
62
+ let pendingText = "";
63
+ let currentMessageResponseIndexes: number[] = [];
64
+ let deliveryChain = Promise.resolve();
65
+
66
+ const associateCurrentMessageResponses = (
67
+ messageId: string | undefined,
68
+ ): void => {
69
+ if (!messageId || currentMessageResponseIndexes.length === 0) return;
70
+ const responseIndexes = messageIdToResponseIndexes.get(messageId) ?? [];
71
+ for (const responseIndex of currentMessageResponseIndexes) {
72
+ if (!responseIndexes.includes(responseIndex)) {
73
+ responseIndexes.push(responseIndex);
74
+ }
75
+ }
76
+ messageIdToResponseIndexes.set(messageId, responseIndexes);
77
+ currentMessageResponseIndexes = [];
78
+ };
79
+
80
+ const flushPendingText = (
81
+ reason: "before_tool" | "message_complete",
82
+ ): void => {
83
+ const text = pendingText;
84
+ pendingText = "";
85
+
86
+ const deliverableText = text.replace(NO_RESPONSE_INLINE_RE, "").trim();
87
+ if (deliverableText.length === 0) return;
88
+
89
+ textResponseIndex += 1;
90
+ const currentResponseIndex = textResponseIndex;
91
+ currentMessageResponseIndexes.push(currentResponseIndex);
92
+ if (deliveredTextResponseIndexes.has(currentResponseIndex)) return;
93
+
94
+ deliveryChain = deliveryChain
95
+ .catch(() => undefined)
96
+ .then(async () => {
97
+ let result: Awaited<ReturnType<typeof deliverChannelReply>>;
98
+ try {
99
+ result = await deliverChannelReply(replyCallbackUrl, {
100
+ chatId: params.chatId,
101
+ text: deliverableText,
102
+ assistantId: params.assistantId,
103
+ useBlocks: true,
104
+ });
105
+ } catch (err) {
106
+ log.warn(
107
+ { err, chatId: params.chatId },
108
+ "Failed to deliver intermediate Slack DM assistant text",
109
+ );
110
+ return;
111
+ }
112
+
113
+ if (result.ts) {
114
+ responseIndexToMessageTs.set(currentResponseIndex, result.ts);
115
+ }
116
+ deliveredTextResponseIndexes.add(currentResponseIndex);
117
+ try {
118
+ params.onTextResponseDelivered?.(currentResponseIndex, reason);
119
+ } catch (err) {
120
+ log.warn(
121
+ { err, chatId: params.chatId, responseIndex: currentResponseIndex },
122
+ "Failed to persist intermediate Slack DM assistant text progress",
123
+ );
124
+ }
125
+ });
126
+ };
127
+
128
+ return {
129
+ observeEvent(msg) {
130
+ if (msg.type === "assistant_text_delta") {
131
+ pendingText += msg.text;
132
+ return;
133
+ }
134
+
135
+ if (msg.type === "message_complete") {
136
+ flushPendingText("message_complete");
137
+ if (typeof msg.messageId === "string") {
138
+ associateCurrentMessageResponses(msg.messageId);
139
+ }
140
+ currentMessageResponseIndexes = [];
141
+ return;
142
+ }
143
+
144
+ if (msg.type === "tool_use_start") {
145
+ flushPendingText("before_tool");
146
+ }
147
+ },
148
+ waitForPendingDeliveries: () => deliveryChain,
149
+ getFinalDeliveryResumeOptions: (messageId) => {
150
+ if (typeof messageId !== "string") {
151
+ return undefined;
152
+ }
153
+ const responseIndexes = messageIdToResponseIndexes.get(messageId) ?? [];
154
+ let deliveredPrefixCount = 0;
155
+ for (const responseIndex of responseIndexes) {
156
+ if (!deliveredTextResponseIndexes.has(responseIndex)) break;
157
+ deliveredPrefixCount += 1;
158
+ }
159
+ if (deliveredPrefixCount === 0) return undefined;
160
+
161
+ const firstLiveResponseIndex = responseIndexes[0];
162
+ const allTextResponsesDelivered =
163
+ responseIndexes.length > 0 &&
164
+ responseIndexes.every((responseIndex) =>
165
+ deliveredTextResponseIndexes.has(responseIndex),
166
+ );
167
+ const messageTs =
168
+ allTextResponsesDelivered && firstLiveResponseIndex !== undefined
169
+ ? responseIndexToMessageTs.get(firstLiveResponseIndex)
170
+ : undefined;
171
+ return {
172
+ startFromSegment: deliveredPrefixCount,
173
+ ...(messageTs ? { messageTs } : {}),
174
+ };
175
+ },
176
+ };
177
+ }
@@ -64,7 +64,7 @@ const SHAPE_CHANGING_REASONS: ReadonlySet<ConversationListInvalidatedReason> =
64
64
  *
65
65
  * Web consumes `sync_changed` (`conversationsList` for shape changes,
66
66
  * `conversation:<id>:metadata` for content changes) directly and patches
67
- * the cached list in place — see `useAssistantSyncStream` for the consumer
67
+ * the cached list in place — see `useConversationSync` for the consumer
68
68
  * side. macOS (`ConversationRestorer.swift`) still listens for the typed
69
69
  * broadcast.
70
70
  *
@@ -147,6 +147,7 @@ export function createOrReuseToolGrantRequest(
147
147
  sourceEventName: "guardian.question",
148
148
  sourceChannel: sourceChannel as NotificationSourceChannel,
149
149
  sourceContextId: conversationId,
150
+ requiresConversation: true,
150
151
  attentionHints: {
151
152
  requiresAction: true,
152
153
  urgency: "high",
@@ -17,6 +17,13 @@ const logger = getLogger("schedule-store");
17
17
 
18
18
  function notifySchedulesChanged(): void {
19
19
  publishSchedulesChanged();
20
+ void import("../background-wake/publisher.js")
21
+ .then(({ refreshBackgroundWakeIntent }) =>
22
+ refreshBackgroundWakeIntent("schedule-changed"),
23
+ )
24
+ .catch((err) =>
25
+ logger.warn({ err }, "Failed to queue background wake refresh"),
26
+ );
20
27
  }
21
28
 
22
29
  export type ScheduleMode = "notify" | "execute" | "script" | "wake";
@@ -124,7 +131,7 @@ export function createSchedule(params: {
124
131
  const routingIntent = params.routingIntent ?? "all_channels";
125
132
  const routingHints = params.routingHints ?? {};
126
133
  const quiet = params.quiet ?? false;
127
- const reuseConversation = params.reuseConversation ?? false;
134
+ const reuseConversation = params.reuseConversation ?? !isOneShot;
128
135
  const maxRetries = params.maxRetries ?? 3;
129
136
  const retryBackoffMs = params.retryBackoffMs ?? 60000;
130
137