@aexhq/sdk 0.39.0 → 0.40.1

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.
Files changed (88) hide show
  1. package/README.md +22 -16
  2. package/dist/_contracts/api-key.d.ts +49 -0
  3. package/dist/_contracts/api-key.js +87 -0
  4. package/dist/_contracts/bundle-manifest.d.ts +86 -0
  5. package/dist/_contracts/bundle-manifest.js +157 -0
  6. package/dist/_contracts/error-codes.d.ts +26 -0
  7. package/dist/_contracts/error-codes.js +79 -0
  8. package/dist/_contracts/error-factory.d.ts +32 -0
  9. package/dist/_contracts/error-factory.js +142 -0
  10. package/dist/_contracts/event-envelope.d.ts +33 -10
  11. package/dist/_contracts/event-envelope.js +46 -10
  12. package/dist/_contracts/event-view.d.ts +123 -0
  13. package/dist/_contracts/event-view.js +120 -0
  14. package/dist/_contracts/http.js +12 -3
  15. package/dist/_contracts/index.d.ts +8 -4
  16. package/dist/_contracts/index.js +11 -7
  17. package/dist/_contracts/models.d.ts +15 -0
  18. package/dist/_contracts/models.js +33 -0
  19. package/dist/_contracts/operations.d.ts +53 -2
  20. package/dist/_contracts/operations.js +119 -7
  21. package/dist/_contracts/run-record.d.ts +3 -2
  22. package/dist/_contracts/runtime-types.d.ts +126 -36
  23. package/dist/_contracts/runtime-types.js +33 -1
  24. package/dist/_contracts/sdk-errors.d.ts +61 -2
  25. package/dist/_contracts/sdk-errors.js +83 -3
  26. package/dist/_contracts/sdk-secrets.js +31 -6
  27. package/dist/_contracts/stable.d.ts +14 -0
  28. package/dist/_contracts/stable.js +14 -0
  29. package/dist/_contracts/status.d.ts +28 -1
  30. package/dist/_contracts/status.js +48 -3
  31. package/dist/_contracts/submission.d.ts +79 -2
  32. package/dist/_contracts/submission.js +148 -5
  33. package/dist/_contracts/suggest.d.ts +15 -0
  34. package/dist/_contracts/suggest.js +54 -0
  35. package/dist/asset-upload.d.ts +26 -0
  36. package/dist/asset-upload.js +218 -3
  37. package/dist/asset-upload.js.map +1 -1
  38. package/dist/bundle.d.ts +14 -3
  39. package/dist/bundle.js +34 -14
  40. package/dist/bundle.js.map +1 -1
  41. package/dist/canonical-zip.d.ts +68 -0
  42. package/dist/canonical-zip.js +307 -0
  43. package/dist/canonical-zip.js.map +1 -0
  44. package/dist/cli.mjs +1923 -338
  45. package/dist/cli.mjs.sha256 +1 -1
  46. package/dist/client.d.ts +217 -58
  47. package/dist/client.js +749 -261
  48. package/dist/client.js.map +1 -1
  49. package/dist/file.d.ts +33 -6
  50. package/dist/file.js +120 -54
  51. package/dist/file.js.map +1 -1
  52. package/dist/index.d.ts +17 -8
  53. package/dist/index.js +26 -9
  54. package/dist/index.js.map +1 -1
  55. package/dist/node-fs.d.ts +26 -9
  56. package/dist/node-fs.js +13 -38
  57. package/dist/node-fs.js.map +1 -1
  58. package/dist/node-walk.d.ts +69 -0
  59. package/dist/node-walk.js +146 -0
  60. package/dist/node-walk.js.map +1 -0
  61. package/dist/retry.d.ts +9 -13
  62. package/dist/retry.js +18 -17
  63. package/dist/retry.js.map +1 -1
  64. package/dist/skill.d.ts +16 -4
  65. package/dist/skill.js +18 -9
  66. package/dist/skill.js.map +1 -1
  67. package/dist/tool.d.ts +14 -2
  68. package/dist/tool.js +33 -7
  69. package/dist/tool.js.map +1 -1
  70. package/dist/version.d.ts +1 -1
  71. package/dist/version.js +1 -1
  72. package/docs/authentication.md +29 -5
  73. package/docs/billing.md +6 -0
  74. package/docs/concepts/agent-tools.md +11 -0
  75. package/docs/defaults.md +1 -0
  76. package/docs/errors.md +64 -4
  77. package/docs/events.md +84 -49
  78. package/docs/limits-and-quotas.md +24 -0
  79. package/docs/networking.md +7 -1
  80. package/docs/outputs.md +36 -7
  81. package/docs/quickstart.md +17 -7
  82. package/docs/run-config.md +3 -3
  83. package/docs/secrets.md +14 -0
  84. package/examples/feature-tour.ts +4 -6
  85. package/examples/spike-settle-latency.ts +125 -0
  86. package/package.json +4 -3
  87. package/dist/_contracts/event-guards.d.ts +0 -67
  88. package/dist/_contracts/event-guards.js +0 -36
package/dist/cli.mjs CHANGED
@@ -7,7 +7,7 @@ var __export = (target, all) => {
7
7
 
8
8
  // dist/cli.js
9
9
  import { readFile, writeFile, readdir, stat, mkdir, chmod, rm } from "node:fs/promises";
10
- import { resolve as resolvePath4, join, dirname as dirname2 } from "node:path";
10
+ import { resolve as resolvePath5, join, dirname as dirname2 } from "node:path";
11
11
  import { homedir } from "node:os";
12
12
 
13
13
  // ../contracts/dist/provider-support.js
@@ -98,6 +98,46 @@ var PROVIDER_PUBLIC_SUPPORT = {
98
98
  }
99
99
  };
100
100
 
101
+ // ../contracts/dist/suggest.js
102
+ function suggest(input, candidates) {
103
+ const needle = input.trim();
104
+ if (!needle)
105
+ return void 0;
106
+ const lower = needle.toLowerCase();
107
+ const prefixHits = candidates.filter((c) => c.toLowerCase().startsWith(lower));
108
+ if (prefixHits.length === 1)
109
+ return prefixHits[0];
110
+ let best;
111
+ let bestDist = Number.POSITIVE_INFINITY;
112
+ for (const candidate of candidates) {
113
+ const dist = levenshtein(needle, candidate);
114
+ if (dist < bestDist) {
115
+ bestDist = dist;
116
+ best = candidate;
117
+ }
118
+ }
119
+ return bestDist <= 2 ? best : void 0;
120
+ }
121
+ function levenshtein(a, b) {
122
+ const m = a.length;
123
+ const n = b.length;
124
+ if (m === 0)
125
+ return n;
126
+ if (n === 0)
127
+ return m;
128
+ let prev = Array.from({ length: n + 1 }, (_, j) => j);
129
+ let curr = new Array(n + 1);
130
+ for (let i2 = 1; i2 <= m; i2++) {
131
+ curr[0] = i2;
132
+ for (let j = 1; j <= n; j++) {
133
+ const cost = a[i2 - 1] === b[j - 1] ? 0 : 1;
134
+ curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
135
+ }
136
+ [prev, curr] = [curr, prev];
137
+ }
138
+ return prev[n];
139
+ }
140
+
101
141
  // ../contracts/dist/models.js
