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
|
@@ -1,486 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
// SPDX-FileCopyrightText: 2026 vibeOS <https://github.com/DrunkkToys/vibeOS>
|
|
3
|
-
// Resolution Tracker — state-of-progress estimator for dialogue trajectory.
|
|
4
|
-
// Ported from theWay: src/decision/resolution_tracker.py
|
|
5
|
-
export class ResolutionTracker {
|
|
6
|
-
static SUB_REGIMES = ["INIT", "DIVERGENT", "EXPLORING", "REFINING", "CONVERGING", "CLOSED", "LOOPING"];
|
|
7
|
-
sessionId;
|
|
8
|
-
maxHistory;
|
|
9
|
-
history;
|
|
10
|
-
loopCount;
|
|
11
|
-
pivotHistory;
|
|
12
|
-
outcomeHistory;
|
|
13
|
-
calibratedWeights;
|
|
14
|
-
constructor(sessionId, maxHistory = 10) {
|
|
15
|
-
this.sessionId = sessionId;
|
|
16
|
-
this.maxHistory = maxHistory;
|
|
17
|
-
this.history = [];
|
|
18
|
-
this.loopCount = 0;
|
|
19
|
-
this.pivotHistory = [];
|
|
20
|
-
this.outcomeHistory = [];
|
|
21
|
-
this.calibratedWeights = null;
|
|
22
|
-
}
|
|
23
|
-
update(userText, features, action, entropy, uncertainty, embedding = null) {
|
|
24
|
-
const entry = {
|
|
25
|
-
text: userText,
|
|
26
|
-
features: { ...features },
|
|
27
|
-
action,
|
|
28
|
-
entropy,
|
|
29
|
-
uncertainty,
|
|
30
|
-
embedding: embedding ? [...embedding] : null,
|
|
31
|
-
timestamp: Date.now() / 1000,
|
|
32
|
-
};
|
|
33
|
-
if (this.history.length >= 2) {
|
|
34
|
-
entry.is_pivot = this.detectPivotSignal(entry, this.history[this.history.length - 1]);
|
|
35
|
-
if (entry.is_pivot) {
|
|
36
|
-
this.pivotHistory.push(this.history.length);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
this.history.push(entry);
|
|
40
|
-
if (this.history.length > this.maxHistory) {
|
|
41
|
-
this.history.shift();
|
|
42
|
-
}
|
|
43
|
-
const state = this.computeState();
|
|
44
|
-
if (state.is_looping) {
|
|
45
|
-
this.loopCount++;
|
|
46
|
-
this.history[this.history.length - 1].outcome = this.history[this.history.length - 1].outcome || "loop_detected";
|
|
47
|
-
}
|
|
48
|
-
else if (state.sub_regime !== "LOOPING") {
|
|
49
|
-
this.loopCount = Math.max(0, this.loopCount - 1);
|
|
50
|
-
}
|
|
51
|
-
return state;
|
|
52
|
-
}
|
|
53
|
-
detectPivotSignal(current, previous) {
|
|
54
|
-
const drift = this.history.length >= 4
|
|
55
|
-
? this.computeIntentState().drift_rate
|
|
56
|
-
: 0;
|
|
57
|
-
const repeatRatio = (current.features?.repetition || 0);
|
|
58
|
-
const instructionChange = Math.abs((current.features?.instruction_density || 0.6) - (previous.features?.instruction_density || 0.6));
|
|
59
|
-
const lengthRatio = previous.text.length > 0
|
|
60
|
-
? Math.abs(current.text.length - previous.text.length) / previous.text.length
|
|
61
|
-
: 0;
|
|
62
|
-
const pivotScore = drift * 0.35 + repeatRatio * 0.15 + instructionChange * 0.25 + lengthRatio * 0.25;
|
|
63
|
-
return pivotScore > 0.45;
|
|
64
|
-
}
|
|
65
|
-
computeState() {
|
|
66
|
-
const n = this.history.length;
|
|
67
|
-
if (n < 1) {
|
|
68
|
-
return {
|
|
69
|
-
sub_regime: "INIT",
|
|
70
|
-
resolution: "unresolved",
|
|
71
|
-
momentum: 0.0,
|
|
72
|
-
signals: { action_consistency: 1.0, entropy_trend: 0.0, feature_contradiction: 0.0, embedding_delta: 0.0 },
|
|
73
|
-
intent_state: { volatility_score: 0.0, drift_rate: 0.0, core_goal_embedding: null },
|
|
74
|
-
continuity_state: "HIGH",
|
|
75
|
-
is_looping: false,
|
|
76
|
-
loop_consecutive: 0,
|
|
77
|
-
loop_intervention_level: "none",
|
|
78
|
-
pivot_detected: false,
|
|
79
|
-
pivot_score: 0.0,
|
|
80
|
-
outcome: null,
|
|
81
|
-
n_interactions: 0,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
const actionConsistency = this.calcActionConsistency();
|
|
85
|
-
const entropyTrend = this.calcEntropyTrend();
|
|
86
|
-
const featureContradiction = this.calcFeatureContradiction();
|
|
87
|
-
const embeddingDelta = this.calcEmbeddingDelta();
|
|
88
|
-
const isLooping = this.detectLoop();
|
|
89
|
-
const intentState = this.computeIntentState();
|
|
90
|
-
const continuityState = this.continuityState(intentState);
|
|
91
|
-
let subRegime;
|
|
92
|
-
if (n === 1) {
|
|
93
|
-
subRegime = "INIT";
|
|
94
|
-
}
|
|
95
|
-
else if (isLooping) {
|
|
96
|
-
subRegime = "LOOPING";
|
|
97
|
-
}
|
|
98
|
-
else if (this.isClosed(actionConsistency, embeddingDelta, featureContradiction)) {
|
|
99
|
-
subRegime = "CLOSED";
|
|
100
|
-
}
|
|
101
|
-
else if (this.isDivergent(entropyTrend, featureContradiction, actionConsistency)) {
|
|
102
|
-
subRegime = "DIVERGENT";
|
|
103
|
-
}
|
|
104
|
-
else if (this.isExploring(featureContradiction, entropyTrend, actionConsistency)) {
|
|
105
|
-
subRegime = "EXPLORING";
|
|
106
|
-
}
|
|
107
|
-
else if (this.isRefining(featureContradiction, embeddingDelta, actionConsistency, entropyTrend)) {
|
|
108
|
-
subRegime = "REFINING";
|
|
109
|
-
}
|
|
110
|
-
else if (this.isConverging(actionConsistency, embeddingDelta, entropyTrend)) {
|
|
111
|
-
subRegime = "CONVERGING";
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
subRegime = "EXPLORING";
|
|
115
|
-
}
|
|
116
|
-
let resolution;
|
|
117
|
-
if (isLooping) {
|
|
118
|
-
resolution = "looping";
|
|
119
|
-
}
|
|
120
|
-
else if (subRegime === "CLOSED") {
|
|
121
|
-
resolution = "solved";
|
|
122
|
-
}
|
|
123
|
-
else if (subRegime === "CONVERGING" && actionConsistency > 0.5) {
|
|
124
|
-
resolution = "converging";
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
resolution = "unresolved";
|
|
128
|
-
}
|
|
129
|
-
const lastEntry = this.history[this.history.length - 1];
|
|
130
|
-
const momentum = this.calcMomentum(entropyTrend, actionConsistency, embeddingDelta, isLooping, lastEntry.action, lastEntry.entropy);
|
|
131
|
-
let loopLevel = "none";
|
|
132
|
-
if (isLooping) {
|
|
133
|
-
if (this.loopCount >= 4)
|
|
134
|
-
loopLevel = "escalated";
|
|
135
|
-
else if (this.loopCount >= 3)
|
|
136
|
-
loopLevel = "assertive";
|
|
137
|
-
else if (this.loopCount >= 2)
|
|
138
|
-
loopLevel = "suggestive";
|
|
139
|
-
else
|
|
140
|
-
loopLevel = "gentle";
|
|
141
|
-
}
|
|
142
|
-
const pivotDetected = lastEntry.is_pivot || false;
|
|
143
|
-
const pivotScore = pivotDetected ? 1.0
|
|
144
|
-
: (intentState.drift_rate * 0.6 + (intentState.volatility_score * 0.4));
|
|
145
|
-
return {
|
|
146
|
-
sub_regime: subRegime,
|
|
147
|
-
resolution,
|
|
148
|
-
momentum: Math.round(momentum * 10000) / 10000,
|
|
149
|
-
signals: {
|
|
150
|
-
action_consistency: Math.round(actionConsistency * 10000) / 10000,
|
|
151
|
-
entropy_trend: Math.round(entropyTrend * 10000) / 10000,
|
|
152
|
-
feature_contradiction: Math.round(featureContradiction * 10000) / 10000,
|
|
153
|
-
embedding_delta: Math.round(embeddingDelta * 10000) / 10000,
|
|
154
|
-
},
|
|
155
|
-
intent_state: {
|
|
156
|
-
volatility_score: Math.round(intentState.volatility_score * 10000) / 10000,
|
|
157
|
-
drift_rate: Math.round(intentState.drift_rate * 10000) / 10000,
|
|
158
|
-
core_goal_embedding: intentState.core_goal_embedding,
|
|
159
|
-
},
|
|
160
|
-
continuity_state: continuityState,
|
|
161
|
-
is_looping: isLooping,
|
|
162
|
-
loop_consecutive: this.loopCount,
|
|
163
|
-
loop_intervention_level: loopLevel,
|
|
164
|
-
pivot_detected: pivotDetected,
|
|
165
|
-
pivot_score: Math.round(pivotScore * 10000) / 10000,
|
|
166
|
-
outcome: lastEntry.outcome || null,
|
|
167
|
-
n_interactions: n,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
calcActionConsistency() {
|
|
171
|
-
if (this.history.length < 2)
|
|
172
|
-
return 1.0;
|
|
173
|
-
const recent = this.history.slice(-5).map(e => e.action);
|
|
174
|
-
const counts = {};
|
|
175
|
-
for (const a of recent) {
|
|
176
|
-
counts[a] = (counts[a] || 0) + 1;
|
|
177
|
-
}
|
|
178
|
-
let mostCommonCount = 0;
|
|
179
|
-
for (const count of Object.values(counts)) {
|
|
180
|
-
if (count > mostCommonCount)
|
|
181
|
-
mostCommonCount = count;
|
|
182
|
-
}
|
|
183
|
-
return mostCommonCount / recent.length;
|
|
184
|
-
}
|
|
185
|
-
calcEntropyTrend() {
|
|
186
|
-
if (this.history.length < 2)
|
|
187
|
-
return 0.0;
|
|
188
|
-
const entropies = this.history.slice(-5).map(e => e.entropy);
|
|
189
|
-
if (entropies.length < 2)
|
|
190
|
-
return 0.0;
|
|
191
|
-
return linearTrend(entropies);
|
|
192
|
-
}
|
|
193
|
-
calcFeatureContradiction() {
|
|
194
|
-
if (this.history.length < 2)
|
|
195
|
-
return 0.0;
|
|
196
|
-
const current = this.history[this.history.length - 1].features;
|
|
197
|
-
const prev = this.history[this.history.length - 2].features;
|
|
198
|
-
let contradictionCount = 0;
|
|
199
|
-
for (const key of Object.keys(current)) {
|
|
200
|
-
if (key in prev) {
|
|
201
|
-
const delta = Math.abs(current[key] - prev[key]);
|
|
202
|
-
if (delta > 0.2) {
|
|
203
|
-
contradictionCount++;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
return Math.min(1.0, contradictionCount / 6.0);
|
|
208
|
-
}
|
|
209
|
-
calcEmbeddingDelta() {
|
|
210
|
-
if (this.history.length < 2)
|
|
211
|
-
return 0.0;
|
|
212
|
-
const embPrev = this.history[this.history.length - 2].embedding;
|
|
213
|
-
const embCurr = this.history[this.history.length - 1].embedding;
|
|
214
|
-
if (!embPrev || !embCurr)
|
|
215
|
-
return 0.0;
|
|
216
|
-
const similarity = cosineSimilarity(embPrev, embCurr);
|
|
217
|
-
return 1.0 - similarity;
|
|
218
|
-
}
|
|
219
|
-
detectLoop(k = 3, threshold = 0.6) {
|
|
220
|
-
const effectiveThreshold = this.calibratedWeights?.loopJaccard ?? threshold;
|
|
221
|
-
if (this.history.length < k + 1)
|
|
222
|
-
return false;
|
|
223
|
-
const currWords = new Set(this.history[this.history.length - 1].text.toLowerCase().split(/\s+/));
|
|
224
|
-
const pastWords = new Set(this.history[this.history.length - (k + 1)].text.toLowerCase().split(/\s+/));
|
|
225
|
-
if (currWords.size === 0 || pastWords.size === 0)
|
|
226
|
-
return false;
|
|
227
|
-
const intersection = new Set([...currWords].filter(w => pastWords.has(w)));
|
|
228
|
-
const union = new Set([...currWords, ...pastWords]);
|
|
229
|
-
const jaccard = intersection.size / Math.max(union.size, 1);
|
|
230
|
-
const infoGain = this.history[this.history.length - 1].entropy < this.history[this.history.length - (k + 1)].entropy;
|
|
231
|
-
return jaccard > effectiveThreshold && !infoGain;
|
|
232
|
-
}
|
|
233
|
-
isExploring(contradiction, entropyTrend, _actionConsistency = 0.0) {
|
|
234
|
-
return contradiction > 0.2 || entropyTrend > 0.005;
|
|
235
|
-
}
|
|
236
|
-
isRefining(contradiction, delta, actionConsistency = 0.0, entropyTrend = 0.0) {
|
|
237
|
-
return contradiction < 0.2 && delta < 0.3 && actionConsistency > 0.3 && entropyTrend > -0.01;
|
|
238
|
-
}
|
|
239
|
-
isConverging(consistency, delta, entropyTrend) {
|
|
240
|
-
return consistency >= 0.5 && delta < 0.2 && entropyTrend < 0;
|
|
241
|
-
}
|
|
242
|
-
isClosed(consistency, delta, contradiction) {
|
|
243
|
-
if (this.history.length === 0)
|
|
244
|
-
return false;
|
|
245
|
-
const lastAction = this.history[this.history.length - 1].action;
|
|
246
|
-
const lastEntropy = this.history[this.history.length - 1].entropy;
|
|
247
|
-
const ccThreshold = this.calibratedWeights?.closureConfidence ?? 0.7;
|
|
248
|
-
return (consistency > ccThreshold &&
|
|
249
|
-
delta < 0.1 &&
|
|
250
|
-
contradiction < 0.1 &&
|
|
251
|
-
["act", "commit"].includes(lastAction) &&
|
|
252
|
-
lastEntropy < 0.5);
|
|
253
|
-
}
|
|
254
|
-
isDivergent(entropyTrend, contradiction, actionConsistency) {
|
|
255
|
-
return entropyTrend > 0.03 && (contradiction > 0.3 || actionConsistency < 0.3);
|
|
256
|
-
}
|
|
257
|
-
computeIntentState() {
|
|
258
|
-
if (this.history.length < 2) {
|
|
259
|
-
return { volatility_score: 0.0, drift_rate: 0.0, core_goal_embedding: null };
|
|
260
|
-
}
|
|
261
|
-
const actionIndices = { observe: 0, defer: 1, explore: 2, act: 3, commit: 4, change: 5 };
|
|
262
|
-
const actions = this.history.slice(-5).map(e => e.action);
|
|
263
|
-
const indices = actions.map(a => actionIndices[a] ?? 2);
|
|
264
|
-
const actionVar = variance(indices) / 6.0;
|
|
265
|
-
const embs = this.history.slice(-5).map(e => e.embedding).filter((e) => e !== null);
|
|
266
|
-
let embVar = 0.0;
|
|
267
|
-
if (embs.length >= 3) {
|
|
268
|
-
const embMean = embs[0].map((_, i) => embs.reduce((sum, e) => sum + e[i], 0) / embs.length);
|
|
269
|
-
embVar = embs.reduce((sum, e) => sum + euclideanDistance(e, embMean), 0) / embs.length;
|
|
270
|
-
}
|
|
271
|
-
const volatility = Math.min(1.0, actionVar * 0.6 + embVar * 0.4);
|
|
272
|
-
let drift = 0.0;
|
|
273
|
-
if (embs.length >= 4) {
|
|
274
|
-
const mid = Math.floor(embs.length / 2);
|
|
275
|
-
const firstHalf = embs.slice(0, mid);
|
|
276
|
-
const secondHalf = embs.slice(mid);
|
|
277
|
-
const firstMean = firstHalf[0].map((_, i) => firstHalf.reduce((sum, e) => sum + e[i], 0) / firstHalf.length);
|
|
278
|
-
const secondMean = secondHalf[0].map((_, i) => secondHalf.reduce((sum, e) => sum + e[i], 0) / secondHalf.length);
|
|
279
|
-
drift = 1.0 - cosineSimilarity(firstMean, secondMean);
|
|
280
|
-
}
|
|
281
|
-
const coreGoal = embs.length > 0
|
|
282
|
-
? embs[0].map((_, i) => embs.reduce((sum, e) => sum + e[i], 0) / embs.length)
|
|
283
|
-
: null;
|
|
284
|
-
return {
|
|
285
|
-
volatility_score: Math.round(volatility * 10000) / 10000,
|
|
286
|
-
drift_rate: Math.round(drift * 10000) / 10000,
|
|
287
|
-
core_goal_embedding: coreGoal ? coreGoal.map(v => Math.round(v * 10000) / 10000) : null,
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
continuityState(intentState) {
|
|
291
|
-
const drift = intentState.drift_rate;
|
|
292
|
-
const volatility = intentState.volatility_score;
|
|
293
|
-
if (drift < 0.15 && volatility < 0.3)
|
|
294
|
-
return "HIGH";
|
|
295
|
-
if (drift > 0.4 || volatility > 0.6)
|
|
296
|
-
return "LOW";
|
|
297
|
-
return "MEDIUM";
|
|
298
|
-
}
|
|
299
|
-
static detectOverconfident(diagnostics) {
|
|
300
|
-
const confidence = diagnostics.confidence ?? 0.5;
|
|
301
|
-
const entropy = diagnostics.entropy ?? 1.0;
|
|
302
|
-
return confidence > 0.7 && entropy > 1.5;
|
|
303
|
-
}
|
|
304
|
-
calcMomentum(entropyTrend, actionConsistency, embeddingDelta, isLooping = false, action = "", entropy = 0.0) {
|
|
305
|
-
if (isLooping)
|
|
306
|
-
return -1.0;
|
|
307
|
-
const w = this.calibratedWeights?.momentum || [-0.3, 0.5, 0.2];
|
|
308
|
-
const entropyComponent = entropyTrend * w[0];
|
|
309
|
-
const consistencyComponent = actionConsistency * w[1];
|
|
310
|
-
const deltaComponent = (1.0 - Math.min(1.0, embeddingDelta)) * w[2];
|
|
311
|
-
let momentum = entropyComponent + consistencyComponent + deltaComponent;
|
|
312
|
-
if (["commit", "change"].includes(action) && entropy > 0.8) {
|
|
313
|
-
momentum -= 0.05;
|
|
314
|
-
}
|
|
315
|
-
else if (["observe", "defer"].includes(action) && entropy < 0.5) {
|
|
316
|
-
momentum += 0.05;
|
|
317
|
-
}
|
|
318
|
-
return Math.max(-1.0, Math.min(1.0, momentum));
|
|
319
|
-
}
|
|
320
|
-
reset() {
|
|
321
|
-
this.history = [];
|
|
322
|
-
this.loopCount = 0;
|
|
323
|
-
this.pivotHistory = [];
|
|
324
|
-
this.outcomeHistory = [];
|
|
325
|
-
}
|
|
326
|
-
recordOutcome(outcome) {
|
|
327
|
-
const entry = this.history[this.history.length - 1];
|
|
328
|
-
if (entry) {
|
|
329
|
-
entry.outcome = outcome;
|
|
330
|
-
this.outcomeHistory.push({
|
|
331
|
-
turn: this.history.length,
|
|
332
|
-
outcome,
|
|
333
|
-
timestamp: Date.now() / 1000,
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
getLoopIntervention() {
|
|
338
|
-
const state = this.snapshot();
|
|
339
|
-
if (!state.is_looping)
|
|
340
|
-
return null;
|
|
341
|
-
const interventions = {
|
|
342
|
-
gentle: {
|
|
343
|
-
directive: "You may be repeating yourself — try rephrasing the core question differently or approaching from a new angle.",
|
|
344
|
-
resetSuggested: false,
|
|
345
|
-
},
|
|
346
|
-
suggestive: {
|
|
347
|
-
directive: "The conversation is looping. Step back and identify what new information you need. Consider asking a different question or taking a break from this topic.",
|
|
348
|
-
resetSuggested: false,
|
|
349
|
-
},
|
|
350
|
-
assertive: {
|
|
351
|
-
directive: "You are stuck in a loop. The current approach is not productive. PIVOT: list 3 alternative approaches you haven't tried and pick one.",
|
|
352
|
-
resetSuggested: false,
|
|
353
|
-
},
|
|
354
|
-
escalated: {
|
|
355
|
-
directive: "CRITICAL: You have been looping for several turns. STOP the current approach entirely. Either SWITCH to a completely different topic or reset your strategy. Continued looping wastes time and tokens.",
|
|
356
|
-
resetSuggested: true,
|
|
357
|
-
},
|
|
358
|
-
};
|
|
359
|
-
return {
|
|
360
|
-
level: state.loop_intervention_level,
|
|
361
|
-
...interventions[state.loop_intervention_level] || interventions.gentle,
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
getPivotDirective() {
|
|
365
|
-
const state = this.snapshot();
|
|
366
|
-
if (!state.pivot_detected)
|
|
367
|
-
return null;
|
|
368
|
-
return ("PIVOT DETECTED: The conversation has shifted context. " +
|
|
369
|
-
"The previous resolution state may no longer apply. " +
|
|
370
|
-
"Acknowledge the context change and adapt your guidance accordingly. " +
|
|
371
|
-
"If the new topic is entirely unrelated to the project, confirm the scope change before proceeding.");
|
|
372
|
-
}
|
|
373
|
-
setCalibratedWeights(weights) {
|
|
374
|
-
this.calibratedWeights = weights;
|
|
375
|
-
}
|
|
376
|
-
snapshot() {
|
|
377
|
-
return this.computeState();
|
|
378
|
-
}
|
|
379
|
-
getHistory() {
|
|
380
|
-
return [...this.history];
|
|
381
|
-
}
|
|
382
|
-
getOutcomeHistory() {
|
|
383
|
-
return [...this.outcomeHistory];
|
|
384
|
-
}
|
|
385
|
-
serialize() {
|
|
386
|
-
return {
|
|
387
|
-
sessionId: this.sessionId,
|
|
388
|
-
maxHistory: this.maxHistory,
|
|
389
|
-
history: this.history,
|
|
390
|
-
loopCount: this.loopCount,
|
|
391
|
-
pivotHistory: this.pivotHistory,
|
|
392
|
-
outcomeHistory: this.outcomeHistory,
|
|
393
|
-
calibratedWeights: this.calibratedWeights,
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
static deserialize(data) {
|
|
397
|
-
const tracker = new ResolutionTracker(data.sessionId, data.maxHistory);
|
|
398
|
-
tracker.history = data.history || [];
|
|
399
|
-
tracker.loopCount = data.loopCount || 0;
|
|
400
|
-
tracker.pivotHistory = data.pivotHistory || [];
|
|
401
|
-
tracker.outcomeHistory = data.outcomeHistory || [];
|
|
402
|
-
tracker.calibratedWeights = data.calibratedWeights || null;
|
|
403
|
-
return tracker;
|
|
404
|
-
}
|
|
405
|
-
static extractFeatures(text) {
|
|
406
|
-
if (!text || typeof text !== "string")
|
|
407
|
-
return {};
|
|
408
|
-
const len = text.length;
|
|
409
|
-
const words = text.split(/\s+/).filter(w => w.length > 0);
|
|
410
|
-
const wordCount = words.length;
|
|
411
|
-
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0);
|
|
412
|
-
const sentenceCount = sentences.length;
|
|
413
|
-
const avgWordLen = wordCount > 0 ? words.reduce((s, w) => s + w.length, 0) / wordCount : 0;
|
|
414
|
-
const questions = (text.match(/\?/g) || []).length;
|
|
415
|
-
const questionRatio = sentenceCount > 0 ? questions / sentenceCount : 0;
|
|
416
|
-
const codeBlocks = (text.match(/```/g) || []).length / 2;
|
|
417
|
-
const urgency = /urgent|asap|immediately|critical|broken|failing|crash|error|bug/i.test(text) ? 1.0 : 0.0;
|
|
418
|
-
const repetition = wordCount > 5
|
|
419
|
-
? (text.toLowerCase().match(/(\b\w+\b).*?\1/g) || []).length / wordCount
|
|
420
|
-
: 0;
|
|
421
|
-
const sentimentInds = /thanks|great|perfect|awesome/i.test(text) ? 0.2
|
|
422
|
-
: /frustrat|annoy|not working|doesn't work|stupid|useless/i.test(text) ? 0.8
|
|
423
|
-
: 0.5;
|
|
424
|
-
const complexity = /complex|difficult|hard|confusing|trick|subtle|nuance/i.test(text) ? 1.0 : 0.0;
|
|
425
|
-
const instructionDensity = /do not|must|should|always|never|critical/i.test(text) ? 1.0
|
|
426
|
-
: /please|could you|maybe|perhaps/i.test(text) ? 0.3
|
|
427
|
-
: 0.6;
|
|
428
|
-
return {
|
|
429
|
-
length: Math.min(1.0, len / 5000),
|
|
430
|
-
word_count: Math.min(1.0, wordCount / 500),
|
|
431
|
-
sentence_count: Math.min(1.0, sentenceCount / 50),
|
|
432
|
-
avg_word_length: Math.min(1.0, avgWordLen / 10),
|
|
433
|
-
question_ratio: Math.min(1.0, questionRatio),
|
|
434
|
-
code_blocks: Math.min(1.0, codeBlocks / 5),
|
|
435
|
-
urgency,
|
|
436
|
-
repetition: Math.min(1.0, repetition * 10),
|
|
437
|
-
sentiment: sentimentInds,
|
|
438
|
-
complexity,
|
|
439
|
-
instruction_density: instructionDensity,
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
// ── Math Helpers ───────────────────────────────────────────────────────────
|
|
444
|
-
function linearTrend(values) {
|
|
445
|
-
const n = values.length;
|
|
446
|
-
if (n < 2)
|
|
447
|
-
return 0.0;
|
|
448
|
-
const xMean = (n - 1) / 2;
|
|
449
|
-
const yMean = values.reduce((a, b) => a + b, 0) / n;
|
|
450
|
-
let numerator = 0;
|
|
451
|
-
let denominator = 0;
|
|
452
|
-
for (let i = 0; i < n; i++) {
|
|
453
|
-
const xi = i - xMean;
|
|
454
|
-
numerator += xi * (values[i] - yMean);
|
|
455
|
-
denominator += xi * xi;
|
|
456
|
-
}
|
|
457
|
-
return denominator === 0 ? 0.0 : numerator / denominator;
|
|
458
|
-
}
|
|
459
|
-
function cosineSimilarity(a, b) {
|
|
460
|
-
if (a.length !== b.length || a.length === 0)
|
|
461
|
-
return 0.0;
|
|
462
|
-
let dot = 0;
|
|
463
|
-
let normA = 0;
|
|
464
|
-
let normB = 0;
|
|
465
|
-
for (let i = 0; i < a.length; i++) {
|
|
466
|
-
dot += a[i] * b[i];
|
|
467
|
-
normA += a[i] * a[i];
|
|
468
|
-
normB += b[i] * b[i];
|
|
469
|
-
}
|
|
470
|
-
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
471
|
-
return denom === 0 ? 0.0 : dot / denom;
|
|
472
|
-
}
|
|
473
|
-
function euclideanDistance(a, b) {
|
|
474
|
-
let sum = 0;
|
|
475
|
-
for (let i = 0; i < a.length; i++) {
|
|
476
|
-
const diff = a[i] - b[i];
|
|
477
|
-
sum += diff * diff;
|
|
478
|
-
}
|
|
479
|
-
return Math.sqrt(sum);
|
|
480
|
-
}
|
|
481
|
-
function variance(values) {
|
|
482
|
-
if (values.length === 0)
|
|
483
|
-
return 0.0;
|
|
484
|
-
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
485
|
-
return values.reduce((sum, v) => sum + (v - mean) * (v - mean), 0) / values.length;
|
|
486
|
-
}
|
package/lib/stress.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
const AGGRESSIVE_WORDS = [
|
|
2
|
-
"fuck", "shit", "bullshit", "useless", "wrong", "bad", "slow", "broken",
|
|
3
|
-
"stupid", "idiot", "hell", "damn", "waste", "annoying", "terrible", "hate"
|
|
4
|
-
]
|
|
5
|
-
|
|
6
|
-
const URGENCY_WORDS = [
|
|
7
|
-
"fix", "now", "fast", "urgent", "important", "critical", "hurry", "immediately", "asap"
|
|
8
|
-
]
|
|
9
|
-
|
|
10
|
-
const NEGATIVE_WORDS = [
|
|
11
|
-
"no", "not", "don't", "can't", "won't", "doesn't", "isn't", "shouldn't", "never", "stop"
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
const CAPS_ACRONYMS = new Set([
|
|
15
|
-
"ai", "ui", "api", "cli", "ssh", "dns", "http", "url", "json", "xml",
|
|
16
|
-
"css", "html", "sql", "csv", "yaml", "ide", "tdd", "pr", "ci", "cd",
|
|
17
|
-
"env", "os", "sdk", "gui", "crud", "rest", "crlf", "utf", "ascii"
|
|
18
|
-
])
|
|
19
|
-
|
|
20
|
-
function scoreStress(text) {
|
|
21
|
-
if (!text || typeof text !== "string") return 0
|
|
22
|
-
|
|
23
|
-
const t = text.toLowerCase()
|
|
24
|
-
let score = 0
|
|
25
|
-
|
|
26
|
-
for (const w of AGGRESSIVE_WORDS) {
|
|
27
|
-
const re = new RegExp("\\b" + w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\b", "gi")
|
|
28
|
-
const hits = (t.match(re) || []).length
|
|
29
|
-
score += hits * 0.05
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
for (const w of URGENCY_WORDS) {
|
|
33
|
-
const re = new RegExp("\\b" + w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\b", "gi")
|
|
34
|
-
const hits = (t.match(re) || []).length
|
|
35
|
-
score += hits * 0.04
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
for (const w of NEGATIVE_WORDS) {
|
|
39
|
-
const re = new RegExp("\\b" + w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\b", "gi")
|
|
40
|
-
const hits = (t.match(re) || []).length
|
|
41
|
-
score += hits * 0.02
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const words = text.split(/\s+/)
|
|
45
|
-
for (const w of words) {
|
|
46
|
-
if (w.length >= 3 && /^[A-Z]+$/.test(w) && !CAPS_ACRONYMS.has(w.toLowerCase())) {
|
|
47
|
-
score += 0.03
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const exclamParts = text.match(/!{2,}/g)
|
|
52
|
-
if (exclamParts) score += exclamParts.length * 0.05
|
|
53
|
-
|
|
54
|
-
const qmarkParts = text.match(/\?{2,}/g)
|
|
55
|
-
if (qmarkParts) score += qmarkParts.length * 0.03
|
|
56
|
-
|
|
57
|
-
const qeCombos = text.match(/\?!|!\?/g)
|
|
58
|
-
if (qeCombos) score += qeCombos.length * 0.08
|
|
59
|
-
|
|
60
|
-
if (text.length < 30) score += 0.10
|
|
61
|
-
else if (text.length < 80) score += 0.05
|
|
62
|
-
else if (text.length < 150) score += 0.02
|
|
63
|
-
|
|
64
|
-
return Math.min(score, 1.0)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function getStressLevel(score) {
|
|
68
|
-
if (score > 0.7) return { level: "critical", gauge: "\u2588", directive: "CRITICAL: User is highly stressed. Prioritize quick, actionable responses. Avoid lengthy explanations." }
|
|
69
|
-
if (score > 0.4) return { level: "elevated", gauge: "\u2586\u2588", directive: "Elevated stress detected. Be concise and solution-focused." }
|
|
70
|
-
if (score > 0.2) return { level: "moderate", gauge: "\u2584\u2586\u2588", directive: null }
|
|
71
|
-
return { level: "calm", gauge: "\u2581\u2582\u2583\u2585\u2586\u2588", directive: null }
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function buildStressFooter(score) {
|
|
75
|
-
const { level, gauge } = getStressLevel(score)
|
|
76
|
-
const bar = score > 0.7 ? "\u2588\u2588\u2588\u2588\u2588"
|
|
77
|
-
: score > 0.5 ? "\u2584\u2585\u2586\u2588\u2588"
|
|
78
|
-
: score > 0.3 ? "\u2582\u2583\u2584\u2585\u2586"
|
|
79
|
-
: score > 0.1 ? "\u2581\u2582\u2583\u2584\u2585"
|
|
80
|
-
: "\u2581\u2581\u2581\u2581\u2581"
|
|
81
|
-
return `stress: [${bar}] (${level})`
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export { scoreStress, getStressLevel, buildStressFooter, AGGRESSIVE_WORDS, URGENCY_WORDS, NEGATIVE_WORDS }
|