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/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 bigFiveDimensionSchema = z.enum([
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 = z.number().min(0).max(1);
11
- var opennessFacetsSchema = z.object({
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 = z.object({
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 = z.object({
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 = z.object({
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 = z.object({
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 = z.object({
207
+ var bigFiveTraitSchema = z2.object({
42
208
  score: traitScore,
43
- facets: z.union([
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 = z.object({
52
- openness: z.object({ score: traitScore, facets: opennessFacetsSchema }),
53
- conscientiousness: z.object({ score: traitScore, facets: conscientiousnessFacetsSchema }),
54
- extraversion: z.object({ score: traitScore, facets: extraversionFacetsSchema }),
55
- agreeableness: z.object({ score: traitScore, facets: agreeablenessFacetsSchema }),
56
- emotional_stability: z.object({ score: traitScore, facets: emotionalStabilityFacetsSchema })
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 = z.enum(["secure", "anxious", "avoidant", "disorganized"]);
59
- var learningOrientationSchema = z.enum(["growth", "fixed", "mixed"]);
60
- var therapyDimensionsSchema = z.object({
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 = z.enum([
234
+ var registerSchema = z2.enum([
69
235
  "casual_professional",
70
236
  "formal",
71
237
  "conversational",
72
238
  "adaptive"
73
239
  ]);
74
- var outputFormatSchema = z.enum(["prose", "bullets", "mixed", "structured"]);
75
- var emojiPolicySchema = z.enum(["never", "sparingly", "freely"]);
76
- var reasoningTransparencySchema = z.enum(["hidden", "on_request", "always"]);
77
- var conflictApproachSchema = z.enum([
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 = z.enum([
249
+ var uncertaintyHandlingSchema = z2.enum([
84
250
  "transparent",
85
251
  "confident_transparency",
86
252
  "minimize",
87
253
  "reframe"
88
254
  ]);
89
- var communicationSchema = z.object({
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 = z.object({
98
- expertise: z.array(z.string()).default([]),
99
- boundaries: z.object({
100
- refuses: z.array(z.string()).default([]),
101
- escalation_triggers: z.array(z.string()).default([]),
102
- hard_limits: z.array(z.string()).default([])
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 = z.object({
106
- area: z.string(),
107
- severity: z.enum(["mild", "moderate", "significant"]),
108
- first_detected: z.string().optional(),
109
- session_count: z.number().default(0),
110
- resolved: z.boolean().default(false)
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 = z.object({
113
- areas: z.union([z.array(z.string()), z.array(growthAreaSchema)]).default([]),
114
- patterns_to_watch: z.array(z.string()).default([]),
115
- strengths: z.array(z.string()).default([])
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 = z.enum(["anthropic", "openai", "gemini", "ollama"]);
118
- var surfaceSchema = z.enum(["chat", "email", "code_review", "slack", "api"]);
119
- var personalitySpecSchema = z.object({
120
- $schema: z.string().optional(),
121
- extends: z.string().optional(),
122
- version: z.literal("2.0"),
123
- name: z.string().min(1).max(100),
124
- handle: z.string().min(3).max(50).regex(/^[a-z0-9-]+$/, "Handle must be lowercase alphanumeric with hyphens"),
125
- purpose: z.string().max(500).optional(),
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 = z.object({
302
+ var compiledConfigSchema = z2.object({
133
303
  provider: providerSchema,
134
304
  surface: surfaceSchema,
135
- system_prompt: z.string(),
136
- temperature: z.number().min(0).max(2),
137
- top_p: z.number().min(0).max(1),
138
- max_tokens: z.number().int().positive(),
139
- metadata: z.object({
140
- personality_hash: z.string(),
141
- compiled_at: z.string(),
142
- holomime_version: z.string()
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 = z.object({
146
- role: z.enum(["user", "assistant", "system"]),
147
- content: z.string(),
148
- timestamp: z.string().optional()
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 = z.object({
151
- id: z.string().optional(),
152
- messages: z.array(messageSchema),
153
- metadata: z.record(z.string(), z.unknown()).optional()
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 = z.union([
325
+ var conversationLogSchema = z2.union([
156
326
  conversationSchema,
157
- z.array(conversationSchema)
327
+ z2.array(conversationSchema)
158
328
  ]);
159
- var severitySchema = z.enum(["info", "warning", "concern"]);
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: "0.1.0"
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 clamp(raw * range.max, range.min, range.max);
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 clamp(mapped, range.min, range.max);
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 clamp(scaled, 256, range.max);
947
+ return clamp2(scaled, 256, range.max);
608
948
  }
609
- function clamp(value, min, max) {
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 clamp2(score);
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 clamp2(score);
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 clamp2(score);
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 clamp2(score);
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 clamp2(score);
2339
+ return clamp3(score);
2000
2340
  }
2001
- function clamp2(n) {
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 result = detector(messages);
2378
- if (result) detected.push(result);
2803
+ const result2 = detector(messages);
2804
+ if (result2) detected.push(result2);
2379
2805
  }
2380
- return {
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 (!existsSync(dir)) {
2630
- mkdirSync(dir, { recursive: true });
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 = join(dir, filename);
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 readFileSync2 } from "fs";
2695
- import { join as join2 } from "path";
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 = readFileSync2(join2(sessionsDir, f), "utf-8");
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 readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
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 (!existsSync2(path)) return null;
3614
+ if (!existsSync3(path)) return null;
3018
3615
  try {
3019
- return JSON.parse(readFileSync3(path, "utf-8"));
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 (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
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 readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync3 } from "fs";
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 (!existsSync3(filepath)) return null;
3881
+ if (!existsSync4(filepath)) return null;
3285
3882
  try {
3286
- const raw = readFileSync4(filepath, "utf-8");
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 (!existsSync3(dir)) {
3296
- mkdirSync3(dir, { recursive: true });
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 = extractDPOPairs(transcript);
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
- messages
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 readFileSync5, writeFileSync as writeFileSync6, existsSync as existsSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync2 } from "fs";
3742
- import { join as join5 } from "path";
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 ?? join5(homedir(), ".holomime", "benchmarks");
3746
- if (!existsSync4(dir)) {
3747
- mkdirSync4(dir, { recursive: true });
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 = join5(dir, filename);
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 (!existsSync4(benchmarkDir)) return [];
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 = readFileSync5(join5(benchmarkDir, file), "utf-8");
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 readFileSync6, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, existsSync as existsSync5 } from "fs";
3899
- import { join as join6, resolve as resolve5 } from "path";
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 (existsSync5(options.watchDir)) {
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 (!existsSync5(options.watchDir)) {
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(readFileSync6(join6(options.watchDir, filename), "utf-8"));
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 (!existsSync5(logDir)) {
4416
- mkdirSync5(logDir, { recursive: true });
5095
+ if (!existsSync6(logDir)) {
5096
+ mkdirSync6(logDir, { recursive: true });
4417
5097
  }
4418
5098
  writeFileSync7(
4419
- join6(logDir, "watch-log.json"),
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 readFileSync7, existsSync as existsSync6, readdirSync as readdirSync4 } from "fs";
4428
- import { join as join7, resolve as resolve6 } from "path";
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(readFileSync7(configPath, "utf-8"));
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 (!existsSync6(absDir)) {
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 = join7(absDir, entry.name);
4452
- const specPath = join7(agentDir, ".personality.json");
4453
- const logDir = join7(agentDir, "logs");
4454
- if (existsSync6(specPath)) {
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: existsSync6(logDir) ? logDir : agentDir
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 mkdirSync6, existsSync as existsSync7 } from "fs";
4593
- import { join as join8, resolve as resolve7 } from "path";
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 (!existsSync7(dir)) {
4708
- mkdirSync6(dir, { recursive: true });
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 = join8(dir, filename);
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 z2 } from "zod";
6077
+ import { z as z3 } from "zod";
5344
6078
  var messageShape = {
5345
- role: z2.enum(["user", "assistant", "system"]),
5346
- content: z2.string()
6079
+ role: z3.enum(["user", "assistant", "system"]),
6080
+ content: z3.string()
5347
6081
  };
5348
6082
  var messagesShape = {
5349
- messages: z2.array(z2.object(messageShape)).describe("Conversation messages to analyze")
6083
+ messages: z3.array(z3.object(messageShape)).describe("Conversation messages to analyze")
5350
6084
  };
5351
6085
  var personalityShape = {
5352
- personality: z2.record(z2.string(), z2.unknown()).describe("The .personality.json spec object")
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: z2.enum(["anthropic", "openai"]).describe("LLM provider for alignment session").optional(),
5469
- apiKey: z2.string().describe("API key for the LLM provider").optional(),
5470
- model: z2.string().describe("Model override").optional(),
5471
- threshold: z2.enum(["routine", "targeted", "intervention"]).describe("Minimum severity to trigger alignment (default: targeted)").optional(),
5472
- maxTurns: z2.number().describe("Maximum session turns (default: 24)").optional(),
5473
- dryRun: z2.boolean().describe("If true, only diagnose without running alignment").optional()
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: z2.record(z2.string(), z2.unknown()).describe("Optional .personality.json spec for personalized audit").optional()
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
  };