@vellumai/assistant 0.5.16 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (592) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +69 -16
  3. package/Dockerfile +2 -5
  4. package/bun.lock +6 -2
  5. package/docker-entrypoint.sh +32 -1
  6. package/docs/architecture/integrations.md +1 -1
  7. package/docs/architecture/memory.md +21 -24
  8. package/knip.json +2 -1
  9. package/openapi.yaml +1198 -83
  10. package/package.json +5 -1
  11. package/src/__tests__/actor-token-service.test.ts +68 -0
  12. package/src/__tests__/agent-loop.test.ts +0 -32
  13. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  14. package/src/__tests__/anthropic-provider.test.ts +217 -98
  15. package/src/__tests__/app-compiler.test.ts +120 -0
  16. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  17. package/src/__tests__/app-executors.test.ts +47 -1
  18. package/src/__tests__/app-source-watcher.test.ts +159 -0
  19. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  20. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  21. package/src/__tests__/call-domain.test.ts +2 -6
  22. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  23. package/src/__tests__/call-recovery.test.ts +2 -6
  24. package/src/__tests__/call-routes-http.test.ts +2 -6
  25. package/src/__tests__/call-store.test.ts +2 -6
  26. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  27. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  28. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  29. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  30. package/src/__tests__/checker.test.ts +63 -9
  31. package/src/__tests__/clawhub.test.ts +54 -24
  32. package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
  33. package/src/__tests__/config-schema.test.ts +6 -1
  34. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  35. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  36. package/src/__tests__/contacts-tools.test.ts +31 -0
  37. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  38. package/src/__tests__/context-token-estimator.test.ts +175 -10
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +13 -6
  40. package/src/__tests__/conversation-agent-loop.test.ts +13 -51
  41. package/src/__tests__/conversation-attachments.test.ts +2 -6
  42. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  43. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  44. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  45. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  46. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  47. package/src/__tests__/conversation-error.test.ts +33 -2
  48. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  49. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  50. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
  53. package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
  54. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  55. package/src/__tests__/conversation-store.test.ts +2 -6
  56. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
  57. package/src/__tests__/conversation-usage.test.ts +2 -6
  58. package/src/__tests__/conversation-wipe.test.ts +13 -414
  59. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
  60. package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
  61. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  62. package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
  63. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  64. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  65. package/src/__tests__/credential-security-e2e.test.ts +2 -0
  66. package/src/__tests__/date-context.test.ts +76 -210
  67. package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
  68. package/src/__tests__/file-list-tool.test.ts +219 -0
  69. package/src/__tests__/first-greeting.test.ts +1 -1
  70. package/src/__tests__/followup-tools.test.ts +2 -6
  71. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  72. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  73. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  74. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  75. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  76. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  77. package/src/__tests__/guardian-action-store.test.ts +2 -6
  78. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  79. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  80. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  81. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  82. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  83. package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
  84. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  85. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  86. package/src/__tests__/heartbeat-service.test.ts +180 -3
  87. package/src/__tests__/identity-routes.test.ts +328 -0
  88. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  89. package/src/__tests__/injection-block.test.ts +178 -0
  90. package/src/__tests__/install-meta.test.ts +506 -0
  91. package/src/__tests__/install-skill-routing.test.ts +293 -0
  92. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  93. package/src/__tests__/invite-routes-http.test.ts +2 -6
  94. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +17 -28
  95. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  96. package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
  97. package/src/__tests__/llm-context-normalization.test.ts +18 -18
  98. package/src/__tests__/llm-context-route-provider.test.ts +103 -6
  99. package/src/__tests__/llm-request-log-turn-query.test.ts +164 -6
  100. package/src/__tests__/llm-usage-store.test.ts +2 -6
  101. package/src/__tests__/log-export-workspace.test.ts +74 -111
  102. package/src/__tests__/managed-store.test.ts +38 -11
  103. package/src/__tests__/mcp-abort-signal.test.ts +5 -0
  104. package/src/__tests__/mcp-client-auth.test.ts +5 -0
  105. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  106. package/src/__tests__/memory-recall-log-store.test.ts +134 -6
  107. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  108. package/src/__tests__/migration-export-streaming.test.ts +304 -0
  109. package/src/__tests__/migration-import-commit-http.test.ts +11 -10
  110. package/src/__tests__/mock-fetch.ts +87 -0
  111. package/src/__tests__/non-member-access-request.test.ts +2 -6
  112. package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
  113. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  114. package/src/__tests__/oauth-cli.test.ts +364 -2
  115. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  116. package/src/__tests__/onboarding-template-contract.test.ts +62 -14
  117. package/src/__tests__/outlook-attachments.test.ts +301 -0
  118. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  119. package/src/__tests__/outlook-categories.test.ts +212 -0
  120. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  121. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  122. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  123. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  124. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  125. package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
  126. package/src/__tests__/outlook-trash.test.ts +77 -0
  127. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  128. package/src/__tests__/parser.test.ts +32 -0
  129. package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
  130. package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
  131. package/src/__tests__/permission-mode-sse.test.ts +418 -0
  132. package/src/__tests__/permission-mode-store.test.ts +277 -0
  133. package/src/__tests__/permission-mode.test.ts +101 -0
  134. package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
  135. package/src/__tests__/platform-callback-registration.test.ts +4 -4
  136. package/src/__tests__/playbook-execution.test.ts +76 -80
  137. package/src/__tests__/playbook-tools.test.ts +5 -7
  138. package/src/__tests__/profiler-routes.test.ts +502 -0
  139. package/src/__tests__/profiler-run-store.test.ts +441 -0
  140. package/src/__tests__/provider-error-scenarios.test.ts +21 -0
  141. package/src/__tests__/proxy-approval-callback.test.ts +4 -75
  142. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  143. package/src/__tests__/registry.test.ts +3 -3
  144. package/src/__tests__/require-fresh-approval.test.ts +64 -2
  145. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  146. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  147. package/src/__tests__/sandbox-host-parity.test.ts +5 -4
  148. package/src/__tests__/schedule-store.test.ts +2 -6
  149. package/src/__tests__/schedule-tools.test.ts +2 -6
  150. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  151. package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
  152. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  153. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  154. package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
  155. package/src/__tests__/search-skills-unified.test.ts +422 -0
  156. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  157. package/src/__tests__/send-endpoint-busy.test.ts +44 -9
  158. package/src/__tests__/sequence-store.test.ts +2 -6
  159. package/src/__tests__/server-history-render.test.ts +2 -6
  160. package/src/__tests__/set-permission-mode.test.ts +274 -0
  161. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  162. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  163. package/src/__tests__/skill-load-feature-flag.test.ts +23 -11
  164. package/src/__tests__/skill-memory.test.ts +2 -741
  165. package/src/__tests__/skills-uninstall.test.ts +2 -2
  166. package/src/__tests__/skills.test.ts +1 -1
  167. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  168. package/src/__tests__/strip-memory-injections.test.ts +187 -0
  169. package/src/__tests__/subagent-detail.test.ts +84 -0
  170. package/src/__tests__/subagent-disposal.test.ts +308 -0
  171. package/src/__tests__/subagent-manager-notify.test.ts +19 -10
  172. package/src/__tests__/subagent-notify-parent.test.ts +390 -0
  173. package/src/__tests__/subagent-role-registry.test.ts +108 -0
  174. package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
  175. package/src/__tests__/subagent-tools.test.ts +464 -4
  176. package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
  177. package/src/__tests__/task-compiler.test.ts +2 -6
  178. package/src/__tests__/task-management-tools.test.ts +2 -6
  179. package/src/__tests__/task-memory-cleanup.test.ts +185 -241
  180. package/src/__tests__/task-runner.test.ts +2 -6
  181. package/src/__tests__/task-scheduler.test.ts +2 -6
  182. package/src/__tests__/terminal-tools.test.ts +17 -27
  183. package/src/__tests__/test-preload.ts +7 -0
  184. package/src/__tests__/tool-approval-handler.test.ts +2 -6
  185. package/src/__tests__/tool-executor.test.ts +4 -26
  186. package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
  187. package/src/__tests__/tool-side-effects-slack-dm.test.ts +277 -0
  188. package/src/__tests__/top-level-renderer.test.ts +10 -13
  189. package/src/__tests__/trust-store.test.ts +1 -1
  190. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
  191. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +118 -8
  192. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
  193. package/src/__tests__/trusted-contact-verification.test.ts +2 -6
  194. package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
  195. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
  196. package/src/__tests__/usage-routes.test.ts +2 -6
  197. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  198. package/src/__tests__/voice-invite-redemption.test.ts +2 -6
  199. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
  200. package/src/__tests__/voice-session-bridge.test.ts +2 -6
  201. package/src/__tests__/volume-security-guard.test.ts +2 -0
  202. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  203. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
  204. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
  205. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  206. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
  207. package/src/__tests__/workspace-policy.test.ts +1 -1
  208. package/src/agent/attachments.ts +7 -2
  209. package/src/agent/image-optimize.ts +165 -0
  210. package/src/agent/loop.ts +7 -15
  211. package/src/approvals/guardian-request-resolvers.ts +24 -0
  212. package/src/avatar/traits-png-sync.ts +3 -3
  213. package/src/bundler/app-compiler.ts +179 -2
  214. package/src/bundler/package-resolver.ts +3 -5
  215. package/src/cli/__tests__/notifications.test.ts +1 -2
  216. package/src/cli/__tests__/run-assistant-command.ts +29 -0
  217. package/src/cli/commands/__tests__/email-download.test.ts +245 -0
  218. package/src/cli/commands/__tests__/email-list.test.ts +192 -0
  219. package/src/cli/commands/__tests__/email-register.test.ts +186 -0
  220. package/src/cli/commands/__tests__/email-send.test.ts +291 -0
  221. package/src/cli/commands/__tests__/email-status.test.ts +181 -0
  222. package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
  223. package/src/cli/commands/__tests__/routes.test.ts +562 -0
  224. package/src/cli/commands/avatar.ts +3 -3
  225. package/src/cli/commands/config.ts +26 -13
  226. package/src/cli/commands/conversations.ts +1 -8
  227. package/src/cli/commands/doctor.ts +2 -2
  228. package/src/cli/commands/email.ts +584 -835
  229. package/src/cli/commands/memory.ts +37 -84
  230. package/src/cli/commands/notifications.ts +7 -2
  231. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  232. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  233. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  234. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  235. package/src/cli/commands/oauth/connect.ts +25 -11
  236. package/src/cli/commands/oauth/mode.ts +7 -0
  237. package/src/cli/commands/oauth/shared.ts +39 -3
  238. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  239. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  240. package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
  241. package/src/cli/commands/platform/index.ts +16 -16
  242. package/src/cli/commands/routes.ts +396 -0
  243. package/src/cli/commands/skills.ts +218 -36
  244. package/src/cli/commands/trust.ts +2 -2
  245. package/src/cli/lib/daemon-credential-client.ts +2 -3
  246. package/src/cli/program.ts +2 -0
  247. package/src/cli.ts +1 -120
  248. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  249. package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
  250. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  251. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  252. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  253. package/src/config/bundled-skills/gmail/SKILL.md +4 -12
  254. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  255. package/src/config/bundled-skills/messaging/SKILL.md +17 -18
  256. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  257. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  258. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  259. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  260. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  261. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  262. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  263. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  264. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  265. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  266. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  267. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  268. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  269. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  270. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  271. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  272. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  273. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  274. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  275. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  276. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  277. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  278. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  279. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  280. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  281. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  282. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  283. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  284. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  285. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  286. package/src/config/bundled-skills/schedule/SKILL.md +22 -2
  287. package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
  288. package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
  289. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
  290. package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
  291. package/src/config/bundled-skills/slack/SKILL.md +3 -7
  292. package/src/config/bundled-skills/subagent/SKILL.md +43 -3
  293. package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
  294. package/src/config/bundled-tool-registry.ts +56 -4
  295. package/src/config/env-registry.ts +78 -8
  296. package/src/config/feature-flag-registry.json +38 -125
  297. package/src/config/schema.ts +8 -0
  298. package/src/config/schemas/filing.ts +51 -0
  299. package/src/config/schemas/heartbeat.ts +15 -12
  300. package/src/config/schemas/memory-lifecycle.ts +12 -0
  301. package/src/config/schemas/platform.ts +8 -0
  302. package/src/config/schemas/security.ts +14 -0
  303. package/src/config/schemas/timeouts.ts +1 -1
  304. package/src/config/skills.ts +18 -7
  305. package/src/context/token-estimator.ts +25 -18
  306. package/src/context/window-manager.ts +6 -2
  307. package/src/credential-execution/process-manager.ts +3 -1
  308. package/src/daemon/app-source-watcher.ts +93 -0
  309. package/src/daemon/config-watcher.ts +79 -1
  310. package/src/daemon/context-overflow-reducer.ts +46 -2
  311. package/src/daemon/conversation-agent-loop-handlers.ts +143 -82
  312. package/src/daemon/conversation-agent-loop.ts +236 -108
  313. package/src/daemon/conversation-error.ts +31 -8
  314. package/src/daemon/conversation-history.ts +4 -19
  315. package/src/daemon/conversation-lifecycle.ts +36 -9
  316. package/src/daemon/conversation-media-retry.ts +85 -7
  317. package/src/daemon/conversation-notifiers.ts +4 -1
  318. package/src/daemon/conversation-process.ts +13 -7
  319. package/src/daemon/conversation-runtime-assembly.ts +305 -306
  320. package/src/daemon/conversation-tool-setup.ts +44 -14
  321. package/src/daemon/conversation-workspace.ts +1 -2
  322. package/src/daemon/conversation.ts +59 -2
  323. package/src/daemon/daemon-control.ts +8 -2
  324. package/src/daemon/date-context.ts +26 -53
  325. package/src/daemon/first-greeting.ts +1 -1
  326. package/src/daemon/handlers/conversations.ts +4 -7
  327. package/src/daemon/handlers/shared.test.ts +143 -0
  328. package/src/daemon/handlers/shared.ts +85 -17
  329. package/src/daemon/handlers/skills.ts +416 -209
  330. package/src/daemon/lifecycle.ts +212 -131
  331. package/src/daemon/main.ts +5 -1
  332. package/src/daemon/message-types/conversations.ts +29 -7
  333. package/src/daemon/message-types/messages.ts +12 -2
  334. package/src/daemon/message-types/schedules.ts +1 -0
  335. package/src/daemon/message-types/settings.ts +6 -0
  336. package/src/daemon/message-types/skills.ts +97 -36
  337. package/src/daemon/profiler-run-store.ts +557 -0
  338. package/src/daemon/providers-setup.ts +5 -0
  339. package/src/daemon/server.ts +100 -11
  340. package/src/daemon/shutdown-handlers.ts +5 -0
  341. package/src/daemon/tool-side-effects.ts +50 -8
  342. package/src/export/transcript-formatter.ts +148 -0
  343. package/src/filing/filing-service.ts +228 -0
  344. package/src/heartbeat/heartbeat-service.ts +97 -7
  345. package/src/hooks/cli.ts +2 -2
  346. package/src/hooks/runner.ts +15 -38
  347. package/src/inbound/platform-callback-registration.ts +14 -14
  348. package/src/mcp/client.ts +6 -0
  349. package/src/mcp/mcp-oauth-provider.ts +149 -27
  350. package/src/memory/admin.ts +42 -75
  351. package/src/memory/app-store.ts +69 -0
  352. package/src/memory/conversation-bootstrap.ts +3 -1
  353. package/src/memory/conversation-crud.ts +211 -288
  354. package/src/memory/conversation-group-migration.ts +157 -0
  355. package/src/memory/conversation-queries.ts +61 -13
  356. package/src/memory/conversation-title-service.ts +1 -0
  357. package/src/memory/db-init.ts +194 -361
  358. package/src/memory/embed.ts +73 -0
  359. package/src/memory/embedding-backend.ts +8 -14
  360. package/src/memory/embedding-runtime-manager.ts +12 -114
  361. package/src/memory/fingerprint.ts +2 -2
  362. package/src/memory/graph/bootstrap.ts +521 -0
  363. package/src/memory/graph/capability-seed.ts +449 -0
  364. package/src/memory/graph/consolidation.ts +725 -0
  365. package/src/memory/graph/conversation-graph-memory.ts +659 -0
  366. package/src/memory/graph/decay.test.ts +208 -0
  367. package/src/memory/graph/decay.ts +195 -0
  368. package/src/memory/graph/extraction-job.ts +74 -0
  369. package/src/memory/graph/extraction.test.ts +936 -0
  370. package/src/memory/graph/extraction.ts +1297 -0
  371. package/src/memory/graph/graph-memory-state-store.ts +37 -0
  372. package/src/memory/graph/graph-search.ts +280 -0
  373. package/src/memory/graph/image-ref-utils.ts +29 -0
  374. package/src/memory/graph/injection.test.ts +513 -0
  375. package/src/memory/graph/injection.ts +469 -0
  376. package/src/memory/graph/inspect.ts +543 -0
  377. package/src/memory/graph/narrative.ts +267 -0
  378. package/src/memory/graph/pattern-scan.ts +269 -0
  379. package/src/memory/graph/retriever.ts +1111 -0
  380. package/src/memory/graph/scoring.test.ts +548 -0
  381. package/src/memory/graph/scoring.ts +232 -0
  382. package/src/memory/graph/serendipity.ts +65 -0
  383. package/src/memory/graph/store.test.ts +1098 -0
  384. package/src/memory/graph/store.ts +838 -0
  385. package/src/memory/graph/tool-handlers.ts +301 -0
  386. package/src/memory/graph/tools.ts +97 -0
  387. package/src/memory/graph/triggers.test.ts +487 -0
  388. package/src/memory/graph/triggers.ts +223 -0
  389. package/src/memory/graph/types.ts +295 -0
  390. package/src/memory/group-crud.ts +191 -0
  391. package/src/memory/indexer.ts +37 -19
  392. package/src/memory/job-handlers/cleanup.ts +32 -42
  393. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  394. package/src/memory/job-handlers/embedding.ts +5 -31
  395. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  396. package/src/memory/job-handlers/summarization.ts +32 -17
  397. package/src/memory/job-utils.ts +1 -1
  398. package/src/memory/jobs-store.ts +21 -31
  399. package/src/memory/jobs-worker.ts +180 -129
  400. package/src/memory/llm-request-log-store.ts +96 -12
  401. package/src/memory/memory-recall-log-store.ts +49 -5
  402. package/src/memory/message-content.ts +1 -0
  403. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  404. package/src/memory/migrations/203-drop-memory-items-tables.ts +55 -0
  405. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  406. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  407. package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
  408. package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
  409. package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
  410. package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
  411. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
  412. package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
  413. package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
  414. package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
  415. package/src/memory/migrations/index.ts +12 -0
  416. package/src/memory/migrations/registry.ts +16 -0
  417. package/src/memory/qdrant-client.ts +44 -17
  418. package/src/memory/schema/conversations.ts +14 -0
  419. package/src/memory/schema/index.ts +1 -0
  420. package/src/memory/schema/infrastructure.ts +8 -1
  421. package/src/memory/schema/memory-core.ts +0 -51
  422. package/src/memory/schema/memory-graph.ts +154 -0
  423. package/src/memory/search/semantic.ts +47 -91
  424. package/src/memory/task-memory-cleanup.ts +58 -61
  425. package/src/messaging/providers/outlook/adapter.ts +8 -1
  426. package/src/messaging/providers/outlook/client.ts +299 -0
  427. package/src/messaging/providers/outlook/types.ts +118 -0
  428. package/src/notifications/adapters/macos.ts +1 -0
  429. package/src/notifications/copy-composer.ts +95 -0
  430. package/src/notifications/decision-engine.ts +35 -0
  431. package/src/notifications/signal.ts +16 -0
  432. package/src/oauth/seed-providers.ts +2 -1
  433. package/src/permissions/checker.ts +36 -4
  434. package/src/permissions/defaults.ts +4 -4
  435. package/src/permissions/permission-mode-store.ts +180 -0
  436. package/src/permissions/permission-mode.ts +31 -0
  437. package/src/permissions/workspace-policy.ts +10 -1
  438. package/src/playbooks/playbook-compiler.ts +19 -18
  439. package/src/playbooks/types.ts +4 -3
  440. package/src/prompts/system-prompt.ts +62 -36
  441. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
  442. package/src/prompts/templates/BOOTSTRAP.md +70 -165
  443. package/src/prompts/templates/HEARTBEAT.md +3 -1
  444. package/src/prompts/templates/SOUL.md +25 -4
  445. package/src/prompts/templates/UPDATES.md +8 -0
  446. package/src/providers/anthropic/client.ts +136 -220
  447. package/src/providers/gemini/client.ts +1 -1
  448. package/src/providers/openai/client.ts +1 -1
  449. package/src/providers/registry.ts +1 -1
  450. package/src/providers/retry.ts +19 -3
  451. package/src/runtime/actor-trust-resolver.ts +5 -1
  452. package/src/runtime/auth/route-policy.ts +30 -0
  453. package/src/runtime/guardian-reply-router.ts +5 -1
  454. package/src/runtime/http-server.ts +55 -5
  455. package/src/runtime/http-types.ts +12 -1
  456. package/src/runtime/middleware/auth.ts +20 -0
  457. package/src/runtime/migrations/vbundle-builder.ts +389 -3
  458. package/src/runtime/migrations/vbundle-importer.ts +8 -6
  459. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
  460. package/src/runtime/routes/app-management-routes.ts +1 -11
  461. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
  462. package/src/runtime/routes/archive-utils.ts +29 -0
  463. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  464. package/src/runtime/routes/attachment-routes.ts +106 -16
  465. package/src/runtime/routes/avatar-routes.ts +2 -9
  466. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  467. package/src/runtime/routes/btw-routes.ts +22 -1
  468. package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
  469. package/src/runtime/routes/conversation-management-routes.ts +3 -14
  470. package/src/runtime/routes/conversation-query-routes.ts +49 -3
  471. package/src/runtime/routes/conversation-routes.ts +264 -44
  472. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  473. package/src/runtime/routes/debug-routes.ts +1 -1
  474. package/src/runtime/routes/global-search-routes.ts +21 -19
  475. package/src/runtime/routes/group-routes.ts +207 -0
  476. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  477. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  478. package/src/runtime/routes/heartbeat-routes.ts +4 -10
  479. package/src/runtime/routes/identity-routes.ts +53 -18
  480. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  481. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  482. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  483. package/src/runtime/routes/llm-context-normalization.ts +14 -10
  484. package/src/runtime/routes/log-export-routes.ts +23 -275
  485. package/src/runtime/routes/memory-item-routes.test.ts +170 -247
  486. package/src/runtime/routes/memory-item-routes.ts +341 -388
  487. package/src/runtime/routes/migration-routes.ts +18 -7
  488. package/src/runtime/routes/profiler-routes.ts +350 -0
  489. package/src/runtime/routes/schedule-routes.ts +28 -11
  490. package/src/runtime/routes/settings-routes.ts +95 -8
  491. package/src/runtime/routes/skills-routes.ts +103 -37
  492. package/src/runtime/routes/subagents-routes.ts +28 -7
  493. package/src/runtime/routes/user-route-dispatcher.ts +223 -0
  494. package/src/runtime/routes/user-routes.ts +41 -0
  495. package/src/runtime/routes/work-items-routes.test.ts +2 -6
  496. package/src/runtime/routes/workspace-routes.ts +0 -1
  497. package/src/schedule/schedule-store.ts +30 -0
  498. package/src/schedule/scheduler.ts +52 -18
  499. package/src/security/oauth2.ts +1 -1
  500. package/src/security/secure-keys.ts +4 -8
  501. package/src/shared/provider-env-vars.ts +19 -0
  502. package/src/skills/catalog-cache.ts +5 -0
  503. package/src/skills/catalog-install.ts +25 -16
  504. package/src/skills/clawhub.ts +134 -154
  505. package/src/skills/install-meta.ts +208 -0
  506. package/src/skills/managed-store.ts +29 -18
  507. package/src/skills/skill-memory.ts +12 -229
  508. package/src/skills/skillssh-registry.ts +19 -17
  509. package/src/subagent/index.ts +13 -3
  510. package/src/subagent/manager.ts +308 -29
  511. package/src/subagent/types.ts +68 -0
  512. package/src/tasks/task-runner.ts +7 -5
  513. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  514. package/src/tools/apps/executors.ts +29 -4
  515. package/src/tools/browser/runtime-check.ts +3 -1
  516. package/src/tools/filesystem/list.ts +93 -0
  517. package/src/tools/memory/register.ts +63 -46
  518. package/src/tools/permission-checker.ts +85 -1
  519. package/src/tools/registry.ts +4 -0
  520. package/src/tools/schedule/create.ts +3 -0
  521. package/src/tools/schedule/list.ts +1 -0
  522. package/src/tools/schedule/update.ts +6 -0
  523. package/src/tools/shared/filesystem/errors.ts +5 -0
  524. package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
  525. package/src/tools/shared/filesystem/image-read.ts +22 -85
  526. package/src/tools/shared/filesystem/types.ts +17 -0
  527. package/src/tools/shared/shell-output.ts +31 -2
  528. package/src/tools/subagent/abort.ts +12 -2
  529. package/src/tools/subagent/message.ts +9 -2
  530. package/src/tools/subagent/notify-parent.ts +79 -0
  531. package/src/tools/subagent/read.ts +29 -8
  532. package/src/tools/subagent/resolve.ts +21 -0
  533. package/src/tools/subagent/spawn.ts +2 -0
  534. package/src/tools/subagent/status.ts +11 -1
  535. package/src/tools/system/avatar-generator.ts +3 -3
  536. package/src/tools/system/register.ts +23 -0
  537. package/src/tools/system/set-permission-mode.ts +103 -0
  538. package/src/tools/terminal/parser.ts +30 -5
  539. package/src/tools/terminal/safe-env.ts +17 -1
  540. package/src/tools/tool-manifest.ts +9 -3
  541. package/src/tools/types.ts +2 -0
  542. package/src/util/browser.ts +25 -10
  543. package/src/util/bun-runtime.ts +172 -0
  544. package/src/util/logger.ts +1 -1
  545. package/src/util/platform.ts +50 -17
  546. package/src/watcher/providers/outlook-calendar.ts +343 -0
  547. package/src/watcher/providers/outlook.ts +198 -0
  548. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
  549. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
  550. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  551. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  552. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  553. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
  554. package/src/workspace/migrations/029-seed-pkb.ts +84 -0
  555. package/src/workspace/migrations/registry.ts +10 -0
  556. package/src/workspace/top-level-renderer.ts +5 -9
  557. package/src/__tests__/cli-memory.test.ts +0 -372
  558. package/src/__tests__/clipboard.test.ts +0 -88
  559. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  560. package/src/__tests__/journal-context.test.ts +0 -268
  561. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  562. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  563. package/src/__tests__/memory-query-builder.test.ts +0 -59
  564. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  565. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  566. package/src/__tests__/memory-regressions.test.ts +0 -3696
  567. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  568. package/src/cli/cli-memory.ts +0 -176
  569. package/src/daemon/conversation-memory.ts +0 -207
  570. package/src/memory/conversation-starters-cadence.ts +0 -74
  571. package/src/memory/items-extractor.ts +0 -860
  572. package/src/memory/job-handlers/batch-extraction.ts +0 -753
  573. package/src/memory/job-handlers/extraction.ts +0 -40
  574. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
  575. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  576. package/src/memory/journal-memory.ts +0 -224
  577. package/src/memory/query-builder.ts +0 -47
  578. package/src/memory/query-expansion.ts +0 -83
  579. package/src/memory/retriever.test.ts +0 -1592
  580. package/src/memory/retriever.ts +0 -1331
  581. package/src/memory/search/formatting.test.ts +0 -140
  582. package/src/memory/search/formatting.ts +0 -262
  583. package/src/memory/search/mmr.ts +0 -139
  584. package/src/memory/search/ranking.ts +0 -15
  585. package/src/memory/search/staleness.ts +0 -40
  586. package/src/memory/search/tier-classifier.ts +0 -18
  587. package/src/memory/search/types.ts +0 -121
  588. package/src/prompts/journal-context.ts +0 -154
  589. package/src/tools/memory/definitions.ts +0 -69
  590. package/src/tools/memory/handlers.test.ts +0 -562
  591. package/src/tools/memory/handlers.ts +0 -434
  592. package/src/util/clipboard.ts +0 -34
