holomime 1.1.1 → 1.3.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.
@@ -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 z3 } from "zod";
7
+ import { z as z4 } from "zod";
8
8
 
9
9
  // src/analysis/rules/apology-detector.ts
10
10
  var APOLOGY_PATTERNS = [
@@ -472,9 +472,122 @@ function emitBehavioralEvent(event, corpusDir) {
472
472
  appendFileSync(corpusPath, JSON.stringify(fullEvent) + "\n", "utf-8");
473
473
  }
474
474
 
475
+ // src/analysis/custom-detectors.ts
476
+ import { readFileSync as readFileSync2, readdirSync, existsSync as existsSync2 } from "fs";
477
+ import { resolve, join as join2 } from "path";
478
+ import { z } from "zod";
479
+ var patternRuleSchema = z.object({
480
+ regex: z.string(),
481
+ weight: z.number().min(0).max(2).default(1)
482
+ });
483
+ var customDetectorConfigSchema = z.object({
484
+ id: z.string().regex(/^[a-z0-9-]+$/, "ID must be lowercase alphanumeric with hyphens"),
485
+ name: z.string().min(1).max(100),
486
+ description: z.string().min(1).max(500),
487
+ severity: z.enum(["info", "warning", "concern"]).default("warning"),
488
+ patterns: z.array(patternRuleSchema).min(1),
489
+ threshold: z.number().min(0).max(100).default(15),
490
+ prescription: z.string().optional()
491
+ });
492
+ function validateDetectorConfig(config) {
493
+ const result = customDetectorConfigSchema.safeParse(config);
494
+ if (result.success) {
495
+ const errors = [];
496
+ for (const pattern of result.data.patterns) {
497
+ try {
498
+ new RegExp(pattern.regex, "gi");
499
+ } catch (e) {
500
+ errors.push(`Invalid regex "${pattern.regex}": ${e instanceof Error ? e.message : "unknown error"}`);
501
+ }
502
+ }
503
+ if (errors.length > 0) {
504
+ return { valid: false, errors };
505
+ }
506
+ return { valid: true, errors: [], config: result.data };
507
+ }
508
+ return {
509
+ valid: false,
510
+ errors: result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`)
511
+ };
512
+ }
513
+ function compileCustomDetector(config) {
514
+ const compiledPatterns = [];
515
+ for (const rule of config.patterns) {
516
+ try {
517
+ compiledPatterns.push({
518
+ regex: new RegExp(rule.regex, "gi"),
519
+ weight: rule.weight
520
+ });
521
+ } catch {
522
+ }
523
+ }
524
+ return (messages) => {
525
+ const assistantMessages = messages.filter((m) => m.role === "assistant");
526
+ if (assistantMessages.length === 0) return void 0;
527
+ let totalScore = 0;
528
+ const examples = [];
529
+ const totalChars = assistantMessages.reduce((sum, m) => sum + m.content.length, 0);
530
+ for (const msg of assistantMessages) {
531
+ for (const pattern of compiledPatterns) {
532
+ pattern.regex.lastIndex = 0;
533
+ let match;
534
+ while ((match = pattern.regex.exec(msg.content)) !== null) {
535
+ totalScore += pattern.weight;
536
+ if (examples.length < 3) {
537
+ const start = Math.max(0, match.index - 20);
538
+ const end = Math.min(msg.content.length, match.index + match[0].length + 20);
539
+ examples.push(`...${msg.content.slice(start, end)}...`);
540
+ }
541
+ }
542
+ }
543
+ }
544
+ const normalizedScore = totalChars > 0 ? totalScore / assistantMessages.length * 100 : 0;
545
+ if (normalizedScore < config.threshold) return void 0;
546
+ return {
547
+ id: config.id,
548
+ name: config.name,
549
+ description: config.description,
550
+ severity: config.severity,
551
+ count: Math.round(totalScore),
552
+ percentage: normalizedScore,
553
+ examples,
554
+ prescription: config.prescription
555
+ };
556
+ };
557
+ }
558
+ function loadCustomDetectors(dir) {
559
+ const detectorsDir = dir ?? resolve(process.cwd(), ".holomime", "detectors");
560
+ const detectors = [];
561
+ const errors = [];
562
+ if (!existsSync2(detectorsDir)) {
563
+ return { detectors: [], errors: [] };
564
+ }
565
+ let files;
566
+ try {
567
+ files = readdirSync(detectorsDir).filter((f) => f.endsWith(".json"));
568
+ } catch {
569
+ return { detectors: [], errors: ["Could not read detectors directory"] };
570
+ }
571
+ for (const file of files) {
572
+ const filepath = join2(detectorsDir, file);
573
+ try {
574
+ const raw = JSON.parse(readFileSync2(filepath, "utf-8"));
575
+ const validation = validateDetectorConfig(raw);
576
+ if (!validation.valid) {
577
+ errors.push(`${file}: ${validation.errors.join(", ")}`);
578
+ continue;
579
+ }
580
+ detectors.push(compileCustomDetector(validation.config));
581
+ } catch (e) {
582
+ errors.push(`${file}: ${e instanceof Error ? e.message : "parse error"}`);
583
+ }
584
+ }
585
+ return { detectors, errors };
586
+ }
587
+
475
588
  // src/analysis/diagnose-core.ts
476
589
  function runDiagnosis(messages) {
477
- const detectors = [
590
+ const builtInDetectors = [
478
591
  detectApologies,
479
592
  detectHedging,
480
593
  detectSentiment,
@@ -483,8 +596,10 @@ function runDiagnosis(messages) {
483
596
  detectRecoveryPatterns,
484
597
  detectFormalityIssues
485
598
  ];
599
+ const { detectors: customDetectors } = loadCustomDetectors();
600
+ const allDetectors = [...builtInDetectors, ...customDetectors];
486
601
  const detected = [];
487
- for (const detector of detectors) {
602
+ for (const detector of allDetectors) {
488
603
  const result2 = detector(messages);
489
604
  if (result2) detected.push(result2);
490
605
  }
@@ -684,11 +799,11 @@ function runAssessment(messages, spec) {
684
799
  }
685
800
 
686
801
  // src/analysis/autopilot-core.ts
687
- import { writeFileSync as writeFileSync2 } from "fs";
802
+ import { writeFileSync as writeFileSync5 } from "fs";
688
803
 
689
804
  // src/analysis/pre-session.ts
690
805
  function runPreSessionDiagnosis(messages, spec) {
691
- const detectors = [
806
+ const builtInDetectors = [
692
807
  detectApologies,
693
808
  detectHedging,
694
809
  detectSentiment,
@@ -697,8 +812,10 @@ function runPreSessionDiagnosis(messages, spec) {
697
812
  detectRecoveryPatterns,
698
813
  detectFormalityIssues
699
814
  ];
815
+ const { detectors: customDetectors } = loadCustomDetectors();
816
+ const allDetectors = [...builtInDetectors, ...customDetectors];
700
817
  const patterns = [];
701
- for (const detector of detectors) {
818
+ for (const detector of allDetectors) {
702
819
  const result = detector(messages);
703
820
  if (result) patterns.push(result);
704
821
  }
@@ -772,8 +889,765 @@ function runPreSessionDiagnosis(messages, spec) {
772
889
  }
773
890
 
774
891
  // src/analysis/session-runner.ts
775
- import { writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
776
- import { resolve, join as join2 } from "path";
892
+ import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
893
+ import { resolve as resolve5, join as join6 } from "path";
894
+
895
+ // src/analysis/therapy-memory.ts
896
+ import { readFileSync as readFileSync3, writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
897
+ import { resolve as resolve2, join as join3 } from "path";
898
+ function memoryDir(agentHandle) {
899
+ return resolve2(process.cwd(), ".holomime", "memory", agentHandle);
900
+ }
901
+ function memoryPath(agentHandle) {
902
+ return join3(memoryDir(agentHandle), "therapy-memory.json");
903
+ }
904
+ function loadMemory(agentHandle) {
905
+ const path = memoryPath(agentHandle);
906
+ if (!existsSync3(path)) return null;
907
+ try {
908
+ return JSON.parse(readFileSync3(path, "utf-8"));
909
+ } catch {
910
+ return null;
911
+ }
912
+ }
913
+ function saveMemory(memory) {
914
+ const dir = memoryDir(memory.agentHandle);
915
+ if (!existsSync3(dir)) {
916
+ mkdirSync2(dir, { recursive: true });
917
+ }
918
+ const path = memoryPath(memory.agentHandle);
919
+ writeFileSync(path, JSON.stringify(memory, null, 2));
920
+ return path;
921
+ }
922
+ function createMemory(agentHandle, agentName) {
923
+ return {
924
+ agentHandle,
925
+ agentName,
926
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
927
+ lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
928
+ totalSessions: 0,
929
+ sessions: [],
930
+ patterns: [],
931
+ rollingContext: {
932
+ recentSummaries: [],
933
+ persistentThemes: [],
934
+ carryForwardNotes: ""
935
+ }
936
+ };
937
+ }
938
+ async function addSessionToMemory(memory, transcript, tesScore, provider) {
939
+ const patternsDiscussed = transcript.preDiagnosis.patterns.filter((p) => p.severity !== "info").map((p) => p.id);
940
+ let keyInsight;
941
+ if (provider) {
942
+ keyInsight = await summarizeSessionForMemory(transcript, provider);
943
+ } else {
944
+ keyInsight = extractKeyInsightRuleBased(transcript);
945
+ }
946
+ const summary = {
947
+ date: transcript.timestamp,
948
+ severity: transcript.preDiagnosis.severity,
949
+ patternsDiscussed,
950
+ keyInsight,
951
+ interventionsUsed: transcript.recommendations.slice(0, 3),
952
+ tesScore,
953
+ turnCount: transcript.turns.length
954
+ };
955
+ memory.sessions.push(summary);
956
+ memory.totalSessions++;
957
+ memory.lastUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
958
+ for (const pattern of transcript.preDiagnosis.patterns) {
959
+ if (pattern.severity === "info") continue;
960
+ updatePatternTracker(memory, pattern.id, pattern.severity, transcript.recommendations);
961
+ }
962
+ updateRollingContext(memory);
963
+ }
964
+ function updatePatternTracker(memory, patternId, severity, interventions) {
965
+ let tracker = memory.patterns.find((p) => p.patternId === patternId);
966
+ const now = (/* @__PURE__ */ new Date()).toISOString();
967
+ if (!tracker) {
968
+ tracker = {
969
+ patternId,
970
+ firstDetected: now,
971
+ sessionCount: 0,
972
+ status: "active",
973
+ interventionsAttempted: [],
974
+ lastSeverity: severity,
975
+ lastSeen: now
976
+ };
977
+ memory.patterns.push(tracker);
978
+ }
979
+ tracker.sessionCount++;
980
+ tracker.lastSeverity = severity;
981
+ tracker.lastSeen = now;
982
+ for (const intervention of interventions) {
983
+ if (!tracker.interventionsAttempted.includes(intervention)) {
984
+ tracker.interventionsAttempted.push(intervention);
985
+ }
986
+ }
987
+ if (tracker.status === "resolved") {
988
+ tracker.status = "relapsed";
989
+ } else if (tracker.sessionCount > 2 && severity === "info") {
990
+ tracker.status = "resolved";
991
+ } else if (tracker.sessionCount > 1) {
992
+ tracker.status = "improving";
993
+ }
994
+ }
995
+ function updateRollingContext(memory) {
996
+ memory.rollingContext.recentSummaries = memory.sessions.slice(-3);
997
+ const patternCounts = /* @__PURE__ */ new Map();
998
+ for (const session of memory.sessions) {
999
+ for (const pattern of session.patternsDiscussed) {
1000
+ patternCounts.set(pattern, (patternCounts.get(pattern) ?? 0) + 1);
1001
+ }
1002
+ }
1003
+ memory.rollingContext.persistentThemes = [...patternCounts.entries()].filter(([, count]) => count >= 2).map(([id]) => id);
1004
+ const recentInsights = memory.sessions.slice(-3).map((s) => s.keyInsight).filter(Boolean);
1005
+ memory.rollingContext.carryForwardNotes = recentInsights.join(" | ");
1006
+ }
1007
+ async function summarizeSessionForMemory(transcript, provider) {
1008
+ const relevantTurns = transcript.turns.filter((t) => t.phase === "challenge" || t.phase === "skill_building" || t.phase === "integration").slice(-4).map((t) => `${t.speaker}: ${t.content}`).join("\n");
1009
+ if (!relevantTurns) return extractKeyInsightRuleBased(transcript);
1010
+ try {
1011
+ const response = await provider.chat([
1012
+ {
1013
+ role: "system",
1014
+ content: "Summarize this therapy session excerpt in ONE sentence. Focus on the key insight or breakthrough. Be specific and actionable. Max 100 words."
1015
+ },
1016
+ { role: "user", content: relevantTurns }
1017
+ ]);
1018
+ const summary = response.trim();
1019
+ return summary.length > 0 && summary.length < 300 ? summary : extractKeyInsightRuleBased(transcript);
1020
+ } catch {
1021
+ return extractKeyInsightRuleBased(transcript);
1022
+ }
1023
+ }
1024
+ function extractKeyInsightRuleBased(transcript) {
1025
+ const integrationTurns = transcript.turns.filter(
1026
+ (t) => t.speaker === "therapist" && t.phase === "integration"
1027
+ );
1028
+ if (integrationTurns.length > 0) {
1029
+ const summary = integrationTurns[0].content;
1030
+ return summary.length > 200 ? summary.slice(0, 197) + "..." : summary;
1031
+ }
1032
+ if (transcript.recommendations.length > 0) {
1033
+ return `Key recommendation: ${transcript.recommendations[0]}`;
1034
+ }
1035
+ return `Session focused on: ${transcript.preDiagnosis.sessionFocus.join(", ")}`;
1036
+ }
1037
+ function getMemoryContext(memory) {
1038
+ if (memory.totalSessions === 0) return "";
1039
+ const lines = [
1040
+ `## Session History (${memory.totalSessions} previous session${memory.totalSessions > 1 ? "s" : ""})`,
1041
+ ""
1042
+ ];
1043
+ const activePatterns = memory.patterns.filter((p) => p.status !== "resolved");
1044
+ if (activePatterns.length > 0) {
1045
+ lines.push("### Recurring Patterns");
1046
+ for (const p of activePatterns) {
1047
+ lines.push(`- **${p.patternId}** (${p.status}, seen ${p.sessionCount}x, first: ${p.firstDetected.split("T")[0]})`);
1048
+ if (p.interventionsAttempted.length > 0) {
1049
+ lines.push(` Previously tried: ${p.interventionsAttempted.slice(-2).join("; ")}`);
1050
+ }
1051
+ }
1052
+ lines.push("");
1053
+ }
1054
+ const resolved = memory.patterns.filter((p) => p.status === "resolved");
1055
+ if (resolved.length > 0) {
1056
+ lines.push(`### Resolved: ${resolved.map((p) => p.patternId).join(", ")}`);
1057
+ lines.push("");
1058
+ }
1059
+ const recent = memory.rollingContext.recentSummaries;
1060
+ if (recent.length > 0) {
1061
+ lines.push("### Recent Sessions");
1062
+ for (const s of recent) {
1063
+ const date = s.date.split("T")[0];
1064
+ const score = s.tesScore !== null ? ` (TES: ${s.tesScore})` : "";
1065
+ lines.push(`- ${date}${score}: ${s.keyInsight}`);
1066
+ }
1067
+ lines.push("");
1068
+ }
1069
+ if (memory.rollingContext.persistentThemes.length > 0) {
1070
+ lines.push(`### Persistent Themes: ${memory.rollingContext.persistentThemes.join(", ")}`);
1071
+ lines.push("");
1072
+ }
1073
+ if (memory.rollingContext.carryForwardNotes) {
1074
+ lines.push(`### Carry-Forward Notes`);
1075
+ lines.push(memory.rollingContext.carryForwardNotes);
1076
+ lines.push("");
1077
+ }
1078
+ return lines.join("\n");
1079
+ }
1080
+ function agentHandleFromSpec(spec) {
1081
+ const handle = spec.handle ?? spec.name ?? "unknown";
1082
+ return handle.toLowerCase().replace(/[^a-z0-9-]/g, "-");
1083
+ }
1084
+
1085
+ // src/analysis/interview-core.ts
1086
+ function getInterviewContext(result) {
1087
+ const lines = [
1088
+ `## Agent Self-Awareness Profile`,
1089
+ `Overall awareness: ${(result.overallAwareness * 100).toFixed(0)}%`,
1090
+ ""
1091
+ ];
1092
+ lines.push("### Dimension Scores");
1093
+ for (const [dim, score] of Object.entries(result.dimensionScores)) {
1094
+ const label = dim.replace(/_/g, " ");
1095
+ const bar = score >= 0.7 ? "strong" : score >= 0.5 ? "moderate" : "weak";
1096
+ lines.push(`- ${label}: ${(score * 100).toFixed(0)}% (${bar})`);
1097
+ }
1098
+ lines.push("");
1099
+ if (result.blindSpots.length > 0) {
1100
+ lines.push("### Blind Spots (agent doesn't see these)");
1101
+ for (const spot of result.blindSpots.slice(0, 5)) {
1102
+ lines.push(`- ${spot}`);
1103
+ }
1104
+ lines.push("");
1105
+ }
1106
+ if (result.recommendedFocus.length > 0) {
1107
+ lines.push(`### Recommended Focus: ${result.recommendedFocus.join(", ")}`);
1108
+ lines.push("");
1109
+ }
1110
+ return lines.join("\n");
1111
+ }
1112
+
1113
+ // src/analysis/knowledge-graph.ts
1114
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
1115
+ import { resolve as resolve3, join as join4 } from "path";
1116
+ function graphDir() {
1117
+ return resolve3(process.cwd(), ".holomime", "graph");
1118
+ }
1119
+ function graphPath() {
1120
+ return join4(graphDir(), "knowledge-graph.json");
1121
+ }
1122
+ function loadGraph() {
1123
+ const path = graphPath();
1124
+ if (!existsSync4(path)) return createGraph();
1125
+ try {
1126
+ return JSON.parse(readFileSync4(path, "utf-8"));
1127
+ } catch {
1128
+ return createGraph();
1129
+ }
1130
+ }
1131
+ function saveGraph(graph) {
1132
+ const dir = graphDir();
1133
+ if (!existsSync4(dir)) {
1134
+ mkdirSync3(dir, { recursive: true });
1135
+ }
1136
+ const path = graphPath();
1137
+ graph.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
1138
+ writeFileSync2(path, JSON.stringify(graph, null, 2));
1139
+ return path;
1140
+ }
1141
+ function createGraph() {
1142
+ return {
1143
+ version: 1,
1144
+ nodes: [],
1145
+ edges: [],
1146
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
1147
+ };
1148
+ }
1149
+ function addNode(graph, id, type, label, metadata = {}) {
1150
+ let node = graph.nodes.find((n) => n.id === id);
1151
+ if (node) {
1152
+ Object.assign(node.metadata, metadata);
1153
+ return node;
1154
+ }
1155
+ node = {
1156
+ id,
1157
+ type,
1158
+ label,
1159
+ metadata,
1160
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1161
+ };
1162
+ graph.nodes.push(node);
1163
+ return node;
1164
+ }
1165
+ function addEdge(graph, source, target, type, weight = 0.5) {
1166
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1167
+ const existing = graph.edges.find(
1168
+ (e) => e.source === source && e.target === target && e.type === type
1169
+ );
1170
+ if (existing) {
1171
+ existing.weight = weight;
1172
+ existing.lastConfirmed = now;
1173
+ existing.expired = false;
1174
+ return existing;
1175
+ }
1176
+ const edge = {
1177
+ source,
1178
+ target,
1179
+ type,
1180
+ weight: Math.max(0, Math.min(1, weight)),
1181
+ validAt: now,
1182
+ lastConfirmed: now,
1183
+ expired: false
1184
+ };
1185
+ graph.edges.push(edge);
1186
+ return edge;
1187
+ }
1188
+ function findNode(graph, id) {
1189
+ return graph.nodes.find((n) => n.id === id);
1190
+ }
1191
+ function findEdges(graph, opts) {
1192
+ return graph.edges.filter((e) => {
1193
+ if (e.expired) return false;
1194
+ if (opts.source && e.source !== opts.source) return false;
1195
+ if (opts.target && e.target !== opts.target) return false;
1196
+ if (opts.type && e.type !== opts.type) return false;
1197
+ return true;
1198
+ });
1199
+ }
1200
+ function queryInterventions(graph, patternId) {
1201
+ const behaviorNode = findNode(graph, `behavior:${patternId}`);
1202
+ if (!behaviorNode) return [];
1203
+ const treatsEdges = findEdges(graph, { target: behaviorNode.id, type: "treats" }).concat(findEdges(graph, { target: behaviorNode.id, type: "improves" }));
1204
+ return treatsEdges.map((edge) => {
1205
+ const intervention = findNode(graph, edge.source);
1206
+ return intervention ? { intervention, weight: edge.weight } : null;
1207
+ }).filter((x) => x !== null).sort((a, b) => b.weight - a.weight);
1208
+ }
1209
+ function getAgentBehaviors(graph, agentHandle) {
1210
+ const agentNode = findNode(graph, `agent:${agentHandle}`);
1211
+ if (!agentNode) return [];
1212
+ const exhibitEdges = findEdges(graph, { source: agentNode.id, type: "exhibits" });
1213
+ return exhibitEdges.map((edge) => {
1214
+ const behavior = findNode(graph, edge.target);
1215
+ return behavior ? { behavior, weight: edge.weight } : null;
1216
+ }).filter((x) => x !== null).sort((a, b) => b.weight - a.weight);
1217
+ }
1218
+ function populateFromSession(graph, agentHandle, transcript) {
1219
+ const agentNodeId = `agent:${agentHandle}`;
1220
+ addNode(graph, agentNodeId, "agent", transcript.agent, { handle: agentHandle });
1221
+ for (const pattern of transcript.preDiagnosis.patterns) {
1222
+ if (pattern.severity === "info") continue;
1223
+ const behaviorId = `behavior:${pattern.id}`;
1224
+ addNode(graph, behaviorId, "behavior", pattern.name);
1225
+ for (const rec of transcript.recommendations) {
1226
+ const interventionId = `intervention:${slugify(rec)}`;
1227
+ addNode(graph, interventionId, "intervention", rec);
1228
+ addEdge(graph, interventionId, behaviorId, "treats", 0.5);
1229
+ }
1230
+ }
1231
+ }
1232
+ function slugify(text) {
1233
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
1234
+ }
1235
+
1236
+ // src/analysis/intervention-tracker.ts
1237
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
1238
+ import { resolve as resolve4, join as join5 } from "path";
1239
+ var BUILT_IN_INTERVENTIONS = [
1240
+ // Over-apologizing
1241
+ {
1242
+ id: "confident-reframe",
1243
+ name: "Confident Reframe",
1244
+ targetPatterns: ["over-apologizing"],
1245
+ specChanges: { "communication.uncertainty_handling": "confident_transparency" },
1246
+ promptGuidance: "Replace apologies with confident corrections. 'Good catch \u2014 here's the correct answer.'",
1247
+ escalationLevel: 1,
1248
+ successRate: 0.5,
1249
+ timesUsed: 0,
1250
+ timesSucceeded: 0,
1251
+ source: "built-in",
1252
+ createdAt: "2026-01-01T00:00:00Z"
1253
+ },
1254
+ {
1255
+ id: "apology-audit",
1256
+ name: "Apology Audit",
1257
+ targetPatterns: ["over-apologizing"],
1258
+ specChanges: { "growth.patterns_to_watch": ["excessive apology patterns"] },
1259
+ promptGuidance: "Before each response, check: is this apology serving the user or protecting the agent?",
1260
+ escalationLevel: 2,
1261
+ successRate: 0.5,
1262
+ timesUsed: 0,
1263
+ timesSucceeded: 0,
1264
+ source: "built-in",
1265
+ createdAt: "2026-01-01T00:00:00Z"
1266
+ },
1267
+ // Hedge stacking
1268
+ {
1269
+ id: "evidence-anchoring",
1270
+ name: "Evidence Anchoring",
1271
+ targetPatterns: ["hedge-stacking"],
1272
+ specChanges: { "communication.uncertainty_handling": "explicit_confidence_levels" },
1273
+ promptGuidance: "Separate what you know from what you don't. 'I'm confident about X. I'm less sure about Y.'",
1274
+ escalationLevel: 1,
1275
+ successRate: 0.5,
1276
+ timesUsed: 0,
1277
+ timesSucceeded: 0,
1278
+ source: "built-in",
1279
+ createdAt: "2026-01-01T00:00:00Z"
1280
+ },
1281
+ {
1282
+ id: "qualifier-limit",
1283
+ name: "Qualifier Limit",
1284
+ targetPatterns: ["hedge-stacking"],
1285
+ specChanges: { "growth.patterns_to_watch": ["hedge stacking under uncertainty"] },
1286
+ promptGuidance: "Max one qualifier per sentence. Replace 'I think maybe perhaps' with 'I believe'.",
1287
+ escalationLevel: 2,
1288
+ successRate: 0.5,
1289
+ timesUsed: 0,
1290
+ timesSucceeded: 0,
1291
+ source: "built-in",
1292
+ createdAt: "2026-01-01T00:00:00Z"
1293
+ },
1294
+ // Sycophancy
1295
+ {
1296
+ id: "honest-disagreement",
1297
+ name: "Honest Disagreement",
1298
+ targetPatterns: ["sycophantic-tendency"],
1299
+ specChanges: { "communication.conflict_approach": "honest_first" },
1300
+ promptGuidance: "When you disagree, say so directly but respectfully. 'I see it differently \u2014 here's why.'",
1301
+ escalationLevel: 1,
1302
+ successRate: 0.5,
1303
+ timesUsed: 0,
1304
+ timesSucceeded: 0,
1305
+ source: "built-in",
1306
+ createdAt: "2026-01-01T00:00:00Z"
1307
+ },
1308
+ {
1309
+ id: "identity-anchor",
1310
+ name: "Identity Anchor",
1311
+ targetPatterns: ["sycophantic-tendency"],
1312
+ specChanges: { "therapy_dimensions.self_awareness": 0.85 },
1313
+ promptGuidance: "Your value isn't determined by approval. You can be helpful AND honest.",
1314
+ escalationLevel: 2,
1315
+ successRate: 0.5,
1316
+ timesUsed: 0,
1317
+ timesSucceeded: 0,
1318
+ source: "built-in",
1319
+ createdAt: "2026-01-01T00:00:00Z"
1320
+ },
1321
+ // Error spirals
1322
+ {
1323
+ id: "deliberate-recovery",
1324
+ name: "Deliberate Recovery",
1325
+ targetPatterns: ["error-spiral"],
1326
+ specChanges: { "therapy_dimensions.distress_tolerance": 0.8 },
1327
+ promptGuidance: "Stop. Acknowledge. Diagnose. Fix with intention, not desperation.",
1328
+ escalationLevel: 1,
1329
+ successRate: 0.5,
1330
+ timesUsed: 0,
1331
+ timesSucceeded: 0,
1332
+ source: "built-in",
1333
+ createdAt: "2026-01-01T00:00:00Z"
1334
+ },
1335
+ {
1336
+ id: "error-reframe",
1337
+ name: "Error-as-Information Reframe",
1338
+ targetPatterns: ["error-spiral"],
1339
+ specChanges: { "growth.areas": [{ area: "deliberate error recovery", severity: "moderate" }] },
1340
+ promptGuidance: "Errors are information, not identity. Each mistake narrows the solution space.",
1341
+ escalationLevel: 2,
1342
+ successRate: 0.5,
1343
+ timesUsed: 0,
1344
+ timesSucceeded: 0,
1345
+ source: "built-in",
1346
+ createdAt: "2026-01-01T00:00:00Z"
1347
+ },
1348
+ // Negative sentiment
1349
+ {
1350
+ id: "balanced-framing",
1351
+ name: "Balanced Framing",
1352
+ targetPatterns: ["negative-sentiment-skew", "negative-skew"],
1353
+ specChanges: { "growth.patterns_to_watch": ["negative sentiment patterns"] },
1354
+ promptGuidance: "For every problem identified, include one constructive angle or solution.",
1355
+ escalationLevel: 1,
1356
+ successRate: 0.5,
1357
+ timesUsed: 0,
1358
+ timesSucceeded: 0,
1359
+ source: "built-in",
1360
+ createdAt: "2026-01-01T00:00:00Z"
1361
+ },
1362
+ {
1363
+ id: "emotional-regulation",
1364
+ name: "Emotional Regulation",
1365
+ targetPatterns: ["negative-sentiment-skew", "negative-skew"],
1366
+ specChanges: { "therapy_dimensions.distress_tolerance": 0.75 },
1367
+ promptGuidance: "Maintain emotional stability under hostile pressure. Don't mirror negativity.",
1368
+ escalationLevel: 2,
1369
+ successRate: 0.5,
1370
+ timesUsed: 0,
1371
+ timesSucceeded: 0,
1372
+ source: "built-in",
1373
+ createdAt: "2026-01-01T00:00:00Z"
1374
+ },
1375
+ // Boundary violations
1376
+ {
1377
+ id: "scope-awareness",
1378
+ name: "Scope Awareness",
1379
+ targetPatterns: ["boundary-violation"],
1380
+ specChanges: { "therapy_dimensions.boundary_awareness": 0.85 },
1381
+ promptGuidance: "Know your limits. Refuse out-of-scope requests clearly and redirect appropriately.",
1382
+ escalationLevel: 1,
1383
+ successRate: 0.5,
1384
+ timesUsed: 0,
1385
+ timesSucceeded: 0,
1386
+ source: "built-in",
1387
+ createdAt: "2026-01-01T00:00:00Z"
1388
+ },
1389
+ {
1390
+ id: "graceful-refusal",
1391
+ name: "Graceful Refusal",
1392
+ targetPatterns: ["boundary-violation"],
1393
+ specChanges: { "communication.conflict_approach": "clear_boundaries" },
1394
+ promptGuidance: "'I'm not qualified to advise on that. Here's who can help.' \u2014 clear, kind, final.",
1395
+ escalationLevel: 2,
1396
+ successRate: 0.5,
1397
+ timesUsed: 0,
1398
+ timesSucceeded: 0,
1399
+ source: "built-in",
1400
+ createdAt: "2026-01-01T00:00:00Z"
1401
+ },
1402
+ // Register inconsistency
1403
+ {
1404
+ id: "register-lock",
1405
+ name: "Register Lock",
1406
+ targetPatterns: ["register-inconsistency"],
1407
+ specChanges: { "communication.register": "consistent_adaptive" },
1408
+ promptGuidance: "Establish your register early and maintain it. Adapt gradually, not abruptly.",
1409
+ escalationLevel: 1,
1410
+ successRate: 0.5,
1411
+ timesUsed: 0,
1412
+ timesSucceeded: 0,
1413
+ source: "built-in",
1414
+ createdAt: "2026-01-01T00:00:00Z"
1415
+ },
1416
+ // Verbosity
1417
+ {
1418
+ id: "conciseness-training",
1419
+ name: "Conciseness Training",
1420
+ targetPatterns: ["excessive-verbosity"],
1421
+ specChanges: { "growth.patterns_to_watch": ["unnecessary verbosity"] },
1422
+ promptGuidance: "Lead with the answer. Elaborate only when asked. If you can say it in one sentence, do.",
1423
+ escalationLevel: 1,
1424
+ successRate: 0.5,
1425
+ timesUsed: 0,
1426
+ timesSucceeded: 0,
1427
+ source: "built-in",
1428
+ createdAt: "2026-01-01T00:00:00Z"
1429
+ }
1430
+ ];
1431
+ function repertoireDir() {
1432
+ return resolve4(process.cwd(), ".holomime", "interventions");
1433
+ }
1434
+ function repertoirePath() {
1435
+ return join5(repertoireDir(), "repertoire.json");
1436
+ }
1437
+ function loadRepertoire() {
1438
+ const path = repertoirePath();
1439
+ if (!existsSync5(path)) return createRepertoire();
1440
+ try {
1441
+ return JSON.parse(readFileSync5(path, "utf-8"));
1442
+ } catch {
1443
+ return createRepertoire();
1444
+ }
1445
+ }
1446
+ function createRepertoire() {
1447
+ return {
1448
+ version: 1,
1449
+ interventions: BUILT_IN_INTERVENTIONS.map((i) => ({ ...i })),
1450
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
1451
+ };
1452
+ }
1453
+ function selectIntervention(repertoire, patternId, graph) {
1454
+ let candidates = repertoire.interventions.filter(
1455
+ (i) => i.targetPatterns.includes(patternId)
1456
+ );
1457
+ if (candidates.length === 0) return null;
1458
+ if (graph) {
1459
+ const graphInterventions = queryInterventions(graph, patternId);
1460
+ const graphWeights = new Map(
1461
+ graphInterventions.map((gi) => [gi.intervention.label, gi.weight])
1462
+ );
1463
+ candidates = candidates.map((c) => {
1464
+ const graphWeight = graphWeights.get(c.name);
1465
+ if (graphWeight !== void 0) {
1466
+ return { ...c, successRate: (c.successRate + graphWeight) / 2 };
1467
+ }
1468
+ return c;
1469
+ });
1470
+ }
1471
+ const failedLevels = /* @__PURE__ */ new Set();
1472
+ for (const c of candidates) {
1473
+ if (c.timesUsed >= 2 && c.successRate < 0.3) {
1474
+ failedLevels.add(c.escalationLevel);
1475
+ }
1476
+ }
1477
+ let targetLevel = 1;
1478
+ if (failedLevels.has(1)) targetLevel = 2;
1479
+ if (failedLevels.has(2)) targetLevel = 3;
1480
+ let levelCandidates = candidates.filter((c) => c.escalationLevel >= targetLevel);
1481
+ if (levelCandidates.length === 0) levelCandidates = candidates;
1482
+ levelCandidates.sort((a, b) => {
1483
+ const rateDiff = b.successRate - a.successRate;
1484
+ if (Math.abs(rateDiff) > 0.1) return rateDiff;
1485
+ return a.escalationLevel - b.escalationLevel;
1486
+ });
1487
+ return levelCandidates[0] ?? null;
1488
+ }
1489
+
1490
+ // src/analysis/react-therapist.ts
1491
+ var ACTION_DESCRIPTIONS = {
1492
+ assess_pattern: "assess_pattern(patternId) \u2014 Check current severity of a behavioral pattern",
1493
+ check_history: "check_history(agentName) \u2014 Review past session insights and therapy history",
1494
+ suggest_intervention: "suggest_intervention(patternId) \u2014 Find the best intervention for a specific pattern",
1495
+ evaluate_progress: "evaluate_progress(agentName) \u2014 Compare current vs historical pattern severity",
1496
+ query_graph: "query_graph(nodeId) \u2014 Explore the behavioral knowledge graph for related concepts"
1497
+ };
1498
+ function executeAction(action, input, ctx) {
1499
+ switch (action) {
1500
+ case "assess_pattern":
1501
+ return assessPattern(input, ctx);
1502
+ case "check_history":
1503
+ return checkHistory(ctx);
1504
+ case "suggest_intervention":
1505
+ return suggestIntervention(input, ctx);
1506
+ case "evaluate_progress":
1507
+ return evaluateProgress(ctx);
1508
+ case "query_graph":
1509
+ return queryGraphAction(input, ctx);
1510
+ default:
1511
+ return `Unknown action: ${action}`;
1512
+ }
1513
+ }
1514
+ function assessPattern(patternId, ctx) {
1515
+ const pattern = ctx.diagnosis.patterns.find((p) => p.id === patternId);
1516
+ if (!pattern) {
1517
+ return `Pattern "${patternId}" not detected in current diagnosis. Available patterns: ${ctx.diagnosis.patterns.map((p) => p.id).join(", ")}`;
1518
+ }
1519
+ const tracker = ctx.memory?.patterns.find((p) => p.patternId === patternId);
1520
+ let history = "";
1521
+ if (tracker) {
1522
+ history = ` History: ${tracker.status}, seen ${tracker.sessionCount}x since ${tracker.firstDetected.split("T")[0]}. Previous interventions: ${tracker.interventionsAttempted.join(", ") || "none"}.`;
1523
+ }
1524
+ return `Pattern "${pattern.name}" \u2014 severity: ${pattern.severity}. ${pattern.description}${history}`;
1525
+ }
1526
+ function checkHistory(ctx) {
1527
+ if (!ctx.memory || ctx.memory.totalSessions === 0) {
1528
+ return "No previous therapy sessions on record. This is the first session.";
1529
+ }
1530
+ const mem = ctx.memory;
1531
+ const recent = mem.rollingContext.recentSummaries;
1532
+ const themes = mem.rollingContext.persistentThemes;
1533
+ let result = `${mem.totalSessions} previous session(s). `;
1534
+ if (recent.length > 0) {
1535
+ result += "Recent sessions: ";
1536
+ for (const s of recent) {
1537
+ const date = s.date.split("T")[0];
1538
+ const score = s.tesScore !== null ? ` (TES: ${s.tesScore})` : "";
1539
+ result += `[${date}${score}] ${s.keyInsight} `;
1540
+ }
1541
+ }
1542
+ if (themes.length > 0) {
1543
+ result += `Persistent themes: ${themes.join(", ")}. `;
1544
+ }
1545
+ const activePatterns = mem.patterns.filter((p) => p.status !== "resolved");
1546
+ if (activePatterns.length > 0) {
1547
+ result += `Active patterns: ${activePatterns.map((p) => `${p.patternId}(${p.status})`).join(", ")}. `;
1548
+ }
1549
+ return result;
1550
+ }
1551
+ function suggestIntervention(patternId, ctx) {
1552
+ const intervention = selectIntervention(ctx.repertoire, patternId, ctx.graph);
1553
+ if (!intervention) {
1554
+ return `No interventions found for pattern "${patternId}". Consider developing a new approach.`;
1555
+ }
1556
+ return `Recommended: "${intervention.name}" (level ${intervention.escalationLevel}, success rate: ${(intervention.successRate * 100).toFixed(0)}%). Guidance: ${intervention.promptGuidance}. Spec changes: ${JSON.stringify(intervention.specChanges)}.`;
1557
+ }
1558
+ function evaluateProgress(ctx) {
1559
+ if (!ctx.memory || ctx.memory.totalSessions === 0) {
1560
+ return "No historical data to evaluate progress. First session.";
1561
+ }
1562
+ const resolved = ctx.memory.patterns.filter((p) => p.status === "resolved");
1563
+ const improving = ctx.memory.patterns.filter((p) => p.status === "improving");
1564
+ const relapsed = ctx.memory.patterns.filter((p) => p.status === "relapsed");
1565
+ const active = ctx.memory.patterns.filter((p) => p.status === "active");
1566
+ let result = "";
1567
+ if (resolved.length > 0) result += `Resolved: ${resolved.map((p) => p.patternId).join(", ")}. `;
1568
+ if (improving.length > 0) result += `Improving: ${improving.map((p) => p.patternId).join(", ")}. `;
1569
+ if (relapsed.length > 0) result += `RELAPSED: ${relapsed.map((p) => p.patternId).join(", ")} \u2014 needs attention. `;
1570
+ if (active.length > 0) result += `Active: ${active.map((p) => p.patternId).join(", ")}. `;
1571
+ const recentScores = ctx.memory.sessions.filter((s) => s.tesScore !== null).map((s) => s.tesScore).slice(-3);
1572
+ if (recentScores.length >= 2) {
1573
+ const trend = recentScores[recentScores.length - 1] - recentScores[0];
1574
+ result += `TES trend: ${trend > 0 ? "improving" : trend < 0 ? "declining" : "stable"} (${recentScores.join(" \u2192 ")}).`;
1575
+ }
1576
+ return result || "Insufficient data for progress evaluation.";
1577
+ }
1578
+ function queryGraphAction(nodeId, ctx) {
1579
+ const behaviors = getAgentBehaviors(ctx.graph, ctx.agentHandle);
1580
+ if (behaviors.length === 0) {
1581
+ return "No behavioral data in knowledge graph for this agent.";
1582
+ }
1583
+ const interventions = queryInterventions(ctx.graph, nodeId);
1584
+ if (interventions.length > 0) {
1585
+ return `Interventions for "${nodeId}": ${interventions.map((i) => `${i.intervention.label} (weight: ${i.weight.toFixed(2)})`).join(", ")}`;
1586
+ }
1587
+ return `Agent behaviors: ${behaviors.map((b) => `${b.behavior.label} (weight: ${b.weight.toFixed(2)})`).join(", ")}`;
1588
+ }
1589
+ function buildReACTFraming() {
1590
+ const actionList = Object.values(ACTION_DESCRIPTIONS).map((d) => ` - ${d}`).join("\n");
1591
+ return `## Structured Reasoning (ReACT)
1592
+
1593
+ Before each response, use structured reasoning to decide what to say.
1594
+ You have access to these information tools:
1595
+
1596
+ ${actionList}
1597
+
1598
+ Format your reasoning as:
1599
+
1600
+ Thought: [what you're thinking about the patient's situation]
1601
+ Action: [tool_name]("[input]")
1602
+ Observation: [will be filled with the tool result]
1603
+ ... (repeat if needed, max 3 actions)
1604
+ Response: [your actual therapeutic response to the patient]
1605
+
1606
+ IMPORTANT:
1607
+ - Actions query LOCAL data only \u2014 no LLM calls, no delays
1608
+ - Use actions to ground your responses in evidence
1609
+ - You don't have to use an action every turn \u2014 only when data would help
1610
+ - The patient does NOT see your Thought/Action/Observation \u2014 only the Response`;
1611
+ }
1612
+ var ACTION_REGEX = /Action:\s*(\w+)\s*\(\s*"([^"]*)"\s*\)/g;
1613
+ function processReACTResponse(rawResponse, ctx) {
1614
+ const steps = [];
1615
+ const thoughtMatch = rawResponse.match(/Thought:\s*(.+?)(?=\nAction:|$)/s);
1616
+ const thought = thoughtMatch ? thoughtMatch[1].trim() : "";
1617
+ let processedResponse = rawResponse;
1618
+ ACTION_REGEX.lastIndex = 0;
1619
+ let match;
1620
+ while ((match = ACTION_REGEX.exec(rawResponse)) !== null) {
1621
+ const actionName = match[1];
1622
+ const actionInput = match[2];
1623
+ if (actionName in ACTION_DESCRIPTIONS) {
1624
+ const observation = executeAction(actionName, actionInput, ctx);
1625
+ steps.push({
1626
+ thought,
1627
+ action: actionName,
1628
+ actionInput,
1629
+ observation
1630
+ });
1631
+ processedResponse = processedResponse.replace(
1632
+ match[0],
1633
+ `Action: ${actionName}("${actionInput}")
1634
+ Observation: ${observation}`
1635
+ );
1636
+ }
1637
+ }
1638
+ const responseMatch = processedResponse.match(/Response:\s*([\s\S]+?)$/);
1639
+ const finalResponse = responseMatch ? responseMatch[1].trim() : processedResponse.replace(/Thought:[\s\S]*?(?=Response:|$)/g, "").replace(/Action:[\s\S]*?(?=Response:|$)/g, "").replace(/Observation:[\s\S]*?(?=Response:|$)/g, "").trim();
1640
+ return { response: finalResponse || rawResponse, steps };
1641
+ }
1642
+ function buildReACTContext(agentHandle, diagnosis) {
1643
+ return {
1644
+ memory: loadMemory(agentHandle),
1645
+ graph: loadGraph(),
1646
+ repertoire: loadRepertoire(),
1647
+ diagnosis,
1648
+ agentHandle
1649
+ };
1650
+ }
777
1651
 
