vibeostheog 0.20.1 → 0.20.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 0.20.4
2
+ - fix: add alpha token invalidate switch
3
+ - fix: prefer valid api tokens over placeholder env
4
+ - fix: gate footer stderr by runtime
5
+ - fix: quiet footer stderr noise
6
+ Merge pull request #70 from DrunkkToys/codex/alpha-token-kill-switch
7
+
8
+
9
+ ## 0.20.3
10
+ - fix: embed valid alpha token fallback
11
+ Merge pull request #69 from DrunkkToys/codex/alpha-token-release
12
+
13
+
14
+ ## 0.20.2
15
+ - fix: restore embedded api token fallback
16
+ Merge pull request #68 from DrunkkToys/codex/embed-api-token
17
+
18
+
1
19
  ## 0.20.1
2
20
  - docs: rename blackbox to VibeBoX and document local fallback features
3
21
 
package/README.md CHANGED
@@ -30,6 +30,7 @@ Without a token, vibeOS keeps running in local-only mode with bundled algorithms
30
30
  ### Requires Remote API
31
31
 
32
32
  - Bootstrap token exchange (required for initial API setup)
33
+ - Alpha seat issuance is currently uncapped in the admin tooling
33
34
  - Advanced VibeBoX decision engine with full session history tracking
34
35
  - Dynamic per-prompt delegation decisions (local fallback uses a safe "block all writes on high tier" default)
35
36
  - Learned subagent routing patterns across projects (local fallback uses a static exploratory keyword list)
@@ -111,7 +112,7 @@ The most common controls are:
111
112
  - `trinity repair-state preview|apply` - fix state fingerprint collisions
112
113
  - `trinity VibeBoX on|off|status|reset` - control the decision engine
113
114
  - `trinity guard` - refresh AGENTS.md and README.md checks
114
- - `trinity api-token <token>` - update the remote API token
115
+ - `trinity api-token <token|invalidate>` - update the remote API token, or invalidate the current alpha token and disable remote API
115
116
  - `trinity api-bootstrap-token <token>` - store an alpha bootstrap token and exchange it for a normal API token on alpha builds
116
117
 
117
118
  Additional reporting commands:
@@ -138,7 +139,8 @@ Savings are persisted in `~/.claude/delegation-state.json`.
138
139
  | Variable | Default | Purpose |
139
140
  |---|---|---|
140
141
  | `VIBEOS_API_URL` | `https://api.vibetheog.com` | Remote API server URL |
141
- | `VIBEOS_API_TOKEN` | unset | vos_ffa6c7dacb244a03 |
142
+ | `VIBEOS_API_TOKEN` | unset | vos_8d73804b13bb46711b9a47f036dba7b4d026fd9583d96960e663716e62815a69 |
143
+ | `VIBEOS_API_DISABLED` | `false` | Set to `true` to invalidate the embedded alpha token and keep remote API off until re-enabled |
142
144
  | `VIBEOS_API_BOOTSTRAP_TOKEN` | unset | Alpha bootstrap token for initial auth exchange |
143
145
  | `VIBEOS_API_ENABLED` | `true` | Set to `false` for local-only mode |
144
146
  | `CLAUDE_CREDIT_PERCENT` | `100` | Credit override |
@@ -154,7 +156,7 @@ Without a token, vibeOS keeps running in local-only mode with bundled algorithms
154
156
  - If the model will not switch, run `trinity rebuild` and then `trinity set brain|medium|cheap`.
155
157
  - If writes or edits are blocked, that is usually delegation enforcement working as intended on the brain tier.
156
158
  - If the footer is missing, check that the plugin is enabled and that the current OpenCode session is receiving assistant completions.
157
- - If the remote API is down or the token is invalid, use `trinity api-token <token>` or `trinity api-bootstrap-token <token>` on alpha builds, or rely on local-only mode.
159
+ - If the remote API is down or the token is invalid, use `trinity api-token <token>` or `trinity api-bootstrap-token <token>` on alpha builds. Use `trinity api-token invalidate` when you want to intentionally revoke the alpha token and stay local-only.
158
160
  - If the dashboard does not load, rebuild the plugin with `npm run build` and restart OpenCode.
