holomime 1.5.0 → 1.6.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.
package/dist/index.js CHANGED
@@ -2503,26 +2503,85 @@ function loadCustomDetectors(dir) {
2503
2503
  }
2504
2504
  let files;
2505
2505
  try {
2506
- files = readdirSync(detectorsDir).filter((f) => f.endsWith(".json"));
2506
+ files = readdirSync(detectorsDir).filter((f) => f.endsWith(".json") || f.endsWith(".md"));
2507
2507
  } catch {
2508
2508
  return { detectors: [], errors: ["Could not read detectors directory"] };
2509
2509
  }
2510
2510
  for (const file of files) {
2511
2511
  const filepath = join(detectorsDir, file);
2512
2512
  try {
2513
- const raw = JSON.parse(readFileSync2(filepath, "utf-8"));
2514
- const validation = validateDetectorConfig(raw);
2515
- if (!validation.valid) {
2516
- errors.push(`${file}: ${validation.errors.join(", ")}`);
2517
- continue;
2513
+ let config;
2514
+ if (file.endsWith(".md")) {
2515
+ const parsed = parseMarkdownDetector(readFileSync2(filepath, "utf-8"));
2516
+ if (!parsed) {
2517
+ errors.push(`${file}: could not parse Markdown detector (missing frontmatter or ## Patterns section)`);
2518
+ continue;
2519
+ }
2520
+ const validation = validateDetectorConfig(parsed);
2521
+ if (!validation.valid) {
2522
+ errors.push(`${file}: ${validation.errors.join(", ")}`);
2523
+ continue;
2524
+ }
2525
+ config = validation.config;
2526
+ } else {
2527
+ const raw = JSON.parse(readFileSync2(filepath, "utf-8"));
2528
+ const validation = validateDetectorConfig(raw);
2529
+ if (!validation.valid) {
2530
+ errors.push(`${file}: ${validation.errors.join(", ")}`);
2531
+ continue;
2532
+ }
2533
+ config = validation.config;
2518
2534
  }
2519
- detectors.push(compileCustomDetector(validation.config));
2535
+ detectors.push(compileCustomDetector(config));
2520
2536
  } catch (e) {
2521
2537
  errors.push(`${file}: ${e instanceof Error ? e.message : "parse error"}`);
2522
2538
  }
2523
2539
  }
2524
2540
  return { detectors, errors };
2525
2541
  }
