@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,7 +1,8 @@
1
1
  /**
2
2
  * Route handlers for attachment upload, download, and deletion.
3
3
  */
4
- import { existsSync, statSync } from "node:fs";
4
+ import { existsSync, realpathSync, statSync } from "node:fs";
5
+ import { join, resolve, sep } from "node:path";
5
6
 
6
7
  import { z } from "zod";
7
8
 
@@ -11,12 +12,82 @@ import {
11
12
  getFilePathForAttachment,
12
13
  validateAttachmentUpload,
13
14
  } from "../../memory/attachments-store.js";
15
+ import { getWorkspaceDir } from "../../util/platform.js";
14
16
  import { httpError } from "../http-errors.js";
15
17
  import type { RouteDefinition } from "../http-router.js";
16
18
 
17
19
  /** 150 MB — base64-encoded 100 MB attachment ≈ 134 MB plus JSON wrapper overhead. */
18
20
  const MAX_UPLOAD_BODY_BYTES = 150 * 1024 * 1024;
19
21
 
22
+ function resolveCanonicalPath(filePath: string): string {
23
+ try {
24
+ return realpathSync(filePath);
25
+ } catch {
26
+ return resolve(filePath);
27
+ }
28
+ }
29
+
30
+ function isPathWithinDirectory(filePath: string, allowedDir: string): boolean {
31
+ return filePath === allowedDir || filePath.startsWith(allowedDir + sep);
32
+ }
33
+
34
+ function resolveAllowedAttachmentDirectories(): string[] {
35
+ const workspaceAttachmentsDir = join(
36
+ getWorkspaceDir(),
37
+ "data",
38
+ "attachments",
39
+ );
40
+ const recordingsDir = join(
41
+ process.env.HOME ?? "",
42
+ "Library/Application Support/vellum-assistant/recordings",
43
+ );
44
+ return [workspaceAttachmentsDir, recordingsDir].map((dir) => {
45
+ try {
46
+ return realpathSync(dir);
47
+ } catch {
48
+ return resolve(dir);
49
+ }
50
+ });
51
+ }
52
+
53
+ /**
54
+ * Check if a resolved path is inside a conversation attachments subdirectory.
55
+ * Matches: <conversationsDir>/<conversationId>/attachments/...
56
+ */
57
+ function isConversationAttachmentPath(resolvedPath: string): boolean {
58
+ const conversationsDir = join(getWorkspaceDir(), "conversations");
59
+ let resolvedConversationsDir: string;
60
+ try {
61
+ resolvedConversationsDir = realpathSync(conversationsDir);
62
+ } catch {
63
+ resolvedConversationsDir = resolve(conversationsDir);
64
+ }
65
+
66
+ if (!isPathWithinDirectory(resolvedPath, resolvedConversationsDir)) {
67
+ return false;
68
+ }
69
+
70
+ // Extract the relative path after conversations/ and verify it contains
71
+ // an "attachments" segment: <conversationId>/attachments/...
72
+ const relativePath = resolvedPath.slice(resolvedConversationsDir.length + 1);
73
+ const segments = relativePath.split(sep);
74
+ return segments.length >= 3 && segments[1] === "attachments";
75
+ }
76
+
77
+ export function resolveAllowedFileBackedAttachmentPath(
78
+ filePath: string,
79
+ ): string | null {
80
+ const resolvedPath = resolveCanonicalPath(filePath);
81
+ const allowedDirs = resolveAllowedAttachmentDirectories();
82
+ if (allowedDirs.some((dir) => isPathWithinDirectory(resolvedPath, dir))) {
83
+ return resolvedPath;
84
+ }
85
+ if (isConversationAttachmentPath(resolvedPath)) {
86
+ return resolvedPath;
87
+ }
88
+ return null;
89
+ }
90
+
20
91
  export async function handleUploadAttachment(req: Request): Promise<Response> {
21
92
  const rawBody = await req.arrayBuffer();
22
93
  if (rawBody.byteLength > MAX_UPLOAD_BODY_BYTES) {
@@ -56,14 +127,22 @@ export async function handleUploadAttachment(req: Request): Promise<Response> {
56
127
  // This supports retry of file-backed attachments (e.g. recordings) where the
57
128
  // client no longer holds the inline data but the file still exists on disk.
58
129
  if (filePath && typeof filePath === "string" && (!data || data === "")) {
59
- if (!existsSync(filePath)) {
130
+ const resolvedPath = resolveAllowedFileBackedAttachmentPath(filePath);
131
+ if (!resolvedPath) {
132
+ return httpError(
133
+ "BAD_REQUEST",
134
+ "filePath is outside allowed upload directories",
135
+ 400,
136
+ );
137
+ }
138
+ if (!existsSync(resolvedPath)) {
60
139
  return httpError("BAD_REQUEST", "filePath does not exist on disk", 400);
61
140
  }
62
- const sizeBytes = statSync(filePath).size;
141
+ const sizeBytes = statSync(resolvedPath).size;
63
142
  attachment = attachmentsStore.uploadFileBackedAttachment(
64
143
  filename,
65
144
  mimeType,
66
- filePath,
145
+ resolvedPath,
67
146
  sizeBytes,
68
147
  );
69
148
  } else {
@@ -130,24 +209,29 @@ export async function handleDeleteAttachment(req: Request): Promise<Response> {
130
209
  }
131
210
 
132
211
  function handleGetAttachment(attachmentId: string): Response {
212
+ // Use the file_path column to detect file-backed attachments, not string
213
+ // truthiness of dataBase64 (which would also match valid zero-byte uploads).
214
+ const isFileBacked = !!getFilePathForAttachment(attachmentId);
215
+
216
+ // Skip hydrating file data for file-backed attachments — clients should
217
+ // fetch content via GET /attachments/:id/content (which validates the path
218
+ // against the directory allowlist).
133
219
  const attachment = attachmentsStore.getAttachmentById(attachmentId, {
134
- hydrateFileData: true,
220
+ hydrateFileData: !isFileBacked,
135
221
  });
136
222
  if (!attachment) {
137
223
  return httpError("NOT_FOUND", "Attachment not found", 404);
138
224
  }
139
225
 
140
- // Use the file_path column to detect file-backed attachments, not string
141
- // truthiness of dataBase64 (which would also match valid zero-byte uploads).
142
- const isFileBacked = !!getFilePathForAttachment(attachmentId);
143
-
144
226
  return Response.json({
145
227
  id: attachment.id,
146
228
  filename: attachment.originalFilename,
147
229
  mimeType: attachment.mimeType,
148
230
  sizeBytes: attachment.sizeBytes,
149
231
  kind: attachment.kind,
150
- data: attachment.dataBase64,
232
+ // Return null for file-backed attachments so the gateway's hydration check
233
+ // (payload.data == null) triggers a fetch from the /content endpoint.
234
+ data: isFileBacked ? null : attachment.dataBase64,
151
235
  // Signal to clients that they should fetch content via the /content endpoint
152
236
  ...(isFileBacked ? { fileBacked: true } : {}),
153
237
  });
@@ -162,21 +246,27 @@ export function handleGetAttachmentContent(
162
246
  attachmentId: string,
163
247
  req: Request,
164
248
  ): Response {
249
+ // Check for file-backed attachment first so we can skip hydration — file-backed
250
+ // content is served directly from disk via Bun.file, not from the hydrated base64.
251
+ const filePath = getFilePathForAttachment(attachmentId);
252
+ const isFileBacked = !!filePath;
253
+
165
254
  const attachment = attachmentsStore.getAttachmentById(attachmentId, {
166
- hydrateFileData: true,
255
+ hydrateFileData: !isFileBacked,
167
256
  });
168
257
  if (!attachment) {
169
258
  return httpError("NOT_FOUND", "Attachment not found", 404);
170
259
  }
171
-
172
- // Check for file-backed attachment
173
- const filePath = getFilePathForAttachment(attachmentId);
174
260
  if (filePath) {
175
- if (!existsSync(filePath)) {
261
+ const resolvedPath = resolveAllowedFileBackedAttachmentPath(filePath);
262
+ if (!resolvedPath) {
263
+ return httpError("NOT_FOUND", "Attachment content not found", 404);
264
+ }
265
+ if (!existsSync(resolvedPath)) {
176
266
  return httpError("NOT_FOUND", "Recording file not found on disk", 404);
177
267
  }
178
268
 
179
- const file = Bun.file(filePath);
269
+ const file = Bun.file(resolvedPath);
180
270
  const rangeHeader = req.headers.get("Range");
181
271
 
182
272
  if (rangeHeader) {
@@ -1,5 +1,3 @@
1
- import { join } from "node:path";
2
-
3
1
  import { z } from "zod";
4
2
 
5
3
  import { getCharacterComponents } from "../../avatar/character-components.js";
@@ -8,7 +6,7 @@ import {
8
6
  writeTraitsAndRenderAvatar,
9
7
  } from "../../avatar/traits-png-sync.js";
10
8
  import { getLogger } from "../../util/logger.js";
11
- import { getWorkspaceDir } from "../../util/platform.js";
9
+ import { getAvatarImagePath } from "../../util/platform.js";
12
10
  import { buildAssistantEvent } from "../assistant-event.js";
13
11
  import { assistantEventHub } from "../assistant-event-hub.js";
14
12
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
@@ -18,12 +16,7 @@ import type { RouteDefinition } from "../http-router.js";
18
16
  const log = getLogger("avatar-routes");
19
17
 
20
18
  function publishAvatarUpdated(): void {
21
- const avatarPath = join(
22
- getWorkspaceDir(),
23
- "data",
24
- "avatar",
25
- "avatar-image.png",
26
- );
19
+ const avatarPath = getAvatarImagePath();
27
20
  assistantEventHub
28
21
  .publish(
29
22
  buildAssistantEvent(DAEMON_INTERNAL_ASSISTANT_ID, {
@@ -2,7 +2,7 @@
2
2
  * Route handlers for the brain graph visualization endpoint.
3
3
  *
4
4
  * Queries the memory database to return a knowledge graph shaped for brain-lobe
5
- * visualization, with memory items mapped to brain regions based on their kind.
5
+ * visualization, with memory items mapped to brain regions based on their type.
6
6
  */
7
7
 
8
8
  import { readFileSync } from "node:fs";
@@ -12,24 +12,28 @@ import { count } from "drizzle-orm";
12
12
  import { z } from "zod";
13
13
 
14
14
  import { getDb } from "../../memory/db.js";
15
- import { memoryItems } from "../../memory/schema.js";
15
+ import { memoryGraphNodes } from "../../memory/schema.js";
16
16
  import { resolveBundledDir } from "../../util/bundled-asset.js";
17
17
  import type { RouteDefinition } from "../http-router.js";
18
18
 
19
19
  function getMemoryKindColor(kind: string): string {
20
20
  switch (kind) {
21
- case "identity":
22
- return "#8b5cf6";
23
- case "preference":
24
- return "#3b82f6";
25
- case "project":
26
- return "#10b981";
27
- case "decision":
28
- return "#f59e0b";
29
- case "constraint":
30
- return "#ef4444";
31
- case "event":
32
- return "#ec4899";
21
+ case "episodic":
22
+ return "#ec4899"; // pink — specific moments/events
23
+ case "semantic":
24
+ return "#3b82f6"; // blue — facts/knowledge
25
+ case "procedural":
26
+ return "#10b981"; // green — skills/how-to
27
+ case "emotional":
28
+ return "#ef4444"; // red — feelings
29
+ case "prospective":
30
+ return "#f59e0b"; // amber — future-oriented
31
+ case "behavioral":
32
+ return "#8b5cf6"; // violet — behavioral patterns
33
+ case "narrative":
34
+ return "#6366f1"; // indigo — stories
35
+ case "shared":
36
+ return "#14b8a6"; // teal — relationship memories
33
37
  default:
34
38
  return "#94a3b8";
35
39
  }
@@ -41,11 +45,11 @@ function handleGetBrainGraph(): Response {
41
45
 
42
46
  const kindCountRows = db
43
47
  .select({
44
- kind: memoryItems.kind,
48
+ kind: memoryGraphNodes.type,
45
49
  count: count(),
46
50
  })
47
- .from(memoryItems)
48
- .groupBy(memoryItems.kind)
51
+ .from(memoryGraphNodes)
52
+ .groupBy(memoryGraphNodes.type)
49
53
  .all();
50
54
 
51
55
  const memorySummary = kindCountRows.map((row) => ({
@@ -86,9 +90,6 @@ export function handleServeBrainGraphUI(bearerToken?: string): Response {
86
90
  );
87
91
  let html = readFileSync(join(brainGraphDir, "brain-graph.html"), "utf-8");
88
92
  if (bearerToken) {
89
- // Inject token as a meta tag for client-side fetch authentication.
90
- // HTML-escape the token value to guard against injection if the token
91
- // comes from an environment variable with special characters.
92
93
  const escapedToken = bearerToken
93
94
  .replace(/&/g, "&amp;")
94
95
  .replace(/"/g, "&quot;")
@@ -99,8 +100,6 @@ export function handleServeBrainGraphUI(bearerToken?: string): Response {
99
100
  ` <meta name="api-token" content="${escapedToken}">\n</head>`,
100
101
  );
101
102
  }
102
- // CSP permits the CDN sources required by D3.js and Three.js.
103
- // 'unsafe-eval' is needed by Three.js's shader compilation path.
104
103
  const csp = [
105
104
  "default-src 'self'",
106
105
  "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://d3js.org",
@@ -16,6 +16,8 @@ import { existsSync, readFileSync } from "node:fs";
16
16
 
17
17
  import { z } from "zod";
18
18
 
19
+ import { getConfig } from "../../config/loader.js";
20
+ import { readNowScratchpad } from "../../daemon/conversation-runtime-assembly.js";
19
21
  import { getConversationByKey } from "../../memory/conversation-key-store.js";
20
22
  import {
21
23
  resolveChannelPersona,
@@ -35,6 +37,9 @@ const log = getLogger("btw-routes");
35
37
  /** Conversation key used by the client for identity intro generation. */
36
38
  const IDENTITY_INTRO_KEY = "identity-intro";
37
39
 
40
+ /** Conversation key used by the client for empty-state greeting generation. */
41
+ const GREETING_KEY = "greeting";
42
+
38
43
  /**
39
44
  * Parse the `## Identity Intro` section from SOUL.md.
40
45
  * Returns the first non-empty line under that heading, or null.
@@ -132,6 +137,18 @@ async function handleBtw(
132
137
  }
133
138
  }
134
139
 
140
+ // ----- Greeting context enrichment -----
141
+ // Inject NOW.md scratchpad so the model has contextual awareness (mood,
142
+ // current activity) and produces varied, relevant greetings instead of
143
+ // the same deterministic output each time.
144
+ let effectiveContent = trimmedContent;
145
+ if (conversationKey === GREETING_KEY) {
146
+ const now = readNowScratchpad();
147
+ if (now) {
148
+ effectiveContent = `${trimmedContent}\n\n<context>\n${now}\n</context>`;
149
+ }
150
+ }
151
+
135
152
  // Look up an existing conversation — never create one. BTW is ephemeral
136
153
  // (the file header promises "No messages are persisted"), so we must not
137
154
  // call getOrCreateConversation which would insert a DB row. When no
@@ -150,14 +167,18 @@ async function handleBtw(
150
167
  (async () => {
151
168
  try {
152
169
  const isIntroRequest = conversationKey === IDENTITY_INTRO_KEY;
170
+ const isGreeting = conversationKey === GREETING_KEY;
153
171
  const userPersona = resolveGuardianPersona();
154
172
  const channelPersona = resolveChannelPersona(undefined);
155
173
  const result = await runBtwSidechain({
156
- content: trimmedContent,
174
+ content: effectiveContent,
157
175
  conversation,
158
176
  signal: req.signal,
159
177
  userPersona,
160
178
  channelPersona,
179
+ ...(isGreeting
180
+ ? { modelIntent: getConfig().ui.greetingModelIntent }
181
+ : {}),
161
182
  onEvent: (event) => {
162
183
  if (event.type === "text_delta") {
163
184
  controller.enqueue(
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Route handler for conversation analysis.
3
+ *
4
+ * POST /v1/conversations/:id/analyze — analyze a conversation via a new
5
+ * agent loop that produces a structured self-assessment.
6
+ */
7
+
8
+ import type { ServerMessage } from "../../daemon/message-protocol.js";
9
+ import {
10
+ addMessage,
11
+ createConversation,
12
+ getConversation,
13
+ getMessages,
14
+ } from "../../memory/conversation-crud.js";
15
+ import { resolveConversationId } from "../../memory/conversation-key-store.js";
16
+ import { getLogger } from "../../util/logger.js";
17
+ import { buildAssistantEvent } from "../assistant-event.js";
18
+ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
19
+ import { httpError } from "../http-errors.js";
20
+ import type { RouteDefinition } from "../http-router.js";
21
+ import type { SendMessageDeps } from "../http-types.js";
22
+ import { resolveLocalTrustContext } from "../local-actor-identity.js";
23
+
24
+ const log = getLogger("conversation-analysis-routes");
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // Dependency types — injected by the daemon at wiring time
28
+ // ---------------------------------------------------------------------------
29
+
30
+ export interface ConversationAnalysisDeps {
31
+ sendMessageDeps: SendMessageDeps;
32
+ buildConversationDetailResponse: (
33
+ id: string,
34
+ ) => Record<string, unknown> | null;
35
+ }
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Route definitions
39
+ // ---------------------------------------------------------------------------
40
+
41
+ export function conversationAnalysisRouteDefinitions(
42
+ deps: ConversationAnalysisDeps,
43
+ ): RouteDefinition[] {
44
+ return [
45
+ {
46
+ endpoint: "conversations/:id/analyze",
47
+ method: "POST",
48
+ policyKey: "conversations/analyze",
49
+ summary: "Analyze a conversation",
50
+ description:
51
+ "Create a new conversation with a structured self-assessment of an existing conversation.",
52
+ tags: ["conversations"],
53
+ handler: async ({ params }) => {
54
+ // a. Resolve conversation ID
55
+ const resolvedId = resolveConversationId(params.id);
56
+ if (!resolvedId) {
57
+ return httpError(
58
+ "NOT_FOUND",
59
+ `Conversation ${params.id} not found`,
60
+ 404,
61
+ );
62
+ }
63
+
64
+ // b. Load the conversation
65
+ const conversation = getConversation(resolvedId);
66
+ if (!conversation) {
67
+ return httpError(
68
+ "NOT_FOUND",
69
+ `Conversation ${resolvedId} not found`,
70
+ 404,
71
+ );
72
+ }
73
+
74
+ // c. Reject private conversations
75
+ if (conversation.conversationType === "private") {
76
+ return httpError(
77
+ "FORBIDDEN",
78
+ "Private conversations cannot be analyzed",
79
+ 403,
80
+ );
81
+ }
82
+
83
+ // d. Check for messages
84
+ const existingMessages = getMessages(resolvedId);
85
+ if (existingMessages.length === 0) {
86
+ return httpError(
87
+ "BAD_REQUEST",
88
+ "Conversation has no messages to analyze",
89
+ 400,
90
+ );
91
+ }
92
+
93
+ // e. Build the analysis transcript
94
+ const { buildAnalysisTranscript } =
95
+ await import("../../export/transcript-formatter.js");
96
+ const transcript = buildAnalysisTranscript(resolvedId);
97
+
98
+ // f. Create a new conversation for the analysis
99
+ const newConv = createConversation({
100
+ title: `Analysis: ${conversation.title ?? "Untitled"}`,
101
+ });
102
+
103
+ // g. Build the analysis prompt
104
+ const prompt = `<transcript>
105
+ ${transcript}
106
+ </transcript>
107
+
108
+ Analyze the conversation above. Provide a structured self-assessment:
109
+
110
+ 1. **Summary**: What was the user trying to accomplish? What was the outcome?
111
+ 2. **What went well**: Effective tool usage, good reasoning, helpful responses, problem-solving patterns.
112
+ 3. **What went wrong**: Errors, unnecessary tool calls, incorrect assumptions, wasted turns, misunderstandings.
113
+ 4. **Root causes**: Why did failures happen? Missing context? Wrong approach? Tool limitations?
114
+ 5. **Recommendations**: Specific, actionable improvements for similar conversations next time.
115
+
116
+ Be honest and specific. Reference particular moments in the transcript. Focus on patterns that generalize beyond this specific conversation.
117
+
118
+ If you identify insights worth remembering for future conversations, use your memory tools to save them.`;
119
+
120
+ // h. Persist the user message
121
+ const message = await addMessage(
122
+ newConv.id,
123
+ "user",
124
+ JSON.stringify([{ type: "text", text: prompt }]),
125
+ { provenanceTrustClass: "guardian" as const },
126
+ );
127
+ const messageId = message.id;
128
+
129
+ // i. Load the conversation into memory and set guardian trust context
130
+ const analysisConversation =
131
+ await deps.sendMessageDeps.getOrCreateConversation(newConv.id);
132
+ analysisConversation.setTrustContext(resolveLocalTrustContext("vellum"));
133
+ await analysisConversation.ensureActorScopedHistory();
134
+
135
+ // j. Build onEvent using inline hub publisher
136
+ const onEvent = (msg: ServerMessage) => {
137
+ deps.sendMessageDeps.assistantEventHub.publish(
138
+ buildAssistantEvent(DAEMON_INTERNAL_ASSISTANT_ID, msg, newConv.id),
139
+ );
140
+ };
141
+
142
+ // k. Set up processing state (required by runAgentLoop guard)
143
+ analysisConversation.processing = true;
144
+ analysisConversation.abortController = new AbortController();
145
+ analysisConversation.currentRequestId = crypto.randomUUID();
146
+
147
+ // l. Fire-and-forget the agent loop
148
+ analysisConversation
149
+ .runAgentLoop(prompt, messageId, onEvent, {
150
+ isInteractive: false,
151
+ isUserMessage: true,
152
+ })
153
+ .catch((err) => {
154
+ log.error(
155
+ { err, conversationId: newConv.id },
156
+ "Analysis agent loop failed",
157
+ );
158
+ });
159
+
160
+ // m. Return the new conversation detail
161
+ const detail = deps.buildConversationDetailResponse(newConv.id);
162
+ if (!detail) {
163
+ return httpError(
164
+ "INTERNAL_ERROR",
165
+ `Analysis conversation ${newConv.id} could not be loaded`,
166
+ 500,
167
+ );
168
+ }
169
+ return Response.json(detail);
170
+ },
171
+ },
172
+ ];
173
+ }
@@ -344,12 +344,6 @@ export function conversationManagementRouteDefinitions(
344
344
  targetId: segId,
345
345
  });
346
346
  }
347
- for (const itemId of result.orphanedItemIds) {
348
- enqueueMemoryJob("delete_qdrant_vectors", {
349
- targetType: "item",
350
- targetId: itemId,
351
- });
352
- }
353
347
  for (const summaryId of result.deletedSummaryIds) {
354
348
  enqueueMemoryJob("delete_qdrant_vectors", {
355
349
  targetType: "summary",
@@ -359,7 +353,6 @@ export function conversationManagementRouteDefinitions(
359
353
  log.info(
360
354
  {
361
355
  conversationId: resolvedId,
362
- unsuperseded: result.unsupersededItemIds.length,
363
356
  summariesDeleted: result.deletedSummaryIds.length,
364
357
  jobsCancelled: result.cancelledJobCount,
365
358
  },
@@ -367,7 +360,7 @@ export function conversationManagementRouteDefinitions(
367
360
  );
368
361
  return Response.json({
369
362
  wiped: true,
370
- unsupersededItems: result.unsupersededItemIds.length,
363
+ unsupersededItems: 0,
371
364
  deletedSummaries: result.deletedSummaryIds.length,
372
365
  cancelledJobs: result.cancelledJobCount,
373
366
  });
@@ -417,12 +410,6 @@ export function conversationManagementRouteDefinitions(
417
410
  targetId: segId,
418
411
  });
419
412
  }
420
- for (const itemId of deleted.orphanedItemIds) {
421
- enqueueMemoryJob("delete_qdrant_vectors", {
422
- targetType: "item",
423
- targetId: itemId,
424
- });
425
- }
426
413
  for (const summaryId of deleted.deletedSummaryIds) {
427
414
  enqueueMemoryJob("delete_qdrant_vectors", {
428
415
  targetType: "summary",
@@ -528,6 +515,7 @@ export function conversationManagementRouteDefinitions(
528
515
  conversationId: string;
529
516
  displayOrder?: number;
530
517
  isPinned?: boolean;
518
+ groupId?: string | null;
531
519
  }>;
532
520
  };
533
521
  if (!Array.isArray(body.updates)) {
@@ -538,6 +526,7 @@ export function conversationManagementRouteDefinitions(
538
526
  id: u.conversationId,
539
527
  displayOrder: u.displayOrder ?? null,
540
528
  isPinned: u.isPinned ?? false,
529
+ groupId: u.groupId,
541
530
  })),
542
531
  );
543
532
  return Response.json({ ok: true });
@@ -43,7 +43,10 @@ import {
43
43
  } from "../../daemon/handlers/conversation-history.js";
44
44
  import { deleteQueuedMessage } from "../../daemon/handlers/conversations.js";
45
45
  import { getAssistantMessageIdsInTurn } from "../../memory/conversation-crud.js";
46
- import { getRequestLogsByMessageId } from "../../memory/llm-request-log-store.js";
46
+ import {
47
+ getRequestLogById,
48
+ getRequestLogsByMessageId,
49
+ } from "../../memory/llm-request-log-store.js";
47
50
  import { getMemoryRecallLogByMessageIds } from "../../memory/memory-recall-log-store.js";
48
51
  import { resolvePricingForUsage } from "../../util/pricing.js";
49
52
  import { httpError } from "../http-errors.js";
@@ -487,8 +490,8 @@ export function conversationQueryRouteDefinitions(
487
490
  );
488
491
  return {
489
492
  id: log.id,
490
- requestPayload,
491
- responsePayload,
493
+ requestPayload: null,
494
+ responsePayload: null,
492
495
  createdAt: log.createdAt,
493
496
  ...result,
494
497
  };
@@ -498,6 +501,49 @@ export function conversationQueryRouteDefinitions(
498
501
  },
499
502
  },
500
503
 
504
+ // ── Raw payload for a single LLM request log ─────────────────────
505
+ {
506
+ endpoint: "llm-request-logs/:id/payload",
507
+ method: "GET",
508
+ policyKey: "llm-request-logs/payload",
509
+ summary: "Get raw payload for a single LLM request log",
510
+ description:
511
+ "Return the full request and response payloads for a specific log entry.",
512
+ tags: ["messages"],
513
+ responseBody: z.object({
514
+ id: z.string(),
515
+ requestPayload: z.unknown(),
516
+ responsePayload: z.unknown(),
517
+ }),
518
+ handler: ({ params }) => {
519
+ const logId = params.id;
520
+ if (!logId) {
521
+ return httpError("BAD_REQUEST", "log id is required", 400);
522
+ }
523
+ const log = getRequestLogById(logId);
524
+ if (!log) {
525
+ return httpError("NOT_FOUND", "log not found", 404);
526
+ }
527
+ let requestPayload: unknown;
528
+ try {
529
+ requestPayload = JSON.parse(log.requestPayload);
530
+ } catch {
531
+ requestPayload = log.requestPayload;
532
+ }
533
+ let responsePayload: unknown;
534
+ try {
535
+ responsePayload = JSON.parse(log.responsePayload);
536
+ } catch {
537
+ responsePayload = log.responsePayload;
538
+ }
539
+ return Response.json({
540
+ id: log.id,
541
+ requestPayload,
542
+ responsePayload,
543
+ });
544
+ },
545
+ },
546
+
501
547
  // ── Delete queued message ─────────────────────────────────────────
502
548
  {
503
549
  endpoint: "messages/queued/:id",