@vellumai/assistant 0.6.5 → 0.6.6

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 (443) hide show
  1. package/AGENTS.md +9 -1
  2. package/ARCHITECTURE.md +15 -17
  3. package/Dockerfile +6 -4
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
  5. package/docs/architecture/integrations.md +32 -39
  6. package/docs/architecture/memory.md +25 -30
  7. package/docs/architecture/security.md +7 -6
  8. package/docs/browser-use-architecture-phase2.md +63 -20
  9. package/docs/plugins.md +761 -0
  10. package/examples/plugins/echo/README.md +132 -0
  11. package/examples/plugins/echo/package.json +17 -0
  12. package/examples/plugins/echo/register.ts +187 -0
  13. package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
  14. package/openapi.yaml +212 -68
  15. package/package.json +1 -1
  16. package/src/__tests__/app-compiler.test.ts +57 -0
  17. package/src/__tests__/approval-cascade.test.ts +7 -2
  18. package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
  19. package/src/__tests__/avatar-generator.test.ts +4 -2
  20. package/src/__tests__/bundled-asset.test.ts +6 -6
  21. package/src/__tests__/catalog-cache.test.ts +69 -0
  22. package/src/__tests__/checker.test.ts +459 -171
  23. package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
  24. package/src/__tests__/compaction-events.test.ts +501 -0
  25. package/src/__tests__/compaction-pipeline.test.ts +210 -0
  26. package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
  27. package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
  28. package/src/__tests__/config-model-image-provider.test.ts +110 -0
  29. package/src/__tests__/config-schema.test.ts +22 -9
  30. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
  31. package/src/__tests__/contacts-tools.test.ts +26 -0
  32. package/src/__tests__/context-overflow-policy.test.ts +7 -7
  33. package/src/__tests__/context-window-manager.test.ts +355 -4
  34. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  35. package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
  36. package/src/__tests__/conversation-agent-loop.test.ts +30 -141
  37. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
  38. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  39. package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
  40. package/src/__tests__/conversation-pairing.test.ts +174 -10
  41. package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
  42. package/src/__tests__/conversation-process-callsite.test.ts +3 -0
  43. package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
  44. package/src/__tests__/conversation-queue.test.ts +29 -14
  45. package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
  47. package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
  48. package/src/__tests__/conversation-seed-composer.test.ts +2 -2
  49. package/src/__tests__/conversation-slash-queue.test.ts +7 -2
  50. package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
  51. package/src/__tests__/conversation-speed-override.test.ts +6 -1
  52. package/src/__tests__/conversation-title-service.test.ts +116 -0
  53. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
  54. package/src/__tests__/conversation-usage.test.ts +1 -1
  55. package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
  56. package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
  57. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
  58. package/src/__tests__/credential-health-service.test.ts +78 -9
  59. package/src/__tests__/credential-security-invariants.test.ts +2 -2
  60. package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
  61. package/src/__tests__/empty-response-pipeline.test.ts +305 -0
  62. package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
  63. package/src/__tests__/first-greeting.test.ts +247 -5
  64. package/src/__tests__/headless-browser-mode.test.ts +57 -0
  65. package/src/__tests__/history-repair-pipeline.test.ts +399 -0
  66. package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
  67. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
  68. package/src/__tests__/host-proxy-interface.test.ts +36 -2
  69. package/src/__tests__/image-credentials.test.ts +137 -0
  70. package/src/__tests__/image-service-dispatcher.test.ts +186 -0
  71. package/src/__tests__/injector-chain.test.ts +526 -0
  72. package/src/__tests__/intent-routing.test.ts +0 -26
  73. package/src/__tests__/llm-call-pipeline.test.ts +285 -0
  74. package/src/__tests__/llm-schema.test.ts +1 -1
  75. package/src/__tests__/media-generate-image.test.ts +119 -13
  76. package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
  77. package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
  78. package/src/__tests__/migration-import-from-url.test.ts +5 -68
  79. package/src/__tests__/model-intents.test.ts +4 -2
  80. package/src/__tests__/notification-broadcaster.test.ts +3 -3
  81. package/src/__tests__/notification-decision-strategy.test.ts +0 -11
  82. package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
  83. package/src/__tests__/oauth-apps-routes.test.ts +1 -1
  84. package/src/__tests__/oauth-cli.test.ts +14 -12
  85. package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
  86. package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
  87. package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
  88. package/src/__tests__/oauth-providers-routes.test.ts +3 -2
  89. package/src/__tests__/oauth-store.test.ts +41 -76
  90. package/src/__tests__/onboarding-template-contract.test.ts +16 -64
  91. package/src/__tests__/openai-image-service.test.ts +368 -0
  92. package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
  93. package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
  94. package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
  95. package/src/__tests__/persistence-pipeline.test.ts +377 -0
  96. package/src/__tests__/pipeline-runner.test.ts +565 -0
  97. package/src/__tests__/platform.test.ts +5 -2
  98. package/src/__tests__/plugin-bootstrap.test.ts +483 -0
  99. package/src/__tests__/plugin-registry.test.ts +273 -0
  100. package/src/__tests__/plugin-route-contribution.test.ts +288 -0
  101. package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
  102. package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
  103. package/src/__tests__/plugin-types.test.ts +320 -0
  104. package/src/__tests__/pricing.test.ts +44 -12
  105. package/src/__tests__/proxy-approval-callback.test.ts +69 -8
  106. package/src/__tests__/reaction-persistence.test.ts +1 -0
  107. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  108. package/src/__tests__/registry.test.ts +0 -2
  109. package/src/__tests__/schedule-routes.test.ts +131 -1
  110. package/src/__tests__/scheduler-recurrence.test.ts +14 -70
  111. package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
  112. package/src/__tests__/secret-detection-handler.test.ts +0 -10
  113. package/src/__tests__/shell-identity.test.ts +0 -134
  114. package/src/__tests__/suggestion-routes.test.ts +103 -4
  115. package/src/__tests__/task-memory-cleanup.test.ts +1 -0
  116. package/src/__tests__/task-scheduler.test.ts +3 -15
  117. package/src/__tests__/test-preload.ts +11 -0
  118. package/src/__tests__/title-generate-pipeline.test.ts +224 -0
  119. package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
  120. package/src/__tests__/tool-error-pipeline.test.ts +244 -0
  121. package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
  122. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
  123. package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
  124. package/src/__tests__/tool-executor.test.ts +141 -0
  125. package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
  126. package/src/__tests__/tool-result-truncation.test.ts +0 -110
  127. package/src/__tests__/user-plugin-loader.test.ts +191 -0
  128. package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
  129. package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
  130. package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
  131. package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
  132. package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
  133. package/src/__tests__/workspace-policy.test.ts +21 -3
  134. package/src/agent/loop.ts +340 -102
  135. package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
  136. package/src/approvals/guardian-request-resolvers.ts +80 -0
  137. package/src/backup/__tests__/backup-worker.test.ts +2 -13
  138. package/src/backup/backup-worker.ts +3 -15
  139. package/src/bundler/app-compiler.ts +84 -1
  140. package/src/calls/call-state.ts +2 -2
  141. package/src/channels/__tests__/types.test.ts +3 -3
  142. package/src/channels/types.ts +6 -4
  143. package/src/cli/__tests__/notifications.test.ts +87 -211
  144. package/src/cli/commands/__tests__/backup.test.ts +1 -1
  145. package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
  146. package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
  147. package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
  148. package/src/cli/commands/backup.ts +2 -2
  149. package/src/cli/commands/clients.ts +138 -0
  150. package/src/cli/commands/completions.ts +2 -9
  151. package/src/cli/commands/conversations.ts +55 -7
  152. package/src/cli/commands/image-generation.ts +33 -34
  153. package/src/cli/commands/notifications.ts +68 -103
  154. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
  155. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  156. package/src/cli/commands/oauth/connect.ts +2 -2
  157. package/src/cli/commands/oauth/providers.ts +176 -8
  158. package/src/cli/commands/oauth/status.ts +46 -36
  159. package/src/cli/commands/skills.ts +3 -4
  160. package/src/cli/program.ts +25 -29
  161. package/src/config/__tests__/backup-schema.test.ts +7 -2
  162. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  163. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
  164. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
  165. package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
  166. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
  167. package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
  168. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
  169. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
  170. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  171. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
  172. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
  173. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
  174. package/src/config/bundled-skills/schedule/SKILL.md +8 -3
  175. package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
  176. package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
  177. package/src/config/bundled-tool-registry.ts +0 -15
  178. package/src/config/feature-flag-registry.json +17 -1
  179. package/src/config/schema.ts +19 -0
  180. package/src/config/schemas/backup.ts +1 -1
  181. package/src/config/schemas/conversations.ts +16 -0
  182. package/src/config/schemas/llm.ts +2 -3
  183. package/src/config/schemas/security.ts +6 -6
  184. package/src/config/schemas/tts.ts +11 -0
  185. package/src/config/skill-state.ts +6 -2
  186. package/src/config/skills.ts +94 -5
  187. package/src/context/__tests__/compact-prompt.test.ts +27 -9
  188. package/src/context/prompts/compact.md +26 -12
  189. package/src/context/tool-result-truncation.ts +3 -63
  190. package/src/context/window-manager.ts +190 -16
  191. package/src/credential-health/credential-health-service.ts +19 -6
  192. package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
  193. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
  194. package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
  195. package/src/daemon/config-watcher.ts +0 -2
  196. package/src/daemon/context-overflow-policy.ts +4 -13
  197. package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
  198. package/src/daemon/conversation-agent-loop.ts +984 -683
  199. package/src/daemon/conversation-history.ts +10 -19
  200. package/src/daemon/conversation-lifecycle.ts +37 -19
  201. package/src/daemon/conversation-notifiers.ts +2 -110
  202. package/src/daemon/conversation-process.ts +14 -7
  203. package/src/daemon/conversation-runtime-assembly.ts +532 -411
  204. package/src/daemon/conversation-tool-setup.ts +41 -4
  205. package/src/daemon/conversation.ts +80 -35
  206. package/src/daemon/external-plugins-bootstrap.ts +478 -0
  207. package/src/daemon/first-greeting.ts +191 -14
  208. package/src/daemon/handlers/config-model.ts +11 -0
  209. package/src/daemon/handlers/skills.ts +5 -1
  210. package/src/daemon/lifecycle.ts +33 -68
  211. package/src/daemon/message-types/computer-use.ts +2 -34
  212. package/src/daemon/message-types/conversations.ts +49 -0
  213. package/src/daemon/message-types/messages.ts +12 -0
  214. package/src/daemon/server.ts +5 -3
  215. package/src/daemon/shutdown-handlers.ts +2 -12
  216. package/src/daemon/tool-side-effects.ts +14 -56
  217. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
  218. package/src/heartbeat/heartbeat-service.ts +24 -1
  219. package/src/home/__tests__/feed-population-integration.test.ts +312 -0
  220. package/src/home/emit-feed-event.ts +7 -0
  221. package/src/home/feed-types.ts +41 -2
  222. package/src/home/rewrite-command-preview.ts +66 -0
  223. package/src/ipc/__tests__/socket-path.test.ts +11 -50
  224. package/src/ipc/cli-client.ts +1 -1
  225. package/src/ipc/cli-server.ts +3 -3
  226. package/src/ipc/gateway-client.ts +4 -1
  227. package/src/ipc/routes/browser-context.ts +2 -0
  228. package/src/ipc/routes/browser.ts +1 -0
  229. package/src/ipc/routes/get-contact.ts +16 -0
  230. package/src/ipc/routes/index.ts +14 -0
  231. package/src/ipc/routes/list-clients.ts +31 -0
  232. package/src/ipc/routes/merge-contacts.ts +17 -0
  233. package/src/ipc/routes/notification.ts +133 -0
  234. package/src/ipc/routes/rename-conversation.ts +59 -0
  235. package/src/ipc/routes/search-contacts.ts +19 -0
  236. package/src/ipc/routes/upsert-contact.ts +25 -0
  237. package/src/ipc/socket-path.ts +14 -38
  238. package/src/media/app-icon-generator.ts +23 -46
  239. package/src/media/avatar-router.ts +26 -41
  240. package/src/media/gemini-image-service.ts +8 -41
  241. package/src/media/image-credentials.ts +73 -0
  242. package/src/media/image-service.ts +85 -0
  243. package/src/media/openai-image-service.ts +131 -0
  244. package/src/media/types.ts +46 -0
  245. package/src/memory/conversation-crud.ts +48 -18
  246. package/src/memory/conversation-queries.ts +57 -4
  247. package/src/memory/conversation-title-service.ts +25 -0
  248. package/src/memory/db-init.ts +8 -0
  249. package/src/memory/embedding-gemini.test.ts +41 -2
  250. package/src/memory/embedding-gemini.ts +6 -1
  251. package/src/memory/graph/bootstrap.test.ts +282 -0
  252. package/src/memory/graph/bootstrap.ts +8 -5
  253. package/src/memory/graph/extraction.ts +10 -2
  254. package/src/memory/graph/graph-search.test.ts +1 -0
  255. package/src/memory/graph/inspect.ts +2 -2
  256. package/src/memory/graph/retriever.ts +10 -3
  257. package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
  258. package/src/memory/migrations/149-oauth-tables.ts +1 -0
  259. package/src/memory/migrations/223-schedule-script-column.ts +11 -0
  260. package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
  261. package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
  262. package/src/memory/migrations/index.ts +4 -0
  263. package/src/memory/pkb/pkb-index.test.ts +1 -0
  264. package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
  265. package/src/memory/pkb/pkb-search.test.ts +65 -4
  266. package/src/memory/pkb/pkb-search.ts +40 -18
  267. package/src/memory/qdrant-client.test.ts +60 -0
  268. package/src/memory/qdrant-client.ts +25 -0
  269. package/src/memory/schema/infrastructure.ts +1 -0
  270. package/src/memory/schema/oauth.ts +4 -1
  271. package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
  272. package/src/messaging/providers/slack/render-transcript.ts +58 -0
  273. package/src/notifications/conversation-pairing.ts +78 -19
  274. package/src/notifications/copy-composer.ts +0 -5
  275. package/src/notifications/emit-signal.ts +1 -1
  276. package/src/notifications/signal.ts +1 -2
  277. package/src/oauth/AGENTS.md +1 -1
  278. package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
  279. package/src/oauth/connect-orchestrator.ts +8 -34
  280. package/src/oauth/connect-types.ts +6 -10
  281. package/src/oauth/manual-token-connection.ts +23 -0
  282. package/src/oauth/oauth-store.ts +30 -14
  283. package/src/oauth/provider-serializer.ts +6 -1
  284. package/src/oauth/seed-providers.ts +56 -108
  285. package/src/outbound-proxy/http-forwarder.ts +9 -0
  286. package/src/permissions/approval-policy.test.ts +293 -18
  287. package/src/permissions/approval-policy.ts +110 -58
  288. package/src/permissions/arg-parser.test.ts +161 -0
  289. package/src/permissions/arg-parser.ts +141 -0
  290. package/src/permissions/bash-risk-classifier.test.ts +414 -2
  291. package/src/permissions/bash-risk-classifier.ts +303 -60
  292. package/src/permissions/checker.ts +157 -29
  293. package/src/permissions/command-registry.test.ts +239 -0
  294. package/src/permissions/command-registry.ts +234 -54
  295. package/src/permissions/defaults.ts +5 -4
  296. package/src/permissions/gateway-threshold-reader.ts +196 -0
  297. package/src/permissions/prompter.ts +4 -0
  298. package/src/permissions/risk-types.ts +61 -4
  299. package/src/permissions/schedule-risk-classifier.test.ts +129 -0
  300. package/src/permissions/schedule-risk-classifier.ts +85 -0
  301. package/src/permissions/shell-identity.ts +2 -42
  302. package/src/permissions/types.ts +2 -0
  303. package/src/permissions/workspace-policy.ts +8 -3
  304. package/src/plugins/defaults/circuit-breaker.ts +146 -0
  305. package/src/plugins/defaults/compaction.ts +145 -0
  306. package/src/plugins/defaults/empty-response.ts +126 -0
  307. package/src/plugins/defaults/history-repair.ts +85 -0
  308. package/src/plugins/defaults/index.ts +116 -0
  309. package/src/plugins/defaults/injectors.ts +491 -0
  310. package/src/plugins/defaults/llm-call.ts +82 -0
  311. package/src/plugins/defaults/memory-retrieval.ts +226 -0
  312. package/src/plugins/defaults/overflow-reduce.ts +181 -0
  313. package/src/plugins/defaults/persistence.ts +129 -0
  314. package/src/plugins/defaults/title-generate.ts +95 -0
  315. package/src/plugins/defaults/token-estimate.ts +104 -0
  316. package/src/plugins/defaults/tool-error.ts +126 -0
  317. package/src/plugins/defaults/tool-execute.ts +89 -0
  318. package/src/plugins/defaults/tool-result-truncate.ts +88 -0
  319. package/src/plugins/pipeline.ts +316 -0
  320. package/src/plugins/plugin-skill-contributions.ts +292 -0
  321. package/src/plugins/registry.ts +241 -0
  322. package/src/plugins/types.ts +1134 -0
  323. package/src/plugins/user-loader.ts +177 -0
  324. package/src/prompts/templates/BOOTSTRAP.md +27 -77
  325. package/src/providers/model-catalog.ts +52 -29
  326. package/src/providers/model-intents.ts +1 -1
  327. package/src/providers/openrouter/client.ts +5 -1
  328. package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
  329. package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
  330. package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
  331. package/src/providers/speech-to-text/xai-realtime.ts +39 -14
  332. package/src/runtime/AGENTS.md +25 -16
  333. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
  334. package/src/runtime/__tests__/client-registry.test.ts +293 -0
  335. package/src/runtime/client-registry.ts +261 -0
  336. package/src/runtime/http-server.ts +77 -8
  337. package/src/runtime/http-types.ts +0 -2
  338. package/src/runtime/migrations/vbundle-builder.ts +1 -22
  339. package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
  340. package/src/runtime/routes/approval-routes.ts +17 -0
  341. package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
  342. package/src/runtime/routes/conversation-routes.ts +223 -116
  343. package/src/runtime/routes/inbound-message-handler.ts +88 -13
  344. package/src/runtime/routes/memory-item-routes.test.ts +1 -0
  345. package/src/runtime/routes/migration-routes.ts +0 -3
  346. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
  347. package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
  348. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
  349. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
  350. package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
  351. package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
  352. package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
  353. package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
  354. package/src/runtime/routes/playground/deps.ts +56 -0
  355. package/src/runtime/routes/playground/force-compact.ts +73 -0
  356. package/src/runtime/routes/playground/guard.ts +37 -0
  357. package/src/runtime/routes/playground/index.ts +28 -0
  358. package/src/runtime/routes/playground/inject-failures.ts +159 -0
  359. package/src/runtime/routes/playground/reset-circuit.ts +115 -0
  360. package/src/runtime/routes/playground/seed-conversation.ts +139 -0
  361. package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
  362. package/src/runtime/routes/playground/state.ts +78 -0
  363. package/src/runtime/routes/schedule-routes.ts +89 -8
  364. package/src/runtime/skill-route-registry.ts +75 -15
  365. package/src/schedule/run-script.ts +68 -0
  366. package/src/schedule/schedule-store.ts +7 -1
  367. package/src/schedule/scheduler.ts +48 -8
  368. package/src/skills/catalog-cache.ts +12 -5
  369. package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
  370. package/src/tools/browser/browser-execution.ts +88 -19
  371. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
  372. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
  373. package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
  374. package/src/tools/browser/cdp-client/factory.ts +15 -4
  375. package/src/tools/executor.ts +126 -74
  376. package/src/tools/network/script-proxy/session-manager.ts +37 -1
  377. package/src/tools/permission-checker.ts +98 -49
  378. package/src/tools/policy-context.ts +4 -0
  379. package/src/tools/registry.ts +140 -3
  380. package/src/tools/schedule/create.ts +23 -8
  381. package/src/tools/schedule/update.ts +3 -1
  382. package/src/tools/secret-detection-handler.ts +0 -51
  383. package/src/tools/system/avatar-generator.ts +6 -2
  384. package/src/tools/types.ts +28 -2
  385. package/src/util/platform.ts +7 -2
  386. package/src/util/pricing.ts +26 -3
  387. package/src/workspace/migrations/006-services-config.ts +2 -4
  388. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
  389. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
  390. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
  391. package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
  392. package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
  393. package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
  394. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
  395. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
  396. package/src/workspace/migrations/registry.ts +12 -0
  397. package/tsconfig.json +1 -1
  398. package/hook-templates/debug-prompt-logger/hook.json +0 -7
  399. package/hook-templates/debug-prompt-logger/run.sh +0 -66
  400. package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
  401. package/src/__tests__/context-overflow-approval.test.ts +0 -156
  402. package/src/__tests__/hooks-blocking.test.ts +0 -178
  403. package/src/__tests__/hooks-cli.test.ts +0 -182
  404. package/src/__tests__/hooks-config.test.ts +0 -108
  405. package/src/__tests__/hooks-discovery.test.ts +0 -211
  406. package/src/__tests__/hooks-integration.test.ts +0 -196
  407. package/src/__tests__/hooks-manager.test.ts +0 -226
  408. package/src/__tests__/hooks-runner.test.ts +0 -175
  409. package/src/__tests__/hooks-settings.test.ts +0 -160
  410. package/src/__tests__/hooks-templates.test.ts +0 -169
  411. package/src/__tests__/hooks-ts-runner.test.ts +0 -170
  412. package/src/__tests__/hooks-watch.test.ts +0 -112
  413. package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
  414. package/src/__tests__/oauth-scope-policy.test.ts +0 -180
  415. package/src/__tests__/send-notification-tool.test.ts +0 -83
  416. package/src/cli/commands/shotgun.ts +0 -266
  417. package/src/config/bundled-skills/conversations/SKILL.md +0 -20
  418. package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
  419. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
  420. package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
  421. package/src/config/bundled-skills/notifications/SKILL.md +0 -40
  422. package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
  423. package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
  424. package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
  425. package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
  426. package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
  427. package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
  428. package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
  429. package/src/daemon/context-overflow-approval.ts +0 -52
  430. package/src/daemon/watch-handler.ts +0 -399
  431. package/src/hooks/cli.ts +0 -253
  432. package/src/hooks/config.ts +0 -100
  433. package/src/hooks/discovery.ts +0 -135
  434. package/src/hooks/manager.ts +0 -179
  435. package/src/hooks/runner.ts +0 -117
  436. package/src/hooks/templates.ts +0 -77
  437. package/src/hooks/types.ts +0 -75
  438. package/src/oauth/scope-policy.ts +0 -89
  439. package/src/runtime/gateway-internal-client.ts +0 -94
  440. package/src/runtime/routes/watch-routes.ts +0 -156
  441. package/src/signals/shotgun.ts +0 -203
  442. package/src/tools/watch/screen-watch.ts +0 -144
  443. package/src/tools/watch/watch-state.ts +0 -142
