@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
@@ -42,17 +42,16 @@ import {
42
42
  provenanceFromTrustContext,
43
43
  updateConversationContextWindow,
44
44
  updateConversationTitle,
45
+ updateMessageMetadata,
45
46
  } from "../memory/conversation-crud.js";
46
- import {
47
- rebuildConversationDiskViewFromDbState,
48
- syncMessageToDisk,
49
- } from "../memory/conversation-disk-view.js";
47
+ import { syncMessageToDisk } from "../memory/conversation-disk-view.js";
50
48
  import {
51
49
  isReplaceableTitle,
52
50
  queueGenerateConversationTitle,
53
51
  queueRegenerateConversationTitle,
54
52
  UNTITLED_FALLBACK,
55
53
  } from "../memory/conversation-title-service.js";
54
+ import type { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
56
55
  import { recordMemoryRecallLog } from "../memory/memory-recall-log-store.js";
57
56
  import type { PermissionPrompter } from "../permissions/prompter.js";
58
57
  import type { ContentBlock, Message } from "../providers/types.js";
@@ -89,34 +88,30 @@ import {
89
88
  classifyConversationError,
90
89
  isUserCancellation,
91
90
  } from "./conversation-error.js";
92
- import { consolidateAssistantMessages } from "./conversation-history.js";
93
91
  import { raceWithTimeout } from "./conversation-media-retry.js";
94
- import { prepareMemoryContext } from "./conversation-memory.js";
95
92
  import type { MessageQueue } from "./conversation-queue-manager.js";
96
93
  import type { QueueDrainReason } from "./conversation-queue-manager.js";
97
94
  import type {
98
95
  ActiveSurfaceContext,
99
96
  ChannelCapabilities,
100
- ChannelTurnContextParams,
101
97
  InboundActorContext,
102
98
  InjectionMode,
103
- InterfaceTurnContextParams,
104
99
  TrustContext,
105
100
  } from "./conversation-runtime-assembly.js";
106
101
  import {
107
102
  applyRuntimeInjections,
103
+ buildUnifiedTurnContextBlock,
104
+ findLastInjectedNowContent,
108
105
  inboundActorContextFromTrust,
109
106
  inboundActorContextFromTrustContext,
110
107
  readNowScratchpad,
111
- stripInjectedContext,
108
+ readPkbContext,
109
+ stripInjectionsForCompaction,
112
110
  } from "./conversation-runtime-assembly.js";
113
111
  import type { SkillProjectionCache } from "./conversation-skill-tools.js";
114
112
  import { resolveTrustClass } from "./conversation-tool-setup.js";
115
113
  import { recordUsage } from "./conversation-usage.js";
116
- import {
117
- buildTemporalContext,
118
- extractUserTimeZoneFromRecall,
119
- } from "./date-context.js";
114
+ import { formatTurnTimestamp } from "./date-context.js";
120
115
  import { deepRepairHistory, repairHistory } from "./history-repair.js";
121
116
  import type {
122
117
  DynamicPageSurfaceData,
@@ -125,6 +120,7 @@ import type {
125
120
  SurfaceType,
126
121
  UsageStats,
127
122
  } from "./message-protocol.js";
123
+ import type { MemoryRecalled } from "./message-types/memory.js";
128
124
  import type { TraceEmitter } from "./trace-emitter.js";
129
125
 
130
126
  const log = getLogger("conversation-agent-loop");
@@ -205,6 +201,7 @@ export interface AgentLoopConversationContext {
205
201
  contextCompactedAt: number | null;
206
202
 
207
203
  readonly memoryPolicy: { scopeId: string; includeDefaultFallback: boolean };
204
+ readonly graphMemory: ConversationGraphMemory;
208
205
 
209
206
  currentActiveSurfaceId?: string;
210
207
  currentPage?: string;
@@ -503,6 +500,7 @@ export async function runAgentLoopImpl(
503
500
  }
504
501
 
505
502
  const isFirstMessage = ctx.messages.length === 1;
503
+ let shouldInjectWorkspace = isFirstMessage;
506
504
 
507
505
  const compactCheck = ctx.contextWindowManager.shouldCompact(ctx.messages);
508
506
  if (compactCheck.needed) {
@@ -525,6 +523,9 @@ export async function runAgentLoopImpl(
525
523
  ctx.messages = compacted.messages;
526
524
  ctx.contextCompactedMessageCount += compacted.compactedPersistedMessages;
527
525
  ctx.contextCompactedAt = Date.now();
526
+ // Notify memory graph that compaction happened — triggers full context
527
+ // reload on the next turn to replenish lost memory context.
528
+ ctx.graphMemory.onCompacted(compacted.compactedPersistedMessages);
528
529
  updateConversationContextWindow(
529
530
  ctx.conversationId,
530
531
  compacted.summaryText,
@@ -554,6 +555,7 @@ export async function runAgentLoopImpl(
554
555
  compacted.summaryCacheReadInputTokens ?? 0,
555
556
  collapseRawResponses(compacted.summaryRawResponses),
556
557
  );
558
+ shouldInjectWorkspace = true;
557
559
  }
558
560
 
559
561
  const state = createEventHandlerState();
@@ -597,50 +599,94 @@ export async function runAgentLoopImpl(
597
599
 
598
600
  let runMessages = ctx.messages;
599
601
 
600
- const memoryResult = await prepareMemoryContext(
601
- {
602
- conversationId: ctx.conversationId,
603
- messages: ctx.messages,
604
- systemPrompt: ctx.systemPrompt,
605
- provider: ctx.provider,
606
- scopeId: ctx.memoryPolicy.scopeId,
607
- includeDefaultFallback: ctx.memoryPolicy.includeDefaultFallback,
608
- trustClass: resolveTrustClass(ctx.trustContext),
609
- },
610
- content,
611
- userMessageId,
612
- abortController.signal,
613
- onEvent,
614
- );
602
+ // Memory graph retrieval — dispatches to context-load / per-turn based on
603
+ // conversation state.
604
+ const isTrustedActor = resolveTrustClass(ctx.trustContext) === "guardian";
605
+ if (isTrustedActor) {
606
+ const graphResult = await ctx.graphMemory.prepareMemory(
607
+ ctx.messages,
608
+ getConfig(),
609
+ abortController.signal,
610
+ onEvent,
611
+ );
612
+ runMessages = graphResult.runMessages;
613
+
614
+ // Persist the injected block text in message metadata so it survives
615
+ // conversation reloads (eviction, restart, fork). loadFromDb re-injects
616
+ // from metadata.
617
+ if (graphResult.injectedBlockText) {
618
+ try {
619
+ updateMessageMetadata(userMessageId, {
620
+ memoryInjectedBlock: graphResult.injectedBlockText,
621
+ });
622
+ } catch (err) {
623
+ rlog.warn(
624
+ { err },
625
+ "Failed to persist memory injection to metadata (non-fatal)",
626
+ );
627
+ }
628
+ }
615
629
 
616
- const { recall } = memoryResult;
630
+ const m = graphResult.metrics;
617
631
 
618
- try {
619
- recordMemoryRecallLog({
620
- conversationId: ctx.conversationId,
621
- enabled: recall.enabled,
622
- degraded: recall.degraded,
623
- provider: recall.provider,
624
- model: recall.model,
625
- degradationJson: recall.degradation,
626
- semanticHits: recall.semanticHits,
627
- mergedCount: recall.mergedCount,
628
- selectedCount: recall.selectedCount,
629
- tier1Count: recall.tier1Count ?? 0,
630
- tier2Count: recall.tier2Count ?? 0,
631
- hybridSearchLatencyMs: recall.hybridSearchMs ?? 0,
632
- sparseVectorUsed: recall.sparseVectorUsed ?? false,
633
- injectedTokens: recall.injectedTokens,
634
- latencyMs: recall.latencyMs,
635
- topCandidatesJson: recall.topCandidates,
636
- injectedText: recall.injectedText || undefined,
637
- reason: recall.reason,
638
- });
639
- } catch (err) {
640
- log.warn({ err }, "Failed to persist memory recall log (non-fatal)");
641
- }
632
+ try {
633
+ recordMemoryRecallLog({
634
+ conversationId: ctx.conversationId,
635
+ enabled: true,
636
+ degraded: false,
637
+ provider: m?.embeddingProvider ?? undefined,
638
+ model: m?.embeddingModel ?? undefined,
639
+ semanticHits: m?.semanticHits ?? 0,
640
+ mergedCount: m?.mergedCount ?? 0,
641
+ selectedCount: m?.selectedCount ?? 0,
642
+ tier1Count: m?.tier1Count ?? 0,
643
+ tier2Count: m?.tier2Count ?? 0,
644
+ hybridSearchLatencyMs: m?.hybridSearchLatencyMs ?? 0,
645
+ sparseVectorUsed: m?.sparseVectorUsed ?? false,
646
+ injectedTokens: graphResult.injectedTokens,
647
+ latencyMs: graphResult.latencyMs,
648
+ topCandidatesJson: (m?.topCandidates ?? []).map((c) => ({
649
+ key: c.nodeId,
650
+ type: c.type,
651
+ kind: "graph",
652
+ finalScore: c.score,
653
+ semantic: c.semanticSimilarity,
654
+ recency: c.recencyBoost,
655
+ })),
656
+ injectedText: graphResult.injectedBlockText ?? undefined,
657
+ reason: `graph:${graphResult.mode}`,
658
+ queryContext: m?.queryContext ?? undefined,
659
+ });
660
+ } catch (err) {
661
+ log.warn({ err }, "Failed to persist memory recall log (non-fatal)");
662
+ }
642
663
 
643
- runMessages = memoryResult.runMessages;
664
+ if (m) {
665
+ const memoryRecalledEvent: MemoryRecalled = {
666
+ type: "memory_recalled",
667
+ provider: m.embeddingProvider ?? "unknown",
668
+ model: m.embeddingModel ?? "unknown",
669
+ semanticHits: m.semanticHits,
670
+ mergedCount: m.mergedCount,
671
+ selectedCount: m.selectedCount,
672
+ tier1Count: m.tier1Count,
673
+ tier2Count: m.tier2Count,
674
+ hybridSearchLatencyMs: m.hybridSearchLatencyMs,
675
+ sparseVectorUsed: m.sparseVectorUsed,
676
+ injectedTokens: graphResult.injectedTokens,
677
+ latencyMs: graphResult.latencyMs,
678
+ topCandidates: m.topCandidates.map((c) => ({
679
+ key: c.nodeId,
680
+ type: c.type,
681
+ kind: "graph",
682
+ finalScore: c.score,
683
+ semantic: c.semanticSimilarity,
684
+ recency: c.recencyBoost,
685
+ })),
686
+ };
687
+ onEvent(memoryRecalledEvent);
688
+ }
689
+ }
644
690
 
645
691
  // Build active surface context
646
692
  let activeSurface: ActiveSurfaceContext | null = null;
@@ -671,39 +717,20 @@ export async function runAgentLoopImpl(
671
717
 
672
718
  ctx.refreshWorkspaceTopLevelContextIfNeeded();
673
719
 
674
- // Compute fresh temporal context each turn for date grounding.
720
+ // Compute fresh turn timestamp for date grounding.
675
721
  // Absolute "now" is always anchored to assistant host clock, while local
676
722
  // date semantics prefer configured user timezone, then recalled memory.
677
723
  const hostTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
678
724
  const configuredUserTimeZone = getConfig().ui.userTimezone ?? null;
679
- const recalledUserTimeZone = extractUserTimeZoneFromRecall(
680
- recall.injectedText,
681
- );
682
- const temporalContext = buildTemporalContext({
725
+ const recalledUserTimeZone = null;
726
+ const timestamp = formatTurnTimestamp({
683
727
  hostTimeZone,
684
728
  configuredUserTimeZone,
685
729
  userTimeZone: recalledUserTimeZone,
686
730
  });
687
731
 
688
- // Use the channel/interface context captured at the top of this function
689
- // so it reflects the channel/interface that originally sent *this* turn's
690
- // message, even if a newer message from a different channel arrived since.
691
- const channelTurnContext: ChannelTurnContextParams = {
692
- turnContext: capturedTurnChannelContext,
693
- conversationOriginChannel: getConversationOriginChannel(
694
- ctx.conversationId,
695
- ),
696
- };
697
-
698
- const interfaceTurnContext: InterfaceTurnContextParams = {
699
- turnContext: capturedTurnInterfaceContext,
700
- conversationOriginInterface: getConversationOriginInterface(
701
- ctx.conversationId,
702
- ),
703
- };
704
-
705
- // Resolve the inbound actor context for the model's <inbound_actor_context>
706
- // block. When the conversation carries enough identity info, use the unified
732
+ // Resolve the inbound actor context for the unified <turn_context> block.
733
+ // When the conversation carries enough identity info, use the unified
707
734
  // actor trust resolver so member status/policy and guardian binding details
708
735
  // are fresh for this turn. The conversation runtime context remains the source
709
736
  // for policy gating; this block is model-facing grounding metadata.
@@ -724,29 +751,52 @@ export async function runAgentLoopImpl(
724
751
  }
725
752
  }
726
753
 
727
- // Read NOW.md scratchpad fresh each turn so mid-conversation edits are
728
- // picked up without caching or conversation eviction. Only inject for
729
- // guardian conversations — the scratchpad may contain private context
730
- // (mood, relationship state, personal details) that should not be
731
- // exposed to trusted contacts or unknown actors.
732
- const nowScratchpad =
733
- resolveTrustClass(ctx.trustContext) === "guardian"
734
- ? readNowScratchpad()
735
- : null;
754
+ // Build unified turn context block that replaces the separate temporal,
755
+ // channel, interface, and actor context blocks.
756
+ const interfaceName =
757
+ capturedTurnInterfaceContext.userMessageInterface ?? undefined;
758
+ const channelName =
759
+ capturedTurnChannelContext?.userMessageChannel ?? undefined;
760
+ const isGuardian =
761
+ resolvedInboundActorContext?.trustClass === "guardian" ||
762
+ !resolvedInboundActorContext;
763
+ const unifiedTurnContextStr = buildUnifiedTurnContextBlock(
764
+ isGuardian
765
+ ? { timestamp, interfaceName, channelName }
766
+ : {
767
+ timestamp,
768
+ interfaceName,
769
+ channelName,
770
+ actorContext: resolvedInboundActorContext,
771
+ },
772
+ );
773
+
774
+ // The `remember` tool handles scratchpad-style memory writes directly to the graph.
736
775
 
737
776
  const isInteractiveResolved =
738
777
  options?.isInteractive ?? (!ctx.hasNoClient && !ctx.headlessLock);
739
778
 
779
+ // Only inject NOW.md if it changed since the last injection in the
780
+ // conversation. Keeping the previous injection in place avoids mutating
781
+ // historical user messages and preserves the cached prefix.
782
+ const currentNowContent = readNowScratchpad();
783
+ const lastInjectedNow = findLastInjectedNowContent(ctx.messages);
784
+ const nowScratchpad =
785
+ currentNowContent !== lastInjectedNow ? currentNowContent : null;
786
+
787
+ // Read PKB always-loaded files (INDEX, essentials, threads, buffer)
788
+ const currentPkbContent = readPkbContext();
789
+
740
790
  // Shared injection options — reused whenever we need to re-inject after reduction.
741
791
  const injectionOpts = {
742
792
  activeSurface,
743
- workspaceTopLevelContext: ctx.workspaceTopLevelContext,
793
+ workspaceTopLevelContext: shouldInjectWorkspace
794
+ ? ctx.workspaceTopLevelContext
795
+ : null,
744
796
  channelCapabilities: ctx.channelCapabilities ?? null,
745
797
  channelCommandContext: ctx.commandIntent ?? null,
746
- channelTurnContext,
747
- interfaceTurnContext,
748
- inboundActorContext: resolvedInboundActorContext,
749
- temporalContext,
798
+ unifiedTurnContext: unifiedTurnContextStr,
799
+ pkbContext: currentPkbContent,
750
800
  nowScratchpad,
751
801
  voiceCallControlPrompt: ctx.voiceCallControlPrompt ?? null,
752
802
  transportHints: ctx.transportHints ?? null,
@@ -860,13 +910,29 @@ export async function runAgentLoopImpl(
860
910
  step.compactionResult.summaryCacheReadInputTokens ?? 0,
861
911
  collapseRawResponses(step.compactionResult.summaryRawResponses),
862
912
  );
913
+ ctx.graphMemory.onCompacted(
914
+ step.compactionResult.compactedPersistedMessages,
915
+ );
916
+ shouldInjectWorkspace = true;
863
917
  }
864
918
 
865
- // Re-inject with potentially downgraded injection mode
919
+ // Re-inject with potentially downgraded injection mode.
920
+ // When compaction ran it strips existing NOW.md / PKB blocks, so we
921
+ // must re-inject the current content. Otherwise rely on the deduplicated
922
+ // value from injectionOpts to avoid duplicate injection.
866
923
  runMessages = applyRuntimeInjections(ctx.messages, {
867
924
  ...injectionOpts,
925
+ pkbContext: currentPkbContent,
926
+ ...(step.compactionResult?.compacted && { nowScratchpad: currentNowContent }),
927
+ workspaceTopLevelContext: shouldInjectWorkspace
928
+ ? ctx.workspaceTopLevelContext
929
+ : null,
868
930
  mode: currentInjectionMode,
869
931
  });
932
+ if (isTrustedActor && currentInjectionMode !== "minimal") {
933
+ const memResult = ctx.graphMemory.reinjectCachedMemory(runMessages);
934
+ runMessages = memResult.runMessages;
935
+ }
870
936
 
871
937
  // Re-estimate with injections included — step.estimatedTokens was
872
938
  // computed on bare history (ctx.messages) and doesn't account for
@@ -990,7 +1056,7 @@ export async function runAgentLoopImpl(
990
1056
 
991
1057
  // Strip injected context from updated history before compacting,
992
1058
  // so we compact the "raw" persistent messages.
993
- const rawHistory = stripInjectedContext(updatedHistory);
1059
+ const rawHistory = stripInjectionsForCompaction(updatedHistory);
994
1060
  ctx.messages = rawHistory;
995
1061
 
996
1062
  ctx.emitActivityState(
@@ -1044,13 +1110,27 @@ export async function runAgentLoopImpl(
1044
1110
  midLoopCompact.summaryCacheReadInputTokens ?? 0,
1045
1111
  collapseRawResponses(midLoopCompact.summaryRawResponses),
1046
1112
  );
1113
+ ctx.graphMemory.onCompacted(midLoopCompact.compactedPersistedMessages);
1114
+ shouldInjectWorkspace = true;
1047
1115
  }
1048
1116
 
1049
- // Re-inject runtime context and re-enter the agent loop
1117
+ // Re-inject runtime context and re-enter the agent loop.
1118
+ // stripInjectionsForCompaction() unconditionally removed the existing
1119
+ // NOW.md block from ctx.messages above, so we must always re-inject
1120
+ // the current content regardless of whether compaction actually ran.
1050
1121
  runMessages = applyRuntimeInjections(ctx.messages, {
1051
1122
  ...injectionOpts,
1123
+ pkbContext: currentPkbContent,
1124
+ nowScratchpad: currentNowContent,
1125
+ workspaceTopLevelContext: shouldInjectWorkspace
1126
+ ? ctx.workspaceTopLevelContext
1127
+ : null,
1052
1128
  mode: currentInjectionMode,
1053
1129
  });
1130
+ if (isTrustedActor && currentInjectionMode !== "minimal") {
1131
+ const memResult = ctx.graphMemory.reinjectCachedMemory(runMessages);
1132
+ runMessages = memResult.runMessages;
1133
+ }
1054
1134
  preRepairMessages = runMessages;
1055
1135
  preRunHistoryLength = runMessages.length;
1056
1136
 
@@ -1123,7 +1203,7 @@ export async function runAgentLoopImpl(
1123
1203
  // convergence loop operates on the full (larger) history.
1124
1204
  if (state.contextTooLargeDetected) {
1125
1205
  if (updatedHistory.length > preRunHistoryLength) {
1126
- ctx.messages = stripInjectedContext(updatedHistory);
1206
+ ctx.messages = stripInjectionsForCompaction(updatedHistory);
1127
1207
  preRepairMessages = updatedHistory;
1128
1208
  preRunHistoryLength = updatedHistory.length;
1129
1209
  }
@@ -1240,12 +1320,27 @@ export async function runAgentLoopImpl(
1240
1320
  step.compactionResult.summaryCacheReadInputTokens ?? 0,
1241
1321
  collapseRawResponses(step.compactionResult.summaryRawResponses),
1242
1322
  );
1323
+ ctx.graphMemory.onCompacted(
1324
+ step.compactionResult.compactedPersistedMessages,
1325
+ );
1326
+ shouldInjectWorkspace = true;
1243
1327
  }
1244
1328
 
1329
+ // ctx.messages has been stripped (line 1206/1373) so NOW.md must
1330
+ // always be re-injected regardless of whether compaction ran.
1245
1331
  runMessages = applyRuntimeInjections(ctx.messages, {
1246
1332
  ...injectionOpts,
1333
+ pkbContext: currentPkbContent,
1334
+ nowScratchpad: currentNowContent,
1335
+ workspaceTopLevelContext: shouldInjectWorkspace
1336
+ ? ctx.workspaceTopLevelContext
1337
+ : null,
1247
1338
  mode: currentInjectionMode,
1248
1339
  });
1340
+ if (isTrustedActor && currentInjectionMode !== "minimal") {
1341
+ const memResult = ctx.graphMemory.reinjectCachedMemory(runMessages);
1342
+ runMessages = memResult.runMessages;
1343
+ }
1249
1344
  preRepairMessages = runMessages;
1250
1345
  preRunHistoryLength = runMessages.length;
1251
1346
  state.contextTooLargeDetected = false;
@@ -1277,7 +1372,7 @@ export async function runAgentLoopImpl(
1277
1372
  // tier operates on up-to-date history instead of stale
1278
1373
  // pre-rerun messages.
1279
1374
  if (updatedHistory.length > preRunHistoryLength) {
1280
- ctx.messages = stripInjectedContext(updatedHistory);
1375
+ ctx.messages = stripInjectionsForCompaction(updatedHistory);
1281
1376
  preRepairMessages = updatedHistory;
1282
1377
  preRunHistoryLength = updatedHistory.length;
1283
1378
  }
@@ -1347,12 +1442,28 @@ export async function runAgentLoopImpl(
1347
1442
  emergencyCompact.summaryCacheReadInputTokens ?? 0,
1348
1443
  collapseRawResponses(emergencyCompact.summaryRawResponses),
1349
1444
  );
1445
+ ctx.graphMemory.onCompacted(
1446
+ emergencyCompact.compactedPersistedMessages,
1447
+ );
1448
+ shouldInjectWorkspace = true;
1350
1449
  }
1351
1450
 
1451
+ // ctx.messages was already stripped before the convergence
1452
+ // loop, so NOW.md must always be re-injected here.
1352
1453
  runMessages = applyRuntimeInjections(ctx.messages, {
1353
1454
  ...injectionOpts,
1455
+ pkbContext: currentPkbContent,
1456
+ nowScratchpad: currentNowContent,
1457
+ workspaceTopLevelContext: shouldInjectWorkspace
1458
+ ? ctx.workspaceTopLevelContext
1459
+ : null,
1354
1460
  mode: currentInjectionMode,
1355
1461
  });
1462
+ if (isTrustedActor && currentInjectionMode !== "minimal") {
1463
+ const memResult =
1464
+ ctx.graphMemory.reinjectCachedMemory(runMessages);
1465
+ runMessages = memResult.runMessages;
1466
+ }
1356
1467
  preRepairMessages = runMessages;
1357
1468
  preRunHistoryLength = runMessages.length;
1358
1469
  state.contextTooLargeDetected = false;
@@ -1451,12 +1562,27 @@ export async function runAgentLoopImpl(
1451
1562
  emergencyCompact.summaryCacheReadInputTokens ?? 0,
1452
1563
  collapseRawResponses(emergencyCompact.summaryRawResponses),
1453
1564
  );
1565
+ ctx.graphMemory.onCompacted(
1566
+ emergencyCompact.compactedPersistedMessages,
1567
+ );
1568
+ shouldInjectWorkspace = true;
1454
1569
  }
1455
1570
 
1571
+ // ctx.messages was already stripped before the convergence
1572
+ // loop, so NOW.md must always be re-injected here.
1456
1573
  runMessages = applyRuntimeInjections(ctx.messages, {
1457
1574
  ...injectionOpts,
1575
+ pkbContext: currentPkbContent,
1576
+ nowScratchpad: currentNowContent,
1577
+ workspaceTopLevelContext: shouldInjectWorkspace
1578
+ ? ctx.workspaceTopLevelContext
1579
+ : null,
1458
1580
  mode: currentInjectionMode,
1459
1581
  });
1582
+ if (isTrustedActor && currentInjectionMode !== "minimal") {
1583
+ const memResult = ctx.graphMemory.reinjectCachedMemory(runMessages);
1584
+ runMessages = memResult.runMessages;
1585
+ }
1460
1586
  preRepairMessages = runMessages;
1461
1587
  preRunHistoryLength = runMessages.length;
1462
1588
  state.contextTooLargeDetected = false;
@@ -1594,7 +1720,11 @@ export async function runAgentLoopImpl(
1594
1720
  { providerName: ctx.provider.name, toolTokenBudget },
1595
1721
  );
1596
1722
 
1597
- ctx.messages = stripInjectedContext(restoredHistory);
1723
+ // Persist injections in history: runtime-injected context stays on
1724
+ // historical user messages so the conversation prefix is stable for
1725
+ // Anthropic's prefix caching. Stripping only happens during
1726
+ // compaction/overflow recovery (where a cache miss is expected).
1727
+ ctx.messages = restoredHistory;
1598
1728
 
1599
1729
  emitUsage(
1600
1730
  ctx,
@@ -1609,7 +1739,10 @@ export async function runAgentLoopImpl(
1609
1739
  collapseRawResponses(state.exchangeRawResponses),
1610
1740
  state.exchangeProviderName,
1611
1741
  state.exchangeLlmCallCount,
1612
- { tokens: postLoopContextEstimate, maxTokens: config.contextWindow.maxInputTokens },
1742
+ {
1743
+ tokens: postLoopContextEstimate,
1744
+ maxTokens: config.contextWindow.maxInputTokens,
1745
+ },
1613
1746
  );
1614
1747
 
1615
1748
  void getHookManager().trigger("post-message", {
@@ -1842,15 +1975,10 @@ export async function runAgentLoopImpl(
1842
1975
  // Clear at turn end so they never leak into subsequent unrelated messages.
1843
1976
  ctx.commandIntent = undefined;
1844
1977
 
1845
- if (userMessageId) {
1846
- const didMutateHistory = consolidateAssistantMessages(
1847
- ctx.conversationId,
1848
- userMessageId,
1849
- );
1850
- if (didMutateHistory) {
1851
- rebuildConversationDiskViewFromDbState(ctx.conversationId);
1852
- }
1853
- }
1978
+ // Consolidation deferred to compaction: keeping assistant + tool_result
1979
+ // messages unconsolidated preserves the exact message structure sent to
1980
+ // the API, enabling stable prefix caching across turns. Compaction
1981
+ // consolidates when it summarizes old messages (cache miss is expected).
1854
1982
 
1855
1983
  ctx.drainQueue(yieldedForHandoff ? "checkpoint_handoff" : "loop_complete");
1856
1984
 
@@ -33,12 +33,10 @@ const NETWORK_PATTERNS = [
33
33
  ];
34
34
 
35
35
  // Rate limit patterns (HTTP 429 or explicit rate limit messages)
36
- const RATE_LIMIT_PATTERNS = [
37
- /429/,
38
- /rate.?limit/i,
39
- /too many requests/i,
40
- /overloaded/i,
41
- ];
36
+ const RATE_LIMIT_PATTERNS = [/429/, /rate.?limit/i, /too many requests/i];
37
+
38
+ // Overloaded patterns — provider is capacity-constrained (distinct from rate limiting)
39
+ const OVERLOADED_PATTERNS = [/overloaded/i];
42
40
 
43
41
  // Context-too-large patterns (request exceeds the model's context window)
44
42
  const CONTEXT_TOO_LARGE_PATTERNS = [
@@ -249,11 +247,22 @@ function classifyCore(
249
247
  if (error.statusCode === 429) {
250
248
  return {
251
249
  code: "PROVIDER_RATE_LIMIT",
252
- userMessage: "The AI provider is busy. Please try again in a moment.",
250
+ userMessage:
251
+ "You are being rate limited by the AI provider. Please try again in a moment.",
253
252
  retryable: true,
254
253
  errorCategory: "rate_limit",
255
254
  };
256
255
  }
256
+ // Anthropic uses 529 for overloaded_error
257
+ if (error.statusCode === 529) {
258
+ return {
259
+ code: "PROVIDER_OVERLOADED",
260
+ userMessage:
261
+ "The AI provider is temporarily overloaded. Please try again in a moment.",
262
+ retryable: true,
263
+ errorCategory: "provider_overloaded",
264
+ };
265
+ }
257
266
  if (error.statusCode >= 500) {
258
267
  return {
259
268
  code: "PROVIDER_API",
@@ -363,13 +372,27 @@ function classifyByMessage(
363
372
  if (pattern.test(message)) {
364
373
  return {
365
374
  code: "PROVIDER_RATE_LIMIT",
366
- userMessage: "The AI provider is busy. Please try again in a moment.",
375
+ userMessage:
376
+ "You are being rate limited by the AI provider. Please try again in a moment.",
367
377
  retryable: true,
368
378
  errorCategory: "rate_limit",
369
379
  };
370
380
  }
371
381
  }
372
382
 
383
+ // Overloaded — provider is capacity-constrained (not the user's fault)
384
+ for (const pattern of OVERLOADED_PATTERNS) {
385
+ if (pattern.test(message)) {
386
+ return {
387
+ code: "PROVIDER_OVERLOADED",
388
+ userMessage:
389
+ "The AI provider is temporarily overloaded. Please try again in a moment.",
390
+ retryable: true,
391
+ errorCategory: "provider_overloaded",
392
+ };
393
+ }
394
+ }
395
+
373
396
  // Web-search ordering errors (before general ordering errors)
374
397
  if (isWebSearchOrderingError(message)) {
375
398
  return {