@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
@@ -0,0 +1,36 @@
1
+ import type {
2
+ ToolContext,
3
+ ToolExecutionResult,
4
+ } from "../../../../tools/types.js";
5
+ import * as calendar from "../calendar-client.js";
6
+ import { getCalendarConnection, ok } from "./shared.js";
7
+
8
+ export async function run(
9
+ input: Record<string, unknown>,
10
+ _context: ToolContext,
11
+ ): Promise<ToolExecutionResult> {
12
+ const account = input.account as string | undefined;
13
+ const eventId = input.event_id as string;
14
+ const response = input.response as "accepted" | "declined" | "tentative";
15
+
16
+ const connection = await getCalendarConnection(account);
17
+
18
+ // Fetch the event to get context for the response message
19
+ const event = await calendar.getEvent(connection, eventId);
20
+
21
+ // If the user is the organizer, no RSVP is needed
22
+ if (event.responseStatus?.response === "organizer") {
23
+ return ok("You are the organizer of this event. No RSVP needed.");
24
+ }
25
+
26
+ // Send RSVP via the dedicated Microsoft Graph endpoint, notifying the organizer
27
+ await calendar.rsvpEvent(connection, eventId, response, true);
28
+
29
+ const responseLabel =
30
+ response === "accepted"
31
+ ? "Accepted"
32
+ : response === "declined"
33
+ ? "Declined"
34
+ : "Tentatively accepted";
35
+ return ok(`${responseLabel} the event "${event.subject ?? eventId}".`);
36
+ }
@@ -0,0 +1,17 @@
1
+ import type { OAuthConnection } from "../../../../oauth/connection.js";
2
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
3
+ import type { ToolExecutionResult } from "../../../../tools/types.js";
4
+
5
+ export function ok(content: string): ToolExecutionResult {
6
+ return { content, isError: false };
7
+ }
8
+
9
+ export function err(message: string): ToolExecutionResult {
10
+ return { content: message, isError: true };
11
+ }
12
+
13
+ export async function getCalendarConnection(
14
+ account?: string,
15
+ ): Promise<OAuthConnection> {
16
+ return resolveOAuthConnection("outlook", { account });
17
+ }
@@ -0,0 +1,120 @@
1
+ /** Microsoft Graph date+time pair. timeZone may be omitted when dateTime carries an offset. */
2
+ export interface OutlookDateTimeZone {
3
+ dateTime: string;
4
+ timeZone?: string;
5
+ }
6
+
7
+ /** Attendee on a calendar event. */
8
+ export interface OutlookCalendarAttendee {
9
+ emailAddress: { address: string; name?: string };
10
+ type: "required" | "optional" | "resource";
11
+ status?: {
12
+ response:
13
+ | "none"
14
+ | "organizer"
15
+ | "tentativelyAccepted"
16
+ | "accepted"
17
+ | "declined"
18
+ | "notResponded";
19
+ time?: string;
20
+ };
21
+ }
22
+
23
+ /** Physical or virtual location for a calendar event. */
24
+ export interface OutlookLocation {
25
+ displayName?: string;
26
+ locationType?: string;
27
+ address?: Record<string, unknown>;
28
+ coordinates?: Record<string, unknown>;
29
+ }
30
+
31
+ /** Rich-text body of a calendar item. */
32
+ export interface OutlookItemBody {
33
+ contentType: "text" | "html";
34
+ content: string;
35
+ }
36
+
37
+ /** A single calendar event from Microsoft Graph. */
38
+ export interface OutlookCalendarEvent {
39
+ id: string;
40
+ subject?: string;
41
+ bodyPreview?: string;
42
+ body?: OutlookItemBody;
43
+ start?: OutlookDateTimeZone;
44
+ end?: OutlookDateTimeZone;
45
+ location?: OutlookLocation;
46
+ locations?: OutlookLocation[];
47
+ attendees?: OutlookCalendarAttendee[];
48
+ organizer?: { emailAddress: { address: string; name?: string } };
49
+ isAllDay?: boolean;
50
+ isCancelled?: boolean;
51
+ showAs?:
52
+ | "free"
53
+ | "tentative"
54
+ | "busy"
55
+ | "oof"
56
+ | "workingElsewhere"
57
+ | "unknown";
58
+ importance?: "low" | "normal" | "high";
59
+ sensitivity?: "normal" | "personal" | "private" | "confidential";
60
+ webLink?: string;
61
+ onlineMeetingUrl?: string;
62
+ createdDateTime?: string;
63
+ lastModifiedDateTime?: string;
64
+ seriesMasterId?: string;
65
+ type?: "singleInstance" | "occurrence" | "exception" | "seriesMaster";
66
+ categories?: string[];
67
+ responseStatus?: { response: string; time?: string };
68
+ }
69
+
70
+ /** Paginated list of calendar events. */
71
+ export interface OutlookCalendarEventListResponse {
72
+ value?: OutlookCalendarEvent[];
73
+ "@odata.nextLink"?: string;
74
+ "@odata.deltaLink"?: string;
75
+ "@odata.count"?: number;
76
+ }
77
+
78
+ /** A single schedule item (free/busy block). */
79
+ export interface OutlookScheduleItem {
80
+ status:
81
+ | "free"
82
+ | "tentative"
83
+ | "busy"
84
+ | "oof"
85
+ | "workingElsewhere"
86
+ | "unknown";
87
+ start: OutlookDateTimeZone;
88
+ end: OutlookDateTimeZone;
89
+ subject?: string;
90
+ location?: string;
91
+ }
92
+
93
+ /** Schedule information for one user. */
94
+ export interface OutlookScheduleInformation {
95
+ scheduleId: string;
96
+ availabilityView: string;
97
+ scheduleItems: OutlookScheduleItem[];
98
+ error?: Record<string, unknown>;
99
+ }
100
+
101
+ /** Response from the getSchedule endpoint. */
102
+ export interface OutlookScheduleResponse {
103
+ value?: OutlookScheduleInformation[];
104
+ }
105
+
106
+ /** A calendar in the user's mailbox. */
107
+ export interface OutlookCalendar {
108
+ id: string;
109
+ name?: string;
110
+ color?: string;
111
+ isDefaultCalendar?: boolean;
112
+ canEdit?: boolean;
113
+ owner?: { name: string; address: string };
114
+ }
115
+
116
+ /** Paginated list of calendars. */
117
+ export interface OutlookCalendarListResponse {
118
+ value?: OutlookCalendar[];
119
+ "@odata.nextLink"?: string;
120
+ }
@@ -1,10 +1,10 @@
1
- import { and, eq } from "drizzle-orm";
2
- import { v4 as uuid } from "uuid";
1
+ import { sql } from "drizzle-orm";
3
2
 
