@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
@@ -40,6 +40,18 @@ mock.module("../daemon/conversation-store.js", () => ({
40
40
  },
41
41
  }));
42
42
 
43
+ /**
44
+ * Whether the daemon activated the fixture plugin. Run-now answers only for
45
+ * plugins this process brought up, and no route test runs the plugin loader,
46
+ * so the double says "activated" unless a case turns it off.
47
+ */
48
+ let pluginActivated = true;
49
+ const realMtimeCache = await import("../plugins/mtime-cache.js");
50
+ mock.module("../plugins/mtime-cache.js", () => ({
51
+ ...realMtimeCache,
52
+ isPluginDirActivated: () => pluginActivated,
53
+ }));
54
+
43
55
  import type { AssistantEventEnvelope } from "../api/index.js";
44
56
  import { SYNC_TAGS } from "../daemon/message-types/sync.js";
45
57
  import {
@@ -1574,6 +1586,7 @@ describe("plugin-sourced schedules over routes", () => {
1574
1586
 
1575
1587
  beforeEach(() => {
1576
1588
  rmSync(RUN_MARKER, { force: true });
1589
+ pluginActivated = true;
1577
1590
  });
1578
1591
 
1579
1592
  function seedSourcedScript() {
@@ -1626,6 +1639,16 @@ describe("plugin-sourced schedules over routes", () => {
1626
1639
  expect(existsSync(RUN_MARKER)).toBe(false);
1627
1640
  });
1628
1641
 
1642
+ test("refuses a sourced row whose plugin the daemon never activated", async () => {
1643
+ const sourced = await seedSourcedScript();
1644
+ // The declaration is intact on disk, but nothing ran the plugin's
1645
+ // `init`, so run-now must not execute its script.
1646
+ pluginActivated = false;
1647
+
1648
+ await expect(runNow(sourced.id)).rejects.toThrow(BadRequestError);
1649
+ expect(existsSync(RUN_MARKER)).toBe(false);
1650
+ });
1651
+
1629
1652
  test("runs a sourced row whose plugin is healthy", async () => {
1630
1653
  const sourced = await seedSourcedScript();
1631
1654
 
@@ -23,6 +23,7 @@ import {
23
23
  } from "../persistence/conversation-crud.js";
24
24
  import { getDb } from "../persistence/db-connection.js";
25
25
  import { initializeDb } from "../persistence/db-init.js";
26
+ import { isPluginDirActivated } from "../plugins/mtime-cache.js";
26
27
  import { assistantEventHub } from "../runtime/assistant-event-hub.js";
27
28
  import {
28
29
  hasOwnerDeferProvenance,
@@ -1856,6 +1857,22 @@ describe("declared schedules", () => {
1856
1857
  expect(result!.enabled).toBe(false);
1857
1858
  });
1858
1859
 
1860
+ test("enabling a schedule re-arms it on the on-disk declaration alone", async () => {
1861
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1862
+ await setUserEnabled(created.id, false);
1863
+ // A schedule tool reaches this from a conversation turn, which can run in
1864
+ // a sidecar worker process where no plugin is activated. The probe must
1865
+ // therefore read the disk alone: asking about activation here would refuse
1866
+ // every re-enable. Whether the plugin stays armed is the reconciler's
1867
+ // call, and its next sweep disarms the row if it should not be.
1868
+ expect(isPluginDirActivated(examplePluginDir())).toBe(false);
1869
+
1870
+ const result = await setUserEnabled(created.id, true);
1871
+
1872
+ expect(result!.userEnabled).toBe(true);
1873
+ expect(result!.enabled).toBe(true);
1874
+ });
1875
+
1859
1876
  test("enabling a schedule whose plugin manifest is broken records the override without re-arming", async () => {
1860
1877
  const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1861
1878
  await setUserEnabled(created.id, false);
@@ -375,19 +375,24 @@ describe("scoped-approval-grants / expiry", () => {
375
375
  });
376
376
 
377
377
  test("already-consumed grants are not affected by expiry sweep", () => {
378
- const _pastExpiry = Date.now() - 1_000;
378
+ const expiresAt = Date.now() + 60_000;
379
379
  createScopedApprovalGrant(
380
380
  grantParams({
381
381
  scopeMode: "request_id",
382
382
  requestId: "req-consumed",
383
- expiresAt: Date.now() + 60_000,
383
+ expiresAt,
384
384
  }),
385
385
  );
386
- consumeScopedApprovalGrantByRequestId("req-consumed", "c1");
386
+ const consumed = consumeScopedApprovalGrantByRequestId(
387
+ "req-consumed",
388
+ "c1",
389
+ expiresAt - 1,
390
+ );
391
+ expect(consumed.ok).toBe(true);
387
392
 
388
- // Force the expiry time to the past for the consumed grant (simulating time passing)
389
- // The sweep should not touch consumed grants
390
- const count = expireScopedApprovalGrants();
393
+ // Sweep after the grant's expiresAt. Status is consumed, so the active-only
394
+ // expiry update must leave it alone.
395
+ const count = expireScopedApprovalGrants(expiresAt);
391
396
  expect(count).toBe(0);
392
397
  });
393
398
  });
@@ -62,7 +62,6 @@ function makeParams(
62
62
  sourceChannel: "slack" as const,
63
63
  } as any,
64
64
  replyCallbackUrl: undefined,
65
- canonicalAssistantId: "self",
66
65
  ...overrides,
67
66
  };
68
67
  }
@@ -155,7 +155,7 @@ const runAgentLoopMock = mock(async () => undefined);
155
155
  function makeSendMessageDeps() {
156
156
  const session = {
157
157
  setTrustContext: () => {},
158
- updateClient: () => {},
158
+ replayActivityState: () => {},
159
159
  emitConfirmationStateChanged: () => {},
160
160
  emitActivityState: () => {},
161
161
  setTurnChannelContext: () => {},
@@ -130,7 +130,7 @@ function makeCompletingConversation(): Conversation {
130
130
  setTurnInterfaceContext: () => {},
131
131
  ensureActorScopedHistory: async () => {},
132
132
  usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
133
- updateClient: () => {},
133
+ replayActivityState: () => {},
134
134
  setHostBrowserProxy: () => {},
135
135
  setHostCuProxy: () => {},
136
136
  setHostAppControlProxy: () => {},
@@ -180,7 +180,7 @@ function makeHangingConversation(): Conversation {
180
180
  setTurnInterfaceContext: () => {},
181
181
  ensureActorScopedHistory: async () => {},
182
182
  usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
183
- updateClient: () => {},
183
+ replayActivityState: () => {},
184
184
  setHostBrowserProxy: () => {},
185
185
  setHostCuProxy: () => {},
186
186
  setHostAppControlProxy: () => {},
@@ -261,7 +261,7 @@ function makePendingApprovalConversation(
261
261
  setTurnInterfaceContext: () => {},
262
262
  ensureActorScopedHistory: async () => {},
263
263
  usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
264
- updateClient: () => {},
264
+ replayActivityState: () => {},
265
265
  setHostBrowserProxy: () => {},
266
266
  setHostCuProxy: () => {},
267
267
  setHostAppControlProxy: () => {},
@@ -825,6 +825,38 @@ describe("always-candidate frontmatter parsing", () => {
825
825
  .map(({ id, chars }) => `${id}: ${chars}`);
826
826
  expect(overrun).toEqual([]);
827
827
  });
828
+
829
+ test("the bundled acp skill's card fits the default budget", async () => {
830
+ // `acp` owns Claude Code and Codex setup, sign-in, and delegation, and the
831
+ // terms that route those turns to it live in this card. The card is
832
+ // hard-truncated at the default budget, so an overrun drops the trailing
833
+ // hints and the whole avoid-when list from what the selector sees.
834
+ const { DEFAULT_CARD_CHARS, buildSkillContent } =
835
+ await import("../plugins/defaults/memory/substrate/skill-content.js");
836
+ const acp = loadSkillCatalog().find((skill) => skill.id === "acp");
837
+ expect(acp).toBeDefined();
838
+
839
+ // A card that overran is cut to exactly the budget, so staying under it is
840
+ // what proves nothing was dropped. `avoid-when` is appended last and is
841
+ // therefore the first content a truncation eats.
842
+ const card = buildSkillContent(acp!);
843
+ expect(card.length).toBeLessThan(DEFAULT_CARD_CHARS);
844
+ expect(card).toContain("Avoid when:");
845
+ });
846
+
847
+ test("the bundled acp card carries its setup and sign-in vocabulary", async () => {
848
+ // A "configure Claude Code" or "let's do the auth" turn only reaches this
849
+ // skill if the card actually says it covers setup and authentication;
850
+ // delegation-only wording routes those turns to a terminal skill instead.
851
+ const { buildSkillContent } =
852
+ await import("../plugins/defaults/memory/substrate/skill-content.js");
853
+ const acp = loadSkillCatalog().find((skill) => skill.id === "acp");
854
+ const card = buildSkillContent(acp!).toLowerCase();
855
+
856
+ for (const term of ["set up", "install", "authenticate", "connect"]) {
857
+ expect(card).toContain(term);
858
+ }
859
+ });
828
860
  });
829
861
 
830
862
  describe("bundled skill categories", () => {
@@ -59,7 +59,6 @@ async function applyEdit(eventId: string, content: string): Promise<void> {
59
59
  conversationExternalId: CHANNEL,
60
60
  externalMessageId: eventId,
61
61
  sourceMessageId: ORIGINAL_TS,
62
- canonicalAssistantId: "self",
63
62
  assistantId: "self",
64
63
  content,
65
64
  });
@@ -39,10 +39,12 @@ import {
39
39
  } from "../daemon/conversation-surfaces.js";
40
40
  import { asConversation } from "./helpers/mock-conversation.js";
41
41
 
42
- function makeContext(): Conversation {
42
+ function makeContext(
43
+ emit: (msg: AssistantEvent) => void = () => {},
44
+ ): Conversation {
43
45
  return asConversation({
44
46
  conversationId: "session-1",
45
- sendToClient: () => {},
47
+ emit,
46
48
  pendingSurfaceActions: new Map<string, { surfaceType: SurfaceType }>(),
47
49
  lastSurfaceAction: new Map<
48
50
  string,
@@ -131,8 +133,7 @@ describe("starter task surface actions", () => {
131
133
 
132
134
  test("app_open registers dynamic_page surface as action-capable", async () => {
133
135
  const sent: AssistantEvent[] = [];
134
- const ctx = makeContext();
135
- ctx.sendToClient = (msg) => sent.push(msg);
136
+ const ctx = makeContext((msg) => sent.push(msg));
136
137
 
137
138
  const result = await surfaceProxyResolver(ctx, "app_open", {
138
139
  app_id: "test-app",
@@ -184,8 +184,11 @@ import {
184
184
  clearConversations,
185
185
  setConversation,
186
186
  } from "../daemon/conversation-registry.js";
187
+ import type { TrustContext } from "../daemon/trust-context-types.js";
187
188
  import { CallSiteRoutingProvider } from "../providers/call-site-routing.js";
188
189
  import { SubagentManager } from "../subagent/manager.js";
190
+ import { mockAuthContext } from "./helpers/mock-actor-context.js";
191
+ import { asConversation } from "./helpers/mock-conversation.js";
189
192
 
190
193
  // Seed the workspace `llm` config per-case. The real loader schema-merges the
191
194
  // raw fragment over defaults exactly as the code path reads it.
@@ -304,26 +307,29 @@ describe("SubagentManager — provider call-site routing", () => {
304
307
  test("copies parent guardian and auth context into spawned conversation", async () => {
305
308
  setLlmConfig(makeLlmFixture());
306
309
 
307
- const parentTrustContext = {
310
+ const parentTrustContext: TrustContext = {
308
311
  sourceChannel: "vellum",
309
312
  trustClass: "guardian",
310
313
  guardianPrincipalId: "guardian-1",
311
314
  guardianExternalUserId: "guardian-1",
312
315
  };
313
- const parentAuthContext = {
316
+ const parentAuthContext = mockAuthContext({
314
317
  subject: "local:self:parent-perms",
315
318
  actorPrincipalId: "guardian-1",
316
- };
319
+ });
317
320
 
318
321
  capturedConversations.length = 0;
319
322
  clearConversations();
320
323
  const manager = new SubagentManager();
321
- setConversation("parent-perms", {
322
- trustContext: parentTrustContext,
323
- getAuthContext: () => parentAuthContext,
324
- assistantId: "self",
325
- getCurrentSystemPrompt: () => "parent system",
326
- } as any);
324
+ setConversation(
325
+ "parent-perms",
326
+ asConversation({
327
+ trustContext: parentTrustContext,
328
+ getAuthContext: () => parentAuthContext,
329
+ assistantId: "self",
330
+ getCurrentSystemPrompt: () => "parent system",
331
+ }),
332
+ );
327
333
 
328
334
  await manager.spawn(
329
335
  {
@@ -354,11 +360,14 @@ describe("SubagentManager — provider call-site routing", () => {
354
360
  capturedConversations.length = 0;
355
361
  clearConversations();
356
362
  const manager = new SubagentManager();
357
- setConversation("parent-scoped", {
358
- enabledPlugins: parentScope,
359
- getAuthContext: () => undefined,
360
- getCurrentSystemPrompt: () => "parent system",
361
- } as any);
363
+ setConversation(
364
+ "parent-scoped",
365
+ asConversation({
366
+ enabledPlugins: parentScope,
367
+ getAuthContext: () => undefined,
368
+ getCurrentSystemPrompt: () => "parent system",
369
+ }),
370
+ );
362
371
 
363
372
  await manager.spawn(
364
373
  {
@@ -383,11 +392,14 @@ describe("SubagentManager — provider call-site routing", () => {
383
392
  capturedConversations.length = 0;
384
393
  clearConversations();
385
394
  const manager = new SubagentManager();
386
- setConversation("parent-unscoped", {
387
- enabledPlugins: null,
388
- getAuthContext: () => undefined,
389
- getCurrentSystemPrompt: () => "parent system",
390
- } as any);
395
+ setConversation(
396
+ "parent-unscoped",
397
+ asConversation({
398
+ enabledPlugins: null,
399
+ getAuthContext: () => undefined,
400
+ getCurrentSystemPrompt: () => "parent system",
401
+ }),
402
+ );
391
403
 
392
404
  await manager.spawn(
393
405
  {
@@ -182,7 +182,7 @@ describe("SubagentManager fork — prompt source and role decoupling", () => {
182
182
  }
183
183
 
184
184
  test("fork with systemPromptOverride uses that prompt and does not set hasSystemPromptOverride", async () => {
185
- const overridePrompt = "You are an advisor. Frame the context as advice.";
185
+ const overridePrompt = "You are a reviewer. Frame the context as review.";
186
186
  const created = await spawnFork({ systemPromptOverride: overridePrompt });
187
187
 
188
188
  expect(created.systemPrompt).toBe(overridePrompt);
@@ -207,6 +207,7 @@ import {
207
207
  setConversation,
208
208
  } from "../daemon/conversation-registry.js";
209
209
  import { SubagentAbortedError, SubagentManager } from "../subagent/manager.js";
210
+ import { asConversation } from "./helpers/mock-conversation.js";
210
211
 
211
212
  function makeConfig(overrides: Record<string, unknown> = {}) {
212
213
  return {
@@ -229,16 +230,19 @@ function registerFakeParent(parentConversationId: string): {
229
230
  enqueuedCount: () => number;
230
231
  } {
231
232
  let enqueued = 0;
232
- setConversation(parentConversationId, {
233
- // Accessors read by setUpSubagent when copying trust/auth context.
234
- trustContext: undefined,
235
- getAuthContext: () => undefined,
236
- assistantId: undefined,
237
- enqueueMessage: () => {
238
- enqueued += 1;
239
- return { rejected: false, queued: true };
240
- },
241
- } as never);
233
+ setConversation(
234
+ parentConversationId,
235
+ asConversation({
236
+ // Accessors read by setUpSubagent when copying trust/auth context.
237
+ trustContext: undefined,
238
+ getAuthContext: () => undefined,
239
+ assistantId: undefined,
240
+ enqueueMessage: () => {
241
+ enqueued += 1;
242
+ return { rejected: false, queued: true, requestId: "req-fake" };
243
+ },
244
+ }),
245
+ );
242
246
  return { enqueuedCount: () => enqueued };
243
247
  }
244
248
 
@@ -585,20 +589,17 @@ describe("SubagentManager — first user message framing", () => {
585
589
  await manager.spawnAndAwait(
586
590
  makeConfig({
587
591
  objective: "Please advise.",
588
- fork: true,
589
592
  role: "advisor",
593
+ spawnMode: "advisor_consult",
590
594
  // The advisor always supplies its own framing; setUpSubagent uses it
591
- // verbatim and never falls back to parentSystemPrompt.
595
+ // in place of a built subagent preamble.
592
596
  systemPromptOverride: "You are a senior advisor.",
593
- parentMessages: [
594
- { role: "user", content: [{ type: "text", text: "prior turn" }] },
595
- ],
596
597
  }),
597
598
  () => {},
598
599
  );
599
600
 
600
- // The advisor's user turn is the bare advice request — the generic fork
601
- // directive would fight the advisor system prompt.
601
+ // The consult is a regular blocking spawn, so its user turn is the brief
602
+ // itself: the fork directive would fight the advisor system prompt.
602
603
  expect(lastPersistedUserMessage).toBe("Please advise.");
603
604
  expect(lastPersistedUserMessage).not.toContain("FORK TASK");
604
605
  });
@@ -118,7 +118,7 @@ function makeSetupCtx(overrides: Partial<Conversation> = {}): Conversation {
118
118
  workingDir: "/tmp/test",
119
119
  getTurnActorPrincipalId: () => undefined,
120
120
  abortController: null,
121
- sendToClient: mock(() => {}),
121
+ emit: mock(() => {}),
122
122
  pendingSurfaceActions: new Map(),
123
123
  lastSurfaceAction: new Map(),
124
124
  surfaceState: new Map(),
@@ -1073,16 +1073,24 @@ describe("Subagent spawn repeat-loop guard", () => {
1073
1073
  const objective = "Audit the advisor pipeline for drift";
1074
1074
  seedSpawns("guard-advisor", objective, 6);
1075
1075
 
1076
- const { result } = await spawnWithGuard({
1077
- label: "Consult",
1078
- objective,
1079
- role: "advisor",
1080
- });
1076
+ const manager = getSubagentManager();
1077
+ const originalAwait = manager.spawnAndAwait.bind(manager);
1078
+ manager.spawnAndAwait = (async () =>
1079
+ "guidance") as unknown as typeof manager.spawnAndAwait;
1080
+ try {
1081
+ const { result } = await spawnWithGuard({
1082
+ label: "Consult",
1083
+ objective,
1084
+ role: "advisor",
1085
+ });
1081
1086
 
1082
- // The advisor branch runs and reports its own missing-parent notice, so the
1083
- // guard never saw the repetition.
1084
- expect(result.content).toContain("advisor unavailable");
1085
- expect(result.content).not.toContain("confirm_repeat");
1087
+ // The advisor branch runs and returns its guidance, so the guard never
1088
+ // saw the repetition.
1089
+ expect(result.content).toBe("guidance");
1090
+ expect(result.content).not.toContain("confirm_repeat");
1091
+ } finally {
1092
+ manager.spawnAndAwait = originalAwait;
1093
+ }
1086
1094
  });
1087
1095
 
1088
1096
  test("copies still running trip the guard before any of them completes", async () => {
@@ -2831,19 +2839,27 @@ describe("Subagent output contract", () => {
2831
2839
  });
2832
2840
 
2833
2841
  test("a null contract reads as omitted and reaches the advisor branch", async () => {
2834
- const result = await executeSubagentSpawn(
2835
- {
2836
- label: "Consult",
2837
- objective: "Check the plan",
2838
- role: "advisor",
2839
- output_contract: null,
2840
- },
2841
- makeContext("sess-contract-advisor-none", { sendToClient: () => {} }),
2842
- );
2843
- // The advisor branch runs and reports its own missing-parent notice, so
2844
- // the contract check waved this spawn through.
2845
- expect(result.content).toContain("advisor unavailable");
2846
- expect(result.content).not.toContain("does not apply to the advisor");
2842
+ const manager = getSubagentManager();
2843
+ const originalAwait = manager.spawnAndAwait.bind(manager);
2844
+ manager.spawnAndAwait = (async () =>
2845
+ "guidance") as unknown as typeof manager.spawnAndAwait;
2846
+ try {
2847
+ const result = await executeSubagentSpawn(
2848
+ {
2849
+ label: "Consult",
2850
+ objective: "Check the plan",
2851
+ role: "advisor",
2852
+ output_contract: null,
2853
+ },
2854
+ makeContext("sess-contract-advisor-none", { sendToClient: () => {} }),
2855
+ );
2856
+ // The advisor branch runs and returns its guidance, so the contract check
2857
+ // waved this spawn through.
2858
+ expect(result.content).toBe("guidance");
2859
+ expect(result.content).not.toContain("does not apply to the advisor");
2860
+ } finally {
2861
+ manager.spawnAndAwait = originalAwait;
2862
+ }
2847
2863
  });
2848
2864
 
2849
2865
  test("verdict defaults the child to the cost-optimized tier", async () => {
@@ -2998,7 +3014,6 @@ describe("Subagent output contract", () => {
2998
3014
  // ── Advisor-role consult ────────────────────────────────────────────
2999
3015
 
3000
3016
  describe("Subagent advisor-role consult", () => {
3001
- type Block = { type: string; [k: string]: unknown };
3002
3017
  type CapturedAwait = {
3003
3018
  config: Record<string, unknown>;
3004
3019
  opts?: {
@@ -3062,16 +3077,11 @@ describe("Subagent advisor-role consult", () => {
3062
3077
  expect(result.content).toBe("Here is my advice.");
3063
3078
  // Ran synchronously through spawnAndAwait, not fire-and-forget spawn.
3064
3079
  expect(captured.current).toBeDefined();
3065
- expect(captured.current!.config.fork).toBe(true);
3066
3080
  expect(captured.current!.config.role).toBe("advisor");
3067
3081
  // The advisor is a ROLE, not an `LLMCallSiteEnum` value, so its usage
3068
3082
  // lands under `subagentSpawn` like any other subagent. The declared
3069
- // spawn mode is what separates it from a plain fork in cost telemetry.
3083
+ // spawn mode is what separates it from a plain spawn in cost telemetry.
3070
3084
  expect(captured.current!.config.spawnMode).toBe("advisor_consult");
3071
- // Framing embeds the executor prompt as advisor system prompt context.
3072
- expect(captured.current!.config.systemPromptOverride).toContain(
3073
- "PARENT SYSTEM PROMPT",
3074
- );
3075
3085
  // The situational context pack must never ride display surfaces: the
3076
3086
  // system prompt stays minimal and `objective` (rendered verbatim by the
3077
3087
  // subagent detail panel) stays the concise advice request. Only the
@@ -3089,26 +3099,18 @@ describe("Subagent advisor-role consult", () => {
3089
3099
  }
3090
3100
  });
3091
3101
 
3092
- test("advisor inherits and sanitizes the parent transcript", async () => {
3093
- // Parent in-memory history carries a thinking block (must be stripped) and
3094
- // a completed tool_use/tool_result pair (must be preserved).
3102
+ test("advisor carries no parent transcript or system prompt", async () => {
3103
+ // The consult is a regular blocking spawn: the brief is its whole input, so
3104
+ // nothing of the parent conversation is snapshotted into it.
3095
3105
  mockFindConversation = () => ({
3096
3106
  messages: [
3097
3107
  { role: "user", content: [{ type: "text", text: "Do the task" }] },
3098
3108
  {
3099
3109
  role: "assistant",
3100
- content: [
3101
- { type: "thinking", thinking: "secret reasoning" },
3102
- { type: "text", text: "Working on it" },
3103
- { type: "tool_use", id: "t1", name: "bash", input: {} },
3104
- ],
3105
- },
3106
- {
3107
- role: "user",
3108
- content: [{ type: "tool_result", tool_use_id: "t1", content: "ok" }],
3110
+ content: [{ type: "text", text: "Working on it" }],
3109
3111
  },
3110
3112
  ],
3111
- getCurrentSystemPrompt: () => "SYS",
3113
+ getCurrentSystemPrompt: () => "PARENT SYSTEM PROMPT",
3112
3114
  });
3113
3115
  const { captured, restore } = stubAwait("advice");
3114
3116
  try {
@@ -3116,77 +3118,55 @@ describe("Subagent advisor-role consult", () => {
3116
3118
  { label: "Consult", objective: "x", role: "advisor" },
3117
3119
  makeContext("advisor-sess-2", { sendToClient: () => {} }),
3118
3120
  );
3119
- const msgs = captured.current!.config.parentMessages as Array<{
3120
- role: string;
3121
- content: Block[];
3122
- }>;
3123
- const allBlocks = msgs.flatMap((m) => m.content);
3124
- // Thinking is stripped; the completed tool_use/tool_result pair survives.
3125
- expect(allBlocks.some((b) => b.type === "thinking")).toBe(false);
3126
- expect(allBlocks.some((b) => b.type === "tool_use")).toBe(true);
3127
- expect(allBlocks.some((b) => b.type === "tool_result")).toBe(true);
3121
+ expect(captured.current!.config.fork).toBeUndefined();
3122
+ expect(captured.current!.config.parentMessages).toBeUndefined();
3123
+ expect(captured.current!.config.parentSystemPrompt).toBeUndefined();
3124
+ expect(captured.current!.config.systemPromptOverride).not.toContain(
3125
+ "PARENT SYSTEM PROMPT",
3126
+ );
3128
3127
  } finally {
3129
3128
  restore();
3130
3129
  mockFindConversation = () => undefined;
3131
3130
  }
3132
3131
  });
3133
3132
 
3134
- test("in-flight plan is visible to the advisor with no dangling tool_use", async () => {
3135
- // The in-memory snapshot ends on the user turn — the in-flight assistant
3136
- // turn (this turn's plan + the pending advisor tool_use) lives only in the
3137
- // DB at consult time. It must be appended, and its dangling tool_use stripped.
3138
- mockFindConversation = () => ({
3139
- messages: [
3140
- { role: "user", content: [{ type: "text", text: "Plan the work" }] },
3141
- ],
3142
- getCurrentSystemPrompt: () => "SYS",
3143
- });
3144
- mockGetMessages = (convId: string) => {
3145
- if (convId !== "advisor-sess-3") {
3146
- return null;
3147
- }
3148
- return [
3149
- {
3150
- role: "user",
3151
- content: [{ type: "text", text: "Plan the work" }],
3152
- },
3153
- {
3154
- role: "assistant",
3155
- content: [
3156
- { type: "text", text: "My plan: step 1, step 2." },
3157
- {
3158
- type: "tool_use",
3159
- id: "adv-1",
3160
- name: "subagent_spawn",
3161
- input: {},
3162
- },
3163
- ],
3164
- },
3165
- ];
3166
- };
3133
+ test("the agent's objective reaches the advisor as its brief", async () => {
3134
+ // The brief is the advisor's only account of the work, so it must travel
3135
+ // verbatim inside the request fence.
3136
+ const brief = [
3137
+ "Task: migrate the queue to the new store.",
3138
+ "Plan: dual-write, then cut over.",
3139
+ "Evidence: src/queue/store.ts already exports both writers.",
3140
+ "Question: is dual-write the right shape here?",
3141
+ ].join("\n");
3167
3142
  const { captured, restore } = stubAwait("advice");
3168
3143
  try {
3169
3144
  await executeSubagentSpawn(
3170
- { label: "Consult", objective: "x", role: "advisor" },
3171
- makeContext("advisor-sess-3", { sendToClient: () => {} }),
3145
+ { label: "Consult", objective: brief, role: "advisor" },
3146
+ makeContext("advisor-sess-brief", { sendToClient: () => {} }),
3147
+ );
3148
+ expect(captured.current!.config.requestText).toContain(
3149
+ `<agent_request>\n${brief}\n</agent_request>`,
3172
3150
  );
3173
- const msgs = captured.current!.config.parentMessages as Array<{
3174
- role: string;
3175
- content: Block[];
3176
- }>;
3177
- const allBlocks = msgs.flatMap((m) => m.content);
3178
- // The plan text the model wrote this turn is present in the consult.
3179
- expect(
3180
- allBlocks.some(
3181
- (b) => b.type === "text" && b.text === "My plan: step 1, step 2.",
3182
- ),
3183
- ).toBe(true);
3184
- // No dangling tool_use is sent.
3185
- expect(allBlocks.some((b) => b.type === "tool_use")).toBe(false);
3186
3151
  } finally {
3187
3152
  restore();
3188
- mockFindConversation = () => undefined;
3189
- mockGetMessages = () => null;
3153
+ }
3154
+ });
3155
+
3156
+ test("advisor consults still run when the parent conversation is gone", async () => {
3157
+ // The parent is looked up only for its warm skill catalog, so an evicted
3158
+ // conversation costs a section of the context pack, not the consult.
3159
+ mockFindConversation = () => undefined;
3160
+ const { captured, restore } = stubAwait("advice");
3161
+ try {
3162
+ const result = await executeSubagentSpawn(
3163
+ { label: "Consult", objective: "advise me", role: "advisor" },
3164
+ makeContext("advisor-sess-no-parent", { sendToClient: () => {} }),
3165
+ );
3166
+ expect(result.content).toBe("advice");
3167
+ expect(captured.current!.config.role).toBe("advisor");
3168
+ } finally {
3169
+ restore();
3190
3170
  }
3191
3171
  });
3192
3172