@vellumai/assistant 0.5.16 → 0.6.0

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 (407) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/Dockerfile +0 -3
  3. package/knip.json +2 -1
  4. package/openapi.yaml +660 -80
  5. package/package.json +1 -1
  6. package/src/__tests__/actor-token-service.test.ts +68 -0
  7. package/src/__tests__/agent-loop.test.ts +0 -32
  8. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  9. package/src/__tests__/anthropic-provider.test.ts +57 -3
  10. package/src/__tests__/app-compiler.test.ts +120 -0
  11. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  12. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  13. package/src/__tests__/call-domain.test.ts +2 -6
  14. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  15. package/src/__tests__/call-recovery.test.ts +2 -6
  16. package/src/__tests__/call-routes-http.test.ts +2 -6
  17. package/src/__tests__/call-store.test.ts +2 -6
  18. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  19. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  20. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  21. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  22. package/src/__tests__/checker.test.ts +25 -3
  23. package/src/__tests__/clawhub.test.ts +54 -24
  24. package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
  25. package/src/__tests__/cli-memory.test.ts +74 -69
  26. package/src/__tests__/config-schema.test.ts +1 -1
  27. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  28. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  29. package/src/__tests__/contacts-tools.test.ts +31 -0
  30. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  31. package/src/__tests__/context-token-estimator.test.ts +175 -10
  32. package/src/__tests__/conversation-agent-loop-overflow.test.ts +9 -0
  33. package/src/__tests__/conversation-agent-loop.test.ts +9 -0
  34. package/src/__tests__/conversation-attachments.test.ts +2 -6
  35. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  36. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  37. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  38. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  39. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  40. package/src/__tests__/conversation-error.test.ts +33 -2
  41. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  42. package/src/__tests__/conversation-history-web-search.test.ts +5 -0
  43. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  44. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  45. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  46. package/src/__tests__/conversation-store.test.ts +2 -6
  47. package/src/__tests__/conversation-usage.test.ts +2 -6
  48. package/src/__tests__/conversation-wipe.test.ts +11 -408
  49. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  50. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  51. package/src/__tests__/credential-security-e2e.test.ts +2 -0
  52. package/src/__tests__/followup-tools.test.ts +2 -6
  53. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  54. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  55. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  56. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  57. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  58. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  59. package/src/__tests__/guardian-action-store.test.ts +2 -6
  60. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  61. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  62. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  63. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  64. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  65. package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
  66. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  67. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  68. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  69. package/src/__tests__/injection-block.test.ts +154 -0
  70. package/src/__tests__/install-meta.test.ts +506 -0
  71. package/src/__tests__/install-skill-routing.test.ts +292 -0
  72. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  73. package/src/__tests__/invite-routes-http.test.ts +2 -6
  74. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -14
  75. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  76. package/src/__tests__/llm-context-route-provider.test.ts +2 -6
  77. package/src/__tests__/llm-request-log-turn-query.test.ts +2 -6
  78. package/src/__tests__/llm-usage-store.test.ts +2 -6
  79. package/src/__tests__/log-export-workspace.test.ts +2 -6
  80. package/src/__tests__/managed-store.test.ts +38 -11
  81. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  82. package/src/__tests__/memory-recall-log-store.test.ts +2 -6
  83. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  84. package/src/__tests__/non-member-access-request.test.ts +2 -6
  85. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  86. package/src/__tests__/oauth-cli.test.ts +364 -2
  87. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  88. package/src/__tests__/outlook-attachments.test.ts +301 -0
  89. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  90. package/src/__tests__/outlook-categories.test.ts +212 -0
  91. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  92. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  93. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  94. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  95. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  96. package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
  97. package/src/__tests__/outlook-trash.test.ts +77 -0
  98. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  99. package/src/__tests__/platform-callback-registration.test.ts +4 -4
  100. package/src/__tests__/playbook-execution.test.ts +76 -80
  101. package/src/__tests__/playbook-tools.test.ts +5 -7
  102. package/src/__tests__/provider-error-scenarios.test.ts +21 -0
  103. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  104. package/src/__tests__/registry.test.ts +2 -2
  105. package/src/__tests__/require-fresh-approval.test.ts +64 -2
  106. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  107. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  108. package/src/__tests__/schedule-store.test.ts +2 -6
  109. package/src/__tests__/schedule-tools.test.ts +2 -6
  110. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  111. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  112. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  113. package/src/__tests__/search-skills-unified.test.ts +421 -0
  114. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  115. package/src/__tests__/send-endpoint-busy.test.ts +2 -6
  116. package/src/__tests__/sequence-store.test.ts +2 -6
  117. package/src/__tests__/server-history-render.test.ts +2 -6
  118. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  119. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  120. package/src/__tests__/skill-load-feature-flag.test.ts +11 -11
  121. package/src/__tests__/skill-memory.test.ts +140 -98
  122. package/src/__tests__/skills-uninstall.test.ts +2 -2
  123. package/src/__tests__/skills.test.ts +1 -1
  124. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  125. package/src/__tests__/task-compiler.test.ts +2 -6
  126. package/src/__tests__/task-management-tools.test.ts +2 -6
  127. package/src/__tests__/task-memory-cleanup.test.ts +173 -229
  128. package/src/__tests__/task-runner.test.ts +2 -6
  129. package/src/__tests__/task-scheduler.test.ts +2 -6
  130. package/src/__tests__/test-preload.ts +3 -0
  131. package/src/__tests__/tool-approval-handler.test.ts +2 -6
  132. package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
  133. package/src/__tests__/tool-side-effects-slack-dm.test.ts +276 -0
  134. package/src/__tests__/trust-store.test.ts +1 -1
  135. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
  136. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -6
  137. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
  138. package/src/__tests__/trusted-contact-verification.test.ts +2 -6
  139. package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
  140. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
  141. package/src/__tests__/usage-routes.test.ts +2 -6
  142. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  143. package/src/__tests__/voice-invite-redemption.test.ts +2 -6
  144. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
  145. package/src/__tests__/voice-session-bridge.test.ts +2 -6
  146. package/src/__tests__/volume-security-guard.test.ts +2 -0
  147. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  148. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
  149. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
  150. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  151. package/src/__tests__/workspace-policy.test.ts +1 -1
  152. package/src/agent/attachments.ts +7 -2
  153. package/src/agent/image-optimize.ts +165 -0
  154. package/src/agent/loop.ts +1 -15
  155. package/src/bundler/app-compiler.ts +179 -2
  156. package/src/bundler/package-resolver.ts +3 -5
  157. package/src/cli/__tests__/notifications.test.ts +1 -2
  158. package/src/cli/cli-memory.ts +67 -64
  159. package/src/cli/commands/avatar.ts +3 -3
  160. package/src/cli/commands/config.ts +26 -13
  161. package/src/cli/commands/doctor.ts +2 -2
  162. package/src/cli/commands/memory.ts +41 -55
  163. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  164. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  165. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  166. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  167. package/src/cli/commands/oauth/connect.ts +11 -6
  168. package/src/cli/commands/oauth/mode.ts +7 -0
  169. package/src/cli/commands/oauth/shared.ts +39 -3
  170. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  171. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  172. package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
  173. package/src/cli/commands/platform/index.ts +16 -16
  174. package/src/cli/commands/skills.ts +88 -16
  175. package/src/cli/commands/trust.ts +2 -2
  176. package/src/cli/lib/daemon-credential-client.ts +2 -3
  177. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  178. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  179. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  180. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  181. package/src/config/bundled-skills/gmail/SKILL.md +2 -10
  182. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  183. package/src/config/bundled-skills/messaging/SKILL.md +10 -18
  184. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  185. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  186. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  187. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  188. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  189. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  190. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  191. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  192. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  193. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  194. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  195. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  196. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  197. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  198. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  199. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  200. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  201. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  202. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  203. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  204. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  205. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  206. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  207. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  208. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  209. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  210. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  211. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  212. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  213. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  214. package/src/config/bundled-skills/slack/SKILL.md +1 -7
  215. package/src/config/bundled-tool-registry.ts +56 -4
  216. package/src/config/env-registry.ts +15 -8
  217. package/src/config/feature-flag-registry.json +21 -124
  218. package/src/config/schemas/platform.ts +8 -0
  219. package/src/config/schemas/timeouts.ts +1 -1
  220. package/src/config/skills.ts +18 -7
  221. package/src/context/token-estimator.ts +25 -18
  222. package/src/context/window-manager.ts +6 -2
  223. package/src/credential-execution/process-manager.ts +3 -1
  224. package/src/daemon/context-overflow-reducer.ts +46 -2
  225. package/src/daemon/conversation-agent-loop-handlers.ts +123 -82
  226. package/src/daemon/conversation-agent-loop.ts +96 -61
  227. package/src/daemon/conversation-error.ts +31 -8
  228. package/src/daemon/conversation-lifecycle.ts +33 -0
  229. package/src/daemon/conversation-media-retry.ts +85 -7
  230. package/src/daemon/conversation-notifiers.ts +4 -1
  231. package/src/daemon/conversation-runtime-assembly.ts +5 -0
  232. package/src/daemon/conversation.ts +41 -2
  233. package/src/daemon/daemon-control.ts +8 -2
  234. package/src/daemon/handlers/shared.ts +22 -12
  235. package/src/daemon/handlers/skills.ts +416 -202
  236. package/src/daemon/lifecycle.ts +40 -1
  237. package/src/daemon/main.ts +5 -1
  238. package/src/daemon/message-types/conversations.ts +4 -1
  239. package/src/daemon/message-types/messages.ts +3 -1
  240. package/src/daemon/message-types/skills.ts +97 -36
  241. package/src/daemon/providers-setup.ts +5 -0
  242. package/src/daemon/server.ts +11 -2
  243. package/src/daemon/tool-side-effects.ts +27 -5
  244. package/src/heartbeat/heartbeat-service.ts +1 -0
  245. package/src/hooks/cli.ts +2 -2
  246. package/src/hooks/runner.ts +15 -38
  247. package/src/inbound/platform-callback-registration.ts +14 -14
  248. package/src/memory/admin.ts +11 -45
  249. package/src/memory/conversation-bootstrap.ts +2 -0
  250. package/src/memory/conversation-crud.ts +242 -348
  251. package/src/memory/conversation-group-migration.ts +157 -0
  252. package/src/memory/conversation-queries.ts +4 -2
  253. package/src/memory/db-init.ts +30 -3
  254. package/src/memory/embed.ts +73 -0
  255. package/src/memory/embedding-backend.ts +8 -14
  256. package/src/memory/embedding-runtime-manager.ts +12 -114
  257. package/src/memory/fingerprint.ts +2 -2
  258. package/src/memory/graph/bootstrap.ts +512 -0
  259. package/src/memory/graph/capability-seed.ts +297 -0
  260. package/src/memory/graph/consolidation.ts +691 -0
  261. package/src/memory/graph/conversation-graph-memory.ts +630 -0
  262. package/src/memory/graph/decay.test.ts +208 -0
  263. package/src/memory/graph/decay.ts +195 -0
  264. package/src/memory/graph/extraction-job.ts +69 -0
  265. package/src/memory/graph/extraction.test.ts +936 -0
  266. package/src/memory/graph/extraction.ts +1254 -0
  267. package/src/memory/graph/graph-search.ts +266 -0
  268. package/src/memory/graph/image-ref-utils.ts +29 -0
  269. package/src/memory/graph/injection.test.ts +513 -0
  270. package/src/memory/graph/injection.ts +439 -0
  271. package/src/memory/graph/inspect.ts +534 -0
  272. package/src/memory/graph/narrative.ts +267 -0
  273. package/src/memory/graph/pattern-scan.ts +269 -0
  274. package/src/memory/graph/retriever.ts +1008 -0
  275. package/src/memory/graph/scoring.test.ts +548 -0
  276. package/src/memory/graph/scoring.ts +232 -0
  277. package/src/memory/graph/serendipity.ts +65 -0
  278. package/src/memory/graph/store.test.ts +1050 -0
  279. package/src/memory/graph/store.ts +699 -0
  280. package/src/memory/graph/tool-handlers.ts +426 -0
  281. package/src/memory/graph/tools.ts +141 -0
  282. package/src/memory/graph/triggers.test.ts +487 -0
  283. package/src/memory/graph/triggers.ts +223 -0
  284. package/src/memory/graph/types.ts +271 -0
  285. package/src/memory/group-crud.ts +191 -0
  286. package/src/memory/indexer.ts +37 -19
  287. package/src/memory/job-handlers/cleanup.ts +0 -53
  288. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  289. package/src/memory/job-handlers/embedding.ts +5 -31
  290. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  291. package/src/memory/job-handlers/summarization.ts +32 -17
  292. package/src/memory/job-utils.ts +1 -1
  293. package/src/memory/jobs-store.ts +50 -70
  294. package/src/memory/jobs-worker.ts +147 -112
  295. package/src/memory/message-content.ts +1 -0
  296. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  297. package/src/memory/migrations/203-drop-memory-items-tables.ts +23 -0
  298. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  299. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  300. package/src/memory/migrations/index.ts +4 -0
  301. package/src/memory/migrations/registry.ts +8 -0
  302. package/src/memory/qdrant-client.ts +44 -17
  303. package/src/memory/schema/index.ts +1 -0
  304. package/src/memory/schema/memory-graph.ts +139 -0
  305. package/src/memory/search/semantic.ts +47 -91
  306. package/src/memory/task-memory-cleanup.ts +28 -50
  307. package/src/messaging/providers/outlook/adapter.ts +8 -1
  308. package/src/messaging/providers/outlook/client.ts +299 -0
  309. package/src/messaging/providers/outlook/types.ts +118 -0
  310. package/src/notifications/adapters/macos.ts +1 -0
  311. package/src/notifications/copy-composer.ts +9 -0
  312. package/src/notifications/signal.ts +16 -0
  313. package/src/oauth/seed-providers.ts +2 -1
  314. package/src/permissions/checker.ts +24 -3
  315. package/src/permissions/defaults.ts +4 -4
  316. package/src/permissions/workspace-policy.ts +1 -1
  317. package/src/playbooks/playbook-compiler.ts +19 -18
  318. package/src/playbooks/types.ts +4 -3
  319. package/src/prompts/system-prompt.ts +3 -29
  320. package/src/providers/anthropic/client.ts +47 -19
  321. package/src/providers/gemini/client.ts +1 -1
  322. package/src/providers/openai/client.ts +1 -1
  323. package/src/providers/registry.ts +1 -1
  324. package/src/providers/retry.ts +19 -3
  325. package/src/runtime/actor-trust-resolver.ts +5 -1
  326. package/src/runtime/auth/route-policy.ts +7 -0
  327. package/src/runtime/guardian-reply-router.ts +5 -1
  328. package/src/runtime/http-server.ts +23 -3
  329. package/src/runtime/middleware/auth.ts +20 -0
  330. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  331. package/src/runtime/routes/attachment-routes.ts +106 -16
  332. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  333. package/src/runtime/routes/btw-routes.ts +8 -0
  334. package/src/runtime/routes/conversation-management-routes.ts +2 -0
  335. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  336. package/src/runtime/routes/debug-routes.ts +1 -1
  337. package/src/runtime/routes/global-search-routes.ts +21 -19
  338. package/src/runtime/routes/group-routes.ts +207 -0
  339. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  340. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  341. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  342. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  343. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  344. package/src/runtime/routes/memory-item-routes.test.ts +2 -14
  345. package/src/runtime/routes/memory-item-routes.ts +341 -388
  346. package/src/runtime/routes/schedule-routes.ts +2 -0
  347. package/src/runtime/routes/skills-routes.ts +103 -37
  348. package/src/runtime/routes/work-items-routes.test.ts +2 -6
  349. package/src/schedule/scheduler.ts +8 -1
  350. package/src/security/oauth2.ts +1 -1
  351. package/src/security/secure-keys.ts +4 -8
  352. package/src/shared/provider-env-vars.ts +19 -0
  353. package/src/skills/catalog-cache.ts +5 -0
  354. package/src/skills/catalog-install.ts +15 -14
  355. package/src/skills/clawhub.ts +134 -154
  356. package/src/skills/install-meta.ts +208 -0
  357. package/src/skills/managed-store.ts +27 -16
  358. package/src/skills/skill-memory.ts +152 -77
  359. package/src/skills/skillssh-registry.ts +19 -17
  360. package/src/tasks/task-runner.ts +3 -1
  361. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  362. package/src/tools/browser/runtime-check.ts +3 -1
  363. package/src/tools/memory/register.ts +63 -46
  364. package/src/tools/permission-checker.ts +7 -1
  365. package/src/tools/shared/filesystem/image-read.ts +22 -85
  366. package/src/tools/terminal/safe-env.ts +1 -0
  367. package/src/tools/tool-manifest.ts +3 -3
  368. package/src/util/browser.ts +25 -10
  369. package/src/util/bun-runtime.ts +172 -0
  370. package/src/watcher/providers/outlook-calendar.ts +343 -0
  371. package/src/watcher/providers/outlook.ts +198 -0
  372. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  373. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  374. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  375. package/src/workspace/migrations/registry.ts +6 -0
  376. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  377. package/src/__tests__/journal-context.test.ts +0 -268
  378. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  379. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  380. package/src/__tests__/memory-query-builder.test.ts +0 -59
  381. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  382. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  383. package/src/__tests__/memory-regressions.test.ts +0 -3696
  384. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  385. package/src/daemon/conversation-memory.ts +0 -207
  386. package/src/memory/conversation-starters-cadence.ts +0 -74
  387. package/src/memory/items-extractor.ts +0 -860
  388. package/src/memory/job-handlers/batch-extraction.ts +0 -753
  389. package/src/memory/job-handlers/extraction.ts +0 -40
  390. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
  391. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  392. package/src/memory/journal-memory.ts +0 -224
  393. package/src/memory/query-builder.ts +0 -47
  394. package/src/memory/query-expansion.ts +0 -83
  395. package/src/memory/retriever.test.ts +0 -1592
  396. package/src/memory/retriever.ts +0 -1331
  397. package/src/memory/search/formatting.test.ts +0 -140
  398. package/src/memory/search/formatting.ts +0 -262
  399. package/src/memory/search/mmr.ts +0 -139
  400. package/src/memory/search/ranking.ts +0 -15
  401. package/src/memory/search/staleness.ts +0 -40
  402. package/src/memory/search/tier-classifier.ts +0 -18
  403. package/src/memory/search/types.ts +0 -121
  404. package/src/prompts/journal-context.ts +0 -154
  405. package/src/tools/memory/definitions.ts +0 -69
  406. package/src/tools/memory/handlers.test.ts +0 -562
  407. package/src/tools/memory/handlers.ts +0 -434
