@vellumai/assistant 0.7.0 → 0.7.1

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 (666) hide show
  1. package/ARCHITECTURE.md +6 -7
  2. package/Dockerfile +1 -0
  3. package/README.md +2 -2
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +79 -139
  5. package/bun.lock +3 -0
  6. package/docs/architecture/security.md +18 -16
  7. package/knip.json +1 -0
  8. package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +1 -5
  9. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -5
  10. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -16
  11. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +1 -9
  12. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +12 -12
  13. package/node_modules/@vellumai/slack-text/bun.lock +24 -0
  14. package/node_modules/@vellumai/slack-text/package.json +18 -0
  15. package/node_modules/@vellumai/slack-text/src/index.test.ts +153 -0
  16. package/node_modules/@vellumai/slack-text/src/index.ts +235 -0
  17. package/node_modules/@vellumai/slack-text/tsconfig.json +20 -0
  18. package/openapi.yaml +294 -107
  19. package/package.json +4 -2
  20. package/scripts/generate-openapi.ts +16 -111
  21. package/src/__tests__/agent-wake-override-profile.test.ts +23 -1
  22. package/src/__tests__/anthropic-provider.test.ts +56 -13
  23. package/src/__tests__/app-conversation-ids-backfill.test.ts +278 -0
  24. package/src/__tests__/app-conversation-ids.test.ts +151 -0
  25. package/src/__tests__/approval-cascade.test.ts +0 -15
  26. package/src/__tests__/approval-routes-http.test.ts +6 -17
  27. package/src/__tests__/assistant-event-hub.test.ts +126 -77
  28. package/src/__tests__/assistant-event.test.ts +0 -5
  29. package/src/__tests__/assistant-events-sse-hardening.test.ts +37 -15
  30. package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -29
  31. package/src/__tests__/background-shell-host-bash.test.ts +34 -43
  32. package/src/__tests__/call-controller.test.ts +1 -1
  33. package/src/__tests__/call-site-routing-provider.test.ts +193 -0
  34. package/src/__tests__/channel-approval-routes.test.ts +10 -296
  35. package/src/__tests__/channel-approvals.test.ts +25 -17
  36. package/src/__tests__/channel-guardian.test.ts +100 -146
  37. package/src/__tests__/checker.test.ts +20 -34
  38. package/src/__tests__/compact-event-conversation-id-guard.test.ts +50 -0
  39. package/src/__tests__/compaction-events.test.ts +2 -0
  40. package/src/__tests__/config-schema.test.ts +6 -48
  41. package/src/__tests__/config-watcher.test.ts +12 -0
  42. package/src/__tests__/connection-policy.test.ts +1 -52
  43. package/src/__tests__/contacts-write.test.ts +2 -64
  44. package/src/__tests__/context-image-dimensions.test.ts +1 -1
  45. package/src/__tests__/context-search-memory-source.test.ts +120 -1
  46. package/src/__tests__/context-search-memory-v2-source.test.ts +383 -0
  47. package/src/__tests__/context-search-pkb-source.test.ts +49 -0
  48. package/src/__tests__/context-search-workspace-source.test.ts +9 -22
  49. package/src/__tests__/context-window-manager.test.ts +46 -0
  50. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  51. package/src/__tests__/conversation-agent-loop-overflow.test.ts +102 -29
  52. package/src/__tests__/conversation-agent-loop.test.ts +980 -13
  53. package/src/__tests__/conversation-analysis-routes.test.ts +12 -10
  54. package/src/__tests__/conversation-attention-telegram.test.ts +11 -3
  55. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -291
  56. package/src/__tests__/conversation-history-web-search.test.ts +4 -3
  57. package/src/__tests__/conversation-inference-profile-route.test.ts +12 -23
  58. package/src/__tests__/conversation-lifecycle.test.ts +4 -4
  59. package/src/__tests__/conversation-process-callsite.test.ts +79 -2
  60. package/src/__tests__/conversation-queue.test.ts +3 -8
  61. package/src/__tests__/conversation-routes-disk-view.test.ts +1 -161
  62. package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -32
  63. package/src/__tests__/conversation-routes-slash-commands.test.ts +75 -66
  64. package/src/__tests__/conversation-runtime-assembly.test.ts +257 -3
  65. package/src/__tests__/conversation-slash-commands.test.ts +24 -4
  66. package/src/__tests__/conversation-slash-queue.test.ts +2 -0
  67. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  68. package/src/__tests__/conversation-starter-routes.test.ts +79 -2
  69. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -5
  70. package/src/__tests__/conversation-surfaces-standalone.test.ts +18 -14
  71. package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -2
  72. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +8 -46
  73. package/src/__tests__/conversation-usage.test.ts +253 -3
  74. package/src/__tests__/credential-execution-shell-lockdown.test.ts +0 -39
  75. package/src/__tests__/credential-health-service.test.ts +68 -0
  76. package/src/__tests__/credential-security-e2e.test.ts +4 -3
  77. package/src/__tests__/credential-security-invariants.test.ts +1 -5
  78. package/src/__tests__/credential-token-resolver.test.ts +180 -0
  79. package/src/__tests__/cu-unified-flow.test.ts +33 -16
  80. package/src/__tests__/daemon-assistant-events.test.ts +34 -21
  81. package/src/__tests__/daemon-credential-client.test.ts +4 -1
  82. package/src/__tests__/db-connection-isolation.test.ts +125 -0
  83. package/src/__tests__/db-migration-rollback.test.ts +101 -0
  84. package/src/__tests__/db-slack-compaction-watermark-migration.test.ts +169 -0
  85. package/src/__tests__/deterministic-verification-control-plane.test.ts +7 -80
  86. package/src/__tests__/document-conversations.test.ts +332 -0
  87. package/src/__tests__/embedding-managed-proxy-selection.test.ts +2 -2
  88. package/src/__tests__/emit-event-signal.test.ts +4 -6
  89. package/src/__tests__/events-client-registration.test.ts +193 -49
  90. package/src/__tests__/filing-service.test.ts +58 -7
  91. package/src/__tests__/first-greeting.test.ts +156 -150
  92. package/src/__tests__/fixtures/mock-chrome-extension.ts +108 -66
  93. package/src/__tests__/get-skill-detail-audit.test.ts +3 -8
  94. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  95. package/src/__tests__/guardian-dispatch.test.ts +1 -1
  96. package/src/__tests__/guardian-grant-minting.test.ts +7 -2
  97. package/src/__tests__/guardian-routing-invariants.test.ts +7 -2
  98. package/src/__tests__/guardian-routing-state.test.ts +1 -1
  99. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +32 -11
  100. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +2 -83
  101. package/src/__tests__/headless-browser-mode.test.ts +4 -9
  102. package/src/__tests__/headless-browser-navigate.test.ts +21 -20
  103. package/src/__tests__/heartbeat-service.test.ts +289 -7
  104. package/src/__tests__/helpers/channel-test-adapter.ts +2 -2
  105. package/src/__tests__/helpers/create-guardian-binding.ts +91 -0
  106. package/src/__tests__/host-bash-proxy.test.ts +46 -122
  107. package/src/__tests__/host-browser-e2e-cloud.test.ts +36 -497
  108. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +26 -96
  109. package/src/__tests__/host-browser-proxy.test.ts +111 -185
  110. package/src/__tests__/host-browser-routes.test.ts +45 -75
  111. package/src/__tests__/host-browser-ws-events-e2e.test.ts +26 -30
  112. package/src/__tests__/host-cu-proxy.test.ts +56 -111
  113. package/src/__tests__/host-file-proxy.test.ts +44 -98
  114. package/src/__tests__/host-file-read-tool.test.ts +42 -21
  115. package/src/__tests__/host-shell-tool.test.ts +33 -68
  116. package/src/__tests__/host-transfer-pending-interactions.test.ts +2 -18
  117. package/src/__tests__/host-transfer-proxy.test.ts +43 -53
  118. package/src/__tests__/http-user-message-parity.test.ts +0 -6
  119. package/src/__tests__/inbound-slack-persistence.test.ts +31 -0
  120. package/src/__tests__/injector-chain.test.ts +10 -5
  121. package/src/__tests__/injector-pkb-v2-silenced.test.ts +124 -0
  122. package/src/__tests__/inline-command-runner.test.ts +0 -66
  123. package/src/__tests__/inline-skill-load-permissions.test.ts +0 -2
  124. package/src/__tests__/install-skill-routing.test.ts +1 -13
  125. package/src/__tests__/llm-callsite-catalog.test.ts +34 -0
  126. package/src/__tests__/llm-catalog-parity.test.ts +90 -0
  127. package/src/__tests__/llm-context-resolution.test.ts +180 -0
  128. package/src/__tests__/llm-resolver.test.ts +80 -12
  129. package/src/__tests__/llm-usage-store.test.ts +269 -4
  130. package/src/__tests__/log-export-routes.test.ts +89 -0
  131. package/src/__tests__/managed-profile-guard.test.ts +225 -0
  132. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -10
  133. package/src/__tests__/manual-token-reconciliation.test.ts +334 -0
  134. package/src/__tests__/memory-v2-static-injector.test.ts +95 -0
  135. package/src/__tests__/migration-cross-version-compatibility.test.ts +197 -291
  136. package/src/__tests__/migration-export-http.test.ts +33 -26
  137. package/src/__tests__/migration-export-streaming.test.ts +18 -10
  138. package/src/__tests__/migration-export-to-gcs.test.ts +49 -9
  139. package/src/__tests__/migration-import-commit-http.test.ts +66 -21
  140. package/src/__tests__/migration-import-from-gcs.test.ts +50 -9
  141. package/src/__tests__/migration-import-from-url.test.ts +20 -6
  142. package/src/__tests__/migration-import-preflight-http.test.ts +95 -95
  143. package/src/__tests__/migration-parity-persistence.test.ts +62 -25
  144. package/src/__tests__/migration-transport.test.ts +115 -23
  145. package/src/__tests__/migration-validate-http.test.ts +105 -80
  146. package/src/__tests__/migration-wizard.test.ts +133 -27
  147. package/src/__tests__/non-member-access-request.test.ts +1 -1
  148. package/src/__tests__/notification-guardian-path.test.ts +1 -1
  149. package/src/__tests__/oauth-store.test.ts +19 -0
  150. package/src/__tests__/platform-bash-auto-approve.test.ts +21 -12
  151. package/src/__tests__/prechat-onboarding-contract.test.ts +31 -7
  152. package/src/__tests__/pricing.test.ts +68 -4
  153. package/src/__tests__/process-message-background-slack.test.ts +331 -0
  154. package/src/__tests__/provider-managed-proxy-integration.test.ts +153 -17
  155. package/src/__tests__/provider-send-message-override-profile.test.ts +50 -0
  156. package/src/__tests__/provider-usage-tracking.test.ts +208 -0
  157. package/src/__tests__/reaction-persistence.test.ts +9 -6
  158. package/src/__tests__/rebind-secrets-screen.test.ts +53 -16
  159. package/src/__tests__/recording-handler.test.ts +64 -81
  160. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +4 -3
  161. package/src/__tests__/relay-server.test.ts +18 -13
  162. package/src/__tests__/require-fresh-approval.test.ts +13 -22
  163. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
  164. package/src/__tests__/runtime-events-sse-parity.test.ts +3 -4
  165. package/src/__tests__/runtime-events-sse.test.ts +3 -12
  166. package/src/__tests__/search-skills-unified.test.ts +9 -15
  167. package/src/__tests__/secret-ingress-cli.test.ts +2 -5
  168. package/src/__tests__/secret-ingress-http.test.ts +0 -4
  169. package/src/__tests__/secret-onetime-send.test.ts +4 -2
  170. package/src/__tests__/secret-prompt-log-hygiene.test.ts +24 -7
  171. package/src/__tests__/secret-prompter-channel-fallback.test.ts +42 -47
  172. package/src/__tests__/secret-response-routing.test.ts +29 -15
  173. package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -1
  174. package/src/__tests__/secret-scanner.test.ts +2 -545
  175. package/src/__tests__/send-endpoint-busy.test.ts +9 -24
  176. package/src/__tests__/settings-routes.test.ts +1 -1
  177. package/src/__tests__/shell-credential-ref.test.ts +0 -8
  178. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -56
  179. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -11
  180. package/src/__tests__/skill-tool-factory.test.ts +97 -0
  181. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -30
  182. package/src/__tests__/skills-files-catalog-fallback.test.ts +11 -17
  183. package/src/__tests__/slack-inbound-verification.test.ts +1 -62
  184. package/src/__tests__/subagent-fork-notifications.test.ts +57 -47
  185. package/src/__tests__/subagent-manager-notify.test.ts +70 -70
  186. package/src/__tests__/subagent-notify-parent.test.ts +80 -83
  187. package/src/__tests__/system-prompt.test.ts +115 -13
  188. package/src/__tests__/terminal-tools.test.ts +0 -89
  189. package/src/__tests__/thread-backfill.test.ts +945 -31
  190. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -36
  191. package/src/__tests__/tool-execute-pipeline.test.ts +0 -6
  192. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -16
  193. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +9 -19
  194. package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -7
  195. package/src/__tests__/tool-executor.test.ts +12 -19
  196. package/src/__tests__/tool-metrics-listener.test.ts +0 -35
  197. package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
  198. package/src/__tests__/tool-trace-listener.test.ts +0 -17
  199. package/src/__tests__/transfer-progress-screen.test.ts +63 -26
  200. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -149
  201. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -4
  202. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  203. package/src/__tests__/tts-catalog-parity.test.ts +16 -5
  204. package/src/__tests__/usage-attribution.test.ts +247 -0
  205. package/src/__tests__/usage-cli.test.ts +143 -0
  206. package/src/__tests__/usage-grouped-buckets.test.ts +155 -0
  207. package/src/__tests__/usage-routes.test.ts +150 -0
  208. package/src/__tests__/validation-results-screen.test.ts +39 -16
  209. package/src/__tests__/vbundle-pax-and-symlink.test.ts +12 -3
  210. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +49 -137
  211. package/src/__tests__/verification-control-plane-policy.test.ts +4 -7
  212. package/src/__tests__/voice-session-bridge.test.ts +5 -5
  213. package/src/__tests__/workspace-migration-062-drop-memory-v2-edges-json.test.ts +103 -0
  214. package/src/__tests__/workspace-migration-063-release-notes-dynamic-model-context.test.ts +77 -0
  215. package/src/__tests__/workspace-migration-064-unwind-main-agent-opus-seed.test.ts +225 -0
  216. package/src/__tests__/workspace-migration-memory-v2-init.test.ts +8 -30
  217. package/src/acp/index.ts +0 -15
  218. package/src/acp/session-manager.ts +37 -34
  219. package/src/agent/loop.ts +16 -1
  220. package/src/approvals/AGENTS.md +4 -0
  221. package/src/approvals/__tests__/guardian-feed-event.test.ts +10 -3
  222. package/src/approvals/guardian-request-resolvers.ts +10 -2
  223. package/src/backup/__tests__/backup-worker.test.ts +36 -8
  224. package/src/backup/__tests__/paths.test.ts +2 -2
  225. package/src/backup/__tests__/restore.test.ts +45 -28
  226. package/src/backup/backup-worker.ts +36 -2
  227. package/src/backup/paths.ts +9 -6
  228. package/src/browser-session/events.ts +0 -9
  229. package/src/calls/call-store.ts +1 -34
  230. package/src/calls/guardian-question-copy.ts +0 -108
  231. package/src/calls/relay-server.ts +0 -24
  232. package/src/calls/twilio-rest.ts +0 -38
  233. package/src/calls/twilio-routes.ts +1 -1
  234. package/src/calls/voice-session-bridge.ts +7 -38
  235. package/src/channels/types.ts +1 -36
  236. package/src/cli/commands/__tests__/cache.test.ts +152 -5
  237. package/src/cli/commands/__tests__/memory-v2.test.ts +14 -28
  238. package/src/cli/commands/__tests__/trust.test.ts +21 -387
  239. package/src/cli/commands/backup.ts +4 -4
  240. package/src/cli/commands/cache-fs.ts +8 -0
  241. package/src/cli/commands/cache.ts +153 -82
  242. package/src/cli/commands/clients.ts +63 -5
  243. package/src/cli/commands/completions.ts +3 -3
  244. package/src/cli/commands/contacts.ts +231 -76
  245. package/src/cli/commands/keys.ts +4 -1
  246. package/src/cli/commands/memory-v2.ts +24 -52
  247. package/src/cli/commands/oauth/shared.ts +2 -29
  248. package/src/cli/commands/pending.ts +102 -0
  249. package/src/cli/commands/skills.ts +77 -35
  250. package/src/cli/commands/trust.ts +70 -430
  251. package/src/cli/commands/usage.ts +25 -16
  252. package/src/cli/lib/daemon-credential-client.ts +14 -0
  253. package/src/cli/program.ts +2 -0
  254. package/src/cli.ts +0 -21
  255. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  256. package/src/config/bundled-skills/messaging/TOOLS.json +14 -4
  257. package/src/config/env-registry.ts +12 -2
  258. package/src/config/env.ts +3 -14
  259. package/src/config/feature-flag-registry.json +30 -30
  260. package/src/config/llm-callsite-catalog.ts +12 -0
  261. package/src/config/llm-context-resolution.ts +80 -0
  262. package/src/config/llm-resolver.ts +58 -22
  263. package/src/config/loader.ts +3 -3
  264. package/src/config/schema.ts +2 -158
  265. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  266. package/src/config/schemas/call-site-catalog.ts +271 -0
  267. package/src/config/schemas/calls.ts +5 -5
  268. package/src/config/schemas/inference.ts +1 -1
  269. package/src/config/schemas/ingress.ts +1 -1
  270. package/src/config/schemas/llm.ts +31 -3
  271. package/src/config/schemas/memory-retrieval.ts +2 -2
  272. package/src/config/schemas/memory-v2.ts +9 -0
  273. package/src/config/schemas/security.ts +1 -42
  274. package/src/config/schemas/services.ts +6 -6
  275. package/src/config/schemas/skills.ts +5 -5
  276. package/src/config/schemas/tts.ts +1 -1
  277. package/src/config/seed-inference-profiles.ts +117 -0
  278. package/src/config/skills.ts +0 -90
  279. package/src/config/types.ts +3 -6
  280. package/src/contacts/contact-store.ts +0 -17
  281. package/src/contacts/contacts-write.ts +1 -105
  282. package/src/context/window-manager.ts +44 -5
  283. package/src/credential-execution/process-manager.ts +34 -10
  284. package/src/credential-health/credential-health-service.ts +21 -16
  285. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +75 -82
  286. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -9
  287. package/src/daemon/connection-policy.ts +1 -26
  288. package/src/daemon/conversation-agent-loop-handlers.ts +53 -4
  289. package/src/daemon/conversation-agent-loop.ts +277 -36
  290. package/src/daemon/conversation-history.ts +8 -8
  291. package/src/daemon/conversation-launch.ts +20 -135
  292. package/src/daemon/conversation-lifecycle.ts +1 -1
  293. package/src/daemon/conversation-messaging.ts +1 -0
  294. package/src/daemon/conversation-process.ts +83 -163
  295. package/src/daemon/conversation-runtime-assembly.ts +219 -76
  296. package/src/daemon/conversation-slash.ts +47 -5
  297. package/src/daemon/conversation-store.ts +7 -31
  298. package/src/daemon/conversation-surfaces.ts +22 -28
  299. package/src/daemon/conversation-tool-setup.ts +3 -33
  300. package/src/daemon/conversation-usage.ts +36 -0
  301. package/src/daemon/conversation.ts +117 -233
  302. package/src/daemon/daemon-control.ts +3 -71
  303. package/src/daemon/daemon-skill-host.ts +8 -11
  304. package/src/daemon/dictation-profile-store.ts +2 -26
  305. package/src/daemon/first-greeting.ts +44 -156
  306. package/src/daemon/handlers/config-channels.ts +12 -12
  307. package/src/daemon/handlers/config-ingress.ts +4 -165
  308. package/src/daemon/handlers/config-model.ts +1 -1
  309. package/src/daemon/handlers/config-voice.ts +0 -42
  310. package/src/daemon/handlers/conversations.ts +11 -190
  311. package/src/daemon/handlers/recording.ts +26 -158
  312. package/src/daemon/handlers/shared.ts +23 -71
  313. package/src/daemon/handlers/skills.ts +42 -93
  314. package/src/daemon/host-bash-proxy.ts +67 -45
  315. package/src/daemon/host-browser-proxy.ts +65 -27
  316. package/src/daemon/host-cu-proxy.ts +40 -39
  317. package/src/daemon/host-file-proxy.ts +58 -37
  318. package/src/daemon/host-transfer-proxy.ts +84 -46
  319. package/src/daemon/lifecycle.ts +49 -15
  320. package/src/daemon/message-types/conversations.ts +7 -0
  321. package/src/daemon/message-types/host-bash.ts +1 -0
  322. package/src/daemon/message-types/host-cu.ts +1 -0
  323. package/src/daemon/message-types/host-file.ts +1 -0
  324. package/src/daemon/message-types/host-transfer.ts +1 -0
  325. package/src/daemon/message-types/messages.ts +10 -9
  326. package/src/daemon/message-types/workspace.ts +1 -1
  327. package/src/daemon/process-message.ts +102 -239
  328. package/src/daemon/server.ts +13 -462
  329. package/src/daemon/shutdown-handlers.ts +2 -2
  330. package/src/daemon/tool-side-effects.ts +125 -107
  331. package/src/daemon/trust-context.ts +13 -0
  332. package/src/daemon/wake-target-adapter.ts +4 -9
  333. package/src/events/domain-events.ts +0 -8
  334. package/src/events/tool-audit-listener.ts +3 -1
  335. package/src/events/tool-domain-event-publisher.ts +0 -10
  336. package/src/events/tool-metrics-listener.ts +0 -17
  337. package/src/events/tool-trace-listener.ts +0 -14
  338. package/src/filing/filing-service.ts +13 -1
  339. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +6 -2
  340. package/src/heartbeat/heartbeat-service.ts +23 -5
  341. package/src/home/__tests__/feed-writer.test.ts +0 -4
  342. package/src/home/__tests__/relationship-state-writer.test.ts +30 -0
  343. package/src/home/feed-writer.ts +1 -2
  344. package/src/home/relationship-state-writer.ts +16 -3
  345. package/src/ipc/__tests__/browser-ipc.test.ts +2 -12
  346. package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -1
  347. package/src/ipc/assistant-server.ts +3 -10
  348. package/src/ipc/routes/__tests__/memory-v2-backfill.test.ts +39 -20
  349. package/src/ipc/routes/route-adapter.ts +1 -1
  350. package/src/ipc/routes/trust-rules.test.ts +0 -95
  351. package/src/ipc/skill-ipc-types.ts +41 -0
  352. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +13 -27
  353. package/src/ipc/skill-routes/__tests__/identity.test.ts +4 -23
  354. package/src/ipc/skill-routes/events.ts +12 -23
  355. package/src/ipc/skill-routes/identity.ts +4 -17
  356. package/src/ipc/skill-routes/index.ts +1 -1
  357. package/src/ipc/skill-server.ts +6 -39
  358. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +0 -8
  359. package/src/live-voice/protocol.ts +4 -13
  360. package/src/mcp/manager.ts +0 -5
  361. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +55 -0
  362. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +127 -0
  363. package/src/memory/app-git-service.ts +0 -32
  364. package/src/memory/app-store.ts +154 -0
  365. package/src/memory/attachments-store.ts +6 -0
  366. package/src/memory/context-search/sources/memory-v2.ts +578 -0
  367. package/src/memory/context-search/sources/memory.ts +5 -0
  368. package/src/memory/context-search/sources/pkb.ts +10 -1
  369. package/src/memory/context-search/sources/workspace.ts +3 -2
  370. package/src/memory/conversation-crud.ts +29 -4
  371. package/src/memory/conversation-disk-view.ts +1 -5
  372. package/src/memory/conversation-starter-checkpoints.ts +63 -0
  373. package/src/memory/db-connection.ts +62 -0
  374. package/src/memory/db-init.ts +14 -0
  375. package/src/memory/embedding-backend.ts +3 -21
  376. package/src/memory/embedding-gemini.ts +0 -2
  377. package/src/memory/embedding-local.ts +6 -6
  378. package/src/memory/embedding-ollama.ts +6 -6
  379. package/src/memory/embedding-openai.ts +6 -6
  380. package/src/memory/embedding-types.ts +21 -0
  381. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +3 -7
  382. package/src/memory/graph/conversation-graph-memory.ts +35 -13
  383. package/src/memory/graph/injection.test.ts +2 -2
  384. package/src/memory/graph/injection.ts +1 -1
  385. package/src/memory/guardian-action-store.ts +0 -83
  386. package/src/memory/guardian-approvals.ts +0 -48
  387. package/src/memory/indexer.ts +1 -15
  388. package/src/memory/job-handlers/conversation-starters.ts +36 -53
  389. package/src/memory/job-utils.ts +0 -6
  390. package/src/memory/jobs-store.ts +0 -1
  391. package/src/memory/jobs-worker.ts +2 -16
  392. package/src/memory/llm-request-log-store.ts +0 -41
  393. package/src/memory/llm-usage-store.ts +129 -43
  394. package/src/memory/memory-v2-activation-log-store.ts +115 -0
  395. package/src/memory/migrations/233-document-conversations.ts +54 -0
  396. package/src/memory/migrations/234-memory-v2-activation-logs.ts +55 -0
  397. package/src/memory/migrations/235-llm-usage-attribution.ts +31 -0
  398. package/src/memory/migrations/235-slack-compaction-watermark.ts +44 -0
  399. package/src/memory/migrations/236-tool-invocations-matched-rule-id.ts +26 -0
  400. package/src/memory/migrations/__tests__/234-memory-v2-activation-logs.test.ts +182 -0
  401. package/src/memory/migrations/index.ts +14 -0
  402. package/src/memory/migrations/registry.ts +24 -0
  403. package/src/memory/raw-query.ts +2 -68
  404. package/src/memory/schema/conversations.ts +7 -0
  405. package/src/memory/schema/infrastructure.ts +25 -0
  406. package/src/memory/search/semantic.ts +5 -16
  407. package/src/memory/tool-usage-store.ts +2 -0
  408. package/src/memory/usage-buckets.ts +40 -1
  409. package/src/memory/usage-grouped-buckets.ts +127 -0
  410. package/src/memory/v2/__tests__/activation.test.ts +289 -90
  411. package/src/memory/v2/__tests__/backfill-jobs.test.ts +2 -129
  412. package/src/memory/v2/__tests__/consolidation-job.test.ts +28 -11
  413. package/src/memory/v2/__tests__/edge-index.test.ts +278 -0
  414. package/src/memory/v2/__tests__/injection.test.ts +384 -15
  415. package/src/memory/v2/__tests__/migration.test.ts +64 -36
  416. package/src/memory/v2/__tests__/page-store.test.ts +191 -8
  417. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +181 -0
  418. package/src/memory/v2/__tests__/skill-store.test.ts +115 -3
  419. package/src/memory/v2/__tests__/static-context.test.ts +153 -0
  420. package/src/memory/v2/activation.ts +168 -97
  421. package/src/memory/v2/backfill-jobs.ts +15 -100
  422. package/src/memory/v2/consolidation-job.ts +14 -12
  423. package/src/memory/v2/edge-index.ts +191 -0
  424. package/src/memory/v2/injection.ts +182 -58
  425. package/src/memory/v2/migration.ts +57 -64
  426. package/src/memory/v2/now-text.ts +2 -3
  427. package/src/memory/v2/page-store.ts +168 -31
  428. package/src/memory/v2/prompts/consolidation.ts +118 -42
  429. package/src/memory/v2/prompts/sweep.ts +3 -3
  430. package/src/memory/v2/skill-store.ts +55 -7
  431. package/src/memory/v2/static-context.ts +62 -0
  432. package/src/memory/v2/types.ts +10 -20
  433. package/src/memory/validation.ts +0 -11
  434. package/src/messaging/draft-store.ts +0 -6
  435. package/src/messaging/provider-types.ts +8 -0
  436. package/src/messaging/provider.ts +7 -0
  437. package/src/messaging/providers/gmail/client.ts +1 -121
  438. package/src/messaging/providers/outlook/client.ts +0 -73
  439. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +226 -0
  440. package/src/messaging/providers/slack/adapter.ts +122 -21
  441. package/src/messaging/providers/slack/backfill.test.ts +95 -6
  442. package/src/messaging/providers/slack/backfill.ts +89 -11
  443. package/src/messaging/providers/slack/client.ts +10 -124
  444. package/src/messaging/providers/slack/message-metadata.ts +12 -2
  445. package/src/messaging/providers/slack/render-transcript.test.ts +56 -0
  446. package/src/messaging/providers/slack/render-transcript.ts +126 -25
  447. package/src/messaging/providers/slack/types.ts +1 -0
  448. package/src/oauth/connection-resolver.test.ts +8 -0
  449. package/src/oauth/connection-resolver.ts +8 -16
  450. package/src/oauth/credential-token-resolver.ts +97 -0
  451. package/src/oauth/manual-token-connection.ts +30 -34
  452. package/src/oauth/oauth-store.ts +6 -4
  453. package/src/outbound-proxy/certs.ts +0 -7
  454. package/src/outbound-proxy/config.ts +0 -74
  455. package/src/outbound-proxy/health.ts +0 -44
  456. package/src/outbound-proxy/index.ts +0 -22
  457. package/src/permissions/approval-provenance.test.ts +184 -0
  458. package/src/permissions/approval-provenance.ts +70 -0
  459. package/src/permissions/checker.ts +4 -1
  460. package/src/permissions/gateway-threshold-reader.ts +4 -1
  461. package/src/permissions/prompter.ts +9 -2
  462. package/src/permissions/secret-prompter.ts +21 -48
  463. package/src/permissions/types.ts +33 -0
  464. package/src/permissions/workspace-policy.ts +0 -5
  465. package/src/platform/sync-identity.ts +0 -8
  466. package/src/plugins/defaults/injectors.ts +69 -2
  467. package/src/plugins/defaults/overflow-reduce.ts +3 -2
  468. package/src/plugins/types.ts +8 -0
  469. package/src/prompts/system-prompt.ts +34 -70
  470. package/src/prompts/templates/BOOTSTRAP.md +52 -6
  471. package/src/prompts/update-bulletin-job.ts +2 -0
  472. package/src/providers/__tests__/retry-callsite.test.ts +138 -1
  473. package/src/providers/anthropic/client.ts +72 -33
  474. package/src/providers/call-site-routing.ts +42 -3
  475. package/src/providers/gemini/client.ts +18 -2
  476. package/src/providers/managed-proxy/context.ts +0 -5
  477. package/src/providers/model-catalog.ts +105 -19
  478. package/src/providers/openai/chat-completions-provider.ts +6 -0
  479. package/src/providers/openai/responses-provider.ts +7 -1
  480. package/src/providers/provider-send-message.ts +45 -2
  481. package/src/providers/ratelimit.ts +7 -2
  482. package/src/providers/registry.ts +14 -9
  483. package/src/providers/retry.ts +96 -8
  484. package/src/providers/types.ts +13 -0
  485. package/src/providers/usage-tracking.ts +96 -0
  486. package/src/runtime/AGENTS.md +10 -6
  487. package/src/runtime/__tests__/agent-wake.test.ts +89 -0
  488. package/src/runtime/agent-wake.ts +39 -2
  489. package/src/runtime/assistant-event-hub.ts +541 -45
  490. package/src/runtime/assistant-event.ts +1 -6
  491. package/src/runtime/auth/context.ts +0 -9
  492. package/src/runtime/auth/middleware.ts +1 -1
  493. package/src/runtime/auth/route-policy.ts +11 -9
  494. package/src/runtime/auth/token-service.ts +0 -11
  495. package/src/runtime/channel-approvals.ts +6 -2
  496. package/src/runtime/channel-verification-service.ts +3 -5
  497. package/src/runtime/http-errors.ts +0 -34
  498. package/src/runtime/http-router.ts +6 -3
  499. package/src/runtime/http-server.ts +22 -82
  500. package/src/runtime/http-types.ts +5 -0
  501. package/src/runtime/interactive-ui.ts +0 -1
  502. package/src/runtime/middleware/auth.ts +0 -20
  503. package/src/runtime/migrations/__tests__/v1-test-helpers.ts +112 -0
  504. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +11 -4
  505. package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +253 -0
  506. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +19 -6
  507. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +71 -27
  508. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +41 -2
  509. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +143 -79
  510. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +143 -23
  511. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +2 -2
  512. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +371 -0
  513. package/src/runtime/migrations/migration-transport.ts +46 -13
  514. package/src/runtime/migrations/migration-wizard.ts +2 -2
  515. package/src/runtime/migrations/origin-mode.ts +40 -0
  516. package/src/runtime/migrations/vbundle-builder.ts +133 -79
  517. package/src/runtime/migrations/vbundle-import-analyzer.ts +9 -7
  518. package/src/runtime/migrations/vbundle-importer.ts +7 -7
  519. package/src/runtime/migrations/vbundle-metadata-merge.ts +1 -1
  520. package/src/runtime/migrations/vbundle-streaming-importer.ts +3 -3
  521. package/src/runtime/migrations/vbundle-streaming-validator.ts +48 -26
  522. package/src/runtime/migrations/vbundle-validator.ts +214 -41
  523. package/src/runtime/pending-interactions.ts +13 -4
  524. package/src/runtime/routes/__tests__/acp-routes.test.ts +0 -1
  525. package/src/runtime/routes/__tests__/backup-routes.test.ts +28 -19
  526. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +235 -0
  527. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +58 -0
  528. package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +54 -0
  529. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +19 -6
  530. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +7 -7
  531. package/src/runtime/routes/acp-routes.test.ts +0 -3
  532. package/src/runtime/routes/acp-routes.ts +3 -7
  533. package/src/runtime/routes/app-management-routes.ts +18 -9
  534. package/src/runtime/routes/approval-routes.ts +55 -14
  535. package/src/runtime/routes/avatar-routes.ts +3 -5
  536. package/src/runtime/routes/browser-routes.ts +1 -15
  537. package/src/runtime/routes/channel-guardian-routes.ts +1 -5
  538. package/src/runtime/routes/channel-readiness-routes.ts +3 -7
  539. package/src/runtime/routes/channel-route-shared.ts +2 -28
  540. package/src/runtime/routes/client-routes.ts +45 -12
  541. package/src/runtime/routes/consolidation-routes.ts +115 -0
  542. package/src/runtime/routes/conversation-list-routes.ts +12 -29
  543. package/src/runtime/routes/conversation-management-routes.ts +14 -51
  544. package/src/runtime/routes/conversation-query-routes.ts +120 -8
  545. package/src/runtime/routes/conversation-routes.ts +44 -528
  546. package/src/runtime/routes/conversation-starter-routes.ts +19 -40
  547. package/src/runtime/routes/documents-routes.ts +53 -18
  548. package/src/runtime/routes/events-routes.ts +59 -91
  549. package/src/runtime/routes/filing-routes.ts +18 -1
  550. package/src/runtime/routes/guardian-action-routes.ts +4 -9
  551. package/src/runtime/routes/host-bash-routes.ts +3 -2
  552. package/src/runtime/routes/host-browser-routes.ts +9 -33
  553. package/src/runtime/routes/host-cu-routes.ts +6 -1
  554. package/src/runtime/routes/host-file-routes.ts +3 -2
  555. package/src/runtime/routes/host-transfer-routes.ts +11 -15
  556. package/src/runtime/routes/identity-routes.ts +78 -6
  557. package/src/runtime/routes/inbound-message-handler.ts +580 -137
  558. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -88
  559. package/src/runtime/routes/inbound-stages/background-dispatch.ts +3 -0
  560. package/src/runtime/routes/index.ts +4 -0
  561. package/src/runtime/routes/integrations/slack/channel.ts +0 -24
  562. package/src/runtime/routes/llm-call-sites-routes.ts +22 -0
  563. package/src/runtime/routes/memory-v2-routes.ts +10 -15
  564. package/src/runtime/routes/migration-routes.ts +188 -31
  565. package/src/runtime/routes/playground/guard.ts +1 -1
  566. package/src/runtime/routes/playground/index.ts +0 -2
  567. package/src/runtime/routes/recording-routes.ts +4 -24
  568. package/src/runtime/routes/rename-conversation-routes.ts +2 -6
  569. package/src/runtime/routes/schedule-routes.ts +3 -6
  570. package/src/runtime/routes/secret-routes.ts +87 -18
  571. package/src/runtime/routes/settings-routes.ts +29 -28
  572. package/src/runtime/routes/skills-routes.ts +12 -31
  573. package/src/runtime/routes/suggest-trust-rule-routes.ts +32 -1
  574. package/src/runtime/routes/task-routes.ts +6 -6
  575. package/src/runtime/routes/trust-rules-routes.ts +3 -94
  576. package/src/runtime/routes/types.ts +4 -4
  577. package/src/runtime/routes/upgrade-broadcast-routes.ts +3 -10
  578. package/src/runtime/routes/usage-routes.ts +87 -10
  579. package/src/runtime/routes/user-routes.ts +17 -31
  580. package/src/runtime/routes/work-items-routes.ts +1 -4
  581. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -2
  582. package/src/runtime/services/analyze-conversation.ts +7 -17
  583. package/src/runtime/services/conversation-serializer.ts +2 -4
  584. package/src/runtime/verification-outbound-actions.ts +1 -1
  585. package/src/runtime/verification-rate-limiter.ts +1 -1
  586. package/src/schedule/schedule-store.ts +0 -16
  587. package/src/security/secret-scanner.ts +14 -547
  588. package/src/security/secure-keys.ts +31 -11
  589. package/src/security/token-manager.ts +7 -3
  590. package/src/signals/cancel.ts +16 -25
  591. package/src/signals/conversation-undo.ts +2 -27
  592. package/src/signals/emit-event.ts +1 -2
  593. package/src/signals/user-message.ts +108 -22
  594. package/src/skills/catalog-install.ts +1 -0
  595. package/src/skills/clawhub.ts +2 -2
  596. package/src/skills/inline-command-runner.ts +1 -7
  597. package/src/subagent/manager.ts +67 -84
  598. package/src/tasks/task-store.ts +1 -28
  599. package/src/telemetry/types.ts +6 -0
  600. package/src/telemetry/usage-telemetry-reporter.test.ts +38 -15
  601. package/src/telemetry/usage-telemetry-reporter.ts +3 -5
  602. package/src/tools/acp/spawn.test.ts +1 -2
  603. package/src/tools/acp/steer.test.ts +1 -2
  604. package/src/tools/browser/__tests__/browser-status.test.ts +44 -127
  605. package/src/tools/browser/browser-execution.ts +31 -147
  606. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +92 -68
  607. package/src/tools/browser/cdp-client/factory.ts +48 -76
  608. package/src/tools/browser/cdp-client/index.ts +1 -14
  609. package/src/tools/executor.ts +44 -31
  610. package/src/tools/host-filesystem/edit.ts +3 -2
  611. package/src/tools/host-filesystem/read.ts +3 -2
  612. package/src/tools/host-filesystem/transfer.test.ts +45 -42
  613. package/src/tools/host-filesystem/transfer.ts +4 -3
  614. package/src/tools/host-filesystem/write.ts +3 -2
  615. package/src/tools/host-terminal/host-shell.ts +4 -3
  616. package/src/tools/network/script-proxy/index.ts +1 -10
  617. package/src/tools/permission-checker.ts +66 -1
  618. package/src/tools/skills/sandbox-runner.ts +1 -6
  619. package/src/tools/skills/skill-tool-factory.ts +32 -0
  620. package/src/tools/terminal/safe-env.ts +1 -0
  621. package/src/tools/terminal/shell.ts +2 -78
  622. package/src/tools/types.ts +12 -39
  623. package/src/tts/__tests__/provider-catalog.test.ts +2 -2
  624. package/src/tts/provider-catalog.ts +1 -1
  625. package/src/usage/actors.ts +2 -1
  626. package/src/usage/attribution.ts +185 -0
  627. package/src/usage/pricing.ts +166 -0
  628. package/src/usage/types.ts +14 -0
  629. package/src/util/json.ts +13 -0
  630. package/src/util/logger.ts +3 -3
  631. package/src/util/pricing.ts +50 -3
  632. package/src/work-items/work-item-runner.ts +15 -42
  633. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +4 -3
  634. package/src/workspace/migrations/052-seed-default-inference-profiles.ts +3 -3
  635. package/src/workspace/migrations/060-memory-v2-init.ts +2 -18
  636. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +59 -0
  637. package/src/workspace/migrations/062-drop-memory-v2-edges-json.ts +27 -0
  638. package/src/workspace/migrations/063-release-notes-dynamic-model-context.ts +70 -0
  639. package/src/workspace/migrations/064-unwind-main-agent-opus-seed.ts +64 -0
  640. package/src/workspace/migrations/registry.ts +8 -0
  641. package/src/workspace/provider-commit-message-generator.ts +3 -3
  642. package/src/__tests__/sandbox-diagnostics.test.ts +0 -138
  643. package/src/__tests__/sandbox-host-parity.test.ts +0 -1024
  644. package/src/__tests__/secret-detection-handler.test.ts +0 -67
  645. package/src/__tests__/secret-scanner-executor.test.ts +0 -450
  646. package/src/__tests__/tcc-sandbox-deny.test.ts +0 -198
  647. package/src/__tests__/terminal-sandbox.test.ts +0 -374
  648. package/src/__tests__/tool-notification-listener.test.ts +0 -65
  649. package/src/context/__tests__/microcompact.test.ts +0 -805
  650. package/src/context/microcompact.ts +0 -443
  651. package/src/daemon/handlers/slack-channel-oauth-install.ts +0 -197
  652. package/src/events/tool-notification-listener.ts +0 -17
  653. package/src/ipc/routes/__tests__/memory-v2-validate.test.ts +0 -219
  654. package/src/memory/v2/__tests__/edges.test.ts +0 -435
  655. package/src/memory/v2/edges.ts +0 -217
  656. package/src/prompts/__tests__/system-prompt-memory-v2.test.ts +0 -197
  657. package/src/runtime/__tests__/chrome-extension-registry.test.ts +0 -518
  658. package/src/runtime/__tests__/client-registry.test.ts +0 -271
  659. package/src/runtime/chrome-extension-registry.ts +0 -368
  660. package/src/runtime/client-registry.ts +0 -254
  661. package/src/runtime/routes/inbound-stages/verification-intercept.ts +0 -329
  662. package/src/tools/secret-detection-handler.ts +0 -269
  663. package/src/tools/terminal/backends/native.ts +0 -327
  664. package/src/tools/terminal/backends/types.ts +0 -37
  665. package/src/tools/terminal/sandbox-diagnostics.ts +0 -87
  666. package/src/tools/terminal/sandbox.ts +0 -40
