omniagent 0.1.13 → 0.1.16

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: ["weekly"],
1336
+ launch: {
1337
+ command: "agy",
1338
+ timeoutMs: 7e4
1339
+ },
1340
+ extract: async (context) => {
1341
+ const { extractAgyUsage } = await import("./agy-DlbWeyP_.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-CcxRb8OW.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-DhlBosXm.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"
@@ -10554,6 +10701,13 @@ function emptyToNull(value) {
10554
10701
  function escapeRegExp(value) {
10555
10702
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10556
10703
  }
10704
+ class UsageExtractionError extends Error {
10705
+ constructor(code, message) {
10706
+ super(message);
10707
+ this.code = code;
10708
+ this.name = "UsageExtractionError";
10709
+ }
10710
+ }
10557
10711
  const USAGE_BAR_WIDTH = 12;
10558
10712
  const ANSI = {
10559
10713
  reset: "\x1B[0m",
@@ -10588,6 +10742,12 @@ class UsageExtractionTimeoutError extends Error {
10588
10742
  this.timeoutMs = timeoutMs;
10589
10743
  }
10590
10744
  }
10745
+ class UsageCommandInterruptedError extends Error {
10746
+ constructor() {
10747
+ super("Usage cancelled.");
10748
+ this.name = "UsageCommandInterruptedError";
10749
+ }
10750
+ }
10591
10751
  function normalizeOptionalWindow(value) {
10592
10752
  if (value == null) {
10593
10753
  return null;
@@ -10658,6 +10818,68 @@ function uniqueNormalizedWindows(values) {
10658
10818
  }
10659
10819
  return result;
10660
10820
  }
10821
+ class UsageConfirmationPrompter {
10822
+ rl = null;
10823
+ queue = Promise.resolve();
10824
+ closed = false;
10825
+ interrupt;
10826
+ constructor(interrupt) {
10827
+ this.interrupt = interrupt;
10828
+ }
10829
+ confirm = (request, signal) => {
10830
+ const confirmation = this.queue.then(() => this.ask(request, signal));
10831
+ this.queue = confirmation.then(
10832
+ () => void 0,
10833
+ () => void 0
10834
+ );
10835
+ return confirmation;
10836
+ };
10837
+ close() {
10838
+ this.closed = true;
10839
+ this.rl?.close();
10840
+ this.rl = null;
10841
+ }
10842
+ async ask(request, signal) {
10843
+ if (this.closed) {
10844
+ throw new Error("Usage confirmation is no longer available.");
10845
+ }
10846
+ if (signal.aborted) {
10847
+ throw signal.reason instanceof Error ? signal.reason : new Error("Usage confirmation was cancelled.");
10848
+ }
10849
+ const scope = request.managed ? "managed usage directory" : "project directory";
10850
+ const question = `${request.displayName} needs to trust this ${scope}:
10851
+ ${request.path}
10852
+
10853
+ Allow this? [y/N] `;
10854
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
10855
+ this.rl = rl;
10856
+ const handleInterrupt = () => this.interrupt();
10857
+ rl.once("SIGINT", handleInterrupt);
10858
+ try {
10859
+ while (true) {
10860
+ const answer = (await rl.question(question, { signal })).trim().toLowerCase();
10861
+ if (!answer || answer === "n" || answer === "no") {
10862
+ return false;
10863
+ }
10864
+ if (answer === "y" || answer === "yes") {
10865
+ return true;
10866
+ }
10867
+ console.error("Please enter yes or no.");
10868
+ }
10869
+ } catch (error) {
10870
+ if (signal.aborted && signal.reason instanceof Error) {
10871
+ throw signal.reason;
10872
+ }
10873
+ throw error;
10874
+ } finally {
10875
+ rl.removeListener("SIGINT", handleInterrupt);
10876
+ rl.close();
10877
+ if (this.rl === rl) {
10878
+ this.rl = null;
10879
+ }
10880
+ }
10881
+ }
10882
+ }
10661
10883
  function formatUsageTargetLabel(targets) {
10662
10884
  return buildSupportedTargetLabel(targets);
10663
10885
  }
@@ -10668,6 +10890,13 @@ function formatSupportedUsageTargetsMessage(targets) {
10668
10890
  function getUsageCommand(target) {
10669
10891
  return target.usage?.launch?.command;
10670
10892
  }
10893
+ function supportsUsageConfirmation(target) {
10894
+ if (target.id.trim().toLowerCase() === "agy") {
10895
+ return true;
10896
+ }
10897
+ const builtInAgyExtractor = BUILTIN_TARGETS.find((candidate) => candidate.id === "agy")?.usage?.extract;
10898
+ return builtInAgyExtractor != null && target.usage?.extract === builtInAgyExtractor;
10899
+ }
10671
10900
  async function checkUsageCommandAvailability(target) {
10672
10901
  const command = getUsageCommand(target)?.trim();
10673
10902
  if (!command) {
@@ -10725,6 +10954,7 @@ function validateCodexCommand(candidate) {
10725
10954
  }
10726
10955
  function buildContext(options) {
10727
10956
  const windows = uniqueNormalizedWindows(options.target.usage?.windows ?? []);
10957
+ const confirm = options.confirm;
10728
10958
  const launch = {
10729
10959
  ...options.target.usage?.launch ?? {},
10730
10960
  timeoutMs: options.timeoutMs
@@ -10741,6 +10971,7 @@ function buildContext(options) {
10741
10971
  homeDir: options.homeDir,
10742
10972
  launch,
10743
10973
  signal: options.signal,
10974
+ confirm: confirm == null ? void 0 : (request) => confirm(request, options.signal),
10744
10975
  debug: {
10745
10976
  enabled: options.debug,
10746
10977
  includeRawOutput: options.debug,
@@ -10754,7 +10985,11 @@ function filterTargetResult(target, result, selectedWindow) {
10754
10985
  window: normalizeUsageWindow(limit.window)
10755
10986
  }));
10756
10987
  const filteredLimits = selectedWindow == null ? normalizedLimits : normalizedLimits.filter((limit) => limit.window === selectedWindow);
10757
- const notes = selectedWindow != null && filteredLimits.length === 0 ? [`${target.displayName} reported no usage rows for window "${selectedWindow}".`] : [];
10988
+ const resultNotes = result.notes ?? [];
10989
+ const notes = selectedWindow != null && filteredLimits.length === 0 ? [
10990
+ ...resultNotes,
10991
+ `${target.displayName} reported no usage rows for window "${selectedWindow}".`
10992
+ ] : resultNotes;
10758
10993
  return {
10759
10994
  result: {
10760
10995
  targetId: result.targetId,
@@ -10789,10 +11024,14 @@ async function extractUsageForTarget(options) {
10789
11024
  debug: []
10790
11025
  };
10791
11026
  }
10792
- const result = await withUsageTimeout((signal) => {
10793
- const context = buildContext({ ...options, signal });
10794
- return extractor(context);
10795
- }, options.timeoutMs);
11027
+ const result = await withUsageTimeout(
11028
+ (signal) => {
11029
+ const context = buildContext({ ...options, signal });
11030
+ return extractor(context);
11031
+ },
11032
+ options.timeoutMs,
11033
+ options.commandSignal
11034
+ );
10796
11035
  const filtered = filterTargetResult(options.target, result, options.selectedWindow);
10797
11036
  return {
10798
11037
  status: "success",
@@ -10804,7 +11043,7 @@ async function extractUsageForTarget(options) {
10804
11043
  };
10805
11044
  } catch (error) {
10806
11045
  const message = error instanceof Error ? error.message : String(error);
10807
- const code = isUsageTimeoutError(error) ? "usage_extraction_timeout" : "usage_extraction_failed";
11046
+ const code = isUsageTimeoutError(error) ? "usage_extraction_timeout" : getUsageExtractionErrorCode(error);
10808
11047
  return {
10809
11048
  status: "error",
10810
11049
  target: options.target,
@@ -10813,6 +11052,21 @@ async function extractUsageForTarget(options) {
10813
11052
  };
10814
11053
  }
10815
11054
  }
11055
+ function getUsageExtractionErrorCode(error) {
11056
+ if (error instanceof UsageExtractionError) {
11057
+ return error.code;
11058
+ }
11059
+ if (error && typeof error === "object") {
11060
+ const code = error.code;
11061
+ if (typeof code === "string") {
11062
+ const normalized = code.trim();
11063
+ if (/^[a-z][a-z0-9_]*$/.test(normalized)) {
11064
+ return normalized;
11065
+ }
11066
+ }
11067
+ }
11068
+ return "usage_extraction_failed";
11069
+ }
10816
11070
  function isUsageTimeoutError(error) {
10817
11071
  if (error instanceof UsageExtractionTimeoutError) {
10818
11072
  return true;
@@ -10836,18 +11090,25 @@ function isDebugArtifact(value) {
10836
11090
  const artifact = value;
10837
11091
  return (artifact.type === "raw-output" || artifact.type === "screen-snapshot") && typeof artifact.label === "string";
10838
11092
  }
10839
- function withUsageTimeout(run, timeoutMs) {
11093
+ function withUsageTimeout(run, timeoutMs, commandSignal) {
10840
11094
  return new Promise((resolve, reject) => {
10841
11095
  const controller = new AbortController();
10842
11096
  let settled = false;
10843
11097
  let timeoutFired = false;
10844
- let timeout;
11098
+ let timeout = null;
11099
+ let removeCommandAbortListener = null;
11100
+ const cleanup = () => {
11101
+ if (timeout != null) {
11102
+ clearTimeout(timeout);
11103
+ }
11104
+ removeCommandAbortListener?.();
11105
+ };
10845
11106
  const settleResolve = (value) => {
10846
11107
  if (settled) {
10847
11108
  return;
10848
11109
  }
10849
11110
  settled = true;
10850
- clearTimeout(timeout);
11111
+ cleanup();
10851
11112
  resolve(value);
10852
11113
  };
10853
11114
  const settleReject = (error) => {
@@ -10855,7 +11116,7 @@ function withUsageTimeout(run, timeoutMs) {
10855
11116
  return;
10856
11117
  }
10857
11118
  settled = true;
10858
- clearTimeout(timeout);
11119
+ cleanup();
10859
11120
  reject(error);
10860
11121
  };
10861
11122
  timeout = setTimeout(() => {
@@ -10866,6 +11127,19 @@ function withUsageTimeout(run, timeoutMs) {
10866
11127
  settleReject(error);
10867
11128
  });
10868
11129
  }, timeoutMs);
11130
+ if (commandSignal) {
11131
+ const abortFromCommand = () => {
11132
+ const reason = commandSignal.reason instanceof Error ? commandSignal.reason : new UsageCommandInterruptedError();
11133
+ controller.abort(reason);
11134
+ settleReject(reason);
11135
+ };
11136
+ if (commandSignal.aborted) {
11137
+ abortFromCommand();
11138
+ return;
11139
+ }
11140
+ commandSignal.addEventListener("abort", abortFromCommand, { once: true });
11141
+ removeCommandAbortListener = () => commandSignal.removeEventListener("abort", abortFromCommand);
11142
+ }
10869
11143
  let promise;
10870
11144
  try {
10871
11145
  promise = run(controller.signal);
@@ -11142,6 +11416,9 @@ function leftCellText(row) {
11142
11416
  if (row.status === "error") {
11143
11417
  return "-";
11144
11418
  }
11419
+ if (row.limit.percentRemaining == null && row.limit.remainingText) {
11420
+ return row.limit.remainingText;
11421
+ }
11145
11422
  return percentText(row.limit.percentRemaining);
11146
11423
  }
11147
11424
  function resetCellText(row) {
@@ -11481,9 +11758,17 @@ async function runUsageCommand(argv) {
11481
11758
  }
11482
11759
  }
11483
11760
  const now = /* @__PURE__ */ new Date();
11484
- const outcomes = await Promise.all(
11485
- selectedTargets.map(
11486
- (target) => extractUsageForTarget({
11761
+ const commandController = new AbortController();
11762
+ const confirmationPrompter = !jsonOutput && Boolean(process.stdin.isTTY) && Boolean(process.stderr.isTTY) ? new UsageConfirmationPrompter(() => {
11763
+ if (!commandController.signal.aborted) {
11764
+ commandController.abort(new UsageCommandInterruptedError());
11765
+ }
11766
+ }) : null;
11767
+ let outcomes;
11768
+ try {
11769
+ let confirmationTargetChain = Promise.resolve();
11770
+ const outcomePromises = selectedTargets.map((target) => {
11771
+ const extract = () => extractUsageForTarget({
11487
11772
  target,
11488
11773
  repoRoot,
11489
11774
  agentsDir,
@@ -11492,10 +11777,29 @@ async function runUsageCommand(argv) {
11492
11777
  timeoutMs: resolveTargetTimeoutMs(target, cliTimeoutMs),
11493
11778
  debug: debugOutput,
11494
11779
  now,
11495
- command: resolvedUsageCommands.get(target.id)
11496
- })
11497
- )
11498
- );
11780
+ command: resolvedUsageCommands.get(target.id),
11781
+ confirm: supportsUsageConfirmation(target) ? confirmationPrompter?.confirm : void 0,
11782
+ commandSignal: commandController.signal
11783
+ });
11784
+ if (confirmationPrompter == null || !supportsUsageConfirmation(target)) {
11785
+ return extract();
11786
+ }
11787
+ const outcome = confirmationTargetChain.then(extract);
11788
+ confirmationTargetChain = outcome.then(
11789
+ () => void 0,
11790
+ () => void 0
11791
+ );
11792
+ return outcome;
11793
+ });
11794
+ outcomes = await Promise.all(outcomePromises);
11795
+ } finally {
11796
+ confirmationPrompter?.close();
11797
+ }
11798
+ if (commandController.signal.reason instanceof UsageCommandInterruptedError) {
11799
+ console.error(commandController.signal.reason.message);
11800
+ process.exit(130);
11801
+ return null;
11802
+ }
11499
11803
  const targets = [];
11500
11804
  const errors = [];
11501
11805
  const notes = [];
@@ -11581,7 +11885,7 @@ const usageCommand = {
11581
11885
  type: "boolean",
11582
11886
  describe: "Print JSON and include extractor debug artifacts when available."
11583
11887
  }).epilog(
11584
- "Usage extraction may launch agent TUIs and may incur cost if an agent reads repo context on startup. omniagent uses cheap/minimal launch settings where possible."
11888
+ "Usage extraction may launch agent TUIs and may incur cost if an agent reads repo context on startup. Interactive runs may ask before forwarding directory trust; JSON, debug, and non-interactive runs never prompt."
11585
11889
  ),
11586
11890
  handler: async (argv) => {
11587
11891
  await runUsageCommand(argv);
@@ -11708,6 +12012,9 @@ function translateInvocation(invocation, cli, options = {}) {
11708
12012
  args.push(...mapped);
11709
12013
  }
11710
12014
  }
12015
+ if (invocation.structuredOutput) {
12016
+ args.push(...invocation.structuredOutput.args);
12017
+ }
11711
12018
  if (mode === "one-shot" && base.args?.includes("exec")) {
11712
12019
  const searchIndex = args.indexOf("--search");
11713
12020
  if (searchIndex > -1) {
@@ -11765,9 +12072,344 @@ function buildAgentArgs(invocation) {
11765
12072
  warnings: translated.warnings
11766
12073
  };
11767
12074
  }
12075
+ const PREVIOUS_OUTPUT_LIMIT = 4e3;
12076
+ const RESPONSE_RULES = "Respond with only the JSON value - no explanations, no markdown code fences, no additional text.";
12077
+ function schemaBlock(schemaJson) {
12078
+ return `Your entire response must be a single JSON value that conforms to this JSON Schema:
12079
+ ${schemaJson}`;
12080
+ }
12081
+ function truncate(text, limit) {
12082
+ if (text.length <= limit) {
12083
+ return text;
12084
+ }
12085
+ return `${text.slice(0, limit)}
12086
+ [truncated]`;
12087
+ }
12088
+ function buildFallbackPrompt(prompt, schemaJson) {
12089
+ const base = prompt.trim().length > 0 ? `${prompt}
12090
+
12091
+ ` : "";
12092
+ return `${base}${schemaBlock(schemaJson)}
12093
+
12094
+ ${RESPONSE_RULES}`;
12095
+ }
12096
+ function buildRetryPrompt(prompt, schemaJson, previousOutput, errors) {
12097
+ const base = prompt.trim().length > 0 ? `${prompt}
12098
+
12099
+ ` : "";
12100
+ const previous = previousOutput.trim().length > 0 ? truncate(previousOutput.trim(), PREVIOUS_OUTPUT_LIMIT) : "(empty response)";
12101
+ const errorLines = errors.map((error) => `- ${error}`).join("\n");
12102
+ return [
12103
+ `${base}${schemaBlock(schemaJson)}`,
12104
+ `Your previous response failed validation.
12105
+
12106
+ Previous response:
12107
+ ${previous}`,
12108
+ `Validation errors:
12109
+ ${errorLines}`,
12110
+ `Respond again with only the corrected JSON value. ${RESPONSE_RULES}`
12111
+ ].join("\n\n");
12112
+ }
12113
+ function tryParse(text) {
12114
+ try {
12115
+ return { ok: true, value: JSON.parse(text) };
12116
+ } catch {
12117
+ return null;
12118
+ }
12119
+ }
12120
+ function extractJsonPayload(text) {
12121
+ const trimmed = text.trim();
12122
+ if (trimmed.length === 0) {
12123
+ return { ok: false, error: "the response was empty" };
12124
+ }
12125
+ const whole = tryParse(trimmed);
12126
+ if (whole) {
12127
+ return whole;
12128
+ }
12129
+ const fenced = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
12130
+ if (fenced?.[1]) {
12131
+ const parsed = tryParse(fenced[1].trim());
12132
+ if (parsed) {
12133
+ return parsed;
12134
+ }
12135
+ }
12136
+ for (const [open, close] of [
12137
+ ["{", "}"],
12138
+ ["[", "]"]
12139
+ ]) {
12140
+ const start = trimmed.indexOf(open);
12141
+ const end = trimmed.lastIndexOf(close);
12142
+ if (start !== -1 && end > start) {
12143
+ const parsed = tryParse(trimmed.slice(start, end + 1));
12144
+ if (parsed) {
12145
+ return parsed;
12146
+ }
12147
+ }
12148
+ }
12149
+ return { ok: false, error: "the response did not contain parseable JSON" };
12150
+ }
12151
+ function withPipedStdout(stdio) {
12152
+ if (Array.isArray(stdio)) {
12153
+ return [stdio[0] ?? "inherit", "pipe", stdio[2] ?? "inherit"];
12154
+ }
12155
+ return [stdio, "pipe", stdio];
12156
+ }
12157
+ function mapExitCode(code) {
12158
+ if (code === 0) {
12159
+ return { exitCode: 0, reason: "success" };
12160
+ }
12161
+ if (code === 2) {
12162
+ return { exitCode: 2, reason: "invalid-usage" };
12163
+ }
12164
+ if (code === 3) {
12165
+ return { exitCode: 3, reason: "blocked" };
12166
+ }
12167
+ return { exitCode: 1, reason: "execution-error" };
12168
+ }
12169
+ async function finalizeStructuredOutput(options) {
12170
+ const { plan, agentId, code, captured, stdout, stderr } = options;
12171
+ if (code !== 0) {
12172
+ if (plan.capture.type === "json-envelope" && captured.length > 0) {
12173
+ stderr.write(captured);
12174
+ }
12175
+ return mapExitCode(code);
12176
+ }
12177
+ if (plan.capture.type === "json-envelope") {
12178
+ let envelope;
12179
+ try {
12180
+ envelope = JSON.parse(captured);
12181
+ } catch {
12182
+ envelope = void 0;
12183
+ }
12184
+ if (typeof envelope !== "object" || envelope === null || Array.isArray(envelope)) {
12185
+ stderr.write(captured);
12186
+ stderr.write(`Error: ${agentId} did not return a JSON envelope.
12187
+ `);
12188
+ return { exitCode: 1, reason: "execution-error" };
12189
+ }
12190
+ const record = envelope;
12191
+ if (record.is_error === true) {
12192
+ stderr.write(captured);
12193
+ stderr.write(`Error: ${agentId} reported an error result.
12194
+ `);
12195
+ return { exitCode: 1, reason: "execution-error" };
12196
+ }
12197
+ const payload = record[plan.capture.field];
12198
+ if (payload === void 0 || payload === null) {
12199
+ stderr.write(captured);
12200
+ stderr.write(`Error: ${agentId} response is missing ${plan.capture.field}.
12201
+ `);
12202
+ return { exitCode: 1, reason: "execution-error" };
12203
+ }
12204
+ stdout.write(`${JSON.stringify(payload)}
12205
+ `);
12206
+ return { exitCode: 0, reason: "success" };
12207
+ }
12208
+ if (plan.capture.type === "fallback") {
12209
+ return { exitCode: 1, reason: "execution-error" };
12210
+ }
12211
+ let contents;
12212
+ try {
12213
+ contents = await readFile(plan.capture.path, "utf8");
12214
+ } catch {
12215
+ contents = "";
12216
+ }
12217
+ const trimmed = contents.trim();
12218
+ if (!trimmed) {
12219
+ stderr.write(`Error: ${agentId} did not produce a structured output message.
12220
+ `);
12221
+ return { exitCode: 1, reason: "execution-error" };
12222
+ }
12223
+ stdout.write(`${trimmed}
12224
+ `);
12225
+ return { exitCode: 0, reason: "success" };
12226
+ }
12227
+ function runCapturedAttempt(params) {
12228
+ return new Promise((resolve) => {
12229
+ let settled = false;
12230
+ const settle = (value) => {
12231
+ if (!settled) {
12232
+ settled = true;
12233
+ resolve(value);
12234
+ }
12235
+ };
12236
+ const child = params.spawn(params.command, params.args, { stdio: params.stdio });
12237
+ const chunks = [];
12238
+ const childStdout = child.stdout;
12239
+ if (childStdout) {
12240
+ childStdout.on("data", (chunk) => {
12241
+ chunks.push(Buffer.from(chunk));
12242
+ });
12243
+ }
12244
+ child.on("error", (error) => {
12245
+ params.stderr.write(`Error: ${error.message}
12246
+ `);
12247
+ settle({ kind: "spawn-error" });
12248
+ });
12249
+ child.on("close", (code) => {
12250
+ if (!childStdout) {
12251
+ params.stderr.write(`Error: ${params.agentId} stdout was not captured.
12252
+ `);
12253
+ settle({ kind: "spawn-error" });
12254
+ return;
12255
+ }
12256
+ settle({ kind: "closed", code, captured: Buffer.concat(chunks).toString("utf8") });
12257
+ });
12258
+ });
12259
+ }
12260
+ function extractEnvelopeText(params) {
12261
+ const { captured, field, agentId, stderr } = params;
12262
+ let envelope;
12263
+ try {
12264
+ envelope = JSON.parse(captured);
12265
+ } catch {
12266
+ envelope = void 0;
12267
+ }
12268
+ if (typeof envelope !== "object" || envelope === null || Array.isArray(envelope)) {
12269
+ if (captured.length > 0) {
12270
+ stderr.write(captured);
12271
+ }
12272
+ stderr.write(`Error: ${agentId} did not return a JSON envelope.
12273
+ `);
12274
+ return { ok: false };
12275
+ }
12276
+ const record = envelope;
12277
+ if (record.error !== void 0 && record.error !== null) {
12278
+ stderr.write(captured);
12279
+ stderr.write(`Error: ${agentId} reported an error result.
12280
+ `);
12281
+ return { ok: false };
12282
+ }
12283
+ const payload = record[field];
12284
+ if (payload === void 0 || payload === null) {
12285
+ stderr.write(captured);
12286
+ stderr.write(`Error: ${agentId} response is missing ${field}.
12287
+ `);
12288
+ return { ok: false };
12289
+ }
12290
+ return { ok: true, text: typeof payload === "string" ? payload : JSON.stringify(payload) };
12291
+ }
12292
+ async function runFallbackAttempts(params) {
12293
+ const { invocation, plan, capture, options, stdout, stderr } = params;
12294
+ const agentId = invocation.agent.id;
12295
+ const spawn$1 = options.spawn ?? spawn;
12296
+ const stdio = withPipedStdout(options.stdio ?? "inherit");
12297
+ const originalPrompt = invocation.prompt ?? "";
12298
+ const validate = plan.validate ?? (() => ({ valid: true, errors: [] }));
12299
+ let feedback = null;
12300
+ for (let attempt = 1; attempt <= capture.maxAttempts; attempt += 1) {
12301
+ const attemptPrompt = feedback ? buildRetryPrompt(originalPrompt, plan.schemaJson, feedback.output, feedback.errors) : buildFallbackPrompt(originalPrompt, plan.schemaJson);
12302
+ const built = buildAgentArgs({ ...invocation, prompt: attemptPrompt });
12303
+ if (attempt === 1) {
12304
+ if (options.traceTranslate) {
12305
+ const trace = {
12306
+ agent: agentId,
12307
+ mode: invocation.mode,
12308
+ command: built.command,
12309
+ args: built.args,
12310
+ shimArgs: built.shimArgs,
12311
+ passthroughArgs: built.passthroughArgs,
12312
+ warnings: built.warnings,
12313
+ requests: invocation.requests,
12314
+ structuredOutput: { capture: capture.type, maxAttempts: capture.maxAttempts }
12315
+ };
12316
+ stderr.write(`OA_TRANSLATION=${JSON.stringify(trace)}
12317
+ `);
12318
+ }
12319
+ for (const warning of built.warnings) {
12320
+ stderr.write(`${warning}
12321
+ `);
12322
+ }
12323
+ for (const notice of plan.notices ?? []) {
12324
+ stderr.write(`${notice}
12325
+ `);
12326
+ }
12327
+ }
12328
+ const attemptResult = await runCapturedAttempt({
12329
+ spawn: spawn$1,
12330
+ command: built.command,
12331
+ args: built.args,
12332
+ stdio,
12333
+ agentId,
12334
+ stderr
12335
+ });
12336
+ if (attemptResult.kind === "spawn-error") {
12337
+ return { exitCode: 1, reason: "execution-error" };
12338
+ }
12339
+ if (attemptResult.code !== 0) {
12340
+ if (attemptResult.captured.length > 0) {
12341
+ stderr.write(attemptResult.captured);
12342
+ }
12343
+ return mapExitCode(attemptResult.code);
12344
+ }
12345
+ let responseText = attemptResult.captured;
12346
+ if (capture.extraction.type === "json-envelope") {
12347
+ const outcome = extractEnvelopeText({
12348
+ captured: attemptResult.captured,
12349
+ field: capture.extraction.field,
12350
+ agentId,
12351
+ stderr
12352
+ });
12353
+ if (!outcome.ok) {
12354
+ return { exitCode: 1, reason: "execution-error" };
12355
+ }
12356
+ responseText = outcome.text;
12357
+ }
12358
+ const extracted = extractJsonPayload(responseText);
12359
+ if (extracted.ok) {
12360
+ const result = validate(extracted.value);
12361
+ if (result.valid) {
12362
+ stdout.write(`${JSON.stringify(extracted.value)}
12363
+ `);
12364
+ return { exitCode: 0, reason: "success" };
12365
+ }
12366
+ feedback = { output: responseText, errors: result.errors };
12367
+ } else {
12368
+ feedback = { output: responseText, errors: [extracted.error] };
12369
+ }
12370
+ if (attempt < capture.maxAttempts) {
12371
+ stderr.write(
12372
+ `Attempt ${attempt} failed schema validation; retrying (${attempt + 1}/${capture.maxAttempts}).
12373
+ `
12374
+ );
12375
+ for (const error of feedback.errors) {
12376
+ stderr.write(`- ${error}
12377
+ `);
12378
+ }
12379
+ }
12380
+ }
12381
+ if (feedback) {
12382
+ const lastOutput = feedback.output.trim();
12383
+ if (lastOutput.length > 0) {
12384
+ stderr.write(`${lastOutput}
12385
+ `);
12386
+ }
12387
+ for (const error of feedback.errors) {
12388
+ stderr.write(`- ${error}
12389
+ `);
12390
+ }
12391
+ }
12392
+ stderr.write(
12393
+ `Error: ${agentId} response failed schema validation after ${capture.maxAttempts} attempts.
12394
+ `
12395
+ );
12396
+ return { exitCode: 1, reason: "execution-error" };
12397
+ }
11768
12398
  async function executeInvocation(invocation, options = {}) {
11769
- const { command, args, warnings, shimArgs, passthroughArgs } = buildAgentArgs(invocation);
11770
12399
  const stderr = options.stderr ?? process.stderr;
12400
+ const stdout = options.stdout ?? process.stdout;
12401
+ const plan = invocation.structuredOutput;
12402
+ if (plan && plan.capture.type === "fallback") {
12403
+ return runFallbackAttempts({
12404
+ invocation,
12405
+ plan,
12406
+ capture: plan.capture,
12407
+ options,
12408
+ stdout,
12409
+ stderr
12410
+ });
12411
+ }
12412
+ const { command, args, warnings, shimArgs, passthroughArgs } = buildAgentArgs(invocation);
11771
12413
  if (options.traceTranslate) {
11772
12414
  const trace = {
11773
12415
  agent: invocation.agent.id,
@@ -11777,17 +12419,23 @@ async function executeInvocation(invocation, options = {}) {
11777
12419
  shimArgs,
11778
12420
  passthroughArgs,
11779
12421
  warnings,
11780
- requests: invocation.requests
12422
+ requests: invocation.requests,
12423
+ ...plan ? { structuredOutput: { capture: plan.capture.type } } : {}
11781
12424
  };
11782
12425
  stderr.write(`OA_TRANSLATION=${JSON.stringify(trace)}
11783
12426
  `);
11784
12427
  }
11785
12428
  for (const warning of warnings) {
11786
12429
  stderr.write(`${warning}
12430
+ `);
12431
+ }
12432
+ for (const notice of plan?.notices ?? []) {
12433
+ stderr.write(`${notice}
11787
12434
  `);
11788
12435
  }
11789
12436
  const spawn$1 = options.spawn ?? spawn;
11790
- const stdio = options.stdio ?? "inherit";
12437
+ const baseStdio = options.stdio ?? "inherit";
12438
+ const stdio = plan ? withPipedStdout(baseStdio) : baseStdio;
11791
12439
  return await new Promise((resolve) => {
11792
12440
  const child = spawn$1(command, args, { stdio });
11793
12441
  child.on("error", (error) => {
@@ -11795,20 +12443,40 @@ async function executeInvocation(invocation, options = {}) {
11795
12443
  `);
11796
12444
  resolve({ exitCode: 1, reason: "execution-error" });
11797
12445
  });
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;
12446
+ if (!plan) {
12447
+ child.on("exit", (code) => {
12448
+ resolve(mapExitCode(code));
12449
+ });
12450
+ return;
12451
+ }
12452
+ const chunks = [];
12453
+ const childStdout = child.stdout;
12454
+ if (childStdout) {
12455
+ if (plan.capture.type === "json-envelope") {
12456
+ childStdout.on("data", (chunk) => {
12457
+ chunks.push(Buffer.from(chunk));
12458
+ });
12459
+ } else {
12460
+ childStdout.on("data", (chunk) => {
12461
+ stderr.write(chunk);
12462
+ });
11806
12463
  }
11807
- if (code === 3) {
11808
- resolve({ exitCode: 3, reason: "blocked" });
12464
+ }
12465
+ child.on("close", (code) => {
12466
+ if (!childStdout) {
12467
+ stderr.write(`Error: ${invocation.agent.id} stdout was not captured.
12468
+ `);
12469
+ resolve({ exitCode: 1, reason: "execution-error" });
11809
12470
  return;
11810
12471
  }
11811
- resolve({ exitCode: 1, reason: "execution-error" });
12472
+ void finalizeStructuredOutput({
12473
+ plan,
12474
+ agentId: invocation.agent.id,
12475
+ code,
12476
+ captured: Buffer.concat(chunks).toString("utf8"),
12477
+ stdout,
12478
+ stderr
12479
+ }).then(resolve);
11812
12480
  });
11813
12481
  });
11814
12482
  }
@@ -11846,6 +12514,16 @@ function readFlagValue(args, index, label) {
11846
12514
  }
11847
12515
  return [value, index + 1];
11848
12516
  }
12517
+ function parseRetriesValue(value) {
12518
+ const normalized = normalizeValue(value, "--output-schema-retries");
12519
+ const parsed = Number(normalized);
12520
+ if (!Number.isInteger(parsed) || parsed < 0 || parsed > 10) {
12521
+ throw new InvalidUsageError(
12522
+ "Invalid value for --output-schema-retries. Provide an integer between 0 and 10."
12523
+ );
12524
+ }
12525
+ return parsed;
12526
+ }
11849
12527
  function parseBooleanValue(label, value) {
11850
12528
  const normalized = normalizeValue(value, label).toLowerCase();
11851
12529
  if (["on", "true", "1"].includes(normalized)) {
@@ -11874,6 +12552,9 @@ function parseShimFlags(argv) {
11874
12552
  let webExplicit = false;
11875
12553
  let agent = null;
11876
12554
  let agentExplicit = false;
12555
+ let outputSchema = null;
12556
+ let outputSchemaExplicit = false;
12557
+ let outputSchemaRetries = null;
11877
12558
  let traceTranslate = false;
11878
12559
  let help = false;
11879
12560
  let version = false;
@@ -12011,6 +12692,28 @@ function parseShimFlags(argv) {
12011
12692
  agentExplicit = true;
12012
12693
  continue;
12013
12694
  }
12695
+ if (arg === "--output-schema-retries") {
12696
+ const [value, nextIndex] = readFlagValue(preArgs, index, "--output-schema-retries");
12697
+ outputSchemaRetries = parseRetriesValue(value);
12698
+ index = nextIndex;
12699
+ continue;
12700
+ }
12701
+ if (arg.startsWith("--output-schema-retries=")) {
12702
+ outputSchemaRetries = parseRetriesValue(arg.slice("--output-schema-retries=".length));
12703
+ continue;
12704
+ }
12705
+ if (arg === "--output-schema") {
12706
+ const [value, nextIndex] = readFlagValue(preArgs, index, "--output-schema");
12707
+ outputSchema = normalizeValue(value, "--output-schema");
12708
+ outputSchemaExplicit = true;
12709
+ index = nextIndex;
12710
+ continue;
12711
+ }
12712
+ if (arg.startsWith("--output-schema=")) {
12713
+ outputSchema = normalizeValue(arg.slice("--output-schema=".length), "--output-schema");
12714
+ outputSchemaExplicit = true;
12715
+ continue;
12716
+ }
12014
12717
  if (arg === "--trace-translate") {
12015
12718
  traceTranslate = true;
12016
12719
  continue;
@@ -12029,6 +12732,14 @@ function parseShimFlags(argv) {
12029
12732
  if (outputSelections.length > 0) {
12030
12733
  output = outputSelections[outputSelections.length - 1];
12031
12734
  }
12735
+ if (outputSchema !== null && outputExplicit) {
12736
+ throw new InvalidUsageError(
12737
+ "--output-schema cannot be combined with --output, --json, or --stream-json."
12738
+ );
12739
+ }
12740
+ if (outputSchemaRetries !== null && outputSchema === null) {
12741
+ throw new InvalidUsageError("--output-schema-retries requires --output-schema.");
12742
+ }
12032
12743
  if (approval === "yolo" && !sandboxExplicit) {
12033
12744
  sandbox = "off";
12034
12745
  }
@@ -12047,6 +12758,9 @@ function parseShimFlags(argv) {
12047
12758
  webExplicit,
12048
12759
  agent,
12049
12760
  agentExplicit,
12761
+ outputSchema,
12762
+ outputSchemaExplicit,
12763
+ outputSchemaRetries,
12050
12764
  traceTranslate,
12051
12765
  help,
12052
12766
  version,
@@ -12145,6 +12859,157 @@ async function resolveAgentSelection(flags, repoRoot, agentsDir) {
12145
12859
  }
12146
12860
  return { resolution: { selection, targetId: resolvedId }, targetMap };
12147
12861
  }
12862
+ const AJV_OPTIONS = { allErrors: true, strict: false, validateFormats: false };
12863
+ const instances = /* @__PURE__ */ new Map();
12864
+ function resolveDialect(schema) {
12865
+ const declared = typeof schema.$schema === "string" ? schema.$schema : "";
12866
+ if (declared.includes("2020-12")) {
12867
+ return "2020-12";
12868
+ }
12869
+ if (declared.includes("2019-09")) {
12870
+ return "2019-09";
12871
+ }
12872
+ return "draft-07";
12873
+ }
12874
+ function ajvForDialect(dialect) {
12875
+ let instance = instances.get(dialect);
12876
+ if (!instance) {
12877
+ instance = dialect === "2020-12" ? new Ajv2020(AJV_OPTIONS) : dialect === "2019-09" ? new Ajv2019(AJV_OPTIONS) : new Ajv(AJV_OPTIONS);
12878
+ instances.set(dialect, instance);
12879
+ }
12880
+ return instance;
12881
+ }
12882
+ function compileSchemaValidator(schemaJson) {
12883
+ let compiled;
12884
+ try {
12885
+ const schema = JSON.parse(schemaJson);
12886
+ compiled = ajvForDialect(resolveDialect(schema)).compile(schema);
12887
+ } catch (error) {
12888
+ const message = error instanceof Error ? error.message : String(error);
12889
+ throw new InvalidUsageError(`Invalid value for --output-schema: ${message}.`);
12890
+ }
12891
+ return (data) => {
12892
+ const valid = compiled(data) === true;
12893
+ const errors = (compiled.errors ?? []).map(
12894
+ (error) => `${error.instancePath || "/"}: ${error.message ?? "is invalid"}`
12895
+ );
12896
+ return { valid, errors };
12897
+ };
12898
+ }
12899
+ const SCHEMA_LABEL = "--output-schema";
12900
+ const DEFAULT_SCHEMA_RETRIES = 2;
12901
+ function isPlainObject(value) {
12902
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12903
+ }
12904
+ function parseSchemaJson(text, sourceLabel) {
12905
+ let parsed;
12906
+ try {
12907
+ parsed = JSON.parse(text);
12908
+ } catch {
12909
+ throw new InvalidUsageError(`Invalid value for ${SCHEMA_LABEL}: ${sourceLabel}.`);
12910
+ }
12911
+ if (!isPlainObject(parsed)) {
12912
+ throw new InvalidUsageError(`Invalid value for ${SCHEMA_LABEL}: schema must be a JSON object.`);
12913
+ }
12914
+ return JSON.stringify(parsed);
12915
+ }
12916
+ async function resolveOutputSchema(raw) {
12917
+ const trimmed = raw.trim();
12918
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
12919
+ return parseSchemaJson(trimmed, "schema is not valid JSON");
12920
+ }
12921
+ let contents;
12922
+ try {
12923
+ contents = await readFile(trimmed, "utf8");
12924
+ } catch {
12925
+ throw new InvalidUsageError(
12926
+ `Invalid value for ${SCHEMA_LABEL}: cannot read schema file ${trimmed}.`
12927
+ );
12928
+ }
12929
+ return parseSchemaJson(contents, `schema file ${trimmed} is not valid JSON`);
12930
+ }
12931
+ async function planStructuredOutput(options) {
12932
+ const { rawSchema, mode, agentId, spec } = options;
12933
+ if (rawSchema === null) {
12934
+ return null;
12935
+ }
12936
+ if (mode !== "one-shot") {
12937
+ throw new InvalidUsageError(
12938
+ `${SCHEMA_LABEL} requires one-shot mode; provide -p/--prompt or pipe stdin.`
12939
+ );
12940
+ }
12941
+ if (!spec) {
12942
+ return planFallback(options);
12943
+ }
12944
+ const notices = [];
12945
+ if (options.retries !== null && options.retries !== void 0) {
12946
+ notices.push(
12947
+ `Warning: ${agentId} uses native ${SCHEMA_LABEL} support; ignoring ${SCHEMA_LABEL}-retries.`
12948
+ );
12949
+ }
12950
+ const schemaJson = await resolveOutputSchema(rawSchema);
12951
+ const needsTempDir = spec.delivery === "file" || spec.extraction.type === "last-message-file";
12952
+ const tempPaths = [];
12953
+ let tempDirPath = null;
12954
+ if (needsTempDir) {
12955
+ tempDirPath = await mkdtemp(path.join(options.tempDir ?? os.tmpdir(), "omniagent-schema-"));
12956
+ tempPaths.push(tempDirPath);
12957
+ }
12958
+ let deliveryValue = schemaJson;
12959
+ if (spec.delivery === "file" && tempDirPath) {
12960
+ deliveryValue = path.join(tempDirPath, "schema.json");
12961
+ await writeFile(deliveryValue, schemaJson, "utf8");
12962
+ }
12963
+ const args = [...spec.flag, deliveryValue, ...spec.companionArgs ?? []];
12964
+ let capture;
12965
+ if (spec.extraction.type === "json-envelope") {
12966
+ capture = { type: "json-envelope", field: spec.extraction.field };
12967
+ } else {
12968
+ const lastMessagePath = path.join(tempDirPath ?? os.tmpdir(), "last-message.txt");
12969
+ args.push(...spec.extraction.flag, lastMessagePath);
12970
+ capture = { type: "last-message-file", path: lastMessagePath };
12971
+ }
12972
+ return { schemaJson, args, capture, tempPaths, notices };
12973
+ }
12974
+ async function planFallback(options) {
12975
+ const { rawSchema, agentId, fallbackSpec } = options;
12976
+ if (rawSchema === null) {
12977
+ return null;
12978
+ }
12979
+ if (options.promptDeliverable === false) {
12980
+ throw new InvalidUsageError(
12981
+ `${agentId} cannot use the ${SCHEMA_LABEL} fallback: target defines no prompt flag.`
12982
+ );
12983
+ }
12984
+ const schemaJson = await resolveOutputSchema(rawSchema);
12985
+ const validate = compileSchemaValidator(schemaJson);
12986
+ const maxAttempts = (options.retries ?? DEFAULT_SCHEMA_RETRIES) + 1;
12987
+ return {
12988
+ schemaJson,
12989
+ args: [...fallbackSpec?.args ?? []],
12990
+ capture: {
12991
+ type: "fallback",
12992
+ extraction: fallbackSpec?.extraction ?? { type: "text" },
12993
+ maxAttempts
12994
+ },
12995
+ tempPaths: [],
12996
+ validate,
12997
+ notices: [
12998
+ `Notice: ${agentId} lacks native ${SCHEMA_LABEL} support; using prompt-based fallback with client-side validation.`
12999
+ ]
13000
+ };
13001
+ }
13002
+ async function cleanupStructuredOutput(plan) {
13003
+ if (!plan) {
13004
+ return;
13005
+ }
13006
+ for (const tempPath of plan.tempPaths) {
13007
+ try {
13008
+ await rm(tempPath, { recursive: true, force: true });
13009
+ } catch {
13010
+ }
13011
+ }
13012
+ }
12148
13013
  function normalizeKey(value) {
12149
13014
  return value.trim().toLowerCase();
12150
13015
  }
@@ -12165,6 +13030,16 @@ async function resolveInvocationFromFlags(options) {
12165
13030
  const agent = resolution.selection;
12166
13031
  const session = buildSession(flags);
12167
13032
  const requests = buildRequests(flags);
13033
+ const structuredOutput = await planStructuredOutput({
13034
+ rawSchema: flags.outputSchema,
13035
+ mode,
13036
+ agentId: agent.id,
13037
+ spec: target.cli?.flags?.structuredOutput,
13038
+ fallbackSpec: target.cli?.flags?.structuredOutputFallback,
13039
+ retries: flags.outputSchemaRetries,
13040
+ promptDeliverable: Boolean(target.cli?.prompt),
13041
+ tempDir: options.tempDir
13042
+ });
12168
13043
  return {
12169
13044
  mode,
12170
13045
  prompt,
@@ -12176,7 +13051,8 @@ async function resolveInvocationFromFlags(options) {
12176
13051
  passthrough: {
12177
13052
  hasDelimiter: flags.hasDelimiter,
12178
13053
  args: flags.passthroughArgs
12179
- }
13054
+ },
13055
+ structuredOutput
12180
13056
  };
12181
13057
  }
12182
13058
  async function readStreamText(stream) {
@@ -12195,6 +13071,7 @@ async function runShim(argv, runtime = {}) {
12195
13071
  const stdin = runtime.stdin ?? process.stdin;
12196
13072
  const repoRoot = runtime.repoRoot ?? await findRepoRoot(process.cwd()) ?? process.cwd();
12197
13073
  const stdinIsTTY = runtime.stdinIsTTY ?? stdin.isTTY ?? false;
13074
+ let invocation = null;
12198
13075
  try {
12199
13076
  const flags = parseShimFlags(argv);
12200
13077
  let stdinText = runtime.stdinText ?? null;
@@ -12210,16 +13087,18 @@ async function runShim(argv, runtime = {}) {
12210
13087
  }
12211
13088
  }
12212
13089
  const stdio = !stdinIsTTY && flags.promptExplicit ? ["ignore", "inherit", "inherit"] : "inherit";
12213
- const invocation = await resolveInvocationFromFlags({
13090
+ invocation = await resolveInvocationFromFlags({
12214
13091
  flags,
12215
13092
  stdinIsTTY,
12216
13093
  stdinText,
12217
13094
  repoRoot,
12218
- agentsDir: runtime.agentsDir
13095
+ agentsDir: runtime.agentsDir,
13096
+ tempDir: runtime.tempDir
12219
13097
  });
12220
13098
  const result = await executeInvocation(invocation, {
12221
13099
  spawn: runtime.spawn,
12222
13100
  stderr,
13101
+ stdout: runtime.stdout,
12223
13102
  stdio,
12224
13103
  traceTranslate: flags.traceTranslate
12225
13104
  });
@@ -12234,6 +13113,8 @@ async function runShim(argv, runtime = {}) {
12234
13113
  stderr.write(`Error: ${message}
12235
13114
  `);
12236
13115
  return exitCodeFor("execution-error");
13116
+ } finally {
13117
+ await cleanupStructuredOutput(invocation?.structuredOutput);
12237
13118
  }
12238
13119
  }
12239
13120
  function resolveVersion() {
@@ -12260,11 +13141,13 @@ const VERSION = resolveVersion();
12260
13141
  const KNOWN_COMMANDS = /* @__PURE__ */ new Set(["hello", "greet", "echo", "sync", "dev", "profiles", "usage"]);
12261
13142
  const SHIM_CAPABILITIES = [
12262
13143
  "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."
13144
+ " codex: approval, sandbox, output, model, web, output-schema",
13145
+ " claude: approval, output, model, output-schema",
13146
+ " agy: approval, sandbox, model, output-schema (fallback) (alias: gemini)",
13147
+ " copilot: approval, model, output-schema (fallback)",
13148
+ "Unsupported shared flags for a selected agent emit a warning and are ignored.",
13149
+ "--output-schema is one-shot only; agents without native support use a prompt-based",
13150
+ "fallback with client-side validation and retries (--output-schema-retries, default 2)."
12268
13151
  ].join("\n");
12269
13152
  function formatError(message, args) {
12270
13153
  if (message.startsWith("Unknown command:")) {
@@ -12345,6 +13228,12 @@ function runCli(argv = process.argv, options = {}) {
12345
13228
  }).option("agent", {
12346
13229
  type: "string",
12347
13230
  describe: "Select the agent (built-in id or configured alias)."
13231
+ }).option("output-schema", {
13232
+ type: "string",
13233
+ describe: "JSON schema file path or inline JSON; emit the final response as schema-conforming JSON (one-shot only)."
13234
+ }).option("output-schema-retries", {
13235
+ type: "number",
13236
+ describe: "Max retries when a prompt-based --output-schema fallback response fails validation (0-10, default 2)."
12348
13237
  }).option("trace-translate", {
12349
13238
  type: "boolean",
12350
13239
  describe: "Emit a JSON line to stderr with the translated agent command/args."
@@ -12367,11 +13256,12 @@ if (!entry) {
12367
13256
  }
12368
13257
  }
12369
13258
  export {
13259
+ UsageExtractionError as U,
12370
13260
  compactLines as a,
12371
- parsePercentRemaining as b,
13261
+ parsePercentUsed as b,
12372
13262
  cleanControlOutput as c,
12373
13263
  parseResetText as d,
12374
13264
  makeUsageLimit as m,
12375
- parsePercentUsed as p,
13265
+ parsePercentRemaining as p,
12376
13266
  runCli
12377
13267
  };