@runtypelabs/cli 2.42.3 → 2.43.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 (2) hide show
  1. package/dist/index.js +378 -102
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -11312,7 +11312,7 @@ function isDataConnectionToolId(toolId) {
11312
11312
  return toolId.startsWith("dc:");
11313
11313
  }
11314
11314
  //#endregion
11315
- //#region ../shared/dist/chunk-DTCym20H.mjs
11315
+ //#region ../shared/dist/chunk-CN8mv6N6.mjs
11316
11316
  /**
11317
11317
  * Maps model families to provider-specific model IDs.
11318
11318
  * Use for routing: family + provider → exact model ID
@@ -11745,6 +11745,11 @@ const MODEL_FAMILY_PROVIDER_IDS = {
11745
11745
  "vercel": "openai/gpt-5.6-terra"
11746
11746
  },
11747
11747
  "gpt-5.6-terra-fast": { "vercel": "openai/gpt-5.6-terra-fast" },
11748
+ "gpt-6-1-sol": {
11749
+ "openai": "gpt-6.1-sol",
11750
+ "vercel": "openai/gpt-6.1-sol"
11751
+ },
11752
+ "gpt-6-1-sol-fast": { "vercel": "openai/gpt-6.1-sol-fast" },
11748
11753
  "gpt-6-astra": {
11749
11754
  "openai": "gpt-6-astra",
11750
11755
  "vercel": "openai/gpt-6-astra"
@@ -11760,6 +11765,11 @@ const MODEL_FAMILY_PROVIDER_IDS = {
11760
11765
  "vercel": "openai/gpt-6-sol"
11761
11766
  },
11762
11767
  "gpt-6-sol-fast": { "vercel": "openai/gpt-6-sol-fast" },
11768
+ "gpt-6.1-sol": {
11769
+ "openai": "gpt-6.1-sol",
11770
+ "vercel": "openai/gpt-6.1-sol"
11771
+ },
11772
+ "gpt-6.1-sol-fast": { "vercel": "openai/gpt-6.1-sol-fast" },
11763
11773
  "gpt-image-1-5": { "openai": "gpt-image-1.5" },
11764
11774
  "gpt-image-1-mini": { "openai": "gpt-image-1-mini" },
11765
11775
  "gpt-image-1.5": { "openai": "gpt-image-1.5" },
@@ -11848,10 +11858,14 @@ const MODEL_FAMILY_PROVIDER_IDS = {
11848
11858
  "ling-3-0-flash-sante": { "vercel": "inclusionai/ling-3.0-flash-sante" },
11849
11859
  "ling-3-0-flash-sante-free": { "vercel": "inclusionai/ling-3.0-flash-sante-free" },
11850
11860
  "ling-3-0-flash-vl": { "vercel": "inclusionai/ling-3.0-flash-vl" },
11861
+ "ling-3-1-flash": { "vercel": "inclusionai/ling-3.1-flash" },
11862
+ "ling-3-1-flash-free": { "vercel": "inclusionai/ling-3.1-flash-free" },
11851
11863
  "ling-3.0-flash": { "vercel": "inclusionai/ling-3.0-flash" },
11852
11864
  "ling-3.0-flash-sante": { "vercel": "inclusionai/ling-3.0-flash-sante" },
11853
11865
  "ling-3.0-flash-sante-free": { "vercel": "inclusionai/ling-3.0-flash-sante-free" },
11854
11866
  "ling-3.0-flash-vl": { "vercel": "inclusionai/ling-3.0-flash-vl" },
11867
+ "ling-3.1-flash": { "vercel": "inclusionai/ling-3.1-flash" },
11868
+ "ling-3.1-flash-free": { "vercel": "inclusionai/ling-3.1-flash-free" },
11855
11869
  "llama-3.1-70b": { "vercel": "meta/llama-3.1-70b" },
11856
11870
  "llama-3.1-8b": { "vercel": "meta/llama-3.1-8b" },
11857
11871
  "llama-3.3-70b": { "vercel": "meta/llama-3.3-70b" },
@@ -12265,7 +12279,7 @@ const CREATOR_TO_PROVIDER = {
12265
12279
  "unknown": "openai"
12266
12280
  };
12267
12281
  //#endregion
12268
- //#region ../shared/dist/chunk-CZWIfVCf.mjs
12282
+ //#region ../shared/dist/chunk-AzlYBwl7.mjs
12269
12283
  /**
12270
12284
  * The reserved model-facing name of the agent state write tool
12271
12285
  * (`packages/runtime/src/engine/state-write-tool.ts`). The model is told to call
@@ -12464,7 +12478,7 @@ const GEN_AI_ATTRIBUTES = {
12464
12478
  toolCallId: "gen_ai.tool.call.id"
12465
12479
  };
12466
12480
  //#endregion
12467
- //#region ../shared/dist/chunk-DEoSlEa_.mjs
12481
+ //#region ../shared/dist/chunk-B1SxgkWd.mjs
12468
12482
  /**
12469
12483
  * GenAI semconv content attributes, set by a customer's own instrumentation when it opts into content export.
12470
12484
  * Two first-party producers write them: @runtypelabs/runtime's OTLP tracer for exported agents (content on by default), and @runtypelabs/flue-otel (opt-in).
@@ -14828,6 +14842,11 @@ _enum([
14828
14842
  "pending"
14829
14843
  ]);
14830
14844
  /**
14845
+ * Pattern enforced by `SecretStoreService.validateSecretKey`:
14846
+ * `/^[A-Z][A-Z0-9_]{0,98}[A-Z0-9]$/`. Keep in sync if that validator changes.
14847
+ */
14848
+ const SECRET_KEY_PATTERN = /^[A-Z][A-Z0-9_]{0,98}[A-Z0-9]$/;
14849
+ /**
14831
14850
  * Operator allowlist for a single filter condition. No raw SQL passthrough —
14832
14851
  * the SQL compiler maps each operator to a parameterized fragment.
14833
14852
  */
