vibeostheog 0.23.40 → 0.23.43
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 +9 -0
- package/package.json +1 -1
- package/src/lib/credit-api.js +4 -0
- package/src/lib/hooks/chat-transform.js +41 -7
- package/src/lib/hooks/footer.js +44 -37
- package/src/lib/hooks/tool-execute.js +47 -25
- package/src/lib/mode-policy.js +12 -4
- package/src/lib/pricing.js +45 -1
- package/src/lib/turn-classify.js +2 -2
- package/src/vibeOS-lib/blackbox/meta-controller.js +8 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## 0.23.43
|
|
2
|
+
- fix: support desktop footer alert chain
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
## 0.23.42
|
|
6
|
+
Fix paused build followup recovery
|
|
7
|
+
Fix stuck startup plan restore
|
|
8
|
+
|
|
9
|
+
|
|
1
10
|
## 0.23.40
|
|
2
11
|
- fix: bypass remote selector for manual modes
|
|
3
12
|
- fix: ML pipeline — autoselect unification, branded mode passthrough, vibeultrax/vibeqmax MODE_DELTAS
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeostheog",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.43",
|
|
4
4
|
"description": "Cost-aware delegation enforcer for OpenCode. Tracks model usage, routes Task subagents to cheaper tiers, surfaces cumulative savings in chat. Includes research audit, reporting framework, project memory, progressive scratchpad decadence, and trinity CLI for brain/medium/cheap slot switching.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"release": "node scripts/release.mjs",
|
package/src/lib/credit-api.js
CHANGED
|
@@ -120,6 +120,10 @@ export function _lazyRefresh() {
|
|
|
120
120
|
if (_creditTimer.unref)
|
|
121
121
|
_creditTimer.unref();
|
|
122
122
|
}
|
|
123
|
+
export async function refreshCreditSnapshot() {
|
|
124
|
+
await _snapshot();
|
|
125
|
+
return loadCredit();
|
|
126
|
+
}
|
|
123
127
|
export function loadCredit() {
|
|
124
128
|
const pct = _cachedPct();
|
|
125
129
|
if (pct !== null)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
-
import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync, rmSync } from "node:fs";
|
|
3
|
-
import { join, basename } from "node:path";
|
|
2
|
+
import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync, rmSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import { join, dirname, basename } from "node:path";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { currentModel, currentProjectFingerprint, currentProjectName, _blackboxEnabled, loadSelection, writeSelection, safeJsonParse, applyDecadence, getSessionScratchpadDir, ensureSessionScratchpadDirs, indexAppend, briefedProjects, getActiveJobForProject, loadTodos, promotedProjectPatterns, detectTechStack, projectFingerprint, SCRATCHPAD_ROOT, 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";
|
|
6
|
+
import { applySlot, TRINITY_CHEAP, TRINITY_MEDIUM, cacheSavePer1MInputTokens, clearWorkspaceFollowupPauseForSession, } from "../pricing.js";
|
|
7
7
|
import { scoreStress, classifyTurnSimple, loadOptimizationMode, selectOptimizationModeRemote, computeControlVector, getBlackboxTracker, loadBlackboxState as loadBlackboxStateFromCtx, saveBlackboxState as saveBlackboxStateToCtx, extractLastUserText, isLikelyOffTopic, fetchBlackboxEnrichment, estimateContextBudget, buildControlHistoryEntry, setBlackboxEnabled, } from "../turn-classify.js";
|
|
8
8
|
import { applyBudgetFirstMode, peekBudgetFirstMode } from "../mode-policy.js";
|
|
9
9
|
import { BRANDED_MODES, RUNTIME_MODES } from "../mode-router.js";
|
|
@@ -19,6 +19,32 @@ const BYTES_PER_TOKEN = 4;
|
|
|
19
19
|
function getVibeOSHome() {
|
|
20
20
|
return process.env.VIBEOS_HOME || join(process.env.HOME || "", ".claude");
|
|
21
21
|
}
|
|
22
|
+
function resolveRestorableOpenCodeAgent(currentSel) {
|
|
23
|
+
const remembered = typeof currentSel?.previous_default_agent === "string" ? currentSel.previous_default_agent.trim() : "";
|
|
24
|
+
if (remembered && remembered !== "plan")
|
|
25
|
+
return remembered;
|
|
26
|
+
try {
|
|
27
|
+
const configDir = dirname(TRINITY_OPENCODE_CONFIG || join(process.env.HOME || "", ".config/opencode/opencode.json"));
|
|
28
|
+
const candidates = readdirSync(configDir)
|
|
29
|
+
.filter((name) => /^opencode\.json\.bak/.test(name))
|
|
30
|
+
.map((name) => {
|
|
31
|
+
const path = join(configDir, name);
|
|
32
|
+
return { path, mtime: statSync(path).mtimeMs };
|
|
33
|
+
})
|
|
34
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
35
|
+
for (const candidate of candidates) {
|
|
36
|
+
try {
|
|
37
|
+
const snapshot = safeJsonParse(readFileSync(candidate.path, "utf-8"));
|
|
38
|
+
const agent = typeof snapshot?.default_agent === "string" ? snapshot.default_agent.trim() : "";
|
|
39
|
+
if (agent && agent !== "plan")
|
|
40
|
+
return agent;
|
|
41
|
+
}
|
|
42
|
+
catch { }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch { }
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
22
48
|
function getOpenCodeHome() {
|
|
23
49
|
return process.env.VIBEOS_OPENCODE_HOME || join(process.env.HOME || "", ".config", "opencode");
|
|
24
50
|
}
|
|
@@ -161,6 +187,12 @@ export function syncControlSettings(cv, options = {}) {
|
|
|
161
187
|
return;
|
|
162
188
|
try {
|
|
163
189
|
const sid = _OC_SID;
|
|
190
|
+
if (!cv.agent_mode) {
|
|
191
|
+
try {
|
|
192
|
+
clearWorkspaceFollowupPauseForSession(sid);
|
|
193
|
+
}
|
|
194
|
+
catch { }
|
|
195
|
+
}
|
|
164
196
|
const persistOptimizationMode = options.persistOptimizationMode !== false;
|
|
165
197
|
const currentSel = loadSelection();
|
|
166
198
|
const userSetMode = loadSessionOptMode(sid + "_opt");
|
|
@@ -248,11 +280,13 @@ export function syncControlSettings(cv, options = {}) {
|
|
|
248
280
|
const OC_CONFIG = TRINITY_OPENCODE_CONFIG || join(getOpenCodeHome(), "opencode.json");
|
|
249
281
|
if (existsSync(OC_CONFIG)) {
|
|
250
282
|
const oc = safeJsonParse(readFileSync(OC_CONFIG, "utf-8"));
|
|
251
|
-
const
|
|
252
|
-
if (oc.default_agent === "plan"
|
|
253
|
-
oc.default_agent =
|
|
283
|
+
const restoreAgent = oc.default_agent === "plan" ? resolveRestorableOpenCodeAgent(currentSel) : null;
|
|
284
|
+
if (restoreAgent && oc.default_agent === "plan") {
|
|
285
|
+
oc.default_agent = restoreAgent;
|
|
254
286
|
writeFileSync(OC_CONFIG, JSON.stringify(oc, null, 2) + "\n");
|
|
255
|
-
|
|
287
|
+
clearWorkspaceFollowupPauseForSession(sid);
|
|
288
|
+
if (currentSel.previous_default_agent)
|
|
289
|
+
writeSelection("previous_default_agent", null);
|
|
256
290
|
}
|
|
257
291
|
}
|
|
258
292
|
}
|
package/src/lib/hooks/footer.js
CHANGED
|
@@ -168,21 +168,27 @@ async function _appendFooter(input, output, directory) {
|
|
|
168
168
|
output?.messageId ||
|
|
169
169
|
output?.message?.id ||
|
|
170
170
|
null;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
171
|
+
if (messageID && textCompletePainted.has(messageID))
|
|
172
|
+
return;
|
|
173
|
+
function _payload(obj) {
|
|
174
|
+
if (obj?.message && typeof obj.message === "object")
|
|
175
|
+
return obj.message;
|
|
176
|
+
return obj;
|
|
177
|
+
}
|
|
178
|
+
function _extractText(obj) {
|
|
179
|
+
const payload = _payload(obj);
|
|
180
|
+
if (typeof payload?.text === "string")
|
|
181
|
+
return payload.text;
|
|
182
|
+
if (typeof payload?.result === "string")
|
|
183
|
+
return payload.result;
|
|
184
|
+
if (typeof payload?.content === "string")
|
|
185
|
+
return payload.content;
|
|
186
|
+
if (Array.isArray(payload?.content))
|
|
187
|
+
return payload.content.filter(p => p?.type === "text").map(p => p.text).filter(Boolean).join("\n");
|
|
188
|
+
if (Array.isArray(payload?.parts))
|
|
189
|
+
return payload.parts.filter(p => p?.type === "text").map(p => p.text).filter(Boolean).join("\n");
|
|
190
|
+
return "";
|
|
191
|
+
}
|
|
186
192
|
const text = _extractText(output);
|
|
187
193
|
if (!text)
|
|
188
194
|
return;
|
|
@@ -330,31 +336,32 @@ async function _appendFooter(input, output, directory) {
|
|
|
330
336
|
}
|
|
331
337
|
}
|
|
332
338
|
catch { }
|
|
339
|
+
}
|
|
340
|
+
function _setFooter(obj, text) {
|
|
341
|
+
const target = _payload(obj);
|
|
342
|
+
if (typeof target?.text === "string")
|
|
343
|
+
target.text = text;
|
|
344
|
+
else if (typeof target?.result === "string")
|
|
345
|
+
target.result = text;
|
|
346
|
+
else if (typeof target?.content === "string")
|
|
347
|
+
target.content = text;
|
|
348
|
+
else if (Array.isArray(target?.content)) {
|
|
349
|
+
const textParts = target.content.filter(p => p?.type === "text");
|
|
350
|
+
if (textParts.length > 0)
|
|
351
|
+
textParts[textParts.length - 1].text = text;
|
|
352
|
+
else
|
|
353
|
+
target.content.push({ type: "text", text });
|
|
333
354
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
obj.result = text;
|
|
339
|
-
else if (typeof obj?.content === "string")
|
|
340
|
-
obj.content = text;
|
|
341
|
-
else if (Array.isArray(obj?.content)) {
|
|
342
|
-
const textParts = obj.content.filter(p => p?.type === "text");
|
|
343
|
-
if (textParts.length > 0)
|
|
344
|
-
textParts[textParts.length - 1].text = text;
|
|
345
|
-
else
|
|
346
|
-
obj.content.push({ type: "text", text });
|
|
347
|
-
}
|
|
348
|
-
else if (Array.isArray(obj?.parts)) {
|
|
349
|
-
const textParts = obj.parts.filter(p => p?.type === "text");
|
|
350
|
-
if (textParts.length > 0)
|
|
351
|
-
textParts[textParts.length - 1].text = text;
|
|
352
|
-
else
|
|
353
|
-
obj.parts.push({ type: "text", text });
|
|
354
|
-
}
|
|
355
|
+
else if (Array.isArray(target?.parts)) {
|
|
356
|
+
const textParts = target.parts.filter(p => p?.type === "text");
|
|
357
|
+
if (textParts.length > 0)
|
|
358
|
+
textParts[textParts.length - 1].text = text;
|
|
355
359
|
else
|
|
356
|
-
|
|
360
|
+
target.parts.push({ type: "text", text });
|
|
357
361
|
}
|
|
362
|
+
else
|
|
363
|
+
target.text = text;
|
|
364
|
+
}
|
|
358
365
|
_setFooter(output, footerText);
|
|
359
366
|
_lastStrippedText = stripped;
|
|
360
367
|
// CLI/pipe mode: stdout is already rendered, write footer to stderr
|
|
@@ -7,6 +7,7 @@ import { classify, modelCostPerTurn, isModelFree, detectContext7, isDocsTarget,
|
|
|
7
7
|
import { latestUserIntent } from "./chat-transform.js";
|
|
8
8
|
import { loadSessionOptMode } from "../selection-manager.js";
|
|
9
9
|
import { loadOptimizationMode } from "../turn-classify.js";
|
|
10
|
+
import { loadCredit, refreshCreditSnapshot } from "../credit-api.js";
|
|
10
11
|
function modeCapitalized(mode) {
|
|
11
12
|
if (!mode)
|
|
12
13
|
return "Budget";
|
|
@@ -14,7 +15,6 @@ function modeCapitalized(mode) {
|
|
|
14
15
|
}
|
|
15
16
|
import { scoreStress, extractFirstWordFromArgs, shouldLogWarn, isUserAskingForTests, resolveEnforcementMode, getLearnedExploratoryWords, noteTaskRoutingLearning, incrementTurnCounter, } from "../turn-classify.js";
|
|
16
17
|
import { saveReport } from "../reporting.js";
|
|
17
|
-
import { loadCredit } from "../credit-api.js";
|
|
18
18
|
import { remoteCall, VIBEOS_API_ENABLED } from "../api-client.js";
|
|
19
19
|
import { getCostAnomalyDetector } from "../cost-anomaly.js";
|
|
20
20
|
import { checkFlowRules } from "../../vibeOS-lib/flow-enforcer.js";
|
|
@@ -336,7 +336,15 @@ export const onToolExecuteBefore = async (input, output) => {
|
|
|
336
336
|
}
|
|
337
337
|
}
|
|
338
338
|
// Credit < 40% + Task: force to cheap slot (mirrors CC's rwh path).
|
|
339
|
-
|
|
339
|
+
let _credit = loadCredit();
|
|
340
|
+
if (_credit < 40) {
|
|
341
|
+
try {
|
|
342
|
+
const refreshed = await refreshCreditSnapshot();
|
|
343
|
+
if (Number.isFinite(refreshed))
|
|
344
|
+
_credit = refreshed;
|
|
345
|
+
}
|
|
346
|
+
catch { }
|
|
347
|
+
}
|
|
340
348
|
if (_credit < 40 && t === "task" && TRINITY_CHEAP && args && typeof args === "object") {
|
|
341
349
|
if (args.model !== TRINITY_CHEAP) {
|
|
342
350
|
args.model = TRINITY_CHEAP;
|
|
@@ -370,6 +378,10 @@ export const onToolExecuteBefore = async (input, output) => {
|
|
|
370
378
|
const apiRoute = await remoteCall("routeModel", [_prompt, currentTier, TRINITY_CHEAP, TRINITY_MEDIUM, LEARNED_EXPLORATORY, stressScore], null);
|
|
371
379
|
if (apiRoute?.target) {
|
|
372
380
|
_target = apiRoute.target;
|
|
381
|
+
if (currentTier === "high" && !_exploratoryTarget && TRINITY_MEDIUM && _target === TRINITY_CHEAP) {
|
|
382
|
+
_target = TRINITY_MEDIUM;
|
|
383
|
+
console.error(`[vibeOS] 🔀 Task floor: preserving medium tier for high-tier brain task`);
|
|
384
|
+
}
|
|
373
385
|
}
|
|
374
386
|
else if (_target === TRINITY_CHEAP && TRINITY_MEDIUM) {
|
|
375
387
|
if (stressScore > 0.5) {
|
|
@@ -738,17 +750,21 @@ export const onToolExecuteAfter = async (input, output) => {
|
|
|
738
750
|
_footerText += ` | $${formatUsd(ltTotal)}`;
|
|
739
751
|
}
|
|
740
752
|
_footerText += ` | ${vibeBrand}${flashIcon} —\n\n`;
|
|
753
|
+
const footerTarget = _payload(output);
|
|
741
754
|
output.title = _footerText.trim();
|
|
742
|
-
if (
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
755
|
+
if (footerTarget !== output && footerTarget && typeof footerTarget === "object") {
|
|
756
|
+
footerTarget.title = _footerText.trim();
|
|
757
|
+
}
|
|
758
|
+
if (typeof footerTarget?.output === "string")
|
|
759
|
+
footerTarget.output = _footerText + footerTarget.output;
|
|
760
|
+
else if (typeof footerTarget?.result === "string")
|
|
761
|
+
footerTarget.result = _footerText + footerTarget.result;
|
|
762
|
+
else if (typeof footerTarget?.text === "string")
|
|
763
|
+
footerTarget.text = _footerText + footerTarget.text;
|
|
764
|
+
else if (typeof footerTarget?.content === "string")
|
|
765
|
+
footerTarget.content = _footerText + footerTarget.content;
|
|
750
766
|
else
|
|
751
|
-
|
|
767
|
+
footerTarget.output = _footerText;
|
|
752
768
|
_autoReportCount = (_autoReportCount || 0) + 1;
|
|
753
769
|
if (_autoReportCount % 5 === 0 && ltTotal > 0) {
|
|
754
770
|
saveReport({
|
|
@@ -835,30 +851,36 @@ export const onToolExecuteAfter = async (input, output) => {
|
|
|
835
851
|
return s;
|
|
836
852
|
});
|
|
837
853
|
}
|
|
854
|
+
function _payload(obj) {
|
|
855
|
+
if (obj?.message && typeof obj.message === "object")
|
|
856
|
+
return obj.message;
|
|
857
|
+
return obj;
|
|
858
|
+
}
|
|
838
859
|
// Inject pending delegation UI note (set in tool.execute.before).
|
|
839
860
|
// This surfaces the warning in the OC chat transcript, not just stderr.
|
|
840
861
|
if (pendingUiNote) {
|
|
862
|
+
const target = _payload(output);
|
|
841
863
|
if (enforcementBlocked) {
|
|
842
864
|
const note = `[vibeOS] ${pendingUiNote}`;
|
|
843
|
-
if (typeof
|
|
844
|
-
|
|
845
|
-
else if (typeof
|
|
846
|
-
|
|
847
|
-
else if (typeof
|
|
848
|
-
|
|
865
|
+
if (typeof target?.result === "string")
|
|
866
|
+
target.result += `\n\n${note}`;
|
|
867
|
+
else if (typeof target?.text === "string")
|
|
868
|
+
target.text += `\n\n${note}`;
|
|
869
|
+
else if (typeof target?.content === "string")
|
|
870
|
+
target.content += `\n\n${note}`;
|
|
849
871
|
else
|
|
850
|
-
|
|
872
|
+
target.result = pendingUiNote;
|
|
851
873
|
}
|
|
852
874
|
else {
|
|
853
875
|
const note = `\n\n${pendingUiNote}`;
|
|
854
|
-
if (typeof
|
|
855
|
-
|
|
856
|
-
else if (typeof
|
|
857
|
-
|
|
858
|
-
else if (typeof
|
|
859
|
-
|
|
876
|
+
if (typeof target?.result === "string")
|
|
877
|
+
target.result += note;
|
|
878
|
+
else if (typeof target?.text === "string")
|
|
879
|
+
target.text += note;
|
|
880
|
+
else if (typeof target?.content === "string")
|
|
881
|
+
target.content += note;
|
|
860
882
|
else
|
|
861
|
-
|
|
883
|
+
target.result = pendingUiNote;
|
|
862
884
|
}
|
|
863
885
|
pendingUiNote = null;
|
|
864
886
|
}
|
package/src/lib/mode-policy.js
CHANGED
|
@@ -2,12 +2,12 @@ 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", "vibeqmax", "vibeultrax"]);
|
|
6
6
|
function normalizeMode(mode) {
|
|
7
7
|
const normalized = String(mode || BASELINE_MODE).toLowerCase();
|
|
8
8
|
if (normalized === "auto" || normalized === "")
|
|
9
9
|
return BASELINE_MODE;
|
|
10
|
-
if (normalized === "budget" || normalized === "quality" || normalized === "speed" || normalized === "longrun" || normalized === "balanced") {
|
|
10
|
+
if (normalized === "budget" || normalized === "quality" || normalized === "speed" || normalized === "longrun" || normalized === "balanced" || normalized === "vibemax" || normalized === "vibeqmax" || normalized === "vibeultrax") {
|
|
11
11
|
return normalized;
|
|
12
12
|
}
|
|
13
13
|
return BASELINE_MODE;
|
|
@@ -84,20 +84,28 @@ function persistSessionPolicy(state, session, policy, mode) {
|
|
|
84
84
|
}
|
|
85
85
|
export function peekBudgetFirstMode(input = {}) {
|
|
86
86
|
const requestedMode = normalizeMode(input.requestedMode);
|
|
87
|
+
if (isManualOverride(requestedMode)) {
|
|
88
|
+
return {
|
|
89
|
+
active: false,
|
|
90
|
+
mode: requestedMode,
|
|
91
|
+
reason: "manual",
|
|
92
|
+
shouldPersistRequestedMode: true,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
87
95
|
const { policy } = loadSessionPolicy();
|
|
88
96
|
if (policy.active && policy.active_mode && normalizeMode(policy.active_mode) !== BASELINE_MODE) {
|
|
89
97
|
return {
|
|
90
98
|
active: true,
|
|
91
99
|
mode: normalizeMode(policy.active_mode),
|
|
92
100
|
reason: policy.reason || "episode",
|
|
93
|
-
shouldPersistRequestedMode:
|
|
101
|
+
shouldPersistRequestedMode: false,
|
|
94
102
|
};
|
|
95
103
|
}
|
|
96
104
|
return {
|
|
97
105
|
active: false,
|
|
98
106
|
mode: BASELINE_MODE,
|
|
99
107
|
reason: "budget",
|
|
100
|
-
shouldPersistRequestedMode:
|
|
108
|
+
shouldPersistRequestedMode: false,
|
|
101
109
|
};
|
|
102
110
|
}
|
|
103
111
|
export function applyBudgetFirstMode(input = {}) {
|
package/src/lib/pricing.js
CHANGED
|
@@ -18,7 +18,7 @@ import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync, sta
|
|
|
18
18
|
import { join, dirname, basename, resolve } from "node:path";
|
|
19
19
|
import { homedir, tmpdir } from "node:os";
|
|
20
20
|
import { createHash } from "node:crypto";
|
|
21
|
-
import { currentModel, currentTier, setCurrentModel, setCurrentTier, safeJsonParse, HIGH_TIER_RE, MID_TIER_RE, loadTierRegexes, _modelLocked } from "./state.js";
|
|
21
|
+
import { currentModel, currentTier, setCurrentModel, setCurrentTier, safeJsonParse, HIGH_TIER_RE, MID_TIER_RE, loadTierRegexes, _modelLocked, getCurrentSessionId } from "./state.js";
|
|
22
22
|
export { HIGH_TIER_RE, MID_TIER_RE, loadTierRegexes };
|
|
23
23
|
const USER_HOME = (() => { try {
|
|
24
24
|
return homedir();
|
|
@@ -996,6 +996,49 @@ function readWorkspaceSessionModel(directory = "") {
|
|
|
996
996
|
}
|
|
997
997
|
return "";
|
|
998
998
|
}
|
|
999
|
+
export function clearWorkspaceFollowupPauseForSession(sessionId = "") {
|
|
1000
|
+
let changed = false;
|
|
1001
|
+
const sid = String(sessionId || "").trim();
|
|
1002
|
+
const latestSid = String(readLatestOpenCodeSessionId() || "").trim();
|
|
1003
|
+
const candidates = [...new Set([sid, latestSid].filter(Boolean))];
|
|
1004
|
+
if (candidates.length === 0)
|
|
1005
|
+
return false;
|
|
1006
|
+
const roots = [getOpenCodeDesktopHome(), getOpenCodeHome()];
|
|
1007
|
+
for (const root of roots) {
|
|
1008
|
+
try {
|
|
1009
|
+
if (!existsSync(root) || !statSync(root).isDirectory())
|
|
1010
|
+
continue;
|
|
1011
|
+
const files = readdirSync(root)
|
|
1012
|
+
.filter((name) => /^opencode\.workspace\..*\.dat$/i.test(name))
|
|
1013
|
+
.map((name) => join(root, name))
|
|
1014
|
+
.sort((a, b) => statSync(b).mtimeMs - statSync(a).mtimeMs);
|
|
1015
|
+
for (const file of files) {
|
|
1016
|
+
try {
|
|
1017
|
+
const outer = safeJsonParse(readFileSync(file, "utf-8"));
|
|
1018
|
+
const followupRaw = outer?.["workspace:followup"];
|
|
1019
|
+
const followup = typeof followupRaw === "string" ? safeJsonParse(followupRaw) : followupRaw;
|
|
1020
|
+
if (!followup || typeof followup !== "object" || !followup.paused)
|
|
1021
|
+
continue;
|
|
1022
|
+
let touched = false;
|
|
1023
|
+
for (const candidate of candidates) {
|
|
1024
|
+
if (followup.paused[candidate]) {
|
|
1025
|
+
delete followup.paused[candidate];
|
|
1026
|
+
touched = true;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
if (!touched)
|
|
1030
|
+
continue;
|
|
1031
|
+
outer["workspace:followup"] = JSON.stringify(followup);
|
|
1032
|
+
writeFileSync(file, JSON.stringify(outer, null, 2) + "\n");
|
|
1033
|
+
changed = true;
|
|
1034
|
+
}
|
|
1035
|
+
catch { }
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
catch { }
|
|
1039
|
+
}
|
|
1040
|
+
return changed;
|
|
1041
|
+
}
|
|
999
1042
|
function readLatestOpenCodeSessionId(directory = "") {
|
|
1000
1043
|
try {
|
|
1001
1044
|
const globalPath = join(getOpenCodeDesktopHome(), "opencode.global.dat");
|
|
@@ -1251,6 +1294,7 @@ export function applySlot(slot, projectDir = "") {
|
|
|
1251
1294
|
oc.model = ocModel;
|
|
1252
1295
|
writeFileSync(ocConfig, JSON.stringify(oc, null, 2) + "\n");
|
|
1253
1296
|
}
|
|
1297
|
+
clearWorkspaceFollowupPauseForSession(getCurrentSessionId());
|
|
1254
1298
|
_refreshModel(process.cwd());
|
|
1255
1299
|
return { ok: true, ocModel };
|
|
1256
1300
|
}
|
package/src/lib/turn-classify.js
CHANGED
|
@@ -36,8 +36,8 @@ export function resolveOptimizationMode(subRegime, stressMultiplier, optimizatio
|
|
|
36
36
|
}
|
|
37
37
|
export function resolveOptimizationSlot(mode) {
|
|
38
38
|
const normalized = String(mode || "budget").toLowerCase();
|
|
39
|
-
return normalized === "speed" ? "medium"
|
|
40
|
-
: normalized === "quality" || normalized === "longrun" ? "brain"
|
|
39
|
+
return normalized === "speed" || normalized === "vibemax" ? "medium"
|
|
40
|
+
: normalized === "quality" || normalized === "longrun" || normalized === "vibeultrax" || normalized === "vibeqmax" ? "brain"
|
|
41
41
|
: "cheap";
|
|
42
42
|
}
|
|
43
43
|
export function bootstrapOptimizationSession() {
|
|
@@ -209,10 +209,14 @@ const MODE_DELTAS = {
|
|
|
209
209
|
};
|
|
210
210
|
export function autoSelectMode(subRegime, stressMultiplier) {
|
|
211
211
|
const regime = String(subRegime || "INIT").toUpperCase();
|
|
212
|
-
if (regime === "AUDIT" || regime === "FORENSIC")
|
|
213
|
-
|
|
214
|
-
if (regime === "
|
|
215
|
-
|
|
212
|
+
if (regime === "AUDIT" || regime === "FORENSIC")
|
|
213
|
+
return regime.toLowerCase();
|
|
214
|
+
if (regime === "LOOPING")
|
|
215
|
+
return "speed";
|
|
216
|
+
if (regime === "CONVERGING" || regime === "CLOSED")
|
|
217
|
+
return "quality";
|
|
218
|
+
if (stressMultiplier && stressMultiplier > QUALITY_STRESS_THRESHOLD)
|
|
219
|
+
return "quality";
|
|
216
220
|
return "budget";
|
|
217
221
|
}
|
|
218
222
|
export function computeControlVector(state, action, optimizationMode) {
|