@agentfield/sdk 0.1.130-rc.3 → 0.1.130-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +24 -1
- package/dist/index.js +452 -172
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs, { promises, readFileSync } from 'fs';
|
|
2
|
-
import * as
|
|
3
|
-
import
|
|
2
|
+
import * as path3 from 'path';
|
|
3
|
+
import path3__default, { resolve, dirname } from 'path';
|
|
4
4
|
import { createRequire } from 'module';
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
6
|
import express from 'express';
|
|
@@ -197,7 +197,7 @@ function estimateTokens(text2) {
|
|
|
197
197
|
}
|
|
198
198
|
function writeSchemaFile(schemaJson, cwd) {
|
|
199
199
|
const filePath = getSchemaPath(cwd);
|
|
200
|
-
fs.mkdirSync(
|
|
200
|
+
fs.mkdirSync(path3__default.dirname(filePath), { recursive: true });
|
|
201
201
|
const fd = fs.openSync(filePath, "w", 384);
|
|
202
202
|
try {
|
|
203
203
|
fs.writeFileSync(fd, schemaJson, "utf8");
|
|
@@ -213,10 +213,10 @@ function validateAgainstSchema(data, schema) {
|
|
|
213
213
|
return data;
|
|
214
214
|
}
|
|
215
215
|
function getOutputPath(cwd) {
|
|
216
|
-
return
|
|
216
|
+
return path3__default.join(cwd, OUTPUT_FILENAME);
|
|
217
217
|
}
|
|
218
218
|
function getSchemaPath(cwd) {
|
|
219
|
-
return
|
|
219
|
+
return path3__default.join(cwd, SCHEMA_FILENAME);
|
|
220
220
|
}
|
|
221
221
|
function schemaToJsonSchema(schema) {
|
|
222
222
|
if (isRecord2(schema)) {
|
|
@@ -322,7 +322,7 @@ function parseAndValidate(filePath, schema) {
|
|
|
322
322
|
function cleanupTempFiles(cwd) {
|
|
323
323
|
for (const filename of [OUTPUT_FILENAME, SCHEMA_FILENAME]) {
|
|
324
324
|
try {
|
|
325
|
-
fs.unlinkSync(
|
|
325
|
+
fs.unlinkSync(path3__default.join(cwd, filename));
|
|
326
326
|
} catch {
|
|
327
327
|
}
|
|
328
328
|
}
|
|
@@ -361,6 +361,388 @@ var init_types = __esm({
|
|
|
361
361
|
"src/harness/types.ts"() {
|
|
362
362
|
}
|
|
363
363
|
});
|
|
364
|
+
function resolveIdleMs(idleSeconds) {
|
|
365
|
+
let seconds = idleSeconds;
|
|
366
|
+
if (seconds === void 0) {
|
|
367
|
+
const raw = process.env.AGENTFIELD_HARNESS_IDLE_SECONDS;
|
|
368
|
+
const parsed = raw !== void 0 ? Number(raw) : NaN;
|
|
369
|
+
seconds = Number.isFinite(parsed) ? parsed : DEFAULT_IDLE_SECONDS;
|
|
370
|
+
}
|
|
371
|
+
return seconds > 0 ? seconds * 1e3 : void 0;
|
|
372
|
+
}
|
|
373
|
+
function runCli(cmd, options) {
|
|
374
|
+
return new Promise((resolve3, reject) => {
|
|
375
|
+
const [bin, ...args] = cmd;
|
|
376
|
+
const env = { ...process.env, ...options?.env };
|
|
377
|
+
applyOpenRouterAttributionEnv(env);
|
|
378
|
+
const hasInput = options?.inputText !== void 0;
|
|
379
|
+
const proc = spawn(bin, args, {
|
|
380
|
+
env,
|
|
381
|
+
cwd: options?.cwd,
|
|
382
|
+
stdio: [hasInput ? "pipe" : "ignore", "pipe", "pipe"]
|
|
383
|
+
});
|
|
384
|
+
if (hasInput) {
|
|
385
|
+
proc.stdin?.end(options.inputText);
|
|
386
|
+
}
|
|
387
|
+
let stdout = "";
|
|
388
|
+
let stderr = "";
|
|
389
|
+
let settled = false;
|
|
390
|
+
let lastActivity = Date.now();
|
|
391
|
+
proc.stdout.on("data", (data) => {
|
|
392
|
+
stdout += data.toString();
|
|
393
|
+
lastActivity = Date.now();
|
|
394
|
+
});
|
|
395
|
+
proc.stderr.on("data", (data) => {
|
|
396
|
+
stderr += data.toString();
|
|
397
|
+
lastActivity = Date.now();
|
|
398
|
+
});
|
|
399
|
+
const timer = options?.timeout ? setTimeout(() => {
|
|
400
|
+
if (settled) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
settled = true;
|
|
404
|
+
cleanup();
|
|
405
|
+
proc.kill("SIGKILL");
|
|
406
|
+
reject(new Error(`CLI timed out after ${options.timeout}ms`));
|
|
407
|
+
}, options.timeout) : void 0;
|
|
408
|
+
const idleMs = resolveIdleMs(options?.idleSeconds);
|
|
409
|
+
const idleTimer = idleMs ? setInterval(() => {
|
|
410
|
+
if (settled) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
if (Date.now() - lastActivity >= idleMs) {
|
|
414
|
+
settled = true;
|
|
415
|
+
cleanup();
|
|
416
|
+
proc.kill("SIGKILL");
|
|
417
|
+
reject(
|
|
418
|
+
new Error(
|
|
419
|
+
`CLI made no progress for ${Math.round(idleMs / 1e3)}s`
|
|
420
|
+
)
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
}, Math.min(idleMs, 1e3)) : void 0;
|
|
424
|
+
function cleanup() {
|
|
425
|
+
if (timer) {
|
|
426
|
+
clearTimeout(timer);
|
|
427
|
+
}
|
|
428
|
+
if (idleTimer) {
|
|
429
|
+
clearInterval(idleTimer);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
proc.on("close", (code) => {
|
|
433
|
+
if (settled) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
settled = true;
|
|
437
|
+
cleanup();
|
|
438
|
+
resolve3({ stdout, stderr, exitCode: code ?? 0 });
|
|
439
|
+
});
|
|
440
|
+
proc.on("error", (err) => {
|
|
441
|
+
if (settled) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
settled = true;
|
|
445
|
+
cleanup();
|
|
446
|
+
reject(err);
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
function parseJsonl(text2) {
|
|
451
|
+
const events = [];
|
|
452
|
+
for (const line of text2.split("\n")) {
|
|
453
|
+
const trimmed = line.trim();
|
|
454
|
+
if (!trimmed) {
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
try {
|
|
458
|
+
events.push(JSON.parse(trimmed));
|
|
459
|
+
} catch {
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return events;
|
|
464
|
+
}
|
|
465
|
+
function extractFinalText(events) {
|
|
466
|
+
let result;
|
|
467
|
+
for (const event of events) {
|
|
468
|
+
const type = event.type;
|
|
469
|
+
if (type === "item.completed") {
|
|
470
|
+
const item = event.item;
|
|
471
|
+
if (typeof item === "object" && item !== null) {
|
|
472
|
+
const itemType = item.type;
|
|
473
|
+
const itemText = item.text;
|
|
474
|
+
if (itemType === "agent_message" && typeof itemText === "string") {
|
|
475
|
+
result = itemText;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
} else if (type === "result") {
|
|
479
|
+
const candidate = event.result ?? event.text;
|
|
480
|
+
if (typeof candidate === "string") {
|
|
481
|
+
result = candidate;
|
|
482
|
+
}
|
|
483
|
+
} else if (type === "turn.completed" && typeof event.text === "string") {
|
|
484
|
+
result = event.text;
|
|
485
|
+
} else if ((type === "message" || type === "assistant") && typeof event.content === "string") {
|
|
486
|
+
result = event.content;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return result;
|
|
490
|
+
}
|
|
491
|
+
var DEFAULT_IDLE_SECONDS;
|
|
492
|
+
var init_cli = __esm({
|
|
493
|
+
"src/harness/cli.ts"() {
|
|
494
|
+
init_openrouterAttribution();
|
|
495
|
+
DEFAULT_IDLE_SECONDS = 120;
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
// src/harness/providers/aforge.ts
|
|
500
|
+
var aforge_exports = {};
|
|
501
|
+
__export(aforge_exports, {
|
|
502
|
+
AforgeProvider: () => AforgeProvider
|
|
503
|
+
});
|
|
504
|
+
function resolveMaxConcurrent() {
|
|
505
|
+
const parsed = Number.parseInt(process.env.AFORGE_MAX_CONCURRENT ?? "", 10);
|
|
506
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_MAX_CONCURRENT;
|
|
507
|
+
}
|
|
508
|
+
function stripOpenRouterPrefix(model) {
|
|
509
|
+
return model.startsWith("openrouter/") ? model.slice("openrouter/".length) : model;
|
|
510
|
+
}
|
|
511
|
+
function parseEnvelope(stdout) {
|
|
512
|
+
try {
|
|
513
|
+
const value = JSON.parse(stdout.trim());
|
|
514
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && ("deliverable" in value || "text" in value)) {
|
|
515
|
+
return value;
|
|
516
|
+
}
|
|
517
|
+
} catch {
|
|
518
|
+
}
|
|
519
|
+
const lines = stdout.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
520
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
521
|
+
try {
|
|
522
|
+
const value = JSON.parse(lines[index]);
|
|
523
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && ("deliverable" in value || "text" in value)) {
|
|
524
|
+
return value;
|
|
525
|
+
}
|
|
526
|
+
} catch {
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return void 0;
|
|
530
|
+
}
|
|
531
|
+
function numeric(value) {
|
|
532
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
533
|
+
}
|
|
534
|
+
function timeoutSeconds() {
|
|
535
|
+
const parsed = Number.parseInt(process.env.AGENTFIELD_HARNESS_TIMEOUT_SECONDS ?? "", 10);
|
|
536
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TIMEOUT_SECONDS;
|
|
537
|
+
}
|
|
538
|
+
function innerTimeout(outer) {
|
|
539
|
+
return outer > LANDING_WINDOW_SECONDS ? outer - LANDING_WINDOW_SECONDS : 1;
|
|
540
|
+
}
|
|
541
|
+
function taskInput(prompt, systemPrompt) {
|
|
542
|
+
return typeof systemPrompt === "string" && systemPrompt.trim() ? `${systemPrompt.trim()}
|
|
543
|
+
|
|
544
|
+
Task:
|
|
545
|
+
${prompt}` : prompt;
|
|
546
|
+
}
|
|
547
|
+
function crashMessage(exitCode, blockedOn, deliverable, stderr) {
|
|
548
|
+
const cleanStderr = stderr.trim().replace(ANSI_PATTERN, "");
|
|
549
|
+
const exitContext = `aforge exit code ${exitCode}`;
|
|
550
|
+
let message = exitCode < 0 ? `Process killed by signal ${-exitCode}. ${exitContext}` : exitContext;
|
|
551
|
+
if (cleanStderr) {
|
|
552
|
+
message += `. stderr: ${cleanStderr.slice(0, 1e3)}`;
|
|
553
|
+
} else if (blockedOn) {
|
|
554
|
+
message += `. blocked_on: ${blockedOn.slice(0, 1e3)}`;
|
|
555
|
+
} else if (deliverable) {
|
|
556
|
+
message += `. partial: ${deliverable.slice(0, 1e3)}`;
|
|
557
|
+
}
|
|
558
|
+
return message;
|
|
559
|
+
}
|
|
560
|
+
function stringOptions(value) {
|
|
561
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
562
|
+
return {};
|
|
563
|
+
}
|
|
564
|
+
const result = {};
|
|
565
|
+
for (const [key, item] of Object.entries(value)) {
|
|
566
|
+
if (typeof item === "string") {
|
|
567
|
+
result[key] = item;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return result;
|
|
571
|
+
}
|
|
572
|
+
var REASONING_VARIANTS, DEFAULT_TIMEOUT_SECONDS, LANDING_WINDOW_SECONDS, DEFAULT_MAX_CONCURRENT, ANSI_PATTERN, Semaphore, aforgeSemaphore, AforgeProvider;
|
|
573
|
+
var init_aforge = __esm({
|
|
574
|
+
"src/harness/providers/aforge.ts"() {
|
|
575
|
+
init_types();
|
|
576
|
+
init_cli();
|
|
577
|
+
init_modelVariant();
|
|
578
|
+
REASONING_VARIANTS = /* @__PURE__ */ new Set(["off", "low", "medium", "high"]);
|
|
579
|
+
DEFAULT_TIMEOUT_SECONDS = 1800;
|
|
580
|
+
LANDING_WINDOW_SECONDS = 5;
|
|
581
|
+
DEFAULT_MAX_CONCURRENT = 8;
|
|
582
|
+
ANSI_PATTERN = /\x1B\[[0-?]*[ -/]*[@-~]/g;
|
|
583
|
+
Semaphore = class {
|
|
584
|
+
constructor(limit) {
|
|
585
|
+
this.limit = limit;
|
|
586
|
+
}
|
|
587
|
+
limit;
|
|
588
|
+
active = 0;
|
|
589
|
+
waiters = [];
|
|
590
|
+
async use(operation) {
|
|
591
|
+
await this.acquire();
|
|
592
|
+
try {
|
|
593
|
+
return await operation();
|
|
594
|
+
} finally {
|
|
595
|
+
this.release();
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
acquire() {
|
|
599
|
+
if (this.active < this.limit) {
|
|
600
|
+
this.active += 1;
|
|
601
|
+
return Promise.resolve();
|
|
602
|
+
}
|
|
603
|
+
return new Promise((resolve3) => {
|
|
604
|
+
this.waiters.push(() => {
|
|
605
|
+
this.active += 1;
|
|
606
|
+
resolve3();
|
|
607
|
+
});
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
release() {
|
|
611
|
+
this.active -= 1;
|
|
612
|
+
this.waiters.shift()?.();
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
aforgeSemaphore = new Semaphore(resolveMaxConcurrent());
|
|
616
|
+
AforgeProvider = class {
|
|
617
|
+
bin;
|
|
618
|
+
constructor(bin = "aforge") {
|
|
619
|
+
this.bin = bin === "aforge" ? process.env.AFORGE_BIN?.trim() || bin : bin;
|
|
620
|
+
}
|
|
621
|
+
async execute(prompt, options) {
|
|
622
|
+
return aforgeSemaphore.use(() => this.executeImpl(prompt, options));
|
|
623
|
+
}
|
|
624
|
+
async executeImpl(prompt, options) {
|
|
625
|
+
const projectDir = typeof options.projectDir === "string" ? options.projectDir : typeof options.project_dir === "string" ? options.project_dir : void 0;
|
|
626
|
+
const cwd = typeof options.cwd === "string" ? options.cwd : void 0;
|
|
627
|
+
const root = projectDir ?? cwd ?? ".";
|
|
628
|
+
const outerTimeout = timeoutSeconds();
|
|
629
|
+
const command = (process.env.AGENTFIELD_AFORGE_COMMAND ?? "exec").trim().toLowerCase();
|
|
630
|
+
if (command !== "do" && command !== "exec") {
|
|
631
|
+
return createRawResult({
|
|
632
|
+
isError: true,
|
|
633
|
+
errorMessage: `AGENTFIELD_AFORGE_COMMAND must be 'do' or 'exec', got ${JSON.stringify(command)}`,
|
|
634
|
+
failureType: "crash",
|
|
635
|
+
metrics: createMetrics()
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
const systemPrompt = options.systemPrompt ?? options.system_prompt;
|
|
639
|
+
const cmd = command === "exec" ? [
|
|
640
|
+
this.bin,
|
|
641
|
+
"exec",
|
|
642
|
+
"--json",
|
|
643
|
+
"-w",
|
|
644
|
+
root,
|
|
645
|
+
"--timeout",
|
|
646
|
+
String(innerTimeout(outerTimeout)),
|
|
647
|
+
"--context-fill",
|
|
648
|
+
"60",
|
|
649
|
+
"--completion-reserve",
|
|
650
|
+
"65536"
|
|
651
|
+
] : [
|
|
652
|
+
this.bin,
|
|
653
|
+
"do",
|
|
654
|
+
"--json",
|
|
655
|
+
"--yes-spend",
|
|
656
|
+
"-w",
|
|
657
|
+
root,
|
|
658
|
+
"--timeout",
|
|
659
|
+
String(innerTimeout(outerTimeout))
|
|
660
|
+
];
|
|
661
|
+
if (command === "exec" && typeof systemPrompt === "string" && systemPrompt.trim()) {
|
|
662
|
+
cmd.push("--system", systemPrompt.trim());
|
|
663
|
+
}
|
|
664
|
+
const { model, variant } = resolveModelAndVariant(options);
|
|
665
|
+
const env = command === "exec" ? { AFORGE_MODELS: "" } : {};
|
|
666
|
+
if (model) {
|
|
667
|
+
const slug = stripOpenRouterPrefix(model);
|
|
668
|
+
env.AFORGE_MODEL = slug;
|
|
669
|
+
if (command === "exec") {
|
|
670
|
+
cmd.push("--model", slug, "--plan-model", slug);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
if (variant) {
|
|
674
|
+
const normalized = variant.trim().toLowerCase();
|
|
675
|
+
if (REASONING_VARIANTS.has(normalized)) {
|
|
676
|
+
env.AFORGE_EXEC_REASONING = normalized;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
Object.assign(env, stringOptions(options.env));
|
|
680
|
+
const startApi = Date.now();
|
|
681
|
+
try {
|
|
682
|
+
const { stdout, stderr, exitCode } = await runCli(cmd, {
|
|
683
|
+
env,
|
|
684
|
+
cwd: void 0,
|
|
685
|
+
timeout: outerTimeout * 1e3,
|
|
686
|
+
idleSeconds: 0,
|
|
687
|
+
inputText: command === "exec" ? prompt : taskInput(prompt, systemPrompt)
|
|
688
|
+
});
|
|
689
|
+
const envelope = parseEnvelope(stdout);
|
|
690
|
+
const outputValue = command === "exec" ? envelope?.text : envelope?.deliverable;
|
|
691
|
+
const resultText = typeof outputValue === "string" && outputValue.trim() ? outputValue.trim() : void 0;
|
|
692
|
+
const blockedOn = typeof envelope?.blocked_on === "string" ? envelope.blocked_on.trim() : "";
|
|
693
|
+
const stop = typeof envelope?.stop === "string" ? envelope.stop.trim() : "";
|
|
694
|
+
const usage = typeof envelope?.usage === "object" && envelope.usage !== null && !Array.isArray(envelope.usage) ? envelope.usage : {};
|
|
695
|
+
const isError = command === "exec" ? exitCode < 0 || resultText === void 0 || ![0, 2, 3].includes(exitCode) : exitCode !== 0 || resultText === void 0 || blockedOn !== "";
|
|
696
|
+
const inputTokens = Math.trunc(numeric(usage.prompt_tokens) ?? 0);
|
|
697
|
+
const outputTokens = Math.trunc(numeric(usage.completion_tokens) ?? 0);
|
|
698
|
+
const cacheReadTokens = Math.trunc(numeric(usage.cached_tokens) ?? 0);
|
|
699
|
+
const calls = Math.trunc(numeric(command === "exec" ? envelope?.turns : usage.calls) ?? 0);
|
|
700
|
+
const nativeSpend = numeric(envelope?.spend);
|
|
701
|
+
const legacyCost = numeric(usage.cost);
|
|
702
|
+
const providerCost = nativeSpend !== void 0 && nativeSpend > 0 ? nativeSpend : legacyCost;
|
|
703
|
+
return createRawResult({
|
|
704
|
+
result: resultText,
|
|
705
|
+
messages: envelope ? [envelope] : [],
|
|
706
|
+
metrics: createMetrics({
|
|
707
|
+
durationApiMs: Date.now() - startApi,
|
|
708
|
+
numTurns: calls,
|
|
709
|
+
totalCostUsd: providerCost !== void 0 && providerCost > 0 ? providerCost : void 0,
|
|
710
|
+
usage,
|
|
711
|
+
sessionId: "",
|
|
712
|
+
inputTokens,
|
|
713
|
+
outputTokens,
|
|
714
|
+
cacheReadTokens,
|
|
715
|
+
cacheCreationTokens: 0,
|
|
716
|
+
totalTokens: inputTokens + outputTokens,
|
|
717
|
+
model
|
|
718
|
+
}),
|
|
719
|
+
isError,
|
|
720
|
+
errorMessage: isError ? crashMessage(exitCode, blockedOn || stop, resultText, stderr) : void 0,
|
|
721
|
+
failureType: isError ? command === "do" && exitCode === 2 || command === "exec" && exitCode === 4 ? "timeout" : "crash" : "none",
|
|
722
|
+
returnCode: exitCode
|
|
723
|
+
});
|
|
724
|
+
} catch (error) {
|
|
725
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
726
|
+
if (message.includes("ENOENT")) {
|
|
727
|
+
return createRawResult({
|
|
728
|
+
isError: true,
|
|
729
|
+
errorMessage: `AForge binary not found at '${this.bin}'. Install it with \`af aforge ensure\`, or set AFORGE_BIN to its path.`,
|
|
730
|
+
failureType: "crash",
|
|
731
|
+
metrics: createMetrics({ durationApiMs: Date.now() - startApi })
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
const timedOut = /timed out|deadline exceeded|no progress/i.test(message);
|
|
735
|
+
return createRawResult({
|
|
736
|
+
isError: true,
|
|
737
|
+
errorMessage: message,
|
|
738
|
+
failureType: timedOut ? "timeout" : "crash",
|
|
739
|
+
metrics: createMetrics({ durationApiMs: Date.now() - startApi })
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
});
|
|
364
746
|
|
|
365
747
|
// src/harness/providers/claude.ts
|
|
366
748
|
var claude_exports = {};
|
|
@@ -499,136 +881,6 @@ var init_claude = __esm({
|
|
|
499
881
|
};
|
|
500
882
|
}
|
|
501
883
|
});
|
|
502
|
-
function resolveIdleMs(idleSeconds) {
|
|
503
|
-
let seconds = idleSeconds;
|
|
504
|
-
if (seconds === void 0) {
|
|
505
|
-
const raw = process.env.AGENTFIELD_HARNESS_IDLE_SECONDS;
|
|
506
|
-
const parsed = raw !== void 0 ? Number(raw) : NaN;
|
|
507
|
-
seconds = Number.isFinite(parsed) ? parsed : DEFAULT_IDLE_SECONDS;
|
|
508
|
-
}
|
|
509
|
-
return seconds > 0 ? seconds * 1e3 : void 0;
|
|
510
|
-
}
|
|
511
|
-
function runCli(cmd, options) {
|
|
512
|
-
return new Promise((resolve3, reject) => {
|
|
513
|
-
const [bin, ...args] = cmd;
|
|
514
|
-
const env = { ...process.env, ...options?.env };
|
|
515
|
-
applyOpenRouterAttributionEnv(env);
|
|
516
|
-
const proc = spawn(bin, args, {
|
|
517
|
-
env,
|
|
518
|
-
cwd: options?.cwd,
|
|
519
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
520
|
-
});
|
|
521
|
-
let stdout = "";
|
|
522
|
-
let stderr = "";
|
|
523
|
-
let settled = false;
|
|
524
|
-
let lastActivity = Date.now();
|
|
525
|
-
proc.stdout.on("data", (data) => {
|
|
526
|
-
stdout += data.toString();
|
|
527
|
-
lastActivity = Date.now();
|
|
528
|
-
});
|
|
529
|
-
proc.stderr.on("data", (data) => {
|
|
530
|
-
stderr += data.toString();
|
|
531
|
-
lastActivity = Date.now();
|
|
532
|
-
});
|
|
533
|
-
const timer = options?.timeout ? setTimeout(() => {
|
|
534
|
-
if (settled) {
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
settled = true;
|
|
538
|
-
cleanup();
|
|
539
|
-
proc.kill("SIGKILL");
|
|
540
|
-
reject(new Error(`CLI timed out after ${options.timeout}ms`));
|
|
541
|
-
}, options.timeout) : void 0;
|
|
542
|
-
const idleMs = resolveIdleMs(options?.idleSeconds);
|
|
543
|
-
const idleTimer = idleMs ? setInterval(() => {
|
|
544
|
-
if (settled) {
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
if (Date.now() - lastActivity >= idleMs) {
|
|
548
|
-
settled = true;
|
|
549
|
-
cleanup();
|
|
550
|
-
proc.kill("SIGKILL");
|
|
551
|
-
reject(
|
|
552
|
-
new Error(
|
|
553
|
-
`CLI made no progress for ${Math.round(idleMs / 1e3)}s`
|
|
554
|
-
)
|
|
555
|
-
);
|
|
556
|
-
}
|
|
557
|
-
}, Math.min(idleMs, 1e3)) : void 0;
|
|
558
|
-
function cleanup() {
|
|
559
|
-
if (timer) {
|
|
560
|
-
clearTimeout(timer);
|
|
561
|
-
}
|
|
562
|
-
if (idleTimer) {
|
|
563
|
-
clearInterval(idleTimer);
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
proc.on("close", (code) => {
|
|
567
|
-
if (settled) {
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
settled = true;
|
|
571
|
-
cleanup();
|
|
572
|
-
resolve3({ stdout, stderr, exitCode: code ?? 0 });
|
|
573
|
-
});
|
|
574
|
-
proc.on("error", (err) => {
|
|
575
|
-
if (settled) {
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
settled = true;
|
|
579
|
-
cleanup();
|
|
580
|
-
reject(err);
|
|
581
|
-
});
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
function parseJsonl(text2) {
|
|
585
|
-
const events = [];
|
|
586
|
-
for (const line of text2.split("\n")) {
|
|
587
|
-
const trimmed = line.trim();
|
|
588
|
-
if (!trimmed) {
|
|
589
|
-
continue;
|
|
590
|
-
}
|
|
591
|
-
try {
|
|
592
|
-
events.push(JSON.parse(trimmed));
|
|
593
|
-
} catch {
|
|
594
|
-
continue;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
return events;
|
|
598
|
-
}
|
|
599
|
-
function extractFinalText(events) {
|
|
600
|
-
let result;
|
|
601
|
-
for (const event of events) {
|
|
602
|
-
const type = event.type;
|
|
603
|
-
if (type === "item.completed") {
|
|
604
|
-
const item = event.item;
|
|
605
|
-
if (typeof item === "object" && item !== null) {
|
|
606
|
-
const itemType = item.type;
|
|
607
|
-
const itemText = item.text;
|
|
608
|
-
if (itemType === "agent_message" && typeof itemText === "string") {
|
|
609
|
-
result = itemText;
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
} else if (type === "result") {
|
|
613
|
-
const candidate = event.result ?? event.text;
|
|
614
|
-
if (typeof candidate === "string") {
|
|
615
|
-
result = candidate;
|
|
616
|
-
}
|
|
617
|
-
} else if (type === "turn.completed" && typeof event.text === "string") {
|
|
618
|
-
result = event.text;
|
|
619
|
-
} else if ((type === "message" || type === "assistant") && typeof event.content === "string") {
|
|
620
|
-
result = event.content;
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
return result;
|
|
624
|
-
}
|
|
625
|
-
var DEFAULT_IDLE_SECONDS;
|
|
626
|
-
var init_cli = __esm({
|
|
627
|
-
"src/harness/cli.ts"() {
|
|
628
|
-
init_openrouterAttribution();
|
|
629
|
-
DEFAULT_IDLE_SECONDS = 120;
|
|
630
|
-
}
|
|
631
|
-
});
|
|
632
884
|
|
|
633
885
|
// src/harness/providers/codex.ts
|
|
634
886
|
var codex_exports = {};
|
|
@@ -875,34 +1127,49 @@ ${prompt}`;
|
|
|
875
1127
|
});
|
|
876
1128
|
|
|
877
1129
|
// src/harness/providers/factory.ts
|
|
1130
|
+
function resolveProviderName(explicit) {
|
|
1131
|
+
const explicitName = explicit?.trim();
|
|
1132
|
+
if (explicitName) {
|
|
1133
|
+
return explicitName;
|
|
1134
|
+
}
|
|
1135
|
+
const envName = process.env[HARNESS_PROVIDER_ENV_VAR]?.trim();
|
|
1136
|
+
return envName || DEFAULT_HARNESS_PROVIDER;
|
|
1137
|
+
}
|
|
878
1138
|
async function buildProvider(config) {
|
|
879
|
-
|
|
1139
|
+
const provider = resolveProviderName(config.provider);
|
|
1140
|
+
if (!SUPPORTED_PROVIDERS.has(provider)) {
|
|
880
1141
|
throw new Error(
|
|
881
|
-
`Unknown harness provider: "${
|
|
1142
|
+
`Unknown harness provider: "${provider}". Supported: ${[...SUPPORTED_PROVIDERS].sort().join(", ")}`
|
|
882
1143
|
);
|
|
883
1144
|
}
|
|
884
|
-
if (
|
|
1145
|
+
if (provider === "aforge") {
|
|
1146
|
+
const { AforgeProvider: AforgeProvider2 } = await Promise.resolve().then(() => (init_aforge(), aforge_exports));
|
|
1147
|
+
return new AforgeProvider2(config.aforgeBin ?? "aforge");
|
|
1148
|
+
}
|
|
1149
|
+
if (provider === "claude-code") {
|
|
885
1150
|
const { ClaudeCodeProvider: ClaudeCodeProvider2 } = await Promise.resolve().then(() => (init_claude(), claude_exports));
|
|
886
1151
|
return new ClaudeCodeProvider2();
|
|
887
1152
|
}
|
|
888
|
-
if (
|
|
1153
|
+
if (provider === "codex") {
|
|
889
1154
|
const { CodexProvider: CodexProvider2 } = await Promise.resolve().then(() => (init_codex(), codex_exports));
|
|
890
1155
|
return new CodexProvider2(config.codexBin ?? "codex");
|
|
891
1156
|
}
|
|
892
|
-
if (
|
|
1157
|
+
if (provider === "gemini") {
|
|
893
1158
|
const { GeminiProvider: GeminiProvider2 } = await Promise.resolve().then(() => (init_gemini(), gemini_exports));
|
|
894
1159
|
return new GeminiProvider2(config.geminiBin ?? "gemini");
|
|
895
1160
|
}
|
|
896
|
-
if (
|
|
1161
|
+
if (provider === "opencode") {
|
|
897
1162
|
const { OpenCodeProvider: OpenCodeProvider2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
|
|
898
1163
|
return new OpenCodeProvider2(config.opencodeBin ?? "opencode");
|
|
899
1164
|
}
|
|
900
|
-
throw new Error(`Provider "${
|
|
1165
|
+
throw new Error(`Provider "${provider}" is not yet implemented.`);
|
|
901
1166
|
}
|
|
902
|
-
var SUPPORTED_PROVIDERS;
|
|
1167
|
+
var SUPPORTED_PROVIDERS, DEFAULT_HARNESS_PROVIDER, HARNESS_PROVIDER_ENV_VAR;
|
|
903
1168
|
var init_factory = __esm({
|
|
904
1169
|
"src/harness/providers/factory.ts"() {
|
|
905
|
-
SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["claude-code", "codex", "gemini", "opencode"]);
|
|
1170
|
+
SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["aforge", "claude-code", "codex", "gemini", "opencode"]);
|
|
1171
|
+
DEFAULT_HARNESS_PROVIDER = "aforge";
|
|
1172
|
+
HARNESS_PROVIDER_ENV_VAR = "AGENTFIELD_HARNESS_PROVIDER";
|
|
906
1173
|
}
|
|
907
1174
|
});
|
|
908
1175
|
|
|
@@ -945,22 +1212,28 @@ var init_runner = __esm({
|
|
|
945
1212
|
async run(prompt, options = {}) {
|
|
946
1213
|
const { schema, ...rest } = options;
|
|
947
1214
|
const resolved = this.resolveOptions(this.config, rest);
|
|
948
|
-
|
|
949
|
-
throw new Error("No harness provider specified. Set 'provider' in HarnessConfig or pass it to .harness() call.");
|
|
950
|
-
}
|
|
951
|
-
const cwd = resolved.cwd ?? ".";
|
|
1215
|
+
resolved.provider = resolveProviderName(resolved.provider);
|
|
952
1216
|
const provider = await this.buildProvider(resolved.provider, resolved);
|
|
953
|
-
const
|
|
1217
|
+
const cwd = resolved.cwd ?? ".";
|
|
1218
|
+
const outputRoot = resolved.projectDir ?? cwd;
|
|
1219
|
+
let outputDir;
|
|
1220
|
+
if (schema !== void 0) {
|
|
1221
|
+
fs.mkdirSync(outputRoot, { recursive: true });
|
|
1222
|
+
outputDir = fs.mkdtempSync(path3__default.join(outputRoot, ".agentfield-out-"));
|
|
1223
|
+
}
|
|
1224
|
+
const effectivePrompt = schema === void 0 ? prompt : `${prompt}${buildPromptSuffix(schema, outputDir)}`;
|
|
954
1225
|
const startTime = Date.now();
|
|
955
1226
|
try {
|
|
956
1227
|
const raw = await this.executeWithRetry(provider, effectivePrompt, resolved);
|
|
957
1228
|
if (schema !== void 0) {
|
|
958
|
-
return this.handleSchemaOutput(raw, schema,
|
|
1229
|
+
return this.handleSchemaOutput(raw, schema, outputDir, startTime);
|
|
959
1230
|
}
|
|
960
1231
|
return createHarnessResult({
|
|
961
1232
|
result: raw.result,
|
|
962
1233
|
isError: raw.isError,
|
|
963
1234
|
errorMessage: raw.errorMessage,
|
|
1235
|
+
failureType: raw.failureType,
|
|
1236
|
+
returnCode: raw.returnCode,
|
|
964
1237
|
costUsd: raw.metrics.totalCostUsd,
|
|
965
1238
|
numTurns: raw.metrics.numTurns,
|
|
966
1239
|
durationMs: Date.now() - startTime,
|
|
@@ -970,7 +1243,8 @@ var init_runner = __esm({
|
|
|
970
1243
|
});
|
|
971
1244
|
} finally {
|
|
972
1245
|
if (schema !== void 0) {
|
|
973
|
-
cleanupTempFiles(
|
|
1246
|
+
cleanupTempFiles(outputDir);
|
|
1247
|
+
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
974
1248
|
}
|
|
975
1249
|
}
|
|
976
1250
|
}
|
|
@@ -992,6 +1266,8 @@ var init_runner = __esm({
|
|
|
992
1266
|
"systemPrompt",
|
|
993
1267
|
"env",
|
|
994
1268
|
"cwd",
|
|
1269
|
+
"projectDir",
|
|
1270
|
+
"aforgeBin",
|
|
995
1271
|
"codexBin",
|
|
996
1272
|
"geminiBin",
|
|
997
1273
|
"opencodeBin"
|
|
@@ -1056,6 +1332,8 @@ var init_runner = __esm({
|
|
|
1056
1332
|
result: raw.result,
|
|
1057
1333
|
parsed,
|
|
1058
1334
|
isError: false,
|
|
1335
|
+
failureType: raw.failureType,
|
|
1336
|
+
returnCode: raw.returnCode,
|
|
1059
1337
|
costUsd: raw.metrics.totalCostUsd,
|
|
1060
1338
|
numTurns: raw.metrics.numTurns,
|
|
1061
1339
|
durationMs: Date.now() - startTime,
|
|
@@ -1068,6 +1346,8 @@ var init_runner = __esm({
|
|
|
1068
1346
|
result: raw.result,
|
|
1069
1347
|
isError: true,
|
|
1070
1348
|
errorMessage: "Schema validation failed after parse and cosmetic repair attempts.",
|
|
1349
|
+
failureType: "schema",
|
|
1350
|
+
returnCode: raw.returnCode,
|
|
1071
1351
|
costUsd: raw.metrics.totalCostUsd,
|
|
1072
1352
|
numTurns: raw.metrics.numTurns,
|
|
1073
1353
|
durationMs: Date.now() - startTime,
|
|
@@ -5849,22 +6129,22 @@ var Agent = class {
|
|
|
5849
6129
|
},
|
|
5850
6130
|
message: { error: "rate_limit_exceeded", message: "Too many authentication attempts. Try again later." },
|
|
5851
6131
|
skip: (req) => {
|
|
5852
|
-
const
|
|
5853
|
-
if (!
|
|
6132
|
+
const path4 = req.path;
|
|
6133
|
+
if (!path4.startsWith("/reasoners/") && !path4.startsWith("/skills/") && !path4.startsWith("/execute") && !path4.startsWith("/api/v1/reasoners/") && !path4.startsWith("/api/v1/skills/")) {
|
|
5854
6134
|
return true;
|
|
5855
6135
|
}
|
|
5856
|
-
const parts =
|
|
6136
|
+
const parts = path4.replace(/^\/+/, "").split("/");
|
|
5857
6137
|
const funcName = parts[parts.length - 1] ?? "";
|
|
5858
6138
|
return realtimeFunctions.has(funcName);
|
|
5859
6139
|
}
|
|
5860
6140
|
});
|
|
5861
6141
|
this.app.use(authRateLimiter);
|
|
5862
6142
|
this.app.use(async (req, res, next) => {
|
|
5863
|
-
const
|
|
5864
|
-
if (!
|
|
6143
|
+
const path4 = req.path;
|
|
6144
|
+
if (!path4.startsWith("/reasoners/") && !path4.startsWith("/skills/") && !path4.startsWith("/execute") && !path4.startsWith("/api/v1/reasoners/") && !path4.startsWith("/api/v1/skills/")) {
|
|
5865
6145
|
return next();
|
|
5866
6146
|
}
|
|
5867
|
-
const parts =
|
|
6147
|
+
const parts = path4.replace(/^\/+/, "").split("/");
|
|
5868
6148
|
const funcName = parts[parts.length - 1] ?? "";
|
|
5869
6149
|
if (realtimeFunctions.has(funcName)) {
|
|
5870
6150
|
return next();
|
|
@@ -6064,9 +6344,9 @@ var Agent = class {
|
|
|
6064
6344
|
return handler(req, res);
|
|
6065
6345
|
}
|
|
6066
6346
|
async handleServerlessEvent(event) {
|
|
6067
|
-
const
|
|
6347
|
+
const path4 = event?.path ?? event?.rawPath ?? "";
|
|
6068
6348
|
const action = event?.action ?? "";
|
|
6069
|
-
if (
|
|
6349
|
+
if (path4 === "/discover" || action === "discover") {
|
|
6070
6350
|
return {
|
|
6071
6351
|
statusCode: 200,
|
|
6072
6352
|
headers: { "content-type": "application/json" },
|
|
@@ -6075,7 +6355,7 @@ var Agent = class {
|
|
|
6075
6355
|
}
|
|
6076
6356
|
const body = this.normalizeEventBody(event);
|
|
6077
6357
|
const invocation = this.extractInvocationDetails({
|
|
6078
|
-
path:
|
|
6358
|
+
path: path4,
|
|
6079
6359
|
query: event?.queryStringParameters,
|
|
6080
6360
|
body,
|
|
6081
6361
|
reasoner: event?.reasoner,
|
|
@@ -6154,9 +6434,9 @@ var Agent = class {
|
|
|
6154
6434
|
const input = this.normalizeInputPayload(params.body);
|
|
6155
6435
|
return { name: name ?? void 0, targetType: typeValue, input };
|
|
6156
6436
|
}
|
|
6157
|
-
parsePathTarget(
|
|
6158
|
-
if (!
|
|
6159
|
-
const normalized =
|
|
6437
|
+
parsePathTarget(path4) {
|
|
6438
|
+
if (!path4) return {};
|
|
6439
|
+
const normalized = path4.split("?")[0];
|
|
6160
6440
|
const reasonerMatch = normalized.match(/\/reasoners\/([^/]+)/);
|
|
6161
6441
|
if (reasonerMatch?.[1]) {
|
|
6162
6442
|
return { name: reasonerMatch[1], targetType: "reasoner" };
|
|
@@ -7326,7 +7606,7 @@ var MultimodalResponse = class {
|
|
|
7326
7606
|
*/
|
|
7327
7607
|
async saveImage(image, imagePath) {
|
|
7328
7608
|
const bytes = await this.getImageBytes(image);
|
|
7329
|
-
await promises.mkdir(
|
|
7609
|
+
await promises.mkdir(path3.dirname(imagePath), { recursive: true });
|
|
7330
7610
|
await promises.writeFile(imagePath, bytes);
|
|
7331
7611
|
}
|
|
7332
7612
|
/**
|
|
@@ -7334,7 +7614,7 @@ var MultimodalResponse = class {
|
|
|
7334
7614
|
*/
|
|
7335
7615
|
async saveAudio(audio, audioPath) {
|
|
7336
7616
|
const bytes = await this.getAudioBytes(audio);
|
|
7337
|
-
await promises.mkdir(
|
|
7617
|
+
await promises.mkdir(path3.dirname(audioPath), { recursive: true });
|
|
7338
7618
|
await promises.writeFile(audioPath, bytes);
|
|
7339
7619
|
}
|
|
7340
7620
|
/**
|
|
@@ -7342,7 +7622,7 @@ var MultimodalResponse = class {
|
|
|
7342
7622
|
*/
|
|
7343
7623
|
async saveFile(file, filePath) {
|
|
7344
7624
|
const bytes = await this.getFileBytes(file);
|
|
7345
|
-
await promises.mkdir(
|
|
7625
|
+
await promises.mkdir(path3.dirname(filePath), { recursive: true });
|
|
7346
7626
|
await promises.writeFile(filePath, bytes);
|
|
7347
7627
|
}
|
|
7348
7628
|
/**
|
|
@@ -7353,7 +7633,7 @@ var MultimodalResponse = class {
|
|
|
7353
7633
|
const savedFiles = {};
|
|
7354
7634
|
await promises.mkdir(outputDir, { recursive: true });
|
|
7355
7635
|
if (this._audio) {
|
|
7356
|
-
const audioPath =
|
|
7636
|
+
const audioPath = path3.join(outputDir, `${prefix}_audio.${this._audio.format}`);
|
|
7357
7637
|
await this.saveAudio(this._audio, audioPath);
|
|
7358
7638
|
savedFiles["audio"] = audioPath;
|
|
7359
7639
|
}
|
|
@@ -7361,22 +7641,22 @@ var MultimodalResponse = class {
|
|
|
7361
7641
|
const image = this._images[i];
|
|
7362
7642
|
let ext = "png";
|
|
7363
7643
|
if (image.url) {
|
|
7364
|
-
const urlExt =
|
|
7644
|
+
const urlExt = path3.extname(image.url).slice(1);
|
|
7365
7645
|
if (urlExt) ext = urlExt;
|
|
7366
7646
|
}
|
|
7367
|
-
const imagePath =
|
|
7647
|
+
const imagePath = path3.join(outputDir, `${prefix}_image_${i}.${ext}`);
|
|
7368
7648
|
await this.saveImage(image, imagePath);
|
|
7369
7649
|
savedFiles[`image_${i}`] = imagePath;
|
|
7370
7650
|
}
|
|
7371
7651
|
for (let i = 0; i < this._files.length; i++) {
|
|
7372
7652
|
const file = this._files[i];
|
|
7373
7653
|
const filename = file.filename || `${prefix}_file_${i}`;
|
|
7374
|
-
const filePath =
|
|
7654
|
+
const filePath = path3.join(outputDir, filename);
|
|
7375
7655
|
await this.saveFile(file, filePath);
|
|
7376
7656
|
savedFiles[`file_${i}`] = filePath;
|
|
7377
7657
|
}
|
|
7378
7658
|
if (this._text) {
|
|
7379
|
-
const textPath =
|
|
7659
|
+
const textPath = path3.join(outputDir, `${prefix}_text.txt`);
|
|
7380
7660
|
await promises.writeFile(textPath, this._text, "utf-8");
|
|
7381
7661
|
savedFiles["text"] = textPath;
|
|
7382
7662
|
}
|