@vellumai/assistant 0.11.1 → 0.11.2-staging.2

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 (318) hide show
  1. package/Dockerfile +1 -3
  2. package/README.md +1 -1
  3. package/eslint.config.mjs +28 -8
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
  7. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  8. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
  9. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
  10. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  11. package/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
  12. package/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
  13. package/node_modules/@vellumai/slack-text/src/index.test.ts +48 -0
  14. package/node_modules/@vellumai/slack-text/src/index.ts +25 -6
  15. package/openapi.yaml +292 -5
  16. package/package.json +1 -1
  17. package/src/__tests__/agent-loop-resume-interrupted.test.ts +223 -0
  18. package/src/__tests__/byok-default-profile-ensure.test.ts +43 -0
  19. package/src/__tests__/cli-logger-boundary-guard.test.ts +87 -0
  20. package/src/__tests__/compaction-events.test.ts +139 -2
  21. package/src/__tests__/config-loader-backfill.test.ts +35 -3
  22. package/src/__tests__/config-schema.test.ts +1 -0
  23. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +30 -0
  24. package/src/__tests__/conversation-agent-loop.test.ts +180 -7
  25. package/src/__tests__/conversation-error.test.ts +211 -4
  26. package/src/__tests__/conversation-options-turn-scoped-transport.test.ts +64 -0
  27. package/src/__tests__/conversation-queue.test.ts +126 -0
  28. package/src/__tests__/conversation-retry-route.test.ts +46 -7
  29. package/src/__tests__/conversation-slash.test.ts +4 -2
  30. package/src/__tests__/conversation-summarize-route.test.ts +7 -1
  31. package/src/__tests__/conversation-summarize-up-to.test.ts +71 -5
  32. package/src/__tests__/document-create-dedupe.test.ts +2 -1
  33. package/src/__tests__/document-find-replace.test.ts +2 -1
  34. package/src/__tests__/document-tool-security.test.ts +2 -1
  35. package/src/__tests__/document-update-default-surface.test.ts +2 -1
  36. package/src/__tests__/document-workspace-file.test.ts +467 -0
  37. package/src/__tests__/emit-signal-routing-intent.test.ts +290 -49
  38. package/src/__tests__/guardian-card-withdrawal.test.ts +91 -3
  39. package/src/__tests__/http-user-message-parity.test.ts +66 -0
  40. package/src/__tests__/list-messages-attachments.test.ts +87 -0
  41. package/src/__tests__/list-messages-provider-error.test.ts +143 -0
  42. package/src/__tests__/list-messages-system-card.test.ts +100 -0
  43. package/src/__tests__/llm-resolver.test.ts +19 -9
  44. package/src/__tests__/managed-profile-guard.test.ts +23 -0
  45. package/src/__tests__/notification-platform-adapter.test.ts +130 -2
  46. package/src/__tests__/notification-telegram-adapter.test.ts +6 -0
  47. package/src/__tests__/notification-vellum-adapter.test.ts +45 -0
  48. package/src/__tests__/plugin-api-model-profiles.test.ts +10 -1
  49. package/src/__tests__/plugin-import-boundary-guard.test.ts +3 -0
  50. package/src/__tests__/provider-error-scenarios.test.ts +140 -0
  51. package/src/__tests__/provider-send-message-override-profile.test.ts +95 -0
  52. package/src/__tests__/run-conversation-turn-persistence.test.ts +66 -3
  53. package/src/__tests__/scripted-turn-metadata-persistence.test.ts +209 -0
  54. package/src/__tests__/skills.test.ts +27 -0
  55. package/src/__tests__/slack-channels-routes.test.ts +0 -2
  56. package/src/__tests__/slack-share-routes.test.ts +0 -3
  57. package/src/__tests__/slack-users-routes.test.ts +0 -2
  58. package/src/__tests__/subagent-tools.test.ts +11 -0
  59. package/src/__tests__/tool-preview-lifecycle.test.ts +58 -0
  60. package/src/__tests__/tool-result-spool.test.ts +5 -4
  61. package/src/__tests__/turn-boundary-resolution.test.ts +53 -0
  62. package/src/__tests__/turn-events-store.test.ts +26 -0
  63. package/src/__tests__/ui-visual-surface.test.ts +695 -0
  64. package/src/__tests__/unified-turn-context-visible-app.test.ts +99 -0
  65. package/src/__tests__/visible-app-context.test.ts +189 -0
  66. package/src/__tests__/workspace-git-service.test.ts +173 -33
  67. package/src/__tests__/workspace-migration-137-repair-retired-fireworks-minimax-model-id.test.ts +157 -0
  68. package/src/__tests__/workspace-migration-138-backfill-home-feed-titles.test.ts +373 -0
  69. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +137 -0
  70. package/src/agent/loop.ts +36 -7
  71. package/src/api/events/context-window-usage.ts +31 -0
  72. package/src/api/events/notification-intent.ts +8 -0
  73. package/src/api/events/ui-surface-pending.ts +35 -0
  74. package/src/api/index.ts +14 -0
  75. package/src/api/responses/conversation-message.ts +25 -4
  76. package/src/api/surfaces.ts +90 -1
  77. package/src/approvals/guardian-card-withdrawal.ts +66 -31
  78. package/src/approvals/guardian-decision-primitive.ts +4 -0
  79. package/src/calls/__tests__/call-setup-router.test.ts +156 -26
  80. package/src/calls/__tests__/voice-session-bridge.test.ts +201 -12
  81. package/src/calls/call-setup-router.ts +80 -36
  82. package/src/calls/voice-session-bridge.ts +173 -26
  83. package/src/cli/commands/inference.help.ts +3 -3
  84. package/src/cli/commands/notifications.help.ts +3 -2
  85. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +42 -128
  86. package/src/cli/commands/platform/__tests__/credits.test.ts +9 -78
  87. package/src/cli/commands/platform/__tests__/helpers.ts +90 -0
  88. package/src/cli/commands/platform/__tests__/invoices.test.ts +238 -0
  89. package/src/cli/commands/platform/__tests__/plans.test.ts +9 -88
  90. package/src/cli/commands/platform/__tests__/status.test.ts +12 -87
  91. package/src/cli/commands/platform/__tests__/subscription.test.ts +9 -86
  92. package/src/cli/commands/platform/index.help.ts +92 -0
  93. package/src/cli/commands/platform/index.ts +7 -0
  94. package/src/cli/commands/platform/invoices.ts +132 -0
  95. package/src/cli/commands/usage.help.ts +1 -1
  96. package/src/cli/lib/list-installed-plugins.ts +2 -1
  97. package/src/config/__tests__/default-profile-catalog.test.ts +7 -10
  98. package/src/config/__tests__/deployment-context-defaults.test.ts +27 -5
  99. package/src/config/assistant-feature-flags.ts +7 -2
  100. package/src/config/bundled-skills/app-builder/SKILL.md +3 -2
  101. package/src/config/bundled-skills/subagent/SKILL.md +3 -1
  102. package/src/config/bundled-skills/subagent/TOOLS.json +3 -3
  103. package/src/config/bundled-skills/visualize/SKILL.md +163 -0
  104. package/src/config/call-site-defaults.ts +3 -2
  105. package/src/config/default-profile-catalog.ts +75 -70
  106. package/src/config/default-profile-names.ts +13 -28
  107. package/src/config/env-registry.ts +1 -0
  108. package/src/config/feature-flag-registry.json +16 -0
  109. package/src/config/llm-resolver.ts +21 -5
  110. package/src/config/loader.ts +18 -11
  111. package/src/config/schemas/llm.ts +1 -9
  112. package/src/config/schemas/memory-retrospective.ts +9 -0
  113. package/src/config/schemas/monitoring.ts +28 -2
  114. package/src/config/schemas/workspace-git.ts +15 -0
  115. package/src/config/seed-inference-profiles.ts +19 -5
  116. package/src/context/post-turn-tool-result-truncation.ts +2 -2
  117. package/src/conversations/__tests__/message-consolidation.test.ts +54 -0
  118. package/src/conversations/message-consolidation.ts +17 -15
  119. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +182 -0
  120. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +181 -0
  121. package/src/daemon/conversation-agent-loop-handlers.ts +90 -7
  122. package/src/daemon/conversation-agent-loop.ts +107 -22
  123. package/src/daemon/conversation-error.ts +169 -48
  124. package/src/daemon/conversation-messaging.ts +87 -4
  125. package/src/daemon/conversation-process.ts +57 -45
  126. package/src/daemon/conversation-runtime-assembly.ts +57 -0
  127. package/src/daemon/conversation-store.ts +36 -1
  128. package/src/daemon/conversation-surfaces.ts +68 -6
  129. package/src/daemon/conversation-turn-finalize.ts +76 -18
  130. package/src/daemon/conversation.ts +122 -27
  131. package/src/daemon/lifecycle.ts +9 -0
  132. package/src/daemon/message-types/conversations.ts +8 -0
  133. package/src/daemon/message-types/surfaces.ts +3 -0
  134. package/src/documents/document-store.ts +247 -10
  135. package/src/home/__tests__/feed-types.test.ts +43 -8
  136. package/src/home/__tests__/feed-writer.test.ts +59 -0
  137. package/src/home/feed-types.ts +34 -7
  138. package/src/home/feed-writer.ts +15 -6
  139. package/src/live-voice/__tests__/activity-label.test.ts +95 -0
  140. package/src/live-voice/__tests__/live-activity-reporter.test.ts +86 -5
  141. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +310 -25
  142. package/src/live-voice/__tests__/live-voice-events.test.ts +4 -4
  143. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +8 -4
  144. package/src/live-voice/__tests__/live-voice-vad.test.ts +9 -1
  145. package/src/live-voice/activity-label.ts +169 -0
  146. package/src/live-voice/live-activity-reporter.ts +35 -6
  147. package/src/live-voice/live-voice-session.ts +282 -41
  148. package/src/live-voice/protocol.ts +35 -0
  149. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +84 -2
  150. package/src/messaging/providers/slack/adapter.ts +86 -19
  151. package/src/messaging/providers/slack/api.test.ts +85 -1
  152. package/src/messaging/providers/slack/api.ts +121 -288
  153. package/src/messaging/providers/slack/client.ts +20 -251
  154. package/src/messaging/providers/slack/send.test.ts +4 -9
  155. package/src/messaging/providers/slack/send.ts +1 -1
  156. package/src/messaging/providers/slack/types.ts +5 -0
  157. package/src/messaging/providers/slack/web-api-transport.test.ts +181 -0
  158. package/src/messaging/providers/slack/web-api-transport.ts +367 -0
  159. package/src/messaging/providers/slack/withdraw.ts +6 -14
  160. package/src/messaging/providers/telegram-bot/send.test.ts +31 -1
  161. package/src/messaging/providers/telegram-bot/send.ts +23 -2
  162. package/src/messaging/providers/telegram-bot/withdraw.test.ts +152 -0
  163. package/src/messaging/providers/telegram-bot/withdraw.ts +166 -0
  164. package/src/monitoring/__tests__/db-integrity-sample.test.ts +18 -4
  165. package/src/monitoring/__tests__/file-descriptors.test.ts +144 -0
  166. package/src/monitoring/file-descriptors.ts +262 -0
  167. package/src/monitoring/process-memory.ts +4 -11
  168. package/src/monitoring/resource-sampler.ts +5 -0
  169. package/src/monitoring/worker.ts +12 -0
  170. package/src/notifications/__tests__/assistant-reply-producer.test.ts +740 -0
  171. package/src/notifications/__tests__/broadcaster.test.ts +347 -7
  172. package/src/notifications/__tests__/copy-composer.test.ts +53 -3
  173. package/src/notifications/__tests__/decision-engine.test.ts +349 -29
  174. package/src/notifications/__tests__/deterministic-checks.test.ts +21 -0
  175. package/src/notifications/__tests__/edit-notification.test.ts +330 -0
  176. package/src/notifications/__tests__/guardian-delivery-recorder.test.ts +71 -0
  177. package/src/notifications/__tests__/home-feed-side-effect.test.ts +170 -12
  178. package/src/notifications/adapters/macos.ts +3 -0
  179. package/src/notifications/adapters/platform.ts +90 -14
  180. package/src/notifications/adapters/telegram.ts +6 -4
  181. package/src/notifications/assistant-reply-producer.ts +219 -0
  182. package/src/notifications/broadcaster.ts +523 -317
  183. package/src/notifications/copy-composer.ts +27 -6
  184. package/src/notifications/decision-engine.ts +154 -77
  185. package/src/notifications/deterministic-checks.ts +6 -7
  186. package/src/notifications/edit-notification.ts +8 -4
  187. package/src/notifications/emit-signal.ts +49 -16
  188. package/src/notifications/guardian-delivery-recorder.ts +8 -2
  189. package/src/notifications/home-feed-side-effect.ts +43 -6
  190. package/src/notifications/notification-utils.ts +39 -4
  191. package/src/notifications/signal.ts +10 -0
  192. package/src/notifications/types.ts +17 -0
  193. package/src/persistence/bookmark-crud.ts +18 -9
  194. package/src/persistence/conversation-attention-store.ts +26 -0
  195. package/src/persistence/conversation-crud.ts +79 -32
  196. package/src/persistence/conversation-queries.ts +30 -15
  197. package/src/persistence/conversation-title-service.ts +5 -170
  198. package/src/persistence/conversation-types.ts +192 -1
  199. package/src/persistence/db-init.ts +14 -1
  200. package/src/persistence/db-maintenance.ts +5 -4
  201. package/src/persistence/embeddings/__tests__/plugin-index-qdrant-init.test.ts +219 -0
  202. package/src/persistence/embeddings/__tests__/plugin-index.test.ts +22 -0
  203. package/src/persistence/embeddings/__tests__/worker-script-version.test.ts +76 -0
  204. package/src/persistence/embeddings/embedding-local.ts +2 -1
  205. package/src/persistence/embeddings/embedding-runtime-manager.ts +29 -5
  206. package/src/persistence/embeddings/plugin-index.ts +84 -12
  207. package/src/persistence/migrations/360-add-document-workspace-path.test.ts +110 -0
  208. package/src/persistence/migrations/360-add-document-workspace-path.ts +39 -0
  209. package/src/persistence/planner-statistics.ts +14 -0
  210. package/src/persistence/schema/documents.ts +26 -11
  211. package/src/persistence/steps.ts +2 -0
  212. package/src/platform/client.test.ts +172 -2
  213. package/src/platform/client.ts +178 -55
  214. package/src/plugin-api/conversation-turn.ts +13 -0
  215. package/src/plugin-api/model-profiles.test.ts +6 -2
  216. package/src/plugins/defaults/memory/__tests__/bookmark-crud.test.ts +34 -0
  217. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +22 -0
  218. package/src/plugins/defaults/memory/__tests__/jobs-store-enqueue-gate.test.ts +11 -0
  219. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +199 -0
  220. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +98 -1
  221. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +105 -1
  222. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +24 -2
  223. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +524 -0
  224. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +1 -1
  225. package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +9 -4
  226. package/src/plugins/defaults/memory/host-utils.ts +5 -0
  227. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +105 -1
  228. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +64 -5
  229. package/src/plugins/defaults/memory/memory-retrospective-job.ts +36 -1
  230. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +12 -3
  231. package/src/plugins/defaults/memory/src/__tests__/memory-v2-simulate-route.test.ts +1 -0
  232. package/src/plugins/defaults/memory/substrate/__tests__/skill-store.test.ts +195 -1
  233. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +1 -1
  234. package/src/plugins/defaults/memory/substrate/skill-store.ts +167 -29
  235. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +131 -0
  236. package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +1 -0
  237. package/src/plugins/defaults/memory/v2/activation-log-store.ts +4 -0
  238. package/src/plugins/defaults/memory/v2/injection.ts +48 -3
  239. package/src/plugins/defaults/memory/v2/rerank-local.ts +6 -2
  240. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +1 -1
  241. package/src/plugins/defaults/platform-hosted/routes/reengage.ts +1 -1
  242. package/src/plugins/defaults/turn-context/injectors.ts +1 -0
  243. package/src/plugins/defaults/turn-context/unified-turn-context.ts +26 -0
  244. package/src/plugins/types.ts +17 -0
  245. package/src/prompts/templates/system-sections.ts +2 -2
  246. package/src/providers/__tests__/dispatch-connection-routing.test.ts +43 -2
  247. package/src/providers/__tests__/vellum-mismatch-routing.test.ts +8 -0
  248. package/src/providers/call-site-routing.ts +77 -14
  249. package/src/providers/connection-resolution.ts +42 -2
  250. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +127 -1
  251. package/src/providers/inference/adapter-factory.ts +70 -12
  252. package/src/providers/model-catalog.ts +0 -11
  253. package/src/providers/model-intents.ts +11 -0
  254. package/src/providers/registry.ts +8 -0
  255. package/src/providers/retry.ts +85 -17
  256. package/src/providers/types.ts +8 -1
  257. package/src/runtime/__tests__/agent-wake.test.ts +181 -0
  258. package/src/runtime/agent-wake.ts +132 -9
  259. package/src/runtime/channel-approval-types.ts +25 -0
  260. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +2 -2
  261. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +11 -0
  262. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +121 -9
  263. package/src/runtime/routes/__tests__/platform-invoice-routes.test.ts +392 -0
  264. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +7 -0
  265. package/src/runtime/routes/__tests__/workspace-commit-routes.test.ts +63 -0
  266. package/src/runtime/routes/consolidation-routes.ts +1 -1
  267. package/src/runtime/routes/conversation-management-routes.ts +18 -6
  268. package/src/runtime/routes/conversation-query-routes.ts +14 -23
  269. package/src/runtime/routes/conversation-routes.ts +113 -8
  270. package/src/runtime/routes/credential-routes.ts +1 -5
  271. package/src/runtime/routes/documents-routes.ts +207 -12
  272. package/src/runtime/routes/identity-routes.ts +3 -93
  273. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +31 -1
  274. package/src/runtime/routes/inbound-stages/admission-policy.ts +18 -0
  275. package/src/runtime/routes/inference-provider-connection-routes.ts +52 -3
  276. package/src/runtime/routes/notification-routes.ts +4 -1
  277. package/src/runtime/routes/platform-routes.ts +238 -3
  278. package/src/runtime/routes/playground/guard.ts +1 -2
  279. package/src/runtime/routes/slack-channel-routes.ts +2 -4
  280. package/src/runtime/routes/workspace-commit-routes.ts +49 -2
  281. package/src/runtime/routes/workspace-routes.ts +11 -12
  282. package/src/runtime/routes/workspace-utils.ts +49 -2
  283. package/src/runtime/services/conversation-serializer.ts +2 -4
  284. package/src/subagent/__tests__/consult-context-gating.test.ts +98 -0
  285. package/src/subagent/__tests__/consult-context-skills.test.ts +82 -0
  286. package/src/subagent/__tests__/consult-context.test.ts +84 -0
  287. package/src/subagent/__tests__/consult-prompt.test.ts +36 -0
  288. package/src/subagent/consult-context.ts +410 -0
  289. package/src/subagent/consult-prompt.ts +38 -5
  290. package/src/subagent/manager.ts +9 -4
  291. package/src/subagent/types.ts +8 -0
  292. package/src/telemetry/telemetry-event-sources.ts +7 -0
  293. package/src/telemetry/telemetry-wire-source.json +1 -1
  294. package/src/telemetry/telemetry-wire.generated.ts +1 -0
  295. package/src/telemetry/turn-events-store.ts +30 -1
  296. package/src/telemetry/types.ts +24 -0
  297. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  298. package/src/tools/browser/pinned-tabs.ts +3 -1
  299. package/src/tools/subagent/spawn.ts +23 -1
  300. package/src/tools/terminal/safe-env.ts +1 -0
  301. package/src/tools/ui-surface/definitions.ts +39 -1
  302. package/src/tools/ui-surface/surface-shape-docs.ts +38 -4
  303. package/src/tools/ui-surface/visual-validation.ts +787 -0
  304. package/src/tools/workflows/run-workflow.test.ts +1 -0
  305. package/src/util/__tests__/short-title.test.ts +229 -0
  306. package/src/util/__tests__/worker-compute.test.ts +65 -0
  307. package/src/util/cgroup-cpu.ts +93 -0
  308. package/src/util/errors.ts +27 -0
  309. package/src/util/process-tree.ts +19 -0
  310. package/src/util/short-title.ts +189 -0
  311. package/src/util/worker-compute.ts +83 -0
  312. package/src/workspace/byok-default-profile-ensure.ts +25 -15
  313. package/src/workspace/git-service.ts +79 -30
  314. package/src/workspace/migrations/137-repair-retired-fireworks-minimax-model-id.ts +131 -0
  315. package/src/workspace/migrations/138-backfill-home-feed-titles.ts +179 -0
  316. package/src/workspace/migrations/139-clear-renamed-cost-profile-label.ts +92 -0
  317. package/src/workspace/migrations/registry.ts +6 -0
  318. package/src/plugins/defaults/memory/substrate/constants.ts +0 -8
