@aexhq/sdk 0.35.0 → 0.36.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.
Files changed (59) hide show
  1. package/README.md +16 -15
  2. package/dist/_contracts/index.d.ts +3 -4
  3. package/dist/_contracts/index.js +1 -4
  4. package/dist/_contracts/operations.d.ts +2 -1
  5. package/dist/_contracts/operations.js +10 -0
  6. package/dist/_contracts/run-config.d.ts +1 -3
  7. package/dist/_contracts/run-config.js +2 -7
  8. package/dist/_contracts/run-trace.d.ts +0 -86
  9. package/dist/_contracts/run-trace.js +1 -184
  10. package/dist/_contracts/run-unit.d.ts +2 -25
  11. package/dist/_contracts/run-unit.js +1 -2
  12. package/dist/_contracts/runtime-manifest.d.ts +1 -1
  13. package/dist/_contracts/runtime-security-profile.d.ts +0 -2
  14. package/dist/_contracts/runtime-security-profile.js +0 -9
  15. package/dist/_contracts/runtime-types.d.ts +25 -4
  16. package/dist/_contracts/stable.d.ts +1 -1
  17. package/dist/_contracts/stable.js +1 -1
  18. package/dist/_contracts/submission.d.ts +4 -72
  19. package/dist/_contracts/submission.js +5 -472
  20. package/dist/cli.mjs +20 -442
  21. package/dist/cli.mjs.sha256 +1 -1
  22. package/dist/client.d.ts +30 -25
  23. package/dist/client.js +251 -66
  24. package/dist/client.js.map +1 -1
  25. package/dist/index.d.ts +7 -15
  26. package/dist/index.js +5 -17
  27. package/dist/index.js.map +1 -1
  28. package/dist/secret.d.ts +2 -2
  29. package/dist/secret.js +1 -1
  30. package/dist/version.d.ts +1 -1
  31. package/dist/version.js +1 -1
  32. package/docs/concepts/composition.md +8 -14
  33. package/docs/credentials.md +59 -101
  34. package/docs/defaults.md +0 -8
  35. package/docs/events.md +8 -9
  36. package/docs/limits-and-quotas.md +1 -4
  37. package/docs/limits.md +2 -6
  38. package/docs/mcp.md +4 -5
  39. package/docs/networking.md +6 -16
  40. package/docs/outputs.md +0 -4
  41. package/docs/public-surface.json +3 -3
  42. package/docs/quickstart.md +3 -7
  43. package/docs/run-config.md +6 -3
  44. package/docs/secrets.md +1 -1
  45. package/docs/skills.md +3 -3
  46. package/docs/vision-skills.md +52 -101
  47. package/examples/feature-tour.ts +4 -21
  48. package/package.json +1 -1
  49. package/dist/_contracts/proxy-protocol.d.ts +0 -305
  50. package/dist/_contracts/proxy-protocol.js +0 -297
  51. package/dist/_contracts/proxy-validation.d.ts +0 -19
  52. package/dist/_contracts/proxy-validation.js +0 -51
  53. package/dist/data-tools.d.ts +0 -82
  54. package/dist/data-tools.js +0 -251
  55. package/dist/data-tools.js.map +0 -1
  56. package/dist/proxy-endpoint.d.ts +0 -131
  57. package/dist/proxy-endpoint.js +0 -144
  58. package/dist/proxy-endpoint.js.map +0 -1
  59. package/examples/chat-corpus.ts +0 -84
@@ -1,9 +1,3 @@
1
- import { authShapeHeaderName, PROXY_ALLOWED_METHODS, PROXY_ENDPOINT_DEFAULTS, PROXY_RETRY_JITTERS, PROXY_RETRY_POLICY_DEFAULTS, PROXY_RESPONSE_MODES } from "./proxy-protocol.js";
2
- // Re-exported from the protocol module (its canonical home, alongside the
3
- // index-file shape the builder fills). Kept on the submission surface so
4
- // existing `@aexhq/contracts` consumers of `PROXY_ENDPOINT_DEFAULTS` are
5
- // unaffected by the move.
6
- export { PROXY_ENDPOINT_DEFAULTS };
7
1
  import { TOOL_NAME_PATTERN, normaliseSkillBundlePath, parseAssetRefFields, parseMcpServerRef } from "./run-config.js";