@@ -7,8 +7,9 @@
7
7
  * - `computeOwnActivation`: applies `A_o = d·prev + c_user·simU + c_a·simA +
8
8
  * c_now·simN`; clamps to [0,1]; orphan in candidates returns 0 when no
9
9
  * sim hits.
10
- * - `spreadActivation`: orphan yields A == A_o; symmetric two-node ring is
11
- * symmetric; hops=2 reaches second-degree but not third; bounded in [0,1].
10
+ * - `spreadActivation`: orphan yields A == A_o; spread walks incoming edges
11
+ * only (A→B boosts B but not A); hops=2 reaches second-degree predecessors
12
+ * but not third; bounded in [0,1].
12
13
  * - `selectInjections`: top-K rank, deterministic tie-break, delta against
13
14
  * `everInjected`.
14
15
  *
@@ -139,7 +140,8 @@ mock.module("@qdrant/js-client-rest", () => ({
139
140
 
140
141
  // Static `import type` is fine — types erase, so they don't run module-init
141
142
  // code that would race the mocks above.
142
- import type { ActivationState, EdgesIndex } from "../types.js";
143
+ import type { EdgeIndex } from "../edge-index.js";
144
+ import type { ActivationState } from "../types.js";
143
145
 
144
146
  const {
145
147
  computeOwnActivation,
@@ -241,7 +243,9 @@ describe("selectCandidates", () => {
241
243
  nowText: "",
242
244
  config: makeConfig(),
243
245
  });
244
- expect(out.size).toBe(0);
246
+ expect(out.candidates.size).toBe(0);
247
+ expect(out.fromPrior.size).toBe(0);
248
+ expect(out.fromAnn.size).toBe(0);
245
249
  // No turn text → no embedding call, no Qdrant call.
246
250
  expect(state.embedCalls).toHaveLength(0);
247
251
  expect(state.queryCalls).toHaveLength(0);
@@ -266,7 +270,9 @@ describe("selectCandidates", () => {
266
270
  nowText: "",
267
271
  config: makeConfig({ epsilon: 0.01 }),
268
272
  });
269
- expect(out).toEqual(new Set(["alice-vscode", "carol-jazz"]));
273
+ expect(out.candidates).toEqual(new Set(["alice-vscode", "carol-jazz"]));
274
+ expect(out.fromPrior).toEqual(new Set(["alice-vscode", "carol-jazz"]));
275
+ expect(out.fromAnn).toEqual(new Set());
270
276
  });
271
277
 
272
278
  test("unions ANN hits with prior-state survivors", async () => {
@@ -289,7 +295,51 @@ describe("selectCandidates", () => {
289
295
  nowText: "",
290
296
  config: makeConfig(),
291
297
  });
292
- expect(out).toEqual(new Set(["alice-vscode", "delta-recipe"]));
298
+ expect(out.candidates).toEqual(new Set(["alice-vscode", "delta-recipe"]));
299
+ expect(out.fromPrior).toEqual(new Set(["alice-vscode"]));
300
+ expect(out.fromAnn).toEqual(new Set(["alice-vscode", "delta-recipe"]));
301
+ });
302
+
303
+ test("tags overlap: a slug in both sources lands in fromPrior ∩ fromAnn", async () => {
304
+ const priorState: ActivationState = {
305
+ messageId: "msg-1",
306
+ state: {
307
+ "alice-vscode": 0.5, // in prior AND in ANN
308
+ "carol-jazz": 0.3, // prior only
309
+ },
310
+ everInjected: [],
311
+ currentTurn: 1,
312
+ updatedAt: 1,
313
+ };
314
+ stageHybridResponse([
315
+ { slug: "alice-vscode", denseScore: 0.7, sparseScore: 1 }, // overlap
316
+ { slug: "delta-recipe", denseScore: 0.4, sparseScore: 1 }, // ANN only
317
+ ]);
318
+
319
+ const out = await selectCandidates({
320
+ priorState,
321
+ userText: "hello",
322
+ assistantText: "",
323
+ nowText: "",
324
+ config: makeConfig(),
325
+ });
326
+
327
+ // Overlap: alice-vscode appears in both source sets.
328
+ const overlap = new Set(
329
+ [...out.fromPrior].filter((slug) => out.fromAnn.has(slug)),
330
+ );
331
+ expect(overlap).toEqual(new Set(["alice-vscode"]));
332
+
333
+ // candidates = fromPrior ∪ fromAnn.
334
+ const union = new Set<string>([...out.fromPrior, ...out.fromAnn]);
335
+ expect(out.candidates).toEqual(union);
336
+ expect(out.candidates).toEqual(
337
+ new Set(["alice-vscode", "carol-jazz", "delta-recipe"]),
338
+ );
339
+
340
+ // Source-set membership matches each slug's actual provenance.
341
+ expect(out.fromPrior).toEqual(new Set(["alice-vscode", "carol-jazz"]));
342
+ expect(out.fromAnn).toEqual(new Set(["alice-vscode", "delta-recipe"]));
293
343
  });
294
344
 
295
345
  test("ANN top-K limit equals 50 and runs without slug restriction", async () => {
@@ -324,7 +374,8 @@ describe("computeOwnActivation", () => {
324
374
  nowText: "now",
325
375
  config: makeConfig(),
326
376
  });
327
- expect(out.size).toBe(0);
377
+ expect(out.activation.size).toBe(0);
378
+ expect(out.breakdown.size).toBe(0);
328
379
  expect(state.embedCalls).toHaveLength(0);
329
380
  expect(state.queryCalls).toHaveLength(0);
330
381
  });
@@ -357,7 +408,7 @@ describe("computeOwnActivation", () => {
357
408
  }),
358
409
  });
