omniagent 0.1.13 → 0.1.14

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/cli.js CHANGED
@@ -3,7 +3,7 @@ import { constants, realpathSync, existsSync, readFileSync } from "node:fs";
3
3
  import { pathToFileURL, fileURLToPath } from "node:url";
4
4
  import yargs from "yargs";
5
5
  import { hideBin } from "yargs/helpers";
6
- import { stat, rm, readdir, access, readFile, mkdir, writeFile, rename } from "node:fs/promises";
6
+ import { stat, rm, readdir, access, readFile, mkdir, writeFile, rename, mkdtemp } from "node:fs/promises";
7
7
  import path from "node:path";
8
8
  import { createHash, randomUUID } from "node:crypto";
9
9
  import os from "node:os";
@@ -11,6 +11,9 @@ import { Minimatch } from "minimatch";
11
11
  import { createInterface } from "node:readline/promises";
12
12
  import { TextDecoder } from "node:util";
13
13
  import { spawn } from "node:child_process";
14
+ import Ajv from "ajv";
15
+ import Ajv2019 from "ajv/dist/2019.js";
16
+ import Ajv2020 from "ajv/dist/2020.js";
14
17
  async function pathExists$2(candidate) {
15
18
  try {
16
19
  await stat(candidate);
@@ -475,14 +478,14 @@ const ALLOWED_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
475
478
  ]);
476
479
  const ALLOWED_CATEGORY_KEYS = new Set(PROFILE_CATEGORIES);
477
480
  const ALLOWED_TARGET_SETTING_KEYS = /* @__PURE__ */ new Set(["enabled"]);
478
- function isPlainObject$1(value) {
481
+ function isPlainObject$2(value) {
479
482
  return !!value && typeof value === "object" && !Array.isArray(value);
480
483
  }
481
484
  function pushError(errors, path2, message) {
482
485
  errors.push({ path: path2, message });
483
486
  }
484
487
  function validatePatternMap(value, pathPrefix, errors) {
485
- if (!isPlainObject$1(value)) {
488
+ if (!isPlainObject$2(value)) {
486
489
  pushError(errors, pathPrefix, "must be an object.");
487
490
  return;
488
491
  }
@@ -507,7 +510,7 @@ function validatePatternMap(value, pathPrefix, errors) {
507
510
  }
508
511
  }
509
512
  function validateTargets(value, errors) {
510
- if (!isPlainObject$1(value)) {
513
+ if (!isPlainObject$2(value)) {
511
514
  pushError(errors, "targets", "must be an object.");
512
515
  return;
513
516
  }
@@ -516,7 +519,7 @@ function validateTargets(value, errors) {
516
519
  pushError(errors, "targets", `target keys must be non-empty strings (got "${targetName}").`);
517
520
  continue;
518
521
  }
519
- if (!isPlainObject$1(setting)) {
522
+ if (!isPlainObject$2(setting)) {
520
523
  pushError(errors, `targets.${targetName}`, "must be an object.");
521
524
  continue;
522
525
  }
@@ -532,7 +535,7 @@ function validateTargets(value, errors) {
532
535
  }
533
536
  function validateProfile(value) {
534
537
  const errors = [];
535
- if (!isPlainObject$1(value)) {
538
+ if (!isPlainObject$2(value)) {
536
539
  return {
537
540
  valid: false,
538
541
  errors: [{ path: "", message: "profile must be a JSON object." }]
@@ -578,7 +581,7 @@ function validateProfile(value) {
578
581
  };
579
582
  }
580
583
  function validateVariables(value, errors) {
581
- if (!isPlainObject$1(value)) {
584
+ if (!isPlainObject$2(value)) {
582
585
  pushError(errors, "variables", "must be an object of string values.");
583
586
  return;
584
587
  }
@@ -1272,6 +1275,74 @@ function resolveEffectiveTargets(options) {
1272
1275
  const skipSet = new Set(overrideSkip.map((target) => normalizeTargetName(target)));
1273
1276
  return dedupeTargets(base.filter((target) => !skipSet.has(normalizeTargetName(target))));
1274
1277
  }
1278
+ const agyTarget = {
1279
+ id: "agy",
1280
+ displayName: "Antigravity CLI",
1281
+ aliases: ["gemini"],
1282
+ cli: {
1283
+ modes: {
1284
+ interactive: { command: "agy" },
1285
+ oneShot: { command: "agy" }
1286
+ },
1287
+ // agy requires the prompt as -p's argument; the shim always passes the
1288
+ // (possibly stdin-derived) prompt inline, so piped stdin works.
1289
+ prompt: { type: "flag", flag: ["-p"] },
1290
+ flags: {
1291
+ approval: {
1292
+ values: {
1293
+ prompt: [],
1294
+ "auto-edit": null,
1295
+ yolo: ["--dangerously-skip-permissions"]
1296
+ }
1297
+ },
1298
+ sandbox: {
1299
+ values: {
1300
+ "workspace-write": ["--sandbox"],
1301
+ off: []
1302
+ }
1303
+ },
1304
+ output: {
1305
+ byMode: {
1306
+ "one-shot": {
1307
+ text: [],
1308
+ json: null,
1309
+ "stream-json": null
1310
+ }
1311
+ }
1312
+ },
1313
+ // Model names are display strings, e.g. "Gemini 3.5 Flash (Low)"; list via `agy models`.
1314
+ model: { flag: ["--model"] },
1315
+ structuredOutputFallback: {
1316
+ extraction: { type: "text" }
1317
+ }
1318
+ }
1319
+ },
1320
+ outputs: {
1321
+ skills: "{repoRoot}/.agents/skills/{itemName}",
1322
+ subagents: {
1323
+ path: "{repoRoot}/.agents/skills/{itemName}",
1324
+ fallback: { mode: "convert", targetType: "skills" }
1325
+ },
1326
+ commands: {
1327
+ fallback: { mode: "convert", targetType: "skills" }
1328
+ },
1329
+ instructions: {
1330
+ filename: "AGENTS.md",
1331
+ group: "agents"
1332
+ }
1333
+ },
1334
+ usage: {
1335
+ windows: ["credits"],
1336
+ launch: {
1337
+ command: "agy",
1338
+ timeoutMs: 7e4
1339
+ },
1340
+ extract: async (context) => {
1341
+ const { extractAgyUsage } = await import("./agy-Clt40QYQ.js");
1342
+ return extractAgyUsage(context);
1343
+ }
1344
+ }
1345
+ };
1275
1346
  const claudeTarget = {
1276
1347
  id: "claude",
1277
1348
  displayName: "Claude Code",
@@ -1298,7 +1369,13 @@ const claudeTarget = {
1298
1369
  }
1299
1370
  }
1300
1371
  },
1301
- model: { flag: ["--model"] }
1372
+ model: { flag: ["--model"] },
1373
+ structuredOutput: {
1374
+ delivery: "inline",
1375
+ flag: ["--json-schema"],
1376
+ companionArgs: ["--output-format", "json"],
1377
+ extraction: { type: "json-envelope", field: "structured_output" }
1378
+ }
1302
1379
  }
1303
1380
  },
1304
1381
  outputs: {
@@ -1322,7 +1399,7 @@ const claudeTarget = {
1322
1399
  timeoutMs: 6e4
1323
1400
  },
1324
1401
  extract: async (context) => {
1325
- const { extractClaudeUsage } = await import("./claude-DWeyS4fP.js");
1402
+ const { extractClaudeUsage } = await import("./claude-BuXp9FHS.js");
1326
1403
  return extractClaudeUsage(context);
1327
1404
  }
1328
1405
  }
@@ -1368,7 +1445,12 @@ const codexTarget = {
1368
1445
  }
1369
1446
  },
1370
1447
  model: { flag: ["-m"] },
1371
- web: { on: ["--search"], off: ["--disable", "web_search_request"] }
1448
+ web: { on: ["--search"], off: ["--disable", "web_search_request"] },
1449
+ structuredOutput: {
1450
+ delivery: "file",
1451
+ flag: ["--output-schema"],
1452
+ extraction: { type: "last-message-file", flag: ["--output-last-message"] }
1453
+ }
1372
1454
  }
1373
1455
  },
1374
1456
  outputs: {
@@ -1402,7 +1484,7 @@ const codexTarget = {
1402
1484
  timeoutMs: 6e4
1403
1485
  },
1404
1486
  extract: async (context) => {
1405
- const { extractCodexUsage } = await import("./codex-0b2YLh_8.js");
1487
+ const { extractCodexUsage } = await import("./codex-OCvtxDjw.js");
1406
1488
  return extractCodexUsage(context);
1407
1489
  }
1408
1490
  }
@@ -1500,7 +1582,11 @@ const copilotTarget = {
1500
1582
  }
1501
1583
  }
1502
1584
  },
1503
- model: { flag: ["--model"] }
1585
+ model: { flag: ["--model"] },
1586
+ structuredOutputFallback: {
1587
+ args: ["--silent"],
1588
+ extraction: { type: "text" }
1589
+ }
1504
1590
  }
1505
1591
  },
1506
1592
  outputs: {
@@ -1547,74 +1633,10 @@ const copilotTarget = {
1547
1633
  }
1548
1634
  }
1549
1635
  };