@@ -1,17 +1,25 @@
1
- import { and, eq } from "drizzle-orm";
1
+ import { and, eq, sql } from "drizzle-orm";
2
2
  import { v4 as uuid } from "uuid";
3
3
 
4
+ import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
4
5
  import { getConfig } from "../config/loader.js";
5
6
  import { resolveSkillStates } from "../config/skill-state.js";
6
7
  import { loadSkillCatalog, type SkillSummary } from "../config/skills.js";
7
8
  import { getDb } from "../memory/db.js";
8
- import { computeMemoryFingerprint } from "../memory/fingerprint.js";
9
9
  import { enqueueMemoryJob } from "../memory/jobs-store.js";
10
- import { memoryItems } from "../memory/schema.js";
10
+ import { memoryGraphNodes } from "../memory/schema.js";
11
11
  import { getLogger } from "../util/logger.js";
12
+ import { getCachedCatalogSync } from "./catalog-cache.js";
13
+ import type { CatalogSkill } from "./catalog-install.js";
12
14
 
13
15
  const log = getLogger("skill-memory");
14
16
 
17
+ /** Escape SQL LIKE wildcards so they match literally.
18
+ * Uses backslash as the escape character — callers must pair with ESCAPE '\\'. */
19
+ function escapeLike(s: string): string {
20
+ return s.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
21
+ }
22
+
15
23
  /**
16
24
  * Generic input for building capability statements.
17
25
  * Decoupled from CatalogSkill so other skill sources (e.g. bundled skills) can
@@ -39,6 +47,20 @@ export function fromSkillSummary(entry: SkillSummary): SkillCapabilityInput {
39
47
  };
40
48
  }
41
49
 
50
+ /**
51
+ * Convert a CatalogSkill to a SkillCapabilityInput.
52
+ * CatalogSkill stores display-name and hints inside nested metadata.
53
+ */
54
+ export function fromCatalogSkill(entry: CatalogSkill): SkillCapabilityInput {
55
+ return {
56
+ id: entry.id,
57
+ displayName: entry.metadata?.vellum?.["display-name"] ?? entry.name,
58
+ description: entry.description,
59
+ activationHints: entry.metadata?.vellum?.["activation-hints"],
60
+ avoidWhen: entry.metadata?.vellum?.["avoid-when"],
61
+ };
62
+ }
63
+
42
64
  /**
43
65
  * Build a semantically rich capability statement from a skill capability input.
44
66
  * Truncated to 500 chars max (matching the limit used by memory item extraction).
@@ -62,8 +84,17 @@ export function buildCapabilityStatement(input: SkillCapabilityInput): string {
62
84
  return statement;
63
85
  }
64
86
 
87
+ /** Default emotional charge for capability graph nodes. */
88
+ const DEFAULT_EMOTIONAL_CHARGE = JSON.stringify({
89
+ valence: 0,
90
+ intensity: 0.1,
91
+ decayCurve: "linear",
92
+ decayRate: 0.05,
93
+ originalIntensity: 0.1,
94
+ });
95
+
65
96
  /**
66
- * Upsert a capability memory item for a skill.
97
+ * Upsert a capability memory graph node for a skill.
67
98
  * Best-effort: errors are logged but never thrown.
68
99
  */
