@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
@@ -170,17 +170,39 @@ describe("Permission Checker", () => {
170
170
  // ── classifyRisk ────────────────────────────────────────────────
171
171
 
172
172
  describe("classifyRisk", () => {
173
- // file_read is always low
173
+ // file_read is low risk except for signing-key material
174
174
  describe("file_read", () => {
175
- test("file_read is always low risk", async () => {
175
+ test("file_read is low risk for regular files", async () => {
176
176
  const risk = await classifyRisk("file_read", { path: "/etc/passwd" });
177
177
  expect(risk).toBe(RiskLevel.Low);
178
178
  });
179
179
 
180
- test("file_read with any path is low risk", async () => {
180
+ test("file_read with arbitrary non-key path is low risk", async () => {
181
181
  const risk = await classifyRisk("file_read", { path: "/tmp/safe.txt" });
182
182
  expect(risk).toBe(RiskLevel.Low);
183
183
  });
184
+
185
+ test("file_read of workspace signing key path is high risk", async () => {
186
+ const workspaceDir = join(checkerTestDir, "workspace");
187
+ const risk = await classifyRisk(
188
+ "file_read",
189
+ { path: "deprecated/actor-token-signing-key" },
190
+ workspaceDir,
191
+ );
192
+ expect(risk).toBe(RiskLevel.High);
193
+ });
194
+
195
+ test("file_read of legacy protected signing key path is high risk", async () => {
196
+ const risk = await classifyRisk("file_read", {
197
+ path: join(
198
+ homedir(),
199
+ ".vellum",
200
+ "protected",
201
+ "actor-token-signing-key",
202
+ ),
203
+ });
204
+ expect(risk).toBe(RiskLevel.High);
205
+ });
184
206
  });
185
207
 
186
208
  // file_write is always low (sandboxed)
@@ -4378,7 +4400,7 @@ describe("Permission Checker", () => {
4378
4400
  });
4379
4401
  });
4380
4402
 
4381
- describe("bash network_mode=proxied — no special-casing", () => {
4403
+ describe("bash network_mode=proxied — risk capped at medium", () => {
4382
4404
  beforeEach(() => {
4383
4405
  clearCache();
4384
4406
  testConfig.permissions = { mode: "workspace" };
@@ -4386,8 +4408,6 @@ describe("bash network_mode=proxied — no special-casing", () => {
4386
4408
  });
4387
4409
 
4388
4410
  test("proxied bash follows normal rules (auto-allowed by default rule)", async () => {
4389
- // Proxied bash is no longer force-prompted — the default allow-bash rule
4390
- // auto-allows low/medium risk commands regardless of network_mode.
4391
4411
  const result = await check(
4392
4412
  "bash",
4393
4413
  { command: "curl https://api.example.com", network_mode: "proxied" },
@@ -4396,6 +4416,41 @@ describe("bash network_mode=proxied — no special-casing", () => {
4396
4416
  expect(result.decision).toBe("allow");
4397
4417
  });
4398
4418
 
4419
+ test("proxied bash caps high-risk commands to medium", async () => {
4420
+ // pipe-to-interpreter (stdin exec) is normally High risk, but proxied mode caps at Medium
4421
+ const risk = await classifyRisk("bash", {
4422
+ command: "cat exploit.py | python3",
4423
+ network_mode: "proxied",
4424
+ });
4425
+ expect(risk).toBe(RiskLevel.Medium);
4426
+ });
4427
+
4428
+ test("pipe to python3 -c is not high risk (inline code, not stdin exec)", async () => {
4429
+ const risk = await classifyRisk("bash", {
4430
+ command: 'cat data.json | python3 -c "import sys; print(sys.stdin.read())"',
4431
+ });
4432
+ expect(risk).toBe(RiskLevel.Low);
4433
+ });
4434
+
4435
+ test("pipe to python3 without -c is high risk (stdin exec)", async () => {
4436
+ const risk = await classifyRisk("bash", {
4437
+ command: "cat exploit.py | python3",
4438
+ });
4439
+ expect(risk).toBe(RiskLevel.High);
4440
+ });
4441
+
4442
+ test("proxied bash with high-risk command is auto-allowed by default rule", async () => {
4443
+ const result = await check(
4444
+ "bash",
4445
+ {
4446
+ command: "cat exploit.py | python3",
4447
+ network_mode: "proxied",
4448
+ },
4449
+ "/tmp",
4450
+ );
4451
+ expect(result.decision).toBe("allow");
4452
+ });
4453
+
4399
4454
  test("host_bash with network_mode=proxied follows normal flow", async () => {
4400
4455
  addRule("host_bash", "**", "everywhere");
4401
4456
  const result = await check(
@@ -4768,15 +4823,14 @@ describe("workspace mode — auto-allow workspace-scoped operations", () => {
4768
4823
  }
4769
4824
  });
4770
4825
 
4771
- // ── proxied bash — follows normal rules (no special-casing) ──
4826
+ // ── proxied bash — risk capped at medium ──
4772
4827
 
4773
- test("bash with network_mode=proxied → allow (follows normal rules in workspace mode)", async () => {
4828
+ test("bash with network_mode=proxied → allow (risk capped at medium)", async () => {
4774
4829
  const result = await check(
4775
4830
  "bash",
4776
4831
  { command: "curl https://api.example.com", network_mode: "proxied" },
4777
4832
  workspaceDir,
4778
4833
  );
4779
- // Default allow-bash rule auto-allows; proxied mode is not special-cased.
4780
4834
  expect(result.decision).toBe("allow");
4781
4835
  });
4782
4836
 
@@ -1,4 +1,10 @@
1
- import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ readFileSync,
5
+ rmSync,
6
+ writeFileSync,
7
+ } from "node:fs";
2
8
  import { join } from "node:path";
3
9
  import { describe, expect, test } from "bun:test";
4
10
  import { mock } from "bun:test";
@@ -15,9 +21,9 @@ mock.module("../util/logger.js", () => ({
15
21
  import {
16
22
  clawhubInspect,
17
23
  clawhubInstall,
18
- loadIntegrityManifest,
19
24
  verifyAndRecordSkillHash,
20
25
  } from "../skills/clawhub.js";
26
+ import type { SkillInstallMeta } from "../skills/install-meta.js";
21
27
 
22
28
  // ---------------------------------------------------------------------------
23
29
  // Slug validation (exercised through public API)
@@ -92,58 +98,65 @@ describe("clawhubInspect slug validation", () => {
92
98
  });
93
99
 
94
100
  // ---------------------------------------------------------------------------
95
- // Integrity manifest edge cases — tested via verifyAndRecordSkillHash
96
- // which is the code path that reads/writes the manifest.
101
+ // Content hash verification — tested via verifyAndRecordSkillHash
102
+ // which reads legacy .integrity.json but always writes to install-meta.json.
97
103
  // ---------------------------------------------------------------------------
98
104
 
99
- describe("integrity manifest", () => {
105
+ describe("content hash verification", () => {
100
106
  function createSkillFiles(slug: string): void {
101
107
  const skillDir = join(TEST_DIR, "skills", slug);
102
108
  mkdirSync(skillDir, { recursive: true });
103
109
  writeFileSync(join(skillDir, "SKILL.md"), "# Test Skill\n", "utf-8");
104
110
  }
105
111
 
112
+ function readSkillInstallMeta(slug: string): SkillInstallMeta {
113
+ const metaPath = join(TEST_DIR, "skills", slug, "install-meta.json");
114
+ return JSON.parse(readFileSync(metaPath, "utf-8")) as SkillInstallMeta;
115
+ }
116
+
106
117
  test("malformed integrity JSON is handled gracefully", () => {
107
118
  mkdirSync(join(TEST_DIR, "skills"), { recursive: true });
108
119
  const integrityPath = join(TEST_DIR, "skills", ".integrity.json");
109
120
  writeFileSync(integrityPath, "{not valid json!!!", "utf-8");
110
121
  createSkillFiles("valid-slug");
111
122
 
112
- // Should not throw — malformed manifest is replaced with a fresh one
123
+ // Should not throw — malformed legacy manifest is ignored; install-meta.json is created
113
124
  verifyAndRecordSkillHash("valid-slug");
114
125
 
115
- // Manifest should now contain a valid entry
116
- const manifest = loadIntegrityManifest();
117
- expect(manifest["valid-slug"]).toBeDefined();
118
- expect(manifest["valid-slug"].sha256).toMatch(/^v2:[0-9a-f]{64}$/);
126
+ // install-meta.json should now contain a valid hash
127
+ const meta = readSkillInstallMeta("valid-slug");
128
+ expect(meta.contentHash).toMatch(/^v2:[0-9a-f]{64}$/);
129
+ expect(meta.origin).toBe("clawhub");
130
+ expect(meta.slug).toBe("valid-slug");
119
131
  });
120
132
 
121
- test("missing integrity manifest is created on first install", () => {
133
+ test("install-meta.json is created on first hash verification for skills without one", () => {
122
134
  const skillsDir = join(TEST_DIR, "skills");
123
135
  mkdirSync(skillsDir, { recursive: true });
124
- const integrityPath = join(skillsDir, ".integrity.json");
125
- // Remove any manifest left by earlier tests so we test the fresh-creation path
126
- rmSync(integrityPath, { force: true });
127
- expect(existsSync(integrityPath)).toBe(false);
128
136
  createSkillFiles("new-skill");
137
+ const metaPath = join(skillsDir, "new-skill", "install-meta.json");
138
+ // Remove any install-meta left by earlier tests
139
+ rmSync(metaPath, { force: true });
140
+ expect(existsSync(metaPath)).toBe(false);
129
141
 
130
142
  verifyAndRecordSkillHash("new-skill");
131
143
 
132
- // Manifest should now exist with the skill's hash
133
- expect(existsSync(integrityPath)).toBe(true);
134
- const manifest = loadIntegrityManifest();
135
- expect(manifest["new-skill"]).toBeDefined();
136
- expect(manifest["new-skill"].sha256).toMatch(/^v2:[0-9a-f]{64}$/);
144
+ // install-meta.json should now exist with the skill's hash
145
+ expect(existsSync(metaPath)).toBe(true);
146
+ const meta = readSkillInstallMeta("new-skill");
147
+ expect(meta.contentHash).toMatch(/^v2:[0-9a-f]{64}$/);
148
+ expect(meta.origin).toBe("clawhub");
149
+ expect(meta.slug).toBe("new-skill");
137
150
  });
138
151
 
139
152
  test("re-install with same content preserves hash", () => {
140
153
  createSkillFiles("stable-skill");
141
154
 
142
155
  verifyAndRecordSkillHash("stable-skill");
143
- const first = loadIntegrityManifest()["stable-skill"].sha256;
156
+ const first = readSkillInstallMeta("stable-skill").contentHash;
144
157
 
145
158
  verifyAndRecordSkillHash("stable-skill");
146
- const second = loadIntegrityManifest()["stable-skill"].sha256;
159
+ const second = readSkillInstallMeta("stable-skill").contentHash;
147
160
 
148
161
  expect(first).toBe(second);
149
162
  });
@@ -151,7 +164,7 @@ describe("integrity manifest", () => {
151
164
  test("re-install with changed content updates hash", () => {
152
165
  createSkillFiles("changing-skill");
153
166
  verifyAndRecordSkillHash("changing-skill");
154
- const first = loadIntegrityManifest()["changing-skill"].sha256;
167
+ const first = readSkillInstallMeta("changing-skill").contentHash;
155
168
 
156
169
  // Modify skill content
157
170
  writeFileSync(
@@ -160,8 +173,25 @@ describe("integrity manifest", () => {
160
173
  "utf-8",
161
174
  );
162
175
  verifyAndRecordSkillHash("changing-skill");
163
- const second = loadIntegrityManifest()["changing-skill"].sha256;
176
+ const second = readSkillInstallMeta("changing-skill").contentHash;
164
177
 
165
178
  expect(first).not.toBe(second);
166
179
  });
180
+
181
+ test(".integrity.json is never written to", () => {
182
+ const skillsDir = join(TEST_DIR, "skills");
183
+ mkdirSync(skillsDir, { recursive: true });
184
+ const integrityPath = join(skillsDir, ".integrity.json");
185
+ // Remove any legacy manifest
186
+ rmSync(integrityPath, { force: true });
187
+ createSkillFiles("no-integrity-write");
188
+
189
+ verifyAndRecordSkillHash("no-integrity-write");
190
+
191
+ // .integrity.json should NOT have been created
192
+ expect(existsSync(integrityPath)).toBe(false);
193
+ // But install-meta.json should exist
194
+ const metaPath = join(skillsDir, "no-integrity-write", "install-meta.json");
195
+ expect(existsSync(metaPath)).toBe(true);
196
+ });
167
197
  });
@@ -208,6 +208,20 @@ describe("CLI command risk guard: elevated assistant subcommands", () => {
208
208
  }
209
209
  });
210
210
 
211
+ test("--help used as option value does not downgrade credentials reveal risk", async () => {
212
+ const risk = await classifyRisk("bash", {
213
+ command: "assistant credentials reveal 123 --service --help",
214
+ });
215
+ expect(risk).toBe(RiskLevel.High);
216
+ });
217
+
218
+ test("-h used as option value does not downgrade oauth mode --set risk", async () => {
219
+ const risk = await classifyRisk("bash", {
220
+ command: "assistant oauth mode --set -h",
221
+ });
222
+ expect(risk).toBe(RiskLevel.High);
223
+ });
224
+
211
225
  test("non-sensitive oauth subcommands remain Low risk", async () => {
212
226
  const lowRiskOauthCommands = [
213
227
  "assistant oauth apps",
@@ -105,7 +105,7 @@ describe("AssistantConfigSchema", () => {
105
105
  shellMaxTimeoutSec: 600,
106
106
  permissionTimeoutSec: 300,
107
107
  toolExecutionTimeoutSec: 120,
108
- providerStreamTimeoutSec: 300,
108
+ providerStreamTimeoutSec: 1800,
109
109
  });
110
110
  expect(result.sandbox).toEqual({
111
111
  enabled: false,
@@ -169,6 +169,7 @@ describe("AssistantConfigSchema", () => {
169
169
  enqueueIntervalMs: 6 * 60 * 60 * 1000,
170
170
  supersededItemRetentionMs: 30 * 24 * 60 * 60 * 1000,
171
171
  conversationRetentionDays: 0,
172
+ llmRequestLogRetentionMs: 7 * 24 * 60 * 60 * 1000,
172
173
  });
173
174
  });
174
175
 
@@ -421,6 +422,8 @@ describe("AssistantConfigSchema", () => {
421
422
  const result = AssistantConfigSchema.parse({});
422
423
  expect(result.permissions).toEqual({
423
424
  mode: "workspace",
425
+ askBeforeActing: true,
426
+ hostAccess: false,
424
427
  });
425
428
  });
426
429
 
@@ -1128,6 +1131,8 @@ describe("loadConfig with schema validation", () => {
1128
1131
  const config = loadConfig();
1129
1132
  expect(config.permissions).toEqual({
1130
1133
  mode: "workspace",
1134
+ askBeforeActing: true,
1135
+ hostAccess: false,
1131
1136
  });
1132
1137
  });
1133
1138
 
@@ -0,0 +1,302 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import { Command } from "commander";
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Mock state
7
+ // ---------------------------------------------------------------------------
8
+
9
+ let mockPlatformClientCreate: () => Promise<Record<
10
+ string,
11
+ unknown
12
+ > | null> = async () => null;
13
+
14
+ let mockLoadRawConfig: () => Record<string, unknown> = () => ({});
15
+ const mockSaveRawConfigCalls: Array<Record<string, unknown>> = [];
16
+ const mockSetNestedValueCalls: Array<{
17
+ obj: Record<string, unknown>;
18
+ key: string;
19
+ value: unknown;
20
+ }> = [];
21
+ let mockGetNestedValue: (
22
+ obj: Record<string, unknown>,
23
+ key: string,
24
+ ) => unknown = () => undefined;
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // Mocks — platform/client (controls requirePlatformConnection)
28
+ // ---------------------------------------------------------------------------
29
+
30
+ mock.module("../platform/client.js", () => ({
31
+ VellumPlatformClient: {
32
+ create: () => mockPlatformClientCreate(),
33
+ },
34
+ }));
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Mocks — config/loader
38
+ // ---------------------------------------------------------------------------
39
+
40
+ mock.module("../config/loader.js", () => ({
41
+ getConfig: () => ({ services: {} }),
42
+ loadConfig: () => ({ services: {} }),
43
+ saveConfig: () => {},
44
+ invalidateConfigCache: () => {},
45
+ loadRawConfig: () => mockLoadRawConfig(),
46
+ saveRawConfig: (raw: Record<string, unknown>) => {
47
+ mockSaveRawConfigCalls.push(raw);
48
+ },
49
+ applyNestedDefaults: (c: unknown) => c,
50
+ deepMergeMissing: (a: unknown) => a,
51
+ deepMergeOverwrite: (a: unknown) => a,
52
+ mergeDefaultWorkspaceConfig: () => {},
53
+ getNestedValue: (obj: Record<string, unknown>, key: string) =>
54
+ mockGetNestedValue(obj, key),
55
+ setNestedValue: (obj: Record<string, unknown>, key: string, value: unknown) =>
56
+ mockSetNestedValueCalls.push({ obj, key, value }),
57
+ API_KEY_PROVIDERS: [
58
+ "anthropic",
59
+ "openai",
60
+ "gemini",
61
+ "ollama",
62
+ "fireworks",
63
+ "openrouter",
64
+ "brave",
65
+ "perplexity",
66
+ ],
67
+ }));
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Mocks — util/logger (suppress log output)
71
+ // ---------------------------------------------------------------------------
72
+
73
+ mock.module("../util/logger.js", () => ({
74
+ getLogger: () => ({
75
+ info: () => {},
76
+ warn: () => {},
77
+ error: () => {},
78
+ debug: () => {},
79
+ }),
80
+ getCliLogger: () => ({
81
+ info: () => {},
82
+ warn: () => {},
83
+ error: () => {},
84
+ debug: () => {},
85
+ }),
86
+ }));
87
+
88
+ // ---------------------------------------------------------------------------
89
+ // Mocks — oauth/oauth-store (transitive dep of oauth/shared.ts)
90
+ // ---------------------------------------------------------------------------
91
+
92
+ mock.module("../oauth/oauth-store.js", () => ({
93
+ disconnectOAuthProvider: async () => "not-found" as const,
94
+ getConnection: () => undefined,
95
+ getConnectionByProvider: () => undefined,
96
+ listConnections: () => [],
97
+ deleteConnection: () => false,
98
+ upsertApp: async () => ({}),
99
+ getApp: () => undefined,
100
+ getAppByProviderAndClientId: () => undefined,
101
+ getMostRecentAppByProvider: () => undefined,
102
+ listApps: () => [],
103
+ deleteApp: async () => false,
104
+ getProvider: () => undefined,
105
+ listProviders: () => [],
106
+ registerProvider: () => ({}),
107
+ updateProvider: () => undefined,
108
+ deleteProvider: () => false,
109
+ seedProviders: () => {},
110
+ getActiveConnection: () => undefined,
111
+ listActiveConnectionsByProvider: () => [],
112
+ createConnection: () => ({}),
113
+ isProviderConnected: () => false,
114
+ updateConnection: () => ({}),
115
+ }));
116
+
117
+ // ---------------------------------------------------------------------------
118
+ // Import the module under test (after mocks are registered)
119
+ // ---------------------------------------------------------------------------
120
+
121
+ const { registerConfigCommand } = await import("../cli/commands/config.js");
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // Test helper
125
+ // ---------------------------------------------------------------------------
126
+
127
+ async function runCli(
128
+ args: string[],
129
+ ): Promise<{ exitCode: number; stdout: string }> {
130
+ const originalStdoutWrite = process.stdout.write.bind(process.stdout);
131
+ const originalStderrWrite = process.stderr.write.bind(process.stderr);
132
+ const stdoutChunks: string[] = [];
133
+
134
+ process.stdout.write = ((chunk: unknown) => {
135
+ stdoutChunks.push(typeof chunk === "string" ? chunk : String(chunk));
136
+ return true;
137
+ }) as typeof process.stdout.write;
138
+
139
+ process.stderr.write = (() => true) as typeof process.stderr.write;
140
+
141
+ process.exitCode = 0;
142
+
143
+ try {
144
+ const program = new Command();
145
+ program.option("--json", "JSON output");
146
+ program.exitOverride();
147
+ program.configureOutput({
148
+ writeErr: () => {},
149
+ writeOut: (str: string) => stdoutChunks.push(str),
150
+ });
151
+ registerConfigCommand(program);
152
+ await program.parseAsync(args);
153
+ } catch {
154
+ if (process.exitCode === 0) process.exitCode = 1;
155
+ } finally {
156
+ process.stdout.write = originalStdoutWrite;
157
+ process.stderr.write = originalStderrWrite;
158
+ }
159
+
160
+ const exitCode = process.exitCode ?? 0;
161
+ process.exitCode = 0;
162
+
163
+ return {
164
+ exitCode,
165
+ stdout: stdoutChunks.join(""),
166
+ };
167
+ }
168
+
169
+ // ---------------------------------------------------------------------------
170
+ // Tests
171
+ // ---------------------------------------------------------------------------
172
+
173
+ describe("config set — platform connection guard for service mode paths", () => {
174
+ beforeEach(() => {
175
+ // Default: not connected to platform
176
+ mockPlatformClientCreate = async () => null;
177
+ mockLoadRawConfig = () => ({});
178
+ mockSaveRawConfigCalls.length = 0;
179
+ mockSetNestedValueCalls.length = 0;
180
+ mockGetNestedValue = () => undefined;
181
+ });
182
+
183
+ test("config set services.inference.mode managed — fails when not connected", async () => {
184
+ const { exitCode, stdout } = await runCli([
185
+ "node",
186
+ "assistant",
187
+ "--json",
188
+ "config",
189
+ "set",
190
+ "services.inference.mode",
191
+ "managed",
192
+ ]);
193
+
194
+ expect(exitCode).toBe(1);
195
+ const parsed = JSON.parse(stdout);
196
+ expect(parsed.ok).toBe(false);
197
+ expect(parsed.error).toContain("vellum platform connect");
198
+ expect(parsed.error).toContain("Not connected");
199
+ // Config should NOT have been written
200
+ expect(mockSaveRawConfigCalls).toHaveLength(0);
201
+ expect(mockSetNestedValueCalls).toHaveLength(0);
202
+ });
203
+
204
+ test("config set services.image-generation.mode your-own — succeeds without platform connection", async () => {
205
+ const { exitCode } = await runCli([
206
+ "node",
207
+ "assistant",
208
+ "--json",
209
+ "config",
210
+ "set",
211
+ "services.image-generation.mode",
212
+ "your-own",
213
+ ]);
214
+
215
+ expect(exitCode).toBe(0);
216
+ // Config should have been written — setting to "your-own" doesn't need platform
217
+ expect(mockSetNestedValueCalls).toHaveLength(1);
218
+ expect(mockSetNestedValueCalls[0]!.key).toBe(
219
+ "services.image-generation.mode",
220
+ );
221
+ expect(mockSetNestedValueCalls[0]!.value).toBe("your-own");
222
+ expect(mockSaveRawConfigCalls).toHaveLength(1);
223
+ });
224
+
225
+ test("config set calls.enabled true — succeeds without platform connection", async () => {
226
+ const { exitCode } = await runCli([
227
+ "node",
228
+ "assistant",
229
+ "config",
230
+ "set",
231
+ "calls.enabled",
232
+ "true",
233
+ ]);
234
+
235
+ expect(exitCode).toBe(0);
236
+ // Config should have been written
237
+ expect(mockSetNestedValueCalls).toHaveLength(1);
238
+ expect(mockSetNestedValueCalls[0]!.key).toBe("calls.enabled");
239
+ expect(mockSetNestedValueCalls[0]!.value).toBe(true);
240
+ expect(mockSaveRawConfigCalls).toHaveLength(1);
241
+ });
242
+
243
+ test("config get services.inference.mode — works without platform connection", async () => {
244
+ mockGetNestedValue = (_obj, key) => {
245
+ if (key === "services.inference.mode") return "your-own";
246
+ return undefined;
247
+ };
248
+
249
+ const { exitCode } = await runCli([
250
+ "node",
251
+ "assistant",
252
+ "config",
253
+ "get",
254
+ "services.inference.mode",
255
+ ]);
256
+
257
+ expect(exitCode).toBe(0);
258
+ // No writes should have occurred
259
+ expect(mockSaveRawConfigCalls).toHaveLength(0);
260
+ expect(mockSetNestedValueCalls).toHaveLength(0);
261
+ });
262
+
263
+ test("config set services.web-search.mode managed — fails when not connected", async () => {
264
+ const { exitCode, stdout } = await runCli([
265
+ "node",
266
+ "assistant",
267
+ "--json",
268
+ "config",
269
+ "set",
270
+ "services.web-search.mode",
271
+ "managed",
272
+ ]);
273
+
274
+ expect(exitCode).toBe(1);
275
+ const parsed = JSON.parse(stdout);
276
+ expect(parsed.ok).toBe(false);
277
+ expect(parsed.error).toContain("vellum platform connect");
278
+ expect(mockSaveRawConfigCalls).toHaveLength(0);
279
+ });
280
+
281
+ test("config set services.inference.mode managed — succeeds when connected", async () => {
282
+ mockPlatformClientCreate = async () => ({
283
+ platformAssistantId: "asst-123",
284
+ fetch: async () => new Response(),
285
+ });
286
+
287
+ const { exitCode } = await runCli([
288
+ "node",
289
+ "assistant",
290
+ "config",
291
+ "set",
292
+ "services.inference.mode",
293
+ "managed",
294
+ ]);
295
+
296
+ expect(exitCode).toBe(0);
297
+ expect(mockSetNestedValueCalls).toHaveLength(1);
298
+ expect(mockSetNestedValueCalls[0]!.key).toBe("services.inference.mode");
299
+ expect(mockSetNestedValueCalls[0]!.value).toBe("managed");
300
+ expect(mockSaveRawConfigCalls).toHaveLength(1);
301
+ });
302
+ });
@@ -8,7 +8,7 @@
8
8
  * 4. Missing guardian binding causes a skip
9
9
  */
10
10
 
11
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
11
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
12
12
 
13
13
  mock.module("../util/logger.js", () => ({
14
14
  getLogger: () =>
@@ -76,7 +76,7 @@ import {
76
76
  generateCanonicalRequestCode,
77
77
  listCanonicalGuardianDeliveries,
78
78
  } from "../memory/canonical-guardian-store.js";
79
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
79
+ import { getDb, initializeDb } from "../memory/db.js";
80
80
  import { bridgeConfirmationRequestToGuardian } from "../runtime/confirmation-request-guardian-bridge.js";
81
81
 
82
82
  initializeDb();
@@ -87,10 +87,6 @@ function resetTables(): void {
87
87
  db.run("DELETE FROM canonical_guardian_requests");
88
88
  }
89
89
 
90
- afterAll(() => {
91
- resetDb();
92
- });
93
-
94
90
  // ---------------------------------------------------------------------------
95
91
  // Helpers
96
92
  // ---------------------------------------------------------------------------