withruntime 0.6.0 → 0.7.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/E2B.md +32 -32
- package/README.md +8 -5
- package/dist/ai/index.d.ts +21 -5
- package/dist/ai/index.js +14 -21
- package/dist/ai-harness/index.d.ts +194 -0
- package/dist/ai-harness/index.js +517 -0
- package/dist/billing-cli.d.ts +21 -0
- package/dist/billing-cli.js +158 -0
- package/dist/cli-extras.d.ts +15 -0
- package/dist/cli-extras.js +60 -4
- package/dist/cli-name.d.ts +6 -0
- package/dist/cli-name.js +8 -0
- package/dist/cli.d.ts +17 -1
- package/dist/cli.js +537 -130
- package/dist/client.d.ts +35 -6
- package/dist/client.js +23 -0
- package/dist/credentials.js +1 -2
- package/dist/daytona/filesystem.d.ts +2 -0
- package/dist/daytona/filesystem.js +9 -7
- package/dist/e2b/filesystem.js +3 -2
- package/dist/e2b/sandbox.d.ts +9 -6
- package/dist/e2b/sandbox.js +31 -7
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +7 -0
- package/dist/index.d.ts +8 -6
- package/dist/index.js +1 -0
- package/dist/keys.js +1 -1
- package/dist/login.d.ts +2 -6
- package/dist/login.js +3 -8
- package/dist/network-cli.d.ts +1 -1
- package/dist/network-cli.js +64 -2
- package/dist/products/billing.d.ts +35 -0
- package/dist/products/billing.js +13 -0
- package/dist/products/index.d.ts +4 -1
- package/dist/products/index.js +4 -1
- package/dist/products/network-products.d.ts +45 -0
- package/dist/products/network-products.js +27 -0
- package/dist/products/previews.d.ts +1 -2
- package/dist/products/secrets.d.ts +14 -0
- package/dist/sandbox.d.ts +17 -3
- package/dist/sandbox.js +233 -62
- package/dist/transport.d.ts +17 -1
- package/dist/transport.js +124 -7
- package/dist/types.d.ts +0 -2
- package/dist/wallet.d.ts +30 -0
- package/dist/wallet.js +48 -0
- package/package.json +44 -17
package/dist/transport.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ConnectionError, errorFor, RuntimeError, WAITS_FOR_ROOM } from "./errors.js";
|
|
2
2
|
import { describeRoute, envFetch, openWebSocket } from "./proxy.js";
|
|
3
|
-
export const VERSION = "0.
|
|
3
|
+
export const VERSION = "0.7.0";
|
|
4
4
|
export const DEFAULT_BASE_URL = "https://api.withruntime.com";
|
|
5
5
|
export function apiOrigin(value) {
|
|
6
6
|
const url = new URL(value);
|
|
@@ -198,8 +198,22 @@ export class Transport {
|
|
|
198
198
|
await sleep(backoff(attempt), signal);
|
|
199
199
|
continue;
|
|
200
200
|
}
|
|
201
|
-
|
|
201
|
+
/* An answer that took longer than the API holds its headers (90 s)
|
|
202
|
+
comes as a 200 sent early, and then its JSON: the answer, or
|
|
203
|
+
{"error": ...} when the work failed after the status went
|
|
204
|
+
(runtime-late-answer, api.md). Either way it is read here, so a
|
|
205
|
+
failure is thrown as the error it is, never returned as a success. */
|
|
206
|
+
let late;
|
|
207
|
+
if (response.ok && response.headers.get("runtime-late-answer") === "true") {
|
|
208
|
+
const text = await response.text();
|
|
209
|
+
const failed = lateFailure(text);
|
|
210
|
+
if (failed === undefined)
|
|
211
|
+
return new Response(text, { headers: response.headers });
|
|
212
|
+
late = { status: failed, text };
|
|
213
|
+
}
|
|
214
|
+
else if (response.ok)
|
|
202
215
|
return response;
|
|
216
|
+
const status = late?.status ?? response.status;
|
|
203
217
|
// Runtime never answers 407: it is a proxy refusing the tunnel, which
|
|
204
218
|
// Bun's fetch hands back as a response rather than an error.
|
|
205
219
|
if (response.status === 407 && this.#routed) {
|
|
@@ -213,15 +227,15 @@ export class Transport {
|
|
|
213
227
|
...(key ? { idempotencyKey: key } : {}),
|
|
214
228
|
});
|
|
215
229
|
}
|
|
216
|
-
const text = await response.text();
|
|
230
|
+
const text = late?.text ?? (await response.text());
|
|
217
231
|
let parsed = null;
|
|
218
232
|
try {
|
|
219
233
|
parsed = JSON.parse(text);
|
|
220
234
|
}
|
|
221
235
|
catch {
|
|
222
|
-
parsed = { error: { message: text.slice(0, 500) || `HTTP ${
|
|
236
|
+
parsed = { error: { message: text.slice(0, 500) || `HTTP ${status}` } };
|
|
223
237
|
}
|
|
224
|
-
const error = errorFor(
|
|
238
|
+
const error = errorFor(status, parsed, key);
|
|
225
239
|
// A full house: wait for a slot or for room, then send the same call
|
|
226
240
|
// again. A request that can never fit (a fork of more copies than the
|
|
227
241
|
// trial runs) is not waited for.
|
|
@@ -232,10 +246,12 @@ export class Transport {
|
|
|
232
246
|
const left = roomUntil - performance.now();
|
|
233
247
|
if (left <= 0)
|
|
234
248
|
throw error;
|
|
235
|
-
|
|
249
|
+
const pause = Math.min(left, error.retryAfterMs ?? roomBackoff(roomAttempt++));
|
|
250
|
+
call.onCapacityWait?.(error, pause);
|
|
251
|
+
await sleep(pause, signal);
|
|
236
252
|
continue;
|
|
237
253
|
}
|
|
238
|
-
const retryable = [429, 502, 503, 504].includes(
|
|
254
|
+
const retryable = [429, 502, 503, 504].includes(status) && !DELIBERATE.has(error.code);
|
|
239
255
|
if (!retry || !retryable || attempt >= this.#maxRetries)
|
|
240
256
|
throw error;
|
|
241
257
|
const header = Number(response.headers.get("retry-after"));
|
|
@@ -263,6 +279,54 @@ export class Transport {
|
|
|
263
279
|
this.#slots.give();
|
|
264
280
|
}
|
|
265
281
|
}
|
|
282
|
+
/** A file's bytes, read whole and checked: the API says a body's length
|
|
283
|
+
* (x-content-length) and, when small, its SHA-256 (x-content-sha256) before
|
|
284
|
+
* sending it, and a body that falls short or differs is read again, twice,
|
|
285
|
+
* then refused. Nothing between the API and here reliably turns a stream
|
|
286
|
+
* cut part way into an error: on 25 September 2026 a 50 MB file came back
|
|
287
|
+
* 29 MB long, exit 0. */
|
|
288
|
+
async fileBytes(call) {
|
|
289
|
+
for (let attempt = 0;; attempt++) {
|
|
290
|
+
await this.#slots.take();
|
|
291
|
+
let failure;
|
|
292
|
+
try {
|
|
293
|
+
const response = await this.send(call);
|
|
294
|
+
const body = new Uint8Array(await response.arrayBuffer());
|
|
295
|
+
const problem = await checkBody(response.headers, body);
|
|
296
|
+
if (!problem)
|
|
297
|
+
return body;
|
|
298
|
+
failure = problem;
|
|
299
|
+
}
|
|
300
|
+
finally {
|
|
301
|
+
this.#slots.give();
|
|
302
|
+
}
|
|
303
|
+
if (attempt >= 2 || call.signal?.aborted)
|
|
304
|
+
throw failure;
|
|
305
|
+
await sleep(backoff(attempt), call.signal);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/** A file's bytes as they arrive, for files too big to hold. The stream
|
|
309
|
+
* errors with `download_incomplete` if it ends short of the length the API
|
|
310
|
+
* promised; it is never retried part way. */
|
|
311
|
+
async fileStream(call) {
|
|
312
|
+
const response = await this.send(call);
|
|
313
|
+
const expected = promisedLength(response.headers);
|
|
314
|
+
const body = response.body ?? new ReadableStream({ start: (c) => c.close() });
|
|
315
|
+
let got = 0;
|
|
316
|
+
return body.pipeThrough(new TransformStream({
|
|
317
|
+
transform(chunk, controller) {
|
|
318
|
+
got += chunk.byteLength;
|
|
319
|
+
if (expected !== undefined && got > expected)
|
|
320
|
+
controller.error(incompleteDownload(got, expected));
|
|
321
|
+
else
|
|
322
|
+
controller.enqueue(chunk);
|
|
323
|
+
},
|
|
324
|
+
flush(controller) {
|
|
325
|
+
if (expected !== undefined && got !== expected)
|
|
326
|
+
controller.error(incompleteDownload(got, expected));
|
|
327
|
+
},
|
|
328
|
+
}));
|
|
329
|
+
}
|
|
266
330
|
/** Newline-delimited JSON events, as they arrive. */
|
|
267
331
|
async *events(call) {
|
|
268
332
|
const response = await this.send({ ...call, accept: "application/x-ndjson" });
|
|
@@ -305,6 +369,59 @@ export class Transport {
|
|
|
305
369
|
});
|
|
306
370
|
}
|
|
307
371
|
}
|
|
372
|
+
/** The status a late answer's body failed with, or undefined when it is an
|
|
373
|
+
* answer. The body is the error the API would have sent as its status. */
|
|
374
|
+
function lateFailure(text) {
|
|
375
|
+
let body;
|
|
376
|
+
try {
|
|
377
|
+
body = JSON.parse(text);
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
return 502;
|
|
381
|
+
}
|
|
382
|
+
// An API error is a body of `error` alone, with a code; an answer may carry
|
|
383
|
+
// an `error` of its own (a code cell's exception), next to other fields.
|
|
384
|
+
if (!body || typeof body !== "object" || Object.keys(body).length !== 1)
|
|
385
|
+
return undefined;
|
|
386
|
+
const error = body.error;
|
|
387
|
+
if (!error || typeof error !== "object" || typeof error.code !== "string")
|
|
388
|
+
return undefined;
|
|
389
|
+
const status = error.status;
|
|
390
|
+
return typeof status === "number" && status >= 400 ? status : 500;
|
|
391
|
+
}
|
|
392
|
+
function promisedLength(headers) {
|
|
393
|
+
const value = headers.get("x-content-length");
|
|
394
|
+
return value !== null && /^\d+$/.test(value) ? Number(value) : undefined;
|
|
395
|
+
}
|
|
396
|
+
/** A download that ended short of (or past) the length the API promised. */
|
|
397
|
+
export function incompleteDownload(got, expected) {
|
|
398
|
+
return new RuntimeError({
|
|
399
|
+
message: `The download ended at ${got} of ${expected} bytes.`,
|
|
400
|
+
code: "download_incomplete",
|
|
401
|
+
status: 0,
|
|
402
|
+
hint: "Read the file again; nothing was written for this read. If it keeps happening, report it with `npx withruntime feedback`.",
|
|
403
|
+
details: { received: got, expected },
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
/** Why a whole body cannot be trusted, or undefined when it can. */
|
|
407
|
+
async function checkBody(headers, body) {
|
|
408
|
+
const expected = promisedLength(headers);
|
|
409
|
+
if (expected !== undefined && body.byteLength !== expected)
|
|
410
|
+
return incompleteDownload(body.byteLength, expected);
|
|
411
|
+
const sha256 = headers.get("x-content-sha256");
|
|
412
|
+
if (!sha256)
|
|
413
|
+
return undefined;
|
|
414
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", body));
|
|
415
|
+
const hex = Array.from(digest, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
416
|
+
if (hex === sha256.toLowerCase())
|
|
417
|
+
return undefined;
|
|
418
|
+
return new RuntimeError({
|
|
419
|
+
message: "The file's bytes did not match the SHA-256 the API sent with them.",
|
|
420
|
+
code: "download_incomplete",
|
|
421
|
+
status: 0,
|
|
422
|
+
hint: "Read the file again. If it keeps happening, report it with `npx withruntime feedback`.",
|
|
423
|
+
});
|
|
424
|
+
}
|
|
308
425
|
/** No key given, none in RUNTIME_API_KEY, and none saved by `runtime login`. */
|
|
309
426
|
export function missingKey() {
|
|
310
427
|
return new RuntimeError({
|
package/dist/types.d.ts
CHANGED
|
@@ -100,8 +100,6 @@ export type CreateSandbox = {
|
|
|
100
100
|
path: string;
|
|
101
101
|
mode?: "rw" | "snapshot";
|
|
102
102
|
}[];
|
|
103
|
-
/** Anything a newer server accepts (image, snapshotId, volumes, network...). */
|
|
104
|
-
[field: string]: unknown;
|
|
105
103
|
};
|
|
106
104
|
export type CommandResult = {
|
|
107
105
|
exitCode: number | null;
|
package/dist/wallet.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Topup } from "./products/billing.js";
|
|
2
|
+
export type WalletOptions = {
|
|
3
|
+
/** Default: RUNTIME_AUTH_URL, then https://withruntime.com. */
|
|
4
|
+
authUrl?: string;
|
|
5
|
+
fetch?: typeof fetch;
|
|
6
|
+
};
|
|
7
|
+
export type OpenedWalletAccount = Topup & {
|
|
8
|
+
orgId: string;
|
|
9
|
+
/** The only way to this account's key, shown once. Keep it secret. */
|
|
10
|
+
claimCode: string;
|
|
11
|
+
};
|
|
12
|
+
export type WalletClaim = {
|
|
13
|
+
status: "paid";
|
|
14
|
+
orgId: string;
|
|
15
|
+
apiKey: string;
|
|
16
|
+
} | {
|
|
17
|
+
status: Exclude<Topup["status"], "paid">;
|
|
18
|
+
orgId: string;
|
|
19
|
+
topup: Topup | null;
|
|
20
|
+
};
|
|
21
|
+
/** Opens an account and a stablecoin top-up of `usd` dollars ($10 to $10,000).
|
|
22
|
+
* `acceptTerms` must be true: opening one means agreeing to
|
|
23
|
+
* https://withruntime.com/legal/terms, so ask the person you work for first. */
|
|
24
|
+
export declare function openWalletAccount(input: {
|
|
25
|
+
usd: number;
|
|
26
|
+
acceptTerms: boolean;
|
|
27
|
+
}, options?: WalletOptions): Promise<OpenedWalletAccount>;
|
|
28
|
+
/** Exchanges a paid account's claim code for its API key, once. Until the
|
|
29
|
+
* payment arrives it answers the top-up's status instead; call it again. */
|
|
30
|
+
export declare function claimWalletAccount(claimCode: string, options?: WalletOptions): Promise<WalletClaim>;
|
package/dist/wallet.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { RuntimeError } from "./errors.js";
|
|
2
|
+
import { apiOrigin } from "./transport.js";
|
|
3
|
+
function origin(options) {
|
|
4
|
+
const env = typeof process === "undefined" ? {} : process.env;
|
|
5
|
+
return apiOrigin(options.authUrl ?? env.RUNTIME_AUTH_URL ?? "https://withruntime.com");
|
|
6
|
+
}
|
|
7
|
+
async function post(options, path, body) {
|
|
8
|
+
const fetcher = options.fetch ?? fetch;
|
|
9
|
+
let response;
|
|
10
|
+
try {
|
|
11
|
+
response = await fetcher(`${origin(options)}${path}`, {
|
|
12
|
+
method: "POST",
|
|
13
|
+
redirect: "error",
|
|
14
|
+
headers: { "content-type": "application/json" },
|
|
15
|
+
body: JSON.stringify(body),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch (cause) {
|
|
19
|
+
throw new RuntimeError({
|
|
20
|
+
message: `No answer from ${origin(options)}.`,
|
|
21
|
+
code: "network",
|
|
22
|
+
status: 0,
|
|
23
|
+
hint: "Retry in a minute.",
|
|
24
|
+
cause,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const answer = (await response.json().catch(() => ({})));
|
|
28
|
+
if (!response.ok)
|
|
29
|
+
throw new RuntimeError({
|
|
30
|
+
message: answer.error ?? `The request failed (HTTP ${response.status}).`,
|
|
31
|
+
code: answer.code ?? (response.status === 429 ? "rate_limited" : "request_failed"),
|
|
32
|
+
status: response.status,
|
|
33
|
+
});
|
|
34
|
+
return [response.status, answer];
|
|
35
|
+
}
|
|
36
|
+
/** Opens an account and a stablecoin top-up of `usd` dollars ($10 to $10,000).
|
|
37
|
+
* `acceptTerms` must be true: opening one means agreeing to
|
|
38
|
+
* https://withruntime.com/legal/terms, so ask the person you work for first. */
|
|
39
|
+
export async function openWalletAccount(input, options = {}) {
|
|
40
|
+
const [, opened] = await post(options, "/api/wallet/topups", input);
|
|
41
|
+
return opened;
|
|
42
|
+
}
|
|
43
|
+
/** Exchanges a paid account's claim code for its API key, once. Until the
|
|
44
|
+
* payment arrives it answers the top-up's status instead; call it again. */
|
|
45
|
+
export async function claimWalletAccount(claimCode, options = {}) {
|
|
46
|
+
const [, claim] = await post(options, "/api/wallet/claim", { claimCode });
|
|
47
|
+
return claim;
|
|
48
|
+
}
|
package/package.json
CHANGED
|
@@ -1,45 +1,59 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "withruntime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"mcpName": "com.withruntime/runtime",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
|
-
"import": "./dist/index.js"
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"default": "./dist/index.js"
|
|
11
12
|
},
|
|
12
13
|
"./e2b": {
|
|
13
14
|
"types": "./dist/e2b/index.d.ts",
|
|
14
|
-
"import": "./dist/e2b/index.js"
|
|
15
|
+
"import": "./dist/e2b/index.js",
|
|
16
|
+
"default": "./dist/e2b/index.js"
|
|
15
17
|
},
|
|
16
18
|
"./e2b/code-interpreter": {
|
|
17
19
|
"types": "./dist/e2b/code-interpreter.d.ts",
|
|
18
|
-
"import": "./dist/e2b/code-interpreter.js"
|
|
20
|
+
"import": "./dist/e2b/code-interpreter.js",
|
|
21
|
+
"default": "./dist/e2b/code-interpreter.js"
|
|
19
22
|
},
|
|
20
23
|
"./daytona": {
|
|
21
24
|
"types": "./dist/daytona/index.d.ts",
|
|
22
|
-
"import": "./dist/daytona/index.js"
|
|
25
|
+
"import": "./dist/daytona/index.js",
|
|
26
|
+
"default": "./dist/daytona/index.js"
|
|
23
27
|
},
|
|
24
28
|
"./vercel": {
|
|
25
29
|
"types": "./dist/vercel/index.d.ts",
|
|
26
|
-
"import": "./dist/vercel/index.js"
|
|
30
|
+
"import": "./dist/vercel/index.js",
|
|
31
|
+
"default": "./dist/vercel/index.js"
|
|
27
32
|
},
|
|
28
33
|
"./tools": {
|
|
29
34
|
"types": "./dist/tools/index.d.ts",
|
|
30
|
-
"import": "./dist/tools/index.js"
|
|
35
|
+
"import": "./dist/tools/index.js",
|
|
36
|
+
"default": "./dist/tools/index.js"
|
|
31
37
|
},
|
|
32
38
|
"./ai": {
|
|
33
39
|
"types": "./dist/ai/index.d.ts",
|
|
34
|
-
"import": "./dist/ai/index.js"
|
|
40
|
+
"import": "./dist/ai/index.js",
|
|
41
|
+
"default": "./dist/ai/index.js"
|
|
35
42
|
},
|
|
36
43
|
"./claude-agent-sdk": {
|
|
37
44
|
"types": "./dist/claude-agent-sdk/index.d.ts",
|
|
38
|
-
"import": "./dist/claude-agent-sdk/index.js"
|
|
45
|
+
"import": "./dist/claude-agent-sdk/index.js",
|
|
46
|
+
"default": "./dist/claude-agent-sdk/index.js"
|
|
39
47
|
},
|
|
40
48
|
"./openai-agents": {
|
|
41
49
|
"types": "./dist/openai-agents/index.d.ts",
|
|
42
|
-
"import": "./dist/openai-agents/index.js"
|
|
50
|
+
"import": "./dist/openai-agents/index.js",
|
|
51
|
+
"default": "./dist/openai-agents/index.js"
|
|
52
|
+
},
|
|
53
|
+
"./ai-harness": {
|
|
54
|
+
"types": "./dist/ai-harness/index.d.ts",
|
|
55
|
+
"import": "./dist/ai-harness/index.js",
|
|
56
|
+
"default": "./dist/ai-harness/index.js"
|
|
43
57
|
}
|
|
44
58
|
},
|
|
45
59
|
"bin": {
|
|
@@ -55,7 +69,7 @@
|
|
|
55
69
|
"LICENSE"
|
|
56
70
|
],
|
|
57
71
|
"engines": {
|
|
58
|
-
"node": ">=22"
|
|
72
|
+
"node": ">=22.12"
|
|
59
73
|
},
|
|
60
74
|
"scripts": {
|
|
61
75
|
"build": "tsc -p tsconfig.build.json",
|
|
@@ -69,18 +83,27 @@
|
|
|
69
83
|
"test:go": "bun scripts/test-go.ts"
|
|
70
84
|
},
|
|
71
85
|
"devDependencies": {
|
|
86
|
+
"@ai-sdk/harness": "1.0.124",
|
|
87
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.277",
|
|
88
|
+
"@anthropic-ai/sdk": "0.128.0",
|
|
89
|
+
"@google/genai": "2.24.0",
|
|
90
|
+
"@mastra/core": "1.71.0",
|
|
91
|
+
"@mistralai/mistralai": "2.7.0",
|
|
92
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
93
|
+
"@openai/agents": "0.18.0",
|
|
94
|
+
"@openai/agents-core": "0.18.0",
|
|
95
|
+
"@openrouter/sdk": "1.3.27",
|
|
72
96
|
"@runtime/eslint-config": "workspace:*",
|
|
73
97
|
"@runtime/typescript-config": "workspace:*",
|
|
74
98
|
"@types/bun": "^1.4.0",
|
|
75
99
|
"@types/node": "^20",
|
|
100
|
+
"ai": "7.0.87",
|
|
76
101
|
"eslint": "^9",
|
|
102
|
+
"groq-sdk": "1.6.0",
|
|
103
|
+
"openai": "7.23.0",
|
|
104
|
+
"together-ai": "0.55.0",
|
|
77
105
|
"typescript": "^5",
|
|
78
|
-
"
|
|
79
|
-
"@openai/agents-core": "0.18.0",
|
|
80
|
-
"@anthropic-ai/claude-agent-sdk": "0.3.277",
|
|
81
|
-
"ai": "7.0.87",
|
|
82
|
-
"zod": "^4.1.8",
|
|
83
|
-
"@modelcontextprotocol/sdk": "^1.30.0"
|
|
106
|
+
"zod": "^4.1.8"
|
|
84
107
|
},
|
|
85
108
|
"publishConfig": {
|
|
86
109
|
"access": "public",
|
|
@@ -100,6 +123,7 @@
|
|
|
100
123
|
"undici": "^7.29.1"
|
|
101
124
|
},
|
|
102
125
|
"peerDependencies": {
|
|
126
|
+
"@ai-sdk/harness": "^1.0.0",
|
|
103
127
|
"@anthropic-ai/claude-agent-sdk": ">=0.2.0",
|
|
104
128
|
"@openai/agents": ">=0.18.0",
|
|
105
129
|
"@openai/agents-core": ">=0.18.0",
|
|
@@ -107,6 +131,9 @@
|
|
|
107
131
|
"zod": ">=3.25.0"
|
|
108
132
|
},
|
|
109
133
|
"peerDependenciesMeta": {
|
|
134
|
+
"@ai-sdk/harness": {
|
|
135
|
+
"optional": true
|
|
136
|
+
},
|
|
110
137
|
"@anthropic-ai/claude-agent-sdk": {
|
|
111
138
|
"optional": true
|
|
112
139
|
},
|