4
3
  import { getDb } from "../../../../memory/db.js";
5
- import { computeMemoryFingerprint } from "../../../../memory/fingerprint.js";
4
+ import { createNode, updateNode } from "../../../../memory/graph/store.js";
5
+ import type { NewNode } from "../../../../memory/graph/types.js";
6
6
  import { enqueueMemoryJob } from "../../../../memory/jobs-store.js";
7
- import { memoryItems } from "../../../../memory/schema.js";
7
+ import { memoryGraphNodes } from "../../../../memory/schema.js";
8
8
  import type {
9
9
  Playbook,
10
10
  PlaybookAutonomyLevel,
@@ -13,7 +13,6 @@ import type {
13
13
  ToolContext,
14
14
  ToolExecutionResult,
15
15
  } from "../../../../tools/types.js";
16
- import { truncate } from "../../../../util/truncate.js";
17
16
 
18
17
  const VALID_AUTONOMY_LEVELS = new Set<string>(["auto", "draft", "notify"]);
19
18
 
@@ -56,27 +55,23 @@ export async function executePlaybookCreate(
56
55
  priority,
57
56
  };
58
57
  const statement = JSON.stringify(playbook);
59
- const subject = truncate(`Playbook: ${trigger}`, 80, "");
58
+ const sanitizedTrigger = trigger.replace(/[\r\n]+/g, " ");
59
+ const subject = `Playbook: ${sanitizedTrigger}`.slice(0, 80);
60
+ const content = `${subject}\n${statement}`;
60
61
  const scopeId = context.memoryScopeId ?? "default";
61
62
 
62
- const fingerprint = computeMemoryFingerprint(
63
- scopeId,
64
- "playbook",
65
- subject,
66
- statement,
67
- );
68
-
69
63
  try {
70
64
  const db = getDb();
71
65
 
66
+ // Check for duplicate by matching content in playbook-prefixed graph nodes
72
67
  const existing = db
73
- .select()
74
- .from(memoryItems)
68
+ .select({ id: memoryGraphNodes.id })
69
+ .from(memoryGraphNodes)
75
70
  .where(
76
- and(
77
- eq(memoryItems.fingerprint, fingerprint),
78
- eq(memoryItems.scopeId, scopeId),
79
- ),
71
+ sql`${memoryGraphNodes.sourceConversations} LIKE '%playbook:%'
72
+ AND ${memoryGraphNodes.content} = ${content}
73
+ AND ${memoryGraphNodes.scopeId} = ${scopeId}
74
+ AND ${memoryGraphNodes.fidelity} != 'gone'`,
80
75
  )
81
76
  .get();
82
77
 
@@ -87,29 +82,41 @@ export async function executePlaybookCreate(
87
82
  };
88
83
  }
89
84
 
90
- const id = uuid();
91
85
  const now = Date.now();
86
+ const newNode: NewNode = {
87
+ content,
88
+ type: "semantic",
89
+ created: now,
90
+ lastAccessed: now,
91
+ lastConsolidated: now,
92
+ eventDate: null,
93
+ emotionalCharge: {
94
+ valence: 0,
95
+ intensity: 0.1,
96
+ decayCurve: "linear",
97
+ decayRate: 0.05,
98
+ originalIntensity: 0.1,
99
+ },
100
+ fidelity: "vivid",
101
+ confidence: 0.95,
102
+ significance: 0.8,
103
+ stability: 14,
104
+ reinforcementCount: 0,
105
+ lastReinforced: now,
106
+ sourceConversations: [],
107
+ sourceType: "direct",
108
+ narrativeRole: null,
109
+ partOfStory: null,
110
+ imageRefs: null,
111
+ scopeId,
112
+ };
92
113
 
93
- db.insert(memoryItems)
94
- .values({
95
- id,
96
- kind: "playbook",
97
- subject,
98
- statement,
99
- status: "active",
100
- confidence: 0.95,
101
- importance: 0.8,
102
- fingerprint,
103
- sourceType: "tool",
104
- verificationState: "user_confirmed",
105
- scopeId,
106
- firstSeenAt: now,
107
- lastSeenAt: now,
108
- lastUsedAt: null,
109
- })
110
- .run();
114
+ const node = createNode(newNode);
115
+ updateNode(node.id, {
116
+ sourceConversations: [`playbook:${node.id}`],
117
+ });
111
118
 
112
- enqueueMemoryJob("embed_item", { itemId: id });
119
+ enqueueMemoryJob("embed_graph_node", { nodeId: node.id });
113
120
 
114
121
  const autonomyLabel =
115
122
  autonomyLevel === "auto"
@@ -121,7 +128,7 @@ export async function executePlaybookCreate(
121
128
  return {
122
129
  content: [
123
130
  "Playbook created successfully.",
124
- ` ID: ${id}`,
131
+ ` ID: ${node.id}`,
125
132
  ` Trigger: ${trigger}`,
126
133
  ` Channel: ${channel}`,
127
134
  ` Category: ${category}`,
@@ -1,7 +1,4 @@
1
- import { and, eq } from "drizzle-orm";
2
-
3
- import { getDb } from "../../../../memory/db.js";
4
- import { memoryItems } from "../../../../memory/schema.js";
1
+ import { getNode, updateNode } from "../../../../memory/graph/store.js";
5
2
  import { parsePlaybookStatement } from "../../../../playbooks/types.js";
6
3
  import type {
7
4
  ToolContext,
@@ -23,38 +20,28 @@ export async function executePlaybookDelete(
23
20
  const scopeId = context.memoryScopeId ?? "default";
24
21
 
25
22
  try {
26
- const db = getDb();
27
-
28
- const existing = db
29
- .select()
30
- .from(memoryItems)
31
- .where(
32
- and(
33
- eq(memoryItems.id, playbookId),
34
- eq(memoryItems.kind, "playbook"),
35
- eq(memoryItems.scopeId, scopeId),
36
- ),
37
- )
38
- .get();
39
-
40
- if (!existing) {
23
+ const existing = getNode(playbookId);
24
+ if (
25
+ !existing ||
26
+ existing.scopeId !== scopeId ||
27
+ !existing.sourceConversations.some((s) => s.startsWith("playbook:")) ||
28
+ existing.fidelity === "gone"
29
+ ) {
41
30
  return {
42
31
  content: `Error: Playbook with ID "${playbookId}" not found`,
43
32
  isError: true,
44
33
  };
45
34
  }
46
35
 
47
- const playbook = parsePlaybookStatement(existing.statement);
48
- const triggerLabel = playbook?.trigger ?? existing.subject;
36
+ // Extract trigger label from content
37
+ const newlineIdx = existing.content.indexOf("\n");
38
+ const statement =
39
+ newlineIdx !== -1 ? existing.content.slice(newlineIdx + 1) : "";
40
+ const playbook = parsePlaybookStatement(statement);
41
+ const triggerLabel = playbook?.trigger ?? existing.content.split("\n")[0];
49
42
 
50
- // Soft-delete by marking as superseded rather than hard-deleting,
51
- // consistent with how other memory items are retired.
52
- // Setting invalidAt so the cleanup job can eventually hard-delete it.
53
- const now = Date.now();
54
- db.update(memoryItems)
55
- .set({ status: "superseded", invalidAt: now })
56
- .where(eq(memoryItems.id, existing.id))
57
- .run();
43
+ // Soft-delete by setting fidelity to "gone"
44
+ updateNode(existing.id, { fidelity: "gone" });
58
45
 
59
46
  return {
60
47
  content: `Playbook deleted (ID: ${existing.id}, trigger: "${triggerLabel}").`,
@@ -1,7 +1,7 @@
1
- import { and, desc, eq, isNull } from "drizzle-orm";
1
+ import { and, eq, sql } from "drizzle-orm";
2
2
 
3
3
  import { getDb } from "../../../../memory/db.js";
4
- import { memoryItems } from "../../../../memory/schema.js";
4
+ import { memoryGraphNodes } from "../../../../memory/schema.js";
5
5
  import { parsePlaybookStatement } from "../../../../playbooks/types.js";
6
6
  import type {
7
7
  ToolContext,
@@ -23,22 +23,20 @@ export async function executePlaybookList(
23
23
 
24
24
  const rows = db
25
25
  .select({
26
- id: memoryItems.id,
27
- subject: memoryItems.subject,
28
- statement: memoryItems.statement,
29
- importance: memoryItems.importance,
30
- lastSeenAt: memoryItems.lastSeenAt,
26
+ id: memoryGraphNodes.id,
27
+ content: memoryGraphNodes.content,
28
+ significance: memoryGraphNodes.significance,
29
+ lastAccessed: memoryGraphNodes.lastAccessed,
31
30
  })
32
- .from(memoryItems)
31
+ .from(memoryGraphNodes)
33
32
  .where(
34
33
  and(
35
- eq(memoryItems.kind, "playbook"),
36
- eq(memoryItems.status, "active"),
37
- eq(memoryItems.scopeId, scopeId),
38
- isNull(memoryItems.invalidAt),
34
+ eq(memoryGraphNodes.scopeId, scopeId),
35
+ sql`${memoryGraphNodes.sourceConversations} LIKE '%playbook:%'`,
36
+ sql`${memoryGraphNodes.fidelity} != 'gone'`,
39
37
  ),
40
38
  )
41
- .orderBy(desc(memoryItems.importance))
39
+ .orderBy(sql`${memoryGraphNodes.significance} DESC`)
42
40
  .all();
43
41
 
44
42
  if (rows.length === 0) {
@@ -47,12 +45,14 @@ export async function executePlaybookList(
47
45
 
48
46
  const entries: Array<{
49
47
  id: string;
50
- subject: string;
51
- statement: string;
52
48
  playbook: NonNullable<ReturnType<typeof parsePlaybookStatement>>;
53
49
  }> = [];
54
50
  for (const row of rows) {
55
- const playbook = parsePlaybookStatement(row.statement);
51
+ // Content format: "Playbook: <trigger>\n<json statement>"
52
+ const newlineIdx = row.content.indexOf("\n");
53
+ if (newlineIdx === -1) continue;
54
+ const statement = row.content.slice(newlineIdx + 1);
55
+ const playbook = parsePlaybookStatement(statement);
56
56
  if (!playbook) continue;
57
57
 
58
58
  // Apply filters
@@ -66,8 +66,6 @@ export async function executePlaybookList(
66
66
 
67
67
  entries.push({
68
68
  id: row.id,
69
- subject: row.subject,
70
- statement: row.statement,
71
69
  playbook,
72
70
  });
73
71
  }
@@ -1,9 +1,9 @@
1
- import { and, eq } from "drizzle-orm";
1
+ import { and, eq, sql } from "drizzle-orm";
2
2
 
3
3
  import { getDb } from "../../../../memory/db.js";
4
- import { computeMemoryFingerprint } from "../../../../memory/fingerprint.js";
4
+ import { getNode, updateNode } from "../../../../memory/graph/store.js";
5
5
  import { enqueueMemoryJob } from "../../../../memory/jobs-store.js";
6
- import { memoryItems } from "../../../../memory/schema.js";
6
+ import { memoryGraphNodes } from "../../../../memory/schema.js";
7
7
  import type {
8
8
  Playbook,
9
9
  PlaybookAutonomyLevel,
@@ -13,7 +13,6 @@ import type {
13
13
  ToolContext,
14
14
  ToolExecutionResult,
15
15
  } from "../../../../tools/types.js";
16
- import { truncate } from "../../../../util/truncate.js";
17
16
 
18
17
  const VALID_AUTONOMY_LEVELS = new Set<string>(["auto", "draft", "notify"]);
19
18
 
@@ -32,28 +31,29 @@ export async function executePlaybookUpdate(
32
31
  const scopeId = context.memoryScopeId ?? "default";
33
32
 
34
33
  try {
35
- const db = getDb();
36
-
37
- const existing = db
38
- .select()
39
- .from(memoryItems)
40
- .where(
41
- and(
42
- eq(memoryItems.id, playbookId),
43
- eq(memoryItems.kind, "playbook"),
44
- eq(memoryItems.scopeId, scopeId),
45
- ),
46
- )
47
- .get();
48
-
49
- if (!existing) {
34
+ const existing = getNode(playbookId);
35
+ if (
36
+ !existing ||
37
+ existing.scopeId !== scopeId ||
38
+ !existing.sourceConversations.some((s) => s.startsWith("playbook:")) ||
39
+ existing.fidelity === "gone"
40
+ ) {
50
41
  return {
51
42
  content: `Error: Playbook with ID "${playbookId}" not found`,
52
43
  isError: true,
53
44
  };
54
45
  }
55
46
 
56
- const currentPlaybook = parsePlaybookStatement(existing.statement);
47
+ // Extract the JSON statement from the content (after the first newline)
48
+ const newlineIdx = existing.content.indexOf("\n");
49
+ if (newlineIdx === -1) {
50
+ return {
51
+ content: `Error: Playbook data is corrupted for ID "${playbookId}"`,
52
+ isError: true,
53
+ };
54
+ }
55
+ const currentStatement = existing.content.slice(newlineIdx + 1);
56
+ const currentPlaybook = parsePlaybookStatement(currentStatement);
57
57
  if (!currentPlaybook) {
58
58
  return {
59
59
  content: `Error: Playbook data is corrupted for ID "${playbookId}"`,
@@ -91,47 +91,39 @@ export async function executePlaybookUpdate(
91
91
  };
92
92
 
93
93
  const statement = JSON.stringify(updated);
94
- const subject = truncate(`Playbook: ${updated.trigger}`, 80, "");
95
- const now = Date.now();
96
-
97
- const fingerprint = computeMemoryFingerprint(
98
- scopeId,
99
- "playbook",
100
- subject,
101
- statement,
102
- );
94
+ const sanitizedTrigger = updated.trigger.replace(/[\r\n]+/g, " ");
95
+ const subject = `Playbook: ${sanitizedTrigger}`.slice(0, 80);
96
+ const content = `${subject}\n${statement}`;
103
97
 
104
- // Check if another playbook already has this fingerprint
98
+ // Check for duplicate content among other playbook nodes
99
+ const db = getDb();
105
100
  const collision = db
106
- .select({ id: memoryItems.id })
107
- .from(memoryItems)
101
+ .select({ id: memoryGraphNodes.id })
102
+ .from(memoryGraphNodes)
108
103
  .where(
109
104
  and(
110
- eq(memoryItems.fingerprint, fingerprint),
111
- eq(memoryItems.scopeId, scopeId),
105
+ eq(memoryGraphNodes.scopeId, scopeId),
106
+ sql`${memoryGraphNodes.sourceConversations} LIKE '%playbook:%'`,
107
+ eq(memoryGraphNodes.content, content),
108
+ sql`${memoryGraphNodes.fidelity} != 'gone'`,
109
+ sql`${memoryGraphNodes.id} != ${existing.id}`,
112
110
  ),
113
111
  )
114
112
  .get();
115
- if (collision && collision.id !== existing.id) {
113
+
114
+ if (collision) {
116
115
  return {
117
116
  content: `Error: Another playbook with this exact configuration already exists (ID: ${collision.id}).`,
118
117
  isError: true,
119
118
  };
120
119
  }
121
120
 
122
- db.update(memoryItems)
123
- .set({
124
- subject,
125
- statement,
126
- fingerprint,
127
- lastSeenAt: now,
128
- sourceType: "tool",
129
- verificationState: "user_confirmed",
130
- })
131
- .where(eq(memoryItems.id, existing.id))
132
- .run();
121
+ updateNode(existing.id, {
122
+ content,
123
+ lastAccessed: Date.now(),
124
+ });
133
125
 
134
- enqueueMemoryJob("embed_item", { itemId: existing.id });
126
+ enqueueMemoryJob("embed_graph_node", { nodeId: existing.id });
135
127
 
136
128
  const autonomyLabel =
137
129
  updated.autonomyLevel === "auto"
@@ -87,6 +87,14 @@ The `mode` parameter controls what happens when a schedule fires:
87
87
 
88
88
  Use `notify` for simple reminders ("remind me to take medicine at 9am") and `execute` for tasks that need assistant action ("check my calendar at 8am and send me a digest").
89
89
 
90
+ ## Conversation Reuse
91
+
92
+ By default, each schedule run creates a new conversation. For recurring schedules that benefit from accumulating context across runs (e.g. polling-style jobs, daily digests that reference prior results), set `reuse_conversation: true`. When enabled, subsequent runs reuse the conversation from the last successful run instead of creating a new one.
93
+
94
+ - Only applies to **recurring** schedules; ignored for one-shot schedules.
95
+ - If the prior conversation has been deleted, a new one is created automatically.
96
+ - On the first run (no prior conversation), a new conversation is created as usual.
97
+
90
98
  ## Routing (notify mode)
91
99
 
92
100
  Control how notify-mode schedules are delivered at trigger time with `routing_intent`:
@@ -101,9 +109,20 @@ Optionally pass `routing_hints` (a JSON object) to influence routing decisions (
101
109
 
102
110
  - **Default to `all_channels`** for most notifications. Users usually want to be notified wherever they are.
103
111
  - **Use `single_channel`** only when the user explicitly specifies a single channel (e.g. "remind me on Telegram").
104
- - **Check `user_message_channel` (or `channel` when all channels are the same)** from the turn context. If the user is currently active on a specific channel, include it as a routing hint:
112
+ - **Determine the originating channel** for routing hints using this priority:
113
+ 1. **`source_channel`** from `<turn_context>` — use directly if present. This is the authoritative channel name.
114
+ 2. **`interface` fallback** — if `source_channel` is absent (common for guardian/direct users), map the `interface` value to a channel name:
115
+ | `interface` value | Channel name |
116
+ | --- | --- |
117
+ | `macos`, `ios` | `vellum` |
118
+ | `telegram` | `telegram` |
119
+ | `slack` | `slack` |
120
+ | `cli` | _(omit — no routable channel)_ |
121
+ 3. If neither field is present or the interface is `cli`, omit `preferred_channels`.
122
+
123
+ When a channel is determined, include it as a routing hint:
105
124
  ```
106
- routing_hints: { preferred_channels: ["vellum"] }
125
+ routing_hints: { preferred_channels: ["<resolved channel>"] }
107
126
  routing_intent: "all_channels"
108
127
  ```
109
128
 
@@ -119,6 +138,7 @@ Use `syntax` + `expression` to specify the schedule type explicitly, or just `ex
119
138
 
120
139
  ## Tips
121
140
 
141
+ - **When the user specifies a name for the schedule, use it exactly as given.** Do not paraphrase, embellish, or generate a descriptive name.
122
142
  - Use `schedule_create` for both recurring automation ("every day at 9am") and one-time reminders ("remind me at 3pm").
123
143
  - For task tracking ("add to my tasks", "add to my queue"), use task_list_add instead.
124
144
  - `fire_at` must be a strict ISO 8601 timestamp with timezone offset or Z (e.g. `2025-03-15T09:00:00-05:00`).
@@ -56,6 +56,10 @@
56
56
  "type": "boolean",
57
57
  "description": "When true, suppress completion notifications for this schedule. The job still runs and produces output, but no notification or conversation message is sent on completion. Useful for high-frequency recurring jobs that report findings separately. Defaults to false."
58
58
  },
59
+ "reuse_conversation": {
60
+ "type": "boolean",
61
+ "description": "When true, reuse the same conversation across recurring schedule runs instead of creating a new one each time. Useful for polling-style schedules that accumulate context over time. Ignored for one-shot schedules. Defaults to false."
62
+ },
59
63
  "activity": {
60
64
  "type": "string",
61
65
  "description": "Brief non-technical explanation of why this tool is being called"
@@ -147,6 +151,10 @@
147
151
  "type": "boolean",
148
152
  "description": "When true, suppress completion notifications for this schedule. Useful for high-frequency jobs that report findings separately."
149
153
  },
154
+ "reuse_conversation": {
155
+ "type": "boolean",
156
+ "description": "When true, reuse the same conversation across recurring schedule runs instead of creating a new one each time. Useful for polling-style schedules that accumulate context over time. Ignored for one-shot schedules."
157
+ },
150
158
  "activity": {
151
159
  "type": "string",
152
160
  "description": "Brief non-technical explanation of why this tool is being called"
@@ -7,27 +7,17 @@ import type {
7
7
  ToolContext,
8
8
  ToolExecutionResult,
9
9
  } from "../../../../tools/types.js";
10
- import { getWorkspaceDir } from "../../../../util/platform.js";
10
+ import { getAvatarDir, getAvatarImagePath } from "../../../../util/platform.js";
11
11
 
12
12
  export async function run(
13
13
  _input: Record<string, unknown>,
14
14
  _context: ToolContext,
15
15
  ): Promise<ToolExecutionResult> {
16
- const avatarPath = join(
17
- getWorkspaceDir(),
18
- "data",
19
- "avatar",
20
- "avatar-image.png",
21
- );
16
+ const avatarPath = getAvatarImagePath();
22
17
 
23
18
  if (!existsSync(avatarPath)) {
24
19
  // Check for native character traits and regenerate the static PNG
25
- const traitsPath = join(
26
- getWorkspaceDir(),
27
- "data",
28
- "avatar",
29
- "character-traits.json",
30
- );
20
+ const traitsPath = join(getAvatarDir(), "character-traits.json");
31
21
  if (existsSync(traitsPath)) {
32
22
  try {
33
23
  const traits = JSON.parse(readFileSync(traitsPath, "utf-8"));