@kyo-so/cli 0.15.2 → 0.16.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.
package/dist/index.js CHANGED
@@ -184081,6 +184081,7 @@ function agentConfigLeafPaths(agent) {
184081
184081
  `agents.${agent}.effort`,
184082
184082
  `agents.${agent}.role`,
184083
184083
  `agents.${agent}.timeoutMs`,
184084
+ `agents.${agent}.timeoutS`,
184084
184085
  `agents.${agent}.env`,
184085
184086
  `agents.${agent}.auth.mode`,
184086
184087
  `agents.${agent}.auth.preferExistingLogin`,
@@ -184089,7 +184090,7 @@ function agentConfigLeafPaths(agent) {
184089
184090
  `agents.${agent}.auth.envWhitelist`
184090
184091
  ];
184091
184092
  if (agent === "codex") {
184092
- paths.push("agents.codex.provider", "agents.codex.allowProjectProvider", "agents.codex.openRouter.streamIdleTimeoutMs", "agents.codex.openRouter.streamMaxRetries", "agents.codex.openRouter.requestMaxRetries");
184093
+ paths.push("agents.codex.provider", "agents.codex.allowProjectProvider", "agents.codex.openRouter.streamIdleTimeoutMs", "agents.codex.openRouter.streamIdleTimeoutS", "agents.codex.openRouter.streamMaxRetries", "agents.codex.openRouter.requestMaxRetries");
184093
184094
  }
184094
184095
  return paths;
184095
184096
  }
