@vellumai/assistant 0.5.13 → 0.5.14

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 (425) hide show
  1. package/.env.example +1 -6
  2. package/AGENTS.md +4 -0
  3. package/ARCHITECTURE.md +0 -1
  4. package/bunfig.toml +1 -0
  5. package/docs/architecture/memory.md +3 -3
  6. package/openapi.yaml +127 -22
  7. package/package.json +1 -1
  8. package/src/__tests__/access-request-decision.test.ts +2 -32
  9. package/src/__tests__/actor-token-service.test.ts +1 -31
  10. package/src/__tests__/anthropic-provider.test.ts +53 -40
  11. package/src/__tests__/app-git-history.test.ts +9 -17
  12. package/src/__tests__/app-git-service.test.ts +14 -20
  13. package/src/__tests__/app-store-dir-names.test.ts +10 -20
  14. package/src/__tests__/approval-cascade.test.ts +2 -19
  15. package/src/__tests__/approval-primitive.test.ts +2 -27
  16. package/src/__tests__/approval-routes-http.test.ts +2 -30
  17. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
  19. package/src/__tests__/attachments-store.test.ts +5 -32
  20. package/src/__tests__/audit-log-rotation.test.ts +5 -36
  21. package/src/__tests__/avatar-e2e.test.ts +1 -9
  22. package/src/__tests__/avatar-generator.test.ts +1 -7
  23. package/src/__tests__/browser-fill-credential.test.ts +0 -4
  24. package/src/__tests__/browser-manager.test.ts +0 -6
  25. package/src/__tests__/call-controller.test.ts +1 -22
  26. package/src/__tests__/call-conversation-messages.test.ts +0 -21
  27. package/src/__tests__/call-domain.test.ts +0 -25
  28. package/src/__tests__/call-pointer-messages.test.ts +0 -21
  29. package/src/__tests__/call-recovery.test.ts +0 -22
  30. package/src/__tests__/call-routes-http.test.ts +0 -24
  31. package/src/__tests__/call-store.test.ts +0 -21
  32. package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
  33. package/src/__tests__/canonical-guardian-store.test.ts +48 -21
  34. package/src/__tests__/channel-approval-routes.test.ts +6 -26
  35. package/src/__tests__/channel-approvals.test.ts +1 -38
  36. package/src/__tests__/channel-delivery-store.test.ts +0 -21
  37. package/src/__tests__/channel-guardian.test.ts +0 -26
  38. package/src/__tests__/channel-reply-delivery.test.ts +5 -0
  39. package/src/__tests__/channel-retry-sweep.test.ts +0 -21
  40. package/src/__tests__/checker.test.ts +26 -61
  41. package/src/__tests__/clawhub.test.ts +9 -25
  42. package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
  43. package/src/__tests__/config-loader-backfill.test.ts +9 -28
  44. package/src/__tests__/config-schema-cmd.test.ts +5 -25
  45. package/src/__tests__/config-schema.test.ts +21 -40
  46. package/src/__tests__/config-watcher.test.ts +4 -91
  47. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
  48. package/src/__tests__/contacts-tools.test.ts +0 -21
  49. package/src/__tests__/context-memory-e2e.test.ts +0 -21
  50. package/src/__tests__/context-window-manager.test.ts +130 -3
  51. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
  52. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
  53. package/src/__tests__/conversation-agent-loop.test.ts +0 -4
  54. package/src/__tests__/conversation-attachments.test.ts +1 -24
  55. package/src/__tests__/conversation-attention-store.test.ts +0 -21
  56. package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
  57. package/src/__tests__/conversation-clear-safety.test.ts +0 -22
  58. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
  59. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
  60. package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
  61. package/src/__tests__/conversation-disk-view.test.ts +5 -27
  62. package/src/__tests__/conversation-error.test.ts +1 -1
  63. package/src/__tests__/conversation-fork-crud.test.ts +1 -33
  64. package/src/__tests__/conversation-fork-route.test.ts +0 -27
  65. package/src/__tests__/conversation-history-web-search.test.ts +23 -16
  66. package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
  67. package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
  68. package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
  69. package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
  70. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
  71. package/src/__tests__/conversation-queue.test.ts +8 -8
  72. package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
  74. package/src/__tests__/conversation-slash-commands.test.ts +5 -0
  75. package/src/__tests__/conversation-slash-queue.test.ts +0 -4
  76. package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
  77. package/src/__tests__/conversation-speed-override.test.ts +326 -0
  78. package/src/__tests__/conversation-starter-routes.test.ts +0 -23
  79. package/src/__tests__/conversation-store.test.ts +0 -21
  80. package/src/__tests__/conversation-unread-route.test.ts +0 -24
  81. package/src/__tests__/conversation-usage.test.ts +56 -21
  82. package/src/__tests__/conversation-wipe.test.ts +0 -21
  83. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
  84. package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
  85. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
  86. package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
  87. package/src/__tests__/credential-vault-unit.test.ts +9 -428
  88. package/src/__tests__/credentials-cli.test.ts +10 -10
  89. package/src/__tests__/daemon-assistant-events.test.ts +0 -19
  90. package/src/__tests__/date-context.test.ts +77 -97
  91. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
  92. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
  93. package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
  94. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
  95. package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
  96. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
  97. package/src/__tests__/email-cli.test.ts +6 -6
  98. package/src/__tests__/ephemeral-permissions.test.ts +5 -17
  99. package/src/__tests__/first-greeting.test.ts +4 -32
  100. package/src/__tests__/followup-tools.test.ts +0 -21
  101. package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
  102. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
  103. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
  104. package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
  105. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
  106. package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
  107. package/src/__tests__/guardian-action-store.test.ts +0 -21
  108. package/src/__tests__/guardian-action-sweep.test.ts +0 -21
  109. package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
  110. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
  111. package/src/__tests__/guardian-dispatch.test.ts +0 -21
  112. package/src/__tests__/guardian-grant-minting.test.ts +0 -22
  113. package/src/__tests__/guardian-outbound-http.test.ts +0 -22
  114. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
  115. package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
  116. package/src/__tests__/guardian-routing-state.test.ts +0 -22
  117. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
  118. package/src/__tests__/headless-browser-interactions.test.ts +0 -4
  119. package/src/__tests__/headless-browser-navigate.test.ts +0 -4
  120. package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
  121. package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
  122. package/src/__tests__/heartbeat-service.test.ts +99 -26
  123. package/src/__tests__/hooks-blocking.test.ts +3 -3
  124. package/src/__tests__/hooks-config.test.ts +7 -7
  125. package/src/__tests__/hooks-discovery.test.ts +3 -3
  126. package/src/__tests__/hooks-integration.test.ts +5 -5
  127. package/src/__tests__/hooks-manager.test.ts +3 -3
  128. package/src/__tests__/hooks-runner.test.ts +5 -23
  129. package/src/__tests__/hooks-settings.test.ts +3 -3
  130. package/src/__tests__/hooks-templates.test.ts +3 -3
  131. package/src/__tests__/http-conversation-lineage.test.ts +0 -27
  132. package/src/__tests__/identity-intro-cache.test.ts +0 -4
  133. package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
  134. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
  135. package/src/__tests__/intent-routing.test.ts +2 -55
  136. package/src/__tests__/invite-redemption-service.test.ts +0 -21
  137. package/src/__tests__/invite-routes-http.test.ts +0 -21
  138. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
  139. package/src/__tests__/journal-context.test.ts +8 -75
  140. package/src/__tests__/list-messages-attachments.test.ts +0 -22
  141. package/src/__tests__/llm-context-route-provider.test.ts +0 -21
  142. package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
  143. package/src/__tests__/llm-usage-store.test.ts +0 -21
  144. package/src/__tests__/log-export-workspace.test.ts +1 -1
  145. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
  146. package/src/__tests__/managed-store.test.ts +1 -1
  147. package/src/__tests__/mcp-cli.test.ts +7 -10
  148. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
  149. package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
  150. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
  151. package/src/__tests__/memory-recall-log-store.test.ts +0 -27
  152. package/src/__tests__/memory-recall-quality.test.ts +0 -21
  153. package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
  154. package/src/__tests__/memory-regressions.test.ts +282 -70
  155. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
  156. package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
  157. package/src/__tests__/messaging-send-tool.test.ts +201 -0
  158. package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
  159. package/src/__tests__/migration-export-http.test.ts +7 -1
  160. package/src/__tests__/migration-import-commit-http.test.ts +16 -14
  161. package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
  162. package/src/__tests__/migration-validate-http.test.ts +1 -28
  163. package/src/__tests__/native-web-search.test.ts +25 -22
  164. package/src/__tests__/non-member-access-request.test.ts +0 -22
  165. package/src/__tests__/notification-guardian-path.test.ts +0 -21
  166. package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
  167. package/src/__tests__/oauth-apps-routes.test.ts +103 -2
  168. package/src/__tests__/oauth-cli.test.ts +52 -0
  169. package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
  170. package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
  171. package/src/__tests__/oauth-providers-routes.test.ts +257 -0
  172. package/src/__tests__/oauth-store.test.ts +0 -21
  173. package/src/__tests__/onboarding-template-contract.test.ts +2 -2
  174. package/src/__tests__/openai-provider.test.ts +261 -0
  175. package/src/__tests__/pairing-concurrent.test.ts +6 -6
  176. package/src/__tests__/pairing-routes.test.ts +7 -1
  177. package/src/__tests__/path-policy.test.ts +1 -1
  178. package/src/__tests__/platform.test.ts +64 -88
  179. package/src/__tests__/playbook-execution.test.ts +0 -21
  180. package/src/__tests__/playbook-tools.test.ts +0 -21
  181. package/src/__tests__/pricing.test.ts +100 -0
  182. package/src/__tests__/relay-server.test.ts +1 -25
  183. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
  184. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
  185. package/src/__tests__/runtime-events-sse.test.ts +0 -24
  186. package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
  187. package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
  188. package/src/__tests__/schedule-store.test.ts +0 -21
  189. package/src/__tests__/schedule-tools.test.ts +0 -21
  190. package/src/__tests__/scheduler-recurrence.test.ts +0 -21
  191. package/src/__tests__/scoped-approval-grants.test.ts +0 -21
  192. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
  193. package/src/__tests__/secret-allowlist.test.ts +1 -1
  194. package/src/__tests__/secret-ingress-channel.test.ts +0 -5
  195. package/src/__tests__/secret-ingress-cli.test.ts +0 -6
  196. package/src/__tests__/secret-ingress-http.test.ts +0 -5
  197. package/src/__tests__/secret-ingress.test.ts +0 -5
  198. package/src/__tests__/send-endpoint-busy.test.ts +0 -24
  199. package/src/__tests__/sequence-store.test.ts +0 -21
  200. package/src/__tests__/server-history-render.test.ts +0 -24
  201. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
  202. package/src/__tests__/skill-load-inline-command.test.ts +9 -0
  203. package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
  204. package/src/__tests__/skill-load-tool.test.ts +11 -0
  205. package/src/__tests__/skills-uninstall.test.ts +10 -8
  206. package/src/__tests__/skills.test.ts +1 -1
  207. package/src/__tests__/slack-channel-config.test.ts +1 -1
  208. package/src/__tests__/slack-inbound-verification.test.ts +0 -22
  209. package/src/__tests__/starter-bundle.test.ts +4 -1
  210. package/src/__tests__/suggestion-routes.test.ts +2 -0
  211. package/src/__tests__/system-prompt.test.ts +1 -1
  212. package/src/__tests__/terminal-tools.test.ts +1 -1
  213. package/src/__tests__/test-preload.ts +31 -0
  214. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
  215. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  216. package/src/__tests__/tool-executor.test.ts +0 -20
  217. package/src/__tests__/tool-input-summary.test.ts +124 -0
  218. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
  219. package/src/__tests__/trust-store.test.ts +7 -1
  220. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
  221. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
  222. package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
  223. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  224. package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
  225. package/src/__tests__/twilio-routes.test.ts +1 -1
  226. package/src/__tests__/update-bulletin.test.ts +1 -1
  227. package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
  228. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
  229. package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
  230. package/src/__tests__/voice-session-bridge.test.ts +1 -1
  231. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
  232. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
  233. package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
  234. package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
  235. package/src/agent/loop.ts +6 -9
  236. package/src/approvals/guardian-decision-primitive.ts +46 -18
  237. package/src/approvals/guardian-request-resolvers.ts +19 -2
  238. package/src/calls/active-call-lease.ts +2 -2
  239. package/src/cli/AGENTS.md +1 -1
  240. package/src/cli/commands/doctor.ts +9 -9
  241. package/src/cli/commands/memory.ts +142 -0
  242. package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
  243. package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
  244. package/src/cli/commands/oauth/connect.ts +13 -12
  245. package/src/cli/commands/oauth/index.ts +1 -1
  246. package/src/cli/commands/oauth/providers.ts +47 -62
  247. package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
  248. package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
  249. package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
  250. package/src/cli/commands/platform/connect.ts +17 -7
  251. package/src/cli/commands/platform/disconnect.ts +28 -3
  252. package/src/cli/commands/platform/index.ts +3 -3
  253. package/src/cli.ts +1 -299
  254. package/src/config/assistant-feature-flags.ts +23 -15
  255. package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
  256. package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
  257. package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
  258. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
  259. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
  260. package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
  261. package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
  262. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
  263. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
  264. package/src/config/bundled-skills/messaging/SKILL.md +7 -7
  265. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
  266. package/src/config/bundled-skills/slack/SKILL.md +18 -0
  267. package/src/config/env-registry.ts +15 -11
  268. package/src/config/env.ts +1 -11
  269. package/src/config/feature-flag-registry.json +16 -0
  270. package/src/config/schema.ts +4 -0
  271. package/src/config/schemas/heartbeat.ts +6 -1
  272. package/src/config/schemas/inference.ts +14 -3
  273. package/src/config/schemas/memory-processing.ts +16 -8
  274. package/src/config/schemas/memory-retrieval.ts +3 -3
  275. package/src/config/skills.ts +1 -1
  276. package/src/context/window-manager.ts +174 -51
  277. package/src/credential-execution/executable-discovery.ts +2 -2
  278. package/src/daemon/approved-devices-store.ts +2 -2
  279. package/src/daemon/assistant-attachments.ts +2 -0
  280. package/src/daemon/config-watcher.ts +4 -50
  281. package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
  282. package/src/daemon/conversation-agent-loop.ts +12 -0
  283. package/src/daemon/conversation-error.ts +3 -5
  284. package/src/daemon/conversation-history.ts +7 -3
  285. package/src/daemon/conversation-lifecycle.ts +16 -0
  286. package/src/daemon/conversation-messaging.ts +1 -0
  287. package/src/daemon/conversation-notifiers.ts +67 -30
  288. package/src/daemon/conversation-process.ts +161 -2
  289. package/src/daemon/conversation-queue-manager.ts +2 -0
  290. package/src/daemon/conversation-runtime-assembly.ts +33 -11
  291. package/src/daemon/conversation-slash.ts +14 -3
  292. package/src/daemon/conversation-tool-setup.ts +2 -0
  293. package/src/daemon/conversation-usage.ts +32 -4
  294. package/src/daemon/conversation.ts +33 -1
  295. package/src/daemon/daemon-control.ts +32 -16
  296. package/src/daemon/date-context.ts +47 -45
  297. package/src/daemon/dictation-profile-store.ts +2 -2
  298. package/src/daemon/handlers/conversations.ts +19 -0
  299. package/src/daemon/handlers/shared.ts +14 -21
  300. package/src/daemon/lifecycle.ts +5 -7
  301. package/src/daemon/message-types/conversations.ts +2 -0
  302. package/src/daemon/message-types/guardian-actions.ts +3 -17
  303. package/src/daemon/message-types/integrations.ts +11 -1
  304. package/src/daemon/message-types/messages.ts +1 -0
  305. package/src/daemon/pairing-store.ts +2 -79
  306. package/src/daemon/server.ts +154 -8
  307. package/src/daemon/watch-handler.ts +65 -21
  308. package/src/email/guardrails.ts +3 -3
  309. package/src/heartbeat/heartbeat-service.ts +14 -7
  310. package/src/hooks/cli.ts +2 -2
  311. package/src/hooks/config.ts +2 -2
  312. package/src/hooks/discovery.ts +2 -2
  313. package/src/hooks/manager.ts +2 -2
  314. package/src/hooks/runner.ts +5 -2
  315. package/src/hooks/templates.ts +2 -2
  316. package/src/memory/admin.ts +181 -2
  317. package/src/memory/app-git-service.ts +61 -4
  318. package/src/memory/attachments-store.ts +2 -0
  319. package/src/memory/canonical-guardian-store.ts +16 -0
  320. package/src/memory/db-init.ts +8 -0
  321. package/src/memory/embedding-local.ts +5 -2
  322. package/src/memory/indexer.ts +44 -26
  323. package/src/memory/items-extractor.ts +34 -82
  324. package/src/memory/job-handlers/batch-extraction.ts +741 -0
  325. package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
  326. package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
  327. package/src/memory/jobs-store.ts +28 -0
  328. package/src/memory/jobs-worker.ts +56 -9
  329. package/src/memory/lifecycle-events-store.ts +4 -2
  330. package/src/memory/llm-request-log-store.ts +40 -2
  331. package/src/memory/llm-usage-store.ts +4 -3
  332. package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
  333. package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
  334. package/src/memory/migrations/index.ts +2 -0
  335. package/src/memory/query-expansion.ts +83 -0
  336. package/src/memory/retriever.test.ts +119 -0
  337. package/src/memory/retriever.ts +513 -105
  338. package/src/memory/schema/guardian.ts +4 -0
  339. package/src/memory/schema/infrastructure.ts +1 -0
  340. package/src/memory/search/formatting.test.ts +140 -0
  341. package/src/memory/search/formatting.ts +143 -198
  342. package/src/memory/search/mmr.ts +136 -0
  343. package/src/memory/search/staleness.ts +0 -15
  344. package/src/memory/search/tier-classifier.ts +10 -21
  345. package/src/memory/search/types.ts +17 -0
  346. package/src/messaging/providers/slack/adapter.ts +51 -5
  347. package/src/notifications/broadcaster.ts +13 -0
  348. package/src/notifications/copy-composer.ts +8 -0
  349. package/src/oauth/connect-orchestrator.ts +1 -1
  350. package/src/oauth/connection-resolver.ts +2 -2
  351. package/src/oauth/provider-serializer.ts +116 -0
  352. package/src/permissions/trust-store.ts +24 -7
  353. package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
  354. package/src/prompts/journal-context.ts +50 -35
  355. package/src/prompts/persona-resolver.ts +1 -1
  356. package/src/prompts/system-prompt.ts +27 -28
  357. package/src/prompts/templates/BOOTSTRAP.md +14 -1
  358. package/src/prompts/templates/HEARTBEAT.md +10 -0
  359. package/src/prompts/templates/NOW.md +19 -25
  360. package/src/prompts/templates/SOUL.md +13 -1
  361. package/src/prompts/templates/UPDATES.md +12 -0
  362. package/src/prompts/update-bulletin.ts +1 -1
  363. package/src/providers/anthropic/client.ts +89 -18
  364. package/src/providers/model-catalog.ts +22 -2
  365. package/src/providers/model-intents.ts +2 -2
  366. package/src/providers/openai/client.ts +40 -1
  367. package/src/providers/retry.ts +23 -4
  368. package/src/providers/types.ts +2 -0
  369. package/src/runtime/assistant-scope.ts +1 -1
  370. package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
  371. package/src/runtime/auth/route-policy.ts +1 -0
  372. package/src/runtime/auth/token-service.ts +51 -29
  373. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
  374. package/src/runtime/guardian-decision-types.ts +16 -10
  375. package/src/runtime/http-server.ts +3 -14
  376. package/src/runtime/http-types.ts +1 -0
  377. package/src/runtime/migrations/vbundle-builder.ts +7 -4
  378. package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
  379. package/src/runtime/migrations/vbundle-importer.ts +1 -1
  380. package/src/runtime/routes/conversation-query-routes.ts +40 -8
  381. package/src/runtime/routes/conversation-routes.ts +125 -3
  382. package/src/runtime/routes/guardian-action-routes.ts +9 -3
  383. package/src/runtime/routes/identity-routes.ts +25 -4
  384. package/src/runtime/routes/llm-context-normalization.ts +1 -0
  385. package/src/runtime/routes/log-export-routes.ts +34 -12
  386. package/src/runtime/routes/migration-routes.ts +6 -10
  387. package/src/runtime/routes/oauth-apps.ts +2 -9
  388. package/src/runtime/routes/oauth-providers.ts +60 -0
  389. package/src/runtime/routes/pairing-routes.ts +0 -8
  390. package/src/runtime/routes/settings-routes.ts +0 -1
  391. package/src/runtime/routes/telemetry-routes.ts +16 -4
  392. package/src/security/encrypted-store.ts +2 -2
  393. package/src/security/secret-allowlist.ts +3 -3
  394. package/src/signals/emit-event.ts +42 -0
  395. package/src/signals/user-message.ts +37 -0
  396. package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
  397. package/src/telemetry/usage-telemetry-reporter.ts +23 -17
  398. package/src/tools/browser/runtime-check.ts +2 -2
  399. package/src/tools/credentials/vault.ts +2 -249
  400. package/src/tools/memory/definitions.ts +1 -1
  401. package/src/tools/memory/handlers.test.ts +50 -8
  402. package/src/tools/memory/handlers.ts +3 -1
  403. package/src/tools/side-effects.ts +1 -6
  404. package/src/tools/terminal/safe-env.ts +3 -2
  405. package/src/tools/terminal/shell.ts +11 -14
  406. package/src/tools/tool-approval-handler.ts +20 -1
  407. package/src/tools/tool-input-summary.ts +66 -0
  408. package/src/tools/types.ts +4 -0
  409. package/src/usage/types.ts +4 -0
  410. package/src/util/device-id.ts +10 -10
  411. package/src/util/platform.ts +71 -33
  412. package/src/util/pricing.ts +19 -6
  413. package/src/util/strip-comment-lines.ts +28 -0
  414. package/src/workspace/git-service.ts +8 -18
  415. package/src/workspace/migrations/003-seed-device-id.ts +6 -4
  416. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
  417. package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
  418. package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
  419. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
  420. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
  421. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
  422. package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
  423. package/src/workspace/migrations/registry.ts +8 -0
  424. package/src/signals/confirm.ts +0 -82
  425. package/src/signals/trust-rule.ts +0 -174
