@voltro/ai 0.86.0 → 0.87.0

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.
package/dist/provider.js CHANGED
@@ -1,10 +1,11 @@
1
- import { applyMiddleware as e } from "./middleware.js";
2
- import { boundedGatewaySettings as t, gatewayImageModel as n, gatewayVideoModel as r, mediaResponseBudget as i } from "./gatewayMedia.js";
3
- import { anthropic as a, createAnthropic as o } from "@ai-sdk/anthropic";
4
- import { createOpenAI as s, openai as c } from "@ai-sdk/openai";
5
- import { createGateway as l, gateway as u } from "@ai-sdk/gateway";
1
+ import { aiSetting as e, isProductionEnv as t } from "./aiSettings.js";
2
+ import { applyMiddleware as n } from "./middleware.js";
3
+ import { boundedGatewaySettings as r, gatewayImageModel as i, gatewayVideoModel as a, mediaResponseBudget as o } from "./gatewayMedia.js";
4
+ import { anthropic as s, createAnthropic as c } from "@ai-sdk/anthropic";
5
+ import { createOpenAI as l, openai as u } from "@ai-sdk/openai";
6
+ import { createGateway as d, gateway as f } from "@ai-sdk/gateway";
6
7
  //#region src/provider.ts
7
- var d = {
8
+ var p = {
8
9
  inputTokens: {
9
10
  total: 0,
10
11
  noCache: 0,
@@ -16,10 +17,10 @@ var d = {
16
17
  text: 0,
17
18
  reasoning: 0
18
19
  }
19
- }, f = (e) => ({
20
+ }, m = (e) => ({
20
21
  unified: e,
21
22
  raw: void 0
22
- }), p = class {
23
+ }), h = class {
23
24
  turns;
24
25
  cursor = 0;
25
26
  calls = [];
@@ -39,12 +40,12 @@ var d = {
39
40
  reset() {
40
41
  this.cursor = 0, this.calls.length = 0;
41
42
  }
42
- }, m = (e) => (e.toolCalls ?? []).map((e, t) => ({
43
+ }, g = (e) => (e.toolCalls ?? []).map((e, t) => ({
43
44
  type: "tool-call",
44
45
  toolCallId: `mock_tool_${t}`,
45
46
  toolName: e.name,
46
47
  input: JSON.stringify(e.input ?? {})
47
- })), h = (e) => (e.sources ?? []).map((e, t) => e.sourceType === "document" ? {
48
+ })), _ = (e) => (e.sources ?? []).map((e, t) => e.sourceType === "document" ? {
48
49
  type: "source",
49
50
  sourceType: "document",
50
51
  id: e.id ?? `mock_src_${t}`,
@@ -57,14 +58,14 @@ var d = {
57
58
  id: e.id ?? `mock_src_${t}`,
58
59
  url: e.url ?? "https://example.com",
59
60
  ...e.title === void 0 ? {} : { title: e.title }
60
- }), g = (e) => (e.files ?? []).map((e) => ({
61
+ }), v = (e) => (e.files ?? []).map((e) => ({
61
62
  type: "file",
62
63
  mediaType: e.mediaType,
63
64
  data: {
64
65
  type: "data",
65
66
  data: e.data
66
67
  }
67
- })), _ = (e) => [
68
+ })), y = (e) => [
68
69
  {
69
70
  type: "text-start",
70
71
  id: "0"
@@ -78,7 +79,7 @@ var d = {
78
79
  type: "text-end",
79
80
  id: "0"
80
81
  }
81
- ], v = (e) => e.length === 0 ? [] : [
82
+ ], b = (e) => e.length === 0 ? [] : [
82
83
  {
83
84
  type: "reasoning-start",
84
85
  id: "r0"
@@ -92,7 +93,7 @@ var d = {
92
93
  type: "reasoning-end",
93
94
  id: "r0"
94
95
  }
95
- ], y = (e, t, n, r) => ({
96
+ ], x = (e, t, n, r) => ({
96
97
  specificationVersion: "v4",
97
98
  provider: "mock",
98
99
  modelId: e,
@@ -109,16 +110,16 @@ var d = {
109
110
  toolName: e.name,
110
111
  input: JSON.stringify(e.input ?? {})
111
112
  })),
112
- finishReason: f("tool-calls"),
113
- usage: d,
113
+ finishReason: m("tool-calls"),
114
+ usage: p,
114
115
  warnings: []
115
116
  } : {
116
117
  content: [{
117
118
  type: "text",
118
119
  text: o?.text ?? t ?? `[mock(${e})] ${s}`
119
120
  }],
120
- finishReason: f("stop"),
121
- usage: d,
121
+ finishReason: m("stop"),
122
+ usage: p,
122
123
  warnings: [],
123
124
  ...r === void 0 ? {} : { providerMetadata: r }
124
125
  };
@@ -134,18 +135,18 @@ var d = {
134
135
  error: Error(i.error.message)
135
136
  }), s.push({
136
137
  type: "finish",
137
- finishReason: f("error"),
138
- usage: d
139
- })) : (s.push(...v(i?.reasoning ?? "")), o.length > 0 && s.push(..._(o)), s.push(...h(i ?? {})), s.push(...g(i ?? {})), s.push(...m(i ?? {})), s.push({
138
+ finishReason: m("error"),
139
+ usage: p
140
+ })) : (s.push(...b(i?.reasoning ?? "")), o.length > 0 && s.push(...y(o)), s.push(..._(i ?? {})), s.push(...v(i ?? {})), s.push(...g(i ?? {})), s.push({
140
141
  type: "finish",
141
- finishReason: f(a ? "tool-calls" : "stop"),
142
- usage: d
142
+ finishReason: m(a ? "tool-calls" : "stop"),
143
+ usage: p
143
144
  })), { stream: new ReadableStream({ start(e) {
144
145
  for (let t of s) e.enqueue(t);
145
146
  e.close();
146
147
  } }) };
147
148
  }
148
- }), b = "iVBORw0KGgo=", x = "SUQzBAAAAAAAAA==", S = "video/mp4", C = "mock transcript", w = (e, t) => ({
149
+ }), S = "iVBORw0KGgo=", C = "SUQzBAAAAAAAAA==", w = "video/mp4", T = "mock transcript", E = (e, t) => ({
149
150
  specificationVersion: "v4",
150
151
  provider: "mock",
151
152
  modelId: e,
@@ -164,7 +165,7 @@ var d = {
164
165
  totalTokens: 0
165
166
  }
166
167
  })
167
- }), T = (e, t, n) => ({
168
+ }), D = (e, t, n) => ({
168
169
  specificationVersion: "v4",
169
170
  provider: "mock",
170
171
  modelId: e,
@@ -177,7 +178,7 @@ var d = {
177
178
  },
178
179
  ...n === void 0 ? {} : { providerMetadata: n }
179
180
  })
