@vellumai/assistant 0.8.10 → 0.8.11-staging.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 (400) hide show
  1. package/bun.lock +62 -1
  2. package/docs/workspace-tools.md +196 -0
  3. package/examples/plugins/echo/README.md +3 -3
  4. package/knip.json +1 -0
  5. package/openapi.yaml +460 -128
  6. package/package.json +2 -1
  7. package/scripts/build-plugin-api.ts +299 -0
  8. package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
  9. package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
  10. package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
  11. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
  12. package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
  13. package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
  14. package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
  15. package/src/__tests__/agent-loop-thinking.test.ts +4 -0
  16. package/src/__tests__/agent-loop.test.ts +578 -5
  17. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
  18. package/src/__tests__/approval-cascade.test.ts +1 -0
  19. package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
  20. package/src/__tests__/btw-routes.test.ts +0 -1
  21. package/src/__tests__/build-persisted-content.test.ts +75 -1
  22. package/src/__tests__/catalog-install-normalize.test.ts +141 -0
  23. package/src/__tests__/ces-startup-timeout.test.ts +60 -0
  24. package/src/__tests__/compaction-events.test.ts +1 -0
  25. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  26. package/src/__tests__/context-overflow-reducer.test.ts +264 -124
  27. package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
  28. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  29. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
  30. package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
  31. package/src/__tests__/conversation-agent-loop.test.ts +131 -551
  32. package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
  33. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  34. package/src/__tests__/conversation-fork-crud.test.ts +259 -0
  35. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  36. package/src/__tests__/conversation-lifecycle.test.ts +257 -1
  37. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  38. package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
  39. package/src/__tests__/conversation-queue.test.ts +1 -39
  40. package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
  41. package/src/__tests__/conversation-skill-tools.test.ts +491 -5
  42. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  43. package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
  44. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  45. package/src/__tests__/conversation-store.test.ts +74 -0
  46. package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
  47. package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
  48. package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
  49. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
  52. package/src/__tests__/corrected-target.test.ts +93 -0
  53. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
  54. package/src/__tests__/credential-execution-tools.test.ts +23 -11
  55. package/src/__tests__/credential-security-invariants.test.ts +6 -1
  56. package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
  57. package/src/__tests__/device-id.test.ts +70 -1
  58. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  59. package/src/__tests__/empty-response-hook.test.ts +242 -66
  60. package/src/__tests__/external-plugin-loader.test.ts +0 -31
  61. package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
  62. package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
  63. package/src/__tests__/history-repair-hook.test.ts +228 -3
  64. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  65. package/src/__tests__/host-browser-proxy.test.ts +254 -9
  66. package/src/__tests__/identity-routes.test.ts +1 -0
  67. package/src/__tests__/image-recovery-hook.test.ts +387 -0
  68. package/src/__tests__/injector-chain.test.ts +5 -4
  69. package/src/__tests__/injector-v3-suppression.test.ts +373 -47
  70. package/src/__tests__/intent-routing.test.ts +7 -0
  71. package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
  72. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  73. package/src/__tests__/oauth-store.test.ts +0 -85
  74. package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
  75. package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
  76. package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
  77. package/src/__tests__/persist-unsendable-image.test.ts +4 -4
  78. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  79. package/src/__tests__/plugin-bootstrap.test.ts +82 -73
  80. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  81. package/src/__tests__/plugin-types.test.ts +0 -8
  82. package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
  83. package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
  84. package/src/__tests__/registry.test.ts +240 -1
  85. package/src/__tests__/require-fresh-approval.test.ts +3 -0
  86. package/src/__tests__/schedule-routes.test.ts +116 -1
  87. package/src/__tests__/schedule-store.test.ts +28 -0
  88. package/src/__tests__/schedule-tools.test.ts +94 -1
  89. package/src/__tests__/server-history-render.test.ts +39 -0
  90. package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
  91. package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
  92. package/src/__tests__/skills.test.ts +202 -0
  93. package/src/__tests__/slim-skill-category.test.ts +195 -0
  94. package/src/__tests__/strip-memory-injections.test.ts +33 -39
  95. package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
  96. package/src/__tests__/title-generate-hook.test.ts +9 -7
  97. package/src/__tests__/tool-audit-listener.test.ts +264 -1
  98. package/src/__tests__/tool-error-hook.test.ts +4 -3
  99. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
  100. package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
  101. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
  102. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  103. package/src/__tests__/tools-get-route.test.ts +202 -0
  104. package/src/__tests__/workspace-tool-loader.test.ts +319 -0
  105. package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
  106. package/src/agent/loop.ts +569 -319
  107. package/src/api/events/tool-result.ts +9 -0
  108. package/src/api/events/tool-use-start.ts +7 -0
  109. package/src/api/index.ts +10 -0
  110. package/src/api/responses/conversation-message.ts +135 -27
  111. package/src/api/responses/memory-v3-selection-log.ts +4 -4
  112. package/src/approvals/guardian-request-resolvers.ts +26 -0
  113. package/src/browser-session/backends/host-bridge.ts +29 -0
  114. package/src/browser-session/index.ts +1 -0
  115. package/src/browser-session/types.ts +5 -1
  116. package/src/cli/commands/__tests__/schedules.test.ts +62 -4
  117. package/src/cli/commands/__tests__/skills.test.ts +53 -0
  118. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  119. package/src/cli/commands/inference-providers.ts +0 -8
  120. package/src/cli/commands/plugins.ts +2 -2
  121. package/src/cli/commands/schedules.ts +27 -4
  122. package/src/cli/commands/skills.ts +187 -146
  123. package/src/cli/commands/tools.ts +106 -0
  124. package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
  125. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
  126. package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
  127. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
  128. package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
  129. package/src/cli/lib/install-from-github.ts +187 -117
  130. package/src/cli/lib/plugin-catalog-cache.ts +9 -9
  131. package/src/cli/lib/plugin-details.ts +38 -68
  132. package/src/cli/lib/plugin-marketplace.ts +42 -14
  133. package/src/cli/lib/search-plugins.ts +29 -129
  134. package/src/cli/program.ts +2 -0
  135. package/src/config/bundled-skills/acp/SKILL.md +1 -0
  136. package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
  137. package/src/config/bundled-skills/app-control/SKILL.md +1 -0
  138. package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
  139. package/src/config/bundled-skills/contacts/SKILL.md +1 -0
  140. package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
  141. package/src/config/bundled-skills/followups/SKILL.md +1 -0
  142. package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
  143. package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
  144. package/src/config/bundled-skills/messaging/SKILL.md +1 -0
  145. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
  146. package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
  147. package/src/config/bundled-skills/schedule/SKILL.md +1 -0
  148. package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
  149. package/src/config/bundled-skills/sequences/SKILL.md +1 -0
  150. package/src/config/bundled-skills/settings/SKILL.md +1 -0
  151. package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
  152. package/src/config/bundled-skills/subagent/SKILL.md +1 -0
  153. package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
  154. package/src/config/env-registry.ts +23 -0
  155. package/src/config/feature-flag-registry.json +17 -81
  156. package/src/config/loader.ts +5 -22
  157. package/src/config/schema.ts +2 -0
  158. package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
  159. package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
  160. package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
  161. package/src/config/schemas/compaction-logs.ts +79 -0
  162. package/src/config/schemas/memory-v2.ts +0 -8
  163. package/src/config/schemas/memory-v3.ts +104 -33
  164. package/src/config/seed-inference-profiles.ts +1 -1
  165. package/src/config/skills.ts +117 -47
  166. package/src/context/compactor.ts +11 -0
  167. package/src/context/strip-injections.ts +38 -4
  168. package/src/credential-execution/feature-gates.ts +0 -21
  169. package/src/credential-execution/startup-timeout.ts +32 -4
  170. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
  171. package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
  172. package/src/daemon/conversation-agent-loop.ts +123 -658
  173. package/src/daemon/conversation-error.ts +6 -33
  174. package/src/daemon/conversation-lifecycle.ts +1 -1
  175. package/src/daemon/conversation-runtime-assembly.ts +183 -22
  176. package/src/daemon/conversation-skill-tools.ts +137 -8
  177. package/src/daemon/conversation-slash.ts +0 -14
  178. package/src/daemon/conversation-store.ts +2 -19
  179. package/src/daemon/conversation-tool-setup.ts +87 -1
  180. package/src/daemon/conversation.ts +119 -50
  181. package/src/daemon/external-plugins-bootstrap.ts +36 -95
  182. package/src/daemon/handlers/config-channels.ts +11 -2
  183. package/src/daemon/handlers/shared.ts +9 -1
  184. package/src/daemon/handlers/skills.ts +10 -4
  185. package/src/daemon/host-app-control-proxy.ts +72 -57
  186. package/src/daemon/host-browser-proxy.ts +117 -22
  187. package/src/daemon/lifecycle.ts +34 -5
  188. package/src/daemon/message-protocol.ts +0 -7
  189. package/src/daemon/message-types/schedules.ts +1 -0
  190. package/src/daemon/message-types/skills.ts +17 -0
  191. package/src/daemon/providers-setup.ts +3 -0
  192. package/src/daemon/server.ts +3 -3
  193. package/src/daemon/tool-setup-types.ts +9 -3
  194. package/src/daemon/trust-context.ts +23 -0
  195. package/src/daemon/workspace-tools-watcher.ts +324 -0
  196. package/src/events/tool-audit-listener.ts +78 -16
  197. package/src/events/tool-metrics-listener.ts +2 -5
  198. package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
  199. package/src/memory/__tests__/conversation-queries.test.ts +176 -0
  200. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
  201. package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
  202. package/src/memory/conversation-crud.ts +134 -3
  203. package/src/memory/conversation-queries.ts +64 -7
  204. package/src/memory/db-init.ts +14 -0
  205. package/src/memory/embedding-backend.test.ts +130 -1
  206. package/src/memory/embedding-backend.ts +79 -106
  207. package/src/memory/embedding-gemini.ts +5 -0
  208. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
  209. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
  210. package/src/memory/graph/conversation-graph-memory.ts +36 -25
  211. package/src/memory/graph/tool-handlers.ts +3 -0
  212. package/src/memory/job-handlers/cleanup.ts +3 -1
  213. package/src/memory/jobs-store.ts +0 -28
  214. package/src/memory/jobs-worker.ts +10 -22
  215. package/src/memory/memory-marker.ts +29 -0
  216. package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
  217. package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
  218. package/src/memory/migrations/270-schedule-description.ts +36 -0
  219. package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
  220. package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
  221. package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
  222. package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
  223. package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
  224. package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
  225. package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
  226. package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
  227. package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
  228. package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
  229. package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
  230. package/src/memory/migrations/index.ts +10 -0
  231. package/src/memory/migrations/registry.ts +8 -0
  232. package/src/memory/schema/conversations.ts +16 -0
  233. package/src/memory/schema/infrastructure.ts +26 -0
  234. package/src/memory/skill-loaded-events-store.test.ts +160 -0
  235. package/src/memory/skill-loaded-events-store.ts +95 -0
  236. package/src/memory/tool-executed-events-store.test.ts +219 -0
  237. package/src/memory/tool-executed-events-store.ts +102 -0
  238. package/src/memory/tool-usage-store.ts +15 -3
  239. package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
  240. package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
  241. package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
  242. package/src/memory/v2/__tests__/page-store.test.ts +33 -0
  243. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
  244. package/src/memory/v2/activation-store.ts +50 -1
  245. package/src/memory/v2/consolidation-job.ts +113 -29
  246. package/src/memory/v2/injected-block-slugs.ts +79 -0
  247. package/src/memory/v2/injection.ts +6 -1
  248. package/src/memory/v2/prompts/consolidation.ts +414 -13
  249. package/src/memory/v2/router.ts +2 -28
  250. package/src/memory/v2/static-context.ts +1 -1
  251. package/src/memory/v2/types.ts +16 -0
  252. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  253. package/src/notifications/access-request-copy.ts +298 -0
  254. package/src/notifications/adapters/slack.ts +3 -3
  255. package/src/notifications/adapters/telegram.ts +2 -1
  256. package/src/notifications/copy-composer.ts +49 -267
  257. package/src/notifications/decision-engine.ts +16 -35
  258. package/src/notifications/home-feed-side-effect.ts +1 -6
  259. package/src/oauth/oauth-store.ts +0 -9
  260. package/src/permissions/checker.test.ts +83 -1
  261. package/src/permissions/checker.ts +25 -2
  262. package/src/permissions/gateway-threshold-reader.test.ts +182 -0
  263. package/src/permissions/gateway-threshold-reader.ts +80 -0
  264. package/src/platform/client.ts +1 -3
  265. package/src/platform/feature-gate.ts +3 -12
  266. package/src/plugin-api/constants.ts +4 -2
  267. package/src/plugin-api/index.ts +63 -11
  268. package/src/plugin-api/types.ts +236 -71
  269. package/src/plugins/defaults/compaction/compact.ts +66 -2
  270. package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
  271. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  272. package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
  273. package/src/plugins/defaults/compaction/window-manager.ts +303 -1
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
  275. package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
  276. package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
  277. package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
  278. package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
  279. package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
  280. package/src/plugins/defaults/history-repair/terminal.ts +39 -2
  281. package/src/plugins/defaults/image-recovery/detect.ts +25 -0
  282. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
  283. package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
  284. package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
  285. package/src/plugins/defaults/image-recovery/package.json +14 -0
  286. package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
  287. package/src/plugins/defaults/index.ts +71 -5
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
  289. package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
  291. package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
  297. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
  304. package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
  305. package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
  306. package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
  307. package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
  308. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
  309. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
  310. package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
  311. package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
  312. package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
  313. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
  314. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
  315. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
  316. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
  317. package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
  318. package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
  319. package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
  320. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
  321. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
  322. package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
  323. package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
  324. package/src/plugins/pipeline.ts +8 -5
  325. package/src/plugins/types.ts +5 -51
  326. package/src/providers/cache-control.ts +26 -0
  327. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
  328. package/src/providers/model-catalog.ts +13 -1
  329. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
  330. package/src/providers/openai/chat-completions-provider.ts +46 -0
  331. package/src/providers/openai/responses-provider.ts +45 -0
  332. package/src/providers/registry.ts +0 -8
  333. package/src/runtime/__tests__/agent-wake.test.ts +0 -1
  334. package/src/runtime/agent-wake.ts +13 -0
  335. package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
  336. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
  337. package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
  338. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
  339. package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
  340. package/src/runtime/routes/acp-routes.test.ts +106 -0
  341. package/src/runtime/routes/acp-routes.ts +248 -2
  342. package/src/runtime/routes/browser-tabs-routes.ts +1 -1
  343. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  344. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
  345. package/src/runtime/routes/consolidation-routes.ts +6 -82
  346. package/src/runtime/routes/conversation-list-routes.ts +6 -0
  347. package/src/runtime/routes/conversation-management-routes.ts +71 -0
  348. package/src/runtime/routes/identity-routes.ts +8 -0
  349. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  350. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
  351. package/src/runtime/routes/plugins-routes.ts +34 -45
  352. package/src/runtime/routes/schedule-routes.ts +43 -5
  353. package/src/runtime/routes/settings-routes.ts +140 -15
  354. package/src/runtime/routes/skills-routes.ts +18 -6
  355. package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
  356. package/src/runtime/services/conversation-serializer.ts +38 -1
  357. package/src/runtime/verification-outbound-actions.ts +147 -2
  358. package/src/runtime/verification-templates.ts +29 -3
  359. package/src/schedule/schedule-store.ts +19 -0
  360. package/src/skills/catalog-install.ts +77 -13
  361. package/src/tasks/task-scheduler.ts +1 -0
  362. package/src/telemetry/types.ts +66 -1
  363. package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
  364. package/src/telemetry/usage-telemetry-reporter.ts +213 -20
  365. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
  366. package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
  367. package/src/tools/browser/browser-execution.ts +27 -9
  368. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
  369. package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
  370. package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
  371. package/src/tools/browser/cdp-client/factory.ts +217 -17
  372. package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
  373. package/src/tools/browser/cdp-client/types.ts +22 -2
  374. package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
  375. package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
  376. package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
  377. package/src/tools/executor.ts +39 -7
  378. package/src/tools/registry.ts +387 -5
  379. package/src/tools/schedule/create.ts +16 -0
  380. package/src/tools/schedule/list.ts +12 -4
  381. package/src/tools/schedule/update.ts +12 -0
  382. package/src/tools/skills/load.ts +11 -6
  383. package/src/tools/terminal/safe-env.ts +2 -0
  384. package/src/tools/types.ts +65 -9
  385. package/src/tools/workspace-tools/loader.ts +673 -0
  386. package/src/usage/attribution.ts +28 -0
  387. package/src/util/device-id.ts +17 -3
  388. package/src/util/platform.ts +16 -0
  389. package/tsconfig.plugin-api.json +13 -0
  390. package/src/__tests__/plugin-external-api.test.ts +0 -68
  391. package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
  392. package/src/daemon/message-types/browser.ts +0 -10
  393. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  394. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
  395. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
  396. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
  397. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
  398. package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
  399. package/src/plugins/external-api.ts +0 -114
  400. package/src/plugins/plugin-skill-contributions.ts +0 -292
