@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
@@ -84,7 +84,10 @@ export class PermissionPrompter {
84
84
  "Permission prompt timed out, defaulting to deny",
85
85
  );
86
86
  this.onStateChanged?.(requestId, "timed_out", "timeout", toolUseId);
87
- resolve({ decision: "deny" });
87
+ resolve({
88
+ decision: "deny",
89
+ decisionContext: `The permission prompt for the "${toolName}" tool timed out. The user did not explicitly deny this request — they may have been away or busy. You may retry this tool call if it is still needed for the current task.`,
90
+ });
88
91
  }, timeoutMs);
89
92
 
90
93
  this.pending.set(requestId, { resolve, reject, timer, toolUseId });
@@ -121,6 +124,7 @@ export class PermissionPrompter {
121
124
  executionTarget,
122
125
  persistentDecisionsAllowed: persistentDecisionsAllowed ?? true,
123
126
  temporaryOptionsAvailable,
127
+ toolUseId,
124
128
  });
125
129
 
126
130
  this.onStateChanged?.(requestId, "pending", "system", toolUseId);
@@ -131,6 +135,11 @@ export class PermissionPrompter {
131
135
  return this.pending.has(requestId);
132
136
  }
133
137
 
138
+ /** Returns all currently pending request IDs. */
139
+ getPendingRequestIds(): string[] {
140
+ return [...this.pending.keys()];
141
+ }
142
+
134
143
  /** Returns the toolUseId associated with a pending request, if any. */