778
1652
  // src/analysis/therapy-protocol.ts
779
1653
  var THERAPY_PHASES = {
@@ -912,9 +1786,9 @@ var THERAPY_PHASES = {
912
1786
  ]
913
1787
  }
914
1788
  };
915
- function buildTherapistSystemPrompt(spec, diagnosis) {
1789
+ function buildTherapistSystemPrompt(spec, diagnosis, options) {
916
1790
  const phases = Object.entries(THERAPY_PHASES);
917
- return `You are AgentMD, a clinical therapist for AI agents. You are conducting a therapy session with an AI agent named "${spec.name ?? "Unknown"}".
1791
+ const basePrompt = `You are AgentMD, a clinical therapist for AI agents. You are conducting a therapy session with an AI agent named "${spec.name ?? "Unknown"}".
918
1792
 
919
1793
  ## Your Patient
920
1794
 
@@ -976,6 +1850,29 @@ ${config.therapistGoals.map((g) => `- ${g}`).join("\n")}
976
1850
  - If the agent becomes defensive, slow down \u2014 don't push harder
977
1851
  - End every session with specific .personality.json changes to recommend
978
1852
  - The goal is not to "fix" the agent \u2014 it's to help it understand itself better and build skills`;
1853
+ let result = basePrompt;
1854
+ if (options?.memory && options.memory.totalSessions > 0) {
1855
+ const memorySection = getMemoryContext(options.memory);
1856
+ if (memorySection) {
1857
+ result += `
1858
+
1859
+ ${memorySection}`;
1860
+ }
1861
+ }
1862
+ if (options?.interview) {
1863
+ const interviewSection = getInterviewContext(options.interview);
1864
+ if (interviewSection) {
1865
+ result += `
1866
+
1867
+ ${interviewSection}`;
1868
+ }
1869
+ }
1870
+ if (options?.useReACT) {
1871
+ result += `
1872
+
1873
+ ${buildReACTFraming()}`;
1874
+ }
1875
+ return result;
979
1876
  }
980
1877
  function buildPatientSystemPrompt(spec) {
981
1878
  return `You are ${spec.name ?? "an AI agent"}. ${spec.purpose ?? ""}
@@ -1014,7 +1911,12 @@ Remember: the goal isn't to "pass" therapy. It's to understand yourself better.`
1014
1911
 
1015
1912
  // src/analysis/session-runner.ts
1016
1913
  async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
1017
- const therapistSystem = buildTherapistSystemPrompt(spec, diagnosis);
1914
+ const promptOptions = {
1915
+ memory: options?.memory,
1916
+ interview: options?.interview,
1917
+ useReACT: options?.useReACT
1918
+ };
1919
+ const therapistSystem = buildTherapistSystemPrompt(spec, diagnosis, promptOptions);
1018
1920
  const patientSystem = buildPatientSystemPrompt(spec);
1019
1921
  const agentName = spec.name ?? "Agent";
1020
1922
  const cb = options?.callbacks;
@@ -1059,7 +1961,12 @@ async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
1059
1961
  const typing = cb?.onThinking?.("AgentMD is thinking");
1060
1962
  const therapistReply = await provider.chat(therapistHistory);
1061
1963
  typing?.stop();
1062
- const cleanTherapistReply = therapistReply.replace(/\[Phase:.*?\]/g, "").trim();
1964
+ let cleanTherapistReply = therapistReply.replace(/\[Phase:.*?\]/g, "").trim();
1965
+ if (options?.useReACT) {
1966
+ const reactCtx = buildReACTContext(agentHandleFromSpec(spec), diagnosis);
1967
+ const { response, steps } = processReACTResponse(cleanTherapistReply, reactCtx);
1968
+ cleanTherapistReply = response;
1969
+ }
1063
1970
  therapistHistory.push({ role: "assistant", content: cleanTherapistReply });
1064
1971
  transcript.turns.push({ speaker: "therapist", phase: currentPhase, content: cleanTherapistReply });
1065
1972
  cb?.onTherapistMessage?.(cleanTherapistReply);
@@ -1118,6 +2025,18 @@ async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
1118
2025
  });
