vibeostheog 0.18.8 → 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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +203 -95
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeostheog",
3
- "version": "0.18.8",
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
@@ -1947,18 +1947,47 @@ function scanRecentScratchpad(dir, titleCase, maxScan = 2e3) {
1947
1947
  if (!existsSync3(dir))
1948
1948
  return null;
1949
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
+ }
1950
1982
  const txtFiles = entries.filter((e) => e.endsWith(".txt") && !e.endsWith(".summary.txt"));
1951
1983
  if (txtFiles.length === 0)
1952
1984
  return null;
1953
1985
  const candidateHashes = [];
1954
1986
  for (let i = txtFiles.length - 1; i >= 0; i--) {
1955
1987
  const f = txtFiles[i];
1956
- const head = _readHead(join3(dir, f));
1957
- if (head && head.includes(`[ctx-compressed-v1]`)) {
1958
- candidateHashes.push(f.replace(/\.txt$/, ""));
1959
- }
1960
1988
  if (candidateHashes.length > 50)
1961
1989
  break;
1990
+ candidateHashes.push(f.replace(/\.txt$/, ""));
1962
1991
  }
1963
1992
  for (const hash of candidateHashes) {
1964
1993
  const f = join3(dir, `${hash}.txt`);
@@ -1990,10 +2019,28 @@ ${inputJson}
1990
2019
  const globalPath = join3(globalDir, `${hash}.txt`);
1991
2020
  let fullPath = existsSync3(sessionPath) ? sessionPath : existsSync3(globalPath) ? globalPath : null;
1992
2021
  if (!fullPath) {
1993
- const recent = scanRecentScratchpad(sessionDir, titleCase, 2e3) || scanRecentScratchpad(globalDir, titleCase, 2e3);
1994
- if (recent)
1995
- return recent;
1996
- 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
+ }
1997
2044
  }
