vibeostheog 0.25.40 → 0.25.43

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,4 +1,22 @@
1
- ## 0.25.40
1
+ ## 0.25.43
2
+ - fix: restore general friction detection in semantic observer
3
+ - fix: no cascade override when prompt has media
4
+ - fix: remove vibelitex medium floor that overrides API target (#262)
5
+ - fix: structural claim grammar with auto-verify and auto-amend turn (#261)
6
+ - fix: cascade trigger and claim grammar auto-verify (#260)
7
+ - fix: cascade now fires regardless of API route target, graph prediction respects existing _target (#257)
8
+ - fix: ML trigger cascade now maps brain to TRINITY_BRAIN, lowers override threshold to 0.7, allows graph prediction override (#255)
9
+ - fix: proper env-isolated contract tests for setup.js bin (#254) (#254)
10
+ - fix: remove duplicate prompt - npx is the single permission gate (#253)
11
+ - fix: update usage line to recommend npx -y (#252) (#252)
12
+ - fix: add permission prompt to setup.js + contract tests for install flow (#251) (#251)
13
+ quality-first: default mode is quality, not budget
14
+
15
+
16
+ ## 0.25.42
17
+ - fix: structural claim grammar with auto-verify and auto-amend turn (#261)
18
+ - fix: cascade trigger and claim grammar auto-verify (#260)
19
+ - fix: cascade now fires regardless of API route target, graph prediction respects existing _target (#257)
2
20
  - fix: ML trigger cascade now maps brain to TRINITY_BRAIN, lowers override threshold to 0.7, allows graph prediction override (#255)
3
21
  - fix: proper env-isolated contract tests for setup.js bin (#254) (#254)
4
22
  - fix: remove duplicate prompt - npx is the single permission gate (#253)
@@ -1 +1 @@
1
- window.__VIBEOS_DASHBOARD_BASE__ = "http://127.0.0.1:34563";
1
+ window.__VIBEOS_DASHBOARD_BASE__ = "http://127.0.0.1:58212";
package/dist/vibeOS.js CHANGED
@@ -213,6 +213,15 @@ function commandFamily(command) {
213
213
  const first = c.replace(/^[a-z_][a-z0-9_]*=\S+\s+/g, "").split(/\s+/)[0];
214
214
  return /^[a-z0-9._/-]{1,30}$/.test(first) ? first : "command";
215
215
  }
216
+ function commandFailed(output) {
217
+ const code = output?.exitCode ?? output?.statusCode ?? output?.code;
218
+ if (Number.isFinite(Number(code)) && Number(code) !== 0)
219
+ return true;
220
+ const raw = output?.result ?? output?.text ?? output?.content ?? output?.data ?? "";
221
+ if (typeof raw !== "string")
222
+ return false;
223
+ return /\b(exit code|exited with code)\s*[:=]?\s*[1-9]\b|\b(assertionerror|syntaxerror|typeerror|referenceerror)\b|\b(failed|error:|err!)\b/i.test(raw);
224
+ }
216
225
  function mergeProjectBucket(dst, src) {
217
226
  const a = dst || {};
218
227
  const b = src || {};
@@ -5114,7 +5123,7 @@ var init_vibeultrax = __esm({
5114
5123
 
5115
5124
  // src/index.ts
5116
5125
  init_flow_enforcer();
5117
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync17, existsSync as existsSync19, mkdirSync as mkdirSync15, copyFileSync as copyFileSync2, renameSync as renameSync6, statSync as statSync9 } from "node:fs";
5126
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync17, existsSync as existsSync20, mkdirSync as mkdirSync15, copyFileSync as copyFileSync2, renameSync as renameSync6, statSync as statSync10 } from "node:fs";
5118
5127
  import { join as join19, dirname as dirname13, basename as basename5 } from "node:path";
5119
5128
 
5120
5129
  // src/vibeOS-lib/session-metrics.js
@@ -11236,7 +11245,7 @@ async function probeModel(modelId, auth, providers = null) {
11236
11245
  }
11237
11246
 
11238
11247
  // src/lib/hooks/footer.js
11239
- import { readFileSync as readFileSync15, appendFileSync as appendFileSync5, mkdirSync as mkdirSync12 } from "node:fs";
11248
+ import { readFileSync as readFileSync15, appendFileSync as appendFileSync5, existsSync as existsSync16, mkdirSync as mkdirSync12, statSync as statSync8 } from "node:fs";
11240
11249
  import { join as join16 } from "node:path";
11241
11250
 
11242
11251
  // src/lib/hooks/chat-transform.js
@@ -11244,6 +11253,7 @@ init_state();
11244
11253
  import { readFileSync as readFileSync14, writeFileSync as writeFileSync14, appendFileSync as appendFileSync4, existsSync as existsSync15, mkdirSync as mkdirSync11, rmSync as rmSync5, readdirSync as readdirSync3, statSync as statSync7 } from "node:fs";
11245
11254
  import { join as join15, dirname as dirname10, basename as basename3 } from "node:path";
11246
11255
  import { createHash as createHash3 } from "node:crypto";
11256
+ import { homedir as homedir6 } from "node:os";
11247
11257
 
11248
11258
  // src/lib/mode-policy.js
11249
11259
  init_state();
@@ -11470,7 +11480,8 @@ function deriveTags(input, output) {
11470
11480
  isGuardBreach: deriveRole(input?.name || "", input, output) === "bypass",
11471
11481
  isProtectedTarget: typeof cmd === "string" && targetsProtectedBranch(cmd),
11472
11482
  exitCode: output?.exitCode ?? output?.statusCode ?? output?.code ?? null,
11473
- family: commandFamily(cmd)
11483
+ family: commandFamily(cmd),
11484
+ isFailed: commandFailed(output)
11474
11485
  };
11475
11486
  }
11476
11487
  function getSessionEventLogPath(sid) {
@@ -11581,6 +11592,43 @@ function detectPatterns(events, fingerprint) {
11581
11592
  });
11582
11593
  }
11583
11594
  }
11595
+ const seen = {};
11596
+ for (let i = 0; i < events.length; i++) {
11597
+ const e = events[i];
11598
+ if (!e.isFailed || e.family === "unknown" || e.family === "command")
11599
+ continue;
11600
+ if (i > 0 && events[i - 1].family === e.family && events[i - 1].isFailed) {
11601
+ const key = "friction:repeat-fail:" + e.family;
11602
+ if (!seen[key]) {
11603
+ seen[key] = true;
11604
+ patterns.push({
11605
+ key,
11606
+ summary: e.family + " failed repeatedly \u2014 possible systematic issue.",
11607
+ kind: "friction"
11608
+ });
11609
+ }
11610
+ }
11611
+ }
11612
+ for (let i = 1; i < events.length; i++) {
11613
+ const ver = events[i];
11614
+ if (ver.role !== "verification" || !ver.isFailed)
11615
+ continue;
11616
+ const prev = events[i - 1];
11617
+ if (prev.role !== "mutation")
11618
+ continue;
11619
+ const diff = ver.at - prev.at;
11620
+ if (diff >= 0 && diff <= 3 * 60 * 1e3) {
11621
+ const key = "friction:post-edit-fail:" + ver.family;
11622
+ if (!seen[key]) {
11623
+ seen[key] = true;
11624
+ patterns.push({
11625
+ key,
11626
+ summary: "Edit followed by " + ver.family + " failure \u2014 check your changes.",
11627
+ kind: "friction"
11628
+ });
11629
+ }
11630
+ }
11631
+ }
11584
11632
  return patterns;
11585
11633
  }
11586
11634
  function getCurrentSid() {
@@ -12616,6 +12664,90 @@ var onMessagesTransform = async (_input, output) => {
12616
12664
  injectWBP(messages);
12617
12665
  applyDecadence();
12618
12666
  await trackBlackbox(messages);
12667
+ try {
12668
+ if (!Array.isArray(messages) || Object.isFrozen(messages))
12669
+ return;
12670
+ const vibeHome = process.env.VIBEOS_HOME || join15(process.env.HOME || homedir6(), ".claude");
12671
+ if (typeof vibeHome !== "string" || vibeHome.length === 0)
12672
+ return;
12673
+ const claimFile = join15(vibeHome, "cascade-audit", "claim-audit.jsonl");
12674
+ const cascadeFile = join15(vibeHome, "cascade-audit", "cascade-audit.jsonl");
12675
+ if (!existsSync15(claimFile))
12676
+ return;
12677
+ const st = statSync7(claimFile);
12678
+ if (!st || st.size <= 0)
12679
+ return;
12680
+ const claimRaw = readFileSync14(claimFile, "utf-8");
12681
+ if (!claimRaw || typeof claimRaw !== "string")
12682
+ return;
12683
+ const claimLines = claimRaw.trim().split("\n").filter(Boolean).slice(-5);
12684
+ if (claimLines.length === 0)
12685
+ return;
12686
+ const cascadeRaw = existsSync15(cascadeFile) ? readFileSync14(cascadeFile, "utf-8") : "";
12687
+ const cascadeLines = cascadeRaw && typeof cascadeRaw === "string" ? cascadeRaw.trim().split("\n").filter(Boolean).slice(-20) : [];
12688
+ const cascadeRuns = cascadeLines.map((l) => {
12689
+ try {
12690
+ return JSON.parse(l);
12691
+ } catch {
12692
+ return null;
12693
+ }
12694
+ }).filter(Boolean);
12695
+ const unsubClaims = [];
12696
+ let lastInjectTs = 0;
12697
+ for (let i = messages.length - 1; i >= Math.max(0, messages.length - 4); i--) {
12698
+ const m = messages[i];
12699
+ if (!m || typeof m !== "object")
12700
+ continue;
12701
+ if (m.role === "assistant" && Array.isArray(m.parts)) {
12702
+ for (const p of m.parts) {
12703
+ if (p && typeof p === "object" && p.type === "text" && typeof p.text === "string" && p.text.includes("[verify]")) {
12704
+ lastInjectTs = Date.now();
12705
+ }
12706
+ }
12707
+ }
12708
+ }
12709
+ if (Date.now() - lastInjectTs < 3e4)
12710
+ return;
12711
+ for (const cl of claimLines) {
12712
+ if (!cl.trim())
12713
+ continue;
12714
+ let entry;
12715
+ try {
12716
+ entry = JSON.parse(cl);
12717
+ } catch {
12718
+ continue;
12719
+ }
12720
+ if (!entry || typeof entry !== "object")
12721
+ continue;
12722
+ const claimTexts = (entry.claims || []).filter(Boolean).map((c) => typeof c === "object" && c.text ? c.text : "").filter(Boolean).join(" | ");
12723
+ if (!claimTexts)
12724
+ continue;
12725
+ if (!/(?:I|we|the)\s+(?:pushed|released|merged|deployed|fixed)\b|(?:tests?|build|CI|checks?)\s+(?:is\s+|are\s+)?(?:pass|green|clean)\b|v\d+\.\d+\.\d+|done|fixed|works|exit\s*code\s*0|\d+%|score|passed/i.test(claimTexts))
12726
+ continue;
12727
+ let cascadeMatch = false;
12728
+ for (const cr of cascadeRuns) {
12729
+ const cTs = typeof cr === "object" && cr ? cr._ts || "" : "";
12730
+ if (cTs && entry.ts && Math.abs(new Date(cTs).getTime() - new Date(entry.ts).getTime()) < 12e4) {
12731
+ cascadeMatch = true;
12732
+ break;
12733
+ }
12734
+ }
12735
+ if (!cascadeMatch) {
12736
+ for (const c of entry.claims || []) {
12737
+ if (c && typeof c === "object" && c.text)
12738
+ unsubClaims.push(c.text);
12739
+ }
12740
+ }
12741
+ }
12742
+ if (unsubClaims.length > 0 && !Object.isFrozen(messages)) {
12743
+ const verifyText = "\n[vibeOS verify]\nUnsubstantiated claims from previous turn:\n" + unsubClaims.slice(0, 5).map((t) => ' - "' + (typeof t === "string" ? t.substring(0, 80) : "") + '"').join("\n") + "\nPlease verify each claim and correct if inaccurate.";
12744
+ try {
12745
+ messages.push({ role: "assistant", parts: [{ type: "text", text: verifyText, synthetic: true }] });
12746
+ } catch {
12747
+ }
12748
+ }
12749
+ } catch {
12750
+ }
12619
12751
  } catch (err) {
12620
12752
  console.error(`[vibeOS] messages.transform failed: ${err.message}`);
12621
12753
  }
@@ -13429,15 +13561,51 @@ async function _appendFooter(input, output, directory3) {
13429
13561
  const rawMode = (typeof loadSelection2 === "function" ? loadSelection2()?.requested_optimization_mode || loadSelection2()?.optimization_mode : null) || displayMode;
13430
13562
  const cv = computeControlVector2({ sub_regime: currentSubRegime, latest_stress_multiplier: _footerStress, user_text: latestUserIntent || "" }, void 0, rawMode);
13431
13563
  const vibeBrand = resolveBrand(loadOptimizationMode() || displayMode, activeSlot);
13432
- const _cp = [/(?:\u005c|['"](?:done|fixed|validated|works|verified|solved|resolved)['"]|\d+%|score|passed)/i];
13433
13564
  let _claimTag = "";
13434
- if (text) {
13435
- for (const _p of _cp) {
13436
- if (_p.test(text)) {
13437
- _claimTag = "[CLAIMS]";
13438
- break;
13565
+ try {
13566
+ const claimAuditFile = join16(VIBEOS_HOME, "cascade-audit", "claim-audit.jsonl");
13567
+ const cascadeAuditFile = join16(VIBEOS_HOME, "cascade-audit", "cascade-audit.jsonl");
13568
+ if (existsSync16(claimAuditFile) && statSync8(claimAuditFile).size > 0) {
13569
+ const claimLines = readFileSync15(claimAuditFile, "utf-8").trim().split("\n").slice(-10);
13570
+ const cascadeLines = existsSync16(cascadeAuditFile) ? readFileSync15(cascadeAuditFile, "utf-8").trim().split("\n").slice(-30) : [];
13571
+ const cascadeRuns = cascadeLines.filter(Boolean).map(function(l) {
13572
+ try {
13573
+ return JSON.parse(l);
13574
+ } catch {
13575
+ }
13576
+ }).filter(Boolean);
13577
+ let unsub = 0;
13578
+ for (const cl of claimLines) {
13579
+ if (!cl.trim())
13580
+ continue;
13581
+ let entry;
13582
+ try {
13583
+ entry = JSON.parse(cl);
13584
+ } catch {
13585
+ continue;
13586
+ }
13587
+ if (!entry)
13588
+ continue;
13589
+ const claimTexts = (entry.claims || []).map(function(c) {
13590
+ return c.text;
13591
+ }).join(" | ");
13592
+ let cascadeMatch = false;
13593
+ for (const cr of cascadeRuns) {
13594
+ const cTs = cr._ts || "";
13595
+ if (cTs && entry.ts && Math.abs(new Date(cTs).getTime() - new Date(entry.ts).getTime()) < 12e4) {
13596
+ cascadeMatch = true;
13597
+ break;
13598
+ }
13599
+ }
13600
+ if (!cascadeMatch)
13601
+ unsub++;
13439
13602
  }
13603
+ if (unsub > 0)
13604
+ _claimTag = "\u26A0" + unsub;
13605
+ else
13606
+ _claimTag = "\u2713";
13440
13607
  }
13608
+ } catch {
13441
13609
  }
13442
13610
  const vibeLine = buildFooterLine({
13443
13611
  activeSlot,
@@ -13513,7 +13681,7 @@ ${vibeLine} \u2014`);
13513
13681
 
13514
13682
  // src/lib/hooks/tool-execute.js
13515
13683
  init_state();
13516
- import { writeFileSync as writeFileSync16, appendFileSync as appendFileSync7, existsSync as existsSync17, mkdirSync as mkdirSync14 } from "node:fs";
13684
+ import { writeFileSync as writeFileSync16, appendFileSync as appendFileSync7, existsSync as existsSync18, mkdirSync as mkdirSync14 } from "node:fs";
13517
13685
  import { join as join18, dirname as dirname12, basename as basename4 } from "node:path";
13518
13686
  import { createHash as createHash5 } from "node:crypto";
13519
13687
  init_selection_manager();
@@ -13583,7 +13751,7 @@ init_smart_cache();
13583
13751
 
13584
13752
  // src/lib/tdd-enforcer.js
13585
13753
  init_state();
13586
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync15, appendFileSync as appendFileSync6, existsSync as existsSync16, mkdirSync as mkdirSync13, statSync as statSync8, readdirSync as readdirSync4, rmSync as rmSync6, openSync as openSync3 } from "node:fs";
13754
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync15, appendFileSync as appendFileSync6, existsSync as existsSync17, mkdirSync as mkdirSync13, statSync as statSync9, readdirSync as readdirSync4, rmSync as rmSync6, openSync as openSync3 } from "node:fs";
13587
13755
  import { join as join17, dirname as dirname11 } from "node:path";
13588
13756
  import { createHash as createHash4 } from "node:crypto";
13589
13757
 
@@ -14727,7 +14895,7 @@ function _detectTestFramework() {
14727
14895
  try {
14728
14896
  const root = directory || process.cwd();
14729
14897
  const pkgPath = join17(root, "package.json");
14730
- if (existsSync16(pkgPath)) {
14898
+ if (existsSync17(pkgPath)) {
14731
14899
  const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
14732
14900
  const testScript = String(pkg?.scripts?.test || "");
14733
14901
  const deps = { ...pkg?.devDependencies, ...pkg?.dependencies };
@@ -14749,7 +14917,7 @@ function _detectTestFramework() {
14749
14917
  const testDirs = ["src/tests", "tests", "test", "__tests__"];
14750
14918
  for (const td of testDirs) {
14751
14919
  const dirPath = join17(root, td);
14752
- if (!existsSync16(dirPath))
14920
+ if (!existsSync17(dirPath))
14753
14921
  continue;
14754
14922
  const files = readdirSync4(dirPath).filter((f) => /\.test\./.test(f) || /\.spec\./.test(f));
14755
14923
  if (files.length > 0) {
@@ -14796,7 +14964,7 @@ function _acquireLock(testPath) {
14796
14964
  if (err.code !== "EEXIST")
14797
14965
  return false;
14798
14966
  try {
14799
- const st = statSync8(lockPath);
14967
+ const st = statSync9(lockPath);
14800
14968
  if (Date.now() - st.mtimeMs >= LOCK_EXPIRE_MS) {
14801
14969
  rmSync6(lockPath, { force: true });
14802
14970
  try {
@@ -14823,7 +14991,7 @@ function _releaseLock(testPath) {
14823
14991
  }
14824
14992
  function _isInCooldown(testPath) {
14825
14993
  try {
14826
- if (!existsSync16(ENFORCEMENT_COOLDOWN_FILE2))
14994
+ if (!existsSync17(ENFORCEMENT_COOLDOWN_FILE2))
14827
14995
  return false;
14828
14996
  const hash = createHash4("sha256").update(testPath).digest("hex").slice(0, 16);
14829
14997
  const lines = readFileSync16(ENFORCEMENT_COOLDOWN_FILE2, "utf-8").trim().split("\n").filter(Boolean);
@@ -14917,7 +15085,7 @@ function enforceTestFile(filePath) {
14917
15085
  console.error(`[vibeOS] [tdd-enforce] enforceTestFile called for ${filePath}`);
14918
15086
  let sourceContent = "";
14919
15087
  try {
14920
- if (existsSync16(filePath)) {
15088
+ if (existsSync17(filePath)) {
14921
15089
  sourceContent = readFileSync16(filePath, "utf-8");
14922
15090
  }
14923
15091
  } catch {
@@ -14926,7 +15094,7 @@ function enforceTestFile(filePath) {
14926
15094
  const skeleton = buildTestSkeleton(filePath, sourceContent, { strict: sel.tdd_strict !== false, quality: sel.tdd_quality !== false });
14927
15095
  if (!skeleton)
14928
15096
  return null;
14929
- if (existsSync16(skeleton.path))
15097
+ if (existsSync17(skeleton.path))
14930
15098
  return null;
14931
15099
  if (_enforcementCooldown.has(skeleton.path))
14932
15100
  return null;
@@ -15278,14 +15446,14 @@ ${argsJson}
15278
15446
  const sessionDir = getSessionScratchpadDir();
15279
15447
  const globalDir = SCRATCHPAD_GLOBAL_DIR;
15280
15448
  const ptrPath = join18(sessionDir, `${curHash}.ptr`);
15281
- if (!existsSync17(ptrPath)) {
15449
+ if (!existsSync18(ptrPath)) {
15282
15450
  for (const similar of prediction.similarEntries) {
15283
15451
  const targetHash = similar.entry.hash;
15284
15452
  if (targetHash.length < 16)
15285
15453
  continue;
15286
15454
  const cachedFile = join18(sessionDir, `${targetHash}.txt`);
15287
15455
  const globalFile = join18(globalDir, `${targetHash}.txt`);
15288
- if (existsSync17(cachedFile) || existsSync17(globalFile)) {
15456
+ if (existsSync18(cachedFile) || existsSync18(globalFile)) {
15289
15457
  ensureSessionScratchpadDirs();
15290
15458
  writeFileSync16(ptrPath, JSON.stringify({
15291
15459
  contentHash: targetHash,
@@ -15346,16 +15514,13 @@ ${argsJson}
15346
15514
  const _exploratoryTarget = EXPLORATORY.has(_firstWord2) ? TRINITY_CHEAP : null;
15347
15515
  const _tierTarget = currentTier === "high" && TRINITY_MEDIUM && TRINITY_MEDIUM !== currentModel ? TRINITY_MEDIUM : TRINITY_CHEAP && TRINITY_CHEAP !== currentModel ? TRINITY_CHEAP : null;
15348
15516
  let _target = _exploratoryTarget ?? _tierTarget;
15517
+ const _hasMedia = /\.(png|jpg|jpeg|gif|webp|bmp|svg|mp4|webm|ogg|mp3|wav|avi|mov)/i.test(_prompt);
15349
15518
  const stressScore = latestUserIntent ? scoreStress(latestUserIntent) : 0;
15350
15519
  const apiRoute = await remoteCall("routeModel", [_prompt, currentTier, TRINITY_CHEAP, TRINITY_MEDIUM, LEARNED_EXPLORATORY, stressScore], null);
15351
15520
  if (apiRoute?.target) {
15352
15521
  _target = apiRoute.target;
15353
- if (currentTier === "high" && !_exploratoryTarget && TRINITY_MEDIUM && _target === TRINITY_CHEAP) {
15354
- _target = TRINITY_MEDIUM;
15355
- console.error(`[vibeOS] \u{1F500} Task floor: preserving medium tier for high-tier brain task`);
15356
- }
15357
- } else if (_target === TRINITY_CHEAP && TRINITY_MEDIUM) {
15358
- if (stressScore > 0.5) {
15522
+ } else {
15523
+ if (_target === TRINITY_CHEAP && TRINITY_MEDIUM && stressScore > 0.5) {
15359
15524
  _target = TRINITY_MEDIUM;
15360
15525
  console.error(`[vibeOS] \u{1F9D8} Stress ${stressScore.toFixed(2)} \u2192 preserving medium tier for Task quality`);
15361
15526
  }
@@ -15367,14 +15532,14 @@ ${argsJson}
15367
15532
  const mlGraphPrediction = predictBestModel(_mlGraph, _firstWord2, currentTier);
15368
15533
  if (mlDifficulty.confidence >= ML_CONFIDENCE_THRESHOLD && mlDifficulty.level !== "moderate") {
15369
15534
  const mlTarget = mlDifficulty.suggestedTier === "cheap" ? TRINITY_CHEAP : mlDifficulty.suggestedTier === "medium" ? TRINITY_MEDIUM : mlDifficulty.suggestedTier === "brain" ? TRINITY_BRAIN : null;
15370
- if (mlTarget && mlTarget !== currentModel) {
15535
+ if (mlTarget && mlTarget !== _target) {
15371
15536
  const tierRank = { budget: 0, cheap: 1, mid: 2, medium: 2, high: 3, brain: 3 };
15372
15537
  const mlRank = tierRank[mlDifficulty.suggestedTier] || 0;
15373
15538
  const curRank = _target ? tierRank[classify(_target)] || 0 : 0;
15374
15539
  if (!_target) {
15375
15540
  _target = mlTarget;
15376
15541
  console.error(`[vibeOS] \u{1F9E0} ML difficulty: ${mlDifficulty.level} (score ${mlDifficulty.score.toFixed(2)}, conf ${mlDifficulty.confidence.toFixed(2)}) \u2192 ${mlTarget}`);
15377
- } else if (mlRank > curRank && mlDifficulty.confidence >= 0.7) {
15542
+ } else if (!_hasMedia && mlRank > curRank && mlDifficulty.confidence >= 0.7) {
15378
15543
  _target = mlTarget;
15379
15544
  console.error(`[vibeOS] \u{1F9E0} ML upgrade: ${mlDifficulty.level} (score ${mlDifficulty.score.toFixed(2)}, conf ${mlDifficulty.confidence.toFixed(2)}) \u2192 ${mlTarget}`);
15380
15545
  }
@@ -15383,8 +15548,10 @@ ${argsJson}
15383
15548
  if (mlGraphPrediction && mlGraphPrediction !== currentModel) {
15384
15549
  const graphNode = _mlGraph.nodes[_firstWord2];
15385
15550
  if (graphNode && graphNode.count >= 3) {
15386
- _target = mlGraphPrediction;
15387
- console.error(`[vibeOS] \u{1F578} ML graph: ${_firstWord2} \u2192 ${mlGraphPrediction} (${graphNode.count} samples)`);
15551
+ if (!_target) {
15552
+ _target = mlGraphPrediction;
15553
+ console.error(`[vibeOS] \u{1F578} ML graph: ${_firstWord2} \u2192 ${mlGraphPrediction} (${graphNode.count} samples)`);
15554
+ }
15388
15555
  }
15389
15556
  }
15390
15557
  if (_target) {
@@ -15396,7 +15563,7 @@ ${argsJson}
15396
15563
  }
15397
15564
  }
15398
15565
  const activePipeline = loadSelection().active_pipeline;
15399
- if (!apiRoute?.target && activePipeline && Array.isArray(activePipeline) && activePipeline.length > 1 && TRINITY_CHEAP && TRINITY_MEDIUM) {
15566
+ if (activePipeline && Array.isArray(activePipeline) && activePipeline.length > 1 && TRINITY_CHEAP && TRINITY_MEDIUM) {
15400
15567
  try {
15401
15568
  const cheapCost = 1e-3;
15402
15569
  const mediumCost = 5e-3;
@@ -15407,13 +15574,13 @@ ${argsJson}
15407
15574
  if (cascadeResult.escalate && pipelineModels.length > 1) {
15408
15575
  if (pipelineModels.length > 2 && cascadeResult.confidence >= 0.8) {
15409
15576
  const escalated = pipelineModels[2];
15410
- if (escalated && escalated !== currentModel && (!_target || escalated !== _target)) {
15577
+ if (escalated && escalated !== currentModel && !_hasMedia && (!_target || escalated !== _target)) {
15411
15578
  _target = escalated;
15412
15579
  console.error(`[vibeOS] \u{1F500} Cascade depth-3 escalate: ${cascadeResult.reason} \u2192 ${escalated}`);
15413
15580
  }
15414
15581
  } else {
15415
15582
  const escalated = pipelineModels[1];
15416
- if (escalated && escalated !== currentModel && (!_target || escalated !== _target)) {
15583
+ if (escalated && escalated !== currentModel && !_target) {
15417
15584
  _target = escalated;
15418
15585
  console.error(`[vibeOS] \u{1F500} Cascade escalate: ${cascadeResult.reason} \u2192 ${escalated}`);
15419
15586
  }
@@ -15592,7 +15759,7 @@ ${argsJson}
15592
15759
  }
15593
15760
  } else {
15594
15761
  const missed = recordMissedContext7(_estC7);
15595
- if (!existsSync17(CONTEXT7_INSTALL_FLAG)) {
15762
+ if (!existsSync18(CONTEXT7_INSTALL_FLAG)) {
15596
15763
  try {
15597
15764
  mkdirSync14(dirname12(CONTEXT7_INSTALL_FLAG), { recursive: true });
15598
15765
  writeFileSync16(CONTEXT7_INSTALL_FLAG, "");
@@ -16035,7 +16202,7 @@ ${pendingUiNote}`;
16035
16202
 
16036
16203
  // src/lib/hooks/session-compact.js
16037
16204
  init_state();
16038
- import { readFileSync as readFileSync17, existsSync as existsSync18 } from "node:fs";
16205
+ import { readFileSync as readFileSync17, existsSync as existsSync19 } from "node:fs";
16039
16206
  var onSessionCompacting = async (_input, output) => {
16040
16207
  if (!loadSelection().enabled)
16041
16208
  return;
@@ -16044,7 +16211,7 @@ var onSessionCompacting = async (_input, output) => {
16044
16211
  const needsCompact = turnCount >= 7;
16045
16212
  const indexPath = getSessionIndexPath();
16046
16213
  let recent = "";
16047
- if (existsSync18(indexPath)) {
16214
+ if (existsSync19(indexPath)) {
16048
16215
  try {
16049
16216
  const lines = readFileSync17(indexPath, "utf-8").trim().split("\n").slice(-30);
16050
16217
  recent = lines.map((l) => {
@@ -16150,11 +16317,15 @@ function ensureDeferredBootstrap() {
16150
16317
  }
16151
16318
  }
16152
16319
  var CLAIM_PATTERNS = [
16320
+ /(?:I|we|the)\s+(?:pushed|released|merged|deployed|fixed|wrote|implemented|completed|committed)\b/i,
16321
+ /(?:tests?|build|CI|checks?|suite|output|result)\s+(?:is\s+|are\s+)?(?:pass(?:ing|ed|es)?|green|clean|succeed|stable|positive)/i,
16322
+ /v\d+\.\d+\.\d+/,
16323
+ /\d+\s*(?:test|spec)s?\s*(?:pass|passing)/i,
16153
16324
  /(?:done|finished|complete)/i,
16154
16325
  /(?:fixed|resolved|solved)/i,
16155
- /(?:working|works|validated|verified)/i,
16156
- /(?:[0-9]+\.[0-9]?%|\d+%)/,
16157
- /(?:score|scored|passing|passed)/i
16326
+ /(?:works|working|validated|verified)/i,
16327
+ /(?:exit\s*code\s*0|0\s*errors|0\s*failures)/i,
16328
+ /(?:\d+%|score|scored|passing|passed)/i
16158
16329
  ];
16159
16330
  function scanClaimsInOutput(output) {
16160
16331
  if (!output || typeof output !== "string") return;
@@ -16175,7 +16346,6 @@ function scanClaimsInOutput(output) {
16175
16346
  claims: claims.slice(0, 10),
16176
16347
  totalClaims: claims.length,
16177
16348
  responseHash: ""
16178
- // crypto hash skipped for performance
16179
16349
  });
16180
16350
  appendFileSync(auditFile, entry + String.fromCharCode(10));
16181
16351
  } catch {
@@ -16196,9 +16366,9 @@ var _runDeferredStartupBootstrap = null;
16196
16366
  function _readOpenCodeConfigObject(dir) {
16197
16367
  const jsonPath = join19(dir, "opencode.json");
16198
16368
  const jsoncPath = join19(dir, "opencode.jsonc");
16199
- if (existsSync19(jsonPath))
16369
+ if (existsSync20(jsonPath))
16200
16370
  return safeJsonParse2(readFileSync18(jsonPath, "utf-8"));
16201
- if (existsSync19(jsoncPath))
16371
+ if (existsSync20(jsoncPath))
16202
16372
  return _parseJsonc(readFileSync18(jsoncPath, "utf-8"));
16203
16373
  return {};
16204
16374
  }
@@ -16251,7 +16421,7 @@ function _loadActiveJobForProject(directory3, fp2 = "") {
16251
16421
  for (const base of candidates) {
16252
16422
  try {
16253
16423
  const activeJobsPath = join19(String(base), "active-jobs.json");
16254
- if (!existsSync19(activeJobsPath))
16424
+ if (!existsSync20(activeJobsPath))
16255
16425
  continue;
16256
16426
  const jobs = safeJsonParse2(readFileSync18(activeJobsPath, "utf-8")) || {};
16257
16427
  const job = fp2 ? jobs?.[fp2] : null;
@@ -16273,9 +16443,9 @@ function _tiersNeedRepair(tiers) {
16273
16443
  async function _seedOrRepairModelTiers(directory3) {
16274
16444
  const TIERS_FILE3 = getTiersFile();
16275
16445
  let existing = null;
16276
- if (existsSync19(TIERS_FILE3)) {
16446
+ if (existsSync20(TIERS_FILE3)) {
16277
16447
  try {
16278
- const st = statSync9(TIERS_FILE3);
16448
+ const st = statSync10(TIERS_FILE3);
16279
16449
  if (st.size > 10485760) {
16280
16450
  _handleStateCorruption(TIERS_FILE3);
16281
16451
  return false;
@@ -16386,7 +16556,7 @@ function loadMcpPort() {
16386
16556
  return n;
16387
16557
  }
16388
16558
  try {
16389
- if (existsSync19(getTiersFile())) {
16559
+ if (existsSync20(getTiersFile())) {
16390
16560
  const tiers = safeJsonParse2(readFileSync18(getTiersFile(), "utf-8"));
16391
16561
  const cfg = tiers?.selection?.mcp_port;
16392
16562
  if (cfg === false || cfg === "disabled" || cfg === 0)
@@ -16401,7 +16571,7 @@ function loadMcpPort() {
16401
16571
  }
16402
16572
  function persistMcpPort(port) {
16403
16573
  try {
16404
- if (!existsSync19(getTiersFile()))
16574
+ if (!existsSync20(getTiersFile()))
16405
16575
  return;
16406
16576
  const tiers = safeJsonParse2(readFileSync18(getTiersFile(), "utf-8"));
16407
16577
  tiers.selection ??= {};
@@ -16503,7 +16673,7 @@ async function ensureMcpServerRunning() {
16503
16673
  getSessionMetrics: () => computeSessionMetrics(readFullState(), _OC_SID),
16504
16674
  getTodos: () => loadTodos(),
16505
16675
  listReports: (filter) => {
16506
- if (!existsSync19(getReportsDir2())) {
16676
+ if (!existsSync20(getReportsDir2())) {
16507
16677
  const e = new Error("reports dir not found");
16508
16678
  e.status = 404;
16509
16679
  throw e;
@@ -16716,9 +16886,9 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
16716
16886
  }
16717
16887
  } catch {
16718
16888
  }
16719
- console.error(`[vibeOS] auto-config guard: currentModel=${currentModel ? "SET" : "NONE"}, TIERS_FILE=${getTiersFile()}, exists=${existsSync19(getTiersFile())}`);
16889
+ console.error(`[vibeOS] auto-config guard: currentModel=${currentModel ? "SET" : "NONE"}, TIERS_FILE=${getTiersFile()}, exists=${existsSync20(getTiersFile())}`);
16720
16890
  try {
16721
- if (!existsSync19(getTiersFile())) {
16891
+ if (!existsSync20(getTiersFile())) {
16722
16892
  console.error(`[vibeOS] model-tiers.json missing at load; will seed on first hook`);
16723
16893
  }
16724
16894
  await _seedOrRepairModelTiers(directory3);
@@ -16778,7 +16948,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
16778
16948
  };
16779
16949
  const backupFileStable = (path, label) => {
16780
16950
  try {
16781
- if (!existsSync19(path))
16951
+ if (!existsSync20(path))
16782
16952
  return null;
16783
16953
  const bkDir = join19(hookVibeHome, ".backups");
16784
16954
  mkdirSync15(bkDir, { recursive: true });
@@ -16818,7 +16988,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
16818
16988
  safeJsonParse: safeJsonParse2,
16819
16989
  readFileSync: readFileSync18,
16820
16990
  writeFileSync: writeFileSync17,
16821
- existsSync: existsSync19,
16991
+ existsSync: existsSync20,
16822
16992
  renameSync: renameSync6,
16823
16993
  mkdirSync: mkdirSync15,
16824
16994
  get TIERS_FILE() {
@@ -16989,6 +17159,51 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
16989
17159
  ensureDeferredBootstrap();
16990
17160
  await _appendFooter(_input, output, directory3);
16991
17161
  scanClaimsInOutput(output);
17162
+ try {
17163
+ const auditDir = join19(getVibeOSHome13(), "cascade-audit");
17164
+ const claimFile = join19(auditDir, "claim-audit.jsonl");
17165
+ const cascadeFile = join19(auditDir, "cascade-audit.jsonl");
17166
+ if (existsSync20(claimFile) && statSync10(claimFile).size > 0) {
17167
+ const claimLines = readFileSync18(claimFile, "utf-8").trim().split("\n").slice(-10);
17168
+ const cascadeLines = existsSync20(cascadeFile) ? readFileSync18(cascadeFile, "utf-8").trim().split("\n").slice(-30) : [];
17169
+ const cascadeRuns = cascadeLines.filter(Boolean).map((l) => {
17170
+ try {
17171
+ return JSON.parse(l);
17172
+ } catch {
17173
+ }
17174
+ }).filter(Boolean);
17175
+ let unsub = 0;
17176
+ for (const cl of claimLines) {
17177
+ if (!cl.trim()) continue;
17178
+ let entry;
17179
+ try {
17180
+ entry = JSON.parse(cl);
17181
+ } catch {
17182
+ continue;
17183
+ }
17184
+ if (!entry) continue;
17185
+ const claimTexts = (entry.claims || []).map(function(c) {
17186
+ return c.text;
17187
+ }).join(" | ");
17188
+ if (!CLAIM_PATTERNS.some(function(p) {
17189
+ return p.test(claimTexts);
17190
+ })) continue;
17191
+ let cascadeMatch = false;
17192
+ for (const cr of cascadeRuns) {
17193
+ const cTs = cr._ts || "";
17194
+ if (cTs && entry.ts) {
17195
+ if (Math.abs(new Date(cTs).getTime() - new Date(entry.ts).getTime()) < 12e4) {
17196
+ cascadeMatch = true;
17197
+ break;
17198
+ }
17199
+ }
17200
+ }
17201
+ if (!cascadeMatch) unsub++;
17202
+ }
17203
+ _unsubstantiatedClaims = unsub;
17204
+ }
17205
+ } catch {
17206
+ }
16992
17207
  },
16993
17208
  "message.updated": async (_input, output) => {
16994
17209
  setVibeOSHomeContext(hookVibeHome);
@@ -16999,6 +17214,51 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
16999
17214
  ensureDeferredBootstrap();
17000
17215
  await _appendFooter(_input, output, directory3);
17001
17216
  scanClaimsInOutput(output);
17217
+ try {
17218
+ const auditDir = join19(getVibeOSHome13(), "cascade-audit");
17219
+ const claimFile = join19(auditDir, "claim-audit.jsonl");
17220
+ const cascadeFile = join19(auditDir, "cascade-audit.jsonl");
17221
+ if (existsSync20(claimFile) && statSync10(claimFile).size > 0) {
17222
+ const claimLines = readFileSync18(claimFile, "utf-8").trim().split("\n").slice(-10);
17223
+ const cascadeLines = existsSync20(cascadeFile) ? readFileSync18(cascadeFile, "utf-8").trim().split("\n").slice(-30) : [];
17224
+ const cascadeRuns = cascadeLines.filter(Boolean).map((l) => {
17225
+ try {
17226
+ return JSON.parse(l);
17227
+ } catch {
17228
+ }
17229
+ }).filter(Boolean);
17230
+ let unsub = 0;
17231
+ for (const cl of claimLines) {
17232
+ if (!cl.trim()) continue;
17233
+ let entry;
17234
+ try {
17235
+ entry = JSON.parse(cl);
17236
+ } catch {
17237
+ continue;
17238
+ }
17239
+ if (!entry) continue;
17240
+ const claimTexts = (entry.claims || []).map(function(c) {
17241
+ return c.text;
17242
+ }).join(" | ");
17243
+ if (!CLAIM_PATTERNS.some(function(p) {
17244
+ return p.test(claimTexts);
17245
+ })) continue;
17246
+ let cascadeMatch = false;
17247
+ for (const cr of cascadeRuns) {
17248
+ const cTs = cr._ts || "";
17249
+ if (cTs && entry.ts) {
17250
+ if (Math.abs(new Date(cTs).getTime() - new Date(entry.ts).getTime()) < 12e4) {
17251
+ cascadeMatch = true;
17252
+ break;
17253
+ }
17254
+ }
17255
+ }
17256
+ if (!cascadeMatch) unsub++;
17257
+ }
17258
+ _unsubstantiatedClaims = unsub;
17259
+ }
17260
+ } catch {
17261
+ }
17002
17262
  },
17003
17263
  tool: {
17004
17264
  trinity: tool(createTrinityTool(trinityDeps)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeostheog",
3
- "version": "0.25.40",
3
+ "version": "0.25.43",
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",