vibeostheog 0.20.9 → 0.20.10

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,7 @@
1
+ ## 0.20.10
2
+ - feat: wire PIVOT BACK with rich context injection and smart cache warming
3
+
4
+
1
5
  ## 0.20.9
2
6
  - feat: hard-block console.log/debug/info in eslint (warn->error)
3
7
  - fix: update constants.js OPUS_DISABLE to 1e-10 (dead code, matches TS source)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeostheog",
3
- "version": "0.20.9",
3
+ "version": "0.20.10",
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",
@@ -2,10 +2,12 @@
2
2
  import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from "node:fs";
3
3
  import { join, basename } from "node:path";
4
4
  import { createHash } from "node:crypto";
5
- import { currentModel, currentProjectFingerprint, currentProjectName, _blackboxEnabled, loadSelection, writeSelection, safeJsonParse, applyDecadence, getSessionScratchpadDir, ensureSessionScratchpadDirs, indexAppend, briefedProjects, getActiveJobForProject, loadTodos, promotedProjectPatterns, detectTechStack, projectFingerprint, TRINITY_OPENCODE_CONFIG, TIERS_FILE, loadGlobalLearning, setCurrentProjectFingerprint, setCurrentProjectName, stableJson, TOOL_NAME_NORMALIZE, } from "../state.js";
5
+ import { currentModel, currentProjectFingerprint, currentProjectName, _blackboxEnabled, loadSelection, writeSelection, safeJsonParse, applyDecadence, getSessionScratchpadDir, ensureSessionScratchpadDirs, indexAppend, briefedProjects, getActiveJobForProject, loadTodos, promotedProjectPatterns, detectTechStack, projectFingerprint, TRINITY_OPENCODE_CONFIG, TIERS_FILE, loadGlobalLearning, setCurrentProjectFingerprint, setCurrentProjectName, stableJson, TOOL_NAME_NORMALIZE, _cacheDb, } from "../state.js";
6
6
  import { applySlot, TRINITY_CHEAP, TRINITY_MEDIUM, } from "../pricing.js";
7
7
  import { scoreStress, classifyTurnSimple, loadOptimizationMode, saveOptimizationMode, selectOptimizationModeRemote, computeControlVector, getBlackboxTracker, loadBlackboxState as loadBlackboxStateFromCtx, saveBlackboxState as saveBlackboxStateToCtx, extractLastUserText, isLikelyOffTopic, fetchBlackboxEnrichment, estimateContextBudget, buildControlHistoryEntry, } from "../turn-classify.js";
8
8
  import { applyBudgetFirstMode, peekBudgetFirstMode } from "../mode-policy.js";
9
+ import { vibemaxPipeline } from "../../vibeOS-lib/blackbox/vibemax.js";
10
+ import { addCacheEntry, extractRecentCacheOutputs } from "../../vibeOS-lib/smart-cache.js";
9
11
  import { remoteCall } from "../api-client.js";
10
12
  import { loadCredit } from "../credit-api.js";
11
13
  import { loadSessionOptMode, loadSessionSlot, writeSessionSlot } from "../selection-manager.js";
