@vellumai/assistant 0.4.48 → 0.4.50

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 (423) hide show
  1. package/ARCHITECTURE.md +26 -35
  2. package/README.md +5 -26
  3. package/docs/architecture/integrations.md +45 -41
  4. package/docs/architecture/keychain-broker.md +3 -3
  5. package/docs/architecture/memory.md +180 -119
  6. package/docs/runbook-trusted-contacts.md +3 -8
  7. package/hook-templates/debug-prompt-logger/hook.json +1 -1
  8. package/hook-templates/debug-prompt-logger/run.sh +1 -3
  9. package/package.json +2 -2
  10. package/src/__tests__/actor-token-service.test.ts +0 -1
  11. package/src/__tests__/agent-loop.test.ts +3 -1
  12. package/src/__tests__/anthropic-provider.test.ts +249 -2
  13. package/src/__tests__/approval-cascade.test.ts +796 -0
  14. package/src/__tests__/approval-primitive.test.ts +0 -1
  15. package/src/__tests__/approval-routes-http.test.ts +4 -0
  16. package/src/__tests__/assistant-attachments.test.ts +12 -34
  17. package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
  18. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +76 -0
  19. package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -1
  20. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  21. package/src/__tests__/canonical-guardian-store.test.ts +95 -0
  22. package/src/__tests__/channel-guardian.test.ts +0 -2
  23. package/src/__tests__/channel-readiness-routes.test.ts +15 -6
  24. package/src/__tests__/channel-readiness-service.test.ts +10 -9
  25. package/src/__tests__/checker.test.ts +13 -20
  26. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
  27. package/src/__tests__/computer-use-tools.test.ts +2 -19
  28. package/src/__tests__/config-schema.test.ts +1 -68
  29. package/src/__tests__/config-watcher.test.ts +0 -1
  30. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  31. package/src/__tests__/context-image-dimensions.test.ts +332 -0
  32. package/src/__tests__/context-memory-e2e.test.ts +11 -100
  33. package/src/__tests__/context-token-estimator.test.ts +196 -13
  34. package/src/__tests__/conversation-attention-store.test.ts +0 -1
  35. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  36. package/src/__tests__/conversation-routes-guardian-reply.test.ts +152 -0
  37. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
  38. package/src/__tests__/credential-metadata-store.test.ts +64 -73
  39. package/src/__tests__/credential-security-e2e.test.ts +1 -0
  40. package/src/__tests__/credential-security-invariants.test.ts +13 -7
  41. package/src/__tests__/credential-vault-unit.test.ts +284 -49
  42. package/src/__tests__/credential-vault.test.ts +150 -16
  43. package/src/__tests__/credentials-cli.test.ts +71 -0
  44. package/src/__tests__/cu-unified-flow.test.ts +532 -0
  45. package/src/__tests__/date-context.test.ts +93 -77
  46. package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
  47. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  48. package/src/__tests__/ephemeral-permissions.test.ts +3 -3
  49. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  50. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
  51. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
  52. package/src/__tests__/guardian-routing-invariants.test.ts +93 -1
  53. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
  54. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -39
  55. package/src/__tests__/heartbeat-service.test.ts +0 -1
  56. package/src/__tests__/history-repair.test.ts +245 -0
  57. package/src/__tests__/host-cu-proxy.test.ts +791 -0
  58. package/src/__tests__/host-shell-tool.test.ts +27 -15
  59. package/src/__tests__/http-user-message-parity.test.ts +2 -0
  60. package/src/__tests__/ingress-url-consistency.test.ts +14 -21
  61. package/src/__tests__/integration-status.test.ts +32 -51
  62. package/src/__tests__/intent-routing.test.ts +0 -1
  63. package/src/__tests__/invite-redemption-service.test.ts +65 -1
  64. package/src/__tests__/invite-routes-http.test.ts +10 -9
  65. package/src/__tests__/keychain-broker-client.test.ts +14 -46
  66. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
  67. package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
  68. package/src/__tests__/memory-recall-quality.test.ts +244 -407
  69. package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
  70. package/src/__tests__/memory-regressions.test.ts +477 -2841
  71. package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
  72. package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
  73. package/src/__tests__/mime-builder.test.ts +28 -0
  74. package/src/__tests__/native-web-search.test.ts +1 -0
  75. package/src/__tests__/notification-routing-intent.test.ts +0 -1
  76. package/src/__tests__/oauth-cli.test.ts +941 -15
  77. package/src/__tests__/oauth-provider-profiles.test.ts +9 -9
  78. package/src/__tests__/oauth-scope-policy.test.ts +4 -6
  79. package/src/__tests__/oauth-store.test.ts +870 -0
  80. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
  81. package/src/__tests__/provider-error-scenarios.test.ts +0 -1
  82. package/src/__tests__/provider-streaming.benchmark.test.ts +0 -1
  83. package/src/__tests__/public-ingress-urls.test.ts +15 -21
  84. package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
  85. package/src/__tests__/recording-handler.test.ts +3 -4
  86. package/src/__tests__/registry.test.ts +2 -3
  87. package/src/__tests__/relay-server.test.ts +46 -1
  88. package/src/__tests__/runtime-events-sse.test.ts +55 -7
  89. package/src/__tests__/schedule-store.test.ts +0 -1
  90. package/src/__tests__/schedule-tools.test.ts +32 -0
  91. package/src/__tests__/scheduler-recurrence.test.ts +0 -1
  92. package/src/__tests__/scoped-approval-grants.test.ts +0 -1
  93. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
  94. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  95. package/src/__tests__/secret-ingress-handler.test.ts +0 -1
  96. package/src/__tests__/secret-onetime-send.test.ts +1 -0
  97. package/src/__tests__/secure-keys.test.ts +7 -2
  98. package/src/__tests__/send-endpoint-busy.test.ts +24 -6
  99. package/src/__tests__/sequence-store.test.ts +0 -1
  100. package/src/__tests__/session-abort-tool-results.test.ts +1 -14
  101. package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
  102. package/src/__tests__/session-agent-loop.test.ts +19 -15
  103. package/src/__tests__/session-confirmation-signals.test.ts +1 -15
  104. package/src/__tests__/session-error.test.ts +124 -2
  105. package/src/__tests__/session-history-web-search.test.ts +918 -0
  106. package/src/__tests__/session-init.benchmark.test.ts +4 -5
  107. package/src/__tests__/session-pre-run-repair.test.ts +1 -14
  108. package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
  109. package/src/__tests__/session-queue.test.ts +37 -27
  110. package/src/__tests__/session-runtime-assembly.test.ts +54 -0
  111. package/src/__tests__/session-slash-known.test.ts +1 -15
  112. package/src/__tests__/session-slash-queue.test.ts +1 -15
  113. package/src/__tests__/session-slash-unknown.test.ts +1 -15
  114. package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
  115. package/src/__tests__/session-workspace-injection.test.ts +3 -37
  116. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
  117. package/src/__tests__/skill-include-graph.test.ts +66 -0
  118. package/src/__tests__/skill-load-feature-flag.test.ts +0 -1
  119. package/src/__tests__/skill-load-tool.test.ts +149 -1
  120. package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
  121. package/src/__tests__/skills-install-extract.test.ts +93 -0
  122. package/src/__tests__/skills-uninstall.test.ts +1 -1
  123. package/src/__tests__/skills.test.ts +3 -3
  124. package/src/__tests__/skillssh-registry.test.ts +451 -0
  125. package/src/__tests__/slack-channel-config.test.ts +67 -3
  126. package/src/__tests__/slack-share-routes.test.ts +17 -19
  127. package/src/__tests__/system-prompt.test.ts +0 -1
  128. package/src/__tests__/telegram-invite-adapter.test.ts +18 -22
  129. package/src/__tests__/terminal-tools.test.ts +4 -3
  130. package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
  131. package/src/__tests__/tool-approval-handler.test.ts +0 -1
  132. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
  133. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  134. package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
  135. package/src/__tests__/tool-executor.test.ts +0 -1
  136. package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
  137. package/src/__tests__/trust-store-pattern-matches.test.ts +29 -0
  138. package/src/__tests__/trust-store.test.ts +7 -13
  139. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  140. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
  141. package/src/__tests__/twilio-routes.test.ts +0 -16
  142. package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
  143. package/src/__tests__/voice-invite-redemption.test.ts +32 -1
  144. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
  145. package/src/agent/ax-tree-compaction.test.ts +286 -0
  146. package/src/agent/loop.ts +104 -131
  147. package/src/approvals/AGENTS.md +1 -1
  148. package/src/approvals/guardian-request-resolvers.ts +14 -2
  149. package/src/bundler/compiler-tools.ts +66 -2
  150. package/src/calls/call-domain.ts +133 -6
  151. package/src/calls/call-store.ts +6 -0
  152. package/src/calls/relay-server.ts +52 -18
  153. package/src/calls/relay-setup-router.ts +17 -1
  154. package/src/calls/twilio-config.ts +3 -8
  155. package/src/calls/twilio-routes.ts +1 -2
  156. package/src/calls/types.ts +3 -1
  157. package/src/calls/voice-ingress-preflight.ts +1 -1
  158. package/src/cli/commands/browser-relay.ts +18 -12
  159. package/src/cli/commands/completions.ts +0 -3
  160. package/src/cli/commands/credentials.ts +101 -15
  161. package/src/cli/commands/doctor.ts +4 -3
  162. package/src/cli/commands/mcp.ts +46 -59
  163. package/src/cli/commands/memory.ts +16 -165
  164. package/src/cli/commands/oauth/apps.ts +284 -0
  165. package/src/cli/commands/oauth/connections.ts +633 -0
  166. package/src/cli/commands/oauth/index.ts +52 -0
  167. package/src/cli/commands/oauth/providers.ts +256 -0
  168. package/src/cli/commands/sessions.ts +5 -2
  169. package/src/cli/commands/skills.ts +177 -339
  170. package/src/cli/http-client.ts +0 -20
  171. package/src/cli/main-screen.tsx +2 -2
  172. package/src/cli/program.ts +6 -11
  173. package/src/cli/reference.ts +1 -3
  174. package/src/cli.ts +4 -10
  175. package/src/config/assistant-feature-flags.ts +0 -3
  176. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
  177. package/src/config/bundled-skills/computer-use/SKILL.md +3 -6
  178. package/src/config/bundled-skills/computer-use/TOOLS.json +23 -5
  179. package/src/config/bundled-skills/computer-use/tools/{computer-use-request-control.ts → computer-use-observe.ts} +1 -5
  180. package/src/config/bundled-skills/google-calendar/calendar-client.ts +21 -16
  181. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -4
  182. package/src/config/bundled-skills/settings/SKILL.md +1 -1
  183. package/src/config/bundled-skills/settings/TOOLS.json +2 -8
  184. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +5 -33
  185. package/src/config/bundled-tool-registry.ts +2 -5
  186. package/src/config/env-registry.ts +14 -83
  187. package/src/config/env.ts +11 -50
  188. package/src/config/feature-flag-registry.json +16 -16
  189. package/src/config/loader.ts +0 -6
  190. package/src/config/schema.ts +4 -13
  191. package/src/config/schemas/memory-lifecycle.ts +0 -9
  192. package/src/config/schemas/memory-processing.ts +0 -180
  193. package/src/config/schemas/memory-retrieval.ts +32 -104
  194. package/src/config/schemas/memory.ts +0 -10
  195. package/src/config/skills.ts +21 -2
  196. package/src/config/types.ts +0 -4
  197. package/src/context/image-dimensions.ts +229 -0
  198. package/src/context/token-estimator.ts +75 -12
  199. package/src/context/window-manager.ts +53 -11
  200. package/src/daemon/assistant-attachments.ts +1 -13
  201. package/src/daemon/config-watcher.ts +61 -3
  202. package/src/daemon/daemon-control.ts +1 -1
  203. package/src/daemon/date-context.ts +114 -31
  204. package/src/daemon/handlers/config-ingress.ts +8 -33
  205. package/src/daemon/handlers/config-slack-channel.ts +49 -46
  206. package/src/daemon/handlers/config-telegram.ts +32 -16
  207. package/src/daemon/handlers/sessions.ts +27 -36
  208. package/src/daemon/handlers/shared.ts +0 -130
  209. package/src/daemon/handlers/skills.ts +20 -1
  210. package/src/daemon/history-repair.ts +72 -8
  211. package/src/daemon/host-cu-proxy.ts +430 -0
  212. package/src/daemon/lifecycle.ts +67 -71
  213. package/src/daemon/mcp-reload-service.ts +2 -2
  214. package/src/daemon/message-protocol.ts +3 -0
  215. package/src/daemon/message-types/computer-use.ts +1 -129
  216. package/src/daemon/message-types/host-cu.ts +19 -0
  217. package/src/daemon/message-types/memory.ts +4 -16
  218. package/src/daemon/message-types/messages.ts +4 -0
  219. package/src/daemon/message-types/sessions.ts +4 -0
  220. package/src/daemon/server.ts +25 -21
  221. package/src/daemon/session-agent-loop-handlers.ts +40 -0
  222. package/src/daemon/session-agent-loop.ts +334 -48
  223. package/src/daemon/session-attachments.ts +1 -2
  224. package/src/daemon/session-error.ts +89 -6
  225. package/src/daemon/session-history.ts +17 -7
  226. package/src/daemon/session-media-retry.ts +6 -2
  227. package/src/daemon/session-memory.ts +69 -149
  228. package/src/daemon/session-process.ts +10 -1
  229. package/src/daemon/session-runtime-assembly.ts +49 -19
  230. package/src/daemon/session-slash.ts +1 -1
  231. package/src/daemon/session-surfaces.ts +43 -28
  232. package/src/daemon/session-tool-setup.ts +9 -10
  233. package/src/daemon/session.ts +150 -17
  234. package/src/daemon/tool-side-effects.ts +2 -8
  235. package/src/daemon/watch-handler.ts +2 -2
  236. package/src/events/tool-metrics-listener.ts +2 -2
  237. package/src/hooks/manager.ts +1 -4
  238. package/src/inbound/public-ingress-urls.ts +7 -7
  239. package/src/instrument.ts +61 -1
  240. package/src/logfire.ts +16 -5
  241. package/src/memory/admin.ts +2 -191
  242. package/src/memory/canonical-guardian-store.ts +38 -2
  243. package/src/memory/conversation-crud.ts +0 -33
  244. package/src/memory/conversation-key-store.ts +21 -0
  245. package/src/memory/conversation-queries.ts +22 -3
  246. package/src/memory/db-init.ts +32 -0
  247. package/src/memory/embedding-backend.ts +84 -8
  248. package/src/memory/embedding-types.ts +9 -1
  249. package/src/memory/indexer.ts +7 -46
  250. package/src/memory/items-extractor.ts +274 -76
  251. package/src/memory/job-handlers/backfill.ts +2 -127
  252. package/src/memory/job-handlers/cleanup.ts +2 -16
  253. package/src/memory/job-handlers/extraction.ts +2 -138
  254. package/src/memory/job-handlers/index-maintenance.ts +1 -6
  255. package/src/memory/job-handlers/summarization.ts +3 -148
  256. package/src/memory/job-utils.ts +21 -59
  257. package/src/memory/jobs-store.ts +1 -159
  258. package/src/memory/jobs-worker.ts +9 -52
  259. package/src/memory/migrations/104-core-indexes.ts +3 -3
  260. package/src/memory/migrations/149-oauth-tables.ts +62 -0
  261. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
  262. package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
  263. package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
  264. package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
  265. package/src/memory/migrations/154-drop-fts.ts +20 -0
  266. package/src/memory/migrations/155-drop-conflicts.ts +7 -0
  267. package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
  268. package/src/memory/migrations/index.ts +8 -0
  269. package/src/memory/qdrant-client.ts +148 -51
  270. package/src/memory/raw-query.ts +1 -1
  271. package/src/memory/retriever.test.ts +294 -273
  272. package/src/memory/retriever.ts +421 -645
  273. package/src/memory/schema/calls.ts +2 -0
  274. package/src/memory/schema/index.ts +1 -0
  275. package/src/memory/schema/memory-core.ts +3 -48
  276. package/src/memory/schema/oauth.ts +67 -0
  277. package/src/memory/search/formatting.ts +263 -176
  278. package/src/memory/search/lexical.ts +1 -254
  279. package/src/memory/search/ranking.ts +0 -455
  280. package/src/memory/search/semantic.ts +100 -14
  281. package/src/memory/search/staleness.ts +47 -0
  282. package/src/memory/search/tier-classifier.ts +21 -0
  283. package/src/memory/search/types.ts +15 -77
  284. package/src/memory/task-memory-cleanup.ts +4 -6
  285. package/src/messaging/provider.ts +4 -4
  286. package/src/messaging/providers/gmail/client.ts +82 -2
  287. package/src/messaging/providers/gmail/mime-builder.ts +17 -7
  288. package/src/messaging/providers/gmail/people-client.ts +10 -10
  289. package/src/messaging/providers/telegram-bot/adapter.ts +17 -17
  290. package/src/messaging/providers/whatsapp/adapter.ts +11 -8
  291. package/src/messaging/registry.ts +2 -32
  292. package/src/notifications/copy-composer.ts +0 -5
  293. package/src/notifications/signal.ts +4 -5
  294. package/src/oauth/byo-connection.test.ts +133 -25
  295. package/src/oauth/byo-connection.ts +22 -6
  296. package/src/oauth/connect-orchestrator.ts +113 -57
  297. package/src/oauth/connect-types.ts +17 -23
  298. package/src/oauth/connection-resolver.ts +35 -11
  299. package/src/oauth/connection.ts +1 -1
  300. package/src/oauth/manual-token-connection.ts +104 -0
  301. package/src/oauth/oauth-store.ts +582 -0
  302. package/src/oauth/platform-connection.test.ts +29 -0
  303. package/src/oauth/platform-connection.ts +6 -5
  304. package/src/oauth/provider-behaviors.ts +124 -0
  305. package/src/oauth/scope-policy.ts +9 -2
  306. package/src/oauth/seed-providers.ts +167 -0
  307. package/src/oauth/token-persistence.ts +81 -77
  308. package/src/permissions/checker.ts +3 -3
  309. package/src/permissions/defaults.ts +1 -1
  310. package/src/permissions/prompter.ts +10 -1
  311. package/src/permissions/trust-store.ts +36 -1
  312. package/src/playbooks/playbook-compiler.ts +1 -1
  313. package/src/prompts/__tests__/build-cli-reference-section.test.ts +3 -1
  314. package/src/prompts/system-prompt.ts +46 -42
  315. package/src/providers/anthropic/client.ts +59 -20
  316. package/src/providers/retry.ts +1 -27
  317. package/src/providers/types.ts +7 -1
  318. package/src/runtime/AGENTS.md +9 -0
  319. package/src/runtime/auth/route-policy.ts +6 -6
  320. package/src/runtime/channel-reply-delivery.ts +0 -40
  321. package/src/runtime/gateway-client.ts +0 -7
  322. package/src/runtime/guardian-reply-router.ts +24 -22
  323. package/src/runtime/http-server.ts +10 -8
  324. package/src/runtime/http-types.ts +2 -2
  325. package/src/runtime/invite-redemption-service.ts +19 -1
  326. package/src/runtime/invite-service.ts +25 -0
  327. package/src/runtime/middleware/twilio-validation.ts +1 -11
  328. package/src/runtime/pending-interactions.ts +14 -12
  329. package/src/runtime/routes/brain-graph-routes.ts +10 -90
  330. package/src/runtime/routes/channel-delivery-routes.ts +0 -1
  331. package/src/runtime/routes/conversation-routes.ts +81 -19
  332. package/src/runtime/routes/events-routes.ts +21 -11
  333. package/src/runtime/routes/host-cu-routes.ts +97 -0
  334. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
  335. package/src/runtime/routes/inbound-stages/background-dispatch.ts +12 -111
  336. package/src/runtime/routes/integrations/slack/share.ts +6 -7
  337. package/src/runtime/routes/log-export-routes.ts +126 -8
  338. package/src/runtime/routes/memory-item-routes.test.ts +754 -0
  339. package/src/runtime/routes/memory-item-routes.ts +503 -0
  340. package/src/runtime/routes/session-management-routes.ts +3 -3
  341. package/src/runtime/routes/settings-routes.ts +55 -48
  342. package/src/runtime/routes/surface-action-routes.ts +1 -1
  343. package/src/runtime/routes/trust-rules-routes.ts +14 -0
  344. package/src/runtime/routes/watch-routes.ts +128 -0
  345. package/src/runtime/routes/workspace-routes.ts +2 -1
  346. package/src/schedule/integration-status.ts +10 -9
  347. package/src/security/credential-key.ts +0 -156
  348. package/src/security/keychain-broker-client.ts +22 -10
  349. package/src/security/oauth2.ts +1 -1
  350. package/src/security/secure-keys.ts +25 -3
  351. package/src/security/token-manager.ts +137 -64
  352. package/src/skills/catalog-install.ts +414 -0
  353. package/src/skills/include-graph.ts +32 -0
  354. package/src/skills/skillssh-registry.ts +503 -0
  355. package/src/telegram/bot-username.ts +2 -3
  356. package/src/tools/assets/search.ts +5 -1
  357. package/src/tools/browser/network-recorder.ts +1 -1
  358. package/src/tools/browser/network-recording-types.ts +1 -1
  359. package/src/tools/computer-use/definitions.ts +36 -11
  360. package/src/tools/computer-use/registry.ts +5 -6
  361. package/src/tools/credentials/broker.ts +1 -2
  362. package/src/tools/credentials/metadata-store.ts +17 -121
  363. package/src/tools/credentials/vault.ts +92 -167
  364. package/src/tools/memory/definitions.ts +4 -13
  365. package/src/tools/memory/handlers.test.ts +83 -103
  366. package/src/tools/memory/handlers.ts +50 -85
  367. package/src/tools/registry.ts +2 -7
  368. package/src/tools/schedule/create.ts +8 -1
  369. package/src/tools/schedule/update.ts +8 -1
  370. package/src/tools/skills/load.ts +85 -3
  371. package/src/tools/watch/watch-state.ts +0 -12
  372. package/src/util/logger.ts +7 -41
  373. package/src/util/platform.ts +9 -28
  374. package/src/watcher/providers/google-calendar.ts +2 -1
  375. package/src/__tests__/clarification-resolver.test.ts +0 -193
  376. package/src/__tests__/computer-use-session-compaction.test.ts +0 -143
  377. package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -322
  378. package/src/__tests__/computer-use-session-working-dir.test.ts +0 -166
  379. package/src/__tests__/computer-use-skill-baseline.test.ts +0 -78
  380. package/src/__tests__/computer-use-skill-endstate.test.ts +0 -105
  381. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -249
  382. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
  383. package/src/__tests__/conflict-policy.test.ts +0 -269
  384. package/src/__tests__/conflict-store.test.ts +0 -372
  385. package/src/__tests__/contradiction-checker.test.ts +0 -361
  386. package/src/__tests__/entity-extractor.test.ts +0 -211
  387. package/src/__tests__/entity-search.test.ts +0 -1117
  388. package/src/__tests__/profile-compiler.test.ts +0 -392
  389. package/src/__tests__/ride-shotgun-handler.test.ts +0 -452
  390. package/src/__tests__/session-conflict-gate.test.ts +0 -1228
  391. package/src/__tests__/session-profile-injection.test.ts +0 -557
  392. package/src/cli/commands/dev.ts +0 -129
  393. package/src/cli/commands/map.ts +0 -391
  394. package/src/cli/commands/oauth.ts +0 -77
  395. package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
  396. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
  397. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
  398. package/src/daemon/computer-use-session.ts +0 -1026
  399. package/src/daemon/ride-shotgun-handler.ts +0 -569
  400. package/src/daemon/session-conflict-gate.ts +0 -167
  401. package/src/daemon/session-dynamic-profile.ts +0 -77
  402. package/src/memory/clarification-resolver.ts +0 -417
  403. package/src/memory/conflict-intent.ts +0 -205
  404. package/src/memory/conflict-policy.ts +0 -127
  405. package/src/memory/conflict-store.ts +0 -410
  406. package/src/memory/contradiction-checker.ts +0 -508
  407. package/src/memory/entity-extractor.ts +0 -535
  408. package/src/memory/format-recall.ts +0 -47
  409. package/src/memory/fts-reconciler.ts +0 -165
  410. package/src/memory/job-handlers/conflict.ts +0 -200
  411. package/src/memory/profile-compiler.ts +0 -195
  412. package/src/memory/recall-cache.ts +0 -117
  413. package/src/memory/search/entity.ts +0 -535
  414. package/src/memory/search/query-expansion.test.ts +0 -70
  415. package/src/memory/search/query-expansion.ts +0 -118
  416. package/src/oauth/provider-base-urls.ts +0 -21
  417. package/src/oauth/provider-profiles.ts +0 -192
  418. package/src/prompts/computer-use-prompt.ts +0 -98
  419. package/src/runtime/routes/computer-use-routes.ts +0 -641
  420. package/src/runtime/routes/mcp-routes.ts +0 -20
  421. package/src/runtime/telegram-streaming-delivery.test.ts +0 -729
  422. package/src/runtime/telegram-streaming-delivery.ts +0 -393
  423. package/src/tools/computer-use/request-computer-control.ts +0 -56