@@ -15221,7 +15240,7 @@ const memoryConfigSchema = object({
15221
15240
  * With 'open', a prompt-injected agent can exfiltrate context to any host; no separate plan entitlement restricts it.
15222
15241
  * Revisit with a Schematic entitlement if/when sandbox egress becomes plan-differentiated.
15223
15242
  */
15224
- const sandboxNetworkAccessSchema = union([_enum([
15243
+ const sandboxNetworkAccessSchema$1 = union([_enum([
15225
15244
  "none",
15226
15245
  "essentials",
15227
15246
  "open"
@@ -15253,7 +15272,7 @@ const sandboxConfigSchema = object({
15253
15272
  "standard-3",
15254
15273
  "standard-4"
15255
15274
  ]).optional().describe("Only standard-4 is supported on the hosted Agent Sandbox path; other sizes cannot be enabled."),
15256
- networkAccess: sandboxNetworkAccessSchema.optional(),
15275
+ networkAccess: sandboxNetworkAccessSchema$1.optional(),
15257
15276
  ttl: _enum([
15258
15277
  "5m",
15259
15278
  "1h",
@@ -15428,6 +15447,288 @@ const agentRuntimeConfigSchema = object({
15428
15447
  durability: agentDurabilityConfigSchema.optional(),
15429
15448
  state: agentStateChannelSchema.optional()
15430
15449
  }).strict();
15450
+ const injectionModeSchema = _enum([
15451
+ "bearer_header",
15452
+ "api_key_header",
15453
+ "query_param",
15454
+ "basic_auth"
15455
+ ]);
15456
+ const credentialProxyConfigSchema = object({
15457
+ secretKey: string().min(1),
15458
+ hostnamePattern: string().min(1),
15459
+ pathPattern: string().default("/*"),
15460
+ injectionMode: injectionModeSchema,
15461
+ injectionConfig: record(string(), unknown()).default({}),
15462
+ envVarName: string().optional()
15463
+ });
15464
+ /**
15465
+ * Sandbox provider capability table, the single source of truth for which `transform-data` sandbox provider runs which language.
15466
+ * #6521 needed the same provider/language relation in four places; the first draft forked it into four unpinned hand-copies.
15467
+ * This module is a pure leaf (no imports, no I/O) so every layer can depend on it; the table MIRRORS `TransformSandboxRouter`, asserted entry-for-entry by `apps/api/tests/flow-eligibility-facts.test.ts`.
15468
+ * Since `transform-data` continues on error by default, anything this table cannot vouch for must route to the legacy engine rather than silently produce wrong data.
15469
+ */
15470
+ /**
15471
+ * The provider a `transform-data` step gets when `config.sandboxProvider` is
15472
+ * unset. Mirrors the default in `TransformSandboxRouter.selectExecutor` and in
15473
+ * the legacy `ApiContextStepExecutor.executeJavaScriptTransform`.
15474
+ */
15475
+ const DEFAULT_SANDBOX_PROVIDER = "cloudflare-worker";
15476
+ /**
15477
+ * Every routable provider mapped to the languages its executor declares.
15478
+ * PROTOTYPE-LESS ON PURPOSE: the provider string is attacker-controlled on unauthenticated validation routes with no enum check in front.
15479
+ * On a normal object literal, `SANDBOX_PROVIDER_LANGUAGES['constructor']` resolves truthy and the following `.includes` throws, surfacing as a 500.
15480
+ * `Object.create(null)` plus an `Object.hasOwn` gate in `lookupLanguages` both close it; kept together since this table is read from four packages.
15481
+ */
15482
+ const SANDBOX_PROVIDER_LANGUAGES = Object.assign(Object.create(null), {
15483
+ quickjs: ["javascript"],
15484
+ "cloudflare-worker": ["javascript"],
15485
+ "runtype-sandbox": [
15486
+ "javascript",
15487
+ "typescript",
15488
+ "python"
15489
+ ],
15490
+ "cloudflare-sandbox": [
15491
+ "javascript",
15492
+ "typescript",
15493
+ "python"
15494
+ ],
15495
+ daytona: [
15496
+ "javascript",
15497
+ "typescript",
15498
+ "python"
15499
+ ]
15500
+ });
15501
+ /** Normalize an unset / non-string provider to the documented default. */
15502
+ function resolveSandboxProvider(provider) {
15503
+ return typeof provider === "string" ? provider : DEFAULT_SANDBOX_PROVIDER;
15504
+ }
15505
+ /**
15506
+ * The one lookup every predicate below goes through. Returns `undefined` for
15507
+ * anything that is not an OWN entry, so an inherited key can never be mistaken
15508
+ * for a provider — see the note on {@link SANDBOX_PROVIDER_LANGUAGES}.
15509
+ */
15510
+ function lookupLanguages(provider) {
15511
+ const resolved = resolveSandboxProvider(provider);
15512
+ if (!Object.prototype.hasOwnProperty.call(SANDBOX_PROVIDER_LANGUAGES, resolved)) return void 0;
15513
+ const languages = SANDBOX_PROVIDER_LANGUAGES[resolved];
15514
+ return Array.isArray(languages) ? languages : void 0;
15515
+ }
15516
+ /** Providers whose executor runs JavaScript and nothing else — the Cloudflare Worker Loader and in-process QuickJS. */
15517
+ function isJsOnlySandboxProvider(provider) {
15518
+ const languages = lookupLanguages(provider);
15519
+ return languages !== void 0 && languages.length === 1 && languages[0] === "javascript";
15520
+ }
15521
+ /**
15522
+ * Can a `transform-data` step on this provider bind a code-mode tool pool? ONLY `cloudflare-worker`.
15523
+ * This is narrower than `isJsOnlySandboxProvider` and the gap is deliberate: the seam is the api's `SANDBOX.workerLoaderExecuteTransformWithTools` RPC, which exists for Worker Loader alone.
15524
+ * Legacy and the dashboard both gate on this same single provider.
15525
+ */
15526
+ function supportsCodeModeToolPool(provider) {
15527
+ return resolveSandboxProvider(provider) === "cloudflare-worker";
15528
+ }
15529
+ /**
15530
+ * Sandbox egress posture shared by `transform-data` steps and `custom` tools:
15531
+ * `'off'` (the default when omitted), `'on'`, or an explicit hostname allowlist.
15532
+ */
15533
+ const sandboxNetworkAccessSchema = union([
15534
+ literal("off"),
15535
+ literal("on"),
15536
+ object({ allowedHostnames: array(string()).min(1) })
15537
+ ]);
15538
+ /**
15539
+ * One managed secret a `custom` tool may send, and where. Code sees only the
15540
+ * phantom token as `secrets.<key>`; the sandbox egress proxy swaps in the real
15541
+ * credential for requests that match `hostnamePattern` and `pathPattern`.
15542
+ */
15543
+ const customToolSecretSchema = object({
15544
+ key: string().regex(SECRET_KEY_PATTERN, "key must be a managed secret name, e.g. GITHUB_TOKEN"),
15545
+ hostnamePattern: string().min(1),
15546
+ pathPattern: string().min(1).optional(),
15547
+ injectionMode: injectionModeSchema,
15548
+ injectionConfig: record(string(), unknown()).optional()
15549
+ });
15550
+ /** Every code {@link collectCustomToolEgressIssues} can emit, for validators that forward core codes. */
15551
+ const CUSTOM_TOOL_EGRESS_ISSUE_CODES = [
15552
+ "CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
15553
+ "CUSTOM_TOOL_EGRESS_PROVIDER_UNSUPPORTED",
15554
+ "CUSTOM_TOOL_SECRET_HOST_NOT_ALLOWED"
15555
+ ];
15556
+ /**
15557
+ * Shared-hosting suffixes where every subdomain belongs to a different tenant, so
15558
+ * `*.<suffix>` would send a credential to strangers. Curated from the Public Suffix
15559
+ * List's private section; extend it when a new host platform shows up.
15560
+ */
15561
+ const SHARED_HOSTING_SUFFIXES = /* @__PURE__ */ new Set([
15562
+ "github.io",
15563
+ "gitlab.io",
15564
+ "pages.dev",
15565
+ "workers.dev",
15566
+ "vercel.app",
15567
+ "netlify.app",
15568
+ "herokuapp.com",
15569
+ "azurewebsites.net",
15570
+ "cloudfront.net",
15571
+ "appspot.com",
15572
+ "firebaseapp.com",
15573
+ "web.app",
15574
+ "onrender.com",
15575
+ "fly.dev",
15576
+ "glitch.me",
15577
+ "repl.co",
15578
+ "replit.app",
15579
+ "deno.dev",
15580
+ "railway.app",
15581
+ "ngrok.io",
15582
+ "ngrok-free.app",
15583
+ "trycloudflare.com",
15584
+ "blogspot.com",
15585
+ "wordpress.com",
15586
+ "s3.amazonaws.com",
15587
+ "amplifyapp.com",
15588
+ "azurestaticapps.net",
15589
+ "surge.sh",
15590
+ "myshopify.com"
15591
+ ]);
15592
+ /** Second-level labels that registries sell under a two-letter country code (co.uk, com.au, ne.jp). */
15593
+ const COUNTRY_SECOND_LEVELS = /* @__PURE__ */ new Set([
15594
+ "ac",
15595
+ "co",
15596
+ "com",
15597
+ "edu",
15598
+ "gov",
15599
+ "go",
15600
+ "ne",
15601
+ "net",
15602
+ "or",
15603
+ "org",
15604
+ "ltd",
15605
+ "plc",
15606
+ "nom",
15607
+ "gob",
15608
+ "mil"
15609
+ ]);
15610
+ /** Is `domain` a public suffix (a registry or shared-hosting zone), not one owner's domain? */
15611
+ function isPublicSuffixDomain(domain) {
15612
+ const host = domain.toLowerCase().replace(/\.+$/, "");
15613
+ if (SHARED_HOSTING_SUFFIXES.has(host)) return true;
15614
+ const labels = host.split(".");
15615
+ if (labels.length === 1) return true;
15616
+ if (labels.length !== 2) return false;
15617
+ const [second = "", top = ""] = labels;
15618
+ return top.length === 2 && COUNTRY_SECOND_LEVELS.has(second);
15619
+ }
15620
+ /**
15621
+ * A secret's hostname pattern must name one public DNS host (`api.github.com`) or one
15622
+ * domain's subdomains (`*.github.com`): never a TLD, a public suffix (`*.co.uk`,
15623
+ * `*.github.io`), everything, an IP literal, `localhost`, or an `.internal` name.
15624
+ */
15625
+ function isConcreteSecretHostnamePattern(pattern) {
15626
+ const wildcard = pattern.startsWith("*.");
15627
+ const host = (wildcard ? pattern.slice(2) : pattern).toLowerCase();
15628
+ if (host.includes("*")) return false;
15629
+ const labels = host.split(".");
15630
+ if (labels.length < 2) return false;
15631
+ if (labels.some((label) => !/^[a-z0-9-]+$/.test(label))) return false;
15632
+ const topLevel = labels[labels.length - 1] ?? "";
15633
+ if (!/[a-z]/.test(topLevel)) return false;
15634
+ if (topLevel === "internal" || topLevel === "local" || topLevel === "localhost") return false;
15635
+ return !(wildcard && isPublicSuffixDomain(host));
15636
+ }
15637
+ /** Is every host `secretPattern` can match also allowed by the tool's network posture? */
15638
+ function networkAccessCoversHostnamePattern(networkAccess, secretPattern) {
15639
+ if (networkAccess === "on") return true;
15640
+ if (networkAccess === "off") return false;
15641
+ return networkAccess.allowedHostnames.some((allowed) => {
15642
+ if (allowed === secretPattern) return true;
15643
+ if (!allowed.startsWith("*.")) return false;
15644
+ const suffix = allowed.slice(1);
15645
+ return (secretPattern.startsWith("*.") ? secretPattern.slice(1) : secretPattern).endsWith(suffix);
15646
+ });
15647
+ }
15648
+ const PROVIDERS_WITHOUT_EGRESS_PROXY = /* @__PURE__ */ new Set(["quickjs"]);
15649
+ const PROVIDERS_WITHOUT_SECRET_PROXY = /* @__PURE__ */ new Set(["quickjs", "daytona"]);
15650
+ const EGRESS_PROVIDER_REMEDIATION = "Set sandboxProvider to \"cloudflare-worker\" (JavaScript) or \"runtype-sandbox\" (JavaScript, TypeScript, Python).";
15651
+ /**
15652
+ * Pure checks on a custom tool's `networkAccess` / `secrets` against its provider.
15653
+ * Returns every issue so validators can surface them together.
15654
+ */
15655
+ function collectCustomToolEgressIssues(config) {
15656
+ if (!isRecord$1(config)) return [];
15657
+ const issues = [];
15658
+ const provider = resolveSandboxProvider(config.sandboxProvider);
15659
+ const networkParse = config.networkAccess === void 0 ? void 0 : sandboxNetworkAccessSchema.safeParse(config.networkAccess);
15660
+ if (networkParse && !networkParse.success) issues.push({
15661
+ code: "CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
15662
+ message: `networkAccess must be 'off', 'on', or { allowedHostnames: [...] } with at least one hostname.`,
15663
+ path: "networkAccess"
15664
+ });
15665
+ const networkAccess = networkParse?.success === true ? networkParse.data : "off";
15666
+ if (networkAccess !== "off" && PROVIDERS_WITHOUT_EGRESS_PROXY.has(provider)) issues.push({
15667
+ code: "CUSTOM_TOOL_EGRESS_PROVIDER_UNSUPPORTED",
15668
+ message: `sandboxProvider "${provider}" has no fetch and no egress proxy, so networkAccess cannot be enabled. ${EGRESS_PROVIDER_REMEDIATION}`,
15669
+ path: "networkAccess"
15670
+ });
15671
+ if (typeof networkAccess === "object" && provider === "daytona") issues.push({
15672
+ code: "CUSTOM_TOOL_EGRESS_PROVIDER_UNSUPPORTED",
15673
+ message: `sandboxProvider "daytona" manages its own egress and cannot enforce an allowedHostnames list. ${EGRESS_PROVIDER_REMEDIATION}`,
15674
+ path: "networkAccess"
15675
+ });
15676
+ if (config.secrets === void 0) return issues;
15677
+ if (!Array.isArray(config.secrets)) {
15678
+ issues.push({
15679
+ code: "CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
15680
+ message: "secrets must be an array of { key, hostnamePattern, injectionMode } entries.",
15681
+ path: "secrets"
15682
+ });
15683
+ return issues;
15684
+ }
15685
+ if (config.secrets.length === 0) return issues;
15686
+ if (PROVIDERS_WITHOUT_SECRET_PROXY.has(provider)) issues.push({
15687
+ code: "CUSTOM_TOOL_EGRESS_PROVIDER_UNSUPPORTED",
15688
+ message: `sandboxProvider "${provider}" has no credential proxy, so managed secrets cannot be injected. ${EGRESS_PROVIDER_REMEDIATION}`,
15689
+ path: "secrets"
15690
+ });
15691
+ if (config.secrets.length > 10) issues.push({
15692
+ code: "CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
15693
+ message: `A custom tool can declare at most 10 secrets.`,
15694
+ path: "secrets"
15695
+ });
15696
+ const seenKeys = /* @__PURE__ */ new Set();
15697
+ config.secrets.forEach((entry, index) => {
15698
+ const path = `secrets[${index}]`;
15699
+ const parsed = customToolSecretSchema.safeParse(entry);
15700
+ if (!parsed.success) {
15701
+ const first = parsed.error.issues[0];
15702
+ issues.push({
15703
+ code: "CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
15704
+ message: `Invalid secret entry: ${first?.path.join(".") || "entry"} ${first?.message ?? "is invalid"}. Use the bare secret name as key (GITHUB_TOKEN, not {{secret:GITHUB_TOKEN}}).`,
15705
+ path
15706
+ });
15707
+ return;
15708
+ }
15709
+ const secret = parsed.data;
15710
+ if (seenKeys.has(secret.key)) issues.push({
15711
+ code: "CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
15712
+ message: `Secret "${secret.key}" is declared twice. Declare each secret once; it is injected for one hostname pattern.`,
15713
+ path: `${path}.key`
15714
+ });
15715
+ seenKeys.add(secret.key);
15716
+ if (!isConcreteSecretHostnamePattern(secret.hostnamePattern)) {
15717
+ issues.push({
15718
+ code: "CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
15719
+ message: `Secret "${secret.key}" hostnamePattern "${secret.hostnamePattern}" is not a single public host. Name the API host (api.github.com) or one domain's subdomains (*.github.com); public suffixes (*.co.uk, *.github.io), IP literals, localhost and internal names are refused.`,
15720
+ path: `${path}.hostnamePattern`
15721
+ });
15722
+ return;
15723
+ }
15724
+ if (!networkAccessCoversHostnamePattern(networkAccess, secret.hostnamePattern)) issues.push({
15725
+ code: "CUSTOM_TOOL_SECRET_HOST_NOT_ALLOWED",
15726
+ message: `Secret "${secret.key}" is sent to "${secret.hostnamePattern}", but networkAccess does not allow that host. Add it to networkAccess.allowedHostnames.`,
15727
+ path: `${path}.hostnamePattern`
15728
+ });
15729
+ });
15730
+ return issues;
15731
+ }
15431
15732
  /**
15432
15733
  * A single entry in a context-step fallback chain. Mirrors `ContextFallback`
15433
15734
  * (`retry` | `step` | `flow`) in `error-handling.ts`. Discriminated by `type`.
@@ -16220,16 +16521,6 @@ const updateRecordConfigSchema = object({
16220
16521
  defaultValue: any().optional()
16221
16522
  });
16222
16523
  /**
16223
- * Network-access policy for sandboxed transform-data execution (cloudflare-worker/runtype-sandbox providers).
16224
- * Mirrors the inline union on TransformDataStep.config.networkAccess: 'off' (default), 'on', or an explicit allowlist.
16225
- * Lives here rather than the api validator because it's transform-data-specific.
16226
- */
16227
- const transformDataNetworkAccessSchema = union([
16228
- literal("off"),
16229
- literal("on"),
16230
- object({ allowedHostnames: array(string()).min(1) })
16231
- ]);
16232
- /**
16233
16524
  * Canonical transform-data config, the strict field set only; superset of the prior validator and TransformDataStep.config.
16234
16525
  * errorHandling tightens to the canonical union (prod-verified safe: of 1654 steps, only 4 carry it, all valid).
16235
16526
  * inputMode/packageJson/persistSandbox/reuseSandboxId are recovered fields the validator silently stripped.
@@ -16256,7 +16547,7 @@ const transformDataConfigSchema = object({
16256
16547
  packageJson: string().optional(),
16257
16548
  persistSandbox: boolean().optional(),
16258
16549
  reuseSandboxId: string().optional(),
16259
- networkAccess: transformDataNetworkAccessSchema.optional(),
16550
+ networkAccess: sandboxNetworkAccessSchema.optional(),
16260
16551
  inference: boolean().optional(),
16261
16552
  errorHandling: contextErrorHandlingConfigSchema.optional(),
16262
16553
  defaultValue: any().optional(),
@@ -18302,71 +18593,6 @@ function sanitizeCodeToolIdentifier(name) {
18302
18593
  return safe;
18303
18594
  }
18304
18595
  /**
18305
- * Sandbox provider capability table, the single source of truth for which `transform-data` sandbox provider runs which language.
18306
- * #6521 needed the same provider/language relation in four places; the first draft forked it into four unpinned hand-copies.
18307
- * This module is a pure leaf (no imports, no I/O) so every layer can depend on it; the table MIRRORS `TransformSandboxRouter`, asserted entry-for-entry by `apps/api/tests/flow-eligibility-facts.test.ts`.
18308
- * Since `transform-data` continues on error by default, anything this table cannot vouch for must route to the legacy engine rather than silently produce wrong data.
18309
- */
18310
- /**
18311
- * The provider a `transform-data` step gets when `config.sandboxProvider` is
18312
- * unset. Mirrors the default in `TransformSandboxRouter.selectExecutor` and in
18313
- * the legacy `ApiContextStepExecutor.executeJavaScriptTransform`.
18314
- */
18315
- const DEFAULT_SANDBOX_PROVIDER = "cloudflare-worker";
18316
- /**
18317
- * Every routable provider mapped to the languages its executor declares.
18318
- * PROTOTYPE-LESS ON PURPOSE: the provider string is attacker-controlled on unauthenticated validation routes with no enum check in front.
18319
- * On a normal object literal, `SANDBOX_PROVIDER_LANGUAGES['constructor']` resolves truthy and the following `.includes` throws, surfacing as a 500.
18320
- * `Object.create(null)` plus an `Object.hasOwn` gate in `lookupLanguages` both close it; kept together since this table is read from four packages.
18321
- */
18322
- const SANDBOX_PROVIDER_LANGUAGES = Object.assign(Object.create(null), {
18323
- quickjs: ["javascript"],
18324
- "cloudflare-worker": ["javascript"],
18325
- "runtype-sandbox": [
18326
- "javascript",
18327
- "typescript",
18328
- "python"
18329
- ],
18330
- "cloudflare-sandbox": [
18331
- "javascript",
18332
- "typescript",
18333
- "python"
18334
- ],
18335
- daytona: [
18336
- "javascript",
18337
- "typescript",
18338
- "python"
18339
- ]
18340
- });
18341
- /** Normalize an unset / non-string provider to the documented default. */
18342
- function resolveSandboxProvider(provider) {
18343
- return typeof provider === "string" ? provider : DEFAULT_SANDBOX_PROVIDER;
18344
- }
18345
- /**
18346
- * The one lookup every predicate below goes through. Returns `undefined` for
18347
- * anything that is not an OWN entry, so an inherited key can never be mistaken
18348
- * for a provider — see the note on {@link SANDBOX_PROVIDER_LANGUAGES}.
18349
- */
18350
- function lookupLanguages(provider) {
18351
- const resolved = resolveSandboxProvider(provider);
18352
- if (!Object.prototype.hasOwnProperty.call(SANDBOX_PROVIDER_LANGUAGES, resolved)) return void 0;
18353
- const languages = SANDBOX_PROVIDER_LANGUAGES[resolved];
18354
- return Array.isArray(languages) ? languages : void 0;
18355
- }
18356
- /** Providers whose executor runs JavaScript and nothing else — the Cloudflare Worker Loader and in-process QuickJS. */
18357
- function isJsOnlySandboxProvider(provider) {
18358
- const languages = lookupLanguages(provider);
18359
- return languages !== void 0 && languages.length === 1 && languages[0] === "javascript";
18360
- }
18361
- /**
18362
- * Can a `transform-data` step on this provider bind a code-mode tool pool? ONLY `cloudflare-worker`.
18363
- * This is narrower than `isJsOnlySandboxProvider` and the gap is deliberate: the seam is the api's `SANDBOX.workerLoaderExecuteTransformWithTools` RPC, which exists for Worker Loader alone.
18364
- * Legacy and the dashboard both gate on this same single provider.
18365
- */
18366
- function supportsCodeModeToolPool(provider) {
18367
- return resolveSandboxProvider(provider) === "cloudflare-worker";
18368
- }
18369
- /**
18370
18596
  * Convert snake_case string to camelCase
18371
18597
  * @example snakeToCamel('flow_name') // 'flowName'
18372
18598
  * @example snakeToCamel('user_id') // 'userId'
@@ -20825,6 +21051,25 @@ function validateCodeModeConfig(value, stepTools, stepIndex, stepRef) {
20825
21051
  }
20826
21052
  return null;
20827
21053
  }
21054
+ /**
21055
+ * Pure half of the custom-tool egress rules (#8103): a runtime `custom` tool whose
21056
+ * `networkAccess` / `secrets` its provider cannot honor, or whose secret is sent to
21057
+ * a host its own networkAccess blocks. The api's validateRuntimeTools enforces the same rules at dispatch.
21058
+ */
21059
+ function validateCustomToolEgress(tools, stepIndex, stepRef, buckets) {
21060
+ if (!Array.isArray(tools.runtimeTools)) return;
21061
+ tools.runtimeTools.forEach((tool, toolIndex) => {
21062
+ if (!isRecord$1(tool) || tool.toolType !== "custom") return;
21063
+ const toolName = typeof tool.name === "string" ? tool.name : `#${toolIndex}`;
21064
+ for (const issue of collectCustomToolEgressIssues(tool.config)) addIssue("error", {
21065
+ code: issue.code,
21066
+ message: `Custom tool "${toolName}": ${issue.message}`,
21067
+ path: `flowSteps[${stepIndex}].config.tools.runtimeTools[${toolIndex}].config.${issue.path}`,
21068
+ step: stepRef,
21069
+ details: { toolName }
21070
+ }, buckets);
21071
+ });
21072
+ }
20828
21073
  function validateTransformDataCodeTools(toolsConfig, stepIndex, stepRef, buckets) {
20829
21074
  const runtimeTools = Array.isArray(toolsConfig.runtimeTools) ? toolsConfig.runtimeTools : [];
20830
21075
  const runtimeToolNames = [];
@@ -21008,6 +21253,7 @@ function collectFlowStructureIssues(flowData, deps, buckets) {
21008
21253
  step: stepRef
21009
21254
  }, buckets);
21010
21255
  }
21256
+ validateCustomToolEgress(step.config.tools, stepIndex, stepRef, buckets);
21011
21257
  if (step.type === "transform-data") validateTransformDataCodeTools(step.config.tools, stepIndex, stepRef, buckets);
21012
21258
  const subagentCfg = step.config.tools.subagentConfig;
21013
21259
  if (subagentCfg !== void 0) {
@@ -23204,7 +23450,8 @@ const FORWARDED_CORE_CODES = /* @__PURE__ */ new Set([
23204
23450
  TOOL_CALL_ERROR_HANDLING_IGNORED_CODE,
23205
23451
  EXECUTE_AGENT_TEMPLATED_REF_UNRUNNABLE_CODE,
23206
23452
  PROVIDER_TOOLS_MIXED_OWNERS_CODE,
23207
- FLOW_TOOL_OUTPUT_VARIABLE_RESERVED_CODE
23453
+ FLOW_TOOL_OUTPUT_VARIABLE_RESERVED_CODE,
23454
+ ...CUSTOM_TOOL_EGRESS_ISSUE_CODES
23208
23455
  ]);
23209
23456
  function validateFlowDefinition(flow, capIndex, context) {
23210
23457
  const result = emptyResult();
@@ -23408,6 +23655,10 @@ function memoryPointerIssues(memory, context, path) {
23408
23655
  * v2 nested-`config` shape. The v2 branch reads the equivalent fields from
23409
23656
  * `agent.config` and reports them under `.config.*` paths.
23410
23657
  */
23658
+ /** Same egress rules the pure flow validator's validateCustomToolEgress applies (#8103), for AGENT-level runtimeTools. */
23659
+ function pushCustomToolEgressIssues(config, configPath, result) {
23660
+ for (const issue of collectCustomToolEgressIssues(config)) result.errors.push(createIssue("error", issue.code, issue.message, `${configPath}.${issue.path}`));
23661
+ }
23411
23662
  function validateAgentDefinition(agent, capIndex) {
23412
23663
  const result = emptyResult();
23413
23664
  const base = `capabilities[${capIndex}].agent`;
@@ -23451,6 +23702,7 @@ function validateFlatAgent(agent, base, result) {
23451
23702
  const outputVariableIssue = flowToolOutputVariableIssue(tool.config?.outputVariable, `${base}.toolsConfig.runtimeTools[${i}].config.outputVariable`);
23452
23703
  if (outputVariableIssue) result.errors.push(outputVariableIssue);
23453
23704
  }
23705
+ if (tool.toolType === "custom") pushCustomToolEgressIssues(tool.config, `${base}.toolsConfig.runtimeTools[${i}].config`, result);
23454
23706
  }
23455
23707
  }
23456
23708
  function validateNestedAgent(agent, base, result) {
@@ -23476,6 +23728,7 @@ function validateNestedAgent(agent, base, result) {
23476
23728
  const outputVariableIssue = flowToolOutputVariableIssue(tool.config?.outputVariable, `${base}.config.tools.runtimeTools[${i}].config.outputVariable`);
23477
23729
  if (outputVariableIssue) result.errors.push(outputVariableIssue);
23478
23730
  }
23731
+ if (tool.toolType === "custom") pushCustomToolEgressIssues(tool.config, `${base}.config.tools.runtimeTools[${i}].config`, result);
23479
23732
  }
23480
23733
  }
23481
23734
  const KEBAB_CASE_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
@@ -24226,20 +24479,6 @@ function isNonAIProvider(id) {
24226
24479
  if (colonIndex > 0) return NON_AI_PROVIDER_IDS.has(normalized.slice(0, colonIndex));
24227
24480
  return false;
24228
24481
  }
24229
- const injectionModeSchema = _enum([
24230
- "bearer_header",
24231
- "api_key_header",
24232
- "query_param",
24233
- "basic_auth"
24234
- ]);
24235
- const credentialProxyConfigSchema = object({
24236
- secretKey: string().min(1),
24237
- hostnamePattern: string().min(1),
24238
- pathPattern: string().default("/*"),
24239
- injectionMode: injectionModeSchema,
24240
- injectionConfig: record(string(), unknown()).default({}),
24241
- envVarName: string().optional()
24242
- });
24243
24482
  const LocalInferenceSurfaceSchema = _enum([
24244
24483
  "playground",
24245
24484
  "flows",
@@ -24874,7 +25113,11 @@ object({
24874
25113
  externalAgent: object({
24875
25114
  contextId: string().optional(),
24876
25115
  taskId: string().optional()
24877
- }).optional()
25116
+ }).optional(),
25117
+ /** The published agent version the run executed; absent when no stored version ran. */
25118
+ agentVersionId: string().optional(),
25119
+ /** The release alias the version was resolved through (`live`, `staging`, ...), when one was. */
25120
+ alias: string().optional()
24878
25121
  });
24879
25122
  object({
24880
25123
  recordId: string().optional(),
@@ -36925,7 +37168,7 @@ function createSandboxInstructions(provider) {
36925
37168
  "{ \"code\": \"...\", \"parameters\": { ... }, \"language\": \"javascript|typescript|python\", \"timeoutMs\": 30000 }",
36926
37169
  "Runtype Sandbox rules:",
36927
37170
  "1. Choose one language: javascript, typescript, or python.",
36928
- "2. Each session is a full Linux container (Node 22, Python 3.12, pnpm, tsx, uv, git).",
37171
+ "2. Each session is a full Linux container (Node 24, Python 3.13 with numpy/pandas/matplotlib, pnpm, tsx, uv, git).",
36929
37172
  "3. Inputs are available as the `parameters` object (JS/TS) or `parameters` dict (Python).",
36930
37173
  "4. async/await, Promises, and long-running processes are fully supported.",
36931
37174
  "5. Write the final result to stdout (JSON string is preferred for structured output).",
@@ -54201,6 +54444,9 @@ function collectExportedExecutionInventory(definition) {
54201
54444
  walkTools(agent.config.tools);
54202
54445
  }
54203
54446
  if (isObject(agent.claudeManagedConfig)) addModel(agent.claudeManagedConfig.model);
54447
+ if (Array.isArray(agent.skills)) {
54448
+ for (const skill of agent.skills) if (isObject(skill)) walkRuntimeTools(skill.resolvedTools);
54449
+ }
54204
54450
  if (Array.isArray(agent.capabilities)) for (const capability of agent.capabilities) {
54205
54451
  if (!isObject(capability)) continue;
54206
54452
  if (isObject(capability.flow)) walkFlow(capability.flow);
@@ -54260,16 +54506,41 @@ function describeAgentExportFailure(err) {
54260
54506
  if ((err.data && typeof err.data === "object" && "code" in err.data ? err.data.code : void 0) === "BYOC_PLAN_REQUIRED") return ["Self-hosting (BYOC) requires an Enterprise plan. Contact support to enable it for your account.", "The API key also needs the RUNTIME:EXPORT scope."];
54261
54507
  return ["The API key needs the RUNTIME:EXPORT scope, and self-hosting (BYOC) requires an Enterprise plan."];
54262
54508
  }
54263
- if (err.statusCode === 404) return ["Agent not found, or not owned by this account."];
54509
+ if (err.statusCode === 404) {
54510
+ const code = err.data && typeof err.data === "object" && "code" in err.data ? err.data.code : void 0;
54511
+ if (typeof code === "string" && RELEASE_SELECTOR_NOT_FOUND_CODES.has(code)) return ["The requested --alias or --version-id does not exist on this agent, or the alias is archived or expired."];
54512
+ return ["Agent not found, or not owned by this account."];
54513
+ }
54264
54514
  return [];
54265
54515
  }
54516
+ const RELEASE_SELECTOR_NOT_FOUND_CODES = /* @__PURE__ */ new Set([
54517
+ "alias_not_found",
54518
+ "alias_archived",
54519
+ "alias_expired",
54520
+ "version_not_found"
54521
+ ]);
54522
+ /** The export-runtime query for a release selector; errors when both selectors are given. */
54523
+ function agentReleaseSelectorParams(options, agentCount) {
54524
+ if (options.alias !== void 0 && options.versionId !== void 0) return { error: "Pass either --alias or --version-id, not both." };
54525
+ if (options.versionId !== void 0 && agentCount !== 1) return { error: "--version-id names one agent version, so pass exactly one --agent with it." };
54526
+ return { params: {
54527
+ ...options.alias !== void 0 ? { alias: options.alias } : {},
54528
+ ...options.versionId !== void 0 ? { versionId: options.versionId } : {}
54529
+ } };
54530
+ }
54531
+ function describeExportedRelease(def) {
54532
+ const release = def.release;
54533
+ if (!release?.versionId) return "";
54534
+ const via = release.alias ? ` via ${release.alias}${release.aliasRevision !== void 0 ? ` r${release.aliasRevision}` : ""}` : "";
54535
+ return chalk.dim(` (${release.versionId}${via})`);
54536
+ }
54266
54537
  const deployCommand = new Command("deploy").description("Export an agent and scaffold a deployment (cloud-run, cloudflare, or vercel)").option("--agent <id>", "Agent ID to export (may be repeated)", (v, acc) => {
54267
54538
  acc.push(v);
54268
54539
  return acc;
54269
54540
  }, []).option("--flow <id>", "Removed (ADR 0024): export the agent that runs the flow instead", (v, acc) => {
54270
54541
  acc.push(v);
54271
54542
  return acc;
54272
- }, []).option("--output <dir>", "Output directory for the scaffold (default: ./runtype-deploy)", "./runtype-deploy").option("--name <name>", "Project name used in package.json (default: derived from output dir)").option("--target <target>", "Deployment target: cloud-run, cloudflare, or vercel (default: cloud-run)", "cloud-run").action(async (options) => {
54543
+ }, []).option("--output <dir>", "Output directory for the scaffold (default: ./runtype-deploy)", "./runtype-deploy").option("--name <name>", "Project name used in package.json (default: derived from output dir)").option("--target <target>", "Deployment target: cloud-run, cloudflare, or vercel (default: cloud-run)", "cloud-run").option("--alias <name>", "Export the version each agent's release alias points at (e.g. staging, live, pr-42)").option("--version-id <id>", "Export this published agent version (requires exactly one --agent)").action(async (options) => {
54273
54544
  const agentIds = options.agent;
54274
54545
  if (options.flow.length > 0) {
54275
54546
  for (const line of RETIRED_DEPLOY_FLOW_MESSAGE) console.error(chalk.yellow(line));
@@ -54279,6 +54550,11 @@ const deployCommand = new Command("deploy").description("Export an agent and sca
54279
54550
  console.error(chalk.red("Error: provide at least one --agent <id>"));
54280
54551
  process.exit(1);
54281
54552
  }
54553
+ const selector = agentReleaseSelectorParams(options, agentIds.length);
54554
+ if ("error" in selector) {
54555
+ console.error(chalk.red(`Error: ${selector.error}`));
54556
+ process.exit(1);
54557
+ }
54282
54558
  const target = options.target;
54283
54559
  if (target !== "cloud-run" && target !== "cloudflare" && target !== "vercel") {
54284
54560
  console.error(chalk.red(`Error: unknown --target "${target}". Must be one of: cloud-run, cloudflare, vercel`));
@@ -54294,13 +54570,13 @@ const deployCommand = new Command("deploy").description("Export an agent and sca
54294
54570
  for (const id of agentIds) {
54295
54571
  process.stdout.write(` Exporting agent ${chalk.green(id)}... `);
54296
54572
  try {
54297
- const def = await client.get(`/agents/${id}/export-runtime`);
54573
+ const def = await client.get(`/agents/${id}/export-runtime`, selector.params);
54298
54574
  agentDefs.push({
54299
54575
  id,
54300
54576
  name: String(def.name ?? id),
54301
54577
  def
54302
54578
  });
54303
- process.stdout.write(chalk.green("✓\n"));
54579
+ process.stdout.write(chalk.green("✓") + describeExportedRelease(def) + "\n");
54304
54580
  } catch (err) {
54305
54581
  const msg = err instanceof RuntypeApiError ? err.message : String(err);
54306
54582
  process.stdout.write(chalk.red(`✗\n`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runtypelabs/cli",
3
- "version": "2.42.3",
3
+ "version": "2.43.0",
4
4
  "description": "Command-line interface for Runtype AI platform",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,7 +11,7 @@
11
11
  "@modelcontextprotocol/client": "2.0.0",
12
12
  "@modelcontextprotocol/core": "2.0.0",
13
13
  "@runtypelabs/ink-components": "0.3.9",
14
- "@runtypelabs/sdk": "10.5.3",
14
+ "@runtypelabs/sdk": "10.7.0",
15
15
  "@runtypelabs/terminal-animations": "0.2.4",
16
16
  "add-mcp": "2.0.0",
17
17
  "chalk": "^5.3.0",
@@ -33,7 +33,7 @@
33
33
  "devDependencies": {
34
34
  "@modelcontextprotocol/server": "2.0.0",
35
35
  "@runtypelabs/persona": "4.24.0",
36
- "@runtypelabs/shared": "3.66.0",
36
+ "@runtypelabs/shared": "3.68.0",
37
37
  "@types/express": "^5.0.6",
38
38
  "@types/micromatch": "^4.0.9",
39
39
  "@types/node": "^25.3.3",