@vellumai/assistant 0.5.16 → 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 (407) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/Dockerfile +0 -3
  3. package/knip.json +2 -1
  4. package/openapi.yaml +660 -80
  5. package/package.json +1 -1
  6. package/src/__tests__/actor-token-service.test.ts +68 -0
  7. package/src/__tests__/agent-loop.test.ts +0 -32
  8. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  9. package/src/__tests__/anthropic-provider.test.ts +57 -3
  10. package/src/__tests__/app-compiler.test.ts +120 -0
  11. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  12. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  13. package/src/__tests__/call-domain.test.ts +2 -6
  14. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  15. package/src/__tests__/call-recovery.test.ts +2 -6
  16. package/src/__tests__/call-routes-http.test.ts +2 -6
  17. package/src/__tests__/call-store.test.ts +2 -6
  18. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  19. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  20. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  21. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  22. package/src/__tests__/checker.test.ts +25 -3
  23. package/src/__tests__/clawhub.test.ts +54 -24
  24. package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
  25. package/src/__tests__/cli-memory.test.ts +74 -69
  26. package/src/__tests__/config-schema.test.ts +1 -1
  27. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  28. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  29. package/src/__tests__/contacts-tools.test.ts +31 -0
  30. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  31. package/src/__tests__/context-token-estimator.test.ts +175 -10
  32. package/src/__tests__/conversation-agent-loop-overflow.test.ts +9 -0
  33. package/src/__tests__/conversation-agent-loop.test.ts +9 -0
  34. package/src/__tests__/conversation-attachments.test.ts +2 -6
  35. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  36. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  37. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  38. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  39. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  40. package/src/__tests__/conversation-error.test.ts +33 -2
  41. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  42. package/src/__tests__/conversation-history-web-search.test.ts +5 -0
  43. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  44. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  45. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  46. package/src/__tests__/conversation-store.test.ts +2 -6
  47. package/src/__tests__/conversation-usage.test.ts +2 -6
  48. package/src/__tests__/conversation-wipe.test.ts +11 -408
  49. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  50. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  51. package/src/__tests__/credential-security-e2e.test.ts +2 -0
  52. package/src/__tests__/followup-tools.test.ts +2 -6
  53. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  54. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  55. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  56. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  57. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  58. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  59. package/src/__tests__/guardian-action-store.test.ts +2 -6
  60. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  61. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  62. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  63. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  64. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  65. package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
  66. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  67. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  68. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  69. package/src/__tests__/injection-block.test.ts +154 -0
  70. package/src/__tests__/install-meta.test.ts +506 -0
  71. package/src/__tests__/install-skill-routing.test.ts +292 -0
  72. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  73. package/src/__tests__/invite-routes-http.test.ts +2 -6
  74. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -14
  75. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  76. package/src/__tests__/llm-context-route-provider.test.ts +2 -6
  77. package/src/__tests__/llm-request-log-turn-query.test.ts +2 -6
  78. package/src/__tests__/llm-usage-store.test.ts +2 -6
  79. package/src/__tests__/log-export-workspace.test.ts +2 -6
  80. package/src/__tests__/managed-store.test.ts +38 -11
  81. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  82. package/src/__tests__/memory-recall-log-store.test.ts +2 -6
  83. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  84. package/src/__tests__/non-member-access-request.test.ts +2 -6
  85. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  86. package/src/__tests__/oauth-cli.test.ts +364 -2
  87. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  88. package/src/__tests__/outlook-attachments.test.ts +301 -0
  89. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  90. package/src/__tests__/outlook-categories.test.ts +212 -0
  91. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  92. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  93. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  94. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  95. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  96. package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
  97. package/src/__tests__/outlook-trash.test.ts +77 -0
  98. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  99. package/src/__tests__/platform-callback-registration.test.ts +4 -4
  100. package/src/__tests__/playbook-execution.test.ts +76 -80
  101. package/src/__tests__/playbook-tools.test.ts +5 -7
  102. package/src/__tests__/provider-error-scenarios.test.ts +21 -0
  103. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  104. package/src/__tests__/registry.test.ts +2 -2
  105. package/src/__tests__/require-fresh-approval.test.ts +64 -2
  106. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  107. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  108. package/src/__tests__/schedule-store.test.ts +2 -6
  109. package/src/__tests__/schedule-tools.test.ts +2 -6
  110. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  111. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  112. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  113. package/src/__tests__/search-skills-unified.test.ts +421 -0
  114. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  115. package/src/__tests__/send-endpoint-busy.test.ts +2 -6
  116. package/src/__tests__/sequence-store.test.ts +2 -6
  117. package/src/__tests__/server-history-render.test.ts +2 -6
  118. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  119. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  120. package/src/__tests__/skill-load-feature-flag.test.ts +11 -11
  121. package/src/__tests__/skill-memory.test.ts +140 -98
  122. package/src/__tests__/skills-uninstall.test.ts +2 -2
  123. package/src/__tests__/skills.test.ts +1 -1
  124. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  125. package/src/__tests__/task-compiler.test.ts +2 -6
  126. package/src/__tests__/task-management-tools.test.ts +2 -6
  127. package/src/__tests__/task-memory-cleanup.test.ts +173 -229
  128. package/src/__tests__/task-runner.test.ts +2 -6
  129. package/src/__tests__/task-scheduler.test.ts +2 -6
  130. package/src/__tests__/test-preload.ts +3 -0
  131. package/src/__tests__/tool-approval-handler.test.ts +2 -6
  132. package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
  133. package/src/__tests__/tool-side-effects-slack-dm.test.ts +276 -0
  134. package/src/__tests__/trust-store.test.ts +1 -1
  135. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
  136. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -6
  137. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
  138. package/src/__tests__/trusted-contact-verification.test.ts +2 -6
  139. package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
  140. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
  141. package/src/__tests__/usage-routes.test.ts +2 -6
  142. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  143. package/src/__tests__/voice-invite-redemption.test.ts +2 -6
  144. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
  145. package/src/__tests__/voice-session-bridge.test.ts +2 -6
  146. package/src/__tests__/volume-security-guard.test.ts +2 -0
  147. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  148. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
  149. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
  150. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  151. package/src/__tests__/workspace-policy.test.ts +1 -1
  152. package/src/agent/attachments.ts +7 -2
  153. package/src/agent/image-optimize.ts +165 -0
  154. package/src/agent/loop.ts +1 -15
  155. package/src/bundler/app-compiler.ts +179 -2
  156. package/src/bundler/package-resolver.ts +3 -5
  157. package/src/cli/__tests__/notifications.test.ts +1 -2
  158. package/src/cli/cli-memory.ts +67 -64
  159. package/src/cli/commands/avatar.ts +3 -3
  160. package/src/cli/commands/config.ts +26 -13
  161. package/src/cli/commands/doctor.ts +2 -2
  162. package/src/cli/commands/memory.ts +41 -55
  163. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  164. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  165. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  166. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  167. package/src/cli/commands/oauth/connect.ts +11 -6
  168. package/src/cli/commands/oauth/mode.ts +7 -0
  169. package/src/cli/commands/oauth/shared.ts +39 -3
  170. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  171. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  172. package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
  173. package/src/cli/commands/platform/index.ts +16 -16
  174. package/src/cli/commands/skills.ts +88 -16
  175. package/src/cli/commands/trust.ts +2 -2
  176. package/src/cli/lib/daemon-credential-client.ts +2 -3
  177. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  178. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  179. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  180. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  181. package/src/config/bundled-skills/gmail/SKILL.md +2 -10
  182. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  183. package/src/config/bundled-skills/messaging/SKILL.md +10 -18
  184. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  185. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  186. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  187. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  188. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  189. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  190. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  191. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  192. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  193. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  194. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  195. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  196. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  197. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  198. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  199. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  200. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  201. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  202. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  203. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  204. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  205. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  206. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  207. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  208. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  209. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  210. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  211. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  212. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  213. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  214. package/src/config/bundled-skills/slack/SKILL.md +1 -7
  215. package/src/config/bundled-tool-registry.ts +56 -4
  216. package/src/config/env-registry.ts +15 -8
  217. package/src/config/feature-flag-registry.json +21 -124
  218. package/src/config/schemas/platform.ts +8 -0
  219. package/src/config/schemas/timeouts.ts +1 -1
  220. package/src/config/skills.ts +18 -7
  221. package/src/context/token-estimator.ts +25 -18
  222. package/src/context/window-manager.ts +6 -2
  223. package/src/credential-execution/process-manager.ts +3 -1
  224. package/src/daemon/context-overflow-reducer.ts +46 -2
  225. package/src/daemon/conversation-agent-loop-handlers.ts +123 -82
  226. package/src/daemon/conversation-agent-loop.ts +96 -61
  227. package/src/daemon/conversation-error.ts +31 -8
  228. package/src/daemon/conversation-lifecycle.ts +33 -0
  229. package/src/daemon/conversation-media-retry.ts +85 -7
  230. package/src/daemon/conversation-notifiers.ts +4 -1
  231. package/src/daemon/conversation-runtime-assembly.ts +5 -0
  232. package/src/daemon/conversation.ts +41 -2
  233. package/src/daemon/daemon-control.ts +8 -2
  234. package/src/daemon/handlers/shared.ts +22 -12
  235. package/src/daemon/handlers/skills.ts +416 -202
  236. package/src/daemon/lifecycle.ts +40 -1
  237. package/src/daemon/main.ts +5 -1
  238. package/src/daemon/message-types/conversations.ts +4 -1
  239. package/src/daemon/message-types/messages.ts +3 -1
  240. package/src/daemon/message-types/skills.ts +97 -36
  241. package/src/daemon/providers-setup.ts +5 -0
  242. package/src/daemon/server.ts +11 -2
  243. package/src/daemon/tool-side-effects.ts +27 -5
  244. package/src/heartbeat/heartbeat-service.ts +1 -0
  245. package/src/hooks/cli.ts +2 -2
  246. package/src/hooks/runner.ts +15 -38
  247. package/src/inbound/platform-callback-registration.ts +14 -14
  248. package/src/memory/admin.ts +11 -45
  249. package/src/memory/conversation-bootstrap.ts +2 -0
  250. package/src/memory/conversation-crud.ts +242 -348
  251. package/src/memory/conversation-group-migration.ts +157 -0
  252. package/src/memory/conversation-queries.ts +4 -2
  253. package/src/memory/db-init.ts +30 -3
  254. package/src/memory/embed.ts +73 -0
  255. package/src/memory/embedding-backend.ts +8 -14
  256. package/src/memory/embedding-runtime-manager.ts +12 -114
  257. package/src/memory/fingerprint.ts +2 -2
  258. package/src/memory/graph/bootstrap.ts +512 -0
  259. package/src/memory/graph/capability-seed.ts +297 -0
  260. package/src/memory/graph/consolidation.ts +691 -0
  261. package/src/memory/graph/conversation-graph-memory.ts +630 -0
  262. package/src/memory/graph/decay.test.ts +208 -0
  263. package/src/memory/graph/decay.ts +195 -0
  264. package/src/memory/graph/extraction-job.ts +69 -0
  265. package/src/memory/graph/extraction.test.ts +936 -0
  266. package/src/memory/graph/extraction.ts +1254 -0
  267. package/src/memory/graph/graph-search.ts +266 -0
  268. package/src/memory/graph/image-ref-utils.ts +29 -0
  269. package/src/memory/graph/injection.test.ts +513 -0
  270. package/src/memory/graph/injection.ts +439 -0
  271. package/src/memory/graph/inspect.ts +534 -0
  272. package/src/memory/graph/narrative.ts +267 -0
  273. package/src/memory/graph/pattern-scan.ts +269 -0
  274. package/src/memory/graph/retriever.ts +1008 -0
  275. package/src/memory/graph/scoring.test.ts +548 -0
  276. package/src/memory/graph/scoring.ts +232 -0
  277. package/src/memory/graph/serendipity.ts +65 -0
  278. package/src/memory/graph/store.test.ts +1050 -0
  279. package/src/memory/graph/store.ts +699 -0
  280. package/src/memory/graph/tool-handlers.ts +426 -0
  281. package/src/memory/graph/tools.ts +141 -0
  282. package/src/memory/graph/triggers.test.ts +487 -0
  283. package/src/memory/graph/triggers.ts +223 -0
  284. package/src/memory/graph/types.ts +271 -0
  285. package/src/memory/group-crud.ts +191 -0
  286. package/src/memory/indexer.ts +37 -19
  287. package/src/memory/job-handlers/cleanup.ts +0 -53
  288. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  289. package/src/memory/job-handlers/embedding.ts +5 -31
  290. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  291. package/src/memory/job-handlers/summarization.ts +32 -17
  292. package/src/memory/job-utils.ts +1 -1
  293. package/src/memory/jobs-store.ts +50 -70
  294. package/src/memory/jobs-worker.ts +147 -112
  295. package/src/memory/message-content.ts +1 -0
  296. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  297. package/src/memory/migrations/203-drop-memory-items-tables.ts +23 -0
  298. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  299. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  300. package/src/memory/migrations/index.ts +4 -0
  301. package/src/memory/migrations/registry.ts +8 -0
  302. package/src/memory/qdrant-client.ts +44 -17
  303. package/src/memory/schema/index.ts +1 -0
  304. package/src/memory/schema/memory-graph.ts +139 -0
  305. package/src/memory/search/semantic.ts +47 -91
  306. package/src/memory/task-memory-cleanup.ts +28 -50
  307. package/src/messaging/providers/outlook/adapter.ts +8 -1
  308. package/src/messaging/providers/outlook/client.ts +299 -0
  309. package/src/messaging/providers/outlook/types.ts +118 -0
  310. package/src/notifications/adapters/macos.ts +1 -0
  311. package/src/notifications/copy-composer.ts +9 -0
  312. package/src/notifications/signal.ts +16 -0
  313. package/src/oauth/seed-providers.ts +2 -1
  314. package/src/permissions/checker.ts +24 -3
  315. package/src/permissions/defaults.ts +4 -4
  316. package/src/permissions/workspace-policy.ts +1 -1
  317. package/src/playbooks/playbook-compiler.ts +19 -18
  318. package/src/playbooks/types.ts +4 -3
  319. package/src/prompts/system-prompt.ts +3 -29
  320. package/src/providers/anthropic/client.ts +47 -19
  321. package/src/providers/gemini/client.ts +1 -1
  322. package/src/providers/openai/client.ts +1 -1
  323. package/src/providers/registry.ts +1 -1
  324. package/src/providers/retry.ts +19 -3
  325. package/src/runtime/actor-trust-resolver.ts +5 -1
  326. package/src/runtime/auth/route-policy.ts +7 -0
  327. package/src/runtime/guardian-reply-router.ts +5 -1
  328. package/src/runtime/http-server.ts +23 -3
  329. package/src/runtime/middleware/auth.ts +20 -0
  330. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  331. package/src/runtime/routes/attachment-routes.ts +106 -16
  332. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  333. package/src/runtime/routes/btw-routes.ts +8 -0
  334. package/src/runtime/routes/conversation-management-routes.ts +2 -0
  335. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  336. package/src/runtime/routes/debug-routes.ts +1 -1
  337. package/src/runtime/routes/global-search-routes.ts +21 -19
  338. package/src/runtime/routes/group-routes.ts +207 -0
  339. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  340. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  341. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  342. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  343. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  344. package/src/runtime/routes/memory-item-routes.test.ts +2 -14
  345. package/src/runtime/routes/memory-item-routes.ts +341 -388
  346. package/src/runtime/routes/schedule-routes.ts +2 -0
  347. package/src/runtime/routes/skills-routes.ts +103 -37
  348. package/src/runtime/routes/work-items-routes.test.ts +2 -6
  349. package/src/schedule/scheduler.ts +8 -1
  350. package/src/security/oauth2.ts +1 -1
  351. package/src/security/secure-keys.ts +4 -8
  352. package/src/shared/provider-env-vars.ts +19 -0
  353. package/src/skills/catalog-cache.ts +5 -0
  354. package/src/skills/catalog-install.ts +15 -14
  355. package/src/skills/clawhub.ts +134 -154
  356. package/src/skills/install-meta.ts +208 -0
  357. package/src/skills/managed-store.ts +27 -16
  358. package/src/skills/skill-memory.ts +152 -77
  359. package/src/skills/skillssh-registry.ts +19 -17
  360. package/src/tasks/task-runner.ts +3 -1
  361. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  362. package/src/tools/browser/runtime-check.ts +3 -1
  363. package/src/tools/memory/register.ts +63 -46
  364. package/src/tools/permission-checker.ts +7 -1
  365. package/src/tools/shared/filesystem/image-read.ts +22 -85
  366. package/src/tools/terminal/safe-env.ts +1 -0
  367. package/src/tools/tool-manifest.ts +3 -3
  368. package/src/util/browser.ts +25 -10
  369. package/src/util/bun-runtime.ts +172 -0
  370. package/src/watcher/providers/outlook-calendar.ts +343 -0
  371. package/src/watcher/providers/outlook.ts +198 -0
  372. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  373. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  374. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  375. package/src/workspace/migrations/registry.ts +6 -0
  376. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  377. package/src/__tests__/journal-context.test.ts +0 -268
  378. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  379. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  380. package/src/__tests__/memory-query-builder.test.ts +0 -59
  381. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  382. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  383. package/src/__tests__/memory-regressions.test.ts +0 -3696
  384. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  385. package/src/daemon/conversation-memory.ts +0 -207
  386. package/src/memory/conversation-starters-cadence.ts +0 -74
  387. package/src/memory/items-extractor.ts +0 -860
  388. package/src/memory/job-handlers/batch-extraction.ts +0 -753
  389. package/src/memory/job-handlers/extraction.ts +0 -40
  390. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
  391. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  392. package/src/memory/journal-memory.ts +0 -224
  393. package/src/memory/query-builder.ts +0 -47
  394. package/src/memory/query-expansion.ts +0 -83
  395. package/src/memory/retriever.test.ts +0 -1592
  396. package/src/memory/retriever.ts +0 -1331
  397. package/src/memory/search/formatting.test.ts +0 -140
  398. package/src/memory/search/formatting.ts +0 -262
  399. package/src/memory/search/mmr.ts +0 -139
  400. package/src/memory/search/ranking.ts +0 -15
  401. package/src/memory/search/staleness.ts +0 -40
  402. package/src/memory/search/tier-classifier.ts +0 -18
  403. package/src/memory/search/types.ts +0 -121
  404. package/src/prompts/journal-context.ts +0 -154
  405. package/src/tools/memory/definitions.ts +0 -69
  406. package/src/tools/memory/handlers.test.ts +0 -562
  407. package/src/tools/memory/handlers.ts +0 -434
