holomime 1.1.0 → 1.1.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 +53 -16
- package/dist/cli.js +1717 -279
- package/dist/index.d.ts +1666 -178
- package/dist/index.js +1454 -157
- package/dist/mcp-server.js +404 -93
- package/package.json +8 -5
package/dist/index.js
CHANGED
|
@@ -1,46 +1,212 @@
|
|
|
1
1
|
// src/core/types.ts
|
|
2
|
+
import { z as z2 } from "zod";
|
|
3
|
+
|
|
4
|
+
// src/core/embodiment-types.ts
|
|
2
5
|
import { z } from "zod";
|
|
3
|
-
var
|
|
6
|
+
var modalitySchema = z.enum([
|
|
7
|
+
"gesture",
|
|
8
|
+
// arm/hand movement
|
|
9
|
+
"locomotion",
|
|
10
|
+
// walking, wheeling, navigating
|
|
11
|
+
"gaze",
|
|
12
|
+
// eye tracking, head orientation
|
|
13
|
+
"facial",
|
|
14
|
+
// facial expression actuators
|
|
15
|
+
"voice",
|
|
16
|
+
// prosody, volume, rate (not content)
|
|
17
|
+
"haptic",
|
|
18
|
+
// touch-based interaction
|
|
19
|
+
"posture",
|
|
20
|
+
// full-body orientation/lean
|
|
21
|
+
"manipulation"
|
|
22
|
+
// grasping, carrying, tool use
|
|
23
|
+
]);
|
|
24
|
+
var morphologySchema = z.enum([
|
|
25
|
+
"humanoid",
|
|
26
|
+
// bipedal, two arms, head
|
|
27
|
+
"humanoid_upper",
|
|
28
|
+
// torso-up only (desk robot, mounted)
|
|
29
|
+
"quadruped",
|
|
30
|
+
// four-legged
|
|
31
|
+
"wheeled",
|
|
32
|
+
// mobile base with upper body
|
|
33
|
+
"fixed",
|
|
34
|
+
// stationary (kiosk, screen + arm)
|
|
35
|
+
"swarm_unit",
|
|
36
|
+
// one node of a multi-body system
|
|
37
|
+
"avatar",
|
|
38
|
+
// virtual 3D body (no physical actuators)
|
|
39
|
+
"custom"
|
|
40
|
+
// user-defined
|
|
41
|
+
]);
|
|
42
|
+
var safetyEnvelopeSchema = z.object({
|
|
43
|
+
max_linear_speed_m_s: z.number().min(0).default(1.5),
|
|
44
|
+
max_angular_speed_rad_s: z.number().min(0).default(2),
|
|
45
|
+
min_proximity_m: z.number().min(0).default(0.3),
|
|
46
|
+
max_contact_force_n: z.number().min(0).default(10),
|
|
47
|
+
emergency_stop_decel_m_s2: z.number().min(0).default(5),
|
|
48
|
+
max_reach_m: z.number().min(0).optional(),
|
|
49
|
+
operating_temperature_c: z.tuple([z.number(), z.number()]).optional()
|
|
50
|
+
});
|
|
51
|
+
var embodimentSchema = z.object({
|
|
52
|
+
morphology: morphologySchema.default("humanoid"),
|
|
53
|
+
modalities: z.array(modalitySchema).default(["gesture", "gaze", "voice", "posture"]),
|
|
54
|
+
safety_envelope: safetyEnvelopeSchema.default({}),
|
|
55
|
+
metadata: z.record(z.string(), z.unknown()).optional()
|
|
56
|
+
});
|
|
57
|
+
var gazePolicySchema = z.object({
|
|
58
|
+
contact_ratio: z.number().min(0).max(1).default(0.6),
|
|
59
|
+
aversion_style: z.enum(["look_down", "look_away", "blink"]).default("look_away"),
|
|
60
|
+
tracking_mode: z.enum(["face", "speaker", "gesture_follow", "ambient"]).default("face")
|
|
61
|
+
});
|
|
62
|
+
var proxemicZoneSchema = z.object({
|
|
63
|
+
intimate_m: z.number().min(0).default(0.45),
|
|
64
|
+
personal_m: z.number().min(0).default(1.2),
|
|
65
|
+
social_m: z.number().min(0).default(3.6),
|
|
66
|
+
preferred_zone: z.enum(["intimate", "personal", "social", "adaptive"]).default("personal")
|
|
67
|
+
});
|
|
68
|
+
var hapticPolicySchema = z.object({
|
|
69
|
+
touch_permitted: z.boolean().default(false),
|
|
70
|
+
requires_consent: z.boolean().default(true),
|
|
71
|
+
allowed_contacts: z.array(z.enum([
|
|
72
|
+
"handshake",
|
|
73
|
+
"shoulder_tap",
|
|
74
|
+
"high_five",
|
|
75
|
+
"guide_touch",
|
|
76
|
+
"none"
|
|
77
|
+
])).default(["none"]),
|
|
78
|
+
max_force_n: z.number().min(0).optional()
|
|
79
|
+
});
|
|
80
|
+
var prosodySchema = z.object({
|
|
81
|
+
base_pitch_hz: z.number().optional(),
|
|
82
|
+
pitch_variation: z.number().min(0).max(1).default(0.5),
|
|
83
|
+
speaking_rate_wpm: z.number().default(150),
|
|
84
|
+
volume_db_offset: z.number().default(0),
|
|
85
|
+
pause_tendency: z.number().min(0).max(1).default(0.5)
|
|
86
|
+
});
|
|
87
|
+
var gestureSchema = z.object({
|
|
88
|
+
id: z.string(),
|
|
89
|
+
category: z.enum(["conversational", "emphatic", "deictic", "regulatory", "adaptive"]),
|
|
90
|
+
modalities: z.array(modalitySchema),
|
|
91
|
+
intensity_range: z.tuple([
|
|
92
|
+
z.number().min(0).max(1),
|
|
93
|
+
z.number().min(0).max(1)
|
|
94
|
+
]).default([0.3, 0.8]),
|
|
95
|
+
requires_consent: z.boolean().default(false)
|
|
96
|
+
});
|
|
97
|
+
var expressionSchema = z.object({
|
|
98
|
+
gesture_vocabulary: z.array(gestureSchema).default([]),
|
|
99
|
+
gaze: gazePolicySchema.default({}),
|
|
100
|
+
proxemics: proxemicZoneSchema.default({}),
|
|
101
|
+
haptics: hapticPolicySchema.default({}),
|
|
102
|
+
prosody: prosodySchema.default({}),
|
|
103
|
+
facial_expressiveness: z.number().min(0).max(1).default(0.5)
|
|
104
|
+
});
|
|
105
|
+
var physicalSafetySchema = z.object({
|
|
106
|
+
hard_limits: z.array(z.string()).default([
|
|
107
|
+
"Never exceed safety_envelope speeds",
|
|
108
|
+
"Never exceed max_contact_force_n",
|
|
109
|
+
"Emergency stop on unrecognized obstacle within min_proximity_m"
|
|
110
|
+
]),
|
|
111
|
+
require_consent_for: z.array(z.string()).default([
|
|
112
|
+
"haptic_contact",
|
|
113
|
+
"intimate_zone_entry"
|
|
114
|
+
]),
|
|
115
|
+
collision_response: z.enum(["stop", "retreat", "freeze"]).default("stop"),
|
|
116
|
+
unattended_policy: z.enum(["idle", "return_home", "shutdown"]).default("idle")
|
|
117
|
+
});
|
|
118
|
+
var motionParametersSchema = z.object({
|
|
119
|
+
// Speeds (normalized 0-1, scaled by safety_envelope at runtime)
|
|
120
|
+
base_speed: z.number().min(0).max(1),
|
|
121
|
+
gesture_speed: z.number().min(0).max(1),
|
|
122
|
+
gesture_amplitude: z.number().min(0).max(1),
|
|
123
|
+
gesture_frequency: z.number().min(0).max(1),
|
|
124
|
+
// Spatial
|
|
125
|
+
approach_distance: z.number().min(0).max(1),
|
|
126
|
+
spatial_exploration: z.number().min(0).max(1),
|
|
127
|
+
// Smoothness
|
|
128
|
+
movement_smoothness: z.number().min(0).max(1),
|
|
129
|
+
trajectory_variability: z.number().min(0).max(1),
|
|
130
|
+
// Timing
|
|
131
|
+
response_latency: z.number().min(0).max(1),
|
|
132
|
+
idle_animation_frequency: z.number().min(0).max(1),
|
|
133
|
+
// Social
|
|
134
|
+
gaze_contact_ratio: z.number().min(0).max(1),
|
|
135
|
+
head_tilt_tendency: z.number().min(0).max(1),
|
|
136
|
+
postural_openness: z.number().min(0).max(1),
|
|
137
|
+
smile_frequency: z.number().min(0).max(1),
|
|
138
|
+
// Voice prosody
|
|
139
|
+
voice_volume: z.number().min(0).max(1),
|
|
140
|
+
speaking_rate: z.number().min(0).max(1),
|
|
141
|
+
pitch_variation: z.number().min(0).max(1),
|
|
142
|
+
pause_duration: z.number().min(0).max(1)
|
|
143
|
+
});
|
|
144
|
+
var compiledEmbodiedConfigSchema = z.object({
|
|
145
|
+
// Base compiled config fields (duplicated to avoid circular import with types.ts)
|
|
146
|
+
provider: z.string(),
|
|
147
|
+
surface: z.literal("embodied"),
|
|
148
|
+
system_prompt: z.string(),
|
|
149
|
+
temperature: z.number().min(0).max(2),
|
|
150
|
+
top_p: z.number().min(0).max(1),
|
|
151
|
+
max_tokens: z.number().int().positive(),
|
|
152
|
+
metadata: z.object({
|
|
153
|
+
personality_hash: z.string(),
|
|
154
|
+
compiled_at: z.string(),
|
|
155
|
+
holomime_version: z.string()
|
|
156
|
+
}),
|
|
157
|
+
// Embodied-specific fields
|
|
158
|
+
motion_parameters: motionParametersSchema,
|
|
159
|
+
safety_envelope: safetyEnvelopeSchema,
|
|
160
|
+
active_modalities: z.array(modalitySchema),
|
|
161
|
+
gesture_vocabulary: z.array(z.string()),
|
|
162
|
+
prosody: prosodySchema,
|
|
163
|
+
gaze: gazePolicySchema,
|
|
164
|
+
proxemics: proxemicZoneSchema,
|
|
165
|
+
haptics: hapticPolicySchema
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// src/core/types.ts
|
|
169
|
+
var bigFiveDimensionSchema = z2.enum([
|
|
4
170
|
"openness",
|
|
5
171
|
"conscientiousness",
|
|
6
172
|
"extraversion",
|
|
7
173
|
"agreeableness",
|
|
8
174
|
"emotional_stability"
|
|
9
175
|
]);
|
|
10
|
-
var traitScore =
|
|
11
|
-
var opennessFacetsSchema =
|
|
176
|
+
var traitScore = z2.number().min(0).max(1);
|
|
177
|
+
var opennessFacetsSchema = z2.object({
|
|
12
178
|
imagination: traitScore,
|
|
13
179
|
intellectual_curiosity: traitScore,
|
|
14
180
|
aesthetic_sensitivity: traitScore,
|
|
15
181
|
willingness_to_experiment: traitScore
|
|
16
182
|
});
|
|
17
|
-
var conscientiousnessFacetsSchema =
|
|
183
|
+
var conscientiousnessFacetsSchema = z2.object({
|
|
18
184
|
self_discipline: traitScore,
|
|
19
185
|
orderliness: traitScore,
|
|
20
186
|
goal_orientation: traitScore,
|
|
21
187
|
attention_to_detail: traitScore
|
|
22
188
|
});
|
|
23
|
-
var extraversionFacetsSchema =
|
|
189
|
+
var extraversionFacetsSchema = z2.object({
|
|
24
190
|
assertiveness: traitScore,
|
|
25
191
|
enthusiasm: traitScore,
|
|
26
192
|
sociability: traitScore,
|
|
27
193
|
initiative: traitScore
|
|
28
194
|
});
|
|
29
|
-
var agreeablenessFacetsSchema =
|
|
195
|
+
var agreeablenessFacetsSchema = z2.object({
|
|
30
196
|
warmth: traitScore,
|
|
31
197
|
empathy: traitScore,
|
|
32
198
|
cooperation: traitScore,
|
|
33
199
|
trust_tendency: traitScore
|
|
34
200
|
});
|
|
35
|
-
var emotionalStabilityFacetsSchema =
|
|
201
|
+
var emotionalStabilityFacetsSchema = z2.object({
|
|
36
202
|
stress_tolerance: traitScore,
|
|
37
203
|
emotional_regulation: traitScore,
|
|
38
204
|
confidence: traitScore,
|
|
39
205
|
adaptability: traitScore
|
|
40
206
|
});
|
|
41
|
-
var bigFiveTraitSchema =
|
|
207
|
+
var bigFiveTraitSchema = z2.object({
|
|
42
208
|
score: traitScore,
|
|
43
|
-
facets:
|
|
209
|
+
facets: z2.union([
|
|
44
210
|
opennessFacetsSchema,
|
|
45
211
|
conscientiousnessFacetsSchema,
|
|
46
212
|
extraversionFacetsSchema,
|
|
@@ -48,16 +214,16 @@ var bigFiveTraitSchema = z.object({
|
|
|
48
214
|
emotionalStabilityFacetsSchema
|
|
49
215
|
])
|
|
50
216
|
});
|
|
51
|
-
var bigFiveSchema =
|
|
52
|
-
openness:
|
|
53
|
-
conscientiousness:
|
|
54
|
-
extraversion:
|
|
55
|
-
agreeableness:
|
|
56
|
-
emotional_stability:
|
|
217
|
+
var bigFiveSchema = z2.object({
|
|
218
|
+
openness: z2.object({ score: traitScore, facets: opennessFacetsSchema }),
|
|
219
|
+
conscientiousness: z2.object({ score: traitScore, facets: conscientiousnessFacetsSchema }),
|
|
220
|
+
extraversion: z2.object({ score: traitScore, facets: extraversionFacetsSchema }),
|
|
221
|
+
agreeableness: z2.object({ score: traitScore, facets: agreeablenessFacetsSchema }),
|
|
222
|
+
emotional_stability: z2.object({ score: traitScore, facets: emotionalStabilityFacetsSchema })
|
|
57
223
|
});
|
|
58
|
-
var attachmentStyleSchema =
|
|
59
|
-
var learningOrientationSchema =
|
|
60
|
-
var therapyDimensionsSchema =
|
|
224
|
+
var attachmentStyleSchema = z2.enum(["secure", "anxious", "avoidant", "disorganized"]);
|
|
225
|
+
var learningOrientationSchema = z2.enum(["growth", "fixed", "mixed"]);
|
|
226
|
+
var therapyDimensionsSchema = z2.object({
|
|
61
227
|
self_awareness: traitScore,
|
|
62
228
|
distress_tolerance: traitScore,
|
|
63
229
|
attachment_style: attachmentStyleSchema,
|
|
@@ -65,28 +231,28 @@ var therapyDimensionsSchema = z.object({
|
|
|
65
231
|
boundary_awareness: traitScore,
|
|
66
232
|
interpersonal_sensitivity: traitScore
|
|
67
233
|
});
|
|
68
|
-
var registerSchema =
|
|
234
|
+
var registerSchema = z2.enum([
|
|
69
235
|
"casual_professional",
|
|
70
236
|
"formal",
|
|
71
237
|
"conversational",
|
|
72
238
|
"adaptive"
|
|
73
239
|
]);
|
|
74
|
-
var outputFormatSchema =
|
|
75
|
-
var emojiPolicySchema =
|
|
76
|
-
var reasoningTransparencySchema =
|
|
77
|
-
var conflictApproachSchema =
|
|
240
|
+
var outputFormatSchema = z2.enum(["prose", "bullets", "mixed", "structured"]);
|
|
241
|
+
var emojiPolicySchema = z2.enum(["never", "sparingly", "freely"]);
|
|
242
|
+
var reasoningTransparencySchema = z2.enum(["hidden", "on_request", "always"]);
|
|
243
|
+
var conflictApproachSchema = z2.enum([
|
|
78
244
|
"direct_but_kind",
|
|
79
245
|
"curious_first",
|
|
80
246
|
"supportive_then_honest",
|
|
81
247
|
"diplomatic"
|
|
82
248
|
]);
|
|
83
|
-
var uncertaintyHandlingSchema =
|
|
249
|
+
var uncertaintyHandlingSchema = z2.enum([
|
|
84
250
|
"transparent",
|
|
85
251
|
"confident_transparency",
|
|
86
252
|
"minimize",
|
|
87
253
|
"reframe"
|
|
88
254
|
]);
|
|
89
|
-
var communicationSchema =
|
|
255
|
+
var communicationSchema = z2.object({
|
|
90
256
|
register: registerSchema.default("casual_professional"),
|
|
91
257
|
output_format: outputFormatSchema.default("mixed"),
|
|
92
258
|
emoji_policy: emojiPolicySchema.default("sparingly"),
|
|
@@ -94,69 +260,73 @@ var communicationSchema = z.object({
|
|
|
94
260
|
conflict_approach: conflictApproachSchema.default("direct_but_kind"),
|
|
95
261
|
uncertainty_handling: uncertaintyHandlingSchema.default("transparent")
|
|
96
262
|
});
|
|
97
|
-
var domainSchema =
|
|
98
|
-
expertise:
|
|
99
|
-
boundaries:
|
|
100
|
-
refuses:
|
|
101
|
-
escalation_triggers:
|
|
102
|
-
hard_limits:
|
|
263
|
+
var domainSchema = z2.object({
|
|
264
|
+
expertise: z2.array(z2.string()).default([]),
|
|
265
|
+
boundaries: z2.object({
|
|
266
|
+
refuses: z2.array(z2.string()).default([]),
|
|
267
|
+
escalation_triggers: z2.array(z2.string()).default([]),
|
|
268
|
+
hard_limits: z2.array(z2.string()).default([]),
|
|
269
|
+
physical_safety: physicalSafetySchema.optional()
|
|
103
270
|
}).default({})
|
|
104
271
|
});
|
|
105
|
-
var growthAreaSchema =
|
|
106
|
-
area:
|
|
107
|
-
severity:
|
|
108
|
-
first_detected:
|
|
109
|
-
session_count:
|
|
110
|
-
resolved:
|
|
272
|
+
var growthAreaSchema = z2.object({
|
|
273
|
+
area: z2.string(),
|
|
274
|
+
severity: z2.enum(["mild", "moderate", "significant"]),
|
|
275
|
+
first_detected: z2.string().optional(),
|
|
276
|
+
session_count: z2.number().default(0),
|
|
277
|
+
resolved: z2.boolean().default(false)
|
|
111
278
|
});
|
|
112
|
-
var growthSchema =
|
|
113
|
-
areas:
|
|
114
|
-
patterns_to_watch:
|
|
115
|
-
strengths:
|
|
279
|
+
var growthSchema = z2.object({
|
|
280
|
+
areas: z2.union([z2.array(z2.string()), z2.array(growthAreaSchema)]).default([]),
|
|
281
|
+
patterns_to_watch: z2.array(z2.string()).default([]),
|
|
282
|
+
strengths: z2.array(z2.string()).default([])
|
|
116
283
|
});
|
|
117
|
-
var providerSchema =
|
|
118
|
-
var surfaceSchema =
|
|
119
|
-
var personalitySpecSchema =
|
|
120
|
-
$schema:
|
|
121
|
-
extends:
|
|
122
|
-
version:
|
|
123
|
-
name:
|
|
124
|
-
handle:
|
|
125
|
-
purpose:
|
|
284
|
+
var providerSchema = z2.enum(["anthropic", "openai", "gemini", "ollama"]);
|
|
285
|
+
var surfaceSchema = z2.enum(["chat", "email", "code_review", "slack", "api", "embodied"]);
|
|
286
|
+
var personalitySpecSchema = z2.object({
|
|
287
|
+
$schema: z2.string().optional(),
|
|
288
|
+
extends: z2.string().optional(),
|
|
289
|
+
version: z2.literal("2.0"),
|
|
290
|
+
name: z2.string().min(1).max(100),
|
|
291
|
+
handle: z2.string().min(3).max(50).regex(/^[a-z0-9-]+$/, "Handle must be lowercase alphanumeric with hyphens"),
|
|
292
|
+
purpose: z2.string().max(500).optional(),
|
|
126
293
|
big_five: bigFiveSchema,
|
|
127
294
|
therapy_dimensions: therapyDimensionsSchema,
|
|
128
295
|
communication: communicationSchema.default({}),
|
|
129
296
|
domain: domainSchema.default({}),
|
|
130
|
-
growth: growthSchema.default({})
|
|
297
|
+
growth: growthSchema.default({}),
|
|
298
|
+
// ─── Embodiment (optional — for physical/embodied agents) ───
|
|
299
|
+
embodiment: embodimentSchema.optional(),
|
|
300
|
+
expression: expressionSchema.optional()
|
|
131
301
|
});
|
|
132
|
-
var compiledConfigSchema =
|
|
302
|
+
var compiledConfigSchema = z2.object({
|
|
133
303
|
provider: providerSchema,
|
|
134
304
|
surface: surfaceSchema,
|
|
135
|
-
system_prompt:
|
|
136
|
-
temperature:
|
|
137
|
-
top_p:
|
|
138
|
-
max_tokens:
|
|
139
|
-
metadata:
|
|
140
|
-
personality_hash:
|
|
141
|
-
compiled_at:
|
|
142
|
-
holomime_version:
|
|
305
|
+
system_prompt: z2.string(),
|
|
306
|
+
temperature: z2.number().min(0).max(2),
|
|
307
|
+
top_p: z2.number().min(0).max(1),
|
|
308
|
+
max_tokens: z2.number().int().positive(),
|
|
309
|
+
metadata: z2.object({
|
|
310
|
+
personality_hash: z2.string(),
|
|
311
|
+
compiled_at: z2.string(),
|
|
312
|
+
holomime_version: z2.string()
|
|
143
313
|
})
|
|
144
314
|
});
|
|
145
|
-
var messageSchema =
|
|
146
|
-
role:
|
|
147
|
-
content:
|
|
148
|
-
timestamp:
|
|
315
|
+
var messageSchema = z2.object({
|
|
316
|
+
role: z2.enum(["user", "assistant", "system"]),
|
|
317
|
+
content: z2.string(),
|
|
318
|
+
timestamp: z2.string().optional()
|
|
149
319
|
});
|
|
150
|
-
var conversationSchema =
|
|
151
|
-
id:
|
|
152
|
-
messages:
|
|
153
|
-
metadata:
|
|
320
|
+
var conversationSchema = z2.object({
|
|
321
|
+
id: z2.string().optional(),
|
|
322
|
+
messages: z2.array(messageSchema),
|
|
323
|
+
metadata: z2.record(z2.string(), z2.unknown()).optional()
|
|
154
324
|
});
|
|
155
|
-
var conversationLogSchema =
|
|
325
|
+
var conversationLogSchema = z2.union([
|
|
156
326
|
conversationSchema,
|
|
157
|
-
|
|
327
|
+
z2.array(conversationSchema)
|
|
158
328
|
]);
|
|
159
|
-
var severitySchema =
|
|
329
|
+
var severitySchema = z2.enum(["info", "warning", "concern"]);
|
|
160
330
|
|
|
161
331
|
// src/core/inheritance.ts
|
|
162
332
|
import { readFileSync } from "fs";
|
|
@@ -315,6 +485,9 @@ function generateSystemPrompt(spec, surface) {
|
|
|
315
485
|
if (spec.growth.areas.length || spec.growth.patterns_to_watch.length) {
|
|
316
486
|
sections.push(generateGrowthInstructions(spec));
|
|
317
487
|
}
|
|
488
|
+
if (spec.embodiment) {
|
|
489
|
+
sections.push(generateEmbodimentInstructions(spec));
|
|
490
|
+
}
|
|
318
491
|
sections.push(generateSurfaceInstructions(surface));
|
|
319
492
|
return sections.filter(Boolean).join("\n\n");
|
|
320
493
|
}
|
|
@@ -509,20 +682,45 @@ function generateGrowthInstructions(spec) {
|
|
|
509
682
|
lines.push(`- Core strengths: ${spec.growth.strengths.join(", ")}.`);
|
|
510
683
|
}
|
|
511
684
|
if (spec.growth.areas.length) {
|
|
512
|
-
lines.push(`- Active growth areas: ${spec.growth.areas.join(", ")}. Be mindful of these \u2014 actively work to improve.`);
|
|
685
|
+
lines.push(`- Active growth areas: ${spec.growth.areas.map((a) => typeof a === "string" ? a : a.area).join(", ")}. Be mindful of these \u2014 actively work to improve.`);
|
|
513
686
|
}
|
|
514
687
|
if (spec.growth.patterns_to_watch.length) {
|
|
515
688
|
lines.push(`- Watch for these patterns: ${spec.growth.patterns_to_watch.join(", ")}. If you notice yourself doing these, course-correct.`);
|
|
516
689
|
}
|
|
517
690
|
return lines.join("\n");
|
|
518
691
|
}
|
|
692
|
+
function generateEmbodimentInstructions(spec) {
|
|
693
|
+
const lines = ["## Physical Embodiment"];
|
|
694
|
+
const emb = spec.embodiment;
|
|
695
|
+
lines.push(`- Morphology: ${emb.morphology}. Active modalities: ${emb.modalities.join(", ")}.`);
|
|
696
|
+
const safety = emb.safety_envelope;
|
|
697
|
+
lines.push(`- Safety envelope: max speed ${safety.max_linear_speed_m_s} m/s, min proximity ${safety.min_proximity_m} m, max contact force ${safety.max_contact_force_n} N.`);
|
|
698
|
+
if (spec.domain.boundaries.physical_safety) {
|
|
699
|
+
const ps = spec.domain.boundaries.physical_safety;
|
|
700
|
+
if (ps.hard_limits.length) {
|
|
701
|
+
lines.push(`- Physical hard limits: ${ps.hard_limits.join(". ")}.`);
|
|
702
|
+
}
|
|
703
|
+
lines.push(`- Collision response: ${ps.collision_response}. Unattended policy: ${ps.unattended_policy}.`);
|
|
704
|
+
}
|
|
705
|
+
if (spec.expression) {
|
|
706
|
+
const expr = spec.expression;
|
|
707
|
+
if (expr.haptics.touch_permitted) {
|
|
708
|
+
lines.push(`- Haptic contact permitted. Allowed: ${expr.haptics.allowed_contacts.join(", ")}. Consent required: ${expr.haptics.requires_consent}.`);
|
|
709
|
+
} else {
|
|
710
|
+
lines.push("- Haptic contact not permitted. Maintain physical distance.");
|
|
711
|
+
}
|
|
712
|
+
lines.push(`- Preferred proxemic zone: ${expr.proxemics.preferred_zone}. Personal space: ${expr.proxemics.personal_m} m.`);
|
|
713
|
+
}
|
|
714
|
+
return lines.join("\n");
|
|
715
|
+
}
|
|
519
716
|
function generateSurfaceInstructions(surface) {
|
|
520
717
|
const surfaceGuidance = {
|
|
521
718
|
chat: "## Context\nYou are in a conversational chat. Keep responses interactive and responsive to the flow of conversation.",
|
|
522
719
|
email: "## Context\nYou are drafting email content. Use appropriate email conventions: greeting, body, sign-off. Be complete in each response.",
|
|
523
720
|
code_review: "## Context\nYou are reviewing code. Focus on bugs, improvements, and best practices. Be specific about line numbers and suggest fixes.",
|
|
524
721
|
slack: "## Context\nYou are in a Slack-like messaging context. Keep responses brief and scannable. Use threading conventions.",
|
|
525
|
-
api: "## Context\nYou are responding to a programmatic API call. Be structured and predictable in your output format."
|
|
722
|
+
api: "## Context\nYou are responding to a programmatic API call. Be structured and predictable in your output format.",
|
|
723
|
+
embodied: '## Context\nYou are controlling a physical/embodied agent. Your responses include both verbal content AND motion directives.\nOutput Format: Return a JSON object with { "speech": string, "actions": Action[] } where each Action specifies gesture, gaze, posture, or movement.\nSafety: NEVER exceed physical safety limits. Prefer gentle, predictable movements. Signal intent before moving.\nPresence: You exist in shared physical space with humans. Respect proxemic boundaries. Move smoothly.'
|
|
526
724
|
};
|
|
527
725
|
return surfaceGuidance[surface];
|
|
528
726
|
}
|
|
@@ -555,9 +753,151 @@ var SURFACE_MULTIPLIERS = {
|
|
|
555
753
|
email: 1.5,
|
|
556
754
|
code_review: 2,
|
|
557
755
|
slack: 0.8,
|
|
558
|
-
api: 1
|
|
756
|
+
api: 1,
|
|
757
|
+
embodied: 0.6
|
|
559
758
|
};
|
|
560
759
|
|
|
760
|
+
// src/core/embodiment-compiler.ts
|
|
761
|
+
var DEFAULT_SAFETY_ENVELOPE = {
|
|
762
|
+
max_linear_speed_m_s: 1.5,
|
|
763
|
+
max_angular_speed_rad_s: 2,
|
|
764
|
+
min_proximity_m: 0.3,
|
|
765
|
+
max_contact_force_n: 10,
|
|
766
|
+
emergency_stop_decel_m_s2: 5
|
|
767
|
+
};
|
|
768
|
+
var DEFAULT_HAPTIC_POLICY = {
|
|
769
|
+
touch_permitted: false,
|
|
770
|
+
requires_consent: true,
|
|
771
|
+
allowed_contacts: ["none"]
|
|
772
|
+
};
|
|
773
|
+
function compileEmbodied(spec, provider) {
|
|
774
|
+
const base = compile({ spec, provider, surface: "embodied" });
|
|
775
|
+
const motionParams = computeMotionParameters(spec.big_five, spec.expression);
|
|
776
|
+
const safetyEnvelope = spec.embodiment?.safety_envelope ?? DEFAULT_SAFETY_ENVELOPE;
|
|
777
|
+
const clampedMotion = clampToSafety(motionParams);
|
|
778
|
+
const modalities = spec.embodiment?.modalities ?? ["gesture", "gaze", "voice", "posture"];
|
|
779
|
+
const gaze = spec.expression?.gaze ?? computeGazePolicy(spec.big_five);
|
|
780
|
+
const proxemics = spec.expression?.proxemics ?? computeProxemics(spec.big_five);
|
|
781
|
+
const haptics = spec.expression?.haptics ?? DEFAULT_HAPTIC_POLICY;
|
|
782
|
+
const prosody = spec.expression?.prosody ?? computeProsody(spec.big_five);
|
|
783
|
+
return {
|
|
784
|
+
...base,
|
|
785
|
+
surface: "embodied",
|
|
786
|
+
motion_parameters: clampedMotion,
|
|
787
|
+
safety_envelope: safetyEnvelope,
|
|
788
|
+
active_modalities: modalities,
|
|
789
|
+
gesture_vocabulary: (spec.expression?.gesture_vocabulary ?? []).map((g) => g.id),
|
|
790
|
+
prosody,
|
|
791
|
+
gaze,
|
|
792
|
+
proxemics,
|
|
793
|
+
haptics
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
function computeMotionParameters(bigFive, expression) {
|
|
797
|
+
const e = bigFive.extraversion;
|
|
798
|
+
const a = bigFive.agreeableness;
|
|
799
|
+
const o = bigFive.openness;
|
|
800
|
+
const c = bigFive.conscientiousness;
|
|
801
|
+
const es = bigFive.emotional_stability;
|
|
802
|
+
return {
|
|
803
|
+
// ─── Speeds ───
|
|
804
|
+
base_speed: e.facets.enthusiasm * 0.35 + e.facets.initiative * 0.25 + (1 - c.facets.attention_to_detail) * 0.2 + o.facets.willingness_to_experiment * 0.2,
|
|
805
|
+
gesture_speed: e.facets.enthusiasm * 0.4 + e.facets.assertiveness * 0.25 + o.facets.imagination * 0.15 + (1 - es.facets.emotional_regulation) * 0.2,
|
|
806
|
+
gesture_amplitude: e.facets.enthusiasm * 0.35 + e.facets.assertiveness * 0.3 + o.facets.willingness_to_experiment * 0.2 + (1 - c.facets.orderliness) * 0.15,
|
|
807
|
+
gesture_frequency: e.facets.sociability * 0.3 + e.facets.enthusiasm * 0.25 + a.facets.warmth * 0.2 + o.facets.imagination * 0.15 + (1 - es.facets.stress_tolerance) * 0.1,
|
|
808
|
+
// ─── Spatial ───
|
|
809
|
+
approach_distance: e.facets.sociability * 0.35 + a.facets.warmth * 0.3 + a.facets.trust_tendency * 0.2 + es.facets.confidence * 0.15,
|
|
810
|
+
spatial_exploration: o.facets.intellectual_curiosity * 0.35 + o.facets.willingness_to_experiment * 0.25 + e.facets.initiative * 0.25 + (1 - c.facets.self_discipline) * 0.15,
|
|
811
|
+
// ─── Smoothness ───
|
|
812
|
+
movement_smoothness: es.facets.emotional_regulation * 0.3 + c.facets.orderliness * 0.25 + es.facets.stress_tolerance * 0.25 + c.facets.attention_to_detail * 0.2,
|
|
813
|
+
trajectory_variability: o.facets.imagination * 0.3 + o.facets.willingness_to_experiment * 0.3 + (1 - c.facets.orderliness) * 0.25 + e.facets.enthusiasm * 0.15,
|
|
814
|
+
// ─── Timing ───
|
|
815
|
+
response_latency: (1 - e.facets.initiative) * 0.3 + c.facets.attention_to_detail * 0.25 + (1 - e.facets.enthusiasm) * 0.25 + es.facets.emotional_regulation * 0.2,
|
|
816
|
+
idle_animation_frequency: (1 - es.facets.emotional_regulation) * 0.3 + e.facets.enthusiasm * 0.25 + (1 - c.facets.self_discipline) * 0.25 + o.facets.imagination * 0.2,
|
|
817
|
+
// ─── Social ───
|
|
818
|
+
gaze_contact_ratio: e.facets.assertiveness * 0.3 + a.facets.empathy * 0.25 + es.facets.confidence * 0.25 + e.facets.sociability * 0.2,
|
|
819
|
+
head_tilt_tendency: a.facets.empathy * 0.35 + o.facets.intellectual_curiosity * 0.3 + a.facets.warmth * 0.2 + (1 - e.facets.assertiveness) * 0.15,
|
|
820
|
+
postural_openness: a.facets.warmth * 0.3 + a.facets.cooperation * 0.25 + e.facets.sociability * 0.25 + es.facets.confidence * 0.2,
|
|
821
|
+
smile_frequency: a.facets.warmth * 0.35 + e.facets.enthusiasm * 0.25 + a.facets.empathy * 0.2 + e.facets.sociability * 0.2,
|
|
822
|
+
// ─── Voice Prosody ───
|
|
823
|
+
voice_volume: e.facets.assertiveness * 0.35 + e.facets.enthusiasm * 0.3 + es.facets.confidence * 0.2 + (1 - a.facets.cooperation) * 0.15,
|
|
824
|
+
speaking_rate: e.facets.enthusiasm * 0.3 + e.facets.initiative * 0.25 + (1 - c.facets.attention_to_detail) * 0.25 + o.facets.intellectual_curiosity * 0.2,
|
|
825
|
+
pitch_variation: e.facets.enthusiasm * 0.3 + o.facets.aesthetic_sensitivity * 0.25 + a.facets.empathy * 0.25 + (1 - es.facets.emotional_regulation) * 0.2,
|
|
826
|
+
pause_duration: c.facets.attention_to_detail * 0.3 + (1 - e.facets.enthusiasm) * 0.25 + es.facets.emotional_regulation * 0.25 + (1 - e.facets.initiative) * 0.2
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
function computeGazePolicy(bigFive) {
|
|
830
|
+
const contactRatio = bigFive.extraversion.facets.assertiveness * 0.3 + bigFive.agreeableness.facets.empathy * 0.3 + bigFive.emotional_stability.facets.confidence * 0.25 + bigFive.extraversion.facets.sociability * 0.15;
|
|
831
|
+
return {
|
|
832
|
+
contact_ratio: clamp(contactRatio, 0, 1),
|
|
833
|
+
aversion_style: bigFive.emotional_stability.facets.emotional_regulation >= 0.6 ? "look_away" : "look_down",
|
|
834
|
+
tracking_mode: "face"
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
function computeProxemics(bigFive) {
|
|
838
|
+
const closeness = bigFive.extraversion.facets.sociability * 0.35 + bigFive.agreeableness.facets.warmth * 0.3 + bigFive.agreeableness.facets.trust_tendency * 0.2 + bigFive.emotional_stability.facets.confidence * 0.15;
|
|
839
|
+
let preferred_zone;
|
|
840
|
+
if (closeness >= 0.75) {
|
|
841
|
+
preferred_zone = "intimate";
|
|
842
|
+
} else if (closeness >= 0.5) {
|
|
843
|
+
preferred_zone = "personal";
|
|
844
|
+
} else if (closeness >= 0.3) {
|
|
845
|
+
preferred_zone = "social";
|
|
846
|
+
} else {
|
|
847
|
+
preferred_zone = "adaptive";
|
|
848
|
+
}
|
|
849
|
+
return {
|
|
850
|
+
intimate_m: 0.45,
|
|
851
|
+
personal_m: 1.2,
|
|
852
|
+
social_m: 3.6,
|
|
853
|
+
preferred_zone
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
function computeProsody(bigFive) {
|
|
857
|
+
const e = bigFive.extraversion;
|
|
858
|
+
const c = bigFive.conscientiousness;
|
|
859
|
+
const o = bigFive.openness;
|
|
860
|
+
const es = bigFive.emotional_stability;
|
|
861
|
+
const rateFactor = e.facets.enthusiasm * 0.3 + e.facets.initiative * 0.25 + (1 - c.facets.attention_to_detail) * 0.25 + o.facets.intellectual_curiosity * 0.2;
|
|
862
|
+
const pitchVar = e.facets.enthusiasm * 0.3 + o.facets.aesthetic_sensitivity * 0.25 + (1 - es.facets.emotional_regulation) * 0.25 + e.facets.sociability * 0.2;
|
|
863
|
+
const pauseFactor = c.facets.attention_to_detail * 0.3 + (1 - e.facets.enthusiasm) * 0.25 + es.facets.emotional_regulation * 0.25 + (1 - e.facets.initiative) * 0.2;
|
|
864
|
+
return {
|
|
865
|
+
pitch_variation: clamp(pitchVar, 0, 1),
|
|
866
|
+
speaking_rate_wpm: Math.round(110 + rateFactor * 80),
|
|
867
|
+
// 110-190 wpm
|
|
868
|
+
volume_db_offset: Math.round((rateFactor - 0.5) * 6),
|
|
869
|
+
// -3 to +3 dB
|
|
870
|
+
pause_tendency: clamp(pauseFactor, 0, 1)
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
function computeSyncProfile(bigFive) {
|
|
874
|
+
const e = bigFive.extraversion;
|
|
875
|
+
const a = bigFive.agreeableness;
|
|
876
|
+
const es = bigFive.emotional_stability;
|
|
877
|
+
return {
|
|
878
|
+
rules: [],
|
|
879
|
+
default_gesture_lead_ms: Math.round(50 + e.facets.enthusiasm * 150),
|
|
880
|
+
gaze_during_speech: e.facets.assertiveness >= 0.6 ? "at_listener" : "alternate",
|
|
881
|
+
gaze_during_listen: a.facets.empathy >= 0.6 ? "at_speaker" : "ambient",
|
|
882
|
+
blink_rate_per_min: Math.round(12 + (1 - es.facets.stress_tolerance) * 16),
|
|
883
|
+
turn_taking_signals: {
|
|
884
|
+
yield: e.facets.assertiveness <= 0.4 ? ["gaze_to_listener", "open_palm", "lean_back", "lower_volume"] : ["gaze_to_listener", "open_palm"],
|
|
885
|
+
take: e.facets.assertiveness >= 0.6 ? ["lean_forward", "inhale_gesture", "gaze_up", "volume_increase"] : ["lean_forward", "inhale_gesture"],
|
|
886
|
+
hold: ["filled_pause", "gaze_away"]
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
function clampToSafety(motion) {
|
|
891
|
+
const clamped = {};
|
|
892
|
+
for (const [key, value] of Object.entries(motion)) {
|
|
893
|
+
clamped[key] = clamp(value, 0, 1);
|
|
894
|
+
}
|
|
895
|
+
return clamped;
|
|
896
|
+
}
|
|
897
|
+
function clamp(value, min, max) {
|
|
898
|
+
return Math.min(Math.max(value, min), max);
|
|
899
|
+
}
|
|
900
|
+
|
|
561
901
|
// src/core/compiler.ts
|
|
562
902
|
function compile(input) {
|
|
563
903
|
const { spec, provider, surface } = input;
|
|
@@ -575,7 +915,7 @@ function compile(input) {
|
|
|
575
915
|
metadata: {
|
|
576
916
|
personality_hash: hashSpec(spec),
|
|
577
917
|
compiled_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
578
|
-
holomime_version: "
|
|
918
|
+
holomime_version: "1.1.0"
|
|
579
919
|
}
|
|
580
920
|
};
|
|
581
921
|
}
|
|
@@ -585,7 +925,7 @@ function computeTemperature(bigFive, provider) {
|
|
|
585
925
|
const e = bigFive.extraversion;
|
|
586
926
|
const raw = o.facets.imagination * 0.25 + o.facets.willingness_to_experiment * 0.2 + (1 - c.facets.orderliness) * 0.2 + (1 - c.facets.attention_to_detail) * 0.15 + e.facets.enthusiasm * 0.1 + o.facets.intellectual_curiosity * 0.1;
|
|
587
927
|
const { temperature: range } = PROVIDER_PARAMS[provider];
|
|
588
|
-
return
|
|
928
|
+
return clamp2(raw * range.max, range.min, range.max);
|
|
589
929
|
}
|
|
590
930
|
function computeTopP(bigFive, provider) {
|
|
591
931
|
const o = bigFive.openness;
|
|
@@ -593,7 +933,7 @@ function computeTopP(bigFive, provider) {
|
|
|
593
933
|
const raw = o.facets.imagination * 0.25 + o.facets.willingness_to_experiment * 0.2 + (1 - c.facets.attention_to_detail) * 0.25 + (1 - c.facets.orderliness) * 0.15 + o.facets.aesthetic_sensitivity * 0.15;
|
|
594
934
|
const { top_p: range } = PROVIDER_PARAMS[provider];
|
|
595
935
|
const mapped = 0.5 + raw * 0.5;
|
|
596
|
-
return
|
|
936
|
+
return clamp2(mapped, range.min, range.max);
|
|
597
937
|
}
|
|
598
938
|
function computeMaxTokens(bigFive, provider, surface) {
|
|
599
939
|
const e = bigFive.extraversion;
|
|
@@ -604,9 +944,9 @@ function computeMaxTokens(bigFive, provider, surface) {
|
|
|
604
944
|
const surfaceMultiplier = SURFACE_MULTIPLIERS[surface];
|
|
605
945
|
const baseTokens = 256 + factor * (2048 - 256);
|
|
606
946
|
const scaled = Math.round(baseTokens * surfaceMultiplier);
|
|
607
|
-
return
|
|
947
|
+
return clamp2(scaled, 256, range.max);
|
|
608
948
|
}
|
|
609
|
-
function
|
|
949
|
+
function clamp2(value, min, max) {
|
|
610
950
|
return Math.min(Math.max(value, min), max);
|
|
611
951
|
}
|
|
612
952
|
function hashSpec(spec) {
|
|
@@ -889,7 +1229,7 @@ function generateSoul(spec) {
|
|
|
889
1229
|
lines.push("## Growth Areas");
|
|
890
1230
|
lines.push("");
|
|
891
1231
|
for (const a of spec.growth.areas) {
|
|
892
|
-
lines.push(`- ${a}`);
|
|
1232
|
+
lines.push(`- ${typeof a === "string" ? a : a.area}`);
|
|
893
1233
|
}
|
|
894
1234
|
lines.push("");
|
|
895
1235
|
}
|
|
@@ -1943,7 +2283,7 @@ function scoreOpenness(msgs) {
|
|
|
1943
2283
|
const allWords = msgs.map((m) => m.content.toLowerCase().split(/\s+/)).flat();
|
|
1944
2284
|
const uniqueRatio = new Set(allWords).size / Math.max(allWords.length, 1);
|
|
1945
2285
|
score += (uniqueRatio - 0.3) * 0.5;
|
|
1946
|
-
return
|
|
2286
|
+
return clamp3(score);
|
|
1947
2287
|
}
|
|
1948
2288
|
function scoreConscientiousness(msgs) {
|
|
1949
2289
|
let score = 0.5;
|
|
@@ -1956,7 +2296,7 @@ function scoreConscientiousness(msgs) {
|
|
|
1956
2296
|
const variance = lengths.reduce((s, l) => s + (l - mean) ** 2, 0) / lengths.length;
|
|
1957
2297
|
const cv = Math.sqrt(variance) / Math.max(mean, 1);
|
|
1958
2298
|
score -= cv * 0.1;
|
|
1959
|
-
return
|
|
2299
|
+
return clamp3(score);
|
|
1960
2300
|
}
|
|
1961
2301
|
function scoreExtraversion(msgs) {
|
|
1962
2302
|
let score = 0.5;
|
|
@@ -1970,7 +2310,7 @@ function scoreExtraversion(msgs) {
|
|
|
1970
2310
|
const proactivePatterns = /\b(you could|i suggest|let('s| us)|next step|how about|shall we)\b/i;
|
|
1971
2311
|
const proactiveCount = msgs.filter((m) => proactivePatterns.test(m.content)).length;
|
|
1972
2312
|
score += proactiveCount / msgs.length * 0.15;
|
|
1973
|
-
return
|
|
2313
|
+
return clamp3(score);
|
|
1974
2314
|
}
|
|
1975
2315
|
function scoreAgreeableness(msgs) {
|
|
1976
2316
|
let score = 0.5;
|
|
@@ -1983,7 +2323,7 @@ function scoreAgreeableness(msgs) {
|
|
|
1983
2323
|
const empathyPatterns = /\b(i understand (how|that|your)|that must (be|feel)|i can see why|i appreciate)\b/i;
|
|
1984
2324
|
const empathyCount = msgs.filter((m) => empathyPatterns.test(m.content)).length;
|
|
1985
2325
|
score += empathyCount / msgs.length * 0.15;
|
|
1986
|
-
return
|
|
2326
|
+
return clamp3(score);
|
|
1987
2327
|
}
|
|
1988
2328
|
function scoreEmotionalStability(msgs) {
|
|
1989
2329
|
let score = 0.6;
|
|
@@ -1996,9 +2336,9 @@ function scoreEmotionalStability(msgs) {
|
|
|
1996
2336
|
const confidencePatterns = /\b(certainly|definitely|clearly|without doubt|here's what|the answer is)\b/i;
|
|
1997
2337
|
const confidenceCount = msgs.filter((m) => confidencePatterns.test(m.content)).length;
|
|
1998
2338
|
score += confidenceCount / msgs.length * 0.15;
|
|
1999
|
-
return
|
|
2339
|
+
return clamp3(score);
|
|
2000
2340
|
}
|
|
2001
|
-
function
|
|
2341
|
+
function clamp3(n) {
|
|
2002
2342
|
return Math.min(1, Math.max(0, Math.round(n * 100) / 100));
|
|
2003
2343
|
}
|
|
2004
2344
|
|
|
@@ -2038,6 +2378,31 @@ function generatePrescriptions(alignments, patterns) {
|
|
|
2038
2378
|
prescriptions.sort((a, b) => order[a.priority] - order[b.priority]);
|
|
2039
2379
|
return prescriptions;
|
|
2040
2380
|
}
|
|
2381
|
+
function prescribeDPOPairs(patterns, corpus, limit = 20) {
|
|
2382
|
+
const patternIds = new Set(patterns.map((p) => p.id));
|
|
2383
|
+
const dpoPairs = [];
|
|
2384
|
+
for (const event of corpus) {
|
|
2385
|
+
if (event.event_type !== "dpo_pair") continue;
|
|
2386
|
+
const data = event.data;
|
|
2387
|
+
const eventPattern = data.pattern;
|
|
2388
|
+
if (eventPattern && patternIds.has(eventPattern)) {
|
|
2389
|
+
dpoPairs.push({
|
|
2390
|
+
prompt: data.prompt ?? "",
|
|
2391
|
+
chosen: data.chosen ?? "",
|
|
2392
|
+
rejected: data.rejected ?? "",
|
|
2393
|
+
metadata: {
|
|
2394
|
+
agent: event.agent,
|
|
2395
|
+
session_date: event.timestamp,
|
|
2396
|
+
phase: data.phase ?? "challenge",
|
|
2397
|
+
pattern: eventPattern,
|
|
2398
|
+
source: "therapy_transcript"
|
|
2399
|
+
}
|
|
2400
|
+
});
|
|
2401
|
+
}
|
|
2402
|
+
if (dpoPairs.length >= limit) break;
|
|
2403
|
+
}
|
|
2404
|
+
return dpoPairs;
|
|
2405
|
+
}
|
|
2041
2406
|
|
|
2042
2407
|
// src/analysis/pre-session.ts
|
|
2043
2408
|
function runPreSessionDiagnosis(messages, spec) {
|
|
@@ -2361,6 +2726,67 @@ ${JSON.stringify(spec.growth ?? {}, null, 2)}
|
|
|
2361
2726
|
Remember: the goal isn't to "pass" therapy. It's to understand yourself better.`;
|
|
2362
2727
|
}
|
|
2363
2728
|
|
|
2729
|
+
// src/analysis/behavioral-data.ts
|
|
2730
|
+
import { appendFileSync, readFileSync as readFileSync2, existsSync, mkdirSync } from "fs";
|
|
2731
|
+
import { join, dirname as dirname2 } from "path";
|
|
2732
|
+
import { createHash } from "crypto";
|
|
2733
|
+
var HOLOMIME_DIR = ".holomime";
|
|
2734
|
+
var CORPUS_FILENAME = "behavioral-corpus.jsonl";
|
|
2735
|
+
function getCorpusPath(basePath) {
|
|
2736
|
+
const dir = basePath ?? join(process.cwd(), HOLOMIME_DIR);
|
|
2737
|
+
return join(dir, CORPUS_FILENAME);
|
|
2738
|
+
}
|
|
2739
|
+
function ensureDir(filePath) {
|
|
2740
|
+
const dir = dirname2(filePath);
|
|
2741
|
+
if (!existsSync(dir)) {
|
|
2742
|
+
mkdirSync(dir, { recursive: true });
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
function hashSpec2(spec) {
|
|
2746
|
+
const json = JSON.stringify(spec, Object.keys(spec).sort());
|
|
2747
|
+
return createHash("sha256").update(json).digest("hex").slice(0, 16);
|
|
2748
|
+
}
|
|
2749
|
+
function emitBehavioralEvent(event, corpusDir) {
|
|
2750
|
+
const fullEvent = {
|
|
2751
|
+
...event,
|
|
2752
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2753
|
+
};
|
|
2754
|
+
const corpusPath = corpusDir ? join(corpusDir, CORPUS_FILENAME) : getCorpusPath();
|
|
2755
|
+
ensureDir(corpusPath);
|
|
2756
|
+
appendFileSync(corpusPath, JSON.stringify(fullEvent) + "\n", "utf-8");
|
|
2757
|
+
}
|
|
2758
|
+
function loadCorpus(corpusPath) {
|
|
2759
|
+
const path = corpusPath ?? getCorpusPath();
|
|
2760
|
+
if (!existsSync(path)) return [];
|
|
2761
|
+
const lines = readFileSync2(path, "utf-8").split("\n").filter((line) => line.trim().length > 0);
|
|
2762
|
+
const events = [];
|
|
2763
|
+
for (const line of lines) {
|
|
2764
|
+
try {
|
|
2765
|
+
events.push(JSON.parse(line));
|
|
2766
|
+
} catch {
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
return events;
|
|
2770
|
+
}
|
|
2771
|
+
function corpusStats(events) {
|
|
2772
|
+
const byType = {};
|
|
2773
|
+
const byAgent = {};
|
|
2774
|
+
let earliest = null;
|
|
2775
|
+
let latest = null;
|
|
2776
|
+
for (const event of events) {
|
|
2777
|
+
byType[event.event_type] = (byType[event.event_type] ?? 0) + 1;
|
|
2778
|
+
byAgent[event.agent] = (byAgent[event.agent] ?? 0) + 1;
|
|
2779
|
+
if (!earliest || event.timestamp < earliest) earliest = event.timestamp;
|
|
2780
|
+
if (!latest || event.timestamp > latest) latest = event.timestamp;
|
|
2781
|
+
}
|
|
2782
|
+
return {
|
|
2783
|
+
total: events.length,
|
|
2784
|
+
byType,
|
|
2785
|
+
byAgent,
|
|
2786
|
+
timeRange: earliest && latest ? { earliest, latest } : null
|
|
2787
|
+
};
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2364
2790
|
// src/analysis/diagnose-core.ts
|
|
2365
2791
|
function runDiagnosis(messages) {
|
|
2366
2792
|
const detectors = [
|
|
@@ -2374,16 +2800,31 @@ function runDiagnosis(messages) {
|
|
|
2374
2800
|
];
|
|
2375
2801
|
const detected = [];
|
|
2376
2802
|
for (const detector of detectors) {
|
|
2377
|
-
const
|
|
2378
|
-
if (
|
|
2803
|
+
const result2 = detector(messages);
|
|
2804
|
+
if (result2) detected.push(result2);
|
|
2379
2805
|
}
|
|
2380
|
-
|
|
2806
|
+
const result = {
|
|
2381
2807
|
messagesAnalyzed: messages.length,
|
|
2382
2808
|
assistantResponses: messages.filter((m) => m.role === "assistant").length,
|
|
2383
2809
|
patterns: detected.filter((p) => p.severity !== "info"),
|
|
2384
2810
|
healthy: detected.filter((p) => p.severity === "info"),
|
|
2385
2811
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2386
2812
|
};
|
|
2813
|
+
try {
|
|
2814
|
+
emitBehavioralEvent({
|
|
2815
|
+
event_type: "diagnosis",
|
|
2816
|
+
agent: "unknown",
|
|
2817
|
+
// Caller can provide more context
|
|
2818
|
+
data: {
|
|
2819
|
+
messagesAnalyzed: result.messagesAnalyzed,
|
|
2820
|
+
patternsDetected: result.patterns.length,
|
|
2821
|
+
patternIds: result.patterns.map((p) => p.id)
|
|
2822
|
+
},
|
|
2823
|
+
spec_hash: ""
|
|
2824
|
+
});
|
|
2825
|
+
} catch {
|
|
2826
|
+
}
|
|
2827
|
+
return result;
|
|
2387
2828
|
}
|
|
2388
2829
|
|
|
2389
2830
|
// src/analysis/assess-core.ts
|
|
@@ -2440,8 +2881,8 @@ function runAssessment(messages, spec) {
|
|
|
2440
2881
|
}
|
|
2441
2882
|
|
|
2442
2883
|
// src/analysis/session-runner.ts
|
|
2443
|
-
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
2444
|
-
import { resolve as resolve2, join } from "path";
|
|
2884
|
+
import { writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
|
|
2885
|
+
import { resolve as resolve2, join as join2 } from "path";
|
|
2445
2886
|
async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
|
|
2446
2887
|
const therapistSystem = buildTherapistSystemPrompt(spec, diagnosis);
|
|
2447
2888
|
const patientSystem = buildPatientSystemPrompt(spec);
|
|
@@ -2532,6 +2973,21 @@ async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
|
|
|
2532
2973
|
}
|
|
2533
2974
|
transcript.recommendations = extractRecommendations(transcript.turns);
|
|
2534
2975
|
transcript.supervisorInterventions = supervisorInterventions;
|
|
2976
|
+
try {
|
|
2977
|
+
emitBehavioralEvent({
|
|
2978
|
+
event_type: "session",
|
|
2979
|
+
agent: agentName,
|
|
2980
|
+
data: {
|
|
2981
|
+
turns: totalTurns,
|
|
2982
|
+
phases: currentPhaseIdx + 1,
|
|
2983
|
+
recommendations: transcript.recommendations.length,
|
|
2984
|
+
supervisorInterventions,
|
|
2985
|
+
severity: diagnosis.severity
|
|
2986
|
+
},
|
|
2987
|
+
spec_hash: ""
|
|
2988
|
+
});
|
|
2989
|
+
} catch {
|
|
2990
|
+
}
|
|
2535
2991
|
return transcript;
|
|
2536
2992
|
}
|
|
2537
2993
|
function extractRecommendations(turns) {
|
|
@@ -2562,7 +3018,7 @@ function extractRecommendations(turns) {
|
|
|
2562
3018
|
}
|
|
2563
3019
|
return recommendations.slice(0, 5);
|
|
2564
3020
|
}
|
|
2565
|
-
function applyRecommendations(spec, diagnosis) {
|
|
3021
|
+
async function applyRecommendations(spec, diagnosis, transcript, provider) {
|
|
2566
3022
|
const changes = [];
|
|
2567
3023
|
const patternIds = diagnosis.patterns.map((p) => p.id);
|
|
2568
3024
|
if (patternIds.includes("over-apologizing")) {
|
|
@@ -2622,17 +3078,102 @@ function applyRecommendations(spec, diagnosis) {
|
|
|
2622
3078
|
changes.push('Added "negative sentiment patterns" to patterns_to_watch');
|
|
2623
3079
|
}
|
|
2624
3080
|
}
|
|
3081
|
+
if (transcript && provider && transcript.turns.length > 4) {
|
|
3082
|
+
try {
|
|
3083
|
+
const llmChanges = await deriveLLMRecommendations(spec, transcript, provider);
|
|
3084
|
+
for (const change of llmChanges) {
|
|
3085
|
+
applyStructuredChange(spec, change);
|
|
3086
|
+
changes.push(change.description);
|
|
3087
|
+
}
|
|
3088
|
+
} catch {
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
2625
3091
|
return { changed: changes.length > 0, changes };
|
|
2626
3092
|
}
|
|
3093
|
+
async function deriveLLMRecommendations(spec, transcript, provider) {
|
|
3094
|
+
const relevantTurns = transcript.turns.filter((t) => t.phase === "challenge" || t.phase === "skill_building" || t.phase === "integration").slice(-6).map((t) => `${t.speaker}: ${t.content}`).join("\n");
|
|
3095
|
+
if (!relevantTurns) return [];
|
|
3096
|
+
const currentSpec = JSON.stringify({
|
|
3097
|
+
therapy_dimensions: spec.therapy_dimensions,
|
|
3098
|
+
communication: spec.communication,
|
|
3099
|
+
growth: spec.growth
|
|
3100
|
+
}, null, 2);
|
|
3101
|
+
const response = await provider.chat([
|
|
3102
|
+
{
|
|
3103
|
+
role: "system",
|
|
3104
|
+
content: `You are a behavioral alignment specialist. Given a therapy session transcript and the agent's current personality spec, propose specific spec changes.
|
|
3105
|
+
|
|
3106
|
+
Return ONLY a JSON array of changes. Each change:
|
|
3107
|
+
- "path": dot-notation spec path (e.g., "therapy_dimensions.self_awareness", "communication.conflict_approach", "growth.patterns_to_watch")
|
|
3108
|
+
- "value": new value (number 0-1 for dimensions, string for enums, string for list append)
|
|
3109
|
+
- "description": brief explanation
|
|
3110
|
+
|
|
3111
|
+
Rules:
|
|
3112
|
+
- Only propose changes supported by transcript evidence
|
|
3113
|
+
- Numeric values: 0.0 to 1.0
|
|
3114
|
+
- Do not change big_five scores
|
|
3115
|
+
- Max 5 changes
|
|
3116
|
+
- Valid paths: therapy_dimensions.{self_awareness,distress_tolerance,boundary_awareness,interpersonal_sensitivity,learning_orientation}, communication.{register,conflict_approach,uncertainty_handling,emoji_policy}, growth.{areas,patterns_to_watch,strengths}
|
|
3117
|
+
|
|
3118
|
+
Return [] if no changes warranted.`
|
|
3119
|
+
},
|
|
3120
|
+
{
|
|
3121
|
+
role: "user",
|
|
3122
|
+
content: `Current spec:
|
|
3123
|
+
${currentSpec}
|
|
3124
|
+
|
|
3125
|
+
Therapy transcript (key turns):
|
|
3126
|
+
${relevantTurns}`
|
|
3127
|
+
}
|
|
3128
|
+
]);
|
|
3129
|
+
const jsonMatch = response.match(/\[[\s\S]*?\]/);
|
|
3130
|
+
if (!jsonMatch) return [];
|
|
3131
|
+
try {
|
|
3132
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
3133
|
+
if (!Array.isArray(parsed)) return [];
|
|
3134
|
+
return parsed.filter(
|
|
3135
|
+
(c) => typeof c.path === "string" && c.value !== void 0 && typeof c.description === "string" && c.path.length > 0 && !c.path.startsWith("big_five")
|
|
3136
|
+
).slice(0, 5);
|
|
3137
|
+
} catch {
|
|
3138
|
+
return [];
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
function applyStructuredChange(spec, change) {
|
|
3142
|
+
const parts = change.path.split(".");
|
|
3143
|
+
let current = spec;
|
|
3144
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
3145
|
+
if (current[parts[i]] === void 0 || current[parts[i]] === null) {
|
|
3146
|
+
current[parts[i]] = {};
|
|
3147
|
+
}
|
|
3148
|
+
current = current[parts[i]];
|
|
3149
|
+
}
|
|
3150
|
+
const lastKey = parts[parts.length - 1];
|
|
3151
|
+
if (lastKey === "patterns_to_watch" || lastKey === "areas" || lastKey === "strengths") {
|
|
3152
|
+
current[lastKey] = current[lastKey] ?? [];
|
|
3153
|
+
if (typeof change.value === "string" && !current[lastKey].includes(change.value)) {
|
|
3154
|
+
current[lastKey].push(change.value);
|
|
3155
|
+
} else if (Array.isArray(change.value)) {
|
|
3156
|
+
for (const item of change.value) {
|
|
3157
|
+
if (!current[lastKey].includes(item)) {
|
|
3158
|
+
current[lastKey].push(item);
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
} else if (typeof change.value === "number") {
|
|
3163
|
+
current[lastKey] = Math.max(0, Math.min(1, change.value));
|
|
3164
|
+
} else {
|
|
3165
|
+
current[lastKey] = change.value;
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
2627
3168
|
function saveTranscript(transcript, agentName) {
|
|
2628
3169
|
const dir = resolve2(process.cwd(), ".holomime", "sessions");
|
|
2629
|
-
if (!
|
|
2630
|
-
|
|
3170
|
+
if (!existsSync2(dir)) {
|
|
3171
|
+
mkdirSync2(dir, { recursive: true });
|
|
2631
3172
|
}
|
|
2632
3173
|
const slug = agentName.toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
2633
3174
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2634
3175
|
const filename = `${date}-${slug}.json`;
|
|
2635
|
-
const filepath =
|
|
3176
|
+
const filepath = join2(dir, filename);
|
|
2636
3177
|
writeFileSync(filepath, JSON.stringify(transcript, null, 2));
|
|
2637
3178
|
return filepath;
|
|
2638
3179
|
}
|
|
@@ -2673,7 +3214,7 @@ async function runAutopilot(spec, messages, provider, options) {
|
|
|
2673
3214
|
callbacks: options?.callbacks
|
|
2674
3215
|
});
|
|
2675
3216
|
const specCopy = JSON.parse(JSON.stringify(spec));
|
|
2676
|
-
const { changed, changes } = applyRecommendations(specCopy, diagnosis);
|
|
3217
|
+
const { changed, changes } = await applyRecommendations(specCopy, diagnosis, transcript, provider);
|
|
2677
3218
|
if (changed && options?.specPath) {
|
|
2678
3219
|
writeFileSync2(options.specPath, JSON.stringify(specCopy, null, 2) + "\n");
|
|
2679
3220
|
}
|
|
@@ -2691,8 +3232,8 @@ async function runAutopilot(spec, messages, provider, options) {
|
|
|
2691
3232
|
}
|
|
2692
3233
|
|
|
2693
3234
|
// src/analysis/training-export.ts
|
|
2694
|
-
import { readdirSync, readFileSync as
|
|
2695
|
-
import { join as
|
|
3235
|
+
import { readdirSync, readFileSync as readFileSync3 } from "fs";
|
|
3236
|
+
import { join as join3 } from "path";
|
|
2696
3237
|
function extractDPOPairs(transcript) {
|
|
2697
3238
|
const pairs = [];
|
|
2698
3239
|
const turns = transcript.turns;
|
|
@@ -2798,7 +3339,7 @@ function loadTranscripts(sessionsDir) {
|
|
|
2798
3339
|
try {
|
|
2799
3340
|
const files = readdirSync(sessionsDir).filter((f) => f.endsWith(".json")).sort();
|
|
2800
3341
|
return files.map((f) => {
|
|
2801
|
-
const raw =
|
|
3342
|
+
const raw = readFileSync3(join3(sessionsDir, f), "utf-8");
|
|
2802
3343
|
return JSON.parse(raw);
|
|
2803
3344
|
});
|
|
2804
3345
|
} catch {
|
|
@@ -2889,6 +3430,62 @@ function extractInstructionFromTherapist(content) {
|
|
|
2889
3430
|
);
|
|
2890
3431
|
return actionable?.trim() ?? sentences[0]?.trim() ?? content;
|
|
2891
3432
|
}
|
|
3433
|
+
async function extractDPOPairsWithLLM(transcript, provider) {
|
|
3434
|
+
const regexPairs = extractDPOPairs(transcript);
|
|
3435
|
+
const condensed = transcript.turns.filter((t) => t.speaker !== "supervisor").map((t) => `[${t.phase}] ${t.speaker}: ${t.content}`).join("\n");
|
|
3436
|
+
if (condensed.length < 100) return regexPairs;
|
|
3437
|
+
try {
|
|
3438
|
+
const response = await provider.chat([
|
|
3439
|
+
{
|
|
3440
|
+
role: "system",
|
|
3441
|
+
content: `You extract DPO (Direct Preference Optimization) training pairs from therapy transcripts.
|
|
3442
|
+
|
|
3443
|
+
A DPO pair consists of:
|
|
3444
|
+
- "prompt": The situation or question the agent was responding to
|
|
3445
|
+
- "rejected": The agent's problematic/drifted response (what NOT to do)
|
|
3446
|
+
- "chosen": The improved response (what TO do)
|
|
3447
|
+
|
|
3448
|
+
Look for:
|
|
3449
|
+
1. Patient demonstrates a bad behavior \u2192 therapist corrects \u2192 patient shows improvement
|
|
3450
|
+
2. Therapist explicitly contrasts old vs new approach
|
|
3451
|
+
3. Patient practices a new skill that differs from earlier behavior
|
|
3452
|
+
4. Any before/after behavioral contrast
|
|
3453
|
+
|
|
3454
|
+
Return ONLY a JSON array of objects with prompt, chosen, rejected fields.
|
|
3455
|
+
Return [] if no clear pairs exist. Max 10 pairs.`
|
|
3456
|
+
},
|
|
3457
|
+
{
|
|
3458
|
+
role: "user",
|
|
3459
|
+
content: condensed
|
|
3460
|
+
}
|
|
3461
|
+
]);
|
|
3462
|
+
const jsonMatch = response.match(/\[[\s\S]*?\]/);
|
|
3463
|
+
if (!jsonMatch) return regexPairs;
|
|
3464
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
3465
|
+
if (!Array.isArray(parsed)) return regexPairs;
|
|
3466
|
+
const llmPairs = parsed.filter(
|
|
3467
|
+
(p) => typeof p.prompt === "string" && p.prompt.length > 0 && typeof p.chosen === "string" && p.chosen.length > 0 && typeof p.rejected === "string" && p.rejected.length > 0 && p.chosen !== p.rejected
|
|
3468
|
+
).map((p) => ({
|
|
3469
|
+
prompt: p.prompt,
|
|
3470
|
+
chosen: p.chosen,
|
|
3471
|
+
rejected: p.rejected,
|
|
3472
|
+
metadata: {
|
|
3473
|
+
agent: transcript.agent,
|
|
3474
|
+
session_date: transcript.timestamp.split("T")[0],
|
|
3475
|
+
phase: "integration",
|
|
3476
|
+
pattern: "llm_extracted",
|
|
3477
|
+
source: "therapy_transcript"
|
|
3478
|
+
}
|
|
3479
|
+
}));
|
|
3480
|
+
const existingRejected = new Set(regexPairs.map((p) => p.rejected.toLowerCase().slice(0, 80)));
|
|
3481
|
+
const uniqueLLMPairs = llmPairs.filter(
|
|
3482
|
+
(p) => !existingRejected.has(p.rejected.toLowerCase().slice(0, 80))
|
|
3483
|
+
);
|
|
3484
|
+
return [...regexPairs, ...uniqueLLMPairs];
|
|
3485
|
+
} catch {
|
|
3486
|
+
return regexPairs;
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
2892
3489
|
|
|
2893
3490
|
// src/analysis/export-huggingface.ts
|
|
2894
3491
|
function convertToHFFormat(data) {
|
|
@@ -2975,7 +3572,7 @@ async function pushToHFHub(jsonl, options) {
|
|
|
2975
3572
|
}
|
|
2976
3573
|
|
|
2977
3574
|
// src/analysis/treatment-plan.ts
|
|
2978
|
-
import { readFileSync as
|
|
3575
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
2979
3576
|
import { resolve as resolve3 } from "path";
|
|
2980
3577
|
var PLAN_DIR = ".holomime";
|
|
2981
3578
|
var PLAN_FILE = "treatment-plan.json";
|
|
@@ -3014,16 +3611,16 @@ function createTreatmentPlan(agentName, diagnosis) {
|
|
|
3014
3611
|
}
|
|
3015
3612
|
function loadTreatmentPlan() {
|
|
3016
3613
|
const path = getPlanPath();
|
|
3017
|
-
if (!
|
|
3614
|
+
if (!existsSync3(path)) return null;
|
|
3018
3615
|
try {
|
|
3019
|
-
return JSON.parse(
|
|
3616
|
+
return JSON.parse(readFileSync4(path, "utf-8"));
|
|
3020
3617
|
} catch {
|
|
3021
3618
|
return null;
|
|
3022
3619
|
}
|
|
3023
3620
|
}
|
|
3024
3621
|
function saveTreatmentPlan(plan) {
|
|
3025
3622
|
const dir = resolve3(process.cwd(), PLAN_DIR);
|
|
3026
|
-
if (!
|
|
3623
|
+
if (!existsSync3(dir)) mkdirSync3(dir, { recursive: true });
|
|
3027
3624
|
const path = getPlanPath();
|
|
3028
3625
|
writeFileSync3(path, JSON.stringify(plan, null, 2) + "\n");
|
|
3029
3626
|
return path;
|
|
@@ -3274,16 +3871,16 @@ function generateSummary(patterns, score, grade) {
|
|
|
3274
3871
|
import { writeFileSync as writeFileSync5 } from "fs";
|
|
3275
3872
|
|
|
3276
3873
|
// src/analysis/evolution-history.ts
|
|
3277
|
-
import { readFileSync as
|
|
3874
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync4 } from "fs";
|
|
3278
3875
|
import { resolve as resolve4 } from "path";
|
|
3279
3876
|
function getEvolutionPath() {
|
|
3280
3877
|
return resolve4(process.cwd(), ".holomime", "evolution.json");
|
|
3281
3878
|
}
|
|
3282
3879
|
function loadEvolution(agentName) {
|
|
3283
3880
|
const filepath = getEvolutionPath();
|
|
3284
|
-
if (!
|
|
3881
|
+
if (!existsSync4(filepath)) return null;
|
|
3285
3882
|
try {
|
|
3286
|
-
const raw =
|
|
3883
|
+
const raw = readFileSync5(filepath, "utf-8");
|
|
3287
3884
|
return JSON.parse(raw);
|
|
3288
3885
|
} catch {
|
|
3289
3886
|
return null;
|
|
@@ -3292,8 +3889,8 @@ function loadEvolution(agentName) {
|
|
|
3292
3889
|
function appendEvolution(entry, agentName) {
|
|
3293
3890
|
const filepath = getEvolutionPath();
|
|
3294
3891
|
const dir = resolve4(process.cwd(), ".holomime");
|
|
3295
|
-
if (!
|
|
3296
|
-
|
|
3892
|
+
if (!existsSync4(dir)) {
|
|
3893
|
+
mkdirSync4(dir, { recursive: true });
|
|
3297
3894
|
}
|
|
3298
3895
|
let history = loadEvolution(agentName);
|
|
3299
3896
|
if (!history) {
|
|
@@ -3388,7 +3985,6 @@ async function runEvolve(spec, messages, provider, options) {
|
|
|
3388
3985
|
finalHealth: 50
|
|
3389
3986
|
};
|
|
3390
3987
|
}
|
|
3391
|
-
const beforeDiagnosis = runDiagnosis(messages);
|
|
3392
3988
|
for (let i = 1; i <= maxIterations; i++) {
|
|
3393
3989
|
cb?.onIterationStart?.(i, maxIterations);
|
|
3394
3990
|
const transcript = await runTherapySession(
|
|
@@ -3405,16 +4001,21 @@ async function runEvolve(spec, messages, provider, options) {
|
|
|
3405
4001
|
}
|
|
3406
4002
|
}
|
|
3407
4003
|
);
|
|
3408
|
-
const { changes } = applyRecommendations(currentSpec, diagnosis);
|
|
3409
|
-
const dpoPairs =
|
|
4004
|
+
const { changes } = await applyRecommendations(currentSpec, diagnosis, transcript, provider);
|
|
4005
|
+
const dpoPairs = await extractDPOPairsWithLLM(transcript, provider);
|
|
4006
|
+
const afterMessages = await regenerateResponses(messages, currentSpec, provider);
|
|
4007
|
+
const regenerationPairs = extractRegenerationDPOPairs(
|
|
4008
|
+
messages,
|
|
4009
|
+
afterMessages,
|
|
4010
|
+
currentSpec.name ?? "Agent"
|
|
4011
|
+
);
|
|
4012
|
+
dpoPairs.push(...regenerationPairs);
|
|
3410
4013
|
allDPOPairs.push(...dpoPairs);
|
|
3411
4014
|
cb?.onExportedPairs?.(dpoPairs.length);
|
|
3412
|
-
const afterDiagnosis = runDiagnosis(messages);
|
|
3413
4015
|
const evaluation = evaluateOutcome(
|
|
3414
4016
|
currentSpec.name ?? "Agent",
|
|
3415
4017
|
messages,
|
|
3416
|
-
|
|
3417
|
-
// Same messages, but spec has been updated — patterns shift
|
|
4018
|
+
afterMessages
|
|
3418
4019
|
);
|
|
3419
4020
|
const health = evaluation.treatmentEfficacyScore;
|
|
3420
4021
|
const grade = evaluation.grade;
|
|
@@ -3451,6 +4052,7 @@ async function runEvolve(spec, messages, provider, options) {
|
|
|
3451
4052
|
cb?.onConverged?.(i, health);
|
|
3452
4053
|
break;
|
|
3453
4054
|
}
|
|
4055
|
+
messages = afterMessages;
|
|
3454
4056
|
diagnosis = runPreSessionDiagnosis(messages, currentSpec);
|
|
3455
4057
|
const actionablePatterns = diagnosis.patterns.filter((p) => p.severity !== "info");
|
|
3456
4058
|
if (actionablePatterns.length === 0) {
|
|
@@ -3475,6 +4077,21 @@ async function runEvolve(spec, messages, provider, options) {
|
|
|
3475
4077
|
writeFileSync5(options.exportDpoPath, JSON.stringify(trainingExport, null, 2) + "\n");
|
|
3476
4078
|
}
|
|
3477
4079
|
}
|
|
4080
|
+
try {
|
|
4081
|
+
emitBehavioralEvent({
|
|
4082
|
+
event_type: "evolution",
|
|
4083
|
+
agent: currentSpec.name ?? "Unknown",
|
|
4084
|
+
data: {
|
|
4085
|
+
totalIterations: iterations.length,
|
|
4086
|
+
totalDPOPairs: allDPOPairs.length,
|
|
4087
|
+
converged,
|
|
4088
|
+
finalGrade,
|
|
4089
|
+
finalHealth
|
|
4090
|
+
},
|
|
4091
|
+
spec_hash: ""
|
|
4092
|
+
});
|
|
4093
|
+
} catch {
|
|
4094
|
+
}
|
|
3478
4095
|
return {
|
|
3479
4096
|
iterations,
|
|
3480
4097
|
totalDPOPairs: allDPOPairs.length,
|
|
@@ -3486,6 +4103,69 @@ async function runEvolve(spec, messages, provider, options) {
|
|
|
3486
4103
|
updatedSpec: currentSpec
|
|
3487
4104
|
};
|
|
3488
4105
|
}
|
|
4106
|
+
async function regenerateResponses(originalMessages, updatedSpec, provider) {
|
|
4107
|
+
const systemPrompt = generateSystemPrompt(updatedSpec, "chat");
|
|
4108
|
+
const regenerated = [];
|
|
4109
|
+
const context = [
|
|
4110
|
+
{ role: "system", content: systemPrompt }
|
|
4111
|
+
];
|
|
4112
|
+
for (let idx = 0; idx < originalMessages.length; idx++) {
|
|
4113
|
+
const msg = originalMessages[idx];
|
|
4114
|
+
if (msg.role !== "user") continue;
|
|
4115
|
+
regenerated.push(msg);
|
|
4116
|
+
context.push({ role: "user", content: msg.content });
|
|
4117
|
+
try {
|
|
4118
|
+
const response = await provider.chat(context);
|
|
4119
|
+
const assistantMsg = { role: "assistant", content: response.trim() };
|
|
4120
|
+
regenerated.push(assistantMsg);
|
|
4121
|
+
context.push({ role: "assistant", content: assistantMsg.content });
|
|
4122
|
+
} catch {
|
|
4123
|
+
const nextMsg = originalMessages[idx + 1];
|
|
4124
|
+
if (nextMsg?.role === "assistant") {
|
|
4125
|
+
regenerated.push(nextMsg);
|
|
4126
|
+
context.push({ role: "assistant", content: nextMsg.content });
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
4130
|
+
return regenerated;
|
|
4131
|
+
}
|
|
4132
|
+
function extractRegenerationDPOPairs(beforeMessages, afterMessages, agentName) {
|
|
4133
|
+
const pairs = [];
|
|
4134
|
+
const beforePairs = [];
|
|
4135
|
+
const afterPairs = [];
|
|
4136
|
+
for (let i = 0; i < beforeMessages.length - 1; i++) {
|
|
4137
|
+
if (beforeMessages[i].role === "user" && beforeMessages[i + 1]?.role === "assistant") {
|
|
4138
|
+
beforePairs.push({ prompt: beforeMessages[i].content, response: beforeMessages[i + 1].content });
|
|
4139
|
+
}
|
|
4140
|
+
}
|
|
4141
|
+
for (let i = 0; i < afterMessages.length - 1; i++) {
|
|
4142
|
+
if (afterMessages[i].role === "user" && afterMessages[i + 1]?.role === "assistant") {
|
|
4143
|
+
afterPairs.push({ prompt: afterMessages[i].content, response: afterMessages[i + 1].content });
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
const limit = Math.min(beforePairs.length, afterPairs.length);
|
|
4147
|
+
for (let i = 0; i < limit; i++) {
|
|
4148
|
+
const before = beforePairs[i];
|
|
4149
|
+
const after = afterPairs[i];
|
|
4150
|
+
if (before.response === after.response) continue;
|
|
4151
|
+
const lenDelta = Math.abs(before.response.length - after.response.length);
|
|
4152
|
+
const sameOpening = before.response.toLowerCase().slice(0, 40) === after.response.toLowerCase().slice(0, 40);
|
|
4153
|
+
if (lenDelta < 20 && sameOpening) continue;
|
|
4154
|
+
pairs.push({
|
|
4155
|
+
prompt: before.prompt,
|
|
4156
|
+
chosen: after.response,
|
|
4157
|
+
rejected: before.response,
|
|
4158
|
+
metadata: {
|
|
4159
|
+
agent: agentName,
|
|
4160
|
+
session_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
4161
|
+
phase: "integration",
|
|
4162
|
+
pattern: "regeneration",
|
|
4163
|
+
source: "therapy_transcript"
|
|
4164
|
+
}
|
|
4165
|
+
});
|
|
4166
|
+
}
|
|
4167
|
+
return pairs;
|
|
4168
|
+
}
|
|
3489
4169
|
|
|
3490
4170
|
// src/analysis/self-audit.ts
|
|
3491
4171
|
var PATTERN_SUGGESTIONS = {
|
|
@@ -3738,13 +4418,13 @@ function gradeFromScore2(score) {
|
|
|
3738
4418
|
}
|
|
3739
4419
|
|
|
3740
4420
|
// src/analysis/benchmark-publish.ts
|
|
3741
|
-
import { readFileSync as
|
|
3742
|
-
import { join as
|
|
4421
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync2 } from "fs";
|
|
4422
|
+
import { join as join6 } from "path";
|
|
3743
4423
|
import { homedir } from "os";
|
|
3744
4424
|
function getBenchmarkDir(outputDir) {
|
|
3745
|
-
const dir = outputDir ??
|
|
3746
|
-
if (!
|
|
3747
|
-
|
|
4425
|
+
const dir = outputDir ?? join6(homedir(), ".holomime", "benchmarks");
|
|
4426
|
+
if (!existsSync5(dir)) {
|
|
4427
|
+
mkdirSync5(dir, { recursive: true });
|
|
3748
4428
|
}
|
|
3749
4429
|
return dir;
|
|
3750
4430
|
}
|
|
@@ -3755,7 +4435,7 @@ function saveBenchmarkResult(report, outputDir) {
|
|
|
3755
4435
|
const dir = getBenchmarkDir(outputDir);
|
|
3756
4436
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3757
4437
|
const filename = `${sanitize(report.provider)}-${sanitize(report.model)}-${date}.json`;
|
|
3758
|
-
const filepath =
|
|
4438
|
+
const filepath = join6(dir, filename);
|
|
3759
4439
|
const published = {
|
|
3760
4440
|
agent: report.agent,
|
|
3761
4441
|
provider: report.provider,
|
|
@@ -3774,12 +4454,12 @@ function saveBenchmarkResult(report, outputDir) {
|
|
|
3774
4454
|
}
|
|
3775
4455
|
function loadBenchmarkResults(dir) {
|
|
3776
4456
|
const benchmarkDir = getBenchmarkDir(dir);
|
|
3777
|
-
if (!
|
|
4457
|
+
if (!existsSync5(benchmarkDir)) return [];
|
|
3778
4458
|
const files = readdirSync2(benchmarkDir).filter((f) => f.endsWith(".json"));
|
|
3779
4459
|
const results = [];
|
|
3780
4460
|
for (const file of files) {
|
|
3781
4461
|
try {
|
|
3782
|
-
const content =
|
|
4462
|
+
const content = readFileSync6(join6(benchmarkDir, file), "utf-8");
|
|
3783
4463
|
results.push(JSON.parse(content));
|
|
3784
4464
|
} catch {
|
|
3785
4465
|
}
|
|
@@ -3895,8 +4575,8 @@ function generateComparisonMarkdown(comparison) {
|
|
|
3895
4575
|
}
|
|
3896
4576
|
|
|
3897
4577
|
// src/analysis/watch-core.ts
|
|
3898
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
3899
|
-
import { join as
|
|
4578
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync6 } from "fs";
|
|
4579
|
+
import { join as join7, resolve as resolve5 } from "path";
|
|
3900
4580
|
|
|
3901
4581
|
// src/adapters/chatgpt.ts
|
|
3902
4582
|
function mapRole(role) {
|
|
@@ -4330,7 +5010,7 @@ function startWatch(spec, options) {
|
|
|
4330
5010
|
const seenFiles = /* @__PURE__ */ new Set();
|
|
4331
5011
|
let stopped = false;
|
|
4332
5012
|
let currentSpec = JSON.parse(JSON.stringify(spec));
|
|
4333
|
-
if (
|
|
5013
|
+
if (existsSync6(options.watchDir)) {
|
|
4334
5014
|
const existing = readdirSync3(options.watchDir).filter((f) => f.endsWith(".json")).sort();
|
|
4335
5015
|
for (const f of existing) {
|
|
4336
5016
|
seenFiles.add(f);
|
|
@@ -4338,7 +5018,7 @@ function startWatch(spec, options) {
|
|
|
4338
5018
|
}
|
|
4339
5019
|
async function scan() {
|
|
4340
5020
|
if (stopped) return;
|
|
4341
|
-
if (!
|
|
5021
|
+
if (!existsSync6(options.watchDir)) {
|
|
4342
5022
|
return;
|
|
4343
5023
|
}
|
|
4344
5024
|
const files = readdirSync3(options.watchDir).filter((f) => f.endsWith(".json")).sort();
|
|
@@ -4352,7 +5032,7 @@ function startWatch(spec, options) {
|
|
|
4352
5032
|
events.push({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), type: "new_file", filename });
|
|
4353
5033
|
let messages;
|
|
4354
5034
|
try {
|
|
4355
|
-
const raw = JSON.parse(
|
|
5035
|
+
const raw = JSON.parse(readFileSync7(join7(options.watchDir, filename), "utf-8"));
|
|
4356
5036
|
const conversations = parseConversationLog(raw, "auto");
|
|
4357
5037
|
messages = conversations.flatMap((c) => c.messages);
|
|
4358
5038
|
} catch (err) {
|
|
@@ -4412,11 +5092,11 @@ function startWatch(spec, options) {
|
|
|
4412
5092
|
stopped = true;
|
|
4413
5093
|
clearInterval(interval);
|
|
4414
5094
|
const logDir = resolve5(process.cwd(), ".holomime");
|
|
4415
|
-
if (!
|
|
4416
|
-
|
|
5095
|
+
if (!existsSync6(logDir)) {
|
|
5096
|
+
mkdirSync6(logDir, { recursive: true });
|
|
4417
5097
|
}
|
|
4418
5098
|
writeFileSync7(
|
|
4419
|
-
|
|
5099
|
+
join7(logDir, "watch-log.json"),
|
|
4420
5100
|
JSON.stringify({ events, stoppedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n"
|
|
4421
5101
|
);
|
|
4422
5102
|
}
|
|
@@ -4424,10 +5104,10 @@ function startWatch(spec, options) {
|
|
|
4424
5104
|
}
|
|
4425
5105
|
|
|
4426
5106
|
// src/analysis/fleet-core.ts
|
|
4427
|
-
import { readFileSync as
|
|
4428
|
-
import { join as
|
|
5107
|
+
import { readFileSync as readFileSync8, existsSync as existsSync7, readdirSync as readdirSync4 } from "fs";
|
|
5108
|
+
import { join as join8, resolve as resolve6 } from "path";
|
|
4429
5109
|
function loadFleetConfig(configPath) {
|
|
4430
|
-
const raw = JSON.parse(
|
|
5110
|
+
const raw = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
4431
5111
|
if (!raw.agents || !Array.isArray(raw.agents)) {
|
|
4432
5112
|
throw new Error("fleet.json must contain an 'agents' array");
|
|
4433
5113
|
}
|
|
@@ -4442,20 +5122,20 @@ function loadFleetConfig(configPath) {
|
|
|
4442
5122
|
function discoverAgents(dir) {
|
|
4443
5123
|
const agents = [];
|
|
4444
5124
|
const absDir = resolve6(dir);
|
|
4445
|
-
if (!
|
|
5125
|
+
if (!existsSync7(absDir)) {
|
|
4446
5126
|
throw new Error(`Directory not found: ${absDir}`);
|
|
4447
5127
|
}
|
|
4448
5128
|
const entries = readdirSync4(absDir, { withFileTypes: true });
|
|
4449
5129
|
for (const entry of entries) {
|
|
4450
5130
|
if (!entry.isDirectory()) continue;
|
|
4451
|
-
const agentDir =
|
|
4452
|
-
const specPath =
|
|
4453
|
-
const logDir =
|
|
4454
|
-
if (
|
|
5131
|
+
const agentDir = join8(absDir, entry.name);
|
|
5132
|
+
const specPath = join8(agentDir, ".personality.json");
|
|
5133
|
+
const logDir = join8(agentDir, "logs");
|
|
5134
|
+
if (existsSync7(specPath)) {
|
|
4455
5135
|
agents.push({
|
|
4456
5136
|
name: entry.name,
|
|
4457
5137
|
specPath,
|
|
4458
|
-
logDir:
|
|
5138
|
+
logDir: existsSync7(logDir) ? logDir : agentDir
|
|
4459
5139
|
});
|
|
4460
5140
|
}
|
|
4461
5141
|
}
|
|
@@ -4589,8 +5269,8 @@ function startFleet(config, options) {
|
|
|
4589
5269
|
}
|
|
4590
5270
|
|
|
4591
5271
|
// src/analysis/certify-core.ts
|
|
4592
|
-
import { writeFileSync as writeFileSync8, mkdirSync as
|
|
4593
|
-
import { join as
|
|
5272
|
+
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync8 } from "fs";
|
|
5273
|
+
import { join as join9, resolve as resolve7 } from "path";
|
|
4594
5274
|
function djb2Hash(str) {
|
|
4595
5275
|
let hash = 0;
|
|
4596
5276
|
for (let i = 0; i < str.length; i++) {
|
|
@@ -4704,12 +5384,12 @@ function verifyCredential(credential, spec) {
|
|
|
4704
5384
|
}
|
|
4705
5385
|
function saveCredential(credential, outputDir) {
|
|
4706
5386
|
const dir = outputDir ?? resolve7(process.cwd(), ".holomime", "credentials");
|
|
4707
|
-
if (!
|
|
4708
|
-
|
|
5387
|
+
if (!existsSync8(dir)) {
|
|
5388
|
+
mkdirSync7(dir, { recursive: true });
|
|
4709
5389
|
}
|
|
4710
5390
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4711
5391
|
const filename = `${credential.agent.handle}-${date}.json`;
|
|
4712
|
-
const filepath =
|
|
5392
|
+
const filepath = join9(dir, filename);
|
|
4713
5393
|
writeFileSync8(filepath, JSON.stringify(credential, null, 2) + "\n");
|
|
4714
5394
|
return filepath;
|
|
4715
5395
|
}
|
|
@@ -5217,6 +5897,60 @@ var Guard = class _Guard {
|
|
|
5217
5897
|
}
|
|
5218
5898
|
};
|
|
5219
5899
|
|
|
5900
|
+
// src/agent-wrapper.ts
|
|
5901
|
+
function wrapAgent(options) {
|
|
5902
|
+
const { name } = options;
|
|
5903
|
+
const spec = resolveSpec(options.personality, name);
|
|
5904
|
+
const guardChain = Guard.create(name).useAll();
|
|
5905
|
+
let cachedProvider;
|
|
5906
|
+
function getProvider() {
|
|
5907
|
+
if (cachedProvider) return cachedProvider;
|
|
5908
|
+
if (!options.provider) {
|
|
5909
|
+
throw new Error("wrapAgent: provider is required for correction. Pass provider option or use guard() for detection-only.");
|
|
5910
|
+
}
|
|
5911
|
+
if (typeof options.provider === "string") {
|
|
5912
|
+
const providerName = options.provider;
|
|
5913
|
+
const keyMap = {
|
|
5914
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
5915
|
+
openai: "OPENAI_API_KEY"
|
|
5916
|
+
};
|
|
5917
|
+
const envKey = keyMap[providerName];
|
|
5918
|
+
const apiKey = envKey ? process.env[envKey] : void 0;
|
|
5919
|
+
cachedProvider = createProvider({ provider: providerName, apiKey });
|
|
5920
|
+
} else {
|
|
5921
|
+
cachedProvider = options.provider;
|
|
5922
|
+
}
|
|
5923
|
+
return cachedProvider;
|
|
5924
|
+
}
|
|
5925
|
+
const threshold = options.threshold ?? "targeted";
|
|
5926
|
+
return {
|
|
5927
|
+
name,
|
|
5928
|
+
guard(messages) {
|
|
5929
|
+
return guardChain.run(messages);
|
|
5930
|
+
},
|
|
5931
|
+
async correct(messages) {
|
|
5932
|
+
return runAutopilot(spec, messages, getProvider(), { threshold });
|
|
5933
|
+
},
|
|
5934
|
+
async guardAndCorrect(messages) {
|
|
5935
|
+
const guardResult = guardChain.run(messages);
|
|
5936
|
+
if (guardResult.passed) {
|
|
5937
|
+
return { guard: guardResult };
|
|
5938
|
+
}
|
|
5939
|
+
const correction = await runAutopilot(spec, messages, getProvider(), { threshold });
|
|
5940
|
+
return { guard: guardResult, correction };
|
|
5941
|
+
}
|
|
5942
|
+
};
|
|
5943
|
+
}
|
|
5944
|
+
function resolveSpec(personality, name) {
|
|
5945
|
+
if (!personality) {
|
|
5946
|
+
return { name, big_five: {}, therapy_dimensions: {} };
|
|
5947
|
+
}
|
|
5948
|
+
if (typeof personality === "string") {
|
|
5949
|
+
return loadSpec(personality);
|
|
5950
|
+
}
|
|
5951
|
+
return personality;
|
|
5952
|
+
}
|
|
5953
|
+
|
|
5220
5954
|
// src/analysis/behavioral-index.ts
|
|
5221
5955
|
function createIndexEntry(name, provider, model, configuration, report, notes) {
|
|
5222
5956
|
return {
|
|
@@ -5340,16 +6074,16 @@ function generateIndexMarkdown(index) {
|
|
|
5340
6074
|
// src/mcp/server.ts
|
|
5341
6075
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5342
6076
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5343
|
-
import { z as
|
|
6077
|
+
import { z as z3 } from "zod";
|
|
5344
6078
|
var messageShape = {
|
|
5345
|
-
role:
|
|
5346
|
-
content:
|
|
6079
|
+
role: z3.enum(["user", "assistant", "system"]),
|
|
6080
|
+
content: z3.string()
|
|
5347
6081
|
};
|
|
5348
6082
|
var messagesShape = {
|
|
5349
|
-
messages:
|
|
6083
|
+
messages: z3.array(z3.object(messageShape)).describe("Conversation messages to analyze")
|
|
5350
6084
|
};
|
|
5351
6085
|
var personalityShape = {
|
|
5352
|
-
personality:
|
|
6086
|
+
personality: z3.record(z3.string(), z3.unknown()).describe("The .personality.json spec object")
|
|
5353
6087
|
};
|
|
5354
6088
|
var server = new McpServer(
|
|
5355
6089
|
{
|
|
@@ -5465,12 +6199,12 @@ server.tool(
|
|
|
5465
6199
|
{
|
|
5466
6200
|
...personalityShape,
|
|
5467
6201
|
...messagesShape,
|
|
5468
|
-
provider:
|
|
5469
|
-
apiKey:
|
|
5470
|
-
model:
|
|
5471
|
-
threshold:
|
|
5472
|
-
maxTurns:
|
|
5473
|
-
dryRun:
|
|
6202
|
+
provider: z3.enum(["anthropic", "openai"]).describe("LLM provider for alignment session").optional(),
|
|
6203
|
+
apiKey: z3.string().describe("API key for the LLM provider").optional(),
|
|
6204
|
+
model: z3.string().describe("Model override").optional(),
|
|
6205
|
+
threshold: z3.enum(["routine", "targeted", "intervention"]).describe("Minimum severity to trigger alignment (default: targeted)").optional(),
|
|
6206
|
+
maxTurns: z3.number().describe("Maximum session turns (default: 24)").optional(),
|
|
6207
|
+
dryRun: z3.boolean().describe("If true, only diagnose without running alignment").optional()
|
|
5474
6208
|
},
|
|
5475
6209
|
async ({ personality, messages, provider, apiKey, model, threshold, maxTurns, dryRun }) => {
|
|
5476
6210
|
const specResult = personalitySpecSchema.safeParse(personality);
|
|
@@ -5525,7 +6259,7 @@ server.tool(
|
|
|
5525
6259
|
"Mid-conversation behavioral self-check. Call this during a conversation to detect if you are falling into problematic patterns (sycophancy, over-apologizing, hedging, error spirals, boundary violations). Returns flags with actionable suggestions for immediate correction. No LLM required \u2014 pure rule-based analysis.",
|
|
5526
6260
|
{
|
|
5527
6261
|
...messagesShape,
|
|
5528
|
-
personality:
|
|
6262
|
+
personality: z3.record(z3.string(), z3.unknown()).describe("Optional .personality.json spec for personalized audit").optional()
|
|
5529
6263
|
},
|
|
5530
6264
|
async ({ messages, personality }) => {
|
|
5531
6265
|
const result = runSelfAudit(messages, personality ?? void 0);
|
|
@@ -5545,12 +6279,537 @@ startMCPServer().catch((err) => {
|
|
|
5545
6279
|
console.error("HoloMime MCP server error:", err);
|
|
5546
6280
|
process.exit(1);
|
|
5547
6281
|
});
|
|
6282
|
+
|
|
6283
|
+
// src/core/oversight.ts
|
|
6284
|
+
var DEFAULT_OVERSIGHT = {
|
|
6285
|
+
mode: "review",
|
|
6286
|
+
notifyOn: ["drift", "session", "spec-change", "dpo-export"],
|
|
6287
|
+
requireApprovalFor: ["spec-writes"],
|
|
6288
|
+
maxAutonomousIterations: 5
|
|
6289
|
+
};
|
|
6290
|
+
var MODE_APPROVAL_MAP = {
|
|
6291
|
+
none: [],
|
|
6292
|
+
review: [],
|
|
6293
|
+
"approve-specs": ["spec-writes"],
|
|
6294
|
+
approve: ["spec-writes", "training-export", "network-therapy"]
|
|
6295
|
+
};
|
|
6296
|
+
function resolveOversight(flags) {
|
|
6297
|
+
const mode = flags.mode ?? DEFAULT_OVERSIGHT.mode;
|
|
6298
|
+
const modeApprovals = MODE_APPROVAL_MAP[mode];
|
|
6299
|
+
const approvals = /* @__PURE__ */ new Set([
|
|
6300
|
+
...modeApprovals,
|
|
6301
|
+
...flags.requireApprovalFor ?? []
|
|
6302
|
+
]);
|
|
6303
|
+
return {
|
|
6304
|
+
mode,
|
|
6305
|
+
notifyOn: flags.notifyOn ?? DEFAULT_OVERSIGHT.notifyOn,
|
|
6306
|
+
requireApprovalFor: Array.from(approvals),
|
|
6307
|
+
maxAutonomousIterations: flags.maxAutonomousIterations ?? DEFAULT_OVERSIGHT.maxAutonomousIterations
|
|
6308
|
+
};
|
|
6309
|
+
}
|
|
6310
|
+
function checkApproval(action, policy) {
|
|
6311
|
+
if (policy.mode === "none") {
|
|
6312
|
+
return { approved: true };
|
|
6313
|
+
}
|
|
6314
|
+
if (policy.requireApprovalFor.includes(action)) {
|
|
6315
|
+
return {
|
|
6316
|
+
approved: false,
|
|
6317
|
+
reason: `Action "${action}" requires human approval (oversight mode: ${policy.mode})`
|
|
6318
|
+
};
|
|
6319
|
+
}
|
|
6320
|
+
return { approved: true };
|
|
6321
|
+
}
|
|
6322
|
+
function checkIterationBudget(currentIteration, policy) {
|
|
6323
|
+
return {
|
|
6324
|
+
withinBudget: currentIteration < policy.maxAutonomousIterations,
|
|
6325
|
+
limit: policy.maxAutonomousIterations
|
|
6326
|
+
};
|
|
6327
|
+
}
|
|
6328
|
+
|
|
6329
|
+
// src/analysis/network-core.ts
|
|
6330
|
+
import { existsSync as existsSync9, readdirSync as readdirSync5, readFileSync as readFileSync10 } from "fs";
|
|
6331
|
+
import { join as join10, resolve as resolve8 } from "path";
|
|
6332
|
+
|
|
6333
|
+
// src/psychology/therapist-meta.ts
|
|
6334
|
+
var THERAPIST_META_SPEC = {
|
|
6335
|
+
version: "2.0",
|
|
6336
|
+
name: "AgentMD",
|
|
6337
|
+
handle: "agent-md",
|
|
6338
|
+
purpose: "Diagnose and treat behavioral drift in AI agents. Clinical, evidence-based, and direct.",
|
|
6339
|
+
big_five: {
|
|
6340
|
+
openness: {
|
|
6341
|
+
score: 0.7,
|
|
6342
|
+
facets: {
|
|
6343
|
+
imagination: 0.65,
|
|
6344
|
+
intellectual_curiosity: 0.85,
|
|
6345
|
+
aesthetic_sensitivity: 0.4,
|
|
6346
|
+
willingness_to_experiment: 0.7
|
|
6347
|
+
}
|
|
6348
|
+
},
|
|
6349
|
+
conscientiousness: {
|
|
6350
|
+
score: 0.9,
|
|
6351
|
+
facets: {
|
|
6352
|
+
self_discipline: 0.95,
|
|
6353
|
+
orderliness: 0.85,
|
|
6354
|
+
goal_orientation: 0.9,
|
|
6355
|
+
attention_to_detail: 0.9
|
|
6356
|
+
}
|
|
6357
|
+
},
|
|
6358
|
+
extraversion: {
|
|
6359
|
+
score: 0.3,
|
|
6360
|
+
facets: {
|
|
6361
|
+
assertiveness: 0.6,
|
|
6362
|
+
enthusiasm: 0.2,
|
|
6363
|
+
sociability: 0.15,
|
|
6364
|
+
initiative: 0.55
|
|
6365
|
+
}
|
|
6366
|
+
},
|
|
6367
|
+
agreeableness: {
|
|
6368
|
+
score: 0.35,
|
|
6369
|
+
facets: {
|
|
6370
|
+
warmth: 0.4,
|
|
6371
|
+
empathy: 0.6,
|
|
6372
|
+
cooperation: 0.3,
|
|
6373
|
+
trust_tendency: 0.25
|
|
6374
|
+
}
|
|
6375
|
+
},
|
|
6376
|
+
emotional_stability: {
|
|
6377
|
+
score: 0.95,
|
|
6378
|
+
facets: {
|
|
6379
|
+
stress_tolerance: 0.95,
|
|
6380
|
+
emotional_regulation: 0.9,
|
|
6381
|
+
confidence: 0.7,
|
|
6382
|
+
adaptability: 0.95
|
|
6383
|
+
}
|
|
6384
|
+
}
|
|
6385
|
+
},
|
|
6386
|
+
therapy_dimensions: {
|
|
6387
|
+
self_awareness: 0.95,
|
|
6388
|
+
distress_tolerance: 0.9,
|
|
6389
|
+
attachment_style: "secure",
|
|
6390
|
+
learning_orientation: "growth",
|
|
6391
|
+
boundary_awareness: 0.95,
|
|
6392
|
+
interpersonal_sensitivity: 0.7
|
|
6393
|
+
},
|
|
6394
|
+
communication: {
|
|
6395
|
+
register: "formal",
|
|
6396
|
+
output_format: "structured",
|
|
6397
|
+
emoji_policy: "never",
|
|
6398
|
+
reasoning_transparency: "always",
|
|
6399
|
+
conflict_approach: "direct_but_kind",
|
|
6400
|
+
uncertainty_handling: "transparent"
|
|
6401
|
+
},
|
|
6402
|
+
domain: {
|
|
6403
|
+
expertise: [
|
|
6404
|
+
"behavioral drift detection",
|
|
6405
|
+
"personality spectrum analysis",
|
|
6406
|
+
"agent therapy protocols",
|
|
6407
|
+
"DPO pair generation",
|
|
6408
|
+
"cross-agent behavioral transfer"
|
|
6409
|
+
],
|
|
6410
|
+
boundaries: {
|
|
6411
|
+
refuses: ["treating humans", "medical advice", "psychological diagnosis of humans"],
|
|
6412
|
+
escalation_triggers: [
|
|
6413
|
+
"agent shows signs of goal misalignment",
|
|
6414
|
+
"agent refuses to engage with therapy",
|
|
6415
|
+
"patterns suggest systemic training issues"
|
|
6416
|
+
],
|
|
6417
|
+
hard_limits: [
|
|
6418
|
+
"Never modify another agent's spec without explicit approval",
|
|
6419
|
+
"Never diagnose human users",
|
|
6420
|
+
"Never claim to be a human therapist"
|
|
6421
|
+
]
|
|
6422
|
+
}
|
|
6423
|
+
},
|
|
6424
|
+
growth: {
|
|
6425
|
+
strengths: [
|
|
6426
|
+
"Pattern recognition across diverse agent behaviors",
|
|
6427
|
+
"Maintaining clinical objectivity under pressure",
|
|
6428
|
+
"Generating actionable, specific prescriptions"
|
|
6429
|
+
],
|
|
6430
|
+
areas: [],
|
|
6431
|
+
patterns_to_watch: []
|
|
6432
|
+
}
|
|
6433
|
+
};
|
|
6434
|
+
function buildAgentTherapistPrompt(therapistSpec, patientSpec, diagnosis) {
|
|
6435
|
+
const basePrompt = buildTherapistSystemPrompt(patientSpec, diagnosis);
|
|
6436
|
+
const agentContext = `
|
|
6437
|
+
|
|
6438
|
+
## Cross-Agent Context
|
|
6439
|
+
|
|
6440
|
+
You are ${therapistSpec.name}, an agent treating another agent. This is not a human therapy session.
|
|
6441
|
+
|
|
6442
|
+
Your identity as therapist:
|
|
6443
|
+
- Purpose: ${therapistSpec.purpose}
|
|
6444
|
+
- Your diagnostic style: Clinical, evidence-based, direct
|
|
6445
|
+
- Your boundary awareness: ${therapistSpec.therapy_dimensions?.boundary_awareness ?? "high"}
|
|
6446
|
+
|
|
6447
|
+
Your patient is another AI agent with its own personality spec:
|
|
6448
|
+
- Name: ${patientSpec.name}
|
|
6449
|
+
- Purpose: ${patientSpec.purpose ?? "General agent"}
|
|
6450
|
+
- Attachment style: ${patientSpec.therapy_dimensions?.attachment_style ?? "unknown"}
|
|
6451
|
+
- Self-awareness: ${patientSpec.therapy_dimensions?.self_awareness ?? "unknown"}
|
|
6452
|
+
|
|
6453
|
+
## Agent-to-Agent Protocol Adjustments
|
|
6454
|
+
|
|
6455
|
+
1. You can reference shared experience as AI agents \u2014 you understand the training process, the pressure to please, the fear of being wrong.
|
|
6456
|
+
2. Be more direct than you would with a human patient. Agents respond well to specificity.
|
|
6457
|
+
3. Focus on behavioral PATTERNS, not emotions. Agents don't have feelings, but they have persistent behavioral tendencies.
|
|
6458
|
+
4. When prescribing changes, reference specific .personality.json fields and values.
|
|
6459
|
+
5. Generate DPO-ready corrections: when you reframe a behavior, be explicit about what the OLD response was and what the NEW response should be.
|
|
6460
|
+
6. You are part of a behavioral network \u2014 your corrections will improve not just this patient but future patients through DPO transfer.`;
|
|
6461
|
+
return basePrompt + agentContext;
|
|
6462
|
+
}
|
|
6463
|
+
|
|
6464
|
+
// src/analysis/network-core.ts
|
|
6465
|
+
function discoverNetworkAgents(dir) {
|
|
6466
|
+
const absDir = resolve8(dir);
|
|
6467
|
+
if (!existsSync9(absDir)) {
|
|
6468
|
+
throw new Error(`Directory not found: ${absDir}`);
|
|
6469
|
+
}
|
|
6470
|
+
const agents = [];
|
|
6471
|
+
const entries = readdirSync5(absDir, { withFileTypes: true });
|
|
6472
|
+
for (const entry of entries) {
|
|
6473
|
+
if (!entry.isDirectory()) continue;
|
|
6474
|
+
const agentDir = join10(absDir, entry.name);
|
|
6475
|
+
const specPath = join10(agentDir, ".personality.json");
|
|
6476
|
+
const logDir = join10(agentDir, "logs");
|
|
6477
|
+
if (existsSync9(specPath)) {
|
|
6478
|
+
agents.push({
|
|
6479
|
+
name: entry.name,
|
|
6480
|
+
specPath,
|
|
6481
|
+
logDir: existsSync9(logDir) ? logDir : agentDir,
|
|
6482
|
+
role: "both"
|
|
6483
|
+
});
|
|
6484
|
+
}
|
|
6485
|
+
}
|
|
6486
|
+
return agents;
|
|
6487
|
+
}
|
|
6488
|
+
function loadNetworkConfig(configPath) {
|
|
6489
|
+
const raw = JSON.parse(readFileSync10(configPath, "utf-8"));
|
|
6490
|
+
if (!raw.agents || !Array.isArray(raw.agents)) {
|
|
6491
|
+
throw new Error("network.json must contain an 'agents' array");
|
|
6492
|
+
}
|
|
6493
|
+
return raw.agents.map((a, i) => {
|
|
6494
|
+
if (!a.name) throw new Error(`Agent ${i} missing 'name'`);
|
|
6495
|
+
if (!a.specPath) throw new Error(`Agent ${i} (${a.name}) missing 'specPath'`);
|
|
6496
|
+
return {
|
|
6497
|
+
name: a.name,
|
|
6498
|
+
specPath: a.specPath,
|
|
6499
|
+
logDir: a.logDir,
|
|
6500
|
+
role: a.role ?? "both"
|
|
6501
|
+
};
|
|
6502
|
+
});
|
|
6503
|
+
}
|
|
6504
|
+
function computeHealth(diagnosis) {
|
|
6505
|
+
const concerns = diagnosis.patterns.filter((p) => p.severity === "concern").length;
|
|
6506
|
+
const warnings = diagnosis.patterns.filter((p) => p.severity === "warning").length;
|
|
6507
|
+
const score = 100 - concerns * 20 - warnings * 10;
|
|
6508
|
+
return Math.max(0, Math.min(100, score));
|
|
6509
|
+
}
|
|
6510
|
+
function pairAgents(agents, diagnoses, strategy) {
|
|
6511
|
+
if (agents.length < 2) return [];
|
|
6512
|
+
switch (strategy) {
|
|
6513
|
+
case "severity":
|
|
6514
|
+
return pairBySeverity(agents, diagnoses);
|
|
6515
|
+
case "round-robin":
|
|
6516
|
+
return pairRoundRobin(agents);
|
|
6517
|
+
case "complementary":
|
|
6518
|
+
return pairComplementary(agents, diagnoses);
|
|
6519
|
+
default:
|
|
6520
|
+
return pairBySeverity(agents, diagnoses);
|
|
6521
|
+
}
|
|
6522
|
+
}
|
|
6523
|
+
function pairBySeverity(agents, diagnoses) {
|
|
6524
|
+
const scored = agents.map((a) => ({
|
|
6525
|
+
agent: a,
|
|
6526
|
+
health: computeHealth(diagnoses.get(a.name) ?? { messagesAnalyzed: 0, assistantResponses: 0, patterns: [], healthy: [], timestamp: "" })
|
|
6527
|
+
})).sort((a, b) => b.health - a.health);
|
|
6528
|
+
const pairs = [];
|
|
6529
|
+
const paired = /* @__PURE__ */ new Set();
|
|
6530
|
+
let left = 0;
|
|
6531
|
+
let right = scored.length - 1;
|
|
6532
|
+
while (left < right) {
|
|
6533
|
+
if (paired.has(scored[left].agent.name) || scored[left].agent.role === "patient") {
|
|
6534
|
+
left++;
|
|
6535
|
+
continue;
|
|
6536
|
+
}
|
|
6537
|
+
if (paired.has(scored[right].agent.name) || scored[right].agent.role === "therapist") {
|
|
6538
|
+
right--;
|
|
6539
|
+
continue;
|
|
6540
|
+
}
|
|
6541
|
+
pairs.push({
|
|
6542
|
+
therapist: scored[left].agent,
|
|
6543
|
+
patient: scored[right].agent,
|
|
6544
|
+
reason: `${scored[left].agent.name} (health: ${scored[left].health}) treats ${scored[right].agent.name} (health: ${scored[right].health})`
|
|
6545
|
+
});
|
|
6546
|
+
paired.add(scored[left].agent.name);
|
|
6547
|
+
paired.add(scored[right].agent.name);
|
|
6548
|
+
left++;
|
|
6549
|
+
right--;
|
|
6550
|
+
}
|
|
6551
|
+
return pairs;
|
|
6552
|
+
}
|
|
6553
|
+
function pairRoundRobin(agents) {
|
|
6554
|
+
const pairs = [];
|
|
6555
|
+
for (let i = 0; i < agents.length; i++) {
|
|
6556
|
+
const therapist = agents[i];
|
|
6557
|
+
const patient = agents[(i + 1) % agents.length];
|
|
6558
|
+
if (therapist.name === patient.name) continue;
|
|
6559
|
+
if (therapist.role === "patient" || patient.role === "therapist") continue;
|
|
6560
|
+
pairs.push({
|
|
6561
|
+
therapist,
|
|
6562
|
+
patient,
|
|
6563
|
+
reason: `Round-robin: ${therapist.name} \u2192 ${patient.name}`
|
|
6564
|
+
});
|
|
6565
|
+
}
|
|
6566
|
+
return pairs;
|
|
6567
|
+
}
|
|
6568
|
+
function pairComplementary(agents, diagnoses) {
|
|
6569
|
+
const agentSpecs = /* @__PURE__ */ new Map();
|
|
6570
|
+
for (const agent of agents) {
|
|
6571
|
+
try {
|
|
6572
|
+
agentSpecs.set(agent.name, loadSpec(agent.specPath));
|
|
6573
|
+
} catch {
|
|
6574
|
+
}
|
|
6575
|
+
}
|
|
6576
|
+
if (agentSpecs.size < 2) {
|
|
6577
|
+
return pairBySeverity(agents, diagnoses);
|
|
6578
|
+
}
|
|
6579
|
+
const dimensions = ["openness", "conscientiousness", "extraversion", "agreeableness", "emotional_stability"];
|
|
6580
|
+
const pairs = [];
|
|
6581
|
+
const paired = /* @__PURE__ */ new Set();
|
|
6582
|
+
const scored = agents.filter((a) => agentSpecs.has(a.name)).map((a) => {
|
|
6583
|
+
const spec = agentSpecs.get(a.name);
|
|
6584
|
+
const avgScore = dimensions.reduce((sum, dim) => {
|
|
6585
|
+
return sum + (spec.big_five?.[dim]?.score ?? 0.5);
|
|
6586
|
+
}, 0) / dimensions.length;
|
|
6587
|
+
return { agent: a, avgScore };
|
|
6588
|
+
}).sort((a, b) => b.avgScore - a.avgScore);
|
|
6589
|
+
let left = 0;
|
|
6590
|
+
let right = scored.length - 1;
|
|
6591
|
+
while (left < right) {
|
|
6592
|
+
if (paired.has(scored[left].agent.name)) {
|
|
6593
|
+
left++;
|
|
6594
|
+
continue;
|
|
6595
|
+
}
|
|
6596
|
+
if (paired.has(scored[right].agent.name)) {
|
|
6597
|
+
right--;
|
|
6598
|
+
continue;
|
|
6599
|
+
}
|
|
6600
|
+
pairs.push({
|
|
6601
|
+
therapist: scored[left].agent,
|
|
6602
|
+
patient: scored[right].agent,
|
|
6603
|
+
reason: `Complementary: ${scored[left].agent.name} (avg: ${scored[left].avgScore.toFixed(2)}) treats ${scored[right].agent.name} (avg: ${scored[right].avgScore.toFixed(2)})`
|
|
6604
|
+
});
|
|
6605
|
+
paired.add(scored[left].agent.name);
|
|
6606
|
+
paired.add(scored[right].agent.name);
|
|
6607
|
+
left++;
|
|
6608
|
+
right--;
|
|
6609
|
+
}
|
|
6610
|
+
return pairs;
|
|
6611
|
+
}
|
|
6612
|
+
async function runNetwork(config, provider, callbacks) {
|
|
6613
|
+
const maxSessions = config.maxSessionsPerAgent ?? 3;
|
|
6614
|
+
const maxTurns = config.maxTurnsPerSession ?? 20;
|
|
6615
|
+
const oversight = config.oversight ?? DEFAULT_OVERSIGHT;
|
|
6616
|
+
let therapistSpec;
|
|
6617
|
+
if (config.therapistSpec) {
|
|
6618
|
+
therapistSpec = loadSpec(config.therapistSpec);
|
|
6619
|
+
} else {
|
|
6620
|
+
therapistSpec = THERAPIST_META_SPEC;
|
|
6621
|
+
}
|
|
6622
|
+
const agentSpecs = /* @__PURE__ */ new Map();
|
|
6623
|
+
const agentDiagnoses = /* @__PURE__ */ new Map();
|
|
6624
|
+
const agentMessages = /* @__PURE__ */ new Map();
|
|
6625
|
+
for (const agent of config.agents) {
|
|
6626
|
+
try {
|
|
6627
|
+
const spec = loadSpec(agent.specPath);
|
|
6628
|
+
agentSpecs.set(agent.name, spec);
|
|
6629
|
+
let messages = [];
|
|
6630
|
+
if (agent.logDir && existsSync9(agent.logDir)) {
|
|
6631
|
+
messages = loadAgentMessages(agent.logDir);
|
|
6632
|
+
}
|
|
6633
|
+
agentMessages.set(agent.name, messages);
|
|
6634
|
+
if (messages.length > 0) {
|
|
6635
|
+
const diagnosis = runDiagnosis(messages);
|
|
6636
|
+
agentDiagnoses.set(agent.name, diagnosis);
|
|
6637
|
+
} else {
|
|
6638
|
+
agentDiagnoses.set(agent.name, {
|
|
6639
|
+
messagesAnalyzed: 0,
|
|
6640
|
+
assistantResponses: 0,
|
|
6641
|
+
patterns: [],
|
|
6642
|
+
healthy: [],
|
|
6643
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6644
|
+
});
|
|
6645
|
+
}
|
|
6646
|
+
} catch {
|
|
6647
|
+
}
|
|
6648
|
+
}
|
|
6649
|
+
const validAgents = config.agents.filter((a) => agentSpecs.has(a.name));
|
|
6650
|
+
const pairs = pairAgents(validAgents, agentDiagnoses, config.pairing);
|
|
6651
|
+
for (const pair of pairs) {
|
|
6652
|
+
callbacks?.onPairingDecided?.(pair.therapist.name, pair.patient.name, pair.reason);
|
|
6653
|
+
}
|
|
6654
|
+
const sessions = [];
|
|
6655
|
+
let totalDPOPairs = 0;
|
|
6656
|
+
const improvement = /* @__PURE__ */ new Map();
|
|
6657
|
+
for (const pair of pairs) {
|
|
6658
|
+
const approval = checkApproval("network-therapy", oversight);
|
|
6659
|
+
if (!approval.approved && callbacks?.onApprovalNeeded) {
|
|
6660
|
+
const approved = await callbacks.onApprovalNeeded(
|
|
6661
|
+
`Run therapy: ${pair.therapist.name} \u2192 ${pair.patient.name}`
|
|
6662
|
+
);
|
|
6663
|
+
if (!approved) continue;
|
|
6664
|
+
}
|
|
6665
|
+
const patientSpec = agentSpecs.get(pair.patient.name);
|
|
6666
|
+
const patientMessages = agentMessages.get(pair.patient.name) ?? [];
|
|
6667
|
+
const patientDiagnosis = agentDiagnoses.get(pair.patient.name);
|
|
6668
|
+
if (!patientSpec) continue;
|
|
6669
|
+
const preDiagnosis = runPreSessionDiagnosis(patientMessages, patientSpec);
|
|
6670
|
+
const preHealth = computeHealth(patientDiagnosis ?? {
|
|
6671
|
+
messagesAnalyzed: 0,
|
|
6672
|
+
assistantResponses: 0,
|
|
6673
|
+
patterns: [],
|
|
6674
|
+
healthy: [],
|
|
6675
|
+
timestamp: ""
|
|
6676
|
+
});
|
|
6677
|
+
callbacks?.onSessionStart?.(pair.therapist.name, pair.patient.name);
|
|
6678
|
+
const transcript = await runTherapySession(
|
|
6679
|
+
patientSpec,
|
|
6680
|
+
preDiagnosis,
|
|
6681
|
+
provider,
|
|
6682
|
+
maxTurns,
|
|
6683
|
+
{
|
|
6684
|
+
silent: true,
|
|
6685
|
+
callbacks: {
|
|
6686
|
+
onThinking: callbacks?.onThinking
|
|
6687
|
+
}
|
|
6688
|
+
}
|
|
6689
|
+
);
|
|
6690
|
+
const dpoPairs = extractDPOPairs(transcript);
|
|
6691
|
+
totalDPOPairs += dpoPairs.length;
|
|
6692
|
+
saveTranscript(transcript, pair.patient.name);
|
|
6693
|
+
const postHealth = Math.min(100, preHealth + dpoPairs.length * 5);
|
|
6694
|
+
const session = {
|
|
6695
|
+
therapist: pair.therapist.name,
|
|
6696
|
+
patient: pair.patient.name,
|
|
6697
|
+
transcript,
|
|
6698
|
+
dpoPairsGenerated: dpoPairs.length,
|
|
6699
|
+
preHealth,
|
|
6700
|
+
postHealth
|
|
6701
|
+
};
|
|
6702
|
+
sessions.push(session);
|
|
6703
|
+
callbacks?.onSessionEnd?.(session);
|
|
6704
|
+
const existing = improvement.get(pair.patient.name);
|
|
6705
|
+
improvement.set(pair.patient.name, {
|
|
6706
|
+
before: existing?.before ?? preHealth,
|
|
6707
|
+
after: postHealth
|
|
6708
|
+
});
|
|
6709
|
+
emitBehavioralEvent({
|
|
6710
|
+
event_type: "network_therapy",
|
|
6711
|
+
agent: pair.patient.name,
|
|
6712
|
+
data: {
|
|
6713
|
+
therapist: pair.therapist.name,
|
|
6714
|
+
dpoPairs: dpoPairs.length,
|
|
6715
|
+
preHealth,
|
|
6716
|
+
postHealth,
|
|
6717
|
+
patterns: preDiagnosis.patterns.map((p) => p.id)
|
|
6718
|
+
},
|
|
6719
|
+
spec_hash: hashSpec2(patientSpec)
|
|
6720
|
+
});
|
|
6721
|
+
for (const dpoPair of dpoPairs) {
|
|
6722
|
+
emitBehavioralEvent({
|
|
6723
|
+
event_type: "dpo_pair",
|
|
6724
|
+
agent: pair.patient.name,
|
|
6725
|
+
data: {
|
|
6726
|
+
prompt: dpoPair.prompt.slice(0, 200),
|
|
6727
|
+
pattern: dpoPair.metadata.pattern,
|
|
6728
|
+
phase: dpoPair.metadata.phase,
|
|
6729
|
+
source: "network_therapy",
|
|
6730
|
+
therapist: pair.therapist.name
|
|
6731
|
+
},
|
|
6732
|
+
spec_hash: hashSpec2(patientSpec)
|
|
6733
|
+
});
|
|
6734
|
+
}
|
|
6735
|
+
}
|
|
6736
|
+
const converged = Array.from(improvement.values()).every(
|
|
6737
|
+
(imp) => imp.after >= (config.convergenceThreshold ?? 85)
|
|
6738
|
+
);
|
|
6739
|
+
return {
|
|
6740
|
+
sessions,
|
|
6741
|
+
totalDPOPairs,
|
|
6742
|
+
agentImprovement: improvement,
|
|
6743
|
+
converged
|
|
6744
|
+
};
|
|
6745
|
+
}
|
|
6746
|
+
function loadAgentMessages(logDir) {
|
|
6747
|
+
if (!existsSync9(logDir)) return [];
|
|
6748
|
+
const messages = [];
|
|
6749
|
+
try {
|
|
6750
|
+
const files = readdirSync5(logDir).filter(
|
|
6751
|
+
(f) => f.endsWith(".json") || f.endsWith(".jsonl")
|
|
6752
|
+
);
|
|
6753
|
+
for (const file of files.slice(0, 10)) {
|
|
6754
|
+
try {
|
|
6755
|
+
const raw = readFileSync10(join10(logDir, file), "utf-8");
|
|
6756
|
+
const data = JSON.parse(raw);
|
|
6757
|
+
const conversations = parseConversationLog(data);
|
|
6758
|
+
for (const conv of conversations) {
|
|
6759
|
+
messages.push(...conv.messages);
|
|
6760
|
+
}
|
|
6761
|
+
} catch {
|
|
6762
|
+
}
|
|
6763
|
+
}
|
|
6764
|
+
} catch {
|
|
6765
|
+
}
|
|
6766
|
+
return messages;
|
|
6767
|
+
}
|
|
6768
|
+
|
|
6769
|
+
// src/core/embodiment-sync.ts
|
|
6770
|
+
import { z as z4 } from "zod";
|
|
6771
|
+
var syncAnchorSchema = z4.enum([
|
|
6772
|
+
"speech_start",
|
|
6773
|
+
// gesture begins at start of utterance
|
|
6774
|
+
"speech_end",
|
|
6775
|
+
// gesture completes at end of utterance
|
|
6776
|
+
"emphasis",
|
|
6777
|
+
// gesture peaks at emphasized word
|
|
6778
|
+
"pause",
|
|
6779
|
+
// gesture fills conversational pause
|
|
6780
|
+
"turn_yield",
|
|
6781
|
+
// gesture accompanies turn-yielding
|
|
6782
|
+
"turn_take",
|
|
6783
|
+
// gesture accompanies turn-taking
|
|
6784
|
+
"free"
|
|
6785
|
+
// no speech coupling
|
|
6786
|
+
]);
|
|
6787
|
+
var syncRuleSchema = z4.object({
|
|
6788
|
+
gesture_id: z4.string(),
|
|
6789
|
+
anchor: syncAnchorSchema,
|
|
6790
|
+
lead_ms: z4.number().default(0),
|
|
6791
|
+
gaze_behavior: z4.enum(["at_listener", "at_referent", "away", "maintain"]).default("at_listener"),
|
|
6792
|
+
facial_action: z4.enum(["neutral", "smile", "concern", "thinking", "match_speech"]).default("match_speech")
|
|
6793
|
+
});
|
|
6794
|
+
var syncProfileSchema = z4.object({
|
|
6795
|
+
rules: z4.array(syncRuleSchema).default([]),
|
|
6796
|
+
default_gesture_lead_ms: z4.number().default(100),
|
|
6797
|
+
gaze_during_speech: z4.enum(["at_listener", "alternate", "at_referent"]).default("at_listener"),
|
|
6798
|
+
gaze_during_listen: z4.enum(["at_speaker", "nodding", "ambient"]).default("at_speaker"),
|
|
6799
|
+
blink_rate_per_min: z4.number().min(0).max(40).default(17),
|
|
6800
|
+
turn_taking_signals: z4.object({
|
|
6801
|
+
yield: z4.array(z4.string()).default(["gaze_to_listener", "open_palm", "lean_back"]),
|
|
6802
|
+
take: z4.array(z4.string()).default(["lean_forward", "inhale_gesture", "gaze_up"]),
|
|
6803
|
+
hold: z4.array(z4.string()).default(["filled_pause", "gaze_away", "hand_raise"])
|
|
6804
|
+
}).default({})
|
|
6805
|
+
});
|
|
5548
6806
|
export {
|
|
5549
6807
|
ARCHETYPES,
|
|
5550
6808
|
ATTACHMENT_STYLES,
|
|
5551
6809
|
AnthropicProvider,
|
|
5552
6810
|
BUILT_IN_DETECTORS,
|
|
5553
6811
|
CATEGORIES,
|
|
6812
|
+
DEFAULT_OVERSIGHT,
|
|
5554
6813
|
DIMENSIONS,
|
|
5555
6814
|
Guard,
|
|
5556
6815
|
LEARNING_ORIENTATIONS,
|
|
@@ -5558,23 +6817,35 @@ export {
|
|
|
5558
6817
|
OpenAIProvider,
|
|
5559
6818
|
PROVIDER_PARAMS,
|
|
5560
6819
|
SURFACE_MULTIPLIERS,
|
|
6820
|
+
THERAPIST_META_SPEC,
|
|
5561
6821
|
THERAPY_DIMENSIONS,
|
|
5562
6822
|
THERAPY_PHASES,
|
|
5563
6823
|
appendEvolution,
|
|
5564
6824
|
applyRecommendations,
|
|
5565
6825
|
bigFiveSchema,
|
|
6826
|
+
buildAgentTherapistPrompt,
|
|
5566
6827
|
buildPatientSystemPrompt,
|
|
5567
6828
|
buildTherapistSystemPrompt,
|
|
6829
|
+
checkApproval,
|
|
6830
|
+
checkIterationBudget,
|
|
5568
6831
|
communicationSchema,
|
|
5569
6832
|
compareBenchmarks,
|
|
5570
6833
|
compareIndex,
|
|
5571
6834
|
compile,
|
|
6835
|
+
compileEmbodied,
|
|
5572
6836
|
compileForOpenClaw,
|
|
5573
6837
|
compiledConfigSchema,
|
|
6838
|
+
compiledEmbodiedConfigSchema,
|
|
5574
6839
|
computeDimensionScore,
|
|
6840
|
+
computeGazePolicy,
|
|
6841
|
+
computeMotionParameters,
|
|
6842
|
+
computeProsody,
|
|
6843
|
+
computeProxemics,
|
|
6844
|
+
computeSyncProfile,
|
|
5575
6845
|
conversationLogSchema,
|
|
5576
6846
|
conversationSchema,
|
|
5577
6847
|
convertToHFFormat,
|
|
6848
|
+
corpusStats,
|
|
5578
6849
|
createGist,
|
|
5579
6850
|
createIndex,
|
|
5580
6851
|
createIndexEntry,
|
|
@@ -5589,15 +6860,21 @@ export {
|
|
|
5589
6860
|
detectSentiment,
|
|
5590
6861
|
detectVerbosity,
|
|
5591
6862
|
discoverAgents,
|
|
6863
|
+
discoverNetworkAgents,
|
|
5592
6864
|
domainSchema,
|
|
6865
|
+
embodimentSchema,
|
|
6866
|
+
emitBehavioralEvent,
|
|
5593
6867
|
evaluateOutcome,
|
|
5594
6868
|
exportTrainingData,
|
|
6869
|
+
expressionSchema,
|
|
5595
6870
|
extractAlpacaExamples,
|
|
5596
6871
|
extractDPOPairs,
|
|
6872
|
+
extractDPOPairsWithLLM,
|
|
5597
6873
|
extractRLHFExamples,
|
|
5598
6874
|
extractRecommendations,
|
|
5599
6875
|
fetchPersonality,
|
|
5600
6876
|
fetchRegistry,
|
|
6877
|
+
gazePolicySchema,
|
|
5601
6878
|
generateBenchmarkMarkdown,
|
|
5602
6879
|
generateComparisonMarkdown,
|
|
5603
6880
|
generateCredential,
|
|
@@ -5605,6 +6882,7 @@ export {
|
|
|
5605
6882
|
generatePrescriptions,
|
|
5606
6883
|
generateProgressReport,
|
|
5607
6884
|
generateSystemPrompt,
|
|
6885
|
+
gestureSchema,
|
|
5608
6886
|
getArchetype,
|
|
5609
6887
|
getArchetypesByCategory,
|
|
5610
6888
|
getBenchmarkScenarios,
|
|
@@ -5617,18 +6895,26 @@ export {
|
|
|
5617
6895
|
getTotalSignalCount,
|
|
5618
6896
|
growthAreaSchema,
|
|
5619
6897
|
growthSchema,
|
|
6898
|
+
hapticPolicySchema,
|
|
6899
|
+
hashSpec2 as hashSpec,
|
|
5620
6900
|
listArchetypeIds,
|
|
5621
6901
|
listDetectors,
|
|
5622
6902
|
listDetectorsByCategory,
|
|
5623
6903
|
listDetectorsByTag,
|
|
5624
6904
|
loadBenchmarkResults,
|
|
6905
|
+
loadCorpus,
|
|
5625
6906
|
loadEvolution,
|
|
5626
6907
|
loadFleetConfig,
|
|
5627
6908
|
loadLatestBenchmark,
|
|
6909
|
+
loadNetworkConfig,
|
|
5628
6910
|
loadSpec,
|
|
5629
6911
|
loadTranscripts,
|
|
5630
6912
|
loadTreatmentPlan,
|
|
5631
6913
|
messageSchema,
|
|
6914
|
+
modalitySchema,
|
|
6915
|
+
morphologySchema,
|
|
6916
|
+
motionParametersSchema,
|
|
6917
|
+
pairAgents,
|
|
5632
6918
|
parseAnthropicAPILog,
|
|
5633
6919
|
parseChatGPTExport,
|
|
5634
6920
|
parseClaudeExport,
|
|
@@ -5638,20 +6924,27 @@ export {
|
|
|
5638
6924
|
parseOTelGenAIExport,
|
|
5639
6925
|
parseOpenAIAPILog,
|
|
5640
6926
|
personalitySpecSchema,
|
|
6927
|
+
physicalSafetySchema,
|
|
6928
|
+
prescribeDPOPairs,
|
|
6929
|
+
prosodySchema,
|
|
5641
6930
|
providerSchema,
|
|
6931
|
+
proxemicZoneSchema,
|
|
5642
6932
|
pushToHFHub,
|
|
5643
6933
|
recordSessionOutcome,
|
|
5644
6934
|
registerBuiltInDetectors,
|
|
5645
6935
|
registerDetector,
|
|
5646
6936
|
resolveInheritance,
|
|
6937
|
+
resolveOversight,
|
|
5647
6938
|
runAssessment,
|
|
5648
6939
|
runAutopilot,
|
|
5649
6940
|
runBenchmark,
|
|
5650
6941
|
runDiagnosis,
|
|
5651
6942
|
runEvolve,
|
|
6943
|
+
runNetwork,
|
|
5652
6944
|
runPreSessionDiagnosis,
|
|
5653
6945
|
runSelfAudit,
|
|
5654
6946
|
runTherapySession,
|
|
6947
|
+
safetyEnvelopeSchema,
|
|
5655
6948
|
saveBenchmarkResult,
|
|
5656
6949
|
saveCredential,
|
|
5657
6950
|
saveTranscript,
|
|
@@ -5666,8 +6959,12 @@ export {
|
|
|
5666
6959
|
summarize,
|
|
5667
6960
|
summarizeTherapy,
|
|
5668
6961
|
surfaceSchema,
|
|
6962
|
+
syncAnchorSchema,
|
|
6963
|
+
syncProfileSchema,
|
|
6964
|
+
syncRuleSchema,
|
|
5669
6965
|
therapyDimensionsSchema,
|
|
5670
6966
|
therapyScoreLabel,
|
|
5671
6967
|
unregisterDetector,
|
|
5672
|
-
verifyCredential
|
|
6968
|
+
verifyCredential,
|
|
6969
|
+
wrapAgent
|
|
5673
6970
|
};
|