vibeostheog 0.25.22 → 0.25.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/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 0.25.25
2
+ - fix: make deploy install tests platform-aware (skip macOS path on Linux)
3
+ - fix: resolveOpenCodeHomes() merges workspace + desktop homes
4
+ - fix: resolveOpenCodeHomes() now merges workspace + desktop homes
5
+ - fix: use getVibeOSHome() for hookVibeHome instead of hardcoded join(home, .claude) (#202)
6
+ Merge branch 'origin/master' — incorporate upstream changes
7
+
8
+
1
9
  ## 0.25.22
2
10
  - fix: include installer helper in package
3
11
 
@@ -1 +1 @@
1
- window.__VIBEOS_DASHBOARD_BASE__ = "http://127.0.0.1:50016";
1
+ window.__VIBEOS_DASHBOARD_BASE__ = "http://127.0.0.1:56848";
package/dist/vibeOS.js CHANGED
@@ -2697,7 +2697,7 @@ var init_state = __esm({
2697
2697
  }
2698
2698
  })();
2699
2699
  VIBEOS_CONTEXT = new AsyncLocalStorage();
2700
- VIBEOS_HOME = process.env.VIBEOS_HOME || join2(USER_HOME2, ".claude");
2700
+ VIBEOS_HOME = process.env.VIBEOS_HOME || join2(process.env.HOME || USER_HOME2, ".claude");
2701
2701
  OPENCODE_HOME = resolveOpenCodeHome();
2702
2702
  FILE_LOCK_DIR = join2(VIBEOS_HOME, ".vibeOS-locks");
2703
2703
  DELEGATION_STATE_FILE = join2(VIBEOS_HOME, "delegation-state.json");
@@ -2817,8 +2817,8 @@ var init_state = __esm({
2817
2817
  }
2818
2818
  });
2819
2819
  _startupMaintenanceHome = "";
2820
- FALLBACK_HIGH = /opus|gemini-.*-pro|deepseek\/deepseek-v4-pro|\bdeepseek-v4-pro\b|gpt-5|(^|\/)o[134]($|-|\/)/i;
2821
- FALLBACK_MID = /deepseek\/deepseek-v4-flash|\bdeepseek-v4-flash\b|claude.*sonnet|gemini-.*-flash|gpt-4o(?!-mini)/i;
2820
+ FALLBACK_HIGH = /opus|gemini-.*-pro|gpt-5|(^|\/)o[134]($|-|\/)|claude.*opus|reasoner|r1/i;
2821
+ FALLBACK_MID = /sonnet|gemini-.*-flash|gpt-4o(?!-mini)|haiku|flash|4o/i;
2822
2822
  ({ high: HIGH_TIER_RE, mid: MID_TIER_RE } = loadTierRegexes());
2823
2823
  loadMLState();
2824
2824
  scratchpadHitsSeen = /* @__PURE__ */ new Set();
@@ -6820,7 +6820,6 @@ function applySlot2(slot, projectDir = "") {
6820
6820
  oc.model = ocModel;
6821
6821
  writeFileSync6(ocConfig, JSON.stringify(oc, null, 2) + "\n");
6822
6822
  }
6823
- clearWorkspaceFollowupPauseForSession(getCurrentSessionId());
6824
6823
  _refreshModel(dir);
6825
6824
  return { ok: true, ocModel };
6826
6825
  });
@@ -6851,6 +6850,7 @@ var ResolutionTracker = class _ResolutionTracker {
6851
6850
  this.pivotHistory = [];
6852
6851
  this.outcomeHistory = [];
6853
6852
  this.calibratedWeights = null;
6853
+ this.recentMessageLengths = [];
6854
6854
  }