@@ -17,6 +17,7 @@ import {
17
17
  import { getConfig } from "../../config/loader.js";
18
18
  import { renderHistoryContent } from "../../daemon/handlers/shared.js";
19
19
  import { HostBashProxy } from "../../daemon/host-bash-proxy.js";
20
+ import { HostCuProxy } from "../../daemon/host-cu-proxy.js";
20
21
  import { HostFileProxy } from "../../daemon/host-file-proxy.js";
21
22
  import type { ServerMessage } from "../../daemon/message-protocol.js";
22
23
  import {
@@ -449,6 +450,12 @@ function makeHubPublisher(
449
450
  conversationId,
450
451
  kind: "host_file",
451
452
  });
453
+ } else if (msg.type === "host_cu_request") {
454
+ pendingInteractions.register(msg.requestId, {
455
+ session,
456
+ conversationId,
457
+ kind: "host_cu",
458
+ });
452
459
  }
453
460
 
454
461
  // ServerMessage is a large union; sessionId exists on most but not all variants.
@@ -640,9 +647,22 @@ export async function handleSendMessage(
640
647
  });
641
648
  session.setHostFileProxy(fileProxy);
642
649
  }
650
+ if (!session.isProcessing() || !session.hostCuProxy) {
651
+ const cuProxy = new HostCuProxy(onEvent, (requestId) => {
652
+ pendingInteractions.resolve(requestId);
653
+ });
654
+ session.setHostCuProxy(cuProxy);
655
+ }
656
+ // Only preactivate CU when the session is idle — if the session is
657
+ // processing, this message will be queued and preactivation is deferred
658
+ // to dequeue time in drainQueueImpl to avoid mutating in-flight turn state.
659
+ if (!session.isProcessing()) {
660
+ session.addPreactivatedSkillId("computer-use");
661
+ }
643
662
  } else if (!session.isProcessing()) {
644
663
  session.setHostBashProxy(undefined);
645
664
  session.setHostFileProxy(undefined);
665
+ session.setHostCuProxy(undefined);
646
666
  }
