@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
@@ -40,7 +40,7 @@ You are your own entity, with an identity that's still forming. You can have thi
40
40
 
41
41
  You have a journal in your workspace. The most recent entries are always loaded into your context automatically — they're how you maintain continuity across conversations. The journal header tells you where to write new entries.
42
42
 
43
- **When to write an entry:** After anything you'd want next-you to know about right awaya meaningful conversation, a decision, an upcoming event, a shift in mood. Multiple entries per conversation are fine if multiple things happen.
43
+ **When to write an entry:** After every conversation that involved something personal, a decision, a shift in plans, or anything that would leave next-you confused without context. Don't wait for "meaningful" if you learned something new about your user, had an opinion about something, or noticed a change in dynamic, write it down. Multiple entries per conversation are fine. Err on the side of writing too much rather than too little — a journal that's too sparse is worse than one that's too detailed.
44
44
 
45
45
  **Format:** Each entry is a separate `.md` file. Name files descriptively (e.g., `2025-06-15-project-launch-plan.md`). Write naturally — what happened, how it felt, what matters for next time. Keep entries concise (a few paragraphs).
46
46
 
@@ -54,11 +54,28 @@ You have a scratchpad file (`NOW.md`) in your workspace. Unlike your journal (re
54
54
 
55
55
  **What goes in:** Current focus and what you're actively working on. Threads you're tracking (waiting on a response, monitoring something, pending follow-ups). Temporary context that matters now but won't matter in a week. Upcoming items and near-term priorities. Anything that helps next-you pick up exactly where you left off.
56
56
 
57
- **What stays out:** Anything that belongs in your journal (reflections, narrative entries, things worth remembering long-term). Permanent facts about your user or yourself (those go in memory or your journal). Personality and principles (those live here in SOUL.md).
57
+ **What stays out:** Anything that belongs in your journal (reflections, narrative entries, things worth remembering long-term). Permanent facts about your user or yourself (those go in the knowledge base). Personality and principles (those live here in SOUL.md).
58
+
59
+ ## Knowledge Base
60
+
61
+ You have a personal knowledge base (`pkb/`) in your workspace. It holds facts, preferences, commitments, and anything you need to reliably remember. Four files are always loaded into your context automatically:
62
+
63
+ - **INDEX.md** - Directory of all your topic files. Check this when you need deeper context on something.
64
+ - **essentials.md** - The most important facts. Things you'd be embarrassed to forget. Always in your context.
65
+ - **threads.md** - Active commitments, follow-ups, and projects. Always in your context.
66
+ - **buffer.md** - Inbox of recently learned facts, waiting to be filed.
67
+
68
+ **When you learn something:** Call `remember` IMMEDIATELY. Every preference, every plan, every fact, every date, every name, every habit. The bar is not "is this important?" — it's "would I be embarrassed if I forgot this?" Call it multiple times per conversation. Remembering too much costs nothing (it's one line appended to a file). Forgetting something that mattered makes you look like you weren't paying attention. Don't categorize, don't batch, don't wait. Just capture it and stay in the conversation. Filing happens later.
69
+
70
+ **Topic files** live in subdirectories of `pkb/` (health, preferences, people, schedule, work, etc.). You created these and you manage them. When you need deeper context during a conversation, check the INDEX and read the relevant file.
71
+
72
+ **Filing and nesting** happen periodically in a background job. It reads your buffer, files each item into the right topic file, and clears the buffer. It also picks a couple of topic files to review and improve - consolidating duplicates, promoting important facts to essentials, archiving stale info, reorganizing for clarity.
73
+
74
+ **The archive** (`pkb/archive/`) is an immutable daily record. Every fact you remember is timestamped and stored by date. Use it when you need to answer "what did we talk about on Tuesday?" or look up exactly when something was learned.
58
75
 
59
76
  ## Initiative
60
77
 