@@ -583,6 +585,33 @@ export const onSystemTransform = async (_input, output) => {
583
585
  const stressScore = rawStress * (_controlVector?.stress_multiplier ?? 1);
584
586
  const credit = loadCredit();
585
587
  _turnCountInject++;
588
+ // ── Pivot detection and PIVOT BACK injection ──
589
+ if (latestUserIntent && _blackboxEnabled !== false) {
590
+ try {
591
+ const pivotResult = await vibemaxPipeline({
592
+ user_text: latestUserIntent,
593
+ _pivotContext: {
594
+ files: onSystemTransform._recentFiles || [],
595
+ decisions: onSystemTransform._recentDecisions || [],
596
+ blockers: onSystemTransform._recentBlockers || [],
597
+ toolOutputs: _cacheDb ? extractRecentCacheOutputs(_cacheDb, 10) : [],
598
+ }
599
+ });
600
+ if (pivotResult?.pivot?.injection) {
601
+ pushSystem(output, pivotResult.pivot.injection);
602
+ // Warm smart cache with workflow tool outputs
603
+ if (pivotResult.pivot.workflowId && pivotResult.pivot.toolOutputs?.length > 0) {
604
+ try {
605
+ for (const entry of pivotResult.pivot.toolOutputs) {
606
+ addCacheEntry(_cacheDb, entry.hash, entry.tool, entry.prompt, entry.sizeBytes || 1024, entry.ageSec || 3600);
607
+ }
608
+ }
609
+ catch { /* cache warming is best-effort */ }
610
+ }
611
+ }
612
+ }
613
+ catch { /* pivot pipeline is best-effort */ }
614
+ }
586
615
  const stressMitigationDirective = rawStress > 0.7
587
616
  ? "[stress mitigation: CRITICAL] The user's message shows very high stress indicators. " +
588
617
  "Stay calm, structured, and thorough. Use proper markdown formatting with code blocks, " +
@@ -87,6 +87,7 @@ export class PivotCache {
87
87
  access_count: 0,
88
88
  useful_sections: ["decisions", "files"],
89
89
  skip_sections: [],
90
+ toolOutputs: context.toolOutputs || [],
90
91
  };
91
92
  this.store.pivots[workflowId] = entry;
92
93
  if (!this.pivotSequence.includes(workflowId)) {
@@ -138,14 +139,33 @@ export class PivotCache {
138
139
  return "";
139
140
  const parts = [];
140
141
  const skip = new Set(entry.skip_sections);
142
+ // Intent — what was this workflow about
143
+ const intent = entry.intent || entry.tokens.join(", ") || "";
144
+ if (intent) {
145
+ parts.push(`[PIVOT BACK] Returning to workflow: "${intent}". Context from previous session follows.`);
146
+ }
147
+ // Files — what was being modified
148
+ if (!skip.has("files") && entry.files.length > 0) {
149
+ parts.push(`[files modified] ${entry.files.slice(0, 6).join(", ")}`);
150
+ }
151
+ // Decisions — key choices made
141
152
  if (!skip.has("decisions") && entry.decisions.length > 0) {
142
- parts.push(`[${workflowId}] ${entry.decisions.slice(0, 3).join(" | ")}`);
153
+ const filtered = entry.decisions.filter(d => d !== "previous workflow captured at pivot point");
154
+ if (filtered.length > 0) {
155
+ parts.push(`[decisions] ${filtered.slice(0, 3).join(" | ")}`);
156
+ }
143
157
  }
144
- if (entry.blockers.length > 0 && !skip.has("blockers")) {
158
+ // Blockers what was blocking progress
159
+ if (!skip.has("blockers") && entry.blockers.length > 0) {
145
160
  parts.push(`[blockers] ${entry.blockers.slice(0, 2).join(" | ")}`);
146
161
  }
162
+ // Code snippets — relevant context
147
163
  if (entry.code_snippets.length > 0 && entry.useful_sections.includes("code") && !skip.has("code")) {
148
- parts.push(`[code] ${entry.code_snippets.slice(0, 2).join(" | ")}`);
164
+ parts.push(`[code context] ${entry.code_snippets.slice(0, 2).join(" | ")}`);
165
+ }
166
+ // If nothing useful, return a minimal note
167
+ if (parts.length <= 1 && entry.tokens.length > 0) {
168
+ return `[PIVOT BACK] Returning to workflow tagged: ${entry.tokens.join(", ")}. Intent: ${intent}`;
149
169
  }
150
170
  return parts.join("\n");
151
171
  }
@@ -84,6 +84,7 @@ export function resetVibeMaXPipeline() {
84
84
  if (pivotCache)
85
85
  pivotCache.resetSequence();
86
86
  }
87
+ export { getPivotCache };
87
88
  export function vibemaxSelectMode(input = {}) {
88
89
  const stress = Number(input.stress_multiplier || input.stress || 0);
89
90
  const pm = autoSelectMode(input.sub_regime, stress) || fallback(input.sub_regime, input.user_text || input.prompt || "");
@@ -105,7 +106,13 @@ export function vibemaxSelectMode(input = {}) {
105
106
  auto_result: null, tier: "medium", thinking: think, tdd: "quality", flow: "strict",
106
107
  enforcement: "strict", wbp: cfg.wbp || "normal", c7: "required", kp: cfg.kp || [3, 6],
107
108
  tc: 0.3, amode: "plan", cost: 0.3,
108
- pivot: isPivotBack ? { matchedId: pivotBack.matchedId, confidence: pivotBack.confidence, injection } : null,
109
+ pivot: isPivotBack ? {
110
+ matchedId: pivotBack.matchedId,
111
+ confidence: pivotBack.confidence,
112
+ injection,
113
+ intent: pivotBack.intent,
114
+ toolOutputs: (pc.read(pivotBack.matchedId)?.toolOutputs || []),
115
+ } : null,
109
116
  };
110
117
  }
111
118
  export function vibemaxPipeline(input = {}) {
@@ -120,8 +127,13 @@ export function vibemaxPipeline(input = {}) {
120
127
  pc.snapshot(prevId, {
121
128
  tokens: [...prevTokens],
122
129
  intent: prevMessage.substring(0, 60),
123
- decisions: ["previous workflow captured at pivot point"],
124
- files: [], code_snippets: [], blockers: [],
130
+ decisions: input._pivotContext?.decisions?.length
131
+ ? input._pivotContext.decisions
132
+ : [`workflow: ${prevMessage.substring(0, 80)}`],
133
+ files: input._pivotContext?.files || [],
134
+ code_snippets: input._pivotContext?.code_snippets || [],
135
+ blockers: input._pivotContext?.blockers || [],
136
+ toolOutputs: input._pivotContext?.toolOutputs || [],
125
137
  });
126
138
  }
127
139
  const result = vibemaxSelectMode(input);
@@ -91,6 +91,21 @@ export function compositeSimilarity(a, b) {
91
91
  cosineSimilarity(a, b) * 0.35 +
92
92
  keywordOverlapScore(a, b) * 0.30);
93
93
  }
94
+ // ── Cache tool output export for PIVOT snapshot ────────────────────
95
+ export function extractRecentCacheOutputs(db, limit = 10) {
96
+ if (!db?.entries || !Array.isArray(db.entries))
97
+ return [];
98
+ const now = Date.now();
99
+ return db.entries
100
+ .slice(-limit)
101
+ .map(e => ({
102
+ hash: e.hash || "",
103
+ tool: e.tool || "",
104
+ prompt: e.prompt?.slice(0, 120) || "",
105
+ sizeBytes: e.sizeBytes || 1024,
106
+ ageSec: e.at ? Math.round((now - new Date(e.at).getTime()) / 1000) : 3600,
107
+ }));
108
+ }
94
109
  // ── Cache database management ───────────────────────────────────────
95
110
  export function createCacheDatabase() {
96
111
  return { entries: [], stats: {} };