@@ -1,187 +0,0 @@
1
- /**
2
- * Verifies that `emitNotificationSignal` invokes the home-feed side
3
- * effect after dispatching, and that the side effect's background-only
4
- * filter correctly suppresses interactive signals.
5
- *
6
- * The side effect itself is unit-tested in `home-feed-side-effect.test.ts`;
7
- * this test exercises the wire-up boundary in `emit-signal.ts` by mocking
8
- * at the `appendFeedItem` / `getConversation` boundary so the real
9
- * background-filter logic runs end-to-end.
10
- */
11
- import { beforeEach, describe, expect, mock, test } from "bun:test";
12
-
13
- import type { FeedItem } from "../../home/feed-types.js";
14
- import type {
15
- NotificationDecision,
16
- NotificationDeliveryResult,
17
- } from "../types.js";
18
-
19
- // ── Module mocks ───────────────────────────────────────────────────────
20
- //
21
- // `mock.module` is hoisted, so these intercepts apply before the module
22
- // under test resolves its imports. Closures over module-scoped state
23
- // let each test reset and inspect captured calls afterwards.
24
-
25
- const appendCalls: FeedItem[] = [];
26
- let conversationRow: { conversationType: string } | null = null;
27
-
28
- mock.module("../../home/feed-writer.js", () => ({
29
- appendFeedItem: async (item: FeedItem) => {
30
- appendCalls.push(item);
31
- },
32
- }));
33
-
34
- // home-feed-side-effect.ts only consumes `getConversation`.
35
- mock.module("../../memory/conversation-crud.js", () => ({
36
- getConversation: () => conversationRow,
37
- reserveMessage: mock(async () => ({ id: "msg-reserve" })),
38
- }));
39
-
40
- // Stub the broadcaster so emit-signal's `getBroadcaster()` does not need
41
- // to build real adapters or pull in `conversation-pairing.ts` transitively
42
- // (which would force us to mirror the full `conversation-crud.js` surface).
43
- class StubBroadcaster {
44
- setOnConversationCreated() {}
45
- async broadcastDecision() {
46
- return [];
47
- }
48
- }
49
- mock.module("../broadcaster.js", () => ({
50
- NotificationBroadcaster: StubBroadcaster,
51
- }));
52
-
53
- mock.module("../adapters/macos.js", () => ({
54
- VellumAdapter: class {},
55
- isGuardianSensitiveEvent: () => false,
56
- }));
57
- mock.module("../adapters/slack.js", () => ({
58
- SlackAdapter: class {},
59
- }));
60
- mock.module("../adapters/telegram.js", () => ({
61
- TelegramAdapter: class {},
62
- }));
63
-
64
- // Stub out the persistence + channel resolution layers so emitNotificationSignal
65
- // can run end-to-end without touching the DB / contacts store / LLM.
66
-
67
- mock.module("../events-store.js", () => ({
68
- createEvent: (params: { id: string }) => ({
69
- id: params.id,
70
- sourceEventName: "schedule.notify",
71
- sourceChannel: "scheduler",
72
- sourceContextId: "conv-source-1",
73
- attentionHintsJson: "{}",
74
- payloadJson: "{}",
75
- dedupeKey: null,
76
- createdAt: 0,
77
- updatedAt: 0,
78
- }),
79
- updateEventDedupeKey: () => {},
80
- }));
81
-
82
- mock.module("../decisions-store.js", () => ({
83
- updateDecision: () => {},
84
- }));
85
-
86
- mock.module("../../contacts/contact-store.js", () => ({
87
- findGuardianForChannel: () => null,
88
- listGuardianChannels: () => [],
89
- }));
90
-
91
- const stubDecision: NotificationDecision = {
92
- shouldNotify: true,
93
- selectedChannels: ["vellum"],
94
- reasoningSummary: "test",
95
- renderedCopy: {
96
- vellum: { title: "Background job done", body: "Summary of what happened." },
97
- },
98
- dedupeKey: "dk-1",
99
- confidence: 1,
100
- fallbackUsed: false,
101
- };
102
-
103
- mock.module("../decision-engine.js", () => ({
104
- evaluateSignal: async () => stubDecision,
105
- // Pass the decision through unchanged — emit-signal calls this after
106
- // evaluateSignal and only re-persists when the reference changes.
107
- enforceRoutingIntent: (decision: NotificationDecision) => decision,
108
- }));
109
-
110
- mock.module("../deterministic-checks.js", () => ({
111
- runDeterministicChecks: async () => ({ passed: true }),
112
- }));
113
-
114
- const stubDeliveryResults: NotificationDeliveryResult[] = [
115
- {
116
- channel: "vellum",
117
- destination: "vellum-client",
118
- status: "sent",
119
- conversationId: "conv-vellum-1",
120
- },
121
- ];
122
-
123
- mock.module("../runtime-dispatch.js", () => ({
124
- dispatchDecision: async () => ({
125
- dispatched: true,
126
- reason: "Dispatched to 1/1 channels",
127
- deliveryResults: stubDeliveryResults,
128
- }),
129
- }));
130
-
131
- const { emitNotificationSignal } = await import("../emit-signal.js");
132
-
133
- // ── Tests ──────────────────────────────────────────────────────────────
134
-
135
- beforeEach(() => {
136
- appendCalls.length = 0;
137
- conversationRow = null;
138
- });
139
-
140
- describe("emitNotificationSignal home-feed wire-up", () => {
141
- test("background-conversation signal triggers appendFeedItem", async () => {
142
- conversationRow = { conversationType: "background" };
143
-
144
- const result = await emitNotificationSignal({
145
- sourceEventName: "schedule.notify",
146
- sourceChannel: "scheduler",
147
- sourceContextId: "conv-source-1",
148
- contextPayload: { title: "Background job done" },
149
- attentionHints: {
150
- requiresAction: false,
151
- urgency: "medium",
152
- isAsyncBackground: false,
153
- visibleInSourceNow: false,
154
- },
155
- });
156
-
157
- expect(result.dispatched).toBe(true);
158
- expect(appendCalls).toHaveLength(1);
159
- const appended = appendCalls[0]!;
160
- expect(appended.id).toBe(`notif:${result.signalId}`);
161
- expect(appended.title).toBe("Background job done");
162
- expect(appended.summary).toBe("Summary of what happened.");
163
- // The feed item's conversationId points to the source conversation
164
- // that emitted the signal, not the conversation the notification
165
- // pipeline spawned to handle it.
166
- expect(appended.conversationId).toBe("conv-source-1");
167
- });
168
-
169
- test("interactive standard conversation does NOT trigger appendFeedItem", async () => {
170
- conversationRow = { conversationType: "standard" };
171
-
172
- const result = await emitNotificationSignal({
173
- sourceEventName: "schedule.notify",
174
- sourceChannel: "scheduler",
175
- sourceContextId: "conv-source-1",
176
- attentionHints: {
177
- requiresAction: false,
178
- urgency: "medium",
179
- isAsyncBackground: false,
180
- visibleInSourceNow: true,
181
- },
182
- });
183
-
184
- expect(result.dispatched).toBe(true);
185
- expect(appendCalls).toHaveLength(0);
186
- });
187
- });
@@ -1,36 +0,0 @@
1
- {
2
- "_comment": "Synthetic, generic eval turns for the memory-v3 orchestrator. No real content. Domains/slugs are placeholders (domain-a/topic-x, page-a). Each turn names which leaves L1 should open and which page IDs L2 should select per opened leaf; expectedFinalInjection is the orchestrator's deduped slug list for that turn given a SHARED working set across the sequence.",
3
- "turns": [
4
- {
5
- "name": "turn 1 — open topic-x, select page-a (pinned) and page-b",
6
- "currentMessage": "tell me about page a",
7
- "routeIds": [1],
8
- "leafSelections": {
9
- "domain-a/topic-x": { "ids": [1, 2], "pinned_ids": [1] }
10
- },
11
- "expectedOpenedLeaves": ["domain-a/topic-x"],
12
- "expectedFinalInjection": ["page-a", "page-b"]
13
- },
14
- {
15
- "name": "turn 2 — open topic-y, select page-c; page-a carries forward (pinned)",
16
- "currentMessage": "now about page c",
17
- "routeIds": [2],
18
- "leafSelections": {
19
- "domain-a/topic-y": { "ids": [1], "pinned_ids": [] }
20
- },
21
- "expectedOpenedLeaves": ["domain-a/topic-y"],
22
- "expectedFinalInjection": ["page-c", "page-a", "page-b"]
23
- },
24
- {
25
- "name": "turn 3 — open both leaves, select page-b and page-c",
26
- "currentMessage": "compare page b and page c",
27
- "routeIds": [1, 2],
28
- "leafSelections": {
29
- "domain-a/topic-x": { "ids": [2], "pinned_ids": [] },
30
- "domain-a/topic-y": { "ids": [1], "pinned_ids": [] }
31
- },
32
- "expectedOpenedLeaves": ["domain-a/topic-x", "domain-a/topic-y"],
33
- "expectedFinalInjection": ["page-b", "page-c", "page-a"]
34
- }
35
- ]
36
- }
@@ -1,37 +0,0 @@
1
- {
2
- "_comment": "Synthetic, generic turns for the memory-v3 LIVE-injection integration test. No real content. The tree has two leaves (domain-a/topic-x → [page-a, page-b], domain-a/topic-y → [page-c]); L1 id 1 → topic-x, id 2 → topic-y. Each turn names which leaves L1 opens and which page IDs L2 selects (and pins) per opened leaf. Driven through a SHARED WorkingSet across the sequence so carry-forward and window eviction are exercised end-to-end. The WorkingSet uses a small evictWindow so a non-pinned page selected only in turn 1 ages out by a later turn while a pinned page persists.",
3
- "turns": [
4
- {
5
- "name": "turn 1 — open topic-x, select page-a (pinned) + page-b (unpinned)",
6
- "currentMessage": "tell me about page a",
7
- "routeIds": [1],
8
- "leafSelections": {
9
- "domain-a/topic-x": { "ids": [1, 2], "pinned_ids": [1] }
10
- }
11
- },
12
- {
13
- "name": "turn 2 — open topic-y, select page-c; page-a + page-b carry forward",
14
- "currentMessage": "now about page c",
15
- "routeIds": [2],
16
- "leafSelections": {
17
- "domain-a/topic-y": { "ids": [1], "pinned_ids": [] }
18
- }
19
- },
20
- {
21
- "name": "turn 3 — open topic-y again, re-select page-c (refreshes its lastSeen)",
22
- "currentMessage": "more on page c",
23
- "routeIds": [2],
24
- "leafSelections": {
25
- "domain-a/topic-y": { "ids": [1], "pinned_ids": [] }
26
- }
27
- },
28
- {
29
- "name": "turn 4 — open topic-y, re-select page-c; page-b now stale enough to age out",
30
- "currentMessage": "still page c",
31
- "routeIds": [2],
32
- "leafSelections": {
33
- "domain-a/topic-y": { "ids": [1], "pinned_ids": [] }
34
- }
35
- }
36
- ]
37
- }
@@ -1,106 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import { WorkingSetEntry } from "../types.js";
4
- import { ScoreFn, WorkingSet } from "../working-set.js";
5
-
6
- describe("WorkingSet.evict", () => {
7
- test("evicts a page unseen past the window", () => {
8
- const ws = new WorkingSet(150, 5);
9
- ws.recordSelection("alpha", 0, false);
10
-
11
- // currentTurn 6, lastSeen 0 → gap 6 > window 5 → evict.
12
- ws.evict(6, new Set());
13
-
14
- expect(ws.union().has("alpha")).toBe(false);
15
- expect(ws.size()).toBe(0);
16
- });
17
-
18
- test("keeps a page still inside the window", () => {
19
- const ws = new WorkingSet(150, 5);
20
- ws.recordSelection("alpha", 0, false);
21
-
22
- // gap 5 is not > 5 → keep.
23
- ws.evict(5, new Set());
24
-
25
- expect(ws.union().has("alpha")).toBe(true);
26
- });
27
-
28
- test("pinned page never evicts past the window", () => {
29
- const ws = new WorkingSet(150, 5);
30
- ws.recordSelection("alpha", 0, true);
31
-
32
- ws.evict(100, new Set());
33
-
34
- expect(ws.union().has("alpha")).toBe(true);
35
- });
36
-
37
- test("exceeding maxPages evicts the least-recently-selected non-pinned page first", () => {
38
- const ws = new WorkingSet(2, 1000);
39
- ws.recordSelection("oldest", 1, false);
40
- ws.recordSelection("middle", 2, false);
41
- ws.recordSelection("newest", 3, false);
42
-
43
- // No window eviction (window huge); cap is 2 → drop lowest lastSeenTurn.
44
- ws.evict(3, new Set());
45
-
46
- expect(ws.size()).toBe(2);
47
- expect(ws.union().has("oldest")).toBe(false);
48
- expect(ws.union().has("middle")).toBe(true);
49
- expect(ws.union().has("newest")).toBe(true);
50
- });
51
-
52
- test("cap eviction never drops pinned pages even when over capacity", () => {
53
- const ws = new WorkingSet(1, 1000);
54
- ws.recordSelection("pinned-a", 1, true);
55
- ws.recordSelection("pinned-b", 2, true);
56
- ws.recordSelection("loose", 3, false);
57
-
58
- ws.evict(3, new Set());
59
-
60
- // Only the non-pinned page can go; pinned pages stay even over cap.
61
- expect(ws.union().has("pinned-a")).toBe(true);
62
- expect(ws.union().has("pinned-b")).toBe(true);
63
- expect(ws.union().has("loose")).toBe(false);
64
- expect(ws.size()).toBe(2);
65
- });
66
-
67
- test("core slugs passed to evict are dropped if present", () => {
68
- const ws = new WorkingSet(150, 5);
69
- ws.recordSelection("alpha", 5, false);
70
- ws.recordSelection("beta", 5, true); // pinned, but core still wins
71
- ws.recordSelection("gamma", 5, false);
72
-
73
- ws.evict(5, new Set(["alpha", "beta"]));
74
-
75
- expect(ws.union().has("alpha")).toBe(false);
76
- expect(ws.union().has("beta")).toBe(false);
77
- expect(ws.union().has("gamma")).toBe(true);
78
- });
79
-
80
- test("custom scoreFn reorders cap-evictions", () => {
81
- // Score by selectedAtTurn instead of lastSeenTurn: the page selected
82
- // earliest becomes most evictable regardless of recent activity.
83
- const bySelectedAt: ScoreFn = (e: WorkingSetEntry) => e.selectedAtTurn;
84
- const ws = new WorkingSet(2, 1000, bySelectedAt);
85
-
86
- ws.recordSelection("first", 0, false);
87
- ws.recordSelection("second", 1, false);
88
- ws.recordSelection("third", 2, false);
89
- // Bump "first" so plain LRU would protect it, but selectedAt still lowest.
90
- ws.recordSelection("first", 9, false);
91
-
92
- ws.evict(9, new Set());
93
-
94
- expect(ws.size()).toBe(2);
95
- // Under custom score, "first" (selectedAt 0) evicts despite recent touch.
96
- expect(ws.union().has("first")).toBe(false);
97
- expect(ws.union().has("second")).toBe(true);
98
- expect(ws.union().has("third")).toBe(true);
99
- });
100
-
101
- test("default no-arg constructor still works for existing callers", () => {
102
- const ws = new WorkingSet();
103
- ws.recordSelection("alpha", 0, false);
104
- expect(ws.size()).toBe(1);
105
- });
106
- });
@@ -1,44 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import { WorkingSet } from "../working-set.js";
4
-
5
- describe("WorkingSet (skeleton)", () => {
6
- test("recording a selection adds it to union()", () => {
7
- const ws = new WorkingSet();
8
- ws.recordSelection("alpha", 1, false);
9
-
10
- expect(ws.union().has("alpha")).toBe(true);
11
- expect(ws.size()).toBe(1);
12
- });
13
-
14
- test("recording the same slug twice preserves selectedAtTurn and updates lastSeenTurn", () => {
15
- const ws = new WorkingSet();
16
- ws.recordSelection("alpha", 1, false);
17
- ws.recordSelection("alpha", 5, false);
18
-
19
- expect(ws.size()).toBe(1);
20
- const entry = [...ws.union()];
21
- expect(entry).toEqual(["alpha"]);
22
- // selectedAtTurn preserved at original turn, lastSeenTurn advanced.
23
- const internal = (
24
- ws as unknown as {
25
- entries: Map<string, { selectedAtTurn: number; lastSeenTurn: number }>;
26
- }
27
- ).entries.get("alpha");
28
- expect(internal?.selectedAtTurn).toBe(1);
29
- expect(internal?.lastSeenTurn).toBe(5);
30
- });
31
-
32
- test("pinned propagates: non-pinned then pinned keeps pinned true", () => {
33
- const ws = new WorkingSet();
34
- ws.recordSelection("alpha", 1, false);
35
- ws.recordSelection("alpha", 2, true);
36
-
37
- const internal = (
38
- ws as unknown as {
39
- entries: Map<string, { pinned: boolean }>;
40
- }
41
- ).entries.get("alpha");
42
- expect(internal?.pinned).toBe(true);
43
- });
44
- });
@@ -1,91 +0,0 @@
1
- import { Slug, WorkingSetEntry } from "./types.js";
2
-
3
- /**
4
- * Scores a working-set entry for cap eviction. Lower score = more evictable.
5
- * This is the pluggable seam for a future salience-weighted policy; the
6
- * default is plain LRU on `lastSeenTurn`.
7
- */
8
- export type ScoreFn = (entry: WorkingSetEntry) => number;
9
-
10
- /** Default eviction score: least-recently-seen entries evict first. */
11
- function lruScore(entry: WorkingSetEntry): number {
12
- return entry.lastSeenTurn;
13
- }
14
-
15
- export class WorkingSet {
16
- private entries = new Map<Slug, WorkingSetEntry>();
17
-
18
- constructor(
19
- private readonly maxPages = 150,
20
- private readonly evictWindow = 5,
21
- private readonly scoreFn: ScoreFn = lruScore,
22
- ) {}
23
-
24
- recordSelection(slug: Slug, turn: number, pinned: boolean): void {
25
- const existing = this.entries.get(slug);
26
- this.entries.set(slug, {
27
- slug,
28
- selectedAtTurn: existing?.selectedAtTurn ?? turn,
29
- pinned: existing?.pinned || pinned,
30
- lastSeenTurn: turn,
31
- });
32
- }
33
-
34
- union(): Set<Slug> {
35
- return new Set(this.entries.keys());
36
- }
37
-
38
- size(): number {
39
- return this.entries.size;
40
- }
41
-
42
- /**
43
- * Prune the working set, in order:
44
- * 1. Core slugs never belong here — drop any entry whose slug is core.
45
- * The section-lane pipeline has no core set, so `coreSlugs` defaults to
46
- * empty (no-op); the tree pipeline passed its core slugs here. Stale and
47
- * cap eviction semantics are identical either way.
48
- * 2. Pinned entries never evict.
49
- * 3. Window eviction: drop a non-pinned entry unseen for more than
50
- * `evictWindow` turns.
51
- * 4. Cap eviction: while over `maxPages`, evict the lowest-scoring
52
- * non-pinned entry first (ties broken deterministically).
53
- */
54
- evict(currentTurn: number, coreSlugs: Set<Slug> = new Set()): void {
55
- // 1. Core slugs are owned by core, not the working set.
56
- for (const slug of this.entries.keys()) {
57
- if (coreSlugs.has(slug)) {
58
- this.entries.delete(slug);
59
- }
60
- }
61
-
62
- // 2 + 3. Window eviction for stale non-pinned entries.
63
- for (const [slug, entry] of this.entries) {
64
- if (
65
- !entry.pinned &&
66
- currentTurn - entry.lastSeenTurn > this.evictWindow
67
- ) {
68
- this.entries.delete(slug);
69
- }
70
- }
71
-
72
- // 4. Cap eviction: evict the lowest-scoring non-pinned entry until the
73
- // set fits. Sort once (ascending score, then slug for determinism) and
74
- // walk the candidates.
75
- if (this.entries.size <= this.maxPages) {
76
- return;
77
- }
78
- const evictable = [...this.entries.values()]
79
- .filter((entry) => !entry.pinned)
80
- .sort(
81
- (a, b) =>
82
- this.scoreFn(a) - this.scoreFn(b) || (a.slug < b.slug ? -1 : 1),
83
- );
84
- for (const entry of evictable) {
85
- if (this.entries.size <= this.maxPages) {
86
- break;
87
- }
88
- this.entries.delete(entry.slug);
89
- }
90
- }
91
- }
@@ -1,114 +0,0 @@
1
- /**
2
- * External plugin runtime — `globalThis.__vellumPluginRuntime` bridge.
3
- *
4
- * ⚠️ DO NOT DELETE THIS AS "UNUSED". There is intentionally NO in-repo consumer:
5
- * the consumers are out-of-repo, user-authored workspace plugins under
6
- * `<workspaceDir>/plugins/*`, which a repo-wide reference search cannot see. On
7
- * a `bun --compile` daemon (the shipped macOS app) this bridge is the ONLY way
8
- * such a plugin can reach the daemon's real `assistantEventHub` /
9
- * `getSecureKeyAsync` singletons — an absolute-path `import` re-loads a disjoint
10
- * copy (see the module-identity note below). Removing it silently breaks every
11
- * event-subscribing workspace plugin on the next app rebuild. This was already
12
- * removed once on the false "unused" assumption (PR #33825) and restored here.
13
- *
14
- * Workspace-local plugins (`<workspaceDir>/plugins/*`) get dynamic-imported by
15
- * {@link loadUserPlugins}. They need to call `registerPlugin()` from their
16
- * module body and they typically also want to read secrets, subscribe to
17
- * runtime events, etc.
18
- *
19
- * Importing those symbols by absolute path
20
- * (`/abs/path/to/assistant/src/plugins/registry.js`) works when the daemon is
21
- * running from source — both the daemon and the dynamic-imported plugin
22
- * resolve the same on-disk file and share module identity. It DOES NOT work
23
- * when the daemon is a `bun --compile` binary: the daemon's modules are baked
24
- * into the executable, and any absolute-path import re-loads a fresh disk
25
- * copy. `registerPlugin()` then writes into a disjoint registry instance and
26
- * the daemon never sees the plugin.
27
- *
28
- * The fix is to expose a single, stable handle on `globalThis` that plugins
29
- * read at module-load time. The daemon's bundled modules attach themselves
30
- * here once at startup; plugins consume the same instance regardless of how
31
- * the daemon was built.
32
- *
33
- * Plugins use the bridge like this:
34
- *
35
- * const runtime = (globalThis as { __vellumPluginRuntime?: ... })
36
- * .__vellumPluginRuntime;
37
- * if (!runtime || runtime.version !== 1) throw new Error("...");
38
- * const { registerPlugin, assistantEventHub, getSecureKeyAsync } = runtime;
39
- *
40
- * Type-only imports (`import type { Plugin } from "..."`) remain free to use
41
- * absolute paths or workspace-local copies — the TypeScript compiler erases
42
- * them and they have no module-identity effect at runtime.
43
- *
44
- * See `experimental/plugins/README.md` for the full authoring contract and
45
- * `assistant/examples/plugins/echo/` for a worked example.
46
- */
47
-
48
- import { assistantEventHub } from "../runtime/assistant-event-hub.js";
49
- import { getSecureKeyAsync } from "../security/secure-keys.js";
50
- import { registerPlugin } from "./registry.js";
51
-
52
- /**
53
- * The handle plugins read from `globalThis.__vellumPluginRuntime`.
54
- *
55
- * `version` is the contract version. Plugins should assert against the
56
- * version they were authored against and refuse to register if they see a
57
- * different one — bumping it is a breaking change to the plugin surface.
58
- *
59
- * The field set is intentionally small: the most-commonly-needed symbols
60
- * across both static (module-load-time) and dynamic (init-time) plugin
61
- * lifecycle. Plugins that need richer runtime state can still receive it
62
- * through {@link PluginInitContext} during `init()`.
63
- */
64
- export interface VellumPluginRuntime {
65
- readonly version: 1;
66
- readonly registerPlugin: typeof registerPlugin;
67
- readonly assistantEventHub: typeof assistantEventHub;
68
- readonly getSecureKeyAsync: typeof getSecureKeyAsync;
69
- }
70
-
71
- /** Stable globalThis key. Don't rename — plugins reference it by string. */
72
- const RUNTIME_GLOBAL_KEY = "__vellumPluginRuntime" as const;
73
-
74
- interface GlobalWithRuntime {
75
- [RUNTIME_GLOBAL_KEY]?: VellumPluginRuntime;
76
- }
77
-
78
- /**
79
- * Install the plugin runtime bridge on `globalThis`. Idempotent — repeat
80
- * calls are no-ops, so it's safe to invoke from tests that also touch the
81
- * lifecycle path.
82
- *
83
- * Must be called BEFORE {@link loadUserPlugins} runs, otherwise plugins that
84
- * touch `globalThis.__vellumPluginRuntime` in their module body will throw.
85
- */
86
- export function installPluginRuntime(): void {
87
- const g = globalThis as GlobalWithRuntime;
88
- if (g[RUNTIME_GLOBAL_KEY]) return;
89
- g[RUNTIME_GLOBAL_KEY] = {
90
- version: 1,
91
- registerPlugin,
92
- assistantEventHub,
93
- getSecureKeyAsync,
94
- };
95
- }
96
-
97
- /**
98
- * Read the installed runtime. Returns `undefined` if {@link installPluginRuntime}
99
- * hasn't been called yet — plugins should treat this as a fatal error.
100
- *
101
- * Exposed mainly for tests and for the rare in-process consumer that wants
102
- * to read the bridge from the same module graph that installed it.
103
- */
104
- export function getPluginRuntime(): VellumPluginRuntime | undefined {
105
- return (globalThis as GlobalWithRuntime)[RUNTIME_GLOBAL_KEY];
106
- }
107
-
108
- /**
109
- * Tear down the runtime handle. Test-only — production code never uninstalls
110
- * because the runtime lifetime is the daemon lifetime.
111
- */
112
- export function uninstallPluginRuntimeForTests(): void {
113
- delete (globalThis as GlobalWithRuntime)[RUNTIME_GLOBAL_KEY];
114
- }