@vellumai/assistant 0.10.1 → 0.10.2-dev.202606241651.2d2b40d

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 (367) hide show
  1. package/docs/workspace-tools.md +42 -33
  2. package/eslint-rules/cli-no-daemon-internals.js +6 -0
  3. package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
  4. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
  5. package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
  6. package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
  7. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
  8. package/openapi.yaml +74 -1
  9. package/package.json +1 -1
  10. package/scripts/test.sh +36 -15
  11. package/src/__tests__/actor-token-service.test.ts +36 -14
  12. package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
  13. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  14. package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
  15. package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
  16. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  17. package/src/__tests__/approval-cascade.test.ts +2 -0
  18. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
  19. package/src/__tests__/btw-routes.test.ts +2 -0
  20. package/src/__tests__/build-persisted-content.test.ts +2 -0
  21. package/src/__tests__/call-controller.test.ts +19 -0
  22. package/src/__tests__/channel-guardian.test.ts +94 -58
  23. package/src/__tests__/channel-reply-delivery.test.ts +2 -0
  24. package/src/__tests__/compaction-events.test.ts +2 -0
  25. package/src/__tests__/compaction.benchmark.test.ts +2 -0
  26. package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
  27. package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
  28. package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
  29. package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
  30. package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
  31. package/src/__tests__/computer-use-tools.test.ts +13 -0
  32. package/src/__tests__/config-loader-backfill.test.ts +5 -1
  33. package/src/__tests__/config-schema.test.ts +1 -0
  34. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
  35. package/src/__tests__/contacts-relay-reads.test.ts +13 -15
  36. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  37. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
  38. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
  40. package/src/__tests__/conversation-agent-loop.test.ts +7 -0
  41. package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
  42. package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
  43. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
  44. package/src/__tests__/conversation-history-web-search.test.ts +2 -0
  45. package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
  46. package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
  47. package/src/__tests__/conversation-pairing.test.ts +2 -0
  48. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  49. package/src/__tests__/conversation-process-callsite.test.ts +2 -0
  50. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  51. package/src/__tests__/conversation-queue.test.ts +91 -0
  52. package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
  53. package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
  54. package/src/__tests__/conversation-slash-queue.test.ts +2 -0
  55. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  56. package/src/__tests__/conversation-speed-override.test.ts +2 -0
  57. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +65 -0
  58. package/src/__tests__/conversation-title-service.test.ts +2 -0
  59. package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
  60. package/src/__tests__/conversation-usage.test.ts +2 -0
  61. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
  62. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  63. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  64. package/src/__tests__/credential-security-invariants.test.ts +0 -1
  65. package/src/__tests__/db-migration-rollback.test.ts +205 -171
  66. package/src/__tests__/db-test-helpers.ts +5 -4
  67. package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
  68. package/src/__tests__/disk-pressure-guard.test.ts +41 -0
  69. package/src/__tests__/dm-persistence.test.ts +2 -0
  70. package/src/__tests__/emit-signal-routing-intent.test.ts +10 -5
  71. package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
  72. package/src/__tests__/filing-service.test.ts +2 -0
  73. package/src/__tests__/guardian-binding-drift-heal.test.ts +75 -10
  74. package/src/__tests__/guardian-dispatch.test.ts +95 -1
  75. package/src/__tests__/guardian-outbound-http.test.ts +13 -0
  76. package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
  77. package/src/__tests__/heartbeat-service.test.ts +2 -0
  78. package/src/__tests__/helpers/channel-test-adapter.ts +1 -7
  79. package/src/__tests__/host-app-control-routes.test.ts +24 -30
  80. package/src/__tests__/host-bash-routes.test.ts +31 -41
  81. package/src/__tests__/host-browser-routes.test.ts +26 -32
  82. package/src/__tests__/host-cu-proxy.test.ts +299 -0
  83. package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
  84. package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
  85. package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
  86. package/src/__tests__/http-user-message-parity.test.ts +167 -8
  87. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  88. package/src/__tests__/invite-redemption-service.test.ts +43 -0
  89. package/src/__tests__/llm-context-normalization.test.ts +105 -0
  90. package/src/__tests__/llm-usage-store.test.ts +25 -0
  91. package/src/__tests__/media-stream-server-integration.test.ts +127 -0
  92. package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
  93. package/src/__tests__/messaging-send-tool.test.ts +2 -0
  94. package/src/__tests__/migration-import-from-url.test.ts +2 -2
  95. package/src/__tests__/native-web-search.test.ts +2 -0
  96. package/src/__tests__/non-member-access-request.test.ts +189 -17
  97. package/src/__tests__/notification-broadcaster.test.ts +4 -0
  98. package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
  99. package/src/__tests__/notification-deep-link.test.ts +6 -0
  100. package/src/__tests__/notification-guardian-path.test.ts +19 -0
  101. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  102. package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
  103. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  104. package/src/__tests__/plugin-bootstrap.test.ts +3 -73
  105. package/src/__tests__/plugin-route-contribution.test.ts +4 -17
  106. package/src/__tests__/plugin-tool-contribution.test.ts +3 -18
  107. package/src/__tests__/plugin-types.test.ts +0 -2
  108. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  109. package/src/__tests__/process-message-display-content.test.ts +2 -0
  110. package/src/__tests__/provider-usage-tracking.test.ts +39 -0
  111. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
  112. package/src/__tests__/registry.test.ts +3 -0
  113. package/src/__tests__/relay-server.test.ts +694 -25
  114. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  115. package/src/__tests__/secret-ingress-http.test.ts +14 -0
  116. package/src/__tests__/send-endpoint-busy.test.ts +30 -8
  117. package/src/__tests__/skills.test.ts +44 -0
  118. package/src/__tests__/slack-inbound-verification.test.ts +47 -2
  119. package/src/__tests__/sse-actor-principal-guardian-source.test.ts +102 -0
  120. package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
  121. package/src/__tests__/stt-hints.test.ts +44 -13
  122. package/src/__tests__/subagent-detail.test.ts +27 -0
  123. package/src/__tests__/subagent-disposal.test.ts +65 -0
  124. package/src/__tests__/subagent-notify-parent.test.ts +2 -0
  125. package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
  126. package/src/__tests__/subagent-tools.test.ts +2 -0
  127. package/src/__tests__/suggestion-routes.test.ts +2 -0
  128. package/src/__tests__/title-generate-hook.test.ts +2 -0
  129. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
  130. package/src/__tests__/tool-executor.test.ts +16 -11
  131. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  132. package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
  133. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  134. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +10 -10
  135. package/src/__tests__/twilio-routes.test.ts +96 -0
  136. package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
  137. package/src/__tests__/web-search-backend-failure.test.ts +2 -0
  138. package/src/__tests__/workspace-tool-loader.test.ts +195 -2
  139. package/src/agent/loop-exclusive-tool.test.ts +150 -0
  140. package/src/agent/loop.ts +56 -0
  141. package/src/api/constants/sse-replay.ts +41 -0
  142. package/src/api/index.ts +6 -0
  143. package/src/api/responses/llm-request-log-entry.ts +25 -0
  144. package/src/api/responses/subagent-detail.ts +17 -0
  145. package/src/calls/__tests__/relay-setup-router.test.ts +262 -4
  146. package/src/calls/call-domain.ts +3 -3
  147. package/src/calls/guardian-dispatch.ts +10 -8
  148. package/src/calls/inbound-trust-reader.ts +17 -1
  149. package/src/calls/media-stream-server.ts +21 -0
  150. package/src/calls/relay-server.ts +167 -50
  151. package/src/calls/relay-setup-router.ts +37 -7
  152. package/src/calls/relay-verification.ts +4 -4
  153. package/src/calls/stt-hints.ts +9 -12
  154. package/src/calls/twilio-routes.ts +14 -4
  155. package/src/cli/commands/__tests__/cache.test.ts +8 -1
  156. package/src/cli/commands/cache.ts +194 -181
  157. package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
  158. package/src/cli/commands/db/status.ts +37 -1
  159. package/src/cli/commands/mcp.ts +252 -218
  160. package/src/cli/commands/memory/__tests__/worker.test.ts +302 -0
  161. package/src/cli/commands/memory/index.ts +2 -0
  162. package/src/cli/commands/memory/worker.ts +175 -0
  163. package/src/cli/commands/plugins.ts +75 -3
  164. package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
  165. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
  166. package/src/cli/lib/list-installed-plugins.ts +179 -1
  167. package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
  168. package/src/config/bundled-skills/computer-use/TOOLS.json +6 -1
  169. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
  170. package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
  171. package/src/config/feature-flag-registry.json +0 -8
  172. package/src/config/loader.ts +36 -5
  173. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  174. package/src/config/schemas/memory-lifecycle.ts +12 -0
  175. package/src/config/schemas/memory-v3.ts +7 -0
  176. package/src/config/schemas/memory.ts +4 -0
  177. package/src/config/schemas/timeouts.ts +8 -0
  178. package/src/config/seed-inference-profiles.ts +14 -5
  179. package/src/config/skills.ts +27 -5
  180. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
  181. package/src/contacts/contacts-write.ts +3 -0
  182. package/src/contacts/guardian-delivery-reader.ts +223 -0
  183. package/src/daemon/conversation-agent-loop.ts +9 -0
  184. package/src/daemon/conversation-process.ts +39 -17
  185. package/src/daemon/conversation-surfaces.ts +8 -0
  186. package/src/daemon/conversation-tool-setup.ts +49 -16
  187. package/src/daemon/conversation.ts +21 -2
  188. package/src/daemon/disk-pressure-guard.ts +12 -2
  189. package/src/daemon/event-loop-watchdog.ts +28 -1
  190. package/src/daemon/external-plugins-bootstrap.ts +4 -34
  191. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -0
  192. package/src/daemon/handlers/__tests__/config-channels.test.ts +225 -0
  193. package/src/daemon/handlers/config-a2a.ts +6 -14
  194. package/src/daemon/handlers/config-channels.ts +78 -22
  195. package/src/daemon/handlers/conversations.ts +77 -0
  196. package/src/daemon/host-cu-proxy.ts +102 -11
  197. package/src/daemon/lifecycle.ts +4 -0
  198. package/src/daemon/memory-v2-startup.test.ts +72 -0
  199. package/src/daemon/memory-v2-startup.ts +87 -19
  200. package/src/daemon/server.ts +0 -4
  201. package/src/daemon/shutdown-handlers.ts +20 -0
  202. package/src/daemon/tool-setup-types.ts +9 -0
  203. package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
  204. package/src/ipc/assistant-server.ts +2 -2
  205. package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
  206. package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
  207. package/src/memory/__tests__/prompt-override.test.ts +192 -0
  208. package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
  209. package/src/memory/conversation-crud.ts +38 -0
  210. package/src/memory/db-connection.ts +22 -3
  211. package/src/memory/db-init.ts +36 -502
  212. package/src/memory/db-singleton.ts +6 -4
  213. package/src/memory/jobs-worker.ts +58 -0
  214. package/src/memory/llm-usage-store.ts +48 -20
  215. package/src/memory/memory-retrospective-job.ts +9 -8
  216. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +13 -3
  217. package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -27
  218. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +130 -56
  219. package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
  220. package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
  221. package/src/memory/migrations/__tests__/014-backfill-inbox-thread-state.test.ts +108 -0
  222. package/src/memory/migrations/__tests__/136-drop-assistant-id-columns.test.ts +82 -0
  223. package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +224 -0
  224. package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
  225. package/src/memory/migrations/run-migrations.ts +90 -6
  226. package/src/memory/migrations/schema-introspection.ts +14 -0
  227. package/src/memory/migrations/validate-migration-state.ts +101 -66
  228. package/src/memory/prompt-override.ts +129 -0
  229. package/src/memory/schema/conversations.ts +9 -0
  230. package/src/memory/schema/infrastructure.ts +20 -0
  231. package/src/memory/steps.ts +573 -0
  232. package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
  233. package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
  234. package/src/memory/v2/cli-command-store.ts +75 -38
  235. package/src/memory/v2/prompts/consolidation.ts +13 -82
  236. package/src/memory/v2/prompts/router.ts +21 -93
  237. package/src/memory/v2/skill-store.ts +68 -31
  238. package/src/memory/watchdog-events-store.ts +87 -0
  239. package/src/memory/worker-control.ts +118 -0
  240. package/src/memory/worker-process.ts +72 -0
  241. package/src/notifications/__tests__/broadcaster.test.ts +16 -8
  242. package/src/notifications/__tests__/connected-channels.test.ts +114 -0
  243. package/src/notifications/__tests__/decision-engine.test.ts +78 -9
  244. package/src/notifications/__tests__/destination-resolver.test.ts +256 -0
  245. package/src/notifications/broadcaster.ts +8 -1
  246. package/src/notifications/decision-engine.ts +15 -7
  247. package/src/notifications/destination-resolver.ts +68 -24
  248. package/src/notifications/emit-signal.ts +39 -14
  249. package/src/onboarding/checkin-event.test.ts +220 -0
  250. package/src/onboarding/checkin-event.ts +321 -0
  251. package/src/onboarding/schedule-checkin.ts +190 -0
  252. package/src/permissions/question-prompter.test.ts +1 -1
  253. package/src/permissions/question-prompter.ts +7 -4
  254. package/src/plugin-api/index.ts +6 -6
  255. package/src/plugin-api/types.ts +3 -5
  256. package/src/plugin-api/vision-support.test.ts +28 -4
  257. package/src/plugin-api/vision-support.ts +66 -31
  258. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +161 -0
  259. package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +106 -0
  260. package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +60 -0
  261. package/src/plugins/defaults/advisor/consult.ts +110 -6
  262. package/src/plugins/defaults/advisor/context-pack.ts +288 -0
  263. package/src/plugins/defaults/advisor/steering.ts +14 -2
  264. package/src/plugins/defaults/advisor/tools/advisor.ts +32 -5
  265. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
  266. package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +10 -11
  267. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +12 -20
  268. package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
  269. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
  270. package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
  271. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +29 -1
  272. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +8 -1
  273. package/src/plugins/mtime-cache.ts +7 -2
  274. package/src/plugins/types.ts +0 -2
  275. package/src/providers/anthropic/client.ts +5 -0
  276. package/src/providers/call-site-routing.ts +4 -0
  277. package/src/providers/model-catalog.ts +16 -0
  278. package/src/providers/openai/responses-provider.ts +5 -0
  279. package/src/providers/openrouter/client.ts +5 -0
  280. package/src/providers/provider-send-message.ts +4 -0
  281. package/src/providers/ratelimit.ts +4 -0
  282. package/src/providers/retry.ts +4 -0
  283. package/src/providers/types.ts +9 -0
  284. package/src/providers/usage-tracking.ts +4 -0
  285. package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
  286. package/src/runtime/__tests__/guardian-vellum-migration.test.ts +181 -0
  287. package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
  288. package/src/runtime/__tests__/local-principal-trust.test.ts +164 -0
  289. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +335 -3
  290. package/src/runtime/access-request-helper.ts +19 -39
  291. package/src/runtime/actor-trust-resolver.ts +2 -2
  292. package/src/runtime/anchored-guardian.test.ts +156 -0
  293. package/src/runtime/anchored-guardian.ts +135 -0
  294. package/src/runtime/assistant-event-hub.ts +1 -1
  295. package/src/runtime/assistant-stream-state.ts +9 -2
  296. package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
  297. package/src/runtime/auth/require-bound-guardian.ts +21 -11
  298. package/src/runtime/channel-verification-service.ts +56 -31
  299. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
  300. package/src/runtime/guardian-vellum-migration.ts +66 -7
  301. package/src/runtime/invite-redemption-service.ts +50 -18
  302. package/src/runtime/local-actor-identity.ts +76 -11
  303. package/src/runtime/local-principal-trust.ts +52 -0
  304. package/src/runtime/pending-interactions.ts +11 -1
  305. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
  306. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  307. package/src/runtime/routes/__tests__/contact-routes.test.ts +212 -0
  308. package/src/runtime/routes/__tests__/global-search-routes.test.ts +93 -0
  309. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +215 -1
  310. package/src/runtime/routes/browser-routes.ts +1 -1
  311. package/src/runtime/routes/channel-verification-routes.ts +3 -3
  312. package/src/runtime/routes/contact-routes.ts +8 -32
  313. package/src/runtime/routes/conversation-cli-routes.ts +4 -5
  314. package/src/runtime/routes/conversation-list-routes.ts +4 -7
  315. package/src/runtime/routes/conversation-routes.ts +74 -81
  316. package/src/runtime/routes/events-routes.ts +2 -2
  317. package/src/runtime/routes/global-search-routes.ts +3 -1
  318. package/src/runtime/routes/guardian-action-routes.ts +4 -5
  319. package/src/runtime/routes/host-app-control-routes.ts +5 -4
  320. package/src/runtime/routes/host-bash-routes.ts +5 -4
  321. package/src/runtime/routes/host-browser-routes.ts +9 -11
  322. package/src/runtime/routes/host-cu-routes.ts +5 -4
  323. package/src/runtime/routes/host-file-routes.ts +5 -4
  324. package/src/runtime/routes/host-transfer-routes.ts +6 -6
  325. package/src/runtime/routes/http-adapter.ts +1 -1
  326. package/src/runtime/routes/identity-routes.ts +3 -2
  327. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  328. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
  329. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -49
  330. package/src/runtime/routes/inbound-stages/background-dispatch.ts +16 -4
  331. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
  332. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
  333. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
  334. package/src/runtime/routes/index.ts +2 -0
  335. package/src/runtime/routes/llm-context-normalization.ts +71 -0
  336. package/src/runtime/routes/mcp-auth-routes.ts +38 -15
  337. package/src/runtime/routes/migration-rollback-routes.ts +4 -3
  338. package/src/runtime/routes/migration-routes.ts +4 -1
  339. package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
  340. package/src/runtime/routes/subagents-routes.ts +5 -0
  341. package/src/runtime/routes/surface-action-routes.ts +51 -55
  342. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  343. package/src/runtime/services/conversation-serializer.ts +7 -9
  344. package/src/runtime/tool-grant-request-helper.ts +3 -3
  345. package/src/runtime/trust-verdict-consumer.ts +85 -9
  346. package/src/runtime/verification-outbound-actions.ts +18 -18
  347. package/src/signals/user-message.ts +16 -0
  348. package/src/subagent/manager.ts +9 -0
  349. package/src/telemetry/types.ts +34 -1
  350. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
  351. package/src/telemetry/usage-telemetry-reporter.ts +87 -3
  352. package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
  353. package/src/tools/ask-question/ask-question-tool.ts +13 -0
  354. package/src/tools/computer-use/definitions.ts +8 -2
  355. package/src/tools/executor.ts +4 -4
  356. package/src/tools/registry.ts +18 -0
  357. package/src/tools/tool-approval-handler.ts +1 -1
  358. package/src/tools/tool-defaults.ts +9 -2
  359. package/src/tools/types.ts +17 -2
  360. package/src/tools/workspace-tools/loader.ts +348 -244
  361. package/src/util/platform.ts +5 -0
  362. package/src/util/telemetry-db-path.ts +24 -0
  363. package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
  364. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
  365. package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
  366. package/src/daemon/workspace-tools-watcher.ts +0 -328
  367. package/src/memory/migrations/registry.ts +0 -573
