@agentfield/sdk 0.1.140 → 0.1.141-rc.2

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,9 +1,9 @@
1
+ import fs2, { promises, readFileSync } from 'fs';
2
+ import * as path5 from 'path';
3
+ import path5__default, { resolve, dirname } from 'path';
1
4
  import { spawn } from 'child_process';
2
5
  import os, { constants } from 'os';
3
6
  import { createRequire } from 'module';
4
- import * as path4 from 'path';
5
- import path4__default, { resolve, dirname } from 'path';
6
- import fs, { promises, readFileSync } from 'fs';
7
7
  import express from 'express';
8
8
  import rateLimit from 'express-rate-limit';
9
9
  import crypto2, { randomUUID, createHash } from 'crypto';
@@ -150,6 +150,244 @@ var init_openrouterAttribution = __esm({
150
150
  }
151
151
  });
152
152
 
153
+ // src/harness/availability.ts
154
+ var availability_exports = {};
155
+ __export(availability_exports, {
156
+ HarnessProviderUnavailable: () => HarnessProviderUnavailable,
157
+ SUPPORTED_PROVIDER_NAMES: () => SUPPORTED_PROVIDER_NAMES,
158
+ ensureCliAvailable: () => ensureCliAvailable,
159
+ findExecutable: () => findExecutable,
160
+ harnessDoctor: () => harnessDoctor,
161
+ providerUnavailable: () => providerUnavailable
162
+ });
163
+ function executableExtensions(binary, env) {
164
+ if (process.platform !== "win32" || path5__default.extname(binary)) {
165
+ return [""];
166
+ }
167
+ const pathExt = env.PATHEXT ?? env.PathExt ?? ".COM;.EXE;.BAT;.CMD";
168
+ return pathExt.split(";").filter(Boolean).map((extension) => extension.toLowerCase());
169
+ }
170
+ function isExecutableFile(candidate) {
171
+ try {
172
+ if (!fs2.statSync(candidate).isFile()) {
173
+ return false;
174
+ }
175
+ fs2.accessSync(candidate, process.platform === "win32" ? fs2.constants.F_OK : fs2.constants.X_OK);
176
+ return true;
177
+ } catch {
178
+ return false;
179
+ }
180
+ }
181
+ function findExecutable(binary, env = process.env) {
182
+ const extensions = executableExtensions(binary, env);
183
+ const hasPath = path5__default.isAbsolute(binary) || binary.includes("/") || binary.includes("\\");
184
+ const directories = hasPath ? [""] : (env.PATH ?? env.Path ?? env.path ?? "").split(path5__default.delimiter).filter(Boolean);
185
+ for (const directory of directories) {
186
+ for (const extension of extensions) {
187
+ const candidate = `${hasPath ? binary : path5__default.join(directory, binary)}${extension}`;
188
+ if (isExecutableFile(candidate)) {
189
+ return path5__default.resolve(candidate);
190
+ }
191
+ }
192
+ }
193
+ return void 0;
194
+ }
195
+ function ensureCliAvailable(provider, binary, resolveBinary = findExecutable, env = process.env) {
196
+ const resolved = resolveBinary(binary, env);
197
+ if (resolved) {
198
+ return resolved;
199
+ }
200
+ throw providerUnavailable(provider, binary);
201
+ }
202
+ function providerUnavailable(provider, binary) {
203
+ const spec = PROVIDER_SPECS[provider];
204
+ return new HarnessProviderUnavailable(provider, {
205
+ binary,
206
+ installCommand: spec?.installCommand ?? "install the configured harness provider binary"
207
+ });
208
+ }
209
+ function isWindowsBatchFile(command) {
210
+ return process.platform === "win32" && WINDOWS_BATCH_EXTENSIONS.has(path5__default.extname(command).toLowerCase());
211
+ }
212
+ function quoteCmdToken(value) {
213
+ return `"${value.replace(/"/g, '""')}"`;
214
+ }
215
+ async function defaultVersionProbe(command) {
216
+ const { execFile } = await import('child_process');
217
+ return new Promise((resolve3, reject) => {
218
+ const batch = isWindowsBatchFile(command[0]);
219
+ const file = batch ? process.env.ComSpec ?? "cmd.exe" : command[0];
220
+ const args = batch ? ["/d", "/s", "/c", `"${command.map(quoteCmdToken).join(" ")}"`] : command.slice(1);
221
+ execFile(
222
+ file,
223
+ args,
224
+ { timeout: 2e3, windowsHide: true, windowsVerbatimArguments: batch },
225
+ (error, stdout, stderr) => {
226
+ if (error) {
227
+ reject(error);
228
+ return;
229
+ }
230
+ const output = String(stdout || stderr).trim();
231
+ resolve3(output ? output.split(/\r?\n/, 1)[0] : "unknown");
232
+ }
233
+ );
234
+ });
235
+ }
236
+ async function defaultWrapperProbe() {
237
+ try {
238
+ await import('@anthropic-ai/claude-agent-sdk');
239
+ return true;
240
+ } catch {
241
+ return false;
242
+ }
243
+ }
244
+ function authStatus(spec, env) {
245
+ return spec.authEnvVars.some((name) => Boolean(env[name])) ? "configured" : "unknown";
246
+ }
247
+ async function harnessDoctor(providers, options = {}) {
248
+ const selected = providers && providers.length > 0 ? [...providers] : [...SUPPORTED_PROVIDER_NAMES];
249
+ const unknown = selected.filter((provider) => !(provider in PROVIDER_SPECS)).sort();
250
+ if (unknown.length > 0) {
251
+ throw new Error(
252
+ `Unknown harness provider: "${unknown[0]}". Supported providers: ${SUPPORTED_PROVIDER_NAMES.join(", ")}`
253
+ );
254
+ }
255
+ const env = options.env ?? process.env;
256
+ const resolveBinary = options.resolveBinary ?? findExecutable;
257
+ const versionProbe = options.versionProbe ?? defaultVersionProbe;
258
+ const wrapperProbe = options.wrapperProbe ?? defaultWrapperProbe;
259
+ const reports = [];
260
+ for (const providerValue of selected) {
261
+ const provider = providerValue;
262
+ const spec = PROVIDER_SPECS[provider];
263
+ if (provider === "claude-code") {
264
+ const installed = await wrapperProbe(provider);
265
+ reports.push({
266
+ provider,
267
+ binary: null,
268
+ installed,
269
+ version: null,
270
+ auth: authStatus(spec, env),
271
+ usable: installed,
272
+ installCommand: spec.installCommand,
273
+ authEnvVars: [...spec.authEnvVars],
274
+ issues: installed ? [] : ["wrapper_not_installed"]
275
+ });
276
+ continue;
277
+ }
278
+ const binaryName = spec.binary;
279
+ const binary = resolveBinary(binaryName, env) ?? null;
280
+ const issues = [];
281
+ let version = null;
282
+ if (binary === null) {
283
+ issues.push("binary_not_found");
284
+ } else {
285
+ try {
286
+ const output = (await versionProbe([binary, ...spec.versionArgs])).trim();
287
+ version = output ? output.split(/\r?\n/, 1)[0] : "unknown";
288
+ } catch {
289
+ issues.push("version_probe_failed");
290
+ }
291
+ }
292
+ reports.push({
293
+ provider,
294
+ binary,
295
+ installed: binary !== null,
296
+ version,
297
+ auth: authStatus(spec, env),
298
+ usable: binary !== null && issues.length === 0,
299
+ installCommand: spec.installCommand,
300
+ authEnvVars: [...spec.authEnvVars],
301
+ issues
302
+ });
303
+ }
304
+ return reports;
305
+ }
306
+ var PROVIDER_SPECS, SUPPORTED_PROVIDER_NAMES, HarnessProviderUnavailable, WINDOWS_BATCH_EXTENSIONS;
307
+ var init_availability = __esm({
308
+ "src/harness/availability.ts"() {
309
+ PROVIDER_SPECS = {
310
+ aforge: {
311
+ binary: "aforge",
312
+ versionArgs: ["version"],
313
+ installCommand: "af aforge ensure",
314
+ authEnvVars: ["OPENROUTER_API_KEY"]
315
+ },
316
+ "claude-code": {
317
+ binary: null,
318
+ versionArgs: [],
319
+ installCommand: "npm install @anthropic-ai/claude-agent-sdk",
320
+ authEnvVars: ["ANTHROPIC_API_KEY"]
321
+ },
322
+ codex: {
323
+ binary: "codex",
324
+ versionArgs: ["--version"],
325
+ installCommand: "npm install -g @openai/codex",
326
+ authEnvVars: ["OPENAI_API_KEY"]
327
+ },
328
+ gemini: {
329
+ binary: "gemini",
330
+ versionArgs: ["--version"],
331
+ installCommand: "npm install -g @google/gemini-cli",
332
+ authEnvVars: ["GEMINI_API_KEY", "GOOGLE_API_KEY"]
333
+ },
334
+ omp: {
335
+ binary: "omp",
336
+ versionArgs: ["--version"],
337
+ installCommand: "curl -fsSL https://omp.sh/install | sh",
338
+ authEnvVars: [
339
+ "OPENROUTER_API_KEY",
340
+ "ANTHROPIC_API_KEY",
341
+ "OPENAI_API_KEY",
342
+ "GEMINI_API_KEY",
343
+ "GOOGLE_API_KEY"
344
+ ]
345
+ },
346
+ opencode: {
347
+ binary: "opencode",
348
+ versionArgs: ["--version"],
349
+ installCommand: "curl -fsSL https://opencode.ai/install | bash",
350
+ authEnvVars: []
351
+ },
352
+ pi: {
353
+ binary: "pi",
354
+ versionArgs: ["--version"],
355
+ installCommand: "npm install -g --ignore-scripts @earendil-works/pi-coding-agent",
356
+ authEnvVars: [
357
+ "OPENROUTER_API_KEY",
358
+ "ANTHROPIC_API_KEY",
359
+ "OPENAI_API_KEY",
360
+ "GEMINI_API_KEY",
361
+ "GOOGLE_API_KEY"
362
+ ]
363
+ }
364
+ };
365
+ SUPPORTED_PROVIDER_NAMES = Object.freeze(
366
+ Object.keys(PROVIDER_SPECS).sort()
367
+ );
368
+ HarnessProviderUnavailable = class extends Error {
369
+ provider;
370
+ binary;
371
+ installCommand;
372
+ missingAuthEnv;
373
+ constructor(provider, options) {
374
+ const missingAuthEnv = [...options.missingAuthEnv ?? []];
375
+ let message = `Harness provider '${provider}' is unavailable: binary '${options.binary}' was not found. Install it with: ${options.installCommand}`;
376
+ if (missingAuthEnv.length > 0) {
377
+ message += `. Configure one of: ${missingAuthEnv.join(", ")}`;
378
+ }
379
+ super(message);
380
+ this.name = "HarnessProviderUnavailable";
381
+ this.provider = provider;
382
+ this.binary = options.binary;
383
+ this.installCommand = options.installCommand;
384
+ this.missingAuthEnv = missingAuthEnv;
385
+ }
386
+ };
387
+ WINDOWS_BATCH_EXTENSIONS = /* @__PURE__ */ new Set([".bat", ".cmd"]);
388
+ }
389
+ });
390
+
153
391
  // src/harness/providers/base.ts