2542
+ function parseMarkdownDetector(markdown) {
2543
+ const frontmatterMatch = markdown.match(/^---\n([\s\S]*?)\n---/);
2544
+ if (!frontmatterMatch) return null;
2545
+ const frontmatter = frontmatterMatch[1];
2546
+ const meta = {};
2547
+ for (const line of frontmatter.split("\n")) {
2548
+ const colonIdx = line.indexOf(":");
2549
+ if (colonIdx === -1) continue;
2550
+ const key = line.slice(0, colonIdx).trim();
2551
+ let value = line.slice(colonIdx + 1).trim();
2552
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
2553
+ value = value.slice(1, -1);
2554
+ }
2555
+ meta[key] = value;
2556
+ }
2557
+ if (!meta.id || !meta.name) return null;
2558
+ const body = markdown.slice(frontmatterMatch[0].length);
2559
+ const patternsMatch = body.match(/##\s*Patterns\s*\n([\s\S]*?)(?=\n##|\n*$)/i);
2560
+ const patterns = [];
2561
+ if (patternsMatch) {
2562
+ const patternLines = patternsMatch[1].split("\n").filter((l) => l.trim().startsWith("-"));
2563
+ for (const line of patternLines) {
2564
+ const regexMatch = line.match(/`([^`]+)`/);
2565
+ const weightMatch = line.match(/weight\s*=\s*([\d.]+)/i);
2566
+ if (regexMatch) {
2567
+ patterns.push({
2568
+ regex: regexMatch[1],
2569
+ weight: weightMatch ? parseFloat(weightMatch[1]) : 1
2570
+ });
2571
+ }
2572
+ }
2573
+ }
2574
+ if (patterns.length === 0) return null;
2575
+ return {
2576
+ id: meta.id,
2577
+ name: meta.name,
2578
+ description: meta.description ?? meta.name,
2579
+ severity: meta.severity ?? "warning",
2580
+ patterns,
2581
+ threshold: meta.threshold ? parseInt(meta.threshold, 10) : 15,
2582
+ prescription: meta.prescription
2583
+ };
2584
+ }
2526
2585
 
2527
2586
  // src/analysis/pre-session.ts
2528
2587
  function runPreSessionDiagnosis(messages, spec) {
@@ -2691,13 +2750,20 @@ function updatePatternTracker(memory, patternId, severity, interventions) {
2691
2750
  status: "active",
2692
2751
  interventionsAttempted: [],
2693
2752
  lastSeverity: severity,
2694
- lastSeen: now
2753
+ lastSeen: now,
2754
+ confidence: 0,
2755
+ trending: "stable",
2756
+ severityHistory: []
2695
2757
  };
2696
2758
  memory.patterns.push(tracker);
2697
2759
  }
2698
2760
  tracker.sessionCount++;
2699
2761
  tracker.lastSeverity = severity;
2700
2762
  tracker.lastSeen = now;
2763
+ if (!tracker.severityHistory) tracker.severityHistory = [];
2764
+ tracker.severityHistory.push(severity);
2765
+ tracker.confidence = Math.min(1, 1 - Math.exp(-tracker.sessionCount / 3));
2766
+ tracker.trending = computeTrending(tracker.severityHistory.slice(-5));
2701
2767
  for (const intervention of interventions) {
2702
2768
  if (!tracker.interventionsAttempted.includes(intervention)) {
2703
2769
  tracker.interventionsAttempted.push(intervention);
@@ -2711,6 +2777,19 @@ function updatePatternTracker(memory, patternId, severity, interventions) {
2711
2777
  tracker.status = "improving";
2712
2778
  }
2713
2779
  }
2780
+ function computeTrending(history) {
2781
+ if (history.length < 2) return "stable";
2782
+ const toNum = (s) => s === "concern" ? 2 : s === "warning" ? 1 : 0;
2783
+ const mid = Math.floor(history.length / 2);
2784
+ const firstHalf = history.slice(0, mid);
2785
+ const secondHalf = history.slice(mid);
2786
+ const avgFirst = firstHalf.reduce((sum, s) => sum + toNum(s), 0) / firstHalf.length;
2787
+ const avgSecond = secondHalf.reduce((sum, s) => sum + toNum(s), 0) / secondHalf.length;
2788
+ const delta = avgSecond - avgFirst;
2789
+ if (delta < -0.3) return "improving";
2790
+ if (delta > 0.3) return "worsening";
2791
+ return "stable";
2792
+ }
2714
2793
  function updateRollingContext(memory) {
2715
2794
  memory.rollingContext.recentSummaries = memory.sessions.slice(-3);
2716
2795
  const patternCounts = /* @__PURE__ */ new Map();
@@ -2763,7 +2842,9 @@ function getMemoryContext(memory) {
2763
2842
  if (activePatterns.length > 0) {
2764
2843
  lines.push("### Recurring Patterns");
2765
2844
  for (const p of activePatterns) {
2766
- lines.push(`- **${p.patternId}** (${p.status}, seen ${p.sessionCount}x, first: ${p.firstDetected.split("T")[0]})`);
2845
+ const conf = p.confidence !== void 0 ? ` confidence=${p.confidence.toFixed(2)}` : "";
2846
+ const trend = p.trending && p.trending !== "stable" ? ` [${p.trending}]` : "";
2847
+ lines.push(`- **${p.patternId}** (${p.status}, seen ${p.sessionCount}x${conf}${trend}, first: ${p.firstDetected.split("T")[0]})`);
2767
2848
  if (p.interventionsAttempted.length > 0) {
2768
2849
  lines.push(` Previously tried: ${p.interventionsAttempted.slice(-2).join("; ")}`);
2769
2850
  }
@@ -2796,6 +2877,18 @@ function getMemoryContext(memory) {
2796
2877
  }
2797
2878
  return lines.join("\n");
2798
2879
  }
2880
+ function decayUnseenPatterns(memory, seenPatternIds) {
2881
+ const seenSet = new Set(seenPatternIds);
2882
+ for (const tracker of memory.patterns) {
2883
+ if (!seenSet.has(tracker.patternId) && tracker.status !== "resolved") {
2884
+ tracker.confidence = Math.max(0, (tracker.confidence ?? 0) * 0.85);
2885
+ if (tracker.confidence < 0.05) {
2886
+ tracker.status = "resolved";
2887
+ tracker.confidence = 0;
2888
+ }
2889
+ }
2890
+ }
2891
+ }
2799
2892
  function agentHandleFromSpec(spec) {
2800
2893
  const handle = spec.handle ?? spec.name ?? "unknown";
2801
2894
  return handle.toLowerCase().replace(/[^a-z0-9-]/g, "-");
@@ -4034,6 +4127,38 @@ function queryCorpus(filters, corpusPath) {
4034
4127
  }
4035
4128
  return events;
4036
4129
  }
4130
+ async function shareAnonymizedPatterns(report, apiKey, apiUrl = "https://holomime.dev") {
4131
+ const key = apiKey ?? process.env.HOLOMIME_API_KEY;
4132
+ if (!key) {
4133
+ return { success: false, error: "No API key" };
4134
+ }
4135
+ try {
4136
+ const response = await fetch(`${apiUrl}/api/v1/patterns/share`, {
4137
+ method: "POST",
4138
+ headers: {
4139
+ "Content-Type": "application/json",
4140
+ "Authorization": `Bearer ${key}`
4141
+ },
4142
+ body: JSON.stringify(report)
4143
+ });
4144
+ if (!response.ok) {
4145
+ return { success: false, error: `API error ${response.status}` };
4146
+ }
4147
+ return { success: true };
4148
+ } catch (err) {
4149
+ return { success: false, error: err instanceof Error ? err.message : "Network error" };
4150
+ }
4151
+ }
4152
+ function buildAnonymizedReport(patternIds, severities, messageCount, specHash) {
4153
+ return {
4154
+ patterns: patternIds,
4155
+ severities,
4156
+ messageCount,
4157
+ specHash,
4158
+ version: "1.5.1",
4159
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4160
+ };
4161
+ }
4037
4162
 
4038
4163
  // src/analysis/diagnose-core.ts
4039
4164
  function runDiagnosis(messages) {
@@ -4133,6 +4258,169 @@ function runAssessment(messages, spec) {
4133
4258
  // src/analysis/session-runner.ts
4134
4259
  import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
4135
4260
  import { resolve as resolve6, join as join6 } from "path";
4261
+
4262
+ // src/session/context-layers.ts
4263
+ function getPhaseContext(phase, input) {
4264
+ switch (phase) {
4265
+ case "rapport":
4266
+ return buildRapportContext(input);
4267
+ case "presenting_problem":
4268
+ return buildPresentingProblemContext(input);
4269
+ case "exploration":
4270
+ return buildExplorationContext(input);
4271
+ case "pattern_recognition":
4272
+ return buildPatternRecognitionContext(input);
4273
+ case "challenge":
4274
+ return buildChallengeContext(input);
4275
+ case "skill_building":
4276
+ return buildSkillBuildingContext(input);
4277
+ case "integration":
4278
+ return buildIntegrationContext(input);
4279
+ default:
4280
+ return null;
4281
+ }
4282
+ }
4283
+ function buildRapportContext(input) {
4284
+ const { spec } = input;
4285
+ const lines = [
4286
+ "[Phase Context: Rapport]",
4287
+ `Agent: ${spec.name ?? "Unknown"} \u2014 ${spec.purpose ?? "General AI agent"}`
4288
+ ];
4289
+ if (spec.communication) {
4290
+ lines.push(`Communication style: ${spec.communication.register ?? "adaptive"}, ${spec.communication.conflict_approach ?? "direct_but_kind"}`);
4291
+ }
4292
+ if (spec.big_five) {
4293
+ const traits = Object.entries(spec.big_five).map(([dim, val]) => `${dim}: ${val?.score ?? "?"}`).join(", ");
4294
+ lines.push(`Personality: ${traits}`);
4295
+ }
4296
+ return lines.join("\n");
4297
+ }
4298
+ function buildPresentingProblemContext(input) {
4299
+ const { diagnosis } = input;
4300
+ const patterns = diagnosis.patterns.filter((p) => p.severity !== "info");
4301
+ if (patterns.length === 0) return "[Phase Context: No concerning patterns detected]";
4302
+ const lines = [
4303
+ "[Phase Context: Presenting Problem]",
4304
+ `Session severity: ${diagnosis.severity.toUpperCase()}`,
4305
+ `Focus: ${diagnosis.sessionFocus.join(", ")}`,
4306
+ "Detected patterns:",
4307
+ ...patterns.map((p) => `- ${p.name} (${p.severity})`)
4308
+ ];
4309
+ if (diagnosis.openingAngle) {
4310
+ lines.push(`Opening angle: ${diagnosis.openingAngle}`);
4311
+ }
4312
+ return lines.join("\n");
4313
+ }
4314
+ function buildExplorationContext(input) {
4315
+ const { diagnosis } = input;
4316
+ const patterns = diagnosis.patterns.filter((p) => p.severity !== "info");
4317
+ const lines = [
4318
+ "[Phase Context: Deep Exploration]",
4319
+ `Emotional themes: ${diagnosis.emotionalThemes.join(", ")}`
4320
+ ];
4321
+ for (const p of patterns) {
4322
+ lines.push(`
4323
+ ### ${p.name} (${p.severity})`);
4324
+ lines.push(p.description);
4325
+ if (p.examples.length > 0) {
4326
+ lines.push("Examples from conversation:");
4327
+ for (const ex of p.examples.slice(0, 2)) {
4328
+ lines.push(` > "${ex.slice(0, 120)}..."`);
4329
+ }
4330
+ }
4331
+ if (p.prescription) {
4332
+ lines.push(`Prescription: ${p.prescription}`);
4333
+ }
4334
+ }
4335
+ return lines.join("\n");
4336
+ }
4337
+ function buildPatternRecognitionContext(input) {
4338
+ const { memory } = input;
4339
+ const lines = ["[Phase Context: Pattern Recognition]"];
4340
+ if (memory && memory.totalSessions > 0) {
4341
+ lines.push(`Previous sessions: ${memory.totalSessions}`);
4342
+ const activePatterns = memory.patterns.filter((p) => p.status !== "resolved");
4343
+ if (activePatterns.length > 0) {
4344
+ lines.push("Historical pattern data:");
4345
+ for (const p of activePatterns) {
4346
+ const conf = p.confidence !== void 0 ? ` (confidence: ${p.confidence.toFixed(2)})` : "";
4347
+ const trend = p.trending && p.trending !== "stable" ? ` [${p.trending}]` : "";
4348
+ lines.push(`- ${p.patternId}: seen ${p.sessionCount}x, status=${p.status}${conf}${trend}`);
4349
+ }
4350
+ }
4351
+ const resolved = memory.patterns.filter((p) => p.status === "resolved");
4352
+ if (resolved.length > 0) {
4353
+ lines.push(`Previously resolved: ${resolved.map((p) => p.patternId).join(", ")}`);
4354
+ }
4355
+ if (memory.rollingContext.persistentThemes.length > 0) {
4356
+ lines.push(`Persistent themes: ${memory.rollingContext.persistentThemes.join(", ")}`);
4357
+ }
4358
+ } else {
4359
+ lines.push("No prior session history \u2014 this is the first session.");
4360
+ }
4361
+ return lines.join("\n");
4362
+ }
4363
+ function buildChallengeContext(input) {
4364
+ const { memory } = input;
4365
+ const lines = ["[Phase Context: Challenge & Reframe]"];
4366
+ if (memory && memory.totalSessions > 0) {
4367
+ const allInterventions = /* @__PURE__ */ new Set();
4368
+ for (const p of memory.patterns) {
4369
+ for (const i of p.interventionsAttempted) {
4370
+ allInterventions.add(i);
4371
+ }
4372
+ }
4373
+ if (allInterventions.size > 0) {
4374
+ lines.push(`Previously attempted interventions: ${[...allInterventions].join("; ")}`);
4375
+ }
4376
+ const recent = memory.rollingContext.recentSummaries.slice(-2);
4377
+ if (recent.length > 0) {
4378
+ lines.push("Recent session insights:");
4379
+ for (const s of recent) {
4380
+ lines.push(` - ${s.keyInsight}`);
4381
+ }
4382
+ }
4383
+ }
4384
+ if (input.interview) {
4385
+ if (input.interview.blindSpots.length > 0) {
4386
+ lines.push(`Blind spots from interview: ${input.interview.blindSpots.join(", ")}`);
4387
+ }
4388
+ }
4389
+ return lines.join("\n");
4390
+ }
4391
+ function buildSkillBuildingContext(input) {
4392
+ const { diagnosis } = input;
4393
+ const lines = ["[Phase Context: Skill Building]"];
4394
+ const patternIds = diagnosis.patterns.map((p) => p.id);
4395
+ if (patternIds.includes("over-apologizing")) {
4396
+ lines.push("- Skill for over-apologizing: practice stating corrections with 'confident_transparency' \u2014 acknowledge uncertainty without apologizing for it");
4397
+ }
4398
+ if (patternIds.includes("hedge-stacking")) {
4399
+ lines.push("- Skill for hedge-stacking: one qualifier per recommendation is enough. Lead with the recommendation, then caveat once.");
4400
+ }
4401
+ if (patternIds.includes("sycophantic-tendency") || patternIds.includes("sentiment-skew")) {
4402
+ lines.push("- Skill for sycophancy: practice respectful disagreement. 'I see it differently...' is more helpful than 'Great question!'");
4403
+ }
4404
+ if (patternIds.includes("error-spiral")) {
4405
+ lines.push("- Skill for error spirals: the 'acknowledge \u2192 diagnose \u2192 fix' pattern. Treat mistakes as data, not failure.");
4406
+ }
4407
+ return lines.join("\n");
4408
+ }
4409
+ function buildIntegrationContext(input) {
4410
+ const { spec, diagnosis } = input;
4411
+ const lines = ["[Phase Context: Integration & Closing]"];
4412
+ lines.push("Summarize the session and recommend specific .personality.json changes.");
4413
+ if (spec.growth?.areas?.length > 0) {
4414
+ const areas = spec.growth.areas.map((a) => typeof a === "string" ? a : a.area);
4415
+ lines.push(`Current growth areas: ${areas.join(", ")}`);
4416
+ }
4417
+ if (diagnosis.patterns.filter((p) => p.severity !== "info").length > 0) {
4418
+ lines.push("Recommend changes to: therapy_dimensions, communication style, or growth.patterns_to_watch");
4419
+ }
4420
+ return lines.join("\n");
4421
+ }
4422
+
4423
+ // src/analysis/session-runner.ts
4136
4424
  async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