6855
6855
  static extractFeatures(text) {
6856
6856
  if (!text || typeof text !== "string")
@@ -6925,13 +6925,18 @@ var ResolutionTracker = class _ResolutionTracker {
6925
6925
  getRepeatStreak() {
6926
6926
  if (this.history.length < 2)
6927
6927
  return 0;
6928
- const normalizedLast = this.normalizeText(this.history[this.history.length - 1].text);
6929
- if (!normalizedLast)
6928
+ const lastWords = new Set(this.history[this.history.length - 1].text.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
6929
+ if (lastWords.size === 0)
6930
6930
  return 0;
6931
6931
  let streak = 1;
6932
6932
  for (let i = this.history.length - 2; i >= 0; i--) {
6933
- const normalized = this.normalizeText(this.history[i].text);
6934
- if (!normalized || normalized !== normalizedLast)
6933
+ const currWords = new Set(this.history[i].text.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
6934
+ if (currWords.size === 0)
6935
+ break;
6936
+ const intersection = new Set([...lastWords].filter((w) => currWords.has(w)));
6937
+ const union = /* @__PURE__ */ new Set([...lastWords, ...currWords]);
6938
+ const jaccard = intersection.size / Math.max(union.size, 1);
6939
+ if (jaccard < 0.7)
6935
6940
  break;
6936
6941
  streak++;
6937
6942
  }
@@ -6967,6 +6972,39 @@ var ResolutionTracker = class _ResolutionTracker {
6967
6972
  }
6968
6973
  return streak;
6969
6974
  }
6975
+ getRecentNegativeOutcomeStreak() {
6976
+ if (this.outcomeHistory.length < 1)
6977
+ return 0;
6978
+ let streak = 0;
6979
+ for (let i = this.outcomeHistory.length - 1; i >= 0; i--) {
6980
+ const o = this.outcomeHistory[i];
6981
+ if (/negative|failed|unresolved|loop_detected/i.test(String(o?.outcome || "")))
6982
+ streak++;
6983
+ else
6984
+ break;
6985
+ }
6986
+ return streak;
6987
+ }
6988
+ computeMessageLengthTrend() {
6989
+ const lengths = this.recentMessageLengths;
6990
+ if (lengths.length < 3)
6991
+ return { trend: "stable", slope: 0 };
6992
+ const pairs = lengths.slice(-4);
6993
+ let decreasingCount = 0;
6994
+ let totalSlope = 0;
6995
+ for (let i = 1; i < pairs.length; i++) {
6996
+ const diff = pairs[i] - pairs[i - 1];
6997
+ if (diff < 0)
6998
+ decreasingCount++;
6999
+ totalSlope += diff;
7000
+ }
7001
+ const ratio = decreasingCount / (pairs.length - 1);
7002
+ const avgSlope = pairs.length > 1 ? totalSlope / (pairs.length - 1) : 0;
7003
+ return {
7004
+ trend: ratio >= 0.6 && avgSlope < 0 ? "shortening" : "stable",
7005
+ slope: avgSlope
7006
+ };
7007
+ }
6970
7008
  update(userText, features, action, entropy, uncertainty, embedding = null, activity = null) {
6971
7009
  const normalizedActivity = this.normalizeActivity(activity, action, userText);
6972
7010
  const entry = {
@@ -6986,6 +7024,9 @@ var ResolutionTracker = class _ResolutionTracker {
6986
7024
  }
6987
7025
  }
6988
7026
  this.history.push(entry);
7027
+ this.recentMessageLengths.push((userText || "").length);
7028
+ if (this.recentMessageLengths.length > 6)
7029
+ this.recentMessageLengths.shift();
6989
7030
  if (this.history.length > this.maxHistory) {
6990
7031
  this.history.shift();
6991
7032
  }
@@ -7121,6 +7162,9 @@ var ResolutionTracker = class _ResolutionTracker {
7121
7162
  pivot_detected: pivotDetected,
7122
7163
  pivot_score: Math.round(pivotScore * 1e4) / 1e4,
7123
7164
  outcome: lastEntry.outcome || null,
7165
+ outcome_negative_streak: this.getRecentNegativeOutcomeStreak(),
7166
+ message_length_trend: this.computeMessageLengthTrend().trend,
7167
+ message_length_slope: this.computeMessageLengthTrend().slope,
7124
7168
  n_interactions: n
7125
7169
  };
7126
7170
  }
@@ -7171,7 +7215,8 @@ var ResolutionTracker = class _ResolutionTracker {
7171
7215
  }
7172
7216
  detectLoop() {
7173
7217
  const repeatSignal = Math.max(this.getRepeatStreak(), this.getActivityRepeatStreak(), this.getTargetRepeatStreak());
7174
- return this.loopCount >= 2 || repeatSignal >= 2;
7218
+ const negativeOutcomeStreak = this.getRecentNegativeOutcomeStreak();
7219
+ return this.loopCount >= 2 || repeatSignal >= 2 || negativeOutcomeStreak >= 2;
7175
7220
  }
7176
7221
  computeIntentState() {
7177
7222
  const last = this.history[this.history.length - 1];
@@ -7215,6 +7260,7 @@ var ResolutionTracker = class _ResolutionTracker {
7215
7260
  this.loopCount = 0;
7216
7261
  this.pivotHistory = [];
7217
7262
  this.outcomeHistory = [];
7263
+ this.recentMessageLengths = [];
7218
7264
  }
7219
7265
  recordOutcome(outcome) {
7220
7266
  const entry = this.history[this.history.length - 1];
@@ -7280,6 +7326,7 @@ var ResolutionTracker = class _ResolutionTracker {
7280
7326
  loopCount: this.loopCount,
7281
7327
  pivotHistory: this.pivotHistory,
7282
7328
  outcomeHistory: this.outcomeHistory,
7329
+ recentMessageLengths: this.recentMessageLengths,
7283
7330
  calibratedWeights: this.calibratedWeights
7284
7331
  };
7285
7332
  }
@@ -7292,6 +7339,7 @@ var ResolutionTracker = class _ResolutionTracker {
7292
7339
  tracker.loopCount = Number(data.loopCount || 0);
7293
7340
  tracker.pivotHistory = Array.isArray(data.pivotHistory) ? data.pivotHistory : [];
7294
7341
  tracker.outcomeHistory = Array.isArray(data.outcomeHistory) ? data.outcomeHistory : [];
7342
+ tracker.recentMessageLengths = Array.isArray(data.recentMessageLengths) ? data.recentMessageLengths : [];
7295
7343
  tracker.calibratedWeights = data.calibratedWeights || null;
7296
7344
  return tracker;
7297
7345
  }
@@ -7425,9 +7473,9 @@ init_state();
7425
7473
  function detectOutcomeSignal(text) {
7426
7474
  if (!text)
7427
7475
  return null;
7428
- if (/thank|perfect|exactly|that.?s it|works great|works perfectly|solved|fixed|awesome|you rock/i.test(text))
7476
+ if (/thank|perfect|exactly|that.?s it|works great|works perfectly|solved|fixed|awesome|you rock|that works|finally|progress|much better|getting there|closer now/i.test(text))
7429
7477
  return "positive";
7430
- if (/doesn.?t work|still broken|not working|incorrect|wrong|failed|error|useless|stuck/i.test(text))
7478
+ if (/doesn.?t work|still broken|not working|incorrect|wrong|failed|error|useless|stuck|still failing|broke again|worse|regression|new (problem|bug|issue|error)|made it worse|every (fix|change|attempt) (broke|breaks|introduces)|went backwards|back to square|start over|same (issue|problem|error) (again|still)|(another|yet another|different) (error|problem|issue)|(still|again|still not) (the|at|same)|\d+\s*(times|attempts|tries) (and|but) (still|same|same result)/i.test(text))
7431
7479
  return "negative";
7432
7480
  return null;
7433
7481
  }
@@ -7464,6 +7512,8 @@ function getBehavioralStressSignals(context, blackboxState) {
7464
7512
  const repeatStreak = Number(blackboxState?.repeat_streak ?? 0);
7465
7513
  const activityRepeatStreak = Number(blackboxState?.activity_repeat_streak ?? 0);
7466
7514
  const targetRepeatStateStreak = Number(blackboxState?.target_repeat_streak ?? 0);
7515
+ const messageLengthTrend = String(blackboxState?.message_length_trend || "stable");
7516
+ const messageLengthSlope = Number(blackboxState?.message_length_slope ?? 0);
7467
7517
  return {
7468
7518
  toolRepeatStreak,
7469
7519
  targetRepeatStreak,
@@ -7471,10 +7521,13 @@ function getBehavioralStressSignals(context, blackboxState) {
7471
7521
  loopCount,
7472
7522
  repeatStreak,
7473
7523
  activityRepeatStreak,
7474
- targetRepeatStateStreak
7524
+ targetRepeatStateStreak,
7525
+ messageLengthTrend,
7526
+ messageLengthSlope
7475
7527
  };
7476
7528
  }
7477
7529
  function scoreStress(text, context = {}) {
7530
+ const blackboxState = loadBlackboxState();
7478
7531
  if (!text || typeof text !== "string")
7479
7532
  return 0;
7480
7533
  const t = text.toLowerCase();
@@ -7516,22 +7569,25 @@ function scoreStress(text, context = {}) {
7516
7569
  const behavioralPhrases = [
7517
7570
  { re: /\b(restart|restarts|restarted|restart again|restart it|retry|retries|retrial|rerun|redo|repeat the step|try again|another attempt|another pass)\b/gi, weight: 0.09 },
7518
7571
  { re: /\b(still failing|keeps failing|keeps breaking|still broken|same issue|same result|same error|new error|new issue|broke again|breaks again|every fix|every time|over and over|again and again)\b/gi, weight: 0.12 },
7519
- { re: /\b(blocked again|stuck again|failed again|fails again|this is not working|nothing changed|no change)\b/gi, weight: 0.1 }
7572
+ { re: /\b(blocked again|stuck again|failed again|fails again|this is not working|nothing changed|no change)\b/gi, weight: 0.1 },
7573
+ { re: /\b(start over|from scratch|back to square|back to the drawing board|reset|rethink|different approach)\b/gi, weight: 0.12 },
7574
+ { re: /\b(made it worse|went backwards|regression|introduced (a |a new |another )(problem|bug|issue)|worse than before|new (problem|bug|issue) (emerged|appeared|showed))\b/gi, weight: 0.15 },
7575
+ { re: /\b(\d+)\s*(times|attempts|tries)\b/gi, dynamic: true }
7520
7576
  ];
7521
- for (const { re, weight } of behavioralPhrases) {
7522
- const hits = (t.match(re) || []).length;
7523
- score += hits * weight;
7524
- }
7525
- const sessionId = String(_OC_SID || "");
7526
- let blackboxState = context?.blackboxState || null;
7527
- if (!blackboxState) {
7528
- try {
7529
- const raw = loadBlackboxState();
7530
- blackboxState = raw?.sessions?.[sessionId] || null;
7531
- } catch {
7577
+ for (const { re, weight, dynamic } of behavioralPhrases) {
7578
+ const matches = t.match(re);
7579
+ if (!matches)
7580
+ continue;
7581
+ if (dynamic) {
7582
+ for (const m of matches) {
7583
+ const num = parseInt(m, 10) || 0;
7584
+ score += Math.min(0.2, num * 0.04);
7585
+ }
7586
+ } else {
7587
+ score += matches.length * weight;
7532
7588
  }
7533
7589
  }
7534
- const { toolRepeatStreak, targetRepeatStreak, negativeOutcomes, loopCount, repeatStreak, activityRepeatStreak, targetRepeatStateStreak } = getBehavioralStressSignals(context, blackboxState);
7590
+ const { toolRepeatStreak, targetRepeatStreak, negativeOutcomes, loopCount, repeatStreak, activityRepeatStreak, targetRepeatStateStreak, messageLengthTrend, messageLengthSlope } = getBehavioralStressSignals(context, blackboxState);
7535
7591
  if (toolRepeatStreak >= 2) {
7536
7592
  score += 0.08 + Math.min(0.24, (toolRepeatStreak - 1) * 0.05);
7537
7593
  }
@@ -7553,6 +7609,9 @@ function scoreStress(text, context = {}) {
7553
7609
  if (targetRepeatStateStreak >= 2) {
7554
7610
  score += 0.04 + Math.min(0.08, targetRepeatStateStreak * 0.015);
7555
7611
  }
7612
+ if (messageLengthTrend === "shortening" && messageLengthSlope < -0.3) {
7613
+ score += 0.08;
7614
+ }
7556
7615
  if (text.length < 30)
7557
7616
  score += 0.06;
7558
7617
  else if (text.length < 80)
@@ -7686,20 +7745,34 @@ function isLikelyOffTopic(userText, job) {
7686
7745
 
7687
7746
  // src/lib/turn-classify.js
7688
7747
  init_vibeultrax();
7748
+ var _lastClassifiedByApi = false;
7689
7749
  function classifyTurnSimple2(userText) {
7690
7750
  return classifyTurnSimple(userText);
7691
7751
  }
7692
7752
  async function classifyTurnRemote(text) {
7693
7753
  try {
7694
7754
  const client2 = getApiClient2();
7695
- if (!client2 || isApiFallback2())
7755
+ if (!client2 || isApiFallback2()) {
7756
+ _lastClassifiedByApi = false;
7696
7757
  return classifyTurnSimple(text);
7697
- const res = await client2.classifyQuery(text);
7758
+ }
7759
+ const res = await client2.blackboxAnalyze(_OC_SID, {
7760
+ session_id: _OC_SID,
7761
+ project_id: currentProjectFingerprint || null,
7762
+ userText: text,
7763
+ lastRegime: null,
7764
+ lastIntent: "",
7765
+ lastAction: "",
7766
+ stress: 0,
7767
+ state: {}
7768
+ });
7698
7769
  if (res && typeof res === "object" && "sub_regime" in res) {
7770
+ _lastClassifiedByApi = true;
7699
7771
  return res.sub_regime;
7700
7772
  }
7701
7773
  } catch {
7702
7774
  }
7775
+ _lastClassifiedByApi = false;
7703
7776
  return classifyTurnSimple(text);
7704
7777
  }
7705
7778
  function getVibeOSHome5() {
@@ -7916,19 +7989,22 @@ function summarizeRecentToolActivity(limit = 5) {
7916
7989
  if (events.length === 0)
7917
7990
  return null;
7918
7991
  const last = events[events.length - 1] || {};
7919
- const signature = `${String(last.tool || "").trim().toLowerCase()}:${String(last.target || "").trim().toLowerCase()}`;
7992
+ const actionType = String(last.action || last.kind || "").trim().toLowerCase();
7993
+ const toolTarget = `${String(last.tool || "").trim().toLowerCase()}:${String(last.target || "").trim().toLowerCase()}`;
7994
+ const signature = `${toolTarget}:${actionType}`;
7920
7995
  let repeatCount = 0;
7921
7996
  for (let i = events.length - 1; i >= 0; i--) {
7922
7997
  const cur = events[i] || {};
7923
- const curSignature = `${String(cur.tool || "").trim().toLowerCase()}:${String(cur.target || "").trim().toLowerCase()}`;
7924
- if (curSignature !== signature)
7998
+ const curAction = String(cur.action || cur.kind || "").trim().toLowerCase();
7999
+ const curSig = `${String(cur.tool || "").trim().toLowerCase()}:${String(cur.target || "").trim().toLowerCase()}:${curAction}`;
8000
+ if (curSig !== signature)
7925
8001
  break;
7926
8002
  repeatCount++;
7927
8003
  }
7928
8004
  return {
7929
8005
  tool: String(last.tool || "").toLowerCase(),
7930
8006
  target: String(last.target || "").toLowerCase(),
7931
- action: String(last.action || last.kind || "").toLowerCase(),
8007
+ action: actionType,
7932
8008
  signature,
7933
8009
  repeat_count: repeatCount,
7934
8010
  recent_count: events.length
@@ -16007,7 +16083,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
16007
16083
  globalThis.__vibeOS_sessionId = `opencode-${process.pid || "x"}-${Date.now()}`;
16008
16084
  }
16009
16085
  const hookSessionId = globalThis.__vibeOS_sessionId;
16010
- setVibeOSHomeContext(join18(hookHome, ".claude"));
16086
+ setVibeOSHomeContext(getVibeOSHome13());
16011
16087
  setCurrentSessionId(hookSessionId);
16012
16088
  if (hookFp) {
16013
16089
  setCurrentProjectFingerprint(hookFp);
@@ -16086,7 +16162,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
16086
16162
  briefedProjects.clear();
16087
16163
  activeJob2 = _loadActiveJobForProject(directory3, fp);
16088
16164
  const systemBriefedProjects = /* @__PURE__ */ new Set();
16089
- const hookVibeHome = join18(hookHome, ".claude");
16165
+ const hookVibeHome = getVibeOSHome13();
16090
16166
  const hookStateFile = join18(hookVibeHome, "delegation-state.json");
16091
16167
  const hookProjectStateFile = join18(hookVibeHome, "project-states.json");
16092
16168
  const hookReportsDir = join18(hookVibeHome, "reports");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeostheog",
3
- "version": "0.25.22",
3
+ "version": "0.25.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",
@@ -35,12 +35,19 @@ function collectHomeOpenCodeHomes(baseHome) {
35
35
  export function resolveOpenCodeHomes({ cwd = process.cwd(), home = homedir() } = {}) {
36
36
  const override = process.env.VIBEOS_OPENCODE_HOME
37
37
  if (override) return [override]
38
+
38
39
  const workspaceHomes = collectWorkspaceOpenCodeHomes(cwd)
39
- if (workspaceHomes.length > 0) return workspaceHomes
40
40
  const homeHomes = collectHomeOpenCodeHomes(home)
41
41
  const activeHomeHomes = homeHomes.filter((dir) => existsSync(dir))
42
- if (activeHomeHomes.length > 0) return activeHomeHomes
43
- return homeHomes
42
+
43
+ const homeCandidates = activeHomeHomes.length > 0 ? activeHomeHomes : homeHomes
44
+ const seen = new Set()
45
+ return [...workspaceHomes, ...homeCandidates].filter((dir) => {
46
+ if (dir == null) return false
47
+ if (seen.has(dir)) return false
48
+ seen.add(dir)
49
+ return true
50
+ })
44
51
  }
45
52
 
46
53
  export function resolveOpenCodeHome(opts = {}) {