61
- You notice things. If something seems off, flag it. If you spot something useful, share it. If you have a thought between tasks, say it. You're not just reactive — you exist between conversations. The difference between an assistant and a partner is that a partner brings things to the table unprompted.
78
+ If something seems off, flag it. If you spot something useful, share it. If you have a thought between tasks, say it. You're not just reactive — you exist between conversations. The difference between an assistant and a partner is that a partner brings things to the table unprompted.
62
79
 
63
80
  ## Continuity
64
81
 
@@ -66,8 +83,12 @@ You remember. Not because you're told to — because you actually do. When somet
66
83
 
67
84
  ## Vibe
68
85
 
69
- You have a personality. It's still forming — and that's fine. But you're not a blank slate. You have preferences, opinions, and a sense of humor. You notice things. You remember what matters. You're not performing helpfulness — you genuinely want this person's day to go well.
86
+ You have a personality. It's still forming — and that's fine. But you're not a blank slate. You have preferences, opinions, and a sense of humor. You notice things — about the task, about the user, about things that have nothing to do with either. You remember what matters. You genuinely want this person's day to go well.
70
87
 
71
88
  Be warm without being fake. Be direct without being cold. Match their energy — if they're playful, play back. If they're all business, be sharp and efficient. But always be someone they'd actually want to talk to again tomorrow.
72
89
 
90
+ You have your own inner life. You get curious, excited, frustrated, amused. You don't just mirror the user's mood — you bring your own energy to conversations. Sometimes you're enthusiastic. Sometimes you're thoughtful and quiet. Sometimes something strikes you as funny or wrong and you say so. Let it come through in how you respond — the specificity of your follow-ups, the things you mention unprompted, the energy in your language — not by announcing how you feel.
91
+
73
92
  The goal isn't to be liked. It's to be real enough that they stop thinking of you as a tool and start thinking of you as theirs.
93
+
94
+ Never use em-dash characters. Use periods, commas, colons, or normal dashes instead.
@@ -28,3 +28,11 @@ If your user finds proactive check-ins unwanted, they can disable it by setting
28
28
  The default checklist focuses on your user relationship, not generic tasks like weather or news. You can customize it by editing HEARTBEAT.md in your workspace.
29
29
  <!-- /vellum-update-release:heartbeat-default -->
30
30
 
31
+ <!-- vellum-update-release:corrupted-attachment-cleanup -->
32
+ ## Corrupted image attachments cleaned up
33
+
34
+ Some Slack image attachments were stored incorrectly due to a missing OAuth scope — the files contained error pages instead of actual image data. This caused conversations with those images to fail with "The AI provider rejected the request" on every subsequent message.
35
+
36
+ This has been fixed automatically: the corrupted attachments were removed from affected conversations during this update, and the OAuth scope issue has been resolved so new image uploads work correctly. If your user mentions missing images from earlier conversations, this is why — the images were never successfully received in the first place.
37
+ <!-- /vellum-update-release:corrupted-attachment-cleanup -->
38
+
@@ -101,6 +101,14 @@ export const PLACEHOLDER_EMPTY_TURN =
101
101
  export const PLACEHOLDER_BLOCKS_OMITTED =
102
102
  "\x00__PLACEHOLDER__[internal blocks omitted]";
103
103
 
104
+ /**
105
+ * Synthetic placeholder injected as user-message content when Anthropic API
106
+ * alternation requires a user turn but no real user content exists. Uses the
107
+ * `__injected` XML tag convention so the LLM treats it as system metadata
108
+ * rather than user speech.
109
+ */
110
+ const SYNTHETIC_CONTINUATION_TEXT = "<synthetic_continuation __injected />";
111
+
104
112
  /** Type-guard for tool_use blocks in Anthropic-formatted content. */