159
161
  - If state or config looks inconsistent, run `trinity diagnose` and `trinity guard`.
160
162
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeostheog",
3
- "version": "0.20.1",
3
+ "version": "0.20.4",
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",
@@ -5,6 +5,9 @@ import { fileURLToPath } from "node:url";
5
5
  import { homedir } from "node:os";
6
6
  import { isApiConnected as isRuntimeApiConnected, markApiConnected, markApiDisconnected, resetApiConnection } from "./runtime-state.js";
7
7
  const DEFAULT_API_URL = "https://api.vibetheog.com";
8
+ const EMBEDDED_API_TOKEN = "vos_8d73804b13bb46711b9a47f036dba7b4d026fd9583d96960e663716e62815a69";
9
+ const API_TOKEN_RE = /^vos_[a-f0-9]{64}$/i;
10
+ const API_DISABLED_RE = /^(1|true|yes|on)$/i;
8
11
  const REQUEST_TIMEOUT = 10000;
9
12
  const MAX_RETRIES = 3;
10
13
  const BASE_RETRY_DELAY = 1000;
@@ -33,6 +36,59 @@ export class VibeOSNetworkError extends Error {
33
36
  this.name = "VibeOSNetworkError";
34
37
  }
35
38
  }
39
+ function normalizeApiToken(token, fallback = "") {
40
+ const clean = String(token || "").trim();
41
+ return API_TOKEN_RE.test(clean) ? clean : fallback;
42
+ }
43
+ function isTruthyFlag(value) {
44
+ return API_DISABLED_RE.test(String(value || "").trim());
45
+ }
46
+ function editEnvLine(content, key, value) {
47
+ const lines = String(content || "").split(/\r?\n/);
48
+ const next = [];
49
+ let found = false;
50
+ for (const line of lines) {
51
+ if (line.startsWith(`${key}=`)) {
52
+ found = true;
53
+ if (value !== null)
54
+ next.push(`${key}=${value}`);
55
+ continue;
56
+ }
57
+ next.push(line);
58
+ }
59
+ if (!found && value !== null)
60
+ next.push(`${key}=${value}`);
61
+ while (next.length > 0 && next[next.length - 1] === "")
62
+ next.pop();
63
+ return next.join("\n") + "\n";
64
+ }
65
+ function persistPrimaryApiEnvState(next) {
66
+ const primaryPath = _envPaths[0] + "/.env.production";
67
+ try {
68
+ let envContent = existsSync(primaryPath) ? readFileSync(primaryPath, "utf8") : "";
69
+ if (next.disabled !== undefined) {
70
+ envContent = editEnvLine(envContent, "VIBEOS_API_DISABLED", next.disabled ? "true" : null);
71
+ }
72
+ if (next.token !== undefined) {
73
+ envContent = editEnvLine(envContent, "VIBEOS_API_TOKEN", next.token ? String(next.token).trim() : null);
74
+ }
75
+ if (!envContent.trim()) {
76
+ try {
77
+ if (existsSync(primaryPath))
78
+ rmSync(primaryPath, { force: true });
79
+ }
80
+ catch { }
81
+ return;
82
+ }
83
+ const parentDir = _envPaths[0];
84
+ if (!existsSync(parentDir))
85
+ mkdirSync(parentDir, { recursive: true });
86
+ writeFileSync(primaryPath, envContent.endsWith("\n") ? envContent : envContent + "\n", "utf8");
87
+ }
88
+ catch (diskErr) {
89
+ console.error("[vibeOS] Failed to persist API env state:", diskErr.message);
90
+ }
91
+ }
36
92
  export class VibeOSApiClient {
37
93
  baseUrl;
38
94
  apiToken;
@@ -42,7 +98,7 @@ export class VibeOSApiClient {
42
98
  fallbackStubs;
43
99
  constructor(options = {}) {
44
100
  this.baseUrl = options.baseUrl || process.env.VIBEOS_API_URL || DEFAULT_API_URL;
45
- this.apiToken = options.apiToken || process.env.VIBEOS_API_TOKEN || null;
101
+ this.apiToken = normalizeApiToken(options.apiToken || process.env.VIBEOS_API_TOKEN || "", "") || null;
46
102
  this.masterKey = options.masterKey || process.env.VIBEOS_API_MASTER_KEY || null;
47
103
  this.timeout = options.timeout || REQUEST_TIMEOUT;
48
104
  this.fallbackMode = false;
@@ -305,19 +361,38 @@ export const VIBEOS_API_URL = process.env.VIBEOS_API_URL || "https://api.vibethe
305
361
  const _apiDir = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
306
362
  const _envPaths = [homedir() + "/.claude", _apiDir, process.cwd(), homedir()];
307
363
  const _bootstrapEnvPath = _envPaths[0] + "/.env.alpha";
364
+ function readApiDisabledFromDisk() {
365
+ for (const dir of _envPaths) {
366
+ try {
367
+ const env = readFileSync(dir + "/.env.production", "utf8");
368
+ const m = env.match(/^VIBEOS_API_DISABLED=(.+)$/m);
369
+ if (m && isTruthyFlag(m[1]))
370
+ return true;
371
+ }
372
+ catch { }
373
+ }
374
+ return false;
375
+ }
308
376
  function readTokenFromDisk() {
377
+ if (readApiDisabledFromDisk())
378
+ return "";
309
379
  for (const dir of _envPaths) {
310
380
  try {
311
381
  const env = readFileSync(dir + "/.env.production", "utf8");
312
382
  const m = env.match(/^VIBEOS_API_TOKEN=(.+)$/m);
313
- if (m)
314
- return m[1].trim();
383
+ if (m) {
384
+ const clean = normalizeApiToken(m[1], "");
385
+ if (clean)
386
+ return clean;
387
+ }
315
388
  }
316
389
  catch { }
317
390
  }
318
391
  return "";
319
392
  }
320
393
  function readBootstrapTokenFromDisk() {
394
+ if (readApiDisabledFromDisk())
395
+ return "";
321
396
  try {
322
397
  const env = readFileSync(_bootstrapEnvPath, "utf8");
323
398
  const m = env.match(/^VIBEOS_API_BOOTSTRAP_TOKEN=(.+)$/m);
@@ -327,9 +402,10 @@ function readBootstrapTokenFromDisk() {
327
402
  catch { }
328
403
  return "";
329
404
  }
330
- export let VIBEOS_API_TOKEN = readTokenFromDisk() || process.env.VIBEOS_API_TOKEN || "";
331
- export let VIBEOS_API_BOOTSTRAP_TOKEN = readBootstrapTokenFromDisk() || process.env.VIBEOS_API_BOOTSTRAP_TOKEN || "";
332
- export let VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
405
+ export let VIBEOS_API_DISABLED = readApiDisabledFromDisk() || isTruthyFlag(process.env.VIBEOS_API_DISABLED);
406
+ export let VIBEOS_API_TOKEN = VIBEOS_API_DISABLED ? "" : (readTokenFromDisk() || normalizeApiToken(process.env.VIBEOS_API_TOKEN, "") || EMBEDDED_API_TOKEN);
407
+ export let VIBEOS_API_BOOTSTRAP_TOKEN = VIBEOS_API_DISABLED ? "" : (readBootstrapTokenFromDisk() || process.env.VIBEOS_API_BOOTSTRAP_TOKEN || "");
408
+ export let VIBEOS_API_ENABLED = !VIBEOS_API_DISABLED && process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
333
409
  function persistBootstrapToken(token) {
334
410
  const clean = String(token || "").trim();
335
411
  try {
@@ -352,40 +428,41 @@ function persistBootstrapToken(token) {
352
428
  }
353
429
  export function setApiToken(newToken) {
354
430
  try {
355
- VIBEOS_API_TOKEN = String(newToken || "").trim();
356
- VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && !!VIBEOS_API_TOKEN;
357
- const primaryPath = _envPaths[0] + "/.env.production";
358
- try {
359
- if (existsSync(primaryPath)) {
360
- let envContent = readFileSync(primaryPath, "utf8");
361
- if (/^VIBEOS_API_TOKEN=/m.test(envContent)) {
362
- envContent = envContent.replace(/^VIBEOS_API_TOKEN=.+$/m, `VIBEOS_API_TOKEN=${VIBEOS_API_TOKEN}`);
363
- }
364
- else {
365
- envContent = envContent.trimEnd() + `\nVIBEOS_API_TOKEN=${VIBEOS_API_TOKEN}\n`;
366
- }
367
- writeFileSync(primaryPath, envContent, "utf8");
368
- }
369
- else {
370
- const parentDir = _envPaths[0];
371
- if (!existsSync(parentDir))
372
- mkdirSync(parentDir, { recursive: true });
373
- writeFileSync(primaryPath, `VIBEOS_API_TOKEN=${VIBEOS_API_TOKEN}\n`, "utf8");
374
- }
375
- }
376
- catch (diskErr) {
377
- console.error("[vibeOS] Failed to persist API token to disk:", diskErr.message);
378
- }
431
+ VIBEOS_API_DISABLED = false;
432
+ VIBEOS_API_TOKEN = normalizeApiToken(newToken, EMBEDDED_API_TOKEN);
433
+ VIBEOS_API_BOOTSTRAP_TOKEN = readBootstrapTokenFromDisk() || VIBEOS_API_BOOTSTRAP_TOKEN;
434
+ VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
435
+ persistPrimaryApiEnvState({ token: VIBEOS_API_TOKEN, disabled: false });
379
436
  console.error("[vibeOS] API token updated via setApiToken");
380
437
  }
381
438
  catch (e) {
382
439
  console.error("[vibeOS] Failed to update API token:", e.message);
383
440
  }
384
441
  }
442
+ export function invalidateApiToken() {
443
+ try {
444
+ VIBEOS_API_DISABLED = true;
445
+ VIBEOS_API_TOKEN = "";
446
+ VIBEOS_API_BOOTSTRAP_TOKEN = "";
447
+ VIBEOS_API_ENABLED = false;
448
+ _apiClient = null;
449
+ _apiFallbackMode = false;
450
+ _apiFallbackSince = null;
451
+ persistBootstrapToken("");
452
+ persistPrimaryApiEnvState({ token: "", disabled: true });
453
+ resetApiConnection();
454
+ console.error("[vibeOS] API token invalidated and remote API disabled");
455
+ }
456
+ catch (e) {
457
+ console.error("[vibeOS] Failed to invalidate API token:", e.message);
458
+ }
459
+ }
385
460
  export function setApiBootstrapToken(newToken) {
386
461
  try {
462
+ VIBEOS_API_DISABLED = false;
387
463
  VIBEOS_API_BOOTSTRAP_TOKEN = String(newToken || "").trim();
388
464
  VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
465
+ persistPrimaryApiEnvState({ disabled: false });
389
466
  persistBootstrapToken(VIBEOS_API_BOOTSTRAP_TOKEN);
390
467
  console.error("[vibeOS] Alpha bootstrap token updated");
391
468
  }
@@ -400,6 +477,8 @@ let _bootstrapExchangeInFlight = null;
400
477
  let _bootstrapExchangeFailedAt = 0;
401
478
  export async function ensureBootstrapExchange() {
402
479
  syncApiTokenFromDisk();
480
+ if (VIBEOS_API_DISABLED)
481
+ return false;
403
482
  if (VIBEOS_API_TOKEN)
404
483
  return true;
405
484
  if (!VIBEOS_API_BOOTSTRAP_TOKEN)
@@ -436,10 +515,26 @@ export async function ensureBootstrapExchange() {
436
515
  return _bootstrapExchangeInFlight;
437
516
  }
438
517
  function syncApiTokenFromDisk() {
518
+ const diskDisabled = readApiDisabledFromDisk() || isTruthyFlag(process.env.VIBEOS_API_DISABLED);
439
519
  const diskToken = readTokenFromDisk() || "";
440
520
  const diskBootstrapToken = readBootstrapTokenFromDisk() || "";
441
- const envToken = process.env.VIBEOS_API_TOKEN || "";
521
+ const envToken = normalizeApiToken(process.env.VIBEOS_API_TOKEN, "");
522
+ if (diskDisabled) {
523
+ if (!VIBEOS_API_DISABLED || VIBEOS_API_TOKEN || VIBEOS_API_BOOTSTRAP_TOKEN || VIBEOS_API_ENABLED) {
524
+ VIBEOS_API_DISABLED = true;
525
+ VIBEOS_API_TOKEN = "";
526
+ VIBEOS_API_BOOTSTRAP_TOKEN = "";
527
+ VIBEOS_API_ENABLED = false;
528
+ _apiClient = null;
529
+ _apiFallbackMode = false;
530
+ _apiFallbackSince = null;
531
+ resetApiConnection();
532
+ console.error("[vibeOS] API token disabled from disk (alpha kill switch active)");
533
+ }
534
+ return;
535
+ }
442
536
  if (diskToken && diskToken !== VIBEOS_API_TOKEN) {
537
+ VIBEOS_API_DISABLED = false;
443
538
  VIBEOS_API_TOKEN = diskToken;
444
539
  VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
445
540
  _apiClient = null;
@@ -449,6 +544,7 @@ function syncApiTokenFromDisk() {
449
544
  console.error("[vibeOS] API token synced from disk (disk is newer)");
450
545
  }
451
546
  else if (diskBootstrapToken && diskBootstrapToken !== VIBEOS_API_BOOTSTRAP_TOKEN) {
547
+ VIBEOS_API_DISABLED = false;
452
548
  VIBEOS_API_BOOTSTRAP_TOKEN = diskBootstrapToken;
453
549
  VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
454
550
  _apiFallbackMode = false;
@@ -457,37 +553,19 @@ function syncApiTokenFromDisk() {
457
553
  console.error("[vibeOS] Alpha bootstrap token synced from disk (disk is newer)");
458
554
  }
459
555
  else if (!diskToken && VIBEOS_API_TOKEN) {
460
- const primaryPath = _envPaths[0] + "/.env.production";
461
- try {
462
- if (existsSync(primaryPath)) {
463
- let envContent = readFileSync(primaryPath, "utf8");
464
- if (/^VIBEOS_API_TOKEN=/m.test(envContent)) {
465
- envContent = envContent.replace(/^VIBEOS_API_TOKEN=.+$/m, `VIBEOS_API_TOKEN=${VIBEOS_API_TOKEN}`);
466
- }
467
- else {
468
- envContent = envContent.trimEnd() + `\nVIBEOS_API_TOKEN=${VIBEOS_API_TOKEN}\n`;
469
- }
470
- writeFileSync(primaryPath, envContent, "utf8");
471
- }
472
- else {
473
- const parentDir = _envPaths[0];
474
- if (!existsSync(parentDir))
475
- mkdirSync(parentDir, { recursive: true });
476
- writeFileSync(primaryPath, `VIBEOS_API_TOKEN=${VIBEOS_API_TOKEN}\n`, "utf8");
477
- }
478
- console.error("[vibeOS] API token persisted to disk from memory (disk was empty)");
479
- }
480
- catch (diskErr) {
481
- console.error("[vibeOS] Failed to persist API token to disk from sync:", diskErr.message);
482
- }
556
+ persistPrimaryApiEnvState({ token: VIBEOS_API_TOKEN, disabled: false });
557
+ console.error("[vibeOS] API token persisted to disk from memory (disk was empty)");
483
558
  VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && !!VIBEOS_API_TOKEN;
484
559
  }
485
560
  else if (envToken && !diskToken && !VIBEOS_API_TOKEN) {
561
+ VIBEOS_API_DISABLED = false;
486
562
  VIBEOS_API_TOKEN = envToken;
487
563
  VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
488
564
  console.error("[vibeOS] API token loaded from VIBEOS_API_TOKEN env var");
489
565
  }
490
566
  else {
567
+ VIBEOS_API_DISABLED = false;
568
+ VIBEOS_API_TOKEN ||= EMBEDDED_API_TOKEN;
491
569
  VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
492
570
  }
493
571
  }
@@ -9,6 +9,13 @@ 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, VIBEOS_API_ENABLED } from "../api-client.js";
12
+ const IS_CLI_RUNTIME = Boolean(process.stdout?.isTTY || process.stderr?.isTTY || process.stdin?.isTTY);
13
+ const IS_TEST_RUNTIME = process.env.VIBEOS_MCP_PORT === "0" || process.env.NODE_ENV === "test" || process.env.CI === "true";
14
+ const FOOTER_DEBUG_STDERR = process.env.VIBEOS_DEBUG_FOOTER === "1" || (!IS_CLI_RUNTIME && !IS_TEST_RUNTIME);
15
+ function footerDebug(...args) {
16
+ if (FOOTER_DEBUG_STDERR)
17
+ console.error(...args);
18
+ }
12
19
  function getVibeOSHome() {
13
20
  return process.env.VIBEOS_HOME || join(process.env.HOME || "", ".claude");
14
21
  }
@@ -38,9 +45,7 @@ async function apiAutoSelectMode(regime, stress) {
38
45
  return res.mode;
39
46
  }
40
47
  }
41
- catch (e) {
42
- console.error("[vibeOS] apiAutoSelectMode error:", e.message);
43
- }
48
+ catch (e) { footerDebug("[vibeOS] apiAutoSelectMode error:", e.message); }
44
49
  const fallback = regimeToMode(regime, stress);
45
50
  if (!_cachedAutoMode || _cachedAutoMode === "balanced")
46
51
  _cachedAutoMode = fallback;
@@ -142,15 +147,15 @@ async function _appendFooter(input, output, directory) {
142
147
  // Always prefer the live OpenCode model setting when available.
143
148
  try {
144
149
  const cfg = await client.config.get("model");
145
- if (cfg) {
146
- const cfgModel = String(cfg);
147
- if (cfgModel !== currentModel) {
148
- setCurrentModel(cfgModel);
149
- setCurrentTier(classify(cfgModel));
150
- console.error(`[vibeOS] client-detected model: ${currentModel} (tier=${currentTier})`);
150
+ if (cfg) {
151
+ const cfgModel = String(cfg);
152
+ if (cfgModel !== currentModel) {
153
+ setCurrentModel(cfgModel);
154
+ setCurrentTier(classify(cfgModel));
155
+ footerDebug(`[vibeOS] client-detected model: ${currentModel} (tier=${currentTier})`);
156
+ }
151
157
  }
152
158
  }
153
- }
154
159
  catch { /* client.config may not be available */ }
155
160
  try {
156
161
  const messageID = input?.messageID ||
@@ -221,9 +226,7 @@ async function _appendFooter(input, output, directory) {
221
226
  tags: ["auto", "cost"],
222
227
  });
223
228
  }
224
- catch (e) {
225
- console.error("[vibeOS] auto-report:", e.message);
226
- }
229
+ catch (e) { footerDebug("[vibeOS] auto-report:", e.message); }
227
230
  }
228
231
  // Enforcement state tags for footer — dynamically adjusted by control vector
229
232
  const selNowFooter = loadSelection();
@@ -262,7 +265,7 @@ async function _appendFooter(input, output, directory) {
262
265
  const ltTotal = ltTasks + ltCache;
263
266
  const optMode = (resolvedMode || "budget").toLowerCase();
264
267
  const modeLabel = optMode === "quality" ? "quality" : optMode === "speed" ? "speed" : optMode === "longrun" ? "longrun" : "";
265
- let vibeLine = `— ${flashIcon ? `${flashIcon} ` : ""}Quality: ${execution.quality_label} | Provider: ${execution.provider_label} | Model: ${execution.model_label}`;
268
+ let vibeLine = `— ${flashIcon ? `${flashIcon} ` : ""}Quality: ${execution.quality_label} | Provider: ${execution.provider_label} | Model: ${execution.model}`;
266
269
  if (ltTotal > 0) {
267
270
  vibeLine += ` | $${formatUsd(ltTotal)} saved`;
268
271
  }
@@ -325,7 +328,7 @@ async function _appendFooter(input, output, directory) {
325
328
  }
326
329
  }
327
330
  catch (err) {
328
- console.error(`[vibeOS] footer failed: ${err.message}`);
331
+ footerDebug(`[vibeOS] footer failed: ${err.message}`);
329
332
  }
330
333
  }
331
334
  export { _appendFooter, scoreTaskQuality, readRewardSignals };
@@ -617,7 +617,7 @@ export const onToolExecuteAfter = async (input, output) => {
617
617
  const displayModel = resolveDisplayModelId(liveModel || currentModel || "", projectDirectory) || liveModel || currentModel;
618
618
  if (ltTotal > 0) {
619
619
  const execution = resolveExecutionIdentity(input?.args?.model || liveModel || currentModel || displayModel || "", projectDirectory);
620
- _footerText = `— ${flashIcon ? `${flashIcon} ` : ""}Quality: ${formatQualityName(execution.quality)} | Provider: ${formatProviderName(execution.provider)} | Model: ${execution.model_label} | $${formatUsd(ltTotal)} saved | VIBE${flashIcon ? " ⚡" : ""} —\n\n`;
620
+ _footerText = `— ${flashIcon ? `${flashIcon} ` : ""}Quality: ${formatQualityName(execution.quality)} | Provider: ${formatProviderName(execution.provider)} | Model: ${execution.model} | $${formatUsd(ltTotal)} saved | VIBE${flashIcon ? " ⚡" : ""} —\n\n`;
621
621
  }
622
622
  else {
623
623
  _footerText = `${statusLine}${stressTag}\n\n`;
@@ -1,6 +1,7 @@
1
1
  // @ts-nocheck
2
2
  import { join } from "node:path";
3
3
  import { LABEL_MODES, buildDeterministicTrinity, resolveExecutionIdentity } from "./pricing.js";
4
+ import { invalidateApiToken } from "./api-client.js";
4
5
  export function createTrinityTool(deps) {
5
6
  return {
6
7
  description: "Control the vibeOS plugin and active model slot. " +
@@ -18,7 +19,7 @@ export function createTrinityTool(deps) {
18
19
  "Use action='setup' to create a compatibility profile for first-time users. " +
19
20
  "Use action='project' to show per-project analytics and optimization suggestions. " +
20
21
  "Use action='patterns' to inspect learned project patterns or slot='clear' to clear them. " +
21
- "Use action='guard' to ensure AGENTS.md and README.md exist and stay current. Use action='api-token' with token='<new_token>' to update the API token and re-enable remote control-vector " +
22
+ "Use action='guard' to ensure AGENTS.md and README.md exist and stay current. Use action='api-token' with token='<new_token>' to update the API token and re-enable remote control-vector, or token='invalidate' to disable the embedded alpha token " +
22
23
  "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. " +
23
24
  "Call this when the user says things like 'switch to medium', 'use cheap model', 'disable plugin', 'trinity status'.",
24
25
  args: {
@@ -726,7 +727,12 @@ export function createTrinityTool(deps) {
726
727
  }
727
728
  if (action === "api-token") {
728
729
  if (!token)
729
- return "Usage: trinity api-token <token>\nProvide a valid VIBEOS_API_TOKEN to enable remote control-vector computation.";
730
+ return "Usage: trinity api-token <token|invalidate>\nProvide a valid VIBEOS_API_TOKEN to enable remote control-vector computation, or 'invalidate' to disable it for alpha.";
731
+ const cleanToken = String(token).trim();
732
+ if (["invalidate", "disable", "clear", "revoke"].includes(cleanToken.toLowerCase())) {
733
+ invalidateApiToken();
734
+ return "[vibeOS] API token invalidated. Remote API disabled until a new token is set.";
735
+ }
730
736
  deps.setApiToken(token);
731
737
  return "[vibeOS] API token updated. Remote API re-enabled.";
732
738
  }
@@ -1102,8 +1108,8 @@ export function createTrinityTool(deps) {
1102
1108
  " trinity tdd on/off Toggle auto test skeleton creation",
1103
1109
  " trinity setup Create a compatibility profile for new users",
1104
1110
  " trinity guard Ensure AGENTS.md/README.md exist and are current",
1105
- " trinity api-token Update VIBEOS_API_TOKEN and re-enable remote API",
1106
- " trinity api-token Update VIBEOS_API_TOKEN and re-enable remote API",
1111
+ " trinity api-token <token|invalidate> Update or invalidate VIBEOS_API_TOKEN",
1112
+ " trinity api-token <token|invalidate> Update or invalidate VIBEOS_API_TOKEN",
1107
1113
  " trinity flow Show flow violations this session",
1108
1114
  "",
1109
1115
  "DIAGNOSTICS:",