@vellumai/assistant 0.5.16 → 0.6.1

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 (592) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +69 -16
  3. package/Dockerfile +2 -5
  4. package/bun.lock +6 -2
  5. package/docker-entrypoint.sh +32 -1
  6. package/docs/architecture/integrations.md +1 -1
  7. package/docs/architecture/memory.md +21 -24
  8. package/knip.json +2 -1
  9. package/openapi.yaml +1198 -83
  10. package/package.json +5 -1
  11. package/src/__tests__/actor-token-service.test.ts +68 -0
  12. package/src/__tests__/agent-loop.test.ts +0 -32
  13. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  14. package/src/__tests__/anthropic-provider.test.ts +217 -98
  15. package/src/__tests__/app-compiler.test.ts +120 -0
  16. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  17. package/src/__tests__/app-executors.test.ts +47 -1
  18. package/src/__tests__/app-source-watcher.test.ts +159 -0
  19. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  20. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  21. package/src/__tests__/call-domain.test.ts +2 -6
  22. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  23. package/src/__tests__/call-recovery.test.ts +2 -6
  24. package/src/__tests__/call-routes-http.test.ts +2 -6
  25. package/src/__tests__/call-store.test.ts +2 -6
  26. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  27. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  28. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  29. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  30. package/src/__tests__/checker.test.ts +63 -9
  31. package/src/__tests__/clawhub.test.ts +54 -24
  32. package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
  33. package/src/__tests__/config-schema.test.ts +6 -1
  34. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  35. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  36. package/src/__tests__/contacts-tools.test.ts +31 -0
  37. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  38. package/src/__tests__/context-token-estimator.test.ts +175 -10
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +13 -6
  40. package/src/__tests__/conversation-agent-loop.test.ts +13 -51
  41. package/src/__tests__/conversation-attachments.test.ts +2 -6
  42. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  43. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  44. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  45. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  46. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  47. package/src/__tests__/conversation-error.test.ts +33 -2
  48. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  49. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  50. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
  53. package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
  54. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  55. package/src/__tests__/conversation-store.test.ts +2 -6
  56. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
  57. package/src/__tests__/conversation-usage.test.ts +2 -6
  58. package/src/__tests__/conversation-wipe.test.ts +13 -414
  59. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
  60. package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
  61. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  62. package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
  63. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  64. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  65. package/src/__tests__/credential-security-e2e.test.ts +2 -0
  66. package/src/__tests__/date-context.test.ts +76 -210
  67. package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
  68. package/src/__tests__/file-list-tool.test.ts +219 -0
  69. package/src/__tests__/first-greeting.test.ts +1 -1
  70. package/src/__tests__/followup-tools.test.ts +2 -6
  71. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  72. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  73. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  74. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  75. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  76. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  77. package/src/__tests__/guardian-action-store.test.ts +2 -6
  78. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  79. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  80. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  81. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  82. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  83. package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
  84. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  85. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  86. package/src/__tests__/heartbeat-service.test.ts +180 -3
  87. package/src/__tests__/identity-routes.test.ts +328 -0
  88. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  89. package/src/__tests__/injection-block.test.ts +178 -0
  90. package/src/__tests__/install-meta.test.ts +506 -0
  91. package/src/__tests__/install-skill-routing.test.ts +293 -0
  92. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  93. package/src/__tests__/invite-routes-http.test.ts +2 -6
  94. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +17 -28
  95. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  96. package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
  97. package/src/__tests__/llm-context-normalization.test.ts +18 -18
  98. package/src/__tests__/llm-context-route-provider.test.ts +103 -6
  99. package/src/__tests__/llm-request-log-turn-query.test.ts +164 -6
  100. package/src/__tests__/llm-usage-store.test.ts +2 -6
  101. package/src/__tests__/log-export-workspace.test.ts +74 -111
  102. package/src/__tests__/managed-store.test.ts +38 -11
  103. package/src/__tests__/mcp-abort-signal.test.ts +5 -0
  104. package/src/__tests__/mcp-client-auth.test.ts +5 -0
  105. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  106. package/src/__tests__/memory-recall-log-store.test.ts +134 -6
  107. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  108. package/src/__tests__/migration-export-streaming.test.ts +304 -0
  109. package/src/__tests__/migration-import-commit-http.test.ts +11 -10
  110. package/src/__tests__/mock-fetch.ts +87 -0
  111. package/src/__tests__/non-member-access-request.test.ts +2 -6
  112. package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
  113. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  114. package/src/__tests__/oauth-cli.test.ts +364 -2
  115. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  116. package/src/__tests__/onboarding-template-contract.test.ts +62 -14
  117. package/src/__tests__/outlook-attachments.test.ts +301 -0
  118. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  119. package/src/__tests__/outlook-categories.test.ts +212 -0
  120. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  121. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  122. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  123. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  124. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  125. package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
  126. package/src/__tests__/outlook-trash.test.ts +77 -0
  127. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  128. package/src/__tests__/parser.test.ts +32 -0
  129. package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
  130. package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
  131. package/src/__tests__/permission-mode-sse.test.ts +418 -0
  132. package/src/__tests__/permission-mode-store.test.ts +277 -0
  133. package/src/__tests__/permission-mode.test.ts +101 -0
  134. package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
  135. package/src/__tests__/platform-callback-registration.test.ts +4 -4
  136. package/src/__tests__/playbook-execution.test.ts +76 -80
  137. package/src/__tests__/playbook-tools.test.ts +5 -7
  138. package/src/__tests__/profiler-routes.test.ts +502 -0
  139. package/src/__tests__/profiler-run-store.test.ts +441 -0
  140. package/src/__tests__/provider-error-scenarios.test.ts +21 -0
  141. package/src/__tests__/proxy-approval-callback.test.ts +4 -75
  142. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  143. package/src/__tests__/registry.test.ts +3 -3
  144. package/src/__tests__/require-fresh-approval.test.ts +64 -2
  145. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  146. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  147. package/src/__tests__/sandbox-host-parity.test.ts +5 -4
  148. package/src/__tests__/schedule-store.test.ts +2 -6
  149. package/src/__tests__/schedule-tools.test.ts +2 -6
  150. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  151. package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
  152. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  153. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  154. package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
  155. package/src/__tests__/search-skills-unified.test.ts +422 -0
  156. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  157. package/src/__tests__/send-endpoint-busy.test.ts +44 -9
  158. package/src/__tests__/sequence-store.test.ts +2 -6
  159. package/src/__tests__/server-history-render.test.ts +2 -6
  160. package/src/__tests__/set-permission-mode.test.ts +274 -0
  161. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  162. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  163. package/src/__tests__/skill-load-feature-flag.test.ts +23 -11
  164. package/src/__tests__/skill-memory.test.ts +2 -741
  165. package/src/__tests__/skills-uninstall.test.ts +2 -2
  166. package/src/__tests__/skills.test.ts +1 -1
  167. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  168. package/src/__tests__/strip-memory-injections.test.ts +187 -0
  169. package/src/__tests__/subagent-detail.test.ts +84 -0
  170. package/src/__tests__/subagent-disposal.test.ts +308 -0
  171. package/src/__tests__/subagent-manager-notify.test.ts +19 -10
  172. package/src/__tests__/subagent-notify-parent.test.ts +390 -0
  173. package/src/__tests__/subagent-role-registry.test.ts +108 -0
  174. package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
  175. package/src/__tests__/subagent-tools.test.ts +464 -4
  176. package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
  177. package/src/__tests__/task-compiler.test.ts +2 -6
  178. package/src/__tests__/task-management-tools.test.ts +2 -6
  179. package/src/__tests__/task-memory-cleanup.test.ts +185 -241
  180. package/src/__tests__/task-runner.test.ts +2 -6
  181. package/src/__tests__/task-scheduler.test.ts +2 -6
  182. package/src/__tests__/terminal-tools.test.ts +17 -27
  183. package/src/__tests__/test-preload.ts +7 -0
  184. package/src/__tests__/tool-approval-handler.test.ts +2 -6
  185. package/src/__tests__/tool-executor.test.ts +4 -26
  186. package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
  187. package/src/__tests__/tool-side-effects-slack-dm.test.ts +277 -0
  188. package/src/__tests__/top-level-renderer.test.ts +10 -13
  189. package/src/__tests__/trust-store.test.ts +1 -1
  190. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
  191. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +118 -8
  192. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
  193. package/src/__tests__/trusted-contact-verification.test.ts +2 -6
  194. package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
  195. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
  196. package/src/__tests__/usage-routes.test.ts +2 -6
  197. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  198. package/src/__tests__/voice-invite-redemption.test.ts +2 -6
  199. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
  200. package/src/__tests__/voice-session-bridge.test.ts +2 -6
  201. package/src/__tests__/volume-security-guard.test.ts +2 -0
  202. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  203. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
  204. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
  205. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  206. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
  207. package/src/__tests__/workspace-policy.test.ts +1 -1
  208. package/src/agent/attachments.ts +7 -2
  209. package/src/agent/image-optimize.ts +165 -0
  210. package/src/agent/loop.ts +7 -15
  211. package/src/approvals/guardian-request-resolvers.ts +24 -0
  212. package/src/avatar/traits-png-sync.ts +3 -3
  213. package/src/bundler/app-compiler.ts +179 -2
  214. package/src/bundler/package-resolver.ts +3 -5
  215. package/src/cli/__tests__/notifications.test.ts +1 -2
  216. package/src/cli/__tests__/run-assistant-command.ts +29 -0
  217. package/src/cli/commands/__tests__/email-download.test.ts +245 -0
  218. package/src/cli/commands/__tests__/email-list.test.ts +192 -0
  219. package/src/cli/commands/__tests__/email-register.test.ts +186 -0
  220. package/src/cli/commands/__tests__/email-send.test.ts +291 -0
  221. package/src/cli/commands/__tests__/email-status.test.ts +181 -0
  222. package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
  223. package/src/cli/commands/__tests__/routes.test.ts +562 -0
  224. package/src/cli/commands/avatar.ts +3 -3
  225. package/src/cli/commands/config.ts +26 -13
  226. package/src/cli/commands/conversations.ts +1 -8
  227. package/src/cli/commands/doctor.ts +2 -2
  228. package/src/cli/commands/email.ts +584 -835
  229. package/src/cli/commands/memory.ts +37 -84
  230. package/src/cli/commands/notifications.ts +7 -2
  231. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  232. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  233. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  234. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  235. package/src/cli/commands/oauth/connect.ts +25 -11
  236. package/src/cli/commands/oauth/mode.ts +7 -0
  237. package/src/cli/commands/oauth/shared.ts +39 -3
  238. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  239. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  240. package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
  241. package/src/cli/commands/platform/index.ts +16 -16
  242. package/src/cli/commands/routes.ts +396 -0
  243. package/src/cli/commands/skills.ts +218 -36
  244. package/src/cli/commands/trust.ts +2 -2
  245. package/src/cli/lib/daemon-credential-client.ts +2 -3
  246. package/src/cli/program.ts +2 -0
  247. package/src/cli.ts +1 -120
  248. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  249. package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
  250. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  251. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  252. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  253. package/src/config/bundled-skills/gmail/SKILL.md +4 -12
  254. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  255. package/src/config/bundled-skills/messaging/SKILL.md +17 -18
  256. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  257. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  258. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  259. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  260. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  261. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  262. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  263. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  264. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  265. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  266. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  267. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  268. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  269. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  270. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  271. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  272. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  273. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  274. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  275. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  276. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  277. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  278. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  279. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  280. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  281. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  282. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  283. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  284. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  285. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  286. package/src/config/bundled-skills/schedule/SKILL.md +22 -2
  287. package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
  288. package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
  289. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
  290. package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
  291. package/src/config/bundled-skills/slack/SKILL.md +3 -7
  292. package/src/config/bundled-skills/subagent/SKILL.md +43 -3
  293. package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
  294. package/src/config/bundled-tool-registry.ts +56 -4
  295. package/src/config/env-registry.ts +78 -8
  296. package/src/config/feature-flag-registry.json +38 -125
  297. package/src/config/schema.ts +8 -0
  298. package/src/config/schemas/filing.ts +51 -0
  299. package/src/config/schemas/heartbeat.ts +15 -12
  300. package/src/config/schemas/memory-lifecycle.ts +12 -0
  301. package/src/config/schemas/platform.ts +8 -0
  302. package/src/config/schemas/security.ts +14 -0
  303. package/src/config/schemas/timeouts.ts +1 -1
  304. package/src/config/skills.ts +18 -7
  305. package/src/context/token-estimator.ts +25 -18
  306. package/src/context/window-manager.ts +6 -2
  307. package/src/credential-execution/process-manager.ts +3 -1
  308. package/src/daemon/app-source-watcher.ts +93 -0
  309. package/src/daemon/config-watcher.ts +79 -1
  310. package/src/daemon/context-overflow-reducer.ts +46 -2
  311. package/src/daemon/conversation-agent-loop-handlers.ts +143 -82
  312. package/src/daemon/conversation-agent-loop.ts +236 -108
  313. package/src/daemon/conversation-error.ts +31 -8
  314. package/src/daemon/conversation-history.ts +4 -19
  315. package/src/daemon/conversation-lifecycle.ts +36 -9
  316. package/src/daemon/conversation-media-retry.ts +85 -7
  317. package/src/daemon/conversation-notifiers.ts +4 -1
  318. package/src/daemon/conversation-process.ts +13 -7
  319. package/src/daemon/conversation-runtime-assembly.ts +305 -306
  320. package/src/daemon/conversation-tool-setup.ts +44 -14
  321. package/src/daemon/conversation-workspace.ts +1 -2
  322. package/src/daemon/conversation.ts +59 -2
  323. package/src/daemon/daemon-control.ts +8 -2
  324. package/src/daemon/date-context.ts +26 -53
  325. package/src/daemon/first-greeting.ts +1 -1
  326. package/src/daemon/handlers/conversations.ts +4 -7
  327. package/src/daemon/handlers/shared.test.ts +143 -0
  328. package/src/daemon/handlers/shared.ts +85 -17
  329. package/src/daemon/handlers/skills.ts +416 -209
  330. package/src/daemon/lifecycle.ts +212 -131
  331. package/src/daemon/main.ts +5 -1
  332. package/src/daemon/message-types/conversations.ts +29 -7
  333. package/src/daemon/message-types/messages.ts +12 -2
  334. package/src/daemon/message-types/schedules.ts +1 -0
  335. package/src/daemon/message-types/settings.ts +6 -0
  336. package/src/daemon/message-types/skills.ts +97 -36
  337. package/src/daemon/profiler-run-store.ts +557 -0
  338. package/src/daemon/providers-setup.ts +5 -0
  339. package/src/daemon/server.ts +100 -11
  340. package/src/daemon/shutdown-handlers.ts +5 -0
  341. package/src/daemon/tool-side-effects.ts +50 -8
  342. package/src/export/transcript-formatter.ts +148 -0
  343. package/src/filing/filing-service.ts +228 -0
  344. package/src/heartbeat/heartbeat-service.ts +97 -7
  345. package/src/hooks/cli.ts +2 -2
  346. package/src/hooks/runner.ts +15 -38
  347. package/src/inbound/platform-callback-registration.ts +14 -14
  348. package/src/mcp/client.ts +6 -0
  349. package/src/mcp/mcp-oauth-provider.ts +149 -27
  350. package/src/memory/admin.ts +42 -75
  351. package/src/memory/app-store.ts +69 -0
  352. package/src/memory/conversation-bootstrap.ts +3 -1
  353. package/src/memory/conversation-crud.ts +211 -288
  354. package/src/memory/conversation-group-migration.ts +157 -0
  355. package/src/memory/conversation-queries.ts +61 -13
  356. package/src/memory/conversation-title-service.ts +1 -0
  357. package/src/memory/db-init.ts +194 -361
  358. package/src/memory/embed.ts +73 -0
  359. package/src/memory/embedding-backend.ts +8 -14
  360. package/src/memory/embedding-runtime-manager.ts +12 -114
  361. package/src/memory/fingerprint.ts +2 -2
  362. package/src/memory/graph/bootstrap.ts +521 -0
  363. package/src/memory/graph/capability-seed.ts +449 -0
  364. package/src/memory/graph/consolidation.ts +725 -0
  365. package/src/memory/graph/conversation-graph-memory.ts +659 -0
  366. package/src/memory/graph/decay.test.ts +208 -0
  367. package/src/memory/graph/decay.ts +195 -0
  368. package/src/memory/graph/extraction-job.ts +74 -0
  369. package/src/memory/graph/extraction.test.ts +936 -0
  370. package/src/memory/graph/extraction.ts +1297 -0
  371. package/src/memory/graph/graph-memory-state-store.ts +37 -0
  372. package/src/memory/graph/graph-search.ts +280 -0
  373. package/src/memory/graph/image-ref-utils.ts +29 -0
  374. package/src/memory/graph/injection.test.ts +513 -0
  375. package/src/memory/graph/injection.ts +469 -0
  376. package/src/memory/graph/inspect.ts +543 -0
  377. package/src/memory/graph/narrative.ts +267 -0
  378. package/src/memory/graph/pattern-scan.ts +269 -0
  379. package/src/memory/graph/retriever.ts +1111 -0
  380. package/src/memory/graph/scoring.test.ts +548 -0
  381. package/src/memory/graph/scoring.ts +232 -0
  382. package/src/memory/graph/serendipity.ts +65 -0
  383. package/src/memory/graph/store.test.ts +1098 -0
  384. package/src/memory/graph/store.ts +838 -0
  385. package/src/memory/graph/tool-handlers.ts +301 -0
  386. package/src/memory/graph/tools.ts +97 -0
  387. package/src/memory/graph/triggers.test.ts +487 -0
  388. package/src/memory/graph/triggers.ts +223 -0
  389. package/src/memory/graph/types.ts +295 -0
  390. package/src/memory/group-crud.ts +191 -0
  391. package/src/memory/indexer.ts +37 -19
  392. package/src/memory/job-handlers/cleanup.ts +32 -42
  393. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  394. package/src/memory/job-handlers/embedding.ts +5 -31
  395. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  396. package/src/memory/job-handlers/summarization.ts +32 -17
  397. package/src/memory/job-utils.ts +1 -1
  398. package/src/memory/jobs-store.ts +21 -31
  399. package/src/memory/jobs-worker.ts +180 -129
  400. package/src/memory/llm-request-log-store.ts +96 -12
  401. package/src/memory/memory-recall-log-store.ts +49 -5
  402. package/src/memory/message-content.ts +1 -0
  403. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  404. package/src/memory/migrations/203-drop-memory-items-tables.ts +55 -0
  405. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  406. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  407. package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
  408. package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
  409. package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
  410. package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
  411. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
  412. package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
  413. package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
  414. package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
  415. package/src/memory/migrations/index.ts +12 -0
  416. package/src/memory/migrations/registry.ts +16 -0
  417. package/src/memory/qdrant-client.ts +44 -17
  418. package/src/memory/schema/conversations.ts +14 -0
  419. package/src/memory/schema/index.ts +1 -0
  420. package/src/memory/schema/infrastructure.ts +8 -1
  421. package/src/memory/schema/memory-core.ts +0 -51
  422. package/src/memory/schema/memory-graph.ts +154 -0
  423. package/src/memory/search/semantic.ts +47 -91
  424. package/src/memory/task-memory-cleanup.ts +58 -61
  425. package/src/messaging/providers/outlook/adapter.ts +8 -1
  426. package/src/messaging/providers/outlook/client.ts +299 -0
  427. package/src/messaging/providers/outlook/types.ts +118 -0
  428. package/src/notifications/adapters/macos.ts +1 -0
  429. package/src/notifications/copy-composer.ts +95 -0
  430. package/src/notifications/decision-engine.ts +35 -0
  431. package/src/notifications/signal.ts +16 -0
  432. package/src/oauth/seed-providers.ts +2 -1
  433. package/src/permissions/checker.ts +36 -4
  434. package/src/permissions/defaults.ts +4 -4
  435. package/src/permissions/permission-mode-store.ts +180 -0
  436. package/src/permissions/permission-mode.ts +31 -0
  437. package/src/permissions/workspace-policy.ts +10 -1
  438. package/src/playbooks/playbook-compiler.ts +19 -18
  439. package/src/playbooks/types.ts +4 -3
  440. package/src/prompts/system-prompt.ts +62 -36
  441. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
  442. package/src/prompts/templates/BOOTSTRAP.md +70 -165
  443. package/src/prompts/templates/HEARTBEAT.md +3 -1
  444. package/src/prompts/templates/SOUL.md +25 -4
  445. package/src/prompts/templates/UPDATES.md +8 -0
  446. package/src/providers/anthropic/client.ts +136 -220
  447. package/src/providers/gemini/client.ts +1 -1
  448. package/src/providers/openai/client.ts +1 -1
  449. package/src/providers/registry.ts +1 -1
  450. package/src/providers/retry.ts +19 -3
  451. package/src/runtime/actor-trust-resolver.ts +5 -1
  452. package/src/runtime/auth/route-policy.ts +30 -0
  453. package/src/runtime/guardian-reply-router.ts +5 -1
  454. package/src/runtime/http-server.ts +55 -5
  455. package/src/runtime/http-types.ts +12 -1
  456. package/src/runtime/middleware/auth.ts +20 -0
  457. package/src/runtime/migrations/vbundle-builder.ts +389 -3
  458. package/src/runtime/migrations/vbundle-importer.ts +8 -6
  459. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
  460. package/src/runtime/routes/app-management-routes.ts +1 -11
  461. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
  462. package/src/runtime/routes/archive-utils.ts +29 -0
  463. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  464. package/src/runtime/routes/attachment-routes.ts +106 -16
  465. package/src/runtime/routes/avatar-routes.ts +2 -9
  466. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  467. package/src/runtime/routes/btw-routes.ts +22 -1
  468. package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
  469. package/src/runtime/routes/conversation-management-routes.ts +3 -14
  470. package/src/runtime/routes/conversation-query-routes.ts +49 -3
  471. package/src/runtime/routes/conversation-routes.ts +264 -44
  472. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  473. package/src/runtime/routes/debug-routes.ts +1 -1
  474. package/src/runtime/routes/global-search-routes.ts +21 -19
  475. package/src/runtime/routes/group-routes.ts +207 -0
  476. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  477. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  478. package/src/runtime/routes/heartbeat-routes.ts +4 -10
  479. package/src/runtime/routes/identity-routes.ts +53 -18
  480. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  481. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  482. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  483. package/src/runtime/routes/llm-context-normalization.ts +14 -10
  484. package/src/runtime/routes/log-export-routes.ts +23 -275
  485. package/src/runtime/routes/memory-item-routes.test.ts +170 -247
  486. package/src/runtime/routes/memory-item-routes.ts +341 -388
  487. package/src/runtime/routes/migration-routes.ts +18 -7
  488. package/src/runtime/routes/profiler-routes.ts +350 -0
  489. package/src/runtime/routes/schedule-routes.ts +28 -11
  490. package/src/runtime/routes/settings-routes.ts +95 -8
  491. package/src/runtime/routes/skills-routes.ts +103 -37
  492. package/src/runtime/routes/subagents-routes.ts +28 -7
  493. package/src/runtime/routes/user-route-dispatcher.ts +223 -0
  494. package/src/runtime/routes/user-routes.ts +41 -0
  495. package/src/runtime/routes/work-items-routes.test.ts +2 -6
  496. package/src/runtime/routes/workspace-routes.ts +0 -1
  497. package/src/schedule/schedule-store.ts +30 -0
  498. package/src/schedule/scheduler.ts +52 -18
  499. package/src/security/oauth2.ts +1 -1
  500. package/src/security/secure-keys.ts +4 -8
  501. package/src/shared/provider-env-vars.ts +19 -0
  502. package/src/skills/catalog-cache.ts +5 -0
  503. package/src/skills/catalog-install.ts +25 -16
  504. package/src/skills/clawhub.ts +134 -154
  505. package/src/skills/install-meta.ts +208 -0
  506. package/src/skills/managed-store.ts +29 -18
  507. package/src/skills/skill-memory.ts +12 -229
  508. package/src/skills/skillssh-registry.ts +19 -17
  509. package/src/subagent/index.ts +13 -3
  510. package/src/subagent/manager.ts +308 -29
  511. package/src/subagent/types.ts +68 -0
  512. package/src/tasks/task-runner.ts +7 -5
  513. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  514. package/src/tools/apps/executors.ts +29 -4
  515. package/src/tools/browser/runtime-check.ts +3 -1
  516. package/src/tools/filesystem/list.ts +93 -0
  517. package/src/tools/memory/register.ts +63 -46
  518. package/src/tools/permission-checker.ts +85 -1
  519. package/src/tools/registry.ts +4 -0
  520. package/src/tools/schedule/create.ts +3 -0
  521. package/src/tools/schedule/list.ts +1 -0
  522. package/src/tools/schedule/update.ts +6 -0
  523. package/src/tools/shared/filesystem/errors.ts +5 -0
  524. package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
  525. package/src/tools/shared/filesystem/image-read.ts +22 -85
  526. package/src/tools/shared/filesystem/types.ts +17 -0
  527. package/src/tools/shared/shell-output.ts +31 -2
  528. package/src/tools/subagent/abort.ts +12 -2
  529. package/src/tools/subagent/message.ts +9 -2
  530. package/src/tools/subagent/notify-parent.ts +79 -0
  531. package/src/tools/subagent/read.ts +29 -8
  532. package/src/tools/subagent/resolve.ts +21 -0
  533. package/src/tools/subagent/spawn.ts +2 -0
  534. package/src/tools/subagent/status.ts +11 -1
  535. package/src/tools/system/avatar-generator.ts +3 -3
  536. package/src/tools/system/register.ts +23 -0
  537. package/src/tools/system/set-permission-mode.ts +103 -0
  538. package/src/tools/terminal/parser.ts +30 -5
  539. package/src/tools/terminal/safe-env.ts +17 -1
  540. package/src/tools/tool-manifest.ts +9 -3
  541. package/src/tools/types.ts +2 -0
  542. package/src/util/browser.ts +25 -10
  543. package/src/util/bun-runtime.ts +172 -0
  544. package/src/util/logger.ts +1 -1
  545. package/src/util/platform.ts +50 -17
  546. package/src/watcher/providers/outlook-calendar.ts +343 -0
  547. package/src/watcher/providers/outlook.ts +198 -0
  548. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
  549. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
  550. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  551. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  552. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  553. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
  554. package/src/workspace/migrations/029-seed-pkb.ts +84 -0
  555. package/src/workspace/migrations/registry.ts +10 -0
  556. package/src/workspace/top-level-renderer.ts +5 -9
  557. package/src/__tests__/cli-memory.test.ts +0 -372
  558. package/src/__tests__/clipboard.test.ts +0 -88
  559. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  560. package/src/__tests__/journal-context.test.ts +0 -268
  561. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  562. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  563. package/src/__tests__/memory-query-builder.test.ts +0 -59
  564. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  565. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  566. package/src/__tests__/memory-regressions.test.ts +0 -3696
  567. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  568. package/src/cli/cli-memory.ts +0 -176
  569. package/src/daemon/conversation-memory.ts +0 -207
  570. package/src/memory/conversation-starters-cadence.ts +0 -74
  571. package/src/memory/items-extractor.ts +0 -860
  572. package/src/memory/job-handlers/batch-extraction.ts +0 -753
  573. package/src/memory/job-handlers/extraction.ts +0 -40
  574. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
  575. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  576. package/src/memory/journal-memory.ts +0 -224
  577. package/src/memory/query-builder.ts +0 -47
  578. package/src/memory/query-expansion.ts +0 -83
  579. package/src/memory/retriever.test.ts +0 -1592
  580. package/src/memory/retriever.ts +0 -1331
  581. package/src/memory/search/formatting.test.ts +0 -140
  582. package/src/memory/search/formatting.ts +0 -262
  583. package/src/memory/search/mmr.ts +0 -139
  584. package/src/memory/search/ranking.ts +0 -15
  585. package/src/memory/search/staleness.ts +0 -40
  586. package/src/memory/search/tier-classifier.ts +0 -18
  587. package/src/memory/search/types.ts +0 -121
  588. package/src/prompts/journal-context.ts +0 -154
  589. package/src/tools/memory/definitions.ts +0 -69
  590. package/src/tools/memory/handlers.test.ts +0 -562
  591. package/src/tools/memory/handlers.ts +0 -434
  592. package/src/util/clipboard.ts +0 -34
@@ -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
- }