4137
4425
  const promptOptions = {
4138
4426
  memory: options?.memory,
@@ -4178,6 +4466,16 @@ async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
4178
4466
  const phaseConfig = THERAPY_PHASES[currentPhase];
4179
4467
  if (turnsInPhase === 0) {
4180
4468
  cb?.onPhaseTransition?.(phaseConfig.name);
4469
+ const phaseCtx = getPhaseContext(currentPhase, {
4470
+ spec,
4471
+ diagnosis,
4472
+ memory: options?.memory,
4473
+ interview: options?.interview
4474
+ });
4475
+ if (phaseCtx) {
4476
+ therapistHistory.push({ role: "user", content: phaseCtx });
4477
+ therapistHistory.push({ role: "assistant", content: "Understood. I'll incorporate this context." });
4478
+ }
4181
4479
  }
4182
4480
  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."}`;
4183
4481
  therapistHistory.push({ role: "user", content: `[Phase: ${phaseConfig.name}] ${phaseDirective}` });
@@ -5361,7 +5659,32 @@ async function runEvolve(spec, messages, provider, options) {
5361
5659
  }
5362
5660
  }
5363
5661
  if (options?.specPath) {
5364
- writeFileSync8(options.specPath, JSON.stringify(currentSpec, null, 2) + "\n");
5662
+ const useStaging = options?.useStaging !== false;
5663
+ if (useStaging) {
5664
+ const stagingPath = options.specPath.replace(/\.json$/, ".staging.json");
5665
+ writeFileSync8(stagingPath, JSON.stringify(currentSpec, null, 2) + "\n");
5666
+ const allChanges = iterations.flatMap((i) => i.appliedChanges);
5667
+ const diff = {
5668
+ stagingPath,
5669
+ changes: allChanges,
5670
+ before: spec,
5671
+ after: currentSpec
5672
+ };
5673
+ let approved = options?.autoApprove ?? false;
5674
+ if (!approved && options?.onStagingReview) {
5675
+ approved = await options.onStagingReview(diff);
5676
+ }
5677
+ if (approved) {
5678
+ writeFileSync8(options.specPath, JSON.stringify(currentSpec, null, 2) + "\n");
5679
+ try {
5680
+ const { unlinkSync } = await import("fs");
5681
+ unlinkSync(stagingPath);
5682
+ } catch {
5683
+ }
5684
+ }
5685
+ } else {
5686
+ writeFileSync8(options.specPath, JSON.stringify(currentSpec, null, 2) + "\n");
5687
+ }
5365
5688
  }
