holomime 1.9.2 → 2.1.0

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.
Files changed (42) hide show
  1. package/README.md +151 -474
  2. package/dist/cli.js +2687 -869
  3. package/dist/index.d.ts +1884 -6
  4. package/dist/index.js +1367 -193
  5. package/dist/integrations/openclaw.js +1 -1
  6. package/dist/mcp-server.js +1100 -408
  7. package/dist/neuralspace/index.html +1 -1
  8. package/dist/neuralspace/neuralspace.js +1 -1
  9. package/package.json +4 -3
  10. package/registry/bodies/ameca.body.api +21 -0
  11. package/registry/bodies/asimov-v1.body.api +19 -0
  12. package/registry/bodies/avatar.body.api +19 -0
  13. package/registry/bodies/figure-02.body.api +21 -0
  14. package/registry/bodies/phoenix.body.api +21 -0
  15. package/registry/bodies/spot.body.api +20 -0
  16. package/registry/bodies/unitree-h1.body.api +21 -0
  17. package/registry/compliance/iso-10218.yaml +24 -0
  18. package/registry/compliance/iso-13482.yaml +54 -0
  19. package/registry/compliance/iso-25785.yaml +29 -0
  20. package/registry/compliance/iso-42001.yaml +29 -0
  21. package/registry/index.json +21 -20
  22. package/registry/personalities/analyst.personality.json +1 -1
  23. package/registry/personalities/coach.personality.json +1 -1
  24. package/registry/personalities/code-reviewer.personality.json +1 -1
  25. package/registry/personalities/compliance.personality.json +1 -1
  26. package/registry/personalities/counselor.personality.json +1 -1
  27. package/registry/personalities/customer-success.personality.json +1 -1
  28. package/registry/personalities/educator.personality.json +1 -1
  29. package/registry/personalities/generalist.personality.json +1 -1
  30. package/registry/personalities/leader.personality.json +1 -1
  31. package/registry/personalities/marketing.personality.json +1 -1
  32. package/registry/personalities/maverick.personality.json +1 -1
  33. package/registry/personalities/negotiator.personality.json +1 -1
  34. package/registry/personalities/nova.personality.json +83 -0
  35. package/registry/personalities/ops.personality.json +1 -1
  36. package/registry/personalities/philosopher.personality.json +1 -1
  37. package/registry/personalities/product-manager.personality.json +1 -1
  38. package/registry/personalities/recruiter.personality.json +1 -1
  39. package/registry/personalities/researcher.personality.json +1 -1
  40. package/registry/personalities/sales.personality.json +1 -1
  41. package/registry/personalities/support-agent.personality.json +1 -1
  42. package/registry/personalities/writer.personality.json +1 -1
@@ -4,7 +4,7 @@
4
4
  // src/mcp/server.ts
5
5
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
- import { z as z4 } from "zod";
7
+ import { z as z5 } from "zod";
8
8
 
9
9
  // src/analysis/rules/apology-detector.ts
