@aiden-ade/sandbox-agent 0.1.57 → 0.1.58

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.cjs +296 -262
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -11774,6 +11774,9 @@ function cacheResolvedCli(command, resolvedPath) {
11774
11774
  negativeResolutionExpiry.delete(command);
11775
11775
  }
11776
11776
  }
11777
+ function cliResolutionCacheKey(command, env) {
11778
+ return [command, env.PATH ?? "", env.HOME ?? env.USERPROFILE ?? "", env.PATHEXT ?? ""].join("\0");
11779
+ }
11777
11780
  function getCachedResolvedCli(command) {
11778
11781
  const cached2 = resolvedCliCache.get(command);
11779
11782
  if (cached2 === null) {
@@ -11878,23 +11881,21 @@ function windowsCommandCandidates(command, pathExt) {
11878
11881
  const extensions = pathExt.split(";").map((ext) => ext.trim()).filter(Boolean);
11879
11882
  return [trimmed, ...extensions.map((ext) => `${trimmed}${ext}`)];
11880
11883
  }
11881
- function augmentCliPath(env) {
11884
+ function augmentCliPath(env, includeSystemPaths = true) {
11882
11885
  const home = env.HOME || env.USERPROFILE || (0, import_node_os.homedir)();
11883
11886
  const isWin = (0, import_node_os.platform)() === "win32";
11884
- const extraPaths = isWin ? [
11887
+ const userPaths = isWin ? [
11885
11888
  (0, import_node_path.join)(home, "AppData", "Roaming", "npm"),
11886
11889
  (0, import_node_path.join)(home, "AppData", "Local", "Programs", "Microsoft", "WindowsApps"),
11887
- (0, import_node_path.join)(home, ".local", "bin"),
11888
- "C:\\Program Files\\nodejs",
11889
- "C:\\Program Files\\Git\\cmd"
11890
+ (0, import_node_path.join)(home, ".local", "bin")
11890
11891
  ] : [
11891
11892
  (0, import_node_path.join)(home, ".local", "bin"),
11892
11893
  (0, import_node_path.join)(home, ".local", "node", "bin"),
11893
11894
  (0, import_node_path.join)(home, ".bun", "bin"),
11894
- (0, import_node_path.join)(home, ".cargo", "bin"),
11895
- "/opt/homebrew/bin",
11896
- "/usr/local/bin"
11895
+ (0, import_node_path.join)(home, ".cargo", "bin")
11897
11896
  ];
11897
+ const systemPaths = isWin ? ["C:\\Program Files\\nodejs", "C:\\Program Files\\Git\\cmd"] : ["/opt/homebrew/bin", "/usr/local/bin"];
11898
+ const extraPaths = includeSystemPaths ? [...userPaths, ...systemPaths] : userPaths;
11898
11899
  const basePath = env.PATH ?? "";
11899
11900
  const existing = new Set(splitPath(basePath));
11900
11901
  const missing = extraPaths.filter((dir) => !existing.has(dir));
@@ -11904,18 +11905,18 @@ function augmentCliPath(env) {
11904
11905
  PATH: missing.length > 0 ? `${missing.join(pathSeparator())}${pathSeparator()}${basePath}` : basePath
11905
11906
  };
11906
11907
  }
11907
- function resolveCliExecutable(command, env = getDaemonCliEnvironment()) {
11908
+ function resolveCliExecutable(command, env) {
11908
11909
  const trimmed = command.trim();
11909
11910
  if (!trimmed) return null;
11910
- const cached2 = getCachedResolvedCli(trimmed);
11911
+ const enriched = augmentCliPath(env ?? getDaemonCliEnvironment(), env === void 0);
11912
+ const cacheKey = cliResolutionCacheKey(trimmed, enriched);
11913
+ const cached2 = getCachedResolvedCli(cacheKey);
11911
11914
  if (cached2 === null) return null;
11912
11915
  if (cached2 && isRunnableFile(cached2)) return cached2;
11913
11916
  if ((trimmed.includes("/") || trimmed.includes("\\")) && isRunnableFile(trimmed)) {
11914
- cacheResolvedCli(trimmed, trimmed);
11917
+ cacheResolvedCli(cacheKey, trimmed);
11915
11918
  return trimmed;
11916
11919
  }
11917
- const enriched = augmentCliPath(env);
11918
- const home = enriched.HOME || enriched.USERPROFILE || (0, import_node_os.homedir)();
11919
11920
  const isWin = (0, import_node_os.platform)() === "win32";
11920
11921
  const pathExt = enriched.PATHEXT ?? (isWin ? ".EXE;.CMD;.BAT;.COM" : "");
11921
11922
  const commandNames = [trimmed, ...CLI_COMMAND_ALIASES[trimmed] ?? []];
@@ -11931,28 +11932,22 @@ function resolveCliExecutable(command, env = getDaemonCliEnvironment()) {
11931
11932
  for (const dir of splitPath(enriched.PATH)) {
11932
11933
  candidates.push((0, import_node_path.join)(dir, name));
11933
11934
  }
11934
- candidates.push((0, import_node_path.join)(home, ".local", "bin", name), (0, import_node_path.join)(home, ".local", "node", "bin", name));
11935
- if (!isWin) {
11936
- candidates.push((0, import_node_path.join)("/opt/homebrew/bin", name), (0, import_node_path.join)("/usr/local/bin", name));
11937
- } else {
11938
- candidates.push((0, import_node_path.join)(home, "AppData", "Roaming", "npm", name));
11939
- }
11940
11935
  }
11941
11936
  const seen = /* @__PURE__ */ new Set();
11942
11937
  for (const candidate of candidates) {
11943
11938
  if (seen.has(candidate)) continue;
11944
11939
  seen.add(candidate);
11945
11940
  if (isRunnableFile(candidate)) {
11946
- cacheResolvedCli(trimmed, candidate);
11941
+ cacheResolvedCli(cacheKey, candidate);
11947
11942
  return candidate;
11948
11943
  }
11949
11944
  }
11950
11945
  const viaWhere = resolveViaWhere(trimmed, enriched);
11951
11946
  if (viaWhere) {
11952
- cacheResolvedCli(trimmed, viaWhere);
11947
+ cacheResolvedCli(cacheKey, viaWhere);
11953
11948
  return viaWhere;
11954
11949
  }
11955
- cacheResolvedCli(trimmed, null);
11950
+ cacheResolvedCli(cacheKey, null);
11956
11951
  return null;
11957
11952
  }
11958
11953
  function normalizeDiscoverableProvider(provider) {
@@ -11963,49 +11958,52 @@ function normalizeDiscoverableProvider(provider) {
11963
11958
  }
11964
11959
  return null;
11965
11960
  }
11966
- function resolveProviderCliCommand(provider, env = getDaemonCliEnvironment()) {
11961
+ function resolveProviderCliCommand(provider, env) {
11967
11962
  const normalized = normalizeDiscoverableProvider(provider);
11968
11963
  if (!normalized) return null;
11964
+ const cliEnv = env ?? getDaemonCliEnvironment();
11969
11965
  const spec = PROVIDER_CLI_COMMANDS[normalized];
11970
- const override = spec.envVars.map((name) => process.env[name]?.trim() || env[name]?.trim()).find(Boolean);
11966
+ const override = spec.envVars.map((name) => process.env[name]?.trim() || cliEnv[name]?.trim()).find(Boolean);
11971
11967
  if (override) {
11972
- const resolved2 = resolveCliExecutable(override, env) ?? (isRunnableFile(override) ? override : null);
11973
- if (resolved2) cacheResolvedCli(spec.command, resolved2);
11974
- return resolved2;
11968
+ const resolved = resolveCliExecutable(override, cliEnv) ?? (isRunnableFile(override) ? override : null);
11969
+ return resolved;
11975
11970
  }
11976
- const resolved = resolveCliExecutable(spec.command, env);
11977
- if (resolved) cacheResolvedCli(spec.command, resolved);
11978
- return resolved;
11971
+ return resolveCliExecutable(spec.command, cliEnv);
11979
11972
  }
11980
- function resolveBackendRuntimeCommand(backendKind, env = getDaemonCliEnvironment()) {
11973
+ function resolveBackendRuntimeCommand(backendKind, env) {
11981
11974
  if (!backendKind) return void 0;
11982
11975
  const command = BACKEND_CLI_COMMANDS[backendKind];
11983
11976
  if (!command) return void 0;
11984
- const cached2 = getCachedResolvedCli(command);
11985
- if (cached2 === null) return void 0;
11986
- if (cached2 && isRunnableFile(cached2)) return cached2;
11977
+ const cliEnv = env ?? getDaemonCliEnvironment();
11987
11978
  const spec = Object.values(PROVIDER_CLI_COMMANDS).find((entry) => entry.command === command);
11988
- const envOverride = spec ? spec.envVars.map((name) => env[name]?.trim()).find(Boolean) : void 0;
11989
- const resolved = resolveCliExecutable(envOverride?.trim() || command, env);
11990
- cacheResolvedCli(command, resolved);
11979
+ const envOverride = spec ? spec.envVars.map((name) => cliEnv[name]?.trim()).find(Boolean) : void 0;
11980
+ const resolved = resolveCliExecutable(envOverride?.trim() || command, cliEnv);
11991
11981
  return resolved ?? void 0;
11992
11982
  }
11993
11983
  function invalidateResolvedCli(command) {
11994
11984
  const trimmed = command.trim();
11995
11985
  if (trimmed) {
11996
- resolvedCliCache.delete(trimmed);
11997
- negativeResolutionExpiry.delete(trimmed);
11986
+ const prefix = `${trimmed}\0`;
11987
+ for (const cacheKey of resolvedCliCache.keys()) {
11988
+ if (cacheKey === trimmed || cacheKey.startsWith(prefix)) resolvedCliCache.delete(cacheKey);
11989
+ }
11990
+ for (const cacheKey of negativeResolutionExpiry.keys()) {
11991
+ if (cacheKey === trimmed || cacheKey.startsWith(prefix)) {
11992
+ negativeResolutionExpiry.delete(cacheKey);
11993
+ }
11994
+ }
11998
11995
  }
11999
11996
  }
12000
- function revalidateBackendRuntimeCommand(backendKind, env = getDaemonCliEnvironment()) {
11997
+ function revalidateBackendRuntimeCommand(backendKind, env) {
12001
11998
  if (!backendKind) return void 0;
12002
11999
  const command = BACKEND_CLI_COMMANDS[backendKind];
12003
12000
  if (!command) return void 0;
12001
+ const cliEnv = env ?? getDaemonCliEnvironment();
12004
12002
  invalidateResolvedCli(command);
12005
12003
  const spec = Object.values(PROVIDER_CLI_COMMANDS).find((entry) => entry.command === command);
12006
- const envOverride = spec ? spec.envVars.map((name) => env[name]?.trim()).find(Boolean) : void 0;
12004
+ const envOverride = spec ? spec.envVars.map((name) => cliEnv[name]?.trim()).find(Boolean) : void 0;
12007
12005
  if (envOverride) invalidateResolvedCli(envOverride);
12008
- return resolveBackendRuntimeCommand(backendKind, env);
12006
+ return resolveBackendRuntimeCommand(backendKind, cliEnv);
12009
12007
  }
12010
12008
 
12011
12009
  // src/cli-help.ts
@@ -12325,7 +12323,7 @@ function describeError(error2) {
12325
12323
  }
12326
12324
 
12327
12325
  // src/version.ts
12328
- var AGENT_VERSION = "0.1.57";
12326
+ var AGENT_VERSION = "0.1.58";
12329
12327
 
12330
12328
  // src/daemon-worktree.ts
12331
12329
  var import_node_child_process3 = require("child_process");
@@ -19564,7 +19562,8 @@ function inferEffortLevelsForModel(harness, modelId) {
19564
19562
  return [];
19565
19563
  }
19566
19564
  function resolveWireEffort(input) {
19567
- const allowed = input.effortLevels && input.effortLevels.length > 0 ? input.effortLevels : inferEffortLevelsForModel(input.harness, input.modelId);
19565
+ const reportedEffortLevels = input.effortLevels?.filter(isEffortLevel);
19566
+ const allowed = reportedEffortLevels && reportedEffortLevels.length > 0 ? reportedEffortLevels : inferEffortLevelsForModel(input.harness, input.modelId);
19568
19567
  const clamped = clampEffortToSupported(input.selectedEffortLevel, allowed);
19569
19568
  if (!clamped)
19570
19569
  return null;
@@ -19696,11 +19695,15 @@ var ENTRIES = [
19696
19695
  // Persisted by the same Antigravity build that shipped display-name ids.
19697
19696
  ["claude-opus-4-6-thinking", "Opus 4.6 Thinking", "previous"],
19698
19697
  ["claude-opus-4-7", "Opus 4.7", "previous"],
19698
+ ["claude-opus-4-7-fast", "Opus 4.7 Fast", "fast"],
19699
19699
  ["claude-opus-4-6", "Opus 4.6", "previous"],
19700
+ ["claude-opus-4-6-fast", "Opus 4.6 Fast", "fast"],
19700
19701
  ["claude-opus-4-5", "Opus 4.5", "previous"],
19701
19702
  ["claude-opus-4-1", "Opus 4.1", "previous"],
19703
+ ["claude-opus-4-0", "Opus 4.0", "previous"],
19702
19704
  ["claude-sonnet-4-5", "Sonnet 4.5", "previous"],
19703
19705
  ["claude-sonnet-4", "Sonnet 4", "previous"],
19706
+ ["claude-sonnet-4-0", "Sonnet 4.0", "previous"],
19704
19707
  // OpenAI
19705
19708
  ["gpt-5-6-sol", "GPT-5.6 Sol", "frontier"],
19706
19709
  ["gpt-5-6-terra", "GPT-5.6 Terra", "frontier"],
@@ -19723,6 +19726,7 @@ var ENTRIES = [
19723
19726
  ["gpt-5-codex", "GPT-5 Codex", "previous"],
19724
19727
  ["gpt-5-mini", "GPT-5 Mini", "previous"],
19725
19728
  ["gpt-5-nano", "GPT-5 Nano", "previous"],
19729
+ ["gpt-oss-120b", "GPT OSS 120B", "balanced"],
19726
19730
  // Google
19727
19731
  ["gemini-3-1-pro", "Gemini 3.1 Pro", "frontier"],
19728
19732
  ["gemini-3-pro", "Gemini 3 Pro", "frontier"],
@@ -19767,6 +19771,9 @@ var ENTRIES = [
19767
19771
  // OpenCode Zen free tier
19768
19772
  ["big-pickle", "Big Pickle", "balanced"],
19769
19773
  ["hy3-free", "Hy3 Free", "balanced"],
19774
+ ["laguna-s-2-1-free", "Laguna S 2.1 Free", "balanced"],
19775
+ ["ling-3-0-tiny-free", "Ling 3.0 Tiny Free", "fast"],
19776
+ ["longcat-2-0-free", "LongCat 2.0 Free", "balanced"],
19770
19777
  ["mimo-v2-5-free", "MiMo V2.5 Free", "balanced"],
19771
19778
  ["nemotron-3-ultra-free", "Nemotron 3 Ultra Free", "balanced"],
19772
19779
  ["north-mini-code-free", "North Mini Code Free", "fast"],
@@ -19924,62 +19931,6 @@ function getCursorModelCollapseKey(modelId) {
19924
19931
  return modelId.trim();
19925
19932
  return parsed.baseId;
19926
19933
  }
19927
- function getCursorCatalogCollapseKey(modelId) {
19928
- const trimmed = modelId.trim();
19929
- if (trimmed.endsWith("-medium-thinking")) {
19930
- return trimmed.slice(0, -"-medium-thinking".length);
19931
- }
19932
- let key = getCursorModelCollapseKey(trimmed);
19933
- if (key.endsWith("-thinking")) {
19934
- key = getCursorModelCollapseKey(key.slice(0, -"-thinking".length));
19935
- }
19936
- return key;
19937
- }
19938
- function mergeContextWindows(existing, discovered) {
19939
- const merged = /* @__PURE__ */ new Set([...existing ?? [], ...discovered ?? []]);
19940
- return Array.from(merged);
19941
- }
19942
- function mergeModelCapabilities(preferred, other) {
19943
- const contextWindows = mergeContextWindows(preferred.capabilities?.contextWindows, other.capabilities?.contextWindows);
19944
- const effortLevels = mergeEffortLevels(preferred.capabilities?.effortLevels, new Set(other.capabilities?.effortLevels ?? []));
19945
- if (contextWindows.length === 0 && effortLevels.length === 0)
19946
- return preferred;
19947
- return {
19948
- ...preferred,
19949
- capabilities: {
19950
- contextWindows,
19951
- effortLevels,
19952
- ...preferred.capabilities?.defaultContext ? { defaultContext: preferred.capabilities.defaultContext } : {},
19953
- ...preferred.capabilities?.defaultEffort ? { defaultEffort: preferred.capabilities.defaultEffort } : {},
19954
- ...preferred.capabilities?.supportsAttachments !== void 0 ? { supportsAttachments: preferred.capabilities.supportsAttachments } : {}
19955
- }
19956
- };
19957
- }
19958
- function collapseCursorCatalogModels(catalogModels) {
19959
- const mergedByKey = /* @__PURE__ */ new Map();
19960
- for (const model of catalogModels) {
19961
- const key = getCursorCatalogCollapseKey(model.id);
19962
- const canonical = key === model.id ? model : { ...model, id: key };
19963
- const existing = mergedByKey.get(key);
19964
- if (!existing) {
19965
- mergedByKey.set(key, canonical);
19966
- continue;
19967
- }
19968
- const preferred = existing.id.endsWith("-thinking") ? canonical : existing;
19969
- const secondary = preferred === existing ? canonical : existing;
19970
- mergedByKey.set(key, mergeModelCapabilities(preferred, secondary));
19971
- }
19972
- const seen = /* @__PURE__ */ new Set();
19973
- const result = [];
19974
- for (const model of catalogModels) {
19975
- const key = getCursorCatalogCollapseKey(model.id);
19976
- if (seen.has(key))
19977
- continue;
19978
- seen.add(key);
19979
- result.push(mergedByKey.get(key));
19980
- }
19981
- return result;
19982
- }
19983
19934
  function collapseCursorDiscoveredModelIds(modelIds) {
19984
19935
  const keys = /* @__PURE__ */ new Set();
19985
19936
  for (const id of modelIds) {
@@ -19989,16 +19940,6 @@ function collapseCursorDiscoveredModelIds(modelIds) {
19989
19940
  }
19990
19941
  return Array.from(keys);
19991
19942
  }
19992
- function mergeEffortLevels(existing, discovered) {
19993
- const merged = new Set(existing ?? []);
19994
- for (const level of discovered)
19995
- merged.add(level);
19996
- return orderEffortLevels(merged);
19997
- }
19998
- function orderEffortLevels(levels) {
19999
- const set2 = levels instanceof Set ? levels : new Set(levels);
20000
- return EFFORT_LEVELS.filter((level) => set2.has(level));
20001
- }
20002
19943
  function lookupKnownModelCapabilities(modelId) {
20003
19944
  for (const catalog of [AVAILABLE_MODELS, CURSOR_AGENT_MODELS]) {
20004
19945
  const capabilities = catalog.find((model) => model.id === modelId)?.capabilities;
@@ -20178,13 +20119,33 @@ var FREE_ATTACHMENT = {
20178
20119
  billing: "free",
20179
20120
  capabilities: { contextWindows: [], effortLevels: [], supportsAttachments: true }
20180
20121
  };
20122
+ function freeTextWithVariants(effortLevels) {
20123
+ return {
20124
+ billing: "free",
20125
+ capabilities: { contextWindows: [], effortLevels, supportsAttachments: false }
20126
+ };
20127
+ }
20181
20128
  var OPENCODE_ZEN_MODELS = defineProviderModels([
20182
20129
  { id: "opencode/big-pickle", ...FREE_TEXT },
20183
- { id: "opencode/deepseek-v4-flash-free", ...FREE_TEXT },
20130
+ {
20131
+ id: "opencode/deepseek-v4-flash-free",
20132
+ ...freeTextWithVariants(["low", "high", "max"])
20133
+ },
20134
+ {
20135
+ id: "opencode/laguna-s-2.1-free",
20136
+ ...freeTextWithVariants(["low", "medium", "high"])
20137
+ },
20138
+ { id: "opencode/ling-3.0-tiny-free", ...FREE_TEXT },
20139
+ {
20140
+ id: "opencode/longcat-2.0-free",
20141
+ ...freeTextWithVariants(["low", "medium", "high"])
20142
+ },
20184
20143
  { id: "opencode/mimo-v2.5-free", ...FREE_ATTACHMENT },
20185
- { id: "opencode/hy3-free", ...FREE_TEXT },
20186
20144
  { id: "opencode/nemotron-3-ultra-free", ...FREE_TEXT },
20187
- { id: "opencode/north-mini-code-free", ...FREE_TEXT },
20145
+ { id: "opencode/north-mini-code-free", ...freeTextWithVariants(["none", "high"]) },
20146
+ // Retained for environments whose OpenCode build still reports it. Billing
20147
+ // metadata also keeps it in the free pre-boot fallback without a second id list.
20148
+ { id: "opencode/hy3-free", ...FREE_TEXT },
20188
20149
  { id: "opencode/claude-fable-5", ...PAID },
20189
20150
  { id: "opencode/claude-opus-4-8", ...PAID },
20190
20151
  { id: "opencode/claude-opus-4-7", ...PAID },
@@ -20232,18 +20193,17 @@ var OPENCODE_ZEN_MODELS = defineProviderModels([
20232
20193
  { id: "opencode/qwen3.5-plus", ...PAID }
20233
20194
  ]);
20234
20195
 
20196
+ // ../shared/dist/agent-selection/catalog/models/supatest.js
20197
+ var SUPATEST_CLI_MODELS = defineProviderModels([
20198
+ { id: "small" },
20199
+ { id: "medium" },
20200
+ { id: "premium" },
20201
+ { id: "claude-sonnet-4-5" },
20202
+ { id: "claude-opus-4-5" },
20203
+ { id: "claude-haiku-4-5" }
20204
+ ]);
20205
+
20235
20206
  // ../shared/dist/agent-selection/catalog/models/picker-fallback.js
20236
- var CLAUDE_5_EFFORT = [
20237
- "minimal",
20238
- "low",
20239
- "medium",
20240
- "high",
20241
- "xhigh",
20242
- "max"
20243
- ];
20244
- var CODEX_54_EFFORT = ["low", "medium", "high"];
20245
- var ANTIGRAVITY_EFFORT = ["minimal", "low", "medium", "high", "max"];
20246
- var SONNET_46_THINKING_EFFORT = ["low", "medium", "high", "xhigh"];
20247
20207
  function caps(effortLevels, contextWindows = []) {
20248
20208
  return { contextWindows, effortLevels };
20249
20209
  }
@@ -20255,61 +20215,56 @@ function slugModel(providerKind, id, contextWindows) {
20255
20215
  };
20256
20216
  }
20257
20217
  var CLAUDE_CLI_PICKER_FALLBACK_MODELS = defineProviderModels([
20258
- slugModel("claude_cli", ""),
20259
- slugModel("claude_cli", "claude-sonnet-5", ["200k", "1m"]),
20260
- slugModel("claude_cli", "claude-4.6-sonnet", ["1m"]),
20261
- slugModel("claude_cli", "claude-fable-5", ["1m"]),
20262
- slugModel("claude_cli", "claude-opus-4-8", ["200k", "1m"])
20218
+ { id: "", capabilities: caps([]) },
20219
+ ...[
20220
+ "claude-sonnet-5",
20221
+ "claude-opus-4-8",
20222
+ "claude-opus-5",
20223
+ "claude-fable-5",
20224
+ "claude-sonnet-4-6",
20225
+ "claude-sonnet-4-5",
20226
+ "claude-opus-4-5",
20227
+ "claude-opus-4-1",
20228
+ "claude-opus-4-0",
20229
+ "claude-sonnet-4-0",
20230
+ "claude-haiku-4-5",
20231
+ "claude-opus-4-6-fast",
20232
+ "claude-opus-4-7-fast",
20233
+ "sonnet",
20234
+ "opus",
20235
+ "haiku",
20236
+ "fable"
20237
+ ].map((id) => slugModel("claude_cli", id))
20263
20238
  ]);
20264
20239
  var CODEX_APP_SERVER_PICKER_FALLBACK_MODELS = defineProviderModels([
20265
- slugModel("codex_app_server", "gpt-5.6-sol"),
20266
- slugModel("codex_app_server", "gpt-5.6-terra"),
20267
- slugModel("codex_app_server", "gpt-5.6-luna"),
20268
- slugModel("codex_app_server", "gpt-5.5", ["1m"]),
20269
- slugModel("codex_app_server", "gpt-5.3-codex-spark")
20240
+ { id: "gpt-5.6-sol", capabilities: caps(GPT_5_6_ULTRA_EFFORT_LEVELS) },
20241
+ {
20242
+ id: "gpt-5.6-terra",
20243
+ capabilities: caps(GPT_5_6_ULTRA_EFFORT_LEVELS)
20244
+ },
20245
+ { id: "gpt-5.6-luna", capabilities: caps(GPT_5_6_EFFORT_LEVELS) },
20246
+ { id: "gpt-5.5", capabilities: caps(CODEX_5X_EFFORT_LEVELS, ["1m"]) },
20247
+ { id: "gpt-5.4", capabilities: caps(CODEX_5X_EFFORT_LEVELS) },
20248
+ { id: "gpt-5.4-mini", capabilities: caps(CODEX_5X_EFFORT_LEVELS) },
20249
+ { id: "gpt-5.3-codex-spark", capabilities: caps(CODEX_5X_EFFORT_LEVELS) }
20270
20250
  ]);
20271
20251
  var ANTIGRAVITY_CLI_PICKER_FALLBACK_MODELS = defineProviderModels([
20272
20252
  { id: "", capabilities: caps([]) },
20273
- { id: "gemini-3.5-flash", capabilities: caps(ANTIGRAVITY_EFFORT) },
20274
- { id: "gemini-3.1-pro", capabilities: caps(ANTIGRAVITY_EFFORT) },
20275
- { id: "gemini-3-flash", capabilities: caps(ANTIGRAVITY_EFFORT) },
20276
- // Display-name id shipped by an earlier build and persisted into
20277
- // `provider_configs.models`; see the note in antigravity.ts. It canonicalises
20278
- // onto `claude-sonnet-4-6-thinking`, so it now shares Cursor's label.
20279
- {
20280
- id: "Claude Sonnet 4.6 (Thinking)",
20281
- capabilities: caps(SONNET_46_THINKING_EFFORT, ["1m"])
20282
- }
20253
+ { id: "gemini-3.6-flash", capabilities: caps(["low", "medium", "high"]) },
20254
+ { id: "gemini-3.5-flash", capabilities: caps(["low", "medium", "high"]) },
20255
+ { id: "gemini-3.1-pro", capabilities: caps(["low", "high"]) },
20256
+ { id: "claude-sonnet-4-6" },
20257
+ { id: "claude-opus-4-6-thinking" },
20258
+ { id: "gpt-oss-120b", capabilities: caps(["medium"]) }
20283
20259
  ]);
20284
- var CURSOR_AGENT_PICKER_FALLBACK_MODELS = collapseCursorCatalogModels(CURSOR_AGENT_MODELS);
20260
+ var CURSOR_AGENT_PICKER_FALLBACK_MODELS = CURSOR_AGENT_MODELS;
20285
20261
  var OPENCODE_CLI_PICKER_FALLBACK_MODELS = OPENCODE_ZEN_MODELS.filter((model) => model.billing === "free");
20286
- var COPILOT_FALLBACK_IDS = [
20287
- "auto",
20288
- "claude-sonnet-4.6",
20289
- "gpt-5.4",
20290
- "gpt-5.4-mini",
20291
- "gpt-5.3-codex"
20292
- ];
20293
- var COPILOT_CLI_PICKER_FALLBACK_MODELS = defineProviderModels(COPILOT_FALLBACK_IDS.map((id) => {
20294
- const fromCatalog = COPILOT_CLI_MODELS.find((model) => model.id === id);
20295
- if (!fromCatalog)
20296
- return { id };
20297
- const inferred = id.startsWith("gpt-") ? inferSlugModelCapabilities("codex_app_server", id) : inferSlugModelCapabilities("claude_cli", id);
20298
- const capabilities = inferred ?? fromCatalog.capabilities;
20299
- return { id, ...capabilities ? { capabilities } : {} };
20300
- }));
20301
- var DROID_CLI_PICKER_FALLBACK_MODELS = [...DROID_CLI_MODELS];
20302
- var KIMI_CLI_PICKER_FALLBACK_MODELS = defineProviderModels([
20303
- { id: "kimi-latest" },
20304
- { id: "kimi-k2.6", capabilities: caps(CLAUDE_5_EFFORT) },
20305
- { id: "kimi-k2.6-thinking", capabilities: caps(SONNET_46_THINKING_EFFORT) }
20306
- ]);
20307
- var GROK_CLI_PICKER_FALLBACK_MODELS = defineProviderModels(GROK_CLI_MODELS.map((model) => ({ id: model.id, capabilities: caps(CODEX_54_EFFORT) })));
20308
- var SUPATEST_CLI_PICKER_FALLBACK_MODELS = defineProviderModels([
20309
- { id: "medium" },
20310
- { id: "premium" },
20311
- slugModel("claude_cli", "claude-sonnet-5", ["200k", "1m"])
20312
- ]);
20262
+ var COPILOT_CLI_PICKER_FALLBACK_MODELS = COPILOT_CLI_MODELS;
20263
+ var DROID_CLI_PICKER_FALLBACK_MODELS = DROID_CLI_MODELS;
20264
+ var KIMI_CLI_PICKER_FALLBACK_MODELS = KIMI_CLI_MODELS;
20265
+ var GROK_CLI_PICKER_FALLBACK_MODELS = GROK_CLI_MODELS;
20266
+ var SUPATEST_PICKER_MODEL_IDS = /* @__PURE__ */ new Set(["small", "medium", "premium"]);
20267
+ var SUPATEST_CLI_PICKER_FALLBACK_MODELS = SUPATEST_CLI_MODELS.filter((model) => SUPATEST_PICKER_MODEL_IDS.has(model.id));
20313
20268
  var PROVIDER_PICKER_FALLBACK_MODELS = {
20314
20269
  claude_cli: CLAUDE_CLI_PICKER_FALLBACK_MODELS,
20315
20270
  codex_app_server: CODEX_APP_SERVER_PICKER_FALLBACK_MODELS,
@@ -20326,16 +20281,6 @@ function getProviderPickerFallbackModels(providerKind) {
20326
20281
  return PROVIDER_PICKER_FALLBACK_MODELS[providerKind] ?? [];
20327
20282
  }
20328
20283
 
20329
- // ../shared/dist/agent-selection/catalog/models/supatest.js
20330
- var SUPATEST_CLI_MODELS = defineProviderModels([
20331
- { id: "small" },
20332
- { id: "medium" },
20333
- { id: "premium" },
20334
- { id: "claude-sonnet-4-5" },
20335
- { id: "claude-opus-4-5" },
20336
- { id: "claude-haiku-4-5" }
20337
- ]);
20338
-
20339
20284
  // ../shared/dist/agent-selection/catalog/models/index.js
20340
20285
  var PROVIDER_MODELS = {
20341
20286
  claude_cli: CLAUDE_CLI_SEED_MODELS,
@@ -20479,8 +20424,37 @@ var PROVIDERS = [
20479
20424
  ];
20480
20425
  var SELECTABLE_PROVIDERS = PROVIDERS.filter((provider) => provider.selectable);
20481
20426
  var PROVIDER_BY_KIND = new Map(PROVIDERS.map((provider) => [provider.kind, provider]));
20427
+ function isOpenCodeProviderKind(kind) {
20428
+ return kind === "opencode_cli" || kind === "opencode_serve";
20429
+ }
20482
20430
  var WORKFLOW_ELIGIBLE_PROVIDER_KINDS = PROVIDERS.filter((provider) => provider.workflowEligible).map((provider) => provider.kind);
20483
20431
 
20432
+ // ../shared/dist/agent-selection/discovery/model-variants.js
20433
+ var MAX_MODEL_VARIANT_LENGTH = 256;
20434
+ function hasControlCharacters(value2) {
20435
+ for (let index = 0; index < value2.length; index += 1) {
20436
+ const code = value2.charCodeAt(index);
20437
+ if (code <= 31 || code === 127)
20438
+ return true;
20439
+ }
20440
+ return false;
20441
+ }
20442
+ function normalizeDiscoveredModelVariants(values) {
20443
+ const normalized = [];
20444
+ const seen = /* @__PURE__ */ new Set();
20445
+ for (const value2 of values) {
20446
+ if (typeof value2 !== "string")
20447
+ continue;
20448
+ const trimmed = value2.trim();
20449
+ if (!trimmed || trimmed.length > MAX_MODEL_VARIANT_LENGTH || hasControlCharacters(trimmed) || seen.has(trimmed)) {
20450
+ continue;
20451
+ }
20452
+ seen.add(trimmed);
20453
+ normalized.push(trimmed);
20454
+ }
20455
+ return normalized;
20456
+ }
20457
+
20484
20458
  // ../shared/dist/runtime-discovered-models.js
20485
20459
  var OPENCODE_ZEN_MODEL_BY_ID = new Map(OPENCODE_ZEN_MODELS.map((model) => [model.id, model]));
20486
20460
  var DROID_MODEL_ID_PATTERN = /^(?:claude-(?:sonnet|opus|haiku|fable)-[a-z0-9][a-z0-9.-]*|gpt-[a-z0-9][a-z0-9.-]*|gemini-[a-z0-9][a-z0-9.-]*|glm-[a-z0-9][a-z0-9.-]*|kimi-[a-z0-9][a-z0-9.-]*)$/i;
@@ -20517,7 +20491,7 @@ function catalogModelsForProvider(providerKind, modelsFromConfig) {
20517
20491
  return withRegistryNames(catalog);
20518
20492
  if (DISCOVERY_SEED_PROVIDERS.has(providerKind)) {
20519
20493
  const configById = new Map(modelsFromConfig.map((model) => [model.id, model]));
20520
- const merged = catalog.map((entry) => configById.get(entry.id) ?? entry);
20494
+ const merged = catalog.map((entry) => ({ ...entry, ...configById.get(entry.id) ?? {} }));
20521
20495
  const spineIds = new Set(catalog.map((entry) => entry.id));
20522
20496
  for (const entry of modelsFromConfig) {
20523
20497
  if (spineIds.has(entry.id))
@@ -20543,7 +20517,7 @@ function collapseDiscoveredModelIdsForProvider(providerKind, discoveredModelIds)
20543
20517
  if (providerKind === "droid_cli") {
20544
20518
  return discoveredModelIds.map((id) => id.trim()).filter(isValidDroidModelId);
20545
20519
  }
20546
- if (providerKind === "opencode_cli" || providerKind === "opencode_serve") {
20520
+ if (isOpenCodeProviderKind(providerKind)) {
20547
20521
  const keys = /* @__PURE__ */ new Set();
20548
20522
  for (const id of discoveredModelIds) {
20549
20523
  const trimmed = id.trim();
@@ -27068,6 +27042,35 @@ function unwrapOpenCodeTaskOutput(output) {
27068
27042
  if (error2?.[1] !== void 0) return error2[1].trim();
27069
27043
  return output;
27070
27044
  }
27045
+ function mapOpencodePartDelta(partId, field, delta, context, state, opts = {}) {
27046
+ if (!partId || field !== "text") return false;
27047
+ const type = state.opencodePartTypeById?.get(partId);
27048
+ if (type !== "text" && type !== "reasoning" && type !== "thinking") return false;
27049
+ if (!delta) return true;
27050
+ const parentToolUseId = opts.parentToolUseId ?? void 0;
27051
+ if (type === "text") {
27052
+ state.opencodeTextByPartId ??= /* @__PURE__ */ new Map();
27053
+ const previous2 = state.opencodeTextByPartId.get(partId) ?? "";
27054
+ state.opencodeTextByPartId.set(partId, previous2 + delta);
27055
+ state.summary += delta;
27056
+ if (parentToolUseId) {
27057
+ void context.presenter.onAssistantText(delta, parentToolUseId);
27058
+ } else {
27059
+ void context.presenter.onAssistantText(delta);
27060
+ }
27061
+ return true;
27062
+ }
27063
+ state.opencodeReasoningByPartId ??= /* @__PURE__ */ new Map();
27064
+ const previous = state.opencodeReasoningByPartId.get(partId) ?? "";
27065
+ state.opencodeReasoningByPartId.set(partId, previous + delta);
27066
+ state.iterations = Math.max(state.iterations, 1);
27067
+ if (parentToolUseId) {
27068
+ void context.presenter.onThinking(delta, parentToolUseId);
27069
+ } else {
27070
+ void context.presenter.onThinking(delta);
27071
+ }
27072
+ return true;
27073
+ }
27071
27074
  function emitOpenCodeToolPart(presenter, state, part, opts) {
27072
27075
  const stateRecord = getRecord(part.state);
27073
27076
  const status = getString(stateRecord?.status);
@@ -27097,6 +27100,11 @@ function emitOpenCodeToolPart(presenter, state, part, opts) {
27097
27100
  function mapOpencodePart(part, context, state, opts = {}) {
27098
27101
  const presenter = context.presenter;
27099
27102
  const type = getString(part.type);
27103
+ const partId = getOpenCodePartId(part);
27104
+ if (partId && type) {
27105
+ state.opencodePartTypeById ??= /* @__PURE__ */ new Map();
27106
+ state.opencodePartTypeById.set(partId, type);
27107
+ }
27100
27108
  switch (type) {
27101
27109
  case "step-start": {
27102
27110
  state.iterations += 1;
@@ -27106,10 +27114,14 @@ function mapOpencodePart(part, context, state, opts = {}) {
27106
27114
  case "text": {
27107
27115
  const text = typeof part.text === "string" ? part.text : "";
27108
27116
  if (text) {
27109
- const delta = resolveOpenCodeTextDelta(text, getOpenCodePartId(part), state);
27117
+ const delta = resolveOpenCodeTextDelta(text, partId, state);
27110
27118
  if (delta) {
27111
27119
  state.summary += delta;
27112
- void presenter.onAssistantText(delta);
27120
+ if (opts.parentToolUseId) {
27121
+ void presenter.onAssistantText(delta, opts.parentToolUseId);
27122
+ } else {
27123
+ void presenter.onAssistantText(delta);
27124
+ }
27113
27125
  }
27114
27126
  }
27115
27127
  return true;
@@ -27120,10 +27132,14 @@ function mapOpencodePart(part, context, state, opts = {}) {
27120
27132
  case "thinking": {
27121
27133
  const text = typeof part.text === "string" ? part.text : "";
27122
27134
  if (!text) return true;
27123
- const delta = resolveOpenCodeReasoningDelta(text, getOpenCodePartId(part), state);
27135
+ const delta = resolveOpenCodeReasoningDelta(text, partId, state);
27124
27136
  if (!delta) return true;
27125
27137
  state.iterations = Math.max(state.iterations, 1);
27126
- void presenter.onThinking(delta);
27138
+ if (opts.parentToolUseId) {
27139
+ void presenter.onThinking(delta, opts.parentToolUseId);
27140
+ } else {
27141
+ void presenter.onThinking(delta);
27142
+ }
27127
27143
  return true;
27128
27144
  }
27129
27145
  case "tool": {
@@ -27501,6 +27517,18 @@ async function sendPrompt(baseUrl, sessionId, context, parts2, signal) {
27501
27517
  throw new Error(`OpenCode prompt request failed: HTTP ${response.status} ${text}`.trim());
27502
27518
  }
27503
27519
  }
27520
+ async function consumeOpenCodeEventsDuringPrompt(events, promptRequest, closeEvents, handleEvent) {
27521
+ const promptOutcome = { error: null };
27522
+ const observedPromptRequest = promptRequest.catch((error2) => {
27523
+ promptOutcome.error = error2 instanceof Error ? error2.message : String(error2);
27524
+ closeEvents();
27525
+ });
27526
+ for await (const event of events) {
27527
+ if (handleEvent(event)) break;
27528
+ }
27529
+ await observedPromptRequest;
27530
+ return promptOutcome.error;
27531
+ }
27504
27532
  async function replyToPermission(baseUrl, requestId, reply, signal) {
27505
27533
  await fetch(`${baseUrl}/permission/${encodeURIComponent(requestId)}/reply`, {
27506
27534
  method: "POST",
@@ -27616,23 +27644,20 @@ function createOpencodeServeBackend(command = "opencode") {
27616
27644
  context.abortController.signal
27617
27645
  );
27618
27646
  sseClose = connection.close;
27619
- const promptError = await sendPrompt(
27647
+ const promptRequest = sendPrompt(
27620
27648
  server.baseUrl,
27621
27649
  sessionId,
27622
27650
  runContext,
27623
27651
  parts2,
27624
27652
  context.abortController.signal
27625
- ).then(() => null).catch((error2) => ({
27626
- message: error2 instanceof Error ? error2.message : String(error2)
27627
- }));
27628
- if (context.abortController.signal.aborted) {
27629
- } else if (promptError) {
27630
- state.error = promptError.message;
27631
- void context.presenter.onError(promptError.message);
27632
- } else {
27633
- for await (const event of connection.events) {
27634
- if (aborted2) break;
27635
- const done = handleOpenCodeServeEvent(
27653
+ );
27654
+ const promptError = await consumeOpenCodeEventsDuringPrompt(
27655
+ connection.events,
27656
+ promptRequest,
27657
+ connection.close,
27658
+ (event) => {
27659
+ if (aborted2) return true;
27660
+ return handleOpenCodeServeEvent(
27636
27661
  event,
27637
27662
  sessionId,
27638
27663
  runContext,
@@ -27640,8 +27665,11 @@ function createOpencodeServeBackend(command = "opencode") {
27640
27665
  readOnly,
27641
27666
  server.baseUrl
27642
27667
  );
27643
- if (done) break;
27644
27668
  }
27669
+ );
27670
+ if (!context.abortController.signal.aborted && promptError) {
27671
+ state.error = promptError;
27672
+ void context.presenter.onError(promptError);
27645
27673
  }
27646
27674
  return buildAgentResult(state, sessionId, aborted2);
27647
27675
  } catch (error2) {
@@ -27709,8 +27737,16 @@ function handleOpenCodeChildSessionEvent(event, eventSessionId, context, state,
27709
27737
  mapOpencodePart(part, context, state, { parentToolUseId });
27710
27738
  return false;
27711
27739
  }
27712
- case "message.part.delta":
27740
+ case "message.part.delta": {
27741
+ const messageId = getString(properties.messageID);
27742
+ if (messageId && state.opencodeUserMessageIds?.has(messageId)) return false;
27743
+ const partId = getString(properties.partID);
27744
+ const field = getString(properties.field);
27745
+ const delta = typeof properties.delta === "string" ? properties.delta : "";
27746
+ const parentToolUseId = resolveChildSessionLauncherToolId(state, eventSessionId);
27747
+ mapOpencodePartDelta(partId, field, delta, context, state, { parentToolUseId });
27713
27748
  return false;
27749
+ }
27714
27750
  case "permission.asked": {
27715
27751
  decideAndReplyToPermission(
27716
27752
  context.presenter,
@@ -27771,10 +27807,15 @@ function handleOpenCodeServeEvent(event, sessionId, context, state, readOnly, ba
27771
27807
  mapOpencodePart(part, context, state);
27772
27808
  return false;
27773
27809
  }
27774
- // Live token-by-token streaming is explicitly out of scope — only the
27775
- // start/complete snapshots from message.part.updated are consumed.
27776
- case "message.part.delta":
27810
+ case "message.part.delta": {
27811
+ const messageId = getString(properties.messageID);
27812
+ if (messageId && state.opencodeUserMessageIds?.has(messageId)) return false;
27813
+ const partId = getString(properties.partID);
27814
+ const field = getString(properties.field);
27815
+ const delta = typeof properties.delta === "string" ? properties.delta : "";
27816
+ mapOpencodePartDelta(partId, field, delta, context, state);
27777
27817
  return false;
27818
+ }
27778
27819
  case "permission.asked": {
27779
27820
  decideAndReplyToPermission(
27780
27821
  context.presenter,
@@ -30901,6 +30942,7 @@ var ANTIGRAVITY_MODEL_DISCOVERY_TIMEOUT_MS = 12e3;
30901
30942
  var OPENCODE_ZEN_MODEL_IDS = OPENCODE_ZEN_MODELS.map((model) => model.id);
30902
30943
  var CLAUDE_MODEL_ID_PATTERN = /claude-(?:sonnet|opus|haiku|fable)-[a-z0-9][a-z0-9-]*/g;
30903
30944
  var CLAUDE_MODEL_ALIASES = ["sonnet", "opus", "haiku", "fable"];
30945
+ var CLAUDE_MODEL_ALIAS_SET = new Set(CLAUDE_MODEL_ALIASES);
30904
30946
  var CLAUDE_MODEL_QUALIFIER_SEGMENTS = /* @__PURE__ */ new Set(["fast", "thinking", "latest", "preview"]);
30905
30947
  var PROTECTED_CATALOG_MODEL_IDS = new Set(
30906
30948
  AVAILABLE_MODELS.map((model) => model.id).filter((id) => id.length > 0)
@@ -30941,10 +30983,7 @@ function normalizeCodexReasoningEffort(effort) {
30941
30983
  const normalized = effort.trim().toLowerCase();
30942
30984
  if (!normalized) return null;
30943
30985
  if (normalized === "none") return "minimal";
30944
- if (normalized === "minimal" || normalized === "low" || normalized === "medium" || normalized === "high" || normalized === "xhigh" || normalized === "max" || normalized === "ultra") {
30945
- return normalized;
30946
- }
30947
- return null;
30986
+ return isEffortLevel(normalized) ? normalized : null;
30948
30987
  }
30949
30988
  function extractCodexDiscoveredModelIdsFromDebugJson(value2) {
30950
30989
  if (!value2 || typeof value2 !== "object") return [];
@@ -30973,17 +31012,6 @@ function extractCodexDiscoveredModelIdsFromDebugJson(value2) {
30973
31012
  return [...new Set(ids)];
30974
31013
  }
30975
31014
  var OPENCODE_MODEL_ID_LINE_PATTERN = /^[a-z0-9][\w.-]*\/[\w.-]+$/i;
30976
- var OPENCODE_KNOWN_VARIANT_KEYS = /* @__PURE__ */ new Set([
30977
- "minimal",
30978
- "none",
30979
- "low",
30980
- "medium",
30981
- "high",
30982
- "xhigh",
30983
- "extra-high",
30984
- "max",
30985
- "ultra"
30986
- ]);
30987
31015
  function parseOpenCodeVerboseModelOutput(text) {
30988
31016
  const entries = [];
30989
31017
  const lines = text.split(/\r?\n/);
@@ -31028,10 +31056,16 @@ function parseOpenCodeVerboseModelOutput(text) {
31028
31056
  if (endLineIdx === -1) break;
31029
31057
  try {
31030
31058
  const obj = JSON.parse(buffer);
31059
+ const inputCost = obj.cost?.input;
31060
+ const outputCost = obj.cost?.output;
31061
+ const hasNumericCost = typeof inputCost === "number" || typeof outputCost === "number";
31031
31062
  entries.push({
31032
31063
  id: line,
31033
31064
  reasoning: obj.capabilities?.reasoning === true,
31034
- variants: obj.variants ? Object.keys(obj.variants) : []
31065
+ variants: obj.variants ? Object.keys(obj.variants) : [],
31066
+ ...hasNumericCost ? {
31067
+ billing: inputCost === 0 && outputCost === 0 ? "free" : "paid"
31068
+ } : {}
31035
31069
  });
31036
31070
  } catch {
31037
31071
  }
@@ -31039,34 +31073,18 @@ function parseOpenCodeVerboseModelOutput(text) {
31039
31073
  }
31040
31074
  return entries;
31041
31075
  }
31042
- function extractOpenCodeModelCapabilitiesFromVerboseOutput(text) {
31043
- return parseOpenCodeVerboseModelOutput(text).filter((entry) => entry.id.startsWith("opencode/")).map((entry) => ({
31076
+ function openCodeCapabilitiesFromEntries(entries) {
31077
+ return entries.filter((entry) => entry.id.startsWith("opencode/")).map((entry) => ({
31044
31078
  id: entry.id,
31045
- variants: entry.reasoning ? entry.variants.filter((variant) => {
31046
- const trimmed = variant.trim();
31047
- return Boolean(trimmed) && trimmed.length <= 256 && !Array.from(trimmed).some((character) => {
31048
- const code = character.charCodeAt(0);
31049
- return code <= 31 || code === 127;
31050
- });
31051
- }) : []
31079
+ variants: entry.reasoning ? normalizeDiscoveredModelVariants(entry.variants) : []
31052
31080
  }));
31053
31081
  }
31054
- function extractOpenCodeDiscoveredModelIdsFromVerboseOutput(text) {
31055
- const entries = extractOpenCodeModelCapabilitiesFromVerboseOutput(text);
31056
- const ids = [];
31082
+ function openCodeBillingFromEntries(entries) {
31083
+ const billing = {};
31057
31084
  for (const entry of entries) {
31058
- const knownVariants = entry.variants.filter(
31059
- (variant) => OPENCODE_KNOWN_VARIANT_KEYS.has(variant)
31060
- );
31061
- if (knownVariants.length === 0) {
31062
- ids.push(entry.id);
31063
- continue;
31064
- }
31065
- for (const variant of knownVariants) {
31066
- ids.push(`${entry.id}-${variant}`);
31067
- }
31085
+ if (entry.id.startsWith("opencode/") && entry.billing) billing[entry.id] = entry.billing;
31068
31086
  }
31069
- return [...new Set(ids)];
31087
+ return billing;
31070
31088
  }
31071
31089
  function extractModelIdsFromText(value2) {
31072
31090
  const ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
@@ -31078,19 +31096,22 @@ function extractModelIdsFromText(value2) {
31078
31096
  }
31079
31097
  function extractModelIdsFromLabeledList(value2) {
31080
31098
  const ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
31081
- const ignoredLinePattern = /^(available models|models|usage|error|warning:)/i;
31099
+ const ignoredLinePattern = /^(available models|fetching available models|models|usage|error|warning:)/i;
31082
31100
  const ids = [];
31083
31101
  for (const rawLine of value2.split(/\r?\n/)) {
31084
31102
  const line = rawLine.replace(ansiPattern, "").trim();
31085
31103
  if (!line || ignoredLinePattern.test(line)) continue;
31086
31104
  const dashIdx = line.indexOf(" - ");
31087
- const id = (dashIdx >= 0 ? line.slice(0, dashIdx) : line).trim();
31105
+ const tabIdx = line.indexOf(" ");
31106
+ const separatorIndexes = [dashIdx, tabIdx].filter((index) => index >= 0);
31107
+ const separatorIdx = separatorIndexes.length > 0 ? Math.min(...separatorIndexes) : -1;
31108
+ const id = (separatorIdx >= 0 ? line.slice(0, separatorIdx) : line).trim();
31088
31109
  if (id) ids.push(id);
31089
31110
  }
31090
31111
  return [...new Set(ids)];
31091
31112
  }
31092
31113
  function isValidClaudeModelId(id) {
31093
- if (CLAUDE_MODEL_ALIASES.includes(id)) return true;
31114
+ if (CLAUDE_MODEL_ALIAS_SET.has(id)) return true;
31094
31115
  const match = id.match(/^claude-(sonnet|opus|haiku|fable)-([a-z0-9][a-z0-9-]*)$/);
31095
31116
  if (!match) return false;
31096
31117
  if (id.includes(".")) return false;
@@ -31186,17 +31207,27 @@ async function discoverOpenCodeModels(executable, env) {
31186
31207
  MODEL_DISCOVERY_TIMEOUT_MS
31187
31208
  );
31188
31209
  if (verbose && !verbose.error && verbose.status === 0 && verbose.stdout.trim()) {
31189
- const withEffort = extractOpenCodeDiscoveredModelIdsFromVerboseOutput(verbose.stdout);
31190
- if (withEffort.length > 0) return withEffort;
31210
+ const entries = parseOpenCodeVerboseModelOutput(verbose.stdout);
31211
+ const modelCapabilities = openCodeCapabilitiesFromEntries(entries);
31212
+ if (modelCapabilities.length > 0) {
31213
+ const modelBilling = openCodeBillingFromEntries(entries);
31214
+ return {
31215
+ models: modelCapabilities.map(({ id }) => id),
31216
+ modelVariants: Object.fromEntries(
31217
+ modelCapabilities.map(({ id, variants }) => [id, variants])
31218
+ ),
31219
+ ...Object.keys(modelBilling).length > 0 ? { modelBilling } : {}
31220
+ };
31221
+ }
31191
31222
  }
31192
31223
  const plain = await probeJsonOrTextModels(executable, env, [["models"]]);
31193
- if (plain.length > 0) return plain;
31224
+ if (plain.length > 0) return { models: plain };
31194
31225
  const legacy = await probeJsonOrTextModels(executable, env, [
31195
31226
  ["models", "--json"],
31196
31227
  ["models", "list", "--json"],
31197
31228
  ["model", "list", "--json"]
31198
31229
  ]);
31199
- return legacy.length > 0 ? legacy : OPENCODE_ZEN_MODEL_IDS;
31230
+ return { models: legacy.length > 0 ? legacy : OPENCODE_ZEN_MODEL_IDS };
31200
31231
  }
31201
31232
  async function discoverAntigravityModels(executable, env) {
31202
31233
  return probeJsonOrTextModels(executable, env, [["models"]], {
@@ -31236,13 +31267,14 @@ var CATALOG_ONLY_PROVIDERS = /* @__PURE__ */ new Set(["supatest_cli", "droid_cli
31236
31267
  function supportsProviderModelDiscovery(provider) {
31237
31268
  return provider === "opencode_serve" || provider in CATALOG_MODEL_IDS_BY_PROVIDER;
31238
31269
  }
31239
- async function discoverProviderModels(provider, executable, env) {
31270
+ async function discoverProviderModelDetails(provider, executable, env) {
31240
31271
  if (CATALOG_ONLY_PROVIDERS.has(provider)) {
31241
- return getCatalogModelIds(provider);
31272
+ return { models: getCatalogModelIds(provider) };
31242
31273
  }
31243
31274
  let probed = [];
31244
- if (provider === "opencode_cli" || provider === "opencode_serve") {
31245
- probed = await discoverOpenCodeModels(executable, env);
31275
+ if (isOpenCodeProviderKind(provider)) {
31276
+ const discovered = await discoverOpenCodeModels(executable, env);
31277
+ if (discovered.models.length > 0) return discovered;
31246
31278
  } else if (provider === "antigravity_cli") {
31247
31279
  probed = await discoverAntigravityModels(executable, env);
31248
31280
  } else if (provider === "cursor_agent_cli") {
@@ -31254,8 +31286,8 @@ async function discoverProviderModels(provider, executable, env) {
31254
31286
  } else if (provider === "kimi_cli" || provider === "grok_cli") {
31255
31287
  probed = await discoverGenericModels(executable, env);
31256
31288
  }
31257
- if (probed.length > 0) return probed;
31258
- return getCatalogModelIds(provider);
31289
+ if (probed.length > 0) return { models: probed };
31290
+ return { models: getCatalogModelIds(provider) };
31259
31291
  }
31260
31292
 
31261
31293
  // src/daemon-provider-scan.ts
@@ -31328,9 +31360,9 @@ function refreshSingleProviderModels(provider, executable, env) {
31328
31360
  if (inFlight) return inFlight;
31329
31361
  const probe = (async () => {
31330
31362
  try {
31331
- const models = await discoverProviderModels(provider, executable, env);
31332
- if (models.length > 0 || !providerModelCache.has(cacheKey)) {
31333
- providerModelCache.set(cacheKey, models);
31363
+ const discovery = await discoverProviderModelDetails(provider, executable, env);
31364
+ if (discovery.models.length > 0 || !providerModelCache.has(cacheKey)) {
31365
+ providerModelCache.set(cacheKey, discovery);
31334
31366
  }
31335
31367
  } finally {
31336
31368
  providerModelProbesInFlight.delete(cacheKey);
@@ -31387,13 +31419,15 @@ function discoverCapabilities() {
31387
31419
  providerAvailabilitySeen.delete(provider);
31388
31420
  }
31389
31421
  const version2 = providerVersionCache.get(provider);
31390
- const models = executable ? providerModelCache.get(providerModelCacheKey(provider, executable)) ?? [] : [];
31422
+ const modelDiscovery = executable ? providerModelCache.get(providerModelCacheKey(provider, executable)) : void 0;
31391
31423
  const auth2 = available ? providerAuthCache.get(provider) : void 0;
31392
31424
  return {
31393
31425
  provider,
31394
31426
  available,
31395
31427
  ...version2 ? { version: version2 } : {},
31396
- models,
31428
+ models: modelDiscovery?.models ?? [],
31429
+ ...modelDiscovery?.modelVariants ? { modelVariants: modelDiscovery.modelVariants } : {},
31430
+ ...modelDiscovery?.modelBilling ? { modelBilling: modelDiscovery.modelBilling } : {},
31397
31431
  lastCheckedAt: now,
31398
31432
  ...auth2 ? {
31399
31433
  authStatus: auth2.status,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiden-ade/sandbox-agent",
3
- "version": "0.1.57",
3
+ "version": "0.1.58",
4
4
  "type": "module",
5
5
  "description": "Alan agent runtime — cloud sandbox and local daemon (alan-agent CLI)",
6
6
  "bin": {