@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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import fs, { promises, readFileSync } from 'fs';
2
- import * as path2 from 'path';
3
- import path2__default, { resolve, dirname } from 'path';
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(path2__default.dirname(filePath), { recursive: true });
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 path2__default.join(cwd, OUTPUT_FILENAME);
216
+ return path3__default.join(cwd, OUTPUT_FILENAME);
217
217
  }
218
218
  function getSchemaPath(cwd) {
219
- return path2__default.join(cwd, SCHEMA_FILENAME);
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(path2__default.join(cwd, filename));
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
- if (!SUPPORTED_PROVIDERS.has(config.provider)) {
1139
+ const provider = resolveProviderName(config.provider);
1140
+ if (!SUPPORTED_PROVIDERS.has(provider)) {
880
1141
  throw new Error(
881
- `Unknown harness provider: "${config.provider}". Supported: ${[...SUPPORTED_PROVIDERS].sort().join(", ")}`
1142
+ `Unknown harness provider: "${provider}". Supported: ${[...SUPPORTED_PROVIDERS].sort().join(", ")}`
882
1143
  );
883
1144
  }
884
- if (config.provider === "claude-code") {
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 (config.provider === "codex") {
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 (config.provider === "gemini") {
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 (config.provider === "opencode") {
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 "${config.provider}" is not yet implemented.`);
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
- if (!resolved.provider) {
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 effectivePrompt = schema === void 0 ? prompt : `${prompt}${buildPromptSuffix(schema, cwd)}`;
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, cwd, startTime);
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(cwd);
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 path3 = req.path;
5853
- if (!path3.startsWith("/reasoners/") && !path3.startsWith("/skills/") && !path3.startsWith("/execute") && !path3.startsWith("/api/v1/reasoners/") && !path3.startsWith("/api/v1/skills/")) {
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 = path3.replace(/^\/+/, "").split("/");
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 path3 = req.path;
5864
- if (!path3.startsWith("/reasoners/") && !path3.startsWith("/skills/") && !path3.startsWith("/execute") && !path3.startsWith("/api/v1/reasoners/") && !path3.startsWith("/api/v1/skills/")) {
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 = path3.replace(/^\/+/, "").split("/");
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 path3 = event?.path ?? event?.rawPath ?? "";
6347
+ const path4 = event?.path ?? event?.rawPath ?? "";
6068
6348
  const action = event?.action ?? "";
6069
- if (path3 === "/discover" || action === "discover") {
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: path3,
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(path3) {
6158
- if (!path3) return {};
6159
- const normalized = path3.split("?")[0];
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(path2.dirname(imagePath), { recursive: true });
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(path2.dirname(audioPath), { recursive: true });
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(path2.dirname(filePath), { recursive: true });
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 = path2.join(outputDir, `${prefix}_audio.${this._audio.format}`);
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 = path2.extname(image.url).slice(1);
7644
+ const urlExt = path3.extname(image.url).slice(1);
7365
7645
  if (urlExt) ext = urlExt;
7366
7646
  }
7367
- const imagePath = path2.join(outputDir, `${prefix}_image_${i}.${ext}`);
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 = path2.join(outputDir, filename);
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 = path2.join(outputDir, `${prefix}_text.txt`);
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
  }