@@ -1,140 +0,0 @@
1
- /**
2
- * Unit tests for buildMemoryInjection — focused on echoes/serendipity
3
- * rendering and budget enforcement.
4
- */
5
- import { describe, expect, test } from "bun:test";
6
-
7
- import { buildMemoryInjection } from "./formatting.js";
8
- import type { Candidate } from "./types.js";
9
-
10
- type CandidateWithLabel = Candidate & { sourceLabel?: string };
11
-
12
- function makeCandidate(
13
- overrides: Partial<CandidateWithLabel> & { id: string },
14
- ): CandidateWithLabel {
15
- return {
16
- key: `item:${overrides.id}`,
17
- type: "item",
18
- source: "semantic",
19
- text: overrides.text ?? `Statement for ${overrides.id}`,
20
- kind: overrides.kind ?? "fact",
21
- confidence: 1,
22
- importance: overrides.importance ?? 0.5,
23
- createdAt: overrides.createdAt ?? Date.now(),
24
- semantic: 0.8,
25
- recency: 0.5,
26
- finalScore: overrides.finalScore ?? 0.6,
27
- ...overrides,
28
- };
29
- }
30
-
31
- describe("buildMemoryInjection — echoes section", () => {
32
- test("renders <echoes> after <recalled> when serendipity items provided", () => {
33
- const candidates = [makeCandidate({ id: "c1", finalScore: 0.8 })];
34
- const serendipityItems = [
35
- makeCandidate({ id: "s1", finalScore: 0, importance: 0.7 }),
36
- ];
37
-
38
- const result = buildMemoryInjection({
39
- candidates,
40
- serendipityItems,
41
- totalBudgetTokens: 2000,
42
- });
43
-
44
- expect(result).toContain("<recalled>");
45
- expect(result).toContain("</recalled>");
46
- expect(result).toContain("<echoes>");
47
- expect(result).toContain("</echoes>");
48
- // <echoes> comes after </recalled>
49
- const recalledEnd = result.indexOf("</recalled>");
50
- const echoesStart = result.indexOf("<echoes>");
51
- expect(echoesStart).toBeGreaterThan(recalledEnd);
52
- });
53
-
54
- test("renders only <echoes> when no recalled candidates but serendipity items exist", () => {
55
- const serendipityItems = [
56
- makeCandidate({ id: "s1", finalScore: 0, importance: 0.6 }),
57
- makeCandidate({ id: "s2", finalScore: 0, importance: 0.4 }),
58
- ];
59
-
60
- const result = buildMemoryInjection({
61
- candidates: [],
62
- serendipityItems,
63
- totalBudgetTokens: 2000,
64
- });
65
-
66
- expect(result).toContain("<memory_context");
67
- expect(result).not.toContain("<recalled>");
68
- expect(result).toContain("<echoes>");
69
- expect(result).toContain("</echoes>");
70
- expect(result).toContain("s1");
71
- expect(result).toContain("s2");
72
- });
73
-
74
- test("echoes section respects ~400 token cap", () => {
75
- // Create serendipity items with very long text to test budget
76
- const longText = "word ".repeat(200); // ~200 tokens
77
- const serendipityItems = [
78
- makeCandidate({ id: "s1", text: longText, finalScore: 0 }),
79
- makeCandidate({ id: "s2", text: longText, finalScore: 0 }),
80
- makeCandidate({ id: "s3", text: longText, finalScore: 0 }),
81
- ];
82
-
83
- const result = buildMemoryInjection({
84
- candidates: [],
85
- serendipityItems,
86
- totalBudgetTokens: 5000, // plenty of total budget
87
- });
88
-
89
- // At ~200 tokens each, the 400-token echoes cap should allow at most 2
90
- const itemMatches = result.match(/<item /g) ?? [];
91
- expect(itemMatches.length).toBeLessThanOrEqual(2);
92
- });
93
-
94
- test("no <echoes> section when serendipity array is empty", () => {
95
- const candidates = [makeCandidate({ id: "c1", finalScore: 0.8 })];
96
-
97
- const result = buildMemoryInjection({
98
- candidates,
99
- serendipityItems: [],
100
- totalBudgetTokens: 2000,
101
- });
102
-
103
- expect(result).toContain("<recalled>");
104
- expect(result).not.toContain("<echoes>");
105
- });
106
-
107
- test("no <echoes> section when serendipity items omitted", () => {
108
- const candidates = [makeCandidate({ id: "c1", finalScore: 0.8 })];
109
-
110
- const result = buildMemoryInjection({
111
- candidates,
112
- totalBudgetTokens: 2000,
113
- });
114
-
115
- expect(result).toContain("<recalled>");
116
- expect(result).not.toContain("<echoes>");
117
- });
118
-
119
- test("echoes items include importance and kind attributes", () => {
120
- const serendipityItems = [
121
- makeCandidate({
122
- id: "echo1",
123
- kind: "preference",
124
- importance: 0.85,
125
- text: "User likes dark mode",
126
- finalScore: 0,
127
- }),
128
- ];
129
-
130
- const result = buildMemoryInjection({
131
- candidates: [],
132
- serendipityItems,
133
- totalBudgetTokens: 2000,
134
- });
135
-
136
- expect(result).toContain('kind="preference"');
137
- expect(result).toContain('importance="0.85"');
138
- expect(result).toContain("User likes dark mode");
139
- });
140
- });
@@ -1,262 +0,0 @@
1
- import { eq } from "drizzle-orm";
2
-
3
- import { estimateTextTokens } from "../../context/token-estimator.js";
4
- import { getLogger } from "../../util/logger.js";
5
- import { getDb } from "../db.js";
6
- import { memoryItems } from "../schema.js";
7
- import type { Candidate } from "./types.js";
8
-
9
- const log = getLogger("memory-formatting");
10
-
11
- /**
12
- * Escape XML-like tag sequences in recalled text to prevent delimiter injection.
13
- * Recalled content is interpolated verbatim inside `<memory>` wrapper tags,
14
- * so any literal `</memory>` (or similar) in the text could break the wrapper
15
- * and let recalled content masquerade as top-level prompt instructions.
16
- *
17
- * Strategy: replace `<` in any XML-tag-like pattern with the Unicode full-width
18
- * less-than sign (U+FF1C) which is visually similar but won't be parsed as XML.
19
- */
20
- export function escapeXmlTags(text: string): string {
21
- // Match anything that looks like an XML tag: <word...> or </word...>
22
- return text.replace(
23
- /<\/?[a-zA-Z][a-zA-Z0-9_-]*[\s>\/]/g,
24
- (match) => "\uFF1C" + match.slice(1),
25
- );
26
- }
27
-
28
- /**
29
- * Convert an epoch-ms timestamp to a timezone-aware absolute time string.
30
- * Format: "YYYY-MM-DD HH:mm TZ" (e.g. "2025-02-13 15:30 PST").
31
- */
32
- export function formatAbsoluteTime(epochMs: number): string {
33
- const date = new Date(epochMs);
34
- const year = date.getFullYear();
35
- const month = String(date.getMonth() + 1).padStart(2, "0");
36
- const day = String(date.getDate()).padStart(2, "0");
37
- const hours = String(date.getHours()).padStart(2, "0");
38
- const minutes = String(date.getMinutes()).padStart(2, "0");
39
-
40
- // Extract short timezone abbreviation (e.g. "PST", "EST", "UTC")
41
- const tz =
42
- new Intl.DateTimeFormat("en-US", { timeZoneName: "short" })
43
- .formatToParts(date)
44
- .find((p) => p.type === "timeZoneName")?.value ?? "UTC";
45
-
46
- return `${year}-${month}-${day} ${hours}:${minutes} ${tz}`;
47
- }
48
-
49
- /**
50
- * Convert an epoch-ms timestamp to a human-readable relative time string.
51
- */
52
- export function formatRelativeTime(epochMs: number): string {
53
- const elapsed = Math.max(0, Date.now() - epochMs);
54
- const hours = elapsed / (1000 * 60 * 60);
55
- if (hours < 1) return "just now";
56
- if (hours < 24) {
57
- const h = Math.floor(hours);
58
- return `${h} hour${h === 1 ? "" : "s"} ago`;
59
- }
60
- const days = hours / 24;
61
- if (days < 7) {
62
- const d = Math.floor(days);
63
- return `${d} day${d === 1 ? "" : "s"} ago`;
64
- }
65
- if (days < 30) {
66
- const w = Math.floor(days / 7);
67
- return `${w} week${w === 1 ? "" : "s"} ago`;
68
- }
69
- if (days < 365) {
70
- const m = Math.floor(days / 30);
71
- return `${m} month${m === 1 ? "" : "s"} ago`;
72
- }
73
- const y = Math.floor(days / 365);
74
- return `${y} year${y === 1 ? "" : "s"} ago`;
75
- }
76
-
77
- // ---------------------------------------------------------------------------
78
- // Unified injection format
79
- // ---------------------------------------------------------------------------
80
-
81
- /**
82
- * Build a unified `<memory_context>` XML injection block from scored candidates.
83
- *
84
- * All candidates are rendered in a single `<recalled>` section sorted by
85
- * `finalScore` descending, with each candidate tagged by type:
86
- * - items: `<item id="item:ID" kind="KIND" importance="N.NN" timestamp="..." from="...">`
87
- * - segments: `<segment id="seg:ID" timestamp="..." from="...">`
88
- * - summaries: `<summary id="sum:ID" timestamp="..." from="...">`
89
- *
90
- * An optional `<echoes>` section renders serendipity items — random
91
- * importance-weighted memories for unexpected connections.
92
- *
93
- * Respects token budget: iterates candidates in score order, accumulates
94
- * token estimates, and stops when the budget is exhausted.
95
- */
96
- export function buildMemoryInjection(params: {
97
- candidates: Array<Candidate & { sourceLabel?: string; staleness?: string; supersedes?: string }>;
98
- serendipityItems?: Array<Candidate & { sourceLabel?: string }>;
99
- totalBudgetTokens?: number;
100
- }): string {
101
- const { candidates, serendipityItems, totalBudgetTokens } = params;
102
-
103
- if (candidates.length === 0 && (!serendipityItems || serendipityItems.length === 0)) {
104
- return "";
105
- }
106
-
107
- // Sort by finalScore descending
108
- const sorted = [...candidates].sort((a, b) => b.finalScore - a.finalScore);
109
-
110
- // Reserve tokens for structural overhead
111
- const WRAPPER_OVERHEAD_TOKENS = estimateTextTokens(
112
- "<memory_context __injected>\n<recalled>\n</recalled>\n</memory_context>",
113
- );
114
- let remainingTokens = totalBudgetTokens
115
- ? Math.max(1, totalBudgetTokens - WRAPPER_OVERHEAD_TOKENS)
116
- : Infinity;
117
-
118
- // Render candidates within budget
119
- const lines: string[] = [];
120
- for (const c of sorted) {
121
- if (remainingTokens <= 0) break;
122
- const line = renderCandidate(c);
123
- const tokens = estimateTextTokens(line);
124
- if (tokens > remainingTokens) continue;
125
- lines.push(line);
126
- remainingTokens -= tokens;
127
- }
128
-
129
- if (lines.length === 0 && (!serendipityItems || serendipityItems.length === 0)) {
130
- return "";
131
- }
132
-
133
- const sections: string[] = [];
134
-
135
- if (lines.length > 0) {
136
- sections.push(`<recalled>\n${lines.join("\n")}\n</recalled>`);
137
- }
138
-
139
- // Echoes section for serendipity items — capped at ~400 tokens of
140
- // the remaining budget after <recalled> items are rendered.
141
- if (serendipityItems && serendipityItems.length > 0) {
142
- const ECHOES_MAX_TOKENS = 400;
143
- let echoesBudget = Math.min(remainingTokens, ECHOES_MAX_TOKENS);
144
- const echoLines: string[] = [];
145
- for (const c of serendipityItems) {
146
- if (echoesBudget <= 0) break;
147
- const line = renderCandidate(c);
148
- const tokens = estimateTextTokens(line);
149
- if (tokens > echoesBudget) continue;
150
- echoLines.push(line);
151
- echoesBudget -= tokens;
152
- remainingTokens -= tokens;
153
- }
154
- if (echoLines.length > 0) {
155
- sections.push(`<echoes>\n${echoLines.join("\n")}\n</echoes>`);
156
- }
157
- }
158
-
159
- if (sections.length === 0) return "";
160
-
161
- return `<memory_context __injected>\n${sections.join("\n")}\n</memory_context>`;
162
- }
163
-
164
- /**
165
- * Look up the supersession chain for a given superseded item ID.
166
- *
167
- * Returns the immediate predecessor's statement and timestamp, plus the
168
- * total chain depth (how many items were superseded in sequence).
169
- * Chain traversal is capped at 10 iterations to prevent infinite loops.
170
- */
171
- export function lookupSupersessionChain(supersededId: string): {
172
- previousStatement: string;
173
- previousTimestamp: number;
174
- chainDepth: number;
175
- } | null {
176
- try {
177
- const db = getDb();
178
-
179
- // Look up the immediate predecessor
180
- const predecessor = db
181
- .select({
182
- statement: memoryItems.statement,
183
- firstSeenAt: memoryItems.firstSeenAt,
184
- supersedes: memoryItems.supersedes,
185
- })
186
- .from(memoryItems)
187
- .where(eq(memoryItems.id, supersededId))
188
- .get();
189
-
190
- if (!predecessor) return null;
191
-
192
- // Count chain depth by following supersedes links (cap at 10)
193
- let chainDepth = 1;
194
- let currentSupersedes = predecessor.supersedes;
195
- const MAX_CHAIN_DEPTH = 10;
196
-
197
- while (currentSupersedes && chainDepth < MAX_CHAIN_DEPTH) {
198
- const ancestor = db
199
- .select({ supersedes: memoryItems.supersedes })
200
- .from(memoryItems)
201
- .where(eq(memoryItems.id, currentSupersedes))
202
- .get();
203
-
204
- if (!ancestor) break;
205
- chainDepth++;
206
- currentSupersedes = ancestor.supersedes;
207
- }
208
-
209
- return {
210
- previousStatement: predecessor.statement,
211
- previousTimestamp: predecessor.firstSeenAt,
212
- chainDepth,
213
- };
214
- } catch (err) {
215
- log.warn({ err }, "Failed to look up supersession chain");
216
- return null;
217
- }
218
- }
219
-
220
- /**
221
- * Render a single candidate as an XML element based on its type.
222
- */
223
- function renderCandidate(c: Candidate & { sourceLabel?: string; supersedes?: string }): string {
224
- const text = escapeXmlTags(c.text);
225
- const timestamp = formatAbsoluteTime(c.createdAt);
226
- const fromAttr = c.sourceLabel
227
- ? ` from="${escapeXmlAttr(c.sourceLabel)}"`
228
- : "";
229
- const pathAttr = c.sourcePath
230
- ? ` path="${escapeXmlAttr(c.sourcePath)}"`
231
- : "";
232
-
233
- // Build inline supersession suffix for items
234
- let supersessionSuffix = "";
235
- if (c.type === "item" && c.supersedes) {
236
- const chain = lookupSupersessionChain(c.supersedes);
237
- if (chain) {
238
- const prevTimestamp = formatAbsoluteTime(chain.previousTimestamp);
239
- supersessionSuffix = `<supersedes count="${chain.chainDepth}">${escapeXmlTags(chain.previousStatement)} (${prevTimestamp})</supersedes>`;
240
- }
241
- }
242
-
243
- switch (c.type) {
244
- case "item":
245
- return `<item id="item:${escapeXmlAttr(c.id)}" kind="${escapeXmlAttr(c.kind)}" importance="${c.importance.toFixed(2)}" timestamp="${escapeXmlAttr(timestamp)}"${fromAttr}${pathAttr}>${text}${supersessionSuffix}</item>`;
246
- case "segment":
247
- return `<segment id="seg:${escapeXmlAttr(c.id)}" timestamp="${escapeXmlAttr(timestamp)}"${fromAttr}${pathAttr}>${text}</segment>`;
248
- case "summary":
249
- return `<summary id="sum:${escapeXmlAttr(c.id)}" timestamp="${escapeXmlAttr(timestamp)}"${fromAttr}${pathAttr}>${text}</summary>`;
250
- default:
251
- // media or unknown types — render as item
252
- return `<item id="item:${escapeXmlAttr(c.id)}" kind="${escapeXmlAttr(c.kind)}" importance="${c.importance.toFixed(2)}" timestamp="${escapeXmlAttr(timestamp)}"${fromAttr}${pathAttr}>${text}${supersessionSuffix}</item>`;
253
- }
254
- }
255
-
256
- function escapeXmlAttr(text: string): string {
257
- return text
258
- .replace(/&/g, "&amp;")
259
- .replace(/"/g, "&quot;")
260
- .replace(/</g, "&lt;")
261
- .replace(/>/g, "&gt;");
262
- }
@@ -1,139 +0,0 @@
1
- import { generateSparseEmbedding } from "../embedding-backend.js";
2
- import type { SparseEmbedding } from "../embedding-types.js";
3
- import type { TieredCandidate } from "./tier-classifier.js";
4
-
5
- /**
6
- * Compute cosine similarity between two sparse vectors.
7
- * Returns 0 if either vector has zero magnitude.
8
- */
9
- function sparseCosine(a: SparseEmbedding, b: SparseEmbedding): number {
10
- // Build index→value map for vector b
11
- const bMap = new Map<number, number>();
12
- for (let i = 0; i < b.indices.length; i++) {
13
- bMap.set(b.indices[i]!, b.values[i]!);
14
- }
15
-
16
- // Compute dot product over shared indices
17
- let dotProduct = 0;
18
- for (let i = 0; i < a.indices.length; i++) {
19
- const bVal = bMap.get(a.indices[i]!);
20
- if (bVal !== undefined) {
21
- dotProduct += a.values[i]! * bVal;
22
- }
23
- }
24
-
25
- // Compute magnitudes
26
- let magA = 0;
27
- for (const v of a.values) magA += v * v;
28
- magA = Math.sqrt(magA);
29
-
30
- let magB = 0;
31
- for (const v of b.values) magB += v * v;
32
- magB = Math.sqrt(magB);
33
-
34
- if (magA === 0 || magB === 0) return 0;
35
- return dotProduct / (magA * magB);
36
- }
37
-
38
- /**
39
- * Apply Maximal Marginal Relevance (MMR) diversity ranking to candidates.
40
- *
41
- * Items are re-ranked using a greedy selection loop that progressively
42
- * penalizes candidates whose text is similar to already-selected ones.
43
- * Non-item candidates (segments, summaries, media) pass through unpenalized
44
- * since they represent different conversation windows.
45
- *
46
- * @param candidates - Scored candidates from upstream ranking
47
- * @param penalty - Float 0..1. 0 = no diversity pressure, 1 = maximum
48
- * @returns Re-ranked candidates with adjusted finalScores
49
- */
50
- export function applyMMR(
51
- candidates: TieredCandidate[],
52
- penalty: number,
53
- ): TieredCandidate[] {
54
- // Separate items from non-items
55
- const items: { index: number; candidate: TieredCandidate }[] = [];
56
- const nonItems: TieredCandidate[] = [];
57
-
58
- for (let i = 0; i < candidates.length; i++) {
59
- const c = candidates[i]!;
60
- if (c.type === "item") {
61
- items.push({ index: i, candidate: c });
62
- } else {
63
- nonItems.push(c);
64
- }
65
- }
66
-
67
- // If no items or no penalty, pass through in original order
68
- if (items.length === 0 || penalty === 0) {
69
- return candidates;
70
- }
71
-
72
- // Pre-compute sparse embeddings only for items (not segments, summaries, media)
73
- const embeddings = new Map<number, SparseEmbedding>();
74
- for (const item of items) {
75
- embeddings.set(item.index, generateSparseEmbedding(item.candidate.text));
76
- }
77
-
78
- // Greedy MMR selection loop
79
- const selected: number[] = [];
80
- const remaining = new Set<number>(items.map((_, i) => i));
81
- const adjustedScores = new Map<number, number>();
82
-
83
- // Select the item with the highest finalScore first
84
- let bestIdx = -1;
85
- let bestScore = -Infinity;
86
- for (const idx of remaining) {
87
- const score = items[idx]!.candidate.finalScore;
88
- if (score > bestScore) {
89
- bestScore = score;
90
- bestIdx = idx;
91
- }
92
- }
93
- selected.push(bestIdx);
94
- remaining.delete(bestIdx);
95
- adjustedScores.set(bestIdx, items[bestIdx]!.candidate.finalScore);
96
-
97
- // Iteratively select remaining items
98
- while (remaining.size > 0) {
99
- let nextBestIdx = -1;
100
- let nextBestScore = -Infinity;
101
-
102
- for (const idx of remaining) {
103
- const itemEmbIdx = items[idx]!.index;
104
-
105
- // Compute max similarity to any already-selected item
106
- let maxSim = 0;
107
- for (const selIdx of selected) {
108
- const selEmbIdx = items[selIdx]!.index;
109
- const sim = sparseCosine(
110
- embeddings.get(itemEmbIdx)!,
111
- embeddings.get(selEmbIdx)!,
112
- );
113
- if (sim > maxSim) maxSim = sim;
114
- }
115
-
116
- const adjustedScore =
117
- items[idx]!.candidate.finalScore * (1 - maxSim * penalty);
118
- if (adjustedScore > nextBestScore) {
119
- nextBestScore = adjustedScore;
120
- nextBestIdx = idx;
121
- }
122
- }
123
-
124
- selected.push(nextBestIdx);
125
- remaining.delete(nextBestIdx);
126
- adjustedScores.set(nextBestIdx, nextBestScore);
127
- }
128
-
129
- // Rebuild output: non-items first (original order), then items in selected order
130
- const result: TieredCandidate[] = [...nonItems];
131
- for (const idx of selected) {
132
- result.push({
133
- ...items[idx]!.candidate,
134
- finalScore: adjustedScores.get(idx)!,
135
- });
136
- }
137
-
138
- return result;
139
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * Logarithmic recency decay (ACT-R inspired).
3
- *
4
- * Old formula `1/(1+ageDays)` decays far too aggressively:
5
- * - 30 days -> 0.032, 1 year -> 0.003
6
- *
7
- * New formula `1/(1+log2(1+ageDays))` preserves long-term recall:
8
- * - 1 day -> 0.50, 7 days -> 0.25, 30 days -> 0.17
9
- * - 90 days -> 0.15, 1 year -> 0.12, 2 years -> 0.10
10
- */
11
- export function computeRecencyScore(createdAt: number): number {
12
- const ageMs = Math.max(0, Date.now() - createdAt);
13
- const ageDays = ageMs / (24 * 60 * 60 * 1000);
14
- return 1 / (1 + Math.log2(1 + ageDays));
15
- }
@@ -1,40 +0,0 @@
1
- import type { StalenessLevel } from "./types.js";
2
-
3
- const BASE_LIFETIME_MS: Record<string, number> = {
4
- identity: 180 * 86_400_000, // 6 months
5
- preference: 90 * 86_400_000, // 3 months
6
- constraint: 30 * 86_400_000, // 1 month
7
- project: 14 * 86_400_000, // 2 weeks
8
- decision: 14 * 86_400_000, // 2 weeks
9
- event: 3 * 86_400_000, // 3 days
10
- // Journals are experiential reflections and forward-looking notes — more
11
- // durable than ephemeral events or decisions, but not as permanent as
12
- // identity. 90 days mirrors "preference" lifetime.
13
- journal: 90 * 86_400_000, // 3 months
14
- capability: Infinity,
15
- };
16
-
17
- const DEFAULT_LIFETIME_MS = 30 * 86_400_000;
18
-
19
- export function computeStaleness(
20
- item: {
21
- kind: string;
22
- firstSeenAt: number;
23
- sourceConversationCount: number;
24
- },
25
- now: number,
26
- ): { level: StalenessLevel; ratio: number } {
27
- const baseLifetime = BASE_LIFETIME_MS[item.kind] ?? DEFAULT_LIFETIME_MS;
28
- const reinforcement = Math.max(
29
- 1,
30
- 1 + 0.3 * (item.sourceConversationCount - 1),
31
- );
32
- const effectiveLifetime = baseLifetime * reinforcement;
33
- const age = now - item.firstSeenAt;
34
- const ratio = age / effectiveLifetime;
35
-
36
- if (ratio < 0.5) return { level: "fresh", ratio };
37
- if (ratio <= 1) return { level: "aging", ratio };
38
- if (ratio <= 2) return { level: "stale", ratio };
39
- return { level: "very_stale", ratio };
40
- }
@@ -1,18 +0,0 @@
1
- import type { Candidate } from "./types.js";
2
-
3
- /** Backward-compatible alias — downstream files import this type. */
4
- export type TieredCandidate = Candidate & {
5
- /** Human-readable label for the source conversation/summary (e.g. conversation title). */
6
- sourceLabel?: string;
7
- };
8
-
9
- const MIN_SCORE_THRESHOLD = 0.2;
10
-
11
- /**
12
- * Filter candidates to those exceeding the minimum relevance threshold.
13
- * Replaces the former tier 1/tier 2 classification — all surviving candidates
14
- * are treated equally and ranked by score.
15
- */
16
- export function filterByMinScore(candidates: Candidate[]): TieredCandidate[] {
17
- return candidates.filter((c) => c.finalScore > MIN_SCORE_THRESHOLD);
18
- }