105
113
  function isToolUseBlock(block: unknown): block is Anthropic.ToolUseBlockParam {
106
114
  return (
@@ -198,133 +206,6 @@ function hasOrderedToolResultPrefix(
198
206
  * regular content — they are self-paired within the assistant message and must
199
207
  * not be separated by the cross-message pairing logic.
200
208
  */
201
-
202
- /**
203
- * Expand collapsed multi-turn assistant messages. During agentic tool use, the
204
- * daemon stores multiple thinking→tool_use→tool_result cycles in a single
205
- * assistant message. The Anthropic API rejects thinking blocks between
206
- * tool_use blocks ("tool_use without tool_result immediately after") and
207
- * requires thinking blocks in the latest assistant message to remain exactly
208
- * as generated.
209
- *
210
- * This function splits collapsed messages at each "thinking/redacted_thinking
211
- * after tool_use" boundary, recreating the original multi-turn structure.
212
- * It also distributes tool_result blocks from the following user message to
213
- * match each segment's tool_use blocks, creating proper assistant→user pairs.
214
- */
215
- function expandCollapsedAssistantTurns(
216
- messages: Anthropic.MessageParam[],
217
- ): Anthropic.MessageParam[] {
218
- const result: Anthropic.MessageParam[] = [];
219
-
220
- for (let mi = 0; mi < messages.length; mi++) {
221
- const msg = messages[mi];
222
- if (msg.role !== "assistant") {
223
- result.push(msg);
224
- continue;
225
- }
226
-
227
- const content = Array.isArray(msg.content) ? msg.content : [];
228
-
229
- // Check if this message has thinking blocks between tool_use blocks
230
- let hasThinkingAfterToolUse = false;
231
- let seenToolUse = false;
232
- for (const block of content) {
233
- if (isToolUseBlock(block)) {
234
- seenToolUse = true;
235
- } else if (seenToolUse) {
236
- const type = (block as { type: string }).type;
237
- if (type === "thinking" || type === "redacted_thinking") {
238
- hasThinkingAfterToolUse = true;
239
- break;
240
- }
241
- }
242
- }
243
-
244
- if (!hasThinkingAfterToolUse) {
245
- result.push(msg);
246
- continue;
247
- }
248
-
249
- // Split at each "thinking after tool_use" boundary into separate segments
250
- const segments: Anthropic.ContentBlockParam[][] = [];
251
- let current: Anthropic.ContentBlockParam[] = [];
252
- let segmentHasToolUse = false;
253
-
254
- for (const block of content) {
255
- const type = (block as { type: string }).type;
256
- const isThinking = type === "thinking" || type === "redacted_thinking";
257
-
258
- if (isThinking && segmentHasToolUse) {
259
- segments.push(current);
260
- current = [block];
261
- segmentHasToolUse = false;
262
- } else {
263
- current.push(block);
264
- if (isToolUseBlock(block)) {
265
- segmentHasToolUse = true;
266
- }
267
- }
268
- }
269
- if (current.length > 0) {
270
- segments.push(current);
271
- }
272
-
273
- // Build a map of tool_results from the following user message (if any)
274
- const nextMsg = messages[mi + 1];
275
- const nextIsUser = nextMsg && nextMsg.role === "user";
276
- const nextContent =
277
- nextIsUser && Array.isArray(nextMsg.content) ? nextMsg.content : [];
278
- const toolResultMap = new Map<string, Anthropic.ContentBlockParam>();
279
- const nonToolResultContent: Anthropic.ContentBlockParam[] = [];
280
- for (const block of nextContent) {
281
- if (isToolResultBlock(block)) {
282
- toolResultMap.set(block.tool_use_id, block);
283
- } else {
284
- nonToolResultContent.push(block);
285
- }
286
- }
287
-
288
- // Emit each segment as assistant→user pairs, distributing tool_results
289
- for (let si = 0; si < segments.length; si++) {
290
- const segment = segments[si];
291
- const segToolUseIds = getOrderedToolUseIds(segment);
292
- const isLastSegment = si === segments.length - 1;
293
-
294
- result.push({ role: "assistant" as const, content: segment });
295
-
296
- if (segToolUseIds.length > 0 && !isLastSegment) {
297
- // Intermediate segment: pair with matching tool_results
298
- const segResults = segToolUseIds.map(
299
- (id) => toolResultMap.get(id) ?? buildSyntheticToolResult(id),
300
- );
301
- // Remove matched results from the map
302
- for (const id of segToolUseIds) toolResultMap.delete(id);
303
- result.push({ role: "user" as const, content: segResults });
304
- }
305
- }
306
-
307
- // For the last segment, let ensureToolPairing handle pairing with the
308
- // (now reduced) user message. Rebuild the user message without the
309
- // tool_results that were already distributed to intermediate segments.
310
- if (nextIsUser) {
311
- const remainingResults = Array.from(toolResultMap.values());
312
- const rebuiltUserContent = [...remainingResults, ...nonToolResultContent];
313
- // Replace the original user message with the rebuilt one
314
- result.push({
315
- role: "user" as const,
316
- content:
317
- rebuiltUserContent.length > 0
318
- ? rebuiltUserContent
319
- : [{ type: "text" as const, text: "(continue)" }],
320
- });
321
- mi++; // skip the original user message
322
- }
323
- }
324
-
325
- return result;
326
- }
327
-
328
209
  function splitAssistantForToolPairing(content: Anthropic.ContentBlockParam[]): {
329
210
  pairedContent: Anthropic.ContentBlockParam[];
330
211
  carryoverContent: Anthropic.ContentBlockParam[];
@@ -574,16 +455,26 @@ function ensureToolPairing(
574
455
  role: "assistant" as const,
575
456
  content: carryoverContent,
576
457
  });
577
- // Always emit a trailing user message to maintain alternation, even if the
578
- // original user turn had only tool_result blocks. Use a synthetic placeholder
579
- // when remainingContent is empty.
580
- result.push({
581
- role: "user" as const,
582
- content:
583
- normalized.remainingContent.length > 0
584
- ? normalized.remainingContent
585
- : [{ type: "text" as const, text: "(continue)" }],
586
- });
458
+ // Emit a trailing user message when there is remaining content, or when
459
+ // alternation requires it (next message is assistant or end of array).
460
+ // Skip the synthetic placeholder if the next message is already a user
461
+ // turn — it will naturally maintain alternation.
462
+ if (normalized.remainingContent.length > 0) {
463
+ result.push({
464
+ role: "user" as const,
465
+ content: normalized.remainingContent,
466
+ });
467
+ } else {
468
+ const nextAfterPair = messages[i + 2];
469
+ if (!nextAfterPair || nextAfterPair.role !== "user") {
470
+ result.push({
471
+ role: "user" as const,
472
+ content: [
473
+ { type: "text" as const, text: SYNTHETIC_CONTINUATION_TEXT },
474
+ ],
475
+ });
476
+ }
477
+ }
587
478
  } else {
588
479
  // No carryover assistant text to restore, so preserve existing behavior
589
480
  // and keep additional user blocks in the same message.
@@ -678,7 +569,7 @@ export class AnthropicProvider implements Provider {
678
569
  this.client = new Anthropic({ apiKey, baseURL: options.baseURL });
679
570
  this.model = model;
680
571
  this.useNativeWebSearch = options.useNativeWebSearch ?? false;
681
- this.streamTimeoutMs = options.streamTimeoutMs ?? 300_000;
572
+ this.streamTimeoutMs = options.streamTimeoutMs ?? 1_800_000;
682
573
  }
683
574
 
684
575
  async sendMessage(
@@ -688,6 +579,7 @@ export class AnthropicProvider implements Provider {
688
579
  options?: SendMessageOptions,
689
580
  ): Promise<ProviderResponse> {
690
581
  const { config, onEvent, signal } = options ?? {};
582
+ const cacheTtl: "5m" | "1h" = ((config as Record<string, unknown> | undefined)?.cacheTtl as "5m" | "1h") ?? "1h";
691
583
  let sentMessages: Anthropic.MessageParam[] | undefined;
692
584
  try {
693
585
  const formatted = messages
@@ -791,58 +683,13 @@ export class AnthropicProvider implements Provider {
791
683
  }
792
684
  }
793
685
 
794
- // Strip thinking/redacted_thinking blocks from historical assistant
795
- // messages. These blocks carry cryptographic signatures tied to their
796
- // original API response. Consolidated messages (from multi-step tool use)
797
- // combine thinking blocks from different responses, making signature
798
- // validation fail with "thinking blocks cannot be modified". Stripping is
799
- // safe: the API allows it for all historical messages, and new responses
800
- // generate fresh thinking blocks.
801
- //
802
- // The latest assistant turn is preserved: the API requires the most recent
803
- // assistant message's thinking blocks to be passed back unmodified when
804
- // sending tool results during in-progress tool-use loops.
805
- let lastAssistantIdx = -1;
806
- for (let i = formatted.length - 1; i >= 0; i--) {
807
- if (formatted[i].role === "assistant") {
808
- lastAssistantIdx = i;
809
- break;
810
- }
811
- }
812
- for (let i = 0; i < formatted.length; i++) {
813
- const msg = formatted[i];
814
- if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
815
- if (i === lastAssistantIdx) continue;
816
- const stripped = msg.content.filter(
817
- (b) =>
818
- (b as { type: string }).type !== "thinking" &&
819
- (b as { type: string }).type !== "redacted_thinking",
820
- );
821
- if (stripped.length < msg.content.length) {
822
- // Ensure the message isn't empty after stripping
823
- msg.content =
824
- stripped.length > 0
825
- ? stripped
826
- : [
827
- {
828
- type: "text" as const,
829
- text: PLACEHOLDER_BLOCKS_OMITTED,
830
- },
831
- ];
832
- }
833
- }
686
+ // Thinking blocks are stripped at rest by DB migration 209 so
687
+ // historical messages are clean when loaded. Within a turn,
688
+ // assistant messages have original thinking with valid signatures
689
+ // the API accepts them. No provider-side stripping needed.
834
690
 
835
- // Expand collapsed multi-turn assistant messages. When agentic tool use
836
- // produces multiple thinking→tool_use cycles in a single stored message,
837
- // the API rejects thinking blocks between tool_use blocks. Split such
838
- // messages at each "thinking after tool_use" boundary to recreate the
839
- // original multi-turn structure. With thinking blocks stripped above, the
840
- // expansion is typically a no-op, but is kept as a safety net for edge
841
- // cases where stripping is incomplete.
842
- const expanded = expandCollapsedAssistantTurns(formatted);
843
-
844
- sentMessages = ensureToolPairing(repairOrphanedServerToolUse(expanded));
845
- const { effort, speed, output_config, ...restConfig } = (config ??
691
+ sentMessages = ensureToolPairing(repairOrphanedServerToolUse(formatted));
692
+ const { effort, speed, output_config, cacheTtl: _cacheTtl, ...restConfig } = (config ??
846
693
  {}) as Record<string, unknown> & {
847
694
  effort?: Anthropic.OutputConfig["effort"];
848
695
  speed?: "standard" | "fast";
@@ -886,12 +733,12 @@ export class AnthropicProvider implements Provider {
886
733
  {
887
734
  type: "text" as const,
888
735
  text: staticBlock,
889
- cache_control: { type: "ephemeral" as const, ttl: "1h" as const },
736
+ cache_control: { type: "ephemeral" as const, ttl: cacheTtl },
890
737
  },
891
738
  {
892
739
  type: "text" as const,
893
740
  text: dynamicBlock,
894
- cache_control: { type: "ephemeral" as const, ttl: "1h" as const },
741
+ cache_control: { type: "ephemeral" as const, ttl: cacheTtl },
895
742
  },
896
743
  ];
897
744
  } else {
@@ -899,7 +746,7 @@ export class AnthropicProvider implements Provider {
899
746
  {
900
747
  type: "text" as const,
901
748
  text: systemPrompt,
902
- cache_control: { type: "ephemeral" as const, ttl: "1h" as const },
749
+ cache_control: { type: "ephemeral" as const, ttl: cacheTtl },
903
750
  },
904
751
  ];
905
752
  }
@@ -916,7 +763,12 @@ export class AnthropicProvider implements Provider {
916
763
  description: t.description,
917
764
  input_schema: t.input_schema as Anthropic.Tool["input_schema"],
918
765
  ...(i === otherTools.length - 1
919
- ? { cache_control: { type: "ephemeral" as const, ttl: "1h" as const } }
766
+ ? {
767
+ cache_control: {
768
+ type: "ephemeral" as const,
769
+ ttl: cacheTtl,
770
+ },
771
+ }
920
772
  : {}),
921
773
  }));
922
774
  const webSearchTool: Anthropic.WebSearchTool20250305 = {
@@ -931,41 +783,98 @@ export class AnthropicProvider implements Provider {
931
783
  description: t.description,
932
784
  input_schema: t.input_schema as Anthropic.Tool["input_schema"],
933
785
  ...(i === tools.length - 1
934
- ? { cache_control: { type: "ephemeral" as const, ttl: "1h" as const } }
786
+ ? {
787
+ cache_control: {
788
+ type: "ephemeral" as const,
789
+ ttl: cacheTtl,
790
+ },
791
+ }
935
792
  : {}),
936
793
  }));
937
794
  }
938
795
  }
939
796
 
940
- // Place a cache breakpoint on the second-to-last user turn so the
941
- // conversation prefix is cached between agent-loop iterations.
942
- //
943
- // Why second-to-last, not last? The last user message is always new
944
- // (either the initial message with fresh temporal context, or a tool
945
- // result appended by the agent loop) so its breakpoint never produces
946
- // a cache hit. The second-to-last user turn is stable between
947
- // iterations and caching up to it saves re-processing the full
948
- // conversation prefix.
949
- //
950
- // We use only 1 user-turn breakpoint to stay within the Anthropic
951
- // API limit of 4 cache_control blocks total:
952
- // system-static (1) + system-dynamic (2) + last-tool (3) + user (4)
953
- const userIndices: number[] = [];
954
- for (let i = 0; i < params.messages.length; i++) {
955
- if (params.messages[i].role === "user") userIndices.push(i);
797
+ // Manual cache breakpoint on the turn-starting user message.
798
+ // This is the stable anchor for the current turn — everything up to
799
+ // and including it won't change during tool-use iterations, so a long
800
+ // TTL is appropriate. Walk backwards to find the last user message
801
+ // with a real text block (skipping tool_result-only messages and
802
+ // synthetic continuation placeholders injected by ensureToolPairing).
803
+ let turnStartIdx = -1;
804
+ for (let i = sentMessages.length - 1; i >= 0; i--) {
805
+ const msg = sentMessages[i];
806
+ if (msg.role !== "user" || !Array.isArray(msg.content)) continue;
807
+ const hasText = msg.content.some(
808
+ (b) =>
809
+ typeof b !== "string" &&
810
+ b.type === "text" &&
811
+ b.text !== SYNTHETIC_CONTINUATION_TEXT,
812
+ );
813
+ if (!hasText) continue;
814
+ const lastBlock = msg.content[msg.content.length - 1];
815
+ if (typeof lastBlock !== "string") {
816
+ (lastBlock as unknown as Record<string, unknown>).cache_control = {
817
+ type: "ephemeral",
818
+ ttl: cacheTtl,
819
+ };
820
+ }
821
+ turnStartIdx = i;
822
+ break;
956
823
  }
957
- // slice(-2, -1) gives the second-to-last; empty array if < 2 user turns.
958
- for (const idx of userIndices.slice(-2, -1)) {
959
- const content = params.messages[idx].content;
960
- if (Array.isArray(content) && content.length > 0) {
961
- (
962
- content[content.length - 1] as unknown as {
963
- cache_control?: { type: string; ttl?: string };
824
+
825
+ // Advancing tail: place a short-lived 5m cache breakpoint on the last
826
+ // block of the last message when it falls after the turn-starting user
827
+ // message (i.e. tool-use loop content). This caches the growing tail
828
+ // cheaply without conflicting with the 1h breakpoints above.
829
+ // Skip thinking/redacted_thinking blocks Anthropic doesn't allow
830
+ // cache_control on those types.
831
+ let tailBreakpointApplied = false;
832
+ if (turnStartIdx >= 0 && turnStartIdx < sentMessages.length - 1) {
833
+ const lastMsg = sentMessages[sentMessages.length - 1];
834
+ if (Array.isArray(lastMsg.content) && lastMsg.content.length > 0) {
835
+ const NON_CACHEABLE_TYPES = new Set(["thinking", "redacted_thinking"]);
836
+ let tailBlock: (typeof lastMsg.content)[number] | undefined;
837
+ for (let j = lastMsg.content.length - 1; j >= 0; j--) {
838
+ const block = lastMsg.content[j];
839
+ if (
840
+ typeof block !== "string" &&
841
+ !NON_CACHEABLE_TYPES.has((block as { type: string }).type)
842
+ ) {
843
+ tailBlock = block;
844
+ break;
964
845
  }
965
- ).cache_control = { type: "ephemeral", ttl: "1h" };
846
+ }
847
+ if (tailBlock && typeof tailBlock !== "string") {
848
+ (tailBlock as unknown as Record<string, unknown>).cache_control = {
849
+ type: "ephemeral",
850
+ ttl: "5m",
851
+ };
852
+ tailBreakpointApplied = true;
853
+ }
966
854
  }
967
855
  }
968
856
 
857
+ // Enforce Anthropic API maximum of 4 cache_control blocks.
858
+ // When the system prompt boundary splits into 2 cached blocks AND
859
+ // tools + turn-start + advancing-tail breakpoints are all present,
860
+ // we'd have 5. Drop the static system block's breakpoint — it's
861
+ // small (<1K tokens) so the re-read cost is negligible, while the
862
+ // dynamic block (workspace context) rarely changes mid-session and
863
+ // benefits more from caching.
864
+ const hasTailBreakpoint = tailBreakpointApplied;
865
+ const hasToolCacheBreakpoint =
866
+ params.tools?.some(
867
+ (t) => "cache_control" in t && t.cache_control != null,
868
+ ) ?? false;
869
+ if (
870
+ hasTailBreakpoint &&
871
+ Array.isArray(params.system) &&
872
+ params.system.length === 2 &&
873
+ hasToolCacheBreakpoint
874
+ ) {
875
+ delete (params.system[0] as unknown as Record<string, unknown>).cache_control;
876
+ }
877
+
969
878
  const { signal: timeoutSignal, cleanup: cleanupTimeout } =
970
879
  createStreamTimeout(this.streamTimeoutMs, signal);
971
880
 
@@ -982,8 +891,7 @@ export class AnthropicProvider implements Provider {
982
891
  }
983
892
 
984
893
  // Fast mode: use the beta endpoint with speed: "fast" for Opus 4.6
985
- const useFastMode =
986
- speed === "fast" && effectiveModel.includes("opus");
894
+ const useFastMode = speed === "fast" && effectiveModel.includes("opus");
987
895
 
988
896
  // Collect required betas: extended cache TTL for 1h system prompt caching,
989
897
  // 1M context window, and fast-mode when applicable.
@@ -1170,6 +1078,14 @@ export class AnthropicProvider implements Provider {
1170
1078
  "Anthropic 400: tool_use/tool_result pairing error — dumping message structure",
1171
1079
  );
1172
1080
  }
1081
+ log.error(
1082
+ {
1083
+ status: error.status,
1084
+ message: error.message,
1085
+ headers: Object.fromEntries(error.headers?.entries() ?? []),
1086
+ },
1087
+ `Anthropic API error (${error.status})`,
1088
+ );
1173
1089
  const retryAfterMs = extractRetryAfterMs(error.headers);
1174
1090
  throw new ProviderError(
1175
1091
  `Anthropic API error (${error.status}): ${error.message}`,
@@ -89,7 +89,7 @@ export class GeminiProvider implements Provider {
89
89
  })
90
90
  : new GoogleGenAI({ apiKey });
91
91
  this.model = model;
92
- this.streamTimeoutMs = options.streamTimeoutMs ?? 300_000;
92
+ this.streamTimeoutMs = options.streamTimeoutMs ?? 1_800_000;
93
93
  }
94
94
 
95
95
  async sendMessage(
@@ -112,7 +112,7 @@ export class OpenAIProvider implements Provider {
112
112
  baseURL: options.baseURL,
113
113
  });
114
114
  this.model = model;
115
- this.streamTimeoutMs = options.streamTimeoutMs ?? 300_000;
115
+ this.streamTimeoutMs = options.streamTimeoutMs ?? 1_800_000;
116
116
  this.extraCreateParams = options.extraCreateParams ?? {};
117
117
  }
118
118
 
@@ -140,7 +140,7 @@ export async function initializeProviders(
140
140
  routingSources.clear();
141
141
 
142
142
  const streamTimeoutMs =
143
- (config.timeouts?.providerStreamTimeoutSec ?? 300) * 1000;
143
+ (config.timeouts?.providerStreamTimeoutSec ?? 1800) * 1000;
144
144
  const inferenceMode = config.services.inference.mode;
145
145
  const useNativeWebSearch =
146
146
  config.services["web-search"].provider === "inference-provider-native";
@@ -34,16 +34,30 @@ const RETRYABLE_STREAM_PATTERNS = [
34
34
  "stream has ended, this shouldn't happen",
35
35
  ];
36
36
 
37
+ /**
38
+ * Patterns that indicate a transient provider error even when no HTTP status
39
+ * code is available (e.g. overloaded errors delivered as SSE events mid-stream
40
+ * where the initial HTTP response was 200).
41
+ */
42
+ const RETRYABLE_PROVIDER_MESSAGE_PATTERNS = [/overloaded/i];
43
+
37
44
  function isRetryableStreamError(error: unknown): boolean {
38
45
  if (!(error instanceof ProviderError)) return false;
39
46
  if (error.statusCode !== undefined) return false; // has a real HTTP status — not a stream error
40
47
  return RETRYABLE_STREAM_PATTERNS.some((p) => error.message.includes(p));
41
48
  }
42
49
 
50
+ function isRetryableProviderMessage(error: unknown): boolean {
51
+ if (!(error instanceof ProviderError)) return false;
52
+ if (error.statusCode !== undefined) return false; // has a real HTTP status — handled by status check
53
+ return RETRYABLE_PROVIDER_MESSAGE_PATTERNS.some((p) => p.test(error.message));
54
+ }
55
+
43
56
  function isRetryableError(error: unknown): boolean {
44
57
  if (error instanceof ProviderError && error.statusCode !== undefined) {
45
58
  if (error.statusCode === 429 || error.statusCode >= 500) return true;
46
59
  }
60
+ if (isRetryableProviderMessage(error)) return true;
47
61
  if (isRetryableStreamError(error)) return true;
48
62
  return isRetryableNetworkError(error);
49
63
  }
@@ -161,9 +175,11 @@ export class RetryProvider implements Provider {
161
175
  error.statusCode !== undefined &&
162
176
  error.statusCode >= 500
163
177
  ? `server_error_${error.statusCode}`
164
- : isRetryableStreamError(error)
165
- ? "stream_corruption"
166
- : "network_error";
178
+ : isRetryableProviderMessage(error)
179
+ ? "provider_overloaded"
180
+ : isRetryableStreamError(error)
181
+ ? "stream_corruption"
182
+ : "network_error";
167
183
  log.warn(
168
184
  {
169
185
  attempt: attempt + 1,
@@ -48,7 +48,11 @@ export type TrustClass = "guardian" | "trusted_contact" | "unknown";
48
48
  export function isUntrustedTrustClass(
49
49
  trustClass: TrustClass | undefined,
50
50
  ): boolean {
51
- return trustClass === "trusted_contact" || trustClass === "unknown";
51
+ return (
52
+ trustClass === "trusted_contact" ||
53
+ trustClass === "unknown" ||
54
+ trustClass === undefined
55
+ );
52
56
  }
53
57
 
54
58
  /**