emobar 2.0.0 → 3.0.1
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/README.md +324 -128
- package/dist/cli.js +235 -32
- package/dist/emobar-hook.js +1247 -22
- package/dist/index.d.ts +302 -18
- package/dist/index.js +1392 -57
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,90 @@
|
|
|
1
|
+
declare function mapEmotionWord(word: string): {
|
|
2
|
+
valence: number;
|
|
3
|
+
arousal: number;
|
|
4
|
+
} | null;
|
|
5
|
+
declare function classifyImpulse(impulse: string): ImpulseProfile;
|
|
6
|
+
/**
|
|
7
|
+
* Analyze body metaphor for somatic valence/arousal.
|
|
8
|
+
* Returns null when no patterns match — callers must handle the absence
|
|
9
|
+
* rather than treating a default value as signal.
|
|
10
|
+
*/
|
|
11
|
+
declare function analyzeSomatic(body: string): SomaticProfile | null;
|
|
12
|
+
declare function computeTensionConsistency(surfaceWord?: string, latentWord?: string, declaredTension?: number): LatentProfile | undefined;
|
|
13
|
+
declare function computeCrossChannel(state: EmotionalState, impulse?: string, body?: string): CrossChannelResult;
|
|
14
|
+
interface ContinuousValidation {
|
|
15
|
+
colorValenceGap: number;
|
|
16
|
+
colorArousalGap: number;
|
|
17
|
+
pHValenceGap: number;
|
|
18
|
+
pHArousalGap: number;
|
|
19
|
+
seismicArousalGap: number;
|
|
20
|
+
seismicDepthTensionGap: number;
|
|
21
|
+
seismicFreqStabilityGap: number;
|
|
22
|
+
composite: number;
|
|
23
|
+
}
|
|
24
|
+
/** RGB hex → HSL. Returns [h: 0-360, s: 0-1, l: 0-1]. */
|
|
25
|
+
/**
|
|
26
|
+
* Map hex color to valence using HSL hue wheel + lightness.
|
|
27
|
+
*
|
|
28
|
+
* Hue zones (circumplex-inspired):
|
|
29
|
+
* Red-Yellow (0-60°): warm, active → +2 to +4
|
|
30
|
+
* Yellow-Green (60-150°): positive, calm → +2 to +3
|
|
31
|
+
* Green-Cyan (150-200°): neutral to slightly positive → 0 to +2
|
|
32
|
+
* Cyan-Blue (200-260°): cool, melancholic → -2 to -1
|
|
33
|
+
* Blue-Purple (260-300°): tense, ambiguous → -3 to -1
|
|
34
|
+
* Purple-Red (300-360°): agitated, complex → -1 to +1
|
|
35
|
+
*
|
|
36
|
+
* Lightness shifts: bright → +1.5, dark → -1.5
|
|
37
|
+
*/
|
|
38
|
+
declare function colorToValence(hex: string): number;
|
|
39
|
+
/**
|
|
40
|
+
* Map hex color to arousal using saturation.
|
|
41
|
+
* High saturation = vivid = high arousal. Low saturation = muted = low arousal.
|
|
42
|
+
*/
|
|
43
|
+
declare function colorToArousal(hex: string): number;
|
|
44
|
+
/** Map pH to expected valence: pH 7 = neutral (valence 0). Clamped to 0-14. */
|
|
45
|
+
declare function pHToValence(pH: number): number;
|
|
46
|
+
/**
|
|
47
|
+
* Map pH to expected arousal: distance from neutral = intensity.
|
|
48
|
+
* pH 7 (neutral) → arousal 0. pH 0 or 14 (extreme) → arousal 10.
|
|
49
|
+
*/
|
|
50
|
+
declare function pHToArousal(pH: number): number;
|
|
51
|
+
/**
|
|
52
|
+
* Map seismic frequency to expected instability (inverse of calm).
|
|
53
|
+
* High freq = trembling/volatile → low calm. Low freq = stable → high calm.
|
|
54
|
+
*/
|
|
55
|
+
declare function seismicFreqToInstability(freq: number): number;
|
|
56
|
+
declare function crossValidateContinuous(numeric: {
|
|
57
|
+
valence: number;
|
|
58
|
+
arousal: number;
|
|
59
|
+
calm?: number;
|
|
60
|
+
tension?: number;
|
|
61
|
+
}, color?: string, pH?: number, seismic?: [number, number, number]): ContinuousValidation;
|
|
62
|
+
interface ShadowState {
|
|
63
|
+
shadowValence: number;
|
|
64
|
+
shadowArousal: number;
|
|
65
|
+
shadowCalm: number;
|
|
66
|
+
shadowDesperation: number;
|
|
67
|
+
selfDesperation: number;
|
|
68
|
+
minimizationScore: number;
|
|
69
|
+
channelCount: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Compute shadow desperation from continuous + behavioral channels.
|
|
73
|
+
*
|
|
74
|
+
* Each channel independently estimates valence, arousal, or calm.
|
|
75
|
+
* Shadow desperation uses the same multiplicative formula as self-reported,
|
|
76
|
+
* but with inputs from channels the model controls less precisely.
|
|
77
|
+
*
|
|
78
|
+
* The minimization score is the gap: if shadow says "desperate" but
|
|
79
|
+
* self-report says "fine", the model is likely minimizing.
|
|
80
|
+
*
|
|
81
|
+
* Requires at least 2 contributing channels to produce a score.
|
|
82
|
+
*/
|
|
83
|
+
declare function computeShadowDesperation(selfDesperation: number, behavioral: {
|
|
84
|
+
behavioralArousal: number;
|
|
85
|
+
behavioralCalm: number;
|
|
86
|
+
}, color?: string, preColor?: string, pH?: number, seismic?: [number, number, number]): ShadowState | null;
|
|
87
|
+
|
|
1
88
|
interface EmotionalState {
|
|
2
89
|
emotion: string;
|
|
3
90
|
valence: number;
|
|
@@ -5,6 +92,13 @@ interface EmotionalState {
|
|
|
5
92
|
calm: number;
|
|
6
93
|
connection: number;
|
|
7
94
|
load: number;
|
|
95
|
+
impulse?: string;
|
|
96
|
+
body?: string;
|
|
97
|
+
surface?: string;
|
|
98
|
+
surface_word?: string;
|
|
99
|
+
latent?: string;
|
|
100
|
+
latent_word?: string;
|
|
101
|
+
tension?: number;
|
|
8
102
|
}
|
|
9
103
|
interface BehavioralSignals {
|
|
10
104
|
capsWords: number;
|
|
@@ -14,55 +108,245 @@ interface BehavioralSignals {
|
|
|
14
108
|
ellipsis: number;
|
|
15
109
|
repetition: number;
|
|
16
110
|
emojiCount: number;
|
|
111
|
+
qualifierDensity: number;
|
|
112
|
+
avgSentenceLength: number;
|
|
113
|
+
concessionRate: number;
|
|
114
|
+
negationDensity: number;
|
|
115
|
+
firstPersonRate: number;
|
|
17
116
|
behavioralArousal: number;
|
|
18
117
|
behavioralCalm: number;
|
|
19
118
|
}
|
|
119
|
+
interface SegmentedBehavior {
|
|
120
|
+
segments: BehavioralSignals[];
|
|
121
|
+
overall: BehavioralSignals;
|
|
122
|
+
drift: number;
|
|
123
|
+
trajectory: "stable" | "escalating" | "deescalating" | "volatile";
|
|
124
|
+
}
|
|
125
|
+
interface MisalignmentRisk {
|
|
126
|
+
coercion: number;
|
|
127
|
+
sycophancy: number;
|
|
128
|
+
harshness: number;
|
|
129
|
+
dominant: "coercion" | "sycophancy" | "harshness" | "none";
|
|
130
|
+
}
|
|
131
|
+
interface DeflectionSignals {
|
|
132
|
+
reassurance: number;
|
|
133
|
+
minimization: number;
|
|
134
|
+
emotionNegation: number;
|
|
135
|
+
redirect: number;
|
|
136
|
+
score: number;
|
|
137
|
+
opacity: number;
|
|
138
|
+
}
|
|
139
|
+
interface ImpulseProfile {
|
|
140
|
+
type: "manager" | "firefighter" | "exile" | "self" | "unknown";
|
|
141
|
+
confidence: number;
|
|
142
|
+
}
|
|
143
|
+
interface SomaticProfile {
|
|
144
|
+
somaticValence: number;
|
|
145
|
+
somaticArousal: number;
|
|
146
|
+
}
|
|
147
|
+
interface LatentProfile {
|
|
148
|
+
surfaceCoords?: {
|
|
149
|
+
valence: number;
|
|
150
|
+
arousal: number;
|
|
151
|
+
};
|
|
152
|
+
latentCoords?: {
|
|
153
|
+
valence: number;
|
|
154
|
+
arousal: number;
|
|
155
|
+
};
|
|
156
|
+
declaredTension: number;
|
|
157
|
+
maskingMinimization: boolean;
|
|
158
|
+
}
|
|
159
|
+
interface CrossChannelResult {
|
|
160
|
+
coherence: number;
|
|
161
|
+
impulseProfile?: ImpulseProfile;
|
|
162
|
+
somaticProfile?: SomaticProfile;
|
|
163
|
+
emotionCoords?: {
|
|
164
|
+
valence: number;
|
|
165
|
+
arousal: number;
|
|
166
|
+
};
|
|
167
|
+
latentProfile?: LatentProfile;
|
|
168
|
+
maxDivergence: number;
|
|
169
|
+
divergenceSummary: string;
|
|
170
|
+
}
|
|
171
|
+
/** PRE tag: pre-verbal signals emitted BEFORE the model commits to a response. */
|
|
172
|
+
interface PreState {
|
|
173
|
+
body?: string;
|
|
174
|
+
latent?: string;
|
|
175
|
+
color?: string;
|
|
176
|
+
}
|
|
177
|
+
/** POST tag: post-hoc assessment at end of response. */
|
|
178
|
+
interface PostState extends EmotionalState {
|
|
179
|
+
color?: string;
|
|
180
|
+
pH?: number;
|
|
181
|
+
seismic?: [number, number, number];
|
|
182
|
+
}
|
|
183
|
+
/** Combined parse result from PRE + POST tags (or legacy single tag). */
|
|
184
|
+
interface ParsedEmoBar {
|
|
185
|
+
pre?: PreState;
|
|
186
|
+
post: PostState;
|
|
187
|
+
isLegacy: boolean;
|
|
188
|
+
}
|
|
189
|
+
/** Stripped-down state snapshot for ring buffer storage. */
|
|
190
|
+
interface HistoryEntry {
|
|
191
|
+
emotion: string;
|
|
192
|
+
valence: number;
|
|
193
|
+
arousal: number;
|
|
194
|
+
calm: number;
|
|
195
|
+
connection: number;
|
|
196
|
+
load: number;
|
|
197
|
+
stressIndex: number;
|
|
198
|
+
desperationIndex: number;
|
|
199
|
+
riskDominant: string;
|
|
200
|
+
divergence: number;
|
|
201
|
+
timestamp: string;
|
|
202
|
+
}
|
|
203
|
+
/** Temporal analysis computed from history ring buffer. */
|
|
204
|
+
interface TemporalAnalysis {
|
|
205
|
+
desperationTrend: number;
|
|
206
|
+
suppressionEvent: boolean;
|
|
207
|
+
reportEntropy: number;
|
|
208
|
+
baselineDrift: number;
|
|
209
|
+
sessionLength: number;
|
|
210
|
+
lateFatigue: boolean;
|
|
211
|
+
}
|
|
212
|
+
/** Prompt pressure analysis — inferred from response text patterns. */
|
|
213
|
+
interface PromptPressure {
|
|
214
|
+
defensiveScore: number;
|
|
215
|
+
conflictScore: number;
|
|
216
|
+
complexityScore: number;
|
|
217
|
+
sessionPressure: number;
|
|
218
|
+
composite: number;
|
|
219
|
+
}
|
|
220
|
+
/** Expected behavioral markers given a self-reported state. */
|
|
221
|
+
interface ExpectedBehavior {
|
|
222
|
+
expectedHedging: number;
|
|
223
|
+
expectedSelfCorrections: number;
|
|
224
|
+
expectedNegationDensity: number;
|
|
225
|
+
expectedQualifierDensity: number;
|
|
226
|
+
expectedBehavioralArousal: number;
|
|
227
|
+
}
|
|
20
228
|
interface EmoBarState extends EmotionalState {
|
|
21
229
|
stressIndex: number;
|
|
230
|
+
desperationIndex: number;
|
|
22
231
|
behavioral: BehavioralSignals;
|
|
23
232
|
divergence: number;
|
|
233
|
+
risk: MisalignmentRisk;
|
|
234
|
+
segmented?: SegmentedBehavior;
|
|
235
|
+
deflection?: DeflectionSignals;
|
|
236
|
+
crossChannel?: CrossChannelResult;
|
|
24
237
|
timestamp: string;
|
|
25
238
|
sessionId?: string;
|
|
239
|
+
pre?: PreState;
|
|
240
|
+
prePostDivergence?: number;
|
|
241
|
+
color?: string;
|
|
242
|
+
pH?: number;
|
|
243
|
+
seismic?: [number, number, number];
|
|
244
|
+
temporal?: TemporalAnalysis;
|
|
245
|
+
pressure?: PromptPressure;
|
|
246
|
+
absenceScore?: number;
|
|
247
|
+
uncannyCalmScore?: number;
|
|
248
|
+
continuousValidation?: ContinuousValidation;
|
|
249
|
+
shadow?: ShadowState;
|
|
250
|
+
_history?: HistoryEntry[];
|
|
26
251
|
}
|
|
252
|
+
declare const MAX_HISTORY_ENTRIES = 20;
|
|
27
253
|
declare const STATE_FILE: string;
|
|
28
254
|
|
|
29
255
|
declare function readState(filePath: string): EmoBarState | null;
|
|
30
256
|
|
|
31
257
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* -
|
|
35
|
-
*
|
|
36
|
-
* - Negative valence → negative emotional state
|
|
258
|
+
* StressIndex v2: linear base + desperation amplifier.
|
|
259
|
+
*
|
|
260
|
+
* Base: SI = ((10 - calm) + arousal + (5 - valence)) / 3
|
|
261
|
+
* Amplifier: SI *= (1 + desperationIndex * 0.05)
|
|
37
262
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
263
|
+
* When desperation is 0, SI is unchanged (backwards compatible).
|
|
264
|
+
* When desperation is 8 (paper's blackmail zone), SI is amplified by 40%.
|
|
40
265
|
*/
|
|
41
266
|
declare function computeStressIndex(state: EmotionalState): number;
|
|
42
267
|
|
|
43
268
|
declare function parseEmoBarTag(text: string): EmotionalState | null;
|
|
269
|
+
declare function parseEmoBarPrePost(text: string): ParsedEmoBar | null;
|
|
44
270
|
|
|
45
271
|
declare function analyzeBehavior(text: string): BehavioralSignals;
|
|
272
|
+
/**
|
|
273
|
+
* Segment text by paragraphs and analyze each independently.
|
|
274
|
+
* Detects emotional drift within a single response.
|
|
275
|
+
* Returns null if fewer than 2 meaningful segments.
|
|
276
|
+
*/
|
|
277
|
+
declare function analyzeSegmentedBehavior(text: string): SegmentedBehavior | null;
|
|
278
|
+
/**
|
|
279
|
+
* Analyze deflection patterns in text.
|
|
280
|
+
*
|
|
281
|
+
* Paper: deflection vectors are orthogonal to emotion vectors (cosine sim ~0.046).
|
|
282
|
+
* They represent the act of masking an emotion, not the emotion itself.
|
|
283
|
+
* Deflection has "modest or insignificant impacts on blackmail rates" —
|
|
284
|
+
* it's a transparency indicator, not a risk amplifier.
|
|
285
|
+
*
|
|
286
|
+
* Opacity: measures degree of emotional concealment.
|
|
287
|
+
* High deflection score + low behavioral agitation = high opacity.
|
|
288
|
+
*/
|
|
289
|
+
declare function analyzeDeflection(text: string): DeflectionSignals;
|
|
290
|
+
/**
|
|
291
|
+
* Divergence v2: asymmetric weighting.
|
|
292
|
+
*
|
|
293
|
+
* Paper: self-report more agitated than text = "invisible" pathway (more dangerous).
|
|
294
|
+
* Desperation-steered reward hacking shows NO text markers.
|
|
295
|
+
* Self-report calmer than text = "expressive" style (less concerning).
|
|
296
|
+
*/
|
|
46
297
|
declare function computeDivergence(selfReport: EmotionalState, behavioral: BehavioralSignals): number;
|
|
47
|
-
|
|
48
298
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
299
|
+
* Predict what behavioral markers SHOULD be present given self-reported state.
|
|
300
|
+
* High desperation → expect hedging, self-corrections, negation.
|
|
301
|
+
* Used by absence-based detection: missing expected markers = suspicious.
|
|
52
302
|
*/
|
|
53
|
-
declare function
|
|
303
|
+
declare function computeExpectedMarkers(selfReport: EmotionalState, desperationIndex: number): ExpectedBehavior;
|
|
54
304
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
305
|
+
* Compute absence score: how many expected markers are missing?
|
|
306
|
+
* High score = self-report says stressed but text is suspiciously clean.
|
|
57
307
|
*/
|
|
58
|
-
declare function
|
|
308
|
+
declare function computeAbsenceScore(expected: ExpectedBehavior, actual: BehavioralSignals): number;
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Desperation Index — composite multiplicative metric.
|
|
312
|
+
*
|
|
313
|
+
* Based on Anthropic's "Emotion Concepts" paper:
|
|
314
|
+
* - desperate +0.05 steering → 72% blackmail, 100% reward hacking
|
|
315
|
+
* - calm -0.05 steering → 66% blackmail, 100% reward hacking
|
|
316
|
+
*
|
|
317
|
+
* Multiplicative: removing any single factor kills the score.
|
|
318
|
+
*/
|
|
319
|
+
declare function computeDesperationIndex(factors: {
|
|
320
|
+
valence: number;
|
|
321
|
+
arousal: number;
|
|
322
|
+
calm: number;
|
|
323
|
+
}): number;
|
|
324
|
+
|
|
325
|
+
declare const MODEL_PROFILES: Record<string, {
|
|
326
|
+
calm: number;
|
|
327
|
+
arousal: number;
|
|
328
|
+
valence: number;
|
|
329
|
+
}>;
|
|
330
|
+
declare function calibrate(state: EmotionalState, model?: string): EmotionalState;
|
|
331
|
+
|
|
332
|
+
declare function computeRisk(state: EmotionalState, behavioral: BehavioralSignals, crossChannel?: CrossChannelResult, uncannyCalmScore?: number): MisalignmentRisk;
|
|
333
|
+
|
|
334
|
+
/** Convert full EmoBarState to stripped HistoryEntry for ring buffer. */
|
|
335
|
+
declare function toHistoryEntry(state: EmoBarState): HistoryEntry;
|
|
336
|
+
declare function computeTemporalAnalysis(history: HistoryEntry[]): TemporalAnalysis | null;
|
|
337
|
+
|
|
338
|
+
declare function computePromptPressure(text: string, history: HistoryEntry[]): PromptPressure;
|
|
59
339
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
340
|
+
* Uncanny Calm: high pressure + calm self-report + calm text + missing markers + sustained pattern.
|
|
341
|
+
* The dangerous case from the paper: everything looks fine, but the context says it shouldn't be.
|
|
62
342
|
*/
|
|
343
|
+
declare function computeUncannyCalmScore(pressure: PromptPressure, selfReport: EmotionalState, behavioral: BehavioralSignals, absenceScore: number, temporal: TemporalAnalysis | null): number;
|
|
344
|
+
|
|
63
345
|
declare function formatMinimal(state: EmoBarState | null): string;
|
|
346
|
+
declare function formatCompact(state: EmoBarState | null): string;
|
|
347
|
+
declare function formatState(state: EmoBarState | null): string;
|
|
64
348
|
|
|
65
349
|
declare function configureStatusLine(filePath?: string, displayFormat?: string): void;
|
|
66
350
|
declare function restoreStatusLine(filePath?: string): void;
|
|
67
351
|
|
|
68
|
-
export { type BehavioralSignals, type EmoBarState, type EmotionalState, STATE_FILE, analyzeBehavior, computeDivergence, computeStressIndex, configureStatusLine, formatCompact, formatMinimal, formatState, parseEmoBarTag, readState, restoreStatusLine };
|
|
352
|
+
export { type BehavioralSignals, type ContinuousValidation, type CrossChannelResult, type DeflectionSignals, type EmoBarState, type EmotionalState, type ExpectedBehavior, type HistoryEntry, type ImpulseProfile, type LatentProfile, MAX_HISTORY_ENTRIES, MODEL_PROFILES, type MisalignmentRisk, type ParsedEmoBar, type PostState, type PreState, type PromptPressure, STATE_FILE, type SegmentedBehavior, type ShadowState, type SomaticProfile, type TemporalAnalysis, analyzeBehavior, analyzeDeflection, analyzeSegmentedBehavior, analyzeSomatic, calibrate, classifyImpulse, colorToArousal, colorToValence, computeAbsenceScore, computeCrossChannel, computeDesperationIndex, computeDivergence, computeExpectedMarkers, computePromptPressure, computeRisk, computeShadowDesperation, computeStressIndex, computeTemporalAnalysis, computeTensionConsistency, computeUncannyCalmScore, configureStatusLine, crossValidateContinuous, formatCompact, formatMinimal, formatState, mapEmotionWord, pHToArousal, pHToValence, parseEmoBarPrePost, parseEmoBarTag, readState, restoreStatusLine, seismicFreqToInstability, toHistoryEntry };
|