@vellumai/assistant 0.6.0 → 0.6.2

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 (358) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +68 -15
  3. package/Dockerfile +2 -2
  4. package/bun.lock +6 -2
  5. package/docker-entrypoint.sh +42 -1
  6. package/docs/architecture/integrations.md +1 -1
  7. package/docs/architecture/memory.md +21 -24
  8. package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
  9. package/openapi.yaml +539 -4
  10. package/package.json +5 -1
  11. package/src/__tests__/anthropic-provider.test.ts +160 -95
  12. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  13. package/src/__tests__/app-executors.test.ts +47 -1
  14. package/src/__tests__/app-source-watcher.test.ts +159 -0
  15. package/src/__tests__/assistant-event-hub.test.ts +30 -0
  16. package/src/__tests__/checker.test.ts +138 -172
  17. package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
  18. package/src/__tests__/config-schema.test.ts +5 -0
  19. package/src/__tests__/context-overflow-approval.test.ts +5 -5
  20. package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -6
  21. package/src/__tests__/conversation-agent-loop.test.ts +4 -51
  22. package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
  23. package/src/__tests__/conversation-directories-parse.test.ts +105 -0
  24. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  25. package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
  26. package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
  27. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
  28. package/src/__tests__/conversation-wipe.test.ts +2 -6
  29. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
  30. package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
  31. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  32. package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
  33. package/src/__tests__/credential-execution-approval-bridge.test.ts +0 -2
  34. package/src/__tests__/date-context.test.ts +76 -210
  35. package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
  36. package/src/__tests__/file-list-tool.test.ts +219 -0
  37. package/src/__tests__/first-greeting.test.ts +1 -1
  38. package/src/__tests__/heartbeat-service.test.ts +180 -3
  39. package/src/__tests__/identity-routes.test.ts +328 -0
  40. package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
  41. package/src/__tests__/injection-block.test.ts +24 -0
  42. package/src/__tests__/inline-command-runner.test.ts +7 -5
  43. package/src/__tests__/install-skill-routing.test.ts +7 -6
  44. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +15 -14
  45. package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
  46. package/src/__tests__/llm-context-normalization.test.ts +18 -18
  47. package/src/__tests__/llm-context-route-provider.test.ts +101 -0
  48. package/src/__tests__/llm-request-log-turn-query.test.ts +162 -0
  49. package/src/__tests__/log-export-workspace.test.ts +257 -100
  50. package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
  51. package/src/__tests__/mcp-abort-signal.test.ts +5 -0
  52. package/src/__tests__/mcp-client-auth.test.ts +5 -0
  53. package/src/__tests__/memory-recall-log-store.test.ts +132 -0
  54. package/src/__tests__/migration-export-streaming.test.ts +304 -0
  55. package/src/__tests__/migration-import-commit-http.test.ts +11 -10
  56. package/src/__tests__/mock-fetch.ts +87 -0
  57. package/src/__tests__/navigate-settings-tab.test.ts +14 -1
  58. package/src/__tests__/notification-broadcaster.test.ts +65 -0
  59. package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
  60. package/src/__tests__/onboarding-template-contract.test.ts +63 -14
  61. package/src/__tests__/parser.test.ts +32 -0
  62. package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
  63. package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
  64. package/src/__tests__/permission-mode-sse.test.ts +418 -0
  65. package/src/__tests__/permission-mode-store.test.ts +277 -0
  66. package/src/__tests__/permission-mode.test.ts +101 -0
  67. package/src/__tests__/pkb-autoinject.test.ts +96 -0
  68. package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
  69. package/src/__tests__/profiler-routes.test.ts +502 -0
  70. package/src/__tests__/profiler-run-store.test.ts +441 -0
  71. package/src/__tests__/proxy-approval-callback.test.ts +4 -75
  72. package/src/__tests__/registry.test.ts +1 -1
  73. package/src/__tests__/require-fresh-approval.test.ts +0 -2
  74. package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
  75. package/src/__tests__/sandbox-host-parity.test.ts +5 -4
  76. package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
  77. package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
  78. package/src/__tests__/search-skills-unified.test.ts +4 -3
  79. package/src/__tests__/send-endpoint-busy.test.ts +42 -3
  80. package/src/__tests__/set-permission-mode.test.ts +274 -0
  81. package/src/__tests__/skill-load-feature-flag.test.ts +12 -0
  82. package/src/__tests__/skill-memory.test.ts +2 -783
  83. package/src/__tests__/strip-memory-injections.test.ts +187 -0
  84. package/src/__tests__/subagent-detail.test.ts +84 -0
  85. package/src/__tests__/subagent-disposal.test.ts +308 -0
  86. package/src/__tests__/subagent-manager-notify.test.ts +19 -10
  87. package/src/__tests__/subagent-notify-parent.test.ts +390 -0
  88. package/src/__tests__/subagent-role-registry.test.ts +108 -0
  89. package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
  90. package/src/__tests__/subagent-tools.test.ts +464 -4
  91. package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
  92. package/src/__tests__/task-memory-cleanup.test.ts +12 -12
  93. package/src/__tests__/terminal-sandbox.test.ts +1 -1
  94. package/src/__tests__/terminal-tools.test.ts +16 -29
  95. package/src/__tests__/test-preload.ts +18 -0
  96. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
  97. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
  98. package/src/__tests__/tool-executor.test.ts +4 -27
  99. package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
  100. package/src/__tests__/top-level-renderer.test.ts +10 -13
  101. package/src/__tests__/transport-hints-queue.test.ts +77 -0
  102. package/src/__tests__/trust-store.test.ts +4 -4
  103. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +116 -2
  104. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
  105. package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
  106. package/src/__tests__/workspace-policy.test.ts +2 -7
  107. package/src/agent/loop.ts +6 -29
  108. package/src/approvals/guardian-request-resolvers.ts +24 -0
  109. package/src/avatar/traits-png-sync.ts +3 -3
  110. package/src/channels/types.ts +5 -0
  111. package/src/cli/__tests__/run-assistant-command.ts +56 -0
  112. package/src/cli/__tests__/unknown-command.test.ts +33 -0
  113. package/src/cli/commands/__tests__/email-download.test.ts +245 -0
  114. package/src/cli/commands/__tests__/email-list.test.ts +192 -0
  115. package/src/cli/commands/__tests__/email-register.test.ts +186 -0
  116. package/src/cli/commands/__tests__/email-send.test.ts +291 -0
  117. package/src/cli/commands/__tests__/email-status.test.ts +181 -0
  118. package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
  119. package/src/cli/commands/__tests__/routes.test.ts +562 -0
  120. package/src/cli/commands/conversations.ts +1 -8
  121. package/src/cli/commands/default-action.ts +68 -1
  122. package/src/cli/commands/email.ts +584 -835
  123. package/src/cli/commands/memory.ts +1 -34
  124. package/src/cli/commands/notifications.ts +7 -2
  125. package/src/cli/commands/oauth/__tests__/connect.test.ts +27 -0
  126. package/src/cli/commands/oauth/connect.ts +25 -5
  127. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  128. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  129. package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
  130. package/src/cli/commands/routes.ts +396 -0
  131. package/src/cli/commands/skills.ts +130 -20
  132. package/src/cli/program.ts +11 -2
  133. package/src/cli.ts +1 -120
  134. package/src/config/assistant-feature-flags.ts +59 -55
  135. package/src/config/bundled-skills/app-builder/SKILL.md +91 -5
  136. package/src/config/bundled-skills/gmail/SKILL.md +13 -8
  137. package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
  138. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
  139. package/src/config/bundled-skills/messaging/SKILL.md +7 -0
  140. package/src/config/bundled-skills/schedule/SKILL.md +22 -2
  141. package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
  142. package/src/config/bundled-skills/settings/TOOLS.json +1 -1
  143. package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
  144. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
  145. package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
  146. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
  147. package/src/config/bundled-skills/slack/SKILL.md +2 -0
  148. package/src/config/bundled-skills/subagent/SKILL.md +43 -3
  149. package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
  150. package/src/config/env-registry.ts +63 -0
  151. package/src/config/feature-flag-registry.json +17 -1
  152. package/src/config/schema.ts +8 -0
  153. package/src/config/schemas/filing.ts +51 -0
  154. package/src/config/schemas/heartbeat.ts +15 -12
  155. package/src/config/schemas/memory-lifecycle.ts +12 -0
  156. package/src/config/schemas/security.ts +14 -0
  157. package/src/config/schemas/services.ts +8 -0
  158. package/src/credential-execution/approval-bridge.ts +0 -1
  159. package/src/credential-execution/managed-catalog.ts +3 -7
  160. package/src/daemon/app-source-watcher.ts +93 -0
  161. package/src/daemon/config-watcher.ts +85 -3
  162. package/src/daemon/context-overflow-approval.ts +0 -1
  163. package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
  164. package/src/daemon/conversation-agent-loop.ts +179 -65
  165. package/src/daemon/conversation-attachments.ts +0 -1
  166. package/src/daemon/conversation-history.ts +4 -19
  167. package/src/daemon/conversation-lifecycle.ts +8 -14
  168. package/src/daemon/conversation-messaging.ts +3 -0
  169. package/src/daemon/conversation-process.ts +30 -8
  170. package/src/daemon/conversation-queue-manager.ts +8 -0
  171. package/src/daemon/conversation-runtime-assembly.ts +359 -308
  172. package/src/daemon/conversation-surfaces.ts +65 -0
  173. package/src/daemon/conversation-tool-setup.ts +44 -17
  174. package/src/daemon/conversation-workspace.ts +1 -2
  175. package/src/daemon/conversation.ts +19 -3
  176. package/src/daemon/date-context.ts +26 -53
  177. package/src/daemon/first-greeting.ts +1 -1
  178. package/src/daemon/handlers/conversations.ts +5 -7
  179. package/src/daemon/handlers/shared.test.ts +143 -0
  180. package/src/daemon/handlers/shared.ts +70 -5
  181. package/src/daemon/handlers/skills.ts +11 -18
  182. package/src/daemon/lifecycle.ts +220 -158
  183. package/src/daemon/message-types/conversations.ts +29 -6
  184. package/src/daemon/message-types/messages.ts +9 -2
  185. package/src/daemon/message-types/notifications.ts +12 -0
  186. package/src/daemon/message-types/schedules.ts +1 -0
  187. package/src/daemon/message-types/settings.ts +18 -0
  188. package/src/daemon/profiler-run-store.ts +557 -0
  189. package/src/daemon/server.ts +87 -10
  190. package/src/daemon/shutdown-handlers.ts +5 -0
  191. package/src/daemon/tool-side-effects.ts +23 -3
  192. package/src/daemon/transport-hints.ts +33 -0
  193. package/src/export/transcript-formatter.ts +148 -0
  194. package/src/filing/filing-service.ts +228 -0
  195. package/src/heartbeat/heartbeat-service.ts +96 -7
  196. package/src/index.ts +1 -1
  197. package/src/mcp/client.ts +6 -0
  198. package/src/mcp/mcp-oauth-provider.ts +149 -27
  199. package/src/memory/admin.ts +33 -32
  200. package/src/memory/app-store.ts +69 -0
  201. package/src/memory/conversation-bootstrap.ts +1 -1
  202. package/src/memory/conversation-crud.ts +151 -117
  203. package/src/memory/conversation-directories.ts +39 -0
  204. package/src/memory/conversation-group-migration.ts +66 -6
  205. package/src/memory/conversation-queries.ts +58 -12
  206. package/src/memory/conversation-title-service.ts +1 -0
  207. package/src/memory/db-init.ts +182 -376
  208. package/src/memory/embedding-local.ts +1 -1
  209. package/src/memory/graph/bootstrap.ts +75 -66
  210. package/src/memory/graph/capability-seed.ts +167 -17
  211. package/src/memory/graph/consolidation.ts +38 -4
  212. package/src/memory/graph/conversation-graph-memory.ts +133 -104
  213. package/src/memory/graph/extraction-job.ts +9 -4
  214. package/src/memory/graph/extraction.ts +66 -23
  215. package/src/memory/graph/graph-memory-state-store.ts +37 -0
  216. package/src/memory/graph/graph-search.ts +29 -15
  217. package/src/memory/graph/injection.ts +38 -8
  218. package/src/memory/graph/inspect.ts +12 -3
  219. package/src/memory/graph/retriever.ts +365 -262
  220. package/src/memory/graph/store.test.ts +48 -0
  221. package/src/memory/graph/store.ts +150 -11
  222. package/src/memory/graph/tool-handlers.ts +84 -209
  223. package/src/memory/graph/tools.ts +8 -52
  224. package/src/memory/graph/types.ts +24 -0
  225. package/src/memory/group-crud.ts +25 -9
  226. package/src/memory/job-handlers/cleanup.ts +44 -1
  227. package/src/memory/jobs-store.ts +70 -60
  228. package/src/memory/jobs-worker.ts +44 -28
  229. package/src/memory/llm-request-log-store.ts +96 -12
  230. package/src/memory/memory-recall-log-store.ts +49 -5
  231. package/src/memory/migrations/203-drop-memory-items-tables.ts +33 -1
  232. package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
  233. package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
  234. package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
  235. package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
  236. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
  237. package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
  238. package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
  239. package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
  240. package/src/memory/migrations/index.ts +8 -0
  241. package/src/memory/migrations/registry.ts +8 -0
  242. package/src/memory/schema/conversations.ts +14 -0
  243. package/src/memory/schema/infrastructure.ts +8 -1
  244. package/src/memory/schema/memory-core.ts +0 -51
  245. package/src/memory/schema/memory-graph.ts +15 -0
  246. package/src/memory/task-memory-cleanup.ts +30 -11
  247. package/src/messaging/provider.ts +1 -1
  248. package/src/notifications/broadcaster.ts +6 -0
  249. package/src/notifications/conversation-pairing.ts +12 -4
  250. package/src/notifications/copy-composer.ts +86 -0
  251. package/src/notifications/decision-engine.ts +35 -0
  252. package/src/notifications/emit-signal.ts +14 -0
  253. package/src/notifications/signal.ts +11 -0
  254. package/src/oauth/platform-connection.test.ts +2 -2
  255. package/src/oauth/seed-providers.ts +1 -0
  256. package/src/permissions/checker.ts +15 -4
  257. package/src/permissions/defaults.ts +7 -8
  258. package/src/permissions/permission-mode-store.ts +180 -0
  259. package/src/permissions/permission-mode.ts +31 -0
  260. package/src/permissions/prompter.ts +0 -2
  261. package/src/permissions/workspace-policy.ts +9 -0
  262. package/src/platform/client.ts +1 -1
  263. package/src/prompts/system-prompt.ts +59 -7
  264. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
  265. package/src/prompts/templates/BOOTSTRAP.md +76 -162
  266. package/src/prompts/templates/HEARTBEAT.md +3 -1
  267. package/src/prompts/templates/SOUL.md +30 -9
  268. package/src/prompts/templates/UPDATES.md +8 -0
  269. package/src/providers/anthropic/client.ts +107 -219
  270. package/src/runtime/assistant-event-hub.ts +22 -0
  271. package/src/runtime/auth/route-policy.ts +23 -0
  272. package/src/runtime/auth/token-service.ts +8 -0
  273. package/src/runtime/http-server.ts +32 -2
  274. package/src/runtime/http-types.ts +12 -1
  275. package/src/runtime/migrations/vbundle-builder.ts +389 -3
  276. package/src/runtime/migrations/vbundle-importer.ts +8 -6
  277. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
  278. package/src/runtime/routes/app-management-routes.ts +1 -11
  279. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
  280. package/src/runtime/routes/archive-utils.ts +29 -0
  281. package/src/runtime/routes/avatar-routes.ts +2 -9
  282. package/src/runtime/routes/btw-routes.ts +14 -1
  283. package/src/runtime/routes/conversation-analysis-routes.ts +185 -0
  284. package/src/runtime/routes/conversation-management-routes.ts +1 -14
  285. package/src/runtime/routes/conversation-query-routes.ts +49 -3
  286. package/src/runtime/routes/conversation-routes.ts +270 -44
  287. package/src/runtime/routes/group-routes.ts +22 -8
  288. package/src/runtime/routes/heartbeat-routes.ts +4 -10
  289. package/src/runtime/routes/identity-routes.ts +53 -18
  290. package/src/runtime/routes/llm-context-normalization.ts +14 -10
  291. package/src/runtime/routes/log-export/AGENTS.md +104 -0
  292. package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
  293. package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
  294. package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
  295. package/src/runtime/routes/log-export-routes.ts +41 -278
  296. package/src/runtime/routes/memory-item-routes.test.ts +168 -233
  297. package/src/runtime/routes/migration-routes.ts +18 -7
  298. package/src/runtime/routes/profiler-routes.ts +350 -0
  299. package/src/runtime/routes/schedule-routes.ts +27 -12
  300. package/src/runtime/routes/settings-routes.ts +95 -8
  301. package/src/runtime/routes/subagents-routes.ts +28 -7
  302. package/src/runtime/routes/user-route-dispatcher.ts +223 -0
  303. package/src/runtime/routes/user-routes.ts +41 -0
  304. package/src/runtime/routes/workspace-routes.ts +0 -1
  305. package/src/schedule/schedule-store.ts +30 -0
  306. package/src/schedule/scheduler.ts +45 -18
  307. package/src/skills/catalog-install.ts +10 -2
  308. package/src/skills/inline-command-runner.ts +12 -14
  309. package/src/skills/managed-store.ts +2 -2
  310. package/src/skills/skill-memory.ts +1 -293
  311. package/src/subagent/index.ts +13 -3
  312. package/src/subagent/manager.ts +308 -29
  313. package/src/subagent/types.ts +68 -0
  314. package/src/tasks/task-runner.ts +4 -4
  315. package/src/tools/apps/executors.ts +29 -4
  316. package/src/tools/filesystem/list.ts +93 -0
  317. package/src/tools/permission-checker.ts +78 -18
  318. package/src/tools/registry.ts +4 -0
  319. package/src/tools/schedule/create.ts +3 -0
  320. package/src/tools/schedule/list.ts +1 -0
  321. package/src/tools/schedule/update.ts +6 -0
  322. package/src/tools/secret-detection-handler.ts +0 -1
  323. package/src/tools/shared/filesystem/errors.ts +5 -0
  324. package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
  325. package/src/tools/shared/filesystem/types.ts +17 -0
  326. package/src/tools/shared/shell-output.ts +31 -2
  327. package/src/tools/skills/sandbox-runner.ts +3 -6
  328. package/src/tools/subagent/abort.ts +12 -2
  329. package/src/tools/subagent/message.ts +9 -2
  330. package/src/tools/subagent/notify-parent.ts +79 -0
  331. package/src/tools/subagent/read.ts +29 -8
  332. package/src/tools/subagent/resolve.ts +21 -0
  333. package/src/tools/subagent/spawn.ts +2 -0
  334. package/src/tools/subagent/status.ts +11 -1
  335. package/src/tools/system/avatar-generator.ts +3 -3
  336. package/src/tools/system/register.ts +23 -0
  337. package/src/tools/system/set-permission-mode.ts +103 -0
  338. package/src/tools/terminal/parser.ts +30 -5
  339. package/src/tools/terminal/safe-env.ts +16 -1
  340. package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
  341. package/src/tools/terminal/sandbox.ts +4 -1
  342. package/src/tools/terminal/shell.ts +3 -5
  343. package/src/tools/tool-manifest.ts +6 -0
  344. package/src/tools/types.ts +2 -3
  345. package/src/util/logger.ts +1 -1
  346. package/src/util/platform.ts +50 -17
  347. package/src/watcher/provider-types.ts +1 -1
  348. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
  349. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
  350. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
  351. package/src/workspace/migrations/029-seed-pkb.ts +85 -0
  352. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
  353. package/src/workspace/migrations/registry.ts +6 -0
  354. package/src/workspace/top-level-renderer.ts +5 -9
  355. package/src/__tests__/cli-memory.test.ts +0 -377
  356. package/src/__tests__/clipboard.test.ts +0 -88
  357. package/src/cli/cli-memory.ts +0 -179
  358. package/src/util/clipboard.ts +0 -34
