@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,29 +1,36 @@
1
1
  import { getConfig } from "../config/loader.js";
2
2
  import type { AssistantConfig } from "../config/types.js";
3
3
  import { getLogger } from "../util/logger.js";
4
- import { rawAll, rawRun } from "./db.js";
4
+ import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
5
+ import { bootstrapFromHistory } from "./graph/bootstrap.js";
6
+ import { runConsolidation } from "./graph/consolidation.js";
7
+ import { runDecayTick } from "./graph/decay.js";
8
+ import { graphExtractJob } from "./graph/extraction-job.js";
9
+ import {
10
+ embedGraphNodeJob,
11
+ embedGraphTriggerJob,
12
+ } from "./graph/graph-search.js";
13
+ import { runNarrativeRefinement } from "./graph/narrative.js";
14
+ import { runPatternScan } from "./graph/pattern-scan.js";
5
15
  import { backfillJob } from "./job-handlers/backfill.js";
6
- import { batchExtractJob } from "./job-handlers/batch-extraction.js";
7
16
  import {
8
- cleanupStaleSupersededItemsJob,
9
17
  pruneOldConversationsJob,
18
+ pruneOldLlmRequestLogsJob,
10
19
  } from "./job-handlers/cleanup.js";
11
20
  import { generateConversationStartersJob } from "./job-handlers/conversation-starters.js";
12
21
  // ── Per-job-type handlers ──────────────────────────────────────────
13
22
  import {
14
23
  embedAttachmentJob,
15
- embedItemJob,
16
24
  embedMediaJob,
17
25
  embedSegmentJob,
18
26
  embedSummaryJob,
19
27
  } from "./job-handlers/embedding.js";
20
- import { extractItemsJob } from "./job-handlers/extraction.js";
21
28
  import {
22
29
  deleteQdrantVectorsJob,
23
30
  rebuildIndexJob,
24
31
  } from "./job-handlers/index-maintenance.js";
25
- import { journalCarryForwardJob } from "./job-handlers/journal-carry-forward.js";
26
32
  import { mediaProcessingJob } from "./job-handlers/media-processing.js";
