@agentfield/sdk 0.1.136 → 0.1.137-rc.10
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/README.md +4 -0
- package/dist/index.d.ts +227 -193
- package/dist/index.js +1289 -884
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import os, { constants } from 'os';
|
|
1
3
|
import { createRequire } from 'module';
|
|
2
4
|
import * as path4 from 'path';
|
|
3
5
|
import path4__default, { resolve, dirname } from 'path';
|
|
4
6
|
import fs, { promises, readFileSync } from 'fs';
|
|
5
|
-
import { spawn } from 'child_process';
|
|
6
7
|
import express from 'express';
|
|
7
8
|
import rateLimit from 'express-rate-limit';
|
|
8
9
|
import crypto2, { randomUUID, createHash } from 'crypto';
|
|
@@ -19,12 +20,11 @@ import { createGroq } from '@ai-sdk/groq';
|
|
|
19
20
|
import { createXai } from '@ai-sdk/xai';
|
|
20
21
|
import { createDeepSeek } from '@ai-sdk/deepseek';
|
|
21
22
|
import { createCohere } from '@ai-sdk/cohere';
|
|
22
|
-
import
|
|
23
|
+
import { readFile } from 'fs/promises';
|
|
23
24
|
import WebSocket from 'ws';
|
|
24
25
|
import { Buffer as Buffer$1 } from 'buffer';
|
|
25
26
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
26
27
|
import { generateKeyPair, exportJWK, importJWK, CompactEncrypt, compactDecrypt } from 'jose';
|
|
27
|
-
import { readFile } from 'fs/promises';
|
|
28
28
|
import { fileURLToPath } from 'url';
|
|
29
29
|
|
|
30
30
|
var __defProp = Object.defineProperty;
|
|
@@ -150,250 +150,17 @@ var init_openrouterAttribution = __esm({
|
|
|
150
150
|
}
|
|
151
151
|
});
|
|
152
152
|
|
|
153
|
-
// src/harness/
|
|
154
|
-
function
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const sepIndex = model.indexOf(MODEL_VARIANT_SEP);
|
|
159
|
-
const base = sepIndex === -1 ? model : model.slice(0, sepIndex);
|
|
160
|
-
const variant = sepIndex === -1 ? "" : model.slice(sepIndex + 1);
|
|
161
|
-
return {
|
|
162
|
-
model: base.trim() || void 0,
|
|
163
|
-
variant: variant.trim() || void 0
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
function resolveModelAndVariant(options) {
|
|
167
|
-
const { model, variant } = splitModelVariant(options.model);
|
|
168
|
-
const explicit = options.variant;
|
|
169
|
-
if (typeof explicit === "string" && explicit.trim()) {
|
|
170
|
-
return { model, variant: explicit.trim() };
|
|
171
|
-
}
|
|
172
|
-
return { model, variant };
|
|
173
|
-
}
|
|
174
|
-
var MODEL_VARIANT_SEP;
|
|
175
|
-
var init_modelVariant = __esm({
|
|
176
|
-
"src/harness/modelVariant.ts"() {
|
|
177
|
-
MODEL_VARIANT_SEP = "#";
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
function isRecord2(value) {
|
|
181
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
182
|
-
}
|
|
183
|
-
function isZod4Schema(value) {
|
|
184
|
-
if (!isRecord2(value) || !("_zod" in value) || !isRecord2(value._zod)) {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
return isRecord2(value._zod.def);
|
|
188
|
-
}
|
|
189
|
-
function getZod4Converter() {
|
|
190
|
-
if (zod4Converter !== void 0) {
|
|
191
|
-
return zod4Converter;
|
|
192
|
-
}
|
|
193
|
-
const requires = [];
|
|
194
|
-
try {
|
|
195
|
-
requires.push(createRequire(path4__default.join(process.cwd(), "package.json")));
|
|
196
|
-
} catch {
|
|
197
|
-
}
|
|
198
|
-
requires.push(createRequire(import.meta.url));
|
|
199
|
-
for (const req of requires) {
|
|
200
|
-
for (const moduleName of ["zod", "zod/v4"]) {
|
|
201
|
-
try {
|
|
202
|
-
const mod = req(moduleName);
|
|
203
|
-
if (typeof mod.toJSONSchema === "function") {
|
|
204
|
-
zod4Converter = mod.toJSONSchema;
|
|
205
|
-
return zod4Converter;
|
|
206
|
-
}
|
|
207
|
-
} catch {
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
zod4Converter = null;
|
|
212
|
-
return zod4Converter;
|
|
213
|
-
}
|
|
214
|
-
function zod4ToJsonSchema(schema) {
|
|
215
|
-
const converter = getZod4Converter();
|
|
216
|
-
if (converter === null) {
|
|
217
|
-
throw new TypeError(
|
|
218
|
-
'Cannot convert a Zod 4 schema: install the "zod" package with its "zod/v4" entry point available.'
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
return converter(schema);
|
|
222
|
-
}
|
|
223
|
-
var zod4Converter;
|
|
224
|
-
var init_zod_schema = __esm({
|
|
225
|
-
"src/utils/zod-schema.ts"() {
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
function getZodConverter() {
|
|
229
|
-
if (zodConverter !== void 0) {
|
|
230
|
-
return zodConverter;
|
|
231
|
-
}
|
|
232
|
-
try {
|
|
233
|
-
const require2 = createRequire(import.meta.url);
|
|
234
|
-
const mod = require2("zod-to-json-schema");
|
|
235
|
-
zodConverter = mod.zodToJsonSchema ?? mod.default ?? null;
|
|
236
|
-
} catch {
|
|
237
|
-
zodConverter = null;
|
|
238
|
-
}
|
|
239
|
-
return zodConverter;
|
|
240
|
-
}
|
|
241
|
-
function isRecord3(value) {
|
|
242
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
243
|
-
}
|
|
244
|
-
function hasJsonSchema(value) {
|
|
245
|
-
return isRecord3(value) && typeof value.jsonSchema === "function";
|
|
246
|
-
}
|
|
247
|
-
function hasParse(value) {
|
|
248
|
-
return isRecord3(value) && typeof value.parse === "function";
|
|
249
|
-
}
|
|
250
|
-
function estimateTokens(text2) {
|
|
251
|
-
return Math.floor(text2.length / 4);
|
|
252
|
-
}
|
|
253
|
-
function writeSchemaFile(schemaJson, cwd) {
|
|
254
|
-
const filePath = getSchemaPath(cwd);
|
|
255
|
-
fs.mkdirSync(path4__default.dirname(filePath), { recursive: true });
|
|
256
|
-
const fd = fs.openSync(filePath, "w", 384);
|
|
257
|
-
try {
|
|
258
|
-
fs.writeFileSync(fd, schemaJson, "utf8");
|
|
259
|
-
} finally {
|
|
260
|
-
fs.closeSync(fd);
|
|
261
|
-
}
|
|
262
|
-
return filePath;
|
|
263
|
-
}
|
|
264
|
-
function validateAgainstSchema(data, schema) {
|
|
265
|
-
if (hasParse(schema)) {
|
|
266
|
-
return schema.parse(data);
|
|
267
|
-
}
|
|
268
|
-
return data;
|
|
269
|
-
}
|
|
270
|
-
function getOutputPath(cwd) {
|
|
271
|
-
return path4__default.join(cwd, OUTPUT_FILENAME);
|
|
272
|
-
}
|
|
273
|
-
function getSchemaPath(cwd) {
|
|
274
|
-
return path4__default.join(cwd, SCHEMA_FILENAME);
|
|
275
|
-
}
|
|
276
|
-
function schemaToJsonSchema(schema) {
|
|
277
|
-
if (isZod4Schema(schema)) {
|
|
278
|
-
return zod4ToJsonSchema(schema);
|
|
279
|
-
}
|
|
280
|
-
if (isRecord3(schema)) {
|
|
281
|
-
if ("type" in schema || "properties" in schema || "$schema" in schema) {
|
|
282
|
-
return schema;
|
|
283
|
-
}
|
|
284
|
-
if (hasJsonSchema(schema)) {
|
|
285
|
-
return schema.jsonSchema();
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
const converter = getZodConverter();
|
|
289
|
-
if (converter !== null) {
|
|
290
|
-
return converter(schema);
|
|
291
|
-
}
|
|
292
|
-
throw new TypeError(
|
|
293
|
-
'Unsupported schema type. Expected a Zod schema (requires "zod-to-json-schema" for Zod 3 or "zod" for Zod 4), JSON schema object, or jsonSchema() provider.'
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
function isLargeSchema(schemaJson) {
|
|
297
|
-
return estimateTokens(schemaJson) > LARGE_SCHEMA_TOKEN_THRESHOLD;
|
|
298
|
-
}
|
|
299
|
-
function buildPromptSuffix(schema, cwd) {
|
|
300
|
-
const jsonSchema2 = schemaToJsonSchema(schema);
|
|
301
|
-
const schemaJson = JSON.stringify(jsonSchema2, null, 2);
|
|
302
|
-
const outputPath = getOutputPath(cwd);
|
|
303
|
-
if (isLargeSchema(schemaJson)) {
|
|
304
|
-
const schemaPath = writeSchemaFile(schemaJson, cwd);
|
|
305
|
-
return `
|
|
306
|
-
|
|
307
|
-
---
|
|
308
|
-
OUTPUT REQUIREMENTS:
|
|
309
|
-
Read the JSON Schema at: ${schemaPath}
|
|
310
|
-
Write your final answer as valid JSON conforming to that schema to: ${outputPath}
|
|
311
|
-
Do not include any text outside the JSON in that file. Do not wrap in markdown fences.`;
|
|
312
|
-
}
|
|
313
|
-
return `
|
|
314
|
-
|
|
315
|
-
---
|
|
316
|
-
OUTPUT REQUIREMENTS:
|
|
317
|
-
Write your final answer as valid JSON to the file: ${outputPath}
|
|
318
|
-
The JSON must conform to this schema:
|
|
319
|
-
${schemaJson}
|
|
320
|
-
Do not include any text outside the JSON in that file. Do not wrap in markdown fences.`;
|
|
321
|
-
}
|
|
322
|
-
function cosmeticRepair(raw) {
|
|
323
|
-
let text2 = raw.trim();
|
|
324
|
-
const fenceMatch = text2.match(/^```(?:json)?\s*\n([\s\S]*?)```\s*$/);
|
|
325
|
-
if (fenceMatch) {
|
|
326
|
-
text2 = fenceMatch[1].trim();
|
|
327
|
-
}
|
|
328
|
-
if (text2.length > 0 && text2[0] !== "{" && text2[0] !== "[") {
|
|
329
|
-
const firstJsonCharIndex = [...text2].findIndex((char) => char === "{" || char === "[");
|
|
330
|
-
if (firstJsonCharIndex >= 0) {
|
|
331
|
-
text2 = text2.slice(firstJsonCharIndex);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
text2 = text2.replace(/,\s*([}\]])/g, "$1");
|
|
335
|
-
const openBraces = (text2.match(/{/g)?.length ?? 0) - (text2.match(/}/g)?.length ?? 0);
|
|
336
|
-
const openBrackets = (text2.match(/\[/g)?.length ?? 0) - (text2.match(/\]/g)?.length ?? 0);
|
|
337
|
-
if (openBraces > 0 || openBrackets > 0) {
|
|
338
|
-
text2 += "]".repeat(openBrackets) + "}".repeat(openBraces);
|
|
339
|
-
}
|
|
340
|
-
return text2;
|
|
341
|
-
}
|
|
342
|
-
function readAndParse(filePath) {
|
|
343
|
-
try {
|
|
344
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
345
|
-
if (content.trim().length === 0) {
|
|
346
|
-
return null;
|
|
347
|
-
}
|
|
348
|
-
return JSON.parse(content);
|
|
349
|
-
} catch {
|
|
350
|
-
return null;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
function readRepairAndParse(filePath) {
|
|
354
|
-
try {
|
|
355
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
356
|
-
if (content.trim().length === 0) {
|
|
357
|
-
return null;
|
|
358
|
-
}
|
|
359
|
-
return JSON.parse(cosmeticRepair(content));
|
|
360
|
-
} catch {
|
|
361
|
-
return null;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
function parseAndValidate(filePath, schema) {
|
|
365
|
-
const parsed = readAndParse(filePath);
|
|
366
|
-
if (parsed !== null) {
|
|
367
|
-
try {
|
|
368
|
-
return validateAgainstSchema(parsed, schema);
|
|
369
|
-
} catch {
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
const repaired = readRepairAndParse(filePath);
|
|
373
|
-
if (repaired !== null) {
|
|
374
|
-
try {
|
|
375
|
-
return validateAgainstSchema(repaired, schema);
|
|
376
|
-
} catch {
|
|
377
|
-
return null;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
return null;
|
|
381
|
-
}
|
|
382
|
-
function cleanupTempFiles(cwd) {
|
|
383
|
-
for (const filename of [OUTPUT_FILENAME, SCHEMA_FILENAME]) {
|
|
384
|
-
try {
|
|
385
|
-
fs.unlinkSync(path4__default.join(cwd, filename));
|
|
386
|
-
} catch {
|
|
153
|
+
// src/harness/providers/base.ts
|
|
154
|
+
function resolveRoot(options) {
|
|
155
|
+
for (const value of [options.projectDir, options.project_dir, options.cwd]) {
|
|
156
|
+
if (typeof value === "string" && value.length > 0) {
|
|
157
|
+
return value;
|
|
387
158
|
}
|
|
388
159
|
}
|
|
160
|
+
return void 0;
|
|
389
161
|
}
|
|
390
|
-
var
|
|
391
|
-
|
|
392
|
-
"src/harness/schema.ts"() {
|
|
393
|
-
init_zod_schema();
|
|
394
|
-
OUTPUT_FILENAME = ".agentfield_output.json";
|
|
395
|
-
SCHEMA_FILENAME = ".agentfield_schema.json";
|
|
396
|
-
LARGE_SCHEMA_TOKEN_THRESHOLD = 4e3;
|
|
162
|
+
var init_base = __esm({
|
|
163
|
+
"src/harness/providers/base.ts"() {
|
|
397
164
|
}
|
|
398
165
|
});
|
|
399
166
|
|
|
@@ -434,7 +201,13 @@ function resolveIdleMs(idleSeconds) {
|
|
|
434
201
|
function runCli(cmd, options) {
|
|
435
202
|
return new Promise((resolve3, reject) => {
|
|
436
203
|
const [bin, ...args] = cmd;
|
|
437
|
-
const
|
|
204
|
+
const parsedParentDepth = Number.parseInt(process.env.AGENTFIELD_HARNESS_DEPTH ?? "", 10);
|
|
205
|
+
const parentDepth = Number.isFinite(parsedParentDepth) && parsedParentDepth >= 0 ? parsedParentDepth : 0;
|
|
206
|
+
const env = {
|
|
207
|
+
...process.env,
|
|
208
|
+
AGENTFIELD_HARNESS_DEPTH: String(parentDepth + 1),
|
|
209
|
+
...options?.env
|
|
210
|
+
};
|
|
438
211
|
applyOpenRouterAttributionEnv(env);
|
|
439
212
|
const hasInput = options?.inputText !== void 0;
|
|
440
213
|
const proc = spawn(bin, args, {
|
|
@@ -490,13 +263,18 @@ function runCli(cmd, options) {
|
|
|
490
263
|
clearInterval(idleTimer);
|
|
491
264
|
}
|
|
492
265
|
}
|
|
493
|
-
proc.on("close", (code) => {
|
|
266
|
+
proc.on("close", (code, signal) => {
|
|
494
267
|
if (settled) {
|
|
495
268
|
return;
|
|
496
269
|
}
|
|
497
270
|
settled = true;
|
|
498
271
|
cleanup();
|
|
499
|
-
|
|
272
|
+
const signalNumber = signal ? constants.signals[signal] : void 0;
|
|
273
|
+
resolve3({
|
|
274
|
+
stdout,
|
|
275
|
+
stderr,
|
|
276
|
+
exitCode: code ?? (signalNumber === void 0 ? 0 : -signalNumber)
|
|
277
|
+
});
|
|
500
278
|
});
|
|
501
279
|
proc.on("error", (err) => {
|
|
502
280
|
if (settled) {
|
|
@@ -557,18 +335,46 @@ var init_cli = __esm({
|
|
|
557
335
|
}
|
|
558
336
|
});
|
|
559
337
|
|
|
560
|
-
// src/harness/
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
const
|
|
568
|
-
return
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
338
|
+
// src/harness/modelVariant.ts
|
|
339
|
+
function splitModelVariant(model) {
|
|
340
|
+
if (typeof model !== "string" || !model.trim()) {
|
|
341
|
+
return {};
|
|
342
|
+
}
|
|
343
|
+
const sepIndex = model.indexOf(MODEL_VARIANT_SEP);
|
|
344
|
+
const base = sepIndex === -1 ? model : model.slice(0, sepIndex);
|
|
345
|
+
const variant = sepIndex === -1 ? "" : model.slice(sepIndex + 1);
|
|
346
|
+
return {
|
|
347
|
+
model: base.trim() || void 0,
|
|
348
|
+
variant: variant.trim() || void 0
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function resolveModelAndVariant(options) {
|
|
352
|
+
const { model, variant } = splitModelVariant(options.model);
|
|
353
|
+
const explicit = options.variant;
|
|
354
|
+
if (typeof explicit === "string" && explicit.trim()) {
|
|
355
|
+
return { model, variant: explicit.trim() };
|
|
356
|
+
}
|
|
357
|
+
return { model, variant };
|
|
358
|
+
}
|
|
359
|
+
var MODEL_VARIANT_SEP;
|
|
360
|
+
var init_modelVariant = __esm({
|
|
361
|
+
"src/harness/modelVariant.ts"() {
|
|
362
|
+
MODEL_VARIANT_SEP = "#";
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// src/harness/providers/aforge.ts
|
|
367
|
+
var aforge_exports = {};
|
|
368
|
+
__export(aforge_exports, {
|
|
369
|
+
AFORGE_DEFAULT_MODEL: () => AFORGE_DEFAULT_MODEL,
|
|
370
|
+
AforgeProvider: () => AforgeProvider
|
|
371
|
+
});
|
|
372
|
+
function resolveMaxConcurrent() {
|
|
373
|
+
const parsed = Number.parseInt(process.env.AFORGE_MAX_CONCURRENT ?? "", 10);
|
|
374
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_MAX_CONCURRENT;
|
|
375
|
+
}
|
|
376
|
+
function stripOpenRouterPrefix(model) {
|
|
377
|
+
return model.startsWith("openrouter/") ? model.slice("openrouter/".length) : model;
|
|
572
378
|
}
|
|
573
379
|
function parseEnvelope(stdout) {
|
|
574
380
|
try {
|
|
@@ -634,6 +440,7 @@ function stringOptions(value) {
|
|
|
634
440
|
var REASONING_VARIANTS, DEFAULT_TIMEOUT_SECONDS, LANDING_WINDOW_SECONDS, DEFAULT_MAX_CONCURRENT, ANSI_PATTERN, AFORGE_DEFAULT_MODEL, Semaphore, aforgeSemaphore, AforgeProvider;
|
|
635
441
|
var init_aforge = __esm({
|
|
636
442
|
"src/harness/providers/aforge.ts"() {
|
|
443
|
+
init_base();
|
|
637
444
|
init_types();
|
|
638
445
|
init_cli();
|
|
639
446
|
init_modelVariant();
|
|
@@ -685,9 +492,7 @@ var init_aforge = __esm({
|
|
|
685
492
|
return aforgeSemaphore.use(() => this.executeImpl(prompt, options));
|
|
686
493
|
}
|
|
687
494
|
async executeImpl(prompt, options) {
|
|
688
|
-
const
|
|
689
|
-
const cwd = typeof options.cwd === "string" ? options.cwd : void 0;
|
|
690
|
-
const root = projectDir ?? cwd ?? ".";
|
|
495
|
+
const root = resolveRoot(options) ?? ".";
|
|
691
496
|
const outerTimeout = timeoutSeconds();
|
|
692
497
|
const command = (process.env.AGENTFIELD_AFORGE_COMMAND ?? "exec").trim().toLowerCase();
|
|
693
498
|
if (command !== "do" && command !== "exec") {
|
|
@@ -815,7 +620,7 @@ var claude_exports = {};
|
|
|
815
620
|
__export(claude_exports, {
|
|
816
621
|
ClaudeCodeProvider: () => ClaudeCodeProvider
|
|
817
622
|
});
|
|
818
|
-
function
|
|
623
|
+
function isRecord2(value) {
|
|
819
624
|
return typeof value === "object" && value !== null;
|
|
820
625
|
}
|
|
821
626
|
function getString(record, key) {
|
|
@@ -829,6 +634,7 @@ function getNumber(record, key) {
|
|
|
829
634
|
var ClaudeCodeProvider;
|
|
830
635
|
var init_claude = __esm({
|
|
831
636
|
"src/harness/providers/claude.ts"() {
|
|
637
|
+
init_base();
|
|
832
638
|
init_types();
|
|
833
639
|
init_modelVariant();
|
|
834
640
|
ClaudeCodeProvider = class {
|
|
@@ -845,7 +651,8 @@ var init_claude = __esm({
|
|
|
845
651
|
const agentOptions = {};
|
|
846
652
|
const { model: modelValue } = resolveModelAndVariant(options);
|
|
847
653
|
if (modelValue !== void 0) agentOptions.model = modelValue;
|
|
848
|
-
|
|
654
|
+
const root = resolveRoot(options);
|
|
655
|
+
if (root !== void 0) agentOptions.cwd = root;
|
|
849
656
|
if (options.maxTurns !== void 0) agentOptions.maxTurns = options.maxTurns;
|
|
850
657
|
if (options.tools !== void 0) agentOptions.allowedTools = options.tools;
|
|
851
658
|
if (options.systemPrompt !== void 0) agentOptions.systemPrompt = options.systemPrompt;
|
|
@@ -870,7 +677,7 @@ var init_claude = __esm({
|
|
|
870
677
|
const startApi = Date.now();
|
|
871
678
|
try {
|
|
872
679
|
for await (const msg of sdk.query({ prompt, options: agentOptions })) {
|
|
873
|
-
const msgObj =
|
|
680
|
+
const msgObj = isRecord2(msg) ? msg : { raw: String(msg) };
|
|
874
681
|
messages.push(msgObj);
|
|
875
682
|
const msgType = getString(msgObj, "type") ?? "";
|
|
876
683
|
if (msgType === "result") {
|
|
@@ -889,7 +696,7 @@ var init_claude = __esm({
|
|
|
889
696
|
}
|
|
890
697
|
const turns = getNumber(msgObj, "num_turns");
|
|
891
698
|
numTurns = turns === void 0 ? messages.length : Math.trunc(turns);
|
|
892
|
-
if (
|
|
699
|
+
if (isRecord2(msgObj.usage)) {
|
|
893
700
|
usageObj = msgObj.usage;
|
|
894
701
|
inputTokens = getNumber(usageObj, "input_tokens");
|
|
895
702
|
outputTokens = getNumber(usageObj, "output_tokens");
|
|
@@ -898,19 +705,19 @@ var init_claude = __esm({
|
|
|
898
705
|
}
|
|
899
706
|
modelName = getString(msgObj, "model") ?? modelName;
|
|
900
707
|
} else if (msgType === "assistant") {
|
|
901
|
-
if (modelName === void 0 &&
|
|
708
|
+
if (modelName === void 0 && isRecord2(msgObj.message)) {
|
|
902
709
|
modelName = getString(msgObj.message, "model");
|
|
903
710
|
}
|
|
904
711
|
if (resultText !== void 0) continue;
|
|
905
712
|
let content = msgObj.content;
|
|
906
|
-
if (content === void 0 &&
|
|
713
|
+
if (content === void 0 && isRecord2(msgObj.message)) {
|
|
907
714
|
content = msgObj.message.content;
|
|
908
715
|
}
|
|
909
716
|
if (typeof content === "string") {
|
|
910
717
|
resultText = content;
|
|
911
718
|
} else if (Array.isArray(content)) {
|
|
912
719
|
for (const block of content) {
|
|
913
|
-
if (
|
|
720
|
+
if (isRecord2(block) && block.type === "text" && typeof block.text === "string") {
|
|
914
721
|
resultText = block.text;
|
|
915
722
|
}
|
|
916
723
|
}
|
|
@@ -956,6 +763,7 @@ __export(codex_exports, {
|
|
|
956
763
|
var CodexProvider;
|
|
957
764
|
var init_codex = __esm({
|
|
958
765
|
"src/harness/providers/codex.ts"() {
|
|
766
|
+
init_base();
|
|
959
767
|
init_types();
|
|
960
768
|
init_cli();
|
|
961
769
|
init_modelVariant();
|
|
@@ -966,8 +774,9 @@ var init_codex = __esm({
|
|
|
966
774
|
}
|
|
967
775
|
async execute(prompt, options) {
|
|
968
776
|
const cmd = [this.bin, "exec", "--json"];
|
|
969
|
-
|
|
970
|
-
|
|
777
|
+
const root = resolveRoot(options);
|
|
778
|
+
if (root) {
|
|
779
|
+
cmd.push("-C", root);
|
|
971
780
|
}
|
|
972
781
|
if (options.permissionMode === "auto") {
|
|
973
782
|
cmd.push("--full-auto");
|
|
@@ -984,7 +793,7 @@ var init_codex = __esm({
|
|
|
984
793
|
try {
|
|
985
794
|
const { stdout, stderr, exitCode } = await runCli(cmd, {
|
|
986
795
|
env: options.env,
|
|
987
|
-
cwd:
|
|
796
|
+
cwd: root
|
|
988
797
|
});
|
|
989
798
|
const events = parseJsonl(stdout);
|
|
990
799
|
const resultText = extractFinalText(events);
|
|
@@ -1037,6 +846,7 @@ __export(gemini_exports, {
|
|
|
1037
846
|
var GeminiProvider;
|
|
1038
847
|
var init_gemini = __esm({
|
|
1039
848
|
"src/harness/providers/gemini.ts"() {
|
|
849
|
+
init_base();
|
|
1040
850
|
init_types();
|
|
1041
851
|
init_cli();
|
|
1042
852
|
init_modelVariant();
|
|
@@ -1061,7 +871,7 @@ var init_gemini = __esm({
|
|
|
1061
871
|
try {
|
|
1062
872
|
const { stdout, stderr, exitCode } = await runCli(cmd, {
|
|
1063
873
|
env: options.env,
|
|
1064
|
-
cwd: options
|
|
874
|
+
cwd: resolveRoot(options)
|
|
1065
875
|
});
|
|
1066
876
|
const resultText = stdout.trim() || void 0;
|
|
1067
877
|
const isError = exitCode !== 0 && !resultText;
|
|
@@ -1113,6 +923,7 @@ function extractOpenCodeError(stderr) {
|
|
|
1113
923
|
var ANSI_PATTERN2, STDERR_ERROR_PATTERNS, OpenCodeProvider;
|
|
1114
924
|
var init_opencode = __esm({
|
|
1115
925
|
"src/harness/providers/opencode.ts"() {
|
|
926
|
+
init_base();
|
|
1116
927
|
init_types();
|
|
1117
928
|
init_cli();
|
|
1118
929
|
init_modelVariant();
|
|
@@ -1132,10 +943,9 @@ var init_opencode = __esm({
|
|
|
1132
943
|
}
|
|
1133
944
|
async execute(prompt, options) {
|
|
1134
945
|
const cmd = [this.bin, "run"];
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
cmd.push("--dir", options.project_dir);
|
|
946
|
+
const root = resolveRoot(options);
|
|
947
|
+
if (root) {
|
|
948
|
+
cmd.push("--dir", root);
|
|
1139
949
|
}
|
|
1140
950
|
const env = { ...options.env };
|
|
1141
951
|
const { model: modelValue, variant: variantValue } = resolveModelAndVariant(options);
|
|
@@ -1221,6 +1031,229 @@ ${prompt}`;
|
|
|
1221
1031
|
}
|
|
1222
1032
|
});
|
|
1223
1033
|
|
|
1034
|
+
// src/harness/providers/pi.ts
|
|
1035
|
+
var pi_exports = {};
|
|
1036
|
+
__export(pi_exports, {
|
|
1037
|
+
OMPProvider: () => OMPProvider,
|
|
1038
|
+
PiProvider: () => PiProvider
|
|
1039
|
+
});
|
|
1040
|
+
function normalizeTools(tools, flavor) {
|
|
1041
|
+
const normalized = [];
|
|
1042
|
+
for (const tool2 of tools) {
|
|
1043
|
+
let name = String(tool2).trim().toLowerCase();
|
|
1044
|
+
if (!name) {
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
if (name === "glob") {
|
|
1048
|
+
name = flavor === "omp" ? "glob" : "find";
|
|
1049
|
+
}
|
|
1050
|
+
if (!normalized.includes(name)) {
|
|
1051
|
+
normalized.push(name);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
return normalized;
|
|
1055
|
+
}
|
|
1056
|
+
function numberValue(value) {
|
|
1057
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
1058
|
+
}
|
|
1059
|
+
function textContent(message) {
|
|
1060
|
+
if (typeof message.content === "string") {
|
|
1061
|
+
return message.content || void 0;
|
|
1062
|
+
}
|
|
1063
|
+
if (!Array.isArray(message.content)) {
|
|
1064
|
+
return void 0;
|
|
1065
|
+
}
|
|
1066
|
+
const text2 = message.content.filter((part) => typeof part === "object" && part !== null).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join("");
|
|
1067
|
+
return text2 || void 0;
|
|
1068
|
+
}
|
|
1069
|
+
function parsePiEvents(events, configuredModel) {
|
|
1070
|
+
let result;
|
|
1071
|
+
let sessionId = "";
|
|
1072
|
+
let numTurns = 0;
|
|
1073
|
+
let inputTokens = 0;
|
|
1074
|
+
let outputTokens = 0;
|
|
1075
|
+
let cacheReadTokens = 0;
|
|
1076
|
+
let cacheCreationTokens = 0;
|
|
1077
|
+
let totalCostUsd;
|
|
1078
|
+
let reportedModel;
|
|
1079
|
+
let providerError;
|
|
1080
|
+
for (const event of events) {
|
|
1081
|
+
if (event.type === "session" && typeof event.id === "string") {
|
|
1082
|
+
sessionId = event.id;
|
|
1083
|
+
}
|
|
1084
|
+
if (event.type === "turn_end") {
|
|
1085
|
+
numTurns += 1;
|
|
1086
|
+
}
|
|
1087
|
+
if (event.type !== "message_end" || typeof event.message !== "object" || event.message === null) {
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
const message = event.message;
|
|
1091
|
+
if (message.role !== "assistant") {
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1094
|
+
result = textContent(message) ?? result;
|
|
1095
|
+
if (typeof message.model === "string") {
|
|
1096
|
+
reportedModel = message.model;
|
|
1097
|
+
}
|
|
1098
|
+
if (typeof message.usage === "object" && message.usage !== null) {
|
|
1099
|
+
const usage = message.usage;
|
|
1100
|
+
inputTokens += numberValue(usage.input);
|
|
1101
|
+
outputTokens += numberValue(usage.output);
|
|
1102
|
+
cacheReadTokens += numberValue(usage.cacheRead);
|
|
1103
|
+
cacheCreationTokens += numberValue(usage.cacheWrite);
|
|
1104
|
+
if (typeof usage.cost === "object" && usage.cost !== null) {
|
|
1105
|
+
const cost = usage.cost.total;
|
|
1106
|
+
if (typeof cost === "number" && Number.isFinite(cost)) {
|
|
1107
|
+
totalCostUsd = (totalCostUsd ?? 0) + cost;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
if (message.stopReason === "error" || message.stopReason === "aborted") {
|
|
1112
|
+
providerError = String(
|
|
1113
|
+
message.errorMessage ?? message.error ?? `Pi stopped with reason ${String(message.stopReason)}.`
|
|
1114
|
+
);
|
|
1115
|
+
} else {
|
|
1116
|
+
providerError = void 0;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
if (numTurns === 0 && result) {
|
|
1120
|
+
numTurns = 1;
|
|
1121
|
+
}
|
|
1122
|
+
return {
|
|
1123
|
+
result,
|
|
1124
|
+
providerError,
|
|
1125
|
+
metrics: createMetrics({
|
|
1126
|
+
numTurns,
|
|
1127
|
+
totalCostUsd,
|
|
1128
|
+
sessionId,
|
|
1129
|
+
inputTokens,
|
|
1130
|
+
outputTokens,
|
|
1131
|
+
cacheReadTokens,
|
|
1132
|
+
cacheCreationTokens,
|
|
1133
|
+
totalTokens: inputTokens + outputTokens,
|
|
1134
|
+
model: configuredModel ?? reportedModel
|
|
1135
|
+
})
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
var READ_ONLY_TOOLS, ANSI_PATTERN3, PiFamilyProvider, PiProvider, OMPProvider;
|
|
1139
|
+
var init_pi = __esm({
|
|
1140
|
+
"src/harness/providers/pi.ts"() {
|
|
1141
|
+
init_base();
|
|
1142
|
+
init_types();
|
|
1143
|
+
init_cli();
|
|
1144
|
+
init_modelVariant();
|
|
1145
|
+
READ_ONLY_TOOLS = /* @__PURE__ */ new Set(["read", "grep", "find", "glob", "ls", "lsp"]);
|
|
1146
|
+
ANSI_PATTERN3 = /\x1B\[[0-?]*[ -/]*[@-~]/g;
|
|
1147
|
+
PiFamilyProvider = class {
|
|
1148
|
+
constructor(flavor, bin) {
|
|
1149
|
+
this.flavor = flavor;
|
|
1150
|
+
this.bin = bin;
|
|
1151
|
+
}
|
|
1152
|
+
flavor;
|
|
1153
|
+
bin;
|
|
1154
|
+
async execute(prompt, options) {
|
|
1155
|
+
const cmd = [this.bin, "--print", "--mode", "json"];
|
|
1156
|
+
const root = resolveRoot(options);
|
|
1157
|
+
if (this.flavor === "omp" && root) {
|
|
1158
|
+
cmd.push("--cwd", root);
|
|
1159
|
+
}
|
|
1160
|
+
const { model, variant } = resolveModelAndVariant(options);
|
|
1161
|
+
if (model) {
|
|
1162
|
+
cmd.push("--model", model);
|
|
1163
|
+
}
|
|
1164
|
+
if (variant) {
|
|
1165
|
+
cmd.push("--thinking", variant);
|
|
1166
|
+
}
|
|
1167
|
+
if (typeof options.systemPrompt === "string" && options.systemPrompt.trim()) {
|
|
1168
|
+
cmd.push("--system-prompt", options.systemPrompt.trim());
|
|
1169
|
+
}
|
|
1170
|
+
if (typeof options.resumeSessionId === "string" && options.resumeSessionId) {
|
|
1171
|
+
cmd.push(this.flavor === "omp" ? "--resume" : "--session", options.resumeSessionId);
|
|
1172
|
+
}
|
|
1173
|
+
if (options.permissionMode === "auto") {
|
|
1174
|
+
if (this.flavor === "omp") {
|
|
1175
|
+
cmd.push("--auto-approve");
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
const explicitTools = Array.isArray(options.tools);
|
|
1179
|
+
let tools = explicitTools ? normalizeTools(options.tools, this.flavor) : [];
|
|
1180
|
+
if (options.permissionMode === "plan") {
|
|
1181
|
+
tools = tools.filter((tool2) => READ_ONLY_TOOLS.has(tool2));
|
|
1182
|
+
if (tools.length === 0) {
|
|
1183
|
+
tools = ["read", "grep", this.flavor === "omp" ? "glob" : "find"];
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
if (explicitTools || options.permissionMode === "plan") {
|
|
1187
|
+
if (tools.length > 0) {
|
|
1188
|
+
cmd.push("--tools", tools.join(","));
|
|
1189
|
+
} else {
|
|
1190
|
+
cmd.push("--no-tools");
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
const env = { ...options.env };
|
|
1194
|
+
const startApi = Date.now();
|
|
1195
|
+
try {
|
|
1196
|
+
const { stdout, stderr, exitCode } = await runCli(cmd, {
|
|
1197
|
+
env,
|
|
1198
|
+
cwd: root,
|
|
1199
|
+
inputText: prompt,
|
|
1200
|
+
timeout: typeof options.timeout === "number" ? options.timeout * 1e3 : void 0
|
|
1201
|
+
});
|
|
1202
|
+
const events = parseJsonl(stdout);
|
|
1203
|
+
const parsed = parsePiEvents(events, model);
|
|
1204
|
+
parsed.metrics.durationApiMs = Date.now() - startApi;
|
|
1205
|
+
const cleanStderr = stderr.trim().replace(ANSI_PATTERN3, "").slice(0, 1e3);
|
|
1206
|
+
let errorMessage;
|
|
1207
|
+
let failureType;
|
|
1208
|
+
if (exitCode < 0) {
|
|
1209
|
+
errorMessage = `Process killed by signal ${-exitCode}.`;
|
|
1210
|
+
failureType = "crash";
|
|
1211
|
+
} else if (exitCode !== 0) {
|
|
1212
|
+
errorMessage = cleanStderr || parsed.providerError || `Process exited with code ${exitCode}.`;
|
|
1213
|
+
failureType = "crash";
|
|
1214
|
+
} else if (parsed.providerError) {
|
|
1215
|
+
errorMessage = parsed.providerError;
|
|
1216
|
+
failureType = "api_error";
|
|
1217
|
+
} else if (!parsed.result) {
|
|
1218
|
+
errorMessage = cleanStderr || `${this.flavor} exited successfully without an assistant response.`;
|
|
1219
|
+
failureType = "no_output";
|
|
1220
|
+
} else {
|
|
1221
|
+
failureType = "none";
|
|
1222
|
+
}
|
|
1223
|
+
return createRawResult({
|
|
1224
|
+
result: parsed.result,
|
|
1225
|
+
messages: events,
|
|
1226
|
+
metrics: parsed.metrics,
|
|
1227
|
+
isError: failureType !== "none",
|
|
1228
|
+
errorMessage,
|
|
1229
|
+
failureType,
|
|
1230
|
+
returnCode: exitCode
|
|
1231
|
+
});
|
|
1232
|
+
} catch (error) {
|
|
1233
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1234
|
+
const binaryMissing = message.includes("ENOENT");
|
|
1235
|
+
return createRawResult({
|
|
1236
|
+
isError: true,
|
|
1237
|
+
errorMessage: binaryMissing ? `${this.flavor === "omp" ? "OMP" : "Pi"} binary not found at '${this.bin}'. ${this.flavor === "omp" ? "Install: curl -fsSL https://omp.sh/install | sh" : "Install: npm install -g --ignore-scripts @earendil-works/pi-coding-agent"}` : message,
|
|
1238
|
+
failureType: /timed out|deadline exceeded|no progress/i.test(message) ? "timeout" : "crash",
|
|
1239
|
+
metrics: createMetrics({ durationApiMs: Date.now() - startApi })
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
PiProvider = class extends PiFamilyProvider {
|
|
1245
|
+
constructor(binPath = "pi") {
|
|
1246
|
+
super("pi", binPath);
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
OMPProvider = class extends PiFamilyProvider {
|
|
1250
|
+
constructor(binPath = "omp") {
|
|
1251
|
+
super("omp", binPath);
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1224
1257
|
// src/harness/providers/factory.ts
|
|
1225
1258
|
function resolveProviderName(explicit) {
|
|
1226
1259
|
const explicitName = explicit?.trim();
|
|
@@ -1257,126 +1290,363 @@ async function buildProvider(config) {
|
|
|
1257
1290
|
const { OpenCodeProvider: OpenCodeProvider2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
|
|
1258
1291
|
return new OpenCodeProvider2(config.opencodeBin ?? "opencode");
|
|
1259
1292
|
}
|
|
1293
|
+
if (provider === "pi") {
|
|
1294
|
+
const { PiProvider: PiProvider2 } = await Promise.resolve().then(() => (init_pi(), pi_exports));
|
|
1295
|
+
return new PiProvider2(config.piBin ?? "pi");
|
|
1296
|
+
}
|
|
1297
|
+
if (provider === "omp") {
|
|
1298
|
+
const { OMPProvider: OMPProvider2 } = await Promise.resolve().then(() => (init_pi(), pi_exports));
|
|
1299
|
+
return new OMPProvider2(config.ompBin ?? "omp");
|
|
1300
|
+
}
|
|
1260
1301
|
throw new Error(`Provider "${provider}" is not yet implemented.`);
|
|
1261
1302
|
}
|
|
1262
1303
|
var SUPPORTED_PROVIDERS, DEFAULT_HARNESS_PROVIDER, HARNESS_PROVIDER_ENV_VAR;
|
|
1263
1304
|
var init_factory = __esm({
|
|
1264
1305
|
"src/harness/providers/factory.ts"() {
|
|
1265
|
-
SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set([
|
|
1306
|
+
SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set([
|
|
1307
|
+
"aforge",
|
|
1308
|
+
"claude-code",
|
|
1309
|
+
"codex",
|
|
1310
|
+
"gemini",
|
|
1311
|
+
"omp",
|
|
1312
|
+
"opencode",
|
|
1313
|
+
"pi"
|
|
1314
|
+
]);
|
|
1266
1315
|
DEFAULT_HARNESS_PROVIDER = "aforge";
|
|
1267
1316
|
HARNESS_PROVIDER_ENV_VAR = "AGENTFIELD_HARNESS_PROVIDER";
|
|
1268
1317
|
}
|
|
1269
1318
|
});
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
var runner_exports = {};
|
|
1273
|
-
__export(runner_exports, {
|
|
1274
|
-
HarnessRunner: () => HarnessRunner
|
|
1275
|
-
});
|
|
1276
|
-
function tokenMetrics(raw) {
|
|
1277
|
-
const { inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, model } = raw.metrics;
|
|
1278
|
-
return { inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, model };
|
|
1319
|
+
function isRecord3(value) {
|
|
1320
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1279
1321
|
}
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
constructor(config) {
|
|
1304
|
-
this.config = config;
|
|
1305
|
-
}
|
|
1306
|
-
config;
|
|
1307
|
-
async run(prompt, options = {}) {
|
|
1308
|
-
const { schema, ...rest } = options;
|
|
1309
|
-
const resolved = this.resolveOptions(this.config, rest);
|
|
1310
|
-
resolved.provider = resolveProviderName(resolved.provider);
|
|
1311
|
-
const provider = await this.buildProvider(resolved.provider, resolved);
|
|
1312
|
-
const cwd = resolved.cwd ?? ".";
|
|
1313
|
-
const outputRoot = resolved.projectDir ?? cwd;
|
|
1314
|
-
let outputDir;
|
|
1315
|
-
if (schema !== void 0) {
|
|
1316
|
-
fs.mkdirSync(outputRoot, { recursive: true });
|
|
1317
|
-
outputDir = fs.mkdtempSync(path4__default.join(outputRoot, ".agentfield-out-"));
|
|
1318
|
-
}
|
|
1319
|
-
const effectivePrompt = schema === void 0 ? prompt : `${prompt}${buildPromptSuffix(schema, outputDir)}`;
|
|
1320
|
-
const startTime = Date.now();
|
|
1321
|
-
try {
|
|
1322
|
-
const raw = await this.executeWithRetry(provider, effectivePrompt, resolved);
|
|
1323
|
-
if (schema !== void 0) {
|
|
1324
|
-
return this.handleSchemaOutput(raw, schema, outputDir, startTime);
|
|
1325
|
-
}
|
|
1326
|
-
return createHarnessResult({
|
|
1327
|
-
result: raw.result,
|
|
1328
|
-
isError: raw.isError,
|
|
1329
|
-
errorMessage: raw.errorMessage,
|
|
1330
|
-
failureType: raw.failureType,
|
|
1331
|
-
returnCode: raw.returnCode,
|
|
1332
|
-
costUsd: raw.metrics.totalCostUsd,
|
|
1333
|
-
numTurns: raw.metrics.numTurns,
|
|
1334
|
-
durationMs: Date.now() - startTime,
|
|
1335
|
-
sessionId: raw.metrics.sessionId,
|
|
1336
|
-
messages: raw.messages,
|
|
1337
|
-
...tokenMetrics(raw)
|
|
1338
|
-
});
|
|
1339
|
-
} finally {
|
|
1340
|
-
if (schema !== void 0) {
|
|
1341
|
-
cleanupTempFiles(outputDir);
|
|
1342
|
-
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
1343
|
-
}
|
|
1322
|
+
function isZod4Schema(value) {
|
|
1323
|
+
if (!isRecord3(value) || !("_zod" in value) || !isRecord3(value._zod)) {
|
|
1324
|
+
return false;
|
|
1325
|
+
}
|
|
1326
|
+
return isRecord3(value._zod.def);
|
|
1327
|
+
}
|
|
1328
|
+
function getZod4Converter() {
|
|
1329
|
+
if (zod4Converter !== void 0) {
|
|
1330
|
+
return zod4Converter;
|
|
1331
|
+
}
|
|
1332
|
+
const requires = [];
|
|
1333
|
+
try {
|
|
1334
|
+
requires.push(createRequire(path4__default.join(process.cwd(), "package.json")));
|
|
1335
|
+
} catch {
|
|
1336
|
+
}
|
|
1337
|
+
requires.push(createRequire(import.meta.url));
|
|
1338
|
+
for (const req of requires) {
|
|
1339
|
+
for (const moduleName of ["zod", "zod/v4"]) {
|
|
1340
|
+
try {
|
|
1341
|
+
const mod = req(moduleName);
|
|
1342
|
+
if (typeof mod.toJSONSchema === "function") {
|
|
1343
|
+
zod4Converter = mod.toJSONSchema;
|
|
1344
|
+
return zod4Converter;
|
|
1344
1345
|
}
|
|
1346
|
+
} catch {
|
|
1345
1347
|
}
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
zod4Converter = null;
|
|
1351
|
+
return zod4Converter;
|
|
1352
|
+
}
|
|
1353
|
+
function zod4ToJsonSchema(schema) {
|
|
1354
|
+
const converter = getZod4Converter();
|
|
1355
|
+
if (converter === null) {
|
|
1356
|
+
throw new TypeError(
|
|
1357
|
+
'Cannot convert a Zod 4 schema: install the "zod" package with its "zod/v4" entry point available.'
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
return converter(schema);
|
|
1361
|
+
}
|
|
1362
|
+
var zod4Converter;
|
|
1363
|
+
var init_zod_schema = __esm({
|
|
1364
|
+
"src/utils/zod-schema.ts"() {
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
function getZodConverter() {
|
|
1368
|
+
if (zodConverter !== void 0) {
|
|
1369
|
+
return zodConverter;
|
|
1370
|
+
}
|
|
1371
|
+
try {
|
|
1372
|
+
const require2 = createRequire(import.meta.url);
|
|
1373
|
+
const mod = require2("zod-to-json-schema");
|
|
1374
|
+
zodConverter = mod.zodToJsonSchema ?? mod.default ?? null;
|
|
1375
|
+
} catch {
|
|
1376
|
+
zodConverter = null;
|
|
1377
|
+
}
|
|
1378
|
+
return zodConverter;
|
|
1379
|
+
}
|
|
1380
|
+
function isRecord4(value) {
|
|
1381
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1382
|
+
}
|
|
1383
|
+
function hasJsonSchema(value) {
|
|
1384
|
+
return isRecord4(value) && typeof value.jsonSchema === "function";
|
|
1385
|
+
}
|
|
1386
|
+
function hasParse(value) {
|
|
1387
|
+
return isRecord4(value) && typeof value.parse === "function";
|
|
1388
|
+
}
|
|
1389
|
+
function estimateTokens(text2) {
|
|
1390
|
+
return Math.floor(text2.length / 4);
|
|
1391
|
+
}
|
|
1392
|
+
function writeSchemaFile(schemaJson, cwd) {
|
|
1393
|
+
const filePath = getSchemaPath(cwd);
|
|
1394
|
+
fs.mkdirSync(path4__default.dirname(filePath), { recursive: true });
|
|
1395
|
+
const fd = fs.openSync(filePath, "w", 384);
|
|
1396
|
+
try {
|
|
1397
|
+
fs.writeFileSync(fd, schemaJson, "utf8");
|
|
1398
|
+
} finally {
|
|
1399
|
+
fs.closeSync(fd);
|
|
1400
|
+
}
|
|
1401
|
+
return filePath;
|
|
1402
|
+
}
|
|
1403
|
+
function validateAgainstSchema(data, schema) {
|
|
1404
|
+
if (hasParse(schema)) {
|
|
1405
|
+
return schema.parse(data);
|
|
1406
|
+
}
|
|
1407
|
+
return data;
|
|
1408
|
+
}
|
|
1409
|
+
function getOutputPath(cwd) {
|
|
1410
|
+
return path4__default.join(cwd, OUTPUT_FILENAME);
|
|
1411
|
+
}
|
|
1412
|
+
function getSchemaPath(cwd) {
|
|
1413
|
+
return path4__default.join(cwd, SCHEMA_FILENAME);
|
|
1414
|
+
}
|
|
1415
|
+
function schemaToJsonSchema(schema) {
|
|
1416
|
+
if (isZod4Schema(schema)) {
|
|
1417
|
+
return zod4ToJsonSchema(schema);
|
|
1418
|
+
}
|
|
1419
|
+
if (isRecord4(schema)) {
|
|
1420
|
+
if ("type" in schema || "properties" in schema || "$schema" in schema) {
|
|
1421
|
+
return schema;
|
|
1422
|
+
}
|
|
1423
|
+
if (hasJsonSchema(schema)) {
|
|
1424
|
+
return schema.jsonSchema();
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
const converter = getZodConverter();
|
|
1428
|
+
if (converter !== null) {
|
|
1429
|
+
return converter(schema);
|
|
1430
|
+
}
|
|
1431
|
+
throw new TypeError(
|
|
1432
|
+
'Unsupported schema type. Expected a Zod schema (requires "zod-to-json-schema" for Zod 3 or "zod" for Zod 4), JSON schema object, or jsonSchema() provider.'
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
function isLargeSchema(schemaJson) {
|
|
1436
|
+
return estimateTokens(schemaJson) > LARGE_SCHEMA_TOKEN_THRESHOLD;
|
|
1437
|
+
}
|
|
1438
|
+
function buildPromptSuffix(schema, cwd) {
|
|
1439
|
+
const jsonSchema2 = schemaToJsonSchema(schema);
|
|
1440
|
+
const schemaJson = JSON.stringify(jsonSchema2, null, 2);
|
|
1441
|
+
const outputPath = getOutputPath(cwd);
|
|
1442
|
+
if (isLargeSchema(schemaJson)) {
|
|
1443
|
+
const schemaPath = writeSchemaFile(schemaJson, cwd);
|
|
1444
|
+
return `
|
|
1445
|
+
|
|
1446
|
+
---
|
|
1447
|
+
OUTPUT REQUIREMENTS:
|
|
1448
|
+
Read the JSON Schema at: ${schemaPath}
|
|
1449
|
+
Write your final answer as valid JSON conforming to that schema to: ${outputPath}
|
|
1450
|
+
Do not include any text outside the JSON in that file. Do not wrap in markdown fences.`;
|
|
1451
|
+
}
|
|
1452
|
+
return `
|
|
1453
|
+
|
|
1454
|
+
---
|
|
1455
|
+
OUTPUT REQUIREMENTS:
|
|
1456
|
+
Write your final answer as valid JSON to the file: ${outputPath}
|
|
1457
|
+
The JSON must conform to this schema:
|
|
1458
|
+
${schemaJson}
|
|
1459
|
+
Do not include any text outside the JSON in that file. Do not wrap in markdown fences.`;
|
|
1460
|
+
}
|
|
1461
|
+
function cosmeticRepair(raw) {
|
|
1462
|
+
let text2 = raw.trim();
|
|
1463
|
+
const fenceMatch = text2.match(/^```(?:json)?\s*\n([\s\S]*?)```\s*$/);
|
|
1464
|
+
if (fenceMatch) {
|
|
1465
|
+
text2 = fenceMatch[1].trim();
|
|
1466
|
+
}
|
|
1467
|
+
if (text2.length > 0 && text2[0] !== "{" && text2[0] !== "[") {
|
|
1468
|
+
const firstJsonCharIndex = [...text2].findIndex((char) => char === "{" || char === "[");
|
|
1469
|
+
if (firstJsonCharIndex >= 0) {
|
|
1470
|
+
text2 = text2.slice(firstJsonCharIndex);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
text2 = text2.replace(/,\s*([}\]])/g, "$1");
|
|
1474
|
+
const openBraces = (text2.match(/{/g)?.length ?? 0) - (text2.match(/}/g)?.length ?? 0);
|
|
1475
|
+
const openBrackets = (text2.match(/\[/g)?.length ?? 0) - (text2.match(/\]/g)?.length ?? 0);
|
|
1476
|
+
if (openBraces > 0 || openBrackets > 0) {
|
|
1477
|
+
text2 += "]".repeat(openBrackets) + "}".repeat(openBraces);
|
|
1478
|
+
}
|
|
1479
|
+
return text2;
|
|
1480
|
+
}
|
|
1481
|
+
function readAndParse(filePath) {
|
|
1482
|
+
try {
|
|
1483
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
1484
|
+
if (content.trim().length === 0) {
|
|
1485
|
+
return null;
|
|
1486
|
+
}
|
|
1487
|
+
return JSON.parse(content);
|
|
1488
|
+
} catch {
|
|
1489
|
+
return null;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
function readRepairAndParse(filePath) {
|
|
1493
|
+
try {
|
|
1494
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
1495
|
+
if (content.trim().length === 0) {
|
|
1496
|
+
return null;
|
|
1497
|
+
}
|
|
1498
|
+
return JSON.parse(cosmeticRepair(content));
|
|
1499
|
+
} catch {
|
|
1500
|
+
return null;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
function parseAndValidate(filePath, schema) {
|
|
1504
|
+
const parsed = readAndParse(filePath);
|
|
1505
|
+
if (parsed !== null) {
|
|
1506
|
+
try {
|
|
1507
|
+
return validateAgainstSchema(parsed, schema);
|
|
1508
|
+
} catch {
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
const repaired = readRepairAndParse(filePath);
|
|
1512
|
+
if (repaired !== null) {
|
|
1513
|
+
try {
|
|
1514
|
+
return validateAgainstSchema(repaired, schema);
|
|
1515
|
+
} catch {
|
|
1516
|
+
return null;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
return null;
|
|
1520
|
+
}
|
|
1521
|
+
function cleanupTempFiles(cwd) {
|
|
1522
|
+
for (const filename of [OUTPUT_FILENAME, SCHEMA_FILENAME]) {
|
|
1523
|
+
try {
|
|
1524
|
+
fs.unlinkSync(path4__default.join(cwd, filename));
|
|
1525
|
+
} catch {
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
var OUTPUT_FILENAME, SCHEMA_FILENAME, LARGE_SCHEMA_TOKEN_THRESHOLD, zodConverter;
|
|
1530
|
+
var init_schema = __esm({
|
|
1531
|
+
"src/harness/schema.ts"() {
|
|
1532
|
+
init_zod_schema();
|
|
1533
|
+
OUTPUT_FILENAME = ".agentfield_output.json";
|
|
1534
|
+
SCHEMA_FILENAME = ".agentfield_schema.json";
|
|
1535
|
+
LARGE_SCHEMA_TOKEN_THRESHOLD = 4e3;
|
|
1536
|
+
}
|
|
1537
|
+
});
|
|
1538
|
+
|
|
1539
|
+
// src/harness/runner.ts
|
|
1540
|
+
var runner_exports = {};
|
|
1541
|
+
__export(runner_exports, {
|
|
1542
|
+
HarnessRunner: () => HarnessRunner
|
|
1543
|
+
});
|
|
1544
|
+
function tokenMetrics(raw) {
|
|
1545
|
+
const { inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, model } = raw.metrics;
|
|
1546
|
+
return { inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, model };
|
|
1547
|
+
}
|
|
1548
|
+
var TRANSIENT_PATTERNS, HarnessRunner;
|
|
1549
|
+
var init_runner = __esm({
|
|
1550
|
+
"src/harness/runner.ts"() {
|
|
1551
|
+
init_schema();
|
|
1552
|
+
init_factory();
|
|
1553
|
+
init_base();
|
|
1554
|
+
init_types();
|
|
1555
|
+
TRANSIENT_PATTERNS = [
|
|
1556
|
+
"rate limit",
|
|
1557
|
+
"rate_limit",
|
|
1558
|
+
"overloaded",
|
|
1559
|
+
"timeout",
|
|
1560
|
+
"timed out",
|
|
1561
|
+
"connection reset",
|
|
1562
|
+
"connection refused",
|
|
1563
|
+
"temporarily unavailable",
|
|
1564
|
+
"service unavailable",
|
|
1565
|
+
"503",
|
|
1566
|
+
"502",
|
|
1567
|
+
"504",
|
|
1568
|
+
"internal server error",
|
|
1569
|
+
"500"
|
|
1570
|
+
];
|
|
1571
|
+
HarnessRunner = class {
|
|
1572
|
+
constructor(config) {
|
|
1573
|
+
this.config = config;
|
|
1574
|
+
}
|
|
1575
|
+
config;
|
|
1576
|
+
async run(prompt, options = {}) {
|
|
1577
|
+
const { schema, ...rest } = options;
|
|
1578
|
+
const resolved = this.resolveOptions(this.config, rest);
|
|
1579
|
+
resolved.provider = resolveProviderName(resolved.provider);
|
|
1580
|
+
const provider = await this.buildProvider(resolved.provider, resolved);
|
|
1581
|
+
const outputRoot = resolveRoot(resolved) ?? ".";
|
|
1582
|
+
let outputDir;
|
|
1583
|
+
if (schema !== void 0) {
|
|
1584
|
+
fs.mkdirSync(outputRoot, { recursive: true });
|
|
1585
|
+
outputDir = fs.mkdtempSync(path4__default.join(outputRoot, ".agentfield-out-"));
|
|
1586
|
+
}
|
|
1587
|
+
const effectivePrompt = schema === void 0 ? prompt : `${prompt}${buildPromptSuffix(schema, outputDir)}`;
|
|
1588
|
+
const startTime = Date.now();
|
|
1589
|
+
try {
|
|
1590
|
+
const raw = await this.executeWithRetry(provider, effectivePrompt, resolved);
|
|
1591
|
+
if (schema !== void 0) {
|
|
1592
|
+
return this.handleSchemaOutput(raw, schema, outputDir, startTime);
|
|
1593
|
+
}
|
|
1594
|
+
return createHarnessResult({
|
|
1595
|
+
result: raw.result,
|
|
1596
|
+
isError: raw.isError,
|
|
1597
|
+
errorMessage: raw.errorMessage,
|
|
1598
|
+
failureType: raw.failureType,
|
|
1599
|
+
returnCode: raw.returnCode,
|
|
1600
|
+
costUsd: raw.metrics.totalCostUsd,
|
|
1601
|
+
numTurns: raw.metrics.numTurns,
|
|
1602
|
+
durationMs: Date.now() - startTime,
|
|
1603
|
+
sessionId: raw.metrics.sessionId,
|
|
1604
|
+
messages: raw.messages,
|
|
1605
|
+
...tokenMetrics(raw)
|
|
1606
|
+
});
|
|
1607
|
+
} finally {
|
|
1608
|
+
if (schema !== void 0) {
|
|
1609
|
+
cleanupTempFiles(outputDir);
|
|
1610
|
+
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
resolveOptions(config, overrides) {
|
|
1615
|
+
const out = {};
|
|
1616
|
+
if (config) {
|
|
1617
|
+
for (const key of [
|
|
1618
|
+
"provider",
|
|
1619
|
+
"model",
|
|
1620
|
+
"variant",
|
|
1621
|
+
"maxTurns",
|
|
1622
|
+
"maxBudgetUsd",
|
|
1623
|
+
"maxRetries",
|
|
1624
|
+
"initialDelay",
|
|
1625
|
+
"maxDelay",
|
|
1626
|
+
"backoffFactor",
|
|
1627
|
+
"tools",
|
|
1628
|
+
"permissionMode",
|
|
1629
|
+
"systemPrompt",
|
|
1630
|
+
"env",
|
|
1631
|
+
"cwd",
|
|
1632
|
+
"projectDir",
|
|
1633
|
+
"aforgeBin",
|
|
1634
|
+
"codexBin",
|
|
1635
|
+
"geminiBin",
|
|
1636
|
+
"opencodeBin",
|
|
1637
|
+
"piBin",
|
|
1638
|
+
"ompBin"
|
|
1639
|
+
]) {
|
|
1640
|
+
const value = config[key];
|
|
1641
|
+
if (value !== void 0 && value !== null) {
|
|
1642
|
+
out[key] = value;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
1647
|
+
if (value !== void 0 && value !== null) {
|
|
1648
|
+
out[key] = value;
|
|
1649
|
+
}
|
|
1380
1650
|
}
|
|
1381
1651
|
return out;
|
|
1382
1652
|
}
|
|
@@ -2889,103 +3159,391 @@ var StatelessRateLimiter = class {
|
|
|
2889
3159
|
return x / 4294967296;
|
|
2890
3160
|
};
|
|
2891
3161
|
}
|
|
2892
|
-
_calculateBackoffDelay(attempt, retryAfter) {
|
|
2893
|
-
let baseDelay;
|
|
2894
|
-
if (retryAfter && retryAfter <= this.maxDelay) {
|
|
2895
|
-
baseDelay = retryAfter;
|
|
2896
|
-
} else {
|
|
2897
|
-
baseDelay = Math.min(this.baseDelay * 2 ** attempt, this.maxDelay);
|
|
2898
|
-
}
|
|
2899
|
-
const jitterRange = baseDelay * this.jitterFactor;
|
|
2900
|
-
const rng = this._createJitterRng(this._containerSeed + attempt);
|
|
2901
|
-
const jitter = (rng() * 2 - 1) * jitterRange;
|
|
2902
|
-
const delay = Math.max(0.1, baseDelay + jitter);
|
|
2903
|
-
return delay;
|
|
3162
|
+
_calculateBackoffDelay(attempt, retryAfter) {
|
|
3163
|
+
let baseDelay;
|
|
3164
|
+
if (retryAfter && retryAfter <= this.maxDelay) {
|
|
3165
|
+
baseDelay = retryAfter;
|
|
3166
|
+
} else {
|
|
3167
|
+
baseDelay = Math.min(this.baseDelay * 2 ** attempt, this.maxDelay);
|
|
3168
|
+
}
|
|
3169
|
+
const jitterRange = baseDelay * this.jitterFactor;
|
|
3170
|
+
const rng = this._createJitterRng(this._containerSeed + attempt);
|
|
3171
|
+
const jitter = (rng() * 2 - 1) * jitterRange;
|
|
3172
|
+
const delay = Math.max(0.1, baseDelay + jitter);
|
|
3173
|
+
return delay;
|
|
3174
|
+
}
|
|
3175
|
+
_checkCircuitBreaker() {
|
|
3176
|
+
if (this._circuitOpenTime === void 0) {
|
|
3177
|
+
return false;
|
|
3178
|
+
}
|
|
3179
|
+
if (this._now() - this._circuitOpenTime > this.circuitBreakerTimeout) {
|
|
3180
|
+
this._circuitOpenTime = void 0;
|
|
3181
|
+
this._consecutiveFailures = 0;
|
|
3182
|
+
return false;
|
|
3183
|
+
}
|
|
3184
|
+
return true;
|
|
3185
|
+
}
|
|
3186
|
+
_updateCircuitBreaker(success) {
|
|
3187
|
+
if (success) {
|
|
3188
|
+
this._consecutiveFailures = 0;
|
|
3189
|
+
this._circuitOpenTime = void 0;
|
|
3190
|
+
return;
|
|
3191
|
+
}
|
|
3192
|
+
this._consecutiveFailures += 1;
|
|
3193
|
+
if (this._consecutiveFailures >= this.circuitBreakerThreshold && this._circuitOpenTime === void 0) {
|
|
3194
|
+
this._circuitOpenTime = this._now();
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
async _sleep(delaySeconds) {
|
|
3198
|
+
await new Promise((resolve3) => setTimeout(resolve3, delaySeconds * 1e3));
|
|
3199
|
+
}
|
|
3200
|
+
_now() {
|
|
3201
|
+
return Date.now() / 1e3;
|
|
3202
|
+
}
|
|
3203
|
+
async executeWithRetry(fn) {
|
|
3204
|
+
if (this._checkCircuitBreaker()) {
|
|
3205
|
+
throw new RateLimitError(
|
|
3206
|
+
`Circuit breaker is open. Too many consecutive rate limit failures. Will retry after ${this.circuitBreakerTimeout} seconds.`
|
|
3207
|
+
);
|
|
3208
|
+
}
|
|
3209
|
+
let lastError;
|
|
3210
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt += 1) {
|
|
3211
|
+
try {
|
|
3212
|
+
const result = await fn();
|
|
3213
|
+
this._updateCircuitBreaker(true);
|
|
3214
|
+
return result;
|
|
3215
|
+
} catch (error) {
|
|
3216
|
+
lastError = error;
|
|
3217
|
+
if (!this._isRateLimitError(error)) {
|
|
3218
|
+
throw error;
|
|
3219
|
+
}
|
|
3220
|
+
this._updateCircuitBreaker(false);
|
|
3221
|
+
if (attempt >= this.maxRetries) {
|
|
3222
|
+
break;
|
|
3223
|
+
}
|
|
3224
|
+
const retryAfter = this._extractRetryAfter(error);
|
|
3225
|
+
const delay = this._calculateBackoffDelay(attempt, retryAfter);
|
|
3226
|
+
await this._sleep(delay);
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
throw new RateLimitError(
|
|
3230
|
+
`Rate limit retries exhausted after ${this.maxRetries} attempts. Last error: ${String(lastError)}`,
|
|
3231
|
+
this._extractRetryAfter(lastError)
|
|
3232
|
+
);
|
|
3233
|
+
}
|
|
3234
|
+
};
|
|
3235
|
+
function toError(error) {
|
|
3236
|
+
if (error instanceof Error) return error;
|
|
3237
|
+
return new Error(String(error));
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
// src/ai/AIClient.ts
|
|
3241
|
+
init_openrouterAttribution();
|
|
3242
|
+
|
|
3243
|
+
// src/ai/openrouterUsage.ts
|
|
3244
|
+
function withOpenRouterUsageInclude(baseFetch) {
|
|
3245
|
+
return (input, init) => {
|
|
3246
|
+
const impl = ((...args) => globalThis.fetch(...args));
|
|
3247
|
+
try {
|
|
3248
|
+
if (init && typeof init.body === "string") {
|
|
3249
|
+
const parsed = JSON.parse(init.body);
|
|
3250
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && parsed.usage === void 0) {
|
|
3251
|
+
parsed.usage = { include: true };
|
|
3252
|
+
return impl(input, { ...init, body: JSON.stringify(parsed) });
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
} catch {
|
|
3256
|
+
}
|
|
3257
|
+
return impl(input, init);
|
|
3258
|
+
};
|
|
3259
|
+
}
|
|
3260
|
+
var IMAGE_MIME_TYPES = {
|
|
3261
|
+
".jpg": "image/jpeg",
|
|
3262
|
+
".jpeg": "image/jpeg",
|
|
3263
|
+
".png": "image/png",
|
|
3264
|
+
".gif": "image/gif",
|
|
3265
|
+
".webp": "image/webp",
|
|
3266
|
+
".bmp": "image/bmp"
|
|
3267
|
+
};
|
|
3268
|
+
var AUDIO_MIME_TYPES = {
|
|
3269
|
+
".wav": "audio/wav",
|
|
3270
|
+
".mp3": "audio/mpeg",
|
|
3271
|
+
".flac": "audio/flac",
|
|
3272
|
+
".ogg": "audio/ogg"
|
|
3273
|
+
};
|
|
3274
|
+
var VIDEO_MIME_TYPES = {
|
|
3275
|
+
".mp4": "video/mp4",
|
|
3276
|
+
".mpeg": "video/mpeg",
|
|
3277
|
+
".mpg": "video/mpeg",
|
|
3278
|
+
".mov": "video/quicktime",
|
|
3279
|
+
".webm": "video/webm"
|
|
3280
|
+
};
|
|
3281
|
+
var Text = class {
|
|
3282
|
+
type = "text";
|
|
3283
|
+
text;
|
|
3284
|
+
constructor(text2) {
|
|
3285
|
+
this.text = text2;
|
|
3286
|
+
}
|
|
3287
|
+
};
|
|
3288
|
+
var Image = class _Image {
|
|
3289
|
+
type = "image_url";
|
|
3290
|
+
imageUrl;
|
|
3291
|
+
constructor(imageUrl) {
|
|
3292
|
+
this.imageUrl = imageUrl;
|
|
3293
|
+
}
|
|
3294
|
+
/**
|
|
3295
|
+
* Create Image from a local file by converting to base64 data URL.
|
|
3296
|
+
*/
|
|
3297
|
+
static async fromFile(filePath, detail = "high") {
|
|
3298
|
+
const absolutePath = resolve(filePath);
|
|
3299
|
+
const buffer = await readFile(absolutePath);
|
|
3300
|
+
const base64Data = buffer.toString("base64");
|
|
3301
|
+
const ext = getExtension(absolutePath).toLowerCase();
|
|
3302
|
+
const mimeType = IMAGE_MIME_TYPES[ext] || "image/jpeg";
|
|
3303
|
+
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
3304
|
+
return new _Image({ url: dataUrl, detail });
|
|
3305
|
+
}
|
|
3306
|
+
/**
|
|
3307
|
+
* Create Image from a URL.
|
|
3308
|
+
*/
|
|
3309
|
+
static fromUrl(url, detail = "high") {
|
|
3310
|
+
return new _Image({ url, detail });
|
|
3311
|
+
}
|
|
3312
|
+
/**
|
|
3313
|
+
* Create Image from a buffer.
|
|
3314
|
+
*/
|
|
3315
|
+
static async fromBuffer(buffer, mimeType = "image/jpeg", detail = "high") {
|
|
3316
|
+
const base64Data = Buffer.from(buffer).toString("base64");
|
|
3317
|
+
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
3318
|
+
return new _Image({ url: dataUrl, detail });
|
|
3319
|
+
}
|
|
3320
|
+
/**
|
|
3321
|
+
* Create Image from a base64 string.
|
|
3322
|
+
*/
|
|
3323
|
+
static async fromBase64(base64Data, mimeType = "image/jpeg", detail = "high") {
|
|
3324
|
+
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
3325
|
+
return new _Image({ url: dataUrl, detail });
|
|
3326
|
+
}
|
|
3327
|
+
};
|
|
3328
|
+
var Audio = class _Audio {
|
|
3329
|
+
type = "input_audio";
|
|
3330
|
+
audio;
|
|
3331
|
+
constructor(audio) {
|
|
3332
|
+
this.audio = audio;
|
|
3333
|
+
}
|
|
3334
|
+
/**
|
|
3335
|
+
* Create Audio from a local file by converting to base64.
|
|
3336
|
+
*/
|
|
3337
|
+
static async fromFile(filePath, format) {
|
|
3338
|
+
const absolutePath = resolve(filePath);
|
|
3339
|
+
const ext = getExtension(absolutePath).toLowerCase().replace(".", "");
|
|
3340
|
+
const audioFormat = format || (["wav", "mp3", "flac", "ogg"].includes(ext) ? ext : "wav");
|
|
3341
|
+
const buffer = await readFile(absolutePath);
|
|
3342
|
+
const base64Data = buffer.toString("base64");
|
|
3343
|
+
return new _Audio({ data: base64Data, format: audioFormat });
|
|
3344
|
+
}
|
|
3345
|
+
/**
|
|
3346
|
+
* Create Audio from a URL (downloads and converts to base64).
|
|
3347
|
+
*/
|
|
3348
|
+
static async fromUrl(url, format = "wav") {
|
|
3349
|
+
try {
|
|
3350
|
+
const response = await fetch(url);
|
|
3351
|
+
if (!response.ok) {
|
|
3352
|
+
throw new Error(`Failed to fetch audio from URL: ${response.status} ${response.statusText}`);
|
|
3353
|
+
}
|
|
3354
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
3355
|
+
const base64Data = Buffer.from(arrayBuffer).toString("base64");
|
|
3356
|
+
return new _Audio({ data: base64Data, format });
|
|
3357
|
+
} catch (error) {
|
|
3358
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
3359
|
+
throw new Error("URL download requires a fetch-compatible environment");
|
|
3360
|
+
}
|
|
3361
|
+
throw error;
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
/**
|
|
3365
|
+
* Create Audio from a buffer.
|
|
3366
|
+
*/
|
|
3367
|
+
static async fromBuffer(buffer, format = "wav") {
|
|
3368
|
+
const base64Data = Buffer.from(buffer).toString("base64");
|
|
3369
|
+
return new _Audio({ data: base64Data, format });
|
|
3370
|
+
}
|
|
3371
|
+
/**
|
|
3372
|
+
* Create Audio from a base64 string.
|
|
3373
|
+
*/
|
|
3374
|
+
static async fromBase64(base64Data, format = "wav") {
|
|
3375
|
+
return new _Audio({ data: base64Data, format });
|
|
3376
|
+
}
|
|
3377
|
+
};
|
|
3378
|
+
var Video = class _Video {
|
|
3379
|
+
type = "video_url";
|
|
3380
|
+
videoUrl;
|
|
3381
|
+
constructor(videoUrl) {
|
|
3382
|
+
this.videoUrl = videoUrl;
|
|
3383
|
+
}
|
|
3384
|
+
/**
|
|
3385
|
+
* Create Video from a local file by converting to a base64 data URL.
|
|
3386
|
+
*/
|
|
3387
|
+
static async fromFile(filePath) {
|
|
3388
|
+
const absolutePath = resolve(filePath);
|
|
3389
|
+
const buffer = await readFile(absolutePath);
|
|
3390
|
+
const base64Data = buffer.toString("base64");
|
|
3391
|
+
const ext = getExtension(absolutePath).toLowerCase();
|
|
3392
|
+
const mimeType = VIDEO_MIME_TYPES[ext] || "video/mp4";
|
|
3393
|
+
return new _Video({ url: `data:${mimeType};base64,${base64Data}` });
|
|
3394
|
+
}
|
|
3395
|
+
/**
|
|
3396
|
+
* Create Video from a URL.
|
|
3397
|
+
*/
|
|
3398
|
+
static fromUrl(url) {
|
|
3399
|
+
return new _Video({ url });
|
|
3400
|
+
}
|
|
3401
|
+
/**
|
|
3402
|
+
* Create Video from a buffer.
|
|
3403
|
+
*/
|
|
3404
|
+
static async fromBuffer(buffer, mimeType = "video/mp4") {
|
|
3405
|
+
const base64Data = Buffer.from(buffer).toString("base64");
|
|
3406
|
+
return new _Video({ url: `data:${mimeType};base64,${base64Data}` });
|
|
2904
3407
|
}
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
this._circuitOpenTime = void 0;
|
|
2911
|
-
this._consecutiveFailures = 0;
|
|
2912
|
-
return false;
|
|
2913
|
-
}
|
|
2914
|
-
return true;
|
|
3408
|
+
/**
|
|
3409
|
+
* Create Video from a base64 string.
|
|
3410
|
+
*/
|
|
3411
|
+
static async fromBase64(base64Data, mimeType = "video/mp4") {
|
|
3412
|
+
return new _Video({ url: `data:${mimeType};base64,${base64Data}` });
|
|
2915
3413
|
}
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
this._consecutiveFailures += 1;
|
|
2923
|
-
if (this._consecutiveFailures >= this.circuitBreakerThreshold && this._circuitOpenTime === void 0) {
|
|
2924
|
-
this._circuitOpenTime = this._now();
|
|
2925
|
-
}
|
|
3414
|
+
};
|
|
3415
|
+
var File = class _File {
|
|
3416
|
+
type = "file";
|
|
3417
|
+
file;
|
|
3418
|
+
constructor(file) {
|
|
3419
|
+
this.file = file;
|
|
2926
3420
|
}
|
|
2927
|
-
|
|
2928
|
-
|
|
3421
|
+
/**
|
|
3422
|
+
* Create File from a local file path.
|
|
3423
|
+
*/
|
|
3424
|
+
static async fromFile(filePath, mimeType) {
|
|
3425
|
+
const absolutePath = resolve(filePath);
|
|
3426
|
+
const detectedMimeType = mimeType || guessMimeType(absolutePath) || "application/octet-stream";
|
|
3427
|
+
const buffer = await readFile(absolutePath);
|
|
3428
|
+
const base64Data = buffer.toString("base64");
|
|
3429
|
+
const dataUrl = `data:${detectedMimeType};base64,${base64Data}`;
|
|
3430
|
+
return new _File({ url: dataUrl, mimeType: detectedMimeType });
|
|
2929
3431
|
}
|
|
2930
|
-
|
|
2931
|
-
|
|
3432
|
+
/**
|
|
3433
|
+
* Create File from a URL.
|
|
3434
|
+
*/
|
|
3435
|
+
static fromUrl(url, mimeType) {
|
|
3436
|
+
return new _File({ url, mimeType });
|
|
2932
3437
|
}
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
}
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
if (!this._isRateLimitError(error)) {
|
|
2948
|
-
throw error;
|
|
2949
|
-
}
|
|
2950
|
-
this._updateCircuitBreaker(false);
|
|
2951
|
-
if (attempt >= this.maxRetries) {
|
|
2952
|
-
break;
|
|
2953
|
-
}
|
|
2954
|
-
const retryAfter = this._extractRetryAfter(error);
|
|
2955
|
-
const delay = this._calculateBackoffDelay(attempt, retryAfter);
|
|
2956
|
-
await this._sleep(delay);
|
|
2957
|
-
}
|
|
2958
|
-
}
|
|
2959
|
-
throw new RateLimitError(
|
|
2960
|
-
`Rate limit retries exhausted after ${this.maxRetries} attempts. Last error: ${String(lastError)}`,
|
|
2961
|
-
this._extractRetryAfter(lastError)
|
|
2962
|
-
);
|
|
3438
|
+
/**
|
|
3439
|
+
* Create File from a buffer.
|
|
3440
|
+
*/
|
|
3441
|
+
static async fromBuffer(buffer, mimeType) {
|
|
3442
|
+
const base64Data = Buffer.from(buffer).toString("base64");
|
|
3443
|
+
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
3444
|
+
return new _File({ url: dataUrl, mimeType });
|
|
3445
|
+
}
|
|
3446
|
+
/**
|
|
3447
|
+
* Create File from a base64 string.
|
|
3448
|
+
*/
|
|
3449
|
+
static async fromBase64(base64Data, mimeType) {
|
|
3450
|
+
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
3451
|
+
return new _File({ url: dataUrl, mimeType });
|
|
2963
3452
|
}
|
|
2964
3453
|
};
|
|
2965
|
-
function
|
|
2966
|
-
|
|
2967
|
-
|
|
3454
|
+
function getExtension(filePath) {
|
|
3455
|
+
const lastDot = filePath.lastIndexOf(".");
|
|
3456
|
+
if (lastDot === -1) {
|
|
3457
|
+
return "";
|
|
3458
|
+
}
|
|
3459
|
+
return filePath.slice(lastDot);
|
|
2968
3460
|
}
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
3461
|
+
function guessMimeType(filePath) {
|
|
3462
|
+
const ext = getExtension(filePath).toLowerCase();
|
|
3463
|
+
if (ext in IMAGE_MIME_TYPES) {
|
|
3464
|
+
return IMAGE_MIME_TYPES[ext];
|
|
3465
|
+
}
|
|
3466
|
+
if (ext in AUDIO_MIME_TYPES) {
|
|
3467
|
+
return AUDIO_MIME_TYPES[ext];
|
|
3468
|
+
}
|
|
3469
|
+
if (ext in VIDEO_MIME_TYPES) {
|
|
3470
|
+
return VIDEO_MIME_TYPES[ext];
|
|
3471
|
+
}
|
|
3472
|
+
const documentMimeTypes = {
|
|
3473
|
+
".pdf": "application/pdf",
|
|
3474
|
+
".doc": "application/msword",
|
|
3475
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
3476
|
+
".xls": "application/vnd.ms-excel",
|
|
3477
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
3478
|
+
".txt": "text/plain",
|
|
3479
|
+
".csv": "text/csv",
|
|
3480
|
+
".html": "text/html",
|
|
3481
|
+
".json": "application/json",
|
|
3482
|
+
".xml": "application/xml",
|
|
3483
|
+
".zip": "application/zip"
|
|
2988
3484
|
};
|
|
3485
|
+
return documentMimeTypes[ext] || null;
|
|
3486
|
+
}
|
|
3487
|
+
function getDataUrlMimeType(url) {
|
|
3488
|
+
const match = /^data:([^;,]+)(?:;[^,]*)?,/i.exec(url);
|
|
3489
|
+
return match?.[1] || null;
|
|
3490
|
+
}
|
|
3491
|
+
function guessUrlMimeType(url) {
|
|
3492
|
+
return getDataUrlMimeType(url) ?? guessMimeType(url.split(/[?#]/, 1)[0]);
|
|
3493
|
+
}
|
|
3494
|
+
function audioMediaType(format) {
|
|
3495
|
+
return AUDIO_MIME_TYPES[`.${format.toLowerCase()}`] ?? `audio/${format}`;
|
|
3496
|
+
}
|
|
3497
|
+
function text(content) {
|
|
3498
|
+
return new Text(content);
|
|
3499
|
+
}
|
|
3500
|
+
async function imageFromFile(filePath, detail = "high") {
|
|
3501
|
+
return Image.fromFile(filePath, detail);
|
|
3502
|
+
}
|
|
3503
|
+
function imageFromUrl(url, detail = "high") {
|
|
3504
|
+
return Image.fromUrl(url, detail);
|
|
3505
|
+
}
|
|
3506
|
+
async function imageFromBuffer(buffer, mimeType = "image/jpeg", detail = "high") {
|
|
3507
|
+
return Image.fromBuffer(buffer, mimeType, detail);
|
|
3508
|
+
}
|
|
3509
|
+
async function imageFromBase64(base64Data, mimeType, detail = "high") {
|
|
3510
|
+
return Image.fromBase64(base64Data, mimeType, detail);
|
|
3511
|
+
}
|
|
3512
|
+
async function audioFromFile(filePath, format) {
|
|
3513
|
+
return Audio.fromFile(filePath, format);
|
|
3514
|
+
}
|
|
3515
|
+
async function audioFromUrl(url, format = "wav") {
|
|
3516
|
+
return Audio.fromUrl(url, format);
|
|
3517
|
+
}
|
|
3518
|
+
async function audioFromBuffer(buffer, format = "wav") {
|
|
3519
|
+
return Audio.fromBuffer(buffer, format);
|
|
3520
|
+
}
|
|
3521
|
+
async function audioFromBase64(base64Data, format = "wav") {
|
|
3522
|
+
return Audio.fromBase64(base64Data, format);
|
|
3523
|
+
}
|
|
3524
|
+
async function videoFromFile(filePath) {
|
|
3525
|
+
return Video.fromFile(filePath);
|
|
3526
|
+
}
|
|
3527
|
+
function videoFromUrl(url) {
|
|
3528
|
+
return Video.fromUrl(url);
|
|
3529
|
+
}
|
|
3530
|
+
async function videoFromBuffer(buffer, mimeType = "video/mp4") {
|
|
3531
|
+
return Video.fromBuffer(buffer, mimeType);
|
|
3532
|
+
}
|
|
3533
|
+
async function videoFromBase64(base64Data, mimeType = "video/mp4") {
|
|
3534
|
+
return Video.fromBase64(base64Data, mimeType);
|
|
3535
|
+
}
|
|
3536
|
+
async function fileFromPath(filePath, mimeType) {
|
|
3537
|
+
return File.fromFile(filePath, mimeType);
|
|
3538
|
+
}
|
|
3539
|
+
function fileFromUrl(url, mimeType) {
|
|
3540
|
+
return File.fromUrl(url, mimeType);
|
|
3541
|
+
}
|
|
3542
|
+
async function fileFromBuffer(buffer, mimeType) {
|
|
3543
|
+
return File.fromBuffer(buffer, mimeType);
|
|
3544
|
+
}
|
|
3545
|
+
async function fileFromBase64(base64Data, mimeType) {
|
|
3546
|
+
return File.fromBase64(base64Data, mimeType);
|
|
2989
3547
|
}
|
|
2990
3548
|
|
|
2991
3549
|
// src/ai/AIClient.ts
|
|
@@ -3025,11 +3583,12 @@ var AIClient = class {
|
|
|
3025
3583
|
async generate(prompt, options = {}) {
|
|
3026
3584
|
const { provider, modelName } = this.resolveModelChoice(options);
|
|
3027
3585
|
const model = this.buildModel(options);
|
|
3586
|
+
const requestPrompt = this.buildPrompt(prompt, options.content);
|
|
3028
3587
|
if (options.schema) {
|
|
3029
3588
|
const schema = options.schema;
|
|
3030
3589
|
const call2 = async () => generateObject({
|
|
3031
3590
|
model,
|
|
3032
|
-
prompt,
|
|
3591
|
+
prompt: requestPrompt,
|
|
3033
3592
|
output: "object",
|
|
3034
3593
|
system: options.system,
|
|
3035
3594
|
temperature: options.temperature ?? this.config.temperature,
|
|
@@ -3043,7 +3602,7 @@ var AIClient = class {
|
|
|
3043
3602
|
}
|
|
3044
3603
|
const call = async () => generateText({
|
|
3045
3604
|
model,
|
|
3046
|
-
prompt,
|
|
3605
|
+
prompt: requestPrompt,
|
|
3047
3606
|
system: options.system,
|
|
3048
3607
|
temperature: options.temperature ?? this.config.temperature,
|
|
3049
3608
|
maxOutputTokens: options.maxTokens ?? this.config.maxTokens
|
|
@@ -3060,7 +3619,7 @@ var AIClient = class {
|
|
|
3060
3619
|
const model = this.buildModel(options);
|
|
3061
3620
|
const streamResult = streamText({
|
|
3062
3621
|
model,
|
|
3063
|
-
prompt,
|
|
3622
|
+
prompt: this.buildPrompt(prompt, options.content),
|
|
3064
3623
|
system: options.system,
|
|
3065
3624
|
temperature: options.temperature ?? this.config.temperature,
|
|
3066
3625
|
maxOutputTokens: options.maxTokens ?? this.config.maxTokens
|
|
@@ -3186,6 +3745,40 @@ var AIClient = class {
|
|
|
3186
3745
|
}
|
|
3187
3746
|
}
|
|
3188
3747
|
}
|
|
3748
|
+
buildPrompt(prompt, content) {
|
|
3749
|
+
if (!content?.length) return prompt;
|
|
3750
|
+
const messageContent = [
|
|
3751
|
+
{ type: "text", text: prompt },
|
|
3752
|
+
...content.map((part) => {
|
|
3753
|
+
if (part.type === "text") return { type: "text", text: part.text };
|
|
3754
|
+
if (part.type === "image_url") return { type: "image", image: part.imageUrl.url };
|
|
3755
|
+
if (part.type === "video_url") {
|
|
3756
|
+
return {
|
|
3757
|
+
type: "file",
|
|
3758
|
+
data: part.videoUrl.url,
|
|
3759
|
+
mediaType: guessUrlMimeType(part.videoUrl.url) ?? "video/mp4"
|
|
3760
|
+
};
|
|
3761
|
+
}
|
|
3762
|
+
if (part.type === "input_audio") {
|
|
3763
|
+
return {
|
|
3764
|
+
type: "file",
|
|
3765
|
+
data: part.audio.data,
|
|
3766
|
+
mediaType: audioMediaType(part.audio.format)
|
|
3767
|
+
};
|
|
3768
|
+
}
|
|
3769
|
+
return {
|
|
3770
|
+
type: "file",
|
|
3771
|
+
data: part.file.url,
|
|
3772
|
+
mediaType: part.file.mimeType ?? guessUrlMimeType(part.file.url) ?? "application/octet-stream"
|
|
3773
|
+
};
|
|
3774
|
+
})
|
|
3775
|
+
];
|
|
3776
|
+
const messages = [{
|
|
3777
|
+
role: "user",
|
|
3778
|
+
content: messageContent
|
|
3779
|
+
}];
|
|
3780
|
+
return messages;
|
|
3781
|
+
}
|
|
3189
3782
|
buildEmbeddingModel(options) {
|
|
3190
3783
|
const provider = options.provider ?? this.config.provider ?? "openai";
|
|
3191
3784
|
const modelName = options.model ?? this.config.embeddingModel ?? "text-embedding-3-small";
|
|
@@ -3568,6 +4161,14 @@ var AgentFieldClient = class {
|
|
|
3568
4161
|
});
|
|
3569
4162
|
return res.data;
|
|
3570
4163
|
}
|
|
4164
|
+
async shutdown(nodeId) {
|
|
4165
|
+
const bodyStr = JSON.stringify({ reason: "shutdown", version: this.config.version ?? "" });
|
|
4166
|
+
const authHeaders = this.didAuthenticator.signRequest(Buffer.from(bodyStr));
|
|
4167
|
+
const res = await this.http.post(`/api/v1/nodes/${encodeURIComponent(nodeId)}/shutdown`, bodyStr, {
|
|
4168
|
+
headers: this.mergeHeaders({ "Content-Type": "application/json", ...authHeaders })
|
|
4169
|
+
});
|
|
4170
|
+
return res.data;
|
|
4171
|
+
}
|
|
3571
4172
|
async heartbeat(status = "ready") {
|
|
3572
4173
|
const nodeId = this.config.nodeId;
|
|
3573
4174
|
const bodyStr = JSON.stringify({ status, version: this.config.version ?? "", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -4114,6 +4715,7 @@ function wrapResult(result) {
|
|
|
4114
4715
|
}
|
|
4115
4716
|
|
|
4116
4717
|
// src/agent/Agent.ts
|
|
4718
|
+
init_factory();
|
|
4117
4719
|
init_modelVariant();
|
|
4118
4720
|
var MemoryClientBase = class {
|
|
4119
4721
|
http;
|
|
@@ -5447,10 +6049,26 @@ function buildSessionDefinition(name, options) {
|
|
|
5447
6049
|
};
|
|
5448
6050
|
}
|
|
5449
6051
|
|
|
6052
|
+
// src/agent/signals.ts
|
|
6053
|
+
var DEFAULT_SHUTDOWN_TIMEOUT_MS = 3e4;
|
|
6054
|
+
function parseShutdownTimeout(value, warn = console.warn) {
|
|
6055
|
+
if (!value?.trim()) return DEFAULT_SHUTDOWN_TIMEOUT_MS;
|
|
6056
|
+
const text2 = value.trim();
|
|
6057
|
+
if (/^\d+$/.test(text2)) return Number(text2) * 1e3;
|
|
6058
|
+
const match = text2.match(/^(\d+(?:\.\d+)?)(ms|s|m|h)$/);
|
|
6059
|
+
if (match) {
|
|
6060
|
+
const factors = { ms: 1, s: 1e3, m: 6e4, h: 36e5 };
|
|
6061
|
+
return Number(match[1]) * factors[match[2]];
|
|
6062
|
+
}
|
|
6063
|
+
warn(`invalid AGENTFIELD_SHUTDOWN_TIMEOUT ${JSON.stringify(value)}; using 30s`);
|
|
6064
|
+
return DEFAULT_SHUTDOWN_TIMEOUT_MS;
|
|
6065
|
+
}
|
|
6066
|
+
|
|
5450
6067
|
// src/agent/Agent.ts
|
|
5451
6068
|
var TargetNotFoundError = class extends Error {
|
|
5452
6069
|
};
|
|
5453
6070
|
var AGENTFIELD_TS_SDK_VERSION = "0.1.82";
|
|
6071
|
+
var POST_CANCEL_SETTLEMENT_MS = 5e3;
|
|
5454
6072
|
var harnessRunners = /* @__PURE__ */ new WeakMap();
|
|
5455
6073
|
function normalizeAcceptsWebhook(value) {
|
|
5456
6074
|
if (value === true) return "true";
|
|
@@ -5480,6 +6098,10 @@ var Agent = class {
|
|
|
5480
6098
|
skills = new SkillRegistry();
|
|
5481
6099
|
server;
|
|
5482
6100
|
heartbeatTimer;
|
|
6101
|
+
shutdownPromise;
|
|
6102
|
+
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
6103
|
+
shuttingDown = false;
|
|
6104
|
+
signalHandlers;
|
|
5483
6105
|
aiClient;
|
|
5484
6106
|
agentFieldClient;
|
|
5485
6107
|
memoryClient;
|
|
@@ -5701,7 +6323,7 @@ var Agent = class {
|
|
|
5701
6323
|
if (!inputTokens && !outputTokens && !cacheRead && !cacheCreation && !reportedTotal && cost === null) {
|
|
5702
6324
|
return;
|
|
5703
6325
|
}
|
|
5704
|
-
const providerName = options?.provider ?? this.config.harnessConfig?.provider;
|
|
6326
|
+
const providerName = resolveProviderName(options?.provider ?? this.config.harnessConfig?.provider);
|
|
5705
6327
|
const harnessName = providerName ? String(providerName).replace(/-/g, "_") : null;
|
|
5706
6328
|
const rawModelName = String(
|
|
5707
6329
|
result.model ?? options?.model ?? this.config.harnessConfig?.model ?? providerName ?? "harness"
|
|
@@ -5879,7 +6501,7 @@ var Agent = class {
|
|
|
5879
6501
|
agentNodeDid: current.agentNodeDid
|
|
5880
6502
|
};
|
|
5881
6503
|
}
|
|
5882
|
-
async serve() {
|
|
6504
|
+
async serve(options = {}) {
|
|
5883
6505
|
await this.registerWithControlPlane();
|
|
5884
6506
|
if (this.localVerifier) {
|
|
5885
6507
|
try {
|
|
@@ -5899,19 +6521,70 @@ var Agent = class {
|
|
|
5899
6521
|
});
|
|
5900
6522
|
this.memoryEventClient.start();
|
|
5901
6523
|
this.startHeartbeat();
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
this.
|
|
5908
|
-
|
|
5909
|
-
|
|
6524
|
+
if (options.handleSignals !== false) this.installSignalHandlers();
|
|
6525
|
+
}
|
|
6526
|
+
shutdown() {
|
|
6527
|
+
if (this.shutdownPromise) return this.shutdownPromise;
|
|
6528
|
+
this.shuttingDown = true;
|
|
6529
|
+
this.shutdownPromise = this.performShutdown();
|
|
6530
|
+
return this.shutdownPromise;
|
|
6531
|
+
}
|
|
6532
|
+
async performShutdown() {
|
|
6533
|
+
const listenerClosed = new Promise((resolve3, reject) => {
|
|
6534
|
+
if (!this.server) return resolve3();
|
|
6535
|
+
this.server.close((err) => {
|
|
5910
6536
|
if (err) reject(err);
|
|
5911
6537
|
else resolve3();
|
|
5912
6538
|
});
|
|
5913
6539
|
});
|
|
6540
|
+
try {
|
|
6541
|
+
await this.agentFieldClient.shutdown(this.config.nodeId);
|
|
6542
|
+
} catch (err) {
|
|
6543
|
+
console.warn("[Agent] Failed to notify control plane of shutdown:", err);
|
|
6544
|
+
}
|
|
6545
|
+
if (this.heartbeatTimer) {
|
|
6546
|
+
clearInterval(this.heartbeatTimer);
|
|
6547
|
+
}
|
|
6548
|
+
await listenerClosed;
|
|
6549
|
+
const timeoutMs = parseShutdownTimeout(process.env.AGENTFIELD_SHUTDOWN_TIMEOUT);
|
|
6550
|
+
let timer;
|
|
6551
|
+
const timeout = new Promise((resolve3) => {
|
|
6552
|
+
timer = setTimeout(() => resolve3("timeout"), timeoutMs);
|
|
6553
|
+
});
|
|
6554
|
+
const drained = Promise.allSettled([...this.inFlightExecutions.values()]).then(() => "drained");
|
|
6555
|
+
if (await Promise.race([drained, timeout]) === "timeout") {
|
|
6556
|
+
for (const executionId of this.inFlightExecutions.keys()) {
|
|
6557
|
+
this.cancelRegistry.cancel(executionId, "shutdown_timeout");
|
|
6558
|
+
}
|
|
6559
|
+
this.pauseManager.cancelAll();
|
|
6560
|
+
let settlementTimer;
|
|
6561
|
+
const settlementTimeout = new Promise((resolve3) => {
|
|
6562
|
+
settlementTimer = setTimeout(resolve3, POST_CANCEL_SETTLEMENT_MS);
|
|
6563
|
+
});
|
|
6564
|
+
await Promise.race([
|
|
6565
|
+
Promise.allSettled([...this.inFlightExecutions.values()]),
|
|
6566
|
+
settlementTimeout
|
|
6567
|
+
]);
|
|
6568
|
+
if (settlementTimer) clearTimeout(settlementTimer);
|
|
6569
|
+
}
|
|
6570
|
+
if (timer) clearTimeout(timer);
|
|
5914
6571
|
this.memoryEventClient.stop();
|
|
6572
|
+
if (this.signalHandlers) {
|
|
6573
|
+
process.off("SIGTERM", this.signalHandlers.SIGTERM);
|
|
6574
|
+
process.off("SIGINT", this.signalHandlers.SIGINT);
|
|
6575
|
+
this.signalHandlers = void 0;
|
|
6576
|
+
}
|
|
6577
|
+
}
|
|
6578
|
+
installSignalHandlers() {
|
|
6579
|
+
if (this.signalHandlers) return;
|
|
6580
|
+
const handle = (signal) => () => {
|
|
6581
|
+
void this.shutdown().finally(() => {
|
|
6582
|
+
process.exit(signal === "SIGTERM" ? 143 : 130);
|
|
6583
|
+
});
|
|
6584
|
+
};
|
|
6585
|
+
this.signalHandlers = { SIGTERM: handle("SIGTERM"), SIGINT: handle("SIGINT") };
|
|
6586
|
+
process.on("SIGTERM", this.signalHandlers.SIGTERM);
|
|
6587
|
+
process.on("SIGINT", this.signalHandlers.SIGINT);
|
|
5915
6588
|
}
|
|
5916
6589
|
async call(target, input) {
|
|
5917
6590
|
const { agentId, name } = this.parseTarget(target);
|
|
@@ -6333,11 +7006,17 @@ var Agent = class {
|
|
|
6333
7006
|
this.app.post("/execute/:name", (req, res) => this.executeServerlessHttp(req, res, req.params.name));
|
|
6334
7007
|
}
|
|
6335
7008
|
async executeReasoner(req, res, name) {
|
|
7009
|
+
if (this.shuttingDown) {
|
|
7010
|
+
res.status(503).json({ error: "agent shutting down" });
|
|
7011
|
+
return;
|
|
7012
|
+
}
|
|
6336
7013
|
const metadata = this.buildMetadata(req);
|
|
6337
7014
|
const reasoner = this.reasoners.get(name);
|
|
6338
7015
|
if (reasoner && this.shouldRunAsync(req)) {
|
|
6339
7016
|
res.status(202).json({ status: "processing", execution_id: metadata.executionId });
|
|
6340
|
-
|
|
7017
|
+
const execution = this.runReasonerAsync(reasoner, { targetName: name, input: req.body, metadata });
|
|
7018
|
+
this.inFlightExecutions.set(metadata.executionId, execution);
|
|
7019
|
+
void execution.finally(() => this.inFlightExecutions.delete(metadata.executionId));
|
|
6341
7020
|
return;
|
|
6342
7021
|
}
|
|
6343
7022
|
try {
|
|
@@ -6387,6 +7066,10 @@ var Agent = class {
|
|
|
6387
7066
|
return this.buildMetadataFromHeaders(req.headers);
|
|
6388
7067
|
}
|
|
6389
7068
|
async executeServerlessHttp(req, res, explicitName) {
|
|
7069
|
+
if (this.shuttingDown) {
|
|
7070
|
+
res.status(503).json({ error: "agent shutting down" });
|
|
7071
|
+
return;
|
|
7072
|
+
}
|
|
6390
7073
|
const invocation = this.extractInvocationDetails({
|
|
6391
7074
|
path: req.path,
|
|
6392
7075
|
explicitTarget: explicitName,
|
|
@@ -7141,416 +7824,138 @@ var Agent = class {
|
|
|
7141
7824
|
}
|
|
7142
7825
|
});
|
|
7143
7826
|
}
|
|
7144
|
-
parseTarget(target) {
|
|
7145
|
-
if (!target.includes(".")) {
|
|
7146
|
-
return { name: target };
|
|
7147
|
-
}
|
|
7148
|
-
const [agentId, remainder] = target.split(".", 2);
|
|
7149
|
-
const name = remainder.replace(":", "/");
|
|
7150
|
-
return { agentId, name };
|
|
7151
|
-
}
|
|
7152
|
-
};
|
|
7153
|
-
|
|
7154
|
-
// src/router/AgentRouter.ts
|
|
7155
|
-
var AgentRouter = class {
|
|
7156
|
-
prefix;
|
|
7157
|
-
tags;
|
|
7158
|
-
reasoners = [];
|
|
7159
|
-
skills = [];
|
|
7160
|
-
constructor(options = {}) {
|
|
7161
|
-
this.prefix = options.prefix;
|
|
7162
|
-
this.tags = options.tags;
|
|
7163
|
-
}
|
|
7164
|
-
reasoner(name, handler, options) {
|
|
7165
|
-
const fullName = this.prefix ? `${sanitize(this.prefix)}_${name}` : name;
|
|
7166
|
-
this.reasoners.push({ name: fullName, handler, options });
|
|
7167
|
-
return this;
|
|
7168
|
-
}
|
|
7169
|
-
skill(name, handler, options) {
|
|
7170
|
-
const fullName = this.prefix ? `${sanitize(this.prefix)}_${name}` : name;
|
|
7171
|
-
this.skills.push({ name: fullName, handler, options });
|
|
7172
|
-
return this;
|
|
7173
|
-
}
|
|
7174
|
-
};
|
|
7175
|
-
function sanitize(value) {
|
|
7176
|
-
return value.replace(/[^0-9a-zA-Z]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
7177
|
-
}
|
|
7178
|
-
var JWE_ALG = "ECDH-ES";
|
|
7179
|
-
var JWE_ENC = "A256GCM";
|
|
7180
|
-
var KEY_AGREEMENT_TYPE = "X25519KeyAgreementKey2020";
|
|
7181
|
-
var PayloadEncryptionError = class extends Error {
|
|
7182
|
-
constructor(message) {
|
|
7183
|
-
super(message);
|
|
7184
|
-
this.name = "PayloadEncryptionError";
|
|
7185
|
-
}
|
|
7186
|
-
};
|
|
7187
|
-
var encoder = new TextEncoder();
|
|
7188
|
-
function payloadToBytes(payload) {
|
|
7189
|
-
if (payload instanceof Uint8Array) return payload;
|
|
7190
|
-
if (typeof payload === "string") return encoder.encode(payload);
|
|
7191
|
-
return encoder.encode(JSON.stringify(payload));
|
|
7192
|
-
}
|
|
7193
|
-
async function generateX25519KeyPair() {
|
|
7194
|
-
const { privateKey, publicKey } = await generateKeyPair("ECDH-ES", {
|
|
7195
|
-
crv: "X25519",
|
|
7196
|
-
extractable: true
|
|
7197
|
-
});
|
|
7198
|
-
const privateJwk = await exportJWK(privateKey);
|
|
7199
|
-
const publicJwk = await exportJWK(publicKey);
|
|
7200
|
-
return { privateJwk, publicJwk };
|
|
7201
|
-
}
|
|
7202
|
-
function extractKeyAgreementJwk(didDocument) {
|
|
7203
|
-
if (typeof didDocument !== "object" || didDocument === null) {
|
|
7204
|
-
throw new PayloadEncryptionError("DID document must be an object");
|
|
7205
|
-
}
|
|
7206
|
-
const doc = didDocument["did_document"] ?? didDocument;
|
|
7207
|
-
const flat = doc["key_agreement"];
|
|
7208
|
-
if (flat && typeof flat === "object" && flat.crv === "X25519") {
|
|
7209
|
-
return flat;
|
|
7210
|
-
}
|
|
7211
|
-
const keyAgreement = doc["keyAgreement"];
|
|
7212
|
-
if (!keyAgreement || Array.isArray(keyAgreement) && keyAgreement.length === 0) {
|
|
7213
|
-
throw new PayloadEncryptionError(
|
|
7214
|
-
"DID document has no keyAgreement key; the agent has not published an X25519 encryption key"
|
|
7215
|
-
);
|
|
7216
|
-
}
|
|
7217
|
-
const verificationMethods = /* @__PURE__ */ new Map();
|
|
7218
|
-
for (const vm2 of doc["verificationMethod"] ?? []) {
|
|
7219
|
-
if (vm2 && typeof vm2 === "object" && typeof vm2["id"] === "string") {
|
|
7220
|
-
verificationMethods.set(vm2["id"], vm2);
|
|
7221
|
-
}
|
|
7222
|
-
}
|
|
7223
|
-
const entry = Array.isArray(keyAgreement) ? keyAgreement[0] : keyAgreement;
|
|
7224
|
-
let vm;
|
|
7225
|
-
if (typeof entry === "string") {
|
|
7226
|
-
vm = verificationMethods.get(entry);
|
|
7227
|
-
if (!vm) {
|
|
7228
|
-
throw new PayloadEncryptionError(
|
|
7229
|
-
`keyAgreement references unknown verification method ${entry}`
|
|
7230
|
-
);
|
|
7231
|
-
}
|
|
7232
|
-
} else if (entry && typeof entry === "object") {
|
|
7233
|
-
vm = entry;
|
|
7234
|
-
} else {
|
|
7235
|
-
throw new PayloadEncryptionError("unsupported keyAgreement entry shape");
|
|
7236
|
-
}
|
|
7237
|
-
const jwk = vm["publicKeyJwk"];
|
|
7238
|
-
if (!jwk || jwk.crv !== "X25519") {
|
|
7239
|
-
throw new PayloadEncryptionError(
|
|
7240
|
-
"keyAgreement verification method does not carry an X25519 publicKeyJwk"
|
|
7241
|
-
);
|
|
7242
|
-
}
|
|
7243
|
-
return jwk;
|
|
7244
|
-
}
|
|
7245
|
-
async function encryptToJwk(publicJwk, payload) {
|
|
7246
|
-
if (!publicJwk || publicJwk.crv !== "X25519") {
|
|
7247
|
-
throw new PayloadEncryptionError("publicJwk must be an X25519 OKP JWK");
|
|
7248
|
-
}
|
|
7249
|
-
try {
|
|
7250
|
-
const key = await importJWK(publicJwk, JWE_ALG);
|
|
7251
|
-
return await new CompactEncrypt(payloadToBytes(payload)).setProtectedHeader({ alg: JWE_ALG, enc: JWE_ENC }).encrypt(key);
|
|
7252
|
-
} catch (err) {
|
|
7253
|
-
if (err instanceof PayloadEncryptionError) throw err;
|
|
7254
|
-
throw new PayloadEncryptionError(`encryption failed: ${err.message}`);
|
|
7255
|
-
}
|
|
7256
|
-
}
|
|
7257
|
-
async function encryptForDid(did, payload, resolver) {
|
|
7258
|
-
const document = await resolver(did);
|
|
7259
|
-
if (!document) {
|
|
7260
|
-
throw new PayloadEncryptionError(`could not resolve DID ${did}`);
|
|
7261
|
-
}
|
|
7262
|
-
const publicJwk = extractKeyAgreementJwk(document);
|
|
7263
|
-
return encryptToJwk(publicJwk, payload);
|
|
7264
|
-
}
|
|
7265
|
-
async function decrypt(token, privateJwk) {
|
|
7266
|
-
try {
|
|
7267
|
-
const key = await importJWK(privateJwk, JWE_ALG);
|
|
7268
|
-
const { plaintext } = await compactDecrypt(token, key);
|
|
7269
|
-
return Uint8Array.from(plaintext);
|
|
7270
|
-
} catch (err) {
|
|
7271
|
-
throw new PayloadEncryptionError(`decryption failed: ${err.message}`);
|
|
7272
|
-
}
|
|
7273
|
-
}
|
|
7274
|
-
async function decryptToString(token, privateJwk) {
|
|
7275
|
-
return new TextDecoder().decode(await decrypt(token, privateJwk));
|
|
7276
|
-
}
|
|
7277
|
-
var IMAGE_MIME_TYPES = {
|
|
7278
|
-
".jpg": "image/jpeg",
|
|
7279
|
-
".jpeg": "image/jpeg",
|
|
7280
|
-
".png": "image/png",
|
|
7281
|
-
".gif": "image/gif",
|
|
7282
|
-
".webp": "image/webp",
|
|
7283
|
-
".bmp": "image/bmp"
|
|
7284
|
-
};
|
|
7285
|
-
var AUDIO_MIME_TYPES = {
|
|
7286
|
-
".wav": "audio/wav",
|
|
7287
|
-
".mp3": "audio/mpeg",
|
|
7288
|
-
".flac": "audio/flac",
|
|
7289
|
-
".ogg": "audio/ogg"
|
|
7290
|
-
};
|
|
7291
|
-
var VIDEO_MIME_TYPES = {
|
|
7292
|
-
".mp4": "video/mp4",
|
|
7293
|
-
".mpeg": "video/mpeg",
|
|
7294
|
-
".mpg": "video/mpeg",
|
|
7295
|
-
".mov": "video/quicktime",
|
|
7296
|
-
".webm": "video/webm"
|
|
7297
|
-
};
|
|
7298
|
-
var Text = class {
|
|
7299
|
-
type = "text";
|
|
7300
|
-
text;
|
|
7301
|
-
constructor(text2) {
|
|
7302
|
-
this.text = text2;
|
|
7303
|
-
}
|
|
7304
|
-
};
|
|
7305
|
-
var Image = class _Image {
|
|
7306
|
-
type = "image_url";
|
|
7307
|
-
imageUrl;
|
|
7308
|
-
constructor(imageUrl) {
|
|
7309
|
-
this.imageUrl = imageUrl;
|
|
7310
|
-
}
|
|
7311
|
-
/**
|
|
7312
|
-
* Create Image from a local file by converting to base64 data URL.
|
|
7313
|
-
*/
|
|
7314
|
-
static async fromFile(filePath, detail = "high") {
|
|
7315
|
-
const absolutePath = resolve(filePath);
|
|
7316
|
-
const buffer = await readFile(absolutePath);
|
|
7317
|
-
const base64Data = buffer.toString("base64");
|
|
7318
|
-
const ext = getExtension(absolutePath).toLowerCase();
|
|
7319
|
-
const mimeType = IMAGE_MIME_TYPES[ext] || "image/jpeg";
|
|
7320
|
-
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
7321
|
-
return new _Image({ url: dataUrl, detail });
|
|
7322
|
-
}
|
|
7323
|
-
/**
|
|
7324
|
-
* Create Image from a URL.
|
|
7325
|
-
*/
|
|
7326
|
-
static fromUrl(url, detail = "high") {
|
|
7327
|
-
return new _Image({ url, detail });
|
|
7328
|
-
}
|
|
7329
|
-
/**
|
|
7330
|
-
* Create Image from a buffer.
|
|
7331
|
-
*/
|
|
7332
|
-
static async fromBuffer(buffer, mimeType = "image/jpeg", detail = "high") {
|
|
7333
|
-
const base64Data = Buffer.from(buffer).toString("base64");
|
|
7334
|
-
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
7335
|
-
return new _Image({ url: dataUrl, detail });
|
|
7336
|
-
}
|
|
7337
|
-
/**
|
|
7338
|
-
* Create Image from a base64 string.
|
|
7339
|
-
*/
|
|
7340
|
-
static async fromBase64(base64Data, mimeType = "image/jpeg", detail = "high") {
|
|
7341
|
-
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
7342
|
-
return new _Image({ url: dataUrl, detail });
|
|
7343
|
-
}
|
|
7344
|
-
};
|
|
7345
|
-
var Audio = class _Audio {
|
|
7346
|
-
type = "input_audio";
|
|
7347
|
-
audio;
|
|
7348
|
-
constructor(audio) {
|
|
7349
|
-
this.audio = audio;
|
|
7350
|
-
}
|
|
7351
|
-
/**
|
|
7352
|
-
* Create Audio from a local file by converting to base64.
|
|
7353
|
-
*/
|
|
7354
|
-
static async fromFile(filePath, format) {
|
|
7355
|
-
const absolutePath = resolve(filePath);
|
|
7356
|
-
const ext = getExtension(absolutePath).toLowerCase().replace(".", "");
|
|
7357
|
-
const audioFormat = format || (["wav", "mp3", "flac", "ogg"].includes(ext) ? ext : "wav");
|
|
7358
|
-
const buffer = await readFile(absolutePath);
|
|
7359
|
-
const base64Data = buffer.toString("base64");
|
|
7360
|
-
return new _Audio({ data: base64Data, format: audioFormat });
|
|
7361
|
-
}
|
|
7362
|
-
/**
|
|
7363
|
-
* Create Audio from a URL (downloads and converts to base64).
|
|
7364
|
-
*/
|
|
7365
|
-
static async fromUrl(url, format = "wav") {
|
|
7366
|
-
try {
|
|
7367
|
-
const response = await fetch(url);
|
|
7368
|
-
if (!response.ok) {
|
|
7369
|
-
throw new Error(`Failed to fetch audio from URL: ${response.status} ${response.statusText}`);
|
|
7370
|
-
}
|
|
7371
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
7372
|
-
const base64Data = Buffer.from(arrayBuffer).toString("base64");
|
|
7373
|
-
return new _Audio({ data: base64Data, format });
|
|
7374
|
-
} catch (error) {
|
|
7375
|
-
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
7376
|
-
throw new Error("URL download requires a fetch-compatible environment");
|
|
7377
|
-
}
|
|
7378
|
-
throw error;
|
|
7379
|
-
}
|
|
7380
|
-
}
|
|
7381
|
-
/**
|
|
7382
|
-
* Create Audio from a buffer.
|
|
7383
|
-
*/
|
|
7384
|
-
static async fromBuffer(buffer, format = "wav") {
|
|
7385
|
-
const base64Data = Buffer.from(buffer).toString("base64");
|
|
7386
|
-
return new _Audio({ data: base64Data, format });
|
|
7387
|
-
}
|
|
7388
|
-
/**
|
|
7389
|
-
* Create Audio from a base64 string.
|
|
7390
|
-
*/
|
|
7391
|
-
static async fromBase64(base64Data, format = "wav") {
|
|
7392
|
-
return new _Audio({ data: base64Data, format });
|
|
7393
|
-
}
|
|
7394
|
-
};
|
|
7395
|
-
var Video = class _Video {
|
|
7396
|
-
type = "video_url";
|
|
7397
|
-
videoUrl;
|
|
7398
|
-
constructor(videoUrl) {
|
|
7399
|
-
this.videoUrl = videoUrl;
|
|
7827
|
+
parseTarget(target) {
|
|
7828
|
+
if (!target.includes(".")) {
|
|
7829
|
+
return { name: target };
|
|
7830
|
+
}
|
|
7831
|
+
const [agentId, remainder] = target.split(".", 2);
|
|
7832
|
+
const name = remainder.replace(":", "/");
|
|
7833
|
+
return { agentId, name };
|
|
7400
7834
|
}
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7835
|
+
};
|
|
7836
|
+
|
|
7837
|
+
// src/router/AgentRouter.ts
|
|
7838
|
+
var AgentRouter = class {
|
|
7839
|
+
prefix;
|
|
7840
|
+
tags;
|
|
7841
|
+
reasoners = [];
|
|
7842
|
+
skills = [];
|
|
7843
|
+
constructor(options = {}) {
|
|
7844
|
+
this.prefix = options.prefix;
|
|
7845
|
+
this.tags = options.tags;
|
|
7411
7846
|
}
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
return new _Video({ url });
|
|
7847
|
+
reasoner(name, handler, options) {
|
|
7848
|
+
const fullName = this.prefix ? `${sanitize(this.prefix)}_${name}` : name;
|
|
7849
|
+
this.reasoners.push({ name: fullName, handler, options });
|
|
7850
|
+
return this;
|
|
7417
7851
|
}
|
|
7418
|
-
|
|
7419
|
-
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
const base64Data = Buffer.from(buffer).toString("base64");
|
|
7423
|
-
return new _Video({ url: `data:${mimeType};base64,${base64Data}` });
|
|
7852
|
+
skill(name, handler, options) {
|
|
7853
|
+
const fullName = this.prefix ? `${sanitize(this.prefix)}_${name}` : name;
|
|
7854
|
+
this.skills.push({ name: fullName, handler, options });
|
|
7855
|
+
return this;
|
|
7424
7856
|
}
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7857
|
+
};
|
|
7858
|
+
function sanitize(value) {
|
|
7859
|
+
return value.replace(/[^0-9a-zA-Z]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
7860
|
+
}
|
|
7861
|
+
var JWE_ALG = "ECDH-ES";
|
|
7862
|
+
var JWE_ENC = "A256GCM";
|
|
7863
|
+
var KEY_AGREEMENT_TYPE = "X25519KeyAgreementKey2020";
|
|
7864
|
+
var PayloadEncryptionError = class extends Error {
|
|
7865
|
+
constructor(message) {
|
|
7866
|
+
super(message);
|
|
7867
|
+
this.name = "PayloadEncryptionError";
|
|
7430
7868
|
}
|
|
7431
7869
|
};
|
|
7432
|
-
var
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7870
|
+
var encoder = new TextEncoder();
|
|
7871
|
+
function payloadToBytes(payload) {
|
|
7872
|
+
if (payload instanceof Uint8Array) return payload;
|
|
7873
|
+
if (typeof payload === "string") return encoder.encode(payload);
|
|
7874
|
+
return encoder.encode(JSON.stringify(payload));
|
|
7875
|
+
}
|
|
7876
|
+
async function generateX25519KeyPair() {
|
|
7877
|
+
const { privateKey, publicKey } = await generateKeyPair("ECDH-ES", {
|
|
7878
|
+
crv: "X25519",
|
|
7879
|
+
extractable: true
|
|
7880
|
+
});
|
|
7881
|
+
const privateJwk = await exportJWK(privateKey);
|
|
7882
|
+
const publicJwk = await exportJWK(publicKey);
|
|
7883
|
+
return { privateJwk, publicJwk };
|
|
7884
|
+
}
|
|
7885
|
+
function extractKeyAgreementJwk(didDocument) {
|
|
7886
|
+
if (typeof didDocument !== "object" || didDocument === null) {
|
|
7887
|
+
throw new PayloadEncryptionError("DID document must be an object");
|
|
7437
7888
|
}
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
const absolutePath = resolve(filePath);
|
|
7443
|
-
const detectedMimeType = mimeType || guessMimeType(absolutePath) || "application/octet-stream";
|
|
7444
|
-
const buffer = await readFile(absolutePath);
|
|
7445
|
-
const base64Data = buffer.toString("base64");
|
|
7446
|
-
const dataUrl = `data:${detectedMimeType};base64,${base64Data}`;
|
|
7447
|
-
return new _File({ url: dataUrl, mimeType: detectedMimeType });
|
|
7889
|
+
const doc = didDocument["did_document"] ?? didDocument;
|
|
7890
|
+
const flat = doc["key_agreement"];
|
|
7891
|
+
if (flat && typeof flat === "object" && flat.crv === "X25519") {
|
|
7892
|
+
return flat;
|
|
7448
7893
|
}
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7894
|
+
const keyAgreement = doc["keyAgreement"];
|
|
7895
|
+
if (!keyAgreement || Array.isArray(keyAgreement) && keyAgreement.length === 0) {
|
|
7896
|
+
throw new PayloadEncryptionError(
|
|
7897
|
+
"DID document has no keyAgreement key; the agent has not published an X25519 encryption key"
|
|
7898
|
+
);
|
|
7454
7899
|
}
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
7461
|
-
return new _File({ url: dataUrl, mimeType });
|
|
7900
|
+
const verificationMethods = /* @__PURE__ */ new Map();
|
|
7901
|
+
for (const vm2 of doc["verificationMethod"] ?? []) {
|
|
7902
|
+
if (vm2 && typeof vm2 === "object" && typeof vm2["id"] === "string") {
|
|
7903
|
+
verificationMethods.set(vm2["id"], vm2);
|
|
7904
|
+
}
|
|
7462
7905
|
}
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7906
|
+
const entry = Array.isArray(keyAgreement) ? keyAgreement[0] : keyAgreement;
|
|
7907
|
+
let vm;
|
|
7908
|
+
if (typeof entry === "string") {
|
|
7909
|
+
vm = verificationMethods.get(entry);
|
|
7910
|
+
if (!vm) {
|
|
7911
|
+
throw new PayloadEncryptionError(
|
|
7912
|
+
`keyAgreement references unknown verification method ${entry}`
|
|
7913
|
+
);
|
|
7914
|
+
}
|
|
7915
|
+
} else if (entry && typeof entry === "object") {
|
|
7916
|
+
vm = entry;
|
|
7917
|
+
} else {
|
|
7918
|
+
throw new PayloadEncryptionError("unsupported keyAgreement entry shape");
|
|
7469
7919
|
}
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7920
|
+
const jwk = vm["publicKeyJwk"];
|
|
7921
|
+
if (!jwk || jwk.crv !== "X25519") {
|
|
7922
|
+
throw new PayloadEncryptionError(
|
|
7923
|
+
"keyAgreement verification method does not carry an X25519 publicKeyJwk"
|
|
7924
|
+
);
|
|
7475
7925
|
}
|
|
7476
|
-
return
|
|
7926
|
+
return jwk;
|
|
7477
7927
|
}
|
|
7478
|
-
function
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
return IMAGE_MIME_TYPES[ext];
|
|
7482
|
-
}
|
|
7483
|
-
if (ext in AUDIO_MIME_TYPES) {
|
|
7484
|
-
return AUDIO_MIME_TYPES[ext];
|
|
7928
|
+
async function encryptToJwk(publicJwk, payload) {
|
|
7929
|
+
if (!publicJwk || publicJwk.crv !== "X25519") {
|
|
7930
|
+
throw new PayloadEncryptionError("publicJwk must be an X25519 OKP JWK");
|
|
7485
7931
|
}
|
|
7486
|
-
|
|
7487
|
-
|
|
7932
|
+
try {
|
|
7933
|
+
const key = await importJWK(publicJwk, JWE_ALG);
|
|
7934
|
+
return await new CompactEncrypt(payloadToBytes(payload)).setProtectedHeader({ alg: JWE_ALG, enc: JWE_ENC }).encrypt(key);
|
|
7935
|
+
} catch (err) {
|
|
7936
|
+
if (err instanceof PayloadEncryptionError) throw err;
|
|
7937
|
+
throw new PayloadEncryptionError(`encryption failed: ${err.message}`);
|
|
7488
7938
|
}
|
|
7489
|
-
const documentMimeTypes = {
|
|
7490
|
-
".pdf": "application/pdf",
|
|
7491
|
-
".doc": "application/msword",
|
|
7492
|
-
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
7493
|
-
".xls": "application/vnd.ms-excel",
|
|
7494
|
-
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
7495
|
-
".txt": "text/plain",
|
|
7496
|
-
".csv": "text/csv",
|
|
7497
|
-
".html": "text/html",
|
|
7498
|
-
".json": "application/json",
|
|
7499
|
-
".xml": "application/xml",
|
|
7500
|
-
".zip": "application/zip"
|
|
7501
|
-
};
|
|
7502
|
-
return documentMimeTypes[ext] || null;
|
|
7503
|
-
}
|
|
7504
|
-
function text(content) {
|
|
7505
|
-
return new Text(content);
|
|
7506
|
-
}
|
|
7507
|
-
async function imageFromFile(filePath, detail = "high") {
|
|
7508
|
-
return Image.fromFile(filePath, detail);
|
|
7509
|
-
}
|
|
7510
|
-
function imageFromUrl(url, detail = "high") {
|
|
7511
|
-
return Image.fromUrl(url, detail);
|
|
7512
|
-
}
|
|
7513
|
-
async function imageFromBuffer(buffer, mimeType = "image/jpeg", detail = "high") {
|
|
7514
|
-
return Image.fromBuffer(buffer, mimeType, detail);
|
|
7515
|
-
}
|
|
7516
|
-
async function imageFromBase64(base64Data, mimeType, detail = "high") {
|
|
7517
|
-
return Image.fromBase64(base64Data, mimeType, detail);
|
|
7518
|
-
}
|
|
7519
|
-
async function audioFromFile(filePath, format) {
|
|
7520
|
-
return Audio.fromFile(filePath, format);
|
|
7521
|
-
}
|
|
7522
|
-
async function audioFromUrl(url, format = "wav") {
|
|
7523
|
-
return Audio.fromUrl(url, format);
|
|
7524
|
-
}
|
|
7525
|
-
async function audioFromBuffer(buffer, format = "wav") {
|
|
7526
|
-
return Audio.fromBuffer(buffer, format);
|
|
7527
|
-
}
|
|
7528
|
-
async function audioFromBase64(base64Data, format = "wav") {
|
|
7529
|
-
return Audio.fromBase64(base64Data, format);
|
|
7530
|
-
}
|
|
7531
|
-
async function videoFromFile(filePath) {
|
|
7532
|
-
return Video.fromFile(filePath);
|
|
7533
|
-
}
|
|
7534
|
-
function videoFromUrl(url) {
|
|
7535
|
-
return Video.fromUrl(url);
|
|
7536
7939
|
}
|
|
7537
|
-
async function
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
return File.fromFile(filePath, mimeType);
|
|
7545
|
-
}
|
|
7546
|
-
function fileFromUrl(url, mimeType) {
|
|
7547
|
-
return File.fromUrl(url, mimeType);
|
|
7940
|
+
async function encryptForDid(did, payload, resolver) {
|
|
7941
|
+
const document = await resolver(did);
|
|
7942
|
+
if (!document) {
|
|
7943
|
+
throw new PayloadEncryptionError(`could not resolve DID ${did}`);
|
|
7944
|
+
}
|
|
7945
|
+
const publicJwk = extractKeyAgreementJwk(document);
|
|
7946
|
+
return encryptToJwk(publicJwk, payload);
|
|
7548
7947
|
}
|
|
7549
|
-
async function
|
|
7550
|
-
|
|
7948
|
+
async function decrypt(token, privateJwk) {
|
|
7949
|
+
try {
|
|
7950
|
+
const key = await importJWK(privateJwk, JWE_ALG);
|
|
7951
|
+
const { plaintext } = await compactDecrypt(token, key);
|
|
7952
|
+
return Uint8Array.from(plaintext);
|
|
7953
|
+
} catch (err) {
|
|
7954
|
+
throw new PayloadEncryptionError(`decryption failed: ${err.message}`);
|
|
7955
|
+
}
|
|
7551
7956
|
}
|
|
7552
|
-
async function
|
|
7553
|
-
return
|
|
7957
|
+
async function decryptToString(token, privateJwk) {
|
|
7958
|
+
return new TextDecoder().decode(await decrypt(token, privateJwk));
|
|
7554
7959
|
}
|
|
7555
7960
|
var MultimodalResponse = class {
|
|
7556
7961
|
_text;
|
|
@@ -8367,7 +8772,7 @@ var OpenRouterMediaProvider = class {
|
|
|
8367
8772
|
);
|
|
8368
8773
|
}
|
|
8369
8774
|
const audioChunks = [];
|
|
8370
|
-
let
|
|
8775
|
+
let textContent2 = "";
|
|
8371
8776
|
const reader = res.body?.getReader();
|
|
8372
8777
|
if (!reader) {
|
|
8373
8778
|
throw new MediaProviderError("No response body stream available", {
|
|
@@ -8399,7 +8804,7 @@ var OpenRouterMediaProvider = class {
|
|
|
8399
8804
|
const delta = choice.delta;
|
|
8400
8805
|
if (!delta) continue;
|
|
8401
8806
|
if (typeof delta.content === "string") {
|
|
8402
|
-
|
|
8807
|
+
textContent2 += delta.content;
|
|
8403
8808
|
}
|
|
8404
8809
|
const audioDelta = delta.audio;
|
|
8405
8810
|
if (audioDelta?.data) {
|
|
@@ -8430,7 +8835,7 @@ var OpenRouterMediaProvider = class {
|
|
|
8430
8835
|
const delta = choice.delta;
|
|
8431
8836
|
if (!delta) continue;
|
|
8432
8837
|
if (typeof delta.content === "string") {
|
|
8433
|
-
|
|
8838
|
+
textContent2 += delta.content;
|
|
8434
8839
|
}
|
|
8435
8840
|
const audioDelta = delta.audio;
|
|
8436
8841
|
if (audioDelta?.data) {
|
|
@@ -8444,7 +8849,7 @@ var OpenRouterMediaProvider = class {
|
|
|
8444
8849
|
}
|
|
8445
8850
|
}
|
|
8446
8851
|
const resp = emptyMediaResponse(null);
|
|
8447
|
-
resp.text =
|
|
8852
|
+
resp.text = textContent2;
|
|
8448
8853
|
if (audioChunks.length > 0) {
|
|
8449
8854
|
let b64 = audioChunks.join("");
|
|
8450
8855
|
try {
|
|
@@ -8589,6 +8994,6 @@ function loadFixture(source) {
|
|
|
8589
8994
|
);
|
|
8590
8995
|
}
|
|
8591
8996
|
|
|
8592
|
-
export { ACTIVE_STATUSES, AIClient, Agent, AgentRouter, ApprovalClient, ApprovalResult, Audio, CANONICAL_STATUSES, CostTracker, DIDAuthenticator, DidClient, DidInterface, DidManager, ExecutionContext, ExecutionLogger, ExecutionStatus, File, HEADER_CALLER_DID, HEADER_DID_NONCE, HEADER_DID_SIGNATURE, HEADER_DID_TIMESTAMP, HarnessRunner, Image, JWE_ALG, JWE_ENC, KEY_AGREEMENT_TYPE, MODEL_VARIANT_SEP, MediaProviderError, MediaRouter, MemoryClient, MemoryClientBase, MemoryEventClient, MemoryInterface, MultimodalResponse, OpenRouterMediaProvider, PauseClock, PauseManager, PayloadEncryptionError, RateLimitError, RealtimeSession, ReasonerContext, SUPPORTED_PROVIDERS, SUPPORTED_SESSION_TRANSPORTS, SessionTransportError, SkillContext, StatelessRateLimiter, TERMINAL_STATUSES, Text, USAGE_ENVELOPE_KEY, Video, WorkflowReporter, applyTriggerTransform, attachUsageToSyncResult, audioFromBase64, audioFromBuffer, audioFromFile, audioFromUrl, buildProvider, buildSessionDefinition, buildToolConfig, capabilitiesToTools, capabilityToMetadataTool, capabilityToTool, createExecutionLogger, createHarnessResult, createMetrics, createMultimodalResponse, createRawResult, decrypt, decryptToString, deriveProvider, encryptForDid, encryptToJwk, eventTrigger, executeToolCallLoop, extractKeyAgreementJwk, fileFromBase64, fileFromBuffer, fileFromPath, fileFromUrl, generateX25519KeyPair, getCurrentContext, getCurrentSkillContext, imageFromBase64, imageFromBuffer, imageFromFile, imageFromUrl, installApprovalWebhookRoute, isActive, isExecutionLogBatchPayload, isTerminal, isTriggerEnvelope, loadFixture, normalizeExecutionLogEntry, normalizeSessionTransportValue, normalizeStatus, resolveModelAndVariant, scheduleTrigger, serializeExecutionLogEntry, simulateSchedule, simulateTrigger, splitModelVariant, text, triggerToPayload, unwrapEnvelope, usageSummaryOrNull, validateSessionTransport, videoFromBase64, videoFromBuffer, videoFromFile, videoFromUrl };
|
|
8997
|
+
export { ACTIVE_STATUSES, AIClient, Agent, AgentRouter, ApprovalClient, ApprovalResult, Audio, CANONICAL_STATUSES, CostTracker, DEFAULT_HARNESS_PROVIDER, DIDAuthenticator, DidClient, DidInterface, DidManager, ExecutionContext, ExecutionLogger, ExecutionStatus, File, HARNESS_PROVIDER_ENV_VAR, HEADER_CALLER_DID, HEADER_DID_NONCE, HEADER_DID_SIGNATURE, HEADER_DID_TIMESTAMP, HarnessRunner, Image, JWE_ALG, JWE_ENC, KEY_AGREEMENT_TYPE, MODEL_VARIANT_SEP, MediaProviderError, MediaRouter, MemoryClient, MemoryClientBase, MemoryEventClient, MemoryInterface, MultimodalResponse, OpenRouterMediaProvider, PauseClock, PauseManager, PayloadEncryptionError, RateLimitError, RealtimeSession, ReasonerContext, SUPPORTED_PROVIDERS, SUPPORTED_SESSION_TRANSPORTS, SessionTransportError, SkillContext, StatelessRateLimiter, TERMINAL_STATUSES, Text, USAGE_ENVELOPE_KEY, Video, WorkflowReporter, applyTriggerTransform, attachUsageToSyncResult, audioFromBase64, audioFromBuffer, audioFromFile, audioFromUrl, audioMediaType, buildProvider, buildSessionDefinition, buildToolConfig, capabilitiesToTools, capabilityToMetadataTool, capabilityToTool, createExecutionLogger, createHarnessResult, createMetrics, createMultimodalResponse, createRawResult, decrypt, decryptToString, deriveProvider, encryptForDid, encryptToJwk, eventTrigger, executeToolCallLoop, extractKeyAgreementJwk, fileFromBase64, fileFromBuffer, fileFromPath, fileFromUrl, generateX25519KeyPair, getCurrentContext, getCurrentSkillContext, guessUrlMimeType, imageFromBase64, imageFromBuffer, imageFromFile, imageFromUrl, installApprovalWebhookRoute, isActive, isExecutionLogBatchPayload, isTerminal, isTriggerEnvelope, loadFixture, normalizeExecutionLogEntry, normalizeSessionTransportValue, normalizeStatus, resolveModelAndVariant, resolveProviderName, scheduleTrigger, serializeExecutionLogEntry, simulateSchedule, simulateTrigger, splitModelVariant, text, triggerToPayload, unwrapEnvelope, usageSummaryOrNull, validateSessionTransport, videoFromBase64, videoFromBuffer, videoFromFile, videoFromUrl };
|
|
8593
8998
|
//# sourceMappingURL=index.js.map
|
|
8594
8999
|
//# sourceMappingURL=index.js.map
|