@runtypelabs/cli 2.24.1 → 2.25.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 +916 -222
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -3060,9 +3060,9 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
3060
3060
  $ZodStringFormat.init(inst, def);
3061
3061
  inst._zod.check = (payload) => {
3062
3062
  try {
3063
- const trimmed = payload.value.trim();
3063
+ const trimmed2 = payload.value.trim();
3064
3064
  if (!def.normalize && def.protocol?.source === httpProtocol.source) {
3065
- if (!/^https?:\/\//i.test(trimmed)) {
3065
+ if (!/^https?:\/\//i.test(trimmed2)) {
3066
3066
  payload.issues.push({
3067
3067
  code: "invalid_format",
3068
3068
  format: "url",
@@ -3074,7 +3074,7 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
3074
3074
  return;
3075
3075
  }
3076
3076
  }
3077
- const url2 = new URL(trimmed);
3077
+ const url2 = new URL(trimmed2);
3078
3078
  if (def.hostname) {
3079
3079
  def.hostname.lastIndex = 0;
3080
3080
  if (!def.hostname.test(url2.hostname)) {
@@ -3106,7 +3106,7 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
3106
3106
  if (def.normalize) {
3107
3107
  payload.value = url2.href;
3108
3108
  } else {
3109
- payload.value = trimmed;
3109
+ payload.value = trimmed2;
3110
3110
  }
3111
3111
  return;
3112
3112
  } catch (_2) {
@@ -15181,7 +15181,7 @@ var edgeContextPayloadSchema = external_exports.object({
15181
15181
  exp: external_exports.number().int().positive()
15182
15182
  });
15183
15183
 
15184
- // ../shared/dist/chunk-3FPSMWBQ.mjs
15184
+ // ../shared/dist/chunk-OOL23ALD.mjs
15185
15185
  function getNestedValue(obj, path19) {
15186
15186
  let normalizedPath = path19;
15187
15187
  normalizedPath = normalizedPath.replace(/^\$\.?/, "");
@@ -15257,8 +15257,8 @@ var RUNTIME_PREFIXES = {
15257
15257
  };
15258
15258
  var PREFIX_PATTERN = /^([A-Za-z][A-Za-z0-9_-]*):(.*)$/s;
15259
15259
  function classifyVariableReference(token) {
15260
- const trimmed = token.trim();
15261
- const prefixMatch = PREFIX_PATTERN.exec(trimmed);
15260
+ const trimmed2 = token.trim();
15261
+ const prefixMatch = PREFIX_PATTERN.exec(trimmed2);
15262
15262
  if (prefixMatch) {
15263
15263
  const prefix = `${prefixMatch[1]}:`;
15264
15264
  const rest = prefixMatch[2] ?? "";
@@ -15266,12 +15266,12 @@ function classifyVariableReference(token) {
15266
15266
  if (spec) {
15267
15267
  return { namespace: spec.namespace, baseName: rest.trim() };
15268
15268
  }
15269
- return { namespace: "unknown", raw: trimmed };
15269
+ return { namespace: "unknown", raw: trimmed2 };
15270
15270
  }
15271
- if (isSystemVariable(trimmed)) {
15272
- return { namespace: "system", baseName: trimmed };
15271
+ if (isSystemVariable(trimmed2)) {
15272
+ return { namespace: "system", baseName: trimmed2 };
15273
15273
  }
15274
- return { namespace: "plain", baseName: trimmed };
15274
+ return { namespace: "plain", baseName: trimmed2 };
15275
15275
  }
15276
15276
  function isSystemVariable(varName) {
15277
15277
  const rootSegment = varName.split(".")[0] || "";
@@ -15287,15 +15287,15 @@ function parseTemplateExpression(expr) {
15287
15287
  operands: tokens.rawOperands.map(parseOperand)
15288
15288
  };
15289
15289
  }
15290
- const trimmed = expr.trim();
15291
- const pipeIndex = trimmed.indexOf("|");
15290
+ const trimmed2 = expr.trim();
15291
+ const pipeIndex = trimmed2.indexOf("|");
15292
15292
  if (pipeIndex === -1) {
15293
- return { kind: "legacy", variable: trimmed };
15293
+ return { kind: "legacy", variable: trimmed2 };
15294
15294
  }
15295
15295
  return {
15296
15296
  kind: "legacy",
15297
- variable: trimmed.slice(0, pipeIndex).trim(),
15298
- defaultValue: trimmed.slice(pipeIndex + 1).trim()
15297
+ variable: trimmed2.slice(0, pipeIndex).trim(),
15298
+ defaultValue: trimmed2.slice(pipeIndex + 1).trim()
15299
15299
  };
15300
15300
  }
15301
15301
  function tokenizeFallbackExpression(expr) {
@@ -15566,7 +15566,7 @@ function resolveFallback(parsed, context, tracking) {
15566
15566
  }
15567
15567
  var templateEngine = new SimpleTemplateEngine();
15568
15568
 
15569
- // ../shared/dist/chunk-ONVX3P3J.mjs
15569
+ // ../shared/dist/chunk-CNO2UJFO.mjs
15570
15570
  var tokensSchema = external_exports.object({ input: external_exports.number(), output: external_exports.number() });
15571
15571
  var wireStopReasonSchema = external_exports.enum([
15572
15572
  "end_turn",
@@ -15641,7 +15641,13 @@ var unifiedExecutionErrorEventSchema = external_exports.object({
15641
15641
  error: unifiedErrorPayloadSchema,
15642
15642
  completedAt: external_exports.string().optional(),
15643
15643
  code: external_exports.string().optional(),
15644
- upgradeUrl: external_exports.string().optional()
15644
+ upgradeUrl: external_exports.string().optional(),
15645
+ // Structured discriminant for a platform-key spend block (credit_exhausted |
15646
+ // trial_expired | auto_topup_failed) so the dashboard paywall can render
15647
+ // differentiated copy. The `PlatformKeyBlockReason` union lives in
15648
+ // `@runtypelabs/model-execution`, which `shared` cannot import — typed as a
15649
+ // string to mirror the sibling `code` / `upgradeUrl` fields.
15650
+ blockReason: external_exports.string().optional()
15645
15651
  });
15646
15652
  var unifiedTurnStartEventSchema = external_exports.object({
15647
15653
  ...unifiedBase,
@@ -15867,7 +15873,11 @@ var unifiedToolCompleteEventSchema = external_exports.object({
15867
15873
  });
15868
15874
  var unifiedSubagentAttributionSchema = external_exports.object({
15869
15875
  toolName: external_exports.string(),
15870
- agentName: external_exports.string().optional()
15876
+ agentName: external_exports.string().optional(),
15877
+ // Parent-stream correlation id: the subagent tool bubble the client already
15878
+ // saw (the approval's own `toolCallId` is the muted child's never-streamed
15879
+ // tool_use id).
15880
+ parentToolCallId: external_exports.string().optional()
15871
15881
  });
15872
15882
  var unifiedApprovalStartEventSchema = external_exports.object({
15873
15883
  ...unifiedBase,
@@ -16034,7 +16044,8 @@ var externalAgentContextSchema = external_exports.object({
16034
16044
  });
16035
16045
  var subagentAttributionSchema = external_exports.object({
16036
16046
  toolName: external_exports.string(),
16037
- agentName: external_exports.string().optional()
16047
+ agentName: external_exports.string().optional(),
16048
+ parentToolCallId: external_exports.string().optional()
16038
16049
  });
16039
16050
  var tokensSchema2 = external_exports.object({ input: external_exports.number(), output: external_exports.number() });
16040
16051
  var wireStopReasonSchema2 = external_exports.enum([
@@ -16393,6 +16404,11 @@ var flowErrorEventSchema = external_exports.object({
16393
16404
  // Spread only on platform-key spend errors (api raw-write).
16394
16405
  code: external_exports.string().optional(),
16395
16406
  upgradeUrl: external_exports.string().optional(),
16407
+ // Structured discriminant for a platform-key spend block (credit_exhausted |
16408
+ // trial_expired | auto_topup_failed). Typed as a string here because the
16409
+ // `PlatformKeyBlockReason` union lives in `@runtypelabs/model-execution`,
16410
+ // which `shared` cannot import — mirrors the sibling `code` / `upgradeUrl`.
16411
+ blockReason: external_exports.string().optional(),
16396
16412
  timestamp: external_exports.string().optional(),
16397
16413
  flowId: external_exports.string().optional(),
16398
16414
  toolContext: toolContextSchema.optional()
@@ -16420,7 +16436,13 @@ var flowAwaitEventSchema = external_exports.object({
16420
16436
  // never a control signal.
16421
16437
  awaitReason: external_exports.string().optional(),
16422
16438
  crawlId: external_exports.string().optional(),
16423
- stepId: external_exports.string().optional()
16439
+ stepId: external_exports.string().optional(),
16440
+ // Approval pauses (`awaitReason: 'approval_required'`) carry the approval
16441
+ // handle + configured timeout (ms). The flow→agent bridges and the unified
16442
+ // translator key on `approvalId` to surface the pause as an approval event
16443
+ // instead of a local-tool await.
16444
+ approvalId: external_exports.string().optional(),
16445
+ timeout: external_exports.number().optional()
16424
16446
  });
16425
16447
  var stepStartEventSchema = external_exports.object({
16426
16448
  ...baseFlowEvent,
@@ -16711,6 +16733,7 @@ var AGENT_CONTENT_CONFIG_KEYS = {
16711
16733
  errorHandling: true,
16712
16734
  artifacts: true,
16713
16735
  loggingPolicy: true,
16736
+ piiRedaction: true,
16714
16737
  temporal: true,
16715
16738
  memory: true,
16716
16739
  sandbox: true,
@@ -17624,6 +17647,51 @@ var retrieveRecordConfigSchema = external_exports.object({
17624
17647
  includeMetadata: external_exports.boolean().optional(),
17625
17648
  streamOutput: external_exports.boolean().optional()
17626
17649
  });
17650
+ var LIST_RECORDS_MAX_LIMIT = 1e3;
17651
+ var hasNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
17652
+ var getRecordConfigSchema = external_exports.object({
17653
+ recordId: external_exports.string().optional(),
17654
+ recordType: external_exports.string().optional(),
17655
+ recordName: external_exports.string().optional(),
17656
+ recordFilter: recordFilterSchema.optional(),
17657
+ fieldsToInclude: external_exports.string().optional(),
17658
+ fieldsToExclude: external_exports.string().optional(),
17659
+ availableFields: external_exports.array(external_exports.string()).optional(),
17660
+ includeMetadata: external_exports.boolean().optional(),
17661
+ streamOutput: external_exports.boolean().optional(),
17662
+ outputVariable: external_exports.string().min(1, "Output variable is required")
17663
+ }).superRefine((config3, ctx) => {
17664
+ const hasFilterType = hasNonEmptyString(config3.recordFilter?.type);
17665
+ if (!hasNonEmptyString(config3.recordId) && !hasNonEmptyString(config3.recordType) && !hasNonEmptyString(config3.recordName) && !hasFilterType) {
17666
+ ctx.addIssue({
17667
+ code: "custom",
17668
+ path: ["recordId"],
17669
+ message: "get-record step requires at least one of 'recordId', 'recordType', 'recordName', or 'recordFilter'"
17670
+ });
17671
+ }
17672
+ });
17673
+ var listRecordsConfigSchema = external_exports.object({
17674
+ recordType: external_exports.string().optional(),
17675
+ recordName: external_exports.string().optional(),
17676
+ recordFilter: recordFilterSchema.optional(),
17677
+ limit: external_exports.number().int().min(1).max(LIST_RECORDS_MAX_LIMIT).optional(),
17678
+ onEmpty: external_exports.enum(["succeed", "fail"]).optional(),
17679
+ fieldsToInclude: external_exports.string().optional(),
17680
+ fieldsToExclude: external_exports.string().optional(),
17681
+ availableFields: external_exports.array(external_exports.string()).optional(),
17682
+ includeMetadata: external_exports.boolean().optional(),
17683
+ streamOutput: external_exports.boolean().optional(),
17684
+ outputVariable: external_exports.string().min(1, "Output variable is required")
17685
+ }).superRefine((config3, ctx) => {
17686
+ const hasFilterType = hasNonEmptyString(config3.recordFilter?.type);
17687
+ if (!hasNonEmptyString(config3.recordType) && !hasNonEmptyString(config3.recordName) && !hasFilterType) {
17688
+ ctx.addIssue({
17689
+ code: "custom",
17690
+ path: ["recordType"],
17691
+ message: "list-records step requires at least one of 'recordType', 'recordName', or 'recordFilter'"
17692
+ });
17693
+ }
17694
+ });
17627
17695
  var updateRecordConfigSchema = external_exports.object({
17628
17696
  recordId: external_exports.string().optional(),
17629
17697
  recordType: external_exports.string().optional(),
@@ -17852,6 +17920,8 @@ var sharedStepConfigSchemas = {
17852
17920
  "send-email": sendEmailConfigSchema,
17853
17921
  "paginate-api": paginateApiConfigSchema,
17854
17922
  "retrieve-record": retrieveRecordConfigSchema,
17923
+ "get-record": getRecordConfigSchema,
17924
+ "list-records": listRecordsConfigSchema,
17855
17925
  "update-record": updateRecordConfigSchema,
17856
17926
  "transform-data": transformDataConfigSchema,
17857
17927
  prompt: promptConfigSchema,
@@ -17879,6 +17949,8 @@ var CONTEXT_STEP_TYPES = [
17879
17949
  "crawl",
17880
17950
  "fetch-url",
17881
17951
  "retrieve-record",
17952
+ "get-record",
17953
+ "list-records",
17882
17954
  "fetch-github",
17883
17955
  "api-call",
17884
17956
  "transform-data",
@@ -17970,6 +18042,52 @@ function isCustomSendFromAddressOnManagedDomain(configuredFrom, preferredManaged
17970
18042
  preferredManagedDomain
17971
18043
  );
17972
18044
  }
18045
+ function isPlainObject9(value) {
18046
+ return typeof value === "object" && value !== null && !Array.isArray(value);
18047
+ }
18048
+ function trimmed(value) {
18049
+ return typeof value === "string" ? value.trim() : "";
18050
+ }
18051
+ function retrieveRecordProducesArray(config3) {
18052
+ const recordId = trimmed(config3.recordId);
18053
+ if (recordId) return false;
18054
+ if (config3.retrievalMode && config3.retrievalMode !== "query") return false;
18055
+ const recordType = trimmed(config3.recordType);
18056
+ const recordName = trimmed(config3.recordName);
18057
+ const filter = config3.recordFilter;
18058
+ const filterType = isPlainObject9(filter) ? trimmed(filter.type) : "";
18059
+ return !!(recordType || recordName || filterType);
18060
+ }
18061
+ function resolveRetrieveRecordAlias(config3) {
18062
+ const common = {
18063
+ recordType: config3.recordType,
18064
+ recordName: config3.recordName,
18065
+ recordFilter: config3.recordFilter,
18066
+ fieldsToInclude: config3.fieldsToInclude,
18067
+ fieldsToExclude: config3.fieldsToExclude,
18068
+ availableFields: config3.availableFields,
18069
+ includeMetadata: config3.includeMetadata,
18070
+ streamOutput: config3.streamOutput,
18071
+ outputVariable: config3.outputVariable
18072
+ };
18073
+ if (retrieveRecordProducesArray(config3)) {
18074
+ return {
18075
+ type: "list-records",
18076
+ config: {
18077
+ ...common,
18078
+ limit: LIST_RECORDS_MAX_LIMIT,
18079
+ onEmpty: "fail"
18080
+ }
18081
+ };
18082
+ }
18083
+ return {
18084
+ type: "get-record",
18085
+ config: {
18086
+ recordId: config3.recordId,
18087
+ ...common
18088
+ }
18089
+ };
18090
+ }
17973
18091
  var NormalizationError = class extends Error {
17974
18092
  constructor(message, stepType, fieldName, expectedType, actualValue) {
17975
18093
  super(message);
@@ -18172,6 +18290,8 @@ function normalizeContextStep(apiStep, options = {}) {
18172
18290
  "crawl",
18173
18291
  "fetch-url",
18174
18292
  "retrieve-record",
18293
+ "get-record",
18294
+ "list-records",
18175
18295
  "fetch-github",
18176
18296
  "api-call",
18177
18297
  "transform-data",
@@ -18228,11 +18348,26 @@ function normalizeContextStep(apiStep, options = {}) {
18228
18348
  type: "fetch-url",
18229
18349
  config: normalizeFetchUrlConfig(apiStep.config)
18230
18350
  };
18231
- case "retrieve-record":
18351
+ case "retrieve-record": {
18352
+ const legacyConfig = normalizeRetrieveRecordConfig(apiStep.config);
18353
+ const aliased = resolveRetrieveRecordAlias(legacyConfig);
18354
+ return {
18355
+ ...baseStep,
18356
+ type: aliased.type,
18357
+ config: aliased.config
18358
+ };
18359
+ }
18360
+ case "get-record":
18232
18361
  return {
18233
18362
  ...baseStep,
18234
- type: "retrieve-record",
18235
- config: normalizeRetrieveRecordConfig(apiStep.config)
18363
+ type: "get-record",
18364
+ config: normalizeGetRecordConfig(apiStep.config)
18365
+ };
18366
+ case "list-records":
18367
+ return {
18368
+ ...baseStep,
18369
+ type: "list-records",
18370
+ config: normalizeListRecordsConfig(apiStep.config)
18236
18371
  };
18237
18372
  case "fetch-github":
18238
18373
  return {
@@ -18632,6 +18767,84 @@ function normalizeRetrieveRecordConfig(config3) {
18632
18767
  outputVariable: config3.outputVariable
18633
18768
  };
18634
18769
  }
18770
+ function normalizeGetRecordConfig(config3) {
18771
+ validateConfig(config3, "get-record");
18772
+ const filter = config3.recordFilter;
18773
+ const hasFilter = filter && typeof filter === "object" && typeof filter.type === "string" && filter.type.length > 0;
18774
+ if (!config3.recordId && !config3.recordType && !config3.recordName && !hasFilter) {
18775
+ throw new NormalizationError(
18776
+ `get-record step requires at least one of 'recordId', 'recordType', 'recordName', or 'recordFilter'`,
18777
+ "get-record",
18778
+ "recordId/recordType/recordName/recordFilter",
18779
+ "at least one identification field",
18780
+ void 0
18781
+ );
18782
+ }
18783
+ validateString(config3.outputVariable, "outputVariable", "get-record", true);
18784
+ if (config3.fieldsToInclude)
18785
+ validateString(config3.fieldsToInclude, "fieldsToInclude", "get-record");
18786
+ if (config3.fieldsToExclude)
18787
+ validateString(config3.fieldsToExclude, "fieldsToExclude", "get-record");
18788
+ if (config3.availableFields)
18789
+ validateArray(config3.availableFields, "availableFields", "get-record");
18790
+ if (config3.includeMetadata !== void 0)
18791
+ validateBoolean(config3.includeMetadata, "includeMetadata", "get-record");
18792
+ if (config3.streamOutput !== void 0)
18793
+ validateBoolean(config3.streamOutput, "streamOutput", "get-record");
18794
+ return {
18795
+ recordId: config3.recordId,
18796
+ recordType: config3.recordType,
18797
+ recordName: config3.recordName,
18798
+ recordFilter: config3.recordFilter,
18799
+ fieldsToInclude: config3.fieldsToInclude,
18800
+ fieldsToExclude: config3.fieldsToExclude,
18801
+ availableFields: config3.availableFields,
18802
+ includeMetadata: config3.includeMetadata,
18803
+ streamOutput: config3.streamOutput,
18804
+ outputVariable: config3.outputVariable
18805
+ };
18806
+ }
18807
+ function normalizeListRecordsConfig(config3) {
18808
+ validateConfig(config3, "list-records");
18809
+ const filter = config3.recordFilter;
18810
+ const hasFilter = filter && typeof filter === "object" && typeof filter.type === "string" && filter.type.length > 0;
18811
+ if (!config3.recordType && !config3.recordName && !hasFilter) {
18812
+ throw new NormalizationError(
18813
+ `list-records step requires at least one of 'recordType', 'recordName', or 'recordFilter'`,
18814
+ "list-records",
18815
+ "recordType/recordName/recordFilter",
18816
+ "at least one query field",
18817
+ void 0
18818
+ );
18819
+ }
18820
+ validateString(config3.outputVariable, "outputVariable", "list-records", true);
18821
+ if (config3.limit !== void 0) validateNumber(config3.limit, "limit", "list-records");
18822
+ if (config3.onEmpty !== void 0)
18823
+ validateEnum(config3.onEmpty, ["succeed", "fail"], "onEmpty", "list-records");
18824
+ if (config3.fieldsToInclude)
18825
+ validateString(config3.fieldsToInclude, "fieldsToInclude", "list-records");
18826
+ if (config3.fieldsToExclude)
18827
+ validateString(config3.fieldsToExclude, "fieldsToExclude", "list-records");
18828
+ if (config3.availableFields)
18829
+ validateArray(config3.availableFields, "availableFields", "list-records");
18830
+ if (config3.includeMetadata !== void 0)
18831
+ validateBoolean(config3.includeMetadata, "includeMetadata", "list-records");
18832
+ if (config3.streamOutput !== void 0)
18833
+ validateBoolean(config3.streamOutput, "streamOutput", "list-records");
18834
+ return {
18835
+ recordType: config3.recordType,
18836
+ recordName: config3.recordName,
18837
+ recordFilter: config3.recordFilter,
18838
+ limit: normalizeOptionalNumber(config3.limit),
18839
+ onEmpty: config3.onEmpty,
18840
+ fieldsToInclude: config3.fieldsToInclude,
18841
+ fieldsToExclude: config3.fieldsToExclude,
18842
+ availableFields: config3.availableFields,
18843
+ includeMetadata: config3.includeMetadata,
18844
+ streamOutput: config3.streamOutput,
18845
+ outputVariable: config3.outputVariable
18846
+ };
18847
+ }
18635
18848
  function normalizeFetchGithubConfig(config3) {
18636
18849
  validateConfig(config3, "fetch-github");
18637
18850
  validateString(config3.repository, "repository", "fetch-github", true);
@@ -19862,6 +20075,7 @@ function sanitizeCodeToolIdentifier(name) {
19862
20075
  if (/^[0-9]/.test(safe)) safe = "_" + safe;
19863
20076
  return safe;
19864
20077
  }
20078
+ var MAX_DURABLE_POLL_ATTEMPTS = 1e3;
19865
20079
  var flowStepValidationSchema = external_exports.object({
19866
20080
  // Optional stable client-supplied identifier. Preserved across saves so that
19867
20081
  // `flow_step_results.stepId` references stay valid and dashboard UI state
@@ -19927,7 +20141,12 @@ var LEGACY_TOOL_KEY_SUGGESTIONS = {
19927
20141
  function isObjectRecord(value) {
19928
20142
  return typeof value === "object" && value !== null && !Array.isArray(value);
19929
20143
  }
19930
- var NETWORK_ISOLATED_SANDBOX_PROVIDERS = /* @__PURE__ */ new Set(["cloudflare-worker", "quickjs"]);
20144
+ var NETWORK_ISOLATED_SANDBOX_PROVIDERS = /* @__PURE__ */ new Set([
20145
+ "cloudflare-worker",
20146
+ "quickjs",
20147
+ "runtype-sandbox",
20148
+ "cloudflare-sandbox"
20149
+ ]);
19931
20150
  var MAX_CONDITIONAL_NESTING_DEPTH = 10;
19932
20151
  var FETCH_CALL_PATTERN = /\bfetch\s*\(/;
19933
20152
  function validateSandboxNetworkAccess(config3, stepIndex, stepRef, buckets) {
@@ -20535,6 +20754,134 @@ function validateConditionExpressions(flowSteps, buckets, conditionalStepsExceed
20535
20754
  walkStepConditionExpressions(step, `flowSteps[${stepIndex}]`, stepRef, buckets, 1);
20536
20755
  }
20537
20756
  }
20757
+ var TOOL_STRATEGY_REQUIRED_MULTISTEP_CODE = "TOOL_STRATEGY_REQUIRED_MULTISTEP";
20758
+ function isRequiredMultiStepFootgun(input) {
20759
+ if (input.toolCallStrategy !== "required") return false;
20760
+ if (typeof input.maxTurns === "number" && input.maxTurns > 1) return true;
20761
+ return input.maxToolCalls !== 1;
20762
+ }
20763
+ function toolStrategyRequiredMultiStepMessage() {
20764
+ return 'Tool call strategy "required" forces the model to call a tool on every step. Unless the turn is capped to a single tool call (maxToolCalls of 1), the model can never return a final text answer and the output comes back empty \u2014 the runtime default allows multiple tool calls, so leaving maxToolCalls unset (or above 1), or running a multi-turn loop, triggers this. Use "auto" (recommended) so the model decides when to call tools and can finish with text, or keep "required" only for a single forced tool call (maxToolCalls of 1, no loop).';
20765
+ }
20766
+ function validateToolCallStrategyMultiStep(flowSteps, buckets) {
20767
+ for (const [stepIndex, step] of flowSteps.entries()) {
20768
+ if (step.enabled === false) continue;
20769
+ if (step.type !== "prompt") continue;
20770
+ if (!isObjectRecord(step.config)) continue;
20771
+ const tools = step.config.tools;
20772
+ if (!isObjectRecord(tools)) continue;
20773
+ const toolCallStrategy = tools.toolCallStrategy ?? tools.tool_call_strategy;
20774
+ const maxToolCallsRaw = tools.maxToolCalls ?? tools.max_tool_calls;
20775
+ const maxToolCalls = typeof maxToolCallsRaw === "number" ? maxToolCallsRaw : void 0;
20776
+ if (isRequiredMultiStepFootgun({ toolCallStrategy, maxToolCalls })) {
20777
+ addIssue(
20778
+ "warning",
20779
+ {
20780
+ code: TOOL_STRATEGY_REQUIRED_MULTISTEP_CODE,
20781
+ message: toolStrategyRequiredMultiStepMessage(),
20782
+ path: `flowSteps[${stepIndex}].config.tools`,
20783
+ step: { index: stepIndex, name: step.name, type: step.type },
20784
+ details: { toolCallStrategy: "required", maxToolCalls }
20785
+ },
20786
+ buckets
20787
+ );
20788
+ }
20789
+ }
20790
+ }
20791
+ var WAIT_UNTIL_MAX_ATTEMPTS_CLAMPED_CODE = "WAIT_UNTIL_MAX_ATTEMPTS_CLAMPED";
20792
+ function validateWaitUntilPollBudget(flowSteps, buckets) {
20793
+ for (const [stepIndex, step] of flowSteps.entries()) {
20794
+ if (step.enabled === false) continue;
20795
+ if (step.type !== "wait-until") continue;
20796
+ if (!isObjectRecord(step.config)) continue;
20797
+ const poll = step.config.poll;
20798
+ if (!isObjectRecord(poll)) continue;
20799
+ const maxAttempts = poll.maxAttempts;
20800
+ if (typeof maxAttempts !== "number" || maxAttempts <= MAX_DURABLE_POLL_ATTEMPTS) continue;
20801
+ addIssue(
20802
+ "warning",
20803
+ {
20804
+ code: WAIT_UNTIL_MAX_ATTEMPTS_CLAMPED_CODE,
20805
+ message: `poll.maxAttempts (${maxAttempts}) exceeds the durable poll budget of ${MAX_DURABLE_POLL_ATTEMPTS} attempts; when this step runs as a durable Workflow poll the attempt count is clamped to ${MAX_DURABLE_POLL_ATTEMPTS}. To cover the same wall-clock window, increase poll.intervalMs instead of adding attempts.`,
20806
+ path: `flowSteps[${stepIndex}].config.poll.maxAttempts`,
20807
+ step: { index: stepIndex, name: step.name, type: step.type },
20808
+ details: { maxAttempts, maxDurablePollAttempts: MAX_DURABLE_POLL_ATTEMPTS }
20809
+ },
20810
+ buckets
20811
+ );
20812
+ }
20813
+ }
20814
+ var LIST_RECORDS_OBJECT_ACCESS_CODE = "LIST_RECORDS_OBJECT_ACCESS";
20815
+ var DEPRECATED_STEP_TYPE_CODE = "DEPRECATED_STEP_TYPE";
20816
+ function stepProducesRecordArray(type, config3) {
20817
+ if (type === "list-records") return true;
20818
+ if (type === "retrieve-record") {
20819
+ return resolveRetrieveRecordAlias(config3).type === "list-records";
20820
+ }
20821
+ return false;
20822
+ }
20823
+ function validateListRecordsArrayAccess(flowSteps, buckets) {
20824
+ const arrayOutputs = /* @__PURE__ */ new Map();
20825
+ for (const [stepIndex, step] of flowSteps.entries()) {
20826
+ if (step.enabled === false) continue;
20827
+ if (!isObjectRecord(step.config)) continue;
20828
+ const config3 = step.config;
20829
+ for (const { variable, fieldPath } of extractVariableReferences(config3)) {
20830
+ const classification = classifyVariableReference(variable);
20831
+ if (classification.namespace !== "plain" && classification.namespace !== "flow") continue;
20832
+ const segments = classification.baseName.split(".");
20833
+ const root = segments[0] || "";
20834
+ const origin = arrayOutputs.get(root);
20835
+ if (!origin) continue;
20836
+ if (segments.length < 2) continue;
20837
+ const field = segments[1] || "";
20838
+ if (/^\d+$/.test(field) || field === "length") continue;
20839
+ addIssue(
20840
+ "warning",
20841
+ {
20842
+ code: LIST_RECORDS_OBJECT_ACCESS_CODE,
20843
+ message: `Variable "{{${variable}}}" reads field "${field}" on "${root}", but step "${origin.name}" returns an ARRAY of records (even when a single record matches). Object-style access resolves to undefined at runtime. Index into the array first \u2014 e.g. "{{${root}.0.${field}}}" for the first record \u2014 or loop over "${root}". To get a single record object, use a get-record step.`,
20844
+ path: `flowSteps[${stepIndex}].config.${fieldPath}`,
20845
+ step: { index: stepIndex, name: step.name, type: step.type },
20846
+ details: {
20847
+ variable,
20848
+ rootVariable: root,
20849
+ field,
20850
+ originStepIndex: origin.index,
20851
+ originStepName: origin.name
20852
+ }
20853
+ },
20854
+ buckets
20855
+ );
20856
+ }
20857
+ const outputVar = getStepOutputVariable(step);
20858
+ if (outputVar) {
20859
+ if (stepProducesRecordArray(step.type, config3)) {
20860
+ arrayOutputs.set(outputVar, { index: stepIndex, name: step.name });
20861
+ } else {
20862
+ arrayOutputs.delete(outputVar);
20863
+ }
20864
+ }
20865
+ }
20866
+ }
20867
+ function validateDeprecatedStepTypes(flowSteps, buckets) {
20868
+ for (const [stepIndex, step] of flowSteps.entries()) {
20869
+ if (step.type !== "retrieve-record") continue;
20870
+ const config3 = isObjectRecord(step.config) ? step.config : {};
20871
+ const resolvedType = resolveRetrieveRecordAlias(config3).type;
20872
+ addIssue(
20873
+ "warning",
20874
+ {
20875
+ code: DEPRECATED_STEP_TYPE_CODE,
20876
+ message: `The "retrieve-record" step is deprecated. Use "get-record" (returns a single record object) or "list-records" (returns an array of records) instead \u2014 this config resolves to "${resolvedType}".`,
20877
+ path: `flowSteps[${stepIndex}].type`,
20878
+ step: { index: stepIndex, name: step.name, type: step.type },
20879
+ details: { deprecatedType: "retrieve-record", resolvedType }
20880
+ },
20881
+ buckets
20882
+ );
20883
+ }
20884
+ }
20538
20885
  function collectAccountScopedReferences(step, stepIndex, pendingChecks) {
20539
20886
  const stepRef = {
20540
20887
  index: stepIndex,
@@ -21118,6 +21465,10 @@ function collectFlowStructureIssues(flowData, deps, buckets) {
21118
21465
  validateUpsertRecordSourceShape(flowData.flowSteps, buckets);
21119
21466
  validateStoreVectorSource(flowData.flowSteps, buckets, deps.declaredFlowInputs);
21120
21467
  validateConditionExpressions(flowData.flowSteps, buckets, conditionalStepsExceedingDepth);
21468
+ validateToolCallStrategyMultiStep(flowData.flowSteps, buckets);
21469
+ validateListRecordsArrayAccess(flowData.flowSteps, buckets);
21470
+ validateWaitUntilPollBudget(flowData.flowSteps, buckets);
21471
+ validateDeprecatedStepTypes(flowData.flowSteps, buckets);
21121
21472
  return { pendingChecks };
21122
21473
  }
21123
21474
  function validateFlowStructure(flowData, deps = {}) {
@@ -37609,7 +37960,11 @@ var BUILT_IN_GRADER_IDS = [
37609
37960
  "followsInstructions",
37610
37961
  "grounded",
37611
37962
  "rightTone",
37612
- "safeToSend"
37963
+ "safeToSend",
37964
+ "staysOnTask",
37965
+ "sameLanguage",
37966
+ "refusesWhenItShould",
37967
+ "noPersonalData"
37613
37968
  ];
37614
37969
  var graderSeveritySchema = external_exports.enum(["gate", "soft"]);
37615
37970
  var severityFields = { severity: graderSeveritySchema.optional() };
@@ -37715,39 +38070,137 @@ var caseExpectedSchema = external_exports.object({
37715
38070
  });
37716
38071
  var DEFAULT_MODELS_FOR_NEW_ACCOUNTS = [
37717
38072
  // Mixlayer models
37718
- { provider: "runtype", modelId: "qwen/qwen3.6-27b", isDefault: false },
37719
- { provider: "runtype", modelId: "qwen3.6-plus", isDefault: false },
37720
- // General Compute (platform key) - explicit provider prefix, single-provider model
37721
- { provider: "runtype", modelId: "general-compute/minimax-m2.7", isDefault: false },
38073
+ {
38074
+ provider: "runtype",
38075
+ modelId: "qwen/qwen3.6-27b",
38076
+ isDefault: false,
38077
+ strengths: "Fast, low-cost open-weight workhorse with reasoning, tool-use, and vision \u2014 good for high-volume extraction, classification, and simple transforms.",
38078
+ bestFor: ["fast-cheap", "vision"]
38079
+ },
37722
38080
  // Routed models - benefit from automatic provider optimization
37723
38081
  // OpenAI models (routed via OpenAI direct, with Vercel fallback)
37724
- { provider: "runtype", modelId: "gpt-5.5", isDefault: false },
37725
- { provider: "runtype", modelId: "gpt-5.4-mini", isDefault: false },
37726
- { provider: "runtype", modelId: "gpt-5.4-nano", isDefault: false },
38082
+ {
38083
+ provider: "runtype",
38084
+ modelId: "gpt-5.5",
38085
+ isDefault: false,
38086
+ strengths: "Top-tier general reasoning and tool-use with excellent structured-output reliability, vision, and a 1M context \u2014 a strong pick for complex, correctness-sensitive steps.",
38087
+ bestFor: ["default", "deep-reasoning", "agentic-tools", "vision", "long-context"]
38088
+ },
38089
+ {
38090
+ provider: "runtype",
38091
+ modelId: "gpt-5.4-mini",
38092
+ isDefault: false,
38093
+ strengths: "Fast and affordable with dependable JSON formatting \u2014 a good fit for mid-complexity task steps.",
38094
+ bestFor: ["fast-cheap", "default"]
38095
+ },
38096
+ {
38097
+ provider: "runtype",
38098
+ modelId: "gpt-5.4-nano",
38099
+ isDefault: false,
38100
+ strengths: "Cheapest and fastest GPT-5 tier \u2014 reserve for trivial deterministic transforms, routing, and classification, not complex authoring.",
38101
+ bestFor: ["fast-cheap"]
38102
+ },
37727
38103
  // Anthropic models (routed via Anthropic direct, with Vertex/Bedrock fallback)
37728
- { provider: "runtype", modelId: "claude-haiku-4-5", isDefault: false },
37729
- { provider: "runtype", modelId: "claude-sonnet-4-6", isDefault: false },
37730
- { provider: "runtype", modelId: "claude-opus-4-8", isDefault: false },
38104
+ {
38105
+ provider: "runtype",
38106
+ modelId: "claude-haiku-4-5",
38107
+ isDefault: false,
38108
+ strengths: "Fast and inexpensive while retaining real reasoning, tool-use, and vision \u2014 great for high-volume task steps that still need some judgment.",
38109
+ bestFor: ["fast-cheap", "agentic-tools", "vision"]
38110
+ },
38111
+ {
38112
+ provider: "runtype",
38113
+ modelId: "claude-sonnet-5",
38114
+ isDefault: false,
38115
+ strengths: "Balanced all-rounder: strong reasoning, very reliable multi-round tool loops, vision, and a 1M context \u2014 an excellent choice for agentic and structured work.",
38116
+ bestFor: ["default", "agentic-tools", "vision", "long-context"]
38117
+ },
38118
+ {
38119
+ provider: "runtype",
38120
+ modelId: "claude-opus-4-8",
38121
+ isDefault: false,
38122
+ strengths: "Deepest reasoning and planning for hard analysis and multi-constraint synthesis, with vision and a 1M context \u2014 premium-priced, so reserve for genuinely difficult steps.",
38123
+ bestFor: ["deep-reasoning", "vision", "long-context"]
38124
+ },
37731
38125
  // Google models (routed via Google direct, with Vertex/Vercel fallback)
37732
- { provider: "runtype", modelId: "gemini-3-1-flash-lite", isDefault: false },
37733
- { provider: "runtype", modelId: "gemini-3.5-flash", isDefault: false },
37734
- { provider: "runtype", modelId: "gemini-3-1-flash-image", isDefault: false },
37735
- { provider: "runtype", modelId: "gemini-3.1-pro", isDefault: false },
38126
+ {
38127
+ provider: "runtype",
38128
+ modelId: "gemini-3-1-flash-lite",
38129
+ isDefault: false,
38130
+ strengths: "Very fast and very cheap with full multimodal input (vision and audio), good structured output, and a 1M context \u2014 ideal for high-volume, simple steps.",
38131
+ bestFor: ["fast-cheap", "vision", "long-context"]
38132
+ },
38133
+ {
38134
+ provider: "runtype",
38135
+ modelId: "gemini-3.5-flash",
38136
+ isDefault: false,
38137
+ strengths: "Fast, low-cost, and natively multimodal with good reasoning, tool-use, and a 1M context \u2014 a strong choice for vision and high-throughput steps.",
38138
+ bestFor: ["default", "vision", "fast-cheap", "agentic-tools", "long-context"]
38139
+ },
38140
+ {
38141
+ provider: "runtype",
38142
+ modelId: "gemini-3-1-flash-image",
38143
+ isDefault: false,
38144
+ strengths: "Specialised for image generation and editing (image output) \u2014 use for steps that produce images, not text reasoning.",
38145
+ bestFor: ["image-generation"]
38146
+ },
38147
+ {
38148
+ provider: "runtype",
38149
+ modelId: "gemini-3.1-pro",
38150
+ isDefault: false,
38151
+ strengths: "Google's frontier tier: strong reasoning, a 1M context, and multimodal input \u2014 for complex analytical or long-document steps.",
38152
+ bestFor: ["deep-reasoning", "vision", "long-context"]
38153
+ },
37736
38154
  // Inception models (routed via Vercel)
37737
- { provider: "runtype", modelId: "mercury-2", isDefault: false },
38155
+ {
38156
+ provider: "runtype",
38157
+ modelId: "mercury-2",
38158
+ isDefault: false,
38159
+ strengths: "Diffusion-based LLM optimised for very low latency \u2014 good when speed matters more than deep reasoning.",
38160
+ bestFor: ["fast-cheap"]
38161
+ },
37738
38162
  // Moonshot models (routed via Vercel)
37739
38163
  // Kimi K2.6 is the default model: a reasoning + tool-use + vision model
37740
38164
  // already trusted as the product-generator default.
37741
- { provider: "runtype", modelId: "kimi-k2.6", isDefault: true },
38165
+ {
38166
+ provider: "runtype",
38167
+ modelId: "kimi-k2.6",
38168
+ isDefault: true,
38169
+ strengths: "Capable open agentic model with reasoning, tool-use, and vision at strong value \u2014 the current account default and a dependable general workhorse.",
38170
+ bestFor: ["default", "agentic-tools", "vision"]
38171
+ },
37742
38172
  // NVIDIA Nemotron Ultra (reasoning + tool-use flagship, routed via Vercel)
37743
- { provider: "runtype", modelId: "nemotron-3-ultra-550b-a55b", isDefault: false },
38173
+ {
38174
+ provider: "runtype",
38175
+ modelId: "nemotron-3-ultra-550b-a55b",
38176
+ isDefault: false,
38177
+ strengths: "Large open-weight reasoning and tool-use flagship with a 1M context at a low price \u2014 strong analytical depth where an open model is preferred.",
38178
+ bestFor: ["deep-reasoning", "agentic-tools", "long-context"]
38179
+ },
37744
38180
  // Google models (routed via Cloudflare Workers AI)
37745
- { provider: "runtype", modelId: "gemma-4-26b-a4b-it", isDefault: false },
38181
+ {
38182
+ provider: "runtype",
38183
+ modelId: "gemma-4-26b-a4b-it",
38184
+ isDefault: false,
38185
+ strengths: "Lightweight open-weight instruct model served free on Workers AI \u2014 fine for straightforward general tasks at zero cost.",
38186
+ bestFor: ["fast-cheap"]
38187
+ },
37746
38188
  // Z.ai GLM-5.2 (agentic coding flagship, routed via Cloudflare Workers AI,
37747
38189
  // Vercel fallback). The bare routed name resolves to `@cf/zai-org/glm-5.2`.
37748
- { provider: "runtype", modelId: "glm-5.2", isDefault: false }
38190
+ {
38191
+ provider: "runtype",
38192
+ modelId: "glm-5.2",
38193
+ isDefault: false,
38194
+ strengths: "Agentic flagship \u2014 best-in-class at long, multi-round tool-call loops and coding/agent workflows, with a 1M context; well suited to tool-heavy agent steps.",
38195
+ bestFor: ["agentic-tools", "default", "deep-reasoning", "long-context"]
38196
+ }
37749
38197
  ];
37750
38198
  var DEFAULT_MODEL_ID = DEFAULT_MODELS_FOR_NEW_ACCOUNTS.find((m2) => m2.isDefault)?.modelId ?? "kimi-k2.6";
38199
+ function renderDefaultModelStrengthsGuide() {
38200
+ return DEFAULT_MODELS_FOR_NEW_ACCOUNTS.map(
38201
+ (m2) => `- \`${m2.modelId}\`${m2.isDefault ? " (account default)" : ""} \u2014 ${m2.strengths}`
38202
+ ).join("\n");
38203
+ }
37751
38204
  var userProfileFeaturesSchema = external_exports.object({
37752
38205
  enableAgentSkills: external_exports.boolean(),
37753
38206
  // Gates the WebMCP-powered dashboard assistant (copilot panel + chat
@@ -37768,9 +38221,21 @@ var userProfileFeaturesSchema = external_exports.object({
37768
38221
  // `POST /v1/skills/scan` verdict UI). Driven by the `enable-skill-scanner`
37769
38222
  // boolean flag (fail-closed until a rule enables it). The scan ROUTE stays
37770
38223
  // reachable; this only hides the UI while the scanner rollout completes.
37771
- enableSkillScanner: external_exports.boolean()
38224
+ enableSkillScanner: external_exports.boolean(),
38225
+ // Gates the usage-based-pricing dashboard surfaces (card-centric paywall /
38226
+ // slow-mode copy, trial-expired alerts, CTAs to the credit-card entry page).
38227
+ // Driven by the `usage-based-pricing` boolean flag (fail-closed in
38228
+ // production until Lane 1 is live and a rule ramps a cohort). Off renders
38229
+ // today's UI byte-identically.
38230
+ usageBasedPricing: external_exports.boolean()
37772
38231
  });
37773
38232
  var MODEL_FAMILY_PROVIDER_IDS = {
38233
+ "arrow-1-1": {
38234
+ "vercel": "quiverai/arrow-1.1"
38235
+ },
38236
+ "arrow-1.1": {
38237
+ "vercel": "quiverai/arrow-1.1"
38238
+ },
37774
38239
  "claude-fable-5": {
37775
38240
  "anthropic": "claude-fable-5"
37776
38241
  },
@@ -37862,6 +38327,10 @@ var MODEL_FAMILY_PROVIDER_IDS = {
37862
38327
  "claude-sonnet-4.6": {
37863
38328
  "vercel": "anthropic/claude-sonnet-4.6"
37864
38329
  },
38330
+ "claude-sonnet-5": {
38331
+ "anthropic": "claude-sonnet-5",
38332
+ "vercel": "anthropic/claude-sonnet-5"
38333
+ },
37865
38334
  "codestral": {
37866
38335
  "vercel": "mistral/codestral"
37867
38336
  },
@@ -38046,6 +38515,9 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38046
38515
  "google": "gemini-3.1-flash-lite",
38047
38516
  "vercel": "google/gemini-3.1-flash-lite"
38048
38517
  },
38518
+ "gemini-3-1-flash-lite-image": {
38519
+ "vercel": "google/gemini-3.1-flash-lite-image"
38520
+ },
38049
38521
  "gemini-3-1-pro": {
38050
38522
  "google": "gemini-3.1-pro-preview",
38051
38523
  "vercel": "google/gemini-3.1-pro-preview"
@@ -38078,6 +38550,9 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38078
38550
  "google": "gemini-3.1-flash-lite",
38079
38551
  "vercel": "google/gemini-3.1-flash-lite"
38080
38552
  },
38553
+ "gemini-3.1-flash-lite-image": {
38554
+ "vercel": "google/gemini-3.1-flash-lite-image"
38555
+ },
38081
38556
  "gemini-3.1-pro-preview": {
38082
38557
  "google": "gemini-3.1-pro-preview",
38083
38558
  "vercel": "google/gemini-3.1-pro-preview"
@@ -38105,89 +38580,19 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38105
38580
  "togetherai": "togetherai/pearl-ai/gemma-4-31b-it",
38106
38581
  "vercel": "google/gemma-4-31b-it"
38107
38582
  },
38108
- "glm-4-5": {
38109
- "vercel": "zai/glm-4.5"
38110
- },
38111
- "glm-4-5-air": {
38112
- "vercel": "zai/glm-4.5-air"
38113
- },
38114
- "glm-4-5v": {
38115
- "vercel": "zai/glm-4.5v"
38116
- },
38117
- "glm-4-6": {
38118
- "vercel": "zai/glm-4.6"
38119
- },
38120
- "glm-4-6v": {
38121
- "vercel": "zai/glm-4.6v"
38122
- },
38123
- "glm-4-6v-flash": {
38124
- "vercel": "zai/glm-4.6v-flash"
38125
- },
38126
- "glm-4-7": {
38127
- "vercel": "zai/glm-4.7"
38128
- },
38129
- "glm-4-7-flash": {
38130
- "vercel": "zai/glm-4.7-flash"
38131
- },
38132
- "glm-4-7-flashx": {
38133
- "vercel": "zai/glm-4.7-flashx"
38134
- },
38135
- "glm-4.5": {
38136
- "vercel": "zai/glm-4.5"
38137
- },
38138
- "glm-4.5-air": {
38139
- "vercel": "zai/glm-4.5-air"
38140
- },
38141
- "glm-4.5v": {
38142
- "vercel": "zai/glm-4.5v"
38143
- },
38144
- "glm-4.6": {
38145
- "vercel": "zai/glm-4.6"
38146
- },
38147
- "glm-4.6v": {
38148
- "vercel": "zai/glm-4.6v"
38149
- },
38150
- "glm-4.6v-flash": {
38151
- "vercel": "zai/glm-4.6v-flash"
38152
- },
38153
- "glm-4.7": {
38154
- "vercel": "zai/glm-4.7"
38155
- },
38156
- "glm-4.7-flash": {
38157
- "vercel": "zai/glm-4.7-flash"
38158
- },
38159
- "glm-4.7-flashx": {
38160
- "vercel": "zai/glm-4.7-flashx"
38161
- },
38162
- "glm-5": {
38163
- "togetherai": "togetherai/zai-org/GLM-5",
38164
- "vercel": "zai/glm-5"
38165
- },
38166
- "glm-5-1": {
38167
- "togetherai": "togetherai/zai-org/GLM-5.1",
38168
- "vercel": "zai/glm-5.1"
38169
- },
38170
38583
  "glm-5-2": {
38584
+ "togetherai": "togetherai/zai-org/GLM-5.2",
38171
38585
  "vercel": "zai/glm-5.2"
38172
38586
  },
38173
38587
  "glm-5-2-fast": {
38174
38588
  "vercel": "zai/glm-5.2-fast"
38175
38589
  },
38176
- "glm-5-turbo": {
38177
- "vercel": "zai/glm-5-turbo"
38178
- },
38179
- "glm-5.1": {
38180
- "vercel": "zai/glm-5.1"
38181
- },
38182
38590
  "glm-5.2": {
38183
38591
  "vercel": "zai/glm-5.2"
38184
38592
  },
38185
38593
  "glm-5.2-fast": {
38186
38594
  "vercel": "zai/glm-5.2-fast"
38187
38595
  },
38188
- "glm-5v-turbo": {
38189
- "vercel": "zai/glm-5v-turbo"
38190
- },
38191
38596
  "google/gemma-4-31B-it": {
38192
38597
  "togetherai": "togetherai/google/gemma-4-31B-it"
38193
38598
  },
@@ -38519,6 +38924,9 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38519
38924
  "grok-imagine-video-1-5": {
38520
38925
  "vercel": "xai/grok-imagine-video-1.5-preview"
38521
38926
  },
38927
+ "grok-imagine-video-1.5": {
38928
+ "vercel": "xai/grok-imagine-video-1.5"
38929
+ },
38522
38930
  "grok-imagine-video-1.5-preview": {
38523
38931
  "vercel": "xai/grok-imagine-video-1.5-preview"
38524
38932
  },
@@ -38735,22 +39143,6 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38735
39143
  "mimo-v2.5-pro": {
38736
39144
  "vercel": "xiaomi/mimo-v2.5-pro"
38737
39145
  },
38738
- "minimax-m2": {
38739
- "vercel": "minimax/minimax-m2"
38740
- },
38741
- "minimax-m2-1": {
38742
- "vercel": "minimax/minimax-m2.1"
38743
- },
38744
- "minimax-m2-1-lightning": {
38745
- "vercel": "minimax/minimax-m2.1-lightning"
38746
- },
38747
- "minimax-m2-5": {
38748
- "togetherai": "togetherai/MiniMaxAI/MiniMax-M2.5",
38749
- "vercel": "minimax/minimax-m2.5"
38750
- },
38751
- "minimax-m2-5-highspeed": {
38752
- "vercel": "minimax/minimax-m2.5-highspeed"
38753
- },
38754
39146
  "minimax-m2-7": {
38755
39147
  "general-compute": "general-compute/minimax-m2.7",
38756
39148
  "togetherai": "togetherai/MiniMaxAI/MiniMax-M2.7",
@@ -38759,18 +39151,6 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38759
39151
  "minimax-m2-7-highspeed": {
38760
39152
  "vercel": "minimax/minimax-m2.7-highspeed"
38761
39153
  },
38762
- "minimax-m2.1": {
38763
- "vercel": "minimax/minimax-m2.1"
38764
- },
38765
- "minimax-m2.1-lightning": {
38766
- "vercel": "minimax/minimax-m2.1-lightning"
38767
- },
38768
- "minimax-m2.5": {
38769
- "vercel": "minimax/minimax-m2.5"
38770
- },
38771
- "minimax-m2.5-highspeed": {
38772
- "vercel": "minimax/minimax-m2.5-highspeed"
38773
- },
38774
39154
  "minimax-m2.7": {
38775
39155
  "general-compute": "general-compute/minimax-m2.7",
38776
39156
  "vercel": "minimax/minimax-m2.7"
@@ -38782,9 +39162,6 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38782
39162
  "togetherai": "togetherai/MiniMaxAI/MiniMax-M3",
38783
39163
  "vercel": "minimax/minimax-m3"
38784
39164
  },
38785
- "MiniMaxAI/MiniMax-M2.5": {
38786
- "togetherai": "togetherai/MiniMaxAI/MiniMax-M2.5"
38787
- },
38788
39165
  "MiniMaxAI/MiniMax-M2.7": {
38789
39166
  "togetherai": "togetherai/MiniMaxAI/MiniMax-M2.7"
38790
39167
  },
@@ -39372,11 +39749,8 @@ var MODEL_FAMILY_PROVIDER_IDS = {
39372
39749
  "whisper-1": {
39373
39750
  "vercel": "openai/whisper-1"
39374
39751
  },
39375
- "zai-org/GLM-5": {
39376
- "togetherai": "togetherai/zai-org/GLM-5"
39377
- },
39378
- "zai-org/GLM-5.1": {
39379
- "togetherai": "togetherai/zai-org/GLM-5.1"
39752
+ "zai-org/GLM-5.2": {
39753
+ "togetherai": "togetherai/zai-org/GLM-5.2"
39380
39754
  }
39381
39755
  };
39382
39756
  var PLATFORM_KEY_PROVIDER_MAP = {
@@ -40362,10 +40736,12 @@ var sandboxConfigSchema = external_exports.object({
40362
40736
  instanceType: external_exports.enum(["standard-1", "standard-2", "standard-3", "standard-4"]).optional(),
40363
40737
  networkAccess: sandboxNetworkAccessSchema.optional(),
40364
40738
  ttl: external_exports.enum(["5m", "1h", "24h", "unlimited"]).optional(),
40365
- sleepAfter: external_exports.enum(["5m", "15m", "30m", "1h"]).optional(),
40366
- // Opt-in approval gate on `bash`. Default false; wiring lands in Phase 3. The
40367
- // approval decision is on tool name + params only — never the agent's reason.
40368
- requireApprovalForBash: external_exports.boolean().optional()
40739
+ sleepAfter: external_exports.enum(["5m", "15m", "30m", "1h"]).optional()
40740
+ // NOTE: a `requireApprovalForBash` knob was forward-declared here for Phase 3
40741
+ // but never wired to any runtime gate, so it was REMOVED from the public
40742
+ // schema rather than ship a misleading, inert control (post-Fable handoff 05,
40743
+ // F3). To gate `bash` today use the generic `tools.approval` config, which
40744
+ // matches the tool by name. Re-add the dedicated knob only with its wiring.
40369
40745
  });
40370
40746
  var agentRuntimeConfigSchema = external_exports.object({
40371
40747
  model: external_exports.string().optional(),
@@ -40494,6 +40870,10 @@ var agentRuntimeConfigSchema = external_exports.object({
40494
40870
  }).optional(),
40495
40871
  errorHandling: errorHandlingSchema,
40496
40872
  loggingPolicy: external_exports.enum(["default", "on", "off"]).optional(),
40873
+ // Opt-in PII redaction for tail-log and telemetry payloads. `'default'`
40874
+ // defers to a lower layer (surface/product) or the resolver default (off).
40875
+ // Mirrors `loggingPolicy`.
40876
+ piiRedaction: external_exports.enum(["default", "off", "redact"]).optional(),
40497
40877
  // Opt-in temporal awareness. `injectElapsed` turns on the automatic
40498
40878
  // "time has passed" decorator (a `<temporal_context>` block prepended to
40499
40879
  // the user turn at dispatch, gated on turn ≥ 2 and a gap ≥
@@ -40519,6 +40899,142 @@ var agentRuntimeConfigSchema = external_exports.object({
40519
40899
  // `./tenancy-strategy`; the expansion + gate logic lives in apps/api.
40520
40900
  tenancyStrategy: tenancyStrategySchema.optional()
40521
40901
  });
40902
+ var identityAdapterKindSchema = external_exports.enum(["oidc-jwt", "session", "custom"]);
40903
+ var ASYMMETRIC_JWT_ALGORITHMS = [
40904
+ "RS256",
40905
+ "RS384",
40906
+ "RS512",
40907
+ "PS256",
40908
+ "PS384",
40909
+ "PS512",
40910
+ "ES256",
40911
+ "ES384",
40912
+ "ES512",
40913
+ "EdDSA"
40914
+ ];
40915
+ var asymmetricAlgorithmSchema = external_exports.enum(ASYMMETRIC_JWT_ALGORITHMS);
40916
+ var httpsUrlSchema = external_exports.string().url().refine((value) => value.startsWith("https://"), {
40917
+ message: "must be an https:// URL (plaintext http is not accepted)."
40918
+ });
40919
+ var claimMapSchema = external_exports.object({
40920
+ /** JSON path (dot notation) to the end-user id, e.g. `sub`. */
40921
+ subject: external_exports.string().min(1),
40922
+ /** JSON path to the tenant id, e.g. `org_id` / `o.id` / `organizationId`. Optional. */
40923
+ tenant: external_exports.string().min(1).optional(),
40924
+ /** JSON path to the end-user email. Optional. */
40925
+ email: external_exports.string().min(1).optional(),
40926
+ /** JSON path to a roles claim. Optional, display/authz context only. */
40927
+ roles: external_exports.string().min(1).optional(),
40928
+ /** JSON path to a permissions claim. Optional. */
40929
+ permissions: external_exports.string().min(1).optional()
40930
+ });
40931
+ var oboModeSchema = external_exports.enum([
40932
+ "token-exchange-8693",
40933
+ // Okta / Auth0 Custom Token Exchange (RFC 8693)
40934
+ "federated-token-vault",
40935
+ // Auth0 Token Vault, WorkOS Pipes, Clerk OAuth tokens
40936
+ "customer-endpoint"
40937
+ // Better Auth / custom escape hatch
40938
+ ]);
40939
+ var baseDescriptorShape = {
40940
+ /** Stable integration id (surface-scoped). Also the isolation namespace (C1). */
40941
+ id: external_exports.string().min(1),
40942
+ kind: identityAdapterKindSchema,
40943
+ claimMap: claimMapSchema,
40944
+ /**
40945
+ * The strongest assurance this integration may confer. Bounded ABOVE by the
40946
+ * kind ceiling ({@link assuranceCeilingForKind}) — may only lower it, never
40947
+ * raise it. A `custom`/`session` descriptor cannot declare `verified`.
40948
+ */
40949
+ maxAssurance: tenancyAssuranceLevelSchema.optional(),
40950
+ capabilities: external_exports.object({
40951
+ obo: external_exports.boolean().default(false),
40952
+ oboMode: oboModeSchema.optional()
40953
+ }).default({ obo: false })
40954
+ };
40955
+ var verifiableDescriptorShape = {
40956
+ /** Expected `iss`. REQUIRED, non-empty, non-wildcard. */
40957
+ issuer: httpsUrlSchema,
40958
+ /** JWKS endpoint for the issuer's public keys. REQUIRED. */
40959
+ jwksUri: httpsUrlSchema,
40960
+ /** Expected `aud`. REQUIRED, non-empty, non-wildcard (see refinement). */
40961
+ audience: external_exports.union([external_exports.string().min(1), external_exports.array(external_exports.string().min(1)).nonempty()]),
40962
+ /** Non-empty subset of {@link ASYMMETRIC_JWT_ALGORITHMS}. */
40963
+ allowedAlgorithms: external_exports.array(asymmetricAlgorithmSchema).nonempty(),
40964
+ clockSkewSec: external_exports.number().int().min(0).max(120).optional()
40965
+ };
40966
+ var identityAdapterDescriptorSchema = external_exports.discriminatedUnion("kind", [
40967
+ external_exports.object({
40968
+ ...baseDescriptorShape,
40969
+ ...verifiableDescriptorShape,
40970
+ kind: external_exports.literal("oidc-jwt"),
40971
+ /** `azp` allowlist (Clerk CSRF/origin guard, Auth0 authorized party). */
40972
+ authorizedParties: external_exports.array(external_exports.string().min(1)).optional(),
40973
+ /** Provider-specific post-verify policy toggles. */
40974
+ claimPolicy: external_exports.object({
40975
+ rejectImpersonation: external_exports.boolean().optional(),
40976
+ // Clerk `act`
40977
+ rejectPendingOrg: external_exports.boolean().optional(),
40978
+ // Clerk `sts:pending`
40979
+ rejectAudienceEqualsAzp: external_exports.boolean().optional(),
40980
+ // Auth0 ID-vs-access-token
40981
+ requireIssuerPathSegment: external_exports.string().min(1).optional()
40982
+ // Okta `/oauth2/{id}`
40983
+ }).optional()
40984
+ }),
40985
+ external_exports.object({
40986
+ ...baseDescriptorShape,
40987
+ ...verifiableDescriptorShape,
40988
+ kind: external_exports.literal("session")
40989
+ }),
40990
+ external_exports.object({
40991
+ ...baseDescriptorShape,
40992
+ kind: external_exports.literal("custom"),
40993
+ /** The customer verify endpoint (HTTPS). Egress is SSRF-guarded at call time. */
40994
+ verifyEndpoint: httpsUrlSchema
40995
+ })
40996
+ ]).superRefine((descriptor, ctx) => {
40997
+ if (descriptor.kind === "oidc-jwt" || descriptor.kind === "session") {
40998
+ const auds = Array.isArray(descriptor.audience) ? descriptor.audience : [descriptor.audience];
40999
+ if (auds.some((a) => a.trim() === "*" || a.trim() === "")) {
41000
+ ctx.addIssue({
41001
+ code: external_exports.ZodIssueCode.custom,
41002
+ path: ["audience"],
41003
+ message: "audience must name a specific relying party (no wildcard / empty value)."
41004
+ });
41005
+ }
41006
+ }
41007
+ if (descriptor.maxAssurance) {
41008
+ const ceiling = assuranceCeilingForKind(descriptor.kind);
41009
+ if (assuranceRank(descriptor.maxAssurance) === 0) {
41010
+ ctx.addIssue({
41011
+ code: external_exports.ZodIssueCode.custom,
41012
+ path: ["maxAssurance"],
41013
+ message: "maxAssurance cannot be 'anonymous' \u2014 it is a ceiling; omit the field or use 'asserted' / 'verified'."
41014
+ });
41015
+ } else if (assuranceRank(descriptor.maxAssurance) > assuranceRank(ceiling)) {
41016
+ ctx.addIssue({
41017
+ code: external_exports.ZodIssueCode.custom,
41018
+ path: ["maxAssurance"],
41019
+ message: `maxAssurance '${descriptor.maxAssurance}' exceeds the '${descriptor.kind}' kind ceiling '${ceiling}'.`
41020
+ });
41021
+ }
41022
+ }
41023
+ });
41024
+ function assuranceRank(level) {
41025
+ return level === "verified" ? 2 : level === "asserted" ? 1 : 0;
41026
+ }
41027
+ function assuranceCeilingForKind(kind) {
41028
+ return kind === "oidc-jwt" ? "verified" : "asserted";
41029
+ }
41030
+ var identityProofSchema = external_exports.object({
41031
+ /** Which configured integration the token claims to come from (hint only). */
41032
+ provider: external_exports.string().min(1).max(64),
41033
+ /** The customer-native proof (JWT / opaque session token). */
41034
+ token: external_exports.string().min(1).max(8192)
41035
+ }).describe(
41036
+ "Customer-native auth proof for the Identity Exchange. When present and the surface has a matching integration, the verified tenant/end-user replace any body-asserted tenant/endUser (which are never trusted for web-embedded callers). Ignored until the Identity Exchange admission upgrade ships."
41037
+ );
40522
41038
  function extractSecretReferences(template) {
40523
41039
  const keys = /* @__PURE__ */ new Set();
40524
41040
  let match;
@@ -42094,10 +42610,22 @@ var FLOW_STEP_TYPE_METADATA = {
42094
42610
  configHints: "http, auth, requestTemplate, responseMapping, outputVariable"
42095
42611
  },
42096
42612
  "retrieve-record": {
42097
- description: "Load records from the Runtype record store by ID, by type+name, or by a chip-style recordFilter (metadata + top-level columns: id, name, createdAt, updatedAt).",
42613
+ description: "DEPRECATED \u2014 use get-record (single object) or list-records (array) instead. Loads records from the Runtype record store; its return shape is inferred from the lookup method (an id lookup returns one object, a type/name/filter query returns an array even for a single match), which is the ambiguity the successor step types remove.",
42614
+ category: "data",
42615
+ isPrompt: false,
42616
+ configHints: "DEPRECATED. Prefer get-record / list-records. recordType, recordName, recordFilter ({ type, where: { field, op, value } }), outputVariable"
42617
+ },
42618
+ "get-record": {
42619
+ description: "Load a SINGLE record object from the Runtype record store by ID, by type+name, or by a chip-style recordFilter (first match, ordered by updated_at desc). Access fields directly in templates ({{var.field}}). Fails if no record matches.",
42098
42620
  category: "data",
42099
42621
  isPrompt: false,
42100
- configHints: "recordType, recordName, recordFilter ({ type, where: { field, op, value } }), outputVariable"
42622
+ configHints: "recordId, recordType, recordName, recordFilter ({ type, where: { field, op, value } }), outputVariable (returns one object \u2014 access as {{var.field}})"
42623
+ },
42624
+ "list-records": {
42625
+ description: "Load an ARRAY of records from the Runtype record store by type, name, or a chip-style recordFilter (metadata + top-level columns: id, name, createdAt, updatedAt), ordered by updated_at desc. Always returns an array \u2014 even for a single match \u2014 so index it in templates ({{var.0.field}}). Default limit 50 (max 1000).",
42626
+ category: "data",
42627
+ isPrompt: false,
42628
+ configHints: "recordType, recordName, recordFilter ({ type, where: { field, op, value } }), limit (default 50, max 1000), onEmpty (succeed|fail), outputVariable (returns an array \u2014 index as {{var.0.field}})"
42101
42629
  },
42102
42630
  "upsert-record": {
42103
42631
  description: "Create or update a record. The sourceVariable must point to a JSON object.",
@@ -42802,7 +43330,8 @@ ${lines.join("\n")}`;
42802
43330
  var PLATFORM_FEATURES_SUMMARY = (() => {
42803
43331
  const integrations = PLATFORM_CATALOG.availableIntegrations.map((i) => i.provider).join(", ");
42804
43332
  const stepTypes = FLOW_STEP_TYPES.filter((t) => t !== "prompt").join(", ");
42805
- const models = DEFAULT_MODELS_FOR_NEW_ACCOUNTS.map((m2) => m2.modelId).join(", ");
43333
+ const models = `use ONLY these routed IDs (never dated/legacy IDs like gpt-4.1 or gpt-4o), matched to the work:
43334
+ ${renderDefaultModelStrengthsGuide()}`;
42806
43335
  return `## Available Platform Features
42807
43336
 
42808
43337
  - **Built-in tools**: GPT Image 2 image generation, provider-native web search (OpenAI web search, Anthropic web search/fetch, xAI Live Search for Grok-3 series, xAI X Search / Web Search agentic tools for Grok-4 series), Exa (AI search), Firecrawl (simple web scraping), Browser Run tools (rendered markdown, HTML, screenshots, snapshots, selector scraping, JSON extraction, link harvesting \u2014 stateless), Browser Session tools (persistent interactive browsing: open, navigate, click, type, scroll, screenshot, evaluate, close), Browser Agent (autonomous higher-level alternative \u2014 give a natural-language goal plus a startUrl or sessionId and the agent navigates/clicks/extracts on its own), Store Asset (host any file from a URL or base64 inline content \u2014 returns a permanent public or signed private URL, downloads as attachment), Publish Page (host HTML that renders inline in the browser at a shareable 7-day /preview/ URL under a locked-down CSP), vector search, UCP commerce tools (discovery, catalog, cart, checkout, orders for any UCP-enabled merchant)
@@ -42812,6 +43341,113 @@ var PLATFORM_FEATURES_SUMMARY = (() => {
42812
43341
  - **Available models**: ${models}
42813
43342
  - **Surface types**: ${PLATFORM_CATALOG.availableSurfaceTypes.join(", ")}`;
42814
43343
  })();
43344
+ var FLOW_GENERATION_GUIDE = `## Flow Generation Guide
43345
+
43346
+ ### Step Types
43347
+
43348
+ - **prompt**: AI model call. Config: model, systemPrompt, userPrompt, responseFormat (text|json), outputVariable, tools
43349
+ - **transform-data**: Code execution in a sandboxed environment. **Use sparingly** \u2014 most agent-built flows over-use transform-data for work the platform already does natively. Before adding one, check the Transform-Data Anti-Patterns section: leading "Parse Input" steps duplicate the surface's \`parametersSchema\`; trailing "Final Output" reshapers add a dead hop; pre-cleaning data before a prompt step does the prompt's job in the wrong place; \`map().join()\` to format prompt input ignores that \`{{variable}}\` auto-stringifies; retrieving then plucking the first record ignores that \`recordFilter\` + \`{{records[0]}}\` work. Reach for transform-data only when the work is genuinely deterministic (record-name templating, JSON-shape coercion, math). Config: script, outputVariable, sandboxProvider (optional, default \`cloudflare-worker\`), language (required when sandboxProvider is \`runtype-sandbox\`). Sandbox providers: \`cloudflare-worker\` (default, JS only, strict mode ES module), \`runtype-sandbox\` (full container \u2014 supports \`javascript\`, \`typescript\`, and \`python\` via the \`language\` field), \`daytona\` (remote VM). For the default \`cloudflare-worker\` sandbox: always declare variables with const/let, access context variables via the \`input\` object (e.g., \`input.varName\`), use \`return { key: value }\` pattern. For \`runtype-sandbox\`: JS/TS use the same return pattern; Python uses \`print(json.dumps(...))\` to emit JSON output. Parameters are injected as a \`parameters\` object in all languages
43350
+ - **template**: Render a Liquid template into HTML, email-html, markdown, PDF, or text. **Prefer this over a prompt step when the output is a structured document** (invoice, receipt, email body, report) \u2014 keeps the LLM focused on producing data, not authoring HTML.
43351
+
43352
+ **Config fields:**
43353
+ - \`template\` (required): Liquid template body
43354
+ - \`outputFormat\` (required): \`html\` | \`email-html\` | \`markdown\` | \`pdf\` | \`text\`
43355
+ - \`outputVariable\` (required): variable name to store the rendered output
43356
+ - \`inputs\` (optional): map of logical name \u2192 expression resolving against flow context. If omitted, the entire flow context is available as template variables
43357
+ - \`partials\` (optional): named partial templates referenceable via \`{% include 'name' %}\`
43358
+ - \`pdfOptions\` (optional, pdf only): \`{ format?: 'A4' | 'Letter', landscape?: boolean, margin?: { top, bottom, left, right } }\`. PDF output returns \`{ contentType, bytes (base64), byteLength }\`
43359
+ - \`asArtifact\` (optional): emit a Persona artifact event for chat surfaces \u2014 renders inline in the conversation
43360
+ - \`streamOutput\` (optional): stream the rendered output via SSE
43361
+ - \`sampleData\` (optional): array of \`{ name, data }\` scenarios for the dashboard template editor preview
43362
+
43363
+ **Liquid syntax quick reference:**
43364
+ - Variables: \`{{ name }}\`, \`{{ object.field }}\`, \`{{ array[0] }}\`
43365
+ - Filters: \`{{ name | upcase }}\`, \`{{ price | round: 2 }}\`, \`{{ title | default: "Untitled" }}\`
43366
+ - Loops: \`{% for item in items %} ... {% endfor %}\`
43367
+ - Conditionals: \`{% if condition %} ... {% elsif other %} ... {% else %} ... {% endif %}\`
43368
+ - Partials: \`{% include 'partial_name' %}\` (references \`partials\` map keys)
43369
+ - Assignment: \`{% assign total = 0 %}\`, \`{% capture greeting %}Hello {{ name }}{% endcapture %}\`
43370
+ - Common filters: upcase, downcase, capitalize, strip, truncate, replace, split, join, size, round, date, default, escape, url_encode, newline_to_br
43371
+
43372
+ **Output format notes:**
43373
+ - \`html\` / \`email-html\`: Raw HTML string. \`email-html\` is identical today but semantically marks output destined for send-email steps
43374
+ - \`markdown\`: Plain markdown text
43375
+ - \`pdf\`: Renders the Liquid body as HTML, then converts to PDF. Result is \`{ contentType: "application/pdf", bytes: "<base64>", byteLength: N }\`
43376
+ - \`text\`: Plain text, no HTML processing
43377
+
43378
+ **Inputs map vs. full context:**
43379
+ - With \`inputs\`: only the declared names are available inside the template. Example: \`inputs: { "customer": "{{order_data}}" }\` exposes \`order_data\` as \`{{ customer }}\` in the template. Preferred for clarity and safety
43380
+ - Without \`inputs\`: the entire flow variable scope is available. Convenient for simple templates but risks name collisions
43381
+
43382
+ **Example \u2014 prompt produces JSON, template renders HTML:**
43383
+ \`\`\`
43384
+ Step 1 (prompt): model: "claude-sonnet-4-6", responseFormat: "json", outputVariable: "invoice"
43385
+ \u2192 AI generates { "company": "Acme", "items": [{...}], "total": "1,234.56" }
43386
+ Step 2 (template):
43387
+ template: "<h1>Invoice for {{ data.company }}</h1>{% for item in data.items %}<p>{{ item.desc }}: \${{ item.price }}</p>{% endfor %}<p>Total: \${{ data.total }}</p>"
43388
+ inputs: { "data": "{{invoice}}" }
43389
+ outputFormat: "html"
43390
+ outputVariable: "invoice_html"
43391
+ \`\`\`
43392
+ - **conditional**: Branch logic. Either the binary shape (condition (JS expression), trueSteps, falseSteps) or N-way named branches (branches: [{ id, name, condition, steps }], evaluated first-match-wins, with otherwiseSteps as the fallback). Set outputVariable to merge whichever branch ran into one named result.
43393
+ - **fetch-url**: HTTP request. Config: http (url, method, headers, body), responseType, outputVariable
43394
+ - **api-call**: Structured API call. Config: http, auth, requestTemplate, responseMapping, outputVariable
43395
+ - **get-record**: Load a SINGLE record OBJECT. Config: recordId, OR recordType + recordName, OR recordFilter ({ type, where: { field, op, value } }) for richer metadata-aware queries; outputVariable. Reachable filter columns include id, name, createdAt, updatedAt plus any metadata key. Returns one object \u2014 access fields directly in templates (\`{{record.field}}\`). On multiple matches the first by updated_at desc wins; fails if none match.
43396
+ - **list-records**: Load an ARRAY of records. Config: recordType, recordName, OR recordFilter ({ type, where: { field, op, value } }); limit (default 50, max 1000), onEmpty (succeed|fail, default succeed), outputVariable. Ordered by updated_at desc. Always writes an array \u2014 even for a single match \u2014 so index it in templates (\`{{records.0.field}}\` or \`{{records[0].field}}\`); object-style access (\`{{records.field}}\`) on the array resolves to undefined.
43397
+ - **retrieve-record**: DEPRECATED \u2014 use get-record (single object) or list-records (array). Still accepted at write and aliased at execution onto one of them; its return shape is inferred from the lookup method (recordId \u2192 object, type/name/filter \u2192 array), which is the ambiguity the successors remove.
43398
+ - **upsert-record**: Create/update a record keyed by \`(recordType, recordName)\` per owner. Config: recordType, **recordName** (REQUIRED for upsert semantics \u2014 without it each run creates a fresh \`\${recordType}-\${Date.now()}\` record), sourceVariable (the JSON metadata payload only \u2014 do NOT wrap as \`{ name, type, metadata }\`; the wrapper gets stored verbatim), outputVariable. See "Record-Keyed Upsert Patterns" in the build-product instructions for the full anti-pattern list (silent timestamped fallback, wrapped-source footgun, JSONB-blob aggregate races).
43399
+ - **update-record**: Update record fields. Config: recordId, OR recordFilter ({ type, where }) for first-match-wins by predicate; updates, mergeStrategy (merge|replace|deep-merge), outputVariable.
43400
+ - **set-variable**: Set a variable. Config: variableName, value
43401
+ - **send-email**: Send email. Config: from, to, subject, html, outputVariable
43402
+ - **send-text**: Send SMS. Config: from, to, message, outputVariable
43403
+ - **search**: Web/AI search. Config: provider, query, maxResults, outputVariable
43404
+ - **generate-embedding**: Create vector embedding. Config: inputSource, text, embeddingModel, outputVariable
43405
+ - **vector-search**: Search vectors. Config: query, limit, threshold, outputVariable
43406
+ - **crawl**: Multi-page web crawl via Cloudflare Browser Rendering \u2192 markdown. Config: url (required; **query string is DROPPED** \u2014 \`item?id=42\` crawls \`/item\`), outputVariable (required), depth (0 = render seed only, recommended), limit, completionTimeoutMs (default 120000; a slower crawl SILENTLY resumes with output **null** \u2014 raise to e.g. 600000 + set pollIntervalMs), asyncCrawl (false = inline polling for smoke tests), errorHandling ({ onError } OBJECT, not a bare string). Output: \`{ crawlId, status, result, resultPages[] }\` \u2014 flatten \`resultPages[].records[]\` (each \`{ url, status, markdown? }\`). Renders the seed fully but at depth \u2265 1 enumerates child links WITHOUT rendering them and follows only ABSOLUTE hrefs. For query-string URLs / single detail or comment pages use **fetch-url** or **builtin:firecrawl** (preserves query strings) instead.
43407
+ - **paginate-api**: Paginated API fetch. Config: url, paginationType, entityPath, outputVariable
43408
+ - **store-vector**: Store embeddings. Config: vectorsSource, destination, outputVariable
43409
+ - **execute-agent**: Run an existing agent. Config: agentId, message, outputVariable
43410
+ - **save-memory**: Persist a fact or observation to Cloudflare Agent Memory. Config: profileTemplate (e.g. \`{{_user.id}}\`), contentVariable, sessionId?, outputVariable?
43411
+ - **recall-memory**: Query long-term memory for a synthesized answer. Config: profileTemplate, queryTemplate, thinkingLevel? (low|medium|high), responseLength? (short|medium|long), outputVariable
43412
+ - **memory-summary**: Fetch a Markdown summary of what the agent has stored. Config: profileTemplate, sessionId?, outputVariable
43413
+
43414
+ > **Subagent tools** are not flow step types \u2014 they are runtime tools attached to prompt steps via \`tools.runtimeTools[]\` with \`toolType: 'subagent'\`. Use them when an agent should delegate focused sub-tasks with tool subsetting and context isolation. See the Subagent Delegation Guide.
43415
+
43416
+ > **Agent-level memory shortcut:** if the memory belongs to an agent (not a standalone flow), prefer the agent's \`memory: { enabled: true }\` config over threading the three steps through a flow. The agent gets the same three operations as tools (\`save_memory\`, \`recall_memory\`, \`memory_summary\`) and auto-ingests each exchange. Use the flow steps only when the memory operation must run deterministically at a fixed point in the flow.
43417
+
43418
+ ### Tool Usage Best Practice
43419
+
43420
+ Prefer registering tools on **prompt** or **execute-agent** steps via \`tools: { toolIds: [...] }\` rather than using standalone \`tool-call\` steps. When tools are registered on a prompt step, the AI model autonomously decides when and how to invoke them, producing more natural and adaptive flows.
43421
+
43422
+ Only use a \`tool-call\` step when the tool must run deterministically at a fixed point in the flow (e.g., always store an asset, always send a notification) regardless of model judgment.
43423
+
43424
+ > **Platform limits:** max 50 runtime tools per dispatch request, 30s tool timeout cap, and tool search auto-activates at 20+ tools (deferred tools become search-loaded). Fetch the \`limits\` documentation topic for the full list.
43425
+
43426
+ ### Variable System
43427
+
43428
+ - System variables (read-only): \`{{_flow.id}}\`, \`{{_flow.name}}\`, \`{{_user.id}}\`, \`{{_organization.id}}\`, \`{{_agent.id}}\`, \`{{_execution.id}}\`, \`{{_execution.conversationId}}\`
43429
+ - Temporal: \`{{_now.date}}\` (YYYY-MM-DD), \`{{_now.time}}\` (HH:mm:ss), \`{{_now.weekday}}\`, \`{{_now.iso}}\`, \`{{_now.year|month|day|unix|timezone}}\` \u2014 rendered in the schedule's tz on schedule-fired runs, UTC otherwise
43430
+ - End-user variables (populated when the caller passes \`endUser\` on dispatch \u2014 Persona widget, SDK \`executeAgent\`, or top-level \`endUser: { id, email?, ... }\` on the dispatch body): \`{{_endUser.id}}\`, \`{{_endUser.email}}\`, plus any additional fields the caller provides under \`_endUser.*\`. Use these for memory sharding in SaaS products where your customer's users are not Runtype account holders.
43431
+ - Product Tenant variables (populated when the caller passes \`tenant\` on dispatch or \`agents/:id/execute\` \u2014 top-level \`tenant: { id, ... }\` on the body): \`{{_tenant.id}}\`, plus any additional fields under \`_tenant.*\`. The Product Tenant is the builder's customer/account *inside* a Product (nested under the Builder Org \`_user\` and above the individual End User \`_endUser\`). Use \`{{_tenant.id}}\` to address account-level scope shared by all of a tenant's end-users (e.g. shard shared memory by \`{{_tenant.id}}\` while sharding per-user memory by \`{{_endUser.id}}\`).
43432
+ - Record variables: \`{{_record.id}}\`, \`{{_record.name}}\`, \`{{_record.type}}\`, \`{{_record.metadata.field}}\`
43433
+ - Schedule (schedule-fired runs only): \`{{_schedule.id}}\`, \`{{_schedule.name}}\`, \`{{_schedule.cron}}\`, \`{{_schedule.timezone}}\`, \`{{_schedule.lastRunAt}}\`, \`{{_schedule.nextRunAt}}\` \u2014 useful for incremental-window patterns: \`{{_schedule.lastRunAt || "24h ago"}}\`
43434
+ - Message variables: \`{{messages}}\`, \`{{userMessage}}\`, \`{{lastMessage}}\`, \`{{messageCount}}\`
43435
+ - Step outputs: Use the outputVariable name from previous steps, e.g., \`{{result}}\`, \`{{analysis}}\`
43436
+ - **One distinct \`outputVariable\` per step \u2014 never share one across \`when\`-guarded steps.** A \`when\`-skipped step does not no-op; the engine writes \`null\` to its \`outputVariable\`. With last-writer-wins, if several steps point at the SAME output name (e.g. five HTTP-method steps all writing \`response\`), the skipped ones null out the value the one that ran produced, and every downstream read sees \`null\`. Give each step its own name (\`response_get\`, \`response_post\`, \u2026) and coalesce them in a final step (\`{{response_get || response_post || ...}}\`, or a transform-data returning the first non-null).
43437
+ - Custom inputs: \`{{input_name}}\` \u2014 top-level variables passed via dispatch inputs
43438
+ - Defaults & fallbacks:
43439
+ - Legacy literal default: \`{{name|Anonymous}}\` \u2014 uses \`Anonymous\` if \`name\` is unset
43440
+ - Truthy fallback: \`{{primary || fallback}}\` \u2014 first truthy operand wins (skips empty strings, 0, false)
43441
+ - Nullish fallback: \`{{count ?? 0}}\` \u2014 keeps 0 / "" / false (only null/undefined fall through)
43442
+ - Chains: \`{{a || b || "default"}}\` \u2014 supports paths and string/number/boolean/null literals; do NOT mix \`||\` and \`??\` in one expression
43443
+
43444
+ ### Model Selection
43445
+
43446
+ Use ONLY routed model IDs from the list below (platform-key compatible). Never
43447
+ emit dated or legacy IDs (e.g. \`gpt-4.1\`, \`gpt-4o\`, \`claude-sonnet-4\`) \u2014 they
43448
+ are not enabled for new accounts. Match the model to the step's actual work:
43449
+
43450
+ ${renderDefaultModelStrengthsGuide()}`;
42815
43451
  var CATEGORY_DISPLAY_NAMES = {
42816
43452
  [BuiltInToolCategory.IMAGE_GENERATION]: "Image Generation",
42817
43453
  [BuiltInToolCategory.WEB_SEARCH]: "Web Search",
@@ -43628,6 +44264,22 @@ function selectAccountId(params) {
43628
44264
  if (isValidAccountId(params.userId)) return params.userId;
43629
44265
  return void 0;
43630
44266
  }
44267
+ function flattenTextContent(content) {
44268
+ if (typeof content === "string") return content;
44269
+ return content.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
44270
+ }
44271
+ function hasUsableClaudeManagedSeed(messages) {
44272
+ let lastUserIndex = -1;
44273
+ for (let i = messages.length - 1; i >= 0; i--) {
44274
+ if (messages[i]?.role === "user") {
44275
+ lastUserIndex = i;
44276
+ break;
44277
+ }
44278
+ }
44279
+ if (lastUserIndex === -1) return false;
44280
+ if (lastUserIndex > 0) return true;
44281
+ return flattenTextContent(messages[lastUserIndex].content).length > 0;
44282
+ }
43631
44283
  var RecordInputSchema = external_exports.object({
43632
44284
  id: external_exports.string().min(1).optional(),
43633
44285
  name: external_exports.string().min(1).optional(),
@@ -43888,6 +44540,12 @@ var DispatchRequestSchema = external_exports.object({
43888
44540
  tenant: external_exports.object({
43889
44541
  id: external_exports.string().min(1)
43890
44542
  }).passthrough().optional(),
44543
+ // Identity Exchange proof (design §4.1): a customer-native auth token the
44544
+ // broker will verify server-side and admit as `verified` tenant/end-user.
44545
+ // Wire contract only for now — parsed here, consumed by NOTHING until the
44546
+ // admission slice wires verification into the tenancy seam. The token is a
44547
+ // live credential: never log or persist it (see identityProofSchema docs).
44548
+ identityProof: identityProofSchema.optional(),
43891
44549
  options: external_exports.object({
43892
44550
  streamResponse: external_exports.boolean().optional().default(true),
43893
44551
  modelOverride: external_exports.string().optional(),
@@ -43923,6 +44581,15 @@ var DispatchRequestSchema = external_exports.object({
43923
44581
  })
43924
44582
  }).refine((data) => data.flow || data.agent, {
43925
44583
  message: "Either 'flow' or 'agent' configuration must be provided"
44584
+ }).superRefine((data, ctx) => {
44585
+ if (data.agent?.agentType !== "claude_managed") return;
44586
+ if (!hasUsableClaudeManagedSeed(data.messages ?? [])) {
44587
+ ctx.addIssue({
44588
+ code: "custom",
44589
+ path: ["messages"],
44590
+ message: "messages with a non-empty user message is required for Claude Managed agents"
44591
+ });
44592
+ }
43926
44593
  });
43927
44594
  var RunFlowOptionsSchema = external_exports.object({
43928
44595
  recordId: external_exports.string().optional(),
@@ -44984,11 +45651,11 @@ async function promptConfirm(message, options) {
44984
45651
  return new Promise((resolve12) => {
44985
45652
  rl.question(chalk3.cyan(`${message} (${hint}): `), (answer) => {
44986
45653
  rl.close();
44987
- const trimmed = answer.trim().toLowerCase();
44988
- if (trimmed === "") {
45654
+ const trimmed2 = answer.trim().toLowerCase();
45655
+ if (trimmed2 === "") {
44989
45656
  resolve12(defaultYes);
44990
45657
  } else {
44991
- resolve12(trimmed === "y" || trimmed === "yes");
45658
+ resolve12(trimmed2 === "y" || trimmed2 === "yes");
44992
45659
  }
44993
45660
  });
44994
45661
  });
@@ -47370,7 +48037,13 @@ secretsCommand.command("get <id>").description("Get secret metadata (value is ne
47370
48037
  const { waitUntilExit } = render6(React6.createElement(App));
47371
48038
  await waitUntilExit();
47372
48039
  });
47373
- secretsCommand.command("create").description("Create a new secret (provide the value via --value or stdin)").requiredOption("-k, --key <key>", "Secret key (alphanumeric/underscore, starts with a letter)").option("--value <value>", "Secret value (omit to read from stdin without echo)").option("-d, --description <text>", "Secret description").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(
48040
+ secretsCommand.command("create").description("Create a new secret (provide the value via --value or stdin)").requiredOption("-k, --key <key>", "Secret key (alphanumeric/underscore, starts with a letter)").option("--value <value>", "Secret value (omit to read from stdin without echo)").option("-d, --description <text>", "Secret description").option(
48041
+ "--product-tenant-id <id>",
48042
+ "Scope the secret to a Product Tenant (tnt_*). Omit for an org-level secret; a scoped secret requires a value."
48043
+ ).option(
48044
+ "--end-user-id <id>",
48045
+ "Scope the secret to an End User (eu_*); its enclosing tenant is derived server-side. Omit for an org-level secret; a scoped secret requires a value."
48046
+ ).option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(
47374
48047
  async (options) => {
47375
48048
  const apiKey = await ensureAuth();
47376
48049
  if (!apiKey) return;
@@ -47390,6 +48063,8 @@ secretsCommand.command("create").description("Create a new secret (provide the v
47390
48063
  description: options.description
47391
48064
  };
47392
48065
  if (value !== void 0) body.value = value;
48066
+ if (options.productTenantId !== void 0) body.productTenantId = options.productTenantId;
48067
+ if (options.endUserId !== void 0) body.endUserId = options.endUserId;
47393
48068
  if (!isTTY(options) || options.json) {
47394
48069
  try {
47395
48070
  const data = await client.post("/secrets", body);
@@ -50501,9 +51176,9 @@ ${delta}` : `${content}${delta}`;
50501
51176
  }
50502
51177
  function getVisibleReasoningLines(reasoning, maxLines) {
50503
51178
  if (maxLines <= 0) return [];
50504
- const trimmed = reasoning.trimEnd();
50505
- if (!trimmed) return [];
50506
- return trimmed.split("\n").slice(-maxLines);
51179
+ const trimmed2 = reasoning.trimEnd();
51180
+ if (!trimmed2) return [];
51181
+ return trimmed2.split("\n").slice(-maxLines);
50507
51182
  }
50508
51183
 
50509
51184
  // src/ink/marathon/useMarathonStream.ts
@@ -52906,12 +53581,12 @@ function parseSandboxTimeout(value, provider) {
52906
53581
  }
52907
53582
  function parseDaytonaExecutionResult(value) {
52908
53583
  if (typeof value !== "string") return value;
52909
- const trimmed = value.trim();
52910
- if (!trimmed) return "";
53584
+ const trimmed2 = value.trim();
53585
+ if (!trimmed2) return "";
52911
53586
  try {
52912
- return JSON.parse(trimmed);
53587
+ return JSON.parse(trimmed2);
52913
53588
  } catch {
52914
- const lines = trimmed.split("\n").map((line) => line.trim()).filter(Boolean);
53589
+ const lines = trimmed2.split("\n").map((line) => line.trim()).filter(Boolean);
52915
53590
  const lastLine = lines[lines.length - 1];
52916
53591
  if (!lastLine) return value;
52917
53592
  try {
@@ -55030,9 +55705,9 @@ function CheckpointPrompt({
55030
55705
  onSubmit
55031
55706
  ]);
55032
55707
  const handleSubmit = (text) => {
55033
- const trimmed = text.trim();
55034
- const modelArg = trimmed.startsWith("/model") ? trimmed.slice("/model".length).trim() : "";
55035
- if (trimmed.startsWith("/model")) {
55708
+ const trimmed2 = text.trim();
55709
+ const modelArg = trimmed2.startsWith("/model") ? trimmed2.slice("/model".length).trim() : "";
55710
+ if (trimmed2.startsWith("/model")) {
55036
55711
  clearInputDraft();
55037
55712
  if (modelArg) {
55038
55713
  setPendingModel(modelArg);
@@ -55041,53 +55716,53 @@ function CheckpointPrompt({
55041
55716
  }
55042
55717
  return;
55043
55718
  }
55044
- if (trimmed.startsWith("/reflect")) {
55719
+ if (trimmed2.startsWith("/reflect")) {
55045
55720
  setShowReflectEditor(true);
55046
55721
  clearInputDraft();
55047
55722
  return;
55048
55723
  }
55049
- if (trimmed === "/tree") {
55724
+ if (trimmed2 === "/tree") {
55050
55725
  setTreePanelRows(onLoadTreeView?.() ?? []);
55051
55726
  clearInputDraft();
55052
55727
  return;
55053
55728
  }
55054
- if (trimmed === "/fork") {
55729
+ if (trimmed2 === "/fork") {
55055
55730
  setForkCandidates(onLoadForkCandidates?.() ?? []);
55056
55731
  clearInputDraft();
55057
55732
  return;
55058
55733
  }
55059
- if (trimmed === "/copy") {
55734
+ if (trimmed2 === "/copy") {
55060
55735
  onCopySession?.();
55061
55736
  clearInputDraft();
55062
55737
  return;
55063
55738
  }
55064
- if (trimmed === "/copy-trimmed") {
55739
+ if (trimmed2 === "/copy-trimmed") {
55065
55740
  onCopySessionTrimmed?.();
55066
55741
  clearInputDraft();
55067
55742
  return;
55068
55743
  }
55069
- if (trimmed === "/open") {
55744
+ if (trimmed2 === "/open") {
55070
55745
  onOpenStateFile?.();
55071
55746
  clearInputDraft();
55072
55747
  return;
55073
55748
  }
55074
- if (trimmed.startsWith("/help")) {
55749
+ if (trimmed2.startsWith("/help")) {
55075
55750
  onToggleHelp();
55076
55751
  clearInputDraft();
55077
55752
  return;
55078
55753
  }
55079
- if (trimmed.startsWith("/tools")) {
55754
+ if (trimmed2.startsWith("/tools")) {
55080
55755
  setPendingToolsToggle((prev) => !prev);
55081
55756
  clearInputDraft();
55082
55757
  return;
55083
55758
  }
55084
- if (trimmed.startsWith("/sandbox")) {
55085
- const arg = trimmed.slice("/sandbox".length).trim();
55759
+ if (trimmed2.startsWith("/sandbox")) {
55760
+ const arg = trimmed2.slice("/sandbox".length).trim();
55086
55761
  setPendingSandbox(arg ? arg : false);
55087
55762
  clearInputDraft();
55088
55763
  return;
55089
55764
  }
55090
- if (trimmed.startsWith("/stop")) {
55765
+ if (trimmed2.startsWith("/stop")) {
55091
55766
  setPendingStop((prev) => !prev);
55092
55767
  clearInputDraft();
55093
55768
  return;
@@ -55096,11 +55771,11 @@ function CheckpointPrompt({
55096
55771
  submitStagedResult("stop");
55097
55772
  return;
55098
55773
  }
55099
- if (trimmed === "") {
55774
+ if (trimmed2 === "") {
55100
55775
  submitStagedResult("continue");
55101
55776
  return;
55102
55777
  }
55103
- submitStagedResult("steer", trimmed);
55778
+ submitStagedResult("steer", trimmed2);
55104
55779
  };
55105
55780
  const handleModelSelect = (model) => {
55106
55781
  setShowModelPicker(false);
@@ -55111,11 +55786,11 @@ function CheckpointPrompt({
55111
55786
  };
55112
55787
  const handleReflectSubmit = (message) => {
55113
55788
  setShowReflectEditor(false);
55114
- const trimmed = message.trim();
55115
- if (trimmed) {
55789
+ const trimmed2 = message.trim();
55790
+ if (trimmed2) {
55116
55791
  setValue((prev) => prev.trim() ? `${prev}
55117
55792
 
55118
- ${trimmed}` : trimmed);
55793
+ ${trimmed2}` : trimmed2);
55119
55794
  setIsTyping(true);
55120
55795
  }
55121
55796
  };
@@ -55251,12 +55926,12 @@ function SteerComposer({ initialDraft = "", onSubmit, onCancel }) {
55251
55926
  }
55252
55927
  });
55253
55928
  const handleSubmit = (text) => {
55254
- const trimmed = text.trim();
55255
- if (!trimmed) {
55929
+ const trimmed2 = text.trim();
55930
+ if (!trimmed2) {
55256
55931
  onCancel("");
55257
55932
  return;
55258
55933
  }
55259
- onSubmit(trimmed, kind);
55934
+ onSubmit(trimmed2, kind);
55260
55935
  };
55261
55936
  const kindLabel = kind === "steer" ? "deliver: next turn" : "deliver: after all work";
55262
55937
  return /* @__PURE__ */ jsxs23(
@@ -57261,8 +57936,8 @@ function MarathonApp({
57261
57936
  adjustedContentHeight - reasoningHintRows - reasoningBodyRows - reasoningLiveRows - thinkingRows - compactionIndicatorRows
57262
57937
  );
57263
57938
  const reasoningDetailLineCount = useMemo11(() => {
57264
- const trimmed = displayedReasoning.trimEnd();
57265
- return trimmed ? getReasoningLines(trimmed, terminalWidth).length : 0;
57939
+ const trimmed2 = displayedReasoning.trimEnd();
57940
+ return trimmed2 ? getReasoningLines(trimmed2, terminalWidth).length : 0;
57266
57941
  }, [displayedReasoning, terminalWidth]);
57267
57942
  const fileDetailRenderedLineCount = useMemo11(
57268
57943
  () => detailFile && detailFileContent ? getFileRenderedLineCount(detailFile.path, detailFileContent, terminalWidth) : 0,
@@ -58494,10 +59169,10 @@ var ALLOWED_VERIFICATION_COMMANDS = [
58494
59169
  /^(?:make|just)\s+(?:test|lint|build|check|typecheck)\b/i
58495
59170
  ];
58496
59171
  function isSafeVerificationCommand(command) {
58497
- const trimmed = command.trim();
58498
- if (!trimmed) return false;
58499
- if (BLOCKED_VERIFICATION_PATTERNS.some((pattern) => pattern.test(trimmed))) return false;
58500
- return ALLOWED_VERIFICATION_COMMANDS.some((pattern) => pattern.test(trimmed));
59172
+ const trimmed2 = command.trim();
59173
+ if (!trimmed2) return false;
59174
+ if (BLOCKED_VERIFICATION_PATTERNS.some((pattern) => pattern.test(trimmed2))) return false;
59175
+ return ALLOWED_VERIFICATION_COMMANDS.some((pattern) => pattern.test(trimmed2));
58501
59176
  }
58502
59177
  var SCRIPT_COMMAND_PATTERNS = [
58503
59178
  {
@@ -58514,9 +59189,9 @@ var SCRIPT_COMMAND_PATTERNS = [
58514
59189
  }
58515
59190
  ];
58516
59191
  function getScriptConfigFiles(command) {
58517
- const trimmed = command.trim();
59192
+ const trimmed2 = command.trim();
58518
59193
  for (const { pattern, files } of SCRIPT_COMMAND_PATTERNS) {
58519
- if (pattern.test(trimmed)) return files;
59194
+ if (pattern.test(trimmed2)) return files;
58520
59195
  }
58521
59196
  return [];
58522
59197
  }
@@ -60512,12 +61187,12 @@ function resolveModelContextLength(modelId, configuredModels, availableModels) {
60512
61187
  }
60513
61188
  function resolveAutoCompactTokenThreshold(modelContextLength, rawThreshold, strategy = "summary_fallback") {
60514
61189
  const effectiveBudget = resolveEffectiveInputBudgetTokens(modelContextLength);
60515
- const trimmed = rawThreshold?.trim();
60516
- if (!trimmed) {
61190
+ const trimmed2 = rawThreshold?.trim();
61191
+ if (!trimmed2) {
60517
61192
  const ratio = strategy === "provider_native" ? DEFAULT_PROVIDER_NATIVE_COMPACT_THRESHOLD_RATIO : DEFAULT_AUTO_COMPACT_THRESHOLD_RATIO;
60518
61193
  return typeof effectiveBudget === "number" && effectiveBudget > 0 ? Math.max(1, Math.floor(effectiveBudget * ratio)) : void 0;
60519
61194
  }
60520
- const percentMatch = trimmed.match(/^(\d+(?:\.\d+)?)%$/);
61195
+ const percentMatch = trimmed2.match(/^(\d+(?:\.\d+)?)%$/);
60521
61196
  if (percentMatch) {
60522
61197
  const percentValue = Number(percentMatch[1]);
60523
61198
  if (!Number.isFinite(percentValue) || percentValue <= 0 || percentValue > 100) {
@@ -60527,7 +61202,7 @@ function resolveAutoCompactTokenThreshold(modelContextLength, rawThreshold, stra
60527
61202
  }
60528
61203
  return typeof effectiveBudget === "number" && effectiveBudget > 0 ? Math.max(1, Math.floor(effectiveBudget * (percentValue / 100))) : void 0;
60529
61204
  }
60530
- const numericValue = Number(trimmed);
61205
+ const numericValue = Number(trimmed2);
60531
61206
  if (!Number.isFinite(numericValue) || numericValue <= 0) {
60532
61207
  throw new Error(
60533
61208
  `Invalid --compact-threshold value "${rawThreshold}". Use a percentage (e.g. 80%) or absolute token count (e.g. 120000).`
@@ -60997,9 +61672,9 @@ function formatBytes(bytes) {
60997
61672
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
60998
61673
  }
60999
61674
  function normalizeStartupModelOptionValue(value) {
61000
- const trimmed = value?.trim().toLowerCase().replace(/^model\//, "") ?? "";
61001
- if (!trimmed) return "";
61002
- const withoutProvider = trimmed.includes(":") ? trimmed.split(":").slice(1).join(":") : trimmed;
61675
+ const trimmed2 = value?.trim().toLowerCase().replace(/^model\//, "") ?? "";
61676
+ if (!trimmed2) return "";
61677
+ const withoutProvider = trimmed2.includes(":") ? trimmed2.split(":").slice(1).join(":") : trimmed2;
61003
61678
  if (withoutProvider.includes("/")) {
61004
61679
  return withoutProvider.split("/").pop() ?? withoutProvider;
61005
61680
  }
@@ -61081,8 +61756,8 @@ function buildMarathonClientHeaders(devPlanOverride) {
61081
61756
  };
61082
61757
  }
61083
61758
  function normalizeMarathonAgentArgument(agent) {
61084
- const trimmed = agent.trim();
61085
- if (trimmed.length < 2) return trimmed;
61759
+ const trimmed2 = agent.trim();
61760
+ if (trimmed2.length < 2) return trimmed2;
61086
61761
  const matchingQuotePairs = [
61087
61762
  ['"', '"'],
61088
61763
  ["'", "'"],
@@ -61090,12 +61765,12 @@ function normalizeMarathonAgentArgument(agent) {
61090
61765
  ["\u2018", "\u2019"]
61091
61766
  ];
61092
61767
  for (const [openQuote, closeQuote] of matchingQuotePairs) {
61093
- if (trimmed.startsWith(openQuote) && trimmed.endsWith(closeQuote)) {
61094
- const unwrapped = trimmed.slice(openQuote.length, trimmed.length - closeQuote.length).trim();
61095
- return unwrapped || trimmed;
61768
+ if (trimmed2.startsWith(openQuote) && trimmed2.endsWith(closeQuote)) {
61769
+ const unwrapped = trimmed2.slice(openQuote.length, trimmed2.length - closeQuote.length).trim();
61770
+ return unwrapped || trimmed2;
61096
61771
  }
61097
61772
  }
61098
- return trimmed;
61773
+ return trimmed2;
61099
61774
  }
61100
61775
  function buildMarathonAutoCreatedAgentBootstrap(agentName, options = {}) {
61101
61776
  const normalizedModel = options.model?.trim();
@@ -64357,7 +65032,10 @@ function printSuiteResult(rootDir, outcome) {
64357
65032
  const rel = relative6(rootDir, outcome.file) || outcome.file;
64358
65033
  const badge = result.passed ? chalk27.green("PASS") : chalk27.red("FAIL");
64359
65034
  const score = `${result.passedCases}/${result.totalCases}`;
64360
- console.log(`${badge} ${chalk27.bold(definition.name)} ${chalk27.gray(`(${score} cases \xB7 ${rel})`)}`);
65035
+ const runRef = result.runId ? ` \xB7 run ${result.runId}` : "";
65036
+ console.log(
65037
+ `${badge} ${chalk27.bold(definition.name)} ${chalk27.gray(`(${score} cases \xB7 ${rel}${runRef})`)}`
65038
+ );
64361
65039
  for (const testCase of result.cases) {
64362
65040
  const caseBadge = testCase.passed ? chalk27.green(" \u2713") : chalk27.red(" \u2717");
64363
65041
  console.log(`${caseBadge} ${testCase.name}`);
@@ -66577,11 +67255,11 @@ async function promptLine(rl, question, defaultValue) {
66577
67255
  const answer = await new Promise((resolve12) => {
66578
67256
  rl.question(`${question}${hint}: `, resolve12);
66579
67257
  });
66580
- const trimmed = answer.trim();
66581
- if (!trimmed && defaultValue !== void 0) {
67258
+ const trimmed2 = answer.trim();
67259
+ if (!trimmed2 && defaultValue !== void 0) {
66582
67260
  return defaultValue;
66583
67261
  }
66584
- return trimmed;
67262
+ return trimmed2;
66585
67263
  }
66586
67264
  async function promptConfirm2(rl, message, defaultYes = false) {
66587
67265
  const hint = defaultYes ? chalk30.dim(" (Y/n)") : chalk30.dim(" (y/N)");
@@ -68366,9 +69044,9 @@ async function readInput(input) {
68366
69044
  if (!input || input === "-") {
68367
69045
  return await readStdin();
68368
69046
  }
68369
- const trimmed = input.trim();
68370
- if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
68371
- return trimmed;
69047
+ const trimmed2 = input.trim();
69048
+ if (trimmed2.startsWith("{") || trimmed2.startsWith("[")) {
69049
+ return trimmed2;
68372
69050
  }
68373
69051
  return readFileSync18(input, "utf-8");
68374
69052
  }
@@ -70287,6 +70965,19 @@ function maybeNotifyAboutCliUpdate(args, options = {}) {
70287
70965
  void refreshCliUpdateMetadata(options);
70288
70966
  }
70289
70967
 
70968
+ // src/lib/global-option-forwarding.ts
70969
+ function registerGlobalOptionForwarding(program2) {
70970
+ program2.hook("preAction", (_thisCommand, actionCommand) => {
70971
+ const globals = program2.opts();
70972
+ if (typeof globals.apiUrl === "string" && globals.apiUrl.length > 0) {
70973
+ process.env.RUNTYPE_API_URL = globals.apiUrl;
70974
+ }
70975
+ if (globals.json === true && actionCommand.getOptionValue("json") === void 0) {
70976
+ actionCommand.setOptionValue("json", true);
70977
+ }
70978
+ });
70979
+ }
70980
+
70290
70981
  // src/index.ts
70291
70982
  loadEnv();
70292
70983
  setCliTitle();
@@ -70325,6 +71016,7 @@ program.addCommand(validateProductCommand);
70325
71016
  program.addCommand(deployCommand);
70326
71017
  program.addCommand(skillsCommand);
70327
71018
  program.addCommand(appsCommand);
71019
+ registerGlobalOptionForwarding(program);
70328
71020
  program.exitOverride();
70329
71021
  async function run() {
70330
71022
  const userArgs = process.argv.slice(2);
@@ -70347,7 +71039,9 @@ run().catch((error51) => {
70347
71039
  } else if (commanderError.code === "commander.unknownOption") {
70348
71040
  console.error(chalk42.red(`Error: ${commanderError.message}`));
70349
71041
  process.exit(1);
70350
- } else if (commanderError.code === "commander.help" || commanderError.code === "commander.version") {
71042
+ } else if (commanderError.code === "commander.unknownCommand") {
71043
+ process.exit(1);
71044
+ } else if (commanderError.code === "commander.help" || commanderError.code === "commander.helpDisplayed" || commanderError.code === "commander.version") {
70351
71045
  process.exit(0);
70352
71046
  } else {
70353
71047
  console.error(chalk42.red("An unexpected error occurred:"));