33
+ import { buildConversationSummaryJob } from "./job-handlers/summarization.js";
27
34
  import {
28
35
  BackendUnavailableError,
29
36
  classifyError,
@@ -34,18 +41,37 @@ import {
34
41
  claimMemoryJobs,
35
42
  completeMemoryJob,
36
43
  deferMemoryJob,
37
- enqueueCleanupStaleSupersededItemsJob,
38
44
  enqueueMemoryJob,
39
45
  enqueuePruneOldConversationsJob,
46
+ enqueuePruneOldLlmRequestLogsJob,
40
47
  failMemoryJob,
41
48
  failStalledJobs,
42
49
  type MemoryJob,
50
+ type MemoryJobType,
43
51
  resetRunningJobsToPending,
44
52
  } from "./jobs-store.js";
45
53
  import { QdrantCircuitOpenError } from "./qdrant-circuit-breaker.js";
46
54
 
47
55
  const log = getLogger("memory-jobs-worker");
48
56
 
57
+ /**
58
+ * Job types whose handlers have been removed. Existing rows may still sit in
59
+ * the database — the worker completes them silently instead of throwing.
60
+ */
61
+ const LEGACY_JOB_TYPES = new Set([
62
+ "embed_item",
63
+ "extract_items",
64
+ "batch_extract",
65
+ "extract_entities",
66
+ "cleanup_stale_superseded_items",
67
+ "backfill_entity_relations",
68
+ "refresh_weekly_summary",
69
+ "refresh_monthly_summary",
70
+ "journal_carry_forward",
71
+ "generate_capability_cards",
72
+ "generate_thread_starters",
73
+ ]);
74
+
49
75
  export const POLL_INTERVAL_MIN_MS = 1_500;
50
76
  export const POLL_INTERVAL_MAX_MS = 30_000;
51
77
 
@@ -60,31 +86,6 @@ export function startMemoryJobsWorker(): MemoryJobsWorker {
60
86
  log.info({ recovered }, "Recovered stale running memory jobs");
61
87
  }
62
88
 
63
- // Startup recovery: enqueue batch_extract for conversations with pending
64
- // unextracted messages (e.g. after a crash mid-conversation).
65
- try {
66
- const pendingRows = rawAll<{ key: string; value: string }>(
67
- `SELECT key, value FROM memory_checkpoints WHERE key LIKE 'batch_extract:%:pending_count' AND CAST(value AS INTEGER) > 0`,
68
- );
69
- for (const row of pendingRows) {
70
- // Extract conversationId from key: "batch_extract:<conversationId>:pending_count"
71
- const parts = row.key.split(":");
72
- if (parts.length >= 3) {
73
- const conversationId = parts.slice(1, -1).join(":");
74
- enqueueMemoryJob("batch_extract", { conversationId });
75
- log.info(
76
- { conversationId, pendingCount: row.value },
77
- "Recovered pending batch extraction on startup",
78
- );
79
- }
80
- }
81
- } catch (err) {
82
- log.warn(
83
- { err: err instanceof Error ? err.message : String(err) },
84
- "Failed to recover pending batch extractions on startup",
85
- );
86
- }
87
-
88
89
  let stopped = false;
89
90
  let tickRunning = false;
90
91
  let timer: ReturnType<typeof setTimeout>;
@@ -145,9 +146,6 @@ export async function runMemoryJobsOnce(
145
146
  if (!config.memory.enabled) return 0;
146
147
  const enableScheduledCleanup = options.enableScheduledCleanup === true;
147
148
 
148
- // Periodic stale item sweep (throttled to at most once per hour)
149
- sweepStaleItems(config);
150
-
151
149
  // Fail jobs that have been running longer than the configured timeout
152
150
  const timedOut = failStalledJobs(config.memory.jobs.stalledJobTimeoutMs);
153
151
  if (timedOut > 0) {
@@ -161,6 +159,7 @@ export async function runMemoryJobsOnce(
161
159
  if (enableScheduledCleanup) {
162
160
  maybeEnqueueScheduledCleanupJobs(config);
163
161
  }
162
+ maybeEnqueueGraphMaintenanceJobs();
164
163
  return 0;
165
164
  }
166
165
 
@@ -256,9 +255,67 @@ export async function runMemoryJobsOnce(
256
255
  if (enableScheduledCleanup) {
257
256
  maybeEnqueueScheduledCleanupJobs(config);
258
257
  }
258
+ maybeEnqueueGraphMaintenanceJobs();
259
259
  return processed;
260
260
  }
261
261
 
262
+ // ── Graph lifecycle job handlers ──────────────────────────────────
263
+
264
+ function graphDecayJob(job: MemoryJob): void {
265
+ const scopeId = (job.payload as { scopeId?: string })?.scopeId ?? "default";
266
+ const result = runDecayTick(scopeId);
267
+ log.info({ jobId: job.id, ...result }, "Graph decay tick complete");
268
+ }
269
+
270
+ async function graphConsolidateJob(
271
+ job: MemoryJob,
272
+ config: AssistantConfig,
273
+ ): Promise<void> {
274
+ const scopeId = (job.payload as { scopeId?: string })?.scopeId ?? "default";
275
+ const result = await runConsolidation(scopeId, config);
276
+ log.info(
277
+ {
278
+ jobId: job.id,
279
+ updated: result.totalUpdated,
280
+ deleted: result.totalDeleted,
281
+ mergeEdges: result.totalMergeEdges,
282
+ },
283
+ "Graph consolidation complete",
284
+ );
285
+ }
286
+
287
+ async function graphPatternScanJob(
288
+ job: MemoryJob,
289
+ config: AssistantConfig,
290
+ ): Promise<void> {
291
+ const scopeId = (job.payload as { scopeId?: string })?.scopeId ?? "default";
292
+ const result = await runPatternScan(scopeId, config);
293
+ log.info(
294
+ {
295
+ jobId: job.id,
296
+ patterns: result.patternsDetected,
297
+ edges: result.edgesCreated,
298
+ },
299
+ "Graph pattern scan complete",
300
+ );
301
+ }
302
+
303
+ async function graphNarrativeRefineJob(
304
+ job: MemoryJob,
305
+ config: AssistantConfig,
306
+ ): Promise<void> {
307
+ const scopeId = (job.payload as { scopeId?: string })?.scopeId ?? "default";
308
+ const result = await runNarrativeRefinement(scopeId, config);
309
+ log.info(
310
+ {
311
+ jobId: job.id,
312
+ updated: result.nodesUpdated,
313
+ arcs: result.arcsIdentified,
314
+ },
315
+ "Graph narrative refinement complete",
316
+ );
317
+ }
318
+
262
319
  // ── Job error handling ─────────────────────────────────────────────
263
320
 
264
321
  function handleJobError(job: MemoryJob, err: unknown): void {
@@ -321,45 +378,21 @@ async function processJob(
321
378
  case "embed_segment":
322
379
  await embedSegmentJob(job, config);
323
380
  return;
324
- case "embed_item":
325
- await embedItemJob(job, config);
326
- return;
327
381
  case "embed_summary":
328
382
  await embedSummaryJob(job, config);
329
383
  return;
330
- case "extract_items":
331
- await extractItemsJob(job);
332
- return;
333
- case "batch_extract":
334
- await batchExtractJob(job);
335
- return;
336
- case "extract_entities":
337
- // Entity extraction has been removed — silently drop legacy jobs
338
- return;
339
- case "cleanup_stale_superseded_items":
340
- cleanupStaleSupersededItemsJob(job, config);
341
- return;
342
384
  case "prune_old_conversations":
343
385
  pruneOldConversationsJob(job, config);
344
386
  return;
387
+ case "prune_old_llm_request_logs":
388
+ pruneOldLlmRequestLogsJob(job, config);
389
+ return;
345
390
  case "build_conversation_summary":
346
- // Deprecated: conversation summaries are now produced as a side-effect
347
- // of batch extraction. Silently skip legacy jobs.
348
- log.debug(
349
- { jobId: job.id },
350
- "Skipping deprecated build_conversation_summary job — handled by batch extraction",
351
- );
391
+ await buildConversationSummaryJob(job, config);
352
392
  return;
353
393
  case "backfill":
354
394
  await backfillJob(job, config);
355
395
  return;
356
- case "backfill_entity_relations":
357
- // Entity relation backfill has been removed — silently drop legacy jobs
358
- return;
359
- case "refresh_weekly_summary":
360
- case "refresh_monthly_summary":
361
- // Global summary rollups have been removed — silently drop legacy jobs
362
- return;
363
396
  case "rebuild_index":
364
397
  await rebuildIndexJob();
365
398
  return;
@@ -375,22 +408,42 @@ async function processJob(
375
408
  case "embed_attachment":
376
409
  await embedAttachmentJob(job, config);
377
410
  return;
378
- case "journal_carry_forward":
379
- await journalCarryForwardJob(job);
411
+ case "embed_graph_node":
412
+ await embedGraphNodeJob(job, config);
413
+ return;
414
+ case "graph_trigger_embed":
415
+ await embedGraphTriggerJob(job, config);
416
+ return;
417
+ case "graph_extract":
418
+ await graphExtractJob(job, config);
419
+ return;
420
+ case "graph_decay":
421
+ graphDecayJob(job);
422
+ return;
423
+ case "graph_consolidate":
424
+ await graphConsolidateJob(job, config);
425
+ return;
426
+ case "graph_pattern_scan":
427
+ await graphPatternScanJob(job, config);
428
+ return;
429
+ case "graph_narrative_refine":
430
+ await graphNarrativeRefineJob(job, config);
380
431
  return;
381
432
  case "generate_conversation_starters":
382
433
  await generateConversationStartersJob(job);
383
434
  return;
384
- case "generate_capability_cards":
385
- // Capability cards were removed — silently drop legacy jobs.
386
- return;
387
- case "generate_thread_starters":
388
- // Thread starters renamed to conversation starters — silently drop legacy jobs
435
+ case "graph_bootstrap":
436
+ await bootstrapFromHistory();
389
437
  return;
390
- default:
391
- throw new Error(
392
- `Unknown memory job type: ${(job as { type: string }).type}`,
393
- );
438
+
439
+ default: {
440
+ const rawType = (job as { type: string }).type;
441
+ if (LEGACY_JOB_TYPES.has(rawType)) {
442
+ log.debug({ jobId: job.id, type: rawType }, "Dropping legacy job");
443
+ return;
444
+ }
445
+ throw new Error(`Unknown memory job type: ${rawType}`);
446
+ }
394
447
  }
395
448
  }
396
449
 
@@ -416,82 +469,80 @@ export function maybeEnqueueScheduledCleanupJobs(
416
469
  if (nowMs - lastScheduledCleanupEnqueueMs < cleanup.enqueueIntervalMs)
417
470
  return false;
418
471
 
419
- const staleSupersededItemsJobId = enqueueCleanupStaleSupersededItemsJob(
420
- cleanup.supersededItemRetentionMs,
421
- );
422
472
  const pruneConversationsJobId =
423
473
  cleanup.conversationRetentionDays > 0
424
474
  ? enqueuePruneOldConversationsJob(cleanup.conversationRetentionDays)
425
475
  : null;
476
+ const pruneLlmRequestLogsJobId =
477
+ cleanup.llmRequestLogRetentionMs > 0
478
+ ? enqueuePruneOldLlmRequestLogsJob(cleanup.llmRequestLogRetentionMs)
479
+ : null;
426
480
  lastScheduledCleanupEnqueueMs = nowMs;
427
481
  log.debug(
428
482
  {
429
- staleSupersededItemsJobId,
430
483
  pruneConversationsJobId,
484
+ pruneLlmRequestLogsJobId,
431
485
  enqueueIntervalMs: cleanup.enqueueIntervalMs,
432
- supersededItemRetentionMs: cleanup.supersededItemRetentionMs,
433
486
  conversationRetentionDays: cleanup.conversationRetentionDays,
487
+ llmRequestLogRetentionMs: cleanup.llmRequestLogRetentionMs,
434
488
  },
435
489
  "Enqueued scheduled memory cleanup jobs",
436
490
  );
437
491
  return true;
438
492
  }
439
493
 
440
- // ── Stale item sweep ───────────────────────────────────────────────
494
+ // ── Graph maintenance scheduling ──────────────────────────────────
441
495
 
442
- const STALE_SWEEP_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
443
- let lastStaleSweepMs = 0;
496
+ const GRAPH_DECAY_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
497
+ const GRAPH_CONSOLIDATE_INTERVAL_MS = 4 * 60 * 60 * 1000; // 4 hours
498
+ const GRAPH_PATTERN_SCAN_INTERVAL_MS = 24 * 60 * 60 * 1000; // 1 day
499
+ const GRAPH_NARRATIVE_INTERVAL_MS = 7 * 24 * 60 * 60 * 1000; // 1 week
444
500
 
445
- /** Reset the sweep throttle so tests can call sweepStaleItems back-to-back. */
446
- export function resetStaleSweepThrottle(): void {
447
- lastStaleSweepMs = 0;
448
- }
501
+ const GRAPH_MAINTENANCE_CHECKPOINTS = {
502
+ decay: "graph_maintenance:decay:last_run",
503
+ consolidate: "graph_maintenance:consolidate:last_run",
504
+ patternScan: "graph_maintenance:pattern_scan:last_run",
505
+ narrative: "graph_maintenance:narrative:last_run",
506
+ } as const;
449
507
 
450
508
  /**
451
- * Mark deeply stale memory items as invalid. An item is considered deeply
452
- * stale when it has exceeded 2x its freshness window for its kind and has
453
- * not been recently accessed.
454
- *
455
- * This is non-destructive: items keep their data but get an `invalid_at`
456
- * timestamp that excludes them from retrieval queries.
509
+ * Enqueue periodic graph maintenance jobs (decay, consolidation, pattern scan, narrative).
510
+ * Uses durable checkpoints so intervals survive daemon restarts jobs only fire
511
+ * when the actual elapsed time since last run exceeds the interval.
457
512
  */
458
- export function sweepStaleItems(config: AssistantConfig): number {
459
- const freshness = config.memory.retrieval.freshness;
460
- if (!freshness.enabled) return 0;
461
-
462
- const now = Date.now();
463
- // Throttle: at most once per hour
464
- if (now - lastStaleSweepMs < STALE_SWEEP_INTERVAL_MS) return 0;
465
- lastStaleSweepMs = now;
466
-
467
- let totalMarked = 0;
468
- for (const [kind, maxAgeDays] of Object.entries(freshness.maxAgeDays)) {
469
- if (maxAgeDays <= 0) continue;
470
- // Mark invalid if: past 2x window, no access in the shield period, and not already invalid
471
- const cutoffMs = now - maxAgeDays * 2 * 86_400_000;
472
- const shieldCutoffMs = now - freshness.reinforcementShieldDays * 86_400_000;
473
- const changes = rawRun(
474
- `
475
- UPDATE memory_items
476
- SET invalid_at = ?
477
- WHERE kind = ?
478
- AND status = 'active'
479
- AND invalid_at IS NULL
480
- AND last_seen_at < ?
481
- AND (access_count = 0 OR COALESCE(last_used_at, 0) < ?)
482
- `,
483
- now,
484
- kind,
485
- cutoffMs,
486
- shieldCutoffMs,
487
- );
488
- if (changes > 0) {
489
- log.info(
490
- { kind, marked: changes, cutoffMs },
491
- "Marked stale memory items as invalid",
492
- );
493
- totalMarked += changes;
513
+ function maybeEnqueueGraphMaintenanceJobs(nowMs = Date.now()): void {
514
+ const schedule: Array<{
515
+ key: string;
516
+ intervalMs: number;
517
+ jobType: MemoryJobType;
518
+ }> = [
519
+ {
520
+ key: GRAPH_MAINTENANCE_CHECKPOINTS.decay,
521
+ intervalMs: GRAPH_DECAY_INTERVAL_MS,
522
+ jobType: "graph_decay",
523
+ },
524
+ {
525
+ key: GRAPH_MAINTENANCE_CHECKPOINTS.consolidate,
526
+ intervalMs: GRAPH_CONSOLIDATE_INTERVAL_MS,
527
+ jobType: "graph_consolidate",
528
+ },
529
+ {
530
+ key: GRAPH_MAINTENANCE_CHECKPOINTS.patternScan,
531
+ intervalMs: GRAPH_PATTERN_SCAN_INTERVAL_MS,
532
+ jobType: "graph_pattern_scan",
533
+ },
534
+ {
535
+ key: GRAPH_MAINTENANCE_CHECKPOINTS.narrative,
536
+ intervalMs: GRAPH_NARRATIVE_INTERVAL_MS,
537
+ jobType: "graph_narrative_refine",
538
+ },
539
+ ];
540
+
541
+ for (const { key, intervalMs, jobType } of schedule) {
542
+ const lastRun = parseInt(getMemoryCheckpoint(key) ?? "0", 10);
543
+ if (nowMs - lastRun >= intervalMs) {
544
+ enqueueMemoryJob(jobType, {});
545
+ setMemoryCheckpoint(key, String(nowMs));
494
546
  }
495
547
  }
496
- return totalMarked;
497
548
  }
@@ -91,10 +91,7 @@ export function backfillMessageIdOnLogs(
91
91
  * for a conversation), this targets only the given log rows — safe for
92
92
  * concurrent watch/assistant turns.
93
93
  */
94
- export function setMessageIdOnLogs(
95
- logIds: string[],
96
- messageId: string,
97
- ): void {
94
+ export function setMessageIdOnLogs(logIds: string[], messageId: string): void {
98
95
  if (logIds.length === 0) return;
99
96
  const db = getDb();
100
97
  db.update(llmRequestLogs)
@@ -185,18 +182,75 @@ function selectOrphanedLogsInRange(
185
182
  .all();
186
183
  }
187
184
 
185
+ /**
186
+ * Find unlinked logs — logs with `message_id IS NULL` that haven't been
187
+ * backfilled yet. This covers the race where the client queries the inspector
188
+ * before `backfillMessageIdOnLogs` runs in `handleMessageComplete`, or when
189
+ * the backfill fails silently (try-catch in the agent loop).
190
+ *
191
+ * Scoped to a single conversation and a time range to avoid cross-turn bleed.
192
+ */
193
+ function selectUnlinkedLogsInRange(
194
+ conversationId: string,
195
+ startTime: number,
196
+ endTime: number,
197
+ ): LogRow[] {
198
+ if (endTime <= startTime) return [];
199
+ const db = getDb();
200
+ return db
201
+ .select({
202
+ id: llmRequestLogs.id,
203
+ conversationId: llmRequestLogs.conversationId,
204
+ messageId: llmRequestLogs.messageId,
205
+ provider: llmRequestLogs.provider,
206
+ requestPayload: llmRequestLogs.requestPayload,
207
+ responsePayload: llmRequestLogs.responsePayload,
208
+ createdAt: llmRequestLogs.createdAt,
209
+ })
210
+ .from(llmRequestLogs)
211
+ .where(
212
+ and(
213
+ eq(llmRequestLogs.conversationId, conversationId),
214
+ gte(llmRequestLogs.createdAt, startTime),
215
+ lte(llmRequestLogs.createdAt, endTime),
216
+ isNull(llmRequestLogs.messageId),
217
+ ),
218
+ )
219
+ .orderBy(llmRequestLogs.createdAt)
220
+ .all();
221
+ }
222
+
223
+ export function getRequestLogById(logId: string): LogRow | null {
224
+ const db = getDb();
225
+ return (
226
+ db
227
+ .select({
228
+ id: llmRequestLogs.id,
229
+ conversationId: llmRequestLogs.conversationId,
230
+ messageId: llmRequestLogs.messageId,
231
+ provider: llmRequestLogs.provider,
232
+ requestPayload: llmRequestLogs.requestPayload,
233
+ responsePayload: llmRequestLogs.responsePayload,
234
+ createdAt: llmRequestLogs.createdAt,
235
+ })
236
+ .from(llmRequestLogs)
237
+ .where(eq(llmRequestLogs.id, logId))
238
+ .get() ?? null
239
+ );
240
+ }
241
+
188
242
  export function getRequestLogsByMessageId(messageId: string): LogRow[] {
189
243
  // Resolve all assistant message IDs in the same turn so the inspector
190
244
  // shows every LLM call from the entire agent turn, not just the queried message.
191
245
  const turnMessageIds = getAssistantMessageIdsInTurn(messageId);
192
246
  const turnLogs = selectLogsByMessageIds(turnMessageIds);
193
247
 
194
- // Orphaned-log recovery: intermediate assistant messages within a turn can
195
- // be deleted (e.g. by retry / deleteLastExchange) while their
196
- // llm_request_logs rows survive. When that happens the message-ID-based
197
- // query misses those logs. We detect orphaned logs (logs whose message_id
198
- // references a deleted message) within the turn's time window and merge
199
- // them with the message-ID-based results.
248
+ // Recovery: find logs in the turn's time window that the message-ID-based
249
+ // query missed. Two categories:
250
+ // 1. Orphaned messageId references a deleted message (retry/deleteLastExchange).
251
+ // 2. Unlinked messageId is still NULL because the backfill hasn't run yet
252
+ // or failed silently. This covers the race where the client queries the
253
+ // inspector before handleMessageComplete persists and backfills.
200
254
  const message = getMessageById(messageId);
201
255
  if (message) {
202
256
  const bounds = getTurnTimeBounds(message.conversationId, message.createdAt);
@@ -206,10 +260,16 @@ export function getRequestLogsByMessageId(messageId: string): LogRow[] {
206
260
  bounds.startTime,
207
261
  bounds.endTime,
208
262
  );
209
- if (orphanedLogs.length > 0) {
263
+ const unlinkedLogs = selectUnlinkedLogsInRange(
264
+ message.conversationId,
265
+ bounds.startTime,
266
+ bounds.endTime,
267
+ );
268
+
269
+ if (orphanedLogs.length > 0 || unlinkedLogs.length > 0) {
210
270
  const seen = new Set(turnLogs.map((l) => l.id));
211
271
  const merged = [...turnLogs];
212
- for (const log of orphanedLogs) {
272
+ for (const log of [...orphanedLogs, ...unlinkedLogs]) {
213
273
  if (!seen.has(log.id)) {
214
274
  merged.push(log);
215
275
  seen.add(log.id);
@@ -218,6 +278,30 @@ export function getRequestLogsByMessageId(messageId: string): LogRow[] {
218
278
  merged.sort(
219
279
  (a, b) => a.createdAt - b.createdAt || a.id.localeCompare(b.id),
220
280
  );
281
+
282
+ // Opportunistically backfill recovered unlinked logs so future queries
283
+ // hit the fast indexed-by-messageId path. Guard with isNull so this
284
+ // recovery path never overwrites a messageId already set by an
285
+ // authoritative caller (e.g. watch-notifier).
286
+ if (unlinkedLogs.length > 0 && turnMessageIds.length > 0) {
287
+ try {
288
+ const db = getDb();
289
+ const ids = unlinkedLogs.map((l) => l.id);
290
+ const targetMessageId = turnMessageIds[turnMessageIds.length - 1]!;
291
+ db.update(llmRequestLogs)
292
+ .set({ messageId: targetMessageId })
293
+ .where(
294
+ and(
295
+ inArray(llmRequestLogs.id, ids),
296
+ isNull(llmRequestLogs.messageId),
297
+ ),
298
+ )
299
+ .run();
300
+ } catch {
301
+ // non-fatal — the recovery already returned the right data
302
+ }
303
+ }
304
+
221
305
  return merged;
222
306
  }
223
307
  }
@@ -23,9 +23,12 @@ export interface RecordMemoryRecallLogParams {
23
23
  topCandidatesJson: unknown;
24
24
  injectedText?: string;
25
25
  reason?: string;
26
+ queryContext?: string;
26
27
  }
27
28
 
28
- export function recordMemoryRecallLog(params: RecordMemoryRecallLogParams): void {
29
+ export function recordMemoryRecallLog(
30
+ params: RecordMemoryRecallLogParams,
31
+ ): void {
29
32
  const db = getDb();
30
33
  db.insert(memoryRecallLogs)
31
34
  .values({
@@ -51,6 +54,7 @@ export function recordMemoryRecallLog(params: RecordMemoryRecallLogParams): void
51
54
  topCandidatesJson: JSON.stringify(params.topCandidatesJson),
52
55
  injectedText: params.injectedText ?? null,
53
56
  reason: params.reason ?? null,
57
+ queryContext: params.queryContext ?? null,
54
58
  createdAt: Date.now(),
55
59
  })
56
60
  .run();
@@ -90,6 +94,47 @@ export interface MemoryRecallLog {
90
94
  topCandidates: unknown;
91
95
  injectedText: string | null;
92
96
  reason: string | null;
97
+ queryContext: string | null;
98
+ }
99
+
100
+ /**
101
+ * Normalizes top-candidate entries from the stored SSE-event format
102
+ * (key/finalScore/semantic/recency/kind) to the inspector format expected
103
+ * by the Swift MemoryRecallCandidate struct (nodeId/score/semanticSimilarity/recencyBoost).
104
+ * Entries already in inspector format pass through unchanged.
105
+ */
106
+ export function normalizeTopCandidates(raw: unknown): unknown {
107
+ if (!Array.isArray(raw)) return raw;
108
+ return raw.flatMap((entry: Record<string, unknown>) => {
109
+ if (!entry || typeof entry !== "object") return [];
110
+
111
+ // Start with a shallow copy, then apply field renames
112
+ const { key, finalScore, semantic, recency, kind: _kind, ...rest } = entry;
113
+
114
+ // nodeId: prefer existing nodeId, fall back to key
115
+ if (rest.nodeId === undefined && key !== undefined) {
116
+ rest.nodeId = key;
117
+ }
118
+
119
+ // score: prefer existing score, fall back to finalScore
120
+ if (rest.score === undefined && finalScore !== undefined) {
121
+ rest.score = finalScore;
122
+ }
123
+
124
+ // semanticSimilarity: prefer existing, fall back to semantic
125
+ if (rest.semanticSimilarity === undefined && semantic !== undefined) {
126
+ rest.semanticSimilarity = semantic;
127
+ }
128
+
129
+ // recencyBoost: prefer existing, fall back to recency
130
+ if (rest.recencyBoost === undefined && recency !== undefined) {
131
+ rest.recencyBoost = recency;
132
+ }
133
+
134
+ // kind is stripped (not in the Swift model) — already excluded via destructuring
135
+
136
+ return rest;
137
+ });
93
138
  }
94
139
 
95
140
  export function getMemoryRecallLogByMessageIds(
@@ -109,9 +154,7 @@ export function getMemoryRecallLogByMessageIds(
109
154
  degraded: !!row.degraded,
110
155
  provider: row.provider,
111
156
  model: row.model,
112
- degradation: row.degradationJson
113
- ? JSON.parse(row.degradationJson)
114
- : null,
157
+ degradation: row.degradationJson ? JSON.parse(row.degradationJson) : null,
115
158
  semanticHits: row.semanticHits,
116
159
  mergedCount: row.mergedCount,
117
160
  selectedCount: row.selectedCount,
@@ -121,8 +164,9 @@ export function getMemoryRecallLogByMessageIds(
121
164
  sparseVectorUsed: !!row.sparseVectorUsed,
122
165
  injectedTokens: row.injectedTokens,
123
166
  latencyMs: row.latencyMs,
124
- topCandidates: JSON.parse(row.topCandidatesJson),
167
+ topCandidates: normalizeTopCandidates(JSON.parse(row.topCandidatesJson)),
125
168
  injectedText: row.injectedText,
126
169
  reason: row.reason,
170
+ queryContext: row.queryContext,
127
171
  };
128
172
  }
@@ -121,6 +121,7 @@ export function extractMediaBlockMeta(
121
121
  }
122
122
  }
123
123
 
124
+
124
125
  function stableJson(value: unknown): string {
125
126
  try {
126
127
  return JSON.stringify(value);