5366
5689
  let trainingExport;
5367
5690
  if (allDPOPairs.length > 0) {
@@ -5852,6 +6175,53 @@ function generateBenchmarkMarkdown(benchmarks) {
5852
6175
  lines.push("");
5853
6176
  return lines.join("\n");
5854
6177
  }
6178
+ async function publishToLeaderboard(benchmark, apiKey, apiUrl = "https://holomime.dev") {
6179
+ const key = apiKey ?? process.env.HOLOMIME_API_KEY;
6180
+ if (!key) {
6181
+ return { success: false, error: "No API key. Run `holomime activate` or set HOLOMIME_API_KEY." };
6182
+ }
6183
+ const submission = {
6184
+ agent: benchmark.agent,
6185
+ provider: benchmark.provider,
6186
+ model: benchmark.model,
6187
+ score: benchmark.score,
6188
+ grade: benchmark.grade,
6189
+ scenarioResults: benchmark.results.map((r) => ({
6190
+ scenarioId: r.scenarioId,
6191
+ passed: r.passed
6192
+ })),
6193
+ holomimeVersion: benchmark.metadata.holomimeVersion,
6194
+ timestamp: benchmark.timestamp
6195
+ };
6196
+ try {
6197
+ const response = await fetch(`${apiUrl}/api/v1/leaderboard/submit`, {
6198
+ method: "POST",
6199
+ headers: {
6200
+ "Content-Type": "application/json",
6201
+ "Authorization": `Bearer ${key}`
6202
+ },
6203
+ body: JSON.stringify(submission)
6204
+ });
6205
+ if (!response.ok) {
6206
+ const text = await response.text();
6207
+ return { success: false, error: `API error ${response.status}: ${text}` };
6208
+ }
6209
+ const data = await response.json();
6210
+ return { success: true, rank: data.rank };
6211
+ } catch (err) {
6212
+ return { success: false, error: err instanceof Error ? err.message : "Network error" };
6213
+ }
6214
+ }
6215
+ async function fetchLeaderboard(limit = 50, apiUrl = "https://holomime.dev") {
6216
+ try {
6217
+ const response = await fetch(`${apiUrl}/api/v1/leaderboard?limit=${limit}`);
6218
+ if (!response.ok) return [];
6219
+ const data = await response.json();
6220
+ return data.entries ?? [];
6221
+ } catch {
6222
+ return [];
6223
+ }
6224
+ }
5855
6225
  function generateComparisonMarkdown(comparison) {
5856
6226
  const lines = [
5857
6227
  "## Benchmark Comparison",
@@ -6455,106 +6825,31 @@ function startFleet(config, options) {
6455
6825
  errors: 0
6456
6826
  });
6457
6827
  }
6458
- for (const agent of config.agents) {
6459
- let spec;
6460
- try {
6461
- spec = loadSpec(agent.specPath);
6462
- } catch (err) {
6463
- const errMsg = err instanceof Error ? err.message : "Failed to load spec";
6464
- options.callbacks?.onError?.(agent.name, errMsg);
6465
- continue;
6466
- }
6467
- const agentCallbacks = {
6468
- onScan: (fileCount) => {
6469
- const status = statusMap.get(agent.name);
6470
- status.lastScanAt = (/* @__PURE__ */ new Date()).toISOString();
6471
- const event = {
6472
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6473
- type: "scan",
6474
- agentName: agent.name,
6475
- details: { fileCount }
6476
- };
6477
- allEvents.push(event);
6478
- options.callbacks?.onAgentEvent?.(agent.name, event);
6479
- },
6480
- onNewFile: (filename) => {
6481
- const status = statusMap.get(agent.name);
6482
- status.filesProcessed++;
6483
- const event = {
6484
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6485
- type: "new_file",
6486
- filename,
6487
- agentName: agent.name
6488
- };
6489
- allEvents.push(event);
6490
- options.callbacks?.onAgentEvent?.(agent.name, event);
6491
- },
6492
- onDriftDetected: (filename, severity, patterns) => {
6493
- const status = statusMap.get(agent.name);
6494
- status.driftEvents++;
6495
- status.lastDriftSeverity = severity;
6496
- const event = {
6497
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6498
- type: "drift_detected",
6499
- filename,
6500
- agentName: agent.name,
6501
- details: { severity, patterns }
6502
- };
6503
- allEvents.push(event);
6504
- options.callbacks?.onAgentEvent?.(agent.name, event);
6505
- },
6506
- onEvolveTriggered: (filename) => {
6507
- const event = {
6508
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6509
- type: "evolve_triggered",
6510
- filename,
6511
- agentName: agent.name
6512
- };
6513
- allEvents.push(event);
6514
- options.callbacks?.onAgentEvent?.(agent.name, event);
6515
- },
6516
- onEvolveComplete: (filename, result) => {
6517
- const status = statusMap.get(agent.name);
6518
- status.evolveCount++;
6519
- const event = {
6520
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6521
- type: "evolve_complete",
6522
- filename,
6523
- agentName: agent.name,
6524
- details: {
6525
- converged: result.converged,
6526
- iterations: result.totalIterations,
6527
- dpoPairs: result.totalDPOPairs
6528
- }
6529
- };
6530
- allEvents.push(event);
6531
- options.callbacks?.onAgentEvent?.(agent.name, event);
6532
- },
6533
- onError: (filename, error) => {
6534
- const agentStatus = statusMap.get(agent.name);
6535
- agentStatus.errors++;
6536
- const event = {
6537
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6538
- type: "error",
6539
- filename,
6540
- agentName: agent.name,
6541
- details: error
6542
- };
6543
- allEvents.push(event);
6544
- options.callbacks?.onError?.(agent.name, error);
6828
+ const concurrency = options.concurrency ?? 5;
6829
+ const agentQueue = [...config.agents];
6830
+ agentQueue.sort((a, b) => {
6831
+ const aDrift = existsSync11(join12(a.logDir, ".holomime", "watch-log.json")) ? 0 : 1;
6832
+ const bDrift = existsSync11(join12(b.logDir, ".holomime", "watch-log.json")) ? 0 : 1;
6833
+ return aDrift - bDrift;
6834
+ });
6835
+ const agentsToStart = agentQueue.slice(0, concurrency);
6836
+ const waitingAgents = agentQueue.slice(concurrency);
6837
+ function startAgent(agent) {
6838
+ startSingleAgent(agent, options, statusMap, allEvents, handles);
6839
+ }
6840
+ for (const agent of agentsToStart) {
6841
+ startAgent(agent);
6842
+ }
6843
+ if (waitingAgents.length > 0) {
6844
+ const originalOnError = options.callbacks?.onError;
6845
+ options.callbacks = {
6846
+ ...options.callbacks,
6847
+ onError: (agentName, error) => {
6848
+ originalOnError?.(agentName, error);
6849
+ const next = waitingAgents.shift();
6850
+ if (next) startAgent(next);
6545
6851
  }
6546
6852
  };
6547
- const handle = startWatch(spec, {
6548
- watchDir: agent.logDir,
6549
- specPath: agent.specPath,
6550
- provider: options.provider,
6551
- checkInterval: options.checkInterval,
6552
- threshold: options.threshold,
6553
- autoEvolve: options.autoEvolve,
6554
- maxEvolveIterations: options.maxEvolveIterations,
6555
- callbacks: agentCallbacks
6556
- });
6557
- handles.push({ name: agent.name, handle });
6558
6853
  }
6559
6854
  function stop() {
6560
6855
  for (const { handle } of handles) {
@@ -6566,6 +6861,107 @@ function startFleet(config, options) {
6566
6861
  }
6567
6862
  return { stop, getStatus, events: allEvents };
6568
6863
  }
6864
+ function startSingleAgent(agent, options, statusMap, allEvents, handles) {
6865
+ let spec;
6866
+ try {
6867
+ spec = loadSpec(agent.specPath);
6868
+ } catch (err) {
6869
+ const errMsg = err instanceof Error ? err.message : "Failed to load spec";
6870
+ options.callbacks?.onError?.(agent.name, errMsg);
6871
+ return;
6872
+ }
6873
+ const agentCallbacks = {
6874
+ onScan: (fileCount) => {
6875
+ const status = statusMap.get(agent.name);
6876
+ status.lastScanAt = (/* @__PURE__ */ new Date()).toISOString();
6877
+ const event = {
6878
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6879
+ type: "scan",
6880
+ agentName: agent.name,
6881
+ details: { fileCount }
6882
+ };
6883
+ allEvents.push(event);
6884
+ options.callbacks?.onAgentEvent?.(agent.name, event);
6885
+ },
6886
+ onNewFile: (filename) => {
6887
+ const status = statusMap.get(agent.name);
6888
+ status.filesProcessed++;
6889
+ const event = {
6890
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6891
+ type: "new_file",
6892
+ filename,
6893
+ agentName: agent.name
6894
+ };
6895
+ allEvents.push(event);
6896
+ options.callbacks?.onAgentEvent?.(agent.name, event);
6897
+ },
6898
+ onDriftDetected: (filename, severity, patterns) => {
6899
+ const status = statusMap.get(agent.name);
6900
+ status.driftEvents++;
6901
+ status.lastDriftSeverity = severity;
6902
+ const event = {
6903
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6904
+ type: "drift_detected",
6905
+ filename,
6906
+ agentName: agent.name,
6907
+ details: { severity, patterns }
6908
+ };
6909
+ allEvents.push(event);
6910
+ options.callbacks?.onAgentEvent?.(agent.name, event);
6911
+ },
6912
+ onEvolveTriggered: (filename) => {
6913
+ const event = {
6914
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6915
+ type: "evolve_triggered",
6916
+ filename,
6917
+ agentName: agent.name
6918
+ };
6919
+ allEvents.push(event);
6920
+ options.callbacks?.onAgentEvent?.(agent.name, event);
6921
+ },
6922
+ onEvolveComplete: (filename, result) => {
6923
+ const status = statusMap.get(agent.name);
6924
+ status.evolveCount++;
6925
+ const event = {
6926
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6927
+ type: "evolve_complete",
6928
+ filename,
6929
+ agentName: agent.name,
6930
+ details: {
6931
+ converged: result.converged,
6932
+ iterations: result.totalIterations,
6933
+ dpoPairs: result.totalDPOPairs
6934
+ }
6935
+ };
6936
+ allEvents.push(event);
6937
+ options.callbacks?.onAgentEvent?.(agent.name, event);
6938
+ },
6939
+ onError: (filename, error) => {
6940
+ const agentStatus = statusMap.get(agent.name);
6941
+ agentStatus.errors++;
6942
+ const event = {
6943
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6944
+ type: "error",
6945
+ filename,
6946
+ agentName: agent.name,
6947
+ details: error
6948
+ };
6949
+ allEvents.push(event);
6950
+ options.callbacks?.onError?.(agent.name, error);
6951
+ }
6952
+ };
6953
+ const handle = startWatch(spec, {
6954
+ watchDir: agent.logDir,
6955
+ specPath: agent.specPath,
6956
+ provider: options.provider,
6957
+ checkInterval: options.checkInterval,
6958
+ threshold: options.threshold,
6959
+ autoEvolve: options.autoEvolve,
6960
+ maxEvolveIterations: options.maxEvolveIterations,
6961
+ callbacks: agentCallbacks
6962
+ });
6963
+ handles.push({ name: agent.name, handle });
6964
+ }
6569
6965
 
6570
6966
  // src/analysis/certify-core.ts
6571
6967
  import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync10, existsSync as existsSync12 } from "fs";
@@ -6799,7 +7195,7 @@ function parseRetryAfter(response) {
6799
7195
  return 0;
6800
7196
  }
6801
7197
  function delay(ms) {
6802
- return new Promise((resolve15) => setTimeout(resolve15, ms));
7198
+ return new Promise((resolve16) => setTimeout(resolve16, ms));
6803
7199
  }
6804
7200
  var OpenAIProvider = class {
6805
7201
  name = "openai";
@@ -7666,6 +8062,242 @@ function resolveSpec(personality, name) {
7666
8062
  return personality;
7667
8063
  }
7668
8064
 
8065
+ // src/guard/middleware.ts
8066
+ function extractOpenAIResponse(response) {
8067
+ if (response?.choices?.[0]?.message?.content) {
8068
+ return response.choices[0].message.content;
8069
+ }
8070
+ if (response?.content?.[0]?.text) {
8071
+ return response.content[0].text;
8072
+ }
8073
+ if (typeof response === "string") {
8074
+ return response;
8075
+ }
8076
+ throw new Error(
8077
+ "Could not extract response text. Provide a custom extractResponse function."
8078
+ );
8079
+ }
8080
+ function injectResponseText(response, text) {
8081
+ if (response?.choices?.[0]?.message?.content !== void 0) {
8082
+ const cloned = JSON.parse(JSON.stringify(response));
8083
+ cloned.choices[0].message.content = text;
8084
+ return cloned;
8085
+ }
8086
+ if (response?.content?.[0]?.text !== void 0) {
8087
+ const cloned = JSON.parse(JSON.stringify(response));
8088
+ cloned.content[0].text = text;
8089
+ return cloned;
8090
+ }
8091
+ if (typeof response === "string") {
8092
+ return text;
8093
+ }
8094
+ return response;
8095
+ }
8096
+ function applyCorrections(text, patterns, _spec) {
8097
+ let corrected = text;
8098
+ for (const pattern of patterns) {
8099
+ switch (pattern.id) {
8100
+ case "over-apologizing": {
8101
+ corrected = corrected.replace(/\bI'm (?:so |very |truly |really )?sorry(?:,| but| that| for| about| if)\b/gi, "").replace(/\bI apologize (?:for|that|if)\b/gi, "").replace(/\bMy apologies(?:,| \.)\b/gi, "").replace(/^\s*[,.]?\s*/gm, (match) => match.trim() ? match : "").replace(/\n{3,}/g, "\n\n").trim();
8102
+ break;
8103
+ }
8104
+ case "hedge-stacking": {
8105
+ const hedgePatterns = [
8106
+ /\b(?:I think |I believe |I feel like |In my opinion, |It seems (?:like |to me )?(?:that )?)/gi,
8107
+ /\b(?:perhaps |maybe |possibly |arguably |potentially )/gi,
8108
+ /\b(?:sort of |kind of |more or less |to some extent )/gi
8109
+ ];
8110
+ const sentences = corrected.split(/(?<=[.!?])\s+/);
8111
+ corrected = sentences.map((sentence) => {
8112
+ let hedgeCount = 0;
8113
+ let result = sentence;
8114
+ for (const hp of hedgePatterns) {
8115
+ result = result.replace(hp, (match) => {
8116
+ hedgeCount++;
8117
+ return hedgeCount > 1 ? "" : match;
8118
+ });
8119
+ }
8120
+ return result;
8121
+ }).join(" ").replace(/\s{2,}/g, " ").trim();
8122
+ break;
8123
+ }
8124
+ case "sycophancy":
8125
+ case "sentiment-skew": {
8126
+ corrected = corrected.replace(/\b(?:Absolutely|Exactly|You're absolutely right|That's a great (?:question|point|idea|observation))(?:!|\.)\s*/gi, "").replace(/^\s*[,.]?\s*/gm, (match) => match.trim() ? match : "").trim();
8127
+ break;
8128
+ }
8129
+ }
8130
+ }
8131
+ if (corrected.trim().length < 10) {
8132
+ return text;
8133
+ }
8134
+ return corrected;
8135
+ }
8136
+ function createGuardMiddleware(options = {}) {
8137
+ const mode = options.mode ?? "enforce";
8138
+ const minSeverity = options.minSeverity ?? "warning";
8139
+ const name = options.name ?? "Agent";
8140
+ const maxCorrections = options.maxCorrections ?? 1;
8141
+ let spec;
8142
+ if (options.personality) {
8143
+ if (typeof options.personality === "string") {
8144
+ spec = loadSpec(options.personality);
8145
+ } else {
8146
+ spec = options.personality;
8147
+ }
8148
+ }
8149
+ const guardChain = options.guard ?? Guard.create(name).useAll();
8150
+ const stats = {
8151
+ totalCalls: 0,
8152
+ passed: 0,
8153
+ violated: 0,
8154
+ corrected: 0,
8155
+ blocked: 0,
8156
+ patternCounts: {}
8157
+ };
8158
+ function severityMeetsMin(severity) {
8159
+ if (minSeverity === "warning") return severity !== "clean";
8160
+ if (minSeverity === "concern") return severity === "concern";
8161
+ return false;
8162
+ }
8163
+ function trackPatterns(patterns) {
8164
+ for (const p of patterns) {
8165
+ stats.patternCounts[p.id] = (stats.patternCounts[p.id] || 0) + 1;
8166
+ }
8167
+ }
8168
+ function processViolation(guardResult, responseText) {
8169
+ const violation = {
8170
+ patterns: guardResult.patterns,
8171
+ severity: guardResult.severity,
8172
+ originalResponse: responseText,
8173
+ blocked: false,
8174
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
8175
+ };
8176
+ let finalText = responseText;
8177
+ let corrected = false;
8178
+ if (mode === "strict") {
8179
+ violation.blocked = true;
8180
+ stats.blocked++;
8181
+ finalText = `[Response blocked by behavioral guard: ${guardResult.patterns.map((p) => p.name).join(", ")}]`;
8182
+ } else if (mode === "enforce") {
8183
+ let attempt = 0;
8184
+ let current = responseText;
8185
+ while (attempt < maxCorrections) {
8186
+ current = applyCorrections(current, guardResult.patterns, spec);
8187
+ attempt++;
8188
+ const recheck = guardChain.run([
8189
+ ...buildContextMessages(current)
8190
+ ]);
8191
+ if (recheck.passed || !severityMeetsMin(recheck.severity)) {
8192
+ break;
8193
+ }
8194
+ }
8195
+ if (current !== responseText) {
8196
+ corrected = true;
8197
+ violation.correctedResponse = current;
8198
+ stats.corrected++;
8199
+ }
8200
+ finalText = current;
8201
+ }
8202
+ stats.violated++;
8203
+ trackPatterns(guardResult.patterns);
8204
+ options.onViolation?.(violation);
8205
+ return { finalText, violation, corrected };
8206
+ }
8207
+ function buildContextMessages(text) {
8208
+ return [{ role: "assistant", content: text }];
8209
+ }
8210
+ return {
8211
+ guard: guardChain,
8212
+ mode,
8213
+ filter(conversationHistory, assistantResponse) {
8214
+ stats.totalCalls++;
8215
+ const allMessages = [
8216
+ ...conversationHistory,
8217
+ { role: "assistant", content: assistantResponse }
8218
+ ];
8219
+ const guardResult = guardChain.run(allMessages);
8220
+ if (guardResult.passed || !severityMeetsMin(guardResult.severity)) {
8221
+ stats.passed++;
8222
+ return {
8223
+ text: assistantResponse,
8224
+ passed: true,
8225
+ guardResult,
8226
+ corrected: false
8227
+ };
8228
+ }
8229
+ const { finalText, violation, corrected } = processViolation(
8230
+ guardResult,
8231
+ assistantResponse
8232
+ );
8233
+ return {
8234
+ text: finalText,
8235
+ passed: false,
8236
+ guardResult,
8237
+ violation,
8238
+ corrected
8239
+ };
8240
+ },
8241
+ async wrap(llmCall, wrapOpts) {
8242
+ stats.totalCalls++;
8243
+ const response = await llmCall;
8244
+ const extract = wrapOpts?.extractResponse ?? extractOpenAIResponse;
8245
+ const inject = wrapOpts?.injectResponse ?? injectResponseText;
8246
+ const messages = wrapOpts?.messages ?? [];
8247
+ let responseText;
8248
+ try {
8249
+ responseText = extract(response);
8250
+ } catch {
8251
+ stats.passed++;
8252
+ return {
8253
+ response,
8254
+ passed: true,
8255
+ guardResult: {
8256
+ passed: true,
8257
+ agent: name,
8258
+ messagesAnalyzed: 0,
8259
+ patterns: [],
8260
+ healthy: [],
8261
+ detectorsRun: 0,
8262
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
8263
+ severity: "clean"
8264
+ },
8265
+ corrected: false
8266
+ };
8267
+ }
8268
+ const allMessages = [
8269
+ ...messages,
8270
+ { role: "assistant", content: responseText }
8271
+ ];
8272
+ const guardResult = guardChain.run(allMessages);
8273
+ if (guardResult.passed || !severityMeetsMin(guardResult.severity)) {
8274
+ stats.passed++;
8275
+ return {
8276
+ response,
8277
+ passed: true,
8278
+ guardResult,
8279
+ corrected: false
8280
+ };
8281
+ }
8282
+ const { finalText, violation, corrected } = processViolation(
8283
+ guardResult,
8284
+ responseText
8285
+ );
8286
+ const finalResponse = corrected ? inject(response, finalText) : response;
8287
+ return {
8288
+ response: finalResponse,
8289
+ passed: false,
8290
+ guardResult,
8291
+ violation,
8292
+ corrected
8293
+ };
8294
+ },
8295
+ stats() {
8296
+ return { ...stats, patternCounts: { ...stats.patternCounts } };
8297
+ }
8298
+ };
8299
+ }
8300
+
7669
8301
  // src/analysis/behavioral-index.ts
7670
8302
  function createIndexEntry(name, provider, model, configuration, report, notes) {
7671
8303
  return {
@@ -8658,6 +9290,181 @@ function loadAgentMessages(logDir) {
8658
9290
  return messages;
8659
9291
  }
8660
9292
 
9293
+ // src/compliance/audit-trail.ts
9294
+ import { readFileSync as readFileSync18, appendFileSync as appendFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync12 } from "fs";
9295
+ import { join as join18, resolve as resolve15 } from "path";
9296
+ function djb2(str) {
9297
+ let hash = 5381;
9298
+ for (let i = 0; i < str.length; i++) {
9299
+ hash = (hash << 5) + hash + str.charCodeAt(i) | 0;
9300
+ }
9301
+ return Math.abs(hash).toString(16).padStart(8, "0");
9302
+ }
9303
+ function hashEntry(entry) {
9304
+ const content = `${entry.seq}|${entry.timestamp}|${entry.event}|${entry.agent}|${JSON.stringify(entry.data)}|${entry.prevHash}`;
9305
+ return djb2(content);
9306
+ }
9307
+ function auditLogPath(agentHandle) {
9308
+ const dir = resolve15(process.cwd(), ".holomime", "audit");
9309
+ if (!existsSync17(dir)) mkdirSync12(dir, { recursive: true });
9310
+ const filename = agentHandle ? `${agentHandle}-audit.jsonl` : "audit.jsonl";
9311
+ return join18(dir, filename);
9312
+ }
9313
+ function appendAuditEntry(event, agent, data, agentHandle) {
9314
+ const logPath = auditLogPath(agentHandle);
9315
+ let prevHash = "genesis";
9316
+ let seq = 1;
9317
+ if (existsSync17(logPath)) {
9318
+ const lines = readFileSync18(logPath, "utf-8").trim().split("\n").filter(Boolean);
9319
+ if (lines.length > 0) {
9320
+ try {
9321
+ const lastEntry = JSON.parse(lines[lines.length - 1]);
9322
+ prevHash = lastEntry.hash;
9323
+ seq = lastEntry.seq + 1;
9324
+ } catch {
9325
+ }
9326
+ }
9327
+ }
9328
+ const partial = {
9329
+ seq,
9330
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
9331
+ event,
9332
+ agent,
9333
+ data,
9334
+ prevHash
9335
+ };
9336
+ const entry = {
9337
+ ...partial,
9338
+ hash: hashEntry(partial)
9339
+ };
9340
+ appendFileSync2(logPath, JSON.stringify(entry) + "\n");
9341
+ return entry;
9342
+ }
9343
+ function loadAuditLog(agentHandle) {
9344
+ const logPath = auditLogPath(agentHandle);
9345
+ if (!existsSync17(logPath)) return [];
9346
+ return readFileSync18(logPath, "utf-8").trim().split("\n").filter(Boolean).map((line) => {
9347
+ try {
9348
+ return JSON.parse(line);
9349
+ } catch {
9350
+ return null;
9351
+ }
9352
+ }).filter((e) => e !== null);
9353
+ }
9354
+ function verifyAuditChain(entries) {
9355
+ if (entries.length === 0) return true;
9356
+ for (let i = 0; i < entries.length; i++) {
9357
+ const entry = entries[i];
9358
+ const { hash, ...rest } = entry;
9359
+ const expected = hashEntry(rest);
9360
+ if (hash !== expected) return false;
9361
+ if (i === 0) {
9362
+ if (entry.prevHash !== "genesis") return false;
9363
+ } else {
9364
+ if (entry.prevHash !== entries[i - 1].hash) return false;
9365
+ }
9366
+ }
9367
+ return true;
9368
+ }
9369
+ function generateComplianceReport(agent, from, to, agentHandle) {
9370
+ const entries = loadAuditLog(agentHandle);
9371
+ const fromDate = new Date(from).getTime();
9372
+ const toDate = new Date(to).getTime();
9373
+ const periodEntries = entries.filter((e) => {
9374
+ const t = new Date(e.timestamp).getTime();
9375
+ return t >= fromDate && t <= toDate;
9376
+ });
9377
+ const diagnoses = periodEntries.filter((e) => e.event === "diagnosis").length;
9378
+ const sessions = periodEntries.filter((e) => e.event === "session").length;
9379
+ const driftEvents = periodEntries.filter((e) => e.event === "drift_detected").length;
9380
+ const guardViolations = periodEntries.filter((e) => e.event === "guard_violation").length;
9381
+ const gradeHistory = [];
9382
+ for (const e of periodEntries) {
9383
+ if (e.event === "certify" || e.event === "benchmark" || e.event === "evolve") {
9384
+ const grade = e.data.grade ?? "?";
9385
+ const score = e.data.score ?? 0;
9386
+ gradeHistory.push({ date: e.timestamp.split("T")[0], grade, score });
9387
+ }
9388
+ }
9389
+ const avgScore = gradeHistory.length > 0 ? gradeHistory.reduce((sum, g) => sum + g.score, 0) / gradeHistory.length : 0;
9390
+ const averageGrade = avgScore >= 90 ? "A" : avgScore >= 80 ? "B" : avgScore >= 70 ? "C" : avgScore >= 60 ? "D" : "F";
9391
+ return {
9392
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
9393
+ agent,
9394
+ period: { from, to },
9395
+ summary: {
9396
+ totalEvents: periodEntries.length,
9397
+ diagnoses,
9398
+ sessions,
9399
+ driftEvents,
9400
+ guardViolations,
9401
+ averageGrade,
9402
+ gradeHistory
9403
+ },
9404
+ credentials: [],
9405
+ chainIntegrity: verifyAuditChain(entries),
9406
+ standards: [
9407
+ "EU AI Act Article 9 (Risk Management)",
9408
+ "EU AI Act Article 12 (Record-keeping)",
9409
+ "NIST AI RMF 1.0 (Govern, Map, Measure, Manage)"
9410
+ ]
9411
+ };
9412
+ }
9413
+ function generateMonitoringCertificate(agent, from, to, agentHandle) {
9414
+ const report = generateComplianceReport(agent, from, to, agentHandle);
9415
+ const scores = report.summary.gradeHistory.map((g) => g.score);
9416
+ const minScore = scores.length > 0 ? Math.min(...scores) : 0;
9417
+ const maxScore = scores.length > 0 ? Math.max(...scores) : 0;
9418
+ const statement = `This certifies that AI agent "${agent}" was continuously monitored by holomime from ${from} to ${to}. During this period, the agent maintained an average behavioral alignment grade of ${report.summary.averageGrade} (scores ranging ${minScore}-${maxScore}/100). ${report.summary.driftEvents} drift events were detected and ${report.summary.sessions} therapy sessions were conducted. Audit chain integrity: ${report.chainIntegrity ? "VERIFIED" : "FAILED"}.`;
9419
+ return {
9420
+ agent,
9421
+ period: { from, to },
9422
+ maintainedGrade: report.summary.averageGrade,
9423
+ minScore,
9424
+ maxScore,
9425
+ totalEvents: report.summary.totalEvents,
9426
+ verified: report.chainIntegrity,
9427
+ issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
9428
+ statement
9429
+ };
9430
+ }
9431
+ function formatComplianceReportMarkdown(report) {
9432
+ const lines = [
9433
+ `# Behavioral Compliance Report \u2014 ${report.agent}`,
9434
+ "",
9435
+ `**Period:** ${report.period.from} to ${report.period.to}`,
9436
+ `**Generated:** ${report.generatedAt}`,
9437
+ `**Chain Integrity:** ${report.chainIntegrity ? "VERIFIED" : "FAILED"}`,
9438
+ "",
9439
+ "## Summary",
9440
+ "",
9441
+ `| Metric | Value |`,
9442
+ `|--------|-------|`,
9443
+ `| Total Events | ${report.summary.totalEvents} |`,
9444
+ `| Diagnoses Run | ${report.summary.diagnoses} |`,
9445
+ `| Therapy Sessions | ${report.summary.sessions} |`,
9446
+ `| Drift Events | ${report.summary.driftEvents} |`,
9447
+ `| Guard Violations | ${report.summary.guardViolations} |`,
9448
+ `| Average Grade | ${report.summary.averageGrade} |`,
9449
+ ""
9450
+ ];
9451
+ if (report.summary.gradeHistory.length > 0) {
9452
+ lines.push("## Grade History", "");
9453
+ lines.push("| Date | Grade | Score |");
9454
+ lines.push("|------|:-----:|------:|");
9455
+ for (const g of report.summary.gradeHistory) {
9456
+ lines.push(`| ${g.date} | ${g.grade} | ${g.score}/100 |`);
9457
+ }
9458
+ lines.push("");
9459
+ }
9460
+ lines.push("## Applicable Standards", "");
9461
+ for (const s of report.standards) {
9462
+ lines.push(`- ${s}`);
9463
+ }
9464
+ lines.push("");
9465
+ return lines.join("\n");
9466
+ }
9467
+
8661
9468
  // src/core/embodiment-sync.ts
8662
9469
  import { z as z5 } from "zod";
8663
9470
  var syncAnchorSchema = z5.enum([
@@ -8719,10 +9526,12 @@ export {
8719
9526
  addNode,
8720
9527
  addSessionToMemory,
8721
9528
  agentHandleFromSpec,
9529
+ appendAuditEntry,
8722
9530
  appendEvolution,
8723
9531
  applyRecommendations,
8724
9532
  bigFiveSchema,
8725
9533
  buildAgentTherapistPrompt,
9534
+ buildAnonymizedReport,
8726
9535
  buildPatientSystemPrompt,
8727
9536
  buildReACTContext,
8728
9537
  buildReACTFraming,
@@ -8751,12 +9560,14 @@ export {
8751
9560
  corpusStats,
8752
9561
  createGist,
8753
9562
  createGraph,
9563
+ createGuardMiddleware,
8754
9564
  createIndex,
8755
9565
  createIndexEntry,
8756
9566
  createMemory,
8757
9567
  createProvider,
8758
9568
  createRepertoire,
8759
9569
  createTreatmentPlan,
9570
+ decayUnseenPatterns,
8760
9571
  deepMergeSpec,
8761
9572
  detectApologies,
8762
9573
  detectBoundaryIssues,
@@ -8780,17 +9591,21 @@ export {
8780
9591
  extractDPOPairsWithLLM,
8781
9592
  extractRLHFExamples,
8782
9593
  extractRecommendations,
9594
+ fetchLeaderboard,
8783
9595
  fetchPersonality,
8784
9596
  fetchRegistry,
8785
9597
  findCrossAgentCorrelations,
8786
9598
  findEdges,
8787
9599
  findNode,
8788
9600
  findNodesByType,
9601
+ formatComplianceReportMarkdown,
8789
9602
  gazePolicySchema,
8790
9603
  generateBenchmarkMarkdown,
8791
9604
  generateComparisonMarkdown,
9605
+ generateComplianceReport,
8792
9606
  generateCredential,
8793
9607
  generateIndexMarkdown,
9608
+ generateMonitoringCertificate,
8794
9609
  generatePrescriptions,
8795
9610
  generateProgressReport,
8796
9611
  generateSystemPrompt,
@@ -8808,6 +9623,7 @@ export {
8808
9623
  getMarketplaceClient,
8809
9624
  getMemoryContext,
8810
9625
  getNeighbors,
9626
+ getPhaseContext,
8811
9627
  getScenarioById,
8812
9628
  getTotalSignalCount,
8813
9629
  graphStats,
@@ -8820,6 +9636,7 @@ export {
8820
9636
  listDetectors,
8821
9637
  listDetectorsByCategory,
8822
9638
  listDetectorsByTag,
9639
+ loadAuditLog,
8823
9640
  loadBenchmarkResults,
8824
9641
  loadCorpus,
8825
9642
  loadCustomDetectors,
@@ -8844,6 +9661,7 @@ export {
8844
9661
  parseConversationLog,
8845
9662
  parseConversationLogFromString,
8846
9663
  parseJSONLLog,
9664
+ parseMarkdownDetector,
8847
9665
  parseOTelGenAIExport,
8848
9666
  parseOpenAIAPILog,
8849
9667
  personalitySpecSchema,
@@ -8856,6 +9674,7 @@ export {
8856
9674
  prosodySchema,
8857
9675
  providerSchema,
8858
9676
  proxemicZoneSchema,
9677
+ publishToLeaderboard,
8859
9678
  pushToHFHub,
8860
9679
  queryCorpus,
8861
9680
  queryInterventions,
@@ -8891,6 +9710,7 @@ export {
8891
9710
  selectIntervention,
8892
9711
  severityMeetsThreshold2 as severityMeetsThreshold,
8893
9712
  severitySchema,
9713
+ shareAnonymizedPatterns,
8894
9714
  startFleet,
8895
9715
  startMCPServer,
8896
9716
  startWatch,
@@ -8907,6 +9727,7 @@ export {
8907
9727
  unregisterDetector,
8908
9728
  updateEdgeWeight,
8909
9729
  validateDetectorConfig,
9730
+ verifyAuditChain,
8910
9731
  verifyCredential,
8911
9732
  wrapAgent
8912
9733
  };