359
410
  // Expected: 0.3*0.6 + 0.3*0.5 + 0.2*0.4 + 0.2*0.2 = 0.18+0.15+0.08+0.04 = 0.45
360
- expect(out.get("alice")).toBeCloseTo(0.45, 6);
411
+ expect(out.activation.get("alice")).toBeCloseTo(0.45, 6);
361
412
  });
362
413
 
363
414
  test("clamps over-1.0 results down to [0, 1]", async () => {
@@ -388,7 +439,7 @@ describe("computeOwnActivation", () => {
388
439
  c_now: 0.5,
389
440
  }),
390
441
  });
391
- expect(out.get("alice")).toBe(1);
442
+ expect(out.activation.get("alice")).toBe(1);
392
443
  });
393
444
 
394
445
  test("missing prior state defaults `prev` to 0", async () => {
@@ -410,7 +461,7 @@ describe("computeOwnActivation", () => {
410
461
  }),
411
462
  });
412
463
  // 0.3*0 + 0.3*1 + 0.2*0 + 0.2*0 = 0.3
413
- expect(out.get("fresh")).toBeCloseTo(0.3, 6);
464
+ expect(out.activation.get("fresh")).toBeCloseTo(0.3, 6);
414
465
  });
415
466
 
416
467
  test("candidate with no sim hits resolves to 0", async () => {
@@ -426,7 +477,60 @@ describe("computeOwnActivation", () => {
426
477
  nowText: "n",
427
478
  config: makeConfig(),
428
479
  });
429
- expect(out.get("ghost")).toBe(0);
480
+ expect(out.activation.get("ghost")).toBe(0);
481
+ });
482
+
483
+ test("breakdown captures `d * prev` and the raw sims for each candidate", async () => {
484
+ stageHybridResponse([{ slug: "alice", denseScore: 0.5 }]); // simU
485
+ stageHybridResponse([{ slug: "alice", denseScore: 0.4 }]); // simA
486
+ stageHybridResponse([{ slug: "alice", denseScore: 0.2 }]); // simN
487
+
488
+ const priorState: ActivationState = {
489
+ messageId: "msg-1",
490
+ state: { alice: 0.6 },
491
+ everInjected: [],
492
+ currentTurn: 1,
493
+ updatedAt: 1,
494
+ };
495
+ const d = 0.3;
496
+ const out = await computeOwnActivation({
497
+ candidates: new Set(["alice"]),
498
+ priorState,
499
+ userText: "u",
500
+ assistantText: "a",
501
+ nowText: "n",
502
+ config: makeConfig({
503
+ d,
504
+ c_user: 0.3,
505
+ c_assistant: 0.2,
506
+ c_now: 0.2,
507
+ }),
508
+ });
509
+ const breakdown = out.breakdown.get("alice");
510
+ expect(breakdown).toBeDefined();
511
+ // priorContribution is `d * prev`, not the weighted sim term.
512
+ expect(breakdown?.priorContribution).toBeCloseTo(d * 0.6, 6);
513
+ // Raw sims, captured before c_user / c_assistant / c_now weighting.
514
+ expect(breakdown?.simUser).toBeCloseTo(0.5, 6);
515
+ expect(breakdown?.simAssistant).toBeCloseTo(0.4, 6);
516
+ expect(breakdown?.simNow).toBeCloseTo(0.2, 6);
517
+ });
518
+
519
+ test("breakdown defaults priorContribution to 0 when priorState is null", async () => {
520
+ stageHybridResponse([{ slug: "fresh", denseScore: 0.5 }]);
521
+ stageHybridResponse([{ slug: "fresh", denseScore: 0.5 }]);
522
+ stageHybridResponse([{ slug: "fresh", denseScore: 0.5 }]);
523
+
524
+ const out = await computeOwnActivation({
525
+ candidates: new Set(["fresh"]),
526
+ priorState: null,
527
+ userText: "u",
528
+ assistantText: "a",
529
+ nowText: "n",
530
+ config: makeConfig({ d: 0.9 }),
531
+ });
532
+ // No prior state → prev=0 → priorContribution=0 regardless of `d`.
533
+ expect(out.breakdown.get("fresh")?.priorContribution).toBe(0);
430
534
  });