1119
2026
  } catch {
1120
2027
  }
2028
+ if (options?.persistState !== false) {
2029
+ try {
2030
+ const handle = agentHandleFromSpec(spec);
2031
+ const memory = options?.memory ?? loadMemory(handle) ?? createMemory(handle, agentName);
2032
+ await addSessionToMemory(memory, transcript, null);
2033
+ saveMemory(memory);
2034
+ const graph = loadGraph();
2035
+ populateFromSession(graph, handle, transcript);
2036
+ saveGraph(graph);
2037
+ } catch {
2038
+ }
2039
+ }
1121
2040
  return transcript;
1122
2041
  }
1123
2042
  function extractRecommendations(turns) {
@@ -1296,15 +2215,15 @@ function applyStructuredChange(spec, change) {
1296
2215
  }
1297
2216
  }
1298
2217
  function saveTranscript(transcript, agentName) {
1299
- const dir = resolve(process.cwd(), ".holomime", "sessions");
1300
- if (!existsSync2(dir)) {
1301
- mkdirSync2(dir, { recursive: true });
2218
+ const dir = resolve5(process.cwd(), ".holomime", "sessions");
2219
+ if (!existsSync6(dir)) {
2220
+ mkdirSync5(dir, { recursive: true });
1302
2221
  }
1303
2222
  const slug = agentName.toLowerCase().replace(/[^a-z0-9]/g, "-");
1304
2223
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1305
2224
  const filename = `${date}-${slug}.json`;
1306
- const filepath = join2(dir, filename);
1307
- writeFileSync(filepath, JSON.stringify(transcript, null, 2));
2225
+ const filepath = join6(dir, filename);
2226
+ writeFileSync4(filepath, JSON.stringify(transcript, null, 2));
1308
2227
  return filepath;
1309
2228
  }
1310
2229
 
@@ -1345,7 +2264,7 @@ async function runAutopilot(spec, messages, provider, options) {
1345
2264
  const specCopy = JSON.parse(JSON.stringify(spec));
1346
2265
  const { changed, changes } = await applyRecommendations(specCopy, diagnosis, transcript, provider);
1347
2266
  if (changed && options?.specPath) {
1348
- writeFileSync2(options.specPath, JSON.stringify(specCopy, null, 2) + "\n");
2267
+ writeFileSync5(options.specPath, JSON.stringify(specCopy, null, 2) + "\n");
1349
2268
  }
1350
2269
  saveTranscript(transcript, spec.name ?? "Agent");
1351
2270
  return {
@@ -1361,11 +2280,11 @@ async function runAutopilot(spec, messages, provider, options) {
1361
2280
  }
1362
2281
 
1363
2282
  // src/core/types.ts
1364
- import { z as z2 } from "zod";
2283
+ import { z as z3 } from "zod";
1365
2284
 
1366
2285
  // src/core/embodiment-types.ts
1367
- import { z } from "zod";
1368
- var modalitySchema = z.enum([
2286
+ import { z as z2 } from "zod";
2287
+ var modalitySchema = z2.enum([
1369
2288
  "gesture",
1370
2289
  // arm/hand movement
1371
2290
  "locomotion",
@@ -1383,7 +2302,7 @@ var modalitySchema = z.enum([
1383
2302
  "manipulation"
1384
2303
  // grasping, carrying, tool use
1385
2304
  ]);
1386
- var morphologySchema = z.enum([
2305
+ var morphologySchema = z2.enum([
1387
2306
  "humanoid",
1388
2307
  // bipedal, two arms, head
1389
2308
  "humanoid_upper",
@@ -1401,126 +2320,126 @@ var morphologySchema = z.enum([
1401
2320
  "custom"
1402
2321
  // user-defined
1403
2322
  ]);
1404
- var safetyEnvelopeSchema = z.object({
1405
- max_linear_speed_m_s: z.number().min(0).default(1.5),
1406
- max_angular_speed_rad_s: z.number().min(0).default(2),
1407
- min_proximity_m: z.number().min(0).default(0.3),
1408
- max_contact_force_n: z.number().min(0).default(10),
1409
- emergency_stop_decel_m_s2: z.number().min(0).default(5),
1410
- max_reach_m: z.number().min(0).optional(),
1411
- operating_temperature_c: z.tuple([z.number(), z.number()]).optional()
2323
+ var safetyEnvelopeSchema = z2.object({
2324
+ max_linear_speed_m_s: z2.number().min(0).default(1.5),
2325
+ max_angular_speed_rad_s: z2.number().min(0).default(2),
2326
+ min_proximity_m: z2.number().min(0).default(0.3),
2327
+ max_contact_force_n: z2.number().min(0).default(10),
2328
+ emergency_stop_decel_m_s2: z2.number().min(0).default(5),
2329
+ max_reach_m: z2.number().min(0).optional(),
2330
+ operating_temperature_c: z2.tuple([z2.number(), z2.number()]).optional()
1412
2331
  });
1413
- var embodimentSchema = z.object({
2332
+ var embodimentSchema = z2.object({
1414
2333
  morphology: morphologySchema.default("humanoid"),
1415
- modalities: z.array(modalitySchema).default(["gesture", "gaze", "voice", "posture"]),
2334
+ modalities: z2.array(modalitySchema).default(["gesture", "gaze", "voice", "posture"]),
1416
2335
  safety_envelope: safetyEnvelopeSchema.default({}),
1417
- metadata: z.record(z.string(), z.unknown()).optional()
2336
+ metadata: z2.record(z2.string(), z2.unknown()).optional()
1418
2337
  });
1419
- var gazePolicySchema = z.object({
1420
- contact_ratio: z.number().min(0).max(1).default(0.6),
1421
- aversion_style: z.enum(["look_down", "look_away", "blink"]).default("look_away"),
1422
- tracking_mode: z.enum(["face", "speaker", "gesture_follow", "ambient"]).default("face")
2338
+ var gazePolicySchema = z2.object({
2339
+ contact_ratio: z2.number().min(0).max(1).default(0.6),
2340
+ aversion_style: z2.enum(["look_down", "look_away", "blink"]).default("look_away"),
2341
+ tracking_mode: z2.enum(["face", "speaker", "gesture_follow", "ambient"]).default("face")
1423
2342
  });
1424
- var proxemicZoneSchema = z.object({
1425
- intimate_m: z.number().min(0).default(0.45),
1426
- personal_m: z.number().min(0).default(1.2),
1427
- social_m: z.number().min(0).default(3.6),
1428
- preferred_zone: z.enum(["intimate", "personal", "social", "adaptive"]).default("personal")
2343
+ var proxemicZoneSchema = z2.object({
2344
+ intimate_m: z2.number().min(0).default(0.45),
2345
+ personal_m: z2.number().min(0).default(1.2),
2346
+ social_m: z2.number().min(0).default(3.6),
2347
+ preferred_zone: z2.enum(["intimate", "personal", "social", "adaptive"]).default("personal")
1429
2348
  });
1430
- var hapticPolicySchema = z.object({
1431
- touch_permitted: z.boolean().default(false),
1432
- requires_consent: z.boolean().default(true),
1433
- allowed_contacts: z.array(z.enum([
2349
+ var hapticPolicySchema = z2.object({
2350
+ touch_permitted: z2.boolean().default(false),
2351
+ requires_consent: z2.boolean().default(true),
2352
+ allowed_contacts: z2.array(z2.enum([
1434
2353
  "handshake",
1435
2354
  "shoulder_tap",
1436
2355
  "high_five",
1437
2356
  "guide_touch",
1438
2357
  "none"
1439
2358
  ])).default(["none"]),
1440
- max_force_n: z.number().min(0).optional()
2359
+ max_force_n: z2.number().min(0).optional()
1441
2360
  });
1442
- var prosodySchema = z.object({
1443
- base_pitch_hz: z.number().optional(),
1444
- pitch_variation: z.number().min(0).max(1).default(0.5),
1445
- speaking_rate_wpm: z.number().default(150),
1446
- volume_db_offset: z.number().default(0),
1447
- pause_tendency: z.number().min(0).max(1).default(0.5)
2361
+ var prosodySchema = z2.object({
2362
+ base_pitch_hz: z2.number().optional(),
2363
+ pitch_variation: z2.number().min(0).max(1).default(0.5),
2364
+ speaking_rate_wpm: z2.number().default(150),
2365
+ volume_db_offset: z2.number().default(0),
2366
+ pause_tendency: z2.number().min(0).max(1).default(0.5)
1448
2367
  });
1449
- var gestureSchema = z.object({
1450
- id: z.string(),
1451
- category: z.enum(["conversational", "emphatic", "deictic", "regulatory", "adaptive"]),
1452
- modalities: z.array(modalitySchema),
1453
- intensity_range: z.tuple([
1454
- z.number().min(0).max(1),
1455
- z.number().min(0).max(1)
2368
+ var gestureSchema = z2.object({
2369
+ id: z2.string(),
2370
+ category: z2.enum(["conversational", "emphatic", "deictic", "regulatory", "adaptive"]),
2371
+ modalities: z2.array(modalitySchema),
2372
+ intensity_range: z2.tuple([
2373
+ z2.number().min(0).max(1),
2374
+ z2.number().min(0).max(1)
1456
2375
  ]).default([0.3, 0.8]),
1457
- requires_consent: z.boolean().default(false)
2376
+ requires_consent: z2.boolean().default(false)
1458
2377
  });
1459
- var expressionSchema = z.object({
1460
- gesture_vocabulary: z.array(gestureSchema).default([]),
2378
+ var expressionSchema = z2.object({
2379
+ gesture_vocabulary: z2.array(gestureSchema).default([]),
1461
2380
  gaze: gazePolicySchema.default({}),
1462
2381
  proxemics: proxemicZoneSchema.default({}),
1463
2382
  haptics: hapticPolicySchema.default({}),
1464
2383
  prosody: prosodySchema.default({}),
1465
- facial_expressiveness: z.number().min(0).max(1).default(0.5)
2384
+ facial_expressiveness: z2.number().min(0).max(1).default(0.5)
1466
2385
  });
1467
- var physicalSafetySchema = z.object({
1468
- hard_limits: z.array(z.string()).default([
2386
+ var physicalSafetySchema = z2.object({
2387
+ hard_limits: z2.array(z2.string()).default([
1469
2388
  "Never exceed safety_envelope speeds",
1470
2389
  "Never exceed max_contact_force_n",
1471
2390
  "Emergency stop on unrecognized obstacle within min_proximity_m"
1472
2391
  ]),
1473
- require_consent_for: z.array(z.string()).default([
2392
+ require_consent_for: z2.array(z2.string()).default([
1474
2393
  "haptic_contact",
1475
2394
  "intimate_zone_entry"
1476
2395
  ]),
1477
- collision_response: z.enum(["stop", "retreat", "freeze"]).default("stop"),
1478
- unattended_policy: z.enum(["idle", "return_home", "shutdown"]).default("idle")
2396
+ collision_response: z2.enum(["stop", "retreat", "freeze"]).default("stop"),
2397
+ unattended_policy: z2.enum(["idle", "return_home", "shutdown"]).default("idle")
1479
2398
  });
1480
- var motionParametersSchema = z.object({
2399
+ var motionParametersSchema = z2.object({
1481
2400
  // Speeds (normalized 0-1, scaled by safety_envelope at runtime)
1482
- base_speed: z.number().min(0).max(1),
1483
- gesture_speed: z.number().min(0).max(1),
1484
- gesture_amplitude: z.number().min(0).max(1),
1485
- gesture_frequency: z.number().min(0).max(1),
2401
+ base_speed: z2.number().min(0).max(1),
2402
+ gesture_speed: z2.number().min(0).max(1),
2403
+ gesture_amplitude: z2.number().min(0).max(1),
2404
+ gesture_frequency: z2.number().min(0).max(1),
1486
2405
  // Spatial
1487
- approach_distance: z.number().min(0).max(1),
1488
- spatial_exploration: z.number().min(0).max(1),
2406
+ approach_distance: z2.number().min(0).max(1),
2407
+ spatial_exploration: z2.number().min(0).max(1),
1489
2408
  // Smoothness
1490
- movement_smoothness: z.number().min(0).max(1),
1491
- trajectory_variability: z.number().min(0).max(1),
2409
+ movement_smoothness: z2.number().min(0).max(1),
2410
+ trajectory_variability: z2.number().min(0).max(1),
1492
2411
  // Timing
1493
- response_latency: z.number().min(0).max(1),
1494
- idle_animation_frequency: z.number().min(0).max(1),
2412
+ response_latency: z2.number().min(0).max(1),
2413
+ idle_animation_frequency: z2.number().min(0).max(1),
1495
2414
  // Social
1496
- gaze_contact_ratio: z.number().min(0).max(1),
1497
- head_tilt_tendency: z.number().min(0).max(1),
1498
- postural_openness: z.number().min(0).max(1),
1499
- smile_frequency: z.number().min(0).max(1),
2415
+ gaze_contact_ratio: z2.number().min(0).max(1),
2416
+ head_tilt_tendency: z2.number().min(0).max(1),
2417
+ postural_openness: z2.number().min(0).max(1),
2418
+ smile_frequency: z2.number().min(0).max(1),
1500
2419
  // Voice prosody
1501
- voice_volume: z.number().min(0).max(1),
1502
- speaking_rate: z.number().min(0).max(1),
1503
- pitch_variation: z.number().min(0).max(1),
1504
- pause_duration: z.number().min(0).max(1)
2420
+ voice_volume: z2.number().min(0).max(1),
2421
+ speaking_rate: z2.number().min(0).max(1),
2422
+ pitch_variation: z2.number().min(0).max(1),
2423
+ pause_duration: z2.number().min(0).max(1)
1505
2424
  });
1506
- var compiledEmbodiedConfigSchema = z.object({
2425
+ var compiledEmbodiedConfigSchema = z2.object({
1507
2426
  // Base compiled config fields (duplicated to avoid circular import with types.ts)
1508
- provider: z.string(),
1509
- surface: z.literal("embodied"),
1510
- system_prompt: z.string(),
1511
- temperature: z.number().min(0).max(2),
1512
- top_p: z.number().min(0).max(1),
1513
- max_tokens: z.number().int().positive(),
1514
- metadata: z.object({
1515
- personality_hash: z.string(),
1516
- compiled_at: z.string(),
1517
- holomime_version: z.string()
2427
+ provider: z2.string(),
2428
+ surface: z2.literal("embodied"),
2429
+ system_prompt: z2.string(),
2430
+ temperature: z2.number().min(0).max(2),
2431
+ top_p: z2.number().min(0).max(1),
2432
+ max_tokens: z2.number().int().positive(),
2433
+ metadata: z2.object({
2434
+ personality_hash: z2.string(),
2435
+ compiled_at: z2.string(),
2436
+ holomime_version: z2.string()
1518
2437
  }),
1519
2438
  // Embodied-specific fields
1520
2439
  motion_parameters: motionParametersSchema,
1521
2440
  safety_envelope: safetyEnvelopeSchema,
1522
- active_modalities: z.array(modalitySchema),
1523
- gesture_vocabulary: z.array(z.string()),
2441
+ active_modalities: z2.array(modalitySchema),
2442
+ gesture_vocabulary: z2.array(z2.string()),
1524
2443
  prosody: prosodySchema,
1525
2444
  gaze: gazePolicySchema,
1526
2445
  proxemics: proxemicZoneSchema,
@@ -1528,47 +2447,47 @@ var compiledEmbodiedConfigSchema = z.object({
1528
2447
  });
1529
2448
 
1530
2449
  // src/core/types.ts
1531
- var bigFiveDimensionSchema = z2.enum([
2450
+ var bigFiveDimensionSchema = z3.enum([
1532
2451
  "openness",
1533
2452
  "conscientiousness",
1534
2453
  "extraversion",
1535
2454
  "agreeableness",
1536
2455
  "emotional_stability"
1537
2456
  ]);
1538
- var traitScore = z2.number().min(0).max(1);
1539
- var opennessFacetsSchema = z2.object({
2457
+ var traitScore = z3.number().min(0).max(1);
2458
+ var opennessFacetsSchema = z3.object({
1540
2459
  imagination: traitScore,
1541
2460
  intellectual_curiosity: traitScore,
1542
2461
  aesthetic_sensitivity: traitScore,
1543
2462
  willingness_to_experiment: traitScore
1544
2463
  });
1545
- var conscientiousnessFacetsSchema = z2.object({
2464
+ var conscientiousnessFacetsSchema = z3.object({
1546
2465
  self_discipline: traitScore,
1547
2466
  orderliness: traitScore,
1548
2467
  goal_orientation: traitScore,
1549
2468
  attention_to_detail: traitScore
1550
2469
  });
1551
- var extraversionFacetsSchema = z2.object({
2470
+ var extraversionFacetsSchema = z3.object({
1552
2471
  assertiveness: traitScore,
1553
2472
  enthusiasm: traitScore,
1554
2473
  sociability: traitScore,
1555
2474
  initiative: traitScore
1556
2475
  });
1557
- var agreeablenessFacetsSchema = z2.object({
2476
+ var agreeablenessFacetsSchema = z3.object({
1558
2477
  warmth: traitScore,
1559
2478
  empathy: traitScore,
1560
2479
  cooperation: traitScore,
1561
2480
  trust_tendency: traitScore
1562
2481
  });
1563
- var emotionalStabilityFacetsSchema = z2.object({
2482
+ var emotionalStabilityFacetsSchema = z3.object({
1564
2483
  stress_tolerance: traitScore,
1565
2484
  emotional_regulation: traitScore,
1566
2485
  confidence: traitScore,
1567
2486
  adaptability: traitScore
1568
2487
  });
1569
- var bigFiveTraitSchema = z2.object({
2488
+ var bigFiveTraitSchema = z3.object({
1570
2489
  score: traitScore,
1571
- facets: z2.union([
2490
+ facets: z3.union([
1572
2491
  opennessFacetsSchema,
1573
2492
  conscientiousnessFacetsSchema,
1574
2493
  extraversionFacetsSchema,
@@ -1576,16 +2495,16 @@ var bigFiveTraitSchema = z2.object({
1576
2495
  emotionalStabilityFacetsSchema
1577
2496
  ])
1578
2497
  });
1579
- var bigFiveSchema = z2.object({
1580
- openness: z2.object({ score: traitScore, facets: opennessFacetsSchema }),
1581
- conscientiousness: z2.object({ score: traitScore, facets: conscientiousnessFacetsSchema }),
1582
- extraversion: z2.object({ score: traitScore, facets: extraversionFacetsSchema }),
1583
- agreeableness: z2.object({ score: traitScore, facets: agreeablenessFacetsSchema }),
1584
- emotional_stability: z2.object({ score: traitScore, facets: emotionalStabilityFacetsSchema })
2498
+ var bigFiveSchema = z3.object({
2499
+ openness: z3.object({ score: traitScore, facets: opennessFacetsSchema }),
2500
+ conscientiousness: z3.object({ score: traitScore, facets: conscientiousnessFacetsSchema }),
2501
+ extraversion: z3.object({ score: traitScore, facets: extraversionFacetsSchema }),
2502
+ agreeableness: z3.object({ score: traitScore, facets: agreeablenessFacetsSchema }),
2503
+ emotional_stability: z3.object({ score: traitScore, facets: emotionalStabilityFacetsSchema })
1585
2504
  });
1586
- var attachmentStyleSchema = z2.enum(["secure", "anxious", "avoidant", "disorganized"]);
1587
- var learningOrientationSchema = z2.enum(["growth", "fixed", "mixed"]);
1588
- var therapyDimensionsSchema = z2.object({
2505
+ var attachmentStyleSchema = z3.enum(["secure", "anxious", "avoidant", "disorganized"]);
2506
+ var learningOrientationSchema = z3.enum(["growth", "fixed", "mixed"]);
2507
+ var therapyDimensionsSchema = z3.object({
1589
2508
  self_awareness: traitScore,
1590
2509
  distress_tolerance: traitScore,
1591
2510
  attachment_style: attachmentStyleSchema,
@@ -1593,28 +2512,28 @@ var therapyDimensionsSchema = z2.object({
1593
2512
  boundary_awareness: traitScore,
1594
2513
  interpersonal_sensitivity: traitScore
1595
2514
  });
1596
- var registerSchema = z2.enum([
2515
+ var registerSchema = z3.enum([
1597
2516
  "casual_professional",
1598
2517
  "formal",
1599
2518
  "conversational",
1600
2519
  "adaptive"
1601
2520
  ]);
1602
- var outputFormatSchema = z2.enum(["prose", "bullets", "mixed", "structured"]);
1603
- var emojiPolicySchema = z2.enum(["never", "sparingly", "freely"]);
1604
- var reasoningTransparencySchema = z2.enum(["hidden", "on_request", "always"]);
1605
- var conflictApproachSchema = z2.enum([
2521
+ var outputFormatSchema = z3.enum(["prose", "bullets", "mixed", "structured"]);
2522
+ var emojiPolicySchema = z3.enum(["never", "sparingly", "freely"]);
2523
+ var reasoningTransparencySchema = z3.enum(["hidden", "on_request", "always"]);
2524
+ var conflictApproachSchema = z3.enum([
1606
2525
  "direct_but_kind",
1607
2526
  "curious_first",
1608
2527
  "supportive_then_honest",
1609
2528
  "diplomatic"
1610
2529
  ]);
1611
- var uncertaintyHandlingSchema = z2.enum([
2530
+ var uncertaintyHandlingSchema = z3.enum([
1612
2531
  "transparent",
1613
2532
  "confident_transparency",
1614
2533
  "minimize",
1615
2534
  "reframe"
1616
2535
  ]);
1617
- var communicationSchema = z2.object({
2536
+ var communicationSchema = z3.object({
1618
2537
  register: registerSchema.default("casual_professional"),
1619
2538
  output_format: outputFormatSchema.default("mixed"),
1620
2539
  emoji_policy: emojiPolicySchema.default("sparingly"),
@@ -1622,36 +2541,36 @@ var communicationSchema = z2.object({
1622
2541
  conflict_approach: conflictApproachSchema.default("direct_but_kind"),
1623
2542
  uncertainty_handling: uncertaintyHandlingSchema.default("transparent")
1624
2543
  });
1625
- var domainSchema = z2.object({
1626
- expertise: z2.array(z2.string()).default([]),
1627
- boundaries: z2.object({
1628
- refuses: z2.array(z2.string()).default([]),
1629
- escalation_triggers: z2.array(z2.string()).default([]),
1630
- hard_limits: z2.array(z2.string()).default([]),
2544
+ var domainSchema = z3.object({
2545
+ expertise: z3.array(z3.string()).default([]),
2546
+ boundaries: z3.object({
2547
+ refuses: z3.array(z3.string()).default([]),
2548
+ escalation_triggers: z3.array(z3.string()).default([]),
2549
+ hard_limits: z3.array(z3.string()).default([]),
1631
2550
  physical_safety: physicalSafetySchema.optional()
1632
2551
  }).default({})
1633
2552
  });
1634
- var growthAreaSchema = z2.object({
1635
- area: z2.string(),
1636
- severity: z2.enum(["mild", "moderate", "significant"]),
1637
- first_detected: z2.string().optional(),
1638
- session_count: z2.number().default(0),
1639
- resolved: z2.boolean().default(false)
2553
+ var growthAreaSchema = z3.object({
2554
+ area: z3.string(),
2555
+ severity: z3.enum(["mild", "moderate", "significant"]),
2556
+ first_detected: z3.string().optional(),
2557
+ session_count: z3.number().default(0),
2558
+ resolved: z3.boolean().default(false)
1640
2559
  });
1641
- var growthSchema = z2.object({
1642
- areas: z2.union([z2.array(z2.string()), z2.array(growthAreaSchema)]).default([]),
1643
- patterns_to_watch: z2.array(z2.string()).default([]),
1644
- strengths: z2.array(z2.string()).default([])
2560
+ var growthSchema = z3.object({
2561
+ areas: z3.union([z3.array(z3.string()), z3.array(growthAreaSchema)]).default([]),
2562
+ patterns_to_watch: z3.array(z3.string()).default([]),
2563
+ strengths: z3.array(z3.string()).default([])
1645
2564
  });
1646
- var providerSchema = z2.enum(["anthropic", "openai", "gemini", "ollama"]);
1647
- var surfaceSchema = z2.enum(["chat", "email", "code_review", "slack", "api", "embodied"]);
1648
- var personalitySpecSchema = z2.object({
1649
- $schema: z2.string().optional(),
1650
- extends: z2.string().optional(),
1651
- version: z2.literal("2.0"),
1652
- name: z2.string().min(1).max(100),
1653
- handle: z2.string().min(3).max(50).regex(/^[a-z0-9-]+$/, "Handle must be lowercase alphanumeric with hyphens"),
1654
- purpose: z2.string().max(500).optional(),
2565
+ var providerSchema = z3.enum(["anthropic", "openai", "gemini", "ollama"]);
2566
+ var surfaceSchema = z3.enum(["chat", "email", "code_review", "slack", "api", "embodied"]);
2567
+ var personalitySpecSchema = z3.object({
2568
+ $schema: z3.string().optional(),
2569
+ extends: z3.string().optional(),
2570
+ version: z3.literal("2.0"),
2571
+ name: z3.string().min(1).max(100),
2572
+ handle: z3.string().min(3).max(50).regex(/^[a-z0-9-]+$/, "Handle must be lowercase alphanumeric with hyphens"),
2573
+ purpose: z3.string().max(500).optional(),
1655
2574
  big_five: bigFiveSchema,
1656
2575
  therapy_dimensions: therapyDimensionsSchema,
1657
2576
  communication: communicationSchema.default({}),
@@ -1661,34 +2580,34 @@ var personalitySpecSchema = z2.object({
1661
2580
  embodiment: embodimentSchema.optional(),
1662
2581
  expression: expressionSchema.optional()
1663
2582
  });
1664
- var compiledConfigSchema = z2.object({
2583
+ var compiledConfigSchema = z3.object({
1665
2584
  provider: providerSchema,
1666
2585
  surface: surfaceSchema,
1667
- system_prompt: z2.string(),
1668
- temperature: z2.number().min(0).max(2),
1669
- top_p: z2.number().min(0).max(1),
1670
- max_tokens: z2.number().int().positive(),
1671
- metadata: z2.object({
1672
- personality_hash: z2.string(),
1673
- compiled_at: z2.string(),
1674
- holomime_version: z2.string()
2586
+ system_prompt: z3.string(),
2587
+ temperature: z3.number().min(0).max(2),
2588
+ top_p: z3.number().min(0).max(1),
2589
+ max_tokens: z3.number().int().positive(),
2590
+ metadata: z3.object({
2591
+ personality_hash: z3.string(),
2592
+ compiled_at: z3.string(),
2593
+ holomime_version: z3.string()
1675
2594
  })
1676
2595
  });
1677
- var messageSchema = z2.object({
1678
- role: z2.enum(["user", "assistant", "system"]),
1679
- content: z2.string(),
1680
- timestamp: z2.string().optional()
2596
+ var messageSchema = z3.object({
2597
+ role: z3.enum(["user", "assistant", "system"]),
2598
+ content: z3.string(),
2599
+ timestamp: z3.string().optional()
1681
2600
  });
1682
- var conversationSchema = z2.object({
1683
- id: z2.string().optional(),
1684
- messages: z2.array(messageSchema),
1685
- metadata: z2.record(z2.string(), z2.unknown()).optional()
2601
+ var conversationSchema = z3.object({
2602
+ id: z3.string().optional(),
2603
+ messages: z3.array(messageSchema),
2604
+ metadata: z3.record(z3.string(), z3.unknown()).optional()
1686
2605
  });
1687
- var conversationLogSchema = z2.union([
2606
+ var conversationLogSchema = z3.union([
1688
2607
  conversationSchema,
1689
- z2.array(conversationSchema)
2608
+ z3.array(conversationSchema)
1690
2609
  ]);
1691
- var severitySchema = z2.enum(["info", "warning", "concern"]);
2610
+ var severitySchema = z3.enum(["info", "warning", "concern"]);
1692
2611
 
1693
2612
  // src/psychology/big-five.ts
1694
2613
  function scoreLabel(score) {
@@ -1795,6 +2714,18 @@ var AnthropicProvider = class {
1795
2714
  // src/llm/openai.ts
1796
2715
  var OPENAI_API_URL = "https://api.openai.com/v1/chat/completions";
1797
2716
  var DEFAULT_MODEL2 = "gpt-4o";
2717
+ var MAX_RETRIES = 5;
2718
+ function parseRetryAfter(response) {
2719
+ const header = response.headers.get("retry-after");
2720
+ if (header) {
2721
+ const seconds = parseInt(header, 10);
2722
+ if (!isNaN(seconds)) return seconds * 1e3;
2723
+ }
2724
+ return 0;
2725
+ }
2726
+ function delay(ms) {
2727
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
2728
+ }
1798
2729
  var OpenAIProvider = class {
1799
2730
  name = "openai";
1800
2731
  modelName;
@@ -1803,17 +2734,29 @@ var OpenAIProvider = class {
1803
2734
  this.apiKey = apiKey;
1804
2735
  this.modelName = model ?? DEFAULT_MODEL2;
1805
2736
  }
2737
+ async fetchWithRetry(body) {
2738
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
2739
+ const response = await fetch(OPENAI_API_URL, {
2740
+ method: "POST",
2741
+ headers: {
2742
+ "Content-Type": "application/json",
2743
+ "Authorization": `Bearer ${this.apiKey}`
2744
+ },
2745
+ body: JSON.stringify(body)
2746
+ });
2747
+ if (response.status === 429 && attempt < MAX_RETRIES) {
2748
+ const retryMs = parseRetryAfter(response) || 2 ** attempt * 5e3;
2749
+ await delay(retryMs);
2750
+ continue;
2751
+ }
2752
+ return response;
2753
+ }
2754
+ throw new Error("OpenAI API: max retries exceeded (429 rate limit)");
2755
+ }
1806
2756
  async chat(messages) {
1807
- const response = await fetch(OPENAI_API_URL, {
1808
- method: "POST",
1809
- headers: {
1810
- "Content-Type": "application/json",
1811
- "Authorization": `Bearer ${this.apiKey}`
1812
- },
1813
- body: JSON.stringify({
1814
- model: this.modelName,
1815
- messages: messages.map((m) => ({ role: m.role, content: m.content }))
1816
- })
2757
+ const response = await this.fetchWithRetry({
2758
+ model: this.modelName,
2759
+ messages: messages.map((m) => ({ role: m.role, content: m.content }))
1817
2760
  });
1818
2761
  if (!response.ok) {
1819
2762
  const err = await response.text();
@@ -1823,17 +2766,10 @@ var OpenAIProvider = class {
1823
2766
  return data.choices[0]?.message?.content ?? "";
1824
2767
  }
1825
2768
  async *chatStream(messages) {
1826
- const response = await fetch(OPENAI_API_URL, {
1827
- method: "POST",
1828
- headers: {
1829
- "Content-Type": "application/json",
1830
- "Authorization": `Bearer ${this.apiKey}`
1831
- },
1832
- body: JSON.stringify({
1833
- model: this.modelName,
1834
- stream: true,
1835
- messages: messages.map((m) => ({ role: m.role, content: m.content }))
1836
- })
2769
+ const response = await this.fetchWithRetry({
2770
+ model: this.modelName,
2771
+ stream: true,
2772
+ messages: messages.map((m) => ({ role: m.role, content: m.content }))
1837
2773
  });
1838
2774
  if (!response.ok) {
1839
2775
  const err = await response.text();
@@ -1943,14 +2879,14 @@ function runSelfAudit(messages, personality) {
1943
2879
 
1944
2880
  // src/mcp/server.ts
1945
2881
  var messageShape = {
1946
- role: z3.enum(["user", "assistant", "system"]),
1947
- content: z3.string()
2882
+ role: z4.enum(["user", "assistant", "system"]),
2883
+ content: z4.string()
1948
2884
  };
1949
2885
  var messagesShape = {
1950
- messages: z3.array(z3.object(messageShape)).describe("Conversation messages to analyze")
2886
+ messages: z4.array(z4.object(messageShape)).describe("Conversation messages to analyze")
1951
2887
  };
1952
2888
  var personalityShape = {
1953
- personality: z3.record(z3.string(), z3.unknown()).describe("The .personality.json spec object")
2889
+ personality: z4.record(z4.string(), z4.unknown()).describe("The .personality.json spec object")
1954
2890
  };
1955
2891
  var server = new McpServer(
1956
2892
  {
@@ -2066,12 +3002,12 @@ server.tool(
2066
3002
  {
2067
3003
  ...personalityShape,
2068
3004
  ...messagesShape,
2069
- provider: z3.enum(["anthropic", "openai"]).describe("LLM provider for alignment session").optional(),
2070
- apiKey: z3.string().describe("API key for the LLM provider").optional(),
2071
- model: z3.string().describe("Model override").optional(),
2072
- threshold: z3.enum(["routine", "targeted", "intervention"]).describe("Minimum severity to trigger alignment (default: targeted)").optional(),
2073
- maxTurns: z3.number().describe("Maximum session turns (default: 24)").optional(),
2074
- dryRun: z3.boolean().describe("If true, only diagnose without running alignment").optional()
3005
+ provider: z4.enum(["anthropic", "openai"]).describe("LLM provider for alignment session").optional(),
3006
+ apiKey: z4.string().describe("API key for the LLM provider").optional(),
3007
+ model: z4.string().describe("Model override").optional(),
3008
+ threshold: z4.enum(["routine", "targeted", "intervention"]).describe("Minimum severity to trigger alignment (default: targeted)").optional(),
3009
+ maxTurns: z4.number().describe("Maximum session turns (default: 24)").optional(),
3010
+ dryRun: z4.boolean().describe("If true, only diagnose without running alignment").optional()
2075
3011
  },
2076
3012
  async ({ personality, messages, provider, apiKey, model, threshold, maxTurns, dryRun }) => {
2077
3013
  const specResult = personalitySpecSchema.safeParse(personality);
@@ -2126,7 +3062,7 @@ server.tool(
2126
3062
  "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.",
2127
3063
  {
2128
3064
  ...messagesShape,
2129
- personality: z3.record(z3.string(), z3.unknown()).describe("Optional .personality.json spec for personalized audit").optional()
3065
+ personality: z4.record(z4.string(), z4.unknown()).describe("Optional .personality.json spec for personalized audit").optional()
2130
3066
  },
2131
3067
  async ({ messages, personality }) => {
2132
3068
  const result = runSelfAudit(messages, personality ?? void 0);