@agentfield/sdk 0.1.136 → 0.1.137-rc.10

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