@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
@@ -0,0 +1,562 @@
1
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
4
+
5
+ import { Command } from "commander";
6
+
7
+ import { getWorkspaceRoutesDir } from "../../../util/platform.js";
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // Mock state
11
+ // ---------------------------------------------------------------------------
12
+
13
+ let mockPublicBaseUrl: string | null = null;
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Mocks
17
+ // ---------------------------------------------------------------------------
18
+
19
+ mock.module("../../../config/loader.js", () => ({
20
+ getConfig: () => ({
21
+ ingress: mockPublicBaseUrl
22
+ ? { publicBaseUrl: mockPublicBaseUrl }
23
+ : undefined,
24
+ }),
25
+ }));
26
+
27
+ mock.module("../../../inbound/public-ingress-urls.js", () => ({
28
+ getPublicBaseUrl: (config: { ingress?: { publicBaseUrl?: string } }) => {
29
+ const url = config.ingress?.publicBaseUrl;
30
+ if (!url) throw new Error("No public base URL configured");
31
+ return url;
32
+ },
33
+ }));
34
+
35
+ mock.module("../../../util/logger.js", () => ({
36
+ getLogger: () => ({
37
+ info: () => {},
38
+ warn: () => {},
39
+ error: () => {},
40
+ debug: () => {},
41
+ }),
42
+ getCliLogger: () => ({
43
+ info: () => {},
44
+ warn: () => {},
45
+ error: () => {},
46
+ debug: () => {},
47
+ }),
48
+ }));
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // Import module under test (after mocks are registered)
52
+ // ---------------------------------------------------------------------------
53
+
54
+ const { registerRoutesCommand } = await import("../routes.js");
55
+
56
+ // ---------------------------------------------------------------------------
57
+ // Test helper
58
+ // ---------------------------------------------------------------------------
59
+
60
+ async function runCommand(
61
+ args: string[],
62
+ ): Promise<{ stdout: string; exitCode: number }> {
63
+ const originalStdoutWrite = process.stdout.write.bind(process.stdout);
64
+ const originalStderrWrite = process.stderr.write.bind(process.stderr);
65
+ const originalConsoleLog = console.log.bind(console);
66
+ const stdoutChunks: string[] = [];
67
+
68
+ process.stdout.write = ((chunk: unknown) => {
69
+ stdoutChunks.push(typeof chunk === "string" ? chunk : String(chunk));
70
+ return true;
71
+ }) as typeof process.stdout.write;
72
+
73
+ process.stderr.write = (() => true) as typeof process.stderr.write;
74
+
75
+ console.log = (...logArgs: unknown[]) => {
76
+ stdoutChunks.push(
77
+ logArgs.map((a) => (typeof a === "string" ? a : String(a))).join(" ") +
78
+ "\n",
79
+ );
80
+ };
81
+
82
+ process.exitCode = 0;
83
+
84
+ try {
85
+ const program = new Command();
86
+ program.exitOverride();
87
+ program.configureOutput({
88
+ writeErr: () => {},
89
+ writeOut: (str: string) => stdoutChunks.push(str),
90
+ });
91
+ registerRoutesCommand(program);
92
+ await program.parseAsync(["node", "assistant", ...args]);
93
+ } catch {
94
+ if (process.exitCode === 0) process.exitCode = 1;
95
+ } finally {
96
+ process.stdout.write = originalStdoutWrite;
97
+ process.stderr.write = originalStderrWrite;
98
+ console.log = originalConsoleLog;
99
+ }
100
+
101
+ const exitCode = process.exitCode ?? 0;
102
+ process.exitCode = 0;
103
+
104
+ return { exitCode, stdout: stdoutChunks.join("") };
105
+ }
106
+
107
+ // ---------------------------------------------------------------------------
108
+ // Helpers for writing handler files into the workspace routes dir
109
+ // ---------------------------------------------------------------------------
110
+
111
+ let routesDir: string;
112
+
113
+ function writeHandler(relativePath: string, content: string): void {
114
+ const fullPath = join(routesDir, relativePath);
115
+ const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
116
+ mkdirSync(dir, { recursive: true });
117
+ writeFileSync(fullPath, content, "utf-8");
118
+ }
119
+
120
+ // ---------------------------------------------------------------------------
121
+ // Setup / teardown
122
+ // ---------------------------------------------------------------------------
123
+
124
+ beforeEach(() => {
125
+ routesDir = getWorkspaceRoutesDir();
126
+ mkdirSync(routesDir, { recursive: true });
127
+ mockPublicBaseUrl = null;
128
+ process.exitCode = 0;
129
+ });
130
+
131
+ afterEach(() => {
132
+ try {
133
+ rmSync(routesDir, { recursive: true, force: true });
134
+ } catch {
135
+ /* best-effort cleanup */
136
+ }
137
+ });
138
+
139
+ // ---------------------------------------------------------------------------
140
+ // routes list
141
+ // ---------------------------------------------------------------------------
142
+
143
+ describe("assistant routes list", () => {
144
+ test("empty routes dir returns zero routes in JSON", async () => {
145
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
146
+ expect(exitCode).toBe(0);
147
+ const parsed = JSON.parse(stdout);
148
+ expect(parsed.ok).toBe(true);
149
+ expect(parsed.routes).toEqual([]);
150
+ });
151
+
152
+ test("empty routes dir shows guidance in human output", async () => {
153
+ const { exitCode } = await runCommand(["routes", "list"]);
154
+ expect(exitCode).toBe(0);
155
+ });
156
+
157
+ test("discovers a single GET handler", async () => {
158
+ writeHandler(
159
+ "status.ts",
160
+ `export async function GET(req: Request) { return new Response("ok"); }`,
161
+ );
162
+
163
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
164
+ expect(exitCode).toBe(0);
165
+ const parsed = JSON.parse(stdout);
166
+ expect(parsed.ok).toBe(true);
167
+ expect(parsed.routes).toHaveLength(1);
168
+ expect(parsed.routes[0].routePath).toBe("/x/status");
169
+ expect(parsed.routes[0].methods).toEqual(["GET"]);
170
+ });
171
+
172
+ test("discovers multiple routes sorted alphabetically", async () => {
173
+ writeHandler(
174
+ "zebra.ts",
175
+ `export function GET() { return new Response("z"); }`,
176
+ );
177
+ writeHandler(
178
+ "alpha.ts",
179
+ `export function POST() { return new Response("a"); }`,
180
+ );
181
+
182
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
183
+ expect(exitCode).toBe(0);
184
+ const parsed = JSON.parse(stdout);
185
+ expect(parsed.routes).toHaveLength(2);
186
+ expect(parsed.routes[0].routePath).toBe("/x/alpha");
187
+ expect(parsed.routes[1].routePath).toBe("/x/zebra");
188
+ });
189
+
190
+ test("discovers multi-method handler", async () => {
191
+ writeHandler(
192
+ "items.ts",
193
+ [
194
+ `export function GET() { return new Response("list"); }`,
195
+ `export function POST() { return new Response("create"); }`,
196
+ `export function DELETE() { return new Response("remove"); }`,
197
+ ].join("\n"),
198
+ );
199
+
200
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
201
+ expect(exitCode).toBe(0);
202
+ const parsed = JSON.parse(stdout);
203
+ expect(parsed.routes[0].methods).toEqual(["GET", "POST", "DELETE"]);
204
+ });
205
+
206
+ test("discovers index file as directory route", async () => {
207
+ writeHandler(
208
+ "my-app/index.ts",
209
+ `export function GET() { return new Response("app"); }`,
210
+ );
211
+
212
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
213
+ expect(exitCode).toBe(0);
214
+ const parsed = JSON.parse(stdout);
215
+ expect(parsed.routes).toHaveLength(1);
216
+ expect(parsed.routes[0].routePath).toBe("/x/my-app");
217
+ });
218
+
219
+ test("discovers subdirectory routes", async () => {
220
+ writeHandler(
221
+ "api/v1/users.ts",
222
+ `export function GET() { return new Response("users"); }`,
223
+ );
224
+
225
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
226
+ expect(exitCode).toBe(0);
227
+ const parsed = JSON.parse(stdout);
228
+ expect(parsed.routes[0].routePath).toBe("/x/api/v1/users");
229
+ });
230
+
231
+ test("discovers .js handlers", async () => {
232
+ writeHandler(
233
+ "health.js",
234
+ `export function GET() { return new Response("ok"); }`,
235
+ );
236
+
237
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
238
+ expect(exitCode).toBe(0);
239
+ const parsed = JSON.parse(stdout);
240
+ expect(parsed.routes).toHaveLength(1);
241
+ expect(parsed.routes[0].routePath).toBe("/x/health");
242
+ });
243
+
244
+ test("extracts description export", async () => {
245
+ writeHandler(
246
+ "submit.ts",
247
+ [
248
+ `export const description = "Form submission handler";`,
249
+ `export function POST() { return new Response("ok"); }`,
250
+ ].join("\n"),
251
+ );
252
+
253
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
254
+ expect(exitCode).toBe(0);
255
+ const parsed = JSON.parse(stdout);
256
+ expect(parsed.routes[0].description).toBe("Form submission handler");
257
+ });
258
+
259
+ test("null description when not exported", async () => {
260
+ writeHandler(
261
+ "simple.ts",
262
+ `export function GET() { return new Response("ok"); }`,
263
+ );
264
+
265
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
266
+ expect(exitCode).toBe(0);
267
+ const parsed = JSON.parse(stdout);
268
+ expect(parsed.routes[0].description).toBeNull();
269
+ });
270
+
271
+ test("includes publicUrl when public base URL is configured", async () => {
272
+ mockPublicBaseUrl = "https://example.ngrok-free.app/v1/assistants/asst_xyz";
273
+ writeHandler(
274
+ "status.ts",
275
+ `export function GET() { return new Response("ok"); }`,
276
+ );
277
+
278
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
279
+ expect(exitCode).toBe(0);
280
+ const parsed = JSON.parse(stdout);
281
+ expect(parsed.routes[0].publicUrl).toBe(
282
+ "https://example.ngrok-free.app/v1/assistants/asst_xyz/x/status",
283
+ );
284
+ });
285
+
286
+ test("publicUrl is null when no public base URL configured", async () => {
287
+ mockPublicBaseUrl = null;
288
+ writeHandler(
289
+ "status.ts",
290
+ `export function GET() { return new Response("ok"); }`,
291
+ );
292
+
293
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
294
+ expect(exitCode).toBe(0);
295
+ const parsed = JSON.parse(stdout);
296
+ expect(parsed.routes[0].publicUrl).toBeNull();
297
+ });
298
+
299
+ test("ignores non-handler files", async () => {
300
+ writeHandler("readme.md", "# Routes\nDocumentation file");
301
+ writeHandler(
302
+ "handler.ts",
303
+ `export function GET() { return new Response("ok"); }`,
304
+ );
305
+
306
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
307
+ expect(exitCode).toBe(0);
308
+ const parsed = JSON.parse(stdout);
309
+ expect(parsed.routes).toHaveLength(1);
310
+ expect(parsed.routes[0].routePath).toBe("/x/handler");
311
+ });
312
+
313
+ test("human output runs without error for populated routes", async () => {
314
+ writeHandler(
315
+ "status.ts",
316
+ `export function GET() { return new Response("ok"); }`,
317
+ );
318
+
319
+ const { exitCode } = await runCommand(["routes", "list"]);
320
+ expect(exitCode).toBe(0);
321
+ });
322
+
323
+ test("root index file maps to /x/", async () => {
324
+ writeHandler(
325
+ "index.ts",
326
+ `export function GET() { return new Response("root"); }`,
327
+ );
328
+
329
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
330
+ expect(exitCode).toBe(0);
331
+ const parsed = JSON.parse(stdout);
332
+ expect(parsed.routes).toHaveLength(1);
333
+ expect(parsed.routes[0].routePath).toBe("/x/");
334
+ });
335
+
336
+ test("JSON output includes filePath relative to routes dir", async () => {
337
+ writeHandler(
338
+ "api/submit.ts",
339
+ `export function POST() { return new Response("ok"); }`,
340
+ );
341
+
342
+ const { exitCode, stdout } = await runCommand(["routes", "list", "--json"]);
343
+ expect(exitCode).toBe(0);
344
+ const parsed = JSON.parse(stdout);
345
+ expect(parsed.routes[0].filePath).toBe("api/submit.ts");
346
+ });
347
+ });
348
+
349
+ // ---------------------------------------------------------------------------
350
+ // routes inspect
351
+ // ---------------------------------------------------------------------------
352
+
353
+ describe("assistant routes inspect", () => {
354
+ test("inspects a handler by route path (JSON)", async () => {
355
+ writeHandler(
356
+ "status.ts",
357
+ [
358
+ `export const description = "Health check endpoint";`,
359
+ `export function GET() { return new Response("ok"); }`,
360
+ `export function POST() { return new Response("created"); }`,
361
+ ].join("\n"),
362
+ );
363
+
364
+ const { exitCode, stdout } = await runCommand([
365
+ "routes",
366
+ "inspect",
367
+ "status",
368
+ "--json",
369
+ ]);
370
+ expect(exitCode).toBe(0);
371
+ const parsed = JSON.parse(stdout);
372
+ expect(parsed.ok).toBe(true);
373
+ expect(parsed.route.routePath).toBe("/x/status");
374
+ expect(parsed.route.methods).toEqual(["GET", "POST"]);
375
+ expect(parsed.route.description).toBe("Health check endpoint");
376
+ expect(parsed.route.filePath).toContain("status.ts");
377
+ expect(parsed.route.fileSize).toBeGreaterThan(0);
378
+ expect(parsed.route.modifiedAt).toBeTruthy();
379
+ });
380
+
381
+ test("inspect resolves index file convention", async () => {
382
+ writeHandler(
383
+ "dashboard/index.ts",
384
+ `export function GET() { return new Response("dashboard"); }`,
385
+ );
386
+
387
+ const { exitCode, stdout } = await runCommand([
388
+ "routes",
389
+ "inspect",
390
+ "dashboard",
391
+ "--json",
392
+ ]);
393
+ expect(exitCode).toBe(0);
394
+ const parsed = JSON.parse(stdout);
395
+ expect(parsed.ok).toBe(true);
396
+ expect(parsed.route.routePath).toBe("/x/dashboard");
397
+ expect(parsed.route.methods).toEqual(["GET"]);
398
+ expect(parsed.route.filePath).toContain("index.ts");
399
+ });
400
+
401
+ test("inspect resolves .js files", async () => {
402
+ writeHandler(
403
+ "legacy.js",
404
+ `export function POST() { return new Response("ok"); }`,
405
+ );
406
+
407
+ const { exitCode, stdout } = await runCommand([
408
+ "routes",
409
+ "inspect",
410
+ "legacy",
411
+ "--json",
412
+ ]);
413
+ expect(exitCode).toBe(0);
414
+ const parsed = JSON.parse(stdout);
415
+ expect(parsed.ok).toBe(true);
416
+ expect(parsed.route.filePath).toContain("legacy.js");
417
+ });
418
+
419
+ test("inspect includes publicUrl when configured", async () => {
420
+ mockPublicBaseUrl = "https://example.com/v1/assistants/asst_1";
421
+ writeHandler(
422
+ "submit.ts",
423
+ `export function POST() { return new Response("ok"); }`,
424
+ );
425
+
426
+ const { exitCode, stdout } = await runCommand([
427
+ "routes",
428
+ "inspect",
429
+ "submit",
430
+ "--json",
431
+ ]);
432
+ expect(exitCode).toBe(0);
433
+ const parsed = JSON.parse(stdout);
434
+ expect(parsed.route.publicUrl).toBe(
435
+ "https://example.com/v1/assistants/asst_1/x/submit",
436
+ );
437
+ });
438
+
439
+ test("inspect publicUrl is null when not configured", async () => {
440
+ mockPublicBaseUrl = null;
441
+ writeHandler(
442
+ "submit.ts",
443
+ `export function POST() { return new Response("ok"); }`,
444
+ );
445
+
446
+ const { exitCode, stdout } = await runCommand([
447
+ "routes",
448
+ "inspect",
449
+ "submit",
450
+ "--json",
451
+ ]);
452
+ expect(exitCode).toBe(0);
453
+ const parsed = JSON.parse(stdout);
454
+ expect(parsed.route.publicUrl).toBeNull();
455
+ });
456
+
457
+ test("inspect returns error for missing handler (JSON)", async () => {
458
+ const { exitCode, stdout } = await runCommand([
459
+ "routes",
460
+ "inspect",
461
+ "nonexistent",
462
+ "--json",
463
+ ]);
464
+ expect(exitCode).toBe(1);
465
+ const parsed = JSON.parse(stdout);
466
+ expect(parsed.ok).toBe(false);
467
+ expect(parsed.error).toContain("No handler file found");
468
+ expect(parsed.error).toContain("nonexistent");
469
+ });
470
+
471
+ test("inspect returns error for missing handler (human output)", async () => {
472
+ const { exitCode } = await runCommand(["routes", "inspect", "nonexistent"]);
473
+ expect(exitCode).toBe(1);
474
+ });
475
+
476
+ test("inspect handles subdirectory routes", async () => {
477
+ writeHandler(
478
+ "api/v2/users.ts",
479
+ `export function GET() { return new Response("users"); }`,
480
+ );
481
+
482
+ const { exitCode, stdout } = await runCommand([
483
+ "routes",
484
+ "inspect",
485
+ "api/v2/users",
486
+ "--json",
487
+ ]);
488
+ expect(exitCode).toBe(0);
489
+ const parsed = JSON.parse(stdout);
490
+ expect(parsed.ok).toBe(true);
491
+ expect(parsed.route.routePath).toBe("/x/api/v2/users");
492
+ });
493
+
494
+ test("inspect human output runs without error", async () => {
495
+ writeHandler(
496
+ "check.ts",
497
+ `export function GET() { return new Response("ok"); }`,
498
+ );
499
+
500
+ const { exitCode } = await runCommand(["routes", "inspect", "check"]);
501
+ expect(exitCode).toBe(0);
502
+ });
503
+
504
+ test("inspect shows handler with no exported methods", async () => {
505
+ writeHandler("empty.ts", `export const description = "Placeholder";`);
506
+
507
+ const { exitCode, stdout } = await runCommand([
508
+ "routes",
509
+ "inspect",
510
+ "empty",
511
+ "--json",
512
+ ]);
513
+ expect(exitCode).toBe(0);
514
+ const parsed = JSON.parse(stdout);
515
+ expect(parsed.route.methods).toEqual([]);
516
+ expect(parsed.route.description).toBe("Placeholder");
517
+ });
518
+
519
+ test("inspect prefers direct file over index file", async () => {
520
+ writeHandler(
521
+ "ambiguous.ts",
522
+ `export function GET() { return new Response("direct"); }`,
523
+ );
524
+ writeHandler(
525
+ "ambiguous/index.ts",
526
+ `export function POST() { return new Response("index"); }`,
527
+ );
528
+
529
+ const { exitCode, stdout } = await runCommand([
530
+ "routes",
531
+ "inspect",
532
+ "ambiguous",
533
+ "--json",
534
+ ]);
535
+ expect(exitCode).toBe(0);
536
+ const parsed = JSON.parse(stdout);
537
+ // Direct file should be preferred over index
538
+ expect(parsed.route.methods).toEqual(["GET"]);
539
+ });
540
+
541
+ test("inspect prefers .ts over .js", async () => {
542
+ writeHandler(
543
+ "both.ts",
544
+ `export function GET() { return new Response("ts"); }`,
545
+ );
546
+ writeHandler(
547
+ "both.js",
548
+ `export function POST() { return new Response("js"); }`,
549
+ );
550
+
551
+ const { exitCode, stdout } = await runCommand([
552
+ "routes",
553
+ "inspect",
554
+ "both",
555
+ "--json",
556
+ ]);
557
+ expect(exitCode).toBe(0);
558
+ const parsed = JSON.parse(stdout);
559
+ // .ts is checked first in HANDLER_EXTENSIONS
560
+ expect(parsed.route.methods).toEqual(["GET"]);
561
+ });
562
+ });
@@ -35,7 +35,7 @@ The avatar system supports two modes:
35
35
  2. Custom image — an externally provided image file placed directly in the
