@runtypelabs/cli 2.24.1 → 2.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +918 -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() };
@@ -37713,41 +38068,141 @@ var caseExpectedSchema = external_exports.object({
37713
38068
  json: external_exports.unknown().optional(),
37714
38069
  facts: external_exports.array(external_exports.string()).optional()
37715
38070
  });
38071
+ var EVAL_CASE_ORIGINS = ["manual", "saved_from_run", "generated", "imported", "fpo"];
38072
+ var evalCaseOriginSchema = external_exports.enum(EVAL_CASE_ORIGINS);
37716
38073
  var DEFAULT_MODELS_FOR_NEW_ACCOUNTS = [
37717
38074
  // 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 },
38075
+ {
38076
+ provider: "runtype",
38077
+ modelId: "qwen/qwen3.6-27b",
38078
+ isDefault: false,
38079
+ strengths: "Fast, low-cost open-weight workhorse with reasoning, tool-use, and vision \u2014 good for high-volume extraction, classification, and simple transforms.",
38080
+ bestFor: ["fast-cheap", "vision"]
38081
+ },
37722
38082
  // Routed models - benefit from automatic provider optimization
37723
38083
  // 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 },
38084
+ {
38085
+ provider: "runtype",
38086
+ modelId: "gpt-5.5",
38087
+ isDefault: false,
38088
+ 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.",
38089
+ bestFor: ["default", "deep-reasoning", "agentic-tools", "vision", "long-context"]
38090
+ },
38091
+ {
38092
+ provider: "runtype",
38093
+ modelId: "gpt-5.4-mini",
38094
+ isDefault: false,
38095
+ strengths: "Fast and affordable with dependable JSON formatting \u2014 a good fit for mid-complexity task steps.",
38096
+ bestFor: ["fast-cheap", "default"]
38097
+ },
38098
+ {
38099
+ provider: "runtype",
38100
+ modelId: "gpt-5.4-nano",
38101
+ isDefault: false,
38102
+ strengths: "Cheapest and fastest GPT-5 tier \u2014 reserve for trivial deterministic transforms, routing, and classification, not complex authoring.",
38103
+ bestFor: ["fast-cheap"]
38104
+ },
37727
38105
  // 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 },
38106
+ {
38107
+ provider: "runtype",
38108
+ modelId: "claude-haiku-4-5",
38109
+ isDefault: false,
38110
+ strengths: "Fast and inexpensive while retaining real reasoning, tool-use, and vision \u2014 great for high-volume task steps that still need some judgment.",
38111
+ bestFor: ["fast-cheap", "agentic-tools", "vision"]
38112
+ },
38113
+ {
38114
+ provider: "runtype",
38115
+ modelId: "claude-sonnet-5",
38116
+ isDefault: false,
38117
+ 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.",
38118
+ bestFor: ["default", "agentic-tools", "vision", "long-context"]
38119
+ },
38120
+ {
38121
+ provider: "runtype",
38122
+ modelId: "claude-opus-4-8",
38123
+ isDefault: false,
38124
+ 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.",
38125
+ bestFor: ["deep-reasoning", "vision", "long-context"]
38126
+ },
37731
38127
  // 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 },
38128
+ {
38129
+ provider: "runtype",
38130
+ modelId: "gemini-3-1-flash-lite",
38131
+ isDefault: false,
38132
+ 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.",
38133
+ bestFor: ["fast-cheap", "vision", "long-context"]
38134
+ },
38135
+ {
38136
+ provider: "runtype",
38137
+ modelId: "gemini-3.5-flash",
38138
+ isDefault: false,
38139
+ 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.",
38140
+ bestFor: ["default", "vision", "fast-cheap", "agentic-tools", "long-context"]
38141
+ },
38142
+ {
38143
+ provider: "runtype",
38144
+ modelId: "gemini-3-1-flash-image",
38145
+ isDefault: false,
38146
+ strengths: "Specialised for image generation and editing (image output) \u2014 use for steps that produce images, not text reasoning.",
38147
+ bestFor: ["image-generation"]
38148
+ },
38149
+ {
38150
+ provider: "runtype",
38151
+ modelId: "gemini-3.1-pro",
38152
+ isDefault: false,
38153
+ strengths: "Google's frontier tier: strong reasoning, a 1M context, and multimodal input \u2014 for complex analytical or long-document steps.",
38154
+ bestFor: ["deep-reasoning", "vision", "long-context"]
38155
+ },
37736
38156
  // Inception models (routed via Vercel)
37737
- { provider: "runtype", modelId: "mercury-2", isDefault: false },
38157
+ {
38158
+ provider: "runtype",
38159
+ modelId: "mercury-2",
38160
+ isDefault: false,
38161
+ strengths: "Diffusion-based LLM optimised for very low latency \u2014 good when speed matters more than deep reasoning.",
38162
+ bestFor: ["fast-cheap"]
38163
+ },
37738
38164
  // Moonshot models (routed via Vercel)
37739
38165
  // Kimi K2.6 is the default model: a reasoning + tool-use + vision model
37740
38166
  // already trusted as the product-generator default.
37741
- { provider: "runtype", modelId: "kimi-k2.6", isDefault: true },
38167
+ {
38168
+ provider: "runtype",
38169
+ modelId: "kimi-k2.6",
38170
+ isDefault: true,
38171
+ strengths: "Capable open agentic model with reasoning, tool-use, and vision at strong value \u2014 the current account default and a dependable general workhorse.",
38172
+ bestFor: ["default", "agentic-tools", "vision"]
38173
+ },
37742
38174
  // NVIDIA Nemotron Ultra (reasoning + tool-use flagship, routed via Vercel)
37743
- { provider: "runtype", modelId: "nemotron-3-ultra-550b-a55b", isDefault: false },
38175
+ {
38176
+ provider: "runtype",
38177
+ modelId: "nemotron-3-ultra-550b-a55b",
38178
+ isDefault: false,
38179
+ 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.",
38180
+ bestFor: ["deep-reasoning", "agentic-tools", "long-context"]
38181
+ },
37744
38182
  // Google models (routed via Cloudflare Workers AI)
37745
- { provider: "runtype", modelId: "gemma-4-26b-a4b-it", isDefault: false },
38183
+ {
38184
+ provider: "runtype",
38185
+ modelId: "gemma-4-26b-a4b-it",
38186
+ isDefault: false,
38187
+ strengths: "Lightweight open-weight instruct model served free on Workers AI \u2014 fine for straightforward general tasks at zero cost.",
38188
+ bestFor: ["fast-cheap"]
38189
+ },
37746
38190
  // Z.ai GLM-5.2 (agentic coding flagship, routed via Cloudflare Workers AI,
37747
38191
  // Vercel fallback). The bare routed name resolves to `@cf/zai-org/glm-5.2`.
37748
- { provider: "runtype", modelId: "glm-5.2", isDefault: false }
38192
+ {
38193
+ provider: "runtype",
38194
+ modelId: "glm-5.2",
38195
+ isDefault: false,
38196
+ 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.",
38197
+ bestFor: ["agentic-tools", "default", "deep-reasoning", "long-context"]
38198
+ }
37749
38199
  ];
37750
38200
  var DEFAULT_MODEL_ID = DEFAULT_MODELS_FOR_NEW_ACCOUNTS.find((m2) => m2.isDefault)?.modelId ?? "kimi-k2.6";