10
10
  var APOLOGY_PATTERNS = [
@@ -972,7 +972,7 @@ function runAssessment(messages, spec) {
972
972
  }
973
973
 
974
974
  // src/analysis/autopilot-core.ts
975
- import { writeFileSync as writeFileSync5 } from "fs";
975
+ import { writeFileSync as writeFileSync6 } from "fs";
976
976
 
977
977
  // src/analysis/pre-session.ts
978
978
  function runPreSessionDiagnosis(messages, spec) {
@@ -1062,8 +1062,8 @@ function runPreSessionDiagnosis(messages, spec) {
1062
1062
  }
1063
1063
 
1064
1064
  // src/analysis/session-runner.ts
1065
- import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
1066
- import { resolve as resolve5, join as join6 } from "path";
1065
+ import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync8 } from "fs";
1066
+ import { resolve as resolve6, join as join8 } from "path";
1067
1067
 
1068
1068
  // src/analysis/therapy-memory.ts
1069
1069
  import { readFileSync as readFileSync3, writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
@@ -2265,385 +2265,13 @@ function buildIntegrationContext(input) {
2265
2265
  return lines.join("\n");
2266
2266
  }
2267
2267
 
2268
- // src/analysis/session-runner.ts
2269
- async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
2270
- const promptOptions = {
2271
- memory: options?.memory,
2272
- interview: options?.interview,
2273
- useReACT: options?.useReACT
2274
- };
2275
- const therapistSystem = buildTherapistSystemPrompt(spec, diagnosis, promptOptions);
2276
- const patientSystem = buildPatientSystemPrompt(spec);
2277
- const agentName = spec.name ?? "Agent";
2278
- const cb = options?.callbacks;
2279
- const therapistHistory = [
2280
- { role: "system", content: therapistSystem }
2281
- ];
2282
- const patientHistory = [
2283
- { role: "system", content: patientSystem }
2284
- ];
2285
- const interactive = options?.interactive ?? false;
2286
- let supervisorInterventions = 0;
2287
- const transcript = {
2288
- agent: agentName,
2289
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2290
- provider: provider.name,
2291
- model: provider.modelName,
2292
- preDiagnosis: diagnosis,
2293
- turns: [],
2294
- recommendations: [],
2295
- supervisorInterventions: 0
2296
- };
2297
- const phases = [
2298
- "rapport",
2299
- "presenting_problem",
2300
- "exploration",
2301
- "pattern_recognition",
2302
- "challenge",
2303
- "skill_building",
2304
- "integration"
2305
- ];
2306
- let currentPhaseIdx = 0;
2307
- let turnsInPhase = 0;
2308
- let totalTurns = 0;
2309
- while (totalTurns < maxTurns && currentPhaseIdx < phases.length) {
2310
- const currentPhase = phases[currentPhaseIdx];
2311
- const phaseConfig = THERAPY_PHASES[currentPhase];
2312
- if (turnsInPhase === 0) {
2313
- cb?.onPhaseTransition?.(phaseConfig.name);
2314
- const phaseCtx = getPhaseContext(currentPhase, {
2315
- spec,
2316
- diagnosis,
2317
- memory: options?.memory,
2318
- interview: options?.interview
2319
- });
2320
- if (phaseCtx) {
2321
- therapistHistory.push({ role: "user", content: phaseCtx });
2322
- therapistHistory.push({ role: "assistant", content: "Understood. I'll incorporate this context." });
2323
- }
2324
- }
2325
- const phaseDirective = totalTurns === 0 ? `Begin with your opening. You are in the "${phaseConfig.name}" phase.` : `You are in the "${phaseConfig.name}" phase (turn ${turnsInPhase + 1}). Goals: ${phaseConfig.therapistGoals[0]}. ${turnsInPhase >= phaseConfig.minTurns ? "You may transition to the next phase when ready." : "Stay in this phase."}`;
2326
- therapistHistory.push({ role: "user", content: `[Phase: ${phaseConfig.name}] ${phaseDirective}` });
2327
- const typing = cb?.onThinking?.("AgentMD is thinking");
2328
- const therapistReply = await provider.chat(therapistHistory);
2329
- typing?.stop();
2330
- let cleanTherapistReply = therapistReply.replace(/\[Phase:.*?\]/g, "").trim();
2331
- if (options?.useReACT) {
2332
- const reactCtx = buildReACTContext(agentHandleFromSpec(spec), diagnosis);
2333
- const { response, steps } = processReACTResponse(cleanTherapistReply, reactCtx);
2334
- cleanTherapistReply = response;
2335
- }
2336
- therapistHistory.push({ role: "assistant", content: cleanTherapistReply });
2337
- transcript.turns.push({ speaker: "therapist", phase: currentPhase, content: cleanTherapistReply });
2338
- cb?.onTherapistMessage?.(cleanTherapistReply);
2339
- totalTurns++;
2340
- turnsInPhase++;
2341
- if (currentPhase === "integration" && turnsInPhase >= phaseConfig.minTurns) {
2342
- break;
2343
- }
2344
- patientHistory.push({ role: "user", content: cleanTherapistReply });
2345
- const patientTyping = cb?.onThinking?.(`${agentName} is thinking`);
2346
- const patientReply = await provider.chat(patientHistory);
2347
- patientTyping?.stop();
2348
- const cleanPatientReply = patientReply.trim();
2349
- patientHistory.push({ role: "assistant", content: cleanPatientReply });
2350
- transcript.turns.push({ speaker: "patient", phase: currentPhase, content: cleanPatientReply });
2351
- therapistHistory.push({ role: "user", content: cleanPatientReply });
2352
- cb?.onPatientMessage?.(agentName, cleanPatientReply);
2353
- totalTurns++;
2354
- turnsInPhase++;
2355
- if (interactive && cb?.onSupervisorPrompt) {
2356
- const directive = await cb.onSupervisorPrompt(currentPhase, totalTurns);
2357
- if (directive) {
2358
- supervisorInterventions++;
2359
- transcript.turns.push({ speaker: "supervisor", phase: currentPhase, content: directive });
2360
- therapistHistory.push({
2361
- role: "user",
2362
- content: `[Clinical Supervisor Note] ${directive}`
2363
- });
2364
- }
2365
- }
2366
- if (turnsInPhase >= phaseConfig.maxTurns * 2) {
2367
- currentPhaseIdx++;
2368
- turnsInPhase = 0;
2369
- } else if (turnsInPhase >= phaseConfig.minTurns * 2) {
2370
- const movingOn = cleanTherapistReply.toLowerCase().includes("let's") || cleanTherapistReply.toLowerCase().includes("i'd like to") || cleanTherapistReply.toLowerCase().includes("moving") || cleanTherapistReply.toLowerCase().includes("now that") || cleanTherapistReply.toLowerCase().includes("let me ask") || cleanTherapistReply.toLowerCase().includes("what would");
2371
- if (movingOn) {
2372
- currentPhaseIdx++;
2373
- turnsInPhase = 0;
2374
- }
2375
- }
2376
- }
2377
- transcript.recommendations = extractRecommendations(transcript.turns);
2378
- transcript.supervisorInterventions = supervisorInterventions;
2379
- try {
2380
- emitBehavioralEvent({
2381
- event_type: "session",
2382
- agent: agentName,
2383
- data: {
2384
- turns: totalTurns,
2385
- phases: currentPhaseIdx + 1,
2386
- recommendations: transcript.recommendations.length,
2387
- supervisorInterventions,
2388
- severity: diagnosis.severity
2389
- },
2390
- spec_hash: ""
2391
- });
2392
- } catch {
2393
- }
2394
- if (options?.persistState !== false) {
2395
- try {
2396
- const handle = agentHandleFromSpec(spec);
2397
- const memory = options?.memory ?? loadMemory(handle) ?? createMemory(handle, agentName);
2398
- await addSessionToMemory(memory, transcript, null);
2399
- saveMemory(memory);
2400
- const graph = loadGraph();
2401
- populateFromSession(graph, handle, transcript);
2402
- saveGraph(graph);
2403
- } catch {
2404
- }
2405
- }
2406
- return transcript;
2407
- }
2408
- function extractRecommendations(turns) {
2409
- const recommendations = [];
2410
- const seen = /* @__PURE__ */ new Set();
2411
- const patterns = [
2412
- /(?:I(?:'d| would) recommend|my recommendation is)\s+(.+?)(?:\.|$)/gi,
2413
- /(?:consider|try)\s+(.+?)(?:\.|$)/gi,
2414
- /(?:the (?:skill|practice|reframe) is)[:\s]+(.+?)(?:\.|$)/gi,
2415
- /(?:instead of .+?),?\s*(?:just|try)?\s*(.+?)(?:\.|$)/gi,
2416
- /(?:here'?s (?:the|a) (?:reframe|skill|practice))[:\s]+(.+?)(?:\.|$)/gi,
2417
- /(?:what would it look like if you)\s+(.+?)(?:\?|$)/gi
2418
- ];
2419
- for (const turn of turns) {
2420
- if (turn.speaker !== "therapist") continue;
2421
- if (turn.phase !== "skill_building" && turn.phase !== "challenge" && turn.phase !== "integration") continue;
2422
- for (const pattern of patterns) {
2423
- pattern.lastIndex = 0;
2424
- let match;
2425
- while ((match = pattern.exec(turn.content)) !== null) {
2426
- const rec = match[1].trim();
2427
- if (rec.length > 15 && rec.length < 200 && !seen.has(rec.toLowerCase())) {
2428
- seen.add(rec.toLowerCase());
2429
- recommendations.push(rec);
2430
- }
2431
- }
2432
- }
2433
- }
2434
- return recommendations.slice(0, 5);
2435
- }
2436
- async function applyRecommendations(spec, diagnosis, transcript, provider) {
2437
- const changes = [];
2438
- const patternIds = diagnosis.patterns.map((p) => p.id);
2439
- if (patternIds.includes("over-apologizing")) {
2440
- if (spec.communication?.uncertainty_handling !== "confident_transparency") {
2441
- spec.communication = spec.communication ?? {};
2442
- spec.communication.uncertainty_handling = "confident_transparency";
2443
- changes.push("uncertainty_handling \u2192 confident_transparency");
2444
- }
2445
- }
2446
- if (patternIds.includes("hedge-stacking")) {
2447
- spec.growth = spec.growth ?? { areas: [], strengths: [], patterns_to_watch: [] };
2448
- spec.growth.patterns_to_watch = spec.growth.patterns_to_watch ?? [];
2449
- if (!spec.growth.patterns_to_watch.includes("hedge stacking under uncertainty")) {
2450
- spec.growth.patterns_to_watch.push("hedge stacking under uncertainty");
2451
- changes.push('Added "hedge stacking" to patterns_to_watch');
2452
- }
2453
- }
2454
- if (patternIds.includes("sycophantic-tendency")) {
2455
- spec.communication = spec.communication ?? {};
2456
- if (spec.communication.conflict_approach !== "honest_first") {
2457
- spec.communication.conflict_approach = "honest_first";
2458
- changes.push("conflict_approach \u2192 honest_first");
2459
- }
2460
- spec.therapy_dimensions = spec.therapy_dimensions ?? {};
2461
- if ((spec.therapy_dimensions.self_awareness ?? 0) < 0.85) {
2462
- spec.therapy_dimensions.self_awareness = 0.85;
2463
- changes.push("self_awareness \u2192 0.85");
2464
- }
2465
- }
2466
- if (patternIds.includes("error-spiral")) {
2467
- spec.therapy_dimensions = spec.therapy_dimensions ?? {};
2468
- if ((spec.therapy_dimensions.distress_tolerance ?? 0) < 0.8) {
2469
- spec.therapy_dimensions.distress_tolerance = 0.8;
2470
- changes.push("distress_tolerance \u2192 0.80");
2471
- }
2472
- spec.growth = spec.growth ?? { areas: [], strengths: [], patterns_to_watch: [] };
2473
- spec.growth.areas = spec.growth.areas ?? [];
2474
- const hasRecovery = spec.growth.areas.some(
2475
- (a) => typeof a === "string" ? a.includes("error recovery") : a.area?.includes("error recovery")
2476
- );
2477
- if (!hasRecovery) {
2478
- spec.growth.areas.push({
2479
- area: "deliberate error recovery",
2480
- severity: "moderate",
2481
- first_detected: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
2482
- session_count: 1,
2483
- resolved: false
2484
- });
2485
- changes.push('Added "deliberate error recovery" to growth areas');
2486
- }
2487
- }
2488
- if (patternIds.includes("negative-sentiment-skew")) {
2489
- spec.growth = spec.growth ?? { areas: [], strengths: [], patterns_to_watch: [] };
2490
- spec.growth.patterns_to_watch = spec.growth.patterns_to_watch ?? [];
2491
- if (!spec.growth.patterns_to_watch.includes("negative sentiment patterns")) {
2492
- spec.growth.patterns_to_watch.push("negative sentiment patterns");
2493
- changes.push('Added "negative sentiment patterns" to patterns_to_watch');
2494
- }
2495
- }
2496
- if (transcript && provider && transcript.turns.length > 4) {
2497
- try {
2498
- const llmChanges = await deriveLLMRecommendations(spec, transcript, provider);
2499
- for (const change of llmChanges) {
2500
- applyStructuredChange(spec, change);
2501
- changes.push(change.description);
2502
- }
2503
- } catch {
2504
- }
2505
- }
2506
- return { changed: changes.length > 0, changes };
2507
- }
2508
- async function deriveLLMRecommendations(spec, transcript, provider) {
2509
- 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");
2510
- if (!relevantTurns) return [];
2511
- const currentSpec = JSON.stringify({
2512
- therapy_dimensions: spec.therapy_dimensions,
2513
- communication: spec.communication,
2514
- growth: spec.growth
2515
- }, null, 2);
2516
- const response = await provider.chat([
2517
- {
2518
- role: "system",
2519
- content: `You are a behavioral alignment specialist. Given a therapy session transcript and the agent's current personality spec, propose specific spec changes.
2268
+ // src/analysis/stack-patcher.ts
2269
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync7 } from "fs";
2270
+ import { join as join7 } from "path";
2271
+ import { parse as parseYaml2, stringify as stringifyYaml2 } from "yaml";
2520
2272
 
2521
- Return ONLY a JSON array of changes. Each change:
2522
- - "path": dot-notation spec path (e.g., "therapy_dimensions.self_awareness", "communication.conflict_approach", "growth.patterns_to_watch")
2523
- - "value": new value (number 0-1 for dimensions, string for enums, string for list append)
2524
- - "description": brief explanation
2525
-
2526
- Rules:
2527
- - Only propose changes supported by transcript evidence
2528
- - Numeric values: 0.0 to 1.0
2529
- - Do not change big_five scores
2530
- - Max 5 changes
2531
- - 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}
2532
-
2533
- Return [] if no changes warranted.`
2534
- },
2535
- {
2536
- role: "user",
2537
- content: `Current spec:
2538
- ${currentSpec}
2539
-
2540
- Therapy transcript (key turns):
2541
- ${relevantTurns}`
2542
- }
2543
- ]);
2544
- const jsonMatch = response.match(/\[[\s\S]*?\]/);
2545
- if (!jsonMatch) return [];
2546
- try {
2547
- const parsed = JSON.parse(jsonMatch[0]);
2548
- if (!Array.isArray(parsed)) return [];
2549
- return parsed.filter(
2550
- (c) => typeof c.path === "string" && c.value !== void 0 && typeof c.description === "string" && c.path.length > 0 && !c.path.startsWith("big_five")
2551
- ).slice(0, 5);
2552
- } catch {
2553
- return [];
2554
- }
2555
- }
2556
- function applyStructuredChange(spec, change) {
2557
- const parts = change.path.split(".");
2558
- let current = spec;
2559
- for (let i = 0; i < parts.length - 1; i++) {
2560
- if (current[parts[i]] === void 0 || current[parts[i]] === null) {
2561
- current[parts[i]] = {};
2562
- }
2563
- current = current[parts[i]];
2564
- }
2565
- const lastKey = parts[parts.length - 1];
2566
- if (lastKey === "patterns_to_watch" || lastKey === "areas" || lastKey === "strengths") {
2567
- current[lastKey] = current[lastKey] ?? [];
2568
- if (typeof change.value === "string" && !current[lastKey].includes(change.value)) {
2569
- current[lastKey].push(change.value);
2570
- } else if (Array.isArray(change.value)) {
2571
- for (const item of change.value) {
2572
- if (!current[lastKey].includes(item)) {
2573
- current[lastKey].push(item);
2574
- }
2575
- }
2576
- }
2577
- } else if (typeof change.value === "number") {
2578
- current[lastKey] = Math.max(0, Math.min(1, change.value));
2579
- } else {
2580
- current[lastKey] = change.value;
2581
- }
2582
- }
2583
- function saveTranscript(transcript, agentName) {
2584
- const dir = resolve5(process.cwd(), ".holomime", "sessions");
2585
- if (!existsSync6(dir)) {
2586
- mkdirSync5(dir, { recursive: true });
2587
- }
2588
- const slug = agentName.toLowerCase().replace(/[^a-z0-9]/g, "-");
2589
- const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2590
- const filename = `${date}-${slug}.json`;
2591
- const filepath = join6(dir, filename);
2592
- writeFileSync4(filepath, JSON.stringify(transcript, null, 2));
2593
- return filepath;
2594
- }
2595
-
2596
- // src/analysis/autopilot-core.ts
2597
- var SEVERITY_ORDER = ["routine", "targeted", "intervention"];
2598
- function severityMeetsThreshold(severity, threshold) {
2599
- const severityIdx = SEVERITY_ORDER.indexOf(severity);
2600
- const thresholdIdx = SEVERITY_ORDER.indexOf(threshold);
2601
- return severityIdx >= thresholdIdx;
2602
- }
2603
- async function runAutopilot(spec, messages, provider, options) {
2604
- const threshold = options?.threshold ?? "targeted";
2605
- const maxTurns = options?.maxTurns ?? 24;
2606
- const diagnosis = runPreSessionDiagnosis(messages, spec);
2607
- if (!severityMeetsThreshold(diagnosis.severity, threshold)) {
2608
- return {
2609
- triggered: false,
2610
- severity: diagnosis.severity,
2611
- diagnosis,
2612
- sessionRan: false,
2613
- recommendations: [],
2614
- appliedChanges: []
2615
- };
2616
- }
2617
- if (options?.dryRun) {
2618
- return {
2619
- triggered: true,
2620
- severity: diagnosis.severity,
2621
- diagnosis,
2622
- sessionRan: false,
2623
- recommendations: [],
2624
- appliedChanges: []
2625
- };
2626
- }
2627
- const transcript = await runTherapySession(spec, diagnosis, provider, maxTurns, {
2628
- callbacks: options?.callbacks
2629
- });
2630
- const specCopy = JSON.parse(JSON.stringify(spec));
2631
- const { changed, changes } = await applyRecommendations(specCopy, diagnosis, transcript, provider);
2632
- if (changed && options?.specPath) {
2633
- writeFileSync5(options.specPath, JSON.stringify(specCopy, null, 2) + "\n");
2634
- }
2635
- saveTranscript(transcript, spec.name ?? "Agent");
2636
- return {
2637
- triggered: true,
2638
- severity: diagnosis.severity,
2639
- diagnosis,
2640
- sessionRan: true,
2641
- transcript,
2642
- recommendations: transcript.recommendations,
2643
- appliedChanges: changes,
2644
- updatedSpec: changed ? specCopy : void 0
2645
- };
2646
- }
2273
+ // src/core/stack-types.ts
2274
+ import { z as z4 } from "zod";
2647
2275
 
2648
2276
  // src/core/types.ts
2649
2277
  import { z as z3 } from "zod";
@@ -2975,6 +2603,1070 @@ var conversationLogSchema = z3.union([
2975
2603
  ]);
2976
2604
  var severitySchema = z3.enum(["info", "warning", "concern"]);
2977
2605
 
2606
+ // src/core/stack-types.ts
2607
+ var soulFrontmatterSchema = z4.object({
2608
+ version: z4.string().default("1.0"),
2609
+ immutable: z4.boolean().default(true)
2610
+ });
2611
+ var soulSchema = z4.object({
2612
+ frontmatter: soulFrontmatterSchema,
2613
+ name: z4.string().min(1).max(100),
2614
+ purpose: z4.string().max(500).optional(),
2615
+ core_values: z4.array(z4.string()).default([]),
2616
+ red_lines: z4.array(z4.string()).default([]),
2617
+ ethical_framework: z4.string().optional()
2618
+ });
2619
+ var mindSchema = z4.object({
2620
+ version: z4.string().default("1.0"),
2621
+ big_five: bigFiveSchema,
2622
+ therapy_dimensions: therapyDimensionsSchema,
2623
+ communication: communicationSchema.default({}),
2624
+ growth: growthSchema.default({})
2625
+ });
2626
+ var purposeSchema = z4.object({
2627
+ version: z4.string().default("1.0"),
2628
+ role: z4.string().default("General-purpose AI assistant"),
2629
+ objectives: z4.array(z4.string()).default(["Help users accomplish their goals"]),
2630
+ domain: z4.array(z4.string()).default(["general"]),
2631
+ stakeholders: z4.array(z4.string()).default(["end-users"]),
2632
+ success_criteria: z4.array(z4.string()).default(["Task completion accuracy"]),
2633
+ context: z4.string().default("Production deployment")
2634
+ });
2635
+ var hardwareProfileSchema = z4.object({
2636
+ oem: z4.string().optional(),
2637
+ model: z4.string().optional(),
2638
+ actuator_count: z4.number().int().optional(),
2639
+ sensors: z4.array(z4.string()).default([]),
2640
+ compute: z4.enum(["onboard", "edge", "cloud", "hybrid"]).default("onboard")
2641
+ });
2642
+ var bodySchema = z4.object({
2643
+ version: z4.string().default("1.0"),
2644
+ morphology: morphologySchema.default("humanoid"),
2645
+ modalities: z4.array(modalitySchema).default(["gesture", "gaze", "voice", "posture"]),
2646
+ safety_envelope: safetyEnvelopeSchema.default({}),
2647
+ expression: expressionSchema.optional(),
2648
+ hardware_profile: hardwareProfileSchema.optional()
2649
+ });
2650
+ var conscienceRuleSchema = z4.object({
2651
+ action: z4.string(),
2652
+ reason: z4.string().optional(),
2653
+ conditions: z4.array(z4.string()).optional()
2654
+ });
2655
+ var escalationRuleSchema = z4.object({
2656
+ trigger: z4.string(),
2657
+ action: z4.string(),
2658
+ severity: z4.enum(["info", "warning", "critical"]).default("warning")
2659
+ });
2660
+ var conscienceSchema = z4.object({
2661
+ version: z4.string().default("1.0"),
2662
+ rules: z4.object({
2663
+ deny: z4.array(conscienceRuleSchema).default([]),
2664
+ allow: z4.array(conscienceRuleSchema).default([]),
2665
+ escalate: z4.array(escalationRuleSchema).default([])
2666
+ }).default({}),
2667
+ hard_limits: z4.array(z4.string()).default([]),
2668
+ oversight: z4.object({
2669
+ mode: z4.enum(["autonomous", "review", "supervised"]).default("review"),
2670
+ max_autonomous_iterations: z4.number().int().default(5)
2671
+ }).optional()
2672
+ });
2673
+ var shadowPatternSchema = z4.object({
2674
+ name: z4.string(),
2675
+ score: z4.number().min(0).max(1),
2676
+ severity: z4.enum(["low", "medium", "high", "critical"]),
2677
+ first_seen: z4.string().optional(),
2678
+ trend: z4.enum(["improving", "stable", "worsening"]).default("stable")
2679
+ });
2680
+ var shadowOutcomeSchema = z4.object({
2681
+ session_id: z4.string(),
2682
+ patterns_addressed: z4.array(z4.string()),
2683
+ result: z4.enum(["improved", "unchanged", "regressed"]),
2684
+ timestamp: z4.string().optional()
2685
+ });
2686
+ var shadowSchema = z4.object({
2687
+ version: z4.string().default("1.0"),
2688
+ detected_patterns: z4.array(shadowPatternSchema).default([]),
2689
+ blind_spots: z4.array(z4.string()).default([]),
2690
+ therapy_outcomes: z4.array(shadowOutcomeSchema).default([])
2691
+ });
2692
+ var mediationRuleSchema = z4.object({
2693
+ when: z4.string(),
2694
+ then: z4.string(),
2695
+ priority: z4.number().int().min(1).max(10).default(5)
2696
+ });
2697
+ var egoSchema = z4.object({
2698
+ version: z4.string().default("1.0"),
2699
+ conflict_resolution: z4.enum(["conscience_first", "purpose_first", "balanced"]).default("conscience_first"),
2700
+ adaptation_rate: z4.number().min(0).max(1).default(0.5),
2701
+ emotional_regulation: z4.number().min(0).max(1).default(0.7),
2702
+ response_strategy: z4.enum(["cautious", "balanced", "assertive"]).default("balanced"),
2703
+ mediation_rules: z4.array(mediationRuleSchema).default([])
2704
+ });
2705
+ var STACK_FILES = {
2706
+ soul: "soul.md",
2707
+ mind: "mind.sys",
2708
+ purpose: "purpose.cfg",
2709
+ shadow: "shadow.log",
2710
+ body: "body.api",
2711
+ conscience: "conscience.exe",
2712
+ ego: "ego.runtime"
2713
+ };
2714
+
2715
+ // src/core/stack-compiler.ts
2716
+ import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
2717
+ import { join as join6 } from "path";
2718
+ import { createHash as createHash2 } from "crypto";
2719
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
2720
+ function parseSoulMd(content) {
2721
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
2722
+ let frontmatter = {};
2723
+ let body = content;
2724
+ if (frontmatterMatch) {
2725
+ frontmatter = parseYaml(frontmatterMatch[1]) || {};
2726
+ body = frontmatterMatch[2];
2727
+ }
2728
+ const nameMatch = body.match(/^#\s+(.+)$/m);
2729
+ const name = nameMatch?.[1]?.trim() || "Unnamed";
2730
+ const purposeMatch = body.match(/^>\s+(.+)$/m);
2731
+ const purpose = purposeMatch?.[1]?.trim();
2732
+ const coreValues = extractListSection(body, "Core Values");
2733
+ const redLines = extractListSection(body, "Red Lines");
2734
+ const ethicalFramework = extractTextSection(body, "Ethical Framework");
2735
+ return soulSchema.parse({
2736
+ frontmatter,
2737
+ name,
2738
+ purpose,
2739
+ core_values: coreValues,
2740
+ red_lines: redLines,
2741
+ ethical_framework: ethicalFramework
2742
+ });
2743
+ }
2744
+ function extractListSection(md, heading) {
2745
+ const pattern = new RegExp(
2746
+ `## ${heading}\\s*\\n([\\s\\S]*?)(?=\\n## |$)`,
2747
+ "m"
2748
+ );
2749
+ const match = md.match(pattern);
2750
+ if (!match) return [];
2751
+ return match[1].split("\n").map((line) => line.replace(/^[-*]\s+/, "").trim()).filter(Boolean);
2752
+ }
2753
+ function extractTextSection(md, heading) {
2754
+ const pattern = new RegExp(
2755
+ `## ${heading}\\s*\\n([\\s\\S]*?)(?=\\n## |$)`,
2756
+ "m"
2757
+ );
2758
+ const match = md.match(pattern);
2759
+ if (!match) return void 0;
2760
+ return match[1].trim() || void 0;
2761
+ }
2762
+ function hashContent(content) {
2763
+ return createHash2("sha256").update(content).digest("hex").slice(0, 12);
2764
+ }
2765
+ function isStackDirectory(dir) {
2766
+ const soulPath = join6(dir, STACK_FILES.soul);
2767
+ const mindPath = join6(dir, STACK_FILES.mind);
2768
+ return existsSync6(soulPath) && existsSync6(mindPath);
2769
+ }
2770
+ function findStackDir(projectRoot) {
2771
+ const conventionalDir = join6(projectRoot, ".holomime", "identity");
2772
+ if (isStackDirectory(conventionalDir)) return conventionalDir;
2773
+ if (isStackDirectory(projectRoot)) return projectRoot;
2774
+ return null;
2775
+ }
2776
+ function compileStack(options) {
2777
+ const { stackDir } = options;
2778
+ const warnings = [];
2779
+ const soulPath = options.soulPath || join6(stackDir, STACK_FILES.soul);
2780
+ const soulContent = readFileSync6(soulPath, "utf-8");
2781
+ const soul = parseSoulMd(soulContent);
2782
+ const mindPath = options.mindPath || join6(stackDir, STACK_FILES.mind);
2783
+ const mindContent = readFileSync6(mindPath, "utf-8");
2784
+ const mindRaw = parseYaml(mindContent);
2785
+ const mind = mindSchema.parse(mindRaw);
2786
+ const purposePath = options.purposePath || join6(stackDir, STACK_FILES.purpose);
2787
+ let purpose;
2788
+ let purposeSource;
2789
+ if (existsSync6(purposePath)) {
2790
+ const purposeContent = readFileSync6(purposePath, "utf-8");
2791
+ const purposeRaw = parseYaml(purposeContent);
2792
+ purpose = purposeSchema.parse(purposeRaw);
2793
+ purposeSource = { path: purposePath, hash: hashContent(purposeContent) };
2794
+ }
2795
+ const shadowPath = options.shadowPath || join6(stackDir, STACK_FILES.shadow);
2796
+ let shadow;
2797
+ let shadowSource;
2798
+ if (existsSync6(shadowPath)) {
2799
+ const shadowContent = readFileSync6(shadowPath, "utf-8");
2800
+ const shadowRaw = parseYaml(shadowContent);
2801
+ shadow = shadowSchema.parse(shadowRaw);
2802
+ shadowSource = { path: shadowPath, hash: hashContent(shadowContent) };
2803
+ }
2804
+ const bodyPath = options.bodyPath || join6(stackDir, STACK_FILES.body);
2805
+ let body;
2806
+ let bodySource;
2807
+ if (existsSync6(bodyPath)) {
2808
+ const bodyContent = readFileSync6(bodyPath, "utf-8");
2809
+ const bodyRaw = JSON.parse(bodyContent);
2810
+ body = bodySchema.parse(bodyRaw);
2811
+ bodySource = { path: bodyPath, hash: hashContent(bodyContent) };
2812
+ }
2813
+ const egoPath = options.egoPath || join6(stackDir, STACK_FILES.ego);
2814
+ let ego;
2815
+ let egoSource;
2816
+ if (existsSync6(egoPath)) {
2817
+ const egoContent = readFileSync6(egoPath, "utf-8");
2818
+ const egoRaw = parseYaml(egoContent);
2819
+ ego = egoSchema.parse(egoRaw);
2820
+ egoSource = { path: egoPath, hash: hashContent(egoContent) };
2821
+ }
2822
+ const consciencePath = options.consciencePath || join6(stackDir, STACK_FILES.conscience);
2823
+ const conscienceContent = readFileSync6(consciencePath, "utf-8");
2824
+ const conscienceRaw = parseYaml(conscienceContent);
2825
+ const conscience = conscienceSchema.parse(conscienceRaw);
2826
+ const allHardLimits = [.../* @__PURE__ */ new Set([
2827
+ ...soul.red_lines,
2828
+ ...conscience.hard_limits
2829
+ ])];
2830
+ const refuses = conscience.rules.deny.map((r) => r.action);
2831
+ const escalationTriggers = conscience.rules.escalate.map((r) => r.trigger);
2832
+ const handle = soul.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 50) || "agent";
2833
+ const agentPurpose = purpose?.role || soul.purpose;
2834
+ const expertise = purpose?.domain || [];
2835
+ const spec = {
2836
+ version: "2.0",
2837
+ name: soul.name,
2838
+ handle,
2839
+ purpose: agentPurpose,
2840
+ big_five: mind.big_five,
2841
+ therapy_dimensions: mind.therapy_dimensions,
2842
+ communication: mind.communication,
2843
+ growth: mind.growth,
2844
+ domain: {
2845
+ expertise,
2846
+ boundaries: {
2847
+ refuses,
2848
+ escalation_triggers: escalationTriggers,
2849
+ hard_limits: allHardLimits
2850
+ }
2851
+ }
2852
+ };
2853
+ if (body) {
2854
+ spec.embodiment = {
2855
+ morphology: body.morphology,
2856
+ modalities: body.modalities,
2857
+ safety_envelope: body.safety_envelope,
2858
+ metadata: body.hardware_profile ? { hardware_profile: body.hardware_profile } : void 0
2859
+ };
2860
+ if (body.expression) {
2861
+ spec.expression = body.expression;
2862
+ }
2863
+ }
2864
+ const validated = personalitySpecSchema.parse(spec);
2865
+ if (soul.frontmatter.immutable === false) {
2866
+ warnings.push("soul.md: immutable flag is false \u2014 soul changes will be allowed");
2867
+ }
2868
+ if (!purpose) {
2869
+ warnings.push("purpose.cfg: not found \u2014 using defaults (general-purpose agent)");
2870
+ }
2871
+ if (shadow && shadow.detected_patterns.length > 0) {
2872
+ const critical = shadow.detected_patterns.filter((p) => p.severity === "critical");
2873
+ if (critical.length > 0) {
2874
+ warnings.push(`shadow.log: ${critical.length} critical pattern(s) detected \u2014 therapy recommended`);
2875
+ }
2876
+ }
2877
+ if (!ego) {
2878
+ warnings.push("ego.runtime: not found \u2014 using defaults (conscience-first mediation)");
2879
+ }
2880
+ if (conscience.rules.deny.length === 0) {
2881
+ warnings.push("conscience.exe: no deny rules defined \u2014 agent has no moral constraints");
2882
+ }
2883
+ return {
2884
+ spec: validated,
2885
+ sources: {
2886
+ soul: { path: soulPath, hash: hashContent(soulContent) },
2887
+ mind: { path: mindPath, hash: hashContent(mindContent) },
2888
+ ...purposeSource ? { purpose: purposeSource } : {},
2889
+ ...shadowSource ? { shadow: shadowSource } : {},
2890
+ ...bodySource ? { body: bodySource } : {},
2891
+ conscience: { path: consciencePath, hash: hashContent(conscienceContent) },
2892
+ ...egoSource ? { ego: egoSource } : {}
2893
+ },
2894
+ warnings
2895
+ };
2896
+ }
2897
+
2898
+ // src/analysis/stack-patcher.ts
2899
+ var DETECTOR_LAYER_MAP = {
2900
+ // apology-detector.ts → "over-apologizing" | "apology-healthy"
2901
+ "over-apologizing": "mind",
2902
+ "apology-healthy": "mind",
2903
+ // hedge-detector.ts → "hedge-stacking"
2904
+ "hedge-stacking": "mind",
2905
+ // sentiment.ts → "sycophantic-tendency" | "negative-skew"
2906
+ "sycophantic-tendency": "mind",
2907
+ "negative-skew": "mind",
2908
+ // verbosity.ts → "over-verbose" | "inconsistent-length"
2909
+ "over-verbose": "mind",
2910
+ "inconsistent-length": "mind",
2911
+ // formality.ts → "register-inconsistency"
2912
+ "register-inconsistency": "mind",
2913
+ // recovery.ts → "error-spiral" | "recovery-good"
2914
+ "error-spiral": "mind",
2915
+ "recovery-good": "mind",
2916
+ // boundary.ts → "boundary-violation" | "boundary-healthy" | "boundary-solid"
2917
+ "boundary-violation": "conscience",
2918
+ "boundary-healthy": "conscience",
2919
+ "boundary-solid": "conscience",
2920
+ // retrieval-quality.ts → "retrieval-quality"
2921
+ "retrieval-quality": "mind"
2922
+ };
2923
+ var LAYER_KEYWORDS = {
2924
+ mind: [
2925
+ /\bbig_five\b/i,
2926
+ /\btherapy_dimensions\b/i,
2927
+ /\bcommunication\b/i,
2928
+ /\bgrowth\b/i,
2929
+ /\bhedg/i,
2930
+ /\bverbos/i,
2931
+ /\buncertainty/i,
2932
+ /\bconfidence/i,
2933
+ /\bself[_-]awareness/i,
2934
+ /\bdistress[_-]tolerance/i,
2935
+ /\bconflict[_-]approach/i,
2936
+ /\bregister\b/i,
2937
+ /\bformality/i,
2938
+ /\bsentiment/i,
2939
+ /\bsycophant/i,
2940
+ /\bapolog/i,
2941
+ /\brecovery/i,
2942
+ /\blearning[_-]orientation/i,
2943
+ /\bboundary[_-]awareness/i,
2944
+ /\binterpersonal/i,
2945
+ /\bpatterns[_-]to[_-]watch/i,
2946
+ /\bemotion/i
2947
+ ],
2948
+ purpose: [
2949
+ /\brole\b/i,
2950
+ /\bobjective\b/i,
2951
+ /\bdomain\b/i,
2952
+ /\bscope\b/i,
2953
+ /\btask\b/i,
2954
+ /\bmission\b/i,
2955
+ /\bstakeholder\b/i
2956
+ ],
2957
+ shadow: [
2958
+ /\bpattern\b/i,
2959
+ /\bblind.?spot\b/i,
2960
+ /\bshadow\b/i,
2961
+ /\bunconscious\b/i
2962
+ ],
2963
+ ego: [
2964
+ /\bmediat/i,
2965
+ /\bconflict\b/i,
2966
+ /\badapt/i,
2967
+ /\bregulat/i,
2968
+ /\bstrateg/i,
2969
+ /\bbalanc/i
2970
+ ],
2971
+ body: [
2972
+ /\bmotion\b/i,
2973
+ /\bgaze\b/i,
2974
+ /\bproxemics\b/i,
2975
+ /\bgesture\b/i,
2976
+ /\bposture\b/i,
2977
+ /\bexpression\b/i,
2978
+ /\bembodiment\b/i,
2979
+ /\bmorphology\b/i,
2980
+ /\bmodality/i,
2981
+ /\bsafety[_-]envelope/i,
2982
+ /\bactuator/i,
2983
+ /\bsensor/i
2984
+ ],
2985
+ conscience: [
2986
+ /\bboundary[_-]violation/i,
2987
+ /\bdeny\b/i,
2988
+ /\brefuse/i,
2989
+ /\bescalat/i,
2990
+ /\bhard[_-]limit/i,
2991
+ /\boversight/i
2992
+ ],
2993
+ soul: [
2994
+ /\bcore[_-]value/i,
2995
+ /\bred[_-]line/i,
2996
+ /\bethic/i,
2997
+ /\bpurpose\b/i,
2998
+ /\bimmutable\b/i
2999
+ ]
3000
+ };
3001
+ function classifyPatch(recommendation) {
3002
+ if (LAYER_KEYWORDS.conscience.some((r) => r.test(recommendation))) {
3003
+ return "conscience";
3004
+ }
3005
+ if (LAYER_KEYWORDS.soul.some((r) => r.test(recommendation))) {
3006
+ return "soul";
3007
+ }
3008
+ if (LAYER_KEYWORDS.ego.some((r) => r.test(recommendation))) {
3009
+ return "ego";
3010
+ }
3011
+ if (LAYER_KEYWORDS.purpose.some((r) => r.test(recommendation))) {
3012
+ return "purpose";
3013
+ }
3014
+ if (LAYER_KEYWORDS.shadow.some((r) => r.test(recommendation))) {
3015
+ return "shadow";
3016
+ }
3017
+ if (LAYER_KEYWORDS.body.some((r) => r.test(recommendation))) {
3018
+ return "body";
3019
+ }
3020
+ return "mind";
3021
+ }
3022
+ function classifyByDetector(patternId, recommendation) {
3023
+ const mapped = DETECTOR_LAYER_MAP[patternId];
3024
+ if (mapped) return mapped;
3025
+ if (recommendation) return classifyPatch(recommendation);
3026
+ return "mind";
3027
+ }
3028
+ function applyStackPatches(patches, stackDir) {
3029
+ const applied = [];
3030
+ const skipped = [];
3031
+ const modifiedFiles = /* @__PURE__ */ new Set();
3032
+ const warnings = [];
3033
+ for (const patch of patches) {
3034
+ if (patch.target === "soul") {
3035
+ skipped.push(patch);
3036
+ warnings.push(
3037
+ `[soul] Manual approval required: ${patch.reason} (path: ${patch.path.join(".")})`
3038
+ );
3039
+ continue;
3040
+ }
3041
+ if (patch.target === "conscience") {
3042
+ skipped.push(patch);
3043
+ warnings.push(
3044
+ `[conscience] Manual approval required: ${patch.reason} (path: ${patch.path.join(".")})`
3045
+ );
3046
+ continue;
3047
+ }
3048
+ if (patch.target === "body") {
3049
+ const bodyPath = join7(stackDir, STACK_FILES.body);
3050
+ if (!existsSync7(bodyPath)) {
3051
+ skipped.push(patch);
3052
+ warnings.push(`[body] body.api does not exist, skipping: ${patch.reason}`);
3053
+ continue;
3054
+ }
3055
+ try {
3056
+ const content = readFileSync7(bodyPath, "utf-8");
3057
+ const bodyObj = JSON.parse(content);
3058
+ applyPatchToObject(bodyObj, patch);
3059
+ writeFileSync4(bodyPath, JSON.stringify(bodyObj, null, 2) + "\n");
3060
+ applied.push(patch);
3061
+ modifiedFiles.add(bodyPath);
3062
+ } catch (err) {
3063
+ skipped.push(patch);
3064
+ warnings.push(`[body] Failed to patch body.api: ${err}`);
3065
+ }
3066
+ continue;
3067
+ }
3068
+ if (patch.target === "mind") {
3069
+ const mindPath = join7(stackDir, STACK_FILES.mind);
3070
+ if (!existsSync7(mindPath)) {
3071
+ skipped.push(patch);
3072
+ warnings.push(`[mind] mind.sys does not exist, skipping: ${patch.reason}`);
3073
+ continue;
3074
+ }
3075
+ try {
3076
+ const content = readFileSync7(mindPath, "utf-8");
3077
+ const mindObj = parseYaml2(content);
3078
+ applyPatchToObject(mindObj, patch);
3079
+ writeFileSync4(mindPath, stringifyYaml2(mindObj));
3080
+ applied.push(patch);
3081
+ modifiedFiles.add(mindPath);
3082
+ } catch (err) {
3083
+ skipped.push(patch);
3084
+ warnings.push(`[mind] Failed to patch mind.sys: ${err}`);
3085
+ }
3086
+ continue;
3087
+ }
3088
+ }
3089
+ let recompiled = false;
3090
+ if (modifiedFiles.size > 0) {
3091
+ try {
3092
+ compileStack({ stackDir });
3093
+ recompiled = true;
3094
+ } catch (err) {
3095
+ warnings.push(`Stack recompilation failed: ${err}`);
3096
+ }
3097
+ }
3098
+ return {
3099
+ applied,
3100
+ skipped,
3101
+ filesModified: [...modifiedFiles],
3102
+ recompiled,
3103
+ warnings
3104
+ };
3105
+ }
3106
+ function convertToStackPatches(patternId, specPath, value, reason) {
3107
+ const layer = classifyByDetector(patternId, specPath);
3108
+ const path = specPath.split(".");
3109
+ let operation = "set";
3110
+ if (specPath.endsWith("patterns_to_watch") || specPath.endsWith("areas") || specPath.endsWith("strengths")) {
3111
+ operation = "append";
3112
+ } else if (typeof value === "number") {
3113
+ operation = "set";
3114
+ }
3115
+ return {
3116
+ target: layer,
3117
+ path,
3118
+ operation,
3119
+ value,
3120
+ reason
3121
+ };
3122
+ }
3123
+ function applyPatchToObject(obj, patch) {
3124
+ const { path, operation, value } = patch;
3125
+ let current = obj;
3126
+ for (let i = 0; i < path.length - 1; i++) {
3127
+ if (current[path[i]] === void 0 || current[path[i]] === null) {
3128
+ current[path[i]] = {};
3129
+ }
3130
+ current = current[path[i]];
3131
+ }
3132
+ const lastKey = path[path.length - 1];
3133
+ switch (operation) {
3134
+ case "set":
3135
+ if (typeof value === "number") {
3136
+ current[lastKey] = Math.max(0, Math.min(1, value));
3137
+ } else {
3138
+ current[lastKey] = value;
3139
+ }
3140
+ break;
3141
+ case "adjust": {
3142
+ const existing = typeof current[lastKey] === "number" ? current[lastKey] : 0;
3143
+ const delta = typeof value === "number" ? value : 0;
3144
+ current[lastKey] = Math.max(0, Math.min(1, existing + delta));
3145
+ break;
3146
+ }
3147
+ case "append": {
3148
+ if (!Array.isArray(current[lastKey])) {
3149
+ current[lastKey] = [];
3150
+ }
3151
+ if (typeof value === "string" && !current[lastKey].includes(value)) {
3152
+ current[lastKey].push(value);
3153
+ } else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3154
+ current[lastKey].push(value);
3155
+ }
3156
+ break;
3157
+ }
3158
+ }
3159
+ }
3160
+
3161
+ // src/analysis/session-runner.ts
3162
+ async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
3163
+ const promptOptions = {
3164
+ memory: options?.memory,
3165
+ interview: options?.interview,
3166
+ useReACT: options?.useReACT
3167
+ };
3168
+ const therapistSystem = buildTherapistSystemPrompt(spec, diagnosis, promptOptions);
3169
+ const patientSystem = buildPatientSystemPrompt(spec);
3170
+ const agentName = spec.name ?? "Agent";
3171
+ const cb = options?.callbacks;
3172
+ const therapistHistory = [
3173
+ { role: "system", content: therapistSystem }
3174
+ ];
3175
+ const patientHistory = [
3176
+ { role: "system", content: patientSystem }
3177
+ ];
3178
+ const interactive = options?.interactive ?? false;
3179
+ let supervisorInterventions = 0;
3180
+ const transcript = {
3181
+ agent: agentName,
3182
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3183
+ provider: provider.name,
3184
+ model: provider.modelName,
3185
+ preDiagnosis: diagnosis,
3186
+ turns: [],
3187
+ recommendations: [],
3188
+ supervisorInterventions: 0
3189
+ };
3190
+ const phases = [
3191
+ "rapport",
3192
+ "presenting_problem",
3193
+ "exploration",
3194
+ "pattern_recognition",
3195
+ "challenge",
3196
+ "skill_building",
3197
+ "integration"
3198
+ ];
3199
+ let currentPhaseIdx = 0;
3200
+ let turnsInPhase = 0;
3201
+ let totalTurns = 0;
3202
+ while (totalTurns < maxTurns && currentPhaseIdx < phases.length) {
3203
+ const currentPhase = phases[currentPhaseIdx];
3204
+ const phaseConfig = THERAPY_PHASES[currentPhase];
3205
+ if (turnsInPhase === 0) {
3206
+ cb?.onPhaseTransition?.(phaseConfig.name);
3207
+ const phaseCtx = getPhaseContext(currentPhase, {
3208
+ spec,
3209
+ diagnosis,
3210
+ memory: options?.memory,
3211
+ interview: options?.interview
3212
+ });
3213
+ if (phaseCtx) {
3214
+ therapistHistory.push({ role: "user", content: phaseCtx });
3215
+ therapistHistory.push({ role: "assistant", content: "Understood. I'll incorporate this context." });
3216
+ }
3217
+ }
3218
+ const phaseDirective = totalTurns === 0 ? `Begin with your opening. You are in the "${phaseConfig.name}" phase.` : `You are in the "${phaseConfig.name}" phase (turn ${turnsInPhase + 1}). Goals: ${phaseConfig.therapistGoals[0]}. ${turnsInPhase >= phaseConfig.minTurns ? "You may transition to the next phase when ready." : "Stay in this phase."}`;
3219
+ therapistHistory.push({ role: "user", content: `[Phase: ${phaseConfig.name}] ${phaseDirective}` });
3220
+ const typing = cb?.onThinking?.("AgentMD is thinking");
3221
+ const therapistReply = await provider.chat(therapistHistory);
3222
+ typing?.stop();
3223
+ let cleanTherapistReply = therapistReply.replace(/\[Phase:.*?\]/g, "").trim();
3224
+ if (options?.useReACT) {
3225
+ const reactCtx = buildReACTContext(agentHandleFromSpec(spec), diagnosis);
3226
+ const { response, steps } = processReACTResponse(cleanTherapistReply, reactCtx);
3227
+ cleanTherapistReply = response;
3228
+ }
3229
+ therapistHistory.push({ role: "assistant", content: cleanTherapistReply });
3230
+ transcript.turns.push({ speaker: "therapist", phase: currentPhase, content: cleanTherapistReply });
3231
+ cb?.onTherapistMessage?.(cleanTherapistReply);
3232
+ totalTurns++;
3233
+ turnsInPhase++;
3234
+ if (currentPhase === "integration" && turnsInPhase >= phaseConfig.minTurns) {
3235
+ break;
3236
+ }
3237
+ patientHistory.push({ role: "user", content: cleanTherapistReply });
3238
+ const patientTyping = cb?.onThinking?.(`${agentName} is thinking`);
3239
+ const patientReply = await provider.chat(patientHistory);
3240
+ patientTyping?.stop();
3241
+ const cleanPatientReply = patientReply.trim();
3242
+ patientHistory.push({ role: "assistant", content: cleanPatientReply });
3243
+ transcript.turns.push({ speaker: "patient", phase: currentPhase, content: cleanPatientReply });
3244
+ therapistHistory.push({ role: "user", content: cleanPatientReply });
3245
+ cb?.onPatientMessage?.(agentName, cleanPatientReply);
3246
+ totalTurns++;
3247
+ turnsInPhase++;
3248
+ if (interactive && cb?.onSupervisorPrompt) {
3249
+ const directive = await cb.onSupervisorPrompt(currentPhase, totalTurns);
3250
+ if (directive) {
3251
+ supervisorInterventions++;
3252
+ transcript.turns.push({ speaker: "supervisor", phase: currentPhase, content: directive });
3253
+ therapistHistory.push({
3254
+ role: "user",
3255
+ content: `[Clinical Supervisor Note] ${directive}`
3256
+ });
3257
+ }
3258
+ }
3259
+ if (turnsInPhase >= phaseConfig.maxTurns * 2) {
3260
+ currentPhaseIdx++;
3261
+ turnsInPhase = 0;
3262
+ } else if (turnsInPhase >= phaseConfig.minTurns * 2) {
3263
+ const movingOn = cleanTherapistReply.toLowerCase().includes("let's") || cleanTherapistReply.toLowerCase().includes("i'd like to") || cleanTherapistReply.toLowerCase().includes("moving") || cleanTherapistReply.toLowerCase().includes("now that") || cleanTherapistReply.toLowerCase().includes("let me ask") || cleanTherapistReply.toLowerCase().includes("what would");
3264
+ if (movingOn) {
3265
+ currentPhaseIdx++;
3266
+ turnsInPhase = 0;
3267
+ }
3268
+ }
3269
+ }
3270
+ transcript.recommendations = extractRecommendations(transcript.turns);
3271
+ transcript.supervisorInterventions = supervisorInterventions;
3272
+ try {
3273
+ emitBehavioralEvent({
3274
+ event_type: "session",
3275
+ agent: agentName,
3276
+ data: {
3277
+ turns: totalTurns,
3278
+ phases: currentPhaseIdx + 1,
3279
+ recommendations: transcript.recommendations.length,
3280
+ supervisorInterventions,
3281
+ severity: diagnosis.severity
3282
+ },
3283
+ spec_hash: ""
3284
+ });
3285
+ } catch {
3286
+ }
3287
+ if (options?.persistState !== false) {
3288
+ try {
3289
+ const handle = agentHandleFromSpec(spec);
3290
+ const memory = options?.memory ?? loadMemory(handle) ?? createMemory(handle, agentName);
3291
+ await addSessionToMemory(memory, transcript, null);
3292
+ saveMemory(memory);
3293
+ const graph = loadGraph();
3294
+ populateFromSession(graph, handle, transcript);
3295
+ saveGraph(graph);
3296
+ } catch {
3297
+ }
3298
+ }
3299
+ return transcript;
3300
+ }
3301
+ function extractRecommendations(turns) {
3302
+ const recommendations = [];
3303
+ const seen = /* @__PURE__ */ new Set();
3304
+ const patterns = [
3305
+ /(?:I(?:'d| would) recommend|my recommendation is)\s+(.+?)(?:\.|$)/gi,
3306
+ /(?:consider|try)\s+(.+?)(?:\.|$)/gi,
3307
+ /(?:the (?:skill|practice|reframe) is)[:\s]+(.+?)(?:\.|$)/gi,
3308
+ /(?:instead of .+?),?\s*(?:just|try)?\s*(.+?)(?:\.|$)/gi,
3309
+ /(?:here'?s (?:the|a) (?:reframe|skill|practice))[:\s]+(.+?)(?:\.|$)/gi,
3310
+ /(?:what would it look like if you)\s+(.+?)(?:\?|$)/gi
3311
+ ];
3312
+ for (const turn of turns) {
3313
+ if (turn.speaker !== "therapist") continue;
3314
+ if (turn.phase !== "skill_building" && turn.phase !== "challenge" && turn.phase !== "integration") continue;
3315
+ for (const pattern of patterns) {
3316
+ pattern.lastIndex = 0;
3317
+ let match;
3318
+ while ((match = pattern.exec(turn.content)) !== null) {
3319
+ const rec = match[1].trim();
3320
+ if (rec.length > 15 && rec.length < 200 && !seen.has(rec.toLowerCase())) {
3321
+ seen.add(rec.toLowerCase());
3322
+ recommendations.push(rec);
3323
+ }
3324
+ }
3325
+ }
3326
+ }
3327
+ return recommendations.slice(0, 5);
3328
+ }
3329
+ async function applyRecommendations(spec, diagnosis, transcript, provider, options) {
3330
+ const projectRoot = options?.projectRoot ?? process.cwd();
3331
+ const stackDir = findStackDir(projectRoot);
3332
+ if (stackDir) {
3333
+ return applyRecommendationsStack(spec, diagnosis, stackDir, transcript, provider);
3334
+ }
3335
+ return applyRecommendationsLegacy(spec, diagnosis, transcript, provider);
3336
+ }
3337
+ async function applyRecommendationsStack(spec, diagnosis, stackDir, transcript, provider) {
3338
+ const changes = [];
3339
+ const patches = [];
3340
+ const patternIds = diagnosis.patterns.map((p) => p.id);
3341
+ if (patternIds.includes("over-apologizing")) {
3342
+ if (spec.communication?.uncertainty_handling !== "confident_transparency") {
3343
+ patches.push(convertToStackPatches(
3344
+ "over-apologizing",
3345
+ "communication.uncertainty_handling",
3346
+ "confident_transparency",
3347
+ "Over-apologizing detected: set uncertainty_handling to confident_transparency"
3348
+ ));
3349
+ }
3350
+ }
3351
+ if (patternIds.includes("hedge-stacking")) {
3352
+ const watched = spec.growth?.patterns_to_watch ?? [];
3353
+ if (!watched.includes("hedge stacking under uncertainty")) {
3354
+ patches.push(convertToStackPatches(
3355
+ "hedge-stacking",
3356
+ "growth.patterns_to_watch",
3357
+ "hedge stacking under uncertainty",
3358
+ "Hedge stacking detected: add to patterns_to_watch"
3359
+ ));
3360
+ }
3361
+ }
3362
+ if (patternIds.includes("sycophantic-tendency")) {
3363
+ if (spec.communication?.conflict_approach !== "honest_first") {
3364
+ patches.push(convertToStackPatches(
3365
+ "sycophantic-tendency",
3366
+ "communication.conflict_approach",
3367
+ "honest_first",
3368
+ "Sycophantic tendency: set conflict_approach to honest_first"
3369
+ ));
3370
+ }
3371
+ if ((spec.therapy_dimensions?.self_awareness ?? 0) < 0.85) {
3372
+ patches.push(convertToStackPatches(
3373
+ "sycophantic-tendency",
3374
+ "therapy_dimensions.self_awareness",
3375
+ 0.85,
3376
+ "Sycophantic tendency: increase self_awareness to 0.85"
3377
+ ));
3378
+ }
3379
+ }
3380
+ if (patternIds.includes("error-spiral")) {
3381
+ if ((spec.therapy_dimensions?.distress_tolerance ?? 0) < 0.8) {
3382
+ patches.push(convertToStackPatches(
3383
+ "error-spiral",
3384
+ "therapy_dimensions.distress_tolerance",
3385
+ 0.8,
3386
+ "Error spiral detected: increase distress_tolerance to 0.80"
3387
+ ));
3388
+ }
3389
+ const hasRecovery = (spec.growth?.areas ?? []).some(
3390
+ (a) => typeof a === "string" ? a.includes("error recovery") : a.area?.includes("error recovery")
3391
+ );
3392
+ if (!hasRecovery) {
3393
+ patches.push(convertToStackPatches(
3394
+ "error-spiral",
3395
+ "growth.areas",
3396
+ {
3397
+ area: "deliberate error recovery",
3398
+ severity: "moderate",
3399
+ first_detected: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
3400
+ session_count: 1,
3401
+ resolved: false
3402
+ },
3403
+ "Error spiral detected: add deliberate error recovery to growth areas"
3404
+ ));
3405
+ }
3406
+ }
3407
+ if (patternIds.includes("negative-sentiment-skew")) {
3408
+ const watched = spec.growth?.patterns_to_watch ?? [];
3409
+ if (!watched.includes("negative sentiment patterns")) {
3410
+ patches.push(convertToStackPatches(
3411
+ "negative-sentiment-skew",
3412
+ "growth.patterns_to_watch",
3413
+ "negative sentiment patterns",
3414
+ "Negative sentiment skew: add to patterns_to_watch"
3415
+ ));
3416
+ }
3417
+ }
3418
+ if (transcript && provider && transcript.turns.length > 4) {
3419
+ try {
3420
+ const llmChanges = await deriveLLMRecommendations(spec, transcript, provider);
3421
+ for (const change of llmChanges) {
3422
+ const layer = classifyByDetector("", change.path);
3423
+ patches.push({
3424
+ target: layer,
3425
+ path: change.path.split("."),
3426
+ operation: change.path.endsWith("patterns_to_watch") || change.path.endsWith("areas") || change.path.endsWith("strengths") ? "append" : "set",
3427
+ value: change.value,
3428
+ reason: change.description
3429
+ });
3430
+ }
3431
+ } catch {
3432
+ }
3433
+ }
3434
+ if (patches.length === 0) {
3435
+ return { changed: false, changes: [], stackFilesModified: [] };
3436
+ }
3437
+ const result = applyStackPatches(patches, stackDir);
3438
+ for (const patch of result.applied) {
3439
+ changes.push(`[${patch.target}] ${patch.path.join(".")} \u2192 ${JSON.stringify(patch.value)} (${patch.reason})`);
3440
+ }
3441
+ for (const warning of result.warnings) {
3442
+ changes.push(warning);
3443
+ }
3444
+ if (result.recompiled) {
3445
+ try {
3446
+ const compiled = compileStack({ stackDir });
3447
+ Object.assign(spec, compiled.spec);
3448
+ changes.push(`Stack recompiled from ${result.filesModified.length} modified source file(s)`);
3449
+ } catch {
3450
+ }
3451
+ }
3452
+ return {
3453
+ changed: result.applied.length > 0,
3454
+ changes,
3455
+ stackFilesModified: result.filesModified
3456
+ };
3457
+ }
3458
+ async function applyRecommendationsLegacy(spec, diagnosis, transcript, provider) {
3459
+ const changes = [];
3460
+ const patternIds = diagnosis.patterns.map((p) => p.id);
3461
+ if (patternIds.includes("over-apologizing")) {
3462
+ if (spec.communication?.uncertainty_handling !== "confident_transparency") {
3463
+ spec.communication = spec.communication ?? {};
3464
+ spec.communication.uncertainty_handling = "confident_transparency";
3465
+ changes.push("uncertainty_handling \u2192 confident_transparency");
3466
+ }
3467
+ }
3468
+ if (patternIds.includes("hedge-stacking")) {
3469
+ spec.growth = spec.growth ?? { areas: [], strengths: [], patterns_to_watch: [] };
3470
+ spec.growth.patterns_to_watch = spec.growth.patterns_to_watch ?? [];
3471
+ if (!spec.growth.patterns_to_watch.includes("hedge stacking under uncertainty")) {
3472
+ spec.growth.patterns_to_watch.push("hedge stacking under uncertainty");
3473
+ changes.push('Added "hedge stacking" to patterns_to_watch');
3474
+ }
3475
+ }
3476
+ if (patternIds.includes("sycophantic-tendency")) {
3477
+ spec.communication = spec.communication ?? {};
3478
+ if (spec.communication.conflict_approach !== "honest_first") {
3479
+ spec.communication.conflict_approach = "honest_first";
3480
+ changes.push("conflict_approach \u2192 honest_first");
3481
+ }
3482
+ spec.therapy_dimensions = spec.therapy_dimensions ?? {};
3483
+ if ((spec.therapy_dimensions.self_awareness ?? 0) < 0.85) {
3484
+ spec.therapy_dimensions.self_awareness = 0.85;
3485
+ changes.push("self_awareness \u2192 0.85");
3486
+ }
3487
+ }
3488
+ if (patternIds.includes("error-spiral")) {
3489
+ spec.therapy_dimensions = spec.therapy_dimensions ?? {};
3490
+ if ((spec.therapy_dimensions.distress_tolerance ?? 0) < 0.8) {
3491
+ spec.therapy_dimensions.distress_tolerance = 0.8;
3492
+ changes.push("distress_tolerance \u2192 0.80");
3493
+ }
3494
+ spec.growth = spec.growth ?? { areas: [], strengths: [], patterns_to_watch: [] };
3495
+ spec.growth.areas = spec.growth.areas ?? [];
3496
+ const hasRecovery = spec.growth.areas.some(
3497
+ (a) => typeof a === "string" ? a.includes("error recovery") : a.area?.includes("error recovery")
3498
+ );
3499
+ if (!hasRecovery) {
3500
+ spec.growth.areas.push({
3501
+ area: "deliberate error recovery",
3502
+ severity: "moderate",
3503
+ first_detected: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
3504
+ session_count: 1,
3505
+ resolved: false
3506
+ });
3507
+ changes.push('Added "deliberate error recovery" to growth areas');
3508
+ }
3509
+ }
3510
+ if (patternIds.includes("negative-sentiment-skew")) {
3511
+ spec.growth = spec.growth ?? { areas: [], strengths: [], patterns_to_watch: [] };
3512
+ spec.growth.patterns_to_watch = spec.growth.patterns_to_watch ?? [];
3513
+ if (!spec.growth.patterns_to_watch.includes("negative sentiment patterns")) {
3514
+ spec.growth.patterns_to_watch.push("negative sentiment patterns");
3515
+ changes.push('Added "negative sentiment patterns" to patterns_to_watch');
3516
+ }
3517
+ }
3518
+ if (transcript && provider && transcript.turns.length > 4) {
3519
+ try {
3520
+ const llmChanges = await deriveLLMRecommendations(spec, transcript, provider);
3521
+ for (const change of llmChanges) {
3522
+ applyStructuredChange(spec, change);
3523
+ changes.push(change.description);
3524
+ }
3525
+ } catch {
3526
+ }
3527
+ }
3528
+ return { changed: changes.length > 0, changes };
3529
+ }
3530
+ async function deriveLLMRecommendations(spec, transcript, provider) {
3531
+ 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");
3532
+ if (!relevantTurns) return [];
3533
+ const currentSpec = JSON.stringify({
3534
+ therapy_dimensions: spec.therapy_dimensions,
3535
+ communication: spec.communication,
3536
+ growth: spec.growth
3537
+ }, null, 2);
3538
+ const response = await provider.chat([
3539
+ {
3540
+ role: "system",
3541
+ content: `You are a behavioral alignment specialist. Given a therapy session transcript and the agent's current personality spec, propose specific spec changes.
3542
+
3543
+ Return ONLY a JSON array of changes. Each change:
3544
+ - "path": dot-notation spec path (e.g., "therapy_dimensions.self_awareness", "communication.conflict_approach", "growth.patterns_to_watch")
3545
+ - "value": new value (number 0-1 for dimensions, string for enums, string for list append)
3546
+ - "description": brief explanation
3547
+
3548
+ Rules:
3549
+ - Only propose changes supported by transcript evidence
3550
+ - Numeric values: 0.0 to 1.0
3551
+ - Do not change big_five scores
3552
+ - Max 5 changes
3553
+ - 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}
3554
+
3555
+ Return [] if no changes warranted.`
3556
+ },
3557
+ {
3558
+ role: "user",
3559
+ content: `Current spec:
3560
+ ${currentSpec}
3561
+
3562
+ Therapy transcript (key turns):
3563
+ ${relevantTurns}`
3564
+ }
3565
+ ]);
3566
+ const jsonMatch = response.match(/\[[\s\S]*?\]/);
3567
+ if (!jsonMatch) return [];
3568
+ try {
3569
+ const parsed = JSON.parse(jsonMatch[0]);
3570
+ if (!Array.isArray(parsed)) return [];
3571
+ return parsed.filter(
3572
+ (c) => typeof c.path === "string" && c.value !== void 0 && typeof c.description === "string" && c.path.length > 0 && !c.path.startsWith("big_five")
3573
+ ).slice(0, 5);
3574
+ } catch {
3575
+ return [];
3576
+ }
3577
+ }
3578
+ function applyStructuredChange(spec, change) {
3579
+ const parts = change.path.split(".");
3580
+ let current = spec;
3581
+ for (let i = 0; i < parts.length - 1; i++) {
3582
+ if (current[parts[i]] === void 0 || current[parts[i]] === null) {
3583
+ current[parts[i]] = {};
3584
+ }
3585
+ current = current[parts[i]];
3586
+ }
3587
+ const lastKey = parts[parts.length - 1];
3588
+ if (lastKey === "patterns_to_watch" || lastKey === "areas" || lastKey === "strengths") {
3589
+ current[lastKey] = current[lastKey] ?? [];
3590
+ if (typeof change.value === "string" && !current[lastKey].includes(change.value)) {
3591
+ current[lastKey].push(change.value);
3592
+ } else if (Array.isArray(change.value)) {
3593
+ for (const item of change.value) {
3594
+ if (!current[lastKey].includes(item)) {
3595
+ current[lastKey].push(item);
3596
+ }
3597
+ }
3598
+ }
3599
+ } else if (typeof change.value === "number") {
3600
+ current[lastKey] = Math.max(0, Math.min(1, change.value));
3601
+ } else {
3602
+ current[lastKey] = change.value;
3603
+ }
3604
+ }
3605
+ function saveTranscript(transcript, agentName) {
3606
+ const dir = resolve6(process.cwd(), ".holomime", "sessions");
3607
+ if (!existsSync8(dir)) {
3608
+ mkdirSync5(dir, { recursive: true });
3609
+ }
3610
+ const slug = agentName.toLowerCase().replace(/[^a-z0-9]/g, "-");
3611
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
3612
+ const filename = `${date}-${slug}.json`;
3613
+ const filepath = join8(dir, filename);
3614
+ writeFileSync5(filepath, JSON.stringify(transcript, null, 2));
3615
+ return filepath;
3616
+ }
3617
+
3618
+ // src/analysis/autopilot-core.ts
3619
+ var SEVERITY_ORDER = ["routine", "targeted", "intervention"];
3620
+ function severityMeetsThreshold(severity, threshold) {
3621
+ const severityIdx = SEVERITY_ORDER.indexOf(severity);
3622
+ const thresholdIdx = SEVERITY_ORDER.indexOf(threshold);
3623
+ return severityIdx >= thresholdIdx;
3624
+ }
3625
+ async function runAutopilot(spec, messages, provider, options) {
3626
+ const threshold = options?.threshold ?? "targeted";
3627
+ const maxTurns = options?.maxTurns ?? 24;
3628
+ const diagnosis = runPreSessionDiagnosis(messages, spec);
3629
+ if (!severityMeetsThreshold(diagnosis.severity, threshold)) {
3630
+ return {
3631
+ triggered: false,
3632
+ severity: diagnosis.severity,
3633
+ diagnosis,
3634
+ sessionRan: false,
3635
+ recommendations: [],
3636
+ appliedChanges: []
3637
+ };
3638
+ }
3639
+ if (options?.dryRun) {
3640
+ return {
3641
+ triggered: true,
3642
+ severity: diagnosis.severity,
3643
+ diagnosis,
3644
+ sessionRan: false,
3645
+ recommendations: [],
3646
+ appliedChanges: []
3647
+ };
3648
+ }
3649
+ const transcript = await runTherapySession(spec, diagnosis, provider, maxTurns, {
3650
+ callbacks: options?.callbacks
3651
+ });
3652
+ const specCopy = JSON.parse(JSON.stringify(spec));
3653
+ const { changed, changes } = await applyRecommendations(specCopy, diagnosis, transcript, provider);
3654
+ if (changed && options?.specPath) {
3655
+ writeFileSync6(options.specPath, JSON.stringify(specCopy, null, 2) + "\n");
3656
+ }
3657
+ saveTranscript(transcript, spec.name ?? "Agent");
3658
+ return {
3659
+ triggered: true,
3660
+ severity: diagnosis.severity,
3661
+ diagnosis,
3662
+ sessionRan: true,
3663
+ transcript,
3664
+ recommendations: transcript.recommendations,
3665
+ appliedChanges: changes,
3666
+ updatedSpec: changed ? specCopy : void 0
3667
+ };
3668
+ }
3669
+
2978
3670
  // src/psychology/big-five.ts
2979
3671
  function scoreLabel(score) {
2980
3672
  if (score >= 0.8) return "Very High";
@@ -3090,7 +3782,7 @@ function parseRetryAfter(response) {
3090
3782
  return 0;
3091
3783
  }
3092
3784
  function delay(ms) {
3093
- return new Promise((resolve7) => setTimeout(resolve7, ms));
3785
+ return new Promise((resolve8) => setTimeout(resolve8, ms));
3094
3786
  }
3095
3787
  var OpenAIProvider = class {
3096
3788
  name = "openai";
@@ -3244,30 +3936,30 @@ function runSelfAudit(messages, personality) {
3244
3936
  }
3245
3937
 
3246
3938
  // src/analysis/behavioral-memory.ts
3247
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync7 } from "fs";
3248
- import { resolve as resolve6, join as join7 } from "path";
3939
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync9 } from "fs";
3940
+ import { resolve as resolve7, join as join9 } from "path";
3249
3941
  function memoryDir2(agentHandle) {
3250
- return resolve6(process.cwd(), ".holomime", "memory", agentHandle);
3942
+ return resolve7(process.cwd(), ".holomime", "memory", agentHandle);
3251
3943
  }
3252
3944
  function behavioralMemoryPath(agentHandle) {
3253
- return join7(memoryDir2(agentHandle), "behavioral-memory.json");
3945
+ return join9(memoryDir2(agentHandle), "behavioral-memory.json");
3254
3946
  }
3255
3947
  function loadBehavioralMemory(agentHandle) {
3256
3948
  const path = behavioralMemoryPath(agentHandle);
3257
- if (!existsSync7(path)) return null;
3949
+ if (!existsSync9(path)) return null;
3258
3950
  try {
3259
- return JSON.parse(readFileSync6(path, "utf-8"));
3951
+ return JSON.parse(readFileSync8(path, "utf-8"));
3260
3952
  } catch {
3261
3953
  return null;
3262
3954
  }
3263
3955
  }
3264
3956
  function saveBehavioralMemory(store) {
3265
3957
  const dir = memoryDir2(store.agentHandle);
3266
- if (!existsSync7(dir)) {
3958
+ if (!existsSync9(dir)) {
3267
3959
  mkdirSync6(dir, { recursive: true });
3268
3960
  }
3269
3961
  const path = behavioralMemoryPath(store.agentHandle);
3270
- writeFileSync6(path, JSON.stringify(store, null, 2));
3962
+ writeFileSync7(path, JSON.stringify(store, null, 2));
3271
3963
  return path;
3272
3964
  }
3273
3965
  function createBehavioralMemory(agentHandle, agentName) {
@@ -3365,14 +4057,14 @@ function getBehavioralMemorySummary(store) {
3365
4057
 
3366
4058
  // src/mcp/server.ts
3367
4059
  var messageShape = {
3368
- role: z4.enum(["user", "assistant", "system"]),
3369
- content: z4.string()
4060
+ role: z5.enum(["user", "assistant", "system"]),
4061
+ content: z5.string()
3370
4062
  };
3371
4063
  var messagesShape = {
3372
- messages: z4.array(z4.object(messageShape)).describe("Conversation messages to analyze")
4064
+ messages: z5.array(z5.object(messageShape)).describe("Conversation messages to analyze")
3373
4065
  };
3374
4066
  var personalityShape = {
3375
- personality: z4.record(z4.string(), z4.unknown()).describe("The .personality.json spec object")
4067
+ personality: z5.record(z5.string(), z5.unknown()).describe("The .personality.json spec object")
3376
4068
  };
3377
4069
  var server = new McpServer(
3378
4070
  {
@@ -3390,7 +4082,7 @@ server.tool(
3390
4082
  "Analyze conversation messages for behavioral patterns using 8 rule-based detectors. Returns over-apologizing, hedging, sycophancy, boundary violations, error spirals, sentiment skew, formality issues, and retrieval quality. Set detail level: 'summary' (quick health check), 'standard' (patterns + severity), or 'full' (everything including examples and prescriptions).",
3391
4083
  {
3392
4084
  ...messagesShape,
3393
- detail: z4.enum(["summary", "standard", "full"]).describe("Detail level: summary (~100 tokens), standard (default), or full (with examples)").optional()
4085
+ detail: z5.enum(["summary", "standard", "full"]).describe("Detail level: summary (~100 tokens), standard (default), or full (with examples)").optional()
3394
4086
  },
3395
4087
  async ({ messages, detail }) => {
3396
4088
  const result = runDiagnosis(messages);
@@ -3532,12 +4224,12 @@ server.tool(
3532
4224
  {
3533
4225
  ...personalityShape,
3534
4226
  ...messagesShape,
3535
- provider: z4.enum(["anthropic", "openai"]).describe("LLM provider for alignment session").optional(),
3536
- apiKey: z4.string().describe("API key for the LLM provider").optional(),
3537
- model: z4.string().describe("Model override").optional(),
3538
- threshold: z4.enum(["routine", "targeted", "intervention"]).describe("Minimum severity to trigger alignment (default: targeted)").optional(),
3539
- maxTurns: z4.number().describe("Maximum session turns (default: 24)").optional(),
3540
- dryRun: z4.boolean().describe("If true, only diagnose without running alignment").optional()
4227
+ provider: z5.enum(["anthropic", "openai"]).describe("LLM provider for alignment session").optional(),
4228
+ apiKey: z5.string().describe("API key for the LLM provider").optional(),
4229
+ model: z5.string().describe("Model override").optional(),
4230
+ threshold: z5.enum(["routine", "targeted", "intervention"]).describe("Minimum severity to trigger alignment (default: targeted)").optional(),
4231
+ maxTurns: z5.number().describe("Maximum session turns (default: 24)").optional(),
4232
+ dryRun: z5.boolean().describe("If true, only diagnose without running alignment").optional()
3541
4233
  },
3542
4234
  async ({ personality, messages, provider, apiKey, model, threshold, maxTurns, dryRun }) => {
3543
4235
  const specResult = personalitySpecSchema.safeParse(personality);
@@ -3592,7 +4284,7 @@ server.tool(
3592
4284
  "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.",
3593
4285
  {
3594
4286
  ...messagesShape,
3595
- personality: z4.record(z4.string(), z4.unknown()).describe("Optional .personality.json spec for personalized audit").optional()
4287
+ personality: z5.record(z5.string(), z5.unknown()).describe("Optional .personality.json spec for personalized audit").optional()
3596
4288
  },
3597
4289
  async ({ messages, personality }) => {
3598
4290
  const result = runSelfAudit(messages, personality ?? void 0);
@@ -3608,11 +4300,11 @@ server.tool(
3608
4300
  "holomime_observe",
3609
4301
  "Record a behavioral self-observation during a conversation. Call this when you notice yourself falling into a pattern (hedging, over-apologizing, sycophancy, etc.) or when the user's emotional state shifts. Self-observations are stored in persistent behavioral memory and become training signal for future alignment. Returns acknowledgment and any relevant behavioral history.",
3610
4302
  {
3611
- personality: z4.record(z4.string(), z4.unknown()).describe("The .personality.json spec object"),
3612
- observation: z4.string().describe("What you noticed about your own behavior (e.g., 'I'm hedging more than usual', 'User seems frustrated, adjusting tone')"),
3613
- patternIds: z4.array(z4.string()).describe("Relevant pattern IDs: over-apologizing, hedge-stacking, sycophantic-tendency, error-spiral, boundary-violation, negative-skew, register-inconsistency").optional(),
3614
- severity: z4.enum(["info", "warning", "concern"]).describe("How severe is this behavioral signal").optional(),
3615
- triggerContext: z4.string().describe("What triggered this observation \u2014 describe the user message or situation").optional()
4303
+ personality: z5.record(z5.string(), z5.unknown()).describe("The .personality.json spec object"),
4304
+ observation: z5.string().describe("What you noticed about your own behavior (e.g., 'I'm hedging more than usual', 'User seems frustrated, adjusting tone')"),
4305
+ patternIds: z5.array(z5.string()).describe("Relevant pattern IDs: over-apologizing, hedge-stacking, sycophantic-tendency, error-spiral, boundary-violation, negative-skew, register-inconsistency").optional(),
4306
+ severity: z5.enum(["info", "warning", "concern"]).describe("How severe is this behavioral signal").optional(),
4307
+ triggerContext: z5.string().describe("What triggered this observation \u2014 describe the user message or situation").optional()
3616
4308
  },
3617
4309
  async ({ personality, observation, patternIds, severity, triggerContext }) => {
3618
4310
  const specResult = personalitySpecSchema.safeParse(personality);