@vellumai/assistant 0.11.3 → 0.11.4-dev.202608190019.b94dbf2

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 (596) hide show
  1. package/AGENTS.md +9 -3
  2. package/ARCHITECTURE.md +32 -10
  3. package/docs/architecture/memory.md +43 -5
  4. package/docs/architecture/security.md +96 -152
  5. package/docs/architecture/turn-actor.md +70 -0
  6. package/docs/browser-use-architecture-phase2.md +128 -56
  7. package/docs/flux-turn-detection-spike.md +248 -0
  8. package/docs/guardian-request-flow.md +35 -0
  9. package/docs/stt-provider-onboarding.md +3 -1
  10. package/knip.json +3 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  12. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  13. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  14. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  15. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  19. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  20. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  21. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  23. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
  24. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
  25. package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
  26. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  27. package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  28. package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  29. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  30. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  31. package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  32. package/openapi.yaml +289 -113
  33. package/package.json +1 -1
  34. package/scripts/voice-ttft-spike.ts +3 -3
  35. package/scripts/write-plugin-api-shim.ts +10 -0
  36. package/src/__tests__/app-compiler.test.ts +38 -3
  37. package/src/__tests__/app-control-flow.test.ts +1 -0
  38. package/src/__tests__/approval-routes-http.test.ts +2 -2
  39. package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -3
  40. package/src/__tests__/attachments-store.test.ts +21 -12
  41. package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
  42. package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
  43. package/src/__tests__/call-site-routing-provider.test.ts +1 -1
  44. package/src/__tests__/channel-availability-routes.test.ts +14 -1
  45. package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
  46. package/src/__tests__/channel-delivery-store.test.ts +14 -14
  47. package/src/__tests__/channel-inbound-disk-pressure.test.ts +4 -4
  48. package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
  49. package/src/__tests__/checker.test.ts +20 -314
  50. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
  51. package/src/__tests__/compaction-events.test.ts +8 -10
  52. package/src/__tests__/config-loader-backfill.test.ts +3 -3
  53. package/src/__tests__/config-schema.test.ts +25 -10
  54. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  55. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
  56. package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
  57. package/src/__tests__/conversation-agent-loop.test.ts +32 -21
  58. package/src/__tests__/conversation-attachments.test.ts +0 -1
  59. package/src/__tests__/conversation-attention-store.test.ts +63 -0
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +112 -0
  61. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
  62. package/src/__tests__/conversation-fork-crud.test.ts +69 -0
  63. package/src/__tests__/conversation-fork-referential.test.ts +67 -0
  64. package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
  65. package/src/__tests__/conversation-load-history-repair.test.ts +209 -0
  66. package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
  67. package/src/__tests__/conversation-queue.test.ts +154 -4
  68. package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
  69. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
  70. package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
  71. package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
  72. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +187 -102
  74. package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
  75. package/src/__tests__/conversation-slash-queue.test.ts +3 -0
  76. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
  77. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
  78. package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
  79. package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
  80. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
  81. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
  82. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
  83. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
  84. package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
  85. package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
  86. package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
  87. package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
  88. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
  89. package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
  90. package/src/__tests__/credential-prompt-route.test.ts +7 -10
  91. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  92. package/src/__tests__/cu-unified-flow.test.ts +1 -0
  93. package/src/__tests__/custom-profile-ensure.test.ts +5 -1
  94. package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
  95. package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
  96. package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
  97. package/src/__tests__/document-append-idempotency.test.ts +233 -0
  98. package/src/__tests__/document-sync-tags.test.ts +0 -75
  99. package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
  100. package/src/__tests__/edit-propagation.test.ts +107 -11
  101. package/src/__tests__/file-ops-service.test.ts +163 -30
  102. package/src/__tests__/filesystem-tools.test.ts +23 -24
  103. package/src/__tests__/gateway-only-guard.test.ts +2 -5
  104. package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
  105. package/src/__tests__/helpers/mock-actor-context.ts +49 -0
  106. package/src/__tests__/helpers/mock-conversation.ts +13 -1
  107. package/src/__tests__/host-file-read-tool.test.ts +16 -19
  108. package/src/__tests__/http-user-message-parity.test.ts +1 -1
  109. package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
  110. package/src/__tests__/injector-chain.test.ts +63 -41
  111. package/src/__tests__/injector-disk-pressure.test.ts +11 -23
  112. package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
  113. package/src/__tests__/llm-context-resolution.test.ts +73 -1
  114. package/src/__tests__/llm-schema.test.ts +5 -2
  115. package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
  116. package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
  117. package/src/__tests__/media-generate-image.test.ts +131 -21
  118. package/src/__tests__/memory-retrieval-hook.test.ts +100 -7
  119. package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
  120. package/src/__tests__/mtime-cache.test.ts +3 -1
  121. package/src/__tests__/non-member-access-request.test.ts +0 -20
  122. package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
  123. package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
  124. package/src/__tests__/plugin-api-store-credential.test.ts +261 -0
  125. package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
  126. package/src/__tests__/plugin-disabled-state.test.ts +2 -0
  127. package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
  128. package/src/__tests__/plugin-execution-context.test.ts +73 -0
  129. package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
  130. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -4
  131. package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
  132. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
  133. package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
  134. package/src/__tests__/proxy-approval-callback.test.ts +1 -0
  135. package/src/__tests__/qdrant-manager.test.ts +14 -1
  136. package/src/__tests__/reaction-persistence.test.ts +200 -7
  137. package/src/__tests__/require-fresh-approval.test.ts +0 -4
  138. package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
  139. package/src/__tests__/run-conversation-turn-persistence.test.ts +460 -105
  140. package/src/__tests__/run-due-schedules.test.ts +21 -0
  141. package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
  142. package/src/__tests__/schedule-routes.test.ts +23 -0
  143. package/src/__tests__/schedule-store.test.ts +17 -0
  144. package/src/__tests__/scoped-approval-grants.test.ts +11 -6
  145. package/src/__tests__/secret-ingress-channel.test.ts +0 -1
  146. package/src/__tests__/secret-ingress-http.test.ts +1 -1
  147. package/src/__tests__/send-endpoint-busy.test.ts +3 -3
  148. package/src/__tests__/skills.test.ts +32 -0
  149. package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
  150. package/src/__tests__/starter-task-flow.test.ts +5 -4
  151. package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
  152. package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
  153. package/src/__tests__/subagent-spawn-and-await.test.ts +18 -17
  154. package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
  155. package/src/__tests__/subagent-tools.test.ts +81 -101
  156. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
  157. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  158. package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
  159. package/src/__tests__/tool-executor.test.ts +68 -39
  160. package/src/__tests__/turn-events-store.test.ts +43 -0
  161. package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
  162. package/src/__tests__/ui-shape-teaching.test.ts +33 -0
  163. package/src/__tests__/ui-visual-surface.test.ts +1 -1
  164. package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
  165. package/src/__tests__/ui-work-result-surface.test.ts +1 -1
  166. package/src/__tests__/user-plugin-loader.test.ts +3 -1
  167. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  168. package/src/__tests__/visible-app-context.test.ts +16 -9
  169. package/src/__tests__/voice-config-update.test.ts +40 -0
  170. package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
  171. package/src/__tests__/voice-session-bridge.test.ts +85 -29
  172. package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
  173. package/src/__tests__/worker-plugin-surface.test.ts +77 -0
  174. package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
  175. package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
  176. package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
  177. package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
  178. package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
  179. package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
  180. package/src/acp/__tests__/auth-required.test.ts +161 -0
  181. package/src/acp/acp-claude-oauth.ts +19 -2
  182. package/src/acp/agent-process.test.ts +100 -0
  183. package/src/acp/agent-process.ts +29 -26
  184. package/src/acp/auth-required.ts +102 -0
  185. package/src/acp/session-manager.test.ts +119 -0
  186. package/src/acp/session-manager.ts +76 -3
  187. package/src/api/events/acp-auth-required.ts +55 -0
  188. package/src/api/events/host-file.ts +2 -2
  189. package/src/api/index.ts +7 -0
  190. package/src/api/surfaces.ts +12 -3
  191. package/src/apps/app-store.ts +3 -0
  192. package/src/bundler/package-resolver.ts +2 -30
  193. package/src/calls/__tests__/voice-session-bridge.test.ts +194 -11
  194. package/src/calls/__tests__/voice-triage-escalate.test.ts +102 -0
  195. package/src/calls/call-controller.ts +19 -3
  196. package/src/calls/call-setup-flow.ts +0 -1
  197. package/src/calls/media-stream-stt-session.ts +15 -0
  198. package/src/calls/voice-session-bridge.ts +115 -41
  199. package/src/calls/voice-triage-escalate.ts +105 -2
  200. package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
  201. package/src/channels/config.ts +13 -0
  202. package/src/channels/plugin-channel-declarations.ts +108 -0
  203. package/src/channels/types.ts +30 -0
  204. package/src/cli/AGENTS.md +5 -2
  205. package/src/cli/bundled-modules.ts +29 -0
  206. package/src/cli/commands/credentials.help.ts +2 -2
  207. package/src/cli/commands/db/repair.ts +4 -8
  208. package/src/cli/commands/domain.ts +6 -3
  209. package/src/cli/commands/email.ts +6 -3
  210. package/src/cli/commands/inference-providers.ts +1 -1
  211. package/src/cli/commands/keys.ts +8 -3
  212. package/src/cli/commands/mcp.help.ts +13 -4
  213. package/src/cli/commands/mcp.ts +9 -0
  214. package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
  215. package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
  216. package/src/cli/commands/memory/index.help.ts +84 -28
  217. package/src/cli/commands/memory/index.ts +2 -0
  218. package/src/cli/commands/memory/memory-v2.ts +58 -54
  219. package/src/cli/commands/memory/memory-v3.ts +64 -0
  220. package/src/cli/commands/memory/memory-validate.ts +18 -0
  221. package/src/cli/commands/plugins.ts +85 -36
  222. package/src/cli/commands/schedules.ts +35 -1
  223. package/src/cli/commands/stt.help.ts +27 -2
  224. package/src/cli/commands/trust.ts +3 -14
  225. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
  226. package/src/cli/lib/bundled-marketplace.json +14 -1
  227. package/src/cli/lib/upgrade-plugin.ts +42 -0
  228. package/src/config/__tests__/balanced-model-experiment.test.ts +278 -0
  229. package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
  230. package/src/config/__tests__/default-provider.test.ts +6 -1
  231. package/src/config/__tests__/profile-materialization.test.ts +75 -19
  232. package/src/config/assistant-feature-flags.ts +36 -15
  233. package/src/config/balanced-model-experiment.ts +35 -0
  234. package/src/config/bundled-skills/acp/SKILL.md +6 -7
  235. package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
  236. package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
  237. package/src/config/bundled-skills/image-studio/SKILL.md +5 -4
  238. package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
  239. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
  240. package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
  241. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  242. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
  243. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
  244. package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
  245. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
  246. package/src/config/bundled-skills/skill-management/TOOLS.json +9 -3
  247. package/src/config/bundled-skills/subagent/SKILL.md +17 -12
  248. package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
  249. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
  250. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
  251. package/src/config/call-site-defaults.ts +11 -5
  252. package/src/config/default-profile-catalog.ts +179 -16
  253. package/src/config/default-profile-names.ts +4 -1
  254. package/src/config/default-provider-resolution.ts +4 -0
  255. package/src/config/feature-flag-registry.json +12 -12
  256. package/src/config/llm-context-resolution.ts +11 -3
  257. package/src/config/llm-resolver.ts +28 -1
  258. package/src/config/profile-materialization.ts +70 -22
  259. package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
  260. package/src/config/schemas/call-site-catalog.ts +4 -4
  261. package/src/config/schemas/live-voice.ts +57 -23
  262. package/src/config/schemas/llm.ts +59 -32
  263. package/src/config/schemas/mcp.ts +23 -0
  264. package/src/config/schemas/plugin-updates.ts +6 -2
  265. package/src/config/schemas/stt.ts +1 -0
  266. package/src/config/skills.ts +9 -2
  267. package/src/context/outbound-sanitize.ts +96 -1
  268. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
  269. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
  270. package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
  271. package/src/daemon/conversation-agent-loop.ts +31 -19
  272. package/src/daemon/conversation-messaging.ts +5 -1
  273. package/src/daemon/conversation-notifiers.ts +20 -10
  274. package/src/daemon/conversation-process.ts +20 -6
  275. package/src/daemon/conversation-runtime-assembly.ts +12 -6
  276. package/src/daemon/conversation-store.ts +14 -4
  277. package/src/daemon/conversation-surfaces.ts +75 -26
  278. package/src/daemon/conversation-tool-setup.ts +3 -6
  279. package/src/daemon/conversation.ts +154 -51
  280. package/src/daemon/doordash-steps.ts +2 -2
  281. package/src/daemon/lifecycle.ts +14 -1
  282. package/src/daemon/mcp-reload-service.ts +36 -6
  283. package/src/daemon/process-message.ts +13 -26
  284. package/src/daemon/providers-setup.ts +6 -3
  285. package/src/daemon/trust-context-types.ts +29 -0
  286. package/src/daemon/trust-context.ts +12 -7
  287. package/src/daemon/wake-conversation-ops.ts +3 -2
  288. package/src/daemon/windows-compiled-entry.ts +4 -0
  289. package/src/documents/document-store.ts +143 -240
  290. package/src/hooks/hook-loader.ts +3 -3
  291. package/src/hooks/registry.ts +103 -45
  292. package/src/hooks/types.ts +5 -0
  293. package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
  294. package/src/inbound/oauth-callback-url.ts +61 -0
  295. package/src/ipc/gateway-client.test.ts +1 -1
  296. package/src/ipc/gateway-client.ts +14 -21
  297. package/src/ipc/gateway-flag-listener.ts +17 -3
  298. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
  299. package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
  300. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +1050 -0
  301. package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
  302. package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
  303. package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
  304. package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
  305. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
  306. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
  307. package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
  308. package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
  309. package/src/live-voice/live-voice-archive.ts +2 -0
  310. package/src/live-voice/live-voice-manager.ts +16 -3
  311. package/src/live-voice/live-voice-metrics.ts +57 -32
  312. package/src/live-voice/live-voice-photo.ts +1 -2
  313. package/src/live-voice/live-voice-session.ts +598 -323
  314. package/src/live-voice/progress-narration.ts +277 -0
  315. package/src/live-voice/protocol.ts +21 -1
  316. package/src/live-voice/windows-compiled-live-voice.ts +4 -0
  317. package/src/mcp/__tests__/effective-config.test.ts +238 -0
  318. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  319. package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
  320. package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
  321. package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
  322. package/src/mcp/client.ts +16 -11
  323. package/src/mcp/effective-config.ts +113 -0
  324. package/src/mcp/manager.ts +11 -6
  325. package/src/mcp/mcp-auth-orchestrator.ts +13 -22
  326. package/src/mcp/mcp-oauth-provider.ts +205 -240
  327. package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
  328. package/src/monitoring/control.ts +1 -0
  329. package/src/monitoring/db-integrity-sample.ts +4 -5
  330. package/src/monitoring/plugin-auto-update.ts +128 -24
  331. package/src/notifications/AGENTS.md +2 -0
  332. package/src/notifications/approval-card-data.ts +33 -0
  333. package/src/notifications/signal.ts +1 -0
  334. package/src/permissions/AGENTS.md +16 -0
  335. package/src/permissions/checker.test.ts +75 -122
  336. package/src/permissions/checker.ts +44 -560
  337. package/src/permissions/confirmation-guardian-request.test.ts +15 -11
  338. package/src/permissions/confirmation-guardian-request.ts +2 -2
  339. package/src/permissions/prompter.ts +1 -5
  340. package/src/permissions/question-guardian-request.test.ts +14 -6
  341. package/src/permissions/question-guardian-request.ts +1 -2
  342. package/src/persistence/attachments-store.ts +8 -1
  343. package/src/persistence/bookmark-crud.ts +3 -7
  344. package/src/persistence/conversation-attention-store.ts +16 -45
  345. package/src/persistence/conversation-crud.ts +33 -4
  346. package/src/persistence/conversation-lineage.ts +9 -0
  347. package/src/persistence/conversation-queries.ts +182 -52
  348. package/src/persistence/delivery-crud.ts +150 -36
  349. package/src/persistence/embeddings/qdrant-manager.ts +84 -49
  350. package/src/persistence/external-conversation-store.ts +32 -4
  351. package/src/persistence/llm-request-log-store.ts +4 -10
  352. package/src/persistence/llm-usage-store.ts +8 -3
  353. package/src/persistence/message-reads.test.ts +197 -0
  354. package/src/persistence/message-reads.ts +211 -0
  355. package/src/persistence/migrations/360-add-document-workspace-path.ts +5 -14
  356. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
  357. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
  358. package/src/persistence/real-user-turn-filter.ts +27 -3
  359. package/src/persistence/schema/documents.ts +4 -4
  360. package/src/persistence/steps.ts +9 -0
  361. package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
  362. package/src/plugin-api/constants.ts +8 -0
  363. package/src/plugin-api/conversation-turn.ts +195 -5
  364. package/src/plugin-api/index.ts +38 -6
  365. package/src/plugin-api/resolve-credential.ts +3 -2
  366. package/src/plugin-api/store-credential.ts +146 -0
  367. package/src/plugin-api/vision-support.test.ts +1 -1
  368. package/src/plugin-api/webhook-url.ts +13 -11
  369. package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
  370. package/src/plugins/defaults/main.ts +21 -7
  371. package/src/plugins/defaults/memory/AGENTS.md +15 -3
  372. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
  373. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
  374. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
  375. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
  376. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
  377. package/src/plugins/defaults/memory/buffer-format.ts +165 -0
  378. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
  379. package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
  380. package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
  381. package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
  382. package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
  383. package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
  384. package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +17 -24
  385. package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
  386. package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
  387. package/src/plugins/defaults/memory/indexer.ts +3 -1
  388. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
  389. package/src/plugins/defaults/memory/memory-retrospective-job.ts +4 -4
  390. package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
  391. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
  392. package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
  393. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +147 -0
  394. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
  395. package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
  396. package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
  397. package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
  398. package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
  399. package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
  400. package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +195 -58
  401. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +93 -24
  402. package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
  403. package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
  404. package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
  405. package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
  406. package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
  407. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
  408. package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
  409. package/src/plugins/defaults/memory/substrate/static-context.ts +160 -33
  410. package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
  411. package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
  412. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +18 -0
  413. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
  414. package/src/plugins/defaults/memory/v3/card.ts +9 -9
  415. package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
  416. package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
  417. package/src/plugins/defaults/memory/v3/edge.ts +13 -57
  418. package/src/plugins/defaults/memory/v3/injector.ts +8 -0
  419. package/src/plugins/defaults/memory/v3/prune.ts +2 -0
  420. package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
  421. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +23 -9
  422. package/src/plugins/defaults/memory/worker-control.ts +1 -0
  423. package/src/plugins/defaults/memory/worker.ts +6 -3
  424. package/src/plugins/defaults/worker-entrypoints.ts +3 -0
  425. package/src/plugins/external-plugin-loader.ts +47 -0
  426. package/src/plugins/mcp-servers.ts +361 -0
  427. package/src/plugins/mtime-cache.ts +37 -46
  428. package/src/plugins/pipeline.ts +14 -7
  429. package/src/plugins/plugin-execution-context.ts +35 -11
  430. package/src/plugins/worker-plugin-surface.ts +33 -0
  431. package/src/prompts/templates/system-sections.ts +0 -7
  432. package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
  433. package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
  434. package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
  435. package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
  436. package/src/providers/__tests__/retry-callsite.test.ts +25 -2
  437. package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
  438. package/src/providers/anthropic/client.ts +332 -241
  439. package/src/providers/call-site-routing.ts +30 -3
  440. package/src/providers/connection-resolution.ts +194 -11
  441. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +4 -0
  442. package/src/providers/inference/adapter-factory.ts +3 -0
  443. package/src/providers/inference/auth.ts +6 -0
  444. package/src/providers/inference/connection-availability.ts +24 -2
  445. package/src/providers/inference/connections.ts +2 -0
  446. package/src/providers/model-catalog.ts +3 -3
  447. package/src/providers/model-intents.ts +28 -8
  448. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +163 -40
  449. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
  450. package/src/providers/openai/chat-completions-provider.ts +133 -36
  451. package/src/providers/openai/codex-models.ts +2 -1
  452. package/src/providers/provider-send-message.ts +32 -3
  453. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
  454. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +625 -0
  455. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
  456. package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
  457. package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
  458. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +679 -0
  459. package/src/providers/speech-to-text/provider-catalog.ts +99 -8
  460. package/src/providers/speech-to-text/resolve.ts +25 -2
  461. package/src/routes/control.ts +1 -0
  462. package/src/routes/route-host-client.ts +1 -0
  463. package/src/routes/route-host-protocol.ts +7 -0
  464. package/src/routes/worker.ts +48 -15
  465. package/src/runtime/AGENTS.md +16 -17
  466. package/src/runtime/access-request-helper.ts +9 -12
  467. package/src/runtime/agent-wake.ts +18 -15
  468. package/src/runtime/effective-capabilities.test.ts +43 -0
  469. package/src/runtime/effective-capabilities.ts +27 -0
  470. package/src/runtime/guardian-reply-router.ts +6 -2
  471. package/src/runtime/pre-first-message-gate.ts +4 -0
  472. package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
  473. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +522 -2
  474. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
  475. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
  476. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
  477. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
  478. package/src/runtime/routes/__tests__/plugins-routes.test.ts +132 -35
  479. package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
  480. package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
  481. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
  482. package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
  483. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +133 -1
  484. package/src/runtime/routes/channel-availability-routes.ts +32 -14
  485. package/src/runtime/routes/channel-route-shared.ts +0 -6
  486. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
  487. package/src/runtime/routes/conversation-list-routes.ts +181 -23
  488. package/src/runtime/routes/conversation-management-routes.ts +17 -12
  489. package/src/runtime/routes/conversation-query-routes.ts +40 -27
  490. package/src/runtime/routes/conversation-routes.ts +43 -29
  491. package/src/runtime/routes/credential-prompt-routes.ts +4 -7
  492. package/src/runtime/routes/credential-routes.ts +26 -82
  493. package/src/runtime/routes/default-provider-routes.ts +15 -0
  494. package/src/runtime/routes/documents-routes.ts +3 -222
  495. package/src/runtime/routes/inbound-message-handler.ts +21 -45
  496. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
  497. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
  498. package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
  499. package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
  500. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
  501. package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -79
  502. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
  503. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -7
  504. package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -8
  505. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -79
  506. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
  507. package/src/runtime/routes/inference-profiles-routes.ts +20 -11
  508. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
  509. package/src/runtime/routes/log-export-routes.ts +3 -0
  510. package/src/runtime/routes/mcp-auth-routes.ts +148 -57
  511. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +2 -0
  512. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
  513. package/src/runtime/routes/playground/inject-failures.ts +2 -2
  514. package/src/runtime/routes/playground/reset-circuit.ts +1 -1
  515. package/src/runtime/routes/plugins-routes.ts +38 -8
  516. package/src/runtime/routes/schedule-routes.ts +94 -7
  517. package/src/runtime/routes/settings-routes.ts +6 -6
  518. package/src/runtime/routes/stt-routes.ts +31 -25
  519. package/src/runtime/routes/surface-action-routes.ts +20 -19
  520. package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
  521. package/src/runtime/routes/user-route-dispatcher.ts +69 -15
  522. package/src/runtime/routes/user-route-import.ts +108 -0
  523. package/src/runtime/routes/user-route-resolution.ts +18 -1
  524. package/src/runtime/routes/workspace-routes.ts +0 -9
  525. package/src/runtime/routes/workspace-utils.ts +3 -13
  526. package/src/runtime/services/conversation-serializer.ts +7 -2
  527. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
  528. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
  529. package/src/schedule/plugin-schedule-availability.ts +58 -0
  530. package/src/schedule/plugin-schedule-declarations.ts +23 -27
  531. package/src/schedule/plugin-schedule-reconciler.ts +12 -3
  532. package/src/schedule/schedule-store.ts +5 -1
  533. package/src/schedule/scheduler.ts +9 -4
  534. package/src/schedule/worker-control.ts +1 -0
  535. package/src/schedule/worker.ts +6 -0
  536. package/src/security/oauth2.ts +6 -22
  537. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
  538. package/src/stt/__tests__/types.test.ts +94 -0
  539. package/src/stt/daemon-batch-transcriber.ts +10 -0
  540. package/src/stt/stt-stream-session.ts +8 -4
  541. package/src/stt/types.ts +103 -0
  542. package/src/subagent/__tests__/consult-prompt.test.ts +26 -15
  543. package/src/subagent/consult-context.ts +11 -11
  544. package/src/subagent/consult-prompt.ts +26 -35
  545. package/src/subagent/manager.ts +21 -40
  546. package/src/subagent/notify.ts +7 -1
  547. package/src/subagent/types.ts +15 -12
  548. package/src/tools/__tests__/tool-input-schemas.test.ts +7 -7
  549. package/src/tools/acp/spawn.test.ts +97 -0
  550. package/src/tools/acp/spawn.ts +38 -4
  551. package/src/tools/credentials/ref-parse.ts +35 -0
  552. package/src/tools/credentials/resolve.ts +4 -10
  553. package/src/tools/credentials/store.ts +168 -0
  554. package/src/tools/document/document-tool.ts +12 -3
  555. package/src/tools/executor.ts +53 -15
  556. package/src/tools/filesystem/read.ts +27 -10
  557. package/src/tools/host-filesystem/read.ts +27 -15
  558. package/src/tools/network/url-safety.ts +9 -16
  559. package/src/tools/permission-checker.ts +43 -52
  560. package/src/tools/registry.ts +2 -1
  561. package/src/tools/shared/filesystem/file-ops-service.ts +63 -35
  562. package/src/tools/shared/filesystem/legacy-read-args.ts +22 -0
  563. package/src/tools/shared/filesystem/types.ts +5 -5
  564. package/src/tools/skills/scaffold-managed.ts +25 -7
  565. package/src/tools/subagent/spawn.ts +28 -88
  566. package/src/tools/tool-types.ts +26 -13
  567. package/src/tools/types.ts +6 -6
  568. package/src/tools/ui-surface/surface-shape-docs.ts +12 -1
  569. package/src/tools/workflows/run-workflow.ts +1 -2
  570. package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
  571. package/src/tts/reasoning-tag-filter.ts +89 -0
  572. package/src/util/__tests__/worker-process-command.test.ts +37 -0
  573. package/src/util/logger.ts +16 -0
  574. package/src/util/think-tag-stream.ts +95 -0
  575. package/src/util/worker-process.ts +37 -4
  576. package/src/windows-compiled-cli.ts +32 -0
  577. package/src/windows-compiled-entry.ts +4 -0
  578. package/src/windows-compiled-logger.ts +6 -0
  579. package/src/windows-compiled-worker-entry.ts +29 -0
  580. package/src/workspace/byok-default-profile-ensure.ts +76 -24
  581. package/src/workspace/custom-profile-ensure.ts +4 -24
  582. package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
  583. package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
  584. package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
  585. package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
  586. package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
  587. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
  588. package/src/workspace/migrations/registry.ts +10 -0
  589. package/src/workspace/provider-commit-message-generator.ts +7 -5
  590. package/src/__tests__/document-workspace-file.test.ts +0 -467
  591. package/src/live-voice/__tests__/front-decision.test.ts +0 -645
  592. package/src/live-voice/front-decision.ts +0 -476
  593. package/src/permissions/ipc-risk-types.ts +0 -143
  594. package/src/permissions/risk-types.ts +0 -76
  595. package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
  596. package/src/subagent/consult-transcript.ts +0 -90