@@ -0,0 +1,320 @@
1
+ /**
2
+ * Shape-only tests for the plugin core types (PR 11).
3
+ *
4
+ * These tests don't exercise any runtime behavior — they only assert, via
5
+ * the `satisfies` operator, that a fully-populated `Plugin` literal lines
6
+ * up with the public interface. If a later PR changes a field name or
7
+ * signature in a breaking way, this file fails to type-check and the
8
+ * regression is caught at `tsc --noEmit` / `bun test` time.
9
+ */
10
+
11
+ import { describe, expect, test } from "bun:test";
12
+
13
+ import type { TrustContext } from "../daemon/conversation-runtime-assembly.js";
14
+ import { RiskLevel } from "../permissions/types.js";
15
+ import {
16
+ type CircuitBreakerArgs,
17
+ type CircuitBreakerResult,
18
+ type CompactionArgs,
19
+ type CompactionResult,
20
+ type EmptyResponseArgs,
21
+ type EmptyResponseResult,
22
+ type EstimateArgs,
23
+ type EstimateResult,
24
+ type Injector,
25
+ type LLMCallArgs,
26
+ type LLMCallResult,
27
+ type MemoryArgs,
28
+ type MemoryResult,
29
+ type Middleware,
30
+ type OverflowReduceArgs,
31
+ type OverflowReduceResult,
32
+ type PersistArgs,
33
+ type PersistResult,
34
+ type Plugin,
35
+ PluginExecutionError,
36
+ type PluginInitContext,
37
+ type PluginManifest,
38
+ PluginTimeoutError,
39
+ type TitleArgs,
40
+ type TitleResult,
41
+ type ToolErrorArgs,
42
+ type ToolErrorDecision,
43
+ type ToolExecuteArgs,
44
+ type ToolExecuteResult,
45
+ type ToolResultTruncateArgs,
46
+ type ToolResultTruncateResult,
47
+ type TurnContext,
48
+ } from "../plugins/types.js";
49
+ import type { Tool } from "../tools/types.js";
50
+
51
+ const sampleTrust: TrustContext = {
52
+ sourceChannel: "vellum",
53
+ trustClass: "guardian",
54
+ };
55
+
56
+ const sampleTurnContext: TurnContext = {
57
+ requestId: "req-abc",
58
+ conversationId: "conv-xyz",
59
+ turnIndex: 0,
60
+ pluginName: "sample-plugin",
61
+ trust: sampleTrust,
62
+ };
63
+
64
+ describe("plugin core types", () => {
65
+ test("a fully-populated Plugin literal satisfies the interface", () => {
66
+ const manifest: PluginManifest = {
67
+ name: "sample-plugin",
68
+ version: "0.1.0",
69
+ provides: { sampleApi: "v1" },
70
+ requires: { pluginRuntime: "v1" },
71
+ requiresCredential: ["SAMPLE_API_KEY"],
72
+ requiresFlag: ["sample-feature"],
73
+ config: { parse: (input: unknown) => input },
74
+ };
75
+
76
+ // Generic passthrough — typed per slot below because per-pipeline
77
+ // arg/result types have diverged from the early `{input: unknown}` /
78
+ // `{output: unknown}` placeholders as individual pipeline wrap-up PRs
79
+ // land.
80
+ function passthroughFor<A, R>(): Middleware<A, R> {
81
+ return async (args, next, _ctx) => next(args);
82
+ }
83
+ const passthrough: Middleware<
84
+ { input: unknown },
85
+ { output: unknown }
86
+ > = async (args, next, _ctx) => next(args);
87
+ const passthroughHistoryRepair = passthroughFor<
88
+ import("../plugins/types.js").HistoryRepairArgs,
89
+ import("../plugins/types.js").HistoryRepairResult
90
+ >();
91
+
92
+ // `llmCall` has concrete arg/result types (upgraded in PR 15).
93
+ const llmCallPassthrough: Middleware<LLMCallArgs, LLMCallResult> = async (
94
+ args,
95
+ next,
96
+ _ctx,
97
+ ) => next(args);
98
+
99
+ // `toolExecute` has concrete arg/result types (refined in PR 16).
100
+ const toolExecutePassthrough: Middleware<
101
+ ToolExecuteArgs,
102
+ ToolExecuteResult
103
+ > = async (args, next, _ctx) => next(args);
104
+
105
+ // `toolResultTruncate` has a concrete args/result shape (PR 17) so we
106
+ // need a dedicated passthrough for that slot.
107
+ const truncatePassthrough: Middleware<
108
+ ToolResultTruncateArgs,
109
+ ToolResultTruncateResult
110
+ > = async (args, _next, _ctx) => ({
111
+ content: args.content,
112
+ truncated: false,
113
+ });
114
+
115
+ // The `emptyResponse` slot has concrete args/result types; use a
116
+ // dedicated passthrough so the `satisfies Plugin` check stays honest.
117
+ const emptyResponsePassthrough: Middleware<
118
+ EmptyResponseArgs,
119
+ EmptyResponseResult
120
+ > = async (args, next, _ctx) => next(args);
121
+
122
+ // The `toolError` slot has concrete args/result types (PR 19); use a
123
+ // dedicated passthrough so the shape-only test keeps compiling as types
124
+ // get tightened.
125
+ const toolErrorPassthrough: Middleware<
126
+ ToolErrorArgs,
127
+ ToolErrorDecision
128
+ > = async (args, next, _ctx) => next(args);
129
+
130
+ // `memoryRetrieval` has a concrete typed signature (MemoryArgs →
131
+ // MemoryResult) introduced in PR 20, so it can't use the generic
132
+ // `{ input }` passthrough above.
133
+ const memoryPassthrough: Middleware<MemoryArgs, MemoryResult> = async (
134
+ args,
135
+ next,
136
+ _ctx,
137
+ ) => next(args);
138
+
139
+ // `tokenEstimate` has a concrete arg/result shape (refined in the
140
+ // tokenEstimate-pipeline PR), so its middleware can't share the generic
141
+ // `{ input, output }` passthrough. A slot-specific passthrough keeps the
142
+ // shape-only assertion honest across type-refinement PRs.
143
+ const tokenEstimatePassthrough: Middleware<
144
+ EstimateArgs,
145
+ EstimateResult
146
+ > = async (args, next, _ctx) => next(args);
147
+
148
+ // `overflowReduce` has a concrete arg/result shape (PR 23). Uses a
149
+ // dedicated passthrough that returns a structurally-correct result so
150
+ // `satisfies Plugin` keeps verifying the signature.
151
+ const overflowReducePassthrough: Middleware<
152
+ OverflowReduceArgs,
153
+ OverflowReduceResult
154
+ > = async (args, _next, _ctx) => ({
155
+ messages: args.messages,
156
+ runMessages: args.runMessages,
157
+ injectionMode: "full",
158
+ reducerState: {
159
+ appliedTiers: [],
160
+ injectionMode: "full",
161
+ exhausted: true,
162
+ },
163
+ reducerCompacted: false,
164
+ attempts: 0,
165
+ });
166
+
167
+ // Slot-specific passthrough for the `compaction` pipeline — PR 25
168
+ // narrowed its args/result away from the generic `{ input: unknown }`
169
+ // placeholder, so the generic `passthrough` no longer satisfies its
170
+ // middleware signature. This dedicated middleware keeps the shape-only
171
+ // assertion for the slot without forcing every other slot to narrow.
172
+ const compactionPassthrough: Middleware<
173
+ CompactionArgs,
174
+ CompactionResult
175
+ > = async (args, next, _ctx) => next(args);
176
+
177
+ // `circuitBreaker` carries a concrete arg shape (pipeline wrapping
178
+ // landed ahead of the other slots) so it needs its own passthrough
179
+ // rather than reusing the generic placeholder.
180
+ const circuitPassthrough: Middleware<
181
+ CircuitBreakerArgs,
182
+ CircuitBreakerResult
183
+ > = async (args, next, _ctx) => next(args);
184
+
185
+ // `persistence` has concrete discriminated-union arg/result types
186
+ // (upgraded from the initial `{ input }/{ output }` placeholder in PR 27)
187
+ // so it gets its own passthrough rather than sharing the generic one
188
+ // above.
189
+ const persistPassthrough: Middleware<PersistArgs, PersistResult> = async (
190
+ args,
191
+ next,
192
+ _ctx,
193
+ ) => next(args);
194
+
195
+ // The `titleGenerate` slot now has concrete arg/result types (PR 28)
196
+ // rather than the placeholder `{ input/output: unknown }` shape, so it
197
+ // needs its own passthrough implementation.
198
+ const titlePassthrough: Middleware<TitleArgs, TitleResult> = async (
199
+ args,
200
+ next,
201
+ _ctx,
202
+ ) => next(args);
203
+
204
+ const injector: Injector = {
205
+ name: "sample-injector",
206
+ order: 10,
207
+ async produce(_ctx) {
208
+ return { id: "sample-block", text: "hello", meta: { kind: "demo" } };
209
+ },
210
+ };
211
+
212
+ const sampleTool: Tool = {
213
+ name: "sample-tool",
214
+ description: "Sample plugin tool",
215
+ category: "plugins",
216
+ defaultRiskLevel: RiskLevel.Low,
217
+ getDefinition() {
218
+ return {
219
+ name: "sample-tool",
220
+ description: "Sample plugin tool",
221
+ input_schema: { type: "object", properties: {}, required: [] },
222
+ };
223
+ },
224
+ async execute() {
225
+ return { content: "ok", isError: false };
226
+ },
227
+ };
228
+
229
+ const plugin = {
230
+ manifest,
231
+ async init(ctx: PluginInitContext) {
232
+ // Touch every field so refactors that rename any of them break here.
233
+ void ctx.config;
234
+ void ctx.credentials;
235
+ void ctx.logger;
236
+ void ctx.pluginStorageDir;
237
+ void ctx.assistantVersion;
238
+ void ctx.apiVersions;
239
+ },
240
+ async onShutdown() {
241
+ // no-op
242
+ },
243
+ tools: [sampleTool],
244
+ routes: [
245
+ {
246
+ pattern: /^\/sample$/,
247
+ methods: ["GET"],
248
+ handler: async () => new Response("ok", { status: 200 }),
249
+ },
250
+ ],
251
+ skills: [
252
+ {
253
+ id: "sample-skill",
254
+ name: "Sample Skill",
255
+ description: "Demo plugin-contributed skill",
256
+ body: "## Sample\n\nPlugin-provided skill body.",
257
+ },
258
+ ],
259
+ injectors: [injector],
260
+ middleware: {
261
+ turn: passthrough,
262
+ llmCall: llmCallPassthrough,
263
+ toolExecute: toolExecutePassthrough,
264
+ memoryRetrieval: memoryPassthrough,
265
+ historyRepair: passthroughHistoryRepair,
266
+ tokenEstimate: tokenEstimatePassthrough,
267
+ compaction: compactionPassthrough,
268
+ overflowReduce: overflowReducePassthrough,
269
+ persistence: persistPassthrough,
270
+ titleGenerate: titlePassthrough,
271
+ toolResultTruncate: truncatePassthrough,
272
+ emptyResponse: emptyResponsePassthrough,
273
+ toolError: toolErrorPassthrough,
274
+ circuitBreaker: circuitPassthrough,
275
+ },
276
+ } satisfies Plugin;
277
+
278
+ // Minimal runtime check so the test body is non-empty.
279
+ expect(plugin.manifest.name).toBe("sample-plugin");
280
+ expect(plugin.middleware.turn).toBe(passthrough);
281
+ expect(plugin.middleware.historyRepair).toBe(passthroughHistoryRepair);
282
+ });
283
+
284
+ test("PluginTimeoutError carries pipeline, plugin, and elapsed fields", () => {
285
+ const err = new PluginTimeoutError("compaction", "sample-plugin", 30000);
286
+ expect(err).toBeInstanceOf(Error);
287
+ expect(err.name).toBe("PluginTimeoutError");
288
+ expect(err.pipeline).toBe("compaction");
289
+ expect(err.pluginName).toBe("sample-plugin");
290
+ expect(err.elapsedMs).toBe(30000);
291
+ expect(err.message).toContain("compaction");
292
+ expect(err.message).toContain("30000");
293
+ expect(err.message).toContain("sample-plugin");
294
+ });
295
+
296
+ test("PluginTimeoutError omits plugin suffix when unknown", () => {
297
+ const err = new PluginTimeoutError("llmCall", undefined, 1234);
298
+ expect(err.pluginName).toBeUndefined();
299
+ expect(err.message).not.toContain("offending plugin");
300
+ });
301
+
302
+ test("PluginExecutionError carries the plugin name and message", () => {
303
+ const err = new PluginExecutionError(
304
+ "plugin 'x' requires memoryApi@v2, assistant exposes v1",
305
+ "x",
306
+ );
307
+ expect(err).toBeInstanceOf(Error);
308
+ expect(err.name).toBe("PluginExecutionError");
309
+ expect(err.pluginName).toBe("x");
310
+ expect(err.message).toContain("memoryApi@v2");
311
+ });
312
+
313
+ test("TurnContext references the canonical TrustContext", () => {
314
+ // Assignment is the real assertion — if `TurnContext.trust` drifts from
315
+ // `TrustContext` this fails to compile.
316
+ const ctx: TurnContext = sampleTurnContext;
317
+ expect(ctx.trust.trustClass).toBe("guardian");
318
+ expect(ctx.trust.sourceChannel).toBe("vellum");
319
+ });
320
+ });
@@ -30,7 +30,7 @@ describe("resolvePricing", () => {
30
30
  1_000_000,
31
31
  );
32
32
  expect(result.pricingStatus).toBe("priced");
33
- expect(result.estimatedCostUsd).toBe(15 + 75);
33
+ expect(result.estimatedCostUsd).toBe(5 + 25);
34
34
  });
