@vellumai/assistant 0.5.15 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (503) hide show
  1. package/ARCHITECTURE.md +3 -3
  2. package/Dockerfile +0 -3
  3. package/docs/architecture/integrations.md +15 -14
  4. package/knip.json +4 -1
  5. package/openapi.yaml +670 -122
  6. package/package.json +1 -1
  7. package/src/__tests__/actor-token-service.test.ts +68 -0
  8. package/src/__tests__/agent-loop.test.ts +0 -32
  9. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  10. package/src/__tests__/anthropic-provider.test.ts +57 -3
  11. package/src/__tests__/app-compiler.test.ts +120 -0
  12. package/src/__tests__/assistant-feature-flags-integration.test.ts +5 -377
  13. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  14. package/src/__tests__/call-domain.test.ts +2 -6
  15. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  16. package/src/__tests__/call-recovery.test.ts +2 -6
  17. package/src/__tests__/call-routes-http.test.ts +2 -6
  18. package/src/__tests__/call-store.test.ts +2 -6
  19. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  20. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  21. package/src/__tests__/ces-rpc-credential-backend.test.ts +4 -1
  22. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  23. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  24. package/src/__tests__/checker.test.ts +84 -3
  25. package/src/__tests__/clawhub.test.ts +54 -24
  26. package/src/__tests__/cli-command-risk-guard.test.ts +108 -6
  27. package/src/__tests__/cli-memory.test.ts +377 -0
  28. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +12 -2
  29. package/src/__tests__/config-schema.test.ts +1 -3
  30. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  31. package/src/__tests__/config-watcher-feature-flags.test.ts +211 -0
  32. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  33. package/src/__tests__/contacts-tools.test.ts +31 -0
  34. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  35. package/src/__tests__/context-token-estimator.test.ts +175 -10
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +9 -0
  37. package/src/__tests__/conversation-agent-loop.test.ts +9 -0
  38. package/src/__tests__/conversation-attachments.test.ts +2 -6
  39. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  40. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  41. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  42. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  43. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  44. package/src/__tests__/conversation-error.test.ts +33 -2
  45. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  46. package/src/__tests__/conversation-history-web-search.test.ts +5 -0
  47. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  48. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  49. package/src/__tests__/conversation-runtime-assembly.test.ts +7 -4
  50. package/src/__tests__/conversation-slash-commands.test.ts +2 -6
  51. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  52. package/src/__tests__/conversation-store.test.ts +2 -6
  53. package/src/__tests__/conversation-usage.test.ts +3 -6
  54. package/src/__tests__/conversation-wipe.test.ts +11 -408
  55. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  56. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  57. package/src/__tests__/credential-security-e2e.test.ts +6 -1
  58. package/src/__tests__/docker-signing-key-bootstrap.test.ts +7 -73
  59. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -7
  60. package/src/__tests__/followup-tools.test.ts +2 -6
  61. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  62. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  63. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  64. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  65. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  66. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  67. package/src/__tests__/guardian-action-store.test.ts +2 -6
  68. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  69. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  70. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  71. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  72. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  73. package/src/__tests__/guardian-routing-invariants.test.ts +343 -6
  74. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  75. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  76. package/src/__tests__/heartbeat-service.test.ts +1 -3
  77. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  78. package/src/__tests__/injection-block.test.ts +154 -0
  79. package/src/__tests__/install-meta.test.ts +506 -0
  80. package/src/__tests__/install-skill-routing.test.ts +292 -0
  81. package/src/__tests__/intent-routing.test.ts +6 -18
  82. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  83. package/src/__tests__/invite-routes-http.test.ts +2 -6
  84. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -14
  85. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  86. package/src/__tests__/llm-context-route-provider.test.ts +2 -6
  87. package/src/__tests__/llm-request-log-turn-query.test.ts +2 -6
  88. package/src/__tests__/llm-usage-store.test.ts +2 -6
  89. package/src/__tests__/log-export-workspace.test.ts +4 -34
  90. package/src/__tests__/managed-skill-lifecycle.test.ts +7 -37
  91. package/src/__tests__/managed-store.test.ts +40 -21
  92. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  93. package/src/__tests__/memory-recall-log-store.test.ts +2 -6
  94. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  95. package/src/__tests__/messaging-send-tool.test.ts +6 -6
  96. package/src/__tests__/migration-cross-version-compatibility.test.ts +1 -29
  97. package/src/__tests__/migration-export-http.test.ts +3 -34
  98. package/src/__tests__/migration-import-commit-http.test.ts +1 -29
  99. package/src/__tests__/migration-import-preflight-http.test.ts +3 -34
  100. package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +2 -1
  101. package/src/__tests__/non-member-access-request.test.ts +2 -6
  102. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  103. package/src/__tests__/oauth-apps-routes.test.ts +120 -10
  104. package/src/__tests__/oauth-cli.test.ts +364 -2
  105. package/src/__tests__/oauth-connect-orchestrator.test.ts +709 -0
  106. package/src/__tests__/oauth-provider-serializer.test.ts +2 -1
  107. package/src/__tests__/oauth-provider-visibility.test.ts +149 -0
  108. package/src/__tests__/oauth-providers-routes.test.ts +5 -2
  109. package/src/__tests__/oauth-store.test.ts +0 -5
  110. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  111. package/src/__tests__/outlook-attachments.test.ts +301 -0
  112. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  113. package/src/__tests__/outlook-categories.test.ts +212 -0
  114. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  115. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  116. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  117. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  118. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  119. package/src/__tests__/outlook-messaging-provider.test.ts +1071 -0
  120. package/src/__tests__/outlook-trash.test.ts +77 -0
  121. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  122. package/src/__tests__/path-policy.test.ts +2 -17
  123. package/src/__tests__/permission-types.test.ts +0 -1
  124. package/src/__tests__/platform-callback-registration.test.ts +7 -11
  125. package/src/__tests__/playbook-execution.test.ts +76 -80
  126. package/src/__tests__/playbook-tools.test.ts +5 -7
  127. package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
  128. package/src/__tests__/provider-error-scenarios.test.ts +21 -2
  129. package/src/__tests__/qdrant-manager.test.ts +68 -21
  130. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  131. package/src/__tests__/registry.test.ts +2 -2
  132. package/src/__tests__/require-fresh-approval.test.ts +64 -3
  133. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  134. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  135. package/src/__tests__/sandbox-diagnostics.test.ts +20 -29
  136. package/src/__tests__/scaffold-managed-skill-tool.test.ts +2 -10
  137. package/src/__tests__/schedule-store.test.ts +2 -6
  138. package/src/__tests__/schedule-tools.test.ts +2 -6
  139. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  140. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  141. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  142. package/src/__tests__/search-skills-unified.test.ts +421 -0
  143. package/src/__tests__/secret-allowlist.test.ts +20 -35
  144. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  145. package/src/__tests__/send-endpoint-busy.test.ts +2 -6
  146. package/src/__tests__/sequence-store.test.ts +2 -6
  147. package/src/__tests__/server-history-render.test.ts +2 -6
  148. package/src/__tests__/shell-credential-ref.test.ts +0 -5
  149. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  150. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  151. package/src/__tests__/skill-load-feature-flag.test.ts +13 -54
  152. package/src/__tests__/skill-load-inline-command.test.ts +3 -65
  153. package/src/__tests__/skill-load-inline-includes.test.ts +3 -65
  154. package/src/__tests__/skill-load-tool.test.ts +3 -67
  155. package/src/__tests__/skill-memory.test.ts +480 -195
  156. package/src/__tests__/skills-uninstall.test.ts +2 -2
  157. package/src/__tests__/skills.test.ts +23 -50
  158. package/src/__tests__/slack-channel-config.test.ts +2 -21
  159. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  160. package/src/__tests__/starter-bundle.test.ts +2 -8
  161. package/src/__tests__/stt-hints.test.ts +7 -2
  162. package/src/__tests__/system-prompt.test.ts +25 -45
  163. package/src/__tests__/task-compiler.test.ts +2 -27
  164. package/src/__tests__/task-management-tools.test.ts +2 -27
  165. package/src/__tests__/task-memory-cleanup.test.ts +173 -250
  166. package/src/__tests__/task-runner.test.ts +2 -27
  167. package/src/__tests__/task-scheduler.test.ts +2 -27
  168. package/src/__tests__/terminal-tools.test.ts +1 -17
  169. package/src/__tests__/test-preload.ts +3 -0
  170. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +0 -79
  171. package/src/__tests__/tool-approval-handler.test.ts +4 -27
  172. package/src/__tests__/tool-execution-abort-cleanup.test.ts +2 -11
  173. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -25
  174. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  175. package/src/__tests__/tool-executor.test.ts +0 -1
  176. package/src/__tests__/tool-grant-request-escalation.test.ts +4 -27
  177. package/src/__tests__/tool-preview-lifecycle.test.ts +0 -20
  178. package/src/__tests__/tool-side-effects-slack-dm.test.ts +276 -0
  179. package/src/__tests__/trust-store.test.ts +10 -42
  180. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -30
  181. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +3 -27
  182. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -28
  183. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -28
  184. package/src/__tests__/trusted-contact-verification.test.ts +2 -28
  185. package/src/__tests__/turn-boundary-resolution.test.ts +2 -34
  186. package/src/__tests__/twilio-provider.test.ts +0 -16
  187. package/src/__tests__/twilio-routes-twiml.test.ts +7 -12
  188. package/src/__tests__/twilio-routes.test.ts +0 -24
  189. package/src/__tests__/update-bulletin.test.ts +17 -89
  190. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -26
  191. package/src/__tests__/usage-routes.test.ts +2 -27
  192. package/src/__tests__/user-reference.test.ts +1 -5
  193. package/src/__tests__/vbundle-pax-and-symlink.test.ts +4 -34
  194. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +2 -53
  195. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  196. package/src/__tests__/voice-invite-redemption.test.ts +2 -27
  197. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -30
  198. package/src/__tests__/voice-session-bridge.test.ts +2 -27
  199. package/src/__tests__/volume-security-guard.test.ts +2 -0
  200. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  201. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -29
  202. package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +2 -2
  203. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +4 -29
  204. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  205. package/src/__tests__/workspace-migration-down-functions.test.ts +0 -6
  206. package/src/__tests__/workspace-policy.test.ts +1 -1
  207. package/src/acp/client-handler.ts +1 -2
  208. package/src/agent/attachments.ts +7 -2
  209. package/src/agent/image-optimize.ts +165 -0
  210. package/src/agent/loop.ts +1 -15
  211. package/src/bundler/app-compiler.ts +179 -2
  212. package/src/bundler/package-resolver.ts +3 -5
  213. package/src/cli/__tests__/notifications.test.ts +1 -24
  214. package/src/cli/cli-memory.ts +179 -0
  215. package/src/cli/commands/avatar.ts +3 -3
  216. package/src/cli/commands/config.ts +26 -13
  217. package/src/cli/commands/doctor.ts +2 -2
  218. package/src/cli/commands/memory.ts +41 -55
  219. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  220. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  221. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  222. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  223. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  224. package/src/cli/commands/oauth/connect.ts +26 -6
  225. package/src/cli/commands/oauth/mode.ts +7 -0
  226. package/src/cli/commands/oauth/providers.ts +49 -42
  227. package/src/cli/commands/oauth/shared.ts +39 -3
  228. package/src/cli/commands/platform/__tests__/connect.test.ts +3 -49
  229. package/src/cli/commands/platform/__tests__/disconnect.test.ts +3 -49
  230. package/src/cli/commands/platform/__tests__/status.test.ts +5 -55
  231. package/src/cli/commands/platform/index.ts +16 -16
  232. package/src/cli/commands/skills.ts +88 -16
  233. package/src/cli/commands/trust.ts +2 -2
  234. package/src/cli/lib/daemon-credential-client.ts +2 -3
  235. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  236. package/src/config/bundled-skills/computer-use/TOOLS.json +7 -7
  237. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  238. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  239. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  240. package/src/config/bundled-skills/gmail/SKILL.md +2 -10
  241. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  242. package/src/config/bundled-skills/messaging/SKILL.md +26 -19
  243. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  244. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  245. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  246. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  247. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  248. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  249. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  250. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  251. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  252. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  253. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  254. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  255. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  256. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  257. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  258. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  259. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  260. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  261. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  262. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  263. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  264. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  265. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  266. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  267. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  268. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  269. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  270. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  271. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  272. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  273. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  274. package/src/config/bundled-skills/slack/SKILL.md +1 -7
  275. package/src/config/bundled-tool-registry.ts +56 -4
  276. package/src/config/env-registry.ts +15 -8
  277. package/src/config/feature-flag-registry.json +29 -116
  278. package/src/config/loader.ts +4 -0
  279. package/src/config/schemas/platform.ts +8 -0
  280. package/src/config/schemas/security.ts +0 -6
  281. package/src/config/schemas/services.ts +8 -0
  282. package/src/config/schemas/timeouts.ts +1 -1
  283. package/src/config/skills.ts +18 -7
  284. package/src/context/token-estimator.ts +25 -18
  285. package/src/context/window-manager.ts +32 -9
  286. package/src/credential-execution/approval-bridge.ts +0 -1
  287. package/src/credential-execution/process-manager.ts +3 -1
  288. package/src/daemon/config-watcher.ts +51 -0
  289. package/src/daemon/context-overflow-reducer.ts +46 -2
  290. package/src/daemon/conversation-agent-loop-handlers.ts +123 -82
  291. package/src/daemon/conversation-agent-loop.ts +99 -63
  292. package/src/daemon/conversation-error.ts +31 -8
  293. package/src/daemon/conversation-lifecycle.ts +33 -0
  294. package/src/daemon/conversation-media-retry.ts +85 -7
  295. package/src/daemon/conversation-notifiers.ts +4 -1
  296. package/src/daemon/conversation-process.ts +1 -0
  297. package/src/daemon/conversation-runtime-assembly.ts +5 -0
  298. package/src/daemon/conversation-usage.ts +1 -0
  299. package/src/daemon/conversation.ts +41 -2
  300. package/src/daemon/daemon-control.ts +8 -2
  301. package/src/daemon/handlers/shared.ts +22 -12
  302. package/src/daemon/handlers/skills.ts +423 -201
  303. package/src/daemon/lifecycle.ts +52 -4
  304. package/src/daemon/main.ts +5 -1
  305. package/src/daemon/message-types/conversations.ts +5 -1
  306. package/src/daemon/message-types/messages.ts +3 -1
  307. package/src/daemon/message-types/skills.ts +97 -36
  308. package/src/daemon/providers-setup.ts +7 -0
  309. package/src/daemon/server.ts +35 -22
  310. package/src/daemon/tool-side-effects.ts +27 -5
  311. package/src/events/domain-events.ts +1 -2
  312. package/src/heartbeat/heartbeat-service.ts +1 -0
  313. package/src/hooks/cli.ts +2 -2
  314. package/src/hooks/runner.ts +15 -38
  315. package/src/inbound/platform-callback-registration.ts +14 -14
  316. package/src/memory/admin.ts +11 -45
  317. package/src/memory/conversation-bootstrap.ts +2 -0
  318. package/src/memory/conversation-crud.ts +242 -348
  319. package/src/memory/conversation-group-migration.ts +157 -0
  320. package/src/memory/conversation-queries.ts +4 -2
  321. package/src/memory/db-init.ts +39 -3
  322. package/src/memory/embed.ts +73 -0
  323. package/src/memory/embedding-backend.ts +8 -14
  324. package/src/memory/embedding-runtime-manager.ts +12 -114
  325. package/src/memory/fingerprint.ts +2 -2
  326. package/src/memory/graph/bootstrap.ts +512 -0
  327. package/src/memory/graph/capability-seed.ts +297 -0
  328. package/src/memory/graph/consolidation.ts +691 -0
  329. package/src/memory/graph/conversation-graph-memory.ts +630 -0
  330. package/src/memory/graph/decay.test.ts +208 -0
  331. package/src/memory/graph/decay.ts +195 -0
  332. package/src/memory/graph/extraction-job.ts +69 -0
  333. package/src/memory/graph/extraction.test.ts +936 -0
  334. package/src/memory/graph/extraction.ts +1254 -0
  335. package/src/memory/graph/graph-search.ts +266 -0
  336. package/src/memory/graph/image-ref-utils.ts +29 -0
  337. package/src/memory/graph/injection.test.ts +513 -0
  338. package/src/memory/graph/injection.ts +439 -0
  339. package/src/memory/graph/inspect.ts +534 -0
  340. package/src/memory/graph/narrative.ts +267 -0
  341. package/src/memory/graph/pattern-scan.ts +269 -0
  342. package/src/memory/graph/retriever.ts +1008 -0
  343. package/src/memory/graph/scoring.test.ts +548 -0
  344. package/src/memory/graph/scoring.ts +232 -0
  345. package/src/memory/graph/serendipity.ts +65 -0
  346. package/src/memory/graph/store.test.ts +1050 -0
  347. package/src/memory/graph/store.ts +699 -0
  348. package/src/memory/graph/tool-handlers.ts +426 -0
  349. package/src/memory/graph/tools.ts +141 -0
  350. package/src/memory/graph/triggers.test.ts +487 -0
  351. package/src/memory/graph/triggers.ts +223 -0
  352. package/src/memory/graph/types.ts +271 -0
  353. package/src/memory/group-crud.ts +191 -0
  354. package/src/memory/indexer.ts +37 -19
  355. package/src/memory/job-handlers/cleanup.ts +0 -53
  356. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  357. package/src/memory/job-handlers/embedding.test.ts +3 -27
  358. package/src/memory/job-handlers/embedding.ts +5 -31
  359. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  360. package/src/memory/job-handlers/summarization.ts +32 -17
  361. package/src/memory/job-utils.ts +1 -1
  362. package/src/memory/jobs-store.ts +50 -70
  363. package/src/memory/jobs-worker.ts +147 -112
  364. package/src/memory/llm-usage-store.ts +35 -2
  365. package/src/memory/message-content.ts +1 -0
  366. package/src/memory/migrations/201-oauth-providers-feature-flag.ts +11 -0
  367. package/src/memory/migrations/202-drop-callback-transport-column.ts +13 -0
  368. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  369. package/src/memory/migrations/203-drop-memory-items-tables.ts +23 -0
  370. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  371. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  372. package/src/memory/migrations/index.ts +6 -0
  373. package/src/memory/migrations/registry.ts +8 -0
  374. package/src/memory/qdrant-client.ts +44 -17
  375. package/src/memory/qdrant-manager.ts +26 -5
  376. package/src/memory/schema/index.ts +1 -0
  377. package/src/memory/schema/memory-graph.ts +139 -0
  378. package/src/memory/schema/oauth.ts +1 -1
  379. package/src/memory/search/semantic.ts +47 -91
  380. package/src/memory/slack-thread-store.ts +17 -0
  381. package/src/memory/task-memory-cleanup.ts +28 -50
  382. package/src/messaging/providers/outlook/adapter.ts +200 -0
  383. package/src/messaging/providers/outlook/client.ts +610 -0
  384. package/src/messaging/providers/outlook/types.ts +201 -0
  385. package/src/notifications/adapters/macos.ts +1 -0
  386. package/src/notifications/adapters/slack.ts +1 -1
  387. package/src/notifications/copy-composer.ts +9 -0
  388. package/src/notifications/signal.ts +16 -0
  389. package/src/oauth/__tests__/identity-verifier.test.ts +1 -1
  390. package/src/oauth/connect-orchestrator.ts +10 -3
  391. package/src/oauth/oauth-store.ts +10 -11
  392. package/src/oauth/provider-serializer.ts +3 -0
  393. package/src/oauth/provider-visibility.ts +16 -0
  394. package/src/oauth/seed-providers.ts +50 -17
  395. package/src/permissions/checker.ts +62 -9
  396. package/src/permissions/defaults.ts +4 -4
  397. package/src/permissions/types.ts +2 -4
  398. package/src/permissions/workspace-policy.ts +1 -1
  399. package/src/playbooks/playbook-compiler.ts +19 -18
  400. package/src/playbooks/types.ts +4 -3
  401. package/src/prompts/system-prompt.ts +6 -93
  402. package/src/prompts/templates/UPDATES.md +6 -0
  403. package/src/providers/anthropic/client.ts +47 -19
  404. package/src/providers/gemini/client.ts +1 -1
  405. package/src/providers/openai/client.ts +1 -1
  406. package/src/providers/registry.ts +1 -1
  407. package/src/providers/retry.ts +19 -3
  408. package/src/runtime/actor-trust-resolver.ts +5 -1
  409. package/src/runtime/auth/__tests__/credential-service.test.ts +1 -27
  410. package/src/runtime/auth/__tests__/token-service.test.ts +1 -25
  411. package/src/runtime/auth/route-policy.ts +7 -4
  412. package/src/runtime/guardian-reply-router.ts +10 -2
  413. package/src/runtime/http-server.ts +23 -3
  414. package/src/runtime/middleware/auth.ts +20 -0
  415. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  416. package/src/runtime/routes/attachment-routes.ts +106 -16
  417. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  418. package/src/runtime/routes/btw-routes.ts +8 -0
  419. package/src/runtime/routes/conversation-management-routes.ts +2 -0
  420. package/src/runtime/routes/conversation-query-routes.ts +2 -58
  421. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  422. package/src/runtime/routes/debug-routes.ts +1 -1
  423. package/src/runtime/routes/global-search-routes.ts +21 -19
  424. package/src/runtime/routes/group-routes.ts +207 -0
  425. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  426. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  427. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  428. package/src/runtime/routes/inbound-stages/background-dispatch.ts +43 -2
  429. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  430. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  431. package/src/runtime/routes/memory-item-routes.test.ts +2 -31
  432. package/src/runtime/routes/memory-item-routes.ts +385 -341
  433. package/src/runtime/routes/oauth-apps.ts +18 -1
  434. package/src/runtime/routes/oauth-providers.ts +13 -1
  435. package/src/runtime/routes/schedule-routes.ts +2 -0
  436. package/src/runtime/routes/settings-routes.ts +1 -0
  437. package/src/runtime/routes/skills-routes.ts +103 -37
  438. package/src/runtime/routes/usage-routes.ts +19 -2
  439. package/src/runtime/routes/work-items-routes.test.ts +2 -27
  440. package/src/runtime/routes/workspace-routes.test.ts +3 -27
  441. package/src/schedule/scheduler.ts +8 -1
  442. package/src/security/oauth2.ts +1 -1
  443. package/src/security/secret-allowlist.ts +4 -4
  444. package/src/security/secure-keys.ts +4 -8
  445. package/src/shared/provider-env-vars.ts +19 -0
  446. package/src/skills/catalog-cache.ts +5 -0
  447. package/src/skills/catalog-install.ts +15 -14
  448. package/src/skills/clawhub.ts +134 -154
  449. package/src/skills/install-meta.ts +208 -0
  450. package/src/skills/managed-store.ts +27 -16
  451. package/src/skills/skill-memory.ts +210 -96
  452. package/src/skills/skillssh-registry.ts +19 -17
  453. package/src/tasks/task-runner.ts +3 -1
  454. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  455. package/src/tools/browser/runtime-check.ts +3 -1
  456. package/src/tools/memory/register.ts +63 -46
  457. package/src/tools/permission-checker.ts +7 -19
  458. package/src/tools/shared/filesystem/image-read.ts +22 -85
  459. package/src/tools/skills/skill-script-runner.ts +1 -1
  460. package/src/tools/terminal/safe-env.ts +1 -0
  461. package/src/tools/tool-manifest.ts +3 -3
  462. package/src/util/browser.ts +25 -10
  463. package/src/util/bun-runtime.ts +172 -0
  464. package/src/util/device-id.ts +3 -65
  465. package/src/watcher/providers/outlook-calendar.ts +343 -0
  466. package/src/watcher/providers/outlook.ts +198 -0
  467. package/src/workspace/git-service.ts +27 -6
  468. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  469. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  470. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  471. package/src/workspace/migrations/registry.ts +6 -0
  472. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  473. package/src/__tests__/journal-context.test.ts +0 -268
  474. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  475. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  476. package/src/__tests__/memory-query-builder.test.ts +0 -59
  477. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  478. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  479. package/src/__tests__/memory-regressions.test.ts +0 -3696
  480. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  481. package/src/daemon/conversation-memory.ts +0 -207
  482. package/src/memory/conversation-starters-cadence.ts +0 -74
  483. package/src/memory/items-extractor.ts +0 -860
  484. package/src/memory/job-handlers/batch-extraction.ts +0 -741
  485. package/src/memory/job-handlers/extraction.ts +0 -40
  486. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -383
  487. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  488. package/src/memory/journal-memory.ts +0 -224
  489. package/src/memory/query-builder.ts +0 -47
  490. package/src/memory/query-expansion.ts +0 -83
  491. package/src/memory/retriever.test.ts +0 -1590
  492. package/src/memory/retriever.ts +0 -1323
  493. package/src/memory/search/formatting.test.ts +0 -140
  494. package/src/memory/search/formatting.ts +0 -262
  495. package/src/memory/search/mmr.ts +0 -136
  496. package/src/memory/search/ranking.ts +0 -15
  497. package/src/memory/search/staleness.ts +0 -40
  498. package/src/memory/search/tier-classifier.ts +0 -18
  499. package/src/memory/search/types.ts +0 -121
  500. package/src/prompts/journal-context.ts +0 -156
  501. package/src/tools/memory/definitions.ts +0 -69
  502. package/src/tools/memory/handlers.test.ts +0 -590
  503. package/src/tools/memory/handlers.ts +0 -434