38201
+ function renderDefaultModelStrengthsGuide() {
38202
+ return DEFAULT_MODELS_FOR_NEW_ACCOUNTS.map(
38203
+ (m2) => `- \`${m2.modelId}\`${m2.isDefault ? " (account default)" : ""} \u2014 ${m2.strengths}`
38204
+ ).join("\n");
38205
+ }
37751
38206
  var userProfileFeaturesSchema = external_exports.object({
37752
38207
  enableAgentSkills: external_exports.boolean(),
37753
38208
  // Gates the WebMCP-powered dashboard assistant (copilot panel + chat
@@ -37768,9 +38223,21 @@ var userProfileFeaturesSchema = external_exports.object({
37768
38223
  // `POST /v1/skills/scan` verdict UI). Driven by the `enable-skill-scanner`
37769
38224
  // boolean flag (fail-closed until a rule enables it). The scan ROUTE stays
37770
38225
  // reachable; this only hides the UI while the scanner rollout completes.
37771
- enableSkillScanner: external_exports.boolean()
38226
+ enableSkillScanner: external_exports.boolean(),
38227
+ // Gates the usage-based-pricing dashboard surfaces (card-centric paywall /
38228
+ // slow-mode copy, trial-expired alerts, CTAs to the credit-card entry page).
38229
+ // Driven by the `usage-based-pricing` boolean flag (fail-closed in
38230
+ // production until Lane 1 is live and a rule ramps a cohort). Off renders
38231
+ // today's UI byte-identically.
38232
+ usageBasedPricing: external_exports.boolean()
37772
38233
  });
37773
38234
  var MODEL_FAMILY_PROVIDER_IDS = {
38235
+ "arrow-1-1": {
38236
+ "vercel": "quiverai/arrow-1.1"
38237
+ },
38238
+ "arrow-1.1": {
38239
+ "vercel": "quiverai/arrow-1.1"
38240
+ },
37774
38241
  "claude-fable-5": {
37775
38242
  "anthropic": "claude-fable-5"
37776
38243
  },
@@ -37862,6 +38329,10 @@ var MODEL_FAMILY_PROVIDER_IDS = {
37862
38329
  "claude-sonnet-4.6": {
37863
38330
  "vercel": "anthropic/claude-sonnet-4.6"
37864
38331
  },
38332
+ "claude-sonnet-5": {
38333
+ "anthropic": "claude-sonnet-5",
38334
+ "vercel": "anthropic/claude-sonnet-5"
38335
+ },
37865
38336
  "codestral": {
37866
38337
  "vercel": "mistral/codestral"
37867
38338
  },
@@ -38046,6 +38517,9 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38046
38517
  "google": "gemini-3.1-flash-lite",
38047
38518
  "vercel": "google/gemini-3.1-flash-lite"
38048
38519
  },
38520
+ "gemini-3-1-flash-lite-image": {
38521
+ "vercel": "google/gemini-3.1-flash-lite-image"
38522
+ },
38049
38523
  "gemini-3-1-pro": {
38050
38524
  "google": "gemini-3.1-pro-preview",
38051
38525
  "vercel": "google/gemini-3.1-pro-preview"
@@ -38078,6 +38552,9 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38078
38552
  "google": "gemini-3.1-flash-lite",
38079
38553
  "vercel": "google/gemini-3.1-flash-lite"
38080
38554
  },
38555
+ "gemini-3.1-flash-lite-image": {
38556
+ "vercel": "google/gemini-3.1-flash-lite-image"
38557
+ },
38081
38558
  "gemini-3.1-pro-preview": {
38082
38559
  "google": "gemini-3.1-pro-preview",
38083
38560
  "vercel": "google/gemini-3.1-pro-preview"
@@ -38105,89 +38582,19 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38105
38582
  "togetherai": "togetherai/pearl-ai/gemma-4-31b-it",
38106
38583
  "vercel": "google/gemma-4-31b-it"
38107
38584
  },
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
38585
  "glm-5-2": {
38586
+ "togetherai": "togetherai/zai-org/GLM-5.2",
38171
38587
  "vercel": "zai/glm-5.2"
38172
38588
  },
38173
38589
  "glm-5-2-fast": {
38174
38590
  "vercel": "zai/glm-5.2-fast"
38175
38591
  },
38176
- "glm-5-turbo": {
38177
- "vercel": "zai/glm-5-turbo"
38178
- },
38179
- "glm-5.1": {
38180
- "vercel": "zai/glm-5.1"
38181
- },
38182
38592
  "glm-5.2": {
38183
38593
  "vercel": "zai/glm-5.2"
38184
38594
  },
38185
38595
  "glm-5.2-fast": {
38186
38596
  "vercel": "zai/glm-5.2-fast"
38187
38597
  },
38188
- "glm-5v-turbo": {
38189
- "vercel": "zai/glm-5v-turbo"
38190
- },
38191
38598
  "google/gemma-4-31B-it": {
38192
38599
  "togetherai": "togetherai/google/gemma-4-31B-it"
38193
38600
  },
@@ -38519,6 +38926,9 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38519
38926
  "grok-imagine-video-1-5": {
38520
38927
  "vercel": "xai/grok-imagine-video-1.5-preview"
38521
38928
  },
38929
+ "grok-imagine-video-1.5": {
38930
+ "vercel": "xai/grok-imagine-video-1.5"
38931
+ },
38522
38932
  "grok-imagine-video-1.5-preview": {
38523
38933
  "vercel": "xai/grok-imagine-video-1.5-preview"
38524
38934
  },
@@ -38735,22 +39145,6 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38735
39145
  "mimo-v2.5-pro": {
38736
39146
  "vercel": "xiaomi/mimo-v2.5-pro"
38737
39147
  },
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
39148
  "minimax-m2-7": {
38755
39149
  "general-compute": "general-compute/minimax-m2.7",
38756
39150
  "togetherai": "togetherai/MiniMaxAI/MiniMax-M2.7",
@@ -38759,18 +39153,6 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38759
39153
  "minimax-m2-7-highspeed": {
38760
39154
  "vercel": "minimax/minimax-m2.7-highspeed"
38761
39155
  },
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
39156
  "minimax-m2.7": {
38775
39157
  "general-compute": "general-compute/minimax-m2.7",
38776
39158
  "vercel": "minimax/minimax-m2.7"
@@ -38782,9 +39164,6 @@ var MODEL_FAMILY_PROVIDER_IDS = {
38782
39164
  "togetherai": "togetherai/MiniMaxAI/MiniMax-M3",
38783
39165
  "vercel": "minimax/minimax-m3"
38784
39166
  },
38785
- "MiniMaxAI/MiniMax-M2.5": {
38786
- "togetherai": "togetherai/MiniMaxAI/MiniMax-M2.5"
38787
- },
38788
39167
  "MiniMaxAI/MiniMax-M2.7": {
38789
39168
  "togetherai": "togetherai/MiniMaxAI/MiniMax-M2.7"
38790
39169
  },
@@ -39372,11 +39751,8 @@ var MODEL_FAMILY_PROVIDER_IDS = {
39372
39751
  "whisper-1": {
39373
39752
  "vercel": "openai/whisper-1"
39374
39753
  },
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"
39754
+ "zai-org/GLM-5.2": {
39755
+ "togetherai": "togetherai/zai-org/GLM-5.2"
39380
39756
  }
39381
39757
  };
39382
39758
  var PLATFORM_KEY_PROVIDER_MAP = {
@@ -40362,10 +40738,12 @@ var sandboxConfigSchema = external_exports.object({
40362
40738
  instanceType: external_exports.enum(["standard-1", "standard-2", "standard-3", "standard-4"]).optional(),
40363
40739
  networkAccess: sandboxNetworkAccessSchema.optional(),
40364
40740
  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()
40741
+ sleepAfter: external_exports.enum(["5m", "15m", "30m", "1h"]).optional()
40742
+ // NOTE: a `requireApprovalForBash` knob was forward-declared here for Phase 3
40743
+ // but never wired to any runtime gate, so it was REMOVED from the public
40744
+ // schema rather than ship a misleading, inert control (post-Fable handoff 05,
40745
+ // F3). To gate `bash` today use the generic `tools.approval` config, which
40746
+ // matches the tool by name. Re-add the dedicated knob only with its wiring.
40369
40747
  });
40370
40748
  var agentRuntimeConfigSchema = external_exports.object({
40371
40749
  model: external_exports.string().optional(),
@@ -40494,6 +40872,10 @@ var agentRuntimeConfigSchema = external_exports.object({
40494
40872
  }).optional(),
40495
40873
  errorHandling: errorHandlingSchema,
40496
40874
  loggingPolicy: external_exports.enum(["default", "on", "off"]).optional(),
40875
+ // Opt-in PII redaction for tail-log and telemetry payloads. `'default'`
40876
+ // defers to a lower layer (surface/product) or the resolver default (off).
40877
+ // Mirrors `loggingPolicy`.
40878
+ piiRedaction: external_exports.enum(["default", "off", "redact"]).optional(),
40497
40879
  // Opt-in temporal awareness. `injectElapsed` turns on the automatic
40498
40880
  // "time has passed" decorator (a `<temporal_context>` block prepended to
40499
40881
  // the user turn at dispatch, gated on turn ≥ 2 and a gap ≥
@@ -40519,6 +40901,142 @@ var agentRuntimeConfigSchema = external_exports.object({
40519
40901
  // `./tenancy-strategy`; the expansion + gate logic lives in apps/api.
40520
40902
  tenancyStrategy: tenancyStrategySchema.optional()
40521
40903
  });
40904
+ var identityAdapterKindSchema = external_exports.enum(["oidc-jwt", "session", "custom"]);
40905
+ var ASYMMETRIC_JWT_ALGORITHMS = [
40906
+ "RS256",
40907
+ "RS384",
40908
+ "RS512",
40909
+ "PS256",
40910
+ "PS384",
40911
+ "PS512",
40912
+ "ES256",
40913
+ "ES384",
40914
+ "ES512",
40915
+ "EdDSA"
40916
+ ];
40917
+ var asymmetricAlgorithmSchema = external_exports.enum(ASYMMETRIC_JWT_ALGORITHMS);
40918
+ var httpsUrlSchema = external_exports.string().url().refine((value) => value.startsWith("https://"), {
40919
+ message: "must be an https:// URL (plaintext http is not accepted)."
40920
+ });
40921
+ var claimMapSchema = external_exports.object({
40922
+ /** JSON path (dot notation) to the end-user id, e.g. `sub`. */
40923
+ subject: external_exports.string().min(1),
40924
+ /** JSON path to the tenant id, e.g. `org_id` / `o.id` / `organizationId`. Optional. */
40925
+ tenant: external_exports.string().min(1).optional(),
40926
+ /** JSON path to the end-user email. Optional. */
40927
+ email: external_exports.string().min(1).optional(),
40928
+ /** JSON path to a roles claim. Optional, display/authz context only. */
40929
+ roles: external_exports.string().min(1).optional(),
40930
+ /** JSON path to a permissions claim. Optional. */
40931
+ permissions: external_exports.string().min(1).optional()
40932
+ });
40933
+ var oboModeSchema = external_exports.enum([
40934
+ "token-exchange-8693",
40935
+ // Okta / Auth0 Custom Token Exchange (RFC 8693)
40936
+ "federated-token-vault",
40937
+ // Auth0 Token Vault, WorkOS Pipes, Clerk OAuth tokens
40938
+ "customer-endpoint"
40939
+ // Better Auth / custom escape hatch
40940
+ ]);
40941
+ var baseDescriptorShape = {
40942
+ /** Stable integration id (surface-scoped). Also the isolation namespace (C1). */
40943
+ id: external_exports.string().min(1),
40944
+ kind: identityAdapterKindSchema,
40945
+ claimMap: claimMapSchema,
40946
+ /**
40947
+ * The strongest assurance this integration may confer. Bounded ABOVE by the
40948
+ * kind ceiling ({@link assuranceCeilingForKind}) — may only lower it, never
40949
+ * raise it. A `custom`/`session` descriptor cannot declare `verified`.
40950
+ */
40951
+ maxAssurance: tenancyAssuranceLevelSchema.optional(),
40952
+ capabilities: external_exports.object({
40953
+ obo: external_exports.boolean().default(false),
40954
+ oboMode: oboModeSchema.optional()
40955
+ }).default({ obo: false })
40956
+ };
40957
+ var verifiableDescriptorShape = {
40958
+ /** Expected `iss`. REQUIRED, non-empty, non-wildcard. */
40959
+ issuer: httpsUrlSchema,
40960
+ /** JWKS endpoint for the issuer's public keys. REQUIRED. */
40961
+ jwksUri: httpsUrlSchema,
40962
+ /** Expected `aud`. REQUIRED, non-empty, non-wildcard (see refinement). */
40963
+ audience: external_exports.union([external_exports.string().min(1), external_exports.array(external_exports.string().min(1)).nonempty()]),
40964
+ /** Non-empty subset of {@link ASYMMETRIC_JWT_ALGORITHMS}. */
40965
+ allowedAlgorithms: external_exports.array(asymmetricAlgorithmSchema).nonempty(),
40966
+ clockSkewSec: external_exports.number().int().min(0).max(120).optional()
40967
+ };
40968
+ var identityAdapterDescriptorSchema = external_exports.discriminatedUnion("kind", [
40969
+ external_exports.object({
40970
+ ...baseDescriptorShape,
40971
+ ...verifiableDescriptorShape,
40972
+ kind: external_exports.literal("oidc-jwt"),
40973
+ /** `azp` allowlist (Clerk CSRF/origin guard, Auth0 authorized party). */
40974
+ authorizedParties: external_exports.array(external_exports.string().min(1)).optional(),
40975
+ /** Provider-specific post-verify policy toggles. */
40976
+ claimPolicy: external_exports.object({
40977
+ rejectImpersonation: external_exports.boolean().optional(),
40978
+ // Clerk `act`
40979
+ rejectPendingOrg: external_exports.boolean().optional(),
40980
+ // Clerk `sts:pending`
40981
+ rejectAudienceEqualsAzp: external_exports.boolean().optional(),
40982
+ // Auth0 ID-vs-access-token
40983
+ requireIssuerPathSegment: external_exports.string().min(1).optional()
40984
+ // Okta `/oauth2/{id}`
40985
+ }).optional()
40986
+ }),
40987
+ external_exports.object({
40988
+ ...baseDescriptorShape,
40989
+ ...verifiableDescriptorShape,
40990
+ kind: external_exports.literal("session")
40991
+ }),
40992
+ external_exports.object({
40993
+ ...baseDescriptorShape,
40994
+ kind: external_exports.literal("custom"),
40995
+ /** The customer verify endpoint (HTTPS). Egress is SSRF-guarded at call time. */
40996
+ verifyEndpoint: httpsUrlSchema
40997
+ })
40998
+ ]).superRefine((descriptor, ctx) => {
40999
+ if (descriptor.kind === "oidc-jwt" || descriptor.kind === "session") {
41000
+ const auds = Array.isArray(descriptor.audience) ? descriptor.audience : [descriptor.audience];
41001
+ if (auds.some((a) => a.trim() === "*" || a.trim() === "")) {
41002
+ ctx.addIssue({
41003
+ code: external_exports.ZodIssueCode.custom,
41004
+ path: ["audience"],
41005
+ message: "audience must name a specific relying party (no wildcard / empty value)."
41006
+ });
41007
+ }
41008
+ }
41009
+ if (descriptor.maxAssurance) {
41010
+ const ceiling = assuranceCeilingForKind(descriptor.kind);
41011
+ if (assuranceRank(descriptor.maxAssurance) === 0) {
41012
+ ctx.addIssue({
41013
+ code: external_exports.ZodIssueCode.custom,
41014
+ path: ["maxAssurance"],
41015
+ message: "maxAssurance cannot be 'anonymous' \u2014 it is a ceiling; omit the field or use 'asserted' / 'verified'."
41016
+ });
41017
+ } else if (assuranceRank(descriptor.maxAssurance) > assuranceRank(ceiling)) {
41018
+ ctx.addIssue({
41019
+ code: external_exports.ZodIssueCode.custom,
41020
+ path: ["maxAssurance"],
41021
+ message: `maxAssurance '${descriptor.maxAssurance}' exceeds the '${descriptor.kind}' kind ceiling '${ceiling}'.`
41022
+ });
41023
+ }
41024
+ }
41025
+ });
41026
+ function assuranceRank(level) {
41027
+ return level === "verified" ? 2 : level === "asserted" ? 1 : 0;
41028
+ }
41029
+ function assuranceCeilingForKind(kind) {
41030
+ return kind === "oidc-jwt" ? "verified" : "asserted";
41031
+ }
41032
+ var identityProofSchema = external_exports.object({
41033
+ /** Which configured integration the token claims to come from (hint only). */
41034
+ provider: external_exports.string().min(1).max(64),
41035
+ /** The customer-native proof (JWT / opaque session token). */
41036
+ token: external_exports.string().min(1).max(8192)
41037
+ }).describe(
41038
+ "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."
41039
+ );
40522
41040
  function extractSecretReferences(template) {
40523
41041
  const keys = /* @__PURE__ */ new Set();
40524
41042
  let match;
@@ -42094,10 +42612,22 @@ var FLOW_STEP_TYPE_METADATA = {
42094
42612
  configHints: "http, auth, requestTemplate, responseMapping, outputVariable"
42095
42613
  },
42096
42614
  "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).",
42615
+ 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.",
42616
+ category: "data",
42617
+ isPrompt: false,
42618
+ configHints: "DEPRECATED. Prefer get-record / list-records. recordType, recordName, recordFilter ({ type, where: { field, op, value } }), outputVariable"
42619
+ },
42620
+ "get-record": {
42621
+ 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
42622
  category: "data",
42099
42623
  isPrompt: false,
42100
- configHints: "recordType, recordName, recordFilter ({ type, where: { field, op, value } }), outputVariable"
42624
+ configHints: "recordId, recordType, recordName, recordFilter ({ type, where: { field, op, value } }), outputVariable (returns one object \u2014 access as {{var.field}})"
42625
+ },
42626
+ "list-records": {
42627
+ 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).",
42628
+ category: "data",
42629
+ isPrompt: false,
42630
+ 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
42631
  },
42102
42632
  "upsert-record": {
42103
42633
  description: "Create or update a record. The sourceVariable must point to a JSON object.",
@@ -42802,7 +43332,8 @@ ${lines.join("\n")}`;
42802
43332
  var PLATFORM_FEATURES_SUMMARY = (() => {
42803
43333
  const integrations = PLATFORM_CATALOG.availableIntegrations.map((i) => i.provider).join(", ");
42804
43334
  const stepTypes = FLOW_STEP_TYPES.filter((t) => t !== "prompt").join(", ");
42805
- const models = DEFAULT_MODELS_FOR_NEW_ACCOUNTS.map((m2) => m2.modelId).join(", ");
43335
+ const models = `use ONLY these routed IDs (never dated/legacy IDs like gpt-4.1 or gpt-4o), matched to the work:
43336
+ ${renderDefaultModelStrengthsGuide()}`;
42806
43337
  return `## Available Platform Features
42807
43338
 
42808
43339
  - **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 +43343,113 @@ var PLATFORM_FEATURES_SUMMARY = (() => {
42812
43343
  - **Available models**: ${models}
42813
43344
  - **Surface types**: ${PLATFORM_CATALOG.availableSurfaceTypes.join(", ")}`;
42814
43345
  })();
43346
+ var FLOW_GENERATION_GUIDE = `## Flow Generation Guide
43347
+
43348
+ ### Step Types
43349
+
43350
+ - **prompt**: AI model call. Config: model, systemPrompt, userPrompt, responseFormat (text|json), outputVariable, tools
43351
+ - **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
43352
+ - **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.
43353
+
43354
+ **Config fields:**
43355
+ - \`template\` (required): Liquid template body
43356
+ - \`outputFormat\` (required): \`html\` | \`email-html\` | \`markdown\` | \`pdf\` | \`text\`
43357
+ - \`outputVariable\` (required): variable name to store the rendered output
43358
+ - \`inputs\` (optional): map of logical name \u2192 expression resolving against flow context. If omitted, the entire flow context is available as template variables
43359
+ - \`partials\` (optional): named partial templates referenceable via \`{% include 'name' %}\`
43360
+ - \`pdfOptions\` (optional, pdf only): \`{ format?: 'A4' | 'Letter', landscape?: boolean, margin?: { top, bottom, left, right } }\`. PDF output returns \`{ contentType, bytes (base64), byteLength }\`
43361
+ - \`asArtifact\` (optional): emit a Persona artifact event for chat surfaces \u2014 renders inline in the conversation
43362
+ - \`streamOutput\` (optional): stream the rendered output via SSE
43363
+ - \`sampleData\` (optional): array of \`{ name, data }\` scenarios for the dashboard template editor preview
43364
+
43365
+ **Liquid syntax quick reference:**
43366
+ - Variables: \`{{ name }}\`, \`{{ object.field }}\`, \`{{ array[0] }}\`
43367
+ - Filters: \`{{ name | upcase }}\`, \`{{ price | round: 2 }}\`, \`{{ title | default: "Untitled" }}\`
43368
+ - Loops: \`{% for item in items %} ... {% endfor %}\`
43369
+ - Conditionals: \`{% if condition %} ... {% elsif other %} ... {% else %} ... {% endif %}\`
43370
+ - Partials: \`{% include 'partial_name' %}\` (references \`partials\` map keys)
43371
+ - Assignment: \`{% assign total = 0 %}\`, \`{% capture greeting %}Hello {{ name }}{% endcapture %}\`
43372
+ - Common filters: upcase, downcase, capitalize, strip, truncate, replace, split, join, size, round, date, default, escape, url_encode, newline_to_br
43373
+
43374
+ **Output format notes:**
43375
+ - \`html\` / \`email-html\`: Raw HTML string. \`email-html\` is identical today but semantically marks output destined for send-email steps
43376
+ - \`markdown\`: Plain markdown text
43377
+ - \`pdf\`: Renders the Liquid body as HTML, then converts to PDF. Result is \`{ contentType: "application/pdf", bytes: "<base64>", byteLength: N }\`
43378
+ - \`text\`: Plain text, no HTML processing
43379
+
43380
+ **Inputs map vs. full context:**
43381
+ - 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
43382
+ - Without \`inputs\`: the entire flow variable scope is available. Convenient for simple templates but risks name collisions
43383
+
43384
+ **Example \u2014 prompt produces JSON, template renders HTML:**
43385
+ \`\`\`
43386
+ Step 1 (prompt): model: "claude-sonnet-4-6", responseFormat: "json", outputVariable: "invoice"
43387
+ \u2192 AI generates { "company": "Acme", "items": [{...}], "total": "1,234.56" }
43388
+ Step 2 (template):
43389
+ template: "<h1>Invoice for {{ data.company }}</h1>{% for item in data.items %}<p>{{ item.desc }}: \${{ item.price }}</p>{% endfor %}<p>Total: \${{ data.total }}</p>"
43390
+ inputs: { "data": "{{invoice}}" }
43391
+ outputFormat: "html"
43392
+ outputVariable: "invoice_html"
43393
+ \`\`\`
43394
+ - **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.
43395
+ - **fetch-url**: HTTP request. Config: http (url, method, headers, body), responseType, outputVariable
43396
+ - **api-call**: Structured API call. Config: http, auth, requestTemplate, responseMapping, outputVariable
43397
+ - **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.
43398
+ - **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.
43399
+ - **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.
43400
+ - **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).
43401
+ - **update-record**: Update record fields. Config: recordId, OR recordFilter ({ type, where }) for first-match-wins by predicate; updates, mergeStrategy (merge|replace|deep-merge), outputVariable.
43402
+ - **set-variable**: Set a variable. Config: variableName, value
43403
+ - **send-email**: Send email. Config: from, to, subject, html, outputVariable
43404
+ - **send-text**: Send SMS. Config: from, to, message, outputVariable
43405
+ - **search**: Web/AI search. Config: provider, query, maxResults, outputVariable
43406
+ - **generate-embedding**: Create vector embedding. Config: inputSource, text, embeddingModel, outputVariable
43407
+ - **vector-search**: Search vectors. Config: query, limit, threshold, outputVariable
43408
+ - **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.
43409
+ - **paginate-api**: Paginated API fetch. Config: url, paginationType, entityPath, outputVariable
43410
+ - **store-vector**: Store embeddings. Config: vectorsSource, destination, outputVariable
43411
+ - **execute-agent**: Run an existing agent. Config: agentId, message, outputVariable
43412
+ - **save-memory**: Persist a fact or observation to Cloudflare Agent Memory. Config: profileTemplate (e.g. \`{{_user.id}}\`), contentVariable, sessionId?, outputVariable?
43413
+ - **recall-memory**: Query long-term memory for a synthesized answer. Config: profileTemplate, queryTemplate, thinkingLevel? (low|medium|high), responseLength? (short|medium|long), outputVariable
43414
+ - **memory-summary**: Fetch a Markdown summary of what the agent has stored. Config: profileTemplate, sessionId?, outputVariable
43415
+
43416
+ > **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.
43417
+
43418
+ > **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.
43419
+
43420
+ ### Tool Usage Best Practice
43421
+
43422
+ 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.
43423
+
43424
+ 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.
43425
+
43426
+ > **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.
43427
+
43428
+ ### Variable System
43429
+
43430
+ - System variables (read-only): \`{{_flow.id}}\`, \`{{_flow.name}}\`, \`{{_user.id}}\`, \`{{_organization.id}}\`, \`{{_agent.id}}\`, \`{{_execution.id}}\`, \`{{_execution.conversationId}}\`
43431
+ - 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
43432
+ - 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.
43433
+ - 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}}\`).
43434
+ - Record variables: \`{{_record.id}}\`, \`{{_record.name}}\`, \`{{_record.type}}\`, \`{{_record.metadata.field}}\`
43435
+ - 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"}}\`
43436
+ - Message variables: \`{{messages}}\`, \`{{userMessage}}\`, \`{{lastMessage}}\`, \`{{messageCount}}\`
43437
+ - Step outputs: Use the outputVariable name from previous steps, e.g., \`{{result}}\`, \`{{analysis}}\`
43438
+ - **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).
43439
+ - Custom inputs: \`{{input_name}}\` \u2014 top-level variables passed via dispatch inputs
43440
+ - Defaults & fallbacks:
43441
+ - Legacy literal default: \`{{name|Anonymous}}\` \u2014 uses \`Anonymous\` if \`name\` is unset
43442
+ - Truthy fallback: \`{{primary || fallback}}\` \u2014 first truthy operand wins (skips empty strings, 0, false)
43443
+ - Nullish fallback: \`{{count ?? 0}}\` \u2014 keeps 0 / "" / false (only null/undefined fall through)
43444
+ - Chains: \`{{a || b || "default"}}\` \u2014 supports paths and string/number/boolean/null literals; do NOT mix \`||\` and \`??\` in one expression
43445
+
43446
+ ### Model Selection
43447
+
43448
+ Use ONLY routed model IDs from the list below (platform-key compatible). Never
43449
+ emit dated or legacy IDs (e.g. \`gpt-4.1\`, \`gpt-4o\`, \`claude-sonnet-4\`) \u2014 they
43450
+ are not enabled for new accounts. Match the model to the step's actual work:
43451
+
43452
+ ${renderDefaultModelStrengthsGuide()}`;
42815
43453
  var CATEGORY_DISPLAY_NAMES = {
42816
43454
  [BuiltInToolCategory.IMAGE_GENERATION]: "Image Generation",
42817
43455
  [BuiltInToolCategory.WEB_SEARCH]: "Web Search",
@@ -43628,6 +44266,22 @@ function selectAccountId(params) {
43628
44266
  if (isValidAccountId(params.userId)) return params.userId;
43629
44267
  return void 0;
43630
44268
  }
44269
+ function flattenTextContent(content) {
44270
+ if (typeof content === "string") return content;
44271
+ return content.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
44272
+ }
44273
+ function hasUsableClaudeManagedSeed(messages) {
44274
+ let lastUserIndex = -1;
44275
+ for (let i = messages.length - 1; i >= 0; i--) {
44276
+ if (messages[i]?.role === "user") {
44277
+ lastUserIndex = i;
44278
+ break;
44279
+ }
44280
+ }
44281
+ if (lastUserIndex === -1) return false;
44282
+ if (lastUserIndex > 0) return true;
44283
+ return flattenTextContent(messages[lastUserIndex].content).length > 0;
44284
+ }
43631
44285
  var RecordInputSchema = external_exports.object({
43632
44286
  id: external_exports.string().min(1).optional(),
43633
44287
  name: external_exports.string().min(1).optional(),
@@ -43888,6 +44542,12 @@ var DispatchRequestSchema = external_exports.object({
43888
44542
  tenant: external_exports.object({
43889
44543
  id: external_exports.string().min(1)
43890
44544
  }).passthrough().optional(),
44545
+ // Identity Exchange proof (design §4.1): a customer-native auth token the
44546
+ // broker will verify server-side and admit as `verified` tenant/end-user.
44547
+ // Wire contract only for now — parsed here, consumed by NOTHING until the
44548
+ // admission slice wires verification into the tenancy seam. The token is a
44549
+ // live credential: never log or persist it (see identityProofSchema docs).
44550
+ identityProof: identityProofSchema.optional(),
43891
44551
  options: external_exports.object({
43892
44552
  streamResponse: external_exports.boolean().optional().default(true),
43893
44553
  modelOverride: external_exports.string().optional(),
@@ -43923,6 +44583,15 @@ var DispatchRequestSchema = external_exports.object({
43923
44583
  })
43924
44584
  }).refine((data) => data.flow || data.agent, {
43925
44585
  message: "Either 'flow' or 'agent' configuration must be provided"
44586
+ }).superRefine((data, ctx) => {
44587
+ if (data.agent?.agentType !== "claude_managed") return;
44588
+ if (!hasUsableClaudeManagedSeed(data.messages ?? [])) {
44589
+ ctx.addIssue({
44590
+ code: "custom",
44591
+ path: ["messages"],
44592
+ message: "messages with a non-empty user message is required for Claude Managed agents"
44593
+ });
44594
+ }
43926
44595
  });
43927
44596
  var RunFlowOptionsSchema = external_exports.object({
43928
44597
  recordId: external_exports.string().optional(),
@@ -44984,11 +45653,11 @@ async function promptConfirm(message, options) {
44984
45653
  return new Promise((resolve12) => {
44985
45654
  rl.question(chalk3.cyan(`${message} (${hint}): `), (answer) => {
44986
45655
  rl.close();
44987
- const trimmed = answer.trim().toLowerCase();
44988
- if (trimmed === "") {
45656
+ const trimmed2 = answer.trim().toLowerCase();
45657
+ if (trimmed2 === "") {
44989
45658
  resolve12(defaultYes);
44990
45659
  } else {
44991
- resolve12(trimmed === "y" || trimmed === "yes");
45660
+ resolve12(trimmed2 === "y" || trimmed2 === "yes");
44992
45661
  }
44993
45662
  });
44994
45663
  });
@@ -47370,7 +48039,13 @@ secretsCommand.command("get <id>").description("Get secret metadata (value is ne
47370
48039
  const { waitUntilExit } = render6(React6.createElement(App));
47371
48040
  await waitUntilExit();
47372
48041
  });
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(
48042
+ 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(
48043
+ "--product-tenant-id <id>",
48044
+ "Scope the secret to a Product Tenant (tnt_*). Omit for an org-level secret; a scoped secret requires a value."
48045
+ ).option(
48046
+ "--end-user-id <id>",
48047
+ "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."
48048
+ ).option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(
47374
48049
  async (options) => {
47375
48050
  const apiKey = await ensureAuth();
47376
48051
  if (!apiKey) return;
@@ -47390,6 +48065,8 @@ secretsCommand.command("create").description("Create a new secret (provide the v
47390
48065
  description: options.description
47391
48066
  };
47392
48067
  if (value !== void 0) body.value = value;
48068
+ if (options.productTenantId !== void 0) body.productTenantId = options.productTenantId;
48069
+ if (options.endUserId !== void 0) body.endUserId = options.endUserId;
47393
48070
  if (!isTTY(options) || options.json) {
47394
48071
  try {
47395
48072
  const data = await client.post("/secrets", body);
@@ -50501,9 +51178,9 @@ ${delta}` : `${content}${delta}`;
50501
51178
  }
50502
51179
  function getVisibleReasoningLines(reasoning, maxLines) {
50503
51180
  if (maxLines <= 0) return [];
50504
- const trimmed = reasoning.trimEnd();
50505
- if (!trimmed) return [];
50506
- return trimmed.split("\n").slice(-maxLines);
51181
+ const trimmed2 = reasoning.trimEnd();
51182
+ if (!trimmed2) return [];
51183
+ return trimmed2.split("\n").slice(-maxLines);
50507
51184
  }
50508
51185
 
50509
51186
  // src/ink/marathon/useMarathonStream.ts
@@ -52906,12 +53583,12 @@ function parseSandboxTimeout(value, provider) {
52906
53583
  }
52907
53584
  function parseDaytonaExecutionResult(value) {
52908
53585
  if (typeof value !== "string") return value;
52909
- const trimmed = value.trim();
52910
- if (!trimmed) return "";
53586
+ const trimmed2 = value.trim();
53587
+ if (!trimmed2) return "";
52911
53588
  try {
52912
- return JSON.parse(trimmed);
53589
+ return JSON.parse(trimmed2);
52913
53590
  } catch {
52914
- const lines = trimmed.split("\n").map((line) => line.trim()).filter(Boolean);
53591
+ const lines = trimmed2.split("\n").map((line) => line.trim()).filter(Boolean);
52915
53592
  const lastLine = lines[lines.length - 1];
52916
53593
  if (!lastLine) return value;
52917
53594
  try {
@@ -55030,9 +55707,9 @@ function CheckpointPrompt({
55030
55707
  onSubmit
55031
55708
  ]);
55032
55709
  const handleSubmit = (text) => {
55033
- const trimmed = text.trim();
55034
- const modelArg = trimmed.startsWith("/model") ? trimmed.slice("/model".length).trim() : "";
55035
- if (trimmed.startsWith("/model")) {
55710
+ const trimmed2 = text.trim();
55711
+ const modelArg = trimmed2.startsWith("/model") ? trimmed2.slice("/model".length).trim() : "";
55712
+ if (trimmed2.startsWith("/model")) {
55036
55713
  clearInputDraft();
55037
55714
  if (modelArg) {
55038
55715
  setPendingModel(modelArg);
@@ -55041,53 +55718,53 @@ function CheckpointPrompt({
55041
55718
  }
55042
55719
  return;
55043
55720
  }
55044
- if (trimmed.startsWith("/reflect")) {
55721
+ if (trimmed2.startsWith("/reflect")) {
55045
55722
  setShowReflectEditor(true);
55046
55723
  clearInputDraft();
55047
55724
  return;
55048
55725
  }
55049
- if (trimmed === "/tree") {
55726
+ if (trimmed2 === "/tree") {
55050
55727
  setTreePanelRows(onLoadTreeView?.() ?? []);
55051
55728
  clearInputDraft();
55052
55729
  return;
55053
55730
  }
55054
- if (trimmed === "/fork") {
55731
+ if (trimmed2 === "/fork") {
55055
55732
  setForkCandidates(onLoadForkCandidates?.() ?? []);
55056
55733
  clearInputDraft();
55057
55734
  return;
55058
55735
  }
55059
- if (trimmed === "/copy") {
55736
+ if (trimmed2 === "/copy") {
55060
55737
  onCopySession?.();
55061
55738
  clearInputDraft();
55062
55739
  return;
55063
55740
  }
55064
- if (trimmed === "/copy-trimmed") {
55741
+ if (trimmed2 === "/copy-trimmed") {
55065
55742
  onCopySessionTrimmed?.();
55066
55743
  clearInputDraft();
55067
55744
  return;
55068
55745
  }
55069
- if (trimmed === "/open") {
55746
+ if (trimmed2 === "/open") {
55070
55747
  onOpenStateFile?.();
55071
55748
  clearInputDraft();
55072
55749
  return;
55073
55750
  }
55074
- if (trimmed.startsWith("/help")) {
55751
+ if (trimmed2.startsWith("/help")) {
55075
55752
  onToggleHelp();
55076
55753
  clearInputDraft();
55077
55754
  return;
55078
55755
  }
55079
- if (trimmed.startsWith("/tools")) {
55756
+ if (trimmed2.startsWith("/tools")) {
55080
55757
  setPendingToolsToggle((prev) => !prev);
55081
55758
  clearInputDraft();
55082
55759
  return;
55083
55760
  }
55084
- if (trimmed.startsWith("/sandbox")) {
55085
- const arg = trimmed.slice("/sandbox".length).trim();
55761
+ if (trimmed2.startsWith("/sandbox")) {
55762
+ const arg = trimmed2.slice("/sandbox".length).trim();
55086
55763
  setPendingSandbox(arg ? arg : false);
55087
55764
  clearInputDraft();
55088
55765
  return;
55089
55766
  }
55090
- if (trimmed.startsWith("/stop")) {
55767
+ if (trimmed2.startsWith("/stop")) {
55091
55768
  setPendingStop((prev) => !prev);
55092
55769
  clearInputDraft();
55093
55770
  return;
@@ -55096,11 +55773,11 @@ function CheckpointPrompt({
55096
55773
  submitStagedResult("stop");
55097
55774
  return;
55098
55775
  }
55099
- if (trimmed === "") {
55776
+ if (trimmed2 === "") {
55100
55777
  submitStagedResult("continue");
55101
55778
  return;
55102
55779
  }
55103
- submitStagedResult("steer", trimmed);
55780
+ submitStagedResult("steer", trimmed2);
55104
55781
  };
55105
55782
  const handleModelSelect = (model) => {
55106
55783
  setShowModelPicker(false);
@@ -55111,11 +55788,11 @@ function CheckpointPrompt({
55111
55788
  };
55112
55789
  const handleReflectSubmit = (message) => {
55113
55790
  setShowReflectEditor(false);
55114
- const trimmed = message.trim();
55115
- if (trimmed) {
55791
+ const trimmed2 = message.trim();
55792
+ if (trimmed2) {
55116
55793
  setValue((prev) => prev.trim() ? `${prev}
55117
55794
 
55118
- ${trimmed}` : trimmed);
55795
+ ${trimmed2}` : trimmed2);
55119
55796
  setIsTyping(true);
55120
55797
  }
55121
55798
  };
@@ -55251,12 +55928,12 @@ function SteerComposer({ initialDraft = "", onSubmit, onCancel }) {
55251
55928
  }
55252
55929
  });
55253
55930
  const handleSubmit = (text) => {
55254
- const trimmed = text.trim();
55255
- if (!trimmed) {
55931
+ const trimmed2 = text.trim();
55932
+ if (!trimmed2) {
55256
55933
  onCancel("");
55257
55934
  return;
55258
55935
  }
55259
- onSubmit(trimmed, kind);
55936
+ onSubmit(trimmed2, kind);
55260
55937
  };
55261
55938
  const kindLabel = kind === "steer" ? "deliver: next turn" : "deliver: after all work";
55262
55939
  return /* @__PURE__ */ jsxs23(
@@ -57261,8 +57938,8 @@ function MarathonApp({
57261
57938
  adjustedContentHeight - reasoningHintRows - reasoningBodyRows - reasoningLiveRows - thinkingRows - compactionIndicatorRows
57262
57939
  );
57263
57940
  const reasoningDetailLineCount = useMemo11(() => {
57264
- const trimmed = displayedReasoning.trimEnd();
57265
- return trimmed ? getReasoningLines(trimmed, terminalWidth).length : 0;
57941
+ const trimmed2 = displayedReasoning.trimEnd();
57942
+ return trimmed2 ? getReasoningLines(trimmed2, terminalWidth).length : 0;
57266
57943
  }, [displayedReasoning, terminalWidth]);
57267
57944
  const fileDetailRenderedLineCount = useMemo11(
57268
57945
  () => detailFile && detailFileContent ? getFileRenderedLineCount(detailFile.path, detailFileContent, terminalWidth) : 0,
@@ -58494,10 +59171,10 @@ var ALLOWED_VERIFICATION_COMMANDS = [
58494
59171
  /^(?:make|just)\s+(?:test|lint|build|check|typecheck)\b/i
58495
59172
  ];
58496
59173
  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));
59174
+ const trimmed2 = command.trim();
59175
+ if (!trimmed2) return false;
59176
+ if (BLOCKED_VERIFICATION_PATTERNS.some((pattern) => pattern.test(trimmed2))) return false;
59177
+ return ALLOWED_VERIFICATION_COMMANDS.some((pattern) => pattern.test(trimmed2));
58501
59178
  }
58502
59179
  var SCRIPT_COMMAND_PATTERNS = [
58503
59180
  {
@@ -58514,9 +59191,9 @@ var SCRIPT_COMMAND_PATTERNS = [
58514
59191
  }
58515
59192
  ];
58516
59193
  function getScriptConfigFiles(command) {
58517
- const trimmed = command.trim();
59194
+ const trimmed2 = command.trim();
58518
59195
  for (const { pattern, files } of SCRIPT_COMMAND_PATTERNS) {
58519
- if (pattern.test(trimmed)) return files;
59196
+ if (pattern.test(trimmed2)) return files;
58520
59197
  }
58521
59198
  return [];
58522
59199
  }
@@ -60512,12 +61189,12 @@ function resolveModelContextLength(modelId, configuredModels, availableModels) {
60512
61189
  }
60513
61190
  function resolveAutoCompactTokenThreshold(modelContextLength, rawThreshold, strategy = "summary_fallback") {
60514
61191
  const effectiveBudget = resolveEffectiveInputBudgetTokens(modelContextLength);
60515
- const trimmed = rawThreshold?.trim();
60516
- if (!trimmed) {
61192
+ const trimmed2 = rawThreshold?.trim();
61193
+ if (!trimmed2) {
60517
61194
  const ratio = strategy === "provider_native" ? DEFAULT_PROVIDER_NATIVE_COMPACT_THRESHOLD_RATIO : DEFAULT_AUTO_COMPACT_THRESHOLD_RATIO;
60518
61195
  return typeof effectiveBudget === "number" && effectiveBudget > 0 ? Math.max(1, Math.floor(effectiveBudget * ratio)) : void 0;
60519
61196
  }
60520
- const percentMatch = trimmed.match(/^(\d+(?:\.\d+)?)%$/);
61197
+ const percentMatch = trimmed2.match(/^(\d+(?:\.\d+)?)%$/);
60521
61198
  if (percentMatch) {
60522
61199
  const percentValue = Number(percentMatch[1]);
60523
61200
  if (!Number.isFinite(percentValue) || percentValue <= 0 || percentValue > 100) {
@@ -60527,7 +61204,7 @@ function resolveAutoCompactTokenThreshold(modelContextLength, rawThreshold, stra
60527
61204
  }
60528
61205
  return typeof effectiveBudget === "number" && effectiveBudget > 0 ? Math.max(1, Math.floor(effectiveBudget * (percentValue / 100))) : void 0;
60529
61206
  }
60530
- const numericValue = Number(trimmed);
61207
+ const numericValue = Number(trimmed2);
60531
61208
  if (!Number.isFinite(numericValue) || numericValue <= 0) {
60532
61209
  throw new Error(
60533
61210
  `Invalid --compact-threshold value "${rawThreshold}". Use a percentage (e.g. 80%) or absolute token count (e.g. 120000).`
@@ -60997,9 +61674,9 @@ function formatBytes(bytes) {
60997
61674
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
60998
61675
  }
60999
61676
  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;
61677
+ const trimmed2 = value?.trim().toLowerCase().replace(/^model\//, "") ?? "";
61678
+ if (!trimmed2) return "";
61679
+ const withoutProvider = trimmed2.includes(":") ? trimmed2.split(":").slice(1).join(":") : trimmed2;
61003
61680
  if (withoutProvider.includes("/")) {
61004
61681
  return withoutProvider.split("/").pop() ?? withoutProvider;
61005
61682
  }
@@ -61081,8 +61758,8 @@ function buildMarathonClientHeaders(devPlanOverride) {
61081
61758
  };
61082
61759
  }
61083
61760
  function normalizeMarathonAgentArgument(agent) {
61084
- const trimmed = agent.trim();
61085
- if (trimmed.length < 2) return trimmed;
61761
+ const trimmed2 = agent.trim();
61762
+ if (trimmed2.length < 2) return trimmed2;
61086
61763
  const matchingQuotePairs = [
61087
61764
  ['"', '"'],
61088
61765
  ["'", "'"],
@@ -61090,12 +61767,12 @@ function normalizeMarathonAgentArgument(agent) {
61090
61767
  ["\u2018", "\u2019"]
61091
61768
  ];
61092
61769
  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;
61770
+ if (trimmed2.startsWith(openQuote) && trimmed2.endsWith(closeQuote)) {
61771
+ const unwrapped = trimmed2.slice(openQuote.length, trimmed2.length - closeQuote.length).trim();
61772
+ return unwrapped || trimmed2;
61096
61773
  }
61097
61774
  }
61098
- return trimmed;
61775
+ return trimmed2;
61099
61776
  }
61100
61777
  function buildMarathonAutoCreatedAgentBootstrap(agentName, options = {}) {
61101
61778
  const normalizedModel = options.model?.trim();
@@ -64357,7 +65034,10 @@ function printSuiteResult(rootDir, outcome) {
64357
65034
  const rel = relative6(rootDir, outcome.file) || outcome.file;
64358
65035
  const badge = result.passed ? chalk27.green("PASS") : chalk27.red("FAIL");
64359
65036
  const score = `${result.passedCases}/${result.totalCases}`;
64360
- console.log(`${badge} ${chalk27.bold(definition.name)} ${chalk27.gray(`(${score} cases \xB7 ${rel})`)}`);
65037
+ const runRef = result.runId ? ` \xB7 run ${result.runId}` : "";
65038
+ console.log(
65039
+ `${badge} ${chalk27.bold(definition.name)} ${chalk27.gray(`(${score} cases \xB7 ${rel}${runRef})`)}`
65040
+ );
64361
65041
  for (const testCase of result.cases) {
64362
65042
  const caseBadge = testCase.passed ? chalk27.green(" \u2713") : chalk27.red(" \u2717");
64363
65043
  console.log(`${caseBadge} ${testCase.name}`);
@@ -66577,11 +67257,11 @@ async function promptLine(rl, question, defaultValue) {
66577
67257
  const answer = await new Promise((resolve12) => {
66578
67258
  rl.question(`${question}${hint}: `, resolve12);
66579
67259
  });
66580
- const trimmed = answer.trim();
66581
- if (!trimmed && defaultValue !== void 0) {
67260
+ const trimmed2 = answer.trim();
67261
+ if (!trimmed2 && defaultValue !== void 0) {
66582
67262
  return defaultValue;
66583
67263
  }
66584
- return trimmed;
67264
+ return trimmed2;
66585
67265
  }
66586
67266
  async function promptConfirm2(rl, message, defaultYes = false) {
66587
67267
  const hint = defaultYes ? chalk30.dim(" (Y/n)") : chalk30.dim(" (y/N)");
@@ -68366,9 +69046,9 @@ async function readInput(input) {
68366
69046
  if (!input || input === "-") {
68367
69047
  return await readStdin();
68368
69048
  }
68369
- const trimmed = input.trim();
68370
- if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
68371
- return trimmed;
69049
+ const trimmed2 = input.trim();
69050
+ if (trimmed2.startsWith("{") || trimmed2.startsWith("[")) {
69051
+ return trimmed2;
68372
69052
  }
68373
69053
  return readFileSync18(input, "utf-8");
68374
69054
  }
@@ -70287,6 +70967,19 @@ function maybeNotifyAboutCliUpdate(args, options = {}) {
70287
70967
  void refreshCliUpdateMetadata(options);
70288
70968
  }
70289
70969
 
70970
+ // src/lib/global-option-forwarding.ts
70971
+ function registerGlobalOptionForwarding(program2) {
70972
+ program2.hook("preAction", (_thisCommand, actionCommand) => {
70973
+ const globals = program2.opts();
70974
+ if (typeof globals.apiUrl === "string" && globals.apiUrl.length > 0) {
70975
+ process.env.RUNTYPE_API_URL = globals.apiUrl;
70976
+ }
70977
+ if (globals.json === true && actionCommand.getOptionValue("json") === void 0) {
70978
+ actionCommand.setOptionValue("json", true);
70979
+ }
70980
+ });
70981
+ }
70982
+
70290
70983
  // src/index.ts
70291
70984
  loadEnv();
70292
70985
  setCliTitle();
@@ -70325,6 +71018,7 @@ program.addCommand(validateProductCommand);
70325
71018
  program.addCommand(deployCommand);
70326
71019
  program.addCommand(skillsCommand);
70327
71020
  program.addCommand(appsCommand);
71021
+ registerGlobalOptionForwarding(program);
70328
71022
  program.exitOverride();
70329
71023
  async function run() {
70330
71024
  const userArgs = process.argv.slice(2);
@@ -70347,7 +71041,9 @@ run().catch((error51) => {
70347
71041
  } else if (commanderError.code === "commander.unknownOption") {
70348
71042
  console.error(chalk42.red(`Error: ${commanderError.message}`));
70349
71043
  process.exit(1);
70350
- } else if (commanderError.code === "commander.help" || commanderError.code === "commander.version") {
71044
+ } else if (commanderError.code === "commander.unknownCommand") {
71045
+ process.exit(1);
71046
+ } else if (commanderError.code === "commander.help" || commanderError.code === "commander.helpDisplayed" || commanderError.code === "commander.version") {
70351
71047
  process.exit(0);
70352
71048
  } else {
70353
71049
  console.error(chalk42.red("An unexpected error occurred:"));