@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
@@ -0,0 +1,439 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Memory Graph — Context assembly and injection tracking
3
+ // ---------------------------------------------------------------------------
4
+
5
+ import { optimizeImageForTransport } from "../../agent/image-optimize.js";
6
+ import { getLogger } from "../../util/logger.js";
7
+ import { loadImageRefData } from "./image-ref-utils.js";
8
+ import type { MemoryNode, ScoredNode } from "./types.js";
9
+ import { isCapabilityNode } from "./types.js";
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Image injection budgets
13
+ // ---------------------------------------------------------------------------
14
+
15
+ export const MAX_CONTEXT_LOAD_IMAGES = 3;
16
+ export const MAX_PER_TURN_IMAGES = 1;
17
+ export const MAX_REFRESH_IMAGES = 2;
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Types
21
+ // ---------------------------------------------------------------------------
22
+
23
+ export interface ResolvedImage {
24
+ base64Data: string;
25
+ mediaType: string;
26
+ description: string;
27
+ }
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // InContextTracker — tracks which node IDs are visible to the LLM
31
+ // ---------------------------------------------------------------------------
32
+
33
+ interface InjectionLogEntry {
34
+ nodeId: string;
35
+ turn: number;
36
+ }
37
+
38
+ /**
39
+ * Tracks which memory graph nodes are currently in the LLM's context.
40
+ * Handles:
41
+ * - Deduplication: never re-inject a node already visible
42
+ * - Compaction eviction: when context compaction removes turns,
43
+ * evict those nodes so they can be re-injected if relevant later
44
+ */
45
+ export class InContextTracker {
46
+ private inContext = new Set<string>();
47
+ private log: InjectionLogEntry[] = [];
48
+ private currentTurn = 0;
49
+
50
+ /** Mark nodes as loaded into context. */
51
+ add(nodeIds: string[]): void {
52
+ for (const id of nodeIds) {
53
+ this.inContext.add(id);
54
+ this.log.push({ nodeId: id, turn: this.currentTurn });
55
+ }
56
+ }
57
+
58
+ /** Check if a node is already in context. */
59
+ isInContext(nodeId: string): boolean {
60
+ return this.inContext.has(nodeId);
61
+ }
62
+
63
+ /** Filter candidates to only those not already in context. */
64
+ filterNew(candidates: ScoredNode[]): ScoredNode[] {
65
+ return candidates.filter((c) => !this.inContext.has(c.node.id));
66
+ }
67
+
68
+ /** Advance the turn counter. Called before each retrieval step. */
69
+ advanceTurn(): void {
70
+ this.currentTurn++;
71
+ }
72
+
73
+ /**
74
+ * Evict nodes that were injected in compacted turns.
75
+ * Called when context compaction removes message history.
76
+ */
77
+ evictCompactedTurns(upToTurn: number): void {
78
+ const evicted: string[] = [];
79
+ this.log = this.log.filter((entry) => {
80
+ if (entry.turn <= upToTurn) {
81
+ evicted.push(entry.nodeId);
82
+ return false;
83
+ }
84
+ return true;
85
+ });
86
+
87
+ // Only evict if the node isn't also loaded in a later turn
88
+ const stillPresent = new Set(this.log.map((e) => e.nodeId));
89
+ for (const id of evicted) {
90
+ if (!stillPresent.has(id)) {
91
+ this.inContext.delete(id);
92
+ }
93
+ }
94
+ }
95
+
96
+ /** Get all node IDs currently in context. Useful for extraction. */
97
+ getActiveNodeIds(): string[] {
98
+ return [...this.inContext];
99
+ }
100
+
101
+ /** Get the injection log. Useful for debugging. */
102
+ getLog(): InjectionLogEntry[] {
103
+ return [...this.log];
104
+ }
105
+
106
+ /** Current turn number. */
107
+ getTurn(): number {
108
+ return this.currentTurn;
109
+ }
110
+ }
111
+
112
+ // ---------------------------------------------------------------------------
113
+ // Context assembly — programmatic, not LLM
114
+ //
115
+ // Each node's full prose lives in node.content. The context block gets a
116
+ // compressed version: 1-2 sentences + light metadata (type, age).
117
+ // Full detail available via the recall tool.
118
+ // ---------------------------------------------------------------------------
119
+
120
+ // No assembly options needed — the retriever's node count (30-40) is the only limit.
121
+ // The context block includes full node content.
122
+
123
+ /** Format relative time from epoch ms. */
124
+ function relativeAge(createdMs: number): string {
125
+ const diffMs = Date.now() - createdMs;
126
+ const mins = Math.floor(diffMs / 60_000);
127
+ if (mins < 60) return `${mins}m ago`;
128
+ const hours = Math.floor(mins / 60);
129
+ if (hours < 24) return `${hours}h ago`;
130
+ const days = Math.floor(hours / 24);
131
+ if (days < 90) return `${days}d ago`;
132
+ const months = Math.floor(days / 30);
133
+ return `${months}mo ago`;
134
+ }
135
+
136
+ /**
137
+ * Format an event date as a human-readable string with relative countdown.
138
+ * Examples:
139
+ * "Tue Apr 8, 6:00 PM (in 3d)"
140
+ * "Thu Apr 10 (in 5d)"
141
+ * "Mon Apr 7, 9:00 AM (tomorrow)"
142
+ * "Wed Apr 1 (today)"
143
+ */
144
+ export function formatEventDate(epochMs: number): string {
145
+ const date = new Date(epochMs);
146
+ const now = new Date();
147
+
148
+ // Day names and month names
149
+ const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
150
+ const monthNames = [
151
+ "Jan",
152
+ "Feb",
153
+ "Mar",
154
+ "Apr",
155
+ "May",
156
+ "Jun",
157
+ "Jul",
158
+ "Aug",
159
+ "Sep",
160
+ "Oct",
161
+ "Nov",
162
+ "Dec",
163
+ ];
164
+
165
+ const dayName = dayNames[date.getDay()];
166
+ const monthName = monthNames[date.getMonth()];
167
+ const dayOfMonth = date.getDate();
168
+
169
+ // Include time only when hours/minutes are non-zero (midnight = date-only event)
170
+ // Use UTC methods: date-only events are stored as midnight UTC, so local timezone
171
+ // methods would show non-zero hours in west-of-UTC timezones, defeating the check.
172
+ const hasTime = date.getUTCHours() !== 0 || date.getUTCMinutes() !== 0;
173
+ let datePart = `${dayName} ${monthName} ${dayOfMonth}`;
174
+ if (hasTime) {
175
+ const hours = date.getHours();
176
+ const minutes = date.getMinutes();
177
+ const ampm = hours >= 12 ? "PM" : "AM";
178
+ const displayHour = hours % 12 || 12;
179
+ const timePart =
180
+ minutes === 0
181
+ ? `${displayHour}:00 ${ampm}`
182
+ : `${displayHour}:${String(minutes).padStart(2, "0")} ${ampm}`;
183
+ datePart += `, ${timePart}`;
184
+ }
185
+
186
+ // Calculate relative countdown using calendar days
187
+ const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
188
+ const eventStart = new Date(
189
+ date.getFullYear(),
190
+ date.getMonth(),
191
+ date.getDate(),
192
+ );
193
+ const diffDays = Math.round(
194
+ (eventStart.getTime() - todayStart.getTime()) / (1000 * 60 * 60 * 24),
195
+ );
196
+
197
+ let relative: string;
198
+ if (diffDays === 0) {
199
+ relative = "today";
200
+ } else if (diffDays === -1) {
201
+ // For date-only inputs (no time component), -1 day can be UTC midnight drift — treat as "today".
202
+ // For timed events, -1 day is genuinely yesterday.
203
+ relative = hasTime ? "yesterday" : "today";
204
+ } else if (diffDays < -1) {
205
+ // Past dates
206
+ const absDays = -diffDays;
207
+ if (absDays < 14) {
208
+ relative = `${absDays}d ago`;
209
+ } else if (absDays < 60) {
210
+ relative = `${Math.floor(absDays / 7)}w ago`;
211
+ } else {
212
+ relative = `${Math.floor(absDays / 30)}mo ago`;
213
+ }
214
+ } else if (diffDays === 1) {
215
+ relative = "tomorrow";
216
+ } else if (diffDays < 14) {
217
+ relative = `in ${diffDays}d`;
218
+ } else if (diffDays < 60) {
219
+ relative = `in ${Math.floor(diffDays / 7)}w`;
220
+ } else {
221
+ relative = `in ${Math.floor(diffDays / 30)}mo`;
222
+ }
223
+
224
+ return `${datePart} (${relative})`;
225
+ }
226
+
227
+ /** Format an upcoming entry — uses event date when available, falls back to standard format. */
228
+ function formatUpcomingEntry(scored: ScoredNode): string {
229
+ const node = scored.node;
230
+ if (node.eventDate != null) {
231
+ return `- ${formatEventDate(node.eventDate)} — ${node.content}`;
232
+ }
233
+ return formatNodeEntry(scored);
234
+ }
235
+
236
+ /** Format a single node for the context block. */
237
+ function formatNodeEntry(scored: ScoredNode): string {
238
+ const node = scored.node;
239
+ const age = relativeAge(node.created);
240
+ let entry = `- (${age}) ${node.content}`;
241
+ if (node.imageRefs && node.imageRefs.length > 0) {
242
+ const desc = node.imageRefs[0]!.description;
243
+ entry += ` [image: ${desc}]`;
244
+ }
245
+ return entry;
246
+ }
247
+
248
+ /**
249
+ * Assemble a context block from scored memory nodes.
250
+ *
251
+ * Structure:
252
+ * - Right Now: present-tense state (most recent emotional + very recent episodic)
253
+ * - Active Threads: prospective nodes (commitments, tasks, plans)
254
+ * - What Today Means: date-triggered nodes (anniversaries, milestones)
255
+ * - On My Mind: everything else, ordered by score — no sub-categories
256
+ * - Serendipity: the random mid-tier wildcard(s)
257
+ */
258
+ export function assembleContextBlock(
259
+ nodes: ScoredNode[],
260
+ options?: { serendipityNodes?: ScoredNode[] },
261
+ ): string {
262
+ // Partition nodes into sections
263
+ const rightNow: ScoredNode[] = [];
264
+ const threads: ScoredNode[] = [];
265
+ const triggered: ScoredNode[] = [];
266
+ const upcoming: ScoredNode[] = [];
267
+ const capabilities: ScoredNode[] = [];
268
+ const onMyMind: ScoredNode[] = [];
269
+
270
+ for (const scored of nodes) {
271
+ const node = scored.node;
272
+
273
+ if (scored.scoreBreakdown.triggerBoost > 0) {
274
+ triggered.push(scored);
275
+ } else if (node.eventDate != null && node.eventDate > Date.now()) {
276
+ // Future-dated events without an active trigger go to Upcoming
277
+ upcoming.push(scored);
278
+ } else if (node.type === "prospective") {
279
+ threads.push(scored);
280
+ } else if (isCapabilityNode(node)) {
281
+ capabilities.push(scored);
282
+ } else if (node.type === "emotional" && isRecent(node)) {
283
+ // Recent emotional nodes go in "Right Now" — present-tense state
284
+ rightNow.push(scored);
285
+ } else if (isVeryRecent(node)) {
286
+ // Very recent nodes (last few hours) are "right now" context
287
+ rightNow.push(scored);
288
+ } else {
289
+ onMyMind.push(scored);
290
+ }
291
+ }
292
+
293
+ // Sort upcoming by eventDate ascending (soonest first)
294
+ upcoming.sort((a, b) => (a.node.eventDate ?? 0) - (b.node.eventDate ?? 0));
295
+
296
+ const parts: string[] = [];
297
+
298
+ // --- Right Now ---
299
+ if (rightNow.length > 0) {
300
+ const entries = buildSection(rightNow, 3);
301
+ if (entries.length > 0) {
302
+ parts.push(`### Right Now\n${entries.join("\n")}`);
303
+ }
304
+ }
305
+
306
+ // --- Active Threads ---
307
+ if (threads.length > 0) {
308
+ const entries = buildSection(threads, 5);
309
+ if (entries.length > 0) {
310
+ parts.push(`### Active Threads\n${entries.join("\n")}`);
311
+ }
312
+ }
313
+
314
+ // --- Skills You Can Use ---
315
+ if (capabilities.length > 0) {
316
+ const entries = capabilities.slice(0, 5).map((scored) => {
317
+ const content = scored.node.content.replace(/^(?:skill|cli):\S+\n/, "");
318
+ return `- ${content} → use skill_load to activate`;
319
+ });
320
+ parts.push(`### Skills You Can Use\n${entries.join("\n")}`);
321
+ }
322
+
323
+ // --- Upcoming ---
324
+ if (upcoming.length > 0) {
325
+ const entries = upcoming.slice(0, 5).map(formatUpcomingEntry);
326
+ if (entries.length > 0) {
327
+ parts.push(`### Upcoming\n${entries.join("\n")}`);
328
+ }
329
+ }
330
+
331
+ // --- What Today Means ---
332
+ if (triggered.length > 0) {
333
+ const entries = buildSection(triggered, 3);
334
+ if (entries.length > 0) {
335
+ parts.push(`### What Today Means\n${entries.join("\n")}`);
336
+ }
337
+ }
338
+
339
+ // --- On My Mind ---
340
+ if (onMyMind.length > 0) {
341
+ const entries = buildSection(onMyMind, onMyMind.length);
342
+ if (entries.length > 0) {
343
+ parts.push(`### On My Mind\n${entries.join("\n")}`);
344
+ }
345
+ }
346
+
347
+ // --- Serendipity ---
348
+ const serendipity = options?.serendipityNodes ?? [];
349
+ if (serendipity.length > 0) {
350
+ const entries = buildSection(serendipity, 2);
351
+ if (entries.length > 0) {
352
+ parts.push(`### Serendipity\n${entries.join("\n")}`);
353
+ }
354
+ }
355
+
356
+ if (parts.length === 0) return "";
357
+ return `## What I Remember Right Now\n\n${parts.join("\n\n")}`;
358
+ }
359
+
360
+ function buildSection(nodes: ScoredNode[], maxItems: number): string[] {
361
+ return nodes.slice(0, maxItems).map(formatNodeEntry);
362
+ }
363
+
364
+ /**
365
+ * Assemble an injection block for mid-conversation memory flashes.
366
+ * Uses the same per-node format as context-load (age + full content).
367
+ */
368
+ export function assembleInjectionBlock(nodes: ScoredNode[]): string {
369
+ if (nodes.length === 0) return "";
370
+ return nodes
371
+ .map((scored) => {
372
+ if (isCapabilityNode(scored.node)) {
373
+ const content = scored.node.content.replace(/^(?:skill|cli):\S+\n/, "");
374
+ return `- [skill] ${content} → use skill_load to activate`;
375
+ }
376
+ if (scored.node.eventDate != null) {
377
+ return formatUpcomingEntry(scored);
378
+ }
379
+ return formatNodeEntry(scored);
380
+ })
381
+ .join("\n");
382
+ }
383
+
384
+ // ---------------------------------------------------------------------------
385
+ // Image resolution
386
+ // ---------------------------------------------------------------------------
387
+
388
+ /**
389
+ * Load and optimize images for memory injection.
390
+ * Iterates scored nodes (already sorted by score), resolves the first
391
+ * image ref for each image-bearing node, and returns up to maxImages.
392
+ */
393
+ export async function resolveInjectionImages(
394
+ nodes: ScoredNode[],
395
+ maxImages: number,
396
+ ): Promise<Map<string, ResolvedImage>> {
397
+ const log = getLogger("memory-graph");
398
+ const result = new Map<string, ResolvedImage>();
399
+ for (const scored of nodes) {
400
+ if (result.size >= maxImages) break;
401
+ const refs = scored.node.imageRefs;
402
+ if (!refs || refs.length === 0) continue;
403
+
404
+ try {
405
+ const data = await loadImageRefData(refs[0]!);
406
+ if (!data) continue;
407
+
408
+ const optimized = optimizeImageForTransport(
409
+ data.data.toString("base64"),
410
+ data.mimeType,
411
+ );
412
+
413
+ result.set(scored.node.id, {
414
+ base64Data: optimized.data,
415
+ mediaType: optimized.mediaType,
416
+ description: refs[0]!.description,
417
+ });
418
+ } catch (err) {
419
+ log.warn(
420
+ `Skipping image for node ${scored.node.id}: ${err instanceof Error ? err.message : String(err)}`,
421
+ );
422
+ }
423
+ }
424
+ return result;
425
+ }
426
+
427
+ // ---------------------------------------------------------------------------
428
+ // Helpers
429
+ // ---------------------------------------------------------------------------
430
+
431
+ function isRecent(node: MemoryNode): boolean {
432
+ const dayMs = 1000 * 60 * 60 * 24;
433
+ return Date.now() - node.created < 2 * dayMs;
434
+ }
435
+
436
+ function isVeryRecent(node: MemoryNode): boolean {
437
+ const hourMs = 1000 * 60 * 60;
438
+ return Date.now() - node.created < 4 * hourMs;
439
+ }