@vellumai/assistant 0.8.2 → 0.8.4

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 (503) hide show
  1. package/ARCHITECTURE.md +11 -12
  2. package/docker-entrypoint.sh +13 -2
  3. package/docker-init-apt-root.sh +79 -6
  4. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  5. package/openapi.yaml +945 -36
  6. package/package.json +1 -1
  7. package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
  8. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  9. package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
  10. package/src/__tests__/agent-loop.test.ts +88 -3
  11. package/src/__tests__/anthropic-provider.test.ts +272 -0
  12. package/src/__tests__/approval-cascade.test.ts +1 -1
  13. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
  14. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  15. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  16. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  17. package/src/__tests__/compaction-events.test.ts +1 -1
  18. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  19. package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
  20. package/src/__tests__/config-get-vision-flag.test.ts +136 -0
  21. package/src/__tests__/config-loader-backfill.test.ts +115 -18
  22. package/src/__tests__/config-watcher.test.ts +1 -1
  23. package/src/__tests__/context-token-estimator.test.ts +112 -57
  24. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  25. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
  26. package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
  27. package/src/__tests__/conversation-agent-loop.test.ts +77 -3
  28. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  29. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  30. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  31. package/src/__tests__/conversation-fork-crud.test.ts +161 -0
  32. package/src/__tests__/conversation-lifecycle.test.ts +1 -1
  33. package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
  34. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  35. package/src/__tests__/conversation-media-retry.test.ts +19 -8
  36. package/src/__tests__/conversation-pairing.test.ts +2 -2
  37. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  38. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
  39. package/src/__tests__/conversation-queue.test.ts +1 -1
  40. package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
  41. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  42. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  43. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  44. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  45. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  46. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  47. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
  48. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  49. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  50. package/src/__tests__/credential-security-invariants.test.ts +6 -0
  51. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  52. package/src/__tests__/date-context.test.ts +45 -0
  53. package/src/__tests__/dm-backfill.test.ts +64 -0
  54. package/src/__tests__/dm-persistence.test.ts +33 -0
  55. package/src/__tests__/document-find-replace.test.ts +501 -0
  56. package/src/__tests__/external-plugin-loader.test.ts +91 -19
  57. package/src/__tests__/first-greeting.test.ts +23 -2
  58. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
  59. package/src/__tests__/guardian-dispatch.test.ts +1 -0
  60. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  61. package/src/__tests__/heartbeat-service.test.ts +24 -164
  62. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  63. package/src/__tests__/host-app-control-proxy.test.ts +241 -0
  64. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  65. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  66. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  67. package/src/__tests__/host-file-proxy.test.ts +8 -1
  68. package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
  69. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  70. package/src/__tests__/identity-routes.test.ts +57 -0
  71. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  72. package/src/__tests__/injector-background-turn.test.ts +153 -0
  73. package/src/__tests__/injector-chain.test.ts +7 -0
  74. package/src/__tests__/injector-document-comments.test.ts +378 -0
  75. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  76. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
  77. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  78. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  79. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  80. package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
  81. package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
  82. package/src/__tests__/llm-catalog-parity.test.ts +3 -0
  83. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  84. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
  85. package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
  86. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
  87. package/src/__tests__/llm-resolver.test.ts +340 -3
  88. package/src/__tests__/log-export-routes.test.ts +99 -2
  89. package/src/__tests__/managed-profile-guard.test.ts +10 -0
  90. package/src/__tests__/message-queue-steer.test.ts +114 -0
  91. package/src/__tests__/notification-decision-fallback.test.ts +0 -91
  92. package/src/__tests__/notification-decision-strategy.test.ts +14 -31
  93. package/src/__tests__/notification-deep-link.test.ts +15 -0
  94. package/src/__tests__/notification-guardian-path.test.ts +1 -2
  95. package/src/__tests__/notification-platform-adapter.test.ts +5 -4
  96. package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
  97. package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
  98. package/src/__tests__/openai-provider.test.ts +323 -3
  99. package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
  100. package/src/__tests__/openai-responses-provider.test.ts +4 -4
  101. package/src/__tests__/openrouter-provider-only.test.ts +51 -3
  102. package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
  103. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  104. package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
  105. package/src/__tests__/platform-proxy-context.test.ts +6 -1
  106. package/src/__tests__/platform.test.ts +0 -3
  107. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  108. package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
  109. package/src/__tests__/plugin-types.test.ts +2 -2
  110. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  111. package/src/__tests__/process-message-display-content.test.ts +21 -16
  112. package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
  113. package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
  114. package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
  115. package/src/__tests__/server-history-render.test.ts +83 -4
  116. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  117. package/src/__tests__/system-prompt.test.ts +57 -101
  118. package/src/__tests__/terminal-tools.test.ts +11 -1
  119. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  120. package/src/__tests__/thread-backfill.test.ts +370 -22
  121. package/src/__tests__/tool-executor.test.ts +90 -1
  122. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  123. package/src/__tests__/twilio-routes.test.ts +1 -1
  124. package/src/__tests__/web-fetch.test.ts +2 -2
  125. package/src/__tests__/workspace-git-service.test.ts +88 -5
  126. package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
  127. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  128. package/src/a2a/__tests__/agent-card.test.ts +98 -0
  129. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
  130. package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
  131. package/src/a2a/__tests__/task-store.test.ts +246 -0
  132. package/src/a2a/agent-card.ts +58 -0
  133. package/src/a2a/feature-gate.ts +8 -0
  134. package/src/a2a/protocol-constants.ts +21 -0
  135. package/src/a2a/protocol-errors.ts +50 -0
  136. package/src/a2a/protocol-types.ts +162 -0
  137. package/src/a2a/task-store.ts +168 -0
  138. package/src/agent/attachments.ts +1 -0
  139. package/src/agent/loop.ts +208 -22
  140. package/src/background-wake/next-wake.test.ts +289 -0
  141. package/src/background-wake/next-wake.ts +172 -0
  142. package/src/browser/operations.ts +15 -0
  143. package/src/channels/config.ts +9 -0
  144. package/src/channels/types.ts +14 -0
  145. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  146. package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
  147. package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
  148. package/src/cli/commands/__tests__/schedules.test.ts +469 -0
  149. package/src/cli/commands/conversations.ts +128 -1
  150. package/src/cli/commands/inference-providers.ts +147 -1
  151. package/src/cli/commands/memory-v2.ts +308 -0
  152. package/src/cli/commands/notifications.ts +89 -37
  153. package/src/cli/commands/plugins.ts +67 -0
  154. package/src/cli/commands/schedules.ts +297 -5
  155. package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
  156. package/src/cli/lib/install-from-github.ts +8 -9
  157. package/src/cli/lib/search-plugins.ts +163 -0
  158. package/src/cli/program.ts +14 -0
  159. package/src/cli/utils/conversation-id.ts +17 -5
  160. package/src/config/assistant-feature-flags.ts +24 -54
  161. package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
  162. package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
  163. package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
  164. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  165. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  166. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  167. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  168. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  169. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  170. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  171. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  172. package/src/config/bundled-tool-registry.ts +22 -12
  173. package/src/config/call-site-defaults.ts +124 -0
  174. package/src/config/feature-flag-registry.json +111 -23
  175. package/src/config/llm-resolver.ts +66 -1
  176. package/src/config/schema.ts +2 -0
  177. package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
  178. package/src/config/schemas/call-site-catalog.ts +21 -0
  179. package/src/config/schemas/channels.ts +9 -0
  180. package/src/config/schemas/conversations.ts +10 -0
  181. package/src/config/schemas/heartbeat.ts +14 -0
  182. package/src/config/schemas/llm.ts +4 -3
  183. package/src/config/schemas/memory-retrospective.ts +1 -1
  184. package/src/config/schemas/memory-v2.ts +51 -4
  185. package/src/config/schemas/memory.ts +3 -1
  186. package/src/config/seed-inference-profiles.ts +99 -29
  187. package/src/context/compactor.ts +80 -13
  188. package/src/context/token-estimator.ts +72 -31
  189. package/src/context/window-manager.ts +25 -0
  190. package/src/credential-health/credential-health-service.ts +34 -19
  191. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
  192. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  193. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  194. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  195. package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
  196. package/src/daemon/conversation-agent-loop.ts +252 -56
  197. package/src/daemon/conversation-lifecycle.ts +142 -116
  198. package/src/daemon/conversation-messaging.ts +3 -0
  199. package/src/daemon/conversation-process.ts +273 -0
  200. package/src/daemon/conversation-queue-manager.ts +14 -0
  201. package/src/daemon/conversation-runtime-assembly.ts +144 -75
  202. package/src/daemon/conversation-slash.ts +37 -5
  203. package/src/daemon/conversation-surfaces.ts +45 -2
  204. package/src/daemon/conversation-tool-setup.ts +7 -0
  205. package/src/daemon/conversation.ts +42 -12
  206. package/src/daemon/date-context.ts +40 -0
  207. package/src/daemon/first-greeting.ts +10 -0
  208. package/src/daemon/guardian-action-generators.ts +1 -125
  209. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  210. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
  211. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
  212. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
  213. package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
  214. package/src/daemon/handlers/config-a2a.ts +449 -0
  215. package/src/daemon/handlers/config-model.test.ts +1 -0
  216. package/src/daemon/handlers/conversations.ts +80 -0
  217. package/src/daemon/handlers/shared.ts +92 -29
  218. package/src/daemon/host-app-control-proxy.ts +69 -18
  219. package/src/daemon/host-bash-proxy.ts +1 -1
  220. package/src/daemon/host-cu-proxy.ts +1 -1
  221. package/src/daemon/host-file-proxy.ts +1 -1
  222. package/src/daemon/host-proxy-preactivation.ts +85 -18
  223. package/src/daemon/host-transfer-proxy.ts +1 -1
  224. package/src/daemon/lifecycle.ts +67 -65
  225. package/src/daemon/memory-v2-startup.ts +49 -13
  226. package/src/daemon/message-protocol.ts +4 -0
  227. package/src/daemon/message-types/conversations.ts +8 -0
  228. package/src/daemon/message-types/document-comments.ts +50 -0
  229. package/src/daemon/message-types/messages.ts +68 -1
  230. package/src/daemon/message-types/notifications.ts +21 -0
  231. package/src/daemon/message-types/surfaces.ts +3 -1
  232. package/src/daemon/message-types/web-activity.ts +57 -0
  233. package/src/daemon/pkb-reminder-builder.test.ts +10 -53
  234. package/src/daemon/pkb-reminder-builder.ts +4 -19
  235. package/src/daemon/plugin-source-watcher.ts +135 -3
  236. package/src/daemon/process-message.ts +72 -12
  237. package/src/daemon/query-complexity-router.ts +75 -0
  238. package/src/daemon/skill-memory-refresh.ts +5 -1
  239. package/src/daemon/trust-context.ts +6 -0
  240. package/src/daemon/wake-target-adapter.ts +2 -0
  241. package/src/documents/document-comments-store.test.ts +338 -0
  242. package/src/documents/document-comments-store.ts +237 -0
  243. package/src/documents/document-store.ts +202 -0
  244. package/src/export/__tests__/transcript-formatter.test.ts +121 -0
  245. package/src/export/transcript-formatter.ts +54 -20
  246. package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
  247. package/src/heartbeat/heartbeat-service.ts +35 -191
  248. package/src/home/__tests__/feed-types.test.ts +40 -0
  249. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  250. package/src/home/feed-types.ts +20 -3
  251. package/src/home/home-content-refresh.ts +52 -0
  252. package/src/home/home-greeting-cache.ts +69 -0
  253. package/src/home/home-greeting.ts +94 -0
  254. package/src/home/suggested-prompts.ts +177 -9
  255. package/src/ipc/cli-client.ts +147 -45
  256. package/src/memory/__tests__/conversation-queries.test.ts +220 -0
  257. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  258. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
  259. package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
  260. package/src/memory/conversation-crud.ts +133 -43
  261. package/src/memory/conversation-queries.ts +87 -1
  262. package/src/memory/conversation-title-service.ts +26 -4
  263. package/src/memory/db-init.ts +22 -0
  264. package/src/memory/delivery-crud.ts +41 -0
  265. package/src/memory/delivery-status.ts +141 -15
  266. package/src/memory/external-conversation-store.ts +32 -1
  267. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
  268. package/src/memory/graph/conversation-graph-memory.ts +18 -6
  269. package/src/memory/graph/tools.ts +6 -37
  270. package/src/memory/invite-store.ts +53 -0
  271. package/src/memory/jobs-worker.ts +21 -1
  272. package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
  273. package/src/memory/llm-request-log-store.ts +92 -1
  274. package/src/memory/memory-retrospective-constants.ts +28 -0
  275. package/src/memory/memory-retrospective-enqueue.ts +4 -22
  276. package/src/memory/memory-retrospective-job.ts +438 -21
  277. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  278. package/src/memory/memory-v2-activation-log-store.ts +26 -8
  279. package/src/memory/migrations/100-core-tables.ts +1 -0
  280. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  281. package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
  282. package/src/memory/migrations/251-a2a-tasks.ts +49 -0
  283. package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
  284. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  285. package/src/memory/migrations/253-document-comments.ts +47 -0
  286. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  287. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  288. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  289. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  290. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  291. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  292. package/src/memory/migrations/index.ts +20 -0
  293. package/src/memory/migrations/registry.ts +33 -0
  294. package/src/memory/onboarding-events-store.ts +7 -0
  295. package/src/memory/schema/a2a.ts +15 -0
  296. package/src/memory/schema/calls.ts +1 -0
  297. package/src/memory/schema/conversations.ts +3 -0
  298. package/src/memory/schema/index.ts +1 -0
  299. package/src/memory/schema/inference.ts +2 -0
  300. package/src/memory/schema/infrastructure.ts +2 -0
  301. package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
  302. package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
  303. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
  304. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  305. package/src/memory/v2/__tests__/injection.test.ts +221 -17
  306. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  307. package/src/memory/v2/__tests__/router.test.ts +489 -1
  308. package/src/memory/v2/__tests__/static-context.test.ts +12 -1
  309. package/src/memory/v2/activation-store.ts +14 -16
  310. package/src/memory/v2/cli-command-content.ts +19 -0
  311. package/src/memory/v2/cli-command-store.ts +304 -0
  312. package/src/memory/v2/consolidation-job.ts +14 -0
  313. package/src/memory/v2/frontmatter-sweep.ts +7 -1
  314. package/src/memory/v2/injection-events.ts +101 -0
  315. package/src/memory/v2/injection.ts +69 -29
  316. package/src/memory/v2/page-index.ts +246 -19
  317. package/src/memory/v2/page-store.ts +18 -0
  318. package/src/memory/v2/router.ts +209 -55
  319. package/src/memory/v2/static-context.ts +4 -4
  320. package/src/memory/v2/types.ts +23 -0
  321. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
  322. package/src/messaging/providers/a2a/deliver.ts +156 -0
  323. package/src/messaging/providers/gmail/client.ts +9 -2
  324. package/src/messaging/providers/index.ts +18 -3
  325. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  326. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  327. package/src/messaging/providers/slack/adapter.ts +178 -25
  328. package/src/messaging/providers/slack/api.test.ts +54 -0
  329. package/src/messaging/providers/slack/api.ts +119 -3
  330. package/src/messaging/providers/slack/client.ts +12 -0
  331. package/src/messaging/providers/slack/deep-link.ts +20 -1
  332. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  333. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  334. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  335. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  336. package/src/messaging/providers/slack/send.test.ts +77 -0
  337. package/src/messaging/providers/slack/send.ts +8 -2
  338. package/src/messaging/providers/slack/types.ts +14 -0
  339. package/src/notifications/__tests__/broadcaster.test.ts +203 -0
  340. package/src/notifications/__tests__/decision-engine.test.ts +283 -0
  341. package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
  342. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +5 -1
  343. package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
  344. package/src/notifications/adapters/macos.ts +12 -2
  345. package/src/notifications/broadcaster.ts +29 -4
  346. package/src/notifications/conversation-seed-composer.ts +14 -2
  347. package/src/notifications/copy-composer.ts +17 -64
  348. package/src/notifications/decision-engine.ts +111 -44
  349. package/src/notifications/deferred-emit.ts +135 -0
  350. package/src/notifications/deterministic-checks.ts +96 -0
  351. package/src/notifications/emit-signal.ts +10 -1
  352. package/src/notifications/home-feed-side-effect.ts +136 -27
  353. package/src/notifications/signal.ts +0 -4
  354. package/src/notifications/types.ts +8 -0
  355. package/src/oauth/connect-orchestrator.ts +3 -0
  356. package/src/oauth/credential-token-resolver.ts +2 -0
  357. package/src/oauth/manual-token-connection.ts +19 -0
  358. package/src/oauth/oauth-store.ts +12 -0
  359. package/src/oauth/platform-connection.test.ts +43 -3
  360. package/src/oauth/platform-connection.ts +13 -4
  361. package/src/oauth/seed-providers.ts +22 -0
  362. package/src/permissions/prompter.ts +5 -2
  363. package/src/permissions/secret-prompter.ts +4 -1
  364. package/src/plugins/defaults/injectors.ts +118 -26
  365. package/src/plugins/external-plugin-loader.ts +82 -10
  366. package/src/plugins/types.ts +16 -7
  367. package/src/prompts/__tests__/system-prompt.test.ts +44 -45
  368. package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
  369. package/src/prompts/normalize-onboarding.ts +40 -0
  370. package/src/prompts/sections.ts +32 -14
  371. package/src/prompts/system-prompt.ts +105 -76
  372. package/src/prompts/template-detection.ts +37 -0
  373. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  374. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  375. package/src/prompts/templates/VOICE.md +3 -0
  376. package/src/prompts/templates/system-sections.ts +51 -10
  377. package/src/providers/__tests__/inference.test.ts +2 -0
  378. package/src/providers/anthropic/client.ts +132 -5
  379. package/src/providers/call-site-routing.ts +24 -6
  380. package/src/providers/connection-resolution.ts +63 -13
  381. package/src/providers/fireworks/client.ts +20 -2
  382. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
  383. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  384. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  385. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  386. package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
  387. package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
  388. package/src/providers/inference/adapter-factory.ts +24 -21
  389. package/src/providers/inference/auth.ts +15 -3
  390. package/src/providers/inference/backfill.ts +14 -1
  391. package/src/providers/inference/codex-token-refresh.ts +128 -0
  392. package/src/providers/inference/connections.ts +85 -5
  393. package/src/providers/inference/resolve-auth.ts +50 -5
  394. package/src/providers/model-catalog.ts +244 -242
  395. package/src/providers/model-intents.ts +3 -3
  396. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
  397. package/src/providers/openai/chat-completions-provider.ts +215 -25
  398. package/src/providers/openai/responses-provider.ts +9 -3
  399. package/src/providers/openrouter/client.ts +46 -4
  400. package/src/providers/platform-proxy/constants.ts +3 -4
  401. package/src/providers/provider-catalog-visibility.ts +3 -1
  402. package/src/providers/provider-send-message.ts +27 -12
  403. package/src/providers/registry.ts +30 -1
  404. package/src/providers/types.ts +25 -0
  405. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  406. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  407. package/src/runtime/agent-wake.ts +212 -57
  408. package/src/runtime/auth/route-policy.ts +20 -3
  409. package/src/runtime/background-job-runner.ts +26 -0
  410. package/src/runtime/channel-reply-delivery.ts +182 -47
  411. package/src/runtime/channel-retry-sweep.ts +141 -16
  412. package/src/runtime/http-server.ts +7 -16
  413. package/src/runtime/http-types.ts +7 -51
  414. package/src/runtime/pending-interactions.ts +51 -8
  415. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
  416. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  417. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
  418. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
  419. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
  420. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  421. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
  422. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  423. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  424. package/src/runtime/routes/approval-routes.ts +4 -1
  425. package/src/runtime/routes/channel-availability-routes.ts +5 -0
  426. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  427. package/src/runtime/routes/consolidation-routes.ts +100 -0
  428. package/src/runtime/routes/content-source-routes.ts +78 -0
  429. package/src/runtime/routes/conversation-cli-routes.ts +146 -1
  430. package/src/runtime/routes/conversation-query-routes.ts +130 -12
  431. package/src/runtime/routes/conversation-routes.ts +288 -76
  432. package/src/runtime/routes/document-comments-routes.ts +287 -0
  433. package/src/runtime/routes/documents-routes.ts +33 -0
  434. package/src/runtime/routes/home-feed-routes.ts +6 -3
  435. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  436. package/src/runtime/routes/host-browser-routes.ts +8 -1
  437. package/src/runtime/routes/identity-routes.ts +21 -0
  438. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  439. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  440. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  441. package/src/runtime/routes/index.ts +14 -4
  442. package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
  443. package/src/runtime/routes/integrations/a2a.ts +294 -0
  444. package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
  445. package/src/runtime/routes/log-export-routes.ts +39 -0
  446. package/src/runtime/routes/memory-v2-routes.ts +217 -0
  447. package/src/runtime/routes/notification-routes.ts +19 -2
  448. package/src/runtime/routes/question-routes.ts +4 -1
  449. package/src/runtime/routes/sanity-routes.ts +159 -0
  450. package/src/runtime/routes/slack-channel-routes.ts +187 -0
  451. package/src/runtime/routes/subagents-routes.ts +41 -0
  452. package/src/runtime/services/conversation-serializer.ts +30 -4
  453. package/src/schedule/integration-status.ts +3 -1
  454. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  455. package/src/security/oauth2-device-code.ts +307 -0
  456. package/src/security/oauth2.ts +26 -9
  457. package/src/security/secure-keys.ts +5 -0
  458. package/src/skills/catalog-install.ts +6 -2
  459. package/src/subagent/manager.ts +2 -0
  460. package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
  461. package/src/tools/browser/browser-execution.ts +93 -0
  462. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  463. package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
  464. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
  465. package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
  466. package/src/tools/browser/cdp-client/factory.ts +87 -3
  467. package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
  468. package/src/tools/browser/cdp-client/types.ts +36 -0
  469. package/src/tools/browser/pinned-tabs.ts +90 -0
  470. package/src/tools/document/document-comment-tool.test.ts +379 -0
  471. package/src/tools/document/document-comment-tool.ts +156 -0
  472. package/src/tools/document/document-tool.ts +128 -2
  473. package/src/tools/memory/register.ts +1 -9
  474. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  475. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  476. package/src/tools/network/domain-normalize.ts +17 -0
  477. package/src/tools/network/web-fetch.ts +213 -64
  478. package/src/tools/network/web-search.ts +191 -66
  479. package/src/tools/registry.ts +2 -2
  480. package/src/tools/terminal/safe-env.ts +3 -2
  481. package/src/tools/tool-approval-handler.ts +19 -12
  482. package/src/tools/types.ts +41 -2
  483. package/src/tools/ui-surface/definitions.ts +3 -1
  484. package/src/types/onboarding-context.ts +4 -0
  485. package/src/util/__tests__/favicon.test.ts +84 -0
  486. package/src/util/favicon.ts +40 -0
  487. package/src/util/platform.ts +0 -5
  488. package/src/workspace/git-service.ts +75 -4
  489. package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
  490. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  491. package/src/workspace/migrations/registry.ts +4 -0
  492. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
  493. package/src/config/bundled-skills/document/SKILL.md +0 -54
  494. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  495. package/src/daemon/seed-files.ts +0 -18
  496. package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
  497. package/src/runtime/guardian-action-conversation-turn.ts +0 -99
  498. package/src/runtime/routes/interface-routes.ts +0 -43
  499. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  500. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  501. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  502. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  503. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -1,3 +1,4 @@