8
2
  import { parseRunTimeout, parseRuntimeSize } from "./runtime-sizes.js";
9
3
  import { assertRunModelMatchesProvider, parseRunModel } from "./models.js";
@@ -118,29 +112,6 @@ export const SECRETS_KEY = "secrets";
118
112
  export const SECRET_ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
119
113
  /** Workspace secret handle a `secretEnv` ref points at (and the name `secret.upload` persists to). */
120
114
  export const SECRET_HANDLE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
121
- export const PROXY_ENDPOINT_NAME_PATTERN = /^[a-z][a-z0-9_-]{0,62}$/;
122
- export const RESERVED_PROXY_ENDPOINT_NAMES = new Set(["proxy", "aex", "internal", "admin"]);
123
- /**
124
- * Headers the proxy never lets through, regardless of policy. Lowercase.
125
- * Anything that could re-introduce credentials, cookies, or routing
126
- * primitives. Kept in lockstep with the proxy route's reject list.
127
- */
128
- const PROXY_DENY_HEADER_LIST = new Set([
129
- "authorization",
130
- "cookie",
131
- "set-cookie",
132
- "proxy-authorization",
133
- "host",
134
- "content-length",
135
- "transfer-encoding",
136
- "connection",
137
- "upgrade",
138
- "expect",
139
- "x-forwarded-for",
140
- "x-forwarded-host",
141
- "x-forwarded-proto",
142
- "x-real-ip"
143
- ]);
144
115
  export const deniedSecretFields = new Set([
145
116
  "providerApiKey",
146
117
  "anthropicApiKey",
@@ -313,322 +284,9 @@ function parsePackages(input) {
313
284
  return version ? { name, version, ecosystem } : { name, ecosystem };
314
285
  });
315
286
  }
