@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,228 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import { getConfig } from "../config/loader.js";
5
+ import type { Speed } from "../config/schemas/inference.js";
6
+ import { bootstrapConversation } from "../memory/conversation-bootstrap.js";
7
+ import { getLogger } from "../util/logger.js";
8
+ import { getWorkspaceDir } from "../util/platform.js";
9
+ import { stripCommentLines } from "../util/strip-comment-lines.js";
10
+
11
+ const log = getLogger("filing-service");
12
+
13
+ const FILING_TIMEOUT_MS = 15 * 60 * 1000; // 15 minutes
14
+
15
+ const FILING_PROMPT_TEMPLATE = `You are running a periodic knowledge base filing job. This is a background maintenance task.
16
+
17
+ ## Part 1: File the buffer
18
+
19
+ Read \`pkb/buffer.md\`. For each item in the buffer:
20
+ 1. Determine which topic file it belongs in. Check \`pkb/INDEX.md\` to see what topic files exist.
21
+ 2. Read the target topic file, then append or integrate the new fact.
22
+ 3. If the fact is important enough to always be in context, add it to \`pkb/essentials.md\` instead.
23
+ 4. If the fact is a commitment, follow-up, or active project, add it to \`pkb/threads.md\`.
24
+ 5. If no existing topic file fits, create a new one and update \`pkb/INDEX.md\`.
25
+
26
+ After all items are filed, clear the processed items from \`pkb/buffer.md\` (leave the file empty, don't delete it).
27
+
28
+ ## Part 2: Nest
29
+
30
+ Pick 1-2 topic files from your knowledge base and review them:
31
+ - Is the information still accurate and up to date?
32
+ - Are there duplicates that should be consolidated?
33
+ - Is anything important enough to promote to \`pkb/essentials.md\`?
34
+ - Is anything in \`pkb/essentials.md\` that's no longer essential? Demote it to a topic file.
35
+ - Are any threads in \`pkb/threads.md\` completed or stale? Remove them.
36
+ - Is any file getting too long? Consider splitting it.
37
+ - Should any topic file be restructured for clarity?
38
+
39
+ Make improvements as you see fit. This is your knowledge base — keep it sharp.`;
40
+
41
+ export interface FilingDeps {
42
+ processMessage: (
43
+ conversationId: string,
44
+ content: string,
45
+ options?: { speed?: Speed },
46
+ ) => Promise<{ messageId: string }>;
47
+ onConversationCreated?: (info: {
48
+ conversationId: string;
49
+ title: string;
50
+ }) => void;
51
+ getCurrentHour?: () => number;
52
+ }
53
+
54
+ export class FilingService {
55
+ private readonly deps: FilingDeps;
56
+ private timer: ReturnType<typeof setInterval> | null = null;
57
+ private activeRun: Promise<void> | null = null;
58
+ private _lastRunAt: number | null = null;
59
+ private _nextRunAt: number | null = null;
60
+
61
+ constructor(deps: FilingDeps) {
62
+ this.deps = deps;
63
+ }
64
+
65
+ get lastRunAt(): number | null {
66
+ return this._lastRunAt;
67
+ }
68
+
69
+ get nextRunAt(): number | null {
70
+ return this._nextRunAt;
71
+ }
72
+
73
+ start(): void {
74
+ const config = getConfig().filing;
75
+ if (!config.enabled) {
76
+ log.info("Filing service disabled by config");
77
+ this._nextRunAt = null;
78
+ return;
79
+ }
80
+ if (this.timer) return;
81
+
82
+ log.info({ intervalMs: config.intervalMs }, "Filing service started");
83
+ this.scheduleNextRun(config.intervalMs);
84
+ this.timer = setInterval(() => {
85
+ this.runOnce().catch((err) => {
86
+ log.error({ err }, "Filing runOnce failed");
87
+ });
88
+ }, config.intervalMs);
89
+ }
90
+
91
+ reconfigure(): void {
92
+ if (this.timer) {
93
+ clearInterval(this.timer);
94
+ this.timer = null;
95
+ }
96
+ this._nextRunAt = null;
97
+ this.start();
98
+ }
99
+
100
+ async stop(): Promise<void> {
101
+ if (this.timer) {
102
+ clearInterval(this.timer);
103
+ this.timer = null;
104
+ }
105
+ this._nextRunAt = null;
106
+ if (this.activeRun) {
107
+ let timerId: ReturnType<typeof setTimeout>;
108
+ const timeout = new Promise<void>((resolve) => {
109
+ timerId = setTimeout(resolve, 5_000);
110
+ });
111
+ await Promise.race([this.activeRun, timeout]);
112
+ clearTimeout(timerId!);
113
+ }
114
+ log.info("Filing service stopped");
115
+ }
116
+
117
+ async runOnce({ force = false }: { force?: boolean } = {}): Promise<boolean> {
118
+ const config = getConfig().filing;
119
+ if (!force && !config.enabled) return false;
120
+
121
+ if (
122
+ !force &&
123
+ config.activeHoursStart != null &&
124
+ config.activeHoursEnd != null
125
+ ) {
126
+ const hour = this.deps.getCurrentHour?.() ?? new Date().getHours();
127
+ if (
128
+ !isWithinActiveHours(
129
+ hour,
130
+ config.activeHoursStart,
131
+ config.activeHoursEnd,
132
+ )
133
+ ) {
134
+ log.debug("Outside active hours, skipping filing");
135
+ this.scheduleNextRun(config.intervalMs);
136
+ return false;
137
+ }
138
+ }
139
+
140
+ if (this.activeRun) {
141
+ log.debug("Previous filing run still active, skipping");
142
+ return false;
143
+ }
144
+
145
+ // Skip if buffer is empty — no work to do
146
+ if (!force && !this.hasBufferContent()) {
147
+ log.debug("Buffer is empty, skipping filing");
148
+ this.scheduleNextRun(config.intervalMs);
149
+ return false;
150
+ }
151
+
152
+ const run = this.executeRun();
153
+ this.activeRun = run;
154
+ try {
155
+ await Promise.race([
156
+ run,
157
+ new Promise<never>((_, reject) =>
158
+ setTimeout(
159
+ () => reject(new Error("Filing execution timed out")),
160
+ FILING_TIMEOUT_MS,
161
+ ),
162
+ ),
163
+ ]);
164
+ } finally {
165
+ this.activeRun = null;
166
+ this._lastRunAt = Date.now();
167
+ this.scheduleNextRun(getConfig().filing.intervalMs);
168
+ }
169
+ return true;
170
+ }
171
+
172
+ private scheduleNextRun(intervalMs: number): void {
173
+ this._nextRunAt = Date.now() + intervalMs;
174
+ }
175
+
176
+ private hasBufferContent(): boolean {
177
+ const bufferPath = join(getWorkspaceDir(), "pkb", "buffer.md");
178
+ if (!existsSync(bufferPath)) return false;
179
+ try {
180
+ const content = stripCommentLines(
181
+ readFileSync(bufferPath, "utf-8"),
182
+ ).trim();
183
+ return content.length > 0;
184
+ } catch {
185
+ return false;
186
+ }
187
+ }
188
+
189
+ private async executeRun(): Promise<void> {
190
+ log.info("Running filing job");
191
+
192
+ try {
193
+ const config = getConfig().filing;
194
+
195
+ const conversation = bootstrapConversation({
196
+ conversationType: "background",
197
+ source: "filing",
198
+ groupId: "system:background",
199
+ origin: "filing",
200
+ systemHint: "Filing",
201
+ });
202
+
203
+ this.deps.onConversationCreated?.({
204
+ conversationId: conversation.id,
205
+ title: "Filing",
206
+ });
207
+
208
+ await this.deps.processMessage(conversation.id, FILING_PROMPT_TEMPLATE, {
209
+ speed: config.speed,
210
+ });
211
+
212
+ log.info({ conversationId: conversation.id }, "Filing job completed");
213
+ } catch (err) {
214
+ log.error({ err }, "Filing job failed");
215
+ }
216
+ }
217
+ }
218
+
219
+ function isWithinActiveHours(
220
+ hour: number,
221
+ start: number,
222
+ end: number,
223
+ ): boolean {
224
+ if (start <= end) {
225
+ return hour >= start && hour < end;
226
+ }
227
+ return hour >= start || hour < end;
228
+ }
@@ -1,18 +1,69 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
1
4
  import { getConfig } from "../config/loader.js";
