@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
@@ -3,6 +3,11 @@ import { z } from "zod";
3
3
  import { getDataDir } from "../util/platform.js";
4
4
 
5
5
  // Re-export all domain schemas
6
+ export type { PermissionMode } from "../permissions/permission-mode.js";
7
+ export {
8
+ DEFAULT_PERMISSION_MODE,
9
+ PermissionModeSchema,
10
+ } from "../permissions/permission-mode.js";
6
11
  export type { AcpAgentConfig, AcpConfig } from "./acp-schema.js";
7
12
  export { AcpAgentConfigSchema, AcpConfigSchema } from "./acp-schema.js";
8
13
  export type {
@@ -143,6 +148,7 @@ export type {
143
148
  export {
144
149
  PermissionsConfigSchema,
145
150
  SecretDetectionConfigSchema,
151
+ VALID_PERMISSIONS_MODES,
146
152
  } from "./schemas/security.js";
147
153
  export type {
148
154
  ImageGenerationService,
@@ -197,6 +203,7 @@ import {
197
203
  WhatsAppConfigSchema,
198
204
  } from "./schemas/channels.js";
199
205
  import { ElevenLabsConfigSchema } from "./schemas/elevenlabs.js";
206
+ import { FilingConfigSchema } from "./schemas/filing.js";
200
207
  import { FishAudioConfigSchema } from "./schemas/fish-audio.js";
201
208
  import { HeartbeatConfigSchema } from "./schemas/heartbeat.js";
202
209
  import {
@@ -272,6 +279,7 @@ export const AssistantConfigSchema = z
272
279
  .describe(
273
280
  "Custom pricing overrides for specific provider/model combinations",
274
281
  ),
282
+ filing: FilingConfigSchema.default(FilingConfigSchema.parse({})),
275
283
  heartbeat: HeartbeatConfigSchema.default(HeartbeatConfigSchema.parse({})),
276
284
  journal: JournalConfigSchema.default(JournalConfigSchema.parse({})),
277
285
  mcp: McpConfigSchema.default(McpConfigSchema.parse({})),
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+
3
+ import { SpeedSchema } from "./inference.js";
4
+
5
+ export const FilingConfigSchema = z
6
+ .object({
7
+ enabled: z
8
+ .boolean({ error: "filing.enabled must be a boolean" })
9
+ .default(true)
10
+ .describe(
11
+ "Whether periodic PKB filing is enabled — processes buffer.md into topic files and reviews knowledge base organization",
12
+ ),
13
+ intervalMs: z
14
+ .number({ error: "filing.intervalMs must be a number" })
15
+ .int("filing.intervalMs must be an integer")
16
+ .positive("filing.intervalMs must be a positive integer")
17
+ .default(4 * 3_600_000)
18
+ .describe("Time between filing runs in milliseconds"),
19
+ speed: SpeedSchema.default("standard").describe(
20
+ "Inference speed mode for filing conversations",
21
+ ),
22
+ activeHoursStart: z
23
+ .number({ error: "filing.activeHoursStart must be a number" })
24
+ .int("filing.activeHoursStart must be an integer")
25
+ .min(0, "filing.activeHoursStart must be >= 0")
26
+ .max(23, "filing.activeHoursStart must be <= 23")
27
+ .default(8)
28
+ .describe("Hour of the day (0-23) when filing runs begin"),
29
+ activeHoursEnd: z
30
+ .number({ error: "filing.activeHoursEnd must be a number" })
31
+ .int("filing.activeHoursEnd must be an integer")
32
+ .min(0, "filing.activeHoursEnd must be >= 0")
33
+ .max(23, "filing.activeHoursEnd must be <= 23")
34
+ .default(22)
35
+ .describe("Hour of the day (0-23) when filing runs stop"),
36
+ })
37
+ .describe(
38
+ "Periodic PKB (personal knowledge base) filing — processes the buffer into topic files and maintains knowledge organization",
39
+ )
40
+ .superRefine((config, ctx) => {
41
+ if (config.activeHoursStart === config.activeHoursEnd) {
42
+ ctx.addIssue({
43
+ code: z.ZodIssueCode.custom,
44
+ path: ["activeHoursEnd"],
45
+ message:
46
+ "filing.activeHoursStart and filing.activeHoursEnd must not be equal (would create an empty window)",
47
+ });
48
+ }
49
+ });
50
+
51
+ export type FilingConfig = z.infer<typeof FilingConfigSchema>;
@@ -12,7 +12,7 @@ export const HeartbeatConfigSchema = z
12
12
  .number({ error: "heartbeat.intervalMs must be a number" })
13
13
  .int("heartbeat.intervalMs must be an integer")
14
14
  .positive("heartbeat.intervalMs must be a positive integer")
15
- .default(3_600_000)
15
+ .default(6 * 3_600_000)
16
16
  .describe("Time between heartbeat checks in milliseconds"),
17
17
  speed: SpeedSchema.default("standard").describe(
18
18
  "Inference speed mode for heartbeat conversations — defaults to standard to avoid inheriting the global fast mode multiplier",
@@ -22,35 +22,38 @@ export const HeartbeatConfigSchema = z
22
22
  .int("heartbeat.activeHoursStart must be an integer")
23
23
  .min(0, "heartbeat.activeHoursStart must be >= 0")
24
24
  .max(23, "heartbeat.activeHoursStart must be <= 23")
25
- .optional()
25
+ .nullable()
26
+ .default(8)
26
27
  .describe(
27
- "Hour of the day (0-23) when heartbeat checks begin must be set together with activeHoursEnd",
28
+ "Hour of the day (0-23) when heartbeat checks begin, or null to disable active hours restriction",
28
29
  ),
29
30
  activeHoursEnd: z
30
31
  .number({ error: "heartbeat.activeHoursEnd must be a number" })
31
32
  .int("heartbeat.activeHoursEnd must be an integer")
32
33
  .min(0, "heartbeat.activeHoursEnd must be >= 0")
33
34
  .max(23, "heartbeat.activeHoursEnd must be <= 23")
34
- .optional()
35
+ .nullable()
36
+ .default(22)
35
37
  .describe(
36
- "Hour of the day (0-23) when heartbeat checks stop must be set together with activeHoursStart",
38
+ "Hour of the day (0-23) when heartbeat checks stop, or null to disable active hours restriction",
37
39
  ),
38
40
  })
39
41
  .describe("Periodic heartbeat configuration for health monitoring")
40
42
  .superRefine((config, ctx) => {
41
- const hasStart = config.activeHoursStart != null;
42
- const hasEnd = config.activeHoursEnd != null;
43
- if (hasStart !== hasEnd) {
43
+ const startNull = config.activeHoursStart == null;
44
+ const endNull = config.activeHoursEnd == null;
45
+ if (startNull !== endNull) {
44
46
  ctx.addIssue({
45
47
  code: z.ZodIssueCode.custom,
46
- path: [hasStart ? "activeHoursEnd" : "activeHoursStart"],
48
+ path: [startNull ? "activeHoursStart" : "activeHoursEnd"],
47
49
  message:
48
- "heartbeat.activeHoursStart and heartbeat.activeHoursEnd must both be set or both be omitted",
50
+ "heartbeat.activeHoursStart and heartbeat.activeHoursEnd must both be set or both be null",
49
51
  });
52
+ return;
50
53
  }
51
54
  if (
52
- hasStart &&
53
- hasEnd &&
55
+ config.activeHoursStart != null &&
56
+ config.activeHoursEnd != null &&
54
57
  config.activeHoursStart === config.activeHoursEnd
55
58
  ) {
56
59
  ctx.addIssue({
@@ -72,6 +72,18 @@ export const MemoryCleanupConfigSchema = z
72
72
  .describe(
73
73
  "Number of days to retain conversation data before cleanup (0 disables pruning)",
74
74
  ),
75
+ llmRequestLogRetentionMs: z
76
+ .number({
77
+ error: "memory.cleanup.llmRequestLogRetentionMs must be a number",
78
+ })
79
+ .int("memory.cleanup.llmRequestLogRetentionMs must be an integer")
80
+ .nonnegative(
81
+ "memory.cleanup.llmRequestLogRetentionMs must be non-negative",
82
+ )
83
+ .default(7 * 24 * 60 * 60 * 1000)
84
+ .describe(
85
+ "Retention period for LLM request/response logs in milliseconds (0 disables pruning)",
86
+ ),
75
87
  })
76
88
  .describe("Automatic memory cleanup and garbage collection settings");
77
89
 
@@ -56,6 +56,14 @@ export const UiConfigSchema = z
56
56
  .describe(
57
57
  "IANA timezone identifier for displaying dates and times (e.g. 'America/New_York')",
58
58
  ),
59
+ greetingModelIntent: z
60
+ .enum(["latency-optimized", "quality-optimized"], {
61
+ error: "ui.greetingModelIntent must be 'latency-optimized' or 'quality-optimized'",
62
+ })
63
+ .default("latency-optimized")
64
+ .describe(
65
+ "Model intent for empty-state greeting generation (latency-optimized = fast/small model, quality-optimized = primary model)",
66
+ ),
59
67
  })
60
68
  .describe("User interface display settings");
61
69
 
@@ -79,6 +79,20 @@ export const PermissionsConfigSchema = z
79
79
  .describe(
80
80
  "Permission mode — 'strict' requires explicit approval for all operations, 'workspace' allows operations within the workspace",
81
81
  ),
82
+ askBeforeActing: z
83
+ .boolean({
84
+ error: "permissions.askBeforeActing must be a boolean",
85
+ })
86
+ .default(true)
87
+ .describe("Whether the assistant should check in before taking actions"),
88
+ hostAccess: z
89
+ .boolean({
90
+ error: "permissions.hostAccess must be a boolean",
91
+ })
92
+ .default(false)
93
+ .describe(
94
+ "Whether the assistant can execute commands on the host machine without prompting",
95
+ ),
82
96
  })
83
97
  .describe("Permission enforcement mode for tool operations");
84
98
 
@@ -36,7 +36,7 @@ export const TimeoutConfigSchema = z
36
36
  .number({ error: "timeouts.providerStreamTimeoutSec must be a number" })
37
37
  .finite("timeouts.providerStreamTimeoutSec must be finite")
38
38
  .positive("timeouts.providerStreamTimeoutSec must be a positive number")
39
- .default(300)
39
+ .default(1800)
40
40
  .describe(
41
41
  "Timeout for waiting on the LLM provider's streaming response (seconds)",
42
42
  ),
@@ -73,7 +73,6 @@ export interface SkillSummary {
73
73
  bundled?: boolean;
74
74
  icon?: string;
75
75
  emoji?: string;
76
- homepage?: string;
77
76
  source: SkillSource;
78
77
  /** Parsed tool manifest metadata, if the skill has a valid TOOLS.json. */
79
78
  toolManifest?: SkillToolManifestMeta;
@@ -962,8 +961,9 @@ function isEscapingSymlink(filePath: string, rootDir: string): boolean {
962
961
  /**
963
962
  * Check for a `references/` subdirectory within a skill directory and return
964
963
  * a formatted listing of available `.md` reference files with full absolute
965
- * paths. Returns `null` if no references exist. Files are listed in
966
- * alphabetical order. Non-`.md` files are ignored. Symlinks that resolve
964
+ * paths. Returns `null` if no references exist. Files are discovered
965
+ * recursively through subdirectories and listed alphabetically within each
966
+ * directory level. Non-`.md` files are ignored. Symlinks that resolve
967
967
  * outside the skill directory are skipped.
968
968
  */
969
969
  export function listReferenceFiles(directoryPath: string): string | null {
@@ -977,10 +977,21 @@ export function listReferenceFiles(directoryPath: string): string | null {
977
977
  return null;
978
978
  }
979
979
 
980
- const entries = readdirSync(refsDir);
980
+ const entries = readdirSync(refsDir, { recursive: true }) as string[];
981
981
  const mdFiles = entries
982
982
  .filter((f) => f.toLowerCase().endsWith(".md"))
983
- .filter((f) => !isEscapingSymlink(join(refsDir, f), directoryPath))
983
+ .filter((f) => {
984
+ // Check the file itself
985
+ if (isEscapingSymlink(join(refsDir, f), directoryPath)) return false;
986
+ // Check all intermediate directory components (e.g. for "sub/dir/file.md"
987
+ // check "sub" and "sub/dir") to prevent traversal through symlinked dirs.
988
+ const parts = f.split("/");
989
+ for (let i = 1; i < parts.length; i++) {
990
+ const ancestor = join(refsDir, ...parts.slice(0, i));
991
+ if (isEscapingSymlink(ancestor, directoryPath)) return false;
992
+ }
993
+ return true;
994
+ })
984
995
  .sort((a, b) => a.localeCompare(b));
985
996
 
986
997
  if (mdFiles.length === 0) return null;
@@ -991,8 +1002,8 @@ export function listReferenceFiles(directoryPath: string): string | null {
991
1002
  "The following reference files are available in this skill's directory. Use `file_read` to load any that are relevant to the current task:",
992
1003
  "",
993
1004
  ];
994
- for (const filename of mdFiles) {
995
- lines.push(`- \`${join(refsDir, filename)}\``);
1005
+ for (const filepath of mdFiles) {
1006
+ lines.push(`- \`${join(refsDir, filepath)}\` (references/${filepath})`);
996
1007
  }
997
1008
 
998
1009
  return lines.join("\n");
@@ -9,7 +9,6 @@ const CHARS_PER_TOKEN = 4;
9
9
  const MESSAGE_OVERHEAD_TOKENS = 4;
10
10
  const TEXT_BLOCK_OVERHEAD_TOKENS = 2;
11
11
  const TOOL_BLOCK_OVERHEAD_TOKENS = 16;
12
- const IMAGE_BLOCK_TOKENS = 1024;
13
12
  const IMAGE_BLOCK_OVERHEAD_TOKENS = 16;
14
13
  const FILE_BLOCK_OVERHEAD_TOKENS = 48;
15
14
  const WEB_SEARCH_RESULT_TOKENS = 800;
@@ -20,12 +19,15 @@ const GEMINI_INLINE_FILE_MIME_TYPES = new Set(["application/pdf"]);
20
19
  // Anthropic scales images to fit within 1568x1568 maintaining aspect ratio,
21
20
  // then charges ~(width * height) / 750 tokens.
22
21
  const ANTHROPIC_IMAGE_MAX_DIMENSION = 1568;
22
+ // Anthropic caps images at ~1.2 megapixels in addition to the 1568px dimension limit.
23
+ // Images exceeding this are further scaled down. The docs state images above ~1,600 tokens
24
+ // are resized. 1,200,000 / 750 = 1,600 tokens, matching the documented threshold.
25
+ // Reference table (max sizes that won't be resized):
26
+ // 1:1 → 1092x1092 (~1,590 tokens) 1:2 → 784x1568 (~1,639 tokens)
27
+ // See: https://platform.claude.com/docs/en/build-with-claude/vision#evaluate-image-size
28
+ const ANTHROPIC_IMAGE_MAX_PIXELS = 1_200_000;
23
29
  const ANTHROPIC_IMAGE_TOKENS_PER_PIXEL = 1 / 750;
24
- const ANTHROPIC_IMAGE_MAX_TOKENS = Math.ceil(
25
- ANTHROPIC_IMAGE_MAX_DIMENSION *
26
- ANTHROPIC_IMAGE_MAX_DIMENSION *
27
- ANTHROPIC_IMAGE_TOKENS_PER_PIXEL,
28
- ); // ~3,277 tokens
30
+ const ANTHROPIC_IMAGE_MAX_TOKENS = 1_600;
29
31
 
30
32
  // Anthropic renders each PDF page as an image (~1,568 tokens at standard
31
33
  // resolution) plus any extracted text. Typical PDF pages are 50-150 KB.
@@ -85,17 +87,23 @@ function estimateFileDataTokens(
85
87
  }
86
88
 
87
89
  function estimateAnthropicImageTokens(width: number, height: number): number {
88
- // Scale down to fit within 1568x1568 bounding box, maintaining aspect ratio
89
- const scale = Math.min(
90
+ // Step 1: Scale to fit within 1568px bounding box
91
+ const dimScale = Math.min(
90
92
  1,
91
93
  ANTHROPIC_IMAGE_MAX_DIMENSION / Math.max(width, height),
92
94
  );
93
- const scaledWidth = Math.round(width * scale);
94
- const scaledHeight = Math.round(height * scale);
95
- return Math.max(
96
- IMAGE_BLOCK_TOKENS, // minimum 1024
97
- Math.ceil(scaledWidth * scaledHeight * ANTHROPIC_IMAGE_TOKENS_PER_PIXEL),
98
- );
95
+ let scaledWidth = Math.round(width * dimScale);
96
+ let scaledHeight = Math.round(height * dimScale);
97
+
98
+ // Step 2: Scale further if exceeds megapixel budget
99
+ const pixels = scaledWidth * scaledHeight;
100
+ if (pixels > ANTHROPIC_IMAGE_MAX_PIXELS) {
101
+ const mpScale = Math.sqrt(ANTHROPIC_IMAGE_MAX_PIXELS / pixels);
102
+ scaledWidth = Math.round(scaledWidth * mpScale);
103
+ scaledHeight = Math.round(scaledHeight * mpScale);
104
+ }
105
+
106
+ return Math.ceil(scaledWidth * scaledHeight * ANTHROPIC_IMAGE_TOKENS_PER_PIXEL);
99
107
  }
100
108
 
101
109
  function estimateImageTokens(
@@ -143,11 +151,10 @@ export function estimateContentBlockTokens(
143
151
  return tokens;
144
152
  }
145
153
  case "image":
146
- return Math.max(
147
- IMAGE_BLOCK_TOKENS,
154
+ return (
148
155
  IMAGE_BLOCK_OVERHEAD_TOKENS +
149
- estimateTextTokens(block.source.media_type) +
150
- estimateImageTokens(block, options),
156
+ estimateTextTokens(block.source.media_type) +
157
+ estimateImageTokens(block, options)
151
158
  );
152
159
  case "file":
153
160
  return (
@@ -628,14 +628,18 @@ export class ContextWindowManager {
628
628
  const excess = totalTokens - maxTranscriptTokens;
629
629
  if (blockTokens > excess && result[i].type === "text") {
630
630
  // Truncate this block to shed exactly the excess tokens.
631
- const keepTokens = blockTokens - excess;
631
+ // Subtract the cost of the "[...truncated] " prefix so the final
632
+ // block (prefix + kept text) stays within budget.
633
+ const truncationPrefix = "[...truncated] ";
634
+ const prefixTokens = estimateTextTokens(truncationPrefix);
635
+ const keepTokens = Math.max(1, blockTokens - excess - prefixTokens);
632
636
  const text = (result[i] as { type: "text"; text: string }).text;
633
637
  // Approximate: 1 token ≈ 4 characters for truncation purposes.
634
638
  const keepChars = Math.max(1, Math.floor(keepTokens * 4));
635
639
  const truncatedText = text.slice(-keepChars);
636
640
  const truncatedBlock: ContentBlock = {
637
641
  type: "text",
638
- text: `[...truncated] ${truncatedText}`,
642
+ text: `${truncationPrefix}${truncatedText}`,
639
643
  };
640
644
  const newBlockTokens = estimateBlockTokens(truncatedBlock);
641
645
  droppedTokens += blockTokens - newBlockTokens;
@@ -31,6 +31,7 @@ import { StringDecoder } from "node:string_decoder";
31
31
  import type { Subprocess } from "bun";
32
32
 
33
33
  import type { AssistantConfig } from "../config/schema.js";
34
+ import { ensureBun } from "../util/bun-runtime.js";
34
35
  import { getLogger } from "../util/logger.js";
35
36
  import type { CesTransport } from "./client.js";
36
37
  import {
@@ -266,8 +267,9 @@ export function createCesProcessManager(
266
267
  "Spawning CES child process from source",
267
268
  );
268
269
 
270
+ const bunPath = await ensureBun();
269
271
  const proc = Bun.spawn({
270
- cmd: ["bun", "run", discovery.sourcePath],
272
+ cmd: [bunPath, "run", discovery.sourcePath],
271
273
  stdin: "pipe",
272
274
  stdout: "pipe",
273
275
  stderr: "ignore",
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Filesystem watcher for app source directories.
3
+ *
4
+ * Watches the apps root directory recursively using fs.watch (FSEvents on
5
+ * macOS). When a source file changes, debounces per app ID and calls the
6
+ * provided callback so the server can recompile + refresh surfaces.
7
+ *
8
+ * This catches ALL modification sources (file_edit, file_write, bash, etc.)
9
+ * without relying on individual tool hooks.
10
+ */
11
+
12
+ import { existsSync, type FSWatcher, watch } from "node:fs";
13
+
14
+ import {
15
+ getAppsDir,
16
+ resolveAppIdByDirName,
17
+ } from "../memory/app-store.js";
18
+ import { DebouncerMap } from "../util/debounce.js";
19
+ import { getLogger } from "../util/logger.js";
20
+
21
+ const log = getLogger("app-source-watcher");
22
+
23
+ const APP_REFRESH_DEBOUNCE_MS = 500;
24
+
25
+ export type AppSourceChangeCallback = (appId: string) => void;
26
+
27
+ /**
28
+ * Resolve app ID from a relative path within the apps directory.
29
+ * Returns null if the path is not an app source file (e.g. dist/, records/).
30
+ */
31
+ function resolveAppIdFromRelPath(relPath: string): string | null {
32
+ const slashIdx = relPath.indexOf("/");
33
+ if (slashIdx === -1) return null; // file directly in apps/ (e.g. .json definition)
34
+
35
+ const dirName = relPath.slice(0, slashIdx);
36
+ const innerPath = relPath.slice(slashIdx + 1);
37
+
38
+ // Skip non-source directories (include bare directory names for fs.watch events)
39
+ if (
40
+ innerPath === "records" || innerPath.startsWith("records/") ||
41
+ innerPath === "dist" || innerPath.startsWith("dist/")
42
+ ) {
43
+ return null;
44
+ }
45
+
46
+ return resolveAppIdByDirName(dirName);
47
+ }
48
+
49
+ export class AppSourceWatcher {
50
+ private watcher: FSWatcher | null = null;
51
+ private debouncer = new DebouncerMap({
52
+ defaultDelayMs: APP_REFRESH_DEBOUNCE_MS,
53
+ maxEntries: 50,
54
+ });
55
+
56
+ start(onChange: AppSourceChangeCallback): void {
57
+ let appsDir: string;
58
+ try {
59
+ appsDir = getAppsDir();
60
+ } catch {
61
+ log.warn("Could not resolve apps directory; app source watching disabled");
62
+ return;
63
+ }
64
+
65
+ if (!existsSync(appsDir)) {
66
+ log.info("Apps directory does not exist yet; skipping source watcher");
67
+ return;
68
+ }
69
+
70
+ try {
71
+ this.watcher = watch(appsDir, { recursive: true }, (_eventType, filename) => {
72
+ if (!filename) return;
73
+
74
+ const appId = resolveAppIdFromRelPath(filename);
75
+ if (!appId) return;
76
+
77
+ this.debouncer.schedule(`app:${appId}`, () => {
78
+ onChange(appId);
79
+ });
80
+ });
81
+ } catch (err) {
82
+ log.warn({ err }, "Failed to watch apps directory; source watching disabled");
83
+ }
84
+ }
85
+
86
+ stop(): void {
87
+ this.debouncer.cancelAll();
88
+ if (this.watcher) {
89
+ this.watcher.close();
90
+ this.watcher = null;
91
+ }
92
+ }
93
+ }
@@ -28,7 +28,10 @@ import { handleUserMessageSignal } from "../signals/user-message.js";
28
28
  import { DebouncerMap } from "../util/debounce.js";
29
29
  import { getLogger } from "../util/logger.js";
30
30
  import {
31
+ AVATAR_IMAGE_FILENAME,
32
+ getAvatarDir,
31
33
  getSignalsDir,
34
+ getSoundsDir,
32
35
  getWorkspaceDir,
33
36
  getWorkspaceSkillsDir,
34
37
  } from "../util/platform.js";
@@ -110,7 +113,12 @@ export class ConfigWatcher {
110
113
  * files change and conversations need to be evicted for reload.
111
114
  * `onIdentityChanged` is called when IDENTITY.md changes on disk.
112
115
  */
113
- start(onConversationEvict: () => void, onIdentityChanged?: () => void): void {
116
+ start(
117
+ onConversationEvict: () => void,
118
+ onIdentityChanged?: () => void,
119
+ onSoundsConfigChanged?: () => void,
120
+ onAvatarChanged?: () => void,
121
+ ): void {
114
122
  const workspaceDir = getWorkspaceDir();
115
123
 
116
124
  const workspaceHandlers: Record<string, () => void> = {
@@ -175,6 +183,13 @@ export class ConfigWatcher {
175
183
  "workspace directory for config/prompt changes",
176
184
  );
177
185
 
186
+ if (onSoundsConfigChanged) {
187
+ this.startSoundsWatcher(onSoundsConfigChanged);
188
+ }
189
+ if (onAvatarChanged) {
190
+ this.startAvatarWatcher(onAvatarChanged);
191
+ }
192
+
178
193
  this.startFeatureFlagsWatcher();
179
194
  this.startSignalsWatcher();
180
195
  this.startSkillsWatchers(onConversationEvict);
@@ -188,6 +203,69 @@ export class ConfigWatcher {
188
203
  this.watchers = [];
189
204
  }
190
205
 
206
+ private startSoundsWatcher(onSoundsConfigChanged: () => void): void {
207
+ const soundsDir = getSoundsDir();
208
+ try {
209
+ if (!existsSync(soundsDir)) {
210
+ mkdirSync(soundsDir, { recursive: true });
211
+ }
212
+ } catch {
213
+ // If we can't create it, watching will also fail — handled below.
214
+ }
215
+
216
+ try {
217
+ const watcher = watch(soundsDir, (_eventType, filename) => {
218
+ if (!filename) return;
219
+ this.debounceTimers.schedule("file:sounds", () => {
220
+ log.info(
221
+ { file: String(filename) },
222
+ "Sounds directory changed, notifying clients",
223
+ );
224
+ onSoundsConfigChanged();
225
+ });
226
+ });
227
+ this.watchers.push(watcher);
228
+ log.info({ dir: soundsDir }, "Watching sounds directory for changes");
229
+ } catch (err) {
230
+ log.warn(
231
+ { err, dir: soundsDir },
232
+ "Failed to watch sounds directory. Sound config changes will require a restart.",
233
+ );
234
+ }
235
+ }
236
+
237
+ private startAvatarWatcher(onAvatarChanged: () => void): void {
238
+ const avatarDir = getAvatarDir();
239
+ try {
240
+ if (!existsSync(avatarDir)) {
241
+ mkdirSync(avatarDir, { recursive: true });
242
+ }
243
+ } catch {
244
+ // If we can't create it, watching will also fail — handled below.
245
+ }
246
+
247
+ try {
248
+ const watcher = watch(avatarDir, (_eventType, filename) => {
249
+ if (!filename) return;
250
+ if (String(filename) !== AVATAR_IMAGE_FILENAME) return;
251
+ this.debounceTimers.schedule("file:avatar", () => {
252
+ log.info(
253
+ { file: String(filename) },
254
+ "Avatar image changed, notifying clients",
255
+ );
256
+ onAvatarChanged();
257
+ });
258
+ });
259
+ this.watchers.push(watcher);
260
+ log.info({ dir: avatarDir }, "Watching avatar directory for changes");
261
+ } catch (err) {
262
+ log.warn(
263
+ { err, dir: avatarDir },
264
+ "Failed to watch avatar directory. Avatar changes will require a restart.",
265
+ );
266
+ }
267
+ }
268
+
191
269
  private startFeatureFlagsWatcher(): void {
192
270
  const protectedDir = process.env.GATEWAY_SECURITY_DIR
193
271
  ? process.env.GATEWAY_SECURITY_DIR
@@ -17,7 +17,10 @@
17
17
  */
18
18
 
19
19
  import type { ContextWindowConfig } from "../config/types.js";
20
- import { estimatePromptTokens } from "../context/token-estimator.js";
20
+ import {
21
+ estimateContentBlockTokens,
22
+ estimatePromptTokens,
23
+ } from "../context/token-estimator.js";
21
24
  import { truncateToolResultsAcrossHistory } from "../context/tool-result-truncation.js";
22
25
  import type {
23
26
  ContextWindowCompactOptions,
@@ -26,6 +29,7 @@ import type {
26
29
  import type { Message } from "../providers/types.js";
27
30
  import {
28
31
  countMediaBlocks,
32
+ estimateUnconditionalStubTokens,
29
33
  stripMediaPayloadsForRetry,
30
34
  } from "./conversation-media-retry.js";
31
35
  import type { InjectionMode } from "./conversation-runtime-assembly.js";
@@ -240,7 +244,47 @@ function applyMediaStubbing(
240
244
  let nextMessages = messages;
241
245
 
242
246
  if (mediaCount > 0) {
243
- const stripped = stripMediaPayloadsForRetry(messages);
247
+ // Compute the token budget available for media content.
248
+ const totalTokens = estimatePromptTokens(messages, config.systemPrompt, {
249
+ providerName: config.providerName,
250
+ toolTokenBudget: config.toolTokenBudget,
251
+ });
252
+
253
+ // Sum tokens for all image and file blocks (top-level and nested in tool_result).
254
+ let mediaTokens = 0;
255
+ for (const msg of messages) {
256
+ for (const block of msg.content) {
257
+ if (block.type === "image" || block.type === "file") {
258
+ mediaTokens += estimateContentBlockTokens(block, {
259
+ providerName: config.providerName,
260
+ });
261
+ } else if (block.type === "tool_result" && block.contentBlocks) {
262
+ for (const cb of block.contentBlocks) {
263
+ if (cb.type === "image" || cb.type === "file") {
264
+ mediaTokens += estimateContentBlockTokens(cb, {
265
+ providerName: config.providerName,
266
+ });
267
+ }
268
+ }
269
+ }
270
+ }
271
+ }
272
+
273
+ const nonMediaTokens = totalTokens - mediaTokens;
274
+
275
+ // Account for the token cost of text stubs that replace unconditionally
276
+ // stubbed media (non-latest-user images/files, tool_result-nested media).
277
+ // Without this adjustment the budget is systematically over-allocated.
278
+ const estimatedStubTokens = estimateUnconditionalStubTokens(messages, {
279
+ providerName: config.providerName,
280
+ });
281
+ const adjustedNonMediaTokens = nonMediaTokens + estimatedStubTokens;
282
+ const mediaTokenBudget = Math.max(0, config.targetTokens - adjustedNonMediaTokens);
283
+
284
+ const stripped = stripMediaPayloadsForRetry(messages, {
285
+ mediaTokenBudget,
286
+ providerName: config.providerName,
287
+ });
244
288
  if (stripped.modified) {
245
289
  nextMessages = stripped.messages;
246
290
  }