647
667
  // Wire sendToClient to the SSE hub so all subsystems can reach the HTTP client.
648
668
  // Called after setHostBashProxy so updateSender targets the current proxy.
@@ -679,7 +699,13 @@ export async function handleSendMessage(
679
699
  attachments,
680
700
  session,
681
701
  onEvent,
682
- approvalConversationGenerator: deps.approvalConversationGenerator,
702
+ // Desktop path: disable NL classification to avoid consuming non-decision
703
+ // messages while a tool confirmation is pending. Deterministic code-prefix
704
+ // and callback parsing remain active. Mirrors session-process.ts behavior.
705
+ approvalConversationGenerator:
706
+ sourceChannel === "vellum"
707
+ ? undefined
708
+ : deps.approvalConversationGenerator,
683
709
  verifiedActorExternalUserId,
684
710
  verifiedActorPrincipalId,
685
711
  });
@@ -687,6 +713,7 @@ export async function handleSendMessage(
687
713
  return Response.json(
688
714
  {
689
715
  accepted: true,
716
+ conversationId: mapping.conversationId,
690
717
  ...(inlineReplyResult.messageId
691
718
  ? { messageId: inlineReplyResult.messageId }
692
719
  : {}),
@@ -751,7 +778,10 @@ export async function handleSendMessage(
751
778
  pendingInteractions.removeBySession(session);
752
779
  }
753
780
 
754
- return Response.json({ accepted: true, queued: true }, { status: 202 });
781
+ return Response.json(
782
+ { accepted: true, queued: true, conversationId: mapping.conversationId },
783
+ { status: 202 },
784
+ );
755
785
  }
756
786
 
757
787
  // Session is idle — persist and fire agent loop immediately
@@ -782,6 +812,7 @@ export async function handleSendMessage(
782
812
 
783
813
  if (slashResult.kind === "unknown") {
784
814
  session.processing = true;
815
+ let cleanupDeferred = false;
785
816
  try {
786
817
  const provenance = provenanceFromTrustContext(session.trustContext);
787
818
  const channelMeta = {
@@ -818,26 +849,54 @@ export async function handleSendMessage(
818
849
  sourceInterface,
819
850
  );
820
851
 
821
- // Emit fresh model info before the text delta so the client has
822
- // up-to-date configuredProviders when rendering /model, /models,
823
- // and provider shortcut commands (/gpt4, /opus, etc.).
824
- if (isModelSlashCommand(rawContent) || isProviderShortcut(rawContent)) {
825
- onEvent(buildModelInfoEvent());
826
- }
827
-
828
- onEvent({ type: "assistant_text_delta", text: slashResult.message });
829
- onEvent({
830
- type: "message_complete",
831
- sessionId: mapping.conversationId,
832
- });
852
+ // Snapshot model info now so the deferred callback cannot observe
853
+ // a config change from a concurrent request.
854
+ const modelInfoEvent =
855
+ isModelSlashCommand(rawContent) || isProviderShortcut(rawContent)
856
+ ? buildModelInfoEvent()
857
+ : null;
833
858
 
834
- return Response.json(
835
- { accepted: true, messageId: persisted.id },
859
+ const response = Response.json(
860
+ {
861
+ accepted: true,
862
+ messageId: persisted.id,
863
+ conversationId: mapping.conversationId,
864
+ },
836
865
  { status: 202 },
837
866
  );
867
+
868
+ // Defer event publishing to next tick so the HTTP response reaches the
869
+ // client first. This ensures the client's serverToLocalSessionMap is
870
+ // populated before SSE events arrive, preventing dropped events in new
871
+ // desktop threads.
872
+ //
873
+ // session.processing and drainQueue are also deferred so the current
874
+ // slash command's events are emitted before the next queued message
875
+ // starts processing.
876
+ const conversationId = mapping.conversationId;
877
+ const message = slashResult.message;
878
+ setTimeout(() => {
879
+ if (modelInfoEvent) {
880
+ onEvent(modelInfoEvent);
881
+ }
882
+ onEvent({ type: "assistant_text_delta", text: message });
883
+ onEvent({
884
+ type: "message_complete",
885
+ sessionId: conversationId,
886
+ });
887
+ session.processing = false;
888
+ session.drainQueue().catch(() => {});
889
+ }, 0);
890
+
891
+ cleanupDeferred = true;
892
+ return response;
838
893
  } finally {
839
- session.processing = false;
840
- session.drainQueue().catch(() => {});
894
+ // No-op for the slash-command early-return path (handled inside
895
+ // setTimeout above), but still needed for error paths.
896
+ if (!cleanupDeferred && session.processing) {
897
+ session.processing = false;
898
+ session.drainQueue().catch(() => {});
899
+ }
841
900
  }
842
901
  }
843
902
 
@@ -874,7 +933,10 @@ export async function handleSendMessage(
874
933
  );
875
934
  });
876
935
 
877
- return Response.json({ accepted: true, messageId }, { status: 202 });
936
+ return Response.json(
937
+ { accepted: true, messageId, conversationId: mapping.conversationId },
938
+ { status: 202 },
939
+ );
878
940
  }
879
941
 
880
942
  async function generateLlmSuggestion(
@@ -7,12 +7,17 @@
7
7
  * is called. The AuthContext is threaded through from the HTTP server
8
8
  * layer, so no additional actor-token verification is needed here.
9
9
  *
10
- * Subscribers receive all assistant events scoped to the given conversation.
10
+ * When `conversationKey` is provided, subscribers receive events scoped to
11
+ * that conversation. When omitted, subscribers receive events from ALL
12
+ * conversations for this assistant (unfiltered).
11
13
  */
12
14
 
13
15
  import { getOrCreateConversation } from "../../memory/conversation-key-store.js";
14
16
  import { formatSseFrame, formatSseHeartbeat } from "../assistant-event.js";
15
- import type { AssistantEventSubscription } from "../assistant-event-hub.js";
17
+ import type {
18
+ AssistantEventFilter,
19
+ AssistantEventSubscription,
20
+ } from "../assistant-event-hub.js";
16
21
  import {
17
22
  AssistantEventHub,
18
23
  assistantEventHub,
@@ -26,10 +31,12 @@ import type { RouteDefinition } from "../http-router.js";
26
31
  const DEFAULT_HEARTBEAT_INTERVAL_MS = 30_000;
27
32
 
28
33
  /**
29
- * Stream assistant events as Server-Sent Events for a specific conversation.
34
+ * Stream assistant events as Server-Sent Events.
30
35
  *
31
36
  * Query params:
32
- * conversationKey -- required; scopes the stream to one conversation.
37
+ * conversationKey -- optional; when provided, scopes the stream to one
38
+ * conversation. When omitted, the stream delivers events
39
+ * from ALL conversations for this assistant.
33
40
  *
34
41
  * Options (for testing):
35
42
  * hub -- override the event hub (defaults to process singleton).
@@ -56,15 +63,21 @@ export function handleSubscribeAssistantEvents(
56
63
  // scope and principal type requirements.
57
64
 
58
65
  const conversationKey = url.searchParams.get("conversationKey");
59
- if (!conversationKey) {
60
- return httpError("BAD_REQUEST", "conversationKey is required", 400);
66
+ if (url.searchParams.has("conversationKey") && !conversationKey?.trim()) {
67
+ return httpError("BAD_REQUEST", "conversationKey must not be empty", 400);
61
68
  }
62
69
 
63
70
  const hub = options?.hub ?? assistantEventHub;
64
71
  const heartbeatIntervalMs =
65
72
  options?.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
66
73
 
67
- const mapping = getOrCreateConversation(conversationKey);
74
+ const filter: AssistantEventFilter = {
75
+ assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
76
+ };
77
+ if (conversationKey) {
78
+ const mapping = getOrCreateConversation(conversationKey);
79
+ filter.sessionId = mapping.conversationId;
80
+ }
68
81
  const encoder = new TextEncoder();
69
82
 
70
83
  // -- Eager subscribe --------------------------------------------------------
@@ -90,10 +103,7 @@ export function handleSubscribeAssistantEvents(
90
103
 
91
104
  try {
92
105
  sub = hub.subscribe(
93
- {
94
- assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
95
- sessionId: mapping.conversationId,
96
- },
106
+ filter,
97
107
  (event) => {
98
108
  const controller = controllerRef;
99
109
  if (!controller) return;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Route handler for host CU (computer-use) result submissions.
3
+ *
4
+ * Resolves pending host CU proxy requests by requestId when the desktop
5
+ * client returns observation results via HTTP.
6
+ */
7
+ import { requireBoundGuardian } from "../auth/require-bound-guardian.js";
8
+ import type { AuthContext } from "../auth/types.js";
9
+ import { httpError } from "../http-errors.js";
10
+ import type { RouteDefinition } from "../http-router.js";
11
+ import * as pendingInteractions from "../pending-interactions.js";
12
+
13
+ /**
14
+ * POST /v1/host-cu-result — resolve a pending host CU request by requestId.
15
+ * Requires AuthContext with guardian-bound actor.
16
+ */
17
+ export async function handleHostCuResult(
18
+ req: Request,
19
+ authContext: AuthContext,
20
+ ): Promise<Response> {
21
+ const authError = requireBoundGuardian(authContext);
22
+ if (authError) return authError;
23
+
24
+ const body = (await req.json()) as {
25
+ requestId?: string;
26
+ axTree?: string;
27
+ axDiff?: string;
28
+ screenshot?: string;
29
+ screenshotWidthPx?: number;
30
+ screenshotHeightPx?: number;
31
+ screenWidthPt?: number;
32
+ screenHeightPt?: number;
33
+ executionResult?: string;
34
+ executionError?: string;
35
+ secondaryWindows?: string;
36
+ userGuidance?: string;
37
+ };
38
+
39
+ const { requestId } = body;
40
+
41
+ if (!requestId || typeof requestId !== "string") {
42
+ return httpError("BAD_REQUEST", "requestId is required", 400);
43
+ }
44
+
45
+ // Peek first (non-destructive) so we can validate the interaction kind
46
+ // without accidentally consuming a confirmation or secret interaction.
47
+ const peeked = pendingInteractions.get(requestId);
48
+ if (!peeked) {
49
+ return httpError(
50
+ "NOT_FOUND",
51
+ "No pending interaction found for this requestId",
52
+ 404,
53
+ );
54
+ }
55
+
56
+ if (peeked.kind !== "host_cu") {
57
+ return httpError(
58
+ "CONFLICT",
59
+ `Pending interaction is of kind "${peeked.kind}", expected "host_cu"`,
60
+ 409,
61
+ );
62
+ }
63
+
64
+ // Validation passed — consume the pending interaction.
65
+ const interaction = pendingInteractions.resolve(requestId)!;
66
+
67
+ interaction.session.resolveHostCu(requestId, {
68
+ axTree: body.axTree,
69
+ axDiff: body.axDiff,
70
+ screenshot: body.screenshot,
71
+ screenshotWidthPx: body.screenshotWidthPx,
72
+ screenshotHeightPx: body.screenshotHeightPx,
73
+ screenWidthPt: body.screenWidthPt,
74
+ screenHeightPt: body.screenHeightPt,
75
+ executionResult: body.executionResult,
76
+ executionError: body.executionError,
77
+ secondaryWindows: body.secondaryWindows,
78
+ userGuidance: body.userGuidance,
79
+ });
80
+
81
+ return Response.json({ accepted: true });
82
+ }
83
+
84
+ // ---------------------------------------------------------------------------
85
+ // Route definitions
86
+ // ---------------------------------------------------------------------------
87
+
88
+ export function hostCuRouteDefinitions(): RouteDefinition[] {
89
+ return [
90
+ {
91
+ endpoint: "host-cu-result",
92
+ method: "POST",
93
+ handler: async ({ req, authContext }) =>
94
+ handleHostCuResult(req, authContext),
95
+ },
96
+ ];
97
+ }
@@ -334,7 +334,7 @@ export async function enforceIngressAcl(
334
334
  dmCallbackUrl,
335
335
  {
336
336
  chatId: senderUserId,
337
- text: "I've notified the owner. They'll share a verification code with you if they approve access. You can reply with the code here.",
337
+ text: "I've notified the owner that you'd like to chat with me. If they approve your request, they'll share a 6-digit verification code with you. You can reply with the code here.",
338
338
  assistantId,
339
339
  },
340
340
  mintBearerToken(),
@@ -423,11 +423,12 @@ export async function enforceIngressAcl(
423
423
 
424
424
  if (resolvedMember) {
425
425
  if (resolvedMember.channel.status !== "active") {
426
+ const isBlockedMember = resolvedMember.channel.status === "blocked";
426
427
  // Same bypass logic as the no-member branch: verification codes and
427
- // bootstrap commands must pass through even when the member record is
428
- // revoked/blocked otherwise the user can never re-verify.
428
+ // bootstrap commands must pass through for re-verifiable states
429
+ // (pending/revoked), but never for blocked members.
429
430
  let denyInactiveMember = true;
430
- if (isGuardianVerifyCode) {
431
+ if (!isBlockedMember && isGuardianVerifyCode) {
431
432
  const hasPendingChallenge = !!getPendingSession(sourceChannel);
432
433
  const hasActiveOutboundSession = !!findActiveSession(sourceChannel);
433
434
  if (hasPendingChallenge || hasActiveOutboundSession) {
@@ -444,7 +445,7 @@ export async function enforceIngressAcl(
444
445
  );
445
446
  }
446
447
  }
447
- if (isBootstrapCommand) {
448
+ if (!isBlockedMember && isBootstrapCommand) {
448
449
  const bootstrapPayload = (
449
450
  rawCommandIntentForAcl as Record<string, unknown>
450
451
  ).payload as string;
@@ -471,9 +472,11 @@ export async function enforceIngressAcl(
471
472
  }
472
473
 
473
474
  // ── Invite token intercept (inactive member) ──
474
- // Same as the non-member branch: invite tokens can reactivate
475
- // revoked/pending members without requiring guardian approval.
476
- if (inviteToken && denyInactiveMember) {
475
+ // Invite tokens can reactivate revoked/pending members without
476
+ // requiring guardian approval, but blocked members are excluded so
477
+ // they are short-circuited at the ACL layer rather than entering the
478
+ // redemption path.
479
+ if (!isBlockedMember && inviteToken && denyInactiveMember) {
477
480
  const inviteResult = await handleInviteTokenIntercept({
478
481
  rawToken: inviteToken,
479
482
  sourceChannel,
@@ -496,9 +499,15 @@ export async function enforceIngressAcl(
496
499
  }
497
500
 
498
501
  // ── 6-digit invite code intercept (inactive member) ──
499
- // Same as the non-member branch: codes can reactivate revoked/pending
500
- // members. Non-matching codes fall through to normal processing.
501
- if (denyInactiveMember && /^\d{6}$/.test(trimmedContent)) {
502
+ // Codes can reactivate revoked/pending members; non-matching codes
503
+ // fall through. Blocked members are excluded here for consistency —
504
+ // the redemption service would reject them anyway, but early exit
505
+ // avoids unnecessary work.
506
+ if (
507
+ !isBlockedMember &&
508
+ denyInactiveMember &&
509
+ /^\d{6}$/.test(trimmedContent)
510
+ ) {
502
511
  const codeInterceptResult = await handleInviteCodeIntercept({
503
512
  code: trimmedContent,
504
513
  sourceChannel,
@@ -579,7 +588,7 @@ export async function enforceIngressAcl(
579
588
  dmCallbackUrl,
580
589
  {
581
590
  chatId: senderUserId,
582
- text: "I've notified the owner. They'll share a verification code with you if they approve access. You can reply with the code here.",
591
+ text: "I've notified the owner that you'd like to chat with me. If they approve your request, they'll share a 6-digit verification code with you. You can reply with the code here.",
583
592
  assistantId,
584
593
  },
585
594
  mintBearerToken(),
@@ -31,12 +31,8 @@ import type {
31
31
  ApprovalCopyGenerator,
32
32
  MessageProcessor,
33
33
  } from "../../http-types.js";
34
- import { TelegramStreamingDelivery } from "../../telegram-streaming-delivery.js";
35
34
  import { resolveRoutingState } from "../../trust-context-resolver.js";
36
- import {
37
- deliverAttachmentsOnly,
38
- deliverReplyViaCallback,
39
- } from "../channel-delivery-routes.js";
35
+ import { deliverReplyViaCallback } from "../channel-delivery-routes.js";
40
36
  import { deliverGeneratedApprovalPrompt } from "../guardian-approval-prompt.js";
41
37
 
42
38
  const log = getLogger("runtime-http");
@@ -112,12 +108,6 @@ export function processChannelMessageInBackground(
112
108
  } = params;
113
109
 
114
110
  (async () => {
115
- const boundGuardianActor = isBoundGuardianActor({
116
- trustClass: trustCtx.trustClass,
117
- guardianExternalUserId: trustCtx.guardianExternalUserId,
118
- requesterExternalUserId: trustCtx.requesterExternalUserId,
119
- });
120
-
121
111
  const typingCallbackUrl = shouldEmitTelegramTyping(
122
112
  sourceChannel,
123
113
  replyCallbackUrl,
@@ -181,16 +171,6 @@ export function processChannelMessageInBackground(
181
171
  }
182
172
  }
183
173
 
184
- const telegramStreaming =
185
- sourceChannel === "telegram" && replyCallbackUrl
186
- ? new TelegramStreamingDelivery({
187
- callbackUrl: replyCallbackUrl,
188
- chatId: externalChatId,
189
- mintBearerToken,
190
- assistantId,
191
- })
192
- : undefined;
193
-
194
174
  try {
195
175
  const cmdIntent =
196
176
  commandIntent && typeof commandIntent.type === "string"
@@ -218,9 +198,6 @@ export function processChannelMessageInBackground(
218
198
  trustContext: trustCtx,
219
199
  isInteractive: resolveRoutingState(trustCtx).promptWaitingAllowed,
220
200
  ...(cmdIntent ? { commandIntent: cmdIntent } : {}),
221
- ...(telegramStreaming
222
- ? { onEvent: (msg) => telegramStreaming.onEvent(msg) }
223
- : {}),
224
201
  },
225
202
  sourceChannel,
226
203
  sourceInterface,
@@ -228,94 +205,18 @@ export function processChannelMessageInBackground(
228
205
  deliveryCrud.linkMessage(eventId, userMessageId);
229
206
  deliveryStatus.markProcessed(eventId);
230
207
 
231
- if (telegramStreaming) {
232
- // Retrieve approval metadata from pending interactions (if any)
233
- // so approval buttons can be attached to the final streamed message.
234
- // Approval prompts are guardian-only and must never be attached for
235
- // non-guardian or unverified actors.
236
- const prompt = boundGuardianActor
237
- ? getChannelApprovalPrompt(conversationId)
238
- : undefined;
239
- const pending = boundGuardianActor
240
- ? getApprovalInfoByConversation(conversationId)
241
- : [];
242
- const approvalMeta =
243
- prompt && pending.length > 0
244
- ? buildApprovalUIMetadata(prompt, pending[0])
245
- : undefined;
246
- try {
247
- await telegramStreaming.finish(approvalMeta);
248
- deliveryChannels.updateDeliveredSegmentCount(eventId, 1);
249
- } catch (err) {
250
- log.error(
251
- { err, conversationId },
252
- "Telegram streaming finalization failed",
253
- );
254
- // Fallback: deliver approval as a standalone message so buttons
255
- // are not permanently lost when finish() fails.
256
- if (approvalMeta && replyCallbackUrl) {
257
- try {
258
- await deliverChannelReply(
259
- replyCallbackUrl,
260
- {
261
- chatId: externalChatId,
262
- text: approvalMeta.plainTextFallback ?? "Action needed:",
263
- approval: approvalMeta,
264
- assistantId,
265
- },
266
- mintBearerToken(),
267
- );
268
- } catch (fallbackErr) {
269
- log.error(
270
- { err: fallbackErr, conversationId },
271
- "Fallback approval delivery also failed",
272
- );
273
- }
274
- }
275
- }
276
- }
277
-
278
208
  if (replyCallbackUrl) {
279
- // Streaming fully succeeded — only send attachments since text
280
- // was already delivered via streaming edits.
281
- const streamingFullyDelivered =
282
- telegramStreaming?.hasDeliveredText &&
283
- telegramStreaming.finishSucceeded;
284
-
285
- if (streamingFullyDelivered) {
286
- await deliverAttachmentsOnly(
287
- conversationId,
288
- externalChatId,
289
- replyCallbackUrl,
290
- mintBearerToken(),
291
- assistantId,
292
- );
293
- } else {
294
- // Non-streaming path, or streaming partially failed (some text
295
- // was delivered but finish/finalization threw). In the partial
296
- // failure case the user has a truncated message, so we deliver
297
- // the full response to ensure nothing is lost.
298
- if (
299
- telegramStreaming?.hasDeliveredText &&
300
- !telegramStreaming.finishSucceeded
301
- ) {
302
- log.warn(
303
- { conversationId },
304
- "Telegram streaming partially failed — falling back to full text delivery",
305
- );
306
- }
307
- await deliverReplyViaCallback(
308
- conversationId,
309
- externalChatId,
310
- replyCallbackUrl,
311
- mintBearerToken(),
312
- assistantId,
313
- {
314
- onSegmentDelivered: (count) =>
315
- deliveryChannels.updateDeliveredSegmentCount(eventId, count),
316
- },
317
- );
318
- }
209
+ await deliverReplyViaCallback(
210
+ conversationId,
211
+ externalChatId,
212
+ replyCallbackUrl,
213
+ mintBearerToken(),
214
+ assistantId,
215
+ {
216
+ onSegmentDelivered: (count) =>
217
+ deliveryChannels.updateDeliveredSegmentCount(eventId, count),
218
+ },
219
+ );
319
220
  }
320
221
  } catch (err) {
321
222
  log.error(
@@ -12,7 +12,7 @@ import {
12
12
  userInfo,
13
13
  } from "../../../../messaging/providers/slack/client.js";
14
14
  import type { SlackConversation } from "../../../../messaging/providers/slack/types.js";
15
- import { credentialKey } from "../../../../security/credential-key.js";
15
+ import { getConnectionByProvider } from "../../../../oauth/oauth-store.js";
16
16
  import { getSecureKey } from "../../../../security/secure-keys.js";
17
17
  import { getLogger } from "../../../../util/logger.js";
18
18
  import { httpError } from "../../../http-errors.js";
@@ -25,14 +25,13 @@ const log = getLogger("slack-share");
25
25
  // ---------------------------------------------------------------------------
26
26
 
27
27
  /**
28
- * Resolve the Slack bot token from secure storage.
29
- * Prefers the OAuth integration token, falls back to the legacy channel token.
28
+ * Resolve the Slack bot token from the OAuth connection store.
30
29
  */
31
30
  function resolveSlackToken(): string | undefined {
32
- return (
33
- getSecureKey(credentialKey("integration:slack", "access_token")) ??
34
- getSecureKey(credentialKey("slack_channel", "bot_token"))
35
- );
31
+ const conn = getConnectionByProvider("integration:slack");
32
+ return conn
33
+ ? getSecureKey(`oauth_connection/${conn.id}/access_token`)
34
+ : undefined;
36
35
  }
37
36
 
38
37
  // ---------------------------------------------------------------------------