@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
@@ -39,6 +39,85 @@ export interface SkillRouteDeps {
39
39
  getSkillContext: () => SkillOperationContext;
40
40
  }
41
41
 
42
+ const slimSkillBase = {
43
+ id: z.string(),
44
+ name: z.string(),
45
+ description: z.string(),
46
+ emoji: z.string().optional(),
47
+ kind: z.enum(["bundled", "installed", "catalog"]),
48
+ status: z.enum(["enabled", "disabled", "available"]),
49
+ };
50
+
51
+ const slimSkillSchema = z.discriminatedUnion("origin", [
52
+ z.object({ ...slimSkillBase, origin: z.literal("vellum") }),
53
+ z.object({
54
+ ...slimSkillBase,
55
+ origin: z.literal("clawhub"),
56
+ slug: z.string(),
57
+ author: z.string(),
58
+ stars: z.number(),
59
+ installs: z.number(),
60
+ reports: z.number(),
61
+ publishedAt: z.string().optional(),
62
+ }),
63
+ z.object({
64
+ ...slimSkillBase,
65
+ origin: z.literal("skillssh"),
66
+ slug: z.string(),
67
+ sourceRepo: z.string(),
68
+ installs: z.number(),
69
+ }),
70
+ z.object({ ...slimSkillBase, origin: z.literal("custom") }),
71
+ ]);
72
+
73
+ const skillDetailSchema = z.discriminatedUnion("origin", [
74
+ z.object({ ...slimSkillBase, origin: z.literal("vellum") }),
75
+ z.object({
76
+ ...slimSkillBase,
77
+ origin: z.literal("clawhub"),
78
+ slug: z.string(),
79
+ author: z.string(),
80
+ stars: z.number(),
81
+ installs: z.number(),
82
+ reports: z.number(),
83
+ publishedAt: z.string().optional(),
84
+ owner: z
85
+ .object({
86
+ handle: z.string(),
87
+ displayName: z.string(),
88
+ image: z.string().optional(),
89
+ })
90
+ .nullable()
91
+ .optional(),
92
+ stats: z
93
+ .object({
94
+ stars: z.number(),
95
+ installs: z.number(),
96
+ downloads: z.number(),
97
+ versions: z.number(),
98
+ })
99
+ .nullable()
100
+ .optional(),
101
+ latestVersion: z
102
+ .object({
103
+ version: z.string(),
104
+ changelog: z.string().optional(),
105
+ })
106
+ .nullable()
107
+ .optional(),
108
+ createdAt: z.number().nullable().optional(),
109
+ updatedAt: z.number().nullable().optional(),
110
+ }),
111
+ z.object({
112
+ ...slimSkillBase,
113
+ origin: z.literal("skillssh"),
114
+ slug: z.string(),
115
+ sourceRepo: z.string(),
116
+ installs: z.number(),
117
+ }),
118
+ z.object({ ...slimSkillBase, origin: z.literal("custom") }),
119
+ ]);
120
+
42
121
  export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
43
122
  const ctx = () => deps.getSkillContext();
44
123
 
@@ -60,34 +139,7 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
60
139
  },
61
140
  ],
62
141
  responseBody: z.object({
63
- skills: z
64
- .array(
65
- z.object({
66
- id: z.string(),
67
- name: z.string(),
68
- description: z.string(),
69
- emoji: z.string().optional(),
70
- homepage: z.string().optional(),
71
- source: z.enum([
72
- "bundled",
73
- "managed",
74
- "workspace",
75
- "clawhub",
76
- "extra",
77
- "catalog",
78
- ]),
79
- state: z.enum(["enabled", "disabled"]),
80
- installStatus: z.enum(["bundled", "installed", "available"]),
81
- updateAvailable: z.boolean(),
82
- provenance: z.object({
83
- kind: z.enum(["first-party", "third-party", "local"]),
84
- provider: z.string().optional(),
85
- originId: z.string().optional(),
86
- sourceUrl: z.string().optional(),
87
- }),
88
- }),
89
- )
90
- .describe("Skill objects"),
142
+ skills: z.array(slimSkillSchema).describe("Skill objects"),
91
143
  }),
92
144
  handler: async ({ url }) => {
93
145
  const include = url.searchParams.get("include");
@@ -201,16 +253,23 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
201
253
  url: z.string().describe("Skill URL"),
202
254
  spec: z.string().describe("Skill spec"),
203
255
  version: z.string(),
256
+ origin: z
257
+ .enum(["clawhub", "skillssh"])
258
+ .optional()
259
+ .describe(
260
+ "Which registry to install from. When omitted, the install flow auto-detects based on slug format.",
261
+ ),
204
262
  }),