@@ -184126,12 +184127,15 @@ var kyosoConfigKnownLeafPaths = [
184126
184127
  "judge.mode",
184127
184128
  "judge.provider",
184128
184129
  "judge.timeoutMs",
184130
+ "judge.timeoutS",
184129
184131
  "verification.enabled",
184130
184132
  "verification.maxFindings",
184131
184133
  "verification.timeoutMs",
184134
+ "verification.timeoutS",
184132
184135
  "verification.allowDemotion",
184133
184136
  "reviewBudget.maxModelCalls",
184134
184137
  "reviewBudget.maxTotalWallTimeMs",
184138
+ "reviewBudget.maxTotalWallTimeS",
184135
184139
  "reviewBudget.warnAgentOutputBytes",
184136
184140
  "reviewBudget.maxAgentOutputBytes",
184137
184141
  "reviewBudget.maxFindingsPerAgent",
@@ -184203,7 +184207,7 @@ var defaultConfig = {
184203
184207
  enabled: true,
184204
184208
  type: "acp",
184205
184209
  command: "npx",
184206
- args: ["-y", "@agentclientprotocol/claude-agent-acp@0.61.0"],
184210
+ args: ["-y", "@agentclientprotocol/claude-agent-acp@0.62.0"],
184207
184211
  role: "architecture_security_reviewer",
184208
184212
  timeoutMs: DEFAULT_AGENT_TIMEOUT_MS,
184209
184213
  env: {
@@ -184321,22 +184325,27 @@ var kyosoConfigOverridePaths = [
184321
184325
  "agents.codex.model",
184322
184326
  "agents.codex.provider",
184323
184327
  "agents.codex.openRouter.streamIdleTimeoutMs",
184328
+ "agents.codex.openRouter.streamIdleTimeoutS",
184324
184329
  "agents.codex.openRouter.streamMaxRetries",
184325
184330
  "agents.codex.openRouter.requestMaxRetries",
184326
184331
  "agents.codex.effort",
184327
184332
  "agents.codex.role",
184328
184333
  "agents.codex.timeoutMs",
184334
+ "agents.codex.timeoutS",
184329
184335
  "agents.claude.enabled",
184330
184336
  "agents.claude.model",
184331
184337
  "agents.claude.effort",
184332
184338
  "agents.claude.role",
184333
184339
  "agents.claude.timeoutMs",
184340
+ "agents.claude.timeoutS",
184334
184341
  "verification.enabled",
184335
184342
  "verification.maxFindings",
184336
184343
  "verification.timeoutMs",
184344
+ "verification.timeoutS",
184337
184345
  "judge.mode",
184338
184346
  "judge.provider",
184339
- "judge.timeoutMs"
184347
+ "judge.timeoutMs",
184348
+ "judge.timeoutS"
184340
184349
  ];
184341
184350
  var CONFIG_OVERRIDE_PATHS = new Set(kyosoConfigOverridePaths);
184342
184351
  function isAllowedConfigOverridePath(path) {
@@ -185573,6 +185582,146 @@ function isMissingPathError(error51) {
185573
185582
  return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
185574
185583
  }
185575
185584
 
185585
+ // src/utils/timeUnits.ts
185586
+ class TimeUnitValidationError extends Error {
185587
+ field;
185588
+ constructor(field, message) {
185589
+ super(`${field} ${message}`);
185590
+ this.name = "TimeUnitValidationError";
185591
+ this.field = field;
185592
+ }
185593
+ }
185594
+ function validateMilliseconds(value, field, constraints = {}) {
185595
+ assertFiniteNumber(value, field);
185596
+ if (!Number.isSafeInteger(value)) {
185597
+ throw new TimeUnitValidationError(field, "must be a safe integer number of milliseconds.");
185598
+ }
185599
+ assertMinimumMilliseconds(value, field, constraints);
185600
+ return value;
185601
+ }
185602
+ function secondsToMilliseconds(value, field, constraints = {}) {
185603
+ assertFiniteNumber(value, field);
185604
+ const milliseconds = value * 1000;
185605
+ if (!Number.isSafeInteger(milliseconds)) {
185606
+ throw new TimeUnitValidationError(field, "must convert to a safe integer number of milliseconds.");
185607
+ }
185608
+ assertMinimumMilliseconds(milliseconds, field, constraints);
185609
+ return milliseconds;
185610
+ }
185611
+ function resolveTimeUnitPair(input, fields, constraints = {}) {
185612
+ const hasMilliseconds = input.milliseconds !== undefined;
185613
+ const hasSeconds = input.seconds !== undefined;
185614
+ if (!hasMilliseconds && !hasSeconds)
185615
+ return;
185616
+ const milliseconds = hasMilliseconds ? validateMilliseconds(input.milliseconds, fields.milliseconds, constraints) : undefined;
185617
+ const secondsMilliseconds = hasSeconds ? secondsToMilliseconds(input.seconds, fields.seconds, constraints) : undefined;
185618
+ if (secondsMilliseconds !== undefined) {
185619
+ return {
185620
+ milliseconds: secondsMilliseconds,
185621
+ source: "seconds",
185622
+ sourceField: fields.seconds
185623
+ };
185624
+ }
185625
+ return {
185626
+ milliseconds,
185627
+ source: "milliseconds",
185628
+ sourceField: fields.milliseconds
185629
+ };
185630
+ }
185631
+ function assertFiniteNumber(value, field) {
185632
+ if (typeof value !== "number" || !Number.isFinite(value)) {
185633
+ throw new TimeUnitValidationError(field, "must be a finite number.");
185634
+ }
185635
+ }
185636
+ function assertMinimumMilliseconds(value, field, constraints) {
185637
+ if (constraints.allowZero && value === 0)
185638
+ return;
185639
+ const minimumMilliseconds = constraints.minimumMilliseconds ?? 1;
185640
+ if (value < minimumMilliseconds) {
185641
+ throw new TimeUnitValidationError(field, `must be at least ${minimumMilliseconds} milliseconds.`);
185642
+ }
185643
+ }
185644
+
185645
+ // src/config/timeUnits.ts
185646
+ var configTimeUnitPairs = [
185647
+ {
185648
+ parentPath: ["agents", "codex"],
185649
+ millisecondsKey: "timeoutMs",
185650
+ secondsKey: "timeoutS"
185651
+ },
185652
+ {
185653
+ parentPath: ["agents", "claude"],
185654
+ millisecondsKey: "timeoutMs",
185655
+ secondsKey: "timeoutS"
185656
+ },
185657
+ {
185658
+ parentPath: ["agents", "codex", "openRouter"],
185659
+ millisecondsKey: "streamIdleTimeoutMs",
185660
+ secondsKey: "streamIdleTimeoutS",
185661
+ constraints: { minimumMilliseconds: 1000 }
185662
+ },
185663
+ {
185664
+ parentPath: ["judge"],
185665
+ millisecondsKey: "timeoutMs",
185666
+ secondsKey: "timeoutS"
185667
+ },
185668
+ {
185669
+ parentPath: ["verification"],
185670
+ millisecondsKey: "timeoutMs",
185671
+ secondsKey: "timeoutS"
185672
+ },
185673
+ {
185674
+ parentPath: ["reviewBudget"],
185675
+ millisecondsKey: "maxTotalWallTimeMs",
185676
+ secondsKey: "maxTotalWallTimeS"
185677
+ }
185678
+ ];
185679
+ function normalizeConfigTimeUnits(input) {
185680
+ let normalized = input;
185681
+ for (const pair of configTimeUnitPairs) {
185682
+ normalized = updateRecordAtPath(normalized, pair.parentPath, (parent) => normalizePair(parent, pair));
185683
+ }
185684
+ return normalized;
185685
+ }
185686
+ function normalizePair(parent, pair) {
185687
+ const hasSecondsProperty = Object.hasOwn(parent, pair.secondsKey);
185688
+ if (!hasSecondsProperty)
185689
+ return parent;
185690
+ const normalized = { ...parent };
185691
+ if (parent[pair.secondsKey] === undefined) {
185692
+ delete normalized[pair.secondsKey];
185693
+ return normalized;
185694
+ }
185695
+ const parentPath = pair.parentPath.join(".");
185696
+ const resolved = resolveTimeUnitPair({
185697
+ milliseconds: parent[pair.millisecondsKey],
185698
+ seconds: parent[pair.secondsKey]
185699
+ }, {
185700
+ milliseconds: `${parentPath}.${pair.millisecondsKey}`,
185701
+ seconds: `${parentPath}.${pair.secondsKey}`
185702
+ }, pair.constraints);
185703
+ if (resolved)
185704
+ normalized[pair.millisecondsKey] = resolved.milliseconds;
185705
+ delete normalized[pair.secondsKey];
185706
+ return normalized;
185707
+ }
185708
+ function updateRecordAtPath(input, path, update) {
185709
+ if (path.length === 0) {
185710
+ return isRecord3(input) ? update(input) : input;
185711
+ }
185712
+ if (!isRecord3(input))
185713
+ return input;
185714
+ const [key, ...rest] = path;
185715
+ if (key === undefined)
185716
+ return input;
185717
+ const current = input[key];
185718
+ const updated = updateRecordAtPath(current, rest, update);
185719
+ return updated === current ? input : { ...input, [key]: updated };
185720
+ }
185721
+ function isRecord3(value) {
185722
+ return typeof value === "object" && value !== null && !Array.isArray(value);
185723
+ }
185724
+
185576
185725
  // src/config/loadConfig.ts
185577
185726
  var configValidationContexts = new WeakMap;
185578
185727
  class ProjectOpenRouterAuthorizationError extends Error {
@@ -185608,7 +185757,7 @@ async function loadConfig(options = {}) {
185608
185757
  let configTrustStatus = options.ignoreConfig ? "ignored" : "not_found";
185609
185758
  if (!options.ignoreConfig) {
185610
185759
  if (await exists(globalConfigPath)) {
185611
- const globalConfig2 = await loadTomlConfigFile(globalConfigPath);
185760
+ const globalConfig2 = normalizeConfigTimeUnits(await loadTomlConfigFile(globalConfigPath));
185612
185761
  validateExplicitReviewBudgetThresholds(globalConfig2, defaultConfig);
185613
185762
  const globalConfigWarnings = collectGlobalConfigWarnings(globalConfigPath, globalConfig2);
185614
185763
  const securitySensitiveWarnings = globalConfigWarnings.filter((warning) => warning.startsWith("security-sensitive unknown settings "));
@@ -185735,7 +185884,7 @@ async function loadProjectConfig(input) {
185735
185884
  const { canonicalDirectory, canonicalPath, requestedPath } = input.projectConfig;
185736
185885
  const extension = extname2(requestedPath);
185737
185886
  if (extension === ".toml") {
185738
- const projectTomlConfig = await loadTomlConfigFile(canonicalPath);
185887
+ const projectTomlConfig = normalizeConfigTimeUnits(await loadTomlConfigFile(canonicalPath));
185739
185888
  const projectChangesOpenRouterPolicy = projectConfigChangesOpenRouterPolicy(projectTomlConfig, input.baseConfig);
185740
185889
  await assertProjectOpenRouterAuthorization({
185741
185890
  projectConfig: projectTomlConfig,
@@ -185781,7 +185930,7 @@ function configSelectsOpenRouter(config2) {
185781
185930
  return flattenLeaves(config2).some((leaf) => leaf.path.join(".") === "agents.codex.provider" && leaf.value === CODEX_OPENROUTER_PROVIDER);
185782
185931
  }
185783
185932
  function applyProjectCodexProviderReset(baseConfig, projectConfig, mergedConfig) {
185784
- if (!projectConfigSelectsDefaultProvider(projectConfig) || !configSelectsOpenRouter(baseConfig) || !isRecord3(mergedConfig) || !isRecord3(mergedConfig.agents) || !isRecord3(mergedConfig.agents.codex)) {
185933
+ if (!projectConfigSelectsDefaultProvider(projectConfig) || !configSelectsOpenRouter(baseConfig) || !isRecord4(mergedConfig) || !isRecord4(mergedConfig.agents) || !isRecord4(mergedConfig.agents.codex)) {
185785
185934
  return mergedConfig;
185786
185935
  }
185787
185936
  const shouldClearInheritedModel = !projectConfigSuppliesCodexModel(projectConfig);
@@ -185813,13 +185962,13 @@ async function assertProjectOpenRouterAuthorization(input) {
185813
185962
  });
185814
185963
  }
185815
185964
  async function projectProviderIsAuthorized(config2, projectDirectory) {
185816
- if (!isRecord3(config2))
185965
+ if (!isRecord4(config2))
185817
185966
  return false;
185818
185967
  const agents = config2.agents;
185819
- if (!isRecord3(agents))
185968
+ if (!isRecord4(agents))
185820
185969
  return false;
185821
185970
  const codex = agents.codex;
185822
- if (!isRecord3(codex) || !Array.isArray(codex.allowProjectProvider)) {
185971
+ if (!isRecord4(codex) || !Array.isArray(codex.allowProjectProvider)) {
185823
185972
  return false;
185824
185973
  }
185825
185974
  for (const directory of codex.allowProjectProvider) {
@@ -185862,7 +186011,7 @@ async function loadProjectTsConfig(input) {
185862
186011
  options: input.options
185863
186012
  });
185864
186013
  if (trustDecision.execute) {
185865
- const userConfig = await loadUserConfig(canonicalPath, source);
186014
+ const userConfig = normalizeConfigTimeUnits(await loadUserConfig(canonicalPath, source));
185866
186015
  validateExplicitReviewBudgetThresholds(userConfig, input.baseConfig);
185867
186016
  const projectChangesOpenRouterPolicy = projectConfigChangesOpenRouterPolicy(userConfig, input.baseConfig);
185868
186017
  await assertProjectOpenRouterAuthorization({
@@ -185949,7 +186098,7 @@ async function promptForConfigTrust(configPath, configHash) {
185949
186098
  }
185950
186099
  }
185951
186100
  function deepMerge2(base, override) {
185952
- if (!isRecord3(base) || !isRecord3(override))
186101
+ if (!isRecord4(base) || !isRecord4(override))
185953
186102
  return override ?? base;
185954
186103
  const result = { ...base };
185955
186104
  for (const [key, value] of Object.entries(override)) {
@@ -185969,12 +186118,12 @@ function validateExplicitReviewBudgetThresholds(config2, baseConfig) {
185969
186118
  }
185970
186119
  }
185971
186120
  function readRecord(value, key) {
185972
- if (!isRecord3(value))
186121
+ if (!isRecord4(value))
185973
186122
  return;
185974
186123
  const nested = value[key];
185975
- return isRecord3(nested) ? nested : undefined;
186124
+ return isRecord4(nested) ? nested : undefined;
185976
186125
  }
185977
- function isRecord3(value) {
186126
+ function isRecord4(value) {
185978
186127
  return typeof value === "object" && value !== null && !Array.isArray(value);
185979
186128
  }
185980
186129
  async function exists(path) {
@@ -185989,9 +186138,14 @@ async function exists(path) {
185989
186138
  // src/config/configOverrides.ts
185990
186139
  var NUMBER_VALUE = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
185991
186140
  var UNSET_NUMBER_OVERRIDE_PATHS = new Set([
186141
+ "agents.codex.timeoutS",
186142
+ "agents.claude.timeoutS",
185992
186143
  "agents.codex.openRouter.streamIdleTimeoutMs",
186144
+ "agents.codex.openRouter.streamIdleTimeoutS",
185993
186145
  "agents.codex.openRouter.streamMaxRetries",
185994
- "agents.codex.openRouter.requestMaxRetries"
186146
+ "agents.codex.openRouter.requestMaxRetries",
186147
+ "judge.timeoutS",
186148
+ "verification.timeoutS"
185995
186149
  ]);
185996
186150
  function applyConfigOverrides(config2, assignments) {
185997
186151
  if (assignments.length === 0)
@@ -186002,9 +186156,21 @@ function applyConfigOverrides(config2, assignments) {
186002
186156
  for (const override of overrides) {
186003
186157
  writePath2(overridden, override.path, parseConfigOverrideValue(override.value, readPath2(baseConfig, override.path), override.path));
186004
186158
  }
186005
- clearInheritedOpenRouterConfigForProviderReset(baseConfig, overridden, overrides);
186006
- assertOpenRouterProviderOverrideIncludesModel(baseConfig, overridden, overrides);
186007
- const parsed = kyosoConfigSchema.safeParse(overridden);
186159
+ let normalized;
186160
+ try {
186161
+ normalized = normalizeConfigTimeUnits(overridden);
186162
+ } catch (error51) {
186163
+ if (!(error51 instanceof TimeUnitValidationError))
186164
+ throw error51;
186165
+ const assignment2 = findAssignmentForPath(overrides, error51.field);
186166
+ if (!assignment2) {
186167
+ throw new Error(`Invalid config time value: ${error51.message}`);
186168
+ }
186169
+ throw new Error(`Invalid --set value ${JSON.stringify(assignment2)}: ${error51.message}`);
186170
+ }
186171
+ clearInheritedOpenRouterConfigForProviderReset(baseConfig, normalized, overrides);
186172
+ assertOpenRouterProviderOverrideIncludesModel(baseConfig, normalized, overrides);
186173
+ const parsed = kyosoConfigSchema.safeParse(normalized);
186008
186174
  if (parsed.success)
186009
186175
  return parsed.data;
186010
186176
  const issue2 = parsed.error.issues[0];
@@ -186034,7 +186200,7 @@ function clearInheritedOpenRouterConfigForProviderReset(baseConfig, overridden,
186034
186200
  return;
186035
186201
  }
186036
186202
  const codex = readPath2(overridden, ["agents", "codex"]);
186037
- if (!isRecord4(codex))
186203
+ if (!isRecord5(codex))
186038
186204
  return;
186039
186205
  if (!suppliesModel)
186040
186206
  delete codex.model;
@@ -186083,7 +186249,7 @@ function parseConfigOverrideValue(value, currentValue, path) {
186083
186249
  function readPath2(target, path) {
186084
186250
  let current = target;
186085
186251
  for (const key of path) {
186086
- if (!isRecord4(current))
186252
+ if (!isRecord5(current))
186087
186253
  return;
186088
186254
  current = current[key];
186089
186255
  }
@@ -186093,7 +186259,7 @@ function writePath2(target, path, value) {
186093
186259
  let current = target;
186094
186260
  for (const key of path.slice(0, -1)) {
186095
186261
  const child = current[key];
186096
- if (!isRecord4(child)) {
186262
+ if (!isRecord5(child)) {
186097
186263
  throw new Error(`Cannot apply --set key ${JSON.stringify(path.join("."))}.`);
186098
186264
  }
186099
186265
  current = child;
@@ -186102,7 +186268,7 @@ function writePath2(target, path, value) {
186102
186268
  if (leaf)
186103
186269
  current[leaf] = value;
186104
186270
  }
186105
- function isRecord4(value) {
186271
+ function isRecord5(value) {
186106
186272
  return typeof value === "object" && value !== null && !Array.isArray(value);
186107
186273
  }
186108
186274
 
@@ -188510,17 +188676,17 @@ function isResponseMessage(value) {
188510
188676
  function isNotificationMessage(value) {
188511
188677
  return isJsonRpcEnvelope(value) && !("id" in value) && typeof value["method"] === "string";
188512
188678
  }
188513
- function isRecord5(value) {
188679
+ function isRecord6(value) {
188514
188680
  return typeof value === "object" && value !== null;
188515
188681
  }
188516
188682
  function isJsonRpcEnvelope(value) {
188517
- return isRecord5(value) && value["jsonrpc"] === "2.0";
188683
+ return isRecord6(value) && value["jsonrpc"] === "2.0";
188518
188684
  }
188519
188685
  function isJsonRpcId(value) {
188520
188686
  return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
188521
188687
  }
188522
188688
  function isResponseShapedMessage(value) {
188523
- return isRecord5(value) && !("method" in value) && (("id" in value) || ("result" in value) || ("error" in value));
188689
+ return isRecord6(value) && !("method" in value) && (("id" in value) || ("result" in value) || ("error" in value));
188524
188690
  }
188525
188691
  function isResponseBatch(batch) {
188526
188692
  let hasValidCall = false;
@@ -188530,7 +188696,7 @@ function isResponseBatch(batch) {
188530
188696
  for (const entry of batch) {
188531
188697
  hasValidCall ||= isRequestMessage(entry) || isNotificationMessage(entry);
188532
188698
  hasValidResponse ||= isResponseMessage(entry);
188533
- if (!isRecord5(entry)) {
188699
+ if (!isRecord6(entry)) {
188534
188700
  continue;
188535
188701
  }
188536
188702
  hasCallShape ||= "method" in entry;
@@ -188545,13 +188711,13 @@ function isResponseBatch(batch) {
188545
188711
  return hasResponseShape && !hasCallShape;
188546
188712
  }
188547
188713
  function cancelRequestId(params) {
188548
- if (!isRecord5(params) || !isJsonRpcId(params["requestId"])) {
188714
+ if (!isRecord6(params) || !isJsonRpcId(params["requestId"])) {
188549
188715
  return;
188550
188716
  }
188551
188717
  return params["requestId"];
188552
188718
  }
188553
188719
  function isErrorResponse(value) {
188554
- return isRecord5(value) && typeof value["code"] === "number" && Number.isInteger(value["code"]) && typeof value["message"] === "string";
188720
+ return isRecord6(value) && typeof value["code"] === "number" && Number.isInteger(value["code"]) && typeof value["message"] === "string";
188555
188721
  }
188556
188722
  var Handled = {
188557
188723
  yes() {
@@ -188946,7 +189112,7 @@ class Connection {
188946
189112
  this.receiveBatch(message);
188947
189113
  return;
188948
189114
  }
188949
- if (!isRecord5(message)) {
189115
+ if (!isRecord6(message)) {
188950
189116
  console.error("Invalid message", { message });
188951
189117
  return;
188952
189118
  }
@@ -189007,7 +189173,7 @@ class Connection {
189007
189173
  if (this.abortController.signal.aborted) {
189008
189174
  return Promise.resolve();
189009
189175
  }
189010
- if (!isRecord5(message)) {
189176
+ if (!isRecord6(message)) {
189011
189177
  console.error("Invalid message", { message });
189012
189178
  return Promise.resolve();
189013
189179
  }
@@ -189314,7 +189480,7 @@ function ndJsonStream(output, input) {
189314
189480
  if (trimmedLine) {
189315
189481
  try {
189316
189482
  const message = JSON.parse(trimmedLine);
189317
- if (isRecord5(message) || Array.isArray(message)) {
189483
+ if (isRecord6(message) || Array.isArray(message)) {
189318
189484
  controller.enqueue(message);
189319
189485
  } else {
189320
189486
  console.warn("Skipping JSON line that is not an object:", trimmedLine);
@@ -190264,7 +190430,7 @@ function createModelExecutionIdentity(input) {
190264
190430
  };
190265
190431
  }
190266
190432
  function normalizeModelExecutionIdentity(value) {
190267
- if (!isRecord6(value) || !isModelProviderRoute(value.providerRoute)) {
190433
+ if (!isRecord7(value) || !isModelProviderRoute(value.providerRoute)) {
190268
190434
  return;
190269
190435
  }
190270
190436
  return createModelExecutionIdentity({
@@ -190286,7 +190452,7 @@ function sanitizeIdentityValue(value) {
190286
190452
  function isModelProviderRoute(value) {
190287
190453
  return typeof value === "string" && MODEL_PROVIDER_ROUTES.has(value);
190288
190454
  }
190289
- function isRecord6(value) {
190455
+ function isRecord7(value) {
190290
190456
  return value !== null && typeof value === "object" && !Array.isArray(value);
190291
190457
  }
190292
190458
 
@@ -190300,7 +190466,7 @@ var TOKEN_USAGE_KEYS = [
190300
190466
  "cachedWriteTokens"
190301
190467
  ];
190302
190468
  function normalizeModelTokenUsage(usage) {
190303
- if (!isRecord7(usage))
190469
+ if (!isRecord8(usage))
190304
190470
  return;
190305
190471
  const normalized = {};
190306
190472
  for (const key of TOKEN_USAGE_KEYS) {
@@ -190313,7 +190479,7 @@ function normalizeModelTokenUsage(usage) {
190313
190479
  function isTokenCount(value) {
190314
190480
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
190315
190481
  }
190316
- function isRecord7(value) {
190482
+ function isRecord8(value) {
190317
190483
  return typeof value === "object" && value !== null && !Array.isArray(value);
190318
190484
  }
190319
190485
 
@@ -190704,12 +190870,12 @@ class AgentOutputAccumulator {
190704
190870
  var RETRY_ATTEMPT_PATTERN = /Reconnecting\.\.\.\s*(\d+)\/(\d+)/;
190705
190871
  var DISPLAY_MESSAGE_FIELDS = ["title", "text", "message", "description"];
190706
190872
  function parseCodexRetryUpdate(update) {
190707
- if (!isRecord8(update) || update.sessionUpdate !== "session_info_update") {
190873
+ if (!isRecord9(update) || update.sessionUpdate !== "session_info_update") {
190708
190874
  return;
190709
190875
  }
190710
- const meta3 = isRecord8(update._meta) ? update._meta : undefined;
190711
- const codex = meta3 && isRecord8(meta3.codex) ? meta3.codex : undefined;
190712
- const error51 = codex && isRecord8(codex.error) ? codex.error : undefined;
190876
+ const meta3 = isRecord9(update._meta) ? update._meta : undefined;
190877
+ const codex = meta3 && isRecord9(meta3.codex) ? meta3.codex : undefined;
190878
+ const error51 = codex && isRecord9(codex.error) ? codex.error : undefined;
190713
190879
  if (error51?.willRetry !== true)
190714
190880
  return;
190715
190881
  const rawMessage = typeof error51.message === "string" ? error51.message : findDisplayMessage(update) ?? "model stream retry";
@@ -190729,7 +190895,7 @@ function findDisplayMessage(update) {
190729
190895
  }
190730
190896
  return;
190731
190897
  }
190732
- function isRecord8(value) {
190898
+ function isRecord9(value) {
190733
190899
  return value !== null && typeof value === "object" && !Array.isArray(value);
190734
190900
  }
190735
190901
 
@@ -191214,7 +191380,7 @@ function isSeverity(value) {
191214
191380
  return typeof value === "string" && severities.includes(value);
191215
191381
  }
191216
191382
  function normalizeCisaSecureByDesign(value) {
191217
- if (!isRecord9(value))
191383
+ if (!isRecord10(value))
191218
191384
  return;
191219
191385
  const normalized = {};
191220
191386
  const customerSecurityOutcomes = normalizeGateStatus(value.customerSecurityOutcomes);
@@ -191255,7 +191421,7 @@ function isEvidenceQuality(value) {
191255
191421
  return typeof value === "string" && evidenceQualities.includes(value);
191256
191422
  }
191257
191423
  function isStrictAgentOpinion(value) {
191258
- if (!isRecord9(value) || !hasOnlyKeys(value, STRICT_ROOT_KEYS))
191424
+ if (!isRecord10(value) || !hasOnlyKeys(value, STRICT_ROOT_KEYS))
191259
191425
  return false;
191260
191426
  if (typeof value.summary !== "string")
191261
191427
  return false;
@@ -191265,7 +191431,7 @@ function isStrictAgentOpinion(value) {
191265
191431
  return value.cisaSecureByDesign === undefined || isStrictCisaSecureByDesign(value.cisaSecureByDesign);
191266
191432
  }
191267
191433
  function isStrictFinding(value) {
191268
- if (!isRecord9(value) || !hasOnlyKeys(value, STRICT_FINDING_KEYS)) {
191434
+ if (!isRecord10(value) || !hasOnlyKeys(value, STRICT_FINDING_KEYS)) {
191269
191435
  return false;
191270
191436
  }
191271
191437
  if (!isSeverity(value.severity) || !isCategory(value.category) || !isNonEmptyString(value.title) || !isNonEmptyString(value.evidence) || !isNonEmptyString(value.recommendation) || !isConfidence(value.confidence)) {
@@ -191277,11 +191443,11 @@ function isStrictFinding(value) {
191277
191443
  return true;
191278
191444
  }
191279
191445
  function isStrictFindingFiles(value) {
191280
- return Array.isArray(value) && value.every((item) => isRecord9(item) && hasOnlyKeys(item, STRICT_FILE_KEYS) && isNonEmptyString(item.path) && isOptionalLineNumber(item.lineStart) && isOptionalLineNumber(item.lineEnd));
191446
+ return Array.isArray(value) && value.every((item) => isRecord10(item) && hasOnlyKeys(item, STRICT_FILE_KEYS) && isNonEmptyString(item.path) && isOptionalLineNumber(item.lineStart) && isOptionalLineNumber(item.lineEnd));
191281
191447
  }
191282
191448
  function isStrictEvidenceRefs(value) {
191283
191449
  return Array.isArray(value) && value.length <= MAX_EVIDENCE_REFS2 && value.every((item) => {
191284
- if (!isRecord9(item) || !hasOnlyKeys(item, STRICT_EVIDENCE_REF_KEYS)) {
191450
+ if (!isRecord10(item) || !hasOnlyKeys(item, STRICT_EVIDENCE_REF_KEYS)) {
191285
191451
  return false;
191286
191452
  }
191287
191453
  if (item.kind !== "file" && item.kind !== "diff_hunk" && item.kind !== "plan_clause") {
@@ -191297,7 +191463,7 @@ function isStrictEvidenceRefs(value) {
191297
191463
  });
191298
191464
  }
191299
191465
  function isStrictCisaSecureByDesign(value) {
191300
- if (!isRecord9(value) || !hasOnlyKeys(value, STRICT_CISA_KEYS))
191466
+ if (!isRecord10(value) || !hasOnlyKeys(value, STRICT_CISA_KEYS))
191301
191467
  return false;
191302
191468
  for (const key of [
191303
191469
  "customerSecurityOutcomes",
@@ -191336,7 +191502,7 @@ function normalizeFindingFiles(value) {
191336
191502
  if (!Array.isArray(value))
191337
191503
  return;
191338
191504
  const files = value.flatMap((item) => {
191339
- if (!isRecord9(item) || typeof item.path !== "string" || item.path.trim().length === 0) {
191505
+ if (!isRecord10(item) || typeof item.path !== "string" || item.path.trim().length === 0) {
191340
191506
  return [];
191341
191507
  }
191342
191508
  const file2 = {
@@ -191356,7 +191522,7 @@ function normalizeEvidenceRefs2(value) {
191356
191522
  if (!Array.isArray(value))
191357
191523
  return;
191358
191524
  const references = value.slice(0, MAX_EVIDENCE_REFS2).flatMap((item) => {
191359
- if (!isRecord9(item) || item.kind !== "file" && item.kind !== "diff_hunk" && item.kind !== "plan_clause") {
191525
+ if (!isRecord10(item) || item.kind !== "file" && item.kind !== "diff_hunk" && item.kind !== "plan_clause") {
191360
191526
  return [];
191361
191527
  }
191362
191528
  const reference = { kind: item.kind };
@@ -191379,7 +191545,7 @@ function normalizeEvidenceRefs2(value) {
191379
191545
  function normalizeLineNumber(value) {
191380
191546
  return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= MAX_EVIDENCE_LINE2 ? value : undefined;
191381
191547
  }
191382
- function isRecord9(value) {
191548
+ function isRecord10(value) {
191383
191549
  return typeof value === "object" && value !== null && !Array.isArray(value);
191384
191550
  }
191385
191551
 
@@ -192004,7 +192170,7 @@ function normalizeUsage(usage) {
192004
192170
  return normalizeModelTokenUsage(usage);
192005
192171
  }
192006
192172
  function withReportedExecutionIdentity(identity, metadata) {
192007
- const record2 = isRecord10(metadata) ? metadata : {};
192173
+ const record2 = isRecord11(metadata) ? metadata : {};
192008
192174
  return createModelExecutionIdentity({
192009
192175
  providerRoute: identity.providerRoute,
192010
192176
  requestedModel: identity.requestedModel,
@@ -192013,9 +192179,9 @@ function withReportedExecutionIdentity(identity, metadata) {
192013
192179
  });
192014
192180
  }
192015
192181
  function readChunkMeta(update) {
192016
- const record2 = isRecord10(update) ? update : {};
192017
- const metadata = isRecord10(record2._meta) ? record2._meta : {};
192018
- const codex = isRecord10(metadata.codex) ? metadata.codex : {};
192182
+ const record2 = isRecord11(update) ? update : {};
192183
+ const metadata = isRecord11(record2._meta) ? record2._meta : {};
192184
+ const codex = isRecord11(metadata.codex) ? metadata.codex : {};
192019
192185
  const phase = codex.phase === "commentary" || codex.phase === "final_answer" ? codex.phase : "unknown";
192020
192186
  return {
192021
192187
  ...typeof record2.messageId === "string" ? { messageId: record2.messageId } : {},
@@ -192023,15 +192189,15 @@ function readChunkMeta(update) {
192023
192189
  };
192024
192190
  }
192025
192191
  function readCodexThreadStatus(update) {
192026
- const record2 = isRecord10(update) ? update : {};
192192
+ const record2 = isRecord11(update) ? update : {};
192027
192193
  if (record2.sessionUpdate !== "session_info_update")
192028
192194
  return;
192029
- const metadata = isRecord10(record2._meta) ? record2._meta : {};
192030
- const codex = isRecord10(metadata.codex) ? metadata.codex : {};
192031
- const threadStatus = isRecord10(codex.threadStatus) ? codex.threadStatus : {};
192195
+ const metadata = isRecord11(record2._meta) ? record2._meta : {};
192196
+ const codex = isRecord11(metadata.codex) ? metadata.codex : {};
192197
+ const threadStatus = isRecord11(codex.threadStatus) ? codex.threadStatus : {};
192032
192198
  return typeof threadStatus.type === "string" ? threadStatus.type : undefined;
192033
192199
  }
192034
- function isRecord10(value) {
192200
+ function isRecord11(value) {
192035
192201
  return value !== null && typeof value === "object" && !Array.isArray(value);
192036
192202
  }
192037
192203
  function resolveEffortConfigOption(agent, effort) {
@@ -193736,7 +193902,7 @@ function validateReviewContract(request) {
193736
193902
  const contract = request.reviewContract;
193737
193903
  if (contract === undefined)
193738
193904
  return;
193739
- if (!isRecord11(contract)) {
193905
+ if (!isRecord12(contract)) {
193740
193906
  throw new KyosoRequestError("reviewContract must be an object", "VALIDATION_ERROR");
193741
193907
  }
193742
193908
  const allowedKeys = new Set(["focus", "nonGoals", "acceptedRisks"]);
@@ -193753,7 +193919,7 @@ function validateReviewContract(request) {
193753
193919
  throw new KyosoRequestError("reviewContract.nonGoals must contain at most 20 non-empty strings of 500 characters or fewer", "VALIDATION_ERROR");
193754
193920
  }
193755
193921
  const acceptedRisks = contract.acceptedRisks;
193756
- if (acceptedRisks !== undefined && (!Array.isArray(acceptedRisks) || acceptedRisks.length > 20 || acceptedRisks.some((risk) => !isRecord11(risk) || typeof risk.findingFingerprint !== "string" || !/^sha256:[0-9a-f]{64}$/.test(risk.findingFingerprint) || typeof risk.rationale !== "string" || risk.rationale.trim().length === 0 || risk.rationale.length > 500))) {
193922
+ if (acceptedRisks !== undefined && (!Array.isArray(acceptedRisks) || acceptedRisks.length > 20 || acceptedRisks.some((risk) => !isRecord12(risk) || typeof risk.findingFingerprint !== "string" || !/^sha256:[0-9a-f]{64}$/.test(risk.findingFingerprint) || typeof risk.rationale !== "string" || risk.rationale.trim().length === 0 || risk.rationale.length > 500))) {
193757
193923
  throw new KyosoRequestError("reviewContract.acceptedRisks must contain valid finding fingerprints and bounded rationales", "VALIDATION_ERROR");
193758
193924
  }
193759
193925
  }
@@ -193765,13 +193931,13 @@ function validateSelectedFiles(request) {
193765
193931
  throw new KyosoRequestError("selectedFiles must be an array", "VALIDATION_ERROR");
193766
193932
  }
193767
193933
  for (const file2 of selectedFiles) {
193768
- if (!isRecord11(file2) || typeof file2.path !== "string" || file2.path.trim().length === 0 || typeof file2.content !== "string" || file2.language !== undefined && typeof file2.language !== "string" || file2.truncated !== undefined && typeof file2.truncated !== "boolean") {
193934
+ if (!isRecord12(file2) || typeof file2.path !== "string" || file2.path.trim().length === 0 || typeof file2.content !== "string" || file2.language !== undefined && typeof file2.language !== "string" || file2.truncated !== undefined && typeof file2.truncated !== "boolean") {
193769
193935
  throw new KyosoRequestError("selectedFiles entries require string path/content and valid optional metadata", "VALIDATION_ERROR");
193770
193936
  }
193771
193937
  normalizeRelativePath(file2.path);
193772
193938
  }
193773
193939
  }
193774
- function isRecord11(value) {
193940
+ function isRecord12(value) {
193775
193941
  return typeof value === "object" && value !== null && !Array.isArray(value);
193776
193942
  }
193777
193943
 
@@ -194032,7 +194198,7 @@ function parseJudgeOutput(text, fallbackSummaryText) {
194032
194198
  const parsed = JSON.parse(json2);
194033
194199
  const summaryText = typeof parsed.summaryText === "string" && parsed.summaryText.trim().length > 0 ? sanitizeText(parsed.summaryText) : fallbackSummaryText;
194034
194200
  const disagreementComments = Array.isArray(parsed.disagreementComments) ? parsed.disagreementComments.flatMap((item) => {
194035
- if (!isRecord12(item) || typeof item.topic !== "string" || typeof item.judgeComment !== "string") {
194201
+ if (!isRecord13(item) || typeof item.topic !== "string" || typeof item.judgeComment !== "string") {
194036
194202
  return [];
194037
194203
  }
194038
194204
  return [
@@ -194048,7 +194214,7 @@ function parseJudgeOutput(text, fallbackSummaryText) {
194048
194214
  return { summaryText, disagreementComments, analysis };
194049
194215
  }
194050
194216
  function parseAnalysis(value) {
194051
- if (!isRecord12(value))
194217
+ if (!isRecord13(value))
194052
194218
  return;
194053
194219
  if (!Array.isArray(value.blindSpots) || !Array.isArray(value.contradictions) || !Array.isArray(value.partialCoverage)) {
194054
194220
  return;
@@ -194056,7 +194222,7 @@ function parseAnalysis(value) {
194056
194222
  return {
194057
194223
  blindSpots: value.blindSpots.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => typeof item === "string" ? [sanitizeAnalysisText(item)] : []),
194058
194224
  contradictions: value.contradictions.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => {
194059
- if (!isRecord12(item) || typeof item.topic !== "string" || typeof item.detail !== "string") {
194225
+ if (!isRecord13(item) || typeof item.topic !== "string" || typeof item.detail !== "string") {
194060
194226
  return [];
194061
194227
  }
194062
194228
  return [
@@ -194067,7 +194233,7 @@ function parseAnalysis(value) {
194067
194233
  ];
194068
194234
  }),
194069
194235
  partialCoverage: value.partialCoverage.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => {
194070
- if (!isRecord12(item) || typeof item.note !== "string")
194236
+ if (!isRecord13(item) || typeof item.note !== "string")
194071
194237
  return [];
194072
194238
  const findingId = typeof item.findingId === "string" ? sanitizeAnalysisText(item.findingId) : undefined;
194073
194239
  return [
@@ -194114,7 +194280,7 @@ function extractFirstJsonObject2(text) {
194114
194280
  }
194115
194281
  return;
194116
194282
  }
194117
- function isRecord12(value) {
194283
+ function isRecord13(value) {
194118
194284
  return typeof value === "object" && value !== null && !Array.isArray(value);
194119
194285
  }
194120
194286
 
@@ -194659,11 +194825,11 @@ function canonicalJson(value) {
194659
194825
  function canonicalize(value) {
194660
194826
  if (Array.isArray(value))
194661
194827
  return value.map(canonicalize);
194662
- if (!isRecord13(value))
194828
+ if (!isRecord14(value))
194663
194829
  return value;
194664
194830
  return Object.fromEntries(Object.entries(value).filter(([, child]) => child !== undefined).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, child]) => [key, canonicalize(child)]));
194665
194831
  }
194666
- function isRecord13(value) {
194832
+ function isRecord14(value) {
194667
194833
  return typeof value === "object" && value !== null && !Array.isArray(value);
194668
194834
  }
194669
194835
 
@@ -194671,19 +194837,24 @@ function isRecord13(value) {
194671
194837
  var REVIEW_BUDGET_KEYS = new Set([
194672
194838
  "maxModelCalls",
194673
194839
  "maxTotalWallTimeMs",
194840
+ "maxTotalWallTimeS",
194674
194841
  "maxAgentOutputBytes",
194675
194842
  "maxFindingsPerAgent",
194676
194843
  "skipOptionalPhasesWhenTokenUsageUnknown"
194677
194844
  ]);
194678
194845
  var MODEL_CALL_KINDS = ["primary", "verifier", "judge"];
194679
194846
  function resolveReviewBudget(ceiling, requested) {
194680
- if (requested !== undefined && !isRecord14(requested)) {
194847
+ if (requested !== undefined && !isRecord15(requested)) {
194681
194848
  throw new KyosoRequestError("options.reviewBudget must be an object.", "REVIEW_BUDGET_INVALID");
194682
194849
  }
194850
+ const hasWallTimeSeconds = requested?.maxTotalWallTimeS !== undefined;
194683
194851
  for (const [key, value] of Object.entries(requested ?? {})) {
194684
194852
  if (!REVIEW_BUDGET_KEYS.has(key)) {
194685
194853
  throw new KyosoRequestError(`options.reviewBudget.${key} is not supported.`, "REVIEW_BUDGET_INVALID");
194686
194854
  }
194855
+ if (key === "maxTotalWallTimeS" || key === "maxTotalWallTimeMs" && hasWallTimeSeconds) {
194856
+ continue;
194857
+ }
194687
194858
  if (key === "skipOptionalPhasesWhenTokenUsageUnknown") {
194688
194859
  if (typeof value !== "boolean") {
194689
194860
  throw new KyosoRequestError(`options.reviewBudget.${key} must be a boolean.`, "REVIEW_BUDGET_INVALID");
@@ -194694,12 +194865,26 @@ function resolveReviewBudget(ceiling, requested) {
194694
194865
  throw new KyosoRequestError(`options.reviewBudget.${key} must be a positive integer.`, "REVIEW_BUDGET_INVALID");
194695
194866
  }
194696
194867
  }
194697
- const numericKeys = [
194698
- "maxModelCalls",
194699
- "maxTotalWallTimeMs",
194700
- "maxAgentOutputBytes",
194701
- "maxFindingsPerAgent"
194702
- ];
194868
+ let requestedWallTime;
194869
+ if (hasWallTimeSeconds) {
194870
+ try {
194871
+ requestedWallTime = resolveTimeUnitPair({
194872
+ milliseconds: requested?.maxTotalWallTimeMs,
194873
+ seconds: requested?.maxTotalWallTimeS
194874
+ }, {
194875
+ milliseconds: "options.reviewBudget.maxTotalWallTimeMs",
194876
+ seconds: "options.reviewBudget.maxTotalWallTimeS"
194877
+ });
194878
+ } catch (error51) {
194879
+ if (error51 instanceof TimeUnitValidationError) {
194880
+ throw new KyosoRequestError(error51.message, "REVIEW_BUDGET_INVALID");
194881
+ }
194882
+ throw error51;
194883
+ }
194884
+ }
194885
+ const numericKeys = ["maxModelCalls", "maxAgentOutputBytes", "maxFindingsPerAgent"];
194886
+ if (!hasWallTimeSeconds)
194887
+ numericKeys.push("maxTotalWallTimeMs");
194703
194888
  for (const key of numericKeys) {
194704
194889
  const value = requested?.[key];
194705
194890
  if (value === undefined)
@@ -194708,13 +194893,19 @@ function resolveReviewBudget(ceiling, requested) {
194708
194893
  throw new KyosoRequestError(`options.reviewBudget.${key} cannot exceed the user-global ceiling.`, "REVIEW_BUDGET_EXCEEDS_CEILING");
194709
194894
  }
194710
194895
  }
194896
+ if (hasWallTimeSeconds && requested?.maxTotalWallTimeMs !== undefined && requested.maxTotalWallTimeMs > ceiling.maxTotalWallTimeMs) {
194897
+ throw new KyosoRequestError("options.reviewBudget.maxTotalWallTimeMs cannot exceed the user-global ceiling.", "REVIEW_BUDGET_EXCEEDS_CEILING");
194898
+ }
194899
+ if (requestedWallTime && requestedWallTime.milliseconds > ceiling.maxTotalWallTimeMs) {
194900
+ throw new KyosoRequestError(`${requestedWallTime.sourceField} cannot exceed the user-global ceiling.`, "REVIEW_BUDGET_EXCEEDS_CEILING");
194901
+ }
194711
194902
  if (ceiling.skipOptionalPhasesWhenTokenUsageUnknown && requested?.skipOptionalPhasesWhenTokenUsageUnknown === false) {
194712
194903
  throw new KyosoRequestError("options.reviewBudget.skipOptionalPhasesWhenTokenUsageUnknown cannot relax the user-global ceiling.", "REVIEW_BUDGET_EXCEEDS_CEILING");
194713
194904
  }
194714
194905
  const maxAgentOutputBytes = requested?.maxAgentOutputBytes ?? ceiling.maxAgentOutputBytes;
194715
194906
  return {
194716
194907
  maxModelCalls: requested?.maxModelCalls ?? ceiling.maxModelCalls,
194717
- maxTotalWallTimeMs: requested?.maxTotalWallTimeMs ?? ceiling.maxTotalWallTimeMs,
194908
+ maxTotalWallTimeMs: requestedWallTime?.milliseconds ?? requested?.maxTotalWallTimeMs ?? ceiling.maxTotalWallTimeMs,
194718
194909
  warnAgentOutputBytes: ceiling.warnAgentOutputBytes,
194719
194910
  maxAgentOutputBytes,
194720
194911
  maxFindingsPerAgent: requested?.maxFindingsPerAgent ?? ceiling.maxFindingsPerAgent,
@@ -195031,10 +195222,63 @@ function emptyReviewModelCallPlan() {
195031
195222
  ceilingEffects: []
195032
195223
  };
195033
195224
  }
195034
- function isRecord14(value) {
195225
+ function isRecord15(value) {
195035
195226
  return typeof value === "object" && value !== null && !Array.isArray(value);
195036
195227
  }
195037
195228
 
195229
+ // src/core/requestTimeUnits.ts
195230
+ function normalizeRequestTimeUnits(request) {
195231
+ if (!request.options)
195232
+ return request;
195233
+ const options = { ...request.options };
195234
+ normalizeRequestTimeUnitField(options, "maxAgentTimeoutMs", "maxAgentTimeoutS", {
195235
+ milliseconds: "options.maxAgentTimeoutMs",
195236
+ seconds: "options.maxAgentTimeoutS"
195237
+ });
195238
+ if (options.reviewBudget) {
195239
+ const reviewBudget = { ...options.reviewBudget };
195240
+ normalizeRequestTimeUnitField(reviewBudget, "maxTotalWallTimeMs", "maxTotalWallTimeS", {
195241
+ milliseconds: "options.reviewBudget.maxTotalWallTimeMs",
195242
+ seconds: "options.reviewBudget.maxTotalWallTimeS"
195243
+ });
195244
+ options.reviewBudget = reviewBudget;
195245
+ }
195246
+ return { ...request, options };
195247
+ }
195248
+ function resolveProgressHeartbeatMs(input) {
195249
+ if (input.progressHeartbeatS === undefined) {
195250
+ return input.progressHeartbeatMs;
195251
+ }
195252
+ return resolveRequestTimeUnit({
195253
+ milliseconds: input.progressHeartbeatMs,
195254
+ seconds: input.progressHeartbeatS
195255
+ }, {
195256
+ milliseconds: "progressHeartbeatMs",
195257
+ seconds: "progressHeartbeatS"
195258
+ }, { allowZero: true })?.milliseconds;
195259
+ }
195260
+ function normalizeRequestTimeUnitField(target, millisecondsKey, secondsKey, fields) {
195261
+ if (target[secondsKey] !== undefined) {
195262
+ const resolved = resolveRequestTimeUnit({
195263
+ milliseconds: target[millisecondsKey],
195264
+ seconds: target[secondsKey]
195265
+ }, fields);
195266
+ if (resolved)
195267
+ target[millisecondsKey] = resolved.milliseconds;
195268
+ }
195269
+ delete target[secondsKey];
195270
+ }
195271
+ function resolveRequestTimeUnit(input, fields, constraints) {
195272
+ try {
195273
+ return resolveTimeUnitPair(input, fields, constraints);
195274
+ } catch (error51) {
195275
+ if (error51 instanceof TimeUnitValidationError) {
195276
+ throw new KyosoRequestError(error51.message, "VALIDATION_ERROR");
195277
+ }
195278
+ throw error51;
195279
+ }
195280
+ }
195281
+
195038
195282
  // src/core/verification.ts
195039
195283
  var REAL_AGENTS = ["codex", "claude"];
195040
195284
  var VERIFIABLE_SEVERITIES = new Set(["critical", "high"]);
@@ -195093,7 +195337,7 @@ function parseVerificationVerdicts(rawText) {
195093
195337
  if (!Array.isArray(parsed.verdicts))
195094
195338
  return;
195095
195339
  return parsed.verdicts.flatMap((item) => {
195096
- if (!isRecord15(item))
195340
+ if (!isRecord16(item))
195097
195341
  return [];
195098
195342
  if (typeof item.findingId !== "string")
195099
195343
  return [];
@@ -195171,7 +195415,7 @@ function verificationNote(reasoning) {
195171
195415
  function isVerdict(value) {
195172
195416
  return value === "confirmed" || value === "refuted" || value === "uncertain";
195173
195417
  }
195174
- function isRecord15(value) {
195418
+ function isRecord16(value) {
195175
195419
  return typeof value === "object" && value !== null && !Array.isArray(value);
195176
195420
  }
195177
195421
 
@@ -195179,7 +195423,7 @@ function isRecord15(value) {
195179
195423
  var MAX_AGENT_RETRY_PROGRESS_EVENTS = 100;
195180
195424
  function requestForRecursionFingerprint(request) {
195181
195425
  try {
195182
- return scanAndRedactSecrets(request).redactedRequest;
195426
+ return scanAndRedactSecrets(normalizeRequestTimeUnits(request)).redactedRequest;
195183
195427
  } catch {
195184
195428
  return { goal: "" };
195185
195429
  }
@@ -195439,15 +195683,17 @@ async function runReview(tool, request, options = {}) {
195439
195683
  });
195440
195684
  validateReviewRequest(tool, request);
195441
195685
  const reviewBudget = resolveReviewBudget(loaded.config.reviewBudget, request.options?.reviewBudget);
195442
- const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs, configuredReviewModelCallPlan(loaded.config, reviewBudget, options.env ?? process.env, request.options?.judgeProvider));
195443
- assertTrustedWorkspaceRoot(request.workspace?.root, loaded.config.workspace.root, cwd);
195444
- const networkMode = resolveNetworkMode(request.options?.network, loaded.config.network.defaultMode, options.mcpNetworkMode);
195686
+ const normalizedRequest = normalizeRequestTimeUnits(request);
195687
+ const progressHeartbeatMs = resolveProgressHeartbeatMs(options);
195688
+ const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs, configuredReviewModelCallPlan(loaded.config, reviewBudget, options.env ?? process.env, normalizedRequest.options?.judgeProvider));
195689
+ assertTrustedWorkspaceRoot(normalizedRequest.workspace?.root, loaded.config.workspace.root, cwd);
195690
+ const networkMode = resolveNetworkMode(normalizedRequest.options?.network, loaded.config.network.defaultMode, options.mcpNetworkMode);
195445
195691
  if (networkMode === "unrestricted" && !loaded.config.network.allowUnrestricted) {
195446
195692
  throw new KyosoRequestError("unrestricted network mode is disabled by config", "NETWORK_MODE_DISABLED");
195447
195693
  }
195448
195694
  const disabledPolicy = disabledReviewPolicy(tool, loaded.config, options.entrypoint);
195449
195695
  if (disabledPolicy) {
195450
- const redactedRequest = requestForRecursionFingerprint(request);
195696
+ const redactedRequest = requestForRecursionFingerprint(normalizedRequest);
195451
195697
  const requestFingerprint2 = createRequestFingerprint({
195452
195698
  tool,
195453
195699
  request: redactedRequest,
@@ -195498,7 +195744,7 @@ async function runReview(tool, request, options = {}) {
195498
195744
  warnings.push("Network mode is unrestricted; write policy remains denied.");
195499
195745
  }
195500
195746
  startPhase("context");
195501
- const secretScan = scanAndRedactSecrets(request);
195747
+ const secretScan = scanAndRedactSecrets(normalizedRequest);
195502
195748
  await trace.write({
195503
195749
  type: "secret_scan_completed",
195504
195750
  traceId,
@@ -195506,7 +195752,7 @@ async function runReview(tool, request, options = {}) {
195506
195752
  redactions: secretScan.redactions,
195507
195753
  timestamp: new Date().toISOString()
195508
195754
  });
195509
- const allowSecretOverride = loaded.config.secrets.allowOverride && request.options?.allowSecretRedaction === true;
195755
+ const allowSecretOverride = loaded.config.secrets.allowOverride && normalizedRequest.options?.allowSecretRedaction === true;
195510
195756
  if (secretScan.detected && loaded.config.secrets.blockOnDetectedSecret && !allowSecretOverride) {
195511
195757
  const requestFingerprint2 = createRequestFingerprint({
195512
195758
  tool,
@@ -195593,7 +195839,7 @@ async function runReview(tool, request, options = {}) {
195593
195839
  budgetTracker,
195594
195840
  progressDispatcher: dispatcher,
195595
195841
  signal: options.signal,
195596
- progressHeartbeatMs: options.progressHeartbeatMs
195842
+ progressHeartbeatMs
195597
195843
  });
195598
195844
  completePhase("primary");
195599
195845
  warnings.push(...agentResults.flatMap((result) => (result.warnings ?? []).map((warning) => `Agent ${result.agent} ${warning}`)));