180
- }), E = (e, t, n) => ({
181
+ }), O = (e, t, n) => ({
181
182
  specificationVersion: "v4",
182
183
  provider: "mock",
183
184
  modelId: e,
@@ -195,7 +196,7 @@ var d = {
195
196
  },
196
197
  ...n === void 0 ? {} : { providerMetadata: n }
197
198
  })
198
- }), D = (e, t, n) => ({
199
+ }), k = (e, t, n) => ({
199
200
  specificationVersion: "v4",
200
201
  provider: "mock",
201
202
  modelId: e,
@@ -210,94 +211,109 @@ var d = {
210
211
  },
211
212
  ...n === void 0 ? {} : { providerMetadata: n }
212
213
  })
213
- }), O = (e) => e.apiKey !== void 0 || e.baseURL !== void 0, k = (e) => ({
214
+ }), A = (e) => e.apiKey !== void 0 || e.baseURL !== void 0, j = (e) => ({
214
215
  ...e.apiKey === void 0 ? {} : { apiKey: e.apiKey },
215
216
  ...e.baseURL === void 0 ? {} : { baseURL: e.baseURL }
216
- }), A = (t, n) => {
217
+ }), M = (e, t) => {
217
218
  let r = (() => {
218
- switch (t.name) {
219
- case "mock": return y(t.model ?? "mock", t.mockText, t.script, t.providerMetadata);
220
- case "anthropic": return O(t) ? o(k(t))(t.model) : a(t.model);
221
- case "openai": return O(t) ? s(k(t))(t.model) : c(t.model);
222
- case "gateway": return O(t) ? l(k(t))(t.model) : u(t.model);
219
+ switch (e.name) {
220
+ case "mock": return x(e.model ?? "mock", e.mockText, e.script, e.providerMetadata);
221
+ case "anthropic": return A(e) ? c(j(e))(e.model) : s(e.model);
222
+ case "openai": return A(e) ? l(j(e))(e.model) : u(e.model);
223
+ case "gateway": return A(e) ? d(j(e))(e.model) : f(e.model);
223
224
  }
224
225
  })();
225
- return e(r, n);
226
- }, j = (e) => {
226
+ return n(r, t);
227
+ }, N = (e) => {
227
228
  switch (e.name) {
228
- case "mock": return w(e.model ?? "mock", e.mockMedia?.imageBase64 ?? b);
229
- case "openai": return O(e) ? s(k(e)).image(e.model) : c.image(e.model);
230
- case "gateway": return n(t(k(e), i(e.maxResponseBytes)), e.model);
229
+ case "mock": return E(e.model ?? "mock", e.mockMedia?.imageBase64 ?? S);
230
+ case "openai": return A(e) ? l(j(e)).image(e.model) : u.image(e.model);
231
+ case "gateway": return i(r(j(e), o(e.maxResponseBytes)), e.model);
231
232
  case "anthropic": throw Error("Provider 'anthropic' does not support image generation. Use 'openai' or 'gateway'.");
232
233
  }
233
- }, M = (e) => {
234
+ }, P = (e) => {
234
235
  switch (e.name) {
235
- case "mock": return T(e.model ?? "mock", e.mockMedia?.audioBase64 ?? x, e.providerMetadata);
236
- case "openai": return O(e) ? s(k(e)).speech(e.model) : c.speech(e.model);
237
- case "gateway": return l(t(k(e), i(e.maxResponseBytes))).speech(e.model);
236
+ case "mock": return D(e.model ?? "mock", e.mockMedia?.audioBase64 ?? C, e.providerMetadata);
237
+ case "openai": return A(e) ? l(j(e)).speech(e.model) : u.speech(e.model);
238
+ case "gateway": return d(r(j(e), o(e.maxResponseBytes))).speech(e.model);
238
239
  case "anthropic": throw Error("Provider 'anthropic' does not support speech generation. Use 'openai' or 'gateway'.");
239
240
  }
240
- }, N = (e) => {
241
+ }, F = (e) => {
241
242
  switch (e.name) {
242
- case "mock": return E(e.model ?? "mock", e.mockMedia?.transcript ?? C, e.providerMetadata);
243
- case "openai": return O(e) ? s(k(e)).transcription(e.model) : c.transcription(e.model);
244
- case "gateway": return l(t(k(e), i(e.maxResponseBytes))).transcription(e.model);
243
+ case "mock": return O(e.model ?? "mock", e.mockMedia?.transcript ?? T, e.providerMetadata);
244
+ case "openai": return A(e) ? l(j(e)).transcription(e.model) : u.transcription(e.model);
245
+ case "gateway": return d(r(j(e), o(e.maxResponseBytes))).transcription(e.model);
245
246
  case "anthropic": throw Error("Provider 'anthropic' does not support transcription. Use 'openai' or 'gateway'.");
246
247
  }
247
- }, P = (e) => {
248
+ }, I = (e) => {
248
249
  switch (e.name) {
249
250
  case "mock": {
250
251
  let t = e.mockMedia, n = t?.videoBase64 === void 0 ? {
251
252
  type: "url",
252
253
  url: t?.videoUrl ?? `https://mock.local/${e.model ?? "mock"}.mp4`,
253
- mediaType: S
254
+ mediaType: w
254
255
  } : {
255
256
  type: "base64",
256
257
  data: t.videoBase64,
257
- mediaType: S
258
+ mediaType: w
258
259
  };
259
- return D(e.model ?? "mock", n, e.providerMetadata);
260
+ return k(e.model ?? "mock", n, e.providerMetadata);
260
261
  }
261
- case "gateway": return r(t(k(e), i(e.maxResponseBytes)), e.model);
262
+ case "gateway": return a(r(j(e), o(e.maxResponseBytes)), e.model);
262
263
  case "openai": throw Error("Provider 'openai' does not support video generation. Use 'gateway'.");
263
264
  case "anthropic": throw Error("Provider 'anthropic' does not support video generation. Use 'gateway'.");
264
265
  }
265
- }, F = null, I = (e) => {
266
- let t = F;
267
- return F = e, t;
268
- }, L = () => F, R = (e) => {
266
+ }, L = null, R = (e) => {
267
+ let t = L;
268
+ return L = e, t;
269
+ }, z = () => L, B = (e) => {
269
270
  switch (e.toLowerCase()) {
270
271
  case "anthropic": return "ANTHROPIC_API_KEY";
271
272
  case "openai": return "OPENAI_API_KEY";
272
273
  case "gateway": return "AI_GATEWAY_API_KEY";
273
274
  default: return;
274
275
  }
275
- }, z = (e = process.env) => {
276
- if (F) return F;
277
- let t = (e.AI_PROVIDER ?? "mock").toLowerCase(), n = e.AI_MODEL;
278
- switch (t) {
276
+ }, V = (t = process.env) => {
277
+ if (L) return {
278
+ name: L.name,
279
+ chosen: !0
280
+ };
281
+ let n = e("AI_PROVIDER", t);
282
+ return n === void 0 ? {
283
+ name: "mock",
284
+ chosen: !1
285
+ } : {
286
+ name: n.toLowerCase(),
287
+ chosen: !0
288
+ };
289
+ }, H = (n = process.env) => {
290
+ if (L) return L;
291
+ let r = V(n);
292
+ if (!r.chosen && t(n)) throw Error("No AI provider is chosen in this production process: AI_PROVIDER is unset and this call passes no `provider` (or its agent declares no `model`), so it would answer from the mock provider. Set AI_PROVIDER (anthropic | openai | gateway) for every process that makes model calls, or declare it in `defineEnv` with a `default`, or pass `provider` to the call. Set AI_PROVIDER=mock to serve mock answers deliberately.");
293
+ let i = r.name, a = e("AI_MODEL", n);
294
+ switch (i) {
279
295
  case "mock": return {
280
296
  name: "mock",
281
- model: n ?? "mock"
297
+ model: a ?? "mock"
282
298
  };
283
299
  case "anthropic": return {
284
300
  name: "anthropic",
285
- model: n ?? "claude-opus-4-8"
301
+ model: a ?? "claude-opus-4-8"
286
302
  };
287
303
  case "openai": return {
288
304
  name: "openai",
289
- model: n ?? "gpt-5.5"
305
+ model: a ?? "gpt-5.5"
290
306
  };
291
307
  case "gateway": {
292
- let e = n === void 0 ? -1 : n.indexOf("/");
293
- if (n === void 0 || e <= 0 || e === n.length - 1) throw Error("AI_PROVIDER=gateway requires AI_MODEL set to a 'creator/model' id, e.g. 'openai/gpt-5.5'");
308
+ let e = a === void 0 ? -1 : a.indexOf("/");
309
+ if (a === void 0 || e <= 0 || e === a.length - 1) throw Error("AI_PROVIDER=gateway requires AI_MODEL set to a 'creator/model' id, e.g. 'openai/gpt-5.5'");
294
310
  return {
295
311
  name: "gateway",
296
- model: n
312
+ model: a
297
313
  };
298
314
  }
299
- default: throw Error(`Unknown AI_PROVIDER '${t}'. Expected one of: mock, anthropic, openai, gateway.`);
315
+ default: throw Error(`Unknown AI_PROVIDER '${i}'. Expected one of: mock, anthropic, openai, gateway.`);
300
316
  }
301
317
  };
302
318
  //#endregion
303
- export { p as MockScript, L as getProviderOverride, z as providerFromEnv, R as providerKeyEnvVar, j as resolveImageModel, A as resolveModel, M as resolveSpeechModel, N as resolveTranscriptionModel, P as resolveVideoModel, I as setProviderOverride };
319
+ export { h as MockScript, z as getProviderOverride, V as providerChoice, H as providerFromEnv, B as providerKeyEnvVar, N as resolveImageModel, M as resolveModel, P as resolveSpeechModel, F as resolveTranscriptionModel, I as resolveVideoModel, R as setProviderOverride };
@@ -227,7 +227,7 @@ var D = (e) => e._tag === "done" || e._tag === "error", O = (e, n, i = {}) => {
227
227
  seq: e.seq,
228
228
  event: e.event,
229
229
  createdAt: r
230
- })));
230
+ })), { returning: "none" });
231
231
  }),