36
36
  avatar directory. Custom images are not managed through this CLI.
37
37
 
38
- Files are stored in ~/.vellum/workspace/data/avatar/:
38
+ Files are stored in $VELLUM_WORKSPACE_DIR/data/avatar/:
39
39
  character-traits.json Current trait selection (bodyShape, eyeStyle, color)
40
40
  avatar-image.png Rendered PNG of the character
41
41
  character-ascii.txt ASCII art representation (best-effort; may not be written)
@@ -61,7 +61,7 @@ Generates an avatar image using AI based on the provided text description
61
61
  and saves it as the assistant's avatar PNG. This replaces any existing
62
62
  native character avatar — the character traits and ASCII files are removed.
63
63
 
64
- On success, writes avatar-image.png to ~/.vellum/workspace/data/avatar/
64
+ On success, writes avatar-image.png to $VELLUM_WORKSPACE_DIR/data/avatar/
65
65
  and removes character-traits.json and character-ascii.txt if they exist.
66
66
 
67
67
  Examples:
@@ -155,7 +155,7 @@ The --color flag sets the body fill color. Valid values:
155
155
  green, orange, pink, purple, teal, yellow
156
156
 
157
157
  On success, writes character-traits.json and avatar-image.png to
158
- ~/.vellum/workspace/data/avatar/. character-ascii.txt is written on a
158
+ $VELLUM_WORKSPACE_DIR/data/avatar/. character-ascii.txt is written on a
159
159
  best-effort basis and may be skipped if ASCII rendering fails.
