@vellumai/assistant 0.8.3 → 0.8.5

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 (665) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/docker-entrypoint.sh +0 -1
  3. package/docs/browser-use-architecture-phase2.md +1 -1
  4. package/knip.json +2 -1
  5. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  6. package/openapi.yaml +1492 -100
  7. package/package.json +1 -1
  8. package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
  9. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  10. package/src/__tests__/agent-loop.test.ts +88 -3
  11. package/src/__tests__/anthropic-provider.test.ts +302 -33
  12. package/src/__tests__/approval-cascade.test.ts +1 -1
  13. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  14. package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
  15. package/src/__tests__/audit-log-rotation.test.ts +70 -16
  16. package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
  17. package/src/__tests__/btw-routes.test.ts +2 -3
  18. package/src/__tests__/call-controller.test.ts +0 -1
  19. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  20. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  21. package/src/__tests__/channel-guardian.test.ts +3 -3
  22. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  23. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  24. package/src/__tests__/checker.test.ts +6 -15
  25. package/src/__tests__/compaction-events.test.ts +2 -1
  26. package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
  27. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  28. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
  29. package/src/__tests__/computer-use-tools.test.ts +2 -4
  30. package/src/__tests__/config-watcher.test.ts +1 -1
  31. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  32. package/src/__tests__/context-token-estimator.test.ts +91 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  34. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
  35. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
  37. package/src/__tests__/conversation-agent-loop.test.ts +188 -129
  38. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
  39. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  40. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  41. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  42. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  43. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  44. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  45. package/src/__tests__/conversation-error.test.ts +31 -0
  46. package/src/__tests__/conversation-fork-crud.test.ts +324 -0
  47. package/src/__tests__/conversation-lifecycle.test.ts +53 -12
  48. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  49. package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
  50. package/src/__tests__/conversation-pairing.test.ts +2 -2
  51. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  52. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -1
  53. package/src/__tests__/conversation-queue.test.ts +1 -1
  54. package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
  55. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
  56. package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
  57. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  58. package/src/__tests__/conversation-skill-tools.test.ts +2 -5
  59. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  60. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  61. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  62. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  63. package/src/__tests__/conversation-store.test.ts +1 -1
  64. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  65. package/src/__tests__/conversation-sync-tags.test.ts +99 -32
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -1
  67. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  69. package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
  70. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  71. package/src/__tests__/credential-security-invariants.test.ts +7 -0
  72. package/src/__tests__/credential-vault-unit.test.ts +2 -2
  73. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  74. package/src/__tests__/dm-backfill.test.ts +64 -0
  75. package/src/__tests__/dm-persistence.test.ts +33 -0
  76. package/src/__tests__/document-find-replace.test.ts +501 -0
  77. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  78. package/src/__tests__/email-html-renderer.test.ts +12 -0
  79. package/src/__tests__/first-greeting.test.ts +23 -2
  80. package/src/__tests__/gateway-flag-listener.test.ts +237 -0
  81. package/src/__tests__/gemini-provider.test.ts +78 -0
  82. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  83. package/src/__tests__/guardian-outbound-http.test.ts +7 -5
  84. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  85. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  87. package/src/__tests__/heartbeat-service.test.ts +4 -0
  88. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  89. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  90. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  91. package/src/__tests__/host-file-proxy.test.ts +8 -1
  92. package/src/__tests__/host-shell-tool.test.ts +1 -1
  93. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  94. package/src/__tests__/identity-routes.test.ts +57 -0
  95. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  96. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  97. package/src/__tests__/injector-chain.test.ts +2 -0
  98. package/src/__tests__/injector-document-comments.test.ts +378 -0
  99. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  100. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  102. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  103. package/src/__tests__/list-messages-tool-merge.test.ts +77 -17
  104. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  105. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  106. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  107. package/src/__tests__/llm-resolver.test.ts +161 -9
  108. package/src/__tests__/llm-usage-store.test.ts +66 -0
  109. package/src/__tests__/log-export-routes.test.ts +99 -2
  110. package/src/__tests__/logger.test.ts +89 -0
  111. package/src/__tests__/mcp-abort-signal.test.ts +2 -2
  112. package/src/__tests__/media-generate-image.test.ts +31 -0
  113. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  114. package/src/__tests__/message-queue-steer.test.ts +114 -0
  115. package/src/__tests__/model-intents.test.ts +2 -4
  116. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  117. package/src/__tests__/onboarding-template-contract.test.ts +1 -1
  118. package/src/__tests__/openai-provider.test.ts +151 -0
  119. package/src/__tests__/openai-responses-provider.test.ts +118 -16
  120. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  121. package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
  122. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  123. package/src/__tests__/platform.test.ts +2 -5
  124. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  125. package/src/__tests__/plugin-bootstrap.test.ts +2 -2
  126. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  127. package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
  128. package/src/__tests__/plugin-types.test.ts +3 -2
  129. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  130. package/src/__tests__/pricing.test.ts +12 -0
  131. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  132. package/src/__tests__/process-message-display-content.test.ts +21 -16
  133. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  134. package/src/__tests__/registry.test.ts +2 -8
  135. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  136. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  137. package/src/__tests__/server-history-render.test.ts +83 -4
  138. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  139. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  140. package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
  141. package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
  142. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  143. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  144. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  145. package/src/__tests__/suggestion-routes.test.ts +1 -0
  146. package/src/__tests__/sync-message-contract.test.ts +59 -0
  147. package/src/__tests__/system-prompt.test.ts +161 -124
  148. package/src/__tests__/terminal-tools.test.ts +12 -2
  149. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  150. package/src/__tests__/thread-backfill.test.ts +370 -22
  151. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  152. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  153. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
  155. package/src/__tests__/tool-executor.test.ts +89 -53
  156. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  157. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  158. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  159. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  160. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  161. package/src/__tests__/twilio-routes.test.ts +1 -1
  162. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  163. package/src/__tests__/usage-routes.test.ts +3 -0
  164. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  165. package/src/__tests__/web-fetch.test.ts +2 -2
  166. package/src/__tests__/workspace-git-service.test.ts +94 -10
  167. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  168. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  169. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  170. package/src/acp/prepare-agent-env.ts +78 -0
  171. package/src/acp/session-manager.ts +1 -1
  172. package/src/agent/attachments.ts +1 -0
  173. package/src/agent/loop.ts +65 -20
  174. package/src/api/README.md +5 -0
  175. package/src/api/index.ts +4 -0
  176. package/src/api/package.json +10 -0
  177. package/src/background-wake/background-wake-routes.test.ts +233 -0
  178. package/src/background-wake/next-wake.test.ts +289 -0
  179. package/src/background-wake/next-wake.ts +172 -0
  180. package/src/background-wake/runtime-registry.ts +24 -0
  181. package/src/browser/operations.ts +15 -0
  182. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  183. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  184. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  185. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  186. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  187. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  188. package/src/cli/commands/__tests__/memory-v2.test.ts +10 -12
  189. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  190. package/src/cli/commands/browser.ts +247 -0
  191. package/src/cli/commands/conversations.ts +128 -1
  192. package/src/cli/commands/domain.ts +91 -41
  193. package/src/cli/commands/inference-providers.ts +147 -1
  194. package/src/cli/commands/inference.ts +93 -40
  195. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  196. package/src/cli/commands/memory-v2.ts +483 -0
  197. package/src/cli/commands/memory-v3-render.ts +344 -0
  198. package/src/cli/commands/memory-v3.ts +316 -0
  199. package/src/cli/commands/notifications.ts +24 -2
  200. package/src/cli/program.ts +2 -0
  201. package/src/cli/utils/conversation-id.ts +17 -5
  202. package/src/config/assistant-feature-flags.ts +21 -9
  203. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  204. package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
  205. package/src/config/bundled-skills/document-editor/TOOLS.json +258 -0
  206. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  207. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  208. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  209. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  210. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  211. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  212. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  213. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  214. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  215. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  216. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  217. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  218. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  219. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  220. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  221. package/src/config/bundled-tool-registry.ts +24 -12
  222. package/src/config/call-site-defaults.ts +20 -0
  223. package/src/config/feature-flag-registry.json +115 -3
  224. package/src/config/llm-resolver.ts +16 -2
  225. package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
  226. package/src/config/schemas/call-site-catalog.ts +35 -0
  227. package/src/config/schemas/llm.ts +14 -0
  228. package/src/config/schemas/memory-v2.ts +294 -1
  229. package/src/config/schemas/memory.ts +2 -1
  230. package/src/context/compactor.ts +60 -1
  231. package/src/context/token-estimator.ts +47 -4
  232. package/src/context/window-manager.ts +25 -0
  233. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  234. package/src/conversations/message-consolidation.ts +404 -0
  235. package/src/credential-health/credential-health-service.ts +34 -19
  236. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
  237. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  238. package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
  239. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  240. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  241. package/src/daemon/conversation-agent-loop-handlers.ts +155 -36
  242. package/src/daemon/conversation-agent-loop.ts +307 -88
  243. package/src/daemon/conversation-error.ts +31 -1
  244. package/src/daemon/conversation-lifecycle.ts +149 -118
  245. package/src/daemon/conversation-messaging.ts +3 -0
  246. package/src/daemon/conversation-process.ts +273 -0
  247. package/src/daemon/conversation-queue-manager.ts +14 -0
  248. package/src/daemon/conversation-runtime-assembly.ts +145 -84
  249. package/src/daemon/conversation-slash.ts +37 -5
  250. package/src/daemon/conversation-surfaces.ts +45 -2
  251. package/src/daemon/conversation-tool-setup.ts +70 -3
  252. package/src/daemon/conversation-usage.ts +2 -0
  253. package/src/daemon/conversation.ts +54 -32
  254. package/src/daemon/disk-pressure-guard.ts +14 -2
  255. package/src/daemon/first-greeting.ts +10 -0
  256. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  257. package/src/daemon/handlers/config-a2a.ts +160 -0
  258. package/src/daemon/handlers/config-model.test.ts +2 -0
  259. package/src/daemon/handlers/conversations.ts +90 -3
  260. package/src/daemon/handlers/shared.ts +92 -29
  261. package/src/daemon/host-bash-proxy.ts +1 -1
  262. package/src/daemon/host-browser-proxy.ts +5 -5
  263. package/src/daemon/host-cu-proxy.ts +5 -5
  264. package/src/daemon/host-file-proxy.ts +5 -5
  265. package/src/daemon/host-proxy-base.ts +4 -4
  266. package/src/daemon/host-transfer-proxy.ts +11 -11
  267. package/src/daemon/lifecycle.ts +40 -23
  268. package/src/daemon/meet-manifest-loader.ts +1 -7
  269. package/src/daemon/message-protocol.ts +4 -0
  270. package/src/daemon/message-types/conversations.ts +14 -9
  271. package/src/daemon/message-types/document-comments.ts +50 -0
  272. package/src/daemon/message-types/home.ts +1 -13
  273. package/src/daemon/message-types/messages.ts +66 -7
  274. package/src/daemon/message-types/surfaces.ts +3 -1
  275. package/src/daemon/message-types/sync.ts +14 -0
  276. package/src/daemon/message-types/web-activity.ts +57 -0
  277. package/src/daemon/plugin-source-watcher.ts +135 -3
  278. package/src/daemon/process-message.ts +69 -12
  279. package/src/daemon/shutdown-handlers.ts +24 -5
  280. package/src/daemon/switch-inference-profile-tool.ts +52 -0
  281. package/src/daemon/tool-setup-types.ts +13 -0
  282. package/src/daemon/trust-context.ts +6 -0
  283. package/src/documents/document-comments-store.test.ts +338 -0
  284. package/src/documents/document-comments-store.ts +237 -0
  285. package/src/documents/document-store.ts +202 -0
  286. package/src/events/relationship-state-updated.ts +25 -0
  287. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
  288. package/src/heartbeat/heartbeat-service.ts +1 -0
  289. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  290. package/src/home/feed-types.ts +6 -1
  291. package/src/home/home-content-refresh.ts +52 -0
  292. package/src/home/home-greeting-cache.ts +69 -0
  293. package/src/home/home-greeting.ts +85 -0
  294. package/src/home/suggested-prompts.ts +168 -9
  295. package/src/ipc/gateway-flag-listener.ts +123 -0
  296. package/src/ipc/skill-routes/registries.ts +8 -12
  297. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  298. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  299. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
  300. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  301. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  302. package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
  303. package/src/memory/auto-analysis-enqueue.ts +5 -1
  304. package/src/memory/conversation-crud.ts +191 -100
  305. package/src/memory/conversation-starters-cadence.ts +3 -1
  306. package/src/memory/conversation-title-service.ts +19 -3
  307. package/src/memory/db-async-query.ts +214 -0
  308. package/src/memory/db-init.ts +26 -0
  309. package/src/memory/db-maintenance.ts +30 -21
  310. package/src/memory/delivery-crud.ts +41 -0
  311. package/src/memory/delivery-status.ts +141 -15
  312. package/src/memory/external-conversation-store.ts +32 -1
  313. package/src/memory/graph/bootstrap.ts +8 -1
  314. package/src/memory/graph/capability-seed.ts +7 -3
  315. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  316. package/src/memory/graph/extraction.ts +1 -5
  317. package/src/memory/graph/graph-search.ts +7 -1
  318. package/src/memory/indexer.ts +28 -18
  319. package/src/memory/job-handlers/cleanup.ts +76 -18
  320. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  321. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  322. package/src/memory/jobs-store.ts +14 -0
  323. package/src/memory/jobs-worker.ts +68 -15
  324. package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
  325. package/src/memory/llm-request-log-source-local.ts +7 -0
  326. package/src/memory/llm-request-log-source.ts +9 -2
  327. package/src/memory/llm-request-log-store.ts +43 -1
  328. package/src/memory/llm-usage-store.ts +24 -0
  329. package/src/memory/memory-retrospective-constants.ts +28 -0
  330. package/src/memory/memory-retrospective-enqueue.ts +11 -3
  331. package/src/memory/memory-retrospective-job.ts +413 -18
  332. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  333. package/src/memory/memory-v2-activation-log-store.ts +41 -14
  334. package/src/memory/migrations/100-core-tables.ts +1 -0
  335. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  336. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  337. package/src/memory/migrations/253-document-comments.ts +47 -0
  338. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  339. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  340. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  341. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  342. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  343. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  344. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  345. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  346. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  347. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  348. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  349. package/src/memory/migrations/index.ts +34 -0
  350. package/src/memory/migrations/registry.ts +58 -0
  351. package/src/memory/onboarding-events-store.ts +7 -0
  352. package/src/memory/schema/calls.ts +1 -0
  353. package/src/memory/schema/conversations.ts +3 -0
  354. package/src/memory/schema/infrastructure.ts +22 -0
  355. package/src/memory/tool-usage-store.ts +36 -8
  356. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  357. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  358. package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
  359. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
  362. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  363. package/src/memory/v2/__tests__/injection.test.ts +158 -112
  364. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  365. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  366. package/src/memory/v2/__tests__/router.test.ts +660 -4
  367. package/src/memory/v2/consolidation-job.ts +14 -0
  368. package/src/memory/v2/harness/compare.ts +57 -0
  369. package/src/memory/v2/harness/metrics.ts +124 -0
  370. package/src/memory/v2/harness/oracle.ts +145 -0
  371. package/src/memory/v2/harness/replay-input.ts +224 -0
  372. package/src/memory/v2/harness/retriever.ts +74 -0
  373. package/src/memory/v2/harness/router-retriever.ts +43 -0
  374. package/src/memory/v2/harness/runner.ts +106 -0
  375. package/src/memory/v2/harness/trace.ts +58 -0
  376. package/src/memory/v2/injection-events.ts +101 -0
  377. package/src/memory/v2/injection.ts +42 -25
  378. package/src/memory/v2/page-index.ts +209 -7
  379. package/src/memory/v2/page-store.ts +18 -0
  380. package/src/memory/v2/prompts/router.ts +26 -1
  381. package/src/memory/v2/qdrant.ts +14 -2
  382. package/src/memory/v2/router.ts +369 -62
  383. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  384. package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
  385. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  386. package/src/memory/v3/__tests__/edges.test.ts +563 -0
  387. package/src/memory/v3/__tests__/filter.test.ts +512 -0
  388. package/src/memory/v3/__tests__/gate.test.ts +574 -0
  389. package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
  390. package/src/memory/v3/__tests__/loop.test.ts +530 -0
  391. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  392. package/src/memory/v3/__tests__/scouts.test.ts +440 -0
  393. package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
  394. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  395. package/src/memory/v3/__tests__/traversal.test.ts +469 -0
  396. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  397. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  398. package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
  399. package/src/memory/v3/__tests__/validate.test.ts +245 -0
  400. package/src/memory/v3/auto-edges.ts +223 -0
  401. package/src/memory/v3/coactivation-store.ts +124 -0
  402. package/src/memory/v3/consolidation-job.ts +323 -0
  403. package/src/memory/v3/edge-learning-job.ts +160 -0
  404. package/src/memory/v3/edges.ts +249 -0
  405. package/src/memory/v3/filter.ts +281 -0
  406. package/src/memory/v3/gate.ts +334 -0
  407. package/src/memory/v3/index-composition.ts +113 -0
  408. package/src/memory/v3/llm-capture.ts +46 -0
  409. package/src/memory/v3/loop.ts +382 -0
  410. package/src/memory/v3/maintenance.ts +144 -0
  411. package/src/memory/v3/prompt-context.ts +33 -0
  412. package/src/memory/v3/prompts/consolidation.ts +458 -0
  413. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  414. package/src/memory/v3/retriever.ts +33 -0
  415. package/src/memory/v3/scouts.ts +420 -0
  416. package/src/memory/v3/shadow-middleware.ts +305 -0
  417. package/src/memory/v3/traversal.ts +206 -0
  418. package/src/memory/v3/tree-index.ts +237 -0
  419. package/src/memory/v3/tree-store.ts +394 -0
  420. package/src/memory/v3/tree-walk.ts +351 -0
  421. package/src/memory/v3/types.ts +65 -0
  422. package/src/memory/v3/validate.ts +300 -0
  423. package/src/messaging/providers/index.ts +7 -1
  424. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  425. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  426. package/src/messaging/providers/slack/adapter.ts +178 -25
  427. package/src/messaging/providers/slack/api.test.ts +54 -0
  428. package/src/messaging/providers/slack/api.ts +119 -3
  429. package/src/messaging/providers/slack/client.ts +12 -0
  430. package/src/messaging/providers/slack/deep-link.ts +20 -1
  431. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  432. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  433. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  434. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  435. package/src/messaging/providers/slack/send.test.ts +77 -0
  436. package/src/messaging/providers/slack/send.ts +8 -2
  437. package/src/messaging/providers/slack/types.ts +14 -0
  438. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
  439. package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
  440. package/src/notifications/adapters/macos.ts +18 -1
  441. package/src/notifications/adapters/platform.ts +1 -1
  442. package/src/notifications/conversation-seed-composer.ts +14 -2
  443. package/src/notifications/decision-engine.ts +1 -4
  444. package/src/notifications/deferred-emit.ts +135 -0
  445. package/src/notifications/emit-signal.ts +38 -50
  446. package/src/notifications/home-feed-side-effect.ts +60 -30
  447. package/src/oauth/connect-orchestrator.ts +3 -0
  448. package/src/oauth/credential-token-resolver.ts +2 -0
  449. package/src/oauth/manual-token-connection.ts +19 -0
  450. package/src/oauth/oauth-store.ts +12 -0
  451. package/src/oauth/seed-providers.ts +22 -0
  452. package/src/permissions/prompter.ts +8 -5
  453. package/src/permissions/question-prompter.ts +5 -2
  454. package/src/permissions/secret-prompter.ts +6 -3
  455. package/src/plugin-api/index.ts +4 -0
  456. package/src/plugin-api/types.ts +7 -33
  457. package/src/plugins/defaults/index.ts +6 -0
  458. package/src/plugins/defaults/injectors.ts +100 -20
  459. package/src/plugins/external-plugin-loader.ts +5 -68
  460. package/src/plugins/types.ts +11 -16
  461. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  462. package/src/prompts/__tests__/system-prompt.test.ts +46 -2
  463. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  464. package/src/prompts/normalize-onboarding.ts +40 -0
  465. package/src/prompts/persona-resolver.ts +36 -21
  466. package/src/prompts/sections.ts +69 -19
  467. package/src/prompts/system-prompt.ts +118 -216
  468. package/src/prompts/template-detection.ts +37 -0
  469. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  470. package/src/prompts/templates/BOOTSTRAP.md +10 -2
  471. package/src/prompts/templates/VOICE.md +3 -0
  472. package/src/prompts/templates/system-sections.ts +281 -9
  473. package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
  474. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  475. package/src/providers/anthropic/client.ts +159 -66
  476. package/src/providers/call-site-routing.ts +14 -2
  477. package/src/providers/connection-model-compat.ts +38 -0
  478. package/src/providers/connection-resolution.ts +16 -2
  479. package/src/providers/fireworks/client.ts +20 -2
  480. package/src/providers/gemini/client.ts +49 -6
  481. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  482. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  483. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  484. package/src/providers/inference/adapter-factory.ts +18 -1
  485. package/src/providers/inference/auth.ts +3 -3
  486. package/src/providers/inference/codex-token-refresh.ts +128 -0
  487. package/src/providers/inference/resolve-auth.ts +49 -6
  488. package/src/providers/minimax/client.ts +106 -0
  489. package/src/providers/model-catalog.ts +91 -1
  490. package/src/providers/model-intents.ts +1 -1
  491. package/src/providers/openai/chat-completions-provider.ts +63 -23
  492. package/src/providers/openai/codex-models.ts +18 -0
  493. package/src/providers/openai/responses-provider.ts +86 -23
  494. package/src/providers/openrouter/client.ts +5 -1
  495. package/src/providers/provider-send-message.ts +7 -1
  496. package/src/providers/retry.ts +34 -3
  497. package/src/providers/thinking-config.ts +26 -1
  498. package/src/providers/types.ts +25 -0
  499. package/src/providers/usage-tracking.ts +2 -0
  500. package/src/runtime/AGENTS.md +2 -2
  501. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  502. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  503. package/src/runtime/agent-wake.ts +152 -56
  504. package/src/runtime/assistant-event-hub.ts +76 -6
  505. package/src/runtime/auth/route-policy.ts +43 -3
  506. package/src/runtime/background-job-runner.ts +26 -0
  507. package/src/runtime/btw-sidechain.ts +0 -6
  508. package/src/runtime/channel-reply-delivery.ts +182 -47
  509. package/src/runtime/channel-retry-sweep.ts +141 -16
  510. package/src/runtime/http-types.ts +7 -6
  511. package/src/runtime/migrations/vbundle-builder.ts +10 -3
  512. package/src/runtime/pending-interactions.ts +50 -8
  513. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  514. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
  515. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  516. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
  517. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  518. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  519. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  520. package/src/runtime/routes/acp-routes.test.ts +255 -6
  521. package/src/runtime/routes/acp-routes.ts +8 -1
  522. package/src/runtime/routes/approval-routes.ts +4 -1
  523. package/src/runtime/routes/avatar-routes.ts +10 -10
  524. package/src/runtime/routes/background-wake-routes.ts +188 -0
  525. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  526. package/src/runtime/routes/btw-routes.ts +0 -6
  527. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  528. package/src/runtime/routes/content-source-routes.ts +78 -0
  529. package/src/runtime/routes/conversation-cli-routes.ts +147 -2
  530. package/src/runtime/routes/conversation-list-routes.ts +12 -4
  531. package/src/runtime/routes/conversation-management-routes.ts +77 -20
  532. package/src/runtime/routes/conversation-query-routes.ts +196 -31
  533. package/src/runtime/routes/conversation-routes.ts +472 -425
  534. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  535. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  536. package/src/runtime/routes/document-comments-routes.ts +287 -0
  537. package/src/runtime/routes/documents-routes.ts +33 -0
  538. package/src/runtime/routes/domain-routes.ts +60 -10
  539. package/src/runtime/routes/email-routes.ts +5 -2
  540. package/src/runtime/routes/events-routes.ts +54 -10
  541. package/src/runtime/routes/group-routes.ts +24 -8
  542. package/src/runtime/routes/home-feed-routes.ts +6 -3
  543. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  544. package/src/runtime/routes/host-browser-routes.ts +17 -2
  545. package/src/runtime/routes/host-cu-routes.ts +2 -2
  546. package/src/runtime/routes/identity-routes.ts +21 -0
  547. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  548. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  549. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  550. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  551. package/src/runtime/routes/index.ts +20 -4
  552. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  553. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  554. package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
  555. package/src/runtime/routes/integrations/a2a.ts +60 -1
  556. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  557. package/src/runtime/routes/log-export-routes.ts +39 -0
  558. package/src/runtime/routes/memory-item-routes.ts +8 -3
  559. package/src/runtime/routes/memory-v2-routes.ts +427 -0
  560. package/src/runtime/routes/memory-v3-routes.ts +316 -0
  561. package/src/runtime/routes/migration-routes.ts +21 -24
  562. package/src/runtime/routes/notification-routes.ts +19 -2
  563. package/src/runtime/routes/plugins-routes.ts +337 -0
  564. package/src/runtime/routes/question-routes.ts +4 -1
  565. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  566. package/src/runtime/routes/sanity-routes.ts +159 -0
  567. package/src/runtime/routes/secret-routes.ts +25 -5
  568. package/src/runtime/routes/settings-routes.ts +12 -11
  569. package/src/runtime/routes/slack-channel-routes.ts +188 -0
  570. package/src/runtime/routes/workspace-routes.ts +25 -10
  571. package/src/runtime/services/conversation-serializer.ts +30 -4
  572. package/src/runtime/sync/resource-sync-events.ts +106 -38
  573. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  574. package/src/runtime/sync/sync-publisher.ts +2 -1
  575. package/src/runtime/verification-outbound-actions.ts +73 -1
  576. package/src/schedule/integration-status.ts +3 -1
  577. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  578. package/src/security/oauth2-device-code.ts +307 -0
  579. package/src/security/oauth2.ts +26 -9
  580. package/src/security/secure-keys.ts +5 -0
  581. package/src/skills/catalog-install.ts +6 -2
  582. package/src/telemetry/types.ts +12 -0
  583. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  584. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  585. package/src/tools/acp/spawn.test.ts +119 -0
  586. package/src/tools/acp/spawn.ts +15 -2
  587. package/src/tools/apps/definitions.ts +2 -8
  588. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  589. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  590. package/src/tools/browser/__tests__/pinned-tabs.test.ts +150 -0
  591. package/src/tools/browser/browser-execution.ts +106 -0
  592. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  593. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  594. package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
  595. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
  596. package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
  597. package/src/tools/browser/cdp-client/factory.ts +171 -4
  598. package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
  599. package/src/tools/browser/cdp-client/types.ts +101 -0
  600. package/src/tools/browser/pinned-tabs.ts +146 -0
  601. package/src/tools/computer-use/definitions.ts +22 -78
  602. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  603. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  604. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  605. package/src/tools/credentials/vault.ts +3 -9
  606. package/src/tools/document/document-comment-tool.test.ts +379 -0
  607. package/src/tools/document/document-comment-tool.ts +156 -0
  608. package/src/tools/document/document-tool.ts +187 -2
  609. package/src/tools/execution-target.ts +21 -23
  610. package/src/tools/executor.ts +6 -1
  611. package/src/tools/filesystem/edit.ts +3 -9
  612. package/src/tools/filesystem/list.ts +3 -9
  613. package/src/tools/filesystem/read.ts +3 -9
  614. package/src/tools/filesystem/write.ts +3 -9
  615. package/src/tools/host-filesystem/edit.ts +3 -9
  616. package/src/tools/host-filesystem/read.ts +3 -9
  617. package/src/tools/host-filesystem/transfer.ts +3 -9
  618. package/src/tools/host-filesystem/write.ts +3 -9
  619. package/src/tools/host-terminal/host-shell.ts +3 -9
  620. package/src/tools/mcp/mcp-tool-factory.ts +1 -8
  621. package/src/tools/memory/register.test.ts +1 -1
  622. package/src/tools/memory/register.ts +4 -9
  623. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  624. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  625. package/src/tools/network/domain-normalize.ts +17 -0
  626. package/src/tools/network/web-fetch.ts +216 -73
  627. package/src/tools/network/web-search.ts +216 -98
  628. package/src/tools/registry.ts +7 -23
  629. package/src/tools/schema-transforms.ts +1 -1
  630. package/src/tools/skills/execute.ts +3 -9
  631. package/src/tools/skills/load.ts +3 -9
  632. package/src/tools/skills/skill-tool-factory.ts +1 -8
  633. package/src/tools/subagent/notify-parent.ts +3 -9
  634. package/src/tools/system/request-permission.ts +3 -9
  635. package/src/tools/terminal/safe-env.ts +3 -2
  636. package/src/tools/terminal/shell.ts +3 -9
  637. package/src/tools/tool-approval-handler.ts +19 -12
  638. package/src/tools/tool-defaults.ts +94 -0
  639. package/src/tools/types.ts +31 -98
  640. package/src/tools/ui-surface/definitions.ts +9 -23
  641. package/src/types/onboarding-context.ts +4 -0
  642. package/src/usage/pricing.ts +23 -0
  643. package/src/usage/types.ts +12 -0
  644. package/src/util/__tests__/favicon.test.ts +84 -0
  645. package/src/util/favicon.ts +40 -0
  646. package/src/util/logger.ts +16 -7
  647. package/src/util/platform.ts +7 -7
  648. package/src/util/sqlite3-runtime.ts +65 -0
  649. package/src/workspace/git-service.ts +75 -4
  650. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  651. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  652. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  653. package/src/workspace/migrations/registry.ts +4 -0
  654. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  655. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  656. package/src/config/bundled-skills/document/SKILL.md +0 -54
  657. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  658. package/src/daemon/seed-files.ts +0 -18
  659. package/src/prompts/cache-boundary.ts +0 -8
  660. package/src/runtime/routes/interface-routes.ts +0 -43
  661. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  662. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  663. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  664. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  665. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -77,6 +77,14 @@ mock.module("../messaging/providers/slack/adapter.js", () => ({
77
77
  _account: string | undefined,
78
78
  fn: (token: string) => Promise<unknown>,
79
79
  ) => fn("test-slack-token"),
80
+ resolveSlackBotUserId: async (
81
+ _account: string | undefined,
82
+ botId: string,
83
+ ) => {
84
+ if (botId === "B_ASSISTANT") return "U_BOT";
85
+ if (botId === "B_OTHER") return "U_OTHER_BOT";
86
+ return null;
87
+ },
80
88
  }));