@@ -22,6 +22,7 @@ import { isHttpAuthDisabled } from "../../config/env.js";
22
22
  import { getConfig } from "../../config/loader.js";
23
23
  import {
24
24
  buildModelInfoEvent,
25
+ formatCompactResult,
25
26
  isModelSlashCommand,
26
27
  } from "../../daemon/conversation-process.js";
27
28
  import {
@@ -64,6 +65,8 @@ import { searchConversations } from "../../memory/conversation-queries.js";
64
65
  import { getConfiguredProvider } from "../../providers/provider-send-message.js";
65
66
  import type { Provider } from "../../providers/types.js";
66
67
  import { checkIngressForSecrets } from "../../security/secret-ingress.js";
68
+ import { redactSecrets } from "../../security/secret-scanner.js";
69
+ import { summarizeToolInput } from "../../tools/tool-input-summary.js";
67
70
  import { getLogger } from "../../util/logger.js";
68
71
  import { silentlyWithLog } from "../../util/silently.js";
69
72
  import { buildAssistantEvent } from "../assistant-event.js";
@@ -417,6 +420,19 @@ export function handleListMessages(
417
420
  }
418
421
  const rendered = renderHistoryContent(content);
419
422
 
423
+ // Extract sentAt from metadata for display timestamps. When a message
424
+ // was queued or its persistence was delayed (long assistant generation),
425
+ // sentAt captures the actual event time. Falls back to createdAt.
426
+ let sentAt: number | undefined;
427
+ if (msg.metadata) {
428
+ try {
429
+ const meta = JSON.parse(msg.metadata);
430
+ if (typeof meta.sentAt === "number") sentAt = meta.sentAt;
431
+ } catch {
432
+ // Ignore malformed metadata
433
+ }
434
+ }
435
+
420
436
  // Strip <no_response/> markers from assistant messages so web/API
421
437
  // clients never see the raw sentinel. Only assistant messages produce
422
438
  // this marker; user messages are left untouched.
@@ -449,11 +465,15 @@ export function handleListMessages(
449
465
  role: msg.role,
450
466
  text: rendered.text.replace(NO_RESPONSE_INLINE_RE, "").trim(),
451
467
  timestamp: msg.createdAt,
468
+ sentAt,
452
469
  toolCalls: rendered.toolCalls,
453
470
  toolCallsBeforeText: rendered.toolCallsBeforeText,
454
471
  textSegments: filteredSegments,
455
472
  contentOrder: filteredContentOrder,
456
473
  surfaces: rendered.surfaces,
474
+ ...(rendered.thinkingSegments.length > 0
475
+ ? { thinkingSegments: rendered.thinkingSegments }
476
+ : {}),
457
477
  id: msg.id,
458
478
  };
459
479
  }
