@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
@@ -1,167 +0,0 @@
1
- /**
2
- * Conflict-gate logic extracted from Session.
3
- *
4
- * Handles pending memory conflicts internally: dismisses non-user-evidenced
5
- * and non-actionable conflicts, and attempts resolution when the user's reply
6
- * looks like an explicit clarification with topical relevance. Never produces
7
- * user-facing clarification text.
8
- */
9
-
10
- import { resolveConflictClarification } from "../memory/clarification-resolver.js";
11
- import {
12
- areStatementsCoherent,
13
- computeConflictRelevance,
14
- looksLikeClarificationReply,
15
- shouldAttemptConflictResolution,
16
- } from "../memory/conflict-intent.js";
17
- import {
18
- isConflictKindPairEligible,
19
- isConflictUserEvidenced,
20
- isStatementConflictEligible,
21
- } from "../memory/conflict-policy.js";
22
- import type { PendingConflictDetail } from "../memory/conflict-store.js";
23
- import {
24
- applyConflictResolution,
25
- listPendingConflictDetails,
26
- resolveConflict,
27
- } from "../memory/conflict-store.js";
28
-
29
- export class ConflictGate {
30
- async evaluate(
31
- userMessage: string,
32
- conflictConfig: {
33
- enabled: boolean;
34
- gateMode: string;
35
- relevanceThreshold: number;
36
- resolverLlmTimeoutMs: number;
37
- conflictableKinds: readonly string[];
38
- },
39
- scopeId = "default",
40
- ): Promise<void> {
41
- if (!conflictConfig.enabled || conflictConfig.gateMode !== "soft") return;
42
-
43
- const pendingBeforeResolve = listPendingConflictDetails(scopeId, 50);
44
-
45
- // Dismiss non-actionable conflicts (kind/statement policy, incoherent pair,
46
- // or assistant-inferred-only provenance with no user evidence)
47
- const dismissedIds = new Set<string>();
48
- for (const conflict of pendingBeforeResolve) {
49
- const dismissReason = this.getDismissReason(
50
- conflict,
51
- conflictConfig.conflictableKinds,
52
- );
53
- if (dismissReason) {
54
- resolveConflict(conflict.id, {
55
- status: "dismissed",
56
- resolutionNote: dismissReason,
57
- });
58
- dismissedIds.add(conflict.id);
59
- }
60
- }
61
-
62
- const actionablePending = pendingBeforeResolve.filter(
63
- (c) => !dismissedIds.has(c.id),
64
- );
65
-
66
- // Attempt resolution only for explicit clarification-like replies with
67
- // topical relevance to the conflict statements
68
- const clarificationReply = looksLikeClarificationReply(userMessage);
69
- const candidatesBeforeResolve = actionablePending.filter((conflict) => {
70
- const relevance = computeConflictRelevance(userMessage, conflict);
71
- return shouldAttemptConflictResolution({
72
- clarificationReply,
73
- relevance,
74
- });
75
- });
76
- await this.resolvePendingConflicts(
77
- userMessage,
78
- conflictConfig.resolverLlmTimeoutMs,
79
- candidatesBeforeResolve,
80
- );
81
- }
82
-
83
- private async resolvePendingConflicts(
84
- userMessage: string,
85
- resolverTimeoutMs: number,
86
- pendingConflicts: PendingConflictDetail[],
87
- ): Promise<void> {
88
- for (const conflict of pendingConflicts) {
89
- const resolution = await resolveConflictClarification(
90
- {
91
- existingStatement: conflict.existingStatement,
92
- candidateStatement: conflict.candidateStatement,
93
- userMessage,
94
- },
95
- { timeoutMs: resolverTimeoutMs },
96
- );
97
- if (resolution.resolution === "still_unclear") continue;
98
-
99
- applyConflictResolution({
100
- conflictId: conflict.id,
101
- resolution: resolution.resolution,
102
- mergedStatement:
103
- resolution.resolution === "merge"
104
- ? resolution.resolvedStatement
105
- : null,
106
- resolutionNote: resolution.explanation,
107
- });
108
- }
109
- }
110
-
111
- /**
112
- * Returns a dismissal reason if the conflict should be dismissed, or null if actionable.
113
- */
114
- private getDismissReason(
115
- conflict: PendingConflictDetail,
116
- conflictableKinds: readonly string[],
117
- ): string | null {
118
- if (
119
- !isConflictKindPairEligible(
120
- conflict.existingKind,
121
- conflict.candidateKind,
122
- { conflictableKinds },
123
- )
124
- ) {
125
- return "Dismissed by conflict policy (kind not eligible).";
126
- }
127
- if (
128
- !isStatementConflictEligible(
129
- conflict.existingKind,
130
- conflict.existingStatement,
131
- { conflictableKinds },
132
- )
133
- ) {
134
- return "Dismissed by conflict policy (transient/non-durable).";
135
- }
136
- if (
137
- !isStatementConflictEligible(
138
- conflict.candidateKind,
139
- conflict.candidateStatement,
140
- { conflictableKinds },
141
- )
142
- ) {
143
- return "Dismissed by conflict policy (transient/non-durable).";
144
- }
145
- // Dismiss incoherent conflicts where the two statements have zero topical overlap
146
- if (
147
- !areStatementsCoherent(
148
- conflict.existingStatement,
149
- conflict.candidateStatement,
150
- )
151
- ) {
152
- return "Dismissed by conflict policy (incoherent — zero statement overlap).";
153
- }
154
- // Dismiss conflicts where neither side has user-evidenced provenance
155
- if (
156
- !isConflictUserEvidenced(
157
- conflict.existingVerificationState,
158
- conflict.candidateVerificationState,
159
- )
160
- ) {
161
- return "Dismissed by conflict policy (no user-evidenced provenance).";
162
- }
163
- return null;
164
- }
165
- }
166
-
167
- export { computeConflictRelevance, looksLikeClarificationReply };
@@ -1,77 +0,0 @@
1
- /**
2
- * Dynamic-profile injection and stripping helpers extracted from Session.
3
- *
4
- * These are pure functions with no state — they wrap profile text into user
5
- * messages at runtime and strip it back out before persistence.
6
- */
7
-
8
- import type { Message } from "../providers/types.js";
9
-
10
- export function injectDynamicProfileIntoUserMessage(
11
- message: Message,
12
- profileText: string,
13
- ): Message {
14
- const trimmedProfile = profileText.trim();
15
- if (trimmedProfile.length === 0) return message;
16
- const block = [
17
- "<dynamic-profile-context>",
18
- trimmedProfile,
19
- "</dynamic-profile-context>",
20
- ].join("\n");
21
- return {
22
- ...message,
23
- content: [...message.content, { type: "text", text: `\n\n${block}` }],
24
- };
25
- }
26
-
27
- export function stripDynamicProfileMessages(
28
- messages: Message[],
29
- profileText: string,
30
- ): Message[] {
31
- const trimmedProfile = profileText.trim();
32
- if (trimmedProfile.length === 0) return messages;
33
- const injectedBlock = `\n\n<dynamic-profile-context>\n${trimmedProfile}\n</dynamic-profile-context>`;
34
- // Find the last user message that actually contains the injected profile block.
35
- // We can't just target the last user message by role — tool_result messages also
36
- // have role 'user', so after tool use the last user message won't be the one
37
- // we injected the profile into.
38
- let lastUserIdx = -1;
39
- for (let i = messages.length - 1; i >= 0; i--) {
40
- if (
41
- messages[i].role === "user" &&
42
- messages[i].content.some(
43
- (b) => b.type === "text" && b.text.includes(injectedBlock),
44
- )
45
- ) {
46
- lastUserIdx = i;
47
- break;
48
- }
49
- }
50
- if (lastUserIdx === -1) return messages;
51
- const message = messages[lastUserIdx];
52
- let changed = false;
53
- const nextContent = message.content
54
- .map((block) => {
55
- if (block.type !== "text") return block;
56
- const nextText = block.text.split(injectedBlock).join("");
57
- if (nextText === block.text) return block;
58
- changed = true;
59
- const stripped = nextText.replace(/\n{3,}/g, "\n\n").trimEnd();
60
- return stripped.length > 0 ? { ...block, text: stripped } : null;
61
- })
62
- .filter((block): block is NonNullable<typeof block> => block != null);
63
- if (!changed) return messages;
64
- // If stripping removed all content blocks, drop the message entirely
65
- // to avoid sending an empty content array to the provider.
66
- if (nextContent.length === 0) {
67
- return [
68
- ...messages.slice(0, lastUserIdx),
69
- ...messages.slice(lastUserIdx + 1),
70
- ];
71
- }
72
- return [
73
- ...messages.slice(0, lastUserIdx),
74
- { ...message, content: nextContent },
75
- ...messages.slice(lastUserIdx + 1),
76
- ];
77
- }
@@ -1,417 +0,0 @@
1
- import {
2
- createTimeout,
3
- extractToolUse,
4
- getConfiguredProvider,
5
- userMessage,
6
- } from "../providers/provider-send-message.js";
7
- import type { ModelIntent } from "../providers/types.js";
8
- import { truncate } from "../util/truncate.js";
9
-
10
- const DEFAULT_RESOLVER_MODEL_INTENT: ModelIntent = "latency-optimized";
11
- const DEFAULT_RESOLVER_TIMEOUT_MS = 12_000;
12
-
13
- const DIRECTIONAL_EXISTING_CUES = [
14
- "existing",
15
- "old",
16
- "previous",
17
- "first",
18
- "earlier",
19
- "original",
20
- ];
21
- const DIRECTIONAL_CANDIDATE_CUES = [
22
- "candidate",
23
- "new",
24
- "latest",
25
- "second",
26
- "updated",
27
- "instead",
28
- "replace",
29
- ];
30
- const MERGE_CUES = [
31
- "both",
32
- "merge",
33
- "combine",
34
- "together",
35
- "depends",
36
- "either",
37
- "mix",
38
- ];
39
-
40
- const STOP_WORDS = new Set([
41
- "about",
42
- "after",
43
- "again",
44
- "also",
45
- "because",
46
- "been",
47
- "before",
48
- "being",
49
- "between",
50
- "could",
51
- "doing",
52
- "from",
53
- "have",
54
- "into",
55
- "just",
56
- "more",
57
- "most",
58
- "only",
59
- "over",
60
- "same",
61
- "should",
62
- "some",
63
- "than",
64
- "that",
65
- "their",
66
- "there",
67
- "these",
68
- "they",
69
- "this",
70
- "those",
71
- "were",
72
- "what",
73
- "when",
74
- "where",
75
- "which",
76
- "while",
77
- "with",
78
- "would",
79
- "your",
80
- ]);
81
-
82
- export type ClarificationResolution =
83
- | "keep_existing"
84
- | "keep_candidate"
85
- | "merge"
86
- | "still_unclear";
87
-
88
- export type ClarificationStrategy =
89
- | "heuristic"
90
- | "llm"
91
- | "llm_timeout"
92
- | "llm_error"
93
- | "no_llm_key";
94
-
95
- export interface ClarificationResolverInput {
96
- existingStatement: string;
97
- candidateStatement: string;
98
- userMessage: string;
99
- }
100
-
101
- export interface ClarificationResolverOptions {
102
- apiKey?: string;
103
- model?: string;
104
- modelIntent?: ModelIntent;
105
- timeoutMs?: number;
106
- }
107
-
108
- export interface ClarificationResolverResult {
109
- resolution: ClarificationResolution;
110
- strategy: ClarificationStrategy;
111
- resolvedStatement: string | null;
112
- explanation: string;
113
- }
114
-
115
- export async function resolveConflictClarification(
116
- input: ClarificationResolverInput,
117
- options?: ClarificationResolverOptions,
118
- ): Promise<ClarificationResolverResult> {
119
- const heuristicResult = resolveWithHeuristics(input);
120
- if (heuristicResult) return heuristicResult;
121
-
122
- const provider = getConfiguredProvider();
123
- if (!provider) {
124
- return {
125
- resolution: "still_unclear",
126
- strategy: "no_llm_key",
127
- resolvedStatement: null,
128
- explanation:
129
- "Configured provider unavailable for clarification fallback.",
130
- };
131
- }
132
-
133
- try {
134
- return await resolveWithLlm(input, {
135
- model: options?.model,
136
- modelIntent: options?.modelIntent ?? DEFAULT_RESOLVER_MODEL_INTENT,
137
- timeoutMs: options?.timeoutMs ?? DEFAULT_RESOLVER_TIMEOUT_MS,
138
- });
139
- } catch (err) {
140
- const message = err instanceof Error ? err.message : String(err);
141
- if (message === "clarification_resolver_timeout") {
142
- return {
143
- resolution: "still_unclear",
144
- strategy: "llm_timeout",
145
- resolvedStatement: null,
146
- explanation: "Clarification resolver timed out.",
147
- };
148
- }
149
- return {
150
- resolution: "still_unclear",
151
- strategy: "llm_error",
152
- resolvedStatement: null,
153
- explanation: `Clarification resolver failed: ${truncate(
154
- message,
155
- 300,
156
- "",
157
- )}`,
158
- };
159
- }
160
- }
161
-
162
- function resolveWithHeuristics(
163
- input: ClarificationResolverInput,
164
- ): ClarificationResolverResult | null {
165
- const normalizedMessage = normalize(input.userMessage);
166
- if (!normalizedMessage) return null;
167
-
168
- const lowerMessage = normalizedMessage.toLowerCase();
169
-
170
- const hasExistingCue = containsAnyCue(
171
- lowerMessage,
172
- DIRECTIONAL_EXISTING_CUES,
173
- );
174
- const hasCandidateCue = containsAnyCue(
175
- lowerMessage,
176
- DIRECTIONAL_CANDIDATE_CUES,
177
- );
178
- const hasMergeCue = containsAnyCue(lowerMessage, MERGE_CUES);
179
-
180
- // When multiple cue categories match, delegate to LLM to avoid misclassification
181
- const matchCount = [hasExistingCue, hasCandidateCue, hasMergeCue].filter(
182
- Boolean,
183
- ).length;
184
- if (matchCount > 1) return null;
185
-
186
- if (hasMergeCue) {
187
- return {
188
- resolution: "merge",
189
- strategy: "heuristic",
190
- resolvedStatement: buildMergedStatement(input),
191
- explanation: "User response includes merge cues.",
192
- };
193
- }
194
-
195
- if (hasExistingCue) {
196
- return {
197
- resolution: "keep_existing",
198
- strategy: "heuristic",
199
- resolvedStatement: null,
200
- explanation: "User response explicitly points to existing/old statement.",
201
- };
202
- }
203
-
204
- if (hasCandidateCue) {
205
- return {
206
- resolution: "keep_candidate",
207
- strategy: "heuristic",
208
- resolvedStatement: null,
209
- explanation:
210
- "User response explicitly points to candidate/new statement.",
211
- };
212
- }
213
-
214
- const messageTokens = tokenize(normalizedMessage);
215
- const existingOverlap = overlapScore(
216
- messageTokens,
217
- tokenize(input.existingStatement),
218
- );
219
- const candidateOverlap = overlapScore(
220
- messageTokens,
221
- tokenize(input.candidateStatement),
222
- );
223
-
224
- if (existingOverlap >= 2 && existingOverlap >= candidateOverlap + 1) {
225
- return {
226
- resolution: "keep_existing",
227
- strategy: "heuristic",
228
- resolvedStatement: null,
229
- explanation:
230
- "User response overlaps more with existing statement details.",
231
- };
232
- }
233
-
234
- if (candidateOverlap >= 2 && candidateOverlap >= existingOverlap + 1) {
235
- return {
236
- resolution: "keep_candidate",
237
- strategy: "heuristic",
238
- resolvedStatement: null,
239
- explanation:
240
- "User response overlaps more with candidate statement details.",
241
- };
242
- }
243
-
244
- if (existingOverlap > 0 && candidateOverlap > 0) {
245
- return {
246
- resolution: "merge",
247
- strategy: "heuristic",
248
- resolvedStatement: buildMergedStatement(input),
249
- explanation: "User response overlaps with both statements.",
250
- };
251
- }
252
-
253
- return null;
254
- }
255
-
256
- async function resolveWithLlm(
257
- input: ClarificationResolverInput,
258
- options: { model?: string; modelIntent: ModelIntent; timeoutMs: number },
259
- ): Promise<ClarificationResolverResult> {
260
- const provider = getConfiguredProvider()!;
261
- const userPrompt = [
262
- "You are resolving a memory clarification response.",
263
- "",
264
- `Existing statement: ${input.existingStatement}`,
265
- `Candidate statement: ${input.candidateStatement}`,
266
- `User clarification: ${input.userMessage}`,
267
- ].join("\n");
268
-
269
- const { signal, cleanup } = createTimeout(options.timeoutMs);
270
-
271
- try {
272
- const response = await provider.sendMessage(
273
- [userMessage(userPrompt)],
274
- [
275
- {
276
- name: "resolve_conflict_clarification",
277
- description:
278
- "Resolve a pending memory contradiction using user clarification.",
279
- input_schema: {
280
- type: "object" as const,
281
- properties: {
282
- resolution: {
283
- type: "string",
284
- enum: [
285
- "keep_existing",
286
- "keep_candidate",
287
- "merge",
288
- "still_unclear",
289
- ],
290
- },
291
- resolved_statement: {
292
- type: "string",
293
- description: "Required only when resolution is merge.",
294
- },
295
- explanation: {
296
- type: "string",
297
- description: "One short rationale for the classification.",
298
- },
299
- },
300
- required: ["resolution", "explanation"],
301
- },
302
- },
303
- ],
304
- [
305
- "Classify the user clarification for conflicting memory statements.",
306
- "Return exactly one resolution:",
307
- "- keep_existing",
308
- "- keep_candidate",
309
- "- merge",
310
- "- still_unclear",
311
- ].join("\n"),
312
- {
313
- config: {
314
- ...(options.model
315
- ? { model: options.model }
316
- : { modelIntent: options.modelIntent }),
317
- max_tokens: 256,
318
- tool_choice: {
319
- type: "tool" as const,
320
- name: "resolve_conflict_clarification",
321
- },
322
- },
323
- signal,
324
- },
325
- );
326
- cleanup();
327
-
328
- const toolBlock = extractToolUse(response);
329
- if (!toolBlock) {
330
- throw new Error("No tool_use block in clarification resolver response.");
331
- }
332
-
333
- const parsed = toolBlock.input as {
334
- resolution?: string;
335
- resolved_statement?: string;
336
- explanation?: string;
337
- };
338
-
339
- if (!isResolution(parsed.resolution)) {
340
- throw new Error(
341
- `Invalid clarification resolution: ${String(parsed.resolution)}`,
342
- );
343
- }
344
-
345
- const resolvedStatement =
346
- parsed.resolution === "merge"
347
- ? normalize(parsed.resolved_statement ?? buildMergedStatement(input)) ||
348
- buildMergedStatement(input)
349
- : null;
350
-
351
- return {
352
- resolution: parsed.resolution,
353
- strategy: "llm",
354
- resolvedStatement,
355
- explanation: truncate(
356
- normalize(parsed.explanation ?? "Resolved via LLM fallback."),
357
- 500,
358
- "",
359
- ),
360
- };
361
- } catch (err) {
362
- cleanup();
363
- if (signal.aborted) {
364
- throw new Error("clarification_resolver_timeout");
365
- }
366
- throw err;
367
- }
368
- }
369
-
370
- function isResolution(
371
- value: string | undefined,
372
- ): value is ClarificationResolution {
373
- return (
374
- value === "keep_existing" ||
375
- value === "keep_candidate" ||
376
- value === "merge" ||
377
- value === "still_unclear"
378
- );
379
- }
380
-
381
- function containsAnyCue(input: string, cues: readonly string[]): boolean {
382
- return cues.some((cue) => new RegExp(`\\b${cue}\\b`).test(input));
383
- }
384
-
385
- function overlapScore(left: Set<string>, right: Set<string>): number {
386
- let score = 0;
387
- for (const token of left) {
388
- if (right.has(token)) score += 1;
389
- }
390
- return score;
391
- }
392
-
393
- function tokenize(input: string): Set<string> {
394
- const words = input
395
- .toLowerCase()
396
- .split(/[^a-z0-9]+/g)
397
- .map((word) => word.trim())
398
- .filter((word) => word.length >= 4 && !STOP_WORDS.has(word));
399
- return new Set(words);
400
- }
401
-
402
- function normalize(input: string): string {
403
- return input.replace(/\s+/g, " ").trim();
404
- }
405
-
406
- function buildMergedStatement(input: ClarificationResolverInput): string {
407
- const normalizedUserMessage = normalize(input.userMessage);
408
- if (
409
- normalizedUserMessage.length >= 8 &&
410
- normalizedUserMessage.length <= 320
411
- ) {
412
- return normalizedUserMessage;
413
- }
414
- const existing = truncate(normalize(input.existingStatement), 140, "");
415
- const candidate = truncate(normalize(input.candidateStatement), 140, "");
416
- return truncate(`Merged clarification: ${existing}; ${candidate}`, 320, "");
417
- }