69
100
  export function upsertSkillCapabilityMemory(
@@ -72,124 +103,121 @@ export function upsertSkillCapabilityMemory(
72
103
  ): void {
73
104
  try {
74
105
  const db = getDb();
75
- const subject = `skill:${skillId}`;
76
106
  const statement = buildCapabilityStatement(input);
77
- const kind = "capability";
107
+ const content = `skill:${skillId}\n${statement}`;
78
108
  const scopeId = "default";
79
- const confidence = 1.0;
80
- const importance = 0.7;
81
- const fingerprint = computeMemoryFingerprint(
82
- scopeId,
83
- kind,
84
- subject,
85
- statement,
86
- );
87
109
  const now = Date.now();
88
110
 
89
111
  const existing = db
90
112
  .select()
91
- .from(memoryItems)
113
+ .from(memoryGraphNodes)
92
114
  .where(
93
115
  and(
94
- eq(memoryItems.kind, kind),
95
- eq(memoryItems.subject, subject),
96
- eq(memoryItems.scopeId, scopeId),
116
+ eq(memoryGraphNodes.type, "procedural"),
117
+ sql`${memoryGraphNodes.content} LIKE ${'skill:' + escapeLike(skillId) + '\n%'} ESCAPE '\\'`,
118
+ eq(memoryGraphNodes.scopeId, scopeId),
97
119
  ),
98
120
  )
99
121
  .get();
100
122
 
101
123
  if (existing) {
102
124
  if (
103
- existing.status === "active" &&
104
- existing.fingerprint === fingerprint
125
+ existing.content === content &&
126
+ existing.fidelity !== "gone"
105
127
  ) {
106
- // Same content — just touch lastSeenAt
107
- db.update(memoryItems)
108
- .set({ lastSeenAt: now })
109
- .where(eq(memoryItems.id, existing.id))
128
+ // Same content — just touch lastAccessed
129
+ db.update(memoryGraphNodes)
130
+ .set({ lastAccessed: now })
131
+ .where(eq(memoryGraphNodes.id, existing.id))
110
132
  .run();
111
133
  return;
112
134
  }
113
135
 
114
- if (existing.status === "active") {
115
- // Content changed — update statement and fingerprint
116
- db.update(memoryItems)
136
+ if (existing.fidelity !== "gone") {
137
+ // Content changed — update content
138
+ db.update(memoryGraphNodes)
117
139
  .set({
118
- statement,
119
- fingerprint,
120
- lastSeenAt: now,
140
+ content,
141
+ lastAccessed: now,
121
142
  })
122
- .where(eq(memoryItems.id, existing.id))
143
+ .where(eq(memoryGraphNodes.id, existing.id))
123
144
  .run();
124
- enqueueMemoryJob("embed_item", { itemId: existing.id });
145
+ enqueueMemoryJob("embed_graph_node", { nodeId: existing.id });
125
146
  return;
126
147
  }
127
148
 
128
- // status === "deleted" or other — reactivate
129
- db.update(memoryItems)
149
+ // fidelity === "gone" — reactivate
150
+ db.update(memoryGraphNodes)
130
151
  .set({
131
- status: "active",
132
- statement,
133
- fingerprint,
134
- lastSeenAt: now,
135
- firstSeenAt: now,
152
+ fidelity: "vivid",
153
+ content,
154
+ created: now,
155
+ lastAccessed: now,
136
156
  })
137
- .where(eq(memoryItems.id, existing.id))
157
+ .where(eq(memoryGraphNodes.id, existing.id))
138
158
  .run();
139
- enqueueMemoryJob("embed_item", { itemId: existing.id });
159
+ enqueueMemoryJob("embed_graph_node", { nodeId: existing.id });
160
+ log.info({ skillId, nodeId: existing.id }, "Reactivated skill capability memory");
140
161
  return;
141
162
  }
142
163
 
143
- // No existing — insert new row
164
+ // No existing — insert new graph node
144
165
  const id = uuid();
145
- db.insert(memoryItems)
166
+ db.insert(memoryGraphNodes)
146
167
  .values({
147
168
  id,
148
- kind,
149
- subject,
150
- statement,
151
- status: "active",
152
- confidence,
153
- importance,
154
- fingerprint,
155
- sourceType: "extraction",
169
+ content,
170
+ type: "procedural",
171
+ created: now,
172
+ lastAccessed: now,
173
+ lastConsolidated: now,
174
+ emotionalCharge: DEFAULT_EMOTIONAL_CHARGE,
175
+ fidelity: "vivid",
176
+ confidence: 1.0,
177
+ significance: 0.7,
178
+ stability: 14,
179
+ reinforcementCount: 0,
180
+ lastReinforced: now,
181
+ sourceConversations: JSON.stringify([]),
182
+ sourceType: "inferred",
183
+ narrativeRole: null,
184
+ partOfStory: null,
156
185
  scopeId,
157
- firstSeenAt: now,
158
- lastSeenAt: now,
159
186
  })
160
187
  .run();
161
- enqueueMemoryJob("embed_item", { itemId: id });
188
+ enqueueMemoryJob("embed_graph_node", { nodeId: id });
189
+ log.info({ skillId, nodeId: id }, "Created skill capability memory");
162
190
  } catch (err) {
163
191
  log.warn({ err, skillId }, "Failed to upsert skill capability memory");
164
192
  }
165
193
  }
166
194
 
167
195
  /**
168
- * Soft-delete the capability memory item for a skill.
196
+ * Soft-delete the capability memory graph node for a skill.
169
197
  * Best-effort: errors are logged but never thrown.
170
198
  */
171
199
  export function deleteSkillCapabilityMemory(skillId: string): void {
172
200
  try {
173
201
  const db = getDb();
174
- const subject = `skill:${skillId}`;
175
202
  const now = Date.now();
176
203
 
177
204
  const existing = db
178
205
  .select()
179
- .from(memoryItems)
206
+ .from(memoryGraphNodes)
180
207
  .where(
181
208
  and(
182
- eq(memoryItems.kind, "capability"),
183
- eq(memoryItems.subject, subject),
184
- eq(memoryItems.scopeId, "default"),
209
+ eq(memoryGraphNodes.type, "procedural"),
210
+ sql`${memoryGraphNodes.content} LIKE ${'skill:' + escapeLike(skillId) + '\n%'} ESCAPE '\\'`,
211
+ eq(memoryGraphNodes.scopeId, "default"),
212
+ sql`${memoryGraphNodes.fidelity} != 'gone'`,
185
213
  ),
186
214
  )
187
215
  .get();
188
216
 
189
- if (existing && existing.status !== "deleted") {
190
- db.update(memoryItems)
191
- .set({ status: "deleted", lastSeenAt: now })
192
- .where(eq(memoryItems.id, existing.id))
217
+ if (existing) {
218
+ db.update(memoryGraphNodes)
219
+ .set({ fidelity: "gone", lastAccessed: now })
220
+ .where(eq(memoryGraphNodes.id, existing.id))
193
221
  .run();
194
222
  }
195
223
  } catch (err) {
@@ -198,7 +226,7 @@ export function deleteSkillCapabilityMemory(skillId: string): void {
198
226
  }
199
227
 
200
228
  /**
201
- * Seed capability memory items for all enabled skills (bundled, managed, workspace, extra).
229
+ * Seed capability memory graph nodes for all enabled skills (bundled, managed, workspace, extra).
202
230
  * Prunes stale entries whose skills are no longer in the enabled set.
203
231
  * Best-effort: errors are logged but never thrown.
204
232
  */
@@ -231,31 +259,78 @@ export function seedCatalogSkillMemories(): void {
231
259
  }
232
260
 
233
261
  // Prune stale capability memories for skills no longer in the enabled set
262
+ // and not available in the remote/local catalog.
234
263
  const db = getDb();
235
264
  const allCapabilities = db
236
265
  .select()
237
- .from(memoryItems)
266
+ .from(memoryGraphNodes)
238
267
  .where(
239
268
  and(
240
- eq(memoryItems.kind, "capability"),
241
- eq(memoryItems.scopeId, "default"),
242
- eq(memoryItems.status, "active"),
269
+ eq(memoryGraphNodes.type, "procedural"),
270
+ eq(memoryGraphNodes.scopeId, "default"),
271
+ sql`${memoryGraphNodes.fidelity} != 'gone'`,
243
272
  ),
244
273
  )
245
274
  .all();
246
275
 
276
+ const allLocalSkillIds = new Set(catalog.map((s) => s.id));
277
+ const cachedCatalog = getCachedCatalogSync();
278
+ const cachedCatalogIds = new Set(cachedCatalog.map((s) => s.id));
279
+
247
280
  const now = Date.now();
248
281
  for (const item of allCapabilities) {
249
- if (!item.subject.startsWith("skill:")) continue;
250
- const itemSkillId = item.subject.replace("skill:", "");
251
- if (!catalogIds.has(itemSkillId)) {
252
- db.update(memoryItems)
253
- .set({ status: "deleted", lastSeenAt: now })
254
- .where(eq(memoryItems.id, item.id))
255
- .run();
256
- }
282
+ if (!item.content.startsWith("skill:")) continue;
283
+ const itemSkillId = item.content.split("\n")[0].replace("skill:", "");
284
+
285
+ // Keep enabled skills
286
+ if (catalogIds.has(itemSkillId)) continue;
287
+
288
+ // Keep uninstalled catalog skills that are still in the remote catalog
289
+ if (cachedCatalogIds.has(itemSkillId)) continue;
290
+
291
+ // If the catalog cache is empty (cold start, before async fetch),
292
+ // we can't tell whether an unknown skill is a stale entry or
293
+ // a valid uninstalled catalog skill. Only prune skills we can
294
+ // positively identify as local-but-disabled.
295
+ if (cachedCatalogIds.size === 0 && !allLocalSkillIds.has(itemSkillId)) continue;
296
+
297
+ log.info({ skillId: itemSkillId, nodeId: item.id, catalogSize: catalogIds.size, cacheSize: cachedCatalogIds.size }, "Pruning stale skill capability memory");
298
+ db.update(memoryGraphNodes)
299
+ .set({ fidelity: "gone", lastAccessed: now })
300
+ .where(eq(memoryGraphNodes.id, item.id))
301
+ .run();
257
302
  }
258
303
  } catch (err) {
259
304
  log.warn({ err }, "Failed to seed catalog skill memories");
260
305
  }
261
306
  }
307
+
308
+ /**
309
+ * Seed capability memories for catalog skills that are not yet installed.
310
+ * This makes uninstalled skills discoverable via memory injection so the LLM
311
+ * can auto-install them via skill_load when relevant.
312
+ * Best-effort: errors are logged but never thrown.
313
+ */
314
+ export async function seedUninstalledCatalogSkillMemories(): Promise<void> {
315
+ try {
316
+ const { getCatalog } = await import("./catalog-cache.js");
317
+ const fullCatalog = await getCatalog();
318
+ if (fullCatalog.length === 0) return;
319
+
320
+ const installedCatalog = loadSkillCatalog();
321
+ const installedIds = new Set(installedCatalog.map((s) => s.id));
322
+
323
+ const config = getConfig();
324
+ for (const entry of fullCatalog) {
325
+ if (installedIds.has(entry.id)) continue;
326
+
327
+ const flagKey = entry.metadata?.vellum?.["feature-flag"];
328
+ if (flagKey && !isAssistantFeatureFlagEnabled(flagKey, config)) continue;
329
+
330
+ const input = fromCatalogSkill(entry);
331
+ upsertSkillCapabilityMemory(entry.id, input);
332
+ }
333
+ } catch (err) {
334
+ log.warn({ err }, "Failed to seed uninstalled catalog skill memories");
335
+ }
336
+ }
@@ -5,6 +5,7 @@ import { dirname, join, resolve, sep } from "node:path";
5
5
 
6
6
  import { getWorkspaceSkillsDir } from "../util/platform.js";
7
7
  import { upsertSkillsIndex } from "./catalog-install.js";
8
+ import { computeSkillHash, writeInstallMeta } from "./install-meta.js";
8
9
 
9
10
  // ─── Types ───────────────────────────────────────────────────────────────────
10
11
 
@@ -434,9 +435,12 @@ export function validateSkillSlug(slug: string): void {
434
435
  * 1. Validates the skill slug for path safety
435
436
  * 2. Fetches all files from `skills/<skillSlug>/` in the source repo
436
437
  * 3. Writes them to `<workspace>/skills/<skillSlug>/` with path traversal protection
437
- * 4. Writes `version.json` with origin metadata
438
- * 5. Runs `bun install` if a `package.json` is present
439
- * 6. Registers the skill in SKILLS.md only after all steps succeed
438
+ * 4. Writes `install-meta.json` with origin metadata
439
+ * 5. Installs npm dependencies (if package.json exists)
440
+ * 6. Updates SKILLS.md index
441
+ *
442
+ * Auto-enable and memory seeding are handled by the caller (e.g.
443
+ * `postInstallSkill()` in the daemon, or left to the user for CLI installs).
440
444
  */
441
445
  export async function installExternalSkill(
442
446
  owner: string,
@@ -444,6 +448,7 @@ export async function installExternalSkill(
444
448
  skillSlug: string,
445
449
  overwrite: boolean,
446
450
  ref?: string,
451
+ contactId?: string,
447
452
  ): Promise<void> {
448
453
  // Validate slug before using in filesystem paths
449
454
  validateSkillSlug(skillSlug);
@@ -480,20 +485,19 @@ export async function installExternalSkill(
480
485
  writeFileSync(destPath, content, "utf-8");
481
486
  }
482
487
 
483
- // Write origin metadata
484
- const meta = {
485
- origin: "skills.sh",
486
- source: `${owner}/${repo}`,
487
- skillSlug,
488
+ // Write install metadata
489
+ writeInstallMeta(skillDir, {
490
+ origin: "skillssh",
491
+ slug: skillSlug,
492
+ sourceRepo: `${owner}/${repo}`,
488
493
  installedAt: new Date().toISOString(),
489
- };
490
- writeFileSync(
491
- join(skillDir, "version.json"),
492
- JSON.stringify(meta, null, 2) + "\n",
493
- "utf-8",
494
- );
494
+ ...(contactId ? { installedBy: contactId } : {}),
495
+ contentHash: computeSkillHash(skillDir) ?? undefined,
496
+ });
495
497
 
496
- // Install npm dependencies if the skill ships a package.json
498
+ // Post-install: install dependencies first, then index the skill.
499
+ // Running bun install before upsertSkillsIndex ensures we don't index a
500
+ // skill whose dependencies failed to install.
497
501
  if (existsSync(join(skillDir, "package.json"))) {
498
502
  const bunPath = `${homedir()}/.bun/bin`;
499
503
  execSync("bun install", {
@@ -502,7 +506,5 @@ export async function installExternalSkill(
502
506
  env: { ...process.env, PATH: `${bunPath}:${process.env.PATH}` },
503
507
  });
504
508
  }
505
-
506
- // Register in SKILLS.md only after files are written and deps installed
507
509
  upsertSkillsIndex(skillSlug);
508
510
  }
@@ -64,8 +64,10 @@ export async function runTask(
64
64
  // Scheduled section; non-schedule tasks use "background" to stay out of
65
65
  // the main conversation list.
66
66
  conversationType: opts.source === "schedule" ? undefined : "background",
67
- source: opts.source,
67
+ source: opts.source === "schedule" ? "schedule" : "task",
68
68
  scheduleJobId: opts.scheduleJobId,
69
+ groupId:
70
+ opts.source === "schedule" ? "system:scheduled" : "system:background",
69
71
  origin: "task",
70
72
  systemHint: `Task: ${task.title}`,
71
73
  });
@@ -504,17 +504,15 @@ describe("UsageTelemetryReporter", () => {
504
504
  // No HTTP call should have been made
505
505
  expect(mockFetch).not.toHaveBeenCalled();
506
506
 
507
- // All 6 watermarks should have been advanced (3 timestamps + 3 IDs)
508
- expect(mockSetMemoryCheckpoint).toHaveBeenCalledTimes(6);
507
+ // All 3 timestamp watermarks should have been advanced (IDs left untouched
508
+ // so the compound-cursor branch stays active)
509
+ expect(mockSetMemoryCheckpoint).toHaveBeenCalledTimes(3);
509
510
 
510
511
  const calls = mockSetMemoryCheckpoint.mock.calls;
511
512
  const keys = calls.map((c) => c[0]);
512
513
  expect(keys).toContain("telemetry:usage:last_reported_at");
513
- expect(keys).toContain("telemetry:usage:last_reported_id");
514
514
  expect(keys).toContain("telemetry:turns:last_reported_at");
515
- expect(keys).toContain("telemetry:turns:last_reported_id");
516
515
  expect(keys).toContain("telemetry:lifecycle:last_reported_at");
517
- expect(keys).toContain("telemetry:lifecycle:last_reported_id");
518
516
  });
519
517
 
520
518
  test("events sent normally after re-enabling collectUsageData", async () => {
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
 
4
+ import { ensureBun } from "../../util/bun-runtime.js";
4
5
  import { getExternalDir } from "../../util/platform.js";
5
6
 
6
7
  export interface BrowserRuntimeStatus {
@@ -83,7 +84,8 @@ export async function importPlaywright(): Promise<typeof import("playwright")> {
83
84
  if (!existsSync(join(externalDir, "package.json"))) {
84
85
  writeFileSync(join(externalDir, "package.json"), '{"private":true}\n');
85
86
  }
86
- const proc = Bun.spawn(["bun", "add", "playwright"], {
87
+ const bunPath = await ensureBun();
88
+ const proc = Bun.spawn([bunPath, "add", "playwright"], {
87
89
  cwd: externalDir,
88
90
  stdout: "pipe",
89
91
  stderr: "pipe",
@@ -1,67 +1,56 @@
1
1
  import { getConfig } from "../../config/loader.js";
2
+ import {
3
+ handleRecall,
4
+ handleRemember,
5
+ type RecallInput,
6
+ type RememberInput,
7
+ } from "../../memory/graph/tool-handlers.js";
8
+ import {
9
+ graphRecallDefinition,
10
+ graphRememberDefinition,
11
+ } from "../../memory/graph/tools.js";
2
12
  import { RiskLevel } from "../../permissions/types.js";
3
13
  import type { ToolDefinition } from "../../providers/types.js";
4
14
  import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
5
- import {
6
- memoryManageDefinition,
7
- memoryRecallDefinition,
8
- } from "./definitions.js";
9
- import {
10
- handleMemoryDelete,
11
- handleMemoryRecall,
12
- handleMemorySave,
13
- handleMemoryUpdate,
14
- } from "./handlers.js";
15
15
 
16
- // ── memory_manage ────────────────────────────────────────────────────
16
+ // ── remember ────────────────────────────────────────────────────────
17
17
 
18
- class MemoryManageTool implements Tool {
19
- name = "memory_manage";
20
- description = memoryManageDefinition.description;
18
+ class RememberTool implements Tool {
19
+ name = "remember";
20
+ description = graphRememberDefinition.description;
21
21
  category = "memory";
22
22
  defaultRiskLevel = RiskLevel.Low;
23
23
 
24
24
  getDefinition(): ToolDefinition {
25
- return memoryManageDefinition;
25
+ return graphRememberDefinition;
26
26
  }
27
27
 
28
28
  async execute(
29
29
  input: Record<string, unknown>,
30
30
  context: ToolContext,
31
31
  ): Promise<ToolExecutionResult> {
32
- const config = getConfig();
33
- switch (input.op) {
34
- case "save":
35
- return handleMemorySave(
36
- input,
37
- config,
38
- context.conversationId,
39
- context.requestId,
40
- context.memoryScopeId,
41
- );
42
- case "update":
43
- return handleMemoryUpdate(input, config, context.memoryScopeId);
44
- case "delete":
45
- return handleMemoryDelete(input, config, context.memoryScopeId);
46
- default:
47
- return {
48
- content: `Error: unknown op "${input.op}". Must be one of: save, update, delete`,
49
- isError: true,
50
- };
51
- }
32
+ const result = handleRemember(
33
+ input as unknown as RememberInput,
34
+ context.conversationId,
35
+ context.memoryScopeId ?? "default",
36
+ );
37
+ return {
38
+ content: result.message,
39
+ isError: !result.success,
40
+ };
52
41
  }
53
42
  }
54
43
 
55
- // ── memory_recall ────────────────────────────────────────────────────
44
+ // ── recall ──────────────────────────────────────────────────────────
56
45
 
57
- class MemoryRecallTool implements Tool {
58
- name = "memory_recall";
59
- description = memoryRecallDefinition.description;
46
+ class RecallTool implements Tool {
47
+ name = "recall";
48
+ description = graphRecallDefinition.description;
60
49
  category = "memory";
61
50
  defaultRiskLevel = RiskLevel.Low;
62
51
 
63
52
  getDefinition(): ToolDefinition {
64
- return memoryRecallDefinition;
53
+ return graphRecallDefinition;
65
54
  }
66
55
 
67
56
  async execute(
@@ -69,16 +58,44 @@ class MemoryRecallTool implements Tool {
69
58
  context: ToolContext,
70
59
  ): Promise<ToolExecutionResult> {
71
60
  const config = getConfig();
72
- return handleMemoryRecall(
73
- input,
61
+ const result = await handleRecall(
62
+ input as unknown as RecallInput,
74
63
  config,
75
- context.memoryScopeId,
76
- context.conversationId,
64
+ context.memoryScopeId ?? "default",
77
65
  );
66
+
67
+ if (result.results.length === 0) {
68
+ return { content: "No results found.", isError: false };
69
+ }
70
+
71
+ const formatted = result.results
72
+ .map((r) => {
73
+ const ts = formatTimestamp(r.created);
74
+ const meta =
75
+ result.mode === "memory"
76
+ ? `[${r.type}] ${ts} (confidence: ${r.confidence.toFixed(2)}, score: ${r.score.toFixed(3)})`
77
+ : `[archive] ${ts}`;
78
+ return `${meta}\n${r.content}`;
79
+ })
80
+ .join("\n\n---\n\n");
81
+
82
+ return { content: formatted, isError: false };
78
83
  }
79
84
  }
80
85
 
86
+ // ── Helpers ─────────────────────────────────────────────────────────
87
+
88
+ function formatTimestamp(epochMs: number): string {
89
+ const d = new Date(epochMs);
90
+ const mm = String(d.getMonth() + 1).padStart(2, "0");
91
+ const dd = String(d.getDate()).padStart(2, "0");
92
+ const yy = String(d.getFullYear()).slice(-2);
93
+ const hh = String(d.getHours()).padStart(2, "0");
94
+ const min = String(d.getMinutes()).padStart(2, "0");
95
+ return `${mm}/${dd}/${yy} ${hh}:${min}`;
96
+ }
97
+
81
98
  // ── Exported tool instances ──────────────────────────────────────────
82
99
 
83
- export const memoryManageTool = new MemoryManageTool();
84
- export const memoryRecallTool = new MemoryRecallTool();
100
+ export const rememberTool = new RememberTool();
101
+ export const recallTool = new RecallTool();
@@ -8,6 +8,7 @@ import {
8
8
  } from "../permissions/checker.js";
9
9
  import type { PermissionPrompter } from "../permissions/prompter.js";
10
10
  import { addRule } from "../permissions/trust-store.js";
11
+ import { RiskLevel } from "../permissions/types.js";
11
12
  import {
12
13
  getEffectiveMode,
13
14
  setConversationMode,
@@ -145,6 +146,10 @@ export class PermissionChecker {
145
146
  // Exception: inline-command skill loads (skill_load_dynamic:*) must
146
147
  // never be silently auto-approved — they execute embedded commands
147
148
  // and require explicit human review or a pinned trust rule.
149
+ // Exception: high-risk tools (e.g. destructive shell commands, writes
150
+ // to sensitive paths) are denied — unattended sessions must not
151
+ // auto-approve operations that could cause significant damage if
152
+ // triggered by prompt injection from untrusted content.
148
153
  const isDynamicSkillLoad =
149
154
  result.matchedRule?.pattern.startsWith("skill_load_dynamic:") ===
150
155
  true;
@@ -152,7 +157,8 @@ export class PermissionChecker {
152
157
  context.isInteractive === false &&
153
158
  context.trustClass === "guardian" &&
154
159
  !context.requireFreshApproval &&
155
- !isDynamicSkillLoad
160
+ !isDynamicSkillLoad &&
161
+ riskLevel !== RiskLevel.High
156
162
  ) {
157
163
  log.info(
158
164
  { toolName: name, riskLevel },