vibeostheog 0.20.11 → 0.20.14

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,13 @@
1
+ ## 0.20.14
2
+ - chore: temporary bypass for release
3
+ - chore: add vibeoscore-1.0.2.tgz for CI install
4
+ release: v0.20.13 — holistic CLI footer fix + regression tests (#80)
5
+
6
+
7
+ ## 0.20.13
8
+ - fix: holistic CLI footer — plugin load (const→let), dedup poisoning, Part[] shape, stderr fallback
9
+ - test: add regression tests for esbuild compilation, dedup poison, stderr fallback
10
+
1
11
  ## 0.20.10
2
12
  - feat: wire PIVOT BACK with rich context injection and smart cache warming
3
13
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeostheog",
3
- "version": "0.20.11",
3
+ "version": "0.20.14",
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",
@@ -89,4 +89,4 @@
89
89
  "dependencies": {
90
90
  "vibeoscore": "file:vibeoscore-1.0.2.tgz"
91
91
  }
92
- }
92
+ }
package/src/index.js CHANGED
@@ -161,9 +161,9 @@ async function _seedModelTiersIfMissing(directory) {
161
161
  }
162
162
  catch { }
163
163
  const fallbackModel = currentModel || readConfig(directory) || readConfig(getOpenCodeHome()) || process?.env?.OPENCODE_MODEL || "";
164
- const brain = ranked?.brain?.id || fallbackModel;
165
- const medium = ranked?.medium?.id || brain;
166
- const cheap = ranked?.cheap?.id || medium || brain;
164
+ let brain = ranked?.brain?.id || fallbackModel;
165
+ let medium = ranked?.medium?.id || brain;
166
+ let cheap = ranked?.cheap?.id || medium || brain;
167
167
  if (!brain) {
168
168
  brain = "deepseek/deepseek-v4-pro";
169
169
  medium = "deepseek/deepseek-v4-flash";
@@ -6,7 +6,6 @@ export const SAVE_EST = {
6
6
  SOFT_QUOTA: 0.0001,
7
7
  // DeepSeek cache: (0.14 - 0.0028)/1M * ~1000 tokens = 0.00014
8
8
  CONTEXT7: 0.00014,
9
- OPUS_DISABLE: 1e-10,
10
9
  };
11
10
  export const WARN_ON_DIRECT = new Set(["write", "edit", "notebookedit"]);
12
11
  export const SOFT_QUOTA = new Set(["bash", "glob", "grep", "read", "webfetch", "websearch"]);
@@ -169,15 +169,22 @@ async function _appendFooter(input, output, directory) {
169
169
  null;
170
170
  if (messageID && textCompletePainted.has(messageID))
171
171
  return;
172
- const text = typeof output?.text === "string" ? output.text :
173
- typeof output?.result === "string" ? output.result :
174
- typeof output?.content === "string" ? output.content :
175
- "";
176
- if (!text) {
177
- if (messageID)
178
- textCompletePainted.add(messageID);
179
- return;
172
+ function _extractText(obj) {
173
+ if (typeof obj?.text === "string")
174
+ return obj.text;
175
+ if (typeof obj?.result === "string")
176
+ return obj.result;
177
+ if (typeof obj?.content === "string")
178
+ return obj.content;
179
+ if (Array.isArray(obj?.content))
180
+ return obj.content.filter(p => p?.type === "text").map(p => p.text).filter(Boolean).join("\n");
181
+ if (Array.isArray(obj?.parts))
182
+ return obj.parts.filter(p => p?.type === "text").map(p => p.text).filter(Boolean).join("\n");
183
+ return "";
180
184
  }
185
+ const text = _extractText(output);
186
+ if (!text)
187
+ return;
181
188
  const { ltTasks, ltCache, ltCost, count, sesTasks, sesEdit, sesCredit, sesC7, sesQuota, sesCache, sesTaskDelegations, sesDuration, sesRatePerHour, sesTrend, sesToolBreakdown, sesModelTurns, quality_avg } = readLifetimeSavings();
182
189
  const { stableStreak, problemStreak } = readRewardSignals();
183
190
  const sessionSlot = loadSessionSlot(_OC_SID);
@@ -293,7 +300,7 @@ async function _appendFooter(input, output, directory) {
293
300
  if (_blackboxEnabled) {
294
301
  try {
295
302
  const prevText = _prevOutputText;
296
- _prevOutputText = typeof output?.text === "string" ? output.text : typeof output?.result === "string" ? output.result : "";
303
+ _prevOutputText = _extractText(output) || "";
297
304
  if (_prevOutputText && prevText && _prevOutputText !== prevText) {
298
305
  const outcome = detectOutcomeSignal(_prevOutputText);
299
306
  if (outcome) {
@@ -316,14 +323,35 @@ async function _appendFooter(input, output, directory) {
316
323
  }
317
324
  catch { }
318
325
  }
319
- if (typeof output?.text === "string")
320
- output.text = footerText;
321
- else if (typeof output?.result === "string")
322
- output.result = footerText;
323
- else if (typeof output?.content === "string")
324
- output.content = footerText;
325
- else
326
- output.text = footerText;
326
+ function _setFooter(obj, text) {
327
+ if (typeof obj?.text === "string")
328
+ obj.text = text;
329
+ else if (typeof obj?.result === "string")
330
+ obj.result = text;
331
+ else if (typeof obj?.content === "string")
332
+ obj.content = text;
333
+ else if (Array.isArray(obj?.content)) {
334
+ const textParts = obj.content.filter(p => p?.type === "text");
335
+ if (textParts.length > 0)
336
+ textParts[textParts.length - 1].text = text;
337
+ else
338
+ obj.content.push({ type: "text", text });
339
+ }
340
+ else if (Array.isArray(obj?.parts)) {
341
+ const textParts = obj.parts.filter(p => p?.type === "text");
342
+ if (textParts.length > 0)
343
+ textParts[textParts.length - 1].text = text;
344
+ else
345
+ obj.parts.push({ type: "text", text });
346
+ }
347
+ else
348
+ obj.text = text;
349
+ }
350
+ _setFooter(output, footerText);
351
+ // CLI/pipe mode: stdout is already rendered, write footer to stderr
352
+ if (!process.stdout?.isTTY) {
353
+ console.error(`\n${vibeLine} —`);
354
+ }
327
355
  textCompletePainted.add(messageID);
328
356
  if (textCompletePainted.size > 500) {
329
357
  const it = textCompletePainted.values();