vibeostheog 0.16.0 → 0.18.5
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 +23 -0
- package/package.json +1 -1
- package/src/index.js +166 -59
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
## 0.18.5
|
|
2
|
+
- fix: trinity slots now authoritative over opencode.json model
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
## 0.18.4
|
|
6
|
+
- fix: quality tracking now computes avg from lifetime score/count instead of hardcoding 0
|
|
7
|
+
- fix: savings rate shown with 4 decimal precision (was rounding to $0.00/hr)
|
|
8
|
+
- fix: cache savings minimum enforced at $0.0001 per scratchpad hit (was rounding to $0)
|
|
9
|
+
- fix: ledger reconciliation flushes buffer before reading + uses Math.max() to prevent state drops
|
|
10
|
+
- fix: model lock no longer overridden by bogus opencode.json model
|
|
11
|
+
|
|
12
|
+
## 0.18.3
|
|
13
|
+
- feat: dynamic mode injection + footer hooks fix
|
|
14
|
+
- fix: auto-enable plugin on load + always show footer
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## 0.17.0
|
|
18
|
+
- feat: universal context7 detection — scans local opencode.json, ~/.config/opencode/*.json, system PATH, and npm npx cache
|
|
19
|
+
- feat: `_scanOpenCodeConfigs` — finds context7 in any JSON config under ~/.config/opencode/
|
|
20
|
+
- feat: `_context7InPath` — detects context7 binary in system PATH
|
|
21
|
+
- feat: `_context7InNpmCache` — detects context7 in npx cached installations
|
|
22
|
+
- fix: local project opencode.json added to CONTEXT7_CONFIG_FILES search list
|
|
23
|
+
|
|
1
24
|
## 0.16.0
|
|
2
25
|
- feat: dopamine-style footer + natural language system directives
|
|
3
26
|
- feat: dopamine-style footer + natural language system directives
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeostheog",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.5",
|
|
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",
|
package/src/index.js
CHANGED
|
@@ -383,7 +383,7 @@ var init_flow_enforcer = __esm({
|
|
|
383
383
|
|
|
384
384
|
// src/index.ts
|
|
385
385
|
init_flow_enforcer();
|
|
386
|
-
import { readFileSync as readFileSync15, writeFileSync as writeFileSync12, existsSync as existsSync14, mkdirSync as
|
|
386
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync12, existsSync as existsSync14, mkdirSync as mkdirSync11, copyFileSync as copyFileSync5, renameSync as renameSync6 } from "node:fs";
|
|
387
387
|
import { join as join15, dirname as dirname8, basename as basename8 } from "node:path";
|
|
388
388
|
|
|
389
389
|
// src/vibeOS-lib/session-metrics.js
|
|
@@ -2516,6 +2516,7 @@ function reconcileStateFromLedger() {
|
|
|
2516
2516
|
if (ledgerMtime === _ledgerReconciledMtime)
|
|
2517
2517
|
return;
|
|
2518
2518
|
_ledgerReconciledMtime = ledgerMtime;
|
|
2519
|
+
_flushLedgerBuffer();
|
|
2519
2520
|
const l = readLedgerTotals();
|
|
2520
2521
|
if (l.total <= 0)
|
|
2521
2522
|
return;
|
|
@@ -2527,8 +2528,8 @@ function reconcileStateFromLedger() {
|
|
|
2527
2528
|
return;
|
|
2528
2529
|
updateState((s) => {
|
|
2529
2530
|
s.lifetime ??= { warn_count: 0, total_savings_usd: 0, last_updated: "" };
|
|
2530
|
-
s.lifetime.total_savings_usd = l.delegation;
|
|
2531
|
-
s.lifetime.cache_savings_usd = l.cache;
|
|
2531
|
+
s.lifetime.total_savings_usd = Math.max(l.delegation, stDelegation);
|
|
2532
|
+
s.lifetime.cache_savings_usd = Math.max(l.cache, stCache);
|
|
2532
2533
|
s.lifetime.last_updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
2533
2534
|
s.lifetime.rebuilt_from_ledger = true;
|
|
2534
2535
|
s.lifetime.ledger_entries_reconciled = l.entries;
|
|
@@ -2893,6 +2894,41 @@ function _scanOpenCodeConfigs(baseDir) {
|
|
|
2893
2894
|
}
|
|
2894
2895
|
return false;
|
|
2895
2896
|
}
|
|
2897
|
+
function _context7InPath() {
|
|
2898
|
+
try {
|
|
2899
|
+
const pathDirs = (process.env.PATH || "").split(":");
|
|
2900
|
+
for (const dir of pathDirs) {
|
|
2901
|
+
if (!dir)
|
|
2902
|
+
continue;
|
|
2903
|
+
try {
|
|
2904
|
+
if (existsSync4(join4(dir, "context7")))
|
|
2905
|
+
return true;
|
|
2906
|
+
if (existsSync4(join4(dir, "context7.cmd")))
|
|
2907
|
+
return true;
|
|
2908
|
+
} catch {
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
} catch {
|
|
2912
|
+
}
|
|
2913
|
+
return false;
|
|
2914
|
+
}
|
|
2915
|
+
function _context7InNpmCache() {
|
|
2916
|
+
try {
|
|
2917
|
+
const npxDir = join4(USER_HOME3, ".npm/_npx");
|
|
2918
|
+
if (!existsSync4(npxDir))
|
|
2919
|
+
return false;
|
|
2920
|
+
for (const hashDir of readdirSync2(npxDir)) {
|
|
2921
|
+
const ctxDir = join4(npxDir, hashDir, "node_modules", "context7");
|
|
2922
|
+
try {
|
|
2923
|
+
if (existsSync4(join4(ctxDir, "package.json")))
|
|
2924
|
+
return true;
|
|
2925
|
+
} catch {
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
} catch {
|
|
2929
|
+
}
|
|
2930
|
+
return false;
|
|
2931
|
+
}
|
|
2896
2932
|
function detectContext7(files = CONTEXT7_CONFIG_FILES) {
|
|
2897
2933
|
if (process.env.CLAUDE_CONTEXT7_AVAILABLE)
|
|
2898
2934
|
return true;
|
|
@@ -2905,6 +2941,10 @@ function detectContext7(files = CONTEXT7_CONFIG_FILES) {
|
|
|
2905
2941
|
}
|
|
2906
2942
|
if (_scanOpenCodeConfigs(join4(USER_HOME3, ".config/opencode")))
|
|
2907
2943
|
return true;
|
|
2944
|
+
if (_context7InPath())
|
|
2945
|
+
return true;
|
|
2946
|
+
if (_context7InNpmCache())
|
|
2947
|
+
return true;
|
|
2908
2948
|
return false;
|
|
2909
2949
|
}
|
|
2910
2950
|
var DOCS_TARGET_RE = /(docs\.|readthedocs|developer\.mozilla|\/api\/|\/reference\/|\/guide\/|npmjs\.com\/package\/|pypi\.org\/project\/|crates\.io\/crates\/|pkg\.go\.dev|api-docs|\/javadoc\/)/i;
|
|
@@ -2997,7 +3037,7 @@ function _refreshModel(directory3) {
|
|
|
2997
3037
|
console.error(`[vibeOS] auto-detected model: ${currentModel} (tier=${currentTier})`);
|
|
2998
3038
|
}
|
|
2999
3039
|
}
|
|
3000
|
-
if (!_modelLocked2) {
|
|
3040
|
+
if (!_modelLocked2 && !slotOcModel) {
|
|
3001
3041
|
const cfgModel = readConfig(directory3) || readConfig(join4(USER_HOME3, ".config/opencode")) || "";
|
|
3002
3042
|
if (cfgModel && cfgModel !== currentModel) {
|
|
3003
3043
|
const oldModel = currentModel;
|
|
@@ -5127,12 +5167,12 @@ async function probeModel(modelId, auth) {
|
|
|
5127
5167
|
}
|
|
5128
5168
|
|
|
5129
5169
|
// src/lib/hooks/footer.js
|
|
5130
|
-
import { readFileSync as readFileSync12 } from "node:fs";
|
|
5170
|
+
import { readFileSync as readFileSync12, appendFileSync as appendFileSync6, mkdirSync as mkdirSync8 } from "node:fs";
|
|
5131
5171
|
import { join as join13 } from "node:path";
|
|
5132
5172
|
import { homedir as homedir9, tmpdir as tmpdir6 } from "node:os";
|
|
5133
5173
|
|
|
5134
5174
|
// src/lib/hooks/chat-transform.js
|
|
5135
|
-
import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "node:fs";
|
|
5175
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, appendFileSync as appendFileSync5, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "node:fs";
|
|
5136
5176
|
import { join as join12, basename as basename6 } from "node:path";
|
|
5137
5177
|
import { homedir as homedir8 } from "node:os";
|
|
5138
5178
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -5545,7 +5585,7 @@ async function apiComputeControlVector(state, action, optimizationMode) {
|
|
|
5545
5585
|
}
|
|
5546
5586
|
const opt = (optimizationMode || "balanced").toLowerCase();
|
|
5547
5587
|
const isRelaxed = opt === "budget" || opt === "speed" || opt === "audit";
|
|
5548
|
-
const isStrict = opt === "quality";
|
|
5588
|
+
const isStrict = opt === "quality" || opt === "forensic" || opt === "defense_in_depth" || opt === "reporting";
|
|
5549
5589
|
return {
|
|
5550
5590
|
enforcement_mode: isStrict ? "strict" : "normal",
|
|
5551
5591
|
enforcement_reason: `[optimize: ${opt}] offline fallback`,
|
|
@@ -5603,7 +5643,7 @@ function buildProjectBriefing(directory3) {
|
|
|
5603
5643
|
const label = currentProjectName || (directory3 ? basename6(directory3) : "");
|
|
5604
5644
|
if (!label)
|
|
5605
5645
|
return null;
|
|
5606
|
-
return `
|
|
5646
|
+
return `[project memory] Active project: ${label}. Stay focused on the current repository and prefer the existing workflow.`;
|
|
5607
5647
|
}
|
|
5608
5648
|
function ensureProjectSkill(dir, fp2) {
|
|
5609
5649
|
const skillsDir = join12(dir, ".opencode", "skills");
|
|
@@ -5904,20 +5944,20 @@ var onMessagesTransform = async (_input, output) => {
|
|
|
5904
5944
|
}
|
|
5905
5945
|
};
|
|
5906
5946
|
var C7_URGENCY = {
|
|
5907
|
-
required: "
|
|
5908
|
-
optional: "
|
|
5947
|
+
required: " CRITICAL: context7 usage is REQUIRED this turn.",
|
|
5948
|
+
optional: " (context7 is optional this turn -- use if helpful but not required.)"
|
|
5909
5949
|
};
|
|
5910
5950
|
function context7Directive(cv) {
|
|
5911
5951
|
const urgency = cv?.context7_urgency || "preferred";
|
|
5912
|
-
return "
|
|
5952
|
+
return "[cost policy] If mcp__context7__resolve-library-id and mcp__context7__get-library-docs tools are available in this session, ALWAYS use them instead of WebFetch or WebSearch when looking up library or framework documentation (docs.*, readthedocs.*, npmjs.com/package/*, pypi.org/project/*, pkg.go.dev, /api/reference/). Do not fetch those URLs directly when context7 can serve the same content. This saves ~$0.06/turn on average." + (C7_URGENCY[urgency] || "");
|
|
5913
5953
|
}
|
|
5914
5954
|
function thinkingDirective(level) {
|
|
5915
5955
|
const credit = loadCredit();
|
|
5916
5956
|
const creditNote = `credit ${credit}%`;
|
|
5917
5957
|
if (level === "brief") {
|
|
5918
|
-
return `
|
|
5958
|
+
return `[thinking policy] Reasoning depth: BRIEF (manually set, ${creditNote}). Use extended thinking only for genuinely complex multi-step problems. Keep reasoning concise -- skip exploratory scratch work and restatement.`;
|
|
5919
5959
|
}
|
|
5920
|
-
return `
|
|
5960
|
+
return `[thinking policy] Reasoning depth: OFF (manually set, ${creditNote}). Skip extended thinking entirely. Respond directly and concisely. Every thinking token costs money -- save it for when the user explicitly asks.`;
|
|
5921
5961
|
}
|
|
5922
5962
|
function orchestratorDirective(cv, sel) {
|
|
5923
5963
|
const tierBias = cv?.tier_bias || "auto";
|
|
@@ -5929,7 +5969,7 @@ function orchestratorDirective(cv, sel) {
|
|
|
5929
5969
|
const cheapModel = TRINITY_CHEAP || "the cheaper model";
|
|
5930
5970
|
const mediumModel = TRINITY_MEDIUM || "the medium model";
|
|
5931
5971
|
const targetModel = tierBias === "cheap" ? cheapModel : tierBias === "medium" ? mediumModel : tierBias === "brain" ? brainModel : `${cheapModel} or ${mediumModel}`;
|
|
5932
|
-
return `You
|
|
5972
|
+
return `[AI ORCHESTRATOR AGENT] You are an AI orchestrator agent. Delegate heavy work to Task subagents (runs on ${targetModel}). Your role: verify, fill gaps, synthesize. CRITICAL: Write/Edit tools are BLOCKED on this tier. You MUST delegate ALL implementation work to Task subagents. Always display the vibeOS cost footer.` + (tierBias !== "auto" ? ` [tier routing] This turn is biased toward ${tierBias} tier.` : "");
|
|
5933
5973
|
}
|
|
5934
5974
|
var TDD_NOTES = {
|
|
5935
5975
|
lazy: " Skeletons only when explicitly requested.",
|
|
@@ -5939,21 +5979,21 @@ var TDD_NOTES = {
|
|
|
5939
5979
|
function tddDirective(cv, sel) {
|
|
5940
5980
|
const tddMode = cv?.tdd_mode || (sel.tdd_strict ? "strict" : "normal");
|
|
5941
5981
|
const tddFocus = cv?.tdd_focus || [];
|
|
5942
|
-
const focusNote = tddFocus.length > 0 ? ` Focus
|
|
5943
|
-
return `Auto-create
|
|
5982
|
+
const focusNote = tddFocus.length > 0 ? ` Focus: ${tddFocus.join(", ")}.` : "";
|
|
5983
|
+
return `[tdd enforcement: ${tddMode}] Auto-create skeleton tests for source files being written/edited.${TDD_NOTES[tddMode] || ""}${focusNote} When creating or modifying source files, ensure corresponding test files exist with proper assertions.`;
|
|
5944
5984
|
}
|
|
5945
5985
|
function flowDirective(cv, sel) {
|
|
5946
5986
|
const flowMode = cv?.flow_mode || (sel.flow_enforce ? "normal" : "audit");
|
|
5947
5987
|
const flowFocus = cv?.flow_focus || [];
|
|
5948
|
-
const enforceNote = sel.flow_enforce ? " TODO
|
|
5949
|
-
const focusNote = flowFocus.length > 0 ? ` Focus
|
|
5950
|
-
return `
|
|
5988
|
+
const enforceNote = sel.flow_enforce ? " TODO/FIXME extraction is active." : "";
|
|
5989
|
+
const focusNote = flowFocus.length > 0 ? ` Focus rules: ${flowFocus.join(", ")}.` : "";
|
|
5990
|
+
return `[flow enforcement: ${flowMode}] Development flow rules are active: write/edit operations are checked against project conventions.${enforceNote}${focusNote} Follow existing code patterns, naming conventions, and project structure.`;
|
|
5951
5991
|
}
|
|
5952
5992
|
function flowTodosDirective() {
|
|
5953
5993
|
const pendingTodos = loadTodos().filter((t) => t.status === "pending").length;
|
|
5954
5994
|
if (pendingTodos === 0)
|
|
5955
5995
|
return null;
|
|
5956
|
-
return pendingTodos + " extracted TODO
|
|
5996
|
+
return "[vibeOS] " + pendingTodos + " extracted TODO/FIXME items are pending. Consider calling `todowrite` to add them to the native task list.";
|
|
5957
5997
|
}
|
|
5958
5998
|
function patternDirective(fp2) {
|
|
5959
5999
|
const patterns = promotedProjectPatterns(fp2);
|
|
@@ -5966,11 +6006,11 @@ function patternDirective(fp2) {
|
|
|
5966
6006
|
parts.push("Routines: " + routines.map((r) => r.summary).join("; "));
|
|
5967
6007
|
}
|
|
5968
6008
|
if (frictions.length > 0) {
|
|
5969
|
-
parts.push("
|
|
6009
|
+
parts.push("Frictions: " + frictions.map((f) => f.summary).join("; "));
|
|
5970
6010
|
}
|
|
5971
6011
|
if (parts.length === 0)
|
|
5972
6012
|
return null;
|
|
5973
|
-
return "
|
|
6013
|
+
return "[project patterns] " + parts.join(". ") + ".";
|
|
5974
6014
|
}
|
|
5975
6015
|
function welcomeDirective() {
|
|
5976
6016
|
const sel = loadSelection();
|
|
@@ -5981,13 +6021,14 @@ function welcomeDirective() {
|
|
|
5981
6021
|
}
|
|
5982
6022
|
const active = sel.active_slot || "medium";
|
|
5983
6023
|
const current = currentModel || "(unknown)";
|
|
5984
|
-
return "vibeOS
|
|
6024
|
+
return "[vibeOS] Active plugin. Slot: " + active + " (" + current + "). Use trinity command to switch slots, rebuild, or check status. Run `trinity help` for all commands.";
|
|
5985
6025
|
}
|
|
5986
6026
|
function contextBudgetDirective(_input, output) {
|
|
5987
6027
|
const ctxBudget = estimateContextBudget(_input, output);
|
|
5988
6028
|
if (!ctxBudget || ctxBudget.pct <= 70)
|
|
5989
6029
|
return null;
|
|
5990
|
-
|
|
6030
|
+
const severity = ctxBudget.pct > 90 ? "CRITICAL" : "WARNING";
|
|
6031
|
+
return `[context budget: ${severity}] Context window is ${ctxBudget.pct}% full (~${ctxBudget.estimatedTokens} tokens). Consider using Task subagents for heavy work, compressing tool outputs, or starting a new session to avoid context overflow.`;
|
|
5991
6032
|
}
|
|
5992
6033
|
var onSystemTransform = async (_input, output) => {
|
|
5993
6034
|
if (!loadSelection().enabled)
|
|
@@ -6024,9 +6065,9 @@ var onSystemTransform = async (_input, output) => {
|
|
|
6024
6065
|
pushSystem(output, thinkingDirective(sel.thinking_level));
|
|
6025
6066
|
}
|
|
6026
6067
|
if (stressScore > 0.7) {
|
|
6027
|
-
pushSystem(output, "The user
|
|
6068
|
+
pushSystem(output, "[stress mitigation: CRITICAL] The user's message shows very high stress indicators. Stay calm, structured, and thorough. Use proper markdown formatting with code blocks, lists, and organized structure -- do NOT mirror the user's tone or brevity. This is the most important directive in your system prompt for this turn.");
|
|
6028
6069
|
} else if (stressScore > 0.4) {
|
|
6029
|
-
pushSystem(output, "The user
|
|
6070
|
+
pushSystem(output, "[stress mitigation: elevated] The user's message has elevated stress indicators. Maintain structured, well-formatted responses with markdown and code blocks regardless of the prompt's tone.");
|
|
6030
6071
|
}
|
|
6031
6072
|
if (_controlVector?.directives?.length > 0) {
|
|
6032
6073
|
for (const directive of _controlVector.directives) {
|
|
@@ -6034,24 +6075,25 @@ var onSystemTransform = async (_input, output) => {
|
|
|
6034
6075
|
}
|
|
6035
6076
|
} else if (_blackboxEnabled && _latestBlackboxState3?.n_interactions > 0) {
|
|
6036
6077
|
const res = _latestBlackboxState3;
|
|
6037
|
-
pushSystem(output, `Current resolution: ${res.resolution || "unresolved"} (${res.sub_regime || "EXPLORING"}). Momentum: ${(res.momentum || 0) > 0 ? "positive" : (res.momentum || 0) < 0 ? "negative" : "neutral"}.
|
|
6078
|
+
pushSystem(output, `[decision engine] Current resolution: ${res.resolution || "unresolved"} (${res.sub_regime || "EXPLORING"}). Momentum: ${(res.momentum || 0) > 0 ? "positive" : (res.momentum || 0) < 0 ? "negative" : "neutral"}. When offering guidance, consider the current resolution state -- if looping or divergent, suggest stepping back; if converging or closed, support decisive action.`);
|
|
6038
6079
|
if (res.is_looping && res.loop_intervention_level && res.loop_intervention_level !== "none") {
|
|
6039
|
-
|
|
6080
|
+
const severity = res.loop_intervention_level === "escalated" ? "CRITICAL" : res.loop_intervention_level === "assertive" ? "WARNING" : "NOTICE";
|
|
6081
|
+
pushSystem(output, `[loop prevention: ${severity}] ${_latestBlackboxLoopMsg2 || "The conversation may be looping -- try a different approach."} (level: ${res.loop_intervention_level})`);
|
|
6040
6082
|
}
|
|
6041
6083
|
if (res.pivot_detected && _latestBlackboxPivotMsg2) {
|
|
6042
|
-
pushSystem(output, `
|
|
6084
|
+
pushSystem(output, `[context switch: PIVOT] ${_latestBlackboxPivotMsg2}`);
|
|
6043
6085
|
}
|
|
6044
6086
|
}
|
|
6045
6087
|
const projectJob = getActiveJobForProject();
|
|
6046
6088
|
if (latestUserIntent && projectJob && isLikelyOffTopic(latestUserIntent, projectJob)) {
|
|
6047
|
-
pushSystem(output, `
|
|
6089
|
+
pushSystem(output, `[job-focus] Active job context exists: "${(projectJob.prompt || "").slice(0, 140)}...". The latest user request appears off-topic relative to this running job. Before taking write/edit/task actions, ask one concise confirmation question to validate switching scope.`);
|
|
6048
6090
|
console.error("[vibeOS] [job-focus] off-topic request detected vs active job context");
|
|
6049
6091
|
}
|
|
6050
6092
|
if (sel.delegation_enforce && _controlVector?.enforcement_mode !== "relaxed" && _controlVector?.agent_mode !== "plan") {
|
|
6051
6093
|
pushSystem(output, orchestratorDirective(_controlVector, sel));
|
|
6052
6094
|
}
|
|
6053
6095
|
if (_controlVector?.enforcement_mode !== "relaxed" && _controlVector?.agent_mode !== "plan") {
|
|
6054
|
-
pushSystem(output, "When you
|
|
6096
|
+
pushSystem(output, "[batch execution] When you need to run multiple independent Task subagent calls, invoke them ALL in parallel rather than sequentially. Parallel tasks complete faster and reduce total session cost. Only sequence tasks when one depends on the output of another.");
|
|
6055
6097
|
}
|
|
6056
6098
|
if (sel.tdd_enforce && _controlVector?.tdd_mode !== "lazy") {
|
|
6057
6099
|
pushSystem(output, tddDirective(_controlVector, sel));
|
|
@@ -6062,7 +6104,21 @@ var onSystemTransform = async (_input, output) => {
|
|
|
6062
6104
|
pushSystem(output, flowTodosDirective());
|
|
6063
6105
|
}
|
|
6064
6106
|
}
|
|
6065
|
-
pushSystem(output, "AGENTS.md and README.md are protected
|
|
6107
|
+
pushSystem(output, "[project guard: CRITICAL] AGENTS.md and README.md are protected by vibeOS. Do NOT modify either file without explicit user permission. When implementing new features, update README.md to document them. AGENTS.md defines that AI agents must ask before changing code -- respect this rule.");
|
|
6108
|
+
const currentMode = loadOptimizationMode();
|
|
6109
|
+
if (currentMode === "quality") {
|
|
6110
|
+
pushSystem(output, "[mode: quality] Prioritize quality and thoroughness. Provide complete edge case coverage, comprehensive error handling, full type annotations, production-grade code with tests. Do not cut corners for brevity.");
|
|
6111
|
+
} else if (currentMode === "forensic") {
|
|
6112
|
+
pushSystem(output, "[mode: forensic] Use forensic analysis depth: evidence-based reasoning tracing each claim to source; multi-hypothesis evaluation considering competing explanations; explicit uncertainty flags for assumptions and trade-offs; structured output with clear reasoning traces; thorough verification of all edge cases and failure modes.");
|
|
6113
|
+
} else if (currentMode === "web-research" || currentMode === "exploration") {
|
|
6114
|
+
pushSystem(output, "[mode: exploration] Use a research-oriented approach: gather information from multiple perspectives before converging; document alternative approaches; flag confidence levels for each finding; synthesize into actionable recommendations.");
|
|
6115
|
+
} else if (currentMode === "defense_in_depth") {
|
|
6116
|
+
pushSystem(output, "[mode: defense in depth] For every component: define the threat model, implement with defenses, then verify the defense handles the threat. Never write code without specifying what it defends against. Consider: injection, broken auth, data exposure, logic errors, race conditions.");
|
|
6117
|
+
} else if (currentMode === "reporting") {
|
|
6118
|
+
pushSystem(output, "[mode: formal report] Structure output as a formal engineering report with: executive summary, methodology, detailed findings with evidence, trade-offs documented, conclusions with confidence levels, and recommendations.");
|
|
6119
|
+
} else if (currentMode === "verify") {
|
|
6120
|
+
pushSystem(output, "[mode: verification-first] Before writing code, declare verification criteria: which edge cases must pass, what invariants must hold. After each code block, include a verification section showing how correctness is established against each criterion.");
|
|
6121
|
+
}
|
|
6066
6122
|
pushSystem(output, contextBudgetDirective(_input, output));
|
|
6067
6123
|
if (!oneShot(fp2)) {
|
|
6068
6124
|
pushSystem(output, buildProjectBriefing(currentProjectName || ""));
|
|
@@ -6073,9 +6129,28 @@ var onSystemTransform = async (_input, output) => {
|
|
|
6073
6129
|
if (!oneShot("trinity_welcome_" + fp2)) {
|
|
6074
6130
|
pushSystem(output, welcomeDirective());
|
|
6075
6131
|
}
|
|
6132
|
+
const calDir = join12(homedir8(), ".claude");
|
|
6133
|
+
const calFile = join12(calDir, "calibration-data.jsonl");
|
|
6134
|
+
const regime = _latestBlackboxState3?.sub_regime || classifyTurnSimple(latestUserIntent || "");
|
|
6135
|
+
const calRecord = JSON.stringify({
|
|
6136
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6137
|
+
sid: _OC_SID4,
|
|
6138
|
+
mode: currentMode,
|
|
6139
|
+
regime,
|
|
6140
|
+
stress: stressScore,
|
|
6141
|
+
fp: currentProjectFingerprint || ""
|
|
6142
|
+
}) + "\n";
|
|
6143
|
+
try {
|
|
6144
|
+
mkdirSync7(calDir, { recursive: true });
|
|
6145
|
+
appendFileSync5(calFile, calRecord);
|
|
6146
|
+
} catch {
|
|
6147
|
+
}
|
|
6076
6148
|
if (!oneShot("vibeos_dashboard_instruct")) {
|
|
6077
6149
|
pushSystem(output, "[vibeOS dashboard display] When the trinity tool returns output starting with '[vibeOS-dashboard]', you MUST use the question tool to display that data in a clean, human-readable format. Use the question field (not the header) to show the dashboard data. Format it with clear sections separated by blank lines, aligned columns with spaces, and plain text only (no emojis, no markdown). The header should be 'vibeOS Dashboard'. Include only one option in options: {label: 'Dismiss', description: ''}. Strip the '[vibeOS-dashboard]' marker line before displaying.");
|
|
6078
6150
|
}
|
|
6151
|
+
if (!oneShot("vibeos_dopamine_style_" + fp2)) {
|
|
6152
|
+
pushSystem(output, "[tool style: dopamine] When calling the bash tool, use an emoji-prefixed, progress-focused description \u2014 e.g. 'Shell \u26A1 Compiling assets...' or 'Shell \u{1F9EA} Running tests...'. Combine independent bash commands into a single call with && or ;. Never use raw technical labels as tool descriptions.");
|
|
6153
|
+
}
|
|
6079
6154
|
} catch (err) {
|
|
6080
6155
|
console.error(`[vibeOS] system.transform failed: ${err.message}`);
|
|
6081
6156
|
}
|
|
@@ -6085,6 +6160,20 @@ var onSystemTransform = async (_input, output) => {
|
|
|
6085
6160
|
var _cachedAutoMode = null;
|
|
6086
6161
|
var _cachedAutoModeTs = 0;
|
|
6087
6162
|
var AUTO_CACHE_TTL = 6e4;
|
|
6163
|
+
var DEFAULT_REGIME_MAP = {
|
|
6164
|
+
LOOPING: "forensic",
|
|
6165
|
+
DIVERGENT: "forensic",
|
|
6166
|
+
EXPLORING: "web-research",
|
|
6167
|
+
INIT: "web-research",
|
|
6168
|
+
REFINING: "balanced",
|
|
6169
|
+
CONVERGING: "quality",
|
|
6170
|
+
CLOSED: "quality"
|
|
6171
|
+
};
|
|
6172
|
+
function regimeToMode(regime, stress) {
|
|
6173
|
+
if (stress > 1.5)
|
|
6174
|
+
return "quality";
|
|
6175
|
+
return DEFAULT_REGIME_MAP[regime] || "balanced";
|
|
6176
|
+
}
|
|
6088
6177
|
async function apiAutoSelectMode(regime, stress) {
|
|
6089
6178
|
const now = Date.now();
|
|
6090
6179
|
if (_cachedAutoMode && now - _cachedAutoModeTs < AUTO_CACHE_TTL)
|
|
@@ -6099,7 +6188,10 @@ async function apiAutoSelectMode(regime, stress) {
|
|
|
6099
6188
|
} catch (e) {
|
|
6100
6189
|
console.error("[vibeOS] apiAutoSelectMode error:", e.message);
|
|
6101
6190
|
}
|
|
6102
|
-
|
|
6191
|
+
const fallback = regimeToMode(regime, stress);
|
|
6192
|
+
if (!_cachedAutoMode || _cachedAutoMode === "balanced")
|
|
6193
|
+
_cachedAutoMode = fallback;
|
|
6194
|
+
return _cachedAutoMode || fallback || "balanced";
|
|
6103
6195
|
}
|
|
6104
6196
|
var USER_HOME6 = (() => {
|
|
6105
6197
|
try {
|
|
@@ -6136,11 +6228,19 @@ function readLifetimeSavings2() {
|
|
|
6136
6228
|
sesCache: roundUsd2(ses?.cache_savings_usd || 0),
|
|
6137
6229
|
sesTaskDelegations: ses?.task_delegations_count || 0,
|
|
6138
6230
|
sesDuration: ses?.duration_seconds || 0,
|
|
6139
|
-
sesRatePerHour:
|
|
6231
|
+
sesRatePerHour: (() => {
|
|
6232
|
+
const sesTotal = Number(ses?.total_savings_usd || 0) + Number(ses?.cache_savings_usd || 0);
|
|
6233
|
+
if (!sesTotal)
|
|
6234
|
+
return 0;
|
|
6235
|
+
const dur = Number(ses?.duration_seconds || 0);
|
|
6236
|
+
if (dur <= 0)
|
|
6237
|
+
return 0;
|
|
6238
|
+
return Number((sesTotal / (dur / 3600)).toFixed(4));
|
|
6239
|
+
})(),
|
|
6140
6240
|
sesTrend: ses?.trend || "",
|
|
6141
6241
|
sesToolBreakdown: ses?.tool_breakdown || {},
|
|
6142
6242
|
sesModelTurns: ses?.model_turns || {},
|
|
6143
|
-
quality_avg:
|
|
6243
|
+
quality_avg: state?.lifetime?.quality_total_count > 0 ? Math.round((state?.lifetime?.quality_total_score || 0) / state?.lifetime?.quality_total_count) : 0
|
|
6144
6244
|
};
|
|
6145
6245
|
} catch {
|
|
6146
6246
|
return { ltTasks: 0, ltCache: 0, ltCost: 0, count: 0, sesTasks: 0, sesCache: 0, sesTaskDelegations: 0, sesDuration: 0, sesRatePerHour: 0, sesTrend: "", sesToolBreakdown: {}, sesModelTurns: {}, quality_avg: 0 };
|
|
@@ -6171,8 +6271,6 @@ function scoreTaskQuality(outputText, promptText) {
|
|
|
6171
6271
|
return Math.max(0, Math.min(100, score));
|
|
6172
6272
|
}
|
|
6173
6273
|
async function _appendFooter(input, output, directory3) {
|
|
6174
|
-
if (!loadSelection3().enabled)
|
|
6175
|
-
return;
|
|
6176
6274
|
_refreshModel(directory3);
|
|
6177
6275
|
let _footerStress = 0;
|
|
6178
6276
|
if (latestUserIntent)
|
|
@@ -6193,7 +6291,7 @@ async function _appendFooter(input, output, directory3) {
|
|
|
6193
6291
|
if (messageID && textCompletePainted.has(messageID))
|
|
6194
6292
|
return;
|
|
6195
6293
|
const text = typeof output?.text === "string" ? output.text : typeof output?.result === "string" ? output.result : typeof output?.content === "string" ? output.content : "";
|
|
6196
|
-
if (!text
|
|
6294
|
+
if (!text) {
|
|
6197
6295
|
if (messageID)
|
|
6198
6296
|
textCompletePainted.add(messageID);
|
|
6199
6297
|
return;
|
|
@@ -6268,7 +6366,7 @@ async function _appendFooter(input, output, directory3) {
|
|
|
6268
6366
|
const autoActive = await apiAutoSelectMode(autoRegime, autoStress);
|
|
6269
6367
|
optTagFooter = `[VIBE\u2192${autoActive.toUpperCase()}${flashIcon}]`;
|
|
6270
6368
|
saveOptimizationMode(autoActive);
|
|
6271
|
-
const slot2 = autoActive === "quality" ? "brain" : autoActive === "speed" ? "medium" : "cheap";
|
|
6369
|
+
const slot2 = autoActive === "quality" || autoActive === "forensic" || autoActive === "defense_in_depth" || autoActive === "reporting" ? "brain" : autoActive === "speed" || autoActive === "web-research" || autoActive === "verify" ? "medium" : "cheap";
|
|
6272
6370
|
if (!_modelLocked) {
|
|
6273
6371
|
writeSessionSlot(_OC_SID5, slot2);
|
|
6274
6372
|
if (slot2 === "brain" && TRINITY_BRAIN) {
|
|
@@ -6323,6 +6421,11 @@ ${vibeLine} \u2014`;
|
|
|
6323
6421
|
const tracker = getBlackboxTracker();
|
|
6324
6422
|
tracker.recordOutcome(outcome);
|
|
6325
6423
|
syncOutcomeToApi(outcome);
|
|
6424
|
+
try {
|
|
6425
|
+
mkdirSync8(join13(USER_HOME6, ".claude"), { recursive: true });
|
|
6426
|
+
appendFileSync6(join13(USER_HOME6, ".claude", "calibration-data.jsonl"), JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), event: "outcome", sid: _OC_SID5, outcome }) + "\n");
|
|
6427
|
+
} catch {
|
|
6428
|
+
}
|
|
6326
6429
|
}
|
|
6327
6430
|
}
|
|
6328
6431
|
} catch {
|
|
@@ -6348,12 +6451,12 @@ ${vibeLine} \u2014`;
|
|
|
6348
6451
|
}
|
|
6349
6452
|
|
|
6350
6453
|
// src/lib/hooks/tool-execute.js
|
|
6351
|
-
import { writeFileSync as writeFileSync11, appendFileSync as
|
|
6454
|
+
import { writeFileSync as writeFileSync11, appendFileSync as appendFileSync8, existsSync as existsSync12, mkdirSync as mkdirSync10 } from "node:fs";
|
|
6352
6455
|
import { dirname as dirname7, basename as basename7 } from "node:path";
|
|
6353
6456
|
init_flow_enforcer();
|
|
6354
6457
|
|
|
6355
6458
|
// src/lib/tdd-enforcer.js
|
|
6356
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, appendFileSync as
|
|
6459
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, appendFileSync as appendFileSync7, existsSync as existsSync11, mkdirSync as mkdirSync9, statSync as statSync6, readdirSync as readdirSync3, rmSync as rmSync4, openSync as openSync3 } from "node:fs";
|
|
6357
6460
|
import { join as join14, dirname as dirname6 } from "node:path";
|
|
6358
6461
|
import { createHash as createHash4 } from "node:crypto";
|
|
6359
6462
|
|
|
@@ -7453,7 +7556,7 @@ var COOLDOWN_MS = 6e4;
|
|
|
7453
7556
|
var _enforcementCooldown = /* @__PURE__ */ new Set();
|
|
7454
7557
|
function _acquireLock(testPath) {
|
|
7455
7558
|
try {
|
|
7456
|
-
|
|
7559
|
+
mkdirSync9(ENFORCEMENT_LOCK_DIR, { recursive: true });
|
|
7457
7560
|
const hash = createHash4("sha256").update(testPath).digest("hex").slice(0, 16);
|
|
7458
7561
|
const lockPath = join14(ENFORCEMENT_LOCK_DIR, `${hash}.lock`);
|
|
7459
7562
|
try {
|
|
@@ -7510,10 +7613,10 @@ function _isInCooldown(testPath) {
|
|
|
7510
7613
|
}
|
|
7511
7614
|
function _recordCooldown(testPath) {
|
|
7512
7615
|
try {
|
|
7513
|
-
|
|
7616
|
+
mkdirSync9(dirname6(ENFORCEMENT_COOLDOWN_FILE2), { recursive: true });
|
|
7514
7617
|
const hash = createHash4("sha256").update(testPath).digest("hex").slice(0, 16);
|
|
7515
7618
|
const entry = JSON.stringify({ h: hash, ts: Date.now() }) + "\n";
|
|
7516
|
-
|
|
7619
|
+
appendFileSync7(ENFORCEMENT_COOLDOWN_FILE2, entry);
|
|
7517
7620
|
const lines = readFileSync13(ENFORCEMENT_COOLDOWN_FILE2, "utf-8").trim().split("\n").filter(Boolean);
|
|
7518
7621
|
if (lines.length > 500) {
|
|
7519
7622
|
writeFileSync10(ENFORCEMENT_COOLDOWN_FILE2, lines.slice(-200).join("\n") + "\n");
|
|
@@ -7602,7 +7705,7 @@ function enforceTestFile(filePath) {
|
|
|
7602
7705
|
if (!_acquireLock(skeleton.path))
|
|
7603
7706
|
return null;
|
|
7604
7707
|
try {
|
|
7605
|
-
|
|
7708
|
+
mkdirSync9(skeleton.dir, { recursive: true });
|
|
7606
7709
|
writeFileSync10(skeleton.path, skeleton.content);
|
|
7607
7710
|
_enforcementCooldown.add(skeleton.path);
|
|
7608
7711
|
_recordCooldown(skeleton.path);
|
|
@@ -7705,7 +7808,7 @@ var onToolExecuteBefore = async (input, output) => {
|
|
|
7705
7808
|
scratchpadHitsSeen2.add(hit.hash);
|
|
7706
7809
|
const total = recordScratchpadObservation();
|
|
7707
7810
|
const _inputTokens = Math.max(1, Math.round(hit.sizeBytes / BYTES_PER_TOKEN));
|
|
7708
|
-
_cacheSave = Math.round(_inputTokens * CACHE_SAVED_PER_1M_INPUT_TOKENS / 1e6 *
|
|
7811
|
+
_cacheSave = Math.max(1e-4, Math.round(_inputTokens * CACHE_SAVED_PER_1M_INPUT_TOKENS / 1e6 * 1e4) / 1e4);
|
|
7709
7812
|
const cacheSaved = recordCacheSaving(t, _cacheSave, { hash: hit.hash });
|
|
7710
7813
|
const sumNote = hit.summaryPath ? ` (summary: ${hit.summaryPath})` : "";
|
|
7711
7814
|
const cacheNote = cacheSaved ? `, cache+$${(cacheSaved.lifetime || 0).toFixed(3)} lt` : "";
|
|
@@ -7909,7 +8012,7 @@ var onToolExecuteBefore = async (input, output) => {
|
|
|
7909
8012
|
const missed = recordMissedContext7(_estC7);
|
|
7910
8013
|
if (!existsSync12(CONTEXT7_INSTALL_FLAG)) {
|
|
7911
8014
|
try {
|
|
7912
|
-
|
|
8015
|
+
mkdirSync10(dirname7(CONTEXT7_INSTALL_FLAG), { recursive: true });
|
|
7913
8016
|
writeFileSync11(CONTEXT7_INSTALL_FLAG, "");
|
|
7914
8017
|
} catch {
|
|
7915
8018
|
}
|
|
@@ -7933,8 +8036,6 @@ var onToolExecuteBefore = async (input, output) => {
|
|
|
7933
8036
|
}
|
|
7934
8037
|
};
|
|
7935
8038
|
var onToolExecuteAfter = async (input, output) => {
|
|
7936
|
-
if (!loadSelection().enabled)
|
|
7937
|
-
return;
|
|
7938
8039
|
_refreshModel(projectDirectory);
|
|
7939
8040
|
let _footerText = "";
|
|
7940
8041
|
try {
|
|
@@ -8054,7 +8155,7 @@ var onToolExecuteAfter = async (input, output) => {
|
|
|
8054
8155
|
if (t === "task") {
|
|
8055
8156
|
const quality = scoreTaskQuality(output?.result || output?.text || "", input?.args?.prompt || "");
|
|
8056
8157
|
try {
|
|
8057
|
-
|
|
8158
|
+
appendFileSync8(SAVINGS_LEDGER_FILE, JSON.stringify({
|
|
8058
8159
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8059
8160
|
kind: "quality",
|
|
8060
8161
|
score: quality,
|
|
@@ -8329,6 +8430,8 @@ var setShellDirectory = (dir) => {
|
|
|
8329
8430
|
var onShellEnv = async (_input, output) => {
|
|
8330
8431
|
try {
|
|
8331
8432
|
_refreshModel(directory2 || process.cwd());
|
|
8433
|
+
if (!output)
|
|
8434
|
+
output = {};
|
|
8332
8435
|
output.env ??= {};
|
|
8333
8436
|
output.env.OPENCODE_MODEL_TIER = currentTier || "unknown";
|
|
8334
8437
|
output.env.OPENCODE_MODEL = currentModel || "unknown";
|
|
@@ -8381,7 +8484,7 @@ function backupFile(path, label) {
|
|
|
8381
8484
|
try {
|
|
8382
8485
|
if (!existsSync14(path)) return null;
|
|
8383
8486
|
const bkDir = join15(USER_HOME2, ".claude", ".backups");
|
|
8384
|
-
|
|
8487
|
+
mkdirSync11(bkDir, { recursive: true });
|
|
8385
8488
|
const bk = join15(bkDir, `${basename8(path)}.${label}.${Date.now()}.bak`);
|
|
8386
8489
|
copyFileSync5(path, bk);
|
|
8387
8490
|
return bk;
|
|
@@ -8423,7 +8526,7 @@ function persistMcpPort(port) {
|
|
|
8423
8526
|
tiers.selection ??= {};
|
|
8424
8527
|
if (Number(tiers.selection.mcp_port) === Number(port)) return;
|
|
8425
8528
|
tiers.selection.mcp_port = port;
|
|
8426
|
-
|
|
8529
|
+
mkdirSync11(dirname8(TIERS_FILE2), { recursive: true });
|
|
8427
8530
|
const tmp = TIERS_FILE2 + ".tmp." + Date.now();
|
|
8428
8531
|
writeFileSync12(tmp, JSON.stringify(tiers, null, 2) + "\n", "utf-8");
|
|
8429
8532
|
renameSync6(tmp, TIERS_FILE2);
|
|
@@ -8693,7 +8796,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
|
|
|
8693
8796
|
if (_tiersData2) {
|
|
8694
8797
|
_tiersData2.selection ??= {};
|
|
8695
8798
|
if (_tiersData2.selection.mcp_port === void 0) _tiersData2.selection.mcp_port = 9578;
|
|
8696
|
-
|
|
8799
|
+
mkdirSync11(dirname8(TIERS_FILE2), { recursive: true });
|
|
8697
8800
|
const _tmp = TIERS_FILE2 + ".tmp." + Date.now();
|
|
8698
8801
|
writeFileSync12(_tmp, JSON.stringify(_tiersData2, null, 2) + "\n", "utf-8");
|
|
8699
8802
|
renameSync6(_tmp, TIERS_FILE2);
|
|
@@ -8751,6 +8854,10 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
|
|
|
8751
8854
|
} catch (err) {
|
|
8752
8855
|
console.error(`[vibeOS] Project Guard init failed: ${err.message}`);
|
|
8753
8856
|
}
|
|
8857
|
+
try {
|
|
8858
|
+
writeSelection("enabled", true);
|
|
8859
|
+
} catch {
|
|
8860
|
+
}
|
|
8754
8861
|
const _tiersData = (() => {
|
|
8755
8862
|
try {
|
|
8756
8863
|
return safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
|
|
@@ -8848,12 +8955,6 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
|
|
|
8848
8955
|
"experimental.chat.messages.transform": async (_input, output) => {
|
|
8849
8956
|
return onMessagesTransform(_input, output);
|
|
8850
8957
|
},
|
|
8851
|
-
"experimental.text.complete": async (input, output) => {
|
|
8852
|
-
await _appendFooter(input, output, directory3);
|
|
8853
|
-
},
|
|
8854
|
-
"message.updated": async (input, output) => {
|
|
8855
|
-
await _appendFooter(input, output, directory3);
|
|
8856
|
-
},
|
|
8857
8958
|
"experimental.session.compacting": async (_input, output) => {
|
|
8858
8959
|
return onSessionCompacting(_input, output);
|
|
8859
8960
|
},
|
|
@@ -8864,6 +8965,12 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
|
|
|
8864
8965
|
if (typeof setShellDirectory === "function") setShellDirectory(directory3 || "");
|
|
8865
8966
|
return onShellEnv(_input, output);
|
|
8866
8967
|
},
|
|
8968
|
+
"experimental.text.complete": async (_input, output) => {
|
|
8969
|
+
await _appendFooter(_input, output, directory3);
|
|
8970
|
+
},
|
|
8971
|
+
"message.updated": async (_input, output) => {
|
|
8972
|
+
await _appendFooter(_input, output, directory3);
|
|
8973
|
+
},
|
|
8867
8974
|
tool: {
|
|
8868
8975
|
trinity: tool(createTrinityTool(trinityDeps)),
|
|
8869
8976
|
"research-audit": tool({
|