@@ -1,4 +1,5 @@
1
1
  import { bootstrapConversation } from "../memory/conversation-bootstrap.js";
2
+ import { getConversation } from "../memory/conversation-crud.js";
2
3
  import { invalidateAssistantInferredItemsForConversation } from "../memory/task-memory-cleanup.js";
3
4
  import { runSequencesOnce } from "../sequence/engine.js";
4
5
  import { getLogger } from "../util/logger.js";
@@ -14,6 +15,7 @@ import {
14
15
  completeScheduleRun,
15
16
  createScheduleRun,
16
17
  failOneShot,
18
+ getLastScheduleConversationId,
17
19
  type RoutingIntent,
18
20
  } from "./schedule-store.js";
19
21
 
@@ -188,7 +190,12 @@ async function runScheduleOnce(
188
190
  );
189
191
  const { runTask } = await import("../tasks/task-runner.js");
190
192
  const result = await runTask(
191
- { taskId, workingDir: process.cwd(), source: "schedule", scheduleJobId: job.id },
193
+ {
194
+ taskId,
195
+ workingDir: process.cwd(),
196
+ source: "schedule",
197
+ scheduleJobId: job.id,
198
+ },
192
199
  processMessage as (
193
200
  conversationId: string,
194
201
  message: string,
@@ -235,8 +242,10 @@ async function runScheduleOnce(
235
242
  );
236
243
  // Create a fallback conversation for the schedule run record
237
244
  const fallbackConversation = bootstrapConversation({
245
+ conversationType: "scheduled",
238
246
  source: "schedule",
239
247
  scheduleJobId: job.id,
248
+ groupId: "system:scheduled",
240
249
  origin: "schedule",
241
250
  systemHint: `Schedule: ${job.name}`,
242
251
  });
@@ -252,18 +261,36 @@ async function runScheduleOnce(
252
261
  continue;
253
262
  }
254
263
 
255
- const conversation = bootstrapConversation({
256
- source: "schedule",
257
- scheduleJobId: job.id,
258
- origin: "schedule",
259
- systemHint: isOneShot ? `Reminder: ${job.name}` : `Schedule: ${job.name}`,
260
- });
264
+ // Reuse the conversation from the last successful run when the flag is set
265
+ // and a prior conversation still exists; otherwise bootstrap a new one.
266
+ let conversationId: string | null = null;
267
+ let conversationReused = false;
268
+ if (job.reuseConversation && !isOneShot) {
269
+ const lastId = getLastScheduleConversationId(job.id);
270
+ if (lastId && getConversation(lastId)) {
271
+ conversationId = lastId;
272
+ conversationReused = true;
273
+ }
274
+ }
275
+ if (!conversationId) {
276
+ const conversation = bootstrapConversation({
277
+ conversationType: "scheduled",
278
+ source: "schedule",
279
+ scheduleJobId: job.id,
280
+ groupId: "system:scheduled",
281
+ origin: "schedule",
282
+ systemHint: isOneShot
283
+ ? `Reminder: ${job.name}`
284
+ : `Schedule: ${job.name}`,
285
+ });
286
+ conversationId = conversation.id;
287
+ }
261
288
  onScheduleConversationCreated?.({
262
- conversationId: conversation.id,
289
+ conversationId,
263
290
  scheduleJobId: job.id,
264
291
  title: job.name,
265
292
  });
266
- const runId = createScheduleRun(job.id, conversation.id);
293
+ const runId = createScheduleRun(job.id, conversationId);
267
294
  const isRruleSetMsg =
268
295
  job.syntax === "rrule" &&
269
296
  job.expression != null &&
@@ -278,11 +305,11 @@ async function runScheduleOnce(
278
305
  expression: job.expression,
279
306
  isRruleSet: isRruleSetMsg,
280
307
  isOneShot,
281
- conversationId: conversation.id,
308
+ conversationId,
282
309
  },
283
310
  isOneShot ? "Executing one-shot schedule" : "Executing schedule",
284
311
  );
285
- await processMessage(conversation.id, job.message, {
312
+ await processMessage(conversationId, job.message, {
286
313
  trustClass: "guardian",
287
314
  });
288
315
  completeScheduleRun(runId, { status: "ok" });
@@ -310,13 +337,20 @@ async function runScheduleOnce(
310
337
  completeScheduleRun(runId, { status: "error", error: message });
311
338
  if (isOneShot) failOneShot(job.id);
312
339
 
313
- try {
314
- invalidateAssistantInferredItemsForConversation(conversation.id);
315
- } catch (cleanupErr) {
316
- log.warn(
317
- { err: cleanupErr, conversationId: conversation.id },
318
- "Failed to invalidate assistant-inferred memory items",
319
- );
340
+ // Only skip invalidation when the conversation was *actually* reused,
341
+ // i.e. it contains prior successful context worth preserving. When
342
+ // reuseConversation is true but no prior conversation existed (first run
343
+ // or deleted), the conversation is brand-new and should be invalidated
344
+ // like any other failed conversation.
345
+ if (!conversationReused) {
346
+ try {
347
+ invalidateAssistantInferredItemsForConversation(conversationId);
348
+ } catch (cleanupErr) {
349
+ log.warn(
350
+ { err: cleanupErr, conversationId },
351
+ "Failed to invalidate assistant-inferred memory items",
352
+ );
353
+ }
320
354
  }
321
355
  }
322
356
  }
@@ -61,7 +61,7 @@ export interface OAuth2TokenResult {
61
61
 
62
62
  export interface OAuth2FlowCallbacks {
63
63
  /** Open a URL in the user's browser (e.g. via `open_url` message). */
64
- openUrl: (url: string) => void;
64
+ openUrl: (url: string) => void | Promise<void>;
65
65
  }
66
66
 
67
67
  export interface OAuth2FlowOptions {
@@ -22,9 +22,9 @@ import type {
22
22
  SecureKeyDeleteResult,
23
23
  } from "@vellumai/credential-storage";
24
24
 
25
- import providerEnvVarsRegistry from "../../../meta/provider-env-vars.json" with { type: "json" };
26
25
  import { getIsContainerized } from "../config/env-registry.js";
27
26
  import type { CesClient } from "../credential-execution/client.js";
27
+ import { PROVIDER_ENV_VAR_NAMES } from "../shared/provider-env-vars.js";
28
28
  import { getLogger } from "../util/logger.js";
29
29
  import { createCesCredentialBackend } from "./ces-credential-client.js";
30
30
  import { CesRpcCredentialBackend } from "./ces-rpc-credential-backend.js";
@@ -395,14 +395,10 @@ export async function deleteSecureKeyAsync(
395
395
  // ---------------------------------------------------------------------------
396
396
 
397
397
  /**
398
- * Env var names keyed by provider. Loaded from the shared registry at
399
- * `meta/provider-env-vars.json`the single source of truth also consumed
400
- * by the CLI and the macOS client.
401
- * Ollama is intentionally omitted from the registry — it doesn't require
402
- * an API key.
398
+ * Env var names keyed by provider.
399
+ * Ollama is intentionally omitted it doesn't require an API key.
403
400
  */
404
- const PROVIDER_ENV_VARS: Record<string, string> =
405
- providerEnvVarsRegistry.providers;
401
+ const PROVIDER_ENV_VARS: Record<string, string> = PROVIDER_ENV_VAR_NAMES;
406
402
 
407
403
  /**
408
404
  * Retrieve a provider API key, checking secure storage first and falling
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Provider API key environment variable names, keyed by provider ID.
3
+ *
4
+ * Keep in sync with:
5
+ * - cli/src/shared/provider-env-vars.ts
6
+ * - meta/provider-env-vars.json (consumed by the macOS client build)
7
+ *
8
+ * Once a consolidated shared package exists in packages/, all three
9
+ * copies can be replaced by a single import.
10
+ */
11
+ export const PROVIDER_ENV_VAR_NAMES: Record<string, string> = {
12
+ anthropic: "ANTHROPIC_API_KEY",
13
+ openai: "OPENAI_API_KEY",
14
+ gemini: "GEMINI_API_KEY",
15
+ fireworks: "FIREWORKS_API_KEY",
16
+ openrouter: "OPENROUTER_API_KEY",
17
+ brave: "BRAVE_API_KEY",
18
+ perplexity: "PERPLEXITY_API_KEY",
19
+ };
@@ -37,6 +37,11 @@ export async function getCatalog(): Promise<CatalogSkill[]> {
37
37
  return catalog;
38
38
  }
39
39
 
40
+ /** Return the cached catalog synchronously, or [] if no cache exists yet. */
41
+ export function getCachedCatalogSync(): CatalogSkill[] {
42
+ return cachedCatalog ?? [];
43
+ }
44
+
40
45
  /** Invalidate the cache (for testing or forced refresh). */
41
46
  export function invalidateCatalogCache(): void {
42
47
  cachedCatalog = null;
@@ -14,9 +14,10 @@ import { dirname, join, posix, resolve, sep } from "node:path";
14
14
  import { gunzipSync } from "node:zlib";
15
15
 
16
16
  import { getPlatformBaseUrl } from "../config/env.js";
17
+ import { deleteSkillCapabilityNode } from "../memory/graph/capability-seed.js";
17
18
  import { getLogger } from "../util/logger.js";
18
19
  import { getWorkspaceSkillsDir, readPlatformToken } from "../util/platform.js";
19
- import { deleteSkillCapabilityMemory } from "./skill-memory.js";
20
+ import { computeSkillHash, writeInstallMeta } from "./install-meta.js";
20
21
 
21
22
  const log = getLogger("catalog-install");
22
23
 
@@ -263,13 +264,14 @@ export function uninstallSkillLocally(skillId: string): void {
263
264
 
264
265
  rmSync(skillDir, { recursive: true, force: true });
265
266
  removeSkillsIndexEntry(skillId);
266
- deleteSkillCapabilityMemory(skillId);
267
+ deleteSkillCapabilityNode(skillId);
267
268
  }
268
269
 
269
270
  export async function installSkillLocally(
270
271
  skillId: string,
271
272
  catalogEntry: CatalogSkill,
272
273
  overwrite: boolean,
274
+ contactId?: string,
273
275
  ): Promise<void> {
274
276
  const skillDir = join(getWorkspaceSkillsDir(), skillId);
275
277
  const skillFilePath = join(skillDir, "SKILL.md");
@@ -294,19 +296,18 @@ export async function installSkillLocally(
294
296
  await fetchAndExtractSkill(skillId, skillDir);
295
297
  }
296
298
 
297
- // Write version metadata
298
- if (catalogEntry.version) {
299
- const meta = {
300
- version: catalogEntry.version,
301
- installedAt: new Date().toISOString(),
302
- };
303
- atomicWriteFile(
304
- join(skillDir, "version.json"),
305
- JSON.stringify(meta, null, 2) + "\n",
306
- );
307
- }
299
+ // Write install metadata
300
+ writeInstallMeta(skillDir, {
301
+ origin: "vellum",
302
+ installedAt: new Date().toISOString(),
303
+ ...(catalogEntry.version ? { version: catalogEntry.version } : {}),
304
+ ...(contactId ? { installedBy: contactId } : {}),
305
+ contentHash: computeSkillHash(skillDir) ?? undefined,
306
+ });
308
307
 
309
- // Install npm dependencies if the skill has a package.json
308
+ // Post-install: install dependencies first, then index the skill.
309
+ // Running bun install before upsertSkillsIndex ensures we don't index a
310
+ // skill whose dependencies failed to install.
310
311
  if (existsSync(join(skillDir, "package.json"))) {
311
312
  const bunPath = `${homedir()}/.bun/bin`;
312
313
  execSync("bun install", {
@@ -315,8 +316,6 @@ export async function installSkillLocally(
315
316
  env: { ...process.env, PATH: `${bunPath}:${process.env.PATH}` },
316
317
  });
317
318
  }
318
-
319
- // Register in SKILLS.md only after all steps succeed
320
319
  upsertSkillsIndex(skillId);
321
320
  }
322
321
 
@@ -393,6 +392,16 @@ export async function autoInstallFromCatalog(
393
392
  return false;
394
393
  }
395
394
 
395
+ // If the skill already exists on disk (stale index), re-index it instead
396
+ // of attempting a fresh install that would fail.
397
+ const skillDir = join(getWorkspaceSkillsDir(), skillId);
398
+ if (existsSync(join(skillDir, "SKILL.md"))) {
399
+ upsertSkillsIndex(skillId);
400
+ return true;
401
+ }
402
+
403
+ // installSkillLocally handles dependency installation and SKILLS.md indexing.
396
404
  await installSkillLocally(skillId, entry, false);
405
+
397
406
  return true;
398
407
  }
@@ -1,14 +1,13 @@
1
- import {
2
- existsSync,
3
- readdirSync,
4
- readFileSync,
5
- statSync,
6
- writeFileSync,
7
- } from "node:fs";
1
+ import { existsSync, readFileSync } from "node:fs";
8
2
  import { dirname, join } from "node:path";
9
3
 
10
4
  import { getLogger } from "../util/logger.js";
11
5
  import { getWorkspaceSkillsDir } from "../util/platform.js";
6
+ import {
7
+ computeSkillHash,
8
+ readInstallMeta,
9
+ writeInstallMeta,
10
+ } from "./install-meta.js";
12
11
 
13
12
  const log = getLogger("clawhub");
14
13
 
@@ -54,61 +53,15 @@ export function loadIntegrityManifest(): IntegrityManifest {
54
53
  }
55
54
  }
56
55
 
57
- function saveIntegrityManifest(manifest: IntegrityManifest): void {
58
- writeFileSync(
59
- getIntegrityPath(),
60
- JSON.stringify(manifest, null, 2) + "\n",
61
- "utf-8",
62
- );
63
- }
64
-
65
- /** Collect all file contents in a directory tree, sorted by relative path for determinism. */
66
- function collectFileContents(
67
- dir: string,
68
- prefix = "",
69
- ): Array<{ relPath: string; content: Buffer }> {
70
- const results: Array<{ relPath: string; content: Buffer }> = [];
71
- if (!existsSync(dir)) return results;
72
-
73
- const entries = readdirSync(dir, { withFileTypes: true });
74
- for (const entry of entries) {
75
- const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
76
- const fullPath = join(dir, entry.name);
77
- if (entry.isDirectory()) {
78
- results.push(...collectFileContents(fullPath, relPath));
79
- } else if (entry.isFile()) {
80
- results.push({ relPath, content: readFileSync(fullPath) });
81
- }
82
- }
83
- return results.sort((a, b) => a.relPath.localeCompare(b.relPath));
84
- }
85
-
86
- /**
87
- * Compute a SHA-256 hash over all files in a skill directory.
88
- * Returns format: "v2:sha256hex" (version prefix added to support hash format evolution).
89
- */
90
- function computeSkillHash(skillDir: string): string | null {
91
- if (!existsSync(skillDir) || !statSync(skillDir).isDirectory()) return null;
92
-
93
- const files = collectFileContents(skillDir);
94
- if (files.length === 0) return null;
95
-
96
- const hasher = new Bun.CryptoHasher("sha256");
97
- for (const file of files) {
98
- // Length-prefix each segment to prevent boundary ambiguity collisions
99
- const pathBuf = Buffer.from(file.relPath, "utf-8");
100
- hasher.update(`${pathBuf.length}:`);
101
- hasher.update(pathBuf);
102
- hasher.update(`${file.content.length}:`);
103
- hasher.update(file.content);
104
- }
105
- return `v2:${hasher.digest("hex")}`;
106
- }
107
-
108
56
  /**
109
57
  * Record or verify the content hash of an installed skill.
110
58
  * On first install: stores the hash (trust-on-first-use).
111
59
  * On subsequent installs: compares with stored hash and warns on mismatch.
60
+ *
61
+ * Always writes to `install-meta.json`. For skills that don't have one yet,
62
+ * creates a new `install-meta.json` with minimal metadata. Reads from the
63
+ * legacy `.integrity.json` manifest as a read-only fallback for stored hashes
64
+ * from skills that haven't been migrated yet.
112
65
  */
113
66
  export function verifyAndRecordSkillHash(slug: string): void {
114
67
  const skillDir = join(getManagedSkillsDir(), slug);
@@ -118,17 +71,28 @@ export function verifyAndRecordSkillHash(slug: string): void {
118
71
  return;
119
72
  }
120
73
 
121
- const manifest = loadIntegrityManifest();
122
- const existing = manifest[slug];
74
+ // Try install-meta.json first for stored hash
75
+ const installMeta = readInstallMeta(skillDir);
76
+ let storedHash: string | undefined;
123
77
 
124
- if (existing) {
125
- const storedHash = existing.sha256;
78
+ if (installMeta?.contentHash) {
79
+ storedHash = installMeta.contentHash;
80
+ } else {
81
+ // Fall back to legacy .integrity.json manifest
82
+ const manifest = loadIntegrityManifest();
83
+ const existing = manifest[slug];
84
+ if (existing) {
85
+ storedHash =
86
+ typeof existing.sha256 === "string" ? existing.sha256 : undefined;
87
+ }
88
+ }
126
89
 
127
- // Guard against corrupted manifest entries where sha256 is not a string
90
+ if (storedHash) {
91
+ // Guard against corrupted entries where hash is not a string
128
92
  if (typeof storedHash !== "string") {
129
93
  log.warn(
130
94
  { slug },
131
- "Integrity manifest entry has non-string sha256 — re-recording hash",
95
+ "Stored hash has non-string value — re-recording hash",
132
96
  );
133
97
  } else if (!storedHash.startsWith("v2:")) {
134
98
  // Unknown format (not v2: prefix) — warn about integrity mismatch
@@ -155,9 +119,18 @@ export function verifyAndRecordSkillHash(slug: string): void {
155
119
  );
156
120
  }
157
121
 
158
- // Always store the latest hash
159
- manifest[slug] = { sha256: hash, installedAt: new Date().toISOString() };
160
- saveIntegrityManifest(manifest);
122
+ // Write to install-meta.json (preferred). If no install-meta exists yet,
123
+ // create one with minimal metadata.
124
+ if (installMeta) {
125
+ writeInstallMeta(skillDir, { ...installMeta, contentHash: hash });
126
+ } else {
127
+ writeInstallMeta(skillDir, {
128
+ origin: "clawhub",
129
+ installedAt: new Date().toISOString(),
130
+ slug,
131
+ contentHash: hash,
132
+ });
133
+ }
161
134
  }
162
135
 
163
136
  interface ClawhubInstallResult {
@@ -248,7 +221,7 @@ async function runClawhub(
248
221
 
249
222
  export async function clawhubInstall(
250
223
  slug: string,
251
- opts?: { version?: string },
224
+ opts?: { version?: string; contactId?: string },
252
225
  ): Promise<ClawhubInstallResult> {
253
226
  if (!validateSlug(slug)) {
254
227
  return { success: false, error: `Invalid skill slug: ${slug}` };
@@ -264,7 +237,19 @@ export async function clawhubInstall(
264
237
  result.stderr.trim() || result.stdout.trim() || "Unknown error";
265
238
  return { success: false, error };
266
239
  }
267
- verifyAndRecordSkillHash(slug);
240
+
241
+ // Write install-meta.json for the installed skill.
242
+ // contentHash is included here, so there's no need to call
243
+ // verifyAndRecordSkillHash() — it would just rewrite the same data.
244
+ const skillDir = join(getManagedSkillsDir(), slug);
245
+ writeInstallMeta(skillDir, {
246
+ origin: "clawhub",
247
+ slug,
248
+ installedAt: new Date().toISOString(),
249
+ ...(opts?.contactId ? { installedBy: opts.contactId } : {}),
250
+ contentHash: computeSkillHash(skillDir) ?? undefined,
251
+ });
252
+
268
253
  return { success: true, skillName: slug };
269
254
  } catch (err) {
270
255
  const message = err instanceof Error ? err.message : String(err);
@@ -292,63 +277,63 @@ export async function clawhubUpdate(
292
277
 
293
278
  export async function clawhubSearch(
294
279
  query: string,
280
+ opts?: { limit?: number },
295
281
  ): Promise<ClawhubSearchResult> {
282
+ const limit = opts?.limit ?? 25;
283
+
296
284
  // Empty query: use explore (browse trending) instead of search
297
285
  if (!query.trim()) {
298
- return clawhubExplore();
286
+ return clawhubExplore({ limit });
299
287
  }
300
288
 
289
+ const result = await runClawhub(["search", query, "--limit", String(limit)]);
290
+ if (result.exitCode !== 0) {
291
+ const error =
292
+ result.stderr.trim() || result.stdout.trim() || "Unknown error";
293
+ throw new Error(`clawhub search failed: ${error}`);
294
+ }
295
+ // Try JSON first
301
296
  try {
302
- const result = await runClawhub(["search", query, "--limit", "25"]);
303
- if (result.exitCode !== 0) {
304
- return { skills: [] };
297
+ const parsed = JSON.parse(result.stdout);
298
+ if (Array.isArray(parsed)) {
299
+ return {
300
+ skills: parsed.map((s: ClawhubSearchResultItem) => ({
301
+ ...s,
302
+ source: s.source ?? ("clawhub" as const),
303
+ })),
304
+ };
305
305
  }
306
- // Try JSON first
307
- try {
308
- const parsed = JSON.parse(result.stdout);
309
- if (Array.isArray(parsed)) {
310
- return {
311
- skills: parsed.map((s: ClawhubSearchResultItem) => ({
312
- ...s,
313
- source: s.source ?? ("clawhub" as const),
314
- })),
315
- };
316
- }
317
- if (parsed.skills && Array.isArray(parsed.skills)) {
318
- return {
319
- skills: parsed.skills.map((s: ClawhubSearchResultItem) => ({
320
- ...s,
321
- source: s.source ?? ("clawhub" as const),
322
- })),
323
- };
324
- }
325
- } catch {
326
- // CLI outputs text: "slug vVersion DisplayName (score)"
306
+ if (parsed.skills && Array.isArray(parsed.skills)) {
307
+ return {
308
+ skills: parsed.skills.map((s: ClawhubSearchResultItem) => ({
309
+ ...s,
310
+ source: s.source ?? ("clawhub" as const),
311
+ })),
312
+ };
327
313
  }
314
+ } catch {
315
+ // CLI outputs text: "slug vVersion DisplayName (score)"
316
+ }
328
317
 
329
- // Parse text output lines: "slug vVersion Display Name (score)"
330
- const skills: ClawhubSearchResultItem[] = [];
331
- for (const line of result.stdout.split("\n")) {
332
- const match = line.match(/^(\S+)\s+v(\S+)\s+(.+?)\s+\([\d.]+\)\s*$/);
333
- if (match) {
334
- skills.push({
335
- slug: match[1],
336
- version: match[2],
337
- name: match[3].trim(),
338
- description: "",
339
- author: "",
340
- stars: 0,
341
- installs: 0,
342
- createdAt: 0,
343
- source: "clawhub",
344
- });
345
- }
318
+ // Parse text output lines: "slug vVersion Display Name (score)"
319
+ const skills: ClawhubSearchResultItem[] = [];
320
+ for (const line of result.stdout.split("\n")) {
321
+ const match = line.match(/^(\S+)\s+v(\S+)\s+(.+?)\s+\([\d.]+\)\s*$/);
322
+ if (match) {
323
+ skills.push({
324
+ slug: match[1],
325
+ version: match[2],
326
+ name: match[3].trim(),
327
+ description: "",
328
+ author: "",
329
+ stars: 0,
330
+ installs: 0,
331
+ createdAt: 0,
332
+ source: "clawhub",
333
+ });
346
334
  }
347
- return { skills };
348
- } catch (err) {
349
- log.warn({ err }, "clawhub search failed");
350
- return { skills: [] };
351
335
  }
336
+ return { skills };
352
337
  }
353
338
 
354
339
  export async function clawhubExplore(opts?: {
@@ -358,46 +343,41 @@ export async function clawhubExplore(opts?: {
358
343
  const limit = String(opts?.limit ?? 25);
359
344
  const sort = opts?.sort ?? "installsAllTime";
360
345
 
346
+ const result = await runClawhub([
347
+ "explore",
348
+ "--json",
349
+ "--limit",
350
+ limit,
351
+ "--sort",
352
+ sort,
353
+ ]);
354
+ if (result.exitCode !== 0) {
355
+ const error =
356
+ result.stderr.trim() || result.stdout.trim() || "Unknown error";
357
+ throw new Error(`clawhub explore failed: ${error}`);
358
+ }
361
359
  try {
362
- const result = await runClawhub([
363
- "explore",
364
- "--json",
365
- "--limit",
366
- limit,
367
- "--sort",
368
- sort,
369
- ]);
370
- if (result.exitCode !== 0) {
371
- return { skills: [] };
372
- }
373
- try {
374
- const parsed = JSON.parse(result.stdout);
375
- const items = parsed.items ?? parsed;
376
- if (!Array.isArray(items)) return { skills: [] };
377
-
378
- // Normalize explore response to ClawhubSearchResultItem shape
379
- const skills: ClawhubSearchResultItem[] = items.map(
380
- (item: Record<string, unknown>) => ({
381
- name: (item.displayName as string) ?? (item.slug as string) ?? "",
382
- slug: (item.slug as string) ?? "",
383
- description: (item.summary as string) ?? "",
384
- author: (item.author as string) ?? "",
385
- stars: (item.stats as Record<string, number>)?.stars ?? 0,
386
- installs:
387
- (item.stats as Record<string, number>)?.installsAllTime ?? 0,
388
- version: (item.tags as Record<string, string>)?.latest ?? "",
389
- createdAt: (item.createdAt as number) ?? 0,
390
- source: "clawhub",
391
- }),
392
- );
393
- return { skills };
394
- } catch {
395
- // parse failure
396
- }
397
- return { skills: [] };
398
- } catch (err) {
399
- log.warn({ err }, "clawhub explore failed");
400
- return { skills: [] };
360
+ const parsed = JSON.parse(result.stdout);
361
+ const items = parsed.items ?? parsed;
362
+ if (!Array.isArray(items)) return { skills: [] };
363
+
364
+ // Normalize explore response to ClawhubSearchResultItem shape
365
+ const skills: ClawhubSearchResultItem[] = items.map(
366
+ (item: Record<string, unknown>) => ({
367
+ name: (item.displayName as string) ?? (item.slug as string) ?? "",
368
+ slug: (item.slug as string) ?? "",
369
+ description: (item.summary as string) ?? "",
370
+ author: (item.author as string) ?? "",
371
+ stars: (item.stats as Record<string, number>)?.stars ?? 0,
372
+ installs: (item.stats as Record<string, number>)?.installsAllTime ?? 0,
373
+ version: (item.tags as Record<string, string>)?.latest ?? "",
374
+ createdAt: (item.createdAt as number) ?? 0,
375
+ source: "clawhub",
376
+ }),
377
+ );
378
+ return { skills };
379
+ } catch {
380
+ throw new Error("Failed to parse clawhub explore output");
401
381
  }
402
382
  }
403
383