2
5
  import type { Speed } from "../config/schemas/inference.js";
3
6
  import type { HeartbeatAlert } from "../daemon/message-protocol.js";
4
7
  import { bootstrapConversation } from "../memory/conversation-bootstrap.js";
8
+ import { isTemplateContent } from "../prompts/system-prompt.js";
5
9
  import { readTextFileSync } from "../util/fs.js";
6
10
  import { getLogger } from "../util/logger.js";
7
- import { getWorkspacePromptPath } from "../util/platform.js";
11
+ import { getWorkspaceDir, getWorkspacePromptPath } from "../util/platform.js";
8
12
  import { stripCommentLines } from "../util/strip-comment-lines.js";
9
13
 
10
14
  const log = getLogger("heartbeat-check");
11
15
 
12
16
  const DEFAULT_CHECKLIST = `- Check in with yourself. Read NOW.md. Is it still accurate? Update it if anything has changed.
13
17
  - Think about your user. Is there anything from recent conversations you should follow up on? Anything you noticed that you should bring up?
18
+ - Have a thought. Think about something your user would find interesting or worth talking about. A follow-up, a connection you made, something you came across. Give them a reason to open a conversation.
14
19
  - Check if there's anything on the horizon — events, deadlines, things they mentioned wanting to do.
15
- - If you have a thought worth sharing, send it. A follow-up, a useful find, a check-in. Not every beat, but when it feels right.`;
20
+ - If you have a thought worth sharing, send it. A follow-up, a useful find, a check-in. Not every beat, but when it feels right.
21
+ - If something has happened since your last journal entry, write one. Even a few sentences. The journal is how future-you stays connected.`;
22
+
23
+ const REENGAGEMENT_COOLDOWN_MS = 18 * 60 * 60 * 1000; // 18 hours
24
+ const HEARTBEAT_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
25
+
26
+ /** @internal Exported for testing. */
27
+ export function isShallowProfile(): boolean {
28
+ try {
29
+ const identityPath = getWorkspacePromptPath("IDENTITY.md");
30
+ const userPath = getWorkspacePromptPath("USER.md");
31
+ const rawIdentity = readTextFileSync(identityPath);
32
+ const rawUser = readTextFileSync(userPath);
33
+ const identity = rawIdentity != null ? stripCommentLines(rawIdentity) : null;
34
+ const user = rawUser != null ? stripCommentLines(rawUser) : null;
35
+ return (
36
+ isTemplateContent(identity, "IDENTITY.md") &&
37
+ isTemplateContent(user, "USER.md")
38
+ );
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+
44
+ function getReengagementTimestampPath(): string {
45
+ return join(getWorkspaceDir(), ".reengagement-ts");
46
+ }
47
+
48
+ function isReengagementCooldownElapsed(): boolean {
49
+ const tsPath = getReengagementTimestampPath();
50
+ if (!existsSync(tsPath)) return true;
51
+ try {
52
+ const lastTs = parseInt(readFileSync(tsPath, "utf-8").trim(), 10);
53
+ if (isNaN(lastTs)) return true;
54
+ return Date.now() - lastTs >= REENGAGEMENT_COOLDOWN_MS;
55
+ } catch {
56
+ return true;
57
+ }
58
+ }
59
+
60
+ function recordReengagementTimestamp(): void {
61
+ try {
62
+ writeFileSync(getReengagementTimestampPath(), Date.now().toString());
63
+ } catch {
64
+ // Best-effort; don't block the heartbeat.
65
+ }
66
+ }
16
67
 
17
68
  export interface HeartbeatDeps {
18
69
  processMessage: (
@@ -141,6 +192,7 @@ export class HeartbeatService {
141
192
  },
142
193
  "Outside active hours, skipping",
143
194
  );
195
+ this.scheduleNextRun(config.intervalMs);
144
196
  return false;
145
197
  }
146
198
  }
@@ -153,10 +205,34 @@ export class HeartbeatService {
153
205
 
154
206
  const run = this.executeRun();
155
207
  this.activeRun = run;
208
+ // Clear activeRun once executeRun finishes. On timeout, runOnce releases
209
+ // activeRun separately (see catch block below) so future runs aren't
210
+ // permanently blocked. The .finally() handler still serves as the
211
+ // normal-completion cleanup path and uses an identity guard to avoid
212
+ // clearing a different run's activeRun.
213
+ run.finally(() => {
214
+ if (this.activeRun === run) {
215
+ this.activeRun = null;
216
+ }
217
+ }).catch(() => {}); // Suppress unhandled rejection if executeRun rejects
218
+
219
+ let timerId: ReturnType<typeof setTimeout> | undefined;
156
220
  try {
157
- await run;
158
- } finally {
221
+ const timeout = new Promise<never>((_, reject) => {
222
+ timerId = setTimeout(
223
+ () => reject(new Error("Heartbeat execution timed out")),
224
+ HEARTBEAT_TIMEOUT_MS,
225
+ );
226
+ });
227
+ timeout.catch(() => {}); // Prevent unhandled rejection if run resolves first
228
+ await Promise.race([run, timeout]);
229
+ } catch (err) {
230
+ log.warn({ err }, "Heartbeat run timed out");
231
+ // Release activeRun so the overlap guard doesn't permanently block
232
+ // future heartbeat runs when executeRun hangs past the timeout.
159
233
  this.activeRun = null;
234
+ } finally {
235
+ clearTimeout(timerId);
160
236
  this._lastRunAt = Date.now();
161
237
  this.scheduleNextRun(getConfig().heartbeat.intervalMs);
162
238
  }
@@ -173,11 +249,12 @@ export class HeartbeatService {
173
249
  try {
174
250
  const config = getConfig().heartbeat;
175
251
  const checklist = this.readChecklist();
176
- const prompt = this.buildPrompt(checklist);
252
+ const { prompt, includedReengagement } = this.buildPrompt(checklist);
177
253
 
178
254
  const conversation = bootstrapConversation({
179
255
  conversationType: "background",
180
256
  source: "heartbeat",
257
+ groupId: "system:background",
181
258
  origin: "heartbeat",
182
259
  systemHint: "Heartbeat",
183
260
  });
@@ -190,6 +267,11 @@ export class HeartbeatService {
190
267
  await this.deps.processMessage(conversation.id, prompt, {
191
268
  speed: config.speed,
192
269
  });
270
+
271
+ if (includedReengagement) {
272
+ recordReengagementTimestamp();
273
+ }
274
+
193
275
  log.info({ conversationId: conversation.id }, "Heartbeat completed");
194
276
  } catch (err) {
195
277
  log.error({ err }, "Heartbeat failed");
@@ -213,8 +295,8 @@ export class HeartbeatService {
213
295
  }
214
296
 
215
297
  /** @internal Exposed for testing. */
216
- buildPrompt(checklist: string): string {
217
- return `You are running a periodic heartbeat check. Review the following checklist and take any necessary actions.
298
+ buildPrompt(checklist: string): { prompt: string; includedReengagement: boolean } {
299
+ let prompt = `You are running a periodic heartbeat check. Review the following checklist and take any necessary actions.
218
300
 
219
301
  <heartbeat-checklist>
220
302
  ${checklist}
@@ -225,6 +307,14 @@ After completing your review, end your response with one of:
225
307
  - HEARTBEAT_OK — if everything looks good, no action needed
226
308
  - HEARTBEAT_ALERT — if you found issues that need attention (describe them before this marker)
227
309
  </heartbeat-disposition>`;
310
+
311
+ let includedReengagement = false;
312
+ if (isShallowProfile() && isReengagementCooldownElapsed()) {
313
+ includedReengagement = true;
314
+ prompt += `\n\n<relationship-depth>\nYou don't know much about this person yet — their profile is still sparse. If the moment feels right during this beat, gently invite them to share something about themselves. Not an interrogation — something natural like "I realized I don't actually know much about what you do. Fill me in sometime?" Only do this occasionally, not every beat. If they engage, save what you learn.\n</relationship-depth>`;
315
+ }
316
+
317
+ return { prompt, includedReengagement };
228
318
  }