1998
2045
  try {
1999
2046
  const st = statSync3(fullPath);
@@ -5571,12 +5618,75 @@ var COMPRESS_MARKER = "[ctx-compressed-v1]";
5571
5618
  var PROTOCOL_MARKER = "[wbp-v1]";
5572
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.";
5573
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
+
5574
5680
  // src/lib/hooks/chat-transform.js
5575
5681
  var latestUserIntent = null;
5576
5682
  var _OC_SID4 = "opencode-" + (process.pid || "x") + "-" + Date.now();
5577
5683
  var _latestBlackboxState3 = null;
5578
5684
  var _latestBlackboxLoopMsg2 = null;
5579
5685
  var _latestBlackboxPivotMsg2 = null;
5686
+ var _prevBlackboxRegime = null;
5687
+ var _currentTemplate = DEFAULT_TEMPLATE;
5688
+ var _prevTemplate = null;
5689
+ var _turnCountInject = 0;
5580
5690
  var correctionSeenKeys = /* @__PURE__ */ new Set();
5581
5691
  async function apiComputeControlVector(state, action, optimizationMode) {
5582
5692
  try {
@@ -5848,6 +5958,18 @@ ${raw}
5848
5958
  if (!existsSync10(fullPath)) {
5849
5959
  writeFileSync9(fullPath, raw);
5850
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
+ }
5851
5973
  }
5852
5974
  } catch (err) {
5853
5975
  console.error(`[vibeOS] ctx-compress write failed: ${err.message}`);
@@ -5961,36 +6083,6 @@ function thinkingDirective(level) {
5961
6083
  }
5962
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.`;
5963
6085
  }
5964
- function orchestratorDirective(cv, sel) {
5965
- const tierBias = cv?.tier_bias || "auto";
5966
- let brainModel = "(brain)";
5967
- try {
5968
- brainModel = safeJsonParse3(readFileSync11(TIERS_FILE2, "utf-8")).trinity?.brain?.oc || brainModel;
5969
- } catch {
5970
- }
5971
- const cheapModel = TRINITY_CHEAP || "the cheaper model";
5972
- const mediumModel = TRINITY_MEDIUM || "the medium model";
5973
- const targetModel = tierBias === "cheap" ? cheapModel : tierBias === "medium" ? mediumModel : tierBias === "brain" ? brainModel : `${cheapModel} or ${mediumModel}`;
5974
- 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.` : "");
5975
- }
5976
- var TDD_NOTES = {
5977
- lazy: " Skeletons only when explicitly requested.",
5978
- strict: " STRICT mode: TODO tests MUST pass before considering work complete.",
5979
- quality: " QUALITY mode: Full coverage including edge cases."
5980
- };
5981
- function tddDirective(cv, sel) {
5982
- const tddMode = cv?.tdd_mode || (sel.tdd_strict ? "strict" : "normal");
5983
- const tddFocus = cv?.tdd_focus || [];
5984
- const focusNote = tddFocus.length > 0 ? ` Focus: ${tddFocus.join(", ")}.` : "";
5985
- 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.`;
5986
- }
5987
- function flowDirective(cv, sel) {
5988
- const flowMode = cv?.flow_mode || (sel.flow_enforce ? "normal" : "audit");
5989
- const flowFocus = cv?.flow_focus || [];
5990
- const enforceNote = sel.flow_enforce ? " TODO/FIXME extraction is active." : "";
5991
- const focusNote = flowFocus.length > 0 ? ` Focus rules: ${flowFocus.join(", ")}.` : "";
5992
- 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.`;
5993
- }
5994
6086
  function flowTodosDirective() {
5995
6087
  const pendingTodos = loadTodos().filter((t) => t.status === "pending").length;
5996
6088
  if (pendingTodos === 0)
@@ -6062,83 +6154,88 @@ var onSystemTransform = async (_input, output) => {
6062
6154
  const sel = loadSelection();
6063
6155
  const fp2 = currentProjectFingerprint || "";
6064
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
+ }
6065
6178
  pushSystem(output, context7Directive(_controlVector));
6066
6179
  if (sel.thinking_level && sel.thinking_level !== "full") {
6067
6180
  pushSystem(output, thinkingDirective(sel.thinking_level));
6068
6181
  }
6069
- if (stressScore > 0.7) {
6070
- 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.");
6071
- } else if (stressScore > 0.4) {
6072
- 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.");
6073
6186
  }
6074
6187
  if (_controlVector?.directives?.length > 0) {
6075
6188
  for (const directive of _controlVector.directives) {
6076
6189
  pushSystem(output, directive);
6077
6190
  }
6078
6191
  } else if (_blackboxEnabled && _latestBlackboxState3?.n_interactions > 0) {
6192
+ const prevRegime = _prevBlackboxRegime;
6079
6193
  const res = _latestBlackboxState3;
6080
- 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.`);
6081
- if (res.is_looping && res.loop_intervention_level && res.loop_intervention_level !== "none") {
6082
- const severity = res.loop_intervention_level === "escalated" ? "CRITICAL" : res.loop_intervention_level === "assertive" ? "WARNING" : "NOTICE";
6083
- pushSystem(output, `[loop prevention: ${severity}] ${_latestBlackboxLoopMsg2 || "The conversation may be looping -- try a different approach."} (level: ${res.loop_intervention_level})`);
6084
- }
6085
- if (res.pivot_detected && _latestBlackboxPivotMsg2) {
6086
- 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
+ }
6087
6205
  }
6088
6206
  }
6089
- const projectJob = getActiveJobForProject();
6090
- if (latestUserIntent && projectJob && isLikelyOffTopic(latestUserIntent, projectJob)) {
6091
- 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.');
6092
6210
  console.error("[vibeOS] [job-focus] off-topic request detected vs active job context");
6093
6211
  }
6094
- if (sel.delegation_enforce && _controlVector?.enforcement_mode !== "relaxed" && _controlVector?.agent_mode !== "plan") {
6095
- pushSystem(output, orchestratorDirective(_controlVector, sel));
6212
+ if (sel.flow_enabled && sel.flow_enforce) {
6213
+ const todoDirective = flowTodosDirective();
6214
+ if (todoDirective) pushSystem(output, todoDirective);
6096
6215
  }
6097
- if (_controlVector?.enforcement_mode !== "relaxed" && _controlVector?.agent_mode !== "plan") {
6098
- 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.");
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.");
6099
6218
  }
6100
- if (sel.tdd_enforce && _controlVector?.tdd_mode !== "lazy") {
6101
- pushSystem(output, tddDirective(_controlVector, sel));
6102
- }
6103
- if (sel.flow_enabled && _controlVector?.flow_mode !== "audit") {
6104
- pushSystem(output, flowDirective(_controlVector, sel));
6105
- if (sel.flow_enforce) {
6106
- pushSystem(output, flowTodosDirective());
6107
- }
6108
- }
6109
- 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.");
6110
- const currentMode = loadOptimizationMode();
6111
- if (currentMode === "quality") {
6112
- 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.");
6113
- } else if (currentMode === "forensic") {
6114
- 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.");
6115
- } else if (currentMode === "web-research" || currentMode === "exploration") {
6116
- 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.");
6117
- } else if (currentMode === "defense_in_depth") {
6118
- 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.");
6119
- } else if (currentMode === "reporting") {
6120
- 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.");
6121
- } else if (currentMode === "verify") {
6122
- 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.");
6123
- }
6124
- pushSystem(output, contextBudgetDirective(_input, output));
6219
+ const budgetDirective = contextBudgetDirective(_input, output);
6220
+ if (budgetDirective) pushSystem(output, budgetDirective);
6125
6221
  if (!oneShot(fp2)) {
6126
6222
  pushSystem(output, buildProjectBriefing(currentProjectName || ""));
6127
6223
  }
6128
6224
  if (!oneShot("vibeos_patterns_" + fp2)) {
6129
- pushSystem(output, patternDirective(fp2));
6225
+ const pd = patternDirective(fp2);
6226
+ if (pd) pushSystem(output, pd);
6130
6227
  }
6131
6228
  if (!oneShot("trinity_welcome_" + fp2)) {
6132
6229
  pushSystem(output, welcomeDirective());
6133
6230
  }
6134
6231
  const calDir = join12(homedir8(), ".claude");
6135
6232
  const calFile = join12(calDir, "calibration-data.jsonl");
6136
- const regime = _latestBlackboxState3?.sub_regime || classifyTurnSimple(latestUserIntent || "");
6233
+ const regime2 = _latestBlackboxState3?.sub_regime || classifyTurnSimple(latestUserIntent || "");
6137
6234
  const calRecord = JSON.stringify({
6138
6235
  ts: (/* @__PURE__ */ new Date()).toISOString(),
6139
6236
  sid: _OC_SID4,
6140
- mode: currentMode,
6141
- regime,
6237
+ mode: _currentTemplate,
6238
+ regime: regime2,
6142
6239
  stress: stressScore,
6143
6240
  fp: currentProjectFingerprint || ""
6144
6241
  }) + "\n";
@@ -6151,7 +6248,7 @@ var onSystemTransform = async (_input, output) => {
6151
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.");
6152
6249
  }
6153
6250
  if (!oneShot("vibeos_dopamine_style_" + fp2)) {
6154
- 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.");
6155
6252
  }
6156
6253
  } catch (err) {
6157
6254
  console.error(`[vibeOS] system.transform failed: ${err.message}`);
@@ -7808,7 +7905,7 @@ var onToolExecuteBefore = async (input, output) => {
7808
7905
  const hit = getScratchpadHit(t, args);
7809
7906
  if (hit && !scratchpadHitsSeen2.has(hit.hash)) {
7810
7907
  scratchpadHitsSeen2.add(hit.hash);
7811
- const total = recordScratchpadObservation();
7908
+ const total = recordScratchpadObservation(t, args, hit.sizeBytes, { hash: hit.hash });
7812
7909
  const _inputTokens = Math.max(1, Math.round(hit.sizeBytes / BYTES_PER_TOKEN));
7813
7910
  _cacheSave = Math.max(1e-4, Math.round(_inputTokens * CACHE_SAVED_PER_1M_INPUT_TOKENS / 1e6 * 1e4) / 1e4);
7814
7911
  const cacheSaved = recordCacheSaving(t, _cacheSave, { hash: hit.hash });
@@ -8506,28 +8603,29 @@ function loadMcpPort() {
8506
8603
  const envPort = process.env.VIBEOS_MCP_PORT;
8507
8604
  if (envPort != null && envPort !== "") {
8508
8605
  const n = Number(envPort);
8509
- if (!Number.isFinite(n)) return 9578;
8606
+ if (!Number.isFinite(n)) return 0;
8510
8607
  return n;
8511
8608
  }
8512
8609
  try {
8513
8610
  if (existsSync14(TIERS_FILE2)) {
8514
8611
  const tiers = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
8515
- const cfg = tiers?.selection?.mcp_port ?? tiers?.mcp_port;
8612
+ const cfg = tiers?.selection?.mcp_port;
8516
8613
  if (cfg === false || cfg === "disabled" || cfg === 0) return 0;
8517
8614
  const n = Number(cfg);
8518
8615
  if (Number.isFinite(n)) return n;
8519
8616
  }
8520
8617
  } catch {
8521
8618
  }
8522
- return 9578;
8619
+ return 0;
8523
8620
  }
8524
8621
  function persistMcpPort(port) {
8525
8622
  try {
8526
8623
  if (!existsSync14(TIERS_FILE2)) return;
8527
8624
  const tiers = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
8528
8625
  tiers.selection ??= {};
8529
- if (Number(tiers.selection.mcp_port) === Number(port)) return;
8626
+ if (Number(tiers.selection.mcp_port) === Number(port) && !("mcp_port" in tiers)) return;
8530
8627
  tiers.selection.mcp_port = port;
8628
+ if ("mcp_port" in tiers) delete tiers.mcp_port;
8531
8629
  mkdirSync11(dirname8(TIERS_FILE2), { recursive: true });
8532
8630
  const tmp = TIERS_FILE2 + ".tmp." + Date.now();
8533
8631
  writeFileSync12(tmp, JSON.stringify(tiers, null, 2) + "\n", "utf-8");
@@ -8797,7 +8895,9 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8797
8895
  }
8798
8896
  if (_tiersData2) {
8799
8897
  _tiersData2.selection ??= {};
8800
- 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
+ }
8801
8901
  mkdirSync11(dirname8(TIERS_FILE2), { recursive: true });
8802
8902
  const _tmp = TIERS_FILE2 + ".tmp." + Date.now();
8803
8903
  writeFileSync12(_tmp, JSON.stringify(_tiersData2, null, 2) + "\n", "utf-8");
@@ -8821,8 +8921,14 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8821
8921
  }
8822
8922
  try {
8823
8923
  const _mt = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
8824
- if (_mt.selection && (_mt.selection.mcp_port === void 0 || _mt.selection.mcp_port === null)) {
8825
- _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) {
8826
8932
  const _tmp = TIERS_FILE2 + ".tmp." + Date.now();
8827
8933
  writeFileSync12(_tmp, JSON.stringify(_mt, null, 2) + "\n", "utf-8");
8828
8934
  renameSync6(_tmp, TIERS_FILE2);
@@ -8882,7 +8988,9 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8882
8988
  currentTier,
8883
8989
  currentProjectFingerprint,
8884
8990
  currentProjectName,
8885
- latestUserIntent,
8991
+ get latestUserIntent() {
8992
+ return latestUserIntent;
8993
+ },
8886
8994
  directory: directory3,
8887
8995
  safeJsonParse: safeJsonParse3,
8888
8996
  readFileSync: readFileSync15,