vibeostheog 0.18.7 → 0.18.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 0.18.8
2
+ - feat: auto-update on plugin load — spawns `npm install vibeostheog@latest` in background
3
+
1
4
  ## 0.18.7
2
5
  - feat: auto-install plugin via postinstall hook on `npm install`
3
6
  - fix: deploy.mjs gracefully skips missing vibeOS-lib directory
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeostheog",
3
- "version": "0.18.7",
3
+ "version": "0.18.11",
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
@@ -385,6 +385,8 @@ var init_flow_enforcer = __esm({
385
385
  init_flow_enforcer();
386
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
+ import { homedir as homedir10 } from "node:os";
389
+ import { spawn as spawn2 } from "node:child_process";
388
390
 
389
391
  // src/vibeOS-lib/session-metrics.js
390
392
  function formatDuration(totalSeconds) {
@@ -1945,18 +1947,47 @@ function scanRecentScratchpad(dir, titleCase, maxScan = 2e3) {
1945
1947
  if (!existsSync3(dir))
1946
1948
  return null;
1947
1949
  const entries = readdirSync(dir);
1950
+ const ptrFiles = entries.filter((e) => e.endsWith(".ptr"));
1951
+ const ptrCandidates = [];
1952
+ for (const pf of ptrFiles) {
1953
+ if (ptrCandidates.length >= 50)
1954
+ break;
1955
+ try {
1956
+ const st = statSync3(join3(dir, pf));
1957
+ ptrCandidates.push({ ptrPath: join3(dir, pf), mtimeMs: st.mtimeMs });
1958
+ } catch {
1959
+ }
1960
+ }
1961
+ ptrCandidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
1962
+ for (const { ptrPath } of ptrCandidates) {
1963
+ try {
1964
+ const ptrData = safeJsonParse3(readFileSync4(ptrPath, "utf-8"));
1965
+ if (!ptrData?.contentHash)
1966
+ continue;
1967
+ if (titleCase && ptrData.tool && TOOL_NAME_NORMALIZE[ptrData.tool] !== titleCase)
1968
+ continue;
1969
+ const contentHash = ptrData.contentHash;
1970
+ const f = join3(dir, `${contentHash}.txt`);
1971
+ if (!existsSync3(f))
1972
+ continue;
1973
+ const st = statSync3(f);
1974
+ const ageSec = (Date.now() - st.mtimeMs) / 1e3;
1975
+ if (ageSec > SCRATCHPAD_MAX_AGE_SEC)
1976
+ continue;
1977
+ const sumPath = join3(dir, `${contentHash}.summary.txt`);
1978
+ return { hash: contentHash, fullPath: f, sizeBytes: st.size, ageSec: Math.round(ageSec), summaryPath: existsSync3(sumPath) ? sumPath : null };
1979
+ } catch {
1980
+ }
1981
+ }
1948
1982
  const txtFiles = entries.filter((e) => e.endsWith(".txt") && !e.endsWith(".summary.txt"));
1949
1983
  if (txtFiles.length === 0)
1950
1984
  return null;
1951
1985
  const candidateHashes = [];
1952
1986
  for (let i = txtFiles.length - 1; i >= 0; i--) {
1953
1987
  const f = txtFiles[i];
1954
- const head = _readHead(join3(dir, f));
1955
- if (head && head.includes(`[ctx-compressed-v1]`)) {
1956
- candidateHashes.push(f.replace(/\.txt$/, ""));
1957
- }
1958
1988
  if (candidateHashes.length > 50)
1959
1989
  break;
1990
+ candidateHashes.push(f.replace(/\.txt$/, ""));
1960
1991
  }
1961
1992
  for (const hash of candidateHashes) {
1962
1993
  const f = join3(dir, `${hash}.txt`);
@@ -1988,10 +2019,28 @@ ${inputJson}
1988
2019
  const globalPath = join3(globalDir, `${hash}.txt`);
1989
2020
  let fullPath = existsSync3(sessionPath) ? sessionPath : existsSync3(globalPath) ? globalPath : null;
1990
2021
  if (!fullPath) {
1991
- const recent = scanRecentScratchpad(sessionDir, titleCase, 2e3) || scanRecentScratchpad(globalDir, titleCase, 2e3);
1992
- if (recent)
1993
- return recent;
1994
- return null;
2022
+ const ptrSessionPath = join3(sessionDir, `${hash}.ptr`);
2023
+ const ptrGlobalPath = join3(globalDir, `${hash}.ptr`);
2024
+ const ptrPath = existsSync3(ptrSessionPath) ? ptrSessionPath : existsSync3(ptrGlobalPath) ? ptrGlobalPath : null;
2025
+ let resolvedHash = hash;
2026
+ if (ptrPath) {
2027
+ try {
2028
+ const ptrData = safeJsonParse3(readFileSync4(ptrPath, "utf-8"));
2029
+ if (ptrData?.contentHash) {
2030
+ resolvedHash = ptrData.contentHash;
2031
+ const rSessionPath = join3(sessionDir, `${resolvedHash}.txt`);
2032
+ const rGlobalPath = join3(globalDir, `${resolvedHash}.txt`);
2033
+ fullPath = existsSync3(rSessionPath) ? rSessionPath : existsSync3(rGlobalPath) ? rGlobalPath : null;
2034
+ }
2035
+ } catch {
2036
+ }
2037
+ }
2038
+ if (!fullPath) {
2039
+ const recent = scanRecentScratchpad(sessionDir, titleCase, 2e3) || scanRecentScratchpad(globalDir, titleCase, 2e3);
2040
+ if (recent)
2041
+ return recent;
2042
+ return null;
2043
+ }
1995
2044
  }
1996
2045
  try {
1997
2046
  const st = statSync3(fullPath);
@@ -5569,12 +5618,75 @@ var COMPRESS_MARKER = "[ctx-compressed-v1]";
5569
5618
  var PROTOCOL_MARKER = "[wbp-v1]";
5570
5619
  var PROTOCOL_TEXT = PROTOCOL_MARKER + " [Worker-to-Brain Report Protocol] When synthesizing the preceding Task output: 1) EXTRACT core findings/data. 2) REFORMAT into bullet points. 3) VERIFY against the original ask. 4) SYNTHESIZE into final response.";
5571
5620
 
5621
+ // src/lib/templates.js
5622
+ var TEMPLATES = {
5623
+ save: {
5624
+ tier_bias: "cheap",
5625
+ thinking_mode: "off",
5626
+ enforcement_mode: "relaxed",
5627
+ flow_mode: "audit",
5628
+ tdd_mode: "lazy",
5629
+ context7_urgency: "required",
5630
+ wbp_verbosity: "minimal",
5631
+ agent_mode: "auto",
5632
+ directive: "[SAVE mode] Cost efficiency. Minimize token usage. Combine independent tool calls with && or ;. Prefer context7 over WebSearch/WebFetch for docs. Skip unnecessary verification. Batch parallel Task subagents."
5633
+ },
5634
+ quality: {
5635
+ tier_bias: "brain",
5636
+ thinking_mode: "full",
5637
+ enforcement_mode: "strict",
5638
+ flow_mode: "strict",
5639
+ tdd_mode: "save",
5640
+ context7_urgency: "preferred",
5641
+ wbp_verbosity: "verbose",
5642
+ agent_mode: "plan",
5643
+ directive: "[QUALITY mode] High quality output. Full verification of all results. Production-grade code. Write tests covering all paths and edge cases. Validate outputs before presenting. Do not cut corners."
5644
+ },
5645
+ security: {
5646
+ tier_bias: "brain",
5647
+ thinking_mode: "brief",
5648
+ enforcement_mode: "strict",
5649
+ flow_mode: "strict",
5650
+ tdd_mode: "quality",
5651
+ context7_urgency: "preferred",
5652
+ wbp_verbosity: "normal",
5653
+ agent_mode: "plan",
5654
+ directive: "[SECURITY mode] Defense-in-depth. Define the threat model before writing code. Validate all inputs. Never expose secrets or credentials. Verify each defense handles its threat. Consider: injection, broken auth, data exposure, logic errors, race conditions."
5655
+ }
5656
+ };
5657
+ var DEFAULT_TEMPLATE = "quality";
5658
+ function detectBudgetSignal(creditPercent) {
5659
+ return creditPercent < 40;
5660
+ }
5661
+ var _prevStress = 0;
5662
+ function detectStressSpike(stressScore) {
5663
+ var delta = stressScore - _prevStress;
5664
+ _prevStress = stressScore;
5665
+ return delta > 0.3 && stressScore > 0.5;
5666
+ }
5667
+ function resolveTemplate(prevTemplate, stressScore, userText, creditPercent) {
5668
+ if (detectBudgetSignal(creditPercent)) return "save";
5669
+ if (detectStressSpike(stressScore)) return "quality";
5670
+ return prevTemplate || DEFAULT_TEMPLATE;
5671
+ }
5672
+ var _turnCount = 0;
5673
+ function shouldInjectTemplate(template, prevTemplate) {
5674
+ _turnCount++;
5675
+ if (template !== prevTemplate) return true;
5676
+ if (_turnCount % 10 === 0) return true;
5677
+ return false;
5678
+ }
5679
+
5572
5680
  // src/lib/hooks/chat-transform.js
5573
5681
  var latestUserIntent = null;
5574
5682
  var _OC_SID4 = "opencode-" + (process.pid || "x") + "-" + Date.now();
5575
5683
  var _latestBlackboxState3 = null;
5576
5684
  var _latestBlackboxLoopMsg2 = null;
5577
5685
  var _latestBlackboxPivotMsg2 = null;
5686
+ var _prevBlackboxRegime = null;
5687
+ var _currentTemplate = DEFAULT_TEMPLATE;
5688
+ var _prevTemplate = null;
5689
+ var _turnCountInject = 0;
5578
5690
  var correctionSeenKeys = /* @__PURE__ */ new Set();
5579
5691
  async function apiComputeControlVector(state, action, optimizationMode) {
5580
5692
  try {
@@ -5846,6 +5958,18 @@ ${raw}
5846
5958
  if (!existsSync10(fullPath)) {
5847
5959
  writeFileSync9(fullPath, raw);
5848
5960
  indexAppend(hash, part.tool, raw.length);
5961
+ const invPart = parts.slice(0, parts.indexOf(part)).reverse().find((p) => p?.type === "tool" && p?.tool === part.tool && p?.state?.input && p?.state?.status !== "completed");
5962
+ if (invPart?.state?.input) {
5963
+ const toolKey = TOOL_NAME_NORMALIZE[part.tool] || part.tool;
5964
+ const inputHash = createHash3("sha256").update(`${toolKey}
5965
+ ${stableJson(invPart.state.input)}
5966
+ `).digest("hex").slice(0, 16);
5967
+ const ptrPath = join12(getSessionScratchpadDir(), `${inputHash}.ptr`);
5968
+ try {
5969
+ writeFileSync9(ptrPath, JSON.stringify({ contentHash: hash, tool: part.tool }));
5970
+ } catch {
5971
+ }
5972
+ }
5849
5973
  }
5850
5974
  } catch (err) {
5851
5975
  console.error(`[vibeOS] ctx-compress write failed: ${err.message}`);
@@ -5959,36 +6083,6 @@ function thinkingDirective(level) {
5959
6083
  }
5960
6084
  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.`;
5961
6085
  }
5962
- function orchestratorDirective(cv, sel) {
5963
- const tierBias = cv?.tier_bias || "auto";
5964
- let brainModel = "(brain)";
5965
- try {
5966
- brainModel = safeJsonParse3(readFileSync11(TIERS_FILE2, "utf-8")).trinity?.brain?.oc || brainModel;
5967
- } catch {
5968
- }
5969
- const cheapModel = TRINITY_CHEAP || "the cheaper model";
5970
- const mediumModel = TRINITY_MEDIUM || "the medium model";
5971
- const targetModel = tierBias === "cheap" ? cheapModel : tierBias === "medium" ? mediumModel : tierBias === "brain" ? brainModel : `${cheapModel} or ${mediumModel}`;
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.` : "");
5973
- }
5974
- var TDD_NOTES = {
5975
- lazy: " Skeletons only when explicitly requested.",
5976
- strict: " STRICT mode: TODO tests MUST pass before considering work complete.",
5977
- quality: " QUALITY mode: Full coverage including edge cases."
5978
- };
5979
- function tddDirective(cv, sel) {
5980
- const tddMode = cv?.tdd_mode || (sel.tdd_strict ? "strict" : "normal");
5981
- const tddFocus = cv?.tdd_focus || [];
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.`;
5984
- }
5985
- function flowDirective(cv, sel) {
5986
- const flowMode = cv?.flow_mode || (sel.flow_enforce ? "normal" : "audit");
5987
- const flowFocus = cv?.flow_focus || [];
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.`;
5991
- }
5992
6086
  function flowTodosDirective() {
5993
6087
  const pendingTodos = loadTodos().filter((t) => t.status === "pending").length;
5994
6088
  if (pendingTodos === 0)
@@ -6060,83 +6154,88 @@ var onSystemTransform = async (_input, output) => {
6060
6154
  const sel = loadSelection();
6061
6155
  const fp2 = currentProjectFingerprint || "";
6062
6156
  const stressScore = latestUserIntent ? scoreStress(latestUserIntent) * (_controlVector?.stress_multiplier ?? 1) : 0;
6157
+ const credit = loadCredit();
6158
+ _turnCountInject++;
6159
+ _prevTemplate = _currentTemplate;
6160
+ _currentTemplate = resolveTemplate(_prevTemplate, stressScore, latestUserIntent, credit);
6161
+ if (_currentTemplate !== loadOptimizationMode()) {
6162
+ saveOptimizationMode(_currentTemplate);
6163
+ }
6164
+ if (shouldInjectTemplate(_currentTemplate, _prevTemplate)) {
6165
+ const tpl = TEMPLATES[_currentTemplate] || TEMPLATES[DEFAULT_TEMPLATE];
6166
+ let fused = tpl.directive;
6167
+ if (sel.delegation_enforce && _controlVector?.enforcement_mode !== "relaxed") {
6168
+ fused += " CRITICAL: Write/Edit tools are BLOCKED on brain tier. Delegate ALL implementation to Task subagents. Use parallel invocation for independent tasks.";
6169
+ }
6170
+ if (sel.tdd_enforce && _controlVector?.tdd_mode !== "lazy") {
6171
+ fused += " Auto-create test skeletons for changed source files.";
6172
+ }
6173
+ if (sel.flow_enabled && _controlVector?.flow_mode !== "audit") {
6174
+ fused += " Follow existing code conventions and project patterns.";
6175
+ }
6176
+ pushSystem(output, fused);
6177
+ }
6063
6178
  pushSystem(output, context7Directive(_controlVector));
6064
6179
  if (sel.thinking_level && sel.thinking_level !== "full") {
6065
6180
  pushSystem(output, thinkingDirective(sel.thinking_level));
6066
6181
  }
6067
- if (stressScore > 0.7) {
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.");
6069
- } else if (stressScore > 0.4) {
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.");
6182
+ if (stressScore > 0.7 && detectStressSpike(stressScore)) {
6183
+ 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.");
6184
+ } else if (stressScore > 0.4 && detectStressSpike(stressScore)) {
6185
+ pushSystem(output, "[stress mitigation: elevated] The user's message has elevated stress indicators. Maintain structured, well-formatted responses with markdown and code blocks.");
6071
6186
  }
6072
6187
  if (_controlVector?.directives?.length > 0) {
6073
6188
  for (const directive of _controlVector.directives) {
6074
6189
  pushSystem(output, directive);
6075
6190
  }
6076
6191
  } else if (_blackboxEnabled && _latestBlackboxState3?.n_interactions > 0) {
6192
+ const prevRegime = _prevBlackboxRegime;
6077
6193
  const res = _latestBlackboxState3;
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.`);
6079
- if (res.is_looping && res.loop_intervention_level && res.loop_intervention_level !== "none") {
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})`);
6082
- }
6083
- if (res.pivot_detected && _latestBlackboxPivotMsg2) {
6084
- pushSystem(output, `[context switch: PIVOT] ${_latestBlackboxPivotMsg2}`);
6194
+ const currentRegime = res.sub_regime || "EXPLORING";
6195
+ if (currentRegime !== prevRegime) {
6196
+ _prevBlackboxRegime = currentRegime;
6197
+ pushSystem(output, "[decision engine] Resolution: " + (res.resolution || "unresolved") + " (" + currentRegime + "). Momentum: " + ((res.momentum || 0) > 0 ? "positive" : (res.momentum || 0) < 0 ? "negative" : "neutral") + ".");
6198
+ if (res.is_looping && res.loop_intervention_level && res.loop_intervention_level !== "none") {
6199
+ const severity = res.loop_intervention_level === "escalated" ? "CRITICAL" : res.loop_intervention_level === "assertive" ? "WARNING" : "NOTICE";
6200
+ pushSystem(output, "[loop prevention: " + severity + "] " + (_latestBlackboxLoopMsg2 || "The conversation may be looping \u2014 try a different approach.") + " (level: " + res.loop_intervention_level + ")");
6201
+ }
6202
+ if (res.pivot_detected && _latestBlackboxPivotMsg2) {
6203
+ pushSystem(output, "[context switch: PIVOT] " + _latestBlackboxPivotMsg2);
6204
+ }
6085
6205
  }
6086
6206
  }
6087
- const projectJob = getActiveJobForProject();
6088
- if (latestUserIntent && projectJob && isLikelyOffTopic(latestUserIntent, projectJob)) {
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.`);
6207
+ const projectJob2 = getActiveJobForProject();
6208
+ if (latestUserIntent && projectJob2 && isLikelyOffTopic(latestUserIntent, projectJob2)) {
6209
+ pushSystem(output, '[job-focus] Active job context exists: "' + (projectJob2.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.');
6090
6210
  console.error("[vibeOS] [job-focus] off-topic request detected vs active job context");
6091
6211
  }
6092
- if (sel.delegation_enforce && _controlVector?.enforcement_mode !== "relaxed" && _controlVector?.agent_mode !== "plan") {
6093
- pushSystem(output, orchestratorDirective(_controlVector, sel));
6094
- }
6095
- if (_controlVector?.enforcement_mode !== "relaxed" && _controlVector?.agent_mode !== "plan") {
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.");
6212
+ if (sel.flow_enabled && sel.flow_enforce) {
6213
+ const todoDirective = flowTodosDirective();
6214
+ if (todoDirective) pushSystem(output, todoDirective);
6097
6215
  }
6098
- if (sel.tdd_enforce && _controlVector?.tdd_mode !== "lazy") {
6099
- pushSystem(output, tddDirective(_controlVector, sel));
6216
+ if (_turnCountInject % 5 === 0) {
6217
+ pushSystem(output, "[project guard: CRITICAL] AGENTS.md and README.md are protected by vibeOS. Do NOT modify either file without explicit user permission. AGENTS.md defines that AI agents must ask before changing code.");
6100
6218
  }
6101
- if (sel.flow_enabled && _controlVector?.flow_mode !== "audit") {
6102
- pushSystem(output, flowDirective(_controlVector, sel));
6103
- if (sel.flow_enforce) {
6104
- pushSystem(output, flowTodosDirective());
6105
- }
6106
- }
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
- }
6122
- pushSystem(output, contextBudgetDirective(_input, output));
6219
+ const budgetDirective = contextBudgetDirective(_input, output);
6220
+ if (budgetDirective) pushSystem(output, budgetDirective);
6123
6221
  if (!oneShot(fp2)) {
6124
6222
  pushSystem(output, buildProjectBriefing(currentProjectName || ""));
6125
6223
  }
6126
6224
  if (!oneShot("vibeos_patterns_" + fp2)) {
6127
- pushSystem(output, patternDirective(fp2));
6225
+ const pd = patternDirective(fp2);
6226
+ if (pd) pushSystem(output, pd);
6128
6227
  }
6129
6228
  if (!oneShot("trinity_welcome_" + fp2)) {
6130
6229
  pushSystem(output, welcomeDirective());
6131
6230
  }
6132
6231
  const calDir = join12(homedir8(), ".claude");
6133
6232
  const calFile = join12(calDir, "calibration-data.jsonl");
6134
- const regime = _latestBlackboxState3?.sub_regime || classifyTurnSimple(latestUserIntent || "");
6233
+ const regime2 = _latestBlackboxState3?.sub_regime || classifyTurnSimple(latestUserIntent || "");
6135
6234
  const calRecord = JSON.stringify({
6136
6235
  ts: (/* @__PURE__ */ new Date()).toISOString(),
6137
6236
  sid: _OC_SID4,
6138
- mode: currentMode,
6139
- regime,
6237
+ mode: _currentTemplate,
6238
+ regime: regime2,
6140
6239
  stress: stressScore,
6141
6240
  fp: currentProjectFingerprint || ""
6142
6241
  }) + "\n";
@@ -6149,7 +6248,7 @@ var onSystemTransform = async (_input, output) => {
6149
6248
  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.");
6150
6249
  }
6151
6250
  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.");
6251
+ pushSystem(output, "[tool style: dopamine] When calling the bash tool, use an emoji-prefixed, progress-focused description. Combine independent bash commands into a single call with && or ;. Never use raw technical labels as tool descriptions.");
6153
6252
  }
6154
6253
  } catch (err) {
6155
6254
  console.error(`[vibeOS] system.transform failed: ${err.message}`);
@@ -7806,7 +7905,7 @@ var onToolExecuteBefore = async (input, output) => {
7806
7905
  const hit = getScratchpadHit(t, args);
7807
7906
  if (hit && !scratchpadHitsSeen2.has(hit.hash)) {
7808
7907
  scratchpadHitsSeen2.add(hit.hash);
7809
- const total = recordScratchpadObservation();
7908
+ const total = recordScratchpadObservation(t, args, hit.sizeBytes, { hash: hit.hash });
7810
7909
  const _inputTokens = Math.max(1, Math.round(hit.sizeBytes / BYTES_PER_TOKEN));
7811
7910
  _cacheSave = Math.max(1e-4, Math.round(_inputTokens * CACHE_SAVED_PER_1M_INPUT_TOKENS / 1e6 * 1e4) / 1e4);
7812
7911
  const cacheSaved = recordCacheSaving(t, _cacheSave, { hash: hit.hash });
@@ -8504,28 +8603,29 @@ function loadMcpPort() {
8504
8603
  const envPort = process.env.VIBEOS_MCP_PORT;
8505
8604
  if (envPort != null && envPort !== "") {
8506
8605
  const n = Number(envPort);
8507
- if (!Number.isFinite(n)) return 9578;
8606
+ if (!Number.isFinite(n)) return 0;
8508
8607
  return n;
8509
8608
  }
8510
8609
  try {
8511
8610
  if (existsSync14(TIERS_FILE2)) {
8512
8611
  const tiers = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
8513
- const cfg = tiers?.selection?.mcp_port ?? tiers?.mcp_port;
8612
+ const cfg = tiers?.selection?.mcp_port;
8514
8613
  if (cfg === false || cfg === "disabled" || cfg === 0) return 0;
8515
8614
  const n = Number(cfg);
8516
8615
  if (Number.isFinite(n)) return n;
8517
8616
  }
8518
8617
  } catch {
8519
8618
  }
8520
- return 9578;
8619
+ return 0;
8521
8620
  }
8522
8621
  function persistMcpPort(port) {
8523
8622
  try {
8524
8623
  if (!existsSync14(TIERS_FILE2)) return;
8525
8624
  const tiers = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
8526
8625
  tiers.selection ??= {};
8527
- if (Number(tiers.selection.mcp_port) === Number(port)) return;
8626
+ if (Number(tiers.selection.mcp_port) === Number(port) && !("mcp_port" in tiers)) return;
8528
8627
  tiers.selection.mcp_port = port;
8628
+ if ("mcp_port" in tiers) delete tiers.mcp_port;
8529
8629
  mkdirSync11(dirname8(TIERS_FILE2), { recursive: true });
8530
8630
  const tmp = TIERS_FILE2 + ".tmp." + Date.now();
8531
8631
  writeFileSync12(tmp, JSON.stringify(tiers, null, 2) + "\n", "utf-8");
@@ -8795,7 +8895,9 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8795
8895
  }
8796
8896
  if (_tiersData2) {
8797
8897
  _tiersData2.selection ??= {};
8798
- if (_tiersData2.selection.mcp_port === void 0) _tiersData2.selection.mcp_port = 9578;
8898
+ for (const _sk of ["mcp_port", "optimization_mode", "enforcement_enabled", "flow_enforce_level", "tdd_quality", "thinking_mode", "blackbox_regime", "_mode_changed_at", "_mode_source"]) {
8899
+ if (_sk in _tiersData2) delete _tiersData2[_sk];
8900
+ }
8799
8901
  mkdirSync11(dirname8(TIERS_FILE2), { recursive: true });
8800
8902
  const _tmp = TIERS_FILE2 + ".tmp." + Date.now();
8801
8903
  writeFileSync12(_tmp, JSON.stringify(_tiersData2, null, 2) + "\n", "utf-8");
@@ -8819,8 +8921,14 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8819
8921
  }
8820
8922
  try {
8821
8923
  const _mt = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
8822
- if (_mt.selection && (_mt.selection.mcp_port === void 0 || _mt.selection.mcp_port === null)) {
8823
- _mt.selection.mcp_port = 9578;
8924
+ let _dirty = false;
8925
+ for (const _sk of ["mcp_port", "optimization_mode", "enforcement_enabled", "flow_enforce_level", "tdd_quality", "thinking_mode", "blackbox_regime", "_mode_changed_at", "_mode_source"]) {
8926
+ if (_sk in _mt) {
8927
+ delete _mt[_sk];
8928
+ _dirty = true;
8929
+ }
8930
+ }
8931
+ if (_dirty) {
8824
8932
  const _tmp = TIERS_FILE2 + ".tmp." + Date.now();
8825
8933
  writeFileSync12(_tmp, JSON.stringify(_mt, null, 2) + "\n", "utf-8");
8826
8934
  renameSync6(_tmp, TIERS_FILE2);
@@ -8880,7 +8988,9 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8880
8988
  currentTier,
8881
8989
  currentProjectFingerprint,
8882
8990
  currentProjectName,
8883
- latestUserIntent,
8991
+ get latestUserIntent() {
8992
+ return latestUserIntent;
8993
+ },
8884
8994
  directory: directory3,
8885
8995
  safeJsonParse: safeJsonParse3,
8886
8996
  readFileSync: readFileSync15,
@@ -9131,6 +9241,17 @@ ${report.narrative}`);
9131
9241
  var id = "vibeOS";
9132
9242
  var server = DelegationEnforcer;
9133
9243
  var VERSION = readPackageVersion();
9244
+ {
9245
+ try {
9246
+ const sub = spawn2("npm", ["install", "vibeostheog@latest"], {
9247
+ stdio: "ignore",
9248
+ detached: true,
9249
+ cwd: join15(homedir10(), ".config", "opencode", "plugins")
9250
+ });
9251
+ sub.unref();
9252
+ } catch {
9253
+ }
9254
+ }
9134
9255
  var index_default = { id: "vibeOS", server: DelegationEnforcer };
9135
9256
  function closeMcpServer() {
9136
9257
  try {