316
- function parseProxyEndpoints(input) {
317
- if (input === undefined) {
318
- return undefined;
319
- }
320
- if (!Array.isArray(input)) {
321
- throw new Error("proxyEndpoints must be an array");
322
- }
323
- if (input.length === 0) {
324
- return undefined;
325
- }
326
- const seen = new Set();
327
- return input.map((entry, index) => {
328
- const endpoint = parseProxyEndpoint(entry, `proxyEndpoints[${index}]`);
329
- if (seen.has(endpoint.name)) {
330
- throw new Error(`proxyEndpoints duplicate name: ${endpoint.name}`);
331
- }
332
- seen.add(endpoint.name);
333
- return endpoint;
334
- });
335
- }
336
- function parseProxyEndpoint(input, path) {
337
- const value = requireRecord(input, path);
338
- const allowed = new Set([
339
- "name",
340
- "baseUrl",
341
- "authShape",
342
- "allowMethods",
343
- "allowPathPrefixes",
344
- "allowHeaders",
345
- "responseMode",
346
- "maxRequestBytes",
347
- "maxResponseBytes",
348
- "timeoutMs",
349
- "retry"
350
- ]);
351
- for (const key of Object.keys(value)) {
352
- if (!allowed.has(key)) {
353
- throw new Error(`${path}.${key} is not an allowed field`);
354
- }
355
- }
356
- const name = requireString(value.name, `${path}.name`);
357
- if (!PROXY_ENDPOINT_NAME_PATTERN.test(name)) {
358
- throw new Error(`${path}.name must match ${PROXY_ENDPOINT_NAME_PATTERN} (lowercase letters, digits, '_' and '-'; <=63 chars)`);
359
- }
360
- if (RESERVED_PROXY_ENDPOINT_NAMES.has(name)) {
361
- throw new Error(`${path}.name is reserved: ${name}`);
362
- }
363
- const baseUrl = parseProxyBaseUrl(value.baseUrl, `${path}.baseUrl`);
364
- const authShape = parseProxyAuthShape(value.authShape, `${path}.authShape`);
365
- const allowMethods = parseProxyMethods(value.allowMethods, `${path}.allowMethods`);
366
- const allowPathPrefixes = parseProxyPathPrefixes(value.allowPathPrefixes, `${path}.allowPathPrefixes`);
367
- const allowHeaders = parseProxyAllowedHeaders(value.allowHeaders, `${path}.allowHeaders`, authShape);
368
- const responseMode = optionalEnum(value.responseMode, `${path}.responseMode`, PROXY_RESPONSE_MODES);
369
- const maxRequestBytes = optionalPositiveInt(value.maxRequestBytes, `${path}.maxRequestBytes`);
370
- const maxResponseBytes = optionalPositiveInt(value.maxResponseBytes, `${path}.maxResponseBytes`);
371
- const timeoutMs = optionalPositiveInt(value.timeoutMs, `${path}.timeoutMs`);
372
- const retry = parseProxyRetryPolicy(value.retry, `${path}.retry`);
373
- return {
374
- name,
375
- baseUrl,
376
- authShape,
377
- allowMethods,
378
- allowPathPrefixes,
379
- ...(allowHeaders ? { allowHeaders } : {}),
380
- ...(responseMode ? { responseMode } : {}),
381
- ...(maxRequestBytes !== undefined ? { maxRequestBytes } : {}),
382
- ...(maxResponseBytes !== undefined ? { maxResponseBytes } : {}),
383
- ...(timeoutMs !== undefined ? { timeoutMs } : {}),
384
- ...(retry !== undefined ? { retry } : {})
385
- };
386
- }
387
- export function parseProxyRetryPolicy(input, field) {
388
- if (input === undefined) {
389
- return undefined;
390
- }
391
- const value = requireRecord(input, field);
392
- const allowed = new Set([
393
- "maxAttempts",
394
- "initialDelayMs",
395
- "maxDelayMs",
396
- "jitter",
397
- "retryOnStatuses",
398
- "retryOnMethods",
399
- "respectRetryAfter"
400
- ]);
401
- for (const key of Object.keys(value)) {
402
- if (!allowed.has(key)) {
403
- throw new Error(`${field}.${key} is not an allowed field`);
404
- }
405
- }
406
- const maxAttempts = parseOptionalBoundedInt(value.maxAttempts, `${field}.maxAttempts`, 1, 5);
407
- const initialDelayMs = optionalPositiveInt(value.initialDelayMs, `${field}.initialDelayMs`);
408
- const maxDelayMs = optionalPositiveInt(value.maxDelayMs, `${field}.maxDelayMs`);
409
- const effectiveInitialDelayMs = initialDelayMs ?? PROXY_RETRY_POLICY_DEFAULTS.initialDelayMs;
410
- const effectiveMaxDelayMs = maxDelayMs ?? PROXY_RETRY_POLICY_DEFAULTS.maxDelayMs;
411
- if (effectiveMaxDelayMs < effectiveInitialDelayMs) {
412
- throw new Error(`${field}.maxDelayMs must be greater than or equal to ${field}.initialDelayMs`);
413
- }
414
- const jitter = optionalEnum(value.jitter, `${field}.jitter`, PROXY_RETRY_JITTERS);
415
- const retryOnStatuses = parseProxyRetryStatuses(value.retryOnStatuses, `${field}.retryOnStatuses`);
416
- const retryOnMethods = parseProxyRetryMethods(value.retryOnMethods, `${field}.retryOnMethods`);
417
- const respectRetryAfter = parseOptionalBoolean(value.respectRetryAfter, `${field}.respectRetryAfter`);
418
- return {
419
- ...(maxAttempts !== undefined ? { maxAttempts } : {}),
420
- ...(initialDelayMs !== undefined ? { initialDelayMs } : {}),
421
- ...(maxDelayMs !== undefined ? { maxDelayMs } : {}),
422
- ...(jitter !== undefined ? { jitter } : {}),
423
- ...(retryOnStatuses !== undefined ? { retryOnStatuses } : {}),
424
- ...(retryOnMethods !== undefined ? { retryOnMethods } : {}),
425
- ...(respectRetryAfter !== undefined ? { respectRetryAfter } : {})
426
- };
427
- }
428
- function parseProxyRetryStatuses(input, field) {
429
- if (input === undefined) {
430
- return undefined;
431
- }
432
- if (!Array.isArray(input)) {
433
- throw new Error(`${field} must be an array of HTTP status codes`);
434
- }
435
- const seen = new Set();
436
- for (const entry of input) {
437
- if (typeof entry !== "number" ||
438
- !Number.isSafeInteger(entry) ||
439
- entry < 100 ||
440
- entry > 599) {
441
- throw new Error(`${field} entries must be HTTP status codes between 100 and 599`);
442
- }
443
- seen.add(entry);
444
- }
445
- return Array.from(seen);
446
- }
447
- function parseProxyRetryMethods(input, field) {
448
- if (input === undefined) {
449
- return undefined;
450
- }
451
- if (!Array.isArray(input)) {
452
- throw new Error(`${field} must be an array of HTTP methods`);
453
- }
454
- const seen = new Set();
455
- for (const entry of input) {
456
- if (typeof entry !== "string") {
457
- throw new Error(`${field} entries must be strings`);
458
- }
459
- const upper = entry.toUpperCase();
460
- if (!PROXY_ALLOWED_METHODS.includes(upper)) {
461
- throw new Error(`${field} contains unsupported method: ${entry}`);
462
- }
463
- seen.add(upper);
464
- }
465
- return Array.from(seen);
466
- }
467
- function parseProxyBaseUrl(input, field) {
468
- const raw = requireString(input, field);
469
- let parsed;
470
- try {
471
- parsed = new URL(raw);
472
- }
473
- catch {
474
- throw new Error(`${field} must be a valid absolute URL`);
475
- }
476
- if (parsed.protocol !== "https:") {
477
- throw new Error(`${field} must use https://`);
478
- }
479
- if (parsed.username || parsed.password) {
480
- throw new Error(`${field} must not embed credentials`);
481
- }
482
- if (parsed.search || parsed.hash) {
483
- throw new Error(`${field} must not include a query string or fragment`);
484
- }
485
- // Normalize: strip trailing slash so prefix matching is predictable.
486
- const normalized = `${parsed.origin}${parsed.pathname.replace(/\/+$/, "")}`;
487
- return normalized;
488
- }
489
- export function parseProxyAuthShape(input, field) {
490
- const value = requireRecord(input, field);
491
- const type = requireString(value.type, `${field}.type`);
492
- switch (type) {
493
- case "none":
494
- assertOnlyKeys(value, field, ["type"]);
495
- return { type: "none" };
496
- case "bearer":
497
- assertOnlyKeys(value, field, ["type"]);
498
- return { type: "bearer" };
499
- case "basic":
500
- assertOnlyKeys(value, field, ["type"]);
501
- return { type: "basic" };
502
- case "header": {
503
- assertOnlyKeys(value, field, ["type", "name"]);
504
- const name = requireString(value.name, `${field}.name`);
505
- assertHeaderName(name, `${field}.name`);
506
- return { type: "header", name };
507
- }
508
- case "query": {
509
- assertOnlyKeys(value, field, ["type", "name"]);
510
- const name = requireString(value.name, `${field}.name`);
511
- if (!/^[a-zA-Z0-9_\-.]{1,64}$/.test(name)) {
512
- throw new Error(`${field}.name must be a URL-safe identifier (<=64 chars)`);
513
- }
514
- return { type: "query", name };
515
- }
516
- default:
517
- throw new Error(`${field}.type must be one of: none, bearer, basic, header, query`);
518
- }
519
- }
520
- export function parseProxyMethods(input, field) {
521
- if (!Array.isArray(input) || input.length === 0) {
522
- throw new Error(`${field} must be a non-empty array of HTTP methods`);
523
- }
524
- const seen = new Set();
525
- for (const entry of input) {
526
- if (typeof entry !== "string") {
527
- throw new Error(`${field} entries must be strings`);
528
- }
529
- const upper = entry.toUpperCase();
530
- if (!PROXY_ALLOWED_METHODS.includes(upper)) {
531
- throw new Error(`${field} contains unsupported method: ${entry}`);
532
- }
533
- seen.add(upper);
534
- }
535
- return Array.from(seen);
536
- }
537
- export function parseProxyPathPrefixes(input, field) {
538
- if (!Array.isArray(input) || input.length === 0) {
539
- throw new Error(`${field} must be a non-empty array of path prefixes`);
540
- }
541
- const seen = new Set();
542
- for (const entry of input) {
543
- if (typeof entry !== "string" || !entry.startsWith("/")) {
544
- throw new Error(`${field} entries must be non-empty strings starting with '/'`);
545
- }
546
- // Reject traversal / encoded traversal at config time so we never
547
- // need to second-guess at request time.
548
- if (entry.includes("..") || entry.toLowerCase().includes("%2e%2e")) {
549
- throw new Error(`${field} entry must not contain path traversal: ${entry}`);
550
- }
551
- seen.add(entry);
552
- }
553
- return Array.from(seen);
554
- }
555
- export function parseProxyAllowedHeaders(input, field, authShape) {
556
- if (input === undefined) {
557
- return undefined;
558
- }
559
- if (!Array.isArray(input)) {
560
- throw new Error(`${field} must be an array of header names`);
561
- }
562
- const seen = new Set();
563
- const result = [];
564
- for (const entry of input) {
565
- if (typeof entry !== "string" || entry.length === 0) {
566
- throw new Error(`${field} entries must be non-empty strings`);
567
- }
568
- const lower = entry.toLowerCase();
569
- assertHeaderName(entry, field);
570
- if (PROXY_DENY_HEADER_LIST.has(lower)) {
571
- throw new Error(`${field} contains a forbidden header: ${entry}`);
572
- }
573
- const authHeader = authShapeHeaderName(authShape);
574
- if (authHeader && lower === authHeader) {
575
- throw new Error(`${field} must not contain the auth header for this endpoint (${authHeader}); the proxy injects it from secrets.proxyEndpointAuth`);
576
- }
577
- if (seen.has(lower)) {
578
- continue;
579
- }
580
- seen.add(lower);
581
- result.push(lower);
582
- }
583
- return result;
584
- }
585
- function assertHeaderName(value, field) {
586
- // RFC 7230 token chars, conservative.
587
- if (!/^[A-Za-z0-9!#$%&'*+\-.^_`|~]{1,64}$/.test(value)) {
588
- throw new Error(`${field} must be a valid header token (<=64 chars): ${value}`);
589
- }
590
- }
591
- function assertOnlyKeys(value, field, allowed) {
592
- const permitted = new Set(allowed);
593
- for (const key of Object.keys(value)) {
594
- if (!permitted.has(key)) {
595
- throw new Error(`${field}.${key} is not an allowed field; permitted: ${allowed.join(", ")}`);
596
- }
597
- }
598
- }
599
- export function crossValidateProxyEndpointsAndAuth(endpoints, auth) {
600
- const endpointsList = endpoints ?? [];
601
- const authList = auth ?? [];
602
- const endpointsByName = new Map(endpointsList.map((e) => [e.name, e]));
603
- const authByName = new Map(authList.map((a) => [a.name, a]));
604
- for (const endpoint of endpointsList) {
605
- const authEntry = authByName.get(endpoint.name);
606
- if (endpoint.authShape.type === "none") {
607
- // Keyless endpoints carry no auth value. Reject any matching
608
- // auth entry so callers don't accidentally ship a secret bound
609
- // to a "none" endpoint (which would be silently ignored at
610
- // request time — confusing and a leak risk).
611
- if (authEntry) {
612
- throw new Error(`proxyEndpoints[${endpoint.name}] has authShape "none" but a matching secrets.proxyEndpointAuth entry was supplied; remove the auth entry`);
613
- }
614
- continue;
615
- }
616
- if (!authEntry) {
617
- throw new Error(`proxyEndpoints[${endpoint.name}] has no matching secrets.proxyEndpointAuth entry`);
618
- }
619
- if (authEntry.value.type !== endpoint.authShape.type) {
620
- throw new Error(`secrets.proxyEndpointAuth[${endpoint.name}].value.type must equal proxyEndpoints[${endpoint.name}].authShape.type (expected ${endpoint.authShape.type}, got ${authEntry.value.type})`);
621
- }
622
- }
623
- for (const authEntry of authList) {
624
- if (!endpointsByName.has(authEntry.name)) {
625
- throw new Error(`secrets.proxyEndpointAuth[${authEntry.name}] has no matching proxyEndpoints entry`);
626
- }
627
- }
628
- }
629
287
  /**
630
288
  * Cross-check `submission.secretEnv` declarations against `secrets.envSecrets`
631
- * values. Mirrors {@link crossValidateProxyEndpointsAndAuth}:
289
+ * values:
632
290
  *
633
291
  * - `{ ephemeral: true }` MUST have a matching `secrets.envSecrets` value.
634
292
  * - `{ ref }` MUST NOT supply a value (the value lives in the workspace store).
@@ -664,13 +322,12 @@ export function parseInlineSecrets(input) {
664
322
  if (input === undefined || input === null)
665
323
  return {};
666
324
  const value = requireRecord(input, "secrets");
667
- const allowedTopLevel = new Set(["apiKeys", "mcpServers", "proxyEndpointAuth", "envSecrets"]);
325
+ const allowedTopLevel = new Set(["apiKeys", "mcpServers", "envSecrets"]);
668
326
  for (const key of Object.keys(value)) {
669
327
  if (key.startsWith("__aex_")) {
670
- // Platform-internal namespace (e.g. __aex_proxy_token). The BFF
671
- // mutates the vaulted bundle to inject these; inbound submissions
672
- // are never allowed to set them, to prevent a malicious caller
673
- // from forging the bearer.
328
+ // Platform-internal namespace. The BFF may mutate the vaulted bundle
329
+ // to inject reserved values; inbound submissions are never allowed to
330
+ // set them.
674
331
  throw new Error(`secrets.${key} uses the platform-internal __aex_ namespace and may not be set by callers`);
675
332
  }
676
333
  if (!allowedTopLevel.has(key)) {
@@ -679,12 +336,10 @@ export function parseInlineSecrets(input) {
679
336
  }
680
337
  const apiKeys = parseApiKeys(value.apiKeys);
681
338
  const mcpServers = parseMcpServerSecrets(value.mcpServers);
682
- const proxyEndpointAuth = parseProxyEndpointAuth(value.proxyEndpointAuth);
683
339
  const envSecrets = parseEnvSecrets(value.envSecrets);
684
340
  return {
685
341
  ...(apiKeys ? { apiKeys } : {}),
686
342
  ...(mcpServers ? { mcpServers } : {}),
687
- ...(proxyEndpointAuth ? { proxyEndpointAuth } : {}),
688
343
  ...(envSecrets ? { envSecrets } : {})
689
344
  };
690
345
  }
@@ -755,103 +410,6 @@ function parseMcpServerSecret(input, path) {
755
410
  const headers = optionalStringRecord(value.headers, `${path}.headers`);
756
411
  return headers ? { name, url, headers } : { name, url };
757
412
  }
758
- function parseProxyEndpointAuth(input) {
759
- if (input === undefined) {
760
- return undefined;
761
- }
762
- if (!Array.isArray(input)) {
763
- throw new Error("secrets.proxyEndpointAuth must be an array");
764
- }
765
- if (input.length === 0) {
766
- return undefined;
767
- }
768
- const seen = new Set();
769
- return input.map((entry, index) => {
770
- const auth = parseProxyEndpointAuthEntry(entry, `secrets.proxyEndpointAuth[${index}]`);
771
- if (seen.has(auth.name)) {
772
- throw new Error(`secrets.proxyEndpointAuth duplicate name: ${auth.name}`);
773
- }
774
- seen.add(auth.name);
775
- return auth;
776
- });
777
- }
778
- function parseProxyEndpointAuthEntry(input, path) {
779
- const value = requireRecord(input, path);
780
- const allowed = new Set(["name", "value"]);
781
- for (const key of Object.keys(value)) {
782
- if (!allowed.has(key)) {
783
- throw new Error(`${path}.${key} is not an allowed field; permitted: name, value`);
784
- }
785
- }
786
- const name = requireString(value.name, `${path}.name`);
787
- if (!PROXY_ENDPOINT_NAME_PATTERN.test(name)) {
788
- throw new Error(`${path}.name must match the same pattern as proxyEndpoints[].name (lowercase letters, digits, '_' and '-'; <=63 chars)`);
789
- }
790
- const valueField = parseProxyAuthValue(value.value, `${path}.value`);
791
- return { name, value: valueField };
792
- }
793
- function parseProxyAuthValue(input, path) {
794
- const value = requireRecord(input, path);
795
- const type = requireString(value.type, `${path}.type`);
796
- switch (type) {
797
- case "bearer": {
798
- assertOnlyKeys(value, path, ["type", "token"]);
799
- const token = requireSecretValue(value.token, `${path}.token`);
800
- return { type: "bearer", token };
801
- }
802
- case "basic": {
803
- assertOnlyKeys(value, path, ["type", "username", "password"]);
804
- // Usernames are not redactable in the strict sense (often public
805
- // identifiers like an email), so we only enforce non-emptiness.
806
- // The password is the secret-bearing half.
807
- const username = requireString(value.username, `${path}.username`);
808
- const password = requireSecretValue(value.password, `${path}.password`);
809
- return { type: "basic", username, password };
810
- }
811
- case "header": {
812
- assertOnlyKeys(value, path, ["type", "value"]);
813
- const headerValue = requireSecretValue(value.value, `${path}.value`);
814
- return { type: "header", value: headerValue };
815
- }
816
- case "query": {
817
- assertOnlyKeys(value, path, ["type", "value"]);
818
- const queryValue = requireSecretValue(value.value, `${path}.value`);
819
- return { type: "query", value: queryValue };
820
- }
821
- default:
822
- throw new Error(`${path}.type must be one of: bearer, basic, header, query`);
823
- }
824
- }
825
- /**
826
- * The proxy body-redactor refuses to mask any derived target string shorter
827
- * than this many bytes — masking a 1-byte literal would corrupt the response
828
- * body. This is the floor for the *derived* redaction targets (e.g.
829
- * `Bearer <token>`, base64 fragments), used by
830
- * the hosted proxy redactor, which imports this constant so the two sides can
831
- * never silently diverge.
832
- */
833
- export const MIN_REDACTION_TARGET_BYTES = 4;
834
- /**
835
- * Minimum byte length for an accepted proxy secret *value*. Strictly greater
836
- * than {@link MIN_REDACTION_TARGET_BYTES}: a secret short enough to fall under
837
- * the redactor's floor could slip through unmasked, so the submission parser
838
- * rejects it up front. The `satisfies` check below pins that invariant at
839
- * compile time.
840
- */
841
- const MIN_PROXY_SECRET_BYTES = 8;
842
- // Invariant: an accepted secret must always be long enough for the redactor to
843
- // mask it. If someone lowers MIN_PROXY_SECRET_BYTES below the redaction floor,
844
- // this errors at compile time.
845
- const _MIN_PROXY_SECRET_BYTES_OK = (MIN_PROXY_SECRET_BYTES >= MIN_REDACTION_TARGET_BYTES);
846
- void _MIN_PROXY_SECRET_BYTES_OK;
847
- function requireSecretValue(input, field) {
848
- const value = requireString(input, field);
849
- const byteLen = Buffer.byteLength(value, "utf8");
850
- if (byteLen < MIN_PROXY_SECRET_BYTES) {
851
- throw new Error(`${field} must be at least ${MIN_PROXY_SECRET_BYTES} bytes; shorter values cannot be reliably redacted from upstream responses`);
852
- }
853
- return value;
854
- }
855
413
  export function assertNoSecretBearingFields(input, path) {
856
414
  if (Array.isArray(input)) {
857
415
  input.forEach((item, index) => assertNoSecretBearingFields(item, [...path, String(index)]));
@@ -949,27 +507,6 @@ export function optionalPositiveNumber(input, field) {
949
507
  }
950
508
  return input;
951
509
  }
952
- function parseOptionalBoundedInt(input, field, min, max) {
953
- if (input === undefined) {
954
- return undefined;
955
- }
956
- if (typeof input !== "number" ||
957
- !Number.isSafeInteger(input) ||
958
- input < min ||
959
- input > max) {
960
- throw new Error(`${field} must be a safe integer between ${min} and ${max}`);
961
- }
962
- return input;
963
- }
964
- function parseOptionalBoolean(input, field) {
965
- if (input === undefined) {
966
- return undefined;
967
- }
968
- if (typeof input !== "boolean") {
969
- throw new Error(`${field} must be a boolean`);
970
- }
971
- return input;
972
- }
973
510
  function isJsonValue(input) {
974
511
  if (typeof input === "number") {
975
512
  return Number.isFinite(input);
@@ -994,7 +531,6 @@ export function parseRunSubmissionRequest(input, options = {}) {
994
531
  "submission",
995
532
  "runtimeSize",
996
533
  "timeout",
997
- "proxyEndpoints",
998
534
  "webhook",
999
535
  "limits",
1000
536
  "machine",
@@ -1024,10 +560,8 @@ export function parseRunSubmissionRequest(input, options = {}) {
1024
560
  const webhook = parseRunWebhook(value.webhook);
1025
561
  const limits = parseRunLimits(value.limits);
1026
562
  const machine = parseRunMachine(value.machine);
1027
- const proxyEndpoints = parseProxyEndpoints(value.proxyEndpoints);
1028
563
  const secrets = parseInlineSecrets(value.secrets);
1029
564
  enforceCredentialSecretPolicy(secrets, provider);
1030
- crossValidateProxyEndpointsAndAuth(proxyEndpoints, secrets.proxyEndpointAuth);
1031
565
  const submission = parseSubmission(value.submission);
1032
566
  assertRunModelMatchesProvider(provider, submission.model);
1033
567
  crossValidateSecretEnvAndValues(submission.secretEnv, secrets.envSecrets);
@@ -1056,7 +590,6 @@ export function parseRunSubmissionRequest(input, options = {}) {
1056
590
  submission,
1057
591
  ...(runtimeSize ? { runtimeSize } : {}),
1058
592
  ...(timeoutMs !== undefined ? { timeoutMs } : {}),
1059
- ...(proxyEndpoints ? { proxyEndpoints } : {}),
1060
593
  ...(webhook !== undefined ? { webhook } : {}),
1061
594
  ...(limits !== undefined ? { limits } : {}),
1062
595
  ...(machine !== undefined ? { machine } : {}),