@aexhq/sdk 0.26.4 → 0.27.0

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.
@@ -196,6 +196,7 @@ export const deniedSecretFields = new Set([
196
196
  "providerApiKey",
197
197
  "anthropicApiKey",
198
198
  "apiKey",
199
+ "apiKeys",
199
200
  "accessToken",
200
201
  "refreshToken",
201
202
  "password",
@@ -708,8 +709,12 @@ export function crossValidateSecretEnvAndValues(secretEnv, envSecrets) {
708
709
  }
709
710
  }
710
711
  export function parseInlineSecrets(input) {
712
+ // A child run (parentRunId set) inherits its provider keys server-side from
713
+ // the parent's vault, so it may omit `secrets` entirely.
714
+ if (input === undefined || input === null)
715
+ return {};
711
716
  const value = requireRecord(input, "secrets");
712
- const allowedTopLevel = new Set(["apiKey", "mcpServers", "proxyEndpointAuth", "envSecrets"]);
717
+ const allowedTopLevel = new Set(["apiKey", "apiKeys", "mcpServers", "proxyEndpointAuth", "envSecrets"]);
713
718
  for (const key of Object.keys(value)) {
714
719
  if (key.startsWith("__aex_")) {
715
720
  // Platform-internal namespace (e.g. __aex_proxy_token). The BFF
@@ -723,16 +728,39 @@ export function parseInlineSecrets(input) {
723
728
  }
724
729
  }
725
730
  const apiKey = value.apiKey !== undefined ? requireString(value.apiKey, "secrets.apiKey") : undefined;
731
+ const apiKeys = parseApiKeys(value.apiKeys);
726
732
  const mcpServers = parseMcpServerSecrets(value.mcpServers);
727
733
  const proxyEndpointAuth = parseProxyEndpointAuth(value.proxyEndpointAuth);
728
734
  const envSecrets = parseEnvSecrets(value.envSecrets);
729
735
  return {
730
736
  ...(apiKey !== undefined ? { apiKey } : {}),
737
+ ...(apiKeys ? { apiKeys } : {}),
731
738
  ...(mcpServers ? { mcpServers } : {}),
732
739
  ...(proxyEndpointAuth ? { proxyEndpointAuth } : {}),
733
740
  ...(envSecrets ? { envSecrets } : {})
734
741
  };
735
742
  }
743
+ /**
744
+ * Parse the per-provider BYOK key map. Each key must name a known
745
+ * {@link RunProvider}; each value must be a non-empty string. Returns
746
+ * `undefined` for an absent or empty map so the spread above stays clean.
747
+ */
748
+ function parseApiKeys(input) {
749
+ if (input === undefined || input === null)
750
+ return undefined;
751
+ const value = requireRecord(input, "secrets.apiKeys");
752
+ const out = {};
753
+ for (const [provider, key] of Object.entries(value)) {
754
+ if (!RUN_PROVIDERS.includes(provider)) {
755
+ throw new Error(`secrets.apiKeys["${provider}"] is not a known provider; permitted: ${RUN_PROVIDERS.join(", ")}`);
756
+ }
757
+ if (typeof key !== "string" || key.length === 0) {
758
+ throw new Error(`secrets.apiKeys["${provider}"] must be a non-empty string`);
759
+ }
760
+ out[provider] = key;
761
+ }
762
+ return Object.keys(out).length > 0 ? out : undefined;
763
+ }
736
764
  function parseEnvSecrets(input) {
737
765
  if (input === undefined || input === null)
738
766
  return undefined;
@@ -1050,7 +1078,9 @@ export function parseRunSubmissionRequest(input, options = {}) {
1050
1078
  const postHook = parsePostHook(value.postHook, "submission.postHook");
1051
1079
  const proxyEndpoints = parseProxyEndpoints(value.proxyEndpoints);
1052
1080
  const secrets = parseInlineSecrets(value.secrets);
1053
- enforceCredentialSecretPolicy(credentialMode, secrets);
1081
+ enforceCredentialSecretPolicy(credentialMode, secrets, provider, {
1082
+ inheritsFromParent: parentRunId !== undefined
1083
+ });
1054
1084
  crossValidateProxyEndpointsAndAuth(proxyEndpoints, secrets.proxyEndpointAuth);
1055
1085
  const submission = parseSubmission(value.submission);
1056
1086
  assertRunModelMatchesProvider(provider, submission.model);
@@ -1169,13 +1199,22 @@ export function parseRunProvider(input) {
1169
1199
  }
1170
1200
  /**
1171
1201
  * Cross-check the supplied secrets bundle against the credential mode. BYOK
1172
- * requires `secrets.apiKey` (the provider key for the run's `provider`). MCP /
1173
- * proxy endpoint auth carry across providers and are not checked here.
1202
+ * requires `secrets.apiKeys[provider]` (the key for the run's own `provider`).
1203
+ * Additional provider keys are optional (validated for shape only) so the run
1204
+ * can supply keys for the other providers its subagents may use. MCP / proxy
1205
+ * endpoint auth carry across providers and are not checked here.
1206
+ *
1207
+ * A CHILD run (`inheritsFromParent`) is exempt from the own-key requirement: it
1208
+ * inherits its provider keys server-side from the parent's vaulted bundle, so
1209
+ * it need not carry any of its own. The server still verifies, at admission,
1210
+ * that the parent actually holds a key for the child's provider.
1174
1211
  */
1175
- export function enforceCredentialSecretPolicy(credentialMode, secrets) {
1212
+ export function enforceCredentialSecretPolicy(credentialMode, secrets, provider, opts) {
1176
1213
  void credentialMode;
1177
- if (!secrets.apiKey) {
1178
- throw new Error(`secrets.apiKey is required when credentialMode is byok`);
1214
+ if (opts?.inheritsFromParent)
1215
+ return;
1216
+ if (!(secrets.apiKeys?.[provider] ?? secrets.apiKey)) {
1217
+ throw new Error(`secrets.apiKey is required when credentialMode is byok (or secrets.apiKeys["${provider}"])`);
1179
1218
  }
1180
1219
  }
1181
1220
  export function parseSubmission(input) {
@@ -1194,7 +1233,7 @@ export function parseSubmission(input) {
1194
1233
  "securityProfile",
1195
1234
  "metadata",
1196
1235
  "outputs",
1197
- "builtins",
1236
+ "includeBuiltinTools",
1198
1237
  "outputMode",
1199
1238
  "platform"
1200
1239
  ]);
@@ -1207,7 +1246,7 @@ export function parseSubmission(input) {
1207
1246
  const system = optionalString(value.system, "submission.system");
1208
1247
  const prompt = parsePrompt(value.prompt);
1209
1248
  const skills = parseSkills(value.skills);
1210
- const tools = parseTools(value.tools);
1249
+ const { tools, builtinTools } = parseTools(value.tools);
1211
1250
  const agentsMd = parseAgentsMd(value.agentsMd);
1212
1251
  const files = parseFiles(value.files);
1213
1252
  const mcpServers = parseMcpServers(value.mcpServers);
@@ -1216,7 +1255,7 @@ export function parseSubmission(input) {
1216
1255
  const securityProfile = parseRuntimeSecurityProfile(value.securityProfile);
1217
1256
  const metadata = optionalJsonRecord(value.metadata, "submission.metadata");
1218
1257
  const outputs = parseOutputs(value.outputs);
1219
- const builtins = parseBuiltins(value.builtins);
1258
+ const includeBuiltinTools = parseIncludeBuiltinTools(value.includeBuiltinTools);
1220
1259
  const outputMode = parseOutputMode(value.outputMode);
1221
1260
  const platform = parsePlatformConfig(value.platform);
1222
1261
  return {
@@ -1233,7 +1272,8 @@ export function parseSubmission(input) {
1233
1272
  ...(securityProfile ? { securityProfile } : {}),
1234
1273
  ...(metadata ? { metadata } : {}),
1235
1274
  ...(outputs ? { outputs } : {}),
1236
- ...(builtins !== undefined ? { builtins } : {}),
1275
+ ...(includeBuiltinTools !== undefined ? { includeBuiltinTools } : {}),
1276
+ ...(builtinTools.length > 0 ? { builtinTools } : {}),
1237
1277
  ...(outputMode !== undefined ? { outputMode } : {}),
1238
1278
  ...(platform ? { platform } : {})
1239
1279
  };
@@ -1297,108 +1337,105 @@ function parseOutputMode(input) {
1297
1337
  return input;
1298
1338
  }
1299
1339
  /**
1300
- * Managed-runtime builtins the closed set the managed runtime accepts.
1301
- * Closed so an invalid name is a compile error via {@link Builtins}, not a
1302
- * silent runtime no-op.
1340
+ * The CLOSED set of builtin tool NAMES the managed runtime can inject — one per
1341
+ * machine tool the hands implement. This list is the single source of truth for
1342
+ * validating builtin tool references; the platform's `HANDS_TOOLS` (the execute
1343
+ * vocabulary) is pinned EQUAL to it at module load (`platform-runtime-agent`
1344
+ * `assertNamesMatch`), so a rename on either side fails loudly rather than
1345
+ * silently shipping a name the executors do not speak.
1303
1346
  *
1304
- * The first entries are the recommended concrete builtins. The legacy aggregate
1305
- * extension names remain accepted for existing callers, but are not the default.
1347
+ * Order mirrors `HANDS_TOOLS`. A builtin tool reference (a bare string in
1348
+ * `submission.tools`) must be a member of this set.
1306
1349
  */
1307
- export const BUILTINS = [
1308
- "web_search",
1309
- "web_fetch",
1310
- "read",
1311
- "edit",
1312
- "glob",
1313
- "grep",
1314
- "head",
1315
- "tail",
1350
+ export const BUILTIN_TOOL_NAMES = [
1316
1351
  "bash",
1317
- "notebook",
1318
- "developer",
1319
- "computercontroller",
1320
- "memory",
1321
- "autovisualiser",
1322
- "tutorial"
1323
- ];
1324
- /**
1325
- * DX-first managed-runtime defaults. Omitted `builtins` resolves to this list.
1326
- * Notebook support remains opt-in through {@link Builtins.NOTEBOOK}.
1327
- */
1328
- export const DEFAULT_BUILTINS = [
1329
- "web_search",
1330
- "web_fetch",
1331
- "read",
1332
- "edit",
1333
- "glob",
1352
+ "read_file",
1353
+ "write_file",
1354
+ "edit_file",
1334
1355
  "grep",
1356
+ "glob",
1335
1357
  "head",
1336
1358
  "tail",
1337
- "bash"
1359
+ "todo_write",
1360
+ "subagent",
1361
+ "subagent_result",
1362
+ "web_fetch",
1363
+ "web_search",
1364
+ "notebook_edit",
1365
+ "bash_output",
1366
+ "bash_kill",
1367
+ "code_execution",
1368
+ "wait",
1369
+ "git"
1338
1370
  ];
1339
1371
  /**
1340
- * Symbol-style accessors for the closed builtin set, e.g.
1341
- * `Builtins.WEB_SEARCH`.
1372
+ * Typo-safe accessors for the closed builtin tool set: each key maps to the
1373
+ * real tool NAME string. Reference a builtin in `submission.tools` via
1374
+ * `BuiltinTools.notebook_edit` rather than the bare string so a rename is a
1375
+ * compile error, not a runtime 400.
1376
+ *
1377
+ * Keys are the real tool names; a unit test asserts `Object.values(BuiltinTools)`
1378
+ * deep-equals `BUILTIN_TOOL_NAMES` so the two can never drift.
1342
1379
  */
1343
- export const Builtins = {
1344
- /** Managed web search. Included in {@link DEFAULT_BUILTINS}. */
1345
- WEB_SEARCH: "web_search",
1346
- /** Fetch a URL and return readable text. Included in {@link DEFAULT_BUILTINS}. */
1347
- WEB_FETCH: "web_fetch",
1348
- /** Read files. Included in {@link DEFAULT_BUILTINS}. */
1349
- READ: "read",
1350
- /** Create/modify files. Included in {@link DEFAULT_BUILTINS}. */
1351
- EDIT: "edit",
1352
- /** Search paths by glob. Included in {@link DEFAULT_BUILTINS}. */
1353
- GLOB: "glob",
1354
- /** Search file contents. Included in {@link DEFAULT_BUILTINS}. */
1355
- GREP: "grep",
1356
- /** Read the first lines of a file. Included in {@link DEFAULT_BUILTINS}. */
1357
- HEAD: "head",
1358
- /** Read the last lines of a file. Included in {@link DEFAULT_BUILTINS}. */
1359
- TAIL: "tail",
1360
- /** Shell command execution. Included in {@link DEFAULT_BUILTINS}. */
1361
- BASH: "bash",
1362
- /** Jupyter notebook editing. Optional; not in {@link DEFAULT_BUILTINS}. */
1363
- NOTEBOOK: "notebook",
1364
- /** Legacy aggregate: shell/filesystem/navigation/web/notebook tools. */
1365
- DEVELOPER: "developer",
1366
- /** Legacy aggregate alias retained for existing callers. */
1367
- COMPUTER_CONTROLLER: "computercontroller",
1368
- /** Legacy aggregate alias retained for existing callers. */
1369
- MEMORY: "memory",
1370
- /** Legacy aggregate alias retained for existing callers. */
1371
- AUTOVISUALISER: "autovisualiser",
1372
- /** Legacy aggregate alias retained for existing callers. */
1373
- TUTORIAL: "tutorial"
1380
+ export const BuiltinTools = {
1381
+ bash: "bash",
1382
+ read_file: "read_file",
1383
+ write_file: "write_file",
1384
+ edit_file: "edit_file",
1385
+ grep: "grep",
1386
+ glob: "glob",
1387
+ head: "head",
1388
+ tail: "tail",
1389
+ todo_write: "todo_write",
1390
+ subagent: "subagent",
1391
+ subagent_result: "subagent_result",
1392
+ web_fetch: "web_fetch",
1393
+ web_search: "web_search",
1394
+ notebook_edit: "notebook_edit",
1395
+ bash_output: "bash_output",
1396
+ bash_kill: "bash_kill",
1397
+ code_execution: "code_execution",
1398
+ wait: "wait",
1399
+ git: "git"
1374
1400
  };
1375
- const MAX_BUILTINS = 16;
1376
- function parseBuiltins(input) {
1401
+ /**
1402
+ * The default builtin tool set injected when `includeBuiltinTools !== false`:
1403
+ * every builtin tool EXCEPT `notebook_edit` (notebook editing stays opt-in —
1404
+ * add `BuiltinTools.notebook_edit` to `tools` to enable it). Derived by
1405
+ * filtering {@link BUILTIN_TOOL_NAMES} so it can never drift from the closed
1406
+ * set.
1407
+ */
1408
+ export const DEFAULT_BUILTIN_TOOLS = BUILTIN_TOOL_NAMES.filter((name) => name !== "notebook_edit");
1409
+ /**
1410
+ * Resolve the set of builtin tool NAMES a submission injects, deduplicated and
1411
+ * in {@link BUILTIN_TOOL_NAMES} order.
1412
+ *
1413
+ * - `includeBuiltinTools !== false` ⇒ start from {@link DEFAULT_BUILTIN_TOOLS}
1414
+ * (the standard set); `false` ⇒ start from none (pure-MCP / pure-custom).
1415
+ * - union in every builtin-name string the caller listed in `tools` (a
1416
+ * cherry-pick, e.g. `BuiltinTools.notebook_edit` to opt the notebook in).
1417
+ *
1418
+ * Every `toolRefs` string MUST be a member of {@link BUILTIN_TOOL_NAMES}; the
1419
+ * union is validated ⊆ the closed set so an invalid name can never leak through.
1420
+ */
1421
+ export function resolveBuiltinToolNames(includeBuiltinTools, toolRefs) {
1422
+ const enabled = new Set(includeBuiltinTools !== false ? DEFAULT_BUILTIN_TOOLS : []);
1423
+ for (const ref of toolRefs ?? []) {
1424
+ if (!BUILTIN_TOOL_NAMES.includes(ref)) {
1425
+ throw new Error(`${JSON.stringify(ref)} is not a builtin tool; expected one of: ${BUILTIN_TOOL_NAMES.join(", ")}`);
1426
+ }
1427
+ enabled.add(ref);
1428
+ }
1429
+ return BUILTIN_TOOL_NAMES.filter((name) => enabled.has(name));
1430
+ }
1431
+ /** Validate the optional `includeBuiltinTools` flag (default `true`). */
1432
+ function parseIncludeBuiltinTools(input) {
1377
1433
  if (input === undefined || input === null)
1378
1434
  return undefined;
1379
- if (!Array.isArray(input)) {
1380
- throw new Error("submission.builtins must be an array of strings");
1381
- }
1382
- if (input.length > MAX_BUILTINS) {
1383
- throw new Error(`submission.builtins exceeds the max of ${MAX_BUILTINS} entries`);
1384
- }
1385
- const seen = new Set();
1386
- const out = [];
1387
- for (let i = 0; i < input.length; i++) {
1388
- const v = input[i];
1389
- if (typeof v !== "string") {
1390
- throw new Error(`submission.builtins[${i}] must be a string`);
1391
- }
1392
- if (!BUILTINS.includes(v)) {
1393
- throw new Error(`submission.builtins[${i}] (${JSON.stringify(v)}) is not a managed-runtime builtin; ` +
1394
- `expected one of: ${BUILTINS.join(", ")}`);
1395
- }
1396
- if (seen.has(v))
1397
- continue; // dedupe silently
1398
- seen.add(v);
1399
- out.push(v);
1435
+ if (typeof input !== "boolean") {
1436
+ throw new Error("submission.includeBuiltinTools must be a boolean");
1400
1437
  }
1401
- return out;
1438
+ return input;
1402
1439
  }
1403
1440
  /**
1404
1441
  * Maximum number of output capture entries accepted per list.
@@ -1626,17 +1663,35 @@ function parseSkills(input) {
1626
1663
  return ref;
1627
1664
  });
1628
1665
  }
1666
+ /**
1667
+ * Parse the `submission.tools` union: each entry is either a BARE STRING (a
1668
+ * builtin tool reference, validated against {@link BUILTIN_TOOL_NAMES}) or a
1669
+ * custom tool bundle OBJECT ({@link ToolRef}). Returns the two groups split:
1670
+ * `tools` (custom bundles, the existing downstream shape) and `builtinTools`
1671
+ * (the deduped builtin-name references, in {@link BUILTIN_TOOL_NAMES} order).
1672
+ */
1629
1673
  function parseTools(input) {
1630
1674
  if (input === undefined) {
1631
- return [];
1675
+ return { tools: [], builtinTools: [] };
1632
1676
  }
1633
1677
  if (!Array.isArray(input)) {
1634
- throw new Error("submission.tools must be an array of ToolRef objects");
1678
+ throw new Error("submission.tools must be an array of builtin tool names or ToolRef objects");
1635
1679
  }
1636
1680
  const seenNames = new Set();
1637
1681
  const seenAssetIds = new Set();
1638
- return input.map((item, index) => {
1682
+ const seenBuiltins = new Set();
1683
+ const tools = [];
1684
+ input.forEach((item, index) => {
1639
1685
  const path = `submission.tools[${index}]`;
1686
+ // A bare string is a builtin tool reference (e.g. BuiltinTools.notebook_edit).
1687
+ if (typeof item === "string") {
1688
+ if (!BUILTIN_TOOL_NAMES.includes(item)) {
1689
+ throw new Error(`${path} (${JSON.stringify(item)}) is not a builtin tool name; ` +
1690
+ `expected one of: ${BUILTIN_TOOL_NAMES.join(", ")}`);
1691
+ }
1692
+ seenBuiltins.add(item);
1693
+ return;
1694
+ }
1640
1695
  const raw = requireRecord(item, path);
1641
1696
  for (const key of Object.keys(raw)) {
1642
1697
  if (key !== "kind" &&
@@ -1678,15 +1733,17 @@ function parseTools(input) {
1678
1733
  throw new Error(`${path}.input_schema.type must be "object"`);
1679
1734
  }
1680
1735
  const entry = normaliseSkillBundlePath(requireString(raw.entry, `${path}.entry`));
1681
- return {
1736
+ tools.push({
1682
1737
  kind: "asset",
1683
1738
  assetId: fields.assetId,
1684
1739
  name: fields.name,
1685
1740
  description,
1686
1741
  input_schema: inputSchema,
1687
1742
  entry
1688
- };
1743
+ });
1689
1744
  });
1745
+ const builtinTools = BUILTIN_TOOL_NAMES.filter((name) => seenBuiltins.has(name));
1746
+ return { tools, builtinTools };
1690
1747
  }
1691
1748
  function parseAgentsMd(input) {
1692
1749
  if (input === undefined)
package/dist/cli.mjs CHANGED
@@ -798,6 +798,28 @@ function parseRunRegion(input) {
798
798
  }
799
799
  return input;
800
800
  }
801
+ var BUILTIN_TOOL_NAMES = [
802
+ "bash",
803
+ "read_file",
804
+ "write_file",
805
+ "edit_file",
806
+ "grep",
807
+ "glob",
808
+ "head",
809
+ "tail",
810
+ "todo_write",
811
+ "subagent",
812
+ "subagent_result",
813
+ "web_fetch",
814
+ "web_search",
815
+ "notebook_edit",
816
+ "bash_output",
817
+ "bash_kill",
818
+ "code_execution",
819
+ "wait",
820
+ "git"
821
+ ];
822
+ var DEFAULT_BUILTIN_TOOLS = BUILTIN_TOOL_NAMES.filter((name) => name !== "notebook_edit");
801
823
  var MAX_OUTPUT_CAPTURE_TIMEOUT_MS = 6 * 60 * 60 * 1e3;
802
824
 
803
825
  // ../contracts/dist/connection-ticket.js
@@ -950,7 +972,7 @@ function highEntropyShannonBits(value) {
950
972
  return bits;
951
973
  }
952
974
  function isForbiddenCustodyFieldName(key) {
953
- return /^(apiKey|secretValue|bearerHash|signedUrl|objectStoreKey|objectKey|vaultId|providerResponseBody|responseBody|privateResourceHandle|resourceHandle|rawBody)$/i.test(key);
975
+ return /^(apiKey|apiKeys|secretValue|bearerHash|signedUrl|objectStoreKey|objectKey|vaultId|providerResponseBody|responseBody|privateResourceHandle|resourceHandle|rawBody)$/i.test(key);
954
976
  }
955
977
 
956
978
  // ../contracts/dist/run-record.js
@@ -1376,6 +1398,8 @@ function extractErrorMessage(body) {
1376
1398
  // ../contracts/dist/operations.js
1377
1399
  var operations_exports = {};
1378
1400
  __export(operations_exports, {
1401
+ READ_OUTPUT_TEXT_DEFAULT_BYTES: () => READ_OUTPUT_TEXT_DEFAULT_BYTES,
1402
+ READ_OUTPUT_TEXT_MAX_BYTES: () => READ_OUTPUT_TEXT_MAX_BYTES,
1379
1403
  cancelRun: () => cancelRun,
1380
1404
  classifyOutput: () => classifyOutput,
1381
1405
  createAgentsMd: () => createAgentsMd,
@@ -1414,10 +1438,12 @@ __export(operations_exports, {
1414
1438
  listFiles: () => listFiles,
1415
1439
  listOutputs: () => listOutputs,
1416
1440
  listRunEvents: () => listRunEvents,
1441
+ listRuns: () => listRuns,
1417
1442
  listSecrets: () => listSecrets,
1418
1443
  listSkills: () => listSkills,
1419
1444
  normalizeOutputLinkExpiresIn: () => normalizeOutputLinkExpiresIn,
1420
1445
  outputLink: () => outputLink,
1446
+ readOutputText: () => readOutputText,
1421
1447
  redeliverRunWebhook: () => redeliverRunWebhook,
1422
1448
  resolveOutputFileSelector: () => resolveOutputFileSelector,
1423
1449
  rotateSecret: () => rotateSecret,
@@ -2151,6 +2177,18 @@ async function getRun(http, runId) {
2151
2177
  async function getRunUnit(http, runId) {
2152
2178
  return http.request(`/api/runs/${encodeURIComponent(runId)}`);
2153
2179
  }
2180
+ async function listRuns(http, query) {
2181
+ const params = {};
2182
+ if (query?.status !== void 0)
2183
+ params.status = query.status;
2184
+ if (query?.since !== void 0)
2185
+ params.since = query.since;
2186
+ if (query?.limit !== void 0)
2187
+ params.limit = String(query.limit);
2188
+ if (query?.cursor !== void 0)
2189
+ params.cursor = query.cursor;
2190
+ return http.request("/api/runs", {}, params);
2191
+ }
2154
2192
  var LIST_EVENTS_PAGE_BUDGET = 1e3;
2155
2193
  async function listRunEvents(http, runId) {
2156
2194
  const path = `/api/runs/${encodeURIComponent(runId)}/events`;
@@ -2249,6 +2287,74 @@ async function downloadOutput(http, runId, selector) {
2249
2287
  const { response } = await http.download(`/api/runs/${encodeURIComponent(runId)}/outputs/${encodeURIComponent(output.id)}/download`);
2250
2288
  return { output, bytes: new Uint8Array(await response.arrayBuffer()) };
2251
2289
  }
2290
+ var READ_OUTPUT_TEXT_MAX_BYTES = 1e7;
2291
+ var READ_OUTPUT_TEXT_DEFAULT_BYTES = 5e4;
2292
+ async function readOutputText(http, runId, selector, options) {
2293
+ const maxBytes = Math.max(1, Math.min(options?.maxBytes ?? READ_OUTPUT_TEXT_DEFAULT_BYTES, READ_OUTPUT_TEXT_MAX_BYTES));
2294
+ const output = isPathSelector(selector) ? resolveOutputFileSelector(await listOutputs(http, runId), selector, runId) : resolveOutputFileSelector([], selector, runId);
2295
+ const { response } = await http.download(`/api/runs/${encodeURIComponent(runId)}/outputs/${encodeURIComponent(output.id)}/download`);
2296
+ const capped = await readCappedText(response, maxBytes);
2297
+ const text = options?.grep === void 0 ? capped.text : grepLines(capped.text, options.grep);
2298
+ return { output, text, truncated: capped.truncated, totalBytes: capped.totalBytes };
2299
+ }
2300
+ async function readCappedText(response, maxBytes) {
2301
+ const declaredRaw = response.headers.get("content-length");
2302
+ const declared = declaredRaw !== null && /^\d+$/.test(declaredRaw) ? Number(declaredRaw) : void 0;
2303
+ const decoder = new TextDecoder("utf-8");
2304
+ const body = response.body;
2305
+ if (!body) {
2306
+ const buf = new Uint8Array(await response.arrayBuffer());
2307
+ const total = declared ?? buf.byteLength;
2308
+ return {
2309
+ text: decoder.decode(buf.subarray(0, maxBytes)),
2310
+ truncated: buf.byteLength > maxBytes,
2311
+ totalBytes: total
2312
+ };
2313
+ }
2314
+ const reader = body.getReader();
2315
+ const chunks = [];
2316
+ let read = 0;
2317
+ let sawMore = false;
2318
+ try {
2319
+ while (read < maxBytes) {
2320
+ const { done, value } = await reader.read();
2321
+ if (done)
2322
+ break;
2323
+ if (value && value.byteLength > 0) {
2324
+ read += value.byteLength;
2325
+ chunks.push(value);
2326
+ }
2327
+ }
2328
+ if (read >= maxBytes) {
2329
+ const next = await reader.read();
2330
+ if (!next.done && next.value && next.value.byteLength > 0)
2331
+ sawMore = true;
2332
+ }
2333
+ } finally {
2334
+ await reader.cancel().catch(() => {
2335
+ });
2336
+ }
2337
+ const merged = concatBytes(chunks).subarray(0, maxBytes);
2338
+ const truncated = declared !== void 0 ? declared > maxBytes : sawMore;
2339
+ const totalBytes = declared ?? read;
2340
+ return { text: decoder.decode(merged), truncated, totalBytes };
2341
+ }
2342
+ function concatBytes(chunks) {
2343
+ if (chunks.length === 1)
2344
+ return chunks[0];
2345
+ const total = chunks.reduce((n, c) => n + c.byteLength, 0);
2346
+ const out = new Uint8Array(total);
2347
+ let offset = 0;
2348
+ for (const c of chunks) {
2349
+ out.set(c, offset);
2350
+ offset += c.byteLength;
2351
+ }
2352
+ return out;
2353
+ }
2354
+ function grepLines(text, pattern) {
2355
+ const test = typeof pattern === "string" ? (line) => line.toLowerCase().includes(pattern.toLowerCase()) : (line) => pattern.test(line);
2356
+ return text.split("\n").filter((line) => test(line)).join("\n");
2357
+ }
2252
2358
  async function cancelRun(http, runId) {
2253
2359
  await http.request(`/api/runs/${encodeURIComponent(runId)}/cancel`, { method: "POST" });
2254
2360
  }
@@ -3408,15 +3514,6 @@ async function runRunCmd(io2, argv) {
3408
3514
  `);
3409
3515
  return USAGE_ERR;
3410
3516
  }
3411
- for (const p of RUN_PROVIDERS) {
3412
- if (p === provider)
3413
- continue;
3414
- if (providerKeyValues[p] !== void 0) {
3415
- io2.stderr(`--${p}-api-key is not allowed when --provider is ${provider}
3416
- `);
3417
- return USAGE_ERR;
3418
- }
3419
- }
3420
3517
  const runtimeFlag = takeFlagValue(rest, "--runtime");
3421
3518
  if (runtimeFlag.error) {
3422
3519
  io2.stderr(`${runtimeFlag.error}
@@ -3720,8 +3817,10 @@ async function runRunCmd(io2, argv) {
3720
3817
  ...runConfig.environment ? { environment: runConfig.environment } : {},
3721
3818
  ...runConfig.metadata ? { metadata: runConfig.metadata } : {}
3722
3819
  };
3820
+ const hasAdditionalProviderKeys = Object.keys(providerKeyValues).some((p) => p !== provider);
3723
3821
  const secrets = {
3724
3822
  apiKey: providerKeyValues[provider],
3823
+ ...hasAdditionalProviderKeys ? { apiKeys: providerKeyValues } : {},
3725
3824
  ...mcpServerSecrets.length > 0 ? { mcpServers: mcpServerSecrets } : {},
3726
3825
  ...proxyAuth.length > 0 ? { proxyEndpointAuth: proxyAuth } : {}
3727
3826
  };
@@ -1 +1 @@
1
- 65e49dd08ff380ad5b4235dbc0f2fd2e6a7a677f1d8f7b67df91b448aa4472a8 cli.mjs
1
+ 0386d2625bfa4f8aea426cffc3bf21546c48b33b7134eb7cd4bbfefdc2d69d96 cli.mjs