vibeoscore 1.0.2 → 1.0.8
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/client.js +1 -0
- package/client.ts +2 -0
- package/lib/logger.js +27 -0
- package/mcp-server.js +5 -4
- package/mcp-server.ts +4 -3
- package/package.json +4 -10
- package/dashboard/dist/assets/index-BnPt1Fii.js +0 -1
- package/dashboard/dist/assets/index-CfH00tOL.css +0 -1
- package/dashboard/dist/index.html +0 -3
- package/lib/blackbox-rf.js +0 -1099
- package/lib/blackbox.js +0 -137
- package/lib/compression.js +0 -119
- package/lib/db.js +0 -106
- package/lib/db.ts +0 -113
- package/lib/delegation.js +0 -137
- package/lib/meta-controller.js +0 -418
- package/lib/meta-controller.mjs +0 -499
- package/lib/patterns.js +0 -150
- package/lib/resolution-tracker.js +0 -486
- package/lib/stress.js +0 -84
- package/lib/tdd.js +0 -218
- package/lib/tier-routing.js +0 -48
- package/middleware/auth.js +0 -75
- package/middleware/auth.ts +0 -87
- package/middleware/usage-logging.js +0 -29
- package/middleware/usage-logging.ts +0 -41
- package/nginx-vibetheog-api.conf +0 -64
- package/routes/admin.js +0 -93
- package/routes/admin.ts +0 -107
- package/routes/blackbox.js +0 -463
- package/routes/compression.js +0 -12
- package/routes/delegation.js +0 -30
- package/routes/patterns.js +0 -53
- package/routes/pricing.js +0 -62
- package/routes/stress.js +0 -30
- package/routes/tdd.js +0 -68
- package/routes/tier-routing.js +0 -31
- package/scripts/dashboard-server.mjs +0 -246
- package/scripts/deploy-zero-downtime.sh +0 -77
- package/scripts/deploy.sh +0 -68
- package/scripts/release.mjs +0 -30
- package/scripts/seed-master-token.js +0 -29
- package/scripts/start-all.mjs +0 -34
- package/server.js +0 -88
- package/vibeos-api.service +0 -19
package/lib/blackbox.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { ResolutionTracker as SharedResolutionTracker } from "./resolution-tracker.js"
|
|
2
|
-
|
|
3
|
-
const SUB_REGIMES = ["INIT", "DIVERGENT", "EXPLORING", "REFINING", "CONVERGING", "CLOSED", "LOOPING"]
|
|
4
|
-
|
|
5
|
-
class ResolutionTracker {
|
|
6
|
-
constructor(sessionId, projectId, maxHistory = 50) {
|
|
7
|
-
this._inner = new SharedResolutionTracker(sessionId, maxHistory)
|
|
8
|
-
this.projectId = projectId || null
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
update(entry) {
|
|
12
|
-
const userText = entry.userText || ""
|
|
13
|
-
const features = entry.features && typeof entry.features === "object" && !Array.isArray(entry.features)
|
|
14
|
-
? entry.features
|
|
15
|
-
: {}
|
|
16
|
-
const action = (entry.actions && entry.actions.length > 0) ? entry.actions[0] : (entry.action || "explore")
|
|
17
|
-
const entropy = entry.entropy ?? 1.0
|
|
18
|
-
const uncertainty = entry.uncertainty ?? 50
|
|
19
|
-
const embedding = entry.embedding || null
|
|
20
|
-
|
|
21
|
-
const state = this._inner.update(userText, features, action, entropy, uncertainty, embedding)
|
|
22
|
-
if (entry.telemetry && typeof entry.telemetry === "object" && !Array.isArray(entry.telemetry)) {
|
|
23
|
-
const lastEntry = this._inner.history?.[this._inner.history.length - 1]
|
|
24
|
-
if (lastEntry) {
|
|
25
|
-
lastEntry.telemetry = {
|
|
26
|
-
...(lastEntry.telemetry || {}),
|
|
27
|
-
...entry.telemetry,
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return state
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
getState() {
|
|
35
|
-
const snap = this._inner.snapshot()
|
|
36
|
-
return {
|
|
37
|
-
sub_regime: snap.sub_regime || "INIT",
|
|
38
|
-
resolution: snap.resolution || "unresolved",
|
|
39
|
-
momentum: snap.momentum || 0,
|
|
40
|
-
signals: snap.signals || {},
|
|
41
|
-
intent_state: snap.intent_state || { volatility_score: 0, drift_rate: 0, core_goal_embedding: null },
|
|
42
|
-
continuity_state: snap.continuity_state || null,
|
|
43
|
-
is_looping: snap.is_looping || false,
|
|
44
|
-
loop_consecutive: snap.loop_consecutive || 0,
|
|
45
|
-
loop_intervention_level: snap.loop_intervention_level || "none",
|
|
46
|
-
pivot_detected: snap.pivot_detected || false,
|
|
47
|
-
pivot_score: snap.pivot_score || 0,
|
|
48
|
-
outcome: snap.outcome || null,
|
|
49
|
-
n_interactions: snap.n_interactions || 0,
|
|
50
|
-
loop_count: snap.is_looping ? snap.loop_consecutive || 1 : 0,
|
|
51
|
-
turn_count: snap.n_interactions || 0,
|
|
52
|
-
history_length: this._inner.getHistory().length,
|
|
53
|
-
telemetry: this._inner.getHistory().slice(-1)[0]?.telemetry || null,
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
annotateLastTurn(telemetry) {
|
|
58
|
-
if (!telemetry || typeof telemetry !== "object" || Array.isArray(telemetry)) {
|
|
59
|
-
return
|
|
60
|
-
}
|
|
61
|
-
const lastEntry = this._inner.history?.[this._inner.history.length - 1]
|
|
62
|
-
if (lastEntry) {
|
|
63
|
-
lastEntry.telemetry = {
|
|
64
|
-
...(lastEntry.telemetry || {}),
|
|
65
|
-
...telemetry,
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
recordOutcome(outcome) {
|
|
71
|
-
this._inner.recordOutcome(outcome)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
getLoopIntervention() {
|
|
75
|
-
return this._inner.getLoopIntervention()
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
getPivotDirective() {
|
|
79
|
-
return this._inner.getPivotDirective()
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
setCalibratedWeights(weights) {
|
|
83
|
-
this._inner.setCalibratedWeights(weights)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
getOutcomeHistory() {
|
|
87
|
-
return this._inner.getOutcomeHistory()
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
reset() {
|
|
91
|
-
this._inner.reset()
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
serialize() {
|
|
95
|
-
return this._inner.serialize()
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
static deserialize(data) {
|
|
99
|
-
const tracker = new ResolutionTracker(data.sessionId)
|
|
100
|
-
tracker._inner = SharedResolutionTracker.deserialize(data)
|
|
101
|
-
return tracker
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function extractFeatures(text) {
|
|
106
|
-
if (!text || typeof text !== "string") return {}
|
|
107
|
-
const len = text.length
|
|
108
|
-
const words = text.split(/\s+/).filter(w => w.length > 0)
|
|
109
|
-
const wordCount = words.length
|
|
110
|
-
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0)
|
|
111
|
-
const sentenceCount = sentences.length
|
|
112
|
-
const avgWordLen = wordCount > 0 ? words.reduce((s, w) => s + w.length, 0) / wordCount : 0
|
|
113
|
-
const questions = (text.match(/\?/g) || []).length
|
|
114
|
-
const questionRatio = sentenceCount > 0 ? questions / sentenceCount : 0
|
|
115
|
-
const codeBlocks = (text.match(/```/g) || []).length / 2
|
|
116
|
-
const urgency = /urgent|asap|immediately|critical|broken|failing|crash|error|bug/i.test(text) ? 1.0 : 0.0
|
|
117
|
-
const repetition = wordCount > 5
|
|
118
|
-
? (text.toLowerCase().match(/(\b\w+\b).*?\1/g) || []).length / wordCount
|
|
119
|
-
: 0
|
|
120
|
-
const sentimentIndicators = /thanks|great|perfect|awesome/i.test(text) ? 0.2
|
|
121
|
-
: /frustrat|annoy|not working|doesn't work|stupid|useless/i.test(text) ? 0.8
|
|
122
|
-
: 0.5
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
length: Math.min(1.0, len / 5000),
|
|
126
|
-
word_count: Math.min(1.0, wordCount / 500),
|
|
127
|
-
sentence_count: Math.min(1.0, sentenceCount / 50),
|
|
128
|
-
avg_word_length: Math.min(1.0, avgWordLen / 10),
|
|
129
|
-
question_ratio: Math.min(1.0, questionRatio),
|
|
130
|
-
code_blocks: Math.min(1.0, codeBlocks / 5),
|
|
131
|
-
urgency,
|
|
132
|
-
repetition: Math.min(1.0, repetition * 10),
|
|
133
|
-
sentiment: sentimentIndicators,
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export { ResolutionTracker, SUB_REGIMES, extractFeatures }
|
package/lib/compression.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
const VERBOSE_LINE_RE = [
|
|
2
|
-
/^\s*(Sure|Certainly|Absolutely|Of course|Great question)[!.,]?\s*$/i,
|
|
3
|
-
/^\s*(Hope this helps|Let me know if|Feel free to|Happy to|Please let me know).*$/i,
|
|
4
|
-
/^\s*(I'd be happy|I can help|I'm here|Is there anything|Do you need).*$/i,
|
|
5
|
-
]
|
|
6
|
-
|
|
7
|
-
const BULLET_PATTERNS = [
|
|
8
|
-
/^\s*\w[^:]{0,80}:/,
|
|
9
|
-
/^\s*[-*\u2022]\s/,
|
|
10
|
-
/^\s*\d+\.\s/,
|
|
11
|
-
/^\s*(NOTE|TIP|IMPORTANT|WARNING|FIX|TODO|HACK)\b/i,
|
|
12
|
-
/^\s*[A-Z][A-Z\s_-]{4,}:\s/,
|
|
13
|
-
/^\s*>\s/,
|
|
14
|
-
/^\s*```\w*$/,
|
|
15
|
-
/^\s*#{1,6}\s/,
|
|
16
|
-
]
|
|
17
|
-
|
|
18
|
-
const COMPRESS_RATIO = 0.30
|
|
19
|
-
const COMPRESS_THRESHOLD = 2000
|
|
20
|
-
const MIN_KEPT_LINES_RATIO = 0.40
|
|
21
|
-
|
|
22
|
-
function compressText(text) {
|
|
23
|
-
if (!text || typeof text !== "string" || text.length <= COMPRESS_THRESHOLD) {
|
|
24
|
-
return text
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let lines = text.split("\n")
|
|
28
|
-
|
|
29
|
-
lines = lines.filter(line => {
|
|
30
|
-
return !VERBOSE_LINE_RE.some(re => re.test(line))
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
let result = lines.join("\n")
|
|
34
|
-
result = result.replace(/\n{3,}/g, "\n\n")
|
|
35
|
-
|
|
36
|
-
lines = result.split("\n")
|
|
37
|
-
const originalCharCount = result.length
|
|
38
|
-
const targetChars = Math.ceil(originalCharCount * COMPRESS_RATIO)
|
|
39
|
-
const minLines = Math.ceil(lines.length * MIN_KEPT_LINES_RATIO)
|
|
40
|
-
|
|
41
|
-
if (originalCharCount > targetChars && lines.length > minLines) {
|
|
42
|
-
lines = extractBulletLines(lines, targetChars, minLines)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
result = lines.join("\n")
|
|
46
|
-
|
|
47
|
-
if (result.length > originalCharCount * 0.6) {
|
|
48
|
-
result = safetyTruncate(result, Math.ceil(originalCharCount * 0.5))
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (!result.trim() && text.trim()) {
|
|
52
|
-
return text.split("\n").slice(0, Math.max(5, Math.ceil(text.split("\n").length * 0.2))).join("\n")
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return result
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function extractBulletLines(lines, targetChars, minLines) {
|
|
59
|
-
const keyLines = []
|
|
60
|
-
const otherLines = []
|
|
61
|
-
|
|
62
|
-
for (const line of lines) {
|
|
63
|
-
if (BULLET_PATTERNS.some(re => re.test(line))) {
|
|
64
|
-
keyLines.push(line)
|
|
65
|
-
} else {
|
|
66
|
-
otherLines.push(line)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const selected = [...keyLines]
|
|
71
|
-
|
|
72
|
-
for (const line of otherLines) {
|
|
73
|
-
if (selected.length >= minLines && selected.join("\n").length >= targetChars) {
|
|
74
|
-
break
|
|
75
|
-
}
|
|
76
|
-
selected.push(line)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
while (selected.length > minLines && selected.join("\n").length > targetChars * 2) {
|
|
80
|
-
selected.pop()
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return selected
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function safetyTruncate(text, maxChars) {
|
|
87
|
-
if (text.length <= maxChars) return text
|
|
88
|
-
|
|
89
|
-
const lines = text.split("\n")
|
|
90
|
-
const selected = []
|
|
91
|
-
let charCount = 0
|
|
92
|
-
|
|
93
|
-
for (const line of lines) {
|
|
94
|
-
if (charCount + line.length > maxChars && selected.length >= 3) {
|
|
95
|
-
break
|
|
96
|
-
}
|
|
97
|
-
selected.push(line)
|
|
98
|
-
charCount += line.length + 1
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return selected.join("\n")
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function compressToolOutput(output, threshold = COMPRESS_THRESHOLD) {
|
|
105
|
-
if (!output || typeof output !== "string" || output.length <= threshold) {
|
|
106
|
-
return { compressed: false, content: output, original_length: output?.length || 0 }
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const compressed = compressText(output)
|
|
110
|
-
return {
|
|
111
|
-
compressed: true,
|
|
112
|
-
content: compressed,
|
|
113
|
-
original_length: output.length,
|
|
114
|
-
compressed_length: compressed.length,
|
|
115
|
-
savings_percent: Math.round((1 - compressed.length / output.length) * 100),
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export { compressText, compressToolOutput, extractBulletLines, COMPRESS_THRESHOLD, COMPRESS_RATIO }
|
package/lib/db.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import Database from "better-sqlite3";
|
|
2
|
-
import { existsSync, mkdirSync } from "node:fs";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const DB_PATH = process.env.VIBEOS_API_DB_PATH || join(__dirname, "..", "data", "vibeos-api.db");
|
|
7
|
-
let db;
|
|
8
|
-
export function getDb() {
|
|
9
|
-
if (db)
|
|
10
|
-
return db;
|
|
11
|
-
const dbDir = dirname(DB_PATH);
|
|
12
|
-
if (!existsSync(dbDir)) {
|
|
13
|
-
mkdirSync(dbDir, { recursive: true });
|
|
14
|
-
}
|
|
15
|
-
db = new Database(DB_PATH);
|
|
16
|
-
db.pragma("journal_mode = WAL");
|
|
17
|
-
db.pragma("foreign_keys = ON");
|
|
18
|
-
db.exec(`
|
|
19
|
-
CREATE TABLE IF NOT EXISTS seats (
|
|
20
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
21
|
-
name TEXT NOT NULL,
|
|
22
|
-
email TEXT,
|
|
23
|
-
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'suspended', 'cancelled')),
|
|
24
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
25
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
CREATE TABLE IF NOT EXISTS api_tokens (
|
|
29
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
30
|
-
token TEXT NOT NULL UNIQUE,
|
|
31
|
-
seat_id INTEGER NOT NULL,
|
|
32
|
-
label TEXT,
|
|
33
|
-
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'revoked', 'expired')),
|
|
34
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
35
|
-
revoked_at TEXT,
|
|
36
|
-
expires_at TEXT,
|
|
37
|
-
last_used_at TEXT,
|
|
38
|
-
FOREIGN KEY (seat_id) REFERENCES seats(id) ON DELETE CASCADE
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
CREATE TRIGGER IF NOT EXISTS prevent_api_token_delete
|
|
42
|
-
BEFORE DELETE ON api_tokens
|
|
43
|
-
BEGIN
|
|
44
|
-
SELECT RAISE(ABORT, 'api_tokens rows cannot be deleted; update status instead');
|
|
45
|
-
END;
|
|
46
|
-
|
|
47
|
-
CREATE TABLE IF NOT EXISTS blackbox_sessions (
|
|
48
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49
|
-
session_id TEXT NOT NULL UNIQUE,
|
|
50
|
-
project_id TEXT,
|
|
51
|
-
state_json TEXT NOT NULL DEFAULT '{}',
|
|
52
|
-
outcome TEXT,
|
|
53
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
54
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
CREATE TABLE IF NOT EXISTS blackbox_calibration (
|
|
58
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
59
|
-
project_id TEXT NOT NULL DEFAULT 'global',
|
|
60
|
-
weights_json TEXT NOT NULL DEFAULT '{}',
|
|
61
|
-
samples_used INTEGER NOT NULL DEFAULT 0,
|
|
62
|
-
precision REAL,
|
|
63
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
64
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
65
|
-
UNIQUE(project_id)
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
CREATE TABLE IF NOT EXISTS usage_log (
|
|
69
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
70
|
-
token_id INTEGER NOT NULL,
|
|
71
|
-
endpoint TEXT NOT NULL,
|
|
72
|
-
request_body TEXT,
|
|
73
|
-
response_size INTEGER,
|
|
74
|
-
latency_ms INTEGER,
|
|
75
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
76
|
-
FOREIGN KEY (token_id) REFERENCES api_tokens(id)
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
CREATE TABLE IF NOT EXISTS admin_audit_log (
|
|
80
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
81
|
-
method TEXT NOT NULL,
|
|
82
|
-
endpoint TEXT NOT NULL,
|
|
83
|
-
request_body TEXT,
|
|
84
|
-
response_status INTEGER NOT NULL,
|
|
85
|
-
response_size INTEGER,
|
|
86
|
-
latency_ms INTEGER,
|
|
87
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
CREATE INDEX IF NOT EXISTS idx_tokens_token ON api_tokens(token);
|
|
91
|
-
CREATE INDEX IF NOT EXISTS idx_tokens_seat ON api_tokens(seat_id);
|
|
92
|
-
CREATE INDEX IF NOT EXISTS idx_tokens_status ON api_tokens(status);
|
|
93
|
-
CREATE INDEX IF NOT EXISTS idx_seats_status ON seats(status);
|
|
94
|
-
CREATE INDEX IF NOT EXISTS idx_blackbox_project ON blackbox_sessions(project_id);
|
|
95
|
-
CREATE INDEX IF NOT EXISTS idx_blackbox_updated ON blackbox_sessions(updated_at);
|
|
96
|
-
CREATE INDEX IF NOT EXISTS idx_usage_token ON usage_log(token_id);
|
|
97
|
-
CREATE INDEX IF NOT EXISTS idx_usage_created ON usage_log(created_at);
|
|
98
|
-
CREATE INDEX IF NOT EXISTS idx_admin_audit_created ON admin_audit_log(created_at);
|
|
99
|
-
CREATE INDEX IF NOT EXISTS idx_admin_audit_endpoint ON admin_audit_log(endpoint);
|
|
100
|
-
`);
|
|
101
|
-
return db;
|
|
102
|
-
}
|
|
103
|
-
export function initDb() {
|
|
104
|
-
getDb();
|
|
105
|
-
return db;
|
|
106
|
-
}
|
package/lib/db.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import Database from "better-sqlite3"
|
|
2
|
-
import { existsSync, mkdirSync } from "node:fs"
|
|
3
|
-
import { dirname, join } from "node:path"
|
|
4
|
-
import { fileURLToPath } from "node:url"
|
|
5
|
-
|
|
6
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
7
|
-
const DB_PATH = process.env.VIBEOS_API_DB_PATH || join(__dirname, "..", "data", "vibeos-api.db")
|
|
8
|
-
|
|
9
|
-
let db: any
|
|
10
|
-
|
|
11
|
-
export function getDb() {
|
|
12
|
-
if (db) return db
|
|
13
|
-
|
|
14
|
-
const dbDir = dirname(DB_PATH)
|
|
15
|
-
if (!existsSync(dbDir)) {
|
|
16
|
-
mkdirSync(dbDir, { recursive: true })
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
db = new Database(DB_PATH)
|
|
20
|
-
db.pragma("journal_mode = WAL")
|
|
21
|
-
db.pragma("foreign_keys = ON")
|
|
22
|
-
|
|
23
|
-
db.exec(`
|
|
24
|
-
CREATE TABLE IF NOT EXISTS seats (
|
|
25
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
26
|
-
name TEXT NOT NULL,
|
|
27
|
-
email TEXT,
|
|
28
|
-
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'suspended', 'cancelled')),
|
|
29
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
30
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
CREATE TABLE IF NOT EXISTS api_tokens (
|
|
34
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
35
|
-
token TEXT NOT NULL UNIQUE,
|
|
36
|
-
seat_id INTEGER NOT NULL,
|
|
37
|
-
label TEXT,
|
|
38
|
-
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'revoked', 'expired')),
|
|
39
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
40
|
-
revoked_at TEXT,
|
|
41
|
-
expires_at TEXT,
|
|
42
|
-
last_used_at TEXT,
|
|
43
|
-
FOREIGN KEY (seat_id) REFERENCES seats(id) ON DELETE CASCADE
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
CREATE TRIGGER IF NOT EXISTS prevent_api_token_delete
|
|
47
|
-
BEFORE DELETE ON api_tokens
|
|
48
|
-
BEGIN
|
|
49
|
-
SELECT RAISE(ABORT, 'api_tokens rows cannot be deleted; update status instead');
|
|
50
|
-
END;
|
|
51
|
-
|
|
52
|
-
CREATE TABLE IF NOT EXISTS blackbox_sessions (
|
|
53
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
54
|
-
session_id TEXT NOT NULL UNIQUE,
|
|
55
|
-
project_id TEXT,
|
|
56
|
-
state_json TEXT NOT NULL DEFAULT '{}',
|
|
57
|
-
outcome TEXT,
|
|
58
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
59
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
CREATE TABLE IF NOT EXISTS blackbox_calibration (
|
|
63
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
64
|
-
project_id TEXT NOT NULL DEFAULT 'global',
|
|
65
|
-
weights_json TEXT NOT NULL DEFAULT '{}',
|
|
66
|
-
samples_used INTEGER NOT NULL DEFAULT 0,
|
|
67
|
-
precision REAL,
|
|
68
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
69
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
70
|
-
UNIQUE(project_id)
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
CREATE TABLE IF NOT EXISTS usage_log (
|
|
74
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
75
|
-
token_id INTEGER NOT NULL,
|
|
76
|
-
endpoint TEXT NOT NULL,
|
|
77
|
-
request_body TEXT,
|
|
78
|
-
response_size INTEGER,
|
|
79
|
-
latency_ms INTEGER,
|
|
80
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
81
|
-
FOREIGN KEY (token_id) REFERENCES api_tokens(id)
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
CREATE TABLE IF NOT EXISTS admin_audit_log (
|
|
85
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
86
|
-
method TEXT NOT NULL,
|
|
87
|
-
endpoint TEXT NOT NULL,
|
|
88
|
-
request_body TEXT,
|
|
89
|
-
response_status INTEGER NOT NULL,
|
|
90
|
-
response_size INTEGER,
|
|
91
|
-
latency_ms INTEGER,
|
|
92
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
CREATE INDEX IF NOT EXISTS idx_tokens_token ON api_tokens(token);
|
|
96
|
-
CREATE INDEX IF NOT EXISTS idx_tokens_seat ON api_tokens(seat_id);
|
|
97
|
-
CREATE INDEX IF NOT EXISTS idx_tokens_status ON api_tokens(status);
|
|
98
|
-
CREATE INDEX IF NOT EXISTS idx_seats_status ON seats(status);
|
|
99
|
-
CREATE INDEX IF NOT EXISTS idx_blackbox_project ON blackbox_sessions(project_id);
|
|
100
|
-
CREATE INDEX IF NOT EXISTS idx_blackbox_updated ON blackbox_sessions(updated_at);
|
|
101
|
-
CREATE INDEX IF NOT EXISTS idx_usage_token ON usage_log(token_id);
|
|
102
|
-
CREATE INDEX IF NOT EXISTS idx_usage_created ON usage_log(created_at);
|
|
103
|
-
CREATE INDEX IF NOT EXISTS idx_admin_audit_created ON admin_audit_log(created_at);
|
|
104
|
-
CREATE INDEX IF NOT EXISTS idx_admin_audit_endpoint ON admin_audit_log(endpoint);
|
|
105
|
-
`)
|
|
106
|
-
|
|
107
|
-
return db
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function initDb() {
|
|
111
|
-
getDb()
|
|
112
|
-
return db
|
|
113
|
-
}
|
package/lib/delegation.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
const WARN_ON_DIRECT = new Set(["write", "edit", "notebookedit"])
|
|
2
|
-
const SOFT_QUOTA = new Set(["bash", "webfetch", "websearch"])
|
|
3
|
-
|
|
4
|
-
const SAVE_EST = {
|
|
5
|
-
WRITE_EDIT: 0.0006,
|
|
6
|
-
SOFT_QUOTA: 0.0003,
|
|
7
|
-
CONTEXT7: 0.0006,
|
|
8
|
-
OPUS_DISABLE: 0.03,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const MODEL_USD_PER_TURN = {
|
|
12
|
-
"anthropic/claude-opus-4-7": 0.033,
|
|
13
|
-
"anthropic/claude-opus-4-5": 0.033,
|
|
14
|
-
"anthropic/claude-sonnet-4-6": 0.0066,
|
|
15
|
-
"anthropic/claude-sonnet-4-5": 0.0066,
|
|
16
|
-
"anthropic/claude-3-5-sonnet": 0.0066,
|
|
17
|
-
"anthropic/claude-3-5-sonnet-20241022": 0.0066,
|
|
18
|
-
"anthropic/claude-3-5-sonnet-20240620": 0.0066,
|
|
19
|
-
"anthropic/claude-3-7-sonnet": 0.0066,
|
|
20
|
-
"anthropic/claude-3-7-sonnet-20250219": 0.0066,
|
|
21
|
-
"anthropic/claude-3-opus": 0.03,
|
|
22
|
-
"anthropic/claude-3-opus-20240229": 0.03,
|
|
23
|
-
"anthropic/claude-3-haiku": 0.0006,
|
|
24
|
-
"anthropic/claude-3-5-haiku": 0.0016,
|
|
25
|
-
"google/gemini-2.5-pro": 0.0035,
|
|
26
|
-
"google/gemini-2.5-flash": 0.00075,
|
|
27
|
-
"google/gemini-2.0-flash": 0.0002,
|
|
28
|
-
"google/gemini-pro-1.5": 0.0035,
|
|
29
|
-
"deepseek/deepseek-chat": 0,
|
|
30
|
-
"deepseek/deepseek-v3": 0,
|
|
31
|
-
"deepseek/deepseek-v4-pro": 0.002,
|
|
32
|
-
"deepseek/deepseek-v4-flash": 0,
|
|
33
|
-
"openai/gpt-5": 0.02,
|
|
34
|
-
"openai/gpt-4o": 0.005,
|
|
35
|
-
"openai/gpt-4o-mini": 0.0003,
|
|
36
|
-
"openai/o1": 0.03,
|
|
37
|
-
"openai/o3": 0.02,
|
|
38
|
-
"openai/o4-mini": 0.0022,
|
|
39
|
-
"mistral/mistral-large-2": 0.004,
|
|
40
|
-
"mistral/codestral-2501": 0.001,
|
|
41
|
-
"x-ai/grok-3": 0.006,
|
|
42
|
-
"x-ai/grok-3-mini": 0.0006,
|
|
43
|
-
"qwen/qwen3-235b-a22b": 0.0004,
|
|
44
|
-
"qwen/qwen3-30b-a3b": 0.0001,
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const TURN_BLEND_INPUT_TOKENS = 0.000003
|
|
48
|
-
const TURN_BLEND_OUTPUT_TOKENS = 0.000012
|
|
49
|
-
|
|
50
|
-
function normalizeModelId(model) {
|
|
51
|
-
if (!model) return ""
|
|
52
|
-
const s = String(model).toLowerCase().trim()
|
|
53
|
-
return s.replace(/^openrouter\//, "").replace(/^anthropic\//, "anthropic/").replace(/^google\//, "google/").replace(/^deepseek\//, "deepseek/").replace(/^openai\//, "openai/").replace(/^mistral\//, "mistral/").replace(/^x-ai\//, "x-ai/").replace(/^qwen\//, "qwen/")
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function modelCostPerTurn(model, dynamicCache = {}) {
|
|
57
|
-
if (!model) return 0
|
|
58
|
-
const dyn = dynamicCostFor(model, dynamicCache)
|
|
59
|
-
if (dyn != null) return dyn
|
|
60
|
-
const key = normalizeModelId(model)
|
|
61
|
-
if (Object.prototype.hasOwnProperty.call(MODEL_USD_PER_TURN, key)) return MODEL_USD_PER_TURN[key]
|
|
62
|
-
for (const [k, v] of Object.entries(MODEL_USD_PER_TURN)) {
|
|
63
|
-
if (key.startsWith(k) && /-\d+$/.test(key) && key.charAt(k.length) === "-") return v
|
|
64
|
-
}
|
|
65
|
-
return null
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function dynamicCostFor(model, dynamicCache = {}) {
|
|
69
|
-
const key = normalizeModelId(model)
|
|
70
|
-
if (Object.prototype.hasOwnProperty.call(dynamicCache, key)) return dynamicCache[key]
|
|
71
|
-
for (const [k, v] of Object.entries(dynamicCache)) {
|
|
72
|
-
if (key === k) return v
|
|
73
|
-
if (key.startsWith(k) && /-\d+$/.test(key) && key.charAt(k.length) === "-") return v
|
|
74
|
-
}
|
|
75
|
-
return null
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function parseOpenRouterTurnCost(modelRow) {
|
|
79
|
-
const p = modelRow?.pricing || {}
|
|
80
|
-
const inTok = Number(p.prompt ?? p.input ?? p.request)
|
|
81
|
-
const outTok = Number(p.completion ?? p.output ?? p.response)
|
|
82
|
-
if (Number.isFinite(inTok) && Number.isFinite(outTok)) {
|
|
83
|
-
return inTok * TURN_BLEND_INPUT_TOKENS + outTok * TURN_BLEND_OUTPUT_TOKENS
|
|
84
|
-
}
|
|
85
|
-
const oneTok = Number(p.price ?? p.total ?? p.input ?? p.output)
|
|
86
|
-
if (Number.isFinite(oneTok)) return oneTok * 1000
|
|
87
|
-
return null
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function checkDelegation(tool, tier, model, prompt, dynamicCache = {}) {
|
|
91
|
-
const toolLower = String(tool || "").toLowerCase()
|
|
92
|
-
|
|
93
|
-
if (!WARN_ON_DIRECT.has(toolLower)) {
|
|
94
|
-
return { blocked: false, reason: null, savings: 0 }
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (tier !== "high") {
|
|
98
|
-
return { blocked: false, reason: null, savings: 0 }
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const cost = modelCostPerTurn(model, dynamicCache) ?? SAVE_EST.WRITE_EDIT
|
|
102
|
-
const savings = cost > 0 ? cost : SAVE_EST.WRITE_EDIT
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
blocked: true,
|
|
106
|
-
reason: `Direct ${tool} blocked on Brain tier. Delegate via Task or switch tier.`,
|
|
107
|
-
savings: savings,
|
|
108
|
-
redirect_path: toolLower === "write" ? `/tmp/vibeos-enforcement-blocked-${Date.now()}` : null,
|
|
109
|
-
old_string_replacement: (toolLower === "edit" || toolLower === "notebookedit") ? `__THE_SAVER_ENFORCEMENT_BLOCK_${Date.now()}__` : null,
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function checkSoftQuota(tool, currentCount, limit = 5) {
|
|
114
|
-
const toolLower = String(tool || "").toLowerCase()
|
|
115
|
-
if (!SOFT_QUOTA.has(toolLower)) return { warned: false }
|
|
116
|
-
if (currentCount >= limit) {
|
|
117
|
-
return {
|
|
118
|
-
warned: true,
|
|
119
|
-
message: `Soft quota reached for ${tool} (${currentCount}/${limit}). Consider delegating.`,
|
|
120
|
-
savings: SAVE_EST.SOFT_QUOTA,
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return { warned: false }
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export {
|
|
127
|
-
WARN_ON_DIRECT,
|
|
128
|
-
SOFT_QUOTA,
|
|
129
|
-
SAVE_EST,
|
|
130
|
-
MODEL_USD_PER_TURN,
|
|
131
|
-
normalizeModelId,
|
|
132
|
-
modelCostPerTurn,
|
|
133
|
-
dynamicCostFor,
|
|
134
|
-
parseOpenRouterTurnCost,
|
|
135
|
-
checkDelegation,
|
|
136
|
-
checkSoftQuota,
|
|
137
|
-
}
|