1
+ import type { RenderedHistoryContent } from "../daemon/handlers/shared.js";
1
2
  import { renderHistoryContent } from "../daemon/handlers/shared.js";
2
3
  import { getAttachmentMetadataForMessage } from "../memory/attachments-store.js";
3
4
  import {
@@ -76,6 +77,18 @@ function toDeliverableTextSegments(
76
77
  return [];
77
78
  }
78
79
 
80
+ function hasDeliverableReply(
81
+ rendered: RenderedHistoryContent,
82
+ attachments: RuntimeAttachmentMetadata[],
83
+ ): boolean {
84
+ return (
85
+ toDeliverableTextSegments(rendered.textSegments, rendered.text).length >
86
+ 0 ||
87
+ attachments.length > 0 ||
88
+ hasNoResponseMarker(rendered.textSegments)
89
+ );
90
+ }
91
+
79
92
  export async function deliverRenderedReplyViaCallback(
80
93
  params: DeliverRenderedReplyParams,
81
94
  ): Promise<void> {
@@ -169,6 +182,13 @@ export async function deliverRenderedReplyViaCallback(
169
182
  }
170
183
 
171
184
  export type DeliverReplyOptions = {
185
+ /** Persisted assistant message row to deliver; defaults to latest assistant. */
186
+ messageId?: string;
187
+ /**
188
+ * Internal conversation message id for the user row that started this
189
+ * delivery. When set, fallback scans never cross into older turns.
190
+ */
191
+ sinceMessageId?: string;
172
192
  startFromSegment?: number;
173
193
  onSegmentDelivered?: (deliveredCount: number) => void;
174
194
  /** Deliver as ephemeral (visible only to `user`). Fire-and-forget. */
@@ -181,6 +201,125 @@ export type DeliverReplyOptions = {
181
201
  onMessageTs?: (ts: string) => void;
182
202
  };
183
203
 
204
+ type PersistedMessage = ReturnType<typeof getMessages>[number];
205
+
206
+ function readPersistedAssistantReply(msg: PersistedMessage): {
207
+ rendered: RenderedHistoryContent;
208
+ replyAttachments: RuntimeAttachmentMetadata[];
209
+ } {
210
+ let parsed: unknown;
211
+ try {
212
+ parsed = JSON.parse(msg.content);
213
+ } catch {
214
+ parsed = msg.content;
215
+ }
216
+ const rendered = renderHistoryContent(parsed);
217
+
218
+ const linked = getAttachmentMetadataForMessage(msg.id);
219
+ const replyAttachments: RuntimeAttachmentMetadata[] = linked.map((a) => ({
220
+ id: a.id,
221
+ filename: a.originalFilename,
222
+ mimeType: a.mimeType,
223
+ sizeBytes: a.sizeBytes,
224
+ kind: a.kind,
225
+ }));
226
+
227
+ return { rendered, replyAttachments };
228
+ }
229
+
230
+ function isToolResultUserMessage(msg: PersistedMessage): boolean {
231
+ if (msg.role !== "user") return false;
232
+ try {
233
+ const parsed = JSON.parse(msg.content) as unknown;
234
+ return (
235
+ Array.isArray(parsed) &&
236
+ parsed.length > 0 &&
237
+ parsed.every(
238
+ (block) =>
239
+ block !== null &&
240
+ typeof block === "object" &&
241
+ (block as Record<string, unknown>).type === "tool_result",
242
+ )
243
+ );
244
+ } catch {
245
+ return false;
246
+ }
247
+ }
248
+
249
+ export function findAssistantReplyMessageIdForTurn(
250
+ conversationId: string,
251
+ userMessageId: string,
252
+ ): string | undefined {
253
+ const msgs = getMessages(conversationId);
254
+ const userIndex = msgs.findIndex((msg) => msg.id === userMessageId);
255
+ if (userIndex === -1) return undefined;
256
+
257
+ let turnEndIndex = msgs.length;
258
+ for (let i = userIndex + 1; i < msgs.length; i++) {
259
+ const msg = msgs[i];
260
+ if (msg.role === "user" && !isToolResultUserMessage(msg)) {
261
+ turnEndIndex = i;
262
+ break;
263
+ }
264
+ }
265
+
266
+ for (let i = turnEndIndex - 1; i > userIndex; i--) {
267
+ const msg = msgs[i];
268
+ if (msg.role === "assistant") {
269
+ const { rendered, replyAttachments } = readPersistedAssistantReply(msg);
270
+ if (hasDeliverableReply(rendered, replyAttachments)) {
271
+ return msg.id;
272
+ }
273
+ }
274
+ }
275
+ return undefined;
276
+ }
277
+
278
+ async function deliverPersistedAssistantMessageViaCallback(
279
+ msg: PersistedMessage,
280
+ externalChatId: string,
281
+ callbackUrl: string,
282
+ assistantId: string | undefined,
283
+ options: DeliverReplyOptions | undefined,
284
+ ): Promise<boolean> {
285
+ const { rendered, replyAttachments } = readPersistedAssistantReply(msg);
286
+ if (!hasDeliverableReply(rendered, replyAttachments)) {
287
+ return false;
288
+ }
289
+
290
+ // Compose an `onMessageTs` that reconciles `slackMeta.channelTs` on the
291
+ // persisted assistant row once Slack returns the authoritative ts. The
292
+ // assistant row was written BEFORE the gateway POST in
293
+ // `handleMessageComplete`, so the partial `slackMeta` it carries is
294
+ // missing `channelTs` and would otherwise be rejected by
295
+ // `readSlackMetadata`, dropping the row out of chronological/thread-tag
296
+ // rendering. We only act on the FIRST ts (top-level segment); any
297
+ // subsequent split segments become independent Slack messages with
298
+ // their own ts and are not represented as separate DB rows.
299
+ const reconcileOnMessageTs = makeChannelTsReconciler(msg.id);
300
+ const callerOnMessageTs = options?.onMessageTs;
301
+ const composedOnMessageTs = (ts: string): void => {
302
+ reconcileOnMessageTs(ts);
303
+ callerOnMessageTs?.(ts);
304
+ };
305
+
306
+ await deliverRenderedReplyViaCallback({
307
+ callbackUrl,
308
+ chatId: externalChatId,
309
+ textSegments: rendered.textSegments,
310
+ fallbackText: rendered.text,
311
+ attachments: replyAttachments,
312
+ assistantId,
313
+ startFromSegment: options?.startFromSegment,
314
+ onSegmentDelivered: options?.onSegmentDelivered,
315
+ ephemeral: options?.ephemeral,
316
+ user: options?.user,
317
+ messageTs: options?.messageTs,
318
+ onMessageTs: composedOnMessageTs,
319
+ });
320
+ return true;
321
+ }
322
+
184
323
  export async function deliverReplyViaCallback(
185
324
  conversationId: string,
186
325
  externalChatId: string,
@@ -188,58 +327,54 @@ export async function deliverReplyViaCallback(
188
327
  assistantId?: string,
189
328
  options?: DeliverReplyOptions,
190
329
  ): Promise<void> {
330
+ if (options?.messageId) {
331
+ const msg = getMessageById(options.messageId, conversationId);
332
+ if (!msg || msg.role !== "assistant") {
333
+ throw new Error(
334
+ `Target assistant reply message not found: ${options.messageId}`,
335
+ );
336
+ }
337
+ await deliverPersistedAssistantMessageViaCallback(
338
+ msg,
339
+ externalChatId,
340
+ callbackUrl,
341
+ assistantId,
342
+ options,
343
+ );
344
+ return;
345
+ }
346
+
347
+ if (options?.sinceMessageId) {
348
+ const replyMessageId = findAssistantReplyMessageIdForTurn(
349
+ conversationId,
350
+ options.sinceMessageId,
351
+ );
352
+ if (replyMessageId) {
353
+ const msg = getMessageById(replyMessageId, conversationId);
354
+ if (msg && msg.role === "assistant") {
355
+ await deliverPersistedAssistantMessageViaCallback(
356
+ msg,
357
+ externalChatId,
358
+ callbackUrl,
359
+ assistantId,
360
+ options,
361
+ );
362
+ }
363
+ }
364
+ return;
365
+ }
366
+
191
367
  const msgs = getMessages(conversationId);
192
368
  for (let i = msgs.length - 1; i >= 0; i--) {
193
369
  if (msgs[i].role !== "assistant") continue;
194
-
195
- let parsed: unknown;
196
- try {
197
- parsed = JSON.parse(msgs[i].content);
198
- } catch {
199
- parsed = msgs[i].content;
200
- }
201
- const rendered = renderHistoryContent(parsed);
202
-
203
- const linked = getAttachmentMetadataForMessage(msgs[i].id);
204
- const replyAttachments: RuntimeAttachmentMetadata[] = linked.map((a) => ({
205
- id: a.id,
206
- filename: a.originalFilename,
207
- mimeType: a.mimeType,
208
- sizeBytes: a.sizeBytes,
209
- kind: a.kind,
210
- }));
211
-
212
- // Compose an `onMessageTs` that reconciles `slackMeta.channelTs` on the
213
- // persisted assistant row once Slack returns the authoritative ts. The
214
- // assistant row was written BEFORE the gateway POST in
215
- // `handleMessageComplete`, so the partial `slackMeta` it carries is
216
- // missing `channelTs` and would otherwise be rejected by
217
- // `readSlackMetadata`, dropping the row out of chronological/thread-tag
218
- // rendering. We only act on the FIRST ts (top-level segment); any
219
- // subsequent split segments become independent Slack messages with
220
- // their own ts and are not represented as separate DB rows.
221
- const reconcileOnMessageTs = makeChannelTsReconciler(msgs[i].id);
222
- const callerOnMessageTs = options?.onMessageTs;
223
- const composedOnMessageTs = (ts: string): void => {
224
- reconcileOnMessageTs(ts);
225
- callerOnMessageTs?.(ts);
226
- };
227
-
228
- await deliverRenderedReplyViaCallback({
370
+ const delivered = await deliverPersistedAssistantMessageViaCallback(
371
+ msgs[i],
372
+ externalChatId,
229
373
  callbackUrl,
230
- chatId: externalChatId,
231
- textSegments: rendered.textSegments,
232
- fallbackText: rendered.text,
233
- attachments: replyAttachments,
234
374
  assistantId,
235
- startFromSegment: options?.startFromSegment,
236
- onSegmentDelivered: options?.onSegmentDelivered,
237
- ephemeral: options?.ephemeral,
238
- user: options?.user,
239
- messageTs: options?.messageTs,
240
- onMessageTs: composedOnMessageTs,
241
- });
242
- break;
375
+ options,
376
+ );
377
+ if (delivered) break;
243
378
  }
244
379
  }
245
380
 
@@ -9,17 +9,28 @@ import {
9
9
  } from "../channels/types.js";
10
10
  import { getDiskPressureStatus } from "../daemon/disk-pressure-guard.js";
11
11
  import { classifyDiskPressureTurnPolicy } from "../daemon/disk-pressure-policy.js";
12
+ import type { ServerMessage } from "../daemon/message-protocol.js";
12
13
  import type { TrustContext } from "../daemon/trust-context.js";
13
14
  import { updateDeliveredSegmentCount } from "../memory/delivery-channels.js";
14
- import { clearPayload, linkMessage } from "../memory/delivery-crud.js";
15
15
  import {
16
+ clearPayload,
17
+ linkMessage,
18
+ storeReplyMessageId,
19
+ } from "../memory/delivery-crud.js";
20
+ import {
21
+ getRetryableDeliveryEvents,
16
22
  getRetryableEvents,
23
+ markDeliveryDelivered,
17
24
  markProcessed,
18
25
  markRetryableFailure,
26
+ recordDeliveryFailure,
19
27
  recordProcessingFailure,
20
28
  } from "../memory/delivery-status.js";
21
29
  import { getLogger } from "../util/logger.js";
22
- import { deliverReplyViaCallback } from "./channel-reply-delivery.js";
30
+ import {
31
+ deliverReplyViaCallback,
32
+ findAssistantReplyMessageIdForTurn,
33
+ } from "./channel-reply-delivery.js";
23
34
  import { deliverChannelReply } from "./gateway-client.js";
24
35
  import type { MessageProcessor } from "./http-types.js";
25
36
  import { resolveRoutingStateFromRuntime } from "./trust-context-resolver.js";
@@ -91,9 +102,13 @@ export async function sweepFailedEvents(
91
102
  processMessage: MessageProcessor,
92
103
  ): Promise<void> {
93
104
  const events = getRetryableEvents();
94
- if (events.length === 0) return;
105
+ const deliveryEvents = getRetryableDeliveryEvents();
106
+ if (events.length === 0 && deliveryEvents.length === 0) return;
95
107
 
96
- log.info({ count: events.length }, "Retrying failed channel inbound events");
108
+ log.info(
109
+ { processingCount: events.length, deliveryCount: deliveryEvents.length },
110
+ "Retrying failed channel inbound events",
111
+ );
97
112
 
98
113
  for (const event of events) {
99
114
  if (!event.rawPayload) {
@@ -243,9 +258,20 @@ export async function sweepFailedEvents(
243
258
  sourceMetadata.chatType.trim().length > 0
244
259
  ? sourceMetadata.chatType.trim()
245
260
  : undefined;
261
+ let replyMessageId: string | undefined;
262
+ const observeAgentEvent = (msg: ServerMessage): void => {
263
+ if (
264
+ msg.type === "message_complete" &&
265
+ (msg.source === undefined || msg.source === "main") &&
266
+ typeof msg.messageId === "string"
267
+ ) {
268
+ replyMessageId = msg.messageId;
269
+ }
270
+ };
246
271
 
272
+ let userMessageId: string | undefined;
247
273
  try {
248
- const { messageId: userMessageId } = await processMessage(
274
+ const result = await processMessage(
249
275
  event.conversationId,
250
276
  content,
251
277
  attachmentIds,
@@ -260,27 +286,39 @@ export async function sweepFailedEvents(
260
286
  trustContext,
261
287
  isInteractive:
262
288
  resolveRoutingStateFromRuntime(trustContext).promptWaitingAllowed,
289
+ onEvent: observeAgentEvent,
263
290
  },
264
291
  sourceChannel,
265
292
  sourceInterface,
266
293
  );
294
+ userMessageId = result.messageId;
267
295
  linkMessage(event.id, userMessageId);
268
296
  markProcessed(event.id);
297
+ replyMessageId ??= result.assistantMessageId;
298
+ if (replyMessageId) {
299
+ storeReplyMessageId(event.id, replyMessageId);
300
+ }
269
301
  log.info(
270
302
  { eventId: event.id },
271
303
  "Successfully replayed failed channel event",
272
304
  );
305
+ } catch (err) {
306
+ log.error({ err, eventId: event.id }, "Retry failed for channel event");
307
+ recordProcessingFailure(event.id, err);
308
+ continue;
309
+ }
273
310
 
274
- const replyCallbackUrl =
275
- typeof payload.replyCallbackUrl === "string"
276
- ? payload.replyCallbackUrl
311
+ const replyCallbackUrl =
312
+ typeof payload.replyCallbackUrl === "string"
313
+ ? payload.replyCallbackUrl
314
+ : undefined;
315
+ if (replyCallbackUrl) {
316
+ const externalChatId =
317
+ typeof payload.externalChatId === "string"
318
+ ? payload.externalChatId
277
319
  : undefined;
278
- if (replyCallbackUrl) {
279
- const externalChatId =
280
- typeof payload.externalChatId === "string"
281
- ? payload.externalChatId
282
- : undefined;
283
- if (externalChatId) {
320
+ if (externalChatId) {
321
+ try {
284
322
  // processMessage above generated a fresh assistant response, so any
285
323
  // previously tracked segment progress belongs to the old response and
286
324
  // must not carry over. Reset to 0 so we deliver all segments of the
@@ -292,16 +330,103 @@ export async function sweepFailedEvents(
292
330
  replyCallbackUrl,
293
331
  assistantId,
294
332
  {
333
+ messageId: replyMessageId,
334
+ sinceMessageId: userMessageId,
295
335
  startFromSegment: 0,
296
336
  onSegmentDelivered: (count) =>
297
337
  updateDeliveredSegmentCount(event.id, count),
298
338
  },
299
339
  );
340
+ markDeliveryDelivered(event.id);
341
+ } catch (err) {
342
+ log.error(
343
+ { err, eventId: event.id },
344
+ "Retry delivery failed for channel event",
345
+ );
346
+ recordDeliveryFailure(event.id, err);
300
347
  }
301
348
  }
349
+ }
350
+ }
351
+
352
+ for (const event of deliveryEvents) {
353
+ if (!event.rawPayload) {
354
+ recordDeliveryFailure(
355
+ event.id,
356
+ new Error("No raw payload stored for delivery retry"),
357
+ );
358
+ continue;
359
+ }
360
+
361
+ let payload: Record<string, unknown>;
362
+ try {
363
+ payload = JSON.parse(event.rawPayload) as Record<string, unknown>;
364
+ } catch {
365
+ recordDeliveryFailure(
366
+ event.id,
367
+ new Error("Failed to parse stored raw payload for delivery retry"),
368
+ );
369
+ continue;
370
+ }
371
+
372
+ const replyCallbackUrl =
373
+ typeof payload.replyCallbackUrl === "string"
374
+ ? payload.replyCallbackUrl
375
+ : undefined;
376
+ const externalChatId =
377
+ typeof payload.externalChatId === "string"
378
+ ? payload.externalChatId
379
+ : undefined;
380
+ let replyMessageId =
381
+ typeof payload.replyMessageId === "string"
382
+ ? payload.replyMessageId
383
+ : undefined;
384
+ const assistantId =
385
+ typeof payload.assistantId === "string" ? payload.assistantId : undefined;
386
+ if (!replyCallbackUrl || !externalChatId) {
387
+ recordDeliveryFailure(
388
+ event.id,
389
+ new Error("Stored payload is missing delivery callback details"),
390
+ );
391
+ continue;
392
+ }
393
+ if (!replyMessageId && event.messageId) {
394
+ replyMessageId = findAssistantReplyMessageIdForTurn(
395
+ event.conversationId,
396
+ event.messageId,
397
+ );
398
+ if (replyMessageId) {
399
+ storeReplyMessageId(event.id, replyMessageId);
400
+ }
401
+ }
402
+ if (!replyMessageId) {
403
+ recordDeliveryFailure(
404
+ event.id,
405
+ new Error("Stored payload is missing assistant reply message id"),
406
+ );
407
+ continue;
408
+ }
409
+
410
+ try {
411
+ await deliverReplyViaCallback(
412
+ event.conversationId,
413
+ externalChatId,
414
+ replyCallbackUrl,
415
+ assistantId,
416
+ {
417
+ messageId: replyMessageId,
418
+ startFromSegment: event.deliveredSegmentCount,
419
+ onSegmentDelivered: (count) =>
420
+ updateDeliveredSegmentCount(event.id, count),
421
+ },
422
+ );
423
+ markDeliveryDelivered(event.id);
302
424
  } catch (err) {
303
- log.error({ err, eventId: event.id }, "Retry failed for channel event");
304
- recordProcessingFailure(event.id, err);
425
+ log.error(
426
+ { err, eventId: event.id },
427
+ "Retry delivery failed for processed channel event",
428
+ );
429
+ recordDeliveryFailure(event.id, err);
305
430
  }
306
431
  }
307
432
  }
@@ -28,6 +28,8 @@ import {
28
28
  import { isHttpAuthDisabled } from "../config/env.js";
29
29
  import { getIsPlatform } from "../config/env-registry.js";
30
30
  import { getConfig } from "../config/loader.js";
31
+ import { createApprovalCopyGenerator } from "../daemon/approval-generators.js";
32
+ import { createGuardianActionCopyGenerator } from "../daemon/guardian-action-generators.js";
31
33
  import { processMessage } from "../daemon/process-message.js";
32
34
  import { createLiveVoiceSession } from "../live-voice/live-voice-session.js";
33
35
  import { LiveVoiceSessionManager } from "../live-voice/live-voice-session-manager.js";
@@ -96,10 +98,8 @@ import { matchSkillRoute } from "./skill-route-registry.js";
96
98
  export { isPrivateAddress } from "./middleware/auth.js";
97
99
 
98
100
  import type {
99
- ApprovalConversationGenerator,
100
101
  ApprovalCopyGenerator,
101
102
  GuardianActionCopyGenerator,
102
- GuardianFollowUpConversationGenerator,
103
103
  RuntimeHttpServerOptions,
104
104
  } from "./http-types.js";
105
105
 
@@ -161,10 +161,8 @@ export class RuntimeHttpServer {
161
161
  private port: number;
162
162
  private hostname: string;
163
163
 
164
- private approvalCopyGenerator?: ApprovalCopyGenerator;
165
- private approvalConversationGenerator?: ApprovalConversationGenerator;
166
- private guardianActionCopyGenerator?: GuardianActionCopyGenerator;
167
- private guardianFollowUpConversationGenerator?: GuardianFollowUpConversationGenerator;
164
+ private readonly approvalCopyGenerator: ApprovalCopyGenerator;
165
+ private readonly guardianActionCopyGenerator: GuardianActionCopyGenerator;
168
166
  private retrySweepTimer: ReturnType<typeof setInterval> | null = null;
169
167
  private sweepInProgress = false;
170
168
 
@@ -175,11 +173,8 @@ export class RuntimeHttpServer {
175
173
  this.port = options.port ?? DEFAULT_PORT;
176
174
  this.hostname = options.hostname ?? DEFAULT_HOSTNAME;
177
175
 
178
- this.approvalCopyGenerator = options.approvalCopyGenerator;
179
- this.approvalConversationGenerator = options.approvalConversationGenerator;
180
- this.guardianActionCopyGenerator = options.guardianActionCopyGenerator;
181
- this.guardianFollowUpConversationGenerator =
182
- options.guardianFollowUpConversationGenerator;
176
+ this.approvalCopyGenerator = createApprovalCopyGenerator();
177
+ this.guardianActionCopyGenerator = createGuardianActionCopyGenerator();
183
178
  this.liveVoiceSessionManager = new LiveVoiceSessionManager({
184
179
  createSession: (context) => createLiveVoiceSession(context),
185
180
  });
@@ -560,11 +555,7 @@ export class RuntimeHttpServer {
560
555
  const endpoint = url.pathname.slice("/v1/".length).replace(/\/$/, "");
561
556
  meta = this.router.findLoggingMetadata(req.method, endpoint) ?? undefined;
562
557
  }
563
- return withRequestLogging(
564
- req,
565
- () => this.routeRequest(req, server),
566
- meta,
567
- );
558
+ return withRequestLogging(req, () => this.routeRequest(req, server), meta);
568
559
  }
569
560
 
570
561
  private async routeRequest(
@@ -14,10 +14,6 @@ import type {
14
14
  export type { SlackInboundMessageMetadata };
15
15
  import type { ServerMessage } from "../daemon/message-protocol.js";
16
16
  import type { AssistantEventHub } from "./assistant-event-hub.js";
17
- import type {
18
- ApprovalCopyGenerator,
19
- GuardianActionCopyGenerator,
20
- } from "./message-composer-types.js";
21
17
 
22
18
  export type {
23
19
  ApprovalCopyGenerator,
@@ -61,47 +57,13 @@ export type ApprovalConversationGenerator = (
61
57
  context: ApprovalConversationContext,
62
58
  ) => Promise<ApprovalConversationResult>;
63
59
 
64
- // ---------------------------------------------------------------------------
65
- // Guardian follow-up conversation flow types
66
- // ---------------------------------------------------------------------------
67
-
68
- /** The disposition returned by the guardian follow-up conversation engine. */
69
- export type GuardianFollowUpDisposition =
70
- | "call_back"
71
- | "decline"
72
- | "keep_pending";
73
-
74
- /** Structured result from a single turn of the guardian follow-up conversation. */
75
- export interface GuardianFollowUpTurnResult {
76
- disposition: GuardianFollowUpDisposition;
77
- replyText: string;
78
- }
79
-
80
- /** Input context for the guardian follow-up conversation engine. */
81
- export interface GuardianFollowUpConversationContext {
82
- /** The original question that was asked during the voice call. */
83
- questionText: string;
84
- /** The guardian's late answer text that initiated the follow-up. */
85
- lateAnswerText: string;
86
- /** The guardian's latest reply in the follow-up conversation. */
87
- guardianReply: string;
88
- }
89
-
90
- /**
91
- * Daemon-injected function that processes one turn of a guardian follow-up
92
- * conversation. Classifies the guardian's intent into a structured disposition
93
- * and produces a natural reply.
94
- */
95
- export type GuardianFollowUpConversationGenerator = (
96
- context: GuardianFollowUpConversationContext,
97
- ) => Promise<GuardianFollowUpTurnResult>;
98
-
99
60
  export interface RuntimeMessageConversationOptions {
100
61
  transport?: {
101
62
  channelId: ChannelId;
102
63
  hints?: string[];
103
64
  uxBrief?: string;
104
65
  chatType?: string;
66
+ clientTimezone?: string;
105
67
  };
106
68
  assistantId?: string;
107
69
  trustContext?: TrustContext;
@@ -113,8 +75,6 @@ export interface RuntimeMessageConversationOptions {
113
75
  isInteractive?: boolean;
114
76
  /** Channel command intent metadata (e.g. Telegram /start). */
115
77
  commandIntent?: { type: string; payload?: string; languageCode?: string };
116
- /** Slack-only non-persisted notice injected into the active model turn. */
117
- slackRuntimeContextNotice?: string;
118
78
  /**
119
79
  * Persisted user-facing content. When present, storage/UI use this value
120
80
  * while the model-facing turn continues to use `content`.
@@ -145,7 +105,7 @@ export type MessageProcessor = (
145
105
  options?: RuntimeMessageConversationOptions,
146
106
  sourceChannel?: ChannelId,
147
107
  sourceInterface?: InterfaceId,
148
- ) => Promise<{ messageId: string }>;
108
+ ) => Promise<{ messageId: string; assistantMessageId?: string }>;
149
109
 
150
110
  /**
151
111
  * Dependencies for the POST /v1/messages handler.
@@ -173,14 +133,6 @@ export interface RuntimeHttpServerOptions {
173
133
  port?: number;
174
134
  /** Hostname / IP to bind to. Defaults to '127.0.0.1' (loopback-only). */
175
135
  hostname?: string;
176
- /** Daemon-injected generator for approval copy (provider-backed). */
177
- approvalCopyGenerator?: ApprovalCopyGenerator;
178
- /** Daemon-injected generator for conversational approval flow (provider-backed). */
179
- approvalConversationGenerator?: ApprovalConversationGenerator;
180
- /** Daemon-injected generator for guardian action copy (provider-backed). */
181
- guardianActionCopyGenerator?: GuardianActionCopyGenerator;
182
- /** Daemon-injected generator for guardian follow-up conversation (provider-backed). */
183
- guardianFollowUpConversationGenerator?: GuardianFollowUpConversationGenerator;
184
136
  }
185
137
 
186
138
  export interface RuntimeAttachmentMetadata {
@@ -214,7 +166,6 @@ export interface RuntimeMessagePayload {
214
166
  approvalReason?: string;
215
167
  riskThreshold?: string;
216
168
  }>;
217
- interfaces?: string[];
218
169
  surfaces?: Array<{
219
170
  surfaceId: string;
220
171
  surfaceType: string;
@@ -236,8 +187,13 @@ export interface RuntimeMessagePayload {
236
187
  };
237
188
  slackMessage?: {
238
189
  channelId: string;
190
+ channelName?: string;
239
191
  channelTs: string;
240
192
  threadTs?: string;
193
+ sender?: {
194
+ displayName?: string;
195
+ externalUserId?: string;
196
+ };
241
197
  messageLink?: {
242
198
  appUrl?: string;
243
199
  webUrl?: string;