@@ -18,7 +18,15 @@
18
18
  import { readFile } from "node:fs/promises";
19
19
  import { join } from "node:path";
20
20
 
21
- import { formatRememberEntry } from "../graph/tool-handlers.js";
21
+ import {
22
+ formatRememberEntry,
23
+ matchBufferEntryStart,
24
+ } from "../buffer-format.js";
25
+ import {
26
+ extractWikilinkTargets,
27
+ WIKILINK_REGEX,
28
+ wikilinkTarget,
29
+ } from "../substrate/page-links.js";
22
30
  import type { MemoryGraphEdge, MemoryGraphNode } from "./types.js";
23
31
 
24
32
  /** Node id prefix marking a pending buffer entry (id = prefix + text hash). */
@@ -36,22 +44,10 @@ const MAX_PENDING_NODES = 200;
36
44
 
37
45
  const PENDING_LABEL_MAX_CHARS = 60;
38
46
 
39
- /**
40
- * Matches a real entry start: `- [Mon D, h:mm AM/PM] fact`, the exact shape
41
- * `formatRememberEntry` writes. The timestamp must be present and
42
- * timestamp-shaped — a bullet with other bracketed text (e.g. a `- [ ]`
43
- * checklist item inside a multiline fact) is a continuation, not an entry.
44
- */
45
- const BUFFER_ENTRY_REGEX =
46
- /^-\s+\[[A-Z][a-z]{2}\s+\d{1,2},\s+\d{1,2}:\d{2}\s+[AP]M\]\s*(.*)$/;
47
-
48
47
  /** Lenient fallback for hand-written buffers: a plain bullet with no
49
48
  * timestamped entry seen yet still counts as an entry of its own. */