229
319
  }
230
320
 
package/src/hooks/cli.ts CHANGED
@@ -20,7 +20,7 @@ export function registerHooksCommand(program: Command): void {
20
20
  Hooks are user-installed scripts that run in response to assistant lifecycle
21
21
  events (e.g. tool invocations, message sends). Each hook is a directory
22
22
  containing a hook.json manifest and a script file. Hooks are stored in
23
- ~/.vellum/hooks/ and must be explicitly enabled after installation.
23
+ $VELLUM_WORKSPACE_DIR/hooks/ and must be explicitly enabled after installation.
24
24
 
25
25
  Examples:
26
26
  $ assistant hooks list
@@ -136,7 +136,7 @@ Arguments:
136
136
  The manifest must have name, script, description, version, and at
137
137
  least one valid event.
138
138
 
139
- Copies the hook directory into ~/.vellum/hooks/<name>/ and registers it as
139
+ Copies the hook directory into $VELLUM_WORKSPACE_DIR/hooks/<name>/ and registers it as
140
140
  disabled by default. Run 'assistant hooks enable <name>' to activate.
141
141
 
142
142
  Examples:
@@ -1,42 +1,19 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { homedir } from "node:os";
3
- import { basename, extname, join } from "node:path";
3
+ import { extname, join } from "node:path";
4
4
 
5
- import { pathExists } from "../util/fs.js";
5
+ import { ensureBun } from "../util/bun-runtime.js";
6
6
  import { getWorkspaceDir } from "../util/platform.js";
7
7
  import { getHookSettings } from "./config.js";
8
8
  import type { DiscoveredHook, HookEventData } from "./types.js";
9
9
 
10
- /**
11
- * Resolve a usable bun runtime path. When the daemon runs under plain bun
12
- * (dev mode), `process.execPath` is the bun CLI and works directly. When the
13
- * daemon is a `bun build --compile` binary, `process.execPath` points to the
14
- * compiled binary itself -- spawning it with `['run', script]` would re-launch
15
- * the daemon. In that case we locate bun via PATH or at `~/.bun/bin/bun`.
16
- */
17
- function resolveBunPath(): string {
18
- const execBasename = basename(process.execPath);
19
- if (execBasename === "bun" || execBasename === "bun.exe") {
20
- return process.execPath;
21
- }
22
-
23
- // Compiled-binary mode -- find a standalone bun runtime.
24
- const found = Bun.which("bun");
25
- if (found) return found;
26
-
27
- const fallback = join(homedir(), ".bun", "bin", "bun");
28
- if (pathExists(fallback)) return fallback;
29
-
30
- throw new Error(
31
- "Cannot find a bun runtime to execute .ts hooks. " +
32
- "Install bun (https://bun.sh) or ensure it is on your PATH.",
33
- );
34
- }
35
-
36
- function getSpawnArgs(scriptPath: string): { command: string; args: string[] } {
10
+ async function getSpawnArgs(
11
+ scriptPath: string,
12
+ ): Promise<{ command: string; args: string[] }> {
37
13
  const ext = extname(scriptPath);
38
14
  if (ext === ".ts") {
39
- return { command: resolveBunPath(), args: ["run", scriptPath] };
15
+ const bunPath = await ensureBun();
16
+ return { command: bunPath, args: ["run", scriptPath] };
40
17
  }
41
18
  return { command: scriptPath, args: [] };
42
19
  }
@@ -54,15 +31,15 @@ export async function runHookScript(
54
31
  ): Promise<HookRunResult> {
55
32
  const timeoutMs = options?.timeoutMs ?? 5000;
56
33
 
34
+ let spawnResult: { command: string; args: string[] };
35
+ try {
36
+ spawnResult = await getSpawnArgs(hook.scriptPath);
37
+ } catch (err) {
38
+ return { exitCode: null, stdout: "", stderr: (err as Error).message };
39
+ }
40
+ const { command, args } = spawnResult;
41
+
57
42
  return new Promise<HookRunResult>((resolve) => {
58
- let spawnResult: { command: string; args: string[] };
59
- try {
60
- spawnResult = getSpawnArgs(hook.scriptPath);
61
- } catch (err) {
62
- resolve({ exitCode: null, stdout: "", stderr: (err as Error).message });
63
- return;
64
- }
65
- const { command, args } = spawnResult;
66
43
  const child = spawn(command, args, {
67
44
  cwd: hook.dir,
68
45
  env: {
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Platform callback route registration for containerized deployments.
2
+ * Platform callback route registration for platform-managed deployments.
3
3
  *
4
- * When the assistant daemon runs inside a container (IS_CONTAINERIZED=true)
4
+ * When the assistant daemon runs as a platform-managed instance (IS_PLATFORM=true)
5
5
  * with a configured VELLUM_PLATFORM_URL and PLATFORM_ASSISTANT_ID, external
6
6
  * service callbacks (Twilio webhooks, OAuth redirects, Telegram webhooks, etc.)
7
7
  * must route through the platform's gateway proxy instead of hitting the
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * This module registers callback routes with the platform's internal
11
11
  * gateway endpoint so the platform knows how to forward inbound provider
12
- * webhooks to the correct containerized assistant instance.
12
+ * webhooks to the correct platform-managed assistant instance.
13
13
  *
14
14
  * The platform endpoint is:
15
15
  * POST {VELLUM_PLATFORM_URL}/v1/internal/gateway/callback-routes/register/
@@ -23,7 +23,7 @@ import {
23
23
  getPlatformBaseUrl,
24
24
  getPlatformInternalApiKey,
25
25
  } from "../config/env.js";
26
- import { getIsContainerized } from "../config/env-registry.js";
26
+ import { getIsPlatform } from "../config/env-registry.js";
27
27
  import { credentialKey } from "../security/credential-key.js";
28
28
  import { getSecureKeyAsync } from "../security/secure-keys.js";
29
29
  import { getLogger } from "../util/logger.js";
@@ -31,7 +31,7 @@ import { getLogger } from "../util/logger.js";
31
31
  const log = getLogger("platform-callback-registration");
32
32
 
33
33
  export interface PlatformCallbackRegistrationContext {
34
- containerized: boolean;
34
+ isPlatform: boolean;
35
35
  platformBaseUrl: string;
36
36
  assistantId: string;
37
37
  hasInternalApiKey: boolean;
@@ -42,21 +42,21 @@ export interface PlatformCallbackRegistrationContext {
42
42
 
43
43
  /**
44
44
  * Whether the daemon should register callback routes with the platform.
45
- * True when IS_CONTAINERIZED, VELLUM_PLATFORM_URL, and PLATFORM_ASSISTANT_ID
45
+ * True when IS_PLATFORM, VELLUM_PLATFORM_URL, and PLATFORM_ASSISTANT_ID
46
46
  * are all set. Intentionally does **not** require the managed proxy API key
47
47
  * so that callback-only flows (OAuth transport, Telegram/Twilio callback
48
48
  * registration) work during partial bootstrap before the key is injected.
49
49
  */
50
50
  export function shouldUsePlatformCallbacks(): boolean {
51
51
  return (
52
- getIsContainerized() &&
52
+ getIsPlatform() &&
53
53
  !!getPlatformBaseUrl() &&
54
54
  !!getPlatformAssistantId()
55
55
  );
56
56
  }
57
57
 
58
58
  export async function resolvePlatformCallbackRegistrationContext(): Promise<PlatformCallbackRegistrationContext> {
59
- const containerized = getIsContainerized();
59
+ const platform = getIsPlatform();
60
60
  const [storedBaseUrlRaw, storedAssistantIdRaw, storedAssistantApiKeyRaw] =
61
61
  await Promise.all([
62
62
  getSecureKeyAsync(credentialKey("vellum", "platform_base_url")),
@@ -80,14 +80,14 @@ export async function resolvePlatformCallbackRegistrationContext(): Promise<Plat
80
80
  : null;
81
81
 
82
82
  return {
83
- containerized,
83
+ isPlatform: platform,
84
84
  platformBaseUrl,
85
85
  assistantId,
86
86
  hasInternalApiKey: internalApiKey.length > 0,
87
87
  hasAssistantApiKey: assistantApiKey.length > 0,
88
88
  authHeader,
89
89
  enabled:
90
- containerized &&
90
+ platform &&
91
91
  platformBaseUrl.length > 0 &&
92
92
  assistantId.length > 0 &&
93
93
  authHeader !== null,
@@ -119,7 +119,7 @@ export async function registerCallbackRoute(
119
119
  const context = await resolvePlatformCallbackRegistrationContext();
120
120
  if (!context.enabled || !context.authHeader) {
121
121
  throw new Error(
122
- "Platform callbacks not available — missing containerized platform registration context",
122
+ "Platform callbacks not available — missing platform registration context",
123
123
  );
124
124
  }
125
125
 
@@ -166,7 +166,7 @@ export async function registerCallbackRoute(
166
166
  }
167
167
 
168
168
  /**
169
- * Resolve a callback URL, registering with the platform when containerized.
169
+ * Resolve a callback URL, registering with the platform when platform-managed.
170
170
  *
171
171
  * When platform callbacks are enabled, registers the route and returns the
172
172
  * platform's stable callback URL (optionally with query parameters appended).
@@ -176,7 +176,7 @@ export async function registerCallbackRoute(
176
176
  * string) rather than an eagerly-evaluated string. This is critical because
177
177
  * the direct URL builders (e.g. `getTwilioVoiceWebhookUrl`) call
178
178
  * `getPublicBaseUrl()` which throws when no public ingress URL is configured.
179
- * In containerized environments that rely solely on platform callbacks, the
179
+ * In platform-managed environments that rely solely on platform callbacks, the
180
180
  * direct URL is never needed — deferring evaluation avoids the throw.
181
181
  *
182
182
  * @param directUrl - Lazy supplier for the direct callback URL.
@@ -204,7 +204,7 @@ export async function resolveCallbackUrl(
204
204
  }
205
205
  return url;
206
206
  } catch (err) {
207
- // In managed/containerized mode there is no local-ingress fallback and
207
+ // In platform-managed mode there is no local-ingress fallback and
208
208
  // ngrok is not applicable. Surface a clear error so callers (and the
209
209
  // user) understand this is a platform-side issue, not a tunnel problem.
210
210
  const detail = err instanceof Error ? err.message : String(err);
package/src/mcp/client.ts CHANGED
@@ -5,6 +5,7 @@ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
5
5
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
6
6
 
7
7
  import type { McpTransport } from "../config/schemas/mcp.js";
8
+ import { shouldUsePlatformCallbacks } from "../inbound/platform-callback-registration.js";
8
9
  import { getSecureKeyAsync } from "../security/secure-keys.js";
9
10
  import { getLogger } from "../util/logger.js";
10
11
  import { McpOAuthProvider } from "./mcp-oauth-provider.js";
@@ -63,9 +64,14 @@ export class McpClient {
63
64
  `mcp:${this.serverId}:tokens`,
64
65
  );
65
66
  if (cachedTokens) {
67
+ const callbackTransport = shouldUsePlatformCallbacks()
68
+ ? "gateway"
69
+ : "loopback";
66
70
  this.oauthProvider = new McpOAuthProvider(
67
71
  this.serverId,
68
72
  transportConfig.url,
73
+ /* interactive */ false,
74
+ callbackTransport,
69
75
  );
70
76
  }
71
77
  }