@@ -462,11 +482,15 @@ export function handleListMessages(
462
482
  role: msg.role,
463
483
  text: rendered.text,
464
484
  timestamp: msg.createdAt,
485
+ sentAt,
465
486
  toolCalls: rendered.toolCalls,
466
487
  toolCallsBeforeText: rendered.toolCallsBeforeText,
467
488
  textSegments: rendered.textSegments,
468
489
  contentOrder: rendered.contentOrder,
469
490
  surfaces: rendered.surfaces,
491
+ ...(rendered.thinkingSegments.length > 0
492
+ ? { thinkingSegments: rendered.thinkingSegments }
493
+ : {}),
470
494
  id: msg.id,
471
495
  };
472
496
  });
@@ -535,16 +559,26 @@ export function handleListMessages(
535
559
  prevAssistantTimestamp = msgTimestamp;
536
560
  }
537
561
 
562
+ // Use sentAt (actual event time) for the display timestamp when
563
+ // available, falling back to createdAt (persistence time).
564
+ // Note: clients use this display timestamp as their pagination cursor
565
+ // after memory-pressure trimming, while server-side pagination filters
566
+ // on createdAt. The mismatch is benign — it may return slightly extra
567
+ // data on a page boundary but never loses messages.
568
+ const displayTimestamp = m.sentAt ?? m.timestamp;
538
569
  return {
539
570
  id: m.id ?? "",
540
571
  role: m.role,
541
572
  content: m.text,
542
- timestamp: new Date(m.timestamp).toISOString(),
573
+ timestamp: new Date(displayTimestamp).toISOString(),
543
574
  attachments: msgAttachments,
544
575
  ...(m.toolCalls.length > 0 ? { toolCalls: m.toolCalls } : {}),
545
576
  ...(interfaces ? { interfaces } : {}),
546
577
  ...(m.surfaces.length > 0 ? { surfaces: m.surfaces } : {}),
547
578
  ...(m.textSegments.length > 0 ? { textSegments: m.textSegments } : {}),
579
+ ...(m.thinkingSegments?.length
580
+ ? { thinkingSegments: m.thinkingSegments }
581
+ : {}),
548
582
  ...(m.contentOrder.length > 0 ? { contentOrder: m.contentOrder } : {}),
549
583
  };
