@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,165 +0,0 @@
1
- import { getLogger } from "../util/logger.js";
2
- import { rawGet, rawRun } from "./db.js";
3
-
4
- const log = getLogger("fts-reconciler");
5
-
6
- export interface FtsReconciliationResult {
7
- table: string;
8
- baseCount: number;
9
- ftsCount: number;
10
- missingInserted: number;
11
- orphansRemoved: number;
12
- staleRefreshed: number;
13
- }
14
-
15
- /**
16
- * Reconcile a single FTS index against its base table. Detects missing entries
17
- * (rows in the base table with no corresponding FTS row) and orphaned entries
18
- * (FTS rows whose base table row no longer exists), then repairs both.
19
- *
20
- * This is lighter than a full rebuild — it only touches the delta rather than
21
- * wiping and re-inserting the entire index.
22
- */
23
- function reconcileTable(opts: {
24
- ftsTable: string;
25
- ftsIdColumn: string;
26
- ftsContentColumn: string;
27
- baseTable: string;
28
- baseIdColumn: string;
29
- baseContentColumn: string;
30
- }): FtsReconciliationResult {
31
- const {
32
- ftsTable,
33
- ftsIdColumn,
34
- ftsContentColumn,
35
- baseTable,
36
- baseIdColumn,
37
- baseContentColumn,
38
- } = opts;
39
-
40
- const baseCount = (
41
- rawGet<{ c: number }>(`SELECT COUNT(*) AS c FROM ${baseTable}`) ?? { c: 0 }
42
- ).c;
43
- const ftsCount = (
44
- rawGet<{ c: number }>(`SELECT COUNT(*) AS c FROM ${ftsTable}`) ?? { c: 0 }
45
- ).c;
46
-
47
- // Find base table rows missing from the FTS index
48
- const missingInserted = rawRun(/*sql*/ `
49
- INSERT INTO ${ftsTable}(${ftsIdColumn}, ${ftsContentColumn})
50
- SELECT b.${baseIdColumn}, b.${baseContentColumn}
51
- FROM ${baseTable} b
52
- LEFT JOIN ${ftsTable} f ON f.${ftsIdColumn} = b.${baseIdColumn}
53
- WHERE f.${ftsIdColumn} IS NULL
54
- `);
55
-
56
- // Find FTS rows whose base table row no longer exists
57
- const orphansRemoved = rawRun(/*sql*/ `
58
- DELETE FROM ${ftsTable}
59
- WHERE ${ftsIdColumn} IN (
60
- SELECT f.${ftsIdColumn}
61
- FROM ${ftsTable} f
62
- LEFT JOIN ${baseTable} b ON b.${baseIdColumn} = f.${ftsIdColumn}
63
- WHERE b.${baseIdColumn} IS NULL
64
- )
65
- `);
66
-
67
- // Refresh FTS rows whose content is stale (base row was updated but the
68
- // update trigger didn't fire or was missing). Delete-then-insert is the
69
- // standard FTS5 update pattern.
70
- const staleDeleted = rawRun(/*sql*/ `
71
- DELETE FROM ${ftsTable}
72
- WHERE ${ftsIdColumn} IN (
73
- SELECT f.${ftsIdColumn}
74
- FROM ${ftsTable} f
75
- JOIN ${baseTable} b ON b.${baseIdColumn} = f.${ftsIdColumn}
76
- WHERE b.${baseContentColumn} IS NOT f.${ftsContentColumn}
77
- )
78
- `);
79
- if (staleDeleted > 0) {
80
- rawRun(/*sql*/ `
81
- INSERT INTO ${ftsTable}(${ftsIdColumn}, ${ftsContentColumn})
82
- SELECT b.${baseIdColumn}, b.${baseContentColumn}
83
- FROM ${baseTable} b
84
- LEFT JOIN ${ftsTable} f ON f.${ftsIdColumn} = b.${baseIdColumn}
85
- WHERE f.${ftsIdColumn} IS NULL
86
- `);
87
- }
88
-
89
- return {
90
- table: ftsTable,
91
- baseCount,
92
- ftsCount,
93
- missingInserted,
94
- orphansRemoved,
95
- staleRefreshed: staleDeleted,
96
- };
97
- }
98
-
99
- /**
100
- * Reconcile all FTS indexes. Returns results for each table so callers can
101
- * inspect what was repaired.
102
- */
103
- export function reconcileFtsIndexes(): FtsReconciliationResult[] {
104
- const results: FtsReconciliationResult[] = [];
105
- const errors: unknown[] = [];
106
-
107
- // memory_segment_fts tracks memory_segments
108
- try {
109
- const memResult = reconcileTable({
110
- ftsTable: "memory_segment_fts",
111
- ftsIdColumn: "segment_id",
112
- ftsContentColumn: "text",
113
- baseTable: "memory_segments",
114
- baseIdColumn: "id",
115
- baseContentColumn: "text",
116
- });
117
- results.push(memResult);
118
- if (
119
- memResult.missingInserted > 0 ||
120
- memResult.orphansRemoved > 0 ||
121
- memResult.staleRefreshed > 0
122
- ) {
123
- log.info(memResult, "Reconciled memory_segment_fts");
124
- } else {
125
- log.debug(memResult, "memory_segment_fts is in sync");
126
- }
127
- } catch (err) {
128
- log.error({ err }, "Failed to reconcile memory_segment_fts");
129
- errors.push(err);
130
- }
131
-
132
- // messages_fts tracks messages
133
- try {
134
- const msgResult = reconcileTable({
135
- ftsTable: "messages_fts",
136
- ftsIdColumn: "message_id",
137
- ftsContentColumn: "content",
138
- baseTable: "messages",
139
- baseIdColumn: "id",
140
- baseContentColumn: "content",
141
- });
142
- results.push(msgResult);
143
- if (
144
- msgResult.missingInserted > 0 ||
145
- msgResult.orphansRemoved > 0 ||
146
- msgResult.staleRefreshed > 0
147
- ) {
148
- log.info(msgResult, "Reconciled messages_fts");
149
- } else {
150
- log.debug(msgResult, "messages_fts is in sync");
151
- }
152
- } catch (err) {
153
- log.error({ err }, "Failed to reconcile messages_fts");
154
- errors.push(err);
155
- }
156
-
157
- if (errors.length > 0) {
158
- throw new AggregateError(
159
- errors,
160
- `FTS reconciliation failed for ${errors.length} table(s)`,
161
- );
162
- }
163
-
164
- return results;
165
- }
@@ -1,200 +0,0 @@
1
- import { and, asc, eq, inArray, lt, ne } from "drizzle-orm";
2
-
3
- import type { AssistantConfig } from "../../config/types.js";
4
- import { getLogger } from "../../util/logger.js";
5
- import { resolveConflictClarification } from "../clarification-resolver.js";
6
- import {
7
- areStatementsCoherent,
8
- computeConflictRelevance,
9
- looksLikeClarificationReply,
10
- shouldAttemptConflictResolution,
11
- } from "../conflict-intent.js";
12
- import {
13
- isConflictKindPairEligible,
14
- isConflictUserEvidenced,
15
- isStatementConflictEligible,
16
- } from "../conflict-policy.js";
17
- import {
18
- applyConflictResolution,
19
- listPendingConflictDetails,
20
- resolveConflict,
21
- } from "../conflict-store.js";
22
- import { getDb } from "../db.js";
23
- import { asPositiveMs, asString } from "../job-utils.js";
24
- import { enqueueMemoryJob, type MemoryJob } from "../jobs-store.js";
25
- import { extractTextFromStoredMessageContent } from "../message-content.js";
26
- import { memoryItemConflicts, messages } from "../schema.js";
27
-
28
- const log = getLogger("memory-jobs-worker");
29
-
30
- const CLEANUP_BATCH_LIMIT = 250;
31
-
32
- export async function resolvePendingConflictsForMessageJob(
33
- job: MemoryJob,
34
- config: AssistantConfig,
35
- ): Promise<void> {
36
- if (!config.memory.conflicts.enabled) return;
37
- const messageId = asString(job.payload.messageId);
38
- if (!messageId) return;
39
- const scopeId = asString(job.payload.scopeId) ?? "default";
40
- const db = getDb();
41
- const message = db
42
- .select({
43
- id: messages.id,
44
- role: messages.role,
45
- content: messages.content,
46
- createdAt: messages.createdAt,
47
- })
48
- .from(messages)
49
- .where(eq(messages.id, messageId))
50
- .get();
51
- if (!message || message.role !== "user") return;
52
-
53
- const userMessage = extractTextFromStoredMessageContent(
54
- message.content,
55
- ).trim();
56
- if (userMessage.length === 0) return;
57
- const clarificationReply = looksLikeClarificationReply(userMessage);
58
- if (!clarificationReply) return;
59
-
60
- const pending = listPendingConflictDetails(scopeId, 25);
61
-
62
- // Dismiss non-actionable conflicts (kind or statement policy)
63
- const conflictableKinds = config.memory.conflicts.conflictableKinds;
64
- for (const conflict of pending) {
65
- const kindEligible = isConflictKindPairEligible(
66
- conflict.existingKind,
67
- conflict.candidateKind,
68
- { conflictableKinds },
69
- );
70
- if (
71
- !kindEligible ||
72
- !isStatementConflictEligible(
73
- conflict.existingKind,
74
- conflict.existingStatement,
75
- { conflictableKinds },
76
- ) ||
77
- !isStatementConflictEligible(
78
- conflict.candidateKind,
79
- conflict.candidateStatement,
80
- { conflictableKinds },
81
- )
82
- ) {
83
- resolveConflict(conflict.id, {
84
- status: "dismissed",
85
- resolutionNote: "Dismissed by conflict policy (transient/non-durable).",
86
- });
87
- } else if (
88
- !isConflictUserEvidenced(
89
- conflict.existingVerificationState,
90
- conflict.candidateVerificationState,
91
- )
92
- ) {
93
- resolveConflict(conflict.id, {
94
- status: "dismissed",
95
- resolutionNote:
96
- "Dismissed by conflict policy (no user-evidenced provenance).",
97
- });
98
- } else if (
99
- !areStatementsCoherent(
100
- conflict.existingStatement,
101
- conflict.candidateStatement,
102
- )
103
- ) {
104
- resolveConflict(conflict.id, {
105
- status: "dismissed",
106
- resolutionNote:
107
- "Dismissed by conflict policy (incoherent — zero statement overlap).",
108
- });
109
- }
110
- }
111
-
112
- // Re-fetch after dismissal
113
- const actionablePending = listPendingConflictDetails(scopeId, 25);
114
- const eligible = actionablePending.filter(
115
- (conflict) => conflict.createdAt <= message.createdAt,
116
- );
117
- if (eligible.length === 0) return;
118
- const candidates = eligible.filter((conflict) => {
119
- const relevance = computeConflictRelevance(userMessage, conflict);
120
- return shouldAttemptConflictResolution({
121
- clarificationReply,
122
- relevance,
123
- });
124
- });
125
- if (candidates.length === 0) return;
126
-
127
- let resolvedCount = 0;
128
- for (const conflict of candidates) {
129
- const resolution = await resolveConflictClarification(
130
- {
131
- existingStatement: conflict.existingStatement,
132
- candidateStatement: conflict.candidateStatement,
133
- userMessage,
134
- },
135
- { timeoutMs: config.memory.conflicts.resolverLlmTimeoutMs },
136
- );
137
- if (resolution.resolution === "still_unclear") continue;
138
- const resolved = applyConflictResolution({
139
- conflictId: conflict.id,
140
- resolution: resolution.resolution,
141
- mergedStatement:
142
- resolution.resolution === "merge" ? resolution.resolvedStatement : null,
143
- resolutionNote: `Background message resolver (${resolution.strategy}): ${resolution.explanation}`,
144
- });
145
- if (resolved) resolvedCount += 1;
146
- }
147
-
148
- log.debug(
149
- {
150
- messageId,
151
- scopeId,
152
- pendingConflicts: pending.length,
153
- eligibleConflicts: eligible.length,
154
- candidateConflicts: candidates.length,
155
- resolvedConflicts: resolvedCount,
156
- },
157
- "Processed pending conflict resolution job",
158
- );
159
- }
160
-
161
- export function cleanupResolvedConflictsJob(
162
- job: MemoryJob,
163
- config: AssistantConfig,
164
- ): void {
165
- const db = getDb();
166
- const retentionMs =
167
- asPositiveMs(job.payload.retentionMs) ??
168
- config.memory.cleanup.resolvedConflictRetentionMs;
169
- const cutoff = Date.now() - retentionMs;
170
- const stale = db
171
- .select({ id: memoryItemConflicts.id })
172
- .from(memoryItemConflicts)
173
- .where(
174
- and(
175
- ne(memoryItemConflicts.status, "pending_clarification"),
176
- lt(memoryItemConflicts.resolvedAt, cutoff),
177
- ),
178
- )
179
- .orderBy(asc(memoryItemConflicts.resolvedAt), asc(memoryItemConflicts.id))
180
- .limit(CLEANUP_BATCH_LIMIT)
181
- .all();
182
- if (stale.length === 0) return;
183
-
184
- const ids = stale.map((row) => row.id);
185
- db.delete(memoryItemConflicts)
186
- .where(inArray(memoryItemConflicts.id, ids))
187
- .run();
188
- if (stale.length === CLEANUP_BATCH_LIMIT) {
189
- enqueueMemoryJob("cleanup_resolved_conflicts", { retentionMs });
190
- }
191
-
192
- log.debug(
193
- {
194
- removedConflicts: stale.length,
195
- retentionMs,
196
- cutoff,
197
- },
198
- "Cleaned up resolved memory conflicts",
199
- );
200
- }
@@ -1,195 +0,0 @@
1
- import { and, desc, eq, inArray, isNull } from "drizzle-orm";
2
-
3
- import { getConfig } from "../config/loader.js";
4
- import { estimateTextTokens } from "../context/token-estimator.js";
5
- import { getDb } from "./db.js";
6
- import { memoryItems } from "./schema.js";
7
-
8
- const PROFILE_KIND_ALLOWLIST = [
9
- "profile",
10
- "preference",
11
- "constraint",
12
- "instruction",
13
- "style",
14
- ] as const;
15
-
16
- const TRUST_RANK: Record<string, number> = {
17
- user_confirmed: 3,
18
- user_reported: 2,
19
- assistant_inferred: 1,
20
- };
21
-
22
- export interface CompileProfileOptions {
23
- scopeId?: string;
24
- maxInjectTokensOverride?: number;
25
- /** When true and scopeId is not 'default', query both the given scope and 'default'. */
26
- includeDefaultFallback?: boolean;
27
- }
28
-
29
- export interface CompiledProfile {
30
- text: string;
31
- sourceCount: number;
32
- selectedCount: number;
33
- budgetTokens: number;
34
- tokenEstimate: number;
35
- }
36
-
37
- interface ProfileCandidate {
38
- kind: string;
39
- subject: string;
40
- statement: string;
41
- verificationState: string;
42
- confidence: number;
43
- importance: number | null;
44
- lastSeenAt: number;
45
- firstSeenAt: number;
46
- scopeId: string;
47
- }
48
-
49
- export function compileDynamicProfile(
50
- options?: CompileProfileOptions,
51
- ): CompiledProfile {
52
- const config = getConfig();
53
- const profileConfig = config.memory.profile;
54
- const scopeId = options?.scopeId ?? "default";
55
- const budgetTokens = Math.max(
56
- 0,
57
- Math.floor(
58
- options?.maxInjectTokensOverride ?? profileConfig.maxInjectTokens,
59
- ),
60
- );
61
- if (!profileConfig.enabled || budgetTokens <= 0) {
62
- return {
63
- text: "",
64
- sourceCount: 0,
65
- selectedCount: 0,
66
- budgetTokens,
67
- tokenEstimate: 0,
68
- };
69
- }
70
-
71
- const db = getDb();
72
- const shouldFallback =
73
- options?.includeDefaultFallback === true && scopeId !== "default";
74
- const scopeFilter = shouldFallback
75
- ? inArray(memoryItems.scopeId, [scopeId, "default"])
76
- : eq(memoryItems.scopeId, scopeId);
77
- const rows = db
78
- .select({
79
- kind: memoryItems.kind,
80
- subject: memoryItems.subject,
81
- statement: memoryItems.statement,
82
- verificationState: memoryItems.verificationState,
83
- confidence: memoryItems.confidence,
84
- importance: memoryItems.importance,
85
- lastSeenAt: memoryItems.lastSeenAt,
86
- firstSeenAt: memoryItems.firstSeenAt,
87
- scopeId: memoryItems.scopeId,
88
- })
89
- .from(memoryItems)
90
- .where(
91
- and(
92
- scopeFilter,
93
- eq(memoryItems.status, "active"),
94
- isNull(memoryItems.invalidAt),
95
- inArray(memoryItems.kind, [...PROFILE_KIND_ALLOWLIST]),
96
- ),
97
- )
98
- .orderBy(desc(memoryItems.lastSeenAt))
99
- .all();
100
-
101
- const nowMs = Date.now();
102
- const trusted = rows
103
- .filter((row) => TRUST_RANK[row.verificationState] !== undefined)
104
- .sort((a, b) =>
105
- compareProfileCandidates(
106
- a,
107
- b,
108
- nowMs,
109
- shouldFallback ? scopeId : undefined,
110
- ),
111
- );
112
-
113
- const selectedLines: string[] = [];
114
- const seenKeys = new Set<string>();
115
- for (const candidate of trusted) {
116
- const subject = normalizeWhitespace(candidate.subject, 80);
117
- const statement = normalizeWhitespace(candidate.statement, 220);
118
- if (!subject || !statement) continue;
119
- const dedupeKey = `${candidate.kind}|${subject.toLowerCase()}`;
120
- if (seenKeys.has(dedupeKey)) continue;
121
-
122
- const line = `- ${subject}: ${statement}`;
123
- const tentative = renderProfileText([...selectedLines, line]);
124
- if (estimateTextTokens(tentative) > budgetTokens) continue;
125
- seenKeys.add(dedupeKey);
126
- selectedLines.push(line);
127
- }
128
-
129
- const text = renderProfileText(selectedLines);
130
- const tokenEstimate = text ? estimateTextTokens(text) : 0;
131
- return {
132
- text,
133
- sourceCount: trusted.length,
134
- selectedCount: selectedLines.length,
135
- budgetTokens,
136
- tokenEstimate,
137
- };
138
- }
139
-
140
- /** Half-life for recency decay — items seen this many ms ago score ~0.5. (30 days) */
141
- const RECENCY_HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1000;
142
-
143
- function recencyScore(lastSeenAt: number, nowMs: number): number {
144
- const ageMs = Math.max(0, nowMs - lastSeenAt);
145
- return Math.pow(0.5, ageMs / RECENCY_HALF_LIFE_MS);
146
- }
147
-
148
- function candidateRankScore(c: ProfileCandidate, nowMs: number): number {
149
- const importance = c.importance ?? 0.5;
150
- const recency = recencyScore(c.lastSeenAt, nowMs);
151
- // Use weighted additive formula: importance dominates, recency is a minor boost
152
- // This preserves high-importance items even when they haven't been seen recently
153
- return importance * 0.7 + recency * 0.3;
154
- }
155
-
156
- function compareProfileCandidates(
157
- left: ProfileCandidate,
158
- right: ProfileCandidate,
159
- nowMs: number,
160
- /** When set, entries matching this scope sort before 'default' entries. */
161
- preferredScopeId?: string,
162
- ): number {
163
- // Scoped entries beat default fallback entries so local overrides win deduplication
164
- if (preferredScopeId !== undefined) {
165
- const leftIsPreferred = left.scopeId === preferredScopeId;
166
- const rightIsPreferred = right.scopeId === preferredScopeId;
167
- if (leftIsPreferred && !rightIsPreferred) return -1;
168
- if (!leftIsPreferred && rightIsPreferred) return 1;
169
- }
170
-
171
- const trustDelta =
172
- (TRUST_RANK[right.verificationState] ?? 0) -
173
- (TRUST_RANK[left.verificationState] ?? 0);
174
- if (trustDelta !== 0) return trustDelta;
175
-
176
- const scoreDelta =
177
- candidateRankScore(right, nowMs) - candidateRankScore(left, nowMs);
178
- if (scoreDelta !== 0) return scoreDelta;
179
-
180
- const confidenceDelta = right.confidence - left.confidence;
181
- if (confidenceDelta !== 0) return confidenceDelta;
182
-
183
- return right.firstSeenAt - left.firstSeenAt;
184
- }
185
-
186
- function normalizeWhitespace(input: string, maxLength: number): string {
187
- return input.replace(/\s+/g, " ").trim().slice(0, maxLength);
188
- }
189
-
190
- function renderProfileText(lines: string[]): string {
191
- if (lines.length === 0) return "";
192
- return ["<dynamic-user-profile>", ...lines, "</dynamic-user-profile>"].join(
193
- "\n",
194
- );
195
- }
@@ -1,117 +0,0 @@
1
- import { createHash } from "crypto";
2
-
3
- import type {
4
- MemoryRecallOptions,
5
- MemoryRecallResult,
6
- } from "./search/types.js";
7
-
8
- /**
9
- * In-memory cache for memory recall results.
10
- *
11
- * The full retrieval pipeline (FTS5 + Qdrant + entity graph + RRF merge) is
12
- * expensive. When the same query is issued multiple turns in a row (common
13
- * when the conversation context hasn't changed), we can serve the cached
14
- * result instantly.
15
- *
16
- * Invalidation: a monotonic version counter is bumped whenever new memory
17
- * is indexed (segments, items, embeddings). Cache entries are only valid
18
- * when their version matches the current global version.
19
- */
20
-
21
- interface CacheEntry {
22
- version: number;
23
- createdAt: number;
24
- result: MemoryRecallResult;
25
- }
26
-
27
- const MAX_ENTRIES = 32;
28
- const TTL_MS = 60_000; // 60 seconds
29
-
30
- let _version = 0;
31
- const _cache = new Map<string, CacheEntry>();
32
-
33
- /** Bump the global memory version, invalidating all cached recall results. */
34
- export function bumpMemoryVersion(): void {
35
- _version++;
36
- }
37
-
38
- /** Return the current memory version (for snapshot-based staleness checks). */
39
- export function getMemoryVersion(): number {
40
- return _version;
41
- }
42
-
43
- /** Build a deterministic cache key from the recall inputs. */
44
- function buildCacheKey(
45
- query: string,
46
- conversationId: string,
47
- options?: MemoryRecallOptions,
48
- configFingerprint?: string,
49
- ): string {
50
- const parts = [
51
- query,
52
- conversationId,
53
- options?.scopeId ?? "",
54
- options?.scopePolicyOverride
55
- ? `${options.scopePolicyOverride.scopeId}:${options.scopePolicyOverride.fallbackToDefault}`
56
- : "",
57
- options?.excludeMessageIds
58
- ? [...options.excludeMessageIds].sort().join(",")
59
- : "",
60
- options?.maxInjectTokensOverride != null
61
- ? String(options.maxInjectTokensOverride)
62
- : "",
63
- configFingerprint ?? "",
64
- ];
65
- return createHash("sha256").update(parts.join("\0")).digest("hex");
66
- }
67
-
68
- /** Look up a cached recall result. Returns undefined on miss or stale entry. */
69
- export function getCachedRecall(
70
- query: string,
71
- conversationId: string,
72
- options?: MemoryRecallOptions,
73
- configFingerprint?: string,
74
- ): MemoryRecallResult | undefined {
75
- const key = buildCacheKey(query, conversationId, options, configFingerprint);
76
- const entry = _cache.get(key);
77
- if (!entry) return undefined;
78
- if (entry.version !== _version || Date.now() - entry.createdAt > TTL_MS) {
79
- _cache.delete(key);
80
- return undefined;
81
- }
82
- // Move to end of Map iteration order so it's treated as most-recently-used
83
- _cache.delete(key);
84
- _cache.set(key, entry);
85
- return entry.result;
86
- }
87
-
88
- /**
89
- * Store a recall result in the cache. Evicts least-recently-used entries when full.
90
- *
91
- * When `snapshotVersion` is provided, the entry is only stored if the
92
- * snapshot still matches the current global version — this prevents a
93
- * stale result from being cached under a version that was bumped while
94
- * the retrieval pipeline was in flight.
95
- */
96
- export function setCachedRecall(
97
- query: string,
98
- conversationId: string,
99
- options: MemoryRecallOptions | undefined,
100
- result: MemoryRecallResult,
101
- snapshotVersion?: number,
102
- configFingerprint?: string,
103
- ): void {
104
- // If a snapshot version was provided, only cache when it still matches
105
- // the current version — otherwise the result may be stale.
106
- if (snapshotVersion !== undefined && snapshotVersion !== _version) return;
107
-
108
- const key = buildCacheKey(query, conversationId, options, configFingerprint);
109
-
110
- // Evict oldest entries if at capacity
111
- if (_cache.size >= MAX_ENTRIES && !_cache.has(key)) {
112
- const oldest = _cache.keys().next().value;
113
- if (oldest !== undefined) _cache.delete(oldest);
114
- }
115
-
116
- _cache.set(key, { version: _version, createdAt: Date.now(), result });
117
- }