@@ -1,79 +1,13 @@
1
- import { beforeEach, describe, expect, mock, test } from "bun:test";
1
+ import { beforeEach, describe, expect, test } from "bun:test";
2
2
 
3
- import { Command } from "commander";
3
+ import { runPlatform, setupPlatformIpcMock } from "./helpers.js";
4
4
 
5
- // ---------------------------------------------------------------------------
6
- // Mock state
7
- // ---------------------------------------------------------------------------
8
-
9
- let mockCalls: Array<[string, Record<string, unknown>]> = [];
10
- let mockResponse: unknown = {
11
- ok: true,
12
- result: {
13
- plans: [
14
- {
15
- id: "base",
16
- name: "Base",
17
- price_cents: 0,
18
- billing_interval: "month",
19
- included_features: ["Pay-as-you-go credits"],
20
- },
21
- {
22
- id: "pro",
23
- name: "Pro",
24
- base_price_cents: 2000,
25
- billing_interval: "month",
26
- included_features: ["Configurable machine size"],
27
- },
28
- ],
29
- },
30
- };
31
-
32
- // ---------------------------------------------------------------------------
33
- // Mocks
34
- // ---------------------------------------------------------------------------
35
-
36
- mock.module("../../../../ipc/cli-client.js", () => ({
37
- cliIpcCall: async (method: string, params: Record<string, unknown>) => {
38
- mockCalls.push([method, params]);
39
- return mockResponse;
40
- },
41
- exitFromIpcResult: (_r: unknown, _cmd: unknown) => {
42
- throw new Error("exitFromIpcResult called");
43
- },
44
- }));
45
-
46
- const { registerPlatformCommand } = await import("../index.js");
47
-
48
- function buildProgram(): Command {
49
- const program = new Command();
50
- program.exitOverride();
51
- registerPlatformCommand(program);
52
- return program;
53
- }
54
-
55
- function captureStdout(fn: () => Promise<void>): Promise<string[]> {
56
- const chunks: string[] = [];
57
- const origWrite = process.stdout.write.bind(process.stdout);
58
- process.stdout.write = ((chunk: unknown) => {
59
- chunks.push(typeof chunk === "string" ? chunk : String(chunk));
60
- return true;
61
- }) as typeof process.stdout.write;
62
- return fn()
63
- .then(() => chunks)
64
- .finally(() => {
65
- process.stdout.write = origWrite;
66
- });
67
- }
68
-
69
- // ---------------------------------------------------------------------------
70
- // Tests
71
- // ---------------------------------------------------------------------------
5
+ const ipc = setupPlatformIpcMock();
72
6
 
