@vellumai/assistant 0.10.9 → 0.10.10-dev.202607162206.d08e98e

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 (456) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/Dockerfile +8 -0
  3. package/docs/activation-funnel-telemetry.md +13 -7
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  7. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  8. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  9. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  10. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  11. package/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  12. package/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  13. package/openapi.yaml +530 -107
  14. package/package.json +1 -1
  15. package/scripts/generate-openapi.ts +8 -0
  16. package/src/__tests__/activation-early-marking.test.ts +6 -5
  17. package/src/__tests__/agent-loop-override-profile.test.ts +22 -25
  18. package/src/__tests__/agent-wake-override-profile.test.ts +21 -48
  19. package/src/__tests__/app-builder-tool-scripts.test.ts +0 -1
  20. package/src/__tests__/app-bundler.test.ts +4 -12
  21. package/src/__tests__/app-executors.test.ts +2 -49
  22. package/src/__tests__/app-routes-csp.test.ts +114 -146
  23. package/src/__tests__/auth-fallback-events-store.test.ts +8 -1
  24. package/src/__tests__/build-persisted-content.test.ts +96 -0
  25. package/src/__tests__/bundle-scanner.test.ts +27 -1
  26. package/src/__tests__/call-controller.test.ts +291 -0
  27. package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +165 -0
  28. package/src/__tests__/chat-credential-redaction.test.ts +1395 -0
  29. package/src/__tests__/chat-reveal-guard-priming.test.ts +791 -0
  30. package/src/__tests__/compaction.benchmark.test.ts +2 -1
  31. package/src/__tests__/compactor-image-manifest-trust.test.ts +50 -0
  32. package/src/__tests__/config-loader-backfill.test.ts +9 -4
  33. package/src/__tests__/config-schema-cmd.test.ts +10 -11
  34. package/src/__tests__/config-schema.test.ts +190 -257
  35. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +208 -0
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +5 -19
  37. package/src/__tests__/conversation-agent-loop.test.ts +22 -9
  38. package/src/__tests__/conversation-error.test.ts +31 -0
  39. package/src/__tests__/conversation-load-history-repair.test.ts +110 -0
  40. package/src/__tests__/conversation-process-callsite.test.ts +12 -19
  41. package/src/__tests__/conversation-routes-slash-commands.test.ts +7 -21
  42. package/src/__tests__/conversation-summarize-route.test.ts +36 -44
  43. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
  44. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +7 -7
  45. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -1
  46. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -1
  47. package/src/__tests__/conversation-usage.test.ts +4 -12
  48. package/src/__tests__/credential-routes.test.ts +250 -0
  49. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  50. package/src/__tests__/db-migration-rollback.test.ts +22 -0
  51. package/src/__tests__/empty-response-hook.test.ts +186 -1
  52. package/src/__tests__/external-plugin-loader.test.ts +11 -5
  53. package/src/__tests__/heartbeat-service.test.ts +0 -28
  54. package/src/__tests__/host-shell-tool.test.ts +2 -0
  55. package/src/__tests__/inactive-tool-error-messages.test.ts +2 -2
  56. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +52 -8
  57. package/src/__tests__/internal-telemetry-routes.test.ts +23 -4
  58. package/src/__tests__/invite-routes-http.test.ts +12 -16
  59. package/src/__tests__/list-all-apps.test.ts +0 -4
  60. package/src/__tests__/llm-context-resolution.test.ts +32 -56
  61. package/src/__tests__/llm-request-log-turn-query.test.ts +109 -0
  62. package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -52
  63. package/src/__tests__/llm-resolver.test.ts +342 -602
  64. package/src/__tests__/llm-schema.test.ts +79 -37
  65. package/src/__tests__/max-tokens-continue-hook.test.ts +19 -0
  66. package/src/__tests__/media-stream-output.test.ts +259 -3
  67. package/src/__tests__/media-stream-server-integration.test.ts +22 -1
  68. package/src/__tests__/media-stream-stt-session.test.ts +47 -0
  69. package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +33 -0
  70. package/src/__tests__/memory-recall-log-store.test.ts +47 -13
  71. package/src/__tests__/mock-gateway-ipc.ts +46 -1
  72. package/src/__tests__/mtime-cache.test.ts +61 -0
  73. package/src/__tests__/navigate-settings-tab.test.ts +2 -0
  74. package/src/__tests__/normalize-onboarding.test.ts +33 -0
  75. package/src/__tests__/onboarding-persona-write.test.ts +26 -0
  76. package/src/__tests__/plugin-api-resolve-credential.test.ts +140 -0
  77. package/src/__tests__/plugin-app-serve-routes.test.ts +166 -14
  78. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -0
  79. package/src/__tests__/post-turn-tool-result-truncation.test.ts +38 -0
  80. package/src/__tests__/provider-commit-message-generator.test.ts +27 -20
  81. package/src/__tests__/provider-connections-backfill.test.ts +138 -0
  82. package/src/__tests__/provider-platform-proxy-integration.test.ts +10 -31
  83. package/src/__tests__/provider-registry-ollama.test.ts +8 -18
  84. package/src/__tests__/provider-send-message-override-profile.test.ts +23 -23
  85. package/src/__tests__/provider-usage-tracking.test.ts +9 -19
  86. package/src/__tests__/prune-old-conversations-job.test.ts +12 -0
  87. package/src/__tests__/published-app-updater.test.ts +22 -16
  88. package/src/__tests__/registry.test.ts +5 -16
  89. package/src/__tests__/retry-openrouter-only-normalization.test.ts +22 -23
  90. package/src/__tests__/retry-thinking-adaptive-only.test.ts +43 -48
  91. package/src/__tests__/retry-thinking-tool-choice.test.ts +57 -66
  92. package/src/__tests__/retry-verbosity-normalization.test.ts +24 -23
  93. package/src/__tests__/reveal-success-registry.test.ts +123 -0
  94. package/src/__tests__/run-conversation-turn-persistence.test.ts +130 -0
  95. package/src/__tests__/secret-fixtures.ts +9 -0
  96. package/src/__tests__/server-history-render.test.ts +28 -0
  97. package/src/__tests__/skills.test.ts +9 -4
  98. package/src/__tests__/slack-share-routes.test.ts +0 -1
  99. package/src/__tests__/stt-stream-session.test.ts +6 -5
  100. package/src/__tests__/subagent-call-site-routing.test.ts +69 -95
  101. package/src/__tests__/subagent-disposal.test.ts +2 -0
  102. package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
  103. package/src/__tests__/subagent-fork-spawn.test.ts +2 -0
  104. package/src/__tests__/subagent-manager-notify.test.ts +2 -0
  105. package/src/__tests__/subagent-role-registry.test.ts +37 -0
  106. package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
  107. package/src/__tests__/subagent-terminal-message.test.ts +50 -0
  108. package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -5
  109. package/src/__tests__/surface-completion-nudge-hook.test.ts +19 -0
  110. package/src/__tests__/telemetry-routes.test.ts +99 -19
  111. package/src/__tests__/tool-audit.test.ts +34 -4
  112. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  113. package/src/__tests__/tool-profiler.test.ts +72 -1
  114. package/src/__tests__/tool-result-spool.test.ts +49 -4
  115. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -1
  116. package/src/__tests__/ui-channel-variants.test.ts +108 -0
  117. package/src/__tests__/ui-shape-teaching.test.ts +255 -0
  118. package/src/__tests__/usage-attribution.test.ts +18 -41
  119. package/src/__tests__/user-plugin-loader.test.ts +4 -4
  120. package/src/__tests__/voice-config-update.test.ts +46 -0
  121. package/src/__tests__/voice-session-bridge.test.ts +216 -84
  122. package/src/__tests__/workspace-migration-131-drop-web-fetch-mode.test.ts +120 -0
  123. package/src/agent/loop.ts +4 -3
  124. package/src/api/events/open-conversation.test.ts +64 -0
  125. package/src/api/events/open-conversation.ts +33 -0
  126. package/src/api/index.ts +6 -0
  127. package/src/api/responses/conversation-message.ts +5 -0
  128. package/src/apps/app-store.ts +25 -35
  129. package/src/bundler/app-bundler.ts +34 -48
  130. package/src/bundler/app-compiler.ts +39 -4
  131. package/src/bundler/bundle-scanner.ts +13 -0
  132. package/src/bundler/manifest.ts +1 -1
  133. package/src/calls/__tests__/voice-session-bridge.test.ts +47 -0
  134. package/src/calls/call-constants.ts +5 -0
  135. package/src/calls/call-controller.ts +100 -32
  136. package/src/calls/call-transport.ts +9 -0
  137. package/src/calls/media-stream-output.ts +107 -4
  138. package/src/calls/media-stream-server.ts +29 -9
  139. package/src/calls/media-stream-stt-session.ts +7 -1
  140. package/src/calls/media-turn-detector.ts +11 -1
  141. package/src/calls/voice-session-bridge.ts +84 -63
  142. package/src/cli/commands/__tests__/inference-providers.test.ts +270 -33
  143. package/src/cli/commands/__tests__/notifications.test.ts +24 -3
  144. package/src/cli/commands/config.help.ts +6 -6
  145. package/src/cli/commands/credentials.help.ts +13 -0
  146. package/src/cli/commands/credentials.ts +6 -1
  147. package/src/cli/commands/email.help.ts +7 -0
  148. package/src/cli/commands/email.ts +35 -1
  149. package/src/cli/commands/inference-providers.ts +168 -107
  150. package/src/cli/commands/inference.help.ts +118 -46
  151. package/src/cli/commands/memory/index.help.ts +23 -0
  152. package/src/cli/commands/memory/nodes.ts +146 -0
  153. package/src/cli/commands/notifications.help.ts +10 -10
  154. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +40 -0
  155. package/src/cli/commands/oauth/connect-surface-guidance.ts +54 -0
  156. package/src/cli/commands/oauth/connect.test.ts +126 -0
  157. package/src/cli/commands/oauth/connect.ts +29 -6
  158. package/src/cli/commands/oauth/index.help.ts +7 -1
  159. package/src/cli/commands/oauth/status.test.ts +69 -3
  160. package/src/cli/commands/oauth/status.ts +50 -12
  161. package/src/cli/commands/plugins.help.ts +13 -2
  162. package/src/cli/commands/plugins.ts +61 -7
  163. package/src/cli/commands/telemetry.help.ts +13 -0
  164. package/src/cli/commands/telemetry.ts +45 -5
  165. package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +8 -2
  166. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +213 -9
  167. package/src/cli/lib/bundled-marketplace.json +93 -36
  168. package/src/cli/lib/inspect-plugin.ts +5 -1
  169. package/src/cli/lib/install-from-github.ts +68 -4
  170. package/src/cli/lib/plugin-catalog-local.ts +6 -2
  171. package/src/cli/lib/plugin-fingerprint.ts +3 -3
  172. package/src/cli/lib/upgrade-plugin.ts +236 -35
  173. package/src/config/__tests__/default-profile-catalog.test.ts +8 -12
  174. package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +137 -0
  175. package/src/config/__tests__/profile-materialization.test.ts +1 -88
  176. package/src/config/bundled-skills/AGENTS.md +3 -30
  177. package/src/config/bundled-skills/app-builder/SKILL.md +5 -3
  178. package/src/config/bundled-skills/app-builder/TOOLS.json +23 -0
  179. package/src/config/bundled-skills/app-builder/tools/app-open.ts +32 -0
  180. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  181. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +7 -7
  182. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  183. package/src/config/bundled-skills/schedule/TOOLS.json +1 -1
  184. package/src/config/bundled-skills/settings/TOOLS.json +3 -1
  185. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +2 -0
  186. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +18 -10
  187. package/src/config/bundled-skills/subagent/SKILL.md +2 -0
  188. package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
  189. package/src/config/call-site-defaults.ts +3 -3
  190. package/src/config/feature-flag-registry.json +24 -39
  191. package/src/config/llm-resolver.ts +88 -475
  192. package/src/config/profile-materialization.ts +11 -11
  193. package/src/config/schema.ts +93 -127
  194. package/src/config/schemas/__tests__/live-voice.test.ts +24 -6
  195. package/src/config/schemas/__tests__/stt.test.ts +31 -3
  196. package/src/config/schemas/live-voice.ts +10 -4
  197. package/src/config/schemas/llm.ts +52 -15
  198. package/src/config/schemas/memory-lifecycle.ts +1 -1
  199. package/src/config/schemas/memory-retrospective.ts +8 -0
  200. package/src/config/schemas/memory-v2.ts +2 -2
  201. package/src/config/schemas/memory-v3.ts +1 -1
  202. package/src/config/schemas/services.ts +6 -3
  203. package/src/config/schemas/stt.ts +38 -21
  204. package/src/config/schemas/tts.ts +13 -18
  205. package/src/config/skills.ts +31 -21
  206. package/src/context/compactor.ts +44 -7
  207. package/src/context/post-turn-tool-result-truncation.ts +4 -2
  208. package/src/context/tool-result-spool.ts +26 -5
  209. package/src/conversations/__tests__/message-consolidation.test.ts +48 -0
  210. package/src/conversations/message-consolidation.ts +22 -2
  211. package/src/daemon/__tests__/conversation-tool-setup.test.ts +5 -12
  212. package/src/daemon/app-source-watcher.ts +17 -23
  213. package/src/daemon/chat-credential-redaction.ts +1365 -0
  214. package/src/daemon/conversation-agent-loop-handlers.ts +627 -34
  215. package/src/daemon/conversation-agent-loop.ts +71 -31
  216. package/src/daemon/conversation-error.ts +36 -14
  217. package/src/daemon/conversation-process.ts +22 -0
  218. package/src/daemon/conversation-store.ts +35 -0
  219. package/src/daemon/conversation-surfaces.ts +25 -25
  220. package/src/daemon/conversation-tool-setup.ts +33 -7
  221. package/src/daemon/conversation.ts +55 -5
  222. package/src/daemon/handlers/shared.ts +33 -2
  223. package/src/daemon/lifecycle.ts +4 -4
  224. package/src/daemon/message-types/conversations.ts +6 -17
  225. package/src/daemon/providers-setup.ts +8 -0
  226. package/src/daemon/tool-setup-types.ts +7 -1
  227. package/src/daemon/wake-conversation-ops.ts +10 -1
  228. package/src/hooks/types.ts +9 -0
  229. package/src/ipc/__tests__/email-ipc.test.ts +90 -0
  230. package/src/ipc/gateway-client.test.ts +59 -0
  231. package/src/ipc/gateway-client.ts +70 -27
  232. package/src/live-voice/__tests__/live-voice-events.test.ts +14 -2
  233. package/src/live-voice/__tests__/live-voice-integration.test.ts +116 -2
  234. package/src/live-voice/__tests__/live-voice-vad.test.ts +804 -13
  235. package/src/live-voice/__tests__/protocol.test.ts +122 -0
  236. package/src/live-voice/live-voice-session.ts +443 -40
  237. package/src/live-voice/protocol.ts +143 -1
  238. package/src/monitoring/__tests__/plugin-source-watch.test.ts +3 -1
  239. package/src/monitoring/plugin-source-watch.ts +3 -62
  240. package/src/notifications/README.md +1 -1
  241. package/src/permissions/checker.ts +8 -4
  242. package/src/persistence/__tests__/db-init-migrations-ok.test.ts +26 -0
  243. package/src/persistence/conversation-crud.ts +104 -2
  244. package/src/persistence/db-init.ts +14 -3
  245. package/src/persistence/job-handlers/cleanup.ts +15 -7
  246. package/src/persistence/llm-request-log-store.ts +88 -52
  247. package/src/persistence/migrations/298-move-memory-jobs-to-memory-db.ts +7 -31
  248. package/src/persistence/migrations/305-drop-contact-acl-columns.ts +3 -2
  249. package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +8 -34
  250. package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +90 -0
  251. package/src/persistence/migrations/337-move-memory-recall-logs-to-memory-db.ts +114 -0
  252. package/src/persistence/migrations/338-move-memory-v3-selections-to-memory-db.ts +84 -0
  253. package/src/persistence/migrations/339-move-activation-sessions-to-memory-db.ts +52 -0
  254. package/src/persistence/migrations/__tests__/run-migrations.test.ts +155 -0
  255. package/src/persistence/migrations/helpers/relocation.ts +44 -1
  256. package/src/persistence/migrations/run-migrations.ts +25 -1
  257. package/src/persistence/schema/infrastructure.ts +9 -0
  258. package/src/persistence/schema/memory-core.ts +3 -0
  259. package/src/persistence/schema/memory-injection.ts +2 -0
  260. package/src/persistence/steps.ts +36 -0
  261. package/src/platform/client.test.ts +1 -44
  262. package/src/platform/client.ts +10 -20
  263. package/src/platform/consent-cache.test.ts +89 -23
  264. package/src/platform/consent-cache.ts +71 -33
  265. package/src/plugin-api/constants.ts +12 -0
  266. package/src/plugin-api/conversation-turn.ts +37 -14
  267. package/src/plugin-api/index.ts +11 -1
  268. package/src/plugin-api/resolve-credential.ts +75 -0
  269. package/src/plugin-api/vision-support.test.ts +8 -19
  270. package/src/plugin-api/vision-support.ts +26 -17
  271. package/src/plugins/collect-source-versions.ts +77 -0
  272. package/src/plugins/defaults/compaction/compact.ts +6 -0
  273. package/src/plugins/defaults/compaction/window-manager.ts +9 -0
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +18 -24
  275. package/src/plugins/defaults/empty-response/hooks/user-prompt-submit.ts +38 -0
  276. package/src/plugins/defaults/empty-response/refusal-quarantine.ts +99 -0
  277. package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +7 -3
  278. package/src/plugins/defaults/index.ts +8 -1
  279. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +4 -1
  280. package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +48 -6
  281. package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +28 -22
  282. package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +148 -0
  283. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +80 -8
  284. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +202 -0
  285. package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +64 -25
  286. package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +24 -11
  287. package/src/plugins/defaults/memory/__tests__/prompt-override.test.ts +70 -6
  288. package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +278 -0
  289. package/src/plugins/defaults/memory/activation-session-store.ts +27 -20
  290. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -9
  291. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -8
  292. package/src/plugins/defaults/memory/graph/retriever.test.ts +6 -6
  293. package/src/plugins/defaults/memory/graph/store.ts +114 -0
  294. package/src/plugins/defaults/memory/graph/tool-handlers.ts +36 -1
  295. package/src/plugins/defaults/memory/graph/tools.ts +47 -13
  296. package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +16 -35
  297. package/src/plugins/defaults/memory/jobs-worker.ts +10 -1
  298. package/src/plugins/defaults/memory/memory-db.ts +3 -2
  299. package/src/plugins/defaults/memory/memory-recall-log-store.ts +129 -66
  300. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +8 -0
  301. package/src/plugins/defaults/memory/memory-retrospective-job.ts +11 -122
  302. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +216 -0
  303. package/src/plugins/defaults/memory/memory-v2-activation-log-store.ts +84 -46
  304. package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +42 -32
  305. package/src/plugins/defaults/memory/path-containment.ts +21 -0
  306. package/src/plugins/defaults/memory/prompt-override.ts +51 -13
  307. package/src/plugins/defaults/memory/tools.test.ts +34 -0
  308. package/src/plugins/defaults/memory/tools.ts +12 -2
  309. package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +19 -15
  310. package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +24 -19
  311. package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +19 -15
  312. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +22 -2
  313. package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +5 -3
  314. package/src/plugins/defaults/memory/v2/harness/oracle.ts +59 -41
  315. package/src/plugins/defaults/memory/v2/harness/replay-input.ts +29 -25
  316. package/src/plugins/defaults/memory/v2/migration.ts +46 -16
  317. package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +4 -0
  318. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +17 -15
  319. package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +24 -22
  320. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +9 -5
  321. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +277 -10
  322. package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +57 -5
  323. package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +9 -7
  324. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +38 -7
  325. package/src/plugins/defaults/memory/v3/hot-set.test.ts +36 -18
  326. package/src/plugins/defaults/memory/v3/hot-set.ts +12 -14
  327. package/src/plugins/defaults/memory/v3/learned-edges.test.ts +47 -27
  328. package/src/plugins/defaults/memory/v3/learned-edges.ts +12 -14
  329. package/src/plugins/defaults/memory/v3/orchestrate.ts +139 -22
  330. package/src/plugins/defaults/memory/v3/prune.test.ts +10 -3
  331. package/src/plugins/defaults/memory/v3/prune.ts +17 -11
  332. package/src/plugins/defaults/memory/v3/selection-log-store.ts +23 -11
  333. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +70 -53
  334. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +9 -6
  335. package/src/plugins/external-plugin-loader.ts +15 -15
  336. package/src/plugins/mtime-cache.ts +47 -0
  337. package/src/plugins/pipeline.ts +9 -1
  338. package/src/plugins/plugin-execution-context.ts +44 -0
  339. package/src/plugins/plugin-tree-walk.ts +31 -24
  340. package/src/plugins/source-fingerprint.ts +3 -4
  341. package/src/prompts/normalize-onboarding.ts +12 -0
  342. package/src/prompts/persona-resolver.ts +8 -0
  343. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +1 -1
  344. package/src/providers/__tests__/dispatch-connection-routing.test.ts +6 -9
  345. package/src/providers/__tests__/registry-native-web-search.test.ts +4 -10
  346. package/src/providers/__tests__/retry-callsite.test.ts +235 -215
  347. package/src/providers/__tests__/satellite-connection-routing.test.ts +8 -16
  348. package/src/providers/atlascloud/client.ts +10 -49
  349. package/src/providers/baseten/client.ts +43 -0
  350. package/src/providers/call-site-routing.ts +26 -13
  351. package/src/providers/connection-resolution.ts +9 -11
  352. package/src/providers/fetch-provider-catalog.ts +4 -2
  353. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +29 -4
  354. package/src/providers/inference/__tests__/connection-availability-keyless.test.ts +78 -0
  355. package/src/providers/inference/adapter-factory.ts +27 -6
  356. package/src/providers/inference/auth.ts +29 -1
  357. package/src/providers/inference/backfill.ts +61 -10
  358. package/src/providers/inference/connection-availability.ts +3 -2
  359. package/src/providers/inference/resolve-auth.ts +9 -1
  360. package/src/providers/model-catalog.ts +37 -0
  361. package/src/providers/openai/__tests__/api-error-normalization.test.ts +24 -2
  362. package/src/providers/openai/api-key-validation.ts +70 -0
  363. package/src/providers/retry.ts +2 -0
  364. package/src/providers/types.ts +11 -4
  365. package/src/providers/vellum-model-routing.test.ts +26 -0
  366. package/src/providers/vellum-model-routing.ts +30 -0
  367. package/src/providers/voice-error-copy.ts +47 -0
  368. package/src/runtime/agent-wake.ts +40 -24
  369. package/src/runtime/for-chat-mint-registry.ts +118 -0
  370. package/src/runtime/reveal-nonce.ts +49 -0
  371. package/src/runtime/reveal-success-registry.ts +306 -0
  372. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +88 -3
  373. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +161 -30
  374. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +7 -0
  375. package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +15 -0
  376. package/src/runtime/routes/app-management-routes.ts +152 -45
  377. package/src/runtime/routes/app-routes.ts +16 -77
  378. package/src/runtime/routes/canned-message-complete.ts +16 -16
  379. package/src/runtime/routes/conversation-management-routes.ts +17 -14
  380. package/src/runtime/routes/conversation-query-routes.ts +54 -6
  381. package/src/runtime/routes/conversation-routes.ts +48 -2
  382. package/src/runtime/routes/credential-routes.ts +116 -3
  383. package/src/runtime/routes/email-routes.ts +17 -1
  384. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +33 -5
  385. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +6 -5
  386. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -28
  387. package/src/runtime/routes/inference-send-routes.ts +1 -1
  388. package/src/runtime/routes/internal-telemetry-routes.ts +7 -6
  389. package/src/runtime/routes/plugins-routes.ts +37 -6
  390. package/src/runtime/routes/publish-routes.ts +15 -18
  391. package/src/runtime/routes/schedule-worker-routes.ts +9 -0
  392. package/src/runtime/routes/secret-routes.ts +10 -0
  393. package/src/runtime/routes/telemetry-routes.ts +149 -45
  394. package/src/schedule/__tests__/schedule-timezone.test.ts +101 -0
  395. package/src/schedule/__tests__/worker-watchdog.test.ts +209 -0
  396. package/src/schedule/schedule-store.ts +12 -1
  397. package/src/schedule/schedule-timezone.ts +63 -0
  398. package/src/schedule/scheduler.ts +100 -6
  399. package/src/schedule/worker-control.ts +19 -0
  400. package/src/security/auth-fallback-events-store.ts +7 -6
  401. package/src/security/secret-scanner.ts +26 -1
  402. package/src/services/published-app-updater.ts +6 -11
  403. package/src/stt/stt-stream-session.ts +36 -16
  404. package/src/subagent/manager.ts +34 -8
  405. package/src/telemetry/AGENTS.md +30 -1
  406. package/src/telemetry/__tests__/config-setting-snapshot.test.ts +18 -0
  407. package/src/telemetry/__tests__/outbox-test-harness.ts +5 -3
  408. package/src/telemetry/config-setting-snapshot.ts +44 -10
  409. package/src/telemetry/telemetry-event-sources.test.ts +124 -30
  410. package/src/telemetry/telemetry-event-sources.ts +137 -83
  411. package/src/telemetry/telemetry-events-outbox.test.ts +46 -1
  412. package/src/telemetry/telemetry-events-outbox.ts +60 -9
  413. package/src/telemetry/telemetry-wire-source.json +1 -1
  414. package/src/telemetry/telemetry-wire-validation.ts +39 -2
  415. package/src/telemetry/telemetry-wire.generated.ts +8 -0
  416. package/src/telemetry/tool-audit.ts +15 -9
  417. package/src/telemetry/tool-executed-events-store.test.ts +1 -1
  418. package/src/telemetry/turn-events-store.ts +20 -0
  419. package/src/telemetry/types.ts +45 -12
  420. package/src/telemetry/usage-telemetry-reporter.test.ts +294 -25
  421. package/src/telemetry/usage-telemetry-reporter.ts +117 -18
  422. package/src/telemetry/watchdog-direct-emit.test.ts +11 -3
  423. package/src/telemetry/watchdog-direct-emit.ts +11 -6
  424. package/src/tools/apps/executors.ts +11 -36
  425. package/src/tools/executor.ts +14 -1
  426. package/src/tools/host-terminal/host-shell.ts +6 -2
  427. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +1 -1
  428. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +25 -0
  429. package/src/tools/network/web-fetch.ts +6 -2
  430. package/src/tools/skills/sandbox-runner.ts +5 -2
  431. package/src/tools/subagent/spawn.ts +7 -11
  432. package/src/tools/terminal/shell.ts +5 -2
  433. package/src/tools/tool-manifest.ts +0 -2
  434. package/src/tools/tool-profiler.ts +37 -6
  435. package/src/tools/ui-surface/channel-variants.ts +101 -0
  436. package/src/tools/ui-surface/definitions.ts +23 -67
  437. package/src/tools/ui-surface/surface-shape-docs.ts +229 -0
  438. package/src/tts/__tests__/provider-adapters.test.ts +18 -0
  439. package/src/tts/provider-catalog.ts +2 -4
  440. package/src/tts/providers/deepgram-provider.ts +10 -1
  441. package/src/types/onboarding-context.ts +8 -0
  442. package/src/usage/attribution.ts +18 -112
  443. package/src/{config/bundled-skills/messaging/tools/gmail-mime-helpers.ts → util/mime-type.ts} +4 -1
  444. package/src/util/provider-error-patterns.ts +7 -1
  445. package/src/util/worker-process.ts +105 -0
  446. package/src/watcher/__tests__/telemetry.test.ts +17 -4
  447. package/src/watcher/telemetry.ts +5 -5
  448. package/src/workspace/migrations/131-drop-web-fetch-mode.ts +61 -0
  449. package/src/workspace/migrations/registry.ts +2 -0
  450. package/src/workspace/provider-commit-message-generator.ts +6 -4
  451. package/src/__tests__/app-open-proxy.test.ts +0 -67
  452. package/src/onboarding/onboarding-research-events-store.test.ts +0 -230
  453. package/src/onboarding/onboarding-research-events-store.ts +0 -104
  454. package/src/runtime/routes/assets/vellum-design-system.css +0 -2236
  455. package/src/tools/apps/definitions.ts +0 -73
  456. package/src/tools/apps/open-proxy.ts +0 -43