232
232
  read: (t, n, r) => c.promise(async () => (await e.query({
233
233
  table: N,
@@ -29,55 +29,58 @@ var s = (e) => [...e].sort((e, t) => e.order - t.order || e.stepOrder - t.stepOr
29
29
  content: u(n)
30
30
  });
31
31
  }), u = (e) => e.filter((e) => e.type === "text").map((e) => e.text).join(""), d = (i, s) => a.gen(function* () {
32
- let d = yield* c(i, {
32
+ let d = s.provider ?? (yield* a.try({
33
+ try: () => e(),
34
+ catch: (e) => e instanceof Error ? e : Error(String(e))
35
+ })), f = yield* c(i, {
33
36
  threadId: s.threadId,
34
37
  tenantId: s.tenantId ?? null,
35
38
  order: s.order
36
- }), f = "", p = "", m = /* @__PURE__ */ new Map(), h = [], g = [], _ = [], v = 0, y = null, b, x = s.throttleMs ?? 100, S = () => {
39
+ }), p = "", m = "", h = /* @__PURE__ */ new Map(), g = [], _ = [], v = [], y = 0, b = null, x, S = s.throttleMs ?? 100, C = () => {
37
40
  let e = [];
38
- f.length > 0 && e.push({
41
+ p.length > 0 && e.push({
39
42
  type: "reasoning",
40
- text: f
41
- }), p.length > 0 && e.push({
42
- type: "text",
43
43
  text: p
44
+ }), m.length > 0 && e.push({
45
+ type: "text",
46
+ text: m
44
47
  });
45
- for (let t of m.values()) e.push(t);
46
- for (let t of h) e.push(t);
48
+ for (let t of h.values()) e.push(t);
47
49
  for (let t of g) e.push(t);
48
50
  for (let t of _) e.push(t);
51
+ for (let t of v) e.push(t);
49
52
  return e;
50
- }, C = (e) => {
53
+ }, w = (e) => {
51
54
  let t = Date.now();
52
- return !e && t - v < x ? a.void : (v = t, l(i, d, S()));
55
+ return !e && t - y < S ? a.void : (y = t, l(i, f, C()));
53
56
  };
54
57
  yield* n({
55
58
  prompt: s.prompt,
56
59
  ...s.messages === void 0 ? {} : { messages: s.messages },
57
60
  ...s.system === void 0 ? {} : { system: s.system },
58
61
  ...s.tools === void 0 ? {} : { tools: s.tools },
59
- ...s.provider === void 0 ? {} : { provider: s.provider },
62
+ provider: d,
60
63
  ...s.maxSteps === void 0 ? {} : { maxSteps: s.maxSteps },
61
64
  ...s.toolContext === void 0 ? {} : { toolContext: s.toolContext },
62
65
  ...s.providerOptions === void 0 ? {} : { providerOptions: s.providerOptions }
63
66
  }).pipe(o.runForEach((e) => a.gen(function* () {
64
67
  switch (e._tag) {
65
68
  case "token":
66
- p += e.text, yield* C(!1);
69
+ m += e.text, yield* w(!1);
67
70
  break;
68
71
  case "toolCall":
69
- m.set(e.id, {
72
+ h.set(e.id, {
70
73
  type: "tool",
71
74
  toolCallId: e.id,
72
75
  toolName: e.name,
73
76
  state: "input-available",
74
77
  input: e.input,
75
78
  ...e.execution === void 0 ? {} : { execution: e.execution }
76
- }), yield* C(!0);
79
+ }), yield* w(!0);
77
80
  break;
78
81
  case "toolResult": {
79
- let t = m.get(e.id);
80
- m.set(e.id, {
82
+ let t = h.get(e.id);
83
+ h.set(e.id, {
81
84
  type: "tool",
82
85
  toolCallId: e.id,
83
86
  toolName: e.name,
@@ -85,64 +88,60 @@ var s = (e) => [...e].sort((e, t) => e.order - t.order || e.stepOrder - t.stepOr
85
88
  input: t?.input,
86
89
  output: e.output,
87
90
  ...e.error ? { errorText: e.error } : {}
88
- }), yield* C(!0);
91
+ }), yield* w(!0);
89
92
  break;
90
93
  }
91
94
  case "reasoning":
92
- f += e.text, yield* C(!1);
95
+ p += e.text, yield* w(!1);
93
96
  break;
94
97
  case "source": {
95
98
  let { _tag: t, ...n } = e;
96
- h.push({
99
+ g.push({
97
100
  type: t,
98
101
  ...n
99
- }), yield* C(!0);
102
+ }), yield* w(!0);
100
103
  break;
101
104
  }
102
105
  case "file": {
103
106
  let { _tag: t, ...n } = e;
104
- g.push({
107
+ _.push({
105
108
  type: t,
106
109
  ...n
107
- }), yield* C(!0);
110
+ }), yield* w(!0);
108
111
  break;
109
112
  }
110
113
  case "stepMetadata": {
111
114
  let { _tag: t, ...n } = e;
112
- _.push({
115
+ v.push({
113
116
  type: t,
114
117
  ...n
115
- }), yield* C(!0);
118
+ }), yield* w(!0);
116
119
  break;
117
120
  }
118
121
  case "error":
119
- y = e.message;
122
+ b = e.message;
120
123
  break;
121
- case "done": b = e.usage;
124
+ case "done": x = e.usage;
122
125
  }
123
126
  })));
124
- let w = S();
125
- if (yield* a.promise(() => i.update(r, d, {
126
- parts: w,
127
- content: u(w),
127
+ let T = C();
128
+ return yield* a.promise(() => i.update(r, f, {
129
+ parts: T,
130
+ content: u(T),
128
131
  streaming: !1
129
- })), s.recordUsage !== !1 && b !== void 0) {
130
- let n = s.provider ?? e();
131
- yield* t(i, {
132
- tenantId: s.tenantId ?? null,
133
- provider: n.name,
134
- model: n.model ?? "unknown",
135
- operation: "agent.turn",
136
- agent: s.agent ?? null,
137
- requestId: d,
138
- usage: {
139
- inputTokens: b.inputTokens,
140
- outputTokens: b.outputTokens,
141
- ...b.totalTokens === void 0 ? {} : { totalTokens: b.totalTokens }
142
- }
143
- });
144
- }
145
- return y ? yield* a.fail(Error(y)) : u(w);
132
+ })), s.recordUsage !== !1 && x !== void 0 && (yield* t(i, {
133
+ tenantId: s.tenantId ?? null,
134
+ provider: d.name,
135
+ model: d.model ?? "unknown",
136
+ operation: "agent.turn",
137
+ agent: s.agent ?? null,
138
+ requestId: f,
139
+ usage: {
140
+ inputTokens: x.inputTokens,
141
+ outputTokens: x.outputTokens,
142
+ ...x.totalTokens === void 0 ? {} : { totalTokens: x.totalTokens }
143
+ }
144
+ })), b ? yield* a.fail(Error(b)) : u(T);
146
145
  });
147
146
  //#endregion
148
147
  export { c as appendStreamingMessage, s as historyToModelMessages, l as patchStreamingMessage, d as runAssistant };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/ai",
3
- "version": "0.86.0",
3
+ "version": "0.87.0",
4
4
  "description": "Effect-native AI primitives — generateText / generateObject / streamText, tools, agents, media generation, cost tracking + budgets, and provider fallback.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -55,10 +55,11 @@
55
55
  "dependencies": {
56
56
  "@ai-sdk/anthropic": "^4.0.56",
57
57
  "@ai-sdk/openai": "^4.0.69",
58
- "@voltro/cache": "0.86.0",
59
- "@voltro/database": "0.86.0",
60
- "@voltro/protocol": "0.86.0",
61
- "@voltro/workflow": "0.86.0",
58
+ "@voltro/cache": "0.87.0",
59
+ "@voltro/database": "0.87.0",
60
+ "@voltro/env": "0.87.0",
61
+ "@voltro/protocol": "0.87.0",
62
+ "@voltro/workflow": "0.87.0",
62
63
  "ajv": "^8.20.0",
63
64
  "ajv-formats": "^3.0.1",
64
65
  "ws": "^8.21.3"