@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
@@ -11,6 +11,8 @@
11
11
  * results with is_valid flag and detailed error descriptions.
12
12
  */
13
13
 
14
+ import { createReadStream } from "node:fs";
15
+ import { Readable } from "node:stream";
14
16
  import { Database } from "bun:sqlite";
15
17
 
16
18
  import { z } from "zod";
@@ -27,7 +29,7 @@ import {
27
29
  } from "../../util/platform.js";
28
30
  import { httpError } from "../http-errors.js";
29
31
  import type { RouteDefinition } from "../http-router.js";
30
- import { buildExportVBundle } from "../migrations/vbundle-builder.js";
32
+ import { streamExportVBundle } from "../migrations/vbundle-builder.js";
31
33
  import {
32
34
  analyzeImport,
33
35
  DefaultPathResolver,
@@ -141,8 +143,10 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
141
143
  }
142
144
  }
143
145
 
146
+ let cleanup: (() => Promise<void>) | undefined;
147
+
144
148
  try {
145
- const { archive, manifest } = buildExportVBundle({
149
+ const result = await streamExportVBundle({
146
150
  // hooksDir is intentionally omitted — hooks now live under workspace/hooks/
147
151
  // and are included in the workspace walk. Passing hooksDir separately would
148
152
  // export them twice (once as workspace/hooks/... and again as hooks/...).
@@ -170,25 +174,32 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
170
174
  },
171
175
  });
172
176
 
177
+ cleanup = result.cleanup;
178
+ const { tempPath, size, manifest } = result;
179
+
173
180
  const timestamp = manifest.created_at.replace(/[:.]/g, "-");
174
181
  const filename = `export-${timestamp}.vbundle`;
175
182
 
176
- const body = archive.buffer.slice(
177
- archive.byteOffset,
178
- archive.byteOffset + archive.byteLength,
179
- ) as ArrayBuffer;
183
+ const fileStream = createReadStream(tempPath);
184
+ fileStream.on("close", () => {
185
+ cleanup?.();
186
+ cleanup = undefined;
187
+ });
188
+
189
+ const body = Readable.toWeb(fileStream) as unknown as ReadableStream;
180
190
 
181
191
  return new Response(body, {
182
192
  status: 200,
183
193
  headers: {
184
194
  "Content-Type": "application/octet-stream",
185
195
  "Content-Disposition": `attachment; filename="${filename}"`,
186
- "Content-Length": String(archive.length),
196
+ "Content-Length": String(size),
187
197
  "X-Vbundle-Schema-Version": manifest.schema_version,
188
198
  "X-Vbundle-Manifest-Sha256": manifest.manifest_sha256,
189
199
  },
190
200
  });
191
201
  } catch (err) {
202
+ await cleanup?.();
192
203
  log.error({ err }, "Failed to build export bundle");
193
204
  return httpError(
194
205
  "INTERNAL_ERROR",
@@ -0,0 +1,350 @@
1
+ /**
2
+ * HTTP route handlers for managed profiler run management.
3
+ *
4
+ * Control-plane callers (proxied via vembda) can enumerate, inspect, export,
5
+ * and delete completed profiler runs without opening a shell on the assistant
6
+ * pod.
7
+ *
8
+ * Routes:
9
+ * GET /v1/profiler/runs — list all profiler runs
10
+ * GET /v1/profiler/runs/:runId — detail for a single run
11
+ * POST /v1/profiler/runs/:runId/export — tar.gz export of a single run
12
+ * DELETE /v1/profiler/runs/:runId — delete a completed run
13
+ */
14
+
15
+ import {
16
+ existsSync,
17
+ lstatSync,
18
+ mkdirSync,
19
+ mkdtempSync,
20
+ readdirSync,
21
+ readFileSync,
22
+ rmSync,
23
+ writeFileSync,
24
+ } from "node:fs";
25
+ import { tmpdir } from "node:os";
26
+ import { join } from "node:path";
27
+
28
+ import { z } from "zod";
29
+
30
+ import {
31
+ getProfilerMaxBytes,
32
+ getProfilerRunId,
33
+ } from "../../config/env-registry.js";
34
+ import {
35
+ rescanRuns,
36
+ runProfilerSweep,
37
+ } from "../../daemon/profiler-run-store.js";
38
+ import { getLogger } from "../../util/logger.js";
39
+ import { getProfilerRunDir } from "../../util/platform.js";
40
+ import { httpError } from "../http-errors.js";
41
+ import type { RouteDefinition } from "../http-router.js";
42
+ import { createTarGz, MAX_ARCHIVE_BYTES } from "./archive-utils.js";
43
+
44
+ const log = getLogger("profiler-routes");
45
+
46
+ // ── Helpers ────────────────────────────────────────────────────────────
47
+
48
+ /**
49
+ * Read the Bun-generated profiler markdown summary from a run directory.
50
+ * Bun writes CPU profile summaries as `.md` files; we look for the first
51
+ * markdown file in the run directory.
52
+ */
53
+ function readProfileSummary(runDir: string): string | undefined {
54
+ try {
55
+ const entries = readdirSync(runDir);
56
+ const mdFile = entries.find((e) => e.endsWith(".md"));
57
+ if (!mdFile) return undefined;
58
+ return readFileSync(join(runDir, mdFile), "utf-8");
59
+ } catch {
60
+ return undefined;
61
+ }
62
+ }
63
+
64
+ // ── Route handlers ─────────────────────────────────────────────────────
65
+
66
+ /**
67
+ * GET /v1/profiler/runs — list all profiler runs with manifest metadata.
68
+ */
69
+ function handleListRuns(): Response {
70
+ const manifests = rescanRuns({ readOnly: true });
71
+
72
+ // Sort newest-first for the listing
73
+ manifests.sort(
74
+ (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
75
+ );
76
+
77
+ return Response.json({
78
+ runs: manifests,
79
+ totalRuns: manifests.length,
80
+ activeRunId: getProfilerRunId() ?? null,
81
+ });
82
+ }
83
+
84
+ /** Default max total bytes across all completed runs: 500 MB */
85
+ const DEFAULT_MAX_BYTES = 500 * 1024 * 1024;
86
+
87
+ /**
88
+ * GET /v1/profiler/runs/:runId — detail view with manifest + markdown summary
89
+ * plus current budget/retention state.
90
+ */
91
+ function handleGetRun(runId: string): Response {
92
+ if (runId.includes("..") || runId.includes("/") || runId.includes("\\")) {
93
+ return httpError("BAD_REQUEST", "Invalid run ID", 400);
94
+ }
95
+
96
+ // Rescan all runs first to get freshly computed totalBytes for every run,
97
+ // then find the target run in the results. This avoids using a stale
98
+ // manifest from the last write-through rescan.
99
+ const allManifests = rescanRuns({ readOnly: true });
100
+ const manifest = allManifests.find((m) => m.runId === runId);
101
+ if (!manifest) {
102
+ return httpError("NOT_FOUND", `Profiler run '${runId}' not found`, 404);
103
+ }
104
+
105
+ const runDir = getProfilerRunDir(runId);
106
+ const summary = readProfileSummary(runDir);
107
+ const activeRunId = getProfilerRunId();
108
+
109
+ // Compute budget state from all runs
110
+ const maxBytes = getProfilerMaxBytes() ?? DEFAULT_MAX_BYTES;
111
+ const totalBytesAllRuns = allManifests.reduce(
112
+ (sum, m) => sum + m.totalBytes,
113
+ 0,
114
+ );
115
+ const remainingBytes = Math.max(0, maxBytes - totalBytesAllRuns);
116
+ const overBudget = totalBytesAllRuns > maxBytes;
117
+
118
+ return Response.json({
119
+ ...manifest,
120
+ summary: summary ?? null,
121
+ isActive: runId === activeRunId,
122
+ budget: {
123
+ maxBytes,
124
+ totalBytesAllRuns,
125
+ remainingBytes,
126
+ overBudget,
127
+ },
128
+ });
129
+ }
130
+
131
+ /**
132
+ * POST /v1/profiler/runs/:runId/export — package a single run as tar.gz.
133
+ */
134
+ function handleExportRun(runId: string): Response {
135
+ if (runId.includes("..") || runId.includes("/") || runId.includes("\\")) {
136
+ return httpError("BAD_REQUEST", "Invalid run ID", 400);
137
+ }
138
+
139
+ const runDir = getProfilerRunDir(runId);
140
+ if (!existsSync(runDir)) {
141
+ return httpError("NOT_FOUND", `Profiler run '${runId}' not found`, 404);
142
+ }
143
+
144
+ // Stage the run directory contents into a temp directory to avoid
145
+ // including parent path structure in the archive.
146
+ const staging = mkdtempSync(join(tmpdir(), "vellum-profiler-export-"));
147
+
148
+ try {
149
+ // Copy run directory contents into the staging area
150
+ copyDirContents(runDir, staging);
151
+
152
+ const archiveBytes = createTarGz(staging);
153
+ if (!archiveBytes) {
154
+ log.error(
155
+ { runId },
156
+ "Profiler run archive exceeds size limit or tar failed",
157
+ );
158
+ return httpError(
159
+ "INTERNAL_ERROR",
160
+ `Profiler run '${runId}' exceeds the maximum archive size of ${MAX_ARCHIVE_BYTES} bytes`,
161
+ 500,
162
+ );
163
+ }
164
+
165
+ return new Response(archiveBytes, {
166
+ status: 200,
167
+ headers: {
168
+ "Content-Type": "application/gzip",
169
+ "Content-Disposition": `attachment; filename="profiler-${runId}.tar.gz"`,
170
+ "Content-Length": String(archiveBytes.byteLength),
171
+ },
172
+ });
173
+ } catch (err) {
174
+ const message = err instanceof Error ? err.message : String(err);
175
+ log.error({ err, runId }, "Failed to export profiler run");
176
+ return httpError(
177
+ "INTERNAL_ERROR",
178
+ `Failed to export profiler run: ${message}`,
179
+ 500,
180
+ );
181
+ } finally {
182
+ try {
183
+ rmSync(staging, { recursive: true, force: true });
184
+ } catch {
185
+ // Best-effort cleanup
186
+ }
187
+ }
188
+ }
189
+
190
+ /**
191
+ * DELETE /v1/profiler/runs/:runId — delete a completed run and recalculate
192
+ * disk-budget state.
193
+ */
194
+ function handleDeleteRun(runId: string): Response {
195
+ if (runId.includes("..") || runId.includes("/") || runId.includes("\\")) {
196
+ return httpError("BAD_REQUEST", "Invalid run ID", 400);
197
+ }
198
+
199
+ const activeRunId = getProfilerRunId();
200
+ if (runId === activeRunId) {
201
+ return httpError(
202
+ "CONFLICT",
203
+ `Cannot delete the currently active profiler run '${runId}'`,
204
+ 409,
205
+ );
206
+ }
207
+
208
+ const runDir = getProfilerRunDir(runId);
209
+ if (!existsSync(runDir)) {
210
+ return httpError("NOT_FOUND", `Profiler run '${runId}' not found`, 404);
211
+ }
212
+
213
+ try {
214
+ rmSync(runDir, { recursive: true, force: true });
215
+ } catch (err) {
216
+ const message = err instanceof Error ? err.message : String(err);
217
+ log.error({ err, runId }, "Failed to delete profiler run directory");
218
+ return httpError(
219
+ "INTERNAL_ERROR",
220
+ `Failed to delete profiler run: ${message}`,
221
+ 500,
222
+ );
223
+ }
224
+
225
+ // Re-run the sweep to recompute disk-budget state after deletion
226
+ const sweepResult = runProfilerSweep();
227
+
228
+ log.info(
229
+ { runId, remainingRuns: sweepResult.remainingRuns },
230
+ "Profiler run deleted",
231
+ );
232
+
233
+ return Response.json({
234
+ deleted: true,
235
+ runId,
236
+ remainingRuns: sweepResult.remainingRuns,
237
+ activeRunOverBudget: sweepResult.activeRunOverBudget,
238
+ });
239
+ }
240
+
241
+ // ── File copying helper ────────────────────────────────────────────────
242
+
243
+ /**
244
+ * Recursively copy all files and directories from `src` into `dest`.
245
+ */
246
+ function copyDirContents(src: string, dest: string): void {
247
+ const entries = readdirSync(src);
248
+ for (const entry of entries) {
249
+ const srcPath = join(src, entry);
250
+ const destPath = join(dest, entry);
251
+ try {
252
+ const stat = lstatSync(srcPath);
253
+ // Skip symlinks for defense-in-depth — a symlink inside a run
254
+ // directory could point outside the run and exfiltrate files.
255
+ if (stat.isSymbolicLink()) continue;
256
+ if (stat.isDirectory()) {
257
+ mkdirSync(destPath, { recursive: true });
258
+ copyDirContents(srcPath, destPath);
259
+ } else if (stat.isFile()) {
260
+ const content = readFileSync(srcPath);
261
+ writeFileSync(destPath, content);
262
+ }
263
+ } catch {
264
+ // Skip unreadable entries
265
+ }
266
+ }
267
+ }
268
+
269
+ // ── Route definitions ──────────────────────────────────────────────────
270
+
271
+ export function profilerRouteDefinitions(): RouteDefinition[] {
272
+ return [
273
+ {
274
+ endpoint: "profiler/runs",
275
+ method: "GET",
276
+ policyKey: "profiler/runs",
277
+ summary: "List profiler runs",
278
+ description:
279
+ "Enumerate all profiler run directories with manifest metadata, sorted newest-first.",
280
+ tags: ["profiler"],
281
+ responseBody: z.object({
282
+ runs: z.array(
283
+ z.object({
284
+ runId: z.string(),
285
+ status: z.enum(["active", "completed"]),
286
+ createdAt: z.string(),
287
+ updatedAt: z.string(),
288
+ totalBytes: z.number(),
289
+ completedAt: z.string().optional(),
290
+ }),
291
+ ),
292
+ totalRuns: z.number(),
293
+ activeRunId: z.string().nullable(),
294
+ }),
295
+ handler: () => handleListRuns(),
296
+ },
297
+ {
298
+ endpoint: "profiler/runs/:runId",
299
+ method: "GET",
300
+ policyKey: "profiler/runs",
301
+ summary: "Get profiler run detail",
302
+ description:
303
+ "Return manifest metadata, Bun-generated markdown summary, and current retention state for a single profiler run.",
304
+ tags: ["profiler"],
305
+ responseBody: z.object({
306
+ runId: z.string(),
307
+ status: z.enum(["active", "completed"]),
308
+ createdAt: z.string(),
309
+ updatedAt: z.string(),
310
+ totalBytes: z.number(),
311
+ completedAt: z.string().optional(),
312
+ summary: z.string().nullable(),
313
+ isActive: z.boolean(),
314
+ budget: z.object({
315
+ maxBytes: z.number(),
316
+ totalBytesAllRuns: z.number(),
317
+ remainingBytes: z.number(),
318
+ overBudget: z.boolean(),
319
+ }),
320
+ }),
321
+ handler: ({ params }) => handleGetRun(params.runId),
322
+ },
323
+ {
324
+ endpoint: "profiler/runs/:runId/export",
325
+ method: "POST",
326
+ policyKey: "profiler/runs/export",
327
+ summary: "Export profiler run",
328
+ description:
329
+ "Package a single profiler run directory as a tar.gz bundle, subject to the same archive size limits used by runtime log exports.",
330
+ tags: ["profiler"],
331
+ handler: ({ params }) => handleExportRun(params.runId),
332
+ },
333
+ {
334
+ endpoint: "profiler/runs/:runId",
335
+ method: "DELETE",
336
+ policyKey: "profiler/runs",
337
+ summary: "Delete profiler run",
338
+ description:
339
+ "Delete a completed profiler run and recalculate disk-budget state. Rejects deletion of the currently active run.",
340
+ tags: ["profiler"],
341
+ responseBody: z.object({
342
+ deleted: z.boolean(),
343
+ runId: z.string(),
344
+ remainingRuns: z.number(),
345
+ activeRunOverBudget: z.boolean(),
346
+ }),
347
+ handler: ({ params }) => handleDeleteRun(params.runId),
348
+ },
349
+ ];
350
+ }
@@ -7,12 +7,14 @@
7
7
  import { z } from "zod";
8
8
 
9
9
  import { bootstrapConversation } from "../../memory/conversation-bootstrap.js";
10
+ import { getConversation } from "../../memory/conversation-crud.js";
10
11
  import {
11
12
  cancelSchedule,
12
13
  completeScheduleRun,
13
14
  createScheduleRun,
14
15
  deleteSchedule,
15
16
  describeCronExpression,
17
+ getLastScheduleConversationId,
16
18
  getSchedule,
17
19
  listSchedules,
18
20
  updateSchedule,
@@ -50,6 +52,7 @@ function handleListSchedules(): Response {
50
52
  mode: j.mode,
51
53
  status: j.status,
52
54
  routingIntent: j.routingIntent,
55
+ reuseConversation: j.reuseConversation,
53
56
  isOneShot: j.cronExpression == null,
54
57
  })),
55
58
  });
@@ -145,6 +148,7 @@ function handleUpdateSchedule(
145
148
  "mode",
146
149
  "routingIntent",
147
150
  "quiet",
151
+ "reuseConversation",
148
152
  ] as const) {
149
153
  if (key in body) {
150
154
  updates[key] = body[key];
@@ -229,6 +233,7 @@ async function handleRunScheduleNow(
229
233
  );
230
234
  const fallbackConversation = bootstrapConversation({
231
235
  source: "schedule",
236
+ groupId: "system:scheduled",
232
237
  origin: "schedule",
233
238
  systemHint: `Schedule (manual): ${schedule.name}`,
234
239
  });
@@ -238,29 +243,40 @@ async function handleRunScheduleNow(
238
243
  return handleListSchedules();
239
244
  }
240
245
 
241
- // Regular message-based schedule
242
- const conversation = bootstrapConversation({
243
- source: "schedule",
244
- origin: "schedule",
245
- systemHint: `Schedule (manual): ${schedule.name}`,
246
- });
247
- const runId = createScheduleRun(schedule.id, conversation.id);
246
+ // Regular message-based schedule — respect reuseConversation flag
247
+ const isRecurring = schedule.expression != null;
248
+ let conversationId: string | null = null;
249
+ if (schedule.reuseConversation && isRecurring) {
250
+ const lastId = getLastScheduleConversationId(schedule.id);
251
+ if (lastId && getConversation(lastId)) {
252
+ conversationId = lastId;
253
+ }
254
+ }
255
+ if (!conversationId) {
256
+ const conversation = bootstrapConversation({
257
+ source: "schedule",
258
+ groupId: "system:scheduled",
259
+ origin: "schedule",
260
+ systemHint: `Schedule (manual): ${schedule.name}`,
261
+ });
262
+ conversationId = conversation.id;
263
+ }
264
+ const runId = createScheduleRun(schedule.id, conversationId);
248
265
 
249
266
  try {
250
267
  log.info(
251
268
  {
252
269
  jobId: schedule.id,
253
270
  name: schedule.name,
254
- conversationId: conversation.id,
271
+ conversationId,
255
272
  },
256
273
  "Executing schedule manually via HTTP (run now)",
257
274
  );
258
275
  if (!sendMessageDeps) {
259
276
  throw new Error("sendMessageDeps not available for schedule execution");
260
277
  }
261
- const activeConversation = await sendMessageDeps.getOrCreateConversation(
262
- conversation.id,
263
- );
278
+ const activeConversation =
279
+ await sendMessageDeps.getOrCreateConversation(conversationId);
264
280
  await activeConversation.processMessage(
265
281
  schedule.message,
266
282
  [],
@@ -352,6 +368,7 @@ export function scheduleRouteDefinitions(deps: {
352
368
  .string()
353
369
  .describe("single_channel, multi_channel, or all_channels"),
354
370
  quiet: z.boolean(),
371
+ reuseConversation: z.boolean(),
355
372
  }),
356
373
  responseBody: z.object({
357
374
  schedules: z.array(z.unknown()).describe("Updated schedule list"),
@@ -12,11 +12,16 @@ import { join } from "node:path";
12
12
 
13
13
  import { z } from "zod";
14
14
 
15
+ import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
15
16
  import {
16
17
  getPlatformBaseUrl,
17
18
  setIngressPublicBaseUrl,
18
19
  } from "../../config/env.js";
19
- import { loadRawConfig, saveRawConfig } from "../../config/loader.js";
20
+ import {
21
+ getConfig,
22
+ loadRawConfig,
23
+ saveRawConfig,
24
+ } from "../../config/loader.js";
20
25
  import { loadSkillCatalog } from "../../config/skills.js";
21
26
  import {
22
27
  computeGatewayTarget,
@@ -36,6 +41,12 @@ import {
36
41
  generateAllowlistOptions,
37
42
  generateScopeOptions,
38
43
  } from "../../permissions/checker.js";
44
+ import {
45
+ getMode,
46
+ onModeChanged,
47
+ setAskBeforeActing,
48
+ setHostAccess,
49
+ } from "../../permissions/permission-mode-store.js";
39
50
  import { getSecureKeyAsync } from "../../security/secure-keys.js";
40
51
  import { parseToolManifestFile } from "../../skills/tool-manifest.js";
41
52
  import {
@@ -51,7 +62,7 @@ import { isSideEffectTool } from "../../tools/side-effects.js";
51
62
  import { generateAndSaveAvatar } from "../../tools/system/avatar-generator.js";
52
63
  import { pathExists } from "../../util/fs.js";
53
64
  import { getLogger } from "../../util/logger.js";
54
- import { getWorkspaceDir } from "../../util/platform.js";
65
+ import { getAvatarImagePath, getWorkspaceDir } from "../../util/platform.js";
55
66
  import { buildAssistantEvent } from "../assistant-event.js";
56
67
  import { assistantEventHub } from "../assistant-event-hub.js";
57
68
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
@@ -61,6 +72,24 @@ import { resolveWorkspacePath } from "./workspace-utils.js";
61
72
 
62
73
  const log = getLogger("settings-routes");
63
74
 
75
+ // ---------------------------------------------------------------------------
76
+ // Permission mode SSE broadcast
77
+ // ---------------------------------------------------------------------------
78
+
79
+ onModeChanged((mode) => {
80
+ assistantEventHub
81
+ .publish(
82
+ buildAssistantEvent(DAEMON_INTERNAL_ASSISTANT_ID, {
83
+ type: "permission_mode_update",
84
+ askBeforeActing: mode.askBeforeActing,
85
+ hostAccess: mode.hostAccess,
86
+ }),
87
+ )
88
+ .catch((err) => {
89
+ log.warn({ err }, "Failed to publish permission_mode_update event");
90
+ });
91
+ });
92
+
64
93
  // ---------------------------------------------------------------------------
65
94
  // Voice config
66
95
  // ---------------------------------------------------------------------------
@@ -94,12 +123,7 @@ async function handleGenerateAvatar(description: string): Promise<Response> {
94
123
  return httpError("INTERNAL_ERROR", result.content, 500);
95
124
  }
96
125
 
97
- const avatarPath = join(
98
- getWorkspaceDir(),
99
- "data",
100
- "avatar",
101
- "avatar-image.png",
102
- );
126
+ const avatarPath = getAvatarImagePath();
103
127
 
104
128
  // Notify all connected SSE clients so every macOS/iOS instance
105
129
  // reloads the avatar image immediately.
@@ -897,5 +921,68 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
897
921
  }
898
922
  },
899
923
  },
924
+
925
+ // Permission mode (GET always available; PUT gated on feature flag)
926
+ {
927
+ endpoint: "permission-mode",
928
+ method: "GET",
929
+ policyKey: "permission-mode:GET",
930
+ summary: "Get permission mode",
931
+ description:
932
+ "Return the current two-axis permission mode (askBeforeActing, hostAccess).",
933
+ tags: ["settings"],
934
+ responseBody: z.object({
935
+ askBeforeActing: z.boolean(),
936
+ hostAccess: z.boolean(),
937
+ }),
938
+ handler: () => {
939
+ const mode = getMode();
940
+ return Response.json({
941
+ askBeforeActing: mode.askBeforeActing,
942
+ hostAccess: mode.hostAccess,
943
+ });
944
+ },
945
+ },
946
+ {
947
+ endpoint: "permission-mode",
948
+ method: "PUT",
949
+ policyKey: "permission-mode",
950
+ summary: "Update permission mode",
951
+ description:
952
+ "Update the two-axis permission mode. Requires the permission-controls-v2 feature flag.",
953
+ tags: ["settings"],
954
+ requestBody: z.object({
955
+ askBeforeActing: z.boolean().optional(),
956
+ hostAccess: z.boolean().optional(),
957
+ }),
958
+ responseBody: z.object({
959
+ askBeforeActing: z.boolean(),
960
+ hostAccess: z.boolean(),
961
+ }),
962
+ handler: async ({ req }) => {
963
+ const config = getConfig();
964
+ if (!isAssistantFeatureFlagEnabled("permission-controls-v2", config)) {
965
+ return httpError("NOT_FOUND", "Not found", 404);
966
+ }
967
+
968
+ const body = (await req.json()) as {
969
+ askBeforeActing?: boolean;
970
+ hostAccess?: boolean;
971
+ };
972
+
973
+ if (typeof body.askBeforeActing === "boolean") {
974
+ setAskBeforeActing(body.askBeforeActing);
975
+ }
976
+ if (typeof body.hostAccess === "boolean") {
977
+ setHostAccess(body.hostAccess);
978
+ }
979
+
980
+ const mode = getMode();
981
+ return Response.json({
982
+ askBeforeActing: mode.askBeforeActing,
983
+ hostAccess: mode.hostAccess,
984
+ });
985
+ },
986
+ },
900
987
  ];
901
988
  }