@@ -883,3 +883,117 @@ export function getNodeEditHistory(
883
883
  .limit(limit)
884
884
  .all();
885
885
  }
886
+
887
+ // ---------------------------------------------------------------------------
888
+ // Graph stats
889
+ // ---------------------------------------------------------------------------
890
+
891
+ /** Fidelity counts for active (non-gone) nodes. */
892
+ export interface FidelityBreakdown {
893
+ vivid: number;
894
+ clear: number;
895
+ faded: number;
896
+ gist: number;
897
+ }
898
+
899
+ /** Aggregate health snapshot of the memory v2 graph. */
900
+ export interface GraphStats {
901
+ /** Total active (non-gone) node count. */
902
+ total: number;
903
+ /** Node count by MemoryType. */
904
+ byType: Partial<Record<MemoryType, number>>;
905
+ /** Node count by fidelity, excluding gone. */
906
+ byFidelity: FidelityBreakdown;
907
+ /** Nodes with significance < 0.15 (at risk of decaying away). */
908
+ atRisk: number;
909
+ /** Live edge count — both endpoints are non-gone. */
910
+ edgeCount: number;
911
+ /** Epoch ms of the oldest active node, or null if the graph is empty. */
912
+ oldestCreated: number | null;
913
+ /** Epoch ms of the newest active node, or null if the graph is empty. */
914
+ newestCreated: number | null;
915
+ /** Epoch ms of the most recent reinforcement event, or null if none. */
916
+ lastReinforced: number | null;
917
+ /** Mean significance across all active nodes (0–1). */
918
+ avgSignificance: number;
919
+ /** Up to 5 highest-significance nodes (content, significance, fidelity, type). */
920
+ topNodes: Array<{
921
+ content: string;
922
+ significance: number;
923
+ fidelity: string;
924
+ type: string;
925
+ }>;
926
+ }
927
+
928
+ /**
929
+ * Compute a health snapshot of the memory v2 graph in two queries:
930
+ * 1. A full scan of active nodes (significance DESC) to derive all
931
+ * per-node aggregates in one JS pass.
932
+ * 2. A SQL COUNT of live edges (both endpoints non-gone).
933
+ *
934
+ * Callers that only need the summary number can use `countNodes()` instead.
935
+ */
936
+ export function computeGraphStats(): GraphStats {
937
+ // Full scan — significance DESC so topNodes is just the first slice.
938
+ const nodes = queryNodes({ fidelityNot: ["gone"] });
939
+
940
+ const byType: Partial<Record<MemoryType, number>> = {};
941
+ const byFidelity: FidelityBreakdown = {
942
+ vivid: 0,
943
+ clear: 0,
944
+ faded: 0,
945
+ gist: 0,
946
+ };
947
+ let atRisk = 0;
948
+ let sigSum = 0;
949
+ let oldestCreated: number | null = null;
950
+ let newestCreated: number | null = null;
951
+ let lastReinforced: number | null = null;
952
+
953
+ for (const n of nodes) {
954
+ byType[n.type] = (byType[n.type] ?? 0) + 1;
955
+ if (n.fidelity !== "gone") {
956
+ byFidelity[n.fidelity as keyof FidelityBreakdown] += 1;
957
+ }
958
+ if (n.significance < 0.15) atRisk++;
959
+ sigSum += n.significance;
960
+ if (oldestCreated === null || n.created < oldestCreated)
961
+ oldestCreated = n.created;
962
+ if (newestCreated === null || n.created > newestCreated)
963
+ newestCreated = n.created;
964
+ if (lastReinforced === null || n.lastReinforced > lastReinforced)
965
+ lastReinforced = n.lastReinforced;
966
+ }
967
+
968
+ // Live edge count: both endpoints must be non-gone.
969
+ const db = getDb();
970
+ const edgeResult = db
971
+ .select({ count: sql<number>`count(*)` })
972
+ .from(memoryGraphEdges)
973
+ .where(
974
+ and(
975
+ sql`NOT EXISTS (SELECT 1 FROM ${memoryGraphNodes} WHERE ${memoryGraphNodes.id} = ${memoryGraphEdges.sourceNodeId} AND ${memoryGraphNodes.fidelity} = 'gone')`,
976
+ sql`NOT EXISTS (SELECT 1 FROM ${memoryGraphNodes} WHERE ${memoryGraphNodes.id} = ${memoryGraphEdges.targetNodeId} AND ${memoryGraphNodes.fidelity} = 'gone')`,
977
+ ),
978
+ )
979
+ .get();
980
+
981
+ return {
982
+ total: nodes.length,
983
+ byType,
984
+ byFidelity,
985
+ atRisk,
986
+ edgeCount: edgeResult?.count ?? 0,
987
+ oldestCreated,
988
+ newestCreated,
989
+ lastReinforced,
990
+ avgSignificance: nodes.length > 0 ? sigSum / nodes.length : 0,
991
+ // nodes is already sorted significance DESC — slice gives the top N.
992
+ topNodes: nodes.slice(0, 5).map((n) => ({
993
+ content: n.content,
994
+ significance: n.significance,
995
+ fidelity: n.fidelity,
996
+ type: n.type,
997
+ })),
998
+ };
999
+ }
@@ -14,7 +14,14 @@ import { enqueueMemoryJob } from "../../../../persistence/jobs-store.js";
14
14
  import { enqueuePkbIndexJob } from "../jobs/embed-pkb-file.js";