73
7
  describe("assistant platform plans", () => {
74
8
  beforeEach(() => {
75
- mockCalls = [];
76
- mockResponse = {
9
+ ipc.calls = [];
10
+ ipc.response = {
77
11
  ok: true,
78
12
  result: {
79
13
  plans: [
@@ -94,22 +28,12 @@ describe("assistant platform plans", () => {
94
28
  ],
95
29
  },
96
30
  };
97
- process.exitCode = 0;
98
31
  });
99
32
 
100
33
  test("calls platform_plans and emits catalog JSON with --json", async () => {
101
- const out = await captureStdout(async () => {
102
- const program = buildProgram();
103
- await program.parseAsync([
104
- "node",
105
- "assistant",
106
- "platform",
107
- "plans",
108
- "--json",
109
- ]);
110
- });
34
+ const out = await runPlatform(["plans", "--json"]);
111
35
 
112
- expect(mockCalls[0][0]).toBe("platform_plans");
36
+ expect(ipc.calls[0][0]).toBe("platform_plans");
113
37
 
114
38
  const parsed = JSON.parse(out.join(""));
115
39
  expect(parsed.plans).toHaveLength(2);
@@ -118,12 +42,9 @@ describe("assistant platform plans", () => {
118
42
  });
119
43
 
120
44
  test("plain text mode does not emit JSON to stdout", async () => {
121
- const out = await captureStdout(async () => {
122
- const program = buildProgram();
123
- await program.parseAsync(["node", "assistant", "platform", "plans"]);
124
- });
45
+ const out = await runPlatform(["plans"]);
125
46
 
126
- // Plain-text mode logs via log.info verify writeOutput (JSON) was NOT called
47
+ // Plain-text mode logs via log.info; verify writeOutput (JSON) was NOT called
127
48
  expect(() => JSON.parse(out.join("").trim())).toThrow();
128
49
  });
129
50
  });
@@ -1,57 +1,13 @@
1
- import { beforeEach, describe, expect, mock, test } from "bun:test";
1
+ import { beforeEach, describe, expect, test } from "bun:test";
2
2
 
3
- import { Command } from "commander";
3
+ import { runPlatform, setupPlatformIpcMock } from "./helpers.js";
4
4
 
5
- // ---------------------------------------------------------------------------
6
- // Mock state
7
- // ---------------------------------------------------------------------------
8
-
9
- let mockCalls: Array<[string, Record<string, unknown>]> = [];
10
- let mockResponse: unknown = {
11
- ok: true,
12
- result: {
13
- isPlatform: false,
14
- baseUrl: "",
15
- assistantId: "",
16
- hasAssistantApiKey: false,
17
- hasWebhookSecret: false,
18
- available: false,
19
- organizationId: null,
20
- userId: null,
21
- },
22
- };
23
-
24
- // ---------------------------------------------------------------------------
25
- // Mocks
26
- // ---------------------------------------------------------------------------
27
-
28
- mock.module("../../../../ipc/cli-client.js", () => ({
29
- cliIpcCall: async (method: string, params: Record<string, unknown>) => {
30
- mockCalls.push([method, params]);
31
- return mockResponse;
32
- },
33
- exitFromIpcResult: (_r: unknown, _cmd: unknown) => {
34
- throw new Error("exitFromIpcResult called");
35
- },
36
- }));
37
-
38
- const { registerPlatformCommand } = await import("../index.js");
39
-
40
- function buildProgram(): Command {
41
- const program = new Command();
42
- program.exitOverride();
43
- registerPlatformCommand(program);
44
- return program;
45
- }
46
-
47
- // ---------------------------------------------------------------------------
48
- // Tests
49
- // ---------------------------------------------------------------------------
5
+ const ipc = setupPlatformIpcMock();
50
6
 
51
7
  describe("assistant platform status", () => {
52
8
  beforeEach(() => {
53
- mockCalls = [];
54
- mockResponse = {
9
+ ipc.calls = [];
10
+ ipc.response = {
55
11
  ok: true,
56
12
  result: {
57
13
  isPlatform: false,
@@ -64,11 +20,10 @@ describe("assistant platform status", () => {
64
20
  userId: null,
65
21
  },
66
22
  };
67
- process.exitCode = 0;
68
23
  });
69
24
 
70
25
  test("platform pod returns full status from context", async () => {
71
- mockResponse = {
26
+ ipc.response = {
72
27
  ok: true,
73
28
  result: {
74
29
  isPlatform: true,
@@ -82,29 +37,11 @@ describe("assistant platform status", () => {
82
37
  },
83
38
  };
84
39
 
85
- const program = buildProgram();
86
- const stdoutChunks: string[] = [];
87
- const origWrite = process.stdout.write.bind(process.stdout);
88
- process.stdout.write = ((chunk: unknown) => {
89
- stdoutChunks.push(typeof chunk === "string" ? chunk : String(chunk));
90
- return true;
91
- }) as typeof process.stdout.write;
40
+ const out = await runPlatform(["status", "--json"]);
92
41
 
93
- try {
94
- await program.parseAsync([
95
- "node",
96
- "assistant",
97
- "platform",
98
- "status",
99
- "--json",
100
- ]);
101
- } finally {
102
- process.stdout.write = origWrite;
103
- }
42
+ expect(ipc.calls[0][0]).toBe("platform_status");
104
43
 
105
- expect(mockCalls[0][0]).toBe("platform_status");
106
-
107
- const parsed = JSON.parse(stdoutChunks.join(""));
44
+ const parsed = JSON.parse(out.join(""));
108
45
  expect(parsed.isPlatform).toBe(true);
109
46
  expect(parsed.baseUrl).toBe("https://platform.vellum.ai");
110
47
  expect(parsed.assistantId).toBe("asst-abc-123");
@@ -116,21 +53,9 @@ describe("assistant platform status", () => {
116
53
  });
117
54
 
118
55
  test("plain text mode does not emit JSON to stdout", async () => {
119
- const program = buildProgram();
120
- const stdoutChunks: string[] = [];
121
- const origWrite = process.stdout.write.bind(process.stdout);
122
- process.stdout.write = ((chunk: unknown) => {
123
- stdoutChunks.push(typeof chunk === "string" ? chunk : String(chunk));
124
- return true;
125
- }) as typeof process.stdout.write;
126
-
127
- try {
128
- await program.parseAsync(["node", "assistant", "platform", "status"]);
129
- } finally {
130
- process.stdout.write = origWrite;
131
- }
56
+ const out = await runPlatform(["status"]);
132
57
 
133
- // Plain-text mode logs via log.info verify writeOutput (JSON) was NOT called
134
- expect(() => JSON.parse(stdoutChunks.join("").trim())).toThrow();
58
+ // Plain-text mode logs via log.info; verify writeOutput (JSON) was NOT called
59
+ expect(() => JSON.parse(out.join("").trim())).toThrow();
135
60
  });
136
61
  });
@@ -1,72 +1,13 @@
1
- import { beforeEach, describe, expect, mock, test } from "bun:test";
1
+ import { beforeEach, describe, expect, test } from "bun:test";
2
2
 
3
- import { Command } from "commander";
3
+ import { runPlatform, setupPlatformIpcMock } from "./helpers.js";
4
4
 
5
- // ---------------------------------------------------------------------------
6
- // Mock state
7
- // ---------------------------------------------------------------------------
8
-
9
- let mockCalls: Array<[string, Record<string, unknown>]> = [];
10
- let mockResponse: unknown = {
11
- ok: true,
12
- result: {
13
- planId: "pro",
14
- status: "active",
15
- renewalDate: "2026-08-01T00:00:00.000Z",
16
- currentPeriodEnd: "2026-08-01T00:00:00.000Z",
17
- cancelAtPeriodEnd: false,
18
- cancelAt: null,
19
- selectedCreditTier: "credits_50",
20
- package: { key: "super", name: "Super", version: 2, customized: false },
21
- entitlements: { managedEmail: true, phoneNumber: false },
22
- },
23
- };
24
-
25
- // ---------------------------------------------------------------------------
26
- // Mocks
27
- // ---------------------------------------------------------------------------
28
-
29
- mock.module("../../../../ipc/cli-client.js", () => ({
30
- cliIpcCall: async (method: string, params: Record<string, unknown>) => {
31
- mockCalls.push([method, params]);
32
- return mockResponse;
33
- },
34
- exitFromIpcResult: (_r: unknown, _cmd: unknown) => {
35
- throw new Error("exitFromIpcResult called");
36
- },
37
- }));
38
-
39
- const { registerPlatformCommand } = await import("../index.js");
40
-
41
- function buildProgram(): Command {
42
- const program = new Command();
43
- program.exitOverride();
44
- registerPlatformCommand(program);
45
- return program;
46
- }
47
-
48
- function captureStdout(fn: () => Promise<void>): Promise<string[]> {
49
- const chunks: string[] = [];
50
- const origWrite = process.stdout.write.bind(process.stdout);
51
- process.stdout.write = ((chunk: unknown) => {
52
- chunks.push(typeof chunk === "string" ? chunk : String(chunk));
53
- return true;
54
- }) as typeof process.stdout.write;
55
- return fn()
56
- .then(() => chunks)
57
- .finally(() => {
58
- process.stdout.write = origWrite;
59
- });
60
- }
61
-
62
- // ---------------------------------------------------------------------------
63
- // Tests
64
- // ---------------------------------------------------------------------------
5
+ const ipc = setupPlatformIpcMock();
65
6
 
66
7
  describe("assistant platform subscription", () => {
67
8
  beforeEach(() => {
68
- mockCalls = [];
69
- mockResponse = {
9
+ ipc.calls = [];
10
+ ipc.response = {
70
11
  ok: true,
71
12
  result: {
72
13
  planId: "pro",
@@ -80,22 +21,12 @@ describe("assistant platform subscription", () => {
80
21
  entitlements: { managedEmail: true, phoneNumber: false },
81
22
  },
82
23
  };
83
- process.exitCode = 0;
84
24
  });
85
25
 
86
26
  test("calls platform_subscription and emits plan JSON with --json", async () => {
87
- const out = await captureStdout(async () => {
88
- const program = buildProgram();
89
- await program.parseAsync([
90
- "node",
91
- "assistant",
92
- "platform",
93
- "subscription",
94
- "--json",
95
- ]);
96
- });
27
+ const out = await runPlatform(["subscription", "--json"]);
97
28
 
98
- expect(mockCalls[0][0]).toBe("platform_subscription");
29
+ expect(ipc.calls[0][0]).toBe("platform_subscription");
99
30
 
100
31
  const parsed = JSON.parse(out.join(""));
101
32
  expect(parsed.planId).toBe("pro");
@@ -105,17 +36,9 @@ describe("assistant platform subscription", () => {
105
36
  });
106
37
 
107
38
  test("plain text mode does not emit JSON to stdout", async () => {
108
- const out = await captureStdout(async () => {
109
- const program = buildProgram();
110
- await program.parseAsync([
111
- "node",
112
- "assistant",
113
- "platform",
114
- "subscription",
115
- ]);
116
- });
39
+ const out = await runPlatform(["subscription"]);
117
40
 
118
- // Plain-text mode logs via log.info verify writeOutput (JSON) was NOT called
41
+ // Plain-text mode logs via log.info; verify writeOutput (JSON) was NOT called
119
42
  expect(() => JSON.parse(out.join("").trim())).toThrow();
120
43
  });
121
44
  });
@@ -23,6 +23,7 @@ Examples:
23
23
  $ assistant platform credits --json
24
24
  $ assistant platform subscription --json
25
25
  $ assistant platform plans --json
26
+ $ assistant platform invoices list --json
26
27
  $ assistant platform connect
27
28
  $ assistant platform disconnect
28
29
  $ assistant platform callback-routes register --path webhooks/telegram --type telegram --json`,
@@ -145,6 +146,97 @@ Examples:
145
146
  $ assistant platform plans
146
147
  $ assistant platform plans --json`,
147
148
  },
149
+ {
150
+ name: "invoices",
151
+ description: "Read the organization's Stripe invoice history",
152
+ helpText: `
153
+ Invoices are read from the platform's billing invoices endpoint using this
154
+ assistant's platform API key. Amounts are Stripe-scaled integers; see
155
+ 'assistant platform invoices list --help' for the scaling rules. 'created'
156
+ is a Unix timestamp in seconds.
157
+
158
+ The platform returns one page of invoices at a time (newest first). When
159
+ the response's 'has_more' is true, pass the last invoice's id via
160
+ --starting-after to fetch the next (older) page.
161
+
162
+ Requires platform credentials (run 'assistant platform connect' first or
163
+ ensure VELLUM_PLATFORM_URL is set and credentials are stored).
164
+
165
+ Examples:
166
+ $ assistant platform invoices list --json
167
+ $ assistant platform invoices list --starting-after in_1AbCdEfGh
168
+ $ assistant platform invoices get in_1AbCdEfGh --json`,
169
+ subcommands: [
170
+ {
171
+ name: "list",
172
+ description:
173
+ "List one page of the organization's Stripe invoices, newest first",
174
+ options: [
175
+ {
176
+ flags: "--starting-after <invoice-id>",
177
+ description:
178
+ "Cursor: return invoices older than this invoice id (use the last id from the previous page)",
179
+ },
180
+ ],
181
+ helpText: `
182
+ Fetches one page of the org's Stripe invoice history from the platform,
183
+ newest first.
184
+
185
+ Fields:
186
+ invoices One page of invoices, each with the fields below
187
+ id Stripe invoice ID; pass it to 'assistant platform
188
+ invoices get' or to --starting-after
189
+ number Human-readable invoice number, or null
190
+ status Stripe status (draft, open, paid, uncollectible,
191
+ void), or null
192
+ currency Lowercase ISO currency code (e.g. usd)
193
+ amount_due Amount due as a Stripe-scaled integer: divide by
194
+ 100 for most currencies, by 1 for Stripe
195
+ zero-decimal currencies (e.g. JPY, KRW), and by
196
+ 1000 for three-decimal ones (e.g. BHD); ISK, HUF,
197
+ TWD, and UGX are two-decimal for charges and
198
+ invoices (Stripe treats them as zero-decimal
199
+ only for payouts)
200
+ amount_paid Amount paid, same Stripe scaling as amount_due
201
+ amount_remaining Amount still owed, same Stripe scaling as amount_due
202
+ created Creation time as a Unix timestamp in seconds
203
+ hosted_invoice_url Link to the hosted Stripe invoice page, or null
204
+ invoice_pdf Link to the invoice PDF, or null
205
+ has_more True when older invoices exist beyond this page;
206
+ fetch them with --starting-after
207
+
208
+ Requires platform credentials (run 'assistant platform connect' first or
209
+ ensure VELLUM_PLATFORM_URL is set and credentials are stored).
210
+
211
+ Examples:
212
+ $ assistant platform invoices list --json
213
+ $ assistant platform invoices list --starting-after in_1AbCdEfGh --json`,
214
+ },
215
+ {
216
+ name: "get",
217
+ description: "Show a single Stripe invoice by ID",
218
+ arguments: [
219
+ {
220
+ name: "<invoice-id>",
221
+ description:
222
+ "Stripe invoice ID (e.g. in_1AbCdEfGh); run 'assistant platform invoices list' to find it",
223
+ },
224
+ ],
225
+ helpText: `
226
+ The platform has no per-invoice endpoint, so the assistant pages through
227
+ the org's invoice list (newest first) until the ID matches. The lookup
228
+ searches the most recent 2,500 invoices (25 pages) and errors if the ID
229
+ is not found within that range.
230
+
231
+ Requires platform credentials (run 'assistant platform connect' first or
232
+ ensure VELLUM_PLATFORM_URL is set and credentials are stored).
233
+
234
+ Examples:
235
+ $ assistant platform invoices get in_1AbCdEfGh
236
+ $ assistant platform invoices get in_1AbCdEfGh --json`,
237
+ },
238
+ ],
239
+ },
148
240
  {
149
241
  name: "disconnect",
150
242
  description:
@@ -14,6 +14,7 @@ import { shouldOutputJson, writeOutput } from "../../output.js";
14
14
  import { registerPlatformConnectCommand } from "./connect.js";
15
15
  import { registerPlatformDisconnectCommand } from "./disconnect.js";
16
16
  import { platformHelp } from "./index.help.js";
17
+ import { registerPlatformInvoicesCommands } from "./invoices.js";
17
18
 
18
19
  interface PlatformStatusResult {
19
20
  isPlatform: boolean;
@@ -236,6 +237,12 @@ export function registerPlatformCommand(program: Command): void {
236
237
  },
237
238
  );
238
239
 
240
+ // -----------------------------------------------------------------------
241
+ // invoices
242
+ // -----------------------------------------------------------------------
243
+
244
+ registerPlatformInvoicesCommands(platform);
245
+
239
246
  // -----------------------------------------------------------------------
240
247
  // disconnect
241
248
  // -----------------------------------------------------------------------
@@ -0,0 +1,132 @@
1
+ import { stripeScaleDigits } from "@vellumai/service-contracts/stripe-currency";
2
+ import type { Command } from "commander";
3
+
4
+ import { cliIpcCall, exitFromIpcResult } from "../../../ipc/cli-client.js";
5
+ import { subcommand } from "../../lib/cli-command-help.js";
6
+ import { log } from "../../logger.js";
7
+ import { shouldOutputJson, writeOutput } from "../../output.js";
8
+
9
+ interface PlatformInvoice {
10
+ id: string;
11
+ number: string | null;
12
+ status: string | null;
13
+ currency: string;
14
+ amount_due: number;
15
+ amount_paid: number;
16
+ amount_remaining: number;
17
+ created: number;
18
+ hosted_invoice_url: string | null;
19
+ invoice_pdf: string | null;
20
+ }
21
+
22
+ interface PlatformInvoicesListResult {
23
+ invoices: PlatformInvoice[];
24
+ has_more: boolean;
25
+ }
26
+
27
+ /**
28
+ * Render a Stripe minor-unit amount as major units, e.g. "USD 12.34". The
29
+ * display fraction digits are forced to match stripeScaleDigits so Intl's
30
+ * ISO metadata cannot apply a different scale (see the currency sets in
31
+ * @vellumai/service-contracts/stripe-currency). Unknown currency codes fall
32
+ * back to the raw minor-unit amount.
33
+ */
34
+ function formatInvoiceAmount(
35
+ amountMinorUnits: number,
36
+ currency: string,
37
+ ): string {
38
+ const code = currency.toUpperCase();
39
+ const digits = stripeScaleDigits(code);
40
+ try {
41
+ const formatter = new Intl.NumberFormat("en-US", {
42
+ style: "currency",
43
+ currency: code,
44
+ currencyDisplay: "code",
45
+ minimumFractionDigits: digits,
46
+ maximumFractionDigits: digits,
47
+ });
48
+ // Intl separates the code and number with U+00A0; use a plain space.
49
+ return formatter
50
+ .format(amountMinorUnits / 10 ** digits)
51
+ .replace(/\u00a0/g, " ");
52
+ } catch {
53
+ // Intl.NumberFormat throws a RangeError on invalid currency codes.
54
+ return `${amountMinorUnits} ${code} (minor units)`;
55
+ }
56
+ }
57
+
58
+ function logInvoice(invoice: PlatformInvoice, includePdf: boolean): void {
59
+ log.info(invoice.number ?? invoice.id);
60
+ log.info(` Status: ${invoice.status ?? "unknown"}`);
61
+ log.info(
62
+ ` Amount: ${formatInvoiceAmount(invoice.amount_due, invoice.currency)}`,
63
+ );
64
+ log.info(
65
+ ` Created: ${new Date(invoice.created * 1000).toISOString().slice(0, 10)}`,
66
+ );
67
+ if (invoice.hosted_invoice_url) {
68
+ log.info(` URL: ${invoice.hosted_invoice_url}`);
69
+ }
70
+ if (includePdf && invoice.invoice_pdf) {
71
+ log.info(` PDF: ${invoice.invoice_pdf}`);
72
+ }
73
+ }
74
+
75
+ export function registerPlatformInvoicesCommands(platform: Command): void {
76
+ const invoices = subcommand(platform, "invoices");
77
+
78
+ subcommand(invoices, "list").action(
79
+ async (opts: { startingAfter?: string }, cmd: Command) => {
80
+ const r = await cliIpcCall<PlatformInvoicesListResult>(
81
+ "platform_invoices_list",
82
+ opts.startingAfter
83
+ ? { queryParams: { starting_after: opts.startingAfter } }
84
+ : {},
85
+ );
86
+ if (!r.ok) {
87
+ return exitFromIpcResult(r, cmd);
88
+ }
89
+
90
+ const result = r.result!;
91
+
92
+ if (shouldOutputJson(cmd)) {
93
+ writeOutput(cmd, result);
94
+ } else {
95
+ if (result.invoices.length === 0) {
96
+ log.info("No invoices found.");
97
+ } else {
98
+ for (const invoice of result.invoices) {
99
+ logInvoice(invoice, false);
100
+ log.info("");
101
+ }
102
+ }
103
+ const lastId = result.invoices.at(-1)?.id;
104
+ if (result.has_more && lastId) {
105
+ log.info(
106
+ `More invoices available. Run 'assistant platform invoices list --starting-after ${lastId}' for the next page.`,
107
+ );
108
+ }
109
+ }
110
+ },
111
+ );
112
+
113
+ subcommand(invoices, "get").action(
114
+ async (invoiceId: string, _opts: Record<string, unknown>, cmd: Command) => {
115
+ const r = await cliIpcCall<PlatformInvoice>(
116
+ "platform_invoices_by_id_get",
117
+ {
118
+ pathParams: { id: invoiceId },
119
+ },
120
+ );
121
+ if (!r.ok) {
122
+ return exitFromIpcResult(r, cmd);
123
+ }
124
+
125
+ if (shouldOutputJson(cmd)) {
126
+ writeOutput(cmd, r.result!);
127
+ } else {
128
+ logInvoice(r.result!, true);
129
+ }
130
+ },
131
+ );
132
+ }
@@ -125,7 +125,7 @@ Grouping dimensions:
125
125
  inference_profile Groups by inference profile; unset historical rows are
126
126
  shown as Default / Unset
127
127
  provider Groups by LLM provider (anthropic, openai, etc.)
128
- model Groups by model name (claude-sonnet-4-20250514, etc.)
128
+ model Groups by model name (claude-sonnet-5, etc.)
129
129
  conversation Groups by conversation ID
130
130
  actor Legacy/internal subsystem grouping (main_agent, etc.)
131
131
 
@@ -14,6 +14,7 @@
14
14
 
15
15
  import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
16
16
  import { dirname, join } from "node:path";
17
+ import { fileURLToPath } from "node:url";
17
18
 
18
19
  import { getWorkspacePluginsDir } from "../../util/platform.js";
19
20
  import { parsePluginIcon } from "./plugin-artifact.js";
@@ -27,7 +28,7 @@ import { readValidatedPluginIcon } from "./plugin-icon-file.js";
27
28
  * dependencies in test environments).
28
29
  */
29
30
  const DEFAULT_PLUGINS_DIR = join(
30
- dirname(new URL(import.meta.url).pathname),
31
+ dirname(fileURLToPath(import.meta.url)),
31
32
  "..",
32
33
  "..",
33
34
  "plugins",