vibeostheog 0.24.24 → 0.24.25

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/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.24.25
2
+ - fix: heal stale vibelitex recovery across cascade
3
+
4
+
1
5
  ## 0.24.24
2
6
  - fix: restore persisted slot lock on reload (#151)
3
7
  - chore: v0.24.23 (#150)
package/dist/vibeOS.js CHANGED
@@ -2348,22 +2348,31 @@ async function remoteCall(method, args, fallbackFn) {
2348
2348
  return null;
2349
2349
  }
2350
2350
  const result = await client2[method](...args);
2351
+ if (_apiFallbackMode) {
2352
+ _apiFallbackMode = false;
2353
+ _apiFallbackSince = null;
2354
+ console.warn(`[vibeOS] API reconnected \u2014 ${method} OK`);
2355
+ }
2351
2356
  _apiFallbackMode = false;
2352
2357
  _apiFallbackSince = null;
2353
2358
  markApiConnected();
2354
2359
  return result;
2355
2360
  } catch (err) {
2361
+ const status = err?.statusCode || err?.status || 0;
2362
+ const body = err?.response?.body || err?.body || "";
2363
+ const bodyPreview = typeof body === "string" ? body.substring(0, 120) : String(body).substring(0, 120);
2364
+ const detail = status ? `status=${status} body=${bodyPreview}` : `message=${err?.message || err}`;
2356
2365
  if (!_apiFallbackMode) {
2357
2366
  _apiFallbackMode = true;
2358
2367
  _apiFallbackSince = (/* @__PURE__ */ new Date()).toISOString();
2359
- console.error(`[vibeOS] API fallback activated: ${err.message}`);
2368
+ console.error(`[vibeOS] API fallback activated (${method}): ${detail}`);
2360
2369
  }
2361
2370
  markApiDisconnected();
2362
2371
  if (fallbackFn) {
2363
2372
  try {
2364
2373
  return fallbackFn();
2365
2374
  } catch (fe) {
2366
- console.error(`[vibeOS] fallback also failed: ${fe.message}`);
2375
+ console.error(`[vibeOS] fallback also failed: ${fe?.message || fe}`);
2367
2376
  }
2368
2377
  }
2369
2378
  return null;
@@ -6147,32 +6156,35 @@ function _refreshModel(directory3) {
6147
6156
  }
6148
6157
  if (!(_modelLocked || sel.slot_locked === true)) {
6149
6158
  const activeIsManual = tiersData?.trinity?.[activeSlot]?.manual === true;
6150
- const cfgModel = activeIsManual ? "" : readConfig(directory3) || readConfig(getOpenCodeHome()) || "";
6151
- if (cfgModel && cfgModel.includes("/") && cfgModel !== currentModel) {
6152
- const oldModel = currentModel;
6153
- const oldTier = currentTier;
6154
- setCurrentModel(cfgModel);
6155
- setCurrentTier(classify(cfgModel));
6156
- if (DEBUG_INTERNALS)
6157
- console.error(`[vibeOS] model refresh (config): ${oldModel}(${oldTier}) \u2192 ${currentModel}(${currentTier})`);
6158
- try {
6159
- if (existsSync6(TIERS_FILE3)) {
6160
- withFileLock2(TIERS_FILE3, () => {
6161
- const t = safeJsonParse3(readFileSync5(TIERS_FILE3, "utf-8"));
6162
- for (const s of getTrinitySlotOrder(t)) {
6163
- if (t?.trinity?.[s]?.oc === cfgModel) {
6164
- t.selection.active_slot = s;
6165
- const _tmp = TIERS_FILE3 + ".tmp." + Date.now() + "." + Math.random().toString(36).slice(2, 8);
6166
- writeFileSync5(_tmp, JSON.stringify(t, null, 2) + "\n", "utf-8");
6167
- renameSync4(_tmp, TIERS_FILE3);
6168
- if (DEBUG_INTERNALS)
6169
- console.error(`[vibeOS] model refresh (config): synced active_slot \u2192 ${s}`);
6170
- break;
6159
+ const currentSlotModel = activeIsManual ? "" : slotOcModel;
6160
+ if (!currentSlotModel) {
6161
+ const cfgModel = readConfig(directory3) || readConfig(getOpenCodeHome()) || "";
6162
+ if (cfgModel && cfgModel.includes("/") && cfgModel !== currentModel) {
6163
+ const oldModel = currentModel;
6164
+ const oldTier = currentTier;
6165
+ setCurrentModel(cfgModel);
6166
+ setCurrentTier(classify(cfgModel));
6167
+ if (DEBUG_INTERNALS)
6168
+ console.error(`[vibeOS] model refresh (config fallback): ${oldModel}(${oldTier}) \u2192 ${currentModel}(${currentTier})`);
6169
+ try {
6170
+ if (existsSync6(TIERS_FILE3)) {
6171
+ withFileLock2(TIERS_FILE3, () => {
6172
+ const t = safeJsonParse3(readFileSync5(TIERS_FILE3, "utf-8"));
6173
+ for (const s of getTrinitySlotOrder(t)) {
6174
+ if (t?.trinity?.[s]?.oc === cfgModel) {
6175
+ t.selection.active_slot = s;
6176
+ const _tmp = TIERS_FILE3 + ".tmp." + Date.now() + "." + Math.random().toString(36).slice(2, 8);
6177
+ writeFileSync5(_tmp, JSON.stringify(t, null, 2) + "\n", "utf-8");
6178
+ renameSync4(_tmp, TIERS_FILE3);
6179
+ if (DEBUG_INTERNALS)
6180
+ console.error(`[vibeOS] model refresh (config fallback): synced active_slot \u2192 ${s}`);
6181
+ break;
6182
+ }
6171
6183
  }
6172
- }
6173
- });
6184
+ });
6185
+ }
6186
+ } catch {
6174
6187
  }
6175
- } catch {
6176
6188
  }
6177
6189
  }
6178
6190
  }
@@ -7283,15 +7295,33 @@ function recoverOptimizationModeFromSelection(sel) {
7283
7295
  return "budget";
7284
7296
  return "budget";
7285
7297
  }
7298
+ function recoverOptimizationModeFromLiveState(sel) {
7299
+ const liveTier = String(currentTier || "").toLowerCase();
7300
+ if (liveTier === "high")
7301
+ return "quality";
7302
+ if (liveTier === "mid")
7303
+ return "vibemax";
7304
+ if (liveTier === "cheap" || liveTier === "budget")
7305
+ return "budget";
7306
+ return recoverOptimizationModeFromSelection(sel);
7307
+ }
7286
7308
  function loadOptimizationMode() {
7287
7309
  try {
7288
7310
  const sel = loadSelection();
7289
7311
  const persistedMode = sel.optimization_mode || null;
7290
- if (persistedMode === "vibelitex") {
7291
- const prevKey = `${_OC_SID}_prev_opt`;
7292
- const sessionMode = loadSessionOptMode(_OC_SID);
7293
- const globalMode = loadGlobalOptMode();
7294
- const recoveryMode = sel.previous_optimization_mode || loadSessionOptMode(prevKey) || (sessionMode && sessionMode !== "vibelitex" ? sessionMode : "") || (globalMode && globalMode !== "vibelitex" ? globalMode : "") || recoverOptimizationModeFromSelection(sel);
7312
+ const prevKey = `${_OC_SID}_prev_opt`;
7313
+ const sessionMode = loadSessionOptMode(_OC_SID);
7314
+ const globalMode = loadGlobalOptMode();
7315
+ const liveRecovery = recoverOptimizationModeFromLiveState(sel);
7316
+ const storedModes = [
7317
+ persistedMode,
7318
+ sel.previous_optimization_mode,
7319
+ loadSessionOptMode(prevKey),
7320
+ sessionMode,
7321
+ globalMode
7322
+ ].map((mode) => String(mode || "").toLowerCase());
7323
+ if (storedModes.includes("vibelitex")) {
7324
+ const recoveryMode = (sel.previous_optimization_mode && sel.previous_optimization_mode !== "vibelitex" ? sel.previous_optimization_mode : "") || loadSessionOptMode(prevKey) || (sessionMode && sessionMode !== "vibelitex" ? sessionMode : "") || (globalMode && globalMode !== "vibelitex" ? globalMode : "") || liveRecovery;
7295
7325
  if (recoveryMode && recoveryMode !== "vibelitex") {
7296
7326
  try {
7297
7327
  writeSelection("optimization_mode", recoveryMode);
@@ -7312,12 +7342,10 @@ function loadOptimizationMode() {
7312
7342
  return recoveryMode;
7313
7343
  }
7314
7344
  }
7315
- const mode = loadSessionOptMode(_OC_SID);
7316
- if (mode && mode !== "auto")
7317
- return mode;
7318
- const global = loadGlobalOptMode();
7319
- if (global && global !== "auto")
7320
- return global;
7345
+ if (sessionMode && sessionMode !== "auto")
7346
+ return sessionMode;
7347
+ if (globalMode && globalMode !== "auto")
7348
+ return globalMode;
7321
7349
  return DFLT_OPTIMIZATION_MODE;
7322
7350
  } catch {
7323
7351
  return DFLT_OPTIMIZATION_MODE;
@@ -8263,6 +8291,13 @@ function createTrinityTool(deps) {
8263
8291
  slot = action;
8264
8292
  action = "set";
8265
8293
  }
8294
+ const keepExistingTrinitySlot = (existingSlot, nextModel) => {
8295
+ const currentOc = String(existingSlot?.oc || "").trim();
8296
+ if (currentOc && !/placeholder/i.test(currentOc) && !/^[^/]+\/[a-z-]+-model$/i.test(currentOc)) {
8297
+ return { ...existingSlot, cc: existingSlot?.cc || deps.modelToCcAlias(currentOc) };
8298
+ }
8299
+ return { oc: nextModel, cc: deps.modelToCcAlias(nextModel) };
8300
+ };
8266
8301
  const _brandedModeIds = ["vibeultrax", "vibeqmax", "vibemax", "vibelitex"];
8267
8302
  const _builtInModeIds = ["budget", "quality", "speed", "longrun", "auto", "balanced", "audit", "forensic"];
8268
8303
  if (!action || action === "status") {
@@ -8300,7 +8335,7 @@ function createTrinityTool(deps) {
8300
8335
  const fallbackModelGuard = currentProvider === "opencode" && selectedProvider !== "opencode";
8301
8336
  if (deps.currentModel && sel.selected_model && deps.currentModel !== sel.selected_model && !apiFallbackActive && !fallbackModelGuard) {
8302
8337
  try {
8303
- const providers = deps._loadOpenCodeProviders();
8338
+ const providers = typeof deps._loadOpenCodeProviders === "function" ? deps._loadOpenCodeProviders(deps.directory) : {};
8304
8339
  const auth = deps._readAuth();
8305
8340
  const models = await deps.discoverAvailableModels(providers, auth);
8306
8341
  const trinity = buildDeterministicTrinity(models, { selectedModelId: deps.currentModel });
@@ -8318,13 +8353,7 @@ function createTrinityTool(deps) {
8318
8353
  const slots = ["brain", "medium", "cheap"];
8319
8354
  for (const s of slots) {
8320
8355
  const autoModel = probed[s].id;
8321
- const oldModel = oldTiers[s]?.oc || "";
8322
- const oldModelProvider = oldModel.includes("/") ? oldModel.split("/")[0] : "";
8323
- if (oldModelProvider && oldModelProvider !== oldProvider && oldModelProvider !== newProvider) {
8324
- tiersData.trinity[s] = oldTiers[s];
8325
- } else {
8326
- tiersData.trinity[s] = { oc: autoModel, cc: deps.modelToCcAlias(autoModel) };
8327
- }
8356
+ tiersData.trinity[s] = keepExistingTrinitySlot(oldTiers[s], autoModel);
8328
8357
  }
8329
8358
  tiersData.selection ??= {};
8330
8359
  tiersData.selection.selected_provider = trinity.provider || resolveExecutionIdentity(deps.currentModel, deps.directory)?.provider || "";
@@ -8672,7 +8701,7 @@ Lock is per-session (resets on restart).`;
8672
8701
  if (action === "setup") {
8673
8702
  const now = (/* @__PURE__ */ new Date()).toISOString();
8674
8703
  const existing = deps.existsSync(deps.TIERS_FILE) ? deps.safeJsonParse(deps.readFileSync(deps.TIERS_FILE, "utf-8")) || {} : {};
8675
- const providers = typeof deps._loadOpenCodeProviders === "function" ? deps._loadOpenCodeProviders() : {};
8704
+ const providers = typeof deps._loadOpenCodeProviders === "function" ? deps._loadOpenCodeProviders(deps.directory) : {};
8676
8705
  const auth = typeof deps._readAuth === "function" ? deps._readAuth() : {};
8677
8706
  let discovered = [];
8678
8707
  try {
@@ -8706,12 +8735,12 @@ Lock is per-session (resets on restart).`;
8706
8735
  tiers.selection.executed_provider = tiers.selection.selected_provider;
8707
8736
  tiers.selection.executed_quality_tier = tiers.selection.selected_quality_tier;
8708
8737
  tiers.selection.executed_model = tiers.selection.selected_model;
8709
- if (brain && existing?.trinity?.brain?.manual !== true)
8710
- tiers.trinity.brain = { oc: brain, cc: deps.modelToCcAlias(brain) };
8711
- if (medium && existing?.trinity?.medium?.manual !== true)
8712
- tiers.trinity.medium = { oc: medium, cc: deps.modelToCcAlias(medium) };
8713
- if (cheap && existing?.trinity?.cheap?.manual !== true)
8714
- tiers.trinity.cheap = { oc: cheap, cc: deps.modelToCcAlias(cheap) };
8738
+ if (brain)
8739
+ tiers.trinity.brain = keepExistingTrinitySlot(existing?.trinity?.brain, brain);
8740
+ if (medium)
8741
+ tiers.trinity.medium = keepExistingTrinitySlot(existing?.trinity?.medium, medium);
8742
+ if (cheap)
8743
+ tiers.trinity.cheap = keepExistingTrinitySlot(existing?.trinity?.cheap, cheap);
8715
8744
  deps.mkdirSync(dirname9(deps.TIERS_FILE), { recursive: true });
8716
8745
  deps.writeFileSync(deps.TIERS_FILE, JSON.stringify(tiers, null, 2) + "\n");
8717
8746
  if (typeof deps._refreshModel === "function")
@@ -9063,7 +9092,7 @@ ${L.repeat(40)}`);
9063
9092
  return "[vibeOS] Alpha bootstrap token saved. Remote API will retry the exchange on the next call.";
9064
9093
  }
9065
9094
  if (action === "rebuild") {
9066
- const providers = deps._loadOpenCodeProviders();
9095
+ const providers = typeof deps._loadOpenCodeProviders === "function" ? deps._loadOpenCodeProviders(deps.directory) : {};
9067
9096
  const auth = deps._readAuth();
9068
9097
  const models = await deps.discoverAvailableModels(providers, auth);
9069
9098
  const selectedModel = deps.currentModel || deps.loadSelection?.().selected_model || deps.loadSelection?.().executed_model || "";
@@ -9092,9 +9121,9 @@ ${L.repeat(40)}`);
9092
9121
  const tiers = deps.safeJsonParse(deps.readFileSync(deps.TIERS_FILE, "utf-8"));
9093
9122
  const existing = tiers.trinity || {};
9094
9123
  tiers.trinity = {
9095
- brain: existing.brain?.manual === true ? { ...existing.brain } : { oc: probed.brain.id, cc: deps.modelToCcAlias(probed.brain.id) },
9096
- medium: existing.medium?.manual === true ? { ...existing.medium } : { oc: probed.medium.id, cc: deps.modelToCcAlias(probed.medium.id) },
9097
- cheap: existing.cheap?.manual === true ? { ...existing.cheap } : { oc: probed.cheap.id, cc: deps.modelToCcAlias(probed.cheap.id) }
9124
+ brain: keepExistingTrinitySlot(existing.brain, probed.brain.id),
9125
+ medium: keepExistingTrinitySlot(existing.medium, probed.medium.id),
9126
+ cheap: keepExistingTrinitySlot(existing.cheap, probed.cheap.id)
9098
9127
  };
9099
9128
  tiers.selection ??= {};
9100
9129
  tiers.selection.selected_provider = trinity.provider || resolveExecutionIdentity(selectedModel, deps.directory)?.provider || "";
@@ -9178,7 +9207,7 @@ ${L.repeat(40)}`);
9178
9207
  } else if (deps.currentModel || !deps.existsSync(deps.TIERS_FILE)) {
9179
9208
  try {
9180
9209
  const auth = deps._readAuth();
9181
- const ok = await deps.probeModel(deps.currentModel, auth, deps._loadOpenCodeProviders());
9210
+ const ok = await deps.probeModel(deps.currentModel, auth, typeof deps._loadOpenCodeProviders === "function" ? deps._loadOpenCodeProviders(deps.directory) : {});
9182
9211
  results.push({
9183
9212
  ok,
9184
9213
  okLabel: ok ? "\u2705" : "\u274C",
@@ -10826,9 +10855,10 @@ function syncControlSettings(cv, options = {}) {
10826
10855
  const restoreMode = sessionPreviousOptMode || previousOptMode2 || inferredRecoveryMode;
10827
10856
  const canRestorePrevious = !!restoreMode && cv.optimization_mode !== "vibelitex" && (previousOptMode2 !== null || sessionPreviousOptMode !== null);
10828
10857
  if (fallbackPinned) {
10829
- if (currentSel.optimization_mode !== "vibelitex") {
10830
- writeIf("previous_optimization_mode", currentSel.optimization_mode);
10831
- writeSessionOptMode(prevSessionKey2, currentSel.optimization_mode || "");
10858
+ const snapshotMode = currentSel.optimization_mode && currentSel.optimization_mode !== "vibelitex" ? currentSel.optimization_mode : previousOptMode2 || sessionPreviousOptMode || inferredRecoveryMode;
10859
+ if (snapshotMode && snapshotMode !== "vibelitex") {
10860
+ writeIf("previous_optimization_mode", snapshotMode);
10861
+ writeSessionOptMode(prevSessionKey2, snapshotMode);
10832
10862
  }
10833
10863
  } else if (canRestorePrevious) {
10834
10864
  writeIf("optimization_mode", restoreMode);
@@ -14418,10 +14448,17 @@ async function _seedOrRepairModelTiers(directory3) {
14418
14448
  }
14419
14449
  const existingSelection = existing?.selection && typeof existing.selection === "object" ? existing.selection : {};
14420
14450
  const existingTrinity = existing?.trinity && typeof existing.trinity === "object" ? existing.trinity : {};
14451
+ const keepExistingSlot = (slotRow, fallbackModel) => {
14452
+ const currentOc = String(slotRow?.oc || "").trim();
14453
+ if (currentOc && !PLACEHOLDER_RE.test(currentOc) && !/placeholder/i.test(currentOc)) {
14454
+ return { ...slotRow, cc: slotRow?.cc || modelToCcAlias(currentOc) };
14455
+ }
14456
+ return { oc: fallbackModel, cc: modelToCcAlias(fallbackModel) };
14457
+ };
14421
14458
  const nextTrinity = {
14422
- brain: existingTrinity.brain?.manual === true && String(existingTrinity.brain?.oc || "").trim() && !PLACEHOLDER_RE.test(String(existingTrinity.brain?.oc || "")) ? { ...existingTrinity.brain, cc: existingTrinity.brain?.cc || modelToCcAlias(String(existingTrinity.brain?.oc || "")) } : { oc: brain, cc: modelToCcAlias(brain) },
14423
- medium: existingTrinity.medium?.manual === true && String(existingTrinity.medium?.oc || "").trim() && !PLACEHOLDER_RE.test(String(existingTrinity.medium?.oc || "")) ? { ...existingTrinity.medium, cc: existingTrinity.medium?.cc || modelToCcAlias(String(existingTrinity.medium?.oc || "")) } : { oc: medium, cc: modelToCcAlias(medium) },
14424
- cheap: existingTrinity.cheap?.manual === true && String(existingTrinity.cheap?.oc || "").trim() && !PLACEHOLDER_RE.test(String(existingTrinity.cheap?.oc || "")) ? { ...existingTrinity.cheap, cc: existingTrinity.cheap?.cc || modelToCcAlias(String(existingTrinity.cheap?.oc || "")) } : { oc: cheap, cc: modelToCcAlias(cheap) }
14459
+ brain: keepExistingSlot(existingTrinity.brain, brain),
14460
+ medium: keepExistingSlot(existingTrinity.medium, medium),
14461
+ cheap: keepExistingSlot(existingTrinity.cheap, cheap)
14425
14462
  };
14426
14463
  const activeSlot = ["brain", "medium", "cheap"].includes(String(existingSelection.active_slot || "").trim()) ? String(existingSelection.active_slot) : "brain";
14427
14464
  const tiers = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeostheog",
3
- "version": "0.24.24",
3
+ "version": "0.24.25",
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",