@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
@@ -0,0 +1,225 @@
1
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
5
+
6
+ import { unwindMainAgentOpusSeedMigration } from "../workspace/migrations/064-unwind-main-agent-opus-seed.js";
7
+
8
+ let workspaceDir: string;
9
+
10
+ function freshWorkspace(): void {
11
+ workspaceDir = join(
12
+ tmpdir(),
13
+ `vellum-migration-064-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
14
+ );
15
+ mkdirSync(workspaceDir, { recursive: true });
16
+ }
17
+
18
+ function writeConfig(data: Record<string, unknown>): void {
19
+ writeFileSync(
20
+ join(workspaceDir, "config.json"),
21
+ JSON.stringify(data, null, 2) + "\n",
22
+ );
23
+ }
24
+
25
+ function readConfig(): Record<string, unknown> {
26
+ return JSON.parse(readFileSync(join(workspaceDir, "config.json"), "utf-8"));
27
+ }
28
+
29
+ beforeEach(() => {
30
+ freshWorkspace();
31
+ });
32
+
33
+ afterEach(() => {
34
+ rmSync(workspaceDir, { recursive: true, force: true });
35
+ });
36
+
37
+ describe("064-unwind-main-agent-opus-seed migration", () => {
38
+ test("has correct migration id", () => {
39
+ expect(unwindMainAgentOpusSeedMigration.id).toBe(
40
+ "064-unwind-main-agent-opus-seed",
41
+ );
42
+ });
43
+
44
+ test("removes exact seeded mainAgent override and sets balanced when activeProfile is missing", () => {
45
+ writeConfig({
46
+ llm: {
47
+ callSites: {
48
+ mainAgent: { model: "claude-opus-4-7", maxTokens: 32000 },
49
+ },
50
+ },
51
+ });
52
+
53
+ unwindMainAgentOpusSeedMigration.run(workspaceDir);
54
+
55
+ const config = readConfig() as {
56
+ llm: {
57
+ activeProfile: string;
58
+ callSites: Record<string, unknown>;
59
+ };
60
+ };
61
+ expect(config.llm.activeProfile).toBe("balanced");
62
+ expect(config.llm.callSites.mainAgent).toBeUndefined();
63
+ });
64
+
65
+ test("preserves balanced activeProfile (set by migration 052) while removing seed", () => {
66
+ // migration 052 sets activeProfile="balanced" before 064 runs; 064 must
67
+ // not override it — balanced is the intentional default, not a signal
68
+ // that the user wants the Opus quality tier.
69
+ writeConfig({
70
+ llm: {
71
+ activeProfile: "balanced",
72
+ callSites: {
73
+ mainAgent: { model: "claude-opus-4-7", maxTokens: 32000 },
74
+ },
75
+ },
76
+ });
77
+
78
+ unwindMainAgentOpusSeedMigration.run(workspaceDir);
79
+
80
+ const config = readConfig() as {
81
+ llm: { activeProfile: string; callSites: Record<string, unknown> };
82
+ };
83
+ expect(config.llm.activeProfile).toBe("balanced");
84
+ expect(config.llm.callSites.mainAgent).toBeUndefined();
85
+ });
86
+
87
+ test("preserves custom activeProfile while removing the stale seed", () => {
88
+ writeConfig({
89
+ llm: {
90
+ activeProfile: "gpt-5-5",
91
+ callSites: {
92
+ mainAgent: { model: "claude-opus-4-7", maxTokens: 32000 },
93
+ },
94
+ },
95
+ });
96
+
97
+ unwindMainAgentOpusSeedMigration.run(workspaceDir);
98
+
99
+ const config = readConfig() as {
100
+ llm: { activeProfile: string; callSites: Record<string, unknown> };
101
+ };
102
+ expect(config.llm.activeProfile).toBe("gpt-5-5");
103
+ expect(config.llm.callSites.mainAgent).toBeUndefined();
104
+ });
105
+
106
+ test("removes only seeded model and maxTokens when user fields are present, preserves balanced", () => {
107
+ writeConfig({
108
+ llm: {
109
+ activeProfile: "balanced",
110
+ callSites: {
111
+ mainAgent: {
112
+ model: "claude-opus-4-7",
113
+ maxTokens: 32000,
114
+ effort: "low",
115
+ thinking: { enabled: false },
116
+ },
117
+ },
118
+ },
119
+ });
120
+
121
+ unwindMainAgentOpusSeedMigration.run(workspaceDir);
122
+
123
+ const config = readConfig() as {
124
+ llm: {
125
+ activeProfile: string;
126
+ callSites: Record<string, Record<string, unknown>>;
127
+ };
128
+ };
129
+ expect(config.llm.activeProfile).toBe("balanced");
130
+ expect(config.llm.callSites.mainAgent).toEqual({
131
+ effort: "low",
132
+ thinking: { enabled: false },
133
+ });
134
+ });
135
+
136
+ test("does not mutate mainAgent entries with explicit provider", () => {
137
+ const mainAgent = {
138
+ provider: "anthropic",
139
+ model: "claude-opus-4-7",
140
+ maxTokens: 32000,
141
+ };
142
+ writeConfig({
143
+ llm: {
144
+ activeProfile: "balanced",
145
+ callSites: { mainAgent },
146
+ },
147
+ });
148
+
149
+ unwindMainAgentOpusSeedMigration.run(workspaceDir);
150
+
151
+ const config = readConfig() as {
152
+ llm: { activeProfile: string; callSites: Record<string, unknown> };
153
+ };
154
+ expect(config.llm.activeProfile).toBe("balanced");
155
+ expect(config.llm.callSites.mainAgent).toEqual(mainAgent);
156
+ });
157
+
158
+ test("does not mutate mainAgent entries with explicit profile", () => {
159
+ const mainAgent = {
160
+ profile: "quality-optimized",
161
+ model: "claude-opus-4-7",
162
+ maxTokens: 32000,
163
+ };
164
+ writeConfig({
165
+ llm: {
166
+ activeProfile: "balanced",
167
+ callSites: { mainAgent },
168
+ },
169
+ });
170
+
171
+ unwindMainAgentOpusSeedMigration.run(workspaceDir);
172
+
173
+ const config = readConfig() as {
174
+ llm: { activeProfile: string; callSites: Record<string, unknown> };
175
+ };
176
+ expect(config.llm.activeProfile).toBe("balanced");
177
+ expect(config.llm.callSites.mainAgent).toEqual(mainAgent);
178
+ });
179
+
180
+ test("does not mutate explicit user pins with different maxTokens", () => {
181
+ const mainAgent = {
182
+ model: "claude-opus-4-7",
183
+ maxTokens: 64000,
184
+ };
185
+ writeConfig({
186
+ llm: {
187
+ activeProfile: "balanced",
188
+ callSites: { mainAgent },
189
+ },
190
+ });
191
+
192
+ unwindMainAgentOpusSeedMigration.run(workspaceDir);
193
+
194
+ const config = readConfig() as {
195
+ llm: { activeProfile: string; callSites: Record<string, unknown> };
196
+ };
197
+ expect(config.llm.activeProfile).toBe("balanced");
198
+ expect(config.llm.callSites.mainAgent).toEqual(mainAgent);
199
+ });
200
+
201
+ test("does not mutate non-main-agent call sites", () => {
202
+ writeConfig({
203
+ llm: {
204
+ activeProfile: "balanced",
205
+ callSites: {
206
+ interactionClassifier: {
207
+ model: "claude-opus-4-7",
208
+ maxTokens: 32000,
209
+ },
210
+ },
211
+ },
212
+ });
213
+
214
+ unwindMainAgentOpusSeedMigration.run(workspaceDir);
215
+
216
+ const config = readConfig() as {
217
+ llm: { activeProfile: string; callSites: Record<string, unknown> };
218
+ };
219
+ expect(config.llm.activeProfile).toBe("balanced");
220
+ expect(config.llm.callSites.interactionClassifier).toEqual({
221
+ model: "claude-opus-4-7",
222
+ maxTokens: 32000,
223
+ });
224
+ });
225
+ });
@@ -3,9 +3,10 @@
3
3
  *
4
4
  * The migration seeds the `memory/` directory tree used by the v2 memory
5
5
  * subsystem: `memory/`, `memory/concepts/`, `memory/archive/`,
6
- * `memory/.v2-state/`, plus an empty `edges.json` and the four prose files
7
- * (`essentials.md`, `threads.md`, `recent.md`, `buffer.md`). It must be
8
- * idempotent and must not touch existing files.
6
+ * `memory/.v2-state/`, plus the four prose files (`essentials.md`,
7
+ * `threads.md`, `recent.md`, `buffer.md`). It must be idempotent and must
8
+ * not touch existing files. Outgoing edges live directly in concept-page
9
+ * frontmatter — there is no separate edges-index file to seed.
9
10
  */
10
11
 
11
12
  import {
@@ -90,11 +91,9 @@ describe("060-memory-v2-init migration", () => {
90
91
  expect(statSync(join(memoryDir, "archive")).isDirectory()).toBe(true);
91
92
  expect(statSync(join(memoryDir, ".v2-state")).isDirectory()).toBe(true);
92
93
 
93
- // edges.json is seeded with the canonical empty index.
94
- const edgesPath = join(memoryDir, "edges.json");
95
- expect(existsSync(edgesPath)).toBe(true);
96
- const edges = JSON.parse(readFileSync(edgesPath, "utf-8"));
97
- expect(edges).toEqual({ version: 1, edges: [] });
94
+ // No edges-index file is seeded outgoing edges live in concept-page
95
+ // frontmatter under the directed-edges model.
96
+ expect(existsSync(join(memoryDir, "edges.json"))).toBe(false);
98
97
 
99
98
  // Each prose file is created and empty.
100
99
  for (const filename of [
@@ -126,26 +125,6 @@ describe("060-memory-v2-init migration", () => {
126
125
  }
127
126
  });
128
127
 
129
- test("preserves an existing edges.json with prior content", () => {
130
- const memoryDir = join(workspaceDir, "memory");
131
- mkdirSync(memoryDir, { recursive: true });
132
-
133
- const customEdges = {
134
- version: 1,
135
- edges: [
136
- ["alice-preferences", "bob-handoff"],
137
- ["bob-handoff", "team-rituals"],
138
- ],
139
- };
140
- const edgesPath = join(memoryDir, "edges.json");
141
- writeFileSync(edgesPath, JSON.stringify(customEdges, null, 2), "utf-8");
142
-
143
- memoryV2InitMigration.run(workspaceDir);
144
-
145
- const reread = JSON.parse(readFileSync(edgesPath, "utf-8"));
146
- expect(reread).toEqual(customEdges);
147
- });
148
-
149
128
  test("preserves existing prose files with content", () => {
150
129
  const memoryDir = join(workspaceDir, "memory");
151
130
  mkdirSync(memoryDir, { recursive: true });
@@ -247,8 +226,7 @@ describe("060-memory-v2-init migration", () => {
247
226
  // .v2-state/ is gone.
248
227
  expect(existsSync(join(memoryDir, ".v2-state"))).toBe(false);
249
228
 
250
- // Prose files and edges.json remain.
251
- expect(existsSync(join(memoryDir, "edges.json"))).toBe(true);
229
+ // Prose files remain.
252
230
  expect(existsSync(join(memoryDir, "essentials.md"))).toBe(true);
253
231
  expect(existsSync(join(memoryDir, "threads.md"))).toBe(true);
254
232
  expect(existsSync(join(memoryDir, "recent.md"))).toBe(true);
package/src/acp/index.ts CHANGED
@@ -1,8 +1,6 @@
1
- export { AcpSessionManager } from "./session-manager.js";
2
1
  export type { AcpAgentConfig, AcpSessionState } from "./types.js";
3
2
 
4
3
  import { getConfig } from "../config/loader.js";
5
- import type { ServerMessage } from "../daemon/message-protocol.js";
6
4
  import { AcpSessionManager } from "./session-manager.js";
7
5
 
8
6
  /** Singleton AcpSessionManager instance shared across the daemon. */
@@ -29,16 +27,3 @@ export function disposeAcpSessionManager(): void {
29
27
  manager = null;
30
28
  }
31
29
  }
32
-
33
- /**
34
- * Broadcast callback set by DaemonServer constructor so ACP session events
35
- * reach all connected SSE clients. Same pattern as
36
- * `getSubagentManager().broadcastToAllClients` in server.ts.
37
- */
38
- export let broadcastToAllClients: ((msg: ServerMessage) => void) | null = null;
39
-
40
- export function setBroadcastToAllClients(
41
- fn: (msg: ServerMessage) => void,
42
- ): void {
43
- broadcastToAllClients = fn;
44
- }
@@ -7,6 +7,7 @@ import { randomUUID } from "node:crypto";
7
7
 
8
8
  import { inArray } from "drizzle-orm";
9
9
 
10
+ import { findConversation } from "../daemon/conversation-store.js";
10
11
  import type { ServerMessage } from "../daemon/message-protocol.js";
11
12
  import type { AcpSessionUpdate } from "../daemon/message-types/acp.js";
12
13
  import { getDb } from "../memory/db-connection.js";
@@ -55,19 +56,6 @@ export class AcpSessionManager {
55
56
  */
56
57
  private eventBuffers = new Map<string, BufferedAcpUpdate[]>();
57
58
 
58
- /**
59
- * Optional callback to inject a completion/failure message into the parent
60
- * conversation so the LLM sees the agent's output.
61
- * Wired by DaemonServer at startup.
62
- */
63
- onAcpSessionFinished:
64
- | ((
65
- parentConversationId: string,
66
- message: string,
67
- sendToClient: (msg: ServerMessage) => void,
68
- ) => Promise<void>)
69
- | null = null;
70
-
71
59
  constructor(private readonly maxConcurrent: number) {
72
60
  this.cleanupStaleRunningRows();
73
61
  }
@@ -446,28 +434,43 @@ export class AcpSessionManager {
446
434
  this.teardownSession(acpSessionId, current);
447
435
 
448
436
  // Notify parent session so the LLM sees the agent's output
449
- if (this.onAcpSessionFinished) {
450
- const agentLabel = current.state.agentId;
451
- const responseText = current.clientHandler.responseText;
452
- const sessionId = current.state.acpSessionId;
453
- // `claude --resume <id>` is Claude Code-specific (the
454
- // claude-agent-acp adapter binary). Other adapters resume
455
- // differently or not at all, so the hint is gated.
456
- const resumeHint =
457
- current.command === "claude-agent-acp"
458
- ? `\n\nTo resume: cd ${current.cwd} && claude --resume ${sessionId}`
459
- : "";
460
- const notifyMessage = `[ACP agent "${agentLabel}" completed]\n\n${responseText}${resumeHint}`;
461
- this.onAcpSessionFinished(
462
- current.parentConversationId,
437
+ const agentLabel = current.state.agentId;
438
+ const responseText = current.clientHandler.responseText;
439
+ const sessionId = current.state.acpSessionId;
440
+ const resumeHint =
441
+ current.command === "claude-agent-acp"
442
+ ? `\n\nTo resume: cd ${current.cwd} && claude --resume ${sessionId}`
443
+ : "";
444
+ const notifyMessage = `[ACP agent "${agentLabel}" completed]\n\n${responseText}${resumeHint}`;
445
+ const parentConversation = findConversation(
446
+ current.parentConversationId,
447
+ );
448
+ if (parentConversation) {
449
+ const enqueueResult = parentConversation.enqueueMessage(
463
450
  notifyMessage,
464
- current.sendToVellum,
465
- ).catch((notifyErr) => {
466
- log.error(
467
- { acpSessionId, notifyErr },
468
- "Failed to notify parent of ACP completion",
469
- );
470
- });
451
+ [],
452
+ );
453
+ if (!enqueueResult.queued && !enqueueResult.rejected) {
454
+ parentConversation
455
+ .persistUserMessage(notifyMessage, [])
456
+ .then((messageId) =>
457
+ parentConversation.runAgentLoop(notifyMessage, messageId),
458
+ )
459
+ .catch((err) => {
460
+ log.error(
461
+ {
462
+ parentConversationId: current.parentConversationId,
463
+ err,
464
+ },
465
+ "Failed to process ACP notification in parent",
466
+ );
467
+ });
468
+ }
469
+ } else {
470
+ log.warn(
471
+ { parentConversationId: current.parentConversationId },
472
+ "ACP agent finished but parent conversation not found",
473
+ );
471
474
  }
472
475
  }
473
476
  })
package/src/agent/loop.ts CHANGED
@@ -93,9 +93,13 @@ export type AgentEvent =
93
93
  contentBlocks?: ContentBlock[];
94
94
  riskLevel?: string;
95
95
  riskReason?: string;
96
+ matchedTrustRuleId?: string;
96
97
  isContainerized?: boolean;
97
98
  riskScopeOptions?: Array<{ pattern: string; label: string }>;
98
99
  riskDirectoryScopeOptions?: Array<{ scope: string; label: string }>;
100
+ approvalMode?: string;
101
+ approvalReason?: string;
102
+ riskThreshold?: string;
99
103
  }
100
104
  | { type: "tool_use_preview_start"; toolUseId: string; toolName: string }
101
105
  | {
@@ -275,9 +279,13 @@ export type LoopToolExecutor = (
275
279
  yieldToUser?: boolean;
276
280
  riskLevel?: string;
277
281
  riskReason?: string;
282
+ matchedTrustRuleId?: string;
278
283
  isContainerized?: boolean;
279
284
  riskScopeOptions?: Array<{ pattern: string; label: string }>;
280
285
  riskDirectoryScopeOptions?: Array<{ scope: string; label: string }>;
286
+ approvalMode?: string;
287
+ approvalReason?: string;
288
+ riskThreshold?: string;
281
289
  }>;
282
290
 
283
291
  export class AgentLoop {
@@ -364,6 +372,7 @@ export class AgentLoop {
364
372
  * default for the lifetime of an agent loop run.
365
373
  */
366
374
  overrideProfile?: string,
375
+ effectiveMaxInputTokens?: number,
367
376
  ): Promise<Message[]> {
368
377
  const history = [...messages];
369
378
  const initialHistoryLength = messages.length;
@@ -462,6 +471,7 @@ export class AgentLoop {
462
471
  // analyze, etc.) pass their own `callSite`.
463
472
  if (callSite) {
464
473
  providerConfig.callSite = callSite;
474
+ providerConfig.usageTracking = "manual";
465
475
  }
466
476
 
467
477
  // Per-call inference-profile override. The resolver layers
@@ -919,7 +929,8 @@ export class AgentLoop {
919
929
  // plugin pipeline so downstream plugins can swap in a smarter
920
930
  // truncation strategy (e.g. a summariser) while the default
921
931
  // middleware preserves the historical tail-drop behaviour.
922
- const contextWindowTokens = this.config.maxInputTokens ?? 180_000;
932
+ const contextWindowTokens =
933
+ effectiveMaxInputTokens ?? this.config.maxInputTokens ?? 180_000;
923
934
  const maxChars = calculateMaxToolResultChars(contextWindowTokens);
924
935
  const truncateMiddlewares = getMiddlewaresFor("toolResultTruncate");
925
936
 
@@ -987,9 +998,13 @@ export class AgentLoop {
987
998
  contentBlocks: result.contentBlocks,
988
999
  riskLevel: result.riskLevel,
989
1000
  riskReason: result.riskReason,
1001
+ matchedTrustRuleId: result.matchedTrustRuleId,
990
1002
  isContainerized: result.isContainerized,
991
1003
  riskScopeOptions: result.riskScopeOptions,
992
1004
  riskDirectoryScopeOptions: result.riskDirectoryScopeOptions,
1005
+ approvalMode: result.approvalMode,
1006
+ approvalReason: result.approvalReason,
1007
+ riskThreshold: result.riskThreshold,
993
1008
  });
994
1009
  }
995
1010
 
@@ -8,6 +8,10 @@
8
8
  - **Requester self-cancel:** A requester with a pending guardian approval must be able to cancel their own request (but not self-approve).
9
9
  - **Unified guardian decision primitive:** All guardian decision paths (callback buttons, conversational engine, requester self-cancel) must route through `applyGuardianDecision()` in `assistant/src/approvals/guardian-decision-primitive.ts`. Do not inline decision logic (approval record updates, grant minting) at individual callsites.
10
10
 
11
+ ## Single-Guardian Invariant
12
+
13
+ Each assistant instance serves exactly one guardian. Multi-guardian is not supported and will never be. All connections, browser sessions, approval channels, and trust contexts within a single assistant process belong to the same guardian principal. Do not introduce guardian-keyed maps, per-guardian routing logic, or multi-guardian multiplexing — they add complexity without a real use case and create the false impression that cross-guardian isolation is required.
14
+
11
15
  ## Guardian Verification Invariant
12
16
 
13
17
  Guardian verification consumption must be identity-bound to the expected recipient identity. Every outbound verification session stores the expected identity (phone E.164, Telegram user/chat ID), and the consume path rejects attempts where the responding actor's identity does not match.
@@ -88,6 +88,11 @@ mock.module("../../runtime/gateway-client.js", () => ({
88
88
  deliverChannelReply: async () => {},
89
89
  }));
90
90
 
91
+ let _mockConversation: Record<string, unknown> | null = null;
92
+ mock.module("../../daemon/conversation-store.js", () => ({
93
+ findConversation: () => _mockConversation,
94
+ }));
95
+
91
96
  // Stub pending-interactions so tool_approval resolver can find/resolve.
92
97
  let mockInteraction: Record<string, unknown> | null = null;
93
98
  let mockResolved: Record<string, unknown> | null = null;
@@ -155,16 +160,18 @@ describe("guardian approval feed events", () => {
155
160
  mockInteraction = {
156
161
  confirmationDetails: { toolName: "web_fetch" },
157
162
  };
163
+ _mockConversation = {
164
+ handleConfirmationResponse: () => {},
165
+ };
158
166
  mockResolved = {
159
- conversation: {
160
- handleConfirmationResponse: () => {},
161
- },
167
+ conversationId: "conv-test",
162
168
  };
163
169
  });
164
170
 
165
171
  afterEach(() => {
166
172
  mockInteraction = null;
167
173
  mockResolved = null;
174
+ _mockConversation = null;
168
175
  });
169
176
 
170
177
  // -----------------------------------------------------------------------
@@ -14,6 +14,7 @@
14
14
  import { answerCall } from "../calls/call-domain.js";
15
15
  import { findContactChannel } from "../contacts/contact-store.js";
16
16
  import { upsertContactChannel } from "../contacts/contacts-write.js";
17
+ import { findConversation } from "../daemon/conversation-store.js";
17
18
  import { emitFeedEvent } from "../home/emit-feed-event.js";
18
19
  import {
19
20
  type CanonicalGuardianRequest,
@@ -203,7 +204,14 @@ const pendingInteractionResolver: GuardianRequestResolver = {
203
204
  // Map action to the permission system's UserDecision type and notify session.
204
205
  const userDecision: UserDecision =
205
206
  decision.action === "reject" ? "deny" : "allow";
206
- resolved.conversation!.handleConfirmationResponse(
207
+ const conversation = findConversation(resolved.conversationId);
208
+ if (!conversation) {
209
+ return {
210
+ ok: false,
211
+ reason: `conversation_not_found: ${resolved.conversationId}`,
212
+ };
213
+ }
214
+ conversation.handleConfirmationResponse(
207
215
  request.id,
208
216
  userDecision,
209
217
  undefined,
@@ -982,7 +990,7 @@ const toolGrantRequestResolver: GuardianRequestResolver = {
982
990
  const resolverRegistry = new Map<string, GuardianRequestResolver>();
983
991
 
984
992
  /** Register a resolver for a given request kind. */
985
- export function registerResolver(resolver: GuardianRequestResolver): void {
993
+ function registerResolver(resolver: GuardianRequestResolver): void {
986
994
  resolverRegistry.set(resolver.kind, resolver);
987
995
  }
988
996
 
@@ -115,12 +115,23 @@ function makeStreamExportStub(): {
115
115
  tempPath,
116
116
  size: 16,
117
117
  manifest: {
118
- schema_version: "1.0",
118
+ schema_version: 1,
119
+ bundle_id: "00000000-0000-4000-8000-000000000000",
119
120
  created_at: new Date().toISOString(),
120
- source: "test",
121
- description: "test stub",
122
- files: [],
123
- manifest_sha256: "0".repeat(64),
121
+ assistant: { id: "self", name: "Test", runtime_version: "0.0.0-test" },
122
+ origin: { mode: "self-hosted-local" },
123
+ compatibility: {
124
+ min_runtime_version: "0.0.0-test",
125
+ max_runtime_version: null,
126
+ },
127
+ contents: [],
128
+ checksum: "0".repeat(64),
129
+ secrets_redacted: false,
130
+ export_options: {
131
+ include_logs: false,
132
+ include_browser_state: false,
133
+ include_memory_vectors: false,
134
+ },
124
135
  },
125
136
  cleanup: async () => {
126
137
  try {
@@ -539,10 +550,27 @@ describe("createSnapshotNow", () => {
539
550
  tempPath,
540
551
  size: 7,
541
552
  manifest: {
542
- schema_version: "1.0",
553
+ schema_version: 1,
554
+ bundle_id: "00000000-0000-4000-8000-000000000000",
543
555
  created_at: new Date().toISOString(),
544
- files: [],
545
- manifest_sha256: "0".repeat(64),
556
+ assistant: {
557
+ id: "self",
558
+ name: "Test",
559
+ runtime_version: "0.0.0-test",
560
+ },
561
+ origin: { mode: "self-hosted-local" },
562
+ compatibility: {
563
+ min_runtime_version: "0.0.0-test",
564
+ max_runtime_version: null,
565
+ },
566
+ contents: [],
567
+ checksum: "0".repeat(64),
568
+ secrets_redacted: false,
569
+ export_options: {
570
+ include_logs: false,
571
+ include_browser_state: false,
572
+ include_memory_vectors: false,
573
+ },
546
574
  },
547
575
  cleanup: async () => {
548
576
  try {
@@ -173,9 +173,9 @@ describe("getBackupKeyPath", () => {
173
173
  }
174
174
  });
175
175
 
176
- test("ends with /protected/backup.key when env var is unset", () => {
176
+ test("ends with /.backup.key when env var is unset", () => {
177
177
  delete process.env.VELLUM_BACKUP_KEY_PATH;
178
- expect(getBackupKeyPath().endsWith("/protected/backup.key")).toBe(true);
178
+ expect(getBackupKeyPath().endsWith("/.backup.key")).toBe(true);
179
179
  });
180
180
 
181
181
  test("returns the env var override when VELLUM_BACKUP_KEY_PATH is set", () => {