15
15
  import { getLogger } from "../logging.js";
16
16
  import { getWorkspaceDir } from "../paths.js";
17
- import { deleteNode, queryNodes, recordNodeEdit, updateNode } from "./store.js";
17
+ import type { GraphStats } from "./store.js";
18
+ import {
19
+ computeGraphStats,
20
+ deleteNode,
21
+ queryNodes,
22
+ recordNodeEdit,
23
+ updateNode,
24
+ } from "./store.js";
18
25
 
19
26
  const log = getLogger("graph-tool-handlers");
20
27
 
@@ -429,3 +436,31 @@ export function handleListMemory(
429
436
  total: filtered.length,
430
437
  };
431
438
  }
439
+
440
+ // ---------------------------------------------------------------------------
441
+ // handleStatsMemory
442
+ // ---------------------------------------------------------------------------
443
+
444
+ export interface StatsMemoryResult {
445
+ success: boolean;
446
+ message: string;
447
+ stats: GraphStats | null;
448
+ }
449
+
450
+ export function handleStatsMemory(config: AssistantConfig): StatsMemoryResult {
451
+ if (!config.memory.v2.enabled) {
452
+ return {
453
+ success: false,
454
+ message: "stats requires memory v2.",
455
+ stats: null,
456
+ };
457
+ }
458
+ const stats = computeGraphStats();
459
+ const n = stats.total;
460
+ const e = stats.edgeCount;
461
+ return {
462
+ success: true,
463
+ message: `${n} active node${n === 1 ? "" : "s"}, ${e} edge${e === 1 ? "" : "s"}.`,
464
+ stats,
465
+ };
466
+ }
@@ -62,18 +62,33 @@ export const graphRecallDefinition = {
62
62
  const REMEMBER_DESCRIPTION =
63
63
  "Remember anything concrete shared in conversation: corrections, plans, decisions, felt moments, names, dates, commitments, preferences. Corrections are the highest priority — call `remember` the same turn the correction lands. You don't have to call this on every turn; a retrospective pass reviews the conversation after each message-count / time interval and saves what you didn't capture. Use judgment: pause and remember when something feels worth marking, not because the volume is required.";
64
64
 
65
+ const REMEMBER_CONTENT_DESCRIPTION =
66
+ "The fact(s) to remember. Pass a single string for one fact, or an array of strings to record several independent facts in one call. When a turn surfaces multiple unrelated facts, pass them all as an array in one call rather than calling `remember` once per fact. Write naturally — a preference, a detail, a commitment, a plan. No need to categorize.";
67
+
65
68
  /**
66
- * Save a fact to the assistant's knowledge base. The fact is appended to
67
- * `buffer.md` (immediately available in the next conversation) and the daily
68
- * archive (permanent date-indexed record). When `memory.v2.enabled` is true,
69
- * writes go under `memory/`; otherwise they go under `pkb/`. Consolidation
70
- * of the buffer into longer-form storage runs as a separate periodic job in
71
- * both modes.
69
+ * Appended to the `content` description only under the wiki memory model
70
+ * (memory v2/v3), where the v2/v3 consolidation prompts treat `[[slug]]`
71
+ * hints as read-first candidates when filing buffer entries. v1/PKB
72
+ * workspaces have no wiki pages for the hints to reference, and the pkb
73
+ * filing job has no instruction to interpret or strip the markup, so it
74
+ * would persist as literal buffer text there.
72
75
  */
73
- export const graphRememberDefinition = {
74
- name: "remember",
75
- description: REMEMBER_DESCRIPTION,
76
- input_schema: {
76
+ export const REMEMBER_PAGE_HINT_GUIDANCE =
77
+ "When a fact relates to memory pages already in your context, reference the most specific ones inline as [[slug]] wikilinks — consolidation reads hinted pages first when filing the fact, which matters most for corrections (the hint names the page carrying the outdated fact). Hint only pages you have actually seen, and prefer specific pages over broad hubs.";
78
+
79
+ /**
80
+ * Build the `remember` input schema. `pageHints` reflects the wiki-memory
81
+ * (`memory.v2.enabled`) state and appends
82
+ * {@link REMEMBER_PAGE_HINT_GUIDANCE} to the `content` description. It is a
83
+ * thunk re-resolved on every read of that description: the registry's
84
+ * finalized tool shares the returned schema object by reference, so a
85
+ * runtime config edit is reflected on the next schema serialization without
86
+ * re-registering the tool.
87
+ */
88
+ export function buildRememberInputSchema(options: {
89
+ pageHints: () => boolean;
90
+ }) {
91
+ return {
77
92
  type: "object",
78
93
  properties: {
79
94
  content: {
@@ -81,8 +96,11 @@ export const graphRememberDefinition = {
81
96
  { type: "string" },
82
97
  { type: "array", items: { type: "string" }, minItems: 1 },
83
98
  ],
84
- description:
85
- "The fact(s) to remember. Pass a single string for one fact, or an array of strings to record several independent facts in one call. When a turn surfaces multiple unrelated facts, pass them all as an array in one call rather than calling `remember` once per fact. Write naturally — a preference, a detail, a commitment, a plan. No need to categorize.",
99
+ get description(): string {
100
+ return options.pageHints()
101
+ ? `${REMEMBER_CONTENT_DESCRIPTION} ${REMEMBER_PAGE_HINT_GUIDANCE}`
102
+ : REMEMBER_CONTENT_DESCRIPTION;
103
+ },
86
104
  },
87
105
  finish_turn: {
88
106
  type: "boolean",
@@ -91,5 +109,21 @@ export const graphRememberDefinition = {
91
109
  },
92
110
  },
93
111
  required: ["content"],
94
- },
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Save a fact to the assistant's knowledge base. The fact is appended to
117
+ * `buffer.md` (immediately available in the next conversation) and the daily
118
+ * archive (permanent date-indexed record). When `memory.v2.enabled` is true,
119
+ * writes go under `memory/`; otherwise they go under `pkb/`. Consolidation
120
+ * of the buffer into longer-form storage runs as a separate periodic job in
121
+ * both modes. This base definition carries the mode-neutral schema; the
122
+ * registered tool appends the page-hint guidance on wiki-memory installs via
123
+ * {@link buildRememberInputSchema}.
124
+ */
125
+ export const graphRememberDefinition = {
126
+ name: "remember",
127
+ description: REMEMBER_DESCRIPTION,
128
+ input_schema: buildRememberInputSchema({ pageHints: () => false }),
95
129
  } satisfies ToolDefinition;
@@ -17,7 +17,6 @@
17
17
 
18
18
  import { isMemoryV3Live } from "../../../../config/memory-v3-gate.js";
19
19
  import type { AssistantConfig } from "../../../../config/types.js";
20
- import { getDb } from "../../../../persistence/db-connection.js";
21
20
  import { getWorkspaceDir } from "../paths.js";
22
21
  import { getPageIndex, type PageIndexEntry } from "../v2/page-index.js";
23
22
  import { readPage, renderPageContent } from "../v2/page-store.js";
@@ -251,28 +250,17 @@ export async function getMemoryGraph(
251
250
 
252
251
  const workspaceDir = getWorkspaceDir();
253
252
  const pageIndex = await getPageIndex(workspaceDir);
254
- // Concepts plus functionality (skills / CLI-command capabilities). Feeding the
255
- // full set to the edge builders is what lets a concept's `[[skills/foo]]` link
256
- // and skill↔concept co-selections resolve to real edges buildEdgeGraph and
257
- // computeLearnedEdgeGraph both drop endpoints outside the set they're given.
258
- // Functionality nodes that end up disconnected are pruned in assembleMemoryGraph.
259
- const entries = pageIndex.entries;
260
-
261
- // Synthetic rows (skills / CLI commands) carry `modifiedAt: 0` and have no
262
- // on-disk page. Keyed by slug (not prefix) so a real user page that happens
263
- // to live under a reserved prefix is NOT mistaken for a synthetic one.
264
- const syntheticSlugs = new Set(
265
- entries.filter((e) => e.modifiedAt <= 0).map((e) => e.slug),
266
- );
253
+ // The Memory graph is the assistant's learned mind: concept pages and the
254
+ // links between them. Skills and plugins have their own dedicated pages, so
255
+ // synthetic capability rows (skills / CLI commands, which carry `modifiedAt: 0`)
256
+ // are excluded here the map is about what the assistant knows, not what it
257
+ // can do.
258
+ const entries = pageIndex.entries.filter((e) => e.modifiedAt > 0);
267
259
 
268
260
  // Raw (frontmatter + body) page reader, matching the v3 lane build. A read
269
261
  // that rejects drops that article's authored/wikilink edges but keeps its
270
- // numeric fallbacks. Synthetic capability rows have no page, so short-circuit
271
- // their guaranteed-miss read; a real page is read so its links are captured.
262
+ // numeric fallbacks.
272
263
  const pageRaw = async (slug: Slug): Promise<string> => {
273
- if (syntheticSlugs.has(slug)) {
274
- return "";
275
- }
276
264
  const page = await readPage(workspaceDir, slug);
277
265
  if (!page) {
278
266
  throw new Error(`page not found: ${slug}`);
@@ -289,27 +277,20 @@ export async function getMemoryGraph(
289
277
  // little extra edge noise) is a fair trade. Floors keep the thresholds sane
290
278
  // even when the retrieval config is aggressive or disables the lane.
291
279
  const learned = config.memory.v3.learnedEdges;
292
- const learnedGraph = computeLearnedEdgeGraph(
293
- { db: getDb() },
294
- {
295
- halfLifeMs: learned.halfLifeDays * DAY_MS,
296
- minCount: Math.max(1, learned.minCount * GRAPH_LEARNED_MIN_COUNT_FACTOR),
297
- npmiFloor: Math.max(
298
- 0,
299
- learned.npmiFloor * GRAPH_LEARNED_NPMI_FLOOR_FACTOR,
300
- ),
301
- maxPerPage: Math.max(learned.maxPerPage, GRAPH_LEARNED_MIN_MAX_PER_PAGE),
302
- now: Date.now(),
303
- windowMs: LEARNED_EDGES_WINDOW_DAYS * DAY_MS,
304
- knownSlugs: new Set(entries.map((e) => e.slug)),
305
- },
306
- );
280
+ const learnedGraph = computeLearnedEdgeGraph({
281
+ halfLifeMs: learned.halfLifeDays * DAY_MS,
282
+ minCount: Math.max(1, learned.minCount * GRAPH_LEARNED_MIN_COUNT_FACTOR),
283
+ npmiFloor: Math.max(0, learned.npmiFloor * GRAPH_LEARNED_NPMI_FLOOR_FACTOR),
284
+ maxPerPage: Math.max(learned.maxPerPage, GRAPH_LEARNED_MIN_MAX_PER_PAGE),
285
+ now: Date.now(),
286
+ windowMs: LEARNED_EDGES_WINDOW_DAYS * DAY_MS,
287
+ knownSlugs: new Set(entries.map((e) => e.slug)),
288
+ });
307
289
 
308
290
  const assembled = assembleMemoryGraph({
309
291
  entries,
310
292
  staticAdjacency: staticGraph.adjacency,
311
293
  learnedAdjacency: learnedGraph.adjacency,
312
- pruneDisconnectedNonConcepts: true,
313
294
  });
314
295
 
315
296
  return { backend: BACKEND_MEMORY_V3, supported: true, ...assembled };
@@ -665,7 +665,10 @@ function recordCleanupEnqueued(kind: CleanupJobKind, nowMs: number): void {
665
665
  * Enqueue periodic cleanup jobs, each on a cadence equal to its own retention
666
666
  * window. A job that keeps data for N is re-enqueued at most once per N:
667
667
  * pruning that retains LLM logs for 1h runs hourly, pruning that retains
668
- * conversations for 30d runs every 30d. Each job's throttle is tracked
668
+ * conversations for 30d runs every 30d. A non-positive window disables its job
669
+ * (`conversationRetentionDays`/`auditLog.retentionDays` of 0, or
670
+ * `llmRequestLogRetentionMs` of `null`/0) — a 0 window would otherwise make the
671
+ * cadence 0 and busy-loop the enqueue. Each job's throttle is tracked
669
672
  * independently in cleanup-schedule-state (and persisted via checkpoints so the
670
673
  * cadence survives restarts), and enqueue is deduped in jobs-store, so repeated
671
674
  * calls remain safe.
@@ -708,8 +711,14 @@ export function maybeEnqueueScheduledCleanupJobs(
708
711
  );
709
712
  }
710
713
 
714
+ // A retention of `null` or `0` disables LLM-request-log pruning (keep
715
+ // forever). `0` must be excluded here, not just `null`: the cadence interval
716
+ // equals the retention window, so `isDue("llm_request_logs", 0)` reduces to
717
+ // `nowMs - lastEnqueue >= 0` — always true — which would re-enqueue the prune
718
+ // on every idle/drain tick and spin a sqlite3 subprocess many times a second.
711
719
  if (
712
720
  cleanup.llmRequestLogRetentionMs !== null &&
721
+ cleanup.llmRequestLogRetentionMs > 0 &&
713
722
  isDue("llm_request_logs", cleanup.llmRequestLogRetentionMs)
714
723
  ) {
715
724
  const jobId = enqueuePruneOldLlmRequestLogsJob(
@@ -5,8 +5,9 @@ const log = getLogger("memory-db");
5
5
 
6
6
  /**
7
7
  * The plugin's accessor for the dedicated memory database
8
- * (`assistant-memory.db`), where the memory plugin's relocated tables live
9
- * (`memory_v2_injection_events`, with more to follow).
8
+ * (`assistant-memory.db`), where the memory plugin's relocated tables live.
9
+ * The `move-*-to-memory-db` migrations in `persistence/migrations/` define
10
+ * the current set of relocated tables.
10
11
  *
11
12
  * Fail-soft: returns `null` when the file cannot be opened, logging a warn
12
13
  * tagged with the calling context. Callers degrade rather than throw — the
@@ -1,8 +1,9 @@
1
- import { and, eq, inArray, isNull } from "drizzle-orm";
2
1
  import { v4 as uuid } from "uuid";
3
2
 
4
- import { getDb } from "../../../persistence/db-connection.js";
5
- import { memoryRecallLogs } from "../../../persistence/schema/index.js";
3
+ import { getLogger } from "./logging.js";
4
+ import { memorySqliteOrNull } from "./memory-db.js";
5
+
6
+ const log = getLogger("memory-recall-log-store");
6
7
 
7
8
  export interface RecordMemoryRecallLogParams {
8
9
  conversationId: string;
@@ -29,51 +30,74 @@ export interface RecordMemoryRecallLogParams {
29
30
  export function recordMemoryRecallLog(
30
31
  params: RecordMemoryRecallLogParams,
31
32
  ): void {
32
- const db = getDb();
33
- db.insert(memoryRecallLogs)
34
- .values({
35
- id: uuid(),
36
- conversationId: params.conversationId,
37
- messageId: null,
38
- enabled: params.enabled ? 1 : 0,
39
- degraded: params.degraded ? 1 : 0,
40
- provider: params.provider ?? null,
41
- model: params.model ?? null,
42
- degradationJson: params.degradationJson
43
- ? JSON.stringify(params.degradationJson)
44
- : null,
45
- semanticHits: params.semanticHits,
46
- mergedCount: params.mergedCount,
47
- selectedCount: params.selectedCount,
48
- tier1Count: params.tier1Count,
49
- tier2Count: params.tier2Count,
50
- hybridSearchLatencyMs: params.hybridSearchLatencyMs,
51
- sparseVectorUsed: params.sparseVectorUsed ? 1 : 0,
52
- injectedTokens: params.injectedTokens,
53
- latencyMs: params.latencyMs,
54
- topCandidatesJson: JSON.stringify(params.topCandidatesJson),
55
- injectedText: params.injectedText ?? null,
56
- reason: params.reason ?? null,
57
- queryContext: params.queryContext ?? null,
58
- createdAt: Date.now(),
59
- })
60
- .run();
33
+ // Best-effort telemetry writes must never abort the agent turn, so a
34
+ // degraded memory connection or a failed insert only logs a warning.
35
+ try {
36
+ const raw = memorySqliteOrNull("recordMemoryRecallLog");
37
+ if (!raw) {
38
+ return;
39
+ }
40
+ raw
41
+ .prepare(
42
+ `INSERT INTO memory_recall_logs (
43
+ id, conversation_id, message_id, enabled, degraded, provider,
44
+ model, degradation_json, semantic_hits, merged_count,
45
+ selected_count, tier1_count, tier2_count,
46
+ hybrid_search_latency_ms, sparse_vector_used, injected_tokens,
47
+ latency_ms, top_candidates_json, injected_text, reason,
48
+ query_context, created_at
49
+ ) VALUES (?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
50
+ )
51
+ .run(
52
+ uuid(),
53
+ params.conversationId,
54
+ params.enabled ? 1 : 0,
55
+ params.degraded ? 1 : 0,
56
+ params.provider ?? null,
57
+ params.model ?? null,
58
+ params.degradationJson ? JSON.stringify(params.degradationJson) : null,
59
+ params.semanticHits,
60
+ params.mergedCount,
61
+ params.selectedCount,
62
+ params.tier1Count,
63
+ params.tier2Count,
64
+ params.hybridSearchLatencyMs,
65
+ params.sparseVectorUsed ? 1 : 0,
66
+ params.injectedTokens,
67
+ params.latencyMs,
68
+ JSON.stringify(params.topCandidatesJson),
69
+ params.injectedText ?? null,
70
+ params.reason ?? null,
71
+ params.queryContext ?? null,
72
+ Date.now(),
73
+ );
74
+ } catch (err) {
75
+ log.warn({ err }, "failed to record memory recall log; continuing");
76
+ }
61
77
  }
62
78
 
63
79
  export function backfillMemoryRecallLogMessageId(
64
80
  conversationId: string,
65
81
  messageId: string,
66
82
  ): void {
67
- const db = getDb();
68
- db.update(memoryRecallLogs)
69
- .set({ messageId })
70
- .where(
71
- and(
72
- eq(memoryRecallLogs.conversationId, conversationId),
73
- isNull(memoryRecallLogs.messageId),
74
- ),
75
- )
76
- .run();
83
+ try {
84
+ const raw = memorySqliteOrNull("backfillMemoryRecallLogMessageId");
85
+ if (!raw) {
86
+ return;
87
+ }
88
+ raw
89
+ .prepare(
90
+ `UPDATE memory_recall_logs
91
+ SET message_id = ?
92
+ WHERE conversation_id = ? AND message_id IS NULL`,
93
+ )
94
+ .run(messageId, conversationId);
95
+ } catch (err) {
96
+ log.warn(
97
+ { err },
98
+ "failed to backfill memory recall log messageId; continuing",
99
+ );
100
+ }
77
101
  }
78
102
 
79
103
  export interface MemoryRecallLog {
@@ -104,9 +128,13 @@ export interface MemoryRecallLog {
104
128
  * Entries already in inspector format pass through unchanged.
105
129
  */
106
130
  export function normalizeTopCandidates(raw: unknown): unknown {
107
- if (!Array.isArray(raw)) return raw;
131
+ if (!Array.isArray(raw)) {
132
+ return raw;
133
+ }
108
134
  return raw.flatMap((entry: Record<string, unknown>) => {
109
- if (!entry || typeof entry !== "object") return [];
135
+ if (!entry || typeof entry !== "object") {
136
+ return [];
137
+ }
110
138
 
111
139
  // Start with a shallow copy, then apply field renames
112
140
  const { key, finalScore, semantic, recency, kind: _kind, ...rest } = entry;
@@ -137,36 +165,71 @@ export function normalizeTopCandidates(raw: unknown): unknown {
137
165
  });
138
166
  }
139
167
 
168
+ interface MemoryRecallLogRow {
169
+ enabled: number;
170
+ degraded: number;
171
+ provider: string | null;
172
+ model: string | null;
173
+ degradation_json: string | null;
174
+ semantic_hits: number;
175
+ merged_count: number;
176
+ selected_count: number;
177
+ tier1_count: number;
178
+ tier2_count: number;
179
+ hybrid_search_latency_ms: number;
180
+ sparse_vector_used: number;
181
+ injected_tokens: number;
182
+ latency_ms: number;
183
+ top_candidates_json: string;
184
+ injected_text: string | null;
185
+ reason: string | null;
186
+ query_context: string | null;
187
+ }
188
+
140
189
  export function getMemoryRecallLogByMessageIds(
141
190
  messageIds: string[],
142
191
  ): MemoryRecallLog | null {
143
- if (messageIds.length === 0) return null;
144
- const db = getDb();
145
- const rows = db
146
- .select()
147
- .from(memoryRecallLogs)
148
- .where(inArray(memoryRecallLogs.messageId, messageIds))
149
- .all();
150
- if (rows.length === 0) return null;
151
- const row = rows[0]!;
192
+ if (messageIds.length === 0) {
193
+ return null;
194
+ }
195
+ const raw = memorySqliteOrNull("getMemoryRecallLogByMessageIds");
196
+ if (!raw) {
197
+ return null;
198
+ }
199
+ const placeholders = messageIds.map(() => "?").join(",");
200
+ const row = raw
201
+ .query(
202
+ `SELECT enabled, degraded, provider, model, degradation_json,
203
+ semantic_hits, merged_count, selected_count, tier1_count,
204
+ tier2_count, hybrid_search_latency_ms, sparse_vector_used,
205
+ injected_tokens, latency_ms, top_candidates_json,
206
+ injected_text, reason, query_context
207
+ FROM memory_recall_logs
208
+ WHERE message_id IN (${placeholders})
209
+ LIMIT 1`,
210
+ )
211
+ .get(...messageIds) as MemoryRecallLogRow | null;
212
+ if (!row) {
213
+ return null;
214
+ }
152
215
  return {
153
216
  enabled: !!row.enabled,
154
217
  degraded: !!row.degraded,
155
218
  provider: row.provider,
156
219
  model: row.model,
157
- degradation: row.degradationJson ? JSON.parse(row.degradationJson) : null,
158
- semanticHits: row.semanticHits,
159
- mergedCount: row.mergedCount,
160
- selectedCount: row.selectedCount,
161
- tier1Count: row.tier1Count,
162
- tier2Count: row.tier2Count,
163
- hybridSearchLatencyMs: row.hybridSearchLatencyMs,
164
- sparseVectorUsed: !!row.sparseVectorUsed,
165
- injectedTokens: row.injectedTokens,
166
- latencyMs: row.latencyMs,
167
- topCandidates: normalizeTopCandidates(JSON.parse(row.topCandidatesJson)),
168
- injectedText: row.injectedText,
220
+ degradation: row.degradation_json ? JSON.parse(row.degradation_json) : null,
221
+ semanticHits: row.semantic_hits,
222
+ mergedCount: row.merged_count,
223
+ selectedCount: row.selected_count,
224
+ tier1Count: row.tier1_count,
225
+ tier2Count: row.tier2_count,
226
+ hybridSearchLatencyMs: row.hybrid_search_latency_ms,
227
+ sparseVectorUsed: !!row.sparse_vector_used,
228
+ injectedTokens: row.injected_tokens,
229
+ latencyMs: row.latency_ms,
230
+ topCandidates: normalizeTopCandidates(JSON.parse(row.top_candidates_json)),
231
+ injectedText: row.injected_text,
169
232
  reason: row.reason,
170
- queryContext: row.queryContext,
233
+ queryContext: row.query_context,
171
234
  };
172
235
  }
@@ -70,3 +70,11 @@ export const SKILL_CARD_MESSAGE_KIND = "skill-authored-card";
70
70
  * the `"memory_retrospective"` member of `TitleOrigin`.
71
71
  */
72
72
  export const MEMORY_RETROSPECTIVE_ORIGIN = "memory_retrospective";
73
+
74
+ /**
75
+ * Bundled skill that provides the retrospective's authoring tools
76
+ * (`find_similar_skills`, `scaffold_managed_skill`, and the `skill_load`
77
+ * target). Preactivated for the fork wake so those tools join the turn's active
78
+ * set from turn 1, and matched by the permission checker's origin-scoped grant.
79
+ */
80
+ export const SKILL_MANAGEMENT_SKILL_ID = "skill-management";