205
263
  responseBody: z.object({
206
264
  ok: z.boolean(),
207
265
  }),
208
- handler: async ({ req }) => {
266
+ handler: async ({ req, authContext }) => {
209
267
  const body = (await req.json()) as {
210
268
  slug?: string;
211
269
  url?: string;
212
270
  spec?: string;
213
271
  version?: string;
272
+ origin?: "clawhub" | "skillssh";
214
273
  };
215
274
  const slug = body.slug ?? body.url ?? body.spec;
216
275
  if (!slug || typeof slug !== "string") {
@@ -220,8 +279,9 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
220
279
  400,
221
280
  );
222
281
  }
282
+ const contactId = authContext.actorPrincipalId ?? undefined;
223
283
  const result = await installSkill(
224
- { slug, version: body.version },
284
+ { slug, version: body.version, origin: body.origin, contactId },
225
285
  ctx(),
226
286
  );
227
287
  if (!result.success) {
@@ -265,7 +325,9 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
265
325
  },
266
326
  ],
267
327
  responseBody: z.object({
268
- data: z.object({}).passthrough().describe("Search results"),
328
+ skills: z
329
+ .array(slimSkillSchema)
330
+ .describe("Skill objects matching the search query"),
269
331
  }),
270
332
  handler: async ({ url }) => {
271
333
  const query = url.searchParams.get("q") ?? "";
@@ -276,7 +338,7 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
276
338
  if (!result.success) {
277
339
  return httpError("INTERNAL_ERROR", result.error, 500);
278
340
  }
279
- return Response.json({ data: result.data });
341
+ return Response.json({ skills: result.skills });
280
342
  },
281
343
  },
282
344
 
@@ -323,7 +385,7 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
323
385
  responseBody: z.object({
324
386
  ok: z.boolean(),
325
387
  }),
