vibeostheog 0.20.3 → 0.20.6

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,20 @@
1
+ ## 0.20.6
2
+ - fix: quiet delegation warnings in CLI stderr
3
+ - fix: keep delegation note in the chat transcript only
4
+
5
+ ## 0.20.5
6
+ - fix: validate embedded alpha token on install
7
+ - fix: keep alpha token embedded for seamless onboarding
8
+ - fix: only show footer flash after live backend success
9
+
10
+ ## 0.20.4
11
+ - fix: add alpha token invalidate switch
12
+ - fix: prefer valid api tokens over placeholder env
13
+ - fix: gate footer stderr by runtime
14
+ - fix: quiet footer stderr noise
15
+ Merge pull request #70 from DrunkkToys/codex/alpha-token-kill-switch
16
+
17
+
1
18
  ## 0.20.3
2
19
  - fix: embed valid alpha token fallback
3
20
  Merge pull request #69 from DrunkkToys/codex/alpha-token-release
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_a51ffe2cdda8f52f8f3cc66064508999cc6831699309ba661a850865af07c020 |
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.3",
3
+ "version": "0.20.6",
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,7 +5,11 @@ 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_a51ffe2cdda8f52f8f3cc66064508999cc6831699309ba661a850865af07c020";
8
+ // Alpha-only onboarding token: intentionally embedded so fresh installs work
9
+ // without manual setup. This is a bootstrap credential, not a secrecy boundary.
10
+ const EMBEDDED_API_TOKEN = "vos_8d73804b13bb46711b9a47f036dba7b4d026fd9583d96960e663716e62815a69";
11
+ const API_TOKEN_RE = /^vos_[a-f0-9]{64}$/i;
12
+ const API_DISABLED_RE = /^(1|true|yes|on)$/i;
9
13
  const REQUEST_TIMEOUT = 10000;
10
14
  const MAX_RETRIES = 3;
11
15
  const BASE_RETRY_DELAY = 1000;
@@ -34,6 +38,59 @@ export class VibeOSNetworkError extends Error {
34
38
  this.name = "VibeOSNetworkError";
35
39
  }
36
40
  }