431
535
  });
432
536
 
@@ -434,143 +538,214 @@ describe("computeOwnActivation", () => {
434
538
  // spreadActivation
435
539
  // ---------------------------------------------------------------------------
436
540
 
541
+ /**
542
+ * Build a directed `EdgeIndex` from a flat list of `[from, to]` pairs. Each
543
+ * entry is interpreted as a directed edge `from → to`; self-loops are dropped.
544
+ */
545
+ function buildEdgeIndex(edges: Array<[string, string]>): EdgeIndex {
546
+ const outgoing = new Map<string, Set<string>>();
547
+ const incoming = new Map<string, Set<string>>();
548
+ for (const [from, to] of edges) {
549
+ if (from === to) continue;
550
+ let outSet = outgoing.get(from);
551
+ if (!outSet) {
552
+ outSet = new Set<string>();
553
+ outgoing.set(from, outSet);
554
+ }
555
+ outSet.add(to);
556
+ let inSet = incoming.get(to);
557
+ if (!inSet) {
558
+ inSet = new Set<string>();
559
+ incoming.set(to, inSet);
560
+ }
561
+ inSet.add(from);
562
+ }
563
+ return { outgoing, incoming };
564
+ }
565
+
437
566
  describe("spreadActivation", () => {
438
567
  test("orphan node yields A == A_o", () => {
439
- const edges: EdgesIndex = { version: 1, edges: [] };
568
+ const edges = buildEdgeIndex([]);
440
569
  const own = new Map([["alice", 0.7]]);
441
570
  const out = spreadActivation(own, edges, 0.5, 2);
442
- expect(out.get("alice")).toBeCloseTo(0.7, 6);
571
+ expect(out.final.get("alice")).toBeCloseTo(0.7, 6);
443
572
  });
444
573
 
445
- test("symmetric two-node ring yields symmetric activation", () => {
446
- const edges: EdgesIndex = {
447
- version: 1,
448
- edges: [["alice", "bob"]],
449
- };
574
+ test("directed edge boosts only the target, not the source", () => {
575
+ // Edge alice→bob: alice activation flows into bob; bob does NOT push back
576
+ // into alice. alice (a pure source under this graph) keeps its own value.
577
+ const edges = buildEdgeIndex([["alice", "bob"]]);
450
578
  const own = new Map([
451
579
  ["alice", 0.6],
452
- ["bob", 0.6],
580
+ ["bob", 0.0],
453
581
  ]);
454
582
  const out = spreadActivation(own, edges, 0.5, 2);
455
- // Both nodes have one neighbor, equal own-activation equal final A.
456
- expect(out.get("alice")).toBeCloseTo(out.get("bob") ?? 0, 6);
457
- // Numerator: 0.6 + 0.5*0.6 = 0.9. Denominator: 1 + 0.5*1 = 1.5. A = 0.6.
458
- expect(out.get("alice")).toBeCloseTo(0.6, 6);
583
+ // alice has no incoming edges → final == own.
584
+ expect(out.final.get("alice")).toBeCloseTo(0.6, 6);
585
+ // bob's incoming = {alice}: numerator = 0 + 0.5*0.6 = 0.3, denom = 1.5.
586
+ expect(out.final.get("bob")).toBeCloseTo(0.3 / 1.5, 6);
459
587
  });
460
588
 
461
- test("asymmetric two-node ring picks up neighbor activation", () => {
462
- const edges: EdgesIndex = {
463
- version: 1,
464
- edges: [["alice", "bob"]],
465
- };
589
+ test("two-cycle (A→B and B→A) lets activation flow both ways", () => {
590
+ // With both directions present, each node is the other's predecessor.
591
+ const edges = buildEdgeIndex([
592
+ ["alice", "bob"],
593
+ ["bob", "alice"],
594
+ ]);
466
595
  const own = new Map([
467
596
  ["alice", 0.0],
468
597
  ["bob", 0.8],
469
598
  ]);
470
599
  const out = spreadActivation(own, edges, 0.5, 2);
471
- // alice: numerator = 0 + 0.5*0.8 = 0.4. denominator = 1 + 0.5 = 1.5.
472
- // A = 0.2666...
473
- expect(out.get("alice")).toBeCloseTo(0.4 / 1.5, 6);
474
- // bob: numerator = 0.8 + 0.5*0 = 0.8. denominator = 1.5. A = 0.5333...
475
- expect(out.get("bob")).toBeCloseTo(0.8 / 1.5, 6);
476
- });
477
-
478
- test("hops=2 reaches second-degree neighbors but stops there", () => {
479
- // Path graph: alice -- bob -- carol -- delta
480
- // From alice's perspective: bob is 1-hop, carol is 2-hop, delta is 3-hop.
481
- const edges: EdgesIndex = {
482
- version: 1,
483
- edges: [
484
- ["alice", "bob"],
485
- ["bob", "carol"],
486
- ["carol", "delta"],
487
- ],
488
- };
600
+ // alice: incoming {bob}=0.8 → numerator = 0 + 0.5*0.8 = 0.4, denom = 1.5.
601
+ expect(out.final.get("alice")).toBeCloseTo(0.4 / 1.5, 6);
602
+ // bob: incoming {alice}=0.0 → numerator = 0.8 + 0 = 0.8, denom = 1.5.
603
+ expect(out.final.get("bob")).toBeCloseTo(0.8 / 1.5, 6);
604
+ });
605
+
606
+ test("pure source (high outgoing, zero incoming) collapses to final == own", () => {
607
+ // alice bob carol; alice has no incoming edges.
608
+ const edges = buildEdgeIndex([
609
+ ["alice", "bob"],
610
+ ["bob", "carol"],
611
+ ]);
489
612
  const own = new Map([
490
- ["alice", 0.0],
613
+ ["alice", 0.5],
491
614
  ["bob", 0.0],
492
- ["carol", 1.0], // 2 hops from alice
493
- ["delta", 1.0], // 3 hops from alice — must NOT contribute
615
+ ["carol", 0.0],
494
616
  ]);
495
617
  const out = spreadActivation(own, edges, 0.5, 2);
496
- // alice: 1-hop = {bob} (0), 2-hop = {carol} (1.0).
618
+ expect(out.final.get("alice")).toBeCloseTo(0.5, 6);
619
+ });
620
+
621
+ test("hops=2 reaches second-degree predecessors but stops there", () => {
622
+ // Directed path: alice → bob → carol → delta
623
+ // From delta's perspective: carol is 1-hop predecessor, bob is 2-hop,
624
+ // alice is 3-hop. Activation on bob (2-hop) reaches delta; activation on
625
+ // alice (3-hop) does NOT.
626
+ const edges = buildEdgeIndex([
627
+ ["alice", "bob"],
628
+ ["bob", "carol"],
629
+ ["carol", "delta"],
630
+ ]);
631
+ const own = new Map([
632
+ ["alice", 1.0], // 3-hop predecessor of delta — must NOT contribute
633
+ ["bob", 1.0], // 2-hop predecessor of delta
634
+ ["carol", 0.0],
635
+ ["delta", 0.0],
636
+ ]);
637
+ const out = spreadActivation(own, edges, 0.5, 2);
638
+ // delta: 1-hop {carol}=0, 2-hop {bob}=1.0.
497
639
  // numerator = 0 + 0.5*0 + 0.25*1.0 = 0.25
498
640
  // denominator = 1 + 0.5*1 + 0.25*1 = 1.75
499
641
  // A = 0.25 / 1.75 ≈ 0.142857
500
- expect(out.get("alice")).toBeCloseTo(0.25 / 1.75, 6);
642
+ expect(out.final.get("delta")).toBeCloseTo(0.25 / 1.75, 6);
501
643
  });
502
644
 
503
645
  test("output is bounded in [0, 1] for arbitrary inputs", () => {
504
- const edges: EdgesIndex = {
505
- version: 1,
506
- edges: [
507
- ["alice", "bob"],
508
- ["bob", "carol"],
509
- ["alice", "carol"],
510
- ],
511
- };
646
+ const edges = buildEdgeIndex([
647
+ ["alice", "bob"],
648
+ ["bob", "carol"],
649
+ ["alice", "carol"],
650
+ ]);
512
651
  const own = new Map([
513
652
  ["alice", 1.0],
514
653
  ["bob", 1.0],
515
654
  ["carol", 1.0],
516
655
  ]);
517
656
  const out = spreadActivation(own, edges, 0.99, 2);
518
- for (const [, value] of out) {
657
+ for (const [, value] of out.final) {
519
658
  expect(value).toBeGreaterThanOrEqual(0);
520
659
  expect(value).toBeLessThanOrEqual(1);
521
660
  }
522
661
  });
523
662
 
524
663
  test("hops=0 collapses to A == A_o", () => {
525
- const edges: EdgesIndex = {
526
- version: 1,
527
- edges: [["alice", "bob"]],
528
- };
664
+ const edges = buildEdgeIndex([["alice", "bob"]]);
529
665
  const own = new Map([
530
666
  ["alice", 0.4],
531
667
  ["bob", 0.9],
532
668
  ]);
533
669
  const out = spreadActivation(own, edges, 0.5, 0);
534
- expect(out.get("alice")).toBeCloseTo(0.4, 6);
535
- expect(out.get("bob")).toBeCloseTo(0.9, 6);
670
+ expect(out.final.get("alice")).toBeCloseTo(0.4, 6);
671
+ expect(out.final.get("bob")).toBeCloseTo(0.9, 6);
536
672
  });
537
673
 
538
674
  test("k=0 collapses to A == A_o", () => {
539
- const edges: EdgesIndex = {
540
- version: 1,
541
- edges: [["alice", "bob"]],
542
- };
675
+ const edges = buildEdgeIndex([["alice", "bob"]]);
543
676
  const own = new Map([
544
677
  ["alice", 0.4],
545
678
  ["bob", 0.9],
546
679
  ]);
547
680
  const out = spreadActivation(own, edges, 0, 5);
548
- expect(out.get("alice")).toBeCloseTo(0.4, 6);
549
- expect(out.get("bob")).toBeCloseTo(0.9, 6);
681
+ expect(out.final.get("alice")).toBeCloseTo(0.4, 6);
682
+ expect(out.final.get("bob")).toBeCloseTo(0.9, 6);
550
683
  });
551
684
 
552
- test("missing neighbor activation contributes 0 to the numerator", () => {
553
- // alice and bob are connected, but bob is not in `ownActivation` — so
554
- // bob's contribution is 0, while the denominator still counts the
555
- // structural neighbor.
556
- const edges: EdgesIndex = {
557
- version: 1,
558
- edges: [["alice", "bob"]],
559
- };
560
- const own = new Map([["alice", 0.6]]);
685
+ test("missing predecessor activation contributes 0 to the numerator", () => {
686
+ // Edge alice→bob: bob has predecessor alice. alice is not in
687
+ // `ownActivation`, so it contributes 0 to the numerator while the
688
+ // denominator still counts the structural predecessor.
689
+ const edges = buildEdgeIndex([["alice", "bob"]]);
690
+ const own = new Map([["bob", 0.6]]);
561
691
  const out = spreadActivation(own, edges, 0.5, 2);
562
692
  // numerator = 0.6 + 0.5*0 = 0.6. denominator = 1 + 0.5*1 = 1.5.
563
- expect(out.get("alice")).toBeCloseTo(0.4, 6);
693
+ expect(out.final.get("bob")).toBeCloseTo(0.4, 6);
564
694
  });
565
695
 
566
696
  test("empty own-activation map returns empty result", () => {
567
697
  const out = spreadActivation(
568
698
  new Map(),
569
- { version: 1, edges: [["a", "b"]] },
699
+ buildEdgeIndex([["a", "b"]]),
570
700
  0.5,
571
701
  2,
572
702
  );
573
- expect(out.size).toBe(0);
703
+ expect(out.final.size).toBe(0);
704
+ expect(out.contribution.size).toBe(0);
705
+ });
706
+
707
+ test("contribution equals final - own for each slug", () => {
708
+ // Two-cycle: A→B and B→A so both nodes have predecessors and the spread
709
+ // moves each off its own value in opposite directions.
710
+ const edges = buildEdgeIndex([
711
+ ["alice", "bob"],
712
+ ["bob", "alice"],
713
+ ]);
714
+ const own = new Map([
715
+ ["alice", 0.0],
716
+ ["bob", 0.8],
717
+ ]);
718
+ const out = spreadActivation(own, edges, 0.5, 2);
719
+ for (const [slug, finalValue] of out.final) {
720
+ const ownValue = own.get(slug) ?? 0;
721
+ expect(out.contribution.get(slug)).toBeCloseTo(finalValue - ownValue, 6);
722
+ }
723
+ // alice gained spread (predecessor bob=0.8); bob lost some (predecessor
724
+ // alice=0 dilutes its own 0.8).
725
+ expect(out.contribution.get("alice")).toBeGreaterThan(0);
726
+ expect(out.contribution.get("bob")).toBeLessThan(0);
727
+ });
728
+
729
+ test("contribution is 0 for every slug when hops == 0", () => {
730
+ const edges = buildEdgeIndex([["alice", "bob"]]);
731
+ const own = new Map([
732
+ ["alice", 0.4],
733
+ ["bob", 0.9],
734
+ ]);
735
+ const out = spreadActivation(own, edges, 0.5, 0);
736
+ expect(out.contribution.get("alice")).toBe(0);
737
+ expect(out.contribution.get("bob")).toBe(0);
738
+ });
739
+
740
+ test("contribution is 0 for every slug when k == 0", () => {
741
+ const edges = buildEdgeIndex([["alice", "bob"]]);
742
+ const own = new Map([
743
+ ["alice", 0.4],
744
+ ["bob", 0.9],
745
+ ]);
746
+ const out = spreadActivation(own, edges, 0, 5);
747
+ expect(out.contribution.get("alice")).toBe(0);
748
+ expect(out.contribution.get("bob")).toBe(0);
574
749
  });
575
750
  });
576
751
 
@@ -766,7 +941,8 @@ describe("computeSkillActivation", () => {
766
941
  nowText: "n",
767
942
  config: makeConfig(),
768
943
  });
769
- expect(out.size).toBe(0);
944
+ expect(out.activation.size).toBe(0);
945
+ expect(out.breakdown.size).toBe(0);
770
946
  expect(state.embedCalls).toHaveLength(0);
771
947
  expect(state.queryCalls).toHaveLength(0);
772
948
  });
@@ -790,7 +966,7 @@ describe("computeSkillActivation", () => {
790
966
  }),
791
967
  });
792
968
  // No `d · prev` term: 0.3*0.5 + 0.2*0.4 + 0.2*0.2 = 0.15 + 0.08 + 0.04 = 0.27
793
- expect(out.get("example-skill-a")).toBeCloseTo(0.27, 6);
969
+ expect(out.activation.get("example-skill-a")).toBeCloseTo(0.27, 6);
794
970
  });
795
971
 
796
972
  test("output excludes any decay term — d coefficient is unused", async () => {
@@ -824,8 +1000,8 @@ describe("computeSkillActivation", () => {
824
1000
  });
825
1001
 
826
1002
  // Both equal `0.3*0.4 + 0.2*0.4 + 0.2*0.4 = 0.28` — d is ignored.
827
- expect(withHighD.get("alpha")).toBeCloseTo(0.28, 6);
828
- expect(withZeroD.get("alpha")).toBeCloseTo(0.28, 6);
1003
+ expect(withHighD.activation.get("alpha")).toBeCloseTo(0.28, 6);
1004
+ expect(withZeroD.activation.get("alpha")).toBeCloseTo(0.28, 6);
829
1005
  });
830
1006
 
831
1007
  test("clamps over-1.0 results down to [0, 1]", async () => {
@@ -846,7 +1022,7 @@ describe("computeSkillActivation", () => {
846
1022
  c_now: 0.5,
847
1023
  }),
848
1024
  });
849
- expect(out.get("loud-skill")).toBe(1);
1025
+ expect(out.activation.get("loud-skill")).toBe(1);
850
1026
  });
851
1027
 
852
1028
  test("candidate with no sim hits resolves to 0", async () => {
@@ -861,7 +1037,30 @@ describe("computeSkillActivation", () => {
861
1037
  nowText: "n",
862
1038
  config: makeConfig(),
863
1039
  });
864
- expect(out.get("ghost-skill")).toBe(0);
1040
+ expect(out.activation.get("ghost-skill")).toBe(0);
1041
+ });
1042
+
1043
+ test("breakdown captures the raw sims for each candidate", async () => {
1044
+ stageSkillHybridResponse([{ id: "example-skill-a", denseScore: 0.5 }]); // simU
1045
+ stageSkillHybridResponse([{ id: "example-skill-a", denseScore: 0.4 }]); // simA
1046
+ stageSkillHybridResponse([{ id: "example-skill-a", denseScore: 0.2 }]); // simN
1047
+
1048
+ const out = await computeSkillActivation({
1049
+ candidates: new Set(["example-skill-a"]),
1050
+ userText: "u",
1051
+ assistantText: "a",
1052
+ nowText: "n",
1053
+ config: makeConfig({
1054
+ c_user: 0.3,
1055
+ c_assistant: 0.2,
1056
+ c_now: 0.2,
1057
+ }),
1058
+ });
1059
+ const breakdown = out.breakdown.get("example-skill-a");
1060
+ expect(breakdown).toBeDefined();
1061
+ expect(breakdown?.simUser).toBeCloseTo(0.5, 6);
1062
+ expect(breakdown?.simAssistant).toBeCloseTo(0.4, 6);
1063
+ expect(breakdown?.simNow).toBeCloseTo(0.2, 6);
865
1064
  });
866
1065
 
867
1066
  test("uses the dedicated skills collection and never queries concept pages", async () => {