vibeostheog 0.20.14 → 0.20.16
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 +33 -0
- package/README.md +49 -0
- package/package.json +3 -3
- package/src/index.js +54 -1
- package/src/lib/api-client.js +15 -0
- package/src/lib/hooks/chat-transform.js +37 -12
- package/src/lib/hooks/tool-execute.js +18 -5
- package/src/lib/mode-policy.js +1 -1
- package/src/lib/pricing.js +135 -1
- package/src/lib/trinity-tool.js +9 -5
- package/src/lib/turn-classify.js +7 -6
- package/src/lib/vibeos-mcp-server.js +32 -1
- package/src/vibeOS-lib/blackbox/meta-controller.js +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
## 0.20.16
|
|
2
|
+
- fix: skip cache savings for free models + add modelCostPerTurn fallback + regression tests
|
|
3
|
+
- fix: wire incrementTurnCounter into onToolExecuteAfter so session compaction fires at turn 7+
|
|
4
|
+
- fix: make tests resilient in CI environment
|
|
5
|
+
- perf: add MODEL_PRICING_PER_1M with per-provider input/output rates
|
|
6
|
+
- perf: provider-aware cache savings with isModelFree gate + regression tests
|
|
7
|
+
- perf: dynamic cache savings rate from per-model input pricing
|
|
8
|
+
- perf: record cache savings for compressed tool outputs (write path)
|
|
9
|
+
- ci: retrigger checks for merge
|
|
10
|
+
Merge pull request #92 from DrunkkToys/pr/regression-tests-cache-savings
|
|
11
|
+
Merge pull request #91 from DrunkkToys/pr/cache-write-savings
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## 0.20.15
|
|
15
|
+
- feat: dashboard blackbox telemetry — bidirectional BE/FE sync
|
|
16
|
+
- fix: mock auth and clear OPENCODE_MODEL in bootstrap test, commit blackbox .js for CI
|
|
17
|
+
- fix: mock auth and clear OPENCODE_MODEL in bootstrap test, commit blackbox .js for CI
|
|
18
|
+
- docs: fix speed mode quality rating in comparison table (#83)
|
|
19
|
+
- docs: fix token defaults in env vars table
|
|
20
|
+
- docs: update README to reflect actual features and fix inaccuracies
|
|
21
|
+
- chore: fix auto-fixable ESLint warnings project-wide (453 fixed, 899 -> 446)
|
|
22
|
+
- chore: restore vibeoscore-1.0.2.tgz
|
|
23
|
+
Add vibemax and vibeqmax mode aliases to trinity mode command
|
|
24
|
+
Fix VibeMaX recognized as manual mode, route to medium tier
|
|
25
|
+
Fix VibeMaX routing to use medium tier, not brain
|
|
26
|
+
List vibeOS-lib tests explicitly in test:ci to fix CI glob resolution
|
|
27
|
+
Exclude blackbox TS from compilation to prevent CI clobbering JS sources
|
|
28
|
+
Fix test:ci glob pattern for CI compatibility
|
|
29
|
+
Remove VibeMaX auto-gate from meta-controller.ts
|
|
30
|
+
Move VibeMaX ML pipeline to backend API
|
|
31
|
+
revert: remove temporary release bypass
|
|
32
|
+
|
|
33
|
+
|
|
1
34
|
## 0.20.14
|
|
2
35
|
- chore: temporary bypass for release
|
|
3
36
|
- chore: add vibeoscore-1.0.2.tgz for CI install
|
package/README.md
CHANGED
|
@@ -30,6 +30,55 @@ Every `write`/`edit`/`notebookedit` on the **brain tier** is intercepted, cost-e
|
|
|
30
30
|
|
|
31
31
|
Every blocked brain-tier write/edit saves at least $0.026 (Opus→Sonnet). The running total is tracked in `~/.claude/delegation-state.json` and displayed in the live footer.
|
|
32
32
|
|
|
33
|
+
## VibeBoX Optimization Modes
|
|
34
|
+
|
|
35
|
+
Benchmarked on the DeepSeek v4 family — the default model stack for vibeOS.
|
|
36
|
+
|
|
37
|
+
### Model Pricing (700 input + 300 output tokens)
|
|
38
|
+
|
|
39
|
+
| Model | API ID | Per Turn | Per 1K Turns |
|
|
40
|
+
|---|---|---|---|
|
|
41
|
+
| v4 Pro (brain) | `deepseek/deepseek-v4-pro` | $0.00057 | $0.57 |
|
|
42
|
+
| v4 Flash (medium) | `deepseek/deepseek-v4-flash` | $0.00018 | $0.18 |
|
|
43
|
+
| DeepSeek Chat (budget) | `deepseek/deepseek-chat` | $0.00015 | $0.15 |
|
|
44
|
+
|
|
45
|
+
### Mode Comparison — All Modes vs Raw Top Tier
|
|
46
|
+
|
|
47
|
+
| Mode | Model | Thinking | Enforcement | Flow | TDD | Quality | Cost/Turn | vs Raw | Saves |
|
|
48
|
+
|---|---|---|---|---|---|---|---|---|---|
|
|
49
|
+
| **Raw Top Tier** | v4 Pro | full | — | — | — | baseline | $0.00057 | 1.00x | — |
|
|
50
|
+
| **VibeQMaX** (quality) | v4 Pro | full | strict | strict | quality | ~baseline | $0.00029 | 0.50x | **50%** |
|
|
51
|
+
| **VibeMaX** ⭐ | v4 Flash | full | strict | strict | quality | ~70% | $0.00021 | 0.37x | **63%** |
|
|
52
|
+
| **speed** | v4 Flash | off | relaxed | audit | lazy | ~55% | $0.00018 | 0.32x | 68% |
|
|
53
|
+
| **budget** | DeepSeek Chat | off | relaxed | audit | lazy | ~40% | $0.00015 | 0.26x | 74% |
|
|
54
|
+
| **auto** | varies | auto | auto | auto | auto | varies | varies | varies | varies |
|
|
55
|
+
|
|
56
|
+
### Cost vs Quality Visual
|
|
57
|
+
|
|
58
|
+
The raw model (v4 Pro, full thinking) sets the quality baseline. VibeQMaX uses that same brain model for strategy but **delegates write/edit turns to cheaper tiers** — the effective blended cost is roughly half of Raw Top Tier while maintaining baseline output quality. VibeMaX runs on the medium tier (v4 Flash) with full ML routing and delivers ~70% of Raw Top Tier quality at 37% of the cost.
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
Quality
|
|
62
|
+
baseline ● Raw Top Tier · VibeQMaX
|
|
63
|
+
~70% │ ● VibeMaX ⭐
|
|
64
|
+
~55% │ ● speed
|
|
65
|
+
~40% │ ● budget
|
|
66
|
+
│
|
|
67
|
+
└────────────────────────
|
|
68
|
+
1.0x 0.50x 0.37x 0.32x 0.26x
|
|
69
|
+
Cost Multiplier
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Branded Modes
|
|
73
|
+
|
|
74
|
+
**VibeQMaX (Quality Max)** — The highest-assurance configuration. Routes strategic turns through `deepseek/deepseek-v4-pro` with full thinking, strict enforcement, strict flow checks, and quality TDD. Write/edit turns are delegated to cheaper tiers per enforcement rules, yielding an **effective blended cost of ~$0.00029/turn (≈50% of Raw Top Tier)**. Guardrails include: delegation enforcement blocks costly mistakes, flow pattern validation prevents structural issues, TDD skeleton generation ensures test coverage, and context7 optimization reduces context waste. VibeQMaX maps to the system's **quality** mode — brain-tier settings with the full vibeOS control plane active.
|
|
75
|
+
|
|
76
|
+
**VibeMaX (ML-Optimized)** — The intelligent cost-quality sweet spot. Routes through `deepseek/deepseek-v4-flash` (medium tier) and uses a random forest classifier (29 trees, gini-split, trained on telemetry) to decide each turn whether to apply optimized (full quality) or budget (fast/cheap) treatment. Classifies on 11 derived features: message length, code block density, urgency signals, complexity, instruction density, question ratio, and more. Trained via `trainVibeMaXModelFromTelemetry()` on real session data with bootstrap fallback. PivotCache integration detects return-to-workflow patterns and restores prior context. Benchmarked at **~70% of Raw Top Tier quality at 37% of the cost**.
|
|
77
|
+
|
|
78
|
+
### Benchmark Details
|
|
79
|
+
|
|
80
|
+
All tests run with `deepseek/deepseek-v4-pro` (brain), `deepseek/deepseek-v4-flash` (medium), and `deepseek/deepseek-chat` (budget). Quality scores measured against Raw Top Tier (v4 Pro, full thinking, no vibeOS overhead). VibeMaX quality benchmark derived from real session telemetry with bootstrap confidence intervals.
|
|
81
|
+
|
|
33
82
|
---
|
|
34
83
|
|
|
35
84
|
## Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeostheog",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.16",
|
|
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",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"checkpoint:validate": "node scripts/checkpoint-validate.mjs",
|
|
15
15
|
"test:scripts": "node --test scripts/tests/checkpoint-validate.test.mjs tests/release-pack.test.mjs",
|
|
16
16
|
"ts:audit": "node scripts/ts-audit.mjs",
|
|
17
|
-
"test": "VIBEOS_MCP_PORT=0 node --test --test-timeout=240000 tests/deep_integration.test.mjs tests/production_regressions.test.mjs tests/release_hardening_tigerteam.test.mjs tests/test_api_migration.neutral.test.mjs tests/test_const_assignment_regression.test.mjs tests/test_delegation_enforcer.test.mjs tests/test_diagnose_cmd.test.mjs tests/test_install_and_recovery.test.mjs tests/test_internals_stress_patterns_offtopic.test.mjs tests/test_saveos_e2e_cleanup.test.mjs tests/test_tdd_enforcer.test.mjs src/tests/*.test.js src/utils/tests/*.test.mjs \"src/vibeOS-lib/tests
|
|
18
|
-
"test:ci": "VIBEOS_MCP_PORT=0 node --test --test-timeout=30000 tests/production_regressions.test.mjs tests/release_hardening_tigerteam.test.mjs tests/test_const_assignment_regression.test.mjs tests/test_diagnose_cmd.test.mjs tests/test_install_and_recovery.test.mjs tests/test_saveos_e2e_cleanup.test.mjs tests/test_tdd_enforcer.test.mjs src/tests/*.test.js src/utils/tests/*.test.mjs \"src/vibeOS-lib/tests
|
|
17
|
+
"test": "VIBEOS_MCP_PORT=0 node --test --test-timeout=240000 tests/deep_integration.test.mjs tests/production_regressions.test.mjs tests/release_hardening_tigerteam.test.mjs tests/test_api_migration.neutral.test.mjs tests/test_const_assignment_regression.test.mjs tests/test_delegation_enforcer.test.mjs tests/test_diagnose_cmd.test.mjs tests/test_install_and_recovery.test.mjs tests/test_internals_stress_patterns_offtopic.test.mjs tests/test_saveos_e2e_cleanup.test.mjs tests/test_tdd_enforcer.test.mjs src/tests/*.test.js src/utils/tests/*.test.mjs \"src/vibeOS-lib/tests/auto-select-mode.test.mjs\" \"src/vibeOS-lib/tests/blackbox-regression.test.mjs\" \"src/vibeOS-lib/tests/blackbox-smoke.test.mjs\" \"src/vibeOS-lib/tests/budget-first-mode.test.mjs\" \"src/vibeOS-lib/tests/flow-enforcer.test.mjs\" \"src/vibeOS-lib/tests/flow-secrets.test.mjs\" \"src/vibeOS-lib/tests/session-metrics.test.mjs\" \"src/vibeOS-lib/tests/test_stress.test.mjs\"",
|
|
18
|
+
"test:ci": "VIBEOS_MCP_PORT=0 node --test --test-timeout=30000 tests/production_regressions.test.mjs tests/release_hardening_tigerteam.test.mjs tests/test_const_assignment_regression.test.mjs tests/test_diagnose_cmd.test.mjs tests/test_install_and_recovery.test.mjs tests/test_saveos_e2e_cleanup.test.mjs tests/test_tdd_enforcer.test.mjs src/tests/*.test.js src/utils/tests/*.test.mjs \"src/vibeOS-lib/tests/auto-select-mode.test.mjs\" \"src/vibeOS-lib/tests/blackbox-regression.test.mjs\" \"src/vibeOS-lib/tests/blackbox-smoke.test.mjs\" \"src/vibeOS-lib/tests/budget-first-mode.test.mjs\" \"src/vibeOS-lib/tests/flow-enforcer.test.mjs\" \"src/vibeOS-lib/tests/flow-secrets.test.mjs\" \"src/vibeOS-lib/tests/session-metrics.test.mjs\" \"src/vibeOS-lib/tests/test_stress.test.mjs\"",
|
|
19
19
|
"codex:guard": "bash plugins/vibetheog-codex/scripts/run-guard.sh",
|
|
20
20
|
"codex:guard:full": "VIBETHEOG_GUARD_FULL=1 bash plugins/vibetheog-codex/scripts/run-guard.sh",
|
|
21
21
|
"codex:hook:precommit": "bash plugins/vibetheog-codex/hooks/pre-commit.sh",
|
package/src/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { createMcpServer } from "./lib/vibeos-mcp-server.js";
|
|
|
15
15
|
import { isApiConnected, setApiToken, setApiBootstrapToken, ensureBootstrapExchange, VIBEOS_API_URL } from "./lib/api-client.js";
|
|
16
16
|
import { applySlot, modelCostPerTurn, detectContext7, formatUsd, classify, _refreshModel, HIGH_TIER_RE, MID_TIER_RE, PLACEHOLDER_RE, readConfig, getTrinitySlotOrder, loadTrinitySlotsFromTiersFile, } from "./lib/pricing.js";
|
|
17
17
|
import { scoreStress, detectTechStack, loadBlackboxState, saveBlackboxState, getBlackboxTracker, getBlackboxResolution, saveOptimizationMode, } from "./lib/turn-classify.js";
|
|
18
|
-
import { safeJsonParse, readFullState, loadSelection, writeSelection, readLifetimeSavings, _OC_SID, _modelLocked, _blackboxEnabled, setBlackboxEnabled, _lockedSlot, _lockedModel, currentTier, currentModel, currentProjectFingerprint, currentProjectName, setCurrentTier, setCurrentModel, setCurrentProjectFingerprint, setCurrentProjectName, setCurrentSessionId, briefedProjects, _latestBlackboxState, getActiveJobForProject, projectFingerprint, loadProjectState, saveProjectState, ensureProjectBucket, mergeProjectBucket, setVibeOSHomeContext, SAVINGS_LEDGER_FILE, USER_HOME, CREDIT_CACHE_F, pruneScratchpadOnce, registerSessionCleanupHandlers, promotedProjectPatterns, projectPatternRows, clearProjectPatterns, loadTodos, getTodos, upsertTodo, markTodoDone, tool, } from "./lib/state.js";
|
|
18
|
+
import { safeJsonParse, readFullState, loadSelection, writeSelection, readLifetimeSavings, _OC_SID, _modelLocked, _blackboxEnabled, setBlackboxEnabled, _lockedSlot, _lockedModel, currentTier, currentModel, currentProjectFingerprint, currentProjectName, setCurrentTier, setCurrentModel, setCurrentProjectFingerprint, setCurrentProjectName, setCurrentSessionId, briefedProjects, _latestBlackboxState, _latestBlackboxLoopMsg, _latestBlackboxPivotMsg, getActiveJobForProject, projectFingerprint, loadProjectState, saveProjectState, ensureProjectBucket, mergeProjectBucket, setVibeOSHomeContext, SAVINGS_LEDGER_FILE, USER_HOME, CREDIT_CACHE_F, pruneScratchpadOnce, registerSessionCleanupHandlers, promotedProjectPatterns, projectPatternRows, clearProjectPatterns, loadTodos, getTodos, upsertTodo, markTodoDone, tool, } from "./lib/state.js";
|
|
19
19
|
import { researchAudit } from "./lib/research-audit.js";
|
|
20
20
|
import { buildStatusPayload, buildSavingsPayload, buildSessionCheckout, diagnoseStructuredFromText, projectStructuredFromText, } from "./lib/runtime-surface.js";
|
|
21
21
|
import { saveReport, listReports, readReport } from "./lib/reporting.js";
|
|
@@ -701,6 +701,59 @@ export async function DelegationEnforcer({ client, directory } = {}) {
|
|
|
701
701
|
const reportId = saveReport(checkout.report);
|
|
702
702
|
return { ok: true, summary: checkout.summary, report_id: reportId };
|
|
703
703
|
},
|
|
704
|
+
getBlackboxState: () => {
|
|
705
|
+
const tracker = getBlackboxTracker();
|
|
706
|
+
const res = getBlackboxResolution();
|
|
707
|
+
return {
|
|
708
|
+
sub_regime: res?.sub_regime || _latestBlackboxState?.sub_regime || "INIT",
|
|
709
|
+
resolution: res?.resolution || "INIT",
|
|
710
|
+
momentum: res?.momentum ?? 0,
|
|
711
|
+
features: _latestBlackboxState?.features || {},
|
|
712
|
+
signals: _latestBlackboxState?.signals || {},
|
|
713
|
+
loop: {
|
|
714
|
+
active: _latestBlackboxLoopMsg !== null,
|
|
715
|
+
message: _latestBlackboxLoopMsg,
|
|
716
|
+
intervention_level: _latestBlackboxLoopMsg?.intervention_level || _latestBlackboxState?.loop?.intervention_level || 0,
|
|
717
|
+
consecutive_loops: _latestBlackboxState?.loop?.consecutive_loops || 0,
|
|
718
|
+
},
|
|
719
|
+
pivot: {
|
|
720
|
+
detected: _latestBlackboxPivotMsg !== null,
|
|
721
|
+
message: _latestBlackboxPivotMsg,
|
|
722
|
+
},
|
|
723
|
+
continuity_state: _latestBlackboxState?.continuity_state || null,
|
|
724
|
+
turn_index: _latestBlackboxState?.turn_index ?? 0,
|
|
725
|
+
stress_level: _latestBlackboxState?.stress_level ?? 0,
|
|
726
|
+
session_id: _OC_SID,
|
|
727
|
+
project_fingerprint: currentProjectFingerprint,
|
|
728
|
+
};
|
|
729
|
+
},
|
|
730
|
+
saveBlackboxVector: (vector) => {
|
|
731
|
+
const state = loadBlackboxState() || {};
|
|
732
|
+
const sid = currentSessionId || _OC_SID;
|
|
733
|
+
if (!state.sessions) state.sessions = {};
|
|
734
|
+
if (!state.sessions[sid]) state.sessions[sid] = {};
|
|
735
|
+
if (!state.sessions[sid].dashboard_vectors) state.sessions[sid].dashboard_vectors = [];
|
|
736
|
+
state.sessions[sid].dashboard_vectors.push({
|
|
737
|
+
timestamp: Date.now(),
|
|
738
|
+
received_at: new Date().toISOString(),
|
|
739
|
+
...vector,
|
|
740
|
+
});
|
|
741
|
+
saveBlackboxState(state);
|
|
742
|
+
},
|
|
743
|
+
saveBlackboxOutcome: (outcome) => {
|
|
744
|
+
const state = loadBlackboxState() || {};
|
|
745
|
+
const sid = currentSessionId || _OC_SID;
|
|
746
|
+
if (!state.sessions) state.sessions = {};
|
|
747
|
+
if (!state.sessions[sid]) state.sessions[sid] = {};
|
|
748
|
+
if (!state.sessions[sid].dashboard_outcomes) state.sessions[sid].dashboard_outcomes = [];
|
|
749
|
+
state.sessions[sid].dashboard_outcomes.push({
|
|
750
|
+
timestamp: Date.now(),
|
|
751
|
+
received_at: new Date().toISOString(),
|
|
752
|
+
...outcome,
|
|
753
|
+
});
|
|
754
|
+
saveBlackboxState(state);
|
|
755
|
+
},
|
|
756
|
+
|
|
704
757
|
});
|
|
705
758
|
}
|
|
706
759
|
const mcpServer = await _mcpServerRuntime.start(port);
|
package/src/lib/api-client.js
CHANGED
|
@@ -274,6 +274,21 @@ export class VibeOSApiClient {
|
|
|
274
274
|
async blackboxSelectMode(subRegime, stressMultiplier) {
|
|
275
275
|
return this.request("/api/v1/blackbox/select-mode", { sub_regime: subRegime, stress_multiplier: stressMultiplier });
|
|
276
276
|
}
|
|
277
|
+
async vibemaxSelect(input = {}) {
|
|
278
|
+
return this.request("/api/v1/vibemax/select", input);
|
|
279
|
+
}
|
|
280
|
+
async vibemaxPipeline(input = {}) {
|
|
281
|
+
return this.request("/api/v1/vibemax/pipeline", input);
|
|
282
|
+
}
|
|
283
|
+
async vibemaxReset() {
|
|
284
|
+
return this.request("/api/v1/vibemax/reset", null);
|
|
285
|
+
}
|
|
286
|
+
async vibemaxModel() {
|
|
287
|
+
return this.request("/api/v1/vibemax/model", null);
|
|
288
|
+
}
|
|
289
|
+
async vibemaxTrain(telemetryPath = null) {
|
|
290
|
+
return this.request("/api/v1/vibemax/train", { telemetry_path: telemetryPath });
|
|
291
|
+
}
|
|
277
292
|
async tddExports(sourceContent, ext) {
|
|
278
293
|
return this.request("/api/v1/tdd/exports", { source_content: sourceContent, ext });
|
|
279
294
|
}
|
|
@@ -2,11 +2,10 @@
|
|
|
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, _cacheDb, } from "../state.js";
|
|
6
|
-
import { applySlot, TRINITY_CHEAP, TRINITY_MEDIUM, } from "../pricing.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, recordCacheSaving, } from "../state.js";
|
|
6
|
+
import { applySlot, TRINITY_CHEAP, TRINITY_MEDIUM, cacheSavePer1MInputTokens, } 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
9
|
import { addCacheEntry, extractRecentCacheOutputs } from "../../vibeOS-lib/smart-cache.js";
|
|
11
10
|
import { remoteCall } from "../api-client.js";
|
|
12
11
|
import { loadCredit } from "../credit-api.js";
|
|
@@ -15,6 +14,7 @@ import { noteProjectPattern } from "../index-helpers.js";
|
|
|
15
14
|
import { saveSessionStress } from "../index-helpers.js";
|
|
16
15
|
import { COMPRESS_THRESHOLD, KEEP_HOT, COMPRESS_MARKER, PROTOCOL_MARKER, PROTOCOL_TEXT } from "../constants.js";
|
|
17
16
|
import { TEMPLATES, DEFAULT_TEMPLATE, resolveTemplate, shouldInjectTemplate } from "../templates.js";
|
|
17
|
+
const BYTES_PER_TOKEN = 4;
|
|
18
18
|
function getVibeOSHome() {
|
|
19
19
|
return process.env.VIBEOS_HOME || join(process.env.HOME || "", ".claude");
|
|
20
20
|
}
|
|
@@ -309,6 +309,13 @@ function compressToolOutputs(messages) {
|
|
|
309
309
|
`[summary] ${summary}`;
|
|
310
310
|
state.output = ref;
|
|
311
311
|
compressedBytes += raw.length - ref.length;
|
|
312
|
+
const toolKey = TOOL_NAME_NORMALIZE[part.tool] || part.tool;
|
|
313
|
+
const rate = cacheSavePer1MInputTokens(currentModel);
|
|
314
|
+
if (rate > 0) {
|
|
315
|
+
const inputTokens = Math.max(1, Math.round((raw.length - ref.length) / BYTES_PER_TOKEN));
|
|
316
|
+
const saveEst = Math.max(0.0001, Math.round(inputTokens * rate / 1_000_000 * 10000) / 10000);
|
|
317
|
+
recordCacheSaving(toolKey, saveEst, { hash });
|
|
318
|
+
}
|
|
312
319
|
console.error(`[vibeOS] ctx-compress: ${raw.length}\u2192${ref.length} chars (hash: ${hash})`);
|
|
313
320
|
}
|
|
314
321
|
}
|
|
@@ -588,15 +595,33 @@ export const onSystemTransform = async (_input, output) => {
|
|
|
588
595
|
// ── Pivot detection and PIVOT BACK injection ──
|
|
589
596
|
if (latestUserIntent && _blackboxEnabled !== false) {
|
|
590
597
|
try {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
598
|
+
let pivotResult = null;
|
|
599
|
+
try {
|
|
600
|
+
const remote = await remoteCall("vibemaxPipeline", [{
|
|
601
|
+
user_text: latestUserIntent,
|
|
602
|
+
_pivotContext: {
|
|
603
|
+
files: onSystemTransform._recentFiles || [],
|
|
604
|
+
decisions: onSystemTransform._recentDecisions || [],
|
|
605
|
+
blockers: onSystemTransform._recentBlockers || [],
|
|
606
|
+
toolOutputs: _cacheDb ? extractRecentCacheOutputs(_cacheDb, 10) : [],
|
|
607
|
+
},
|
|
608
|
+
}], null);
|
|
609
|
+
if (remote?.pivot)
|
|
610
|
+
pivotResult = remote;
|
|
611
|
+
}
|
|
612
|
+
catch { /* remote vibemax pipeline */ }
|
|
613
|
+
if (!pivotResult) {
|
|
614
|
+
const { vibemaxPipeline: localPipeline } = await import("../../vibeOS-lib/blackbox/vibemax.js");
|
|
615
|
+
pivotResult = await localPipeline({
|
|
616
|
+
user_text: latestUserIntent,
|
|
617
|
+
_pivotContext: {
|
|
618
|
+
files: onSystemTransform._recentFiles || [],
|
|
619
|
+
decisions: onSystemTransform._recentDecisions || [],
|
|
620
|
+
blockers: onSystemTransform._recentBlockers || [],
|
|
621
|
+
toolOutputs: _cacheDb ? extractRecentCacheOutputs(_cacheDb, 10) : [],
|
|
622
|
+
},
|
|
623
|
+
});
|
|
624
|
+
}
|
|
600
625
|
if (pivotResult?.pivot?.injection) {
|
|
601
626
|
pushSystem(output, pivotResult.pivot.injection);
|
|
602
627
|
// Warm smart cache with workflow tool outputs
|
|
@@ -3,9 +3,9 @@ import { writeFileSync, appendFileSync, existsSync, mkdirSync } from "node:fs";
|
|
|
3
3
|
import { join, dirname, basename } from "node:path";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { currentTier, currentModel, setCurrentModel, setCurrentTier, _OC_SID, _modelLocked, loadSelection, readLifetimeSavings, recordCacheSaving, recordMissedContext7, getScratchpadHit, recordScratchpadObservation, recordPrivacyTelemetry, updateState, getSessionScratchpadDir, ensureSessionScratchpadDirs, SAVINGS_LEDGER_FILE, CONTEXT7_INSTALL_FLAG, SOFT_QUOTA_LIMIT, upsertTodo, ML_ENABLED, _mlGraph, _cacheDb, _mlSavePending, ML_CONFIDENCE_THRESHOLD, setMlSavePending, saveMLState, SCRATCHPAD_TOOLS, SCRATCHPAD_GLOBAL_DIR, TOOL_NAME_NORMALIZE, stableJson, applyDecadence, } from "../state.js";
|
|
6
|
-
import { classify, modelCostPerTurn, isModelFree, detectContext7, isDocsTarget, shortModelName, formatUsd, _refreshModel, readConfig, resolveDisplayModelId, TRINITY_CHEAP, TRINITY_MEDIUM, trendDisplay, modelToSlotLabel, resolveExecutionIdentity, formatProviderName, formatQualityName, } from "../pricing.js";
|
|
6
|
+
import { classify, modelCostPerTurn, isModelFree, detectContext7, isDocsTarget, shortModelName, formatUsd, _refreshModel, readConfig, resolveDisplayModelId, TRINITY_CHEAP, TRINITY_MEDIUM, cacheSavePer1MInputTokens, trendDisplay, modelToSlotLabel, resolveExecutionIdentity, formatProviderName, formatQualityName, } from "../pricing.js";
|
|
7
7
|
import { latestUserIntent } from "./chat-transform.js";
|
|
8
|
-
import { scoreStress, extractFirstWordFromArgs, shouldLogWarn, isUserAskingForTests, resolveEnforcementMode, getLearnedExploratoryWords, noteTaskRoutingLearning, } from "../turn-classify.js";
|
|
8
|
+
import { scoreStress, extractFirstWordFromArgs, shouldLogWarn, isUserAskingForTests, resolveEnforcementMode, getLearnedExploratoryWords, noteTaskRoutingLearning, incrementTurnCounter, } from "../turn-classify.js";
|
|
9
9
|
import { saveReport } from "../reporting.js";
|
|
10
10
|
import { loadCredit } from "../credit-api.js";
|
|
11
11
|
import { remoteCall, VIBEOS_API_ENABLED } from "../api-client.js";
|
|
@@ -17,7 +17,6 @@ import { setActiveJobFromTaskPrompt, observeToolPattern, compressText, recordSav
|
|
|
17
17
|
import { scoreTaskQuality, readRewardSignals } from "./footer.js";
|
|
18
18
|
import { SAVE_EST, WARN_ON_DIRECT, SOFT_QUOTA, FREE, MONITOR } from "../constants.js";
|
|
19
19
|
const BYTES_PER_TOKEN = 4;
|
|
20
|
-
const CACHE_SAVED_PER_1M_INPUT_TOKENS = 0.10;
|
|
21
20
|
const DEBUG_INTERNALS = process.env.VIBEOS_DEBUG_INTERNALS === "1";
|
|
22
21
|
const IS_CLI_RUNTIME = Boolean(process.stdout?.isTTY || process.stderr?.isTTY || process.stdin?.isTTY);
|
|
23
22
|
function getVibeOSHome() {
|
|
@@ -247,8 +246,12 @@ export const onToolExecuteBefore = async (input, output) => {
|
|
|
247
246
|
// Persist cache savings as a first-class savings type.
|
|
248
247
|
// Compute from actual scratchpad file size: inputs that would
|
|
249
248
|
// have been charged at miss rate are served from cache.
|
|
250
|
-
const
|
|
251
|
-
_cacheSave =
|
|
249
|
+
const rate = cacheSavePer1MInputTokens(currentModel);
|
|
250
|
+
_cacheSave = 0;
|
|
251
|
+
if (rate > 0) {
|
|
252
|
+
const _inputTokens = Math.max(1, Math.round(hit.sizeBytes / BYTES_PER_TOKEN));
|
|
253
|
+
_cacheSave = Math.max(0.0001, Math.round(_inputTokens * rate / 1_000_000 * 10000) / 10000);
|
|
254
|
+
}
|
|
252
255
|
const cacheSaved = recordCacheSaving(t, _cacheSave, { hash: hit.hash });
|
|
253
256
|
const sumNote = hit.summaryPath ? ` (summary: ${hit.summaryPath})` : "";
|
|
254
257
|
const cacheNote = cacheSaved ? `, cache+$${(cacheSaved.lifetime || 0).toFixed(3)} lt` : "";
|
|
@@ -604,6 +607,11 @@ export const onToolExecuteAfter = async (input, output) => {
|
|
|
604
607
|
}
|
|
605
608
|
}
|
|
606
609
|
catch { }
|
|
610
|
+
// ── Increment turn counter for compaction trigger ──
|
|
611
|
+
try {
|
|
612
|
+
incrementTurnCounter();
|
|
613
|
+
}
|
|
614
|
+
catch { }
|
|
607
615
|
// ── Generate footer alert (prepended to tool result, visible in chat) ──
|
|
608
616
|
let _footerText = "";
|
|
609
617
|
try {
|
|
@@ -683,6 +691,11 @@ export const onToolExecuteAfter = async (input, output) => {
|
|
|
683
691
|
}
|
|
684
692
|
}
|
|
685
693
|
catch { }
|
|
694
|
+
// ── Increment turn counter for compaction trigger ──
|
|
695
|
+
try {
|
|
696
|
+
incrementTurnCounter();
|
|
697
|
+
}
|
|
698
|
+
catch { }
|
|
686
699
|
// ── End footer ──
|
|
687
700
|
const t = input?.tool ?? "";
|
|
688
701
|
if (t === "trinity") {
|
package/src/lib/mode-policy.js
CHANGED
|
@@ -2,7 +2,7 @@ import { BLACKBOX_STATE_FILE, _OC_SID, loadBlackboxState, saveBlackboxState, wit
|
|
|
2
2
|
const BASELINE_MODE = "budget";
|
|
3
3
|
const LOOP_REGIMES = new Set(["LOOPING", "DIVERGENT"]);
|
|
4
4
|
const QUALITY_REGIMES = new Set(["CONVERGING", "CLOSED"]);
|
|
5
|
-
const MANUAL_MODES = new Set(["balanced", "quality", "speed", "longrun"]);
|
|
5
|
+
const MANUAL_MODES = new Set(["balanced", "quality", "speed", "longrun", "vibemax"]);
|
|
6
6
|
function normalizeMode(mode) {
|
|
7
7
|
const normalized = String(mode || BASELINE_MODE).toLowerCase();
|
|
8
8
|
if (normalized === "auto" || normalized === "")
|
package/src/lib/pricing.js
CHANGED
|
@@ -260,6 +260,50 @@ export function trendDisplay(sesTrend) {
|
|
|
260
260
|
const CACHE_SAVED_PER_1M_INPUT_TOKENS = 0.10;
|
|
261
261
|
// Approximate bytes per token for JSON/text content (varies 3-6, use 4 as safe estimate).
|
|
262
262
|
const BYTES_PER_TOKEN = 4;
|
|
263
|
+
export function parseOpenRouterInputPer1M(modelRow) {
|
|
264
|
+
const p = modelRow?.pricing || {};
|
|
265
|
+
const inTok = Number(p.prompt ?? p.input ?? p.request);
|
|
266
|
+
if (Number.isFinite(inTok) && inTok > 0) {
|
|
267
|
+
return Math.round(inTok * 1_000_000 * 10000) / 10000;
|
|
268
|
+
}
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
export function cacheSavePer1MInputTokens(model) {
|
|
272
|
+
if (!model)
|
|
273
|
+
return CACHE_SAVED_PER_1M_INPUT_TOKENS;
|
|
274
|
+
if (isModelFree(model))
|
|
275
|
+
return 0;
|
|
276
|
+
const rawKey = String(model || "");
|
|
277
|
+
const key = normalizeModelId(model);
|
|
278
|
+
const rawNoPrefix = rawKey.includes("/") ? rawKey.split("/")[rawKey.split("/").length - 1] : rawKey;
|
|
279
|
+
try {
|
|
280
|
+
const cache = _loadDynamicPricingCache();
|
|
281
|
+
for (const candidate of [rawKey, key, rawNoPrefix]) {
|
|
282
|
+
const entry = cache[candidate];
|
|
283
|
+
const rate = parseOpenRouterInputPer1M(entry);
|
|
284
|
+
if (rate !== null)
|
|
285
|
+
return rate;
|
|
286
|
+
}
|
|
287
|
+
for (const [ck, cv] of Object.entries(cache)) {
|
|
288
|
+
if (ck.endsWith("/" + rawNoPrefix)) {
|
|
289
|
+
const rate = parseOpenRouterInputPer1M(cv);
|
|
290
|
+
if (rate !== null)
|
|
291
|
+
return rate;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch { }
|
|
296
|
+
for (const candidate of [rawKey, key, rawNoPrefix]) {
|
|
297
|
+
const known = MODEL_PRICING_PER_1M[candidate];
|
|
298
|
+
if (known && Number.isFinite(known.input))
|
|
299
|
+
return known.input;
|
|
300
|
+
}
|
|
301
|
+
const turnCost = modelCostPerTurn(model);
|
|
302
|
+
if (Number.isFinite(turnCost) && turnCost > 0) {
|
|
303
|
+
return Math.round(turnCost * 375 * 100) / 100;
|
|
304
|
+
}
|
|
305
|
+
return CACHE_SAVED_PER_1M_INPUT_TOKENS;
|
|
306
|
+
}
|
|
263
307
|
export function roundUsd(v, precision = 6) {
|
|
264
308
|
const n = Number(v ?? 0);
|
|
265
309
|
if (!Number.isFinite(n))
|
|
@@ -284,6 +328,89 @@ export function formatUsd(v) {
|
|
|
284
328
|
// deepseek-chat is free with a DeepSeek API token — priced at $1e-12 (near-zero).
|
|
285
329
|
const FREE_MODEL_TURN_USD = 1e-10;
|
|
286
330
|
const FREE_MODELS = new Set([]);
|
|
331
|
+
// Actual input / output pricing per 1M tokens, sourced from provider API pages
|
|
332
|
+
// and OpenRouter /api/v1/models. Format: USD per 1 million tokens.
|
|
333
|
+
// Entries with provider/ prefix = OpenRouter route; without prefix = native provider.
|
|
334
|
+
const MODEL_PRICING_PER_1M = {
|
|
335
|
+
// ── Anthropic (native + OpenRouter) ─────────────────────
|
|
336
|
+
"anthropic/claude-opus-4-8-fast": { input: 10.0, output: 50.0 },
|
|
337
|
+
"anthropic/claude-opus-4-8": { input: 5.0, output: 25.0 },
|
|
338
|
+
"anthropic/claude-opus-4-7-fast": { input: 30.0, output: 150.0 },
|
|
339
|
+
"anthropic/claude-opus-4-7": { input: 5.0, output: 25.0 },
|
|
340
|
+
"anthropic/claude-opus-4-6-fast": { input: 30.0, output: 150.0 },
|
|
341
|
+
"anthropic/claude-opus-4-6": { input: 5.0, output: 25.0 },
|
|
342
|
+
"anthropic/claude-opus-4-5": { input: 5.0, output: 25.0 },
|
|
343
|
+
"anthropic/claude-opus-4.1": { input: 15.0, output: 75.0 },
|
|
344
|
+
"anthropic/claude-opus-4": { input: 15.0, output: 75.0 },
|
|
345
|
+
"anthropic/claude-sonnet-4-6": { input: 3.0, output: 15.0 },
|
|
346
|
+
"anthropic/claude-sonnet-4-5": { input: 3.0, output: 15.0 },
|
|
347
|
+
"anthropic/claude-sonnet-4": { input: 3.0, output: 15.0 },
|
|
348
|
+
"anthropic/claude-haiku-4-5": { input: 1.0, output: 5.0 },
|
|
349
|
+
"anthropic/claude-3.5-haiku": { input: 0.80, output: 4.0 },
|
|
350
|
+
"anthropic/claude-3-haiku": { input: 0.25, output: 1.25 },
|
|
351
|
+
"haiku": { input: 0.80, output: 4.0 },
|
|
352
|
+
// ── DeepSeek (native — free for chat, paid for pro/flash/r1) ──
|
|
353
|
+
"deepseek-chat": { input: 0, output: 0 }, // native → free
|
|
354
|
+
"deepseek-reasoner": { input: 0.55, output: 2.19 }, // native r1
|
|
355
|
+
// ── DeepSeek (OpenRouter route) ────────────────────────
|
|
356
|
+
"deepseek/deepseek-v4-pro": { input: 0.435, output: 0.870 },
|
|
357
|
+
"deepseek/deepseek-v4-flash": { input: 0.098, output: 0.197 },
|
|
358
|
+
"deepseek/deepseek-chat": { input: 0.229, output: 0.914 },
|
|
359
|
+
"deepseek/deepseek-v3.2": { input: 0.252, output: 0.378 },
|
|
360
|
+
"deepseek/deepseek-v3.2-exp": { input: 0.270, output: 0.410 },
|
|
361
|
+
"deepseek/deepseek-chat-v3.1": { input: 0.210, output: 0.790 },
|
|
362
|
+
"deepseek/deepseek-chat-v3-0324": { input: 0.200, output: 0.770 },
|
|
363
|
+
"deepseek/deepseek-v3.1-terminus": { input: 0.270, output: 0.950 },
|
|
364
|
+
"deepseek/deepseek-r1-0528": { input: 0.500, output: 2.150 },
|
|
365
|
+
"deepseek/deepseek-r1": { input: 0.700, output: 2.500 },
|
|
366
|
+
"deepseek/deepseek-r1-distill-qwen-32b": { input: 0.290, output: 0.290 },
|
|
367
|
+
"deepseek/deepseek-r1-distill-llama-70b": { input: 0.70, output: 0.80 },
|
|
368
|
+
"deepseek/deepseek-v3": { input: 0.252, output: 0.378 },
|
|
369
|
+
"deepseek/haiku": { input: 0.80, output: 4.0 },
|
|
370
|
+
// ── Google Gemini (OpenRouter route) ──────────────────
|
|
371
|
+
"google/gemini-2.5-pro": { input: 1.25, output: 10.0 },
|
|
372
|
+
"google/gemini-2.5-flash": { input: 0.30, output: 2.50 },
|
|
373
|
+
"google/gemini-2.5-flash-lite": { input: 0.10, output: 0.40 },
|
|
374
|
+
"google/gemini-2.0-flash-001": { input: 0.10, output: 0.40 },
|
|
375
|
+
"google/gemini-2.0-flash-lite-001": { input: 0.075, output: 0.30 },
|
|
376
|
+
"google/gemma-4-31b-it": { input: 0.12, output: 0.37 },
|
|
377
|
+
"google/gemma-4-26b-a4b-it": { input: 0.06, output: 0.33 },
|
|
378
|
+
// ── OpenAI (OpenRouter route) ─────────────────────────
|
|
379
|
+
"openai/gpt-5.5-pro": { input: 30.0, output: 180.0 },
|
|
380
|
+
"openai/gpt-5.5": { input: 5.0, output: 30.0 },
|
|
381
|
+
"openai/gpt-5.4-pro": { input: 30.0, output: 180.0 },
|
|
382
|
+
"openai/gpt-5.4": { input: 2.50, output: 15.0 },
|
|
383
|
+
"openai/gpt-5.4-mini": { input: 0.75, output: 4.50 },
|
|
384
|
+
"openai/gpt-5.4-nano": { input: 0.20, output: 1.25 },
|
|
385
|
+
"openai/gpt-5.3-chat": { input: 1.75, output: 14.0 },
|
|
386
|
+
"openai/gpt-5.3-codex": { input: 1.75, output: 14.0 },
|
|
387
|
+
"openai/gpt-5.2": { input: 1.75, output: 14.0 },
|
|
388
|
+
"openai/gpt-5.2-pro": { input: 21.0, output: 168.0 },
|
|
389
|
+
"openai/gpt-5.1": { input: 1.25, output: 10.0 },
|
|
390
|
+
"openai/gpt-5": { input: 1.25, output: 10.0 },
|
|
391
|
+
"openai/gpt-5-mini": { input: 0.25, output: 2.00 },
|
|
392
|
+
"openai/gpt-5-nano": { input: 0.05, output: 0.40 },
|
|
393
|
+
"openai/gpt-4o": { input: 2.50, output: 10.0 },
|
|
394
|
+
"openai/gpt-4o-mini": { input: 0.15, output: 0.60 },
|
|
395
|
+
"openai/gpt-4.1": { input: 2.00, output: 8.00 },
|
|
396
|
+
"openai/gpt-4.1-mini": { input: 0.40, output: 1.60 },
|
|
397
|
+
"openai/gpt-4.1-nano": { input: 0.10, output: 0.40 },
|
|
398
|
+
"openai/o4-mini": { input: 1.10, output: 4.40 },
|
|
399
|
+
"openai/o4-mini-high": { input: 1.10, output: 4.40 },
|
|
400
|
+
"openai/o3-pro": { input: 20.0, output: 80.0 },
|
|
401
|
+
"openai/o3": { input: 2.00, output: 8.00 },
|
|
402
|
+
"openai/o3-mini": { input: 1.10, output: 4.40 },
|
|
403
|
+
"openai/o1-pro": { input: 150.0, output: 600.0 },
|
|
404
|
+
"openai/o1": { input: 15.0, output: 60.0 },
|
|
405
|
+
"openai/gpt-4-turbo": { input: 10.0, output: 30.0 },
|
|
406
|
+
"openai/gpt-4": { input: 30.0, output: 60.0 },
|
|
407
|
+
"openai/gpt-3.5-turbo": { input: 0.50, output: 1.50 },
|
|
408
|
+
// ── Mistral (OpenRouter route) ────────────────────────
|
|
409
|
+
"mistralai/mistral-medium-3-5": { input: 1.50, output: 7.50 },
|
|
410
|
+
"mistralai/mistral-large-2512": { input: 0.50, output: 1.50 },
|
|
411
|
+
"mistralai/mistral-small-2603": { input: 0.15, output: 0.60 },
|
|
412
|
+
"mistralai/mistral-nemo": { input: 0.02, output: 0.03 },
|
|
413
|
+
};
|
|
287
414
|
// Approximate USD per typical ~1 K-token turn (blended input+output).
|
|
288
415
|
// Blend: 700 input + 300 output tokens per turn (line 272-273).
|
|
289
416
|
// Sources: provider API pricing pages, OpenRouter /api/v1/models.
|
|
@@ -518,7 +645,14 @@ export function modelCostPerTurn(model) {
|
|
|
518
645
|
if (key.startsWith(k) && /-\d+$/.test(k) && key.charAt(k.length) === "-")
|
|
519
646
|
return v;
|
|
520
647
|
}
|
|
521
|
-
//
|
|
648
|
+
// Fallback: derive blended turn cost from MODEL_PRICING_PER_1M input/output rates
|
|
649
|
+
for (const candidate of [model, key, bare]) {
|
|
650
|
+
const pricing = MODEL_PRICING_PER_1M[candidate];
|
|
651
|
+
if (pricing && Number.isFinite(pricing.input) && Number.isFinite(pricing.output)) {
|
|
652
|
+
const blended = (pricing.input * 700 + pricing.output * 300) / 1_000_000;
|
|
653
|
+
return Number.isFinite(blended) ? blended : FREE_MODEL_TURN_USD;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
522
656
|
console.error(`[vibeOS] modelCostPerTurn: unknown model '${model}' (normalized: '${key}') — add to MODEL_USD_PER_TURN`);
|
|
523
657
|
return FREE_MODEL_TURN_USD;
|
|
524
658
|
}
|
package/src/lib/trinity-tool.js
CHANGED
|
@@ -178,13 +178,17 @@ export function createTrinityTool(deps) {
|
|
|
178
178
|
return `\u2705 Switched to ${slot} slot (${result.ocModel}). Active now (no restart needed).`;
|
|
179
179
|
}
|
|
180
180
|
if (action === "mode") {
|
|
181
|
-
if (!slot
|
|
182
|
-
return `Provide mode: budget | quality | speed | longrun | auto`;
|
|
181
|
+
if (!slot)
|
|
182
|
+
return `Provide mode: budget | quality | speed | longrun | vibemax | vibeqmax | auto`;
|
|
183
|
+
const modeAlias = { vibemax: "vibemax", vibeqmax: "quality" };
|
|
184
|
+
const resolvedSlot = modeAlias[slot] || slot;
|
|
185
|
+
if (!["budget", "quality", "speed", "longrun", "vibemax", "auto"].includes(resolvedSlot)) {
|
|
186
|
+
return `Provide mode: budget | quality | speed | longrun | vibemax | vibeqmax | auto`;
|
|
183
187
|
}
|
|
184
|
-
const ok = deps.saveOptimizationMode(
|
|
188
|
+
const ok = deps.saveOptimizationMode(resolvedSlot);
|
|
185
189
|
if (!ok)
|
|
186
190
|
return `Failed to write mode`;
|
|
187
|
-
const tierMap = { budget: "cheap", quality: "brain", speed: "medium", longrun: "brain" };
|
|
191
|
+
const tierMap = { budget: "cheap", quality: "brain", speed: "medium", longrun: "brain", vibemax: "medium" };
|
|
188
192
|
const tierSlot = tierMap[slot] || "cheap";
|
|
189
193
|
deps.writeSelection("active_slot", tierSlot);
|
|
190
194
|
deps.writeSelection("onboarding_mode", slot === "quality" || slot === "longrun" ? "strict" : "assist");
|
|
@@ -758,7 +762,7 @@ export function createTrinityTool(deps) {
|
|
|
758
762
|
const probed = {
|
|
759
763
|
brain: models.find(m => m.id === trinity.brain) || { id: trinity.brain, cost: deps._modelCost(trinity.brain), tier: deps._modelTier(trinity.brain) },
|
|
760
764
|
medium: models.find(m => m.id === trinity.medium) || { id: trinity.medium, cost: deps._modelCost(trinity.medium), tier: deps._modelTier(trinity.medium) },
|
|
761
|
-
cheap: models.find(m => m.id === trinity.cheap) || { id: trinity.cheap, cost: deps._modelCost(trinity.cheap), tier: deps._modelTier(trinity.cheap) }
|
|
765
|
+
cheap: models.find(m => m.id === trinity.cheap) || { id: trinity.cheap, cost: deps._modelCost(trinity.cheap), tier: deps._modelTier(trinity.cheap) },
|
|
762
766
|
};
|
|
763
767
|
const failed = [];
|
|
764
768
|
for (const slot of ["brain", "medium", "cheap"]) {
|
package/src/lib/turn-classify.js
CHANGED
|
@@ -24,7 +24,7 @@ export function resolveOptimizationMode(subRegime, stressMultiplier, optimizatio
|
|
|
24
24
|
const normalized = String(optimizationMode || "auto").toLowerCase();
|
|
25
25
|
if (normalized === "auto" || normalized === "")
|
|
26
26
|
return autoSelectMode(subRegime || "INIT", stressMultiplier);
|
|
27
|
-
if (normalized === "balanced" || normalized === "budget" || normalized === "quality" || normalized === "speed" || normalized === "longrun") {
|
|
27
|
+
if (normalized === "balanced" || normalized === "budget" || normalized === "quality" || normalized === "speed" || normalized === "longrun" || normalized === "vibemax") {
|
|
28
28
|
return normalized;
|
|
29
29
|
}
|
|
30
30
|
return "budget";
|
|
@@ -70,7 +70,7 @@ export async function selectOptimizationModeRemote(subRegime, stressMultiplier,
|
|
|
70
70
|
if (client) {
|
|
71
71
|
const res = await client.blackboxSelectMode(subRegime || "INIT", Number(stressMultiplier ?? 0));
|
|
72
72
|
const selected = String(res?.mode || "").toLowerCase();
|
|
73
|
-
if (selected === "balanced" || selected === "budget" || selected === "quality" || selected === "speed" || selected === "longrun") {
|
|
73
|
+
if (selected === "balanced" || selected === "budget" || selected === "quality" || selected === "speed" || selected === "longrun" || selected === "vibemax") {
|
|
74
74
|
return selected;
|
|
75
75
|
}
|
|
76
76
|
}
|
|
@@ -81,13 +81,14 @@ export async function selectOptimizationModeRemote(subRegime, stressMultiplier,
|
|
|
81
81
|
}
|
|
82
82
|
function computeControlVector(_state, _action, _optimizationMode) {
|
|
83
83
|
const mode = resolveOptimizationMode(_state?.sub_regime, _state?.latest_stress_multiplier, _optimizationMode);
|
|
84
|
-
const isStrict = mode === "quality";
|
|
84
|
+
const isStrict = mode === "quality" || mode === "vibemax";
|
|
85
85
|
const isRelaxed = mode === "budget" || mode === "speed";
|
|
86
86
|
const tierBias = mode === "quality" ? "brain"
|
|
87
87
|
: mode === "speed" ? "medium"
|
|
88
|
-
: mode === "
|
|
89
|
-
: mode === "
|
|
90
|
-
: "
|
|
88
|
+
: mode === "vibemax" ? "medium"
|
|
89
|
+
: mode === "longrun" ? "brain"
|
|
90
|
+
: mode === "balanced" ? "auto"
|
|
91
|
+
: "cheap";
|
|
91
92
|
return {
|
|
92
93
|
enforcement_mode: isStrict ? "strict" : isRelaxed ? "relaxed" : "normal",
|
|
93
94
|
enforcement_reason: `[optimize: ${mode}] using safe offline defaults`,
|
|
@@ -121,7 +121,8 @@ export function createMcpServer(deps) {
|
|
|
121
121
|
if (method === "GET" && path === "/status") {
|
|
122
122
|
const state = deps.getState();
|
|
123
123
|
const ok = await probeBackendHealth();
|
|
124
|
-
|
|
124
|
+
const bb = deps.getBlackboxState();
|
|
125
|
+
json(res, 200, { ...state, backend_connected: ok === true, backend_health_url: BACKEND_HEALTH_URL, blackbox: bb ?? null });
|
|
125
126
|
return;
|
|
126
127
|
}
|
|
127
128
|
if (method === "GET" && path === "/savings") {
|
|
@@ -191,6 +192,10 @@ export function createMcpServer(deps) {
|
|
|
191
192
|
json(res, 200, deps.runProject());
|
|
192
193
|
return;
|
|
193
194
|
}
|
|
195
|
+
if (method === "GET" && path === "/blackbox") {
|
|
196
|
+
json(res, 200, deps.getBlackboxState() || {});
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
194
199
|
if (method === "POST" && path === "/trinity") {
|
|
195
200
|
let body;
|
|
196
201
|
try {
|
|
@@ -260,6 +265,32 @@ export function createMcpServer(deps) {
|
|
|
260
265
|
json(res, 200, result);
|
|
261
266
|
return;
|
|
262
267
|
}
|
|
268
|
+
if (method === "POST" && path === "/blackbox/vector") {
|
|
269
|
+
let body;
|
|
270
|
+
try {
|
|
271
|
+
body = await parseBody(req);
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
json(res, 400, { error: "invalid request", status: 400 });
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
deps.saveBlackboxVector(body);
|
|
278
|
+
json(res, 200, { ok: true });
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (method === "POST" && path === "/blackbox/outcome") {
|
|
282
|
+
let body;
|
|
283
|
+
try {
|
|
284
|
+
body = await parseBody(req);
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
json(res, 400, { error: "invalid request", status: 400 });
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
deps.saveBlackboxOutcome(body);
|
|
291
|
+
json(res, 200, { ok: true });
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
263
294
|
if (method === "GET" && path === "/") {
|
|
264
295
|
serveDashboard(res, "/");
|
|
265
296
|
return;
|
|
@@ -193,11 +193,6 @@ export function computeControlVector(state, action, optimizationMode) {
|
|
|
193
193
|
if (effectiveMode === "auto") {
|
|
194
194
|
effectiveMode = autoSelectMode(regime, state.latest_stress_multiplier);
|
|
195
195
|
}
|
|
196
|
-
if (effectiveMode === "vibemax") {
|
|
197
|
-
const baseMode = autoSelectMode(regime, state.latest_stress_multiplier);
|
|
198
|
-
const vibemaxQuality = ["quality", "longrun", "audit"];
|
|
199
|
-
effectiveMode = vibemaxQuality.includes(baseMode) ? "vibemax" : "budget";
|
|
200
|
-
}
|
|
201
196
|
// Apply mode deltas on top of base (only for non-balanced modes)
|
|
202
197
|
const delta = effectiveMode !== "balanced" ? (MODE_DELTAS[effectiveMode] || {}) : {};
|
|
203
198
|
const overridden = {
|