154
392
  function resolveRoot(options) {
155
393
  for (const value of [options.projectDir, options.project_dir, options.cwd]) {
@@ -189,6 +427,10 @@ var init_types = __esm({
189
427
  "src/harness/types.ts"() {
190
428
  }
191
429
  });
430
+ function markProviderCommand(provider, command) {
431
+ providerCommands.set(command, provider);
432
+ return command;
433
+ }
192
434
  function resolveIdleMs(idleSeconds) {
193
435
  let seconds = idleSeconds;
194
436
  if (seconds === void 0) {
@@ -198,17 +440,21 @@ function resolveIdleMs(idleSeconds) {
198
440
  }
199
441
  return seconds > 0 ? seconds * 1e3 : void 0;
200
442
  }
201
- function runCli(cmd, options) {
443
+ async function runCli(cmd, options) {
444
+ const [bin, ...args] = cmd;
445
+ const parsedParentDepth = Number.parseInt(process.env.AGENTFIELD_HARNESS_DEPTH ?? "", 10);
446
+ const parentDepth = Number.isFinite(parsedParentDepth) && parsedParentDepth >= 0 ? parsedParentDepth : 0;
447
+ const env = {
448
+ ...process.env,
449
+ AGENTFIELD_HARNESS_DEPTH: String(parentDepth + 1),
450
+ ...options?.env
451
+ };
452
+ applyOpenRouterAttributionEnv(env);
453
+ const provider = providerCommands.get(cmd);
454
+ if (provider) {
455
+ ensureCliAvailable(provider, bin, void 0, env);
456
+ }
202
457
  return new Promise((resolve3, reject) => {
203
- const [bin, ...args] = cmd;
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
- };
211
- applyOpenRouterAttributionEnv(env);
212
458
  const hasInput = options?.inputText !== void 0;
213
459
  const proc = spawn(bin, args, {
214
460
  env,
@@ -327,11 +573,13 @@ function extractFinalText(events) {
327
573
  }
328
574
  return result;
329
575
  }
330
- var DEFAULT_IDLE_SECONDS;
576
+ var DEFAULT_IDLE_SECONDS, providerCommands;
331
577
  var init_cli = __esm({
332
578
  "src/harness/cli.ts"() {
333
579
  init_openrouterAttribution();
580
+ init_availability();
334
581
  DEFAULT_IDLE_SECONDS = 120;
582
+ providerCommands = /* @__PURE__ */ new WeakMap();
335
583
  }
336
584
  });
337
585
 
@@ -443,6 +691,7 @@ var init_aforge = __esm({
443
691
  init_base();
444
692
  init_types();
445
693
  init_cli();
694
+ init_availability();
446
695
  init_modelVariant();
447
696
  REASONING_VARIANTS = /* @__PURE__ */ new Set(["off", "low", "medium", "high"]);
448
697
  DEFAULT_TIMEOUT_SECONDS = 1800;
@@ -550,7 +799,7 @@ var init_aforge = __esm({
550
799
  const effectiveModel = model || env.AFORGE_MODEL || AFORGE_DEFAULT_MODEL;
551
800
  const startApi = Date.now();
552
801
  try {
553
- const { stdout, stderr, exitCode } = await runCli(cmd, {
802
+ const { stdout, stderr, exitCode } = await runCli(markProviderCommand("aforge", cmd), {
554
803
  env,
555
804
  cwd: void 0,
556
805
  timeout: outerTimeout * 1e3,
@@ -593,14 +842,12 @@ var init_aforge = __esm({
593
842
  returnCode: exitCode
594
843
  });
595
844
  } catch (error) {
845
+ if (error instanceof HarnessProviderUnavailable) {
846
+ throw error;
847
+ }
596
848
  const message = error instanceof Error ? error.message : String(error);
597
849
  if (message.includes("ENOENT")) {
598
- return createRawResult({
599
- isError: true,
600
- errorMessage: `AForge binary not found at '${this.bin}'. Install it with \`af aforge ensure\`, or set AFORGE_BIN to its path.`,
601
- failureType: "crash",
602
- metrics: createMetrics({ durationApiMs: Date.now() - startApi })
603
- });
850
+ throw providerUnavailable("aforge", this.bin);
604
851
  }
605
852
  const timedOut = /timed out|deadline exceeded|no progress/i.test(message);
606
853
  return createRawResult({
@@ -637,6 +884,7 @@ var init_claude = __esm({
637
884
  init_base();
638
885
  init_types();
639
886
  init_modelVariant();
887
+ init_availability();
640
888
  ClaudeCodeProvider = class {
641
889
  async execute(prompt, options) {
642
890
  let sdk;
@@ -644,9 +892,10 @@ var init_claude = __esm({
644
892
  const mod = await import('@anthropic-ai/claude-agent-sdk');
645
893
  sdk = mod;
646
894
  } catch {
647
- throw new Error(
648
- "@anthropic-ai/claude-agent-sdk is required for the 'claude-code' provider. Install it with: npm install @anthropic-ai/claude-agent-sdk"
649
- );
895
+ throw new HarnessProviderUnavailable("claude-code", {
896
+ binary: "@anthropic-ai/claude-agent-sdk",
897
+ installCommand: "npm install @anthropic-ai/claude-agent-sdk"
898
+ });
650
899
  }
651
900
  const agentOptions = {};
652
901
  const { model: modelValue } = resolveModelAndVariant(options);
@@ -766,6 +1015,7 @@ var init_codex = __esm({
766
1015
  init_base();
767
1016
  init_types();
768
1017
  init_cli();
1018
+ init_availability();
769
1019
  init_modelVariant();
770
1020
  CodexProvider = class {
771
1021
  bin;
@@ -791,7 +1041,7 @@ var init_codex = __esm({
791
1041
  cmd.push(prompt);
792
1042
  const startApi = Date.now();
793
1043
  try {
794
- const { stdout, stderr, exitCode } = await runCli(cmd, {
1044
+ const { stdout, stderr, exitCode } = await runCli(markProviderCommand("codex", cmd), {
795
1045
  env: options.env,
796
1046
  cwd: root
797
1047
  });
@@ -819,13 +1069,12 @@ var init_codex = __esm({
819
1069
  errorMessage: isError ? stderr.trim() : void 0
820
1070
  });
821
1071
  } catch (err) {
1072
+ if (err instanceof HarnessProviderUnavailable) {
1073
+ throw err;
1074
+ }
822
1075
  const msg = err instanceof Error ? err.message : String(err);
823
1076
  if (msg.includes("ENOENT")) {
824
- return createRawResult({
825
- isError: true,
826
- errorMessage: `Codex binary not found at '${this.bin}'. Install: https://github.com/openai/codex`,
827
- metrics: createMetrics({ durationApiMs: Date.now() - startApi })
828
- });
1077
+ throw providerUnavailable("codex", this.bin);
829
1078
  }
830
1079
  return createRawResult({
831
1080
  isError: true,
@@ -849,6 +1098,7 @@ var init_gemini = __esm({
849
1098
  init_base();
850
1099
  init_types();
851
1100
  init_cli();
1101
+ init_availability();
852
1102
  init_modelVariant();
853
1103
  GeminiProvider = class {
854
1104
  bin;
@@ -869,7 +1119,7 @@ var init_gemini = __esm({
869
1119
  cmd.push("-p", prompt);
870
1120
  const startApi = Date.now();
871
1121
  try {
872
- const { stdout, stderr, exitCode } = await runCli(cmd, {
1122
+ const { stdout, stderr, exitCode } = await runCli(markProviderCommand("gemini", cmd), {
873
1123
  env: options.env,
874
1124
  cwd: resolveRoot(options)
875
1125
  });
@@ -887,13 +1137,12 @@ var init_gemini = __esm({
887
1137
  errorMessage: isError ? stderr.trim() : void 0
888
1138
  });
889
1139
  } catch (err) {
1140
+ if (err instanceof HarnessProviderUnavailable) {
1141
+ throw err;
1142
+ }
890
1143
  const msg = err instanceof Error ? err.message : String(err);
891
1144
  if (msg.includes("ENOENT")) {
892
- return createRawResult({
893
- isError: true,
894
- errorMessage: `Gemini binary not found at '${this.bin}'. Install: https://github.com/google-gemini/gemini-cli`,
895
- metrics: createMetrics({ durationApiMs: Date.now() - startApi })
896
- });
1145
+ throw providerUnavailable("gemini", this.bin);
897
1146
  }
898
1147
  return createRawResult({
899
1148
  isError: true,
@@ -1081,6 +1330,7 @@ var init_opencode = __esm({
1081
1330
  init_base();
1082
1331
  init_types();
1083
1332
  init_cli();
1333
+ init_availability();
1084
1334
  init_modelVariant();
1085
1335
  init_openrouterAttribution();
1086
1336
  ANSI_PATTERN2 = /\x1B\[[0-?]*[ -/]*[@-~]/g;
@@ -1144,7 +1394,7 @@ var init_opencode = __esm({
1144
1394
  cmd.push(effectivePrompt);
1145
1395
  const startApi = Date.now();
1146
1396
  try {
1147
- const { stdout, stderr, exitCode } = await runCli(cmd, { env });
1397
+ const { stdout, stderr, exitCode } = await runCli(markProviderCommand("opencode", cmd), { env });
1148
1398
  const resultText = stdout.trim() || void 0;
1149
1399
  const cleanStderr = stderr.trim().replace(ANSI_PATTERN2, "");
1150
1400
  let isError = false;
@@ -1173,13 +1423,12 @@ var init_opencode = __esm({
1173
1423
  failureType: isError ? "crash" : "none"
1174
1424
  });
1175
1425
  } catch (err) {
1426
+ if (err instanceof HarnessProviderUnavailable) {
1427
+ throw err;
1428
+ }
1176
1429
  const msg = err instanceof Error ? err.message : String(err);
1177
1430
  if (msg.includes("ENOENT")) {
1178
- return createRawResult({
1179
- isError: true,
1180
- errorMessage: `OpenCode binary not found at '${this.bin}'. Install: https://github.com/opencode-ai/opencode`,
1181
- metrics: createMetrics({ durationApiMs: Date.now() - startApi })
1182
- });
1431
+ throw providerUnavailable("opencode", this.bin);
1183
1432
  }
1184
1433
  return createRawResult({
1185
1434
  isError: true,
@@ -1302,6 +1551,7 @@ var init_pi = __esm({
1302
1551
  init_base();
1303
1552
  init_types();
1304
1553
  init_cli();
1554
+ init_availability();
1305
1555
  init_modelVariant();
1306
1556
  READ_ONLY_TOOLS = /* @__PURE__ */ new Set(["read", "grep", "find", "glob", "ls", "lsp"]);
1307
1557
  ANSI_PATTERN3 = /\x1B\[[0-?]*[ -/]*[@-~]/g;
@@ -1354,7 +1604,7 @@ var init_pi = __esm({
1354
1604
  const env = { ...options.env };
1355
1605
  const startApi = Date.now();
1356
1606
  try {
1357
- const { stdout, stderr, exitCode } = await runCli(cmd, {
1607
+ const { stdout, stderr, exitCode } = await runCli(markProviderCommand(this.flavor, cmd), {
1358
1608
  env,
1359
1609
  cwd: root,
1360
1610
  inputText: prompt,
@@ -1391,11 +1641,17 @@ var init_pi = __esm({
1391
1641
  returnCode: exitCode
1392
1642
  });
1393
1643
  } catch (error) {
1644
+ if (error instanceof HarnessProviderUnavailable) {
1645
+ throw error;
1646
+ }
1394
1647
  const message = error instanceof Error ? error.message : String(error);
1395
1648
  const binaryMissing = message.includes("ENOENT");
1649
+ if (binaryMissing) {
1650
+ throw providerUnavailable(this.flavor, this.bin);
1651
+ }
1396
1652
  return createRawResult({
1397
1653
  isError: true,
1398
- 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,
1654
+ errorMessage: message,
1399
1655
  failureType: /timed out|deadline exceeded|no progress/i.test(message) ? "timeout" : "crash",
1400
1656
  metrics: createMetrics({ durationApiMs: Date.now() - startApi })
1401
1657
  });
@@ -1464,15 +1720,8 @@ async function buildProvider(config) {
1464
1720
  var SUPPORTED_PROVIDERS, DEFAULT_HARNESS_PROVIDER, HARNESS_PROVIDER_ENV_VAR;
1465
1721
  var init_factory = __esm({
1466
1722
  "src/harness/providers/factory.ts"() {
1467
- SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set([
1468
- "aforge",
1469
- "claude-code",
1470
- "codex",
1471
- "gemini",
1472
- "omp",
1473
- "opencode",
1474
- "pi"
1475
- ]);
1723
+ init_availability();
1724
+ SUPPORTED_PROVIDERS = new Set(SUPPORTED_PROVIDER_NAMES);
1476
1725
  DEFAULT_HARNESS_PROVIDER = "aforge";
1477
1726
  HARNESS_PROVIDER_ENV_VAR = "AGENTFIELD_HARNESS_PROVIDER";
1478
1727
  }
@@ -1492,7 +1741,7 @@ function getZod4Converter() {
1492
1741
  }
1493
1742
  const requires = [];
1494
1743
  try {
1495
- requires.push(createRequire(path4__default.join(process.cwd(), "package.json")));
1744
+ requires.push(createRequire(path5__default.join(process.cwd(), "package.json")));
1496
1745
  } catch {
1497
1746
  }
1498
1747
  requires.push(createRequire(import.meta.url));
@@ -1552,12 +1801,12 @@ function estimateTokens(text2) {
1552
1801
  }
1553
1802
  function writeSchemaFile(schemaJson, cwd) {
1554
1803
  const filePath = getSchemaPath(cwd);
1555
- fs.mkdirSync(path4__default.dirname(filePath), { recursive: true });
1556
- const fd = fs.openSync(filePath, "w", 384);
1804
+ fs2.mkdirSync(path5__default.dirname(filePath), { recursive: true });
1805
+ const fd = fs2.openSync(filePath, "w", 384);
1557
1806
  try {
1558
- fs.writeFileSync(fd, schemaJson, "utf8");
1807
+ fs2.writeFileSync(fd, schemaJson, "utf8");
1559
1808
  } finally {
1560
- fs.closeSync(fd);
1809
+ fs2.closeSync(fd);
1561
1810
  }
1562
1811
  return filePath;
1563
1812
  }
@@ -1568,10 +1817,10 @@ function validateAgainstSchema(data, schema) {
1568
1817
  return data;
1569
1818
  }
1570
1819
  function getOutputPath(cwd) {
1571
- return path4__default.join(cwd, OUTPUT_FILENAME);
1820
+ return path5__default.join(cwd, OUTPUT_FILENAME);
1572
1821
  }
1573
1822
  function getSchemaPath(cwd) {
1574
- return path4__default.join(cwd, SCHEMA_FILENAME);
1823
+ return path5__default.join(cwd, SCHEMA_FILENAME);
1575
1824
  }
1576
1825
  function schemaToJsonSchema(schema) {
1577
1826
  if (isZod4Schema(schema)) {
@@ -1641,7 +1890,7 @@ function cosmeticRepair(raw) {
1641
1890
  }
1642
1891
  function readAndParse(filePath) {
1643
1892
  try {
1644
- const content = fs.readFileSync(filePath, "utf8");
1893
+ const content = fs2.readFileSync(filePath, "utf8");
1645
1894
  if (content.trim().length === 0) {
1646
1895
  return null;
1647
1896
  }
@@ -1652,7 +1901,7 @@ function readAndParse(filePath) {
1652
1901
  }
1653
1902
  function readRepairAndParse(filePath) {
1654
1903
  try {
1655
- const content = fs.readFileSync(filePath, "utf8");
1904
+ const content = fs2.readFileSync(filePath, "utf8");
1656
1905
  if (content.trim().length === 0) {
1657
1906
  return null;
1658
1907
  }
@@ -1682,7 +1931,7 @@ function parseAndValidate(filePath, schema) {
1682
1931
  function cleanupTempFiles(cwd) {
1683
1932
  for (const filename of [OUTPUT_FILENAME, SCHEMA_FILENAME]) {
1684
1933
  try {
1685
- fs.unlinkSync(path4__default.join(cwd, filename));
1934
+ fs2.unlinkSync(path5__default.join(cwd, filename));
1686
1935
  } catch {
1687
1936
  }
1688
1937
  }
@@ -1742,8 +1991,8 @@ var init_runner = __esm({
1742
1991
  const outputRoot = resolveRoot(resolved) ?? ".";
1743
1992
  let outputDir;
1744
1993
  if (schema !== void 0) {
1745
- fs.mkdirSync(outputRoot, { recursive: true });
1746
- outputDir = fs.mkdtempSync(path4__default.join(outputRoot, ".agentfield-out-"));
1994
+ fs2.mkdirSync(outputRoot, { recursive: true });
1995
+ outputDir = fs2.mkdtempSync(path5__default.join(outputRoot, ".agentfield-out-"));
1747
1996
  }
1748
1997
  const effectivePrompt = schema === void 0 ? prompt : `${prompt}${buildPromptSuffix(schema, outputDir)}`;
1749
1998
  const startTime = Date.now();
@@ -1768,7 +2017,7 @@ var init_runner = __esm({
1768
2017
  } finally {
1769
2018
  if (schema !== void 0) {
1770
2019
  cleanupTempFiles(outputDir);
1771
- fs.rmSync(outputDir, { recursive: true, force: true });
2020
+ fs2.rmSync(outputDir, { recursive: true, force: true });
1772
2021
  }
1773
2022
  }
1774
2023
  }
@@ -6523,6 +6772,10 @@ var Agent = class {
6523
6772
  this.recordHarnessUsage(result, options);
6524
6773
  return result;
6525
6774
  }
6775
+ async harnessDoctor(providers) {
6776
+ const { harnessDoctor: harnessDoctor2 } = await Promise.resolve().then(() => (init_availability(), availability_exports));
6777
+ return harnessDoctor2(providers);
6778
+ }
6526
6779
  /**
6527
6780
  * Record a harness run's token/cost usage into the current execution's
6528
6781
  * tracker. Mirrors the Python SDK's `_record_harness_usage`: a no-op when
@@ -7137,22 +7390,22 @@ var Agent = class {
7137
7390
  },
7138
7391
  message: { error: "rate_limit_exceeded", message: "Too many authentication attempts. Try again later." },
7139
7392
  skip: (req) => {
7140
- const path5 = req.path;
7141
- if (!path5.startsWith("/reasoners/") && !path5.startsWith("/skills/") && !path5.startsWith("/execute") && !path5.startsWith("/api/v1/reasoners/") && !path5.startsWith("/api/v1/skills/")) {
7393
+ const path6 = req.path;
7394
+ if (!path6.startsWith("/reasoners/") && !path6.startsWith("/skills/") && !path6.startsWith("/execute") && !path6.startsWith("/api/v1/reasoners/") && !path6.startsWith("/api/v1/skills/")) {
7142
7395
  return true;
7143
7396
  }
7144
- const parts = path5.replace(/^\/+/, "").split("/");
7397
+ const parts = path6.replace(/^\/+/, "").split("/");
7145
7398
  const funcName = parts[parts.length - 1] ?? "";
7146
7399
  return realtimeFunctions.has(funcName);
7147
7400
  }
7148
7401
  });
7149
7402
  this.app.use(authRateLimiter);
7150
7403
  this.app.use(async (req, res, next) => {
7151
- const path5 = req.path;
7152
- if (!path5.startsWith("/reasoners/") && !path5.startsWith("/skills/") && !path5.startsWith("/execute") && !path5.startsWith("/api/v1/reasoners/") && !path5.startsWith("/api/v1/skills/")) {
7404
+ const path6 = req.path;
7405
+ if (!path6.startsWith("/reasoners/") && !path6.startsWith("/skills/") && !path6.startsWith("/execute") && !path6.startsWith("/api/v1/reasoners/") && !path6.startsWith("/api/v1/skills/")) {
7153
7406
  return next();
7154
7407
  }
7155
- const parts = path5.replace(/^\/+/, "").split("/");
7408
+ const parts = path6.replace(/^\/+/, "").split("/");
7156
7409
  const funcName = parts[parts.length - 1] ?? "";
7157
7410
  if (realtimeFunctions.has(funcName)) {
7158
7411
  return next();
@@ -7366,9 +7619,9 @@ var Agent = class {
7366
7619
  return handler(req, res);
7367
7620
  }
7368
7621
  async handleServerlessEvent(event) {
7369
- const path5 = event?.path ?? event?.rawPath ?? "";
7622
+ const path6 = event?.path ?? event?.rawPath ?? "";
7370
7623
  const action = event?.action ?? "";
7371
- if (path5 === "/discover" || action === "discover") {
7624
+ if (path6 === "/discover" || action === "discover") {
7372
7625
  return {
7373
7626
  statusCode: 200,
7374
7627
  headers: { "content-type": "application/json" },
@@ -7377,7 +7630,7 @@ var Agent = class {
7377
7630
  }
7378
7631
  const body = this.normalizeEventBody(event);
7379
7632
  const invocation = this.extractInvocationDetails({
7380
- path: path5,
7633
+ path: path6,
7381
7634
  query: event?.queryStringParameters,
7382
7635
  body,
7383
7636
  reasoner: event?.reasoner,
@@ -7456,9 +7709,9 @@ var Agent = class {
7456
7709
  const input = this.normalizeInputPayload(params.body);
7457
7710
  return { name: name ?? void 0, targetType: typeValue, input };
7458
7711
  }
7459
- parsePathTarget(path5) {
7460
- if (!path5) return {};
7461
- const normalized = path5.split("?")[0];
7712
+ parsePathTarget(path6) {
7713
+ if (!path6) return {};
7714
+ const normalized = path6.split("?")[0];
7462
7715
  const reasonerMatch = normalized.match(/\/reasoners\/([^/]+)/);
7463
7716
  if (reasonerMatch?.[1]) {
7464
7717
  return { name: reasonerMatch[1], targetType: "reasoner" };
@@ -8352,7 +8605,7 @@ var MultimodalResponse = class {
8352
8605
  */
8353
8606
  async saveImage(image, imagePath) {
8354
8607
  const bytes = await this.getImageBytes(image);
8355
- await promises.mkdir(path4.dirname(imagePath), { recursive: true });
8608
+ await promises.mkdir(path5.dirname(imagePath), { recursive: true });
8356
8609
  await promises.writeFile(imagePath, bytes);
8357
8610
  }
8358
8611
  /**
@@ -8360,7 +8613,7 @@ var MultimodalResponse = class {
8360
8613
  */
8361
8614
  async saveAudio(audio, audioPath) {
8362
8615
  const bytes = await this.getAudioBytes(audio);
8363
- await promises.mkdir(path4.dirname(audioPath), { recursive: true });
8616
+ await promises.mkdir(path5.dirname(audioPath), { recursive: true });
8364
8617
  await promises.writeFile(audioPath, bytes);
8365
8618
  }
8366
8619
  /**
@@ -8368,7 +8621,7 @@ var MultimodalResponse = class {
8368
8621
  */
8369
8622
  async saveFile(file, filePath) {
8370
8623
  const bytes = await this.getFileBytes(file);
8371
- await promises.mkdir(path4.dirname(filePath), { recursive: true });
8624
+ await promises.mkdir(path5.dirname(filePath), { recursive: true });
8372
8625
  await promises.writeFile(filePath, bytes);
8373
8626
  }
8374
8627
  /**
@@ -8379,7 +8632,7 @@ var MultimodalResponse = class {
8379
8632
  const savedFiles = {};
8380
8633
  await promises.mkdir(outputDir, { recursive: true });
8381
8634
  if (this._audio) {
8382
- const audioPath = path4.join(outputDir, `${prefix}_audio.${this._audio.format}`);
8635
+ const audioPath = path5.join(outputDir, `${prefix}_audio.${this._audio.format}`);
8383
8636
  await this.saveAudio(this._audio, audioPath);
8384
8637
  savedFiles["audio"] = audioPath;
8385
8638
  }
@@ -8387,22 +8640,22 @@ var MultimodalResponse = class {
8387
8640
  const image = this._images[i];
8388
8641
  let ext = "png";
8389
8642
  if (image.url) {
8390
- const urlExt = path4.extname(image.url).slice(1);
8643
+ const urlExt = path5.extname(image.url).slice(1);
8391
8644
  if (urlExt) ext = urlExt;
8392
8645
  }
8393
- const imagePath = path4.join(outputDir, `${prefix}_image_${i}.${ext}`);
8646
+ const imagePath = path5.join(outputDir, `${prefix}_image_${i}.${ext}`);
8394
8647
  await this.saveImage(image, imagePath);
8395
8648
  savedFiles[`image_${i}`] = imagePath;
8396
8649
  }
8397
8650
  for (let i = 0; i < this._files.length; i++) {
8398
8651
  const file = this._files[i];
8399
8652
  const filename = file.filename || `${prefix}_file_${i}`;
8400
- const filePath = path4.join(outputDir, filename);
8653
+ const filePath = path5.join(outputDir, filename);
8401
8654
  await this.saveFile(file, filePath);
8402
8655
  savedFiles[`file_${i}`] = filePath;
8403
8656
  }
8404
8657
  if (this._text) {
8405
- const textPath = path4.join(outputDir, `${prefix}_text.txt`);
8658
+ const textPath = path5.join(outputDir, `${prefix}_text.txt`);
8406
8659
  await promises.writeFile(textPath, this._text, "utf-8");
8407
8660
  savedFiles["text"] = textPath;
8408
8661
  }
@@ -9175,6 +9428,7 @@ function sleep3(ms) {
9175
9428
 
9176
9429
  // src/harness/index.ts
9177
9430
  init_types();
9431
+ init_availability();
9178
9432
  init_modelVariant();
9179
9433
  init_factory();
9180
9434
  init_runner();
@@ -9227,6 +9481,6 @@ function loadFixture(source) {
9227
9481
  );
9228
9482
  }
9229
9483
 
9230
- 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, normalizeTurnDetection, resolveModelAndVariant, resolveProviderName, scheduleTrigger, serializeExecutionLogEntry, simulateSchedule, simulateTrigger, splitModelVariant, text, triggerToPayload, unwrapEnvelope, usageSummaryOrNull, validateSessionTransport, videoFromBase64, videoFromBuffer, videoFromFile, videoFromUrl };
9484
+ 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, HarnessProviderUnavailable, 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_PROVIDER_NAMES, 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, ensureCliAvailable, eventTrigger, executeToolCallLoop, extractKeyAgreementJwk, fileFromBase64, fileFromBuffer, fileFromPath, fileFromUrl, findExecutable, generateX25519KeyPair, getCurrentContext, getCurrentSkillContext, guessUrlMimeType, harnessDoctor, imageFromBase64, imageFromBuffer, imageFromFile, imageFromUrl, installApprovalWebhookRoute, isActive, isExecutionLogBatchPayload, isTerminal, isTriggerEnvelope, loadFixture, normalizeExecutionLogEntry, normalizeSessionTransportValue, normalizeStatus, normalizeTurnDetection, resolveModelAndVariant, resolveProviderName, scheduleTrigger, serializeExecutionLogEntry, simulateSchedule, simulateTrigger, splitModelVariant, text, triggerToPayload, unwrapEnvelope, usageSummaryOrNull, validateSessionTransport, videoFromBase64, videoFromBuffer, videoFromFile, videoFromUrl };
9231
9485
  //# sourceMappingURL=index.js.map
9232
9486
  //# sourceMappingURL=index.js.map