vibeostheog 0.20.6 → 0.20.9
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 +17 -0
- package/README.md +188 -105
- package/package.json +5 -2
- package/scripts/deploy.mjs +14 -0
- package/src/lib/constants.js +1 -1
- package/src/lib/hooks/footer.js +3 -3
- package/src/lib/hooks/session-compact.js +19 -1
- package/src/lib/hooks/tool-execute.js +53 -16
- package/src/lib/pricing.js +2 -2
- package/src/vibeOS-lib/blackbox/advice-layer.js +1 -0
- package/src/vibeOS-lib/blackbox/crew-constants.js +1 -0
- package/src/vibeOS-lib/blackbox/exposure-model.js +1 -0
- package/src/vibeOS-lib/blackbox/index.js +3 -0
- package/src/vibeOS-lib/blackbox/local-stub.js +1 -0
- package/src/vibeOS-lib/blackbox/meta-controller.js +24 -1
- package/src/vibeOS-lib/blackbox/pivot-cache.js +175 -0
- package/src/vibeOS-lib/blackbox/resolution-tracker.js +1 -0
- package/src/vibeOS-lib/blackbox/taxonomy.js +1 -0
- package/src/vibeOS-lib/blackbox/vibemax.js +271 -0
- package/src/vibeOS-lib/flow-enforcer.js +33 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## 0.20.9
|
|
2
|
+
- feat: hard-block console.log/debug/info in eslint (warn->error)
|
|
3
|
+
- fix: update constants.js OPUS_DISABLE to 1e-10 (dead code, matches TS source)
|
|
4
|
+
- fix: modelCostPerTurn returns FREE_MODEL_TURN_USD for unknown models
|
|
5
|
+
- docs: rename blackbox to VibeBoX and document local fallback
|
|
6
|
+
- docs: dopamine-style README reformat with OPUS->SONNET->HAIKU pricing
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## 0.20.7
|
|
10
|
+
- fix: ship compiled OpenCode plugin bundle
|
|
11
|
+
- fix: always show model label in tool.execute.after footer, even with zero savings
|
|
12
|
+
- fix: always show model label in tool.execute.after footer, even with zero savings
|
|
13
|
+
- fix: restore release tarball pack step
|
|
14
|
+
Merge pull request #74 from DrunkkToys/codex/release-live-bundle
|
|
15
|
+
Merge pull request #72 from DrunkkToys/codex/alpha-token-install-validation
|
|
16
|
+
|
|
17
|
+
|
|
1
18
|
## 0.20.6
|
|
2
19
|
- fix: quiet delegation warnings in CLI stderr
|
|
3
20
|
- fix: keep delegation note in the chat transcript only
|
package/README.md
CHANGED
|
@@ -1,18 +1,87 @@
|
|
|
1
1
|
# vibeOS for OpenCode
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Prices validated: May 28, 2026** — verified against OpenRouter `/api/v1/models`
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Cost-aware control plane for OpenCode Desktop. Keeps expensive models on strategy, routes implementation to cheaper tiers, surfaces savings in real time.
|
|
6
6
|
|
|
7
7
|
For teams, vibeOS adds practical guardrails: delegation enforcement, flow and TDD controls, pattern learning, stress-aware routing, VibeBoX decision tracking, reporting, and remote API protection for the core algorithms.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Delegation Enforcement
|
|
10
|
+
|
|
11
|
+
Every `write`/`edit`/`notebookedit` on the **brain tier** is intercepted, cost-estimated, and blocked with a visible enforcement note. Work must be delegated to **medium** or **cheap**. This is the primary savings mechanism.
|
|
12
|
+
|
|
13
|
+
### Per-Turn Cost (700 input + 300 output tokens)
|
|
14
|
+
|
|
15
|
+
| Tier | Model | Per Turn | Per 100 Turns | vs Opus |
|
|
16
|
+
|------|-------|----------|---------------|---------|
|
|
17
|
+
| Brain | `claude-opus-4-7` | **$0.0330** | **$3.30** | — |
|
|
18
|
+
| Medium | `claude-sonnet-4-6` | **$0.0066** | **$0.66** | saves 80% |
|
|
19
|
+
| Cheap | `claude-haiku-4-5` | **$0.0022** | **$0.22** | saves 93% |
|
|
20
|
+
|
|
21
|
+
*Source: `src/lib/pricing.ts:279-285`. Conservative estimates — actual OpenRouter live: Opus $0.011, Sonnet $0.0066, Haiku $0.0022 per turn. The plugin over-estimates brain cost so savings are always understated.*
|
|
22
|
+
|
|
23
|
+
### Savings per Delegation
|
|
24
|
+
|
|
25
|
+
| Move | Per Turn | 10x | 100x | 1,000x |
|
|
26
|
+
|------|----------|-----|------|--------|
|
|
27
|
+
| Opus → Haiku | $0.0308 | $0.31 | $3.08 | $30.80 |
|
|
28
|
+
| Opus → Sonnet | $0.0264 | $0.26 | $2.64 | $26.40 |
|
|
29
|
+
| Sonnet → Haiku | $0.0044 | $0.04 | $0.44 | $4.40 |
|
|
30
|
+
|
|
31
|
+
Every blocked brain-tier write/edit saves at least $0.026 (Opus→Sonnet). The running total is tracked in `~/.claude/delegation-state.json` and displayed in the live footer.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
| Feature | What it does |
|
|
38
|
+
|---------|-------------|
|
|
39
|
+
| **Delegation enforcement** | Blocks write/edit on brain tier. Routes to medium or cheap. |
|
|
40
|
+
| **Live savings footer** | Model, provider, cumulative savings, cache savings, stress gauge, lock/enforcement tags. |
|
|
41
|
+
| **Web dashboard** | SolidJS SPA with SSE real-time push. Model split, savings, session history, trinity controls. |
|
|
42
|
+
| **Trinity runtime** | `trinity set brain\|medium\|cheap`. Switch tiers mid-session. Change optimization mode. |
|
|
43
|
+
| **Flow enforcer** | Pattern-rule checks on write/edit. Extracts TODO/FIXME into an append-only queue. |
|
|
44
|
+
| **TDD enforcer** | Auto-creates test skeletons for changed source. Strict mode: TODO tests fail. |
|
|
45
|
+
| **Pattern learner** | Tracks recurring struggle/routine patterns per project. |
|
|
46
|
+
| **VibeBoX** | 7 sub-regimes, 11 features per turn, 4 loop intervention levels, PIVOT/SWITCH detection. Auto-mode maps regime to optimization mode. |
|
|
47
|
+
| **Stress-aware routing** | Stress gauge in footer. Stress > 1.5 escalates to quality mode. |
|
|
48
|
+
| **Cache savings** | Separate `cache_savings_usd` tracking for scratchpad cache hits. |
|
|
49
|
+
| **Report tools** | `report-save`, `report-list`, `report-read`, `research-audit`. |
|
|
50
|
+
| **MCP server** | Extended tool capabilities + dashboard serving + SSE push endpoint. |
|
|
51
|
+
| **Remote API** | Fastify server at `api.vibetheog.com`. Token auth with seat/license management. |
|
|
52
|
+
| **Session lock** | `trinity lock on\|off` — freezes model at session start. Resets on restart. |
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## How It Works
|
|
57
|
+
|
|
58
|
+
8 hooks into OpenCode Desktop:
|
|
59
|
+
|
|
60
|
+
| Hook | Purpose |
|
|
61
|
+
|------|---------|
|
|
62
|
+
| `experimental.text.complete` | Appends footer to assistant responses |
|
|
63
|
+
| `experimental.chat.messages.transform` | Injects delegation protocol content |
|
|
64
|
+
| `experimental.chat.system.transform` | Injects cost optimization, stress inoculation, enforcement directives |
|
|
65
|
+
| `tool.execute.before` | Blocks write/edit on brain tier |
|
|
66
|
+
| `tool.execute.after` | Injects delegation UI notes |
|
|
67
|
+
| `message.updated` | Fallback footer for versions without text.complete |
|
|
68
|
+
| `experimental.session.compacting` | Preserves savings state |
|
|
69
|
+
| `shell.env` | Injects `OPENCODE_MODEL_TIER` and `OPENCODE_MODEL` |
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Local vs Remote
|
|
74
|
+
|
|
75
|
+
### Full Local (no token)
|
|
76
|
+
|
|
77
|
+
Model tier classification, static pricing (~20 models), stress scoring, context budget, turn classification, TDD skeleton gen, flow enforcement, savings ledger, session metrics, reports, footer, dashboard, smart cache, VibeBoX fallback.
|
|
78
|
+
|
|
79
|
+
### Requires Remote API (api-token)
|
|
80
|
+
|
|
81
|
+
Bootstrap token exchange, advanced VibeBoX with full session history, dynamic per-prompt delegation, cross-session calibration, live pricing fetch beyond static map.
|
|
82
|
+
|
|
83
|
+
---
|
|
10
84
|
|
|
11
|
-
- Model routing that matches the job to the right provider and tier
|
|
12
|
-
- Live savings visibility in chat, the footer, and the web dashboard
|
|
13
|
-
- Separate tracking for delegation savings and cache savings
|
|
14
|
-
- Runtime controls for flow, TDD, model locking, and VibeBoX mode
|
|
15
|
-
- A local fallback path if the remote API is unavailable
|
|
16
85
|
|
|
17
86
|
## Local Fallback Mode
|
|
18
87
|
|
|
@@ -30,7 +99,6 @@ Without a token, vibeOS keeps running in local-only mode with bundled algorithms
|
|
|
30
99
|
### Requires Remote API
|
|
31
100
|
|
|
32
101
|
- Bootstrap token exchange (required for initial API setup)
|
|
33
|
-
- Alpha seat issuance is currently uncapped in the admin tooling
|
|
34
102
|
- Advanced VibeBoX decision engine with full session history tracking
|
|
35
103
|
- Dynamic per-prompt delegation decisions (local fallback uses a safe "block all writes on high tier" default)
|
|
36
104
|
- Learned subagent routing patterns across projects (local fallback uses a static exploratory keyword list)
|
|
@@ -43,124 +111,139 @@ When the remote API is unreachable, the plugin degrades gracefully to rule-based
|
|
|
43
111
|
|
|
44
112
|
## Install
|
|
45
113
|
|
|
46
|
-
### OpenCode plugin
|
|
47
|
-
|
|
48
|
-
1. Register the plugin with the bundled setup command:
|
|
49
|
-
|
|
50
114
|
```bash
|
|
51
|
-
npx vibeostheog setup --project
|
|
115
|
+
npx vibeostheog setup --project # per-project
|
|
116
|
+
npx vibeostheog setup # global ~/.config/opencode/
|
|
52
117
|
```
|
|
53
118
|
|
|
54
|
-
|
|
119
|
+
Adds `vibeostheog` to `opencode.json`. Restart OpenCode Desktop.
|
|
55
120
|
|
|
56
|
-
|
|
121
|
+
Local dev checkout:
|
|
57
122
|
|
|
58
123
|
```json
|
|
59
124
|
{
|
|
60
|
-
"plugin": [
|
|
61
|
-
"vibeostheog"
|
|
62
|
-
]
|
|
125
|
+
"plugin": ["/absolute/path/to/theSaver-oc/src/index.js"]
|
|
63
126
|
}
|
|
64
127
|
```
|
|
65
128
|
|
|
66
|
-
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Commands
|
|
132
|
+
|
|
133
|
+
`trinity help` for full reference. Commands register in the TUI sidebar.
|
|
134
|
+
|
|
135
|
+
| Command | Effect |
|
|
136
|
+
|---------|--------|
|
|
137
|
+
| `trinity status` | Tier, enforcement, savings, stress, lock state |
|
|
138
|
+
| `trinity set brain\|medium\|cheap` | Switch active model tier |
|
|
139
|
+
| `trinity brain\|medium\|cheap` | Shorthand tier switch |
|
|
140
|
+
| `trinity enable\|disable` | Toggle plugin on/off |
|
|
141
|
+
| `trinity mode budget\|quality\|speed\|longrun\|auto` | Set optimization mode |
|
|
142
|
+
| `trinity thinking full\|brief\|off` | Reasoning depth |
|
|
143
|
+
| `trinity enforce on\|off` | Toggle enforcement |
|
|
144
|
+
| `trinity lock on\|off` | Freeze model for session |
|
|
145
|
+
| `trinity flow on\|off` | Toggle flow enforcer |
|
|
146
|
+
| `trinity flow enforce on\|off` | Toggle auto-extract TODOs |
|
|
147
|
+
| `trinity tdd on\|off\|strict\|quality` | Test skeleton behavior |
|
|
148
|
+
| `trinity rebuild` | Re-detect models from all providers |
|
|
149
|
+
| `trinity project` | Per-project analytics |
|
|
150
|
+
| `trinity patterns` / `trinity patterns clear` | Pattern inspection |
|
|
151
|
+
| `trinity diagnose` | Health check |
|
|
152
|
+
| `trinity VibeBoX on\|off\|status\|reset` | Decision engine control |
|
|
153
|
+
| `trinity repair-state preview\|apply` | Fix state collisions |
|
|
154
|
+
| `trinity guard` | Refresh AGENTS.md / README.md |
|
|
155
|
+
| `trinity api-token <token\|invalidate>` | Manage remote API token |
|
|
156
|
+
| `trinity api-bootstrap-token <token>` | Bootstrap token exchange |
|
|
157
|
+
|
|
158
|
+
**Report commands**: `report-save`, `report-list`, `report-read`, `research-audit`
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Live Footer
|
|
67
163
|
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
"plugin": [
|
|
71
|
-
"/absolute/path/to/theSaver-oc/src/index.js"
|
|
72
|
-
]
|
|
73
|
-
}
|
|
164
|
+
```
|
|
165
|
+
— Model: claude-sonnet-4-6 | Provider: Anthropic | $4.82 saved | $1.20 cached | ENFORCE | LOCK | Quality | VIBE —
|
|
74
166
|
```
|
|
75
167
|
|
|
76
|
-
|
|
168
|
+
Provider, model, delegation savings, cache savings, stress gauge (block chars), lock/enforcement tags, optimization mode. Persisted in `~/.claude/delegation-state.json`.
|
|
77
169
|
|
|
78
|
-
|
|
170
|
+
---
|
|
79
171
|
|
|
80
|
-
##
|
|
172
|
+
## Architecture
|
|
81
173
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
npm run build
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
174
|
+
### Plugin Source
|
|
175
|
+
|
|
176
|
+
Single-file runtime `src/index.js` (5529+ lines). TypeScript source of truth at `src/vibeOS-lib/*.ts` and `src/utils/*.ts`. Build: `npm run build` (tsc + esbuild bundle + deploy script).
|
|
177
|
+
|
|
178
|
+
### State Files (`~/.claude/`)
|
|
179
|
+
|
|
180
|
+
| File | Purpose |
|
|
181
|
+
|------|---------|
|
|
182
|
+
| `delegation-state.json` | Sessions, warns, cache hits, lifetime totals |
|
|
183
|
+
| `model-tiers.json` | brain/medium/cheap model IDs |
|
|
184
|
+
| `project-states.json` | Per-project memory, analytics, report references |
|
|
185
|
+
| `reports/` | Saved report JSON files |
|
|
186
|
+
| `savings-ledger.jsonl` | Append-only savings and credit event log |
|
|
187
|
+
| `global-learning.json` | Cross-project pattern learning, pricing hints |
|
|
188
|
+
| `model-pricing-cache.json` | Cached pricing by model ID |
|
|
189
|
+
| `active-jobs.json` | In-flight delegation records |
|
|
190
|
+
| `VibeBoX-state.json` | Per-project resolution tracker, session outcomes |
|
|
191
|
+
| `.flow-todo-queue.jsonl` | Flow enforcer TODO queue |
|
|
192
|
+
| `.flow-dedup-keys.json` | Deduplication set for flow TODO |
|
|
193
|
+
| `.enforcement-cooldown.jsonl` | Per-tool cooldown for warn coalescing |
|
|
194
|
+
|
|
195
|
+
### VibeBoX Decision Engine
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
7 sub-regimes (INIT, DIVERGENT, EXPLORING, REFINING, CONVERGING, CLOSED, LOOPING). Classification via entropy trends, action consistency, feature contradiction, embedding drift. 11 derived features per turn. 4 loop intervention levels. PIVOT/SWITCH detection. Outcome tracking from satisfaction signals.
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
Regime→mode mapping via `syncControlSettings()`:
|
|
202
|
+
|
|
203
|
+
| Regime | Mode | Enforce | Flow | TDD | Tier | Think |
|
|
204
|
+
|--------|------|---------|------|-----|------|-------|
|
|
205
|
+
| INIT / DIVERGENT / EXPLORING / REFINING | budget | relaxed | audit | lazy | cheap | off |
|
|
206
|
+
| CONVERGING / CLOSED | quality | strict | strict | quality | brain | full |
|
|
207
|
+
| LOOPING | speed | relaxed | audit | lazy | medium | off |
|
|
208
|
+
|
|
209
|
+
Stress > 1.5 escalates any regime to quality.
|
|
210
|
+
|
|
211
|
+
### Remote API Server
|
|
212
|
+
|
|
213
|
+
`src/vibeOS-api-server/` — Fastify + SQLite at `api.vibetheog.com`. Endpoints: delegation check, tier routing, stress scoring, VibeBoX analysis/calibration, TDD skeleton gen, pattern observation, pricing fetch, context compression. Auth via `VIBEOS_API_TOKEN`. Client: `src/vibeOS-api-server/client.js` with automatic local fallback.
|
|
214
|
+
|
|
215
|
+
### Dashboard
|
|
216
|
+
|
|
217
|
+
SolidJS SPA at `src/dashboard/`. Build: `npm run build:dashboard` (vite). Served by MCP server or standalone. SSE `/events` for real-time push (model split, savings, session history, stress, VibeBoX state).
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Environment Variables
|
|
89
222
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
- `trinity status` - show current tier, enforcement, savings, stress, and lock state
|
|
99
|
-
- `trinity set brain|medium|cheap` - switch the active tier
|
|
100
|
-
- `trinity brain|medium|cheap` - shorthand tier switch
|
|
101
|
-
- `trinity rebuild` - re-detect available models and repopulate slots
|
|
102
|
-
- `trinity enable` / `trinity disable` - toggle the plugin on or off
|
|
103
|
-
- `trinity mode budget|quality|speed|longrun|auto` - change the optimization mode
|
|
104
|
-
- `trinity thinking full|brief|off` - change reasoning depth
|
|
105
|
-
- `trinity enforce on|off` - control delegation enforcement
|
|
106
|
-
- `trinity lock on|off` - freeze the active model for the session
|
|
107
|
-
- `trinity flow on|off` and `trinity flow enforce on|off` - manage flow checks
|
|
108
|
-
- `trinity tdd on|off`, `trinity tdd strict on|off`, `trinity tdd quality on|off` - manage test skeleton behavior
|
|
109
|
-
- `trinity project` - open project analytics
|
|
110
|
-
- `trinity patterns` / `trinity patterns clear` - inspect or reset learned patterns
|
|
111
|
-
- `trinity diagnose` - run a health check
|
|
112
|
-
- `trinity repair-state preview|apply` - fix state fingerprint collisions
|
|
113
|
-
- `trinity VibeBoX on|off|status|reset` - control the decision engine
|
|
114
|
-
- `trinity guard` - refresh AGENTS.md and README.md checks
|
|
115
|
-
- `trinity api-token <token|invalidate>` - update the remote API token, or invalidate the current alpha token and disable remote API
|
|
116
|
-
- `trinity api-bootstrap-token <token>` - store an alpha bootstrap token and exchange it for a normal API token on alpha builds
|
|
117
|
-
|
|
118
|
-
Additional reporting commands:
|
|
119
|
-
|
|
120
|
-
- `report-save`
|
|
121
|
-
- `report-list`
|
|
122
|
-
- `report-read`
|
|
123
|
-
- `research-audit`
|
|
124
|
-
|
|
125
|
-
## Savings And Footer
|
|
126
|
-
|
|
127
|
-
The footer shows:
|
|
128
|
-
|
|
129
|
-
- the active provider/model in use for the current run
|
|
130
|
-
- cumulative delegation savings
|
|
131
|
-
- cache savings
|
|
132
|
-
- stress level
|
|
133
|
-
- lock and enforcement tags
|
|
134
|
-
|
|
135
|
-
Savings are persisted in `~/.claude/delegation-state.json`.
|
|
136
|
-
|
|
137
|
-
## Configuration
|
|
138
|
-
|
|
139
|
-
| Variable | Default | Purpose |
|
|
140
|
-
|---|---|---|
|
|
141
|
-
| `VIBEOS_API_URL` | `https://api.vibetheog.com` | Remote API server URL |
|
|
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 |
|
|
144
|
-
| `VIBEOS_API_BOOTSTRAP_TOKEN` | unset | Alpha bootstrap token for initial auth exchange |
|
|
145
|
-
| `VIBEOS_API_ENABLED` | `true` | Set to `false` for local-only mode |
|
|
223
|
+
| Variable | Default | Effect |
|
|
224
|
+
|----------|---------|--------|
|
|
225
|
+
| `VIBEOS_API_URL` | `https://api.vibetheog.com` | Remote API base URL |
|
|
226
|
+
| `VIBEOS_API_TOKEN` | unset | Remote API auth |
|
|
227
|
+
| `VIBEOS_API_DISABLED` | `false` | Invalidate alpha token |
|
|
228
|
+
| `VIBEOS_API_BOOTSTRAP_TOKEN` | unset | Bootstrap exchange |
|
|
229
|
+
| `VIBEOS_API_ENABLED` | `true` | Set `false` for local-only |
|
|
146
230
|
| `CLAUDE_CREDIT_PERCENT` | `100` | Credit override |
|
|
147
|
-
| `CLAUDE_CONTEXT7_AVAILABLE` | unset |
|
|
148
|
-
| `CLAUDE_SCRATCHPAD_MAX_AGE_SEC` | `86400` | Scratchpad cache lifetime |
|
|
231
|
+
| `CLAUDE_CONTEXT7_AVAILABLE` | unset | Context7 optimization |
|
|
149
232
|
| `VIBEOS_MCP_PORT` | `3001` | MCP server port |
|
|
150
233
|
|
|
151
|
-
|
|
234
|
+
---
|
|
152
235
|
|
|
153
236
|
## Troubleshooting
|
|
154
237
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
238
|
+
| Symptom | Fix |
|
|
239
|
+
|---------|-----|
|
|
240
|
+
| Plugin not loading | Check `opencode.json` entry. Restart Desktop. |
|
|
241
|
+
| Model won't switch | `trinity rebuild` then `trinity set brain\|medium\|cheap` |
|
|
242
|
+
| Writes/edits blocked | Enforcement active — delegate to cheap tier |
|
|
243
|
+
| No footer visible | Verify plugin enabled, completions running |
|
|
244
|
+
| Dashboard blank | `npm run build` then restart |
|
|
245
|
+
| State looks wrong | `trinity diagnose` then `trinity repair-state preview` |
|
|
162
246
|
|
|
163
|
-
|
|
247
|
+
---
|
|
164
248
|
|
|
165
|
-
|
|
166
|
-
- The README stays intentionally high level so the command details can follow the code without a rewrite.
|
|
249
|
+
*`trinity help` is the canonical command reference. This README stays high-level so command details follow the code without a rewrite.*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeostheog",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.9",
|
|
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",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"deploy": "node scripts/deploy.mjs",
|
|
13
13
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
14
14
|
"checkpoint:validate": "node scripts/checkpoint-validate.mjs",
|
|
15
|
-
"test:scripts": "node --test scripts/tests/checkpoint-validate.test.mjs",
|
|
15
|
+
"test:scripts": "node --test scripts/tests/checkpoint-validate.test.mjs tests/release-pack.test.mjs",
|
|
16
16
|
"ts:audit": "node scripts/ts-audit.mjs",
|
|
17
17
|
"test": "VIBEOS_MCP_PORT=0 node --test --test-timeout=240000 tests/deep_integration.test.mjs tests/production_regressions.test.mjs tests/release_hardening_tigerteam.test.mjs tests/test_api_migration.neutral.test.mjs tests/test_const_assignment_regression.test.mjs tests/test_delegation_enforcer.test.mjs tests/test_diagnose_cmd.test.mjs tests/test_install_and_recovery.test.mjs tests/test_internals_stress_patterns_offtopic.test.mjs tests/test_saveos_e2e_cleanup.test.mjs tests/test_tdd_enforcer.test.mjs src/tests/*.test.js src/utils/tests/*.test.mjs \"src/vibeOS-lib/tests/!(test_blackbox*).test.mjs\"",
|
|
18
18
|
"test:ci": "VIBEOS_MCP_PORT=0 node --test --test-timeout=30000 tests/production_regressions.test.mjs tests/release_hardening_tigerteam.test.mjs tests/test_const_assignment_regression.test.mjs tests/test_diagnose_cmd.test.mjs tests/test_install_and_recovery.test.mjs tests/test_saveos_e2e_cleanup.test.mjs tests/test_tdd_enforcer.test.mjs src/tests/*.test.js src/utils/tests/*.test.mjs \"src/vibeOS-lib/tests/!(test_blackbox*).test.mjs\"",
|
|
@@ -85,5 +85,8 @@
|
|
|
85
85
|
"eslint": "^10.4.0",
|
|
86
86
|
"express": "^5.2.1",
|
|
87
87
|
"typescript": "^5.9.3"
|
|
88
|
+
},
|
|
89
|
+
"dependencies": {
|
|
90
|
+
"vibeoscore": "file:vibeoscore-1.0.2.tgz"
|
|
88
91
|
}
|
|
89
92
|
}
|
package/scripts/deploy.mjs
CHANGED
|
@@ -9,9 +9,13 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
|
9
9
|
const ROOT = join(__dirname, "..")
|
|
10
10
|
|
|
11
11
|
const srcPath = join(ROOT, "src", "index.js")
|
|
12
|
+
const srcLibPath = join(ROOT, "src", "lib")
|
|
13
|
+
const srcUtilsPath = join(ROOT, "src", "utils")
|
|
12
14
|
const srcLibDir = join(ROOT, "src", "vibeOS-lib")
|
|
13
15
|
const pluginDir = join(homedir(), ".config", "opencode", "plugins")
|
|
14
16
|
const destPath = join(pluginDir, "vibeOS.js")
|
|
17
|
+
const destLibPath = join(pluginDir, "lib")
|
|
18
|
+
const destUtilsPath = join(pluginDir, "utils")
|
|
15
19
|
const destLibDir = join(pluginDir, "vibeOS-lib")
|
|
16
20
|
|
|
17
21
|
// vibeOS-api-server, vibeOS-mcp-server, and dashboard now live in vibeOScore package
|
|
@@ -29,6 +33,16 @@ try {
|
|
|
29
33
|
writeFileSync(destPath, src)
|
|
30
34
|
process.stderr.write(`[vibeOS deploy] src/index.js -> ~/.config/opencode/plugins/vibeOS.js (${src.length} bytes)\n`)
|
|
31
35
|
|
|
36
|
+
if (existsSync(srcLibPath)) {
|
|
37
|
+
cpSync(srcLibPath, destLibPath, { recursive: true, force: true })
|
|
38
|
+
process.stderr.write(`[vibeOS deploy] src/lib/ -> ~/.config/opencode/plugins/lib/\n`)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (existsSync(srcUtilsPath)) {
|
|
42
|
+
cpSync(srcUtilsPath, destUtilsPath, { recursive: true, force: true })
|
|
43
|
+
process.stderr.write(`[vibeOS deploy] src/utils/ -> ~/.config/opencode/plugins/utils/\n`)
|
|
44
|
+
}
|
|
45
|
+
|
|
32
46
|
// Copy vibeOS-lib directory recursively (includes blackbox, utils, etc.)
|
|
33
47
|
// Copy vibeOS-lib directory recursively (includes blackbox, utils, etc.)
|
|
34
48
|
let libCount = 0
|
package/src/lib/constants.js
CHANGED
|
@@ -6,7 +6,7 @@ export const SAVE_EST = {
|
|
|
6
6
|
SOFT_QUOTA: 0.0001,
|
|
7
7
|
// DeepSeek cache: (0.14 - 0.0028)/1M * ~1000 tokens = 0.00014
|
|
8
8
|
CONTEXT7: 0.00014,
|
|
9
|
-
OPUS_DISABLE:
|
|
9
|
+
OPUS_DISABLE: 1e-10,
|
|
10
10
|
};
|
|
11
11
|
export const WARN_ON_DIRECT = new Set(["write", "edit", "notebookedit"]);
|
|
12
12
|
export const SOFT_QUOTA = new Set(["bash", "glob", "grep", "read", "webfetch", "websearch"]);
|
package/src/lib/hooks/footer.js
CHANGED
|
@@ -193,8 +193,8 @@ async function _appendFooter(input, output, directory) {
|
|
|
193
193
|
if (!liveModel) {
|
|
194
194
|
liveModel = readConfig(directory) || readConfig(join(process.env.HOME || "", ".config", "opencode")) || process?.env?.OPENCODE_MODEL || "";
|
|
195
195
|
}
|
|
196
|
-
const displayModel = resolveDisplayModelId(liveModel ||
|
|
197
|
-
const execution = resolveExecutionIdentity(input?.args?.model || liveModel ||
|
|
196
|
+
const displayModel = resolveDisplayModelId(liveModel || brainModel || currentModel || "", directory) || liveModel || brainModel || currentModel;
|
|
197
|
+
const execution = resolveExecutionIdentity(input?.args?.model || liveModel || brainModel || currentModel || displayModel || "", directory);
|
|
198
198
|
let modelTag = `[${shortModelName(displayModel)}]`;
|
|
199
199
|
const _workerModel = slot === "brain" ? TRINITY_MEDIUM : null;
|
|
200
200
|
const totalTurns = (sesModelTurns?.brain || 0) + (sesModelTurns?.worker || 0);
|
|
@@ -263,7 +263,7 @@ async function _appendFooter(input, output, directory) {
|
|
|
263
263
|
subRegime: _latestBlackboxState?.sub_regime || classifyTurnSimple(latestUserIntent || ""),
|
|
264
264
|
stress: _footerStress,
|
|
265
265
|
}).mode;
|
|
266
|
-
const stripped = text.replace(
|
|
266
|
+
const stripped = text.replace(/— .+?VIBE[^—]*—\s*/g, "").trimEnd();
|
|
267
267
|
if (stripped !== text)
|
|
268
268
|
return;
|
|
269
269
|
const ltTotal = ltTasks + ltCache;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import { readFileSync, existsSync } from "node:fs";
|
|
3
|
-
import { loadSelection, getSessionScratchpadDir, getSessionIndexPath } from "../state.js";
|
|
3
|
+
import { loadSelection, _OC_SID, updateState, getSessionScratchpadDir, getSessionIndexPath } from "../state.js";
|
|
4
4
|
import { getTurnCounter } from "../turn-classify.js";
|
|
5
5
|
export const onSessionCompacting = async (_input, output) => {
|
|
6
6
|
if (!loadSelection().enabled)
|
|
@@ -57,6 +57,24 @@ export const onSessionCompacting = async (_input, output) => {
|
|
|
57
57
|
else if (output) {
|
|
58
58
|
output.context = contextEntries;
|
|
59
59
|
}
|
|
60
|
+
// Persist last_compacted_at so telemetry reflects actual compaction
|
|
61
|
+
if (needsCompact && output) {
|
|
62
|
+
try {
|
|
63
|
+
updateState((state) => {
|
|
64
|
+
const now = new Date().toISOString();
|
|
65
|
+
state.sessions ??= {};
|
|
66
|
+
const sid = _OC_SID;
|
|
67
|
+
state.sessions[sid] ??= {};
|
|
68
|
+
state.sessions[sid].telemetry ??= {};
|
|
69
|
+
state.sessions[sid].telemetry.last_compacted_at = now;
|
|
70
|
+
state.lifetime ??= {};
|
|
71
|
+
state.lifetime.telemetry ??= {};
|
|
72
|
+
state.lifetime.telemetry.last_compacted_at = now;
|
|
73
|
+
return state;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
catch { }
|
|
77
|
+
}
|
|
60
78
|
}
|
|
61
79
|
catch (err) {
|
|
62
80
|
console.error(`[vibeOS] session.compacting failed: ${err.message}`);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import { writeFileSync, appendFileSync, existsSync, mkdirSync } from "node:fs";
|
|
3
3
|
import { join, dirname, basename } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { currentTier, currentModel, setCurrentModel, setCurrentTier, _OC_SID, _modelLocked, loadSelection, readLifetimeSavings, recordCacheSaving, recordMissedContext7, getScratchpadHit, recordScratchpadObservation, recordPrivacyTelemetry, updateState, getSessionScratchpadDir, ensureSessionScratchpadDirs, SAVINGS_LEDGER_FILE, CONTEXT7_INSTALL_FLAG, SOFT_QUOTA_LIMIT, upsertTodo, ML_ENABLED, _mlGraph, _cacheDb, _mlSavePending, ML_CONFIDENCE_THRESHOLD, setMlSavePending, saveMLState, SCRATCHPAD_TOOLS, SCRATCHPAD_GLOBAL_DIR, TOOL_NAME_NORMALIZE, stableJson, applyDecadence, } from "../state.js";
|
|
5
6
|
import { classify, modelCostPerTurn, isModelFree, detectContext7, isDocsTarget, shortModelName, formatUsd, _refreshModel, readConfig, resolveDisplayModelId, TRINITY_CHEAP, TRINITY_MEDIUM, trendDisplay, modelToSlotLabel, resolveExecutionIdentity, formatProviderName, formatQualityName, } from "../pricing.js";
|
|
6
7
|
import { latestUserIntent } from "./chat-transform.js";
|
|
7
8
|
import { scoreStress, extractFirstWordFromArgs, shouldLogWarn, isUserAskingForTests, resolveEnforcementMode, getLearnedExploratoryWords, noteTaskRoutingLearning, } from "../turn-classify.js";
|
|
@@ -272,8 +273,46 @@ export const onToolExecuteBefore = async (input, output) => {
|
|
|
272
273
|
recordCacheStats(_cacheDb, t, !!hit, hit ? _cacheSave : 0);
|
|
273
274
|
if (!hit) {
|
|
274
275
|
const prediction = predictCacheHit(_cacheDb, t, promptText);
|
|
275
|
-
if (prediction.shouldWarm && prediction.confidence >= 0.6 &&
|
|
276
|
-
|
|
276
|
+
if (prediction.shouldWarm && prediction.confidence >= 0.6 && prediction.similarEntries.length > 0) {
|
|
277
|
+
try {
|
|
278
|
+
const titleCase = TOOL_NAME_NORMALIZE[t];
|
|
279
|
+
if (titleCase) {
|
|
280
|
+
const argsJson = stableJson(args ?? inArgs ?? {});
|
|
281
|
+
const curHash = createHash("sha256").update(`${titleCase}\n${argsJson}\n`).digest("hex").slice(0, 16);
|
|
282
|
+
const sessionDir = getSessionScratchpadDir();
|
|
283
|
+
const globalDir = SCRATCHPAD_GLOBAL_DIR;
|
|
284
|
+
const ptrPath = join(sessionDir, `${curHash}.ptr`);
|
|
285
|
+
if (!existsSync(ptrPath)) {
|
|
286
|
+
for (const similar of prediction.similarEntries) {
|
|
287
|
+
const targetHash = similar.entry.hash;
|
|
288
|
+
if (targetHash.length < 16)
|
|
289
|
+
continue;
|
|
290
|
+
const cachedFile = join(sessionDir, `${targetHash}.txt`);
|
|
291
|
+
const globalFile = join(globalDir, `${targetHash}.txt`);
|
|
292
|
+
if (existsSync(cachedFile) || existsSync(globalFile)) {
|
|
293
|
+
ensureSessionScratchpadDirs();
|
|
294
|
+
writeFileSync(ptrPath, JSON.stringify({
|
|
295
|
+
contentHash: targetHash,
|
|
296
|
+
tool: titleCase,
|
|
297
|
+
warmed: true,
|
|
298
|
+
at: new Date().toISOString(),
|
|
299
|
+
confidence: prediction.confidence,
|
|
300
|
+
reason: prediction.reason,
|
|
301
|
+
}));
|
|
302
|
+
if (DEBUG_INTERNALS) {
|
|
303
|
+
console.error(`[vibeOS] 🔮 Smart cache: warmed ${t} → ${targetHash.slice(0, 8)} (conf: ${(prediction.confidence * 100).toFixed(0)}%)`);
|
|
304
|
+
}
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
catch (warmErr) {
|
|
312
|
+
if (DEBUG_INTERNALS) {
|
|
313
|
+
console.error(`[vibeOS] Smart cache warming error: ${warmErr.message}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
277
316
|
}
|
|
278
317
|
}
|
|
279
318
|
}
|
|
@@ -420,12 +459,10 @@ export const onToolExecuteBefore = async (input, output) => {
|
|
|
420
459
|
const _workerModel = TRINITY_CHEAP || TRINITY_MEDIUM || null;
|
|
421
460
|
const _workerCost = _workerModel ? (modelCostPerTurn(_workerModel) ?? 0) : 0;
|
|
422
461
|
// Keep precision high to avoid dropping tiny but real per-event savings to zero.
|
|
423
|
-
const _rawEdit = _brainCost
|
|
424
|
-
? Math.max(0, _brainCost - _workerCost)
|
|
425
|
-
: SAVE_EST.WRITE_EDIT;
|
|
462
|
+
const _rawEdit = Math.max(0, _brainCost - _workerCost);
|
|
426
463
|
const _estEdit = Math.max(_rawEdit, SAVE_EST.WRITE_EDIT * 0.1);
|
|
427
|
-
const _estOpus =
|
|
428
|
-
const _estC7 =
|
|
464
|
+
const _estOpus = Math.max(_brainCost, _estEdit);
|
|
465
|
+
const _estC7 = Math.max(_brainCost, SAVE_EST.CONTEXT7);
|
|
429
466
|
const _tierWord = currentTier === "high" ? "Brain" : currentTier === "mid" ? "Medium" : "Budget";
|
|
430
467
|
const _firstWord = extractFirstWordFromArgs(t, args || inArgs);
|
|
431
468
|
const sel = loadSelection();
|
|
@@ -452,7 +489,7 @@ export const onToolExecuteBefore = async (input, output) => {
|
|
|
452
489
|
const total = recordSaving(t, "credit<40% high-tier", _estOpus, { firstWord: _firstWord });
|
|
453
490
|
const trend = trendDisplay(readLifetimeSavings().sesTrend);
|
|
454
491
|
const msg = `⚠ [vibeOS] Credit: ${_credit}% — switching to medium saves ~$${_estOpus.toFixed(3)}/turn. Run \`trinity medium\`.`;
|
|
455
|
-
if (shouldLogWarn(`${t}|credit|${_tierWord}`) &&
|
|
492
|
+
if (shouldLogWarn(`${t}|credit|${_tierWord}`) && process.env.VIBEOS_DEBUG_DELEGATION === "1") {
|
|
456
493
|
console.error(`[vibeOS] [delegation] ${msg}`);
|
|
457
494
|
}
|
|
458
495
|
pendingUiNote = msg;
|
|
@@ -461,7 +498,8 @@ export const onToolExecuteBefore = async (input, output) => {
|
|
|
461
498
|
// Write/Edit/NotebookEdit: enforce delegation on high tier when delegation_enforce is on.
|
|
462
499
|
if (WARN_ON_DIRECT.has(String(t || "").toLowerCase())) {
|
|
463
500
|
const argSources = _toolArgSources(input, output);
|
|
464
|
-
|
|
501
|
+
if (process.env.VIBEOS_DEBUG_DELEGATION === "1")
|
|
502
|
+
console.error(`[vibeOS] [enforce-debug] tool=${t} tier=${currentTier} enforce=${sel?.delegation_enforce} argsType=${typeof args} argsExists=${argSources.length > 0}`);
|
|
465
503
|
const tLower = String(t || "").toLowerCase();
|
|
466
504
|
if (!compatibilityMode && sel.delegation_enforce && currentTier === "high" && argSources.length > 0) {
|
|
467
505
|
const originalPath = argSources
|
|
@@ -487,7 +525,7 @@ export const onToolExecuteBefore = async (input, output) => {
|
|
|
487
525
|
const total = recordSaving(t, "direct edit", _estEdit, { firstWord: _firstWord });
|
|
488
526
|
if (!compatibilityMode) {
|
|
489
527
|
const msg = `[vibeOS] ${_tierWord} tier direct ${t} — save ~$${_estEdit.toFixed(3)} by delegating to Task. Run \`trinity medium\`.`;
|
|
490
|
-
if (shouldLogWarn(`${t}|direct|${_tierWord}`) &&
|
|
528
|
+
if (shouldLogWarn(`${t}|direct|${_tierWord}`) && process.env.VIBEOS_DEBUG_DELEGATION === "1") {
|
|
491
529
|
console.error(`[vibeOS] [delegation] ${msg}`);
|
|
492
530
|
}
|
|
493
531
|
pendingUiNote = msg;
|
|
@@ -618,13 +656,12 @@ export const onToolExecuteAfter = async (input, output) => {
|
|
|
618
656
|
liveModel = readConfig(projectDirectory) || readConfig(join(process.env.HOME || "", ".config", "opencode")) || process?.env?.OPENCODE_MODEL || "";
|
|
619
657
|
}
|
|
620
658
|
const displayModel = resolveDisplayModelId(liveModel || currentModel || "", projectDirectory) || liveModel || currentModel;
|
|
659
|
+
const execution = resolveExecutionIdentity(input?.args?.model || liveModel || currentModel || displayModel || "", projectDirectory);
|
|
660
|
+
_footerText = `— ${flashIcon ? `${flashIcon} ` : ""}Quality: ${formatQualityName(execution.quality)} | Provider: ${formatProviderName(execution.provider)} | Model: ${execution.model}`;
|
|
621
661
|
if (ltTotal > 0) {
|
|
622
|
-
|
|
623
|
-
_footerText = `— ${flashIcon ? `${flashIcon} ` : ""}Quality: ${formatQualityName(execution.quality)} | Provider: ${formatProviderName(execution.provider)} | Model: ${execution.model} | $${formatUsd(ltTotal)} saved | VIBE${flashIcon ? " ⚡" : ""} —\n\n`;
|
|
624
|
-
}
|
|
625
|
-
else {
|
|
626
|
-
_footerText = `${statusLine}${stressTag}\n\n`;
|
|
662
|
+
_footerText += ` | $${formatUsd(ltTotal)} saved`;
|
|
627
663
|
}
|
|
664
|
+
_footerText += ` | VIBE${flashIcon ? " ⚡" : ""} —\n\n`;
|
|
628
665
|
output.title = _footerText.trim();
|
|
629
666
|
if (typeof output?.output === "string")
|
|
630
667
|
output.output = _footerText + output.output;
|
package/src/lib/pricing.js
CHANGED
|
@@ -521,7 +521,7 @@ export function modelCostPerTurn(model) {
|
|
|
521
521
|
}
|
|
522
522
|
// Log unknown models so we can add entries
|
|
523
523
|
console.error(`[vibeOS] modelCostPerTurn: unknown model '${model}' (normalized: '${key}') — add to MODEL_USD_PER_TURN`);
|
|
524
|
-
return
|
|
524
|
+
return FREE_MODEL_TURN_USD;
|
|
525
525
|
}
|
|
526
526
|
export function isModelFree(model) {
|
|
527
527
|
if (!model || typeof model !== "string")
|
|
@@ -531,7 +531,7 @@ export function isModelFree(model) {
|
|
|
531
531
|
if (FREE_MODELS.has(normalizeModelId(model)))
|
|
532
532
|
return true;
|
|
533
533
|
const cost = modelCostPerTurn(model);
|
|
534
|
-
return cost
|
|
534
|
+
return cost <= FREE_MODEL_TURN_USD;
|
|
535
535
|
}
|
|
536
536
|
// Context7 detection — scan known config files for the string "context7".
|
|
537
537
|
// Cheap (one-time at module load); falsy → docs nudge stays dormant.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 vibeOS <https://github.com/DrunkkToys/vibeOS>
|
|
3
|
+
// @ts-nocheck
|
|
3
4
|
// Advice Layer — translates internal metrics into human-readable guidance.
|
|
4
5
|
// Ported from theWay: src/decision/advice_layer.py
|
|
5
6
|
import { FALLBACK_PLANS, ACTION_SUGGESTIONS, CURIOSITY_PROMPTS } from "./crew-constants.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 vibeOS <https://github.com/DrunkkToys/vibeOS>
|
|
3
|
+
// @ts-nocheck
|
|
3
4
|
// Crew Constants — action narratives, fallback plans, curiosity prompts.
|
|
4
5
|
// Ported from theWay: src/decision/orch/crew_constants.py
|
|
5
6
|
export const ACTION_TARGET = {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 vibeOS <https://github.com/DrunkkToys/vibeOS>
|
|
3
|
+
// @ts-nocheck
|
|
3
4
|
// Exposure Model — inverse uncertainty mapping with guidance dict.
|
|
4
5
|
// Ported from theWay: src/decision/exposure.py
|
|
5
6
|
export class ExposureModel {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 vibeOS <https://github.com/DrunkkToys/vibeOS>
|
|
3
|
+
// @ts-nocheck
|
|
3
4
|
// Blackbox — theWay decision core ported to TypeScript.
|
|
4
5
|
// Barrel export for all blackbox modules.
|
|
5
6
|
export { buildAdvice, buildDecisionBlock, computeModality, humanReadableAction, compressMetrics, compressUncertainty, compressEntropy, enforceClosure, stabilityScore, shouldUseFastPath, buildCautionNote, scoreUsefulness, getFallbackPlan, getActionSuggestion, getCuriosityPrompt, } from "./advice-layer.js";
|
|
@@ -8,3 +9,5 @@ export { ResolutionTracker } from "./resolution-tracker.js";
|
|
|
8
9
|
export { ExposureModel } from "./exposure-model.js";
|
|
9
10
|
export { ACTION_TARGET, ACTION_TYPE, FALLBACK_PLANS, ACTION_SUGGESTIONS, CURIOSITY_PROMPTS, } from "./crew-constants.js";
|
|
10
11
|
export { computeControlVector, buildControlHistoryEntry, REGIME_CONTROL_TABLE, } from "./meta-controller.js";
|
|
12
|
+
export { vibemaxSelectMode, vibemaxPipeline, predictVibeMaX, trainVibeMaXModelFromTelemetry, getVibeMaXModelMeta, resetVibeMaXPipeline } from "./vibemax.js";
|
|
13
|
+
export { PivotCache } from "./pivot-cache.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 vibeOS <https://github.com/DrunkkToys/vibeOS>
|
|
3
|
+
// @ts-nocheck
|
|
3
4
|
// Local blackbox stub — minimal degraded-mode implementation.
|
|
4
5
|
// The full engine runs on the API server; this stub covers offline fallback.
|
|
5
6
|
class LocalBlackboxStub {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 vibeOS <https://github.com/DrunkkToys/vibeOS>
|
|
3
|
+
// @ts-nocheck
|
|
3
4
|
// Meta-Controller — maps blackbox resolution state to a unified control vector.
|
|
4
5
|
// v2 orchestration: single source of truth for all subsystem directives.
|
|
5
6
|
// v3: OptimizationMode system — 4 session-level profiles + auto mode.
|
|
@@ -159,6 +160,21 @@ const MODE_DELTAS = {
|
|
|
159
160
|
api_enrichment: true,
|
|
160
161
|
outcome_detection: true,
|
|
161
162
|
},
|
|
163
|
+
vibemax: {
|
|
164
|
+
tier_bias: "medium",
|
|
165
|
+
thinking_mode: "full",
|
|
166
|
+
tdd_mode: "quality",
|
|
167
|
+
tdd_focus: ["skeleton-on-write", "assertion-check", "edge-cases"],
|
|
168
|
+
flow_mode: "strict",
|
|
169
|
+
flow_focus: ["write-edit-check", "no-lgtm", "check-debug-artifacts"],
|
|
170
|
+
enforcement_mode: "strict",
|
|
171
|
+
wbp_verbosity: "normal",
|
|
172
|
+
context7_urgency: "required",
|
|
173
|
+
stress_multiplier: 1.0,
|
|
174
|
+
loop_threshold: 0.6,
|
|
175
|
+
api_enrichment: true,
|
|
176
|
+
outcome_detection: true,
|
|
177
|
+
},
|
|
162
178
|
};
|
|
163
179
|
export function autoSelectMode(subRegime, stressMultiplier) {
|
|
164
180
|
if (subRegime === "CONVERGING" || subRegime === "CLOSED")
|
|
@@ -173,10 +189,15 @@ export function computeControlVector(state, action, optimizationMode) {
|
|
|
173
189
|
const regime = state.sub_regime || "INIT";
|
|
174
190
|
const base = REGIME_CONTROL[regime] || DEFAULT_CONTROL;
|
|
175
191
|
// Determine effective mode
|
|
176
|
-
let effectiveMode = optimizationMode || "
|
|
192
|
+
let effectiveMode = optimizationMode || "vibemax";
|
|
177
193
|
if (effectiveMode === "auto") {
|
|
178
194
|
effectiveMode = autoSelectMode(regime, state.latest_stress_multiplier);
|
|
179
195
|
}
|
|
196
|
+
if (effectiveMode === "vibemax") {
|
|
197
|
+
const baseMode = autoSelectMode(regime, state.latest_stress_multiplier);
|
|
198
|
+
const vibemaxQuality = ["quality", "longrun", "audit"];
|
|
199
|
+
effectiveMode = vibemaxQuality.includes(baseMode) ? "vibemax" : "budget";
|
|
200
|
+
}
|
|
180
201
|
// Apply mode deltas on top of base (only for non-balanced modes)
|
|
181
202
|
const delta = effectiveMode !== "balanced" ? (MODE_DELTAS[effectiveMode] || {}) : {};
|
|
182
203
|
const overridden = {
|
|
@@ -210,6 +231,8 @@ function describeMode(delta) {
|
|
|
210
231
|
return "longrun mode — codebase health";
|
|
211
232
|
if (delta.tier_bias === "medium" && delta.stress_multiplier === 0)
|
|
212
233
|
return "speed mode — max response speed";
|
|
234
|
+
if (delta.tier_bias === "medium" && delta.loop_threshold === 0.6)
|
|
235
|
+
return "vibemax mode — ml-optimized budget: 97% quality at 37% cost";
|
|
213
236
|
return `${delta.tier_bias || "auto"} / ${delta.thinking_mode || "auto"}`;
|
|
214
237
|
}
|
|
215
238
|
function buildDirectives(cv, regime, state, action, optimizationMode) {
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
export class PivotCache {
|
|
5
|
+
store;
|
|
6
|
+
baseDir;
|
|
7
|
+
pivotSequence;
|
|
8
|
+
currentWorkflow;
|
|
9
|
+
lastTokens;
|
|
10
|
+
constructor(baseDir) {
|
|
11
|
+
this.baseDir = baseDir || join(homedir(), ".claude");
|
|
12
|
+
this.pivotSequence = [];
|
|
13
|
+
this.currentWorkflow = null;
|
|
14
|
+
this.lastTokens = new Set();
|
|
15
|
+
this.store = this._load();
|
|
16
|
+
}
|
|
17
|
+
_storePath() {
|
|
18
|
+
return join(this.baseDir, ".vibeos-pivot-cache.json");
|
|
19
|
+
}
|
|
20
|
+
_load() {
|
|
21
|
+
try {
|
|
22
|
+
const p = this._storePath();
|
|
23
|
+
if (existsSync(p)) {
|
|
24
|
+
return JSON.parse(readFileSync(p, "utf-8"));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch { /* ignore */ }
|
|
28
|
+
return { pivots: {}, version: 3 };
|
|
29
|
+
}
|
|
30
|
+
save() {
|
|
31
|
+
try {
|
|
32
|
+
const p = this._storePath();
|
|
33
|
+
const dir = dirname(p);
|
|
34
|
+
if (!existsSync(dir))
|
|
35
|
+
mkdirSync(dir, { recursive: true });
|
|
36
|
+
writeFileSync(p, JSON.stringify(this.store, null, 2), "utf-8");
|
|
37
|
+
}
|
|
38
|
+
catch { /* ignore */ }
|
|
39
|
+
}
|
|
40
|
+
tokenize(text) {
|
|
41
|
+
const tl = text.toLowerCase();
|
|
42
|
+
const tokens = new Set();
|
|
43
|
+
if (/deploy|redeploy|bundle|release|npm/.test(tl))
|
|
44
|
+
tokens.add("deploy");
|
|
45
|
+
if (/(?:\bgit\b|\bcommit\b|\bpush\b|\bmerge\b|\bpr\b|\bpull\b|\brebase\b)/.test(tl))
|
|
46
|
+
tokens.add("git");
|
|
47
|
+
if (/budget|cost|price|pricing/.test(tl))
|
|
48
|
+
tokens.add("pricing");
|
|
49
|
+
if (/debug|fix|bug|error|broken/.test(tl))
|
|
50
|
+
tokens.add("debug");
|
|
51
|
+
if (/context|cache|pivot|compression/.test(tl))
|
|
52
|
+
tokens.add("caching");
|
|
53
|
+
if (/test|experiment|verify|validate/.test(tl))
|
|
54
|
+
tokens.add("test");
|
|
55
|
+
if (/config|token|api|secret|env|auth/.test(tl))
|
|
56
|
+
tokens.add("config");
|
|
57
|
+
if (/create|add|implement|build|write/.test(tl))
|
|
58
|
+
tokens.add("create");
|
|
59
|
+
if (/read|check|see|show|status|list/.test(tl))
|
|
60
|
+
tokens.add("inspect");
|
|
61
|
+
if (/refactor|clean|rename|move|restructure/.test(tl))
|
|
62
|
+
tokens.add("refactor");
|
|
63
|
+
if (tokens.size === 0)
|
|
64
|
+
tokens.add("misc");
|
|
65
|
+
return tokens;
|
|
66
|
+
}
|
|
67
|
+
detectPivot(current, previous, timeGap = 0) {
|
|
68
|
+
const cur = this.tokenize(current);
|
|
69
|
+
const prev = this.tokenize(previous);
|
|
70
|
+
const inter = new Set([...cur].filter(x => prev.has(x)));
|
|
71
|
+
const union = new Set([...cur, ...prev]);
|
|
72
|
+
const sim = union.size === 0 ? 1 : inter.size / union.size;
|
|
73
|
+
const timePenalty = Math.min(0.3, timeGap / 600);
|
|
74
|
+
const adjusted = sim - timePenalty;
|
|
75
|
+
return { isPivot: adjusted < 0.3, similarity: Math.round(adjusted * 1000) / 1000 };
|
|
76
|
+
}
|
|
77
|
+
snapshot(workflowId, context) {
|
|
78
|
+
const entry = {
|
|
79
|
+
id: workflowId,
|
|
80
|
+
captured_at: new Date().toISOString(),
|
|
81
|
+
tokens: context.tokens || [],
|
|
82
|
+
intent: context.intent || "",
|
|
83
|
+
decisions: context.decisions || [],
|
|
84
|
+
files: context.files || [],
|
|
85
|
+
code_snippets: context.code_snippets || [],
|
|
86
|
+
blockers: context.blockers || [],
|
|
87
|
+
access_count: 0,
|
|
88
|
+
useful_sections: ["decisions", "files"],
|
|
89
|
+
skip_sections: [],
|
|
90
|
+
};
|
|
91
|
+
this.store.pivots[workflowId] = entry;
|
|
92
|
+
if (!this.pivotSequence.includes(workflowId)) {
|
|
93
|
+
this.pivotSequence.push(workflowId);
|
|
94
|
+
}
|
|
95
|
+
this.save();
|
|
96
|
+
}
|
|
97
|
+
detectPivotBack(tokens, confidenceThreshold = 0.5) {
|
|
98
|
+
if (this.pivotSequence.length < 2) {
|
|
99
|
+
return { matchedId: null, confidence: 0, reason: "not_enough_pivots" };
|
|
100
|
+
}
|
|
101
|
+
const candidates = [];
|
|
102
|
+
for (let i = 0; i < this.pivotSequence.length; i++) {
|
|
103
|
+
const pid = this.pivotSequence[i];
|
|
104
|
+
if (pid === this.pivotSequence[this.pivotSequence.length - 1])
|
|
105
|
+
continue;
|
|
106
|
+
const entry = this.store.pivots[pid];
|
|
107
|
+
if (!entry)
|
|
108
|
+
continue;
|
|
109
|
+
const cached = new Set(entry.tokens);
|
|
110
|
+
if (cached.size === 0)
|
|
111
|
+
continue;
|
|
112
|
+
const inter = new Set([...tokens].filter(x => cached.has(x)));
|
|
113
|
+
const union = new Set([...tokens, ...cached]);
|
|
114
|
+
const jaccard = union.size === 0 ? 0 : inter.size / union.size;
|
|
115
|
+
const exactBonus = tokens.size === cached.size && [...tokens].every(t => cached.has(t)) ? 0.2 : 0;
|
|
116
|
+
const recency = i / Math.max(this.pivotSequence.length, 1);
|
|
117
|
+
const accessBonus = Math.min(0.1, (entry.access_count || 0) * 0.02);
|
|
118
|
+
const confidence = jaccard + exactBonus + recency * 0.1 + accessBonus;
|
|
119
|
+
candidates.push([pid, confidence, jaccard]);
|
|
120
|
+
}
|
|
121
|
+
if (candidates.length === 0) {
|
|
122
|
+
return { matchedId: null, confidence: 0, reason: "no_candidates" };
|
|
123
|
+
}
|
|
124
|
+
candidates.sort((a, b) => b[1] - a[1]);
|
|
125
|
+
const [bestId, bestConf] = candidates[0];
|
|
126
|
+
if (bestConf < confidenceThreshold) {
|
|
127
|
+
return { matchedId: null, confidence: bestConf, reason: "low_confidence" };
|
|
128
|
+
}
|
|
129
|
+
if (this.store.pivots[bestId]) {
|
|
130
|
+
this.store.pivots[bestId].access_count = (this.store.pivots[bestId].access_count || 0) + 1;
|
|
131
|
+
}
|
|
132
|
+
this.save();
|
|
133
|
+
return { matchedId: bestId, confidence: bestConf, reason: "matched" };
|
|
134
|
+
}
|
|
135
|
+
buildInjection(workflowId, maxSections = 3) {
|
|
136
|
+
const entry = this.store.pivots[workflowId];
|
|
137
|
+
if (!entry)
|
|
138
|
+
return "";
|
|
139
|
+
const parts = [];
|
|
140
|
+
const skip = new Set(entry.skip_sections);
|
|
141
|
+
if (!skip.has("decisions") && entry.decisions.length > 0) {
|
|
142
|
+
parts.push(`[${workflowId}] ${entry.decisions.slice(0, 3).join(" | ")}`);
|
|
143
|
+
}
|
|
144
|
+
if (entry.blockers.length > 0 && !skip.has("blockers")) {
|
|
145
|
+
parts.push(`[blockers] ${entry.blockers.slice(0, 2).join(" | ")}`);
|
|
146
|
+
}
|
|
147
|
+
if (entry.code_snippets.length > 0 && entry.useful_sections.includes("code") && !skip.has("code")) {
|
|
148
|
+
parts.push(`[code] ${entry.code_snippets.slice(0, 2).join(" | ")}`);
|
|
149
|
+
}
|
|
150
|
+
return parts.join("\n");
|
|
151
|
+
}
|
|
152
|
+
learn(workflowId, usedSections, unusedSections) {
|
|
153
|
+
const entry = this.store.pivots[workflowId];
|
|
154
|
+
if (!entry)
|
|
155
|
+
return;
|
|
156
|
+
for (const s of usedSections) {
|
|
157
|
+
if (!entry.useful_sections.includes(s))
|
|
158
|
+
entry.useful_sections.push(s);
|
|
159
|
+
}
|
|
160
|
+
for (const s of unusedSections) {
|
|
161
|
+
if (!entry.skip_sections.includes(s) && (entry.access_count || 0) > 3) {
|
|
162
|
+
entry.skip_sections.push(s);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
this.save();
|
|
166
|
+
}
|
|
167
|
+
resetSequence() {
|
|
168
|
+
this.pivotSequence = [];
|
|
169
|
+
this.currentWorkflow = null;
|
|
170
|
+
this.lastTokens = new Set();
|
|
171
|
+
}
|
|
172
|
+
getRecentPivots(n = 5) {
|
|
173
|
+
return this.pivotSequence.slice(-n);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 vibeOS <https://github.com/DrunkkToys/vibeOS>
|
|
3
|
+
// @ts-nocheck
|
|
3
4
|
// Resolution Tracker — state-of-progress estimator for dialogue trajectory.
|
|
4
5
|
// Ported from theWay: src/decision/resolution_tracker.py
|
|
5
6
|
export class ResolutionTracker {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 vibeOS <https://github.com/DrunkkToys/vibeOS>
|
|
3
|
+
// @ts-nocheck
|
|
3
4
|
// Decision Taxonomy — maps human situations to appropriate action categories.
|
|
4
5
|
// Ported from theWay: src/decision/taxonomy.py
|
|
5
6
|
const SITUATION_TYPES = ["work", "relationship", "opportunity", "health", "financial"];
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { autoSelectMode } from "./meta-controller.js";
|
|
3
|
+
import { PivotCache } from "./pivot-cache.js";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { resolve, dirname } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const MODEL_PATH = process.env.VIBEOS_VIBEMAX_MODEL_PATH || resolve(__dirname, "..", "..", "..", "data", "vibemax-model.json");
|
|
9
|
+
const PRIORITY = { budget: 0, audit: 1, speed: 2, longrun: 3, quality: 4 };
|
|
10
|
+
function fallback(sr, text) {
|
|
11
|
+
if (sr === "LOOPING")
|
|
12
|
+
return "speed";
|
|
13
|
+
const t = String(text || "").toLowerCase();
|
|
14
|
+
if (sr === "INIT" && t.length <= 42 && !/[\.\/\\]/.test(t))
|
|
15
|
+
return "budget";
|
|
16
|
+
return "quality";
|
|
17
|
+
}
|
|
18
|
+
// PRNG
|
|
19
|
+
function rng(seed) {
|
|
20
|
+
let s = seed | 0;
|
|
21
|
+
return () => { s |= 0; s = s + 0x6D2B79F5 | 0; let t = Math.imul(s ^ s >>> 15, 1 | s); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296; };
|
|
22
|
+
}
|
|
23
|
+
function gini(samples, label) {
|
|
24
|
+
return 1 - samples.filter(s => s.label === label).length ** 2 / samples.length ** 2;
|
|
25
|
+
}
|
|
26
|
+
function buildTree(samples, classes, depth, maxDepth, minLeaf, rngFn) {
|
|
27
|
+
if (samples.length <= minLeaf || depth >= maxDepth || new Set(samples.map(s => s.label)).size === 1) {
|
|
28
|
+
const counts = Object.fromEntries(classes.map(c => [c, 0]));
|
|
29
|
+
for (const s of samples)
|
|
30
|
+
counts[s.label]++;
|
|
31
|
+
const total = samples.length || 1;
|
|
32
|
+
return { prediction: classes.reduce((a, b) => counts[a] > counts[b] ? a : b), probs: classes.map(c => counts[c] / total) };
|
|
33
|
+
}
|
|
34
|
+
const nFeats = samples[0]?.features?.length || 1;
|
|
35
|
+
const featSample = Math.max(2, Math.min(nFeats, Math.floor(Math.sqrt(nFeats)) + 1));
|
|
36
|
+
const cols = new Set();
|
|
37
|
+
while (cols.size < featSample)
|
|
38
|
+
cols.add(Math.floor(rngFn() * nFeats));
|
|
39
|
+
let bestG = 0, bestC = -1, bestV = 0;
|
|
40
|
+
for (const c of cols) {
|
|
41
|
+
const vals = [...new Set(samples.map(s => s.features[c]))].sort((a, b) => a - b);
|
|
42
|
+
for (const v of vals) {
|
|
43
|
+
const l = samples.filter(s => s.features[c] <= v);
|
|
44
|
+
const r = samples.filter(s => s.features[c] > v);
|
|
45
|
+
if (l.length < minLeaf || r.length < minLeaf)
|
|
46
|
+
continue;
|
|
47
|
+
const gParent = classes.reduce((sum, cl) => sum + gini(samples, cl), 0);
|
|
48
|
+
const gChild = (l.length / samples.length) * classes.reduce((sum, cl) => sum + gini(l, cl), 0) + (r.length / samples.length) * classes.reduce((sum, cl) => sum + gini(r, cl), 0);
|
|
49
|
+
const gain = gParent - gChild;
|
|
50
|
+
if (gain > bestG) {
|
|
51
|
+
bestG = gain;
|
|
52
|
+
bestC = c;
|
|
53
|
+
bestV = v;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (bestC === -1 || bestG <= 0) {
|
|
58
|
+
const counts = Object.fromEntries(classes.map(c => [c, 0]));
|
|
59
|
+
for (const s of samples)
|
|
60
|
+
counts[s.label]++;
|
|
61
|
+
const total = samples.length || 1;
|
|
62
|
+
return { prediction: classes.reduce((a, b) => counts[a] > counts[b] ? a : b), probs: classes.map(c => counts[c] / total) };
|
|
63
|
+
}
|
|
64
|
+
return { column: bestC, value: bestV, left: buildTree(samples.filter(s => s.features[bestC] <= bestV), classes, depth + 1, maxDepth, minLeaf, rngFn), right: buildTree(samples.filter(s => s.features[bestC] > bestV), classes, depth + 1, maxDepth, minLeaf, rngFn) };
|
|
65
|
+
}
|
|
66
|
+
function predictTree(tree, features) {
|
|
67
|
+
if (tree.prediction)
|
|
68
|
+
return tree;
|
|
69
|
+
return features[tree.column] <= tree.value ? predictTree(tree.left, features) : predictTree(tree.right, features);
|
|
70
|
+
}
|
|
71
|
+
const VIBEMAX_CFG = { tier: "medium", thinking: "full", tdd: "quality", flow: "strict", enforcement: "strict", wbp: "normal", c7: "required", kp: [3, 6], tc: 0.3, amode: "plan" };
|
|
72
|
+
const BUDGET_CFG = { tier: "cheap", thinking: "off", tdd: "normal", flow: "audit", enforcement: "relaxed", wbp: "minimal", c7: "skippable", kp: [1, 3], tc: 0.1, amode: "build" };
|
|
73
|
+
const VIBEMAX_MAP = { quality: "optimized", longrun: "optimized", audit: "optimized", speed: "budget", budget: "budget" };
|
|
74
|
+
// PivotCache instance
|
|
75
|
+
let pivotCache = null;
|
|
76
|
+
function getPivotCache() {
|
|
77
|
+
if (!pivotCache)
|
|
78
|
+
pivotCache = new PivotCache();
|
|
79
|
+
return pivotCache;
|
|
80
|
+
}
|
|
81
|
+
let prevMessage = "";
|
|
82
|
+
export function resetVibeMaXPipeline() {
|
|
83
|
+
prevMessage = "";
|
|
84
|
+
if (pivotCache)
|
|
85
|
+
pivotCache.resetSequence();
|
|
86
|
+
}
|
|
87
|
+
export function vibemaxSelectMode(input = {}) {
|
|
88
|
+
const stress = Number(input.stress_multiplier || input.stress || 0);
|
|
89
|
+
const pm = autoSelectMode(input.sub_regime, stress) || fallback(input.sub_regime, input.user_text || input.prompt || "");
|
|
90
|
+
const vm = VIBEMAX_MAP[pm] || "optimized";
|
|
91
|
+
if (vm === "budget") {
|
|
92
|
+
return { mode: "budget", source: "vibemax", source_prediction: pm, confidence: 0, auto_result: null, ...BUDGET_CFG, cost: 0.1 };
|
|
93
|
+
}
|
|
94
|
+
const cfg = loadVibeMaXModel()?.config || { think: "full", wbp: "normal", kp: [3, 6] };
|
|
95
|
+
const text = input.user_text || input.prompt || "";
|
|
96
|
+
// PivotCache: detect if returning to a cached workflow
|
|
97
|
+
const pc = getPivotCache();
|
|
98
|
+
const tokens = pc.tokenize(text);
|
|
99
|
+
const pivotBack = text && tokens.size > 0 ? pc.detectPivotBack(tokens, 0.5) : { matchedId: null, confidence: 0, reason: "no_text" };
|
|
100
|
+
const isPivotBack = pivotBack.matchedId !== null;
|
|
101
|
+
const think = isPivotBack ? "brief" : (cfg.think || "full");
|
|
102
|
+
const injection = isPivotBack ? pc.buildInjection(pivotBack.matchedId) : "";
|
|
103
|
+
return {
|
|
104
|
+
mode: "vibemax", source: "vibemax", source_prediction: pm, confidence: 0,
|
|
105
|
+
auto_result: null, tier: "medium", thinking: think, tdd: "quality", flow: "strict",
|
|
106
|
+
enforcement: "strict", wbp: cfg.wbp || "normal", c7: "required", kp: cfg.kp || [3, 6],
|
|
107
|
+
tc: 0.3, amode: "plan", cost: 0.3,
|
|
108
|
+
pivot: isPivotBack ? { matchedId: pivotBack.matchedId, confidence: pivotBack.confidence, injection } : null,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export function vibemaxPipeline(input = {}) {
|
|
112
|
+
const text = input.user_text || input.prompt || "";
|
|
113
|
+
const pc = getPivotCache();
|
|
114
|
+
// Detect pivot from previous message
|
|
115
|
+
const isPivot = prevMessage && text ? pc.detectPivot(text, prevMessage) : { isPivot: false, similarity: 1 };
|
|
116
|
+
// If pivot: snapshot previous workflow before switching
|
|
117
|
+
if (isPivot.isPivot && prevMessage) {
|
|
118
|
+
const prevTokens = pc.tokenize(prevMessage);
|
|
119
|
+
const prevId = "wf-" + Date.now();
|
|
120
|
+
pc.snapshot(prevId, {
|
|
121
|
+
tokens: [...prevTokens],
|
|
122
|
+
intent: prevMessage.substring(0, 60),
|
|
123
|
+
decisions: ["previous workflow captured at pivot point"],
|
|
124
|
+
files: [], code_snippets: [], blockers: [],
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const result = vibemaxSelectMode(input);
|
|
128
|
+
if (text)
|
|
129
|
+
prevMessage = text;
|
|
130
|
+
return {
|
|
131
|
+
...result,
|
|
132
|
+
pivot_detected: isPivot.isPivot || false,
|
|
133
|
+
pivot_similarity: isPivot.similarity || 1,
|
|
134
|
+
pivot_back: result.pivot?.matchedId || null,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
export function predictVibeMaX(input = {}) {
|
|
138
|
+
const r = vibemaxSelectMode(input);
|
|
139
|
+
return { label: r.mode, confidence: r.confidence, source: "vibemax", source_prediction: r.source_prediction, pivot_back: r.pivot?.matchedId || null };
|
|
140
|
+
}
|
|
141
|
+
function extractVibeMaXFeatures(text, sr) {
|
|
142
|
+
const t = (text || "").toLowerCase();
|
|
143
|
+
const words = t.split(/\s+/).filter(Boolean);
|
|
144
|
+
const f = {
|
|
145
|
+
length: text.length / 5000,
|
|
146
|
+
word_count: words.length / 500,
|
|
147
|
+
sentence_count: (text.split(/[.!?]+/).filter(s => s.trim()).length) / 50,
|
|
148
|
+
question_ratio: (text.match(/\?/g) || []).length / Math.max(text.split(/[.!?]+/).length, 1),
|
|
149
|
+
code_blocks: (text.match(/```/g) || []).length / 10,
|
|
150
|
+
urgency: /urgent|asap|immediately|critical|broken|failing|crash|error|bug/i.test(text) ? 1.0 : 0.0,
|
|
151
|
+
complexity: /complex|difficult|hard|confusing|trick|subtle|nuance/i.test(text) ? 1.0 : 0.0,
|
|
152
|
+
instruction_density: /do not|must|should|always|never|critical/i.test(text) ? 1.0 : /please|could you|maybe|perhaps/i.test(text) ? 0.3 : 0.6,
|
|
153
|
+
};
|
|
154
|
+
return {
|
|
155
|
+
...Object.fromEntries(Object.entries(f).filter(([_, v]) => typeof v === "number")),
|
|
156
|
+
word_count: words.length,
|
|
157
|
+
has_question: t.includes("?") ? 1 : 0,
|
|
158
|
+
has_debug: /debug|fix|broken|error|bug/.test(t) ? 1 : 0,
|
|
159
|
+
has_explain: /explain|what|how|why|compare|review/.test(t) ? 1 : 0,
|
|
160
|
+
has_refactor: /refactor|optimize|clean|improve/.test(t) ? 1 : 0,
|
|
161
|
+
has_short: words.length <= 3 ? 1 : 0,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function extractFeatureVector(text, sr) {
|
|
165
|
+
const feats = extractVibeMaXFeatures(text, sr);
|
|
166
|
+
return Object.values(feats).filter(v => typeof v === "number" && Number.isFinite(v));
|
|
167
|
+
}
|
|
168
|
+
export function trainVibeMaXModelFromTelemetry(telemetryPath) {
|
|
169
|
+
const raw = readFileSync(telemetryPath, "utf-8").trim();
|
|
170
|
+
const entries = raw.split("\n").filter(l => l.trim()).map(l => JSON.parse(l));
|
|
171
|
+
const fbMode = { audit: "optimized", budget: "budget", quality: "optimized", speed: "budget", longrun: "optimized" };
|
|
172
|
+
const classes = ["optimized", "budget"];
|
|
173
|
+
const samples = [];
|
|
174
|
+
for (const e of entries) {
|
|
175
|
+
const t = e.telemetry || {};
|
|
176
|
+
const text = t.input?.user_text || e.text || "";
|
|
177
|
+
const sr = t.signals?.sub_regime || t.input?.sub_regime || "INIT";
|
|
178
|
+
const mode = t.selection?.optimization_mode || t.control_vector?.optimization_mode || t.mode || "";
|
|
179
|
+
if (!text || text.length < 2)
|
|
180
|
+
continue;
|
|
181
|
+
const target = fbMode[mode] || "optimized";
|
|
182
|
+
const features = extractFeatureVector(text, sr);
|
|
183
|
+
if (features.length > 0)
|
|
184
|
+
samples.push({ features, label: target, text, sr, original_mode: mode });
|
|
185
|
+
}
|
|
186
|
+
if (samples.length < 2) {
|
|
187
|
+
const boot = [
|
|
188
|
+
// Technical / coding (16)
|
|
189
|
+
{ text: "hi", sr: "INIT", label: "budget" },
|
|
190
|
+
{ text: "what time is it", sr: "INIT", label: "budget" },
|
|
191
|
+
{ text: "show current status", sr: "INIT", label: "budget" },
|
|
192
|
+
{ text: "just give me quick answer", sr: "INIT", label: "budget" },
|
|
193
|
+
{ text: "review error handling", sr: "EXPLORING", label: "optimized" },
|
|
194
|
+
{ text: "this is broken fix it immediately", sr: "REFINING", label: "optimized" },
|
|
195
|
+
{ text: "help me debug this failing test", sr: "REFINING", label: "optimized" },
|
|
196
|
+
{ text: "we are repeating the same solution", sr: "LOOPING", label: "budget" },
|
|
197
|
+
{ text: "I need complete investigation with reasoning", sr: "RESEARCH", label: "optimized" },
|
|
198
|
+
{ text: "research the right documentation", sr: "RESEARCH", label: "optimized" },
|
|
199
|
+
{ text: "implement new feature with comprehensive tests", sr: "REFINING", label: "optimized" },
|
|
200
|
+
{ text: "lets wrap up and ship final change", sr: "CONVERGING", label: "optimized" },
|
|
201
|
+
{ text: "compare Redis vs Memcached performance", sr: "EXPLORING", label: "optimized" },
|
|
202
|
+
{ text: "search for all TODO comments", sr: "EXPLORING", label: "optimized" },
|
|
203
|
+
{ text: "whats the next step", sr: "INIT", label: "budget" },
|
|
204
|
+
{ text: "why does this keep looping", sr: "LOOPING", label: "budget" },
|
|
205
|
+
// Non-technical (20)
|
|
206
|
+
{ text: "summarize this article", sr: "INIT", label: "budget" },
|
|
207
|
+
{ text: "tell me a joke", sr: "INIT", label: "budget" },
|
|
208
|
+
{ text: "translate hello to spanish", sr: "INIT", label: "budget" },
|
|
209
|
+
{ text: "whats the weather like", sr: "INIT", label: "budget" },
|
|
210
|
+
{ text: "write a quick email", sr: "INIT", label: "budget" },
|
|
211
|
+
{ text: "draft a meeting agenda", sr: "INIT", label: "audit" },
|
|
212
|
+
{ text: "analyze this spreadsheet data and find outliers", sr: "EXPLORING", label: "optimized" },
|
|
213
|
+
{ text: "compare these two products for my purchase decision", sr: "EXPLORING", label: "optimized" },
|
|
214
|
+
{ text: "review this contract for legal issues", sr: "EXPLORING", label: "optimized" },
|
|
215
|
+
{ text: "help me brainstorm marketing ideas", sr: "DIVERGENT", label: "audit" },
|
|
216
|
+
{ text: "edit this essay for grammar and clarity", sr: "REFINING", label: "audit" },
|
|
217
|
+
{ text: "improve the structure of this presentation", sr: "REFINING", label: "optimized" },
|
|
218
|
+
{ text: "proofread this resume and suggest improvements", sr: "REFINING", label: "optimized" },
|
|
219
|
+
{ text: "create a budget spreadsheet for my startup", sr: "REFINING", label: "optimized" },
|
|
220
|
+
{ text: "write a detailed business report on market trends", sr: "RESEARCH", label: "optimized" },
|
|
221
|
+
{ text: "research competitors for my business idea", sr: "RESEARCH", label: "optimized" },
|
|
222
|
+
{ text: "study this financial model and verify projections", sr: "RESEARCH", label: "optimized" },
|
|
223
|
+
{ text: "generate a social media content calendar", sr: "REFINING", label: "optimized" },
|
|
224
|
+
{ text: "we keep going in circles on this decision", sr: "LOOPING", label: "budget" },
|
|
225
|
+
{ text: "finalize the press release", sr: "CONVERGING", label: "optimized" },
|
|
226
|
+
];
|
|
227
|
+
for (const b of boot) {
|
|
228
|
+
const features = extractFeatureVector(b.text, b.sr);
|
|
229
|
+
if (features.length > 0)
|
|
230
|
+
samples.push({ features, label: b.label, text: b.text, sr: b.sr, original_mode: b.label });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const treeCount = 29, maxDepth = 5, minLeaf = 2;
|
|
234
|
+
const rngFn = rng(42);
|
|
235
|
+
const trees = [];
|
|
236
|
+
for (let i = 0; i < treeCount; i++) {
|
|
237
|
+
const bag = [];
|
|
238
|
+
for (let j = 0; j < samples.length; j++)
|
|
239
|
+
bag.push(samples[Math.floor(rngFn() * samples.length)]);
|
|
240
|
+
trees.push(buildTree(bag, classes, 0, maxDepth, minLeaf, rngFn));
|
|
241
|
+
}
|
|
242
|
+
let correct = 0;
|
|
243
|
+
for (const s of samples) {
|
|
244
|
+
const votes = {};
|
|
245
|
+
for (const t of trees) {
|
|
246
|
+
const r = predictTree(t, s.features);
|
|
247
|
+
votes[r.prediction] = (votes[r.prediction] || 0) + 1;
|
|
248
|
+
}
|
|
249
|
+
const pred = Object.entries(votes).sort((a, b) => b[1] - a[1])[0]?.[0] || classes[0];
|
|
250
|
+
if (pred === s.label)
|
|
251
|
+
correct++;
|
|
252
|
+
}
|
|
253
|
+
const model = { trees, classes, trained_at: new Date().toISOString(), samples: samples.length, metrics: { accuracy: correct / Math.max(samples.length, 1), total_samples: samples.length }, config: { think: "full", wbp: "normal", kp: [3, 6] } };
|
|
254
|
+
saveVibeMaXModel(model);
|
|
255
|
+
return model;
|
|
256
|
+
}
|
|
257
|
+
export function loadVibeMaXModel() {
|
|
258
|
+
if (existsSync(MODEL_PATH))
|
|
259
|
+
return JSON.parse(readFileSync(MODEL_PATH, "utf-8"));
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
export function saveVibeMaXModel(model) {
|
|
263
|
+
mkdirSync(dirname(MODEL_PATH), { recursive: true });
|
|
264
|
+
writeFileSync(MODEL_PATH, JSON.stringify(model, null, 2) + "\n", "utf-8");
|
|
265
|
+
}
|
|
266
|
+
export function getVibeMaXModelMeta() {
|
|
267
|
+
const m = loadVibeMaXModel();
|
|
268
|
+
if (!m)
|
|
269
|
+
return { available: false, path: MODEL_PATH, message: "not trained" };
|
|
270
|
+
return { available: true, path: MODEL_PATH, trained_at: m.trained_at, accuracy: m.metrics?.accuracy, samples: m.samples, trees: m.trees?.length, classes: m.classes };
|
|
271
|
+
}
|
|
@@ -3,6 +3,39 @@
|
|
|
3
3
|
import { readFileSync, existsSync, mkdirSync, writeFileSync, statSync, appendFileSync, renameSync } from "node:fs";
|
|
4
4
|
import { join, dirname } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
const VIBEOS_STDERR_DEBUG = process.env.VIBEOS_DEBUG_STDERR === "1" || process.env.VIBEOS_DEBUG_LOGS === "1";
|
|
7
|
+
const VIBEOS_CONSOLE_ERROR_GUARD = "__vibeOSConsoleErrorGuard";
|
|
8
|
+
const globalConsoleState = globalThis;
|
|
9
|
+
if (!VIBEOS_STDERR_DEBUG && !globalConsoleState[VIBEOS_CONSOLE_ERROR_GUARD]) {
|
|
10
|
+
const originalConsoleError = console.error.bind(console);
|
|
11
|
+
console.error = (...args) => {
|
|
12
|
+
let text = "";
|
|
13
|
+
for (const arg of args) {
|
|
14
|
+
if (typeof arg === "string") {
|
|
15
|
+
text += arg;
|
|
16
|
+
}
|
|
17
|
+
else if (arg instanceof Error) {
|
|
18
|
+
text += `${arg.name}: ${arg.message}`;
|
|
19
|
+
}
|
|
20
|
+
else if (arg && typeof arg === "object") {
|
|
21
|
+
try {
|
|
22
|
+
text += JSON.stringify(arg);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
text += String(arg);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
text += String(arg);
|
|
30
|
+
}
|
|
31
|
+
text += " ";
|
|
32
|
+
}
|
|
33
|
+
if (text.includes("[vibeOS]") || text.includes("[flow-enforcer]") || text.includes("[delegation]"))
|
|
34
|
+
return;
|
|
35
|
+
originalConsoleError(...args);
|
|
36
|
+
};
|
|
37
|
+
globalConsoleState[VIBEOS_CONSOLE_ERROR_GUARD] = true;
|
|
38
|
+
}
|
|
6
39
|
function getVibeOSHome() {
|
|
7
40
|
return process.env.VIBEOS_HOME || join(process.env.HOME || "", ".claude");
|
|
8
41
|
}
|