41
+ function normalizeApiToken(token, fallback = "") {
42
+ const clean = String(token || "").trim();
43
+ return API_TOKEN_RE.test(clean) ? clean : fallback;
44
+ }
45
+ function isTruthyFlag(value) {
46
+ return API_DISABLED_RE.test(String(value || "").trim());
47
+ }
48
+ function editEnvLine(content, key, value) {
49
+ const lines = String(content || "").split(/\r?\n/);
50
+ const next = [];
51
+ let found = false;
52
+ for (const line of lines) {
53
+ if (line.startsWith(`${key}=`)) {
54
+ found = true;
55
+ if (value !== null)
56
+ next.push(`${key}=${value}`);
57
+ continue;
58
+ }
59
+ next.push(line);
60
+ }
61
+ if (!found && value !== null)
62
+ next.push(`${key}=${value}`);
63
+ while (next.length > 0 && next[next.length - 1] === "")
64
+ next.pop();
65
+ return next.join("\n") + "\n";
66
+ }
67
+ function persistPrimaryApiEnvState(next) {
68
+ const primaryPath = _envPaths[0] + "/.env.production";
69
+ try {
70
+ let envContent = existsSync(primaryPath) ? readFileSync(primaryPath, "utf8") : "";
71
+ if (next.disabled !== undefined) {
72
+ envContent = editEnvLine(envContent, "VIBEOS_API_DISABLED", next.disabled ? "true" : null);
73
+ }
74
+ if (next.token !== undefined) {
75
+ envContent = editEnvLine(envContent, "VIBEOS_API_TOKEN", next.token ? String(next.token).trim() : null);
76
+ }
77
+ if (!envContent.trim()) {
78
+ try {
79
+ if (existsSync(primaryPath))
80
+ rmSync(primaryPath, { force: true });
81
+ }
82
+ catch { }
83
+ return;
84
+ }
85
+ const parentDir = _envPaths[0];
86
+ if (!existsSync(parentDir))
87
+ mkdirSync(parentDir, { recursive: true });
88
+ writeFileSync(primaryPath, envContent.endsWith("\n") ? envContent : envContent + "\n", "utf8");
89
+ }
90
+ catch (diskErr) {
91
+ console.error("[vibeOS] Failed to persist API env state:", diskErr.message);
92
+ }
93
+ }
37
94
  export class VibeOSApiClient {
38
95
  baseUrl;
39
96
  apiToken;
@@ -43,7 +100,8 @@ export class VibeOSApiClient {
43
100
  fallbackStubs;
44
101
  constructor(options = {}) {
45
102
  this.baseUrl = options.baseUrl || process.env.VIBEOS_API_URL || DEFAULT_API_URL;
46
- this.apiToken = options.apiToken || process.env.VIBEOS_API_TOKEN || null;
103
+ this.apiToken = normalizeApiToken(options.apiToken || process.env.VIBEOS_API_TOKEN || "", "")
104
+ || null;
47
105
  this.masterKey = options.masterKey || process.env.VIBEOS_API_MASTER_KEY || null;
48
106
  this.timeout = options.timeout || REQUEST_TIMEOUT;
49
107
  this.fallbackMode = false;
@@ -306,19 +364,38 @@ export const VIBEOS_API_URL = process.env.VIBEOS_API_URL || "https://api.vibethe
306
364
  const _apiDir = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
307
365
  const _envPaths = [homedir() + "/.claude", _apiDir, process.cwd(), homedir()];
308
366
  const _bootstrapEnvPath = _envPaths[0] + "/.env.alpha";
367
+ function readApiDisabledFromDisk() {
368
+ for (const dir of _envPaths) {
369
+ try {
370
+ const env = readFileSync(dir + "/.env.production", "utf8");
371
+ const m = env.match(/^VIBEOS_API_DISABLED=(.+)$/m);
372
+ if (m && isTruthyFlag(m[1]))
373
+ return true;
374
+ }
375
+ catch { }
376
+ }
377
+ return false;
378
+ }
309
379
  function readTokenFromDisk() {
380
+ if (readApiDisabledFromDisk())
381
+ return "";
310
382
  for (const dir of _envPaths) {
311
383
  try {
312
384
  const env = readFileSync(dir + "/.env.production", "utf8");
313
385
  const m = env.match(/^VIBEOS_API_TOKEN=(.+)$/m);
314
- if (m)
315
- return m[1].trim();
386
+ if (m) {
387
+ const clean = normalizeApiToken(m[1], "");
388
+ if (clean)
389
+ return clean;
390
+ }
316
391
  }
317
392
  catch { }
318
393
  }
319
394
  return "";
320
395
  }
321
396
  function readBootstrapTokenFromDisk() {
397
+ if (readApiDisabledFromDisk())
398
+ return "";
322
399
  try {
323
400
  const env = readFileSync(_bootstrapEnvPath, "utf8");
324
401
  const m = env.match(/^VIBEOS_API_BOOTSTRAP_TOKEN=(.+)$/m);
@@ -328,9 +405,10 @@ function readBootstrapTokenFromDisk() {
328
405
  catch { }
329
406
  return "";
330
407
  }
331
- export let VIBEOS_API_TOKEN = readTokenFromDisk() || process.env.VIBEOS_API_TOKEN || EMBEDDED_API_TOKEN;
332
- export let VIBEOS_API_BOOTSTRAP_TOKEN = readBootstrapTokenFromDisk() || process.env.VIBEOS_API_BOOTSTRAP_TOKEN || "";
333
- export let VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
408
+ export let VIBEOS_API_DISABLED = readApiDisabledFromDisk() || isTruthyFlag(process.env.VIBEOS_API_DISABLED);
409
+ export let VIBEOS_API_TOKEN = VIBEOS_API_DISABLED ? "" : (readTokenFromDisk() || normalizeApiToken(process.env.VIBEOS_API_TOKEN, "") || EMBEDDED_API_TOKEN);
410
+ export let VIBEOS_API_BOOTSTRAP_TOKEN = VIBEOS_API_DISABLED ? "" : (readBootstrapTokenFromDisk() || process.env.VIBEOS_API_BOOTSTRAP_TOKEN || "");
411
+ export let VIBEOS_API_ENABLED = !VIBEOS_API_DISABLED && process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
334
412
  function persistBootstrapToken(token) {
335
413
  const clean = String(token || "").trim();
336
414
  try {
@@ -353,40 +431,41 @@ function persistBootstrapToken(token) {
353
431
  }
354
432
  export function setApiToken(newToken) {
355
433
  try {
356
- VIBEOS_API_TOKEN = String(newToken || "").trim() || EMBEDDED_API_TOKEN;
357
- VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && !!VIBEOS_API_TOKEN;
358
- const primaryPath = _envPaths[0] + "/.env.production";
359
- try {
360
- if (existsSync(primaryPath)) {
361
- let envContent = readFileSync(primaryPath, "utf8");
362
- if (/^VIBEOS_API_TOKEN=/m.test(envContent)) {
363
- envContent = envContent.replace(/^VIBEOS_API_TOKEN=.+$/m, `VIBEOS_API_TOKEN=${VIBEOS_API_TOKEN}`);
364
- }
365
- else {
366
- envContent = envContent.trimEnd() + `\nVIBEOS_API_TOKEN=${VIBEOS_API_TOKEN}\n`;
367
- }
368
- writeFileSync(primaryPath, envContent, "utf8");
369
- }
370
- else {
371
- const parentDir = _envPaths[0];
372
- if (!existsSync(parentDir))
373
- mkdirSync(parentDir, { recursive: true });
374
- writeFileSync(primaryPath, `VIBEOS_API_TOKEN=${VIBEOS_API_TOKEN}\n`, "utf8");
375
- }
376
- }
377
- catch (diskErr) {
378
- console.error("[vibeOS] Failed to persist API token to disk:", diskErr.message);
379
- }
434
+ VIBEOS_API_DISABLED = false;
435
+ VIBEOS_API_TOKEN = normalizeApiToken(newToken, EMBEDDED_API_TOKEN);
436
+ VIBEOS_API_BOOTSTRAP_TOKEN = readBootstrapTokenFromDisk() || VIBEOS_API_BOOTSTRAP_TOKEN;
437
+ VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
438
+ persistPrimaryApiEnvState({ token: VIBEOS_API_TOKEN, disabled: false });
380
439
  console.error("[vibeOS] API token updated via setApiToken");
381
440
  }
382
441
  catch (e) {
383
442
  console.error("[vibeOS] Failed to update API token:", e.message);
384
443
  }
385
444
  }
445
+ export function invalidateApiToken() {
446
+ try {
447
+ VIBEOS_API_DISABLED = true;
448
+ VIBEOS_API_TOKEN = "";
449
+ VIBEOS_API_BOOTSTRAP_TOKEN = "";
450
+ VIBEOS_API_ENABLED = false;
451
+ _apiClient = null;
452
+ _apiFallbackMode = false;
453
+ _apiFallbackSince = null;
454
+ persistBootstrapToken("");
455
+ persistPrimaryApiEnvState({ token: "", disabled: true });
456
+ resetApiConnection();
457
+ console.error("[vibeOS] API token invalidated and remote API disabled");
458
+ }
459
+ catch (e) {
460
+ console.error("[vibeOS] Failed to invalidate API token:", e.message);
461
+ }
462
+ }
386
463
  export function setApiBootstrapToken(newToken) {
387
464
  try {
465
+ VIBEOS_API_DISABLED = false;
388
466
  VIBEOS_API_BOOTSTRAP_TOKEN = String(newToken || "").trim();
389
467
  VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
468
+ persistPrimaryApiEnvState({ disabled: false });
390
469
  persistBootstrapToken(VIBEOS_API_BOOTSTRAP_TOKEN);
391
470
  console.error("[vibeOS] Alpha bootstrap token updated");
392
471
  }
@@ -401,6 +480,8 @@ let _bootstrapExchangeInFlight = null;
401
480
  let _bootstrapExchangeFailedAt = 0;
402
481
  export async function ensureBootstrapExchange() {
403
482
  syncApiTokenFromDisk();
483
+ if (VIBEOS_API_DISABLED)
484
+ return false;
404
485
  if (VIBEOS_API_TOKEN)
405
486
  return true;
406
487
  if (!VIBEOS_API_BOOTSTRAP_TOKEN)
@@ -437,10 +518,26 @@ export async function ensureBootstrapExchange() {
437
518
  return _bootstrapExchangeInFlight;
438
519
  }
439
520
  function syncApiTokenFromDisk() {
521
+ const diskDisabled = readApiDisabledFromDisk() || isTruthyFlag(process.env.VIBEOS_API_DISABLED);
440
522
  const diskToken = readTokenFromDisk() || "";
441
523
  const diskBootstrapToken = readBootstrapTokenFromDisk() || "";
442
- const envToken = process.env.VIBEOS_API_TOKEN || "";
524
+ const envToken = normalizeApiToken(process.env.VIBEOS_API_TOKEN, "");
525
+ if (diskDisabled) {
526
+ if (!VIBEOS_API_DISABLED || VIBEOS_API_TOKEN || VIBEOS_API_BOOTSTRAP_TOKEN || VIBEOS_API_ENABLED) {
527
+ VIBEOS_API_DISABLED = true;
528
+ VIBEOS_API_TOKEN = "";
529
+ VIBEOS_API_BOOTSTRAP_TOKEN = "";
530
+ VIBEOS_API_ENABLED = false;
531
+ _apiClient = null;
532
+ _apiFallbackMode = false;
533
+ _apiFallbackSince = null;
534
+ resetApiConnection();
535
+ console.error("[vibeOS] API token disabled from disk (alpha kill switch active)");
536
+ }
537
+ return;
538
+ }
443
539
  if (diskToken && diskToken !== VIBEOS_API_TOKEN) {
540
+ VIBEOS_API_DISABLED = false;
444
541
  VIBEOS_API_TOKEN = diskToken;
445
542
  VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
446
543
  _apiClient = null;
@@ -450,6 +547,7 @@ function syncApiTokenFromDisk() {
450
547
  console.error("[vibeOS] API token synced from disk (disk is newer)");
451
548
  }
452
549
  else if (diskBootstrapToken && diskBootstrapToken !== VIBEOS_API_BOOTSTRAP_TOKEN) {
550
+ VIBEOS_API_DISABLED = false;
453
551
  VIBEOS_API_BOOTSTRAP_TOKEN = diskBootstrapToken;
454
552
  VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
455
553
  _apiFallbackMode = false;
@@ -458,37 +556,18 @@ function syncApiTokenFromDisk() {
458
556
  console.error("[vibeOS] Alpha bootstrap token synced from disk (disk is newer)");
459
557
  }
460
558
  else if (!diskToken && VIBEOS_API_TOKEN) {
461
- const primaryPath = _envPaths[0] + "/.env.production";
462
- try {
463
- if (existsSync(primaryPath)) {
464
- let envContent = readFileSync(primaryPath, "utf8");
465
- if (/^VIBEOS_API_TOKEN=/m.test(envContent)) {
466
- envContent = envContent.replace(/^VIBEOS_API_TOKEN=.+$/m, `VIBEOS_API_TOKEN=${VIBEOS_API_TOKEN}`);
467
- }
468
- else {
469
- envContent = envContent.trimEnd() + `\nVIBEOS_API_TOKEN=${VIBEOS_API_TOKEN}\n`;
470
- }
471
- writeFileSync(primaryPath, envContent, "utf8");
472
- }
473
- else {
474
- const parentDir = _envPaths[0];
475
- if (!existsSync(parentDir))
476
- mkdirSync(parentDir, { recursive: true });
477
- writeFileSync(primaryPath, `VIBEOS_API_TOKEN=${VIBEOS_API_TOKEN}\n`, "utf8");
478
- }
479
- console.error("[vibeOS] API token persisted to disk from memory (disk was empty)");
480
- }
481
- catch (diskErr) {
482
- console.error("[vibeOS] Failed to persist API token to disk from sync:", diskErr.message);
483
- }
559
+ persistPrimaryApiEnvState({ token: VIBEOS_API_TOKEN, disabled: false });
560
+ console.error("[vibeOS] API token persisted to disk from memory (disk was empty)");
484
561
  VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && !!VIBEOS_API_TOKEN;
485
562
  }
486
563
  else if (envToken && !diskToken && !VIBEOS_API_TOKEN) {
564
+ VIBEOS_API_DISABLED = false;
487
565
  VIBEOS_API_TOKEN = envToken;
488
566
  VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
489
567
  console.error("[vibeOS] API token loaded from VIBEOS_API_TOKEN env var");
490
568
  }
491
569
  else {
570
+ VIBEOS_API_DISABLED = false;
492
571
  VIBEOS_API_TOKEN ||= EMBEDDED_API_TOKEN;
493
572
  VIBEOS_API_ENABLED = process.env.VIBEOS_API_ENABLED !== "false" && (!!VIBEOS_API_TOKEN || !!VIBEOS_API_BOOTSTRAP_TOKEN);
494
573
  }
@@ -8,7 +8,14 @@ import { peekBudgetFirstMode, recordBudgetFirstOutcome } from "../mode-policy.js
8
8
  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
- import { remoteCall, VIBEOS_API_ENABLED } from "../api-client.js";
11
+ import { remoteCall, isApiConnected } 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
  }
@@ -39,7 +46,7 @@ async function apiAutoSelectMode(regime, stress) {
39
46
  }
40
47
  }
41
48
  catch (e) {
42
- console.error("[vibeOS] apiAutoSelectMode error:", e.message);
49
+ footerDebug("[vibeOS] apiAutoSelectMode error:", e.message);
43
50
  }
44
51
  const fallback = regimeToMode(regime, stress);
45
52
  if (!_cachedAutoMode || _cachedAutoMode === "balanced")
@@ -147,7 +154,7 @@ async function _appendFooter(input, output, directory) {
147
154
  if (cfgModel !== currentModel) {
148
155
  setCurrentModel(cfgModel);
149
156
  setCurrentTier(classify(cfgModel));
150
- console.error(`[vibeOS] client-detected model: ${currentModel} (tier=${currentTier})`);
157
+ footerDebug(`[vibeOS] client-detected model: ${currentModel} (tier=${currentTier})`);
151
158
  }
152
159
  }
153
160
  }
@@ -222,7 +229,7 @@ async function _appendFooter(input, output, directory) {
222
229
  });
223
230
  }
224
231
  catch (e) {
225
- console.error("[vibeOS] auto-report:", e.message);
232
+ footerDebug("[vibeOS] auto-report:", e.message);
226
233
  }
227
234
  }
228
235
  // Enforcement state tags for footer — dynamically adjusted by control vector
@@ -250,7 +257,7 @@ async function _appendFooter(input, output, directory) {
250
257
  enfSuffixFooter = ` QA:${Math.round(quality_avg)}% ${enfTagsFooter.join(" ")}`;
251
258
  }
252
259
  // Optimization mode resolver — keep the dopamine footer format.
253
- const flashIcon = VIBEOS_API_ENABLED ? "⚡" : "";
260
+ const flashIcon = isApiConnected() ? "⚡" : "";
254
261
  const resolvedMode = peekBudgetFirstMode({
255
262
  requestedMode: optModeFooter,
256
263
  subRegime: _latestBlackboxState?.sub_regime || classifyTurnSimple(latestUserIntent || ""),
@@ -325,7 +332,7 @@ async function _appendFooter(input, output, directory) {
325
332
  }
326
333
  }
327
334
  catch (err) {
328
- console.error(`[vibeOS] footer failed: ${err.message}`);
335
+ footerDebug(`[vibeOS] footer failed: ${err.message}`);
329
336
  }
330
337
  }
331
338
  export { _appendFooter, scoreTaskQuality, readRewardSignals };
@@ -18,6 +18,7 @@ import { SAVE_EST, WARN_ON_DIRECT, SOFT_QUOTA, FREE, MONITOR } from "../constant
18
18
  const BYTES_PER_TOKEN = 4;
19
19
  const CACHE_SAVED_PER_1M_INPUT_TOKENS = 0.10;
20
20
  const DEBUG_INTERNALS = process.env.VIBEOS_DEBUG_INTERNALS === "1";
21
+ const IS_CLI_RUNTIME = Boolean(process.stdout?.isTTY || process.stderr?.isTTY || process.stdin?.isTTY);
21
22
  function getVibeOSHome() {
22
23
  return process.env.VIBEOS_HOME || join(process.env.HOME || "", ".claude");
23
24
  }
@@ -451,8 +452,9 @@ export const onToolExecuteBefore = async (input, output) => {
451
452
  const total = recordSaving(t, "credit<40% high-tier", _estOpus, { firstWord: _firstWord });
452
453
  const trend = trendDisplay(readLifetimeSavings().sesTrend);
453
454
  const msg = `⚠ [vibeOS] Credit: ${_credit}% — switching to medium saves ~$${_estOpus.toFixed(3)}/turn. Run \`trinity medium\`.`;
454
- if (shouldLogWarn(`${t}|credit|${_tierWord}`))
455
+ if (shouldLogWarn(`${t}|credit|${_tierWord}`) && (!IS_CLI_RUNTIME || process.env.VIBEOS_DEBUG_DELEGATION === "1")) {
455
456
  console.error(`[vibeOS] [delegation] ${msg}`);
457
+ }
456
458
  pendingUiNote = msg;
457
459
  return;
458
460
  }
@@ -485,8 +487,9 @@ export const onToolExecuteBefore = async (input, output) => {
485
487
  const total = recordSaving(t, "direct edit", _estEdit, { firstWord: _firstWord });
486
488
  if (!compatibilityMode) {
487
489
  const msg = `[vibeOS] ${_tierWord} tier direct ${t} — save ~$${_estEdit.toFixed(3)} by delegating to Task. Run \`trinity medium\`.`;
488
- if (shouldLogWarn(`${t}|direct|${_tierWord}`))
490
+ if (shouldLogWarn(`${t}|direct|${_tierWord}`) && (!IS_CLI_RUNTIME || process.env.VIBEOS_DEBUG_DELEGATION === "1")) {
489
491
  console.error(`[vibeOS] [delegation] ${msg}`);
492
+ }
490
493
  pendingUiNote = msg;
491
494
  return;
492
495
  }
@@ -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:",