160
160
 
161
161
  Examples:
@@ -10,6 +10,7 @@ import {
10
10
  import { AssistantConfigSchema } from "../../config/schema.js";
11
11
  import { getSchemaAtPath } from "../../config/schema-utils.js";
12
12
  import { log } from "../logger.js";
13
+ import { requirePlatformConnection } from "./oauth/shared.js";
13
14
 
14
15
  /**
15
16
  * Flatten a nested config object into dotted key paths.
@@ -34,6 +35,9 @@ function flattenConfig(
34
35
  return result;
35
36
  }
36
37
 
38
+ /** Matches config paths like `services.inference.mode`, `services.image-generation.mode`, etc. */
39
+ const SERVICE_MODE_PATH_RE = /^services\.[^.]+\.mode$/;
40
+
37
41
  export function registerConfigCommand(program: Command): void {
38
42
  const config = program.command("config").description("Manage configuration");
39
43
 
@@ -80,19 +84,28 @@ Examples:
80
84
  $ assistant config set services.inference.provider anthropic
81
85
  $ assistant config set calls.enabled true`,
82
86
  )
83
- .action((key: string, value: string) => {
84
- const raw = loadRawConfig();
85
- // Try to parse as JSON for booleans/numbers, fall back to string
86
- let parsed: unknown = value;
87
- try {
88
- parsed = JSON.parse(value);
89
- } catch {
90
- // keep as string
91
- }
92
- setNestedValue(raw, key, parsed);
93
- saveRawConfig(raw);
94
- log.info(`Set ${key} = ${JSON.stringify(parsed)}`);
95
- });
87
+ .action(
88
+ async (key: string, value: string, _opts: unknown, cmd: Command) => {
89
+ // Try to parse as JSON for booleans/numbers, fall back to string
90
+ let parsed: unknown = value;
91
+ try {
92
+ parsed = JSON.parse(value);
93
+ } catch {
94
+ // keep as string
95
+ }
96
+
97
+ // Require platform connection when setting a service mode to "managed"
98
+ if (SERVICE_MODE_PATH_RE.test(key) && parsed === "managed") {
99
+ const connected = await requirePlatformConnection(cmd);
100
+ if (!connected) return;
101
+ }
102
+
103
+ const raw = loadRawConfig();
104
+ setNestedValue(raw, key, parsed);
105
+ saveRawConfig(raw);
106
+ log.info(`Set ${key} = ${JSON.stringify(parsed)}`);
107
+ },
108
+ );
96
109
 
97
110
  config
98
111
  .command("get <key>")
@@ -375,12 +375,6 @@ Examples:
375
375
  targetId: segId,
376
376
  });
377
377
  }
378
- for (const itemId of result.orphanedItemIds) {
379
- enqueueMemoryJob("delete_qdrant_vectors", {
380
- targetType: "item",
381
- targetId: itemId,
382
- });
383
- }
384
378
  for (const summaryId of result.deletedSummaryIds) {
385
379
  enqueueMemoryJob("delete_qdrant_vectors", {
386
380
  targetType: "summary",
@@ -390,8 +384,7 @@ Examples:
390
384
 
391
385
  log.info(
392
386
  `Wiped conversation "${conversation.title ?? "Untitled"}". ` +
393
- `Restored ${result.unsupersededItemIds.length} memory items, ` +
394
- `deleted ${result.deletedSummaryIds.length} summaries, ` +
387
+ `Deleted ${result.deletedSummaryIds.length} summaries, ` +
395
388
  `cancelled ${result.cancelledJobCount} jobs.`,
396
389
  );
397
390
  });
@@ -39,7 +39,7 @@ Diagnostic checks performed:
39
39
  2. API key configured Checks for a valid provider API key in secure storage
40
40
  3. Assistant reachable HTTP health check against the assistant server
41
41
  4. Database exists/readable Opens the SQLite database and runs a test query
42
- 5. Directory structure Verifies required ~/.vellum/ directories exist
42
+ 5. Directory structure Verifies required workspace directories exist
43
43
  6. Disk space Ensures at least 100MB free on the data partition
44
44
  7. Log file size Warns if the log file exceeds 50MB
45
45
  8. Database integrity Runs SQLite PRAGMA integrity_check
@@ -129,7 +129,7 @@ Examples:
129
129
  fail("Database exists and readable", `not found at ${dbPath}`);
130
130
  }
131
131
 
132
- // 5. ~/.vellum/ directory structure (workspace layout)
132
+ // 5. Workspace directory structure
133
133
  const protectedDir = getProtectedDir();
134
134
  const dataDir = getDataDir();
135
135
  const workspaceDir = getWorkspaceDir();