81
89
 
82
90
  // ---------------------------------------------------------------------------
@@ -85,6 +93,11 @@ mock.module("../messaging/providers/slack/adapter.js", () => ({
85
93
 
86
94
  import { v4 as uuid } from "uuid";
87
95
 
96
+ import {
97
+ loadRawConfig,
98
+ saveRawConfig,
99
+ setNestedValue,
100
+ } from "../config/loader.js";
88
101
  import { upsertContactChannel } from "../contacts/contacts-write.js";
89
102
  import {
90
103
  type ChannelCapabilities,
@@ -97,6 +110,7 @@ import { recordInbound } from "../memory/delivery-crud.js";
97
110
  import type { Message as MessagingMessage } from "../messaging/provider-types.js";
98
111
  import * as slackBackfill from "../messaging/providers/slack/backfill.js";
99
112
  import {
113
+ buildSlackTimezoneMetadata,
100
114
  readSlackMetadata,
101
115
  writeSlackMetadata,
102
116
  } from "../messaging/providers/slack/message-metadata.js";
@@ -162,6 +176,20 @@ function resetState(): void {
162
176
  backfillDmMock.mockImplementation(async () => []);
163
177
  downloadSlackFileMock.mockReset();
164
178
  downloadSlackFileMock.mockResolvedValue(null);
179
+ setConfiguredSlackBotUserId("U_BOT");
180
+ setConfiguredUserTimezone(undefined);
181
+ }
182
+
183
+ function setConfiguredSlackBotUserId(botUserId: string): void {
184
+ const raw = loadRawConfig();
185
+ setNestedValue(raw, "slack.botUserId", botUserId);
186
+ saveRawConfig(raw);
187
+ }
188
+
189
+ function setConfiguredUserTimezone(userTimezone: string | undefined): void {
190
+ const raw = loadRawConfig();
191
+ setNestedValue(raw, "ui.userTimezone", userTimezone);
192
+ saveRawConfig(raw);
165
193
  }
166
194
 
167
195
  let convCounter = 0;
@@ -269,6 +297,12 @@ interface PersistedRow {
269
297
  threadTs: string | undefined;
270
298
  displayName: string | undefined;
271
299
  actorExternalUserId: string | undefined;
300
+ actorTimezone: string | undefined;
301
+ actorTimezoneLabel: string | undefined;
302
+ actorTimezoneOffsetSeconds: number | undefined;
303
+ timestampTimezone: string | undefined;
304
+ timestampTimezoneLabel: string | undefined;
305
+ speakerTimezoneLabel: string | undefined;
272
306
  slackFiles: Array<{ name: string; mimetype?: string }> | undefined;
273
307
  provenanceTrustClass: string | undefined;
274
308
  provenanceSourceChannel: string | undefined;
@@ -296,6 +330,12 @@ function readPersistedSlackRows(conversationId: string): PersistedRow[] {
296
330
  threadTs: undefined,
297
331
  displayName: undefined,
298
332
  actorExternalUserId: undefined,
333
+ actorTimezone: undefined,
334
+ actorTimezoneLabel: undefined,
335
+ actorTimezoneOffsetSeconds: undefined,
336
+ timestampTimezone: undefined,
337
+ timestampTimezoneLabel: undefined,
338
+ speakerTimezoneLabel: undefined,
299
339
  slackFiles: undefined,
300
340
  provenanceTrustClass: undefined,
301
341
  provenanceSourceChannel: undefined,
@@ -336,6 +376,12 @@ function readPersistedSlackRows(conversationId: string): PersistedRow[] {
336
376
  threadTs: slackMeta?.threadTs,
337
377
  displayName: slackMeta?.displayName,
338
378
  actorExternalUserId: slackMeta?.actorExternalUserId,
379
+ actorTimezone: slackMeta?.actorTimezone,
380
+ actorTimezoneLabel: slackMeta?.actorTimezoneLabel,
381
+ actorTimezoneOffsetSeconds: slackMeta?.actorTimezoneOffsetSeconds,
382
+ timestampTimezone: slackMeta?.timestampTimezone,
383
+ timestampTimezoneLabel: slackMeta?.timestampTimezoneLabel,
384
+ speakerTimezoneLabel: slackMeta?.speakerTimezoneLabel,
339
385
  slackFiles: slackMeta?.slackFiles?.map((file) => ({
340
386
  name: file.name,
341
387
  ...(file.mimetype ? { mimetype: file.mimetype } : {}),
@@ -978,7 +1024,7 @@ describe("triggerSlackThreadBackfillIfNeeded — gap detection and persistence",
978
1024
  expect(contextImage).toBeDefined();
979
1025
  });
980
1026
 
981
- test("backfilled bot Slack image files are persisted as user image history", async () => {
1027
+ test("backfilled assistant Slack image files are persisted as assistant image history", async () => {
982
1028
  const conv = createTestConversation();
983
1029
 
984
1030
  seedSlackRow(conv.id, "1234.0", undefined, "parent already here");
@@ -992,7 +1038,7 @@ describe("triggerSlackThreadBackfillIfNeeded — gap detection and persistence",
992
1038
  id: "1234.1",
993
1039
  text: "bot posted a diagram",
994
1040
  threadId: "1234.0",
995
- sender: { id: "B_IMAGE", name: "Build Bot" },
1041
+ sender: { id: "U_BOT", name: "Douglas" },
996
1042
  metadata: {
997
1043
  isBot: true,
998
1044
  slackFiles: [
@@ -1020,7 +1066,7 @@ describe("triggerSlackThreadBackfillIfNeeded — gap detection and persistence",
1020
1066
  row.content.includes("bot posted a diagram"),
1021
1067
  );
1022
1068
  expect(botRow).toBeDefined();
1023
- expect(botRow?.role).toBe("user");
1069
+ expect(botRow?.role).toBe("assistant");
1024
1070
  const blocks = JSON.parse(botRow!.content) as Message["content"];
1025
1071
  const textBlock = blocks.find(
1026
1072
  (block): block is Extract<Message["content"][number], { type: "text" }> =>
@@ -1042,6 +1088,27 @@ describe("triggerSlackThreadBackfillIfNeeded — gap detection and persistence",
1042
1088
  });
1043
1089
 
1044
1090
  expect(context).not.toBeNull();
1091
+ const contextBotMessage = context!.messages.find(
1092
+ (message) =>
1093
+ message.role === "assistant" &&
1094
+ message.content.some(
1095
+ (block) =>
1096
+ block.type === "text" &&
1097
+ block.text.includes("bot posted a diagram"),
1098
+ ),
1099
+ );
1100
+ expect(contextBotMessage).toBeDefined();
1101
+ expect(
1102
+ contextBotMessage!.content
1103
+ .filter(
1104
+ (
1105
+ block,
1106
+ ): block is Extract<Message["content"][number], { type: "text" }> =>
1107
+ block.type === "text",
1108
+ )
1109
+ .map((block) => block.text)
1110
+ .join("\n"),
1111
+ ).not.toContain("<external_content");
1045
1112
  const contextImage = context!.messages
1046
1113
  .flatMap((message) => message.content)
1047
1114
  .find((block) => block.type === "image");
@@ -1082,6 +1149,62 @@ describe("triggerSlackThreadBackfillIfNeeded — gap detection and persistence",
1082
1149
  expect(persisted.provenanceRequesterIdentifier).toBe("U_ANITA");
1083
1150
  });
1084
1151
 
1152
+ test("backfilled Slack timezone metadata derives timestamp and speaker fields", async () => {
1153
+ const conv = createTestConversation();
1154
+ setConfiguredUserTimezone("America/Denver");
1155
+
1156
+ backfillThreadMock.mockImplementation(async () => [
1157
+ makeBackfillMessage({
1158
+ id: "1234.0",
1159
+ text: "non-guardian context",
1160
+ threadId: undefined,
1161
+ sender: { id: "U_ANITA", name: "Anita" },
1162
+ metadata: {
1163
+ actorTimezone: "America/New_York",
1164
+ actorTimezoneLabel: "ET",
1165
+ actorTimezoneOffsetSeconds: -18000,
1166
+ },
1167
+ }),
1168
+ makeBackfillMessage({
1169
+ id: "1234.1",
1170
+ text: "trusted context",
1171
+ threadId: "1234.0",
1172
+ sender: { id: "U_GUARDIAN", name: "Guardian User" },
1173
+ metadata: {
1174
+ actorTimezone: "America/Denver",
1175
+ actorTimezoneLabel: "MT",
1176
+ actorTimezoneOffsetSeconds: -25200,
1177
+ },
1178
+ }),
1179
+ ]);
1180
+
1181
+ await triggerSlackThreadBackfillIfNeeded({
1182
+ conversationId: conv.id,
1183
+ channelId: SLACK_CHANNEL_ID,
1184
+ threadTs: "1234.0",
1185
+ guardianExternalUserId: "U_GUARDIAN",
1186
+ });
1187
+
1188
+ const persisted = readPersistedSlackRows(conv.id);
1189
+ const nonGuardian = persisted.find((p) => p.channelTs === "1234.0");
1190
+ expect(nonGuardian).toBeDefined();
1191
+ expect(nonGuardian?.actorTimezone).toBe("America/New_York");
1192
+ expect(nonGuardian?.actorTimezoneLabel).toBe("ET");
1193
+ expect(nonGuardian?.actorTimezoneOffsetSeconds).toBe(-18000);
1194
+ expect(nonGuardian?.timestampTimezone).toBe("America/Denver");
1195
+ expect(nonGuardian?.timestampTimezoneLabel).toBe("MT");
1196
+ expect(nonGuardian?.speakerTimezoneLabel).toBe("ET");
1197
+
1198
+ const guardian = persisted.find((p) => p.channelTs === "1234.1");
1199
+ expect(guardian).toBeDefined();
1200
+ expect(guardian?.actorTimezone).toBe("America/Denver");
1201
+ expect(guardian?.actorTimezoneLabel).toBe("MT");
1202
+ expect(guardian?.actorTimezoneOffsetSeconds).toBe(-25200);
1203
+ expect(guardian?.timestampTimezone).toBe("America/Denver");
1204
+ expect(guardian?.timestampTimezoneLabel).toBe("MT");
1205
+ expect(guardian?.speakerTimezoneLabel).toBeUndefined();
1206
+ });
1207
+
1085
1208
  test("backfilled guardian-authored text is persisted raw with guardian provenance", async () => {
1086
1209
  const conv = createTestConversation();
1087
1210
 
@@ -1115,12 +1238,13 @@ describe("triggerSlackThreadBackfillIfNeeded — gap detection and persistence",
1115
1238
  expect(persisted.provenanceRequesterIdentifier).toBe("U_GUARDIAN");
1116
1239
  });
1117
1240
 
1118
- test("backfilled bot-authored text is persisted raw as user history", async () => {
1241
+ test("backfilled assistant-authored text is persisted raw as assistant history", async () => {
1119
1242
  const conv = createTestConversation();
1243
+ setConfiguredUserTimezone("America/Denver");
1120
1244
 
1121
1245
  backfillThreadMock.mockImplementation(async () => [
1122
1246
  makeBackfillMessage({
1123
- id: "1234.0",
1247
+ id: "1772681880.000000",
1124
1248
  text: "earlier assistant reply",
1125
1249
  threadId: undefined,
1126
1250
  sender: { id: "U_BOT", name: "Douglas" },
@@ -1131,20 +1255,150 @@ describe("triggerSlackThreadBackfillIfNeeded — gap detection and persistence",
1131
1255
  await triggerSlackThreadBackfillIfNeeded({
1132
1256
  conversationId: conv.id,
1133
1257
  channelId: SLACK_CHANNEL_ID,
1134
- threadTs: "1234.0",
1258
+ threadTs: "1772681880.000000",
1135
1259
  });
1136
1260
 
1137
1261
  const [persisted] = readPersistedSlackRows(conv.id).filter(
1138
- (p) => p.channelTs === "1234.0",
1262
+ (p) => p.channelTs === "1772681880.000000",
1139
1263
  );
1140
1264
  expect(persisted).toBeDefined();
1141
- expect(persisted.role).toBe("user");
1265
+ expect(persisted.role).toBe("assistant");
1142
1266
  expect(persisted.rawContent).toBe("earlier assistant reply");
1143
1267
  expect(persisted.rawContent).not.toContain("<external_content");
1144
1268
  expect(persisted.actorExternalUserId).toBe("U_BOT");
1269
+ expect(persisted.timestampTimezone).toBe("America/Denver");
1270
+ expect(persisted.timestampTimezoneLabel).toBe("MT");
1271
+ expect(persisted.speakerTimezoneLabel).toBeUndefined();
1145
1272
  expect(persisted.provenanceTrustClass).toBe("unknown");
1146
1273
  expect(persisted.provenanceSourceChannel).toBe("slack");
1147
1274
  expect(persisted.provenanceRequesterIdentifier).toBe("U_BOT");
1275
+
1276
+ const context = loadSlackChronologicalContext(conv.id, SLACK_CHANNEL_CAPS, {
1277
+ loader: readMessageRowsByConversation,
1278
+ trustClass: "guardian",
1279
+ });
1280
+ expect(context).not.toBeNull();
1281
+ expect(context!.messages).toEqual([
1282
+ {
1283
+ role: "assistant",
1284
+ content: [
1285
+ {
1286
+ type: "text",
1287
+ text: "[mar 4 2026 8:38 PM MT assistant] earlier assistant reply",
1288
+ },
1289
+ ],
1290
+ },
1291
+ ]);
1292
+ });
1293
+
1294
+ test("backfilled bot-id-only assistant text resolves to assistant history", async () => {
1295
+ const conv = createTestConversation();
1296
+
1297
+ backfillThreadMock.mockImplementation(async () => [
1298
+ makeBackfillMessage({
1299
+ id: "1234.0",
1300
+ text: "earlier assistant reply from bot id",
1301
+ threadId: undefined,
1302
+ sender: { id: "B_ASSISTANT", name: "Douglas" },
1303
+ metadata: { isBot: true, slackBotId: "B_ASSISTANT" },
1304
+ }),
1305
+ ]);
1306
+
1307
+ await triggerSlackThreadBackfillIfNeeded({
1308
+ conversationId: conv.id,
1309
+ channelId: SLACK_CHANNEL_ID,
1310
+ threadTs: "1234.0",
1311
+ });
1312
+
1313
+ const [persisted] = readPersistedSlackRows(conv.id).filter(
1314
+ (p) => p.channelTs === "1234.0",
1315
+ );
1316
+ expect(persisted).toBeDefined();
1317
+ expect(persisted.role).toBe("assistant");
1318
+ expect(persisted.rawContent).toBe("earlier assistant reply from bot id");
1319
+ expect(persisted.actorExternalUserId).toBe("B_ASSISTANT");
1320
+ expect(persisted.provenanceRequesterIdentifier).toBe("B_ASSISTANT");
1321
+ });
1322
+
1323
+ test("backfilled third-party bot-authored text stays user history", async () => {
1324
+ const conv = createTestConversation();
1325
+
1326
+ backfillThreadMock.mockImplementation(async () => [
1327
+ makeBackfillMessage({
1328
+ id: "1234.0",
1329
+ text: "deployment bot status update",
1330
+ threadId: undefined,
1331
+ sender: { id: "U_OTHER_BOT", name: "Deploy Bot" },
1332
+ metadata: { isBot: true, slackBotId: "B_OTHER" },
1333
+ }),
1334
+ ]);
1335
+
1336
+ await triggerSlackThreadBackfillIfNeeded({
1337
+ conversationId: conv.id,
1338
+ channelId: SLACK_CHANNEL_ID,
1339
+ threadTs: "1234.0",
1340
+ });
1341
+
1342
+ const [persisted] = readPersistedSlackRows(conv.id).filter(
1343
+ (p) => p.channelTs === "1234.0",
1344
+ );
1345
+ expect(persisted).toBeDefined();
1346
+ expect(persisted.role).toBe("user");
1347
+ expect(persisted.rawContent).toBe("deployment bot status update");
1348
+ expect(persisted.actorExternalUserId).toBe("U_OTHER_BOT");
1349
+ expect(persisted.provenanceTrustClass).toBe("unknown");
1350
+ expect(persisted.provenanceSourceChannel).toBe("slack");
1351
+ expect(persisted.provenanceRequesterIdentifier).toBe("U_OTHER_BOT");
1352
+ });
1353
+
1354
+ test("skips Slack assistant new-thread placeholder during backfill", async () => {
1355
+ const conv = createTestConversation();
1356
+
1357
+ backfillThreadMock.mockImplementation(async () => [
1358
+ makeBackfillMessage({
1359
+ id: "1234.0",
1360
+ text: "New Assistant Thread",
1361
+ threadId: undefined,
1362
+ sender: { id: "B_ASSISTANT", name: "Ada" },
1363
+ metadata: { isBot: true, slackBotId: "B_ASSISTANT" },
1364
+ }),
1365
+ makeBackfillMessage({
1366
+ id: "1234.1",
1367
+ text: "real bot context",
1368
+ threadId: "1234.0",
1369
+ sender: { id: "B_ASSISTANT", name: "Ada" },
1370
+ metadata: { isBot: true, slackBotId: "B_ASSISTANT" },
1371
+ }),
1372
+ makeBackfillMessage({
1373
+ id: "1234.2",
1374
+ text: "New Assistant Thread",
1375
+ threadId: undefined,
1376
+ sender: { id: "B_OTHER", name: "Build Bot" },
1377
+ metadata: { isBot: true, slackBotId: "B_OTHER" },
1378
+ }),
1379
+ ]);
1380
+
1381
+ const result = await triggerSlackThreadBackfillIfNeeded({
1382
+ conversationId: conv.id,
1383
+ channelId: SLACK_CHANNEL_ID,
1384
+ threadTs: "1234.0",
1385
+ });
1386
+
1387
+ expect(result.fetched).toBe(3);
1388
+ expect(result.persisted).toBe(2);
1389
+ const rows = readPersistedSlackRows(conv.id);
1390
+ expect(rows.map((row) => row.rawContent).sort()).toEqual([
1391
+ "New Assistant Thread",
1392
+ "real bot context",
1393
+ ]);
1394
+ const assistantRow = rows.find(
1395
+ (row) => row.rawContent === "real bot context",
1396
+ );
1397
+ expect(assistantRow?.role).toBe("assistant");
1398
+ expect(assistantRow?.actorExternalUserId).toBe("B_ASSISTANT");
1399
+ expect(rows.some((row) => row.actorExternalUserId === "B_OTHER")).toBe(
1400
+ true,
1401
+ );
1148
1402
  });
1149
1403
 
1150
1404
  test("backfilled non-bot message with empty text is persisted unwrapped", async () => {
@@ -1775,13 +2029,25 @@ function buildSlackDmRequest(
1775
2029
 
1776
2030
  interface SlackInboundProcessOptions {
1777
2031
  displayContent?: string;
1778
- slackRuntimeContextNotice?: string;
2032
+ transport?: {
2033
+ channelId?: string;
2034
+ hints?: string[];
2035
+ uxBrief?: string;
2036
+ chatType?: string;
2037
+ clientTimezone?: string;
2038
+ };
1779
2039
  slackInbound?: {
1780
2040
  channelId: string;
1781
2041
  channelTs: string;
1782
2042
  threadTs?: string;
1783
2043
  displayName?: string;
1784
2044
  actorExternalUserId?: string;
2045
+ actorTimezone?: string;
2046
+ actorTimezoneLabel?: string;
2047
+ actorTimezoneOffsetSeconds?: number;
2048
+ timestampTimezone?: string;
2049
+ timestampTimezoneLabel?: string;
2050
+ speakerTimezoneLabel?: string;
1785
2051
  };
1786
2052
  }
1787
2053
 
@@ -1808,6 +2074,15 @@ function persistSlackInboundFromProcessMessage(
1808
2074
  ...(slackInbound.actorExternalUserId
1809
2075
  ? { actorExternalUserId: slackInbound.actorExternalUserId }
1810
2076
  : {}),
2077
+ ...buildSlackTimezoneMetadata({
2078
+ actorTimezone: slackInbound.actorTimezone,
2079
+ actorTimezoneLabel: slackInbound.actorTimezoneLabel,
2080
+ actorTimezoneOffsetSeconds:
2081
+ slackInbound.actorTimezoneOffsetSeconds,
2082
+ timestampTimezone: slackInbound.timestampTimezone,
2083
+ timestampTimezoneLabel: slackInbound.timestampTimezoneLabel,
2084
+ speakerTimezoneLabel: slackInbound.speakerTimezoneLabel,
2085
+ }),
1811
2086
  eventKind: "message",
1812
2087
  }),
1813
2088
  }
@@ -1934,18 +2209,15 @@ describe("handleChannelInbound — Slack thread backfill wiring", () => {
1934
2209
  ]);
1935
2210
 
1936
2211
  let capturedHints: string[] | undefined;
1937
- let capturedSlackNotice: string | undefined;
1938
2212
  const processMessage = async (
1939
2213
  _conversationId: string,
1940
2214
  _content: string,
1941
2215
  _attachmentIds?: string[],
1942
2216
  options?: {
1943
2217
  transport?: { hints?: string[] };
1944
- slackRuntimeContextNotice?: string;
1945
2218
  },
1946
2219
  ): Promise<{ messageId: string }> => {
1947
2220
  capturedHints = options?.transport?.hints;
1948
- capturedSlackNotice = options?.slackRuntimeContextNotice;
1949
2221
  return { messageId: "agent-result-id" };
1950
2222
  };
1951
2223
  setAdapterProcessMessage(processMessage);
@@ -1992,16 +2264,7 @@ describe("handleChannelInbound — Slack thread backfill wiring", () => {
1992
2264
  expect(channelTimestamps.has("1234.0")).toBe(true);
1993
2265
  expect(channelTimestamps.has("1234.1")).toBe(true);
1994
2266
 
1995
- expect(
1996
- capturedHints?.some((hint) => hint.includes("joined an existing thread")),
1997
- ).not.toBe(true);
1998
- expect(capturedSlackNotice).toContain("joined an existing thread");
1999
- const contents = db.$client
2000
- .prepare("SELECT content FROM messages")
2001
- .all() as Array<{ content: string }>;
2002
- expect(
2003
- contents.some((row) => row.content.includes("Slack context note")),
2004
- ).toBe(false);
2267
+ expect(capturedHints ?? []).toEqual([]);
2005
2268
  });
2006
2269
 
2007
2270
  test("live Slack non-guardian passes raw displayContent while wrapping model content", async () => {
@@ -2009,6 +2272,14 @@ describe("handleChannelInbound — Slack thread backfill wiring", () => {
2009
2272
  const captured = await handleAndCaptureLiveSlackProcessMessage(
2010
2273
  buildSlackChannelRequest("1700000000.000300", {
2011
2274
  content: rawContent,
2275
+ clientTimezone: "America/Los_Angeles",
2276
+ sourceMetadata: {
2277
+ messageId: "1700000000.000300",
2278
+ chatType: "channel",
2279
+ timezone: "America/New_York",
2280
+ timezoneLabel: "Eastern Time",
2281
+ timezoneOffsetSeconds: -18000,
2282
+ },
2012
2283
  }),
2013
2284
  );
2014
2285
 
@@ -2020,10 +2291,27 @@ describe("handleChannelInbound — Slack thread backfill wiring", () => {
2020
2291
  expect(captured.options?.slackInbound?.actorExternalUserId).toBe(
2021
2292
  HTTP_SLACK_USER_ID,
2022
2293
  );
2294
+ expect(captured.options?.transport?.clientTimezone).toBe(
2295
+ "America/Los_Angeles",
2296
+ );
2297
+ expect(captured.options?.slackInbound?.actorTimezone).toBe(
2298
+ "America/New_York",
2299
+ );
2300
+ expect(captured.options?.slackInbound?.timestampTimezone).toBe(
2301
+ "America/Los_Angeles",
2302
+ );
2303
+ expect(captured.options?.slackInbound?.timestampTimezoneLabel).toBe("PT");
2304
+ expect(captured.options?.slackInbound?.speakerTimezoneLabel).toBe(
2305
+ "Eastern Time",
2306
+ );
2023
2307
 
2024
2308
  const persisted = readMessagesByConversation(captured.conversationId);
2025
2309
  expect(persisted).toHaveLength(1);
2026
2310
  expect(persisted[0].content).toBe(rawContent);
2311
+ const [persistedSlackRow] = readPersistedSlackRows(captured.conversationId);
2312
+ expect(persistedSlackRow?.timestampTimezone).toBe("America/Los_Angeles");
2313
+ expect(persistedSlackRow?.timestampTimezoneLabel).toBe("PT");
2314
+ expect(persistedSlackRow?.speakerTimezoneLabel).toBe("Eastern Time");
2027
2315
  });
2028
2316
 
2029
2317
  test("live Slack attachment-only passes empty raw displayContent for persistence", async () => {
@@ -2236,6 +2524,66 @@ describe("handleChannelInbound — Slack thread backfill wiring", () => {
2236
2524
  expect(backfillThreadMock).not.toHaveBeenCalled();
2237
2525
  });
2238
2526
 
2527
+ test("threaded Slack DMs use thread backfill instead of whole-DM backfill", async () => {
2528
+ const dmChannelId = "D0HTTPAPPTHREAD";
2529
+ const threadTs = "1700000000.000100";
2530
+ const inboundTs = "1700000000.000300";
2531
+ seedHttpActiveMember(dmChannelId);
2532
+ backfillDmMock.mockImplementation(async () => {
2533
+ throw new Error("whole-DM backfill should not run for threaded DMs");
2534
+ });
2535
+ backfillThreadMock.mockImplementation(async () => [
2536
+ makeBackfillMessage({
2537
+ id: threadTs,
2538
+ conversationId: dmChannelId,
2539
+ text: "app DM thread root",
2540
+ sender: { id: HTTP_SLACK_USER_ID, name: HTTP_SLACK_DISPLAY_NAME },
2541
+ }),
2542
+ makeBackfillMessage({
2543
+ id: "1700000000.000200",
2544
+ conversationId: dmChannelId,
2545
+ text: "app DM thread context",
2546
+ threadId: threadTs,
2547
+ sender: { id: HTTP_SLACK_USER_ID, name: HTTP_SLACK_DISPLAY_NAME },
2548
+ }),
2549
+ ]);
2550
+
2551
+ const processMessage = async (
2552
+ conversationId: string,
2553
+ content: string,
2554
+ _attachmentIds?: string[],
2555
+ options?: SlackInboundProcessOptions,
2556
+ ): Promise<{ messageId: string }> => ({
2557
+ messageId: persistSlackInboundFromProcessMessage(
2558
+ conversationId,
2559
+ content,
2560
+ options,
2561
+ ),
2562
+ });
2563
+ setAdapterProcessMessage(processMessage);
2564
+
2565
+ const resp = await handleChannelInbound(
2566
+ buildSlackDmRequest(dmChannelId, inboundTs, {
2567
+ sourceMetadata: {
2568
+ messageId: inboundTs,
2569
+ threadId: threadTs,
2570
+ chatType: "im",
2571
+ },
2572
+ }),
2573
+ processMessage,
2574
+ TEST_BEARER_TOKEN,
2575
+ );
2576
+
2577
+ expect(resp.status).toBe(200);
2578
+ await new Promise((resolve) => setTimeout(resolve, 100));
2579
+
2580
+ expect(backfillDmMock).not.toHaveBeenCalled();
2581
+ expect(backfillThreadMock.mock.calls.length).toBeGreaterThanOrEqual(1);
2582
+ const [calledChannel, calledThread] = backfillThreadMock.mock.calls[0];
2583
+ expect(calledChannel).toBe(dmChannelId);
2584
+ expect(calledThread).toBe(threadTs);
2585
+ });
2586
+
2239
2587
  test("second thread reply within the TTL window can fetch a newer bounded gap", async () => {
2240
2588
  backfillThreadMock.mockImplementation(async () => [
2241
2589
  makeBackfillMessage({ id: "5678.0", text: "parent" }),
@@ -26,11 +26,7 @@ const fakeTool = {
26
26
  description: "Run a shell command",
27
27
  category: "shell",
28
28
  defaultRiskLevel: "high",
29
- getDefinition: () => ({
30
- name: "bash",
31
- description: "Run a shell command",
32
- input_schema: {},
33
- }),
29
+ input_schema: {},
34
30
  execute: async () => ({ content: "ok", isError: false }),
35
31
  };
36
32
 
@@ -95,7 +95,7 @@ mock.module("../permissions/checker.js", () => ({
95
95
  mock.module("../memory/tool-usage-store.js", () => ({
96
96
  recordToolInvocation: () => {},
97
97
  getRecentInvocations: () => [],
98
- rotateToolInvocations: () => 0,
98
+ rotateToolInvocations: async () => 0,
99
99
  }));
100
100
 
101
101
  // ── Tool registry: return a stub tool whose execute records the call ─
@@ -113,7 +113,7 @@ mock.module("../tools/registry.js", () => ({
113
113
  description: "test tool",
114
114
  category: "test",
115
115
  defaultRiskLevel: "low",
116
- getDefinition: () => ({}),
116
+ input_schema: {},
117
117
  execute: async (input: Record<string, unknown>) => {
118
118
  lastToolCall = { name, input };
119
119
  return fakeToolResult;
@@ -421,11 +421,8 @@ describe("Tool execution pipeline benchmark", () => {
421
421
  description: `Benchmark tool (${sleepMs}ms)`,
422
422
  category: "benchmark",
423
423
  defaultRiskLevel: RiskLevel.Low,
424
- getDefinition: () => ({
425
- name,
426
- description: `Benchmark tool (${sleepMs}ms)`,
427
- input_schema: { type: "object" as const, properties: {} },
428
- }),
424
+ executionTarget: "sandbox",
425
+ input_schema: { type: "object" as const, properties: {} },
429
426
  execute: async (): Promise<ToolExecutionResult> => {
430
427
  if (sleepMs > 0) {
431
428
  await new Promise((r) => setTimeout(r, sleepMs));
@@ -79,7 +79,7 @@ mock.module("../memory/conversation-crud.js", () => ({
79
79
  mock.module("../memory/tool-usage-store.js", () => ({
80
80
  recordToolInvocation: () => {},
81
81
  getRecentInvocations: () => [],
82
- rotateToolInvocations: () => 0,
82
+ rotateToolInvocations: async () => 0,
83
83
  }));
84
84
 
85
85
  mock.module("../tools/registry.js", () => ({
@@ -95,7 +95,7 @@ mock.module("../tools/registry.js", () => ({
95
95
  origin: "skill" as const,
96
96
  ownerSkillId: "test-skill",
97
97
  executionTarget: "host" as const,
98
- getDefinition: () => ({}),
98
+ input_schema: {},
99
99
  execute: async () => {
100
100
  if (toolThrow) throw toolThrow;
101
101
  return fakeToolResult;
@@ -111,7 +111,7 @@ mock.module("../tools/registry.js", () => ({
111
111
  origin: "skill" as const,
112
112
  ownerSkillId: "test-skill",
113
113
  executionTarget: "sandbox" as const,
114
- getDefinition: () => ({}),
114
+ input_schema: {},
115
115
  execute: async () => {
116
116
  if (toolThrow) throw toolThrow;
117
117
  return fakeToolResult;
@@ -129,19 +129,29 @@ mock.module("../tools/registry.js", () => ({
129
129
  origin: "skill" as const,
130
130
  ownerSkillId: "test-skill",
131
131
  executionTarget: "sandbox" as const,
132
- getDefinition: () => ({}),
132
+ input_schema: {},
133
133
  execute: async () => {
134
134
  if (toolThrow) throw toolThrow;
135
135
  return fakeToolResult;
136
136
  },
137
137
  };
138
138
  }
139
+ // Mirror what the real loader stamps onto a tool at registration time
140
+ // (every registered Tool has `executionTarget` set). Mirror the
141
+ // prefix heuristic here so the tests that exercise built-in tools
142
+ // (`bash`, `host_bash`, `file_read`) still observe production-shaped
143
+ // executionTarget values.
144
+ const executionTarget =
145
+ name.startsWith("host_") || name.startsWith("computer_use_")
146
+ ? ("host" as const)
147
+ : ("sandbox" as const);
139
148
  return {
140
149
  name,
141
150
  description: "test tool",
142
151
  category: "test",
143
152
  defaultRiskLevel: "low",
144
- getDefinition: () => ({}),
153
+ executionTarget,
154
+ input_schema: {},
145
155
  execute: async () => {
146
156
  if (toolThrow) throw toolThrow;
147
157
  return fakeToolResult;