@@ -55,6 +55,7 @@ import {
55
55
  updateCallSession,
56
56
  } from "./call-store.js";
57
57
  import { getChannelAdmissionPolicy } from "./channel-admission-reader.js";
58
+ import { getPhoneCallerVerdict } from "./inbound-trust-reader.js";
58
59
  import { routeSetup } from "./relay-setup-router.js";
59
60
  import { resolveCallHints } from "./stt-hints.js";
60
61
  import { resolveTelephonySttRouting } from "./telephony-stt-routing.js";
@@ -298,7 +299,7 @@ async function processVoiceWebhook(
298
299
  "Inbound voice webhook — creating/reusing session",
299
300
  );
300
301
 
301
- const { session } = createInboundVoiceSession({
302
+ const { session } = await createInboundVoiceSession({
302
303
  callSid,
303
304
  fromNumber: callerFrom,
304
305
  toNumber: callerTo,
@@ -484,12 +485,21 @@ async function buildVoiceWebhookTwiml(
484
485
  // admits the caller.
485
486
  const admissionPolicy = await getChannelAdmissionPolicy("phone");
486
487
 
488
+ // Verdict-first caller trust so this preflight matches handleSetup's ACL.
489
+ // routeSetup uses it when present and not resolutionFailed, else falls back
490
+ // to local resolution. The reader returns null on failure, keeping the
491
+ // local path on a gateway blip.
492
+ const isInbound = sessionContext?.direction !== "outbound";
493
+ const otherPartyNumber = isInbound ? from : to;
494
+ const verdict = await getPhoneCallerVerdict(otherPartyNumber);
495
+
487
496
  const { outcome } = routeSetup({
488
497
  callSessionId,
489
498
  session: session ?? null,
490
499
  from,
491
500
  to,
492
501
  admissionPolicy,
502
+ verdict,
493
503
  });
494
504
 
495
505
  // The media-stream transport supports normal_call and deny (which speaks
@@ -522,7 +532,7 @@ async function buildVoiceWebhookTwiml(
522
532
  /**
523
533
  * Build ConversationRelay TwiML for Twilio-native STT providers.
524
534
  */
525
- function buildConversationRelayTwiml(
535
+ async function buildConversationRelayTwiml(
526
536
  callSessionId: string,
527
537
  profile: ReturnType<typeof resolveVoiceQualityProfile>,
528
538
  sessionContext: {
@@ -535,8 +545,8 @@ function buildConversationRelayTwiml(
535
545
  } | null,
536
546
  verificationSessionId: string | null | undefined,
537
547
  sttAttrs: { transcriptionProvider: string; speechModel: string | undefined },
538
- ): string {
539
- const rawHints = resolveCallHints(sessionContext, profile.hints);
548
+ ): Promise<string> {
549
+ const rawHints = await resolveCallHints(sessionContext, profile.hints);
540
550
 
541
551
  const speechConfig: TwilioRelaySpeechConfig = {
542
552
  transcriptionProvider: sttAttrs.transcriptionProvider,
@@ -88,8 +88,15 @@ mock.module("../../../util/logger.js", () => ({
88
88
  }));
89
89
 
90
90
  mock.module("../../lib/cache-fs.js", () => ({
91
- readFileSync: (path: string, encoding?: BufferEncoding) => {
91
+ readFileSync: (path: string | number, encoding?: BufferEncoding) => {
92
+ // Stdin must be read via fd 0, not by reopening "/dev/stdin": a spawned
93
+ // subprocess whose stdin is a pipe (Bun.spawn stdin:"pipe") cannot reopen
94
+ // its read-end by path — open("/dev/stdin") fails ENXIO. Throwing here on
95
+ // the path makes any regression to path-based reading fail loudly.
92
96
  if (path === "/dev/stdin") {
97
+ throw new Error("ENXIO: no such device or address, open '/dev/stdin'");
98
+ }
99
+ if (path === 0) {
93
100
  if (mockStdinContent === null) {
94
101
  throw new Error("EAGAIN: resource temporarily unavailable");
95
102
  }
@@ -17,6 +17,9 @@ import { log } from "../logger.js";
17
17
  /** Warn (stderr) when a raw payload exceeds this byte count. */
18
18
  const MAX_PAYLOAD_BYTES = 1_000_000; // 1 MB
19
19
 
20
+ /** Standard input file descriptor. */
21
+ const STDIN_FD = 0;
22
+
20
23
  // ── TTL parsing ───────────────────────────────────────────────────────
21
24
 
22
25
  const TTL_PATTERN = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h)$/;
@@ -99,6 +102,11 @@ function parseJsonPayload(raw: string, source: string): unknown {
99
102
  * Read JSON payload from stdin when piped. Throws when stdin is a TTY
100
103
  * (no piped input) or when the input is empty/invalid JSON, so the CLI
101
104
  * can surface actionable parse errors.
105
+ *
106
+ * Reads file descriptor 0 directly rather than reopening the `/dev/stdin`
107
+ * path. When the caller is a spawned subprocess whose stdin is a pipe (e.g.
108
+ * `Bun.spawn(..., { stdin: "pipe" })`), `open("/dev/stdin")` fails with ENXIO
109
+ * because a pipe read-end cannot be reopened by path; the fd is readable.
102
110
  */
103
111
  function readPayloadFromStdin(): unknown {
104
112
  if (process.stdin.isTTY) {
@@ -111,7 +119,7 @@ function readPayloadFromStdin(): unknown {
111
119
 
112
120
  let raw: string;
113
121
  try {
114
- raw = readFileSync("/dev/stdin", "utf-8");
122
+ raw = readFileSync(STDIN_FD, "utf-8");
115
123
  } catch (err) {
116
124
  throw new Error(
117
125
  `Failed to read stdin: ${err instanceof Error ? err.message : String(err)}.\n` +
@@ -173,10 +181,9 @@ export function registerCacheCommand(program: Command): void {
173
181
  transport: "ipc",
174
182
  description: "Interact with the assistant's in-memory key/value cache",
175
183
  build: (cache) => {
176
-
177
- cache.addHelpText(
178
- "after",
179
- `
184
+ cache.addHelpText(
185
+ "after",
186
+ `
180
187
  The cache is a TTL-aware, LRU-evicting in-memory store managed by the
181
188
  running assistant. Data is scoped to the assistant process lifetime and
182
189
  is not persisted across restarts.
@@ -190,33 +197,33 @@ Examples:
190
197
  $ echo '{"result": [1,2,3]}' | assistant cache set --ttl 5m
191
198
  $ assistant cache get my-key
192
199
  $ assistant cache delete my-key`,
193
- );
194
-
195
- // ── set ───────────────────────────────────────────────────────────
196
-
197
- cache
198
- .command("set")
199
- .description("Store a JSON value in the cache")
200
- .option(
201
- "--key <key>",
202
- "Cache key for idempotent upsert. Omit to auto-generate.",
203
- )
204
- .option(
205
- "--ttl <duration>",
206
- "Time-to-live (minimum 1s). Units: ms, s, m, h (e.g. 1000ms, 30s, 5m, 2h). Defaults to 30m if omitted.",
207
- )
208
- .option(
209
- "--value <json>",
210
- "JSON payload to store. Alternative to piping via stdin.",
211
- )
212
- .option(
213
- "--file <path>",
214
- "Path to a file containing the JSON payload. Alternative to piping via stdin.",
215
- )
216
- .option("--json", "Output result as machine-readable JSON.")
217
- .addHelpText(
218
- "after",
219
- `
200
+ );
201
+
202
+ // ── set ───────────────────────────────────────────────────────────
203
+
204
+ cache
205
+ .command("set")
206
+ .description("Store a JSON value in the cache")
207
+ .option(
208
+ "--key <key>",
209
+ "Cache key for idempotent upsert. Omit to auto-generate.",
210
+ )
211
+ .option(
212
+ "--ttl <duration>",
213
+ "Time-to-live (minimum 1s). Units: ms, s, m, h (e.g. 1000ms, 30s, 5m, 2h). Defaults to 30m if omitted.",
214
+ )
215
+ .option(
216
+ "--value <json>",
217
+ "JSON payload to store. Alternative to piping via stdin.",
218
+ )
219
+ .option(
220
+ "--file <path>",
221
+ "Path to a file containing the JSON payload. Alternative to piping via stdin.",
222
+ )
223
+ .option("--json", "Output result as machine-readable JSON.")
224
+ .addHelpText(
225
+ "after",
226
+ `
220
227
  Stores a JSON payload in the cache and prints the assigned key. The payload
221
228
  can be provided via --value, --file, or piped through stdin. If --key is
222
229
  provided, the entry is upserted (created or replaced). If omitted, a new
@@ -240,86 +247,86 @@ Examples:
240
247
  $ assistant cache set --file /tmp/payload.json --key scores --ttl 10m
241
248
  $ echo '{"scores":[98,85,72]}' | assistant cache set
242
249
  $ echo '"simple string"' | assistant cache set --ttl 1h --json`,
243
- )
244
- .action(
245
- async (opts: {
246
- key?: string;
247
- ttl?: string;
248
- value?: string;
249
- file?: string;
250
- json?: boolean;
251
- }) => {
252
- let data: unknown;
253
- try {
254
- data = resolvePayload(opts);
255
- } catch (err) {
256
- const msg = err instanceof Error ? err.message : String(err);
257
- if (opts.json) {
258
- process.stdout.write(
259
- JSON.stringify({ ok: false, error: msg }) + "\n",
260
- );
261
- } else {
262
- log.error(msg);
263
- }
264
- process.exitCode = 1;
265
- return;
266
- }
267
-
268
- let ttl_ms: number | undefined;
269
- try {
270
- ttl_ms = parseTtl(opts.ttl);
271
- } catch (err) {
272
- const msg = err instanceof Error ? err.message : String(err);
273
- if (opts.json) {
274
- process.stdout.write(
275
- JSON.stringify({ ok: false, error: msg }) + "\n",
276
- );
277
- } else {
278
- log.error(msg);
279
- }
280
- process.exitCode = 1;
281
- return;
282
- }
283
-
284
- const params: Record<string, unknown> = { data };
285
- if (ttl_ms !== undefined) params.ttl_ms = ttl_ms;
286
- if (opts.key) params.key = opts.key;
287
-
288
- const result = await cliIpcCall<{ key: string }>("cache_set", {
289
- body: params,
290
- });
291
-
292
- if (!result.ok) {
293
- if (opts.json) {
294
- process.stdout.write(
295
- JSON.stringify({ ok: false, error: result.error }) + "\n",
296
- );
297
- } else {
298
- log.error(`Error: ${result.error}`);
299
- }
300
- process.exitCode = 1;
301
- return;
302
- }
303
-
304
- if (opts.json) {
305
- process.stdout.write(
306
- JSON.stringify({ ok: true, key: result.result!.key }) + "\n",
307
- );
308
- } else {
309
- log.info(`Cached with key: ${result.result!.key}`);
310
- }
311
- },
312
- );
250
+ )
251
+ .action(
252
+ async (opts: {
253
+ key?: string;
254
+ ttl?: string;
255
+ value?: string;
256
+ file?: string;
257
+ json?: boolean;
258
+ }) => {
259
+ let data: unknown;
260
+ try {
261
+ data = resolvePayload(opts);
262
+ } catch (err) {
263
+ const msg = err instanceof Error ? err.message : String(err);
264
+ if (opts.json) {
265
+ process.stdout.write(
266
+ JSON.stringify({ ok: false, error: msg }) + "\n",
267
+ );
268
+ } else {
269
+ log.error(msg);
270
+ }
271
+ process.exitCode = 1;
272
+ return;
273
+ }
274
+
275
+ let ttl_ms: number | undefined;
276
+ try {
277
+ ttl_ms = parseTtl(opts.ttl);
278
+ } catch (err) {
279
+ const msg = err instanceof Error ? err.message : String(err);
280
+ if (opts.json) {
281
+ process.stdout.write(
282
+ JSON.stringify({ ok: false, error: msg }) + "\n",
283
+ );
284
+ } else {
285
+ log.error(msg);
286
+ }
287
+ process.exitCode = 1;
288
+ return;
289
+ }
290
+
291
+ const params: Record<string, unknown> = { data };
292
+ if (ttl_ms !== undefined) params.ttl_ms = ttl_ms;
293
+ if (opts.key) params.key = opts.key;
294
+
295
+ const result = await cliIpcCall<{ key: string }>("cache_set", {
296
+ body: params,
297
+ });
298
+
299
+ if (!result.ok) {
300
+ if (opts.json) {
301
+ process.stdout.write(
302
+ JSON.stringify({ ok: false, error: result.error }) + "\n",
303
+ );
304
+ } else {
305
+ log.error(`Error: ${result.error}`);
306
+ }
307
+ process.exitCode = 1;
308
+ return;
309
+ }
310
+
311
+ if (opts.json) {
312
+ process.stdout.write(
313
+ JSON.stringify({ ok: true, key: result.result!.key }) + "\n",
314
+ );
315
+ } else {
316
+ log.info(`Cached with key: ${result.result!.key}`);
317
+ }
318
+ },
319
+ );
313
320
 
314
- // ── get ───────────────────────────────────────────────────────────
321
+ // ── get ───────────────────────────────────────────────────────────
315
322
 
316
- cache
317
- .command("get <key>")
318
- .description("Retrieve a cached value by key")
319
- .option("--json", "Output result as machine-readable JSON.")
320
- .addHelpText(
321
- "after",
322
- `
323
+ cache
324
+ .command("get <key>")
325
+ .description("Retrieve a cached value by key")
326
+ .option("--json", "Output result as machine-readable JSON.")
327
+ .addHelpText(
328
+ "after",
329
+ `
323
330
  Arguments:
324
331
  key The cache key to look up. Run 'assistant cache set' to store a
325
332
  value and receive its key.
@@ -331,49 +338,52 @@ exist or has expired, reports not-found. In --json mode, a miss returns
331
338
  Examples:
332
339
  $ assistant cache get my-key
333
340
  $ assistant cache get my-key --json`,
334
- )
335
- .action(async (key: string, opts: { json?: boolean }) => {
336
- const result = await cliIpcCall<{ data: unknown } | null>("cache_get", {
337
- body: { key },
338
- });
339
-
340
- if (!result.ok) {
341
- if (opts.json) {
342
- process.stdout.write(
343
- JSON.stringify({ ok: false, error: result.error }) + "\n",
341
+ )
342
+ .action(async (key: string, opts: { json?: boolean }) => {
343
+ const result = await cliIpcCall<{ data: unknown } | null>(
344
+ "cache_get",
345
+ {
346
+ body: { key },
347
+ },
344
348
  );
345
- } else {
346
- log.error(`Error: ${result.error}`);
347
- }
348
- process.exitCode = 1;
349
- return;
350
- }
351
-
352
- if (opts.json) {
353
- process.stdout.write(
354
- JSON.stringify({
355
- ok: true,
356
- data: result.result ? result.result.data : null,
357
- }) + "\n",
358
- );
359
- } else {
360
- if (result.result == null) {
361
- log.info(`No cache entry found for key "${key}".`);
362
- } else {
363
- log.info(JSON.stringify(result.result.data, null, 2));
364
- }
365
- }
366
- });
367
-
368
- // ── delete ────────────────────────────────────────────────────────
369
-
370
- cache
371
- .command("delete <key>")
372
- .description("Remove a cached entry by key")
373
- .option("--json", "Output result as machine-readable JSON.")
374
- .addHelpText(
375
- "after",
376
- `
349
+
350
+ if (!result.ok) {
351
+ if (opts.json) {
352
+ process.stdout.write(
353
+ JSON.stringify({ ok: false, error: result.error }) + "\n",
354
+ );
355
+ } else {
356
+ log.error(`Error: ${result.error}`);
357
+ }
358
+ process.exitCode = 1;
359
+ return;
360
+ }
361
+
362
+ if (opts.json) {
363
+ process.stdout.write(
364
+ JSON.stringify({
365
+ ok: true,
366
+ data: result.result ? result.result.data : null,
367
+ }) + "\n",
368
+ );
369
+ } else {
370
+ if (result.result == null) {
371
+ log.info(`No cache entry found for key "${key}".`);
372
+ } else {
373
+ log.info(JSON.stringify(result.result.data, null, 2));
374
+ }
375
+ }
376
+ });
377
+
378
+ // ── delete ────────────────────────────────────────────────────────
379
+
380
+ cache
381
+ .command("delete <key>")
382
+ .description("Remove a cached entry by key")
383
+ .option("--json", "Output result as machine-readable JSON.")
384
+ .addHelpText(
385
+ "after",
386
+ `
377
387
  Arguments:
378
388
  key The cache key to remove. Run 'assistant cache get <key>' to
379
389
  verify a key exists before deleting.
@@ -384,36 +394,39 @@ existed or not, but reports whether an entry was actually removed.
384
394
  Examples:
385
395
  $ assistant cache delete my-key
386
396
  $ assistant cache delete my-key --json`,
387
- )
388
- .action(async (key: string, opts: { json?: boolean }) => {
389
- const result = await cliIpcCall<{ deleted: boolean }>("cache_delete", {
390
- body: { key },
391
- });
392
-
393
- if (!result.ok) {
394
- if (opts.json) {
395
- process.stdout.write(
396
- JSON.stringify({ ok: false, error: result.error }) + "\n",
397
+ )
398
+ .action(async (key: string, opts: { json?: boolean }) => {
399
+ const result = await cliIpcCall<{ deleted: boolean }>(
400
+ "cache_delete",
401
+ {
402
+ body: { key },
403
+ },
397
404
  );
398
- } else {
399
- log.error(`Error: ${result.error}`);
400
- }
401
- process.exitCode = 1;
402
- return;
403
- }
404
-
405
- const deleted = result.result!.deleted;
406
-
407
- if (opts.json) {
408
- process.stdout.write(JSON.stringify({ ok: true, deleted }) + "\n");
409
- } else {
410
- if (deleted) {
411
- log.info(`Deleted cache entry "${key}".`);
412
- } else {
413
- log.info(`No cache entry "${key}" (nothing to delete).`);
414
- }
415
- }
416
- });
405
+
406
+ if (!result.ok) {
407
+ if (opts.json) {
408
+ process.stdout.write(
409
+ JSON.stringify({ ok: false, error: result.error }) + "\n",
410
+ );
411
+ } else {
412
+ log.error(`Error: ${result.error}`);
413
+ }
414
+ process.exitCode = 1;
415
+ return;
416
+ }
417
+
418
+ const deleted = result.result!.deleted;
419
+
420
+ if (opts.json) {
421
+ process.stdout.write(JSON.stringify({ ok: true, deleted }) + "\n");
422
+ } else {
423
+ if (deleted) {
424
+ log.info(`Deleted cache entry "${key}".`);
425
+ } else {
426
+ log.info(`No cache entry "${key}" (nothing to delete).`);
427
+ }
428
+ }
429
+ });
417
430
  },
418
431
  });
419
432
  }
@@ -456,6 +456,9 @@ async function initSchema(): Promise<void> {
456
456
  clearStoredDb("memory");
457
457
  }
458
458
 
459
+ // Every test here runs initSchema(), which replays the full production
460
+ // migration suite, plus at least one repair pass. On a loaded machine that
461
+ // comfortably exceeds bun's 5s per-test default, so each gets a 30s ceiling.
459
462
  describe("assistant db repair — conversation-backfill step", () => {
460
463
  test("backfills a disk-only conversation into SQLite", async () => {
461
464
  await initSchema();
@@ -499,7 +502,7 @@ describe("assistant db repair — conversation-backfill step", () => {
499
502
  } finally {
500
503
  verify.close();
501
504
  }
502
- });
505
+ }, 30_000);
503
506
 
504
507
  test("skips conversations already present (idempotent)", async () => {
505
508
  await initSchema();
@@ -516,8 +519,6 @@ describe("assistant db repair — conversation-backfill step", () => {
516
519
  expect(parsed.steps[1].result.data.recovered).toBe(0);
517
520
  expect(parsed.steps[1].result.data.skipped).toBe(1);
518
521
  expect(parsed.steps[1].result.status).toBe("ok");
519
- // Two full repair passes (each walks the DB via integrity-check) exceed
520
- // bun's 5s per-test default, so raise the ceiling to keep CI stable.
521
522
  }, 30_000);
522
523
 
523
524
  test("reports nothing-to-backfill on an empty conversations dir", async () => {
@@ -529,7 +530,7 @@ describe("assistant db repair — conversation-backfill step", () => {
529
530
  expect(parsed.steps[1].result.status).toBe("ok");
530
531
  expect(parsed.steps[1].result.summary).toContain("nothing to backfill");
531
532
  expect(parsed.steps[1].result.data.recovered).toBe(0);
532
- });
533
+ }, 30_000);
533
534
 
534
535
  test("surfaces warnings for malformed meta.json without erroring the step", async () => {
535
536
  await initSchema();
@@ -547,5 +548,5 @@ describe("assistant db repair — conversation-backfill step", () => {
547
548
  w.includes("malformed meta.json"),
548
549
  ),
549
550
  ).toBe(true);
550
- });
551
+ }, 30_000);
551
552
  });
@@ -31,6 +31,7 @@ import type { Command } from "commander";
31
31
  import { getLogsDbPath } from "../../../util/logs-db-path.js";
32
32
  import { getMemoryDbPath } from "../../../util/memory-db-path.js";
33
33
  import { getDbPath } from "../../../util/platform.js";
34
+ import { getTelemetryDbPath } from "../../../util/telemetry-db-path.js";
34
35
  import { red } from "../../lib/cli-colors.js";
35
36
  import { shouldOutputJson, writeOutput } from "../../output.js";
36
37
  import {
@@ -99,6 +100,15 @@ interface StatusReport {
99
100
  */
100
101
  memoryFile?: FileFacts;
101
102
  memoryDb?: DbFacts | null;
103
+ /**
104
+ * The dedicated telemetry database (`assistant-telemetry.db`). Like
105
+ * `logsFile` and `memoryFile`, it is omitted from the report until the file
106
+ * exists on disk — it is created the first time the daemon opens its
107
+ * connection, so a fresh install that has never run the daemon won't have
108
+ * one.
109
+ */
110
+ telemetryFile?: FileFacts;
111
+ telemetryDb?: DbFacts | null;
102
112
  }
103
113
 
104
114
  interface MigrationSummary {
@@ -327,6 +337,13 @@ function renderHuman(report: StatusReport): string {
327
337
  out += renderDbBlock(report.memoryFile, report.memoryDb ?? null);
328
338
  }
329
339
 
340
+ // The secondary telemetry file is likewise only reported once it exists.
341
+ if (report.telemetryFile?.exists) {
342
+ out += "\n";
343
+ out += "Telemetry database (telemetry event tables)\n";
344
+ out += renderDbBlock(report.telemetryFile, report.telemetryDb ?? null);
345
+ }
346
+
330
347
  // Cross-database migration summary — the checkpoint ledger lives only in
331
348
  // the main DB, so this is a single block at the end covering all three.
332
349
  out += renderSummary(report.migration);
@@ -409,7 +426,13 @@ function readMigrationSummary(mainDbPath: string): MigrationSummary | null {
409
426
  // No table yet.
410
427
  }
411
428
 
412
- return { latestKey, latestUpdatedAtMs, appliedSteps, appliedRegistry, crashed };
429
+ return {
430
+ latestKey,
431
+ latestUpdatedAtMs,
432
+ appliedSteps,
433
+ appliedRegistry,
434
+ crashed,
435
+ };
413
436
  } finally {
414
437
  db.close();
415
438
  }
@@ -519,6 +542,17 @@ export function registerDbStatus(parent: Command): void {
519
542
  }
520
543
  }
521
544
 
545
+ // Same best-effort probe for the dedicated telemetry DB.
546
+ const telemetryFile = readFileFacts(getTelemetryDbPath());
547
+ let telemetryDb: DbFacts | null = null;
548
+ if (telemetryFile.exists) {
549
+ try {
550
+ telemetryDb = readDbFacts(telemetryFile.path);
551
+ } catch {
552
+ telemetryDb = null;
553
+ }
554
+ }
555
+
522
556
  // Cross-database migration summary from the main DB's checkpoint ledger.
523
557
  let migration: MigrationSummary | null = null;
524
558
  try {
@@ -535,6 +569,8 @@ export function registerDbStatus(parent: Command): void {
535
569
  logsDb,
536
570
  memoryFile,
537
571
  memoryDb,
572
+ telemetryFile,
573
+ telemetryDb,
538
574
  };
539
575
  if (shouldOutputJson(this)) {
540
576
  writeOutput(this, report);