50
49
  const PLAIN_BULLET_REGEX = /^-\s+(.+)$/;
51
50
 
52
- /** Matches `[[slug]]` / `[[slug|label]]` wikilinks inside an entry. */
53
- const WIKILINK_REGEX = /\[\[([^[\]|]+)(?:\|[^\]]*)?\]\]/g;
54
-
55
51
  export interface PendingBufferEntry {
56
52
  /** Stable node id (`buffer:` + content hash, deduped within one parse). */
57
53
  id: string;
@@ -102,10 +98,12 @@ export function parseBufferEntries(content: string): PendingBufferEntry[] {
102
98
 
103
99
  for (const rawLine of content.split("\n")) {
104
100
  const line = rawLine.trimEnd();
105
- const entryStart = BUFFER_ENTRY_REGEX.exec(line.trim());
101
+ // Matched against the untrimmed line: an indented entry-shaped bullet is
102
+ // body text inside the fact above it, not a fact of its own.
103
+ const entryStart = matchBufferEntryStart(line);
106
104
  if (entryStart) {
107
105
  flush();
108
- current = [entryStart[1]!.trim()];
106
+ current = [entryStart.text];
109
107
  continue;
110
108
  }
111
109
  if (current !== null) {
@@ -120,13 +118,7 @@ export function parseBufferEntries(content: string): PendingBufferEntry[] {
120
118
  flush();
121
119
 
122
120
  for (const text of texts) {
123
- const slugs: string[] = [];
124
- for (const link of text.matchAll(WIKILINK_REGEX)) {
125
- const slug = link[1]!.trim();
126
- if (slug && !slugs.includes(slug)) {
127
- slugs.push(slug);
128
- }
129
- }
121
+ const slugs = [...new Set(extractWikilinkTargets(text))];
130
122
 
131
123
  const hash = hashText(text);
132
124
  const count = (seen.get(hash) ?? 0) + 1;
@@ -148,8 +140,9 @@ export function parseBufferEntries(content: string): PendingBufferEntry[] {
148
140
  function pendingLabel(text: string): string {
149
141
  const firstLine = text.split("\n", 1)[0] ?? text;
150
142
  const plain = firstLine
151
- .replace(WIKILINK_REGEX, (_match, slug: string) => {
152
- return slug.trim().split("/").pop() ?? slug;
143
+ .replace(WIKILINK_REGEX, (_match, inner: string) => {
144
+ const slug = wikilinkTarget(inner);
145
+ return slug.split("/").pop() || slug;
153
146
  })
154
147
  .trim();
155
148
  if (plain.length <= PENDING_LABEL_MAX_CHARS) {
@@ -70,10 +70,7 @@ const postCompact: HookFunction<PostCompactContext> = async (ctx) => {
70
70
  // concurrent request's actor and the contact/member registry the actor
71
71
  // context derives from can be mutated by contact tools. The snapshots are the
72
72
  // values the turn's initial assembly resolved.
73
- const trust =
74
- conversation?.currentTurnTrustContext ??
75
- conversation?.trustContext ??
76
- FALLBACK_TURN_TRUST;
73
+ const trust = conversation?.getTurnOrRestingTrust() ?? FALLBACK_TURN_TRUST;
77
74
  const actorContext = conversation?.currentTurnInboundActorContext ?? null;
78
75
  // Render the `model_profile:` label from the turn-start notice key, using
79
76
  // the call site self-resolved from the live conversation — the same
@@ -31,7 +31,10 @@ import type {
31
31
  HookFunction,
32
32
  UserPromptSubmitContext,
33
33
  } from "@vellumai/plugin-api";
34
- import { updateMessageMetadata } from "@vellumai/plugin-api";
34
+ import {
35
+ updateMessageMetadata,
36
+ VOICE_ESCALATION_CONTINUATION_MESSAGE_KIND,
37
+ } from "@vellumai/plugin-api";
35
38
 
36
39
  import type { MemoryRecalledEvent } from "../../../../api/events/memory-recalled.js";
37
40
  import { getConfig } from "../../../../config/loader.js";
@@ -48,6 +51,7 @@ import { timeLatencySubSpan } from "../../../../daemon/turn-latency-sub-spans.js
48
51
  import { broadcastMessage } from "../../../../runtime/assistant-event-hub.js";
49
52
  import type { GraphMemoryResult } from "../graph/conversation-graph-memory.js";
50
53
  import { recordMemoryRecallLog } from "../memory-recall-log-store.js";
54
+ import { stripTailInjectionsForReinjection } from "../tail-reinjection-strip.js";
51
55
  import { MEMORY_V3_INJECTED_BLOCK_METADATA_KEY } from "../v3/ever-injected-store.js";
52
56
 
53
57
  /**
@@ -69,6 +73,39 @@ export function shouldRunLegacyMemoryRetrieval(params: {
69
73
  return params.isTrustedActor && !params.memoryV3Live;
70
74
  }
71
75
 
76
+ /**
77
+ * The voice escalation continuation is a model-control message, not a
78
+ * retrieval query. Route memory on the latest preceding user message and strip
79
+ * the runtime context that was frozen onto it during its original turn.
80
+ */
81
+ function legacyRetrievalRoutingMessages(
82
+ messages: UserPromptSubmitContext["latestMessages"],
83
+ routeOnPreviousUser: boolean,
84
+ ): UserPromptSubmitContext["latestMessages"] {
85
+ if (!routeOnPreviousUser) {
86
+ return messages;
87
+ }
88
+
89
+ for (let i = messages.length - 2; i >= 0; i -= 1) {
90
+ if (messages[i]?.role !== "user") {
91
+ continue;
92
+ }
93
+ const candidate = stripTailInjectionsForReinjection(
94
+ messages.slice(0, i + 1),
95
+ );
96
+ const tail = candidate[candidate.length - 1];
97
+ if (
98
+ tail?.role === "user" &&
99
+ tail.content.some(
100
+ (block) => block.type === "text" && block.text.trim().length > 0,
101
+ )
102
+ ) {
103
+ return candidate;
104
+ }
105
+ }
106
+ return [];
107
+ }
108
+
72
109
  /**
73
110
  * Persist and broadcast the retrieval's side effects: the injected block on
74
111
  * the user message's metadata (so it survives reloads), a recall-log row, and
@@ -263,10 +300,11 @@ async function persistInjectionBlocks(
263
300
  * runs for every actor, writing the fully injected result back onto
264
301
  * `latestMessages` and persisting the assembled blocks.
265
302
  *
266
- * Memory retrieval blocks the turn — there is no soft timeout here. Memory is
267
- * critical context, and silently dropping it produces a worse outcome than a
268
- * slower turn. Cancellation still works via `ctx.signal`, which is threaded
269
- * into `prepareMemory`.
303
+ * Memory retrieval ordinarily blocks the turn. The voice front door is the
304
+ * latency-sensitive exception: it keeps carried memory in the front prompt
305
+ * and defers current-turn retrieval to the escalated leg when one is needed.
306
+ * Cancellation still works via the live conversation signal for ordinary
307
+ * retrieval.
270
308
  */
271
309
  const userPromptSubmitMemoryRetrieval: HookFunction<
272
310
  UserPromptSubmitContext
@@ -292,9 +330,14 @@ const userPromptSubmitMemoryRetrieval: HookFunction<
292
330
  // fallback — a v3 empty/failed selection yields no NEW injected memory that
293
331
  // turn (prior turns' frozen v3 cards still ride history).
294
332
  const memoryV3Live = isMemoryV3Live(config);
333
+ const isVoiceFrontDoor = conversation?.currentCallSite === "voiceFrontDoor";
334
+ if (isVoiceFrontDoor && conversation) {
335
+ conversation.graphMemory.recordPkbQueryVectors(undefined, undefined);
336
+ }
295
337
  let v2BlockPersisted = false;
296
338
  if (
297
339
  shouldRunLegacyMemoryRetrieval({ isTrustedActor, memoryV3Live }) &&
340
+ !isVoiceFrontDoor &&
298
341
  conversation &&
299
342
  abortSignal
300
343
  ) {
@@ -311,6 +354,11 @@ const userPromptSubmitMemoryRetrieval: HookFunction<
311
354
  config,
312
355
  abortSignal,
313
356
  broadcastMessage,
357
+ legacyRetrievalRoutingMessages(
358
+ ctx.latestMessages,
359
+ ctx.isHiddenPrompt === true &&
360
+ ctx.messageKind === VOICE_ESCALATION_CONTINUATION_MESSAGE_KIND,
361
+ ),
314
362
  ),
315
363
  );
316
364
 
@@ -123,7 +123,9 @@ export async function indexMessageNow(
123
123
  // memory_segments has no cross-file FK to messages, so this call must not
124
124
  // leave pieces for a message with no row. Skip early when the source
125
125
  // row is already gone, the common case of a backfill job running after the
126
- // message was deleted. A post-write re-check below closes the narrower window
126
+ // message was deleted. Any-state existence check, deliberately: a
127
+ // streaming row exists, and orphan prevention is about deletion, not
128
+ // completeness. A post-write re-check below closes the narrower window
127
129
  // where the delete lands mid-transaction.
128
130
  const sourceMessage = getDb()
129
131
  .select({ id: messages.id })
@@ -52,18 +52,30 @@ export function isSkillCardMessage(row: { metadata: string | null }): boolean {
52
52
  }
53
53
 
54
54
  /**
55
- * `getMessagesAfter` minus skill-card rows. The retrospective job's
56
- * new-message slice: a card-only tail yields an empty slice (the job's
57
- * `no_new_messages` early return), and a mixed tail's cutoff lands on the
58
- * last real message rather than the card.
55
+ * `getMessagesAfter` truncated at the first unfinalized row, minus skill-card
56
+ * rows. The retrospective job's new-message slice: a card-only tail yields an
57
+ * empty slice (the job's `no_new_messages` early return), and a mixed tail's
58
+ * cutoff lands on the last real message rather than the card.
59
+ *
60
+ * The slice STOPS BEFORE the first unfinalized row rather than filtering it
61
+ * out. The job's cutoff (and therefore `lastProcessedMessageId` on success)
62
+ * is chosen from this slice, while the fork the job reviews skips unfinalized
63
+ * rows, so the cursor must never move past one: filtering through a stale
64
+ * mid-slice `finalized = 0` row would advance the cursor beyond it, and once
65
+ * the row later finalizes its `(createdAt, id)` sits behind the cursor,
66
+ * unreviewed forever. Truncating parks the cursor before the unfinalized row;
67
+ * the rows behind it are reviewed by the retrospective that runs after the
68
+ * row resolves (finalizes, or is repaired away by crash recovery).
59
69
  */
60
70
  export function getRetrospectiveMessagesAfter(
61
71
  conversationId: string,
62
72
  afterMessageId: string | null,
63
73
  ): MessageRow[] {
64
- return getMessagesAfter(conversationId, afterMessageId).filter(
65
- (row) => !isSkillCardMessage(row),
66
- );
74
+ const rows = getMessagesAfter(conversationId, afterMessageId);
75
+ const firstUnfinalized = rows.findIndex((row) => row.finalized !== 1);
76
+ const bounded =
77
+ firstUnfinalized === -1 ? rows : rows.slice(0, firstUnfinalized);
78
+ return bounded.filter((row) => !isSkillCardMessage(row));
67
79
  }
68
80
 
69
81
  /**
@@ -702,10 +702,10 @@ interface SourceParityPins {
702
702
  * parity; no system-prompt section branches on the flag, so this pin does not
703
703
  * affect prompt output) and the tool-context pin (the live consumer, gating
704
704
  * tool availability), using the live-turn derivation: interactive
705
- * interfaces run `updateClient(_, false)` (`hasNoClient = false`), while
706
- * channel-routed and chrome-extension turns stay clientless (`true`) — the
707
- * exact `isInteractiveInterface` predicate `conversation-routes.ts` /
708
- * `process-message.ts` apply. Pinned explicitly even when it matches the
705
+ * interfaces run their turns with `isInteractive: true` (`hasNoClient` reads
706
+ * `false` for the turn), while channel-routed and chrome-extension turns stay
707
+ * clientless (`true`) — the exact `isInteractiveInterface` predicate
708
+ * `conversation-routes.ts` / `process-message.ts` apply. Pinned explicitly even when it matches the
709
709
  * fork's hydrated value (`true`) so the parity contract doesn't depend on
710
710
  * hydration defaults.
711
711
  *
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Tests for `handleMemoryV3GateStats` in `memory-v3-routes.ts`.
3
+ *
4
+ * The handler aggregates gate telemetry from the telemetry DB and buckets it
5
+ * by concept page count. These tests cover:
6
+ * - empty DB → zero counts, all buckets present, scoredPassRate null
7
+ * - mixed rows land in the correct buckets by real_concept_page_count
8
+ * - scored vs unscored runs are counted separately
9
+ * - rows with no real_concept_page_count go to unknownPageCount
10
+ * - lookbackDays is clamped to [1, 90]
11
+ * - malformed JSON rows are skipped
12
+ * - null/absent DB returns a zero response
13
+ */
14
+
15
+ import type { Database } from "bun:sqlite";
16
+ import { describe, expect, test } from "bun:test";
17
+
18
+ import { handleMemoryV3GateStats } from "../memory-v3-routes.js";
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Minimal fake SQLite DB that returns pre-set rows
22
+ // ---------------------------------------------------------------------------
23
+
24
+ type FakeRow = { payload: string };
25
+
26
+ function makePayload(detail: Record<string, unknown>): string {
27
+ return JSON.stringify({
28
+ type: "watchdog",
29
+ daemon_event_id: "test-id",
30
+ recorded_at: Date.now(),
31
+ check_name: "memory_v3_injection_gate",
32
+ value: 1,
33
+ detail,
34
+ });
35
+ }
36
+
37
+ function fakeDb(rows: FakeRow[]): Database {
38
+ return {
39
+ query: (_sql: string) => ({
40
+ all: (..._args: unknown[]) => rows,
41
+ }),
42
+ } as unknown as Database;
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Tests
47
+ // ---------------------------------------------------------------------------
48
+
49
+ describe("handleMemoryV3GateStats", () => {
50
+ test("returns zero counts with all buckets when DB is null", () => {
51
+ const result = handleMemoryV3GateStats(30, null);
52
+ expect(result.totalRuns).toBe(0);
53
+ expect(result.buckets).toHaveLength(4);
54
+ for (const bucket of result.buckets) {
55
+ expect(bucket.total).toBe(0);
56
+ expect(bucket.scored).toBe(0);
57
+ expect(bucket.passed).toBe(0);
58
+ expect(bucket.scoredPassRate).toBeNull();
59
+ expect(bucket.reasons).toEqual({});
60
+ }
61
+ expect(result.unknownPageCount).toEqual({
62
+ total: 0,
63
+ passed: 0,
64
+ reasons: {},
65
+ });
66
+ });
67
+
68
+ test("returns zero counts with all buckets when DB returns no rows", () => {
69
+ const result = handleMemoryV3GateStats(30, fakeDb([]));
70
+ expect(result.totalRuns).toBe(0);
71
+ expect(result.buckets[0]!.pageCountRange).toBe("0–9");
72
+ expect(result.buckets[3]!.pageCountRange).toBe("200+");
73
+ for (const b of result.buckets) {
74
+ expect(b.scoredPassRate).toBeNull();
75
+ }
76
+ });
77
+
78
+ test("buckets a dense_pass row into 10–49 bucket", () => {
79
+ const rows = [
80
+ {
81
+ payload: makePayload({
82
+ pass: true,
83
+ reason: "dense_pass",
84
+ scored: true,
85
+ real_concept_page_count: 25,
86
+ }),
87
+ },
88
+ ];
89
+ const result = handleMemoryV3GateStats(30, fakeDb(rows));
90
+ expect(result.totalRuns).toBe(1);
91
+ const bucket = result.buckets.find((b) => b.pageCountRange === "10–49")!;
92
+ expect(bucket.total).toBe(1);
93
+ expect(bucket.scored).toBe(1);
94
+ expect(bucket.passed).toBe(1);
95
+ expect(bucket.scoredPassRate).toBe(1);
96
+ expect(bucket.reasons).toEqual({ dense_pass: 1 });
97
+ });
98
+
99
+ test("buckets a fail row into 200+ bucket", () => {
100
+ const rows = [
101
+ {
102
+ payload: makePayload({
103
+ pass: false,
104
+ reason: "fail_no_signal",
105
+ scored: true,
106
+ real_concept_page_count: 350,
107
+ }),
108
+ },
109
+ ];
110
+ const result = handleMemoryV3GateStats(30, fakeDb(rows));
111
+ const bucket = result.buckets.find((b) => b.pageCountRange === "200+")!;
112
+ expect(bucket.total).toBe(1);
113
+ expect(bucket.passed).toBe(0);
114
+ expect(bucket.scoredPassRate).toBe(0);
115
+ expect(bucket.reasons).toEqual({ fail_no_signal: 1 });
116
+ });
117
+
118
+ test("unscored pass-open rows count in total but not scored", () => {
119
+ const rows = [
120
+ {
121
+ payload: makePayload({
122
+ pass: true,
123
+ reason: "dense_unavailable",
124
+ scored: false,
125
+ real_concept_page_count: 5,
126
+ }),
127
+ },
128
+ ];
129
+ const result = handleMemoryV3GateStats(30, fakeDb(rows));
130
+ const bucket = result.buckets.find((b) => b.pageCountRange === "0–9")!;
131
+ expect(bucket.total).toBe(1);
132
+ expect(bucket.scored).toBe(0);
133
+ expect(bucket.passed).toBe(1);
134
+ // No scored runs → scoredPassRate is null (unscored pass does not count)
135
+ expect(bucket.scoredPassRate).toBeNull();
136
+ });
137
+
138
+ test("unscored pass mixed with scored fail does not inflate scoredPassRate", () => {
139
+ // Regression: before fix, pass-open + scored-fail in same bucket gave 100%
140
+ const rows = [
141
+ {
142
+ payload: makePayload({
143
+ pass: true,
144
+ reason: "dense_unavailable",
145
+ scored: false,
146
+ real_concept_page_count: 30,
147
+ }),
148
+ },
149
+ {
150
+ payload: makePayload({
151
+ pass: false,
152
+ reason: "fail_no_signal",
153
+ scored: true,
154
+ real_concept_page_count: 40,
155
+ }),
156
+ },
157
+ ];
158
+ const result = handleMemoryV3GateStats(30, fakeDb(rows));
159
+ const bucket = result.buckets.find((b) => b.pageCountRange === "10–49")!;
160
+ expect(bucket.total).toBe(2);
161
+ expect(bucket.scored).toBe(1);
162
+ expect(bucket.passed).toBe(1); // total passes (scored + unscored)
163
+ // Only the scored fail counts toward the rate — no scored passes
164
+ expect(bucket.scoredPassRate).toBe(0);
165
+ });
166
+
167
+ test("rows with no real_concept_page_count go to unknownPageCount", () => {
168
+ const rows = [
169
+ {
170
+ payload: makePayload({
171
+ pass: false,
172
+ reason: "fail_no_signal",
173
+ scored: true,
174
+ }),
175
+ },
176
+ ];
177
+ const result = handleMemoryV3GateStats(30, fakeDb(rows));
178
+ expect(result.unknownPageCount.total).toBe(1);
179
+ expect(result.unknownPageCount.passed).toBe(0);
180
+ expect(result.unknownPageCount.reasons).toEqual({ fail_no_signal: 1 });
181
+ for (const b of result.buckets) {
182
+ expect(b.total).toBe(0);
183
+ }
184
+ });
185
+
186
+ test("mixed rows across multiple buckets are aggregated correctly", () => {
187
+ const rows: FakeRow[] = [
188
+ // 0–9 bucket: 1 unscored pass
189
+ {
190
+ payload: makePayload({
191
+ pass: true,
192
+ reason: "dense_disabled",
193
+ scored: false,
194
+ real_concept_page_count: 3,
195
+ }),
196
+ },
197
+ // 10–49 bucket: 1 pass, 1 fail (both scored)
198
+ {
199
+ payload: makePayload({
200
+ pass: true,
201
+ reason: "dense_pass",
202
+ scored: true,
203
+ real_concept_page_count: 20,
204
+ }),
205
+ },
206
+ {
207
+ payload: makePayload({
208
+ pass: false,
209
+ reason: "fail_no_signal",
210
+ scored: true,
211
+ real_concept_page_count: 45,
212
+ }),
213
+ },
214
+ // 50–199 bucket: 2 passes
215
+ {
216
+ payload: makePayload({
217
+ pass: true,
218
+ reason: "sparse_only_strong",
219
+ scored: true,
220
+ real_concept_page_count: 100,
221
+ }),
222
+ },
223
+ {
224
+ payload: makePayload({
225
+ pass: true,
226
+ reason: "dense_pass",
227
+ scored: true,
228
+ real_concept_page_count: 150,
229
+ }),
230
+ },
231
+ ];
232
+
233
+ const result = handleMemoryV3GateStats(30, fakeDb(rows));
234
+ expect(result.totalRuns).toBe(5);
235
+
236
+ const b09 = result.buckets.find((b) => b.pageCountRange === "0–9")!;
237
+ expect(b09.total).toBe(1);
238
+ expect(b09.scored).toBe(0);
239
+ expect(b09.scoredPassRate).toBeNull();
240
+
241
+ const b1049 = result.buckets.find((b) => b.pageCountRange === "10–49")!;
242
+ expect(b1049.total).toBe(2);
243
+ expect(b1049.scored).toBe(2);
244
+ expect(b1049.passed).toBe(1);
245
+ expect(b1049.scoredPassRate).toBe(0.5);
246
+ expect(b1049.reasons).toEqual({ dense_pass: 1, fail_no_signal: 1 });
247
+
248
+ const b50199 = result.buckets.find((b) => b.pageCountRange === "50–199")!;
249
+ expect(b50199.scored).toBe(2);
250
+ expect(b50199.passed).toBe(2);
251
+ expect(b50199.scoredPassRate).toBe(1);
252
+ });
253
+
254
+ test("lookbackDays is clamped to 1–90", () => {
255
+ const r1 = handleMemoryV3GateStats(0, fakeDb([]));
256
+ expect(r1.lookbackDays).toBe(1);
257
+ const r2 = handleMemoryV3GateStats(999, fakeDb([]));
258
+ expect(r2.lookbackDays).toBe(90);
259
+ const r3 = handleMemoryV3GateStats(30, fakeDb([]));
260
+ expect(r3.lookbackDays).toBe(30);
261
+ });
262
+
263
+ test("malformed JSON payload rows are skipped", () => {
264
+ const rows: FakeRow[] = [
265
+ { payload: "not-valid-json" },
266
+ {
267
+ payload: makePayload({
268
+ pass: true,
269
+ reason: "dense_pass",
270
+ scored: true,
271
+ real_concept_page_count: 25,
272
+ }),
273
+ },
274
+ ];
275
+ const result = handleMemoryV3GateStats(30, fakeDb(rows));
276
+ // Only the valid row is counted
277
+ expect(result.totalRuns).toBe(2); // totalRuns counts raw rows before parse
278
+ const bucket = result.buckets.find((b) => b.pageCountRange === "10–49")!;
279
+ expect(bucket.total).toBe(1);
280
+ });
281
+ });
@@ -22,12 +22,9 @@ import type { RouteDefinition } from "../../../../runtime/routes/types.js";
22
22
  import type { RouteHandlerArgs } from "../../../../runtime/routes/types.js";
23
23
  import { getLogger } from "../logging.js";
24
24
  import { getWorkspaceDir } from "../paths.js";
25
- import {
26
- getEdgeIndex,
27
- totalEdgeCount,
28
- validateEdgeTargets,
29
- } from "../substrate/edge-index.js";
25
+ import { getEdgeIndex, totalEdgeCount } from "../substrate/edge-index.js";
30
26
  import { getPageIndex } from "../substrate/page-index.js";
27
+ import type { DanglingLink } from "../substrate/page-links.js";
31
28
  import {
32
29
  getConceptsDir,
33
30
  listPages,
@@ -111,14 +108,17 @@ async function handleBackfill({
111
108
 
112
109
  const MemoryV2ValidateParams = z.object({}).strict();
113
110
 
114
- type MissingEdgeEndpoint = { from: string; to: string };
115
111
  type OversizedPage = { slug: string; chars: number };
116
112
  type ParseFailure = { slug: string; error: string };
117
113
 
118
114
  export type MemoryV2ValidateResult = {
119
115
  pageCount: number;
120
116
  edgeCount: number;
121
- missingEdgeEndpoints: MissingEdgeEndpoint[];
117
+ /**
118
+ * `links:`, `[[wikilink]]`, and `edges:` references whose target has no
119
+ * page (`PageIndex.danglingLinks`).
120
+ */
121
+ danglingLinks: DanglingLink[];
122
122
  oversizedPages: OversizedPage[];
123
123
  parseFailures: ParseFailure[];
124
124
  };
@@ -162,13 +162,15 @@ async function handleValidate({
162
162
  }
163
163
  }
164
164
 
165
- const edgeIndex = await getEdgeIndex(workspaceDir);
166
- const { missing } = validateEdgeTargets(edgeIndex, knownSlugs);
165
+ const [edgeIndex, pageIndex] = await Promise.all([
166
+ getEdgeIndex(workspaceDir),
167
+ getPageIndex(workspaceDir),
168
+ ]);
167
169
 
168
170
  return {
169
171
  pageCount: knownSlugs.size,
170
172
  edgeCount: totalEdgeCount(edgeIndex),
171
- missingEdgeEndpoints: missing,
173
+ danglingLinks: pageIndex.danglingLinks,
172
174
  oversizedPages,
173
175
  parseFailures,
174
176
  };