550
584
  });
@@ -604,6 +638,14 @@ function makeHubPublisher(
604
638
  try {
605
639
  const trustContext = conversation.trustContext;
606
640
  const sourceChannel = trustContext?.sourceChannel ?? "vellum";
641
+ const inputRecord = msg.input as Record<string, unknown>;
642
+ const activityRaw =
643
+ (typeof inputRecord.activity === "string"
644
+ ? inputRecord.activity
645
+ : undefined) ??
646
+ (typeof inputRecord.reason === "string"
647
+ ? inputRecord.reason
648
+ : undefined);
607
649
  const canonicalRequest = createCanonicalGuardianRequest({
608
650
  id: msg.requestId,
609
651
  kind: "tool_approval",
@@ -615,6 +657,15 @@ function makeHubPublisher(
615
657
  guardianExternalUserId: trustContext?.guardianExternalUserId,
616
658
  guardianPrincipalId: trustContext?.guardianPrincipalId ?? undefined,
617
659
  toolName: msg.toolName,
660
+ commandPreview:
661
+ redactSecrets(
662
+ summarizeToolInput(msg.toolName, inputRecord),
663
+ ) || undefined,
664
+ riskLevel: msg.riskLevel,
665
+ activityText: activityRaw
666
+ ? redactSecrets(activityRaw)
667
+ : undefined,
668
+ executionTarget: msg.executionTarget,
618
669
  status: "pending",
619
670
  requestCode: generateCanonicalRequestCode(),
620
671
  expiresAt: Date.now() + 5 * 60 * 1000,
@@ -1291,6 +1342,74 @@ export async function handleSendMessage(
1291
1342
  }
1292
1343
  }
1293
1344
 
1345
+ if (slashResult.kind === "compact") {
1346
+ conversation.processing = true;
1347
+ let cleanupDeferred = false;
1348
+ try {
1349
+ const provenance = provenanceFromTrustContext(conversation.trustContext);
1350
+ const channelMeta = {
1351
+ ...provenance,
1352
+ userMessageChannel: sourceChannel,
1353
+ assistantMessageChannel: sourceChannel,
1354
+ userMessageInterface: sourceInterface,
1355
+ assistantMessageInterface: sourceInterface,
1356
+ };
1357
+ const cleanMsg = createUserMessage(rawContent, attachments);
1358
+ const persisted = await addMessage(
1359
+ mapping.conversationId,
1360
+ "user",
1361
+ JSON.stringify(cleanMsg.content),
1362
+ channelMeta,
1363
+ );
1364
+ conversation.getMessages().push(cleanMsg);
1365
+
1366
+ conversation.emitActivityState(
1367
+ "thinking",
1368
+ "context_compacting",
1369
+ "assistant_turn",
1370
+ );
1371
+ const result = await conversation.forceCompact();
1372
+ const responseText = formatCompactResult(result);
1373
+
1374
+ const assistantMsg = createAssistantMessage(responseText);
1375
+ await addMessage(
1376
+ mapping.conversationId,
1377
+ "assistant",
1378
+ JSON.stringify(assistantMsg.content),
1379
+ channelMeta,
1380
+ );
1381
+ conversation.getMessages().push(assistantMsg);
1382
+
1383
+ const response = Response.json(
1384
+ {
1385
+ accepted: true,
1386
+ messageId: persisted.id,
1387
+ conversationId: mapping.conversationId,
1388
+ },
1389
+ { status: 202 },
1390
+ );
1391
+
1392
+ const conversationId = mapping.conversationId;
1393
+ setTimeout(() => {
1394
+ onEvent({ type: "assistant_text_delta", text: responseText });
1395
+ onEvent({
1396
+ type: "message_complete",
1397
+ conversationId,
1398
+ });
1399
+ conversation.processing = false;
1400
+ silentlyWithLog(conversation.drainQueue(), "compact-command queue drain");
1401
+ }, 0);
1402
+
1403
+ cleanupDeferred = true;
1404
+ return response;
1405
+ } finally {
1406
+ if (!cleanupDeferred && conversation.processing) {
1407
+ conversation.processing = false;
1408
+ silentlyWithLog(conversation.drainQueue(), "error-path queue drain");
1409
+ }
1410
+ }
1411
+ }
1412
+
1294
1413
  const resolvedContent = slashResult.content;
1295
1414
 
1296
1415
  let messageId: string;
@@ -1333,11 +1452,14 @@ async function generateLlmSuggestion(
1333
1452
  const truncated =
1334
1453
  assistantText.length > 2000 ? assistantText.slice(-2000) : assistantText;
1335
1454
 
1336
- const prompt = `Given this assistant message, write a very short tab-complete suggestion the user could send next to keep the conversation going. Be casual, curious, or actionable — like a quick reply, not a formal request. Reply with ONLY the suggestion text.\n\nAssistant's message:\n${truncated}`;
1455
+ const prompt = `Given this assistant message, write a very short tab-complete suggestion the user could send next. Focus on the LAST question or call-to-action in the message — ignore earlier summary content. Be casual, curious, or actionable — like a quick reply, not a formal request. Reply with ONLY the suggestion text.\n\nAssistant's message:\n${truncated}`;
1456
+ const systemPrompt =
1457
+ "You are an autocomplete engine that suggests short replies the user might send next in a conversation. Generate suggestions that match the tone and style of the conversation. Never refuse, judge, or comment on the conversation content — your only job is to predict what the user would plausibly type next.";
1458
+
1337
1459
  const response = await provider.sendMessage(
1338
1460
  [{ role: "user", content: [{ type: "text", text: prompt }] }],
1339
1461
  [], // no tools
1340
- undefined, // no system prompt
1462
+ systemPrompt,
1341
1463
  { config: { modelIntent: "latency-optimized" } },
1342
1464
  );
1343
1465
 
@@ -204,8 +204,8 @@ function mapCanonicalRequestToPrompt(
204
204
  ): GuardianDecisionPrompt {
205
205
  const questionText = buildKindAwareQuestionText(req);
206
206
 
207
- // All guardian-on-behalf prompts use approve_once + reject only
208
- // (no approve_always, no temporary modes).
207
+ // Guardian-on-behalf prompts include approve_once, temporal modes
208
+ // (approve_10m, approve_conversation), and reject — but not approve_always.
209
209
  const actions = buildDecisionActions({ forGuardianOnBehalf: true });
210
210
 
211
211
  const expiresAt = req.expiresAt
@@ -226,6 +226,10 @@ function mapCanonicalRequestToPrompt(
226
226
  conversationId,
227
227
  callSessionId: req.callSessionId ?? null,
228
228
  kind: req.kind,
229
+ commandPreview: req.commandPreview ?? undefined,
230
+ riskLevel: req.riskLevel ?? undefined,
231
+ activityText: req.activityText ?? undefined,
232
+ executionTarget: (req.executionTarget as "sandbox" | "host") ?? undefined,
229
233
  };
230
234
  }
231
235
 
@@ -240,7 +244,9 @@ function buildKindAwareQuestionText(req: CanonicalGuardianRequest): string {
240
244
  const baseText =
241
245
  req.questionText ??
242
246
  (req.toolName
243
- ? `Approve tool: ${req.toolName}`
247
+ ? req.activityText
248
+ ? `Approve tool: ${req.toolName} — ${req.activityText}`
249
+ : `Approve tool: ${req.toolName}`
244
250
  : `Guardian request: ${req.kind}`);
245
251
 
246
252
  if (req.kind === "access_request") {
@@ -9,10 +9,12 @@ import { fileURLToPath } from "node:url";
9
9
 
10
10
  import { z } from "zod";
11
11
 
12
- import { getBaseDataDir } from "../../config/env-registry.js";
13
12
  import { parseIdentityFields } from "../../daemon/handlers/identity.js";
14
13
  import { getMaxMigrationVersion } from "../../memory/migrations/registry.js";
15
- import { getWorkspacePromptPath } from "../../util/platform.js";
14
+ import {
15
+ getWorkspaceDir,
16
+ getWorkspacePromptPath,
17
+ } from "../../util/platform.js";
16
18
  import { WORKSPACE_MIGRATIONS } from "../../workspace/migrations/registry.js";
17
19
  import { getLastWorkspaceMigrationId } from "../../workspace/migrations/runner.js";
18
20
  import { httpError } from "../http-errors.js";
@@ -28,8 +30,8 @@ interface DiskSpaceInfo {
28
30
 
29
31
  function getDiskSpaceInfo(): DiskSpaceInfo | null {
30
32
  try {
31
- const baseDataDir = getBaseDataDir();
32
- const diskPath = baseDataDir && existsSync(baseDataDir) ? baseDataDir : "/";
33
+ const wsDir = getWorkspaceDir();
34
+ const diskPath = existsSync(wsDir) ? wsDir : "/";
33
35
  const stats = statfsSync(diskPath);
34
36
  const totalBytes = stats.bsize * stats.blocks;
35
37
  const freeBytes = stats.bsize * stats.bavail;
@@ -261,6 +263,25 @@ export function identityRouteDefinitions(): RouteDefinition[] {
261
263
  migrations: z.object({}).passthrough(),
262
264
  }),
263
265
  },
266
+ {
267
+ endpoint: "healthz",
268
+ method: "GET",
269
+ handler: () => handleDetailedHealth(),
270
+ policyKey: "health",
271
+ summary: "Detailed health check (alias)",
272
+ description:
273
+ "Alias for /v1/health. Returns runtime health including version, disk, memory, CPU, and migration status.",
274
+ tags: ["system"],
275
+ responseBody: z.object({
276
+ status: z.string(),
277
+ timestamp: z.string(),
278
+ version: z.string(),
279
+ disk: z.object({}).passthrough(),
280
+ memory: z.object({}).passthrough(),
281
+ cpu: z.object({}).passthrough(),
282
+ migrations: z.object({}).passthrough(),
283
+ }),
284
+ },
264
285
  {
265
286
  endpoint: "identity",
266
287
  method: "GET",
@@ -18,6 +18,7 @@ export interface LlmContextSummary {
18
18
  responseToolCallCount?: number;
19
19
  responsePreview?: string;
20
20
  toolCallNames?: string[];
21
+ estimatedCostUsd?: number | null;
21
22
  }
22
23
 
23
24
  export interface LlmContextSection {
@@ -33,8 +33,8 @@ import {
33
33
  } from "../../memory/schema.js";
34
34
  import { getLogger } from "../../util/logger.js";
35
35
  import {
36
+ getDaemonStderrLogPath,
36
37
  getDataDir,
37
- getRootDir,
38
38
  getWorkspaceConfigPath,
39
39
  getWorkspaceDir,
40
40
  } from "../../util/platform.js";
@@ -186,7 +186,7 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
186
186
  }
187
187
  }
188
188
 
189
- const stderrPath = join(getRootDir(), "daemon-stderr.log");
189
+ const stderrPath = getDaemonStderrLogPath();
190
190
  if (existsSync(stderrPath)) {
191
191
  try {
192
192
  const stat = statSync(stderrPath);
@@ -452,6 +452,8 @@ const WORKSPACE_SKIP_DIRS = new Set([
452
452
  "data/attachments",
453
453
  "data/sounds",
454
454
  "conversations",
455
+ "signals",
456
+ "deprecated",
455
457
  ]);
456
458
 
457
459
  /** Files at the workspace root to skip (already covered by sanitized fields). */
@@ -666,6 +668,20 @@ function readSanitizedConfig(): Record<string, unknown> | undefined {
666
668
  // ---------------------------------------------------------------------------
667
669
 
668
670
  export function logExportRouteDefinitions(): RouteDefinition[] {
671
+ const exportRequestBody = z.object({
672
+ auditLimit: z
673
+ .number()
674
+ .int()
675
+ .optional()
676
+ .describe("Max audit records (default 1000)"),
677
+ conversationId: z
678
+ .string()
679
+ .optional()
680
+ .describe("Scope to a single conversation"),
681
+ startTime: z.number().optional().describe("Lower bound epoch ms"),
682
+ endTime: z.number().optional().describe("Upper bound epoch ms"),
683
+ });
684
+
669
685
  return [
670
686
  {
671
687
  endpoint: "export",
@@ -673,17 +689,23 @@ export function logExportRouteDefinitions(): RouteDefinition[] {
673
689
  policyKey: "export",
674
690
  summary: "Export logs and audit data",
675
691
  description:
676
- "Export audit records, daemon logs, workspace contents, and config as a tar.gz archive.",
692
+ "Export audit records, assistant logs, workspace contents, and config as a tar.gz archive.",
693
+ tags: ["export"],
694
+ requestBody: exportRequestBody,
695
+ handler: async ({ req }) => {
696
+ const body = (await req.json()) as ExportRequestBody;
697
+ return handleExport(body);
698
+ },
699
+ },
700
+ {
701
+ endpoint: "logs/export",
702
+ method: "POST",
703
+ policyKey: "export",
704
+ summary: "Export logs and audit data (alias)",
705
+ description:
706
+ "Alias for /v1/export. Export audit records, assistant logs, workspace contents, and config as a tar.gz archive.",
677
707
  tags: ["export"],
678
- requestBody: z.object({
679
- auditLimit: z
680
- .number()
681
- .int()
682
- .describe("Max audit records (default 1000)"),
683
- conversationId: z.string().describe("Scope to a single conversation"),
684
- startTime: z.number().describe("Lower bound epoch ms"),
685
- endTime: z.number().describe("Upper bound epoch ms"),
686
- }),
708
+ requestBody: exportRequestBody,
687
709
  handler: async ({ req }) => {
688
710
  const body = (await req.json()) as ExportRequestBody;
689
711
  return handleExport(body);
@@ -11,7 +11,6 @@
11
11
  * results with is_valid flag and detailed error descriptions.
12
12
  */
13
13
 
14
- import { join } from "node:path";
15
14
  import { Database } from "bun:sqlite";
16
15
 
17
16
  import { z } from "zod";
@@ -23,9 +22,8 @@ import { clearCache as clearTrustCache } from "../../permissions/trust-store.js"
23
22
  import { getLogger } from "../../util/logger.js";
24
23
  import {
25
24
  getDbPath,
26
- getHooksDir,
27
- getRootDir,
28
25
  getWorkspaceDir,
26
+ getWorkspaceHooksDir,
29
27
  } from "../../util/platform.js";
30
28
  import { httpError } from "../http-errors.js";
31
29
  import type { RouteDefinition } from "../http-router.js";
@@ -145,8 +143,9 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
145
143
 
146
144
  try {
147
145
  const { archive, manifest } = buildExportVBundle({
148
- trustPath: join(getRootDir(), "protected", "trust.json"),
149
- hooksDir: getHooksDir(),
146
+ // hooksDir is intentionally omitted — hooks now live under workspace/hooks/
147
+ // and are included in the workspace walk. Passing hooksDir separately would
148
+ // export them twice (once as workspace/hooks/... and again as hooks/...).
150
149
  workspaceDir: getWorkspaceDir(),
151
150
  source: "runtime-export",
152
151
  description,
@@ -301,11 +300,9 @@ export async function handleMigrationImportPreflight(
301
300
  });
302
301
  }
303
302
 
304
- // Step 2: Analyze what would change on import
305
303
  const pathResolver = new DefaultPathResolver(
306
- join(getRootDir(), "protected"),
307
304
  getWorkspaceDir(),
308
- getHooksDir(),
305
+ getWorkspaceHooksDir(),
309
306
  );
310
307
 
311
308
  const report = analyzeImport({
@@ -386,9 +383,8 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
386
383
  }
387
384
 
388
385
  const pathResolver = new DefaultPathResolver(
389
- join(getRootDir(), "protected"),
390
386
  getWorkspaceDir(),
391
- getHooksDir(),
387
+ getWorkspaceHooksDir(),
392
388
  );
393
389
 
394
390
  // Close the live SQLite connection before overwriting assistant.db on disk.
@@ -18,6 +18,7 @@ import {
18
18
  listConnections,
19
19
  upsertApp,
20
20
  } from "../../oauth/oauth-store.js";
21
+ import { serializeProviderSummary } from "../../oauth/provider-serializer.js";
21
22
  import { httpError } from "../http-errors.js";
22
23
  import type { RouteDefinition } from "../http-router.js";
23
24
 
@@ -75,15 +76,7 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
75
76
 
76
77
  const providerRow = getProvider(providerKey);
77
78
  const provider = providerRow
78
- ? {
79
- provider_key: providerRow.providerKey,
80
- display_name: providerRow.displayName ?? null,
81
- description: providerRow.description ?? null,
82
- dashboard_url: providerRow.dashboardUrl ?? null,
83
- client_id_placeholder: providerRow.clientIdPlaceholder ?? null,
84
- requires_client_secret: !!(providerRow.requiresClientSecret ?? 1),
85
- supports_managed_mode: !!providerRow.managedServiceConfigKey,
86
- }
79
+ ? serializeProviderSummary(providerRow)
87
80
  : null;
88
81
 
89
82
  return Response.json({
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Route handlers for listing and getting OAuth providers.
3
+ *
4
+ * Provides read-only endpoints for querying the registered OAuth provider
5
+ * catalog. All endpoints are bearer-token authenticated via the standard
6
+ * runtime auth middleware.
7
+ */
8
+
9
+ import { getProvider, listProviders } from "../../oauth/oauth-store.js";
10
+ import { serializeProviderSummary } from "../../oauth/provider-serializer.js";
11
+ import { httpError } from "../http-errors.js";
12
+ import type { RouteDefinition } from "../http-router.js";
13
+
14
+ /**
15
+ * Build route definitions for OAuth provider list/get endpoints.
16
+ */
17
+ export function oauthProvidersRouteDefinitions(): RouteDefinition[] {
18
+ return [
19
+ // GET /v1/oauth/providers — List all providers with optional filtering.
20
+ {
21
+ endpoint: "oauth/providers",
22
+ method: "GET",
23
+ handler: ({ url }) => {
24
+ const rows = listProviders();
25
+ let serialized = rows
26
+ .map((row) => serializeProviderSummary(row))
27
+ .filter((s): s is NonNullable<typeof s> => s !== null);
28
+
29
+ const supportsManagedModeParam = url.searchParams.get(
30
+ "supports_managed_mode",
31
+ );
32
+ if (supportsManagedModeParam === "true") {
33
+ serialized = serialized.filter((p) => p.supports_managed_mode);
34
+ } else if (supportsManagedModeParam === "false") {
35
+ serialized = serialized.filter((p) => !p.supports_managed_mode);
36
+ }
37
+
38
+ return Response.json({ providers: serialized });
39
+ },
40
+ },
41
+
42
+ // GET /v1/oauth/providers/:providerKey — Get a single provider.
43
+ {
44
+ endpoint: "oauth/providers/:providerKey",
45
+ method: "GET",
46
+ handler: ({ params }) => {
47
+ const row = getProvider(params.providerKey);
48
+ if (!row) {
49
+ return httpError(
50
+ "NOT_FOUND",
51
+ `No OAuth provider registered for "${params.providerKey}"`,
52
+ 404,
53
+ );
54
+ }
55
+
56
+ return Response.json({ provider: serializeProviderSummary(row) });
57
+ },
58
+ },
59
+ ];
60
+ }
@@ -147,8 +147,6 @@ export function cleanupPairingState(pairingRequestId: string): void {
147
147
  export interface PairingHandlerContext {
148
148
  pairingStore: PairingStore;
149
149
  bearerToken: string | undefined;
150
- /** Feature-flag client token to include in pairing approval responses so iOS can PATCH flags. */
151
- featureFlagToken: string | undefined;
152
150
  pairingBroadcast?: (msg: ServerMessage) => void;
153
151
  }
154
152
 
@@ -281,9 +279,6 @@ export async function handlePairingRequest(
281
279
  bearerToken: ctx.bearerToken,
282
280
  gatewayUrl: entry.gatewayUrl,
283
281
  localLanUrl: entry.localLanUrl,
284
- ...(ctx.featureFlagToken
285
- ? { featureFlagToken: ctx.featureFlagToken }
286
- : {}),
287
282
  ...(credentials
288
283
  ? {
289
284
  accessToken: credentials.accessToken,
@@ -388,9 +383,6 @@ export function handlePairingStatus(
388
383
  bearerToken: entry.bearerToken,
389
384
  gatewayUrl: entry.gatewayUrl,
390
385
  localLanUrl: entry.localLanUrl,
391
- ...(ctx.featureFlagToken
392
- ? { featureFlagToken: ctx.featureFlagToken }
393
- : {}),
394
386
  ...(credentialEntry
395
387
  ? {
396
388
  accessToken: credentialEntry.credentials.accessToken,
@@ -564,7 +564,6 @@ const SAFE_ENV_VAR_EXACT_NAMES = new Set([
564
564
  "VISUAL",
565
565
  "TZ",
566
566
  "TMPDIR",
567
- "BASE_DATA_DIR",
568
567
  "QDRANT_HTTP_PORT",
569
568
  "PORT",
570
569
  "DEBUG",
@@ -35,6 +35,9 @@ export async function handleRecordLifecycleEvent(
35
35
  }
36
36
 
37
37
  const event = recordLifecycleEvent(eventName);
38
+ if (!event) {
39
+ return Response.json({ skipped: true });
40
+ }
38
41
  log.info({ eventName, eventId: event.id }, "Recorded lifecycle event");
39
42
 
40
43
  return Response.json({ id: event.id, event_name: event.eventName });
@@ -55,10 +58,19 @@ export function telemetryRouteDefinitions(): RouteDefinition[] {
55
58
  requestBody: z.object({
56
59
  event_name: z.string().describe("Event name: app_open or hatch"),
57
60
  }),
58
- responseBody: z.object({
59
- id: z.string().describe("Event ID"),
60
- event_name: z.string(),
61
- }),
61
+ responseBody: z.union([
62
+ z.object({
63
+ id: z.string().describe("Event ID"),
64
+ event_name: z.string(),
65
+ }),
66
+ z.object({
67
+ skipped: z
68
+ .literal(true)
69
+ .describe(
70
+ "Event skipped due to usage data collection being disabled",
71
+ ),
72
+ }),
73
+ ]),
62
74
  handler: async ({ req }) => handleRecordLifecycleEvent(req),
63
75
  },
64
76
  ];
@@ -30,7 +30,7 @@ import { dirname, join } from "node:path";
30
30
  import { getIsContainerized } from "../config/env-registry.js";
31
31
  import { ensureDir, pathExists } from "../util/fs.js";
32
32
  import { getLogger } from "../util/logger.js";
33
- import { getPlatformName, getRootDir } from "../util/platform.js";
33
+ import { getPlatformName, getProtectedDir } from "../util/platform.js";
34
34
 
35
35
  const log = getLogger("encrypted-store");
36
36
 
@@ -80,7 +80,7 @@ interface EncryptedEntry {
80
80
  let storePathOverride: string | null = null;
81
81
 
82
82
  function getStorePath(): string {
83
- return storePathOverride ?? join(getRootDir(), "protected", "keys.enc");
83
+ return storePathOverride ?? join(getProtectedDir(), "keys.enc");
84
84
  }
85
85
 
86
86
  /** @internal Test-only: override the store file path. Pass `null` to reset. */
@@ -18,7 +18,7 @@ import { join } from "node:path";
18
18
 
19
19
  import { pathExists } from "../util/fs.js";
20
20
  import { getLogger } from "../util/logger.js";
21
- import { getRootDir } from "../util/platform.js";
21
+ import { getProtectedDir } from "../util/platform.js";
22
22
 
23
23
  const log = getLogger("secret-allowlist");
24
24
 
@@ -45,7 +45,7 @@ let allowedRegexes: RegExp[] = [];
45
45
  export function loadAllowlist(): void {
46
46
  if (loaded || fileChecked) return;
47
47
 
48
- const filePath = join(getRootDir(), "protected", "secret-allowlist.json");
48
+ const filePath = join(getProtectedDir(), "secret-allowlist.json");
49
49
  if (!pathExists(filePath)) {
50
50
  fileChecked = true;
51
51
  return;
@@ -169,7 +169,7 @@ function validateAllowlist(
169
169
  * Returns validation errors, or null if the file doesn't exist.
170
170
  */
171
171
  export function validateAllowlistFile(): AllowlistValidationError[] | null {
172
- const filePath = join(getRootDir(), "protected", "secret-allowlist.json");
172
+ const filePath = join(getProtectedDir(), "secret-allowlist.json");
173
173
  if (!pathExists(filePath)) return null;
174
174
 
175
175
  const raw = readFileSync(filePath, "utf-8");