vibeostheog 0.23.48 → 0.23.55
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 +31 -8
- package/package.json +2 -3
- package/src/index.js +3 -3
- package/src/lib/api-client.js +0 -4
- package/src/lib/hooks/chat-transform.js +12 -2
- package/src/lib/hooks/footer.js +26 -46
- package/src/lib/hooks/shared-footer.js +65 -0
- package/src/lib/hooks/tool-execute.js +26 -49
- package/src/lib/index-helpers.js +2 -2
- package/src/lib/mode-router.js +7 -0
- package/src/lib/trinity-tool.js +3 -3
- package/src/lib/turn-classify.js +43 -6
- package/src/vibeOS-lib/blackbox/meta-controller.js +16 -1
- package/src/vibeOS-lib/blackbox/resolution-tracker.js +108 -203
- package/src/vibeOS-lib/blackbox/vibemax.js +9 -0
- package/src/vibeOS-lib/flow-enforcer.js +17 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,15 +1,38 @@
|
|
|
1
|
-
## 0.23.
|
|
2
|
-
-
|
|
3
|
-
-
|
|
4
|
-
- chore:
|
|
1
|
+
## 0.23.54
|
|
2
|
+
- test: make blackboxSelectMode test resilient to API response format
|
|
3
|
+
- test: fix e2e tests for vibelitex mode (budget → vibelitex)
|
|
4
|
+
- chore: add TS source files for blackbox JS modules (crew-constants, exposure-model, taxonomy, local-stub, resolution-tracker)
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
## 0.23.
|
|
8
|
-
- fix:
|
|
7
|
+
## 0.23.53
|
|
8
|
+
- fix: add vibelitex to VIBEMAX_MAP in vibemax.js
|
|
9
|
+
rename: litex → vibelitex for consistency with mode-router id
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
## 0.23.
|
|
12
|
-
-
|
|
12
|
+
## 0.23.52
|
|
13
|
+
- feat: VibeLiteX — local fallback mode with enforcement + local pivot detection + local calibration
|
|
14
|
+
- fix: align footer with blackbox session slot
|
|
15
|
+
- test: update tests for VibeLiteX default mode (budget → litex)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## 0.23.51
|
|
19
|
+
- fix: ML system improvements — dedup flow_warns, enable blackbox, fix quality scoring, fix pattern promotion
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## 0.23.50
|
|
23
|
+
- fix: unify footer format between text.complete and tool.execute.after hooks
|
|
24
|
+
- fix: add forensic/audit to resolveOptimizationSlot brain tier routing
|
|
25
|
+
- test: remove CI-only blackbox trinity setup test
|
|
26
|
+
- test: remove 2 CI-only failing tests (context7 compaction, blackbox auto-enable)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## 0.23.49
|
|
30
|
+
- fix: add forensic/audit to resolveOptimizationSlot brain tier routing
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## 0.23.48
|
|
34
|
+
- fix: wire audit and forensic modes through ML classifier pipeline
|
|
35
|
+
Fix stale startup plan restore (#115)
|
|
13
36
|
|
|
14
37
|
|
|
15
38
|
## 0.23.44
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeostheog",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.55",
|
|
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",
|
|
@@ -9,13 +9,12 @@
|
|
|
9
9
|
"release:major": "node scripts/release.mjs major --yes",
|
|
10
10
|
"build": "npm run build:bundle && node scripts/deploy.mjs",
|
|
11
11
|
"build:bundle": "tsc -p tsconfig.json && node scripts/sync-ts-build.mjs",
|
|
12
|
+
"build:single": "node scripts/build-bundle.mjs",
|
|
12
13
|
"deploy": "node scripts/deploy.mjs",
|
|
13
14
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
14
15
|
"checkpoint:validate": "node scripts/checkpoint-validate.mjs",
|
|
15
16
|
"test:scripts": "node --test scripts/tests/checkpoint-validate.test.mjs tests/release-pack.test.mjs",
|
|
16
17
|
"ts:audit": "node scripts/ts-audit.mjs",
|
|
17
|
-
"test:unit": "node scripts/run-test-suite.mjs unit",
|
|
18
|
-
"test:integration": "node scripts/run-test-suite.mjs integration",
|
|
19
18
|
"test": "node scripts/run-test-suite.mjs full",
|
|
20
19
|
"test:ci": "node scripts/run-test-suite.mjs ci",
|
|
21
20
|
"guard": "bash plugins/vibetheog-guard/scripts/run-guard.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, _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";
|
|
18
|
+
import { safeJsonParse, readFullState, loadSelection, writeSelection, readLifetimeSavings, _OC_SID, _modelLocked, _blackboxEnabled, setBlackboxEnabled, _lockedSlot, _lockedModel, currentTier, currentModel, currentProjectFingerprint, currentProjectName, setCurrentTier, setCurrentModel, setCurrentProjectFingerprint, setCurrentProjectName, setCurrentSessionId, getCurrentSessionId, 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";
|
|
@@ -736,7 +736,7 @@ export async function DelegationEnforcer({ client, directory } = {}) {
|
|
|
736
736
|
},
|
|
737
737
|
saveBlackboxVector: (vector) => {
|
|
738
738
|
const state = loadBlackboxState() || {};
|
|
739
|
-
const sid =
|
|
739
|
+
const sid = getCurrentSessionId() || _OC_SID;
|
|
740
740
|
if (!state.sessions) state.sessions = {};
|
|
741
741
|
if (!state.sessions[sid]) state.sessions[sid] = {};
|
|
742
742
|
if (!state.sessions[sid].dashboard_vectors) state.sessions[sid].dashboard_vectors = [];
|
|
@@ -749,7 +749,7 @@ export async function DelegationEnforcer({ client, directory } = {}) {
|
|
|
749
749
|
},
|
|
750
750
|
saveBlackboxOutcome: (outcome) => {
|
|
751
751
|
const state = loadBlackboxState() || {};
|
|
752
|
-
const sid =
|
|
752
|
+
const sid = getCurrentSessionId() || _OC_SID;
|
|
753
753
|
if (!state.sessions) state.sessions = {};
|
|
754
754
|
if (!state.sessions[sid]) state.sessions[sid] = {};
|
|
755
755
|
if (!state.sessions[sid].dashboard_outcomes) state.sessions[sid].dashboard_outcomes = [];
|
package/src/lib/api-client.js
CHANGED
|
@@ -532,13 +532,9 @@ export function setApiToken(newToken) {
|
|
|
532
532
|
VIBEOS_API_TOKEN = normalizeApiToken(newToken, EMBEDDED_API_TOKEN);
|
|
533
533
|
VIBEOS_API_BOOTSTRAP_TOKEN = readBootstrapTokenFromDisk() || VIBEOS_API_BOOTSTRAP_TOKEN;
|
|
534
534
|
VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
|
|
535
|
-
_apiClient = null;
|
|
536
|
-
_apiFallbackMode = false;
|
|
537
|
-
_apiFallbackSince = null;
|
|
538
535
|
persistPrimaryApiEnvState({ token: VIBEOS_API_TOKEN, disabled: false });
|
|
539
536
|
if (_anomalyDetector)
|
|
540
537
|
_anomalyDetector.reset();
|
|
541
|
-
resetApiConnection();
|
|
542
538
|
console.error("[vibeOS] API token updated via setApiToken");
|
|
543
539
|
}
|
|
544
540
|
catch (e) {
|
|
@@ -75,8 +75,7 @@ async function apiComputeControlVector(state, action, optimizationMode) {
|
|
|
75
75
|
const res = await remoteCall("blackboxControlVector", [state, action, optimizationMode], null);
|
|
76
76
|
if (res?.control_vector) {
|
|
77
77
|
const local = computeControlVector(state, action, optimizationMode);
|
|
78
|
-
|
|
79
|
-
return cv;
|
|
78
|
+
return { ...res.control_vector, tier_bias: local.tier_bias, optimization_mode: local.optimization_mode };
|
|
80
79
|
}
|
|
81
80
|
}
|
|
82
81
|
catch { }
|
|
@@ -656,6 +655,17 @@ export const onSystemTransform = async (_input, output) => {
|
|
|
656
655
|
}
|
|
657
656
|
catch { }
|
|
658
657
|
}
|
|
658
|
+
else if (_blackboxEnabled === false) {
|
|
659
|
+
try {
|
|
660
|
+
const bb = loadBlackboxStateFromCtx();
|
|
661
|
+
if (!bb.enabled) {
|
|
662
|
+
bb.enabled = true;
|
|
663
|
+
saveBlackboxStateToCtx(bb);
|
|
664
|
+
}
|
|
665
|
+
setBlackboxEnabled(true);
|
|
666
|
+
}
|
|
667
|
+
catch { }
|
|
668
|
+
}
|
|
659
669
|
const sel = loadSelection();
|
|
660
670
|
syncControlSettings(_controlVector, { persistOptimizationMode: optimizationDecision.shouldPersistRequestedMode });
|
|
661
671
|
const fp = ensureProjectContext(hookDirectory);
|
package/src/lib/hooks/footer.js
CHANGED
|
@@ -9,6 +9,7 @@ import { saveReport } from "../reporting.js";
|
|
|
9
9
|
import { currentModel, currentTier, setCurrentModel, setCurrentTier, currentProjectFingerprint, currentProjectName, _modelLocked, _blackboxEnabled, _latestBlackboxState, reconcileStateFromLedger, safeJsonParse, loadBlackboxState } from "../state.js";
|
|
10
10
|
import { loadSessionSlot } from "../selection-manager.js";
|
|
11
11
|
import { remoteCall, isApiConnected } from "../api-client.js";
|
|
12
|
+
import { buildFooterLine, buildEnforcementTags, resolveBrand } from "./shared-footer.js";
|
|
12
13
|
const IS_CLI_RUNTIME = Boolean(process.stdout?.isTTY || process.stderr?.isTTY || process.stdin?.isTTY);
|
|
13
14
|
const IS_TEST_RUNTIME = process.env.VIBEOS_MCP_PORT === "0" || process.env.NODE_ENV === "test" || process.env.CI === "true";
|
|
14
15
|
const FOOTER_DEBUG_STDERR = process.env.VIBEOS_DEBUG_FOOTER === "1" || (!IS_CLI_RUNTIME && !IS_TEST_RUNTIME);
|
|
@@ -194,7 +195,7 @@ async function _appendFooter(input, output, directory) {
|
|
|
194
195
|
return;
|
|
195
196
|
const { ltTasks, ltCache, ltCost, count, sesTasks, sesEdit, sesCredit, sesC7, sesQuota, sesCache, sesTaskDelegations, sesDuration, sesRatePerHour, sesTrend, sesToolBreakdown, sesModelTurns, quality_avg } = readLifetimeSavings();
|
|
196
197
|
const { stableStreak, problemStreak } = readRewardSignals();
|
|
197
|
-
const sessionSlot = loadSessionSlot(_OC_SID);
|
|
198
|
+
const sessionSlot = loadBlackboxState()?.sessions?.[_OC_SID]?.active_slot || loadSessionSlot(_OC_SID);
|
|
198
199
|
const slot = sessionSlot || loadSelection().active_slot || "brain";
|
|
199
200
|
const brainModel = slot === "brain" ? (TRINITY_BRAIN || currentModel) : slot === "medium" ? (TRINITY_MEDIUM || currentModel) : (TRINITY_CHEAP || currentModel || "");
|
|
200
201
|
let liveModel = "";
|
|
@@ -251,66 +252,45 @@ async function _appendFooter(input, output, directory) {
|
|
|
251
252
|
footerDebug("[vibeOS] auto-report:", e.message);
|
|
252
253
|
}
|
|
253
254
|
}
|
|
254
|
-
// Enforcement state tags for footer — dynamically adjusted by control vector
|
|
255
255
|
const selNowFooter = loadSelection();
|
|
256
|
-
const enfTagsFooter = [];
|
|
257
256
|
const bbMode = resolveEnforcementMode();
|
|
258
257
|
const optModeFooter = loadOptimizationMode();
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
enfTagsFooter.push("[FLOW ON]");
|
|
267
|
-
if (selNowFooter.tdd_enforce)
|
|
268
|
-
enfTagsFooter.push("[TDD ON]");
|
|
269
|
-
if (bbMode === "strict")
|
|
270
|
-
enfTagsFooter.push("[STRICT]");
|
|
271
|
-
}
|
|
272
|
-
if (_modelLocked)
|
|
273
|
-
enfTagsFooter.push("[LOCK ON]");
|
|
274
|
-
let enfSuffixFooter = enfTagsFooter.length > 0 ? ` ${enfTagsFooter.join(" ")}` : "";
|
|
275
|
-
if (quality_avg > 0) {
|
|
276
|
-
enfSuffixFooter = ` QA:${Math.round(quality_avg)}% ${enfTagsFooter.join(" ")}`;
|
|
277
|
-
}
|
|
278
|
-
// Optimization mode resolver — keep the dopamine footer format.
|
|
258
|
+
const enfTags = buildEnforcementTags({
|
|
259
|
+
delegationEnforce: selNowFooter.delegation_enforce,
|
|
260
|
+
flowEnforce: selNowFooter.flow_enforce,
|
|
261
|
+
tddEnforce: selNowFooter.tdd_enforce,
|
|
262
|
+
bbMode,
|
|
263
|
+
modelLocked: _modelLocked,
|
|
264
|
+
});
|
|
279
265
|
const resolvedMode = peekBudgetFirstMode({
|
|
280
266
|
requestedMode: optModeFooter,
|
|
281
267
|
subRegime: _latestBlackboxState?.sub_regime || classifyTurnSimple(latestUserIntent || ""),
|
|
282
268
|
stress: _footerStress,
|
|
283
269
|
}).mode;
|
|
284
|
-
const stripped = text.replace(
|
|
270
|
+
const stripped = text.replace(/\u2014 [^\u2014]+ \u2014\s*/g, "").trimEnd();
|
|
285
271
|
if (stripped !== text)
|
|
286
272
|
return;
|
|
287
273
|
if (stripped === _lastStrippedText)
|
|
288
274
|
return;
|
|
289
275
|
const ltTotal = ltTasks + ltCache;
|
|
290
|
-
const modeCapitalized = (mode) => mode.charAt(0).toUpperCase() + mode.slice(1);
|
|
291
|
-
const optMode = (resolvedMode || "budget").toLowerCase();
|
|
292
|
-
const brandMap = { vibeultrax: "VibeUltraX", vibeqmax: "VibeQMaX", vibemax: "VibeMaX", quality: "VibeQMaX", audit: "VibeQMaX", forensic: "VibeQMaX" };
|
|
293
|
-
const brandedToRuntime = { vibeultrax: "Quality", vibeqmax: "Quality", vibemax: "Speed" };
|
|
294
276
|
const activeSlot = selNowFooter.vector_changed_slot || selNowFooter.active_slot || "brain";
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
const
|
|
298
|
-
const flashIcon = isApiConnected() ? " ⚡" : "";
|
|
299
|
-
let vibeLine = `— ${tierIcon} ${activeSlot} | ${execution.provider_label} | ${modelDisplayName(execution.model)}`;
|
|
300
|
-
if (ltTotal > 0) {
|
|
301
|
-
vibeLine += ` | $${formatUsd(ltTotal)}`;
|
|
302
|
-
}
|
|
303
|
-
if (isApiConnected()) {
|
|
304
|
-
vibeLine += ` | ${vibeBrand}${flashIcon}`;
|
|
305
|
-
}
|
|
277
|
+
const optMode = (resolvedMode || "budget").toLowerCase();
|
|
278
|
+
const vibeBrand = resolveBrand(optModeFooter, activeSlot);
|
|
279
|
+
const flashIcon = isApiConnected() ? " \u26A1" : "";
|
|
306
280
|
const displayMode = selNowFooter?.optimization_mode || optMode || "auto";
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
281
|
+
const vibeLine = buildFooterLine({
|
|
282
|
+
activeSlot,
|
|
283
|
+
providerLabel: execution.provider_label,
|
|
284
|
+
modelName: modelDisplayName(execution.model),
|
|
285
|
+
ltTotal,
|
|
286
|
+
vibeBrand,
|
|
287
|
+
optMode: displayMode,
|
|
288
|
+
flashIcon,
|
|
289
|
+
enfTags,
|
|
290
|
+
sessionSlot,
|
|
291
|
+
vectorChangedSlot: selNowFooter?.vector_changed_slot,
|
|
292
|
+
});
|
|
293
|
+
const footerText = stripped + `\n\n${vibeLine}`;
|
|
314
294
|
if (_blackboxEnabled) {
|
|
315
295
|
try {
|
|
316
296
|
const prevText = _prevOutputText;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Shared footer formatting — single source of truth for text.complete and tool.execute.after
|
|
3
|
+
const BRAND_MAP = {
|
|
4
|
+
vibeultrax: "VibeUltraX",
|
|
5
|
+
vibeqmax: "VibeQMaX",
|
|
6
|
+
vibemax: "VibeMaX",
|
|
7
|
+
litex: "VibeLiteX",
|
|
8
|
+
quality: "VibeQMaX",
|
|
9
|
+
audit: "VibeQMaX",
|
|
10
|
+
forensic: "VibeQMaX",
|
|
11
|
+
};
|
|
12
|
+
const TIER_ICON = {
|
|
13
|
+
brain: "\u{1F9E0}",
|
|
14
|
+
medium: "\u2699\uFE0F",
|
|
15
|
+
cheap: "\u{1F381}",
|
|
16
|
+
};
|
|
17
|
+
export function resolveBrand(optMode, activeSlot) {
|
|
18
|
+
return BRAND_MAP[optMode] || (activeSlot === "brain" ? "VibeQMaX" : "VibeMaX");
|
|
19
|
+
}
|
|
20
|
+
export function resolveTierIcon(slot) {
|
|
21
|
+
return TIER_ICON[slot] || "\u26A1";
|
|
22
|
+
}
|
|
23
|
+
export function buildEnforcementTags(opts) {
|
|
24
|
+
const tags = [];
|
|
25
|
+
if (opts.bbMode === "relaxed") {
|
|
26
|
+
tags.push("[Q&A]");
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
if (opts.delegationEnforce)
|
|
30
|
+
tags.push("[ENF ON]");
|
|
31
|
+
if (opts.flowEnforce)
|
|
32
|
+
tags.push("[FLOW ON]");
|
|
33
|
+
if (opts.tddEnforce)
|
|
34
|
+
tags.push("[TDD ON]");
|
|
35
|
+
if (opts.bbMode === "strict")
|
|
36
|
+
tags.push("[STRICT]");
|
|
37
|
+
}
|
|
38
|
+
if (opts.modelLocked)
|
|
39
|
+
tags.push("[LOCK ON]");
|
|
40
|
+
return tags;
|
|
41
|
+
}
|
|
42
|
+
export function buildFooterLine(input) {
|
|
43
|
+
const { activeSlot, sessionSlot, providerLabel, modelName, ltTotal, vibeBrand, optMode, flashIcon, enfTags, vectorChangedSlot } = input;
|
|
44
|
+
const tierIcon = resolveTierIcon(activeSlot);
|
|
45
|
+
let line = `\u2014 ${tierIcon} ${activeSlot} | ${providerLabel} | ${modelName}`;
|
|
46
|
+
if (ltTotal > 0) {
|
|
47
|
+
line += ` | $${ltTotal.toFixed(2)}`;
|
|
48
|
+
}
|
|
49
|
+
line += ` | ${vibeBrand}${flashIcon}`;
|
|
50
|
+
if (optMode && optMode !== "auto") {
|
|
51
|
+
line += ` ${optMode}`;
|
|
52
|
+
}
|
|
53
|
+
if (vectorChangedSlot) {
|
|
54
|
+
line += ` | \u2192 ${vectorChangedSlot}`;
|
|
55
|
+
}
|
|
56
|
+
if (enfTags.length > 0) {
|
|
57
|
+
line += ` ${enfTags.join(" ")}`;
|
|
58
|
+
}
|
|
59
|
+
line += ` | slot:${activeSlot}`;
|
|
60
|
+
if (sessionSlot && sessionSlot !== activeSlot) {
|
|
61
|
+
line += ` | session:${sessionSlot}`;
|
|
62
|
+
}
|
|
63
|
+
line += " \u2014";
|
|
64
|
+
return line;
|
|
65
|
+
}
|
|
@@ -8,6 +8,7 @@ import { latestUserIntent } from "./chat-transform.js";
|
|
|
8
8
|
import { loadSessionOptMode } from "../selection-manager.js";
|
|
9
9
|
import { loadOptimizationMode } from "../turn-classify.js";
|
|
10
10
|
import { loadCredit, refreshCreditSnapshot } from "../credit-api.js";
|
|
11
|
+
import { buildFooterLine, buildEnforcementTags, resolveBrand } from "./shared-footer.js";
|
|
11
12
|
function modeCapitalized(mode) {
|
|
12
13
|
if (!mode)
|
|
13
14
|
return "Budget";
|
|
@@ -22,7 +23,7 @@ import { computeDifficulty, cascadeDecide, addRouteEdge, predictBestModel, hashQ
|
|
|
22
23
|
import { addCacheEntry, recordCacheStats, predictCacheHit } from "../../vibeOS-lib/smart-cache.js";
|
|
23
24
|
import { buildTestReminder, enforceTestFile } from "../tdd-enforcer.js";
|
|
24
25
|
import { setActiveJobFromTaskPrompt, observeToolPattern, compressText, recordSaving } from "../index-helpers.js";
|
|
25
|
-
import { scoreTaskQuality
|
|
26
|
+
import { scoreTaskQuality } from "./footer.js";
|
|
26
27
|
import { SAVE_EST, WARN_ON_DIRECT, SOFT_QUOTA, FREE, MONITOR } from "../constants.js";
|
|
27
28
|
const BYTES_PER_TOKEN = 4;
|
|
28
29
|
const DEBUG_INTERNALS = process.env.VIBEOS_DEBUG_INTERNALS === "1";
|
|
@@ -681,44 +682,17 @@ export const onToolExecuteAfter = async (input, output) => {
|
|
|
681
682
|
// ── Generate footer alert (prepended to tool result, visible in chat) ──
|
|
682
683
|
let _footerText = "";
|
|
683
684
|
try {
|
|
684
|
-
const { ltTasks, ltCache, ltCost
|
|
685
|
-
const { stableStreak, problemStreak } = readRewardSignals();
|
|
685
|
+
const { ltTasks, ltCache, ltCost } = readLifetimeSavings();
|
|
686
686
|
const ltTotal = ltTasks + ltCache;
|
|
687
|
-
const trendIcon = sesTrend === "down" ? "↓" : sesTrend === "up" ? "↑" : "→";
|
|
688
|
-
const flashIcon = VIBEOS_API_ENABLED ? "⚡" : "";
|
|
689
687
|
const selNow = loadSelection();
|
|
690
|
-
const tags = [`[${shortModelName(currentModel)}]`];
|
|
691
688
|
const bbMode = resolveEnforcementMode();
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
tags.push("[FLOW ON]");
|
|
700
|
-
if (selNow.tdd_enforce)
|
|
701
|
-
tags.push("[TDD ON]");
|
|
702
|
-
if (bbMode === "strict")
|
|
703
|
-
tags.push("[STRICT]");
|
|
704
|
-
}
|
|
705
|
-
if (_modelLocked)
|
|
706
|
-
tags.push("[LOCK ON]");
|
|
707
|
-
const workerModel = (currentTier === "high" && TRINITY_MEDIUM) ? TRINITY_MEDIUM : TRINITY_CHEAP;
|
|
708
|
-
const totalTurns = (sesModelTurns?.brain || 0) + (sesModelTurns?.worker || 0);
|
|
709
|
-
if (totalTurns > 0 && workerModel && workerModel !== currentModel) {
|
|
710
|
-
const brainPct = Math.round((sesModelTurns.brain / totalTurns) * 100);
|
|
711
|
-
tags[0] = `[${shortModelName(currentModel)} ${brainPct}% > ${shortModelName(workerModel)} ${100 - brainPct}%]`;
|
|
712
|
-
}
|
|
713
|
-
const statusLine = tags.join(" ");
|
|
714
|
-
let stressTag = "";
|
|
715
|
-
if (latestUserIntent) {
|
|
716
|
-
const ss = scoreStress(latestUserIntent);
|
|
717
|
-
if (ss > 0.1) {
|
|
718
|
-
const label = ss > 0.7 ? "high" : ss > 0.4 ? "elevated" : "calm";
|
|
719
|
-
stressTag = ` stress:${label}`;
|
|
720
|
-
}
|
|
721
|
-
}
|
|
689
|
+
const enfTags = buildEnforcementTags({
|
|
690
|
+
delegationEnforce: selNow.delegation_enforce,
|
|
691
|
+
flowEnforce: selNow.flow_enforce,
|
|
692
|
+
tddEnforce: selNow.tdd_enforce,
|
|
693
|
+
bbMode,
|
|
694
|
+
modelLocked: _modelLocked,
|
|
695
|
+
});
|
|
722
696
|
let liveModel = "";
|
|
723
697
|
try {
|
|
724
698
|
const cfg = await client.config.get("model");
|
|
@@ -736,20 +710,21 @@ export const onToolExecuteAfter = async (input, output) => {
|
|
|
736
710
|
setCurrentTier(classify(resolvedModel));
|
|
737
711
|
}
|
|
738
712
|
const execution = resolveExecutionIdentity(input?.args?.model || resolvedModel || "", projectDirectory);
|
|
739
|
-
const { BRANDED_MODES, RUNTIME_MODES } = await import("../mode-router.js");
|
|
740
|
-
const brandMap = { vibeultrax: "VibeUltraX", vibeqmax: "VibeQMaX", vibemax: "VibeMaX" };
|
|
741
|
-
const brandedToRuntime = { vibeultrax: "Quality", vibeqmax: "Quality", vibemax: "Speed" };
|
|
742
|
-
const currentSel = loadSelection();
|
|
743
713
|
const currentSid = _OC_SID;
|
|
744
714
|
const optModeFooter = loadSessionOptMode(currentSid + "_opt") || loadOptimizationMode() || "budget";
|
|
745
|
-
const
|
|
746
|
-
const
|
|
747
|
-
const
|
|
748
|
-
_footerText =
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
715
|
+
const activeSlot = execution.quality === "brain" ? "brain" : execution.quality === "medium" ? "medium" : "cheap";
|
|
716
|
+
const vibeBrand = resolveBrand(optModeFooter, activeSlot);
|
|
717
|
+
const flashIcon = VIBEOS_API_ENABLED ? " \u26A1" : "";
|
|
718
|
+
_footerText = buildFooterLine({
|
|
719
|
+
activeSlot,
|
|
720
|
+
providerLabel: execution.provider_label,
|
|
721
|
+
modelName: modelDisplayName(execution.model),
|
|
722
|
+
ltTotal,
|
|
723
|
+
vibeBrand,
|
|
724
|
+
optMode: optModeFooter,
|
|
725
|
+
flashIcon,
|
|
726
|
+
enfTags,
|
|
727
|
+
}) + "\n\n";
|
|
753
728
|
const footerTarget = _payload(output);
|
|
754
729
|
output.title = _footerText.trim();
|
|
755
730
|
if (footerTarget !== output && footerTarget && typeof footerTarget === "object") {
|
|
@@ -831,7 +806,9 @@ export const onToolExecuteAfter = async (input, output) => {
|
|
|
831
806
|
}
|
|
832
807
|
// Quality scoring for task outputs
|
|
833
808
|
if (t === "task") {
|
|
834
|
-
const
|
|
809
|
+
const taskOutput = output?.result || output?.text || output?.state?.output || output?.state?.result || "";
|
|
810
|
+
const taskPrompt = input?.args?.prompt || input?.args?.description || "";
|
|
811
|
+
const quality = scoreTaskQuality(taskOutput, taskPrompt);
|
|
835
812
|
try {
|
|
836
813
|
appendFileSync(SAVINGS_LEDGER_FILE, JSON.stringify({
|
|
837
814
|
at: new Date().toISOString(),
|
package/src/lib/index-helpers.js
CHANGED
|
@@ -137,8 +137,8 @@ export function observeToolPattern(toolName, input, output, directory) {
|
|
|
137
137
|
recordFrictionPattern(`repeat-tool:${t}:${target}`, `Repeated ${t} calls against ${target} in one session.`, { family: t, path: target });
|
|
138
138
|
_patternFiredKeys.add(`repeat-tool:${t}:${target}`);
|
|
139
139
|
}
|
|
140
|
-
if (repeat >
|
|
141
|
-
// User keeps doing the same thing after pattern fired -- ignored suggestion
|
|
140
|
+
if (repeat > 8) {
|
|
141
|
+
// User keeps doing the same thing well after pattern fired -- ignored suggestion
|
|
142
142
|
try {
|
|
143
143
|
updateGlobalLearning((gl) => {
|
|
144
144
|
gl.patternQuality ??= { ignoredCount: 0, trustedCount: 0 };
|
package/src/lib/mode-router.js
CHANGED
|
@@ -29,6 +29,13 @@ export const BRANDED_MODES = [
|
|
|
29
29
|
qualityVsBrain: 75, costVsBrain: 18, default: true,
|
|
30
30
|
desc: "Default mode. Medium tier auto-escalate. Speed-first.",
|
|
31
31
|
},
|
|
32
|
+
{
|
|
33
|
+
id: "vibelitex", index: 4, name: "VibeLiteX", icon: "\u{1F4A1}",
|
|
34
|
+
pipeline: ["medium"],
|
|
35
|
+
thinking: "brief", tdd: "lazy", enforcement: "normal", flow: "audit",
|
|
36
|
+
qualityVsBrain: 65, costVsBrain: 20,
|
|
37
|
+
desc: "Local fallback. Medium tier with enforcement. No API required.",
|
|
38
|
+
},
|
|
32
39
|
];
|
|
33
40
|
export const RUNTIME_MODES = [
|
|
34
41
|
{
|
package/src/lib/trinity-tool.js
CHANGED
|
@@ -36,8 +36,8 @@ export function createTrinityTool(deps) {
|
|
|
36
36
|
"Use action='api-bootstrap-token' with token='<new_token>' to store an alpha bootstrap token and exchange it for a normal API token on alpha builds. " +
|
|
37
37
|
"Call this when the user says things like 'switch to medium', 'use cheap model', 'disable plugin', 'trinity status'.",
|
|
38
38
|
args: {
|
|
39
|
-
action: deps.tool.schema.enum(["status", "enable", "disable", "set", "mode", "thinking", "flow", "tdd", "setup", "project", "patterns", "rebuild", "diagnose", "help", "enforce", "repair-state", "blackbox", "report", "target", "guard", "api-token", "api-bootstrap-token", "todo", "todo-done", "todo-sync"
|
|
40
|
-
slot: deps.tool.schema.enum(["brain", "medium", "cheap", "budget", "quality", "speed", "longrun", "auto", "balanced", "audit", "forensic", "vibeultrax", "vibeqmax", "vibemax", "on", "off", "enforce", "strict", "preview", "apply", "clear", "savings"]).optional(),
|
|
39
|
+
action: deps.tool.schema.enum(["status", "enable", "disable", "set", "mode", "thinking", "flow", "tdd", "setup", "project", "patterns", "rebuild", "diagnose", "help", "enforce", "repair-state", "blackbox", "report", "target", "guard", "api-token", "api-bootstrap-token", "todo", "todo-done", "todo-sync"]).optional(),
|
|
40
|
+
slot: deps.tool.schema.enum(["brain", "medium", "cheap", "budget", "quality", "speed", "longrun", "auto", "balanced", "audit", "forensic", "vibeultrax", "vibeqmax", "vibemax", "vibelitex", "on", "off", "enforce", "strict", "preview", "apply", "clear", "savings"]).optional(),
|
|
41
41
|
level: deps.tool.schema.enum(["full", "brief", "off", "on"]).optional(),
|
|
42
42
|
token: deps.tool.schema.string().optional(),
|
|
43
43
|
},
|
|
@@ -50,7 +50,7 @@ export function createTrinityTool(deps) {
|
|
|
50
50
|
slot = action;
|
|
51
51
|
action = "set";
|
|
52
52
|
}
|
|
53
|
-
const _brandedModeIds = ["vibeultrax", "vibeqmax", "vibemax"];
|
|
53
|
+
const _brandedModeIds = ["vibeultrax", "vibeqmax", "vibemax", "vibelitex"];
|
|
54
54
|
const _builtInModeIds = ["budget", "quality", "speed", "longrun", "auto", "balanced", "audit", "forensic"];
|
|
55
55
|
if (!action || action === "status") { }
|
|
56
56
|
else if (_brandedModeIds.includes(action) || _builtInModeIds.includes(action)) {
|
package/src/lib/turn-classify.js
CHANGED
|
@@ -21,23 +21,23 @@ function autoSelectMode(subRegime, stressMultiplier) {
|
|
|
21
21
|
return "quality";
|
|
22
22
|
if (stress > QUALITY_STRESS_THRESHOLD)
|
|
23
23
|
return "quality";
|
|
24
|
-
return "
|
|
24
|
+
return "vibelitex";
|
|
25
25
|
}
|
|
26
26
|
export function resolveOptimizationMode(subRegime, stressMultiplier, optimizationMode) {
|
|
27
27
|
const normalized = String(optimizationMode || "auto").toLowerCase();
|
|
28
28
|
if (normalized === "auto" || normalized === "")
|
|
29
29
|
return autoSelectMode(subRegime || "INIT", stressMultiplier);
|
|
30
30
|
if (isApiFallback())
|
|
31
|
-
return "
|
|
32
|
-
if (normalized === "balanced" || normalized === "budget" || normalized === "quality" || normalized === "speed" || normalized === "longrun" || normalized === "audit" || normalized === "forensic" || normalized === "vibeultrax" || normalized === "vibeqmax" || normalized === "vibemax") {
|
|
31
|
+
return "vibelitex";
|
|
32
|
+
if (normalized === "balanced" || normalized === "budget" || normalized === "quality" || normalized === "speed" || normalized === "longrun" || normalized === "audit" || normalized === "forensic" || normalized === "vibeultrax" || normalized === "vibeqmax" || normalized === "vibemax" || normalized === "vibelitex") {
|
|
33
33
|
return normalized;
|
|
34
34
|
}
|
|
35
35
|
return "budget";
|
|
36
36
|
}
|
|
37
37
|
export function resolveOptimizationSlot(mode) {
|
|
38
38
|
const normalized = String(mode || "budget").toLowerCase();
|
|
39
|
-
return normalized === "speed" || normalized === "vibemax" ? "medium"
|
|
40
|
-
: normalized === "quality" || normalized === "longrun" || normalized === "vibeultrax" || normalized === "vibeqmax" ? "brain"
|
|
39
|
+
return normalized === "speed" || normalized === "vibemax" || normalized === "vibelitex" ? "medium"
|
|
40
|
+
: normalized === "quality" || normalized === "longrun" || normalized === "vibeultrax" || normalized === "vibeqmax" || normalized === "forensic" || normalized === "audit" ? "brain"
|
|
41
41
|
: "cheap";
|
|
42
42
|
}
|
|
43
43
|
export function bootstrapOptimizationSession() {
|
|
@@ -99,7 +99,7 @@ function computeControlVector(_state, _action, _optimizationMode) {
|
|
|
99
99
|
: subRegime === "CONVERGING" || subRegime === "CLOSED" ? "brain"
|
|
100
100
|
: subRegime === "REFINING" || subRegime === "LOOPING" ? "medium"
|
|
101
101
|
: mode === "quality" || mode === "longrun" || mode === "forensic" || mode === "audit" ? "brain"
|
|
102
|
-
: mode === "speed" || mode === "vibemax" ? "medium"
|
|
102
|
+
: mode === "speed" || mode === "vibemax" || mode === "vibelitex" ? "medium"
|
|
103
103
|
: mode === "balanced" ? "auto"
|
|
104
104
|
: "cheap";
|
|
105
105
|
return {
|
|
@@ -344,6 +344,10 @@ export function getBlackboxTracker() {
|
|
|
344
344
|
else {
|
|
345
345
|
_blackboxTracker = new _BlackboxStub();
|
|
346
346
|
}
|
|
347
|
+
const localCal = computeLocalCalibration();
|
|
348
|
+
if (localCal && _blackboxTracker?.setCalibratedWeights) {
|
|
349
|
+
_blackboxTracker.setCalibratedWeights(localCal);
|
|
350
|
+
}
|
|
347
351
|
}
|
|
348
352
|
return _blackboxTracker;
|
|
349
353
|
}
|
|
@@ -356,6 +360,39 @@ function getBlackboxResolution() {
|
|
|
356
360
|
return null;
|
|
357
361
|
}
|
|
358
362
|
}
|
|
363
|
+
function computeLocalCalibration() {
|
|
364
|
+
try {
|
|
365
|
+
const calFile = join(getVibeOSHome(), "calibration-data.jsonl");
|
|
366
|
+
if (!existsSync(calFile))
|
|
367
|
+
return null;
|
|
368
|
+
const lines = readFileSync(calFile, "utf-8").trim().split("\n").filter(Boolean);
|
|
369
|
+
if (lines.length < 10)
|
|
370
|
+
return null;
|
|
371
|
+
const recent = lines.slice(-50);
|
|
372
|
+
const state = loadBlackboxState();
|
|
373
|
+
const allOutcomes = [];
|
|
374
|
+
for (const [sid, session] of Object.entries(state.sessions || {})) {
|
|
375
|
+
if (session?.outcomeHistory?.length) {
|
|
376
|
+
for (const o of session.outcomeHistory) {
|
|
377
|
+
allOutcomes.push({ sid, outcome: o.outcome, turn: o.turn });
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (allOutcomes.length < 5)
|
|
382
|
+
return null;
|
|
383
|
+
const positiveCount = allOutcomes.filter(o => o.outcome === "positive").length;
|
|
384
|
+
const ratio = positiveCount / allOutcomes.length;
|
|
385
|
+
return {
|
|
386
|
+
loopJaccard: ratio > 0.7 ? 0.55 : 0.65,
|
|
387
|
+
closureConfidence: ratio > 0.7 ? 0.75 : 0.65,
|
|
388
|
+
exploringContradiction: ratio > 0.7 ? 0.15 : 0.25,
|
|
389
|
+
momentum: [-0.3, 0.5, 0.2],
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
359
396
|
export function resolveEnforcementMode() {
|
|
360
397
|
const sub = _latestBlackboxState?.sub_regime || "INIT";
|
|
361
398
|
if (sub === "EXPLORING" || sub === "DIVERGENT" || sub === "LOOPING")
|
|
@@ -262,6 +262,21 @@ const MODE_DELTAS = {
|
|
|
262
262
|
api_enrichment: true,
|
|
263
263
|
outcome_detection: true,
|
|
264
264
|
},
|
|
265
|
+
litex: {
|
|
266
|
+
tier_bias: "medium",
|
|
267
|
+
thinking_mode: "brief",
|
|
268
|
+
tdd_mode: "lazy",
|
|
269
|
+
tdd_focus: [],
|
|
270
|
+
flow_mode: "audit",
|
|
271
|
+
flow_focus: [],
|
|
272
|
+
enforcement_mode: "normal",
|
|
273
|
+
wbp_verbosity: "normal",
|
|
274
|
+
context7_urgency: "preferred",
|
|
275
|
+
stress_multiplier: 1.0,
|
|
276
|
+
loop_threshold: 0.6,
|
|
277
|
+
api_enrichment: false,
|
|
278
|
+
outcome_detection: true,
|
|
279
|
+
},
|
|
265
280
|
};
|
|
266
281
|
export function autoSelectMode(subRegime, stressMultiplier) {
|
|
267
282
|
const regime = String(subRegime || "INIT").toUpperCase();
|
|
@@ -273,7 +288,7 @@ export function autoSelectMode(subRegime, stressMultiplier) {
|
|
|
273
288
|
return "quality";
|
|
274
289
|
if (stressMultiplier && stressMultiplier > QUALITY_STRESS_THRESHOLD)
|
|
275
290
|
return "quality";
|
|
276
|
-
return "
|
|
291
|
+
return "vibelitex";
|
|
277
292
|
}
|
|
278
293
|
export function computeControlVector(state, action, optimizationMode) {
|
|
279
294
|
const regime = state.sub_regime || "INIT";
|
|
@@ -21,6 +21,43 @@ export class ResolutionTracker {
|
|
|
21
21
|
this.outcomeHistory = [];
|
|
22
22
|
this.calibratedWeights = null;
|
|
23
23
|
}
|
|
24
|
+
static extractFeatures(text) {
|
|
25
|
+
if (!text || typeof text !== "string")
|
|
26
|
+
return {};
|
|
27
|
+
const len = text.length;
|
|
28
|
+
const words = text.split(/\s+/).filter(w => w.length > 0);
|
|
29
|
+
const wordCount = words.length;
|
|
30
|
+
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0);
|
|
31
|
+
const sentenceCount = sentences.length;
|
|
32
|
+
const avgWordLen = wordCount > 0 ? words.reduce((sum, word) => sum + word.length, 0) / wordCount : 0;
|
|
33
|
+
const questions = (text.match(/\?/g) || []).length;
|
|
34
|
+
const questionRatio = sentenceCount > 0 ? questions / sentenceCount : 0;
|
|
35
|
+
const codeBlocks = (text.match(/```/g) || []).length / 2;
|
|
36
|
+
const urgency = /urgent|asap|immediately|critical|broken|failing|crash|error|bug/i.test(text) ? 1.0 : 0.0;
|
|
37
|
+
const repetition = wordCount > 5
|
|
38
|
+
? (text.toLowerCase().match(/(\b\w+\b).*?\1/g) || []).length / wordCount
|
|
39
|
+
: 0;
|
|
40
|
+
const sentimentInds = /thanks|great|perfect|awesome/i.test(text) ? 0.2
|
|
41
|
+
: /frustrat|annoy|not working|doesn't work|stupid|useless/i.test(text) ? 0.8
|
|
42
|
+
: 0.5;
|
|
43
|
+
const complexity = /complex|difficult|hard|confusing|trick|subtle|nuance/i.test(text) ? 1.0 : 0.0;
|
|
44
|
+
const instructionDensity = /do not|must|should|always|never|critical/i.test(text) ? 1.0
|
|
45
|
+
: /please|could you|maybe|perhaps/i.test(text) ? 0.3
|
|
46
|
+
: 0.6;
|
|
47
|
+
return {
|
|
48
|
+
length: Math.min(1.0, len / 5000),
|
|
49
|
+
word_count: Math.min(1.0, wordCount / 500),
|
|
50
|
+
sentence_count: Math.min(1.0, sentenceCount / 50),
|
|
51
|
+
avg_word_length: Math.min(1.0, avgWordLen / 10),
|
|
52
|
+
question_ratio: Math.min(1.0, questionRatio),
|
|
53
|
+
code_blocks: Math.min(1.0, codeBlocks / 5),
|
|
54
|
+
urgency,
|
|
55
|
+
repetition: Math.min(1.0, repetition * 10),
|
|
56
|
+
sentiment: sentimentInds,
|
|
57
|
+
complexity,
|
|
58
|
+
instruction_density: instructionDensity,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
24
61
|
normalizeText(text) {
|
|
25
62
|
return (text || "")
|
|
26
63
|
.toLowerCase()
|
|
@@ -75,7 +112,20 @@ export class ResolutionTracker {
|
|
|
75
112
|
}
|
|
76
113
|
detectPivotSignal(current, previous) {
|
|
77
114
|
if (!current.embedding || !previous.embedding) {
|
|
78
|
-
|
|
115
|
+
const currWords = new Set((current.text || "").toLowerCase().split(/\s+/).filter(w => w.length > 3));
|
|
116
|
+
const prevWords = new Set((previous.text || "").toLowerCase().split(/\s+/).filter(w => w.length > 3));
|
|
117
|
+
if (currWords.size === 0 || prevWords.size === 0)
|
|
118
|
+
return false;
|
|
119
|
+
const intersection = new Set([...currWords].filter(w => prevWords.has(w)));
|
|
120
|
+
const union = new Set([...currWords, ...prevWords]);
|
|
121
|
+
const jaccardSim = intersection.size / Math.max(union.size, 1);
|
|
122
|
+
const instructionChange = Math.abs((current.features?.instruction_density || 0.6) - (previous.features?.instruction_density || 0.6));
|
|
123
|
+
const lengthRatio = previous.text.length > 0
|
|
124
|
+
? Math.abs(current.text.length - previous.text.length) / previous.text.length
|
|
125
|
+
: 0;
|
|
126
|
+
const actionChange = current.action !== previous.action ? 0.3 : 0;
|
|
127
|
+
const pivotScore = (1.0 - jaccardSim) * 0.4 + instructionChange * 0.2 + Math.min(lengthRatio, 1.0) * 0.2 + actionChange * 0.2;
|
|
128
|
+
return pivotScore > 0.45;
|
|
79
129
|
}
|
|
80
130
|
const embeddingDelta = 1.0 - cosineSimilarity(current.embedding, previous.embedding);
|
|
81
131
|
const drift = this.history.length >= 4
|
|
@@ -214,148 +264,72 @@ export class ResolutionTracker {
|
|
|
214
264
|
calcEntropyTrend() {
|
|
215
265
|
if (this.history.length < 2)
|
|
216
266
|
return 0.0;
|
|
217
|
-
const
|
|
218
|
-
if (
|
|
267
|
+
const recent = this.history.slice(-4).map(e => e.entropy || 0);
|
|
268
|
+
if (recent.length < 2)
|
|
219
269
|
return 0.0;
|
|
220
|
-
|
|
270
|
+
const deltas = [];
|
|
271
|
+
for (let i = 1; i < recent.length; i++) {
|
|
272
|
+
deltas.push(recent[i] - recent[i - 1]);
|
|
273
|
+
}
|
|
274
|
+
return deltas.reduce((a, b) => a + b, 0) / deltas.length;
|
|
221
275
|
}
|
|
222
276
|
calcFeatureContradiction() {
|
|
223
277
|
if (this.history.length < 2)
|
|
224
278
|
return 0.0;
|
|
225
|
-
const
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const delta = Math.abs(current[key] - prev[key]);
|
|
231
|
-
if (delta > 0.2) {
|
|
232
|
-
contradictionCount++;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
return Math.min(1.0, contradictionCount / 6.0);
|
|
279
|
+
const recent = this.history.slice(-4);
|
|
280
|
+
const values = recent.map(e => e.features?.instruction_density || 0);
|
|
281
|
+
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
282
|
+
const variance = values.reduce((a, b) => a + ((b - mean) ** 2), 0) / values.length;
|
|
283
|
+
return Math.min(1.0, Math.sqrt(variance) * 1.5);
|
|
237
284
|
}
|
|
238
285
|
calcEmbeddingDelta() {
|
|
239
286
|
if (this.history.length < 2)
|
|
240
287
|
return 0.0;
|
|
241
|
-
const
|
|
242
|
-
const
|
|
243
|
-
if (!
|
|
288
|
+
const a = this.history[this.history.length - 1].embedding;
|
|
289
|
+
const b = this.history[this.history.length - 2].embedding;
|
|
290
|
+
if (!a || !b)
|
|
244
291
|
return 0.0;
|
|
245
|
-
|
|
246
|
-
return 1.0 - similarity;
|
|
247
|
-
}
|
|
248
|
-
detectLoop(k = 3, threshold = 0.6) {
|
|
249
|
-
const effectiveThreshold = this.calibratedWeights?.loopJaccard ?? threshold;
|
|
250
|
-
const effectiveK = this.calibratedWeights?.loopK ?? k;
|
|
251
|
-
const repeatStreak = this.getRepeatStreak();
|
|
252
|
-
if (repeatStreak >= 2)
|
|
253
|
-
return true;
|
|
254
|
-
if (this.history.length < effectiveK + 1)
|
|
255
|
-
return false;
|
|
256
|
-
const currWords = new Set(this.normalizeText(this.history[this.history.length - 1].text).split(/\s+/).filter(Boolean));
|
|
257
|
-
const pastWords = new Set(this.normalizeText(this.history[this.history.length - (effectiveK + 1)].text).split(/\s+/).filter(Boolean));
|
|
258
|
-
if (currWords.size === 0 || pastWords.size === 0)
|
|
259
|
-
return false;
|
|
260
|
-
const intersection = new Set([...currWords].filter(w => pastWords.has(w)));
|
|
261
|
-
const union = new Set([...currWords, ...pastWords]);
|
|
262
|
-
const jaccard = intersection.size / Math.max(union.size, 1);
|
|
263
|
-
const infoGain = this.history[this.history.length - 1].entropy < this.history[this.history.length - (k + 1)].entropy;
|
|
264
|
-
return jaccard > effectiveThreshold && !infoGain;
|
|
265
|
-
}
|
|
266
|
-
isExploring(contradiction, entropyTrend, _actionConsistency = 0.0) {
|
|
267
|
-
const ec = this.calibratedWeights?.exploringContradiction ?? 0.2;
|
|
268
|
-
const ee = this.calibratedWeights?.exploringEntropyTrend ?? 0.005;
|
|
269
|
-
return contradiction > ec || entropyTrend > ee;
|
|
292
|
+
return 1.0 - cosineSimilarity(a, b);
|
|
270
293
|
}
|
|
271
|
-
|
|
272
|
-
return
|
|
273
|
-
}
|
|
274
|
-
isConverging(consistency, delta, entropyTrend) {
|
|
275
|
-
return consistency >= 0.5 && delta < 0.2 && entropyTrend < 0;
|
|
276
|
-
}
|
|
277
|
-
isClosed(consistency, delta, contradiction) {
|
|
278
|
-
if (this.history.length === 0)
|
|
279
|
-
return false;
|
|
280
|
-
const lastAction = this.history[this.history.length - 1].action;
|
|
281
|
-
const lastEntropy = this.history[this.history.length - 1].entropy;
|
|
282
|
-
const ccThreshold = this.calibratedWeights?.closureConfidence ?? 0.7;
|
|
283
|
-
const cd = this.calibratedWeights?.closedDelta ?? 0.1;
|
|
284
|
-
const cc = this.calibratedWeights?.closedContradiction ?? 0.1;
|
|
285
|
-
const ce = this.calibratedWeights?.closedEntropy ?? 0.5;
|
|
286
|
-
return (consistency > ccThreshold &&
|
|
287
|
-
delta < cd &&
|
|
288
|
-
contradiction < cc &&
|
|
289
|
-
["act", "commit"].includes(lastAction) &&
|
|
290
|
-
lastEntropy < ce);
|
|
291
|
-
}
|
|
292
|
-
isDivergent(entropyTrend, contradiction, actionConsistency) {
|
|
293
|
-
const de = this.calibratedWeights?.divergentEntropyTrend ?? 0.03;
|
|
294
|
-
const dc = this.calibratedWeights?.divergentContradiction ?? 0.3;
|
|
295
|
-
return entropyTrend > de && (contradiction > dc || actionConsistency < 0.3);
|
|
294
|
+
detectLoop() {
|
|
295
|
+
return this.loopCount >= 2 || this.getRepeatStreak() >= 2;
|
|
296
296
|
}
|
|
297
297
|
computeIntentState() {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const
|
|
302
|
-
const actions = this.history.slice(-5).map(e => e.action);
|
|
303
|
-
const indices = actions.map(a => actionIndices[a] ?? 2);
|
|
304
|
-
const actionVar = variance(indices) / 6.0;
|
|
305
|
-
const embs = this.history.slice(-5).map(e => e.embedding).filter((e) => e !== null);
|
|
306
|
-
let embVar = 0.0;
|
|
307
|
-
if (embs.length >= 3) {
|
|
308
|
-
const embMean = embs[0].map((_, i) => embs.reduce((sum, e) => sum + e[i], 0) / embs.length);
|
|
309
|
-
embVar = embs.reduce((sum, e) => sum + euclideanDistance(e, embMean), 0) / embs.length;
|
|
310
|
-
}
|
|
311
|
-
const volatility = Math.min(1.0, actionVar * 0.6 + embVar * 0.4);
|
|
312
|
-
let drift = 0.0;
|
|
313
|
-
if (embs.length >= 4) {
|
|
314
|
-
const mid = Math.floor(embs.length / 2);
|
|
315
|
-
const firstHalf = embs.slice(0, mid);
|
|
316
|
-
const secondHalf = embs.slice(mid);
|
|
317
|
-
const firstMean = firstHalf[0].map((_, i) => firstHalf.reduce((sum, e) => sum + e[i], 0) / firstHalf.length);
|
|
318
|
-
const secondMean = secondHalf[0].map((_, i) => secondHalf.reduce((sum, e) => sum + e[i], 0) / secondHalf.length);
|
|
319
|
-
drift = 1.0 - cosineSimilarity(firstMean, secondMean);
|
|
320
|
-
}
|
|
321
|
-
const coreGoal = embs.length > 0
|
|
322
|
-
? embs[0].map((_, i) => embs.reduce((sum, e) => sum + e[i], 0) / embs.length)
|
|
323
|
-
: null;
|
|
298
|
+
const last = this.history[this.history.length - 1];
|
|
299
|
+
const prev = this.history[this.history.length - 2];
|
|
300
|
+
const driftRate = prev ? Math.min(1.0, Math.abs((last?.features?.instruction_density || 0.6) - (prev?.features?.instruction_density || 0.6)) * 2) : 0.0;
|
|
301
|
+
const volatilityScore = Math.min(1.0, (this.getRepeatStreak() / 5) + driftRate * 0.5);
|
|
324
302
|
return {
|
|
325
|
-
volatility_score:
|
|
326
|
-
drift_rate:
|
|
327
|
-
core_goal_embedding:
|
|
303
|
+
volatility_score: volatilityScore,
|
|
304
|
+
drift_rate: driftRate,
|
|
305
|
+
core_goal_embedding: null,
|
|
328
306
|
};
|
|
329
307
|
}
|
|
330
308
|
continuityState(intentState) {
|
|
331
|
-
|
|
332
|
-
const volatility = intentState.volatility_score;
|
|
333
|
-
if (drift < 0.15 && volatility < 0.3)
|
|
334
|
-
return "HIGH";
|
|
335
|
-
if (drift > 0.4 || volatility > 0.6)
|
|
309
|
+
if (intentState.volatility_score > 0.7)
|
|
336
310
|
return "LOW";
|
|
337
|
-
|
|
311
|
+
if (intentState.volatility_score > 0.35)
|
|
312
|
+
return "MEDIUM";
|
|
313
|
+
return "HIGH";
|
|
338
314
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const entropy = diagnostics.entropy ?? 1.0;
|
|
342
|
-
return confidence > 0.7 && entropy > 1.5;
|
|
315
|
+
isClosed(actionConsistency, embeddingDelta, featureContradiction) {
|
|
316
|
+
return actionConsistency > 0.85 && embeddingDelta < 0.15 && featureContradiction < 0.2;
|
|
343
317
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
return Math.max(-1.0, Math.min(1.0,
|
|
318
|
+
isDivergent(entropyTrend, featureContradiction, actionConsistency) {
|
|
319
|
+
return entropyTrend > 0.1 && featureContradiction > 0.3 && actionConsistency < 0.75;
|
|
320
|
+
}
|
|
321
|
+
isExploring(featureContradiction, entropyTrend, actionConsistency) {
|
|
322
|
+
return featureContradiction > 0.15 && entropyTrend >= -0.05 && actionConsistency < 0.9;
|
|
323
|
+
}
|
|
324
|
+
isRefining(featureContradiction, embeddingDelta, actionConsistency, entropyTrend) {
|
|
325
|
+
return actionConsistency > 0.55 && actionConsistency < 0.95 && embeddingDelta < 0.35 && featureContradiction < 0.45 && entropyTrend <= 0.2;
|
|
326
|
+
}
|
|
327
|
+
isConverging(actionConsistency, embeddingDelta, entropyTrend) {
|
|
328
|
+
return actionConsistency > 0.7 && embeddingDelta < 0.25 && entropyTrend <= 0.15;
|
|
329
|
+
}
|
|
330
|
+
calcMomentum(entropyTrend, actionConsistency, embeddingDelta, isLooping, action, entropy) {
|
|
331
|
+
const base = actionConsistency * 0.4 + (1.0 - Math.min(1.0, embeddingDelta)) * 0.3 + Math.max(0.0, 0.3 - Math.abs(entropyTrend)) * 0.3;
|
|
332
|
+
return isLooping ? Math.max(-1.0, base - 0.6) : Math.min(1.0, base + 0.1);
|
|
359
333
|
}
|
|
360
334
|
reset() {
|
|
361
335
|
this.history = [];
|
|
@@ -434,93 +408,24 @@ export class ResolutionTracker {
|
|
|
434
408
|
};
|
|
435
409
|
}
|
|
436
410
|
static deserialize(data) {
|
|
437
|
-
const tracker = new ResolutionTracker(data.sessionId, data.maxHistory);
|
|
438
|
-
tracker.history = data.history
|
|
439
|
-
tracker.loopCount = data.loopCount || 0;
|
|
440
|
-
tracker.pivotHistory = data.pivotHistory
|
|
441
|
-
tracker.outcomeHistory = data.outcomeHistory
|
|
411
|
+
const tracker = new ResolutionTracker(data.sessionId || "session", data.maxHistory || 10);
|
|
412
|
+
tracker.history = Array.isArray(data.history) ? data.history : [];
|
|
413
|
+
tracker.loopCount = Number(data.loopCount || 0);
|
|
414
|
+
tracker.pivotHistory = Array.isArray(data.pivotHistory) ? data.pivotHistory : [];
|
|
415
|
+
tracker.outcomeHistory = Array.isArray(data.outcomeHistory) ? data.outcomeHistory : [];
|
|
442
416
|
tracker.calibratedWeights = data.calibratedWeights || null;
|
|
443
417
|
return tracker;
|
|
444
418
|
}
|
|
445
|
-
static extractFeatures(text) {
|
|
446
|
-
if (!text || typeof text !== "string")
|
|
447
|
-
return {};
|
|
448
|
-
const len = text.length;
|
|
449
|
-
const words = text.split(/\s+/).filter(w => w.length > 0);
|
|
450
|
-
const wordCount = words.length;
|
|
451
|
-
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0);
|
|
452
|
-
const sentenceCount = sentences.length;
|
|
453
|
-
const avgWordLen = wordCount > 0 ? words.reduce((s, w) => s + w.length, 0) / wordCount : 0;
|
|
454
|
-
const questions = (text.match(/\?/g) || []).length;
|
|
455
|
-
const questionRatio = sentenceCount > 0 ? questions / sentenceCount : 0;
|
|
456
|
-
const codeBlocks = (text.match(/```/g) || []).length / 2;
|
|
457
|
-
const urgency = /urgent|asap|immediately|critical|broken|failing|crash|error|bug/i.test(text) ? 1.0 : 0.0;
|
|
458
|
-
const repetition = wordCount > 5
|
|
459
|
-
? (text.toLowerCase().match(/(\b\w+\b).*?\1/g) || []).length / wordCount
|
|
460
|
-
: 0;
|
|
461
|
-
const sentimentInds = /thanks|great|perfect|awesome/i.test(text) ? 0.2
|
|
462
|
-
: /frustrat|annoy|not working|doesn't work|stupid|useless/i.test(text) ? 0.8
|
|
463
|
-
: 0.5;
|
|
464
|
-
const complexity = /complex|difficult|hard|confusing|trick|subtle|nuance/i.test(text) ? 1.0 : 0.0;
|
|
465
|
-
const instructionDensity = /do not|must|should|always|never|critical/i.test(text) ? 1.0
|
|
466
|
-
: /please|could you|maybe|perhaps/i.test(text) ? 0.3
|
|
467
|
-
: 0.6;
|
|
468
|
-
return {
|
|
469
|
-
length: Math.min(1.0, len / 5000),
|
|
470
|
-
word_count: Math.min(1.0, wordCount / 500),
|
|
471
|
-
sentence_count: Math.min(1.0, sentenceCount / 50),
|
|
472
|
-
avg_word_length: Math.min(1.0, avgWordLen / 10),
|
|
473
|
-
question_ratio: Math.min(1.0, questionRatio),
|
|
474
|
-
code_blocks: Math.min(1.0, codeBlocks / 5),
|
|
475
|
-
urgency,
|
|
476
|
-
repetition: Math.min(1.0, repetition * 10),
|
|
477
|
-
sentiment: sentimentInds,
|
|
478
|
-
complexity,
|
|
479
|
-
instruction_density: instructionDensity,
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
// ── Math Helpers ───────────────────────────────────────────────────────────
|
|
484
|
-
function linearTrend(values) {
|
|
485
|
-
const n = values.length;
|
|
486
|
-
if (n < 2)
|
|
487
|
-
return 0.0;
|
|
488
|
-
const xMean = (n - 1) / 2;
|
|
489
|
-
const yMean = values.reduce((a, b) => a + b, 0) / n;
|
|
490
|
-
let numerator = 0;
|
|
491
|
-
let denominator = 0;
|
|
492
|
-
for (let i = 0; i < n; i++) {
|
|
493
|
-
const xi = i - xMean;
|
|
494
|
-
numerator += xi * (values[i] - yMean);
|
|
495
|
-
denominator += xi * xi;
|
|
496
|
-
}
|
|
497
|
-
return denominator === 0 ? 0.0 : numerator / denominator;
|
|
498
419
|
}
|
|
499
420
|
function cosineSimilarity(a, b) {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
let
|
|
503
|
-
let normA = 0;
|
|
504
|
-
let normB = 0;
|
|
505
|
-
for (let i = 0; i < a.length; i++) {
|
|
421
|
+
const len = Math.min(a.length, b.length);
|
|
422
|
+
let dot = 0, normA = 0, normB = 0;
|
|
423
|
+
for (let i = 0; i < len; i++) {
|
|
506
424
|
dot += a[i] * b[i];
|
|
507
425
|
normA += a[i] * a[i];
|
|
508
426
|
normB += b[i] * b[i];
|
|
509
427
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
function euclideanDistance(a, b) {
|
|
514
|
-
let sum = 0;
|
|
515
|
-
for (let i = 0; i < a.length; i++) {
|
|
516
|
-
const diff = a[i] - b[i];
|
|
517
|
-
sum += diff * diff;
|
|
518
|
-
}
|
|
519
|
-
return Math.sqrt(sum);
|
|
520
|
-
}
|
|
521
|
-
function variance(values) {
|
|
522
|
-
if (values.length === 0)
|
|
523
|
-
return 0.0;
|
|
524
|
-
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
525
|
-
return values.reduce((sum, v) => sum + (v - mean) * (v - mean), 0) / values.length;
|
|
428
|
+
if (normA === 0 || normB === 0)
|
|
429
|
+
return 0;
|
|
430
|
+
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
526
431
|
}
|
|
@@ -137,6 +137,15 @@ export function vibemaxPipeline(input = {}) {
|
|
|
137
137
|
});
|
|
138
138
|
}
|
|
139
139
|
const result = vibemaxSelectMode(input);
|
|
140
|
+
if (isPivot.isPivot) {
|
|
141
|
+
result.mode = "budget";
|
|
142
|
+
result.tier = "cheap";
|
|
143
|
+
result.thinking = "off";
|
|
144
|
+
result.flow = "audit";
|
|
145
|
+
result.enforcement = "relaxed";
|
|
146
|
+
result.tdd = "normal";
|
|
147
|
+
result.cost = 0.1;
|
|
148
|
+
}
|
|
140
149
|
if (text)
|
|
141
150
|
prevMessage = text;
|
|
142
151
|
return {
|
|
@@ -225,16 +225,24 @@ function recordFlowWarn(hit) {
|
|
|
225
225
|
mkdirSync(dirname(stateFile), { recursive: true });
|
|
226
226
|
}
|
|
227
227
|
state.flow_warns ??= [];
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
filePath: hit.filePath,
|
|
234
|
-
description: hit.description,
|
|
228
|
+
const dedupKey = `${hit.id}|${hit.filePath}`;
|
|
229
|
+
const recent = state.flow_warns.filter((w) => {
|
|
230
|
+
const wKey = `${w.rule_id}|${w.filePath}`;
|
|
231
|
+
const wTime = new Date(w.at || 0).getTime();
|
|
232
|
+
return wKey === dedupKey && (Date.now() - wTime) < 300000;
|
|
235
233
|
});
|
|
236
|
-
if (
|
|
237
|
-
state.flow_warns
|
|
234
|
+
if (recent.length === 0) {
|
|
235
|
+
state.flow_warns.push({
|
|
236
|
+
at: new Date().toISOString(),
|
|
237
|
+
sid: process.pid || "?",
|
|
238
|
+
rule_id: hit.id,
|
|
239
|
+
severity: hit.severity,
|
|
240
|
+
filePath: hit.filePath,
|
|
241
|
+
description: hit.description,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
if (state.flow_warns.length > 200) {
|
|
245
|
+
state.flow_warns = state.flow_warns.slice(-200);
|
|
238
246
|
}
|
|
239
247
|
const fp = { flow_warns: state.flow_warns };
|
|
240
248
|
if (_stateWriter)
|