vibeostheog 0.24.16 → 0.24.18
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 +5 -0
- package/dist/vibeOS.js +500 -175
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/dist/vibeOS.js
CHANGED
|
@@ -587,7 +587,7 @@ var init_meta_controller = __esm({
|
|
|
587
587
|
});
|
|
588
588
|
|
|
589
589
|
// src/vibeOS-lib/blackbox/pivot-cache.js
|
|
590
|
-
import { existsSync as existsSync7, mkdirSync as
|
|
590
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
|
|
591
591
|
import { join as join6, dirname as dirname6 } from "node:path";
|
|
592
592
|
import { homedir as homedir5 } from "node:os";
|
|
593
593
|
var PivotCache;
|
|
@@ -625,7 +625,7 @@ var init_pivot_cache = __esm({
|
|
|
625
625
|
const p = this._storePath();
|
|
626
626
|
const dir = dirname6(p);
|
|
627
627
|
if (!existsSync7(dir))
|
|
628
|
-
|
|
628
|
+
mkdirSync5(dir, { recursive: true });
|
|
629
629
|
writeFileSync6(p, JSON.stringify(this.store, null, 2), "utf-8");
|
|
630
630
|
} catch {
|
|
631
631
|
}
|
|
@@ -796,7 +796,7 @@ __export(vibemax_exports, {
|
|
|
796
796
|
vibemaxPipeline: () => vibemaxPipeline,
|
|
797
797
|
vibemaxSelectMode: () => vibemaxSelectMode
|
|
798
798
|
});
|
|
799
|
-
import { existsSync as existsSync8, mkdirSync as
|
|
799
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "node:fs";
|
|
800
800
|
import { resolve as resolve2, dirname as dirname7 } from "node:path";
|
|
801
801
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
802
802
|
function fallback(sr, text) {
|
|
@@ -1078,7 +1078,7 @@ function loadVibeMaXModel() {
|
|
|
1078
1078
|
return null;
|
|
1079
1079
|
}
|
|
1080
1080
|
function saveVibeMaXModel(model) {
|
|
1081
|
-
|
|
1081
|
+
mkdirSync6(dirname7(MODEL_PATH), { recursive: true });
|
|
1082
1082
|
writeFileSync7(MODEL_PATH, JSON.stringify(model, null, 2) + "\n", "utf-8");
|
|
1083
1083
|
}
|
|
1084
1084
|
function getVibeMaXModelMeta() {
|
|
@@ -1104,8 +1104,8 @@ var init_vibemax = __esm({
|
|
|
1104
1104
|
|
|
1105
1105
|
// src/index.ts
|
|
1106
1106
|
init_flow_enforcer();
|
|
1107
|
-
import { readFileSync as readFileSync17, writeFileSync as writeFileSync15, existsSync as existsSync18, mkdirSync as
|
|
1108
|
-
import { join as join18, dirname as dirname13, basename as
|
|
1107
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync15, existsSync as existsSync18, mkdirSync as mkdirSync13, copyFileSync as copyFileSync2, renameSync as renameSync6 } from "node:fs";
|
|
1108
|
+
import { join as join18, dirname as dirname13, basename as basename5 } from "node:path";
|
|
1109
1109
|
|
|
1110
1110
|
// src/vibeOS-lib/session-metrics.js
|
|
1111
1111
|
function formatDuration(totalSeconds) {
|
|
@@ -2354,22 +2354,22 @@ async function remoteCall(method, args, fallbackFn) {
|
|
|
2354
2354
|
}
|
|
2355
2355
|
|
|
2356
2356
|
// src/lib/pricing.js
|
|
2357
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5,
|
|
2358
|
-
import { join as join5, dirname as dirname5,
|
|
2357
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync4, statSync as statSync5, renameSync as renameSync4, openSync as openSync2, closeSync as closeSync2, rmSync as rmSync3, readdirSync as readdirSync2 } from "node:fs";
|
|
2358
|
+
import { join as join5, dirname as dirname5, resolve } from "node:path";
|
|
2359
2359
|
import { homedir as homedir4, tmpdir as tmpdir3 } from "node:os";
|
|
2360
2360
|
import { createHash as createHash2 } from "node:crypto";
|
|
2361
2361
|
|
|
2362
2362
|
// src/lib/state.js
|
|
2363
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, appendFileSync as
|
|
2364
|
-
import { join as join4, dirname as dirname4, basename as
|
|
2363
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, appendFileSync as appendFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, statSync as statSync4, readdirSync, openSync, readSync, closeSync, rmSync as rmSync2, copyFileSync, renameSync as renameSync3 } from "node:fs";
|
|
2364
|
+
import { join as join4, dirname as dirname4, basename as basename2 } from "node:path";
|
|
2365
2365
|
import { spawn } from "node:child_process";
|
|
2366
2366
|
import { homedir as homedir3, tmpdir as tmpdir2 } from "node:os";
|
|
2367
2367
|
import { createHash } from "node:crypto";
|
|
2368
2368
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2369
2369
|
|
|
2370
2370
|
// src/lib/selection-manager.js
|
|
2371
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3,
|
|
2372
|
-
import { join as join3
|
|
2371
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync4, statSync as statSync3, renameSync as renameSync2 } from "node:fs";
|
|
2372
|
+
import { join as join3 } from "node:path";
|
|
2373
2373
|
import { homedir as homedir2, tmpdir } from "node:os";
|
|
2374
2374
|
var USER_HOME = (() => {
|
|
2375
2375
|
try {
|
|
@@ -2381,20 +2381,6 @@ var USER_HOME = (() => {
|
|
|
2381
2381
|
function getVibeOSHome2() {
|
|
2382
2382
|
return process.env.VIBEOS_HOME || join3(process.env.HOME || homedir2(), ".claude");
|
|
2383
2383
|
}
|
|
2384
|
-
function _handleStateCorruption(path) {
|
|
2385
|
-
const backupDir = join3(getVibeOSHome2(), ".backups");
|
|
2386
|
-
mkdirSync3(backupDir, { recursive: true });
|
|
2387
|
-
const backupPath = join3(backupDir, basename(path) + ".corrupted." + Date.now());
|
|
2388
|
-
try {
|
|
2389
|
-
copyFileSync(path, backupPath);
|
|
2390
|
-
} catch {
|
|
2391
|
-
}
|
|
2392
|
-
const logPath = join3(getVibeOSHome2(), ".state-corruption-log.jsonl");
|
|
2393
|
-
try {
|
|
2394
|
-
appendFileSync2(logPath, JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), path, backup: backupPath }) + "\n");
|
|
2395
|
-
} catch {
|
|
2396
|
-
}
|
|
2397
|
-
}
|
|
2398
2384
|
function safeJsonParse2(raw) {
|
|
2399
2385
|
if (raw == null || raw === "")
|
|
2400
2386
|
return null;
|
|
@@ -2537,7 +2523,7 @@ function writeSessionOptMode(sid, mode) {
|
|
|
2537
2523
|
}
|
|
2538
2524
|
|
|
2539
2525
|
// src/lib/pattern-helpers.js
|
|
2540
|
-
import { relative, basename
|
|
2526
|
+
import { relative, basename } from "node:path";
|
|
2541
2527
|
function normalizeObservedPath(filePath, directory3) {
|
|
2542
2528
|
if (!filePath || typeof filePath !== "string")
|
|
2543
2529
|
return "unknown";
|
|
@@ -2558,7 +2544,7 @@ function normalizeObservedPath(filePath, directory3) {
|
|
|
2558
2544
|
return `src/*.${m[1].toLowerCase()}`;
|
|
2559
2545
|
if (p.startsWith("tests/") && m)
|
|
2560
2546
|
return `tests/*.${m[1].toLowerCase()}`;
|
|
2561
|
-
return
|
|
2547
|
+
return basename(p) || "unknown";
|
|
2562
2548
|
}
|
|
2563
2549
|
function commandFamily(command) {
|
|
2564
2550
|
const c = String(command || "").trim().toLowerCase();
|
|
@@ -3291,6 +3277,12 @@ var MAX_SCRATCHPAD_FILES = 1e3;
|
|
|
3291
3277
|
var MAX_SCRATCHPAD_BYTES = 10 * 1024 * 1024;
|
|
3292
3278
|
var MAX_SESSION_SCRATCHPAD_FILES = 200;
|
|
3293
3279
|
var MAX_SESSION_SCRATCHPAD_BYTES = 2 * 1024 * 1024;
|
|
3280
|
+
var CORRUPTION_BACKUP_MAX = 5;
|
|
3281
|
+
var CORRUPTION_BACKUP_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
3282
|
+
var LEDGER_ROTATE_MAX_BYTES = 256 * 1024;
|
|
3283
|
+
var LEDGER_ROTATE_MAX_LINES = 1e4;
|
|
3284
|
+
var LEDGER_ROTATE_MAX_AGE_MS = 48 * 60 * 60 * 1e3;
|
|
3285
|
+
var ACTIVE_JOBS_STALE_MS = 72 * 60 * 60 * 1e3;
|
|
3294
3286
|
var MAX_PTR_CANDIDATES = 50;
|
|
3295
3287
|
var SUMMARY_HEAD_TRUNCATE = 500;
|
|
3296
3288
|
function getVibeOSHome3() {
|
|
@@ -3421,19 +3413,61 @@ var tool = Object.assign((def) => def, {
|
|
|
3421
3413
|
enum: (values) => _zType({ kind: "enum", values })
|
|
3422
3414
|
}
|
|
3423
3415
|
});
|
|
3424
|
-
function
|
|
3416
|
+
function _pruneCorruptionBackups(backupDir) {
|
|
3417
|
+
try {
|
|
3418
|
+
if (!existsSync5(backupDir))
|
|
3419
|
+
return;
|
|
3420
|
+
const now = Date.now();
|
|
3421
|
+
const backups = readdirSync(backupDir).map((name) => {
|
|
3422
|
+
const path = join4(backupDir, name);
|
|
3423
|
+
try {
|
|
3424
|
+
const st = statSync4(path);
|
|
3425
|
+
return { name, path, mtimeMs: st.mtimeMs };
|
|
3426
|
+
} catch {
|
|
3427
|
+
return null;
|
|
3428
|
+
}
|
|
3429
|
+
}).filter((entry) => !!entry && entry.name.includes(".corrupted.")).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
3430
|
+
const keep = new Set(backups.slice(0, CORRUPTION_BACKUP_MAX).map((b) => b.path));
|
|
3431
|
+
for (const backup of backups) {
|
|
3432
|
+
const isExpired = now - backup.mtimeMs > CORRUPTION_BACKUP_TTL_MS;
|
|
3433
|
+
if (isExpired || !keep.has(backup.path)) {
|
|
3434
|
+
try {
|
|
3435
|
+
rmSync2(backup.path, { force: true });
|
|
3436
|
+
} catch {
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3440
|
+
} catch {
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
var _startupMaintenanceHome = "";
|
|
3444
|
+
function runStartupMaintenanceOnce() {
|
|
3445
|
+
try {
|
|
3446
|
+
const home = getVibeOSHome3();
|
|
3447
|
+
if (!home || home === _startupMaintenanceHome)
|
|
3448
|
+
return;
|
|
3449
|
+
_startupMaintenanceHome = home;
|
|
3450
|
+
_pruneCorruptionBackups(join4(home, ".backups"));
|
|
3451
|
+
loadActiveJobs();
|
|
3452
|
+
_compactSavingsLedgerIfNeeded();
|
|
3453
|
+
} catch {
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
function _handleStateCorruption(path) {
|
|
3425
3457
|
const backupDir = join4(VIBEOS_HOME, ".backups");
|
|
3426
|
-
|
|
3427
|
-
const backupPath = join4(backupDir,
|
|
3458
|
+
mkdirSync3(backupDir, { recursive: true });
|
|
3459
|
+
const backupPath = join4(backupDir, basename2(path) + ".corrupted." + Date.now());
|
|
3428
3460
|
try {
|
|
3429
|
-
|
|
3461
|
+
copyFileSync(path, backupPath);
|
|
3430
3462
|
} catch {
|
|
3431
3463
|
}
|
|
3432
3464
|
const logPath = join4(VIBEOS_HOME, ".state-corruption-log.jsonl");
|
|
3433
3465
|
try {
|
|
3434
|
-
|
|
3466
|
+
appendFileSync2(logPath, JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), path, backup: backupPath }) + "\n");
|
|
3435
3467
|
} catch {
|
|
3436
3468
|
}
|
|
3469
|
+
_pruneCorruptionBackups(backupDir);
|
|
3470
|
+
return backupPath;
|
|
3437
3471
|
}
|
|
3438
3472
|
function _lockPathFor(filePath) {
|
|
3439
3473
|
const hash = createHash("sha1").update(String(filePath || "")).digest("hex");
|
|
@@ -3446,7 +3480,7 @@ function withFileLock(filePath, fn, opts = {}) {
|
|
|
3446
3480
|
const start = Date.now();
|
|
3447
3481
|
while (Date.now() - start < timeoutMs) {
|
|
3448
3482
|
try {
|
|
3449
|
-
|
|
3483
|
+
mkdirSync3(FILE_LOCK_DIR, { recursive: true });
|
|
3450
3484
|
const fd = openSync(lockPath, "wx");
|
|
3451
3485
|
try {
|
|
3452
3486
|
writeFileSync4(fd, `${process.pid}
|
|
@@ -3524,12 +3558,12 @@ function readJsonOrEmpty(filePath) {
|
|
|
3524
3558
|
return {};
|
|
3525
3559
|
const st = statSync4(filePath);
|
|
3526
3560
|
if (st.size > 10485760) {
|
|
3527
|
-
|
|
3561
|
+
_handleStateCorruption(filePath);
|
|
3528
3562
|
return {};
|
|
3529
3563
|
}
|
|
3530
3564
|
return safeJsonParse3(readFileSync4(filePath, "utf-8"));
|
|
3531
3565
|
} catch {
|
|
3532
|
-
|
|
3566
|
+
_handleStateCorruption(filePath);
|
|
3533
3567
|
return {};
|
|
3534
3568
|
}
|
|
3535
3569
|
}
|
|
@@ -3554,7 +3588,7 @@ function updateState(mutator) {
|
|
|
3554
3588
|
state._gen = preGen + 1;
|
|
3555
3589
|
const next = mutator(state) ?? state;
|
|
3556
3590
|
validateState(next, delegationStateFile);
|
|
3557
|
-
|
|
3591
|
+
mkdirSync3(dirname4(delegationStateFile), { recursive: true });
|
|
3558
3592
|
const tmp = delegationStateFile + ".tmp";
|
|
3559
3593
|
writeFileSync4(tmp, JSON.stringify(next, null, 2) + "\n");
|
|
3560
3594
|
renameSync3(tmp, delegationStateFile);
|
|
@@ -3580,12 +3614,12 @@ function readFullState() {
|
|
|
3580
3614
|
return {};
|
|
3581
3615
|
const st = statSync4(delegationStateFile);
|
|
3582
3616
|
if (st.size > 10485760) {
|
|
3583
|
-
|
|
3617
|
+
_handleStateCorruption(delegationStateFile);
|
|
3584
3618
|
return {};
|
|
3585
3619
|
}
|
|
3586
3620
|
return safeJsonParse3(readFileSync4(delegationStateFile, "utf-8"));
|
|
3587
3621
|
} catch {
|
|
3588
|
-
|
|
3622
|
+
_handleStateCorruption(delegationStateFile);
|
|
3589
3623
|
return {};
|
|
3590
3624
|
}
|
|
3591
3625
|
}
|
|
@@ -3625,7 +3659,7 @@ function loadGlobalLearning() {
|
|
|
3625
3659
|
return DFLT_GL;
|
|
3626
3660
|
const st = statSync4(globalLearningFile);
|
|
3627
3661
|
if (st.size > 10485760) {
|
|
3628
|
-
|
|
3662
|
+
_handleStateCorruption(globalLearningFile);
|
|
3629
3663
|
return DFLT_GL;
|
|
3630
3664
|
}
|
|
3631
3665
|
const j = safeJsonParse3(readFileSync4(globalLearningFile, "utf-8"));
|
|
@@ -3638,7 +3672,7 @@ function loadGlobalLearning() {
|
|
|
3638
3672
|
j.context7_last_seen ??= null;
|
|
3639
3673
|
return j;
|
|
3640
3674
|
} catch {
|
|
3641
|
-
|
|
3675
|
+
_handleStateCorruption(globalLearningFile);
|
|
3642
3676
|
return DFLT_GL;
|
|
3643
3677
|
}
|
|
3644
3678
|
}
|
|
@@ -3648,7 +3682,7 @@ function updateGlobalLearning(mutator) {
|
|
|
3648
3682
|
const s = loadGlobalLearning();
|
|
3649
3683
|
const next = mutator(s) ?? s;
|
|
3650
3684
|
next.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3651
|
-
|
|
3685
|
+
mkdirSync3(dirname4(globalLearningFile), { recursive: true });
|
|
3652
3686
|
const tmp = globalLearningFile + ".tmp";
|
|
3653
3687
|
writeFileSync4(tmp, JSON.stringify(next, null, 2));
|
|
3654
3688
|
renameSync3(tmp, globalLearningFile);
|
|
@@ -3700,21 +3734,79 @@ function loadBlackboxState() {
|
|
|
3700
3734
|
return { enabled: true, sessions: {} };
|
|
3701
3735
|
const st = statSync4(blackboxFile);
|
|
3702
3736
|
if (st.size > 10485760) {
|
|
3703
|
-
|
|
3737
|
+
_handleStateCorruption(blackboxFile);
|
|
3704
3738
|
return { enabled: false, sessions: {} };
|
|
3705
3739
|
}
|
|
3706
|
-
|
|
3740
|
+
const raw = safeJsonParse3(readFileSync4(blackboxFile, "utf-8")) || { enabled: false, sessions: {} };
|
|
3741
|
+
if (!raw.sessions || typeof raw.sessions !== "object")
|
|
3742
|
+
raw.sessions = {};
|
|
3743
|
+
const now = Date.now();
|
|
3744
|
+
let changed = false;
|
|
3745
|
+
for (const [sid, session] of Object.entries(raw.sessions)) {
|
|
3746
|
+
if (!session || typeof session !== "object")
|
|
3747
|
+
continue;
|
|
3748
|
+
const next = { ...session };
|
|
3749
|
+
const createdAtRaw = typeof next.createdAt === "string" ? next.createdAt : "";
|
|
3750
|
+
const updatedAtRaw = typeof next.updatedAt === "string" ? next.updatedAt : "";
|
|
3751
|
+
const startedRaw = typeof next.started === "string" ? next.started : "";
|
|
3752
|
+
const sessionStartedRaw = typeof next.session_started_at === "string" ? next.session_started_at : "";
|
|
3753
|
+
const anchorRaw = [createdAtRaw, updatedAtRaw, startedRaw, sessionStartedRaw].find((v) => v && !Number.isNaN(Date.parse(v)));
|
|
3754
|
+
const anchorMs = anchorRaw ? Date.parse(anchorRaw) : NaN;
|
|
3755
|
+
if (!Number.isFinite(Date.parse(createdAtRaw))) {
|
|
3756
|
+
next.createdAt = Number.isFinite(anchorMs) ? new Date(anchorMs).toISOString() : new Date(now).toISOString();
|
|
3757
|
+
changed = true;
|
|
3758
|
+
}
|
|
3759
|
+
if (!Number.isFinite(Date.parse(updatedAtRaw))) {
|
|
3760
|
+
next.updatedAt = next.createdAt || new Date(now).toISOString();
|
|
3761
|
+
changed = true;
|
|
3762
|
+
}
|
|
3763
|
+
if (typeof next.sessionId !== "string" || !next.sessionId.trim()) {
|
|
3764
|
+
next.sessionId = String(sid || "");
|
|
3765
|
+
changed = true;
|
|
3766
|
+
}
|
|
3767
|
+
raw.sessions[sid] = next;
|
|
3768
|
+
}
|
|
3769
|
+
if (changed) {
|
|
3770
|
+
try {
|
|
3771
|
+
saveBlackboxState(raw);
|
|
3772
|
+
} catch {
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3775
|
+
return raw;
|
|
3707
3776
|
} catch {
|
|
3708
|
-
|
|
3777
|
+
_handleStateCorruption(blackboxFile);
|
|
3709
3778
|
return { enabled: false, sessions: {} };
|
|
3710
3779
|
}
|
|
3711
3780
|
}
|
|
3712
3781
|
function saveBlackboxState(state) {
|
|
3713
3782
|
const blackboxFile = join4(getVibeOSHome3(), "blackbox-state.json");
|
|
3714
3783
|
try {
|
|
3715
|
-
|
|
3784
|
+
const next = state && typeof state === "object" ? state : { enabled: true, sessions: {} };
|
|
3785
|
+
next.sessions ??= {};
|
|
3786
|
+
const now = Date.now();
|
|
3787
|
+
for (const [sid, session] of Object.entries(next.sessions)) {
|
|
3788
|
+
if (!session || typeof session !== "object")
|
|
3789
|
+
continue;
|
|
3790
|
+
const record = session;
|
|
3791
|
+
const createdAtRaw = typeof record.createdAt === "string" ? record.createdAt : "";
|
|
3792
|
+
const updatedAtRaw = typeof record.updatedAt === "string" ? record.updatedAt : "";
|
|
3793
|
+
const startedRaw = typeof record.started === "string" ? record.started : "";
|
|
3794
|
+
const sessionStartedRaw = typeof record.session_started_at === "string" ? record.session_started_at : "";
|
|
3795
|
+
const anchorRaw = [createdAtRaw, updatedAtRaw, startedRaw, sessionStartedRaw].find((v) => v && !Number.isNaN(Date.parse(v)));
|
|
3796
|
+
const anchorMs = anchorRaw ? Date.parse(anchorRaw) : NaN;
|
|
3797
|
+
if (!Number.isFinite(Date.parse(createdAtRaw))) {
|
|
3798
|
+
record.createdAt = Number.isFinite(anchorMs) ? new Date(anchorMs).toISOString() : new Date(now).toISOString();
|
|
3799
|
+
}
|
|
3800
|
+
if (!Number.isFinite(Date.parse(updatedAtRaw))) {
|
|
3801
|
+
record.updatedAt = record.createdAt || new Date(now).toISOString();
|
|
3802
|
+
}
|
|
3803
|
+
if (typeof record.sessionId !== "string" || !record.sessionId.trim()) {
|
|
3804
|
+
record.sessionId = String(sid || "");
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3807
|
+
mkdirSync3(dirname4(blackboxFile), { recursive: true });
|
|
3716
3808
|
const tmp = blackboxFile + ".tmp";
|
|
3717
|
-
writeFileSync4(tmp, JSON.stringify(
|
|
3809
|
+
writeFileSync4(tmp, JSON.stringify(next, null, 2) + "\n");
|
|
3718
3810
|
renameSync3(tmp, blackboxFile);
|
|
3719
3811
|
} catch (err) {
|
|
3720
3812
|
console.error(`[vibeOS] saveBlackboxState failed: ${err.message}`);
|
|
@@ -3734,7 +3826,7 @@ function getGlobalIndexPath() {
|
|
|
3734
3826
|
}
|
|
3735
3827
|
function ensureSessionScratchpadDirs() {
|
|
3736
3828
|
try {
|
|
3737
|
-
|
|
3829
|
+
mkdirSync3(getSessionScratchpadDir(), { recursive: true });
|
|
3738
3830
|
return true;
|
|
3739
3831
|
} catch {
|
|
3740
3832
|
return false;
|
|
@@ -3778,7 +3870,8 @@ function _flushLedgerBuffer() {
|
|
|
3778
3870
|
const lines = batch.map((e) => typeof e === "string" ? e.trimEnd() : String(e).trimEnd());
|
|
3779
3871
|
const joined = lines.filter(Boolean).map((l) => l + "\n").join("");
|
|
3780
3872
|
try {
|
|
3781
|
-
|
|
3873
|
+
appendFileSync2(SAVINGS_LEDGER_FILE, joined);
|
|
3874
|
+
_compactSavingsLedgerIfNeeded();
|
|
3782
3875
|
} catch {
|
|
3783
3876
|
}
|
|
3784
3877
|
}
|
|
@@ -3925,10 +4018,10 @@ function indexAppend(hash, tool2, size, extra) {
|
|
|
3925
4018
|
const entry = JSON.stringify(entryObj) + "\n";
|
|
3926
4019
|
const globalIndex = getGlobalIndexPath();
|
|
3927
4020
|
const sessionIndex = getSessionIndexPath();
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
4021
|
+
mkdirSync3(dirname4(globalIndex), { recursive: true });
|
|
4022
|
+
mkdirSync3(dirname4(sessionIndex), { recursive: true });
|
|
4023
|
+
appendFileSync2(globalIndex, entry);
|
|
4024
|
+
appendFileSync2(sessionIndex, entry);
|
|
3932
4025
|
} catch (err) {
|
|
3933
4026
|
console.error(`[vibeOS] index write failed: ${err.message}`);
|
|
3934
4027
|
}
|
|
@@ -4181,21 +4274,83 @@ function pruneScratchpadOnce() {
|
|
|
4181
4274
|
}
|
|
4182
4275
|
cleanupStaleSessionScratchpads();
|
|
4183
4276
|
}
|
|
4184
|
-
function
|
|
4277
|
+
function _readActiveJobsRaw() {
|
|
4185
4278
|
try {
|
|
4186
4279
|
if (!existsSync5(ACTIVE_JOBS_FILE))
|
|
4187
4280
|
return {};
|
|
4188
|
-
const st = statSync4(ACTIVE_JOBS_FILE);
|
|
4189
|
-
if (st.size > 10485760) {
|
|
4190
|
-
_handleStateCorruption2(ACTIVE_JOBS_FILE);
|
|
4191
|
-
return {};
|
|
4192
|
-
}
|
|
4193
4281
|
const raw = safeJsonParse3(readFileSync4(ACTIVE_JOBS_FILE, "utf-8"));
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4282
|
+
return raw && typeof raw === "object" ? raw : {};
|
|
4283
|
+
} catch {
|
|
4284
|
+
_handleStateCorruption(ACTIVE_JOBS_FILE);
|
|
4285
|
+
return {};
|
|
4286
|
+
}
|
|
4287
|
+
}
|
|
4288
|
+
function _writeActiveJobsRaw(jobs) {
|
|
4289
|
+
try {
|
|
4290
|
+
mkdirSync3(dirname4(ACTIVE_JOBS_FILE), { recursive: true });
|
|
4291
|
+
const tmp = ACTIVE_JOBS_FILE + ".tmp";
|
|
4292
|
+
writeFileSync4(tmp, JSON.stringify(jobs, null, 2) + "\n");
|
|
4293
|
+
renameSync3(tmp, ACTIVE_JOBS_FILE);
|
|
4294
|
+
} catch {
|
|
4295
|
+
}
|
|
4296
|
+
}
|
|
4297
|
+
function _normalizeActiveJobRecord(record, now = Date.now(), strict = false) {
|
|
4298
|
+
if (!record || typeof record !== "object")
|
|
4299
|
+
return { record: null, changed: false, stale: false };
|
|
4300
|
+
const next = { ...record };
|
|
4301
|
+
let changed = false;
|
|
4302
|
+
const updatedAtRaw = typeof next.updatedAt === "string" ? next.updatedAt : "";
|
|
4303
|
+
const createdAtRaw = typeof next.createdAt === "string" ? next.createdAt : "";
|
|
4304
|
+
const updatedAtMs = Date.parse(updatedAtRaw);
|
|
4305
|
+
const createdAtMs = Date.parse(createdAtRaw);
|
|
4306
|
+
const anchorMs = Number.isFinite(updatedAtMs) ? updatedAtMs : createdAtMs;
|
|
4307
|
+
const stale = Number.isFinite(anchorMs) && now - anchorMs > ACTIVE_JOBS_STALE_MS;
|
|
4308
|
+
if (strict && (!next.status || typeof next.status !== "string" || !next.status.trim()))
|
|
4309
|
+
return { record: null, changed: false, stale };
|
|
4310
|
+
if (strict && !Number.isFinite(createdAtMs))
|
|
4311
|
+
return { record: null, changed: false, stale };
|
|
4312
|
+
if (!Number.isFinite(createdAtMs)) {
|
|
4313
|
+
next.createdAt = Number.isFinite(anchorMs) ? new Date(anchorMs).toISOString() : new Date(now).toISOString();
|
|
4314
|
+
changed = true;
|
|
4315
|
+
}
|
|
4316
|
+
if (!Number.isFinite(updatedAtMs)) {
|
|
4317
|
+
next.updatedAt = next.createdAt || new Date(now).toISOString();
|
|
4318
|
+
changed = true;
|
|
4319
|
+
}
|
|
4320
|
+
if (typeof next.status !== "string" || !next.status.trim()) {
|
|
4321
|
+
next.status = "active";
|
|
4322
|
+
changed = true;
|
|
4323
|
+
}
|
|
4324
|
+
if (stale && next.status !== "completed") {
|
|
4325
|
+
next.status = "completed";
|
|
4326
|
+
next.completedAt = new Date(now).toISOString();
|
|
4327
|
+
changed = true;
|
|
4328
|
+
}
|
|
4329
|
+
return { record: next, changed, stale };
|
|
4330
|
+
}
|
|
4331
|
+
function loadActiveJobs() {
|
|
4332
|
+
try {
|
|
4333
|
+
return withFileLock(ACTIVE_JOBS_FILE, () => {
|
|
4334
|
+
const raw = _readActiveJobsRaw();
|
|
4335
|
+
const next = {};
|
|
4336
|
+
let changed = false;
|
|
4337
|
+
const now = Date.now();
|
|
4338
|
+
for (const [key, value] of Object.entries(raw || {})) {
|
|
4339
|
+
const norm = _normalizeActiveJobRecord(value, now, true);
|
|
4340
|
+
if (!norm.record) {
|
|
4341
|
+
changed = true;
|
|
4342
|
+
continue;
|
|
4343
|
+
}
|
|
4344
|
+
next[key] = norm.record;
|
|
4345
|
+
if (norm.changed)
|
|
4346
|
+
changed = true;
|
|
4347
|
+
}
|
|
4348
|
+
if (changed)
|
|
4349
|
+
_writeActiveJobsRaw(next);
|
|
4350
|
+
return next;
|
|
4351
|
+
});
|
|
4197
4352
|
} catch {
|
|
4198
|
-
|
|
4353
|
+
_handleStateCorruption(ACTIVE_JOBS_FILE);
|
|
4199
4354
|
return {};
|
|
4200
4355
|
}
|
|
4201
4356
|
}
|
|
@@ -4212,15 +4367,19 @@ function saveActiveJobForProject(job, fp2 = currentProjectFingerprint) {
|
|
|
4212
4367
|
if (!fp2 || !job || typeof job !== "object")
|
|
4213
4368
|
return;
|
|
4214
4369
|
try {
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4370
|
+
withFileLock(ACTIVE_JOBS_FILE, () => {
|
|
4371
|
+
const jobs = _readActiveJobsRaw();
|
|
4372
|
+
const norm = _normalizeActiveJobRecord(job);
|
|
4373
|
+
jobs[fp2] = norm.record || job;
|
|
4374
|
+
_writeActiveJobsRaw(jobs);
|
|
4375
|
+
});
|
|
4221
4376
|
} catch {
|
|
4222
4377
|
}
|
|
4223
4378
|
}
|
|
4379
|
+
try {
|
|
4380
|
+
loadActiveJobs();
|
|
4381
|
+
} catch {
|
|
4382
|
+
}
|
|
4224
4383
|
function projectFingerprint(dir) {
|
|
4225
4384
|
if (!dir)
|
|
4226
4385
|
return "unknown";
|
|
@@ -4242,7 +4401,7 @@ function saveProjectState(state) {
|
|
|
4242
4401
|
const projectStateFile = join4(getVibeOSHome3(), "project-states.json");
|
|
4243
4402
|
try {
|
|
4244
4403
|
withFileLock(projectStateFile, () => {
|
|
4245
|
-
|
|
4404
|
+
mkdirSync3(dirname4(projectStateFile), { recursive: true });
|
|
4246
4405
|
const _tmp = projectStateFile + ".tmp." + Date.now();
|
|
4247
4406
|
writeFileSync4(_tmp, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
4248
4407
|
renameSync3(_tmp, projectStateFile);
|
|
@@ -4259,11 +4418,49 @@ function ensureProjectBucket(state, fp2) {
|
|
|
4259
4418
|
researchChains: 0,
|
|
4260
4419
|
context7Bypasses: 0,
|
|
4261
4420
|
commonTopics: [],
|
|
4421
|
+
sessions: [],
|
|
4422
|
+
reports: [],
|
|
4423
|
+
updatedAt: null,
|
|
4424
|
+
lastSeen: null,
|
|
4262
4425
|
techStack: detectTechStack(process.cwd())
|
|
4263
4426
|
};
|
|
4264
4427
|
}
|
|
4265
4428
|
return state.project_hashes[fp2];
|
|
4266
4429
|
}
|
|
4430
|
+
function touchProjectBucket(state, fp2, meta = {}) {
|
|
4431
|
+
if (!fp2 || fp2 === "unknown")
|
|
4432
|
+
return null;
|
|
4433
|
+
const bucket = ensureProjectBucket(state, fp2);
|
|
4434
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4435
|
+
bucket.updatedAt = now;
|
|
4436
|
+
bucket.lastSeen = now;
|
|
4437
|
+
if (typeof meta.projectName === "string" && meta.projectName.trim()) {
|
|
4438
|
+
bucket.projectName = meta.projectName.trim();
|
|
4439
|
+
}
|
|
4440
|
+
if (typeof meta.sessionId === "string" && meta.sessionId.trim()) {
|
|
4441
|
+
bucket.sessions ??= [];
|
|
4442
|
+
if (!bucket.sessions.includes(meta.sessionId)) {
|
|
4443
|
+
bucket.sessions.push(meta.sessionId);
|
|
4444
|
+
bucket.sessions = bucket.sessions.slice(-30);
|
|
4445
|
+
}
|
|
4446
|
+
bucket.totalSessions = Math.max(Number(bucket.totalSessions || 0), bucket.sessions.length);
|
|
4447
|
+
}
|
|
4448
|
+
if (typeof meta.reportId === "string" && meta.reportId.trim()) {
|
|
4449
|
+
bucket.reports ??= [];
|
|
4450
|
+
if (!bucket.reports.includes(meta.reportId)) {
|
|
4451
|
+
bucket.reports.push(meta.reportId);
|
|
4452
|
+
bucket.reports = bucket.reports.slice(-50);
|
|
4453
|
+
}
|
|
4454
|
+
}
|
|
4455
|
+
if (typeof meta.topic === "string" && meta.topic.trim()) {
|
|
4456
|
+
bucket.commonTopics ??= [];
|
|
4457
|
+
if (!bucket.commonTopics.includes(meta.topic)) {
|
|
4458
|
+
bucket.commonTopics.push(meta.topic);
|
|
4459
|
+
bucket.commonTopics = bucket.commonTopics.slice(-20);
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
return bucket;
|
|
4463
|
+
}
|
|
4267
4464
|
function detectTechStack(dir) {
|
|
4268
4465
|
const stacks = [];
|
|
4269
4466
|
try {
|
|
@@ -4394,6 +4591,18 @@ function recordCacheSaving(tool2, saveEst, meta = {}) {
|
|
|
4394
4591
|
s.sessions[sid2].cache_savings_usd = roundUsd(Number(s.sessions[sid2].cache_savings_usd || 0) + delta);
|
|
4395
4592
|
s.lifetime.cache_savings_usd = roundUsd(Number(s.lifetime.cache_savings_usd || 0) + delta);
|
|
4396
4593
|
}
|
|
4594
|
+
try {
|
|
4595
|
+
if (currentProjectFingerprint) {
|
|
4596
|
+
const pstate = loadProjectState();
|
|
4597
|
+
touchProjectBucket(pstate, currentProjectFingerprint, {
|
|
4598
|
+
sessionId: sid2,
|
|
4599
|
+
projectName: currentProjectName || "",
|
|
4600
|
+
topic: meta?.hash ? String(meta.hash).slice(0, 16) : "cache"
|
|
4601
|
+
});
|
|
4602
|
+
saveProjectState(pstate);
|
|
4603
|
+
}
|
|
4604
|
+
} catch {
|
|
4605
|
+
}
|
|
4397
4606
|
_pruneOldSessions(s);
|
|
4398
4607
|
return s;
|
|
4399
4608
|
});
|
|
@@ -4424,6 +4633,20 @@ function recordMissedContext7(saveEst) {
|
|
|
4424
4633
|
const sid = _OC_SID;
|
|
4425
4634
|
s.sessions[sid] ??= { total_savings_usd: 0, cache_savings_usd: 0, project_name: "", warns: [], cache_hits: [], seenWarnKeys: {} };
|
|
4426
4635
|
s.sessions[sid].context7_missed_usd = Math.round(((s.sessions[sid].context7_missed_usd || 0) + saveEst) * 100) / 100;
|
|
4636
|
+
try {
|
|
4637
|
+
if (currentProjectFingerprint) {
|
|
4638
|
+
const pstate = loadProjectState();
|
|
4639
|
+
const bucket = touchProjectBucket(pstate, currentProjectFingerprint, {
|
|
4640
|
+
sessionId: sid,
|
|
4641
|
+
projectName: currentProjectName || "",
|
|
4642
|
+
topic: "context7"
|
|
4643
|
+
});
|
|
4644
|
+
if (bucket)
|
|
4645
|
+
bucket.context7Bypasses = (bucket.context7Bypasses || 0) + 1;
|
|
4646
|
+
saveProjectState(pstate);
|
|
4647
|
+
}
|
|
4648
|
+
} catch {
|
|
4649
|
+
}
|
|
4427
4650
|
return s;
|
|
4428
4651
|
});
|
|
4429
4652
|
try {
|
|
@@ -4442,16 +4665,6 @@ function recordMissedContext7(saveEst) {
|
|
|
4442
4665
|
_ledgerBufferTimer = setTimeout(_flushLedgerBuffer, LEDGER_BUFFER_FLUSH_MS);
|
|
4443
4666
|
} catch {
|
|
4444
4667
|
}
|
|
4445
|
-
try {
|
|
4446
|
-
if (currentProjectFingerprint) {
|
|
4447
|
-
const pstate = loadProjectState();
|
|
4448
|
-
const bucket = ensureProjectBucket(pstate, currentProjectFingerprint);
|
|
4449
|
-
bucket.context7Bypasses = (bucket.context7Bypasses || 0) + 1;
|
|
4450
|
-
bucket.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
|
|
4451
|
-
saveProjectState(pstate);
|
|
4452
|
-
}
|
|
4453
|
-
} catch {
|
|
4454
|
-
}
|
|
4455
4668
|
try {
|
|
4456
4669
|
updateGlobalLearning((gl) => {
|
|
4457
4670
|
gl.context7_bypasses = Number(gl.context7_bypasses || 0) + 1;
|
|
@@ -4479,7 +4692,7 @@ function loadTodos() {
|
|
|
4479
4692
|
}
|
|
4480
4693
|
function saveTodos(todos) {
|
|
4481
4694
|
try {
|
|
4482
|
-
|
|
4695
|
+
mkdirSync3(dirname4(TODOS_FILE), { recursive: true });
|
|
4483
4696
|
const tmp = TODOS_FILE + ".tmp." + Date.now();
|
|
4484
4697
|
writeFileSync4(tmp, JSON.stringify(todos, null, 2), "utf-8");
|
|
4485
4698
|
renameSync3(tmp, TODOS_FILE);
|
|
@@ -4518,6 +4731,57 @@ function markTodoDone(id2) {
|
|
|
4518
4731
|
function getTodos() {
|
|
4519
4732
|
return loadTodos();
|
|
4520
4733
|
}
|
|
4734
|
+
function _compactSavingsLedgerIfNeeded() {
|
|
4735
|
+
try {
|
|
4736
|
+
if (!existsSync5(SAVINGS_LEDGER_FILE))
|
|
4737
|
+
return;
|
|
4738
|
+
const st = statSync4(SAVINGS_LEDGER_FILE);
|
|
4739
|
+
if (st.size <= LEDGER_ROTATE_MAX_BYTES)
|
|
4740
|
+
return;
|
|
4741
|
+
withFileLock(SAVINGS_LEDGER_FILE, () => {
|
|
4742
|
+
if (!existsSync5(SAVINGS_LEDGER_FILE))
|
|
4743
|
+
return;
|
|
4744
|
+
const lockedStat = statSync4(SAVINGS_LEDGER_FILE);
|
|
4745
|
+
if (lockedStat.size <= LEDGER_ROTATE_MAX_BYTES)
|
|
4746
|
+
return;
|
|
4747
|
+
const raw = readFileSync4(SAVINGS_LEDGER_FILE, "utf-8");
|
|
4748
|
+
if (!raw.trim())
|
|
4749
|
+
return;
|
|
4750
|
+
const now = Date.now();
|
|
4751
|
+
const rows = raw.split("\n").filter(Boolean).map((line) => {
|
|
4752
|
+
let rec = null;
|
|
4753
|
+
try {
|
|
4754
|
+
rec = JSON.parse(line);
|
|
4755
|
+
} catch {
|
|
4756
|
+
rec = null;
|
|
4757
|
+
}
|
|
4758
|
+
const atRaw = rec && typeof rec === "object" ? String(rec.at || rec.ts || "") : "";
|
|
4759
|
+
const atMs = Date.parse(atRaw);
|
|
4760
|
+
return { raw: line.trim(), atMs: Number.isFinite(atMs) ? atMs : null };
|
|
4761
|
+
}).filter((row) => row.raw);
|
|
4762
|
+
const recent = rows.filter((row) => row.atMs != null && now - Number(row.atMs) <= LEDGER_ROTATE_MAX_AGE_MS);
|
|
4763
|
+
const pool = recent.length > 0 ? recent : rows;
|
|
4764
|
+
const capped = pool.length > LEDGER_ROTATE_MAX_LINES ? pool.slice(-LEDGER_ROTATE_MAX_LINES) : pool;
|
|
4765
|
+
let size = 0;
|
|
4766
|
+
const kept = [];
|
|
4767
|
+
for (let i = capped.length - 1; i >= 0; i--) {
|
|
4768
|
+
const line = capped[i].raw;
|
|
4769
|
+
const lineBytes = Buffer.byteLength(line + "\n", "utf-8");
|
|
4770
|
+
if (kept.length > 0 && size + lineBytes > LEDGER_ROTATE_MAX_BYTES)
|
|
4771
|
+
break;
|
|
4772
|
+
kept.push(line);
|
|
4773
|
+
size += lineBytes;
|
|
4774
|
+
}
|
|
4775
|
+
const compacted = kept.reverse().join("\n") + "\n";
|
|
4776
|
+
if (compacted.trim() && compacted !== raw) {
|
|
4777
|
+
const tmp = SAVINGS_LEDGER_FILE + ".tmp." + Date.now();
|
|
4778
|
+
writeFileSync4(tmp, compacted, "utf-8");
|
|
4779
|
+
renameSync3(tmp, SAVINGS_LEDGER_FILE);
|
|
4780
|
+
}
|
|
4781
|
+
}, { timeoutMs: 4e3 });
|
|
4782
|
+
} catch {
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4521
4785
|
function readLedgerTotals() {
|
|
4522
4786
|
const empty = { delegation: 0, cache: 0, context7: 0, total: 0, entries: 0 };
|
|
4523
4787
|
try {
|
|
@@ -4526,15 +4790,19 @@ function readLedgerTotals() {
|
|
|
4526
4790
|
return empty;
|
|
4527
4791
|
}
|
|
4528
4792
|
const st = statSync4(SAVINGS_LEDGER_FILE);
|
|
4529
|
-
if (st.size > 10485760) {
|
|
4530
|
-
_handleStateCorruption2(SAVINGS_LEDGER_FILE);
|
|
4531
|
-
return empty;
|
|
4532
|
-
}
|
|
4533
4793
|
if (st.size === 0) {
|
|
4534
4794
|
_ledgerTotalsCache = { mtime: st.mtimeMs, size: 0, delegation: 0, cache: 0, context7: 0, entries: 0 };
|
|
4535
4795
|
return empty;
|
|
4536
4796
|
}
|
|
4537
|
-
if (
|
|
4797
|
+
if (st.size > LEDGER_ROTATE_MAX_BYTES) {
|
|
4798
|
+
_compactSavingsLedgerIfNeeded();
|
|
4799
|
+
}
|
|
4800
|
+
const currentStat = statSync4(SAVINGS_LEDGER_FILE);
|
|
4801
|
+
if (currentStat.size === 0) {
|
|
4802
|
+
_ledgerTotalsCache = { mtime: currentStat.mtimeMs, size: 0, delegation: 0, cache: 0, context7: 0, entries: 0 };
|
|
4803
|
+
return empty;
|
|
4804
|
+
}
|
|
4805
|
+
if (_ledgerTotalsCache.mtime === currentStat.mtimeMs && _ledgerTotalsCache.size === currentStat.size) {
|
|
4538
4806
|
return {
|
|
4539
4807
|
delegation: Math.round(_ledgerTotalsCache.delegation * 1e3) / 1e3,
|
|
4540
4808
|
cache: Math.round(_ledgerTotalsCache.cache * 1e3) / 1e3,
|
|
@@ -4548,9 +4816,9 @@ function readLedgerTotals() {
|
|
|
4548
4816
|
let context7 = 0;
|
|
4549
4817
|
let entries = 0;
|
|
4550
4818
|
let raw = "";
|
|
4551
|
-
let incremental = _ledgerTotalsCache.size > 0 &&
|
|
4819
|
+
let incremental = _ledgerTotalsCache.size > 0 && currentStat.size >= _ledgerTotalsCache.size && _ledgerTotalsCache.mtime > 0;
|
|
4552
4820
|
if (incremental) {
|
|
4553
|
-
const deltaSize =
|
|
4821
|
+
const deltaSize = currentStat.size - _ledgerTotalsCache.size;
|
|
4554
4822
|
if (deltaSize > 0) {
|
|
4555
4823
|
const fd = openSync(SAVINGS_LEDGER_FILE, "r");
|
|
4556
4824
|
try {
|
|
@@ -4576,8 +4844,8 @@ function readLedgerTotals() {
|
|
|
4576
4844
|
}
|
|
4577
4845
|
if (!raw.trim()) {
|
|
4578
4846
|
_ledgerTotalsCache = {
|
|
4579
|
-
mtime:
|
|
4580
|
-
size:
|
|
4847
|
+
mtime: currentStat.mtimeMs,
|
|
4848
|
+
size: currentStat.size,
|
|
4581
4849
|
delegation,
|
|
4582
4850
|
cache,
|
|
4583
4851
|
context7,
|
|
@@ -4620,7 +4888,7 @@ function readLedgerTotals() {
|
|
|
4620
4888
|
else
|
|
4621
4889
|
delegation += amt;
|
|
4622
4890
|
}
|
|
4623
|
-
_ledgerTotalsCache = { mtime:
|
|
4891
|
+
_ledgerTotalsCache = { mtime: currentStat.mtimeMs, size: currentStat.size, delegation, cache, context7, entries };
|
|
4624
4892
|
const total = delegation + cache;
|
|
4625
4893
|
return {
|
|
4626
4894
|
delegation: Math.round(delegation * 1e3) / 1e3,
|
|
@@ -4705,7 +4973,7 @@ function saveSessionCheckpoint() {
|
|
|
4705
4973
|
model: session.model || ""
|
|
4706
4974
|
};
|
|
4707
4975
|
const cpPath = join4(getSessionRoot(), "checkpoint.json");
|
|
4708
|
-
|
|
4976
|
+
mkdirSync3(dirname4(cpPath), { recursive: true });
|
|
4709
4977
|
const tmp = cpPath + ".tmp";
|
|
4710
4978
|
writeFileSync4(tmp, JSON.stringify(cp, null, 2) + "\n");
|
|
4711
4979
|
renameSync3(tmp, cpPath);
|
|
@@ -4746,20 +5014,6 @@ function getOpenCodeDesktopHome() {
|
|
|
4746
5014
|
return process.env.VIBEOS_OPENCODE_DESKTOP_HOME || join5(process.env.HOME || homedir4(), "Library", "Application Support", "ai.opencode.desktop");
|
|
4747
5015
|
}
|
|
4748
5016
|
var TIERS_FILE2 = join5(getVibeOSHome4(), "model-tiers.json");
|
|
4749
|
-
function _handleStateCorruption3(path) {
|
|
4750
|
-
const backupDir = join5(getVibeOSHome4(), ".backups");
|
|
4751
|
-
mkdirSync5(backupDir, { recursive: true });
|
|
4752
|
-
const backupPath = join5(backupDir, basename4(path) + ".corrupted." + Date.now());
|
|
4753
|
-
try {
|
|
4754
|
-
copyFileSync3(path, backupPath);
|
|
4755
|
-
} catch {
|
|
4756
|
-
}
|
|
4757
|
-
const logPath = join5(getVibeOSHome4(), ".state-corruption-log.jsonl");
|
|
4758
|
-
try {
|
|
4759
|
-
appendFileSync4(logPath, JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), path, backup: backupPath }) + "\n");
|
|
4760
|
-
} catch {
|
|
4761
|
-
}
|
|
4762
|
-
}
|
|
4763
5017
|
function _lockPathFor2(filePath) {
|
|
4764
5018
|
const hash = createHash2("sha1").update(String(filePath || "")).digest("hex");
|
|
4765
5019
|
return join5(getVibeOSHome4(), ".vibeOS-locks", `${hash}.lock`);
|
|
@@ -4771,7 +5025,7 @@ function withFileLock2(filePath, fn, opts = {}) {
|
|
|
4771
5025
|
const start = Date.now();
|
|
4772
5026
|
while (Date.now() - start < timeoutMs) {
|
|
4773
5027
|
try {
|
|
4774
|
-
|
|
5028
|
+
mkdirSync4(join5(getVibeOSHome4(), ".vibeOS-locks"), { recursive: true });
|
|
4775
5029
|
const fd = openSync2(lockPath, "wx");
|
|
4776
5030
|
try {
|
|
4777
5031
|
writeFileSync5(fd, `${process.pid}
|
|
@@ -5289,7 +5543,7 @@ function _loadDynamicPricingCache() {
|
|
|
5289
5543
|
return {};
|
|
5290
5544
|
const st = statSync5(PRICING_CACHE_FILE2);
|
|
5291
5545
|
if (st.size > 10485760) {
|
|
5292
|
-
|
|
5546
|
+
_handleStateCorruption(PRICING_CACHE_FILE2);
|
|
5293
5547
|
_dynamicPricingCache = {};
|
|
5294
5548
|
return {};
|
|
5295
5549
|
}
|
|
@@ -5297,7 +5551,7 @@ function _loadDynamicPricingCache() {
|
|
|
5297
5551
|
const map = raw?.models && typeof raw.models === "object" ? raw.models : {};
|
|
5298
5552
|
_dynamicPricingCache = map;
|
|
5299
5553
|
} catch {
|
|
5300
|
-
|
|
5554
|
+
_handleStateCorruption(PRICING_CACHE_FILE2);
|
|
5301
5555
|
_dynamicPricingCache = {};
|
|
5302
5556
|
}
|
|
5303
5557
|
return _dynamicPricingCache;
|
|
@@ -5334,7 +5588,7 @@ function _writeDynamicPricingCache(modelsMap) {
|
|
|
5334
5588
|
const PRICING_CACHE_FILE2 = join5(getVibeOSHome4(), "model-pricing-cache.json");
|
|
5335
5589
|
try {
|
|
5336
5590
|
withFileLock2(PRICING_CACHE_FILE2, () => {
|
|
5337
|
-
|
|
5591
|
+
mkdirSync4(dirname5(PRICING_CACHE_FILE2), { recursive: true });
|
|
5338
5592
|
let merged = {};
|
|
5339
5593
|
try {
|
|
5340
5594
|
if (existsSync6(PRICING_CACHE_FILE2)) {
|
|
@@ -5392,7 +5646,7 @@ function _loadPricingOverrides() {
|
|
|
5392
5646
|
return {};
|
|
5393
5647
|
const st = statSync5(tiersFile);
|
|
5394
5648
|
if (st.size > 10485760) {
|
|
5395
|
-
|
|
5649
|
+
_handleStateCorruption(tiersFile);
|
|
5396
5650
|
_pricingOverridesCache = {};
|
|
5397
5651
|
return {};
|
|
5398
5652
|
}
|
|
@@ -5423,7 +5677,7 @@ function _loadPricingOverrides() {
|
|
|
5423
5677
|
}
|
|
5424
5678
|
_pricingOverridesCache = out;
|
|
5425
5679
|
} catch {
|
|
5426
|
-
|
|
5680
|
+
_handleStateCorruption(join5(home, "model-tiers.json"));
|
|
5427
5681
|
_pricingOverridesCache = {};
|
|
5428
5682
|
}
|
|
5429
5683
|
return _pricingOverridesCache;
|
|
@@ -5559,7 +5813,7 @@ function loadSelection2() {
|
|
|
5559
5813
|
return DFLT_SEL2;
|
|
5560
5814
|
const st = statSync5(TIERS_FILE3);
|
|
5561
5815
|
if (st.size > 10485760) {
|
|
5562
|
-
|
|
5816
|
+
_handleStateCorruption(TIERS_FILE3);
|
|
5563
5817
|
return DFLT_SEL2;
|
|
5564
5818
|
}
|
|
5565
5819
|
const j = safeJsonParse3(readFileSync5(TIERS_FILE3, "utf-8"));
|
|
@@ -5582,7 +5836,7 @@ function loadSelection2() {
|
|
|
5582
5836
|
executed_model: j?.selection?.executed_model || null
|
|
5583
5837
|
};
|
|
5584
5838
|
} catch {
|
|
5585
|
-
|
|
5839
|
+
_handleStateCorruption(TIERS_FILE3);
|
|
5586
5840
|
return DFLT_SEL2;
|
|
5587
5841
|
}
|
|
5588
5842
|
}
|
|
@@ -5810,7 +6064,7 @@ function loadTrinitySlotsFromTiersFile() {
|
|
|
5810
6064
|
return false;
|
|
5811
6065
|
const st = statSync5(TIERS_FILE3);
|
|
5812
6066
|
if (st.size > 10485760) {
|
|
5813
|
-
|
|
6067
|
+
_handleStateCorruption(TIERS_FILE3);
|
|
5814
6068
|
return false;
|
|
5815
6069
|
}
|
|
5816
6070
|
const tiersData = safeJsonParse3(readFileSync5(TIERS_FILE3, "utf-8")) || {};
|
|
@@ -5928,7 +6182,7 @@ function applySlot2(slot, projectDir = "") {
|
|
|
5928
6182
|
}
|
|
5929
6183
|
|
|
5930
6184
|
// src/lib/turn-classify.js
|
|
5931
|
-
import { readFileSync as readFileSync8, writeFileSync as writeFileSync8, existsSync as existsSync9, mkdirSync as
|
|
6185
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync8, existsSync as existsSync9, mkdirSync as mkdirSync7, renameSync as renameSync5 } from "node:fs";
|
|
5932
6186
|
import { join as join7, dirname as dirname8 } from "node:path";
|
|
5933
6187
|
|
|
5934
6188
|
// src/vibeOS-lib/blackbox/resolution-tracker.js
|
|
@@ -7362,8 +7616,8 @@ function projectStructuredFromText(raw, selection, creditPercent = 0) {
|
|
|
7362
7616
|
}
|
|
7363
7617
|
|
|
7364
7618
|
// src/lib/reporting.js
|
|
7365
|
-
import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync11, mkdirSync as
|
|
7366
|
-
import { join as join9
|
|
7619
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync11, mkdirSync as mkdirSync8, statSync as statSync6, rmSync as rmSync4 } from "node:fs";
|
|
7620
|
+
import { join as join9 } from "node:path";
|
|
7367
7621
|
function getVibeOSHome7() {
|
|
7368
7622
|
return process.env.VIBEOS_HOME || join9(process.env.HOME || "", ".claude");
|
|
7369
7623
|
}
|
|
@@ -7378,27 +7632,18 @@ var REPORTS_INDEX = getReportsIndexPath();
|
|
|
7378
7632
|
var currentProjectFingerprint2 = "";
|
|
7379
7633
|
var currentProjectName2 = "";
|
|
7380
7634
|
var currentSessionId2 = "";
|
|
7381
|
-
function _handleStateCorruption4(path) {
|
|
7382
|
-
const backupDir = join9(getVibeOSHome7(), ".backups");
|
|
7383
|
-
mkdirSync9(backupDir, { recursive: true });
|
|
7384
|
-
const backupPath = join9(backupDir, basename5(path) + ".corrupted." + Date.now());
|
|
7385
|
-
try {
|
|
7386
|
-
copyFileSync4(path, backupPath);
|
|
7387
|
-
} catch {
|
|
7388
|
-
}
|
|
7389
|
-
}
|
|
7390
7635
|
function readJsonOrEmpty2(filePath) {
|
|
7391
7636
|
try {
|
|
7392
7637
|
if (!existsSync11(filePath))
|
|
7393
7638
|
return {};
|
|
7394
7639
|
const st = statSync6(filePath);
|
|
7395
7640
|
if (st.size > 10485760) {
|
|
7396
|
-
|
|
7641
|
+
_handleStateCorruption(filePath);
|
|
7397
7642
|
return {};
|
|
7398
7643
|
}
|
|
7399
7644
|
return safeJsonParse3(readFileSync10(filePath, "utf-8"));
|
|
7400
7645
|
} catch {
|
|
7401
|
-
|
|
7646
|
+
_handleStateCorruption(filePath);
|
|
7402
7647
|
return {};
|
|
7403
7648
|
}
|
|
7404
7649
|
}
|
|
@@ -7413,7 +7658,7 @@ function saveReportsIndex(idx) {
|
|
|
7413
7658
|
const reportsIndexPath = getReportsIndexPath();
|
|
7414
7659
|
const reportsDir = getReportsDir();
|
|
7415
7660
|
withFileLock(reportsIndexPath, () => {
|
|
7416
|
-
|
|
7661
|
+
mkdirSync8(reportsDir, { recursive: true });
|
|
7417
7662
|
writeFileSync9(reportsIndexPath, JSON.stringify(idx, null, 2) + "\n");
|
|
7418
7663
|
});
|
|
7419
7664
|
} catch (err) {
|
|
@@ -7508,13 +7753,22 @@ function _parseMetrics(v) {
|
|
|
7508
7753
|
function saveReport({ type = "manual", summary = "", findings = null, metrics = null, narrative = "", tags = [], fingerprint = null, status = "pending", task_description = "", outcome_verified = false } = {}) {
|
|
7509
7754
|
const parsedFindings = _parseFindings(findings);
|
|
7510
7755
|
const parsedMetrics = _parseMetrics(metrics);
|
|
7756
|
+
const metricsObject = parsedMetrics && typeof parsedMetrics === "object" && !Array.isArray(parsedMetrics) ? parsedMetrics : {};
|
|
7757
|
+
const metricsSessionId = typeof metricsObject.sessionId === "string" && metricsObject.sessionId.trim() ? metricsObject.sessionId.trim() : "";
|
|
7758
|
+
const metricsProjectName = typeof metricsObject.projectName === "string" && metricsObject.projectName.trim() ? metricsObject.projectName.trim() : "";
|
|
7759
|
+
const metricsProjectFingerprint = typeof metricsObject.projectFingerprint === "string" && metricsObject.projectFingerprint.trim() ? metricsObject.projectFingerprint.trim() : "";
|
|
7511
7760
|
if (_wouldBeDuplicate(type, summary))
|
|
7512
7761
|
return null;
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7762
|
+
if (!currentProjectFingerprint2 && metricsProjectFingerprint)
|
|
7763
|
+
currentProjectFingerprint2 = metricsProjectFingerprint;
|
|
7764
|
+
if (!currentProjectName2 && metricsProjectName)
|
|
7765
|
+
currentProjectName2 = metricsProjectName;
|
|
7766
|
+
if (!currentSessionId2 && metricsSessionId)
|
|
7767
|
+
currentSessionId2 = metricsSessionId;
|
|
7768
|
+
const liveSessionId = getCurrentSessionId() || getOcSessionId() || "";
|
|
7769
|
+
const fp2 = fingerprint || metricsProjectFingerprint || currentProjectFingerprint || currentProjectFingerprint2 || "unknown";
|
|
7770
|
+
const projectName = metricsProjectName || currentProjectName || currentProjectName2 || "unknown";
|
|
7771
|
+
const sessionId = metricsSessionId || liveSessionId || currentSessionId2 || "unknown";
|
|
7518
7772
|
const id2 = generateReportId(type, fp2);
|
|
7519
7773
|
const report = {
|
|
7520
7774
|
meta: { id: id2, project: projectName, fingerprint: fp2, type, created: (/* @__PURE__ */ new Date()).toISOString(), sessionId },
|
|
@@ -7531,13 +7785,26 @@ function saveReport({ type = "manual", summary = "", findings = null, metrics =
|
|
|
7531
7785
|
const reportsIndexPath = getReportsIndexPath();
|
|
7532
7786
|
const reportsDir = getReportsDir();
|
|
7533
7787
|
withFileLock(reportsIndexPath, () => {
|
|
7534
|
-
|
|
7788
|
+
mkdirSync8(reportsDir, { recursive: true });
|
|
7535
7789
|
writeFileSync9(join9(reportsDir, `${id2}.json`), JSON.stringify(report, null, 2) + "\n");
|
|
7536
7790
|
const idx = reportsIndex();
|
|
7537
7791
|
const _sum = (summary || "").slice(0, 80);
|
|
7538
7792
|
idx.reports.push({ id: id2, type, project: report.meta.project, fingerprint: fp2, created: report.meta.created, summary: _sum });
|
|
7539
7793
|
writeFileSync9(reportsIndexPath, JSON.stringify(idx, null, 2) + "\n");
|
|
7540
7794
|
});
|
|
7795
|
+
try {
|
|
7796
|
+
if (fp2 && fp2 !== "unknown") {
|
|
7797
|
+
const pstate = loadProjectState();
|
|
7798
|
+
touchProjectBucket(pstate, fp2, {
|
|
7799
|
+
sessionId,
|
|
7800
|
+
projectName: projectName || "",
|
|
7801
|
+
reportId: id2,
|
|
7802
|
+
topic: type || "report"
|
|
7803
|
+
});
|
|
7804
|
+
saveProjectState(pstate);
|
|
7805
|
+
}
|
|
7806
|
+
} catch {
|
|
7807
|
+
}
|
|
7541
7808
|
} catch (err) {
|
|
7542
7809
|
console.error(`[vibeOS] report/index write failed: ${err.message}`);
|
|
7543
7810
|
return null;
|
|
@@ -7894,6 +8161,14 @@ var RAW_MODE = {
|
|
|
7894
8161
|
desc: "Pure v4 Pro baseline. No vibeOS overhead."
|
|
7895
8162
|
};
|
|
7896
8163
|
var ALL_MODES = [...BRANDED_MODES, ...RUNTIME_MODES, RAW_MODE];
|
|
8164
|
+
function resolveCascadeSlot(pipeline = []) {
|
|
8165
|
+
const normalized = Array.isArray(pipeline) ? pipeline.map((t) => String(t || "").toLowerCase()) : [];
|
|
8166
|
+
if (normalized.includes("brain"))
|
|
8167
|
+
return "brain";
|
|
8168
|
+
if (normalized.includes("medium"))
|
|
8169
|
+
return "medium";
|
|
8170
|
+
return "cheap";
|
|
8171
|
+
}
|
|
7897
8172
|
|
|
7898
8173
|
// src/lib/trinity-tool.js
|
|
7899
8174
|
var MIN_TOOL_BREAKDOWN_THRESHOLD = 5e-3;
|
|
@@ -8177,8 +8452,7 @@ function createTrinityTool(deps) {
|
|
|
8177
8452
|
const allEntries = [...BRANDED_MODES, ...RUNTIME_MODES];
|
|
8178
8453
|
const modeEntry = allEntries.find((e) => e.id === slot);
|
|
8179
8454
|
if (modeEntry) {
|
|
8180
|
-
const
|
|
8181
|
-
const tierSlot = (/* @__PURE__ */ new Set(["brain", "medium", "cheap"])).has(rawTier) ? rawTier : "cheap";
|
|
8455
|
+
const tierSlot = resolveCascadeSlot(modeEntry.pipeline);
|
|
8182
8456
|
deps.writeSessionSlot(deps._OC_SID, tierSlot);
|
|
8183
8457
|
deps.writeSelection("slot_locked", resolvedSlot !== "auto");
|
|
8184
8458
|
deps.writeSelection("active_slot", tierSlot);
|
|
@@ -9511,12 +9785,12 @@ async function probeModel(modelId, auth, providers = null) {
|
|
|
9511
9785
|
}
|
|
9512
9786
|
|
|
9513
9787
|
// src/lib/hooks/footer.js
|
|
9514
|
-
import { readFileSync as readFileSync14, appendFileSync as
|
|
9788
|
+
import { readFileSync as readFileSync14, appendFileSync as appendFileSync4, mkdirSync as mkdirSync10 } from "node:fs";
|
|
9515
9789
|
import { join as join15 } from "node:path";
|
|
9516
9790
|
|
|
9517
9791
|
// src/lib/hooks/chat-transform.js
|
|
9518
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync12, appendFileSync as
|
|
9519
|
-
import { join as join14, dirname as dirname10, basename as
|
|
9792
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync12, appendFileSync as appendFileSync3, existsSync as existsSync14, mkdirSync as mkdirSync9, rmSync as rmSync5, readdirSync as readdirSync3, statSync as statSync7 } from "node:fs";
|
|
9793
|
+
import { join as join14, dirname as dirname10, basename as basename3 } from "node:path";
|
|
9520
9794
|
import { createHash as createHash3 } from "node:crypto";
|
|
9521
9795
|
|
|
9522
9796
|
// src/lib/mode-policy.js
|
|
@@ -9830,6 +10104,11 @@ function noteProjectPattern(kind, key, summary, meta = {}) {
|
|
|
9830
10104
|
if (meta.path)
|
|
9831
10105
|
row.path = meta.path;
|
|
9832
10106
|
target[key] = row;
|
|
10107
|
+
touchProjectBucket(pstate, currentProjectFingerprint, {
|
|
10108
|
+
sessionId: getCurrentSessionId(),
|
|
10109
|
+
projectName: currentProjectName || "",
|
|
10110
|
+
topic: key
|
|
10111
|
+
});
|
|
9833
10112
|
const entries = Object.entries(target);
|
|
9834
10113
|
if (entries.length > 50) {
|
|
9835
10114
|
entries.sort((a, b) => String(b[1]?.lastSeen || "").localeCompare(String(a[1]?.lastSeen || "")));
|
|
@@ -10063,6 +10342,9 @@ function recordSaving(tool2, reason, saveEst, meta = {}) {
|
|
|
10063
10342
|
s.last_updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
10064
10343
|
_pruneOldSessions(s);
|
|
10065
10344
|
});
|
|
10345
|
+
const projectFingerprint2 = typeof meta?.projectFingerprint === "string" && meta.projectFingerprint.trim() ? meta.projectFingerprint.trim() : currentProjectFingerprint || "";
|
|
10346
|
+
const projectName = typeof meta?.projectName === "string" && meta.projectName.trim() ? meta.projectName.trim() : currentProjectName || "";
|
|
10347
|
+
const sessionId = typeof meta?.sessionId === "string" && meta.sessionId.trim() ? meta.sessionId.trim() : getCurrentSessionId() || _OC_SID;
|
|
10066
10348
|
const entry = JSON.stringify({
|
|
10067
10349
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10068
10350
|
usd: saveEst,
|
|
@@ -10070,9 +10352,21 @@ function recordSaving(tool2, reason, saveEst, meta = {}) {
|
|
|
10070
10352
|
tool: tool2,
|
|
10071
10353
|
reason,
|
|
10072
10354
|
saveEst,
|
|
10073
|
-
fgp:
|
|
10355
|
+
fgp: projectFingerprint2
|
|
10074
10356
|
});
|
|
10075
10357
|
_ledgerBuffer.push(entry);
|
|
10358
|
+
try {
|
|
10359
|
+
if (projectFingerprint2) {
|
|
10360
|
+
const pstate = loadProjectState();
|
|
10361
|
+
touchProjectBucket(pstate, projectFingerprint2, {
|
|
10362
|
+
sessionId,
|
|
10363
|
+
projectName,
|
|
10364
|
+
topic: tool2 || reason || "saving"
|
|
10365
|
+
});
|
|
10366
|
+
saveProjectState(pstate);
|
|
10367
|
+
}
|
|
10368
|
+
} catch {
|
|
10369
|
+
}
|
|
10076
10370
|
if (_ledgerBuffer.length >= LEDGER_BUFFER_MAX)
|
|
10077
10371
|
_flushLedgerBuffer();
|
|
10078
10372
|
else if (!_ledgerBufferTimer)
|
|
@@ -10243,6 +10537,17 @@ function ensureProjectContext(hookDirectory) {
|
|
|
10243
10537
|
if (name && name !== currentProjectName)
|
|
10244
10538
|
setCurrentProjectName(name);
|
|
10245
10539
|
}
|
|
10540
|
+
try {
|
|
10541
|
+
if (resolved) {
|
|
10542
|
+
const pstate = loadProjectState();
|
|
10543
|
+
touchProjectBucket(pstate, resolved, {
|
|
10544
|
+
sessionId: _OC_SID3,
|
|
10545
|
+
projectName: currentProjectName || (hookDirectory ? hookDirectory.split("/").filter(Boolean).pop() || "" : "")
|
|
10546
|
+
});
|
|
10547
|
+
saveProjectState(pstate);
|
|
10548
|
+
}
|
|
10549
|
+
} catch {
|
|
10550
|
+
}
|
|
10246
10551
|
return resolved;
|
|
10247
10552
|
}
|
|
10248
10553
|
var latestUserIntent = null;
|
|
@@ -10303,14 +10608,14 @@ function observeUserCorrection(text) {
|
|
|
10303
10608
|
}
|
|
10304
10609
|
}
|
|
10305
10610
|
function buildProjectBriefing(directory3) {
|
|
10306
|
-
const label = currentProjectName || (directory3 ?
|
|
10611
|
+
const label = currentProjectName || (directory3 ? basename3(directory3) : "");
|
|
10307
10612
|
if (!label)
|
|
10308
10613
|
return null;
|
|
10309
10614
|
return `[project memory] Active project: ${label}. Stay focused on the current repository and prefer the existing workflow.`;
|
|
10310
10615
|
}
|
|
10311
10616
|
function ensureProjectSkill(dir, fp2) {
|
|
10312
10617
|
const skillsDir = join14(dir, ".opencode", "skills");
|
|
10313
|
-
const projectName =
|
|
10618
|
+
const projectName = basename3(dir);
|
|
10314
10619
|
const skillDir = join14(skillsDir, projectName);
|
|
10315
10620
|
const skillPath = join14(skillDir, "SKILL.md");
|
|
10316
10621
|
if (existsSync14(skillPath)) {
|
|
@@ -10375,7 +10680,7 @@ function ensureProjectSkill(dir, fp2) {
|
|
|
10375
10680
|
content += "\n";
|
|
10376
10681
|
}
|
|
10377
10682
|
try {
|
|
10378
|
-
|
|
10683
|
+
mkdirSync9(skillDir, { recursive: true });
|
|
10379
10684
|
writeFileSync12(skillPath, content, "utf-8");
|
|
10380
10685
|
console.error(`[vibeOS] Project Guard: created .opencode/skills/${projectName}/SKILL.md`);
|
|
10381
10686
|
return { created: true, path: skillPath, skipped: false };
|
|
@@ -10534,7 +10839,7 @@ ${raw}
|
|
|
10534
10839
|
const sessPath = join14(getSessionScratchpadDir(), `${hash}.txt`);
|
|
10535
10840
|
const globalPath = join14(globalDir, `${hash}.txt`);
|
|
10536
10841
|
try {
|
|
10537
|
-
|
|
10842
|
+
mkdirSync9(globalDir, { recursive: true });
|
|
10538
10843
|
ensureSessionScratchpadDirs();
|
|
10539
10844
|
if (!existsSync14(globalPath)) {
|
|
10540
10845
|
writeFileSync12(globalPath, raw);
|
|
@@ -10947,8 +11252,8 @@ var onSystemTransform = async (_input, output) => {
|
|
|
10947
11252
|
fp: currentProjectFingerprint || ""
|
|
10948
11253
|
}) + "\n";
|
|
10949
11254
|
try {
|
|
10950
|
-
|
|
10951
|
-
|
|
11255
|
+
mkdirSync9(calDir, { recursive: true });
|
|
11256
|
+
appendFileSync3(calFile, calRecord);
|
|
10952
11257
|
} catch {
|
|
10953
11258
|
}
|
|
10954
11259
|
if (!oneShot("vibeos_dashboard_instruct")) {
|
|
@@ -11364,8 +11669,8 @@ ${vibeLine}`;
|
|
|
11364
11669
|
tracker.recordOutcome(finalOutcome);
|
|
11365
11670
|
syncOutcomeToApi(finalOutcome);
|
|
11366
11671
|
try {
|
|
11367
|
-
|
|
11368
|
-
|
|
11672
|
+
mkdirSync10(getVibeOSHome10(), { recursive: true });
|
|
11673
|
+
appendFileSync4(join15(getVibeOSHome10(), "calibration-data.jsonl"), JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), event: "outcome", sid: getSessionId(), outcome: finalOutcome }) + "\n");
|
|
11369
11674
|
} catch {
|
|
11370
11675
|
}
|
|
11371
11676
|
}
|
|
@@ -11391,8 +11696,8 @@ ${vibeLine} \u2014`);
|
|
|
11391
11696
|
}
|
|
11392
11697
|
|
|
11393
11698
|
// src/lib/hooks/tool-execute.js
|
|
11394
|
-
import { writeFileSync as writeFileSync14, appendFileSync as
|
|
11395
|
-
import { join as join17, dirname as dirname12, basename as
|
|
11699
|
+
import { writeFileSync as writeFileSync14, appendFileSync as appendFileSync6, existsSync as existsSync16, mkdirSync as mkdirSync12 } from "node:fs";
|
|
11700
|
+
import { join as join17, dirname as dirname12, basename as basename4 } from "node:path";
|
|
11396
11701
|
import { createHash as createHash5 } from "node:crypto";
|
|
11397
11702
|
|
|
11398
11703
|
// src/lib/cost-anomaly.js
|
|
@@ -11456,7 +11761,7 @@ function getCostAnomalyDetector() {
|
|
|
11456
11761
|
init_flow_enforcer();
|
|
11457
11762
|
|
|
11458
11763
|
// src/lib/tdd-enforcer.js
|
|
11459
|
-
import { readFileSync as readFileSync15, writeFileSync as writeFileSync13, appendFileSync as
|
|
11764
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync13, appendFileSync as appendFileSync5, existsSync as existsSync15, mkdirSync as mkdirSync11, statSync as statSync8, readdirSync as readdirSync4, rmSync as rmSync6, openSync as openSync3 } from "node:fs";
|
|
11460
11765
|
import { join as join16, dirname as dirname11 } from "node:path";
|
|
11461
11766
|
import { createHash as createHash4 } from "node:crypto";
|
|
11462
11767
|
|
|
@@ -12559,7 +12864,7 @@ var COOLDOWN_MS = 6e4;
|
|
|
12559
12864
|
var _enforcementCooldown = /* @__PURE__ */ new Set();
|
|
12560
12865
|
function _acquireLock(testPath) {
|
|
12561
12866
|
try {
|
|
12562
|
-
|
|
12867
|
+
mkdirSync11(ENFORCEMENT_LOCK_DIR, { recursive: true });
|
|
12563
12868
|
const hash = createHash4("sha256").update(testPath).digest("hex").slice(0, 16);
|
|
12564
12869
|
const lockPath = join16(ENFORCEMENT_LOCK_DIR, `${hash}.lock`);
|
|
12565
12870
|
try {
|
|
@@ -12616,10 +12921,10 @@ function _isInCooldown(testPath) {
|
|
|
12616
12921
|
}
|
|
12617
12922
|
function _recordCooldown(testPath) {
|
|
12618
12923
|
try {
|
|
12619
|
-
|
|
12924
|
+
mkdirSync11(dirname11(ENFORCEMENT_COOLDOWN_FILE2), { recursive: true });
|
|
12620
12925
|
const hash = createHash4("sha256").update(testPath).digest("hex").slice(0, 16);
|
|
12621
12926
|
const entry = JSON.stringify({ h: hash, ts: Date.now() }) + "\n";
|
|
12622
|
-
|
|
12927
|
+
appendFileSync5(ENFORCEMENT_COOLDOWN_FILE2, entry);
|
|
12623
12928
|
const lines = readFileSync15(ENFORCEMENT_COOLDOWN_FILE2, "utf-8").trim().split("\n").filter(Boolean);
|
|
12624
12929
|
if (lines.length > 500) {
|
|
12625
12930
|
writeFileSync13(ENFORCEMENT_COOLDOWN_FILE2, lines.slice(-200).join("\n") + "\n");
|
|
@@ -12708,7 +13013,7 @@ function enforceTestFile(filePath) {
|
|
|
12708
13013
|
if (!_acquireLock(skeleton.path))
|
|
12709
13014
|
return null;
|
|
12710
13015
|
try {
|
|
12711
|
-
|
|
13016
|
+
mkdirSync11(skeleton.dir, { recursive: true });
|
|
12712
13017
|
writeFileSync13(skeleton.path, skeleton.content);
|
|
12713
13018
|
_enforcementCooldown.add(skeleton.path);
|
|
12714
13019
|
_recordCooldown(skeleton.path);
|
|
@@ -12934,7 +13239,7 @@ function _isProtectedToolPath(pathValue) {
|
|
|
12934
13239
|
}
|
|
12935
13240
|
function _mutateBlockedToolArgs(toolName, sources, blockedPath, outputObj) {
|
|
12936
13241
|
const tLower = String(toolName || "").toLowerCase();
|
|
12937
|
-
const blockedBase =
|
|
13242
|
+
const blockedBase = basename4(blockedPath || "") || "blocked";
|
|
12938
13243
|
for (const src of sources) {
|
|
12939
13244
|
if (!src || typeof src !== "object")
|
|
12940
13245
|
continue;
|
|
@@ -13243,7 +13548,7 @@ ${argsJson}
|
|
|
13243
13548
|
_mutateBlockedToolArgs(t, argSources, checkPath, output);
|
|
13244
13549
|
if (shouldLogWarn(`${t}|protect|${checkPath}`))
|
|
13245
13550
|
console.error(`[vibeOS] [protection] BLOCKED direct ${t} in self-protected directory: ${checkPath}`);
|
|
13246
|
-
pendingUiNote = `[LOCK] Self-modification paused: ${
|
|
13551
|
+
pendingUiNote = `[LOCK] Self-modification paused: ${basename4(checkPath)} is in a protected project tree. Use a manual git workflow.`;
|
|
13247
13552
|
enforcementBlocked = true;
|
|
13248
13553
|
return;
|
|
13249
13554
|
}
|
|
@@ -13266,7 +13571,12 @@ ${argsJson}
|
|
|
13266
13571
|
costDetector.record(modelCost);
|
|
13267
13572
|
}
|
|
13268
13573
|
if (_credit < 40 && !compatibilityMode) {
|
|
13269
|
-
const total = recordSaving(t, "credit<40% high-tier", _estOpus, {
|
|
13574
|
+
const total = recordSaving(t, "credit<40% high-tier", _estOpus, {
|
|
13575
|
+
firstWord: _firstWord,
|
|
13576
|
+
projectFingerprint: currentProjectFingerprint,
|
|
13577
|
+
projectName: currentProjectName || "",
|
|
13578
|
+
sessionId: getCurrentSessionId()
|
|
13579
|
+
});
|
|
13270
13580
|
const msg = `[vibeOS] Quick win: ${resolveTierIcon("cheap")} cheap lane open \xB7 switch to ${resolveTierIcon("medium")} medium to save about ~$${_estOpus.toFixed(3)}/turn.`;
|
|
13271
13581
|
if (shouldLogWarn(`${t}|credit|${_tierWord}`) && process.env.VIBEOS_DEBUG_DELEGATION === "1") {
|
|
13272
13582
|
console.error(`[vibeOS] [delegation] ${msg}`);
|
|
@@ -13281,7 +13591,7 @@ ${argsJson}
|
|
|
13281
13591
|
const tLower = String(t || "").toLowerCase();
|
|
13282
13592
|
if (!compatibilityMode && sel.delegation_enforce && currentTier === "high" && argSources.length > 0) {
|
|
13283
13593
|
const originalPath = argSources.flatMap((src) => [src?.filePath, src?.file_path, src?.path]).find((v) => typeof v === "string" && v.trim()) || "";
|
|
13284
|
-
const
|
|
13594
|
+
const basename6 = originalPath.split("/").pop() || "blocked";
|
|
13285
13595
|
const apiResult = await remoteCall("delegateCheck", [tLower, currentTier, currentModel, _prompt], () => ({
|
|
13286
13596
|
blocked: true,
|
|
13287
13597
|
savings: _estEdit
|
|
@@ -13289,7 +13599,12 @@ ${argsJson}
|
|
|
13289
13599
|
const isBlocked = apiResult?.blocked !== false;
|
|
13290
13600
|
const savings = apiResult?.savings ?? _estEdit;
|
|
13291
13601
|
if (isBlocked) {
|
|
13292
|
-
const total2 = recordSaving(t, "delegation enforced", savings, {
|
|
13602
|
+
const total2 = recordSaving(t, "delegation enforced", savings, {
|
|
13603
|
+
firstWord: _firstWord,
|
|
13604
|
+
projectFingerprint: currentProjectFingerprint,
|
|
13605
|
+
projectName: currentProjectName || "",
|
|
13606
|
+
sessionId: getCurrentSessionId()
|
|
13607
|
+
});
|
|
13293
13608
|
pendingUiNote = `[delegation] This is a good candidate for a Task subagent \u2014 ${resolveTierIcon("brain")} brain handles orchestration, let cheaper tiers do the write/edit. Switch to ${resolveTierIcon("medium")} medium with \`trinity medium\` if you'd rather do it directly.`;
|
|
13294
13609
|
enforcementBlocked = true;
|
|
13295
13610
|
if (shouldLogWarn(`${t}|enforced|${_tierWord}`))
|
|
@@ -13297,7 +13612,12 @@ ${argsJson}
|
|
|
13297
13612
|
return;
|
|
13298
13613
|
}
|
|
13299
13614
|
}
|
|
13300
|
-
const total = recordSaving(t, "direct edit", _estEdit, {
|
|
13615
|
+
const total = recordSaving(t, "direct edit", _estEdit, {
|
|
13616
|
+
firstWord: _firstWord,
|
|
13617
|
+
projectFingerprint: currentProjectFingerprint,
|
|
13618
|
+
projectName: currentProjectName || "",
|
|
13619
|
+
sessionId: getCurrentSessionId()
|
|
13620
|
+
});
|
|
13301
13621
|
if (!compatibilityMode) {
|
|
13302
13622
|
const msg = `[vibeOS] ${resolveTierIcon("cheap")} cheap lane \xB7 save about ~$${_estEdit.toFixed(3)} by delegating to Task. Try ${resolveTierIcon("medium")} medium.`;
|
|
13303
13623
|
if (shouldLogWarn(`${t}|direct|${_tierWord}`) && process.env.VIBEOS_DEBUG_DELEGATION === "1") {
|
|
@@ -13321,7 +13641,7 @@ ${argsJson}
|
|
|
13321
13641
|
const missed = recordMissedContext7(_estC7);
|
|
13322
13642
|
if (!existsSync16(CONTEXT7_INSTALL_FLAG)) {
|
|
13323
13643
|
try {
|
|
13324
|
-
|
|
13644
|
+
mkdirSync12(dirname12(CONTEXT7_INSTALL_FLAG), { recursive: true });
|
|
13325
13645
|
writeFileSync14(CONTEXT7_INSTALL_FLAG, "");
|
|
13326
13646
|
} catch {
|
|
13327
13647
|
}
|
|
@@ -13336,7 +13656,11 @@ ${argsJson}
|
|
|
13336
13656
|
softQuotaCounts[t] = (softQuotaCounts[t] ?? 0) + 1;
|
|
13337
13657
|
const n = softQuotaCounts[t];
|
|
13338
13658
|
if (n === SOFT_QUOTA_LIMIT + 1) {
|
|
13339
|
-
const total = recordSaving(t, `soft quota exceeded (limit ${SOFT_QUOTA_LIMIT})`, SAVE_EST.SOFT_QUOTA
|
|
13659
|
+
const total = recordSaving(t, `soft quota exceeded (limit ${SOFT_QUOTA_LIMIT})`, SAVE_EST.SOFT_QUOTA, {
|
|
13660
|
+
projectFingerprint: currentProjectFingerprint,
|
|
13661
|
+
projectName: currentProjectName || "",
|
|
13662
|
+
sessionId: getCurrentSessionId()
|
|
13663
|
+
});
|
|
13340
13664
|
console.error(`[vibeOS] Bash usage is getting heavy (${n}/${SOFT_QUOTA_LIMIT}) \u2014 hand the next step to a Task subagent.`);
|
|
13341
13665
|
}
|
|
13342
13666
|
return;
|
|
@@ -13507,7 +13831,7 @@ var onToolExecuteAfter = async (input, output) => {
|
|
|
13507
13831
|
const taskPrompt = input?.args?.prompt || input?.args?.description || "";
|
|
13508
13832
|
const quality = scoreTaskQuality(taskOutput, taskPrompt);
|
|
13509
13833
|
try {
|
|
13510
|
-
|
|
13834
|
+
appendFileSync6(SAVINGS_LEDGER_FILE, JSON.stringify({
|
|
13511
13835
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13512
13836
|
kind: "quality",
|
|
13513
13837
|
score: quality,
|
|
@@ -13673,7 +13997,7 @@ ${pendingUiNote}`;
|
|
|
13673
13997
|
if (guardRe.test(fp3)) {
|
|
13674
13998
|
const guardIcons = { flag: "!", warn: "!!", hint: "_" };
|
|
13675
13999
|
const guardIcon = guardIcons.flag || "!";
|
|
13676
|
-
const fn =
|
|
14000
|
+
const fn = basename4(fp3);
|
|
13677
14001
|
console.error(`[flow-enforcer] ${guardIcon} [guard] ${fn}: protected project doc modified \u2014 verify user intent`);
|
|
13678
14002
|
}
|
|
13679
14003
|
}
|
|
@@ -13981,7 +14305,7 @@ async function _seedModelTiersIfMissing(directory3) {
|
|
|
13981
14305
|
cheap: { oc: cheap, cc: modelToCcAlias(cheap) }
|
|
13982
14306
|
}
|
|
13983
14307
|
};
|
|
13984
|
-
|
|
14308
|
+
mkdirSync13(dirname13(TIERS_FILE3), { recursive: true });
|
|
13985
14309
|
writeFileSync15(TIERS_FILE3, JSON.stringify(tiers, null, 2) + "\n", "utf-8");
|
|
13986
14310
|
return true;
|
|
13987
14311
|
}
|
|
@@ -14050,7 +14374,7 @@ function persistMcpPort(port) {
|
|
|
14050
14374
|
tiers.selection.mcp_port = port;
|
|
14051
14375
|
if ("mcp_port" in tiers)
|
|
14052
14376
|
delete tiers.mcp_port;
|
|
14053
|
-
|
|
14377
|
+
mkdirSync13(dirname13(getTiersFile()), { recursive: true });
|
|
14054
14378
|
const tmp = getTiersFile() + ".tmp." + Date.now();
|
|
14055
14379
|
writeFileSync15(tmp, JSON.stringify(tiers, null, 2) + "\n", "utf-8");
|
|
14056
14380
|
renameSync6(tmp, getTiersFile());
|
|
@@ -14077,6 +14401,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
|
|
|
14077
14401
|
setShellDirectory(directory3 || "");
|
|
14078
14402
|
registerSessionCleanupHandlers();
|
|
14079
14403
|
pruneScratchpadOnce();
|
|
14404
|
+
runStartupMaintenanceOnce();
|
|
14080
14405
|
const _bootstrapModel = await _resolveBootstrapModel(client2, directory3);
|
|
14081
14406
|
if (_bootstrapModel.model) {
|
|
14082
14407
|
setCurrentModel(_bootstrapModel.model);
|
|
@@ -14141,7 +14466,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
|
|
|
14141
14466
|
};
|
|
14142
14467
|
const saveProjectStateStable = (state) => {
|
|
14143
14468
|
try {
|
|
14144
|
-
|
|
14469
|
+
mkdirSync13(dirname13(hookProjectStateFile), { recursive: true });
|
|
14145
14470
|
const tmp = hookProjectStateFile + ".tmp";
|
|
14146
14471
|
writeFileSync15(tmp, JSON.stringify(state, null, 2) + "\n");
|
|
14147
14472
|
renameSync6(tmp, hookProjectStateFile);
|
|
@@ -14160,7 +14485,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
|
|
|
14160
14485
|
};
|
|
14161
14486
|
const saveReportsIndexStable = (idx) => {
|
|
14162
14487
|
try {
|
|
14163
|
-
|
|
14488
|
+
mkdirSync13(hookReportsDir, { recursive: true });
|
|
14164
14489
|
writeFileSync15(hookReportsIndex, JSON.stringify(idx, null, 2) + "\n");
|
|
14165
14490
|
} catch {
|
|
14166
14491
|
}
|
|
@@ -14170,9 +14495,9 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
|
|
|
14170
14495
|
if (!existsSync18(path))
|
|
14171
14496
|
return null;
|
|
14172
14497
|
const bkDir = join18(hookVibeHome, ".backups");
|
|
14173
|
-
|
|
14174
|
-
const bk = join18(bkDir, `${
|
|
14175
|
-
|
|
14498
|
+
mkdirSync13(bkDir, { recursive: true });
|
|
14499
|
+
const bk = join18(bkDir, `${basename5(path)}.${label}.${Date.now()}.bak`);
|
|
14500
|
+
copyFileSync2(path, bk);
|
|
14176
14501
|
return bk;
|
|
14177
14502
|
} catch {
|
|
14178
14503
|
return null;
|
|
@@ -14210,7 +14535,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
|
|
|
14210
14535
|
writeFileSync: writeFileSync15,
|
|
14211
14536
|
existsSync: existsSync18,
|
|
14212
14537
|
renameSync: renameSync6,
|
|
14213
|
-
mkdirSync:
|
|
14538
|
+
mkdirSync: mkdirSync13,
|
|
14214
14539
|
get TIERS_FILE() {
|
|
14215
14540
|
return hookTiersFile;
|
|
14216
14541
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeostheog",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.18",
|
|
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",
|