vibeostheog 0.25.42 → 0.25.45

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,18 @@
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
+
1
16
  ## 0.25.42
2
17
  - fix: structural claim grammar with auto-verify and auto-amend turn (#261)
3
18
  - fix: cascade trigger and claim grammar auto-verify (#260)
@@ -1 +1 @@
1
- window.__VIBEOS_DASHBOARD_BASE__ = "http://127.0.0.1:55491";
1
+ window.__VIBEOS_DASHBOARD_BASE__ = "http://127.0.0.1:61631";
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
@@ -7852,7 +7861,7 @@ function autoSelectMode2(subRegime, stressMultiplier) {
7852
7861
  return "audit";
7853
7862
  if (stress > QUALITY_STRESS_THRESHOLD2)
7854
7863
  return "quality";
7855
- return "vibelitex";
7864
+ return "quality";
7856
7865
  }
7857
7866
  function resolveOptimizationMode(subRegime, stressMultiplier, optimizationMode) {
7858
7867
  const normalized = String(optimizationMode || "auto").toLowerCase();
@@ -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() {
@@ -12380,6 +12428,8 @@ function syncControlSettings(cv, options = {}) {
12380
12428
  const OC_CONFIG = TRINITY_OPENCODE_CONFIG || join15(getOpenCodeHome(), "opencode.json");
12381
12429
  if (existsSync15(OC_CONFIG)) {
12382
12430
  const oc = safeJsonParse2(readFileSync14(OC_CONFIG, "utf-8"));
12431
+ if (!oc)
12432
+ return;
12383
12433
  if (oc.default_agent !== cv.agent_mode) {
12384
12434
  if (cv.agent_mode === "plan" && oc.default_agent && oc.default_agent !== "plan") {
12385
12435
  writeSelection("previous_default_agent", oc.default_agent);
@@ -12395,6 +12445,8 @@ function syncControlSettings(cv, options = {}) {
12395
12445
  const OC_CONFIG = TRINITY_OPENCODE_CONFIG || join15(getOpenCodeHome(), "opencode.json");
12396
12446
  if (existsSync15(OC_CONFIG)) {
12397
12447
  const oc = safeJsonParse2(readFileSync14(OC_CONFIG, "utf-8"));
12448
+ if (!oc)
12449
+ return;
12398
12450
  const restoreAgent = oc.default_agent === "plan" ? resolveRestorableOpenCodeAgent(currentSel) : null;
12399
12451
  if (restoreAgent && oc.default_agent === "plan") {
12400
12452
  oc.default_agent = restoreAgent;
@@ -12438,7 +12490,10 @@ function compressToolOutputs(messages) {
12438
12490
  let compressedBytes = 0;
12439
12491
  const hotStart = Math.max(0, messages.length - KEEP_HOT);
12440
12492
  for (let i = 0; i < messages.length; i++) {
12441
- const { info, parts } = messages[i];
12493
+ const msg = messages[i];
12494
+ if (!msg || typeof msg !== "object")
12495
+ continue;
12496
+ const { info, parts } = msg;
12442
12497
  if (!Array.isArray(parts))
12443
12498
  continue;
12444
12499
  const isCold = i < hotStart;
@@ -12504,7 +12559,10 @@ ${stableJson(invPart.state.input)}
12504
12559
  }
12505
12560
  function injectWBP(messages) {
12506
12561
  for (let i = 0; i < messages.length - 1; i++) {
12507
- const { info, parts } = messages[i];
12562
+ const msg = messages[i];
12563
+ if (!msg || typeof msg !== "object")
12564
+ continue;
12565
+ const { info, parts } = msg;
12508
12566
  if (!Array.isArray(parts))
12509
12567
  continue;
12510
12568
  const hasTask = parts.some((p) => p?.type === "tool" && p?.tool === "task" && p?.state?.status === "completed");
@@ -12616,6 +12674,90 @@ var onMessagesTransform = async (_input, output) => {
12616
12674
  injectWBP(messages);
12617
12675
  applyDecadence();
12618
12676
  await trackBlackbox(messages);
12677
+ try {
12678
+ if (!Array.isArray(messages) || Object.isFrozen(messages))
12679
+ return;
12680
+ const vibeHome = process.env.VIBEOS_HOME || join15(process.env.HOME || homedir6(), ".claude");
12681
+ if (typeof vibeHome !== "string" || vibeHome.length === 0)
12682
+ return;
12683
+ const claimFile = join15(vibeHome, "cascade-audit", "claim-audit.jsonl");
12684
+ const cascadeFile = join15(vibeHome, "cascade-audit", "cascade-audit.jsonl");
12685
+ if (!existsSync15(claimFile))
12686
+ return;
12687
+ const st = statSync7(claimFile);
12688
+ if (!st || st.size <= 0)
12689
+ return;
12690
+ const claimRaw = readFileSync14(claimFile, "utf-8");
12691
+ if (!claimRaw || typeof claimRaw !== "string")
12692
+ return;
12693
+ const claimLines = claimRaw.trim().split("\n").filter(Boolean).slice(-5);
12694
+ if (claimLines.length === 0)
12695
+ return;
12696
+ const cascadeRaw = existsSync15(cascadeFile) ? readFileSync14(cascadeFile, "utf-8") : "";
12697
+ const cascadeLines = cascadeRaw && typeof cascadeRaw === "string" ? cascadeRaw.trim().split("\n").filter(Boolean).slice(-20) : [];
12698
+ const cascadeRuns = cascadeLines.map((l) => {
12699
+ try {
12700
+ return JSON.parse(l);
12701
+ } catch {
12702
+ return null;
12703
+ }
12704
+ }).filter(Boolean);
12705
+ const unsubClaims = [];
12706
+ let lastInjectTs = 0;
12707
+ for (let i = messages.length - 1; i >= Math.max(0, messages.length - 4); i--) {
12708
+ const m = messages[i];
12709
+ if (!m || typeof m !== "object")
12710
+ continue;
12711
+ if (m.role === "assistant" && Array.isArray(m.parts)) {
12712
+ for (const p of m.parts) {
12713
+ if (p && typeof p === "object" && p.type === "text" && typeof p.text === "string" && p.text.includes("[verify]")) {
12714
+ lastInjectTs = Date.now();
12715
+ }
12716
+ }
12717
+ }
12718
+ }
12719
+ if (Date.now() - lastInjectTs < 3e4)
12720
+ return;
12721
+ for (const cl of claimLines) {
12722
+ if (!cl.trim())
12723
+ continue;
12724
+ let entry;
12725
+ try {
12726
+ entry = JSON.parse(cl);
12727
+ } catch {
12728
+ continue;
12729
+ }
12730
+ if (!entry || typeof entry !== "object")
12731
+ continue;
12732
+ const claimTexts = (entry.claims || []).filter(Boolean).map((c) => typeof c === "object" && c.text ? c.text : "").filter(Boolean).join(" | ");
12733
+ if (!claimTexts)
12734
+ continue;
12735
+ 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))
12736
+ continue;
12737
+ let cascadeMatch = false;
12738
+ for (const cr of cascadeRuns) {
12739
+ const cTs = typeof cr === "object" && cr ? cr._ts || "" : "";
12740
+ if (cTs && entry.ts && Math.abs(new Date(cTs).getTime() - new Date(entry.ts).getTime()) < 12e4) {
12741
+ cascadeMatch = true;
12742
+ break;
12743
+ }
12744
+ }
12745
+ if (!cascadeMatch) {
12746
+ for (const c of entry.claims || []) {
12747
+ if (c && typeof c === "object" && c.text)
12748
+ unsubClaims.push(c.text);
12749
+ }
12750
+ }
12751
+ }
12752
+ if (unsubClaims.length > 0 && !Object.isFrozen(messages)) {
12753
+ 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.";
12754
+ try {
12755
+ messages.push({ role: "assistant", parts: [{ type: "text", text: verifyText, synthetic: true }] });
12756
+ } catch {
12757
+ }
12758
+ }
12759
+ } catch {
12760
+ }
12619
12761
  } catch (err) {
12620
12762
  console.error(`[vibeOS] messages.transform failed: ${err.message}`);
12621
12763
  }
@@ -13429,15 +13571,51 @@ async function _appendFooter(input, output, directory3) {
13429
13571
  const rawMode = (typeof loadSelection2 === "function" ? loadSelection2()?.requested_optimization_mode || loadSelection2()?.optimization_mode : null) || displayMode;
13430
13572
  const cv = computeControlVector2({ sub_regime: currentSubRegime, latest_stress_multiplier: _footerStress, user_text: latestUserIntent || "" }, void 0, rawMode);
13431
13573
  const vibeBrand = resolveBrand(loadOptimizationMode() || displayMode, activeSlot);
13432
- const _cp = [/(?:\u005c|['"](?:done|fixed|validated|works|verified|solved|resolved)['"]|\d+%|score|passed)/i];
13433
13574
  let _claimTag = "";
13434
- if (text) {
13435
- for (const _p of _cp) {
13436
- if (_p.test(text)) {
13437
- _claimTag = "[CLAIMS]";
13438
- break;
13575
+ try {
13576
+ const claimAuditFile = join16(VIBEOS_HOME, "cascade-audit", "claim-audit.jsonl");
13577
+ const cascadeAuditFile = join16(VIBEOS_HOME, "cascade-audit", "cascade-audit.jsonl");
13578
+ if (existsSync16(claimAuditFile) && statSync8(claimAuditFile).size > 0) {
13579
+ const claimLines = readFileSync15(claimAuditFile, "utf-8").trim().split("\n").slice(-10);
13580
+ const cascadeLines = existsSync16(cascadeAuditFile) ? readFileSync15(cascadeAuditFile, "utf-8").trim().split("\n").slice(-30) : [];
13581
+ const cascadeRuns = cascadeLines.filter(Boolean).map(function(l) {
13582
+ try {
13583
+ return JSON.parse(l);
13584
+ } catch {
13585
+ }
13586
+ }).filter(Boolean);
13587
+ let unsub = 0;
13588
+ for (const cl of claimLines) {
13589
+ if (!cl.trim())
13590
+ continue;
13591
+ let entry;
13592
+ try {
13593
+ entry = JSON.parse(cl);
13594
+ } catch {
13595
+ continue;
13596
+ }
13597
+ if (!entry)
13598
+ continue;
13599
+ const claimTexts = (entry.claims || []).map(function(c) {
13600
+ return c.text;
13601
+ }).join(" | ");
13602
+ let cascadeMatch = false;
13603
+ for (const cr of cascadeRuns) {
13604
+ const cTs = cr._ts || "";
13605
+ if (cTs && entry.ts && Math.abs(new Date(cTs).getTime() - new Date(entry.ts).getTime()) < 12e4) {
13606
+ cascadeMatch = true;
13607
+ break;
13608
+ }
13609
+ }
13610
+ if (!cascadeMatch)
13611
+ unsub++;
13439
13612
  }
13613
+ if (unsub > 0)
13614
+ _claimTag = "\u26A0" + unsub;
13615
+ else
13616
+ _claimTag = "\u2713";
13440
13617
  }
13618
+ } catch {
13441
13619
  }
13442
13620
  const vibeLine = buildFooterLine({
13443
13621
  activeSlot,
@@ -13513,7 +13691,7 @@ ${vibeLine} \u2014`);
13513
13691
 
13514
13692
  // src/lib/hooks/tool-execute.js
13515
13693
  init_state();
13516
- import { writeFileSync as writeFileSync16, appendFileSync as appendFileSync7, existsSync as existsSync17, mkdirSync as mkdirSync14 } from "node:fs";
13694
+ import { writeFileSync as writeFileSync16, appendFileSync as appendFileSync7, existsSync as existsSync18, mkdirSync as mkdirSync14 } from "node:fs";
13517
13695
  import { join as join18, dirname as dirname12, basename as basename4 } from "node:path";
13518
13696
  import { createHash as createHash5 } from "node:crypto";
13519
13697
  init_selection_manager();
@@ -13583,7 +13761,7 @@ init_smart_cache();
13583
13761
 
13584
13762
  // src/lib/tdd-enforcer.js
13585
13763
  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";
13764
+ 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
13765
  import { join as join17, dirname as dirname11 } from "node:path";
13588
13766
  import { createHash as createHash4 } from "node:crypto";
13589
13767
 
@@ -14727,7 +14905,7 @@ function _detectTestFramework() {
14727
14905
  try {
14728
14906
  const root = directory || process.cwd();
14729
14907
  const pkgPath = join17(root, "package.json");
14730
- if (existsSync16(pkgPath)) {
14908
+ if (existsSync17(pkgPath)) {
14731
14909
  const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
14732
14910
  const testScript = String(pkg?.scripts?.test || "");
14733
14911
  const deps = { ...pkg?.devDependencies, ...pkg?.dependencies };
@@ -14749,7 +14927,7 @@ function _detectTestFramework() {
14749
14927
  const testDirs = ["src/tests", "tests", "test", "__tests__"];
14750
14928
  for (const td of testDirs) {
14751
14929
  const dirPath = join17(root, td);
14752
- if (!existsSync16(dirPath))
14930
+ if (!existsSync17(dirPath))
14753
14931
  continue;
14754
14932
  const files = readdirSync4(dirPath).filter((f) => /\.test\./.test(f) || /\.spec\./.test(f));
14755
14933
  if (files.length > 0) {
@@ -14796,7 +14974,7 @@ function _acquireLock(testPath) {
14796
14974
  if (err.code !== "EEXIST")
14797
14975
  return false;
14798
14976
  try {
14799
- const st = statSync8(lockPath);
14977
+ const st = statSync9(lockPath);
14800
14978
  if (Date.now() - st.mtimeMs >= LOCK_EXPIRE_MS) {
14801
14979
  rmSync6(lockPath, { force: true });
14802
14980
  try {
@@ -14823,7 +15001,7 @@ function _releaseLock(testPath) {
14823
15001
  }
14824
15002
  function _isInCooldown(testPath) {
14825
15003
  try {
14826
- if (!existsSync16(ENFORCEMENT_COOLDOWN_FILE2))
15004
+ if (!existsSync17(ENFORCEMENT_COOLDOWN_FILE2))
14827
15005
  return false;
14828
15006
  const hash = createHash4("sha256").update(testPath).digest("hex").slice(0, 16);
14829
15007
  const lines = readFileSync16(ENFORCEMENT_COOLDOWN_FILE2, "utf-8").trim().split("\n").filter(Boolean);
@@ -14917,7 +15095,7 @@ function enforceTestFile(filePath) {
14917
15095
  console.error(`[vibeOS] [tdd-enforce] enforceTestFile called for ${filePath}`);
14918
15096
  let sourceContent = "";
14919
15097
  try {
14920
- if (existsSync16(filePath)) {
15098
+ if (existsSync17(filePath)) {
14921
15099
  sourceContent = readFileSync16(filePath, "utf-8");
14922
15100
  }
14923
15101
  } catch {
@@ -14926,7 +15104,7 @@ function enforceTestFile(filePath) {
14926
15104
  const skeleton = buildTestSkeleton(filePath, sourceContent, { strict: sel.tdd_strict !== false, quality: sel.tdd_quality !== false });
14927
15105
  if (!skeleton)
14928
15106
  return null;
14929
- if (existsSync16(skeleton.path))
15107
+ if (existsSync17(skeleton.path))
14930
15108
  return null;
14931
15109
  if (_enforcementCooldown.has(skeleton.path))
14932
15110
  return null;
@@ -15278,14 +15456,14 @@ ${argsJson}
15278
15456
  const sessionDir = getSessionScratchpadDir();
15279
15457
  const globalDir = SCRATCHPAD_GLOBAL_DIR;
15280
15458
  const ptrPath = join18(sessionDir, `${curHash}.ptr`);
15281
- if (!existsSync17(ptrPath)) {
15459
+ if (!existsSync18(ptrPath)) {
15282
15460
  for (const similar of prediction.similarEntries) {
15283
15461
  const targetHash = similar.entry.hash;
15284
15462
  if (targetHash.length < 16)
15285
15463
  continue;
15286
15464
  const cachedFile = join18(sessionDir, `${targetHash}.txt`);
15287
15465
  const globalFile = join18(globalDir, `${targetHash}.txt`);
15288
- if (existsSync17(cachedFile) || existsSync17(globalFile)) {
15466
+ if (existsSync18(cachedFile) || existsSync18(globalFile)) {
15289
15467
  ensureSessionScratchpadDirs();
15290
15468
  writeFileSync16(ptrPath, JSON.stringify({
15291
15469
  contentHash: targetHash,
@@ -15346,16 +15524,13 @@ ${argsJson}
15346
15524
  const _exploratoryTarget = EXPLORATORY.has(_firstWord2) ? TRINITY_CHEAP : null;
15347
15525
  const _tierTarget = currentTier === "high" && TRINITY_MEDIUM && TRINITY_MEDIUM !== currentModel ? TRINITY_MEDIUM : TRINITY_CHEAP && TRINITY_CHEAP !== currentModel ? TRINITY_CHEAP : null;
15348
15526
  let _target = _exploratoryTarget ?? _tierTarget;
15527
+ const _hasMedia = /\.(png|jpg|jpeg|gif|webp|bmp|svg|mp4|webm|ogg|mp3|wav|avi|mov)/i.test(_prompt);
15349
15528
  const stressScore = latestUserIntent ? scoreStress(latestUserIntent) : 0;
15350
15529
  const apiRoute = await remoteCall("routeModel", [_prompt, currentTier, TRINITY_CHEAP, TRINITY_MEDIUM, LEARNED_EXPLORATORY, stressScore], null);
15351
15530
  if (apiRoute?.target) {
15352
15531
  _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) {
15532
+ } else {
15533
+ if (_target === TRINITY_CHEAP && TRINITY_MEDIUM && stressScore > 0.5) {
15359
15534
  _target = TRINITY_MEDIUM;
15360
15535
  console.error(`[vibeOS] \u{1F9D8} Stress ${stressScore.toFixed(2)} \u2192 preserving medium tier for Task quality`);
15361
15536
  }
@@ -15367,14 +15542,14 @@ ${argsJson}
15367
15542
  const mlGraphPrediction = predictBestModel(_mlGraph, _firstWord2, currentTier);
15368
15543
  if (mlDifficulty.confidence >= ML_CONFIDENCE_THRESHOLD && mlDifficulty.level !== "moderate") {
15369
15544
  const mlTarget = mlDifficulty.suggestedTier === "cheap" ? TRINITY_CHEAP : mlDifficulty.suggestedTier === "medium" ? TRINITY_MEDIUM : mlDifficulty.suggestedTier === "brain" ? TRINITY_BRAIN : null;
15370
- if (mlTarget && mlTarget !== currentModel) {
15545
+ if (mlTarget && mlTarget !== _target) {
15371
15546
  const tierRank = { budget: 0, cheap: 1, mid: 2, medium: 2, high: 3, brain: 3 };
15372
15547
  const mlRank = tierRank[mlDifficulty.suggestedTier] || 0;
15373
15548
  const curRank = _target ? tierRank[classify(_target)] || 0 : 0;
15374
15549
  if (!_target) {
15375
15550
  _target = mlTarget;
15376
15551
  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) {
15552
+ } else if (!_hasMedia && mlRank > curRank && mlDifficulty.confidence >= 0.7) {
15378
15553
  _target = mlTarget;
15379
15554
  console.error(`[vibeOS] \u{1F9E0} ML upgrade: ${mlDifficulty.level} (score ${mlDifficulty.score.toFixed(2)}, conf ${mlDifficulty.confidence.toFixed(2)}) \u2192 ${mlTarget}`);
15380
15555
  }
@@ -15383,8 +15558,10 @@ ${argsJson}
15383
15558
  if (mlGraphPrediction && mlGraphPrediction !== currentModel) {
15384
15559
  const graphNode = _mlGraph.nodes[_firstWord2];
15385
15560
  if (graphNode && graphNode.count >= 3) {
15386
- _target = mlGraphPrediction;
15387
- console.error(`[vibeOS] \u{1F578} ML graph: ${_firstWord2} \u2192 ${mlGraphPrediction} (${graphNode.count} samples)`);
15561
+ if (!_target) {
15562
+ _target = mlGraphPrediction;
15563
+ console.error(`[vibeOS] \u{1F578} ML graph: ${_firstWord2} \u2192 ${mlGraphPrediction} (${graphNode.count} samples)`);
15564
+ }
15388
15565
  }
15389
15566
  }
15390
15567
  if (_target) {
@@ -15396,7 +15573,7 @@ ${argsJson}
15396
15573
  }
15397
15574
  }
15398
15575
  const activePipeline = loadSelection().active_pipeline;
15399
- if (!apiRoute?.target && activePipeline && Array.isArray(activePipeline) && activePipeline.length > 1 && TRINITY_CHEAP && TRINITY_MEDIUM) {
15576
+ if (activePipeline && Array.isArray(activePipeline) && activePipeline.length > 1 && TRINITY_CHEAP && TRINITY_MEDIUM) {
15400
15577
  try {
15401
15578
  const cheapCost = 1e-3;
15402
15579
  const mediumCost = 5e-3;
@@ -15407,13 +15584,13 @@ ${argsJson}
15407
15584
  if (cascadeResult.escalate && pipelineModels.length > 1) {
15408
15585
  if (pipelineModels.length > 2 && cascadeResult.confidence >= 0.8) {
15409
15586
  const escalated = pipelineModels[2];
15410
- if (escalated && escalated !== currentModel && (!_target || escalated !== _target)) {
15587
+ if (escalated && escalated !== currentModel && !_hasMedia && (!_target || escalated !== _target)) {
15411
15588
  _target = escalated;
15412
15589
  console.error(`[vibeOS] \u{1F500} Cascade depth-3 escalate: ${cascadeResult.reason} \u2192 ${escalated}`);
15413
15590
  }
15414
15591
  } else {
15415
15592
  const escalated = pipelineModels[1];
15416
- if (escalated && escalated !== currentModel && (!_target || escalated !== _target)) {
15593
+ if (escalated && escalated !== currentModel && !_target) {
15417
15594
  _target = escalated;
15418
15595
  console.error(`[vibeOS] \u{1F500} Cascade escalate: ${cascadeResult.reason} \u2192 ${escalated}`);
15419
15596
  }
@@ -15592,7 +15769,7 @@ ${argsJson}
15592
15769
  }
15593
15770
  } else {
15594
15771
  const missed = recordMissedContext7(_estC7);
15595
- if (!existsSync17(CONTEXT7_INSTALL_FLAG)) {
15772
+ if (!existsSync18(CONTEXT7_INSTALL_FLAG)) {
15596
15773
  try {
15597
15774
  mkdirSync14(dirname12(CONTEXT7_INSTALL_FLAG), { recursive: true });
15598
15775
  writeFileSync16(CONTEXT7_INSTALL_FLAG, "");
@@ -16035,7 +16212,7 @@ ${pendingUiNote}`;
16035
16212
 
16036
16213
  // src/lib/hooks/session-compact.js
16037
16214
  init_state();
16038
- import { readFileSync as readFileSync17, existsSync as existsSync18 } from "node:fs";
16215
+ import { readFileSync as readFileSync17, existsSync as existsSync19 } from "node:fs";
16039
16216
  var onSessionCompacting = async (_input, output) => {
16040
16217
  if (!loadSelection().enabled)
16041
16218
  return;
@@ -16044,7 +16221,7 @@ var onSessionCompacting = async (_input, output) => {
16044
16221
  const needsCompact = turnCount >= 7;
16045
16222
  const indexPath = getSessionIndexPath();
16046
16223
  let recent = "";
16047
- if (existsSync18(indexPath)) {
16224
+ if (existsSync19(indexPath)) {
16048
16225
  try {
16049
16226
  const lines = readFileSync17(indexPath, "utf-8").trim().split("\n").slice(-30);
16050
16227
  recent = lines.map((l) => {
@@ -16150,11 +16327,15 @@ function ensureDeferredBootstrap() {
16150
16327
  }
16151
16328
  }
16152
16329
  var CLAIM_PATTERNS = [
16330
+ /(?:I|we|the)\s+(?:pushed|released|merged|deployed|fixed|wrote|implemented|completed|committed)\b/i,
16331
+ /(?:tests?|build|CI|checks?|suite|output|result)\s+(?:is\s+|are\s+)?(?:pass(?:ing|ed|es)?|green|clean|succeed|stable|positive)/i,
16332
+ /v\d+\.\d+\.\d+/,
16333
+ /\d+\s*(?:test|spec)s?\s*(?:pass|passing)/i,
16153
16334
  /(?:done|finished|complete)/i,
16154
16335
  /(?:fixed|resolved|solved)/i,
16155
- /(?:working|works|validated|verified)/i,
16156
- /(?:[0-9]+\.[0-9]?%|\d+%)/,
16157
- /(?:score|scored|passing|passed)/i
16336
+ /(?:works|working|validated|verified)/i,
16337
+ /(?:exit\s*code\s*0|0\s*errors|0\s*failures)/i,
16338
+ /(?:\d+%|score|scored|passing|passed)/i
16158
16339
  ];
16159
16340
  function scanClaimsInOutput(output) {
16160
16341
  if (!output || typeof output !== "string") return;
@@ -16175,7 +16356,6 @@ function scanClaimsInOutput(output) {
16175
16356
  claims: claims.slice(0, 10),
16176
16357
  totalClaims: claims.length,
16177
16358
  responseHash: ""
16178
- // crypto hash skipped for performance
16179
16359
  });
16180
16360
  appendFileSync(auditFile, entry + String.fromCharCode(10));
16181
16361
  } catch {
@@ -16196,9 +16376,9 @@ var _runDeferredStartupBootstrap = null;
16196
16376
  function _readOpenCodeConfigObject(dir) {
16197
16377
  const jsonPath = join19(dir, "opencode.json");
16198
16378
  const jsoncPath = join19(dir, "opencode.jsonc");
16199
- if (existsSync19(jsonPath))
16379
+ if (existsSync20(jsonPath))
16200
16380
  return safeJsonParse2(readFileSync18(jsonPath, "utf-8"));
16201
- if (existsSync19(jsoncPath))
16381
+ if (existsSync20(jsoncPath))
16202
16382
  return _parseJsonc(readFileSync18(jsoncPath, "utf-8"));
16203
16383
  return {};
16204
16384
  }
@@ -16251,7 +16431,7 @@ function _loadActiveJobForProject(directory3, fp2 = "") {
16251
16431
  for (const base of candidates) {
16252
16432
  try {
16253
16433
  const activeJobsPath = join19(String(base), "active-jobs.json");
16254
- if (!existsSync19(activeJobsPath))
16434
+ if (!existsSync20(activeJobsPath))
16255
16435
  continue;
16256
16436
  const jobs = safeJsonParse2(readFileSync18(activeJobsPath, "utf-8")) || {};
16257
16437
  const job = fp2 ? jobs?.[fp2] : null;
@@ -16273,9 +16453,9 @@ function _tiersNeedRepair(tiers) {
16273
16453
  async function _seedOrRepairModelTiers(directory3) {
16274
16454
  const TIERS_FILE3 = getTiersFile();
16275
16455
  let existing = null;
16276
- if (existsSync19(TIERS_FILE3)) {
16456
+ if (existsSync20(TIERS_FILE3)) {
16277
16457
  try {
16278
- const st = statSync9(TIERS_FILE3);
16458
+ const st = statSync10(TIERS_FILE3);
16279
16459
  if (st.size > 10485760) {
16280
16460
  _handleStateCorruption(TIERS_FILE3);
16281
16461
  return false;
@@ -16386,7 +16566,7 @@ function loadMcpPort() {
16386
16566
  return n;
16387
16567
  }
16388
16568
  try {
16389
- if (existsSync19(getTiersFile())) {
16569
+ if (existsSync20(getTiersFile())) {
16390
16570
  const tiers = safeJsonParse2(readFileSync18(getTiersFile(), "utf-8"));
16391
16571
  const cfg = tiers?.selection?.mcp_port;
16392
16572
  if (cfg === false || cfg === "disabled" || cfg === 0)
@@ -16401,7 +16581,7 @@ function loadMcpPort() {
16401
16581
  }
16402
16582
  function persistMcpPort(port) {
16403
16583
  try {
16404
- if (!existsSync19(getTiersFile()))
16584
+ if (!existsSync20(getTiersFile()))
16405
16585
  return;
16406
16586
  const tiers = safeJsonParse2(readFileSync18(getTiersFile(), "utf-8"));
16407
16587
  tiers.selection ??= {};
@@ -16503,7 +16683,7 @@ async function ensureMcpServerRunning() {
16503
16683
  getSessionMetrics: () => computeSessionMetrics(readFullState(), _OC_SID),
16504
16684
  getTodos: () => loadTodos(),
16505
16685
  listReports: (filter) => {
16506
- if (!existsSync19(getReportsDir2())) {
16686
+ if (!existsSync20(getReportsDir2())) {
16507
16687
  const e = new Error("reports dir not found");
16508
16688
  e.status = 404;
16509
16689
  throw e;
@@ -16716,9 +16896,9 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
16716
16896
  }
16717
16897
  } catch {
16718
16898
  }
16719
- console.error(`[vibeOS] auto-config guard: currentModel=${currentModel ? "SET" : "NONE"}, TIERS_FILE=${getTiersFile()}, exists=${existsSync19(getTiersFile())}`);
16899
+ console.error(`[vibeOS] auto-config guard: currentModel=${currentModel ? "SET" : "NONE"}, TIERS_FILE=${getTiersFile()}, exists=${existsSync20(getTiersFile())}`);
16720
16900
  try {
16721
- if (!existsSync19(getTiersFile())) {
16901
+ if (!existsSync20(getTiersFile())) {
16722
16902
  console.error(`[vibeOS] model-tiers.json missing at load; will seed on first hook`);
16723
16903
  }
16724
16904
  await _seedOrRepairModelTiers(directory3);
@@ -16778,7 +16958,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
16778
16958
  };
16779
16959
  const backupFileStable = (path, label) => {
16780
16960
  try {
16781
- if (!existsSync19(path))
16961
+ if (!existsSync20(path))
16782
16962
  return null;
16783
16963
  const bkDir = join19(hookVibeHome, ".backups");
16784
16964
  mkdirSync15(bkDir, { recursive: true });
@@ -16818,7 +16998,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
16818
16998
  safeJsonParse: safeJsonParse2,
16819
16999
  readFileSync: readFileSync18,
16820
17000
  writeFileSync: writeFileSync17,
16821
- existsSync: existsSync19,
17001
+ existsSync: existsSync20,
16822
17002
  renameSync: renameSync6,
16823
17003
  mkdirSync: mkdirSync15,
16824
17004
  get TIERS_FILE() {
@@ -16989,6 +17169,51 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
16989
17169
  ensureDeferredBootstrap();
16990
17170
  await _appendFooter(_input, output, directory3);
16991
17171
  scanClaimsInOutput(output);
17172
+ try {
17173
+ const auditDir = join19(getVibeOSHome13(), "cascade-audit");
17174
+ const claimFile = join19(auditDir, "claim-audit.jsonl");
17175
+ const cascadeFile = join19(auditDir, "cascade-audit.jsonl");
17176
+ if (existsSync20(claimFile) && statSync10(claimFile).size > 0) {
17177
+ const claimLines = readFileSync18(claimFile, "utf-8").trim().split("\n").slice(-10);
17178
+ const cascadeLines = existsSync20(cascadeFile) ? readFileSync18(cascadeFile, "utf-8").trim().split("\n").slice(-30) : [];
17179
+ const cascadeRuns = cascadeLines.filter(Boolean).map((l) => {
17180
+ try {
17181
+ return JSON.parse(l);
17182
+ } catch {
17183
+ }
17184
+ }).filter(Boolean);
17185
+ let unsub = 0;
17186
+ for (const cl of claimLines) {
17187
+ if (!cl.trim()) continue;
17188
+ let entry;
17189
+ try {
17190
+ entry = JSON.parse(cl);
17191
+ } catch {
17192
+ continue;
17193
+ }
17194
+ if (!entry) continue;
17195
+ const claimTexts = (entry.claims || []).map(function(c) {
17196
+ return c.text;
17197
+ }).join(" | ");
17198
+ if (!CLAIM_PATTERNS.some(function(p) {
17199
+ return p.test(claimTexts);
17200
+ })) continue;
17201
+ let cascadeMatch = false;
17202
+ for (const cr of cascadeRuns) {
17203
+ const cTs = cr._ts || "";
17204
+ if (cTs && entry.ts) {
17205
+ if (Math.abs(new Date(cTs).getTime() - new Date(entry.ts).getTime()) < 12e4) {
17206
+ cascadeMatch = true;
17207
+ break;
17208
+ }
17209
+ }
17210
+ }
17211
+ if (!cascadeMatch) unsub++;
17212
+ }
17213
+ _unsubstantiatedClaims = unsub;
17214
+ }
17215
+ } catch {
17216
+ }
16992
17217
  },
16993
17218
  "message.updated": async (_input, output) => {
16994
17219
  setVibeOSHomeContext(hookVibeHome);
@@ -16999,6 +17224,51 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
16999
17224
  ensureDeferredBootstrap();
17000
17225
  await _appendFooter(_input, output, directory3);
17001
17226
  scanClaimsInOutput(output);
17227
+ try {
17228
+ const auditDir = join19(getVibeOSHome13(), "cascade-audit");
17229
+ const claimFile = join19(auditDir, "claim-audit.jsonl");
17230
+ const cascadeFile = join19(auditDir, "cascade-audit.jsonl");
17231
+ if (existsSync20(claimFile) && statSync10(claimFile).size > 0) {
17232
+ const claimLines = readFileSync18(claimFile, "utf-8").trim().split("\n").slice(-10);
17233
+ const cascadeLines = existsSync20(cascadeFile) ? readFileSync18(cascadeFile, "utf-8").trim().split("\n").slice(-30) : [];
17234
+ const cascadeRuns = cascadeLines.filter(Boolean).map((l) => {
17235
+ try {
17236
+ return JSON.parse(l);
17237
+ } catch {
17238
+ }
17239
+ }).filter(Boolean);
17240
+ let unsub = 0;
17241
+ for (const cl of claimLines) {
17242
+ if (!cl.trim()) continue;
17243
+ let entry;
17244
+ try {
17245
+ entry = JSON.parse(cl);
17246
+ } catch {
17247
+ continue;
17248
+ }
17249
+ if (!entry) continue;
17250
+ const claimTexts = (entry.claims || []).map(function(c) {
17251
+ return c.text;
17252
+ }).join(" | ");
17253
+ if (!CLAIM_PATTERNS.some(function(p) {
17254
+ return p.test(claimTexts);
17255
+ })) continue;
17256
+ let cascadeMatch = false;
17257
+ for (const cr of cascadeRuns) {
17258
+ const cTs = cr._ts || "";
17259
+ if (cTs && entry.ts) {
17260
+ if (Math.abs(new Date(cTs).getTime() - new Date(entry.ts).getTime()) < 12e4) {
17261
+ cascadeMatch = true;
17262
+ break;
17263
+ }
17264
+ }
17265
+ }
17266
+ if (!cascadeMatch) unsub++;
17267
+ }
17268
+ _unsubstantiatedClaims = unsub;
17269
+ }
17270
+ } catch {
17271
+ }
17002
17272
  },
17003
17273
  tool: {
17004
17274
  trinity: tool(createTrinityTool(trinityDeps)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeostheog",
3
- "version": "0.25.42",
3
+ "version": "0.25.45",
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",
@@ -92,4 +92,4 @@
92
92
  "express": "^5.2.1",
93
93
  "typescript": "^5.9.3"
94
94
  }
95
- }
95
+ }