326
- handler: async ({ req }) => {
388
+ handler: async ({ req, authContext }) => {
327
389
  const body = (await req.json()) as CreateSkillParams;
328
390
  if (
329
391
  !body.skillId ||
@@ -337,7 +399,8 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
337
399
  400,
338
400
  );
339
401
  }
340
- const result = await createSkill(body, ctx());
402
+ const contactId = authContext.actorPrincipalId ?? undefined;
403
+ const result = await createSkill({ ...body, contactId }, ctx());
341
404
  if (!result.success) {
342
405
  return httpError("INTERNAL_ERROR", result.error, 500);
343
406
  }
@@ -395,10 +458,13 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
395
458
  method: "GET",
396
459
  policyKey: "skills",
397
460
  summary: "Get skill",
398
- description: "Return a single skill by ID.",
461
+ description: "Return a single skill by ID with enriched detail fields.",
399
462
  tags: ["skills"],
400
- handler: ({ params }) => {
401
- const result = getSkill(params.id, ctx());
463
+ responseBody: z.object({
464
+ skill: skillDetailSchema.describe("Skill detail object"),
465
+ }),
466
+ handler: async ({ params }) => {
467
+ const result = await getSkill(params.id, ctx());
402
468
  if ("error" in result) {
403
469
  if (result.status === 404) {
404
470
  return httpError("NOT_FOUND", result.error, 404);
@@ -34,15 +34,17 @@ export interface SubagentDetailResult {
34
34
  }>;
35
35
  }
36
36
 
37
- export function getSubagentDetail(
37
+ /**
38
+ * Parse raw message rows into subagent detail events. Extracted as a pure
39
+ * function so it can be unit-tested without a database.
40
+ */
41
+ export function parseSubagentMessages(
38
42
  subagentId: string,
39
- conversationId: string,
43
+ messages: Array<{ role: string; content: string }>,
40
44
  ): SubagentDetailResult {
41
- const subagentMsgs = getMessages(conversationId);
42
-
43
45
  // Extract objective from the first user message
44
46
  let objective: string | undefined;
45
- const firstUser = subagentMsgs.find((m) => m.role === "user");
47
+ const firstUser = messages.find((m) => m.role === "user");
46
48
  if (firstUser) {
47
49
  try {
48
50
  const parsed = JSON.parse(firstUser.content);
@@ -67,7 +69,7 @@ export function getSubagentDetail(
67
69
  isError?: boolean;
68
70
  }> = [];
69
71
  const pendingTools = new Map<string, string>();
70
- for (const m of subagentMsgs) {
72
+ for (const m of messages) {
71
73
  if (m.role !== "assistant" && m.role !== "user") continue;
72
74
  let content: unknown[];
73
75
  try {
@@ -101,7 +103,19 @@ export function getSubagentDetail(
101
103
  const toolUseId =
102
104
  typeof block.tool_use_id === "string" ? block.tool_use_id : "";
103
105
  const resultContent =
104
- typeof block.content === "string" ? block.content : "";
106
+ typeof block.content === "string"
107
+ ? block.content
108
+ : Array.isArray(block.content)
109
+ ? (block.content as unknown[])
110
+ .filter(
111
+ (b): b is Record<string, unknown> =>
112
+ isRecord(b) &&
113
+ (b as Record<string, unknown>).type === "text" &&
114
+ typeof (b as Record<string, unknown>).text === "string",
115
+ )
116
+ .map((b) => b.text as string)
117
+ .join("\n")
118
+ : "";
105
119
  const isError = block.is_error === true;
106
120
  const toolName = toolUseId ? pendingTools.get(toolUseId) : undefined;
107
121
  events.push({
@@ -117,6 +131,13 @@ export function getSubagentDetail(
117
131
  return { subagentId, objective, events };
118
132
  }
119
133
 
134
+ export function getSubagentDetail(
135
+ subagentId: string,
136
+ conversationId: string,
137
+ ): SubagentDetailResult {
138
+ return parseSubagentMessages(subagentId, getMessages(conversationId));
139
+ }
140
+
120
141
  // ---------------------------------------------------------------------------
121
142
  // Route definitions
122
143
  // ---------------------------------------------------------------------------
@@ -0,0 +1,223 @@
1
+ /**
2
+ * File-based route dispatcher for user-defined HTTP endpoints.
3
+ *
4
+ * Maps requests under the `/x/*` path prefix to handler modules in the
5
+ * workspace routes directory (`$VELLUM_WORKSPACE_DIR/routes/`). Each handler file
6
+ * exports named functions for HTTP methods (GET, POST, PUT, etc.) using
7
+ * the standard Web API Request/Response signature.
8
+ *
9
+ * Modules are lazily loaded on first request and cached by file path +
10
+ * mtime. When a file changes on disk, the next request reloads it via
11
+ * Bun's dynamic `import()` with a cache-busting query parameter.
12
+ */
13
+
14
+ import { existsSync, statSync } from "node:fs";
15
+ import { join, resolve } from "node:path";
16
+
17
+ import { getLogger } from "../../util/logger.js";
18
+ import { getWorkspaceRoutesDir } from "../../util/platform.js";
19
+ import { httpError } from "../http-errors.js";
20
+
21
+ const log = getLogger("user-routes");
22
+
23
+ /** HTTP methods that can be exported from a handler module. */
24
+ const HTTP_METHODS = [
25
+ "GET",
26
+ "POST",
27
+ "PUT",
28
+ "PATCH",
29
+ "DELETE",
30
+ "HEAD",
31
+ "OPTIONS",
32
+ ] as const;
33
+
34
+ type HttpMethod = (typeof HTTP_METHODS)[number];
35
+
36
+ /** The function signature that user-defined route handlers must follow. */
37
+ type RouteHandler = (request: Request) => Response | Promise<Response>;
38
+
39
+ /** A loaded handler module with its cached metadata. */
40
+ interface CachedModule {
41
+ /** The module's exports (keyed by HTTP method name). */
42
+ handlers: Partial<Record<HttpMethod, RouteHandler>>;
43
+ /** Optional description exported by the module for display in CLI. */
44
+ description?: string;
45
+ /** The file's mtime at the time of loading, in milliseconds. */
46
+ mtimeMs: number;
47
+ }
48
+
49
+ /** Default per-request timeout for user-defined route handlers (30 seconds). */
50
+ const DEFAULT_HANDLER_TIMEOUT_MS = 30_000;
51
+
52
+ /** Supported file extensions for handler modules. */
53
+ const HANDLER_EXTENSIONS = [".ts", ".js"] as const;
54
+
55
+ export class UserRouteDispatcher {
56
+ private moduleCache = new Map<string, CachedModule>();
57
+ private handlerTimeoutMs: number;
58
+
59
+ constructor(options?: { handlerTimeoutMs?: number }) {
60
+ this.handlerTimeoutMs =
61
+ options?.handlerTimeoutMs ?? DEFAULT_HANDLER_TIMEOUT_MS;
62
+ }
63
+
64
+ /**
65
+ * Dispatch a request to the appropriate user-defined handler file.
66
+ *
67
+ * @param routePath The path after the `x/` prefix (e.g. `my-app/status`).
68
+ * @param request The original HTTP request.
69
+ * @returns A Response from the handler, or an error response (404, 405, 500).
70
+ */
71
+ async dispatch(routePath: string, request: Request): Promise<Response> {
72
+ if (routePath.includes("..")) {
73
+ return httpError("BAD_REQUEST", "Path traversal is not allowed", 400);
74
+ }
75
+
76
+ const routesDir = getWorkspaceRoutesDir();
77
+ const filePath = this.resolveHandlerFile(routesDir, routePath);
78
+
79
+ if (!filePath) {
80
+ return httpError(
81
+ "NOT_FOUND",
82
+ `No route handler found for /x/${routePath}`,
83
+ 404,
84
+ );
85
+ }
86
+
87
+ const mod = await this.loadModule(filePath);
88
+ const method = request.method as HttpMethod;
89
+ const handler = mod.handlers[method];
90
+
91
+ if (!handler) {
92
+ const allowed = HTTP_METHODS.filter((m) => m in mod.handlers);
93
+ return new Response(null, {
94
+ status: 405,
95
+ headers: { Allow: allowed.join(", ") },
96
+ });
97
+ }
98
+
99
+ return this.executeHandler(handler, request, routePath);
100
+ }
101
+
102
+ /**
103
+ * Resolve a route path to a handler file on disk.
104
+ *
105
+ * Checks for direct file matches first (`<path>.ts`, `<path>.js`),
106
+ * then falls back to index files (`<path>/index.ts`, `<path>/index.js`).
107
+ *
108
+ * Returns the absolute path to the handler file, or null if not found.
109
+ */
110
+ private resolveHandlerFile(
111
+ routesDir: string,
112
+ routePath: string,
113
+ ): string | null {
114
+ const basePath = join(routesDir, routePath);
115
+ const resolved = resolve(basePath);
116
+
117
+ // Ensure the resolved path is within the routes directory to prevent
118
+ // any path traversal that slipped through the initial check.
119
+ if (!resolved.startsWith(resolve(routesDir))) {
120
+ return null;
121
+ }
122
+
123
+ // Direct file match: routes/<path>.ts or routes/<path>.js
124
+ for (const ext of HANDLER_EXTENSIONS) {
125
+ const candidate = `${resolved}${ext}`;
126
+ if (existsSync(candidate)) {
127
+ return candidate;
128
+ }
129
+ }
130
+
131
+ // Index file convention: routes/<path>/index.ts or routes/<path>/index.js
132
+ for (const ext of HANDLER_EXTENSIONS) {
133
+ const candidate = join(resolved, `index${ext}`);
134
+ if (existsSync(candidate)) {
135
+ return candidate;
136
+ }
137
+ }
138
+
139
+ return null;
140
+ }
141
+
142
+ /**
143
+ * Load a handler module, using the mtime-based cache when possible.
144
+ *
145
+ * On cache miss or stale mtime, the module is re-imported via Bun's
146
+ * dynamic `import()` with a cache-busting query parameter derived
147
+ * from the file's current mtime.
148
+ */
149
+ private async loadModule(filePath: string): Promise<CachedModule> {
150
+ const stat = statSync(filePath);
151
+ const mtimeMs = stat.mtimeMs;
152
+
153
+ const cached = this.moduleCache.get(filePath);
154
+ if (cached && cached.mtimeMs === mtimeMs) {
155
+ return cached;
156
+ }
157
+
158
+ // Cache-bust Bun's module cache by appending mtime as a query param.
159
+ const mod = (await import(`${filePath}?t=${mtimeMs}`)) as Record<
160
+ string,
161
+ unknown
162
+ >;
163
+
164
+ const handlers: Partial<Record<HttpMethod, RouteHandler>> = {};
165
+ for (const method of HTTP_METHODS) {
166
+ if (typeof mod[method] === "function") {
167
+ handlers[method] = mod[method] as RouteHandler;
168
+ }
169
+ }
170
+
171
+ const description =
172
+ typeof mod.description === "string" ? mod.description : undefined;
173
+
174
+ const entry: CachedModule = { handlers, description, mtimeMs };
175
+ this.moduleCache.set(filePath, entry);
176
+
177
+ log.info(
178
+ { filePath, methods: Object.keys(handlers), description },
179
+ "Loaded user route handler",
180
+ );
181
+
182
+ return entry;
183
+ }
184
+
185
+ /**
186
+ * Execute a handler function with a per-request timeout and error boundary.
187
+ */
188
+ private async executeHandler(
189
+ handler: RouteHandler,
190
+ request: Request,
191
+ routePath: string,
192
+ ): Promise<Response> {
193
+ try {
194
+ const result = await Promise.race([
195
+ Promise.resolve(handler(request)),
196
+ new Promise<never>((_, reject) =>
197
+ setTimeout(
198
+ () => reject(new Error("Handler timed out")),
199
+ this.handlerTimeoutMs,
200
+ ),
201
+ ),
202
+ ]);
203
+ return result;
204
+ } catch (err) {
205
+ if (err instanceof Error && err.message === "Handler timed out") {
206
+ log.error(
207
+ { routePath, timeoutMs: this.handlerTimeoutMs },
208
+ "User route handler timed out",
209
+ );
210
+ return httpError(
211
+ "SERVICE_UNAVAILABLE",
212
+ `Route handler for /x/${routePath} timed out after ${this.handlerTimeoutMs}ms`,
213
+ 504,
214
+ );
215
+ }
216
+
217
+ log.error({ err, routePath }, "User route handler threw an error");
218
+ const message =
219
+ err instanceof Error ? err.message : "Internal server error";
220
+ return httpError("INTERNAL_ERROR", message, 500);
221
+ }
222
+ }
223
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Route definitions for user-defined endpoints under `/x/*`.
3
+ *
4
+ * Registers a single catch-all route that delegates to the
5
+ * UserRouteDispatcher for file-based dispatch from
6
+ * `$VELLUM_WORKSPACE_DIR/routes/`.
7
+ */
8
+
9
+ import type { RouteDefinition } from "../http-router.js";
10
+ import { UserRouteDispatcher } from "./user-route-dispatcher.js";
11
+
12
+ const dispatcher = new UserRouteDispatcher();
13
+
14
+ /**
15
+ * HTTP methods supported by user-defined route handlers.
16
+ *
17
+ * Each method gets its own route definition so the HttpRouter can match
18
+ * on method before dispatching. The catch-all `x/:path*` pattern ensures
19
+ * all sub-paths are captured regardless of depth.
20
+ */
21
+ const METHODS = [
22
+ "GET",
23
+ "POST",
24
+ "PUT",
25
+ "PATCH",
26
+ "DELETE",
27
+ "HEAD",
28
+ "OPTIONS",
29
+ ] as const;
30
+
31
+ export function userRouteDefinitions(): RouteDefinition[] {
32
+ return METHODS.map((method) => ({
33
+ endpoint: "x/:path*",
34
+ method,
35
+ policyKey: "x",
36
+ summary: `User-defined ${method} route`,
37
+ description: `Dispatches ${method} requests to user-defined handler files in the workspace routes directory.`,
38
+ tags: ["user-routes"],
39
+ handler: ({ params, req }) => dispatcher.dispatch(params.path, req),
40
+ }));
41
+ }
@@ -5,7 +5,7 @@
5
5
  * item falls back to the task-level required tools instead of silently
6
6
  * skipping permission checks.
7
7
  */
8
- import { afterAll, describe, expect, mock, test } from "bun:test";
8
+ import { describe, expect, mock, test } from "bun:test";
9
9
 
10
10
  mock.module("../../util/logger.js", () => ({
11
11
  getLogger: () =>
@@ -19,7 +19,7 @@ mock.module("../../permissions/checker.js", () => ({
19
19
  classifyRisk: async () => "high",
20
20
  }));
21
21
 
22
- import { initializeDb, resetDb } from "../../memory/db.js";
22
+ import { initializeDb } from "../../memory/db.js";
23
23
  import { createTask } from "../../tasks/task-store.js";
24
24
  import { createWorkItem } from "../../work-items/work-item-store.js";
25
25
  import type { RouteContext } from "../http-router.js";
@@ -30,10 +30,6 @@ import {
30
30
 
31
31
  initializeDb();
32
32
 
33
- afterAll(() => {
34
- resetDb();
35
- });
36
-
37
33
  describe("empty required_tools snapshot bypass", () => {
38
34
  test("falls back to task required tools when snapshot requiredTools is empty", async () => {
39
35
  const task = createTask({
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * Route handlers for workspace file browsing and content serving.
3
3
  *
4
- * WARNING: Workspace contents are included in diagnostic log exports.
5
4
  * Do not store secrets here — use the credential store or protected/ directory.
6
5
  */
7
6
  import {
@@ -36,6 +36,7 @@ export interface ScheduleJob {
36
36
  routingIntent: RoutingIntent;
37
37
  routingHints: Record<string, unknown>;
38
38
  quiet: boolean;
39
+ reuseConversation: boolean;
39
40
  status: ScheduleStatus;
40
41
  createdAt: number;
41
42
  updatedAt: number;
@@ -93,6 +94,7 @@ export function createSchedule(params: {
93
94
  routingIntent?: RoutingIntent;
94
95
  routingHints?: Record<string, unknown>;
95
96
  quiet?: boolean;
97
+ reuseConversation?: boolean;
96
98
  }): ScheduleJob {
97
99
  const expression = params.expression ?? params.cronExpression ?? null;
98
100
  const isOneShot = expression == null;
@@ -121,6 +123,7 @@ export function createSchedule(params: {
121
123
  const routingIntent = params.routingIntent ?? "all_channels";
122
124
  const routingHints = params.routingHints ?? {};
123
125
  const quiet = params.quiet ?? false;
126
+ const reuseConversation = params.reuseConversation ?? false;
124
127
 
125
128
  let nextRunAt: number;
126
129
  if (isOneShot) {
@@ -148,6 +151,7 @@ export function createSchedule(params: {
148
151
  routingIntent,
149
152
  routingHintsJson: JSON.stringify(routingHints),
150
153
  quiet,
154
+ reuseConversation,
151
155
  status: "active" as ScheduleStatus,
152
156
  createdAt: now,
153
157
  updatedAt: now,
@@ -220,6 +224,7 @@ export function updateSchedule(
220
224
  routingIntent?: RoutingIntent;
221
225
  routingHints?: Record<string, unknown>;
222
226
  quiet?: boolean;
227
+ reuseConversation?: boolean;
223
228
  },
224
229
  ): ScheduleJob | null {
225
230
  const db = getDb();
@@ -275,6 +280,8 @@ export function updateSchedule(
275
280
  if (updates.routingHints !== undefined)
276
281
  set.routingHintsJson = JSON.stringify(updates.routingHints);
277
282
  if (updates.quiet !== undefined) set.quiet = updates.quiet;
283
+ if (updates.reuseConversation !== undefined)
284
+ set.reuseConversation = updates.reuseConversation;
278
285
 
279
286
  // Recompute nextRunAt if schedule timing may have changed (only for recurring)
280
287
  if (
@@ -563,6 +570,28 @@ export function completeScheduleRun(
563
570
  }
564
571
  }
565
572
 
573
+ /**
574
+ * Return the conversation ID from the most recent successful run
575
+ * for a given schedule, or null if none exists.
576
+ */
577
+ export function getLastScheduleConversationId(jobId: string): string | null {
578
+ const db = getDb();
579
+ const row = db
580
+ .select({ conversationId: scheduleRuns.conversationId })
581
+ .from(scheduleRuns)
582
+ .where(
583
+ and(
584
+ eq(scheduleRuns.jobId, jobId),
585
+ eq(scheduleRuns.status, "ok"),
586
+ sql`${scheduleRuns.conversationId} IS NOT NULL`,
587
+ ),
588
+ )
589
+ .orderBy(desc(scheduleRuns.createdAt))
590
+ .limit(1)
591
+ .get();
592
+ return row?.conversationId ?? null;
593
+ }
594
+
566
595
  export function getScheduleRuns(jobId: string, limit?: number): ScheduleRun[] {
567
596
  const db = getDb();
568
597
  const rows = db
@@ -757,6 +786,7 @@ function parseJobRow(row: typeof scheduleJobs.$inferSelect): ScheduleJob {
757
786
  routingIntent: (row.routingIntent ?? "all_channels") as RoutingIntent,
758
787
  routingHints: safeParseJson(row.routingHintsJson),
759
788
  quiet: row.quiet ?? false,
789
+ reuseConversation: row.reuseConversation ?? false,
760
790
  status: (row.status ?? "active") as ScheduleStatus,
761
791
  createdAt: row.createdAt,
762
792
  updatedAt: row.updatedAt,