@@ -2,14 +2,12 @@
2
2
  * HTTP route handler for exporting audit data and daemon log files.
3
3
  *
4
4
  * A single POST /v1/export endpoint allows clients (e.g. macOS Export Logs)
5
- * to retrieve audit database records, daemon log files, workspace contents,
6
- * and a sanitized config snapshot as a tar.gz archive.
5
+ * to retrieve audit database records, daemon log files, and a sanitized
6
+ * config snapshot as a tar.gz archive.
7
7
  */
8
8
 
9
- import { spawnSync } from "node:child_process";
10
9
  import {
11
10
  existsSync,
12
- lstatSync,
13
11
  mkdirSync,
14
12
  mkdtempSync,
15
13
  readdirSync,
@@ -19,7 +17,7 @@ import {
19
17
  writeFileSync,
20
18
  } from "node:fs";
21
19
  import { tmpdir } from "node:os";
22
- import { join, relative } from "node:path";
20
+ import { join } from "node:path";
23
21
 
24
22
  import { and, desc, eq, gte, lte } from "drizzle-orm";
25
23
  import { z } from "zod";
@@ -31,25 +29,23 @@ import {
31
29
  messages,
32
30
  toolInvocations,
33
31
  } from "../../memory/schema.js";
34
- import { getLogger } from "../../util/logger.js";
32
+ import { getLogger, LOG_FILE_PATTERN } from "../../util/logger.js";
35
33
  import {
36
34
  getDaemonStderrLogPath,
37
35
  getDataDir,
38
36
  getWorkspaceConfigPath,
39
- getWorkspaceDir,
40
37
  } from "../../util/platform.js";
41
38
  import { APP_VERSION, COMMIT_SHA } from "../../version.js";
42
39
  import { httpError } from "../http-errors.js";
43
40
  import type { RouteDefinition } from "../http-router.js";
41
+ import { createTarGz } from "./archive-utils.js";
42
+ import { collectWorkspaceData } from "./log-export/workspace-allowlist.js";
44
43
 
45
44
  const log = getLogger("log-export-routes");
46
45
 
47
46
  /** Maximum total payload size for log file contents (10 MB). */
48
47
  const MAX_LOG_PAYLOAD_BYTES = 10 * 1024 * 1024;
49
48
 
50
- /** Maximum compressed archive size before pruning workspace directories (50 MB). */
51
- const MAX_ARCHIVE_BYTES = 50 * 1024 * 1024;
52
-
53
49
  interface ExportRequestBody {
54
50
  auditLimit?: number;
55
51
  conversationId?: string; // scope to a single conversation
@@ -58,14 +54,14 @@ interface ExportRequestBody {
58
54
  }
59
55
 
60
56
  /**
61
- * Collect audit data, daemon log files, workspace contents, and a sanitized
62
- * config snapshot, then package everything into a tar.gz archive.
57
+ * Collect audit data, daemon log files, and a sanitized config snapshot,
58
+ * then package everything into a tar.gz archive.
63
59
  *
64
60
  * Archive layout:
65
- * audit-data.json — tool invocation records
66
- * config-snapshot.json — sanitized workspace config
67
- * daemon-logs/<name> — daemon log files
68
- * workspace/<relpath> — workspace file tree
61
+ * audit-data.json — tool invocation records
62
+ * config-snapshot.json — sanitized workspace config
63
+ * daemon-logs/<name> — daemon log files
64
+ * workspace/conversations/<dir>/ allowlisted workspace data (see ./log-export/AGENTS.md)
69
65
  */
70
66
  async function handleExport(body: ExportRequestBody): Promise<Response> {
71
67
  const staging = mkdtempSync(join(tmpdir(), "vellum-export-"));
@@ -167,9 +163,19 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
167
163
 
168
164
  const logsDir = join(getDataDir(), "logs");
169
165
  const collectedLogFiles: string[] = [];
166
+ const startDate = startTime ? new Date(startTime) : undefined;
167
+ const endDate = endTime ? new Date(endTime) : undefined;
170
168
  if (existsSync(logsDir)) {
171
169
  const entries = readdirSync(logsDir);
172
170
  for (const entry of entries) {
171
+ // Filter dated log files by time range
172
+ const dateMatch = entry.match(LOG_FILE_PATTERN);
173
+ if (dateMatch && (startDate || endDate)) {
174
+ const fileDate = new Date(dateMatch[1] + "T23:59:59.999Z"); // end of day
175
+ const fileDateStart = new Date(dateMatch[1] + "T00:00:00.000Z");
176
+ if (startDate && fileDate < startDate) continue; // entire day is before range
177
+ if (endDate && fileDateStart > endDate) continue; // entire day is after range
178
+ }
173
179
  const filePath = join(logsDir, entry);
174
180
  try {
175
181
  const stat = statSync(filePath);
@@ -237,6 +243,17 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
237
243
  }
238
244
  }
239
245
 
246
+ // --- Workspace allowlist ---
247
+ // Includes specific subpaths from <workspace>/ governed by the rules in
248
+ // ./log-export/AGENTS.md. Honors the same time + conversation filters as
249
+ // the rest of the export.
250
+ const workspaceResult = collectWorkspaceData({
251
+ staging,
252
+ conversationId: conversationId || undefined,
253
+ startTime: startTime || undefined,
254
+ endTime: endTime || undefined,
255
+ });
256
+
240
257
  // --- Sanitized config snapshot ---
241
258
  const configSnapshot = readSanitizedConfig();
242
259
  if (configSnapshot) {
@@ -247,20 +264,6 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
247
264
  );
248
265
  }
249
266
 
250
- // --- Workspace files (skip for conversation-scoped exports) ---
251
- let workspaceFileCount = 0;
252
- if (!conversationId) {
253
- const workspaceFiles = collectWorkspaceFiles();
254
- const workspaceDir = join(staging, "workspace");
255
- mkdirSync(workspaceDir, { recursive: true });
256
- for (const [relPath, content] of Object.entries(workspaceFiles)) {
257
- const dest = join(workspaceDir, relPath);
258
- mkdirSync(join(dest, ".."), { recursive: true });
259
- writeFileSync(dest, content, "utf-8");
260
- }
261
- workspaceFileCount = Object.keys(workspaceFiles).length;
262
- }
263
-
264
267
  // --- Export manifest ---
265
268
  const manifest = conversationId
266
269
  ? {
@@ -290,64 +293,17 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
290
293
  logFileCount,
291
294
  totalBytes,
292
295
  hasConfig: configSnapshot !== undefined,
293
- workspaceFileCount,
294
296
  conversationId: conversationId ?? null,
297
+ workspaceEntries: workspaceResult.entries.length,
298
+ workspaceBytes: workspaceResult.totalBytes,
295
299
  },
296
300
  "Export collected, creating tar.gz archive",
297
301
  );
298
302
 
299
- // --- Create tar.gz archive, pruning workspace dirs if too large ---
300
- const excludedDirs: string[] = [];
301
- let archiveBytes = createTarGz(staging);
302
-
303
- while (!archiveBytes) {
304
- // Conversation-scoped exports have no workspace directory to prune —
305
- // if the archive still exceeds the size limit, report a clear error.
306
- if (conversationId) {
307
- log.error(
308
- "Conversation-scoped export exceeds archive size limit with no workspace dirs to prune",
309
- );
310
- return httpError(
311
- "INTERNAL_ERROR",
312
- "Conversation export exceeds the maximum archive size",
313
- 500,
314
- );
315
- }
316
-
317
- // Find the largest top-level directory under workspace/ and remove it
318
- const wsDir = join(staging, "workspace");
319
- const largest = findLargestSubdirectory(wsDir);
320
- if (!largest) {
321
- log.error("tar command failed and no workspace dirs to prune");
322
- return httpError("INTERNAL_ERROR", "Failed to create archive", 500);
323
- }
324
-
325
- log.warn(
326
- { dir: largest.name, bytes: largest.bytes },
327
- "Archive exceeds size limit, removing largest workspace directory",
328
- );
329
- excludedDirs.push(
330
- `workspace/${largest.name} (${formatBytes(largest.bytes)})`,
331
- );
332
- rmSync(join(wsDir, largest.name), { recursive: true, force: true });
333
- archiveBytes = createTarGz(staging);
334
- }
335
-
336
- if (excludedDirs.length > 0) {
337
- const errorLines = [
338
- "The following workspace directories were excluded because the archive exceeded the size limit:",
339
- "",
340
- ...excludedDirs.map((d) => ` - ${d}`),
341
- "",
342
- "Use the streaming export endpoint for full workspace exports.",
343
- ];
344
- writeFileSync(join(staging, "error.log"), errorLines.join("\n"), "utf-8");
345
-
346
- // Re-create the archive now that error.log is included
347
- const withErrorLog = createTarGz(staging);
348
- if (withErrorLog) {
349
- archiveBytes = withErrorLog;
350
- }
303
+ // --- Create tar.gz archive ---
304
+ const archiveBytes = createTarGz(staging);
305
+ if (!archiveBytes) {
306
+ return httpError("INTERNAL_ERROR", "Failed to create archive", 500);
351
307
  }
352
308
 
353
309
  return new Response(archiveBytes, {
@@ -371,199 +327,6 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
371
327
  }
372
328
  }
373
329
 
374
- /**
375
- * Attempts to create a tar.gz archive of `staging` into a Buffer.
376
- * Returns the Buffer on success, or `undefined` if the archive exceeds
377
- * the size limit or tar otherwise fails.
378
- */
379
- function createTarGz(staging: string): ArrayBuffer | undefined {
380
- const proc = spawnSync("tar", ["czf", "-", "-C", staging, "."], {
381
- maxBuffer: MAX_ARCHIVE_BYTES,
382
- timeout: 30_000,
383
- });
384
- if (proc.status !== 0) return undefined;
385
- const buf = Buffer.isBuffer(proc.stdout)
386
- ? proc.stdout
387
- : Buffer.from(proc.stdout);
388
- return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
389
- }
390
-
391
- /**
392
- * Returns the name and total byte size of the largest top-level subdirectory
393
- * inside `dir`, or `undefined` if `dir` has no subdirectories.
394
- */
395
- function findLargestSubdirectory(
396
- dir: string,
397
- ): { name: string; bytes: number } | undefined {
398
- if (!existsSync(dir)) return undefined;
399
-
400
- let largest: { name: string; bytes: number } | undefined;
401
-
402
- for (const entry of readdirSync(dir)) {
403
- const fullPath = join(dir, entry);
404
- try {
405
- if (!statSync(fullPath).isDirectory()) continue;
406
- } catch {
407
- continue;
408
- }
409
- const bytes = directorySize(fullPath);
410
- if (!largest || bytes > largest.bytes) {
411
- largest = { name: entry, bytes };
412
- }
413
- }
414
-
415
- return largest;
416
- }
417
-
418
- /** Recursively sums the byte size of all files in `dir`. */
419
- function directorySize(dir: string): number {
420
- let total = 0;
421
- try {
422
- for (const entry of readdirSync(dir)) {
423
- const fullPath = join(dir, entry);
424
- try {
425
- const stat = statSync(fullPath);
426
- if (stat.isDirectory()) {
427
- total += directorySize(fullPath);
428
- } else if (stat.isFile()) {
429
- total += stat.size;
430
- }
431
- } catch {
432
- // skip
433
- }
434
- }
435
- } catch {
436
- // skip
437
- }
438
- return total;
439
- }
440
-
441
- /** Formats a byte count as a human-readable string (e.g. "12.3 MB"). */
442
- function formatBytes(bytes: number): string {
443
- if (bytes < 1024) return `${bytes} B`;
444
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
445
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
446
- }
447
-
448
- /** Directory prefixes to skip when collecting workspace files. */
449
- const WORKSPACE_SKIP_DIRS = new Set([
450
- "embedding-models",
451
- "data/qdrant",
452
- "data/attachments",
453
- "data/sounds",
454
- "conversations",
455
- "signals",
456
- "deprecated",
457
- ]);
458
-
459
- /** Files at the workspace root to skip (already covered by sanitized fields). */
460
- const WORKSPACE_SKIP_ROOT_FILES = new Set(["config.json"]);
461
-
462
- /** Maximum cumulative size for workspace file contents (10 MB). */
463
- const MAX_WORKSPACE_PAYLOAD_BYTES = 10 * 1024 * 1024;
464
-
465
- /**
466
- * Recursively collects files from the workspace directory into a
467
- * `Record<string, string>` map of relative path to content.
468
- *
469
- * - Skips `config.json` at the workspace root (already exported as a
470
- * sanitized `configSnapshot`; the raw file contains secrets).
471
- * - Skips symlinks to prevent reading files outside the workspace.
472
- * - Skips directories in `WORKSPACE_SKIP_DIRS`.
473
- * - For `.db` files, shells out to `sqlite3 <path> .dump` and stores the
474
- * SQL text output with a `.sql` suffix appended to the key.
475
- * - Skips binary files (detected via null-byte heuristic).
476
- * - Stops collecting once `MAX_WORKSPACE_PAYLOAD_BYTES` is reached.
477
- */
478
- function collectWorkspaceFiles(): Record<string, string> {
479
- const wsDir = getWorkspaceDir();
480
- if (!existsSync(wsDir)) return {};
481
-
482
- const result: Record<string, string> = {};
483
- let totalBytes = 0;
484
-
485
- function walk(dir: string): void {
486
- let entries: string[];
487
- try {
488
- entries = readdirSync(dir);
489
- } catch {
490
- return;
491
- }
492
-
493
- for (const entry of entries) {
494
- const fullPath = join(dir, entry);
495
- const relPath = relative(wsDir, fullPath);
496
-
497
- // Check if this path falls under a skipped directory prefix
498
- if (
499
- [...WORKSPACE_SKIP_DIRS].some(
500
- (prefix) => relPath === prefix || relPath.startsWith(prefix + "/"),
501
- )
502
- ) {
503
- continue;
504
- }
505
-
506
- // Skip root-level files that are already exported separately
507
- if (dir === wsDir && WORKSPACE_SKIP_ROOT_FILES.has(entry)) {
508
- continue;
509
- }
510
-
511
- try {
512
- // Use lstatSync to avoid following symlinks
513
- const stat = lstatSync(fullPath);
514
-
515
- // Skip symlinks — they could point outside the workspace
516
- if (stat.isSymbolicLink()) continue;
517
-
518
- if (stat.isDirectory()) {
519
- walk(fullPath);
520
- continue;
521
- }
522
- if (!stat.isFile()) continue;
523
-
524
- // SQLite DB handling: dump as SQL text, then enforce size cap
525
- if (entry.endsWith(".db")) {
526
- // Skip the dump entirely if the budget is already exhausted
527
- if (totalBytes >= MAX_WORKSPACE_PAYLOAD_BYTES) continue;
528
- try {
529
- const proc = spawnSync("sqlite3", [fullPath, ".dump"], {
530
- timeout: 10_000,
531
- });
532
- if (proc.status === 0 && proc.stdout) {
533
- const output =
534
- proc.stdout instanceof Buffer
535
- ? proc.stdout.toString("utf-8")
536
- : String(proc.stdout);
537
- const outputBytes = Buffer.byteLength(output, "utf-8");
538
- if (totalBytes + outputBytes > MAX_WORKSPACE_PAYLOAD_BYTES)
539
- continue;
540
- result[relPath + ".sql"] = output;
541
- totalBytes += outputBytes;
542
- }
543
- } catch {
544
- // Skip if dump fails
545
- }
546
- continue;
547
- }
548
-
549
- // Enforce cumulative size cap for non-DB files
550
- if (totalBytes + stat.size > MAX_WORKSPACE_PAYLOAD_BYTES) continue;
551
-
552
- // Read as UTF-8 and skip binary files (null-byte heuristic)
553
- const content = readFileSync(fullPath, "utf-8");
554
- if (content.includes("\0")) continue;
555
- result[relPath] = content;
556
- totalBytes += stat.size;
557
- } catch {
558
- // Skip unreadable files
559
- }
560
- }
561
- }
562
-
563
- walk(wsDir);
564
- return result;
565
- }
566
-
567
330
  /**
568
331
  * Replaces a string value with a presence flag: "(set)" if truthy, "(empty)" otherwise.
569
332
  */
@@ -689,7 +452,7 @@ export function logExportRouteDefinitions(): RouteDefinition[] {
689
452
  policyKey: "export",
690
453
  summary: "Export logs and audit data",
691
454
  description:
692
- "Export audit records, assistant logs, workspace contents, and config as a tar.gz archive.",
455
+ "Export audit records, assistant logs, and config as a tar.gz archive.",
693
456
  tags: ["export"],
694
457
  requestBody: exportRequestBody,
695
458
  handler: async ({ req }) => {
@@ -703,7 +466,7 @@ export function logExportRouteDefinitions(): RouteDefinition[] {
703
466
  policyKey: "export",
704
467
  summary: "Export logs and audit data (alias)",
705
468
  description:
706
- "Alias for /v1/export. Export audit records, assistant logs, workspace contents, and config as a tar.gz archive.",
469
+ "Alias for /v1/export. Export audit records, assistant logs, and config as a tar.gz archive.",
707
470
  tags: ["export"],
708
471
  requestBody: exportRequestBody,
709
472
  handler: async ({ req }) => {