@@ -0,0 +1,487 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ evaluateEventTriggers,
5
+ evaluateSemanticTriggers,
6
+ evaluateTemporalTriggers,
7
+ } from "./triggers.js";
8
+ import type { MemoryTrigger } from "./types.js";
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Helpers
12
+ // ---------------------------------------------------------------------------
13
+
14
+ const DAY_MS = 1000 * 60 * 60 * 24;
15
+
16
+ function makeTrigger(overrides: Partial<MemoryTrigger> = {}): MemoryTrigger {
17
+ return {
18
+ id: "trigger-1",
19
+ nodeId: "node-1",
20
+ type: "temporal",
21
+ schedule: null,
22
+ condition: null,
23
+ conditionEmbedding: null,
24
+ threshold: null,
25
+ eventDate: null,
26
+ rampDays: null,
27
+ followUpDays: null,
28
+ recurring: false,
29
+ consumed: false,
30
+ cooldownMs: null,
31
+ lastFired: null,
32
+ ...overrides,
33
+ };
34
+ }
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // evaluateTemporalTriggers
38
+ // ---------------------------------------------------------------------------
39
+
40
+ describe("evaluateTemporalTriggers", () => {
41
+ test("fires day-of-week trigger on matching day", () => {
42
+ // 2025-06-16 is a Monday
43
+ const monday = new Date("2025-06-16T10:00:00Z");
44
+ const trigger = makeTrigger({
45
+ type: "temporal",
46
+ schedule: "day-of-week:monday",
47
+ });
48
+ const results = evaluateTemporalTriggers([trigger], monday);
49
+ expect(results).toHaveLength(1);
50
+ expect(results[0].boost).toBe(1.0);
51
+ });
52
+
53
+ test("does not fire day-of-week trigger on non-matching day", () => {
54
+ // 2025-06-17 is a Tuesday
55
+ const tuesday = new Date("2025-06-17T10:00:00Z");
56
+ const trigger = makeTrigger({
57
+ type: "temporal",
58
+ schedule: "day-of-week:monday",
59
+ });
60
+ const results = evaluateTemporalTriggers([trigger], tuesday);
61
+ expect(results).toHaveLength(0);
62
+ });
63
+
64
+ test("fires date trigger on matching date", () => {
65
+ const april8 = new Date("2025-04-08T15:00:00Z");
66
+ const trigger = makeTrigger({
67
+ type: "temporal",
68
+ schedule: "date:04-08",
69
+ });
70
+ const results = evaluateTemporalTriggers([trigger], april8);
71
+ expect(results).toHaveLength(1);
72
+ });
73
+
74
+ test("does not fire date trigger on non-matching date", () => {
75
+ const april9 = new Date("2025-04-09T15:00:00Z");
76
+ const trigger = makeTrigger({
77
+ type: "temporal",
78
+ schedule: "date:04-08",
79
+ });
80
+ const results = evaluateTemporalTriggers([trigger], april9);
81
+ expect(results).toHaveLength(0);
82
+ });
83
+
84
+ test("fires time:morning trigger between 5-11", () => {
85
+ const morning = new Date("2025-06-15T08:00:00"); // local hour 8
86
+ const trigger = makeTrigger({
87
+ type: "temporal",
88
+ schedule: "time:morning",
89
+ });
90
+ const results = evaluateTemporalTriggers([trigger], morning);
91
+ expect(results).toHaveLength(1);
92
+ });
93
+
94
+ test("fires time:afternoon trigger between 12-16", () => {
95
+ const afternoon = new Date("2025-06-15T14:00:00"); // local hour 14
96
+ const trigger = makeTrigger({
97
+ type: "temporal",
98
+ schedule: "time:afternoon",
99
+ });
100
+ const results = evaluateTemporalTriggers([trigger], afternoon);
101
+ expect(results).toHaveLength(1);
102
+ });
103
+
104
+ test("fires time:evening trigger between 17-20", () => {
105
+ const evening = new Date("2025-06-15T18:00:00"); // local hour 18
106
+ const trigger = makeTrigger({
107
+ type: "temporal",
108
+ schedule: "time:evening",
109
+ });
110
+ const results = evaluateTemporalTriggers([trigger], evening);
111
+ expect(results).toHaveLength(1);
112
+ });
113
+
114
+ test("fires time:night trigger at 22:00", () => {
115
+ const night = new Date("2025-06-15T22:00:00"); // local hour 22
116
+ const trigger = makeTrigger({
117
+ type: "temporal",
118
+ schedule: "time:night",
119
+ });
120
+ const results = evaluateTemporalTriggers([trigger], night);
121
+ expect(results).toHaveLength(1);
122
+ });
123
+
124
+ test("fires time:night trigger at 3 AM (wraps past midnight)", () => {
125
+ const lateNight = new Date("2025-06-15T03:00:00"); // local hour 3
126
+ const trigger = makeTrigger({
127
+ type: "temporal",
128
+ schedule: "time:night",
129
+ });
130
+ const results = evaluateTemporalTriggers([trigger], lateNight);
131
+ expect(results).toHaveLength(1);
132
+ });
133
+
134
+ test("does not fire time:morning at 14:00", () => {
135
+ const afternoon = new Date("2025-06-15T14:00:00");
136
+ const trigger = makeTrigger({
137
+ type: "temporal",
138
+ schedule: "time:morning",
139
+ });
140
+ const results = evaluateTemporalTriggers([trigger], afternoon);
141
+ expect(results).toHaveLength(0);
142
+ });
143
+
144
+ test("skips non-temporal triggers", () => {
145
+ const now = new Date("2025-06-16T10:00:00Z");
146
+ const trigger = makeTrigger({
147
+ type: "semantic",
148
+ schedule: "day-of-week:monday",
149
+ });
150
+ const results = evaluateTemporalTriggers([trigger], now);
151
+ expect(results).toHaveLength(0);
152
+ });
153
+
154
+ test("skips triggers without a schedule", () => {
155
+ const now = new Date("2025-06-16T10:00:00Z");
156
+ const trigger = makeTrigger({
157
+ type: "temporal",
158
+ schedule: null,
159
+ });
160
+ const results = evaluateTemporalTriggers([trigger], now);
161
+ expect(results).toHaveLength(0);
162
+ });
163
+
164
+ test("respects cooldown for recurring triggers", () => {
165
+ const now = new Date("2025-06-16T10:00:00Z");
166
+ const trigger = makeTrigger({
167
+ type: "temporal",
168
+ schedule: "day-of-week:monday",
169
+ recurring: true,
170
+ cooldownMs: DAY_MS,
171
+ lastFired: now.getTime() - DAY_MS / 2, // fired 12h ago, cooldown 24h
172
+ });
173
+ const results = evaluateTemporalTriggers([trigger], now);
174
+ expect(results).toHaveLength(0);
175
+ });
176
+
177
+ test("fires recurring trigger after cooldown expires", () => {
178
+ const now = new Date("2025-06-16T10:00:00Z");
179
+ const trigger = makeTrigger({
180
+ type: "temporal",
181
+ schedule: "day-of-week:monday",
182
+ recurring: true,
183
+ cooldownMs: DAY_MS,
184
+ lastFired: now.getTime() - DAY_MS * 2, // fired 2 days ago, cooldown 24h
185
+ });
186
+ const results = evaluateTemporalTriggers([trigger], now);
187
+ expect(results).toHaveLength(1);
188
+ });
189
+
190
+ test("non-recurring triggers bypass cooldown check", () => {
191
+ const now = new Date("2025-06-16T10:00:00Z");
192
+ const trigger = makeTrigger({
193
+ type: "temporal",
194
+ schedule: "day-of-week:monday",
195
+ recurring: false,
196
+ lastFired: now.getTime() - 1000, // fired 1s ago
197
+ });
198
+ const results = evaluateTemporalTriggers([trigger], now);
199
+ expect(results).toHaveLength(1);
200
+ });
201
+
202
+ test("handles case-insensitive schedule matching", () => {
203
+ const monday = new Date("2025-06-16T10:00:00Z");
204
+ const trigger = makeTrigger({
205
+ type: "temporal",
206
+ schedule: "Day-Of-Week:Monday",
207
+ });
208
+ const results = evaluateTemporalTriggers([trigger], monday);
209
+ expect(results).toHaveLength(1);
210
+ });
211
+
212
+ test("evaluates multiple triggers independently", () => {
213
+ const monday = new Date("2025-06-16T10:00:00Z");
214
+ const triggers = [
215
+ makeTrigger({
216
+ id: "t1",
217
+ type: "temporal",
218
+ schedule: "day-of-week:monday",
219
+ }),
220
+ makeTrigger({
221
+ id: "t2",
222
+ type: "temporal",
223
+ schedule: "day-of-week:tuesday",
224
+ }),
225
+ makeTrigger({
226
+ id: "t3",
227
+ type: "temporal",
228
+ schedule: "day-of-week:monday",
229
+ }),
230
+ ];
231
+ const results = evaluateTemporalTriggers(triggers, monday);
232
+ expect(results).toHaveLength(2);
233
+ expect(results.map((r) => r.trigger.id)).toEqual(["t1", "t3"]);
234
+ });
235
+ });
236
+
237
+ // ---------------------------------------------------------------------------
238
+ // evaluateSemanticTriggers
239
+ // ---------------------------------------------------------------------------
240
+
241
+ describe("evaluateSemanticTriggers", () => {
242
+ test("fires when cosine similarity exceeds threshold", () => {
243
+ // Two identical unit vectors → similarity = 1.0
244
+ const embedding = new Float32Array([1, 0, 0]);
245
+ const trigger = makeTrigger({
246
+ type: "semantic",
247
+ conditionEmbedding: new Float32Array([1, 0, 0]),
248
+ threshold: 0.5,
249
+ });
250
+ const results = evaluateSemanticTriggers([trigger], embedding);
251
+ expect(results).toHaveLength(1);
252
+ expect(results[0].boost).toBeGreaterThanOrEqual(0.5);
253
+ });
254
+
255
+ test("does not fire when similarity is below threshold", () => {
256
+ // Orthogonal vectors → similarity = 0
257
+ const embedding = new Float32Array([1, 0, 0]);
258
+ const trigger = makeTrigger({
259
+ type: "semantic",
260
+ conditionEmbedding: new Float32Array([0, 1, 0]),
261
+ threshold: 0.5,
262
+ });
263
+ const results = evaluateSemanticTriggers([trigger], embedding);
264
+ expect(results).toHaveLength(0);
265
+ });
266
+
267
+ test("boost scales by how far above threshold", () => {
268
+ // Identical vectors: similarity = 1.0, threshold = 0.5
269
+ // boost = (1.0 - 0.5) / (1 - 0.5 + 0.001) ≈ 1.0
270
+ const embedding = new Float32Array([1, 0, 0]);
271
+ const trigger = makeTrigger({
272
+ type: "semantic",
273
+ conditionEmbedding: new Float32Array([1, 0, 0]),
274
+ threshold: 0.5,
275
+ });
276
+ const results = evaluateSemanticTriggers([trigger], embedding);
277
+ expect(results[0].boost).toBeCloseTo(1.0, 1);
278
+ });
279
+
280
+ test("boost has minimum of 0.5", () => {
281
+ // Barely above threshold — boost would be near 0, but floored to 0.5
282
+ const embedding = new Float32Array([1, 0, 0]);
283
+ const trigger = makeTrigger({
284
+ type: "semantic",
285
+ conditionEmbedding: new Float32Array([0.98, 0.2, 0]),
286
+ threshold: 0.97,
287
+ });
288
+ const results = evaluateSemanticTriggers([trigger], embedding);
289
+ if (results.length > 0) {
290
+ expect(results[0].boost).toBeGreaterThanOrEqual(0.5);
291
+ }
292
+ });
293
+
294
+ test("skips consumed triggers", () => {
295
+ const embedding = new Float32Array([1, 0, 0]);
296
+ const trigger = makeTrigger({
297
+ type: "semantic",
298
+ conditionEmbedding: new Float32Array([1, 0, 0]),
299
+ threshold: 0.5,
300
+ consumed: true,
301
+ });
302
+ const results = evaluateSemanticTriggers([trigger], embedding);
303
+ expect(results).toHaveLength(0);
304
+ });
305
+
306
+ test("skips triggers without embeddings", () => {
307
+ const embedding = new Float32Array([1, 0, 0]);
308
+ const trigger = makeTrigger({
309
+ type: "semantic",
310
+ conditionEmbedding: null,
311
+ threshold: 0.5,
312
+ });
313
+ const results = evaluateSemanticTriggers([trigger], embedding);
314
+ expect(results).toHaveLength(0);
315
+ });
316
+
317
+ test("skips triggers without threshold", () => {
318
+ const embedding = new Float32Array([1, 0, 0]);
319
+ const trigger = makeTrigger({
320
+ type: "semantic",
321
+ conditionEmbedding: new Float32Array([1, 0, 0]),
322
+ threshold: null,
323
+ });
324
+ const results = evaluateSemanticTriggers([trigger], embedding);
325
+ expect(results).toHaveLength(0);
326
+ });
327
+
328
+ test("skips non-semantic triggers", () => {
329
+ const embedding = new Float32Array([1, 0, 0]);
330
+ const trigger = makeTrigger({
331
+ type: "temporal",
332
+ conditionEmbedding: new Float32Array([1, 0, 0]),
333
+ threshold: 0.5,
334
+ });
335
+ const results = evaluateSemanticTriggers([trigger], embedding);
336
+ expect(results).toHaveLength(0);
337
+ });
338
+
339
+ test("handles mismatched vector lengths gracefully", () => {
340
+ const embedding = new Float32Array([1, 0, 0]);
341
+ const trigger = makeTrigger({
342
+ type: "semantic",
343
+ conditionEmbedding: new Float32Array([1, 0]),
344
+ threshold: 0.5,
345
+ });
346
+ // cosineSimilarity returns 0 for mismatched lengths → no fire
347
+ const results = evaluateSemanticTriggers([trigger], embedding);
348
+ expect(results).toHaveLength(0);
349
+ });
350
+
351
+ test("accepts number[] as query embedding", () => {
352
+ const embedding = [1, 0, 0];
353
+ const trigger = makeTrigger({
354
+ type: "semantic",
355
+ conditionEmbedding: new Float32Array([1, 0, 0]),
356
+ threshold: 0.5,
357
+ });
358
+ const results = evaluateSemanticTriggers([trigger], embedding);
359
+ expect(results).toHaveLength(1);
360
+ });
361
+ });
362
+
363
+ // ---------------------------------------------------------------------------
364
+ // evaluateEventTriggers
365
+ // ---------------------------------------------------------------------------
366
+
367
+ describe("evaluateEventTriggers", () => {
368
+ test("returns background boost (0.05) for events far in the future", () => {
369
+ const now = new Date("2025-06-15T10:00:00Z");
370
+ const trigger = makeTrigger({
371
+ type: "event",
372
+ eventDate: now.getTime() + 30 * DAY_MS, // 30 days away
373
+ rampDays: 7,
374
+ followUpDays: 2,
375
+ });
376
+ const results = evaluateEventTriggers([trigger], now);
377
+ expect(results).toHaveLength(1);
378
+ expect(results[0].boost).toBeCloseTo(0.05, 5);
379
+ });
380
+
381
+ test("ramps linearly from 0.05 to 1.0 as event approaches", () => {
382
+ const now = new Date("2025-06-15T10:00:00Z");
383
+ const eventDate = now.getTime() + 7 * DAY_MS; // exactly at rampDays boundary
384
+ const trigger = makeTrigger({
385
+ type: "event",
386
+ eventDate,
387
+ rampDays: 7,
388
+ followUpDays: 2,
389
+ });
390
+
391
+ // At rampDays boundary: daysUntil = 7, rampDays = 7 → boost = 0.05
392
+ const resultsAtBoundary = evaluateEventTriggers([trigger], now);
393
+ expect(resultsAtBoundary[0].boost).toBeCloseTo(0.05, 5);
394
+
395
+ // Halfway through ramp: 3.5 days before event
396
+ const halfwayNow = new Date(now.getTime() + 3.5 * DAY_MS);
397
+ const resultsHalfway = evaluateEventTriggers([trigger], halfwayNow);
398
+ expect(resultsHalfway[0].boost).toBeCloseTo(0.525, 1);
399
+
400
+ // Day before: daysUntil ≈ 0 → boost ≈ 1.0
401
+ const dayBeforeNow = new Date(eventDate - 0.1 * DAY_MS);
402
+ const resultsDayBefore = evaluateEventTriggers([trigger], dayBeforeNow);
403
+ expect(resultsDayBefore[0].boost).toBeGreaterThan(0.9);
404
+ });
405
+
406
+ test("returns full boost (1.0) on the day of the event", () => {
407
+ const now = new Date("2025-06-15T10:00:00Z");
408
+ const eventDate = now.getTime(); // right now
409
+ const trigger = makeTrigger({
410
+ type: "event",
411
+ eventDate,
412
+ rampDays: 7,
413
+ followUpDays: 2,
414
+ });
415
+ // daysUntil = 0, which is > -1 → day-of boost = 1.0
416
+ const results = evaluateEventTriggers([trigger], now);
417
+ expect(results).toHaveLength(1);
418
+ expect(results[0].boost).toBe(1.0);
419
+ });
420
+
421
+ test("decays exponentially after the event", () => {
422
+ const eventDate = new Date("2025-06-15T10:00:00Z").getTime();
423
+ const trigger = makeTrigger({
424
+ type: "event",
425
+ eventDate,
426
+ rampDays: 7,
427
+ followUpDays: 2,
428
+ });
429
+
430
+ // 1.5 days after event
431
+ const afterNow = new Date(eventDate + 1.5 * DAY_MS);
432
+ const results = evaluateEventTriggers([trigger], afterNow);
433
+ expect(results).toHaveLength(1);
434
+ expect(results[0].boost).toBeGreaterThan(0.01);
435
+ expect(results[0].boost).toBeLessThan(1.0);
436
+ });
437
+
438
+ test("returns 0 after followUpDays", () => {
439
+ const eventDate = new Date("2025-06-15T10:00:00Z").getTime();
440
+ const trigger = makeTrigger({
441
+ type: "event",
442
+ eventDate,
443
+ rampDays: 7,
444
+ followUpDays: 2,
445
+ });
446
+
447
+ // 3 days after event (beyond followUpDays=2)
448
+ const longAfter = new Date(eventDate + 3 * DAY_MS);
449
+ const results = evaluateEventTriggers([trigger], longAfter);
450
+ expect(results).toHaveLength(0);
451
+ });
452
+
453
+ test("skips non-event triggers", () => {
454
+ const now = new Date("2025-06-15T10:00:00Z");
455
+ const trigger = makeTrigger({
456
+ type: "temporal",
457
+ eventDate: now.getTime() + DAY_MS,
458
+ });
459
+ const results = evaluateEventTriggers([trigger], now);
460
+ expect(results).toHaveLength(0);
461
+ });
462
+
463
+ test("skips triggers without eventDate", () => {
464
+ const now = new Date("2025-06-15T10:00:00Z");
465
+ const trigger = makeTrigger({
466
+ type: "event",
467
+ eventDate: null,
468
+ });
469
+ const results = evaluateEventTriggers([trigger], now);
470
+ expect(results).toHaveLength(0);
471
+ });
472
+
473
+ test("uses default rampDays=7 and followUpDays=2 when not specified", () => {
474
+ const now = new Date("2025-06-15T10:00:00Z");
475
+ const eventDate = now.getTime() + 5 * DAY_MS; // 5 days away (inside default 7-day ramp)
476
+ const trigger = makeTrigger({
477
+ type: "event",
478
+ eventDate,
479
+ rampDays: null, // triggers default of 7
480
+ followUpDays: null, // triggers default of 2
481
+ });
482
+ const results = evaluateEventTriggers([trigger], now);
483
+ expect(results).toHaveLength(1);
484
+ // Should be in the ramp-up phase with boost > background
485
+ expect(results[0].boost).toBeGreaterThan(0.05);
486
+ });
487
+ });
@@ -0,0 +1,223 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Memory Graph — Trigger evaluation
3
+ // ---------------------------------------------------------------------------
4
+
5
+ import type { MemoryTrigger } from "./types.js";
6
+
7
+ export interface TriggeredResult {
8
+ trigger: MemoryTrigger;
9
+ /** Relevance boost from this trigger (0–1). */
10
+ boost: number;
11
+ }
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Temporal triggers — pure date math
15
+ // ---------------------------------------------------------------------------
16
+
17
+ /**
18
+ * Evaluate temporal triggers against the current time.
19
+ * Schedule patterns:
20
+ * "day-of-week:monday" → fires every Monday
21
+ * "date:04-08" → fires on April 8 every year
22
+ * "time:morning" → fires between 5 AM and 11 AM
23
+ * "time:afternoon" → fires between 12 PM and 5 PM
24
+ * "time:evening" → fires between 5 PM and 9 PM
25
+ * "time:night" → fires between 9 PM and 5 AM
26
+ */
27
+ export function evaluateTemporalTriggers(
28
+ triggers: MemoryTrigger[],
29
+ now: Date,
30
+ ): TriggeredResult[] {
31
+ const results: TriggeredResult[] = [];
32
+ const dayNames = [
33
+ "sunday",
34
+ "monday",
35
+ "tuesday",
36
+ "wednesday",
37
+ "thursday",
38
+ "friday",
39
+ "saturday",
40
+ ];
41
+ const currentDay = dayNames[now.getDay()];
42
+ const currentMonth = String(now.getMonth() + 1).padStart(2, "0");
43
+ const currentDate = String(now.getDate()).padStart(2, "0");
44
+ const currentHour = now.getHours();
45
+ const dateStr = `${currentMonth}-${currentDate}`;
46
+
47
+ for (const trigger of triggers) {
48
+ if (trigger.type !== "temporal" || !trigger.schedule) continue;
49
+ if (!passesCooldown(trigger, now)) continue;
50
+
51
+ const schedule = trigger.schedule.toLowerCase();
52
+ let fired = false;
53
+
54
+ if (schedule.startsWith("day-of-week:")) {
55
+ const target = schedule.slice("day-of-week:".length).trim();
56
+ fired = currentDay === target;
57
+ } else if (schedule.startsWith("date:")) {
58
+ const target = schedule.slice("date:".length).trim();
59
+ fired = dateStr === target;
60
+ } else if (schedule.startsWith("time:")) {
61
+ const period = schedule.slice("time:".length).trim();
62
+ fired = matchesTimePeriod(period, currentHour);
63
+ }
64
+
65
+ if (fired) {
66
+ results.push({ trigger, boost: 1.0 });
67
+ }
68
+ }
69
+
70
+ return results;
71
+ }
72
+
73
+ function matchesTimePeriod(period: string, hour: number): boolean {
74
+ switch (period) {
75
+ case "morning":
76
+ return hour >= 5 && hour < 12;
77
+ case "afternoon":
78
+ return hour >= 12 && hour < 17;
79
+ case "evening":
80
+ return hour >= 17 && hour < 21;
81
+ case "night":
82
+ return hour >= 21 || hour < 5;
83
+ default:
84
+ return false;
85
+ }
86
+ }
87
+
88
+ // ---------------------------------------------------------------------------
89
+ // Semantic triggers — cosine similarity against pre-computed embeddings
90
+ // ---------------------------------------------------------------------------
91
+
92
+ /**
93
+ * Evaluate semantic triggers by computing cosine similarity between
94
+ * the current conversational context embedding and each trigger's
95
+ * pre-computed condition embedding.
96
+ *
97
+ * Fast: ~5ms for 50 triggers (just vector math, no LLM).
98
+ */
99
+ export function evaluateSemanticTriggers(
100
+ triggers: MemoryTrigger[],
101
+ queryEmbedding: number[] | Float32Array,
102
+ ): TriggeredResult[] {
103
+ const results: TriggeredResult[] = [];
104
+
105
+ for (const trigger of triggers) {
106
+ if (trigger.type !== "semantic") continue;
107
+ if (!trigger.conditionEmbedding || trigger.threshold == null) continue;
108
+ if (trigger.consumed) continue;
109
+ if (!passesCooldown(trigger, new Date())) continue;
110
+
111
+ const similarity = cosineSimilarity(
112
+ queryEmbedding,
113
+ trigger.conditionEmbedding,
114
+ );
115
+ if (similarity >= trigger.threshold) {
116
+ // Scale boost by how far above threshold (0 at threshold, 1 at similarity=1)
117
+ const boost = Math.min(
118
+ 1.0,
119
+ (similarity - trigger.threshold) / (1 - trigger.threshold + 0.001),
120
+ );
121
+ results.push({ trigger, boost: Math.max(0.5, boost) });
122
+ }
123
+ }
124
+
125
+ return results;
126
+ }
127
+
128
+ /**
129
+ * Cosine similarity between two vectors. Returns value in [-1, 1].
130
+ */
131
+ function cosineSimilarity(
132
+ a: number[] | Float32Array,
133
+ b: number[] | Float32Array,
134
+ ): number {
135
+ if (a.length !== b.length) return 0;
136
+ let dot = 0;
137
+ let normA = 0;
138
+ let normB = 0;
139
+ for (let i = 0; i < a.length; i++) {
140
+ dot += a[i] * b[i];
141
+ normA += a[i] * a[i];
142
+ normB += b[i] * b[i];
143
+ }
144
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
145
+ return denom === 0 ? 0 : dot / denom;
146
+ }
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // Event triggers — ramp function for future events
150
+ // ---------------------------------------------------------------------------
151
+
152
+ /**
153
+ * Evaluate event triggers with a relevance ramp:
154
+ *
155
+ * announced → low bg (0.05) → ramping (linear 0.05→1.0) → day-of (1.0)
156
+ * → decay (exponential) → 0
157
+ */
158
+ export function evaluateEventTriggers(
159
+ triggers: MemoryTrigger[],
160
+ now: Date,
161
+ ): TriggeredResult[] {
162
+ const results: TriggeredResult[] = [];
163
+ const nowMs = now.getTime();
164
+
165
+ for (const trigger of triggers) {
166
+ if (trigger.type !== "event" || trigger.eventDate == null) continue;
167
+
168
+ const boost = computeEventRelevance(
169
+ trigger.eventDate,
170
+ trigger.rampDays ?? 7,
171
+ trigger.followUpDays ?? 2,
172
+ nowMs,
173
+ );
174
+
175
+ if (boost > 0.01) {
176
+ results.push({ trigger, boost });
177
+ }
178
+ }
179
+
180
+ return results;
181
+ }
182
+
183
+ /**
184
+ * Compute the relevance boost for an event at the current time.
185
+ */
186
+ function computeEventRelevance(
187
+ eventDateMs: number,
188
+ rampDays: number,
189
+ followUpDays: number,
190
+ nowMs: number,
191
+ ): number {
192
+ const msPerDay = 1000 * 60 * 60 * 24;
193
+ const daysUntil = (eventDateMs - nowMs) / msPerDay;
194
+
195
+ if (daysUntil > rampDays) {
196
+ // Background awareness — the event exists and is coming
197
+ return 0.05;
198
+ }
199
+ if (daysUntil > 0) {
200
+ // Linear ramp from 0.05 to 1.0 over the ramp period
201
+ return 0.05 + 0.95 * (1 - daysUntil / rampDays);
202
+ }
203
+ if (daysUntil > -1) {
204
+ // Day-of: full boost
205
+ return 1.0;
206
+ }
207
+ if (-daysUntil <= followUpDays) {
208
+ // Rapid exponential decay after the event
209
+ return Math.exp(-(-daysUntil - 1));
210
+ }
211
+ // Event is over
212
+ return 0;
213
+ }
214
+
215
+ // ---------------------------------------------------------------------------
216
+ // Helpers
217
+ // ---------------------------------------------------------------------------
218
+
219
+ function passesCooldown(trigger: MemoryTrigger, now: Date): boolean {
220
+ if (!trigger.recurring) return true;
221
+ if (!trigger.lastFired || !trigger.cooldownMs) return true;
222
+ return now.getTime() - trigger.lastFired >= trigger.cooldownMs;
223
+ }