102
142
  var MODEL_PROVIDER_IDS = {
103
143
  "claude-haiku-4-5": { anthropic: "claude-haiku-4-5" },
@@ -190,6 +230,21 @@ function providersForModel(model) {
190
230
  function providerForModel(model) {
191
231
  return providersForModel(model)[0];
192
232
  }
233
+ function resolveModelProvider(model, provider) {
234
+ const providers = providersForModel(model);
235
+ if (provider !== void 0) {
236
+ if (providers.length > 0 && !providers.includes(provider)) {
237
+ throw new Error(`model ${JSON.stringify(model)} is not available for provider ${provider}; available: ${providers.join(", ")}`);
238
+ }
239
+ return provider;
240
+ }
241
+ const inferred = providers[0];
242
+ if (inferred === void 0) {
243
+ const hint = suggest(model, RUN_MODELS);
244
+ throw new Error(`${JSON.stringify(model)} is not a known model id` + (hint ? ` (did you mean ${JSON.stringify(hint)}?)` : "") + "; pass provider explicitly to run it");
245
+ }
246
+ return inferred;
247
+ }
193
248
  function isRunModel(input) {
194
249
  return typeof input === "string" && RUN_MODELS.includes(input);
195
250
  }
@@ -201,6 +256,33 @@ function parseRunModel(input, field = "submission.model") {
201
256
  }
202
257
 
203
258
  // ../contracts/dist/status.js
259
+ var SESSION_LIFECYCLE_STATUSES = [
260
+ "creating",
261
+ "running",
262
+ "idle",
263
+ "suspending",
264
+ "suspended",
265
+ "cancelling",
266
+ "deleting",
267
+ "deleted",
268
+ "expired"
269
+ ];
270
+ var SESSION_TERMINAL_OUTCOMES = [
271
+ "succeeded",
272
+ "failed",
273
+ "timed_out",
274
+ "cancelled"
275
+ ];
276
+ var SESSION_STATUSES = [
277
+ ...SESSION_LIFECYCLE_STATUSES,
278
+ ...SESSION_TERMINAL_OUTCOMES,
279
+ "awaiting_approval"
280
+ ];
281
+ var SESSION_TERMINAL_STATUSES = /* @__PURE__ */ new Set([
282
+ ...SESSION_TERMINAL_OUTCOMES,
283
+ "deleted",
284
+ "expired"
285
+ ]);
204
286
  var TERMINAL_RUN_STATUSES = [
205
287
  "succeeded",
206
288
  "failed",
@@ -222,6 +304,9 @@ var CLEANUP_STATUSES = [
222
304
  ];
223
305
 
224
306
  // ../contracts/dist/run-config.js
307
+ var SKILL_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]{0,127}$/;
308
+ var TOOL_NAME_PATTERN = SKILL_NAME_PATTERN;
309
+ var SKILL_RESERVED_NAMES = /* @__PURE__ */ new Set(["skills", "skill"]);
225
310
  var SKILL_BUNDLE_LIMITS = {
226
311
  /** Compressed (.zip) ceiling. */
227
312
  maxCompressedBytes: 10 * 1024 * 1024 * 1024,
@@ -244,6 +329,66 @@ var SKILL_BUNDLE_LIMITS = {
244
329
  /** Stored directory mode. */
245
330
  defaultDirMode: 493
246
331
  };
332
+ var DEFAULT_FILE_MOUNT_PATH = "/workspace";
333
+ var SkillBundleValidationError = class extends Error {
334
+ constructor(message) {
335
+ super(message);
336
+ this.name = "SkillBundleValidationError";
337
+ }
338
+ };
339
+ function normaliseSkillBundlePath(input) {
340
+ if (typeof input !== "string") {
341
+ throw new SkillBundleValidationError("bundle entry path must be a string");
342
+ }
343
+ if (input.length === 0 || input.trim().length === 0) {
344
+ throw new SkillBundleValidationError("bundle entry path must be non-empty");
345
+ }
346
+ if (input.length > SKILL_BUNDLE_LIMITS.maxPathLength) {
347
+ throw new SkillBundleValidationError(`bundle entry path exceeds maxPathLength (${SKILL_BUNDLE_LIMITS.maxPathLength}): ${input}`);
348
+ }
349
+ if (input.includes("\0")) {
350
+ throw new SkillBundleValidationError(`bundle entry path contains NUL byte: ${JSON.stringify(input)}`);
351
+ }
352
+ if (input.includes("\\")) {
353
+ throw new SkillBundleValidationError(`bundle entry path uses backslash separator: ${input}`);
354
+ }
355
+ if (/^[A-Za-z]:[\\/]/.test(input)) {
356
+ throw new SkillBundleValidationError(`bundle entry path uses a drive letter: ${input}`);
357
+ }
358
+ if (input.startsWith("/")) {
359
+ throw new SkillBundleValidationError(`bundle entry path must be relative: ${input}`);
360
+ }
361
+ if (input.endsWith("/")) {
362
+ throw new SkillBundleValidationError(`bundle entry path must not end with '/': ${input}`);
363
+ }
364
+ const segments = input.split("/");
365
+ for (const segment of segments) {
366
+ if (segment === "..") {
367
+ throw new SkillBundleValidationError(`bundle entry path contains '..' segment: ${input}`);
368
+ }
369
+ if (segment === "." || segment === "") {
370
+ throw new SkillBundleValidationError(`bundle entry path contains empty or '.' segment: ${input}`);
371
+ }
372
+ }
373
+ if (segments.length > SKILL_BUNDLE_LIMITS.maxDepth) {
374
+ throw new SkillBundleValidationError(`bundle entry path exceeds maxDepth (${SKILL_BUNDLE_LIMITS.maxDepth}): ${input}`);
375
+ }
376
+ return input;
377
+ }
378
+ function validateSkillBundleEntry(input) {
379
+ const path = normaliseSkillBundlePath(input.path);
380
+ if (!Number.isFinite(input.size) || !Number.isInteger(input.size) || input.size < 0) {
381
+ throw new SkillBundleValidationError(`bundle entry size must be a non-negative integer (${path})`);
382
+ }
383
+ if (input.size > SKILL_BUNDLE_LIMITS.maxDecompressedBytes) {
384
+ throw new SkillBundleValidationError(`bundle entry size exceeds maxDecompressedBytes (${SKILL_BUNDLE_LIMITS.maxDecompressedBytes}): ${path}`);
385
+ }
386
+ const mode = (input.mode ?? SKILL_BUNDLE_LIMITS.defaultFileMode) & 511;
387
+ if (mode !== SKILL_BUNDLE_LIMITS.defaultFileMode && mode !== SKILL_BUNDLE_LIMITS.defaultDirMode) {
388
+ return { path, size: input.size, mode: SKILL_BUNDLE_LIMITS.defaultFileMode };
389
+ }
390
+ return { path, size: input.size, mode };
391
+ }
247
392
  var REMOTE_MCP_TRANSPORTS = ["http", "sse"];
248
393
  var REMOTE_MCP_STDIO_REJECTED_MESSAGE = "stdio MCP servers are not supported by Aex. Aex supports remote MCP servers over HTTP/SSE only.";
249
394
  var STDIO_ONLY_FIELDS = ["command", "args", "env"];
@@ -497,6 +642,36 @@ var DEFAULT_RUNTIME_SIZE = "shared-0.25x-1gb";
497
642
  var DEFAULT_RUN_TIMEOUT_MS = 8 * 60 * 60 * 1e3;
498
643
  var MAX_RUN_TIMEOUT_MS = 8 * 60 * 60 * 1e3;
499
644
  var MIN_RUN_TIMEOUT_MS = 60 * 1e3;
645
+ var DURATION_PATTERN = /^(\d+(?:\.\d+)?)(ms|s|m|h)?$/;
646
+ function parseDurationToMs(input) {
647
+ const match = DURATION_PATTERN.exec(input.trim());
648
+ if (!match) {
649
+ throw new Error(`invalid duration ${JSON.stringify(input)} (expected e.g. "1h", "90m", "30s", "500ms", or a bare ms integer)`);
650
+ }
651
+ const value = Number(match[1]);
652
+ if (!Number.isFinite(value) || value < 0) {
653
+ throw new Error(`invalid duration ${JSON.stringify(input)} (must be a non-negative number)`);
654
+ }
655
+ const unit = match[2] ?? "ms";
656
+ const factor = unit === "h" ? 36e5 : unit === "m" ? 6e4 : unit === "s" ? 1e3 : 1;
657
+ return Math.round(value * factor);
658
+ }
659
+ function parseRunTimeout(input) {
660
+ if (input === void 0) {
661
+ return void 0;
662
+ }
663
+ if (typeof input !== "string") {
664
+ throw new Error(`timeout must be a duration string (e.g. "1h", "30m"); got ${JSON.stringify(input)}`);
665
+ }
666
+ const ms = parseDurationToMs(input);
667
+ if (ms < MIN_RUN_TIMEOUT_MS) {
668
+ throw new Error(`timeout must be at least ${MIN_RUN_TIMEOUT_MS}ms (1m); got ${ms}ms`);
669
+ }
670
+ if (ms > MAX_RUN_TIMEOUT_MS) {
671
+ throw new Error(`timeout must be at most ${MAX_RUN_TIMEOUT_MS}ms (8h); got ${ms}ms`);
672
+ }
673
+ return ms;
674
+ }
500
675
  var RUN_PROCESS_KILL_GRACE_MS = 60 * 1e3;
501
676
  var RUN_TERMINAL_GRACE_MS = 90 * 1e3;
502
677
 
@@ -540,7 +715,90 @@ var RUN_PROVIDERS = [
540
715
  "doubao",
541
716
  "doubao-cn"
542
717
  ];
543
- var DEFAULT_RUN_PROVIDER = "anthropic";
718
+ function requireRecord(input, field) {
719
+ if (!isRecord(input)) {
720
+ throw new Error(`${field} must be an object`);
721
+ }
722
+ return input;
723
+ }
724
+ function isRecord(input) {
725
+ return typeof input === "object" && input !== null && !Array.isArray(input);
726
+ }
727
+ function requireString(input, field) {
728
+ if (typeof input !== "string" || input.length === 0) {
729
+ throw new Error(`${field} must be a non-empty string`);
730
+ }
731
+ return input;
732
+ }
733
+ function optionalPositiveInt(input, field) {
734
+ if (input === void 0) {
735
+ return void 0;
736
+ }
737
+ if (typeof input !== "number" || !Number.isSafeInteger(input) || input <= 0) {
738
+ throw new Error(`${field} must be a positive safe integer`);
739
+ }
740
+ return input;
741
+ }
742
+ function optionalPositiveNumber(input, field) {
743
+ if (input === void 0) {
744
+ return void 0;
745
+ }
746
+ if (typeof input !== "number" || !Number.isFinite(input) || input <= 0) {
747
+ throw new Error(`${field} must be a positive finite number`);
748
+ }
749
+ return input;
750
+ }
751
+ function parseRunWebhook(input) {
752
+ if (input === void 0) {
753
+ return void 0;
754
+ }
755
+ const value = requireRecord(input, "webhook");
756
+ const allowed = /* @__PURE__ */ new Set(["url"]);
757
+ for (const key of Object.keys(value)) {
758
+ if (!allowed.has(key)) {
759
+ throw new Error(`webhook.${key} is not an allowed field; permitted: ${[...allowed].join(", ")}`);
760
+ }
761
+ }
762
+ const url = requireString(value.url, "webhook.url");
763
+ let parsed;
764
+ try {
765
+ parsed = new URL(url);
766
+ } catch {
767
+ throw new Error(`webhook.url must be a valid absolute URL (got ${JSON.stringify(url)})`);
768
+ }
769
+ if (parsed.protocol !== "https:") {
770
+ throw new Error(`webhook.url must use https (got ${parsed.protocol.replace(/:$/, "")})`);
771
+ }
772
+ if (parsed.username !== "" || parsed.password !== "") {
773
+ throw new Error("webhook.url must not contain userinfo (user:pass@host)");
774
+ }
775
+ return { url };
776
+ }
777
+ function parseRunLimits(input) {
778
+ if (input === void 0) {
779
+ return void 0;
780
+ }
781
+ const value = requireRecord(input, "limits");
782
+ const allowed = /* @__PURE__ */ new Set(["maxConcurrentChildRuns", "maxSubagentDepth", "maxSpendUsd", "maxTurns"]);
783
+ for (const key of Object.keys(value)) {
784
+ if (!allowed.has(key)) {
785
+ throw new Error(`limits.${key} is not an allowed field; permitted: ${[...allowed].join(", ")}`);
786
+ }
787
+ }
788
+ const maxConcurrentChildRuns = optionalPositiveInt(value.maxConcurrentChildRuns, "limits.maxConcurrentChildRuns");
789
+ const maxSubagentDepth = optionalPositiveInt(value.maxSubagentDepth, "limits.maxSubagentDepth");
790
+ const maxSpendUsd = optionalPositiveNumber(value.maxSpendUsd, "limits.maxSpendUsd");
791
+ const maxTurns = optionalPositiveInt(value.maxTurns, "limits.maxTurns");
792
+ if (maxConcurrentChildRuns === void 0 && maxSubagentDepth === void 0 && maxSpendUsd === void 0 && maxTurns === void 0) {
793
+ return void 0;
794
+ }
795
+ return {
796
+ ...maxConcurrentChildRuns !== void 0 ? { maxConcurrentChildRuns } : {},
797
+ ...maxSubagentDepth !== void 0 ? { maxSubagentDepth } : {},
798
+ ...maxSpendUsd !== void 0 ? { maxSpendUsd } : {},
799
+ ...maxTurns !== void 0 ? { maxTurns } : {}
800
+ };
801
+ }
544
802
  var BUILTIN_TOOL_NAMES = [
545
803
  "bash",
546
804
  "read_file",
@@ -559,10 +817,14 @@ var BUILTIN_TOOL_NAMES = [
559
817
  "bash_kill",
560
818
  "code_execution",
561
819
  "wait",
562
- "git"
820
+ "git",
821
+ "ls",
822
+ "stat",
823
+ "wc"
563
824
  ];
564
825
  var DEFAULT_BUILTIN_TOOLS = BUILTIN_TOOL_NAMES;
565
826
  var MAX_OUTPUT_CAPTURE_TIMEOUT_MS = 6 * 60 * 60 * 1e3;
827
+ var SKILLS_MAX = 64;
566
828
 
567
829
  // ../contracts/dist/event-envelope.js
568
830
  var AEX_EVENT_SOURCES = ["agent", "api", "runtime", "mcp", "aex", "workflow", "host"];
@@ -580,7 +842,14 @@ var AEX_EVENT_TYPES = [
580
842
  // off-the-shelf AG-UI client filters logs out by `channel`.
581
843
  "LOG"
582
844
  ];
583
- var AEX_SESSION_PARKED_NAMES = ["aex.session.idle", "aex.session.error", "aex.session.suspended"];
845
+ var AEX_SESSION_PARKED_NAMES = [
846
+ "aex.session.idle",
847
+ "aex.session.suspended",
848
+ "aex.session.succeeded",
849
+ "aex.session.failed",
850
+ "aex.session.timed_out",
851
+ "aex.session.cancelled"
852
+ ];
584
853
  function isSessionParked(e) {
585
854
  const name = customName(e);
586
855
  return name !== null && AEX_SESSION_PARKED_NAMES.includes(name);
@@ -847,8 +1116,8 @@ function parseRunUnitSubmission(input, fallbackModel) {
847
1116
  return fallbackFlat(fallbackModel);
848
1117
  }
849
1118
  function parseFlatProjection(value, fallbackModel) {
850
- const submissionRaw = isRecord(value.submission) ? value.submission : {};
851
- const outputsRaw = isRecord(submissionRaw.outputs) ? submissionRaw.outputs : {};
1119
+ const submissionRaw = isRecord2(value.submission) ? value.submission : {};
1120
+ const outputsRaw = isRecord2(submissionRaw.outputs) ? submissionRaw.outputs : {};
852
1121
  const allowedDirs = toOptionalStringArray(outputsRaw.allowedDirs);
853
1122
  const deniedDirs = toOptionalStringArray(outputsRaw.deniedDirs);
854
1123
  const captureTimeoutMs = toOptionalPositiveInteger(outputsRaw.captureTimeoutMs);
@@ -901,12 +1170,12 @@ function fallbackFlat(fallbackModel) {
901
1170
  }
902
1171
  };
903
1172
  }
904
- function isRecord(value) {
1173
+ function isRecord2(value) {
905
1174
  return typeof value === "object" && value !== null && !Array.isArray(value);
906
1175
  }
907
1176
  function normalizeRunUnit(raw) {
908
- const r = isRecord(raw) ? raw : {};
909
- const eventsRaw = isRecord(r.events) ? r.events : {};
1177
+ const r = isRecord2(raw) ? raw : {};
1178
+ const eventsRaw = isRecord2(r.events) ? r.events : {};
910
1179
  const str3 = (v) => typeof v === "string" ? v : void 0;
911
1180
  const arr = (v) => Array.isArray(v) ? v : [];
912
1181
  return {
@@ -925,7 +1194,7 @@ function normalizeRunUnit(raw) {
925
1194
  // still carry the run's `model` at the top level — prefer it over the
926
1195
  // static fallback so `unit()` never claims a model the run did not use.
927
1196
  submission: parseRunUnitSubmission(r.submission, r.model),
928
- ...isRecord(r.capsSnapshot) ? { capsSnapshot: r.capsSnapshot } : {},
1197
+ ...isRecord2(r.capsSnapshot) ? { capsSnapshot: r.capsSnapshot } : {},
929
1198
  attempts: arr(r.attempts),
930
1199
  events: {
931
1200
  entries: arr(eventsRaw.entries),
@@ -936,8 +1205,8 @@ function normalizeRunUnit(raw) {
936
1205
  rawEventPages: arr(r.rawEventPages),
937
1206
  outputs: arr(r.outputs),
938
1207
  outputCaptureFailures: arr(r.outputCaptureFailures),
939
- ...isRecord(r.costTelemetry) ? { costTelemetry: r.costTelemetry } : {},
940
- ...isRecord(r.runtimeManifest) ? { runtimeManifest: r.runtimeManifest } : {}
1208
+ ...isRecord2(r.costTelemetry) ? { costTelemetry: r.costTelemetry } : {},
1209
+ ...isRecord2(r.runtimeManifest) ? { runtimeManifest: r.runtimeManifest } : {}
941
1210
  };
942
1211
  }
943
1212
  function coerceRunUnitModel(value) {
@@ -950,7 +1219,7 @@ function coerceRunUnitModel(value) {
950
1219
  }
951
1220
  }
952
1221
  function isJsonRecord(value) {
953
- return isRecord(value);
1222
+ return isRecord2(value);
954
1223
  }
955
1224
  function toStringArray(value) {
956
1225
  if (!Array.isArray(value)) {
@@ -979,11 +1248,11 @@ function toMcpServerRefArray(value) {
979
1248
  return out;
980
1249
  }
981
1250
  function parseEnvironment(value) {
982
- if (!isRecord(value)) {
1251
+ if (!isRecord2(value)) {
983
1252
  return void 0;
984
1253
  }
985
1254
  const env = {};
986
- if (isRecord(value.networking)) {
1255
+ if (isRecord2(value.networking)) {
987
1256
  const mode = value.networking.mode;
988
1257
  const allowedHosts = value.networking.allowedHosts;
989
1258
  if (mode === "limited" || mode === "open") {
@@ -994,7 +1263,7 @@ function parseEnvironment(value) {
994
1263
  }
995
1264
  }
996
1265
  if (Array.isArray(value.packages)) {
997
- const pkgs = value.packages.filter(isRecord).map((p) => {
1266
+ const pkgs = value.packages.filter(isRecord2).map((p) => {
998
1267
  const r = p;
999
1268
  if (typeof r.name !== "string")
1000
1269
  return null;
@@ -1009,7 +1278,7 @@ function parseEnvironment(value) {
1009
1278
  env.packages = pkgs;
1010
1279
  }
1011
1280
  }
1012
- if (isRecord(value.envVars)) {
1281
+ if (isRecord2(value.envVars)) {
1013
1282
  const out = {};
1014
1283
  for (const [k, v] of Object.entries(value.envVars)) {
1015
1284
  if (typeof v === "string") {
@@ -1438,6 +1707,9 @@ function isSecretKey(key) {
1438
1707
  return /(?:api[_-]?key|authorization|token|secret|password|credential)/i.test(key);
1439
1708
  }
1440
1709
  function looksHighEntropySecret(value) {
1710
+ return value.split("-").some(isOpaqueSecretRun);
1711
+ }
1712
+ function isOpaqueSecretRun(value) {
1441
1713
  if (charClassCount(value) < MIN_CHAR_CLASSES)
1442
1714
  return false;
1443
1715
  if (shannonEntropyBits(value) < ENTROPY_BITS_PER_CHAR)
@@ -1481,6 +1753,11 @@ var AexError = class extends Error {
1481
1753
  this.details = details === void 0 ? void 0 : redactSecrets(details);
1482
1754
  }
1483
1755
  };
1756
+ var RunConfigValidationError = class extends AexError {
1757
+ constructor(message, details) {
1758
+ super("RUN_CONFIG_INVALID", message, details);
1759
+ }
1760
+ };
1484
1761
  var RunStateError = class extends AexError {
1485
1762
  constructor(message, details) {
1486
1763
  super("RUN_STATE_ERROR", message, details);
@@ -1489,10 +1766,58 @@ var RunStateError = class extends AexError {
1489
1766
  var AexApiError = class extends AexError {
1490
1767
  status;
1491
1768
  body;
1492
- constructor(status2, message, body) {
1493
- super("API_ERROR", message, body);
1769
+ /** The server's stable error code, when the body carried a known one. */
1770
+ apiCode;
1771
+ /** Request id (body `requestId` or a response header) for support correlation. */
1772
+ requestId;
1773
+ constructor(status2, message, body, options) {
1774
+ super("API_ERROR", message, body, options?.cause === void 0 ? void 0 : { cause: options.cause });
1494
1775
  this.status = status2;
1495
1776
  this.body = redactSecrets(body);
1777
+ this.apiCode = options?.apiCode;
1778
+ this.requestId = options?.requestId;
1779
+ }
1780
+ };
1781
+ var AexAuthError = class extends AexApiError {
1782
+ /** The scope the endpoint required, when the server named it (insufficient_scope). */
1783
+ requiredScope;
1784
+ constructor(init) {
1785
+ super(init.status, init.message, init.body, {
1786
+ apiCode: init.apiCode,
1787
+ requestId: init.requestId,
1788
+ cause: init.cause
1789
+ });
1790
+ this.requiredScope = init.requiredScope;
1791
+ }
1792
+ };
1793
+ var AexIdempotencyConflictError = class extends AexApiError {
1794
+ constructor(init) {
1795
+ super(init.status, init.message, init.body, {
1796
+ apiCode: init.apiCode,
1797
+ requestId: init.requestId,
1798
+ cause: init.cause
1799
+ });
1800
+ }
1801
+ };
1802
+ var AexNotFoundError = class extends AexApiError {
1803
+ constructor(init) {
1804
+ super(init.status, init.message, init.body, {
1805
+ apiCode: init.apiCode,
1806
+ requestId: init.requestId,
1807
+ cause: init.cause
1808
+ });
1809
+ }
1810
+ };
1811
+ var AexRateLimitError = class extends AexApiError {
1812
+ /** Suggested backoff (ms), when the server advertised one (Retry-After). */
1813
+ retryAfterMs;
1814
+ constructor(init) {
1815
+ super(init.status, init.message, init.body, {
1816
+ apiCode: init.apiCode,
1817
+ requestId: init.requestId,
1818
+ cause: init.cause
1819
+ });
1820
+ this.retryAfterMs = init.retryAfterMs;
1496
1821
  }
1497
1822
  };
1498
1823
  var AexNetworkError = class extends AexError {
@@ -1504,6 +1829,8 @@ var AexNetworkError = class extends AexError {
1504
1829
  causeCode;
1505
1830
  /** Attempts made when a retry layer exhausted its budget; `1` otherwise. */
1506
1831
  attempts;
1832
+ /** Total elapsed time (ms) across all attempts, when the retry layer set it. */
1833
+ elapsedMs;
1507
1834
  constructor(args) {
1508
1835
  const causeCode = extractErrorCode(args.cause);
1509
1836
  super("NETWORK_ERROR", networkErrorMessage(args, causeCode), { method: args.method, host: args.host, path: args.path, ...causeCode ? { code: causeCode } : {} }, { cause: args.cause });
@@ -1512,6 +1839,7 @@ var AexNetworkError = class extends AexError {
1512
1839
  this.path = args.path;
1513
1840
  this.causeCode = causeCode;
1514
1841
  this.attempts = args.attempts ?? 1;
1842
+ this.elapsedMs = args.elapsedMs;
1515
1843
  }
1516
1844
  };
1517
1845
  function networkErrorMessage(args, causeCode) {
@@ -1575,9 +1903,185 @@ function stringProperty(value, key) {
1575
1903
  return typeof prop === "string" && prop.length > 0 ? prop : void 0;
1576
1904
  }
1577
1905
 
1906
+ // ../contracts/dist/bundle-manifest.js
1907
+ var META_TEXT_ENCODER = new TextEncoder();
1908
+ var META_TEXT_DECODER = new TextDecoder("utf-8", { fatal: false });
1909
+
1578
1910
  // ../contracts/dist/webhook-verify.js
1579
1911
  var encoder2 = new TextEncoder();
1580
1912
 
1913
+ // ../contracts/dist/error-codes.js
1914
+ var AEX_API_ERROR_CODES = [
1915
+ "unauthorized",
1916
+ "forbidden",
1917
+ "insufficient_scope",
1918
+ "token_invalid",
1919
+ "token_revoked",
1920
+ "token_expired",
1921
+ "malformed_token",
1922
+ "not_found",
1923
+ "idempotency_conflict",
1924
+ "session_busy",
1925
+ "run_not_terminal",
1926
+ "unknown_workspace",
1927
+ "workspace_concurrency_exceeded",
1928
+ "workspace_submit_rate_exceeded",
1929
+ "workspace_spend_cap_exceeded",
1930
+ "insufficient_balance",
1931
+ "rate_limited",
1932
+ "upstream_error",
1933
+ "internal_error"
1934
+ ];
1935
+ var AEX_API_ERROR_MESSAGES = {
1936
+ unauthorized: "The request was not authenticated.",
1937
+ forbidden: "The API key is not permitted to perform this action.",
1938
+ insufficient_scope: "The API key is missing a scope this endpoint requires.",
1939
+ token_invalid: "The API key is not valid.",
1940
+ token_revoked: "The API key has been revoked.",
1941
+ token_expired: "The API key has expired.",
1942
+ malformed_token: "The API key is malformed.",
1943
+ not_found: "The requested resource was not found.",
1944
+ idempotency_conflict: "This idempotency key was already used with a different request body.",
1945
+ session_busy: "The session is busy handling another turn.",
1946
+ run_not_terminal: "The run has not reached a terminal state yet.",
1947
+ unknown_workspace: "The workspace could not be resolved from the API key.",
1948
+ workspace_concurrency_exceeded: "The workspace has reached its concurrent-run limit.",
1949
+ workspace_submit_rate_exceeded: "The workspace submit-rate limit was exceeded.",
1950
+ workspace_spend_cap_exceeded: "The workspace monthly spend cap was reached.",
1951
+ insufficient_balance: "The workspace prepaid balance is insufficient to submit this run.",
1952
+ rate_limited: "Too many requests \u2014 retry after a short backoff.",
1953
+ upstream_error: "An upstream provider returned an error.",
1954
+ internal_error: "The aex API encountered an internal error."
1955
+ };
1956
+ var API_ERROR_CODE_SET = new Set(AEX_API_ERROR_CODES);
1957
+ function isAexApiErrorCode(value) {
1958
+ return typeof value === "string" && API_ERROR_CODE_SET.has(value);
1959
+ }
1960
+
1961
+ // ../contracts/dist/error-factory.js
1962
+ function apiErrorKindForCode(code) {
1963
+ switch (code) {
1964
+ case "unauthorized":
1965
+ case "forbidden":
1966
+ case "insufficient_scope":
1967
+ case "token_invalid":
1968
+ case "token_revoked":
1969
+ case "token_expired":
1970
+ case "malformed_token":
1971
+ return "auth";
1972
+ case "idempotency_conflict":
1973
+ return "idempotency";
1974
+ case "not_found":
1975
+ return "not_found";
1976
+ case "rate_limited":
1977
+ case "workspace_concurrency_exceeded":
1978
+ case "workspace_submit_rate_exceeded":
1979
+ return "rate_limit";
1980
+ case "session_busy":
1981
+ case "run_not_terminal":
1982
+ case "unknown_workspace":
1983
+ case "workspace_spend_cap_exceeded":
1984
+ case "insufficient_balance":
1985
+ case "upstream_error":
1986
+ case "internal_error":
1987
+ return "generic";
1988
+ }
1989
+ }
1990
+ function kindForStatus(status2) {
1991
+ if (status2 === 401 || status2 === 403)
1992
+ return "auth";
1993
+ if (status2 === 404)
1994
+ return "not_found";
1995
+ if (status2 === 409)
1996
+ return "idempotency";
1997
+ if (status2 === 429)
1998
+ return "rate_limit";
1999
+ return "generic";
2000
+ }
2001
+ function apiErrorFromResponse(input) {
2002
+ const apiCode = apiCodeFromBody(input.body);
2003
+ const requestId = input.requestId ?? requestIdFromBody(input.body);
2004
+ const message = input.message ?? fallbackMessage(apiCode, input.body);
2005
+ const kind = apiCode !== void 0 ? apiErrorKindForCode(apiCode) : kindForStatus(input.status);
2006
+ const init = {
2007
+ status: input.status,
2008
+ message,
2009
+ body: input.body,
2010
+ apiCode,
2011
+ requestId,
2012
+ cause: input.cause
2013
+ };
2014
+ switch (kind) {
2015
+ case "auth":
2016
+ return new AexAuthError({ ...init, requiredScope: requiredScopeFromBody(input.body) });
2017
+ case "idempotency":
2018
+ return new AexIdempotencyConflictError(init);
2019
+ case "not_found":
2020
+ return new AexNotFoundError(init);
2021
+ case "rate_limit":
2022
+ return new AexRateLimitError({ ...init, retryAfterMs: retryAfterMsFromBody(input.body) });
2023
+ case "generic":
2024
+ return new AexApiError(input.status, message, input.body, {
2025
+ apiCode,
2026
+ requestId,
2027
+ ...input.cause !== void 0 ? { cause: input.cause } : {}
2028
+ });
2029
+ }
2030
+ }
2031
+ function asRecord(body) {
2032
+ return body !== null && typeof body === "object" && !Array.isArray(body) ? body : void 0;
2033
+ }
2034
+ function apiCodeFromBody(body) {
2035
+ const record = asRecord(body);
2036
+ if (!record)
2037
+ return void 0;
2038
+ if (isAexApiErrorCode(record.error))
2039
+ return record.error;
2040
+ if (isAexApiErrorCode(record.code))
2041
+ return record.code;
2042
+ return void 0;
2043
+ }
2044
+ function requestIdFromBody(body) {
2045
+ const record = asRecord(body);
2046
+ const value = record?.requestId;
2047
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
2048
+ }
2049
+ function requiredScopeFromBody(body) {
2050
+ const record = asRecord(body);
2051
+ if (!record)
2052
+ return void 0;
2053
+ for (const key of ["requiredScope", "required_scope", "scope"]) {
2054
+ const value = record[key];
2055
+ if (typeof value === "string" && value.length > 0)
2056
+ return value;
2057
+ }
2058
+ return void 0;
2059
+ }
2060
+ function retryAfterMsFromBody(body) {
2061
+ const record = asRecord(body);
2062
+ if (!record)
2063
+ return void 0;
2064
+ if (typeof record.retryAfterMs === "number" && Number.isFinite(record.retryAfterMs)) {
2065
+ return record.retryAfterMs;
2066
+ }
2067
+ if (typeof record.retryAfter === "number" && Number.isFinite(record.retryAfter)) {
2068
+ return record.retryAfter * 1e3;
2069
+ }
2070
+ return void 0;
2071
+ }
2072
+ function fallbackMessage(apiCode, body) {
2073
+ if (apiCode !== void 0)
2074
+ return AEX_API_ERROR_MESSAGES[apiCode];
2075
+ const record = asRecord(body);
2076
+ const message = record?.message;
2077
+ if (typeof message === "string" && message.length > 0)
2078
+ return message;
2079
+ const error = record?.error;
2080
+ if (typeof error === "string" && error.length > 0)
2081
+ return error;
2082
+ return "aex API request failed";
2083
+ }
2084
+
1581
2085
  // ../contracts/dist/http.js
1582
2086
  var HttpClient = class {
1583
2087
  #baseUrl;
@@ -1629,7 +2133,11 @@ var HttpClient = class {
1629
2133
  const body = await readJson(response);
1630
2134
  if (!response.ok) {
1631
2135
  const errorBody = withResponseRequestId(body, response.headers);
1632
- throw new AexApiError(response.status, extractErrorMessage(errorBody), errorBody);
2136
+ throw apiErrorFromResponse({
2137
+ status: response.status,
2138
+ body: errorBody,
2139
+ message: extractErrorMessage(errorBody)
2140
+ });
1633
2141
  }
1634
2142
  return body;
1635
2143
  }
@@ -1653,7 +2161,11 @@ var HttpClient = class {
1653
2161
  if (!response.ok) {
1654
2162
  const body = await readJson(response);
1655
2163
  const errorBody = withResponseRequestId(body, response.headers);
1656
- throw new AexApiError(response.status, extractErrorMessage(errorBody), errorBody);
2164
+ throw apiErrorFromResponse({
2165
+ status: response.status,
2166
+ body: errorBody,
2167
+ message: extractErrorMessage(errorBody)
2168
+ });
1657
2169
  }
1658
2170
  return { response };
1659
2171
  }
@@ -1735,6 +2247,7 @@ var operations_exports = {};
1735
2247
  __export(operations_exports, {
1736
2248
  READ_OUTPUT_TEXT_DEFAULT_BYTES: () => READ_OUTPUT_TEXT_DEFAULT_BYTES,
1737
2249
  READ_OUTPUT_TEXT_MAX_BYTES: () => READ_OUTPUT_TEXT_MAX_BYTES,
2250
+ approveSession: () => approveSession,
1738
2251
  cancelRun: () => cancelRun,
1739
2252
  cancelSession: () => cancelSession,
1740
2253
  classifyOutput: () => classifyOutput,
@@ -1750,6 +2263,7 @@ __export(operations_exports, {
1750
2263
  deleteSession: () => deleteSession,
1751
2264
  deleteSkill: () => deleteSkill,
1752
2265
  deleteWorkspaceAsset: () => deleteWorkspaceAsset,
2266
+ denySession: () => denySession,
1753
2267
  download: () => download,
1754
2268
  downloadEvents: () => downloadEvents,
1755
2269
  downloadMetadata: () => downloadMetadata,
@@ -1776,6 +2290,7 @@ __export(operations_exports, {
1776
2290
  listAgentsMd: () => listAgentsMd,
1777
2291
  listFiles: () => listFiles,
1778
2292
  listOutputs: () => listOutputs,
2293
+ listRunChildren: () => listRunChildren,
1779
2294
  listRunEvents: () => listRunEvents,
1780
2295
  listRuns: () => listRuns,
1781
2296
  listSecrets: () => listSecrets,
@@ -1788,12 +2303,16 @@ __export(operations_exports, {
1788
2303
  outputLink: () => outputLink,
1789
2304
  readOutputText: () => readOutputText,
1790
2305
  redeliverRunWebhook: () => redeliverRunWebhook,
2306
+ requestApproval: () => requestApproval,
2307
+ resolveIdempotencyKey: () => resolveIdempotencyKey,
1791
2308
  resolveOutputFileSelector: () => resolveOutputFileSelector,
1792
2309
  resumeSession: () => resumeSession,
1793
2310
  rotateSecret: () => rotateSecret,
1794
2311
  sendSessionMessage: () => sendSessionMessage,
2312
+ submit: () => submit,
1795
2313
  submitRun: () => submitRun,
1796
2314
  suspendSession: () => suspendSession,
2315
+ toFilenameMatcher: () => toFilenameMatcher,
1797
2316
  uploadWorkspaceAsset: () => uploadWorkspaceAsset,
1798
2317
  upsertSkill: () => upsertSkill,
1799
2318
  whoami: () => whoami
@@ -2516,6 +3035,7 @@ function zipSync(data, opts) {
2516
3035
  }
2517
3036
 
2518
3037
  // ../contracts/dist/operations.js
3038
+ import { randomUUID } from "node:crypto";
2519
3039
  async function getRun(http, runId) {
2520
3040
  const result = await http.request(`/api/runs/${encodeURIComponent(runId)}`);
2521
3041
  return hasRun(result) ? result.run : result;
@@ -2551,8 +3071,28 @@ async function listRuns(http, query) {
2551
3071
  }
2552
3072
  return changed ? { ...page, runs } : page;
2553
3073
  }
3074
+ function resolveIdempotencyKey(key) {
3075
+ if (key === void 0) {
3076
+ return `aex-idem-${randomUUID()}`;
3077
+ }
3078
+ if (typeof key !== "string" || key.trim().length === 0) {
3079
+ throw new RunConfigValidationError("idempotencyKey must be a non-empty, non-whitespace string", {
3080
+ field: "idempotencyKey",
3081
+ value: key
3082
+ });
3083
+ }
3084
+ return key;
3085
+ }
2554
3086
  function idempotencyHeaders(options) {
2555
- return options?.idempotencyKey ? { "Idempotency-Key": options.idempotencyKey } : void 0;
3087
+ if (options?.idempotencyKey === void 0)
3088
+ return void 0;
3089
+ if (typeof options.idempotencyKey !== "string" || options.idempotencyKey.trim().length === 0) {
3090
+ throw new RunConfigValidationError("idempotencyKey must be a non-empty, non-whitespace string", {
3091
+ field: "idempotencyKey",
3092
+ value: options.idempotencyKey
3093
+ });
3094
+ }
3095
+ return { "Idempotency-Key": options.idempotencyKey };
2556
3096
  }
2557
3097
  async function createSession(http, request, options) {
2558
3098
  const headers = idempotencyHeaders(options);
@@ -2563,6 +3103,26 @@ async function createSession(http, request, options) {
2563
3103
  });
2564
3104
  return unwrapSession(result);
2565
3105
  }
3106
+ async function submit(http, request, options) {
3107
+ const createKey = resolveIdempotencyKey(options?.idempotencyKey);
3108
+ const messageKey = options?.messageIdempotencyKey !== void 0 ? resolveIdempotencyKey(options.messageIdempotencyKey) : `${createKey}:message`;
3109
+ const { input, ...createRequest } = request;
3110
+ assertSubmitInput(input);
3111
+ const created = await createSession(http, createRequest, { idempotencyKey: createKey });
3112
+ const sessionId = created.sessionId ?? created.id;
3113
+ const accepted = await sendSessionMessage(http, sessionId, { input }, { idempotencyKey: messageKey });
3114
+ const session = accepted.session;
3115
+ return { runId: session.sessionId ?? session.id, session };
3116
+ }
3117
+ function assertSubmitInput(input) {
3118
+ const ok = typeof input === "string" && input.length > 0 || Array.isArray(input) && input.length > 0 && input.every((segment) => typeof segment === "string" && segment.length > 0);
3119
+ if (!ok) {
3120
+ throw new RunConfigValidationError("submit: request.input must be a non-empty string or string array", {
3121
+ field: "input",
3122
+ value: input
3123
+ });
3124
+ }
3125
+ }
2566
3126
  async function getSession(http, sessionId) {
2567
3127
  const result = await http.request(`/api/sessions/${encodeURIComponent(sessionId)}`);
2568
3128
  return unwrapSession(result);
@@ -2609,6 +3169,18 @@ async function resumeSession(http, sessionId, options) {
2609
3169
  const headers = idempotencyHeaders(options);
2610
3170
  return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/resume`, { method: "POST", ...headers ? { headers } : {} });
2611
3171
  }
3172
+ async function requestApproval(http, sessionId, options) {
3173
+ const headers = idempotencyHeaders(options);
3174
+ return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/request-approval`, { method: "POST", ...headers ? { headers } : {} });
3175
+ }
3176
+ async function approveSession(http, sessionId, options) {
3177
+ const headers = idempotencyHeaders(options);
3178
+ return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/approve`, { method: "POST", ...headers ? { headers } : {} });
3179
+ }
3180
+ async function denySession(http, sessionId, options) {
3181
+ const headers = idempotencyHeaders(options);
3182
+ return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/deny`, { method: "POST", ...headers ? { headers } : {} });
3183
+ }
2612
3184
  async function deleteSession(http, sessionId, options) {
2613
3185
  const headers = idempotencyHeaders(options);
2614
3186
  return http.request(`/api/sessions/${encodeURIComponent(sessionId)}`, { method: "DELETE", ...headers ? { headers } : {} });
@@ -2665,7 +3237,7 @@ async function findOutput(http, runId, query) {
2665
3237
  return null;
2666
3238
  if (matches.length === 1)
2667
3239
  return matches[0];
2668
- throw new RunStateError("findOutput: output query matched multiple files", {
3240
+ throw new RunStateError("outputs.findOne: output query matched multiple files", {
2669
3241
  runId,
2670
3242
  matches: matches.map((output) => output.filename ?? output.id)
2671
3243
  });
@@ -2708,7 +3280,7 @@ function resolveOutputFileSelector(outputs, selector, runId) {
2708
3280
  if (isPathSelector(selector)) {
2709
3281
  const target = normalizeOutputLookupPath(selector.path);
2710
3282
  if (!target) {
2711
- throw new RunStateError("downloadOutput: output path must be non-empty", { runId, path: selector.path });
3283
+ throw new RunStateError("outputs.download: output path must be non-empty", { runId, path: selector.path });
2712
3284
  }
2713
3285
  const matches = outputs.filter((output) => {
2714
3286
  if (typeof output.filename !== "string")
@@ -2722,15 +3294,15 @@ function resolveOutputFileSelector(outputs, selector, runId) {
2722
3294
  if (matches.length === 1)
2723
3295
  return matches[0];
2724
3296
  if (matches.length > 1) {
2725
- throw new RunStateError(`downloadOutput: output path "${selector.path}" matched multiple files`, { runId, path: selector.path, matches: matches.map((output) => output.filename ?? output.id) });
3297
+ throw new RunStateError(`outputs.download: output path "${selector.path}" matched multiple files`, { runId, path: selector.path, matches: matches.map((output) => output.filename ?? output.id) });
2726
3298
  }
2727
- throw new RunStateError(`downloadOutput: output path "${selector.path}" was not found`, {
3299
+ throw new RunStateError(`outputs.download: output path "${selector.path}" was not found`, {
2728
3300
  runId,
2729
3301
  path: selector.path
2730
3302
  });
2731
3303
  }
2732
3304
  if (typeof selector?.id !== "string" || selector.id.length === 0) {
2733
- throw new RunStateError("downloadOutput: selector must include an output id or path", { runId });
3305
+ throw new RunStateError("outputs.download: selector must include an output id or path", { runId });
2734
3306
  }
2735
3307
  return { ...selector, id: selector.id };
2736
3308
  }
@@ -2807,6 +3379,10 @@ function grepLines(text, pattern) {
2807
3379
  const test = typeof pattern === "string" ? (line) => line.toLowerCase().includes(pattern.toLowerCase()) : (line) => pattern.test(line);
2808
3380
  return text.split("\n").filter((line) => test(line)).join("\n");
2809
3381
  }
3382
+ async function listRunChildren(http, runId) {
3383
+ const result = await http.request(`/api/runs/${encodeURIComponent(runId)}/children`);
3384
+ return Array.isArray(result) ? result : result.children;
3385
+ }
2810
3386
  async function cancelRun(http, runId) {
2811
3387
  await http.request(`/api/runs/${encodeURIComponent(runId)}/cancel`, { method: "POST" });
2812
3388
  }
@@ -2889,6 +3465,16 @@ function normalizeOutputLookupPath(path) {
2889
3465
  function filterOutputs(outputs, query) {
2890
3466
  return outputs.filter((output) => outputMatchesQuery(output, query));
2891
3467
  }
3468
+ function toFilenameMatcher(filename) {
3469
+ if (typeof filename === "string") {
3470
+ const needle = filename.toLowerCase();
3471
+ return (name) => name.toLowerCase().includes(needle);
3472
+ }
3473
+ return (name) => {
3474
+ filename.lastIndex = 0;
3475
+ return filename.test(name);
3476
+ };
3477
+ }
2892
3478
  function classifyOutput(output) {
2893
3479
  const contentType = normalizeContentType(output.contentType);
2894
3480
  if (contentType) {
@@ -3117,7 +3703,7 @@ function jsonlEntry(path, events) {
3117
3703
  }
3118
3704
  function extractSubmissionSnapshot(run) {
3119
3705
  const raw = run.submission;
3120
- if (!isRecord2(raw) || raw.kind !== "submission" || !isRecord2(raw.submission)) {
3706
+ if (!isRecord3(raw) || raw.kind !== "submission" || !isRecord3(raw.submission)) {
3121
3707
  return void 0;
3122
3708
  }
3123
3709
  return {
@@ -3126,9 +3712,9 @@ function extractSubmissionSnapshot(run) {
3126
3712
  }
3127
3713
  function extractCostTelemetry(run) {
3128
3714
  const raw = run.costTelemetry;
3129
- return isRecord2(raw) ? raw : void 0;
3715
+ return isRecord3(raw) ? raw : void 0;
3130
3716
  }
3131
- function isRecord2(value) {
3717
+ function isRecord3(value) {
3132
3718
  return typeof value === "object" && value !== null && !Array.isArray(value);
3133
3719
  }
3134
3720
  async function submitRun(http, request) {
@@ -3277,6 +3863,16 @@ async function uploadWorkspaceAsset(http, input) {
3277
3863
  });
3278
3864
  }
3279
3865
 
3866
+ // ../contracts/dist/api-key.js
3867
+ var API_KEY_PLANES = ["dev", "prd"];
3868
+ var API_KEY_REGION_TO_CODE = {
3869
+ "eu-west-2": "euw2",
3870
+ "us-west-2": "usw2",
3871
+ "ap-northeast-1": "apn1"
3872
+ };
3873
+ var CODE_TO_REGION = Object.fromEntries(Object.entries(API_KEY_REGION_TO_CODE).map(([region, code]) => [code, region]));
3874
+ var API_KEY_PLANE_SET = new Set(API_KEY_PLANES);
3875
+
3280
3876
  // dist/internal.js
3281
3877
  var AEX_INDEX_PATH = "/mnt/session/uploads/aex/index.json";
3282
3878
 
@@ -3296,6 +3892,7 @@ function extractCommonHostFlags(argv) {
3296
3892
  let apiKey = null;
3297
3893
  let aexUrl = null;
3298
3894
  let debug2 = false;
3895
+ let json = false;
3299
3896
  const rest = [];
3300
3897
  for (let i2 = 0; i2 < argv.length; i2++) {
3301
3898
  const arg = argv[i2];
@@ -3303,6 +3900,10 @@ function extractCommonHostFlags(argv) {
3303
3900
  debug2 = true;
3304
3901
  continue;
3305
3902
  }
3903
+ if (arg === "--json") {
3904
+ json = true;
3905
+ continue;
3906
+ }
3306
3907
  if (arg === "--api-key") {
3307
3908
  const v = argv[++i2];
3308
3909
  if (v === void 0)
@@ -3325,13 +3926,13 @@ function extractCommonHostFlags(argv) {
3325
3926
  }
3326
3927
  rest.push(arg);
3327
3928
  }
3328
- return { ok: true, flags: { apiKey, aexUrl, debug: debug2, rest } };
3929
+ return { ok: true, flags: { apiKey, aexUrl, debug: debug2, json, rest } };
3329
3930
  }
3330
3931
  async function resolveCommonHostFlags(io2, argv) {
3331
3932
  const extracted = extractCommonHostFlags(argv);
3332
3933
  if (!extracted.ok)
3333
3934
  return extracted;
3334
- const { apiKey: flagToken, aexUrl: flagUrl, debug: debug2, rest } = extracted.flags;
3935
+ const { apiKey: flagToken, aexUrl: flagUrl, debug: debug2, json, rest } = extracted.flags;
3335
3936
  let token = flagToken;
3336
3937
  let url = flagUrl;
3337
3938
  let source = flagToken ? "flag" : "none";
@@ -3355,7 +3956,7 @@ async function resolveCommonHostFlags(io2, argv) {
3355
3956
  if (!token) {
3356
3957
  return { ok: false, reason: "no API key \u2014 pass --api-key or run `aex login`" };
3357
3958
  }
3358
- return { ok: true, flags: { apiKey: token, aexUrl: resolvedUrl, debug: debug2 }, rest };
3959
+ return { ok: true, flags: { apiKey: token, aexUrl: resolvedUrl, debug: debug2, json }, rest };
3359
3960
  }
3360
3961
  function describeApiError(err2) {
3361
3962
  if (err2 instanceof AexApiError) {
@@ -3428,44 +4029,6 @@ function remedyForNetworkCode(code) {
3428
4029
  return void 0;
3429
4030
  }
3430
4031
  }
3431
- function suggest(input, candidates) {
3432
- const needle = input.trim();
3433
- if (!needle)
3434
- return void 0;
3435
- const lower = needle.toLowerCase();
3436
- const prefixHits = candidates.filter((c) => c.toLowerCase().startsWith(lower));
3437
- if (prefixHits.length === 1)
3438
- return prefixHits[0];
3439
- let best;
3440
- let bestDist = Number.POSITIVE_INFINITY;
3441
- for (const candidate of candidates) {
3442
- const dist = levenshtein(needle, candidate);
3443
- if (dist < bestDist) {
3444
- bestDist = dist;
3445
- best = candidate;
3446
- }
3447
- }
3448
- return bestDist <= 2 ? best : void 0;
3449
- }
3450
- function levenshtein(a, b) {
3451
- const m = a.length;
3452
- const n = b.length;
3453
- if (m === 0)
3454
- return n;
3455
- if (n === 0)
3456
- return m;
3457
- let prev = Array.from({ length: n + 1 }, (_, j) => j);
3458
- let curr = new Array(n + 1);
3459
- for (let i2 = 1; i2 <= m; i2++) {
3460
- curr[0] = i2;
3461
- for (let j = 1; j <= n; j++) {
3462
- const cost = a[i2 - 1] === b[j - 1] ? 0 : 1;
3463
- curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
3464
- }
3465
- [prev, curr] = [curr, prev];
3466
- }
3467
- return prev[n];
3468
- }
3469
4032
  function makeHttpClient(io2, flags) {
3470
4033
  return new HttpClient({
3471
4034
  baseUrl: flags.aexUrl,
@@ -3665,18 +4228,816 @@ async function runOutputsSyncCmd(io2, dirs) {
3665
4228
  return SUCCESS;
3666
4229
  }
3667
4230
 
3668
- // dist/host/run-cmd.js
3669
- import { resolve as resolvePath } from "node:path";
3670
- var DEFAULT_SESSION_IDLE_TTL = "3m";
3671
- async function runRunCmd(io2, argv) {
3672
- if (await refuseInsideManagedRun(io2, "run"))
3673
- return USAGE_ERR;
3674
- const common = await resolveCommonHostFlags(io2, argv);
3675
- if (!common.ok) {
3676
- io2.stderr(`${common.reason}
3677
- `);
3678
- return USAGE_ERR;
3679
- }
4231
+ // dist/host/registry.js
4232
+ var COMMON_HOST_FLAGS = ["--api-key", "--aex-url", "--json", "--debug"];
4233
+ var RUN_FLAGS = [
4234
+ "--provider",
4235
+ "--model",
4236
+ "--system",
4237
+ "--prompt",
4238
+ "--config",
4239
+ "--skill",
4240
+ "--tool",
4241
+ "--agents-md",
4242
+ "--file",
4243
+ "--mcp",
4244
+ "--mcp-auth",
4245
+ "--metadata",
4246
+ "--runtime-size",
4247
+ "--run-timeout",
4248
+ "--idempotency-key",
4249
+ "--webhook",
4250
+ "--follow",
4251
+ "--timeout"
4252
+ ];
4253
+ var OUTPUTS_SUBVERBS = ["read", "download", "link", "find", "search"];
4254
+ var CLI_VERBS = [
4255
+ {
4256
+ name: "run",
4257
+ summary: "One-shot: open a session, send the prompt as the first turn (delegates to the SDK).",
4258
+ usage: [
4259
+ "aex run --model M --prompt P [--system S] [--provider name] --<provider>-api-key K",
4260
+ "aex run --config <run.json> --<provider>-api-key K",
4261
+ " --skill @file Attach a workspace skill bundle (repeatable)",
4262
+ " --tool @file.js Attach a custom tool module (repeatable)",
4263
+ " --agents-md @file Attach an AGENTS.md brief (repeatable)",
4264
+ " --file @path Mount a file into /workspace (repeatable)",
4265
+ " --mcp name=url MCP server (repeatable); --mcp-auth name=Hdr:Val for headers",
4266
+ " --metadata key=value Submission metadata (repeatable)",
4267
+ " --runtime-size <s> Managed runtime preset",
4268
+ " --run-timeout <dur> Server-side run deadline (validated client-side by the SDK)",
4269
+ " --webhook <url> Per-run terminal callback (https)",
4270
+ " --follow Stream the turn's events until the session parks"
4271
+ ],
4272
+ flags: RUN_FLAGS
4273
+ },
4274
+ {
4275
+ name: "status",
4276
+ summary: "Print a session/run record (GET /api/sessions/:id).",
4277
+ usage: ["aex status <session-id>"]
4278
+ },
4279
+ {
4280
+ name: "deliveries",
4281
+ summary: "List a run's webhook delivery attempts.",
4282
+ usage: ["aex deliveries <session-id>"]
4283
+ },
4284
+ {
4285
+ name: "wait",
4286
+ summary: "Poll a session until it parks; exit code reflects the outcome.",
4287
+ usage: ["aex wait <session-id> [--timeout 8m] [--interval 2s]"],
4288
+ flags: ["--timeout", "--interval"]
4289
+ },
4290
+ {
4291
+ name: "events",
4292
+ summary: "List (or --follow) a session's events as NDJSON.",
4293
+ usage: ["aex events <session-id> [--follow] [--timeout 8m]"],
4294
+ flags: ["--follow", "--timeout"]
4295
+ },
4296
+ {
4297
+ name: "tail",
4298
+ summary: "Live human-readable follow over the coordinator event stream.",
4299
+ usage: ["aex tail <session-id> [--filter <type|source>] [--logs] [--from <seq>] [--settle] [--timeout <dur>]"],
4300
+ flags: ["--filter", "--logs", "--from", "--settle", "--timeout"]
4301
+ },
4302
+ {
4303
+ name: "inspect",
4304
+ summary: "One-shot full-timeline render + jump-to-failure summary.",
4305
+ usage: ["aex inspect <session-id> [--filter <type|source>] [--logs] [--timeout <dur>]"],
4306
+ flags: ["--filter", "--logs", "--timeout"]
4307
+ },
4308
+ {
4309
+ name: "outputs",
4310
+ summary: "List a session's captured outputs, or read/download/link/find/search one file.",
4311
+ usage: [
4312
+ "aex outputs <session-id> List captured outputs (NDJSON)",
4313
+ "aex outputs read <session-id> <path> Read one file as capped text",
4314
+ "aex outputs download <session-id> <path> [--out] Download one file's raw bytes",
4315
+ "aex outputs link <session-id> <path> Mint a temporary download URL",
4316
+ "aex outputs find <session-id> [--name S] [--ext E] [--type T]",
4317
+ "aex outputs search [--query S] [--name S] [--ext E] [--run-id ID] Cross-run"
4318
+ ],
4319
+ flags: ["--out", "--name", "--ext", "--type", "--content-type", "--query", "--run-id", "--limit", "--max-bytes"],
4320
+ subverbs: OUTPUTS_SUBVERBS
4321
+ },
4322
+ {
4323
+ name: "download",
4324
+ summary: "Download a session's content as a zip (whole or one namespace).",
4325
+ usage: ["aex download <session-id> [--only outputs|events|metadata] [--out path]"],
4326
+ flags: ["--only", "--out"]
4327
+ },
4328
+ {
4329
+ name: "cancel",
4330
+ summary: "Cancel a running session.",
4331
+ usage: ["aex cancel <session-id>"]
4332
+ },
4333
+ {
4334
+ name: "delete",
4335
+ summary: "Delete a session.",
4336
+ usage: ["aex delete <session-id>"]
4337
+ },
4338
+ {
4339
+ name: "delete-asset",
4340
+ summary: "Delete a workspace asset blob by hash.",
4341
+ usage: ["aex delete-asset <assetId|hash>"]
4342
+ },
4343
+ {
4344
+ name: "runs",
4345
+ summary: "List the workspace's runs (newest first).",
4346
+ usage: ["aex runs [--limit N] [--since ISO]"],
4347
+ flags: ["--limit", "--since"]
4348
+ },
4349
+ {
4350
+ name: "sessions",
4351
+ summary: "List the workspace's sessions (newest first).",
4352
+ usage: ["aex sessions [--limit N]"],
4353
+ flags: ["--limit"]
4354
+ },
4355
+ {
4356
+ name: "whoami",
4357
+ summary: "Resolve the API key to its workspace + scopes.",
4358
+ usage: ["aex whoami [--json]"]
4359
+ },
4360
+ {
4361
+ name: "billing",
4362
+ summary: "Show balance / spend / cap; ledger, upgrade, and portal sub-verbs.",
4363
+ usage: [
4364
+ "aex billing [--json]",
4365
+ "aex billing ledger [--limit N]",
4366
+ "aex billing upgrade pro|team",
4367
+ "aex billing portal"
4368
+ ],
4369
+ subverbs: ["ledger", "upgrade", "portal"]
4370
+ },
4371
+ {
4372
+ name: "webhooks",
4373
+ summary: "Reveal the workspace webhook signing secret.",
4374
+ usage: ["aex webhooks secret"],
4375
+ subverbs: ["secret"]
4376
+ },
4377
+ {
4378
+ name: "login",
4379
+ summary: "Persist the API key + url so other verbs need no --api-key.",
4380
+ usage: ["aex login --api-key T [--aex-url U]"]
4381
+ },
4382
+ {
4383
+ name: "logout",
4384
+ summary: "Clear the stored token.",
4385
+ usage: ["aex logout"]
4386
+ },
4387
+ {
4388
+ name: "auth",
4389
+ summary: "Show the resolved config (token never printed).",
4390
+ usage: ["aex auth status"],
4391
+ subverbs: ["status"]
4392
+ },
4393
+ {
4394
+ name: "models",
4395
+ summary: "List models + default provider (no token needed).",
4396
+ usage: ["aex models list [--json]"]
4397
+ },
4398
+ {
4399
+ name: "providers",
4400
+ summary: "List providers + their models (no token needed).",
4401
+ usage: ["aex providers list [--json]"]
4402
+ },
4403
+ {
4404
+ name: "tools",
4405
+ summary: "List builtin tools (no token needed).",
4406
+ usage: ["aex tools list [--json]"]
4407
+ },
4408
+ {
4409
+ name: "runtime-sizes",
4410
+ summary: "List managed runtime presets (no token needed).",
4411
+ usage: ["aex runtime-sizes list [--json]"]
4412
+ },
4413
+ {
4414
+ name: "debug",
4415
+ summary: "Operator: read the AWS plane directly (AWS creds, not --api-key).",
4416
+ usage: ["aex debug <run-id> [--plane dev|prd] [--region eu-west-2] [--cloudwatch] [--with-outputs]"]
4417
+ }
4418
+ ];
4419
+ var VERB_BY_NAME = new Map(CLI_VERBS.map((v) => [v.name, v]));
4420
+ var CLI_VERB_NAMES = CLI_VERBS.map((v) => v.name);
4421
+ function findVerbSpec(name) {
4422
+ return VERB_BY_NAME.get(name);
4423
+ }
4424
+ function wantsVerbHelp(argv) {
4425
+ return argv.includes("--help") || argv.includes("-h");
4426
+ }
4427
+ function renderVerbHelp(spec) {
4428
+ const lines = [`aex ${spec.name} \u2014 ${spec.summary}`, "", "Usage:"];
4429
+ for (const u of spec.usage)
4430
+ lines.push(` ${u}`);
4431
+ lines.push("", `Common flags: ${COMMON_HOST_FLAGS.join(" ")}`);
4432
+ return lines.join("\n") + "\n";
4433
+ }
4434
+
4435
+ // dist/host/run-cmd.js
4436
+ import { resolve as resolvePath } from "node:path";
4437
+
4438
+ // dist/host/run-submit.js
4439
+ var TEXT = new TextEncoder();
4440
+ var ZIP_EPOCH = new Date(Date.UTC(1980, 0, 1));
4441
+ var UPLOAD_CONCURRENCY = 5;
4442
+ async function submitCliRun(http, fetchImpl, options) {
4443
+ const request = await buildSessionCreateRequest(http, fetchImpl, options);
4444
+ const submitted = await operations_exports.submit(http, request, { idempotencyKey: operations_exports.resolveIdempotencyKey(options.idempotencyKey) });
4445
+ return submitted.session;
4446
+ }
4447
+ async function buildCliSkill(content, source) {
4448
+ const front = extractSkillFrontmatter("Skill.fromContent", { "SKILL.md": content });
4449
+ const name = deriveSkillName("Skill.fromContent", front.name, void 0, void 0);
4450
+ const description = front.description;
4451
+ if (typeof description !== "string" || description.trim().length === 0) {
4452
+ throw new Error(`${source}: a skill description is required in SKILL.md frontmatter`);
4453
+ }
4454
+ if (description.length > 2048) {
4455
+ throw new Error(`${source}: description must be <= 2048 chars`);
4456
+ }
4457
+ const bytes = bundleSkillFiles({ "SKILL.md": content });
4458
+ return {
4459
+ name,
4460
+ description,
4461
+ contentHash: await hashBytes(bytes),
4462
+ bytes
4463
+ };
4464
+ }
4465
+ async function buildCliTool(args) {
4466
+ const inputSchema = { type: "object", properties: {}, additionalProperties: true };
4467
+ const manifest = normalizeToolManifest("Tool.fromFiles", {
4468
+ name: args.name,
4469
+ description: args.description,
4470
+ input_schema: inputSchema,
4471
+ entry: args.entry
4472
+ }, { [args.entry]: args.content });
4473
+ const bytes = bundleToolFiles({ [args.entry]: args.content }, manifest);
4474
+ return {
4475
+ ref: {
4476
+ kind: "asset",
4477
+ contentHash: await hashBytes(bytes),
4478
+ ...manifest
4479
+ },
4480
+ bytes
4481
+ };
4482
+ }
4483
+ async function buildCliAgentsMd(content, name) {
4484
+ if (typeof content !== "string" || content.length === 0) {
4485
+ throw new Error("AgentsMd.fromContent: content must be a non-empty string");
4486
+ }
4487
+ if (!/^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$/.test(name)) {
4488
+ throw new Error("AgentsMd.fromContent: name must be a lowercase workspace slug");
4489
+ }
4490
+ const bytes = zipSync({ "AGENTS.md": [TEXT.encode(content), { mtime: ZIP_EPOCH }] }, { level: 6 });
4491
+ return { name, contentHash: await hashBytes(bytes), bytes };
4492
+ }
4493
+ async function buildCliFile(args) {
4494
+ const filename = sanitiseFilename(args.name);
4495
+ if (filename === void 0) {
4496
+ throw new Error(`File.fromBytes: name ${JSON.stringify(args.name)} is not a valid filename`);
4497
+ }
4498
+ const bytes = TEXT.encode(args.content);
4499
+ if (bytes.byteLength === 0) {
4500
+ throw new Error("File.fromBytes: bytes must be a non-empty Uint8Array");
4501
+ }
4502
+ const zip = zipSync({ [filename]: [bytes, { mtime: ZIP_EPOCH }] }, { level: 6 });
4503
+ return {
4504
+ name: slugFromFilename(filename),
4505
+ contentHash: await hashBytes(zip),
4506
+ mountPath: DEFAULT_FILE_MOUNT_PATH,
4507
+ bytes: zip
4508
+ };
4509
+ }
4510
+ function toCliSessionEnvironment(env) {
4511
+ if (!env)
4512
+ return void 0;
4513
+ const out = {
4514
+ ...env.networking ? { networking: env.networking } : {},
4515
+ ...env.packages ? { packages: env.packages } : {},
4516
+ ...env.envVars ? { variables: env.envVars } : {}
4517
+ };
4518
+ return Object.keys(out).length === 0 ? void 0 : out;
4519
+ }
4520
+ async function buildSessionCreateRequest(http, fetchImpl, options) {
4521
+ const input = normaliseSessionInput(options.message);
4522
+ const provider = resolveModelProvider(options.model, options.provider);
4523
+ validateApiKeys(options.apiKeys, provider);
4524
+ try {
4525
+ parseRunTimeout(options.overrides?.timeout);
4526
+ if (options.webhook !== void 0)
4527
+ parseRunWebhook(options.webhook);
4528
+ } catch (err2) {
4529
+ throw new Error(err2 instanceof Error ? err2.message : String(err2));
4530
+ }
4531
+ const limitsInput = {};
4532
+ if (options.overrides?.maxSpendUsd !== void 0)
4533
+ limitsInput.maxSpendUsd = options.overrides.maxSpendUsd;
4534
+ if (options.overrides?.maxTurns !== void 0)
4535
+ limitsInput.maxTurns = options.overrides.maxTurns;
4536
+ const limits = parseRunLimits(Object.keys(limitsInput).length > 0 ? limitsInput : void 0);
4537
+ const [preparedTools, preparedSkills, preparedAgentsMd, preparedFiles] = await Promise.all([
4538
+ prepareTools(http, fetchImpl, options.tools ?? []),
4539
+ prepareSkills(http, fetchImpl, options.skills ?? []),
4540
+ prepareAgentsMd(http, fetchImpl, options.agentsMd ?? []),
4541
+ prepareFiles(http, fetchImpl, options.files ?? [])
4542
+ ]);
4543
+ const { submissionMcpServers, mergedMcpSecrets } = mergeMcpServers(options.mcpServers ?? []);
4544
+ const environment = sessionEnvironmentForWire(options.environment);
4545
+ const submission = {
4546
+ model: options.model,
4547
+ ...options.system ? { system: options.system } : {},
4548
+ tools: [...preparedTools.builtinNames, ...preparedTools.refs],
4549
+ ...preparedSkills.length > 0 ? { skills: preparedSkills } : {},
4550
+ agentsMd: preparedAgentsMd,
4551
+ files: preparedFiles,
4552
+ mcpServers: submissionMcpServers,
4553
+ ...environment ? { environment } : {},
4554
+ ...options.metadata ? { metadata: options.metadata } : {}
4555
+ };
4556
+ const secrets = {
4557
+ ...options.apiKeys ? { apiKeys: options.apiKeys } : {},
4558
+ ...mergedMcpSecrets.length > 0 ? { mcpServers: mergedMcpSecrets } : {}
4559
+ };
4560
+ return {
4561
+ provider,
4562
+ submission,
4563
+ input,
4564
+ ...options.runtime ? { runtimeSize: options.runtime } : {},
4565
+ ...options.overrides?.timeout ? { timeout: options.overrides.timeout } : {},
4566
+ ...limits ? { limits } : {},
4567
+ retention: { idleTtl: options.overrides?.idleTtl ?? "3m" },
4568
+ ...options.webhook ? { webhook: options.webhook } : {},
4569
+ secrets
4570
+ };
4571
+ }
4572
+ function normaliseSessionInput(input) {
4573
+ if (typeof input === "string") {
4574
+ if (!input)
4575
+ throw new Error("Aex.submit: message must be a non-empty string");
4576
+ return input;
4577
+ }
4578
+ if (!Array.isArray(input) || input.length === 0) {
4579
+ throw new Error("Aex.submit: message must be a non-empty string or string array");
4580
+ }
4581
+ for (const segment of input) {
4582
+ if (typeof segment !== "string" || !segment) {
4583
+ throw new Error("Aex.submit: message segments must be non-empty strings");
4584
+ }
4585
+ }
4586
+ return [...input];
4587
+ }
4588
+ function validateApiKeys(apiKeys, provider) {
4589
+ const key = apiKeys?.[provider];
4590
+ if (typeof key !== "string" || key.length === 0) {
4591
+ throw new Error(`Aex.submit: a provider API key is required for provider ${provider}; pass apiKeys.${provider}`);
4592
+ }
4593
+ }
4594
+ async function prepareTools(http, fetchImpl, tools) {
4595
+ const prepared = await mapWithConcurrency(tools, UPLOAD_CONCURRENCY, async (entry, i2) => {
4596
+ if (typeof entry === "string") {
4597
+ if (!BUILTIN_TOOL_NAMES.includes(entry)) {
4598
+ throw new Error(`aex: tools[${i2}] (${JSON.stringify(entry)}) is not a builtin tool name`);
4599
+ }
4600
+ return { kind: "builtin", name: entry };
4601
+ }
4602
+ const uploaded = await uploadAsset(http, fetchImpl, {
4603
+ bytes: entry.bytes,
4604
+ hash: entry.ref.contentHash,
4605
+ contentType: "application/zip"
4606
+ });
4607
+ const { contentHash: _contentHash, ...ref } = entry.ref;
4608
+ void _contentHash;
4609
+ return { kind: "ref", ref: { ...ref, assetId: uploaded.assetId } };
4610
+ });
4611
+ const refs = [];
4612
+ const seenBuiltins = /* @__PURE__ */ new Set();
4613
+ const builtinNames = [];
4614
+ for (const item of prepared) {
4615
+ if (item.kind === "builtin") {
4616
+ if (!seenBuiltins.has(item.name)) {
4617
+ seenBuiltins.add(item.name);
4618
+ builtinNames.push(item.name);
4619
+ }
4620
+ } else {
4621
+ refs.push(item.ref);
4622
+ }
4623
+ }
4624
+ return { refs, builtinNames };
4625
+ }
4626
+ async function prepareSkills(http, fetchImpl, skills) {
4627
+ if (skills.length > SKILLS_MAX) {
4628
+ throw new Error(`aex: skills exceeds the ${SKILLS_MAX}-skill limit (got ${skills.length})`);
4629
+ }
4630
+ const seen = /* @__PURE__ */ new Set();
4631
+ for (const skill of skills) {
4632
+ if (seen.has(skill.name))
4633
+ throw new Error(`aex: skills duplicate name: ${skill.name}`);
4634
+ seen.add(skill.name);
4635
+ }
4636
+ return mapWithConcurrency(skills, UPLOAD_CONCURRENCY, async (skill) => {
4637
+ await uploadAsset(http, fetchImpl, { bytes: skill.bytes, hash: skill.contentHash, contentType: "application/zip" });
4638
+ await operations_exports.upsertSkill(http, {
4639
+ name: skill.name,
4640
+ contentHash: skill.contentHash,
4641
+ description: skill.description,
4642
+ sizeBytes: skill.bytes.byteLength
4643
+ });
4644
+ return { kind: "skill", name: skill.name };
4645
+ });
4646
+ }
4647
+ async function prepareAgentsMd(http, fetchImpl, agentsMds) {
4648
+ return mapWithConcurrency(agentsMds, UPLOAD_CONCURRENCY, async (entry) => {
4649
+ const uploaded = await uploadAsset(http, fetchImpl, {
4650
+ bytes: entry.bytes,
4651
+ hash: entry.contentHash,
4652
+ contentType: "application/zip"
4653
+ });
4654
+ return { kind: "asset", assetId: uploaded.assetId, name: entry.name };
4655
+ });
4656
+ }
4657
+ async function prepareFiles(http, fetchImpl, files) {
4658
+ return mapWithConcurrency(files, UPLOAD_CONCURRENCY, async (entry) => {
4659
+ const uploaded = await uploadAsset(http, fetchImpl, {
4660
+ bytes: entry.bytes,
4661
+ hash: entry.contentHash,
4662
+ contentType: "application/zip"
4663
+ });
4664
+ return { kind: "asset", assetId: uploaded.assetId, name: entry.name, mountPath: entry.mountPath };
4665
+ });
4666
+ }
4667
+ function mergeMcpServers(inputs) {
4668
+ const submissionMcpServers = [];
4669
+ const mergedMcpSecrets = [];
4670
+ for (const entry of inputs) {
4671
+ submissionMcpServers.push({ name: entry.name, url: entry.url });
4672
+ if (entry.headers && Object.keys(entry.headers).length > 0) {
4673
+ mergedMcpSecrets.push({ name: entry.name, url: entry.url, headers: { ...entry.headers } });
4674
+ }
4675
+ }
4676
+ return { submissionMcpServers, mergedMcpSecrets };
4677
+ }
4678
+ function sessionEnvironmentForWire(environment) {
4679
+ if (environment === void 0)
4680
+ return void 0;
4681
+ const { variables, ...rest } = environment;
4682
+ const out = {
4683
+ ...rest,
4684
+ ...variables !== void 0 ? { envVars: variables } : {}
4685
+ };
4686
+ return Object.keys(out).length === 0 ? void 0 : out;
4687
+ }
4688
+ async function uploadAsset(http, fetchImpl, args) {
4689
+ const expected = args.hash.startsWith("sha256:") ? args.hash.slice("sha256:".length) : args.hash;
4690
+ const actual = await sha256Hex(args.bytes);
4691
+ if (actual !== expected) {
4692
+ throw new Error(`uploadAsset: client-side hash mismatch: computed sha256:${actual} but caller declared ${args.hash}`);
4693
+ }
4694
+ const contentHash = `sha256:${actual}`;
4695
+ const presign = await http.request("/assets/presign", {
4696
+ method: "POST",
4697
+ headers: { "content-type": "application/json" },
4698
+ body: JSON.stringify({ hash: contentHash, sizeBytes: args.bytes.byteLength })
4699
+ });
4700
+ if (presign.exists) {
4701
+ const storedHash2 = presign.contentHash ?? contentHash;
4702
+ return {
4703
+ assetId: presign.assetId ?? assetIdFromContentHash(storedHash2),
4704
+ contentHash: storedHash2,
4705
+ sizeBytes: presign.sizeBytes ?? args.bytes.byteLength,
4706
+ exists: true
4707
+ };
4708
+ }
4709
+ if (!presign.uploadUrl) {
4710
+ throw new Error("uploadAsset: presign returned no uploadUrl and exists:false");
4711
+ }
4712
+ const doFetch = fetchImpl ?? globalThis.fetch;
4713
+ const put = await doFetch(presign.uploadUrl, {
4714
+ method: "PUT",
4715
+ headers: {
4716
+ "content-type": args.contentType ?? "application/zip",
4717
+ ...presign.requiredHeaders ?? {}
4718
+ },
4719
+ body: args.bytes
4720
+ });
4721
+ if (!put.ok) {
4722
+ throw new Error(`uploadAsset: direct upload PUT failed with status ${put.status}`);
4723
+ }
4724
+ const fin = await http.request("/assets/finalize", {
4725
+ method: "POST",
4726
+ headers: { "content-type": "application/json" },
4727
+ body: JSON.stringify({ hash: contentHash, sizeBytes: args.bytes.byteLength })
4728
+ });
4729
+ const storedHash = fin.contentHash ?? presign.contentHash ?? contentHash;
4730
+ return {
4731
+ assetId: fin.assetId ?? presign.assetId ?? assetIdFromContentHash(storedHash),
4732
+ contentHash: storedHash,
4733
+ sizeBytes: fin.sizeBytes ?? args.bytes.byteLength,
4734
+ exists: false
4735
+ };
4736
+ }
4737
+ function bundleSkillFiles(files) {
4738
+ const collected = collectBundleFiles("Skill bundle", files, true);
4739
+ return zipCollected(collected);
4740
+ }
4741
+ function bundleToolFiles(files, manifest) {
4742
+ const collected = collectBundleFiles("Tool bundle", files, false);
4743
+ const entryPath = validateSkillBundleEntry({ path: manifest.entry, size: 0 }).path;
4744
+ if (!collected.has(entryPath)) {
4745
+ throw new Error(`Tool bundle entry "${entryPath}" must exist in files`);
4746
+ }
4747
+ if (collected.has("tool.json")) {
4748
+ throw new Error('Tool bundle files must not include reserved "tool.json"; pass manifest fields instead');
4749
+ }
4750
+ collected.set("tool.json", TEXT.encode(`${JSON.stringify(manifest, null, 2)}
4751
+ `));
4752
+ return zipCollected(collected);
4753
+ }
4754
+ function collectBundleFiles(kind, files, requireSkillMd) {
4755
+ const entries = Object.entries(files);
4756
+ if (entries.length === 0)
4757
+ throw new Error(`${kind} files map cannot be empty`);
4758
+ if (entries.length > SKILL_BUNDLE_LIMITS.maxFiles) {
4759
+ throw new Error(`${kind} exceeds ${SKILL_BUNDLE_LIMITS.maxFiles} file limit (got ${entries.length})`);
4760
+ }
4761
+ const collected = /* @__PURE__ */ new Map();
4762
+ let hasSkillMd = false;
4763
+ let total = 0;
4764
+ for (const [rawPath, contents] of entries) {
4765
+ const bytes = typeof contents === "string" ? TEXT.encode(contents) : contents;
4766
+ if (!(bytes instanceof Uint8Array))
4767
+ throw new Error(`${kind} file "${rawPath}" must be a string or Uint8Array`);
4768
+ const entry = validateSkillBundleEntry({ path: rawPath, size: bytes.byteLength });
4769
+ if (entry.path === "SKILL.md")
4770
+ hasSkillMd = true;
4771
+ total += bytes.byteLength;
4772
+ if (total > SKILL_BUNDLE_LIMITS.maxDecompressedBytes) {
4773
+ throw new Error(`${kind} exceeds decompressed cap of ${SKILL_BUNDLE_LIMITS.maxDecompressedBytes} bytes`);
4774
+ }
4775
+ if (collected.has(entry.path))
4776
+ throw new Error(`${kind} contains duplicate path: ${entry.path}`);
4777
+ collected.set(entry.path, bytes);
4778
+ }
4779
+ if (requireSkillMd && !hasSkillMd) {
4780
+ throw new Error('Skill bundle must contain a "SKILL.md" file at the root.');
4781
+ }
4782
+ return collected;
4783
+ }
4784
+ function zipCollected(collected) {
4785
+ const sorted = [...collected.entries()].sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
4786
+ const zippable = {};
4787
+ for (const [path, bytes] of sorted) {
4788
+ zippable[path] = [bytes, { mtime: ZIP_EPOCH }];
4789
+ }
4790
+ const zip = zipSync(zippable, { level: 6 });
4791
+ if (zip.byteLength > SKILL_BUNDLE_LIMITS.maxCompressedBytes) {
4792
+ throw new Error(`bundle exceeds compressed cap of ${SKILL_BUNDLE_LIMITS.maxCompressedBytes} bytes (got ${zip.byteLength})`);
4793
+ }
4794
+ return zip;
4795
+ }
4796
+ function normalizeToolManifest(source, input, files) {
4797
+ if (typeof input.name !== "string" || !TOOL_NAME_PATTERN.test(input.name)) {
4798
+ throw new Error(`${source}: name must match ${TOOL_NAME_PATTERN.source}`);
4799
+ }
4800
+ if (input.name.includes("__")) {
4801
+ throw new Error(`${source}: name must not contain "__"; that separator is reserved for MCP tools`);
4802
+ }
4803
+ if (typeof input.description !== "string" || input.description.trim().length === 0 || input.description.length > 2048) {
4804
+ throw new Error(`${source}: description must be non-empty and <= 2048 chars`);
4805
+ }
4806
+ const inputSchema = input.input_schema;
4807
+ if (!inputSchema || typeof inputSchema !== "object" || Array.isArray(inputSchema)) {
4808
+ throw new Error(`${source}: inputSchema must be a JSON Schema object`);
4809
+ }
4810
+ if (inputSchema.type !== "object") {
4811
+ throw new Error(`${source}: inputSchema.type must be "object"`);
4812
+ }
4813
+ const entry = normaliseSkillBundlePath(input.entry);
4814
+ if (!/\.(?:js|mjs|cjs)$/i.test(entry.split("/").pop() ?? entry)) {
4815
+ throw new Error(`${source}: entry must be a JS module (.js/.mjs/.cjs)`);
4816
+ }
4817
+ if (!(entry in files) && !(input.entry in files)) {
4818
+ throw new Error(`${source}: entry ${JSON.stringify(input.entry)} is not present in files`);
4819
+ }
4820
+ return { ...input, entry };
4821
+ }
4822
+ function extractSkillFrontmatter(source, files) {
4823
+ const raw = files["SKILL.md"];
4824
+ if (raw === void 0)
4825
+ throw new Error(`${source}: the skill bundle must contain a SKILL.md at its root`);
4826
+ const text = typeof raw === "string" ? raw : new TextDecoder().decode(raw);
4827
+ return parseSkillFrontmatter(text);
4828
+ }
4829
+ function parseSkillFrontmatter(text) {
4830
+ const src = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
4831
+ const match = /^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/.exec(src);
4832
+ if (!match)
4833
+ return {};
4834
+ const out = {};
4835
+ for (const line of match[1].split(/\r?\n/)) {
4836
+ const kv = /^([A-Za-z0-9_-]+)[ \t]*:[ \t]*(.*)$/.exec(line);
4837
+ if (!kv)
4838
+ continue;
4839
+ const key = kv[1].toLowerCase();
4840
+ if (key !== "name" && key !== "description")
4841
+ continue;
4842
+ let value = kv[2].trim();
4843
+ if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
4844
+ value = value.slice(1, -1);
4845
+ }
4846
+ if (value.length > 0)
4847
+ out[key] = value;
4848
+ }
4849
+ return out;
4850
+ }
4851
+ function deriveSkillName(source, frontmatterName, explicitName, dirBasename) {
4852
+ let name = explicitName ?? frontmatterName;
4853
+ if (name === void 0 && dirBasename !== void 0) {
4854
+ const slug = slugifyName(dirBasename);
4855
+ if (slug.length > 0)
4856
+ name = slug;
4857
+ }
4858
+ if (typeof name !== "string" || name.length === 0) {
4859
+ throw new Error(`${source}: a skill name is required`);
4860
+ }
4861
+ if (!SKILL_NAME_PATTERN.test(name)) {
4862
+ throw new Error(`${source}: name ${JSON.stringify(name)} must match ${SKILL_NAME_PATTERN.source}`);
4863
+ }
4864
+ if (name.includes("__")) {
4865
+ throw new Error(`${source}: name must not contain "__"; that separator is reserved for MCP tools`);
4866
+ }
4867
+ if (SKILL_RESERVED_NAMES.has(name)) {
4868
+ throw new Error(`${source}: name ${JSON.stringify(name)} is reserved (${[...SKILL_RESERVED_NAMES].join(", ")})`);
4869
+ }
4870
+ return name;
4871
+ }
4872
+ function sanitiseFilename(name) {
4873
+ if (typeof name !== "string" || name.length === 0 || name.length > 255)
4874
+ return void 0;
4875
+ if (name.includes("/") || name.includes("\\") || name.includes("\0"))
4876
+ return void 0;
4877
+ if (name === "." || name === "..")
4878
+ return void 0;
4879
+ return name;
4880
+ }
4881
+ function slugFromFilename(filename) {
4882
+ const stem = filename.includes(".") ? filename.slice(0, filename.lastIndexOf(".")) : filename;
4883
+ const slug = slugifyName(stem);
4884
+ return slug.length > 0 ? slug : "file";
4885
+ }
4886
+ function slugifyName(input) {
4887
+ return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
4888
+ }
4889
+ async function hashBytes(bytes) {
4890
+ return `sha256:${await sha256Hex(bytes)}`;
4891
+ }
4892
+ async function sha256Hex(bytes) {
4893
+ const subtle = globalThis.crypto?.subtle;
4894
+ if (!subtle) {
4895
+ throw new Error("sha256: globalThis.crypto.subtle is not available");
4896
+ }
4897
+ const view = new Uint8Array(bytes.byteLength);
4898
+ view.set(bytes);
4899
+ const digest = await subtle.digest("SHA-256", view.buffer);
4900
+ return bufferToHex(digest);
4901
+ }
4902
+ function bufferToHex(buffer) {
4903
+ const view = new Uint8Array(buffer);
4904
+ let out = "";
4905
+ for (const byte of view) {
4906
+ out += byte.toString(16).padStart(2, "0");
4907
+ }
4908
+ return out;
4909
+ }
4910
+ function assetIdFromContentHash(contentHash) {
4911
+ const hex = contentHash.startsWith("sha256:") ? contentHash.slice("sha256:".length) : contentHash;
4912
+ return `asset_${hex}`;
4913
+ }
4914
+ async function mapWithConcurrency(items, limit, fn) {
4915
+ const out = new Array(items.length);
4916
+ let next = 0;
4917
+ const lanes = Array.from({ length: Math.min(Math.max(1, limit), items.length) }, async () => {
4918
+ for (let i2 = next++; i2 < items.length; i2 = next++) {
4919
+ out[i2] = await fn(items[i2], i2);
4920
+ }
4921
+ });
4922
+ await Promise.all(lanes);
4923
+ return out;
4924
+ }
4925
+
4926
+ // dist/host/stream-render.js
4927
+ async function* openEnvelopeStream(io2, http, sessionId, options = {}) {
4928
+ const first = await operations_exports.getSessionCoordinatorTicket(http, sessionId);
4929
+ if (options.debug) {
4930
+ let host = "(invalid)";
4931
+ try {
4932
+ host = new URL(first.wsUrl).host;
4933
+ } catch {
4934
+ }
4935
+ options.debug(`ticket minted: host=${host} expiresInMs=${first.expiresAtMs - Date.now()} from=${options.from ?? 0}`);
4936
+ }
4937
+ yield* streamCoordinatorEvents({
4938
+ wsUrl: first.wsUrl,
4939
+ from: options.from ?? 0,
4940
+ fetchTicket: async () => (await operations_exports.getSessionCoordinatorTicket(http, sessionId)).ticket,
4941
+ ...io2.webSocketFactory ? { webSocketFactory: io2.webSocketFactory } : {},
4942
+ ...options.settleConsistent ? { isTerminal: isRunSettled } : {},
4943
+ ...options.signal ? { signal: options.signal } : {}
4944
+ });
4945
+ }
4946
+ var PREVIEW_MAX = 120;
4947
+ function clip(text, max = PREVIEW_MAX) {
4948
+ const oneLine = text.replace(/\s+/g, " ").trim();
4949
+ return oneLine.length > max ? oneLine.slice(0, max - 1) + "\u2026" : oneLine;
4950
+ }
4951
+ function str2(v) {
4952
+ return typeof v === "string" ? v : v === void 0 || v === null ? "" : JSON.stringify(v);
4953
+ }
4954
+ function argsPreview(e) {
4955
+ const d = e.data;
4956
+ const candidate = d.input ?? d.args ?? d.arguments ?? d.params;
4957
+ if (candidate === void 0)
4958
+ return "";
4959
+ return clip(typeof candidate === "string" ? candidate : JSON.stringify(candidate));
4960
+ }
4961
+ function renderEnvelope(e, options = {}) {
4962
+ if (channelOf(e) === "log" && !options.logs)
4963
+ return null;
4964
+ switch (e.type) {
4965
+ case "RUN_STARTED":
4966
+ return "\u25B6 run started";
4967
+ case "TEXT_MESSAGE_CONTENT": {
4968
+ const delta = toAGUI(e).type === "TEXT_MESSAGE_CONTENT" ? toAGUI(e).delta : "";
4969
+ return delta ? delta : null;
4970
+ }
4971
+ case "TOOL_CALL_START": {
4972
+ const agui = toAGUI(e);
4973
+ const name = agui.type === "TOOL_CALL_START" ? agui.toolCallName : str2(e.data.name);
4974
+ const preview = argsPreview(e);
4975
+ return `\xB7 tool ${name || "(unnamed)"}(${preview})`;
4976
+ }
4977
+ case "TOOL_CALL_RESULT": {
4978
+ const name = str2(e.data.name) || "result";
4979
+ const content = e.data.content;
4980
+ const ok = content === void 0 || content === null ? "ok" : clip(str2(content));
4981
+ return ` \u2190 ${name} ${ok}`;
4982
+ }
4983
+ case "CUSTOM": {
4984
+ const label = e.message ?? str2(e.data.name) ?? "custom";
4985
+ if (e.data.name === "aex.session.idle") {
4986
+ const value = e.data.value;
4987
+ const reason = value && typeof value.reason === "string" ? value.reason : "";
4988
+ if (reason && reason !== "completed")
4989
+ return `[aex] ${label} (${reason})`;
4990
+ }
4991
+ return `[aex] ${label}`;
4992
+ }
4993
+ case "LOG": {
4994
+ const level = e.level ?? "info";
4995
+ return `[${level}] ${e.message ?? str2(e.data.message)}`;
4996
+ }
4997
+ case "RUN_FINISHED":
4998
+ return "\u2713 run finished";
4999
+ case "RUN_ERROR": {
5000
+ const agui = toAGUI(e);
5001
+ const message = agui.type === "RUN_ERROR" ? agui.message : e.message ?? "run error";
5002
+ return `\u2717 run error: ${message}`;
5003
+ }
5004
+ default:
5005
+ return null;
5006
+ }
5007
+ }
5008
+ function parseFilters(tokens) {
5009
+ const flattened = tokens.flatMap((t) => t.split(",")).map((s) => s.trim()).filter(Boolean);
5010
+ if (flattened.length === 0)
5011
+ return {};
5012
+ const types = /* @__PURE__ */ new Set();
5013
+ const sources = /* @__PURE__ */ new Set();
5014
+ for (const tok of flattened) {
5015
+ if (AEX_EVENT_TYPES.includes(tok)) {
5016
+ types.add(tok);
5017
+ } else if (AEX_EVENT_SOURCES.includes(tok)) {
5018
+ sources.add(tok);
5019
+ } else {
5020
+ const hint = suggest(tok, [...AEX_EVENT_TYPES, ...AEX_EVENT_SOURCES]);
5021
+ return {
5022
+ error: `unknown --filter token "${tok}"${hint ? `; did you mean "${hint}"?` : ""} (types: ${AEX_EVENT_TYPES.join(",")}; sources: ${AEX_EVENT_SOURCES.join(",")})`
5023
+ };
5024
+ }
5025
+ }
5026
+ const predicate = (e) => (types.size === 0 || types.has(e.type)) && (sources.size === 0 || sources.has(e.source));
5027
+ return { predicate };
5028
+ }
5029
+
5030
+ // dist/host/run-cmd.js
5031
+ var DEFAULT_SESSION_IDLE_TTL = "3m";
5032
+ async function runRunCmd(io2, argv) {
5033
+ if (await refuseInsideManagedRun(io2, "run"))
5034
+ return USAGE_ERR;
5035
+ const common = await resolveCommonHostFlags(io2, argv);
5036
+ if (!common.ok) {
5037
+ io2.stderr(`${common.reason}
5038
+ `);
5039
+ return USAGE_ERR;
5040
+ }
3680
5041
  let rest = common.rest;
3681
5042
  const providerFlag = takeFlagValue(rest, "--provider");
3682
5043
  if (providerFlag.error) {
@@ -3742,9 +5103,10 @@ async function runRunCmd(io2, argv) {
3742
5103
  }
3743
5104
  rest = runTimeoutFlag.remaining;
3744
5105
  if (runTimeoutFlag.value) {
3745
- const parsed = parseDuration(runTimeoutFlag.value);
3746
- if (parsed.error) {
3747
- io2.stderr(`--run-timeout: ${parsed.error}
5106
+ try {
5107
+ parseRunTimeout(runTimeoutFlag.value);
5108
+ } catch (err2) {
5109
+ io2.stderr(`--run-timeout: ${err2.message}
3748
5110
  `);
3749
5111
  return USAGE_ERR;
3750
5112
  }
@@ -3791,6 +5153,34 @@ async function runRunCmd(io2, argv) {
3791
5153
  return USAGE_ERR;
3792
5154
  }
3793
5155
  rest = promptFlags.remaining;
5156
+ const skillFlags = collectRepeated(rest, "--skill");
5157
+ if (skillFlags.error) {
5158
+ io2.stderr(`${skillFlags.error}
5159
+ `);
5160
+ return USAGE_ERR;
5161
+ }
5162
+ rest = skillFlags.remaining;
5163
+ const toolFlags = collectRepeated(rest, "--tool");
5164
+ if (toolFlags.error) {
5165
+ io2.stderr(`${toolFlags.error}
5166
+ `);
5167
+ return USAGE_ERR;
5168
+ }
5169
+ rest = toolFlags.remaining;
5170
+ const agentsMdFlags = collectRepeated(rest, "--agents-md");
5171
+ if (agentsMdFlags.error) {
5172
+ io2.stderr(`${agentsMdFlags.error}
5173
+ `);
5174
+ return USAGE_ERR;
5175
+ }
5176
+ rest = agentsMdFlags.remaining;
5177
+ const fileFlags = collectRepeated(rest, "--file");
5178
+ if (fileFlags.error) {
5179
+ io2.stderr(`${fileFlags.error}
5180
+ `);
5181
+ return USAGE_ERR;
5182
+ }
5183
+ rest = fileFlags.remaining;
3794
5184
  const mcpFlags = collectRepeatedKv(rest, "--mcp");
3795
5185
  if (mcpFlags.error) {
3796
5186
  io2.stderr(`${mcpFlags.error}
@@ -3842,79 +5232,85 @@ async function runRunCmd(io2, argv) {
3842
5232
  `);
3843
5233
  return USAGE_ERR;
3844
5234
  }
3845
- let runConfig;
3846
- let mcpHeadersFromConfig = /* @__PURE__ */ new Map();
5235
+ let model;
5236
+ let system;
5237
+ let promptArray;
5238
+ let configMcpServers = [];
5239
+ let configEnvironment;
5240
+ let configRuntimeSize;
5241
+ let configTimeout;
5242
+ let metadata;
5243
+ const mcpHeaderBag = /* @__PURE__ */ new Map();
3847
5244
  if (config.value) {
3848
5245
  if (modelFlag.value || systemFlag.value || promptFlags.values.length || Object.keys(mcpFlags.entries).length || Object.keys(metadataFlags.entries).length) {
3849
5246
  io2.stderr("--config cannot be combined with --model/--system/--prompt/--mcp/--metadata\n");
3850
5247
  return USAGE_ERR;
3851
5248
  }
5249
+ let runConfig;
3852
5250
  try {
3853
5251
  const absPath = resolvePath(io2.cwd(), config.value);
3854
5252
  const text = await io2.readFile(absPath);
3855
5253
  const raw = JSON.parse(text);
3856
5254
  const { mcpHeaders, normalised } = stripMcpHeadersForParsing(raw);
3857
- mcpHeadersFromConfig = mcpHeaders;
5255
+ for (const [name, headers] of mcpHeaders)
5256
+ mcpHeaderBag.set(name, headers);
3858
5257
  runConfig = parseRunRequestConfig(normalised);
3859
5258
  } catch (err2) {
3860
5259
  io2.stderr(`failed to load --config: ${err2.message}
3861
5260
  `);
3862
5261
  return USAGE_ERR;
3863
5262
  }
5263
+ model = runConfig.model;
5264
+ system = runConfig.system;
5265
+ promptArray = Array.isArray(runConfig.prompt) ? [...runConfig.prompt] : [runConfig.prompt];
5266
+ configMcpServers = runConfig.mcpServers ?? [];
5267
+ configEnvironment = runConfig.environment;
5268
+ configRuntimeSize = runConfig.runtimeSize;
5269
+ configTimeout = runConfig.timeout;
5270
+ metadata = runConfig.metadata ? { ...runConfig.metadata } : void 0;
3864
5271
  } else {
3865
5272
  if (!modelFlag.value) {
3866
5273
  io2.stderr("--model is required when --config is not provided\n");
3867
5274
  return USAGE_ERR;
3868
5275
  }
3869
- if (!RUN_MODELS.includes(modelFlag.value)) {
3870
- const hint = suggest(modelFlag.value, RUN_MODELS);
3871
- io2.stderr(`--model must be one of: ${RUN_MODELS.join(", ")} (got: ${modelFlag.value})${hint ? `; did you mean "${hint}"?` : ""}
3872
- `);
3873
- return USAGE_ERR;
3874
- }
5276
+ model = modelFlag.value;
3875
5277
  if (promptFlags.values.length === 0) {
3876
5278
  io2.stderr("--prompt is required (repeatable)\n");
3877
5279
  return USAGE_ERR;
3878
5280
  }
3879
- let resolvedPrompt;
3880
5281
  try {
3881
- resolvedPrompt = await Promise.all(promptFlags.values.map((v) => readMaybeFile(io2, v)));
5282
+ promptArray = await Promise.all(promptFlags.values.map((v) => readMaybeFile(io2, v)));
3882
5283
  } catch (err2) {
3883
5284
  io2.stderr(`failed to read --prompt file: ${err2.message}
3884
5285
  `);
3885
5286
  return USAGE_ERR;
3886
5287
  }
3887
- let resolvedSystem;
3888
5288
  if (systemFlag.value !== null) {
3889
5289
  try {
3890
- resolvedSystem = await readMaybeFile(io2, systemFlag.value);
5290
+ system = await readMaybeFile(io2, systemFlag.value);
3891
5291
  } catch (err2) {
3892
5292
  io2.stderr(`failed to read --system file: ${err2.message}
3893
5293
  `);
3894
5294
  return USAGE_ERR;
3895
5295
  }
3896
5296
  }
3897
- const mcpRefs = [];
3898
- for (const [name, url] of Object.entries(mcpFlags.entries)) {
3899
- mcpRefs.push({ name, url });
3900
- }
3901
- runConfig = {
3902
- model: modelFlag.value,
3903
- ...resolvedSystem ? { system: resolvedSystem } : {},
3904
- prompt: resolvedPrompt,
3905
- ...mcpRefs.length > 0 ? { mcpServers: mcpRefs } : {},
3906
- ...Object.keys(metadataFlags.entries).length > 0 ? { metadata: { ...metadataFlags.entries } } : {}
3907
- };
5297
+ configMcpServers = Object.entries(mcpFlags.entries).map(([name, url]) => ({ name, url }));
5298
+ metadata = Object.keys(metadataFlags.entries).length > 0 ? { ...metadataFlags.entries } : void 0;
5299
+ }
5300
+ let provider;
5301
+ try {
5302
+ provider = resolveModelProvider(model, explicitProvider);
5303
+ } catch (err2) {
5304
+ io2.stderr(`--model: ${err2.message}
5305
+ `);
5306
+ return USAGE_ERR;
3908
5307
  }
3909
- const provider = explicitProvider ?? providersForModel(runConfig.model)[0] ?? DEFAULT_RUN_PROVIDER;
3910
5308
  if (!providerKeyValues[provider]) {
3911
- io2.stderr(`--${provider}-api-key is required for provider ${provider}${explicitProvider === void 0 ? ` (inferred from --model ${runConfig.model})` : ""} (the platform does not store provider keys on your behalf)
5309
+ const inferred = explicitProvider === void 0 && providersForModel(model).length > 0;
5310
+ io2.stderr(`--${provider}-api-key is required for provider ${provider}${inferred ? ` (inferred from --model ${model})` : ""} (the platform does not store provider keys on your behalf)
3912
5311
  `);
3913
5312
  return USAGE_ERR;
3914
5313
  }
3915
- const mcpServersForSubmission = [...runConfig.mcpServers ?? []];
3916
- const mcpServerSecrets = [];
3917
- const mcpHeaderBag = new Map(mcpHeadersFromConfig);
3918
5314
  for (const [name, headerSpec] of mcpAuthFlags.entries) {
3919
5315
  const colon = headerSpec.indexOf(":");
3920
5316
  if (colon <= 0 || colon >= headerSpec.length - 1) {
@@ -3938,102 +5334,137 @@ async function runRunCmd(io2, argv) {
3938
5334
  existing[headerName] = headerValue;
3939
5335
  mcpHeaderBag.set(name, existing);
3940
5336
  }
3941
- for (const ref of mcpServersForSubmission) {
3942
- const headers = mcpHeaderBag.get(ref.name);
3943
- if (headers && Object.keys(headers).length > 0) {
3944
- mcpServerSecrets.push({ name: ref.name, url: ref.url, headers });
3945
- }
3946
- }
3947
5337
  for (const name of mcpHeaderBag.keys()) {
3948
- if (!mcpServersForSubmission.some((m) => m.name === name)) {
5338
+ if (!configMcpServers.some((m) => m.name === name)) {
3949
5339
  io2.stderr(`--mcp-auth ${name}: no matching --mcp / mcpServers entry declared
3950
5340
  `);
3951
5341
  return USAGE_ERR;
3952
5342
  }
3953
5343
  }
3954
- const promptArray = Array.isArray(runConfig.prompt) ? [...runConfig.prompt] : [runConfig.prompt];
3955
- const submission = {
3956
- model: runConfig.model,
3957
- ...runConfig.system ? { system: runConfig.system } : {},
3958
- agentsMd: [],
3959
- files: [],
3960
- mcpServers: mcpServersForSubmission,
3961
- tools: [],
3962
- ...runConfig.environment ? { environment: runConfig.environment } : {},
3963
- ...runConfig.metadata ? { metadata: runConfig.metadata } : {}
3964
- };
3965
- const hasAdditionalProviderKeys = Object.keys(providerKeyValues).some((p) => p !== provider);
3966
- const secrets = {
3967
- apiKeys: hasAdditionalProviderKeys ? providerKeyValues : { [provider]: providerKeyValues[provider] },
3968
- ...mcpServerSecrets.length > 0 ? { mcpServers: mcpServerSecrets } : {}
3969
- };
3970
- const request = {
5344
+ const mcpServers = configMcpServers.map((m) => {
5345
+ const headers = mcpHeaderBag.get(m.name);
5346
+ return {
5347
+ name: m.name,
5348
+ url: m.url,
5349
+ ...headers && Object.keys(headers).length > 0 ? { headers } : {}
5350
+ };
5351
+ });
5352
+ let skills;
5353
+ let tools;
5354
+ let agentsMd;
5355
+ let files;
5356
+ try {
5357
+ skills = await Promise.all(skillFlags.values.map((ref) => buildSkill(io2, ref)));
5358
+ tools = await Promise.all(toolFlags.values.map((ref) => buildTool(io2, ref)));
5359
+ agentsMd = await Promise.all(agentsMdFlags.values.map((ref) => buildAgentsMd(io2, ref)));
5360
+ files = await Promise.all(fileFlags.values.map((ref) => buildFile(io2, ref)));
5361
+ } catch (err2) {
5362
+ io2.stderr(`failed to attach asset: ${err2.message}
5363
+ `);
5364
+ return USAGE_ERR;
5365
+ }
5366
+ const environment = toCliSessionEnvironment(configEnvironment);
5367
+ const runtimeSize = runtimeSizeFlag.value ?? configRuntimeSize;
5368
+ const timeout = runTimeoutFlag.value ?? configTimeout;
5369
+ const options = {
5370
+ message: promptArray,
3971
5371
  provider,
3972
- submission,
3973
- secrets,
3974
- retention: { idleTtl: DEFAULT_SESSION_IDLE_TTL },
3975
- ...runtimeSizeFlag.value ? { runtimeSize: runtimeSizeFlag.value } : runConfig.runtimeSize ? { runtimeSize: runConfig.runtimeSize } : {},
3976
- ...runTimeoutFlag.value ? { timeout: runTimeoutFlag.value } : runConfig.timeout ? { timeout: runConfig.timeout } : {},
3977
- ...webhookFlag.value ? { webhook: { url: webhookFlag.value } } : {}
5372
+ model,
5373
+ ...system ? { system } : {},
5374
+ ...skills.length > 0 ? { skills } : {},
5375
+ ...tools.length > 0 ? { tools } : {},
5376
+ ...agentsMd.length > 0 ? { agentsMd } : {},
5377
+ ...files.length > 0 ? { files } : {},
5378
+ ...mcpServers.length > 0 ? { mcpServers } : {},
5379
+ ...metadata ? { metadata } : {},
5380
+ apiKeys: providerKeyValues,
5381
+ ...environment ? { environment } : {},
5382
+ ...runtimeSize ? { runtime: runtimeSize } : {},
5383
+ overrides: {
5384
+ idleTtl: DEFAULT_SESSION_IDLE_TTL,
5385
+ ...timeout ? { timeout } : {}
5386
+ },
5387
+ ...webhookFlag.value ? { webhook: { url: webhookFlag.value } } : {},
5388
+ ...idempotency.value ? { idempotencyKey: idempotency.value } : {}
3978
5389
  };
3979
- const createKey = idempotency.value ?? generateIdempotencyKey();
3980
- const messageKey = `${createKey}:message`;
3981
5390
  const http = makeHttpClient(io2, common.flags);
3982
- let session;
3983
- try {
3984
- session = await operations_exports.createSession(http, request, { idempotencyKey: createKey });
3985
- } catch (err2) {
3986
- const d = describeApiError(err2);
3987
- return emitJsonError(io2, "create_failed", d.message, {
3988
- ...d.status !== void 0 ? { status: d.status } : {},
3989
- ...d.remedy ? { remedy: d.remedy } : {}
3990
- });
5391
+ if (follow.present && !io2.webSocketFactory) {
5392
+ io2.stderr(JSON.stringify({
5393
+ error: "websocket_unavailable",
5394
+ message: "`aex run --follow` needs a global WebSocket (Bun or Node >= 22). Upgrade Node or run with bun."
5395
+ }) + "\n");
5396
+ return USAGE_ERR;
3991
5397
  }
3992
- let accepted;
5398
+ let session;
3993
5399
  try {
3994
- accepted = await operations_exports.sendSessionMessage(http, session.id, { input: promptArray }, { idempotencyKey: messageKey });
5400
+ session = await submitCliRun(http, io2.fetchImpl, options);
3995
5401
  } catch (err2) {
3996
5402
  const d = describeApiError(err2);
3997
- return emitJsonError(io2, "send_failed", d.message, {
3998
- sessionId: session.id,
5403
+ return emitJsonError(io2, "run_failed", d.message, {
3999
5404
  ...d.status !== void 0 ? { status: d.status } : {},
4000
5405
  ...d.remedy ? { remedy: d.remedy } : {}
4001
5406
  });
4002
5407
  }
4003
- io2.stdout(JSON.stringify(accepted.session) + "\n");
5408
+ io2.stdout(JSON.stringify(session) + "\n");
4004
5409
  if (!follow.present)
4005
5410
  return SUCCESS;
4006
- const seen = /* @__PURE__ */ new Set();
4007
- let currentStatus = accepted.session.status;
4008
- const deadline = followTimeoutMs === null ? Number.POSITIVE_INFINITY : Date.now() + followTimeoutMs;
4009
- while (!isSessionParked2(currentStatus)) {
4010
- await sleep2(2e3);
4011
- try {
4012
- const events = await operations_exports.listSessionEvents(http, session.id);
4013
- for (const event of events) {
4014
- if (!seen.has(event.id)) {
4015
- seen.add(event.id);
4016
- io2.stdout(JSON.stringify(event) + "\n");
4017
- }
4018
- }
4019
- } catch (err2) {
4020
- io2.stderr(`(transient) event poll failed: ${err2.message}
4021
- `);
4022
- }
4023
- try {
4024
- const updated = await operations_exports.getSession(http, session.id);
4025
- currentStatus = updated.status;
4026
- } catch (err2) {
4027
- io2.stderr(`(transient) status poll failed: ${err2.message}
4028
- `);
5411
+ const controller = new AbortController();
5412
+ let timedOut = false;
5413
+ let interrupted = false;
5414
+ io2.onSignal?.("SIGINT", () => {
5415
+ interrupted = true;
5416
+ controller.abort();
5417
+ });
5418
+ const timer = followTimeoutMs === null ? null : setTimeout(() => {
5419
+ timedOut = true;
5420
+ controller.abort();
5421
+ }, followTimeoutMs);
5422
+ let lastSeq = -1;
5423
+ try {
5424
+ const stream = openEnvelopeStream(io2, http, session.id, {
5425
+ signal: controller.signal,
5426
+ ...common.flags.debug ? { debug: (line) => io2.stderr(`[aex] ${line}
5427
+ `) } : {}
5428
+ });
5429
+ for await (const event of stream) {
5430
+ lastSeq = event.sequence;
5431
+ io2.stdout(JSON.stringify(event) + "\n");
4029
5432
  }
4030
- if (!isSessionParked2(currentStatus) && Date.now() >= deadline) {
5433
+ } catch (err2) {
5434
+ if (timer)
5435
+ clearTimeout(timer);
5436
+ if (timedOut) {
4031
5437
  emitJsonError(io2, "run_follow_timeout", `timed out after ${followTimeoutMs}ms following session`, {
4032
5438
  sessionId: session.id,
5439
+ lastSeq,
5440
+ ...followTimeoutContext(session),
4033
5441
  hint: `aex status ${session.id} | aex events ${session.id} | aex download ${session.id}`
4034
5442
  });
4035
5443
  return TIMEOUT_ERR;
4036
5444
  }
5445
+ if (interrupted) {
5446
+ io2.stderr(`(interrupted) followed up to seq ${lastSeq}
5447
+ `);
5448
+ return SUCCESS;
5449
+ }
5450
+ io2.stderr(`(transient) event stream failed: ${err2.message}
5451
+ `);
5452
+ }
5453
+ if (timer)
5454
+ clearTimeout(timer);
5455
+ if (timedOut) {
5456
+ emitJsonError(io2, "run_follow_timeout", `timed out after ${followTimeoutMs}ms following session`, {
5457
+ sessionId: session.id,
5458
+ lastSeq,
5459
+ ...followTimeoutContext(session),
5460
+ hint: `aex status ${session.id} | aex events ${session.id} | aex download ${session.id}`
5461
+ });
5462
+ return TIMEOUT_ERR;
5463
+ }
5464
+ if (interrupted) {
5465
+ io2.stderr(`(interrupted) followed up to seq ${lastSeq}
5466
+ `);
5467
+ return SUCCESS;
4037
5468
  }
4038
5469
  try {
4039
5470
  const final = await operations_exports.getSession(http, session.id);
@@ -4053,6 +5484,57 @@ async function runRunCmd(io2, argv) {
4053
5484
  return RUNTIME_ERR;
4054
5485
  }
4055
5486
  }
5487
+ async function buildSkill(io2, ref) {
5488
+ const content = await readAtFile(io2, ref);
5489
+ return buildCliSkill(content, ref);
5490
+ }
5491
+ async function buildTool(io2, ref) {
5492
+ const content = await readAtFile(io2, ref);
5493
+ const entry = baseName(stripAt(ref));
5494
+ const name = deriveName(ref, 1);
5495
+ return buildCliTool({
5496
+ name,
5497
+ description: `Custom tool ${name}`,
5498
+ entry,
5499
+ content
5500
+ });
5501
+ }
5502
+ async function buildAgentsMd(io2, ref) {
5503
+ const content = await readAtFile(io2, ref);
5504
+ return buildCliAgentsMd(content, deriveName(ref, 2));
5505
+ }
5506
+ async function buildFile(io2, ref) {
5507
+ const content = await readAtFile(io2, ref);
5508
+ const name = baseName(stripAt(ref));
5509
+ return buildCliFile({ name, content });
5510
+ }
5511
+ async function readAtFile(io2, value) {
5512
+ const path = resolvePath(io2.cwd(), stripAt(value));
5513
+ return io2.readFile(path);
5514
+ }
5515
+ function stripAt(value) {
5516
+ return value.startsWith("@") ? value.slice(1) : value;
5517
+ }
5518
+ function baseName(p) {
5519
+ const trimmed = p.replace(/[\\/]+$/, "");
5520
+ const i2 = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
5521
+ return i2 >= 0 ? trimmed.slice(i2 + 1) : trimmed;
5522
+ }
5523
+ function followTimeoutContext(session) {
5524
+ return {
5525
+ ...typeof session.status === "string" ? { sessionStatus: session.status } : {},
5526
+ ...typeof session.turnSeq === "number" ? { turnSeq: session.turnSeq } : {},
5527
+ ...typeof session.turnStatus === "string" ? { turnStatus: session.turnStatus } : {}
5528
+ };
5529
+ }
5530
+ function deriveName(ref, minLen) {
5531
+ const base = baseName(stripAt(ref));
5532
+ const noExt = base.includes(".") ? base.slice(0, base.lastIndexOf(".")) : base;
5533
+ let slug = noExt.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
5534
+ while (slug.length < minLen)
5535
+ slug += "x";
5536
+ return slug;
5537
+ }
4056
5538
  function stripMcpHeadersForParsing(input) {
4057
5539
  const mcpHeaders = /* @__PURE__ */ new Map();
4058
5540
  if (input === null || typeof input !== "object" || Array.isArray(input)) {
@@ -4094,15 +5576,6 @@ async function readMaybeFile(io2, value) {
4094
5576
  }
4095
5577
  return value;
4096
5578
  }
4097
- function generateIdempotencyKey() {
4098
- const c = globalThis.crypto;
4099
- if (c?.randomUUID)
4100
- return c.randomUUID();
4101
- return `idem-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
4102
- }
4103
- function sleep2(ms) {
4104
- return new Promise((resolve) => setTimeout(resolve, ms));
4105
- }
4106
5579
 
4107
5580
  // dist/host/status.js
4108
5581
  async function runStatusCmd(io2, argv) {
@@ -4220,7 +5693,7 @@ async function runWaitCmd(io2, argv) {
4220
5693
  `);
4221
5694
  if (Date.now() >= deadline)
4222
5695
  return timeout({});
4223
- await sleep3(intervalMs);
5696
+ await sleep2(intervalMs);
4224
5697
  continue;
4225
5698
  }
4226
5699
  if (isSessionParked2(session.status)) {
@@ -4229,10 +5702,10 @@ async function runWaitCmd(io2, argv) {
4229
5702
  }
4230
5703
  if (Date.now() >= deadline)
4231
5704
  return timeout({ lastStatus: session.status });
4232
- await sleep3(intervalMs);
5705
+ await sleep2(intervalMs);
4233
5706
  }
4234
5707
  }
4235
- function sleep3(ms) {
5708
+ function sleep2(ms) {
4236
5709
  return new Promise((resolve) => setTimeout(resolve, ms));
4237
5710
  }
4238
5711
 
@@ -4292,7 +5765,7 @@ async function runEventsCmd(io2, argv) {
4292
5765
  `);
4293
5766
  if (Date.now() >= deadline)
4294
5767
  return emitTimeout(io2, sessionId, timeoutMs);
4295
- await sleep4(2e3);
5768
+ await sleep3(2e3);
4296
5769
  continue;
4297
5770
  }
4298
5771
  for (const event of events) {
@@ -4312,18 +5785,19 @@ async function runEventsCmd(io2, argv) {
4312
5785
  }
4313
5786
  if (Date.now() >= deadline)
4314
5787
  return emitTimeout(io2, sessionId, timeoutMs);
4315
- await sleep4(2e3);
5788
+ await sleep3(2e3);
4316
5789
  }
4317
5790
  }
4318
5791
  function emitTimeout(io2, sessionId, timeoutMs) {
4319
5792
  emitJsonError(io2, "events_follow_timeout", `timed out after ${timeoutMs}ms following session events`, { sessionId });
4320
5793
  return TIMEOUT_ERR;
4321
5794
  }
4322
- function sleep4(ms) {
5795
+ function sleep3(ms) {
4323
5796
  return new Promise((resolve) => setTimeout(resolve, ms));
4324
5797
  }
4325
5798
 
4326
5799
  // dist/host/outputs.js
5800
+ import { resolve as resolvePath2 } from "node:path";
4327
5801
  async function runOutputsCmd(io2, argv) {
4328
5802
  if (await refuseInsideManagedRun(io2, "outputs"))
4329
5803
  return USAGE_ERR;
@@ -4333,31 +5807,241 @@ async function runOutputsCmd(io2, argv) {
4333
5807
  `);
4334
5808
  return USAGE_ERR;
4335
5809
  }
4336
- const positional = common.rest.filter((arg) => !arg.startsWith("--"));
5810
+ const args = common.rest;
5811
+ const sub = args[0];
5812
+ const http = makeHttpClient(io2, common.flags);
5813
+ switch (sub) {
5814
+ case "read":
5815
+ return outputsRead(io2, http, args.slice(1));
5816
+ case "download":
5817
+ return outputsDownload(io2, http, args.slice(1), common.flags);
5818
+ case "link":
5819
+ return outputsLink(io2, http, args.slice(1));
5820
+ case "find":
5821
+ return outputsFind(io2, http, args.slice(1));
5822
+ case "search":
5823
+ return outputsSearch(io2, http, args.slice(1));
5824
+ default:
5825
+ return outputsList(io2, http, args);
5826
+ }
5827
+ }
5828
+ async function outputsList(io2, http, args) {
5829
+ const positional = args.filter((a) => !a.startsWith("--"));
4337
5830
  if (positional.length !== 1) {
4338
5831
  io2.stderr("usage: aex outputs <session-id> [common flags]\n");
4339
5832
  return USAGE_ERR;
4340
5833
  }
4341
5834
  const sessionId = positional[0];
4342
- const http = makeHttpClient(io2, common.flags);
4343
5835
  try {
4344
5836
  const outputs = await operations_exports.listSessionOutputs(http, sessionId);
4345
- for (const out of outputs) {
5837
+ for (const out of outputs)
4346
5838
  io2.stdout(JSON.stringify(out) + "\n");
4347
- }
4348
5839
  return SUCCESS;
4349
5840
  } catch (err2) {
4350
- const d = describeApiError(err2);
4351
- return emitJsonError(io2, "outputs_failed", d.message, {
4352
- sessionId,
4353
- ...d.status !== void 0 ? { status: d.status } : {},
4354
- ...d.remedy ? { remedy: d.remedy } : {}
4355
- });
5841
+ return outputsError(io2, "outputs_failed", err2, { sessionId });
5842
+ }
5843
+ }
5844
+ async function outputsRead(io2, http, args) {
5845
+ const positional = args.filter((a) => !a.startsWith("--"));
5846
+ if (positional.length !== 2) {
5847
+ io2.stderr("usage: aex outputs read <session-id> <path> [common flags]\n");
5848
+ return USAGE_ERR;
5849
+ }
5850
+ const [sessionId, selector] = positional;
5851
+ try {
5852
+ const text = await operations_exports.readOutputText(http, sessionId, { path: selector });
5853
+ io2.stdout(JSON.stringify(text) + "\n");
5854
+ return SUCCESS;
5855
+ } catch (err2) {
5856
+ return outputsError(io2, "outputs_read_failed", err2, { sessionId, path: selector });
5857
+ }
5858
+ }
5859
+ async function outputsDownload(io2, http, args, flags) {
5860
+ const outFlag = takeFlagValue(args, "--out");
5861
+ if (outFlag.error) {
5862
+ io2.stderr(`${outFlag.error}
5863
+ `);
5864
+ return USAGE_ERR;
5865
+ }
5866
+ void flags;
5867
+ const positional = outFlag.remaining.filter((a) => !a.startsWith("--"));
5868
+ if (positional.length !== 2) {
5869
+ io2.stderr("usage: aex outputs download <session-id> <path> [--out file] [common flags]\n");
5870
+ return USAGE_ERR;
5871
+ }
5872
+ const [sessionId, selector] = positional;
5873
+ let bytes;
5874
+ try {
5875
+ bytes = (await operations_exports.downloadOutput(http, sessionId, { path: selector })).bytes;
5876
+ } catch (err2) {
5877
+ return outputsError(io2, "outputs_download_failed", err2, { sessionId, path: selector });
5878
+ }
5879
+ const destination = resolvePath2(io2.cwd(), outFlag.value ?? baseName2(selector));
5880
+ try {
5881
+ await io2.writeFile(destination, bytes);
5882
+ } catch (err2) {
5883
+ return emitJsonError(io2, "write_failed", `failed to write file: ${err2.message}`, { destination });
5884
+ }
5885
+ io2.stdout(JSON.stringify({ sessionId, path: selector, out: destination, bytes: bytes.byteLength }) + "\n");
5886
+ return SUCCESS;
5887
+ }
5888
+ async function outputsLink(io2, http, args) {
5889
+ const positional = args.filter((a) => !a.startsWith("--"));
5890
+ if (positional.length !== 2) {
5891
+ io2.stderr("usage: aex outputs link <session-id> <path> [common flags]\n");
5892
+ return USAGE_ERR;
5893
+ }
5894
+ const [sessionId, selector] = positional;
5895
+ try {
5896
+ const link = await operations_exports.outputLink(http, sessionId, { path: selector });
5897
+ io2.stdout(JSON.stringify(link) + "\n");
5898
+ return SUCCESS;
5899
+ } catch (err2) {
5900
+ return outputsError(io2, "outputs_link_failed", err2, { sessionId, path: selector });
5901
+ }
5902
+ }
5903
+ async function outputsFind(io2, http, args) {
5904
+ const name = takeFlagValue(args, "--name");
5905
+ const ext = takeFlagValue(name.remaining, "--ext");
5906
+ const type = takeFlagValue(ext.remaining, "--type");
5907
+ const contentType = takeFlagValue(type.remaining, "--content-type");
5908
+ const err2 = name.error ?? ext.error ?? type.error ?? contentType.error;
5909
+ if (err2) {
5910
+ io2.stderr(`${err2}
5911
+ `);
5912
+ return USAGE_ERR;
5913
+ }
5914
+ const positional = contentType.remaining.filter((a) => !a.startsWith("--"));
5915
+ if (positional.length !== 1) {
5916
+ io2.stderr("usage: aex outputs find <session-id> [--name S] [--ext E] [--type T] [--content-type CT] [common flags]\n");
5917
+ return USAGE_ERR;
5918
+ }
5919
+ const sessionId = positional[0];
5920
+ const query = {
5921
+ ...name.value !== null ? { filename: name.value } : {},
5922
+ ...ext.value !== null ? { extension: ext.value } : {},
5923
+ ...type.value !== null ? { type: type.value } : {},
5924
+ ...contentType.value !== null ? { contentType: contentType.value } : {}
5925
+ };
5926
+ try {
5927
+ const hits = await searchSessionOutputs(http, sessionId, query);
5928
+ for (const hit of hits)
5929
+ io2.stdout(JSON.stringify(hit) + "\n");
5930
+ return SUCCESS;
5931
+ } catch (err22) {
5932
+ return outputsError(io2, "outputs_find_failed", err22, { sessionId });
5933
+ }
5934
+ }
5935
+ async function outputsSearch(io2, http, args) {
5936
+ const query = takeFlagValue(args, "--query");
5937
+ const name = takeFlagValue(query.remaining, "--name");
5938
+ const ext = takeFlagValue(name.remaining, "--ext");
5939
+ const contentType = takeFlagValue(ext.remaining, "--content-type");
5940
+ const limit = takeFlagValue(contentType.remaining, "--limit");
5941
+ const runIds = collectRepeated(limit.remaining, "--run-id");
5942
+ const err2 = query.error ?? name.error ?? ext.error ?? contentType.error ?? limit.error ?? runIds.error;
5943
+ if (err2) {
5944
+ io2.stderr(`${err2}
5945
+ `);
5946
+ return USAGE_ERR;
5947
+ }
5948
+ const filename = query.value ?? name.value;
5949
+ let limitValue;
5950
+ if (limit.value !== null) {
5951
+ const n = Number(limit.value);
5952
+ if (!Number.isInteger(n) || n < 1) {
5953
+ io2.stderr(`--limit must be a positive integer (got: ${limit.value})
5954
+ `);
5955
+ return USAGE_ERR;
5956
+ }
5957
+ limitValue = n;
5958
+ }
5959
+ const search = {
5960
+ ...filename !== null ? { filename } : {},
5961
+ ...ext.value !== null ? { extension: ext.value } : {},
5962
+ ...contentType.value !== null ? { contentType: contentType.value } : {},
5963
+ ...runIds.values.length > 0 ? { runIds: [...runIds.values] } : {},
5964
+ ...limitValue !== void 0 ? { limit: limitValue } : {}
5965
+ };
5966
+ try {
5967
+ const page = await searchWorkspaceOutputs(http, search);
5968
+ io2.stdout(JSON.stringify(page) + "\n");
5969
+ return SUCCESS;
5970
+ } catch (err22) {
5971
+ return outputsError(io2, "outputs_search_failed", err22, {});
5972
+ }
5973
+ }
5974
+ function outputsError(io2, code, err2, extra) {
5975
+ const d = describeApiError(err2);
5976
+ return emitJsonError(io2, code, d.message, {
5977
+ ...extra,
5978
+ ...d.status !== void 0 ? { status: d.status } : {},
5979
+ ...d.remedy ? { remedy: d.remedy } : {}
5980
+ });
5981
+ }
5982
+ async function searchWorkspaceOutputs(http, query) {
5983
+ assertMetadataOnlyOutputSearch(query, "aex outputs search");
5984
+ const runIds = query.runIds && query.runIds.length > 0 ? [...query.runIds] : void 0;
5985
+ const limit = query.limit ?? 100;
5986
+ const hits = [];
5987
+ const candidates = runIds ?? await listRecentRunIds(http, limit);
5988
+ for (const runId of candidates) {
5989
+ const outputs = await searchSessionOutputs(http, runId, query);
5990
+ for (const output of outputs) {
5991
+ hits.push(outputHit(runId, output));
5992
+ if (hits.length >= limit)
5993
+ return { hits };
5994
+ }
4356
5995
  }
5996
+ return { hits };
5997
+ }
5998
+ async function listRecentRunIds(http, limit) {
5999
+ const out = [];
6000
+ let cursor;
6001
+ while (out.length < limit) {
6002
+ const page = await operations_exports.listSessions(http, { limit: Math.min(100, limit - out.length), ...cursor ? { cursor } : {} });
6003
+ out.push(...page.sessions.map((session) => session.id));
6004
+ if (!page.nextCursor)
6005
+ break;
6006
+ cursor = page.nextCursor;
6007
+ }
6008
+ return out;
6009
+ }
6010
+ async function searchSessionOutputs(http, sessionId, query) {
6011
+ const listQuery = {
6012
+ ...query.extension !== void 0 ? { extension: query.extension } : {},
6013
+ ...query.contentType !== void 0 ? { contentType: query.contentType } : {}
6014
+ };
6015
+ const outputs = await operations_exports.listSessionOutputs(http, sessionId, Object.keys(listQuery).length > 0 ? listQuery : void 0);
6016
+ if (query.filename === void 0)
6017
+ return outputs;
6018
+ const match = operations_exports.toFilenameMatcher(query.filename);
6019
+ return outputs.filter((output) => typeof output.filename === "string" && match(output.filename));
6020
+ }
6021
+ function outputHit(runId, output) {
6022
+ return {
6023
+ runId,
6024
+ outputId: output.id,
6025
+ ...output.filename !== void 0 ? { filename: output.filename } : {},
6026
+ ...output.sizeBytes !== void 0 ? { sizeBytes: output.sizeBytes } : {},
6027
+ ...output.contentType !== void 0 ? { contentType: output.contentType } : {}
6028
+ };
6029
+ }
6030
+ function assertMetadataOnlyOutputSearch(query, surface) {
6031
+ for (const key of ["content", "text", "query", "grep", "body"]) {
6032
+ if (Object.prototype.hasOwnProperty.call(query, key)) {
6033
+ throw new Error(`${surface}: ${key} is not supported; output search is metadata-only`);
6034
+ }
6035
+ }
6036
+ }
6037
+ function baseName2(p) {
6038
+ const trimmed = p.replace(/[\\/]+$/, "");
6039
+ const i2 = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
6040
+ return i2 >= 0 ? trimmed.slice(i2 + 1) : trimmed;
4357
6041
  }
4358
6042
 
4359
6043
  // dist/host/download.js
4360
- import { resolve as resolvePath2 } from "node:path";
6044
+ import { resolve as resolvePath3 } from "node:path";
4361
6045
  var NAMESPACE_DOWNLOADERS = {
4362
6046
  outputs: operations_exports.downloadOutputs,
4363
6047
  events: operations_exports.downloadEvents,
@@ -4420,10 +6104,10 @@ async function runDownloadCmd(io2, argv) {
4420
6104
  }
4421
6105
  function resolveDestination(io2, out, sessionId, namespace2) {
4422
6106
  if (out) {
4423
- return resolvePath2(io2.cwd(), out);
6107
+ return resolvePath3(io2.cwd(), out);
4424
6108
  }
4425
6109
  const suffix = namespace2 ? `-${namespace2}` : "";
4426
- return resolvePath2(io2.cwd(), `aex-session-${sessionId}${suffix}.zip`);
6110
+ return resolvePath3(io2.cwd(), `aex-session-${sessionId}${suffix}.zip`);
4427
6111
  }
4428
6112
 
4429
6113
  // dist/host/cancel.js
@@ -4584,9 +6268,9 @@ async function runBillingCmd(io2, argv) {
4584
6268
  if (common.rest[0] === "portal") {
4585
6269
  return runBillingPortal(io2, common.rest.slice(1), common.flags);
4586
6270
  }
4587
- const { present: json, remaining } = takeBooleanFlag(common.rest, "--json");
4588
- if (remaining.length > 0) {
4589
- io2.stderr(`unexpected arguments: ${remaining.join(" ")}
6271
+ const json = common.flags.json;
6272
+ if (common.rest.length > 0) {
6273
+ io2.stderr(`unexpected arguments: ${common.rest.join(" ")}
4590
6274
  `);
4591
6275
  io2.stderr("usage: aex billing [--json] | aex billing ledger [--limit N] | aex billing upgrade pro|team | aex billing portal [common flags]\n");
4592
6276
  return USAGE_ERR;
@@ -4616,8 +6300,8 @@ async function runBillingCmd(io2, argv) {
4616
6300
  }
4617
6301
  }
4618
6302
  async function runBillingUpgrade(io2, argv, flags) {
4619
- const { present: json, remaining: rest1 } = takeBooleanFlag(argv, "--json");
4620
- const { value: successUrl, remaining: rest2 } = takeOptionFlag(rest1, "--success-url");
6303
+ const json = flags.json;
6304
+ const { value: successUrl, remaining: rest2 } = takeOptionFlag(argv, "--success-url");
4621
6305
  const { value: cancelUrl, remaining: rest3 } = takeOptionFlag(rest2, "--cancel-url");
4622
6306
  const { value: idempotencyKey, remaining } = takeOptionFlag(rest3, "--idempotency-key");
4623
6307
  const planKey = remaining[0];
@@ -4646,8 +6330,8 @@ async function runBillingUpgrade(io2, argv, flags) {
4646
6330
  }
4647
6331
  }
4648
6332
  async function runBillingPortal(io2, argv, flags) {
4649
- const { present: json, remaining: rest1 } = takeBooleanFlag(argv, "--json");
4650
- const { value: returnUrl, remaining } = takeOptionFlag(rest1, "--return-url");
6333
+ const json = flags.json;
6334
+ const { value: returnUrl, remaining } = takeOptionFlag(argv, "--return-url");
4651
6335
  if (remaining.length > 0) {
4652
6336
  io2.stderr(`unexpected arguments: ${remaining.join(" ")}
4653
6337
  `);
@@ -4827,7 +6511,7 @@ async function runSessionsCmd(io2, argv) {
4827
6511
  }
4828
6512
 
4829
6513
  // dist/host/debug.js
4830
- import { dirname, resolve as resolvePath3 } from "node:path";
6514
+ import { dirname, resolve as resolvePath4 } from "node:path";
4831
6515
  function status(source, state, opts = {}) {
4832
6516
  return {
4833
6517
  source,
@@ -5145,7 +6829,7 @@ function toMs(iso) {
5145
6829
  async function writeDebugBundle(io2, baseDir, bundle) {
5146
6830
  const encoder3 = new TextEncoder();
5147
6831
  for (const file2 of bundle.files) {
5148
- const abs = resolvePath3(baseDir, file2.path);
6832
+ const abs = resolvePath4(baseDir, file2.path);
5149
6833
  if (io2.mkdirp)
5150
6834
  await io2.mkdirp(dirname(abs));
5151
6835
  await io2.writeFile(abs, encoder3.encode(file2.content));
@@ -5417,7 +7101,7 @@ ${USAGE}`);
5417
7101
  }
5418
7102
  sinceMs = d.ms;
5419
7103
  }
5420
- const outDir = resolvePath3(io2.cwd(), outF.value ?? `aex-debug-${runId}`);
7104
+ const outDir = resolvePath4(io2.cwd(), outF.value ?? `aex-debug-${runId}`);
5421
7105
  const client = makeAwsSources(region);
5422
7106
  let targets;
5423
7107
  try {
@@ -5490,7 +7174,7 @@ async function runLoginCmd(io2, argv) {
5490
7174
  return USAGE_ERR;
5491
7175
  }
5492
7176
  const resolvedUrl = aexUrl ?? AEX_DEFAULT_BASE_URL;
5493
- const http = makeHttpClient(io2, { apiKey, aexUrl: resolvedUrl, debug: debug2 });
7177
+ const http = makeHttpClient(io2, { apiKey, aexUrl: resolvedUrl, debug: debug2, json: false });
5494
7178
  let workspaceId;
5495
7179
  try {
5496
7180
  const me = await operations_exports.whoami(http);
@@ -5652,110 +7336,6 @@ function hasUnknown(io2, rest, verb) {
5652
7336
  return false;
5653
7337
  }
5654
7338
 
5655
- // dist/host/stream-render.js
5656
- async function* openEnvelopeStream(io2, http, sessionId, options = {}) {
5657
- const first = await operations_exports.getSessionCoordinatorTicket(http, sessionId);
5658
- if (options.debug) {
5659
- let host = "(invalid)";
5660
- try {
5661
- host = new URL(first.wsUrl).host;
5662
- } catch {
5663
- }
5664
- options.debug(`ticket minted: host=${host} expiresInMs=${first.expiresAtMs - Date.now()} from=${options.from ?? 0}`);
5665
- }
5666
- yield* streamCoordinatorEvents({
5667
- wsUrl: first.wsUrl,
5668
- from: options.from ?? 0,
5669
- fetchTicket: async () => (await operations_exports.getSessionCoordinatorTicket(http, sessionId)).ticket,
5670
- ...io2.webSocketFactory ? { webSocketFactory: io2.webSocketFactory } : {},
5671
- ...options.settleConsistent ? { isTerminal: isRunSettled } : {},
5672
- ...options.signal ? { signal: options.signal } : {}
5673
- });
5674
- }
5675
- var PREVIEW_MAX = 120;
5676
- function clip(text, max = PREVIEW_MAX) {
5677
- const oneLine = text.replace(/\s+/g, " ").trim();
5678
- return oneLine.length > max ? oneLine.slice(0, max - 1) + "\u2026" : oneLine;
5679
- }
5680
- function str2(v) {
5681
- return typeof v === "string" ? v : v === void 0 || v === null ? "" : JSON.stringify(v);
5682
- }
5683
- function argsPreview(e) {
5684
- const d = e.data;
5685
- const candidate = d.input ?? d.args ?? d.arguments ?? d.params;
5686
- if (candidate === void 0)
5687
- return "";
5688
- return clip(typeof candidate === "string" ? candidate : JSON.stringify(candidate));
5689
- }
5690
- function renderEnvelope(e, options = {}) {
5691
- if (channelOf(e) === "log" && !options.logs)
5692
- return null;
5693
- switch (e.type) {
5694
- case "RUN_STARTED":
5695
- return "\u25B6 run started";
5696
- case "TEXT_MESSAGE_CONTENT": {
5697
- const delta = toAGUI(e).type === "TEXT_MESSAGE_CONTENT" ? toAGUI(e).delta : "";
5698
- return delta ? delta : null;
5699
- }
5700
- case "TOOL_CALL_START": {
5701
- const agui = toAGUI(e);
5702
- const name = agui.type === "TOOL_CALL_START" ? agui.toolCallName : str2(e.data.name);
5703
- const preview = argsPreview(e);
5704
- return `\xB7 tool ${name || "(unnamed)"}(${preview})`;
5705
- }
5706
- case "TOOL_CALL_RESULT": {
5707
- const name = str2(e.data.name) || "result";
5708
- const content = e.data.content;
5709
- const ok = content === void 0 || content === null ? "ok" : clip(str2(content));
5710
- return ` \u2190 ${name} ${ok}`;
5711
- }
5712
- case "CUSTOM": {
5713
- const label = e.message ?? str2(e.data.name) ?? "custom";
5714
- if (e.data.name === "aex.session.idle") {
5715
- const value = e.data.value;
5716
- const reason = value && typeof value.reason === "string" ? value.reason : "";
5717
- if (reason && reason !== "completed")
5718
- return `[aex] ${label} (${reason})`;
5719
- }
5720
- return `[aex] ${label}`;
5721
- }
5722
- case "LOG": {
5723
- const level = e.level ?? "info";
5724
- return `[${level}] ${e.message ?? str2(e.data.message)}`;
5725
- }
5726
- case "RUN_FINISHED":
5727
- return "\u2713 run finished";
5728
- case "RUN_ERROR": {
5729
- const agui = toAGUI(e);
5730
- const message = agui.type === "RUN_ERROR" ? agui.message : e.message ?? "run error";
5731
- return `\u2717 run error: ${message}`;
5732
- }
5733
- default:
5734
- return null;
5735
- }
5736
- }
5737
- function parseFilters(tokens) {
5738
- const flattened = tokens.flatMap((t) => t.split(",")).map((s) => s.trim()).filter(Boolean);
5739
- if (flattened.length === 0)
5740
- return {};
5741
- const types = /* @__PURE__ */ new Set();
5742
- const sources = /* @__PURE__ */ new Set();
5743
- for (const tok of flattened) {
5744
- if (AEX_EVENT_TYPES.includes(tok)) {
5745
- types.add(tok);
5746
- } else if (AEX_EVENT_SOURCES.includes(tok)) {
5747
- sources.add(tok);
5748
- } else {
5749
- const hint = suggest(tok, [...AEX_EVENT_TYPES, ...AEX_EVENT_SOURCES]);
5750
- return {
5751
- error: `unknown --filter token "${tok}"${hint ? `; did you mean "${hint}"?` : ""} (types: ${AEX_EVENT_TYPES.join(",")}; sources: ${AEX_EVENT_SOURCES.join(",")})`
5752
- };
5753
- }
5754
- }
5755
- const predicate = (e) => (types.size === 0 || types.has(e.type)) && (sources.size === 0 || sources.has(e.source));
5756
- return { predicate };
5757
- }
5758
-
5759
7339
  // dist/host/tail.js
5760
7340
  async function runTailCmd(io2, argv) {
5761
7341
  if (await refuseInsideManagedRun(io2, "tail"))
@@ -5766,8 +7346,8 @@ async function runTailCmd(io2, argv) {
5766
7346
  `);
5767
7347
  return USAGE_ERR;
5768
7348
  }
5769
- const jsonFlag = takeBooleanFlag(common.rest, "--json");
5770
- const logsFlag = takeBooleanFlag(jsonFlag.remaining, "--logs");
7349
+ const json = common.flags.json;
7350
+ const logsFlag = takeBooleanFlag(common.rest, "--logs");
5771
7351
  const settleFlag = takeBooleanFlag(logsFlag.remaining, "--settle");
5772
7352
  const filterFlag = collectRepeated(settleFlag.remaining, "--filter");
5773
7353
  if (filterFlag.error) {
@@ -5847,7 +7427,7 @@ async function runTailCmd(io2, argv) {
5847
7427
  if (filters.predicate && !filters.predicate(e)) {
5848
7428
  continue;
5849
7429
  }
5850
- if (jsonFlag.present) {
7430
+ if (json) {
5851
7431
  if (logsFlag.present || e.channel !== "log") {
5852
7432
  io2.stdout(JSON.stringify(e) + "\n");
5853
7433
  eventCount++;
@@ -5920,8 +7500,8 @@ async function runInspectCmd(io2, argv) {
5920
7500
  `);
5921
7501
  return USAGE_ERR;
5922
7502
  }
5923
- const jsonFlag = takeBooleanFlag(common.rest, "--json");
5924
- const logsFlag = takeBooleanFlag(jsonFlag.remaining, "--logs");
7503
+ const json = common.flags.json;
7504
+ const logsFlag = takeBooleanFlag(common.rest, "--logs");
5925
7505
  const filterFlag = collectRepeated(logsFlag.remaining, "--filter");
5926
7506
  if (filterFlag.error) {
5927
7507
  io2.stderr(`${filterFlag.error}
@@ -5973,7 +7553,7 @@ async function runInspectCmd(io2, argv) {
5973
7553
  ...d.remedy ? { remedy: d.remedy } : {}
5974
7554
  });
5975
7555
  }
5976
- if (!jsonFlag.present) {
7556
+ if (!json) {
5977
7557
  const model = typeof header.model === "string" ? ` \xB7 ${header.model}` : "";
5978
7558
  const created = header.createdAt ? ` \xB7 ${header.createdAt}` : "";
5979
7559
  io2.stdout(`session ${sessionId} \xB7 ${header.status}${model}${created}
@@ -5999,7 +7579,7 @@ async function runInspectCmd(io2, argv) {
5999
7579
  runErrorEvent = e;
6000
7580
  if (filters.predicate && !filters.predicate(e))
6001
7581
  continue;
6002
- if (jsonFlag.present) {
7582
+ if (json) {
6003
7583
  if (logsFlag.present || e.channel !== "log")
6004
7584
  collected.push(e);
6005
7585
  continue;
@@ -6029,7 +7609,7 @@ async function runInspectCmd(io2, argv) {
6029
7609
  finalSession = await operations_exports.getSession(http, sessionId);
6030
7610
  } catch {
6031
7611
  }
6032
- if (jsonFlag.present) {
7612
+ if (json) {
6033
7613
  io2.stdout(JSON.stringify({ session: finalSession, events: collected }) + "\n");
6034
7614
  } else {
6035
7615
  if (runErrorEvent) {
@@ -6080,6 +7660,11 @@ async function dispatch(io2, args) {
6080
7660
  }
6081
7661
  const sub = args[0];
6082
7662
  const rest = args.slice(1);
7663
+ const spec = sub === void 0 ? void 0 : findVerbSpec(sub);
7664
+ if (spec && wantsVerbHelp(rest) && !(sub === "outputs" && rest[0] === "sync")) {
7665
+ io2.stdout(renderVerbHelp(spec));
7666
+ return SUCCESS;
7667
+ }
6083
7668
  switch (sub) {
6084
7669
  case "run":
6085
7670
  return runRunCmd(io2, rest);
@@ -6142,7 +7727,7 @@ async function dispatch(io2, args) {
6142
7727
  }
6143
7728
  }
6144
7729
  async function printGlobalHelp(io2) {
6145
- io2.stdout("aex \u2014 unified CLI for the aex platform (mirrors the SDK 1:1)\n\n");
7730
+ io2.stdout("aex \u2014 unified CLI for the aex platform (a thin pass-through over the SDK)\n\n");
6146
7731
  io2.stdout("Usage:\n");
6147
7732
  io2.stdout(" aex run --config <run.json> --<provider>-api-key K --api-key T [flags]\n");
6148
7733
  io2.stdout(" aex run --model M --prompt P [--system S] [--mcp name=url ...] --<provider>-api-key K --api-key T [flags]\n");
@@ -6214,7 +7799,7 @@ async function walkDirectory(root) {
6214
7799
  async function visit(dir) {
6215
7800
  const entries = await readdir(dir, { withFileTypes: true });
6216
7801
  for (const entry of entries) {
6217
- const full = resolvePath4(dir, entry.name);
7802
+ const full = resolvePath5(dir, entry.name);
6218
7803
  if (entry.isDirectory()) {
6219
7804
  await visit(full);
6220
7805
  } else if (entry.isFile()) {