1550
- const geminiTarget = {
1551
- id: "gemini",
1552
- displayName: "Gemini CLI",
1553
- cli: {
1554
- modes: {
1555
- interactive: { command: "gemini" },
1556
- oneShot: { command: "gemini" }
1557
- },
1558
- prompt: { type: "flag", flag: ["-p"] },
1559
- flags: {
1560
- approval: {
1561
- values: {
1562
- prompt: ["--approval-mode", "default"],
1563
- "auto-edit": ["--approval-mode", "auto_edit"],
1564
- yolo: ["--yolo"]
1565
- }
1566
- },
1567
- sandbox: {
1568
- values: {
1569
- "workspace-write": ["--sandbox"],
1570
- off: []
1571
- }
1572
- },
1573
- output: {
1574
- byMode: {
1575
- "one-shot": {
1576
- text: [],
1577
- json: ["--output-format", "json"],
1578
- "stream-json": ["--output-format", "stream-json"]
1579
- }
1580
- }
1581
- },
1582
- model: { flag: ["--model"] },
1583
- web: { on: [], off: null }
1584
- }
1585
- },
1586
- outputs: {
1587
- skills: "{repoRoot}/.gemini/skills/{itemName}",
1588
- subagents: {
1589
- path: "{repoRoot}/.gemini/skills/{itemName}",
1590
- fallback: { mode: "convert", targetType: "skills" }
1591
- },
1592
- commands: {
1593
- projectPath: "{repoRoot}/.gemini/commands/{itemName}.toml",
1594
- userPath: "{homeDir}/.gemini/commands/{itemName}.toml"
1595
- },
1596
- instructions: {
1597
- filename: "GEMINI.md"
1598
- }
1599
- },
1600
- usage: {
1601
- windows: ["model"],
1602
- launch: {
1603
- command: "gemini",
1604
- // /model inspection does not need a model override; Gemini requires session-scoped trust.
1605
- args: ["--skip-trust"],
1606
- timeoutMs: 7e4
1607
- },
1608
- extract: async (context) => {
1609
- const { extractGeminiUsage } = await import("./gemini-BVRg6OMO.js");
1610
- return extractGeminiUsage(context);
1611
- }
1612
- }
1613
- };
1614
1636
  const BUILTIN_TARGETS = [
1615
1637
  codexTarget,
1616
1638
  claudeTarget,
1617
- geminiTarget,
1639
+ agyTarget,
1618
1640
  copilotTarget
1619
1641
  ];
1620
1642
  Object.freeze(BUILTIN_TARGETS.map((target) => target.id));
@@ -2386,7 +2408,7 @@ const COMMON_PLACEHOLDERS = /* @__PURE__ */ new Set([
2386
2408
  "itemName"
2387
2409
  ]);
2388
2410
  const COMMAND_PLACEHOLDERS = /* @__PURE__ */ new Set([...COMMON_PLACEHOLDERS, "commandLocation"]);