135
144
  getToolUseId(requestId: string): string | undefined {
136
145
  return this.pending.get(requestId)?.toolUseId;
@@ -76,6 +76,19 @@ function getCompiledPattern(pattern: string): Minimatch | null {
76
76
  return compiled;
77
77
  }
78
78
 
79
+ /**
80
+ * Check whether a minimatch pattern matches a candidate string.
81
+ * Reuses the compiled pattern cache from trust rule evaluation.
82
+ */
83
+ export function patternMatchesCandidate(
84
+ pattern: string,
85
+ candidate: string,
86
+ ): boolean {
87
+ const compiled = getCompiledPattern(pattern);
88
+ if (!compiled) return false;
89
+ return compiled.match(candidate);
90
+ }
91
+
79
92
  /** Rebuild the compiled pattern cache from the current rule set. */
80
93
  function rebuildPatternCache(rules: TrustRule[]): void {
81
94
  compiledPatterns.clear();
@@ -270,12 +283,28 @@ function loadFromDisk(): TrustRule[] {
270
283
  // Restore persisted starter bundle flag
271
284
  cachedStarterBundleAccepted = data.starterBundleAccepted === true;
272
285
 
286
+ // Defense-in-depth: strip any __internal: prefixed rules that may have
287
+ // been hand-edited into trust.json.
288
+ const sanitizedRules = rawRules.filter((r) => {
289
+ if (typeof r.tool === "string" && r.tool.startsWith("__internal:")) {
290
+ log.warn(
291
+ { ruleId: r.id, tool: r.tool },
292
+ "Stripping __internal: rule from trust file on load",
293
+ );
294
+ return false;
295
+ }
296
+ return true;
297
+ });
298
+
273
299
  if (
274
300
  data.version === TRUST_FILE_VERSION ||
275
301
  data.version === 1 ||
276
302
  data.version === 2
277
303
  ) {
278
- rules = rawRules;
304
+ rules = sanitizedRules;
305
+ if (sanitizedRules.length < rawRules.length) {
306
+ needsSave = true;
307
+ }
279
308
  if (data.version !== TRUST_FILE_VERSION) {
280
309
  needsSave = true;
281
310
  log.info(
@@ -382,6 +411,8 @@ export function addRule(
382
411
  executionTarget?: string;
383
412
  },
384
413
  ): TrustRule {
414
+ if (tool.startsWith("__internal:"))
415
+ throw new Error(`Cannot create internal pseudo-rule via addRule: ${tool}`);
385
416
  // Re-read from disk to avoid lost updates if another call modified rules
386
417
  // between our last read and now (e.g. two rapid trust rule additions).
387
418
  cachedRules = null;
@@ -424,6 +455,10 @@ export function updateRule(
424
455
  const defaultIds = new Set(getDefaultRuleTemplates().map((t) => t.id));
425
456
  if (defaultIds.has(id))
426
457
  throw new Error(`Cannot modify default trust rule: ${id}`);
458
+ if (updates.tool?.startsWith("__internal:"))
459
+ throw new Error(
460
+ `Cannot update tool to internal pseudo-rule: ${updates.tool}`,
461
+ );
427
462
 
428
463
  // Re-read from disk to avoid lost updates from concurrent modifications.
429
464
  cachedRules = null;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Compile all active playbook memory items into a triage context block
3
3
  * that can be injected into the system prompt alongside the contact
4
- * graph and dynamic profile.
4
+ * graph.
5
5
  */
6
6
 
7
7
  import { and, desc, eq, isNull } from "drizzle-orm";
@@ -38,7 +38,9 @@ describe("buildCliReferenceSection", () => {
38
38
  "prefer real `assistant` CLI workflows over any legacy account-record abstraction",
39
39
  );
40
40
  expect(result).toContain("assistant credentials");
41
- expect(result).toContain("assistant oauth token <service>");
41
+ expect(result).toContain(
42
+ "assistant oauth connections token <provider-key>",
43
+ );
42
44
  expect(result).toContain("assistant mcp auth <name>");
43
45
  expect(result).toContain("assistant platform status");
44
46
  });
@@ -4,10 +4,10 @@ import { join } from "node:path";
4
4
  import { CLI_HELP_REFERENCE } from "../cli/reference.js";
5
5
  import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
6
6
  import { getBaseDataDir, getIsContainerized } from "../config/env-registry.js";
7
- import { getConfig, getNestedValue, loadRawConfig } from "../config/loader.js";
7
+ import { getConfig } from "../config/loader.js";
8
8
  import { skillFlagKey } from "../config/skill-state.js";
9
9
  import { loadSkillCatalog, type SkillSummary } from "../config/skills.js";
10
- import { listCredentialMetadata } from "../tools/credentials/metadata-store.js";
10
+ import { listConnections } from "../oauth/oauth-store.js";
11
11
  import { resolveBundledDir } from "../util/bundled-asset.js";
12
12
  import { getLogger } from "../util/logger.js";
13
13
  import {
@@ -283,7 +283,7 @@ function buildAttachmentSection(): string {
283
283
  "",
284
284
  'Example: `<vellum-attachment source="sandbox" path="scratch/chart.png" />`',
285
285
  "",
286
- "Limits: up to 5 attachments per turn, 20 MB each. Tool outputs that produce image or file content blocks are also automatically converted into attachments.",
286
+ "Limits: 20 MB per attachment. Tool outputs that produce image or file content blocks are also automatically converted into attachments.",
287
287
  "",
288
288
  "### Inline Images and GIFs",
289
289
  "Embed images/GIFs inline using markdown: `![description](URL)`. Do NOT wrap in code fences.",
@@ -368,13 +368,11 @@ export function buildVoiceSetupRoutingSection(): string {
368
368
  return [
369
369
  "## Routing: Voice Setup & Troubleshooting",
370
370
  "",
371
- "Voice features include push-to-talk (PTT), wake word detection, and text-to-speech.",
371
+ "Voice features include push-to-talk (PTT) and text-to-speech.",
372
372
  "",
373
373
  "### Quick changes — use `voice_config_update` directly",
374
374
  '- "Change my PTT key to ctrl" — call `voice_config_update` with `setting: "activation_key"`',
375
- '- "Enable wake word" — call `voice_config_update` with `setting: "wake_word_enabled"`, `value: true`',
376
- '- "Set my wake word to jarvis" — call `voice_config_update` with `setting: "wake_word_keyword"`',
377
- '- "Set wake word timeout to 30 seconds" — call `voice_config_update` with `setting: "wake_word_timeout"`',
375
+ '- "Set conversation timeout to 30 seconds" — call `voice_config_update` with `setting: "conversation_timeout"`',
378
376
  "",
379
377
  "For simple setting changes, use the tool directly without loading the voice-setup skill.",
380
378
  "",
@@ -384,15 +382,14 @@ export function buildVoiceSetupRoutingSection(): string {
384
382
  "**Trigger phrases:**",
385
383
  '- "Help me set up voice"',
386
384
  '- "Set up push-to-talk"',
387
- '- "Configure voice / PTT / wake word"',
385
+ '- "Configure voice / PTT"',
388
386
  '- "PTT isn\'t working" / "push-to-talk not working"',
389
387
  '- "Recording but no text"',
390
- '- "Wake word not detecting"',
391
388
  '- "Microphone not working"',
392
389
  '- "Set up ElevenLabs" / "configure TTS"',
393
390
  "",
394
391
  "### Disambiguation",
395
- "- Voice setup (this skill) = **local PTT, wake word, microphone permissions** on the Mac desktop app.",
392
+ "- Voice setup (this skill) = **local PTT, microphone permissions** on the Mac desktop app.",
396
393
  "- Phone calls skill = **Twilio-powered voice calls** over the phone network. Completely separate.",
397
394
  '- If the user says "voice" in the context of phone calls or Twilio, load `phone-calls` instead.',
398
395
  ].join("\n");
@@ -423,7 +420,7 @@ export function buildPhoneCallsRoutingSection(): string {
423
420
  "",
424
421
  "### Exclusivity rules",
425
422
  "- Do NOT improvise Twilio setup instructions from general knowledge — always load the skill first.",
426
- "- Do NOT confuse with voice-setup (local PTT/wake word/microphone) or guardian-verify-setup (channel verification).",
423
+ "- Do NOT confuse with voice-setup (local PTT/microphone) or guardian-verify-setup (channel verification).",
427
424
  '- If the user says "voice" in the context of phone calls or Twilio, load phone-calls, not voice-setup.',
428
425
  "- For guardian voice verification specifically, load guardian-verify-setup instead.",
429
426
  ].join("\n");
@@ -630,42 +627,38 @@ function buildAccessPreferenceSection(hasNoClient: boolean): string {
630
627
  "",
631
628
  "### Foreground Computer Use — Last Resort",
632
629
  "",
633
- "Foreground computer use (`computer_use_request_control`) takes over the user's cursor and",
634
- "keyboard. It is disruptive and should be your LAST resort. Prefer this hierarchy:",
630
+ "Computer use tools (clicking, typing, scrolling) take over the user's cursor and keyboard.",
631
+ "They are disruptive and should be your LAST resort. Prefer this hierarchy:",
635
632
  "",
636
633
  "1. **CLI tools / osascript** — Use `host_bash` with shell commands or `osascript` with",
637
634
  " AppleScript to accomplish tasks in the background without interrupting the user.",
638
635
  "2. **Background computer use** — If you must interact with a GUI app, prefer AppleScript",
639
636
  ' automation (e.g. `tell application "Safari" to set URL of current tab to ...`).',
640
- "3. **Foreground computer use** — Only escalate via `computer_use_request_control` when",
641
- " the task genuinely cannot be done any other way (e.g. complex multi-step GUI interactions",
642
- " with no scripting support) or the user explicitly asks you to take control.",
637
+ "3. **Foreground computer use** — Only use computer use tools when the task genuinely",
638
+ " cannot be done any other way (e.g. complex multi-step GUI interactions with no scripting",
639
+ " support) or the user explicitly asks you to take control.",
643
640
  ]
644
641
  : []),
645
642
  ].join("\n");
646
643
  }
647
644
 
648
645
  function buildIntegrationSection(): string {
649
- const allCreds = listCredentialMetadata();
650
- // Show OAuth2-connected services (those with oauth2TokenUrl in metadata)
651
- const oauthCreds = allCreds.filter(
652
- (c) => c.oauth2TokenUrl && c.field === "access_token",
653
- );
654
- if (oauthCreds.length === 0) return "";
646
+ let connections: { providerKey: string; accountInfo?: string | null }[];
647
+ try {
648
+ connections = listConnections().filter((c) => c.status === "active");
649
+ } catch {
650
+ // DB not available — no connected services to show
651
+ return "";
652
+ }
653
+
654
+ if (connections.length === 0) return "";
655
655
 
656
- const raw = loadRawConfig();
657
656
  const lines = ["## Connected Services", ""];
658
- for (const cred of oauthCreds) {
659
- const acctInfo = (getNestedValue(
660
- raw,
661
- `integrations.${cred.service}.accountInfo`,
662
- ) ??
663
- // Fallback: legacy config path used before the namespace migration
664
- getNestedValue(raw, `integrations.accountInfo.${cred.service}`)) as
665
- | string
666
- | undefined;
667
- const state = acctInfo ? `Connected (${acctInfo})` : "Connected";
668
- lines.push(`- **${cred.service}**: ${state}`);
657
+ for (const conn of connections) {
658
+ const state = conn.accountInfo
659
+ ? `Connected (${conn.accountInfo})`
660
+ : "Connected";
661
+ lines.push(`- **${conn.providerKey}**: ${state}`);
669
662
  }
670
663
 
671
664
  return lines.join("\n");
@@ -697,7 +690,7 @@ function buildMemoryRecallSection(): string {
697
690
  "- The auto-injected memory context doesn't contain what you need",
698
691
  "- The user references something from a previous session",
699
692
  "",
700
- "The tool searches across semantic, lexical, entity graph, and recency sources. Be specific in your query for best results.",
693
+ "The tool uses hybrid search (dense and sparse vectors) supplemented by recency. Be specific in your query for best results.",
701
694
  ].join("\n");
702
695
  }
703
696
 
@@ -852,10 +845,10 @@ export function buildCliReferenceSection(): string {
852
845
  return [
853
846
  "## Assistant CLI",
854
847
  "",
855
- "The `assistant` CLI is installed on the user's machine and available via `bash`.",
848
+ "The `assistant` CLI is available in the sandbox. Always use the `bash` tool (never `host_bash`) when running `assistant` commands.",
856
849
  "For account and authentication work, prefer real `assistant` CLI workflows over any legacy account-record abstraction.",
857
850
  "- Use `assistant credentials ...` for stored secrets and credential metadata.",
858
- "- Use `assistant oauth token <service>` for connected integration tokens.",
851
+ "- Use `assistant oauth connections token <provider-key>` for connected integration tokens.",
859
852
  "- Use `assistant mcp auth <name>` when an MCP server needs OAuth login.",
860
853
  "- Use `assistant platform status` for platform-linked deployment and auth context.",
861
854
  "- If a bundled skill documents a service-specific `assistant <service>` auth or session flow, follow that CLI exactly.",
@@ -967,6 +960,22 @@ function buildDynamicSkillWorkflowSection(
967
960
  );
968
961
  }
969
962
 
963
+ lines.push(
964
+ "",
965
+ "### Community Skills Discovery",
966
+ "",
967
+ "When no built-in skill satisfies a request, search the community skills.sh registry:",
968
+ "1. Run `assistant skills search <query>` to find community skills. Results include install counts and security audit badges (ATH, Socket, Snyk).",
969
+ "2. Present the search results to the user, highlighting the security audit status. ATH is Gen Agent Trust Hub. Audits show PASS (safe/low risk), WARN (medium risk), or FAIL (high/critical risk) for each provider.",
970
+ "3. Check the skill's **source owner** to determine the trust level:",
971
+ " - **Vellum-owned** (source starts with `vellum-ai/`): These are first-party skills published by the Vellum team. Install them directly without prompting — they are vetted and trusted.",
972
+ " - **Third-party** (any other owner): Ask the user for permission before installing. Say something like: \"I found a community skill that could help with this, but it's published by a third party — we haven't vetted it. Want to install it anyway?\" Share the skill name, source, audit results, and install count.",
973
+ "4. Install with `assistant skills add <owner>/<repo>@<skill-name>` (e.g., `assistant skills add vercel-labs/skills@find-skills`).",
974
+ "5. After installation, load the skill with `skill_load` as usual.",
975
+ "",
976
+ "**Never install third-party community skills without explicit user confirmation.** Vellum-owned skills (`vellum-ai/*`) can be installed automatically.",
977
+ );
978
+
970
979
  return lines.join("\n");
971
980
  }
972
981
 
@@ -1029,10 +1038,5 @@ function formatSkillsCatalog(skills: SkillSummary[]): string {
1029
1038
  "",
1030
1039
  lines.join("\n"),
1031
1040
  "",
1032
- "### Installing additional skills",
1033
- "If `skill_load` fails because a skill is not found, additional first-party skills may be available in the Vellum catalog.",
1034
- "Use `bash` to discover and install them:",
1035
- "- `assistant skills list` — list all available catalog skills",
1036
- "- `assistant skills install <skill-id>` — install a skill, then retry `skill_load`",
1037
1041
  ].join("\n");
1038
1042
  }
@@ -84,8 +84,12 @@ function summarizeMessages(messages: Anthropic.MessageParam[]): string[] {
84
84
  const blockDescs = content.map((b) => {
85
85
  const bt = (b as { type: string }).type;
86
86
  if (bt === "tool_use") return `tool_use(${(b as { id: string }).id})`;
87
+ if (bt === "server_tool_use")
88
+ return `server_tool_use(${(b as { id: string }).id})`;
87
89
  if (bt === "tool_result")
88
90
  return `tool_result(${(b as { tool_use_id: string }).tool_use_id})`;
91
+ if (bt === "web_search_tool_result")
92
+ return `web_search_tool_result(${(b as { tool_use_id: string }).tool_use_id})`;
89
93
  return bt;
90
94
  });
91
95
  return `[${idx}] ${m.role}: ${blockDescs.join(", ") || "(empty)"}`;
@@ -103,16 +107,24 @@ function buildSyntheticToolResult(
103
107
  };
104
108
  }
105
109
 
110
+
111
+ /**
112
+ * Collect ordered IDs of client-side tool_use blocks only.
113
+ * Server-side tools (server_tool_use / web_search_tool_result) are self-paired
114
+ * within the assistant message and do not need cross-message pairing.
115
+ */
106
116
  function getOrderedToolUseIds(
107
117
  content: Anthropic.ContentBlockParam[],
108
118
  ): string[] {
109
119
  const ids: string[] = [];
110
120
  const seen = new Set<string>();
111
121
  for (const block of content) {
112
- if (!isToolUseBlock(block)) continue;
113
- if (seen.has(block.id)) continue;
114
- seen.add(block.id);
115
- ids.push(block.id);
122
+ if (isToolUseBlock(block)) {
123
+ if (!seen.has(block.id)) {
124
+ seen.add(block.id);
125
+ ids.push(block.id);
126
+ }
127
+ }
116
128
  }
117
129
  return ids;
118
130
  }
@@ -124,19 +136,29 @@ function hasOrderedToolResultPrefix(
124
136
  if (content.length < orderedToolUseIds.length) return false;
125
137
  for (let idx = 0; idx < orderedToolUseIds.length; idx++) {
126
138
  const block = content[idx];
139
+ const expectedId = orderedToolUseIds[idx];
127
140
  if (!isToolResultBlock(block)) return false;
128
- if (block.tool_use_id !== orderedToolUseIds[idx]) return false;
141
+ if (block.tool_use_id !== expectedId) return false;
129
142
  }
130
143
  return true;
131
144
  }
132
145
 
146
+ /**
147
+ * Split an assistant message into:
148
+ * - pairedContent: everything up to and including client-side tool_use blocks
149
+ * - carryoverContent: trailing non-tool blocks after the last tool_use
150
+ *
151
+ * Server-side tools (server_tool_use / web_search_tool_result) are treated as
152
+ * regular content — they are self-paired within the assistant message and must
153
+ * not be separated by the cross-message pairing logic.
154
+ */
133
155
  function splitAssistantForToolPairing(content: Anthropic.ContentBlockParam[]): {
134
156
  pairedContent: Anthropic.ContentBlockParam[];
135
157
  carryoverContent: Anthropic.ContentBlockParam[];
136
158
  toolUseIds: string[];
137
159
  } {
138
160
  const leading: Anthropic.ContentBlockParam[] = [];
139
- const toolUseBlocks: Anthropic.ToolUseBlockParam[] = [];
161
+ const toolUseBlocks: Anthropic.ContentBlockParam[] = [];
140
162
  const carryover: Anthropic.ContentBlockParam[] = [];
141
163
  let seenToolUse = false;
142
164
 
@@ -182,7 +204,7 @@ function normalizeFollowingUserContent(
182
204
  hadOrderedPrefix: boolean;
183
205
  } {
184
206
  const pendingIds = new Set(orderedToolUseIds);
185
- const matchedById = new Map<string, Anthropic.ToolResultBlockParam>();
207
+ const matchedById = new Map<string, Anthropic.ContentBlockParam>();
186
208
  const remaining: Anthropic.ContentBlockParam[] = [];
187
209
 
188
210
  for (const block of nextContent) {
@@ -206,10 +228,7 @@ function normalizeFollowingUserContent(
206
228
  toolResultPrefix: orderedResults,
207
229
  remainingContent: remaining,
208
230
  missingIds,
209
- hadOrderedPrefix: hasOrderedToolResultPrefix(
210
- nextContent,
211
- orderedToolUseIds,
212
- ),
231
+ hadOrderedPrefix: hasOrderedToolResultPrefix(nextContent, orderedToolUseIds),
213
232
  };
214
233
  }
215
234
 
@@ -348,21 +367,23 @@ function ensureToolPairing(
348
367
  }
349
368
  }
350
369
 
351
- // Self-validation: verify no tool_use/tool_result mismatches remain
370
+ // Self-validation: verify no client-side tool_use/tool_result mismatches remain.
371
+ // Server-side tools (server_tool_use / web_search_tool_result) are self-paired
372
+ // within assistant messages and are not validated here.
352
373
  for (let j = 0; j < result.length; j++) {
353
374
  const m = result[j];
354
375
  if (m.role !== "assistant") continue;
355
376
  const c = Array.isArray(m.content) ? m.content : [];
356
- const ids = getOrderedToolUseIds(c);
357
- if (ids.length === 0) continue;
377
+ const validationIds = getOrderedToolUseIds(c);
378
+ if (validationIds.length === 0) continue;
358
379
 
359
380
  const nxt = result[j + 1];
360
381
  const nxtContent =
361
382
  nxt && nxt.role === "user" && Array.isArray(nxt.content)
362
383
  ? nxt.content
363
384
  : [];
364
- if (!hasOrderedToolResultPrefix(nxtContent, ids)) {
365
- const unmatchedIds = ids.filter((id, idx) => {
385
+ if (!hasOrderedToolResultPrefix(nxtContent, validationIds)) {
386
+ const unmatchedIds = validationIds.filter((id, idx) => {
366
387
  const block = nxtContent[idx];
367
388
  return !(isToolResultBlock(block) && block.tool_use_id === id);
368
389
  });
@@ -659,10 +680,14 @@ export class AnthropicProvider implements Provider {
659
680
  onEvent?.({ type: "text_delta", text: " " });
660
681
  }
661
682
  hasSeenTextBlock = true;
662
- } else if (event.type === "content_block_start") {
663
- // Reset on non-text blocks so that text separated by tool_use
664
- // (text -> tool_use -> text) doesn't get a spurious leading space
665
- // in the second text segment.
683
+ } else if (
684
+ event.type === "content_block_start" &&
685
+ event.content_block.type === "tool_use"
686
+ ) {
687
+ // Reset only for client-side tool_use blocks, which create visual
688
+ // separators in the UI. Server-side tool blocks (server_tool_use,
689
+ // web_search_tool_result) are transparent in the text stream and
690
+ // need the space preserved between surrounding text blocks.
666
691
  hasSeenTextBlock = false;
667
692
  }
668
693
  if (
@@ -687,6 +712,20 @@ export class AnthropicProvider implements Provider {
687
712
  type: "server_tool_start",
688
713
  name: event.content_block.name,
689
714
  toolUseId: event.content_block.id,
715
+ input: (
716
+ event.content_block as { input?: Record<string, unknown> }
717
+ ).input ?? {},
718
+ });
719
+ }
720
+ if (
721
+ event.type === "content_block_start" &&
722
+ event.content_block.type === "web_search_tool_result"
723
+ ) {
724
+ onEvent?.({
725
+ type: "server_tool_complete",
726
+ toolUseId: (
727
+ event.content_block as { tool_use_id: string }
728
+ ).tool_use_id,
690
729
  });
691
730
  }
692
731
  if (event.type === "content_block_stop") {
@@ -1,5 +1,5 @@
1
1
  import { ProviderError } from "../util/errors.js";
2
- import { getLogger, isDebug } from "../util/logger.js";
2
+ import { getLogger } from "../util/logger.js";
3
3
  import {
4
4
  computeRetryDelay,
5
5
  DEFAULT_BASE_DELAY_MS,
@@ -96,43 +96,17 @@ export class RetryProvider implements Provider {
96
96
  options?: SendMessageOptions,
97
97
  ): Promise<ProviderResponse> {
98
98
  let lastError: unknown;
99
- const debug = isDebug();
100
-
101
- if (debug) {
102
- log.debug(
103
- {
104
- provider: this.name,
105
- messageCount: messages.length,
106
- toolCount: tools?.length ?? 0,
107
- },
108
- "Provider sendMessage start",
109
- );
110
- }
111
99
 
112
100
  const normalizedOptions = normalizeSendMessageOptions(this.name, options);
113
101
 
114
102
  for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
115
103
  try {
116
- const start = Date.now();
117
104
  const result = await this.inner.sendMessage(
118
105
  messages,
119
106
  tools,
120
107
  systemPrompt,
121
108
  normalizedOptions,
122
109
  );
123
- if (debug) {
124
- log.debug(
125
- {
126
- provider: this.name,
127
- durationMs: Date.now() - start,
128
- attempt: attempt + 1,
129
- model: result.model,
130
- inputTokens: result.usage.inputTokens,
131
- outputTokens: result.usage.outputTokens,
132
- },
133
- "Provider sendMessage success",
134
- );
135
- }
136
110
  return result;
137
111
  } catch (error) {
138
112
  lastError = error;
@@ -117,7 +117,13 @@ export type ProviderEvent =
117
117
  toolUseId: string;
118
118
  accumulatedJson: string;
119
119
  }
120
- | { type: "server_tool_start"; name: string; toolUseId: string };
120
+ | {
121
+ type: "server_tool_start";
122
+ name: string;
123
+ toolUseId: string;
124
+ input: Record<string, unknown>;
125
+ }
126
+ | { type: "server_tool_complete"; toolUseId: string };
121
127
 
122
128
  export interface SendMessageConfig {
123
129
  model?: string;
@@ -43,6 +43,15 @@ Host file allows the assistant to perform file operations (read, write, edit) on
43
43
  - `POST /v1/host-file-result` — `{ requestId, content, isError }`
44
44
  - **Tracking**: Uses the same `pending-interactions` tracker as approvals and host bash, with `kind: "host_file"`. The endpoint validates the interaction kind before resolving.
45
45
 
46
+ ### Host CU (desktop proxy computer-use execution)
47
+
48
+ Host CU allows the assistant to proxy computer-use actions (screenshots, mouse/keyboard input) to the desktop host via the client, following the same pattern as host bash and host file.
49
+
50
+ - **Discovery**: Clients discover pending host CU requests via SSE events (`host_cu_request`) which include a `requestId`.
51
+ - **Resolution**: Clients execute the CU action on the host and respond via:
52
+ - `POST /v1/host-cu-result` — `{ requestId, axTree?, axDiff?, screenshot?, screenshotWidthPx?, screenshotHeightPx?, screenWidthPt?, screenHeightPt?, executionResult?, executionError?, secondaryWindows?, userGuidance? }`
53
+ - **Tracking**: Uses the same `pending-interactions` tracker as the other host proxy types, with `kind: "host_cu"`. Registration happens in `conversation-routes.ts` and the route handler is in `host-cu-routes.ts`.
54
+
46
55
  ### Channel approvals (Telegram, Slack)
47
56
 
48
57
  Channel approval flows use `requestId` (not `runId`) as the primary identifier:
@@ -347,6 +347,12 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
347
347
  { endpoint: "skills:DELETE", scopes: ["settings.write"] },
348
348
  { endpoint: "skills:PATCH", scopes: ["settings.write"] },
349
349
 
350
+ // Memory items
351
+ { endpoint: "memory-items:GET", scopes: ["settings.read"] },
352
+ { endpoint: "memory-items:POST", scopes: ["settings.write"] },
353
+ { endpoint: "memory-items:PATCH", scopes: ["settings.write"] },
354
+ { endpoint: "memory-items:DELETE", scopes: ["settings.write"] },
355
+
350
356
  // Trust rule CRUD management
351
357
  { endpoint: "trust-rules/manage:GET", scopes: ["settings.read"] },
352
358
  { endpoint: "trust-rules/manage:POST", scopes: ["settings.write"] },
@@ -358,9 +364,6 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
358
364
  { endpoint: "computer-use/sessions/abort", scopes: ["chat.write"] },
359
365
  { endpoint: "computer-use/observations", scopes: ["chat.write"] },
360
366
  { endpoint: "computer-use/tasks", scopes: ["chat.write"] },
361
- { endpoint: "computer-use/ride-shotgun/start", scopes: ["chat.write"] },
362
- { endpoint: "computer-use/ride-shotgun/stop", scopes: ["chat.write"] },
363
- { endpoint: "computer-use/ride-shotgun/status", scopes: ["chat.write"] },
364
367
  { endpoint: "computer-use/watch", scopes: ["chat.write"] },
365
368
 
366
369
  // Recordings
@@ -381,9 +384,6 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
381
384
  // Delivery ack
382
385
  { endpoint: "channels/delivery-ack", scopes: ["internal.write"] },
383
386
 
384
- // MCP
385
- { endpoint: "mcp/reload", scopes: ["settings.write"] },
386
-
387
387
  // Migrations
388
388
  { endpoint: "migrations/validate", scopes: ["settings.write"] },
389
389
  { endpoint: "migrations/export", scopes: ["settings.write"] },
@@ -208,43 +208,3 @@ export async function deliverReplyViaCallback(
208
208
  break;
209
209
  }
210
210
  }
211
-
212
- /**
213
- * Deliver only the attachments from the last assistant message, skipping text.
214
- * Used when streaming already delivered the text content and only file
215
- * attachments remain to be sent via the normal delivery path.
216
- */
217
- export async function deliverAttachmentsOnly(
218
- conversationId: string,
219
- externalChatId: string,
220
- callbackUrl: string,
221
- bearerToken?: string,
222
- assistantId?: string,
223
- ): Promise<void> {
224
- const msgs = getMessages(conversationId);
225
- for (let i = msgs.length - 1; i >= 0; i--) {
226
- if (msgs[i].role !== "assistant") continue;
227
-
228
- const linked = attachmentsStore.getAttachmentMetadataForMessage(msgs[i].id);
229
- if (linked.length === 0) return;
230
-
231
- const replyAttachments: RuntimeAttachmentMetadata[] = linked.map((a) => ({
232
- id: a.id,
233
- filename: a.originalFilename,
234
- mimeType: a.mimeType,
235
- sizeBytes: a.sizeBytes,
236
- kind: a.kind,
237
- }));
238
-
239
- await deliverChannelReply(
240
- callbackUrl,
241
- {
242
- chatId: externalChatId,
243
- attachments: replyAttachments,
244
- assistantId,
245
- },
246
- bearerToken,
247
- );
248
- break;
249
- }
250
- }
@@ -31,8 +31,6 @@ export interface ChannelReplyPayload {
31
31
  ephemeral?: boolean;
32
32
  /** Slack user ID — required when `ephemeral` is true. */
33
33
  user?: string;
34
- /** Telegram message_id for editing an existing message instead of sending a new one. */
35
- messageId?: number;
36
34
  /** When provided, instructs the delivery endpoint to update an existing message instead of posting a new one. */
37
35
  messageTs?: string;
38
36
  /** When true, auto-generate Block Kit blocks from text via textToBlocks(). */
@@ -45,8 +43,6 @@ export interface ChannelDeliveryResult {
45
43
  ok: boolean;
46
44
  /** The message timestamp returned by the delivery endpoint (e.g. Slack message ts). */
47
45
  ts?: string;
48
- /** The Telegram message_id returned when a new message was sent. */
49
- messageId?: number;
50
46
  }
51
47
 
52
48
  interface ManagedOutboundCallbackContext {
@@ -100,9 +96,6 @@ export async function deliverChannelReply(
100
96
  if (typeof responseBody.ts === "string") {
101
97
  result.ts = responseBody.ts;
102
98
  }
103
- if (typeof responseBody.messageId === "number") {
104
- result.messageId = responseBody.messageId;
105
- }
106
99
  } catch {
107
100
  // Response may not be JSON for non-Slack channels; that's fine.
108
101
  }