35
35
 
36
36
  test("returns priced for claude-sonnet-4", () => {
@@ -52,7 +52,7 @@ describe("resolvePricing", () => {
52
52
  1_000_000,
53
53
  );
54
54
  expect(result.pricingStatus).toBe("priced");
55
- expect(result.estimatedCostUsd).toBe(0.8 + 4);
55
+ expect(result.estimatedCostUsd).toBe(1 + 5);
56
56
  });
57
57
  });
58
58
 
@@ -202,7 +202,7 @@ describe("resolvePricing", () => {
202
202
  1_000_000,
203
203
  );
204
204
  expect(result.pricingStatus).toBe("priced");
205
- expect(result.estimatedCostUsd).toBe(15 + 75);
205
+ expect(result.estimatedCostUsd).toBe(5 + 25);
206
206
  });
207
207
 
208
208
  test("matches gpt-4o-mini-2024-07-18 via gpt-4o-mini prefix", () => {
@@ -269,6 +269,7 @@ describe("resolvePricingForUsage", () => {
269
269
  );
270
270
 
271
271
  expect(result.pricingStatus).toBe("priced");
272
+ // 5 (input) + 50 (output) + 0.15 (cache-read) + 1.25 (5m write) + 1.0 (1h write) = 57.4
272
273
  expect(result.estimatedCostUsd).toBeCloseTo(57.4, 10);
273
274
  });
274
275
 
@@ -306,7 +307,11 @@ describe("fast mode pricing", () => {
306
307
  speed: "fast",
307
308
  };
308
309
 
309
- const result = resolvePricingForUsage("anthropic", "claude-opus-4-6", usage);
310
+ const result = resolvePricingForUsage(
311
+ "anthropic",
312
+ "claude-opus-4-6",
313
+ usage,
314
+ );
310
315
 
311
316
  // Base: $5 input + $25 output = $30; fast: $30 * 6 = $180
312
317
  expect(result.pricingStatus).toBe("priced");
@@ -323,7 +328,11 @@ describe("fast mode pricing", () => {
323
328
  speed: "standard",
324
329
  };
325
330
 
326
- const result = resolvePricingForUsage("anthropic", "claude-opus-4-6", usage);
331
+ const result = resolvePricingForUsage(
332
+ "anthropic",
333
+ "claude-opus-4-6",
334
+ usage,
335
+ );
327
336
 
328
337
  expect(result.pricingStatus).toBe("priced");
329
338
  expect(result.estimatedCostUsd).toBe(5 + 25);
@@ -339,7 +348,11 @@ describe("fast mode pricing", () => {
339
348
  speed: null,
340
349
  };
341
350
 
342
- const result = resolvePricingForUsage("anthropic", "claude-opus-4-6", usage);
351
+ const result = resolvePricingForUsage(
352
+ "anthropic",
353
+ "claude-opus-4-6",
354
+ usage,
355
+ );
343
356
 
344
357
  expect(result.pricingStatus).toBe("priced");
345
358
  expect(result.estimatedCostUsd).toBe(5 + 25);
@@ -374,7 +387,9 @@ describe("fast mode pricing", () => {
374
387
  expect(fastResult.pricingStatus).toBe("priced");
375
388
  expect(standardResult.pricingStatus).toBe("priced");
376
389
  // Fast mode applies 6x to base rates; cache multipliers stack on top
377
- expect(fastResult.estimatedCostUsd).toBe(standardResult.estimatedCostUsd! * 6);
390
+ expect(fastResult.estimatedCostUsd).toBe(
391
+ standardResult.estimatedCostUsd! * 6,
392
+ );
378
393
  });
379
394
 
380
395
  test("does not apply fast mode multiplier for non-Anthropic providers", () => {
@@ -459,7 +474,7 @@ describe("Anthropic models on OpenRouter", () => {
459
474
  1_000_000,
460
475
  );
461
476
  expect(result.pricingStatus).toBe("priced");
462
- expect(result.estimatedCostUsd).toBe(0.8 + 4);
477
+ expect(result.estimatedCostUsd).toBe(1 + 5);
463
478
  });
464
479
 
465
480
  test("prices bare claude-opus-4-6 slug returned unprefixed", () => {
@@ -528,7 +543,7 @@ describe("Anthropic models on OpenRouter", () => {
528
543
  1_000_000,
529
544
  );
530
545
  expect(result.pricingStatus).toBe("priced");
531
- expect(result.estimatedCostUsd).toBe(0.8 + 4);
546
+ expect(result.estimatedCostUsd).toBe(1 + 5);
532
547
  });
533
548
 
534
549
  test("returns unpriced for unknown anthropic model on OpenRouter", () => {
@@ -542,13 +557,24 @@ describe("Anthropic models on OpenRouter", () => {
542
557
  expect(result.estimatedCostUsd).toBeNull();
543
558
  });
544
559
 
545
- test("returns unpriced for non-Anthropic OpenRouter model", () => {
560
+ test("prices non-Anthropic OpenRouter model from catalog", () => {
546
561
  const result = resolvePricing(
547
562
  "openrouter",
548
563
  "x-ai/grok-4.20-beta",
549
564
  1_000_000,
550
565
  1_000_000,
551
566
  );
567
+ expect(result.pricingStatus).toBe("priced");
568
+ expect(result.estimatedCostUsd).toBe(3 + 15);
569
+ });
570
+
571
+ test("returns unpriced for unknown non-Anthropic OpenRouter model", () => {
572
+ const result = resolvePricing(
573
+ "openrouter",
574
+ "unknown-provider/some-model",
575
+ 1_000_000,
576
+ 1_000_000,
577
+ );
552
578
  expect(result.pricingStatus).toBe("unpriced");
553
579
  expect(result.estimatedCostUsd).toBeNull();
554
580
  });
@@ -566,7 +592,11 @@ describe("Anthropic models on OpenRouter", () => {
566
592
  "anthropic/claude-opus-4.6",
567
593
  usage,
568
594
  );
569
- const direct = resolvePricingForUsage("anthropic", "claude-opus-4-6", usage);
595
+ const direct = resolvePricingForUsage(
596
+ "anthropic",
597
+ "claude-opus-4-6",
598
+ usage,
599
+ );
570
600
 
571
601
  // Cache-read tokens are charged at 10% of input rate for Anthropic models.
572
602
  expect(openRouter.pricingStatus).toBe("priced");
@@ -623,7 +653,9 @@ describe("Anthropic models on OpenRouter", () => {
623
653
 
624
654
  describe("usesAnthropicPricingRules", () => {
625
655
  test("returns true for direct Anthropic", () => {
626
- expect(usesAnthropicPricingRules("anthropic", "claude-opus-4-6")).toBe(true);
656
+ expect(usesAnthropicPricingRules("anthropic", "claude-opus-4-6")).toBe(
657
+ true,
658
+ );
627
659
  });
628
660
 
629
661
  test("returns true for anthropic/* on OpenRouter", () => {
@@ -133,13 +133,6 @@ describe("createProxyApprovalCallback", () => {
133
133
  test("returns true when user allows an ask_missing_credential request", async () => {
134
134
  const ctx = makeContext();
135
135
 
136
- const _resolvePrompt:
137
- | ((v: {
138
- decision: string;
139
- selectedPattern?: string;
140
- selectedScope?: string;
141
- }) => void)
142
- | null = null;
143
136
  const prompterSendToClient = mock(() => {});
144
137
  const prompter = new PermissionPrompter(prompterSendToClient);
145
138
 
@@ -381,7 +374,14 @@ describe("createProxyApprovalCallback", () => {
381
374
  // Verify the confirmation request uses the network_request tool name
382
375
  expect(msg.toolName).toBe("network_request");
383
376
  expect(msg.input).toHaveProperty("url", "https://api.fal.ai:443/v1/run");
384
- expect(msg.input).toHaveProperty("matching_patterns", ["*.fal.ai"]);
377
+ expect(msg.input).toHaveProperty("scheme", "https");
378
+ expect(msg.input).toHaveProperty("known_credential_patterns", [
379
+ "*.fal.ai",
380
+ ]);
381
+ expect(msg.input.reason).toMatch(
382
+ /A known credential template matches this host.*caller-supplied auth headers will still be sent/,
383
+ );
384
+ expect(msg.input).not.toHaveProperty("proxy_session_id");
385
385
  prompter.resolveConfirmation(msg.requestId, "allow");
386
386
  return p;
387
387
  };
@@ -390,6 +390,67 @@ describe("createProxyApprovalCallback", () => {
390
390
  await callback(makeAskMissingCredentialRequest());
391
391
  });
392
392
 
393
+ test("surfaces method and curated headers when the proxy has HTTP-level context", async () => {
394
+ const ctx = makeContext();
395
+ const prompterSendToClient = mock(() => {});
396
+ const prompter = new PermissionPrompter(prompterSendToClient);
397
+
398
+ const originalPrompt = prompter.prompt.bind(prompter);
399
+ prompter.prompt = async (...args) => {
400
+ const p = originalPrompt(...args);
401
+ await new Promise((r) => setTimeout(r, 10));
402
+ const call = (prompterSendToClient.mock.calls as unknown[][])[0];
403
+ const msg = call[0] as {
404
+ requestId: string;
405
+ input: Record<string, unknown>;
406
+ };
407
+ expect(msg.input).toHaveProperty("method", "POST");
408
+ expect(msg.input).toHaveProperty("request_headers", {
409
+ "content-type": "application/json",
410
+ "user-agent": "curl/8.5.0",
411
+ });
412
+ expect(msg.input).not.toHaveProperty("connection_detail_available");
413
+ prompter.resolveConfirmation(msg.requestId, "allow");
414
+ return p;
415
+ };
416
+
417
+ const callback = createProxyApprovalCallback(prompter, ctx);
418
+ await callback(
419
+ makeAskUnauthenticatedRequest({
420
+ method: "POST",
421
+ requestHeaders: {
422
+ "content-type": "application/json",
423
+ "user-agent": "curl/8.5.0",
424
+ },
425
+ }),
426
+ );
427
+ });
428
+
429
+ test("marks connection_detail_available=no for HTTPS CONNECT approvals", async () => {
430
+ const ctx = makeContext();
431
+ const prompterSendToClient = mock(() => {});
432
+ const prompter = new PermissionPrompter(prompterSendToClient);
433
+
434
+ const originalPrompt = prompter.prompt.bind(prompter);
435
+ prompter.prompt = async (...args) => {
436
+ const p = originalPrompt(...args);
437
+ await new Promise((r) => setTimeout(r, 10));
438
+ const call = (prompterSendToClient.mock.calls as unknown[][])[0];
439
+ const msg = call[0] as {
440
+ requestId: string;
441
+ input: Record<string, unknown>;
442
+ };
443
+ expect(msg.input).toHaveProperty("connection_detail_available", "no");
444
+ expect(msg.input).not.toHaveProperty("method");
445
+ expect(msg.input).not.toHaveProperty("request_headers");
446
+ prompter.resolveConfirmation(msg.requestId, "allow");
447
+ return p;
448
+ };
449
+
450
+ const callback = createProxyApprovalCallback(prompter, ctx);
451
+ await callback(makeAskUnauthenticatedRequest());
452
+ });
453
+
393
454
  test("sends correct tool name for ask_unauthenticated decisions", async () => {
394
455
  const ctx = makeContext();
395
456
  const prompterSendToClient = mock(() => {});
@@ -80,6 +80,7 @@ function resetState(): void {
80
80
  db.run("DELETE FROM conversations");
81
81
  db.run("DELETE FROM contact_channels");
82
82
  db.run("DELETE FROM contacts");
83
+ _clearApprovalPromptTsTrackerForTesting();
83
84
  }
84
85
 
85
86
  function seedActiveMember(): void {
@@ -54,6 +54,7 @@ mock.module("../memory/qdrant-client.js", () => ({
54
54
  getQdrantClient: () => {
55
55
  throw new Error("Qdrant not initialized");
56
56
  },
57
+ resolveQdrantUrl: () => "http://127.0.0.1:6333",
57
58
  }));
58
59
 
59
60
  import {
@@ -117,8 +117,6 @@ describe("tool manifest", () => {
117
117
  expect(names).toContain("recall");
118
118
  expect(names).toContain("remember");
119
119
  expect(names).toContain("credential_store");
120
- // start_screen_watch moved to the screen-watch bundled skill
121
- expect(names).not.toContain("start_screen_watch");
122
120
  });
123
121
 
124
122
  test("registered tool count is at least eager + host", async () => {