vibeostheog 0.22.24 → 0.22.26

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeostheog",
3
- "version": "0.22.24",
3
+ "version": "0.22.26",
4
4
  "description": "Cost-aware delegation enforcer for OpenCode. Tracks model usage, routes Task subagents to cheaper tiers, surfaces cumulative savings in chat. Includes research audit, reporting framework, project memory, progressive scratchpad decadence, and trinity CLI for brain/medium/cheap slot switching.",
5
5
  "scripts": {
6
6
  "release": "node scripts/release.mjs",
@@ -798,9 +798,6 @@ const DFLT_SEL = { enabled: true, active_slot: null, thinking_level: "off", flow
798
798
  export function readConfig(dir) {
799
799
  try {
800
800
  const configs = [];
801
- const workspaceModel = readWorkspaceSessionModel(dir);
802
- if (workspaceModel)
803
- return workspaceModel;
804
801
  const projectCfg = readOpenCodeConfigObject(dir);
805
802
  if (projectCfg && typeof projectCfg === "object")
806
803
  configs.push(projectCfg);
@@ -810,6 +807,9 @@ export function readConfig(dir) {
810
807
  if (homeCfg && typeof homeCfg === "object")
811
808
  configs.push(homeCfg);
812
809
  }
810
+ const workspaceModel = readWorkspaceSessionModel(dir);
811
+ if (workspaceModel)
812
+ return resolveConfiguredModelId(workspaceModel, configs) || workspaceModel;
813
813
  const selectedCfg = configs[0] || {};
814
814
  const selectedModel = selectedCfg?.agent?.build?.model || selectedCfg?.model || "";
815
815
  return resolveConfiguredModelId(selectedModel, configs);
@@ -910,7 +910,7 @@ function readOpenCodeConfigObject(dir) {
910
910
  }
911
911
  function collectConfiguredProviderModelsFromConfig(cfg) {
912
912
  const out = [];
913
- const providers = cfg?.provider || {};
913
+ const providers = (cfg && typeof cfg === "object") ? (cfg?.provider || {}) : {};
914
914
  for (const [providerName, providerCfg] of Object.entries(providers)) {
915
915
  const models = providerCfg?.models || {};
916
916
  for (const rawId of Object.keys(models)) {
@@ -937,11 +937,21 @@ function resolveConfiguredModelId(model, configs = []) {
937
937
  matches.add(id);
938
938
  }
939
939
  }
940
+ if (matches.size === 0) {
941
+ // No exact match — try suffix/prefix match against bare model names
942
+ for (const cfg of configs) {
943
+ for (const id of collectConfiguredProviderModelsFromConfig(cfg)) {
944
+ const bare = String(id || "").includes("/") ? String(id).split("/").pop() : id;
945
+ const nb = normalizeModelId(bare);
946
+ if (nb.includes(normalized) || normalized.includes(nb))
947
+ matches.add(id);
948
+ }
949
+ }
950
+ }
940
951
  if (matches.size === 0)
941
- return raw;
952
+ return "";
942
953
  if (matches.size === 1)
943
954
  return [...matches][0];
944
- // Multiple providers have this model — prefer provider-qualified name over bare
945
955
  const qualified = [...matches].find(m => m.includes("/"));
946
956
  return qualified || raw;
947
957
  }
@@ -991,6 +1001,9 @@ export function loadTrinitySlotsFromTiersFile() {
991
1001
  // Refresh currentModel/currentTier from disk config.
992
1002
  // Called per-hook so trinity slot changes take effect without restart.
993
1003
  export const PLACEHOLDER_RE = /^[^/]+\/[a-z-]+-model$/i;
1004
+ // Test-only exports for regression coverage
1005
+ export function _resolveConfiguredModelId(model, configs = []) { return resolveConfiguredModelId(model, configs); }
1006
+ export function _collectConfiguredProviderModelsFromConfig(cfg) { return collectConfiguredProviderModelsFromConfig(cfg); }
994
1007
  export function getTrinitySlotOrder(tiersData = null) {
995
1008
  const configured = Array.isArray(tiersData?.selection?.slot_order)
996
1009
  ? tiersData.selection.slot_order
@@ -1043,11 +1056,11 @@ export function _refreshModel(directory) {
1043
1056
  }
1044
1057
  }
1045
1058
  // Reconcile with the directory's opencode.json config.
1046
- // The trinity slot is authoritative UNLESS the directory config specifies a different model.
1059
+ // The trinity slot is authoritative UNLESS the directory config specifies a resolveable model.
1047
1060
  // This prevents the bootstrap's default slot from overriding a project-local model choice.
1048
1061
  if (!_modelLocked) {
1049
1062
  const cfgModel = readConfig(directory) || readConfig(getOpenCodeHome()) || "";
1050
- if (cfgModel && cfgModel !== currentModel) {
1063
+ if (cfgModel && cfgModel.includes("/") && cfgModel !== currentModel) {
1051
1064
  const oldModel = currentModel;
1052
1065
  const oldTier = currentTier;
1053
1066
  setCurrentModel(cfgModel);
package/src/lib/state.js CHANGED
@@ -386,8 +386,8 @@ function roundUsd(v) {
386
386
  return Math.round((Number(v) || 0) * 10000) / 10000;
387
387
  }
388
388
  // ── Tier regexes ─────────────────────────────────────────────────────
389
- const FALLBACK_HIGH = /opus|gemini-.*-pro|deepseek\/deepseek-v4-pro|gpt-5|(^|\/)o[134]($|-|\/)/i;
390
- const FALLBACK_MID = /deepseek\/deepseek-v4-flash|claude.*sonnet|gemini-.*-flash|gpt-4o(?!-mini)/i;
389
+ const FALLBACK_HIGH = /opus|gemini-.*-pro|deepseek\/deepseek-v4-pro|\bdeepseek-v4-pro\b|gpt-5|(^|\/)o[134]($|-|\/)/i;
390
+ const FALLBACK_MID = /deepseek\/deepseek-v4-flash|\bdeepseek-v4-flash\b|claude.*sonnet|gemini-.*-flash|gpt-4o(?!-mini)/i;
391
391
  export function _safeRegex(cfg, fallback, label) {
392
392
  if (!cfg)
393
393
  return fallback;