2389
- function isPlainObject(value) {
2411
+ function isPlainObject$1(value) {
2390
2412
  return !!value && typeof value === "object" && !Array.isArray(value);
2391
2413
  }
2392
2414
  function normalizeString(value) {
@@ -2415,7 +2437,7 @@ function validateStringArray(value, label, errors, options = {}) {
2415
2437
  }
2416
2438
  }
2417
2439
  function validateFlagMapValues(value, label, allowedValues, errors) {
2418
- if (!isPlainObject(value)) {
2440
+ if (!isPlainObject$1(value)) {
2419
2441
  errors.push(`${label} must be an object.`);
2420
2442
  return;
2421
2443
  }
@@ -2431,7 +2453,7 @@ function validateFlagMapValues(value, label, allowedValues, errors) {
2431
2453
  }
2432
2454
  }
2433
2455
  function validateFlagMap(value, label, allowedValues, errors) {
2434
- if (!isPlainObject(value)) {
2456
+ if (!isPlainObject$1(value)) {
2435
2457
  errors.push(`${label} must be an object.`);
2436
2458
  return;
2437
2459
  }
@@ -2439,7 +2461,7 @@ function validateFlagMap(value, label, allowedValues, errors) {
2439
2461
  validateFlagMapValues(value.values, `${label}.values`, allowedValues, errors);
2440
2462
  }
2441
2463
  if (value.byMode !== void 0) {
2442
- if (!isPlainObject(value.byMode)) {
2464
+ if (!isPlainObject$1(value.byMode)) {
2443
2465
  errors.push(`${label}.byMode must be an object.`);
2444
2466
  } else {
2445
2467
  for (const [mode, entries] of Object.entries(value.byMode)) {
@@ -2452,8 +2474,64 @@ function validateFlagMap(value, label, allowedValues, errors) {
2452
2474
  }
2453
2475
  }
2454
2476
  }
2477
+ function validateStructuredOutputSpec(value, label, errors) {
2478
+ if (!isPlainObject$1(value)) {
2479
+ errors.push(`${label} must be an object.`);
2480
+ return;
2481
+ }
2482
+ if (value.delivery !== "inline" && value.delivery !== "file") {
2483
+ errors.push(`${label}.delivery must be "inline" or "file".`);
2484
+ }
2485
+ validateStringArray(value.flag, `${label}.flag`, errors);
2486
+ if (value.companionArgs !== void 0) {
2487
+ validateStringArray(value.companionArgs, `${label}.companionArgs`, errors, {
2488
+ allowEmpty: true
2489
+ });
2490
+ }
2491
+ if (!isPlainObject$1(value.extraction)) {
2492
+ errors.push(`${label}.extraction must be an object.`);
2493
+ return;
2494
+ }
2495
+ if (value.extraction.type === "json-envelope") {
2496
+ if (normalizeString(value.extraction.field) === null) {
2497
+ errors.push(`${label}.extraction.field must be a non-empty string.`);
2498
+ }
2499
+ return;
2500
+ }
2501
+ if (value.extraction.type === "last-message-file") {
2502
+ validateStringArray(value.extraction.flag, `${label}.extraction.flag`, errors);
2503
+ return;
2504
+ }
2505
+ errors.push(`${label}.extraction.type must be "json-envelope" or "last-message-file".`);
2506
+ }
2507
+ function validateStructuredOutputFallbackSpec(value, label, errors) {
2508
+ if (!isPlainObject$1(value)) {
2509
+ errors.push(`${label} must be an object.`);
2510
+ return;
2511
+ }
2512
+ if (value.args !== void 0) {
2513
+ validateStringArray(value.args, `${label}.args`, errors, { allowEmpty: true });
2514
+ }
2515
+ if (value.extraction === void 0) {
2516
+ return;
2517
+ }
2518
+ if (!isPlainObject$1(value.extraction)) {
2519
+ errors.push(`${label}.extraction must be an object.`);
2520
+ return;
2521
+ }
2522
+ if (value.extraction.type === "text") {
2523
+ return;
2524
+ }
2525
+ if (value.extraction.type === "json-envelope") {
2526
+ if (normalizeString(value.extraction.field) === null) {
2527
+ errors.push(`${label}.extraction.field must be a non-empty string.`);
2528
+ }
2529
+ return;
2530
+ }
2531
+ errors.push(`${label}.extraction.type must be "text" or "json-envelope".`);
2532
+ }
2455
2533
  function validateModeCommand(value, label, errors) {
2456
- if (!isPlainObject(value)) {
2534
+ if (!isPlainObject$1(value)) {
2457
2535
  errors.push(`${label} must be an object.`);
2458
2536
  return;
2459
2537
  }
@@ -2466,7 +2544,7 @@ function validateModeCommand(value, label, errors) {
2466
2544
  }
2467
2545
  }
2468
2546
  function validatePromptSpec(value, label, errors) {
2469
- if (!isPlainObject(value)) {
2547
+ if (!isPlainObject$1(value)) {
2470
2548
  errors.push(`${label} must be an object.`);
2471
2549
  return;
2472
2550
  }
@@ -2486,11 +2564,11 @@ function validateCliDefinition(cli, label, errors) {
2486
2564
  if (!cli) {
2487
2565
  return;
2488
2566
  }
2489
- if (!isPlainObject(cli)) {
2567
+ if (!isPlainObject$1(cli)) {
2490
2568
  errors.push(`${label} must be an object.`);
2491
2569
  return;
2492
2570
  }
2493
- if (!isPlainObject(cli.modes)) {
2571
+ if (!isPlainObject$1(cli.modes)) {
2494
2572
  errors.push(`${label}.modes is required.`);
2495
2573
  return;
2496
2574
  }
@@ -2500,7 +2578,7 @@ function validateCliDefinition(cli, label, errors) {
2500
2578
  validatePromptSpec(cli.prompt, `${label}.prompt`, errors);
2501
2579
  }
2502
2580
  if (cli.flags !== void 0) {
2503
- if (!isPlainObject(cli.flags)) {
2581
+ if (!isPlainObject$1(cli.flags)) {
2504
2582
  errors.push(`${label}.flags must be an object.`);
2505
2583
  } else {
2506
2584
  if (cli.flags.approval !== void 0) {
@@ -2513,7 +2591,7 @@ function validateCliDefinition(cli, label, errors) {
2513
2591
  validateFlagMap(cli.flags.output, `${label}.flags.output`, OUTPUT_FORMATS, errors);
2514
2592
  }
2515
2593
  if (cli.flags.model !== void 0) {
2516
- if (!isPlainObject(cli.flags.model)) {
2594
+ if (!isPlainObject$1(cli.flags.model)) {
2517
2595
  errors.push(`${label}.flags.model must be an object.`);
2518
2596
  } else {
2519
2597
  validateStringArray(cli.flags.model.flag, `${label}.flags.model.flag`, errors);
@@ -2527,8 +2605,22 @@ function validateCliDefinition(cli, label, errors) {
2527
2605
  }
2528
2606
  }
2529
2607
  }
2608
+ if (cli.flags.structuredOutput !== void 0) {
2609
+ validateStructuredOutputSpec(
2610
+ cli.flags.structuredOutput,
2611
+ `${label}.flags.structuredOutput`,
2612
+ errors
2613
+ );
2614
+ }
2615
+ if (cli.flags.structuredOutputFallback !== void 0) {
2616
+ validateStructuredOutputFallbackSpec(
2617
+ cli.flags.structuredOutputFallback,
2618
+ `${label}.flags.structuredOutputFallback`,
2619
+ errors
2620
+ );
2621
+ }
2530
2622
  if (cli.flags.web !== void 0) {
2531
- if (!isPlainObject(cli.flags.web)) {
2623
+ if (!isPlainObject$1(cli.flags.web)) {
2532
2624
  errors.push(`${label}.flags.web must be an object.`);
2533
2625
  } else {
2534
2626
  if (cli.flags.web.on !== void 0 && cli.flags.web.on !== null) {
@@ -2554,7 +2646,7 @@ function validateCliDefinition(cli, label, errors) {
2554
2646
  }
2555
2647
  }
2556
2648
  if (cli.passthrough !== void 0) {
2557
- if (!isPlainObject(cli.passthrough)) {
2649
+ if (!isPlainObject$1(cli.passthrough)) {
2558
2650
  errors.push(`${label}.passthrough must be an object.`);
2559
2651
  } else if (cli.passthrough.position !== void 0 && cli.passthrough.position !== "after" && cli.passthrough.position !== "before-prompt") {
2560
2652
  errors.push(`${label}.passthrough.position must be "after" or "before-prompt".`);
@@ -2568,13 +2660,13 @@ function validateUsageDefinition(usage, label, errors) {
2568
2660
  if (usage === void 0) {
2569
2661
  return;
2570
2662
  }
2571
- if (!isPlainObject(usage)) {
2663
+ if (!isPlainObject$1(usage)) {
2572
2664
  errors.push(`${label} must be an object.`);
2573
2665
  return;
2574
2666
  }
2575
2667
  validateStringArray(usage.windows, `${label}.windows`, errors);
2576
2668
  if (usage.launch !== void 0) {
2577
- if (!isPlainObject(usage.launch)) {
2669
+ if (!isPlainObject$1(usage.launch)) {
2578
2670
  errors.push(`${label}.launch must be an object.`);
2579
2671
  } else {
2580
2672
  if (usage.launch.command !== void 0 && normalizeString(usage.launch.command) === null) {
@@ -2632,7 +2724,7 @@ function validateOutputDefinition(output, label, errors) {
2632
2724
  validateTemplate(output, label, COMMON_PLACEHOLDERS, errors);
2633
2725
  return;
2634
2726
  }
2635
- if (!isPlainObject(output)) {
2727
+ if (!isPlainObject$1(output)) {
2636
2728
  errors.push(`${label} must be a string or an object.`);
2637
2729
  return;
2638
2730
  }
@@ -2647,7 +2739,7 @@ function validateCommandOutputDefinition(output, label, errors) {
2647
2739
  validateTemplate(output, label, COMMAND_PLACEHOLDERS, errors);
2648
2740
  return;
2649
2741
  }
2650
- if (!isPlainObject(output)) {
2742
+ if (!isPlainObject$1(output)) {
2651
2743
  errors.push(`${label} must be a string or an object.`);
2652
2744
  return;
2653
2745
  }
@@ -2671,7 +2763,7 @@ function validateInstructionOutputDefinition(output, label, errors) {
2671
2763
  validateTemplate(output, label, COMMON_PLACEHOLDERS, errors);
2672
2764
  return;
2673
2765
  }
2674
- if (!isPlainObject(output)) {
2766
+ if (!isPlainObject$1(output)) {
2675
2767
  errors.push(`${label} must be a string or an object.`);
2676
2768
  return;
2677
2769
  }
@@ -2684,7 +2776,7 @@ function validateOutputs(outputs, label, errors) {
2684
2776
  if (!outputs) {
2685
2777
  return;
2686
2778
  }
2687
- if (!isPlainObject(outputs)) {
2779
+ if (!isPlainObject$1(outputs)) {
2688
2780
  errors.push(`${label} must be an object.`);
2689
2781
  return;
2690
2782
  }
@@ -2702,7 +2794,7 @@ function validateTargetConfig(options) {
2702
2794
  if (config === null) {
2703
2795
  return { valid: true, errors, config: null };
2704
2796
  }
2705
- if (!isPlainObject(config)) {
2797
+ if (!isPlainObject$1(config)) {
2706
2798
  return {
2707
2799
  valid: false,
2708
2800
  errors: ["Config must export an object."],
@@ -2717,9 +2809,11 @@ function validateTargetConfig(options) {
2717
2809
  }
2718
2810
  const builtInIds = new Set(options.builtIns.map((target) => normalizeLower(target.id)));
2719
2811
  const builtInAliasSet = /* @__PURE__ */ new Set();
2812
+ const builtInAliasToId = /* @__PURE__ */ new Map();
2720
2813
  for (const target of options.builtIns) {
2721
2814
  for (const alias of target.aliases ?? []) {
2722
2815
  builtInAliasSet.add(normalizeLower(alias));
2816
+ builtInAliasToId.set(normalizeLower(alias), normalizeLower(target.id));
2723
2817
  }
2724
2818
  }
2725
2819
  const seenIds = /* @__PURE__ */ new Set();
@@ -2736,7 +2830,8 @@ function validateTargetConfig(options) {
2736
2830
  errors.push("disableTargets entries must be non-empty strings.");
2737
2831
  continue;
2738
2832
  }
2739
- const key = normalizeLower(normalized);
2833
+ const rawKey = normalizeLower(normalized);
2834
+ const key = builtInAliasToId.get(rawKey) ?? rawKey;
2740
2835
  if (!builtInIds.has(key)) {
2741
2836
  errors.push(`disableTargets includes unknown built-in: ${normalized}.`);
2742
2837
  continue;
@@ -2755,7 +2850,7 @@ function validateTargetConfig(options) {
2755
2850
  } else {
2756
2851
  for (const [index, entry2] of config.targets.entries()) {
2757
2852
  const label = `targets[${index}]`;
2758
- if (!isPlainObject(entry2)) {
2853
+ if (!isPlainObject$1(entry2)) {
2759
2854
  errors.push(`${label} must be an object.`);
2760
2855
  continue;
2761
2856
  }
@@ -2770,6 +2865,14 @@ function validateTargetConfig(options) {
2770
2865
  } else {
2771
2866
  seenIds.add(idKey);
2772
2867
  }
2868
+ const aliasOwner = builtInAliasToId.get(idKey);
2869
+ if (aliasOwner) {
2870
+ errors.push(
2871
+ `${label}.id collides with built-in alias (${id}) of target ${aliasOwner}. Pick a distinct id.`
2872
+ );
2873
+ } else if (seenAliases.has(idKey)) {
2874
+ errors.push(`${label}.id collides with existing alias (${id}).`);
2875
+ }
2773
2876
  if (entry2.displayName !== void 0 && normalizeString(entry2.displayName) === null) {
2774
2877
  errors.push(`${label}.displayName must be a non-empty string when provided.`);
2775
2878
  }
@@ -2779,7 +2882,8 @@ function validateTargetConfig(options) {
2779
2882
  }
2780
2883
  if (inherits) {
2781
2884
  const inheritKey = normalizeLower(inherits);
2782
- if (!builtInIds.has(inheritKey)) {
2885
+ const canonicalInherit = builtInAliasToId.get(inheritKey) ?? inheritKey;
2886
+ if (!builtInIds.has(canonicalInherit)) {
2783
2887
  errors.push(`${label}.inherits references unknown built-in: ${inherits}.`);
2784
2888
  }
2785
2889
  }
@@ -2818,7 +2922,8 @@ function validateTargetConfig(options) {
2818
2922
  errors.push(`${label}.aliases collides with existing target id (${aliasValue}).`);
2819
2923
  continue;
2820
2924
  }
2821
- if (seenAliases.has(aliasKey) || builtInAliasSet.has(aliasKey)) {
2925
+ const collidesWithOtherBuiltInAlias = builtInAliasSet.has(aliasKey) && builtInAliasToId.get(aliasKey) !== idKey;
2926
+ if (seenAliases.has(aliasKey) || collidesWithOtherBuiltInAlias) {
2822
2927
  errors.push(`${label}.aliases collides with existing alias (${aliasValue}).`);
2823
2928
  continue;
2824
2929
  }
@@ -2835,7 +2940,8 @@ function validateTargetConfig(options) {
2835
2940
  }
2836
2941
  if (config.disableTargets && Array.isArray(config.disableTargets)) {
2837
2942
  for (const targetId of config.disableTargets) {
2838
- const normalized = normalizeLower(targetId);
2943
+ const rawKey = normalizeLower(targetId);
2944
+ const normalized = builtInAliasToId.get(rawKey) ?? rawKey;
2839
2945
  if (customBuiltInIds.has(normalized)) {
2840
2946
  errors.push(`disableTargets cannot include overridden/inherited target (${targetId}).`);
2841
2947
  }
@@ -2890,7 +2996,7 @@ function mergeOutputs(base, override) {
2890
2996
  }
2891
2997
  function resolveInheritTarget(builtIns, inherits) {
2892
2998
  const key = normalizeKey$2(inherits);
2893
- const found = builtIns.find((target) => normalizeKey$2(target.id) === key);
2999
+ const found = builtIns.find((target) => normalizeKey$2(target.id) === key) ?? builtIns.find((target) => (target.aliases ?? []).some((alias) => normalizeKey$2(alias) === key));
2894
3000
  return found ?? null;
2895
3001
  }
2896
3002
  function resolveTargets(options) {
@@ -2899,12 +3005,18 @@ function resolveTargets(options) {
2899
3005
  for (const target of builtIns) {
2900
3006
  builtInMap.set(normalizeKey$2(target.id), target);
2901
3007
  }
3008
+ const builtInAliasToId = /* @__PURE__ */ new Map();
3009
+ for (const target of builtIns) {
3010
+ for (const alias of target.aliases ?? []) {
3011
+ builtInAliasToId.set(normalizeKey$2(alias), normalizeKey$2(target.id));
3012
+ }
3013
+ }
2902
3014
  const disabledTargets = [];
2903
3015
  const disableSet = /* @__PURE__ */ new Set();
2904
3016
  if (options.config?.disableTargets) {
2905
3017
  for (const entry2 of options.config.disableTargets) {
2906
- const key = normalizeKey$2(entry2);
2907
- disableSet.add(key);
3018
+ const rawKey = normalizeKey$2(entry2);
3019
+ disableSet.add(builtInAliasToId.get(rawKey) ?? rawKey);
2908
3020
  disabledTargets.push(entry2);
2909
3021
  }
2910
3022
  }
@@ -2938,7 +3050,9 @@ function resolveTargets(options) {
2938
3050
  resolvedTargets.push({
2939
3051
  id: customTarget.id,
2940
3052
  displayName: customTarget.displayName ?? inherited?.displayName ?? customTarget.id,
2941
- aliases: customTarget.aliases ?? inherited?.aliases ?? [],
3053
+ // Aliases follow the overridden built-in's identity, not the inherited
3054
+ // definition — inheriting them would move the alias between targets.
3055
+ aliases: customTarget.aliases ?? builtIn.aliases ?? [],
2942
3056
  outputs: mergedOutputs,
2943
3057
  cli: cloneCli(customTarget.cli ?? inherited?.cli),
2944
3058
  usage: cloneUsage(customTarget.usage ?? inherited?.usage),
@@ -2951,7 +3065,7 @@ function resolveTargets(options) {
2951
3065
  resolvedTargets.push({
2952
3066
  id: customTarget.id,
2953
3067
  displayName: customTarget.displayName ?? customTarget.id,
2954
- aliases: customTarget.aliases ?? [],
3068
+ aliases: customTarget.aliases ?? builtIn.aliases ?? [],
2955
3069
  outputs: cloneOutputs(customTarget.outputs),
2956
3070
  cli: cloneCli(customTarget.cli),
2957
3071
  usage: cloneUsage(customTarget.usage),
@@ -2972,7 +3086,9 @@ function resolveTargets(options) {
2972
3086
  resolvedTargets.push({
2973
3087
  id: target.id,
2974
3088
  displayName: target.displayName ?? inherited?.displayName ?? target.id,
2975
- aliases: target.aliases ?? inherited?.aliases ?? [],
3089
+ // Aliases name a specific target, so a new custom target never inherits
3090
+ // them — otherwise it would shadow the built-in's alias in aliasToId.
3091
+ aliases: target.aliases ?? [],
2976
3092
  outputs: mergedOutputs,
2977
3093
  cli: cloneCli(target.cli ?? inherited?.cli),
2978
3094
  usage: cloneUsage(target.usage ?? inherited?.usage),
@@ -3144,7 +3260,7 @@ Profile files must be valid JSON. This commented version is just a guide:
3144
3260
 
3145
3261
  "targets": {
3146
3262
  "claude": { "enabled": true }, // includes Claude; overrides an earlier profile setting it false
3147
- "gemini": { "enabled": false } // skips Gemini for this profile
3263
+ "agy": { "enabled": false } // skips Antigravity for this profile
3148
3264
  },
3149
3265
 
3150
3266
  "enable": { // names/globs to include; also opts in items marked enabled:false
@@ -3578,6 +3694,18 @@ function processTemplating(content, options) {
3578
3694
  const validSet = new Set(normalizedValid);
3579
3695
  const context = { sourcePath: options.sourcePath, validAgents: normalizedValid };
3580
3696
  const target = options.target ? options.target.toLowerCase() : null;
3697
+ const targetKeys = target ? /* @__PURE__ */ new Set([target, ...(options.targetAliases ?? []).map((alias) => alias.toLowerCase())]) : null;
3698
+ const intersects = (set) => {
3699
+ if (!targetKeys) {
3700
+ return false;
3701
+ }
3702
+ for (const key of targetKeys) {
3703
+ if (set.has(key)) {
3704
+ return true;
3705
+ }
3706
+ }
3707
+ return false;
3708
+ };
3581
3709
  let output = "";
3582
3710
  let index = 0;
3583
3711
  while (index < content.length) {
@@ -3608,7 +3736,7 @@ function processTemplating(content, options) {
3608
3736
  );
3609
3737
  }
3610
3738
  const { include, exclude } = parseSelectorList(selectorRaw, validSet, context);
3611
- const shouldInclude = target === null ? false : include.size > 0 ? include.has(target) && !exclude.has(target) : !exclude.has(target);
3739
+ const shouldInclude = target === null ? false : include.size > 0 ? intersects(include) && !intersects(exclude) : !intersects(exclude);
3612
3740
  let cursor = contentStart;
3613
3741
  let blockOutput = "";
3614
3742
  while (cursor < content.length) {
@@ -3658,6 +3786,7 @@ function validateAgentTemplating(options) {
3658
3786
  function applyAgentTemplating(options) {
3659
3787
  return processTemplating(options.content, {
3660
3788
  target: options.target,
3789
+ targetAliases: options.targetAliases,
3661
3790
  validAgents: options.validAgents,
3662
3791
  sourcePath: options.sourcePath
3663
3792
  });
@@ -4030,6 +4159,7 @@ const DEFAULT_SKIP_DIRS = /* @__PURE__ */ new Set([
4030
4159
  ".git",
4031
4160
  "node_modules",
4032
4161
  "dist",
4162
+ ".agents",
4033
4163
  ".claude",
4034
4164
  ".codex",
4035
4165
  ".gemini",
@@ -5167,6 +5297,7 @@ async function copySkillDirectory(options) {
5167
5297
  const templated = applyAgentTemplating({
5168
5298
  content: withScripts,
5169
5299
  target: options.targetId,
5300
+ targetAliases: options.targetAliases,
5170
5301
  validAgents: options.validAgents,
5171
5302
  sourcePath: winner.sourcePath
5172
5303
  });
@@ -5186,6 +5317,7 @@ const defaultSkillWriter = {
5186
5317
  source: item.directoryPath,
5187
5318
  destination: options.outputPath,
5188
5319
  targetId: options.context.targetId,
5320
+ targetAliases: options.context.targetAliases,
5189
5321
  validAgents: options.context.validAgents,
5190
5322
  skillFileName: item.skillFileName,
5191
5323
  outputFileName: item.outputFileName,
@@ -5211,6 +5343,7 @@ const defaultSubagentWriter = {
5211
5343
  const templated = applyAgentTemplating({
5212
5344
  content: withScripts,
5213
5345
  target: options.context.targetId,
5346
+ targetAliases: options.context.targetAliases,
5214
5347
  validAgents: options.context.validAgents,
5215
5348
  sourcePath: item.sourcePath
5216
5349
  });
@@ -5552,6 +5685,7 @@ async function syncInstructions(request) {
5552
5685
  const content = applyAgentTemplating({
5553
5686
  content: withScripts,
5554
5687
  target: targetName,
5688
+ targetAliases: selection.target.aliases,
5555
5689
  validAgents: request.validAgents,
5556
5690
  sourcePath: template.sourcePath
5557
5691
  });
@@ -5753,6 +5887,7 @@ async function syncInstructions(request) {
5753
5887
  agentsDir: agentsRoot,
5754
5888
  homeDir,
5755
5889
  targetId: candidate.targetName,
5890
+ targetAliases: selectionById.get(candidate.targetName)?.target.aliases,
5756
5891
  outputType: "instructions",
5757
5892
  validAgents: request.validAgents
5758
5893
  });
@@ -5805,6 +5940,7 @@ async function syncInstructions(request) {
5805
5940
  agentsDir: agentsRoot,
5806
5941
  homeDir,
5807
5942
  targetId: candidate.targetName,
5943
+ targetAliases: selectionById.get(candidate.targetName)?.target.aliases,
5808
5944
  outputType: "instructions",
5809
5945
  validAgents: request.validAgents,
5810
5946
  templateScriptRuntime
@@ -6301,6 +6437,7 @@ async function syncSkills(request) {
6301
6437
  agentsDir: agentsDirPath,
6302
6438
  homeDir,
6303
6439
  targetId: target.id,
6440
+ targetAliases: target.aliases,
6304
6441
  outputType: "skills",
6305
6442
  validAgents: request.validAgents
6306
6443
  });
@@ -6361,6 +6498,7 @@ async function syncSkills(request) {
6361
6498
  agentsDir: agentsDirPath,
6362
6499
  homeDir,
6363
6500
  targetId: target.id,
6501
+ targetAliases: target.aliases,
6364
6502
  outputType: "skills",
6365
6503
  validAgents: request.validAgents,
6366
6504
  templateScriptRuntime
@@ -6698,7 +6836,7 @@ function buildInvalidTargetWarnings$1(commands) {
6698
6836
  }
6699
6837
  return warnings;
6700
6838
  }
6701
- async function applyTemplatingToCommand(command, targetName, validAgents, runtime) {
6839
+ async function applyTemplatingToCommand(command, targetName, validAgents, runtime, targetAliases) {
6702
6840
  const withScripts = runtime ? await evaluateTemplateScripts({
6703
6841
  templatePath: command.sourcePath,
6704
6842
  content: command.rawContents,
@@ -6707,6 +6845,7 @@ async function applyTemplatingToCommand(command, targetName, validAgents, runtim
6707
6845
  const templatedContents = applyAgentTemplating({
6708
6846
  content: withScripts,
6709
6847
  target: targetName,
6848
+ targetAliases,
6710
6849
  validAgents,
6711
6850
  sourcePath: command.sourcePath
6712
6851
  });
@@ -6974,7 +7113,8 @@ async function syncSlashCommands(request) {
6974
7113
  command,
6975
7114
  target.id,
6976
7115
  validAgents,
6977
- templateScriptRuntime
7116
+ templateScriptRuntime,
7117
+ target.aliases
6978
7118
  );
6979
7119
  const writer = resolveWriter(commandDef.writer, writerRegistry);
6980
7120
  const converter = resolveConverter(commandDef.converter, converterRegistry);
@@ -7050,6 +7190,7 @@ async function syncSlashCommands(request) {
7050
7190
  agentsDir: agentsDirPath,
7051
7191
  homeDir,
7052
7192
  targetId: target.id,
7193
+ targetAliases: target.aliases,
7053
7194
  outputType: "commands",
7054
7195
  commandLocation: selected.location,
7055
7196
  validAgents
@@ -7111,6 +7252,7 @@ async function syncSlashCommands(request) {
7111
7252
  agentsDir: agentsDirPath,
7112
7253
  homeDir,
7113
7254
  targetId: target.id,
7255
+ targetAliases: target.aliases,
7114
7256
  outputType: "commands",
7115
7257
  commandLocation: selected.location,
7116
7258
  validAgents,
@@ -8027,6 +8169,7 @@ async function syncSubagents(request) {
8027
8169
  agentsDir: agentsDirPath,
8028
8170
  homeDir,
8029
8171
  targetId: target.id,
8172
+ targetAliases: target.aliases,
8030
8173
  outputType: "subagents",
8031
8174
  validAgents
8032
8175
  });
@@ -8087,6 +8230,7 @@ async function syncSubagents(request) {
8087
8230
  agentsDir: agentsDirPath,
8088
8231
  homeDir,
8089
8232
  targetId: target.id,
8233
+ targetAliases: target.aliases,
8090
8234
  outputType: "subagents",
8091
8235
  validAgents,
8092
8236
  templateScriptRuntime
@@ -8474,7 +8618,9 @@ async function checkTargetAvailability(target) {
8474
8618
  checks
8475
8619
  };
8476
8620
  }
8477
- const DEFAULT_SUPPORTED_TARGETS = BUILTIN_TARGETS.map((target) => target.id).join(", ");
8621
+ const DEFAULT_SUPPORTED_TARGETS = BUILTIN_TARGETS.map(
8622
+ (target) => target.aliases?.length ? `${target.id} (alias: ${target.aliases.join(", ")})` : target.id
8623
+ ).join(", ");
8478
8624
  const LOCAL_CATEGORIES = ["skills", "commands", "agents", "instructions"];
8479
8625
  const LOCAL_CATEGORY_SET = new Set(LOCAL_CATEGORIES);
8480
8626
  const utf8Decoder = new TextDecoder("utf-8", { fatal: true });
@@ -10377,6 +10523,7 @@ function makeUsageLimit(options) {
10377
10523
  modelLabel: options.modelLabel,
10378
10524
  percentUsed: options.percentUsed,
10379
10525
  percentRemaining: options.percentRemaining,
10526
+ remainingText: emptyToNull(options.remainingText) ?? void 0,
10380
10527
  resetAt: parseResetAt(resetText, {
10381
10528
  now: options.now,
10382
10529
  sourceTimeZone: options.resetSourceTimeZone ?? "local"
@@ -11142,6 +11289,9 @@ function leftCellText(row) {
11142
11289
  if (row.status === "error") {
11143
11290
  return "-";
11144
11291
  }
11292
+ if (row.limit.percentRemaining == null && row.limit.remainingText) {
11293
+ return row.limit.remainingText;
11294
+ }
11145
11295
  return percentText(row.limit.percentRemaining);
11146
11296
  }
11147
11297
  function resetCellText(row) {
@@ -11708,6 +11858,9 @@ function translateInvocation(invocation, cli, options = {}) {
11708
11858
  args.push(...mapped);
11709
11859
  }
11710
11860
  }
11861
+ if (invocation.structuredOutput) {
11862
+ args.push(...invocation.structuredOutput.args);
11863
+ }
11711
11864
  if (mode === "one-shot" && base.args?.includes("exec")) {
11712
11865
  const searchIndex = args.indexOf("--search");
11713
11866
  if (searchIndex > -1) {
@@ -11765,9 +11918,344 @@ function buildAgentArgs(invocation) {
11765
11918
  warnings: translated.warnings
11766
11919
  };
11767
11920
  }
11921
+ const PREVIOUS_OUTPUT_LIMIT = 4e3;
11922
+ const RESPONSE_RULES = "Respond with only the JSON value - no explanations, no markdown code fences, no additional text.";
11923
+ function schemaBlock(schemaJson) {
11924
+ return `Your entire response must be a single JSON value that conforms to this JSON Schema:
11925
+ ${schemaJson}`;
11926
+ }
11927
+ function truncate(text, limit) {
11928
+ if (text.length <= limit) {
11929
+ return text;
11930
+ }
11931
+ return `${text.slice(0, limit)}
11932
+ [truncated]`;
11933
+ }
11934
+ function buildFallbackPrompt(prompt, schemaJson) {
11935
+ const base = prompt.trim().length > 0 ? `${prompt}
11936
+
11937
+ ` : "";
11938
+ return `${base}${schemaBlock(schemaJson)}
11939
+
11940
+ ${RESPONSE_RULES}`;
11941
+ }
11942
+ function buildRetryPrompt(prompt, schemaJson, previousOutput, errors) {
11943
+ const base = prompt.trim().length > 0 ? `${prompt}
11944
+
11945
+ ` : "";
11946
+ const previous = previousOutput.trim().length > 0 ? truncate(previousOutput.trim(), PREVIOUS_OUTPUT_LIMIT) : "(empty response)";
11947
+ const errorLines = errors.map((error) => `- ${error}`).join("\n");
11948
+ return [
11949
+ `${base}${schemaBlock(schemaJson)}`,
11950
+ `Your previous response failed validation.
11951
+
11952
+ Previous response:
11953
+ ${previous}`,
11954
+ `Validation errors:
11955
+ ${errorLines}`,
11956
+ `Respond again with only the corrected JSON value. ${RESPONSE_RULES}`
11957
+ ].join("\n\n");
11958
+ }
11959
+ function tryParse(text) {
11960
+ try {
11961
+ return { ok: true, value: JSON.parse(text) };
11962
+ } catch {
11963
+ return null;
11964
+ }
11965
+ }
11966
+ function extractJsonPayload(text) {
11967
+ const trimmed = text.trim();
11968
+ if (trimmed.length === 0) {
11969
+ return { ok: false, error: "the response was empty" };
11970
+ }
11971
+ const whole = tryParse(trimmed);
11972
+ if (whole) {
11973
+ return whole;
11974
+ }
11975
+ const fenced = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
11976
+ if (fenced?.[1]) {
11977
+ const parsed = tryParse(fenced[1].trim());
11978
+ if (parsed) {
11979
+ return parsed;
11980
+ }
11981
+ }
11982
+ for (const [open, close] of [
11983
+ ["{", "}"],
11984
+ ["[", "]"]
11985
+ ]) {
11986
+ const start = trimmed.indexOf(open);
11987
+ const end = trimmed.lastIndexOf(close);
11988
+ if (start !== -1 && end > start) {
11989
+ const parsed = tryParse(trimmed.slice(start, end + 1));
11990
+ if (parsed) {
11991
+ return parsed;
11992
+ }
11993
+ }
11994
+ }
11995
+ return { ok: false, error: "the response did not contain parseable JSON" };
11996
+ }
11997
+ function withPipedStdout(stdio) {
11998
+ if (Array.isArray(stdio)) {
11999
+ return [stdio[0] ?? "inherit", "pipe", stdio[2] ?? "inherit"];
12000
+ }
12001
+ return [stdio, "pipe", stdio];
12002
+ }
12003
+ function mapExitCode(code) {
12004
+ if (code === 0) {
12005
+ return { exitCode: 0, reason: "success" };
12006
+ }
12007
+ if (code === 2) {
12008
+ return { exitCode: 2, reason: "invalid-usage" };
12009
+ }
12010
+ if (code === 3) {
12011
+ return { exitCode: 3, reason: "blocked" };
12012
+ }
12013
+ return { exitCode: 1, reason: "execution-error" };
12014
+ }
12015
+ async function finalizeStructuredOutput(options) {
12016
+ const { plan, agentId, code, captured, stdout, stderr } = options;
12017
+ if (code !== 0) {
12018
+ if (plan.capture.type === "json-envelope" && captured.length > 0) {
12019
+ stderr.write(captured);
12020
+ }
12021
+ return mapExitCode(code);
12022
+ }
12023
+ if (plan.capture.type === "json-envelope") {
12024
+ let envelope;
12025
+ try {
12026
+ envelope = JSON.parse(captured);
12027
+ } catch {
12028
+ envelope = void 0;
12029
+ }
12030
+ if (typeof envelope !== "object" || envelope === null || Array.isArray(envelope)) {
12031
+ stderr.write(captured);
12032
+ stderr.write(`Error: ${agentId} did not return a JSON envelope.
12033
+ `);
12034
+ return { exitCode: 1, reason: "execution-error" };
12035
+ }
12036
+ const record = envelope;
12037
+ if (record.is_error === true) {
12038
+ stderr.write(captured);
12039
+ stderr.write(`Error: ${agentId} reported an error result.
12040
+ `);
12041
+ return { exitCode: 1, reason: "execution-error" };
12042
+ }
12043
+ const payload = record[plan.capture.field];
12044
+ if (payload === void 0 || payload === null) {
12045
+ stderr.write(captured);
12046
+ stderr.write(`Error: ${agentId} response is missing ${plan.capture.field}.
12047
+ `);
12048
+ return { exitCode: 1, reason: "execution-error" };
12049
+ }
12050
+ stdout.write(`${JSON.stringify(payload)}
12051
+ `);
12052
+ return { exitCode: 0, reason: "success" };
12053
+ }
12054
+ if (plan.capture.type === "fallback") {
12055
+ return { exitCode: 1, reason: "execution-error" };
12056
+ }
12057
+ let contents;
12058
+ try {
12059
+ contents = await readFile(plan.capture.path, "utf8");
12060
+ } catch {
12061
+ contents = "";
12062
+ }
12063
+ const trimmed = contents.trim();
12064
+ if (!trimmed) {
12065
+ stderr.write(`Error: ${agentId} did not produce a structured output message.
12066
+ `);
12067
+ return { exitCode: 1, reason: "execution-error" };
12068
+ }
12069
+ stdout.write(`${trimmed}
12070
+ `);
12071
+ return { exitCode: 0, reason: "success" };
12072
+ }
12073
+ function runCapturedAttempt(params) {
12074
+ return new Promise((resolve) => {
12075
+ let settled = false;
12076
+ const settle = (value) => {
12077
+ if (!settled) {
12078
+ settled = true;
12079
+ resolve(value);
12080
+ }
12081
+ };
12082
+ const child = params.spawn(params.command, params.args, { stdio: params.stdio });
12083
+ const chunks = [];
12084
+ const childStdout = child.stdout;
12085
+ if (childStdout) {
12086
+ childStdout.on("data", (chunk) => {
12087
+ chunks.push(Buffer.from(chunk));
12088
+ });
12089
+ }
12090
+ child.on("error", (error) => {
12091
+ params.stderr.write(`Error: ${error.message}
12092
+ `);
12093
+ settle({ kind: "spawn-error" });
12094
+ });
12095
+ child.on("close", (code) => {
12096
+ if (!childStdout) {
12097
+ params.stderr.write(`Error: ${params.agentId} stdout was not captured.
12098
+ `);
12099
+ settle({ kind: "spawn-error" });
12100
+ return;
12101
+ }
12102
+ settle({ kind: "closed", code, captured: Buffer.concat(chunks).toString("utf8") });
12103
+ });
12104
+ });
12105
+ }
12106
+ function extractEnvelopeText(params) {
12107
+ const { captured, field, agentId, stderr } = params;
12108
+ let envelope;
12109
+ try {
12110
+ envelope = JSON.parse(captured);
12111
+ } catch {
12112
+ envelope = void 0;
12113
+ }
12114
+ if (typeof envelope !== "object" || envelope === null || Array.isArray(envelope)) {
12115
+ if (captured.length > 0) {
12116
+ stderr.write(captured);
12117
+ }
12118
+ stderr.write(`Error: ${agentId} did not return a JSON envelope.
12119
+ `);
12120
+ return { ok: false };
12121
+ }
12122
+ const record = envelope;
12123
+ if (record.error !== void 0 && record.error !== null) {
12124
+ stderr.write(captured);
12125
+ stderr.write(`Error: ${agentId} reported an error result.
12126
+ `);
12127
+ return { ok: false };
12128
+ }
12129
+ const payload = record[field];
12130
+ if (payload === void 0 || payload === null) {
12131
+ stderr.write(captured);
12132
+ stderr.write(`Error: ${agentId} response is missing ${field}.
12133
+ `);
12134
+ return { ok: false };
12135
+ }
12136
+ return { ok: true, text: typeof payload === "string" ? payload : JSON.stringify(payload) };
12137
+ }
12138
+ async function runFallbackAttempts(params) {
12139
+ const { invocation, plan, capture, options, stdout, stderr } = params;
12140
+ const agentId = invocation.agent.id;
12141
+ const spawn$1 = options.spawn ?? spawn;
12142
+ const stdio = withPipedStdout(options.stdio ?? "inherit");
12143
+ const originalPrompt = invocation.prompt ?? "";
12144
+ const validate = plan.validate ?? (() => ({ valid: true, errors: [] }));
12145
+ let feedback = null;
12146
+ for (let attempt = 1; attempt <= capture.maxAttempts; attempt += 1) {
12147
+ const attemptPrompt = feedback ? buildRetryPrompt(originalPrompt, plan.schemaJson, feedback.output, feedback.errors) : buildFallbackPrompt(originalPrompt, plan.schemaJson);
12148
+ const built = buildAgentArgs({ ...invocation, prompt: attemptPrompt });
12149
+ if (attempt === 1) {
12150
+ if (options.traceTranslate) {
12151
+ const trace = {
12152
+ agent: agentId,
12153
+ mode: invocation.mode,
12154
+ command: built.command,
12155
+ args: built.args,
12156
+ shimArgs: built.shimArgs,
12157
+ passthroughArgs: built.passthroughArgs,
12158
+ warnings: built.warnings,
12159
+ requests: invocation.requests,
12160
+ structuredOutput: { capture: capture.type, maxAttempts: capture.maxAttempts }
12161
+ };
12162
+ stderr.write(`OA_TRANSLATION=${JSON.stringify(trace)}
12163
+ `);
12164
+ }
12165
+ for (const warning of built.warnings) {
12166
+ stderr.write(`${warning}
12167
+ `);
12168
+ }
12169
+ for (const notice of plan.notices ?? []) {
12170
+ stderr.write(`${notice}
12171
+ `);
12172
+ }
12173
+ }
12174
+ const attemptResult = await runCapturedAttempt({
12175
+ spawn: spawn$1,
12176
+ command: built.command,
12177
+ args: built.args,
12178
+ stdio,
12179
+ agentId,
12180
+ stderr
12181
+ });
12182
+ if (attemptResult.kind === "spawn-error") {
12183
+ return { exitCode: 1, reason: "execution-error" };
12184
+ }
12185
+ if (attemptResult.code !== 0) {
12186
+ if (attemptResult.captured.length > 0) {
12187
+ stderr.write(attemptResult.captured);
12188
+ }
12189
+ return mapExitCode(attemptResult.code);
12190
+ }
12191
+ let responseText = attemptResult.captured;
12192
+ if (capture.extraction.type === "json-envelope") {
12193
+ const outcome = extractEnvelopeText({
12194
+ captured: attemptResult.captured,
12195
+ field: capture.extraction.field,
12196
+ agentId,
12197
+ stderr
12198
+ });
12199
+ if (!outcome.ok) {
12200
+ return { exitCode: 1, reason: "execution-error" };
12201
+ }
12202
+ responseText = outcome.text;
12203
+ }
12204
+ const extracted = extractJsonPayload(responseText);
12205
+ if (extracted.ok) {
12206
+ const result = validate(extracted.value);
12207
+ if (result.valid) {
12208
+ stdout.write(`${JSON.stringify(extracted.value)}
12209
+ `);
12210
+ return { exitCode: 0, reason: "success" };
12211
+ }
12212
+ feedback = { output: responseText, errors: result.errors };
12213
+ } else {
12214
+ feedback = { output: responseText, errors: [extracted.error] };
12215
+ }
12216
+ if (attempt < capture.maxAttempts) {
12217
+ stderr.write(
12218
+ `Attempt ${attempt} failed schema validation; retrying (${attempt + 1}/${capture.maxAttempts}).
12219
+ `
12220
+ );
12221
+ for (const error of feedback.errors) {
12222
+ stderr.write(`- ${error}
12223
+ `);
12224
+ }
12225
+ }
12226
+ }
12227
+ if (feedback) {
12228
+ const lastOutput = feedback.output.trim();
12229
+ if (lastOutput.length > 0) {
12230
+ stderr.write(`${lastOutput}
12231
+ `);
12232
+ }
12233
+ for (const error of feedback.errors) {
12234
+ stderr.write(`- ${error}
12235
+ `);
12236
+ }
12237
+ }
12238
+ stderr.write(
12239
+ `Error: ${agentId} response failed schema validation after ${capture.maxAttempts} attempts.
12240
+ `
12241
+ );
12242
+ return { exitCode: 1, reason: "execution-error" };
12243
+ }
11768
12244
  async function executeInvocation(invocation, options = {}) {
11769
- const { command, args, warnings, shimArgs, passthroughArgs } = buildAgentArgs(invocation);
11770
12245
  const stderr = options.stderr ?? process.stderr;
12246
+ const stdout = options.stdout ?? process.stdout;
12247
+ const plan = invocation.structuredOutput;
12248
+ if (plan && plan.capture.type === "fallback") {
12249
+ return runFallbackAttempts({
12250
+ invocation,
12251
+ plan,
12252
+ capture: plan.capture,
12253
+ options,
12254
+ stdout,
12255
+ stderr
12256
+ });
12257
+ }
12258
+ const { command, args, warnings, shimArgs, passthroughArgs } = buildAgentArgs(invocation);
11771
12259
  if (options.traceTranslate) {
11772
12260
  const trace = {
11773
12261
  agent: invocation.agent.id,
@@ -11777,17 +12265,23 @@ async function executeInvocation(invocation, options = {}) {
11777
12265
  shimArgs,
11778
12266
  passthroughArgs,
11779
12267
  warnings,
11780
- requests: invocation.requests
12268
+ requests: invocation.requests,
12269
+ ...plan ? { structuredOutput: { capture: plan.capture.type } } : {}
11781
12270
  };
11782
12271
  stderr.write(`OA_TRANSLATION=${JSON.stringify(trace)}
11783
12272
  `);
11784
12273
  }
11785
12274
  for (const warning of warnings) {
11786
12275
  stderr.write(`${warning}
12276
+ `);
12277
+ }
12278
+ for (const notice of plan?.notices ?? []) {
12279
+ stderr.write(`${notice}
11787
12280
  `);
11788
12281
  }
11789
12282
  const spawn$1 = options.spawn ?? spawn;
11790
- const stdio = options.stdio ?? "inherit";
12283
+ const baseStdio = options.stdio ?? "inherit";
12284
+ const stdio = plan ? withPipedStdout(baseStdio) : baseStdio;
11791
12285
  return await new Promise((resolve) => {
11792
12286
  const child = spawn$1(command, args, { stdio });
11793
12287
  child.on("error", (error) => {
@@ -11795,20 +12289,40 @@ async function executeInvocation(invocation, options = {}) {
11795
12289
  `);
11796
12290
  resolve({ exitCode: 1, reason: "execution-error" });
11797
12291
  });
11798
- child.on("exit", (code) => {
11799
- if (code === 0) {
11800
- resolve({ exitCode: 0, reason: "success" });
11801
- return;
11802
- }
11803
- if (code === 2) {
11804
- resolve({ exitCode: 2, reason: "invalid-usage" });
11805
- return;
12292
+ if (!plan) {
12293
+ child.on("exit", (code) => {
12294
+ resolve(mapExitCode(code));
12295
+ });
12296
+ return;
12297
+ }
12298
+ const chunks = [];
12299
+ const childStdout = child.stdout;
12300
+ if (childStdout) {
12301
+ if (plan.capture.type === "json-envelope") {
12302
+ childStdout.on("data", (chunk) => {
12303
+ chunks.push(Buffer.from(chunk));
12304
+ });
12305
+ } else {
12306
+ childStdout.on("data", (chunk) => {
12307
+ stderr.write(chunk);
12308
+ });
11806
12309
  }
11807
- if (code === 3) {
11808
- resolve({ exitCode: 3, reason: "blocked" });
12310
+ }
12311
+ child.on("close", (code) => {
12312
+ if (!childStdout) {
12313
+ stderr.write(`Error: ${invocation.agent.id} stdout was not captured.
12314
+ `);
12315
+ resolve({ exitCode: 1, reason: "execution-error" });
11809
12316
  return;
11810
12317
  }
11811
- resolve({ exitCode: 1, reason: "execution-error" });
12318
+ void finalizeStructuredOutput({
12319
+ plan,
12320
+ agentId: invocation.agent.id,
12321
+ code,
12322
+ captured: Buffer.concat(chunks).toString("utf8"),
12323
+ stdout,
12324
+ stderr
12325
+ }).then(resolve);
11812
12326
  });
11813
12327
  });
11814
12328
  }
@@ -11846,6 +12360,16 @@ function readFlagValue(args, index, label) {
11846
12360
  }
11847
12361
  return [value, index + 1];
11848
12362
  }
12363
+ function parseRetriesValue(value) {
12364
+ const normalized = normalizeValue(value, "--output-schema-retries");
12365
+ const parsed = Number(normalized);
12366
+ if (!Number.isInteger(parsed) || parsed < 0 || parsed > 10) {
12367
+ throw new InvalidUsageError(
12368
+ "Invalid value for --output-schema-retries. Provide an integer between 0 and 10."
12369
+ );
12370
+ }
12371
+ return parsed;
12372
+ }
11849
12373
  function parseBooleanValue(label, value) {
11850
12374
  const normalized = normalizeValue(value, label).toLowerCase();
11851
12375
  if (["on", "true", "1"].includes(normalized)) {
@@ -11874,6 +12398,9 @@ function parseShimFlags(argv) {
11874
12398
  let webExplicit = false;
11875
12399
  let agent = null;
11876
12400
  let agentExplicit = false;
12401
+ let outputSchema = null;
12402
+ let outputSchemaExplicit = false;
12403
+ let outputSchemaRetries = null;
11877
12404
  let traceTranslate = false;
11878
12405
  let help = false;
11879
12406
  let version = false;
@@ -12011,6 +12538,28 @@ function parseShimFlags(argv) {
12011
12538
  agentExplicit = true;
12012
12539
  continue;
12013
12540
  }
12541
+ if (arg === "--output-schema-retries") {
12542
+ const [value, nextIndex] = readFlagValue(preArgs, index, "--output-schema-retries");
12543
+ outputSchemaRetries = parseRetriesValue(value);
12544
+ index = nextIndex;
12545
+ continue;
12546
+ }
12547
+ if (arg.startsWith("--output-schema-retries=")) {
12548
+ outputSchemaRetries = parseRetriesValue(arg.slice("--output-schema-retries=".length));
12549
+ continue;
12550
+ }
12551
+ if (arg === "--output-schema") {
12552
+ const [value, nextIndex] = readFlagValue(preArgs, index, "--output-schema");
12553
+ outputSchema = normalizeValue(value, "--output-schema");
12554
+ outputSchemaExplicit = true;
12555
+ index = nextIndex;
12556
+ continue;
12557
+ }
12558
+ if (arg.startsWith("--output-schema=")) {
12559
+ outputSchema = normalizeValue(arg.slice("--output-schema=".length), "--output-schema");
12560
+ outputSchemaExplicit = true;
12561
+ continue;
12562
+ }
12014
12563
  if (arg === "--trace-translate") {
12015
12564
  traceTranslate = true;
12016
12565
  continue;
@@ -12029,6 +12578,14 @@ function parseShimFlags(argv) {
12029
12578
  if (outputSelections.length > 0) {
12030
12579
  output = outputSelections[outputSelections.length - 1];
12031
12580
  }
12581
+ if (outputSchema !== null && outputExplicit) {
12582
+ throw new InvalidUsageError(
12583
+ "--output-schema cannot be combined with --output, --json, or --stream-json."
12584
+ );
12585
+ }
12586
+ if (outputSchemaRetries !== null && outputSchema === null) {
12587
+ throw new InvalidUsageError("--output-schema-retries requires --output-schema.");
12588
+ }
12032
12589
  if (approval === "yolo" && !sandboxExplicit) {
12033
12590
  sandbox = "off";
12034
12591
  }
@@ -12047,6 +12604,9 @@ function parseShimFlags(argv) {
12047
12604
  webExplicit,
12048
12605
  agent,
12049
12606
  agentExplicit,
12607
+ outputSchema,
12608
+ outputSchemaExplicit,
12609
+ outputSchemaRetries,
12050
12610
  traceTranslate,
12051
12611
  help,
12052
12612
  version,
@@ -12145,6 +12705,157 @@ async function resolveAgentSelection(flags, repoRoot, agentsDir) {
12145
12705
  }
12146
12706
  return { resolution: { selection, targetId: resolvedId }, targetMap };
12147
12707
  }
12708
+ const AJV_OPTIONS = { allErrors: true, strict: false, validateFormats: false };
12709
+ const instances = /* @__PURE__ */ new Map();
12710
+ function resolveDialect(schema) {
12711
+ const declared = typeof schema.$schema === "string" ? schema.$schema : "";
12712
+ if (declared.includes("2020-12")) {
12713
+ return "2020-12";
12714
+ }
12715
+ if (declared.includes("2019-09")) {
12716
+ return "2019-09";
12717
+ }
12718
+ return "draft-07";
12719
+ }
12720
+ function ajvForDialect(dialect) {
12721
+ let instance = instances.get(dialect);
12722
+ if (!instance) {
12723
+ instance = dialect === "2020-12" ? new Ajv2020(AJV_OPTIONS) : dialect === "2019-09" ? new Ajv2019(AJV_OPTIONS) : new Ajv(AJV_OPTIONS);
12724
+ instances.set(dialect, instance);
12725
+ }
12726
+ return instance;
12727
+ }
12728
+ function compileSchemaValidator(schemaJson) {
12729
+ let compiled;
12730
+ try {
12731
+ const schema = JSON.parse(schemaJson);
12732
+ compiled = ajvForDialect(resolveDialect(schema)).compile(schema);
12733
+ } catch (error) {
12734
+ const message = error instanceof Error ? error.message : String(error);
12735
+ throw new InvalidUsageError(`Invalid value for --output-schema: ${message}.`);
12736
+ }
12737
+ return (data) => {
12738
+ const valid = compiled(data) === true;
12739
+ const errors = (compiled.errors ?? []).map(
12740
+ (error) => `${error.instancePath || "/"}: ${error.message ?? "is invalid"}`
12741
+ );
12742
+ return { valid, errors };
12743
+ };
12744
+ }
12745
+ const SCHEMA_LABEL = "--output-schema";
12746
+ const DEFAULT_SCHEMA_RETRIES = 2;
12747
+ function isPlainObject(value) {
12748
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12749
+ }
12750
+ function parseSchemaJson(text, sourceLabel) {
12751
+ let parsed;
12752
+ try {
12753
+ parsed = JSON.parse(text);
12754
+ } catch {
12755
+ throw new InvalidUsageError(`Invalid value for ${SCHEMA_LABEL}: ${sourceLabel}.`);
12756
+ }
12757
+ if (!isPlainObject(parsed)) {
12758
+ throw new InvalidUsageError(`Invalid value for ${SCHEMA_LABEL}: schema must be a JSON object.`);
12759
+ }
12760
+ return JSON.stringify(parsed);
12761
+ }
12762
+ async function resolveOutputSchema(raw) {
12763
+ const trimmed = raw.trim();
12764
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
12765
+ return parseSchemaJson(trimmed, "schema is not valid JSON");
12766
+ }
12767
+ let contents;
12768
+ try {
12769
+ contents = await readFile(trimmed, "utf8");
12770
+ } catch {
12771
+ throw new InvalidUsageError(
12772
+ `Invalid value for ${SCHEMA_LABEL}: cannot read schema file ${trimmed}.`
12773
+ );
12774
+ }
12775
+ return parseSchemaJson(contents, `schema file ${trimmed} is not valid JSON`);
12776
+ }
12777
+ async function planStructuredOutput(options) {
12778
+ const { rawSchema, mode, agentId, spec } = options;
12779
+ if (rawSchema === null) {
12780
+ return null;
12781
+ }
12782
+ if (mode !== "one-shot") {
12783
+ throw new InvalidUsageError(
12784
+ `${SCHEMA_LABEL} requires one-shot mode; provide -p/--prompt or pipe stdin.`
12785
+ );
12786
+ }
12787
+ if (!spec) {
12788
+ return planFallback(options);
12789
+ }
12790
+ const notices = [];
12791
+ if (options.retries !== null && options.retries !== void 0) {
12792
+ notices.push(
12793
+ `Warning: ${agentId} uses native ${SCHEMA_LABEL} support; ignoring ${SCHEMA_LABEL}-retries.`
12794
+ );
12795
+ }
12796
+ const schemaJson = await resolveOutputSchema(rawSchema);
12797
+ const needsTempDir = spec.delivery === "file" || spec.extraction.type === "last-message-file";
12798
+ const tempPaths = [];
12799
+ let tempDirPath = null;
12800
+ if (needsTempDir) {
12801
+ tempDirPath = await mkdtemp(path.join(options.tempDir ?? os.tmpdir(), "omniagent-schema-"));
12802
+ tempPaths.push(tempDirPath);
12803
+ }
12804
+ let deliveryValue = schemaJson;
12805
+ if (spec.delivery === "file" && tempDirPath) {
12806
+ deliveryValue = path.join(tempDirPath, "schema.json");
12807
+ await writeFile(deliveryValue, schemaJson, "utf8");
12808
+ }
12809
+ const args = [...spec.flag, deliveryValue, ...spec.companionArgs ?? []];
12810
+ let capture;
12811
+ if (spec.extraction.type === "json-envelope") {
12812
+ capture = { type: "json-envelope", field: spec.extraction.field };
12813
+ } else {
12814
+ const lastMessagePath = path.join(tempDirPath ?? os.tmpdir(), "last-message.txt");
12815
+ args.push(...spec.extraction.flag, lastMessagePath);
12816
+ capture = { type: "last-message-file", path: lastMessagePath };
12817
+ }
12818
+ return { schemaJson, args, capture, tempPaths, notices };
12819
+ }
12820
+ async function planFallback(options) {
12821
+ const { rawSchema, agentId, fallbackSpec } = options;
12822
+ if (rawSchema === null) {
12823
+ return null;
12824
+ }
12825
+ if (options.promptDeliverable === false) {
12826
+ throw new InvalidUsageError(
12827
+ `${agentId} cannot use the ${SCHEMA_LABEL} fallback: target defines no prompt flag.`
12828
+ );
12829
+ }
12830
+ const schemaJson = await resolveOutputSchema(rawSchema);
12831
+ const validate = compileSchemaValidator(schemaJson);
12832
+ const maxAttempts = (options.retries ?? DEFAULT_SCHEMA_RETRIES) + 1;
12833
+ return {
12834
+ schemaJson,
12835
+ args: [...fallbackSpec?.args ?? []],
12836
+ capture: {
12837
+ type: "fallback",
12838
+ extraction: fallbackSpec?.extraction ?? { type: "text" },
12839
+ maxAttempts
12840
+ },
12841
+ tempPaths: [],
12842
+ validate,
12843
+ notices: [
12844
+ `Notice: ${agentId} lacks native ${SCHEMA_LABEL} support; using prompt-based fallback with client-side validation.`
12845
+ ]
12846
+ };
12847
+ }
12848
+ async function cleanupStructuredOutput(plan) {
12849
+ if (!plan) {
12850
+ return;
12851
+ }
12852
+ for (const tempPath of plan.tempPaths) {
12853
+ try {
12854
+ await rm(tempPath, { recursive: true, force: true });
12855
+ } catch {
12856
+ }
12857
+ }
12858
+ }
12148
12859
  function normalizeKey(value) {
12149
12860
  return value.trim().toLowerCase();
12150
12861
  }
@@ -12165,6 +12876,16 @@ async function resolveInvocationFromFlags(options) {
12165
12876
  const agent = resolution.selection;
12166
12877
  const session = buildSession(flags);
12167
12878
  const requests = buildRequests(flags);
12879
+ const structuredOutput = await planStructuredOutput({
12880
+ rawSchema: flags.outputSchema,
12881
+ mode,
12882
+ agentId: agent.id,
12883
+ spec: target.cli?.flags?.structuredOutput,
12884
+ fallbackSpec: target.cli?.flags?.structuredOutputFallback,
12885
+ retries: flags.outputSchemaRetries,
12886
+ promptDeliverable: Boolean(target.cli?.prompt),
12887
+ tempDir: options.tempDir
12888
+ });
12168
12889
  return {
12169
12890
  mode,
12170
12891
  prompt,
@@ -12176,7 +12897,8 @@ async function resolveInvocationFromFlags(options) {
12176
12897
  passthrough: {
12177
12898
  hasDelimiter: flags.hasDelimiter,
12178
12899
  args: flags.passthroughArgs
12179
- }
12900
+ },
12901
+ structuredOutput
12180
12902
  };
12181
12903
  }
12182
12904
  async function readStreamText(stream) {
@@ -12195,6 +12917,7 @@ async function runShim(argv, runtime = {}) {
12195
12917
  const stdin = runtime.stdin ?? process.stdin;
12196
12918
  const repoRoot = runtime.repoRoot ?? await findRepoRoot(process.cwd()) ?? process.cwd();
12197
12919
  const stdinIsTTY = runtime.stdinIsTTY ?? stdin.isTTY ?? false;
12920
+ let invocation = null;
12198
12921
  try {
12199
12922
  const flags = parseShimFlags(argv);
12200
12923
  let stdinText = runtime.stdinText ?? null;
@@ -12210,16 +12933,18 @@ async function runShim(argv, runtime = {}) {
12210
12933
  }
12211
12934
  }
12212
12935
  const stdio = !stdinIsTTY && flags.promptExplicit ? ["ignore", "inherit", "inherit"] : "inherit";
12213
- const invocation = await resolveInvocationFromFlags({
12936
+ invocation = await resolveInvocationFromFlags({
12214
12937
  flags,
12215
12938
  stdinIsTTY,
12216
12939
  stdinText,
12217
12940
  repoRoot,
12218
- agentsDir: runtime.agentsDir
12941
+ agentsDir: runtime.agentsDir,
12942
+ tempDir: runtime.tempDir
12219
12943
  });
12220
12944
  const result = await executeInvocation(invocation, {
12221
12945
  spawn: runtime.spawn,
12222
12946
  stderr,
12947
+ stdout: runtime.stdout,
12223
12948
  stdio,
12224
12949
  traceTranslate: flags.traceTranslate
12225
12950
  });
@@ -12234,6 +12959,8 @@ async function runShim(argv, runtime = {}) {
12234
12959
  stderr.write(`Error: ${message}
12235
12960
  `);
12236
12961
  return exitCodeFor("execution-error");
12962
+ } finally {
12963
+ await cleanupStructuredOutput(invocation?.structuredOutput);
12237
12964
  }
12238
12965
  }
12239
12966
  function resolveVersion() {
@@ -12260,11 +12987,13 @@ const VERSION = resolveVersion();
12260
12987
  const KNOWN_COMMANDS = /* @__PURE__ */ new Set(["hello", "greet", "echo", "sync", "dev", "profiles", "usage"]);
12261
12988
  const SHIM_CAPABILITIES = [
12262
12989
  "Capabilities by agent:",
12263
- " codex: approval, sandbox, output, model, web",
12264
- " claude: approval, output, model",
12265
- " gemini: approval, sandbox, output, model, web",
12266
- " copilot: approval, model",
12267
- "Unsupported shared flags for a selected agent emit a warning and are ignored."
12990
+ " codex: approval, sandbox, output, model, web, output-schema",
12991
+ " claude: approval, output, model, output-schema",
12992
+ " agy: approval, sandbox, model, output-schema (fallback) (alias: gemini)",
12993
+ " copilot: approval, model, output-schema (fallback)",
12994
+ "Unsupported shared flags for a selected agent emit a warning and are ignored.",
12995
+ "--output-schema is one-shot only; agents without native support use a prompt-based",
12996
+ "fallback with client-side validation and retries (--output-schema-retries, default 2)."
12268
12997
  ].join("\n");
12269
12998
  function formatError(message, args) {
12270
12999
  if (message.startsWith("Unknown command:")) {
@@ -12345,6 +13074,12 @@ function runCli(argv = process.argv, options = {}) {
12345
13074
  }).option("agent", {
12346
13075
  type: "string",
12347
13076
  describe: "Select the agent (built-in id or configured alias)."
13077
+ }).option("output-schema", {
13078
+ type: "string",
13079
+ describe: "JSON schema file path or inline JSON; emit the final response as schema-conforming JSON (one-shot only)."
13080
+ }).option("output-schema-retries", {
13081
+ type: "number",
13082
+ describe: "Max retries when a prompt-based --output-schema fallback response fails validation (0-10, default 2)."
12348
13083
  }).option("trace-translate", {
12349
13084
  type: "boolean",
12350
13085
  describe: "Emit a JSON line to stderr with the translated agent command/args."